cleaned up gamecontroller and fixed some survival related things

This commit is contained in:
oy 2012-02-15 01:40:29 +01:00
parent ad33841deb
commit 0f456aebee
8 changed files with 335 additions and 284 deletions

View file

@ -122,6 +122,18 @@ void CHud::RenderStartCountdown()
}
}
void CHud::RenderDeadNotification()
{
if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags == 0 && m_pClient->m_Snap.m_pLocalInfo->m_Team != TEAM_SPECTATORS &&
(m_pClient->m_Snap.m_pLocalInfo->m_PlayerFlags&PLAYERFLAG_DEAD))
{
const char *pText = Localize("Wait for next round");
float FontSize = 16.0f;
float w = TextRender()->TextWidth(0, FontSize, pText, -1);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 50, FontSize, pText, -1);
}
}
void CHud::RenderSuddenDeath()
{
if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_SUDDENDEATH)
@ -329,7 +341,7 @@ void CHud::RenderWarmupTimer()
else if(NotReadyCount > 1)
str_format(aBuf, sizeof(aBuf), Localize("%d players not ready"), NotReadyCount);
else
return;
str_format(aBuf, sizeof(aBuf), Localize("wait for more players"));
}
else
{
@ -551,6 +563,7 @@ void CHud::OnRender()
RenderGameTimer();
RenderPauseTimer();
RenderStartCountdown();
RenderDeadNotification();
RenderSuddenDeath();
RenderScoreHud();
RenderWarmupTimer();

View file

@ -19,6 +19,7 @@ class CHud : public CComponent
void RenderGameTimer();
void RenderPauseTimer();
void RenderStartCountdown();
void RenderDeadNotification();
void RenderSuddenDeath();
void RenderScoreHud();
void RenderSpectatorHud();

View file

@ -233,7 +233,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
{
Graphics()->TextureSet(-1);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.25f);
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.25f*ColorAlpha);
RenderTools()->DrawRoundRect(x, y, w-20.0f, LineHeight, 15.0f);
Graphics()->QuadsEnd();
}

View file

@ -776,7 +776,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
m_VoteUpdate = true;
}
}
else if (MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused)
else if(MsgID == NETMSGTYPE_CL_SETTEAM && m_pController->IsTeamChangeAllowed())
{
CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg;
@ -975,13 +975,13 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
pPlayer->m_LastKill = Server()->Tick();
pPlayer->KillCharacter(WEAPON_SELF);
}
else if (MsgID == NETMSGTYPE_CL_READYCHANGE && g_Config.m_SvPlayerReadyMode)
else if (MsgID == NETMSGTYPE_CL_READYCHANGE)
{
if(pPlayer->m_LastReadyChange && pPlayer->m_LastReadyChange+Server()->TickSpeed()*1 > Server()->Tick())
return;
pPlayer->m_LastReadyChange = Server()->Tick();
pPlayer->m_IsReadyToPlay ^= 1;
m_pController->OnPlayerReadyChange(pPlayer);
}
}
@ -1031,7 +1031,7 @@ void CGameContext::ConPause(IConsole::IResult *pResult, void *pUserData)
if(pResult->NumArguments())
pSelf->m_pController->DoPause(clamp(pResult->GetInteger(0), -1, 1000));
else
pSelf->m_pController->DoPause(pSelf->m_pController->GetGameState() == IGameController::GS_PAUSED ? 0 : IGameController::TIMER_INFINITE);
pSelf->m_pController->DoPause(pSelf->m_pController->IsGamePaused() ? 0 : IGameController::TIMER_INFINITE);
}
void CGameContext::ConChangeMap(IConsole::IResult *pResult, void *pUserData)
@ -1046,7 +1046,7 @@ void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData)
if(pResult->NumArguments())
pSelf->m_pController->DoWarmup(clamp(pResult->GetInteger(0), -1, 1000));
else
pSelf->m_pController->DoRestart();
pSelf->m_pController->DoWarmup(0);
}
void CGameContext::ConBroadcast(IConsole::IResult *pResult, void *pUserData)

View file

