diff --git a/Themes/default/BGAnimations/ScreenStageInformation underlay/default.lua b/Themes/default/BGAnimations/ScreenStageInformation underlay/default.lua index cffe860349..47cb3e9638 100644 --- a/Themes/default/BGAnimations/ScreenStageInformation underlay/default.lua +++ b/Themes/default/BGAnimations/ScreenStageInformation underlay/default.lua @@ -7,26 +7,6 @@ if playMode ~= 'PlayMode_Regular' and playMode ~= 'PlayMode_Rave' and playMode ~ sStage = playMode; end; -if not (GAMESTATE:IsCourseMode() or GAMESTATE:IsExtraStage() or GAMESTATE:IsExtraStage2()) then - local tRemap = { - Stage_Event = 0, - Stage_1st = 1, - Stage_2nd = 2, - Stage_3rd = 3, - Stage_4th = 4, - Stage_5th = 5, - Stage_6th = 6, - }; - - local nSongCount = tRemap[sStage] + (GAMESTATE:GetCurrentSong():GetStageCost()-1); - - if nSongCount >= PREFSMAN:GetPreference("SongsPerPlay") then - sStage = "Stage_Final"; - else - sStage = sStage; - end; -end; - local t = Def.ActorFrame {}; t[#t+1] = Def.Quad { InitCommand=cmd(Center;zoomto,SCREEN_WIDTH,SCREEN_HEIGHT;diffuse,Color("Black")); @@ -47,11 +27,28 @@ else }; end +local stage_num_actor= THEME:GetPathG("ScreenStageInformation", "Stage " .. ToEnumShortString(sStage), true) +if stage_num_actor ~= "" and FILEMAN:DoesFileExist(stage_num_actor) then + stage_num_actor= LoadActor(stage_num_actor) +else + -- Midiman: We need a "Stage Next" actor or something for stages after + -- the 6th. -Kyz + local curStage = GAMESTATE:GetCurrentStage(); + stage_num_actor= Def.BitmapText{ + Font= "Common Normal", Text= thified_curstage_index(false) .. " Stage", + InitCommand= function(self) + self:zoom(1.5) + self:strokecolor(Color.Black) + self:diffuse(StageToColor(curStage)); + self:diffusetopedge(ColorLightTone(StageToColor(curStage))); + end + } +end + t[#t+1] = Def.ActorFrame { InitCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y); OnCommand=cmd(stoptweening;zoom,1.25;decelerate,3;zoom,1); - - LoadActor( THEME:GetPathG("ScreenStageInformation", "Stage " .. ToEnumShortString(sStage) ) ) .. { + stage_num_actor .. { OnCommand=cmd(diffusealpha,0;linear,0.25;diffusealpha,1;sleep,1.75;linear,0.5;zoomy,0;zoomx,2;diffusealpha,0); }; }; diff --git a/Themes/default/Graphics/ScreenEvaluationSummary BannerList.lua b/Themes/default/Graphics/ScreenEvaluationSummary BannerList.lua index 76bc459ed6..2d72017465 100644 --- a/Themes/default/Graphics/ScreenEvaluationSummary BannerList.lua +++ b/Themes/default/Graphics/ScreenEvaluationSummary BannerList.lua @@ -33,7 +33,7 @@ for i=1,#statList do as[#as+1] = Def.ActorFrame { Def.Sprite { InitCommand=cmd(scaletoclipped,256,80); - BeginCommand=function(self) + OnCommand=function(self) local path = statList[j]:GetPlayedSongs()[1]:GetBannerPath() or THEME:GetPathG("Common","fallback banner"); self:LoadBanner(path); end; @@ -43,7 +43,7 @@ for i=1,#statList do OnCommand=cmd(zoomto,80,18;diffuse,Color.Black;diffusealpha,0.5;fadeleft,0.5); }; LoadFont("Common Normal") .. { - Text=StageToLocalizedString( statList[j]:GetStage() ); + Text=thify_number(statList[j]:GetStageIndex()+1); InitCommand=cmd(x,128-4;y,40-4;horizalign,right;vertalign,bottom); OnCommand=cmd(diffuse,StageToColor(statList[j]:GetStage());zoom,0.675;shadowlength,1); }; diff --git a/Themes/default/Graphics/ScreenGameplay StageDisplay.lua b/Themes/default/Graphics/ScreenGameplay StageDisplay.lua index 42e67810ac..ba89053a62 100644 --- a/Themes/default/Graphics/ScreenGameplay StageDisplay.lua +++ b/Themes/default/Graphics/ScreenGameplay StageDisplay.lua @@ -1,20 +1,8 @@ local curScreen = Var "LoadingScreen"; local curStage = GAMESTATE:GetCurrentStage(); -local curStageIndex = GAMESTATE:GetCurrentStageIndex(); +local curStageIndex = GAMESTATE:GetCurrentStageIndex() + 1; local playMode = GAMESTATE:GetPlayMode(); -local tRemap = { - Stage_1st = 1, - Stage_2nd = 2, - Stage_3rd = 3, - Stage_4th = 4, - Stage_5th = 5, - Stage_6th = 6, -}; -if tRemap[curStage] == PREFSMAN:GetPreference("SongsPerPlay") then - curStage = "Stage_Final"; -end - local t = Def.ActorFrame { LoadActor(THEME:GetPathB("_frame","3x3"),"rounded black",64,16); LoadFont("Common Normal") .. { @@ -36,12 +24,13 @@ local t = Def.ActorFrame { self:settextf("%i / %i", songsPlayed, GAMESTATE:GetCurrentCourse():GetEstimatedNumStages()); else if GAMESTATE:IsEventMode() then - self:settextf("Stage %s", curStageIndex+1); + self:settextf("Stage %s", curStageIndex); else + local thed_stage= thified_curstage_index(false) if THEME:GetMetric(curScreen,"StageDisplayUseShortString") then - self:settextf("%s", ToEnumShortString(curStage)); + self:settextf(thed_stage) else - self:settextf("%s Stage", ToEnumShortString(curStage)); + self:settextf("%s Stage", thed_stage) end end end; diff --git a/Themes/default/Graphics/ScreenWithMenuElements StageDisplay.lua b/Themes/default/Graphics/ScreenWithMenuElements StageDisplay.lua index 3f442716f8..051b482d0a 100644 --- a/Themes/default/Graphics/ScreenWithMenuElements StageDisplay.lua +++ b/Themes/default/Graphics/ScreenWithMenuElements StageDisplay.lua @@ -21,18 +21,12 @@ t[#t+1] = Def.ActorFrame { elseif GAMESTATE:IsEventMode() then self:settextf("Stage %s", curStageIndex); else - -- hack: ScreenEvaluation shows the current stage, but it needs to - -- show the last stage instead. PlayedStageStats has the right one. - if string.find(curScreen,"Evaluation") then - local stageStats = STATSMAN:GetPlayedStageStats(1) - curStage = stageStats:GetStage() - end - + local thed_stage= thified_curstage_index(curScreen:find("Evaluation")) if THEME:GetMetric(curScreen,"StageDisplayUseShortString") then - self:settextf("%s", ToEnumShortString(curStage)); + self:settext(thed_stage) self:zoom(0.75); else - self:settextf("%s Stage", ToEnumShortString(curStage)); + self:settextf("%s Stage", thed_stage); self:zoom(1); end; end; diff --git a/Themes/default/Scripts/04 Other.lua b/Themes/default/Scripts/04 Other.lua index 9f804da9fb..64e8c16b45 100644 --- a/Themes/default/Scripts/04 Other.lua +++ b/Themes/default/Scripts/04 Other.lua @@ -17,4 +17,47 @@ function SongMeterDisplayCommand(pn) local xAdd = (pn == PLAYER_1) and -24 or 24 return cmd(draworder,5;rotationz,-90;zoom,0;addx,xAdd;sleep,1.5;decelerate,0.5;zoom,1;addx,xAdd*-1) end -end \ No newline at end of file +end + +function thify_number(n) + if n >= 10 and n < 20 then return n .. "th" end + local th_suffixes= {"st", "nd", "rd"} + local end_digit= n % 10 + return n .. (th_suffixes[end_digit] or "th") +end + +local numbered_stages= { + Stage_1st= true, + Stage_2nd= true, + Stage_3rd= true, + Stage_4th= true, + Stage_5th= true, + Stage_6th= true, + Stage_Next= true, +} + +function is_stage_final() + -- The correct way to determine whether the player has reached Final Stage + -- is to check the number of stages left. + -- If you use the SongsPerPlay preference, you become incorrect after a + -- continue occurs. + local master_player= GAMESTATE:GetMasterPlayerNumber() + local stages_left= GAMESTATE:GetNumStagesLeft(master_player) + local stage_cost= GAMESTATE:GetCurrentSong():GetStageCost() + return (stages_left - stage_cost) < 1 +end + +function thified_curstage_index(on_eval) + local cur_stage= GAMESTATE:GetCurrentStage() + local adjust= 1 + -- hack: ScreenEvaluation shows the current stage, but it needs to show + -- the last stage instead. Adjust the amount. + if on_eval then + adjust= 0 + end + if numbered_stages[cur_stage] then + return thify_number(GAMESTATE:GetCurrentStageIndex() + adjust) + else + return ToEnumShortString(cur_stage) + end +end diff --git a/Themes/default/metrics.ini b/Themes/default/metrics.ini index 36ed3d2482..4a83a33650 100644 --- a/Themes/default/metrics.ini +++ b/Themes/default/metrics.ini @@ -2023,7 +2023,7 @@ SmallBanner4OffCommand= SmallBanner5X=SCREEN_CENTER_X+48 SmallBanner5Y=SCREEN_TOP+100+16 -SmallBanner5OnCommand=zoom,0.5 +SmallBanner5OnCommand=visible,false SmallBanner5OfCommand= SmallBanner6X=SCREEN_CENTER_X+72 diff --git a/src/GameState.cpp b/src/GameState.cpp index eb993a701e..196f56ef78 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -314,6 +314,8 @@ void GameState::Reset() m_iGameSeed = rand(); m_iStageSeed = rand(); + m_AdjustTokensBySongCostForFinalStageCheck= true; + m_pCurSong.Set( GetDefaultSong() ); m_pPreferredSong = NULL; m_pCurCourse.Set( NULL ); @@ -1061,6 +1063,36 @@ bool GameState::IsFinalStageForAnyHumanPlayer() const return GetSmallestNumStagesLeftForAnyHumanPlayer() == 1; } +bool GameState::IsFinalStageForEveryHumanPlayer() const +{ + int song_cost= 1; + if(m_pCurSong != NULL) + { + if(m_pCurSong->IsLong()) + { + song_cost= 2; + } + else if(m_pCurSong->IsMarathon()) + { + song_cost= 3; + } + } + // If we're on gameplay or evaluation, they set this to false because those + // screens have already had the stage tokens subtracted. + song_cost*= m_AdjustTokensBySongCostForFinalStageCheck; + int num_on_final= 0; + int num_humans= 0; + FOREACH_HumanPlayer(p) + { + if(m_iPlayerStageTokens[p] - song_cost <= 0) + { + ++num_on_final; + } + ++num_humans; + } + return num_on_final >= num_humans; +} + bool GameState::IsAnExtraStage() const { if( this->GetMasterPlayerNumber() == PlayerNumber_Invalid ) @@ -1098,10 +1130,11 @@ Stage GameState::GetCurrentStage() const else if( m_PlayMode == PLAY_MODE_ENDLESS ) return Stage_Endless; else if( IsExtraStage() ) return Stage_Extra1; else if( IsExtraStage2() ) return Stage_Extra2; - //else if( IsFinalStageForAnyHumanPlayer() ) return Stage_Final; - // above function behaves weirdly, it will always return final stage if any player is - // on final stage, rather than the last remaining player. The below method seems to make a bit more sense. - else if(m_iPlayerStageTokens[PLAYER_1] == 0 && m_iPlayerStageTokens[PLAYER_2] == 0) return Stage_Final; + // Previous logic did not factor in current song length, or the fact that + // players aren't allowed to start a song with 0 tokens. This new + // function also has logic for handling the Gameplay and Evaluation cases + // which used to require workarounds on the theme side. -Kyz + else if(IsFinalStageForEveryHumanPlayer()) return Stage_Final; else { switch( this->m_iCurrentStageIndex ) diff --git a/src/GameState.h b/src/GameState.h index 3b6bf60345..4051b32677 100644 --- a/src/GameState.h +++ b/src/GameState.h @@ -202,6 +202,9 @@ public: * * This resets whenever a player joins or continues. */ int m_iPlayerStageTokens[NUM_PLAYERS]; + // This is necessary so that IsFinalStageForEveryHumanPlayer knows to + // adjust for the current song cost. + bool m_AdjustTokensBySongCostForFinalStageCheck; RString sExpandedSectionName; @@ -216,6 +219,7 @@ public: int GetNumStagesLeft( PlayerNumber pn ) const; int GetSmallestNumStagesLeftForAnyHumanPlayer() const; bool IsFinalStageForAnyHumanPlayer() const; + bool IsFinalStageForEveryHumanPlayer() const; bool IsAnExtraStage() const; bool IsAnExtraStageAndSelectionLocked() const; bool IsExtraStage() const; diff --git a/src/ScreenEvaluation.cpp b/src/ScreenEvaluation.cpp index 02f7939ce0..1d8c1a836c 100644 --- a/src/ScreenEvaluation.cpp +++ b/src/ScreenEvaluation.cpp @@ -75,6 +75,17 @@ static const int NUM_SHOWN_RADAR_CATEGORIES = 5; AutoScreenMessage( SM_PlayCheer ); REGISTER_SCREEN_CLASS( ScreenEvaluation ); + +ScreenEvaluation::ScreenEvaluation() +{ + GAMESTATE->m_AdjustTokensBySongCostForFinalStageCheck= false; +} + +ScreenEvaluation::~ScreenEvaluation() +{ + GAMESTATE->m_AdjustTokensBySongCostForFinalStageCheck= true; +} + void ScreenEvaluation::Init() { LOG->Trace( "ScreenEvaluation::Init()" ); diff --git a/src/ScreenEvaluation.h b/src/ScreenEvaluation.h index 1fdd3b828a..b098849188 100644 --- a/src/ScreenEvaluation.h +++ b/src/ScreenEvaluation.h @@ -49,6 +49,8 @@ enum DetailLine class ScreenEvaluation : public ScreenWithMenuElements { public: + ScreenEvaluation(); + virtual ~ScreenEvaluation(); virtual void Init(); virtual bool Input( const InputEventPlus &input ); virtual void HandleScreenMessage( const ScreenMessage SM ); diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index aff5d2b36a..4124b1a81f 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -325,6 +325,7 @@ ScreenGameplay::ScreenGameplay() m_pSongBackground = NULL; m_pSongForeground = NULL; m_bForceNoNetwork = false; + GAMESTATE->m_AdjustTokensBySongCostForFinalStageCheck= false; } void ScreenGameplay::Init() @@ -928,6 +929,7 @@ void ScreenGameplay::InitSongQueues() ScreenGameplay::~ScreenGameplay() { + GAMESTATE->m_AdjustTokensBySongCostForFinalStageCheck= true; if( this->IsFirstUpdate() ) { /* We never received any updates. That means we were deleted without being diff --git a/src/ThemeManager.cpp b/src/ThemeManager.cpp index 9f5e6bc6e5..8de94fa0ac 100644 --- a/src/ThemeManager.cpp +++ b/src/ThemeManager.cpp @@ -1317,11 +1317,21 @@ public: lua_pushstring(L, pi.sMatchingElement); return 3; } - static int GetPathF( T* p, lua_State *L ) { lua_pushstring(L, p->GetPathF(SArg(1),SArg(2)) ); return 1; } - static int GetPathG( T* p, lua_State *L ) { lua_pushstring(L, p->GetPathG(SArg(1),SArg(2)) ); return 1; } - static int GetPathB( T* p, lua_State *L ) { lua_pushstring(L, p->GetPathB(SArg(1),SArg(2)) ); return 1; } - static int GetPathS( T* p, lua_State *L ) { lua_pushstring(L, p->GetPathS(SArg(1),SArg(2)) ); return 1; } - static int GetPathO( T* p, lua_State *L ) { lua_pushstring(L, p->GetPathO(SArg(1),SArg(2)) ); return 1; } + // GENERAL_GET_PATH uses lua_toboolean instead of BArg because that makes + // it optional. -Kyz +#define GENERAL_GET_PATH(get_path_name) \ + static int get_path_name(T* p, lua_State* L) \ + { \ + lua_pushstring(L, p->get_path_name( \ + SArg(1), SArg(2), lua_toboolean(L, 3))); \ + return 1; \ + } + GENERAL_GET_PATH(GetPathF); + GENERAL_GET_PATH(GetPathG); + GENERAL_GET_PATH(GetPathB); + GENERAL_GET_PATH(GetPathS); + GENERAL_GET_PATH(GetPathO); +#undef GENERAL_GET_PATH static int RunLuaScripts( T* p, lua_State *L ) { p->RunLuaScripts(SArg(1)); return 1; }