2012-07-03 12:51:03 -05:00
# include "global.h"
# include "ScreenGameplay.h"
# include "SongManager.h"
# include "ScreenManager.h"
# include "GameConstantsAndTypes.h"
# include "PrefsManager.h"
# include "GamePreferences.h"
# include "GameManager.h"
# include "RageFileManager.h"
# include "Steps.h"
# include "RageLog.h"
# include "LifeMeter.h"
# include "LifeMeterBar.h"
# include "GameState.h"
# include "ScoreDisplayNormal.h"
# include "ScoreDisplayPercentage.h"
# include "ScoreDisplayLifeTime.h"
# include "ScoreDisplayOni.h"
# include "ScoreDisplayRave.h"
# include "ThemeManager.h"
# include "RageTimer.h"
# include "ScoreKeeperNormal.h"
# include "ScoreKeeperRave.h"
# include "LyricsLoader.h"
# include "ActorUtil.h"
2015-04-03 14:19:28 -06:00
# include "ArrowEffects.h"
2012-07-03 12:51:03 -05:00
# include "RageSoundManager.h"
# include "RageSoundReader.h"
# include "RageTextureManager.h"
# include "GameSoundManager.h"
# include "CombinedLifeMeterTug.h"
# include "Inventory.h"
# include "Course.h"
# include "NoteDataUtil.h"
# include "UnlockManager.h"
# include "LightsManager.h"
# include "ProfileManager.h"
# include "StatsManager.h"
# include "PlayerAI.h" // for NUM_SKILL_LEVELS
# include "DancingCharacters.h"
# include "ScreenDimensions.h"
# include "ThemeMetric.h"
# include "PlayerState.h"
# include "Style.h"
# include "LuaManager.h"
# include "MemoryCardManager.h"
# include "CommonMetrics.h"
# include "InputMapper.h"
# include "Game.h"
# include "ActiveAttackList.h"
# include "Player.h"
# include "StepsDisplay.h"
# include "XmlFile.h"
# include "Background.h"
# include "Foreground.h"
# include "ScreenSaveSync.h"
# include "AdjustSync.h"
# include "SongUtil.h"
# include "Song.h"
# include "XmlFileUtil.h"
# include "Profile.h" // for replay data stuff
2018-04-20 18:27:37 -07:00
# include "RageDisplay.h"
2012-07-03 12:51:03 -05:00
2023-04-19 14:22:59 +02:00
# include <cmath>
2023-04-19 23:04:25 +02:00
# include <cstddef>
2023-04-19 14:22:59 +02:00
2012-07-03 12:51:03 -05:00
// Defines
# define SHOW_LIFE_METER_FOR_DISABLED_PLAYERS THEME->GetMetricB(m_sName,"ShowLifeMeterForDisabledPlayers")
# define SHOW_SCORE_IN_RAVE THEME->GetMetricB(m_sName,"ShowScoreInRave")
# define SONG_POSITION_METER_WIDTH THEME->GetMetricF(m_sName,"SongPositionMeterWidth")
# define STOP_COURSE_EARLY THEME->GetMetricB(m_sName,"StopCourseEarly") // evaluate this every time it's used
static ThemeMetric < float > INITIAL_BACKGROUND_BRIGHTNESS ( " ScreenGameplay " , " InitialBackgroundBrightness " ) ;
static ThemeMetric < float > SECONDS_BETWEEN_COMMENTS ( " ScreenGameplay " , " SecondsBetweenComments " ) ;
static ThemeMetric < RString > SCORE_KEEPER_CLASS ( " ScreenGameplay " , " ScoreKeeperClass " ) ;
2014-08-19 23:58:35 -06:00
static ThemeMetric < bool > FORCE_IMMEDIATE_FAIL_FOR_BATTERY ( " ScreenGameplay " , " ForceImmediateFailForBattery " ) ;
2012-07-03 12:51:03 -05:00
AutoScreenMessage ( SM_PlayGo ) ;
// received while STATE_DANCING
AutoScreenMessage ( SM_LoadNextSong ) ;
AutoScreenMessage ( SM_StartLoadingNextSong ) ;
// received while STATE_OUTRO
AutoScreenMessage ( SM_DoPrevScreen ) ;
AutoScreenMessage ( SM_DoNextScreen ) ;
// received while STATE_INTRO
AutoScreenMessage ( SM_StartHereWeGo ) ;
AutoScreenMessage ( SM_StopHereWeGo ) ;
AutoScreenMessage ( SM_BattleTrickLevel1 ) ;
AutoScreenMessage ( SM_BattleTrickLevel2 ) ;
AutoScreenMessage ( SM_BattleTrickLevel3 ) ;
static Preference < bool > g_bCenter1Player ( " Center1Player " , false ) ;
static Preference < bool > g_bShowLyrics ( " ShowLyrics " , true ) ;
static Preference < bool > g_bEasterEggs ( " EasterEggs " , true ) ;
PlayerInfo : : PlayerInfo ( ) : m_pn ( PLAYER_INVALID ) , m_mp ( MultiPlayer_Invalid ) ,
m_bIsDummy ( false ) , m_iDummyIndex ( 0 ) , m_iAddToDifficulty ( 0 ) ,
2020-10-20 02:16:02 +09:00
m_bPlayerEnabled ( false ) , m_PlayerStateDummy ( ) ,
2012-07-03 12:51:03 -05:00
m_PlayerStageStatsDummy ( ) , m_SoundEffectControl ( ) ,
2020-10-20 02:16:02 +09:00
m_vpStepsQueue ( ) , m_asModifiersQueue ( ) , m_pLifeMeter ( nullptr ) ,
2013-05-03 23:39:52 -04:00
m_ptextCourseSongNumber ( nullptr ) , m_ptextStepsDescription ( nullptr ) ,
m_pPrimaryScoreDisplay ( nullptr ) , m_pSecondaryScoreDisplay ( nullptr ) ,
m_pPrimaryScoreKeeper ( nullptr ) , m_pSecondaryScoreKeeper ( nullptr ) ,
m_ptextPlayerOptions ( nullptr ) , m_pActiveAttackList ( nullptr ) ,
2020-10-20 02:16:02 +09:00
m_NoteData ( ) , m_pPlayer ( nullptr ) , m_pInventory ( nullptr ) ,
2013-05-03 23:39:52 -04:00
m_pStepsDisplay ( nullptr ) , m_sprOniGameOver ( ) { }
2012-07-03 12:51:03 -05:00
void PlayerInfo : : Load ( PlayerNumber pn , MultiPlayer mp , bool bShowNoteField , int iAddToDifficulty )
{
m_pn = pn ;
m_mp = mp ;
m_bPlayerEnabled = IsEnabled ( ) ;
m_bIsDummy = false ;
m_iAddToDifficulty = iAddToDifficulty ;
2013-05-03 23:39:52 -04:00
m_pLifeMeter = nullptr ;
m_ptextCourseSongNumber = nullptr ;
m_ptextStepsDescription = nullptr ;
2012-07-03 12:51:03 -05:00
if ( ! IsMultiPlayer ( ) )
{
2012-12-27 16:59:35 -05:00
PlayMode mode = GAMESTATE - > m_PlayMode ;
switch ( mode )
2012-07-03 12:51:03 -05:00
{
case PLAY_MODE_REGULAR :
case PLAY_MODE_NONSTOP :
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
if ( PREFSMAN - > m_bPercentageScoring )
m_pPrimaryScoreDisplay = new ScoreDisplayPercentage ;
else
m_pPrimaryScoreDisplay = new ScoreDisplayNormal ;
break ;
case PLAY_MODE_ONI :
case PLAY_MODE_ENDLESS :
2014-08-06 23:26:05 -06:00
if ( GAMESTATE - > m_pPlayerState [ pn ] - > m_PlayerOptions . GetStage ( ) . m_LifeType = = LifeType_Time )
2012-07-03 12:51:03 -05:00
m_pPrimaryScoreDisplay = new ScoreDisplayLifeTime ;
else
m_pPrimaryScoreDisplay = new ScoreDisplayOni ;
break ;
default :
2012-12-27 16:59:35 -05:00
FAIL_M ( ssprintf ( " Invalid PlayMode: %i " , mode ) ) ;
2012-07-03 12:51:03 -05:00
}
}
PlayerState * const pPlayerState = GetPlayerState ( ) ;
PlayerStageStats * const pPlayerStageStats = GetPlayerStageStats ( ) ;
if ( m_pPrimaryScoreDisplay )
m_pPrimaryScoreDisplay - > Init ( pPlayerState , pPlayerStageStats ) ;
switch ( GAMESTATE - > m_PlayMode )
{
case PLAY_MODE_RAVE :
m_pSecondaryScoreDisplay = new ScoreDisplayRave ;
m_pSecondaryScoreDisplay - > SetName ( " ScoreDisplayRave " ) ;
default :
break ;
}
if ( m_pSecondaryScoreDisplay )
m_pSecondaryScoreDisplay - > Init ( pPlayerState , pPlayerStageStats ) ;
m_pPrimaryScoreKeeper = ScoreKeeper : : MakeScoreKeeper ( SCORE_KEEPER_CLASS , pPlayerState , pPlayerStageStats ) ;
switch ( GAMESTATE - > m_PlayMode )
{
case PLAY_MODE_RAVE :
m_pSecondaryScoreKeeper = new ScoreKeeperRave ( pPlayerState , pPlayerStageStats ) ;
default :
break ;
}
2013-05-03 23:39:52 -04:00
m_ptextPlayerOptions = nullptr ;
m_pActiveAttackList = nullptr ;
2012-07-03 12:51:03 -05:00
m_pPlayer = new Player ( m_NoteData , bShowNoteField ) ;
2013-05-03 23:39:52 -04:00
m_pInventory = nullptr ;
m_pStepsDisplay = nullptr ;
2012-07-03 12:51:03 -05:00
if ( IsMultiPlayer ( ) )
{
pPlayerState - > m_PlayerOptions = GAMESTATE - > m_pPlayerState [ PLAYER_1 ] - > m_PlayerOptions ;
}
}
void PlayerInfo : : LoadDummyP1 ( int iDummyIndex , int iAddToDifficulty )
{
m_pn = PLAYER_1 ;
m_bPlayerEnabled = IsEnabled ( ) ;
m_bIsDummy = true ;
m_iDummyIndex = iDummyIndex ;
m_iAddToDifficulty = iAddToDifficulty ;
// don't init any of the scoring objects
m_pPlayer = new Player ( m_NoteData , true ) ;
// PlayerOptions needs to be set now so that we load the correct NoteSkin.
m_PlayerStateDummy = * GAMESTATE - > m_pPlayerState [ PLAYER_1 ] ;
}
PlayerInfo : : ~ PlayerInfo ( )
{
SAFE_DELETE ( m_pLifeMeter ) ;
SAFE_DELETE ( m_ptextCourseSongNumber ) ;
SAFE_DELETE ( m_ptextStepsDescription ) ;
SAFE_DELETE ( m_pPrimaryScoreDisplay ) ;
SAFE_DELETE ( m_pSecondaryScoreDisplay ) ;
SAFE_DELETE ( m_pPrimaryScoreKeeper ) ;
SAFE_DELETE ( m_pSecondaryScoreKeeper ) ;
SAFE_DELETE ( m_ptextPlayerOptions ) ;
SAFE_DELETE ( m_pActiveAttackList ) ;
SAFE_DELETE ( m_pPlayer ) ;
SAFE_DELETE ( m_pInventory ) ;
SAFE_DELETE ( m_pStepsDisplay ) ;
}
void PlayerInfo : : ShowOniGameOver ( )
{
m_sprOniGameOver - > PlayCommand ( " Die " ) ;
}
PlayerState * PlayerInfo : : GetPlayerState ( )
{
if ( m_bIsDummy )
return & m_PlayerStateDummy ;
return IsMultiPlayer ( ) ?
GAMESTATE - > m_pMultiPlayerState [ GetPlayerStateAndStageStatsIndex ( ) ] :
GAMESTATE - > m_pPlayerState [ GetPlayerStateAndStageStatsIndex ( ) ] ;
}
PlayerStageStats * PlayerInfo : : GetPlayerStageStats ( )
{
// multiplayer chooses the PlayerStageStats with the highest score on StageFinalized
if ( m_bIsDummy | | IsMultiPlayer ( ) )
return & m_PlayerStageStatsDummy ;
return & STATSMAN - > m_CurStageStats . m_player [ GetPlayerStateAndStageStatsIndex ( ) ] ;
}
bool PlayerInfo : : IsEnabled ( )
{
if ( m_pn ! = PLAYER_INVALID )
return GAMESTATE - > IsPlayerEnabled ( m_pn ) ;
if ( m_mp ! = MultiPlayer_Invalid )
return GAMESTATE - > IsMultiPlayerEnabled ( m_mp ) ;
else if ( m_bIsDummy )
return true ;
2012-12-27 16:59:35 -05:00
FAIL_M ( " Invalid non-dummy player. " ) ;
2012-07-03 12:51:03 -05:00
}
2022-07-10 18:28:56 +03:00
std : : vector < PlayerInfo > : : iterator
GetNextEnabledPlayerInfo ( std : : vector < PlayerInfo > : : iterator iter , std : : vector < PlayerInfo > & v )
2012-07-03 12:51:03 -05:00
{
for ( ; iter ! = v . end ( ) ; + + iter )
{
if ( ! iter - > m_bPlayerEnabled )
continue ;
return iter ;
}
return iter ;
}
2022-07-10 18:28:56 +03:00
std : : vector < PlayerInfo > : : iterator
GetNextEnabledPlayerInfoNotDummy ( std : : vector < PlayerInfo > : : iterator iter , std : : vector < PlayerInfo > & v )
2012-07-03 12:51:03 -05:00
{
for ( ; iter ! = v . end ( ) ; iter + + )
{
if ( iter - > m_bIsDummy )
continue ;
if ( ! iter - > m_bPlayerEnabled )
continue ;
return iter ;
}
return iter ;
}
2022-07-10 18:28:56 +03:00
std : : vector < PlayerInfo > : : iterator
GetNextEnabledPlayerNumberInfo ( std : : vector < PlayerInfo > : : iterator iter , std : : vector < PlayerInfo > & v )
2012-07-03 12:51:03 -05:00
{
for ( ; iter ! = v . end ( ) ; + + iter )
{
if ( iter - > m_bIsDummy )
continue ;
if ( ! iter - > m_bPlayerEnabled )
continue ;
if ( iter - > m_mp ! = MultiPlayer_Invalid )
continue ;
return iter ;
}
return iter ;
}
2022-07-10 18:28:56 +03:00
std : : vector < PlayerInfo > : : iterator
GetNextPlayerNumberInfo ( std : : vector < PlayerInfo > : : iterator iter , std : : vector < PlayerInfo > & v )
2012-07-03 12:51:03 -05:00
{
for ( ; iter ! = v . end ( ) ; + + iter )
{
if ( iter - > m_bIsDummy )
continue ;
if ( iter - > m_pn = = PLAYER_INVALID )
continue ;
return iter ;
}
return iter ;
}
2022-07-10 18:28:56 +03:00
std : : vector < PlayerInfo > : : iterator
GetNextVisiblePlayerInfo ( std : : vector < PlayerInfo > : : iterator iter , std : : vector < PlayerInfo > & v )
2012-07-03 12:51:03 -05:00
{
for ( ; iter ! = v . end ( ) ; + + iter )
{
if ( ! iter - > m_pPlayer - > HasVisibleParts ( ) )
continue ;
return iter ;
}
return iter ;
}
////////////////////////////////////////////////////////////////////////////////
ScreenGameplay : : ScreenGameplay ( )
{
2013-05-03 23:39:52 -04:00
m_pSongBackground = nullptr ;
m_pSongForeground = nullptr ;
2016-06-09 16:05:49 -06:00
m_delaying_ready_announce = false ;
2014-09-07 04:05:10 -06:00
GAMESTATE - > m_AdjustTokensBySongCostForFinalStageCheck = false ;
2012-07-03 12:51:03 -05:00
}
void ScreenGameplay : : Init ( )
{
SubscribeToMessage ( " Judgment " ) ;
PLAYER_TYPE . Load ( m_sName , " PlayerType " ) ;
PLAYER_INIT_COMMAND . Load ( m_sName , " PlayerInitCommand " ) ;
GIVE_UP_START_TEXT . Load ( m_sName , " GiveUpStartText " ) ;
GIVE_UP_BACK_TEXT . Load ( m_sName , " GiveUpBackText " ) ;
GIVE_UP_ABORTED_TEXT . Load ( m_sName , " GiveUpAbortedText " ) ;
2015-05-05 13:17:15 -06:00
SKIP_SONG_TEXT . Load ( m_sName , " SkipSongText " ) ;
2014-04-15 12:27:54 -05:00
GIVE_UP_SECONDS . Load ( m_sName , " GiveUpSeconds " ) ;
2012-07-03 12:51:03 -05:00
MUSIC_FADE_OUT_SECONDS . Load ( m_sName , " MusicFadeOutSeconds " ) ;
OUT_TRANSITION_LENGTH . Load ( m_sName , " OutTransitionLength " ) ;
COURSE_TRANSITION_LENGTH . Load ( m_sName , " CourseTransitionLength " ) ;
BEGIN_FAILED_DELAY . Load ( m_sName , " BeginFailedDelay " ) ;
MIN_SECONDS_TO_STEP . Load ( m_sName , " MinSecondsToStep " ) ;
MIN_SECONDS_TO_MUSIC . Load ( m_sName , " MinSecondsToMusic " ) ;
MIN_SECONDS_TO_STEP_NEXT_SONG . Load ( m_sName , " MinSecondsToStepNextSong " ) ;
START_GIVES_UP . Load ( m_sName , " StartGivesUp " ) ;
BACK_GIVES_UP . Load ( m_sName , " BackGivesUp " ) ;
2015-05-05 13:17:15 -06:00
SELECT_SKIPS_SONG . Load ( m_sName , " SelectSkipsSong " ) ;
2012-07-03 12:51:03 -05:00
GIVING_UP_GOES_TO_PREV_SCREEN . Load ( m_sName , " GivingUpGoesToPrevScreen " ) ;
FAIL_ON_MISS_COMBO . Load ( m_sName , " FailOnMissCombo " ) ;
ALLOW_CENTER_1_PLAYER . Load ( m_sName , " AllowCenter1Player " ) ;
// configurable:
UNPAUSE_WITH_START . Load ( m_sName , " UnpauseWithStart " ) ;
SURVIVAL_MOD_OVERRIDE . Load ( m_sName , " SurvivalModOverride " ) ;
2014-05-05 14:16:08 -06:00
// Default values. The theme can set its own through the Lua interface.
m_HasteTurningPoints . clear ( ) ;
m_HasteTurningPoints . push_back ( - 1 ) ;
m_HasteTurningPoints . push_back ( 0 ) ;
2014-07-26 19:01:49 -05:00
m_HasteTurningPoints . push_back ( 0.3f ) ;
2014-05-05 14:16:08 -06:00
m_HasteTurningPoints . push_back ( 1 ) ;
m_HasteAddAmounts . clear ( ) ;
2014-07-26 19:01:49 -05:00
m_HasteAddAmounts . push_back ( - 0.5f ) ;
2014-05-05 14:16:08 -06:00
m_HasteAddAmounts . push_back ( 0 ) ;
2014-07-26 19:01:49 -05:00
m_HasteAddAmounts . push_back ( 0.2f ) ;
m_HasteAddAmounts . push_back ( 0.5f ) ;
2014-05-05 14:16:08 -06:00
m_fHasteTimeBetweenUpdates = 4 ;
2014-07-26 19:01:49 -05:00
m_fHasteLifeSwitchPoint = 0.5f ;
2014-05-05 14:16:08 -06:00
m_fCurrHasteRate = 1 ; // Should this be in BeginSong? Not sure whether it should carry over between songs.
2012-07-03 12:51:03 -05:00
if ( UseSongBackgroundAndForeground ( ) )
{
m_pSongBackground = new Background ;
m_pSongForeground = new Foreground ;
}
ScreenWithMenuElements : : Init ( ) ;
this - > FillPlayerInfo ( m_vPlayerInfo ) ;
{
ASSERT_M ( ! m_vPlayerInfo . empty ( ) , " m_vPlayerInfo must be filled by FillPlayerInfo " ) ;
int iNumEnabledPlayers = 0 ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
+ + iNumEnabledPlayers ;
/* If this is 0, we have no active players and havn't been initialized correctly. */
ASSERT ( iNumEnabledPlayers > 0 ) ;
}
/* Pause MEMCARDMAN. If a memory card is removed, we don't want to interrupt the
* player by making a noise until the game finishes. */
if ( ! GAMESTATE - > m_bDemonstrationOrJukebox )
MEMCARDMAN - > PauseMountingThread ( ) ;
2013-05-03 23:39:52 -04:00
m_pSoundMusic = nullptr ;
2015-09-03 14:39:57 -06:00
set_paused_internal ( false ) ;
2012-07-03 12:51:03 -05:00
2013-05-03 23:39:52 -04:00
m_pCombinedLifeMeter = nullptr ;
2012-07-03 12:51:03 -05:00
2013-05-03 23:11:42 -04:00
if ( GAMESTATE - > m_pCurSong = = nullptr & & GAMESTATE - > m_pCurCourse = = nullptr )
2012-07-03 12:51:03 -05:00
return ; // ScreenDemonstration will move us to the next screen. We just need to survive for one update without crashing.
/* Save settings to the profile now. Don't do this on extra stages, since the
* user doesn't have full control; saving would force profiles to Difficulty_Hard
* and save over their default modifiers every time someone got an extra stage.
* Do this before course modifiers are set up. */
if ( ! GAMESTATE - > IsAnExtraStage ( ) )
{
FOREACH_HumanPlayer ( pn )
GAMESTATE - > SaveCurrentSettingsToProfile ( pn ) ;
}
/* Called once per stage (single song or single course). */
GAMESTATE - > BeginStage ( ) ;
int player = 1 ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
unsigned int count = pi - > m_vpStepsQueue . size ( ) ;
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
for ( unsigned int i = 0 ; i < count ; i + + )
{
Steps * curSteps = pi - > m_vpStepsQueue [ i ] ;
if ( curSteps - > IsNoteDataEmpty ( ) )
{
if ( curSteps - > GetNoteDataFromSimfile ( ) )
{
LOG - > Trace ( " Notes should be loaded for player %d " , player ) ;
}
2020-10-20 02:16:02 +09:00
else
2012-07-03 12:51:03 -05:00
{
LOG - > Trace ( " Error loading notes for player %d " , player ) ;
}
}
}
player + + ;
}
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
if ( ! GAMESTATE - > IsCourseMode ( ) & & ! GAMESTATE - > m_bDemonstrationOrJukebox )
{
// fill in difficulty of CPU players with that of the first human player
// this should not need to worry about step content.
FOREACH_PotentialCpuPlayer ( p )
2014-12-07 01:53:17 -07:00
{
PlayerNumber human_pn = GAMESTATE - > GetFirstHumanPlayer ( ) ;
GAMESTATE - > m_pCurSteps [ p ] . Set ( GAMESTATE - > m_pCurSteps [ human_pn ] ) ;
if ( GAMESTATE - > GetCurrentGame ( ) - > m_PlayersHaveSeparateStyles )
{
GAMESTATE - > SetCurrentStyle ( GAMESTATE - > GetCurrentStyle ( human_pn ) , p ) ;
}
}
2012-07-03 12:51:03 -05:00
FOREACH_EnabledPlayer ( p )
2013-05-03 23:01:54 -04:00
ASSERT ( GAMESTATE - > m_pCurSteps [ p ] . Get ( ) ! = nullptr ) ;
2012-07-03 12:51:03 -05:00
}
/* Increment the course play count. */
if ( GAMESTATE - > IsCourseMode ( ) & & ! GAMESTATE - > m_bDemonstrationOrJukebox )
FOREACH_EnabledPlayer ( p )
PROFILEMAN - > IncrementCoursePlayCount ( GAMESTATE - > m_pCurCourse , GAMESTATE - > m_pCurTrail [ p ] , p ) ;
STATSMAN - > m_CurStageStats . m_Stage = GAMESTATE - > GetCurrentStage ( ) ;
STATSMAN - > m_CurStageStats . m_iStageIndex = GAMESTATE - > m_iCurrentStageIndex ;
STATSMAN - > m_CurStageStats . m_playMode = GAMESTATE - > m_PlayMode ;
2014-12-07 01:53:17 -07:00
FOREACH_PlayerNumber ( pn )
{
STATSMAN - > m_CurStageStats . m_player [ pn ] . m_pStyle = GAMESTATE - > GetCurrentStyle ( pn ) ;
}
FOREACH_MultiPlayer ( pn )
{
STATSMAN - > m_CurStageStats . m_multiPlayer [ pn ] . m_pStyle = GAMESTATE - > GetCurrentStyle ( PLAYER_INVALID ) ;
}
2012-07-03 12:51:03 -05:00
/* Record combo rollover. */
FOREACH_EnabledPlayerInfoNotDummy ( m_vPlayerInfo , pi )
pi - > GetPlayerStageStats ( ) - > UpdateComboList ( 0 , true ) ;
m_DancingState = STATE_INTRO ;
// Set this in LoadNextSong()
//m_fTimeLeftBeforeDancingComment = SECONDS_BETWEEN_COMMENTS;
m_bZeroDeltaOnNextUpdate = false ;
if ( m_pSongBackground )
{
m_pSongBackground - > SetName ( " SongBackground " ) ;
m_pSongBackground - > SetDrawOrder ( DRAW_ORDER_BEFORE_EVERYTHING ) ;
ActorUtil : : LoadAllCommands ( * m_pSongBackground , m_sName ) ;
this - > AddChild ( m_pSongBackground ) ;
}
if ( m_pSongForeground )
{
m_pSongForeground - > SetName ( " SongForeground " ) ;
m_pSongForeground - > SetDrawOrder ( DRAW_ORDER_OVERLAY + 1 ) ; // on top of the overlay, but under transitions
ActorUtil : : LoadAllCommands ( * m_pSongForeground , m_sName ) ;
this - > AddChild ( m_pSongForeground ) ;
}
if ( PREFSMAN - > m_bShowBeginnerHelper )
{
m_BeginnerHelper . SetDrawOrder ( DRAW_ORDER_BEFORE_EVERYTHING ) ;
m_BeginnerHelper . SetXY ( SCREEN_CENTER_X , SCREEN_CENTER_Y ) ;
this - > AddChild ( & m_BeginnerHelper ) ;
}
if ( ! GAMESTATE - > m_bDemonstrationOrJukebox ) // only load if we're going to use it
{
m_Toasty . Load ( THEME - > GetPathB ( m_sName , " toasty " ) ) ;
this - > AddChild ( & m_Toasty ) ;
}
2014-12-07 01:53:17 -07:00
// Use the margin function to calculate where the notefields should be and
// what size to zoom them to. This way, themes get margins to put cut-ins
// in, and the engine can have players on different styles without the
// notefields overlapping. -Kyz
LuaReference margarine ;
float margins [ NUM_PLAYERS ] [ 2 ] ;
FOREACH_PlayerNumber ( pn )
{
margins [ pn ] [ 0 ] = 40 ;
margins [ pn ] [ 1 ] = 40 ;
}
THEME - > GetMetric ( m_sName , " MarginFunction " , margarine ) ;
if ( margarine . GetLuaType ( ) ! = LUA_TFUNCTION )
{
LuaHelpers : : ReportScriptErrorFmt ( " MarginFunction metric for %s must be a function. " , m_sName . c_str ( ) ) ;
}
else
{
Lua * L = LUA - > Get ( ) ;
margarine . PushSelf ( L ) ;
lua_createtable ( L , 0 , 0 ) ;
int next_player_slot = 1 ;
FOREACH_EnabledPlayer ( pn )
{
Enum : : Push ( L , pn ) ;
lua_rawseti ( L , - 2 , next_player_slot ) ;
+ + next_player_slot ;
}
Enum : : Push ( L , GAMESTATE - > GetCurrentStyle ( PLAYER_INVALID ) - > m_StyleType ) ;
2017-04-04 17:21:55 -07:00
RString err = " Error running MarginFunction: " ;
if ( LuaHelpers : : RunScriptOnStack ( L , err , 2 , 3 , true ) )
2014-12-07 01:53:17 -07:00
{
RString marge = " Margin value must be a number. " ;
margins [ PLAYER_1 ] [ 0 ] = SafeFArg ( L , - 3 , marge , 40 ) ;
float center = SafeFArg ( L , - 2 , marge , 80 ) ;
margins [ PLAYER_1 ] [ 1 ] = center / 2.0f ;
margins [ PLAYER_2 ] [ 0 ] = center / 2.0f ;
margins [ PLAYER_2 ] [ 1 ] = SafeFArg ( L , - 1 , marge , 40 ) ;
}
lua_settop ( L , 0 ) ;
LUA - > Release ( L ) ;
}
float left_edge [ NUM_PLAYERS ] = { 0.0f , SCREEN_WIDTH / 2.0f } ;
2012-07-03 12:51:03 -05:00
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
RString sName = ssprintf ( " Player%s " , pi - > GetName ( ) . c_str ( ) ) ;
pi - > m_pPlayer - > SetName ( sName ) ;
2014-12-07 01:53:17 -07:00
Style const * style = GAMESTATE - > GetCurrentStyle ( pi - > m_pn ) ;
float style_width = style - > GetWidth ( pi - > m_pn ) ;
float edge = left_edge [ pi - > m_pn ] ;
float screen_space ;
float field_space ;
float left_marge ;
float right_marge ;
# define CENTER_PLAYER_BLOCK \
{ \
edge= 0.0f; \
screen_space= SCREEN_WIDTH; \
left_marge= margins[PLAYER_1][0]; \
right_marge= margins[PLAYER_2][1]; \
field_space= screen_space - left_marge - right_marge; \
2012-07-03 12:51:03 -05:00
}
2020-10-20 02:16:02 +09:00
// If pi->m_pn is set, then the player will be visible. If not, then it's not
2014-12-07 01:53:17 -07:00
// visible and don't bother setting its position.
if ( GAMESTATE - > m_bMultiplayer & & ! pi - > m_bIsDummy )
CENTER_PLAYER_BLOCK
2012-07-03 12:51:03 -05:00
else
{
2014-12-07 01:53:17 -07:00
screen_space = SCREEN_WIDTH / 2.0f ;
left_marge = margins [ pi - > m_pn ] [ 0 ] ;
right_marge = margins [ pi - > m_pn ] [ 1 ] ;
field_space = screen_space - left_marge - right_marge ;
if ( Center1Player ( ) | |
2015-01-20 19:58:53 -07:00
style - > m_StyleType = = StyleType_TwoPlayersSharedSides | |
2014-12-07 01:53:17 -07:00
( style_width > field_space & & GAMESTATE - > GetNumPlayersEnabled ( ) = = 1
& & ( bool ) ALLOW_CENTER_1_PLAYER ) )
CENTER_PLAYER_BLOCK
2012-07-03 12:51:03 -05:00
}
2014-12-07 01:53:17 -07:00
# undef CENTER_PLAYER_BLOCK
float player_x = edge + left_marge + ( field_space / 2.0f ) ;
float field_zoom = field_space / style_width ;
/*
LuaHelpers::ReportScriptErrorFmt("Positioning player %d at %.0f: "
"screen_space %.0f, left_edge %.0f, field_space %.0f, left_marge %.0f,"
" right_marge %.0f, style_width %.0f, field_zoom %.2f.",
pi->m_pn+1, player_x, screen_space, left_edge[pi->m_pn], field_space,
left_marge, right_marge, style_width, field_zoom);
*/
2022-07-10 18:28:56 +03:00
pi - > GetPlayerState ( ) - > m_NotefieldZoom = std : : min ( 1.0f , field_zoom ) ;
2014-12-07 01:53:17 -07:00
pi - > m_pPlayer - > SetX ( player_x ) ;
2012-07-03 12:51:03 -05:00
pi - > m_pPlayer - > RunCommands ( PLAYER_INIT_COMMAND ) ;
//ActorUtil::LoadAllCommands(pi->m_pPlayer, m_sName);
this - > AddChild ( pi - > m_pPlayer ) ;
pi - > m_pPlayer - > PlayCommand ( " On " ) ;
}
FOREACH_EnabledPlayerInfoNotDummy ( m_vPlayerInfo , pi )
{
if ( pi - > m_pPlayer - > HasVisibleParts ( ) )
{
pi - > m_sprOniGameOver . Load ( THEME - > GetPathG ( m_sName , " oni gameover " ) ) ;
pi - > m_sprOniGameOver - > SetName ( ssprintf ( " OniGameOver%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_sprOniGameOver ) ;
this - > AddChild ( pi - > m_sprOniGameOver ) ;
}
}
m_NextSong . Load ( THEME - > GetPathB ( m_sName , " next course song " ) ) ;
m_NextSong . SetDrawOrder ( DRAW_ORDER_TRANSITIONS - 1 ) ;
this - > AddChild ( & m_NextSong ) ;
// Add combined life meter
switch ( GAMESTATE - > m_PlayMode )
{
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
m_pCombinedLifeMeter = new CombinedLifeMeterTug ;
m_pCombinedLifeMeter - > SetName ( " CombinedLife " ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( * m_pCombinedLifeMeter ) ;
this - > AddChild ( m_pCombinedLifeMeter ) ;
default :
break ;
}
// Add individual life meter
switch ( GAMESTATE - > m_PlayMode )
{
case PLAY_MODE_REGULAR :
case PLAY_MODE_ONI :
case PLAY_MODE_NONSTOP :
case PLAY_MODE_ENDLESS :
FOREACH_PlayerNumberInfo ( m_vPlayerInfo , pi )
{
if ( ! GAMESTATE - > IsPlayerEnabled ( pi - > m_pn ) & & ! SHOW_LIFE_METER_FOR_DISABLED_PLAYERS )
continue ; // skip
2014-08-06 23:26:05 -06:00
pi - > m_pLifeMeter = LifeMeter : : MakeLifeMeter ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetStage ( ) . m_LifeType ) ;
2012-07-03 12:51:03 -05:00
pi - > m_pLifeMeter - > Load ( pi - > GetPlayerState ( ) , pi - > GetPlayerStageStats ( ) ) ;
pi - > m_pLifeMeter - > SetName ( ssprintf ( " Life%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_pLifeMeter ) ;
this - > AddChild ( pi - > m_pLifeMeter ) ;
// HACK: When SHOW_LIFE_METER_FOR_DISABLED_PLAYERS is enabled,
// we don't want to have any life in the disabled player's life
// meter. I think this only happens with LifeMeterBars, but I'm
// not 100% sure of that. -freem
if ( ! GAMESTATE - > IsPlayerEnabled ( pi - > m_pn ) & & SHOW_LIFE_METER_FOR_DISABLED_PLAYERS )
{
2014-08-06 23:26:05 -06:00
if ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetStage ( ) . m_LifeType = = LifeType_Bar )
2012-07-03 12:51:03 -05:00
static_cast < LifeMeterBar * > ( pi - > m_pLifeMeter ) - > ChangeLife ( - 1.0f ) ;
}
}
break ;
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
default :
break ;
}
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
// primary score display
if ( pi - > m_pPrimaryScoreDisplay )
{
pi - > m_pPrimaryScoreDisplay - > SetName ( ssprintf ( " Score%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_pPrimaryScoreDisplay ) ;
if ( GAMESTATE - > m_PlayMode ! = PLAY_MODE_RAVE | | SHOW_SCORE_IN_RAVE ) /* XXX: ugly */
this - > AddChild ( pi - > m_pPrimaryScoreDisplay ) ;
}
// secondary score display
if ( pi - > m_pSecondaryScoreDisplay )
{
pi - > m_pSecondaryScoreDisplay - > SetName ( ssprintf ( " SecondaryScore%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_pSecondaryScoreDisplay ) ;
this - > AddChild ( pi - > m_pSecondaryScoreDisplay ) ;
}
}
// Add stage / SongNumber
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
if ( GAMESTATE - > IsCourseMode ( ) )
{
2013-05-03 23:11:42 -04:00
ASSERT ( pi - > m_ptextCourseSongNumber = = nullptr ) ;
2012-07-03 12:51:03 -05:00
SONG_NUMBER_FORMAT . Load ( m_sName , " SongNumberFormat " ) ;
pi - > m_ptextCourseSongNumber = new BitmapText ;
pi - > m_ptextCourseSongNumber - > LoadFromFont ( THEME - > GetPathF ( m_sName , " SongNum " ) ) ;
pi - > m_ptextCourseSongNumber - > SetName ( ssprintf ( " SongNumber%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_ptextCourseSongNumber ) ;
pi - > m_ptextCourseSongNumber - > SetText ( " " ) ;
//pi->m_ptextCourseSongNumber->SetDiffuse( RageColor(0,0.5f,1,1) ); // light blue
this - > AddChild ( pi - > m_ptextCourseSongNumber ) ;
}
2013-05-03 23:11:42 -04:00
ASSERT ( pi - > m_ptextStepsDescription = = nullptr ) ;
2012-07-03 12:51:03 -05:00
pi - > m_ptextStepsDescription = new BitmapText ;
pi - > m_ptextStepsDescription - > LoadFromFont ( THEME - > GetPathF ( m_sName , " StepsDescription " ) ) ;
pi - > m_ptextStepsDescription - > SetName ( ssprintf ( " StepsDescription%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_ptextStepsDescription ) ;
this - > AddChild ( pi - > m_ptextStepsDescription ) ;
// Player/Song options
2013-05-03 23:11:42 -04:00
ASSERT ( pi - > m_ptextPlayerOptions = = nullptr ) ;
2012-07-03 12:51:03 -05:00
pi - > m_ptextPlayerOptions = new BitmapText ;
pi - > m_ptextPlayerOptions - > LoadFromFont ( THEME - > GetPathF ( m_sName , " player options " ) ) ;
pi - > m_ptextPlayerOptions - > SetName ( ssprintf ( " PlayerOptions%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_ptextPlayerOptions ) ;
this - > AddChild ( pi - > m_ptextPlayerOptions ) ;
// Difficulty icon and meter
2013-05-03 23:11:42 -04:00
ASSERT ( pi - > m_pStepsDisplay = = nullptr ) ;
2012-07-03 12:51:03 -05:00
pi - > m_pStepsDisplay = new StepsDisplay ;
pi - > m_pStepsDisplay - > Load ( " StepsDisplayGameplay " , pi - > GetPlayerState ( ) ) ;
pi - > m_pStepsDisplay - > SetName ( ssprintf ( " StepsDisplay%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
PlayerNumber pn = pi - > GetStepsAndTrailIndex ( ) ;
if ( pn ! = PlayerNumber_Invalid )
pi - > m_pStepsDisplay - > PlayCommand ( " Set " + pi - > GetName ( ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_pStepsDisplay ) ;
this - > AddChild ( pi - > m_pStepsDisplay ) ;
/*
switch( GAMESTATE->m_PlayMode )
{
case PLAY_MODE_BATTLE:
pi->m_pInventory = new Inventory;
pi->m_pInventory->Load( p );
this->AddChild( pi->m_pInventory );
break;
}
*/
}
m_textSongOptions . LoadFromFont ( THEME - > GetPathF ( m_sName , " song options " ) ) ;
m_textSongOptions . SetShadowLength ( 0 ) ;
m_textSongOptions . SetName ( " SongOptions " ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( m_textSongOptions ) ;
m_textSongOptions . SetText ( GAMESTATE - > m_SongOptions . GetStage ( ) . GetLocalizedString ( ) ) ;
this - > AddChild ( & m_textSongOptions ) ;
FOREACH_VisiblePlayerInfo ( m_vPlayerInfo , pi )
{
2013-05-03 23:11:42 -04:00
ASSERT ( pi - > m_pActiveAttackList = = nullptr ) ;
2012-07-03 12:51:03 -05:00
pi - > m_pActiveAttackList = new ActiveAttackList ;
pi - > m_pActiveAttackList - > LoadFromFont ( THEME - > GetPathF ( m_sName , " ActiveAttackList " ) ) ;
pi - > m_pActiveAttackList - > Init ( pi - > GetPlayerState ( ) ) ;
pi - > m_pActiveAttackList - > SetName ( ssprintf ( " ActiveAttackList%s " , pi - > GetName ( ) . c_str ( ) ) ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( pi - > m_pActiveAttackList ) ;
this - > AddChild ( pi - > m_pActiveAttackList ) ;
}
if ( g_bShowLyrics )
{
m_LyricDisplay . SetName ( " LyricDisplay " ) ;
LOAD_ALL_COMMANDS ( m_LyricDisplay ) ;
this - > AddChild ( & m_LyricDisplay ) ;
}
if ( ! GAMESTATE - > m_bDemonstrationOrJukebox ) // only load if we're going to use it
{
m_Ready . Load ( THEME - > GetPathB ( m_sName , " ready " ) ) ;
this - > AddChild ( & m_Ready ) ;
m_Go . Load ( THEME - > GetPathB ( m_sName , " go " ) ) ;
this - > AddChild ( & m_Go ) ;
m_Failed . Load ( THEME - > GetPathB ( m_sName , " failed " ) ) ;
m_Failed . SetDrawOrder ( DRAW_ORDER_TRANSITIONS - 1 ) ; // on top of everything else
this - > AddChild ( & m_Failed ) ;
m_textDebug . LoadFromFont ( THEME - > GetPathF ( m_sName , " debug " ) ) ;
m_textDebug . SetName ( " Debug " ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( m_textDebug ) ;
m_textDebug . SetDrawOrder ( DRAW_ORDER_TRANSITIONS - 1 ) ; // just under transitions, over the foreground
this - > AddChild ( & m_textDebug ) ;
m_GameplayAssist . Init ( ) ;
if ( GAMESTATE - > IsAnExtraStage ( ) ) // only load if we're going to use it
{
m_textSurviveTime . LoadFromFont ( THEME - > GetPathF ( m_sName , " survive time " ) ) ;
m_textSurviveTime . SetShadowLength ( 0 ) ;
m_textSurviveTime . SetName ( " SurviveTime " ) ;
LOAD_ALL_COMMANDS_AND_SET_XY ( m_textSurviveTime ) ;
m_textSurviveTime . SetDrawOrder ( DRAW_ORDER_TRANSITIONS - 1 ) ;
m_textSurviveTime . SetDiffuse ( RageColor ( 1 , 1 , 1 , 0 ) ) ;
this - > AddChild ( & m_textSurviveTime ) ;
}
switch ( GAMESTATE - > m_PlayMode )
{
case PLAY_MODE_BATTLE :
m_soundBattleTrickLevel1 . Load ( THEME - > GetPathS ( m_sName , " battle trick level1 " ) , true ) ;
m_soundBattleTrickLevel2 . Load ( THEME - > GetPathS ( m_sName , " battle trick level2 " ) , true ) ;
m_soundBattleTrickLevel3 . Load ( THEME - > GetPathS ( m_sName , " battle trick level3 " ) , true ) ;
default :
break ;
}
}
if ( m_pSongBackground )
m_pSongBackground - > Init ( ) ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
RString sType = PLAYER_TYPE ;
if ( pi - > m_bIsDummy )
sType + = " Dummy " ;
2020-10-20 02:16:02 +09:00
pi - > m_pPlayer - > Init (
2012-07-03 12:51:03 -05:00
sType ,
pi - > GetPlayerState ( ) ,
pi - > GetPlayerStageStats ( ) ,
2020-10-20 02:16:02 +09:00
pi - > m_pLifeMeter ,
m_pCombinedLifeMeter ,
pi - > m_pPrimaryScoreDisplay ,
pi - > m_pSecondaryScoreDisplay ,
pi - > m_pInventory ,
pi - > m_pPrimaryScoreKeeper ,
2012-07-03 12:51:03 -05:00
pi - > m_pSecondaryScoreKeeper ) ;
}
// fill in m_apSongsQueue, m_vpStepsQueue, m_asModifiersQueue
InitSongQueues ( ) ;
// Fill StageStats
STATSMAN - > m_CurStageStats . m_vpPossibleSongs = m_apSongsQueue ;
2013-05-01 23:08:56 -04:00
for ( PlayerInfo & pi : m_vPlayerInfo )
2012-07-03 12:51:03 -05:00
{
2013-05-01 23:08:56 -04:00
if ( pi . GetPlayerStageStats ( ) )
pi . GetPlayerStageStats ( ) - > m_vpPossibleSteps = pi . m_vpStepsQueue ;
2012-07-03 12:51:03 -05:00
}
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
ASSERT ( ! pi - > m_vpStepsQueue . empty ( ) ) ;
if ( pi - > GetPlayerStageStats ( ) )
pi - > GetPlayerStageStats ( ) - > m_bJoined = true ;
if ( pi - > m_pPrimaryScoreKeeper )
pi - > m_pPrimaryScoreKeeper - > Load ( m_apSongsQueue , pi - > m_vpStepsQueue , pi - > m_asModifiersQueue ) ;
if ( pi - > m_pSecondaryScoreKeeper )
pi - > m_pSecondaryScoreKeeper - > Load ( m_apSongsQueue , pi - > m_vpStepsQueue , pi - > m_asModifiersQueue ) ;
}
GAMESTATE - > m_bGameplayLeadIn . Set ( true ) ;
/* LoadNextSong first, since that positions some elements which need to be
* positioned before we TweenOnScreen. */
LoadNextSong ( ) ;
m_GiveUpTimer . SetZero ( ) ;
2015-05-05 13:17:15 -06:00
m_SkipSongTimer . SetZero ( ) ;
2016-06-24 16:55:01 -06:00
m_gave_up = false ;
m_skipped_song = false ;
2012-07-03 12:51:03 -05:00
}
bool ScreenGameplay : : Center1Player ( ) const
{
/* Perhaps this should be handled better by defining a new
* StyleType for ONE_PLAYER_ONE_CREDIT_AND_ONE_COMPUTER,
* but for now just ignore Center1Player when it's Battle or Rave
* Mode. This doesn't begin to address two-player solo (6 arrows) */
2020-10-20 02:16:02 +09:00
return g_bCenter1Player & &
2012-07-03 12:51:03 -05:00
( bool ) ALLOW_CENTER_1_PLAYER & &
GAMESTATE - > m_PlayMode ! = PLAY_MODE_BATTLE & &
GAMESTATE - > m_PlayMode ! = PLAY_MODE_RAVE & &
2014-12-07 01:53:17 -07:00
GAMESTATE - > GetCurrentStyle ( PLAYER_INVALID ) - > m_StyleType = = StyleType_OnePlayerOneSide ;
2012-07-03 12:51:03 -05:00
}
// fill in m_apSongsQueue, m_vpStepsQueue, m_asModifiersQueue
void ScreenGameplay : : InitSongQueues ( )
{
if ( GAMESTATE - > IsCourseMode ( ) )
{
Course * pCourse = GAMESTATE - > m_pCurCourse ;
2013-05-03 23:01:54 -04:00
ASSERT ( pCourse ! = nullptr ) ;
2012-07-03 12:51:03 -05:00
m_apSongsQueue . clear ( ) ;
PlayerNumber pnMaster = GAMESTATE - > GetMasterPlayerNumber ( ) ;
Trail * pTrail = GAMESTATE - > m_pCurTrail [ pnMaster ] ;
2013-05-03 23:01:54 -04:00
ASSERT ( pTrail ! = nullptr ) ;
2013-04-29 23:58:43 -04:00
for ( TrailEntry const & e : pTrail - > m_vEntries )
2012-07-03 12:51:03 -05:00
{
2013-05-03 23:01:54 -04:00
ASSERT ( e . pSong ! = nullptr ) ;
2013-04-29 23:58:43 -04:00
m_apSongsQueue . push_back ( e . pSong ) ;
2012-07-03 12:51:03 -05:00
}
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Trail * lTrail = GAMESTATE - > m_pCurTrail [ pi - > GetStepsAndTrailIndex ( ) ] ;
2013-05-03 23:01:54 -04:00
ASSERT ( lTrail ! = nullptr ) ;
2012-07-03 12:51:03 -05:00
pi - > m_vpStepsQueue . clear ( ) ;
pi - > m_asModifiersQueue . clear ( ) ;
2013-04-29 23:58:43 -04:00
for ( TrailEntry const & e : lTrail - > m_vEntries )
2012-07-03 12:51:03 -05:00
{
2013-05-03 23:01:54 -04:00
ASSERT ( e . pSteps ! = nullptr ) ;
2013-04-29 23:58:43 -04:00
pi - > m_vpStepsQueue . push_back ( e . pSteps ) ;
2012-07-03 12:51:03 -05:00
AttackArray a ;
2013-04-29 23:58:43 -04:00
e . GetAttackArray ( a ) ;
2012-07-03 12:51:03 -05:00
pi - > m_asModifiersQueue . push_back ( a ) ;
}
// In a survival course, override stored mods
if ( pCourse - > GetCourseType ( ) = = COURSE_TYPE_SURVIVAL & & SURVIVAL_MOD_OVERRIDE )
{
2020-10-20 02:16:02 +09:00
pi - > GetPlayerState ( ) - > m_PlayerOptions . FromString ( ModsLevel_Stage ,
2012-07-03 12:51:03 -05:00
" clearall, "
+ CommonMetrics : : DEFAULT_NOTESKIN_NAME . GetValue ( )
+ " , "
+ CommonMetrics : : DEFAULT_MODIFIERS . GetValue ( ) ) ;
pi - > GetPlayerState ( ) - > RebuildPlayerOptionsFromActiveAttacks ( ) ;
}
}
}
else
{
m_apSongsQueue . push_back ( GAMESTATE - > m_pCurSong ) ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Steps * pSteps = GAMESTATE - > m_pCurSteps [ pi - > GetStepsAndTrailIndex ( ) ] ;
pi - > m_vpStepsQueue . push_back ( pSteps ) ;
const PlayerOptions & p = pi - > GetPlayerState ( ) - > m_PlayerOptions . GetCurrent ( ) ;
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
if ( p . m_fNoAttack = = 0 & & p . m_fRandAttack = = 0 & &
pSteps - > m_Attacks . size ( ) > 0 )
{
pi - > m_asModifiersQueue . push_back ( pSteps - > m_Attacks ) ;
}
else
{
AttackArray aa ;
pi - > m_asModifiersQueue . push_back ( aa ) ;
}
}
}
if ( GAMESTATE - > m_bMultiplayer )
{
for ( int i = 0 ; i < ( int ) m_apSongsQueue . size ( ) ; i + + )
{
Song * pSong = m_apSongsQueue [ i ] ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Steps * pOldSteps = pi - > m_vpStepsQueue [ i ] ;
2022-07-10 18:28:56 +03:00
std : : vector < Steps * > vpSteps ;
2012-07-03 12:51:03 -05:00
SongUtil : : GetSteps ( pSong , vpSteps , pOldSteps - > m_StepsType ) ;
StepsUtil : : SortNotesArrayByDifficulty ( vpSteps ) ;
2022-07-10 18:28:56 +03:00
std : : vector < Steps * > : : iterator iter = find ( vpSteps . begin ( ) , vpSteps . end ( ) , pOldSteps ) ;
2012-07-03 12:51:03 -05:00
int iIndexBase = 0 ;
if ( iter ! = vpSteps . end ( ) )
{
iIndexBase = iter - vpSteps . begin ( ) ;
CLAMP ( iIndexBase , 0 , vpSteps . size ( ) - GAMESTATE - > m_iNumMultiplayerNoteFields ) ;
}
int iIndexToUse = iIndexBase + pi - > m_iAddToDifficulty ;
CLAMP ( iIndexToUse , 0 , vpSteps . size ( ) - 1 ) ;
Steps * pSteps = vpSteps [ iIndexToUse ] ;
pi - > m_vpStepsQueue [ i ] = pSteps ;
}
}
}
}
ScreenGameplay : : ~ ScreenGameplay ( )
{
2014-09-07 04:05:10 -06:00
GAMESTATE - > m_AdjustTokensBySongCostForFinalStageCheck = true ;
2012-07-03 12:51:03 -05:00
if ( this - > IsFirstUpdate ( ) )
{
/* We never received any updates. That means we were deleted without being
* used, and never actually played. (This can happen when backing out of
* ScreenStage.) Cancel the stage. */
GAMESTATE - > CancelStage ( ) ;
}
LOG - > Trace ( " ScreenGameplay::~ScreenGameplay() " ) ;
SAFE_DELETE ( m_pSongBackground ) ;
SAFE_DELETE ( m_pSongForeground ) ;
if ( ! GAMESTATE - > m_bDemonstrationOrJukebox )
MEMCARDMAN - > UnPauseMountingThread ( ) ;
SAFE_DELETE ( m_pCombinedLifeMeter ) ;
if ( m_pSoundMusic )
m_pSoundMusic - > StopPlaying ( ) ;
m_GameplayAssist . StopPlaying ( ) ;
}
bool ScreenGameplay : : IsLastSong ( )
{
if ( GAMESTATE - > m_pCurCourse )
{
if ( GAMESTATE - > m_pCurCourse - > m_fGoalSeconds > 0 & & GAMESTATE - > m_bWorkoutGoalComplete )
return true ;
}
if ( GAMESTATE - > m_pCurCourse & & GAMESTATE - > m_pCurCourse - > m_bRepeat )
return false ;
return GAMESTATE - > GetCourseSongIndex ( ) > = ( int ) m_apSongsQueue . size ( ) - 1 ; // GetCourseSongIndex() is 0-based
}
void ScreenGameplay : : SetupSong ( int iSongIndex )
{
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
/* This is the first beat that can be changed without it being visible.
* Until we draw for the first time, any beat can be changed. */
pi - > GetPlayerState ( ) - > m_fLastDrawnBeat = - 100 ;
Steps * pSteps = pi - > m_vpStepsQueue [ iSongIndex ] ;
GAMESTATE - > m_pCurSteps [ pi - > GetStepsAndTrailIndex ( ) ] . Set ( pSteps ) ;
2020-10-20 02:16:02 +09:00
/* Load new NoteData into Player. Do this before
2012-07-03 12:51:03 -05:00
* RebuildPlayerOptionsFromActiveAttacks or else transform mods will get
* propagated to GAMESTATE->m_pPlayerOptions too early and be double-applied
* to the NoteData:
* once in Player::Load, then again in Player::ApplyActiveAttacks.
* This is very bad for transforms like AddMines.
*/
NoteData originalNoteData ;
pSteps - > GetNoteData ( originalNoteData ) ;
2014-12-07 01:53:17 -07:00
const Style * pStyle = GAMESTATE - > GetCurrentStyle ( pi - > m_pn ) ;
2012-07-03 12:51:03 -05:00
NoteData ndTransformed ;
pStyle - > GetTransformedNoteDataForStyle ( pi - > GetStepsAndTrailIndex ( ) , originalNoteData , ndTransformed ) ;
// HACK: Apply NoteSkins from global course options. Do this before
// Player::Load, since it needs to know which note skin to load.
pi - > GetPlayerState ( ) - > m_ModsToApply . clear ( ) ;
for ( unsigned i = 0 ; i < pi - > m_asModifiersQueue [ iSongIndex ] . size ( ) ; + + i )
{
Attack a = pi - > m_asModifiersQueue [ iSongIndex ] [ i ] ;
if ( a . fStartSecond ! = 0 )
continue ;
2013-01-31 13:35:39 -05:00
a . fStartSecond = ATTACK_STARTS_NOW ; // now
2012-07-03 12:51:03 -05:00
PlayerOptions po ;
po . FromString ( a . sModifiers ) ;
if ( po . m_sNoteSkin . empty ( ) )
continue ;
a . sModifiers = po . m_sNoteSkin ;
pi - > GetPlayerState ( ) - > LaunchAttack ( a ) ;
}
/* Update attack bOn flags, and rebuild Current-level options
* from Song-level options. The current NoteSkin could have changed
* because of an attack ending. */
pi - > GetPlayerState ( ) - > Update ( 0 ) ;
// load player
{
pi - > m_NoteData = ndTransformed ;
2014-07-25 18:45:02 -05:00
NoteDataUtil : : RemoveAllTapsOfType ( pi - > m_NoteData , TapNoteType_AutoKeysound ) ;
2012-07-03 12:51:03 -05:00
pi - > m_pPlayer - > Load ( ) ;
}
// load auto keysounds
{
NoteData nd = ndTransformed ;
2014-07-25 18:45:02 -05:00
NoteDataUtil : : RemoveAllTapsExceptForType ( nd , TapNoteType_AutoKeysound ) ;
2012-07-03 12:51:03 -05:00
m_AutoKeysounds . Load ( pi - > GetStepsAndTrailIndex ( ) , nd ) ;
}
{
RString sType ;
switch ( GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_SoundEffectType )
{
2014-05-03 19:27:32 -06:00
case SoundEffectType_Off : sType = " SoundEffectControl_Off " ; break ;
case SoundEffectType_Speed : sType = " SoundEffectControl_Speed " ; break ;
case SoundEffectType_Pitch : sType = " SoundEffectControl_Pitch " ; break ;
2012-07-03 12:51:03 -05:00
default : break ;
}
pi - > m_SoundEffectControl . Load ( sType , pi - > GetPlayerState ( ) , & pi - > m_NoteData ) ;
}
// Put course options into effect. Do this after Player::Load so
// that mods aren't double-applied.
pi - > GetPlayerState ( ) - > m_ModsToApply . clear ( ) ;
for ( unsigned i = 0 ; i < pi - > m_asModifiersQueue [ iSongIndex ] . size ( ) ; + + i )
{
Attack a = pi - > m_asModifiersQueue [ iSongIndex ] [ i ] ;
if ( a . fStartSecond = = 0 )
2013-01-31 13:35:39 -05:00
a . fStartSecond = ATTACK_STARTS_NOW ; // now
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
pi - > GetPlayerState ( ) - > LaunchAttack ( a ) ;
GAMESTATE - > m_SongOptions . FromString ( ModsLevel_Song , a . sModifiers ) ;
}
// Update attack bOn flags.
pi - > GetPlayerState ( ) - > Update ( 0 ) ;
// Hack: Course modifiers that are set to start immediately shouldn't tween on.
pi - > GetPlayerState ( ) - > m_PlayerOptions . SetCurrentToLevel ( ModsLevel_Stage ) ;
}
}
void ScreenGameplay : : ReloadCurrentSong ( )
{
FOREACH_EnabledPlayerInfoNotDummy ( m_vPlayerInfo , pi )
pi - > GetPlayerStageStats ( ) - > m_iSongsPlayed - - ;
LoadNextSong ( ) ;
}
void ScreenGameplay : : LoadNextSong ( )
{
GAMESTATE - > ResetMusicStatistics ( ) ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
pi - > GetPlayerStageStats ( ) - > m_iSongsPlayed + + ;
if ( pi - > m_ptextCourseSongNumber )
pi - > m_ptextCourseSongNumber - > SetText ( ssprintf ( SONG_NUMBER_FORMAT . GetValue ( ) , pi - > GetPlayerStageStats ( ) - > m_iSongsPassed + 1 ) ) ;
}
if ( GAMESTATE - > m_bMultiplayer )
{
FOREACH_ENUM ( MultiPlayer , mp )
this - > UpdateStageStats ( mp ) ;
}
int iPlaySongIndex = GAMESTATE - > GetCourseSongIndex ( ) ;
iPlaySongIndex % = m_apSongsQueue . size ( ) ;
GAMESTATE - > m_pCurSong . Set ( m_apSongsQueue [ iPlaySongIndex ] ) ;
STATSMAN - > m_CurStageStats . m_vpPlayedSongs . push_back ( GAMESTATE - > m_pCurSong ) ;
// No need to do this here. We do it in SongFinished().
//GAMESTATE->RemoveAllActiveAttacks();
2014-08-19 23:58:35 -06:00
// Force immediate fail behavior changed to theme metric by Kyz.
if ( FORCE_IMMEDIATE_FAIL_FOR_BATTERY )
2012-07-03 12:51:03 -05:00
{
2014-08-19 23:58:35 -06:00
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
2014-08-06 23:26:05 -06:00
{
2014-08-19 23:58:35 -06:00
if ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetStage ( ) . m_LifeType = = LifeType_Battery )
{
PO_GROUP_ASSIGN ( pi - > GetPlayerState ( ) - > m_PlayerOptions , ModsLevel_Song , m_FailType , FailType_Immediate ) ;
}
2014-08-06 23:26:05 -06:00
}
2012-07-03 12:51:03 -05:00
}
m_textSongOptions . SetText ( GAMESTATE - > m_SongOptions . GetCurrent ( ) . GetString ( ) ) ;
SetupSong ( iPlaySongIndex ) ;
Song * pSong = GAMESTATE - > m_pCurSong ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Steps * pSteps = GAMESTATE - > m_pCurSteps [ pi - > GetStepsAndTrailIndex ( ) ] ;
+ + pi - > GetPlayerStageStats ( ) - > m_iStepsPlayed ;
2013-05-03 23:01:54 -04:00
ASSERT ( GAMESTATE - > m_pCurSteps [ pi - > GetStepsAndTrailIndex ( ) ] ! = nullptr ) ;
2012-07-03 12:51:03 -05:00
if ( pi - > m_ptextStepsDescription )
pi - > m_ptextStepsDescription - > SetText ( pSteps - > GetDescription ( ) ) ;
/* Increment the play count even if the player fails. (It's still popular,
* even if the people playing it aren't good at it.) */
if ( ! GAMESTATE - > m_bDemonstrationOrJukebox )
{
if ( pi - > m_pn ! = PLAYER_INVALID )
PROFILEMAN - > IncrementStepsPlayCount ( pSong , pSteps , pi - > m_pn ) ;
}
if ( pi - > m_ptextPlayerOptions )
pi - > m_ptextPlayerOptions - > SetText ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetCurrent ( ) . GetString ( ) ) ;
if ( pi - > m_pActiveAttackList )
pi - > m_pActiveAttackList - > Refresh ( ) ;
// reset oni game over graphic
SET_XY_AND_ON_COMMAND ( pi - > m_sprOniGameOver ) ;
2014-08-06 23:26:05 -06:00
if ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetStage ( ) . m_LifeType = = LifeType_Battery & & pi - > GetPlayerStageStats ( ) - > m_bFailed ) // already failed
2012-07-03 12:51:03 -05:00
pi - > ShowOniGameOver ( ) ;
2014-08-06 23:26:05 -06:00
if ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetStage ( ) . m_LifeType = = LifeType_Bar & & pi - > m_pLifeMeter )
2012-07-03 12:51:03 -05:00
pi - > m_pLifeMeter - > UpdateNonstopLifebar ( ) ;
if ( pi - > m_pStepsDisplay )
pi - > m_pStepsDisplay - > SetFromSteps ( pSteps ) ;
/* The actual note data for scoring is the base class of Player. This includes
* transforms, like Wide. Otherwise, the scoring will operate on the wrong data. */
if ( pi - > m_pPrimaryScoreKeeper )
pi - > m_pPrimaryScoreKeeper - > OnNextSong ( GAMESTATE - > GetCourseSongIndex ( ) , pSteps , & pi - > m_pPlayer - > GetNoteData ( ) ) ;
if ( pi - > m_pSecondaryScoreKeeper )
pi - > m_pSecondaryScoreKeeper - > OnNextSong ( GAMESTATE - > GetCourseSongIndex ( ) , pSteps , & pi - > m_pPlayer - > GetNoteData ( ) ) ;
// Don't mess with the PlayerController of the Dummy player
if ( ! pi - > m_bIsDummy )
{
if ( GAMESTATE - > m_bDemonstrationOrJukebox )
{
pi - > GetPlayerState ( ) - > m_PlayerController = PC_CPU ;
pi - > GetPlayerState ( ) - > m_iCpuSkill = 5 ;
}
else if ( GAMESTATE - > IsCpuPlayer ( pi - > GetStepsAndTrailIndex ( ) ) )
{
pi - > GetPlayerState ( ) - > m_PlayerController = PC_CPU ;
int iMeter = pSteps - > GetMeter ( ) ;
int iNewSkill = SCALE ( iMeter , MIN_METER , MAX_METER , 0 , NUM_SKILL_LEVELS - 1 ) ;
/* Watch out: songs aren't actually bound by MAX_METER. */
iNewSkill = clamp ( iNewSkill , 0 , NUM_SKILL_LEVELS - 1 ) ;
pi - > GetPlayerState ( ) - > m_iCpuSkill = iNewSkill ;
}
else
{
if ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetCurrent ( ) . m_fPlayerAutoPlay ! = 0 )
pi - > GetPlayerState ( ) - > m_PlayerController = PC_AUTOPLAY ;
else
pi - > GetPlayerState ( ) - > m_PlayerController = GamePreferences : : m_AutoPlay ;
}
2020-10-20 02:16:02 +09:00
}
2012-07-03 12:51:03 -05:00
}
bool bAllReverse = true ;
bool bAtLeastOneReverse = false ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetCurrent ( ) . m_fScrolls [ PlayerOptions : : SCROLL_REVERSE ] = = 1 )
bAtLeastOneReverse = true ;
else
bAllReverse = false ;
}
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
bool bReverse = pi - > GetPlayerState ( ) - > m_PlayerOptions . GetCurrent ( ) . m_fScrolls [ PlayerOptions : : SCROLL_REVERSE ] = = 1 ;
if ( pi - > m_pStepsDisplay )
pi - > m_pStepsDisplay - > PlayCommand ( bReverse ? " SetReverse " : " SetNoReverse " ) ;
}
m_LyricDisplay . PlayCommand ( bAllReverse ? " SetReverse " : bAtLeastOneReverse ? " SetOneReverse " : " SetNoReverse " ) ;
// Load lyrics
// XXX: don't load this here (who and why? -aj)
LyricsLoader LL ;
if ( GAMESTATE - > m_pCurSong - > HasLyrics ( ) )
LL . LoadFromLRCFile ( GAMESTATE - > m_pCurSong - > GetLyricsPath ( ) , * GAMESTATE - > m_pCurSong ) ;
// Set up song-specific graphics.
// Check to see if any players are in beginner mode.
// Note: steps can be different if turn modifiers are used.
if ( PREFSMAN - > m_bShowBeginnerHelper )
{
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
PlayerNumber pn = pi - > GetStepsAndTrailIndex ( ) ;
if ( GAMESTATE - > IsHumanPlayer ( pn ) & & GAMESTATE - > m_pCurSteps [ pn ] - > GetDifficulty ( ) = = Difficulty_Beginner )
m_BeginnerHelper . AddPlayer ( pn , pi - > m_pPlayer - > GetNoteData ( ) ) ;
}
}
if ( m_pSongBackground )
m_pSongBackground - > Unload ( ) ;
if ( m_pSongForeground )
m_pSongForeground - > Unload ( ) ;
if ( ! PREFSMAN - > m_bShowBeginnerHelper | | ! m_BeginnerHelper . Init ( 2 ) )
{
m_BeginnerHelper . SetVisible ( false ) ;
// BeginnerHelper disabled, or failed to load.
if ( m_pSongBackground )
m_pSongBackground - > LoadFromSong ( GAMESTATE - > m_pCurSong ) ;
if ( ! GAMESTATE - > m_bDemonstrationOrJukebox )
{
/* This will fade from a preset brightness to the actual brightness
* (based on prefs and "cover"). The preset brightness may be 0 (to
* fade from black), or it might be 1, if the stage screen has the
* song BG and we're coming from it (like Pump). This used to be done
* in SM_PlayReady, but that means it's impossible to snap to the
* new brightness immediately. */
if ( m_pSongBackground )
{
m_pSongBackground - > SetBrightness ( INITIAL_BACKGROUND_BRIGHTNESS ) ;
m_pSongBackground - > FadeToActualBrightness ( ) ;
}
}
}
else
{
m_BeginnerHelper . SetVisible ( true ) ;
}
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( ! pi - > GetPlayerStageStats ( ) - > m_bFailed )
{
// give a little life back between stages
if ( pi - > m_pLifeMeter )
pi - > m_pLifeMeter - > OnLoadSong ( ) ;
if ( pi - > m_pPrimaryScoreDisplay )
pi - > m_pPrimaryScoreDisplay - > OnLoadSong ( ) ;
if ( pi - > m_pSecondaryScoreDisplay )
pi - > m_pSecondaryScoreDisplay - > OnLoadSong ( ) ;
}
}
if ( m_pCombinedLifeMeter )
m_pCombinedLifeMeter - > OnLoadSong ( ) ;
if ( m_pSongForeground )
m_pSongForeground - > LoadFromSong ( GAMESTATE - > m_pCurSong ) ;
m_fTimeSinceLastDancingComment = 0 ;
/* m_soundMusic and m_pSongBackground take a very long time to load,
* so cap fDelta at 0 so m_NextSong will show up on screen.
* -Chris */
m_bZeroDeltaOnNextUpdate = true ;
SCREENMAN - > ZeroNextUpdate ( ) ;
// Load cabinet lights data
LoadLights ( ) ;
/* Load the music last, since it may start streaming and we don't want the music
* to compete with other loading. */
m_AutoKeysounds . FinishLoading ( ) ;
m_pSoundMusic = m_AutoKeysounds . GetSound ( ) ;
/* Give SoundEffectControls the new RageSoundReaders. */
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
RageSoundReader * pPlayerSound = m_AutoKeysounds . GetPlayerSound ( pi - > m_pn ) ;
2013-05-03 23:11:42 -04:00
if ( pPlayerSound = = nullptr & & pi - > m_pn = = GAMESTATE - > GetMasterPlayerNumber ( ) )
2012-07-03 12:51:03 -05:00
pPlayerSound = m_AutoKeysounds . GetSharedSound ( ) ;
pi - > m_SoundEffectControl . SetSoundReader ( pPlayerSound ) ;
}
MESSAGEMAN - > Broadcast ( " DoneLoadingNextSong " ) ;
}
void ScreenGameplay : : LoadLights ( )
{
if ( ! LIGHTSMAN - > IsEnabled ( ) )
return ;
// First, check if the song has explicit lights
m_CabinetLightsNoteData . Init ( ) ;
2013-05-03 23:01:54 -04:00
ASSERT ( GAMESTATE - > m_pCurSong ! = nullptr ) ;
2012-07-03 12:51:03 -05:00
const Steps * pSteps = SongUtil : : GetClosestNotes ( GAMESTATE - > m_pCurSong , StepsType_lights_cabinet , Difficulty_Medium ) ;
2013-05-03 23:01:54 -04:00
if ( pSteps ! = nullptr )
2012-07-03 12:51:03 -05:00
{
pSteps - > GetNoteData ( m_CabinetLightsNoteData ) ;
return ;
}
// No explicit lights. Create autogen lights.
RString sDifficulty = PREFSMAN - > m_sLightsStepsDifficulty ;
2022-07-10 18:28:56 +03:00
std : : vector < RString > asDifficulties ;
2012-07-03 12:51:03 -05:00
split ( sDifficulty , " , " , asDifficulties ) ;
// Always use the steps from the primary steps type so that lights are consistent over single and double styles.
StepsType st = GAMEMAN - > GetHowToPlayStyleForGame ( GAMESTATE - > m_pCurGame ) - > m_StepsType ;
Difficulty d1 = Difficulty_Invalid ;
if ( asDifficulties . size ( ) > 0 )
{
if ( asDifficulties [ 0 ] . CompareNoCase ( " selected " ) = = 0 )
{
// Base lights off current difficulty of active player
// Can be either P1 or P2 if they're individual or P1 if both are active
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
PlayerNumber pn = pi - > GetStepsAndTrailIndex ( ) ;
if ( GAMESTATE - > IsPlayerEnabled ( pn ) )
{
d1 = GAMESTATE - > m_pCurSteps [ pn ] - > GetDifficulty ( ) ;
break ;
}
}
}
else
d1 = StringToDifficulty ( asDifficulties [ 0 ] ) ;
}
pSteps = SongUtil : : GetClosestNotes ( GAMESTATE - > m_pCurSong , st , d1 ) ;
// If we can't find anything at all, stop.
2013-05-03 23:11:42 -04:00
if ( pSteps = = nullptr )
2012-07-03 12:51:03 -05:00
return ;
NoteData TapNoteData1 ;
pSteps - > GetNoteData ( TapNoteData1 ) ;
2019-12-16 20:44:02 -06:00
//taken from oitg, restores arrow -> marquee/bass light mapping.
if ( asDifficulties . size ( ) > 1 )
{
Difficulty d2 = StringToDifficulty ( asDifficulties [ 1 ] ) ;
Steps * pSteps2 ;
pSteps2 = SongUtil : : GetClosestNotes ( GAMESTATE - > m_pCurSong , st , d2 ) ;
if ( pSteps2 ! = nullptr )
{
NoteData TapNoteData2 ;
pSteps2 - > GetNoteData ( TapNoteData2 ) ;
NoteDataUtil : : LoadTransformedLightsFromTwo ( TapNoteData1 , TapNoteData2 , m_CabinetLightsNoteData ) ;
return ;
}
2023-04-19 20:55:30 +02:00
// fall through
2019-12-16 20:44:02 -06:00
}
2012-07-03 12:51:03 -05:00
NoteDataUtil : : LoadTransformedLights ( TapNoteData1 , m_CabinetLightsNoteData , GAMEMAN - > GetStepsTypeInfo ( StepsType_lights_cabinet ) . iNumTracks ) ;
}
void ScreenGameplay : : StartPlayingSong ( float fMinTimeToNotes , float fMinTimeToMusic )
{
ASSERT ( fMinTimeToNotes > = 0 ) ;
ASSERT ( fMinTimeToMusic > = 0 ) ;
m_pSoundMusic - > SetProperty ( " AccurateSync " , true ) ;
RageSoundParams p ;
p . m_fSpeed = GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fMusicRate ;
p . StopMode = RageSoundParams : : M_CONTINUE ;
{
const float fFirstSecond = GAMESTATE - > m_pCurSong - > GetFirstSecond ( ) ;
float fStartDelay = fMinTimeToNotes - fFirstSecond ;
2022-07-10 18:28:56 +03:00
fStartDelay = std : : max ( fStartDelay , fMinTimeToMusic ) ;
2012-07-03 12:51:03 -05:00
p . m_StartSecond = - fStartDelay ;
}
ASSERT ( ! m_pSoundMusic - > IsPlaying ( ) ) ;
{
float fSecondsToStartFadingOutMusic , fSecondsToStartTransitioningOut ;
GetMusicEndTiming ( fSecondsToStartFadingOutMusic , fSecondsToStartTransitioningOut ) ;
if ( fSecondsToStartFadingOutMusic < GAMESTATE - > m_pCurSong - > m_fMusicLengthSeconds )
{
p . m_fFadeOutSeconds = MUSIC_FADE_OUT_SECONDS ;
p . m_LengthSeconds = fSecondsToStartFadingOutMusic + MUSIC_FADE_OUT_SECONDS - p . m_StartSecond ;
}
}
2015-02-28 06:25:02 -07:00
m_pSoundMusic - > Play ( false , & p ) ;
2012-07-03 12:51:03 -05:00
if ( m_bPaused )
m_pSoundMusic - > Pause ( true ) ;
/* Make sure GAMESTATE->m_fMusicSeconds is set up. */
GAMESTATE - > m_Position . m_fMusicSeconds = - 5000 ;
UpdateSongPosition ( 0 ) ;
ASSERT ( GAMESTATE - > m_Position . m_fMusicSeconds > - 4000 ) ; /* make sure the "fake timer" code doesn't trigger */
2015-03-14 23:17:35 -06:00
FOREACH_EnabledPlayer ( pn )
{
if ( GAMESTATE - > m_pCurSteps [ pn ] )
{
GAMESTATE - > m_pCurSteps [ pn ] - > GetTimingData ( ) - > PrepareLookup ( ) ;
}
}
2012-07-03 12:51:03 -05:00
}
2015-09-03 14:39:57 -06:00
void ScreenGameplay : : set_paused_internal ( bool p )
{
m_bPaused = p ;
GAMESTATE - > SetPaused ( p ) ;
}
2012-07-03 12:51:03 -05:00
void ScreenGameplay : : PauseGame ( bool bPause , GameController gc )
{
if ( m_bPaused = = bPause )
{
LOG - > Trace ( " ScreenGameplay::PauseGame(%i) received, but already in that state; ignored " , bPause ) ;
return ;
}
// Don't pause if we're already tweening out.
if ( bPause & & m_DancingState = = STATE_OUTRO )
return ;
2015-05-05 13:17:15 -06:00
ResetGiveUpTimers ( false ) ;
2012-07-03 12:51:03 -05:00
2015-09-03 14:39:57 -06:00
set_paused_internal ( bPause ) ;
2012-07-03 12:51:03 -05:00
m_PauseController = gc ;
m_pSoundMusic - > Pause ( bPause ) ;
if ( bPause )
this - > PlayCommand ( " Pause " ) ;
else
this - > PlayCommand ( " Unpause " ) ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
pi - > m_pPlayer - > SetPaused ( m_bPaused ) ;
}
}
// play assist ticks
void ScreenGameplay : : PlayTicks ( )
{
/* TODO: Allow all players to have ticks. Not as simple as it looks.
* If a loop takes place, it could make one player's ticks come later
* than intended. Any help here would be appreciated. -Wolfman2000 */
Player & player = * m_vPlayerInfo [ GAMESTATE - > GetMasterPlayerNumber ( ) ] . m_pPlayer ;
const NoteData & nd = player . GetNoteData ( ) ;
m_GameplayAssist . PlayTicks ( nd , player . GetPlayerState ( ) ) ;
}
/* Play announcer "type" if it's been at least fSeconds since the last announcer. */
void ScreenGameplay : : PlayAnnouncer ( const RString & type , float fSeconds , float * fDeltaSeconds )
{
if ( GAMESTATE - > m_fOpponentHealthPercent = = 0 )
return ; // Shut the announcer up
/* Don't play in demonstration. */
if ( GAMESTATE - > m_bDemonstrationOrJukebox )
return ;
/* Don't play before the first beat, or after we're finished. */
if ( m_DancingState ! = STATE_DANCING )
return ;
2013-05-03 23:11:42 -04:00
if ( GAMESTATE - > m_pCurSong = = nullptr | | // this will be true on ScreenDemonstration sometimes
2012-07-03 12:51:03 -05:00
GAMESTATE - > m_Position . m_fSongBeat < GAMESTATE - > m_pCurSong - > GetFirstBeat ( ) )
return ;
if ( * fDeltaSeconds < fSeconds )
return ;
* fDeltaSeconds = 0 ;
SOUND - > PlayOnceFromAnnouncer ( type ) ;
}
void ScreenGameplay : : UpdateSongPosition ( float fDeltaTime )
{
if ( ! m_pSoundMusic - > IsPlaying ( ) )
return ;
RageTimer tm ;
2019-06-22 12:35:38 -07:00
const float fSeconds = m_pSoundMusic - > GetPositionSeconds ( nullptr , & tm ) ;
2012-07-03 12:51:03 -05:00
const float fAdjust = SOUND - > GetFrameTimingAdjustment ( fDeltaTime ) ;
2014-07-24 19:45:17 -05:00
GAMESTATE - > UpdateSongPosition ( fSeconds + fAdjust , GAMESTATE - > m_pCurSong - > m_SongTiming , tm + fAdjust ) ;
2012-07-03 12:51:03 -05:00
}
void ScreenGameplay : : BeginScreen ( )
{
2013-05-03 23:11:42 -04:00
if ( GAMESTATE - > m_pCurSong = = nullptr )
2012-07-03 12:51:03 -05:00
return ;
ScreenWithMenuElements : : BeginScreen ( ) ;
SOUND - > PlayOnceFromAnnouncer ( " gameplay intro " ) ; // crowd cheer
2022-04-09 13:11:17 +02:00
StartPlayingSong ( MIN_SECONDS_TO_STEP , MIN_SECONDS_TO_MUSIC ) ;
2012-07-03 12:51:03 -05:00
}
bool ScreenGameplay : : AllAreFailing ( )
{
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( pi - > m_pLifeMeter & & ! pi - > m_pLifeMeter - > IsFailing ( ) )
return false ;
}
return true ;
}
void ScreenGameplay : : GetMusicEndTiming ( float & fSecondsToStartFadingOutMusic , float & fSecondsToStartTransitioningOut )
{
float fLastStepSeconds = GAMESTATE - > m_pCurSong - > GetLastSecond ( ) ;
fLastStepSeconds + = Player : : GetMaxStepDistanceSeconds ( ) ;
float fTransitionLength ;
if ( ! GAMESTATE - > IsCourseMode ( ) | | IsLastSong ( ) )
fTransitionLength = OUT_TRANSITION_LENGTH ;
else
fTransitionLength = COURSE_TRANSITION_LENGTH ;
fSecondsToStartTransitioningOut = fLastStepSeconds ;
// Align the end of the music fade to the end of the transition.
float fSecondsToFinishFadingOutMusic = fSecondsToStartTransitioningOut + fTransitionLength ;
if ( fSecondsToFinishFadingOutMusic < GAMESTATE - > m_pCurSong - > m_fMusicLengthSeconds )
fSecondsToStartFadingOutMusic = fSecondsToFinishFadingOutMusic - MUSIC_FADE_OUT_SECONDS ;
else
fSecondsToStartFadingOutMusic = GAMESTATE - > m_pCurSong - > m_fMusicLengthSeconds ; // don't fade
/* Make sure we keep going long enough to register a miss for the last note, and
* never start fading before the last note. */
2022-07-10 18:28:56 +03:00
fSecondsToStartFadingOutMusic = std : : max ( fSecondsToStartFadingOutMusic , fLastStepSeconds ) ;
fSecondsToStartTransitioningOut = std : : max ( fSecondsToStartTransitioningOut , fLastStepSeconds ) ;
2012-07-03 12:51:03 -05:00
/* Make sure the fade finishes before the transition finishes. */
2022-07-10 18:28:56 +03:00
fSecondsToStartTransitioningOut = std : : max ( fSecondsToStartTransitioningOut , fSecondsToStartFadingOutMusic + MUSIC_FADE_OUT_SECONDS - fTransitionLength ) ;
2012-07-03 12:51:03 -05:00
}
void ScreenGameplay : : Update ( float fDeltaTime )
{
2013-05-03 23:11:42 -04:00
if ( GAMESTATE - > m_pCurSong = = nullptr )
2012-07-03 12:51:03 -05:00
{
/* ScreenDemonstration will move us to the next screen. We just need to
* survive for one update without crashing. We need to call Screen::Update
* to make sure we receive the next-screen message. */
Screen : : Update ( fDeltaTime ) ;
return ;
}
UpdateSongPosition ( fDeltaTime ) ;
if ( m_bZeroDeltaOnNextUpdate )
{
Screen : : Update ( 0 ) ;
m_bZeroDeltaOnNextUpdate = false ;
}
else
{
2020-10-20 02:16:02 +09:00
const float fRate = PREFSMAN - > m_bRateModsAffectTweens ? GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fMusicRate : 1.0f ;
Screen : : Update ( fDeltaTime * fRate ) ;
2012-07-03 12:51:03 -05:00
}
/* This happens if ScreenDemonstration::HandleScreenMessage sets a new screen when
* PREFSMAN->m_bDelayedScreenLoad. */
2013-05-03 23:11:42 -04:00
if ( GAMESTATE - > m_pCurSong = = nullptr )
2012-07-03 12:51:03 -05:00
return ;
/* This can happen if ScreenDemonstration::HandleScreenMessage sets a new screen when
* !PREFSMAN->m_bDelayedScreenLoad. (The new screen was loaded when we called Screen::Update,
* and the ctor might set a new GAMESTATE->m_pCurSong, so the above check can fail.) */
if ( SCREENMAN - > GetTopScreen ( ) ! = this )
return ;
/* Update actors when paused, but never move on to another state. */
if ( m_bPaused )
return ;
//LOG->Trace( "m_fOffsetInBeats = %f, m_fBeatsPerSecond = %f, m_Music.GetPositionSeconds = %f", m_fOffsetInBeats, m_fBeatsPerSecond, m_Music.GetPositionSeconds() );
m_AutoKeysounds . Update ( fDeltaTime ) ;
// update GameState HealthState
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
HealthState & hs = pi - > GetPlayerState ( ) - > m_HealthState ;
HealthState OldHealthState = hs ;
2014-05-01 23:23:20 -06:00
if ( GAMESTATE - > GetPlayerFailType ( pi - > GetPlayerState ( ) ) ! = FailType_Off & &
2012-07-03 12:51:03 -05:00
pi - > m_pLifeMeter & & pi - > m_pLifeMeter - > IsFailing ( ) )
{
hs = HealthState_Dead ;
}
else if ( pi - > m_pLifeMeter & & pi - > m_pLifeMeter - > IsHot ( ) )
{
hs = HealthState_Hot ;
}
2014-05-01 23:23:20 -06:00
else if ( GAMESTATE - > GetPlayerFailType ( pi - > GetPlayerState ( ) ) ! = FailType_Off & &
2012-07-03 12:51:03 -05:00
pi - > m_pLifeMeter & & pi - > m_pLifeMeter - > IsInDanger ( ) )
{
hs = HealthState_Danger ;
}
else
{
hs = HealthState_Alive ;
}
if ( hs ! = OldHealthState )
{
Message msg ( " HealthStateChanged " ) ;
msg . SetParam ( " PlayerNumber " , pi - > m_pn ) ;
msg . SetParam ( " HealthState " , hs ) ;
msg . SetParam ( " OldHealthState " , OldHealthState ) ;
MESSAGEMAN - > Broadcast ( msg ) ;
}
pi - > m_SoundEffectControl . Update ( fDeltaTime ) ;
}
{
float fSpeed = GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fMusicRate ;
if ( GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fHaste ! = 0.0f )
fSpeed * = GetHasteRate ( ) ;
RageSoundParams p = m_pSoundMusic - > GetParams ( ) ;
2023-04-19 14:22:59 +02:00
if ( std : : abs ( p . m_fSpeed - fSpeed ) > 0.01f & & fSpeed > = 0.0f )
2012-07-03 12:51:03 -05:00
{
p . m_fSpeed = fSpeed ;
m_pSoundMusic - > SetParams ( p ) ;
}
}
switch ( m_DancingState )
{
case STATE_DANCING :
{
/* Set STATSMAN->m_CurStageStats.bFailed for failed players. In, FAIL_IMMEDIATE, send
* SM_BeginFailed if all players failed, and kill dead Oni players. */
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
PlayerNumber pn = pi - > GetStepsAndTrailIndex ( ) ;
2014-05-01 23:23:20 -06:00
FailType ft = GAMESTATE - > GetPlayerFailType ( pi - > GetPlayerState ( ) ) ;
2014-08-06 23:26:05 -06:00
LifeType lt = pi - > GetPlayerState ( ) - > m_PlayerOptions . GetStage ( ) . m_LifeType ;
2014-05-01 23:23:20 -06:00
if ( ft = = FailType_Off | | ft = = FailType_EndOfSong )
2012-07-03 12:51:03 -05:00
continue ;
// check for individual fail
2013-05-03 23:11:42 -04:00
if ( pi - > m_pLifeMeter = = nullptr | | ! pi - > m_pLifeMeter - > IsFailing ( ) )
2012-07-03 12:51:03 -05:00
continue ; /* isn't failing */
if ( pi - > GetPlayerStageStats ( ) - > m_bFailed )
continue ; /* failed and is already dead */
LOG - > Trace ( " Player %d failed " , ( int ) pn ) ;
pi - > GetPlayerStageStats ( ) - > m_bFailed = true ; // fail
{
Message msg ( " PlayerFailed " ) ;
msg . SetParam ( " PlayerNumber " , pi - > m_pn ) ;
MESSAGEMAN - > Broadcast ( msg ) ;
}
// Check for and do Oni die.
bool bAllowOniDie = false ;
switch ( lt )
{
2014-05-03 19:27:32 -06:00
case LifeType_Battery :
2012-07-03 12:51:03 -05:00
bAllowOniDie = true ;
default :
break ;
}
2014-05-01 23:23:20 -06:00
if ( bAllowOniDie & & ft = = FailType_Immediate )
2012-07-03 12:51:03 -05:00
{
if ( ! STATSMAN - > m_CurStageStats . AllFailed ( ) ) // if not the last one to fail
{
// kill them!
2014-08-06 23:26:05 -06:00
FailFadeRemovePlayer ( & * pi ) ;
2012-07-03 12:51:03 -05:00
}
}
}
bool bAllFailed = true ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
2014-05-01 23:23:20 -06:00
FailType ft = GAMESTATE - > GetPlayerFailType ( pi - > GetPlayerState ( ) ) ;
2012-07-03 12:51:03 -05:00
switch ( ft )
{
2014-05-01 23:23:20 -06:00
case FailType_Immediate :
2013-05-03 23:11:42 -04:00
if ( pi - > m_pLifeMeter = = nullptr | | ( pi - > m_pLifeMeter & & ! pi - > m_pLifeMeter - > IsFailing ( ) ) )
2012-07-03 12:51:03 -05:00
bAllFailed = false ;
break ;
2014-05-01 23:23:20 -06:00
case FailType_ImmediateContinue :
case FailType_EndOfSong :
2012-07-03 12:51:03 -05:00
bAllFailed = false ; // wait until the end of the song to fail.
break ;
2014-05-01 23:23:20 -06:00
case FailType_Off :
2012-07-03 12:51:03 -05:00
bAllFailed = false ; // never fail.
break ;
default :
FAIL_M ( " Invalid fail type! Aborting... " ) ;
}
}
if ( bAllFailed )
{
m_pSoundMusic - > StopPlaying ( ) ;
SCREENMAN - > PostMessageToTopScreen ( SM_NotesEnded , 0 ) ;
m_LyricDisplay . Stop ( ) ;
}
// Update living players' alive time
2020-10-20 02:16:02 +09:00
// HACK: Don't scale alive time when using tab/tilde. Instead of accumulating time from a timer,
2012-07-03 12:51:03 -05:00
// this time should instead be tied to the music position.
float fUnscaledDeltaTime = m_timerGameplaySeconds . GetDeltaTime ( ) ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
if ( ! pi - > GetPlayerStageStats ( ) - > m_bFailed )
pi - > GetPlayerStageStats ( ) - > m_fAliveSeconds + = fUnscaledDeltaTime * GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fMusicRate ;
// update fGameplaySeconds
STATSMAN - > m_CurStageStats . m_fGameplaySeconds + = fUnscaledDeltaTime ;
float curBeat = GAMESTATE - > m_Position . m_fSongBeat ;
Song & s = * GAMESTATE - > m_pCurSong ;
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
if ( curBeat > = s . GetFirstBeat ( ) & & curBeat < s . GetLastBeat ( ) )
{
STATSMAN - > m_CurStageStats . m_fStepsSeconds + = fUnscaledDeltaTime ;
2014-05-05 14:16:08 -06:00
UpdateHasteRate ( ) ;
2012-07-03 12:51:03 -05:00
if ( GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fHaste ! = 0.0f )
{
float fHasteRate = GetHasteRate ( ) ;
2014-05-05 14:16:08 -06:00
// For negative haste, accumulate seconds while the song is slowed down.
if ( GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fHaste < 0 )
{
GAMESTATE - > m_fAccumulatedHasteSeconds - = ( fUnscaledDeltaTime * fHasteRate ) - fUnscaledDeltaTime ;
}
// For positive haste, accumulate seconds while the song is sped up.
else
{
GAMESTATE - > m_fAccumulatedHasteSeconds + = ( fUnscaledDeltaTime * fHasteRate ) - fUnscaledDeltaTime ;
}
2012-07-03 12:51:03 -05:00
}
}
// Check for end of song
{
float fSecondsToStartFadingOutMusic , fSecondsToStartTransitioningOut ;
GetMusicEndTiming ( fSecondsToStartFadingOutMusic , fSecondsToStartTransitioningOut ) ;
bool bAllReallyFailed = STATSMAN - > m_CurStageStats . AllFailed ( ) ;
if ( bAllReallyFailed )
fSecondsToStartTransitioningOut + = BEGIN_FAILED_DELAY ;
if ( GAMESTATE - > m_Position . m_fMusicSeconds > = fSecondsToStartTransitioningOut & & ! m_NextSong . IsTransitioning ( ) )
this - > PostScreenMessage ( SM_NotesEnded , 0 ) ;
}
// update 2d dancing characters
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
2013-05-03 23:39:52 -04:00
DancingCharacters * pCharacter = nullptr ;
2012-07-03 12:51:03 -05:00
if ( m_pSongBackground )
pCharacter = m_pSongBackground - > GetDancingCharacters ( ) ;
2013-05-03 23:01:54 -04:00
if ( pCharacter ! = nullptr )
2012-07-03 12:51:03 -05:00
{
TapNoteScore tns = pi - > m_pPlayer - > GetLastTapNoteScore ( ) ;
ANIM_STATES_2D state = AS2D_MISS ;
switch ( tns )
{
case TNS_W4 :
case TNS_W3 :
state = AS2D_GOOD ;
break ;
case TNS_W2 :
case TNS_W1 :
state = AS2D_GREAT ;
break ;
default :
state = AS2D_MISS ;
break ;
}
if ( state = = AS2D_GREAT & & pi - > GetPlayerState ( ) - > m_HealthState = = HealthState_Hot )
state = AS2D_FEVER ;
pCharacter - > Change2DAnimState ( pi - > m_pn , state ) ;
}
}
// Check for enemy death in enemy battle
static float fLastSeenEnemyHealth = 1 ;
if ( fLastSeenEnemyHealth ! = GAMESTATE - > m_fOpponentHealthPercent )
{
fLastSeenEnemyHealth = GAMESTATE - > m_fOpponentHealthPercent ;
if ( GAMESTATE - > m_fOpponentHealthPercent = = 0 )
{
// HACK: Load incorrect directory on purpose for now.
PlayAnnouncer ( " gameplay battle damage level3 " , 0 ) ;
GAMESTATE - > RemoveAllActiveAttacks ( ) ;
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
if ( ! GAMESTATE - > IsCpuPlayer ( pi - > m_pn ) )
continue ;
2014-08-19 23:58:35 -06:00
FailFadeRemovePlayer ( & * pi ) ;
2012-07-03 12:51:03 -05:00
}
}
}
// update give up
2016-06-24 16:55:01 -06:00
bool bGiveUpTimerFired = false ;
2016-07-12 21:00:54 -06:00
bGiveUpTimerFired = ! m_GiveUpTimer . IsZero ( ) & & m_GiveUpTimer . Ago ( ) > GIVE_UP_SECONDS ;
m_gave_up = bGiveUpTimerFired ;
m_skipped_song = ! m_SkipSongTimer . IsZero ( ) & & m_SkipSongTimer . Ago ( ) > GIVE_UP_SECONDS ;
2014-04-15 12:27:54 -05:00
2012-07-03 12:51:03 -05:00
bool bAllHumanHaveBigMissCombo = true ;
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
2014-05-01 23:23:20 -06:00
if ( pi - > GetPlayerState ( ) - > m_PlayerOptions . GetCurrent ( ) . m_FailType = = FailType_Off | |
2012-07-03 12:51:03 -05:00
pi - > GetPlayerState ( ) - > m_HealthState < HealthState_Dead )
{
bAllHumanHaveBigMissCombo = false ;
break ;
}
}
if ( bAllHumanHaveBigMissCombo ) // possible to get in here.
{
2015-01-23 10:25:06 -06:00
bAllHumanHaveBigMissCombo = FAIL_ON_MISS_COMBO . GetValue ( ) ! = - 1 & & STATSMAN - > m_CurStageStats . GetMinimumMissCombo ( ) > = ( unsigned int ) FAIL_ON_MISS_COMBO ;
2012-07-03 12:51:03 -05:00
}
2015-05-05 13:17:15 -06:00
if ( bGiveUpTimerFired | | bAllHumanHaveBigMissCombo | | m_skipped_song )
2012-07-03 12:51:03 -05:00
{
STATSMAN - > m_CurStageStats . m_bGaveUp = true ;
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
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.
}
2015-05-05 13:17:15 -06:00
ResetGiveUpTimers ( false ) ;
if ( GIVING_UP_GOES_TO_PREV_SCREEN & & ! m_skipped_song )
2012-07-03 12:51:03 -05:00
{
BeginBackingOutFromGameplay ( ) ;
}
else
{
m_pSoundMusic - > StopPlaying ( ) ;
this - > PostScreenMessage ( SM_NotesEnded , 0 ) ;
}
return ;
}
// Check to see if it's time to play a ScreenGameplay comment
m_fTimeSinceLastDancingComment + = fDeltaTime ;
2012-12-27 16:59:35 -05:00
PlayMode mode = GAMESTATE - > m_PlayMode ;
switch ( mode )
2012-07-03 12:51:03 -05:00
{
case PLAY_MODE_REGULAR :
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
if ( GAMESTATE - > OneIsHot ( ) )
PlayAnnouncer ( " gameplay comment hot " , SECONDS_BETWEEN_COMMENTS ) ;
else if ( GAMESTATE - > AllAreInDangerOrWorse ( ) )
PlayAnnouncer ( " gameplay comment danger " , SECONDS_BETWEEN_COMMENTS ) ;
else
PlayAnnouncer ( " gameplay comment good " , SECONDS_BETWEEN_COMMENTS ) ;
break ;
case PLAY_MODE_NONSTOP :
case PLAY_MODE_ONI :
case PLAY_MODE_ENDLESS :
PlayAnnouncer ( " gameplay comment oni " , SECONDS_BETWEEN_COMMENTS ) ;
break ;
default :
2012-12-27 16:59:35 -05:00
FAIL_M ( ssprintf ( " Invalid PlayMode: %i " , mode ) ) ;
2012-07-03 12:51:03 -05:00
}
2012-12-27 16:59:35 -05:00
}
2012-07-03 12:51:03 -05:00
default : break ;
}
PlayTicks ( ) ;
UpdateLights ( ) ;
SendCrossedMessages ( ) ;
2015-04-03 14:19:28 -06:00
// ArrowEffects::Update call moved because having it happen once per
// NoteField (which means twice in two player) seemed wasteful. -Kyz
ArrowEffects : : Update ( ) ;
}
void ScreenGameplay : : DrawPrimitives ( )
{
// ScreenGameplay::DrawPrimitives exists so that the notefield board can be
// above the song background and underneath everything else. This way, a
// theme can put a screen filter in the notefield board and not have it
// obscure custom elements on the screen. Putting the screen filter in the
// notefield board simplifies placement because it ensures that the filter
// is in the same place as the notefield, instead of forcing the filter to
// check conditions and metrics that affect the position of the notefield.
// This also solves the problem of the ComboUnderField metric putting the
// combo underneath the opaque notefield board.
// -Kyz
if ( m_pSongBackground )
{
m_pSongBackground - > m_disable_draw = false ;
m_pSongBackground - > Draw ( ) ;
m_pSongBackground - > m_disable_draw = true ;
}
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
pi - > m_pPlayer - > DrawNoteFieldBoard ( ) ;
}
2018-04-20 18:27:37 -07:00
// Clear the z buffer so 3D notes aren't hidden by anything in the underlay using masking. -Kyz
DISPLAY - > ClearZBuffer ( ) ;
2015-04-03 14:19:28 -06:00
ScreenWithMenuElements : : DrawPrimitives ( ) ;
2012-07-03 12:51:03 -05:00
}
2014-08-06 23:26:05 -06:00
void ScreenGameplay : : FailFadeRemovePlayer ( PlayerInfo * pi )
{
SOUND - > PlayOnceFromDir ( THEME - > GetPathS ( m_sName , " oni die " ) ) ;
pi - > ShowOniGameOver ( ) ;
int tracks = pi - > m_NoteData . GetNumTracks ( ) ;
pi - > m_NoteData . Init ( ) ; // remove all notes and scoring
pi - > m_NoteData . SetNumTracks ( tracks ) ; // reset the number of tracks.
pi - > m_pPlayer - > FadeToFail ( ) ; // tell the NoteField to fade to white
}
2012-07-03 12:51:03 -05:00
float ScreenGameplay : : GetHasteRate ( )
2014-05-05 14:16:08 -06:00
{
return m_fCurrHasteRate ;
}
void ScreenGameplay : : UpdateHasteRate ( )
2012-07-03 12:51:03 -05:00
{
if ( GAMESTATE - > m_Position . m_fMusicSeconds < GAMESTATE - > m_fLastHasteUpdateMusicSeconds | | // new song
2014-05-05 14:16:08 -06:00
GAMESTATE - > m_Position . m_fMusicSeconds > GAMESTATE - > m_fLastHasteUpdateMusicSeconds + m_fHasteTimeBetweenUpdates )
2012-07-03 12:51:03 -05:00
{
bool bAnyPlayerHitAllNotes = false ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( ! GAMESTATE - > IsHumanPlayer ( pi - > m_pn ) )
continue ;
PlayerState * pPS = pi - > GetPlayerState ( ) ;
if ( pPS - > m_iTapsHitSinceLastHasteUpdate > 0 & &
pPS - > m_iTapsMissedSinceLastHasteUpdate = = 0 )
bAnyPlayerHitAllNotes = true ;
pPS - > m_iTapsHitSinceLastHasteUpdate = 0 ;
pPS - > m_iTapsMissedSinceLastHasteUpdate = 0 ;
}
if ( bAnyPlayerHitAllNotes )
GAMESTATE - > m_fHasteRate + = 0.1f ;
CLAMP ( GAMESTATE - > m_fHasteRate , - 1.0f , + 1.0f ) ;
GAMESTATE - > m_fLastHasteUpdateMusicSeconds = GAMESTATE - > m_Position . m_fMusicSeconds ;
}
/* If the life meter is less than half full, push the haste rate down to let
* the player use his accumulated haste time. */
float fMaxLife = 0 ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( ! GAMESTATE - > IsHumanPlayer ( pi - > m_pn ) )
continue ;
2014-07-25 00:20:38 -06:00
// In Battle/Rave mode, the players don't have life meters.
if ( pi - > m_pLifeMeter )
{
2022-07-10 18:28:56 +03:00
fMaxLife = std : : max ( fMaxLife , pi - > m_pLifeMeter - > GetLife ( ) ) ;
2014-07-25 00:20:38 -06:00
}
else
{
fMaxLife = 1 ;
}
2012-07-03 12:51:03 -05:00
}
2014-05-05 14:16:08 -06:00
if ( fMaxLife < = m_fHasteLifeSwitchPoint )
GAMESTATE - > m_fHasteRate = SCALE ( fMaxLife , 0.0f , m_fHasteLifeSwitchPoint , - 1.0f , 0.0f ) ;
CLAMP ( GAMESTATE - > m_fHasteRate , - 1.0f , + 1.0f ) ;
2012-07-03 12:51:03 -05:00
float fSpeed = 1.0f ;
2014-05-05 14:16:08 -06:00
// If there are no turning points or no add amounts, the bad themer probably thinks that's a way to disable haste.
// Since we're outside a lua function, crashing (asserting) won't point back to the source of the problem.
if ( m_HasteTurningPoints . size ( ) < 2 | | m_HasteAddAmounts . size ( ) < 2 | |
m_HasteTurningPoints . size ( ) ! = m_HasteAddAmounts . size ( ) )
{
m_fCurrHasteRate = fSpeed ;
return ;
}
float options_haste = GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fHaste ;
float scale_from_low = - 1 ;
float scale_from_high = 1 ;
float scale_to_low = 0 ;
float scale_to_high = 0 ;
2023-04-19 23:04:25 +02:00
for ( std : : size_t turning_point = 0 ; turning_point < m_HasteTurningPoints . size ( ) ;
2014-05-05 14:16:08 -06:00
+ + turning_point )
{
float curr_turning_point = m_HasteTurningPoints [ turning_point ] ;
scale_from_high = curr_turning_point ;
scale_to_high = m_HasteAddAmounts [ turning_point ] ;
if ( GAMESTATE - > m_fHasteRate < curr_turning_point )
{
break ;
}
scale_from_low = curr_turning_point ;
scale_to_low = m_HasteAddAmounts [ turning_point ] ;
}
// If negative haste is being used, the game instead slows down when the player does well.
float speed_add = SCALE ( GAMESTATE - > m_fHasteRate , scale_from_low , scale_from_high , scale_to_low , scale_to_high ) * options_haste ;
if ( scale_from_low = = scale_from_high )
{
speed_add = scale_to_high * options_haste ;
}
CLAMP ( speed_add , - 1.0f , 1.0f ) ;
2012-07-03 12:51:03 -05:00
2014-05-05 14:16:08 -06:00
// Only adjust speed_add by AccumulatedHasteSeconds when the player is losing seconds. Otherwise, gaining the first second is interfered with.
bool losing_seconds = false ;
if ( options_haste > 0 )
{
losing_seconds = speed_add < 0 ;
}
else
{
losing_seconds = speed_add > 0 ;
}
if ( losing_seconds & & GAMESTATE - > m_fAccumulatedHasteSeconds < = 1 )
2012-07-03 12:51:03 -05:00
{
/* Only allow slowing down the song while the players have accumulated
* haste. This prevents dragging on the song by keeping the life meter
* nearly empty. */
2014-05-05 14:16:08 -06:00
/* In positive haste mode, the player accumulates seconds while the song
* is sped up, and loses them while the song is slowed down. "<= 1"
* means that the player is only eligible to slow the song down when
* they are down to their last accumulated second. -Kyz */
// 1 second left is full speed_add, 0 seconds left is no speed_add.
2022-07-10 18:28:56 +03:00
float clamp_secs = std : : max ( 0.0f , GAMESTATE - > m_fAccumulatedHasteSeconds ) ;
2014-05-05 14:16:08 -06:00
speed_add = speed_add * clamp_secs ;
}
fSpeed + = speed_add ;
m_fCurrHasteRate = fSpeed ;
2012-07-03 12:51:03 -05:00
}
void ScreenGameplay : : UpdateLights ( )
{
if ( ! LIGHTSMAN - > IsEnabled ( ) )
return ;
if ( m_CabinetLightsNoteData . GetNumTracks ( ) = = 0 ) // light data wasn't loaded
return ;
bool bBlinkCabinetLight [ NUM_CabinetLight ] ;
bool bBlinkGameButton [ NUM_GameController ] [ NUM_GameButton ] ;
ZERO ( bBlinkCabinetLight ) ;
ZERO ( bBlinkGameButton ) ;
{
const float fSongBeat = GAMESTATE - > m_Position . m_fLightSongBeat ;
const int iSongRow = BeatToNoteRowNotRounded ( fSongBeat ) ;
static int iRowLastCrossed = 0 ;
FOREACH_CabinetLight ( cl )
{
// for each index we crossed since the last update:
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE ( m_CabinetLightsNoteData , cl , r , iRowLastCrossed + 1 , iSongRow + 1 )
{
2014-07-25 18:45:02 -05:00
if ( m_CabinetLightsNoteData . GetTapNote ( cl , r ) . type ! = TapNoteType_Empty )
2012-07-03 12:51:03 -05:00
bBlinkCabinetLight [ cl ] = true ;
}
if ( m_CabinetLightsNoteData . IsHoldNoteAtRow ( cl , iSongRow ) )
bBlinkCabinetLight [ cl ] = true ;
}
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
2014-12-07 01:53:17 -07:00
const Style * pStyle = GAMESTATE - > GetCurrentStyle ( pi - > m_pn ) ;
2012-07-03 12:51:03 -05:00
const NoteData & nd = pi - > m_pPlayer - > GetNoteData ( ) ;
for ( int t = 0 ; t < nd . GetNumTracks ( ) ; t + + )
{
bool bBlink = false ;
// for each index we crossed since the last update:
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE ( nd , t , r , iRowLastCrossed + 1 , iSongRow + 1 )
{
const TapNote & tn = nd . GetTapNote ( t , r ) ;
2014-07-25 18:45:02 -05:00
if ( tn . type ! = TapNoteType_Mine )
2012-07-03 12:51:03 -05:00
bBlink = true ;
}
// check if a hold should be active
if ( nd . IsHoldNoteAtRow ( t , iSongRow ) )
bBlink = true ;
if ( bBlink )
{
2022-07-10 18:28:56 +03:00
std : : vector < GameInput > gi ;
2015-01-17 21:15:15 -07:00
pStyle - > StyleInputToGameInput ( t , pi - > m_pn , gi ) ;
2023-04-19 23:04:25 +02:00
for ( std : : size_t i = 0 ; i < gi . size ( ) ; + + i )
2015-01-17 21:15:15 -07:00
{
bBlinkGameButton [ gi [ i ] . controller ] [ gi [ i ] . button ] = true ;
}
2012-07-03 12:51:03 -05:00
}
}
}
iRowLastCrossed = iSongRow ;
}
// Before the first beat of the song, all cabinet lights solid on (except for menu buttons).
Song & s = * GAMESTATE - > m_pCurSong ;
bool bOverrideCabinetBlink = ( GAMESTATE - > m_Position . m_fSongBeat < s . GetFirstBeat ( ) ) ;
FOREACH_CabinetLight ( cl )
bBlinkCabinetLight [ cl ] | = bOverrideCabinetBlink ;
// Send blink data.
FOREACH_CabinetLight ( cl )
{
if ( bBlinkCabinetLight [ cl ] )
LIGHTSMAN - > BlinkCabinetLight ( cl ) ;
}
FOREACH_ENUM ( GameController , gc )
{
FOREACH_ENUM ( GameButton , gb )
{
if ( bBlinkGameButton [ gc ] [ gb ] )
LIGHTSMAN - > BlinkGameButton ( GameInput ( gc , gb ) ) ;
}
}
}
void ScreenGameplay : : SendCrossedMessages ( )
{
{
static int iRowLastCrossed = 0 ;
float fPositionSeconds = GAMESTATE - > m_Position . m_fMusicSeconds ;
float fSongBeat = GAMESTATE - > m_pCurSong - > m_SongTiming . GetBeatFromElapsedTime ( fPositionSeconds ) ;
int iRowNow = BeatToNoteRowNotRounded ( fSongBeat ) ;
2022-07-10 18:28:56 +03:00
iRowNow = std : : max ( 0 , iRowNow ) ;
2012-07-03 12:51:03 -05:00
for ( int r = iRowLastCrossed + 1 ; r < = iRowNow ; r + + )
{
if ( GetNoteType ( r ) = = NOTE_TYPE_4TH )
MESSAGEMAN - > Broadcast ( Message_BeatCrossed ) ;
}
iRowLastCrossed = iRowNow ;
}
{
const int NUM_MESSAGES_TO_SEND = 4 ;
const float MESSAGE_SPACING_SECONDS = 0.4f ;
PlayerNumber pn = PLAYER_INVALID ;
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
if ( GAMESTATE - > m_pCurSteps [ pi - > m_pn ] - > GetDifficulty ( ) = = Difficulty_Beginner )
{
pn = pi - > m_pn ;
break ;
}
}
if ( pn = = PLAYER_INVALID )
return ;
const NoteData & nd = m_vPlayerInfo [ pn ] . m_pPlayer - > GetNoteData ( ) ;
static int iRowLastCrossedAll [ NUM_MESSAGES_TO_SEND ] = { 0 , 0 , 0 , 0 } ;
for ( int i = 0 ; i < NUM_MESSAGES_TO_SEND ; i + + )
{
float fNoteWillCrossInSeconds = MESSAGE_SPACING_SECONDS * i ;
float fPositionSeconds = GAMESTATE - > m_Position . m_fMusicSeconds + fNoteWillCrossInSeconds ;
float fSongBeat = GAMESTATE - > m_pCurSong - > m_SongTiming . GetBeatFromElapsedTime ( fPositionSeconds ) ;
int iRowNow = BeatToNoteRowNotRounded ( fSongBeat ) ;
2022-07-10 18:28:56 +03:00
iRowNow = std : : max ( 0 , iRowNow ) ;
2012-07-03 12:51:03 -05:00
int & iRowLastCrossed = iRowLastCrossedAll [ i ] ;
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE ( nd , r , iRowLastCrossed + 1 , iRowNow + 1 )
{
int iNumTracksWithTapOrHoldHead = 0 ;
for ( int t = 0 ; t < nd . GetNumTracks ( ) ; t + + )
{
2014-07-25 18:45:02 -05:00
if ( nd . GetTapNote ( t , r ) . type = = TapNoteType_Empty )
2012-07-03 12:51:03 -05:00
continue ;
iNumTracksWithTapOrHoldHead + + ;
// send crossed message
2014-12-07 01:53:17 -07:00
if ( GAMESTATE - > GetCurrentGame ( ) - > m_PlayersHaveSeparateStyles )
{
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
const Style * pStyle = GAMESTATE - > GetCurrentStyle ( pi - > m_pn ) ;
RString sButton = pStyle - > ColToButtonName ( t ) ;
Message msg ( i = = 0 ? " NoteCrossed " : " NoteWillCross " ) ;
msg . SetParam ( " ButtonName " , sButton ) ;
msg . SetParam ( " NumMessagesFromCrossed " , i ) ;
msg . SetParam ( " PlayerNumber " , pi - > m_pn ) ;
MESSAGEMAN - > Broadcast ( msg ) ;
}
}
else
{
const Style * pStyle = GAMESTATE - > GetCurrentStyle ( PLAYER_INVALID ) ;
RString sButton = pStyle - > ColToButtonName ( t ) ;
Message msg ( i = = 0 ? " NoteCrossed " : " NoteWillCross " ) ;
msg . SetParam ( " ButtonName " , sButton ) ;
msg . SetParam ( " NumMessagesFromCrossed " , i ) ;
MESSAGEMAN - > Broadcast ( msg ) ;
}
2012-07-03 12:51:03 -05:00
}
if ( iNumTracksWithTapOrHoldHead > 0 )
MESSAGEMAN - > Broadcast ( ( MessageID ) ( Message_NoteCrossed + i ) ) ;
if ( i = = 0 & & iNumTracksWithTapOrHoldHead > = 2 )
{
RString sMessageName = " NoteCrossedJump " ;
MESSAGEMAN - > Broadcast ( sMessageName ) ;
}
}
iRowLastCrossed = iRowNow ;
}
}
}
void ScreenGameplay : : BeginBackingOutFromGameplay ( )
{
m_DancingState = STATE_OUTRO ;
2015-05-05 13:17:15 -06:00
ResetGiveUpTimers ( false ) ;
2012-07-03 12:51:03 -05:00
m_pSoundMusic - > StopPlaying ( ) ;
m_GameplayAssist . StopPlaying ( ) ; // Stop any queued assist ticks.
this - > ClearMessageQueue ( ) ;
// If this is the final stage, don't allow extra stage
if ( GAMESTATE - > GetSmallestNumStagesLeftForAnyHumanPlayer ( ) = = 0 )
GAMESTATE - > m_bBackedOutOfFinalStage = true ;
// Disallow backing out of extra stage
if ( GAMESTATE - > IsAnExtraStage ( ) )
SCREENMAN - > PostMessageToTopScreen ( SM_BeginFailed , 0 ) ;
else
m_Cancel . StartTransitioning ( SM_DoPrevScreen ) ;
}
2015-05-05 13:17:15 -06:00
void ScreenGameplay : : AbortGiveUpText ( bool show_abort_text )
2012-07-03 12:51:03 -05:00
{
m_textDebug . StopTweening ( ) ;
2015-05-05 13:17:15 -06:00
if ( show_abort_text )
{
m_textDebug . SetText ( GIVE_UP_ABORTED_TEXT ) ;
}
2012-07-03 12:51:03 -05:00
// otherwise tween out the text that's there
2015-05-05 13:17:15 -06:00
m_textDebug . BeginTweening ( 1 / 2.f ) ;
m_textDebug . SetDiffuse ( RageColor ( 1 , 1 , 1 , 0 ) ) ;
}
void ScreenGameplay : : AbortSkipSong ( bool show_text )
{
if ( m_SkipSongTimer . IsZero ( ) )
{
return ;
}
AbortGiveUpText ( show_text ) ;
m_SkipSongTimer . SetZero ( ) ;
}
void ScreenGameplay : : AbortGiveUp ( bool bShowText )
{
if ( m_GiveUpTimer . IsZero ( ) )
{
return ;
}
AbortGiveUpText ( bShowText ) ;
2012-07-03 12:51:03 -05:00
m_GiveUpTimer . SetZero ( ) ;
}
2015-05-05 13:17:15 -06:00
void ScreenGameplay : : ResetGiveUpTimers ( bool show_text )
{
AbortSkipSong ( show_text ) ;
AbortGiveUp ( show_text ) ;
}
2012-07-03 12:51:03 -05:00
2013-01-12 22:48:38 -05:00
bool ScreenGameplay : : Input ( const InputEventPlus & input )
2012-07-03 12:51:03 -05:00
{
//LOG->Trace( "ScreenGameplay::Input()" );
Message msg ( " " ) ;
if ( m_Codes . InputMessage ( input , msg ) )
this - > HandleMessage ( msg ) ;
if ( m_bPaused )
{
/* If we're paused, only accept GAME_BUTTON_START to unpause. */
if ( GAMESTATE - > IsHumanPlayer ( input . pn ) & & input . MenuI = = GAME_BUTTON_START & & input . type = = IET_FIRST_PRESS )
{
if ( m_PauseController = = GameController_Invalid | | m_PauseController = = input . GameI . controller )
{
// IMO, it's better to have this configurable. -DaisuMaster
2013-01-12 22:48:38 -05:00
if ( UNPAUSE_WITH_START )
{
this - > PauseGame ( false ) ;
return true ;
}
2012-07-03 12:51:03 -05:00
}
}
2013-01-12 22:48:38 -05:00
return false ;
2012-07-03 12:51:03 -05:00
}
2016-07-12 21:00:54 -06:00
if ( m_DancingState ! = STATE_OUTRO & &
2012-07-03 12:51:03 -05:00
GAMESTATE - > IsHumanPlayer ( input . pn ) & &
! m_Cancel . IsTransitioning ( ) )
{
/* Allow bailing out by holding any START button.
* This gives a way to "give up" when a back button isn't available.
* If this is also a style button, don't do this; pump center is start.
*/
bool bHoldingGiveUp = false ;
2014-12-07 01:53:17 -07:00
if ( GAMESTATE - > GetCurrentStyle ( input . pn ) - > GameInputToColumn ( input . GameI ) = = Column_Invalid )
2012-07-03 12:51:03 -05:00
{
bHoldingGiveUp | = ( START_GIVES_UP & & input . MenuI = = GAME_BUTTON_START ) ;
bHoldingGiveUp | = ( BACK_GIVES_UP & & input . MenuI = = GAME_BUTTON_BACK ) ;
}
2015-05-05 13:17:15 -06:00
// Allow holding SELECT to skip the current song in course mode. -Kyz
if ( GAMESTATE - > IsCourseMode ( ) & & SELECT_SKIPS_SONG & &
input . MenuI = = GAME_BUTTON_SELECT )
{
if ( input . type = = IET_RELEASE )
{
AbortSkipSong ( true ) ;
}
else if ( input . type = = IET_FIRST_PRESS & & m_SkipSongTimer . IsZero ( ) )
{
m_textDebug . SetText ( SKIP_SONG_TEXT ) ;
m_textDebug . PlayCommand ( " StartOn " ) ;
m_SkipSongTimer . Touch ( ) ;
}
return true ;
}
2012-07-03 12:51:03 -05:00
if ( bHoldingGiveUp )
{
// No PREFSMAN->m_bDelayedEscape; always delayed.
if ( input . type = = IET_RELEASE )
{
AbortGiveUp ( true ) ;
}
else if ( input . type = = IET_FIRST_PRESS & & m_GiveUpTimer . IsZero ( ) )
{
m_textDebug . SetText ( GIVE_UP_START_TEXT ) ;
m_textDebug . PlayCommand ( " StartOn " ) ;
m_GiveUpTimer . Touch ( ) ; // start the timer
}
2013-01-12 22:48:38 -05:00
return true ;
2012-07-03 12:51:03 -05:00
}
/* Only handle GAME_BUTTON_BACK as a regular BACK button if BACK_GIVES_UP is
* disabled. */
bool bHoldingBack = false ;
2014-12-07 01:53:17 -07:00
if ( GAMESTATE - > GetCurrentStyle ( input . pn ) - > GameInputToColumn ( input . GameI ) = = Column_Invalid )
2012-07-03 12:51:03 -05:00
{
bHoldingBack | = input . MenuI = = GAME_BUTTON_BACK & & ! BACK_GIVES_UP ;
}
if ( bHoldingBack )
{
if ( ( ( ! PREFSMAN - > m_bDelayedBack & & input . type = = IET_FIRST_PRESS ) | |
( input . DeviceI . device = = DEVICE_KEYBOARD & & input . type = = IET_REPEAT ) | |
( input . DeviceI . device ! = DEVICE_KEYBOARD & & INPUTFILTER - > GetSecsHeld ( input . DeviceI ) > = 1.0f ) ) )
{
LOG - > Trace ( " Player %i went back " , input . pn + 1 ) ;
BeginBackingOutFromGameplay ( ) ;
}
else if ( PREFSMAN - > m_bDelayedBack & & input . type = = IET_FIRST_PRESS )
{
m_textDebug . SetText ( GIVE_UP_BACK_TEXT ) ;
m_textDebug . PlayCommand ( " BackOn " ) ;
}
else if ( PREFSMAN - > m_bDelayedBack & & input . type = = IET_RELEASE )
{
m_textDebug . PlayCommand ( " TweenOff " ) ;
}
2013-01-12 22:48:38 -05:00
return true ;
2012-07-03 12:51:03 -05:00
}
}
bool bRelease = input . type = = IET_RELEASE ;
if ( ! input . GameI . IsValid ( ) )
2013-01-12 22:48:38 -05:00
return false ;
2012-07-03 12:51:03 -05:00
2014-12-07 01:53:17 -07:00
int iCol = GAMESTATE - > GetCurrentStyle ( input . pn ) - > GameInputToColumn ( input . GameI ) ;
2012-07-03 12:51:03 -05:00
// Don't pass on any inputs to Player that aren't a press or a release.
switch ( input . type )
{
case IET_FIRST_PRESS :
case IET_RELEASE :
break ;
default :
2013-01-12 22:48:38 -05:00
return false ;
2012-07-03 12:51:03 -05:00
}
if ( GAMESTATE - > m_bMultiplayer )
{
if ( input . mp ! = MultiPlayer_Invalid & & GAMESTATE - > IsMultiPlayerEnabled ( input . mp ) & & iCol ! = - 1 )
{
2013-05-01 23:08:56 -04:00
for ( PlayerInfo const & pi : m_vPlayerInfo )
2012-07-03 12:51:03 -05:00
{
2013-05-01 23:08:56 -04:00
if ( input . mp = = pi . m_mp )
pi . m_pPlayer - > Step ( iCol , - 1 , input . DeviceI . ts , false , bRelease ) ;
2012-07-03 12:51:03 -05:00
}
2013-01-12 22:48:38 -05:00
return true ;
2012-07-03 12:51:03 -05:00
}
}
else
{
// handle a step or battle item activate
if ( GAMESTATE - > IsHumanPlayer ( input . pn ) )
{
2015-05-05 13:17:15 -06:00
ResetGiveUpTimers ( true ) ;
2012-07-03 12:51:03 -05:00
2014-10-27 00:14:35 +09:00
if ( GamePreferences : : m_AutoPlay = = PC_HUMAN & & GAMESTATE - > m_pPlayerState [ input . pn ] - > m_PlayerOptions . GetCurrent ( ) . m_fPlayerAutoPlay = = 0 )
2012-07-03 12:51:03 -05:00
{
PlayerInfo & pi = GetPlayerInfoForInput ( input ) ;
ASSERT ( input . GameI . IsValid ( ) ) ;
GameButtonType gbt = GAMESTATE - > m_pCurGame - > GetPerButtonInfo ( input . GameI . button ) - > m_gbt ;
switch ( gbt )
{
2015-12-05 20:02:00 -06:00
case GameButtonType_Menu :
2013-01-12 22:48:38 -05:00
return false ;
2012-07-03 12:51:03 -05:00
case GameButtonType_Step :
if ( iCol ! = - 1 )
pi . m_pPlayer - > Step ( iCol , - 1 , input . DeviceI . ts , false , bRelease ) ;
2013-01-12 22:48:38 -05:00
return true ;
2012-07-03 12:51:03 -05:00
}
}
}
}
2013-01-12 22:48:38 -05:00
return false ;
2012-07-03 12:51:03 -05:00
}
/* Saving StageStats that are affected by the note pattern is a little tricky:
*
* Stats are cumulative for course play.
*
* For regular songs, it doesn't matter how we do it; the pattern doesn't change
* during play.
*
* The pattern changes during play in battle and course mode. We want to include
* these changes, so run stats for a song after the song finishes.
*
* If we fail, be sure to include the current song in stats,
* with the current modifier set. So:
* 1. At the end of a song in any mode, pass or fail, add stats for that song
* (from m_pPlayer).
* 2. At the end of gameplay in course mode, add stats for any songs that weren't
* played, applying the modifiers the song would have been played with.
* This doesn't include songs that were played but failed; that was done in #1.
*/
void ScreenGameplay : : SaveStats ( )
{
float fMusicLen = GAMESTATE - > m_pCurSong - > m_fMusicLengthSeconds ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
/* Note that adding stats is only meaningful for the counters (eg. RadarCategory_Jumps),
* not for the percentages (RadarCategory_Air). */
RadarValues rv ;
PlayerStageStats & pss = * pi - > GetPlayerStageStats ( ) ;
const NoteData & nd = pi - > m_pPlayer - > GetNoteData ( ) ;
PlayerNumber pn = pi - > m_pn ;
2013-01-23 14:51:18 -05:00
GAMESTATE - > SetProcessedTimingData ( GAMESTATE - > m_pCurSteps [ pn ] - > GetTimingData ( ) ) ;
2012-07-03 12:51:03 -05:00
NoteDataUtil : : CalculateRadarValues ( nd , fMusicLen , rv ) ;
pss . m_radarPossible + = rv ;
NoteDataWithScoring : : GetActualRadarValues ( nd , pss , fMusicLen , rv ) ;
pss . m_radarActual + = rv ;
2013-05-03 23:39:52 -04:00
GAMESTATE - > SetProcessedTimingData ( nullptr ) ;
2012-07-03 12:51:03 -05:00
}
}
void ScreenGameplay : : SongFinished ( )
{
2015-03-14 23:17:35 -06:00
FOREACH_EnabledPlayer ( pn )
{
if ( GAMESTATE - > m_pCurSteps [ pn ] )
{
GAMESTATE - > m_pCurSteps [ pn ] - > GetTimingData ( ) - > ReleaseLookup ( ) ;
}
}
2012-07-03 12:51:03 -05:00
AdjustSync : : HandleSongEnd ( ) ;
SaveStats ( ) ; // Let subclasses save the stats.
/* Extremely important: if we don't remove attacks before moving on to the next
* screen, they'll still be turned on eventually. */
GAMESTATE - > RemoveAllActiveAttacks ( ) ;
FOREACH_VisiblePlayerInfo ( m_vPlayerInfo , pi )
pi - > m_pActiveAttackList - > Refresh ( ) ;
}
void ScreenGameplay : : StageFinished ( bool bBackedOut )
{
if ( GAMESTATE - > IsCourseMode ( ) & & GAMESTATE - > m_PlayMode ! = PLAY_MODE_ENDLESS )
{
LOG - > Trace ( " Stage finished at index %i/%i " , GAMESTATE - > GetCourseSongIndex ( ) , ( int ) m_apSongsQueue . size ( ) ) ;
// +1 to skip the current song; that song has already passed.
for ( unsigned i = GAMESTATE - > GetCourseSongIndex ( ) + 1 ; i < m_apSongsQueue . size ( ) ; + + i )
{
LOG - > Trace ( " Running stats for %i " , i ) ;
SetupSong ( i ) ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
pi - > m_pPlayer - > ApplyWaitingTransforms ( ) ;
SongFinished ( ) ;
}
}
if ( bBackedOut )
{
GAMESTATE - > CancelStage ( ) ;
return ;
}
// If all players failed, kill.
if ( STATSMAN - > m_CurStageStats . AllFailed ( ) )
{
FOREACH_HumanPlayer ( p )
GAMESTATE - > m_iPlayerStageTokens [ p ] = 0 ;
}
FOREACH_HumanPlayer ( pn )
STATSMAN - > m_CurStageStats . m_player [ pn ] . CalcAwards ( pn , STATSMAN - > m_CurStageStats . m_bGaveUp , STATSMAN - > m_CurStageStats . m_bUsedAutoplay ) ;
STATSMAN - > m_CurStageStats . FinalizeScores ( false ) ;
GAMESTATE - > CommitStageStats ( ) ;
// save current stage stats
STATSMAN - > m_vPlayedStageStats . push_back ( STATSMAN - > m_CurStageStats ) ;
STATSMAN - > CalcAccumPlayedStageStats ( ) ;
GAMESTATE - > FinishStage ( ) ;
}
void ScreenGameplay : : HandleScreenMessage ( const ScreenMessage SM )
{
CHECKPOINT_M ( ssprintf ( " HandleScreenMessage(%s) " , ScreenMessageHelpers : : ScreenMessageToString ( SM ) . c_str ( ) ) ) ;
if ( SM = = SM_DoneFadingIn )
{
2016-06-09 16:05:49 -06:00
// If the ready animation is zero length, then playing the sound will
// make it overlap with the go sound.
// If the Ready animation is zero length, and the Go animation is not,
// only play the Go sound.
// If they're both zero length, only play the Ready sound.
// Otherwise, play both sounds.
// -Kyz
2012-07-03 12:51:03 -05:00
m_Ready . StartTransitioning ( SM_PlayGo ) ;
2016-06-09 16:05:49 -06:00
if ( m_Ready . GetTweenTimeLeft ( ) < = .0f )
{
m_delaying_ready_announce = true ;
}
else
{
m_delaying_ready_announce = false ;
SOUND - > PlayOnceFromAnnouncer ( " gameplay ready " ) ;
}
2012-07-03 12:51:03 -05:00
}
else if ( SM = = SM_PlayGo )
{
2016-06-09 16:05:49 -06:00
m_Go . StartTransitioning ( SM_None ) ;
bool should_play_go = true ;
if ( m_delaying_ready_announce )
{
if ( m_Go . GetTweenTimeLeft ( ) < = .0f )
{
SOUND - > PlayOnceFromAnnouncer ( " gameplay ready " ) ;
should_play_go = false ;
}
else
{
should_play_go = true ;
}
}
if ( should_play_go )
{
if ( GAMESTATE - > IsAnExtraStage ( ) )
{
SOUND - > PlayOnceFromAnnouncer ( " gameplay here we go extra " ) ;
}
else if ( GAMESTATE - > GetSmallestNumStagesLeftForAnyHumanPlayer ( ) = = 0 )
{
SOUND - > PlayOnceFromAnnouncer ( " gameplay here we go final " ) ;
}
else
{
SOUND - > PlayOnceFromAnnouncer ( " gameplay here we go normal " ) ;
}
}
2012-07-03 12:51:03 -05:00
2014-09-12 15:07:46 -06:00
GAMESTATE - > m_DanceStartTime . Touch ( ) ;
2012-07-03 12:51:03 -05:00
GAMESTATE - > m_bGameplayLeadIn . Set ( false ) ;
m_DancingState = STATE_DANCING ; // STATE CHANGE! Now the user is allowed to press Back
}
else if ( SM = = SM_NotesEnded ) // received while STATE_DANCING
{
2015-05-05 13:17:15 -06:00
ResetGiveUpTimers ( false ) ; // don't allow giveup while the next song is loading
2012-07-03 12:51:03 -05:00
/* Do this in LoadNextSong, so we don't tween off old attacks until
* m_NextSong finishes. */
// GAMESTATE->RemoveAllActiveAttacks();
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
// Mark failure.
2014-12-22 03:52:08 -07:00
if ( GAMESTATE - > GetPlayerFailType ( pi - > GetPlayerState ( ) ) ! = FailType_Off
& & ( pi - > m_pLifeMeter & & pi - > m_pLifeMeter - > IsFailing ( ) ) )
{
2012-07-03 12:51:03 -05:00
pi - > GetPlayerStageStats ( ) - > m_bFailed = true ;
2014-12-22 03:52:08 -07:00
}
2012-07-03 12:51:03 -05:00
if ( ! pi - > GetPlayerStageStats ( ) - > m_bFailed )
2014-12-22 03:52:08 -07:00
{
2012-07-03 12:51:03 -05:00
pi - > GetPlayerStageStats ( ) - > m_iSongsPassed + + ;
2014-12-22 03:52:08 -07:00
}
2012-07-03 12:51:03 -05:00
// set a life record at the point of failure
if ( pi - > GetPlayerStageStats ( ) - > m_bFailed )
2014-12-22 03:52:08 -07:00
{
pi - > GetPlayerStageStats ( ) - > SetLifeRecordAt (
0 , STATSMAN - > m_CurStageStats . m_fGameplaySeconds ) ;
}
2012-07-03 12:51:03 -05:00
}
/* If all players have *really* failed (bFailed, not the life meter or
* bFailedEarlier): */
const bool bAllReallyFailed = STATSMAN - > m_CurStageStats . AllFailed ( ) ;
const bool bStopCourseEarly = STOP_COURSE_EARLY ;
const bool bIsLastSong = IsLastSong ( ) ;
2014-12-22 03:52:08 -07:00
LOG - > Trace ( " bAllReallyFailed = %d, bStopCourseEarly = %d, "
2015-05-05 13:17:15 -06:00
" bIsLastSong = %d, m_gave_up = %d, m_skipped_song = %d " ,
bAllReallyFailed , bStopCourseEarly , bIsLastSong , m_gave_up ,
m_skipped_song ) ;
2012-07-03 12:51:03 -05:00
2015-05-05 13:17:15 -06:00
if ( ! bIsLastSong & & m_skipped_song )
{
// Load the next song in the course.
HandleScreenMessage ( SM_StartLoadingNextSong ) ;
return ;
}
2014-12-22 03:52:08 -07:00
if ( bStopCourseEarly | | bAllReallyFailed | | bIsLastSong | | m_gave_up )
2012-07-03 12:51:03 -05:00
{
// Time to leave from ScreenGameplay
HandleScreenMessage ( SM_LeaveGameplay ) ;
}
else
{
// Load the next song in the course.
HandleScreenMessage ( SM_StartLoadingNextSong ) ;
return ;
}
}
else if ( SM = = SM_LeaveGameplay )
{
2014-09-12 15:07:46 -06:00
GAMESTATE - > m_DanceDuration = GAMESTATE - > m_DanceStartTime . Ago ( ) ;
2012-07-03 12:51:03 -05:00
// update dancing characters for win / lose
2013-05-03 23:39:52 -04:00
DancingCharacters * pDancers = nullptr ;
2012-07-03 12:51:03 -05:00
if ( m_pSongBackground )
pDancers = m_pSongBackground - > GetDancingCharacters ( ) ;
if ( pDancers )
{
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
// XXX: In battle modes, switch( GAMESTATE->GetStageResult(p) ).
if ( pi - > GetPlayerStageStats ( ) - > m_bFailed )
pDancers - > Change2DAnimState ( pi - > m_pn , AS2D_FAIL ) ; // fail anim
else if ( pi - > m_pLifeMeter & & pi - > GetPlayerState ( ) - > m_HealthState = = HealthState_Hot )
pDancers - > Change2DAnimState ( pi - > m_pn , AS2D_WINFEVER ) ; // full life pass anim
else
pDancers - > Change2DAnimState ( pi - > m_pn , AS2D_WIN ) ; // pass anim
}
}
// End round.
if ( m_DancingState = = STATE_OUTRO ) // ScreenGameplay already ended
return ; // ignore
m_DancingState = STATE_OUTRO ;
2015-05-05 13:17:15 -06:00
ResetGiveUpTimers ( false ) ;
2012-07-03 12:51:03 -05:00
GAMESTATE - > RemoveAllActiveAttacks ( ) ;
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( pi - > m_pActiveAttackList )
pi - > m_pActiveAttackList - > Refresh ( ) ;
}
LIGHTSMAN - > SetLightsMode ( LIGHTSMODE_ALL_CLEARED ) ;
bool bAllReallyFailed = STATSMAN - > m_CurStageStats . AllFailed ( ) ;
if ( bAllReallyFailed )
{
this - > PostScreenMessage ( SM_BeginFailed , 0 ) ;
return ;
}
// todo: add GameplayCleared, StartTransitioningCleared commands -aj
TweenOffScreen ( ) ;
m_Out . StartTransitioning ( SM_DoNextScreen ) ;
// do they deserve an extra stage?
if ( GAMESTATE - > HasEarnedExtraStage ( ) )
SOUND - > PlayOnceFromAnnouncer ( " gameplay extra " ) ;
else
SOUND - > PlayOnceFromAnnouncer ( " gameplay cleared " ) ;
}
else if ( SM = = SM_StartLoadingNextSong )
{
// Next song.
// give a little life back between stages
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( pi - > m_pLifeMeter )
pi - > m_pLifeMeter - > OnSongEnded ( ) ;
}
2014-09-10 02:44:49 -06:00
// If this is a repeating course, and we're at the end of it, repick and
// add new songs to the players' step and song queues.
if ( GAMESTATE - > IsCourseMode ( ) & & GAMESTATE - > m_pCurCourse & &
GAMESTATE - > m_pCurCourse - > m_bRepeat & &
GAMESTATE - > GetCourseSongIndex ( ) > = ( int ) m_apSongsQueue . size ( ) - 1 )
{
Course * course = GAMESTATE - > m_pCurCourse ;
2019-06-22 12:35:38 -07:00
ASSERT ( course ! = nullptr ) ;
2014-09-10 02:44:49 -06:00
// Need to store these so they can be used to refetch the players'
// trails after they're invalidated.
2022-07-10 18:28:56 +03:00
std : : vector < StepsType > trail_sts ;
std : : vector < CourseDifficulty > trail_cds ;
2014-09-10 02:44:49 -06:00
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Trail * trail = GAMESTATE - > m_pCurTrail [ pi - > GetStepsAndTrailIndex ( ) ] ;
2019-06-22 12:35:38 -07:00
ASSERT ( trail ! = nullptr ) ;
2014-09-10 02:44:49 -06:00
trail_sts . push_back ( trail - > m_StepsType ) ;
trail_cds . push_back ( trail - > m_CourseDifficulty ) ;
}
// Set a new stage seed so the order will be different.
GAMESTATE - > SetNewStageSeed ( ) ;
course - > InvalidateTrailCache ( ) ;
course - > RegenerateNonFixedTrails ( ) ;
2023-04-19 23:04:25 +02:00
std : : size_t info_id = 0 ; // Can't use the player number in the playerinfo
2014-09-10 02:44:49 -06:00
// because it won't match up in 2-player.
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Trail * trail = course - > GetTrail ( trail_sts [ info_id ] , trail_cds [ info_id ] ) ;
2019-06-22 12:35:38 -07:00
ASSERT ( trail ! = nullptr ) ;
2014-09-10 02:44:49 -06:00
GAMESTATE - > m_pCurTrail [ pi - > m_pn ] . Set ( trail ) ;
+ + info_id ;
}
PlayerNumber master_pn = GAMESTATE - > GetMasterPlayerNumber ( ) ;
Trail * master_trail = GAMESTATE - > m_pCurTrail [ master_pn ] ;
2019-06-22 12:35:38 -07:00
ASSERT ( master_trail ! = nullptr ) ;
for ( TrailEntry const & entry : master_trail - > m_vEntries )
2014-09-10 02:44:49 -06:00
{
2019-06-22 12:35:38 -07:00
ASSERT ( entry . pSong ! = nullptr ) ;
m_apSongsQueue . push_back ( entry . pSong ) ;
STATSMAN - > m_CurStageStats . m_vpPossibleSongs . push_back ( entry . pSong ) ;
2014-09-10 02:44:49 -06:00
}
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Trail * trail = GAMESTATE - > m_pCurTrail [ pi - > GetStepsAndTrailIndex ( ) ] ;
2019-06-22 12:35:38 -07:00
ASSERT ( trail ! = nullptr ) ;
for ( TrailEntry const & entry : master_trail - > m_vEntries )
2014-09-10 02:44:49 -06:00
{
2019-06-22 12:35:38 -07:00
ASSERT ( entry . pSteps ! = nullptr ) ;
pi - > m_vpStepsQueue . push_back ( entry . pSteps ) ;
2014-09-10 02:44:49 -06:00
AttackArray a ;
2019-06-22 12:35:38 -07:00
entry . GetAttackArray ( a ) ;
2014-09-10 02:44:49 -06:00
pi - > m_asModifiersQueue . push_back ( a ) ;
2019-06-22 12:35:38 -07:00
pi - > GetPlayerStageStats ( ) - > m_vpPossibleSteps . push_back ( entry . pSteps ) ;
2014-09-10 02:44:49 -06:00
}
// In a survival course, override stored mods
if ( course - > GetCourseType ( ) = = COURSE_TYPE_SURVIVAL & &
SURVIVAL_MOD_OVERRIDE )
{
2020-10-20 02:16:02 +09:00
pi - > GetPlayerState ( ) - > m_PlayerOptions . FromString ( ModsLevel_Stage ,
2014-09-10 02:44:49 -06:00
" clearall, " + CommonMetrics : : DEFAULT_NOTESKIN_NAME . GetValue ( ) +
" , " + CommonMetrics : : DEFAULT_MODIFIERS . GetValue ( ) ) ;
pi - > GetPlayerState ( ) - > RebuildPlayerOptionsFromActiveAttacks ( ) ;
}
}
}
2012-07-03 12:51:03 -05:00
GAMESTATE - > m_bLoadingNextSong = true ;
MESSAGEMAN - > Broadcast ( " BeforeLoadingNextCourseSong " ) ;
m_NextSong . Reset ( ) ;
m_NextSong . PlayCommand ( " Start " ) ;
m_NextSong . StartTransitioning ( SM_LoadNextSong ) ;
MESSAGEMAN - > Broadcast ( " ChangeCourseSongIn " ) ;
}
else if ( SM = = SM_LoadNextSong )
{
m_pSoundMusic - > Stop ( ) ;
SongFinished ( ) ;
MESSAGEMAN - > Broadcast ( " ChangeCourseSongOut " ) ;
GAMESTATE - > m_bLoadingNextSong = false ;
LoadNextSong ( ) ;
m_NextSong . Reset ( ) ;
m_NextSong . PlayCommand ( " Finish " ) ;
m_NextSong . StartTransitioning ( SM_None ) ;
StartPlayingSong ( MIN_SECONDS_TO_STEP_NEXT_SONG , 0 ) ;
}
else if ( SM = = SM_PlayToasty )
{
2015-04-18 08:52:57 -06:00
if ( g_bEasterEggs )
2015-04-17 16:26:39 -06:00
{
2015-04-18 08:52:57 -06:00
if ( PREFSMAN - > m_AllowMultipleToasties | |
m_Toasty . IsWaiting ( ) )
{
m_Toasty . Reset ( ) ;
m_Toasty . StartTransitioning ( ) ;
}
2015-04-17 16:26:39 -06:00
}
2012-07-03 12:51:03 -05:00
}
2022-07-10 18:28:56 +03:00
else if ( ScreenMessageHelpers : : ScreenMessageToString ( SM ) . find ( " 0Combo " ) ! = std : : string : : npos )
2012-07-03 12:51:03 -05:00
{
int iCombo ;
RString sCropped = ScreenMessageHelpers : : ScreenMessageToString ( SM ) . substr ( 3 ) ;
sscanf ( sCropped . c_str ( ) , " %d%*s " , & iCombo ) ;
PlayAnnouncer ( ssprintf ( " gameplay %d combo " , iCombo ) , 2 ) ;
}
else if ( SM = = SM_ComboStopped )
{
PlayAnnouncer ( " gameplay combo stopped " , 2 ) ;
}
else if ( SM = = SM_ComboContinuing )
{
PlayAnnouncer ( " gameplay combo overflow " , 2 ) ;
}
else if ( SM > = SM_BattleTrickLevel1 & & SM < = SM_BattleTrickLevel3 )
{
int iTrickLevel = SM - SM_BattleTrickLevel1 + 1 ;
PlayAnnouncer ( ssprintf ( " gameplay battle trick level%d " , iTrickLevel ) , 3 ) ;
2015-02-28 06:25:02 -07:00
if ( SM = = SM_BattleTrickLevel1 ) m_soundBattleTrickLevel1 . Play ( false ) ;
else if ( SM = = SM_BattleTrickLevel2 ) m_soundBattleTrickLevel2 . Play ( false ) ;
else if ( SM = = SM_BattleTrickLevel3 ) m_soundBattleTrickLevel3 . Play ( false ) ;
2012-07-03 12:51:03 -05:00
}
else if ( SM > = SM_BattleDamageLevel1 & & SM < = SM_BattleDamageLevel3 )
{
int iDamageLevel = SM - SM_BattleDamageLevel1 + 1 ;
PlayAnnouncer ( ssprintf ( " gameplay battle damage level%d " , iDamageLevel ) , 3 ) ;
}
else if ( SM = = SM_DoPrevScreen )
{
SongFinished ( ) ;
this - > StageFinished ( true ) ;
m_sNextScreen = GetPrevScreen ( ) ;
if ( AdjustSync : : IsSyncDataChanged ( ) )
ScreenSaveSync : : PromptSaveSync ( SM_GoToPrevScreen ) ;
else
HandleScreenMessage ( SM_GoToPrevScreen ) ;
}
else if ( SM = = SM_DoNextScreen )
{
SongFinished ( ) ;
this - > StageFinished ( false ) ;
// only save replays if the player chose to
if ( GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_bSaveReplay )
SaveReplay ( ) ;
if ( AdjustSync : : IsSyncDataChanged ( ) )
ScreenSaveSync : : PromptSaveSync ( SM_GoToNextScreen ) ;
else
HandleScreenMessage ( SM_GoToNextScreen ) ;
}
else if ( SM = = SM_GainFocus )
{
// We do this ourself.
SOUND - > HandleSongTimer ( false ) ;
}
else if ( SM = = SM_LoseFocus )
{
// We might have turned the song timer off. Be sure to turn it back on.
SOUND - > HandleSongTimer ( true ) ;
}
else if ( SM = = SM_BeginFailed )
{
m_DancingState = STATE_OUTRO ;
2015-05-05 13:17:15 -06:00
ResetGiveUpTimers ( false ) ;
2012-07-03 12:51:03 -05:00
m_GameplayAssist . StopPlaying ( ) ; // Stop any queued assist ticks.
TweenOffScreen ( ) ;
m_Failed . StartTransitioning ( SM_DoNextScreen ) ;
// show the survive time if extra stage
if ( GAMESTATE - > IsAnExtraStage ( ) )
{
float fMaxAliveSeconds = 0 ;
FOREACH_EnabledPlayer ( p )
2022-07-10 18:28:56 +03:00
fMaxAliveSeconds = std : : max ( fMaxAliveSeconds , STATSMAN - > m_CurStageStats . m_player [ p ] . m_fAliveSeconds ) ;
2012-07-03 12:51:03 -05:00
m_textSurviveTime . SetText ( " TIME: " + SecondsToMMSSMsMs ( fMaxAliveSeconds ) ) ;
ON_COMMAND ( m_textSurviveTime ) ;
}
if ( GAMESTATE - > IsCourseMode ( ) )
{
if ( GAMESTATE - > GetCourseSongIndex ( ) > = int ( m_apSongsQueue . size ( ) / 2 ) )
SOUND - > PlayOnceFromAnnouncer ( " gameplay oni failed halfway " ) ;
else
SOUND - > PlayOnceFromAnnouncer ( " gameplay oni failed " ) ;
}
else
{
SOUND - > PlayOnceFromAnnouncer ( " gameplay failed " ) ;
}
}
else if ( SM = = SM_Pause )
{
// Ignore SM_Pause when in demonstration.
if ( GAMESTATE - > m_bDemonstrationOrJukebox )
return ;
if ( ! m_bPaused )
PauseGame ( true ) ;
}
ScreenWithMenuElements : : HandleScreenMessage ( SM ) ;
}
void ScreenGameplay : : HandleMessage ( const Message & msg )
{
if ( msg = = " Judgment " )
{
PlayerNumber pn ;
msg . GetParam ( " Player " , pn ) ;
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
if ( pi - > m_pn ! = pn )
continue ;
if ( ! pi - > GetPlayerState ( ) - > m_PlayerOptions . GetCurrent ( ) . m_bMuteOnError )
continue ;
RageSoundReader * pSoundReader = m_AutoKeysounds . GetPlayerSound ( pn ) ;
2013-05-03 23:11:42 -04:00
if ( pSoundReader = = nullptr )
2012-07-03 12:51:03 -05:00
pSoundReader = m_AutoKeysounds . GetSharedSound ( ) ;
HoldNoteScore hns ;
msg . GetParam ( " HoldNoteScore " , hns ) ;
TapNoteScore tns ;
msg . GetParam ( " TapNoteScore " , tns ) ;
bool bOn = false ;
if ( hns ! = HoldNoteScore_Invalid )
bOn = hns ! = HNS_LetGo ;
else
bOn = tns ! = TNS_Miss ;
if ( pSoundReader )
pSoundReader - > SetProperty ( " Volume " , bOn ? 1.0f : 0.0f ) ;
}
}
ScreenWithMenuElements : : HandleMessage ( msg ) ;
}
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
void ScreenGameplay : : Cancel ( ScreenMessage smSendWhenDone )
{
m_pSoundMusic - > Stop ( ) ;
ScreenWithMenuElements : : Cancel ( smSendWhenDone ) ;
}
Song * ScreenGameplay : : GetNextCourseSong ( ) const
{
ASSERT ( GAMESTATE - > IsCourseMode ( ) ) ;
int iPlaySongIndex = GAMESTATE - > GetCourseSongIndex ( ) + 1 ;
iPlaySongIndex % = m_apSongsQueue . size ( ) ;
return m_apSongsQueue [ iPlaySongIndex ] ;
}
PlayerInfo * ScreenGameplay : : GetPlayerInfo ( PlayerNumber pn )
{
FOREACH_EnabledPlayerNumberInfo ( m_vPlayerInfo , pi )
{
if ( pi - > m_pn = = pn )
return & * pi ;
}
2013-05-03 23:16:39 -04:00
return nullptr ;
2012-07-03 12:51:03 -05:00
}
PlayerInfo * ScreenGameplay : : GetDummyPlayerInfo ( int iDummyIndex )
{
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
if ( pi - > m_bIsDummy & & pi - > m_iDummyIndex = = iDummyIndex )
return & * pi ;
}
2013-05-03 23:16:39 -04:00
return nullptr ;
2012-07-03 12:51:03 -05:00
}
void ScreenGameplay : : SaveReplay ( )
{
/* Replay data TODO:
* Add more player information (?)
* Add AutoGen flag if steps were autogen?
* Add proper steps hash?
* Add modifiers used
* Add date played, machine played on, etc.
* Hash of some stuff to validate data (see Profile)
*/
FOREACH_HumanPlayer ( pn )
{
FOREACH_EnabledPlayerInfo ( m_vPlayerInfo , pi )
{
Profile * pTempProfile = PROFILEMAN - > GetProfile ( pn ) ;
XNode * p = new XNode ( " ReplayData " ) ;
// append version number (in case the format changes)
p - > AppendAttr ( " Version " , 0 ) ;
// song information node
SongID songID ;
songID . FromSong ( GAMESTATE - > m_pCurSong ) ;
XNode * pSongInfoNode = songID . CreateNode ( ) ;
pSongInfoNode - > AppendChild ( " Title " , GAMESTATE - > m_pCurSong - > GetDisplayFullTitle ( ) ) ;
pSongInfoNode - > AppendChild ( " Artist " , GAMESTATE - > m_pCurSong - > GetDisplayArtist ( ) ) ;
p - > AppendChild ( pSongInfoNode ) ;
// steps information
StepsID stepsID ;
stepsID . FromSteps ( GAMESTATE - > m_pCurSteps [ pn ] ) ;
XNode * pStepsInfoNode = stepsID . CreateNode ( ) ;
// hashing = argh
//pStepsInfoNode->AppendChild("StepsHash", stepsID.ToSteps(GAMESTATE->m_pCurSong,false)->GetHash());
p - > AppendChild ( pStepsInfoNode ) ;
// player information node (rival data sup)
XNode * pPlayerInfoNode = new XNode ( " Player " ) ;
pPlayerInfoNode - > AppendChild ( " DisplayName " , pTempProfile - > m_sDisplayName ) ;
pPlayerInfoNode - > AppendChild ( " Guid " , pTempProfile - > m_sGuid ) ;
p - > AppendChild ( pPlayerInfoNode ) ;
// the timings.
p - > AppendChild ( pi - > m_pPlayer - > GetNoteData ( ) . CreateNode ( ) ) ;
// Find a file name for the replay
2022-07-10 18:28:56 +03:00
std : : vector < RString > files ;
2012-07-03 12:51:03 -05:00
GetDirListing ( " Save/Replays/replay* " , files , false , false ) ;
sort ( files . begin ( ) , files . end ( ) ) ;
// Files should be of the form "replay#####.xml".
int iIndex = 0 ;
for ( int i = files . size ( ) - 1 ; i > = 0 ; - - i )
{
static Regex re ( " ^replay([0-9]{5}) \ \ . . . . $ " ) ;
2022-07-10 18:28:56 +03:00
std : : vector < RString > matches ;
2012-07-03 12:51:03 -05:00
if ( ! re . Compare ( files [ i ] , matches ) )
continue ;
ASSERT ( matches . size ( ) = = 1 ) ;
2021-02-15 20:36:51 +00:00
iIndex = StringToInt ( matches [ 0 ] ) + 1 ;
2012-07-03 12:51:03 -05:00
break ;
}
RString sFileName = ssprintf ( " replay%05d.xml " , iIndex ) ;
XmlFileUtil : : SaveToFile ( p , " Save/Replays/ " + sFileName ) ;
SAFE_DELETE ( p ) ;
return ;
}
}
}
/*
bool ScreenGameplay::LoadReplay()
{
// Load replay which was selected via options
}
*/
// lua start
# include "LuaBinding.h"
2014-05-05 14:16:08 -06:00
# include "OptionsBinding.h"
2012-07-03 12:51:03 -05:00
2020-10-20 02:16:02 +09:00
/** @brief Allow Lua to have access to the ScreenGameplay. */
2012-07-03 12:51:03 -05:00
class LunaScreenGameplay : public Luna < ScreenGameplay >
{
public :
static int GetNextCourseSong ( T * p , lua_State * L ) { p - > GetNextCourseSong ( ) - > PushSelf ( L ) ; return 1 ; }
static int Center1Player ( T * p , lua_State * L ) { lua_pushboolean ( L , p - > Center1Player ( ) ) ; return 1 ; }
static int GetLifeMeter ( T * p , lua_State * L )
{
PlayerNumber pn = Enum : : Check < PlayerNumber > ( L , 1 ) ;
PlayerInfo * pi = p - > GetPlayerInfo ( pn ) ;
2013-05-03 23:11:42 -04:00
if ( pi = = nullptr )
2012-07-03 12:51:03 -05:00
return 0 ;
LifeMeter * pLM = pi - > m_pLifeMeter ;
2013-05-03 23:11:42 -04:00
if ( pLM = = nullptr )
2012-07-03 12:51:03 -05:00
return 0 ;
pLM - > PushSelf ( L ) ;
return 1 ;
}
static int GetPlayerInfo ( T * p , lua_State * L )
{
PlayerNumber pn = Enum : : Check < PlayerNumber > ( L , 1 ) ;
PlayerInfo * pi = p - > GetPlayerInfo ( pn ) ;
2013-05-03 23:11:42 -04:00
if ( pi = = nullptr )
2012-07-03 12:51:03 -05:00
return 0 ;
pi - > PushSelf ( L ) ;
return 1 ;
}
static int GetDummyPlayerInfo ( T * p , lua_State * L )
{
int iDummyIndex = IArg ( 1 ) ;
PlayerInfo * pi = p - > GetDummyPlayerInfo ( iDummyIndex ) ;
2013-05-03 23:11:42 -04:00
if ( pi = = nullptr )
2012-07-03 12:51:03 -05:00
return 0 ;
pi - > PushSelf ( L ) ;
return 1 ;
}
static int PauseGame ( T * p , lua_State * L ) { p - > Pause ( BArg ( 1 ) ) ; return 0 ; }
static int IsPaused ( T * p , lua_State * L ) { lua_pushboolean ( L , p - > IsPaused ( ) ) ; return 1 ; }
2014-04-15 12:27:54 -05:00
static int GetHasteRate ( T * p , lua_State * L ) { lua_pushnumber ( L , p - > GetHasteRate ( ) ) ; return 1 ; }
2014-05-05 14:16:08 -06:00
static bool TurningPointsValid ( lua_State * L , int index )
{
2023-04-19 23:04:25 +02:00
std : : size_t size = lua_objlen ( L , index ) ;
2014-05-05 14:16:08 -06:00
if ( size < 2 )
{
luaL_error ( L , " Invalid number of entries %zu " , size ) ;
}
float prev_turning = - 1 ;
2023-04-19 23:04:25 +02:00
for ( std : : size_t n = 1 ; n < size ; + + n )
2014-05-05 14:16:08 -06:00
{
lua_pushnumber ( L , n ) ;
lua_gettable ( L , index ) ;
float v = FArg ( - 1 ) ;
if ( v < prev_turning | | v > 1 )
{
luaL_error ( L , " Invalid value %f " , v ) ;
}
lua_pop ( L , 1 ) ;
}
return true ;
}
static bool AddAmountsValid ( lua_State * L , int index )
{
return TurningPointsValid ( L , index ) ;
}
FLOAT_TABLE_INTERFACE ( HasteTurningPoints , HasteTurningPoints , TurningPointsValid ) ;
FLOAT_TABLE_INTERFACE ( HasteAddAmounts , HasteAddAmounts , AddAmountsValid ) ;
FLOAT_NO_SPEED_INTERFACE ( HasteTimeBetweenUpdates , HasteTimeBetweenUpdates , ( v > 0 ) ) ;
FLOAT_NO_SPEED_INTERFACE ( HasteLifeSwitchPoint , HasteLifeSwitchPoint , ( v > = 0 & & v < = 1 ) ) ;
2016-01-09 11:40:22 -07:00
static int begin_backing_out ( T * p , lua_State * L )
{
p - > BeginBackingOutFromGameplay ( ) ;
COMMON_RETURN_SELF ;
}
2014-05-05 14:16:08 -06:00
static int GetTrueBPS ( T * p , lua_State * L )
{
PlayerNumber pn = Enum : : Check < PlayerNumber > ( L , 1 ) ;
float haste = p - > GetHasteRate ( ) ;
float rate = GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_fMusicRate ;
float bps = GAMESTATE - > m_pPlayerState [ pn ] - > m_Position . m_fCurBPS ;
float true_bps = haste * rate * bps ;
lua_pushnumber ( L , true_bps ) ;
return 1 ;
}
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
LunaScreenGameplay ( )
{
ADD_METHOD ( GetNextCourseSong ) ;
ADD_METHOD ( Center1Player ) ;
ADD_METHOD ( GetLifeMeter ) ;
ADD_METHOD ( GetPlayerInfo ) ;
ADD_METHOD ( GetDummyPlayerInfo ) ;
// sm-ssc additions:
ADD_METHOD ( PauseGame ) ;
ADD_METHOD ( IsPaused ) ;
2014-04-15 12:27:54 -05:00
ADD_METHOD ( GetHasteRate ) ;
2014-05-05 14:16:08 -06:00
ADD_METHOD ( HasteTurningPoints ) ;
ADD_METHOD ( HasteAddAmounts ) ;
ADD_METHOD ( HasteTimeBetweenUpdates ) ;
ADD_METHOD ( HasteLifeSwitchPoint ) ;
2016-01-09 11:40:22 -07:00
ADD_METHOD ( begin_backing_out ) ;
2014-05-05 14:16:08 -06:00
ADD_METHOD ( GetTrueBPS ) ;
2012-07-03 12:51:03 -05:00
}
} ;
LUA_REGISTER_DERIVED_CLASS ( ScreenGameplay , ScreenWithMenuElements )
2020-10-20 02:16:02 +09:00
/** @brief Allow Lua to have access to the PlayerInfo. */
2012-07-03 12:51:03 -05:00
class LunaPlayerInfo : public Luna < PlayerInfo >
{
public :
2014-07-25 00:20:38 -06:00
static int GetLifeMeter ( T * p , lua_State * L )
{
if ( p - > m_pLifeMeter )
{
p - > m_pLifeMeter - > PushSelf ( L ) ;
return 1 ;
}
return 0 ;
}
2020-10-20 02:16:02 +09:00
2012-07-03 12:51:03 -05:00
static int GetStepsQueueWrapped ( T * p , lua_State * L )
{
int iIndex = IArg ( 1 ) ;
iIndex % = p - > m_vpStepsQueue . size ( ) ;
Steps * pSteps = p - > m_vpStepsQueue [ iIndex ] ;
pSteps - > PushSelf ( L ) ;
return 1 ;
}
LunaPlayerInfo ( )
{
ADD_METHOD ( GetLifeMeter ) ;
ADD_METHOD ( GetStepsQueueWrapped ) ;
}
} ;
LUA_REGISTER_CLASS ( PlayerInfo )
// lua end
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
2020-10-20 02:16:02 +09:00
*
2012-07-03 12:51:03 -05:00
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
2020-10-20 02:16:02 +09:00
*
2012-07-03 12:51:03 -05:00
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/