diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 176fd6000b..fd54bfaaea 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -345,6 +345,7 @@ AllowExtraStage=When enabled (and Event Mode isn't on), allows the player to ear AllowSongDeletion=Allow players to permanently delete songs from the system by pressing Ctrl+Backspace when browsing the music wheel. AllowW1=Enable or disable fantastic judgement. NEVER - never show Fantastic, COURSES ONLY - only during nonstop/oni modes, ALWAYS - all modes of play. AllowMultipleHighScoreWithSameName=If turned off, only the highest score with a given name will be saved. If turned on, it will allow mutliple high scores with a given name to be saved. +AllowMultipleToasties=Allow multiple toasties to be shown in one song. Announcer=Choose from this list of installed announcer packs. Appearance=Appearance Appearance Options=Change the theme, announcer, and other style options. @@ -945,6 +946,7 @@ AllowExtraStage=Allow Extra Stage AllowSongDeletion=Allow Song Deletion AllowW1=Fantastic Timing AllowMultipleHighScoreWithSameName=Competitive Mode +AllowMultipleToasties=Extra Toast Alter=Alter Announcer=Announcer Appearance=Appearance diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 0f2d57c30a..0cbf7a4f1e 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -571,6 +571,7 @@ AvoidMineIncrementsCombo=false UseInternalScoring=true # When you hit it, you know. ToastyTriggersAt=250 +ToastyMinTNS='TapNoteScore_W2' [GameState] # Default song and sort. these don't really matter @@ -3128,7 +3129,7 @@ Fallback="ScreenOptionsServiceChild" NextScreen="ScreenOptionsService" PrevScreen="ScreenOptionsService" # stuff tied to arcade features -LineNames="1,2,3,4,5,6,7,8,9,HHLP,10,11,12,13,14" +LineNames="1,2,3,4,5,6,7,8,AMT,9,HHLP,10,11,12,13,14" Line1="conf,GetRankingName" Line2="conf,CoinMode" Line3="conf,SongsPerPlay" @@ -3137,6 +3138,7 @@ Line5="conf,Premium" Line6="conf,EventMode" Line7="conf,AllowMultipleHighScoreWithSameName" Line8="conf,ComboContinuesBetweenSongs" +LineAMT="conf,AllowMultipleToasties" Line9="conf,Disqualification" LineHHLP="conf,HarshHotLifePenalty" Line10="conf,FailOffForFirstStageEasy" diff --git a/src/PrefsManager.cpp b/src/PrefsManager.cpp index 2bdb98f58a..8d14c32e9d 100644 --- a/src/PrefsManager.cpp +++ b/src/PrefsManager.cpp @@ -223,6 +223,7 @@ PrefsManager::PrefsManager() : m_iSongsPerPlay ( "SongsPerPlay", 3, ValidateSongsPerPlay ), m_bDelayedCreditsReconcile ( "DelayedCreditsReconcile", false ), m_bComboContinuesBetweenSongs ( "ComboContinuesBetweenSongs", false ), + m_AllowMultipleToasties("AllowMultipleToasties", true), m_MinTNSToHideNotes("MinTNSToHideNotes", TNS_W3), m_ShowSongOptions ( "ShowSongOptions", Maybe_NO ), m_bDancePointsForOni ( "DancePointsForOni", true ), diff --git a/src/PrefsManager.h b/src/PrefsManager.h index 4aded80335..f710a1c2dd 100644 --- a/src/PrefsManager.h +++ b/src/PrefsManager.h @@ -215,6 +215,7 @@ public: Preference m_iSongsPerPlay; Preference m_bDelayedCreditsReconcile; // zuh? Preference m_bComboContinuesBetweenSongs; + Preference m_AllowMultipleToasties; Preference m_MinTNSToHideNotes; Preference m_ShowSongOptions; Preference m_bDancePointsForOni; diff --git a/src/ScoreKeeperNormal.cpp b/src/ScoreKeeperNormal.cpp index 81f815bcc5..98131d1e1b 100644 --- a/src/ScoreKeeperNormal.cpp +++ b/src/ScoreKeeperNormal.cpp @@ -51,12 +51,10 @@ void ScoreKeeperNormal::Load( m_AvoidMineIncrementsCombo.Load( "Gameplay", "AvoidMineIncrementsCombo" ); m_UseInternalScoring.Load( "Gameplay", "UseInternalScoring" ); - // Toasty triggers (idea from 3.9+) - // Multiple toasty support doesn't seem to be working right now. - // Since it's causing more problems than solutions, I'm going back to - // the old way of a single toasty trigger for now. - //m_vToastyTriggers.Load( "Gameplay", "ToastyTriggersAt" ); - m_ToastyTrigger.Load( "Gameplay", "ToastyTriggersAt" ); + // This can be a function or a number, the type is checked when needed. + // -Kyz + m_toasty_trigger.Load("Gameplay", "ToastyTriggersAt"); + m_toasty_min_tns.Load("Gameplay", "ToastyMinTNS"); // Fill in STATSMAN->m_CurStageStats, calculate multiplier int iTotalPossibleDancePoints = 0; @@ -103,9 +101,12 @@ void ScoreKeeperNormal::Load( m_pPlayerStageStats->m_iPossibleGradePoints = iTotalPossibleGradePoints; m_iScoreRemainder = 0; - m_iCurToastyCombo = 0; - //m_iCurToastyTrigger = 0; - //m_iNextToastyAt = 0; + m_cur_toasty_combo = 0; + m_cur_toasty_level= 0; + // Initialize m_next_toasty_at to 0 so that CalcNextToastyAt just needs to + // add the value. -Kyz + m_next_toasty_at= 0; + m_next_toasty_at= CalcNextToastyAt(m_cur_toasty_level); m_iMaxScoreSoFar = 0; m_iPointBonus = 0; m_iNumTapsAndHolds = 0; @@ -329,6 +330,62 @@ void ScoreKeeperNormal::AddScoreInternal( TapNoteScore score ) } } +int ScoreKeeperNormal::CalcNextToastyAt(int level) +{ + Lua* L= LUA->Get(); + m_toasty_trigger.PushSelf(L); + const int default_amount= 250; + int amount= default_amount; + bool erred= false; + switch(lua_type(L, 1)) + { + case LUA_TNUMBER: + amount= lua_tointeger(L, 1); + break; + case LUA_TFUNCTION: + { + RString err= "Error running ToastyTriggersAt: "; + LuaHelpers::Push(L, m_pPlayerState->m_PlayerNumber); + lua_pushnumber(L, level); + if(LuaHelpers::RunScriptOnStack(L, err, 2, 1, true)) + { + if(lua_isnumber(L, -1)) + { + amount= lua_tointeger(L, -1); + } + else + { + LuaHelpers::ReportScriptError("Gameplay::ToastyTriggersAt " + "function must return a number greater than 0."); + erred= true; + } + } + else + { + erred= true; + } + } + break; + default: + LuaHelpers::ReportScriptError("Gameplay::ToastyTriggersAt metric has " + "a nonsensical type, it must be a number or a function."); + erred= true; + break; + } + if(amount <= 0) + { + if(!erred) + { + LuaHelpers::ReportScriptError("The ToastyTriggersAt value cannot be " + "less than or equal to 0 because that would be silly."); + } + amount= default_amount; + } + lua_settop(L, 0); + LUA->Release(L); + return m_next_toasty_at + amount; +} + void ScoreKeeperNormal::HandleTapScore( const TapNote &tn ) { TapNoteScore tns = tn.result.tns; @@ -504,61 +561,43 @@ void ScoreKeeperNormal::HandleTapRowScore( const NoteData &nd, int iRow ) if( (GamePreferences::m_AutoPlay != PC_HUMAN || m_pPlayerState->m_PlayerOptions.GetCurrent().m_fPlayerAutoPlay != 0) && !GAMESTATE->m_bDemonstrationOrJukebox ) // cheaters always prosper >:D -aj comment edit { - m_iCurToastyCombo = 0; + m_cur_toasty_combo = 0; return; } #endif //DEBUG // Toasty combo - //vector iToastyMilestones; - switch( scoreOfLastTap ) + if(scoreOfLastTap >= m_toasty_min_tns) { - case TNS_W1: - case TNS_W2: - m_iCurToastyCombo += iNumTapsInRow; - - /* - // compile the list of toasty triggers + m_cur_toasty_combo += iNumTapsInRow; + if(m_cur_toasty_combo > m_next_toasty_at && + !GAMESTATE->m_bDemonstrationOrJukebox) { - Lua *L = LUA->Get(); - m_vToastyTriggers.PushSelf(L); - LuaHelpers::ReadArrayFromTable(iToastyMilestones, L); - lua_pop( L, 1 ); - LUA->Release(L); - } - // find out which one we're at. - if(m_iCurToastyTrigger <= int(iToastyMilestones.size())) - { - m_iNextToastyAt = iToastyMilestones[m_iCurToastyTrigger]; - } - else // out of index value? then don't make it toasty! - { - m_iNextToastyAt = -1; - } - */ - - if( m_iCurToastyCombo >= m_ToastyTrigger && - m_iCurToastyCombo - iNumTapsInRow < m_ToastyTrigger && - !GAMESTATE->m_bDemonstrationOrJukebox ) - { - SCREENMAN->PostMessageToTopScreen( SM_PlayToasty, 0 ); + ++m_cur_toasty_level; + // Broadcast the message before posting the screen message so that the + // transition layer can catch the message to know the level and respond + // accordingly. -Kyz Message msg("ToastyAchieved"); - msg.SetParam( "PlayerNumber", m_pPlayerState->m_PlayerNumber ); - msg.SetParam( "ToastyCombo", m_iCurToastyCombo ); + msg.SetParam("PlayerNumber", m_pPlayerState->m_PlayerNumber); + msg.SetParam("ToastyCombo", m_cur_toasty_combo); + msg.SetParam("Level", m_cur_toasty_level); MESSAGEMAN->Broadcast(msg); - + SCREENMAN->PostMessageToTopScreen(SM_PlayToasty, 0); // TODO: keep a pointer to the Profile. Don't index with m_PlayerNumber - PROFILEMAN->IncrementToastiesCount( m_pPlayerState->m_PlayerNumber ); - - //m_iCurToastyTrigger++; + // TODO: Make the profile count the level and combo of the toasty. -Kyz + PROFILEMAN->IncrementToastiesCount(m_pPlayerState->m_PlayerNumber); + m_next_toasty_at= CalcNextToastyAt(m_cur_toasty_level); } - break; - default: - m_iCurToastyCombo = 0; + } + else + { + m_cur_toasty_combo = 0; + m_cur_toasty_level= 0; + m_next_toasty_at= 0; + m_next_toasty_at= CalcNextToastyAt(m_cur_toasty_level); Message msg("ToastyDropped"); msg.SetParam( "PlayerNumber", m_pPlayerState->m_PlayerNumber ); MESSAGEMAN->Broadcast(msg); - break; } // TODO: Remove indexing with PlayerNumber @@ -570,7 +609,7 @@ void ScoreKeeperNormal::HandleTapRowScore( const NoteData &nd, int iRow ) Message msg( "ScoreChanged" ); msg.SetParam( "PlayerNumber", m_pPlayerState->m_PlayerNumber ); msg.SetParam( "MultiPlayer", m_pPlayerState->m_mp ); - msg.SetParam( "ToastyCombo", m_iCurToastyCombo ); + msg.SetParam( "ToastyCombo", m_cur_toasty_combo ); MESSAGEMAN->Broadcast( msg ); } diff --git a/src/ScoreKeeperNormal.h b/src/ScoreKeeperNormal.h index 89e444f392..c648765b21 100644 --- a/src/ScoreKeeperNormal.h +++ b/src/ScoreKeeperNormal.h @@ -18,6 +18,7 @@ AutoScreenMessage( SM_PlayToasty ); class ScoreKeeperNormal: public ScoreKeeper { void AddScoreInternal( TapNoteScore score ); + int CalcNextToastyAt(int level); int m_iScoreRemainder; int m_iMaxPossiblePoints; @@ -26,9 +27,9 @@ class ScoreKeeperNormal: public ScoreKeeper int m_iNumTapsAndHolds; int m_iMaxScoreSoFar; // for nonstop scoring int m_iPointBonus; // the difference to award at the end - int m_iCurToastyCombo; - //int m_iCurToastyTrigger; - //int m_iNextToastyAt; + int m_cur_toasty_combo; + int m_cur_toasty_level; + int m_next_toasty_at; bool m_bIsLastSongInCourse; bool m_bIsBeginner; @@ -43,8 +44,8 @@ class ScoreKeeperNormal: public ScoreKeeper ThemeMetric m_AvoidMineIncrementsCombo; ThemeMetric m_UseInternalScoring; - //ThemeMetric m_vToastyTriggers; - ThemeMetric m_ToastyTrigger; + ThemeMetric m_toasty_min_tns; + ThemeMetric m_toasty_trigger; vector m_apSteps; diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index 7b307f3055..f5a0065131 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -2874,10 +2874,15 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) } else if( SM == SM_PlayToasty ) { - // todo: make multiple toasties work -aj - if( g_bEasterEggs ) - if( !m_Toasty.IsTransitioning() && !m_Toasty.IsFinished() ) // don't play if we've already played it once + if(g_bEasterEggs) + { + if(PREFSMAN->m_AllowMultipleToasties || + m_Toasty.IsWaiting()) + { + m_Toasty.Reset(); m_Toasty.StartTransitioning(); + } + } } else if( ScreenMessageHelpers::ScreenMessageToString(SM).find("0Combo") != string::npos ) { diff --git a/src/ScreenOptionsMasterPrefs.cpp b/src/ScreenOptionsMasterPrefs.cpp index cb87d495dd..1d2f1775ad 100644 --- a/src/ScreenOptionsMasterPrefs.cpp +++ b/src/ScreenOptionsMasterPrefs.cpp @@ -785,6 +785,7 @@ static void InitializeConfOptions() ADD( ConfOption( "AllowExtraStage", MovePref, "Off","On" ) ); ADD( ConfOption( "AllowMultipleHighScoreWithSameName", MovePref, "Off", "On" ) ); ADD( ConfOption( "ComboContinuesBetweenSongs", MovePref, "Off", "On") ); + ADD(ConfOption("AllowMultipleToasties", MovePref, "Off", "On")); ADD( ConfOption( "Disqualification", MovePref, "Off","On" ) ); ADD( ConfOption( "HarshHotLifePenalty", MovePref, "Off", "On") ); ADD( ConfOption( "FailOffForFirstStageEasy", MovePref, "Off","On" ) ); diff --git a/src/Transition.h b/src/Transition.h index 460eae535f..b39c2dba9d 100644 --- a/src/Transition.h +++ b/src/Transition.h @@ -22,6 +22,7 @@ public: virtual float GetTweenTimeLeft() const; void Reset(); // explicitly allow transitioning again + bool IsWaiting() const { return m_State == waiting; }; bool IsTransitioning() const { return m_State == transitioning; }; bool IsFinished() const { return m_State == finished; };