Files
itgmania212121/stepmania/src/GameState.cpp
T

2010 lines
56 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-07-23 01:41:40 +00:00
#include "GameState.h"
#include "IniFile.h"
#include "GameManager.h"
#include "PrefsManager.h"
#include "InputMapper.h"
2003-02-16 04:28:17 +00:00
#include "song.h"
2003-10-14 01:23:16 +00:00
#include "Course.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "SongManager.h"
2003-08-03 00:13:55 +00:00
#include "Steps.h"
#include "NoteSkinManager.h"
2004-12-02 06:29:20 +00:00
#include "GameCommand.h"
2003-04-02 21:57:05 +00:00
#include "NoteFieldPositioning.h"
#include "Character.h"
2005-02-21 06:22:46 +00:00
#include "UnlockManager.h"
2003-07-10 03:36:41 +00:00
#include "AnnouncerManager.h"
#include "ProfileManager.h"
2003-07-22 07:47:27 +00:00
#include "arch/arch.h"
#include "ThemeManager.h"
2003-11-16 04:45:12 +00:00
#include "LightsManager.h"
#include "RageFile.h"
#include "Bookkeeper.h"
#include "MemoryCardManager.h"
2005-02-16 03:25:45 +00:00
#include "StatsManager.h"
2004-03-13 22:18:09 +00:00
#include "GameConstantsAndTypes.h"
2004-04-30 07:48:02 +00:00
#include "StepMania.h"
#include "CommonMetrics.h"
2004-10-28 02:17:50 +00:00
#include "Actor.h"
#include "PlayerState.h"
2005-01-15 02:01:26 +00:00
#include "Style.h"
2004-04-30 07:48:02 +00:00
#include <ctime>
#include <set>
2002-07-23 01:41:40 +00:00
GameState* GAMESTATE = NULL; // global and accessable from anywhere in our program
2003-12-10 09:26:05 +00:00
#define CHARACTERS_DIR "Characters/"
#define NAMES_BLACKLIST_FILE "Data/NamesBlacklist.dat"
2002-07-23 01:41:40 +00:00
GameState::GameState()
{
m_pCurStyle = NULL;
2004-01-21 04:59:15 +00:00
m_pCurGame = NULL;
2003-01-19 04:44:22 +00:00
m_iCoins = 0;
2004-02-22 23:26:46 +00:00
m_timeGameStarted.SetZero();
m_bIsOnSystemMenu = false;
ReloadCharacters();
2003-12-28 19:46:50 +00:00
2004-03-20 19:15:06 +00:00
m_iNumTimesThroughAttract = -1; // initial screen will bump this up to 0
2004-08-06 21:01:28 +00:00
m_iRoundSeed = m_iGameSeed = 0;
2004-01-21 04:59:15 +00:00
m_PlayMode = PLAY_MODE_INVALID; // used by IsPlayerEnabled before the first screen
FOREACH_PlayerNumber( p )
m_bSideIsJoined[p] = false; // used by GetNumSidesJoined before the first screen
FOREACH_PlayerNumber( p )
{
m_pPlayerState[p] = new PlayerState;
m_pPlayerState[p]->m_PlayerNumber = p;
}
2005-01-31 02:00:00 +00:00
2004-01-21 04:59:15 +00:00
/* Don't reset yet; let the first screen do it, so we can
* use PREFSMAN and THEME. */
// Reset();
2002-07-23 01:41:40 +00:00
}
GameState::~GameState()
{
2004-01-21 11:28:31 +00:00
for( unsigned i=0; i<m_pCharacters.size(); i++ )
SAFE_DELETE( m_pCharacters[i] );
FOREACH_PlayerNumber( p )
SAFE_DELETE( m_pPlayerState[p] );
2002-07-23 01:41:40 +00:00
}
void GameState::ApplyGameCommand( const CString &sCommand, PlayerNumber pn )
{
GameCommand m;
m.Load( 0, ParseCommands(sCommand) );
CString sWhy;
if( !m.IsPlayable(&sWhy) )
RageException::Throw( "Can't apply mode \"%s\": %s", sCommand.c_str(), sWhy.c_str() );
if( pn == PLAYER_INVALID )
m.ApplyToAllPlayers();
else
m.Apply( pn );
}
2004-04-30 07:48:02 +00:00
void GameState::ApplyCmdline()
{
2004-05-03 20:14:52 +00:00
/* We need to join players before we can set the style. */
2004-04-30 07:48:02 +00:00
CString sPlayer;
2004-09-21 07:53:39 +00:00
for( int i = 0; GetCommandlineArgument( "player", &sPlayer, i ); ++i )
2004-04-30 07:48:02 +00:00
{
int pn = atoi( sPlayer )-1;
if( !IsAnInt( sPlayer ) || pn < 0 || pn >= NUM_PLAYERS )
RageException::Throw( "Invalid argument \"--player=%s\"", sPlayer.c_str() );
this->JoinPlayer( (PlayerNumber) pn );
}
CString sMode;
2004-09-21 07:53:39 +00:00
for( int i = 0; GetCommandlineArgument( "mode", &sMode, i ); ++i )
2004-04-30 07:48:02 +00:00
{
ApplyGameCommand( sMode );
2004-04-30 07:48:02 +00:00
}
}
2002-07-23 01:41:40 +00:00
void GameState::Reset()
{
EndGame();
ASSERT( THEME );
2004-02-22 23:26:46 +00:00
m_timeGameStarted.SetZero();
2004-06-28 07:26:00 +00:00
m_pCurStyle = NULL;
FOREACH_PlayerNumber( p )
2003-01-19 04:44:22 +00:00
m_bSideIsJoined[p] = false;
MEMCARDMAN->UnlockCards();
2003-01-19 04:44:22 +00:00
// m_iCoins = 0; // don't reset coin count!
2002-08-20 21:00:56 +00:00
m_MasterPlayerNumber = PLAYER_INVALID;
m_mapEnv.clear();
m_sPreferredSongGroup = GROUP_ALL_MUSIC;
m_bChangedFailType = false;
FOREACH_PlayerNumber( p )
{
m_PreferredDifficulty[p] = DIFFICULTY_INVALID;
m_PreferredCourseDifficulty[p] = DIFFICULTY_MEDIUM;
}
m_PreferredSortOrder = SORT_INVALID;
2002-07-29 03:06:55 +00:00
m_PlayMode = PLAY_MODE_INVALID;
m_bEditing = false;
2003-03-09 00:55:49 +00:00
m_bDemonstrationOrJukebox = false;
2003-02-11 02:20:38 +00:00
m_bJukeboxUsesModifiers = false;
2002-07-29 03:06:55 +00:00
m_iCurrentStageIndex = 0;
m_bAllow2ndExtraStage = true;
2003-10-26 03:02:30 +00:00
m_BeatToNoteSkinRev = 0;
2004-01-24 19:09:51 +00:00
m_iNumStagesOfThisSong = 0;
2002-07-29 03:06:55 +00:00
NOTESKIN->RefreshNoteSkinData( this->m_pCurGame );
2003-09-12 04:18:43 +00:00
m_iGameSeed = rand();
m_iRoundSeed = rand();
2002-07-23 01:41:40 +00:00
m_pCurSong = NULL;
m_pPreferredSong = NULL;
FOREACH_PlayerNumber( p )
2004-05-24 06:12:17 +00:00
m_pCurSteps[p] = NULL;
2002-07-23 01:41:40 +00:00
m_pCurCourse = NULL;
m_pPreferredCourse = NULL;
2004-05-24 04:26:54 +00:00
FOREACH_PlayerNumber( p )
m_pCurTrail[p] = NULL;
2002-07-23 01:41:40 +00:00
FOREACH_PlayerNumber( p )
m_pPlayerState[p]->Reset();
2003-11-30 23:13:38 +00:00
2003-01-25 11:05:12 +00:00
ResetMusicStatistics();
ResetStageStatistics();
FOREACH_PlayerNumber( pn )
PROFILEMAN->UnloadProfile( pn );
2004-04-23 01:40:06 +00:00
SONGMAN->FreeAllLoadedFromProfiles();
2004-02-27 21:28:15 +00:00
SONGMAN->UpdateBest();
SONGMAN->UpdateShuffled();
2003-01-25 11:05:12 +00:00
/* We may have cached trails from before everything was loaded (eg. from before
* SongManager::UpdateBest could be called). Erase the cache. */
2004-07-24 06:40:51 +00:00
SONGMAN->RegenerateNonFixedCourses();
2005-02-16 03:25:45 +00:00
STATSMAN->m_vPlayedStageStats.clear();
2002-07-29 03:06:55 +00:00
m_SongOptions.Init();
2003-04-21 23:43:51 +00:00
2004-05-16 02:51:55 +00:00
FOREACH_PlayerNumber(p)
{
2004-02-04 07:58:02 +00:00
// I can't think of a good reason to have both game-specific
// default mods and theme specific default mods. We should choose
// one or the other. -Chris
2004-02-05 21:32:50 +00:00
// Having default modifiers in prefs is needed for several things.
// The theme setting is for eg. BM being reverse by default. (This
2004-12-02 06:29:20 +00:00
// could be done in the title menu GameCommand, but then it wouldn't
2004-02-05 21:32:50 +00:00
// affect demo, and other non-gameplay things ...) -glenn
2004-05-16 02:51:55 +00:00
ApplyModifiers( p, DEFAULT_MODIFIERS );
ApplyModifiers( p, PREFSMAN->m_sDefaultModifiers );
}
2004-05-16 02:51:55 +00:00
FOREACH_PlayerNumber(p)
2003-06-27 08:06:22 +00:00
{
if( PREFSMAN->m_ShowDancingCharacters == PrefsManager::CO_RANDOM)
m_pCurCharacters[p] = GetRandomCharacter();
2003-06-27 08:06:22 +00:00
else
m_pCurCharacters[p] = GetDefaultCharacter();
2004-01-21 04:59:15 +00:00
ASSERT( m_pCurCharacters[p] );
2003-06-27 08:06:22 +00:00
}
2005-01-31 02:00:00 +00:00
m_iStopCourseAtSeconds = -1;
2005-02-21 17:29:49 +00:00
m_bTemporaryEventMode = false;
2004-03-23 06:11:10 +00:00
LIGHTSMAN->SetLightsMode( LIGHTSMODE_ATTRACT );
2004-04-30 07:48:02 +00:00
ApplyCmdline();
}
void GameState::JoinPlayer( PlayerNumber pn )
{
this->m_bSideIsJoined[pn] = true;
if( this->m_MasterPlayerNumber == PLAYER_INVALID )
this->m_MasterPlayerNumber = pn;
// if first player to join, set start time
if( this->GetNumSidesJoined() == 1 )
this->BeginGame();
}
void GameState::BeginGame()
{
2004-02-22 23:26:46 +00:00
m_timeGameStarted.Touch();
2003-12-28 08:20:48 +00:00
m_vpsNamesThatWereFilled.clear();
2003-12-28 19:46:50 +00:00
// Play attract on the ending screen, then on the ranking screen
// even if attract sounds are set to off.
m_iNumTimesThroughAttract = -1;
2004-08-09 05:01:24 +00:00
2005-01-27 19:42:34 +00:00
MEMCARDMAN->UnlockCards();
}
2004-02-08 01:05:53 +00:00
void GameState::PlayersFinalized()
{
if( MEMCARDMAN->GetCardsLocked() )
2004-04-23 02:08:11 +00:00
return;
MEMCARDMAN->LockCards();
// apply saved default modifiers if any
FOREACH_HumanPlayer( pn )
{
2005-01-28 00:12:09 +00:00
MEMCARDMAN->MountCard( pn );
2004-08-09 05:01:24 +00:00
PROFILEMAN->LoadFirstAvailableProfile( pn ); // load full profile
2004-03-29 06:47:10 +00:00
SONGMAN->LoadAllFromProfile( (ProfileSlot) pn );
MEMCARDMAN->UnmountCard( pn );
2004-03-13 19:20:24 +00:00
if( !PROFILEMAN->IsUsingProfile(pn) )
continue; // skip
Profile* pProfile = PROFILEMAN->GetProfile(pn);
CString sModifiers;
if( pProfile->GetDefaultModifiers( this->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. */
GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions.ResetSavedPrefs();
GAMESTATE->ApplyModifiers( pn, sModifiers );
}
2004-12-02 06:29:20 +00:00
// Only set the sort order if it wasn't already set by a GameCommand (or by an earlier profile)
if( m_PreferredSortOrder == SORT_INVALID && pProfile->m_SortOrder != SORT_INVALID )
m_PreferredSortOrder = pProfile->m_SortOrder;
2004-03-25 09:54:28 +00:00
if( pProfile->m_LastDifficulty != DIFFICULTY_INVALID )
m_PreferredDifficulty[pn] = pProfile->m_LastDifficulty;
if( pProfile->m_LastCourseDifficulty != DIFFICULTY_INVALID )
m_PreferredCourseDifficulty[pn] = pProfile->m_LastCourseDifficulty;
if( m_pPreferredSong == NULL )
m_pPreferredSong = pProfile->m_lastSong.ToSong();
if( m_pPreferredCourse == NULL )
m_pPreferredCourse = pProfile->m_lastCourse.ToCourse();
}
SONGMAN->LoadAllFromProfile( PROFILE_SLOT_MACHINE );
FOREACH_PlayerNumber( pn )
2004-05-16 02:51:55 +00:00
{
if( !IsHumanPlayer(pn) )
ApplyModifiers( pn, DEFAULT_CPU_MODIFIERS );
2004-05-16 02:51:55 +00:00
}
2004-02-08 01:05:53 +00:00
}
2004-02-22 23:26:46 +00:00
/* This data is added to each player profile, and to the machine profile per-player. */
2004-08-30 04:09:23 +00:00
void AddPlayerStatsToProfile( Profile *pProfile, const StageStats &ss, PlayerNumber pn )
2004-02-22 23:26:46 +00:00
{
2004-08-30 04:09:23 +00:00
ss.AssertValid( pn );
2004-02-22 23:26:46 +00:00
CHECKPOINT;
2004-07-17 21:49:32 +00:00
StyleID sID;
sID.FromStyle( ss.pStyle );
2004-08-30 04:09:23 +00:00
ASSERT( ss.vpSongs.size() == ss.m_player[pn].vpSteps.size() );
2004-08-30 04:09:23 +00:00
for( unsigned i=0; i<ss.vpSongs.size(); i++ )
{
Steps *pSteps = ss.m_player[pn].vpSteps[i];
2004-08-30 04:09:23 +00:00
pProfile->m_iNumSongsPlayedByPlayMode[ss.playMode]++;
pProfile->m_iNumSongsPlayedByStyle[sID] ++;
pProfile->m_iNumSongsPlayedByDifficulty[pSteps->GetDifficulty()] ++;
2005-01-14 15:58:19 +00:00
int iMeter = clamp( pSteps->GetMeter(), 0, MAX_METER );
pProfile->m_iNumSongsPlayedByMeter[iMeter] ++;
2004-08-30 04:09:23 +00:00
}
pProfile->m_iTotalDancePoints += ss.m_player[pn].iActualDancePoints;
2004-02-22 23:26:46 +00:00
if( ss.StageType == StageStats::STAGE_EXTRA || ss.StageType == StageStats::STAGE_EXTRA2 )
{
if( ss.m_player[pn].bFailed )
2004-02-22 23:26:46 +00:00
++pProfile->m_iNumExtraStagesFailed;
else
++pProfile->m_iNumExtraStagesPassed;
}
2004-08-30 04:09:23 +00:00
// If you fail in a course, you passed all but the final song.
// FIXME: Not true. If playing with 2 players, one player could have failed earlier.
if( !ss.m_player[pn].bFailed )
2004-08-30 06:07:14 +00:00
{
pProfile->m_iNumStagesPassedByPlayMode[ss.playMode] ++;
pProfile->m_iNumStagesPassedByGrade[ss.m_player[pn].GetGrade()] ++;
2004-08-30 06:07:14 +00:00
}
2004-02-22 23:26:46 +00:00
}
void GameState::EndGame()
{
2004-05-04 05:51:18 +00:00
LOG->Trace( "GameState::EndGame" );
if( m_bDemonstrationOrJukebox )
return;
2005-02-16 03:25:45 +00:00
if( m_timeGameStarted.IsZero() || !STATSMAN->m_vPlayedStageStats.size() ) // we were in the middle of a game and played at least one song
return;
2004-05-24 01:28:28 +00:00
/* Finish the final stage. */
FinishStage();
// Update totalPlaySeconds stat
2004-02-22 23:26:46 +00:00
int iPlaySeconds = max( 0, (int) m_timeGameStarted.PeekDeltaTime() );
2003-12-08 06:41:30 +00:00
Profile* pMachineProfile = PROFILEMAN->GetMachineProfile();
2003-12-09 10:20:18 +00:00
pMachineProfile->m_iTotalPlaySeconds += iPlaySeconds;
pMachineProfile->m_iTotalPlays++;
2004-03-20 19:15:06 +00:00
FOREACH_HumanPlayer( p )
{
2004-03-20 19:15:06 +00:00
Profile* pPlayerProfile = PROFILEMAN->GetProfile( p );
2003-12-08 06:41:30 +00:00
if( pPlayerProfile )
2003-12-09 10:20:18 +00:00
{
2003-12-08 06:41:30 +00:00
pPlayerProfile->m_iTotalPlaySeconds += iPlaySeconds;
pPlayerProfile->m_iTotalPlays++;
2004-02-22 02:16:02 +00:00
}
}
BOOKKEEPER->WriteToDisk();
2004-02-08 01:05:53 +00:00
2004-03-13 19:20:24 +00:00
FOREACH_HumanPlayer( pn )
2004-02-08 01:05:53 +00:00
{
2004-03-13 19:20:24 +00:00
if( !PROFILEMAN->IsUsingProfile(pn) )
continue;
bool bWasMemoryCard = PROFILEMAN->ProfileWasLoadedFromMemoryCard(pn);
if( bWasMemoryCard )
2005-01-28 00:12:09 +00:00
MEMCARDMAN->MountCard( pn );
PROFILEMAN->SaveProfile( pn );
if( bWasMemoryCard )
MEMCARDMAN->UnmountCard( pn );
2004-03-13 19:20:24 +00:00
PROFILEMAN->UnloadProfile( pn );
2004-02-08 01:05:53 +00:00
}
PROFILEMAN->SaveMachineProfile();
2004-03-13 19:20:24 +00:00
// Reset the USB storage device numbers -after- saving
CHECKPOINT;
// MEMCARDMAN->FlushAndReset();
2004-03-13 19:20:24 +00:00
CHECKPOINT;
2004-02-08 01:05:53 +00:00
SONGMAN->FreeAllLoadedFromProfiles();
// make sure we don't execute EndGame twice.
m_timeGameStarted.SetZero();
}
2003-01-26 02:21:47 +00:00
void GameState::SaveCurrentSettingsToProfile( PlayerNumber pn )
{
if( !PROFILEMAN->IsUsingProfile(pn) )
return;
if( m_bDemonstrationOrJukebox )
return;
Profile* pProfile = PROFILEMAN->GetProfile(pn);
pProfile->SetDefaultModifiers( this->m_pCurGame, m_pPlayerState[pn]->m_PlayerOptions.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_pPreferredSong )
pProfile->m_lastSong.FromSong( m_pPreferredSong );
if( m_pPreferredCourse )
pProfile->m_lastCourse.FromCourse( m_pPreferredCourse );
}
void GameState::Update( float fDelta )
{
FOREACH_PlayerNumber( p )
{
m_pPlayerState[p]->m_CurrentPlayerOptions.Approach( m_pPlayerState[p]->m_PlayerOptions, fDelta );
2003-04-02 21:57:05 +00:00
2004-03-24 07:37:35 +00:00
// TRICKY: GAMESTATE->Update is run before any of the Screen update's,
// so we'll clear these flags here and let them get turned on later
m_pPlayerState[p]->m_bAttackBeganThisUpdate = false;
m_pPlayerState[p]->m_bAttackEndedThisUpdate = false;
2004-03-24 07:37:35 +00:00
bool bRebuildPlayerOptions = false;
2004-01-26 20:39:14 +00:00
/* See if any delayed attacks are starting or ending. */
for( unsigned s=0; s<m_pPlayerState[p]->m_ActiveAttacks.size(); s++ )
{
Attack &attack = m_pPlayerState[p]->m_ActiveAttacks[s];
// -1 is the "starts now" sentinel value. You must add the attack
// by calling GameState::LaunchAttack, or else the -1 won't be
// converted into the current music time.
ASSERT( attack.fStartSecond != -1 );
bool bCurrentlyEnabled =
attack.bGlobal ||
( attack.fStartSecond < this->m_fMusicSeconds &&
m_fMusicSeconds < attack.fStartSecond+attack.fSecsRemaining );
2003-10-24 09:35:56 +00:00
if( m_pPlayerState[p]->m_ActiveAttacks[s].bOn == bCurrentlyEnabled )
2004-01-26 20:39:14 +00:00
continue; /* OK */
2003-10-24 09:35:56 +00:00
if( m_pPlayerState[p]->m_ActiveAttacks[s].bOn && !bCurrentlyEnabled )
m_pPlayerState[p]->m_bAttackEndedThisUpdate = true;
else if( !m_pPlayerState[p]->m_ActiveAttacks[s].bOn && bCurrentlyEnabled )
m_pPlayerState[p]->m_bAttackBeganThisUpdate = true;
2003-10-24 09:35:56 +00:00
2004-01-12 03:47:55 +00:00
bRebuildPlayerOptions = true;
2004-01-26 20:39:14 +00:00
m_pPlayerState[p]->m_ActiveAttacks[s].bOn = bCurrentlyEnabled;
}
2004-01-12 03:47:55 +00:00
if( bRebuildPlayerOptions )
2005-01-31 03:18:46 +00:00
RebuildPlayerOptionsFromActiveAttacks( p );
2004-01-12 03:47:55 +00:00
if( m_pPlayerState[p]->m_fSecondsUntilAttacksPhasedOut > 0 )
m_pPlayerState[p]->m_fSecondsUntilAttacksPhasedOut = max( 0, m_pPlayerState[p]->m_fSecondsUntilAttacksPhasedOut - fDelta );
}
}
void GameState::ReloadCharacters()
{
2004-09-21 07:53:39 +00:00
for( unsigned i=0; i<m_pCharacters.size(); i++ )
SAFE_DELETE( m_pCharacters[i] );
m_pCharacters.clear();
FOREACH_PlayerNumber( p )
m_pCurCharacters[p] = NULL;
CStringArray as;
2003-07-22 07:47:27 +00:00
GetDirListing( CHARACTERS_DIR "*", as, true, true );
2003-09-28 00:17:08 +00:00
bool FoundDefault = false;
2004-09-21 07:53:39 +00:00
for( unsigned i=0; i<as.size(); i++ )
{
CString sCharName, sDummy;
splitpath(as[i], sDummy, sCharName, sDummy);
sCharName.MakeLower();
if( sCharName == "cvs" ) // the directory called "CVS"
continue; // ignore it
if( sCharName.CompareNoCase("default")==0 )
2003-09-28 00:17:08 +00:00
FoundDefault = true;
Character* pChar = new Character;
if( pChar->Load( as[i] ) )
m_pCharacters.push_back( pChar );
else
delete pChar;
}
2003-09-28 00:17:08 +00:00
if( !FoundDefault )
2003-12-10 09:44:16 +00:00
RageException::Throw( "'Characters/default' is missing." );
2003-11-01 06:30:17 +00:00
// If FoundDefault, then we're not empty. -Chris
// if( m_pCharacters.empty() )
// RageException::Throw( "Couldn't find any character definitions" );
}
const float GameState::MUSIC_SECONDS_INVALID = -5000.0f;
2002-07-29 03:06:55 +00:00
void GameState::ResetMusicStatistics()
{
m_fMusicSeconds = 0; // MUSIC_SECONDS_INVALID;
2002-07-29 03:06:55 +00:00
m_fSongBeat = 0;
m_fCurBPS = 10;
m_bFreeze = false;
2003-01-25 11:05:12 +00:00
m_bPastHereWeGo = false;
Actor::SetBGMTime( 0, 0 );
}
2003-05-13 13:35:32 +00:00
void GameState::ResetStageStatistics()
{
2005-02-16 03:25:45 +00:00
StageStats OldStats = STATSMAN->m_CurStageStats;
STATSMAN->m_CurStageStats = StageStats();
2003-12-08 04:39:29 +00:00
if( PREFSMAN->m_bComboContinuesBetweenSongs )
{
if( GetStageIndex() == 0 )
{
FOREACH_PlayerNumber( p )
2003-12-08 04:39:29 +00:00
{
Profile* pProfile = PROFILEMAN->GetProfile(p);
2003-12-08 04:39:29 +00:00
if( pProfile )
2005-02-16 03:25:45 +00:00
STATSMAN->m_CurStageStats.m_player[p].iCurCombo = pProfile->m_iCurrentCombo;
2003-12-08 04:39:29 +00:00
}
}
else // GetStageIndex() > 0
{
FOREACH_PlayerNumber( p )
{
2005-02-16 03:25:45 +00:00
STATSMAN->m_CurStageStats.m_player[p].iCurCombo = OldStats.m_player[p].iCurCombo;
}
2003-12-08 04:39:29 +00:00
}
}
RemoveAllActiveAttacks();
RemoveAllInventory();
2003-05-13 13:35:32 +00:00
m_fOpponentHealthPercent = 1;
m_fTugLifePercentP1 = 0.5f;
FOREACH_PlayerNumber( p )
{
m_pPlayerState[p]->m_fSuperMeter = 0;
m_pPlayerState[p]->m_HealthState = PlayerState::ALIVE;
2004-01-12 03:47:55 +00:00
m_pPlayerState[p]->m_iLastPositiveSumOfAttackLevels = 0;
m_pPlayerState[p]->m_fSecondsUntilAttacksPhasedOut = 0; // PlayerAI not affected
}
2004-03-07 04:34:49 +00:00
2004-08-30 06:07:14 +00:00
FOREACH_PlayerNumber( p )
2004-03-07 04:34:49 +00:00
{
2004-08-30 06:07:14 +00:00
m_vLastPerDifficultyAwards[p].clear();
m_vLastPeakComboAwards[p].clear();
2004-03-07 04:34:49 +00:00
}
2002-07-29 03:06:55 +00:00
}
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer &timestamp )
2003-02-06 17:40:06 +00:00
{
if( !timestamp.IsZero() )
m_LastBeatUpdate = timestamp;
else
m_LastBeatUpdate.Touch();
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze );
2004-06-16 00:38:31 +00:00
ASSERT_M( m_fSongBeat > -2000, ssprintf("%f %f", m_fSongBeat, fPositionSeconds) );
2004-01-12 01:10:25 +00:00
m_fMusicSeconds = fPositionSeconds;
2004-07-07 21:17:47 +00:00
Actor::SetBGMTime( fPositionSeconds, m_fSongBeat );
2004-07-07 21:17:47 +00:00
2003-05-27 05:27:38 +00:00
// LOG->Trace( "m_fMusicSeconds = %f, m_fSongBeat = %f, m_fCurBPS = %f, m_bFreeze = %f", m_fMusicSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze );
2003-02-06 17:40:06 +00:00
}
2003-10-23 06:16:29 +00:00
float GameState::GetSongPercent( float beat ) const
{
/* 0 = first step; 1 = last step */
return (beat - m_pCurSong->m_fFirstBeat) / m_pCurSong->m_fLastBeat;
}
static int GetNumStagesForCurrentSong()
2004-01-22 02:16:50 +00:00
{
int iNumStagesOfThisSong = 1;
if( GAMESTATE->m_pCurSong )
iNumStagesOfThisSong = SongManager::GetNumStagesForSong( GAMESTATE->m_pCurSong );
else if( GAMESTATE->m_pCurCourse )
iNumStagesOfThisSong = 1;
else
return -1;
ASSERT( iNumStagesOfThisSong >= 1 && iNumStagesOfThisSong <= 3 );
/* Never increment more than one past final stage. That is, if the current
* stage is the final stage, and we picked a stage that takes two songs, it
* only counts as one stage (so it doesn't bump us all the way to Ex2).
* One case where this happens is a long/marathon extra stage. Another is
2004-12-02 06:29:20 +00:00
* if a long/marathon song is selected explicitly in the theme with a GameCommand,
* and PREFSMAN->m_iNumArcadeStages is less than the number of stages that
* song takes. */
int iNumStagesLeft = PREFSMAN->m_iNumArcadeStages - GAMESTATE->m_iCurrentStageIndex;
iNumStagesOfThisSong = min( iNumStagesOfThisSong, iNumStagesLeft );
iNumStagesOfThisSong = max( iNumStagesOfThisSong, 1 );
return iNumStagesOfThisSong;
}
2004-01-24 19:09:51 +00:00
/* Called by ScreenGameplay. Set the length of the current song. */
void GameState::BeginStage()
{
2004-08-31 01:17:30 +00:00
if( m_bDemonstrationOrJukebox )
return;
/* This should only be called once per stage. */
if( m_iNumStagesOfThisSong != 0 )
LOG->Warn( "XXX: m_iNumStagesOfThisSong == %i?", m_iNumStagesOfThisSong );
/* Finish the last stage (if any), if we havn't already. (For example, we might
* have, for some reason, gone from gameplay to evaluation straight back to gameplay.) */
FinishStage();
m_iNumStagesOfThisSong = GetNumStagesForCurrentSong();
ASSERT( m_iNumStagesOfThisSong != -1 );
}
2004-01-23 02:04:34 +00:00
void GameState::CancelStage()
{
m_iNumStagesOfThisSong = 0;
}
/* Called by ScreenSelectMusic (etc). Increment the stage counter if we just played a
* song. Might be called more than once. */
void GameState::FinishStage()
{
/* If m_iNumStagesOfThisSong is 0, we've been called more than once before calling
* BeginStage. This can happen when backing out of the player options screen. */
if( m_iNumStagesOfThisSong == 0 )
return;
// Increment the stage counter.
2004-01-24 19:09:51 +00:00
ASSERT( m_iNumStagesOfThisSong >= 1 && m_iNumStagesOfThisSong <= 3 );
2004-07-20 23:05:49 +00:00
const int iOldStageIndex = m_iCurrentStageIndex;
m_iCurrentStageIndex += m_iNumStagesOfThisSong;
2004-01-22 02:16:50 +00:00
m_iNumStagesOfThisSong = 0;
// The round has ended; change the seed.
GAMESTATE->m_iRoundSeed = rand();
if( m_bDemonstrationOrJukebox )
return;
//
2004-07-11 07:21:33 +00:00
// Add step totals. Use radarActual, since the player might have failed part way
// through the song, in which case we don't want to give credit for the rest of the
// song.
//
FOREACH_HumanPlayer( pn )
{
2005-02-16 03:25:45 +00:00
int iNumTapsAndHolds = (int) STATSMAN->m_CurStageStats.m_player[pn].radarActual[RADAR_NUM_TAPS_AND_HOLDS];
int iNumJumps = (int) STATSMAN->m_CurStageStats.m_player[pn].radarActual[RADAR_NUM_JUMPS];
int iNumHolds = (int) STATSMAN->m_CurStageStats.m_player[pn].radarActual[RADAR_NUM_HOLDS];
int iNumMines = (int) STATSMAN->m_CurStageStats.m_player[pn].radarActual[RADAR_NUM_MINES];
int iNumHands = (int) STATSMAN->m_CurStageStats.m_player[pn].radarActual[RADAR_NUM_HANDS];
float fCaloriesBurned = STATSMAN->m_CurStageStats.m_player[pn].fCaloriesBurned;
PROFILEMAN->AddStepTotals( pn, iNumTapsAndHolds, iNumJumps, iNumHolds, iNumMines, iNumHands, fCaloriesBurned );
}
2004-07-20 23:05:49 +00:00
// Update profile stats
Profile* pMachineProfile = PROFILEMAN->GetMachineProfile();
2005-02-16 03:25:45 +00:00
int iGameplaySeconds = (int)truncf(STATSMAN->m_CurStageStats.fGameplaySeconds);
pMachineProfile->m_iTotalGameplaySeconds += iGameplaySeconds;
pMachineProfile->m_iCurrentCombo = 0;
CHECKPOINT;
2005-01-03 21:16:20 +00:00
FOREACH_HumanPlayer( pn )
{
CHECKPOINT;
2005-01-03 21:16:20 +00:00
Profile* pPlayerProfile = PROFILEMAN->GetProfile( pn );
if( pPlayerProfile )
{
pPlayerProfile->m_iTotalGameplaySeconds += iGameplaySeconds;
pPlayerProfile->m_iCurrentCombo =
PREFSMAN->m_bComboContinuesBetweenSongs ?
2005-02-16 03:25:45 +00:00
STATSMAN->m_CurStageStats.m_player[pn].iCurCombo :
0;
}
2005-02-16 03:25:45 +00:00
const StageStats& ss = STATSMAN->m_CurStageStats;
2005-01-03 21:16:20 +00:00
AddPlayerStatsToProfile( pMachineProfile, ss, pn );
if( pPlayerProfile )
2005-01-03 21:16:20 +00:00
AddPlayerStatsToProfile( pPlayerProfile, ss, pn );
CHECKPOINT;
}
2005-02-21 17:29:49 +00:00
if( GAMESTATE->GetEventMode() )
2004-07-20 23:05:49 +00:00
{
const int iSaveProfileEvery = 3;
if( iOldStageIndex/iSaveProfileEvery < m_iCurrentStageIndex/iSaveProfileEvery )
{
LOG->Trace( "Played %i stages; saving profiles ...", iSaveProfileEvery );
PROFILEMAN->SaveAllProfiles();
}
}
2004-01-22 02:16:50 +00:00
}
2004-01-22 02:15:55 +00:00
int GameState::GetStageIndex() const
2002-07-23 01:41:40 +00:00
{
return m_iCurrentStageIndex;
}
2004-01-22 02:15:55 +00:00
int GameState::GetNumStagesLeft() const
{
2004-01-22 02:15:55 +00:00
if( IsExtraStage() || IsExtraStage2() )
2003-02-12 20:56:19 +00:00
return 1;
2005-02-21 17:29:49 +00:00
if( GAMESTATE->GetEventMode() )
2003-02-14 08:15:42 +00:00
return 999;
return PREFSMAN->m_iNumArcadeStages - m_iCurrentStageIndex;
}
2004-01-22 02:15:55 +00:00
bool GameState::IsFinalStage() const
2002-07-23 01:41:40 +00:00
{
2005-02-21 17:29:49 +00:00
if( GAMESTATE->GetEventMode() )
2002-07-23 01:41:40 +00:00
return false;
if( this->IsCourseMode() )
return true;
/* This changes dynamically on ScreenSelectMusic as the wheel turns. */
int iPredictedStageForCurSong = GetNumStagesForCurrentSong();
if( iPredictedStageForCurSong == -1 )
iPredictedStageForCurSong = 1;
2004-01-22 23:36:26 +00:00
return m_iCurrentStageIndex + iPredictedStageForCurSong == PREFSMAN->m_iNumArcadeStages;
2002-07-23 01:41:40 +00:00
}
2004-01-22 02:15:55 +00:00
bool GameState::IsExtraStage() const
2002-07-23 01:41:40 +00:00
{
2005-02-21 17:29:49 +00:00
if( GAMESTATE->GetEventMode() )
2002-07-23 01:41:40 +00:00
return false;
return m_iCurrentStageIndex == PREFSMAN->m_iNumArcadeStages;
}
2004-01-22 02:15:55 +00:00
bool GameState::IsExtraStage2() const
2002-07-23 01:41:40 +00:00
{
2005-02-21 17:29:49 +00:00
if( GAMESTATE->GetEventMode() )
2002-07-23 01:41:40 +00:00
return false;
return m_iCurrentStageIndex == PREFSMAN->m_iNumArcadeStages+1;
}
2004-01-22 02:15:55 +00:00
CString GameState::GetStageText() const
2002-07-23 01:41:40 +00:00
{
2003-04-01 18:23:27 +00:00
if( m_bDemonstrationOrJukebox ) return "demo";
else if( m_PlayMode == PLAY_MODE_ONI ) return "oni";
else if( m_PlayMode == PLAY_MODE_NONSTOP ) return "nonstop";
else if( m_PlayMode == PLAY_MODE_ENDLESS ) return "endless";
2005-02-21 17:29:49 +00:00
else if( GAMESTATE->GetEventMode() ) return "event";
2003-04-01 18:23:27 +00:00
else if( IsFinalStage() ) return "final";
else if( IsExtraStage() ) return "extra1";
else if( IsExtraStage2() ) return "extra2";
else return ssprintf("%d",m_iCurrentStageIndex+1);
2002-07-23 01:41:40 +00:00
}
2004-01-22 02:15:55 +00:00
void GameState::GetAllStageTexts( CStringArray &out ) const
2003-10-09 04:20:24 +00:00
{
out.clear();
out.push_back( "demo" );
out.push_back( "oni" );
out.push_back( "nonstop" );
out.push_back( "endless" );
out.push_back( "event" );
out.push_back( "final" );
out.push_back( "extra1" );
out.push_back( "extra2" );
for( int stage = 0; stage < PREFSMAN->m_iNumArcadeStages; ++stage )
out.push_back( ssprintf("%d",stage+1) );
2003-10-09 04:20:24 +00:00
}
2004-01-22 02:15:55 +00:00
int GameState::GetCourseSongIndex() const
{
int iSongIndex = 0;
2003-02-25 21:23:21 +00:00
/* iSongsPlayed includes the current song, so it's 1-based; subtract one. */
2005-01-03 21:16:20 +00:00
FOREACH_EnabledPlayer( pn )
2005-02-16 03:25:45 +00:00
iSongIndex = max( iSongIndex, STATSMAN->m_CurStageStats.m_player[pn].iSongsPlayed-1 );
return iSongIndex;
}
2004-05-16 02:51:55 +00:00
CString GameState::GetPlayerDisplayName( PlayerNumber pn ) const
{
ASSERT( IsPlayerEnabled(pn) );
const CString defaultnames[NUM_PLAYERS] = { "Player 1", "Player 2" };
2004-05-16 02:51:55 +00:00
if( IsHumanPlayer(pn) )
{
if( !PROFILEMAN->GetPlayerName(pn).empty() )
return PROFILEMAN->GetPlayerName(pn);
else
return defaultnames[pn];
}
else
{
return "CPU";
}
}
bool GameState::PlayersCanJoin() const
{
2004-06-28 07:26:00 +00:00
return GetNumSidesJoined() == 0 || this->m_pCurStyle == NULL; // selecting a style finalizes the players
2004-03-30 08:03:00 +00:00
}
bool GameState::EnoughCreditsToJoin() const
{
2005-02-21 17:29:49 +00:00
switch( GAMESTATE->GetCoinMode() )
2004-03-30 08:03:00 +00:00
{
case COIN_PAY:
return GAMESTATE->m_iCoins >= PREFSMAN->m_iCoinsPerCredit;
case COIN_HOME:
case COIN_FREE:
return true;
default:
ASSERT(0);
return false;
}
}
int GameState::GetNumSidesJoined() const
{
int iNumSidesJoined = 0;
for( int c=0; c<NUM_PLAYERS; c++ )
if( m_bSideIsJoined[c] )
iNumSidesJoined++; // left side, and right side
return iNumSidesJoined;
}
2004-07-25 17:07:32 +00:00
const Game* GameState::GetCurrentGame()
2002-07-23 01:41:40 +00:00
{
ASSERT( m_pCurGame != NULL ); // the game must be set before calling this
return m_pCurGame;
2002-07-23 01:41:40 +00:00
}
2004-06-28 07:26:00 +00:00
const Style* GameState::GetCurrentStyle() const
2002-07-23 01:41:40 +00:00
{
2004-06-28 07:26:00 +00:00
return m_pCurStyle;
2002-07-23 01:41:40 +00:00
}
2003-03-02 01:43:33 +00:00
2004-01-22 02:15:55 +00:00
bool GameState::IsPlayerEnabled( PlayerNumber pn ) const
2002-07-23 01:41:40 +00:00
{
// In rave, all players are present. Non-human players are CPU controlled.
2003-08-19 04:27:50 +00:00
switch( m_PlayMode )
{
case PLAY_MODE_BATTLE:
case PLAY_MODE_RAVE:
2003-04-07 05:14:27 +00:00
return true;
2003-08-19 04:27:50 +00:00
}
return IsHumanPlayer( pn );
}
2002-07-23 01:41:40 +00:00
2004-01-22 02:15:55 +00:00
int GameState::GetNumPlayersEnabled() const
{
int count = 0;
2005-01-03 21:16:20 +00:00
FOREACH_EnabledPlayer( pn )
count++;
2004-01-22 02:15:55 +00:00
return count;
}
2004-03-20 17:53:04 +00:00
bool GameState::PlayerUsingBothSides() const
{
2005-01-04 01:47:11 +00:00
ASSERT( this->GetCurrentStyle() != NULL );
return this->GetCurrentStyle()->m_StyleType == ONE_PLAYER_TWO_SIDES;
2004-03-20 17:53:04 +00:00
}
2004-01-22 02:15:55 +00:00
bool GameState::IsHumanPlayer( PlayerNumber pn ) const
{
2004-06-28 07:26:00 +00:00
if( m_pCurStyle == NULL ) // no style chosen
{
if( this->PlayersCanJoin() )
return m_bSideIsJoined[pn]; // only allow input from sides that have already joined
else
return true; // if we can't join, then we're on a screen like MusicScroll or GameOver
}
2003-03-09 03:28:34 +00:00
2004-06-28 07:26:00 +00:00
switch( GetCurrentStyle()->m_StyleType )
{
case TWO_PLAYERS_TWO_SIDES:
return true;
case ONE_PLAYER_ONE_SIDE:
case ONE_PLAYER_TWO_SIDES:
2002-08-20 21:00:56 +00:00
return pn == m_MasterPlayerNumber;
default:
2002-08-20 21:00:56 +00:00
ASSERT(0); // invalid style type
return false;
}
2002-07-29 03:06:55 +00:00
}
2004-03-20 17:45:34 +00:00
int GameState::GetNumHumanPlayers() const
{
int count = 0;
2005-01-03 21:16:20 +00:00
FOREACH_HumanPlayer( pn )
count++;
2004-03-20 17:45:34 +00:00
return count;
}
2004-01-22 02:15:55 +00:00
PlayerNumber GameState::GetFirstHumanPlayer() const
2003-04-07 21:24:14 +00:00
{
2005-01-03 21:16:20 +00:00
FOREACH_HumanPlayer( pn )
return pn;
2003-04-07 21:24:14 +00:00
ASSERT(0); // there must be at least 1 human player
return PLAYER_INVALID;
}
2004-12-01 20:42:15 +00:00
PlayerNumber GameState::GetFirstDisabledPlayer() const
{
2005-01-03 21:16:20 +00:00
FOREACH_PlayerNumber( pn )
if( !IsPlayerEnabled(pn) )
return pn;
2004-12-01 20:42:15 +00:00
return PLAYER_INVALID;
}
2004-01-22 02:15:55 +00:00
bool GameState::IsCpuPlayer( PlayerNumber pn ) const
{
return IsPlayerEnabled(pn) && !IsHumanPlayer(pn);
}
2004-01-22 02:15:55 +00:00
bool GameState::AnyPlayersAreCpu() const
{
2005-01-03 21:16:20 +00:00
FOREACH_CpuPlayer( pn )
return true;
2004-01-22 02:15:55 +00:00
return false;
}
2003-02-28 07:26:43 +00:00
bool GameState::IsCourseMode() const
{
switch(m_PlayMode)
{
case PLAY_MODE_ONI:
case PLAY_MODE_NONSTOP:
case PLAY_MODE_ENDLESS:
return true;
default:
return false;
}
}
2003-07-09 20:23:44 +00:00
bool GameState::IsBattleMode() const
{
2004-02-07 22:14:36 +00:00
switch( this->m_PlayMode )
2003-07-09 20:23:44 +00:00
{
2003-08-19 04:27:50 +00:00
case PLAY_MODE_BATTLE:
2003-07-09 20:23:44 +00:00
return true;
default:
return false;
}
}
2004-02-07 22:14:36 +00:00
bool GameState::HasEarnedExtraStage() const
{
2005-02-21 17:29:49 +00:00
if( GAMESTATE->GetEventMode() )
return false;
2003-10-17 08:03:46 +00:00
if( !PREFSMAN->m_bAllowExtraStage )
return false;
if( this->m_PlayMode != PLAY_MODE_REGULAR )
2003-02-05 18:24:36 +00:00
return false;
2004-02-07 22:14:36 +00:00
if( (this->IsFinalStage() || this->IsExtraStage()) )
{
2005-01-03 21:16:20 +00:00
FOREACH_EnabledPlayer( pn )
{
2005-01-03 21:16:20 +00:00
if( this->m_pCurSteps[pn]->GetDifficulty() != DIFFICULTY_HARD &&
this->m_pCurSteps[pn]->GetDifficulty() != DIFFICULTY_CHALLENGE )
continue; /* not hard enough! */
2003-03-27 21:25:33 +00:00
/* If "choose EX" is enabled, then we should only grant EX2 if the chosen
* stage was the EX we would have chosen (m_bAllow2ndExtraStage is true). */
2004-02-07 22:14:36 +00:00
if( PREFSMAN->m_bPickExtraStage && this->IsExtraStage() && !this->m_bAllow2ndExtraStage )
continue;
2005-02-16 03:25:45 +00:00
if( STATSMAN->m_CurStageStats.m_player[pn].GetGrade() <= GRADE_TIER_3 )
return true;
}
}
return false;
2003-01-27 02:00:38 +00:00
}
2004-02-07 22:14:36 +00:00
PlayerNumber GameState::GetBestPlayer() const
{
2005-01-03 21:16:20 +00:00
FOREACH_PlayerNumber( pn )
if( GetStageResult(pn) == RESULT_WIN )
return pn;
return PLAYER_INVALID; // draw
}
2004-02-07 22:14:36 +00:00
StageResult GameState::GetStageResult( PlayerNumber pn ) const
{
2004-02-07 22:14:36 +00:00
switch( this->m_PlayMode )
2003-05-13 13:35:32 +00:00
{
2003-08-19 04:27:50 +00: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: ASSERT(0); return RESULT_LOSE;
}
2003-05-13 13:35:32 +00:00
}
StageResult win = RESULT_WIN;
2005-01-03 21:16:20 +00:00
FOREACH_PlayerNumber( p )
{
if( p == pn )
continue;
/* If anyone did just as well, at best it's a draw. */
2005-02-16 03:25:45 +00:00
if( STATSMAN->m_CurStageStats.m_player[p].iActualDancePoints == STATSMAN->m_CurStageStats.m_player[pn].iActualDancePoints )
win = RESULT_DRAW;
/* If anyone did better, we lost. */
2005-02-16 03:25:45 +00:00
if( STATSMAN->m_CurStageStats.m_player[p].iActualDancePoints > STATSMAN->m_CurStageStats.m_player[pn].iActualDancePoints )
return RESULT_LOSE;
}
return win;
}
2003-03-26 23:08:05 +00:00
2003-04-22 04:54:04 +00:00
void GameState::ApplyModifiers( PlayerNumber pn, CString sModifiers )
2003-04-21 23:43:51 +00:00
{
2004-02-07 22:14:36 +00:00
const SongOptions::FailType ft = this->m_SongOptions.m_FailType;
m_pPlayerState[pn]->m_PlayerOptions.FromString( sModifiers );
2003-04-21 23:43:51 +00:00
m_SongOptions.FromString( sModifiers );
2004-02-07 22:14:36 +00:00
if( ft != this->m_SongOptions.m_FailType )
this->m_bChangedFailType = true;
2003-04-21 23:43:51 +00:00
}
/* Store the player's preferred options. This is called at the very beginning
* of gameplay. */
void GameState::StoreSelectedOptions()
{
2005-01-03 21:16:20 +00:00
FOREACH_PlayerNumber( pn )
m_pPlayerState[pn]->m_StoredPlayerOptions = m_pPlayerState[pn]->m_PlayerOptions;
2003-03-26 23:08:05 +00:00
m_StoredSongOptions = m_SongOptions;
}
/* Restore the preferred options. This is called after a song ends, before
* setting new course options, so options from one song don't carry into the
* next and we default back to the preferred options. This is also called
* at the end of gameplay to restore options. */
void GameState::RestoreSelectedOptions()
{
2005-01-03 21:16:20 +00:00
FOREACH_PlayerNumber( pn )
m_pPlayerState[pn]->m_PlayerOptions = m_pPlayerState[pn]->m_StoredPlayerOptions;
2003-03-26 23:08:05 +00:00
m_SongOptions = m_StoredSongOptions;
}
bool GameState::IsDisqualified( PlayerNumber pn )
{
2004-03-28 20:08:43 +00:00
if( !PREFSMAN->m_bDisqualification )
return false;
if( GAMESTATE->IsCourseMode() )
{
return GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions.IsEasierForCourseAndTrail(
GAMESTATE->m_pCurCourse,
2004-06-03 08:22:02 +00:00
GAMESTATE->m_pCurTrail[pn] );
}
else
{
return GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions.IsEasierForSongAndSteps(
GAMESTATE->m_pCurSong,
2004-05-24 06:12:17 +00:00
GAMESTATE->m_pCurSteps[pn] );
}
}
void GameState::ResetNoteSkins()
{
2004-06-06 20:57:13 +00:00
FOREACH_PlayerNumber( pn )
2005-01-31 03:18:46 +00:00
ResetNoteSkinsForPlayer( pn );
2003-10-26 03:02:30 +00:00
++m_BeatToNoteSkinRev;
}
void GameState::ResetNoteSkinsForPlayer( PlayerNumber pn )
{
m_pPlayerState[pn]->m_BeatToNoteSkin.clear();
m_pPlayerState[pn]->m_BeatToNoteSkin[-1000] = m_pPlayerState[pn]->m_PlayerOptions.m_sNoteSkin;
2003-10-26 03:02:30 +00:00
++m_BeatToNoteSkinRev;
}
2003-10-24 09:35:56 +00:00
void GameState::GetAllUsedNoteSkins( vector<CString> &out ) const
{
FOREACH_EnabledPlayer( pn )
2003-10-24 09:35:56 +00:00
{
out.push_back( m_pPlayerState[pn]->m_PlayerOptions.m_sNoteSkin );
2003-10-24 09:35:56 +00:00
2004-02-07 22:14:36 +00:00
switch( this->m_PlayMode )
2003-10-24 09:35:56 +00:00
{
case PLAY_MODE_BATTLE:
case PLAY_MODE_RAVE:
for( int al=0; al<NUM_ATTACK_LEVELS; al++ )
{
2004-02-07 22:14:36 +00:00
const Character *ch = this->m_pCurCharacters[pn];
2003-10-24 09:35:56 +00:00
ASSERT( ch );
const CString* asAttacks = ch->m_sAttacks[al];
for( int att = 0; att < NUM_ATTACKS_PER_LEVEL; ++att )
{
PlayerOptions po;
po.FromString( asAttacks[att] );
2004-08-18 07:38:41 +00:00
/* Hack: NoteSkin "default" is never applied as an attack,
* so don't waste memory preloading it. */
if( po.m_sNoteSkin != "" && po.m_sNoteSkin.CompareNoCase("default") )
2003-10-24 09:35:56 +00:00
out.push_back( po.m_sNoteSkin );
}
}
}
for( map<float,CString>::const_iterator it = m_pPlayerState[pn]->m_BeatToNoteSkin.begin();
it != m_pPlayerState[pn]->m_BeatToNoteSkin.end(); ++it )
2003-10-24 09:35:56 +00:00
out.push_back( it->second );
}
}
/* From NoteField: */
2003-09-23 23:56:15 +00:00
void GameState::GetUndisplayedBeats( const PlayerState* pPlayerState, float TotalSeconds, float &StartBeat, float &EndBeat ) const
2003-09-23 23:56:15 +00:00
{
/* If reasonable, push the attack forward so notes on screen don't change suddenly. */
StartBeat = min( m_fSongBeat+BEATS_PER_MEASURE*2, pPlayerState->m_fLastDrawnBeat );
2003-09-23 23:56:15 +00:00
StartBeat = truncf(StartBeat)+1;
const float StartSecond = this->m_pCurSong->GetElapsedTimeFromBeat( StartBeat );
const float EndSecond = StartSecond + TotalSeconds;
EndBeat = this->m_pCurSong->GetBeatFromElapsedTime( EndSecond );
EndBeat = truncf(EndBeat)+1;
}
void GameState::SetNoteSkinForBeatRange( PlayerState* pPlayerState, const CString& sNoteSkin, float StartBeat, float EndBeat )
2003-10-24 09:35:56 +00:00
{
map<float,CString> &BeatToNoteSkin = pPlayerState->m_BeatToNoteSkin;
2003-10-24 09:35:56 +00:00
/* Erase any other note skin settings in this range. */
map<float,CString>::iterator it = BeatToNoteSkin.lower_bound( StartBeat );
map<float,CString>::iterator end = BeatToNoteSkin.upper_bound( EndBeat );
while( it != end )
{
map<float,CString>::iterator next = it;
++next;
2003-10-24 09:35:56 +00:00
BeatToNoteSkin.erase( it );
it = next;
}
2003-10-24 09:35:56 +00:00
/* Add the skin to m_BeatToNoteSkin. */
BeatToNoteSkin[StartBeat] = sNoteSkin;
/* Return to the default note skin after the duration. */
BeatToNoteSkin[EndBeat] = pPlayerState->m_StoredPlayerOptions.m_sNoteSkin;
2003-10-24 09:35:56 +00:00
++m_BeatToNoteSkinRev;
}
/* This is called to launch an attack, or to queue an attack if a.fStartSecond
* is set. This is also called by GameState::Update when activating a queued attack. */
2004-10-23 23:41:49 +00:00
void GameState::LaunchAttack( PlayerNumber target, const Attack& a )
2003-10-24 09:35:56 +00:00
{
LOG->Trace( "Launch attack '%s' against P%d at %f", a.sModifier.c_str(), target+1, a.fStartSecond );
2004-10-23 23:41:49 +00:00
Attack attack = a;
2004-01-30 00:58:33 +00:00
/* If fStartSecond is -1, it means "launch as soon as possible". For m_ActiveAttacks,
* mark the real time it's starting (now), so Update() can know when the attack started
* so it can be removed later. For m_ModsToApply, leave the -1 in, so Player::Update
* knows to apply attack transforms correctly. (yuck) */
m_pPlayerState[target]->m_ModsToApply.push_back( attack );
2004-10-23 23:41:49 +00:00
if( attack.fStartSecond == -1 )
attack.fStartSecond = this->m_fMusicSeconds;
m_pPlayerState[target]->m_ActiveAttacks.push_back( attack );
2004-01-30 00:58:33 +00:00
2004-02-07 22:14:36 +00:00
this->RebuildPlayerOptionsFromActiveAttacks( target );
}
2003-10-24 09:35:56 +00:00
void GameState::RemoveActiveAttacksForPlayer( PlayerNumber pn, AttackLevel al )
{
for( unsigned s=0; s<m_pPlayerState[pn]->m_ActiveAttacks.size(); s++ )
{
if( al != NUM_ATTACK_LEVELS && al != m_pPlayerState[pn]->m_ActiveAttacks[s].level )
2003-10-24 09:35:56 +00:00
continue;
m_pPlayerState[pn]->m_ActiveAttacks.erase( m_pPlayerState[pn]->m_ActiveAttacks.begin()+s, m_pPlayerState[pn]->m_ActiveAttacks.begin()+s+1 );
2003-12-01 21:04:40 +00:00
--s;
}
RebuildPlayerOptionsFromActiveAttacks( pn );
}
2003-04-14 22:12:54 +00:00
void GameState::RemoveAllInventory()
{
FOREACH_PlayerNumber( p )
{
2003-07-31 20:34:01 +00:00
for( int s=0; s<NUM_INVENTORY_SLOTS; s++ )
2003-04-14 22:12:54 +00:00
{
m_pPlayerState[p]->m_Inventory[s].fSecsRemaining = 0;
m_pPlayerState[p]->m_Inventory[s].sModifier = "";
2003-04-14 22:12:54 +00:00
}
}
2003-04-14 22:12:54 +00:00
}
void GameState::RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn )
{
// rebuild player options
PlayerOptions po = m_pPlayerState[pn]->m_StoredPlayerOptions;
for( unsigned s=0; s<m_pPlayerState[pn]->m_ActiveAttacks.size(); s++ )
2003-10-24 09:35:56 +00:00
{
if( !m_pPlayerState[pn]->m_ActiveAttacks[s].bOn )
2003-10-24 09:35:56 +00:00
continue; /* hasn't started yet */
po.FromString( m_pPlayerState[pn]->m_ActiveAttacks[s].sModifier );
2003-10-24 09:35:56 +00:00
}
m_pPlayerState[pn]->m_PlayerOptions = po;
2004-01-12 03:47:55 +00:00
int iSumOfAttackLevels = GetSumOfActiveAttackLevels( pn );
if( iSumOfAttackLevels > 0 )
{
m_pPlayerState[pn]->m_iLastPositiveSumOfAttackLevels = iSumOfAttackLevels;
m_pPlayerState[pn]->m_fSecondsUntilAttacksPhasedOut = 10000; // any positive number that won't run out before the attacks
2004-01-12 03:47:55 +00:00
}
else
{
// don't change! m_iLastPositiveSumOfAttackLevels[p] = iSumOfAttackLevels;
m_pPlayerState[pn]->m_fSecondsUntilAttacksPhasedOut = 2; // 2 seconds to phase out
2004-01-12 03:47:55 +00:00
}
}
2003-04-07 22:07:44 +00:00
2004-01-22 02:15:55 +00:00
void GameState::RemoveAllActiveAttacks() // called on end of song
{
FOREACH_PlayerNumber( p )
2005-01-31 03:18:46 +00:00
RemoveActiveAttacksForPlayer( p );
2004-01-22 02:15:55 +00:00
}
2004-02-07 22:14:36 +00:00
int GameState::GetSumOfActiveAttackLevels( PlayerNumber pn ) const
2003-04-07 22:07:44 +00:00
{
int iSum = 0;
for( unsigned s=0; s<m_pPlayerState[pn]->m_ActiveAttacks.size(); s++ )
if( m_pPlayerState[pn]->m_ActiveAttacks[s].fSecsRemaining > 0 && m_pPlayerState[pn]->m_ActiveAttacks[s].level != NUM_ATTACK_LEVELS )
iSum += m_pPlayerState[pn]->m_ActiveAttacks[s].level;
2003-04-07 22:07:44 +00:00
return iSum;
}
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);
}
/* Adjust the fail mode based on the chosen difficulty. This must be called
* after the difficulty has been finalized (usually in ScreenSelectMusic or
* ScreenPlayerOptions), and before the fail mode is displayed or used (usually
* in ScreenSongOptions). */
void GameState::AdjustFailType()
{
/* Single song mode only. */
if( this->IsCourseMode() )
return;
/* If the player changed the fail mode explicitly, leave it alone. */
2004-02-07 22:14:36 +00:00
if( this->m_bChangedFailType )
return;
/* Find the easiest difficulty notes selected by either player. */
const Difficulty dc = GetEasiestNotesDifficulty();
/* Reset the fail type to the default. */
SongOptions so;
so.FromString( PREFSMAN->m_sDefaultModifiers );
2004-02-07 22:14:36 +00:00
this->m_SongOptions.m_FailType = so.m_FailType;
/* Easy and beginner are never harder than FAIL_END_OF_SONG. */
if(dc <= DIFFICULTY_EASY)
2004-02-07 22:14:36 +00:00
setmax(this->m_SongOptions.m_FailType, SongOptions::FAIL_END_OF_SONG);
/* If beginner's steps were chosen, and this is the first stage,
* turn off failure completely--always give a second try. */
if(dc == DIFFICULTY_BEGINNER &&
2005-02-21 17:29:49 +00:00
GAMESTATE->GetEventMode() && /* stage index is meaningless in event mode */
2004-02-07 22:14:36 +00:00
this->m_iCurrentStageIndex == 0)
setmax(this->m_SongOptions.m_FailType, SongOptions::FAIL_OFF);
}
bool GameState::ShowMarvelous() const
{
if (PREFSMAN->m_iMarvelousTiming == 2)
return true;
if (PREFSMAN->m_iMarvelousTiming == 1)
if (IsCourseMode())
return true;
return false;
2003-08-10 03:23:17 +00:00
}
void GameState::GetCharacters( vector<Character*> &apCharactersOut )
{
for( unsigned i=0; i<m_pCharacters.size(); i++ )
if( m_pCharacters[i]->m_sName.CompareNoCase("default")!=0 )
apCharactersOut.push_back( m_pCharacters[i] );
}
Character* GameState::GetRandomCharacter()
{
vector<Character*> apCharacters;
GetCharacters( apCharacters );
if( apCharacters.size() )
2003-09-27 22:23:35 +00:00
return apCharacters[rand()%apCharacters.size()];
else
return GetDefaultCharacter();
}
Character* GameState::GetDefaultCharacter()
{
for( unsigned i=0; i<m_pCharacters.size(); i++ )
{
if( m_pCharacters[i]->m_sName.CompareNoCase("default")==0 )
return m_pCharacters[i];
}
2003-09-28 00:17:08 +00:00
/* We always have the default character. */
ASSERT(0);
return NULL;
}
2003-10-19 21:38:11 +00:00
struct SongAndSteps
{
Song* pSong;
Steps* pSteps;
2003-10-21 06:12:06 +00:00
bool operator==( const SongAndSteps& other ) const { return pSong==other.pSong && pSteps==other.pSteps; }
bool operator<( const SongAndSteps& other ) const { return pSong<=other.pSong && pSteps<=other.pSteps; }
2003-10-19 21:38:11 +00:00
};
void GameState::GetRankingFeats( PlayerNumber pn, vector<RankingFeat> &asFeatsOut ) const
{
if( !IsHumanPlayer(pn) )
return;
2004-02-09 20:46:39 +00:00
Profile *pProf = PROFILEMAN->GetProfile(pn);
2004-02-07 22:14:36 +00:00
CHECKPOINT_M(ssprintf("PlayMode %i",this->m_PlayMode));
switch( this->m_PlayMode )
{
case PLAY_MODE_REGULAR:
{
2003-12-02 19:13:30 +00:00
CHECKPOINT;
2004-06-28 07:26:00 +00:00
StepsType st = this->GetCurrentStyle()->m_StepsType;
2003-10-19 21:38:11 +00: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;
2005-02-16 03:25:45 +00:00
for( unsigned i=0; i<STATSMAN->m_vPlayedStageStats.size(); i++ )
{
2005-02-16 03:25:45 +00:00
CHECKPOINT_M( ssprintf("%u/%i", i, (int)STATSMAN->m_vPlayedStageStats.size() ) );
2003-10-19 21:38:11 +00:00
SongAndSteps sas;
2005-02-16 03:25:45 +00:00
sas.pSong = STATSMAN->m_vPlayedStageStats[i].vpSongs[0];
2003-10-19 21:38:11 +00:00
ASSERT( sas.pSong );
2005-02-16 03:25:45 +00:00
sas.pSteps = STATSMAN->m_vPlayedStageStats[i].m_player[pn].vpSteps[0];
2003-10-19 21:38:11 +00:00
ASSERT( sas.pSteps );
vSongAndSteps.push_back( sas );
}
2003-12-30 05:53:22 +00:00
CHECKPOINT;
2003-10-19 21:38:11 +00:00
sort( vSongAndSteps.begin(), vSongAndSteps.end() );
vector<SongAndSteps>::iterator toDelete = unique( vSongAndSteps.begin(), vSongAndSteps.end() );
vSongAndSteps.erase(toDelete, vSongAndSteps.end());
2003-12-02 19:13:30 +00:00
CHECKPOINT;
2004-08-30 04:09:23 +00:00
for( unsigned i=0; i<vSongAndSteps.size(); i++ )
2003-10-19 21:38:11 +00:00
{
Song* pSong = vSongAndSteps[i].pSong;
Steps* pSteps = vSongAndSteps[i].pSteps;
// Find Machine Records
{
HighScoreList &hsl = PROFILEMAN->GetMachineProfile()->GetStepsHighScoreList(pSong,pSteps);
2004-08-30 04:09:23 +00:00
for( unsigned j=0; j<hsl.vHighScores.size(); j++ )
2004-02-09 06:26:13 +00:00
{
HighScore &hs = hsl.vHighScores[j];
2003-10-14 23:29:34 +00:00
2004-02-09 06:26:13 +00:00
if( hs.sName != RANKING_TO_FILL_IN_MARKER[pn] )
continue;
RankingFeat feat;
feat.Type = RankingFeat::SONG;
feat.pSong = pSong;
feat.pSteps = pSteps;
2004-02-09 06:26:13 +00:00
feat.Feat = ssprintf("MR #%d in %s %s", j+1, pSong->GetTranslitMainTitle().c_str(), DifficultyToString(pSteps->GetDifficulty()).c_str() );
feat.pStringToFill = &hs.sName;
feat.grade = hs.grade;
feat.fPercentDP = hs.fPercentDP;
feat.iScore = hs.iScore;
2004-02-09 06:26:13 +00:00
if( pSong->HasBanner() )
feat.Banner = pSong->GetBannerPath();
asFeatsOut.push_back( feat );
}
}
2003-10-19 21:38:11 +00:00
// Find Personal Records
2004-02-09 20:46:39 +00:00
if( pProf )
2003-10-19 21:38:11 +00:00
{
HighScoreList &hsl = pProf->GetStepsHighScoreList(pSong,pSteps);
2004-08-30 04:09:23 +00:00
for( unsigned j=0; j<hsl.vHighScores.size(); j++ )
2004-02-09 06:26:13 +00:00
{
HighScore &hs = hsl.vHighScores[j];
2003-10-19 21:38:11 +00:00
2004-02-09 06:26:13 +00:00
if( hs.sName != RANKING_TO_FILL_IN_MARKER[pn] )
continue;
2003-10-19 21:38:11 +00:00
RankingFeat feat;
feat.pSong = pSong;
feat.pSteps = pSteps;
feat.Type = RankingFeat::SONG;
2004-02-09 06:26:13 +00:00
feat.Feat = ssprintf("PR #%d in %s %s", j+1, pSong->GetTranslitMainTitle().c_str(), DifficultyToString(pSteps->GetDifficulty()).c_str() );
feat.pStringToFill = &hs.sName;
feat.grade = hs.grade;
feat.fPercentDP = hs.fPercentDP;
feat.iScore = hs.iScore;
// XXX: temporary hack
if( pSong->HasBackground() )
feat.Banner = pSong->GetBackgroundPath();
// if( pSong->HasBanner() )
// feat.Banner = pSong->GetBannerPath();
asFeatsOut.push_back( feat );
}
2003-10-19 21:38:11 +00:00
}
}
2003-12-02 19:13:30 +00:00
CHECKPOINT;
StageStats stats;
2005-02-16 19:40:09 +00:00
STATSMAN->GetFinalEvalStageStats( stats );
2004-02-09 06:26:13 +00:00
// Find Machine Category Records
FOREACH_RankingCategory( rc )
{
HighScoreList &hsl = PROFILEMAN->GetMachineProfile()->GetCategoryHighScoreList( st, rc );
2004-02-09 06:26:13 +00:00
for( unsigned j=0; j<hsl.vHighScores.size(); j++ )
{
2004-02-09 06:26:13 +00:00
HighScore &hs = hsl.vHighScores[j];
if( hs.sName != RANKING_TO_FILL_IN_MARKER[pn] )
continue;
RankingFeat feat;
feat.Type = RankingFeat::CATEGORY;
2004-08-30 04:09:23 +00:00
feat.Feat = ssprintf("MR #%d in Type %c (%d)", j+1, 'A'+rc, stats.GetAverageMeter(pn) );
2004-02-09 06:26:13 +00:00
feat.pStringToFill = &hs.sName;
feat.grade = GRADE_NO_DATA;
2004-08-30 04:09:23 +00:00
feat.iScore = hs.iScore;
feat.fPercentDP = hs.fPercentDP;
asFeatsOut.push_back( feat );
}
}
2004-02-09 06:26:13 +00:00
// Find Personal Category Records
FOREACH_RankingCategory( rc )
{
2004-02-09 20:46:39 +00:00
if( pProf )
{
HighScoreList &hsl = pProf->GetCategoryHighScoreList( st, rc );
2004-02-09 20:46:39 +00:00
for( unsigned j=0; j<hsl.vHighScores.size(); j++ )
{
HighScore &hs = hsl.vHighScores[j];
if( hs.sName != RANKING_TO_FILL_IN_MARKER[pn] )
continue;
RankingFeat feat;
feat.Type = RankingFeat::CATEGORY;
2004-08-30 04:09:23 +00:00
feat.Feat = ssprintf("PR #%d in Type %c (%d)", j+1, 'A'+rc, stats.GetAverageMeter(pn) );
2004-02-09 20:46:39 +00:00
feat.pStringToFill = &hs.sName;
feat.grade = GRADE_NO_DATA;
2004-08-30 04:09:23 +00:00
feat.iScore = hs.iScore;
feat.fPercentDP = hs.fPercentDP;
2004-02-09 20:46:39 +00:00
asFeatsOut.push_back( feat );
}
}
}
}
break;
case PLAY_MODE_BATTLE:
case PLAY_MODE_RAVE:
break;
case PLAY_MODE_NONSTOP:
case PLAY_MODE_ONI:
case PLAY_MODE_ENDLESS:
{
2003-12-02 19:13:30 +00:00
CHECKPOINT;
2004-06-03 08:22:02 +00:00
Course* pCourse = m_pCurCourse;
2003-12-02 19:13:30 +00:00
ASSERT( pCourse );
2004-06-03 08:22:02 +00:00
Trail *pTrail = m_pCurTrail[pn];
ASSERT( pTrail );
CourseDifficulty cd = pTrail->m_CourseDifficulty;
2003-12-21 04:19:51 +00:00
// Find Machine Records
{
2004-02-09 06:26:13 +00:00
Profile* pProfile = PROFILEMAN->GetMachineProfile();
2004-05-23 09:17:10 +00:00
HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, pTrail );
2004-02-09 06:26:13 +00:00
for( unsigned i=0; i<hsl.vHighScores.size(); i++ )
2003-12-21 04:19:51 +00:00
{
2004-02-09 06:26:13 +00:00
HighScore &hs = hsl.vHighScores[i];
if( hs.sName != RANKING_TO_FILL_IN_MARKER[pn] )
2003-12-21 04:19:51 +00:00
continue;
RankingFeat feat;
feat.Type = RankingFeat::COURSE;
feat.pCourse = pCourse;
2004-07-11 10:02:38 +00:00
feat.Feat = ssprintf("MR #%d in %s", i+1, pCourse->GetFullDisplayTitle().c_str() );
if( cd != DIFFICULTY_MEDIUM )
feat.Feat += " " + CourseDifficultyToThemedString(cd);
2004-02-09 06:26:13 +00:00
feat.pStringToFill = &hs.sName;
2003-12-21 04:19:51 +00:00
feat.grade = GRADE_NO_DATA;
2004-02-09 06:26:13 +00:00
feat.iScore = hs.iScore;
feat.fPercentDP = hs.fPercentDP;
2003-12-21 04:19:51 +00:00
if( pCourse->HasBanner() )
feat.Banner = pCourse->m_sBannerPath;
asFeatsOut.push_back( feat );
}
}
// Find Personal Records
2004-02-09 06:26:13 +00:00
if( PROFILEMAN->IsUsingProfile( pn ) )
2003-12-21 04:19:51 +00:00
{
2004-05-23 09:17:10 +00:00
HighScoreList &hsl = pProf->GetCourseHighScoreList( pCourse, pTrail );
2004-02-09 06:26:13 +00:00
for( unsigned i=0; i<hsl.vHighScores.size(); i++ )
2003-12-21 04:19:51 +00:00
{
2004-02-09 06:26:13 +00:00
HighScore& hs = hsl.vHighScores[i];
if( hs.sName != RANKING_TO_FILL_IN_MARKER[pn] )
2003-12-21 04:19:51 +00:00
continue;
2003-10-14 23:29:34 +00:00
RankingFeat feat;
feat.Type = RankingFeat::COURSE;
feat.pCourse = pCourse;
2004-07-11 10:02:38 +00:00
feat.Feat = ssprintf("PR #%d in %s", i+1, pCourse->GetFullDisplayTitle().c_str() );
2004-02-09 06:26:13 +00:00
feat.pStringToFill = &hs.sName;
2003-12-21 04:19:51 +00:00
feat.grade = GRADE_NO_DATA;
2004-02-09 06:26:13 +00:00
feat.iScore = hs.iScore;
feat.fPercentDP = hs.fPercentDP;
2003-12-21 04:19:51 +00:00
if( pCourse->HasBanner() )
feat.Banner = pCourse->m_sBannerPath;
asFeatsOut.push_back( feat );
}
}
}
break;
default:
ASSERT(0);
}
}
2004-05-31 22:42:12 +00:00
/*bool GameState::IsNameBlacklisted( CString name )
{
}*/
void GameState::StoreRankingName( PlayerNumber pn, CString name )
{
2003-10-22 10:49:58 +00:00
//
// Filter swear words from name
//
name.MakeUpper();
2004-09-25 02:50:31 +00:00
RageFile file;
if( file.Open(NAMES_BLACKLIST_FILE) )
2003-12-10 06:49:27 +00:00
{
CString line;
while (!file.AtEOF())
2003-10-22 10:49:58 +00:00
{
2004-06-06 21:04:48 +00:00
if( file.GetLine( line ) == -1 )
{
LOG->Warn( "Error reading \"%s\": %s", NAMES_BLACKLIST_FILE, file.GetError().c_str() );
break;
}
2003-12-10 06:49:27 +00:00
line.MakeUpper();
if( !line.empty() && name.Find(line) != -1 ) // name contains a bad word
{
LOG->Trace( "entered '%s' matches blacklisted item '%s'", name.c_str(), line.c_str() );
2003-12-10 06:49:27 +00:00
name = "";
break;
}
2003-10-22 10:49:58 +00:00
}
2003-12-10 06:49:27 +00:00
}
2003-10-22 10:49:58 +00:00
vector<RankingFeat> aFeats;
GetRankingFeats( pn, aFeats );
for( unsigned i=0; i<aFeats.size(); i++ )
{
*aFeats[i].pStringToFill = name;
2003-12-28 08:20:48 +00:00
// save name pointers as we fill them
m_vpsNamesThatWereFilled.push_back( aFeats[i].pStringToFill );
}
}
bool GameState::AllAreInDangerOrWorse() const
{
2004-07-31 19:14:53 +00:00
FOREACH_EnabledPlayer( p )
if( m_pPlayerState[p]->m_HealthState < PlayerState::DANGER )
2004-07-31 19:14:53 +00:00
return false;
return true;
}
bool GameState::AllAreDead() const
{
2004-07-31 19:14:53 +00:00
FOREACH_EnabledPlayer( p )
if( m_pPlayerState[p]->m_HealthState < PlayerState::DEAD )
2004-07-31 19:14:53 +00:00
return false;
return true;
}
bool GameState::AllHaveComboOf30OrMoreMisses() const
{
FOREACH_EnabledPlayer( p )
2005-02-16 03:25:45 +00:00
if( STATSMAN->m_CurStageStats.m_player[p].iCurMissCombo < 30 )
2004-07-31 19:14:53 +00:00
return false;
return true;
}
bool GameState::OneIsHot() const
{
2004-07-31 19:14:53 +00:00
FOREACH_EnabledPlayer( p )
if( m_pPlayerState[p]->m_HealthState == PlayerState::HOT )
2004-07-31 19:14:53 +00:00
return true;
return false;
}
2003-12-28 19:46:50 +00:00
bool GameState::IsTimeToPlayAttractSounds()
{
2004-03-20 19:15:06 +00:00
// if m_iNumTimesThroughAttract is negative, play attract sounds regardless
// of m_iAttractSoundFrequency.
if( m_iNumTimesThroughAttract<0 )
return true;
// 0 means "never play sound". Avoid a divide by 0 below.
if( PREFSMAN->m_iAttractSoundFrequency == 0 )
2003-12-28 19:46:50 +00:00
return false;
// play attract sounds once every m_iAttractSoundFrequency times through
if( (m_iNumTimesThroughAttract % PREFSMAN->m_iAttractSoundFrequency)==0 )
return true;
return false;
2004-01-15 00:49:59 +00:00
}
bool GameState::DifficultiesLocked()
{
2004-06-03 08:22:02 +00:00
if( GAMESTATE->m_PlayMode == PLAY_MODE_RAVE )
return true;
if( IsCourseMode() )
return PREFSMAN->m_bLockCourseDifficulties;
return false;
}
2004-06-03 08:22:02 +00:00
bool GameState::ChangePreferredDifficulty( PlayerNumber pn, Difficulty dc )
{
this->m_PreferredDifficulty[pn] = dc;
if( DifficultiesLocked() )
2004-06-06 20:57:13 +00:00
FOREACH_PlayerNumber( p )
m_PreferredDifficulty[p] = m_PreferredDifficulty[pn];
return true;
}
2004-07-11 01:58:55 +00:00
void GameState::GetDifficultiesToShow( set<Difficulty> &ret )
{
2004-07-11 01:58:55 +00:00
static float fExpiration = -999;
static set<Difficulty> cache;
if( RageTimer::GetTimeSinceStart() < fExpiration )
{
ret = cache;
return;
}
CStringArray asDiff;
split( DIFFICULTIES_TO_SHOW, ",", asDiff );
2004-07-11 01:58:55 +00:00
ASSERT( asDiff.size() > 0 );
cache.clear();
for( unsigned i = 0; i < asDiff.size(); ++i )
{
2004-07-11 01:58:55 +00:00
Difficulty d = StringToDifficulty(asDiff[i]);
if( d == DIFFICULTY_INVALID )
2004-07-11 01:58:55 +00:00
RageException::Throw( "Unknown difficulty \"%s\" in CourseDifficultiesToShow", asDiff[i].c_str() );
cache.insert( d );
}
2004-07-11 01:58:55 +00:00
fExpiration = RageTimer::GetTimeSinceStart()+1;
ret = cache;
}
bool GameState::ChangePreferredDifficulty( PlayerNumber pn, int dir )
{
set<Difficulty> asDiff;
GetDifficultiesToShow( asDiff );
2004-07-11 01:58:55 +00:00
Difficulty d = m_PreferredDifficulty[pn];
while( 1 )
{
d = (Difficulty)(d+dir);
if( d < 0 || d >= NUM_DIFFICULTIES )
return false;
if( asDiff.find(d) == asDiff.end() )
continue; /* not available */
}
2004-07-11 01:58:55 +00:00
return ChangePreferredDifficulty( pn, d );
}
2004-07-11 01:58:55 +00:00
void GameState::GetCourseDifficultiesToShow( set<CourseDifficulty> &ret )
{
2004-05-21 05:21:03 +00:00
static float fExpiration = -999;
static set<CourseDifficulty> cache;
if( RageTimer::GetTimeSinceStart() < fExpiration )
{
ret = cache;
return;
}
CStringArray asDiff;
split( COURSE_DIFFICULTIES_TO_SHOW, ",", asDiff );
ASSERT( asDiff.size() > 0 );
2004-05-21 05:21:03 +00:00
cache.clear();
for( unsigned i = 0; i < asDiff.size(); ++i )
{
CourseDifficulty cd = StringToCourseDifficulty(asDiff[i]);
2004-07-11 01:58:55 +00:00
if( cd == DIFFICULTY_INVALID )
RageException::Throw( "Unknown difficulty \"%s\" in CourseDifficultiesToShow", asDiff[i].c_str() );
2004-05-21 05:21:03 +00:00
cache.insert( cd );
}
2004-05-21 05:21:03 +00:00
fExpiration = RageTimer::GetTimeSinceStart()+1;
ret = cache;
}
2004-06-03 08:22:02 +00:00
bool GameState::ChangePreferredCourseDifficulty( PlayerNumber pn, CourseDifficulty cd )
{
m_PreferredCourseDifficulty[pn] = cd;
if( PREFSMAN->m_bLockCourseDifficulties )
2004-06-06 20:57:13 +00:00
FOREACH_PlayerNumber( p )
2004-06-03 08:22:02 +00:00
m_PreferredCourseDifficulty[p] = m_PreferredCourseDifficulty[pn];
return true;
}
bool GameState::ChangePreferredCourseDifficulty( PlayerNumber pn, int dir )
{
2004-06-03 21:33:37 +00:00
/* If we have a course selected, only choose among difficulties available in the course. */
const Course *pCourse = this->m_pCurCourse;
2004-05-21 05:21:03 +00:00
set<CourseDifficulty> asDiff;
GetCourseDifficultiesToShow( asDiff );
CourseDifficulty cd = m_PreferredCourseDifficulty[pn];
2004-06-03 21:33:37 +00:00
while( 1 )
{
cd = (CourseDifficulty)(cd+dir);
if( cd < 0 || cd >= NUM_DIFFICULTIES )
return false;
2004-06-03 21:33:37 +00:00
if( asDiff.find(cd) == asDiff.end() )
continue; /* not available */
2004-06-28 07:26:00 +00:00
if( !pCourse || pCourse->GetTrail( GAMESTATE->GetCurrentStyle()->m_StepsType, cd ) )
2004-06-03 21:33:37 +00:00
break;
}
2004-06-03 08:22:02 +00:00
return ChangePreferredCourseDifficulty( pn, cd );
}
bool GameState::IsCourseDifficultyShown( CourseDifficulty cd )
{
2004-05-21 05:21:03 +00:00
set<CourseDifficulty> asDiff;
GetCourseDifficultiesToShow( asDiff );
return asDiff.find(cd) != asDiff.end();
}
Difficulty GameState::GetEasiestNotesDifficulty() const
{
Difficulty dc = DIFFICULTY_INVALID;
FOREACH_HumanPlayer( p )
{
2004-05-24 06:12:17 +00:00
if( this->m_pCurSteps[p] == NULL )
{
LOG->Warn( "GetEasiestNotesDifficulty called but p%i hasn't chosen notes", p+1 );
continue;
}
2004-05-24 06:12:17 +00:00
dc = min( dc, this->m_pCurSteps[p]->GetDifficulty() );
}
return dc;
}
2005-02-21 17:29:49 +00:00
bool GameState::GetEventMode()
2004-02-14 23:26:32 +00:00
{
2005-02-21 17:29:49 +00:00
return m_bTemporaryEventMode || PREFSMAN->m_bEventMode;
}
2004-02-14 23:26:32 +00:00
2005-02-21 17:29:49 +00:00
CoinMode GameState::GetCoinMode()
{
if( GetEventMode() && PREFSMAN->m_CoinMode == COIN_PAY )
return COIN_FREE;
else
return PREFSMAN->m_CoinMode;
}
Premium GameState::GetPremium()
{
if( GetEventMode() )
return PREMIUM_NONE;
else
return PREFSMAN->m_Premium;
2004-02-14 23:26:32 +00:00
}
// lua start
#include "LuaBinding.h"
template<class T>
class LunaGameState : public Luna<T>
{
public:
LunaGameState() { LUA->Register( Register ); }
2005-02-22 23:08:46 +00:00
static int IsPlayerEnabled( T* p, lua_State *L ) { lua_pushboolean(L, p->IsPlayerEnabled((PlayerNumber)IArg(1)) ); return 1; }
static int IsHumanPlayer( T* p, lua_State *L ) { lua_pushboolean(L, p->IsHumanPlayer((PlayerNumber)IArg(1)) ); return 1; }
static int GetPlayerDisplayName( T* p, lua_State *L ) { lua_pushstring(L, p->GetPlayerDisplayName((PlayerNumber)IArg(1)) ); return 1; }
static int GetMasterPlayerNumber( T* p, lua_State *L ) { lua_pushnumber(L, p->m_MasterPlayerNumber ); return 1; }
static int ApplyGameCommand( T* p, lua_State *L )
{
PlayerNumber pn = PLAYER_INVALID;
if( lua_gettop(L) >= 2 && !lua_isnil(L,2) )
pn = (PlayerNumber)(IArg(2)-1);
p->ApplyGameCommand(SArg(1),pn);
return 0;
}
2005-02-22 23:06:51 +00:00
static int GetCurrentSong( T* p, lua_State *L ) { if(p->m_pCurSong) p->m_pCurSong->PushSelf(L); else lua_pushnil(L); return 1; }
2005-02-22 23:08:46 +00:00
static int SetCurrentSong( T* p, lua_State *L )
{
if( lua_isnil(L,1) ) { p->m_pCurSong = NULL; }
else { Song *pS = Luna<Song>::check(L,1); p->m_pCurSong = pS; }
return 0;
}
static int GetCurrentSteps( T* p, lua_State *L )
{
PlayerNumber pn = (PlayerNumber)IArg(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 = (PlayerNumber)IArg(1);
Steps *pSteps = p->m_pCurSteps[pn];
if( lua_isnil(L,2) ) { p->m_pCurSteps[pn] = NULL; }
else { Song *pS = Luna<Song>::check(L,2); p->m_pCurSteps[pn] = pS; }
return 0;
}
2005-02-21 17:29:49 +00:00
static int GetCurrentCourse( T* p, lua_State *L ) { if(p->m_pCurCourse) p->m_pCurCourse->PushSelf(L); else lua_pushnil(L); return 1; }
2005-02-22 23:08:46 +00:00
static int SetCurrentCourse( T* p, lua_State *L )
{
if( lua_isnil(L,1) ) { p->m_pCurCourse = NULL; }
2005-02-22 23:34:18 +00:00
else { Course *pC = Luna<Course>::check(L,1); p->m_pCurCourse = pC; }
2005-02-22 23:08:46 +00:00
return 0;
}
2005-02-21 17:29:49 +00:00
static int SetTemporaryEventMode( T* p, lua_State *L ) { p->m_bTemporaryEventMode = BArg(1); return 0; }
static void Register(lua_State *L)
{
ADD_METHOD( IsPlayerEnabled )
ADD_METHOD( IsHumanPlayer )
ADD_METHOD( GetPlayerDisplayName )
ADD_METHOD( GetMasterPlayerNumber )
ADD_METHOD( ApplyGameCommand )
2005-02-22 23:06:51 +00:00
ADD_METHOD( GetCurrentSong )
ADD_METHOD( SetCurrentSong )
2005-02-21 17:29:49 +00:00
ADD_METHOD( GetCurrentCourse )
ADD_METHOD( SetCurrentCourse )
ADD_METHOD( SetTemporaryEventMode )
Luna<T>::Register( L );
// add global singleton
ASSERT( GAMESTATE );
lua_pushstring(L, "GAMESTATE");
GAMESTATE->PushSelf( LUA->L );
lua_settable(L, LUA_GLOBALSINDEX);
}
};
LUA_REGISTER_CLASS( GameState )
// lua end
2004-02-14 23:26:32 +00:00
#include "LuaFunctions.h"
2004-02-15 23:38:32 +00:00
LuaFunction_PlayerNumber( IsPlayerEnabled, GAMESTATE->IsPlayerEnabled(pn) )
2004-02-14 23:26:32 +00:00
LuaFunction_PlayerNumber( IsHumanPlayer, GAMESTATE->IsHumanPlayer(pn) )
2004-06-22 08:41:25 +00:00
LuaFunction_PlayerNumber( IsPlayerUsingProfile, PROFILEMAN->IsUsingProfile(pn) )
2004-05-06 09:48:55 +00:00
LuaFunction_PlayerNumber( IsWinner, GAMESTATE->GetStageResult(pn)==RESULT_WIN )
2004-02-14 23:26:32 +00:00
LuaFunction_NoArgs( IsCourseMode, GAMESTATE->IsCourseMode() )
LuaFunction_NoArgs( IsDemonstration, GAMESTATE->m_bDemonstrationOrJukebox )
LuaFunction_NoArgs( StageIndex, GAMESTATE->GetStageIndex() )
LuaFunction_NoArgs( NumStagesLeft, GAMESTATE->GetNumStagesLeft() )
LuaFunction_NoArgs( IsFinalStage, GAMESTATE->IsFinalStage() )
LuaFunction_NoArgs( IsExtraStage, GAMESTATE->IsExtraStage() )
LuaFunction_NoArgs( IsExtraStage2, GAMESTATE->IsExtraStage2() )
2005-01-30 03:24:46 +00:00
LuaFunction_NoArgs( GetStageText, GAMESTATE->GetStageText() )
2004-02-14 23:26:32 +00:00
LuaFunction_NoArgs( CourseSongIndex, GAMESTATE->GetCourseSongIndex() )
LuaFunction_NoArgs( PlayModeName, PlayModeToString(GAMESTATE->m_PlayMode) )
2004-06-28 07:26:00 +00:00
LuaFunction_NoArgs( CurStyleName, CString( GAMESTATE->m_pCurStyle == NULL ? "none": GAMESTATE->GetCurrentStyle()->m_szName ) )
LuaFunction_NoArgs( GetNumPlayersEnabled, GAMESTATE->GetNumPlayersEnabled() )
2004-03-20 17:53:04 +00:00
LuaFunction_NoArgs( PlayerUsingBothSides, GAMESTATE->PlayerUsingBothSides() )
LuaFunction_NoArgs( GetEasiestNotesDifficulty, GAMESTATE->GetEasiestNotesDifficulty() )
LuaFunction_Str( GetEnv, GAMESTATE->m_mapEnv[str] )
LuaFunction_StrStr( SetEnv, GAMESTATE->m_mapEnv[str1] = str2 )
2004-02-14 23:26:32 +00:00
/* Return an integer into SONGMAN->m_pSongs. This lets us do input checking, which we
* can't easily do if we return pointers. */
LuaFunction_NoArgs( CurSong, GAMESTATE->m_pCurSong )
2004-05-24 06:12:17 +00:00
LuaFunction_PlayerNumber( CurSteps, GAMESTATE->m_pCurSteps[pn] )
2004-02-14 23:26:32 +00:00
2005-02-21 17:29:49 +00:00
bool PlayerIsUsingModifier( PlayerNumber pn, const CString sModifier )
{
PlayerOptions po = GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions;
SongOptions so = GAMESTATE->m_SongOptions;
po.FromString( sModifier );
so.FromString( sModifier );
return po == GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions && so == GAMESTATE->m_SongOptions;
}
2004-02-14 23:26:32 +00:00
int LuaFunc_UsingModifier( lua_State *L )
{
REQ_ARGS( "UsingModifier", 2 );
REQ_ARG_NUMBER_RANGE( "UsingModifier", 1, 1, NUM_PLAYERS );
REQ_ARG( "UsingModifier", 2, string );
const PlayerNumber pn = (PlayerNumber) (int(lua_tonumber( L, 1 ))-1);
const CString modifier = lua_tostring( L, 2 );
LUA_RETURN( PlayerIsUsingModifier( pn, modifier ), L );
2004-02-14 23:26:32 +00:00
}
LuaFunction( UsingModifier );
2004-06-08 01:24:17 +00:00
/*
* (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.
*/