@ -17,21 +17,22 @@ IGameController::IGameController(CGameContext *pGameServer)
m_pServer = m_pGameServer->Server();
// balancing
m_aTeamSize[TEAM_RED] = 0;
m_aTeamSize[TEAM_BLUE] = 0;
m_UnbalancedTick = TBALANCE_OK;
// game
m_GameState = GS_GAME;
m_GameState = IGS_GAME_RUNNING;
m_GameStateTimer = TIMER_INFINITE;
m_GameStartTick = Server()->Tick();
m_MatchCount = 0;
m_StartCountdownType = SCT_DEFAULT;
m_SuddenDeath = 0;
m_aTeamscore[TEAM_RED] = 0;
m_aTeamscore[TEAM_BLUE] = 0;
if(g_Config.m_SvWarmup)
SetGameState(GS_WARMUP, g_Config.m_SvWarmup);
SetGameState(IGS_WARMUP_USER, g_Config.m_SvWarmup);
else
SetGameState(GS_STARTCOUNTDOWN, TIMER_STARTCOUNTDOWN);
SetGameState(IGS_WARMUP_GAME, TIMER_INFINITE);
// info
m_GameFlags = 0;
@ -122,25 +123,17 @@ void IGameController::CheckTeamBalance()
return;
}
// calc team sizes
int aPlayerCount[NUM_TEAMS] = {0};
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++;
}
// check if teams are unbalanced
char aBuf[256];
if(absolute(aPlayerCount[TEAM_RED]-aPlayerCount[TEAM_BLUE]) >= NUM_TEAMS)
if(absolute(m_aTeamSize[TEAM_RED]-m_aTeamSize[TEAM_BLUE]) >= NUM_TEAMS)
{
str_format(aBuf, sizeof(aBuf), "Teams are NOT balanced (red=%d blue=%d)", aPlayerCount[TEAM_RED], aPlayerCount[TEAM_BLUE]);
str_format(aBuf, sizeof(aBuf), "Teams are NOT balanced (red=%d blue=%d)", m_aTeamSize[TEAM_RED], m_aTeamSize[TEAM_BLUE]);
if(m_UnbalancedTick <= TBALANCE_OK)
m_UnbalancedTick = Server()->Tick();
}
else
{
str_format(aBuf, sizeof(aBuf), "Teams are balanced (red=%d blue=%d)", aPlayerCount[TEAM_RED], aPlayerCount[TEAM_BLUE]);
str_format(aBuf, sizeof(aBuf), "Teams are balanced (red=%d blue=%d)", m_aTeamSize[TEAM_RED], m_aTeamSize[TEAM_BLUE]);
m_UnbalancedTick = TBALANCE_OK;
}
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
@ -148,12 +141,11 @@ void IGameController::CheckTeamBalance()
void IGameController::DoTeamBalance()
{
if(!IsTeamplay())
if(!IsTeamplay() || !g_Config.m_SvTeambalanceTime || absolute(m_aTeamSize[TEAM_RED]-m_aTeamSize[TEAM_BLUE]) < NUM_TEAMS)
return;
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams");
int aPlayerCount[NUM_TEAMS] = {0};
float aTeamScore[NUM_TEAMS] = {0};
float aPlayerScore[MAX_CLIENTS] = {0.0f};
@ -162,55 +154,51 @@ void IGameController::DoTeamBalance()
{
if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
{
aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++;
aPlayerScore[i] = GameServer()->m_apPlayers[i]->m_Score*Server()->TickSpeed()*60.0f/
(Server()->Tick()-GameServer()->m_apPlayers[i]->m_ScoreStartTick);
aTeamScore[GameServer()->m_apPlayers[i]->GetTeam()] += aPlayerScore[i];
}
}
// check if teams are unbalanced
if(absolute(aPlayerCount[TEAM_RED]-aPlayerCount[TEAM_BLUE]) >= NUM_TEAMS)
int BiggerTeam = (m_aTeamSize[TEAM_RED] > m_aTeamSize[TEAM_BLUE]) ? TEAM_RED : TEAM_BLUE;
int NumBalance = absolute(m_aTeamSize[TEAM_RED]-m_aTeamSize[TEAM_BLUE]) / NUM_TEAMS;
// balance teams
do
{
int BiggerTeam = (aPlayerCount[TEAM_RED] > aPlayerCount[TEAM_BLUE]) ? TEAM_RED : TEAM_BLUE;
int NumBalance = absolute(aPlayerCount[TEAM_RED]-aPlayerCount[TEAM_BLUE]) / NUM_TEAMS;
do
CPlayer *pPlayer = 0;
float ScoreDiff = aTeamScore[BiggerTeam];
for(int i = 0; i < MAX_CLIENTS; i++)
{
CPlayer *pPlayer = 0;
float ScoreDiff = aTeamScore[BiggerTeam];
for(int i = 0; i < MAX_CLIENTS; i++)
if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i))
continue;
// remember the player whom would cause lowest score-difference
if(GameServer()->m_apPlayers[i]->GetTeam() == BiggerTeam &&
(!pPlayer || absolute((aTeamScore[BiggerTeam^1]+aPlayerScore[i]) - (aTeamScore[BiggerTeam]-aPlayerScore[i])) < ScoreDiff))
{
if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i))
continue;
// remember the player whom would cause lowest score-difference
if(GameServer()->m_apPlayers[i]->GetTeam() == BiggerTeam &&
(!pPlayer || absolute((aTeamScore[BiggerTeam^1]+aPlayerScore[i]) - (aTeamScore[BiggerTeam]-aPlayerScore[i])) < ScoreDiff))
{
pPlayer = GameServer()->m_apPlayers[i];
ScoreDiff = absolute((aTeamScore[BiggerTeam^1]+aPlayerScore[i]) - (aTeamScore[BiggerTeam]-aPlayerScore[i]));
}
pPlayer = GameServer()->m_apPlayers[i];
ScoreDiff = absolute((aTeamScore[BiggerTeam^1]+aPlayerScore[i]) - (aTeamScore[BiggerTeam]-aPlayerScore[i]));
}
// move the player to the other team
int Temp = pPlayer->m_LastActionTick;
DoTeamChange(pPlayer, BiggerTeam^1);
pPlayer->m_LastActionTick = Temp;
pPlayer->Respawn();
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "You were moved to %s due to team balancing", GetTeamName(pPlayer->GetTeam()));
GameServer()->SendBroadcast(aBuf, pPlayer->GetCID());
}
while(--NumBalance);
// move the player to the other team
int Temp = pPlayer->m_LastActionTick;
DoTeamChange(pPlayer, BiggerTeam^1);
pPlayer->m_LastActionTick = Temp;
pPlayer->Respawn();
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "You were moved to %s due to team balancing", GetTeamName(pPlayer->GetTeam()));
GameServer()->SendBroadcast(aBuf, pPlayer->GetCID());
}
while(--NumBalance);
m_UnbalancedTick = TBALANCE_OK;
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, "Teams have been balanced");
}
// event
int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon)
int IGameController::OnCharacterDeath(CCharacter *pVictim, CPlayer *pKiller, int Weapon)
{
// do scoreing
if(!pKiller || Weapon == WEAPON_GAME)
@ -239,7 +227,7 @@ int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *
return 0;
}
void IGameController::OnCharacterSpawn(class CCharacter *pChr)
void IGameController::OnCharacterSpawn(CCharacter *pChr)
{
if(m_GameFlags&GAMEFLAG_SURVIVAL)
{
@ -337,10 +325,14 @@ void IGameController::OnPlayerDisconnect(CPlayer *pPlayer, const char *pReason)
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "game", aBuf);
}
m_UnbalancedTick = TBALANCE_CHECK;
if(pPlayer->GetTeam() != TEAM_SPECTATORS)
{
--m_aTeamSize[pPlayer->GetTeam()];
m_UnbalancedTick = TBALANCE_CHECK;
}
}
void IGameController::OnPlayerInfoChange(class CPlayer *pPlayer)
void IGameController::OnPlayerInfoChange(CPlayer *pPlayer)
{
const int aTeamColors[2] = {65387, 10223467};
if(IsTeamplay())
@ -359,6 +351,34 @@ void IGameController::OnPlayerInfoChange(class CPlayer *pPlayer)
}
}
void IGameController::OnPlayerReadyChange(CPlayer *pPlayer)
{
if(g_Config.m_SvPlayerReadyMode)
{
// change players ready state
pPlayer->m_IsReadyToPlay ^= 1;
// check if it effects current game state
switch(m_GameState)
{
case IGS_GAME_RUNNING:
// one player isn't ready -> pause the game
if(!pPlayer->m_IsReadyToPlay)
SetGameState(IGS_GAME_PAUSED, TIMER_INFINITE);
break;
case IGS_WARMUP_USER:
// all players are ready -> end warmup
if(GetPlayersReadyState())
SetGameState(IGS_WARMUP_USER, 0);
break;
case IGS_GAME_PAUSED:
// all players are ready -> unpause the game
if(GetPlayersReadyState())
SetGameState(IGS_GAME_PAUSED, 0);
}
}
}
void IGameController::OnReset()
{
for(int i = 0; i < MAX_CLIENTS; i++)
@ -384,7 +404,7 @@ void IGameController::DoWincheckMatch()
if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) ||
(g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_GameStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
{
if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE])
if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE] || m_GameFlags&GAMEFLAG_SURVIVAL)
EndMatch();
else
m_SuddenDeath = 1;
@ -421,125 +441,141 @@ void IGameController::DoWincheckMatch()
}
}
void IGameController::EndMatch()
{
SetGameState(GS_GAMEOVER, 10);
}
void IGameController::EndRound()
{
SetGameState(GS_ROUNDOVER, 10);
}
void IGameController::ResetGame()
{
// reset the game
GameServer()->m_World.m_ResetRequested = true;
SetGameState(GS_GAME);
SetGameState(IGS_GAME_RUNNING);
m_GameStartTick = Server()->Tick();
m_SuddenDeath = 0;
// do team-balancing
DoTeamBalance();
}
void IGameController::SetGameState(int GameState, int Seconds)
void IGameController::SetGameState(EGameState GameState, int Timer)
{
// change game state
switch(GameState)
{
case GS_WARMUP:
case IGS_WARMUP_GAME:
// game based warmup is only possible when game or any warmup is running
if(m_GameState == IGS_GAME_RUNNING || m_GameState == IGS_WARMUP_GAME || m_GameState == IGS_WARMUP_USER)
{
if(GetGameState() == GS_GAME || GetGameState() == GS_WARMUP)
if(Timer == TIMER_INFINITE)
{
if(Seconds < 0 && g_Config.m_SvPlayerReadyMode)
{
m_GameState = GS_WARMUP;
m_GameStateTimer = TIMER_INFINITE;
SetPlayersReadyState(false);
}
else if(Seconds > 0)
{
m_GameState = GS_WARMUP;
m_GameStateTimer = Seconds*Server()->TickSpeed();
}
else
SetGameState(GS_GAME);
if(GetGameState() == GS_WARMUP)
// run warmup till there're enough players
m_GameState = GameState;
m_GameStateTimer = TIMER_INFINITE;
// enable respawning in survival when activating warmup
if(m_GameFlags&GAMEFLAG_SURVIVAL)
{
for(int i = 0; i < MAX_CLIENTS; ++i)
if(GameServer()->m_apPlayers[i])
GameServer()->m_apPlayers[i]->m_RespawnDisabled = false;
}
}
}
break;
case GS_STARTCOUNTDOWN:
{
if(m_GameState == GS_GAME || m_GameState == GS_STARTCOUNTDOWN)
else if(Timer == 0)
{
m_GameState = GS_STARTCOUNTDOWN;
m_GameStateTimer = Seconds*Server()->TickSpeed();
m_StartCountdownType = SCT_DEFAULT;
GameServer()->m_World.m_Paused = true;
// start new match
StartMatch();
}
}
break;
case GS_GAME:
case IGS_WARMUP_USER:
// user based warmup is only possible when the game or a user based warmup is running
if(m_GameState == IGS_GAME_RUNNING || m_GameState == IGS_WARMUP_USER)
{
m_GameState = GS_GAME;
if(Timer != 0)
{
// start warmup
if(Timer < 0 && g_Config.m_SvPlayerReadyMode)
{
// run warmup till all players are ready
m_GameState = GameState;
m_GameStateTimer = TIMER_INFINITE;
SetPlayersReadyState(false);
}
else if(Timer > 0)
{
// run warmup for a specific time intervall
m_GameState = GameState;
m_GameStateTimer = Timer*Server()->TickSpeed();
}
// enable respawning in survival when activating warmup
if(m_GameFlags&GAMEFLAG_SURVIVAL)
{
for(int i = 0; i < MAX_CLIENTS; ++i)
if(GameServer()->m_apPlayers[i])
GameServer()->m_apPlayers[i]->m_RespawnDisabled = false;
}
}
else
{
// start new match
StartMatch();
}
}
break;
case IGS_START_COUNTDOWN:
// only possible when game, pause or start countdown is running
if(m_GameState == IGS_GAME_RUNNING || m_GameState == IGS_GAME_PAUSED || m_GameState == IGS_START_COUNTDOWN)
{
m_GameState = GameState;
m_GameStateTimer = 3*Server()->TickSpeed();
GameServer()->m_World.m_Paused = true;
}
break;
case IGS_GAME_RUNNING:
// always possible
{
m_GameState = GameState;
m_GameStateTimer = TIMER_INFINITE;
SetPlayersReadyState(true);
GameServer()->m_World.m_Paused = false;
}
break;
case GS_PAUSED:
case IGS_GAME_PAUSED:
// only possible when game is running or paused
if(m_GameState == IGS_GAME_RUNNING || m_GameState == IGS_GAME_PAUSED)
{
if(GetGameState() == GS_GAME || GetGameState() == GS_PAUSED)
if(Timer != 0)
{
if(Seconds != 0)
// start pause
if(Timer < 0)
{
if(Seconds < 0)
{
m_GameStateTimer = TIMER_INFINITE;
SetPlayersReadyState(false);
}
else
m_GameStateTimer = Seconds*Server()->TickSpeed();
// pauses infinitely till all players are ready or disabled via rcon command
m_GameStateTimer = TIMER_INFINITE;
SetPlayersReadyState(false);
}
else
{
// pauses for a specific time intervall
m_GameStateTimer = Timer*Server()->TickSpeed();
}
m_GameState = GS_PAUSED;
GameServer()->m_World.m_Paused = true;
}
else
{
bool NeedStartCountdown = m_GameStateTimer != 0;
SetGameState(GS_GAME);
if(NeedStartCountdown)
{
SetGameState(GS_STARTCOUNTDOWN, TIMER_STARTCOUNTDOWN);
m_StartCountdownType = SCT_PAUSED;
}
}
m_GameState = GameState;
GameServer()->m_World.m_Paused = true;
}
else
{
// start a countdown to end pause
SetGameState(IGS_START_COUNTDOWN);
}
}
break;
case GS_ROUNDOVER:
case IGS_END_ROUND:
case IGS_END_MATCH:
// only possible when game is running or over
if(m_GameState == IGS_GAME_RUNNING || m_GameState == IGS_END_MATCH || m_GameState == IGS_END_ROUND)
{
if(GetGameState() != GS_WARMUP && GetGameState() != GS_PAUSED)
{
m_GameState = GS_ROUNDOVER;
m_GameStateTimer = Seconds*Server()->TickSpeed();
m_SuddenDeath = 0;
GameServer()->m_World.m_Paused = true;
}
}
break;
case GS_GAMEOVER:
{
if(GetGameState() != GS_WARMUP && GetGameState() != GS_PAUSED)
{
m_GameState = GS_GAMEOVER;
m_GameStateTimer = Seconds*Server()->TickSpeed();
m_SuddenDeath = 0;
GameServer()->m_World.m_Paused = true;
}
m_GameState = GameState;
m_GameStateTimer = Timer*Server()->TickSpeed();
m_SuddenDeath = 0;
GameServer()->m_World.m_Paused = true;
}
}
}
@ -551,13 +587,11 @@ void IGameController::StartMatch()
m_aTeamscore[TEAM_RED] = 0;
m_aTeamscore[TEAM_BLUE] = 0;
if(IsTeamplay())
{
if(m_UnbalancedTick == TBALANCE_CHECK)
CheckTeamBalance();
if(m_UnbalancedTick > TBALANCE_OK)
DoTeamBalance();
}
// start countdown if there're enough players, otherwise do warmup till there're
if(HasEnoughPlayers())
SetGameState(IGS_START_COUNTDOWN);
else
SetGameState(IGS_WARMUP_GAME, TIMER_INFINITE);
Server()->DemoRecorder_HandleAutoStart();
char aBuf[256];
@ -565,6 +599,17 @@ void IGameController::StartMatch()
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
}
void IGameController::StartRound()
{
ResetGame();
// start countdown if there're enough players, otherwise abort to warmup
if(HasEnoughPlayers())
SetGameState(IGS_START_COUNTDOWN);
else
SetGameState(IGS_WARMUP_GAME, TIMER_INFINITE);
}
// general
void IGameController::Snap(int SnappingClient)
{
@ -576,25 +621,26 @@ void IGameController::Snap(int SnappingClient)
pGameInfoObj->m_GameStateFlags = 0;
pGameInfoObj->m_GameStateTimer = 0;
switch(GetGameState())
switch(m_GameState)
{
case GS_WARMUP:
case IGS_WARMUP_GAME:
case IGS_WARMUP_USER:
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_WARMUP;
pGameInfoObj->m_GameStateTimer = m_GameStateTimer;
break;
case GS_STARTCOUNTDOWN:
case IGS_START_COUNTDOWN:
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_STARTCOUNTDOWN|GAMESTATEFLAG_PAUSED;
pGameInfoObj->m_GameStateTimer = m_GameStateTimer;
break;
case GS_PAUSED:
case IGS_GAME_PAUSED:
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_PAUSED;
pGameInfoObj->m_GameStateTimer = m_GameStateTimer;
break;
case GS_ROUNDOVER:
case IGS_END_ROUND:
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_ROUNDOVER;
pGameInfoObj->m_GameStateTimer = Server()->Tick()-m_GameStartTick-10*Server()->TickSpeed()+m_GameStateTimer;
break;
case GS_GAMEOVER:
case IGS_END_MATCH:
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_GAMEOVER;
pGameInfoObj->m_GameStateTimer = Server()->Tick()-m_GameStartTick-10*Server()->TickSpeed()+m_GameStateTimer;
}
@ -613,90 +659,80 @@ void IGameController::Snap(int SnappingClient)
void IGameController::Tick()
{
// handle game states
if(GetGameState() != GS_GAME)
if(m_GameState != IGS_GAME_RUNNING)
{
if(m_GameStateTimer > 0)
--m_GameStateTimer;
switch(GetGameState())
if(m_GameStateTimer == 0)
{
case GS_WARMUP:
if(m_GameStateTimer == 0 || (m_GameStateTimer == TIMER_INFINITE && (!g_Config.m_SvPlayerReadyMode || GetPlayersReadyState())))
// timer fires
switch(m_GameState)
{
if(m_GameStateTimer == 0)
StartMatch();
else
SetGameState(GS_STARTCOUNTDOWN, TIMER_STARTCOUNTDOWN);
}
break;
case GS_STARTCOUNTDOWN:
if(m_GameStateTimer == 0)
{
switch(m_StartCountdownType)
{
case SCT_PAUSED:
SetGameState(GS_GAME);
break;
case SCT_ROUNDOVER:
ResetGame();
break;
case SCT_DEFAULT:
StartMatch();
};
}
else
++m_GameStartTick;
break;
case GS_PAUSED:
if(m_GameStateTimer == 0 || (m_GameStateTimer == TIMER_INFINITE && g_Config.m_SvPlayerReadyMode && GetPlayersReadyState()))
SetGameState(GS_PAUSED, 0);
else
++m_GameStartTick;
break;
case GS_ROUNDOVER:
if(m_GameStateTimer == 0)
{
ResetGame();
case IGS_WARMUP_USER:
// end warmup
SetGameState(IGS_WARMUP_USER, 0);
break;
case IGS_START_COUNTDOWN:
// unpause the game
SetGameState(IGS_GAME_RUNNING);
break;
case IGS_GAME_PAUSED:
// end pause
SetGameState(IGS_GAME_PAUSED, 0);
break;
case IGS_END_ROUND:
// check if the match is over otherwise start next round
DoWincheckMatch();
if(IsTeamplay())
{
if(m_UnbalancedTick == TBALANCE_CHECK)
CheckTeamBalance();
if(m_UnbalancedTick > TBALANCE_OK && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60)
DoTeamBalance();
}
SetGameState(GS_STARTCOUNTDOWN, TIMER_STARTCOUNTDOWN);
m_StartCountdownType = SCT_ROUNDOVER;
}
break;
case GS_GAMEOVER:
if(m_GameStateTimer == 0)
{
if(m_GameState != IGS_END_MATCH)
StartRound();
break;
case IGS_END_MATCH:
// start next match
CycleMap();
StartMatch();
m_MatchCount++;
}
}
else
{
// timer still running
switch(m_GameState)
{
case IGS_WARMUP_USER:
// check if player ready mode was disabled and it waits that all players are ready -> end warmup
if(!g_Config.m_SvPlayerReadyMode && m_GameStateTimer == TIMER_INFINITE)
SetGameState(IGS_WARMUP_USER, 0);
break;
case IGS_START_COUNTDOWN:
case IGS_GAME_PAUSED:
// freeze the game
++m_GameStartTick;
}
}
}
// check if the game needs to be paused
if(GetGameState() != GS_PAUSED && g_Config.m_SvPlayerReadyMode && !GetPlayersReadyState())
SetGameState(GS_PAUSED, TIMER_INFINITE);
// do team-balancing
// do team-balancing (skip this in survival, done there when a round starts)
if(IsTeamplay() && !(m_GameFlags&GAMEFLAG_SURVIVAL))
{
if(m_UnbalancedTick == TBALANCE_CHECK)
switch(m_UnbalancedTick)
{
case TBALANCE_CHECK:
CheckTeamBalance();
if(m_UnbalancedTick > TBALANCE_OK && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60)
DoTeamBalance();
break;
case TBALANCE_OK:
break;
default:
if(Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60)
DoTeamBalance();
}
}
// check for inactive players
DoActivityCheck();
// win check
if(GetGameState() == GS_GAME && !GameServer()->m_World.m_ResetRequested)
if(m_GameState == IGS_GAME_RUNNING && !GameServer()->m_World.m_ResetRequested)
{
if(m_GameFlags&GAMEFLAG_SURVIVAL)
DoWincheckRound();
@ -725,7 +761,12 @@ bool IGameController::IsFriendlyFire(int ClientID1, int ClientID2) const
bool IGameController::IsPlayerReadyMode() const
{
return g_Config.m_SvPlayerReadyMode != 0 && (m_GameStateTimer == TIMER_INFINITE && (GetGameState() == GS_WARMUP || GetGameState() == GS_PAUSED));
return g_Config.m_SvPlayerReadyMode != 0 && (m_GameStateTimer == TIMER_INFINITE && (m_GameState == IGS_WARMUP_USER || m_GameState == IGS_GAME_PAUSED));
}
bool IGameController::IsTeamChangeAllowed() const
{
return !GameServer()->m_World.m_Paused || (m_GameState == IGS_START_COUNTDOWN && m_GameStartTick == Server()->Tick());
}
const char *IGameController::GetTeamName(int Team) const
@ -737,11 +778,8 @@ const char *IGameController::GetTeamName(int Team) const
else if(Team == TEAM_BLUE)
return "blue team";
}
else
{
if(Team == 0)
return "game";
}
else if(Team == 0)
return "game";
return "spectators";
}
@ -921,7 +959,8 @@ bool IGameController::GetStartRespawnState() const
{
if(m_GameFlags&GAMEFLAG_SURVIVAL)
{
if(GetGameState() == GS_WARMUP || (GetGameState() == GS_STARTCOUNTDOWN && m_StartCountdownType == SCT_DEFAULT))
// players can always respawn during warmup or match/round start countdown
if(m_GameState == IGS_WARMUP_GAME || m_GameState == IGS_WARMUP_USER || (m_GameState == IGS_START_COUNTDOWN && m_GameStartTick == Server()->Tick()))
return false;
else
return true;
@ -936,15 +975,8 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) const
if(!IsTeamplay() || JoinTeam == TEAM_SPECTATORS || !g_Config.m_SvTeambalanceTime)
return true;
// calc team sizes
int aPlayerCount[NUM_TEAMS] = {0};
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++;
}
// simulate what would happen if the player changes team
int aPlayerCount[NUM_TEAMS] = { m_aTeamSize[TEAM_RED], m_aTeamSize[TEAM_BLUE] };
aPlayerCount[JoinTeam]++;
if(pPlayer->GetTeam() != TEAM_SPECTATORS)
aPlayerCount[JoinTeam^1]--;
@ -958,14 +990,8 @@ bool IGameController::CanJoinTeam(int Team, int NotThisID) const
if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS))
return true;
int PlayerCount = 0;
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(GameServer()->m_apPlayers[i] && i != NotThisID && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
++PlayerCount;
}
return PlayerCount < Server()->MaxClients()-g_Config.m_SvSpectatorSlots;
// check if there're enough player slots left
return m_aTeamSize[TEAM_RED]+m_aTeamSize[TEAM_BLUE] < Server()->MaxClients()-g_Config.m_SvSpectatorSlots;
}
int IGameController::ClampTeam(int Team) const
@ -983,6 +1009,7 @@ void IGameController::DoTeamChange(CPlayer *pPlayer, int Team, bool DoChatMsg)
if(Team == pPlayer->GetTeam())
return;
int OldTeam = pPlayer->GetTeam();
pPlayer->SetTeam(Team);
int ClientID = pPlayer->GetCID();
@ -996,14 +1023,26 @@ void IGameController::DoTeamChange(CPlayer *pPlayer, int Team, bool DoChatMsg)
str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' m_Team=%d", ClientID, Server()->ClientName(ClientID), Team);
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
m_UnbalancedTick = TBALANCE_CHECK;
pPlayer->m_IsReadyToPlay = GetGameState() != GS_WARMUP && GetGameState() != GS_PAUSED;
if(m_GameFlags&GAMEFLAG_SURVIVAL)
pPlayer->m_RespawnDisabled = GetStartRespawnState();
// update effected game settings
if(OldTeam != TEAM_SPECTATORS)
{
--m_aTeamSize[OldTeam];
m_UnbalancedTick = TBALANCE_CHECK;
}
if(Team != TEAM_SPECTATORS)
{
++m_aTeamSize[Team];
m_UnbalancedTick = TBALANCE_CHECK;
if(m_GameState == IGS_WARMUP_GAME && HasEnoughPlayers())
SetGameState(IGS_WARMUP_GAME, 0);
pPlayer->m_IsReadyToPlay = !IsPlayerReadyMode();
if(m_GameFlags&GAMEFLAG_SURVIVAL)
pPlayer->m_RespawnDisabled = GetStartRespawnState();
}
OnPlayerInfoChange(pPlayer);
}
int IGameController::GetStartTeam(int NotThisID)
int IGameController::GetStartTeam()
{
// this will force the auto balancer to work overtime aswell
if(g_Config.m_DbgStress)
@ -1012,20 +1051,18 @@ int IGameController::GetStartTeam(int NotThisID)
if(g_Config.m_SvTournamentMode)
return TEAM_SPECTATORS;
int aPlayerCount[NUM_TEAMS] = {0};
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(i != NotThisID && GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
aPlayerCount[GameServer()->m_apPlayers[i]->GetTeam()]++;
}
// determine new team
int Team = TEAM_RED;
if(IsTeamplay())
Team = aPlayerCount[TEAM_RED] > aPlayerCount[TEAM_BLUE] ? TEAM_BLUE : TEAM_RED;
Team = m_aTeamSize[TEAM_RED] > m_aTeamSize[TEAM_BLUE] ? TEAM_BLUE : TEAM_RED;
if(aPlayerCount[TEAM_RED]+aPlayerCount[TEAM_BLUE] < Server()->MaxClients()-g_Config.m_SvSpectatorSlots)
// check if there're enough player slots left
if(m_aTeamSize[TEAM_RED]+m_aTeamSize[TEAM_BLUE] < Server()->MaxClients()-g_Config.m_SvSpectatorSlots)
{
++m_aTeamSize[Team];
m_UnbalancedTick = TBALANCE_CHECK;
if(m_GameState == IGS_WARMUP_GAME && HasEnoughPlayers())
SetGameState(IGS_WARMUP_GAME, 0);
return Team;
}
return TEAM_SPECTATORS;

View file

@ -28,6 +28,7 @@ class IGameController
TBALANCE_CHECK=-2,
TBALANCE_OK,
};
int m_aTeamSize[NUM_TEAMS];
int m_UnbalancedTick;
virtual bool CanBeMovedOnBalance(int ClientID) const;
@ -35,22 +36,30 @@ class IGameController
void DoTeamBalance();
// game
enum
{
// start countdown types
SCT_PAUSED, // unpause the game
SCT_ROUNDOVER, // start new round
SCT_DEFAULT, // start new match
};
int m_GameState;
enum EGameState
{
// internal game states
IGS_WARMUP_GAME, // warmup started by game because there're not enough players (infinite)
IGS_WARMUP_USER, // warmup started by user action via rcon or new match (infinite or timer)
IGS_START_COUNTDOWN, // start countown to unpause the game or start match/round (tick timer)
IGS_GAME_PAUSED, // game paused (infinite or tick timer)
IGS_GAME_RUNNING, // game running (infinite)
IGS_END_MATCH, // match is over (tick timer)
IGS_END_ROUND, // round is over (tick timer)
};
EGameState m_GameState;
int m_GameStateTimer;
int m_StartCountdownType;
virtual void DoWincheckMatch();
virtual void DoWincheckRound() {};
bool HasEnoughPlayers() const { return (IsTeamplay() && m_aTeamSize[TEAM_RED] > 0 && m_aTeamSize[TEAM_BLUE] > 0) || (!IsTeamplay() && m_aTeamSize[TEAM_RED] > 1); }
void ResetGame();
void SetGameState(int GameState, int Seconds=0);
void SetGameState(EGameState GameState, int Timer=0);
void StartMatch();
void StartRound();
// map
char m_aMapWish[128];
@ -91,8 +100,8 @@ protected:
int m_SuddenDeath;
int m_aTeamscore[NUM_TEAMS];
void EndMatch();
void EndRound();
void EndMatch() { SetGameState(IGS_END_MATCH, 10); }
void EndRound() { SetGameState(IGS_END_ROUND, 10); }
// info
int m_GameFlags;
@ -138,27 +147,19 @@ public:
virtual bool OnEntity(int Index, vec2 Pos);
void OnPlayerDisconnect(class CPlayer *pPlayer, const char *pReason);
void OnPlayerInfoChange(class CPlayer *pPlayer);
void OnPlayerInfoChange(class CPlayer *pPlayer);
void OnPlayerReadyChange(class CPlayer *pPlayer);
void OnReset();
// game
enum
{
GS_WARMUP,
GS_STARTCOUNTDOWN,
GS_GAME,
GS_PAUSED,
GS_ROUNDOVER,
GS_GAMEOVER,
TIMER_INFINITE = -1,
TIMER_STARTCOUNTDOWN = 3,
};
void DoPause(int Seconds) { SetGameState(GS_PAUSED, Seconds); }
void DoRestart() { SetGameState(GS_STARTCOUNTDOWN, TIMER_STARTCOUNTDOWN); }
void DoWarmup(int Seconds) { SetGameState(GS_WARMUP, Seconds); }
void DoPause(int Seconds) { SetGameState(IGS_GAME_PAUSED, Seconds); }
void DoWarmup(int Seconds) { SetGameState(IGS_WARMUP_USER, Seconds); }
// general
virtual void Snap(int SnappingClient);
@ -166,10 +167,11 @@ public:
// info
bool IsFriendlyFire(int ClientID1, int ClientID2) const;
bool IsGamePaused() const { return m_GameState == IGS_GAME_PAUSED || m_GameState == IGS_START_COUNTDOWN; }
bool IsPlayerReadyMode() const;
bool IsTeamChangeAllowed() const;
bool IsTeamplay() const { return m_GameFlags&GAMEFLAG_TEAMS; }
int GetGameState() const { return m_GameState; }
const char *GetGameType() const { return m_pGameType; }
const char *GetTeamName(int Team) const;
@ -186,7 +188,7 @@ public:
void DoTeamChange(class CPlayer *pPlayer, int Team, bool DoChatMsg=true);
int GetStartTeam(int NotThisID);
int GetStartTeam();
};
#endif

