PageRenderTime 31ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/game/server/gamecontroller.cpp

https://gitlab.com/uDDRace/tCatch
C++ | 734 lines | 607 code | 93 blank | 34 comment | 220 complexity | 654ccfde4e11dd8f253538cca8980f1a MD5 | raw file
  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>
  5. #include <game/generated/protocol.h>
  6. #include "entities/pickup.h"
  7. #include "gamecontroller.h"
  8. #include "gamecontext.h"
  9. IGameController::IGameController(class CGameContext *pGameServer)
  10. {
  11. m_pGameServer = pGameServer;
  12. m_pServer = m_pGameServer->Server();
  13. m_pGameType = "unknown";
  14. //
  15. DoWarmup(g_Config.m_SvWarmup);
  16. m_GameOverTick = -1;
  17. m_SuddenDeath = 0;
  18. m_RoundStartTick = Server()->Tick();
  19. m_RoundCount = 0;
  20. m_GameFlags = 0;
  21. m_aTeamscore[TEAM_RED] = 0;
  22. m_aTeamscore[TEAM_BLUE] = 0;
  23. m_aMapWish[0] = 0;
  24. m_UnbalancedTick = -1;
  25. m_ForceBalanced = false;
  26. m_aNumSpawnPoints[0] = 0;
  27. m_aNumSpawnPoints[1] = 0;
  28. m_aNumSpawnPoints[2] = 0;
  29. }
  30. IGameController::~IGameController()
  31. {
  32. }
  33. float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos)
  34. {
  35. float Score = 0.0f;
  36. CCharacter *pC = static_cast<CCharacter *>(GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER));
  37. for(; pC; pC = (CCharacter *)pC->TypeNext())
  38. {
  39. // team mates are not as dangerous as enemies
  40. float Scoremod = 1.0f;
  41. if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam)
  42. Scoremod = 0.5f;
  43. float d = distance(Pos, pC->m_Pos);
  44. Score += Scoremod * (d == 0 ? 1000000000.0f : 1.0f/d);
  45. }
  46. return Score;
  47. }
  48. void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type)
  49. {
  50. // get spawn point
  51. for(int i = 0; i < m_aNumSpawnPoints[Type]; i++)
  52. {
  53. // check if the position is occupado
  54. CCharacter *aEnts[MAX_CLIENTS];
  55. int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
  56. 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
  57. int Result = -1;
  58. for(int Index = 0; Index < 5 && Result == -1; ++Index)
  59. {
  60. Result = Index;
  61. for(int c = 0; c < Num; ++c)
  62. if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) ||
  63. distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius)
  64. {
  65. Result = -1;
  66. break;
  67. }
  68. }
  69. if(Result == -1)
  70. continue; // try next spawn point
  71. vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result];
  72. float S = EvaluateSpawnPos(pEval, P);
  73. if(!pEval->m_Got || pEval->m_Score > S)
  74. {
  75. pEval->m_Got = true;
  76. pEval->m_Score = S;
  77. pEval->m_Pos = P;
  78. }
  79. }
  80. }
  81. bool IGameController::CanSpawn(int Team, vec2 *pOutPos)
  82. {
  83. CSpawnEval Eval;
  84. // spectators can't spawn
  85. if(Team == TEAM_SPECTATORS)
  86. return false;
  87. if(IsTeamplay())
  88. {
  89. Eval.m_FriendlyTeam = Team;
  90. // first try own team spawn, then normal spawn and then enemy
  91. EvaluateSpawnType(&Eval, 1+(Team&1));
  92. if(!Eval.m_Got)
  93. {
  94. EvaluateSpawnType(&Eval, 0);
  95. if(!Eval.m_Got)
  96. EvaluateSpawnType(&Eval, 1+((Team+1)&1));
  97. }
  98. }
  99. else
  100. {
  101. EvaluateSpawnType(&Eval, 0);
  102. EvaluateSpawnType(&Eval, 1);
  103. EvaluateSpawnType(&Eval, 2);
  104. }
  105. *pOutPos = Eval.m_Pos;
  106. return Eval.m_Got;
  107. }
  108. bool IGameController::OnEntity(int Index, vec2 Pos)
  109. {
  110. int Type = -1;
  111. int SubType = 0;
  112. if(Index == ENTITY_SPAWN)
  113. m_aaSpawnPoints[0][m_aNumSpawnPoints[0]++] = Pos;
  114. else if(Index == ENTITY_SPAWN_RED)
  115. m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos;
  116. else if(Index == ENTITY_SPAWN_BLUE)
  117. m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos;
  118. else if(Index == ENTITY_ARMOR_1)
  119. Type = POWERUP_ARMOR;
  120. else if(Index == ENTITY_HEALTH_1)
  121. Type = POWERUP_HEALTH;
  122. else if(Index == ENTITY_WEAPON_SHOTGUN)
  123. {
  124. Type = POWERUP_WEAPON;
  125. SubType = WEAPON_SHOTGUN;
  126. }
  127. else if(Index == ENTITY_WEAPON_GRENADE)
  128. {
  129. Type = POWERUP_WEAPON;
  130. SubType = WEAPON_GRENADE;
  131. }
  132. else if(Index == ENTITY_WEAPON_RIFLE)
  133. {
  134. Type = POWERUP_WEAPON;
  135. SubType = WEAPON_RIFLE;
  136. }
  137. else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups)
  138. {
  139. Type = POWERUP_NINJA;
  140. SubType = WEAPON_NINJA;
  141. }
  142. if(Type != -1)
  143. {
  144. CPickup *pPickup = new CPickup(&GameServer()->m_World, Type, SubType);
  145. pPickup->m_Pos = Pos;
  146. return true;
  147. }
  148. return false;
  149. }
  150. void IGameController::EndRound()
  151. {
  152. if(m_Warmup) // game can't end when we are running warmup
  153. return;
  154. GameServer()->m_World.m_Paused = true;
  155. m_GameOverTick = Server()->Tick();
  156. m_SuddenDeath = 0;
  157. }
  158. void IGameController::ResetGame()
  159. {
  160. GameServer()->m_World.m_ResetRequested = true;
  161. }
  162. const char *IGameController::GetTeamName(int Team)
  163. {
  164. if(IsTeamplay())
  165. {
  166. if(Team == TEAM_RED)
  167. return "red team";
  168. else if(Team == TEAM_BLUE)
  169. return "blue team";
  170. }
  171. else
  172. {
  173. if(Team == 0)
  174. return "game";
  175. }
  176. return "spectators";
  177. }
  178. static bool IsSeparator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; }
  179. void IGameController::StartRound()
  180. {
  181. ResetGame();
  182. m_RoundStartTick = Server()->Tick();
  183. m_SuddenDeath = 0;
  184. m_GameOverTick = -1;
  185. GameServer()->m_World.m_Paused = false;
  186. m_aTeamscore[TEAM_RED] = 0;
  187. m_aTeamscore[TEAM_BLUE] = 0;
  188. m_ForceBalanced = false;
  189. Server()->DemoRecorder_HandleAutoStart();
  190. char aBuf[256];
  191. str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS);
  192. GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
  193. }
  194. void IGameController::ChangeMap(const char *pToMap)
  195. {
  196. str_copy(m_aMapWish, pToMap, sizeof(m_aMapWish));
  197. EndRound();
  198. }
  199. void IGameController::CycleMap()
  200. {
  201. if(m_aMapWish[0] != 0)
  202. {
  203. char aBuf[256];
  204. str_format(aBuf, sizeof(aBuf), "rotating map to %s", m_aMapWish);
  205. GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
  206. str_copy(g_Config.m_SvMap, m_aMapWish, sizeof(g_Config.m_SvMap));
  207. m_aMapWish[0] = 0;
  208. m_RoundCount = 0;
  209. return;
  210. }
  211. if(!str_length(g_Config.m_SvMaprotation))
  212. return;
  213. if(m_RoundCount < g_Config.m_SvRoundsPerMap-1)
  214. {
  215. if(g_Config.m_SvRoundSwap)
  216. GameServer()->SwapTeams();
  217. return;
  218. }
  219. // handle maprotation
  220. const char *pMapRotation = g_Config.m_SvMaprotation;
  221. const char *pCurrentMap = g_Config.m_SvMap;
  222. int CurrentMapLen = str_length(pCurrentMap);
  223. const char *pNextMap = pMapRotation;
  224. while(*pNextMap)
  225. {
  226. int WordLen = 0;
  227. while(pNextMap[WordLen] && !IsSeparator(pNextMap[WordLen]))
  228. WordLen++;
  229. if(WordLen == CurrentMapLen && str_comp_num(pNextMap, pCurrentMap, CurrentMapLen) == 0)
  230. {
  231. // map found
  232. pNextMap += CurrentMapLen;
  233. while(*pNextMap && IsSeparator(*pNextMap))
  234. pNextMap++;
  235. break;
  236. }
  237. pNextMap++;
  238. }
  239. // restart rotation
  240. if(pNextMap[0] == 0)
  241. pNextMap = pMapRotation;
  242. // cut out the next map
  243. char aBuf[512] = {0};
  244. for(int i = 0; i < 511; i++)
  245. {
  246. aBuf[i] = pNextMap[i];
  247. if(IsSeparator(pNextMap[i]) || pNextMap[i] == 0)
  248. {
  249. aBuf[i] = 0;
  250. break;
  251. }
  252. }
  253. // skip spaces
  254. int i = 0;
  255. while(IsSeparator(aBuf[i]))
  256. i++;
  257. m_RoundCount = 0;
  258. char aBufMsg[256];
  259. str_format(aBufMsg, sizeof(aBufMsg), "rotating map to %s", &aBuf[i]);
  260. GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
  261. str_copy(g_Config.m_SvMap, &aBuf[i], sizeof(g_Config.m_SvMap));
  262. }
  263. void IGameController::PostReset()
  264. {
  265. for(int i = 0; i < MAX_CLIENTS; i++)
  266. {
  267. if(GameServer()->m_apPlayers[i])
  268. {
  269. GameServer()->m_apPlayers[i]->Respawn();
  270. GameServer()->m_apPlayers[i]->m_Score = 0;
  271. GameServer()->m_apPlayers[i]->m_ScoreStartTick = Server()->Tick();
  272. GameServer()->m_apPlayers[i]->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2;
  273. }
  274. }
  275. }
  276. void IGameController::OnPlayerInfoChange(class CPlayer *pP)
  277. {
  278. const int aTeamColors[2] = {65387, 10223467};
  279. if(IsTeamplay())
  280. {
  281. pP->m_TeeInfos.m_UseCustomColor = 1;
  282. if(pP->GetTeam() >= TEAM_RED && pP->GetTeam() <= TEAM_BLUE)
  283. {
  284. pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetTeam()];
  285. pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetTeam()];
  286. }
  287. else
  288. {
  289. pP->m_TeeInfos.m_ColorBody = 12895054;
  290. pP->m_TeeInfos.m_ColorFeet = 12895054;
  291. }
  292. }
  293. }
  294. int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon)
  295. {
  296. // do scoreing
  297. if(!pKiller || Weapon == WEAPON_GAME)
  298. return 0;
  299. if(pKiller == pVictim->GetPlayer())
  300. pVictim->GetPlayer()->m_Score--; // suicide
  301. else
  302. {
  303. if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam())
  304. pKiller->m_Score--; // teamkill
  305. else
  306. pKiller->m_Score++; // normal kill
  307. }
  308. if(Weapon == WEAPON_SELF)
  309. pVictim->GetPlayer()->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()*3.0f;
  310. return 0;
  311. }
  312. void IGameController::OnCharacterSpawn(class CCharacter *pChr)
  313. {
  314. // default health
  315. pChr->IncreaseHealth(10);
  316. // give default weapons
  317. pChr->GiveWeapon(WEAPON_HAMMER, -1);
  318. pChr->GiveWeapon(WEAPON_GUN, 10);
  319. }
  320. void IGameController::DoWarmup(int Seconds)
  321. {
  322. if(Seconds < 0)
  323. m_Warmup = 0;
  324. else
  325. m_Warmup = Seconds*Server()->TickSpeed();
  326. }
  327. bool IGameController::IsFriendlyFire(int ClientID1, int ClientID2)
  328. {
  329. if(ClientID1 == ClientID2)
  330. return false;
  331. if(IsTeamplay())
  332. {
  333. if(!GameServer()->m_apPlayers[ClientID1] || !GameServer()->m_apPlayers[ClientID2])
  334. return false;
  335. if(GameServer()->m_apPlayers[ClientID1]->GetTeam() == GameServer()->m_apPlayers[ClientID2]->GetTeam())
  336. return true;
  337. }
  338. return false;
  339. }
  340. bool IGameController::IsForceBalanced()
  341. {
  342. if(m_ForceBalanced)
  343. {
  344. m_ForceBalanced = false;
  345. return true;
  346. }
  347. else
  348. return false;
  349. }
  350. bool IGameController::CanBeMovedOnBalance(int ClientID)
  351. {
  352. return true;
  353. }
  354. void IGameController::Tick()
  355. {
  356. // do warmup
  357. if(m_Warmup)
  358. {
  359. m_Warmup--;
  360. if(!m_Warmup)
  361. StartRound();
  362. }
  363. if(m_GameOverTick != -1)
  364. {
  365. // game over.. wait for restart
  366. if(Server()->Tick() > m_GameOverTick+Server()->TickSpeed()*10)
  367. {
  368. CycleMap();
  369. StartRound();
  370. m_RoundCount++;
  371. }
  372. }
  373. // game is Paused
  374. if(GameServer()->m_World.m_Paused)
  375. ++m_RoundStartTick;
  376. // do team-balancing
  377. if(IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60)
  378. {
  379. GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams");
  380. int aT[2] = {0,0};
  381. float aTScore[2] = {0,0};
  382. float aPScore[MAX_CLIENTS] = {0.0f};
  383. for(int i = 0; i < MAX_CLIENTS; i++)
  384. {
  385. if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
  386. {
  387. aT[GameServer()->m_apPlayers[i]->GetTeam()]++;
  388. aPScore[i] = GameServer()->m_apPlayers[i]->m_Score*Server()->TickSpeed()*60.0f/
  389. (Server()->Tick()-GameServer()->m_apPlayers[i]->m_ScoreStartTick);
  390. aTScore[GameServer()->m_apPlayers[i]->GetTeam()] += aPScore[i];
  391. }
  392. }
  393. // are teams unbalanced?
  394. if(absolute(aT[0]-aT[1]) >= 2)
  395. {
  396. int M = (aT[0] > aT[1]) ? 0 : 1;
  397. int NumBalance = absolute(aT[0]-aT[1]) / 2;
  398. do
  399. {
  400. CPlayer *pP = 0;
  401. float PD = aTScore[M];
  402. for(int i = 0; i < MAX_CLIENTS; i++)
  403. {
  404. if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i))
  405. continue;
  406. // remember the player who would cause lowest score-difference
  407. if(GameServer()->m_apPlayers[i]->GetTeam() == M && (!pP || absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])) < PD))
  408. {
  409. pP = GameServer()->m_apPlayers[i];
  410. PD = absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i]));
  411. }
  412. }
  413. // move the player to the other team
  414. int Temp = pP->m_LastActionTick;
  415. pP->SetTeam(M^1);
  416. pP->m_LastActionTick = Temp;
  417. pP->Respawn();
  418. pP->m_ForceBalanced = true;
  419. } while (--NumBalance);
  420. m_ForceBalanced = true;
  421. }
  422. m_UnbalancedTick = -1;
  423. }
  424. // check for inactive players
  425. if(g_Config.m_SvInactiveKickTime > 0)
  426. {
  427. for(int i = 0; i < MAX_CLIENTS; ++i)
  428. {
  429. #ifdef CONF_DEBUG
  430. if(g_Config.m_DbgDummies)
  431. {
  432. if(i >= MAX_CLIENTS-g_Config.m_DbgDummies)
  433. break;
  434. }
  435. #endif
  436. if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i))
  437. {
  438. if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60)
  439. {
  440. switch(g_Config.m_SvInactiveKick)
  441. {
  442. case 0:
  443. {
  444. // move player to spectator
  445. GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
  446. }
  447. break;
  448. case 1:
  449. {
  450. // move player to spectator if the reserved slots aren't filled yet, kick him otherwise
  451. int Spectators = 0;
  452. for(int j = 0; j < MAX_CLIENTS; ++j)
  453. if(GameServer()->m_apPlayers[j] && GameServer()->m_apPlayers[j]->GetTeam() == TEAM_SPECTATORS)
  454. ++Spectators;
  455. if(Spectators >= g_Config.m_SvSpectatorSlots)
  456. Server()->Kick(i, "Kicked for inactivity");
  457. else
  458. GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
  459. }
  460. break;
  461. case 2:
  462. {
  463. // kick the player
  464. Server()->Kick(i, "Kicked for inactivity");
  465. }
  466. }
  467. }
  468. }
  469. }
  470. }
  471. DoWincheck();
  472. }
  473. bool IGameController::IsTeamplay() const
  474. {
  475. return m_GameFlags&GAMEFLAG_TEAMS;
  476. }
  477. void IGameController::Snap(int SnappingClient)
  478. {
  479. CNetObj_GameInfo *pGameInfoObj = (CNetObj_GameInfo *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo));
  480. if(!pGameInfoObj)
  481. return;
  482. pGameInfoObj->m_GameFlags = m_GameFlags;
  483. pGameInfoObj->m_GameStateFlags = 0;
  484. if(m_GameOverTick != -1)
  485. pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_GAMEOVER;
  486. if(m_SuddenDeath)
  487. pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_SUDDENDEATH;
  488. if(GameServer()->m_World.m_Paused)
  489. pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_PAUSED;
  490. pGameInfoObj->m_RoundStartTick = m_RoundStartTick;
  491. pGameInfoObj->m_WarmupTimer = m_Warmup;
  492. pGameInfoObj->m_ScoreLimit = g_Config.m_SvScorelimit;
  493. pGameInfoObj->m_TimeLimit = g_Config.m_SvTimelimit;
  494. pGameInfoObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0;
  495. pGameInfoObj->m_RoundCurrent = m_RoundCount+1;
  496. }
  497. int IGameController::GetAutoTeam(int NotThisID)
  498. {
  499. // this will force the auto balancer to work overtime aswell
  500. if(g_Config.m_DbgStress)
  501. return 0;
  502. int aNumplayers[2] = {0,0};
  503. for(int i = 0; i < MAX_CLIENTS; i++)
  504. {
  505. if(GameServer()->m_apPlayers[i] && i != NotThisID)
  506. {
  507. if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
  508. aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
  509. }
  510. }
  511. int Team = 0;
  512. if(IsTeamplay())
  513. Team = aNumplayers[TEAM_RED] > aNumplayers[TEAM_BLUE] ? TEAM_BLUE : TEAM_RED;
  514. if(CanJoinTeam(Team, NotThisID))
  515. return Team;
  516. return -1;
  517. }
  518. bool IGameController::CanJoinTeam(int Team, int NotThisID)
  519. {
  520. if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS))
  521. return true;
  522. int aNumplayers[2] = {0,0};
  523. for(int i = 0; i < MAX_CLIENTS; i++)
  524. {
  525. if(GameServer()->m_apPlayers[i] && i != NotThisID)
  526. {
  527. if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
  528. aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
  529. }
  530. }
  531. return (aNumplayers[0] + aNumplayers[1]) < Server()->MaxClients()-g_Config.m_SvSpectatorSlots;
  532. }
  533. bool IGameController::CheckTeamBalance()
  534. {
  535. if(!IsTeamplay() || !g_Config.m_SvTeambalanceTime)
  536. return true;
  537. int aT[2] = {0, 0};
  538. for(int i = 0; i < MAX_CLIENTS; i++)
  539. {
  540. CPlayer *pP = GameServer()->m_apPlayers[i];
  541. if(pP && pP->GetTeam() != TEAM_SPECTATORS)
  542. aT[pP->GetTeam()]++;
  543. }
  544. char aBuf[256];
  545. if(absolute(aT[0]-aT[1]) >= 2)
  546. {
  547. str_format(aBuf, sizeof(aBuf), "Teams are NOT balanced (red=%d blue=%d)", aT[0], aT[1]);
  548. GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
  549. if(GameServer()->m_pController->m_UnbalancedTick == -1)
  550. GameServer()->m_pController->m_UnbalancedTick = Server()->Tick();
  551. return false;
  552. }
  553. else
  554. {
  555. str_format(aBuf, sizeof(aBuf), "Teams are balanced (red=%d blue=%d)", aT[0], aT[1]);
  556. GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
  557. GameServer()->m_pController->m_UnbalancedTick = -1;
  558. return true;
  559. }
  560. }
  561. bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam)
  562. {
  563. int aT[2] = {0, 0};
  564. if (!IsTeamplay() || JoinTeam == TEAM_SPECTATORS || !g_Config.m_SvTeambalanceTime)
  565. return true;
  566. for(int i = 0; i < MAX_CLIENTS; i++)
  567. {
  568. CPlayer *pP = GameServer()->m_apPlayers[i];
  569. if(pP && pP->GetTeam() != TEAM_SPECTATORS)
  570. aT[pP->GetTeam()]++;
  571. }
  572. // simulate what would happen if changed team
  573. aT[JoinTeam]++;
  574. if (pPlayer->GetTeam() != TEAM_SPECTATORS)
  575. aT[JoinTeam^1]--;
  576. // there is a player-difference of at least 2
  577. if(absolute(aT[0]-aT[1]) >= 2)
  578. {
  579. // player wants to join team with less players
  580. if ((aT[0] < aT[1] && JoinTeam == TEAM_RED) || (aT[0] > aT[1] && JoinTeam == TEAM_BLUE))
  581. return true;
  582. else
  583. return false;
  584. }
  585. else
  586. return true;
  587. }
  588. void IGameController::DoWincheck()
  589. {
  590. if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested)
  591. {
  592. if(IsTeamplay())
  593. {
  594. // check score win condition
  595. if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) ||
  596. (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
  597. {
  598. if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE])
  599. EndRound();
  600. else
  601. m_SuddenDeath = 1;
  602. }
  603. }
  604. else
  605. {
  606. // gather some stats
  607. int Topscore = 0;
  608. int TopscoreCount = 0;
  609. for(int i = 0; i < MAX_CLIENTS; i++)
  610. {
  611. if(GameServer()->m_apPlayers[i])
  612. {
  613. if(GameServer()->m_apPlayers[i]->m_Score > Topscore)
  614. {
  615. Topscore = GameServer()->m_apPlayers[i]->m_Score;
  616. TopscoreCount = 1;
  617. }
  618. else if(GameServer()->m_apPlayers[i]->m_Score == Topscore)
  619. TopscoreCount++;
  620. }
  621. }
  622. // check score win condition
  623. if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) ||
  624. (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
  625. {
  626. if(TopscoreCount == 1)
  627. EndRound();
  628. else
  629. m_SuddenDeath = 1;
  630. }
  631. }
  632. }
  633. }
  634. int IGameController::ClampTeam(int Team)
  635. {
  636. if(Team < 0)
  637. return TEAM_SPECTATORS;
  638. if(IsTeamplay())
  639. return Team&1;
  640. return 0;
  641. }