I still don't completely like this because after FinishStage(), if an extra stage has been earned, it is immediately in that stage which is sort of strange for SEval. Even worse, if an extra stage was earned in the past but not for the current stage, then the stage doesn't change. (E.g., earn extra stage but not second extra stage, or earn second extra stage.) So once an extra stage has been earned, SEval will be called twice for the same stage. A per-player m_iExtraStagesPlayed could be added to fix this, but it doesn't seem worth it.
372 lines
13 KiB
C++
372 lines
13 KiB
C++
/* GameState - Holds game data that is not saved between sessions. */
|
|
|
|
#ifndef GAMESTATE_H
|
|
#define GAMESTATE_H
|
|
|
|
#include "Attack.h"
|
|
#include "Difficulty.h"
|
|
#include "GameConstantsAndTypes.h"
|
|
#include "Grade.h"
|
|
#include "MessageManager.h"
|
|
#include "ModsGroup.h"
|
|
#include "RageTimer.h"
|
|
#include "PlayerOptions.h"
|
|
#include "SongOptions.h"
|
|
|
|
#include <map>
|
|
#include <deque>
|
|
#include <set>
|
|
|
|
class Character;
|
|
class Course;
|
|
class Game;
|
|
struct lua_State;
|
|
class LuaTable;
|
|
class PlayerState;
|
|
class PlayerOptions;
|
|
class Profile;
|
|
class Song;
|
|
class Steps;
|
|
class StageStats;
|
|
class Style;
|
|
class TimingData;
|
|
class Trail;
|
|
|
|
class GameState
|
|
{
|
|
public:
|
|
GameState();
|
|
~GameState();
|
|
void Reset();
|
|
void ResetPlayer( PlayerNumber pn );
|
|
void ApplyCmdline(); // called by Reset
|
|
void ApplyGameCommand( const RString &sCommand, PlayerNumber pn=PLAYER_INVALID );
|
|
void BeginGame(); // called when first player joins
|
|
void JoinPlayer( PlayerNumber pn );
|
|
void UnjoinPlayer( PlayerNumber pn );
|
|
bool JoinInput( PlayerNumber pn );
|
|
void LoadProfiles( bool bLoadEdits = true );
|
|
void SaveProfiles();
|
|
void SaveProfile( PlayerNumber pn );
|
|
bool HaveProfileToLoad();
|
|
bool HaveProfileToSave();
|
|
void SaveLocalData();
|
|
void LoadCurrentSettingsFromProfile( PlayerNumber pn );
|
|
void SaveCurrentSettingsToProfile( PlayerNumber pn ); // called at the beginning of each stage
|
|
Song* GetDefaultSong() const;
|
|
|
|
void Update( float fDelta );
|
|
|
|
|
|
//
|
|
// Main state info
|
|
//
|
|
void SetCurGame( const Game *pGame ); // Call this instead of m_pCurGame.Set to make sure PREFSMAN->m_sCurrentGame stays in sync
|
|
BroadcastOnChangePtr<const Game> m_pCurGame;
|
|
BroadcastOnChangePtr<const Style> m_pCurStyle;
|
|
bool m_bSideIsJoined[NUM_PLAYERS]; // left side, right side
|
|
MultiPlayerStatus m_MultiPlayerStatus[NUM_MultiPlayer];
|
|
BroadcastOnChange<PlayMode> m_PlayMode; // many screens display different info depending on this value
|
|
int m_iCoins; // not "credits"
|
|
PlayerNumber m_MasterPlayerNumber; // used in Styles where one player controls both sides
|
|
bool m_bMultiplayer;
|
|
bool DifficultiesLocked() const;
|
|
bool ChangePreferredDifficultyAndStepsType( PlayerNumber pn, Difficulty dc, StepsType st );
|
|
bool ChangePreferredDifficulty( PlayerNumber pn, int dir );
|
|
bool ChangePreferredCourseDifficultyAndStepsType( PlayerNumber pn, CourseDifficulty cd, StepsType st );
|
|
bool ChangePreferredCourseDifficulty( PlayerNumber pn, int dir );
|
|
bool IsCourseDifficultyShown( CourseDifficulty cd );
|
|
Difficulty GetClosestShownDifficulty( PlayerNumber pn ) const;
|
|
Difficulty GetEasiestStepsDifficulty() const;
|
|
RageTimer m_timeGameStarted; // from the moment the first player pressed Start
|
|
LuaTable *m_Environment;
|
|
|
|
/* This is set to a random number per-game/round; it can be used for a random seed. */
|
|
int m_iGameSeed, m_iStageSeed;
|
|
|
|
bool PlayersCanJoin() const; // true if it's not too late for a player to join
|
|
int GetCoinsNeededToJoin() const;
|
|
bool EnoughCreditsToJoin() const { return m_iCoins >= GetCoinsNeededToJoin(); }
|
|
int GetNumSidesJoined() const;
|
|
|
|
const Game* GetCurrentGame();
|
|
const Style* GetCurrentStyle() const;
|
|
void SetCurrentStyle( const Style *pStyle );
|
|
|
|
void GetPlayerInfo( PlayerNumber pn, bool& bIsEnabledOut, bool& bIsHumanOut );
|
|
bool IsPlayerEnabled( PlayerNumber pn ) const;
|
|
bool IsMultiPlayerEnabled( MultiPlayer mp ) const;
|
|
bool IsPlayerEnabled( const PlayerState* pPlayerState ) const;
|
|
int GetNumPlayersEnabled() const;
|
|
|
|
bool IsHumanPlayer( PlayerNumber pn ) const;
|
|
int GetNumHumanPlayers() const;
|
|
PlayerNumber GetFirstHumanPlayer() const;
|
|
PlayerNumber GetFirstDisabledPlayer() const;
|
|
bool IsCpuPlayer( PlayerNumber pn ) const;
|
|
bool AnyPlayersAreCpu() const;
|
|
|
|
|
|
bool IsCourseMode() const;
|
|
bool IsBattleMode() const; /* not Rave */
|
|
|
|
bool ShowW1() const;
|
|
|
|
BroadcastOnChange<RString> m_sPreferredSongGroup; // GROUP_ALL denotes no preferred group
|
|
BroadcastOnChange<RString> m_sPreferredCourseGroup; // GROUP_ALL denotes no preferred group
|
|
bool m_bChangedFailTypeOnScreenSongOptions; // true if FailType was changed in the song options screen
|
|
BroadcastOnChange<StepsType> m_PreferredStepsType;
|
|
BroadcastOnChange1D<Difficulty,NUM_PLAYERS> m_PreferredDifficulty;
|
|
BroadcastOnChange1D<CourseDifficulty,NUM_PLAYERS> m_PreferredCourseDifficulty;// used in nonstop
|
|
BroadcastOnChange<SortOrder> m_SortOrder; // set by MusicWheel
|
|
SortOrder m_PreferredSortOrder; // used by MusicWheel
|
|
EditMode m_EditMode;
|
|
bool IsEditing() const { return m_EditMode != EditMode_Invalid; }
|
|
bool m_bDemonstrationOrJukebox; // ScreenGameplay does special stuff when this is true
|
|
bool m_bJukeboxUsesModifiers;
|
|
int m_iNumStagesOfThisSong;
|
|
// Increases every stage, doesn't reset when player continues. It's cosmetic and not used in Stage or Screen branching logic.
|
|
int m_iCurrentStageIndex;
|
|
// Num stages available for player. Resets when player joins/continues.
|
|
int m_iPlayerStageTokens[NUM_PLAYERS];
|
|
|
|
static int GetNumStagesMultiplierForSong( const Song* pSong );
|
|
static int GetNumStagesForSongAndStyleType( const Song* pSong, StyleType st );
|
|
int GetNumStagesForCurrentSongAndStepsOrCourse() const;
|
|
|
|
void BeginStage();
|
|
void CancelStage();
|
|
void CommitStageStats();
|
|
void FinishStage();
|
|
int GetNumStagesLeft( PlayerNumber pn ) const;
|
|
int GetSmallestNumStagesLeftForAnyHumanPlayer() const;
|
|
bool IsAnExtraStage() const;
|
|
bool IsExtraStage() const;
|
|
bool IsExtraStage2() const;
|
|
Stage GetCurrentStage() const;
|
|
int GetCourseSongIndex() const;
|
|
RString GetPlayerDisplayName( PlayerNumber pn ) const;
|
|
|
|
bool m_bLoadingNextSong;
|
|
int GetLoadingCourseSongIndex() const;
|
|
|
|
//
|
|
// State Info used during gameplay
|
|
//
|
|
|
|
// NULL on ScreenSelectMusic if the currently selected wheel item isn't a Song.
|
|
BroadcastOnChangePtr<Song> m_pCurSong;
|
|
// The last Song that the user manually changed to.
|
|
Song* m_pPreferredSong;
|
|
BroadcastOnChangePtr1D<Steps,NUM_PLAYERS> m_pCurSteps;
|
|
|
|
// NULL on ScreenSelectMusic if the currently selected wheel item isn't a Course.
|
|
BroadcastOnChangePtr<Course> m_pCurCourse;
|
|
// The last Course that the user manually changed to.
|
|
Course* m_pPreferredCourse;
|
|
BroadcastOnChangePtr1D<Trail,NUM_PLAYERS> m_pCurTrail;
|
|
|
|
bool m_bBackedOutOfFinalStage;
|
|
|
|
//
|
|
// Music statistics: Arcade: the current stage (one song). Oni/Endles: a single song in a course
|
|
//
|
|
// Let a lot of classes access this info here so the don't have to keep their own copies.
|
|
//
|
|
float m_fMusicSeconds; // time into the current song, not scaled by music rate
|
|
float m_fSongBeat;
|
|
float m_fSongBeatNoOffset;
|
|
float m_fCurBPS;
|
|
float m_fLightSongBeat; // g_fLightsFalloffSeconds ahead
|
|
bool m_bFreeze; // in the middle of a freeze
|
|
RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
|
|
BroadcastOnChange<bool> m_bGameplayLeadIn;
|
|
|
|
float m_fMusicSecondsVisible;
|
|
float m_fSongBeatVisible;
|
|
|
|
void GetAllUsedNoteSkins( vector<RString> &out ) const;
|
|
|
|
static const float MUSIC_SECONDS_INVALID;
|
|
|
|
void ResetMusicStatistics(); // Call this when it's time to play a new song. Clears the values above.
|
|
void UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp = RageZeroTimer );
|
|
float GetSongPercent( float beat ) const;
|
|
|
|
bool AllAreInDangerOrWorse() const;
|
|
bool OneIsHot() const;
|
|
|
|
//
|
|
// Haste
|
|
//
|
|
float m_fHasteRate; // [-1,+1]; 0 = normal speed
|
|
float m_fLastHasteUpdateMusicSeconds;
|
|
float m_fAccumulatedHasteSeconds;
|
|
|
|
// used in PLAY_MODE_BATTLE
|
|
float m_fOpponentHealthPercent;
|
|
|
|
// used in PLAY_MODE_RAVE
|
|
float m_fTugLifePercentP1;
|
|
|
|
// used in workout
|
|
bool m_bGoalComplete[NUM_PLAYERS];
|
|
|
|
void RemoveAllActiveAttacks(); // called on end of song
|
|
PlayerNumber GetBestPlayer() const;
|
|
StageResult GetStageResult( PlayerNumber pn ) const;
|
|
|
|
void ResetStageStatistics(); // Call this when it's time to play a new stage.
|
|
|
|
//
|
|
// Options stuff
|
|
//
|
|
|
|
ModsGroup<SongOptions> m_SongOptions;
|
|
|
|
// True if the current mode has changed the default NoteSkin, such as Edit/Sync Songs does.
|
|
// Note: any mode that wants to use it must set it
|
|
bool m_bDidModeChangeNoteSkin;
|
|
|
|
void GetDefaultPlayerOptions( PlayerOptions &po );
|
|
void GetDefaultSongOptions( SongOptions &so );
|
|
void ResetToDefaultSongOptions( ModsLevel l );
|
|
void ApplyPreferredModifiers( PlayerNumber pn, RString sModifiers );
|
|
void ApplyStageModifiers( PlayerNumber pn, RString sModifiers );
|
|
void ResetOptions();
|
|
|
|
bool CurrentOptionsDisqualifyPlayer( PlayerNumber pn );
|
|
bool PlayerIsUsingModifier( PlayerNumber pn, const RString &sModifier );
|
|
|
|
PlayerOptions::FailType GetPlayerFailType( const PlayerState *pPlayerState ) const;
|
|
|
|
// character stuff
|
|
Character* m_pCurCharacters[NUM_PLAYERS];
|
|
|
|
|
|
bool HasEarnedExtraStage() const { return m_bEarnedExtraStage; }
|
|
|
|
|
|
//
|
|
// Ranking Stuff
|
|
//
|
|
struct RankingFeat
|
|
{
|
|
enum { SONG, COURSE, CATEGORY } Type;
|
|
Song* pSong; // valid if Type == SONG
|
|
Steps* pSteps; // valid if Type == SONG
|
|
Course* pCourse; // valid if Type == COURSE
|
|
Grade grade;
|
|
int iScore;
|
|
float fPercentDP;
|
|
RString Banner;
|
|
RString Feat;
|
|
RString *pStringToFill;
|
|
};
|
|
|
|
void GetRankingFeats( PlayerNumber pn, vector<RankingFeat> &vFeatsOut ) const;
|
|
bool AnyPlayerHasRankingFeats() const;
|
|
void StoreRankingName( PlayerNumber pn, RString name ); // Called by name entry screens
|
|
vector<RString*> m_vpsNamesThatWereFilled; // filled on StoreRankingName,
|
|
|
|
|
|
//
|
|
// Award stuff
|
|
//
|
|
// lowest priority in front, highest priority at the back.
|
|
deque<PerDifficultyAward> m_vLastPerDifficultyAwards[NUM_PLAYERS];
|
|
deque<PeakComboAward> m_vLastPeakComboAwards[NUM_PLAYERS];
|
|
|
|
|
|
//
|
|
// Attract stuff
|
|
//
|
|
int m_iNumTimesThroughAttract; // negative means play regardless of m_iAttractSoundFrequency setting
|
|
bool IsTimeToPlayAttractSounds() const;
|
|
void VisitAttractScreen( const RString sScreenName );
|
|
|
|
//
|
|
// PlayerState
|
|
//
|
|
PlayerState* m_pPlayerState[NUM_PLAYERS];
|
|
PlayerState* m_pMultiPlayerState[NUM_MultiPlayer];
|
|
|
|
//
|
|
// Preference wrappers
|
|
//
|
|
// These options have weird interactions depending on m_bEventMode,
|
|
// so wrap them
|
|
bool m_bTemporaryEventMode;
|
|
bool IsEventMode() const;
|
|
CoinMode GetCoinMode() const;
|
|
Premium GetPremium() const;
|
|
|
|
//
|
|
// Edit stuff
|
|
//
|
|
BroadcastOnChange<StepsType> m_stEdit;
|
|
BroadcastOnChange<CourseDifficulty> m_cdEdit;
|
|
BroadcastOnChangePtr<Steps> m_pEditSourceSteps;
|
|
BroadcastOnChange<StepsType> m_stEditSource;
|
|
BroadcastOnChange<int> m_iEditCourseEntryIndex;
|
|
BroadcastOnChange<RString> m_sEditLocalProfileID;
|
|
Profile* GetEditLocalProfile();
|
|
|
|
// Workout stuff
|
|
float GetGoalPercentComplete( PlayerNumber pn );
|
|
bool IsGoalComplete( PlayerNumber pn ) { return GetGoalPercentComplete( pn ) >= 1; }
|
|
|
|
// Lua
|
|
void PushSelf( lua_State *L );
|
|
|
|
// Keep extra stage logic internal to GameState.
|
|
private:
|
|
bool HasEarnedExtraStageInternal() const;
|
|
int m_iAwardedExtraStages[NUM_PLAYERS];
|
|
bool m_bEarnedExtraStage;
|
|
|
|
};
|
|
|
|
PlayerNumber GetNextHumanPlayer( PlayerNumber pn );
|
|
PlayerNumber GetNextEnabledPlayer( PlayerNumber pn );
|
|
PlayerNumber GetNextCpuPlayer( PlayerNumber pn );
|
|
PlayerNumber GetNextPotentialCpuPlayer( PlayerNumber pn );
|
|
|
|
#define FOREACH_HumanPlayer( pn ) for( PlayerNumber pn=GetNextHumanPlayer((PlayerNumber)-1); pn!=PLAYER_INVALID; pn=GetNextHumanPlayer(pn) )
|
|
#define FOREACH_EnabledPlayer( pn ) for( PlayerNumber pn=GetNextEnabledPlayer((PlayerNumber)-1); pn!=PLAYER_INVALID; pn=GetNextEnabledPlayer(pn) )
|
|
#define FOREACH_CpuPlayer( pn ) for( PlayerNumber pn=GetNextCpuPlayer((PlayerNumber)-1); pn!=PLAYER_INVALID; pn=GetNextCpuPlayer(pn) )
|
|
#define FOREACH_PotentialCpuPlayer( pn ) for( PlayerNumber pn=GetNextPotentialCpuPlayer((PlayerNumber)-1); pn!=PLAYER_INVALID; pn=GetNextPotentialCpuPlayer(pn) )
|
|
|
|
|
|
MultiPlayer GetNextEnabledMultiPlayer( MultiPlayer mp );
|
|
#define FOREACH_EnabledMultiPlayer( mp ) for( MultiPlayer mp=GetNextEnabledMultiPlayer((MultiPlayer)-1); mp!=MultiPlayer_Invalid; mp=GetNextEnabledMultiPlayer(mp) )
|
|
|
|
extern GameState* GAMESTATE; // global and accessable from anywhere in our program
|
|
|
|
#endif
|
|
|
|
/*
|
|
* (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.
|
|
*/
|