fixed editor zooming if gui is not active
[twcon.git] / src / game / server / gamecontroller.cpp
blobf8d418c31b577ce3bc390c5b2b921cefb13eafc8
1 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2 /* If you are missing that file, acquire a complete release at teeworlds.com. */
3 #include <engine/shared/config.h>
4 #include <game/mapitems.h>
6 #include <game/generated/protocol.h>
8 #include "entities/pickup.h"
9 #include "gamecontroller.h"
10 #include "gamecontext.h"
13 IGameController::IGameController(class CGameContext *pGameServer)
15 m_pGameServer = pGameServer;
16 m_pServer = m_pGameServer->Server();
17 m_pGameType = "unknown";
20 DoWarmup(g_Config.m_SvWarmup);
21 m_GameOverTick = -1;
22 m_SuddenDeath = 0;
23 m_RoundStartTick = Server()->Tick();
24 m_RoundCount = 0;
25 m_GameFlags = 0;
26 m_aTeamscore[TEAM_RED] = 0;
27 m_aTeamscore[TEAM_BLUE] = 0;
28 m_aMapWish[0] = 0;
30 m_UnbalancedTick = -1;
31 m_ForceBalanced = false;
33 m_aNumSpawnPoints[0] = 0;
34 m_aNumSpawnPoints[1] = 0;
35 m_aNumSpawnPoints[2] = 0;
38 IGameController::~IGameController()
42 float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos)
44 float Score = 0.0f;
45 CCharacter *pC = static_cast<CCharacter *>(GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER));
46 for(; pC; pC = (CCharacter *)pC->TypeNext())
48 // team mates are not as dangerous as enemies
49 float Scoremod = 1.0f;
50 if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam)
51 Scoremod = 0.5f;
53 float d = distance(Pos, pC->m_Pos);
54 Score += Scoremod * (d == 0 ? 1000000000.0f : 1.0f/d);
57 return Score;
60 void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type)
62 // get spawn point
63 for(int i = 0; i < m_aNumSpawnPoints[Type]; i++)
65 // check if the position is occupado
66 CCharacter *aEnts[MAX_CLIENTS];
67 int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
68 vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; // start, left, up, right, down
69 int Result = -1;
70 for(int Index = 0; Index < 5 && Result == -1; ++Index)
72 Result = Index;
73 for(int c = 0; c < Num; ++c)
74 if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) ||
75 distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius)
77 Result = -1;
78 break;
81 if(Result == -1)
82 continue; // try next spawn point
84 vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result];
85 float S = EvaluateSpawnPos(pEval, P);
86 if(!pEval->m_Got || pEval->m_Score > S)
88 pEval->m_Got = true;
89 pEval->m_Score = S;
90 pEval->m_Pos = P;
95 bool IGameController::CanSpawn(int Team, vec2 *pOutPos)
97 CSpawnEval Eval;
99 // spectators can't spawn
100 if(Team == TEAM_SPECTATORS)
101 return false;
103 if(IsTeamplay())
105 Eval.m_FriendlyTeam = Team;
107 // first try own team spawn, then normal spawn and then enemy
108 EvaluateSpawnType(&Eval, 1+(Team&1));
109 if(!Eval.m_Got)
111 EvaluateSpawnType(&Eval, 0);
112 if(!Eval.m_Got)
113 EvaluateSpawnType(&Eval, 1+((Team+1)&1));
116 else
118 EvaluateSpawnType(&Eval, 0);
119 EvaluateSpawnType(&Eval, 1);
120 EvaluateSpawnType(&Eval, 2);
123 *pOutPos = Eval.m_Pos;
124 return Eval.m_Got;
128 bool IGameController::OnEntity(int Index, vec2 Pos)
130 int Type = -1;
131 int SubType = 0;
133 if(Index == ENTITY_SPAWN)
134 m_aaSpawnPoints[0][m_aNumSpawnPoints[0]++] = Pos;
135 else if(Index == ENTITY_SPAWN_RED)
136 m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos;
137 else if(Index == ENTITY_SPAWN_BLUE)
138 m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos;
139 else if(Index == ENTITY_ARMOR_1)
140 Type = POWERUP_ARMOR;
141 else if(Index == ENTITY_HEALTH_1)
142 Type = POWERUP_HEALTH;
143 else if(Index == ENTITY_WEAPON_SHOTGUN)
145 Type = POWERUP_WEAPON;
146 SubType = WEAPON_SHOTGUN;
148 else if(Index == ENTITY_WEAPON_GRENADE)
150 Type = POWERUP_WEAPON;
151 SubType = WEAPON_GRENADE;
153 else if(Index == ENTITY_WEAPON_RIFLE)
155 Type = POWERUP_WEAPON;
156 SubType = WEAPON_RIFLE;
158 else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups)
160 Type = POWERUP_NINJA;
161 SubType = WEAPON_NINJA;
164 if(Type != -1)
166 CPickup *pPickup = new CPickup(&GameServer()->m_World, Type, SubType);
167 pPickup->m_Pos = Pos;
168 return true;
171 return false;
174 void IGameController::EndRound()
176 if(m_Warmup) // game can't end when we are running warmup
177 return;
179 GameServer()->m_World.m_Paused = true;
180 m_GameOverTick = Server()->Tick();
181 m_SuddenDeath = 0;
184 void IGameController::ResetGame()
186 GameServer()->m_World.m_ResetRequested = true;
189 const char *IGameController::GetTeamName(int Team)
191 if(IsTeamplay())
193 if(Team == TEAM_RED)
194 return "red team";
195 else if(Team == TEAM_BLUE)
196 return "blue team";
198 else
200 if(Team == 0)
201 return "game";
204 return "spectators";
207 static bool IsSeparator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; }
209 void IGameController::StartRound()
211 ResetGame();
213 m_RoundStartTick = Server()->Tick();
214 m_SuddenDeath = 0;
215 m_GameOverTick = -1;
216 GameServer()->m_World.m_Paused = false;
217 m_aTeamscore[TEAM_RED] = 0;
218 m_aTeamscore[TEAM_BLUE] = 0;
219 m_ForceBalanced = false;
220 Server()->DemoRecorder_HandleAutoStart();
221 char aBuf[256];
222 str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS);
223 GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
226 void IGameController::ChangeMap(const char *pToMap)
228 str_copy(m_aMapWish, pToMap, sizeof(m_aMapWish));
229 EndRound();
232 void IGameController::CycleMap()
234 if(m_aMapWish[0] != 0)
236 char aBuf[256];
237 str_format(aBuf, sizeof(aBuf), "rotating map to %s", m_aMapWish);
238 GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
239 str_copy(g_Config.m_SvMap, m_aMapWish, sizeof(g_Config.m_SvMap));
240 m_aMapWish[0] = 0;
241 m_RoundCount = 0;
242 return;
244 if(!str_length(g_Config.m_SvMaprotation))
245 return;
247 if(m_RoundCount < g_Config.m_SvRoundsPerMap-1)
248 return;
250 // handle maprotation
251 const char *pMapRotation = g_Config.m_SvMaprotation;
252 const char *pCurrentMap = g_Config.m_SvMap;
254 int CurrentMapLen = str_length(pCurrentMap);
255 const char *pNextMap = pMapRotation;
256 while(*pNextMap)
258 int WordLen = 0;
259 while(pNextMap[WordLen] && !IsSeparator(pNextMap[WordLen]))
260 WordLen++;
262 if(WordLen == CurrentMapLen && str_comp_num(pNextMap, pCurrentMap, CurrentMapLen) == 0)
264 // map found
265 pNextMap += CurrentMapLen;
266 while(*pNextMap && IsSeparator(*pNextMap))
267 pNextMap++;
269 break;
272 pNextMap++;
275 // restart rotation
276 if(pNextMap[0] == 0)
277 pNextMap = pMapRotation;
279 // cut out the next map
280 char aBuf[512];
281 for(int i = 0; i < 512; i++)
283 aBuf[i] = pNextMap[i];
284 if(IsSeparator(pNextMap[i]) || pNextMap[i] == 0)
286 aBuf[i] = 0;
287 break;
291 // skip spaces
292 int i = 0;
293 while(IsSeparator(aBuf[i]))
294 i++;
296 m_RoundCount = 0;
298 char aBufMsg[256];
299 str_format(aBufMsg, sizeof(aBufMsg), "rotating map to %s", &aBuf[i]);
300 GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
301 str_copy(g_Config.m_SvMap, &aBuf[i], sizeof(g_Config.m_SvMap));
304 void IGameController::PostReset()
306 for(int i = 0; i < MAX_CLIENTS; i++)
308 if(GameServer()->m_apPlayers[i])
310 GameServer()->m_apPlayers[i]->Respawn();
311 GameServer()->m_apPlayers[i]->m_Score = 0;
312 GameServer()->m_apPlayers[i]->m_ScoreStartTick = Server()->Tick();
313 GameServer()->m_apPlayers[i]->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2;
318 void IGameController::OnPlayerInfoChange(class CPlayer *pP)
320 const int aTeamColors[2] = {65387, 10223467};
321 if(IsTeamplay())
323 pP->m_TeeInfos.m_UseCustomColor = 1;
324 if(pP->GetTeam() >= TEAM_RED && pP->GetTeam() <= TEAM_BLUE)
326 pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetTeam()];
327 pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetTeam()];
329 else
331 pP->m_TeeInfos.m_ColorBody = 12895054;
332 pP->m_TeeInfos.m_ColorFeet = 12895054;
338 int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon)
340 // do scoreing
341 if(!pKiller || Weapon == WEAPON_GAME)
342 return 0;
343 if(pKiller == pVictim->GetPlayer())
344 pVictim->GetPlayer()->m_Score--; // suicide
345 else
347 if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam())
348 pKiller->m_Score--; // teamkill
349 else
350 pKiller->m_Score++; // normal kill
352 if(Weapon == WEAPON_SELF)
353 pVictim->GetPlayer()->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()*3.0f;
354 return 0;
357 void IGameController::OnCharacterSpawn(class CCharacter *pChr)
359 // default health
360 pChr->IncreaseHealth(10);
362 // give default weapons
363 pChr->GiveWeapon(WEAPON_HAMMER, -1);
364 pChr->GiveWeapon(WEAPON_GUN, 10);
367 void IGameController::DoWarmup(int Seconds)
369 if(Seconds < 0)
370 m_Warmup = 0;
371 else
372 m_Warmup = Seconds*Server()->TickSpeed();
375 bool IGameController::IsFriendlyFire(int ClientID1, int ClientID2)
377 if(ClientID1 == ClientID2)
378 return false;
380 if(IsTeamplay())
382 if(!GameServer()->m_apPlayers[ClientID1] || !GameServer()->m_apPlayers[ClientID2])
383 return false;
385 if(GameServer()->m_apPlayers[ClientID1]->GetTeam() == GameServer()->m_apPlayers[ClientID2]->GetTeam())
386 return true;
389 return false;
392 bool IGameController::IsForceBalanced()
394 if(m_ForceBalanced)
396 m_ForceBalanced = false;
397 return true;
399 else
400 return false;
403 bool IGameController::CanBeMovedOnBalance(int ClientID)
405 return true;
408 void IGameController::Tick()
410 // do warmup
411 if(m_Warmup)
413 m_Warmup--;
414 if(!m_Warmup)
415 StartRound();
418 if(m_GameOverTick != -1)
420 // game over.. wait for restart
421 if(Server()->Tick() > m_GameOverTick+Server()->TickSpeed()*10)
423 CycleMap();
424 StartRound();
425 m_RoundCount++;
429 // do team-balancing
430 if (IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60)
432 GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams");
434 int aT[2] = {0,0};
435 float aTScore[2] = {0,0};
436 float aPScore[MAX_CLIENTS] = {0.0f};
437 for(int i = 0; i < MAX_CLIENTS; i++)
439 if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
441 aT[GameServer()->m_apPlayers[i]->GetTeam()]++;
442 aPScore[i] = GameServer()->m_apPlayers[i]->m_Score*Server()->TickSpeed()*60.0f/
443 (Server()->Tick()-GameServer()->m_apPlayers[i]->m_ScoreStartTick);
444 aTScore[GameServer()->m_apPlayers[i]->GetTeam()] += aPScore[i];
448 // are teams unbalanced?
449 if(absolute(aT[0]-aT[1]) >= 2)
451 int M = (aT[0] > aT[1]) ? 0 : 1;
452 int NumBalance = absolute(aT[0]-aT[1]) / 2;
456 CPlayer *pP = 0;
457 float PD = aTScore[M];
458 for(int i = 0; i < MAX_CLIENTS; i++)
460 if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i))
461 continue;
462 // remember the player who would cause lowest score-difference
463 if(GameServer()->m_apPlayers[i]->GetTeam() == M && (!pP || absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])) < PD))
465 pP = GameServer()->m_apPlayers[i];
466 PD = absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i]));
470 // move the player to the other team
471 int Temp = pP->m_LastActionTick;
472 pP->SetTeam(M^1);
473 pP->m_LastActionTick = Temp;
475 pP->Respawn();
476 pP->m_ForceBalanced = true;
477 } while (--NumBalance);
479 m_ForceBalanced = true;
481 m_UnbalancedTick = -1;
484 // check for inactive players
485 if(g_Config.m_SvInactiveKickTime > 0)
487 for(int i = 0; i < MAX_CLIENTS; ++i)
489 if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i))
491 if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60)
493 switch(g_Config.m_SvInactiveKick)
495 case 0:
497 // move player to spectator
498 GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
500 break;
501 case 1:
503 // move player to spectator if the reserved slots aren't filled yet, kick him otherwise
504 int Spectators = 0;
505 for(int j = 0; j < MAX_CLIENTS; ++j)
506 if(GameServer()->m_apPlayers[j] && GameServer()->m_apPlayers[j]->GetTeam() == TEAM_SPECTATORS)
507 ++Spectators;
508 if(Spectators >= g_Config.m_SvSpectatorSlots)
509 Server()->Kick(i, "Kicked for inactivity");
510 else
511 GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
513 break;
514 case 2:
516 // kick the player
517 Server()->Kick(i, "Kicked for inactivity");
525 DoWincheck();
529 bool IGameController::IsTeamplay() const
531 return m_GameFlags&GAMEFLAG_TEAMS;
534 void IGameController::Snap(int SnappingClient)
536 CNetObj_GameInfo *pGameInfoObj = (CNetObj_GameInfo *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo));
537 if(!pGameInfoObj)
538 return;
540 pGameInfoObj->m_GameFlags = m_GameFlags;
541 pGameInfoObj->m_GameStateFlags = 0;
542 if(m_GameOverTick != -1)
543 pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_GAMEOVER;
544 if(m_SuddenDeath)
545 pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_SUDDENDEATH;
546 if(GameServer()->m_World.m_Paused)
547 pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_PAUSED;
548 pGameInfoObj->m_RoundStartTick = m_RoundStartTick;
549 pGameInfoObj->m_WarmupTimer = m_Warmup;
551 pGameInfoObj->m_ScoreLimit = g_Config.m_SvScorelimit;
552 pGameInfoObj->m_TimeLimit = g_Config.m_SvTimelimit;
554 pGameInfoObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0;
555 pGameInfoObj->m_RoundCurrent = m_RoundCount+1;
558 int IGameController::GetAutoTeam(int NotThisID)
560 // this will force the auto balancer to work overtime aswell
561 if(g_Config.m_DbgStress)
562 return 0;
564 int aNumplayers[2] = {0,0};
565 for(int i = 0; i < MAX_CLIENTS; i++)
567 if(GameServer()->m_apPlayers[i] && i != NotThisID)
569 if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
570 aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
574 int Team = 0;
575 if(IsTeamplay())
576 Team = aNumplayers[TEAM_RED] > aNumplayers[TEAM_BLUE] ? TEAM_BLUE : TEAM_RED;
578 if(CanJoinTeam(Team, NotThisID))
579 return Team;
580 return -1;
583 bool IGameController::CanJoinTeam(int Team, int NotThisID)
585 if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS))
586 return true;
588 int aNumplayers[2] = {0,0};
589 for(int i = 0; i < MAX_CLIENTS; i++)
591 if(GameServer()->m_apPlayers[i] && i != NotThisID)
593 if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
594 aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
598 return (aNumplayers[0] + aNumplayers[1]) < g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots;
601 bool IGameController::CheckTeamBalance()
603 if(!IsTeamplay() || !g_Config.m_SvTeambalanceTime)
604 return true;
606 int aT[2] = {0, 0};
607 for(int i = 0; i < MAX_CLIENTS; i++)
609 CPlayer *pP = GameServer()->m_apPlayers[i];
610 if(pP && pP->GetTeam() != TEAM_SPECTATORS)
611 aT[pP->GetTeam()]++;
614 char aBuf[256];
615 if(absolute(aT[0]-aT[1]) >= 2)
617 str_format(aBuf, sizeof(aBuf), "Teams are NOT balanced (red=%d blue=%d)", aT[0], aT[1]);
618 GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
619 if(GameServer()->m_pController->m_UnbalancedTick == -1)
620 GameServer()->m_pController->m_UnbalancedTick = Server()->Tick();
621 return false;
623 else
625 str_format(aBuf, sizeof(aBuf), "Teams are balanced (red=%d blue=%d)", aT[0], aT[1]);
626 GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
627 GameServer()->m_pController->m_UnbalancedTick = -1;
628 return true;
632 bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam)
634 int aT[2] = {0, 0};
636 if (!IsTeamplay() || JoinTeam == TEAM_SPECTATORS || !g_Config.m_SvTeambalanceTime)
637 return true;
639 for(int i = 0; i < MAX_CLIENTS; i++)
641 CPlayer *pP = GameServer()->m_apPlayers[i];
642 if(pP && pP->GetTeam() != TEAM_SPECTATORS)
643 aT[pP->GetTeam()]++;
646 // simulate what would happen if changed team
647 aT[JoinTeam]++;
648 if (pPlayer->GetTeam() != TEAM_SPECTATORS)
649 aT[JoinTeam^1]--;
651 // there is a player-difference of at least 2
652 if(absolute(aT[0]-aT[1]) >= 2)
654 // player wants to join team with less players
655 if ((aT[0] < aT[1] && JoinTeam == TEAM_RED) || (aT[0] > aT[1] && JoinTeam == TEAM_BLUE))
656 return true;
657 else
658 return false;
660 else
661 return true;
664 void IGameController::DoWincheck()
666 if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested)
668 if(IsTeamplay())
670 // check score win condition
671 if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) ||
672 (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
674 if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE])
675 EndRound();
676 else
677 m_SuddenDeath = 1;
680 else
682 // gather some stats
683 int Topscore = 0;
684 int TopscoreCount = 0;
685 for(int i = 0; i < MAX_CLIENTS; i++)
687 if(GameServer()->m_apPlayers[i])
689 if(GameServer()->m_apPlayers[i]->m_Score > Topscore)
691 Topscore = GameServer()->m_apPlayers[i]->m_Score;
692 TopscoreCount = 1;
694 else if(GameServer()->m_apPlayers[i]->m_Score == Topscore)
695 TopscoreCount++;
699 // check score win condition
700 if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) ||
701 (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
703 if(TopscoreCount == 1)
704 EndRound();
705 else
706 m_SuddenDeath = 1;
712 int IGameController::ClampTeam(int Team)
714 if(Team < 0)
715 return TEAM_SPECTATORS;
716 if(IsTeamplay())
717 return Team&1;
718 return 0;