//-------------------------------------------------------------------------
void CGameRulesStandardState::ChangeState( EGR_GameState newState )
{
	if (gEnv->bServer)
	{
		if (newState == EGRS_InGame)
		{
			CCCPOINT(GameRulesStandardState_EnterInGame);

#if !defined(_RELEASE) || defined(PERFORMANCE_BUILD)
			if (m_state != EGRS_InGame)
			{
				if (g_pGameCVars->g_gameRules_startCmd[0] != '\0')
				{
					gEnv->pConsole->ExecuteString(g_pGameCVars->g_gameRules_startCmd, false, true);
				}
			}
#endif // #if !defined(_RELEASE) || defined(PERFORMANCE_BUILD)

			m_pGameRules->ResetGameTime();
			IGameRulesStatsRecording	*st=m_pGameRules->GetStatsRecordingModule();
			if (st)
			{
				st->OnInGameBegin();
			}
		}
		else if (newState == EGRS_PostGame)
		{
			m_timeInPostGame = 0.0f;
			IGameRulesStatsRecording	*st=m_pGameRules->GetStatsRecordingModule();
			if (st)
			{
				st->OnPostGameBegin();
			}
#if defined(DEDICATED_SERVER) 
      if ( gEnv->bMultiplayer )
      {
        // Stop cvar probes.
        CGameRules::TPlayers players;
        m_pGameRules->GetPlayers(players);
        const int numPlayers = players.size();
        for (int i = 0; i < numPlayers; ++ i)
        {
          const EntityId playerId = players[i];
          IActor * pActor = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(playerId);
          if ( pActor )
          {

            IGameObject * pGameObject = NULL;
            INetChannel * pNetChannel = NULL;
            IDefenceContext * pDefenceContext = NULL;

            if ( (pGameObject = pActor->GetGameObject()) )
            {
              if ( (pNetChannel = pGameObject->GetNetChannel()) )
              {
                if ( (pDefenceContext = pNetChannel->GetDefenceContext()) )
                {
                  pDefenceContext->EndCvarRequests();
                }
              }
            }
          }
        }
      }
#endif

			CGameLobby *pGameLobby = g_pGame->GetGameLobby();
			if (pGameLobby)
			{
				CryLog("[GameRules] GameFinished, telling clients");
				pGameLobby->SvFinishedGame(0.0f);
			}
		}
	}

	const bool bIsClient = gEnv->IsClient();
	if (bIsClient)
	{
		if (newState == EGRS_PostGame)
		{
			m_timeInPostGame = 0.0f;

			EnterPostGameState(ePGS_Starting);
		}
		else if (newState == EGRS_InGame)
		{
			CHUDEventDispatcher::CallEvent( SHUDEvent(eHUDEvent_OnGameStart) );
		}
		else if (newState == EGRS_Reset)
		{
			m_introMessageShown = false;
		}

		ClientChangeStateFeedback(newState);
	}

	m_state = newState;

	OnStateEntered_NotifyListeners(); 

	if (newState == EGRS_PostGame)
	{
		m_pGameRules->FreezeInput(true);
		g_pGame->GetUI()->ActivateDefaultState();	// must be after settings newState to pick endgame
	
		OnGameEnd_NotifyListeners();
	}
	else if (newState == EGRS_InGame)
	{
		OnGameStart_NotifyListeners();

		if (bIsClient && g_pGameCVars->g_gameRules_preGame_StartSpawnedFrozen)
		{
			g_pGameActions->FilterMPPreGameFreeze()->Enable(false);
			g_pGame->GetUI()->ActivateDefaultState();	// must be after settings newState
		}

		// Have to explicitly call the spawning module, can't use the listener since that would make the modules
		// dependent on the initialisation order - which comes from xml
		IGameRulesSpawningModule *pSpawningModule = m_pGameRules->GetSpawningModule();
		if (pSpawningModule)
		{
			pSpawningModule->OnInGameBegin();
		}
	}

	if (gEnv->bServer)
	{
		CHANGED_NETWORK_STATE(m_pGameRules, STANDARD_STATE_ASPECT);

		if (m_state == EGRS_InGame)
		{
			// Revive players - has to be done after setting m_state since we don't revive players in PreGame
			IGameRulesSpawningModule *pSpawningModule = m_pGameRules->GetSpawningModule();
			if (pSpawningModule)
			{
				const bool bOnlyIfDead = (g_pGameCVars->g_gameRules_preGame_StartSpawnedFrozen!=0);
				pSpawningModule->ReviveAllPlayers(false, bOnlyIfDead);
			}
		}
	}
}
//-------------------------------------------------------------------------
void CGameRulesStandardState::Update( float frameTime )
{
	if (gEnv->bServer)
	{
		if(m_state == EGRS_Intro)
		{
			// We assume there is an intro, if we reach this point and an intro hasnt been registered, we know there isn't one. Onwards and upwards. 
			if(!m_pGameRules->IsIntroSequenceRegistered())
			{
				ChangeState(EGRS_PreGame); 
			}
		}

		if (m_state == EGRS_PreGame)
		{
			if (m_isStarting)
			{
				const float remainingTime = m_pGameRules->GetRemainingStartTimer();
				if (remainingTime <= 0.f)
				{
					CryLog("CGameRulesStandardState::Update(), starting game");

					ChangeState(EGRS_InGame);
				}
			}
			else
			{
				bool bOk = true;
				CGameLobby* pGameLobby = g_pGame->GetGameLobby();
				const int numPlayers = m_pGameRules->GetPlayerCount(true);

				if (pGameLobby)
				{
					if (m_isWaitingForOverrideTimer)
					{
						//-- test override timer
						m_startTimerOverrideWait -= frameTime;
						bOk = (m_startTimerOverrideWait <= 0.0f);

						if (!bOk)
						{
							bOk = true;

							//-- testing min player count doesn't apply to private games
							const bool publicGame = !pGameLobby->IsPrivateGame();
							const bool onlineGame = pGameLobby->IsOnlineGame();
							if (publicGame && onlineGame)
							{
								// Start only when we have enough players
								if (m_pGameRules->GetTeamCount() > 1)
								{
									//-- team game, insist at least 1 player per team
									const int numPlayersPerTeamMin = g_pGameCVars->g_gameRules_startTimerMinPlayersPerTeam;
									const int numPlayersTeam1 = m_pGameRules->GetTeamPlayerCount(1, true);
									const int numPlayersTeam2 = m_pGameRules->GetTeamPlayerCount(2, true);

									bOk = ((numPlayersTeam1 >= numPlayersPerTeamMin) && (numPlayersTeam2 >= numPlayersPerTeamMin));
								}
								else
								{
									//-- not a team game, so just insist on minimum 2 players
									const int numPlayersMin = g_pGameCVars->g_gameRules_startTimerMinPlayers;
									bOk = (numPlayers >= numPlayersMin);
								}
								const int numPlayersInLobby = pGameLobby->GetSessionNames().Size();
								bOk |= (numPlayersInLobby == numPlayers);
							}

							if (bOk)
							{
								//-- Enforce a percentage of lobby players in game before starting countdown
								bOk = (!gEnv->IsClient() || (g_pGame->GetClientActorId() != 0)) && CheckInitialChannelPlayers();
							}
						}
					}
					else
					{
						bOk = false;

						if (numPlayers)
						{
							//-- Start the override timer. 
							m_startTimerOverrideWait = g_pGameCVars->g_gameRules_startTimerOverrideWait;
							m_isWaitingForOverrideTimer = true;
						}
					}
				}

				if (bOk)
				{
					CryLog("CGameRulesStandardState::Update(), we have %i players, starting the game", numPlayers);
					float startTimeLength = 
#if !defined(_RELEASE)
						g_pGameCVars->g_gameRules_skipStartTimer ? 0.0f : 
#endif
						g_pGameCVars->g_gameRules_startTimerLength;

#if USE_PC_PREMATCH
					bool bDoPCPrematch = false;

					CGameRules::EPrematchState prematchState = m_pGameRules->GetPrematchState();
					if (prematchState==CGameRules::ePS_Prematch)
					{
						int numRequiredPlayers = g_pGameCVars->g_minPlayersForRankedGame - m_pGameRules->GetPlayerCount(true);
						if ((numRequiredPlayers > 0) || (pGameLobby && pGameLobby->UseLobbyTeamBalancing() && !pGameLobby->IsGameBalanced()))
						{
							bDoPCPrematch = true;

							CPlaylistManager *pPlaylistManager = g_pGame->GetPlaylistManager();
							if (pGameLobby && pPlaylistManager)
							{
								if (!pGameLobby->IsRankedGame() || pPlaylistManager->IsUsingCustomVariant())
								{
									// Private games don't do prematch
									bDoPCPrematch = false;
								}
							}

							if (bDoPCPrematch)
							{
								// If we are waiting for players on pc, spawn ingame and set a prematch state which waits for players.
								m_pGameRules->StartPrematch();
								startTimeLength = 0.f;
							}
						}

						if (!bDoPCPrematch)
						{
							m_pGameRules->SkipPrematch();
						}
					}
#endif

					m_pGameRules->ResetGameStartTimer(startTimeLength);
					StartCountdown(true);
				}
			}
		}
		else if (m_state == EGRS_PostGame)
		{
			const float prevUpdateStamp = m_timeInPostGame;
			const float timeInPost = (prevUpdateStamp + frameTime);

			const float timeToShowHUDMessage = g_pGameCVars->g_gameRules_postGame_HUDMessageTime;
			const float timeToShowTop3 = g_pGameCVars->g_gameRules_postGame_Top3Time;
			const float timeToShowScoreboard = g_pGameCVars->g_gameRules_postGame_ScoreboardTime;
			float killcamLength = m_pGameRules->GameEndedByWinningKill() ? g_pGameCVars->kc_length : 0.f;
			if (g_pGameCVars->kc_showHighlightsAtEndOfGame)
			{
				CRecordingSystem* pRecordingSystem = g_pGame->GetRecordingSystem();
				if(pRecordingSystem)
				{
					killcamLength += pRecordingSystem->GetHighlightsReelLength();
					killcamLength = min(killcamLength, 20.f);
				}
			}

			const float totalPostGameTime = timeToShowHUDMessage + timeToShowTop3 + timeToShowScoreboard + killcamLength;

			if (timeInPost > totalPostGameTime)
			{
				CRecordingSystem* pRecordingSystem = g_pGame->GetRecordingSystem();
				if (!pRecordingSystem || (!pRecordingSystem->IsPlaybackQueued() && !pRecordingSystem->IsPlayingBack()))
				{
					CGameLobby *pGameLobby = g_pGame->GetGameLobby();
					if (pGameLobby)
					{
						CryLog("[GameRules] Server trying to return to lobby");
						pGameLobby->SvFinishedGame(frameTime);
					}
				}
				else if(pRecordingSystem)
				{
					pRecordingSystem->StopHighlightReel();
				}
			}

			m_timeInPostGame = timeInPost;
		}
	}

	CPlayer * pPlayer = static_cast<CPlayer*>(g_pGame->GetIGameFramework()->GetClientActor());
	if((pPlayer && pPlayer->ShouldPlayIntro() || gEnv->bServer) && m_pGameRules->IsIntroSequenceRegistered() && !m_bHaveNotifiedIntroListeners)
	{
		// All flowgraph nodes that want to listen, should be created at this point
		OnIntroStart_NotifyListeners();
	}
	

#ifndef _RELEASE
	if(g_pGameCVars->g_hud_postgame_debug)
	{
		const char* stateName = "";
		switch(m_state)
		{
		case EGRS_Reset: { stateName = "EGRS_Reset"; break;}
		case EGRS_Intro: { stateName = "EGRS_Intro"; break;}
		case EGRS_PreGame: { stateName = "EGRS_PreGame"; break;}
		case EGRS_InGame: { stateName = "EGRS_InGame"; break;}
		case EGRS_PostGame: { stateName = "EGRS_PostGame"; break;}
		case EGRS_MAX: { stateName = "EGRS_MAX"; break;}
		}
		CryWatch("GameRulesStandardState - State = %s", stateName);

		if(m_state == EGRS_PostGame)
		{
			const char* postGameStateName = "";
			switch(m_postGameState)
			{
			case ePGS_Unknown: { postGameStateName = "ePGS_Unknown"; break; }
			case ePGS_Starting: { postGameStateName = "ePGS_Starting"; break; }
			case ePGS_HudMessage: { postGameStateName = "ePGS_HudMessage"; break; }
			case ePGS_FinalKillcam: { postGameStateName = "ePGS_FinalKillcam"; break; }
			case ePGS_HighlightReel: { postGameStateName = "ePGS_HighlightReel"; break; }
			case ePGS_Top3: { postGameStateName = "ePGS_Top3"; break; }
			case ePGS_Scoreboard: { postGameStateName = "ePGS_Scoreboard"; break; }
			}
			CryWatch("GameRulesStandardState -PostGameState = %s", postGameStateName);

		}
	}
#endif

	if (gEnv->IsClient())
	{
		if (m_state == EGRS_PreGame)
		{
			if( !gEnv->IsDedicated() )
			{
				if (m_isStarting)
				{
					const float timeTillStartInSeconds = m_pGameRules->GetRemainingStartTimer();
					SHUDEventWrapper::UpdateGameStartCountdown( ePreGameCountdown_MatchStarting, timeTillStartInSeconds );
				}
				else
				{
					SHUDEventWrapper::UpdateGameStartCountdown( ePreGameCountdown_WaitingForPlayers, 0.0f );
				}
			}
		}
		else if (m_state == EGRS_InGame && !gEnv->IsDedicated() )
		{
			if (m_introMessageShown == false)	// Show only once
			{
				CGameRules *pGameRules = g_pGame->GetGameRules();
				if (pGameRules && pGameRules->HasGameActuallyStarted())
				{
					if (EntityId localPlayerId = g_pGame->GetIGameFramework()->GetClientActorId())
					{
						int teamId = g_pGame->GetGameRules()->GetTeam(localPlayerId);
						bool bTeamGame = (pGameRules->GetTeamCount() > 1);

						IActor *pActor = g_pGame->GetIGameFramework()->GetClientActor();
						if (pActor->GetSpectatorMode()==CActor::eASM_None && !pActor->IsDead() && (!bTeamGame || teamId!=0))
						{
							if (IGameRulesPlayerStatsModule *statsModule = pGameRules->GetPlayerStatsModule())
							{
								const SGameRulesPlayerStat *stats = statsModule->GetPlayerStats(localPlayerId);
								if (stats->deaths <= 0) // Not died ever
								{
									if (m_startMatchString.empty() == false)
									{
										const char* gamemodeName = g_pGame->GetGameRules()->GetEntity()->GetClass()->GetName();

										CryFixedStringT<32> strSignalName;
										strSignalName.Format("StartGame%s", gamemodeName);
										TAudioSignalID signalId = g_pGame->GetGameAudio()->GetSignalID(strSignalName);

										CryFixedStringT<64> localisedStartString = CHUDUtils::LocalizeString( m_startMatchString.c_str() );

										if (bTeamGame)
										{
											CryFixedStringT<16> strTeamName;
											strTeamName.Format("@ui_hud_team_%d", teamId);
											
											SHUDEventWrapper::TeamMessage(strTeamName.c_str(), teamId, SHUDEventWrapper::SMsgAudio(signalId), false, true);
											SHUDEventWrapper::SimpleBannerMessage(localisedStartString.c_str(), SHUDEventWrapper::kMsgAudioNULL);
										}
										else
										{
											SHUDEventWrapper::RoundMessageNotify(localisedStartString.c_str(), SHUDEventWrapper::SMsgAudio(signalId));
										}
									}
								}
							}

							m_introMessageShown = true; // Or not if has already died, but don't check again anyway.
						}
					}
				}
			}
		}
		else if(m_state == EGRS_PostGame && !gEnv->IsDedicated())
		{
			if (!gEnv->bServer)
			{
				m_timeInPostGame += frameTime;
			}

			m_timeInCurrentPostGameState += frameTime;

			switch (m_postGameState)
			{
				case ePGS_Starting:
				{
					CRecordingSystem* pRecordingSystem = g_pGame->GetRecordingSystem();
					if (pRecordingSystem != NULL && (pRecordingSystem->IsPlayingBack() || pRecordingSystem->IsPlaybackQueued()))
					{
						// Currently showing a killcam, skip to the killcam stage of the postgame flow
						EnterPostGameState(ePGS_FinalKillcam);
					}
					else
					{
						if (m_pGameRules->GetRoundsModule())
						{
							EnterPostGameState(ePGS_Top3);
						}
						else
						{
							EnterPostGameState(ePGS_HudMessage);
						}
					}
					break;
				}
				case ePGS_HudMessage:
				{
					if (m_timeInCurrentPostGameState > g_pGameCVars->g_gameRules_postGame_HUDMessageTime)
					{
						EnterPostGameState(ePGS_FinalKillcam);
					}
					break;
				}
				case ePGS_FinalKillcam:
				{
					CRecordingSystem* pRecordingSystem = g_pGame->GetRecordingSystem();
					if (!pRecordingSystem || !(pRecordingSystem->IsPlayingBack() || (pRecordingSystem->IsPlaybackQueued() && pRecordingSystem->HasWinningKillcam())))
					{
						EnterPostGameState(ePGS_Top3);
					}
#ifndef _RELEASE
					else if(g_pGameCVars->g_hud_postgame_debug && pRecordingSystem)
					{
						CryWatch("PostGameState - Waiting for final killcam to end:"); 
						CryWatch("IsPlaybackQueued: %s, IsPlayingBack: %s", 
							pRecordingSystem->IsPlaybackQueued() ? "True" : "False", pRecordingSystem->IsPlayingBack() ? "True" : "False");
					}
#endif
					break;
				}
				case ePGS_HighlightReel:
				{
					CRecordingSystem* pRecordingSystem = g_pGame->GetRecordingSystem();
					if (!pRecordingSystem || (!pRecordingSystem->IsPlaybackQueued() && !pRecordingSystem->IsPlayingBack()))
					{
						CGameLobby *pGameLobby = g_pGame->GetGameLobby();
						if (pGameLobby)
						{
							CryLog("[GameRules] Client trying to return to lobby");
							pGameLobby->SvFinishedGame(frameTime);
							EnterPostGameState(ePGS_LeavingGame);
						}
					}
#ifndef _RELEASE
					else if(g_pGameCVars->g_hud_postgame_debug && pRecordingSystem)
					{
						CryWatch("PostGameState - Waiting for highlight reel to end:");
						CryWatch("IsPlaybackQueued: %s, IsPlayingBack: %s, IsPlayingHighlightsReel: %s", 
							pRecordingSystem->IsPlaybackQueued() ? "True" : "False", pRecordingSystem->IsPlayingBack() ? "True" : "False", pRecordingSystem->IsPlayingHighlightsReel() ? "True" : "False");
					}
#endif
					break;
				}
				case ePGS_Top3:
				{
					if (m_timeInCurrentPostGameState > g_pGameCVars->g_gameRules_postGame_Top3Time)
					{
						EnterPostGameState(ePGS_Scoreboard);
					}
					break;
				}
				case ePGS_Scoreboard:
				{
					if(m_timeInCurrentPostGameState > g_pGameCVars->g_gameRules_postGame_ScoreboardTime)
					{
						if(!m_bHasShownHighlightReel && g_pGameCVars->kc_showHighlightsAtEndOfGame)
						{
							EnterPostGameState(ePGS_HighlightReel);
						}
						else
						{
							CGameLobby *pGameLobby = g_pGame->GetGameLobby();
							if (pGameLobby)
							{
								CryLog("[GameRules] Client trying to return to lobby [No highlight reel]");
								pGameLobby->SvFinishedGame(frameTime);
							}
						}
					}
					break;
				}
			}
		}
	}
}