diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 2066bf875e..4fd6aa5f36 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1455,6 +1455,7 @@ + @@ -1466,6 +1467,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 5dd55ca0d6..dede17b00c 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -4307,6 +4307,9 @@ save yourself some time, copy this for undocumented things: Sets the NextScreen value to name. + + Sets the PrevScreen value to name. + [02 Other.lua] Gets a string from the current Screen in the current language. @@ -4322,6 +4325,9 @@ save yourself some time, copy this for undocumented things: + + This should behave identically to the normal back button behavior. This function is for the pause menu to use when the player forfeits or restarts, so that a score isn't saved. + Returns true if a single has its NoteField centered. diff --git a/Themes/default/BGAnimations/ScreenEvaluation overlay/default.lua b/Themes/default/BGAnimations/ScreenEvaluation overlay/default.lua index 813de9833f..c0e3d065d9 100644 --- a/Themes/default/BGAnimations/ScreenEvaluation overlay/default.lua +++ b/Themes/default/BGAnimations/ScreenEvaluation overlay/default.lua @@ -65,4 +65,15 @@ t[#t+1] = Def.ActorFrame { InitCommand=cmd(hide_if,not GAMESTATE:IsPlayerEnabled(PLAYER_2);x,WideScale(math.floor(SCREEN_CENTER_X*1.7)+8,math.floor(SCREEN_CENTER_X*1.5)+8);y,SCREEN_CENTER_Y-20); CreateStats( PLAYER_2 ); }; + +if gameplay_pause_count > 0 then + t[#t+1]= Def.BitmapText{ + Font= "Common Normal", + Text= THEME:GetString("PauseMenu", "pause_count") .. ": " .. gameplay_pause_count, + InitCommand= function(self) + self:xy(_screen.cx, 375):diffuse(Color.White):zoom(.75) + end + } +end + return t diff --git a/Themes/default/BGAnimations/ScreenGameplay overlay.lua b/Themes/default/BGAnimations/ScreenGameplay overlay.lua index 116f950fab..60d0d9f580 100644 --- a/Themes/default/BGAnimations/ScreenGameplay overlay.lua +++ b/Themes/default/BGAnimations/ScreenGameplay overlay.lua @@ -37,4 +37,5 @@ if GAMESTATE:GetCurrentCourse() then end; end; t.InitCommand=cmd(SetUpdateFunction,UpdateTime); +t[#t+1]= LoadActor(THEME:GetPathG("", "pause_menu")) return t diff --git a/Themes/default/Graphics/pause_menu.lua b/Themes/default/Graphics/pause_menu.lua new file mode 100644 index 0000000000..452460c79c --- /dev/null +++ b/Themes/default/Graphics/pause_menu.lua @@ -0,0 +1,197 @@ +gameplay_pause_count= 0 + +local pause_buttons= {Start= true, Select= true, Back= true} +local pause_double_tap_time= .5 +local pause_press_times= {} +local screen_gameplay= false +local menu_items= {[PLAYER_1]= {}, [PLAYER_2]= {}} +local menu_frames= {} +local menu_choices= { + "continue_playing", + "restart_song", + "forfeit_song", +} +if GAMESTATE:IsCourseMode() then + menu_choices= { + "continue_playing", + "skip_song", + "forfeit_course", + } +end +local menu_spacing= 32 +local menu_bg_width= _screen.w * .4 +local menu_text_width= _screen.w * .35 +local menu_x= {[PLAYER_1]= _screen.w*.25, [PLAYER_2]= _screen.w*.75} +local menu_y= _screen.cy - (#menu_choices * .5 * menu_spacing) +local current_menu_choice= {} +local menu_is_showing= {} +local enabled_players= {} + +local function create_menu_item(pn, x, y, item_name) + return Def.BitmapText{ + Font= "Common Normal", Text= THEME:GetString("PauseMenu", item_name), + InitCommand= function(self) + self:xy(x, y) + table.insert(menu_items[pn], self) + self:playcommand("LoseFocus") + end, + LoseFocusCommand= function(self) + self:stopeffect():rotationz(0) + end, + GainFocusCommand= function(self) + self:wag():effectperiod(2):effectmagnitude(0, 0, 5) + end, + } +end + +local function create_menu_frame(pn, x, y) + local frame= Def.ActorFrame{ + InitCommand= function(self) + self:xy(x, y):playcommand("Hide") + menu_frames[pn]= self + end, + ShowCommand= function(self) + self:visible(true) + end, + HideCommand= function(self) + self:visible(false) + end, + Def.Quad{ + InitCommand= function(self) + self:setsize(menu_bg_width, menu_spacing * (#menu_choices + 1)) + :y(-menu_spacing):vertalign(top) + :diffuse{0, 0, 0, .25} + :playcommand("Hide") + end, + }, + } + for i, choice in ipairs(menu_choices) do + frame[#frame+1]= create_menu_item(pn, 0, (i-1)*menu_spacing, choice) + end + return frame +end + +local function backout(screen) + screen_gameplay:SetPrevScreenName(screen):begin_backing_out() +end + +local function show_menu(pn) + menu_frames[pn]:playcommand("Show") + for i, item in ipairs(menu_items[pn]) do + item:playcommand("LoseFocus") + end + current_menu_choice[pn]= 1 + menu_items[pn][current_menu_choice[pn]]:playcommand("GainFocus") + menu_is_showing[pn]= true +end + +local function close_menu(pn) + menu_frames[pn]:playcommand("Hide") + menu_is_showing[pn]= false + local stay_paused= false + for pn, showing in pairs(menu_is_showing) do + if showing then + stay_paused= true + end + end + if not stay_paused then + screen_gameplay:PauseGame(false) + end +end + +local choice_actions= { + continue_playing= function(pn) + close_menu(pn) + end, + restart_song= function(pn) + backout("ScreenStageInformation") + end, + forfeit_song= function(pn) + backout(SelectMusicOrCourse()) + end, + skip_song= function(pn) + screen_gameplay:PostScreenMessage("SM_NotesEnded", 0) + end, + forfeit_course= function(pn) + backout(SelectMusicOrCourse()) + end, +} + +local menu_actions= { + Start= function(pn) + local choice_name= menu_choices[current_menu_choice[pn]] + if choice_actions[choice_name] then + choice_actions[choice_name](pn) + end + end, + Left= function(pn) + if current_menu_choice[pn] > 1 then + menu_items[pn][current_menu_choice[pn]]:playcommand("LoseFocus") + current_menu_choice[pn]= current_menu_choice[pn] - 1 + menu_items[pn][current_menu_choice[pn]]:playcommand("GainFocus") + end + end, + Right= function(pn) + if current_menu_choice[pn] < #menu_choices then + menu_items[pn][current_menu_choice[pn]]:playcommand("LoseFocus") + current_menu_choice[pn]= current_menu_choice[pn] + 1 + menu_items[pn][current_menu_choice[pn]]:playcommand("GainFocus") + end + end, +} +menu_actions.Up= menu_actions.Left +menu_actions.Down= menu_actions.Right +menu_actions.MenuLeft= menu_actions.Left +menu_actions.MenuRight= menu_actions.Right +menu_actions.MenuUp= menu_actions.Up +menu_actions.MenuDown= menu_actions.Down + +local function input(event) + local pn= event.PlayerNumber + if not enabled_players[pn] then return end + local button= event.GameButton + if not button then return end + if event.type == "InputEventType_Release" then return end + local is_paused= screen_gameplay:IsPaused() + if is_paused then + if menu_is_showing[pn] then + if menu_actions[button] then + menu_actions[button](pn) + return true + end + else + if pause_buttons[button] then + show_menu(pn) + return true + end + end + else + if event.type ~= "InputEventType_FirstPress" then return end + if pause_buttons[button] then + if pause_press_times[pn] and + GetTimeSinceStart() - pause_press_times[pn] <= pause_double_tap_time then + gameplay_pause_count= gameplay_pause_count + 1 + screen_gameplay:PauseGame(true) + show_menu(pn) + else + pause_press_times[pn]= GetTimeSinceStart() + end + else + pause_press_times[pn]= nil + end + end +end + +local frame= Def.ActorFrame{ + OnCommand= function(self) + screen_gameplay= SCREENMAN:GetTopScreen() + screen_gameplay:AddInputCallback(input) + end, +} + +for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do + enabled_players[pn]= true + frame[#frame+1]= create_menu_frame(pn, menu_x[pn], menu_y) +end + +return frame diff --git a/Themes/default/Languages/en.ini b/Themes/default/Languages/en.ini index 4ff4543f29..9d09ff9bf9 100644 --- a/Themes/default/Languages/en.ini +++ b/Themes/default/Languages/en.ini @@ -85,6 +85,14 @@ HelpText= [ScreenGameplay] HelpText= +[PauseMenu] +continue_playing=Continue Playing +forfeit_course=Forfeit Course +forfeit_song=Forfeit Song +pause_count=Pause Count +restart_song=Restart Song +skip_song=Skip Song + [ScreenHeartEntry] Enter Heart Rate=Enter Heart Rate Song Length=Song Length diff --git a/Themes/default/metrics.ini b/Themes/default/metrics.ini index 51a71e6e8d..17f42c5fba 100644 --- a/Themes/default/metrics.ini +++ b/Themes/default/metrics.ini @@ -1608,7 +1608,13 @@ ActiveAttackListP2Y= ActiveAttackListP2OnCommand=visible,false ActiveAttackListP2OffCommand= # - +# Disable the normal start and select button actions to enable a custom pause +# menu +GivingUpGoesToPrevScreen=false +UnpauseWithStart=false +StartGivesUp=false +BackGivesUp=false +SelectSkipsSong=false [ScreenGameplayShared] diff --git a/src/Screen.cpp b/src/Screen.cpp index 6558a6fb16..c3910009c5 100644 --- a/src/Screen.cpp +++ b/src/Screen.cpp @@ -273,6 +273,11 @@ void Screen::SetNextScreenName(RString const& name) m_sNextScreen= name; } +void Screen::SetPrevScreenName(RString const& name) +{ + m_sPrevScreen= name; +} + RString Screen::GetPrevScreen() const { if( !m_sPrevScreen.empty() ) @@ -411,6 +416,7 @@ public: static int GetNextScreenName( T* p, lua_State *L ) { lua_pushstring(L, p->GetNextScreenName() ); return 1; } static int SetNextScreenName( T* p, lua_State *L ) { p->SetNextScreenName(SArg(1)); COMMON_RETURN_SELF; } static int GetPrevScreenName( T* p, lua_State *L ) { lua_pushstring(L, p->GetPrevScreen() ); return 1; } + static int SetPrevScreenName( T* p, lua_State *L ) { p->SetPrevScreenName(SArg(1)); COMMON_RETURN_SELF; } static int lockinput( T* p, lua_State *L ) { p->SetLockInputSecs(FArg(1)); COMMON_RETURN_SELF; } DEFINE_METHOD( GetScreenType, GetScreenType() ) @@ -447,6 +453,7 @@ public: ADD_METHOD( GetNextScreenName ); ADD_METHOD( SetNextScreenName ); ADD_METHOD( GetPrevScreenName ); + ADD_METHOD( SetPrevScreenName ); ADD_METHOD( PostScreenMessage ); ADD_METHOD( lockinput ); ADD_METHOD( GetScreenType ); diff --git a/src/Screen.h b/src/Screen.h index 0bea3cd95b..3ed00bdf50 100644 --- a/src/Screen.h +++ b/src/Screen.h @@ -124,6 +124,7 @@ public: RString GetNextScreenName() const; RString GetPrevScreen() const; void SetNextScreenName(RString const& name); + void SetPrevScreenName(RString const& name); bool PassInputToLua(const InputEventPlus& input); void AddInputCallbackFromStack(lua_State* L); diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index 950d379403..118fe29bec 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -1959,9 +1959,7 @@ void ScreenGameplay::Update( float fDeltaTime ) pi->GetPlayerStageStats()->m_bFailed |= bAllHumanHaveBigMissCombo; pi->GetPlayerStageStats()->m_bDisqualified |= bGiveUpTimerFired; // Don't disqualify if failing for miss combo. The player should still be eligable for a high score on courses. } - ResetGiveUpTimers(false); - if(GIVING_UP_GOES_TO_PREV_SCREEN && !m_skipped_song) { BeginBackingOutFromGameplay(); @@ -3267,6 +3265,11 @@ public: FLOAT_TABLE_INTERFACE(HasteAddAmounts, HasteAddAmounts, AddAmountsValid); FLOAT_NO_SPEED_INTERFACE(HasteTimeBetweenUpdates, HasteTimeBetweenUpdates, (v > 0)); FLOAT_NO_SPEED_INTERFACE(HasteLifeSwitchPoint, HasteLifeSwitchPoint, (v >= 0 && v <= 1)); + static int begin_backing_out(T* p, lua_State* L) + { + p->BeginBackingOutFromGameplay(); + COMMON_RETURN_SELF; + } static int GetTrueBPS(T* p, lua_State* L) { PlayerNumber pn= Enum::Check(L, 1); @@ -3293,6 +3296,7 @@ public: ADD_METHOD( HasteAddAmounts ); ADD_METHOD( HasteTimeBetweenUpdates ); ADD_METHOD( HasteLifeSwitchPoint ); + ADD_METHOD(begin_backing_out); ADD_METHOD( GetTrueBPS ); } }; diff --git a/src/ScreenGameplay.h b/src/ScreenGameplay.h index 799d78786b..4c78bc3f33 100644 --- a/src/ScreenGameplay.h +++ b/src/ScreenGameplay.h @@ -176,6 +176,7 @@ public: void FailFadeRemovePlayer(PlayerInfo* pi); void FailFadeRemovePlayer(PlayerNumber pn); + void BeginBackingOutFromGameplay(); vector m_HasteTurningPoints; // Values at which the meaning of GAMESTATE->m_fHasteRate changes. vector m_HasteAddAmounts; // Amounts that are added to speed depending on what turning point has been passed. @@ -225,7 +226,6 @@ protected: void PlayAnnouncer( const RString &type, float fSeconds ) { PlayAnnouncer(type, fSeconds, &m_fTimeSinceLastDancingComment); } void UpdateLights(); void SendCrossedMessages(); - void BeginBackingOutFromGameplay(); void PlayTicks(); void UpdateSongPosition( float fDeltaTime );