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"
2017-06-06 06:30:06 -06:00
#include "ActorUtil.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"
2014-12-07 01:53:17 -07:00
#include "NoteData.h"
2011-03-17 01:47:30 -04:00
#include "NoteSkinManager.h"
#include "PlayerState.h"
#include "PrefsManager.h"
#include "Profile.h"
#include "ProfileManager.h"
#include "RageFile.h"
2017-06-06 06:30:06 -06:00
#include "RageFileManager.h"
2011-03-17 01:47:30 -04:00
#include "RageLog.h"
#include "RageUtil.h"
#include "Song.h"
#include "SongManager.h"
#include "SongUtil.h"
#include "StatsManager.h"
2014-09-01 19:00:00 -04:00
#include "StepMania.h"
2011-03-17 01:47:30 -04:00
#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 ;
2014-12-07 01:53:17 -07:00
m_pCurStyle . Set ( NULL );
FOREACH_PlayerNumber ( rpn )
{
m_SeparatedStyles [ rpn ] = NULL ;
}
2011-03-17 01:47:30 -04:00
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 ;
2014-12-07 01:53:17 -07:00
m_pPlayerState [ p ] -> SetPlayerNumber ( p );
2011-03-17 01:47:30 -04:00
}
FOREACH_MultiPlayer ( p )
{
m_pMultiPlayerState [ p ] = new PlayerState ;
2014-12-07 01:53:17 -07:00
m_pMultiPlayerState [ p ] -> SetPlayerNumber ( PLAYER_1 );
2011-03-17 01:47:30 -04:00
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 ) )
2014-07-12 14:59:10 -06:00
{
2014-07-15 13:28:06 -06:00
LuaHelpers :: ReportScriptErrorFmt ( "Can't apply GameCommand \" %s \" : %s" , sCommand . c_str (), sWhy . c_str ());
2014-07-12 14:59:10 -06:00
return ;
}
2011-03-17 01:47:30 -04:00
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 ();
2014-12-07 01:53:17 -07:00
SetCurrentStyle ( NULL , PLAYER_INVALID );
2011-03-17 01:47:30 -04:00
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 );
2014-06-20 13:54:56 -06:00
m_bFailTypeWasExplicitlySet = false ;
2011-03-17 01:47:30 -04:00
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 ();
2014-09-07 04:05:10 -06:00
m_AdjustTokensBySongCostForFinalStageCheck = true ;
2011-03-17 01:47:30 -04:00
m_pCurSong . Set ( GetDefaultSong () );
m_pPreferredSong = NULL ;
m_pCurCourse . Set ( NULL );
m_pPreferredCourse = NULL ;
FOREACH_MultiPlayer ( p )
m_pMultiPlayerState [ p ] -> Reset ();
m_SongOptions . Init ();
2015-09-03 14:39:57 -06:00
m_paused = false ;
2011-03-17 01:47:30 -04:00
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 )
{
2015-05-02 23:36:28 -06:00
// Make sure the join will be successful before doing it. -Kyz
{
2015-05-20 22:26:47 -06:00
int players_joined = 0 ;
for ( int i = 0 ; i < NUM_PLAYERS ; ++ i )
{
players_joined += m_bSideIsJoined [ i ];
}
2015-05-02 23:36:28 -06:00
if ( players_joined > 0 )
{
const Style * cur_style = GetCurrentStyle ( PLAYER_INVALID );
if ( cur_style )
{
const Style * new_style = GAMEMAN -> GetFirstCompatibleStyle ( m_pCurGame ,
players_joined + 1 , cur_style -> m_StepsType );
if ( new_style == NULL )
{
return ;
}
}
}
}
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.
2015-01-22 12:37:48 -07:00
// beat gametype's versus styles use a different stepstype from its single
// styles, so when GameCommand tries to join both players for a versus
// style, it hits the assert when joining the first player. So if the first
// player is being joined and the current styletype is for two players,
// assume that the second player will be joined immediately afterwards and
// don't try to change the style. -Kyz
const Style * cur_style = GetCurrentStyle ( PLAYER_INVALID );
2015-05-02 23:36:28 -06:00
if ( cur_style != NULL && ! ( pn == PLAYER_1 &&
2015-01-22 12:37:48 -07:00
( cur_style -> m_StyleType == StyleType_TwoPlayersTwoSides ||
cur_style -> m_StyleType == StyleType_TwoPlayersSharedSides )))
2011-03-17 01:47:30 -04:00
{
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
2015-01-22 12:37:48 -07:00
if ( cur_style -> m_StyleType == StyleType_OnePlayerTwoSides ||
cur_style -> m_StepsType == StepsType_dance_solo ||
cur_style -> m_StepsType == StepsType_dance_threepanel ||
cur_style -> m_StepsType == StepsType_popn_nine )
pStyle = GAMEMAN -> GetFirstCompatibleStyle ( m_pCurGame , 1 , cur_style -> m_StepsType );
2011-03-17 01:47:30 -04:00
else
2015-01-22 12:37:48 -07:00
pStyle = GAMEMAN -> GetFirstCompatibleStyle ( m_pCurGame , GetNumSidesJoined (), cur_style -> m_StepsType );
2011-03-17 01:47:30 -04:00
// use SetCurrentStyle in case of StyleType_OnePlayerTwoSides
2014-12-07 01:53:17 -07:00
SetCurrentStyle ( pStyle , pn );
2011-03-17 01:47:30 -04:00
}
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 );
2017-02-17 08:21:38 -08:00
// On Join, make sure to update Coins File
BOOKKEEPER -> WriteCoinsFile ( GAMESTATE -> m_iCoins . Get ());
2011-03-17 01:47:30 -04:00
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 )
{
/* 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 ) )
2015-01-17 21:15:15 -07:00
{
if ( m_iPlayerStageTokens [ p ] < m_iNumStagesOfThisSong )
{
LuaHelpers :: ReportScriptErrorFmt ( "Player %d only has %d stage tokens, but needs %d." , p , m_iPlayerStageTokens [ p ], m_iNumStagesOfThisSong );
}
}
2011-03-17 01:47:30 -04:00
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 );
}
2014-12-07 01:53:17 -07:00
bool GameState :: CanSafelyEnterGameplay ( RString & reason )
{
if ( ! IsCourseMode ())
{
Song const * song = m_pCurSong ;
if ( song == NULL )
{
reason = "Current song is NULL." ;
return false ;
}
}
else
{
Course const * song = m_pCurCourse ;
if ( song == NULL )
{
reason = "Current course is NULL." ;
return false ;
}
}
FOREACH_EnabledPlayer ( pn )
{
Style const * style = GetCurrentStyle ( pn );
if ( style == NULL )
{
reason = ssprintf ( "Style for player %d is NULL." , pn + 1 );
return false ;
}
if ( ! IsCourseMode ())
{
Steps const * steps = m_pCurSteps [ pn ];
if ( steps == NULL )
{
reason = ssprintf ( "Steps for player %d is NULL." , pn + 1 );
return false ;
}
if ( steps -> m_StepsType != style -> m_StepsType )
{
reason = ssprintf ( "Player %d StepsType %s for steps does not equal "
"StepsType %s for style." , pn + 1 ,
GAMEMAN -> GetStepsTypeInfo ( steps -> m_StepsType ). szName ,
GAMEMAN -> GetStepsTypeInfo ( style -> m_StepsType ). szName );
return false ;
}
if ( steps -> m_pSong != m_pCurSong )
{
reason = ssprintf ( "Steps for player %d are not for the current song." ,
pn + 1 );
return false ;
}
NoteData ndtemp ;
steps -> GetNoteData ( ndtemp );
if ( ndtemp . GetNumTracks () != style -> m_iColsPerPlayer )
{
reason = ssprintf ( "Steps for player %d have %d columns, style has %d "
"columns." , pn + 1 , ndtemp . GetNumTracks (), style -> m_iColsPerPlayer );
return false ;
}
}
else
{
Trail const * steps = m_pCurTrail [ pn ];
if ( steps == NULL )
{
reason = ssprintf ( "Steps for player %d is NULL." , pn + 1 );
return false ;
}
if ( steps -> m_StepsType != style -> m_StepsType )
{
reason = ssprintf ( "Player %d StepsType %s for steps does not equal "
"StepsType %s for style." , pn + 1 ,
GAMEMAN -> GetStepsTypeInfo ( steps -> m_StepsType ). szName ,
GAMEMAN -> GetStepsTypeInfo ( style -> m_StepsType ). szName );
return false ;
}
}
}
return true ;
}
2015-01-17 21:15:15 -07:00
void GameState :: SetCompatibleStylesForPlayers ()
{
bool style_set = false ;
if ( IsCourseMode ())
{
if ( m_pCurCourse != NULL )
{
const Style * style = m_pCurCourse -> GetCourseStyle ( m_pCurGame , GetNumSidesJoined ());
if ( style != NULL )
{
style_set = true ;
SetCurrentStyle ( style , PLAYER_INVALID );
}
}
2015-01-20 18:40:59 -07:00
else if ( GetCurrentStyle ( PLAYER_INVALID ) == NULL )
2015-01-17 21:15:15 -07:00
{
vector < StepsType > vst ;
GAMEMAN -> GetStepsTypesForGame ( m_pCurGame , vst );
const Style * style = GAMEMAN -> GetFirstCompatibleStyle (
m_pCurGame , GetNumSidesJoined (), vst [ 0 ]);
SetCurrentStyle ( style , PLAYER_INVALID );
}
}
if ( ! style_set )
{
2015-01-20 18:40:59 -07:00
FOREACH_EnabledPlayer ( pn )
2015-01-17 21:15:15 -07:00
{
2015-01-20 18:40:59 -07:00
StepsType st = StepsType_Invalid ;
if ( m_pCurSteps [ pn ] != NULL )
2015-01-17 21:15:15 -07:00
{
2015-01-20 18:40:59 -07:00
st = m_pCurSteps [ pn ] -> m_StepsType ;
}
else if ( m_pCurTrail [ pn ] != NULL )
{
st = m_pCurTrail [ pn ] -> m_StepsType ;
}
else
{
vector < StepsType > vst ;
GAMEMAN -> GetStepsTypesForGame ( m_pCurGame , vst );
st = vst [ 0 ];
2015-01-17 21:15:15 -07:00
}
const Style * style = GAMEMAN -> GetFirstCompatibleStyle (
2015-01-20 18:40:59 -07:00
m_pCurGame , GetNumSidesJoined (), st );
SetCurrentStyle ( style , pn );
2015-01-17 21:15:15 -07:00
}
}
}
void GameState :: ForceSharedSidesMatch ()
{
PlayerNumber pn_with_shared = PLAYER_INVALID ;
const Style * shared_style = NULL ;
FOREACH_EnabledPlayer ( pn )
{
const Style * style = GetCurrentStyle ( pn );
ASSERT_M ( style != NULL , "Style being null should not be possible." );
if ( style -> m_StyleType == StyleType_TwoPlayersSharedSides )
{
pn_with_shared = pn ;
shared_style = style ;
}
}
if ( pn_with_shared != PLAYER_INVALID )
{
ASSERT_M ( GetNumPlayersEnabled () == 2 , "2 players must be enabled for shared sides." );
PlayerNumber other_pn = OPPOSITE_PLAYER [ pn_with_shared ];
const Style * other_style = GetCurrentStyle ( other_pn );
ASSERT_M ( other_style != NULL , "Other player's style being null should not be possible." );
if ( other_style -> m_StyleType != StyleType_TwoPlayersSharedSides )
{
SetCurrentStyle ( shared_style , other_pn );
if ( IsCourseMode ())
{
m_pCurTrail [ other_pn ]. Set ( m_pCurTrail [ pn_with_shared ]);
}
else
{
m_pCurSteps [ other_pn ]. Set ( m_pCurSteps [ pn_with_shared ]);
}
}
}
}
2015-03-02 18:44:01 -07:00
void GameState :: ForceOtherPlayersToCompatibleSteps ( PlayerNumber main )
{
if ( IsCourseMode ())
{
Trail * steps_to_match = m_pCurTrail [ main ]. Get ();
if ( steps_to_match == NULL ) { return ; }
int num_players = GAMESTATE -> GetNumPlayersEnabled ();
StyleType styletype_to_match = GAMEMAN -> GetFirstCompatibleStyle (
GAMESTATE -> GetCurrentGame (), num_players , steps_to_match -> m_StepsType )
-> m_StyleType ;
FOREACH_EnabledPlayer ( pn )
{
Trail * pn_steps = m_pCurTrail [ pn ]. Get ();
2015-05-20 23:22:51 -06:00
bool match_failed = pn_steps == NULL ;
2015-03-02 18:44:01 -07:00
if ( steps_to_match != pn_steps && pn_steps != NULL )
{
StyleType pn_styletype = GAMEMAN -> GetFirstCompatibleStyle (
GAMESTATE -> GetCurrentGame (), num_players , pn_steps -> m_StepsType )
-> m_StyleType ;
if ( styletype_to_match == StyleType_TwoPlayersSharedSides ||
pn_styletype == StyleType_TwoPlayersSharedSides )
{
match_failed = true ;
}
2015-05-20 23:22:51 -06:00
}
if ( match_failed )
{
m_pCurTrail [ pn ]. Set ( steps_to_match );
2015-03-02 18:44:01 -07:00
}
}
}
else
{
Steps * steps_to_match = m_pCurSteps [ main ]. Get ();
if ( steps_to_match == NULL ) { return ; }
int num_players = GAMESTATE -> GetNumPlayersEnabled ();
StyleType styletype_to_match = GAMEMAN -> GetFirstCompatibleStyle (
GAMESTATE -> GetCurrentGame (), num_players , steps_to_match -> m_StepsType )
-> m_StyleType ;
RString music_to_match = steps_to_match -> GetMusicFile ();
FOREACH_EnabledPlayer ( pn )
{
Steps * pn_steps = m_pCurSteps [ pn ]. Get ();
2015-05-20 23:22:51 -06:00
bool match_failed = pn_steps == NULL ;
2015-03-02 18:44:01 -07:00
if ( steps_to_match != pn_steps && pn_steps != NULL )
{
StyleType pn_styletype = GAMEMAN -> GetFirstCompatibleStyle (
GAMESTATE -> GetCurrentGame (), num_players , pn_steps -> m_StepsType )
-> m_StyleType ;
if ( styletype_to_match == StyleType_TwoPlayersSharedSides ||
pn_styletype == StyleType_TwoPlayersSharedSides )
{
match_failed = true ;
}
if ( music_to_match != pn_steps -> GetMusicFile ())
{
match_failed = true ;
}
2015-05-20 23:22:51 -06:00
}
if ( match_failed )
{
m_pCurSteps [ pn ]. Set ( steps_to_match );
2015-03-02 18:44:01 -07:00
}
}
}
}
2011-03-17 01:47:30 -04:00
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
{
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 ();
}
2014-07-24 19:45:17 -05:00
void GameState :: UpdateSongPosition ( float fPositionSeconds , const TimingData & timing , const RageTimer & timestamp )
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. */
2015-09-03 14:39:57 -06:00
if ( fPositionSeconds == m_LastPositionSeconds && ! m_paused )
2012-12-17 16:43:31 -08:00
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
2014-07-24 19:45:17 -05:00
FOREACH_EnabledPlayer ( pn )
2011-05-14 18:17:48 -04:00
{
2014-07-24 19:45:17 -05:00
if ( m_pCurSteps [ pn ] )
2011-05-14 18:17:48 -04:00
{
2014-07-24 19:45:17 -05:00
m_pPlayerState [ pn ] -> m_Position . UpdateSongPosition ( fPositionSeconds , * m_pCurSteps [ pn ] -> GetTimingData (), timestamp );
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 ;
}
2014-09-07 04:05:10 -06:00
bool GameState :: IsFinalStageForEveryHumanPlayer () const
{
int song_cost = 1 ;
if ( m_pCurSong != NULL )
{
if ( m_pCurSong -> IsLong ())
{
song_cost = 2 ;
}
else if ( m_pCurSong -> IsMarathon ())
{
song_cost = 3 ;
}
}
// If we're on gameplay or evaluation, they set this to false because those
// screens have already had the stage tokens subtracted.
song_cost *= m_AdjustTokensBySongCostForFinalStageCheck ;
int num_on_final = 0 ;
int num_humans = 0 ;
FOREACH_HumanPlayer ( p )
{
if ( m_iPlayerStageTokens [ p ] - song_cost <= 0 )
{
++ num_on_final ;
}
++ num_humans ;
}
return num_on_final >= num_humans ;
}
2011-03-17 01:47:30 -04:00
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 ;
2014-09-07 04:05:10 -06:00
// Previous logic did not factor in current song length, or the fact that
// players aren't allowed to start a song with 0 tokens. This new
// function also has logic for handling the Gameplay and Evaluation cases
// which used to require workarounds on the theme side. -Kyz
else if ( IsFinalStageForEveryHumanPlayer ()) return Stage_Final ;
2011-03-17 01:47:30 -04:00
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 ;
}
2017-06-06 06:30:06 -06:00
static char const * prepare_song_failures [] = {
"success" ,
"no_current_song" ,
"card_mount_failed" ,
"load_interrupted" ,
};
int GameState :: prepare_song_for_gameplay ()
{
Song * curr = m_pCurSong ;
if ( curr == NULL )
{
return 1 ;
}
if ( curr -> m_LoadedFromProfile == ProfileSlot_Invalid )
{
return 0 ;
}
ProfileSlot prof_slot = curr -> m_LoadedFromProfile ;
PlayerNumber slot_as_pn = PlayerNumber ( prof_slot );
if ( ! PROFILEMAN -> ProfileWasLoadedFromMemoryCard ( slot_as_pn ))
{
return 0 ;
}
if ( ! MEMCARDMAN -> MountCard ( slot_as_pn ))
{
return 2 ;
}
RString prof_dir = PROFILEMAN -> GetProfileDir ( prof_slot );
// Song loading changes its paths to point to the cache area. -Kyz
RString to_dir = curr -> GetSongDir ();
RString from_dir = curr -> GetPreCustomifyDir ();
// The problem of what files to copy is complicated by steps being able to
// specify their own music file, and the variety of step file formats.
// Complex logic to figure out what files the song actually uses would be
// bug prone. Just copy all audio files and step files. -Kyz
vector < RString > copy_exts = ActorUtil :: GetTypeExtensionList ( FT_Sound );
copy_exts . push_back ( "sm" );
copy_exts . push_back ( "ssc" );
copy_exts . push_back ( "lrc" );
vector < RString > files_in_dir ;
FILEMAN -> GetDirListingWithMultipleExtensions ( from_dir , copy_exts , files_in_dir );
for ( size_t i = 0 ; i < files_in_dir . size (); ++ i )
{
RString & fname = files_in_dir [ i ];
if ( ! FileCopy ( from_dir + fname , to_dir + fname ))
{
return 3 ;
}
}
MEMCARDMAN -> UnmountCard ( slot_as_pn );
return 0 ;
}
2011-03-17 01:47:30 -04:00
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
{
2015-05-02 23:36:28 -06:00
if ( GetNumSidesJoined () == 0 )
{
return true ;
}
2015-05-19 21:58:56 -06:00
// If we check the style and it comes up NULL, either the style has not been
// chosen, or we're on ScreenSelectMusic with AutoSetStyle.
// If the style does not come up NULL, we might be on a screen in a custom
// theme that wants to allow joining after the style is set anyway.
// Either way, we can't use the existence of a style to decide.
// -Kyz
2011-03-17 01:47:30 -04:00
if ( ALLOW_LATE_JOIN . IsLoaded () && ALLOW_LATE_JOIN )
{
Screen * pScreen = SCREENMAN -> GetTopScreen ();
2015-05-02 23:36:28 -06:00
if ( pScreen )
{
if ( ! pScreen -> AllowLateJoin ())
{
return false ;
}
}
// We can't use FOREACH_EnabledPlayer because that uses PlayersCanJoin
// in part of its logic chain. -Kyz
FOREACH_PlayerNumber ( pn )
{
const Style * style = GetCurrentStyle ( pn );
if ( style )
{
const Style * compat_style = GAMEMAN -> GetFirstCompatibleStyle (
m_pCurGame , 2 , style -> m_StepsType );
if ( compat_style == NULL )
{
return false ;
}
}
}
2015-05-19 21:58:56 -06:00
return true ;
2011-03-17 01:47:30 -04:00
}
2015-05-19 21:58:56 -06:00
return false ;
2011-03-17 01:47:30 -04:00
}
int GameState :: GetNumSidesJoined () const
{
int iNumSidesJoined = 0 ;
FOREACH_PlayerNumber ( p )
if ( m_bSideIsJoined [ p ] )
iNumSidesJoined ++ ; // left side, and right side
return iNumSidesJoined ;
}
2014-12-07 01:53:17 -07:00
const Game * GameState :: GetCurrentGame () const
2011-03-17 01:47:30 -04:00
{
ASSERT ( m_pCurGame != NULL ); // the game must be set before calling this
return m_pCurGame ;
}
2014-12-07 01:53:17 -07:00
const Style * GameState :: GetCurrentStyle ( PlayerNumber pn ) const
2011-03-17 01:47:30 -04:00
{
2014-12-07 01:53:17 -07:00
if ( GetCurrentGame () == NULL ) { return NULL ; }
if ( ! GetCurrentGame () -> m_PlayersHaveSeparateStyles )
2011-03-17 01:47:30 -04:00
{
2014-12-07 01:53:17 -07:00
return m_pCurStyle ;
}
else
{
if ( pn >= NUM_PLAYERS )
{
return m_SeparatedStyles [ PLAYER_1 ] == NULL ? m_SeparatedStyles [ PLAYER_2 ]
: m_SeparatedStyles [ PLAYER_1 ];
}
return m_SeparatedStyles [ pn ];
2011-03-17 01:47:30 -04:00
}
}
2014-12-07 01:53:17 -07:00
void GameState :: SetCurrentStyle ( const Style * style , PlayerNumber pn )
{
if ( ! GetCurrentGame () -> m_PlayersHaveSeparateStyles )
{
m_pCurStyle . Set ( style );
}
else
{
if ( pn == PLAYER_INVALID )
{
FOREACH_PlayerNumber ( rpn )
{
m_SeparatedStyles [ rpn ] = style ;
}
}
2015-01-17 21:15:15 -07:00
else
{
m_SeparatedStyles [ pn ] = style ;
}
2014-12-07 01:53:17 -07:00
}
if ( INPUTMAPPER )
{
if ( GetCurrentStyle ( pn ) && GetCurrentStyle ( pn ) -> m_StyleType == StyleType_OnePlayerTwoSides )
2015-02-08 10:33:51 -07:00
{
// If the other player is joined, unjoin them because this style only
// allows one player.
PlayerNumber other_pn = OPPOSITE_PLAYER [ this -> GetMasterPlayerNumber ()];
if ( GetNumSidesJoined () > 1 )
{
UnjoinPlayer ( other_pn );
}
2014-12-07 01:53:17 -07:00
INPUTMAPPER -> SetJoinControllers ( this -> GetMasterPlayerNumber ());
2015-02-08 10:33:51 -07:00
}
2014-12-07 01:53:17 -07:00
else
INPUTMAPPER -> SetJoinControllers ( PLAYER_INVALID );
}
}
bool GameState :: SetCompatibleStyle ( StepsType stype , PlayerNumber pn )
2014-06-27 20:20:50 -06:00
{
2014-06-27 21:11:34 -06:00
bool style_incompatible = false ;
2014-12-07 01:53:17 -07:00
if ( ! GetCurrentStyle ( pn ))
2014-06-27 21:11:34 -06:00
{
style_incompatible = true ;
}
else
{
2014-12-07 01:53:17 -07:00
style_incompatible = stype != GetCurrentStyle ( pn ) -> m_StepsType ;
2014-06-27 21:11:34 -06:00
}
if ( CommonMetrics :: AUTO_SET_STYLE && style_incompatible )
2014-06-27 20:20:50 -06:00
{
const Style * compatible_style = GAMEMAN -> GetFirstCompatibleStyle (
m_pCurGame , GetNumSidesJoined (), stype );
if ( ! compatible_style )
{
return false ;
}
2014-12-07 01:53:17 -07:00
SetCurrentStyle ( compatible_style , pn );
2014-06-27 20:20:50 -06:00
}
2014-12-07 01:53:17 -07:00
return stype == GetCurrentStyle ( pn ) -> m_StepsType ;
2014-06-27 20:20:50 -06:00
}
2011-03-17 01:47:30 -04:00
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 ;
2015-01-17 21:15:15 -07:00
if ( GetCurrentGame () -> m_PlayersHaveSeparateStyles )
{
if ( GetCurrentStyle ( pn ) == NULL ) // no style chosen
{
return m_bSideIsJoined [ pn ];
}
else
{
StyleType type = GetCurrentStyle ( pn ) -> m_StyleType ;
switch ( type )
{
case StyleType_TwoPlayersTwoSides :
case StyleType_TwoPlayersSharedSides :
return true ;
case StyleType_OnePlayerOneSide :
case StyleType_OnePlayerTwoSides :
return pn == this -> GetMasterPlayerNumber ();
default :
FAIL_M ( ssprintf ( "Invalid style type: %i" , type ));
}
}
}
2014-12-07 01:53:17 -07:00
if ( GetCurrentStyle ( pn ) == NULL ) // no style chosen
2011-03-17 01:47:30 -04:00
{
2015-05-19 21:58:56 -06:00
return m_bSideIsJoined [ pn ]; // only allow input from sides that have already joined
2011-03-17 01:47:30 -04:00
}
2014-12-07 01:53:17 -07:00
StyleType type = GetCurrentStyle ( pn ) -> m_StyleType ;
2012-12-27 16:59:35 -05:00
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 );
}
2014-05-01 23:23:20 -06:00
FailType GameState :: GetPlayerFailType ( const PlayerState * pPlayerState ) const
2011-03-17 01:47:30 -04:00
{
PlayerNumber pn = pPlayerState -> m_PlayerNumber ;
2014-05-01 23:23:20 -06:00
FailType ft = pPlayerState -> m_PlayerOptions . GetCurrent (). m_FailType ;
2011-03-17 01:47:30 -04:00
// If the player changed the fail mode explicitly, leave it alone.
2014-06-20 13:54:56 -06:00
if ( m_bFailTypeWasExplicitlySet )
2011-03-17 01:47:30 -04:00
return ft ;
if ( IsCourseMode () )
{
if ( PREFSMAN -> m_bMinimum1FullSongInCourses && GetCourseSongIndex () == 0 )
2014-05-01 23:23:20 -06:00
ft = max ( ft , FailType_ImmediateContinue ); // take the least harsh of the two FailTypes
2011-03-17 01:47:30 -04:00
}
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 )
2014-05-01 23:23:20 -06:00
setmax ( ft , FailType_ImmediateContinue );
2011-03-17 01:47:30 -04:00
if ( dc <= Difficulty_Easy && bFirstStage && PREFSMAN -> m_bFailOffForFirstStageEasy )
2014-05-01 23:23:20 -06:00
setmax ( ft , FailType_Off );
2011-03-17 01:47:30 -04:00
/* If beginner's steps were chosen, and this is the first stage,
* turn off failure completely. */
if ( dc == Difficulty_Beginner && bFirstStage )
2014-05-01 23:23:20 -06:00
setmax ( ft , FailType_Off );
2011-03-17 01:47:30 -04:00
if ( dc == Difficulty_Beginner && PREFSMAN -> m_bFailOffInBeginner )
2014-05-01 23:23:20 -06:00
setmax ( ft , FailType_Off );
2011-03-17 01:47:30 -04:00
}
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.
2015-06-08 18:37:13 -04:00
PlayMode mode = m_PlayMode . Get ();
char const * modeStr = PlayModeToString ( mode ). c_str ();
CHECKPOINT_M ( ssprintf ( "Getting the feats for %s" , modeStr ));
switch ( mode )
2011-03-17 01:47:30 -04:00
{
case PLAY_MODE_REGULAR :
case PLAY_MODE_BATTLE :
case PLAY_MODE_RAVE :
{
2014-12-07 01:53:17 -07:00
StepsType st = GetCurrentStyle ( pn ) -> m_StepsType ;
2011-03-17 01:47:30 -04:00
// 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 );
}
2015-06-08 18:37:13 -04:00
CHECKPOINT_M ( ssprintf ( "All songs/steps from %s gathered" , modeStr ));
2011-03-17 01:47:30 -04:00
sort ( vSongAndSteps . begin (), vSongAndSteps . end () );
vector < SongAndSteps >:: iterator toDelete = unique ( vSongAndSteps . begin (), vSongAndSteps . end () );
vSongAndSteps . erase ( toDelete , vSongAndSteps . end ());
2015-06-08 18:37:13 -04:00
CHECKPOINT_M ( "About to find records from the gathered." );
2011-03-17 01:47:30 -04:00
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 );
}
}
}
2015-06-08 18:37:13 -04:00
CHECKPOINT_M ( "Getting the final evaluation stage stats." );
2011-03-17 01:47:30 -04:00
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 :
{
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 )
{
2014-05-12 22:01:22 -06:00
// The theme can upper it if desired. -Kyz
// sName.MakeUpper();
2011-03-17 01:47:30 -04:00
if ( USE_NAME_BLACKLIST )
{
RageFile file ;
if ( file . Open ( NAME_BLACKLIST_FILE ) )
{
RString sLine ;
while ( ! file . AtEOF () )
{
if ( file . GetLine ( sLine ) == - 1 )
{
2014-07-07 00:39:27 -06:00
LuaHelpers :: ReportScriptErrorFmt ( "Error reading \" %s \" : %s" , NAME_BLACKLIST_FILE , file . GetError (). c_str () );
2011-03-17 01:47:30 -04:00
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 ;
2014-12-07 01:53:17 -07:00
if ( GetCurrentStyle ( PLAYER_INVALID ) -> m_bLockDifficulties )
2011-03-17 01:47:30 -04:00
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 );
2016-03-27 21:52:05 -04:00
for (;;)
2011-03-17 01:47:30 -04:00
{
d = enum_add2 ( d , dir );
if ( d < 0 || d >= NUM_Difficulty )
2016-03-27 21:52:05 -04:00
{
2011-03-17 01:47:30 -04:00
return false ;
2016-03-27 21:52:05 -04:00
}
2011-03-17 01:47:30 -04:00
if ( find ( v . begin (), v . end (), d ) != v . end () )
2016-03-27 21:52:05 -04:00
{
2011-03-17 01:47:30 -04:00
break ; // found
2016-03-27 21:52:05 -04:00
}
2011-03-17 01:47:30 -04:00
}
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 ];
2016-03-27 21:52:05 -04:00
for (;;)
2011-03-17 01:47:30 -04:00
{
cd = enum_add2 ( cd , dir );
if ( cd < 0 || cd >= NUM_Difficulty )
2016-03-27 21:52:05 -04:00
{
2011-03-17 01:47:30 -04:00
return false ;
2016-03-27 21:52:05 -04:00
}
2011-03-17 01:47:30 -04:00
if ( find ( v . begin (), v . end (), cd ) == v . end () )
2016-03-27 21:52:05 -04:00
{
2011-03-17 01:47:30 -04:00
continue ; /* not available */
2016-03-27 21:52:05 -04:00
}
2014-12-07 01:53:17 -07:00
if ( ! pCourse || pCourse -> GetTrail ( GetCurrentStyle ( pn ) -> m_StepsType , cd ) )
2016-03-27 21:52:05 -04:00
{
2011-03-17 01:47:30 -04:00
break ;
2016-03-27 21:52:05 -04:00
}
2011-03-17 01:47:30 -04:00
}
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 )
{
2014-07-07 00:39:27 -06:00
LuaHelpers :: ReportScriptErrorFmt ( "GetEasiestStepsDifficulty called but p%i hasn't chosen notes" , p + 1 );
2011-03-17 01:47:30 -04:00
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 )
{
2014-07-07 00:39:27 -06:00
LuaHelpers :: ReportScriptErrorFmt ( "GetHardestStepsDifficulty called but p%i hasn't chosen notes" , p + 1 );
2011-03-17 01:47:30 -04:00
continue ;
}
dc = max ( dc , m_pCurSteps [ p ] -> GetDifficulty () );
}
return dc ;
}
2014-09-10 02:44:49 -06:00
void GameState :: SetNewStageSeed ()
{
m_iStageSeed = rand ();
}
2011-03-17 01:47:30 -04:00
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"
2014-09-01 19:00:00 -04:00
/** @brief Allow Lua to have access to the GameState. */
2011-03-17 01:47:30 -04:00
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 );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 ); }
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
2014-12-07 01:53:17 -07:00
static int CanSafelyEnterGameplay ( T * p , lua_State * L )
2014-06-27 20:20:50 -06:00
{
2014-12-07 01:53:17 -07:00
RString reason ;
bool can = p -> CanSafelyEnterGameplay ( reason );
lua_pushboolean ( L , can );
LuaHelpers :: Push ( L , reason );
return 2 ;
}
static void SetCompatibleStyleOrError ( T * p , lua_State * L , StepsType stype , PlayerNumber pn )
{
if ( ! p -> SetCompatibleStyle ( stype , pn ))
2014-06-27 20:20:50 -06:00
{
luaL_error ( L , "No compatible style for steps/trail." );
}
2014-12-07 01:53:17 -07:00
if ( ! p -> GetCurrentStyle ( pn ))
2014-06-27 20:20:50 -06:00
{
luaL_error ( L , "No style set and AutoSetStyle is false, cannot set steps/trail." );
}
}
2011-03-17 01:47:30 -04:00
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 );
2014-06-27 20:20:50 -06:00
if ( lua_isnil ( L , 2 ))
{
p -> m_pCurSteps [ pn ]. Set ( NULL );
}
else
{
Steps * pS = Luna < Steps >:: check ( L , 2 );
2014-12-07 01:53:17 -07:00
SetCompatibleStyleOrError ( p , L , pS -> m_StepsType , pn );
2014-06-27 20:20:50 -06:00
p -> m_pCurSteps [ pn ]. Set ( pS );
2015-03-02 18:44:01 -07:00
p -> ForceOtherPlayersToCompatibleSteps ( pn );
2014-06-27 20:20:50 -06:00
}
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 ); }
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 );
2014-06-27 20:20:50 -06:00
if ( lua_isnil ( L , 2 ))
{
p -> m_pCurTrail [ pn ]. Set ( NULL );
}
else
{
Trail * pS = Luna < Trail >:: check ( L , 2 );
2014-12-07 01:53:17 -07:00
SetCompatibleStyleOrError ( p , L , pS -> m_StepsType , pn );
2014-06-27 20:20:50 -06:00
p -> m_pCurTrail [ pn ]. Set ( pS );
2015-03-02 18:44:01 -07:00
p -> ForceOtherPlayersToCompatibleSteps ( pn );
2014-06-27 20:20:50 -06:00
}
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 ; }
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
2014-12-01 22:31:30 -07:00
static int SetTemporaryEventMode ( T * p , lua_State * L ) { p -> m_bTemporaryEventMode = BArg ( 1 ); COMMON_RETURN_SELF ; }
2011-03-17 01:47:30 -04:00
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 );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 ;
}
2014-09-12 15:07:46 -06:00
DEFINE_METHOD ( GetLastGameplayDuration , m_DanceDuration )
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 ;
}
2014-05-03 19:27:32 -06:00
static int GetSongOptionsObject ( T * p , lua_State * L )
{
ModsLevel m = Enum :: Check < ModsLevel > ( L , 1 );
p -> m_SongOptions . Get ( m ). PushSelf ( L );
return 1 ;
}
2011-03-17 01:47:30 -04:00
static int GetDefaultSongOptions ( T * p , lua_State * L )
{
SongOptions so ;
p -> GetDefaultSongOptions ( so );
lua_pushstring ( L , so . GetString ());
return 1 ;
}
2014-12-09 21:59:01 -07:00
static int ApplyPreferredSongOptionsToOtherLevels ( T * p , lua_State * L )
{
p -> m_SongOptions . Assign ( ModsLevel_Preferred ,
p -> m_SongOptions . Get ( ModsLevel_Preferred ));
return 0 ;
}
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 ) );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-09-30 14:50:18 -05:00
}
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 ) );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-09-30 13:44:05 -05:00
}
2011-03-17 01:47:30 -04:00
static int ClearStageModifiersIllegalForCourse ( T * p , lua_State * L )
{
p -> ClearStageModifiersIllegalForCourse ();
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
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 ; }
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 ;
}
2014-12-01 22:31:30 -07:00
static int SetPreferredSongGroup ( T * p , lua_State * L ) { p -> m_sPreferredSongGroup . Set ( SArg ( 1 ) ); COMMON_RETURN_SELF ; }
2011-03-17 01:47:30 -04:00
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 ;
}
2014-04-15 12:09:03 -05:00
static int GetEnabledPlayers ( T * , lua_State * L )
{
vector < PlayerNumber > vEP ;
FOREACH_EnabledPlayer ( pn )
vEP . push_back ( pn );
LuaHelpers :: CreateTableFromArray ( vEP , L );
return 1 ;
2014-09-01 19:00:00 -04:00
}
2011-03-17 01:47:30 -04:00
static int GetCurrentStyle ( T * p , lua_State * L )
{
2014-12-07 01:53:17 -07:00
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 1 , true , true );
Style * pStyle = const_cast < Style *> ( p -> GetCurrentStyle ( pn ));
2011-03-17 01:47:30 -04:00
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 )
2014-09-01 19:00:00 -04:00
{
2012-12-27 12:07:02 -05:00
lua_pushnumber ( L , GAMESTATE -> GetNumStagesForCurrentSongAndStepsOrCourse () );
2014-09-01 19:00:00 -04:00
return 1 ;
2012-12-27 12:07:02 -05:00
}
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 ; }
2014-12-01 22:31:30 -07:00
static int SaveLocalData ( T * p , lua_State * L ) { p -> SaveLocalData (); COMMON_RETURN_SELF ; }
2011-03-17 01:47:30 -04:00
static int SetJukeboxUsesModifiers ( T * p , lua_State * L )
{
2014-12-01 22:31:30 -07:00
p -> m_bJukeboxUsesModifiers = BArg ( 1 ); COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
2014-12-01 22:31:30 -07:00
static int Reset ( T * p , lua_State * L ) { p -> Reset (); COMMON_RETURN_SELF ; }
static int JoinPlayer ( T * p , lua_State * L ) { p -> JoinPlayer ( Enum :: Check < PlayerNumber > ( L , 1 )); COMMON_RETURN_SELF ; }
static int UnjoinPlayer ( T * p , lua_State * L ) { p -> UnjoinPlayer ( Enum :: Check < PlayerNumber > ( L , 1 )); COMMON_RETURN_SELF ; }
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 ;
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-03-17 01:47:30 -04:00
}
2011-08-07 19:08:47 -05:00
static int GetExpandedSectionName ( T * p , lua_State * L ) { lua_pushstring ( L , p -> sExpandedSectionName ); return 1 ; }
2014-12-01 22:31:30 -07:00
static int AddStageToPlayer ( T * p , lua_State * L ) { p -> AddStageToPlayer ( Enum :: Check < PlayerNumber > ( L , 1 )); COMMON_RETURN_SELF ; }
2014-09-01 19:00:00 -04:00
static int InsertCoin ( T * p , lua_State * L )
{
2014-09-01 20:03:45 -04:00
int numCoins = IArg ( 1 );
if ( GAMESTATE -> m_iCoins + numCoins >= 0 )
2014-09-01 20:26:55 -04:00
{
2014-09-01 20:03:45 -04:00
StepMania :: InsertCoin ( numCoins , false );
2014-09-01 20:26:55 -04:00
} else {
// Warn themers if they attempt to set credits to a negative value.
2014-09-01 20:03:45 -04:00
luaL_error ( L , "Credits may not be negative." );
2014-09-01 20:26:55 -04:00
}
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-09-01 19:00:00 -04:00
}
static int InsertCredit ( T * p , lua_State * L )
{
StepMania :: InsertCredit ();
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-09-01 19:00:00 -04:00
}
2011-09-30 13:33:57 -05:00
static int CurrentOptionsDisqualifyPlayer ( T * p , lua_State * L ) { lua_pushboolean ( L , p -> CurrentOptionsDisqualifyPlayer ( Enum :: Check < PlayerNumber > ( L , 1 ))); return 1 ; }
2014-09-01 19:00:00 -04:00
2011-10-29 11:43:49 -04:00
static int ResetPlayerOptions ( T * p , lua_State * L )
{
p -> ResetPlayerOptions ( Enum :: Check < PlayerNumber > ( L , 1 ));
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-10-29 11:43:49 -04:00
}
2014-09-01 19:00:00 -04:00
2011-11-06 10:36:08 -05:00
static int RefreshNoteSkinData ( T * p , lua_State * L )
{
NOTESKIN -> RefreshNoteSkinData ( p -> m_pCurGame );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2011-11-06 10:36:08 -05:00
}
2014-09-01 19:00:00 -04:00
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-15 12:09:03 -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 ();
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-04-15 12:09:03 -05:00
}
2014-09-01 19:00:00 -04:00
2014-04-15 12:09:03 -05:00
static int SaveProfiles ( T * p , lua_State * L )
{
p -> SavePlayerProfiles ();
SCREENMAN -> ZeroNextUpdate ();
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-04-15 12:09:03 -05:00
}
2014-04-02 18:09:21 -05:00
2014-06-20 13:54:56 -06:00
static int SetFailTypeExplicitlySet ( T * p , lua_State * L )
{
p -> m_bFailTypeWasExplicitlySet = true ;
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-06-20 13:54:56 -06:00
}
2014-05-12 22:01:22 -06:00
static int StoreRankingName ( T * p , lua_State * L )
{
p -> StoreRankingName ( Enum :: Check < PlayerNumber > ( L , 1 ), SArg ( 2 ));
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-05-12 22:01:22 -06:00
}
2014-04-02 18:09:21 -05:00
DEFINE_METHOD ( HaveProfileToLoad , HaveProfileToLoad () )
DEFINE_METHOD ( HaveProfileToSave , HaveProfileToSave () )
2014-08-13 22:04:54 -05:00
static bool AreStyleAndPlayModeCompatible ( T * p , lua_State * L , const Style * style , PlayMode pm )
{
if ( pm != PLAY_MODE_BATTLE && pm != PLAY_MODE_RAVE )
{
return true ;
}
// Do not allow styles with StepsTypes with shared sides or that are one player only with Battle or Rave.
if ( style -> m_StyleType != StyleType_TwoPlayersSharedSides )
{
vector < const Style *> vpStyles ;
GAMEMAN -> GetCompatibleStyles ( p -> m_pCurGame , 2 , vpStyles );
FOREACH_CONST ( const Style * , vpStyles , s )
{
if ( ( * s ) -> m_StepsType == style -> m_StepsType )
{
return true ;
}
}
}
luaL_error ( L , "Style %s is incompatible with PlayMode %s" ,
style -> m_szName , PlayModeToString ( pm ). c_str () );
return false ;
}
static void ClearIncompatibleStepsAndTrails ( T * p , lua_State * L )
{
FOREACH_HumanPlayer ( pn )
{
2014-12-07 01:53:17 -07:00
const Style * style = p -> GetCurrentStyle ( pn );
2014-08-13 22:04:54 -05:00
if ( p -> m_pCurSteps [ pn ] && ( ! style || style -> m_StepsType != p -> m_pCurSteps [ pn ] -> m_StepsType ) )
{
p -> m_pCurSteps [ pn ]. Set ( NULL );
}
if ( p -> m_pCurTrail [ pn ] && ( ! style || style -> m_StepsType != p -> m_pCurTrail [ pn ] -> m_StepsType ) )
{
p -> m_pCurTrail [ pn ]. Set ( NULL );
}
}
}
static int SetCurrentStyle ( T * p , lua_State * L )
{
const Style * pStyle = NULL ;
if ( lua_isstring ( L , 1 ) )
{
RString style = SArg ( 1 );
pStyle = GAMEMAN -> GameAndStringToStyle ( GAMESTATE -> m_pCurGame , style );
if ( ! pStyle )
{
luaL_error ( L , "SetCurrentStyle: %s is not a valid style." , style . c_str () );
}
}
else
{
pStyle = Luna < Style >:: check ( L , 1 );
}
StyleType st = pStyle -> m_StyleType ;
2014-09-01 19:00:00 -04:00
if ( p -> GetNumSidesJoined () == 2 &&
2014-08-13 22:04:54 -05:00
( st == StyleType_OnePlayerOneSide || st == StyleType_OnePlayerTwoSides ) )
{
luaL_error ( L , "Too many sides joined for style %s" , pStyle -> m_szName );
}
2014-09-01 19:00:00 -04:00
else if ( p -> GetNumSidesJoined () == 1 &&
2014-08-13 22:04:54 -05:00
( st == StyleType_TwoPlayersTwoSides || st == StyleType_TwoPlayersSharedSides ) )
{
luaL_error ( L , "Too few sides joined for style %s" , pStyle -> m_szName );
}
if ( ! AreStyleAndPlayModeCompatible ( p , L , pStyle , p -> m_PlayMode ) )
2014-09-01 19:00:00 -04:00
{
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-08-13 22:04:54 -05:00
}
2014-12-07 01:53:17 -07:00
PlayerNumber pn = Enum :: Check < PlayerNumber > ( L , 2 , true , true );
2014-09-01 19:00:00 -04:00
2014-12-07 01:53:17 -07:00
p -> SetCurrentStyle ( pStyle , pn );
2014-08-13 22:04:54 -05:00
ClearIncompatibleStepsAndTrails ( p , L );
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-08-13 22:04:54 -05:00
}
static int SetCurrentPlayMode ( T * p , lua_State * L )
{
PlayMode pm = Enum :: Check < PlayMode > ( L , 1 );
2014-12-07 01:53:17 -07:00
if ( AreStyleAndPlayModeCompatible ( p , L , p -> GetCurrentStyle ( PLAYER_INVALID ), pm ) )
2014-08-13 22:04:54 -05:00
{
p -> m_PlayMode . Set ( pm );
}
2014-12-01 22:31:30 -07:00
COMMON_RETURN_SELF ;
2014-08-13 22:04:54 -05:00
}
2014-12-08 00:55:57 -07:00
static int SetStepsForEditMode ( T * p , lua_State * L )
{
// Arg forms:
// 1. Edit existing steps:
// song, steps
// 2. Create new steps to edit:
// song, nil, stepstype, difficulty
// 3. Copy steps to new difficulty to edit:
// song, steps, stepstype, difficulty
Song * song = Luna < Song >:: check ( L , 1 );
Steps * steps = NULL ;
if ( ! lua_isnil ( L , 2 ))
{
steps = Luna < Steps >:: check ( L , 2 );
}
// Form 1.
if ( steps != NULL && lua_gettop ( L ) == 2 )
{
p -> m_pCurSong . Set ( song );
p -> m_pCurSteps [ PLAYER_1 ]. Set ( steps );
p -> SetCurrentStyle ( GAMEMAN -> GetEditorStyleForStepsType (
steps -> m_StepsType ), PLAYER_INVALID );
p -> m_pCurCourse . Set ( NULL );
return 0 ;
}
StepsType stype = Enum :: Check < StepsType > ( L , 3 );
Difficulty diff = Enum :: Check < Difficulty > ( L , 4 );
Steps * new_steps = song -> CreateSteps ();
RString edit_name ;
// Form 2.
if ( steps == NULL )
{
new_steps -> CreateBlank ( stype );
new_steps -> SetMeter ( 1 );
edit_name = "" ;
}
// Form 3.
else
{
new_steps -> CopyFrom ( steps , stype , song -> m_fMusicLengthSeconds );
edit_name = steps -> GetDescription ();
}
SongUtil :: MakeUniqueEditDescription ( song , stype , edit_name );
steps -> SetDescription ( edit_name );
song -> AddSteps ( new_steps );
p -> m_pCurSong . Set ( song );
p -> m_pCurSteps [ PLAYER_1 ]. Set ( steps );
p -> SetCurrentStyle ( GAMEMAN -> GetEditorStyleForStepsType (
steps -> m_StepsType ), PLAYER_INVALID );
p -> m_pCurCourse . Set ( NULL );
return 0 ;
}
2015-01-26 19:43:15 -07:00
static int GetAutoGenFarg ( T * p , lua_State * L )
{
int i = IArg ( 1 ) - 1 ;
if ( i < 0 ) { lua_pushnil ( L ); return 1 ; }
size_t si = static_cast < size_t > ( i );
if ( si >= p -> m_autogen_fargs . size ()) { lua_pushnil ( L ); return 1 ; }
lua_pushnumber ( L , p -> GetAutoGenFarg ( si ));
return 1 ;
}
static int SetAutoGenFarg ( T * p , lua_State * L )
{
int i = IArg ( 1 ) - 1 ;
if ( i < 0 )
{
luaL_error ( L , "%i is not a valid autogen arg index." , i );
}
float v = FArg ( 2 );
size_t si = static_cast < size_t > ( i );
while ( si >= p -> m_autogen_fargs . size ())
{
p -> m_autogen_fargs . push_back ( 0.0f );
}
p -> m_autogen_fargs [ si ] = v ;
COMMON_RETURN_SELF ;
}
2017-06-06 06:30:06 -06:00
static int prepare_song_for_gameplay ( T * p , lua_State * L )
{
int result = p -> prepare_song_for_gameplay ();
lua_pushstring ( L , prepare_song_failures [ result ]);
return 1 ;
}
2015-01-26 19:43:15 -07:00
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 );
2014-12-07 01:53:17 -07:00
ADD_METHOD ( CanSafelyEnterGameplay );
2011-03-17 01:47:30 -04:00
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 );
2014-09-12 15:07:46 -06:00
ADD_METHOD ( GetLastGameplayDuration );
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 );
2014-05-03 19:27:32 -06:00
ADD_METHOD ( GetSongOptionsObject );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( GetDefaultSongOptions );
2014-12-09 21:59:01 -07:00
ADD_METHOD ( ApplyPreferredSongOptionsToOtherLevels );
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 ( 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 );
2014-09-01 19:00:00 -04:00
ADD_METHOD ( InsertCoin );
ADD_METHOD ( InsertCredit );
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 );
2014-06-20 13:54:56 -06:00
ADD_METHOD ( SetFailTypeExplicitlySet );
2014-05-12 22:01:22 -06:00
ADD_METHOD ( StoreRankingName );
2014-08-13 22:04:54 -05:00
ADD_METHOD ( SetCurrentStyle );
ADD_METHOD ( SetCurrentPlayMode );
2014-12-08 00:55:57 -07:00
ADD_METHOD ( SetStepsForEditMode );
2015-01-26 19:43:15 -07:00
ADD_METHOD ( GetAutoGenFarg );
ADD_METHOD ( SetAutoGenFarg );
2017-06-06 06:30:06 -06:00
ADD_METHOD ( prepare_song_for_gameplay );
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.
*/