Team Fortress 2 Source Code as on 22/4/2020
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.

5859 lines
162 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The TF Game rules
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "cs_gamerules.h"
  9. #include "cs_ammodef.h"
  10. #include "weapon_csbase.h"
  11. #include "cs_shareddefs.h"
  12. #include "KeyValues.h"
  13. #include "cs_achievement_constants.h"
  14. #include "fmtstr.h"
  15. #ifdef CLIENT_DLL
  16. #include "networkstringtable_clientdll.h"
  17. #include "utlvector.h"
  18. #else
  19. #include "bot.h"
  20. #include "utldict.h"
  21. #include "cs_player.h"
  22. #include "cs_team.h"
  23. #include "cs_gamerules.h"
  24. #include "voice_gamemgr.h"
  25. #include "igamesystem.h"
  26. #include "weapon_c4.h"
  27. #include "mapinfo.h"
  28. #include "shake.h"
  29. #include "mapentities.h"
  30. #include "game.h"
  31. #include "cs_simple_hostage.h"
  32. #include "cs_gameinterface.h"
  33. #include "player_resource.h"
  34. #include "info_view_parameters.h"
  35. #include "cs_bot_manager.h"
  36. #include "cs_bot.h"
  37. #include "eventqueue.h"
  38. #include "fmtstr.h"
  39. #include "teamplayroundbased_gamerules.h"
  40. #include "gameweaponmanager.h"
  41. #include "cs_gamestats.h"
  42. #include "cs_urlretrieveprices.h"
  43. #include "networkstringtable_gamedll.h"
  44. #include "player_resource.h"
  45. #include "cs_player_resource.h"
  46. #if defined( REPLAY_ENABLED )
  47. #include "replay/ireplaysystem.h"
  48. #include "replay/iserverreplaycontext.h"
  49. #include "replay/ireplaysessionrecorder.h"
  50. #endif // REPLAY_ENABLED
  51. #endif
  52. #include "cs_blackmarket.h"
  53. // memdbgon must be the last include file in a .cpp file!!!
  54. #include "tier0/memdbgon.h"
  55. #ifndef CLIENT_DLL
  56. #define CS_GAME_STATS_UPDATE 79200 //22 hours
  57. #define CS_GAME_STATS_UPDATE_PERIOD 7200 // 2 hours
  58. extern IUploadGameStats *gamestatsuploader;
  59. #if defined( REPLAY_ENABLED )
  60. extern IReplaySystem *g_pReplay;
  61. #endif // REPLAY_ENABLED
  62. #endif
  63. /**
  64. * Player hull & eye position for standing, ducking, etc. This version has a taller
  65. * player height, but goldsrc-compatible collision bounds.
  66. */
  67. static CViewVectors g_CSViewVectors(
  68. Vector( 0, 0, 64 ), // eye position
  69. Vector(-16, -16, 0 ), // hull min
  70. Vector( 16, 16, 62 ), // hull max
  71. Vector(-16, -16, 0 ), // duck hull min
  72. Vector( 16, 16, 45 ), // duck hull max
  73. Vector( 0, 0, 47 ), // duck view
  74. Vector(-10, -10, -10 ), // observer hull min
  75. Vector( 10, 10, 10 ), // observer hull max
  76. Vector( 0, 0, 14 ) // dead view height
  77. );
  78. #ifndef CLIENT_DLL
  79. LINK_ENTITY_TO_CLASS(info_player_terrorist, CPointEntity);
  80. LINK_ENTITY_TO_CLASS(info_player_counterterrorist,CPointEntity);
  81. LINK_ENTITY_TO_CLASS(info_player_logo,CPointEntity);
  82. #endif
  83. REGISTER_GAMERULES_CLASS( CCSGameRules );
  84. BEGIN_NETWORK_TABLE_NOBASE( CCSGameRules, DT_CSGameRules )
  85. #ifdef CLIENT_DLL
  86. RecvPropBool( RECVINFO( m_bFreezePeriod ) ),
  87. RecvPropInt( RECVINFO( m_iRoundTime ) ),
  88. RecvPropFloat( RECVINFO( m_fRoundStartTime ) ),
  89. RecvPropFloat( RECVINFO( m_flGameStartTime ) ),
  90. RecvPropInt( RECVINFO( m_iHostagesRemaining ) ),
  91. RecvPropBool( RECVINFO( m_bMapHasBombTarget ) ),
  92. RecvPropBool( RECVINFO( m_bMapHasRescueZone ) ),
  93. RecvPropBool( RECVINFO( m_bLogoMap ) ),
  94. RecvPropBool( RECVINFO( m_bBlackMarket ) )
  95. #else
  96. SendPropBool( SENDINFO( m_bFreezePeriod ) ),
  97. SendPropInt( SENDINFO( m_iRoundTime ), 16 ),
  98. SendPropFloat( SENDINFO( m_fRoundStartTime ), 32, SPROP_NOSCALE ),
  99. SendPropFloat( SENDINFO( m_flGameStartTime ), 32, SPROP_NOSCALE ),
  100. SendPropInt( SENDINFO( m_iHostagesRemaining ), 4 ),
  101. SendPropBool( SENDINFO( m_bMapHasBombTarget ) ),
  102. SendPropBool( SENDINFO( m_bMapHasRescueZone ) ),
  103. SendPropBool( SENDINFO( m_bLogoMap ) ),
  104. SendPropBool( SENDINFO( m_bBlackMarket ) )
  105. #endif
  106. END_NETWORK_TABLE()
  107. LINK_ENTITY_TO_CLASS( cs_gamerules, CCSGameRulesProxy );
  108. IMPLEMENT_NETWORKCLASS_ALIASED( CSGameRulesProxy, DT_CSGameRulesProxy )
  109. #ifdef CLIENT_DLL
  110. void RecvProxy_CSGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
  111. {
  112. CCSGameRules *pRules = CSGameRules();
  113. Assert( pRules );
  114. *pOut = pRules;
  115. }
  116. BEGIN_RECV_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy )
  117. RecvPropDataTable( "cs_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_CSGameRules ), RecvProxy_CSGameRules )
  118. END_RECV_TABLE()
  119. #else
  120. void* SendProxy_CSGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  121. {
  122. CCSGameRules *pRules = CSGameRules();
  123. Assert( pRules );
  124. return pRules;
  125. }
  126. BEGIN_SEND_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy )
  127. SendPropDataTable( "cs_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_CSGameRules ), SendProxy_CSGameRules )
  128. END_SEND_TABLE()
  129. #endif
  130. ConVar ammo_50AE_max( "ammo_50AE_max", "35", FCVAR_REPLICATED );
  131. ConVar ammo_762mm_max( "ammo_762mm_max", "90", FCVAR_REPLICATED );
  132. ConVar ammo_556mm_max( "ammo_556mm_max", "90", FCVAR_REPLICATED );
  133. ConVar ammo_556mm_box_max( "ammo_556mm_box_max", "200", FCVAR_REPLICATED );
  134. ConVar ammo_338mag_max( "ammo_338mag_max", "30", FCVAR_REPLICATED );
  135. ConVar ammo_9mm_max( "ammo_9mm_max", "120", FCVAR_REPLICATED );
  136. ConVar ammo_buckshot_max( "ammo_buckshot_max", "32", FCVAR_REPLICATED );
  137. ConVar ammo_45acp_max( "ammo_45acp_max", "100", FCVAR_REPLICATED );
  138. ConVar ammo_357sig_max( "ammo_357sig_max", "52", FCVAR_REPLICATED );
  139. ConVar ammo_57mm_max( "ammo_57mm_max", "100", FCVAR_REPLICATED );
  140. ConVar ammo_hegrenade_max( "ammo_hegrenade_max", "1", FCVAR_REPLICATED );
  141. ConVar ammo_flashbang_max( "ammo_flashbang_max", "2", FCVAR_REPLICATED );
  142. ConVar ammo_smokegrenade_max( "ammo_smokegrenade_max", "1", FCVAR_REPLICATED );
  143. //ConVar mp_dynamicpricing( "mp_dynamicpricing", "0", FCVAR_REPLICATED, "Enables or Disables the dynamic weapon prices" );
  144. extern ConVar sv_stopspeed;
  145. ConVar mp_buytime(
  146. "mp_buytime",
  147. "1.5",
  148. FCVAR_REPLICATED,
  149. "How many minutes after round start players can buy items for.",
  150. true, 0.25,
  151. false, 0 );
  152. ConVar mp_playerid(
  153. "mp_playerid",
  154. "0",
  155. FCVAR_REPLICATED,
  156. "Controls what information player see in the status bar: 0 all names; 1 team names; 2 no names",
  157. true, 0,
  158. true, 2 );
  159. ConVar mp_playerid_delay(
  160. "mp_playerid_delay",
  161. "0.5",
  162. FCVAR_REPLICATED,
  163. "Number of seconds to delay showing information in the status bar",
  164. true, 0,
  165. true, 1 );
  166. ConVar mp_playerid_hold(
  167. "mp_playerid_hold",
  168. "0.25",
  169. FCVAR_REPLICATED,
  170. "Number of seconds to keep showing old information in the status bar",
  171. true, 0,
  172. true, 1 );
  173. ConVar mp_round_restart_delay(
  174. "mp_round_restart_delay",
  175. "5.0",
  176. FCVAR_REPLICATED,
  177. "Number of seconds to delay before restarting a round after a win",
  178. true, 0.0f,
  179. true, 10.0f );
  180. ConVar sv_allowminmodels(
  181. "sv_allowminmodels",
  182. "1",
  183. FCVAR_REPLICATED | FCVAR_NOTIFY,
  184. "Allow or disallow the use of cl_minmodels on this server." );
  185. #ifdef CLIENT_DLL
  186. ConVar cl_autowepswitch(
  187. "cl_autowepswitch",
  188. "1",
  189. FCVAR_ARCHIVE | FCVAR_USERINFO,
  190. "Automatically switch to picked up weapons (if more powerful)" );
  191. ConVar cl_autohelp(
  192. "cl_autohelp",
  193. "1",
  194. FCVAR_ARCHIVE | FCVAR_USERINFO,
  195. "Auto-help" );
  196. #else
  197. // longest the intermission can last, in seconds
  198. #define MAX_INTERMISSION_TIME 120
  199. // Falling damage stuff.
  200. #define CS_PLAYER_FATAL_FALL_SPEED 1100 // approx 60 feet
  201. #define CS_PLAYER_MAX_SAFE_FALL_SPEED 580 // approx 20 feet
  202. #define CS_DAMAGE_FOR_FALL_SPEED ((float)100 / ( CS_PLAYER_FATAL_FALL_SPEED - CS_PLAYER_MAX_SAFE_FALL_SPEED )) // damage per unit per second.
  203. // These entities are preserved each round restart. The rest are removed and recreated.
  204. static const char *s_PreserveEnts[] =
  205. {
  206. "ai_network",
  207. "ai_hint",
  208. "cs_gamerules",
  209. "cs_team_manager",
  210. "cs_player_manager",
  211. "env_soundscape",
  212. "env_soundscape_proxy",
  213. "env_soundscape_triggerable",
  214. "env_sun",
  215. "env_wind",
  216. "env_fog_controller",
  217. "func_brush",
  218. "func_wall",
  219. "func_buyzone",
  220. "func_illusionary",
  221. "func_hostage_rescue",
  222. "func_bomb_target",
  223. "infodecal",
  224. "info_projecteddecal",
  225. "info_node",
  226. "info_target",
  227. "info_node_hint",
  228. "info_player_counterterrorist",
  229. "info_player_terrorist",
  230. "info_map_parameters",
  231. "keyframe_rope",
  232. "move_rope",
  233. "info_ladder",
  234. "player",
  235. "point_viewcontrol",
  236. "scene_manager",
  237. "shadow_control",
  238. "sky_camera",
  239. "soundent",
  240. "trigger_soundscape",
  241. "viewmodel",
  242. "predicted_viewmodel",
  243. "worldspawn",
  244. "point_devshot_camera",
  245. "", // END Marker
  246. };
  247. // --------------------------------------------------------------------------------------------------- //
  248. // Voice helper
  249. // --------------------------------------------------------------------------------------------------- //
  250. class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
  251. {
  252. public:
  253. virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity )
  254. {
  255. // Dead players can only be heard by other dead team mates
  256. if ( pTalker->IsAlive() == false )
  257. {
  258. if ( pListener->IsAlive() == false )
  259. return ( pListener->InSameTeam( pTalker ) );
  260. return false;
  261. }
  262. return ( pListener->InSameTeam( pTalker ) );
  263. }
  264. };
  265. CVoiceGameMgrHelper g_VoiceGameMgrHelper;
  266. IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
  267. // --------------------------------------------------------------------------------------------------- //
  268. // Globals.
  269. // --------------------------------------------------------------------------------------------------- //
  270. // NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
  271. const char *sTeamNames[] =
  272. {
  273. "Unassigned",
  274. "Spectator",
  275. "TERRORIST",
  276. "CT"
  277. };
  278. extern ConVar mp_maxrounds;
  279. ConVar mp_startmoney(
  280. "mp_startmoney",
  281. "800",
  282. FCVAR_REPLICATED | FCVAR_NOTIFY,
  283. "amount of money each player gets when they reset",
  284. true, 800,
  285. true, 16000 );
  286. ConVar mp_roundtime(
  287. "mp_roundtime",
  288. "2.5",
  289. FCVAR_REPLICATED | FCVAR_NOTIFY,
  290. "How many minutes each round takes.",
  291. true, 1, // min value
  292. true, 9 // max value
  293. );
  294. ConVar mp_freezetime(
  295. "mp_freezetime",
  296. "6",
  297. FCVAR_REPLICATED | FCVAR_NOTIFY,
  298. "how many seconds to keep players frozen when the round starts",
  299. true, 0, // min value
  300. true, 60 // max value
  301. );
  302. ConVar mp_c4timer(
  303. "mp_c4timer",
  304. "45",
  305. FCVAR_REPLICATED | FCVAR_NOTIFY,
  306. "how long from when the C4 is armed until it blows",
  307. true, 10, // min value
  308. true, 90 // max value
  309. );
  310. ConVar mp_limitteams(
  311. "mp_limitteams",
  312. "2",
  313. FCVAR_REPLICATED | FCVAR_NOTIFY,
  314. "Max # of players 1 team can have over another (0 disables check)",
  315. true, 0, // min value
  316. true, 30 // max value
  317. );
  318. ConVar mp_tkpunish(
  319. "mp_tkpunish",
  320. "0",
  321. FCVAR_REPLICATED,
  322. "Will a TK'er be punished in the next round? {0=no, 1=yes}" );
  323. ConVar mp_autokick(
  324. "mp_autokick",
  325. "1",
  326. FCVAR_REPLICATED,
  327. "Kick idle/team-killing players" );
  328. ConVar mp_spawnprotectiontime(
  329. "mp_spawnprotectiontime",
  330. "5",
  331. FCVAR_REPLICATED,
  332. "Kick players who team-kill within this many seconds of a round restart." );
  333. ConVar mp_humanteam(
  334. "mp_humanteam",
  335. "any",
  336. FCVAR_REPLICATED,
  337. "Restricts human players to a single team {any, CT, T}" );
  338. ConVar mp_ignore_round_win_conditions(
  339. "mp_ignore_round_win_conditions",
  340. "0",
  341. FCVAR_REPLICATED,
  342. "Ignore conditions which would end the current round");
  343. ConCommand EndRound( "endround", &CCSGameRules::EndRound, "End the current round.", FCVAR_CHEAT );
  344. // --------------------------------------------------------------------------------------------------- //
  345. // Global helper functions.
  346. // --------------------------------------------------------------------------------------------------- //
  347. void InitBodyQue(void)
  348. {
  349. // FIXME: Make this work
  350. }
  351. Vector DropToGround(
  352. CBaseEntity *pMainEnt,
  353. const Vector &vPos,
  354. const Vector &vMins,
  355. const Vector &vMaxs )
  356. {
  357. trace_t trace;
  358. UTIL_TraceHull( vPos, vPos + Vector( 0, 0, -500 ), vMins, vMaxs, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &trace );
  359. return trace.endpos;
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Purpose: This function can be used to find a valid placement location for an entity.
  363. // Given an origin to start looking from and a minimum radius to place the entity at,
  364. // it will sweep out a circle around vOrigin and try to find a valid spot (on the ground)
  365. // where mins and maxs will fit.
  366. // Input : *pMainEnt - Entity to place
  367. // &vOrigin - Point to search around
  368. // fRadius - Radius to search within
  369. // nTries - Number of tries to attempt
  370. // &mins - mins of the Entity
  371. // &maxs - maxs of the Entity
  372. // &outPos - Return point
  373. // Output : Returns true and fills in outPos if it found a spot.
  374. //-----------------------------------------------------------------------------
  375. bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround )
  376. {
  377. // This function moves the box out in each dimension in each step trying to find empty space like this:
  378. //
  379. // X
  380. // X X
  381. // Step 1: X Step 2: XXX Step 3: XXXXX
  382. // X X
  383. // X
  384. //
  385. Vector mins, maxs;
  386. pMainEnt->CollisionProp()->WorldSpaceAABB( &mins, &maxs );
  387. mins -= pMainEnt->GetAbsOrigin();
  388. maxs -= pMainEnt->GetAbsOrigin();
  389. // Put some padding on their bbox.
  390. float flPadSize = 5;
  391. Vector vTestMins = mins - Vector( flPadSize, flPadSize, flPadSize );
  392. Vector vTestMaxs = maxs + Vector( flPadSize, flPadSize, flPadSize );
  393. // First test the starting origin.
  394. if ( UTIL_IsSpaceEmpty( pMainEnt, vOrigin + vTestMins, vOrigin + vTestMaxs ) )
  395. {
  396. if ( bDropToGround )
  397. {
  398. outPos = DropToGround( pMainEnt, vOrigin, vTestMins, vTestMaxs );
  399. }
  400. else
  401. {
  402. outPos = vOrigin;
  403. }
  404. return true;
  405. }
  406. Vector vDims = vTestMaxs - vTestMins;
  407. // Keep branching out until we get too far.
  408. int iCurIteration = 0;
  409. int nMaxIterations = 15;
  410. int offset = 0;
  411. do
  412. {
  413. for ( int iDim=0; iDim < 3; iDim++ )
  414. {
  415. float flCurOffset = offset * vDims[iDim];
  416. for ( int iSign=0; iSign < 2; iSign++ )
  417. {
  418. Vector vBase = vOrigin;
  419. vBase[iDim] += (iSign*2-1) * flCurOffset;
  420. if ( UTIL_IsSpaceEmpty( pMainEnt, vBase + vTestMins, vBase + vTestMaxs ) )
  421. {
  422. // Ensure that there is a clear line of sight from the spawnpoint entity to the actual spawn point.
  423. // (Useful for keeping things from spawning behind walls near a spawn point)
  424. trace_t tr;
  425. UTIL_TraceLine( vOrigin, vBase, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &tr );
  426. if ( tr.fraction != 1.0 )
  427. {
  428. continue;
  429. }
  430. if ( bDropToGround )
  431. outPos = DropToGround( pMainEnt, vBase, vTestMins, vTestMaxs );
  432. else
  433. outPos = vBase;
  434. return true;
  435. }
  436. }
  437. }
  438. ++offset;
  439. } while ( iCurIteration++ < nMaxIterations );
  440. // Warning( "EntityPlacementTest for ent %d:%s failed!\n", pMainEnt->entindex(), pMainEnt->GetClassname() );
  441. return false;
  442. }
  443. int UTIL_HumansInGame( bool ignoreSpectators )
  444. {
  445. int iCount = 0;
  446. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  447. {
  448. CCSPlayer *entity = CCSPlayer::Instance( i );
  449. if ( entity && !FNullEnt( entity->edict() ) )
  450. {
  451. if ( FStrEq( entity->GetPlayerName(), "" ) )
  452. continue;
  453. if ( FBitSet( entity->GetFlags(), FL_FAKECLIENT ) )
  454. continue;
  455. if ( ignoreSpectators && entity->GetTeamNumber() != TEAM_TERRORIST && entity->GetTeamNumber() != TEAM_CT )
  456. continue;
  457. if ( ignoreSpectators && entity->State_Get() == STATE_PICKINGCLASS )
  458. continue;
  459. iCount++;
  460. }
  461. }
  462. return iCount;
  463. }
  464. // --------------------------------------------------------------------------------------------------- //
  465. // CCSGameRules implementation.
  466. // --------------------------------------------------------------------------------------------------- //
  467. CCSGameRules::CCSGameRules()
  468. {
  469. m_iRoundTime = 0;
  470. m_iRoundWinStatus = WINNER_NONE;
  471. m_iFreezeTime = 0;
  472. m_fRoundStartTime = 0;
  473. m_bAllowWeaponSwitch = true;
  474. m_bFreezePeriod = true;
  475. m_iNumTerrorist = m_iNumCT = 0; // number of players per team
  476. m_flRestartRoundTime = 0.1f; // restart first round as soon as possible
  477. m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0;
  478. m_bFirstConnected = false;
  479. m_bCompleteReset = false;
  480. m_iAccountTerrorist = m_iAccountCT = 0;
  481. m_iNumCTWins = 0;
  482. m_iNumTerroristWins = 0;
  483. m_iNumConsecutiveCTLoses = 0;
  484. m_iNumConsecutiveTerroristLoses = 0;
  485. m_bTargetBombed = false;
  486. m_bBombDefused = false;
  487. m_iTotalRoundsPlayed = -1;
  488. m_iUnBalancedRounds = 0;
  489. m_flGameStartTime = 0;
  490. m_iHostagesRemaining = 0;
  491. m_bLevelInitialized = false;
  492. m_bLogoMap = false;
  493. m_tmNextPeriodicThink = 0;
  494. m_bMapHasBombTarget = false;
  495. m_bMapHasRescueZone = false;
  496. m_iSpawnPointCount_Terrorist = 0;
  497. m_iSpawnPointCount_CT = 0;
  498. m_bTCantBuy = false;
  499. m_bCTCantBuy = false;
  500. m_bMapHasBuyZone = false;
  501. m_iLoserBonus = 0;
  502. m_iHostagesRescued = 0;
  503. m_iHostagesTouched = 0;
  504. m_flNextHostageAnnouncement = 0.0f;
  505. //=============================================================================
  506. // HPE_BEGIN
  507. // [dwenger] Reset rescue-related achievement values
  508. //=============================================================================
  509. // [tj] reset flawless and lossless round related flags
  510. m_bNoTerroristsKilled = true;
  511. m_bNoCTsKilled = true;
  512. m_bNoTerroristsDamaged = true;
  513. m_bNoCTsDamaged = true;
  514. m_pFirstKill = NULL;
  515. m_firstKillTime = 0;
  516. // [menglish] Reset fun fact values
  517. m_pFirstBlood = NULL;
  518. m_firstBloodTime = 0;
  519. m_bCanDonateWeapons = true;
  520. // [dwenger] Reset rescue-related achievement values
  521. m_pLastRescuer = NULL;
  522. m_iNumRescuers = 0;
  523. m_hostageWasInjured = false;
  524. m_hostageWasKilled = false;
  525. m_pFunFactManager = new CCSFunFactMgr();
  526. m_pFunFactManager->Init();
  527. //=============================================================================
  528. // HPE_END
  529. //=============================================================================
  530. m_iHaveEscaped = 0;
  531. m_bMapHasEscapeZone = false;
  532. m_iNumEscapers = 0;
  533. m_iNumEscapeRounds = 0;
  534. m_iMapHasVIPSafetyZone = 0;
  535. m_pVIP = NULL;
  536. m_iConsecutiveVIP = 0;
  537. m_bMapHasBombZone = false;
  538. m_bBombDropped = false;
  539. m_bBombPlanted = false;
  540. m_pLastBombGuy = NULL;
  541. m_bAllowWeaponSwitch = true;
  542. m_flNextHostageAnnouncement = gpGlobals->curtime; // asap.
  543. ReadMultiplayCvars();
  544. m_pPrices = NULL;
  545. m_bBlackMarket = false;
  546. m_bDontUploadStats = false;
  547. // Create the team managers
  548. for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ )
  549. {
  550. CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "cs_team_manager" ));
  551. pTeam->Init( sTeamNames[i], i );
  552. g_Teams.AddToTail( pTeam );
  553. }
  554. if ( filesystem->FileExists( UTIL_VarArgs( "maps/cfg/%s.cfg", STRING(gpGlobals->mapname) ) ) )
  555. {
  556. // Execute a map specific cfg file - as in Day of Defeat
  557. // Map names cannot contain quotes or control characters so this is safe but silly that we have to do it.
  558. engine->ServerCommand( UTIL_VarArgs( "exec \"%s.cfg\" */maps\n", STRING(gpGlobals->mapname) ) );
  559. engine->ServerExecute();
  560. }
  561. #ifndef CLIENT_DLL
  562. // stats
  563. if ( g_flGameStatsUpdateTime == 0.0f )
  564. {
  565. memset( g_iWeaponPurchases, 0, sizeof( g_iWeaponPurchases) );
  566. memset( g_iTerroristVictories, 0, sizeof( g_iTerroristVictories) );
  567. memset( g_iCounterTVictories, 0, sizeof( g_iTerroristVictories) );
  568. g_flGameStatsUpdateTime = CS_GAME_STATS_UPDATE; //Next update is between 22 and 24 hours.
  569. }
  570. #endif
  571. }
  572. void CCSGameRules::AddPricesToTable( weeklyprice_t prices )
  573. {
  574. int iIndex = m_StringTableBlackMarket->FindStringIndex( "blackmarket_prices" );
  575. if ( iIndex == INVALID_STRING_INDEX )
  576. {
  577. m_StringTableBlackMarket->AddString( CBaseEntity::IsServer(), "blackmarket_prices", sizeof( weeklyprice_t), &prices );
  578. }
  579. else
  580. {
  581. m_StringTableBlackMarket->SetStringUserData( iIndex, sizeof( weeklyprice_t), &prices );
  582. }
  583. SetBlackMarketPrices( false );
  584. }
  585. //-----------------------------------------------------------------------------
  586. // Purpose:
  587. //-----------------------------------------------------------------------------
  588. CCSGameRules::~CCSGameRules()
  589. {
  590. // Note, don't delete each team since they are in the gEntList and will
  591. // automatically be deleted from there, instead.
  592. g_Teams.Purge();
  593. if( m_pFunFactManager )
  594. {
  595. delete m_pFunFactManager;
  596. }
  597. }
  598. //-----------------------------------------------------------------------------
  599. // Purpose:
  600. //-----------------------------------------------------------------------------
  601. void CCSGameRules::UpdateClientData( CBasePlayer *player )
  602. {
  603. }
  604. //-----------------------------------------------------------------------------
  605. // Purpose: TF2 Specific Client Commands
  606. // Input :
  607. // Output :
  608. //-----------------------------------------------------------------------------
  609. bool CCSGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  610. {
  611. CCSPlayer *pPlayer = ToCSPlayer( pEdict );
  612. if ( FStrEq( args[0], "changeteam" ) )
  613. {
  614. return true;
  615. }
  616. else if ( FStrEq( args[0], "nextmap" ) )
  617. {
  618. if ( pPlayer->m_iNextTimeCheck < gpGlobals->curtime )
  619. {
  620. char szNextMap[32];
  621. if ( nextlevel.GetString() && *nextlevel.GetString() )
  622. {
  623. Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
  624. }
  625. else
  626. {
  627. GetNextLevelName( szNextMap, sizeof( szNextMap ) );
  628. }
  629. ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szNextMap);
  630. pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1;
  631. }
  632. return true;
  633. }
  634. else if( pPlayer->ClientCommand( args ) )
  635. {
  636. return true;
  637. }
  638. else if( BaseClass::ClientCommand( pEdict, args ) )
  639. {
  640. return true;
  641. }
  642. else if ( TheBots->ServerCommand( args.GetCommandString() ) )
  643. {
  644. return true;
  645. }
  646. else
  647. {
  648. return TheBots->ClientCommand( pPlayer, args );
  649. }
  650. }
  651. //-----------------------------------------------------------------------------
  652. // Purpose: Player has just spawned. Equip them.
  653. //-----------------------------------------------------------------------------
  654. void CCSGameRules::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
  655. {
  656. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer * >( CBaseEntity::Instance( pEntity ) );
  657. if ( pPlayer )
  658. {
  659. char const *pszCommand = pKeyValues->GetName();
  660. if ( pszCommand && pszCommand[0] )
  661. {
  662. if ( FStrEq( pszCommand, "ClanTagChanged" ) )
  663. {
  664. pPlayer->SetClanTag( pKeyValues->GetString( "tag", "" ) );
  665. const char *teamName = "UNKNOWN";
  666. if ( pPlayer->GetTeam() )
  667. {
  668. teamName = pPlayer->GetTeam()->GetName();
  669. }
  670. UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"clantag\" (value \"%s\")\n",
  671. pPlayer->GetPlayerName(),
  672. pPlayer->GetUserID(),
  673. pPlayer->GetNetworkIDString(),
  674. teamName,
  675. pKeyValues->GetString( "tag", "unknown" ) );
  676. }
  677. }
  678. }
  679. BaseClass::ClientCommandKeyValues( pEntity, pKeyValues );
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose: Player has just spawned. Equip them.
  683. //-----------------------------------------------------------------------------
  684. void CCSGameRules::PlayerSpawn( CBasePlayer *pBasePlayer )
  685. {
  686. CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer );
  687. if ( !pPlayer )
  688. Error( "PlayerSpawn" );
  689. if ( pPlayer->State_Get() != STATE_ACTIVE )
  690. return;
  691. pPlayer->EquipSuit();
  692. bool addDefault = true;
  693. CBaseEntity *pWeaponEntity = NULL;
  694. while ( ( pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL )
  695. {
  696. if ( addDefault )
  697. {
  698. // remove all our weapons and armor before touching the first game_player_equip
  699. pPlayer->RemoveAllItems( true );
  700. }
  701. pWeaponEntity->Touch( pPlayer );
  702. addDefault = false;
  703. }
  704. if ( addDefault || pPlayer->m_bIsVIP )
  705. pPlayer->GiveDefaultItems();
  706. }
  707. void CCSGameRules::BroadcastSound( const char *sound, int team )
  708. {
  709. CBroadcastRecipientFilter filter;
  710. filter.MakeReliable();
  711. if( team != -1 )
  712. {
  713. filter.RemoveAllRecipients();
  714. filter.AddRecipientsByTeam( GetGlobalTeam(team) );
  715. }
  716. UserMessageBegin ( filter, "SendAudio" );
  717. WRITE_STRING( sound );
  718. MessageEnd();
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Purpose: Player has just spawned. Equip them.
  722. //-----------------------------------------------------------------------------
  723. // return a multiplier that should adjust the damage done by a blast at position vecSrc to something at the position
  724. // vecEnd. This will take into account the density of an entity that blocks the line of sight from one position to
  725. // the other.
  726. //
  727. // this algorithm was taken from the HL2 version of RadiusDamage.
  728. float CCSGameRules::GetExplosionDamageAdjustment(Vector & vecSrc, Vector & vecEnd, CBaseEntity *pEntityToIgnore)
  729. {
  730. float retval = 0.0;
  731. trace_t tr;
  732. UTIL_TraceLine(vecSrc, vecEnd, MASK_SHOT, pEntityToIgnore, COLLISION_GROUP_NONE, &tr);
  733. if (tr.fraction == 1.0)
  734. {
  735. retval = 1.0;
  736. }
  737. else if (!(tr.DidHitWorld()) && (tr.m_pEnt != NULL) && (tr.m_pEnt != pEntityToIgnore) && (tr.m_pEnt->GetOwnerEntity() != pEntityToIgnore))
  738. {
  739. // if we didn't hit world geometry perhaps there's still damage to be done here.
  740. CBaseEntity *blockingEntity = tr.m_pEnt;
  741. // check to see if this part of the player is visible if entities are ignored.
  742. UTIL_TraceLine(vecSrc, vecEnd, CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &tr);
  743. if (tr.fraction == 1.0)
  744. {
  745. if ((blockingEntity != NULL) && (blockingEntity->VPhysicsGetObject() != NULL))
  746. {
  747. int nMaterialIndex = blockingEntity->VPhysicsGetObject()->GetMaterialIndex();
  748. float flDensity;
  749. float flThickness;
  750. float flFriction;
  751. float flElasticity;
  752. physprops->GetPhysicsProperties( nMaterialIndex, &flDensity,
  753. &flThickness, &flFriction, &flElasticity );
  754. const float DENSITY_ABSORB_ALL_DAMAGE = 3000.0;
  755. float scale = flDensity / DENSITY_ABSORB_ALL_DAMAGE;
  756. if ((scale >= 0.0) && (scale < 1.0))
  757. {
  758. retval = 1.0 - scale;
  759. }
  760. else if (scale < 0.0)
  761. {
  762. // should never happen, but just in case.
  763. retval = 1.0;
  764. }
  765. }
  766. else
  767. {
  768. retval = 0.75; // we're blocked by something that isn't an entity with a physics module or world geometry, just cut damage in half for now.
  769. }
  770. }
  771. }
  772. return retval;
  773. }
  774. // returns the percentage of the player that is visible from the given point in the world.
  775. // return value is between 0 and 1.
  776. float CCSGameRules::GetAmountOfEntityVisible(Vector & vecSrc, CBaseEntity *entity)
  777. {
  778. float retval = 0.0;
  779. const float damagePercentageChest = 0.40;
  780. const float damagePercentageHead = 0.20;
  781. const float damagePercentageFeet = 0.20;
  782. const float damagePercentageRightSide = 0.10;
  783. const float damagePercentageLeftSide = 0.10;
  784. if (!(entity->IsPlayer()))
  785. {
  786. // the entity is not a player, so the damage is all or nothing.
  787. Vector vecTarget;
  788. vecTarget = entity->BodyTarget(vecSrc, false);
  789. return GetExplosionDamageAdjustment(vecSrc, vecTarget, entity);
  790. }
  791. CCSPlayer *player = (CCSPlayer *)entity;
  792. // check what parts of the player we can see from this point and modify the return value accordingly.
  793. float chestHeightFromFeet;
  794. float armDistanceFromChest = HalfHumanWidth;
  795. // calculate positions of various points on the target player's body
  796. Vector vecFeet = player->GetAbsOrigin();
  797. Vector vecChest = player->BodyTarget(vecSrc, false);
  798. chestHeightFromFeet = vecChest.z - vecFeet.z; // compute the distance from the chest to the feet. (this accounts for ducking and the like)
  799. Vector vecHead = player->GetAbsOrigin();
  800. vecHead.z += HumanHeight;
  801. Vector vecRightFacing;
  802. AngleVectors(player->GetAbsAngles(), NULL, &vecRightFacing, NULL);
  803. vecRightFacing.NormalizeInPlace();
  804. vecRightFacing = vecRightFacing * armDistanceFromChest;
  805. Vector vecLeftSide = player->GetAbsOrigin();
  806. vecLeftSide.x -= vecRightFacing.x;
  807. vecLeftSide.y -= vecRightFacing.y;
  808. vecLeftSide.z += chestHeightFromFeet;
  809. Vector vecRightSide = player->GetAbsOrigin();
  810. vecRightSide.x += vecRightFacing.x;
  811. vecRightSide.y += vecRightFacing.y;
  812. vecRightSide.z += chestHeightFromFeet;
  813. // check chest
  814. float damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecChest, entity);
  815. retval += (damagePercentageChest * damageAdjustment);
  816. // check top of head
  817. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecHead, entity);
  818. retval += (damagePercentageHead * damageAdjustment);
  819. // check feet
  820. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecFeet, entity);
  821. retval += (damagePercentageFeet * damageAdjustment);
  822. // check left "edge"
  823. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecLeftSide, entity);
  824. retval += (damagePercentageLeftSide * damageAdjustment);
  825. // check right "edge"
  826. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecRightSide, entity);
  827. retval += (damagePercentageRightSide * damageAdjustment);
  828. return retval;
  829. }
  830. void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity * pEntityIgnore )
  831. {
  832. RadiusDamage( info, vecSrcIn, flRadius, iClassIgnore, false );
  833. }
  834. // Add the ability to ignore the world trace
  835. void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld )
  836. {
  837. CBaseEntity *pEntity = NULL;
  838. trace_t tr;
  839. float falloff, damagePercentage;
  840. Vector vecSpot;
  841. Vector vecToTarget;
  842. Vector vecEndPos;
  843. //=============================================================================
  844. // HPE_BEGIN:
  845. //=============================================================================
  846. // [tj] The number of enemy players this explosion killed
  847. int numberOfEnemyPlayersKilledByThisExplosion = 0;
  848. // [tj] who we award the achievement to if enough players are killed
  849. CCSPlayer* pCSExplosionAttacker = ToCSPlayer(info.GetAttacker());
  850. // [tj] used to determine which achievement to award for sufficient kills
  851. CBaseEntity* pInflictor = info.GetInflictor();
  852. bool isGrenade = pInflictor && V_strcmp(pInflictor->GetClassname(), "hegrenade_projectile") == 0;
  853. bool isBomb = pInflictor && V_strcmp(pInflictor->GetClassname(), "planted_c4") == 0;
  854. //=============================================================================
  855. // HPE_END
  856. //=============================================================================
  857. vecEndPos.Init();
  858. Vector vecSrc = vecSrcIn;
  859. damagePercentage = 1.0;
  860. if ( flRadius )
  861. falloff = info.GetDamage() / flRadius;
  862. else
  863. falloff = 1.0;
  864. int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER) ? true : false;
  865. vecSrc.z += 1;// in case grenade is lying on the ground
  866. // iterate on all entities in the vicinity.
  867. for ( CEntitySphereQuery sphere( vecSrc, flRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  868. {
  869. //=============================================================================
  870. // HPE_BEGIN:
  871. // [tj] We have to save whether or not the player is killed so we don't give credit
  872. // for pre-dead players.
  873. //=============================================================================
  874. bool wasAliveBeforeExplosion = false;
  875. CCSPlayer* pCSExplosionVictim = ToCSPlayer(pEntity);
  876. if (pCSExplosionVictim)
  877. {
  878. wasAliveBeforeExplosion = pCSExplosionVictim->IsAlive();
  879. }
  880. //=============================================================================
  881. // HPE_END
  882. //=============================================================================
  883. if ( pEntity->m_takedamage != DAMAGE_NO )
  884. {
  885. // UNDONE: this should check a damage mask, not an ignore
  886. if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  887. {// houndeyes don't hurt other houndeyes with their attack
  888. continue;
  889. }
  890. // blasts don't travel into or out of water
  891. if ( !bIgnoreWorld )
  892. {
  893. if (bInWater && pEntity->GetWaterLevel() == 0)
  894. continue;
  895. if (!bInWater && pEntity->GetWaterLevel() == 3)
  896. continue;
  897. }
  898. // radius damage can only be blocked by the world
  899. vecSpot = pEntity->BodyTarget( vecSrc );
  900. bool bHit = false;
  901. if( bIgnoreWorld )
  902. {
  903. vecEndPos = vecSpot;
  904. bHit = true;
  905. }
  906. else
  907. {
  908. // get the percentage of the target entity that is visible from the
  909. // explosion position.
  910. damagePercentage = GetAmountOfEntityVisible(vecSrc, pEntity);
  911. if (damagePercentage > 0.0)
  912. {
  913. vecEndPos = vecSpot;
  914. bHit = true;
  915. }
  916. }
  917. if ( bHit )
  918. {
  919. // the explosion can 'see' this entity, so hurt them!
  920. //vecToTarget = ( vecSrc - vecEndPos );
  921. vecToTarget = ( vecEndPos - vecSrc );
  922. // use a Gaussian function to describe the damage falloff over distance, with flRadius equal to 3 * sigma
  923. // this results in the following values:
  924. //
  925. // Range Fraction Damage
  926. // 0.0 100%
  927. // 0.1 96%
  928. // 0.2 84%
  929. // 0.3 67%
  930. // 0.4 49%
  931. // 0.5 32%
  932. // 0.6 20%
  933. // 0.7 11%
  934. // 0.8 6%
  935. // 0.9 3%
  936. // 1.0 1%
  937. float fDist = vecToTarget.Length();
  938. float fSigma = flRadius / 3.0f; // flRadius specifies 3rd standard deviation (0.0111 damage at this range)
  939. float fGaussianFalloff = exp(-fDist * fDist / (2.0f * fSigma * fSigma));
  940. float flAdjustedDamage = info.GetDamage() * fGaussianFalloff * damagePercentage;
  941. if ( flAdjustedDamage > 0 )
  942. {
  943. CTakeDamageInfo adjustedInfo = info;
  944. adjustedInfo.SetDamage( flAdjustedDamage );
  945. Vector dir = vecToTarget;
  946. VectorNormalize( dir );
  947. // If we don't have a damage force, manufacture one
  948. if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
  949. {
  950. CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc, 1.5 /* explosion scale! */ );
  951. }
  952. else
  953. {
  954. // Assume the force passed in is the maximum force. Decay it based on falloff.
  955. float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
  956. adjustedInfo.SetDamageForce( dir * flForce );
  957. adjustedInfo.SetDamagePosition( vecSrc );
  958. }
  959. Vector vecTarget;
  960. vecTarget = pEntity->BodyTarget(vecSrc, false);
  961. UTIL_TraceLine(vecSrc, vecTarget, MASK_SHOT, NULL, COLLISION_GROUP_NONE, &tr);
  962. // blasts always hit chest
  963. tr.hitgroup = HITGROUP_GENERIC;
  964. if (tr.fraction != 1.0)
  965. {
  966. // this has to be done to make breakable glass work.
  967. ClearMultiDamage( );
  968. pEntity->DispatchTraceAttack( adjustedInfo, dir, &tr );
  969. ApplyMultiDamage();
  970. }
  971. else
  972. {
  973. pEntity->TakeDamage( adjustedInfo );
  974. }
  975. // Now hit all triggers along the way that respond to damage...
  976. pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, vecEndPos, dir );
  977. //=============================================================================
  978. // HPE_BEGIN:
  979. // [sbodenbender] Increment grenade damage stat
  980. //=============================================================================
  981. if (pCSExplosionVictim && pCSExplosionAttacker && isGrenade)
  982. {
  983. CCS_GameStats.IncrementStat(pCSExplosionAttacker, CSSTAT_GRENADE_DAMAGE, static_cast<int>(adjustedInfo.GetDamage()));
  984. }
  985. //=============================================================================
  986. // HPE_END
  987. //=============================================================================
  988. }
  989. }
  990. }
  991. //=============================================================================
  992. // HPE_BEGIN:
  993. // [tj] Count up victims of area of effect damage for achievement purposes
  994. //=============================================================================
  995. if (pCSExplosionVictim)
  996. {
  997. //If the bomb is exploding, set the attacker to the planter (we can't put this in the CTakeDamageInfo, since
  998. //players aren't supposed to get credit for bomb kills)
  999. if (isBomb)
  1000. {
  1001. CPlantedC4* bomb = static_cast<CPlantedC4*> (pInflictor);
  1002. if (bomb)
  1003. {
  1004. pCSExplosionAttacker = bomb->GetPlanter();
  1005. }
  1006. }
  1007. //Count check to make sure we killed an enemy player
  1008. if( pCSExplosionAttacker &&
  1009. !pCSExplosionVictim->IsAlive() &&
  1010. wasAliveBeforeExplosion &&
  1011. pCSExplosionVictim->GetTeamNumber() != pCSExplosionAttacker->GetTeamNumber())
  1012. {
  1013. numberOfEnemyPlayersKilledByThisExplosion++;
  1014. }
  1015. }
  1016. //=============================================================================
  1017. // HPE_END
  1018. //=============================================================================
  1019. }
  1020. //=============================================================================
  1021. // HPE_BEGIN:
  1022. // [tj] //Depending on which type of explosion it was, award the appropriate achievement.
  1023. //=============================================================================
  1024. if (pCSExplosionAttacker && isGrenade && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::GrenadeMultiKill_MinKills)
  1025. {
  1026. pCSExplosionAttacker->AwardAchievement(CSGrenadeMultikill);
  1027. pCSExplosionAttacker->CheckMaxGrenadeKills(numberOfEnemyPlayersKilledByThisExplosion);
  1028. }
  1029. if (pCSExplosionAttacker && isBomb && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::BombMultiKill_MinKills)
  1030. {
  1031. pCSExplosionAttacker->AwardAchievement(CSBombMultikill);
  1032. }
  1033. //=============================================================================
  1034. // HPE_END
  1035. //=============================================================================
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Purpose:
  1039. // Input : *pVictim -
  1040. // *pKiller -
  1041. // *pInflictor -
  1042. //-----------------------------------------------------------------------------
  1043. void CCSGameRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  1044. {
  1045. // Work out what killed the player, and send a message to all clients about it
  1046. const char *killer_weapon_name = "world"; // by default, the player is killed by the world
  1047. int killer_ID = 0;
  1048. // Find the killer & the scorer
  1049. CBaseEntity *pInflictor = info.GetInflictor();
  1050. CBaseEntity *pKiller = info.GetAttacker();
  1051. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
  1052. CCSPlayer *pCSVictim = (CCSPlayer*)(pVictim);
  1053. bool bHeadshot = false;
  1054. if ( pScorer ) // Is the killer a client?
  1055. {
  1056. killer_ID = pScorer->GetUserID();
  1057. if( info.GetDamageType() & DMG_HEADSHOT )
  1058. {
  1059. //to enable drawing the headshot icon as well as the weapon icon,
  1060. bHeadshot = true;
  1061. }
  1062. if ( pInflictor )
  1063. {
  1064. if ( pInflictor == pScorer )
  1065. {
  1066. // If the inflictor is the killer, then it must be their current weapon doing the damage
  1067. if ( pScorer->GetActiveWeapon() )
  1068. {
  1069. killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname(); //GetDeathNoticeName();
  1070. }
  1071. }
  1072. else
  1073. {
  1074. killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy
  1075. }
  1076. }
  1077. }
  1078. else
  1079. {
  1080. killer_weapon_name = STRING( pInflictor->m_iClassname );
  1081. }
  1082. // strip the NPC_* or weapon_* from the inflictor's classname
  1083. if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 )
  1084. {
  1085. killer_weapon_name += 7;
  1086. }
  1087. else if ( strncmp( killer_weapon_name, "NPC_", 8 ) == 0 )
  1088. {
  1089. killer_weapon_name += 8;
  1090. }
  1091. else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 )
  1092. {
  1093. killer_weapon_name += 5;
  1094. }
  1095. else if( strncmp( killer_weapon_name, "hegrenade", 9 ) == 0 ) //"hegrenade_projectile"
  1096. {
  1097. killer_weapon_name = "hegrenade";
  1098. }
  1099. else if( strncmp( killer_weapon_name, "flashbang", 9 ) == 0 ) //"flashbang_projectile"
  1100. {
  1101. killer_weapon_name = "flashbang";
  1102. }
  1103. IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
  1104. if ( event )
  1105. {
  1106. event->SetInt("userid", pVictim->GetUserID() );
  1107. event->SetInt("attacker", killer_ID );
  1108. event->SetString("weapon", killer_weapon_name );
  1109. event->SetInt("headshot", bHeadshot ? 1 : 0 );
  1110. event->SetInt("priority", bHeadshot ? 8 : 7 ); // HLTV event priority, not transmitted
  1111. if ( pCSVictim->GetDeathFlags() & CS_DEATH_DOMINATION )
  1112. {
  1113. event->SetInt( "dominated", 1 );
  1114. }
  1115. else if ( pCSVictim->GetDeathFlags() & CS_DEATH_REVENGE )
  1116. {
  1117. event->SetInt( "revenge", 1 );
  1118. }
  1119. gameeventmanager->FireEvent( event );
  1120. }
  1121. }
  1122. //=========================================================
  1123. //=========================================================
  1124. void CCSGameRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  1125. {
  1126. CBaseEntity *pInflictor = info.GetInflictor();
  1127. CBaseEntity *pKiller = info.GetAttacker();
  1128. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
  1129. CCSPlayer *pCSVictim = (CCSPlayer *)pVictim;
  1130. CCSPlayer *pCSScorer = (CCSPlayer *)pScorer;
  1131. CCS_GameStats.PlayerKilled( pVictim, info );
  1132. //=============================================================================
  1133. // HPE_BEGIN:
  1134. // [tj] Flag the round as non-lossless for the appropriate team.
  1135. // [menglish] Set the death flags depending on a nemesis system
  1136. //=============================================================================
  1137. if (pVictim->GetTeamNumber() == TEAM_TERRORIST)
  1138. {
  1139. m_bNoTerroristsKilled = false;
  1140. m_bNoTerroristsDamaged = false;
  1141. }
  1142. if (pVictim->GetTeamNumber() == TEAM_CT)
  1143. {
  1144. m_bNoCTsKilled = false;
  1145. m_bNoCTsDamaged = false;
  1146. }
  1147. m_bCanDonateWeapons = false;
  1148. if ( m_pFirstKill == NULL && pCSScorer != pVictim )
  1149. {
  1150. m_pFirstKill = pCSScorer;
  1151. m_firstKillTime = gpGlobals->curtime - m_fRoundStartTime;
  1152. }
  1153. // determine if this kill affected a nemesis relationship
  1154. int iDeathFlags = 0;
  1155. if ( pScorer )
  1156. {
  1157. CCS_GameStats.CalculateOverkill( pCSScorer, pCSVictim);
  1158. CCS_GameStats.CalcDominationAndRevenge( pCSScorer, pCSVictim, &iDeathFlags );
  1159. }
  1160. pCSVictim->SetDeathFlags( iDeathFlags );
  1161. //=============================================================================
  1162. // HPE_END
  1163. //=============================================================================
  1164. // If we're killed by the C4, we do a subset of BaseClass::PlayerKilled()
  1165. // Specifically, we shouldn't lose any points or show death notices, to match goldsrc
  1166. if ( Q_strcmp(pKiller->GetClassname(), "planted_c4" ) == 0 )
  1167. {
  1168. // dvsents2: uncomment when removing all FireTargets
  1169. // variant_t value;
  1170. // g_EventQueue.AddEvent( "game_playerdie", "Use", value, 0, pVictim, pVictim );
  1171. FireTargets( "game_playerdie", pVictim, pVictim, USE_TOGGLE, 0 );
  1172. }
  1173. else
  1174. {
  1175. BaseClass::PlayerKilled( pVictim, info );
  1176. }
  1177. // check for team-killing, and give monetary rewards/penalties
  1178. // Find the killer & the scorer
  1179. if ( !pScorer )
  1180. return;
  1181. if ( IPointsForKill( pScorer, pVictim ) < 0 )
  1182. {
  1183. // team-killer!
  1184. pCSScorer->AddAccount( -3300 );
  1185. ++pCSScorer->m_iTeamKills;
  1186. pCSScorer->m_bJustKilledTeammate = true;
  1187. ClientPrint( pCSScorer, HUD_PRINTCENTER, "#Killed_Teammate" );
  1188. if ( mp_autokick.GetBool() )
  1189. {
  1190. char strTeamKills[64];
  1191. Q_snprintf( strTeamKills, sizeof( strTeamKills ), "%d", pCSScorer->m_iTeamKills );
  1192. ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Game_teammate_kills", strTeamKills ); // this includes a " of 3" in it
  1193. if ( pCSScorer->m_iTeamKills >= 3 )
  1194. {
  1195. ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Banned_For_Killing_Teammates" );
  1196. engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pCSScorer->GetUserID() ) );
  1197. }
  1198. else if ( mp_spawnprotectiontime.GetInt() > 0 && GetRoundElapsedTime() < mp_spawnprotectiontime.GetInt() )
  1199. {
  1200. ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Banned_For_Killing_Teammates" );
  1201. engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pCSScorer->GetUserID() ) );
  1202. }
  1203. }
  1204. if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) )
  1205. {
  1206. pCSScorer->m_iDisplayHistoryBits |= DHF_FRIEND_KILLED;
  1207. pCSScorer->HintMessage( "#Hint_careful_around_teammates", false );
  1208. }
  1209. }
  1210. else
  1211. {
  1212. //=============================================================================
  1213. // HPE_BEGIN:
  1214. // [tj] Added a check to make sure we don't get money for suicides.
  1215. //=============================================================================
  1216. if (pCSScorer != pCSVictim)
  1217. {
  1218. //=============================================================================
  1219. // HPE_END
  1220. //=============================================================================
  1221. if ( pCSVictim->IsVIP() )
  1222. {
  1223. pCSScorer->HintMessage( "#Hint_reward_for_killing_vip", true );
  1224. pCSScorer->AddAccount( 2500 );
  1225. }
  1226. else
  1227. {
  1228. pCSScorer->AddAccount( 300 );
  1229. }
  1230. }
  1231. if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_ENEMY_KILLED) )
  1232. {
  1233. pCSScorer->m_iDisplayHistoryBits |= DHF_ENEMY_KILLED;
  1234. pCSScorer->HintMessage( "#Hint_win_round_by_killing_enemy", false );
  1235. }
  1236. }
  1237. }
  1238. void CCSGameRules::InitDefaultAIRelationships()
  1239. {
  1240. // Allocate memory for default relationships
  1241. CBaseCombatCharacter::AllocateDefaultRelationships();
  1242. // --------------------------------------------------------------
  1243. // First initialize table so we can report missing relationships
  1244. // --------------------------------------------------------------
  1245. int i, j;
  1246. for (i=0;i<NUM_AI_CLASSES;i++)
  1247. {
  1248. for (j=0;j<NUM_AI_CLASSES;j++)
  1249. {
  1250. // By default all relationships are neutral of priority zero
  1251. CBaseCombatCharacter::SetDefaultRelationship( (Class_T)i, (Class_T)j, D_NU, 0 );
  1252. }
  1253. }
  1254. }
  1255. //------------------------------------------------------------------------------
  1256. // Purpose : Return classify text for classify type
  1257. //------------------------------------------------------------------------------
  1258. const char *CCSGameRules::AIClassText(int classType)
  1259. {
  1260. switch (classType)
  1261. {
  1262. case CLASS_NONE: return "CLASS_NONE";
  1263. case CLASS_PLAYER: return "CLASS_PLAYER";
  1264. default: return "MISSING CLASS in ClassifyText()";
  1265. }
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. // Purpose: When gaining new technologies in TF, prevent auto switching if we
  1269. // receive a weapon during the switch
  1270. // Input : *pPlayer -
  1271. // *pWeapon -
  1272. // Output : Returns true on success, false on failure.
  1273. //-----------------------------------------------------------------------------
  1274. bool CCSGameRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
  1275. {
  1276. bool bIsBeingGivenItem = false;
  1277. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  1278. if ( pCSPlayer && pCSPlayer->IsBeingGivenItem() )
  1279. bIsBeingGivenItem = true;
  1280. if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() && !bIsBeingGivenItem )
  1281. {
  1282. // Player has an active item, so let's check cl_autowepswitch.
  1283. const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" );
  1284. if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 )
  1285. {
  1286. return false;
  1287. }
  1288. }
  1289. if ( pPlayer->IsBot() && !bIsBeingGivenItem )
  1290. {
  1291. return false;
  1292. }
  1293. if ( !GetAllowWeaponSwitch() )
  1294. {
  1295. return false;
  1296. }
  1297. return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon );
  1298. }
  1299. //-----------------------------------------------------------------------------
  1300. // Purpose:
  1301. // Input : allow -
  1302. //-----------------------------------------------------------------------------
  1303. void CCSGameRules::SetAllowWeaponSwitch( bool allow )
  1304. {
  1305. m_bAllowWeaponSwitch = allow;
  1306. }
  1307. //-----------------------------------------------------------------------------
  1308. // Purpose:
  1309. // Output : Returns true on success, false on failure.
  1310. //-----------------------------------------------------------------------------
  1311. bool CCSGameRules::GetAllowWeaponSwitch()
  1312. {
  1313. return m_bAllowWeaponSwitch;
  1314. }
  1315. //-----------------------------------------------------------------------------
  1316. // Purpose:
  1317. // Input : *pPlayer -
  1318. // Output : const char
  1319. //-----------------------------------------------------------------------------
  1320. const char *CCSGameRules::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
  1321. {
  1322. Assert( pPlayer );
  1323. return BaseClass::SetDefaultPlayerTeam( pPlayer );
  1324. }
  1325. void CCSGameRules::LevelInitPreEntity()
  1326. {
  1327. BaseClass::LevelInitPreEntity();
  1328. // TODO for CZ-style hostages: TheHostageChatter->Precache();
  1329. }
  1330. void CCSGameRules::LevelInitPostEntity()
  1331. {
  1332. BaseClass::LevelInitPostEntity();
  1333. m_bLevelInitialized = false; // re-count CT and T start spots now that they exist
  1334. // Figure out from the entities in the map what kind of map this is (bomb run, prison escape, etc).
  1335. CheckMapConditions();
  1336. }
  1337. INetworkStringTable *g_StringTableBlackMarket = NULL;
  1338. void CCSGameRules::CreateCustomNetworkStringTables( void )
  1339. {
  1340. m_StringTableBlackMarket = g_StringTableBlackMarket;
  1341. if ( 0 )//mp_dynamicpricing.GetBool() )
  1342. {
  1343. m_bBlackMarket = BlackMarket_DownloadPrices();
  1344. if ( m_bBlackMarket == false )
  1345. {
  1346. Msg( "ERROR: mp_dynamicpricing set to 1 but couldn't download the price list!\n" );
  1347. }
  1348. }
  1349. else
  1350. {
  1351. m_bBlackMarket = false;
  1352. SetBlackMarketPrices( true );
  1353. }
  1354. }
  1355. float CCSGameRules::FlPlayerFallDamage( CBasePlayer *pPlayer )
  1356. {
  1357. float fFallVelocity = pPlayer->m_Local.m_flFallVelocity - CS_PLAYER_MAX_SAFE_FALL_SPEED;
  1358. float fallDamage = fFallVelocity * CS_DAMAGE_FOR_FALL_SPEED * 1.25;
  1359. if ( fallDamage > 0.0f )
  1360. {
  1361. // let the bots know
  1362. IGameEvent * event = gameeventmanager->CreateEvent( "player_falldamage" );
  1363. if ( event )
  1364. {
  1365. event->SetInt( "userid", pPlayer->GetUserID() );
  1366. event->SetFloat( "damage", fallDamage );
  1367. event->SetInt( "priority", 4 ); // HLTV event priority, not transmitted
  1368. gameeventmanager->FireEvent( event );
  1369. }
  1370. }
  1371. return fallDamage;
  1372. }
  1373. void CCSGameRules::ClientDisconnected( edict_t *pClient )
  1374. {
  1375. BaseClass::ClientDisconnected( pClient );
  1376. //=============================================================================
  1377. // HPE_BEGIN:
  1378. // [tj] Clear domination data when a player disconnects
  1379. //=============================================================================
  1380. CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pClient ) );
  1381. if ( pPlayer )
  1382. {
  1383. pPlayer->RemoveNemesisRelationships();
  1384. }
  1385. //=============================================================================
  1386. // HPE_END
  1387. //=============================================================================
  1388. CheckWinConditions();
  1389. }
  1390. // Called when game rules are destroyed by CWorld
  1391. void CCSGameRules::LevelShutdown()
  1392. {
  1393. int iLevelIndex = GetCSLevelIndex( STRING( gpGlobals->mapname ) );
  1394. if ( iLevelIndex != -1 )
  1395. {
  1396. g_iTerroristVictories[iLevelIndex] += m_iNumTerroristWins;
  1397. g_iCounterTVictories[iLevelIndex] += m_iNumCTWins;
  1398. }
  1399. BaseClass::LevelShutdown();
  1400. }
  1401. //---------------------------------------------------------------------------------------------------
  1402. /**
  1403. * Check if the scenario has been won/lost.
  1404. * Return true if the scenario is over, false if the scenario is still in progress
  1405. */
  1406. bool CCSGameRules::CheckWinConditions( void )
  1407. {
  1408. if ( mp_ignore_round_win_conditions.GetBool() )
  1409. {
  1410. return false;
  1411. }
  1412. // If a winner has already been determined.. then get the heck out of here
  1413. if (m_iRoundWinStatus != WINNER_NONE)
  1414. {
  1415. // still check if we lost players to where we need to do a full reset next round...
  1416. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  1417. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  1418. bool bNeededPlayers = false;
  1419. NeededPlayersCheck( bNeededPlayers );
  1420. return true;
  1421. }
  1422. // Initialize the player counts..
  1423. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  1424. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  1425. /***************************** OTHER PLAYER's CHECK *********************************************************/
  1426. bool bNeededPlayers = false;
  1427. if ( NeededPlayersCheck( bNeededPlayers ) )
  1428. return false;
  1429. /****************************** ASSASINATION/VIP SCENARIO CHECK *******************************************************/
  1430. if ( VIPRoundEndCheck( bNeededPlayers ) )
  1431. return true;
  1432. /****************************** PRISON ESCAPE CHECK *******************************************************/
  1433. if ( PrisonRoundEndCheck() )
  1434. return true;
  1435. /****************************** BOMB CHECK ********************************************************/
  1436. if ( BombRoundEndCheck( bNeededPlayers ) )
  1437. return true;
  1438. /***************************** TEAM EXTERMINATION CHECK!! *********************************************************/
  1439. // CounterTerrorists won by virture of elimination
  1440. if ( TeamExterminationCheck( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT, bNeededPlayers ) )
  1441. return true;
  1442. /******************************** HOSTAGE RESCUE CHECK ******************************************************/
  1443. if ( HostageRescueRoundEndCheck( bNeededPlayers ) )
  1444. return true;
  1445. // scenario not won - still in progress
  1446. return false;
  1447. }
  1448. bool CCSGameRules::NeededPlayersCheck( bool &bNeededPlayers )
  1449. {
  1450. // We needed players to start scoring
  1451. // Do we have them now?
  1452. if( !m_iNumSpawnableTerrorist || !m_iNumSpawnableCT )
  1453. {
  1454. Msg( "Game will not start until both teams have players.\n" );
  1455. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_scoring" );
  1456. bNeededPlayers = true;
  1457. m_bFirstConnected = false;
  1458. }
  1459. if ( !m_bFirstConnected && m_iNumSpawnableTerrorist && m_iNumSpawnableCT )
  1460. {
  1461. // Start the round immediately when the first person joins
  1462. // UTIL_LogPrintf( "World triggered \"Game_Commencing\"\n" );
  1463. m_bFreezePeriod = false; //Make sure we are not on the FreezePeriod.
  1464. m_bCompleteReset = true;
  1465. TerminateRound( 3.0f, Game_Commencing );
  1466. m_bFirstConnected = true;
  1467. return true;
  1468. }
  1469. return false;
  1470. }
  1471. void CCSGameRules::InitializePlayerCounts(
  1472. int &NumAliveTerrorist,
  1473. int &NumAliveCT,
  1474. int &NumDeadTerrorist,
  1475. int &NumDeadCT
  1476. )
  1477. {
  1478. NumAliveTerrorist = NumAliveCT = NumDeadCT = NumDeadTerrorist = 0;
  1479. m_iNumTerrorist = m_iNumCT = m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0;
  1480. m_iHaveEscaped = 0;
  1481. // Count how many dead players there are on each team.
  1482. for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ )
  1483. {
  1484. CTeam *pTeam = GetGlobalTeam( iTeam );
  1485. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  1486. {
  1487. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  1488. Assert( pPlayer );
  1489. if ( !pPlayer )
  1490. continue;
  1491. Assert( pPlayer->GetTeamNumber() == pTeam->GetTeamNumber() );
  1492. switch ( pTeam->GetTeamNumber() )
  1493. {
  1494. case TEAM_CT:
  1495. m_iNumCT++;
  1496. if ( pPlayer->State_Get() != STATE_PICKINGCLASS )
  1497. m_iNumSpawnableCT++;
  1498. if ( pPlayer->m_lifeState != LIFE_ALIVE )
  1499. NumDeadCT++;
  1500. else
  1501. NumAliveCT++;
  1502. break;
  1503. case TEAM_TERRORIST:
  1504. m_iNumTerrorist++;
  1505. if ( pPlayer->State_Get() != STATE_PICKINGCLASS )
  1506. m_iNumSpawnableTerrorist++;
  1507. if ( pPlayer->m_lifeState != LIFE_ALIVE )
  1508. NumDeadTerrorist++;
  1509. else
  1510. NumAliveTerrorist++;
  1511. // Check to see if this guy escaped.
  1512. if ( pPlayer->m_bEscaped == true )
  1513. m_iHaveEscaped++;
  1514. break;
  1515. }
  1516. }
  1517. }
  1518. }
  1519. bool CCSGameRules::HostageRescueRoundEndCheck( bool bNeededPlayers )
  1520. {
  1521. // Check to see if 50% of the hostages have been rescued.
  1522. CHostage* hostage = NULL;
  1523. int iNumHostages = g_Hostages.Count();
  1524. int iNumLeftToRescue = 0;
  1525. int i;
  1526. for ( i=0; i<iNumHostages; i++ )
  1527. {
  1528. hostage = g_Hostages[i];
  1529. if ( hostage->m_iHealth > 0 && !hostage->IsRescued() ) // We've found a live hostage. don't end the round
  1530. iNumLeftToRescue++;
  1531. }
  1532. m_iHostagesRemaining = iNumLeftToRescue;
  1533. if ( (iNumLeftToRescue == 0) && (iNumHostages > 0) )
  1534. {
  1535. if ( m_iHostagesRescued >= (iNumHostages * 0.5) )
  1536. {
  1537. m_iAccountCT += 2500;
  1538. if ( !bNeededPlayers )
  1539. {
  1540. m_iNumCTWins ++;
  1541. // Update the clients team score
  1542. UpdateTeamScores();
  1543. }
  1544. CCS_GameStats.Event_AllHostagesRescued();
  1545. // tell the bots all the hostages have been rescued
  1546. IGameEvent * event = gameeventmanager->CreateEvent( "hostage_rescued_all" );
  1547. if ( event )
  1548. {
  1549. gameeventmanager->FireEvent( event );
  1550. }
  1551. TerminateRound( mp_round_restart_delay.GetFloat(), All_Hostages_Rescued );
  1552. return true;
  1553. }
  1554. }
  1555. return false;
  1556. }
  1557. bool CCSGameRules::PrisonRoundEndCheck()
  1558. {
  1559. //MIKETODO: get this working when working on prison escape
  1560. /*
  1561. if (m_bMapHasEscapeZone == true)
  1562. {
  1563. float flEscapeRatio;
  1564. flEscapeRatio = (float) m_iHaveEscaped / (float) m_iNumEscapers;
  1565. if (flEscapeRatio >= m_flRequiredEscapeRatio)
  1566. {
  1567. BroadcastSound( "Event.TERWin" );
  1568. m_iAccountTerrorist += 3150;
  1569. if ( !bNeededPlayers )
  1570. {
  1571. m_iNumTerroristWins ++;
  1572. // Update the clients team score
  1573. UpdateTeamScores();
  1574. }
  1575. EndRoundMessage( "#Terrorists_Escaped", Terrorists_Escaped );
  1576. TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_TER );
  1577. return;
  1578. }
  1579. else if ( NumAliveTerrorist == 0 && flEscapeRatio < m_flRequiredEscapeRatio)
  1580. {
  1581. BroadcastSound( "Event.CTWin" );
  1582. m_iAccountCT += (1 - flEscapeRatio) * 3500; // CTs are rewarded based on how many terrorists have escaped...
  1583. if ( !bNeededPlayers )
  1584. {
  1585. m_iNumCTWins++;
  1586. // Update the clients team score
  1587. UpdateTeamScores();
  1588. }
  1589. EndRoundMessage( "#CTs_PreventEscape", CTs_PreventEscape );
  1590. TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT );
  1591. return;
  1592. }
  1593. else if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && m_iNumSpawnableCT > 0 )
  1594. {
  1595. BroadcastSound( "Event.CTWin" );
  1596. m_iAccountCT += (1 - flEscapeRatio) * 3250; // CTs are rewarded based on how many terrorists have escaped...
  1597. if ( !bNeededPlayers )
  1598. {
  1599. m_iNumCTWins++;
  1600. // Update the clients team score
  1601. UpdateTeamScores();
  1602. }
  1603. EndRoundMessage( "#Escaping_Terrorists_Neutralized", Escaping_Terrorists_Neutralized );
  1604. TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT );
  1605. return;
  1606. }
  1607. // else return;
  1608. }
  1609. */
  1610. return false;
  1611. }
  1612. bool CCSGameRules::VIPRoundEndCheck( bool bNeededPlayers )
  1613. {
  1614. if (m_iMapHasVIPSafetyZone != 1)
  1615. return false;
  1616. if (m_pVIP == NULL)
  1617. return false;
  1618. if (m_pVIP->m_bEscaped == true)
  1619. {
  1620. m_iAccountCT += 3500;
  1621. if ( !bNeededPlayers )
  1622. {
  1623. m_iNumCTWins ++;
  1624. // Update the clients team score
  1625. UpdateTeamScores();
  1626. }
  1627. //MIKETODO: get this working when working on VIP scenarios
  1628. /*
  1629. MessageBegin( MSG_SPEC, SVC_DIRECTOR );
  1630. WRITE_BYTE ( 9 ); // command length in bytes
  1631. WRITE_BYTE ( DRC_CMD_EVENT ); // VIP rescued
  1632. WRITE_SHORT( ENTINDEX(m_pVIP->edict()) ); // index number of primary entity
  1633. WRITE_SHORT( 0 ); // index number of secondary entity
  1634. WRITE_LONG( 15 | DRC_FLAG_FINAL); // eventflags (priority and flags)
  1635. MessageEnd();
  1636. */
  1637. // tell the bots the VIP got out
  1638. IGameEvent * event = gameeventmanager->CreateEvent( "vip_escaped" );
  1639. if ( event )
  1640. {
  1641. event->SetInt( "userid", m_pVIP->GetUserID() );
  1642. event->SetInt( "priority", 9 );
  1643. gameeventmanager->FireEvent( event );
  1644. }
  1645. //=============================================================================
  1646. // HPE_BEGIN:
  1647. // [menglish] If the VIP has escaped award him an MVP
  1648. //=============================================================================
  1649. m_pVIP->IncrementNumMVPs( CSMVP_UNDEFINED );
  1650. //=============================================================================
  1651. // HPE_END
  1652. //=============================================================================
  1653. TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Escaped );
  1654. return true;
  1655. }
  1656. else if ( m_pVIP->m_lifeState == LIFE_DEAD ) // The VIP is dead
  1657. {
  1658. m_iAccountTerrorist += 3250;
  1659. if ( !bNeededPlayers )
  1660. {
  1661. m_iNumTerroristWins ++;
  1662. // Update the clients team score
  1663. UpdateTeamScores();
  1664. }
  1665. // tell the bots the VIP was killed
  1666. IGameEvent * event = gameeventmanager->CreateEvent( "vip_killed" );
  1667. if ( event )
  1668. {
  1669. event->SetInt( "userid", m_pVIP->GetUserID() );
  1670. event->SetInt( "priority", 9 );
  1671. gameeventmanager->FireEvent( event );
  1672. }
  1673. TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Assassinated );
  1674. return true;
  1675. }
  1676. return false;
  1677. }
  1678. bool CCSGameRules::BombRoundEndCheck( bool bNeededPlayers )
  1679. {
  1680. // Check to see if the bomb target was hit or the bomb defused.. if so, then let's end the round!
  1681. if ( ( m_bTargetBombed == true ) && ( m_bMapHasBombTarget == true ) )
  1682. {
  1683. m_iAccountTerrorist += 3500;
  1684. if ( !bNeededPlayers )
  1685. {
  1686. m_iNumTerroristWins ++;
  1687. // Update the clients team score
  1688. UpdateTeamScores();
  1689. }
  1690. TerminateRound( mp_round_restart_delay.GetFloat(), Target_Bombed );
  1691. return true;
  1692. }
  1693. else
  1694. if ( ( m_bBombDefused == true ) && ( m_bMapHasBombTarget == true ) )
  1695. {
  1696. m_iAccountCT += 3250;
  1697. m_iAccountTerrorist += 800; // give the T's a little bonus for planting the bomb even though it was defused.
  1698. if ( !bNeededPlayers )
  1699. {
  1700. m_iNumCTWins++;
  1701. // Update the clients team score
  1702. UpdateTeamScores();
  1703. }
  1704. TerminateRound( mp_round_restart_delay.GetFloat(), Bomb_Defused );
  1705. return true;
  1706. }
  1707. return false;
  1708. }
  1709. bool CCSGameRules::TeamExterminationCheck(
  1710. int NumAliveTerrorist,
  1711. int NumAliveCT,
  1712. int NumDeadTerrorist,
  1713. int NumDeadCT,
  1714. bool bNeededPlayers
  1715. )
  1716. {
  1717. if ( ( m_iNumCT > 0 && m_iNumSpawnableCT > 0 ) && ( m_iNumTerrorist > 0 && m_iNumSpawnableTerrorist > 0 ) )
  1718. {
  1719. if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && m_iNumSpawnableCT > 0 )
  1720. {
  1721. bool nowin = false;
  1722. for ( int iGrenade=0; iGrenade < g_PlantedC4s.Count(); iGrenade++ )
  1723. {
  1724. CPlantedC4 *pC4 = g_PlantedC4s[iGrenade];
  1725. if ( pC4->IsBombActive() )
  1726. nowin = true;
  1727. }
  1728. if ( !nowin )
  1729. {
  1730. if ( m_bMapHasBombTarget )
  1731. m_iAccountCT += 3250;
  1732. else
  1733. m_iAccountCT += 3000;
  1734. if ( !bNeededPlayers )
  1735. {
  1736. m_iNumCTWins++;
  1737. // Update the clients team score
  1738. UpdateTeamScores();
  1739. }
  1740. TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win );
  1741. return true;
  1742. }
  1743. }
  1744. // Terrorists WON
  1745. if ( NumAliveCT == 0 && NumDeadCT != 0 && m_iNumSpawnableTerrorist > 0 )
  1746. {
  1747. if ( m_bMapHasBombTarget )
  1748. m_iAccountTerrorist += 3250;
  1749. else
  1750. m_iAccountTerrorist += 3000;
  1751. if ( !bNeededPlayers )
  1752. {
  1753. m_iNumTerroristWins++;
  1754. // Update the clients team score
  1755. UpdateTeamScores();
  1756. }
  1757. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  1758. return true;
  1759. }
  1760. }
  1761. else if ( NumAliveCT == 0 && NumAliveTerrorist == 0 )
  1762. {
  1763. TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw );
  1764. return true;
  1765. }
  1766. return false;
  1767. }
  1768. void CCSGameRules::PickNextVIP()
  1769. {
  1770. // MIKETODO: work on this when getting VIP maps running.
  1771. /*
  1772. if (IsVIPQueueEmpty() != true)
  1773. {
  1774. // Remove the current VIP from his VIP status and make him a regular CT.
  1775. if (m_pVIP != NULL)
  1776. ResetCurrentVIP();
  1777. for (int i = 0; i <= 4; i++)
  1778. {
  1779. if (VIPQueue[i] != NULL)
  1780. {
  1781. m_pVIP = VIPQueue[i];
  1782. m_pVIP->MakeVIP();
  1783. VIPQueue[i] = NULL; // remove this player from the VIP queue
  1784. StackVIPQueue(); // and re-organize the queue
  1785. m_iConsecutiveVIP = 0;
  1786. return;
  1787. }
  1788. }
  1789. }
  1790. else if (m_iConsecutiveVIP >= 3) // If it's been the same VIP for 3 rounds already.. then randomly pick a new one
  1791. {
  1792. m_iLastPick++;
  1793. if (m_iLastPick > m_iNumCT)
  1794. m_iLastPick = 1;
  1795. int iCount = 1;
  1796. CBaseEntity* pPlayer = NULL;
  1797. CBasePlayer* player = NULL;
  1798. CBasePlayer* pLastPlayer = NULL;
  1799. pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
  1800. while ( (pPlayer != NULL) && (!FNullEnt(pPlayer->edict())) )
  1801. {
  1802. if ( !(pPlayer->pev->flags & FL_DORMANT) )
  1803. {
  1804. player = GetClassPtr((CBasePlayer *)pPlayer->pev);
  1805. if ( (player->m_iTeam == CT) && (iCount == m_iLastPick) )
  1806. {
  1807. if ( (player == m_pVIP) && (pLastPlayer != NULL) )
  1808. player = pLastPlayer;
  1809. // Remove the current VIP from his VIP status and make him a regular CT.
  1810. if (m_pVIP != NULL)
  1811. ResetCurrentVIP();
  1812. player->MakeVIP();
  1813. m_iConsecutiveVIP = 0;
  1814. return;
  1815. }
  1816. else if ( player->m_iTeam == CT )
  1817. iCount++;
  1818. if ( player->m_iTeam != SPECTATOR )
  1819. pLastPlayer = player;
  1820. }
  1821. pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
  1822. }
  1823. }
  1824. else if (m_pVIP == NULL) // There is no VIP and there is no one waiting to be the VIP.. therefore just pick the first CT player we can find.
  1825. {
  1826. CBaseEntity* pPlayer = NULL;
  1827. CBasePlayer* player = NULL;
  1828. pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
  1829. while ( (pPlayer != NULL) && (!FNullEnt(pPlayer->edict())) )
  1830. {
  1831. if ( pPlayer->pev->flags != FL_DORMANT )
  1832. {
  1833. player = GetClassPtr((CBasePlayer *)pPlayer->pev);
  1834. if ( player->m_iTeam == CT )
  1835. {
  1836. player->MakeVIP();
  1837. m_iConsecutiveVIP = 0;
  1838. return;
  1839. }
  1840. }
  1841. pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
  1842. }
  1843. }
  1844. */
  1845. }
  1846. void CCSGameRules::ReadMultiplayCvars()
  1847. {
  1848. m_iRoundTime = (int)(mp_roundtime.GetFloat() * 60);
  1849. m_iFreezeTime = mp_freezetime.GetInt();
  1850. }
  1851. void CCSGameRules::RestartRound()
  1852. {
  1853. #if defined( REPLAY_ENABLED )
  1854. if ( g_pReplay )
  1855. {
  1856. // Write replay and stop recording if appropriate
  1857. if ( g_pReplay->IsRecording() )
  1858. {
  1859. g_pReplay->SV_EndRecordingSession();
  1860. }
  1861. int nActivePlayerCount = m_iNumTerrorist + m_iNumCT;
  1862. if ( nActivePlayerCount && g_pReplay->SV_ShouldBeginRecording( false ) )
  1863. {
  1864. // Tell the replay manager that it should begin recording the new round as soon as possible
  1865. g_pReplay->SV_GetContext()->GetSessionRecorder()->StartRecording();
  1866. }
  1867. }
  1868. #endif
  1869. //=============================================================================
  1870. // HPE_BEGIN:
  1871. // [tj] Notify players that the round is about to be reset
  1872. //=============================================================================
  1873. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
  1874. {
  1875. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( clientIndex );
  1876. if(pPlayer)
  1877. {
  1878. pPlayer->OnPreResetRound();
  1879. }
  1880. }
  1881. //=============================================================================
  1882. // HPE_END
  1883. //=============================================================================
  1884. if ( !IsFinite( gpGlobals->curtime ) )
  1885. {
  1886. Warning( "NaN curtime in RestartRound\n" );
  1887. gpGlobals->curtime = 0.0f;
  1888. }
  1889. int i;
  1890. m_iTotalRoundsPlayed++;
  1891. //ClearBodyQue();
  1892. // Hardlock the player accelaration to 5.0
  1893. //CVAR_SET_FLOAT( "sv_accelerate", 5.0 );
  1894. //CVAR_SET_FLOAT( "sv_friction", 4.0 );
  1895. //CVAR_SET_FLOAT( "sv_stopspeed", 75 );
  1896. sv_stopspeed.SetValue( 75.0f );
  1897. // Tabulate the number of players on each team.
  1898. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  1899. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  1900. m_bBombDropped = false;
  1901. m_bBombPlanted = false;
  1902. if ( GetHumanTeam() != TEAM_UNASSIGNED )
  1903. {
  1904. MoveHumansToHumanTeam();
  1905. }
  1906. /*************** AUTO-BALANCE CODE *************/
  1907. if ( mp_autoteambalance.GetInt() != 0 &&
  1908. (m_iUnBalancedRounds >= 1) )
  1909. {
  1910. if ( GetHumanTeam() == TEAM_UNASSIGNED )
  1911. {
  1912. BalanceTeams();
  1913. }
  1914. }
  1915. if ( ((m_iNumSpawnableCT - m_iNumSpawnableTerrorist) >= 2) ||
  1916. ((m_iNumSpawnableTerrorist - m_iNumSpawnableCT) >= 2) )
  1917. {
  1918. m_iUnBalancedRounds++;
  1919. }
  1920. else
  1921. {
  1922. m_iUnBalancedRounds = 0;
  1923. }
  1924. // Warn the players of an impending auto-balance next round...
  1925. if ( mp_autoteambalance.GetInt() != 0 &&
  1926. (m_iUnBalancedRounds == 1) )
  1927. {
  1928. if ( GetHumanTeam() == TEAM_UNASSIGNED )
  1929. {
  1930. UTIL_ClientPrintAll( HUD_PRINTCENTER,"#Auto_Team_Balance_Next_Round");
  1931. }
  1932. }
  1933. /*************** AUTO-BALANCE CODE *************/
  1934. if ( m_bCompleteReset )
  1935. {
  1936. // bounds check
  1937. if ( mp_timelimit.GetInt() < 0 )
  1938. {
  1939. mp_timelimit.SetValue( 0 );
  1940. }
  1941. m_flGameStartTime = gpGlobals->curtime;
  1942. if ( !IsFinite( m_flGameStartTime.Get() ) )
  1943. {
  1944. Warning( "Trying to set a NaN game start time\n" );
  1945. m_flGameStartTime.GetForModify() = 0.0f;
  1946. }
  1947. // Reset total # of rounds played
  1948. m_iTotalRoundsPlayed = 0;
  1949. // Reset score info
  1950. m_iNumTerroristWins = 0;
  1951. m_iNumCTWins = 0;
  1952. m_iNumConsecutiveTerroristLoses = 0;
  1953. m_iNumConsecutiveCTLoses = 0;
  1954. // Reset team scores
  1955. UpdateTeamScores();
  1956. // Reset the player stats
  1957. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  1958. {
  1959. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  1960. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  1961. pPlayer->Reset();
  1962. }
  1963. }
  1964. m_bFreezePeriod = true;
  1965. ReadMultiplayCvars();
  1966. // Check to see if there's a mapping info paramater entity
  1967. if ( g_pMapInfo )
  1968. {
  1969. switch ( g_pMapInfo->m_iBuyingStatus )
  1970. {
  1971. case 0:
  1972. m_bCTCantBuy = false;
  1973. m_bTCantBuy = false;
  1974. Msg( "EVERYONE CAN BUY!\n" );
  1975. break;
  1976. case 1:
  1977. m_bCTCantBuy = false;
  1978. m_bTCantBuy = true;
  1979. Msg( "Only CT's can buy!!\n" );
  1980. break;
  1981. case 2:
  1982. m_bCTCantBuy = true;
  1983. m_bTCantBuy = false;
  1984. Msg( "Only T's can buy!!\n" );
  1985. break;
  1986. case 3:
  1987. m_bCTCantBuy = true;
  1988. m_bTCantBuy = true;
  1989. Msg( "No one can buy!!\n" );
  1990. break;
  1991. default:
  1992. m_bCTCantBuy = false;
  1993. m_bTCantBuy = false;
  1994. break;
  1995. }
  1996. }
  1997. else
  1998. {
  1999. // by default everyone can buy
  2000. m_bCTCantBuy = false;
  2001. m_bTCantBuy = false;
  2002. }
  2003. // Check to see if this map has a bomb target in it
  2004. if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) )
  2005. {
  2006. m_bMapHasBombTarget = true;
  2007. m_bMapHasBombZone = true;
  2008. }
  2009. else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) )
  2010. {
  2011. m_bMapHasBombTarget = true;
  2012. m_bMapHasBombZone = false;
  2013. }
  2014. else
  2015. {
  2016. m_bMapHasBombTarget = false;
  2017. m_bMapHasBombZone = false;
  2018. }
  2019. // Check to see if this map has hostage rescue zones
  2020. if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) )
  2021. m_bMapHasRescueZone = true;
  2022. else
  2023. m_bMapHasRescueZone = false;
  2024. // See if the map has func_buyzone entities
  2025. // Used by CBasePlayer::HandleSignals() to support maps without these entities
  2026. if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) )
  2027. m_bMapHasBuyZone = true;
  2028. else
  2029. m_bMapHasBuyZone = false;
  2030. // GOOSEMAN : See if this map has func_escapezone entities
  2031. if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) )
  2032. {
  2033. m_bMapHasEscapeZone = true;
  2034. m_iHaveEscaped = 0;
  2035. m_iNumEscapers = 0; // Will increase this later when we count how many Ts are starting
  2036. if (m_iNumEscapeRounds >= 3)
  2037. {
  2038. SwapAllPlayers();
  2039. m_iNumEscapeRounds = 0;
  2040. }
  2041. m_iNumEscapeRounds++; // Increment the number of rounds played... After 8 rounds, the players will do a whole sale switch..
  2042. }
  2043. else
  2044. m_bMapHasEscapeZone = false;
  2045. // Check to see if this map has VIP safety zones
  2046. if ( gEntList.FindEntityByClassname( NULL, "func_vip_safetyzone" ) )
  2047. {
  2048. PickNextVIP();
  2049. m_iConsecutiveVIP++;
  2050. m_iMapHasVIPSafetyZone = 1;
  2051. }
  2052. else
  2053. m_iMapHasVIPSafetyZone = 2;
  2054. // Update accounts based on number of hostages remaining..
  2055. int iRescuedHostageBonus = 0;
  2056. for ( int iHostage=0; iHostage < g_Hostages.Count(); iHostage++ )
  2057. {
  2058. CHostage *pHostage = g_Hostages[iHostage];
  2059. if( pHostage->IsRescuable() ) //Alive and not rescued
  2060. {
  2061. iRescuedHostageBonus += 150;
  2062. }
  2063. if ( iRescuedHostageBonus >= 2000 )
  2064. break;
  2065. }
  2066. //*******Catch up code by SupraFiend. Scale up the loser bonus when teams fall into losing streaks
  2067. if (m_iRoundWinStatus == WINNER_TER) // terrorists won
  2068. {
  2069. //check to see if they just broke a losing streak
  2070. if(m_iNumConsecutiveTerroristLoses > 1)
  2071. m_iLoserBonus = 1500;//this is the default losing bonus
  2072. m_iNumConsecutiveTerroristLoses = 0;//starting fresh
  2073. m_iNumConsecutiveCTLoses++;//increment the number of wins the CTs have had
  2074. }
  2075. else if (m_iRoundWinStatus == WINNER_CT) // CT Won
  2076. {
  2077. //check to see if they just broke a losing streak
  2078. if(m_iNumConsecutiveCTLoses > 1)
  2079. m_iLoserBonus = 1500;//this is the default losing bonus
  2080. m_iNumConsecutiveCTLoses = 0;//starting fresh
  2081. m_iNumConsecutiveTerroristLoses++;//increment the number of wins the Terrorists have had
  2082. }
  2083. //check if the losing team is in a losing streak & that the loser bonus hasen't maxed out.
  2084. if((m_iNumConsecutiveTerroristLoses > 1) && (m_iLoserBonus < 3000))
  2085. m_iLoserBonus += 500;//help out the team in the losing streak
  2086. else
  2087. if((m_iNumConsecutiveCTLoses > 1) && (m_iLoserBonus < 3000))
  2088. m_iLoserBonus += 500;//help out the team in the losing streak
  2089. // assign the wining and losing bonuses
  2090. if (m_iRoundWinStatus == WINNER_TER) // terrorists won
  2091. {
  2092. m_iAccountTerrorist += iRescuedHostageBonus;
  2093. m_iAccountCT += m_iLoserBonus;
  2094. }
  2095. else if (m_iRoundWinStatus == WINNER_CT) // CT Won
  2096. {
  2097. m_iAccountCT += iRescuedHostageBonus;
  2098. if (m_bMapHasEscapeZone == false) // only give them the bonus if this isn't an escape map
  2099. m_iAccountTerrorist += m_iLoserBonus;
  2100. }
  2101. //Update CT account based on number of hostages rescued
  2102. m_iAccountCT += m_iHostagesRescued * 750;
  2103. // Update individual players accounts and respawn players
  2104. //**********new code by SupraFiend
  2105. //##########code changed by MartinO
  2106. //the round time stamp must be set before players are spawned
  2107. m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime;
  2108. if ( !IsFinite( m_fRoundStartTime.Get() ) )
  2109. {
  2110. Warning( "Trying to set a NaN round start time\n" );
  2111. m_fRoundStartTime.GetForModify() = 0.0f;
  2112. }
  2113. //Adrian - No cash for anyone at first rounds! ( well, only the default. )
  2114. if ( m_bCompleteReset )
  2115. {
  2116. m_iAccountTerrorist = m_iAccountCT = 0; //No extra cash!.
  2117. //We are starting fresh. So it's like no one has ever won or lost.
  2118. m_iNumTerroristWins = 0;
  2119. m_iNumCTWins = 0;
  2120. m_iNumConsecutiveTerroristLoses = 0;
  2121. m_iNumConsecutiveCTLoses = 0;
  2122. m_iLoserBonus = 1400;
  2123. }
  2124. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  2125. {
  2126. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  2127. if ( !pPlayer )
  2128. continue;
  2129. pPlayer->m_iNumSpawns = 0;
  2130. pPlayer->m_bTeamChanged = false;
  2131. if ( pPlayer->GetTeamNumber() == TEAM_CT )
  2132. {
  2133. if (pPlayer->DoesPlayerGetRoundStartMoney())
  2134. {
  2135. pPlayer->AddAccount( m_iAccountCT );
  2136. }
  2137. }
  2138. else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  2139. {
  2140. m_iNumEscapers++; // Add another potential escaper to the mix!
  2141. if (pPlayer->DoesPlayerGetRoundStartMoney())
  2142. {
  2143. pPlayer->AddAccount( m_iAccountTerrorist );
  2144. }
  2145. }
  2146. // tricky, make players non solid while moving to their spawn points
  2147. if ( (pPlayer->GetTeamNumber() == TEAM_CT) || (pPlayer->GetTeamNumber() == TEAM_TERRORIST) )
  2148. {
  2149. pPlayer->AddSolidFlags( FSOLID_NOT_SOLID );
  2150. }
  2151. }
  2152. //=============================================================================
  2153. // HPE_BEGIN:
  2154. // [tj] Keep track of number of players per side and if they have the same uniform
  2155. //=============================================================================
  2156. int terroristUniform = -1;
  2157. bool allTerroristsWearingSameUniform = true;
  2158. int numberOfTerrorists = 0;
  2159. int ctUniform = -1;
  2160. bool allCtsWearingSameUniform = true;
  2161. int numberOfCts = 0;
  2162. //=============================================================================
  2163. // HPE_END
  2164. //=============================================================================
  2165. // know respawn all players
  2166. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  2167. {
  2168. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  2169. if ( !pPlayer )
  2170. continue;
  2171. if ( pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->PlayerClass() >= FIRST_CT_CLASS && pPlayer->PlayerClass() <= LAST_CT_CLASS )
  2172. {
  2173. //=============================================================================
  2174. // HPE_BEGIN:
  2175. // [tj] Increment CT count and check CT uniforms.
  2176. //=============================================================================
  2177. numberOfCts++;
  2178. if (ctUniform == -1)
  2179. {
  2180. ctUniform = pPlayer->PlayerClass();
  2181. }
  2182. else if (pPlayer->PlayerClass() != ctUniform)
  2183. {
  2184. allCtsWearingSameUniform = false;
  2185. }
  2186. //=============================================================================
  2187. // HPE_END
  2188. //=============================================================================
  2189. pPlayer->RoundRespawn();
  2190. }
  2191. if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->PlayerClass() >= FIRST_T_CLASS && pPlayer->PlayerClass() <= LAST_T_CLASS )
  2192. {
  2193. //=============================================================================
  2194. // HPE_BEGIN:
  2195. // [tj] Increment terrorist count and check terrorist uniforms
  2196. //=============================================================================
  2197. numberOfTerrorists++;
  2198. if (terroristUniform == -1)
  2199. {
  2200. terroristUniform = pPlayer->PlayerClass();
  2201. }
  2202. else if (pPlayer->PlayerClass() != terroristUniform)
  2203. {
  2204. allTerroristsWearingSameUniform = false;
  2205. }
  2206. //=============================================================================
  2207. // HPE_END
  2208. //=============================================================================
  2209. pPlayer->RoundRespawn();
  2210. }
  2211. else
  2212. {
  2213. pPlayer->ObserverRoundRespawn();
  2214. }
  2215. if ( pPlayer->m_iAccount > pPlayer->m_iShouldHaveCash )
  2216. {
  2217. m_bDontUploadStats = true;
  2218. }
  2219. }
  2220. //=============================================================================
  2221. // HPE_BEGIN:
  2222. //=============================================================================
  2223. // [tj] Award same uniform achievement for qualifying teams
  2224. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  2225. {
  2226. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  2227. if ( !pPlayer )
  2228. continue;
  2229. if ( pPlayer->GetTeamNumber() == TEAM_CT && allCtsWearingSameUniform && numberOfCts >= AchievementConsts::SameUniform_MinPlayers)
  2230. {
  2231. pPlayer->AwardAchievement(CSSameUniform);
  2232. }
  2233. if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && allTerroristsWearingSameUniform && numberOfTerrorists >= AchievementConsts::SameUniform_MinPlayers)
  2234. {
  2235. pPlayer->AwardAchievement(CSSameUniform);
  2236. }
  2237. }
  2238. // [menglish] reset per-round achievement variables for each player
  2239. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  2240. {
  2241. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  2242. if( pPlayer )
  2243. {
  2244. pPlayer->ResetRoundBasedAchievementVariables();
  2245. }
  2246. }
  2247. // [pfreese] Reset all round or match stats, depending on type of restart
  2248. if ( m_bCompleteReset )
  2249. {
  2250. CCS_GameStats.ResetAllStats();
  2251. CCS_GameStats.ResetPlayerClassMatchStats();
  2252. }
  2253. else
  2254. {
  2255. CCS_GameStats.ResetRoundStats();
  2256. }
  2257. //=============================================================================
  2258. // HPE_END
  2259. //=============================================================================
  2260. // Respawn entities (glass, doors, etc..)
  2261. CleanUpMap();
  2262. // now run a tkpunish check, after the map has been cleaned up
  2263. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  2264. {
  2265. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  2266. if ( !pPlayer )
  2267. continue;
  2268. if ( pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->PlayerClass() >= FIRST_CT_CLASS && pPlayer->PlayerClass() <= LAST_CT_CLASS )
  2269. {
  2270. pPlayer->CheckTKPunishment();
  2271. }
  2272. if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->PlayerClass() >= FIRST_T_CLASS && pPlayer->PlayerClass() <= LAST_T_CLASS )
  2273. {
  2274. pPlayer->CheckTKPunishment();
  2275. }
  2276. }
  2277. // Give C4 to the terrorists
  2278. if (m_bMapHasBombTarget == true )
  2279. GiveC4();
  2280. // Reset game variables
  2281. m_flIntermissionEndTime = 0;
  2282. m_flRestartRoundTime = 0.0;
  2283. m_iAccountTerrorist = m_iAccountCT = 0;
  2284. m_iHostagesRescued = 0;
  2285. m_iHostagesTouched = 0;
  2286. //=============================================================================
  2287. // HPE_BEGIN
  2288. // [dwenger] Reset rescue-related achievement values
  2289. //=============================================================================
  2290. // [tj] reset flawless and lossless round related flags
  2291. m_bNoTerroristsKilled = true;
  2292. m_bNoCTsKilled = true;
  2293. m_bNoTerroristsDamaged = true;
  2294. m_bNoCTsDamaged = true;
  2295. m_pFirstKill = NULL;
  2296. m_pFirstBlood = NULL;
  2297. m_bCanDonateWeapons = true;
  2298. // [dwenger] Reset rescue-related achievement values
  2299. m_iHostagesRemaining = 0;
  2300. m_pLastRescuer = NULL;
  2301. m_hostageWasInjured = false;
  2302. m_hostageWasKilled = false;
  2303. //=============================================================================
  2304. // HPE_END
  2305. //=============================================================================
  2306. m_iNumRescuers = 0;
  2307. m_iRoundWinStatus = WINNER_NONE;
  2308. m_bTargetBombed = m_bBombDefused = false;
  2309. m_bCompleteReset = false;
  2310. m_flNextHostageAnnouncement = gpGlobals->curtime;
  2311. m_iHostagesRemaining = g_Hostages.Count();
  2312. // fire global game event
  2313. IGameEvent * event = gameeventmanager->CreateEvent( "round_start" );
  2314. if ( event )
  2315. {
  2316. event->SetInt("timelimit", m_iRoundTime );
  2317. event->SetInt("fraglimit", 0 );
  2318. event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
  2319. if ( m_bMapHasRescueZone )
  2320. {
  2321. event->SetString("objective","HOSTAGE RESCUE");
  2322. }
  2323. else if ( m_bMapHasEscapeZone )
  2324. {
  2325. event->SetString("objective","PRISON ESCAPE");
  2326. }
  2327. else if ( m_iMapHasVIPSafetyZone == 1 )
  2328. {
  2329. event->SetString("objective","VIP RESCUE");
  2330. }
  2331. else if ( m_bMapHasBombTarget || m_bMapHasBombZone )
  2332. {
  2333. event->SetString("objective","BOMB TARGET");
  2334. }
  2335. else
  2336. {
  2337. event->SetString("objective","DEATHMATCH");
  2338. }
  2339. gameeventmanager->FireEvent( event );
  2340. }
  2341. UploadGameStats();
  2342. //=============================================================================
  2343. // HPE_BEGIN:
  2344. // [pfreese] I commented out this call to CreateWeaponManager, as the
  2345. // CGameWeaponManager object doesn't appear to be actually used by the CSS
  2346. // code, and in any case, the weapon manager does not support wildcards in
  2347. // entity names (as seemingly indicated) below. When the manager fails to
  2348. // create its factory, it removes itself in any case.
  2349. //=============================================================================
  2350. // CreateWeaponManager( "weapon_*", gpGlobals->maxClients * 2 );
  2351. //=============================================================================
  2352. // HPE_END
  2353. //=============================================================================
  2354. }
  2355. void CCSGameRules::GiveC4()
  2356. {
  2357. enum {
  2358. ALL_TERRORISTS = 0,
  2359. HUMAN_TERRORISTS,
  2360. };
  2361. int iTerrorists[2][ABSOLUTE_PLAYER_LIMIT];
  2362. int numAliveTs[2] = { 0, 0 };
  2363. int lastBombGuyIndex[2] = { -1, -1 };
  2364. //Create an array of the indeces of bomb carrier candidates
  2365. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2366. {
  2367. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  2368. if( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == TEAM_TERRORIST && numAliveTs[ALL_TERRORISTS] < ABSOLUTE_PLAYER_LIMIT )
  2369. {
  2370. if ( pPlayer == m_pLastBombGuy )
  2371. {
  2372. lastBombGuyIndex[ALL_TERRORISTS] = numAliveTs[ALL_TERRORISTS];
  2373. lastBombGuyIndex[HUMAN_TERRORISTS] = numAliveTs[HUMAN_TERRORISTS];
  2374. }
  2375. iTerrorists[ALL_TERRORISTS][numAliveTs[ALL_TERRORISTS]] = i;
  2376. numAliveTs[ALL_TERRORISTS]++;
  2377. if ( !pPlayer->IsBot() )
  2378. {
  2379. iTerrorists[HUMAN_TERRORISTS][numAliveTs[HUMAN_TERRORISTS]] = i;
  2380. numAliveTs[HUMAN_TERRORISTS]++;
  2381. }
  2382. }
  2383. }
  2384. int which = cv_bot_defer_to_human.GetBool();
  2385. if ( numAliveTs[HUMAN_TERRORISTS] == 0 )
  2386. {
  2387. which = ALL_TERRORISTS;
  2388. }
  2389. //pick one of the candidates randomly
  2390. if( numAliveTs[which] > 0 )
  2391. {
  2392. int index = random->RandomInt(0,numAliveTs[which]-1);
  2393. if ( lastBombGuyIndex[which] >= 0 )
  2394. {
  2395. // give the C4 sequentially
  2396. index = (lastBombGuyIndex[which] + 1) % numAliveTs[which];
  2397. }
  2398. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iTerrorists[which][index] ) );
  2399. Assert( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->IsAlive() );
  2400. pPlayer->GiveNamedItem( WEAPON_C4_CLASSNAME );
  2401. m_pLastBombGuy = pPlayer;
  2402. //pPlayer->SetBombIcon();
  2403. //pPlayer->pev->body = 1;
  2404. pPlayer->m_iDisplayHistoryBits |= DHF_BOMB_RETRIEVED;
  2405. pPlayer->HintMessage( "#Hint_you_have_the_bomb", false, true );
  2406. // Log this information
  2407. //UTIL_LogPrintf("\"%s<%i><%s><TERRORIST>\" triggered \"Spawned_With_The_Bomb\"\n",
  2408. // STRING( pPlayer->GetPlayerName() ),
  2409. // GETPLAYERUSERID( pPlayer->edict() ),
  2410. // GETPLAYERAUTHID( pPlayer->edict() ) );
  2411. }
  2412. m_bBombDropped = false;
  2413. }
  2414. void CCSGameRules::Think()
  2415. {
  2416. CGameRules::Think();
  2417. for ( int i = 0; i < GetNumberOfTeams(); i++ )
  2418. {
  2419. GetGlobalTeam( i )->Think();
  2420. }
  2421. ///// Check game rules /////
  2422. if ( CheckGameOver() )
  2423. {
  2424. return;
  2425. }
  2426. // have we hit the max rounds?
  2427. if ( CheckMaxRounds() )
  2428. {
  2429. return;
  2430. }
  2431. // did somebaody hit the fraglimit ?
  2432. if ( CheckFragLimit() )
  2433. {
  2434. return;
  2435. }
  2436. if ( CheckWinLimit() )
  2437. {
  2438. return;
  2439. }
  2440. // Check for the end of the round.
  2441. if ( IsFreezePeriod() )
  2442. {
  2443. CheckFreezePeriodExpired();
  2444. }
  2445. else
  2446. {
  2447. CheckRoundTimeExpired();
  2448. }
  2449. CheckLevelInitialized();
  2450. if ( m_flRestartRoundTime > 0.0f && m_flRestartRoundTime <= gpGlobals->curtime )
  2451. {
  2452. bool botSpeaking = false;
  2453. for ( int i=1; i <= gpGlobals->maxClients; ++i )
  2454. {
  2455. CBasePlayer *player = UTIL_PlayerByIndex( i );
  2456. if (player == NULL)
  2457. continue;
  2458. if (!player->IsBot())
  2459. continue;
  2460. CCSBot *bot = dynamic_cast< CCSBot * >(player);
  2461. if ( !bot )
  2462. continue;
  2463. if ( bot->IsUsingVoice() )
  2464. {
  2465. if ( gpGlobals->curtime > m_flRestartRoundTime + 10.0f )
  2466. {
  2467. Msg( "Ignoring speaking bot %s at round end\n", bot->GetPlayerName() );
  2468. }
  2469. else
  2470. {
  2471. botSpeaking = true;
  2472. break;
  2473. }
  2474. }
  2475. }
  2476. if ( !botSpeaking )
  2477. {
  2478. RestartRound();
  2479. }
  2480. }
  2481. if ( gpGlobals->curtime > m_tmNextPeriodicThink )
  2482. {
  2483. CheckRestartRound();
  2484. m_tmNextPeriodicThink = gpGlobals->curtime + 1.0;
  2485. }
  2486. }
  2487. // The bots do their processing after physics simulation etc so their visibility checks don't recompute
  2488. // bone positions multiple times a frame.
  2489. void CCSGameRules::EndGameFrame( void )
  2490. {
  2491. TheBots->StartFrame();
  2492. BaseClass::EndGameFrame();
  2493. }
  2494. bool CCSGameRules::CheckGameOver()
  2495. {
  2496. if ( g_fGameOver ) // someone else quit the game already
  2497. {
  2498. //=============================================================================
  2499. // HPE_BEGIN:
  2500. // [Forrest] Calling ChangeLevel multiple times was causing IncrementMapCycleIndex
  2501. // to skip over maps in the list. Avoid this using a technique from CTeamplayRoundBasedRules::Think.
  2502. //=============================================================================
  2503. // check to see if we should change levels now
  2504. if ( m_flIntermissionEndTime && ( m_flIntermissionEndTime < gpGlobals->curtime ) )
  2505. {
  2506. ChangeLevel(); // intermission is over
  2507. // Don't run this code again
  2508. m_flIntermissionEndTime = 0.f;
  2509. }
  2510. //=============================================================================
  2511. // HPE_END
  2512. //=============================================================================
  2513. return true;
  2514. }
  2515. return false;
  2516. }
  2517. bool CCSGameRules::CheckFragLimit()
  2518. {
  2519. if ( fraglimit.GetInt() <= 0 )
  2520. return false;
  2521. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2522. {
  2523. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  2524. if ( pPlayer && pPlayer->FragCount() >= fraglimit.GetInt() )
  2525. {
  2526. const char *teamName = "UNKNOWN";
  2527. if ( pPlayer->GetTeam() )
  2528. {
  2529. teamName = pPlayer->GetTeam()->GetName();
  2530. }
  2531. UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"Intermission_Kill_Limit\"\n",
  2532. pPlayer->GetPlayerName(),
  2533. pPlayer->GetUserID(),
  2534. pPlayer->GetNetworkIDString(),
  2535. teamName
  2536. );
  2537. GoToIntermission();
  2538. return true;
  2539. }
  2540. }
  2541. return false;
  2542. }
  2543. bool CCSGameRules::CheckMaxRounds()
  2544. {
  2545. if ( mp_maxrounds.GetInt() != 0 )
  2546. {
  2547. if ( m_iTotalRoundsPlayed >= mp_maxrounds.GetInt() )
  2548. {
  2549. UTIL_LogPrintf("World triggered \"Intermission_Round_Limit\"\n");
  2550. GoToIntermission();
  2551. return true;
  2552. }
  2553. }
  2554. return false;
  2555. }
  2556. bool CCSGameRules::CheckWinLimit()
  2557. {
  2558. // has one team won the specified number of rounds?
  2559. if ( mp_winlimit.GetInt() != 0 )
  2560. {
  2561. if ( m_iNumCTWins >= mp_winlimit.GetInt() )
  2562. {
  2563. UTIL_LogPrintf("Team \"CT\" triggered \"Intermission_Win_Limit\"\n");
  2564. GoToIntermission();
  2565. return true;
  2566. }
  2567. if ( m_iNumTerroristWins >= mp_winlimit.GetInt() )
  2568. {
  2569. UTIL_LogPrintf("Team \"TERRORIST\" triggered \"Intermission_Win_Limit\"\n");
  2570. GoToIntermission();
  2571. return true;
  2572. }
  2573. }
  2574. return false;
  2575. }
  2576. void CCSGameRules::CheckFreezePeriodExpired()
  2577. {
  2578. float startTime = m_fRoundStartTime;
  2579. if ( !IsFinite( startTime ) )
  2580. {
  2581. Warning( "Infinite round start time!\n" );
  2582. m_fRoundStartTime.GetForModify() = gpGlobals->curtime;
  2583. }
  2584. if ( IsFinite( startTime ) && gpGlobals->curtime < startTime )
  2585. {
  2586. return; // not time yet to start round
  2587. }
  2588. // Log this information
  2589. UTIL_LogPrintf("World triggered \"Round_Start\"\n");
  2590. char CT_sentence[40];
  2591. char T_sentence[40];
  2592. switch ( random->RandomInt( 0, 3 ) )
  2593. {
  2594. case 0:
  2595. Q_strncpy(CT_sentence,"radio.moveout", sizeof( CT_sentence ) );
  2596. Q_strncpy(T_sentence ,"radio.moveout", sizeof( T_sentence ) );
  2597. break;
  2598. case 1:
  2599. Q_strncpy(CT_sentence, "radio.letsgo", sizeof( CT_sentence ) );
  2600. Q_strncpy(T_sentence , "radio.letsgo", sizeof( T_sentence ) );
  2601. break;
  2602. case 2:
  2603. Q_strncpy(CT_sentence , "radio.locknload", sizeof( CT_sentence ) );
  2604. Q_strncpy(T_sentence , "radio.locknload", sizeof( T_sentence ) );
  2605. break;
  2606. default:
  2607. Q_strncpy(CT_sentence , "radio.go", sizeof( CT_sentence ) );
  2608. Q_strncpy(T_sentence , "radio.go", sizeof( T_sentence ) );
  2609. break;
  2610. }
  2611. // More specific radio commands for the new scenarios : Prison & Assasination
  2612. if (m_bMapHasEscapeZone == TRUE)
  2613. {
  2614. Q_strncpy(CT_sentence , "radio.elim", sizeof( CT_sentence ) );
  2615. Q_strncpy(T_sentence , "radio.getout", sizeof( T_sentence ) );
  2616. }
  2617. else if (m_iMapHasVIPSafetyZone == 1)
  2618. {
  2619. Q_strncpy(CT_sentence , "radio.vip", sizeof( CT_sentence ) );
  2620. Q_strncpy(T_sentence , "radio.locknload", sizeof( T_sentence ) );
  2621. }
  2622. // Freeze period expired: kill the flag
  2623. m_bFreezePeriod = false;
  2624. IGameEvent * event = gameeventmanager->CreateEvent( "round_freeze_end" );
  2625. if ( event )
  2626. {
  2627. gameeventmanager->FireEvent( event );
  2628. }
  2629. // Update the timers for all clients and play a sound
  2630. bool bCTPlayed = false;
  2631. bool bTPlayed = false;
  2632. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2633. {
  2634. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  2635. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  2636. {
  2637. if ( pPlayer->State_Get() == STATE_ACTIVE )
  2638. {
  2639. if ( (pPlayer->GetTeamNumber() == TEAM_CT) && !bCTPlayed )
  2640. {
  2641. pPlayer->Radio( CT_sentence );
  2642. bCTPlayed = true;
  2643. }
  2644. else if ( (pPlayer->GetTeamNumber() == TEAM_TERRORIST) && !bTPlayed )
  2645. {
  2646. pPlayer->Radio( T_sentence );
  2647. bTPlayed = true;
  2648. }
  2649. }
  2650. //pPlayer->SyncRoundTimer();
  2651. }
  2652. }
  2653. }
  2654. void CCSGameRules::CheckRoundTimeExpired()
  2655. {
  2656. if ( mp_ignore_round_win_conditions.GetBool() )
  2657. return;
  2658. if ( GetRoundRemainingTime() > 0 || m_iRoundWinStatus != WINNER_NONE )
  2659. return; //We haven't completed other objectives, so go for this!.
  2660. if( !m_bFirstConnected )
  2661. return;
  2662. // New code to get rid of round draws!!
  2663. if ( m_bMapHasBombTarget )
  2664. {
  2665. //If the bomb is planted, don't let the round timer end the round.
  2666. //keep going until the bomb explodes or is defused
  2667. if( !m_bBombPlanted )
  2668. {
  2669. m_iAccountCT += 3250;
  2670. m_iNumCTWins++;
  2671. TerminateRound( mp_round_restart_delay.GetFloat(), Target_Saved );
  2672. UpdateTeamScores();
  2673. MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_TERRORIST);
  2674. }
  2675. }
  2676. else if ( m_bMapHasRescueZone )
  2677. {
  2678. m_iAccountTerrorist += 3250;
  2679. m_iNumTerroristWins++;
  2680. TerminateRound( mp_round_restart_delay.GetFloat(), Hostages_Not_Rescued );
  2681. UpdateTeamScores();
  2682. MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_CT);
  2683. }
  2684. else if ( m_bMapHasEscapeZone )
  2685. {
  2686. m_iNumCTWins++;
  2687. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Not_Escaped );
  2688. UpdateTeamScores();
  2689. }
  2690. else if ( m_iMapHasVIPSafetyZone == 1 )
  2691. {
  2692. m_iAccountTerrorist += 3250;
  2693. m_iNumTerroristWins++;
  2694. TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Not_Escaped );
  2695. UpdateTeamScores();
  2696. }
  2697. #if defined( REPLAY_ENABLED )
  2698. if ( g_pReplay )
  2699. {
  2700. // Write replay and stop recording if appropriate
  2701. g_pReplay->SV_EndRecordingSession();
  2702. }
  2703. #endif
  2704. }
  2705. void CCSGameRules::GoToIntermission( void )
  2706. {
  2707. Msg( "Going to intermission...\n" );
  2708. IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" );
  2709. if( winEvent )
  2710. {
  2711. for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; teamIndex++ )
  2712. {
  2713. CTeam *team = GetGlobalTeam( teamIndex );
  2714. if ( team )
  2715. {
  2716. float kills = CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_KILLS];
  2717. float deaths = CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_DEATHS];
  2718. // choose dialog variables to set depending on team
  2719. switch ( teamIndex )
  2720. {
  2721. case TEAM_TERRORIST:
  2722. winEvent->SetInt( "t_score", team->GetScore() );
  2723. if(deaths == 0)
  2724. {
  2725. winEvent->SetFloat( "t_kd", kills );
  2726. }
  2727. else
  2728. {
  2729. winEvent->SetFloat( "t_kd", kills / deaths );
  2730. }
  2731. winEvent->SetInt( "t_objectives_done", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_OBJECTIVES_COMPLETED] );
  2732. winEvent->SetInt( "t_money_earned", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_MONEY_EARNED] );
  2733. break;
  2734. case TEAM_CT:
  2735. winEvent->SetInt( "ct_score", team->GetScore() );
  2736. if(deaths == 0)
  2737. {
  2738. winEvent->SetFloat( "ct_kd", kills );
  2739. }
  2740. else
  2741. {
  2742. winEvent->SetFloat( "ct_kd", kills / deaths );
  2743. }
  2744. winEvent->SetInt( "ct_objectives_done", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_OBJECTIVES_COMPLETED] );
  2745. winEvent->SetInt( "ct_money_earned", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_MONEY_EARNED] );
  2746. break;
  2747. default:
  2748. Assert( false );
  2749. break;
  2750. }
  2751. }
  2752. }
  2753. gameeventmanager->FireEvent( winEvent );
  2754. }
  2755. BaseClass::GoToIntermission();
  2756. // set all players to FL_FROZEN
  2757. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  2758. {
  2759. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  2760. if ( pPlayer )
  2761. {
  2762. pPlayer->AddFlag( FL_FROZEN );
  2763. }
  2764. }
  2765. // freeze players while in intermission
  2766. m_bFreezePeriod = true;
  2767. }
  2768. int PlayerScoreInfoSort( const playerscore_t *p1, const playerscore_t *p2 )
  2769. {
  2770. // check frags
  2771. if ( p1->iScore > p2->iScore )
  2772. return -1;
  2773. if ( p2->iScore > p1->iScore )
  2774. return 1;
  2775. // check index
  2776. if ( p1->iPlayerIndex < p2->iPlayerIndex )
  2777. return -1;
  2778. return 1;
  2779. }
  2780. #if defined (_DEBUG)
  2781. void TestRoundWinpanel( void )
  2782. {
  2783. IGameEvent *event = gameeventmanager->CreateEvent( "round_end" );
  2784. event->SetInt( "winner", TEAM_TERRORIST );
  2785. if ( event )
  2786. {
  2787. gameeventmanager->FireEvent( event );
  2788. }
  2789. IGameEvent *event2 = gameeventmanager->CreateEvent( "player_death" );
  2790. if ( event2 )
  2791. {
  2792. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(1) );
  2793. // pCappingPlayers is a null terminated list of player indeces
  2794. event2->SetInt("userid", pPlayer->GetUserID() );
  2795. event2->SetInt("attacker", pPlayer->GetUserID() );
  2796. event2->SetString("weapon", "Bare Hands" );
  2797. event2->SetInt("headshot", 1 );
  2798. event2->SetInt( "revenge", 1 );
  2799. gameeventmanager->FireEvent( event2 );
  2800. }
  2801. IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_round" );
  2802. if ( winEvent )
  2803. {
  2804. if ( 1 )
  2805. {
  2806. if ( 0 /*team == m_iTimerWinTeam */)
  2807. {
  2808. // timer expired, defenders win
  2809. // show total time that was defended
  2810. winEvent->SetBool( "show_timer_defend", true );
  2811. winEvent->SetInt( "timer_time", 0 /*m_pRoundTimer->GetTimerMaxLength() */);
  2812. }
  2813. else
  2814. {
  2815. // attackers win
  2816. // show time it took for them to win
  2817. winEvent->SetBool( "show_timer_attack", true );
  2818. int iTimeElapsed = 90; //m_pRoundTimer->GetTimerMaxLength() - (int)m_pRoundTimer->GetTimeRemaining();
  2819. winEvent->SetInt( "timer_time", iTimeElapsed );
  2820. }
  2821. }
  2822. else
  2823. {
  2824. winEvent->SetBool( "show_timer_attack", false );
  2825. winEvent->SetBool( "show_timer_defend", false );
  2826. }
  2827. int iLastEvent = Terrorists_Win;
  2828. winEvent->SetInt( "final_event", iLastEvent );
  2829. // Set the fun fact data in the event
  2830. winEvent->SetString( "funfact_token", "#funfact_first_blood" );
  2831. winEvent->SetInt( "funfact_player", 1 );
  2832. winEvent->SetInt( "funfact_data1", 20 );
  2833. winEvent->SetInt( "funfact_data2", 31 );
  2834. winEvent->SetInt( "funfact_data3", 45 );
  2835. gameeventmanager->FireEvent( winEvent );
  2836. }
  2837. }
  2838. ConCommand test_round_winpanel( "test_round_winpanel", TestRoundWinpanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
  2839. void TestMatchWinpanel( void )
  2840. {
  2841. IGameEvent *event = gameeventmanager->CreateEvent( "round_end" );
  2842. event->SetInt( "winner", TEAM_TERRORIST );
  2843. if ( event )
  2844. {
  2845. gameeventmanager->FireEvent( event );
  2846. }
  2847. IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" );
  2848. if ( winEvent )
  2849. {
  2850. winEvent->SetInt( "t_score", 4 );
  2851. winEvent->SetInt( "ct_score", 1 );
  2852. winEvent->SetFloat( "t_kd", 1.8f );
  2853. winEvent->SetFloat( "ct_kd", 0.4f );
  2854. winEvent->SetInt( "t_objectives_done", 5 );
  2855. winEvent->SetInt( "ct_objectives_done", 2 );
  2856. winEvent->SetInt( "t_money_earned", 30000 );
  2857. winEvent->SetInt( "ct_money_earned", 19999 );
  2858. gameeventmanager->FireEvent( winEvent );
  2859. }
  2860. }
  2861. ConCommand test_match_winpanel( "test_match_winpanel", TestMatchWinpanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
  2862. void TestFreezePanel( void )
  2863. {
  2864. IGameEvent *event = gameeventmanager->CreateEvent( "freezecam_started" );
  2865. if ( event )
  2866. {
  2867. gameeventmanager->FireEvent( event );
  2868. }
  2869. IGameEvent *winEvent = gameeventmanager->CreateEvent( "show_freezepanel" );
  2870. if ( winEvent )
  2871. {
  2872. winEvent->SetInt( "killer", 1 );
  2873. gameeventmanager->FireEvent( winEvent );
  2874. }
  2875. }
  2876. ConCommand test_freezepanel( "test_freezepanel", TestFreezePanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
  2877. #endif // _DEBUG
  2878. static void PrintToConsole( CBasePlayer *player, const char *text )
  2879. {
  2880. if ( player )
  2881. {
  2882. ClientPrint( player, HUD_PRINTCONSOLE, text );
  2883. }
  2884. else
  2885. {
  2886. Msg( "%s", text );
  2887. }
  2888. }
  2889. void CCSGameRules::DumpTimers( void ) const
  2890. {
  2891. extern ConVar bot_join_delay;
  2892. CBasePlayer *player = UTIL_GetCommandClient();
  2893. CFmtStr str;
  2894. PrintToConsole( player, str.sprintf( "Timers and related info at %f:\n", gpGlobals->curtime ) );
  2895. PrintToConsole( player, str.sprintf( "m_bCompleteReset: %d\n", m_bCompleteReset ) );
  2896. PrintToConsole( player, str.sprintf( "m_iTotalRoundsPlayed: %d\n", m_iTotalRoundsPlayed ) );
  2897. PrintToConsole( player, str.sprintf( "m_iRoundTime: %d\n", m_iRoundTime.Get() ) );
  2898. PrintToConsole( player, str.sprintf( "m_iRoundWinStatus: %d\n", m_iRoundWinStatus ) );
  2899. PrintToConsole( player, str.sprintf( "first connected: %d\n", m_bFirstConnected ) );
  2900. PrintToConsole( player, str.sprintf( "intermission end time: %f\n", m_flIntermissionEndTime ) );
  2901. PrintToConsole( player, str.sprintf( "freeze period: %d\n", m_bFreezePeriod.Get() ) );
  2902. PrintToConsole( player, str.sprintf( "round restart time: %f\n", m_flRestartRoundTime ) );
  2903. PrintToConsole( player, str.sprintf( "game start time: %f\n", m_flGameStartTime.Get() ) );
  2904. PrintToConsole( player, str.sprintf( "m_fRoundStartTime: %f\n", m_fRoundStartTime.Get() ) );
  2905. PrintToConsole( player, str.sprintf( "freeze time: %d\n", m_iFreezeTime ) );
  2906. PrintToConsole( player, str.sprintf( "next think: %f\n", m_tmNextPeriodicThink ) );
  2907. PrintToConsole( player, str.sprintf( "fraglimit: %d\n", fraglimit.GetInt() ) );
  2908. PrintToConsole( player, str.sprintf( "mp_maxrounds: %d\n", mp_maxrounds.GetInt() ) );
  2909. PrintToConsole( player, str.sprintf( "mp_winlimit: %d\n", mp_winlimit.GetInt() ) );
  2910. PrintToConsole( player, str.sprintf( "bot_quota: %d\n", cv_bot_quota.GetInt() ) );
  2911. PrintToConsole( player, str.sprintf( "bot_quota_mode: %s\n", cv_bot_quota_mode.GetString() ) );
  2912. PrintToConsole( player, str.sprintf( "bot_join_after_player: %d\n", cv_bot_join_after_player.GetInt() ) );
  2913. PrintToConsole( player, str.sprintf( "bot_join_delay: %d\n", bot_join_delay.GetInt() ) );
  2914. PrintToConsole( player, str.sprintf( "nextlevel: %s\n", nextlevel.GetString() ) );
  2915. int humansInGame = UTIL_HumansInGame( true );
  2916. int botsInGame = UTIL_BotsInGame();
  2917. PrintToConsole( player, str.sprintf( "%d humans and %d bots in game\n", humansInGame, botsInGame ) );
  2918. PrintToConsole( player, str.sprintf( "num CTs (spawnable): %d (%d)\n", m_iNumCT, m_iNumSpawnableCT ) );
  2919. PrintToConsole( player, str.sprintf( "num Ts (spawnable): %d (%d)\n", m_iNumTerrorist, m_iNumSpawnableTerrorist ) );
  2920. if ( g_fGameOver )
  2921. {
  2922. PrintToConsole( player, str.sprintf( "Game is over!\n" ) );
  2923. }
  2924. PrintToConsole( player, str.sprintf( "\n" ) );
  2925. }
  2926. CON_COMMAND( mp_dump_timers, "Prints round timers to the console for debugging" )
  2927. {
  2928. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  2929. return;
  2930. if ( CSGameRules() )
  2931. {
  2932. CSGameRules()->DumpTimers();
  2933. }
  2934. }
  2935. // living players on the given team need to be marked as not receiving any money
  2936. // next round.
  2937. void CCSGameRules::MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int team)
  2938. {
  2939. int playerNum;
  2940. for (playerNum = 1; playerNum <= gpGlobals->maxClients; ++playerNum)
  2941. {
  2942. CCSPlayer *player = (CCSPlayer *)UTIL_PlayerByIndex(playerNum);
  2943. if (player == NULL)
  2944. {
  2945. continue;
  2946. }
  2947. if ((player->GetTeamNumber() == team) && (player->IsAlive()))
  2948. {
  2949. player->MarkAsNotReceivingMoneyNextRound();
  2950. }
  2951. }
  2952. }
  2953. void CCSGameRules::CheckLevelInitialized( void )
  2954. {
  2955. if ( !m_bLevelInitialized )
  2956. {
  2957. // Count the number of spawn points for each team
  2958. // This determines the maximum number of players allowed on each
  2959. CBaseEntity* ent = NULL;
  2960. m_iSpawnPointCount_Terrorist = 0;
  2961. m_iSpawnPointCount_CT = 0;
  2962. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL )
  2963. {
  2964. if ( IsSpawnPointValid( ent, NULL ) )
  2965. {
  2966. m_iSpawnPointCount_Terrorist++;
  2967. }
  2968. else
  2969. {
  2970. Warning("Invalid terrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
  2971. ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] );
  2972. }
  2973. }
  2974. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL )
  2975. {
  2976. if ( IsSpawnPointValid( ent, NULL ) )
  2977. {
  2978. m_iSpawnPointCount_CT++;
  2979. }
  2980. else
  2981. {
  2982. Warning("Invalid counterterrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
  2983. ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] );
  2984. }
  2985. }
  2986. // Is this a logo map?
  2987. if ( gEntList.FindEntityByClassname( NULL, "info_player_logo" ) )
  2988. m_bLogoMap = true;
  2989. m_bLevelInitialized = true;
  2990. }
  2991. }
  2992. void CCSGameRules::ShowSpawnPoints( void )
  2993. {
  2994. CBaseEntity* ent = NULL;
  2995. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL )
  2996. {
  2997. if ( IsSpawnPointValid( ent, NULL ) )
  2998. {
  2999. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, 600 );
  3000. }
  3001. else
  3002. {
  3003. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, 600);
  3004. }
  3005. }
  3006. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL )
  3007. {
  3008. if ( IsSpawnPointValid( ent, NULL ) )
  3009. {
  3010. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, 600 );
  3011. }
  3012. else
  3013. {
  3014. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, 600 );
  3015. }
  3016. }
  3017. }
  3018. void CCSGameRules::CheckRestartRound( void )
  3019. {
  3020. // Restart the game if specified by the server
  3021. int iRestartDelay = mp_restartgame.GetInt();
  3022. if ( iRestartDelay > 0 )
  3023. {
  3024. if ( iRestartDelay > 60 )
  3025. iRestartDelay = 60;
  3026. // log the restart
  3027. UTIL_LogPrintf( "World triggered \"Restart_Round_(%i_%s)\"\n", iRestartDelay, iRestartDelay == 1 ? "second" : "seconds" );
  3028. UTIL_LogPrintf( "Team \"CT\" scored \"%i\" with \"%i\" players\n", m_iNumCTWins, m_iNumCT );
  3029. UTIL_LogPrintf( "Team \"TERRORIST\" scored \"%i\" with \"%i\" players\n", m_iNumTerroristWins, m_iNumTerrorist );
  3030. // let the players know
  3031. char strRestartDelay[64];
  3032. Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
  3033. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
  3034. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
  3035. m_flRestartRoundTime = gpGlobals->curtime + iRestartDelay;
  3036. m_bCompleteReset = true;
  3037. mp_restartgame.SetValue( 0 );
  3038. }
  3039. }
  3040. class SetHumanTeamFunctor
  3041. {
  3042. public:
  3043. SetHumanTeamFunctor( int targetTeam )
  3044. {
  3045. m_targetTeam = targetTeam;
  3046. m_sourceTeam = ( m_targetTeam == TEAM_CT ) ? TEAM_TERRORIST : TEAM_CT;
  3047. m_traitors.MakeReliable();
  3048. m_loyalists.MakeReliable();
  3049. m_loyalists.AddAllPlayers();
  3050. }
  3051. bool operator()( CBasePlayer *basePlayer )
  3052. {
  3053. CCSPlayer *player = ToCSPlayer( basePlayer );
  3054. if ( !player )
  3055. return true;
  3056. if ( player->IsBot() )
  3057. return true;
  3058. if ( player->GetTeamNumber() != m_sourceTeam )
  3059. return true;
  3060. if ( player->State_Get() == STATE_PICKINGCLASS )
  3061. return true;
  3062. if ( CSGameRules()->TeamFull( m_targetTeam ) )
  3063. return false;
  3064. if ( CSGameRules()->TeamStacked( m_targetTeam, m_sourceTeam ) )
  3065. return false;
  3066. player->SwitchTeam( m_targetTeam );
  3067. m_traitors.AddRecipient( player );
  3068. m_loyalists.RemoveRecipient( player );
  3069. return true;
  3070. }
  3071. void SendNotice( void )
  3072. {
  3073. if ( m_traitors.GetRecipientCount() > 0 )
  3074. {
  3075. UTIL_ClientPrintFilter( m_traitors, HUD_PRINTCENTER, "#Player_Balanced" );
  3076. UTIL_ClientPrintFilter( m_loyalists, HUD_PRINTCENTER, "#Teams_Balanced" );
  3077. }
  3078. }
  3079. private:
  3080. int m_targetTeam;
  3081. int m_sourceTeam;
  3082. CRecipientFilter m_traitors;
  3083. CRecipientFilter m_loyalists;
  3084. };
  3085. void CCSGameRules::MoveHumansToHumanTeam( void )
  3086. {
  3087. int targetTeam = GetHumanTeam();
  3088. if ( targetTeam != TEAM_TERRORIST && targetTeam != TEAM_CT )
  3089. return;
  3090. SetHumanTeamFunctor setTeam( targetTeam );
  3091. ForEachPlayer( setTeam );
  3092. setTeam.SendNotice();
  3093. }
  3094. void CCSGameRules::BalanceTeams( void )
  3095. {
  3096. int iTeamToSwap = TEAM_UNASSIGNED;
  3097. int iNumToSwap;
  3098. if (m_iMapHasVIPSafetyZone == 1) // The ratio for teams is different for Assasination maps
  3099. {
  3100. int iDesiredNumCT, iDesiredNumTerrorist;
  3101. if ( (m_iNumCT + m_iNumTerrorist)%2 != 0) // uneven number of players
  3102. iDesiredNumCT = (int)((m_iNumCT + m_iNumTerrorist) * 0.55) + 1;
  3103. else
  3104. iDesiredNumCT = (int)((m_iNumCT + m_iNumTerrorist)/2);
  3105. iDesiredNumTerrorist = (m_iNumCT + m_iNumTerrorist) - iDesiredNumCT;
  3106. if ( m_iNumCT < iDesiredNumCT )
  3107. {
  3108. iTeamToSwap = TEAM_TERRORIST;
  3109. iNumToSwap = iDesiredNumCT - m_iNumCT;
  3110. }
  3111. else if ( m_iNumTerrorist < iDesiredNumTerrorist )
  3112. {
  3113. iTeamToSwap = TEAM_CT;
  3114. iNumToSwap = iDesiredNumTerrorist - m_iNumTerrorist;
  3115. }
  3116. else
  3117. return;
  3118. }
  3119. else
  3120. {
  3121. if (m_iNumCT > m_iNumTerrorist)
  3122. {
  3123. iTeamToSwap = TEAM_CT;
  3124. iNumToSwap = (m_iNumCT - m_iNumTerrorist)/2;
  3125. }
  3126. else if (m_iNumTerrorist > m_iNumCT)
  3127. {
  3128. iTeamToSwap = TEAM_TERRORIST;
  3129. iNumToSwap = (m_iNumTerrorist - m_iNumCT)/2;
  3130. }
  3131. else
  3132. {
  3133. return; // Teams are even.. Get out of here.
  3134. }
  3135. }
  3136. if (iNumToSwap > 3) // Don't swap more than 3 players at a time.. This is a naive method of avoiding infinite loops.
  3137. iNumToSwap = 3;
  3138. int iTragetTeam = TEAM_UNASSIGNED;
  3139. if ( iTeamToSwap == TEAM_CT )
  3140. {
  3141. iTragetTeam = TEAM_TERRORIST;
  3142. }
  3143. else if ( iTeamToSwap == TEAM_TERRORIST )
  3144. {
  3145. iTragetTeam = TEAM_CT;
  3146. }
  3147. else
  3148. {
  3149. // no valid team to swap
  3150. return;
  3151. }
  3152. CRecipientFilter traitors;
  3153. CRecipientFilter loyalists;
  3154. traitors.MakeReliable();
  3155. loyalists.MakeReliable();
  3156. loyalists.AddAllPlayers();
  3157. for (int i = 0; i < iNumToSwap; i++)
  3158. {
  3159. // last person to join the server
  3160. int iHighestUserID = -1;
  3161. CCSPlayer *pPlayerToSwap = NULL;
  3162. // check if target team is full, exit if so
  3163. if ( TeamFull(iTragetTeam) )
  3164. break;
  3165. // search for player with highest UserID = most recently joined to switch over
  3166. for ( int j = 1; j <= gpGlobals->maxClients; j++ )
  3167. {
  3168. CCSPlayer *pPlayer = (CCSPlayer *)UTIL_PlayerByIndex( j );
  3169. if ( !pPlayer )
  3170. continue;
  3171. CCSBot *bot = dynamic_cast< CCSBot * >(pPlayer);
  3172. if ( bot )
  3173. continue; // don't swap bots - the bot system will handle that
  3174. if ( pPlayer &&
  3175. ( m_pVIP != pPlayer ) &&
  3176. ( pPlayer->GetTeamNumber() == iTeamToSwap ) &&
  3177. ( engine->GetPlayerUserId( pPlayer->edict() ) > iHighestUserID ) &&
  3178. ( pPlayer->State_Get() != STATE_PICKINGCLASS ) )
  3179. {
  3180. iHighestUserID = engine->GetPlayerUserId( pPlayer->edict() );
  3181. pPlayerToSwap = pPlayer;
  3182. }
  3183. }
  3184. if ( pPlayerToSwap != NULL )
  3185. {
  3186. traitors.AddRecipient( pPlayerToSwap );
  3187. loyalists.RemoveRecipient( pPlayerToSwap );
  3188. pPlayerToSwap->SwitchTeam( iTragetTeam );
  3189. }
  3190. }
  3191. if ( traitors.GetRecipientCount() > 0 )
  3192. {
  3193. UTIL_ClientPrintFilter( traitors, HUD_PRINTCENTER, "#Player_Balanced" );
  3194. UTIL_ClientPrintFilter( loyalists, HUD_PRINTCENTER, "#Teams_Balanced" );
  3195. }
  3196. }
  3197. bool CCSGameRules::TeamFull( int team_id )
  3198. {
  3199. CheckLevelInitialized();
  3200. switch ( team_id )
  3201. {
  3202. case TEAM_TERRORIST:
  3203. return m_iNumTerrorist >= m_iSpawnPointCount_Terrorist;
  3204. case TEAM_CT:
  3205. return m_iNumCT >= m_iSpawnPointCount_CT;
  3206. }
  3207. return false;
  3208. }
  3209. int CCSGameRules::GetHumanTeam()
  3210. {
  3211. if ( FStrEq( "CT", mp_humanteam.GetString() ) )
  3212. {
  3213. return TEAM_CT;
  3214. }
  3215. else if ( FStrEq( "T", mp_humanteam.GetString() ) )
  3216. {
  3217. return TEAM_TERRORIST;
  3218. }
  3219. return TEAM_UNASSIGNED;
  3220. }
  3221. int CCSGameRules::SelectDefaultTeam( bool ignoreBots /*= false*/ )
  3222. {
  3223. if ( ignoreBots && ( FStrEq( cv_bot_join_team.GetString(), "T" ) || FStrEq( cv_bot_join_team.GetString(), "CT" ) ) )
  3224. {
  3225. ignoreBots = false; // don't ignore bots when they can't switch teams
  3226. }
  3227. if ( ignoreBots && !mp_autoteambalance.GetBool() )
  3228. {
  3229. ignoreBots = false; // don't ignore bots when they can't switch teams
  3230. }
  3231. int team = TEAM_UNASSIGNED;
  3232. int numTerrorists = m_iNumTerrorist;
  3233. int numCTs = m_iNumCT;
  3234. if ( ignoreBots )
  3235. {
  3236. numTerrorists = UTIL_HumansOnTeam( TEAM_TERRORIST );
  3237. numCTs = UTIL_HumansOnTeam( TEAM_CT );
  3238. }
  3239. // Choose the team that's lacking players
  3240. if ( numTerrorists < numCTs )
  3241. {
  3242. team = TEAM_TERRORIST;
  3243. }
  3244. else if ( numTerrorists > numCTs )
  3245. {
  3246. team = TEAM_CT;
  3247. }
  3248. // Choose the team that's losing
  3249. else if ( m_iNumTerroristWins < m_iNumCTWins )
  3250. {
  3251. team = TEAM_TERRORIST;
  3252. }
  3253. else if ( m_iNumCTWins < m_iNumTerroristWins )
  3254. {
  3255. team = TEAM_CT;
  3256. }
  3257. else
  3258. {
  3259. // Teams and scores are equal, pick a random team
  3260. if ( random->RandomInt( 0, 1 ) == 0 )
  3261. {
  3262. team = TEAM_CT;
  3263. }
  3264. else
  3265. {
  3266. team = TEAM_TERRORIST;
  3267. }
  3268. }
  3269. if ( TeamFull( team ) )
  3270. {
  3271. // Pick the opposite team
  3272. if ( team == TEAM_TERRORIST )
  3273. {
  3274. team = TEAM_CT;
  3275. }
  3276. else
  3277. {
  3278. team = TEAM_TERRORIST;
  3279. }
  3280. // No choices left
  3281. if ( TeamFull( team ) )
  3282. return TEAM_UNASSIGNED;
  3283. }
  3284. return team;
  3285. }
  3286. //checks to see if the desired team is stacked, returns true if it is
  3287. bool CCSGameRules::TeamStacked( int newTeam_id, int curTeam_id )
  3288. {
  3289. //players are allowed to change to their own team
  3290. if(newTeam_id == curTeam_id)
  3291. return false;
  3292. // if mp_limitteams is 0, don't check
  3293. if ( mp_limitteams.GetInt() == 0 )
  3294. return false;
  3295. switch ( newTeam_id )
  3296. {
  3297. case TEAM_TERRORIST:
  3298. if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR)
  3299. {
  3300. if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt() - 1))
  3301. return true;
  3302. else
  3303. return false;
  3304. }
  3305. else
  3306. {
  3307. if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt()))
  3308. return true;
  3309. else
  3310. return false;
  3311. }
  3312. break;
  3313. case TEAM_CT:
  3314. if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR)
  3315. {
  3316. if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt() - 1))
  3317. return true;
  3318. else
  3319. return false;
  3320. }
  3321. else
  3322. {
  3323. if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt()))
  3324. return true;
  3325. else
  3326. return false;
  3327. }
  3328. break;
  3329. }
  3330. return false;
  3331. }
  3332. //=========================================================
  3333. //=========================================================
  3334. bool CCSGameRules::FPlayerCanRespawn( CBasePlayer *pBasePlayer )
  3335. {
  3336. CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer );
  3337. if ( !pPlayer )
  3338. Error( "FPlayerCanRespawn: pPlayer=0" );
  3339. // Player cannot respawn twice in a round
  3340. if ( pPlayer->m_iNumSpawns > 0 && m_bFirstConnected )
  3341. return false;
  3342. // If they're dead after the map has ended, and it's about to start the next round,
  3343. // wait for the round restart to respawn them.
  3344. if ( gpGlobals->curtime < m_flRestartRoundTime )
  3345. return false;
  3346. // Only valid team members can spawn
  3347. if ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
  3348. return false;
  3349. // Only players with a valid class can spawn
  3350. if ( pPlayer->GetClass() == CS_CLASS_NONE )
  3351. return false;
  3352. // Player cannot respawn until next round if more than 20 seconds in
  3353. // Tabulate the number of players on each team.
  3354. m_iNumCT = GetGlobalTeam( TEAM_CT )->GetNumPlayers();
  3355. m_iNumTerrorist = GetGlobalTeam( TEAM_TERRORIST )->GetNumPlayers();
  3356. if ( m_iNumTerrorist > 0 && m_iNumCT > 0 )
  3357. {
  3358. if ( gpGlobals->curtime > (m_fRoundStartTime + 20) )
  3359. {
  3360. //If this player just connected and fadetoblack is on, then maybe
  3361. //the server admin doesn't want him peeking around.
  3362. color32_s clr = {0,0,0,255};
  3363. if ( mp_fadetoblack.GetBool() )
  3364. {
  3365. UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT );
  3366. }
  3367. return false;
  3368. }
  3369. }
  3370. // Player cannot respawn while in the Choose Appearance menu
  3371. //if ( pPlayer->m_iMenu == Menu_ChooseAppearance )
  3372. // return false;
  3373. return true;
  3374. }
  3375. void CCSGameRules::TerminateRound(float tmDelay, int iReason )
  3376. {
  3377. variant_t emptyVariant;
  3378. int iWinnerTeam = WINNER_NONE;
  3379. const char *text = "UNKNOWN";
  3380. // UTIL_ClientPrintAll( HUD_PRINTCENTER, sentence );
  3381. switch ( iReason )
  3382. {
  3383. // Terror wins:
  3384. case Target_Bombed:
  3385. text = "#Target_Bombed";
  3386. iWinnerTeam = WINNER_TER;
  3387. break;
  3388. case VIP_Assassinated:
  3389. text = "#VIP_Assassinated";
  3390. iWinnerTeam = WINNER_TER;
  3391. break;
  3392. case Terrorists_Escaped:
  3393. text = "#Terrorists_Escaped";
  3394. iWinnerTeam = WINNER_TER;
  3395. break;
  3396. case Terrorists_Win:
  3397. text = "#Terrorists_Win";
  3398. iWinnerTeam = WINNER_TER;
  3399. break;
  3400. case Hostages_Not_Rescued:
  3401. text = "#Hostages_Not_Rescued";
  3402. iWinnerTeam = WINNER_TER;
  3403. break;
  3404. case VIP_Not_Escaped:
  3405. text = "#VIP_Not_Escaped";
  3406. iWinnerTeam = WINNER_TER;
  3407. break;
  3408. // CT wins:
  3409. case VIP_Escaped:
  3410. text = "#VIP_Escaped";
  3411. iWinnerTeam = WINNER_CT;
  3412. break;
  3413. case CTs_PreventEscape:
  3414. text = "#CTs_PreventEscape";
  3415. iWinnerTeam = WINNER_CT;
  3416. break;
  3417. case Escaping_Terrorists_Neutralized:
  3418. text = "#Escaping_Terrorists_Neutralized";
  3419. iWinnerTeam = WINNER_CT;
  3420. break;
  3421. case Bomb_Defused:
  3422. text = "#Bomb_Defused";
  3423. iWinnerTeam = WINNER_CT;
  3424. break;
  3425. case CTs_Win:
  3426. text = "#CTs_Win";
  3427. iWinnerTeam = WINNER_CT;
  3428. break;
  3429. case All_Hostages_Rescued:
  3430. text = "#All_Hostages_Rescued";
  3431. iWinnerTeam = WINNER_CT;
  3432. break;
  3433. case Target_Saved:
  3434. text = "#Target_Saved";
  3435. iWinnerTeam = WINNER_CT;
  3436. break;
  3437. case Terrorists_Not_Escaped:
  3438. text = "#Terrorists_Not_Escaped";
  3439. iWinnerTeam = WINNER_CT;
  3440. break;
  3441. // no winners:
  3442. case Game_Commencing:
  3443. text = "#Game_Commencing";
  3444. iWinnerTeam = WINNER_DRAW;
  3445. break;
  3446. case Round_Draw:
  3447. text = "#Round_Draw";
  3448. iWinnerTeam = WINNER_DRAW;
  3449. break;
  3450. default:
  3451. DevMsg("TerminateRound: unknown round end ID %i\n", iReason );
  3452. break;
  3453. }
  3454. m_iRoundWinStatus = iWinnerTeam;
  3455. m_flRestartRoundTime = gpGlobals->curtime + tmDelay;
  3456. if ( iWinnerTeam == WINNER_CT )
  3457. {
  3458. for( int i=0;i<g_Hostages.Count();i++ )
  3459. g_Hostages[i]->AcceptInput( "CTsWin", NULL, NULL, emptyVariant, 0 );
  3460. }
  3461. else if ( iWinnerTeam == WINNER_TER )
  3462. {
  3463. for( int i=0;i<g_Hostages.Count();i++ )
  3464. g_Hostages[i]->AcceptInput( "TerroristsWin", NULL, NULL, emptyVariant, 0 );
  3465. }
  3466. else
  3467. {
  3468. Assert( iWinnerTeam == WINNER_NONE || iWinnerTeam == WINNER_DRAW );
  3469. }
  3470. //=============================================================================
  3471. // HPE_BEGIN:
  3472. //=============================================================================
  3473. // [tj] Check for any non-player-specific achievements.
  3474. ProcessEndOfRoundAchievements(iWinnerTeam, iReason);
  3475. if( iReason != Game_Commencing )
  3476. {
  3477. // [pfreese] Setup and send win panel event (primarily funfact data)
  3478. FunFact funfact;
  3479. funfact.szLocalizationToken = "";
  3480. funfact.iPlayer = 0;
  3481. funfact.iData1 = 0;
  3482. funfact.iData2 = 0;
  3483. funfact.iData3 = 0;
  3484. m_pFunFactManager->GetRoundEndFunFact( iWinnerTeam, iReason, funfact);
  3485. //Send all the info needed for the win panel
  3486. IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_round" );
  3487. if ( winEvent )
  3488. {
  3489. // determine what categories to send
  3490. if ( GetRoundRemainingTime() <= 0 )
  3491. {
  3492. // timer expired, defenders win
  3493. // show total time that was defended
  3494. winEvent->SetBool( "show_timer_defend", true );
  3495. winEvent->SetInt( "timer_time", m_iRoundTime );
  3496. }
  3497. else
  3498. {
  3499. // attackers win
  3500. // show time it took for them to win
  3501. winEvent->SetBool( "show_timer_attack", true );
  3502. int iTimeElapsed = m_iRoundTime - GetRoundRemainingTime();
  3503. winEvent->SetInt( "timer_time", iTimeElapsed );
  3504. }
  3505. winEvent->SetInt( "final_event", iReason );
  3506. // Set the fun fact data in the event
  3507. winEvent->SetString( "funfact_token", funfact.szLocalizationToken);
  3508. winEvent->SetInt( "funfact_player", funfact.iPlayer );
  3509. winEvent->SetInt( "funfact_data1", funfact.iData1 );
  3510. winEvent->SetInt( "funfact_data2", funfact.iData2 );
  3511. winEvent->SetInt( "funfact_data3", funfact.iData3 );
  3512. gameeventmanager->FireEvent( winEvent );
  3513. }
  3514. }
  3515. // [tj] Inform players that the round is over
  3516. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  3517. {
  3518. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  3519. if(pPlayer)
  3520. {
  3521. pPlayer->OnRoundEnd(iWinnerTeam, iReason);
  3522. }
  3523. }
  3524. //=============================================================================
  3525. // HPE_END
  3526. //=============================================================================
  3527. IGameEvent * event = gameeventmanager->CreateEvent( "round_end" );
  3528. if ( event )
  3529. {
  3530. event->SetInt( "winner", iWinnerTeam );
  3531. event->SetInt( "reason", iReason );
  3532. event->SetString( "message", text );
  3533. event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
  3534. gameeventmanager->FireEvent( event );
  3535. }
  3536. if ( GetMapRemainingTime() == 0.0f )
  3537. {
  3538. UTIL_LogPrintf("World triggered \"Intermission_Time_Limit\"\n");
  3539. GoToIntermission();
  3540. }
  3541. }
  3542. //=============================================================================
  3543. // HPE_BEGIN:
  3544. //=============================================================================
  3545. // Helper to determine if all players on a team are playing for the same clan
  3546. static bool IsClanTeam( CTeam *pTeam )
  3547. {
  3548. uint32 iTeamClan = 0;
  3549. for ( int iPlayer = 0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  3550. {
  3551. CBasePlayer *pPlayer = pTeam->GetPlayer( iPlayer );
  3552. if ( !pPlayer )
  3553. return false;
  3554. const char *pClanID = engine->GetClientConVarValue( pPlayer->entindex(), "cl_clanid" );
  3555. uint32 iPlayerClan = atoi( pClanID );
  3556. if ( iPlayer == 0 )
  3557. {
  3558. // Initialize the team clan
  3559. iTeamClan = iPlayerClan;
  3560. }
  3561. else
  3562. {
  3563. if ( iPlayerClan != iTeamClan || iPlayerClan == 0 )
  3564. return false;
  3565. }
  3566. }
  3567. return iTeamClan != 0;
  3568. }
  3569. // [tj] This is where we check non-player-specific that occur at the end of the round
  3570. void CCSGameRules::ProcessEndOfRoundAchievements(int iWinnerTeam, int iReason)
  3571. {
  3572. if (iWinnerTeam == WINNER_CT || iWinnerTeam == WINNER_TER)
  3573. {
  3574. int losingTeamId = (iWinnerTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
  3575. CTeam* losingTeam = GetGlobalTeam(losingTeamId);
  3576. //Check for players we should ignore when checking team size.
  3577. int ignoreCount = 0;
  3578. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  3579. {
  3580. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  3581. if (pPlayer)
  3582. {
  3583. int teamNum = pPlayer->GetTeamNumber();
  3584. if ( teamNum == losingTeamId )
  3585. {
  3586. if (pPlayer->WasNotKilledNaturally())
  3587. {
  3588. ignoreCount++;
  3589. }
  3590. }
  3591. }
  3592. }
  3593. // [tj] Check extermination with no losses achievement
  3594. if ( ( ( iReason == CTs_Win && m_bNoCTsKilled ) || ( iReason == Terrorists_Win && m_bNoTerroristsKilled ) )
  3595. && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement)
  3596. {
  3597. CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
  3598. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  3599. {
  3600. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  3601. Assert( pPlayer );
  3602. if ( !pPlayer )
  3603. continue;
  3604. pPlayer->AwardAchievement(CSLosslessExtermination);
  3605. }
  3606. }
  3607. // [tj] Check flawless victory achievement - currently requiring extermination
  3608. if (((iReason == CTs_Win && m_bNoCTsDamaged) || (iReason == Terrorists_Win && m_bNoTerroristsDamaged))
  3609. && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement)
  3610. {
  3611. CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
  3612. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  3613. {
  3614. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  3615. Assert( pPlayer );
  3616. if ( !pPlayer )
  3617. continue;
  3618. pPlayer->AwardAchievement(CSFlawlessVictory);
  3619. }
  3620. }
  3621. // [tj] Check bloodless victory achievement
  3622. if (((iWinnerTeam == TEAM_TERRORIST && m_bNoCTsKilled) || (iWinnerTeam == Terrorists_Win && m_bNoTerroristsKilled))
  3623. && losingTeam && losingTeam->GetNumPlayers() >= AchievementConsts::DefaultMinOpponentsForAchievement)
  3624. {
  3625. CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
  3626. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  3627. {
  3628. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  3629. Assert( pPlayer );
  3630. if ( !pPlayer )
  3631. continue;
  3632. pPlayer->AwardAchievement(CSBloodlessVictory);
  3633. }
  3634. }
  3635. // Check the clan match achievement
  3636. CTeam *pWinningTeam = GetGlobalTeam( iWinnerTeam );
  3637. if ( pWinningTeam && pWinningTeam->GetNumPlayers() >= AchievementConsts::DefaultMinOpponentsForAchievement &&
  3638. losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement &&
  3639. IsClanTeam( pWinningTeam ) && IsClanTeam( losingTeam ) )
  3640. {
  3641. for ( int iPlayer=0; iPlayer < pWinningTeam->GetNumPlayers(); iPlayer++ )
  3642. {
  3643. CCSPlayer *pPlayer = ToCSPlayer( pWinningTeam->GetPlayer( iPlayer ) );
  3644. if ( !pPlayer )
  3645. continue;
  3646. pPlayer->AwardAchievement( CSWinClanMatch );
  3647. }
  3648. }
  3649. }
  3650. }
  3651. //[tj] Counts the number of players in each category in the struct (dead, alive, etc...)
  3652. void CCSGameRules::GetPlayerCounts(TeamPlayerCounts teamCounts[TEAM_MAXCOUNT])
  3653. {
  3654. memset(teamCounts, 0, sizeof(TeamPlayerCounts) * TEAM_MAXCOUNT);
  3655. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  3656. {
  3657. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  3658. if (pPlayer)
  3659. {
  3660. int iTeam = pPlayer->GetTeamNumber();
  3661. if (iTeam >= 0 && iTeam < TEAM_MAXCOUNT)
  3662. {
  3663. ++teamCounts[iTeam].totalPlayers;
  3664. if (pPlayer->IsAlive())
  3665. {
  3666. ++teamCounts[iTeam].totalAlivePlayers;
  3667. }
  3668. else
  3669. {
  3670. ++teamCounts[iTeam].totalDeadPlayers;
  3671. //If the player has joined a team bit isn't in the game yet
  3672. if (pPlayer->State_Get() == STATE_PICKINGCLASS)
  3673. {
  3674. ++teamCounts[iTeam].unenteredPlayers;
  3675. }
  3676. else if (pPlayer->WasNotKilledNaturally())
  3677. {
  3678. ++teamCounts[iTeam].suicidedPlayers;
  3679. }
  3680. else
  3681. {
  3682. ++teamCounts[iTeam].killedPlayers;
  3683. }
  3684. }
  3685. }
  3686. }
  3687. }
  3688. }
  3689. //=============================================================================
  3690. // HPE_END
  3691. //=============================================================================
  3692. void CCSGameRules::UpdateTeamScores()
  3693. {
  3694. CTeam *pTerrorists = GetGlobalTeam( TEAM_TERRORIST );
  3695. CTeam *pCTs = GetGlobalTeam( TEAM_CT );
  3696. Assert( pTerrorists && pCTs );
  3697. if( pTerrorists )
  3698. pTerrorists->SetScore( m_iNumTerroristWins );
  3699. if( pCTs )
  3700. pCTs->SetScore( m_iNumCTWins );
  3701. }
  3702. void CCSGameRules::CheckMapConditions()
  3703. {
  3704. // Check to see if this map has a bomb target in it
  3705. if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) )
  3706. {
  3707. m_bMapHasBombTarget = true;
  3708. m_bMapHasBombZone = true;
  3709. }
  3710. else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) )
  3711. {
  3712. m_bMapHasBombTarget = true;
  3713. m_bMapHasBombZone = false;
  3714. }
  3715. else
  3716. {
  3717. m_bMapHasBombTarget = false;
  3718. m_bMapHasBombZone = false;
  3719. }
  3720. // See if the map has func_buyzone entities
  3721. // Used by CBasePlayer::HandleSignals() to support maps without these entities
  3722. if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) )
  3723. {
  3724. m_bMapHasBuyZone = true;
  3725. }
  3726. else
  3727. {
  3728. m_bMapHasBuyZone = false;
  3729. }
  3730. // Check to see if this map has hostage rescue zones
  3731. if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) )
  3732. {
  3733. m_bMapHasRescueZone = true;
  3734. }
  3735. else
  3736. {
  3737. m_bMapHasRescueZone = false;
  3738. }
  3739. // GOOSEMAN : See if this map has func_escapezone entities
  3740. if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) )
  3741. {
  3742. m_bMapHasEscapeZone = true;
  3743. }
  3744. else
  3745. {
  3746. m_bMapHasEscapeZone = false;
  3747. }
  3748. // Check to see if this map has VIP safety zones
  3749. if ( gEntList.FindEntityByClassname( NULL, "func_vip_safetyzone" ) )
  3750. {
  3751. m_iMapHasVIPSafetyZone = 1;
  3752. }
  3753. else
  3754. {
  3755. m_iMapHasVIPSafetyZone = 2;
  3756. }
  3757. }
  3758. void CCSGameRules::SwapAllPlayers()
  3759. {
  3760. // MOTODO we have to make sure that enought spaning points exits
  3761. Assert ( 0 );
  3762. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  3763. {
  3764. /* CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  3765. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  3766. pPlayer->SwitchTeam(); */
  3767. }
  3768. // Swap Team victories
  3769. int iTemp;
  3770. iTemp = m_iNumCTWins;
  3771. m_iNumCTWins = m_iNumTerroristWins;
  3772. m_iNumTerroristWins = iTemp;
  3773. // Update the clients team score
  3774. UpdateTeamScores();
  3775. }
  3776. bool CS_FindInList( const char **pStrings, const char *pToFind )
  3777. {
  3778. return FindInList( pStrings, pToFind );
  3779. }
  3780. void CCSGameRules::CleanUpMap()
  3781. {
  3782. if (IsLogoMap())
  3783. return;
  3784. // Recreate all the map entities from the map data (preserving their indices),
  3785. // then remove everything else except the players.
  3786. // Get rid of all entities except players.
  3787. CBaseEntity *pCur = gEntList.FirstEnt();
  3788. while ( pCur )
  3789. {
  3790. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pCur );
  3791. // Weapons with owners don't want to be removed..
  3792. if ( pWeapon )
  3793. {
  3794. //=============================================================================
  3795. // HPE_BEGIN:
  3796. // [dwenger] Handle round restart processing for the weapon.
  3797. //=============================================================================
  3798. pWeapon->OnRoundRestart();
  3799. //=============================================================================
  3800. // HPE_END
  3801. //=============================================================================
  3802. if ( pWeapon->ShouldRemoveOnRoundRestart() )
  3803. {
  3804. UTIL_Remove( pCur );
  3805. }
  3806. }
  3807. // remove entities that has to be restored on roundrestart (breakables etc)
  3808. else if ( !CS_FindInList( s_PreserveEnts, pCur->GetClassname() ) )
  3809. {
  3810. UTIL_Remove( pCur );
  3811. }
  3812. pCur = gEntList.NextEnt( pCur );
  3813. }
  3814. // Really remove the entities so we can have access to their slots below.
  3815. gEntList.CleanupDeleteList();
  3816. // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that
  3817. // could kill respawning CTs
  3818. g_EventQueue.Clear();
  3819. // Now reload the map entities.
  3820. class CCSMapEntityFilter : public IMapEntityFilter
  3821. {
  3822. public:
  3823. virtual bool ShouldCreateEntity( const char *pClassname )
  3824. {
  3825. // Don't recreate the preserved entities.
  3826. if ( !CS_FindInList( s_PreserveEnts, pClassname ) )
  3827. {
  3828. return true;
  3829. }
  3830. else
  3831. {
  3832. // Increment our iterator since it's not going to call CreateNextEntity for this ent.
  3833. if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
  3834. m_iIterator = g_MapEntityRefs.Next( m_iIterator );
  3835. return false;
  3836. }
  3837. }
  3838. virtual CBaseEntity* CreateNextEntity( const char *pClassname )
  3839. {
  3840. if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
  3841. {
  3842. // This shouldn't be possible. When we loaded the map, it should have used
  3843. // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list
  3844. // with the same list of entities we're referring to here.
  3845. Assert( false );
  3846. return NULL;
  3847. }
  3848. else
  3849. {
  3850. CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
  3851. m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
  3852. if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) )
  3853. {
  3854. // Doh! The entity was delete and its slot was reused.
  3855. // Just use any old edict slot. This case sucks because we lose the baseline.
  3856. return CreateEntityByName( pClassname );
  3857. }
  3858. else
  3859. {
  3860. // Cool, the slot where this entity was is free again (most likely, the entity was
  3861. // freed above). Now create an entity with this specific index.
  3862. return CreateEntityByName( pClassname, ref.m_iEdict );
  3863. }
  3864. }
  3865. }
  3866. public:
  3867. int m_iIterator; // Iterator into g_MapEntityRefs.
  3868. };
  3869. CCSMapEntityFilter filter;
  3870. filter.m_iIterator = g_MapEntityRefs.Head();
  3871. // DO NOT CALL SPAWN ON info_node ENTITIES!
  3872. MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
  3873. }
  3874. bool CCSGameRules::IsThereABomber()
  3875. {
  3876. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  3877. {
  3878. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  3879. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  3880. {
  3881. if ( pPlayer->GetTeamNumber() == TEAM_CT )
  3882. continue;
  3883. if ( pPlayer->HasC4() )
  3884. return true; //There you are.
  3885. }
  3886. }
  3887. //Didn't find a bomber.
  3888. return false;
  3889. }
  3890. void CCSGameRules::EndRound()
  3891. {
  3892. // fake a round end
  3893. CSGameRules()->TerminateRound( 0.0f, Round_Draw );
  3894. }
  3895. CBaseEntity *CCSGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
  3896. {
  3897. // gat valid spwan point
  3898. CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
  3899. // drop down to ground
  3900. Vector GroundPos = DropToGround( pPlayer, pSpawnSpot->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX );
  3901. // Move the player to the place it said.
  3902. pPlayer->Teleport( &pSpawnSpot->GetAbsOrigin(), &pSpawnSpot->GetLocalAngles(), &vec3_origin );
  3903. pPlayer->m_Local.m_vecPunchAngle = vec3_angle;
  3904. return pSpawnSpot;
  3905. }
  3906. // checks if the spot is clear of players
  3907. bool CCSGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer )
  3908. {
  3909. if ( !pSpot->IsTriggered( pPlayer ) )
  3910. {
  3911. return false;
  3912. }
  3913. Vector mins = GetViewVectors()->m_vHullMin;
  3914. Vector maxs = GetViewVectors()->m_vHullMax;
  3915. Vector vTestMins = pSpot->GetAbsOrigin() + mins;
  3916. Vector vTestMaxs = pSpot->GetAbsOrigin() + maxs;
  3917. // First test the starting origin.
  3918. return UTIL_IsSpaceEmpty( pPlayer, vTestMins, vTestMaxs );
  3919. }
  3920. bool CCSGameRules::IsThereABomb()
  3921. {
  3922. bool bBombFound = false;
  3923. /* are there any bombs, either laying around, or in someone's inventory? */
  3924. if( gEntList.FindEntityByClassname( NULL, WEAPON_C4_CLASSNAME ) != 0 )
  3925. {
  3926. bBombFound = true;
  3927. }
  3928. /* what about planted bombs!? */
  3929. else if( gEntList.FindEntityByClassname( NULL, PLANTED_C4_CLASSNAME ) != 0 )
  3930. {
  3931. bBombFound = true;
  3932. }
  3933. return bBombFound;
  3934. }
  3935. void CCSGameRules::HostageTouched()
  3936. {
  3937. if( gpGlobals->curtime > m_flNextHostageAnnouncement && m_iRoundWinStatus == WINNER_NONE )
  3938. {
  3939. //BroadcastSound( "Event.HostageTouched" );
  3940. m_flNextHostageAnnouncement = gpGlobals->curtime + 60.0;
  3941. }
  3942. }
  3943. void CCSGameRules::CreateStandardEntities()
  3944. {
  3945. // Create the player resource
  3946. g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "cs_player_manager", vec3_origin, vec3_angle );
  3947. // Create the entity that will send our data to the client.
  3948. #ifdef DBGFLAG_ASSERT
  3949. CBaseEntity *pEnt =
  3950. #endif
  3951. CBaseEntity::Create( "cs_gamerules", vec3_origin, vec3_angle );
  3952. Assert( pEnt );
  3953. }
  3954. #define MY_USHRT_MAX 0xffff
  3955. #define MY_UCHAR_MAX 0xff
  3956. bool DataHasChanged( void )
  3957. {
  3958. for ( int i = 0; i < CS_NUM_LEVELS; i++ )
  3959. {
  3960. if ( g_iTerroristVictories[i] != 0 || g_iCounterTVictories[i] != 0 )
  3961. return true;
  3962. }
  3963. for ( int i = 0; i < WEAPON_MAX; i++ )
  3964. {
  3965. if ( g_iWeaponPurchases[i] != 0 )
  3966. return true;
  3967. }
  3968. return false;
  3969. }
  3970. void CCSGameRules::UploadGameStats( void )
  3971. {
  3972. g_flGameStatsUpdateTime -= gpGlobals->curtime;
  3973. if ( g_flGameStatsUpdateTime > 0 )
  3974. return;
  3975. if ( IsBlackMarket() == false )
  3976. return;
  3977. if ( m_bDontUploadStats == true )
  3978. return;
  3979. if ( DataHasChanged() == true )
  3980. {
  3981. cs_gamestats_t stats;
  3982. memset( &stats, 0, sizeof(stats) );
  3983. // Header
  3984. stats.header.iVersion = CS_STATS_BLOB_VERSION;
  3985. Q_strncpy( stats.header.szGameName, "cstrike", sizeof(stats.header.szGameName) );
  3986. Q_strncpy( stats.header.szMapName, STRING( gpGlobals->mapname ), sizeof( stats.header.szMapName ) );
  3987. ConVar *hostip = cvar->FindVar( "hostip" );
  3988. if ( hostip )
  3989. {
  3990. int ip = hostip->GetInt();
  3991. stats.header.ipAddr[0] = ip >> 24;
  3992. stats.header.ipAddr[1] = ( ip >> 16 ) & MY_UCHAR_MAX;
  3993. stats.header.ipAddr[2] = ( ip >> 8 ) & MY_UCHAR_MAX;
  3994. stats.header.ipAddr[3] = ( ip ) & MY_UCHAR_MAX;
  3995. }
  3996. ConVar *hostport = cvar->FindVar( "hostip" );
  3997. if ( hostport )
  3998. {
  3999. stats.header.port = hostport->GetInt();
  4000. }
  4001. stats.header.serverid = 0;
  4002. stats.iMinutesPlayed = clamp( (short)( gpGlobals->curtime / 60 ), 0, MY_USHRT_MAX );
  4003. memcpy( stats.iTerroristVictories, g_iTerroristVictories, sizeof( g_iTerroristVictories) );
  4004. memcpy( stats.iCounterTVictories, g_iCounterTVictories, sizeof( g_iCounterTVictories) );
  4005. memcpy( stats.iBlackMarketPurchases, g_iWeaponPurchases, sizeof( g_iWeaponPurchases) );
  4006. stats.iAutoBuyPurchases = g_iAutoBuyPurchases;
  4007. stats.iReBuyPurchases = g_iReBuyPurchases;
  4008. stats.iAutoBuyM4A1Purchases = g_iAutoBuyM4A1Purchases;
  4009. stats.iAutoBuyAK47Purchases = g_iAutoBuyAK47Purchases;
  4010. stats.iAutoBuyFamasPurchases = g_iAutoBuyFamasPurchases;
  4011. stats.iAutoBuyGalilPurchases = g_iAutoBuyGalilPurchases;
  4012. stats.iAutoBuyVestHelmPurchases = g_iAutoBuyVestHelmPurchases;
  4013. stats.iAutoBuyVestPurchases = g_iAutoBuyVestPurchases;
  4014. const void *pvBlobData = ( const void * )( &stats );
  4015. unsigned int uBlobSize = sizeof( stats );
  4016. if ( gamestatsuploader )
  4017. {
  4018. gamestatsuploader->UploadGameStats(
  4019. STRING( gpGlobals->mapname ),
  4020. CS_STATS_BLOB_VERSION,
  4021. uBlobSize,
  4022. pvBlobData );
  4023. }
  4024. memset( g_iWeaponPurchases, 0, sizeof( g_iWeaponPurchases) );
  4025. memset( g_iTerroristVictories, 0, sizeof( g_iTerroristVictories) );
  4026. memset( g_iCounterTVictories, 0, sizeof( g_iTerroristVictories) );
  4027. g_iAutoBuyPurchases = 0;
  4028. g_iReBuyPurchases = 0;
  4029. g_iAutoBuyM4A1Purchases = 0;
  4030. g_iAutoBuyAK47Purchases = 0;
  4031. g_iAutoBuyFamasPurchases = 0;
  4032. g_iAutoBuyGalilPurchases = 0;
  4033. g_iAutoBuyVestHelmPurchases = 0;
  4034. g_iAutoBuyVestPurchases = 0;
  4035. }
  4036. g_flGameStatsUpdateTime = CS_GAME_STATS_UPDATE; //Next update is between 22 and 24 hours.
  4037. }
  4038. #endif // CLIENT_DLL
  4039. CBaseCombatWeapon *CCSGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
  4040. {
  4041. CBaseCombatWeapon *bestWeapon = NULL;
  4042. // search all the weapons looking for the closest next
  4043. for ( int i = 0; i < MAX_WEAPONS; i++ )
  4044. {
  4045. CBaseCombatWeapon *weapon = pPlayer->GetWeapon(i);
  4046. if ( !weapon )
  4047. continue;
  4048. if ( !weapon->CanBeSelected() || weapon == pCurrentWeapon )
  4049. continue;
  4050. #ifndef CLIENT_DLL
  4051. CCSPlayer *csPlayer = ToCSPlayer(pPlayer);
  4052. CWeaponCSBase *csWeapon = static_cast< CWeaponCSBase * >(weapon);
  4053. if ( csPlayer && csPlayer->IsBot() && !TheCSBots()->IsWeaponUseable( csWeapon ) )
  4054. continue;
  4055. #endif // CLIENT_DLL
  4056. if ( bestWeapon )
  4057. {
  4058. if ( weapon->GetSlot() < bestWeapon->GetSlot() )
  4059. {
  4060. bestWeapon = weapon;
  4061. }
  4062. else if ( weapon->GetSlot() == bestWeapon->GetSlot() && weapon->GetPosition() < bestWeapon->GetPosition() )
  4063. {
  4064. bestWeapon = weapon;
  4065. }
  4066. }
  4067. else
  4068. {
  4069. bestWeapon = weapon;
  4070. }
  4071. }
  4072. return bestWeapon;
  4073. }
  4074. float CCSGameRules::GetMapRemainingTime()
  4075. {
  4076. #ifdef GAME_DLL
  4077. if ( nextlevel.GetString() && *nextlevel.GetString() )
  4078. {
  4079. return 0;
  4080. }
  4081. #endif
  4082. // if timelimit is disabled, return -1
  4083. if ( mp_timelimit.GetInt() <= 0 )
  4084. return -1;
  4085. // timelimit is in minutes
  4086. float flTimeLeft = ( m_flGameStartTime + mp_timelimit.GetInt() * 60 ) - gpGlobals->curtime;
  4087. // never return a negative value
  4088. if ( flTimeLeft < 0 )
  4089. flTimeLeft = 0;
  4090. return flTimeLeft;
  4091. }
  4092. float CCSGameRules::GetMapElapsedTime( void )
  4093. {
  4094. return gpGlobals->curtime;
  4095. }
  4096. float CCSGameRules::GetRoundRemainingTime()
  4097. {
  4098. return (float) (m_fRoundStartTime + m_iRoundTime) - gpGlobals->curtime;
  4099. }
  4100. float CCSGameRules::GetRoundStartTime()
  4101. {
  4102. return m_fRoundStartTime;
  4103. }
  4104. float CCSGameRules::GetRoundElapsedTime()
  4105. {
  4106. return gpGlobals->curtime - m_fRoundStartTime;
  4107. }
  4108. bool CCSGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
  4109. {
  4110. if ( collisionGroup0 > collisionGroup1 )
  4111. {
  4112. // swap so that lowest is always first
  4113. ::V_swap(collisionGroup0,collisionGroup1);
  4114. }
  4115. //Don't stand on COLLISION_GROUP_WEAPONs
  4116. if( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT &&
  4117. collisionGroup1 == COLLISION_GROUP_WEAPON )
  4118. {
  4119. return false;
  4120. }
  4121. // TODO: make a CS-SPECIFIC COLLISION GROUP FOR PHYSICS PROPS THAT USE THIS COLLISION BEHAVIOR.
  4122. if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
  4123. collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
  4124. {
  4125. return false;
  4126. }
  4127. if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
  4128. {
  4129. // let debris and multiplayer objects collide
  4130. return true;
  4131. }
  4132. return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
  4133. }
  4134. bool CCSGameRules::IsFreezePeriod()
  4135. {
  4136. return m_bFreezePeriod;
  4137. }
  4138. bool CCSGameRules::IsVIPMap() const
  4139. {
  4140. //MIKETODO: VIP mode
  4141. return false;
  4142. }
  4143. bool CCSGameRules::IsBombDefuseMap() const
  4144. {
  4145. return m_bMapHasBombTarget;
  4146. }
  4147. bool CCSGameRules::IsHostageRescueMap() const
  4148. {
  4149. return m_bMapHasRescueZone;
  4150. }
  4151. bool CCSGameRules::IsLogoMap() const
  4152. {
  4153. return m_bLogoMap;
  4154. }
  4155. float CCSGameRules::GetBuyTimeLength() const
  4156. {
  4157. return ( mp_buytime.GetFloat() * 60 );
  4158. }
  4159. bool CCSGameRules::IsBuyTimeElapsed()
  4160. {
  4161. return ( GetRoundElapsedTime() > GetBuyTimeLength() );
  4162. }
  4163. int CCSGameRules::DefaultFOV()
  4164. {
  4165. return 90;
  4166. }
  4167. const CViewVectors* CCSGameRules::GetViewVectors() const
  4168. {
  4169. return &g_CSViewVectors;
  4170. }
  4171. //-----------------------------------------------------------------------------
  4172. // Purpose: Init CS ammo definitions
  4173. //-----------------------------------------------------------------------------
  4174. // shared ammo definition
  4175. // JAY: Trying to make a more physical bullet response
  4176. #define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
  4177. #define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
  4178. // exaggerate all of the forces, but use real numbers to keep them consistent
  4179. #define BULLET_IMPULSE_EXAGGERATION 1
  4180. // convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
  4181. #define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
  4182. static CCSAmmoDef ammoDef;
  4183. CCSAmmoDef* GetCSAmmoDef()
  4184. {
  4185. GetAmmoDef(); // to initialize the ammo info
  4186. return &ammoDef;
  4187. }
  4188. CAmmoDef* GetAmmoDef()
  4189. {
  4190. static bool bInitted = false;
  4191. if ( !bInitted )
  4192. {
  4193. bInitted = true;
  4194. ammoDef.AddAmmoType( BULLET_PLAYER_50AE, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_50AE_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
  4195. ammoDef.AddAmmoType( BULLET_PLAYER_762MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_762mm_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
  4196. ammoDef.AddAmmoType( BULLET_PLAYER_556MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
  4197. ammoDef.AddAmmoType( BULLET_PLAYER_556MM_BOX, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_box_max",2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
  4198. ammoDef.AddAmmoType( BULLET_PLAYER_338MAG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_338mag_max", 2800 * BULLET_IMPULSE_EXAGGERATION, 0, 12, 16 );
  4199. ammoDef.AddAmmoType( BULLET_PLAYER_9MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_9mm_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 5, 10 );
  4200. ammoDef.AddAmmoType( BULLET_PLAYER_BUCKSHOT, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_buckshot_max", 600 * BULLET_IMPULSE_EXAGGERATION, 0, 3, 6 );
  4201. ammoDef.AddAmmoType( BULLET_PLAYER_45ACP, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_45acp_max", 2100 * BULLET_IMPULSE_EXAGGERATION, 0, 6, 10 );
  4202. ammoDef.AddAmmoType( BULLET_PLAYER_357SIG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 4, 8 );
  4203. ammoDef.AddAmmoType( BULLET_PLAYER_57MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_57mm_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 4, 8 );
  4204. ammoDef.AddAmmoType( AMMO_TYPE_HEGRENADE, DMG_BLAST, TRACER_LINE, 0, 0, "ammo_hegrenade_max", 1, 0 );
  4205. ammoDef.AddAmmoType( AMMO_TYPE_FLASHBANG, 0, TRACER_LINE, 0, 0, "ammo_flashbang_max", 1, 0 );
  4206. ammoDef.AddAmmoType( AMMO_TYPE_SMOKEGRENADE, 0, TRACER_LINE, 0, 0, "ammo_smokegrenade_max", 1, 0 );
  4207. //Adrian: I set all the prices to 0 just so the rest of the buy code works
  4208. //This should be revisited.
  4209. ammoDef.AddAmmoCost( BULLET_PLAYER_50AE, 0, 7 );
  4210. ammoDef.AddAmmoCost( BULLET_PLAYER_762MM, 0, 30 );
  4211. ammoDef.AddAmmoCost( BULLET_PLAYER_556MM, 0, 30 );
  4212. ammoDef.AddAmmoCost( BULLET_PLAYER_556MM_BOX, 0, 30 );
  4213. ammoDef.AddAmmoCost( BULLET_PLAYER_338MAG, 0, 10 );
  4214. ammoDef.AddAmmoCost( BULLET_PLAYER_9MM, 0, 30 );
  4215. ammoDef.AddAmmoCost( BULLET_PLAYER_BUCKSHOT, 0, 8 );
  4216. ammoDef.AddAmmoCost( BULLET_PLAYER_45ACP, 0, 25 );
  4217. ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG, 0, 13 );
  4218. ammoDef.AddAmmoCost( BULLET_PLAYER_57MM, 0, 50 );
  4219. }
  4220. return &ammoDef;
  4221. }
  4222. #ifndef CLIENT_DLL
  4223. const char *CCSGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
  4224. {
  4225. const char *pszPrefix = NULL;
  4226. if ( !pPlayer ) // dedicated server output
  4227. {
  4228. pszPrefix = "";
  4229. }
  4230. else
  4231. {
  4232. // team only
  4233. if ( bTeamOnly == TRUE )
  4234. {
  4235. if ( pPlayer->GetTeamNumber() == TEAM_CT )
  4236. {
  4237. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  4238. {
  4239. pszPrefix = "(Counter-Terrorist)";
  4240. }
  4241. else
  4242. {
  4243. pszPrefix = "*DEAD*(Counter-Terrorist)";
  4244. }
  4245. }
  4246. else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  4247. {
  4248. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  4249. {
  4250. pszPrefix = "(Terrorist)";
  4251. }
  4252. else
  4253. {
  4254. pszPrefix = "*DEAD*(Terrorist)";
  4255. }
  4256. }
  4257. else if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
  4258. {
  4259. pszPrefix = "(Spectator)";
  4260. }
  4261. }
  4262. // everyone
  4263. else
  4264. {
  4265. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  4266. {
  4267. pszPrefix = "";
  4268. }
  4269. else
  4270. {
  4271. if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  4272. {
  4273. pszPrefix = "*DEAD*";
  4274. }
  4275. else
  4276. {
  4277. pszPrefix = "*SPEC*";
  4278. }
  4279. }
  4280. }
  4281. }
  4282. return pszPrefix;
  4283. }
  4284. const char *CCSGameRules::GetChatLocation( bool bTeamOnly, CBasePlayer *pPlayer )
  4285. {
  4286. if ( !pPlayer ) // dedicated server output
  4287. {
  4288. return NULL;
  4289. }
  4290. // only teammates see locations
  4291. if ( !bTeamOnly )
  4292. return NULL;
  4293. // only living players have locations
  4294. if ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
  4295. return NULL;
  4296. if ( !pPlayer->IsAlive() )
  4297. return NULL;
  4298. return pPlayer->GetLastKnownPlaceName();
  4299. }
  4300. const char *CCSGameRules::GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer )
  4301. {
  4302. if ( !pPlayer ) // dedicated server output
  4303. {
  4304. return NULL;
  4305. }
  4306. const char *pszFormat = NULL;
  4307. // team only
  4308. if ( bTeamOnly == TRUE )
  4309. {
  4310. if ( pPlayer->GetTeamNumber() == TEAM_CT )
  4311. {
  4312. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  4313. {
  4314. const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
  4315. if ( chatLocation && *chatLocation )
  4316. {
  4317. pszFormat = "Cstrike_Chat_CT_Loc";
  4318. }
  4319. else
  4320. {
  4321. pszFormat = "Cstrike_Chat_CT";
  4322. }
  4323. }
  4324. else
  4325. {
  4326. pszFormat = "Cstrike_Chat_CT_Dead";
  4327. }
  4328. }
  4329. else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  4330. {
  4331. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  4332. {
  4333. const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
  4334. if ( chatLocation && *chatLocation )
  4335. {
  4336. pszFormat = "Cstrike_Chat_T_Loc";
  4337. }
  4338. else
  4339. {
  4340. pszFormat = "Cstrike_Chat_T";
  4341. }
  4342. }
  4343. else
  4344. {
  4345. pszFormat = "Cstrike_Chat_T_Dead";
  4346. }
  4347. }
  4348. else if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
  4349. {
  4350. pszFormat = "Cstrike_Chat_Spec";
  4351. }
  4352. }
  4353. // everyone
  4354. else
  4355. {
  4356. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  4357. {
  4358. pszFormat = "Cstrike_Chat_All";
  4359. }
  4360. else
  4361. {
  4362. if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  4363. {
  4364. pszFormat = "Cstrike_Chat_AllDead";
  4365. }
  4366. else
  4367. {
  4368. pszFormat = "Cstrike_Chat_AllSpec";
  4369. }
  4370. }
  4371. }
  4372. return pszFormat;
  4373. }
  4374. void CCSGameRules::ClientSettingsChanged( CBasePlayer *pPlayer )
  4375. {
  4376. const char *pszNewName = engine->GetClientConVarValue( pPlayer->entindex(), "name" );
  4377. const char *pszOldName = pPlayer->GetPlayerName();
  4378. CCSPlayer *pCSPlayer = (CCSPlayer*)pPlayer;
  4379. if ( pszOldName[0] != 0 && Q_strncmp( pszOldName, pszNewName, MAX_PLAYER_NAME_LENGTH-1 ) )
  4380. {
  4381. pCSPlayer->ChangeName( pszNewName );
  4382. }
  4383. pCSPlayer->m_bShowHints = true;
  4384. if ( pCSPlayer->IsNetClient() )
  4385. {
  4386. const char *pShowHints = engine->GetClientConVarValue( engine->IndexOfEdict( pCSPlayer->edict() ), "cl_autohelp" );
  4387. if ( pShowHints && atoi( pShowHints ) <= 0 )
  4388. {
  4389. pCSPlayer->m_bShowHints = false;
  4390. }
  4391. }
  4392. }
  4393. bool CCSGameRules::FAllowNPCs( void )
  4394. {
  4395. return false;
  4396. }
  4397. bool CCSGameRules::IsFriendlyFireOn( void )
  4398. {
  4399. return friendlyfire.GetBool();
  4400. }
  4401. CON_COMMAND( map_showspawnpoints, "Shows player spawn points (red=invalid)" )
  4402. {
  4403. CSGameRules()->ShowSpawnPoints();
  4404. }
  4405. void DrawSphere( const Vector& pos, float radius, int r, int g, int b, float lifetime )
  4406. {
  4407. Vector edge, lastEdge;
  4408. NDebugOverlay::Line( pos, pos + Vector( 0, 0, 50 ), r, g, b, true, lifetime );
  4409. lastEdge = Vector( radius + pos.x, pos.y, pos.z );
  4410. float angle;
  4411. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  4412. {
  4413. edge.x = radius * BotCOS( angle ) + pos.x;
  4414. edge.y = pos.y;
  4415. edge.z = radius * BotSIN( angle ) + pos.z;
  4416. NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
  4417. lastEdge = edge;
  4418. }
  4419. lastEdge = Vector( pos.x, radius + pos.y, pos.z );
  4420. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  4421. {
  4422. edge.x = pos.x;
  4423. edge.y = radius * BotCOS( angle ) + pos.y;
  4424. edge.z = radius * BotSIN( angle ) + pos.z;
  4425. NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
  4426. lastEdge = edge;
  4427. }
  4428. lastEdge = Vector( pos.x, radius + pos.y, pos.z );
  4429. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  4430. {
  4431. edge.x = radius * BotCOS( angle ) + pos.x;
  4432. edge.y = radius * BotSIN( angle ) + pos.y;
  4433. edge.z = pos.z;
  4434. NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
  4435. lastEdge = edge;
  4436. }
  4437. }
  4438. CON_COMMAND_F( map_showbombradius, "Shows bomb radius from the center of each bomb site and planted bomb.", FCVAR_CHEAT )
  4439. {
  4440. float flBombDamage = 500.0f;
  4441. if ( g_pMapInfo )
  4442. flBombDamage = g_pMapInfo->m_flBombRadius;
  4443. float flBombRadius = flBombDamage * 3.5f;
  4444. Msg( "Bomb Damage is %.0f, Radius is %.0f\n", flBombDamage, flBombRadius );
  4445. CBaseEntity* ent = NULL;
  4446. while ( ( ent = gEntList.FindEntityByClassname( ent, "func_bomb_target" ) ) != NULL )
  4447. {
  4448. const Vector &pos = ent->WorldSpaceCenter();
  4449. DrawSphere( pos, flBombRadius, 255, 255, 0, 10 );
  4450. }
  4451. ent = NULL;
  4452. while ( ( ent = gEntList.FindEntityByClassname( ent, "planted_c4" ) ) != NULL )
  4453. {
  4454. const Vector &pos = ent->WorldSpaceCenter();
  4455. DrawSphere( pos, flBombRadius, 255, 0, 0, 10 );
  4456. }
  4457. }
  4458. CON_COMMAND_F( map_setbombradius, "Sets the bomb radius for the map.", FCVAR_CHEAT )
  4459. {
  4460. if ( args.ArgC() != 2 )
  4461. return;
  4462. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  4463. return;
  4464. if ( !g_pMapInfo )
  4465. CBaseEntity::Create( "info_map_parameters", vec3_origin, vec3_angle );
  4466. if ( !g_pMapInfo )
  4467. return;
  4468. g_pMapInfo->m_flBombRadius = atof( args[1] );
  4469. map_showbombradius( args );
  4470. }
  4471. void CreateBlackMarketString( void )
  4472. {
  4473. g_StringTableBlackMarket = networkstringtable->CreateStringTable( "BlackMarketTable" , 1 );
  4474. }
  4475. int CCSGameRules::GetStartMoney( void )
  4476. {
  4477. if ( IsBlackMarket() )
  4478. {
  4479. return atoi( mp_startmoney.GetDefault() );
  4480. }
  4481. return mp_startmoney.GetInt();
  4482. }
  4483. //=============================================================================
  4484. // HPE_BEGIN:
  4485. // [menglish] Set up anything for all players that changes based on new players spawning mid-game
  4486. // Find and return fun fact data
  4487. //=============================================================================
  4488. //-----------------------------------------------------------------------------
  4489. // Purpose: Called when a player joins the game after it's started yet can still spawn in
  4490. //-----------------------------------------------------------------------------
  4491. void CCSGameRules::SpawningLatePlayer( CCSPlayer* pLatePlayer )
  4492. {
  4493. //Reset the round kills number of enemies for the opposite team
  4494. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  4495. {
  4496. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  4497. if(pPlayer)
  4498. {
  4499. if(pPlayer->GetTeamNumber() == pLatePlayer->GetTeamNumber())
  4500. {
  4501. continue;
  4502. }
  4503. pPlayer->m_NumEnemiesAtRoundStart++;
  4504. }
  4505. }
  4506. }
  4507. //=============================================================================
  4508. // HPE_END
  4509. //=============================================================================
  4510. //=============================================================================
  4511. // HPE_BEGIN:
  4512. // [pfreese] Test for "pistol" round, defined as the default starting round
  4513. // when players cannot purchase anything primary weapons
  4514. //=============================================================================
  4515. bool CCSGameRules::IsPistolRound()
  4516. {
  4517. return m_iTotalRoundsPlayed == 0 && GetStartMoney() <= 800;
  4518. }
  4519. //=============================================================================
  4520. // HPE_END
  4521. //=============================================================================
  4522. //=============================================================================
  4523. // HPE_BEGIN:
  4524. // [tj] So game rules can react to damage taken
  4525. // [menglish]
  4526. //=============================================================================
  4527. void CCSGameRules::PlayerTookDamage(CCSPlayer* player, const CTakeDamageInfo &damageInfo)
  4528. {
  4529. CBaseEntity *pInflictor = damageInfo.GetInflictor();
  4530. CBaseEntity *pAttacker = damageInfo.GetAttacker();
  4531. CCSPlayer *pCSScorer = (CCSPlayer *)(GetDeathScorer( pAttacker, pInflictor ));
  4532. if ( player && pCSScorer )
  4533. {
  4534. if (player->GetTeamNumber() == TEAM_CT)
  4535. {
  4536. m_bNoCTsDamaged = false;
  4537. }
  4538. if (player->GetTeamNumber() == TEAM_TERRORIST)
  4539. {
  4540. m_bNoTerroristsDamaged = false;
  4541. }
  4542. // set the first blood if this is the first and the victim is on a different team then the player
  4543. if ( m_pFirstBlood == NULL && pCSScorer != player && pCSScorer->GetTeamNumber() != player ->GetTeamNumber() )
  4544. {
  4545. m_pFirstBlood = pCSScorer;
  4546. m_firstBloodTime = gpGlobals->curtime - m_fRoundStartTime;
  4547. }
  4548. }
  4549. }
  4550. //=============================================================================
  4551. // HPE_END
  4552. //=============================================================================
  4553. #endif
  4554. bool CCSGameRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer )
  4555. {
  4556. #ifdef GAME_DLL
  4557. if( pPlayer )
  4558. {
  4559. int iPlayerTeam = pPlayer->GetTeamNumber();
  4560. if( ( iPlayerTeam == TEAM_CT ) || ( iPlayerTeam == TEAM_TERRORIST ) )
  4561. return false;
  4562. }
  4563. #else
  4564. int iLocalPlayerTeam = GetLocalPlayerTeam();
  4565. if( ( iLocalPlayerTeam == TEAM_CT ) || ( iLocalPlayerTeam == TEAM_TERRORIST ) )
  4566. return false;
  4567. #endif
  4568. return true;
  4569. }
  4570. #ifdef GAME_DLL
  4571. struct convar_tags_t
  4572. {
  4573. const char *pszConVar;
  4574. const char *pszTag;
  4575. };
  4576. // The list of convars that automatically turn on tags when they're changed.
  4577. // Convars in this list need to have the FCVAR_NOTIFY flag set on them, so the
  4578. // tags are recalculated and uploaded to the master server when the convar is changed.
  4579. convar_tags_t convars_to_check_for_tags[] =
  4580. {
  4581. { "mp_friendlyfire", "friendlyfire" },
  4582. { "bot_quota", "bots" },
  4583. { "sv_nostats", "nostats" },
  4584. { "mp_startmoney", "startmoney" },
  4585. { "sv_allowminmodels", "nominmodels" },
  4586. { "sv_enablebunnyhopping", "bunnyhopping" },
  4587. { "sv_competitive_minspec", "compspec" },
  4588. { "mp_holiday_nogifts", "nogifts" },
  4589. };
  4590. //-----------------------------------------------------------------------------
  4591. // Purpose: Engine asks for the list of convars that should tag the server
  4592. //-----------------------------------------------------------------------------
  4593. void CCSGameRules::GetTaggedConVarList( KeyValues *pCvarTagList )
  4594. {
  4595. BaseClass::GetTaggedConVarList( pCvarTagList );
  4596. for ( int i = 0; i < ARRAYSIZE(convars_to_check_for_tags); i++ )
  4597. {
  4598. KeyValues *pKV = new KeyValues( "tag" );
  4599. pKV->SetString( "convar", convars_to_check_for_tags[i].pszConVar );
  4600. pKV->SetString( "tag", convars_to_check_for_tags[i].pszTag );
  4601. pCvarTagList->AddSubKey( pKV );
  4602. }
  4603. }
  4604. #endif
  4605. int CCSGameRules::GetBlackMarketPriceForWeapon( int iWeaponID )
  4606. {
  4607. if ( m_pPrices == NULL )
  4608. {
  4609. GetBlackMarketPriceList();
  4610. }
  4611. if ( m_pPrices )
  4612. return m_pPrices->iCurrentPrice[iWeaponID];
  4613. else
  4614. return 0;
  4615. }
  4616. int CCSGameRules::GetBlackMarketPreviousPriceForWeapon( int iWeaponID )
  4617. {
  4618. if ( m_pPrices == NULL )
  4619. {
  4620. GetBlackMarketPriceList();
  4621. }
  4622. if ( m_pPrices )
  4623. return m_pPrices->iPreviousPrice[iWeaponID];
  4624. else
  4625. return 0;
  4626. }
  4627. const weeklyprice_t *CCSGameRules::GetBlackMarketPriceList( void )
  4628. {
  4629. if ( m_StringTableBlackMarket == NULL )
  4630. {
  4631. m_StringTableBlackMarket = networkstringtable->FindTable( CS_GAMERULES_BLACKMARKET_TABLE_NAME);
  4632. }
  4633. if ( m_pPrices == NULL )
  4634. {
  4635. int iSize = 0;
  4636. INetworkStringTable *pTable = m_StringTableBlackMarket;
  4637. if ( pTable && pTable->GetNumStrings() > 0 )
  4638. {
  4639. m_pPrices = (const weeklyprice_t *)pTable->GetStringUserData( 0, &iSize );
  4640. }
  4641. }
  4642. if ( m_pPrices )
  4643. {
  4644. PrepareEquipmentInfo();
  4645. }
  4646. return m_pPrices;
  4647. }
  4648. void CCSGameRules::SetBlackMarketPrices( bool bSetDefaults )
  4649. {
  4650. for ( int i = 1; i < WEAPON_MAX; i++ )
  4651. {
  4652. if ( i == WEAPON_SHIELDGUN )
  4653. continue;
  4654. CCSWeaponInfo *info = GetWeaponInfo( (CSWeaponID)i );
  4655. if ( info == NULL )
  4656. continue;
  4657. if ( bSetDefaults == false )
  4658. {
  4659. info->SetWeaponPrice( GetBlackMarketPriceForWeapon( i ) );
  4660. info->SetPreviousPrice( GetBlackMarketPreviousPriceForWeapon( i ) );
  4661. }
  4662. else
  4663. {
  4664. info->SetWeaponPrice( info->GetDefaultPrice() );
  4665. }
  4666. }
  4667. }
  4668. #ifdef CLIENT_DLL
  4669. CCSGameRules::CCSGameRules()
  4670. {
  4671. CSGameRules()->m_StringTableBlackMarket = NULL;
  4672. m_pPrices = NULL;
  4673. m_bBlackMarket = false;
  4674. }
  4675. void TestTable( void )
  4676. {
  4677. CSGameRules()->m_StringTableBlackMarket = networkstringtable->FindTable( CS_GAMERULES_BLACKMARKET_TABLE_NAME);
  4678. if ( CSGameRules()->m_StringTableBlackMarket == NULL )
  4679. return;
  4680. int iIndex = CSGameRules()->m_StringTableBlackMarket->FindStringIndex( "blackmarket_prices" );
  4681. int iSize = 0;
  4682. const weeklyprice_t *pPrices = NULL;
  4683. pPrices = (const weeklyprice_t *)(CSGameRules()->m_StringTableBlackMarket)->GetStringUserData( iIndex, &iSize );
  4684. }
  4685. #ifdef DEBUG
  4686. ConCommand cs_testtable( "cs_testtable", TestTable );
  4687. #endif
  4688. //-----------------------------------------------------------------------------
  4689. // Enforce certain values on the specified convar.
  4690. //-----------------------------------------------------------------------------
  4691. void EnforceCompetitiveCVar( const char *szCvarName, float fMinValue, float fMaxValue = FLT_MAX, int iArgs = 0, ... )
  4692. {
  4693. // Doing this check first because OK values might be outside the min/max range
  4694. ConVarRef competitiveConvar(szCvarName);
  4695. float fValue = competitiveConvar.GetFloat();
  4696. va_list vl;
  4697. va_start(vl, iArgs);
  4698. for( int i=0; i< iArgs; ++i )
  4699. {
  4700. if( (int)fValue == (int)va_arg(vl,double) )
  4701. return;
  4702. }
  4703. va_end(vl);
  4704. if( fValue < fMinValue || fValue > fMaxValue )
  4705. {
  4706. float fNewValue = MAX( MIN( fValue, fMaxValue ), fMinValue );
  4707. competitiveConvar.SetValue( fNewValue );
  4708. DevMsg( "Convar %s enforced by server (see sv_competitive_minspec.) Set to %2f.\n", szCvarName, fNewValue );
  4709. }
  4710. }
  4711. //-----------------------------------------------------------------------------
  4712. // An interface used by ENABLE_COMPETITIVE_CONVAR macro that lets the classes
  4713. // defined in the macro to be stored and acted on.
  4714. //-----------------------------------------------------------------------------
  4715. class ICompetitiveConvar
  4716. {
  4717. public:
  4718. // It is a best practice to always have a virtual destructor in an interface
  4719. // class. Otherwise if the derived classes have destructors they will not be
  4720. // called.
  4721. virtual ~ICompetitiveConvar() {}
  4722. virtual void BackupConvar() = 0;
  4723. virtual void EnforceRestrictions() = 0;
  4724. virtual void RestoreOriginalValue() = 0;
  4725. virtual void InstallChangeCallback() = 0;
  4726. };
  4727. //-----------------------------------------------------------------------------
  4728. // A manager for all enforced competitive convars.
  4729. //-----------------------------------------------------------------------------
  4730. class CCompetitiveCvarManager : public CAutoGameSystem
  4731. {
  4732. public:
  4733. typedef CUtlVector<ICompetitiveConvar*> CompetitiveConvarList_t;
  4734. static void AddConvarToList( ICompetitiveConvar* pCVar )
  4735. {
  4736. GetConvarList()->AddToTail( pCVar );
  4737. }
  4738. static void BackupAllConvars()
  4739. {
  4740. FOR_EACH_VEC( *GetConvarList(), i )
  4741. {
  4742. (*GetConvarList())[i]->BackupConvar();
  4743. }
  4744. }
  4745. static void EnforceRestrictionsOnAllConvars()
  4746. {
  4747. FOR_EACH_VEC( *GetConvarList(), i )
  4748. {
  4749. (*GetConvarList())[i]->EnforceRestrictions();
  4750. }
  4751. }
  4752. static void RestoreAllOriginalValues()
  4753. {
  4754. FOR_EACH_VEC( *GetConvarList(), i )
  4755. {
  4756. (*GetConvarList())[i]->RestoreOriginalValue();
  4757. }
  4758. }
  4759. static CompetitiveConvarList_t* GetConvarList()
  4760. {
  4761. if( !s_pCompetitiveConvars )
  4762. {
  4763. s_pCompetitiveConvars = new CompetitiveConvarList_t();
  4764. }
  4765. return s_pCompetitiveConvars;
  4766. }
  4767. static KeyValues* GetConVarBackupKV()
  4768. {
  4769. if( !s_pConVarBackups )
  4770. {
  4771. s_pConVarBackups = new KeyValues("ConVarBackups");
  4772. }
  4773. return s_pConVarBackups;
  4774. }
  4775. virtual bool Init()
  4776. {
  4777. FOR_EACH_VEC( *GetConvarList(), i )
  4778. {
  4779. (*GetConvarList())[i]->InstallChangeCallback();
  4780. }
  4781. return true;
  4782. }
  4783. virtual void Shutdown()
  4784. {
  4785. FOR_EACH_VEC( *GetConvarList(), i )
  4786. {
  4787. delete (*GetConvarList())[i];
  4788. }
  4789. delete s_pCompetitiveConvars;
  4790. s_pCompetitiveConvars = null;
  4791. s_pConVarBackups->deleteThis();
  4792. s_pConVarBackups = null;
  4793. }
  4794. private:
  4795. static CompetitiveConvarList_t* s_pCompetitiveConvars;
  4796. static KeyValues* s_pConVarBackups;
  4797. };
  4798. static CCompetitiveCvarManager *s_pCompetitiveCvarManager = new CCompetitiveCvarManager();
  4799. CCompetitiveCvarManager::CompetitiveConvarList_t* CCompetitiveCvarManager::s_pCompetitiveConvars = null;
  4800. KeyValues* CCompetitiveCvarManager::s_pConVarBackups = null;
  4801. //-----------------------------------------------------------------------------
  4802. // Macro to define restrictions on convars with "sv_competitive_minspec 1"
  4803. // Usage: ENABLE_COMPETITIVE_CONVAR( convarName, minValue, maxValue, optionalValues, opVal1, opVal2, ...
  4804. //-----------------------------------------------------------------------------
  4805. #define ENABLE_COMPETITIVE_CONVAR( convarName, ... ) \
  4806. class CCompetitiveMinspecConvar##convarName : public ICompetitiveConvar { \
  4807. public: \
  4808. CCompetitiveMinspecConvar##convarName(){ CCompetitiveCvarManager::AddConvarToList(this);} \
  4809. static void on_changed_##convarName( IConVar *var, const char *pOldValue, float flOldValue ){ \
  4810. if( sv_competitive_minspec.GetBool() ) { \
  4811. EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); }\
  4812. else {\
  4813. CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } } \
  4814. virtual void BackupConvar() { CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } \
  4815. virtual void EnforceRestrictions() { EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); } \
  4816. virtual void RestoreOriginalValue() { ConVarRef(#convarName).SetValue(CCompetitiveCvarManager::GetConVarBackupKV()->GetFloat( #convarName ) ); } \
  4817. virtual void InstallChangeCallback() { static_cast<ConVar*>(ConVarRef( #convarName ).GetLinkedConVar())->InstallChangeCallback( CCompetitiveMinspecConvar##convarName::on_changed_##convarName); } \
  4818. }; \
  4819. static CCompetitiveMinspecConvar##convarName *s_pCompetitiveConvar##convarName = new CCompetitiveMinspecConvar##convarName();
  4820. //-----------------------------------------------------------------------------
  4821. // Callback function for sv_competitive_minspec convar value change.
  4822. //-----------------------------------------------------------------------------
  4823. void sv_competitive_minspec_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
  4824. {
  4825. ConVar *pCvar = static_cast<ConVar*>(var);
  4826. if( pCvar->GetBool() == true && (bool)flOldValue == false )
  4827. {
  4828. // Backup the values of each cvar and enforce new ones
  4829. CCompetitiveCvarManager::BackupAllConvars();
  4830. CCompetitiveCvarManager::EnforceRestrictionsOnAllConvars();
  4831. }
  4832. else if( pCvar->GetBool() == false && (bool)flOldValue == true )
  4833. {
  4834. // If sv_competitive_minspec is disabled, restore old client values
  4835. CCompetitiveCvarManager::RestoreAllOriginalValues();
  4836. }
  4837. }
  4838. #endif
  4839. static ConVar sv_competitive_minspec( "sv_competitive_minspec",
  4840. "0",
  4841. FCVAR_REPLICATED | FCVAR_NOTIFY,
  4842. "Enable to force certain client convars to minimum/maximum values to help prevent competitive advantages:\n \
  4843. r_drawdetailprops = 1\n \
  4844. r_staticprop_lod = minimum -1 maximum 3\n \
  4845. fps_max minimum 59 (0 works too)\n \
  4846. cl_detailfade minimum 400\n \
  4847. cl_detaildist minimum 1200\n \
  4848. cl_interp_ratio = minimum 1 maximum 2\n \
  4849. cl_interp = minimum 0 maximum 0.031\n \
  4850. "
  4851. #ifdef CLIENT_DLL
  4852. ,sv_competitive_minspec_changed_f
  4853. #endif
  4854. );
  4855. #ifdef CLIENT_DLL
  4856. ENABLE_COMPETITIVE_CONVAR( r_drawdetailprops, true, true ); // force r_drawdetailprops on
  4857. ENABLE_COMPETITIVE_CONVAR( r_staticprop_lod, -1, 3 ); // force r_staticprop_lod from -1 to 3
  4858. ENABLE_COMPETITIVE_CONVAR( fps_max, 59, FLT_MAX, 1, 0 ); // force fps_max above 59. One additional value (0) works
  4859. ENABLE_COMPETITIVE_CONVAR( cl_detailfade, 400 ); // force cl_detailfade above 400.
  4860. ENABLE_COMPETITIVE_CONVAR( cl_detaildist, 1200 ); // force cl_detaildist above 1200.
  4861. ENABLE_COMPETITIVE_CONVAR( cl_interp_ratio, 1, 2 ); // force cl_interp_ratio from 1 to 2
  4862. ENABLE_COMPETITIVE_CONVAR( cl_interp, 0, 0.031 ); // force cl_interp from 0.0152 to 0.031
  4863. // Stubs for replay client code
  4864. const char *GetMapDisplayName( const char *pMapName )
  4865. {
  4866. return pMapName;
  4867. }
  4868. bool IsTakingAFreezecamScreenshot()
  4869. {
  4870. return false;
  4871. }
  4872. #endif