View file

@ -174,8 +174,7 @@ void CGameWorld::Tick()
pEnt = m_pNextTraverseEntity;
}
}
else if(GameServer()->m_pController->GetGameState() == IGameController::GS_PAUSED ||
GameServer()->m_pController->GetGameState() == IGameController::GS_STARTCOUNTDOWN)
else if(GameServer()->m_pController->IsGamePaused())
{
// update all objects
for(int i = 0; i < NUM_ENTTYPES; i++)

View file

@ -19,13 +19,12 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy)
m_ScoreStartTick = Server()->Tick();
m_pCharacter = 0;
m_ClientID = ClientID;
m_Team = GameServer()->m_pController->GetStartTeam(ClientID);
m_Team = GameServer()->m_pController->GetStartTeam();
m_SpectatorID = SPEC_FREEVIEW;
m_LastActionTick = Server()->Tick();
m_TeamChangeTick = Server()->Tick();
m_Dummy = Dummy;
m_IsReadyToPlay = GameServer()->m_pController->GetGameState() != IGameController::GS_WARMUP &&
GameServer()->m_pController->GetGameState() != IGameController::GS_PAUSED;
m_IsReadyToPlay = !GameServer()->m_pController->IsPlayerReadyMode();
m_RespawnDisabled = GameServer()->m_pController->GetStartRespawnState();
m_DeadSpecMode = false;
m_Spawning = 0;
@ -71,7 +70,7 @@ void CPlayer::Tick()
m_pCharacter = 0;
}
if(GameServer()->m_pController->GetGameState() != IGameController::GS_PAUSED && GameServer()->m_pController->GetGameState() != IGameController::GS_STARTCOUNTDOWN)
if(!GameServer()->m_pController->IsGamePaused())
{
if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW)
m_ViewPos -= vec2(clamp(m_ViewPos.x-m_LatestActivity.m_TargetX, -500.0f, 500.0f), clamp(m_ViewPos.y-m_LatestActivity.m_TargetY, -400.0f, 400.0f));