Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

578 lines
15 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "keyvalues.h"
  9. #include "gamerules.h"
  10. #include "teamplay_gamerules.h"
  11. #ifdef CLIENT_DLL
  12. #include "c_baseplayer.h"
  13. #include "c_team.h"
  14. #else
  15. #include "player.h"
  16. #include "game.h"
  17. #include "gamevars_shared.h"
  18. #include "team.h"
  19. #endif
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. #ifdef GAME_DLL
  23. static char team_names[MAX_TEAMS][MAX_TEAMNAME_LENGTH];
  24. static int team_scores[MAX_TEAMS];
  25. static int num_teams = 0;
  26. extern bool g_fGameOver;
  27. extern ConVar sv_allchat;
  28. extern ConVar spec_replay_bot;
  29. // [jason] Used to allow dead chat as well
  30. extern ConVar sv_deadtalk;
  31. REGISTER_GAMERULES_CLASS( CTeamplayRules );
  32. CTeamplayRules::CTeamplayRules()
  33. {
  34. m_DisableDeathMessages = false;
  35. m_DisableDeathPenalty = false;
  36. m_bSwitchTeams = false;
  37. m_bScrambleTeams = false;
  38. memset( team_names, 0, sizeof(team_names) );
  39. memset( team_scores, 0, sizeof(team_scores) );
  40. num_teams = 0;
  41. // Copy over the team from the server config
  42. m_szTeamList[0] = 0;
  43. RecountTeams();
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose:
  47. //-----------------------------------------------------------------------------
  48. void CTeamplayRules::Precache( void )
  49. {
  50. BaseClass::Precache();
  51. // Call the Team Manager's precaches
  52. for ( int i = 0; i < GetNumberOfTeams(); i++ )
  53. {
  54. CTeam *pTeam = GetGlobalTeam( i );
  55. pTeam->Precache();
  56. }
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Purpose:
  60. //
  61. // WARNING - this function is NOT called in CS:GO
  62. //
  63. // CCSGameRules (which has CTeamplayRules as a baseclass) ::Think() calls
  64. // CGameRules::Think() directly, bypassing CTeamplayRules::Think() and
  65. // CMultiplayRules::Think()
  66. //
  67. // Code placed in here is likely to NEVER be called
  68. //-----------------------------------------------------------------------------
  69. void CTeamplayRules::Think ( void )
  70. {
  71. BaseClass::Think();
  72. ///// Check game rules /////
  73. if ( g_fGameOver ) // someone else quit the game already
  74. {
  75. BaseClass::Think();
  76. return;
  77. }
  78. float flTimeLimit = mp_timelimit.GetFloat() * 60;
  79. if ( flTimeLimit != 0 && gpGlobals->curtime >= flTimeLimit )
  80. {
  81. ChangeLevel();
  82. return;
  83. }
  84. float flFragLimit = fraglimit.GetFloat();
  85. if ( flFragLimit )
  86. {
  87. // check if any team is over the frag limit
  88. for ( int i = 0; i < num_teams; i++ )
  89. {
  90. if ( team_scores[i] >= flFragLimit )
  91. {
  92. ChangeLevel();
  93. return;
  94. }
  95. }
  96. }
  97. }
  98. //=========================================================
  99. // ClientCommand
  100. // the user has typed a command which is unrecognized by everything else;
  101. // this check to see if the gamerules knows anything about the command
  102. //=========================================================
  103. bool CTeamplayRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  104. {
  105. if( BaseClass::ClientCommand( pEdict, args ) )
  106. return true;
  107. if ( FStrEq( args[0], "menuselect" ) )
  108. {
  109. if ( args.ArgC() < 2 )
  110. return true;
  111. //int slot = atoi( args[1] );
  112. // select the item from the current menu
  113. return true;
  114. }
  115. return false;
  116. }
  117. const char *CTeamplayRules::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
  118. {
  119. return "default";
  120. }
  121. //=========================================================
  122. // InitHUD
  123. //=========================================================
  124. void CTeamplayRules::InitHUD( CBasePlayer *pPlayer )
  125. {
  126. SetDefaultPlayerTeam( pPlayer );
  127. BaseClass::InitHUD( pPlayer );
  128. // this does a string compare and doesn't need to happen every frame
  129. //RecountTeams();
  130. /* TODO this has to be rewritten, maybe add a new USERINFO cvar "team"
  131. const char *team = engine->GetClientConVarValue( pPlayer->entindex(), "cl_team" );
  132. // update the current player of the team he is joining
  133. char text[1024];
  134. if ( !strcmp( mdls, pPlayer->TeamName() ) )
  135. {
  136. Q_snprintf( text,sizeof(text), "You are on team \'%s\'\n", pPlayer->TeamName() );
  137. }
  138. else
  139. {
  140. Q_snprintf( text,sizeof(text), "You were assigned to team %s\n", pPlayer->TeamName() );
  141. }
  142. ChangePlayerTeam( pPlayer, pPlayer->TeamName(), false, false );
  143. if ( Q_strlen( pPlayer->TeamName() ) > 0 )
  144. {
  145. UTIL_SayText( text, pPlayer );
  146. }
  147. RecountTeams(); */
  148. }
  149. void CTeamplayRules::ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, bool bKill, bool bGib )
  150. {
  151. int damageFlags = DMG_GENERIC;
  152. // int clientIndex = pPlayer->entindex();
  153. if ( !bGib )
  154. {
  155. damageFlags |= DMG_NEVERGIB;
  156. }
  157. else
  158. {
  159. damageFlags |= DMG_ALWAYSGIB;
  160. }
  161. // copy out the team name from the model
  162. // pPlayer->SetTeamName( pTeamName );
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Purpose: Player has just left the game
  166. //-----------------------------------------------------------------------------
  167. void CTeamplayRules::ClientDisconnected( edict_t *pClient )
  168. {
  169. // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" );
  170. CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
  171. if ( pPlayer )
  172. {
  173. pPlayer->SetConnected( PlayerDisconnecting );
  174. // Remove the player from his team
  175. if ( pPlayer->GetTeam() )
  176. {
  177. pPlayer->ChangeTeam( 0 );
  178. }
  179. }
  180. BaseClass::ClientDisconnected( pClient );
  181. }
  182. //=========================================================
  183. // ClientUserInfoChanged
  184. //=========================================================
  185. void CTeamplayRules::ClientSettingsChanged( CBasePlayer *pPlayer )
  186. {
  187. /* TODO: handle skin, model & team changes
  188. char text[1024];
  189. // skin/color/model changes
  190. int iTeam = Q_atoi( engine->GetClientConVarValue( pPlayer->entindex(), "cl_team" ) );
  191. int iClass = Q_atoi( engine->GetClientConVarValue( pPlayer->entindex(), "cl_class" ) );
  192. if ( defaultteam.GetBool() )
  193. {
  194. // int clientIndex = pPlayer->entindex();
  195. // engine->SetClientKeyValue( clientIndex, "model", pPlayer->TeamName() );
  196. // engine->SetClientKeyValue( clientIndex, "team", pPlayer->TeamName() );
  197. UTIL_SayText( "Not allowed to change teams in this game!\n", pPlayer );
  198. return;
  199. }
  200. if ( defaultteam.GetFloat() || !IsValidTeam( mdls ) )
  201. {
  202. // int clientIndex = pPlayer->entindex();
  203. // engine->SetClientKeyValue( clientIndex, "model", pPlayer->TeamName() );
  204. Q_snprintf( text,sizeof(text), "Can't change team to \'%s\'\n", mdls );
  205. UTIL_SayText( text, pPlayer );
  206. Q_snprintf( text,sizeof(text), "Server limits teams to \'%s\'\n", m_szTeamList );
  207. UTIL_SayText( text, pPlayer );
  208. return;
  209. }
  210. ChangePlayerTeam( pPlayer, mdls, true, true );
  211. // recound stuff
  212. RecountTeams(); */
  213. const char *pszName = engine->GetClientConVarValue( pPlayer->entindex(), "name" );
  214. const char *pszOldName = pPlayer->GetPlayerName();
  215. // msg everyone if someone changes their name, and it isn't the first time (changing no name to current name)
  216. // Note, this is case sensitive
  217. if ( ( Q_strcmp( pszOldName, pszName ) != 0 ) &&
  218. CanClientCustomizeOwnIdentity() )
  219. {
  220. if ( pszOldName[0] != '\0' )
  221. {
  222. IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
  223. if ( event )
  224. {
  225. event->SetInt( "userid", pPlayer->GetUserID() );
  226. event->SetString( "oldname", pszOldName );
  227. event->SetString( "newname", pszName );
  228. gameeventmanager->FireEvent( event );
  229. }
  230. }
  231. pPlayer->SetPlayerName( pszName );
  232. }
  233. }
  234. //=========================================================
  235. // Deathnotice.
  236. //=========================================================
  237. void CTeamplayRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  238. {
  239. if ( m_DisableDeathMessages )
  240. return;
  241. CBaseEntity *pKiller = info.GetAttacker();
  242. if ( pVictim && pKiller && pKiller->IsPlayer() )
  243. {
  244. CBasePlayer *pk = (CBasePlayer*)pKiller;
  245. if ( pk )
  246. {
  247. if ( (pk != pVictim) && (PlayerRelationship( pVictim, pk ) == GR_TEAMMATE) )
  248. {
  249. IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
  250. if ( event )
  251. {
  252. event->SetInt("killer", pk->GetUserID() );
  253. event->SetInt("victim", pVictim->GetUserID() );
  254. event->SetInt("priority", 7 ); // HLTV event priority, not transmitted
  255. if( !OnReplayPrompt( pVictim, pk ) )
  256. event->SetBool( "noreplay", true );
  257. gameeventmanager->FireEvent( event );
  258. }
  259. return;
  260. }
  261. }
  262. }
  263. BaseClass::DeathNotice( pVictim, info );
  264. }
  265. //=========================================================
  266. //=========================================================
  267. void CTeamplayRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  268. {
  269. if ( !m_DisableDeathPenalty )
  270. {
  271. BaseClass::PlayerKilled( pVictim, info );
  272. RecountTeams();
  273. }
  274. }
  275. //=========================================================
  276. // IsTeamplay
  277. //=========================================================
  278. bool CTeamplayRules::IsTeamplay( void )
  279. {
  280. return true;
  281. }
  282. bool CTeamplayRules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
  283. {
  284. if ( pAttacker && PlayerRelationship( pPlayer, pAttacker ) == GR_TEAMMATE )
  285. {
  286. // my teammate hit me.
  287. if ( ( mp_friendlyfire.GetInt() == 0 ) && ( pAttacker != pPlayer ) )
  288. {
  289. // friendly fire is off, and this hit came from someone other than myself, then don't get hurt
  290. return false;
  291. }
  292. }
  293. return BaseClass::FPlayerCanTakeDamage( pPlayer, pAttacker );
  294. }
  295. //=========================================================
  296. //=========================================================
  297. int CTeamplayRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
  298. {
  299. // half life multiplay has a simple concept of Player Relationships.
  300. // you are either on another player's team, or you are not.
  301. if ( !pPlayer || !pTarget || !pTarget->IsPlayer() )
  302. return GR_NOTTEAMMATE;
  303. // don't do string compares, just compare the team number
  304. // if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
  305. // {
  306. // return GR_TEAMMATE;
  307. // }
  308. if ( pPlayer->GetTeamNumber() != TEAM_INVALID && pTarget->GetTeamNumber() != TEAM_INVALID && pPlayer->GetTeamNumber() == pTarget->GetTeamNumber() )
  309. {
  310. return GR_TEAMMATE;
  311. }
  312. return GR_NOTTEAMMATE;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. // Input : *pListener -
  317. // *pSpeaker -
  318. // Output : Returns true on success, false on failure.
  319. //-----------------------------------------------------------------------------
  320. bool CTeamplayRules::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker, bool bTeamOnly )
  321. {
  322. if ( !bTeamOnly && sv_allchat.GetBool() )
  323. {
  324. if ( !pSpeaker->IsAlive() )
  325. {
  326. // [jason] convar allows the dead to speak/chat with the living
  327. return ( ( !pListener->IsAlive() || sv_deadtalk.GetBool() ) &&
  328. ( PlayerRelationship( pListener, pSpeaker ) == GR_TEAMMATE ) );
  329. }
  330. }
  331. return ( PlayerRelationship( pListener, pSpeaker ) == GR_TEAMMATE );
  332. }
  333. //=========================================================
  334. //=========================================================
  335. bool CTeamplayRules::ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target )
  336. {
  337. // always autoaim, unless target is a teammate
  338. CBaseEntity *pTgt = CBaseEntity::Instance( target );
  339. if ( pTgt && pTgt->IsPlayer() )
  340. {
  341. if ( PlayerRelationship( pPlayer, pTgt ) == GR_TEAMMATE )
  342. return false; // don't autoaim at teammates
  343. }
  344. return BaseClass::ShouldAutoAim( pPlayer, target );
  345. }
  346. //=========================================================
  347. //=========================================================
  348. int CTeamplayRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
  349. {
  350. if ( !pKilled )
  351. return 0;
  352. if ( !pAttacker )
  353. return 1;
  354. if ( pAttacker != pKilled && PlayerRelationship( pAttacker, pKilled ) == GR_TEAMMATE )
  355. return -1;
  356. return 1;
  357. }
  358. //=========================================================
  359. //=========================================================
  360. // const char *CTeamplayRules::GetTeamID( CBaseEntity *pEntity )
  361. // {
  362. // if ( pEntity == NULL || pEntity->edict() == NULL )
  363. // return "";
  364. //
  365. // // return their team name
  366. // return pEntity->TeamID();
  367. // }
  368. int CTeamplayRules::GetTeamIndex( const char *pTeamName )
  369. {
  370. if ( pTeamName && *pTeamName != 0 )
  371. {
  372. // try to find existing team
  373. for ( int tm = 0; tm < num_teams; tm++ )
  374. {
  375. if ( !stricmp( team_names[tm], pTeamName ) )
  376. return tm;
  377. }
  378. }
  379. return -1; // No match
  380. }
  381. const char *CTeamplayRules::GetIndexedTeamName( int teamIndex )
  382. {
  383. if ( teamIndex < 0 || teamIndex >= num_teams )
  384. return "";
  385. return team_names[ teamIndex ];
  386. }
  387. bool CTeamplayRules::IsValidTeam( const char *pTeamName )
  388. {
  389. if ( !m_teamLimit ) // Any team is valid if the teamlist isn't set
  390. return true;
  391. return ( GetTeamIndex( pTeamName ) != -1 ) ? true : false;
  392. }
  393. const char *CTeamplayRules::TeamWithFewestPlayers( void )
  394. {
  395. int i;
  396. int minPlayers = MAX_TEAMS;
  397. int teamCount[ MAX_TEAMS ];
  398. char *pTeamName = NULL;
  399. memset( teamCount, 0, MAX_TEAMS * sizeof(int) );
  400. // loop through all clients, count number of players on each team
  401. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  402. {
  403. CBaseEntity *plr = UTIL_PlayerByIndex( i );
  404. if ( plr )
  405. {
  406. int team = GetTeamIndex( plr->TeamID() );
  407. if ( team >= 0 )
  408. teamCount[team] ++;
  409. }
  410. }
  411. // Find team with least players
  412. for ( i = 0; i < num_teams; i++ )
  413. {
  414. if ( teamCount[i] < minPlayers )
  415. {
  416. minPlayers = teamCount[i];
  417. pTeamName = team_names[i];
  418. }
  419. }
  420. return pTeamName;
  421. }
  422. //=========================================================
  423. //=========================================================
  424. void CTeamplayRules::RecountTeams( void )
  425. {
  426. char *pName;
  427. char teamlist[TEAMPLAY_TEAMLISTLENGTH];
  428. // loop through all teams, recounting everything
  429. num_teams = 0;
  430. // Copy all of the teams from the teamlist
  431. // make a copy because strtok is destructive
  432. Q_strncpy( teamlist, m_szTeamList, sizeof(teamlist) );
  433. pName = teamlist;
  434. pName = strtok( pName, ";" );
  435. while ( pName != NULL && *pName )
  436. {
  437. if ( GetTeamIndex( pName ) < 0 )
  438. {
  439. Q_strncpy( team_names[num_teams], pName, sizeof(team_names[num_teams]));
  440. num_teams++;
  441. }
  442. pName = strtok( NULL, ";" );
  443. }
  444. if ( num_teams < 2 )
  445. {
  446. num_teams = 0;
  447. m_teamLimit = false;
  448. }
  449. // Sanity check
  450. memset( team_scores, 0, sizeof(team_scores) );
  451. // loop through all clients
  452. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  453. {
  454. CBasePlayer *plr = UTIL_PlayerByIndex( i );
  455. if ( plr )
  456. {
  457. const char *pTeamName = plr->TeamID();
  458. // try add to existing team
  459. int tm = GetTeamIndex( pTeamName );
  460. if ( tm < 0 ) // no team match found
  461. {
  462. if ( !m_teamLimit )
  463. {
  464. // add to new team
  465. tm = num_teams;
  466. num_teams++;
  467. team_scores[tm] = 0;
  468. Q_strncpy( team_names[tm], pTeamName, MAX_TEAMNAME_LENGTH );
  469. }
  470. }
  471. if ( tm >= 0 )
  472. {
  473. team_scores[tm] += plr->FragCount();
  474. }
  475. }
  476. }
  477. }
  478. #endif // GAME_DLL