2011-03-17 01:47:30 -04:00
#include "global.h"
#include "GameState.h"
2011-10-02 21:40:33 -05:00
#include "Actor.h"
2011-03-17 01:47:30 -04:00
#include "AdjustSync.h"
#include "AnnouncerManager.h"
2013-06-04 23:47:22 -04:00
#include "Bookkeeper.h"
2011-03-17 01:47:30 -04:00
#include "Character.h"
#include "CharacterManager.h"
#include "CommonMetrics.h"
#include "Course.h"
#include "CryptManager.h"
#include "Foreach.h"
#include "Game.h"
#include "GameCommand.h"
#include "GameConstantsAndTypes.h"
#include "GameManager.h"
#include "GamePreferences.h"
#include "HighScore.h"
#include "LightsManager.h"
#include "LuaReference.h"
#include "MessageManager.h"
#include "MemoryCardManager.h"
#include "NoteSkinManager.h"
#include "PlayerState.h"
#include "PrefsManager.h"
#include "Profile.h"
#include "ProfileManager.h"
#include "RageFile.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "Song.h"
#include "SongManager.h"
#include "SongUtil.h"
#include "StatsManager.h"
#include "Steps.h"
#include "Style.h"
#include "ThemeManager.h"
#include "UnlockManager.h"
#include "ScreenManager.h"
#include "Screen.h"
#include <ctime>
#include <set>
2013-11-30 09:50:54 -06:00
GameState * GAMESTATE = NULL ; // global and accessible from anywhere in our program
2011-03-17 01:47:30 -04:00
#define NAME_BLACKLIST_FILE "/Data/NamesBlacklist.txt"
class GameStateMessageHandler : public MessageSubscriber
{
void HandleMessage ( const Message & msg )
{
if ( msg . GetName () == "RefreshCreditText" )
{
RString sJoined ;
FOREACH_HumanPlayer ( pn )
{
if ( sJoined != "" )
sJoined += ", " ;
sJoined += ssprintf ( "P%i" , pn + 1 );
}
if ( sJoined == "" )
sJoined = "none" ;
LOG -> MapLog ( "JOINED" , "Players joined: %s" , sJoined . c_str () );
}
}
};
struct GameStateImpl
{
GameStateMessageHandler m_Subscriber ;
GameStateImpl ()
{
m_Subscriber . SubscribeToMessage ( "RefreshCreditText" );
}
};
static GameStateImpl * g_pImpl = NULL ;
ThemeMetric < bool > ALLOW_LATE_JOIN ( "GameState" , "AllowLateJoin" );
ThemeMetric < bool > USE_NAME_BLACKLIST ( "GameState" , "UseNameBlacklist" );
ThemeMetric < RString > DEFAULT_SORT ( "GameState" , "DefaultSort" );
SortOrder GetDefaultSort ()
{
return StringToSortOrder ( DEFAULT_SORT );
}
ThemeMetric < RString > DEFAULT_SONG ( "GameState" , "DefaultSong" );
Song * GameState :: GetDefaultSong () const
{
SongID sid ;
sid . FromString ( DEFAULT_SONG );
return sid . ToSong ();
}
2011-09-29 22:09:52 -04:00
static const ThemeMetric < bool > EDIT_ALLOWED_FOR_EXTRA ( "GameState" , "EditAllowedForExtra" );
2011-03-17 01:47:30 -04:00
static const ThemeMetric < Difficulty > MIN_DIFFICULTY_FOR_EXTRA ( "GameState" , "MinDifficultyForExtra" );
static const ThemeMetric < Grade > GRADE_TIER_FOR_EXTRA_1 ( "GameState" , "GradeTierForExtra1" );
static const ThemeMetric < bool > ALLOW_EXTRA_2 ( "GameState" , "AllowExtra2" );
static const ThemeMetric < Grade > GRADE_TIER_FOR_EXTRA_2 ( "GameState" , "GradeTierForExtra2" );
static ThemeMetric < bool > ARE_STAGE_PLAYER_MODS_FORCED ( "GameState" , "AreStagePlayerModsForced" );
static ThemeMetric < bool > ARE_STAGE_SONG_MODS_FORCED ( "GameState" , "AreStageSongModsForced" );
2012-03-01 17:20:10 -06:00
static Preference < Premium > g_Premium ( "Premium" , Premium_DoubleFor1Credit );
2011-03-17 01:47:30 -04:00
Preference < bool > GameState :: m_bAutoJoin ( "AutoJoin" , false );
GameState :: GameState () :
2011-06-08 14:43:59 -04:00
processedTiming ( NULL ),
2011-03-17 01:47:30 -04:00
m_pCurGame ( Message_CurrentGameChanged ),
m_pCurStyle ( Message_CurrentStyleChanged ),
m_PlayMode ( Message_PlayModeChanged ),
2013-06-04 23:47:22 -04:00
m_iCoins ( Message_CoinsChanged ),
2011-03-17 01:47:30 -04:00
m_sPreferredSongGroup ( Message_PreferredSongGroupChanged ),
m_sPreferredCourseGroup ( Message_PreferredCourseGroupChanged ),
m_PreferredStepsType ( Message_PreferredStepsTypeChanged ),
m_PreferredDifficulty ( Message_PreferredDifficultyP1Changed ),
m_PreferredCourseDifficulty ( Message_PreferredCourseDifficultyP1Changed ),
m_SortOrder ( Message_SortOrderChanged ),
m_pCurSong ( Message_CurrentSongChanged ),
m_pCurSteps ( Message_CurrentStepsP1Changed ),
m_pCurCourse ( Message_CurrentCourseChanged ),
m_pCurTrail ( Message_CurrentTrailP1Changed ),
m_bGameplayLeadIn ( Message_GameplayLeadInChanged ),
m_bDidModeChangeNoteSkin ( false ),
2011-06-11 09:36:17 -04:00
m_bIsUsingStepTiming ( true ),
m_bInStepEditor ( false ),
2011-03-17 01:47:30 -04:00
m_stEdit ( Message_EditStepsTypeChanged ),
m_cdEdit ( Message_EditCourseDifficultyChanged ),
m_pEditSourceSteps ( Message_EditSourceStepsChanged ),
m_stEditSource ( Message_EditSourceStepsTypeChanged ),
m_iEditCourseEntryIndex ( Message_EditCourseEntryIndexChanged ),
2011-06-11 09:36:17 -04:00
m_sEditLocalProfileID ( Message_EditLocalProfileIDChanged )
2011-03-17 01:47:30 -04:00
{
g_pImpl = new GameStateImpl ;
SetCurrentStyle ( NULL );
m_pCurGame . Set ( NULL );
2013-06-04 23:47:22 -04:00
m_iCoins . Set ( 0 );
2011-03-17 01:47:30 -04:00
m_timeGameStarted . SetZero ();
m_bDemonstrationOrJukebox = false ;
m_iNumTimesThroughAttract = - 1 ; // initial screen will bump this up to 0
m_iStageSeed = m_iGameSeed = 0 ;
m_PlayMode . Set ( PlayMode_Invalid ); // used by IsPlayerEnabled before the first screen
FOREACH_PlayerNumber ( p )
m_bSideIsJoined [ p ] = false ; // used by GetNumSidesJoined before the first screen
FOREACH_PlayerNumber ( p )
{
m_pPlayerState [ p ] = new PlayerState ;
m_pPlayerState [ p ] -> m_PlayerNumber = p ;
}
FOREACH_MultiPlayer ( p )
{
m_pMultiPlayerState [ p ] = new PlayerState ;
m_pMultiPlayerState [ p ] -> m_PlayerNumber = PLAYER_1 ;
m_pMultiPlayerState [ p ] -> m_mp = p ;
}
m_Environment = new LuaTable ;
m_bDopefish = false ;
2011-08-03 23:56:04 -05:00
sExpandedSectionName = "" ;
2011-03-17 01:47:30 -04:00
// Don't reset yet; let the first screen do it, so we can use PREFSMAN and THEME.
//Reset();
// Register with Lua.
{
Lua * L = LUA -> Get ();
lua_pushstring ( L , "GAMESTATE" );
this -> PushSelf ( L );
lua_settable ( L , LUA_GLOBALSINDEX );
LUA -> Release ( L );
}
}
GameState ::~ GameState ()
{
// Unregister with Lua.
LUA -> UnsetGlobal ( "GAMESTATE" );
FOREACH_PlayerNumber ( p )
SAFE_DELETE ( m_pPlayerState [ p ] );
FOREACH_MultiPlayer ( p )
SAFE_DELETE ( m_pMultiPlayerState [ p ] );
SAFE_DELETE ( m_Environment );
SAFE_DELETE ( g_pImpl );
2011-06-15 02:36:53 -04:00
SAFE_DELETE ( processedTiming );
2011-03-17 01:47:30 -04:00
}
2011-06-06 20:40:11 -04:00
PlayerNumber GameState :: GetMasterPlayerNumber () const
{
return this -> masterPlayerNumber ;
}
void GameState :: SetMasterPlayerNumber ( const PlayerNumber p )
{
this -> masterPlayerNumber = p ;
}
2011-06-08 14:43:59 -04:00
TimingData * GameState :: GetProcessedTimingData () const
{
return this -> processedTiming ;
}
2011-06-08 15:02:54 -04:00
void GameState :: SetProcessedTimingData ( TimingData * t )
2011-06-08 14:43:59 -04:00
{
2011-06-08 15:02:54 -04:00
this -> processedTiming = t ;
2011-06-08 14:43:59 -04:00
}
2011-03-17 01:47:30 -04:00
void GameState :: ApplyGameCommand ( const RString & sCommand , PlayerNumber pn )
{
GameCommand m ;
m . Load ( 0 , ParseCommands ( sCommand ) );
RString sWhy ;
if ( ! m . IsPlayable ( & sWhy ) )
RageException :: Throw ( "Can't apply mode \" %s \" : %s" , sCommand . c_str (), sWhy . c_str () );
if ( pn == PLAYER_INVALID )
m . ApplyToAllPlayers ();
else
m . Apply ( pn );
}
void GameState :: ApplyCmdline ()
{
// We need to join players before we can set the style.
RString sPlayer ;
for ( int i = 0 ; GetCommandlineArgument ( "player" , & sPlayer , i ); ++ i )
{
2011-05-11 15:58:31 -04:00
int pn = StringToInt ( sPlayer ) - 1 ;
2011-03-17 01:47:30 -04:00
if ( ! IsAnInt ( sPlayer ) || pn < 0 || pn >= NUM_PLAYERS )
RageException :: Throw ( "Invalid argument \" --player=%s \" ." , sPlayer . c_str () );
JoinPlayer ( ( PlayerNumber ) pn );
}
RString sMode ;
for ( int i = 0 ; GetCommandlineArgument ( "mode" , & sMode , i ); ++ i )
{
ApplyGameCommand ( sMode );
}
}
void GameState :: ResetPlayer ( PlayerNumber pn )
{
m_PreferredStepsType . Set ( StepsType_Invalid );
m_PreferredDifficulty [ pn ]. Set ( Difficulty_Invalid );
m_PreferredCourseDifficulty [ pn ]. Set ( Difficulty_Medium );
m_iPlayerStageTokens [ pn ] = 0 ;
m_iAwardedExtraStages [ pn ] = 0 ;
m_pCurSteps [ pn ]. Set ( NULL );
m_pCurTrail [ pn ]. Set ( NULL );
m_pPlayerState [ pn ] -> Reset ();
PROFILEMAN -> UnloadProfile ( pn );
2011-10-29 11:43:49 -04:00
ResetPlayerOptions ( pn );
}
2011-03-17 01:47:30 -04:00
2011-10-29 11:43:49 -04:00
void GameState :: ResetPlayerOptions ( PlayerNumber pn )
{
2011-03-17 01:47:30 -04:00
PlayerOptions po ;
GetDefaultPlayerOptions ( po );
m_pPlayerState [ pn ] -> m_PlayerOptions . Assign ( ModsLevel_Preferred , po );
}
void GameState :: Reset ()
{
2011-06-06 20:40:11 -04:00
this -> SetMasterPlayerNumber ( PLAYER_INVALID ); // must initialize for UnjoinPlayer
2011-03-17 01:47:30 -04:00
FOREACH_PlayerNumber ( pn )
UnjoinPlayer ( pn );
2012-12-27 11:38:53 -05:00
ASSERT ( THEME != NULL );
2011-03-17 01:47:30 -04:00
m_timeGameStarted . SetZero ();
SetCurrentStyle ( NULL );
FOREACH_MultiPlayer ( p )
m_MultiPlayerStatus [ p ] = MultiPlayerStatus_NotJoined ;
FOREACH_PlayerNumber ( pn )
MEMCARDMAN -> UnlockCard ( pn );
2013-06-04 23:47:22 -04:00
//m_iCoins = 0; // don't reset coin count!
2011-03-17 01:47:30 -04:00
m_bMultiplayer = false ;
m_iNumMultiplayerNoteFields = 1 ;
* m_Environment = LuaTable ();
m_sPreferredSongGroup . Set ( GROUP_ALL );
m_sPreferredCourseGroup . Set ( GROUP_ALL );
m_bChangedFailTypeOnScreenSongOptions = false ;
m_SortOrder . Set ( SortOrder_Invalid );
m_PreferredSortOrder = GetDefaultSort ();
m_PlayMode . Set ( PlayMode_Invalid );
m_EditMode = EditMode_Invalid ;
m_bDemonstrationOrJukebox = false ;
m_bJukeboxUsesModifiers = false ;
m_iCurrentStageIndex = 0 ;
m_bGameplayLeadIn . Set ( false );
m_iNumStagesOfThisSong = 0 ;
m_bLoadingNextSong = false ;
NOTESKIN -> RefreshNoteSkinData ( m_pCurGame );
m_iGameSeed = rand ();
m_iStageSeed = rand ();
m_pCurSong . Set ( GetDefaultSong () );
m_pPreferredSong = NULL ;
m_pCurCourse . Set ( NULL );
m_pPreferredCourse = NULL ;
FOREACH_MultiPlayer ( p )
m_pMultiPlayerState [ p ] -> Reset ();
m_SongOptions . Init ();
ResetMusicStatistics ();
ResetStageStatistics ();
AdjustSync :: ResetOriginalSyncData ();
SONGMAN -> UpdatePopular ();
SONGMAN -> UpdateShuffled ();
/* We may have cached trails from before everything was loaded (eg. from
* before SongManager::UpdatePopular could be called). Erase the cache. */
SONGMAN -> RegenerateNonFixedCourses ();
STATSMAN -> Reset ();
FOREACH_PlayerNumber ( p )
{
if ( PREFSMAN -> m_ShowDancingCharacters == SDC_Random )
m_pCurCharacters [ p ] = CHARMAN -> GetRandomCharacter ();
else
m_pCurCharacters [ p ] = CHARMAN -> GetDefaultCharacter ();
2012-12-27 11:38:53 -05:00
ASSERT ( m_pCurCharacters [ p ] != NULL );
2011-03-17 01:47:30 -04:00
}
m_bTemporaryEventMode = false ;
LIGHTSMAN -> SetLightsMode ( LIGHTSMODE_ATTRACT );
m_stEdit . Set ( StepsType_Invalid );
m_pEditSourceSteps . Set ( NULL );
m_stEditSource . Set ( StepsType_Invalid );
m_iEditCourseEntryIndex . Set ( - 1 );
m_sEditLocalProfileID . Set ( "" );
m_bBackedOutOfFinalStage = false ;
m_bEarnedExtraStage = false ;
2011-08-03 23:56:04 -05:00
sExpandedSectionName = "" ;
2011-03-17 01:47:30 -04:00
ApplyCmdline ();
}
void GameState :: JoinPlayer ( PlayerNumber pn )
{
2014-02-02 12:53:26 -06:00
/* If joint premium and we're not taking away a credit for the 2nd join,
* give the new player the same number of stage tokens that the old player
* has. */
if ( GetCoinMode () == CoinMode_Pay && GetPremium () == Premium_2PlayersFor1Credit && GetNumSidesJoined () == 1 )
m_iPlayerStageTokens [ pn ] = m_iPlayerStageTokens [ this -> GetMasterPlayerNumber ()];
else
m_iPlayerStageTokens [ pn ] = PREFSMAN -> m_iSongsPerPlay ;
2011-03-17 01:47:30 -04:00
m_bSideIsJoined [ pn ] = true ;
2011-06-06 20:40:11 -04:00
if ( this -> GetMasterPlayerNumber () == PLAYER_INVALID )
this -> SetMasterPlayerNumber ( pn );
2011-03-17 01:47:30 -04:00
// if first player to join, set start time
if ( GetNumSidesJoined () == 1 )
BeginGame ();
// Count each player join as a play.
{
Profile * pMachineProfile = PROFILEMAN -> GetMachineProfile ();
pMachineProfile -> m_iTotalSessions ++ ;
}
// Set the current style to something appropriate for the new number of joined players.
if ( ALLOW_LATE_JOIN && m_pCurStyle != NULL )
{
const Style * pStyle ;
2011-04-02 00:32:30 -05:00
// Only use one player for StyleType_OnePlayerTwoSides and StepsTypes
// that can only be played by one player (e.g. dance-solo,
// dance-threepanel, popn-nine). -aj
2011-03-17 01:47:30 -04:00
// XXX?: still shows joined player as "Insert Card". May not be an issue? -aj
2011-04-02 00:32:30 -05:00
if ( m_pCurStyle -> m_StyleType == StyleType_OnePlayerTwoSides ||
m_pCurStyle -> m_StepsType == StepsType_dance_solo ||
m_pCurStyle -> m_StepsType == StepsType_dance_threepanel ||
m_pCurStyle -> m_StepsType == StepsType_popn_nine )
2011-03-17 01:47:30 -04:00
pStyle = GAMEMAN -> GetFirstCompatibleStyle ( m_pCurGame , 1 , m_pCurStyle -> m_StepsType );
else
pStyle = GAMEMAN -> GetFirstCompatibleStyle ( m_pCurGame , GetNumSidesJoined (), m_pCurStyle -> m_StepsType );
// use SetCurrentStyle in case of StyleType_OnePlayerTwoSides
SetCurrentStyle ( pStyle );
}
Message msg ( MessageIDToString ( Message_PlayerJoined ) );
msg . SetParam ( "Player" , pn );
MESSAGEMAN -> Broadcast ( msg );
}
void GameState :: UnjoinPlayer ( PlayerNumber pn )
{
2014-03-10 21:54:53 -06:00
/* Unjoin STATSMAN first, so steps used by this player are released
* and can be released by PROFILEMAN. */
STATSMAN -> UnjoinPlayer ( pn );
2011-03-17 01:47:30 -04:00
m_bSideIsJoined [ pn ] = false ;
m_iPlayerStageTokens [ pn ] = 0 ;
ResetPlayer ( pn );
2011-06-06 20:40:11 -04:00
if ( this -> GetMasterPlayerNumber () == pn )
2014-03-10 21:54:53 -06:00
{
// We can't use GetFirstHumanPlayer() because if both players were joined, GetFirstHumanPlayer() will always return PLAYER_1, even when PLAYER_1 is the player we're unjoining.
FOREACH_HumanPlayer ( hp )
{
if ( pn != hp )
{
this -> SetMasterPlayerNumber ( hp );
}
}
if ( this -> GetMasterPlayerNumber () == pn )
{
this -> SetMasterPlayerNumber ( PLAYER_INVALID );
}
}
2011-03-17 01:47:30 -04:00
Message msg ( MessageIDToString ( Message_PlayerUnjoined ) );
msg . SetParam ( "Player" , pn );
MESSAGEMAN -> Broadcast ( msg );
// If there are no players left, reset some non-player-specific stuff, too.
2011-06-06 20:40:11 -04:00
if ( this -> GetMasterPlayerNumber () == PLAYER_INVALID )
2011-03-17 01:47:30 -04:00
{
SongOptions so ;
GetDefaultSongOptions ( so );
m_SongOptions . Assign ( ModsLevel_Preferred , so );
m_bDidModeChangeNoteSkin = false ;
}
}
/* xxx: handle multiplayer join? -aj */
namespace
{
bool JoinInputInternal ( PlayerNumber pn )
{
if ( ! GAMESTATE -> PlayersCanJoin () )
return false ;
// If this side is already in, don't re-join.
if ( GAMESTATE -> m_bSideIsJoined [ pn ] )
return false ;
2013-06-04 23:47:22 -04:00
// subtract coins
int iCoinsNeededToJoin = GAMESTATE -> GetCoinsNeededToJoin ();
if ( GAMESTATE -> m_iCoins < iCoinsNeededToJoin )
return false ; // not enough coins
GAMESTATE -> m_iCoins . Set ( GAMESTATE -> m_iCoins - iCoinsNeededToJoin );
2011-03-17 01:47:30 -04:00
GAMESTATE -> JoinPlayer ( pn );
return true ;
}
};
// Handle an input that can join a player. Return true if the player joined.
bool GameState :: JoinInput ( PlayerNumber pn )
{
// When AutoJoin is enabled, join all players on a single start press.
if ( GAMESTATE -> m_bAutoJoin . Get () )
return JoinPlayers ();
else
return JoinInputInternal ( pn );
}
// Attempt to join all players, as if each player pressed Start.
bool GameState :: JoinPlayers ()
{
bool bJoined = false ;
FOREACH_PlayerNumber ( pn )
{
if ( JoinInputInternal ( pn ) )
bJoined = true ;
}
return bJoined ;
}
2013-06-04 23:47:22 -04:00
int GameState :: GetCoinsNeededToJoin () const
{
2014-02-02 12:53:26 -06:00
int iCoinsToCharge = 0 ;
if ( GetCoinMode () == CoinMode_Pay )
iCoinsToCharge = PREFSMAN -> m_iCoinsPerCredit ;
// If joint premium, don't take away a credit for the second join.
if ( GetPremium () == Premium_2PlayersFor1Credit &&
GetNumSidesJoined () == 1 )
iCoinsToCharge = 0 ;
return iCoinsToCharge ;
2013-06-04 23:47:22 -04:00
}
2011-03-17 01:47:30 -04:00
/* Game flow:
*
* BeginGame() - the first player has joined; the game is starting.
*
* PlayersFinalized() - player memory cards are loaded; later joins won't have memory cards this stage
*
* BeginStage() - gameplay is beginning
*
* optional: CancelStage() - gameplay aborted (Back pressed), undo BeginStage and back up
*
* CommitStageStats() - gameplay is finished
* Saves STATSMAN->m_CurStageStats to the profiles, so profile information
* is up-to-date for Evaluation.
*
* FinishStage() - gameplay and evaluation is finished
* Clears data which was stored by CommitStageStats. */
void GameState :: BeginGame ()
{
m_timeGameStarted . Touch ();
m_vpsNamesThatWereFilled . clear ();
// Play attract on the ending screen, then on the ranking screen
// even if attract sounds are set to off.
m_iNumTimesThroughAttract = - 1 ;
FOREACH_PlayerNumber ( pn )
MEMCARDMAN -> UnlockCard ( pn );
}
void GameState :: LoadProfiles ( bool bLoadEdits )
{
// Unlock any cards that we might want to load.
FOREACH_HumanPlayer ( pn )
if ( ! PROFILEMAN -> IsPersistentProfile ( pn ) )
MEMCARDMAN -> UnlockCard ( pn );
MEMCARDMAN -> WaitForCheckingToComplete ();
FOREACH_HumanPlayer ( pn )
{
// If a profile is already loaded, this was already called.
if ( PROFILEMAN -> IsPersistentProfile ( pn ) )
continue ;
MEMCARDMAN -> MountCard ( pn );
bool bSuccess = PROFILEMAN -> LoadFirstAvailableProfile ( pn , bLoadEdits ); // load full profile
MEMCARDMAN -> UnmountCard ( pn );
if ( ! bSuccess )
continue ;
// Lock the card on successful load, so we won't allow it to be changed.
MEMCARDMAN -> LockCard ( pn );
LoadCurrentSettingsFromProfile ( pn );
Profile * pPlayerProfile = PROFILEMAN -> GetProfile ( pn );
if ( pPlayerProfile )
pPlayerProfile -> m_iTotalSessions ++ ;
}
}
void GameState :: SavePlayerProfiles ()
{
FOREACH_HumanPlayer ( pn )
SavePlayerProfile ( pn );
}
void GameState :: SavePlayerProfile ( PlayerNumber pn )
{
if ( ! PROFILEMAN -> IsPersistentProfile ( pn ) )
return ;
// AutoplayCPU should not save scores. -aj
// xxx: this MAY cause issues with Multiplayer. However, without a working
// Multiplayer build, we'll never know. -aj
if ( m_pPlayerState [ pn ] -> m_PlayerController != PC_HUMAN )
return ;
bool bWasMemoryCard = PROFILEMAN -> ProfileWasLoadedFromMemoryCard ( pn );
if ( bWasMemoryCard )
MEMCARDMAN -> MountCard ( pn );
PROFILEMAN -> SaveProfile ( pn );
if ( bWasMemoryCard )
MEMCARDMAN -> UnmountCard ( pn );
}
bool GameState :: HaveProfileToLoad ()
{
FOREACH_HumanPlayer ( pn )
{
// We won't load this profile if it's already loaded.
if ( PROFILEMAN -> IsPersistentProfile ( pn ) )
continue ;
// If a memory card is inserted, we'l try to load it.
if ( MEMCARDMAN -> CardInserted ( pn ) )
return true ;
if ( ! PROFILEMAN -> m_sDefaultLocalProfileID [ pn ]. Get (). empty () )
return true ;
}
return false ;
}
bool GameState :: HaveProfileToSave ()
{
FOREACH_HumanPlayer ( pn )
if ( PROFILEMAN -> IsPersistentProfile ( pn ) )
return true ;
return false ;
}
void GameState :: SaveLocalData ()
{
2013-06-04 23:47:22 -04:00
BOOKKEEPER -> WriteToDisk ();
2011-03-17 01:47:30 -04:00
PROFILEMAN -> SaveMachineProfile ();
}
int GameState :: GetNumStagesMultiplierForSong ( const Song * pSong )
{
int iNumStages = 1 ;
2012-12-27 11:38:53 -05:00
ASSERT ( pSong != NULL );
2011-03-17 01:47:30 -04:00
if ( pSong -> IsMarathon () )
iNumStages *= 3 ;
if ( pSong -> IsLong () )
iNumStages *= 2 ;
return iNumStages ;
}
int GameState :: GetNumStagesForCurrentSongAndStepsOrCourse () const
{
int iNumStagesOfThisSong = 1 ;
if ( m_pCurSong )
{
const Style * pStyle = m_pCurStyle ;
int numSidesJoined = GetNumSidesJoined ();
if ( pStyle == NULL )
{
const Steps * pSteps = NULL ;
2011-06-06 20:40:11 -04:00
if ( this -> GetMasterPlayerNumber () != PlayerNumber_Invalid )
pSteps = m_pCurSteps [ this -> GetMasterPlayerNumber ()];
2011-03-17 01:47:30 -04:00
// Don't call GetFirstCompatibleStyle if numSidesJoined == 0.
// This happens because on SContinue when players are unjoined,
// pCurSteps will still be set while no players are joined. -Chris
if ( pSteps && numSidesJoined > 0 )
{
// If a style isn't set, use the style of the selected steps.
StepsType st = pSteps -> m_StepsType ;
pStyle = GAMEMAN -> GetFirstCompatibleStyle ( m_pCurGame , numSidesJoined , st );
}
else
{
/* If steps aren't set either, pick any style for the number of
* joined players, or one player if no players are joined. */
vector < const Style *> vpStyles ;
int iJoined = max ( numSidesJoined , 1 );
GAMEMAN -> GetCompatibleStyles ( m_pCurGame , iJoined , vpStyles );
ASSERT ( ! vpStyles . empty () );
pStyle = vpStyles [ 0 ];
}
}
/* Extra stages need to only count as one stage in case a multi-stage
* song is chosen. */
if ( IsAnExtraStage () )
iNumStagesOfThisSong = 1 ;
else
2011-08-07 18:33:35 -05:00
iNumStagesOfThisSong = GameState :: GetNumStagesMultiplierForSong ( m_pCurSong );
2011-03-17 01:47:30 -04:00
}
else if ( m_pCurCourse )
iNumStagesOfThisSong = PREFSMAN -> m_iSongsPerPlay ;
else
return - 1 ;
iNumStagesOfThisSong = max ( iNumStagesOfThisSong , 1 );
return iNumStagesOfThisSong ;
}
// Called by ScreenGameplay. Set the length of the current song.
void GameState :: BeginStage ()
{
if ( m_bDemonstrationOrJukebox )
return ;
// This should only be called once per stage.
if ( m_iNumStagesOfThisSong != 0 )
LOG -> Warn ( "XXX: m_iNumStagesOfThisSong == %i?" , m_iNumStagesOfThisSong );
ResetStageStatistics ();
AdjustSync :: ResetOriginalSyncData ();
if ( ! ARE_STAGE_PLAYER_MODS_FORCED )
{
2011-07-06 22:22:45 -04:00
FOREACH_PlayerNumber ( p )
2011-07-06 21:34:01 -04:00
{
ModsGroup < PlayerOptions > & po = m_pPlayerState [ p ] -> m_PlayerOptions ;
po . Assign ( ModsLevel_Stage ,
m_pPlayerState [ p ] -> m_PlayerOptions . GetPreferred ());
}
2011-03-17 01:47:30 -04:00
}
if ( ! ARE_STAGE_SONG_MODS_FORCED )
m_SongOptions . Assign ( ModsLevel_Stage , m_SongOptions . GetPreferred () );
STATSMAN -> m_CurStageStats . m_fMusicRate = m_SongOptions . GetSong (). m_fMusicRate ;
m_iNumStagesOfThisSong = GetNumStagesForCurrentSongAndStepsOrCourse ();
ASSERT ( m_iNumStagesOfThisSong != - 1 );
FOREACH_EnabledPlayer ( p )
{
// only do this check with human players, assume CPU players (Rave)
// always have tokens. -aj (this could probably be moved below, even.)
if ( ! IsEventMode () && ! IsCpuPlayer ( p ) )
ASSERT ( m_iPlayerStageTokens [ p ] >= m_iNumStagesOfThisSong );
m_iPlayerStageTokens [ p ] -= m_iNumStagesOfThisSong ;
}
FOREACH_HumanPlayer ( pn )
if ( CurrentOptionsDisqualifyPlayer ( pn ) )
STATSMAN -> m_CurStageStats . m_player [ pn ]. m_bDisqualified = true ;
m_bEarnedExtraStage = false ;
m_sStageGUID = CryptManager :: GenerateRandomUUID ();
}
void GameState :: CancelStage ()
{
FOREACH_CpuPlayer ( p )
{
switch ( m_PlayMode )
{
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
2011-07-20 11:11:04 -04:00
m_iPlayerStageTokens [ p ] = PREFSMAN -> m_iSongsPerPlay ;
default :
break ;
2011-03-17 01:47:30 -04:00
}
}
FOREACH_EnabledPlayer ( p )
m_iPlayerStageTokens [ p ] += m_iNumStagesOfThisSong ;
m_iNumStagesOfThisSong = 0 ;
ResetStageStatistics ();
}
void GameState :: CommitStageStats ()
{
if ( m_bDemonstrationOrJukebox )
return ;
STATSMAN -> CommitStatsToProfiles ( & STATSMAN -> m_CurStageStats );
// Update TotalPlaySeconds.
int iPlaySeconds = max ( 0 , ( int ) m_timeGameStarted . GetDeltaTime () );
Profile * pMachineProfile = PROFILEMAN -> GetMachineProfile ();
pMachineProfile -> m_iTotalSessionSeconds += iPlaySeconds ;
FOREACH_HumanPlayer ( p )
{
Profile * pPlayerProfile = PROFILEMAN -> GetProfile ( p );
if ( pPlayerProfile )
pPlayerProfile -> m_iTotalSessionSeconds += iPlaySeconds ;
}
}
/* Called by ScreenSelectMusic (etc). Increment the stage counter if we just
* played a song. Might be called more than once. */
void GameState :: FinishStage ()
{
// Increment the stage counter.
const int iOldStageIndex = m_iCurrentStageIndex ;
++ m_iCurrentStageIndex ;
m_iNumStagesOfThisSong = 0 ;
EarnedExtraStage e = CalculateEarnedExtraStage ();
STATSMAN -> m_CurStageStats . m_EarnedExtraStage = e ;
if ( e != EarnedExtraStage_No )
{
LOG -> Trace ( "awarded extra stage" );
FOREACH_HumanPlayer ( p )
{
// todo: unhardcode the extra stage limit? -aj
if ( m_iAwardedExtraStages [ p ] < 2 )
{
++ m_iAwardedExtraStages [ p ];
++ m_iPlayerStageTokens [ p ];
m_bEarnedExtraStage = true ;
}
}
}
2014-02-16 11:43:28 -07:00
// Save the current combo to the profiles so it can be used for ComboContinuesBetweenSongs.
FOREACH_HumanPlayer ( p )
{
Profile * pProfile = PROFILEMAN -> GetProfile ( p );
pProfile -> m_iCurrentCombo = STATSMAN -> m_CurStageStats . m_player [ p ]. m_iCurCombo ;
}
2011-03-17 01:47:30 -04:00
if ( m_bDemonstrationOrJukebox )
return ;
// todo: simplify. profile saving is accomplished in ScreenProfileSave
// now; all this code does differently is save machine profile as well. -aj
if ( IsEventMode () )
{
const int iSaveProfileEvery = 3 ;
if ( iOldStageIndex / iSaveProfileEvery < m_iCurrentStageIndex / iSaveProfileEvery )
{
LOG -> Trace ( "Played %i stages; saving profiles ..." , iSaveProfileEvery );
PROFILEMAN -> SaveMachineProfile ();
this -> SavePlayerProfiles ();
}
}
}
void GameState :: LoadCurrentSettingsFromProfile ( PlayerNumber pn )
{
if ( ! PROFILEMAN -> IsPersistentProfile ( pn ) )
return ;
const Profile * pProfile = PROFILEMAN -> GetProfile ( pn );
// apply saved default modifiers if any
RString sModifiers ;
if ( pProfile -> GetDefaultModifiers ( m_pCurGame , sModifiers ) )
{
/* We don't save negative preferences (eg. "no reverse"). If the theme
* sets a default of "reverse", and the player turns it off, we should
* set it off. However, don't reset modifiers that aren't saved by the
* profile, so we don't ignore unsaved modifiers when a profile is in use. */
PO_GROUP_CALL ( m_pPlayerState [ pn ] -> m_PlayerOptions , ModsLevel_Preferred , ResetSavedPrefs );
ApplyPreferredModifiers ( pn , sModifiers );
}
// Only set the sort order if it wasn't already set by a GameCommand (or by an earlier profile)
if ( m_PreferredSortOrder == SortOrder_Invalid && pProfile -> m_SortOrder != SortOrder_Invalid )
m_PreferredSortOrder = pProfile -> m_SortOrder ;
if ( pProfile -> m_LastDifficulty != Difficulty_Invalid )
m_PreferredDifficulty [ pn ]. Set ( pProfile -> m_LastDifficulty );
if ( pProfile -> m_LastCourseDifficulty != Difficulty_Invalid )
m_PreferredCourseDifficulty [ pn ]. Set ( pProfile -> m_LastCourseDifficulty );
// Only set the PreferredStepsType if it wasn't already set by a GameCommand (or by an earlier profile)
if ( m_PreferredStepsType == StepsType_Invalid && pProfile -> m_LastStepsType != StepsType_Invalid )
m_PreferredStepsType . Set ( pProfile -> m_LastStepsType );
if ( m_pPreferredSong == NULL )
m_pPreferredSong = pProfile -> m_lastSong . ToSong ();
if ( m_pPreferredCourse == NULL )
m_pPreferredCourse = pProfile -> m_lastCourse . ToCourse ();
}
void GameState :: SaveCurrentSettingsToProfile ( PlayerNumber pn )
{
if ( ! PROFILEMAN -> IsPersistentProfile ( pn ) )
return ;
if ( m_bDemonstrationOrJukebox )
return ;
Profile * pProfile = PROFILEMAN -> GetProfile ( pn );
pProfile -> SetDefaultModifiers ( m_pCurGame , m_pPlayerState [ pn ] -> m_PlayerOptions . GetPreferred (). GetSavedPrefsString () );
if ( IsSongSort ( m_PreferredSortOrder ) )
pProfile -> m_SortOrder = m_PreferredSortOrder ;
if ( m_PreferredDifficulty [ pn ] != Difficulty_Invalid )
pProfile -> m_LastDifficulty = m_PreferredDifficulty [ pn ];
if ( m_PreferredCourseDifficulty [ pn ] != Difficulty_Invalid )
pProfile -> m_LastCourseDifficulty = m_PreferredCourseDifficulty [ pn ];
if ( m_PreferredStepsType != StepsType_Invalid )
pProfile -> m_LastStepsType = m_PreferredStepsType ;
if ( m_pPreferredSong )
pProfile -> m_lastSong . FromSong ( m_pPreferredSong );
if ( m_pPreferredCourse )
pProfile -> m_lastCourse . FromCourse ( m_pPreferredCourse );
}
void GameState :: Update ( float fDelta )
{
m_SongOptions . Update ( fDelta );
FOREACH_PlayerNumber ( p )
{
m_pPlayerState [ p ] -> Update ( fDelta );
if ( ! m_bGoalComplete [ p ] && IsGoalComplete ( p ) )
{
m_bGoalComplete [ p ] = true ;
MESSAGEMAN -> Broadcast ( ( MessageID )( Message_GoalCompleteP1 + p ) );
}
}
if ( GAMESTATE -> m_pCurCourse )
{
if ( GAMESTATE -> m_pCurCourse -> m_fGoalSeconds > 0 && ! m_bWorkoutGoalComplete )
{
const StageStats & ssCurrent = STATSMAN -> m_CurStageStats ;
bool bGoalComplete = ssCurrent . m_fGameplaySeconds > GAMESTATE -> m_pCurCourse -> m_fGoalSeconds ;
if ( bGoalComplete )
{
MESSAGEMAN -> Broadcast ( "WorkoutGoalComplete" );
m_bWorkoutGoalComplete = true ;
}
}
}
}
void GameState :: SetCurGame ( const Game * pGame )
{
m_pCurGame . Set ( pGame );
RString sGame = pGame ? RString ( pGame -> m_szName ) : RString ();
PREFSMAN -> SetCurrentGame ( sGame );
}
const float GameState :: MUSIC_SECONDS_INVALID = - 5000.0f ;
void GameState :: ResetMusicStatistics ()
{
2011-05-10 21:22:01 +07:00
m_Position . Reset ();
2012-12-17 16:43:31 -08:00
m_LastPositionTimer . Touch ();
m_LastPositionSeconds = 0.0f ;
2011-05-10 21:22:01 +07:00
2011-03-17 01:47:30 -04:00
Actor :: SetBGMTime ( 0 , 0 , 0 , 0 );
FOREACH_PlayerNumber ( p )
2011-05-10 21:22:01 +07:00
{
2011-03-17 01:47:30 -04:00
m_pPlayerState [ p ] -> ClearHopoState ();
2011-05-10 21:22:01 +07:00
m_pPlayerState [ p ] -> m_Position . Reset ();
}
2011-03-17 01:47:30 -04:00
}
void GameState :: ResetStageStatistics ()
{
2014-02-16 11:43:28 -07:00
StageStats OldStats = STATSMAN -> m_CurStageStats ;
2011-03-17 01:47:30 -04:00
STATSMAN -> m_CurStageStats = StageStats ();
2014-02-16 11:43:28 -07:00
if ( PREFSMAN -> m_bComboContinuesBetweenSongs )
{
FOREACH_PlayerNumber ( p )
{
bool FirstSong = m_iCurrentStageIndex == 0 ;
if ( FirstSong )
{
Profile * pProfile = PROFILEMAN -> GetProfile ( p );
STATSMAN -> m_CurStageStats . m_player [ p ]. m_iCurCombo = pProfile -> m_iCurrentCombo ;
}
else
{
STATSMAN -> m_CurStageStats . m_player [ p ]. m_iCurCombo = OldStats . m_player [ p ]. m_iCurCombo ;
}
}
}
2011-03-17 01:47:30 -04:00
RemoveAllActiveAttacks ();
FOREACH_PlayerNumber ( p )
m_pPlayerState [ p ] -> RemoveAllInventory ();
m_fOpponentHealthPercent = 1 ;
m_fHasteRate = 0 ;
m_fLastHasteUpdateMusicSeconds = 0 ;
m_fAccumulatedHasteSeconds = 0 ;
m_fTugLifePercentP1 = 0.5f ;
FOREACH_PlayerNumber ( p )
{
m_pPlayerState [ p ] -> m_fSuperMeter = 0 ;
m_pPlayerState [ p ] -> m_HealthState = HealthState_Alive ;
m_pPlayerState [ p ] -> m_iLastPositiveSumOfAttackLevels = 0 ;
m_pPlayerState [ p ] -> m_fSecondsUntilAttacksPhasedOut = 0 ; // PlayerAI not affected
m_bGoalComplete [ p ] = false ;
}
m_bWorkoutGoalComplete = false ;
FOREACH_PlayerNumber ( p )
{
m_vLastStageAwards [ p ]. clear ();
m_vLastPeakComboAwards [ p ]. clear ();
}
// Reset the round seed. Do this here and not in FinishStage so that players
// get new shuffle patterns if they Back out of gameplay and play again.
m_iStageSeed = rand ();
}
2011-05-12 16:44:24 +07:00
void GameState :: UpdateSongPosition ( float fPositionSeconds , const TimingData & timing , const RageTimer & timestamp , bool bUpdatePlayers )
2011-03-17 01:47:30 -04:00
{
2012-12-17 16:43:31 -08:00
/* It's not uncommon to get a lot of duplicated positions from the sound
* driver, like so: 13.120953,13.130975,13.130975,13.130975,13.140998,...
* This causes visual stuttering of the arrows. To compensate, keep a
* RageTimer since the last change. */
if ( fPositionSeconds == m_LastPositionSeconds )
fPositionSeconds += m_LastPositionTimer . Ago ();
else
{
//LOG->Info("Time difference: %+f",
// m_LastPositionTimer.Ago() - (fPositionSeconds - m_LastPositionSeconds)
//);
m_LastPositionTimer . Touch ();
m_LastPositionSeconds = fPositionSeconds ;
}
2011-05-10 19:54:05 +07:00
m_Position . UpdateSongPosition ( fPositionSeconds , timing , timestamp );
2011-03-17 01:47:30 -04:00
2011-05-12 16:44:24 +07:00
if ( bUpdatePlayers )
2011-05-14 18:17:48 -04:00
{
2011-05-12 16:44:24 +07:00
FOREACH_EnabledPlayer ( pn )
2011-05-14 18:17:48 -04:00
{
2011-05-12 16:44:24 +07:00
if ( m_pCurSteps [ pn ] )
2011-05-14 17:03:49 -05:00
{
2013-01-23 14:51:18 -05:00
m_pPlayerState [ pn ] -> m_Position . UpdateSongPosition ( fPositionSeconds , * m_pCurSteps [ pn ] -> GetTimingData (), timestamp );
2011-05-15 00:57:35 -04:00
Actor :: SetPlayerBGMBeat ( pn , m_pPlayerState [ pn ] -> m_Position . m_fSongBeatVisible , m_pPlayerState [ pn ] -> m_Position . m_fSongBeatNoOffset );
2011-05-14 18:17:48 -04:00
}
}
}
2011-05-11 20:53:40 +07:00
Actor :: SetBGMTime ( GAMESTATE -> m_Position . m_fMusicSecondsVisible , GAMESTATE -> m_Position . m_fSongBeatVisible , fPositionSeconds , GAMESTATE -> m_Position . m_fSongBeatNoOffset );
2011-03-17 01:47:30 -04:00
// LOG->Trace( "m_fMusicSeconds = %f, m_fSongBeat = %f, m_fCurBPS = %f, m_bFreeze = %f", m_fMusicSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze );
}
/*
update player position code goes here
*/
float GameState :: GetSongPercent ( float beat ) const
{
// 0 = first step; 1 = last step
2011-06-30 01:11:38 -04:00
float curTime = this -> m_pCurSong -> m_SongTiming . GetElapsedTimeFromBeat ( beat );
2011-06-30 02:10:58 -04:00
return ( curTime - m_pCurSong -> GetFirstSecond ()) / m_pCurSong -> GetLastSecond ();
2011-03-17 01:47:30 -04:00
}
int GameState :: GetNumStagesLeft ( PlayerNumber pn ) const
{
return m_iPlayerStageTokens [ pn ];
}
int GameState :: GetSmallestNumStagesLeftForAnyHumanPlayer () const
{
if ( IsEventMode () )
return 999 ;
int iSmallest = INT_MAX ;
FOREACH_HumanPlayer ( p )
iSmallest = min ( iSmallest , m_iPlayerStageTokens [ p ] );
return iSmallest ;
}
bool GameState :: IsFinalStageForAnyHumanPlayer () const
{
return GetSmallestNumStagesLeftForAnyHumanPlayer () == 1 ;
}
bool GameState :: IsAnExtraStage () const
{
2011-06-06 20:40:11 -04:00
if ( this -> GetMasterPlayerNumber () == PlayerNumber_Invalid )
2011-03-17 01:47:30 -04:00
return false ;
2011-06-06 20:40:11 -04:00
return ! IsEventMode () && ! IsCourseMode () && m_iAwardedExtraStages [ this -> GetMasterPlayerNumber ()] > 0 ;
2011-03-17 01:47:30 -04:00
}
static ThemeMetric < bool > LOCK_EXTRA_STAGE_SELECTION ( "GameState" , "LockExtraStageSelection" );
bool GameState :: IsAnExtraStageAndSelectionLocked () const
{
return IsAnExtraStage () && LOCK_EXTRA_STAGE_SELECTION ;
}
bool GameState :: IsExtraStage () const
{
2011-06-06 20:40:11 -04:00
if ( this -> GetMasterPlayerNumber () == PlayerNumber_Invalid )
2011-03-17 01:47:30 -04:00
return false ;
2011-06-06 20:40:11 -04:00
return ! IsEventMode () && ! IsCourseMode () && m_iAwardedExtraStages [ this -> GetMasterPlayerNumber ()] == 1 ;
2011-03-17 01:47:30 -04:00
}
bool GameState :: IsExtraStage2 () const
{
2011-06-06 20:40:11 -04:00
if ( this -> GetMasterPlayerNumber () == PlayerNumber_Invalid )
2011-03-17 01:47:30 -04:00
return false ;
2011-06-06 20:40:11 -04:00
return ! IsEventMode () && ! IsCourseMode () && m_iAwardedExtraStages [ this -> GetMasterPlayerNumber ()] == 2 ;
2011-03-17 01:47:30 -04:00
}
Stage GameState :: GetCurrentStage () const
{
if ( m_bDemonstrationOrJukebox ) return Stage_Demo ;
// "event" has precedence
else if ( IsEventMode () ) return Stage_Event ;
else if ( m_PlayMode == PLAY_MODE_ONI ) return Stage_Oni ;
else if ( m_PlayMode == PLAY_MODE_NONSTOP ) return Stage_Nonstop ;
else if ( m_PlayMode == PLAY_MODE_ENDLESS ) return Stage_Endless ;
else if ( IsExtraStage () ) return Stage_Extra1 ;
else if ( IsExtraStage2 () ) return Stage_Extra2 ;
//else if( IsFinalStageForAnyHumanPlayer() ) return Stage_Final;
// above function behaves weirdly, it will always return final stage if any player is
// on final stage, rather than the last remaining player. The below method seems to make a bit more sense.
else if ( m_iPlayerStageTokens [ PLAYER_1 ] == 0 && m_iPlayerStageTokens [ PLAYER_2 ] == 0 ) return Stage_Final ;
else
{
switch ( this -> m_iCurrentStageIndex )
{
case 0 : return Stage_1st ;
case 1 : return Stage_2nd ;
case 2 : return Stage_3rd ;
case 3 : return Stage_4th ;
case 4 : return Stage_5th ;
case 5 : return Stage_6th ;
default : return Stage_Next ;
}
}
}
int GameState :: GetCourseSongIndex () const
{
// iSongsPlayed includes the current song, so it's 1-based; subtract one.
if ( GAMESTATE -> m_bMultiplayer )
{
FOREACH_EnabledMultiPlayer ( mp )
return STATSMAN -> m_CurStageStats . m_multiPlayer [ mp ]. m_iSongsPlayed - 1 ;
FAIL_M ( "At least one MultiPlayer must be joined." );
}
else
{
2011-06-06 20:40:11 -04:00
return STATSMAN -> m_CurStageStats . m_player [ this -> GetMasterPlayerNumber ()]. m_iSongsPlayed - 1 ;
2011-03-17 01:47:30 -04:00
}
}
/* Hack: when we're loading a new course song, we want to display the new song
* number, even though we haven't started that song yet. */
int GameState :: GetLoadingCourseSongIndex () const
{
int iIndex = GetCourseSongIndex ();
if ( m_bLoadingNextSong )
++ iIndex ;
return iIndex ;
}
static LocalizedString PLAYER1 ( "GameState" , "Player 1" );
static LocalizedString PLAYER2 ( "GameState" , "Player 2" );
static LocalizedString CPU ( "GameState" , "CPU" );
RString GameState :: GetPlayerDisplayName ( PlayerNumber pn ) const
{
ASSERT ( IsPlayerEnabled ( pn ) );
const LocalizedString * pDefaultNames [] = { & PLAYER1 , & PLAYER2 };
if ( IsHumanPlayer ( pn ) )
{
if ( ! PROFILEMAN -> GetPlayerName ( pn ). empty () )
return PROFILEMAN -> GetPlayerName ( pn );
else
return pDefaultNames [ pn ] -> GetValue ();
}
else
{
return CPU . GetValue ();
}
}
bool GameState :: PlayersCanJoin () const
{
bool b = GetNumSidesJoined () == 0 || GetCurrentStyle () == NULL ; // selecting a style finalizes the players
if ( ALLOW_LATE_JOIN . IsLoaded () && ALLOW_LATE_JOIN )
{
Screen * pScreen = SCREENMAN -> GetTopScreen ();
if ( pScreen )
b |= pScreen -> AllowLateJoin ();
}
return b ;
}
int GameState :: GetNumSidesJoined () const
{
int iNumSidesJoined = 0 ;
FOREACH_PlayerNumber ( p )
if ( m_bSideIsJoined [ p ] )
iNumSidesJoined ++ ; // left side, and right side
return iNumSidesJoined ;
}
const Game * GameState :: GetCurrentGame ()
{
ASSERT ( m_pCurGame != NULL ); // the game must be set before calling this
return m_pCurGame ;
}
const Style * GameState :: GetCurrentStyle () const
{
return m_pCurStyle ;
}
void GameState :: SetCurrentStyle ( const Style * pStyle )
{
m_pCurStyle . Set ( pStyle );
if ( INPUTMAPPER )
{
if ( GetCurrentStyle () && GetCurrentStyle () -> m_StyleType == StyleType_OnePlayerTwoSides )
2011-06-06 20:40:11 -04:00
INPUTMAPPER -> SetJoinControllers ( this -> GetMasterPlayerNumber () );
2011-03-17 01:47:30 -04:00
else
INPUTMAPPER -> SetJoinControllers ( PLAYER_INVALID );
}
}
bool GameState :: IsPlayerEnabled ( PlayerNumber pn ) const
{
// In rave, all players are present. Non-human players are CPU controlled.
switch ( m_PlayMode )
{
2011-07-20 11:11:04 -04:00
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
return true ;
default :
return IsHumanPlayer ( pn );
2011-03-17 01:47:30 -04:00
}
}
bool GameState :: IsMultiPlayerEnabled ( MultiPlayer mp ) const
{
return m_MultiPlayerStatus [ mp ] == MultiPlayerStatus_Joined ;
}
bool GameState :: IsPlayerEnabled ( const PlayerState * pPlayerState ) const
{
if ( pPlayerState -> m_mp != MultiPlayer_Invalid )
return IsMultiPlayerEnabled ( pPlayerState -> m_mp );
if ( pPlayerState -> m_PlayerNumber != PLAYER_INVALID )
return IsPlayerEnabled ( pPlayerState -> m_PlayerNumber );
return false ;
}
int GameState :: GetNumPlayersEnabled () const
{
int count = 0 ;
FOREACH_EnabledPlayer ( pn )
count ++ ;
return count ;
}
bool GameState :: IsHumanPlayer ( PlayerNumber pn ) const
{
if ( pn == PLAYER_INVALID )
return false ;
if ( GetCurrentStyle () == NULL ) // no style chosen
{
if ( PlayersCanJoin () )
return m_bSideIsJoined [ pn ]; // only allow input from sides that have already joined
else
return true ; // if we can't join, then we're on a screen like MusicScroll or GameOver
}
2012-12-27 16:59:35 -05:00
StyleType type = GetCurrentStyle () -> m_StyleType ;
switch ( type )
2011-03-17 01:47:30 -04:00
{
case StyleType_TwoPlayersTwoSides :
case StyleType_TwoPlayersSharedSides :
return true ;
case StyleType_OnePlayerOneSide :
case StyleType_OnePlayerTwoSides :
2011-06-06 20:40:11 -04:00
return pn == this -> GetMasterPlayerNumber ();
2011-03-17 01:47:30 -04:00
default :
2012-12-27 16:59:35 -05:00
FAIL_M ( ssprintf ( "Invalid style type: %i" , type ));
2011-03-17 01:47:30 -04:00
}
}
int GameState :: GetNumHumanPlayers () const
{
int count = 0 ;
FOREACH_HumanPlayer ( pn )
count ++ ;
return count ;
}
PlayerNumber GameState :: GetFirstHumanPlayer () const
{
FOREACH_HumanPlayer ( pn )
return pn ;
return PLAYER_INVALID ;
}
PlayerNumber GameState :: GetFirstDisabledPlayer () const
{
FOREACH_PlayerNumber ( pn )
if ( ! IsPlayerEnabled ( pn ) )
return pn ;
return PLAYER_INVALID ;
}
bool GameState :: IsCpuPlayer ( PlayerNumber pn ) const
{
return IsPlayerEnabled ( pn ) && ! IsHumanPlayer ( pn );
}
bool GameState :: AnyPlayersAreCpu () const
{
FOREACH_CpuPlayer ( pn )
return true ;
return false ;
}
bool GameState :: IsCourseMode () const
{
switch ( m_PlayMode )
{
case PLAY_MODE_ONI :
case PLAY_MODE_NONSTOP :
case PLAY_MODE_ENDLESS :
return true ;
default :
return false ;
}
}
bool GameState :: IsBattleMode () const
{
switch ( m_PlayMode )
{
case PLAY_MODE_BATTLE :
return true ;
default :
return false ;
}
}
EarnedExtraStage GameState :: CalculateEarnedExtraStage () const
{
if ( IsEventMode () )
return EarnedExtraStage_No ;
if ( ! PREFSMAN -> m_bAllowExtraStage )
return EarnedExtraStage_No ;
if ( m_PlayMode != PLAY_MODE_REGULAR )
return EarnedExtraStage_No ;
if ( m_bBackedOutOfFinalStage )
return EarnedExtraStage_No ;
if ( GetSmallestNumStagesLeftForAnyHumanPlayer () > 0 )
return EarnedExtraStage_No ;
2011-06-06 20:40:11 -04:00
if ( m_iAwardedExtraStages [ this -> GetMasterPlayerNumber ()] >= 2 )
2011-03-17 01:47:30 -04:00
return EarnedExtraStage_No ;
FOREACH_EnabledPlayer ( pn )
{
Difficulty dc = m_pCurSteps [ pn ] -> GetDifficulty ();
switch ( dc )
{
case Difficulty_Edit :
2011-09-29 22:09:52 -04:00
if ( ! EDIT_ALLOWED_FOR_EXTRA )
continue ; // can't use edit steps
2011-03-17 01:47:30 -04:00
break ;
default :
if ( dc < MIN_DIFFICULTY_FOR_EXTRA )
continue ; // not hard enough!
break ;
}
if ( IsExtraStage () )
{
if ( ALLOW_EXTRA_2 && STATSMAN -> m_CurStageStats . m_player [ pn ]. GetGrade () <= GRADE_TIER_FOR_EXTRA_2 )
return EarnedExtraStage_Extra2 ;
}
else if ( STATSMAN -> m_CurStageStats . m_player [ pn ]. GetGrade () <= GRADE_TIER_FOR_EXTRA_1 )
{
return EarnedExtraStage_Extra1 ;
}
}
return EarnedExtraStage_No ;
}
PlayerNumber GameState :: GetBestPlayer () const
{
FOREACH_PlayerNumber ( pn )
if ( GetStageResult ( pn ) == RESULT_WIN )
return pn ;
return PLAYER_INVALID ; // draw
}
StageResult GameState :: GetStageResult ( PlayerNumber pn ) const
{
switch ( m_PlayMode )
{
2011-07-20 11:11:04 -04:00
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
if ( fabsf ( m_fTugLifePercentP1 - 0.5f ) < 0.0001f )
return RESULT_DRAW ;
switch ( pn )
{
case PLAYER_1 : return ( m_fTugLifePercentP1 >= 0.5f ) ? RESULT_WIN : RESULT_LOSE ;
case PLAYER_2 : return ( m_fTugLifePercentP1 < 0.5f ) ? RESULT_WIN : RESULT_LOSE ;
default : FAIL_M ( "Invalid player for battle! Aborting..." ); return RESULT_LOSE ;
}
default : break ;
2011-03-17 01:47:30 -04:00
}
StageResult win = RESULT_WIN ;
FOREACH_PlayerNumber ( p )
{
if ( p == pn )
continue ;
// If anyone did just as well, at best it's a draw.
if ( STATSMAN -> m_CurStageStats . m_player [ p ]. m_iActualDancePoints == STATSMAN -> m_CurStageStats . m_player [ pn ]. m_iActualDancePoints )
win = RESULT_DRAW ;
// If anyone did better, we lost.
if ( STATSMAN -> m_CurStageStats . m_player [ p ]. m_iActualDancePoints > STATSMAN -> m_CurStageStats . m_player [ pn ]. m_iActualDancePoints )
return RESULT_LOSE ;
}
return win ;
}
void GameState :: GetDefaultPlayerOptions ( PlayerOptions & po )
{
po . Init ();
po . FromString ( PREFSMAN -> m_sDefaultModifiers );
po . FromString ( CommonMetrics :: DEFAULT_MODIFIERS );
if ( po . m_sNoteSkin . empty () )
po . m_sNoteSkin = CommonMetrics :: DEFAULT_NOTESKIN_NAME ;
}
void GameState :: GetDefaultSongOptions ( SongOptions & so )
{
so . Init ();
so . FromString ( PREFSMAN -> m_sDefaultModifiers );
so . FromString ( CommonMetrics :: DEFAULT_MODIFIERS );
}
void GameState :: ResetToDefaultSongOptions ( ModsLevel l )
{
SongOptions so ;
GetDefaultSongOptions ( so );
m_SongOptions . Assign ( l , so );
}
void GameState :: ApplyPreferredModifiers ( PlayerNumber pn , RString sModifiers )
{
m_pPlayerState [ pn ] -> m_PlayerOptions . FromString ( ModsLevel_Preferred , sModifiers );
m_SongOptions . FromString ( ModsLevel_Preferred , sModifiers );
}
void GameState :: ApplyStageModifiers ( PlayerNumber pn , RString sModifiers )
{
m_pPlayerState [ pn ] -> m_PlayerOptions . FromString ( ModsLevel_Stage , sModifiers );
m_SongOptions . FromString ( ModsLevel_Stage , sModifiers );
}
void GameState :: ClearStageModifiersIllegalForCourse ()
{
FOREACH_EnabledPlayer ( pn )
PO_GROUP_CALL ( m_pPlayerState [ pn ] -> m_PlayerOptions , ModsLevel_Stage , ResetSavedPrefsInvalidForCourse );
}
bool GameState :: CurrentOptionsDisqualifyPlayer ( PlayerNumber pn )
{
if ( ! PREFSMAN -> m_bDisqualification )
return false ;
if ( ! IsHumanPlayer ( pn ) )
return false ;
const PlayerOptions & po = m_pPlayerState [ pn ] -> m_PlayerOptions . GetPreferred ();
// Check the stored player options for disqualify. Don't disqualify because
// of mods that were forced.
if ( IsCourseMode () )
return po . IsEasierForCourseAndTrail ( m_pCurCourse , m_pCurTrail [ pn ] );
else
return po . IsEasierForSongAndSteps ( m_pCurSong , m_pCurSteps [ pn ], pn );
}
/* reset noteskins (?)
* GameState::ResetNoteSkins()
* GameState::ResetNoteSkinsForPlayer( PlayerNumber pn )
*
*/
void GameState :: GetAllUsedNoteSkins ( vector < RString > & out ) const
{
FOREACH_EnabledPlayer ( pn )
{
out . push_back ( m_pPlayerState [ pn ] -> m_PlayerOptions . GetCurrent (). m_sNoteSkin );
// Add noteskins that are used in courses.
if ( IsCourseMode () )
{
const Trail * pTrail = m_pCurTrail [ pn ];
2012-12-27 11:38:53 -05:00
ASSERT ( pTrail != NULL );
2011-03-17 01:47:30 -04:00
FOREACH_CONST ( TrailEntry , pTrail -> m_vEntries , e )
{
PlayerOptions po ;
po . FromString ( e -> Modifiers );
if ( ! po . m_sNoteSkin . empty () )
out . push_back ( po . m_sNoteSkin );
}
}
}
// Remove duplicates.
sort ( out . begin (), out . end () );
out . erase ( unique ( out . begin (), out . end () ), out . end () );
}
void GameState :: RemoveAllActiveAttacks () // called on end of song
{
FOREACH_PlayerNumber ( p )
m_pPlayerState [ p ] -> RemoveActiveAttacks ();
}
2011-09-21 16:21:23 -06:00
void GameState :: AddStageToPlayer ( PlayerNumber pn )
{
// Add one stage more to player (bonus) -cerbo
++ m_iPlayerStageTokens [ pn ];
}
2011-03-17 01:47:30 -04:00
template < class T >
void setmin ( T & a , const T & b )
{
a = min ( a , b );
}
template < class T >
void setmax ( T & a , const T & b )
{
a = max ( a , b );
}
PlayerOptions :: FailType GameState :: GetPlayerFailType ( const PlayerState * pPlayerState ) const
{
PlayerNumber pn = pPlayerState -> m_PlayerNumber ;
PlayerOptions :: FailType ft = pPlayerState -> m_PlayerOptions . GetCurrent (). m_FailType ;
// If the player changed the fail mode explicitly, leave it alone.
if ( m_bChangedFailTypeOnScreenSongOptions )
return ft ;
if ( IsCourseMode () )
{
if ( PREFSMAN -> m_bMinimum1FullSongInCourses && GetCourseSongIndex () == 0 )
ft = max ( ft , PlayerOptions :: FAIL_IMMEDIATE_CONTINUE ); // take the least harsh of the two FailTypes
}
else
{
Difficulty dc = Difficulty_Invalid ;
if ( m_pCurSteps [ pn ] )
dc = m_pCurSteps [ pn ] -> GetDifficulty ();
bool bFirstStage = false ;
if ( ! IsEventMode () )
bFirstStage |= m_iPlayerStageTokens [ pPlayerState -> m_PlayerNumber ] == PREFSMAN -> m_iSongsPerPlay - 1 ; // HACK; -1 because this is called during gameplay
// Easy and beginner are never harder than FAIL_IMMEDIATE_CONTINUE.
if ( dc <= Difficulty_Easy )
setmax ( ft , PlayerOptions :: FAIL_IMMEDIATE_CONTINUE );
if ( dc <= Difficulty_Easy && bFirstStage && PREFSMAN -> m_bFailOffForFirstStageEasy )
setmax ( ft , PlayerOptions :: FAIL_OFF );
/* If beginner's steps were chosen, and this is the first stage,
* turn off failure completely. */
if ( dc == Difficulty_Beginner && bFirstStage )
setmax ( ft , PlayerOptions :: FAIL_OFF );
if ( dc == Difficulty_Beginner && PREFSMAN -> m_bFailOffInBeginner )
setmax ( ft , PlayerOptions :: FAIL_OFF );
}
return ft ;
}
bool GameState :: ShowW1 () const
{
2012-12-27 16:59:35 -05:00
AllowW1 pref = PREFSMAN -> m_AllowW1 ;
switch ( pref )
2011-03-17 01:47:30 -04:00
{
case ALLOW_W1_NEVER : return false ;
case ALLOW_W1_COURSES_ONLY : return IsCourseMode ();
case ALLOW_W1_EVERYWHERE : return true ;
2012-12-27 16:59:35 -05:00
default :
FAIL_M ( ssprintf ( "Invalid AllowW1 preference: %i" , pref ));
2011-03-17 01:47:30 -04:00
}
}
static ThemeMetric < bool > PROFILE_RECORD_FEATS ( "GameState" , "ProfileRecordFeats" );
static ThemeMetric < bool > CATEGORY_RECORD_FEATS ( "GameState" , "CategoryRecordFeats" );
void GameState :: GetRankingFeats ( PlayerNumber pn , vector < RankingFeat > & asFeatsOut ) const
{
if ( ! IsHumanPlayer ( pn ) )
return ;
Profile * pProf = PROFILEMAN -> GetProfile ( pn );
// Check for feats even if the PlayMode is rave or battle because the player
// may have made high scores then switched modes.
CHECKPOINT_M ( ssprintf ( "PlayMode %i" , int ( m_PlayMode )) );
switch ( m_PlayMode )
{
case PLAY_MODE_REGULAR :
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
{
CHECKPOINT ;
StepsType st = GetCurrentStyle () -> m_StepsType ;
// Find unique Song and Steps combinations that were played.
// We must keep only the unique combination or else we'll double-count
// high score markers.
vector < SongAndSteps > vSongAndSteps ;
for ( unsigned i = 0 ; i < STATSMAN -> m_vPlayedStageStats . size (); i ++ )
{
CHECKPOINT_M ( ssprintf ( "%u/%i" , i , ( int ) STATSMAN -> m_vPlayedStageStats . size () ) );
SongAndSteps sas ;
ASSERT ( ! STATSMAN -> m_vPlayedStageStats [ i ]. m_vpPlayedSongs . empty () );
sas . pSong = STATSMAN -> m_vPlayedStageStats [ i ]. m_vpPlayedSongs [ 0 ];
2012-12-27 11:38:53 -05:00
ASSERT ( sas . pSong != NULL );
2011-03-17 01:47:30 -04:00
if ( STATSMAN -> m_vPlayedStageStats [ i ]. m_player [ pn ]. m_vpPossibleSteps . empty () )
continue ;
sas . pSteps = STATSMAN -> m_vPlayedStageStats [ i ]. m_player [ pn ]. m_vpPossibleSteps [ 0 ];
2012-12-27 11:38:53 -05:00
ASSERT ( sas . pSteps != NULL );
2011-03-17 01:47:30 -04:00
vSongAndSteps . push_back ( sas );
}
CHECKPOINT ;
sort ( vSongAndSteps . begin (), vSongAndSteps . end () );
vector < SongAndSteps >:: iterator toDelete = unique ( vSongAndSteps . begin (), vSongAndSteps . end () );
vSongAndSteps . erase ( toDelete , vSongAndSteps . end ());
CHECKPOINT ;
for ( unsigned i = 0 ; i < vSongAndSteps . size (); i ++ )
{
Song * pSong = vSongAndSteps [ i ]. pSong ;
Steps * pSteps = vSongAndSteps [ i ]. pSteps ;
// Find Machine Records
{
HighScoreList & hsl = PROFILEMAN -> GetMachineProfile () -> GetStepsHighScoreList ( pSong , pSteps );
for ( unsigned j = 0 ; j < hsl . vHighScores . size (); j ++ )
{
HighScore & hs = hsl . vHighScores [ j ];
if ( hs . GetName () != RANKING_TO_FILL_IN_MARKER [ pn ] )
continue ;
RankingFeat feat ;
feat . Type = RankingFeat :: SONG ;
feat . pSong = pSong ;
feat . pSteps = pSteps ;
feat . Feat = ssprintf ( "MR #%d in %s %s" , j + 1 , pSong -> GetTranslitMainTitle (). c_str (), DifficultyToString ( pSteps -> GetDifficulty ()). c_str () );
feat . pStringToFill = hs . GetNameMutable ();
feat . grade = hs . GetGrade ();
feat . fPercentDP = hs . GetPercentDP ();
feat . iScore = hs . GetScore ();
if ( pSong -> HasBanner () )
feat . Banner = pSong -> GetBannerPath ();
asFeatsOut . push_back ( feat );
}
}
// Find Personal Records
if ( pProf && PROFILE_RECORD_FEATS )
{
HighScoreList & hsl = pProf -> GetStepsHighScoreList ( pSong , pSteps );
for ( unsigned j = 0 ; j < hsl . vHighScores . size (); j ++ )
{
HighScore & hs = hsl . vHighScores [ j ];
if ( hs . GetName () != RANKING_TO_FILL_IN_MARKER [ pn ] )
continue ;
RankingFeat feat ;
feat . pSong = pSong ;
feat . pSteps = pSteps ;
feat . Type = RankingFeat :: SONG ;
feat . Feat = ssprintf ( "PR #%d in %s %s" , j + 1 , pSong -> GetTranslitMainTitle (). c_str (), DifficultyToString ( pSteps -> GetDifficulty ()). c_str () );
feat . pStringToFill = hs . GetNameMutable ();
feat . grade = hs . GetGrade ();
feat . fPercentDP = hs . GetPercentDP ();
feat . iScore = hs . GetScore ();
// XXX: temporary hack
// Why is this here? -aj
if ( pSong -> HasBackground () )
feat . Banner = pSong -> GetBackgroundPath ();
// if( pSong->HasBanner() )
// feat.Banner = pSong->GetBannerPath();
asFeatsOut . push_back ( feat );
}
}
}
CHECKPOINT ;
StageStats stats ;
STATSMAN -> GetFinalEvalStageStats ( stats );
// Find Machine Category Records
FOREACH_ENUM ( RankingCategory , rc )
{
if ( ! CATEGORY_RECORD_FEATS )
continue ;
HighScoreList & hsl = PROFILEMAN -> GetMachineProfile () -> GetCategoryHighScoreList ( st , rc );
for ( unsigned j = 0 ; j < hsl . vHighScores . size (); j ++ )
{
HighScore & hs = hsl . vHighScores [ j ];
if ( hs . GetName () != RANKING_TO_FILL_IN_MARKER [ pn ] )
continue ;
RankingFeat feat ;
feat . Type = RankingFeat :: CATEGORY ;
feat . Feat = ssprintf ( "MR #%d in Type %c (%d)" , j + 1 , 'A' + rc , stats . GetAverageMeter ( pn ) );
feat . pStringToFill = hs . GetNameMutable ();
feat . grade = Grade_NoData ;
feat . iScore = hs . GetScore ();
feat . fPercentDP = hs . GetPercentDP ();
asFeatsOut . push_back ( feat );
}
}
// Find Personal Category Records
FOREACH_ENUM ( RankingCategory , rc )
{
if ( ! CATEGORY_RECORD_FEATS )
continue ;
if ( pProf && PROFILE_RECORD_FEATS )
{
HighScoreList & hsl = pProf -> GetCategoryHighScoreList ( st , rc );
for ( unsigned j = 0 ; j < hsl . vHighScores . size (); j ++ )
{
HighScore & hs = hsl . vHighScores [ j ];
if ( hs . GetName () != RANKING_TO_FILL_IN_MARKER [ pn ] )
continue ;
RankingFeat feat ;
feat . Type = RankingFeat :: CATEGORY ;
feat . Feat = ssprintf ( "PR #%d in Type %c (%d)" , j + 1 , 'A' + rc , stats . GetAverageMeter ( pn ) );
feat . pStringToFill = hs . GetNameMutable ();
feat . grade = Grade_NoData ;
feat . iScore = hs . GetScore ();
feat . fPercentDP = hs . GetPercentDP ();
asFeatsOut . push_back ( feat );
}
}
}
}
break ;
case PLAY_MODE_NONSTOP :
case PLAY_MODE_ONI :
case PLAY_MODE_ENDLESS :
{
CHECKPOINT ;
Course * pCourse = m_pCurCourse ;
2012-12-27 11:38:53 -05:00
ASSERT ( pCourse != NULL );
2011-03-17 01:47:30 -04:00
Trail * pTrail = m_pCurTrail [ pn ];
2012-12-27 11:38:53 -05:00
ASSERT ( pTrail != NULL );
2011-03-17 01:47:30 -04:00
CourseDifficulty cd = pTrail -> m_CourseDifficulty ;
// Find Machine Records
{
Profile * pProfile = PROFILEMAN -> GetMachineProfile ();
HighScoreList & hsl = pProfile -> GetCourseHighScoreList ( pCourse , pTrail );
for ( unsigned i = 0 ; i < hsl . vHighScores . size (); i ++ )
{
HighScore & hs = hsl . vHighScores [ i ];
if ( hs . GetName () != RANKING_TO_FILL_IN_MARKER [ pn ] )
continue ;
RankingFeat feat ;
feat . Type = RankingFeat :: COURSE ;
feat . pCourse = pCourse ;
feat . Feat = ssprintf ( "MR #%d in %s" , i + 1 , pCourse -> GetDisplayFullTitle (). c_str () );
if ( cd != Difficulty_Medium )
feat . Feat += " " + CourseDifficultyToLocalizedString ( cd );
feat . pStringToFill = hs . GetNameMutable ();
feat . grade = Grade_NoData ;
feat . iScore = hs . GetScore ();
feat . fPercentDP = hs . GetPercentDP ();
if ( pCourse -> HasBanner () )
feat . Banner = pCourse -> GetBannerPath ();
asFeatsOut . push_back ( feat );
}
}
// Find Personal Records
if ( PROFILE_RECORD_FEATS && PROFILEMAN -> IsPersistentProfile ( pn ) )
{
HighScoreList & hsl = pProf -> GetCourseHighScoreList ( pCourse , pTrail );
for ( unsigned i = 0 ; i < hsl . vHighScores . size (); i ++ )
{
HighScore & hs = hsl . vHighScores [ i ];
if ( hs . GetName () != RANKING_TO_FILL_IN_MARKER [ pn ] )
continue ;
RankingFeat feat ;
feat . Type = RankingFeat :: COURSE ;
feat . pCourse = pCourse ;
feat . Feat = ssprintf ( "PR #%d in %s" , i + 1 , pCourse -> GetDisplayFullTitle (). c_str () );
feat . pStringToFill = hs . GetNameMutable ();
feat . grade = Grade_NoData ;
feat . iScore = hs . GetScore ();
feat . fPercentDP = hs . GetPercentDP ();
if ( pCourse -> HasBanner () )
feat . Banner = pCourse -> GetBannerPath ();
asFeatsOut . push_back ( feat );
}
}
}
break ;
default :
2013-01-03 03:17:30 -06:00
FAIL_M ( ssprintf ( "Invalid play mode: %i" , int ( m_PlayMode )));
2011-03-17 01:47:30 -04:00
}
}
bool GameState :: AnyPlayerHasRankingFeats () const
{
vector < RankingFeat > vFeats ;
FOREACH_PlayerNumber ( p )
{
GetRankingFeats ( p , vFeats );
if ( ! vFeats . empty () )
return true ;
}
return false ;
}
void GameState :: StoreRankingName ( PlayerNumber pn , RString sName )
{
sName . MakeUpper ();
if ( USE_NAME_BLACKLIST )
{
RageFile file ;
if ( file . Open ( NAME_BLACKLIST_FILE ) )
{
RString sLine ;
while ( ! file . AtEOF () )
{
if ( file . GetLine ( sLine ) == - 1 )
{
LOG -> Warn ( "Error reading \" %s \" : %s" , NAME_BLACKLIST_FILE , file . GetError (). c_str () );
break ;
}
sLine . MakeUpper ();
if ( ! sLine . empty () && sName . find ( sLine ) != string :: npos ) // name contains a bad word
{
LOG -> Trace ( "entered '%s' matches blacklisted item '%s'" , sName . c_str (), sLine . c_str () );
sName = "" ;
break ;
}
}
}
}
vector < RankingFeat > aFeats ;
GetRankingFeats ( pn , aFeats );
for ( unsigned i = 0 ; i < aFeats . size (); i ++ )
{
* aFeats [ i ]. pStringToFill = sName ;
// save name pointers as we fill them
m_vpsNamesThatWereFilled . push_back ( aFeats [ i ]. pStringToFill );
}
Profile * pProfile = PROFILEMAN -> GetMachineProfile ();
if ( ! PREFSMAN -> m_bAllowMultipleHighScoreWithSameName )
{
// erase all but the highest score for each name
FOREACHM ( SongID , Profile :: HighScoresForASong , pProfile -> m_SongHighScores , iter )
FOREACHM ( StepsID , Profile :: HighScoresForASteps , iter -> second . m_StepsHighScores , iter2 )
iter2 -> second . hsl . RemoveAllButOneOfEachName ();
FOREACHM ( CourseID , Profile :: HighScoresForACourse , pProfile -> m_CourseHighScores , iter )
FOREACHM ( TrailID , Profile :: HighScoresForATrail , iter -> second . m_TrailHighScores , iter2 )
iter2 -> second . hsl . RemoveAllButOneOfEachName ();
}
// clamp high score sizes
FOREACHM ( SongID , Profile :: HighScoresForASong , pProfile -> m_SongHighScores , iter )
FOREACHM ( StepsID , Profile :: HighScoresForASteps , iter -> second . m_StepsHighScores , iter2 )
iter2 -> second . hsl . ClampSize ( true );
FOREACHM ( CourseID , Profile :: HighScoresForACourse , pProfile -> m_CourseHighScores , iter )
FOREACHM ( TrailID , Profile :: HighScoresForATrail , iter -> second . m_TrailHighScores , iter2 )
iter2 -> second . hsl . ClampSize ( true );
}
bool GameState :: AllAreInDangerOrWorse () const
{
FOREACH_EnabledPlayer ( p )
if ( m_pPlayerState [ p ] -> m_HealthState < HealthState_Danger )
return false ;
return true ;
}
bool GameState :: OneIsHot () const
{
FOREACH_EnabledPlayer ( p )
if ( m_pPlayerState [ p ] -> m_HealthState == HealthState_Hot )
return true ;
return false ;
}
bool GameState :: IsTimeToPlayAttractSounds () const
{
// m_iNumTimesThroughAttract will be -1 from the first attract screen after
// the end of a game until the next time FIRST_ATTRACT_SCREEN is reached.
// Play attract sounds for this sort span of time regardless of
// m_AttractSoundFrequency because it's awkward to have the machine go
// silent immediately after the end of a game.
if ( m_iNumTimesThroughAttract == - 1 )
return true ;
if ( PREFSMAN -> m_AttractSoundFrequency == ASF_NEVER )
return false ;
// play attract sounds once every m_iAttractSoundFrequency times through
if ( ( m_iNumTimesThroughAttract % PREFSMAN -> m_AttractSoundFrequency ) == 0 )
return true ;
return false ;
}
void GameState :: VisitAttractScreen ( const RString sScreenName )
{
if ( sScreenName == CommonMetrics :: FIRST_ATTRACT_SCREEN . GetValue () )
m_iNumTimesThroughAttract ++ ;
}
bool GameState :: DifficultiesLocked () const
{
if ( m_PlayMode == PLAY_MODE_RAVE )
return true ;
if ( IsCourseMode () )
return PREFSMAN -> m_bLockCourseDifficulties ;
if ( GetCurrentStyle () -> m_bLockDifficulties )
return true ;
return false ;
}
bool GameState :: ChangePreferredDifficultyAndStepsType ( PlayerNumber pn , Difficulty dc , StepsType st )
{
m_PreferredDifficulty [ pn ]. Set ( dc );
m_PreferredStepsType . Set ( st );
if ( DifficultiesLocked () )
FOREACH_PlayerNumber ( p )
if ( p != pn )
m_PreferredDifficulty [ p ]. Set ( m_PreferredDifficulty [ pn ] );
return true ;
}
/* When only displaying difficulties in DIFFICULTIES_TO_SHOW, use GetClosestShownDifficulty
* to find which difficulty to show, and ChangePreferredDifficulty(pn, dir) to change
* difficulty. */
bool GameState :: ChangePreferredDifficulty ( PlayerNumber pn , int dir )
{
const vector < Difficulty > & v = CommonMetrics :: DIFFICULTIES_TO_SHOW . GetValue ();
Difficulty d = GetClosestShownDifficulty ( pn );
while ( 1 )
{
d = enum_add2 ( d , dir );
if ( d < 0 || d >= NUM_Difficulty )
return false ;
if ( find ( v . begin (), v . end (), d ) != v . end () )
break ; // found
}
m_PreferredDifficulty [ pn ]. Set ( d );
return true ;
}
/* The user may be set to prefer a difficulty that isn't always shown; typically,
* Difficulty_Edit. Return the closest shown difficulty <= m_PreferredDifficulty. */
Difficulty GameState :: GetClosestShownDifficulty ( PlayerNumber pn ) const
{
const vector < Difficulty > & v = CommonMetrics :: DIFFICULTIES_TO_SHOW . GetValue ();
Difficulty iClosest = ( Difficulty ) 0 ;
int iClosestDist = - 1 ;
FOREACH_CONST ( Difficulty , v , dc )
{
int iDist = m_PreferredDifficulty [ pn ] - * dc ;
if ( iDist < 0 )
continue ;
if ( iClosestDist != - 1 && iDist > iClosestDist )
continue ;
iClosestDist = iDist ;
iClosest = * dc ;
}
return iClosest ;
}
bool GameState :: ChangePreferredCourseDifficultyAndStepsType ( PlayerNumber pn , CourseDifficulty cd , StepsType st )
{
m_PreferredCourseDifficulty [ pn ]. Set ( cd );
m_PreferredStepsType . Set ( st );
if ( PREFSMAN -> m_bLockCourseDifficulties )
FOREACH_PlayerNumber ( p )
if ( p != pn )
m_PreferredCourseDifficulty [ p ]. Set ( m_PreferredCourseDifficulty [ pn ] );
return true ;
}
bool GameState :: ChangePreferredCourseDifficulty ( PlayerNumber pn , int dir )
{
/* If we have a course selected, only choose among difficulties available in the course. */
const Course * pCourse = m_pCurCourse ;
const vector < CourseDifficulty > & v = CommonMetrics :: COURSE_DIFFICULTIES_TO_SHOW . GetValue ();
CourseDifficulty cd = m_PreferredCourseDifficulty [ pn ];
while ( 1 )
{
cd = enum_add2 ( cd , dir );
if ( cd < 0 || cd >= NUM_Difficulty )
return false ;
if ( find ( v . begin (), v . end (), cd ) == v . end () )
continue ; /* not available */
if ( ! pCourse || pCourse -> GetTrail ( GetCurrentStyle () -> m_StepsType , cd ) )
break ;
}
return ChangePreferredCourseDifficulty ( pn , cd );
}
bool GameState :: IsCourseDifficultyShown ( CourseDifficulty cd )
{
const vector < CourseDifficulty > & v = CommonMetrics :: COURSE_DIFFICULTIES_TO_SHOW . GetValue ();
return find ( v . begin (), v . end (), cd ) != v . end ();
}
Difficulty GameState :: GetEasiestStepsDifficulty () const
{
Difficulty dc = Difficulty_Invalid ;
FOREACH_HumanPlayer ( p )
{
if ( m_pCurSteps [ p ] == NULL )
{
LOG -> Warn ( "GetEasiestStepsDifficulty called but p%i hasn't chosen notes" , p + 1 );
continue ;
}
dc = min ( dc , m_pCurSteps [ p ] -> GetDifficulty () );
}
return dc ;
}
Difficulty GameState :: GetHardestStepsDifficulty () const
{
Difficulty dc = Difficulty_Beginner ;
FOREACH_HumanPlayer ( p )
{
if ( m_pCurSteps [ p ] == NULL )
{
LOG -> Warn ( "GetHardestStepsDifficulty called but p%i hasn't chosen notes" , p + 1 );
continue ;
}
dc = max ( dc , m_pCurSteps [ p ] -> GetDifficulty () );
}
return dc ;
}
bool GameState :: IsEventMode () const
{
return m_bTemporaryEventMode || PREFSMAN -> m_bEventMode ;
}
CoinMode GameState :: GetCoinMode () const
{
2014-02-02 12:53:26 -06:00
// XXX: Event mode shouldn't be valid if CoinMode_Pay
if ( IsEventMode () && GamePreferences :: m_CoinMode == CoinMode_Pay )
return CoinMode_Free ;
else
return GamePreferences :: m_CoinMode ;
2011-03-17 01:47:30 -04:00
}
ThemeMetric < bool > DISABLE_PREMIUM_IN_EVENT_MODE ( "GameState" , "DisablePremiumInEventMode" );
Premium GameState :: GetPremium () const
{
return DISABLE_PREMIUM_IN_EVENT_MODE ? Premium_Off : g_Premium ;
}
float GameState :: GetGoalPercentComplete ( PlayerNumber pn )
{
const Profile * pProfile = PROFILEMAN -> GetProfile ( pn );
const StageStats & ssCurrent = STATSMAN -> m_CurStageStats ;
const PlayerStageStats & pssCurrent = ssCurrent . m_player [ pn ];
float fActual = 0 ;
float fGoal = 0 ;
switch ( pProfile -> m_GoalType )
{
case GoalType_Calories :
fActual = pssCurrent . m_fCaloriesBurned ;
fGoal = ( float ) pProfile -> m_iGoalCalories ;
break ;
case GoalType_Time :
fActual = ssCurrent . m_fGameplaySeconds ;
fGoal = ( float ) pProfile -> m_iGoalSeconds ;
break ;
case GoalType_None :
return 0 ; // never complete
default :
2012-12-27 16:59:35 -05:00
FAIL_M ( ssprintf ( "Invalid GoalType: %i" , pProfile -> m_GoalType ));
2011-03-17 01:47:30 -04:00
}
if ( fGoal == 0 )
return 0 ;
else
return fActual / fGoal ;
}
bool GameState :: PlayerIsUsingModifier ( PlayerNumber pn , const RString & sModifier )
{
PlayerOptions po = m_pPlayerState [ pn ] -> m_PlayerOptions . GetCurrent ();
SongOptions so = m_SongOptions . GetCurrent ();
po . FromString ( sModifier );
so . FromString ( sModifier );
return po == m_pPlayerState [ pn ] -> m_PlayerOptions . GetCurrent () && so == m_SongOptions . GetCurrent ();
}
Profile * GameState :: GetEditLocalProfile ()
{
if ( m_sEditLocalProfileID . Get (). empty () )
return NULL ;
return PROFILEMAN -> GetLocalProfile ( m_sEditLocalProfileID );
}
PlayerNumber GetNextHumanPlayer ( PlayerNumber pn )
{
for ( enum_add ( pn , 1 ); pn < NUM_PLAYERS ; enum_add ( pn , 1 ) )
if ( GAMESTATE -> IsHumanPlayer ( pn ) )
return pn ;
return PLAYER_INVALID ;
}
PlayerNumber GetNextEnabledPlayer ( PlayerNumber pn )
{
for ( enum_add ( pn , 1 ); pn < NUM_PLAYERS ; enum_add ( pn , 1 ) )
if ( GAMESTATE -> IsPlayerEnabled ( pn ) )
return pn ;
return PLAYER_INVALID ;
}
PlayerNumber GetNextCpuPlayer ( PlayerNumber pn )
{
for ( enum_add ( pn , 1 ); pn < NUM_PLAYERS ; enum_add ( pn , 1 ) )
if ( GAMESTATE -> IsCpuPlayer ( pn ) )
return pn ;
return PLAYER_INVALID ;
}
PlayerNumber GetNextPotentialCpuPlayer ( PlayerNumber pn )
{
for ( enum_add ( pn , 1 ); pn < NUM_PLAYERS ; enum_add ( pn , 1 ) )
if ( ! GAMESTATE -> IsHumanPlayer ( pn ) )
return pn ;
return PLAYER_INVALID ;
}
MultiPlayer GetNextEnabledMultiPlayer ( MultiPlayer mp )
{
for ( enum_add ( mp , 1 ); mp < NUM_MultiPlayer ; enum_add ( mp , 1 ) )
if ( GAMESTATE -> IsMultiPlayerEnabled ( mp ) )
return mp ;
return MultiPlayer_Invalid ;
}
// lua start
#include "LuaBinding.h"
#include "Game.h"
/** @brief Allow Lua to have access to the GameState. */
class LunaGameState : public Luna < GameState >
{
public :
DEFINE_METHOD ( IsPlayerEnabled , IsPlayerEnabled ( Enum :: Check < PlayerNumber > ( L , 1 )) )
DEFINE_METHOD ( IsHumanPlayer , IsHumanPlayer ( Enum :: Check < PlayerNumber > ( L , 1 )) )
DEFINE_METHOD ( GetPlayerDisplayName , GetPlayerDisplayName ( Enum :: Check < PlayerNumber > ( L , 1 )) )
2011-06-06 20:40:11 -04:00
DEFINE_METHOD ( GetMasterPlayerNumber , GetMasterPlayerNumber () )
2011-03-17 01:47:30 -04:00
DEFINE_METHOD ( GetMultiplayer , m_bMultiplayer )
static int SetMultiplayer ( T * p , lua_State * L )
{
p -> m_bMultiplayer = BArg ( 1 );
return 0 ;
}
2011-12-12 18:49:10 -05:00
DEFINE_METHOD ( InStepEditor , m_bInStepEditor );
2011-03-17 01:47:30 -04:00
DEFINE_METHOD ( GetNumMultiplayerNoteFields , m_iNumMultiplayerNoteFields )
DEFINE_METHOD ( ShowW1 , ShowW1 () )
static int SetNumMultiplayerNoteFields ( T * p , lua_State * L )
{
p -> m_iNumMultiplayerNoteFields = IArg ( 1 );
return 0 ;
}
static int GetPlayerState ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
p -> m_pPlayerState [ pn ] -> PushSelf ( L );
return 1 ;
}
static int GetMultiPlayerState ( T * p , lua_State * L )
{
MultiPlayer mp = Enum :: Check < MultiPlayer > ( L , 1 );
p -> m_pMultiPlayerState [ mp ] -> PushSelf ( L );
return 1 ;
}
static int ApplyGameCommand ( T * p , lua_State * L )
{
PlayerNumber pn = PLAYER_INVALID ;
2014-02-23 16:41:19 -05:00
if ( lua_gettop ( L ) >= 2 && ! lua_isnil ( L , 2 ) ) {
// Legacy behavior: if an old-style numerical argument
// is given, decrement it before trying to parse
if ( lua_isnumber ( L , 2 ) ) {
int arg = ( int ) lua_tonumber ( L , 2 );
arg -- ;
LuaHelpers :: Push ( L , arg );
lua_replace ( L , - 2 );
}
2011-03-17 01:47:30 -04:00
pn = Enum :: Check < PlayerNumber > ( L , 2 );
2014-02-23 16:41:19 -05:00
}
2011-03-17 01:47:30 -04:00
p -> ApplyGameCommand ( SArg ( 1 ), pn );
return 0 ;
}
static int GetCurrentSong ( T * p , lua_State * L ) { if ( p -> m_pCurSong ) p -> m_pCurSong -> PushSelf ( L ); else lua_pushnil ( L ); return 1 ; }
static int SetCurrentSong ( T * p , lua_State * L )
{
if ( lua_isnil ( L , 1 ) ) { p -> m_pCurSong . Set ( NULL ); }
else { Song * pS = Luna < Song >:: check ( L , 1 , true ); p -> m_pCurSong . Set ( pS ); }
return 0 ;
}
static int GetCurrentSteps ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
Steps * pSteps = p -> m_pCurSteps [ pn ];
if ( pSteps ) { pSteps -> PushSelf ( L ); }
else { lua_pushnil ( L ); }
return 1 ;
}
static int SetCurrentSteps ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
if ( lua_isnil ( L , 2 ) ) { p -> m_pCurSteps [ pn ]. Set ( NULL ); }
else { Steps * pS = Luna < Steps >:: check ( L , 2 ); p -> m_pCurSteps [ pn ]. Set ( pS ); }
2013-01-02 14:07:03 -05:00
ASSERT ( p -> m_pCurSteps [ pn ] -> m_StepsType == p -> m_pCurStyle -> m_StepsType );
2011-03-17 01:47:30 -04:00
// Why Broadcast again? This is double-broadcasting. -Chris
MESSAGEMAN -> Broadcast ( ( MessageID )( Message_CurrentStepsP1Changed + pn ) );
return 0 ;
}
static int GetCurrentCourse ( T * p , lua_State * L ) { if ( p -> m_pCurCourse ) p -> m_pCurCourse -> PushSelf ( L ); else lua_pushnil ( L ); return 1 ; }
static int SetCurrentCourse ( T * p , lua_State * L )
{
if ( lua_isnil ( L , 1 ) ) { p -> m_pCurCourse . Set ( NULL ); }
else { Course * pC = Luna < Course >:: check ( L , 1 ); p -> m_pCurCourse . Set ( pC ); }
return 0 ;
}
static int GetCurrentTrail ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
Trail * pTrail = p -> m_pCurTrail [ pn ];
if ( pTrail ) { pTrail -> PushSelf ( L ); }
else { lua_pushnil ( L ); }
return 1 ;
}
static int SetCurrentTrail ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
if ( lua_isnil ( L , 2 ) ) { p -> m_pCurTrail [ pn ]. Set ( NULL ); }
else { Trail * pS = Luna < Trail >:: check ( L , 2 ); p -> m_pCurTrail [ pn ]. Set ( pS ); }
MESSAGEMAN -> Broadcast ( ( MessageID )( Message_CurrentTrailP1Changed + pn ) );
return 0 ;
}
static int GetPreferredSong ( T * p , lua_State * L ) { if ( p -> m_pPreferredSong ) p -> m_pPreferredSong -> PushSelf ( L ); else lua_pushnil ( L ); return 1 ; }
static int SetPreferredSong ( T * p , lua_State * L )
{
if ( lua_isnil ( L , 1 ) ) { p -> m_pPreferredSong = NULL ; }
else { Song * pS = Luna < Song >:: check ( L , 1 ); p -> m_pPreferredSong = pS ; }
return 0 ;
}
static int SetTemporaryEventMode ( T * p , lua_State * L ) { p -> m_bTemporaryEventMode = BArg ( 1 ); return 0 ; }
static int Env ( T * p , lua_State * L ) { p -> m_Environment -> PushSelf ( L ); return 1 ; }
static int GetEditSourceSteps ( T * p , lua_State * L )
{
Steps * pSteps = p -> m_pEditSourceSteps ;
if ( pSteps ) { pSteps -> PushSelf ( L ); }
else { lua_pushnil ( L ); }
return 1 ;
}
static int SetPreferredDifficulty ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
Difficulty dc = Enum :: Check < Difficulty > ( L , 2 );
p -> m_PreferredDifficulty [ pn ]. Set ( dc );
return 0 ;
}
DEFINE_METHOD ( GetPreferredDifficulty , m_PreferredDifficulty [ Enum :: Check < PlayerNumber > ( L , 1 )] )
DEFINE_METHOD ( AnyPlayerHasRankingFeats , AnyPlayerHasRankingFeats () )
DEFINE_METHOD ( IsCourseMode , IsCourseMode () )
DEFINE_METHOD ( IsBattleMode , IsBattleMode () )
DEFINE_METHOD ( IsDemonstration , m_bDemonstrationOrJukebox )
DEFINE_METHOD ( GetPlayMode , m_PlayMode )
DEFINE_METHOD ( GetSortOrder , m_SortOrder )
DEFINE_METHOD ( GetCurrentStageIndex , m_iCurrentStageIndex )
DEFINE_METHOD ( IsGoalComplete , IsGoalComplete ( Enum :: Check < PlayerNumber > ( L , 1 )) )
DEFINE_METHOD ( PlayerIsUsingModifier , PlayerIsUsingModifier ( Enum :: Check < PlayerNumber > ( L , 1 ), SArg ( 2 )) )
DEFINE_METHOD ( GetCourseSongIndex , GetCourseSongIndex () )
DEFINE_METHOD ( GetLoadingCourseSongIndex , GetLoadingCourseSongIndex () )
DEFINE_METHOD ( GetSmallestNumStagesLeftForAnyHumanPlayer , GetSmallestNumStagesLeftForAnyHumanPlayer () )
DEFINE_METHOD ( IsAnExtraStage , IsAnExtraStage () )
DEFINE_METHOD ( IsExtraStage , IsExtraStage () )
DEFINE_METHOD ( IsExtraStage2 , IsExtraStage2 () )
DEFINE_METHOD ( GetCurrentStage , GetCurrentStage () )
DEFINE_METHOD ( HasEarnedExtraStage , HasEarnedExtraStage () )
2011-09-07 23:48:04 -05:00
DEFINE_METHOD ( GetEarnedExtraStage , GetEarnedExtraStage () )
2011-03-17 01:47:30 -04:00
DEFINE_METHOD ( GetEasiestStepsDifficulty , GetEasiestStepsDifficulty () )
DEFINE_METHOD ( GetHardestStepsDifficulty , GetHardestStepsDifficulty () )
DEFINE_METHOD ( IsEventMode , IsEventMode () )
DEFINE_METHOD ( GetNumPlayersEnabled , GetNumPlayersEnabled () )
2011-05-15 08:21:16 -05:00
/*DEFINE_METHOD( GetSongBeat, m_Position.m_fSongBeat )
2011-05-11 20:53:40 +07:00
DEFINE_METHOD( GetSongBeatVisible, m_Position.m_fSongBeatVisible )
2011-05-10 14:53:59 -04:00
DEFINE_METHOD( GetSongBPS, m_Position.m_fCurBPS )
DEFINE_METHOD( GetSongFreeze, m_Position.m_bFreeze )
2011-05-15 08:21:16 -05:00
DEFINE_METHOD( GetSongDelay, m_Position.m_bDelay )*/
2011-05-15 13:34:01 -05:00
static int GetSongPosition ( T * p , lua_State * L )
{
p -> m_Position . PushSelf ( L );
return 1 ;
}
2011-03-17 01:47:30 -04:00
DEFINE_METHOD ( GetGameplayLeadIn , m_bGameplayLeadIn )
2013-06-04 23:47:22 -04:00
DEFINE_METHOD ( GetCoins , m_iCoins )
2011-03-17 01:47:30 -04:00
DEFINE_METHOD ( IsSideJoined , m_bSideIsJoined [ Enum :: Check < PlayerNumber > ( L , 1 )] )
2013-06-04 23:47:22 -04:00
DEFINE_METHOD ( GetCoinsNeededToJoin , GetCoinsNeededToJoin () )
DEFINE_METHOD ( EnoughCreditsToJoin , EnoughCreditsToJoin () )
2011-03-17 01:47:30 -04:00
DEFINE_METHOD ( PlayersCanJoin , PlayersCanJoin () )
DEFINE_METHOD ( GetNumSidesJoined , GetNumSidesJoined () )
DEFINE_METHOD ( GetCoinMode , GetCoinMode () )
DEFINE_METHOD ( GetPremium , GetPremium () )
DEFINE_METHOD ( GetSongOptionsString , m_SongOptions . GetCurrent (). GetString () )
static int GetSongOptions ( T * p , lua_State * L )
{
ModsLevel m = Enum :: Check < ModsLevel > ( L , 1 );
RString s = p -> m_SongOptions . Get ( m ). GetString ();
LuaHelpers :: Push ( L , s );
return 1 ;
}
static int GetDefaultSongOptions ( T * p , lua_State * L )
{
SongOptions so ;
p -> GetDefaultSongOptions ( so );
lua_pushstring ( L , so . GetString ());
return 1 ;
}
2011-09-30 14:50:18 -05:00
static int ApplyStageModifiers ( T * p , lua_State * L )
{
p -> ApplyStageModifiers ( Enum :: Check < PlayerNumber > ( L , 1 ), SArg ( 2 ) );
return 0 ;
}
2011-09-30 13:44:05 -05:00
static int ApplyPreferredModifiers ( T * p , lua_State * L )
{
p -> ApplyPreferredModifiers ( Enum :: Check < PlayerNumber > ( L , 1 ), SArg ( 2 ) );
return 0 ;
}
2011-03-17 01:47:30 -04:00
static int ClearStageModifiersIllegalForCourse ( T * p , lua_State * L )
{
p -> ClearStageModifiersIllegalForCourse ();
return 0 ;
}
static int SetSongOptions ( T * p , lua_State * L )
{
ModsLevel m = Enum :: Check < ModsLevel > ( L , 1 );
SongOptions so ;
so . FromString ( SArg ( 2 ) );
p -> m_SongOptions . Assign ( m , so );
return 0 ;
}
2012-03-28 18:55:17 -05:00
static int GetStageResult ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
LuaHelpers :: Push ( L , p -> GetStageResult ( pn ) );
return 1 ;
}
2011-03-17 01:47:30 -04:00
static int IsWinner ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
lua_pushboolean ( L , p -> GetStageResult ( pn ) == RESULT_WIN ); return 1 ;
}
static int IsDraw ( T * p , lua_State * L )
{
lua_pushboolean ( L , p -> GetStageResult ( PLAYER_1 ) == RESULT_DRAW ); return 1 ;
}
static int GetCurrentGame ( T * p , lua_State * L ) { const_cast < Game *> ( p -> GetCurrentGame ()) -> PushSelf ( L ); return 1 ; }
//static int SetCurrentGame( T* p, lua_State *L ) { p->SetCurrentGame( GAMEMAN->StringToGame( SArg(1) ) ); return 0; }
DEFINE_METHOD ( GetEditCourseEntryIndex , m_iEditCourseEntryIndex )
DEFINE_METHOD ( GetEditLocalProfileID , m_sEditLocalProfileID . Get () )
static int GetEditLocalProfile ( T * p , lua_State * L )
{
Profile * pProfile = p -> GetEditLocalProfile ();
if ( pProfile )
pProfile -> PushSelf ( L );
else
lua_pushnil ( L );
return 1 ;
}
static int GetCurrentStepsCredits ( T * t , lua_State * L )
{
const Song * pSong = t -> m_pCurSong ;
if ( pSong == NULL )
return 0 ;
// use a vector and not a set so that ordering is maintained
vector < const Steps *> vpStepsToShow ;
FOREACH_HumanPlayer ( p )
{
const Steps * pSteps = GAMESTATE -> m_pCurSteps [ p ];
if ( pSteps == NULL )
return 0 ;
bool bAlreadyAdded = find ( vpStepsToShow . begin (), vpStepsToShow . end (), pSteps ) != vpStepsToShow . end ();
if ( ! bAlreadyAdded )
vpStepsToShow . push_back ( pSteps );
}
for ( unsigned i = 0 ; i < vpStepsToShow . size (); i ++ )
{
const Steps * pSteps = vpStepsToShow [ i ];
RString sDifficulty = CustomDifficultyToLocalizedString ( GetCustomDifficulty ( pSteps -> m_StepsType , pSteps -> GetDifficulty (), CourseType_Invalid ) );
lua_pushstring ( L , sDifficulty );
lua_pushstring ( L , pSteps -> GetDescription () );
}
return vpStepsToShow . size () * 2 ;
}
static int SetPreferredSongGroup ( T * p , lua_State * L ) { p -> m_sPreferredSongGroup . Set ( SArg ( 1 ) ); return 0 ; }
DEFINE_METHOD ( GetPreferredSongGroup , m_sPreferredSongGroup . Get () );
static int GetHumanPlayers ( T * p , lua_State * L )
{
vector < PlayerNumber > vHP ;
FOREACH_HumanPlayer ( pn )
vHP . push_back ( pn );
LuaHelpers :: CreateTableFromArray ( vHP , L );
return 1 ;
}
2012-12-27 12:07:02 -05:00
static int GetEnabledPlayers ( T * , lua_State * L )
2012-01-02 06:07:24 +00:00
{
vector < PlayerNumber > vEP ;
FOREACH_EnabledPlayer ( pn )
vEP . push_back ( pn );
LuaHelpers :: CreateTableFromArray ( vEP , L );
return 1 ;
}
2011-03-17 01:47:30 -04:00
static int GetCurrentStyle ( T * p , lua_State * L )
{
Style * pStyle = const_cast < Style *> ( p -> GetCurrentStyle ());
LuaHelpers :: Push ( L , pStyle );
return 1 ;
}
2012-12-27 12:07:02 -05:00
static int IsAnyHumanPlayerUsingMemoryCard ( T * , lua_State * L )
2011-03-17 01:47:30 -04:00
{
bool bUsingMemoryCard = false ;
FOREACH_HumanPlayer ( pn )
{
if ( MEMCARDMAN -> GetCardState ( pn ) == MemoryCardState_Ready )
bUsingMemoryCard = true ;
}
lua_pushboolean ( L , bUsingMemoryCard );
return 1 ;
}
2012-12-27 12:07:02 -05:00
static int GetNumStagesForCurrentSongAndStepsOrCourse ( T * , lua_State * L )
{
lua_pushnumber ( L , GAMESTATE -> GetNumStagesForCurrentSongAndStepsOrCourse () );
return 1 ;
}
2011-03-17 01:47:30 -04:00
static int GetNumStagesLeft ( T * p , lua_State * L )
{
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 );
lua_pushnumber ( L , p -> GetNumStagesLeft ( pn ));
return 1 ;
}
static int GetGameSeed ( T * p , lua_State * L ) { LuaHelpers :: Push ( L , p -> m_iGameSeed ); return 1 ; }
static int GetStageSeed ( T * p , lua_State * L ) { LuaHelpers :: Push ( L , p -> m_iStageSeed ); return 1 ; }
static int SaveLocalData ( T * p , lua_State * L ) { p -> SaveLocalData (); return 0 ; }
static int SetJukeboxUsesModifiers ( T * p , lua_State * L )
{
p -> m_bJukeboxUsesModifiers = BArg ( 1 ); return 0 ;
}
static int Reset ( T * p , lua_State * L ) { p -> Reset (); return 0 ; }
static int JoinPlayer ( T * p , lua_State * L ) { p -> JoinPlayer ( Enum :: Check < PlayerNumber > ( L , 1 )); return 0 ; }
static int UnjoinPlayer ( T * p , lua_State * L ) { p -> UnjoinPlayer ( Enum :: Check < PlayerNumber > ( L , 1 )); return 0 ; }
2014-02-12 15:45:26 -07:00
static int JoinInput ( T * p , lua_State * L )
{
lua_pushboolean ( L , p -> JoinInput ( Enum :: Check < PlayerNumber > ( L , 1 )));
return 1 ;
}
2011-03-17 01:47:30 -04:00
static int GetSongPercent ( T * p , lua_State * L ) { lua_pushnumber ( L , p -> GetSongPercent ( FArg ( 1 ))); return 1 ; }
2011-05-10 14:53:59 -04:00
DEFINE_METHOD ( GetCurMusicSeconds , m_Position . m_fMusicSeconds )
2011-03-17 01:47:30 -04:00
DEFINE_METHOD ( GetWorkoutGoalComplete , m_bWorkoutGoalComplete )
static int GetCharacter ( T * p , lua_State * L ) { p -> m_pCurCharacters [ Enum :: Check < PlayerNumber > ( L , 1 )] -> PushSelf ( L ); return 1 ; }
static int SetCharacter ( T * p , lua_State * L ){
Character * c = CHARMAN -> GetCharacterFromID ( SArg ( 2 ));
if ( c )
p -> m_pCurCharacters [ Enum :: Check < PlayerNumber > ( L , 1 )] = c ;
return 0 ;
}
2011-08-07 19:08:47 -05:00
static int GetExpandedSectionName ( T * p , lua_State * L ) { lua_pushstring ( L , p -> sExpandedSectionName ); return 1 ; }
2011-09-30 13:33:57 -05:00
static int AddStageToPlayer ( T * p , lua_State * L ) { p -> AddStageToPlayer ( Enum :: Check < PlayerNumber > ( L , 1 )); return 0 ; }
static int CurrentOptionsDisqualifyPlayer ( T * p , lua_State * L ) { lua_pushboolean ( L , p -> CurrentOptionsDisqualifyPlayer ( Enum :: Check < PlayerNumber > ( L , 1 ))); return 1 ; }
2011-10-29 11:43:49 -04:00
static int ResetPlayerOptions ( T * p , lua_State * L )
{
p -> ResetPlayerOptions ( Enum :: Check < PlayerNumber > ( L , 1 ));
return 0 ;
}
2011-11-06 10:36:08 -05:00
static int RefreshNoteSkinData ( T * p , lua_State * L )
{
NOTESKIN -> RefreshNoteSkinData ( p -> m_pCurGame );
return 0 ;
}
2011-03-17 01:47:30 -04:00
static int Dopefish ( T * p , lua_State * L )
{
lua_pushboolean ( L , p -> m_bDopefish );
return 1 ;
}
2014-04-02 18:09:21 -05:00
static int LoadProfiles ( T * p , lua_State * L )
{
bool LoadEdits = true ;
if ( lua_isboolean ( L , 1 ))
{
LoadEdits = BArg ( 1 );
}
p -> LoadProfiles ( LoadEdits );
SCREENMAN -> ZeroNextUpdate ();
return 0 ;
}
static int SaveProfiles ( T * p , lua_State * L )
{
p -> SavePlayerProfiles ();
SCREENMAN -> ZeroNextUpdate ();
return 0 ;
}
DEFINE_METHOD ( HaveProfileToLoad , HaveProfileToLoad () )
DEFINE_METHOD ( HaveProfileToSave , HaveProfileToSave () )
2011-03-17 01:47:30 -04:00
LunaGameState ()
{
ADD_METHOD ( IsPlayerEnabled );
ADD_METHOD ( IsHumanPlayer );
ADD_METHOD ( GetPlayerDisplayName );
ADD_METHOD ( GetMasterPlayerNumber );
ADD_METHOD ( GetMultiplayer );
ADD_METHOD ( SetMultiplayer );
2011-12-12 18:49:10 -05:00
ADD_METHOD ( InStepEditor );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( GetNumMultiplayerNoteFields );
ADD_METHOD ( SetNumMultiplayerNoteFields );
ADD_METHOD ( ShowW1 );
ADD_METHOD ( GetPlayerState );
ADD_METHOD ( GetMultiPlayerState );
ADD_METHOD ( ApplyGameCommand );
ADD_METHOD ( GetCurrentSong );
ADD_METHOD ( SetCurrentSong );
ADD_METHOD ( GetCurrentSteps );
ADD_METHOD ( SetCurrentSteps );
ADD_METHOD ( GetCurrentCourse );
ADD_METHOD ( SetCurrentCourse );
ADD_METHOD ( GetCurrentTrail );
ADD_METHOD ( SetCurrentTrail );
ADD_METHOD ( SetPreferredSong );
ADD_METHOD ( GetPreferredSong );
ADD_METHOD ( SetTemporaryEventMode );
ADD_METHOD ( Env );
ADD_METHOD ( GetEditSourceSteps );
ADD_METHOD ( SetPreferredDifficulty );
ADD_METHOD ( GetPreferredDifficulty );
ADD_METHOD ( AnyPlayerHasRankingFeats );
ADD_METHOD ( IsCourseMode );
ADD_METHOD ( IsBattleMode );
ADD_METHOD ( IsDemonstration );
ADD_METHOD ( GetPlayMode );
ADD_METHOD ( GetSortOrder );
ADD_METHOD ( GetCurrentStageIndex );
ADD_METHOD ( IsGoalComplete );
ADD_METHOD ( PlayerIsUsingModifier );
ADD_METHOD ( GetCourseSongIndex );
ADD_METHOD ( GetLoadingCourseSongIndex );
ADD_METHOD ( GetSmallestNumStagesLeftForAnyHumanPlayer );
ADD_METHOD ( IsAnExtraStage );
ADD_METHOD ( IsExtraStage );
ADD_METHOD ( IsExtraStage2 );
ADD_METHOD ( GetCurrentStage );
ADD_METHOD ( HasEarnedExtraStage );
2011-09-07 23:48:04 -05:00
ADD_METHOD ( GetEarnedExtraStage );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( GetEasiestStepsDifficulty );
ADD_METHOD ( GetHardestStepsDifficulty );
ADD_METHOD ( IsEventMode );
ADD_METHOD ( GetNumPlayersEnabled );
2011-05-15 08:17:32 -05:00
/*ADD_METHOD( GetSongBeat );
2011-03-17 01:47:30 -04:00
ADD_METHOD( GetSongBeatVisible );
ADD_METHOD( GetSongBPS );
ADD_METHOD( GetSongFreeze );
2011-05-15 08:17:32 -05:00
ADD_METHOD( GetSongDelay );*/
2011-05-15 07:53:06 -05:00
ADD_METHOD ( GetSongPosition );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( GetGameplayLeadIn );
2013-06-04 23:47:22 -04:00
ADD_METHOD ( GetCoins );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( IsSideJoined );
2013-06-04 23:47:22 -04:00
ADD_METHOD ( GetCoinsNeededToJoin );
ADD_METHOD ( EnoughCreditsToJoin );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( PlayersCanJoin );
ADD_METHOD ( GetNumSidesJoined );
ADD_METHOD ( GetCoinMode );
ADD_METHOD ( GetPremium );
ADD_METHOD ( GetSongOptionsString );
ADD_METHOD ( GetSongOptions );
ADD_METHOD ( GetDefaultSongOptions );
2011-09-30 13:44:05 -05:00
ADD_METHOD ( ApplyPreferredModifiers );
2011-09-30 14:50:18 -05:00
ADD_METHOD ( ApplyStageModifiers );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( ClearStageModifiersIllegalForCourse );
ADD_METHOD ( SetSongOptions );
2012-03-28 18:55:17 -05:00
ADD_METHOD ( GetStageResult );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( IsWinner );
ADD_METHOD ( IsDraw );
ADD_METHOD ( GetCurrentGame );
//ADD_METHOD( SetCurrentGame );
ADD_METHOD ( GetEditCourseEntryIndex );
ADD_METHOD ( GetEditLocalProfileID );
ADD_METHOD ( GetEditLocalProfile );
ADD_METHOD ( GetCurrentStepsCredits );
ADD_METHOD ( SetPreferredSongGroup );
ADD_METHOD ( GetPreferredSongGroup );
ADD_METHOD ( GetHumanPlayers );
2012-05-27 12:55:57 -05:00
ADD_METHOD ( GetEnabledPlayers );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( GetCurrentStyle );
ADD_METHOD ( IsAnyHumanPlayerUsingMemoryCard );
ADD_METHOD ( GetNumStagesForCurrentSongAndStepsOrCourse );
ADD_METHOD ( GetNumStagesLeft );
ADD_METHOD ( GetGameSeed );
ADD_METHOD ( GetStageSeed );
ADD_METHOD ( SaveLocalData );
ADD_METHOD ( SetJukeboxUsesModifiers );
ADD_METHOD ( GetWorkoutGoalComplete );
ADD_METHOD ( Reset );
ADD_METHOD ( JoinPlayer );
ADD_METHOD ( UnjoinPlayer );
2014-02-12 15:45:26 -07:00
ADD_METHOD ( JoinInput );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( GetSongPercent );
ADD_METHOD ( GetCurMusicSeconds );
ADD_METHOD ( GetCharacter );
ADD_METHOD ( SetCharacter );
2011-08-07 19:08:47 -05:00
ADD_METHOD ( GetExpandedSectionName );
2011-09-21 16:21:23 -06:00
ADD_METHOD ( AddStageToPlayer );
2011-09-30 13:33:57 -05:00
ADD_METHOD ( CurrentOptionsDisqualifyPlayer );
2011-10-29 11:43:49 -04:00
ADD_METHOD ( ResetPlayerOptions );
2011-11-06 10:36:08 -05:00
ADD_METHOD ( RefreshNoteSkinData );
2011-09-30 13:33:57 -05:00
ADD_METHOD ( Dopefish );
2014-04-02 18:09:21 -05:00
ADD_METHOD ( LoadProfiles );
ADD_METHOD ( SaveProfiles );
ADD_METHOD ( HaveProfileToLoad );
ADD_METHOD ( HaveProfileToSave );
2011-03-17 01:47:30 -04:00
}
};
LUA_REGISTER_CLASS ( GameState )
// lua end
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard, Chris Gomez
* All rights reserved.
*
* 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.
*
* 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.
*/