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

18555 lines
633 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The TF Game rules
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #ifdef POSIX
  8. //
  9. // Crazy compiler problems fix -- deque must be included first before any of the Valve game headers, which introduce
  10. // macro defines that completely break STL compile process. STL include dependencies happen as part of RSA crypto
  11. // library dependencies included at the bottom of this file.
  12. //
  13. #include <deque>
  14. #endif
  15. #include "cbase.h"
  16. #include "cs_gamerules.h"
  17. #include "cs_ammodef.h"
  18. #include "weapon_csbase.h"
  19. #include "basecsgrenade_projectile.h"
  20. #include "cs_shareddefs.h"
  21. #include "keyvalues.h"
  22. #include "cs_achievement_constants.h"
  23. #include "iachievementmgr.h"
  24. #include "matchmaking/imatchframework.h"
  25. #include "inputsystem/iinputsystem.h"
  26. #include "platforminputdevice.h"
  27. #include "engine/inetsupport.h"
  28. #include "usermessages.h"
  29. #include "hegrenade_projectile.h"
  30. #ifndef CLIENT_DLL
  31. #include "Effects/inferno.h"
  32. #endif
  33. #include "econ_item_view_helpers.h"
  34. #ifdef CLIENT_DLL
  35. #include "networkstringtable_clientdll.h"
  36. #include "c_cs_player.h"
  37. #include "fmtstr.h"
  38. #include "vgui/ILocalize.h" // temp - needed for GetFriendlyMapName()
  39. #include "c_team.h"
  40. #include "weapon_selection.h"
  41. #include "hud_macros.h"
  42. #include "c_cs_playerresource.h"
  43. #else
  44. #include "baseentity.h"
  45. #include "vote_controller.h"
  46. #include "cs_voteissues.h"
  47. #include "bot.h"
  48. #include "utldict.h"
  49. #include "cs_player.h"
  50. #include "cs_team.h"
  51. #include "cs_gamerules.h"
  52. #include "voice_gamemgr.h"
  53. #include "igamesystem.h"
  54. #include "weapon_c4.h"
  55. #include "mapinfo.h"
  56. #include "shake.h"
  57. #include "mapentities.h"
  58. #include "game.h"
  59. #include "cs_simple_hostage.h"
  60. #include "cs_gameinterface.h"
  61. #include "player_resource.h"
  62. #include "info_view_parameters.h"
  63. #include "cs_bot_manager.h"
  64. #include "cs_bot.h"
  65. #include "eventqueue.h"
  66. #include "fmtstr.h"
  67. #include "teamplayroundbased_gamerules.h"
  68. #include "gameweaponmanager.h"
  69. #include "cs_gamestats.h"
  70. #include "cs_urlretrieveprices.h"
  71. #include "networkstringtable_gamedll.h"
  72. #include "func_bomb_target.h"
  73. #include "func_hostage_rescue.h"
  74. #include "dedicated_server_ugc_manager.h"
  75. #include "vstdlib/vstrtools.h"
  76. #include "platforminputdevice.h"
  77. #include "cs_entity_spotting.h"
  78. #include "props.h"
  79. #include "hltvdirector.h"
  80. #include "econ_gcmessages.h"
  81. #include "env_cascade_light.h"
  82. #include "world.h"
  83. #include "items.h"
  84. #include "Effects/chicken.h"
  85. #endif
  86. #include "gametypes.h"
  87. // memdbgon must be the last include file in a .cpp file!!!
  88. #include "tier0/memdbgon.h"
  89. #ifndef CLIENT_DLL
  90. #define CS_GAME_STATS_UPDATE 79200 //22 hours
  91. #define CS_GAME_STATS_UPDATE_PERIOD 7200 // 2 hours
  92. #define ROUND_END_WARNING_TIME 10.0f
  93. static const float MAX_TIME_TO_WAIT_BEFORE_ENTERING = 5.0f;
  94. // # seconds to delay before displaying autobalance text to clients
  95. static const float AUTOBALANCE_TEXT_DELAY = 3.0f;
  96. extern IUploadGameStats *gamestatsuploader;
  97. extern bool Commentary_IsCommentaryEntity( CBaseEntity *pEntity );
  98. ConVar spec_replay_round_delay( "spec_replay_round_delay", "0", FCVAR_RELEASE, "Round can be delayed by this much due to someone watching a replay; must be at least 3-4 seconds, otherwise the last replay will always be interrupted by round start, assuming normal pause between round_end and round_start events (7 seconds) and freezecam delay (2 seconds) and 7.4 second full replay (5.4 second pre-death and ~2 seconds post-death) and replay in/out switching (up to a second)" );
  99. #endif // !CLIENT_DLL
  100. const float g_flWarmupToFreezetimeDelay = 4.0f;
  101. ConVar sv_server_graphic1( "sv_server_graphic1", "", FCVAR_REPLICATED | FCVAR_RELEASE, "A 360x60 (<16kb) image file in /csgo/ that will be displayed to spectators." );
  102. ConVar sv_server_graphic2( "sv_server_graphic2", "", FCVAR_REPLICATED | FCVAR_RELEASE, "A 220x45 (<16kb) image file in /csgo/ that will be displayed to spectators." );
  103. ConVar sv_disable_observer_interpolation( "sv_disable_observer_interpolation", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Disallow interpolating between observer targets on this server." );
  104. ConVar sv_reward_drop_delay( "sv_reward_drop_delay", "3.0", FCVAR_REPLICATED, "Delay between the end match scoreboard being shown and the beginning of item drops." );
  105. // to define which holiday it is
  106. ConVar sv_holiday_mode( "sv_holiday_mode", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "0 = OFF, 1 = Halloween, 2 = Winter" );
  107. ConVar sv_teamid_overhead_always_prohibit( "sv_teamid_overhead_always_prohibit", "0", FCVAR_RELEASE | FCVAR_REPLICATED | FCVAR_NOTIFY, "Determines whether cl_teamid_overhead_always is prohibited." );
  108. ConVar sv_show_team_equipment_prohibit( "sv_show_team_equipment_prohibit", "0", FCVAR_RELEASE | FCVAR_REPLICATED | FCVAR_NOTIFY, "Determines whether +cl_show_team_equipment is prohibited." );
  109. #if defined( GAME_DLL )
  110. ConVar sv_kick_players_with_cooldown( "sv_kick_players_with_cooldown", "1", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED,
  111. "(0: do not kick on insecure servers; 1: kick players with Untrusted status or convicted by Overwatch; 2: kick players with any cooldown)" );
  112. ConVar sv_matchend_drops_enabled( "sv_matchend_drops_enabled", "1", FCVAR_RELEASE | FCVAR_GAMEDLL,
  113. "Rewards gameplay time is always accumulated for players, but drops at the end of the match can be prevented" );
  114. ConVar sv_buy_status_override( "sv_buy_status_override", "-1", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED, "Override for buy status map info. 0 = everyone can buy, 1 = ct only, 2 = t only 3 = nobody" );
  115. ConVar sv_ct_spawn_on_bombsite( "sv_ct_spawn_on_bombsite", "-1", FCVAR_RELEASE | FCVAR_GAMEDLL, "Force cts to spawn on a bombsite" );
  116. ConVar sv_auto_adjust_bot_difficulty("sv_auto_adjust_bot_difficulty", "1", FCVAR_RELEASE | FCVAR_GAMEDLL, "Adjust the difficulty of bots each round based on contribution score." );
  117. ConVar sv_bots_get_easier_each_win("sv_bots_get_easier_each_win", "0", FCVAR_RELEASE | FCVAR_GAMEDLL, "If > 0, some # of bots will lower thier difficulty each time they win. The argument defines how many will lower their difficulty each time." );
  118. ConVar sv_bots_get_harder_after_each_wave( "sv_bots_get_harder_after_each_wave", "0", FCVAR_RELEASE | FCVAR_GAMEDLL, "If > 0, some # of bots will raise thier difficulty each time CTs beat a Guardian wave. The argument defines how many will raise their difficulty each time" );
  119. ConVar sv_bots_force_rebuy_every_round( "sv_bots_force_rebuy_every_round", "0", FCVAR_RELEASE | FCVAR_GAMEDLL, "If set, this strips the bots of their weapons every round and forces them to rebuy." );
  120. #endif
  121. ConVar mp_team_timeout_time( "mp_team_timeout_time", "60", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED, "Duration of each timeout." );
  122. ConVar mp_team_timeout_max( "mp_team_timeout_max", "1", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED, "Number of timeouts each team gets per match." );
  123. void MaxAllowedNetGraphCallback( IConVar *var, const char *pOldValue, float flOldValue );
  124. ConVar sv_max_allowed_net_graph( "sv_max_allowed_net_graph", "1", FCVAR_NOTIFY | FCVAR_RELEASE | FCVAR_REPLICATED, "Determines max allowed net_graph value for clients.", MaxAllowedNetGraphCallback );
  125. void MaxAllowedNetGraphCallback( IConVar *var, const char *pOldValue, float flOldValue )
  126. {
  127. #ifdef CLIENT_DLL
  128. extern ConVar net_graph;
  129. if ( net_graph.GetInt() > sv_max_allowed_net_graph.GetInt() )
  130. {
  131. net_graph.SetValue( sv_max_allowed_net_graph.GetInt() );
  132. Msg( "Server does not allow net_graph values above %d\n", sv_max_allowed_net_graph.GetInt() );
  133. }
  134. #endif
  135. }
  136. #define SV_QMM_MIN_PLAYERS_FOR_CANCEL_MATCH 0
  137. extern ConVar mp_maxrounds;
  138. extern ConVar mp_match_restart_delay;
  139. ConVar sv_disable_show_team_select_menu( "sv_disable_show_team_select_menu", "0", FCVAR_REPLICATED|FCVAR_RELEASE, "Prevent the team select menu from showing." );
  140. ConVar sv_disable_motd( "sv_disable_motd", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prevent the motd from showing." );
  141. #ifdef CLIENT_DLL
  142. CON_COMMAND_F( print_mapgroup, "Prints the current mapgroup and the contained maps", FCVAR_RELEASE )
  143. #else
  144. CON_COMMAND_F( print_mapgroup_sv, "Prints the current mapgroup and the contained maps", FCVAR_RELEASE )
  145. #endif
  146. {
  147. #if defined ( CLIENT_DLL )
  148. const char* szMapGroup = engine->GetMapGroupName();
  149. #else
  150. const char* szMapGroup = STRING( gpGlobals->mapGroupName );
  151. #endif
  152. const CUtlStringList *pMaps = g_pGameTypes->GetMapGroupMapList( szMapGroup );
  153. Msg( "Map group: %s\n", szMapGroup );
  154. if ( pMaps )
  155. {
  156. FOR_EACH_VEC( *pMaps, i )
  157. {
  158. const char* szMap = (*pMaps)[i];
  159. Msg( " %s\n", szMap );
  160. }
  161. }
  162. else
  163. {
  164. Msg( "No maps in mapgroup map list!\n");
  165. }
  166. }
  167. const float cInitialRestartRoundTime = 0.0f;
  168. /**
  169. * Player hull & eye position for standing, ducking, etc. This version has a taller
  170. * player height, but goldsrc-compatible collision bounds.
  171. */
  172. static CViewVectors g_CSViewVectors(
  173. Vector( 0, 0, 64 ), // eye position
  174. Vector(-16, -16, 0 ), // hull min
  175. Vector( 16, 16, 72 ), // hull max
  176. Vector(-16, -16, 0 ), // duck hull min
  177. Vector( 16, 16, 54 ), // duck hull max
  178. Vector( 0, 0, 46 ), // duck view
  179. Vector(-10, -10, -10 ), // observer hull min
  180. Vector( 10, 10, 10 ), // observer hull max
  181. Vector( 0, 0, 14 ) // dead view height
  182. );
  183. #ifndef CLIENT_DLL
  184. extern ConVar spec_replay_bot;
  185. BEGIN_DATADESC( SpawnPoint )
  186. // Keyfields
  187. DEFINE_KEYFIELD( m_iPriority, FIELD_INTEGER, "priority" ),
  188. DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ),
  189. DEFINE_INPUTFUNC( FIELD_VOID, "SetEnabled", InputSetEnabled ),
  190. DEFINE_INPUTFUNC( FIELD_VOID, "SetDisabled", InputSetDisabled ),
  191. DEFINE_INPUTFUNC( FIELD_VOID, "ToggleEnabled", InputToggleEnabled ),
  192. END_DATADESC()
  193. LINK_ENTITY_TO_CLASS( info_player_terrorist, SpawnPoint );
  194. LINK_ENTITY_TO_CLASS( info_player_counterterrorist, SpawnPoint );
  195. LINK_ENTITY_TO_CLASS( info_player_logo, CPointEntity );
  196. LINK_ENTITY_TO_CLASS( info_deathmatch_spawn, SpawnPoint );
  197. LINK_ENTITY_TO_CLASS( info_armsrace_counterterrorist, SpawnPoint );
  198. LINK_ENTITY_TO_CLASS( info_armsrace_terrorist, SpawnPoint );
  199. template< typename TIssue >
  200. void NewTeamIssue()
  201. {
  202. new TIssue( g_voteControllerT );
  203. new TIssue( g_voteControllerCT );
  204. }
  205. template< typename TIssue >
  206. void NewGlobalIssue()
  207. {
  208. new TIssue( g_voteControllerGlobal );
  209. }
  210. SpawnPoint::SpawnPoint() : m_bEnabled( true ), m_nType( 0 )
  211. {
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. //-----------------------------------------------------------------------------
  216. void SpawnPoint::Spawn( void )
  217. {
  218. BaseClass::Spawn();
  219. if ( CSGameRules() )
  220. CSGameRules()->AddSpawnPointToMasterList( this );
  221. }
  222. void SpawnPoint::InputSetEnabled( inputdata_t &inputdata )
  223. {
  224. SetSpawnEnabled( true );
  225. }
  226. void SpawnPoint::InputSetDisabled( inputdata_t &inputdata )
  227. {
  228. SetSpawnEnabled( false );
  229. }
  230. void SpawnPoint::InputToggleEnabled( inputdata_t &inputdata )
  231. {
  232. m_bEnabled = !m_bEnabled;
  233. if ( CSGameRules() &&
  234. !CSGameRules()->IsPlayingCoopMission() )
  235. {
  236. CSGameRules()->RefreshCurrentSpawnPointLists();
  237. }
  238. }
  239. void SpawnPoint::SetSpawnEnabled( bool bEnabled )
  240. {
  241. bool bChanged = (m_bEnabled != bEnabled);
  242. m_bEnabled = bEnabled;
  243. if ( CSGameRules() && bChanged &&
  244. !CSGameRules()->IsPlayingCoopMission() )
  245. {
  246. CSGameRules()->RefreshCurrentSpawnPointLists();
  247. }
  248. }
  249. LINK_ENTITY_TO_CLASS( info_enemy_terrorist_spawn, SpawnPointCoopEnemy );
  250. BEGIN_DATADESC( SpawnPointCoopEnemy )
  251. DEFINE_KEYFIELD( m_szWeaponsToGive, FIELD_STRING, "weapons_to_give" ),
  252. DEFINE_KEYFIELD( m_szPlayerModelToUse, FIELD_STRING, "model_to_use" ),
  253. DEFINE_KEYFIELD( m_nArmorToSpawnWith, FIELD_INTEGER, "armor_to_give" ),
  254. DEFINE_KEYFIELD( m_nDefaultBehavior, FIELD_INTEGER, "default_behavior" ),
  255. DEFINE_KEYFIELD( m_nBotDifficulty, FIELD_INTEGER, "bot_difficulty" ),
  256. DEFINE_KEYFIELD( m_bIsAgressive, FIELD_BOOLEAN, "is_agressive" ),
  257. DEFINE_KEYFIELD( m_bStartAsleep, FIELD_BOOLEAN, "start_asleep" ),
  258. DEFINE_KEYFIELD( m_flHideRadius, FIELD_FLOAT, "hide_radius" ),
  259. END_DATADESC()
  260. SpawnPointCoopEnemy::SpawnPointCoopEnemy( void )
  261. {
  262. //m_szWeaponsToGive = NULL_STRING;
  263. Assert( CSGameRules()->IsPlayingCoopMission() );
  264. m_pMyArea = NULL;
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose:
  268. //-----------------------------------------------------------------------------
  269. void SpawnPointCoopEnemy::Spawn( void )
  270. {
  271. BaseClass::Spawn();
  272. Precache();
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose:
  276. //-----------------------------------------------------------------------------
  277. void SpawnPointCoopEnemy::Precache( void )
  278. {
  279. if ( V_stricmp( GetPlayerModelToUse(), "" ) != 0 )
  280. {
  281. PrecacheModel( GetPlayerModelToUse() );
  282. }
  283. BaseClass::Precache();
  284. }
  285. CNavArea * SpawnPointCoopEnemy::FindNearestArea( void )
  286. {
  287. // These don't move so only do the expensive find the first time
  288. if ( !m_pMyArea )
  289. m_pMyArea = TheNavMesh->GetNearestNavArea( this );
  290. return m_pMyArea;
  291. }
  292. class CPointGiveAmmo : public CPointEntity
  293. {
  294. DECLARE_CLASS( CPointGiveAmmo, CPointEntity );
  295. public:
  296. void Spawn( void );
  297. void Precache( void );
  298. // Input handlers
  299. void InputGiveAmmo( inputdata_t &inputdata );
  300. DECLARE_DATADESC();
  301. // int m_nDamage;
  302. // int m_bitsDamageType;
  303. // float m_flRadius;
  304. // float m_flDelay;
  305. // string_t m_strTarget;
  306. EHANDLE m_pActivator;
  307. };
  308. BEGIN_DATADESC( CPointGiveAmmo )
  309. // DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "DamageRadius" ),
  310. // DEFINE_KEYFIELD( m_nDamage, FIELD_INTEGER, "Damage" ),
  311. // DEFINE_KEYFIELD( m_flDelay, FIELD_FLOAT, "DamageDelay" ),
  312. // DEFINE_KEYFIELD( m_bitsDamageType, FIELD_INTEGER, "DamageType" ),
  313. // DEFINE_KEYFIELD( m_strTarget, FIELD_STRING, "DamageTarget" ),
  314. // Function Pointers
  315. // Inputs
  316. DEFINE_INPUTFUNC( FIELD_VOID, "GiveAmmo", InputGiveAmmo ),
  317. DEFINE_FIELD( m_pActivator, FIELD_EHANDLE ),
  318. END_DATADESC()
  319. LINK_ENTITY_TO_CLASS( point_give_ammo, CPointGiveAmmo );
  320. //-----------------------------------------------------------------------------
  321. // Purpose:
  322. //-----------------------------------------------------------------------------
  323. void CPointGiveAmmo::Spawn( void )
  324. {
  325. SetThink( NULL );
  326. SetUse( NULL );
  327. m_pActivator = NULL;
  328. Precache();
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Purpose:
  332. //-----------------------------------------------------------------------------
  333. void CPointGiveAmmo::Precache( void )
  334. {
  335. BaseClass::Precache();
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose: Input handler for instantaneously hurting whatever is near us.
  339. //-----------------------------------------------------------------------------
  340. void CPointGiveAmmo::InputGiveAmmo( inputdata_t &data )
  341. {
  342. m_pActivator = data.pActivator;
  343. CBasePlayer *pPlayer = dynamic_cast< CBasePlayer* >( m_pActivator.Get() );
  344. if ( pPlayer )
  345. {
  346. for ( int i = 0; i < 2; ++i )
  347. {
  348. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pPlayer->Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  349. if ( i == 1 )
  350. pWeapon = dynamic_cast< CWeaponCSBase* >( pPlayer->Weapon_GetSlot( WEAPON_SLOT_PISTOL ) );
  351. if ( pWeapon )
  352. {
  353. if ( pWeapon->UsesPrimaryAmmo() )
  354. {
  355. int ammoIndex = pWeapon->GetPrimaryAmmoType();
  356. if ( ammoIndex != -1 )
  357. {
  358. int giveAmount;
  359. giveAmount = GetAmmoDef()->MaxCarry( ammoIndex, pPlayer );
  360. pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex( ammoIndex )->pName );
  361. }
  362. }
  363. if ( pWeapon->UsesSecondaryAmmo() && pWeapon->HasSecondaryAmmo() )
  364. {
  365. // Give secondary ammo out, as long as the player already has some
  366. // from a presumeably natural source. This prevents players on XBox
  367. // having Combine Balls and so forth in areas of the game that
  368. // were not tested with these items.
  369. int ammoIndex = pWeapon->GetSecondaryAmmoType();
  370. if ( ammoIndex != -1 )
  371. {
  372. int giveAmount;
  373. giveAmount = GetAmmoDef()->MaxCarry( ammoIndex, pPlayer );
  374. pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex( ammoIndex )->pName );
  375. }
  376. }
  377. }
  378. }
  379. }
  380. }
  381. //-----------------------------------------------------------------------------
  382. class CCoopBonusCoin : public CDynamicProp
  383. {
  384. public:
  385. DECLARE_CLASS( CCoopBonusCoin, CDynamicProp );
  386. DECLARE_DATADESC();
  387. void CoinTouch( CBaseEntity *pOther );
  388. void CoinFadeOut( void );
  389. //void CoinThink( void );
  390. void Precache( void );
  391. void Spawn( void ) OVERRIDE;
  392. void StartFadeOut( float delay );
  393. };
  394. LINK_ENTITY_TO_CLASS( item_coop_coin, CCoopBonusCoin );
  395. PRECACHE_REGISTER( item_coop_coin );
  396. BEGIN_DATADESC( CCoopBonusCoin )
  397. // Function Pointers
  398. DEFINE_ENTITYFUNC( CoinTouch ),
  399. DEFINE_THINKFUNC( CoinFadeOut ),
  400. END_DATADESC()
  401. void CCoopBonusCoin::CoinTouch( CBaseEntity *pOther )
  402. {
  403. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( pOther );
  404. if ( !pPlayer || !CSGameRules() )
  405. return;
  406. CSGameRules()->CoopCollectBonusCoin();
  407. EmitSound( "CollectableCoin.Collect" );
  408. ResetSequence( LookupSequence( "challenge_coin_collect" ) );
  409. SetTouch( NULL );
  410. StartFadeOut( 0.125 );
  411. }
  412. // void CCoopBonusCoin::CoinThink( void )
  413. // {
  414. // StudioFrameAdvance();
  415. // }
  416. void CCoopBonusCoin::Precache( void )
  417. {
  418. PrecacheModel( "models/coop/challenge_coin.mdl" );
  419. PrecacheScriptSound( "CollectableCoin.Collect" );
  420. }
  421. void CCoopBonusCoin::Spawn( void )
  422. {
  423. Precache();
  424. SetModel( "models/coop/challenge_coin.mdl" );
  425. SetModelName( MAKE_STRING( "models/coop/challenge_coin.mdl" ) );
  426. BaseClass::Spawn();
  427. //m_bShouldGlow = true;
  428. SetMoveType( MOVETYPE_NONE );
  429. SetSolid( SOLID_BBOX );
  430. SetBlocksLOS( false );
  431. AddSolidFlags( FSOLID_TRIGGER );
  432. AddEFlags( EFL_NO_ROTORWASH_PUSH );
  433. SetCollisionGroup( COLLISION_GROUP_WEAPON );
  434. //if ( HasBloatedCollision() )
  435. //{
  436. //CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT );
  437. //}
  438. SetTouch( &CCoopBonusCoin::CoinTouch );
  439. ResetSequence( LookupSequence( "idle" ) );
  440. //SetThink( &CCoopBonusCoin::CoinThink );
  441. }
  442. void CCoopBonusCoin::StartFadeOut( float delay )
  443. {
  444. SetThink( &CCoopBonusCoin::CoinFadeOut );
  445. SetNextThink( gpGlobals->curtime + delay );
  446. SetRenderAlpha( 255 );
  447. m_nRenderMode = kRenderNormal;
  448. }
  449. //-----------------------------------------------------------------------------
  450. // Purpose: Fade out slowly
  451. //-----------------------------------------------------------------------------
  452. void CCoopBonusCoin::CoinFadeOut( void )
  453. {
  454. float dt = gpGlobals->frametime;
  455. if ( dt > 0.1f )
  456. dt = 0.1f;
  457. m_nRenderMode = kRenderTransTexture;
  458. int speed = MAX( 0.35, 256 * dt ); // fade out
  459. SetRenderAlpha( UTIL_Approach( 0, m_clrRender->a, speed ) );
  460. if ( m_clrRender->a == 0 )
  461. {
  462. UTIL_Remove( this );
  463. }
  464. else
  465. {
  466. SetNextThink( gpGlobals->curtime );
  467. }
  468. }
  469. #endif
  470. #if defined ( CLIENT_DLL )
  471. bool __MsgFunc_SendPlayerItemDrops( const CCSUsrMsg_SendPlayerItemDrops &msg );
  472. bool __MsgFunc_SendPlayerItemFound( const CCSUsrMsg_SendPlayerItemFound &msg );
  473. #endif
  474. REGISTER_GAMERULES_CLASS( CCSGameRules );
  475. BEGIN_NETWORK_TABLE_NOBASE( CCSGameRules, DT_CSGameRules )
  476. #ifdef CLIENT_DLL
  477. RecvPropBool( RECVINFO( m_bFreezePeriod ) ),
  478. RecvPropBool( RECVINFO( m_bMatchWaitingForResume ) ),
  479. RecvPropBool( RECVINFO( m_bWarmupPeriod ) ),
  480. RecvPropFloat( RECVINFO( m_fWarmupPeriodEnd ) ), // DUMMY VAR FOR DEMOS
  481. RecvPropFloat( RECVINFO( m_fWarmupPeriodStart ) ),
  482. RecvPropBool( RECVINFO( m_bTerroristTimeOutActive ) ),
  483. RecvPropBool( RECVINFO( m_bCTTimeOutActive ) ),
  484. RecvPropFloat( RECVINFO( m_flTerroristTimeOutRemaining ) ),
  485. RecvPropFloat( RECVINFO( m_flCTTimeOutRemaining ) ),
  486. RecvPropInt( RECVINFO( m_nTerroristTimeOuts ) ),
  487. RecvPropInt( RECVINFO( m_nCTTimeOuts ) ),
  488. RecvPropInt( RECVINFO( m_iRoundTime ) ),
  489. RecvPropInt( RECVINFO( m_gamePhase ) ),
  490. RecvPropInt( RECVINFO( m_totalRoundsPlayed ) ),
  491. RecvPropInt( RECVINFO( m_nOvertimePlaying ) ),
  492. RecvPropFloat( RECVINFO( m_timeUntilNextPhaseStarts ) ),
  493. RecvPropFloat( RECVINFO( m_flCMMItemDropRevealStartTime ) ),
  494. RecvPropFloat( RECVINFO( m_flCMMItemDropRevealEndTime ) ),
  495. RecvPropFloat( RECVINFO( m_fRoundStartTime ) ),
  496. RecvPropBool( RECVINFO( m_bGameRestart ) ),
  497. RecvPropFloat( RECVINFO( m_flRestartRoundTime ) ),
  498. RecvPropFloat( RECVINFO( m_flGameStartTime ) ),
  499. RecvPropInt( RECVINFO( m_iHostagesRemaining ) ),
  500. RecvPropBool( RECVINFO( m_bAnyHostageReached ) ),
  501. RecvPropBool( RECVINFO( m_bMapHasBombTarget ) ),
  502. RecvPropBool( RECVINFO( m_bMapHasRescueZone ) ),
  503. RecvPropBool( RECVINFO( m_bMapHasBuyZone ) ),
  504. RecvPropBool( RECVINFO( m_bIsQueuedMatchmaking ) ),
  505. RecvPropBool( RECVINFO( m_bIsValveDS ) ),
  506. RecvPropBool( RECVINFO( m_bIsQuestEligible ) ),
  507. RecvPropBool( RECVINFO( m_bLogoMap ) ),
  508. RecvPropInt( RECVINFO( m_iNumGunGameProgressiveWeaponsCT ) ),
  509. RecvPropInt( RECVINFO( m_iNumGunGameProgressiveWeaponsT ) ),
  510. RecvPropInt( RECVINFO( m_iSpectatorSlotCount ) ),
  511. RecvPropBool( RECVINFO( m_bBombDropped ) ),
  512. RecvPropBool( RECVINFO( m_bBombPlanted ) ),
  513. RecvPropInt( RECVINFO( m_iRoundWinStatus ) ),
  514. RecvPropInt( RECVINFO( m_eRoundWinReason ) ),
  515. RecvPropFloat( RECVINFO( m_flDMBonusStartTime ) ),
  516. RecvPropFloat( RECVINFO( m_flDMBonusTimeLength ) ),
  517. RecvPropInt( RECVINFO( m_unDMBonusWeaponLoadoutSlot ) ),
  518. RecvPropBool( RECVINFO( m_bDMBonusActive ) ),
  519. RecvPropBool( RECVINFO( m_bTCantBuy ) ),
  520. RecvPropBool( RECVINFO( m_bCTCantBuy ) ),
  521. RecvPropFloat( RECVINFO( m_flGuardianBuyUntilTime ) ),
  522. RecvPropArray3( RECVINFO_ARRAY( m_iMatchStats_RoundResults ), RecvPropInt( RECVINFO( m_iMatchStats_RoundResults[0] ) ) ),
  523. RecvPropArray3( RECVINFO_ARRAY( m_iMatchStats_PlayersAlive_T ), RecvPropInt( RECVINFO( m_iMatchStats_PlayersAlive_T[0] ) ) ),
  524. RecvPropArray3( RECVINFO_ARRAY( m_iMatchStats_PlayersAlive_CT ), RecvPropInt( RECVINFO( m_iMatchStats_PlayersAlive_CT[0] ) ) ),
  525. RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponOrderCT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponOrderCT[0] ) ) ),
  526. RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponOrderT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponOrderT[0] ) ) ),
  527. RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderCT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponKillUpgradeOrderCT[0] ) ) ),
  528. RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponKillUpgradeOrderT[0] ) ) ),
  529. RecvPropInt( RECVINFO( m_MatchDevice ) ),
  530. RecvPropBool( RECVINFO( m_bHasMatchStarted ) ),
  531. RecvPropArray3( RECVINFO_ARRAY(m_TeamRespawnWaveTimes), RecvPropFloat( RECVINFO(m_TeamRespawnWaveTimes[0]) ) ),
  532. RecvPropArray3( RECVINFO_ARRAY(m_flNextRespawnWave), RecvPropTime( RECVINFO(m_flNextRespawnWave[0]) ) ),
  533. RecvPropInt( RECVINFO( m_nNextMapInMapgroup ) ),
  534. RecvPropArray3( RECVINFO_ARRAY( m_nEndMatchMapGroupVoteOptions ), RecvPropInt( RECVINFO( m_nEndMatchMapGroupVoteOptions[0] ) ) ),
  535. RecvPropBool( RECVINFO( m_bIsDroppingItems ) ),
  536. RecvPropInt( RECVINFO( m_iActiveAssassinationTargetMissionID ) ),
  537. RecvPropFloat( RECVINFO( m_fMatchStartTime ) ),
  538. RecvPropString( RECVINFO( m_szTournamentEventName ) ),
  539. RecvPropString( RECVINFO( m_szTournamentEventStage ) ),
  540. RecvPropString( RECVINFO( m_szTournamentPredictionsTxt ) ),
  541. RecvPropInt( RECVINFO( m_nTournamentPredictionsPct ) ),
  542. RecvPropString( RECVINFO( m_szMatchStatTxt ) ),
  543. // guardian mode
  544. RecvPropInt( RECVINFO( m_nGuardianModeWaveNumber ) ),
  545. RecvPropInt( RECVINFO( m_nGuardianModeSpecialKillsRemaining ) ),
  546. RecvPropInt( RECVINFO( m_nGuardianModeSpecialWeaponNeeded ) ),
  547. // halloween
  548. RecvPropInt( RECVINFO( m_nHalloweenMaskListSeed ) ),
  549. // Gifts global info
  550. RecvPropInt( RECVINFO( m_numGlobalGiftsGiven ) ),
  551. RecvPropInt( RECVINFO( m_numGlobalGifters ) ),
  552. RecvPropInt( RECVINFO( m_numGlobalGiftsPeriodSeconds ) ),
  553. RecvPropArray3( RECVINFO_ARRAY( m_arrFeaturedGiftersAccounts ), RecvPropInt (RECVINFO( m_arrFeaturedGiftersAccounts[0] ) ) ),
  554. RecvPropArray3( RECVINFO_ARRAY( m_arrFeaturedGiftersGifts ), RecvPropInt (RECVINFO( m_arrFeaturedGiftersGifts[0] ) ) ),
  555. RecvPropArray3( RECVINFO_ARRAY( m_arrProhibitedItemIndices ), RecvPropInt( RECVINFO( m_arrProhibitedItemIndices[ 0 ] ) ) ),
  556. // Tournament Casters
  557. RecvPropInt( RECVINFO( m_numBestOfMaps ) ),
  558. RecvPropArray3( RECVINFO_ARRAY( m_arrTournamentActiveCasterAccounts ), RecvPropInt ( RECVINFO( m_arrTournamentActiveCasterAccounts[0] ), 0, CCSGameRules::RecvProxy_TournamentActiveCasterAccounts ) )
  559. #else
  560. SendPropBool( SENDINFO( m_bFreezePeriod ) ),
  561. SendPropBool( SENDINFO( m_bMatchWaitingForResume ) ),
  562. SendPropBool( SENDINFO( m_bWarmupPeriod ) ),
  563. SendPropFloat( SENDINFO( m_fWarmupPeriodEnd ) ), // DUMMY VAR FOR DEMOS
  564. SendPropFloat( SENDINFO( m_fWarmupPeriodStart ) ),
  565. SendPropBool( SENDINFO( m_bTerroristTimeOutActive ) ),
  566. SendPropBool( SENDINFO( m_bCTTimeOutActive ) ),
  567. SendPropFloat( SENDINFO( m_flTerroristTimeOutRemaining ) ),
  568. SendPropFloat( SENDINFO( m_flCTTimeOutRemaining ) ),
  569. SendPropInt( SENDINFO( m_nTerroristTimeOuts ) ),
  570. SendPropInt( SENDINFO( m_nCTTimeOuts ) ),
  571. SendPropInt( SENDINFO( m_iRoundTime ), 16 ),
  572. SendPropInt( SENDINFO( m_gamePhase ), 4, SPROP_UNSIGNED ),
  573. SendPropInt( SENDINFO( m_totalRoundsPlayed ), 16 ),
  574. SendPropInt( SENDINFO( m_nOvertimePlaying ), 16 ),
  575. SendPropFloat( SENDINFO( m_timeUntilNextPhaseStarts ), 32, SPROP_NOSCALE ),
  576. SendPropFloat( SENDINFO( m_flCMMItemDropRevealStartTime ), 32, SPROP_NOSCALE ),
  577. SendPropFloat( SENDINFO( m_flCMMItemDropRevealEndTime ), 32, SPROP_NOSCALE ),
  578. SendPropFloat( SENDINFO( m_fRoundStartTime ), 32, SPROP_NOSCALE ),
  579. SendPropFloat( SENDINFO( m_flRestartRoundTime ) ),
  580. SendPropBool( SENDINFO( m_bGameRestart ) ),
  581. SendPropFloat( SENDINFO( m_flGameStartTime ), 32, SPROP_NOSCALE ),
  582. SendPropInt( SENDINFO( m_iHostagesRemaining ), 4 ),
  583. SendPropBool( SENDINFO( m_bAnyHostageReached ) ),
  584. SendPropBool( SENDINFO( m_bMapHasBombTarget ) ),
  585. SendPropBool( SENDINFO( m_bMapHasRescueZone ) ),
  586. SendPropBool( SENDINFO( m_bMapHasBuyZone ) ),
  587. SendPropBool( SENDINFO( m_bIsQueuedMatchmaking ) ),
  588. SendPropBool( SENDINFO( m_bIsValveDS ) ),
  589. SendPropBool( SENDINFO( m_bIsQuestEligible ) ),
  590. SendPropBool( SENDINFO( m_bLogoMap ) ),
  591. SendPropInt( SENDINFO( m_iNumGunGameProgressiveWeaponsCT ) ),
  592. SendPropInt( SENDINFO( m_iNumGunGameProgressiveWeaponsT ) ),
  593. SendPropInt( SENDINFO( m_iSpectatorSlotCount ) ),
  594. SendPropBool( SENDINFO( m_bBombDropped ) ),
  595. SendPropBool( SENDINFO( m_bBombPlanted ) ),
  596. SendPropInt( SENDINFO( m_iRoundWinStatus ) ),
  597. SendPropInt( SENDINFO( m_eRoundWinReason ) ),
  598. SendPropFloat( SENDINFO( m_flDMBonusStartTime ) ),
  599. SendPropFloat( SENDINFO( m_flDMBonusTimeLength ) ),
  600. SendPropInt( SENDINFO( m_unDMBonusWeaponLoadoutSlot ) ),
  601. SendPropBool( SENDINFO( m_bDMBonusActive ) ),
  602. SendPropBool( SENDINFO( m_bTCantBuy ) ),
  603. SendPropBool( SENDINFO( m_bCTCantBuy ) ),
  604. SendPropFloat( SENDINFO( m_flGuardianBuyUntilTime ) ),
  605. SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_RoundResults ), SendPropInt (SENDINFO_ARRAY( m_iMatchStats_RoundResults ), 8, SPROP_UNSIGNED ) ),
  606. SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_PlayersAlive_T ), SendPropInt (SENDINFO_ARRAY( m_iMatchStats_PlayersAlive_T ), 6, SPROP_UNSIGNED ) ),
  607. SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_PlayersAlive_CT ), SendPropInt (SENDINFO_ARRAY( m_iMatchStats_PlayersAlive_CT ), 6, SPROP_UNSIGNED ) ),
  608. SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponOrderCT ), SendPropInt (SENDINFO_ARRAY( m_GGProgressiveWeaponOrderCT ), 0, SPROP_UNSIGNED ) ),
  609. SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponOrderT ), SendPropInt( SENDINFO_ARRAY( m_GGProgressiveWeaponOrderT ), 0, SPROP_UNSIGNED ) ),
  610. SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponKillUpgradeOrderCT ), SendPropInt( SENDINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderCT ), 0, SPROP_UNSIGNED ) ),
  611. SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponKillUpgradeOrderT ), SendPropInt( SENDINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderT ), 0, SPROP_UNSIGNED ) ),
  612. SendPropInt( SENDINFO( m_MatchDevice ) ),
  613. SendPropBool( SENDINFO( m_bHasMatchStarted ) ),
  614. SendPropArray3( SENDINFO_ARRAY3(m_TeamRespawnWaveTimes), SendPropFloat( SENDINFO_ARRAY(m_TeamRespawnWaveTimes) ) ),
  615. SendPropArray3( SENDINFO_ARRAY3(m_flNextRespawnWave), SendPropTime( SENDINFO_ARRAY(m_flNextRespawnWave) ) ),
  616. SendPropInt( SENDINFO( m_nNextMapInMapgroup ) ),
  617. SendPropArray3( SENDINFO_ARRAY3( m_nEndMatchMapGroupVoteOptions ), SendPropInt (SENDINFO_ARRAY( m_nEndMatchMapGroupVoteOptions ) ) ),
  618. SendPropBool( SENDINFO( m_bIsDroppingItems ) ),
  619. SendPropInt( SENDINFO( m_iActiveAssassinationTargetMissionID ) ),
  620. SendPropFloat( SENDINFO( m_fMatchStartTime ), 32, SPROP_NOSCALE ),
  621. SendPropString( SENDINFO( m_szTournamentEventName ) ),
  622. SendPropString( SENDINFO( m_szTournamentEventStage ) ),
  623. SendPropString( SENDINFO( m_szTournamentPredictionsTxt ) ),
  624. SendPropInt( SENDINFO( m_nTournamentPredictionsPct ) ),
  625. SendPropString( SENDINFO( m_szMatchStatTxt ) ),
  626. // guardian mode
  627. SendPropInt( SENDINFO( m_nGuardianModeWaveNumber ) ),
  628. SendPropInt( SENDINFO( m_nGuardianModeSpecialKillsRemaining ) ),
  629. SendPropInt( SENDINFO( m_nGuardianModeSpecialWeaponNeeded ) ),
  630. // Halloween
  631. SendPropInt( SENDINFO( m_nHalloweenMaskListSeed ) ),
  632. // Gifts global info
  633. SendPropInt( SENDINFO( m_numGlobalGiftsGiven ), 0, SPROP_UNSIGNED ),
  634. SendPropInt( SENDINFO( m_numGlobalGifters ), 0, SPROP_UNSIGNED ),
  635. SendPropInt( SENDINFO( m_numGlobalGiftsPeriodSeconds ), 0, SPROP_UNSIGNED ),
  636. SendPropArray3( SENDINFO_ARRAY3( m_arrFeaturedGiftersAccounts ), SendPropInt (SENDINFO_ARRAY( m_arrFeaturedGiftersAccounts ), 0, SPROP_UNSIGNED ) ),
  637. SendPropArray3( SENDINFO_ARRAY3( m_arrFeaturedGiftersGifts ), SendPropInt (SENDINFO_ARRAY( m_arrFeaturedGiftersGifts ), 0, SPROP_UNSIGNED ) ),
  638. SendPropArray3( SENDINFO_ARRAY3( m_arrProhibitedItemIndices ), SendPropInt( SENDINFO_ARRAY( m_arrProhibitedItemIndices ), 0, SPROP_UNSIGNED ) ),
  639. // Tournament Casters
  640. SendPropInt( SENDINFO( m_numBestOfMaps ), 4, SPROP_UNSIGNED ), // supporting no more than best-of-7 (1+2+4)
  641. SendPropArray3( SENDINFO_ARRAY3( m_arrTournamentActiveCasterAccounts ), SendPropInt (SENDINFO_ARRAY( m_arrTournamentActiveCasterAccounts ), 0, SPROP_UNSIGNED ) )
  642. #endif
  643. END_NETWORK_TABLE()
  644. IMPLEMENT_NETWORKCLASS_ALIASED( CSGameRulesProxy, DT_CSGameRulesProxy )
  645. LINK_ENTITY_TO_CLASS_ALIASED( cs_gamerules, CSGameRulesProxy );
  646. #ifdef GAME_DLL
  647. ConVar mp_teamname_1( "mp_teamname_1", "", FCVAR_RELEASE, "A non-empty string overrides the first team's name." );
  648. ConVar mp_teamname_2( "mp_teamname_2", "", FCVAR_RELEASE, "A non-empty string overrides the second team's name." );
  649. ConVar mp_teamflag_1( "mp_teamflag_1", "", FCVAR_RELEASE, "Enter a country's alpha 2 code to show that flag next to team 1's name in the spectator scoreboard." );
  650. ConVar mp_teamflag_2( "mp_teamflag_2", "", FCVAR_RELEASE, "Enter a country's alpha 2 code to show that flag next to team 2's name in the spectator scoreboard." );
  651. ConVar mp_teamlogo_1( "mp_teamlogo_1", "", FCVAR_RELEASE, "Enter a team's shorthand image name to display their logo. Images can be found here: 'resource/flash/econ/tournaments/teams'" );
  652. ConVar mp_teamlogo_2( "mp_teamlogo_2", "", FCVAR_RELEASE, "Enter a team's shorthand image name to display their logo. Images can be found here: 'resource/flash/econ/tournaments/teams'" );
  653. ConVar mp_teamprediction_txt( "mp_teamprediction_txt", "#SFUIHUD_Spectate_Predictions", FCVAR_RELEASE, "A value between 1 and 99 will set predictions in favor of first team." );
  654. ConVar mp_teamprediction_pct( "mp_teamprediction_pct", "0", FCVAR_RELEASE, "A value between 1 and 99 will show predictions in favor of CT team." );
  655. ConVar mp_teammatchstat_txt( "mp_teammatchstat_txt", "", FCVAR_RELEASE, "A non-empty string sets the match stat description, e.g. 'Match 2 of 3'." );
  656. ConVar mp_teammatchstat_1( "mp_teammatchstat_1", "", FCVAR_RELEASE, "A non-empty string sets first team's match stat." );
  657. ConVar mp_teammatchstat_2( "mp_teammatchstat_2", "", FCVAR_RELEASE, "A non-empty string sets second team's match stat." );
  658. ConVar mp_teamscore_1( "mp_teamscore_1", "", FCVAR_RELEASE, "A non-empty string for best-of-N maps won by the first team." );
  659. ConVar mp_teamscore_2( "mp_teamscore_2", "", FCVAR_RELEASE, "A non-empty string for best-of-N maps won by the second team." );
  660. void FnChangeCallback_mp_teamscore_max( IConVar *var, const char *pOldValue, float flOldValue )
  661. {
  662. ConVarRef ref( var );
  663. int nVal = ref.GetInt();
  664. if ( CCSGameRules *pCSR = CSGameRules() )
  665. {
  666. if ( nVal != pCSR->m_numBestOfMaps )
  667. pCSR->m_numBestOfMaps = nVal;
  668. }
  669. }
  670. ConVar mp_teamscore_max( "mp_teamscore_max", "0", FCVAR_RELEASE, "How many maps to win the series (bo3 max=2; bo5 max=3; bo7 max=4)", true, 0, true, 7, FnChangeCallback_mp_teamscore_max );
  671. ConVar mp_teammatchstat_holdtime( "mp_teammatchstat_holdtime", "5", FCVAR_RELEASE, "Decide on a match stat and hold it additionally for at least so many seconds" );
  672. ConVar mp_teammatchstat_cycletime( "mp_teammatchstat_cycletime", "45", FCVAR_RELEASE, "Cycle match stats after so many seconds" );
  673. #endif
  674. // COOP
  675. #define COOPMISSION_SCORE_MULTIPLIER_TIMELEFT 40
  676. #define COOPMISSION_SCORE_MULTIPLIER_DAMTAKEN -10
  677. #define COOPMISSION_SCORE_MULTIPLIER_ROUNDSFAILED -1000
  678. static uint32 Helper_ScoreLeaderboardData_FindEntryValue( uint32 nTag, const ::google::protobuf::RepeatedPtrField< ::ScoreLeaderboardData_Entry >&arr )
  679. {
  680. for ( int i = 0; i < arr.size(); ++ i )
  681. {
  682. if ( arr.Get( i ).tag() == nTag )
  683. return arr.Get( i ).val();
  684. }
  685. return 0;
  686. }
  687. static uint32 Helper_ScoreLeaderboardData_FindEntryValueSum( uint32 nTag, const ::google::protobuf::RepeatedPtrField< ::ScoreLeaderboardData_AccountEntries >&arr )
  688. {
  689. uint32 val = 0;
  690. for ( int i = 0; i < arr.size(); ++i )
  691. {
  692. val += Helper_ScoreLeaderboardData_FindEntryValue( nTag, arr.Get( i ).entries() );
  693. }
  694. return val;
  695. }
  696. int32 CoopScoreGetRatingEntryFromLeaderboardData( ScoreLeaderboardData &sld, bool bBonus, int nIndex, bool bAsScore )
  697. { // Note: this function should not be referencing gamerules because we can be looking at scores from Steam friends leaderboards
  698. bool bGuardian = false;
  699. bool bCoopMission = true;
  700. if ( sld.quest_id() )
  701. {
  702. const CEconQuestDefinition *pQuest = GetItemSchema()->GetQuestDefinition( sld.quest_id() );
  703. if ( !pQuest )
  704. return 0.0f;
  705. if ( !V_stricmp( pQuest->GetGameMode(), "cooperative" ) )
  706. {
  707. bGuardian = true;
  708. bCoopMission = false;
  709. }
  710. // otherwise assume coopmission
  711. }
  712. else if ( CSGameRules() )
  713. { // keeping this around for local compatibility testing with listenservers
  714. bGuardian = CSGameRules()->IsPlayingCoopGuardian();
  715. bCoopMission = CSGameRules()->IsPlayingCoopMission();
  716. // otherwise assume coopmission
  717. }
  718. else
  719. return 0.0f;
  720. int32 nResult = 0;
  721. //
  722. // Base scorechart
  723. //
  724. if ( !bBonus )
  725. {
  726. if ( bGuardian )
  727. {
  728. if ( nIndex == 0 )
  729. { // Damage to enemies ratio
  730. uint32 numDmgInflicted = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_HpDmgInflicted, sld.accountentries() );
  731. uint32 numDmgSuffered = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_HpDmgSuffered, sld.accountentries() );
  732. if ( !numDmgInflicted )
  733. nResult = 0;
  734. else if ( numDmgSuffered )
  735. {
  736. nResult = int( 10000.0f * float( numDmgInflicted ) / float( numDmgInflicted + numDmgSuffered ) );
  737. if ( numDmgInflicted && ( nResult <= 0 ) )
  738. nResult = 1;
  739. }
  740. else
  741. nResult = 10000;
  742. //if ( bAsScore )
  743. //{
  744. // nResult = nResult;
  745. //}
  746. }
  747. else if ( nIndex == 3 )
  748. { // Rounds failed penalty
  749. nResult = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_RoundsPlayed, sld.matchentries() );
  750. nResult = ( nResult > 1 ) ? ( nResult - 1 ) : 0;
  751. if ( bAsScore )
  752. {
  753. nResult = COOPMISSION_SCORE_MULTIPLIER_ROUNDSFAILED*nResult;
  754. }
  755. }
  756. }
  757. else
  758. {
  759. if ( nIndex == 0 )
  760. { // Time Remaining on the clock
  761. nResult = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_TimeRemaining, sld.matchentries() );
  762. if ( bAsScore )
  763. {
  764. nResult = COOPMISSION_SCORE_MULTIPLIER_TIMELEFT*nResult;
  765. }
  766. }
  767. else if ( nIndex == 3 )
  768. { // Total damage taken
  769. nResult = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_HpDmgSuffered, sld.accountentries() );
  770. if ( bAsScore )
  771. {
  772. nResult = COOPMISSION_SCORE_MULTIPLIER_DAMTAKEN*nResult;
  773. }
  774. }
  775. }
  776. if ( bGuardian || bCoopMission )
  777. { // Shared categories for Guardian and Coop Mission
  778. if ( nIndex == 1 )
  779. { // Bullets Accuracy
  780. uint32 numBulletsFired = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_ShotsFired, sld.accountentries() );
  781. uint32 numBulletsOnTarget = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_ShotsOnTarget, sld.accountentries() );
  782. if ( !numBulletsFired )
  783. nResult = 0;
  784. else if ( numBulletsFired > 0 && numBulletsOnTarget < numBulletsFired )
  785. {
  786. nResult = int( 10000.0f * float( numBulletsOnTarget ) / float( numBulletsFired ) );
  787. if ( numBulletsOnTarget && ( nResult <= 0 ) )
  788. nResult = 1;
  789. }
  790. else
  791. nResult = 10000;
  792. //if ( bAsScore )
  793. //{
  794. // nResult = nResult;
  795. //}
  796. }
  797. else if ( nIndex == 2 )
  798. { // Headshots kill percentage
  799. uint32 numHeadshots = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_Headshots, sld.accountentries() );
  800. uint32 numKills = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_Kills, sld.accountentries() );
  801. if ( !numKills )
  802. nResult = 0;
  803. else if ( numKills > 0 && numHeadshots < numKills )
  804. {
  805. nResult = int( 10000.0f * float( numHeadshots ) / float( numKills ) );
  806. if ( numHeadshots && ( nResult <= 0 ) )
  807. nResult = 1;
  808. }
  809. else
  810. nResult = 10000;
  811. //if ( bAsScore )
  812. //{
  813. // nResult = nResult;
  814. //}
  815. }
  816. }
  817. }
  818. //
  819. // Scorechart for bonuses!
  820. //
  821. else
  822. {
  823. if ( bGuardian )
  824. {
  825. if ( nIndex == 0 )
  826. { // Under 3 rounds?
  827. uint32 numRoundsPlayed = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_RoundsPlayed, sld.matchentries() );
  828. nResult = numRoundsPlayed;
  829. if ( bAsScore )
  830. {
  831. nResult = ( numRoundsPlayed <= 3 ) ? 5000 : 0;
  832. }
  833. }
  834. }
  835. if ( bCoopMission )
  836. {
  837. if ( nIndex == 0 )
  838. { // No Deaths?
  839. uint32 numDeaths = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_Deaths, sld.accountentries() );
  840. nResult = numDeaths;
  841. if ( bAsScore )
  842. {
  843. nResult = ( numDeaths == 0 ) ? 5000 : 0;
  844. }
  845. }
  846. else if ( nIndex == 1 )
  847. { // All Challenge Coins?
  848. uint32 numChallenge = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_BonusChallenge, sld.matchentries() );
  849. nResult = numChallenge;
  850. if ( bAsScore )
  851. {
  852. nResult = ( numChallenge ) ? 5000 : 0;
  853. }
  854. }
  855. else if ( nIndex == 2 )
  856. { // Pistols Only?
  857. uint32 numPistolsOnly = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_BonusPistolOnly, sld.matchentries() );
  858. nResult = numPistolsOnly;
  859. if ( bAsScore )
  860. {
  861. nResult = ( numPistolsOnly ) ? 10000 : 0;
  862. }
  863. }
  864. else if ( nIndex == 3 )
  865. { // Hard Mode?
  866. uint32 numHardMode = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_BonusHardMode, sld.matchentries() );
  867. nResult = numHardMode;
  868. if ( bAsScore )
  869. {
  870. nResult = ( numHardMode ) ? 25000 : 0;
  871. }
  872. }
  873. }
  874. }
  875. return nResult;
  876. }
  877. #ifndef CLIENT_DLL
  878. ConVar mp_backup_round_auto( "mp_backup_round_auto", "1", FCVAR_RELEASE, "If enabled will keep in-memory backups to handle reconnecting players even if the backup files aren't written to disk" );
  879. ConVar mp_backup_round_file( "mp_backup_round_file", "backup", FCVAR_RELEASE, "If set then server will save all played rounds information to files filename_date_time_team1_team2_mapname_roundnum_score1_score2.txt" );
  880. ConVar mp_backup_round_file_pattern( "mp_backup_round_file_pattern", "%prefix%_round%round%.txt", FCVAR_RELEASE, "If set then server will save all played rounds information to files named by this pattern, e.g.'%prefix%_%date%_%time%_%team1%_%team2%_%map%_round%round%_score_%score1%_%score2%.txt'" );
  881. ConVar mp_backup_round_file_last( "mp_backup_round_file_last", "", FCVAR_RELEASE, "Every time a backup file is written the value of this convar gets updated to hold the name of the backup file." );
  882. static bool Helper_mp_backup_round_IsEnabled()
  883. {
  884. if ( g_pGameTypes->GetCurrentGameType() == CS_GameType_GunGame )
  885. {
  886. switch ( g_pGameTypes->GetCurrentGameMode() )
  887. {
  888. case CS_GameMode::GunGame_Progressive:
  889. case CS_GameMode::GunGame_Bomb:
  890. return !!*mp_backup_round_file.GetString(); // round backups aren't supported in 'auto' mode in ArmsRace/Demolition, but we'll honor server config to write backup file
  891. }
  892. }
  893. return mp_backup_round_auto.GetBool() || *mp_backup_round_file.GetString();
  894. }
  895. static int Helper_mp_backup_restore_from_file_sorter( char const * const *a, char const * const *b )
  896. {
  897. return -Q_stricmp( *a, *b ); // sort more recent files earlier
  898. }
  899. static void Helper_FilenameTokenReplace( char* pchBufFilename, int nSize, const char *aReplacePattern, const char *theActualValue )
  900. {
  901. char const *pchReplacePattern, *pchActualValue;
  902. char const *pchEndFilename = ( char * ) pchBufFilename + nSize;
  903. pchReplacePattern = aReplacePattern;
  904. pchActualValue = theActualValue;
  905. while ( char *pchReplace = Q_strstr( pchBufFilename, pchReplacePattern ) )
  906. {
  907. int lenReplace = Q_strlen( pchReplacePattern ), lenValue = Q_strlen( pchActualValue );
  908. if ( pchReplace + lenValue >= pchEndFilename )
  909. {
  910. break;
  911. }
  912. Q_memmove( pchReplace + lenValue, pchReplace + lenReplace, pchEndFilename - pchReplace - MAX( lenReplace, lenValue ) );
  913. Q_memmove( pchReplace, pchActualValue, lenValue );
  914. }
  915. }
  916. static bool Helper_ShouldBroadcastCoopScoreLeaderboardData()
  917. {
  918. if ( !CSGameRules() )
  919. return false;
  920. if ( CSGameRules()->IsPlayingCoopGuardian() || CSGameRules()->IsPlayingCoopMission() )
  921. {
  922. // Check if the humans won the match
  923. int numHumanRoundsWon = CSGameRules()->IsPlayingCoopMission()
  924. ? CSGameRules()->m_match.GetCTScore()
  925. : ( CSGameRules()->IsHostageRescueMap()
  926. ? CSGameRules()->m_match.GetTerroristScore()
  927. : CSGameRules()->m_match.GetCTScore()
  928. );
  929. if ( numHumanRoundsWon > 0 )
  930. return true;
  931. }
  932. return false;
  933. }
  934. static void Helper_FillScoreLeaderboardData( ScoreLeaderboardData &sld )
  935. {
  936. //
  937. // This function is used in both official and community server build
  938. // In official build it will deliver the leaderboard data to GC
  939. // in both official and community build this data is also replicated to clients for the end of match scoreboard
  940. //
  941. if ( !Helper_ShouldBroadcastCoopScoreLeaderboardData() )
  942. return;
  943. //
  944. // Per player stats
  945. //
  946. FOR_EACH_MAP( CSGameRules()->m_mapQueuedMatchmakingPlayersData, i )
  947. {
  948. CCSGameRules::CQMMPlayerData_t const &qmm = *CSGameRules()->m_mapQueuedMatchmakingPlayersData.Element( i );
  949. ScoreLeaderboardData_AccountEntries *pAcc = sld.add_accountentries();
  950. pAcc->set_accountid( qmm.m_uiPlayerAccountId );
  951. if ( int n = qmm.m_numEnemyKills )
  952. {
  953. ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries();
  954. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_Kills );
  955. pEnt->set_val( n );
  956. }
  957. if ( int n = qmm.m_numEnemyKillHeadshots )
  958. {
  959. ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries();
  960. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_Headshots );
  961. pEnt->set_val( n );
  962. }
  963. if ( int n = qmm.m_numDeaths )
  964. {
  965. ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries();
  966. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_Deaths );
  967. pEnt->set_val( n );
  968. }
  969. if ( int n = qmm.m_numHealthPointsRemovedTotal )
  970. {
  971. ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries();
  972. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_HpDmgSuffered );
  973. pEnt->set_val( n );
  974. }
  975. if ( int n = qmm.m_numHealthPointsDealtTotal )
  976. {
  977. ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries();
  978. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_HpDmgInflicted );
  979. pEnt->set_val( n );
  980. }
  981. if ( int n = qmm.m_numShotsFiredTotal )
  982. {
  983. ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries();
  984. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_ShotsFired );
  985. pEnt->set_val( n );
  986. }
  987. if ( int n = qmm.m_numShotsOnTargetTotal )
  988. {
  989. ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries();
  990. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_ShotsOnTarget );
  991. pEnt->set_val( n );
  992. }
  993. }
  994. //
  995. // Match stats
  996. //
  997. if ( int n = CSGameRules()->GetTotalRoundsPlayed() )
  998. {
  999. ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries();
  1000. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_RoundsPlayed );
  1001. pEnt->set_val( n );
  1002. }
  1003. if ( CSGameRules()->IsPlayingCoopMission() )
  1004. {
  1005. int nRemainingTime = CSGameRules()->GetRoundRemainingTime();
  1006. if ( nRemainingTime < 0 )
  1007. nRemainingTime = 0;
  1008. if ( int n = nRemainingTime )
  1009. {
  1010. ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries();
  1011. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_TimeRemaining );
  1012. pEnt->set_val( n );
  1013. }
  1014. static ConVarRef mp_coopmission_bot_difficulty_offset( "mp_coopmission_bot_difficulty_offset" );
  1015. int nHardMode = ( mp_coopmission_bot_difficulty_offset.GetInt() >= 3 ) ? 1 : 0;
  1016. if ( int n = nHardMode )
  1017. {
  1018. ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries();
  1019. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_BonusHardMode );
  1020. pEnt->set_val( n );
  1021. }
  1022. if ( int n = CSGameRules()->m_coopBonusPistolsOnly ? 1 : 0 )
  1023. {
  1024. ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries();
  1025. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_BonusPistolOnly );
  1026. pEnt->set_val( n );
  1027. }
  1028. if ( int n = ( CSGameRules()->m_coopBonusCoinsFound == 3 ) ? 1 : 0 )
  1029. {
  1030. ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries();
  1031. pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_BonusChallenge );
  1032. pEnt->set_val( n );
  1033. }
  1034. }
  1035. //
  1036. // Set the final score and the questid if applicable
  1037. //
  1038. int32 nTotalScore = 0;
  1039. for ( int nBonus = 0; nBonus <= 1; ++ nBonus )
  1040. {
  1041. int32 nScoreTier = 0;
  1042. for ( int iCategory = 0; iCategory < 5; ++ iCategory )
  1043. {
  1044. nScoreTier += CoopScoreGetRatingEntryFromLeaderboardData( sld, !!nBonus, iCategory, true );
  1045. }
  1046. if ( nScoreTier < 0 )
  1047. nScoreTier = 0;
  1048. nTotalScore += nScoreTier;
  1049. }
  1050. sld.set_score( nTotalScore );
  1051. }
  1052. bool IsAssassinationQuest( const CEconQuestDefinition *pQuest )
  1053. {
  1054. if ( pQuest &&
  1055. ( V_stristr( pQuest->GetQuestExpression(), "act_kill_target" ) ||
  1056. V_stristr( pQuest->GetQuestExpression(), "act_pick_up_trophy" ) ) )
  1057. return true;
  1058. return false;
  1059. }
  1060. bool IsAssassinationQuest( uint32 questID )
  1061. {
  1062. const CEconQuestDefinition *pQuest = GetItemSchema()->GetQuestDefinition( questID );
  1063. return IsAssassinationQuest( pQuest );
  1064. }
  1065. // Checks basic conditions for a quest (mapgroup, mode, etc) to see if a quest is possible to complete
  1066. bool Helper_CheckQuestMapAndMode( const CEconQuestDefinition *pQuest )
  1067. {
  1068. const char *szMapName = NULL;
  1069. const char *szMapGroupName = NULL;
  1070. #if defined ( CLIENT_DLL )
  1071. szMapName = engine->GetLevelNameShort();
  1072. szMapGroupName = engine->GetMapGroupName();
  1073. #else
  1074. szMapName = V_UnqualifiedFileName( STRING( gpGlobals->mapname ) );
  1075. szMapGroupName = STRING( gpGlobals->mapGroupName );
  1076. #endif
  1077. // Wrong map
  1078. if ( !StringIsEmpty( pQuest->GetMap() ) && V_strcmp( szMapName, pQuest->GetMap() ) )
  1079. return false;
  1080. // Unless the map group is named after our map (so queued for a single map) also confirm we're using the right map group
  1081. if ( V_strcmp( szMapGroupName, CFmtStr( "mg_%s", szMapName ) ) )
  1082. {
  1083. if ( !StringIsEmpty( pQuest->GetMapGroup() ) && V_strcmp( szMapGroupName, pQuest->GetMapGroup() ) )
  1084. {
  1085. return false;
  1086. }
  1087. }
  1088. const char *szCurrentModeAsString = g_pGameTypes->GetGameModeFromInt( g_pGameTypes->GetCurrentGameType(), g_pGameTypes->GetCurrentGameMode() );
  1089. // Mode doesn't match
  1090. if ( V_strcmp( pQuest->GetGameMode(), szCurrentModeAsString ) )
  1091. return false;
  1092. return true;
  1093. }
  1094. bool IsAssassinationQuestActive( const CEconQuestDefinition *pQuest )
  1095. {
  1096. if ( CSGameRules() && CSGameRules()->IsWarmupPeriod() )
  1097. return false;
  1098. // We need to have an active quest with the 'act_kill_target' requirement
  1099. if ( !pQuest || !IsAssassinationQuest( pQuest ) )
  1100. return false;
  1101. // Validate target team
  1102. if ( pQuest->GetTargetTeam() != TEAM_TERRORIST && pQuest->GetTargetTeam() != TEAM_CT )
  1103. return false;
  1104. if ( !Helper_CheckQuestMapAndMode( pQuest ) )
  1105. return false;
  1106. return true;
  1107. }
  1108. #if BACKUPSUPPORTZEROZERO
  1109. static char const * const g_szRoundBackupZeroZero = "0:0";
  1110. static char const * const g_szRoundBackupZeroZeroFileName = "00restart";
  1111. #endif
  1112. class CBackupFilesEnumerator : public CUtlVector< char const * >
  1113. {
  1114. public:
  1115. CBackupFilesEnumerator()
  1116. {
  1117. if ( !mp_backup_round_file.GetString()[0] )
  1118. {
  1119. Warning( "Current backup file prefix is not set, use mp_backup_round_file to set it.\n" );
  1120. return;
  1121. }
  1122. Warning( "Listing backup files with prefix: %s\n", mp_backup_round_file.GetString() );
  1123. FileFindHandle_t hFind = NULL;
  1124. CUtlBuffer bufFilename;
  1125. int nBufSize = 1024;
  1126. bufFilename.EnsureCapacity( nBufSize );
  1127. char *pchBufFilename = ( char * ) bufFilename.Base();
  1128. Q_memset( pchBufFilename, 0, nBufSize );
  1129. Q_snprintf( pchBufFilename, nBufSize, "%s", mp_backup_round_file_pattern.GetString() );
  1130. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%prefix%", mp_backup_round_file.GetString() );
  1131. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%date%", "*" );
  1132. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%time%", "*" );
  1133. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%round%", "*" );
  1134. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%score1%", "*" );
  1135. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%score2%", "*" );
  1136. char chTeam1[64] = {0};
  1137. Q_snprintf( chTeam1, sizeof( chTeam1 ), "%s", GetGlobalTeam( CSGameRules()->AreTeamsPlayingSwitchedSides() ? TEAM_TERRORIST : TEAM_CT )->GetClanName() );
  1138. for ( char *pch = chTeam1; *pch; ++ pch )
  1139. {
  1140. if (
  1141. ( ( pch[0] >= 'a' ) && ( pch[0] <= 'z' ) ) ||
  1142. ( ( pch[0] >= 'A' ) && ( pch[0] <= 'Z' ) ) ||
  1143. ( ( pch[0] >= '0' ) && ( pch[0] <= '9' ) )
  1144. )
  1145. ;
  1146. else
  1147. {
  1148. Q_memmove( pch, pch + 1, sizeof( chTeam1 ) - ( pch - chTeam1 ) - 1 );
  1149. -- pch;
  1150. }
  1151. }
  1152. char chTeam2[64] = {0};
  1153. Q_snprintf( chTeam2, sizeof( chTeam2 ), "%s", GetGlobalTeam( CSGameRules()->AreTeamsPlayingSwitchedSides() ? TEAM_CT : TEAM_TERRORIST )->GetClanName() );
  1154. for ( char *pch = chTeam2; *pch; ++ pch )
  1155. {
  1156. if (
  1157. ( ( pch[0] >= 'a' ) && ( pch[0] <= 'z' ) ) ||
  1158. ( ( pch[0] >= 'A' ) && ( pch[0] <= 'Z' ) ) ||
  1159. ( ( pch[0] >= '0' ) && ( pch[0] <= '9' ) )
  1160. )
  1161. ;
  1162. else
  1163. {
  1164. Q_memmove( pch, pch + 1, sizeof( chTeam2 ) - ( pch - chTeam2 ) - 1 );
  1165. -- pch;
  1166. }
  1167. }
  1168. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%team1%", chTeam1 );
  1169. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%team2%", chTeam2 );
  1170. { // Map might have a path in it, make sure we strip it
  1171. const char *szMapNameToken = STRING( gpGlobals->mapname );
  1172. if ( const char *pchSlash = strrchr( szMapNameToken, '/' ) )
  1173. szMapNameToken = pchSlash + 1;
  1174. if ( const char *pchSlash2 = strrchr( szMapNameToken, '\\' ) )
  1175. szMapNameToken = pchSlash2 + 1;
  1176. Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%map%", szMapNameToken );
  1177. }
  1178. for ( char const *szFileName = filesystem->FindFirst( pchBufFilename, &hFind );
  1179. szFileName && *szFileName; szFileName = filesystem->FindNext( hFind ) )
  1180. {
  1181. m_mapFiles.AddString( szFileName );
  1182. }
  1183. filesystem->FindClose( hFind );
  1184. for ( int iStr = 0; iStr < m_mapFiles.GetNumStrings(); ++ iStr )
  1185. {
  1186. AddToTail( m_mapFiles.String( iStr ) );
  1187. }
  1188. Sort( Helper_mp_backup_restore_from_file_sorter );
  1189. #if BACKUPSUPPORTZEROZERO
  1190. // add 0:0 backup
  1191. static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL );
  1192. if ( s_pchTournamentServer )
  1193. AddToTail( g_szRoundBackupZeroZeroFileName );
  1194. #endif
  1195. }
  1196. private:
  1197. CUtlStringMap< bool > m_mapFiles;
  1198. };
  1199. #if defined( _DEBUG )
  1200. #define ROUND_BACKUP_REQUEST_RATE_LIMIT 5
  1201. #else
  1202. #define ROUND_BACKUP_REQUEST_RATE_LIMIT 10
  1203. #endif
  1204. // concommand that requests a user message with the round backup filenames in it.
  1205. CON_COMMAND_F ( send_round_backup_file_list, "", FCVAR_GAMEDLL | FCVAR_RELEASE |FCVAR_HIDDEN|FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS )
  1206. {
  1207. CBasePlayer* pPlayer = UTIL_GetCommandClient();
  1208. if ( !pPlayer )
  1209. return;
  1210. static CUtlMap<int, int > s_mapLastRequestTime;
  1211. if ( s_mapLastRequestTime.Count() == 0 )
  1212. {
  1213. s_mapLastRequestTime.SetLessFunc( DefLessFunc( int ) );
  1214. }
  1215. // prevent spamming of the command. store each user's previous request time.
  1216. int nIndex = s_mapLastRequestTime.Find( pPlayer->entindex() );
  1217. if ( ( s_mapLastRequestTime.IsValidIndex( nIndex ) ) &&
  1218. ( gpGlobals->curtime > s_mapLastRequestTime[ nIndex ] ) &&
  1219. ( ( gpGlobals->curtime - s_mapLastRequestTime[ nIndex ] ) <= ROUND_BACKUP_REQUEST_RATE_LIMIT ) )
  1220. return;
  1221. s_mapLastRequestTime.InsertOrReplace( pPlayer->entindex(), ( int )gpGlobals->curtime );
  1222. CCSUsrMsg_RoundBackupFilenames msg;
  1223. CBackupFilesEnumerator arrStrings;
  1224. // to avoid the proto message limit we're sending these in individual messages rather than one.
  1225. for ( int idx = 0; idx < Min( 10, arrStrings.Count() ); idx++ )
  1226. {
  1227. CCSUsrMsg_RoundBackupFilenames msg;
  1228. msg.set_count( Min( 10, arrStrings.Count() ) );
  1229. msg.set_index( idx );
  1230. msg.set_filename( arrStrings[ idx ] );
  1231. // create human readable name
  1232. KeyValues *kvSaveFile = new KeyValues( "" );
  1233. KeyValues::AutoDelete autodelete_kvSaveFile( kvSaveFile );
  1234. autodelete_kvSaveFile->UsesEscapeSequences( true );
  1235. CUtlString nicefilename = arrStrings[ idx ];
  1236. #if BACKUPSUPPORTZEROZERO
  1237. if ( !V_strcmp( arrStrings[ idx ], g_szRoundBackupZeroZeroFileName ) )
  1238. {
  1239. nicefilename = g_szRoundBackupZeroZero;
  1240. }
  1241. else
  1242. #endif
  1243. if ( kvSaveFile->LoadFromFile( filesystem, arrStrings[ idx ] ) )
  1244. {
  1245. nicefilename = CFmtStr( "%s score %d:%d",
  1246. kvSaveFile->GetString( "timestamp" ),
  1247. kvSaveFile->GetInt( "FirstHalfScore/team1" ) + kvSaveFile->GetInt( "SecondHalfScore/team1" ) + kvSaveFile->GetInt( "OvertimeScore/team1" ),
  1248. kvSaveFile->GetInt( "FirstHalfScore/team2" ) + kvSaveFile->GetInt( "SecondHalfScore/team2" ) + kvSaveFile->GetInt( "OvertimeScore/team2" ));
  1249. }
  1250. msg.set_nicename( nicefilename );
  1251. CSingleUserRecipientFilter filter( pPlayer );
  1252. SendUserMessage( filter, CS_UM_RoundBackupFilenames, msg );
  1253. }
  1254. }
  1255. CON_COMMAND_F ( mp_backup_restore_list_files, "Lists recent backup round files matching the prefix, most recent files first, accepts a numeric parameter to limit the number of files displayed", FCVAR_GAMEDLL | FCVAR_RELEASE )
  1256. {
  1257. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1258. return;
  1259. int numFilesToList = 5;
  1260. if ( args.ArgC() >= 2 )
  1261. {
  1262. numFilesToList = Q_atoi( args.Arg( 1 ) );
  1263. }
  1264. CBackupFilesEnumerator arrStrings;
  1265. FOR_EACH_VEC( arrStrings, idxString )
  1266. {
  1267. if ( idxString >= numFilesToList )
  1268. break;
  1269. Warning( " %s\n", arrStrings[idxString] );
  1270. }
  1271. if ( !arrStrings.Count() )
  1272. {
  1273. Warning( "No matching backup files found.\n" );
  1274. }
  1275. else if ( numFilesToList < arrStrings.Count() )
  1276. {
  1277. Warning( "%d backup files found, %d most recent listed, use 'mp_backup_restore_list_files %d' to list all.\n", arrStrings.Count(), numFilesToList, arrStrings.Count() );
  1278. }
  1279. else
  1280. {
  1281. Warning( "%d backup files listed.\n", arrStrings.Count() );
  1282. }
  1283. }
  1284. ConVar mp_backup_restore_load_autopause( "mp_backup_restore_load_autopause", "1", FCVAR_RELEASE, "Whether to automatically pause the match after restoring round data from backup" );
  1285. CON_COMMAND_F ( mp_backup_restore_load_file, "Loads player cash, KDA, scores and team scores; resets to the next round after the backup", FCVAR_GAMEDLL | FCVAR_RELEASE )
  1286. {
  1287. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1288. return;
  1289. if ( args.ArgC() < 2 )
  1290. {
  1291. Warning( "Usage: mp_backup_restore_load_file [filename], use 'mp_backup_restore_list_files' to list matching backup files.\n" );
  1292. return;
  1293. }
  1294. CSGameRules()->LoadRoundDataInformation( args.Arg( 1 ) );
  1295. }
  1296. CMsgGCCStrike15_v2_MatchmakingGC2ServerReserve CCSGameRules::sm_QueuedServerReservation;
  1297. #endif
  1298. #ifdef CLIENT_DLL
  1299. void RecvProxy_CSGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
  1300. {
  1301. CCSGameRules *pRules = CSGameRules();
  1302. Assert( pRules );
  1303. *pOut = pRules;
  1304. }
  1305. BEGIN_RECV_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy )
  1306. RecvPropDataTable( "cs_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_CSGameRules ), RecvProxy_CSGameRules )
  1307. END_RECV_TABLE()
  1308. #else
  1309. void* SendProxy_CSGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  1310. {
  1311. CCSGameRules *pRules = CSGameRules();
  1312. Assert( pRules );
  1313. return pRules;
  1314. }
  1315. BEGIN_SEND_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy )
  1316. SendPropDataTable( "cs_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_CSGameRules ), SendProxy_CSGameRules )
  1317. END_SEND_TABLE()
  1318. #endif
  1319. // MOSTLY DECPRECATED.
  1320. // All weapons ( except exhaustibles, ITEM_FLAG_EXHAUSTIBLE, such as grenades ) store their reserve ammo on the weapon entity
  1321. // and no longer on the player. The reserve amount is specified in schema as attributes "primary reserve ammo max" and "secondary reserve ammo max"
  1322. ConVar ammo_50AE_max( "ammo_50AE_max", "35", FCVAR_REPLICATED | FCVAR_RELEASE );
  1323. ConVar ammo_762mm_max( "ammo_762mm_max", "90", FCVAR_REPLICATED | FCVAR_RELEASE );
  1324. ConVar ammo_556mm_max( "ammo_556mm_max", "90", FCVAR_REPLICATED | FCVAR_RELEASE );
  1325. ConVar ammo_556mm_small_max( "ammo_556mm_small_max", "40", FCVAR_REPLICATED | FCVAR_RELEASE );
  1326. ConVar ammo_556mm_box_max( "ammo_556mm_box_max", "200", FCVAR_REPLICATED | FCVAR_RELEASE );
  1327. ConVar ammo_338mag_max( "ammo_338mag_max", "30", FCVAR_REPLICATED | FCVAR_RELEASE );
  1328. ConVar ammo_9mm_max( "ammo_9mm_max", "120", FCVAR_REPLICATED | FCVAR_RELEASE );
  1329. ConVar ammo_buckshot_max( "ammo_buckshot_max", "32", FCVAR_REPLICATED | FCVAR_RELEASE );
  1330. ConVar ammo_45acp_max( "ammo_45acp_max", "100", FCVAR_REPLICATED | FCVAR_RELEASE );
  1331. ConVar ammo_357sig_max( "ammo_357sig_max", "52", FCVAR_REPLICATED | FCVAR_RELEASE );
  1332. ConVar ammo_357sig_p250_max( "ammo_357sig_p250_max", "26", FCVAR_REPLICATED | FCVAR_RELEASE );
  1333. ConVar ammo_357sig_small_max( "ammo_357sig_small_max", "24", FCVAR_REPLICATED | FCVAR_RELEASE );
  1334. ConVar ammo_357sig_min_max( "ammo_357sig_min_max", "12", FCVAR_REPLICATED | FCVAR_RELEASE );
  1335. ConVar ammo_57mm_max( "ammo_57mm_max", "100", FCVAR_REPLICATED | FCVAR_RELEASE );
  1336. ConVar ammo_grenade_limit_default( "ammo_grenade_limit_default", "1", FCVAR_REPLICATED | FCVAR_RELEASE );
  1337. ConVar ammo_grenade_limit_flashbang( "ammo_grenade_limit_flashbang", "1", FCVAR_REPLICATED | FCVAR_RELEASE );
  1338. ConVar ammo_grenade_limit_total( "ammo_grenade_limit_total", "3", FCVAR_REPLICATED | FCVAR_RELEASE );
  1339. ConVar ammo_item_limit_healthshot( "ammo_item_limit_healthshot", "4", FCVAR_REPLICATED | FCVAR_RELEASE );
  1340. ConVar ammo_50AE_impulse( "ammo_50AE_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1341. ConVar ammo_762mm_impulse( "ammo_762mm_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1342. ConVar ammo_556mm_impulse( "ammo_556mm_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1343. ConVar ammo_556mm_box_impulse( "ammo_556mm_box_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1344. ConVar ammo_338mag_impulse( "ammo_338mag_impulse", "2800", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1345. ConVar ammo_9mm_impulse( "ammo_9mm_impulse", "2000", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1346. ConVar ammo_buckshot_impulse( "ammo_buckshot_impulse", "600", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1347. ConVar ammo_45acp_impulse( "ammo_45acp_impulse", "2100", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1348. ConVar ammo_357sig_impulse( "ammo_357sig_impulse", "2000", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1349. ConVar ammo_57mm_impulse( "ammo_57mm_impulse", "2000", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1350. ConVar ammo_50AE_headshot_mult( "ammo_50AE_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1351. ConVar ammo_762mm_headshot_mult( "ammo_762mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1352. ConVar ammo_556mm_headshot_mult( "ammo_556mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1353. ConVar ammo_556mm_box_headshot_mult( "ammo_556mm_box_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1354. ConVar ammo_338mag_headshot_mult( "ammo_338mag_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1355. ConVar ammo_9mm_headshot_mult( "ammo_9mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1356. ConVar ammo_buckshot_headshot_mult( "ammo_buckshot_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1357. ConVar ammo_45acp_headshot_mult( "ammo_45acp_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1358. ConVar ammo_357sig_headshot_mult( "ammo_357sig_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1359. ConVar ammo_57mm_headshot_mult( "ammo_57mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." );
  1360. //ConVar mp_dynamicpricing( "mp_dynamicpricing", "0", FCVAR_REPLICATED, "Enables or Disables the dynamic weapon prices" );
  1361. ConVar mp_spec_swapplayersides(
  1362. "mp_spec_swapplayersides",
  1363. "0",
  1364. FCVAR_REPLICATED | FCVAR_RELEASE,
  1365. "Toggle set the player names and team names to the opposite side in which they are are on the spectator panel.",
  1366. true, 0,
  1367. true, 1
  1368. );
  1369. ConVar mp_force_assign_teams(
  1370. "mp_force_assign_teams",
  1371. "0",
  1372. FCVAR_REPLICATED | FCVAR_RELEASE,
  1373. "Players don't get to choose what team they are on, it is auto assinged.",
  1374. true, 0,
  1375. true, 1
  1376. );
  1377. #if defined( GAME_DLL )
  1378. // need this defined for the server so it can load from game.cfg
  1379. ConVar sv_gameinstructor_disable( "sv_gameinstructor_disable", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Force all clients to disable their game instructors." );
  1380. ConVar cs_AssistDamageThreshold( "cs_AssistDamageThreshold", "40.0", FCVAR_DEVELOPMENTONLY, "cs_AssistDamageThreshold defines the amount of damage needed to score an assist" );
  1381. #endif
  1382. ConVar sv_matchpause_auto_5v5( "sv_matchpause_auto_5v5", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "When enabled will automatically pause the match at next freeze time if less than 5 players are connected on each team." );
  1383. extern ConVar sv_stopspeed;
  1384. extern ConVar mp_randomspawn;
  1385. extern ConVar mp_randomspawn_los;
  1386. extern ConVar mp_teammates_are_enemies;
  1387. extern ConVar mp_respawnwavetime;
  1388. extern ConVar mp_hostages_max;
  1389. extern ConVar mp_hostages_spawn_farthest;
  1390. extern ConVar mp_hostages_spawn_force_positions;
  1391. extern ConVar mp_hostages_spawn_same_every_round;
  1392. extern ConVar weapon_max_before_cleanup;
  1393. ConVar mp_spectators_max(
  1394. "mp_spectators_max",
  1395. "2",
  1396. FCVAR_REPLICATED | FCVAR_RELEASE,
  1397. "How many spectators are allowed in a match.",
  1398. true, 0,
  1399. false, 0 );
  1400. ConVar mp_buytime(
  1401. "mp_buytime",
  1402. "90",
  1403. FCVAR_REPLICATED | FCVAR_RELEASE,
  1404. "How many seconds after round start players can buy items for.",
  1405. true, 0,
  1406. false, 0 );
  1407. ConVar mp_buy_allow_grenades(
  1408. "mp_buy_allow_grenades",
  1409. "1",
  1410. FCVAR_REPLICATED | FCVAR_RELEASE,
  1411. "Whether players can purchase grenades from the buy menu or not.",
  1412. true, 0,
  1413. true, 1 );
  1414. ConVar mp_do_warmup_period(
  1415. "mp_do_warmup_period",
  1416. "1",
  1417. FCVAR_REPLICATED | FCVAR_RELEASE,
  1418. "Whether or not to do a warmup period at the start of a match.",
  1419. true, 0,
  1420. true, 1 );
  1421. ConVar mp_do_warmup_offine(
  1422. "mp_do_warmup_offine",
  1423. "0",
  1424. FCVAR_REPLICATED | FCVAR_RELEASE,
  1425. "Whether or not to do a warmup period at the start of a match in an offline (bot) match.",
  1426. true, 0,
  1427. true, 1 );
  1428. ConVar mp_startmoney(
  1429. "mp_startmoney",
  1430. "800",
  1431. FCVAR_REPLICATED | FCVAR_RELEASE,
  1432. "amount of money each player gets when they reset",
  1433. true, 0,
  1434. false, 0 );
  1435. ConVar mp_maxmoney(
  1436. "mp_maxmoney",
  1437. "16000",
  1438. FCVAR_REPLICATED | FCVAR_RELEASE,
  1439. "maximum amount of money allowed in a player's account",
  1440. true, 0,
  1441. false, 0 );
  1442. ConVar mp_afterroundmoney(
  1443. "mp_afterroundmoney",
  1444. "0",
  1445. FCVAR_REPLICATED | FCVAR_RELEASE,
  1446. "amount of money awared to every player after each round" );
  1447. ConVar mp_playercashawards(
  1448. "mp_playercashawards",
  1449. "1",
  1450. FCVAR_REPLICATED | FCVAR_RELEASE,
  1451. "Players can earn money by performing in-game actions" );
  1452. ConVar mp_teamcashawards(
  1453. "mp_teamcashawards",
  1454. "1",
  1455. FCVAR_REPLICATED | FCVAR_RELEASE,
  1456. "Teams can earn money by performing in-game actions" );
  1457. ConVar mp_overtime_enable(
  1458. "mp_overtime_enable",
  1459. "0",
  1460. FCVAR_REPLICATED | FCVAR_RELEASE,
  1461. "If a match ends in a tie, use overtime rules to determine winner" );
  1462. ConVar mp_overtime_maxrounds(
  1463. "mp_overtime_maxrounds",
  1464. "6",
  1465. FCVAR_REPLICATED | FCVAR_RELEASE,
  1466. "When overtime is enabled play additional rounds to determine winner" );
  1467. ConVar mp_overtime_startmoney(
  1468. "mp_overtime_startmoney",
  1469. "10000",
  1470. FCVAR_REPLICATED | FCVAR_RELEASE,
  1471. "Money assigned to all players at start of every overtime half" );
  1472. ConVar mp_hostages_takedamage(
  1473. "mp_hostages_takedamage",
  1474. "0",
  1475. FCVAR_REPLICATED | FCVAR_RELEASE,
  1476. "Whether or not hostages can be hurt." );
  1477. ConVar mp_hostages_rescuetowin(
  1478. "mp_hostages_rescuetowin",
  1479. "1",
  1480. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  1481. "0 == all alive, any other number is the number the CT's need to rescue to win the round." );
  1482. ConVar mp_hostages_rescuetime(
  1483. "mp_hostages_rescuetime",
  1484. "1",
  1485. FCVAR_REPLICATED | FCVAR_RELEASE,
  1486. "Additional time added to round time if a hostage is reached by a CT." );
  1487. ConVar mp_anyone_can_pickup_c4(
  1488. "mp_anyone_can_pickup_c4",
  1489. "0",
  1490. FCVAR_REPLICATED | FCVAR_RELEASE,
  1491. "If set, everyone can pick up the c4, not just Ts." );
  1492. ConVar mp_c4_cannot_be_defused(
  1493. "mp_c4_cannot_be_defused",
  1494. "0",
  1495. FCVAR_REPLICATED | FCVAR_RELEASE,
  1496. "If set, the planted c4 cannot be defused." );
  1497. ConVar sv_coaching_enabled(
  1498. "sv_coaching_enabled",
  1499. "0",
  1500. FCVAR_REPLICATED | FCVAR_RELEASE,
  1501. "Allows spectating and communicating with a team ( 'coach t' or 'coach ct' )" );
  1502. ConVar sv_allow_thirdperson(
  1503. "sv_allow_thirdperson",
  1504. "0",
  1505. FCVAR_REPLICATED | FCVAR_RELEASE,
  1506. "Allows the server set players in third person mode without the client slamming it back (if cheats are on, all clients can set thirdperson without this convar being set)" );
  1507. ConVar sv_party_mode(
  1508. "sv_party_mode",
  1509. "0",
  1510. FCVAR_REPLICATED | FCVAR_RELEASE,
  1511. "Party!!" );
  1512. // ConVar mp_hostages_canpickup(
  1513. // "mp_hostages_canpickup",
  1514. // "1",
  1515. // FCVAR_REPLICATED | FCVAR_RELEASE,
  1516. // "1 = hostages are picked up when used, 0 == hostages uyse the old method of following behind players." );
  1517. #ifndef CLIENT_DLL
  1518. CON_COMMAND( timeout_terrorist_start, "" )
  1519. {
  1520. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1521. return;
  1522. CSGameRules()->StartTerroristTimeOut();
  1523. }
  1524. CON_COMMAND( timeout_ct_start, "" )
  1525. {
  1526. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1527. return;
  1528. CSGameRules()->StartCTTimeOut();
  1529. }
  1530. CON_COMMAND( mp_warmup_start, "Start warmup." )
  1531. {
  1532. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1533. return;
  1534. if ( CSGameRules() )
  1535. {
  1536. if ( CSGameRules()->IsQueuedMatchmaking() )
  1537. {
  1538. Msg( "mp_warmup_start cannot be used on official servers, use a longer mp_warmuptime instead!\n" );
  1539. }
  1540. else
  1541. {
  1542. CSGameRules()->StartWarmup();
  1543. }
  1544. }
  1545. }
  1546. CON_COMMAND( mp_warmup_end, "End warmup immediately." )
  1547. {
  1548. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1549. {
  1550. // // Special handling here allowing players to unpause warmup timer
  1551. // // if it is in mode #2 where it is paused until getting unpaused by players
  1552. // extern ConVar mp_warmup_pausetimer;
  1553. // extern ConVar mp_warmuptime;
  1554. // if ( CSGameRules() && CSGameRules()->IsWarmupPeriod() && ( mp_warmup_pausetimer.GetInt() == 2 ) )
  1555. // {
  1556. // int nNewWarmupTime = mp_warmuptime.GetInt();
  1557. // if ( nNewWarmupTime < 15 )
  1558. // {
  1559. // mp_warmuptime.SetValue( 15 );
  1560. // }
  1561. // mp_warmup_pausetimer.SetValue( 0 );
  1562. //
  1563. // CSGameRules()->SetWarmupPeriodStartTime( gpGlobals->curtime );
  1564. // CSGameRules()->m_fWarmupNextChatNoticeTime = gpGlobals->curtime + 10;
  1565. //
  1566. // CBroadcastRecipientFilter filter;
  1567. // int issuingPlayerIndex = UTIL_GetCommandClientIndex();
  1568. // if ( CCSPlayer *pPlayer = ToCSPlayer( UTIL_EntityByIndex( issuingPlayerIndex ) ) )
  1569. // {
  1570. // Warning( "mp_warmup_end issued by player#%d(%s), unpausing warmup...\n", issuingPlayerIndex, pPlayer->GetPlayerName() );
  1571. // UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Player_Wants_Restart", pPlayer->GetPlayerName() );
  1572. // }
  1573. // else
  1574. // {
  1575. // Warning( "mp_warmup_end issued by player#%d with no player info, unpausing warmup...\n", issuingPlayerIndex );
  1576. // }
  1577. // UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Match_Will_Start_Chat" );
  1578. // }
  1579. return;
  1580. }
  1581. if ( CSGameRules() )
  1582. {
  1583. if ( CSGameRules()->IsQueuedMatchmaking() )
  1584. {
  1585. Msg( "mp_warmup_start cannot be used on official servers, use a shorter mp_warmuptime instead!\n" );
  1586. }
  1587. else
  1588. {
  1589. CSGameRules()->EndWarmup();
  1590. }
  1591. }
  1592. }
  1593. #endif
  1594. static void mpwarmuptime_f( IConVar *pConVar, const char *pOldString, float flOldValue )
  1595. {
  1596. if ( CSGameRules() )
  1597. {
  1598. CSGameRules()->SetWarmupPeriodStartTime( gpGlobals->curtime );
  1599. }
  1600. }
  1601. ConVar mp_verbose_changelevel_spew( "mp_verbose_changelevel_spew", "1", FCVAR_RELEASE );
  1602. ConVar mp_warmuptime(
  1603. "mp_warmuptime",
  1604. "30",
  1605. FCVAR_REPLICATED | FCVAR_RELEASE,
  1606. "How long the warmup period lasts. Changing this value resets warmup.",
  1607. true, 5,
  1608. false, 0,
  1609. mpwarmuptime_f );
  1610. ConVar mp_warmuptime_all_players_connected(
  1611. "mp_warmuptime_all_players_connected",
  1612. "60",
  1613. FCVAR_REPLICATED | FCVAR_RELEASE,
  1614. "Warmup time to use when all players have connected in official competitive. 0 to disable." );
  1615. ConVar mp_warmup_pausetimer(
  1616. "mp_warmup_pausetimer",
  1617. "0",
  1618. FCVAR_REPLICATED | FCVAR_RELEASE,
  1619. "Set to 1 to stay in warmup indefinitely. Set to 0 to resume the timer." );
  1620. ConVar mp_halftime_pausetimer(
  1621. "mp_halftime_pausetimer",
  1622. "0",
  1623. FCVAR_REPLICATED | FCVAR_RELEASE,
  1624. "Set to 1 to stay in halftime indefinitely. Set to 0 to resume the timer." );
  1625. ConVar mp_halftime_pausematch(
  1626. "mp_halftime_pausematch",
  1627. "0",
  1628. FCVAR_REPLICATED | FCVAR_RELEASE,
  1629. "Set to 1 to pause match after halftime countdown elapses. Match must be resumed by vote or admin." );
  1630. ConVar mp_overtime_halftime_pausetimer(
  1631. "mp_overtime_halftime_pausetimer",
  1632. "0",
  1633. FCVAR_REPLICATED | FCVAR_RELEASE,
  1634. "If set to 1 will set mp_halftime_pausetimer to 1 before every half of overtime. Set mp_halftime_pausetimer to 0 to resume the timer." );
  1635. ConVar mp_respawn_immunitytime("mp_respawn_immunitytime", "4.0", FCVAR_RELEASE | FCVAR_REPLICATED, "How many seconds after respawn immunity lasts." );
  1636. ConVar mp_playerid(
  1637. "mp_playerid",
  1638. "0",
  1639. FCVAR_REPLICATED | FCVAR_RELEASE,
  1640. "Controls what information player see in the status bar: 0 all names; 1 team names; 2 no names",
  1641. true, 0,
  1642. true, 2 );
  1643. ConVar mp_playerid_delay(
  1644. "mp_playerid_delay",
  1645. "0.4",
  1646. FCVAR_REPLICATED | FCVAR_RELEASE,
  1647. "Number of seconds to delay showing information in the status bar",
  1648. true, 0,
  1649. true, 1 );
  1650. ConVar mp_playerid_hold(
  1651. "mp_playerid_hold",
  1652. "0.2",
  1653. FCVAR_REPLICATED | FCVAR_RELEASE,
  1654. "Number of seconds to keep showing old information in the status bar",
  1655. true, 0,
  1656. true, 1 );
  1657. ConVar mp_round_restart_delay(
  1658. "mp_round_restart_delay",
  1659. "7.0",
  1660. FCVAR_REPLICATED | FCVAR_RELEASE,
  1661. "Number of seconds to delay before restarting a round after a win",
  1662. true, 0.0f,
  1663. true, 14.0f ); // 10 - 11 seconds is a comfortable minimum to fit the end of round replay
  1664. ConVar mp_halftime_duration(
  1665. "mp_halftime_duration",
  1666. "15.0",
  1667. FCVAR_REPLICATED | FCVAR_RELEASE,
  1668. "Number of seconds that halftime lasts",
  1669. true, 0.0f,
  1670. true, 300.0f );
  1671. ConVar mp_match_can_clinch(
  1672. "mp_match_can_clinch",
  1673. "1",
  1674. FCVAR_REPLICATED | FCVAR_RELEASE,
  1675. "Can a team clinch and end the match by being so far ahead that the other team has no way to catching up?" );
  1676. ConVar mp_ggtr_end_round_kill_bonus(
  1677. "mp_ggtr_end_round_kill_bonus",
  1678. "1",
  1679. FCVAR_REPLICATED | FCVAR_RELEASE,
  1680. "Number of bonus points awarded in Demolition Mode when knife kill ends round",
  1681. true, 0,
  1682. true, 10 );
  1683. ConVar mp_ggtr_last_weapon_kill_ends_half(
  1684. "mp_ggtr_last_weapon_kill_ends_half",
  1685. "0",
  1686. FCVAR_REPLICATED | FCVAR_RELEASE,
  1687. "End the half and give a team round point when a player makes a kill using the final weapon",
  1688. true, 0,
  1689. true, 1 );
  1690. ConVar mp_ggprogressive_round_restart_delay(
  1691. "mp_ggprogressive_round_restart_delay",
  1692. "15.0",
  1693. FCVAR_REPLICATED | FCVAR_RELEASE,
  1694. "Number of seconds to delay before restarting a round after a win in gungame progessive",
  1695. true, 0.0f,
  1696. true, 90.0f );
  1697. ConVar mp_ggprogressive_use_random_weapons(
  1698. "mp_ggprogressive_use_random_weapons",
  1699. "1",
  1700. FCVAR_REPLICATED | FCVAR_RELEASE,
  1701. "If set, selects random weapons from set categories for the progression order" );
  1702. ConVar mp_ggprogressive_random_weapon_kills_needed(
  1703. "mp_ggprogressive_random_weapon_kills_needed",
  1704. "2",
  1705. FCVAR_REPLICATED | FCVAR_RELEASE,
  1706. "If mp_ggprogressive_use_random_weapons is set, this is the number of kills needed with each weapon" );
  1707. ConVar mp_ggtr_num_rounds_autoprogress(
  1708. "mp_ggtr_num_rounds_autoprogress",
  1709. "3",
  1710. FCVAR_REPLICATED | FCVAR_RELEASE,
  1711. "Upgrade the player's weapon after this number of rounds without upgrading" );
  1712. ConVar mp_ct_default_melee(
  1713. "mp_ct_default_melee",
  1714. "weapon_knife",
  1715. FCVAR_REPLICATED | FCVAR_RELEASE,
  1716. "The default melee weapon that the CTs will spawn with. Even if this is blank, a knife will be given. To give a taser, it should look like this: 'weapon_knife weapon_taser'. Remember to set mp_weapons_allow_zeus to 1 if you want to give a taser!" );
  1717. ConVar mp_ct_default_secondary(
  1718. "mp_ct_default_secondary",
  1719. "weapon_hkp2000",
  1720. FCVAR_REPLICATED | FCVAR_RELEASE,
  1721. "The default secondary (pistol) weapon that the CTs will spawn with" );
  1722. ConVar mp_ct_default_primary(
  1723. "mp_ct_default_primary",
  1724. "",
  1725. FCVAR_REPLICATED | FCVAR_RELEASE,
  1726. "The default primary (rifle) weapon that the CTs will spawn with" );
  1727. ConVar mp_ct_default_grenades(
  1728. "mp_ct_default_grenades",
  1729. "",
  1730. FCVAR_REPLICATED | FCVAR_RELEASE,
  1731. "The default grenades that the CTs will spawn with. To give multiple grenades, separate each weapon class with a space like this: 'weapon_molotov weapon_hegrenade'" );
  1732. ConVar mp_t_default_melee(
  1733. "mp_t_default_melee",
  1734. "weapon_knife",
  1735. FCVAR_REPLICATED | FCVAR_RELEASE,
  1736. "The default melee weapon that the Ts will spawn with" );
  1737. ConVar mp_t_default_secondary(
  1738. "mp_t_default_secondary",
  1739. "weapon_glock",
  1740. FCVAR_REPLICATED | FCVAR_RELEASE,
  1741. "The default secondary (pistol) weapon that the Ts will spawn with" );
  1742. ConVar mp_t_default_primary(
  1743. "mp_t_default_primary",
  1744. "",
  1745. FCVAR_REPLICATED | FCVAR_RELEASE,
  1746. "The default primary (rifle) weapon that the Ts will spawn with" );
  1747. ConVar mp_t_default_grenades(
  1748. "mp_t_default_grenades",
  1749. "",
  1750. FCVAR_REPLICATED | FCVAR_RELEASE,
  1751. "The default grenades that the Ts will spawn with. To give multiple grenades, separate each weapon class with a space like this: 'weapon_molotov weapon_hegrenade'" );
  1752. ConVar mp_join_grace_time(
  1753. "mp_join_grace_time",
  1754. "0.0",
  1755. FCVAR_REPLICATED | FCVAR_RELEASE,
  1756. "Number of seconds after round start to allow a player to join a game",
  1757. true, 0.0f,
  1758. true, 30.0f );
  1759. ConVar mp_win_panel_display_time(
  1760. "mp_win_panel_display_time",
  1761. "3",
  1762. FCVAR_REPLICATED | FCVAR_RELEASE,
  1763. "The amount of time to show the win panel between matches / halfs" );
  1764. ConVar mp_ggtr_bomb_pts_for_upgrade(
  1765. "mp_ggtr_bomb_pts_for_upgrade",
  1766. "2.0",
  1767. FCVAR_REPLICATED | FCVAR_RELEASE,
  1768. "Kill points required to upgrade a player's weapon",
  1769. true, 1.0,
  1770. true, 10.0 );
  1771. ConVar mp_ggtr_bomb_pts_for_he(
  1772. "mp_ggtr_bomb_pts_for_he",
  1773. "3",
  1774. FCVAR_REPLICATED | FCVAR_RELEASE,
  1775. "Kill points required in a round to get a bonus HE grenade",
  1776. true, 1,
  1777. true, 5);
  1778. ConVar mp_ggtr_bomb_pts_for_flash(
  1779. "mp_ggtr_bomb_pts_for_flash",
  1780. "4",
  1781. FCVAR_REPLICATED | FCVAR_RELEASE,
  1782. "Kill points required in a round to get a bonus flash grenade",
  1783. true, 1,
  1784. true, 5);
  1785. ConVar mp_ggtr_bomb_pts_for_molotov(
  1786. "mp_ggtr_bomb_pts_for_molotov",
  1787. "5",
  1788. FCVAR_REPLICATED | FCVAR_RELEASE,
  1789. "Kill points required in a round to get a bonus molotov cocktail",
  1790. true, 1,
  1791. true, 5);
  1792. ConVar mp_molotovusedelay(
  1793. "mp_molotovusedelay",
  1794. "15.0",
  1795. FCVAR_REPLICATED | FCVAR_RELEASE,
  1796. "Number of seconds to delay before the molotov can be used after acquiring it",
  1797. true, 0.0,
  1798. true, 30.0 );
  1799. ConVar mp_ggtr_halftime_delay(
  1800. "mp_ggtr_halftime_delay",
  1801. "0.0",
  1802. FCVAR_REPLICATED | FCVAR_RELEASE,
  1803. "Number of seconds to delay during TR Mode halftime",
  1804. true, 0.0,
  1805. true, 30.0 );
  1806. ConVar mp_ggtr_bomb_respawn_delay(
  1807. "mp_ggtr_bomb_respawn_delay",
  1808. "0.0",
  1809. FCVAR_REPLICATED | FCVAR_RELEASE,
  1810. "Number of seconds to delay before making the bomb available to a respawner in gun game",
  1811. true, 0.0,
  1812. true, 30.0 );
  1813. ConVar mp_ggtr_bomb_defuse_bonus(
  1814. "mp_ggtr_bomb_defuse_bonus",
  1815. "1.0",
  1816. FCVAR_REPLICATED | FCVAR_RELEASE,
  1817. "Number of bonus upgrades to award the CTs when they defuse a gun game bomb",
  1818. true, 1.0,
  1819. true, 10.0 );
  1820. ConVar mp_ggtr_bomb_detonation_bonus(
  1821. "mp_ggtr_bomb_detonation_bonus",
  1822. "1.0",
  1823. FCVAR_REPLICATED | FCVAR_RELEASE,
  1824. "Number of bonus upgrades to award the Ts when they detonate a gun game bomb",
  1825. true, 1.0,
  1826. true, 10.0 );
  1827. ConVar mp_dm_bonus_percent(
  1828. "mp_dm_bonus_percent",
  1829. "50",
  1830. FCVAR_REPLICATED | FCVAR_RELEASE,
  1831. "Percent of points additionally awarded when someone gets a kill with the bonus weapon during the bonus period." );
  1832. ConVar mp_display_kill_assists(
  1833. "mp_display_kill_assists",
  1834. "1",
  1835. FCVAR_REPLICATED | FCVAR_RELEASE,
  1836. "Whether to display and score player assists",
  1837. true, 0,
  1838. true, 1 );
  1839. ConVar mp_match_end_restart(
  1840. "mp_match_end_restart",
  1841. #if defined (CSTRIKE15)
  1842. "0",
  1843. #else
  1844. "0",
  1845. #endif
  1846. FCVAR_REPLICATED | FCVAR_RELEASE,
  1847. "At the end of the match, perform a restart instead of loading a new map",
  1848. true, 0,
  1849. true, 1 );
  1850. ConVar mp_match_end_changelevel(
  1851. "mp_match_end_changelevel",
  1852. "0",
  1853. FCVAR_REPLICATED | FCVAR_RELEASE,
  1854. "At the end of the match, perform a changelevel even if next map is the same",
  1855. true, 0,
  1856. true, 1 );
  1857. ConVar mp_defuser_allocation(
  1858. "mp_defuser_allocation",
  1859. "0",
  1860. FCVAR_REPLICATED | FCVAR_RELEASE,
  1861. "How to allocate defusers to CTs at start or round: 0=none, 1=random, 2=everyone",
  1862. true, 0,
  1863. true, 2 );
  1864. ConVar mp_give_player_c4(
  1865. "mp_give_player_c4",
  1866. "1",
  1867. FCVAR_REPLICATED | FCVAR_RELEASE,
  1868. "Whether this map should spawn a c4 bomb for a player or not.",
  1869. true, 0,
  1870. true, 1 );
  1871. ConVar mp_death_drop_gun(
  1872. "mp_death_drop_gun",
  1873. "1",
  1874. FCVAR_REPLICATED | FCVAR_RELEASE,
  1875. "Which gun to drop on player death: 0=none, 1=best, 2=current or best",
  1876. true, 0,
  1877. true, 2 );
  1878. ConVar mp_death_drop_c4(
  1879. "mp_death_drop_c4",
  1880. "1",
  1881. FCVAR_REPLICATED | FCVAR_RELEASE,
  1882. "Whether c4 is droppable" );
  1883. ConVar mp_death_drop_grenade(
  1884. "mp_death_drop_grenade",
  1885. "2",
  1886. FCVAR_REPLICATED | FCVAR_RELEASE,
  1887. "Which grenade to drop on player death: 0=none, 1=best, 2=current or best, 3=all grenades",
  1888. true, 0,
  1889. true, 3 );
  1890. ConVar mp_death_drop_defuser(
  1891. "mp_death_drop_defuser",
  1892. "1",
  1893. FCVAR_REPLICATED | FCVAR_RELEASE,
  1894. "Drop defuser on player death",
  1895. true, 0,
  1896. true, 1 );
  1897. ConVar mp_coop_force_join_ct(
  1898. "mp_coop_force_join_ct",
  1899. "0",
  1900. FCVAR_REPLICATED | FCVAR_RELEASE,
  1901. "If set, real players will auto join CT on join." );
  1902. ConVar mp_coopmission_mission_number(
  1903. "mp_coopmission_mission_number",
  1904. "0",
  1905. FCVAR_REPLICATED | FCVAR_RELEASE,
  1906. "Which mission the map should run after it loads." );
  1907. ConVar mp_force_pick_time(
  1908. "mp_force_pick_time",
  1909. "15",
  1910. FCVAR_REPLICATED | FCVAR_RELEASE,
  1911. "The amount of time a player has on the team screen to make a selection before being auto-teamed" );
  1912. ConVar bot_autodifficulty_threshold_low(
  1913. "bot_autodifficulty_threshold_low",
  1914. "-2.0",
  1915. FCVAR_REPLICATED | FCVAR_RELEASE,
  1916. "Lower bound below Average Human Contribution Score that a bot must be below to change its difficulty",
  1917. true, -20.0,
  1918. true, 20.0 );
  1919. ConVar bot_autodifficulty_threshold_high(
  1920. "bot_autodifficulty_threshold_high",
  1921. "5.0",
  1922. FCVAR_REPLICATED | FCVAR_RELEASE,
  1923. "Upper bound above Average Human Contribution Score that a bot must be above to change its difficulty",
  1924. true, -20.0,
  1925. true, 20.0 );
  1926. ConVar mp_weapons_allow_zeus(
  1927. "mp_weapons_allow_zeus",
  1928. "1",
  1929. FCVAR_REPLICATED | FCVAR_RELEASE,
  1930. "Determines how many Zeus purchases a player can make per round (0 to disallow, -1 to have no limit)." );
  1931. ConVar mp_weapons_allow_typecount(
  1932. "mp_weapons_allow_typecount",
  1933. "5",
  1934. FCVAR_REPLICATED | FCVAR_RELEASE,
  1935. "Determines how many purchases of each weapon type allowed per player per round (0 to disallow purchasing, -1 to have no limit)." );
  1936. ConVar mp_weapons_allow_map_placed(
  1937. "mp_weapons_allow_map_placed",
  1938. "0",
  1939. FCVAR_REPLICATED | FCVAR_RELEASE,
  1940. "If this convar is set, when a match starts, the game will not delete weapons placed in the map." );
  1941. ConVar mp_default_team_winner_no_objective(
  1942. "mp_default_team_winner_no_objective",
  1943. "-1",
  1944. FCVAR_REPLICATED | FCVAR_RELEASE,
  1945. "If the map doesn't define an objective (bomb, hostage, etc), the value of this convar will declare the winner when the time runs out in the round." );
  1946. ConVar mp_weapons_glow_on_ground(
  1947. "mp_weapons_glow_on_ground",
  1948. "0",
  1949. FCVAR_REPLICATED | FCVAR_RELEASE,
  1950. "If this convar is set, weapons on the ground will have a glow around them." );
  1951. ConVar mp_respawn_on_death_t(
  1952. "mp_respawn_on_death_t",
  1953. "0",
  1954. FCVAR_REPLICATED | FCVAR_RELEASE,
  1955. "When set to 1, terrorists will respawn after dying." );
  1956. ConVar mp_respawn_on_death_ct(
  1957. "mp_respawn_on_death_ct",
  1958. "0",
  1959. FCVAR_REPLICATED | FCVAR_RELEASE,
  1960. "When set to 1, counter-terrorists will respawn after dying." );
  1961. ConVar mp_use_respawn_waves(
  1962. "mp_use_respawn_waves",
  1963. "0",
  1964. FCVAR_REPLICATED | FCVAR_RELEASE,
  1965. "When set to 1, and that player's team is set to respawn, they will respawn in waves. If set to 2, teams will respawn when the whole team is dead." );
  1966. void ProhibitedItemsCallback( IConVar *var, const char *pOldValue, float flOldValue )
  1967. {
  1968. #ifdef GAME_DLL
  1969. if ( !CSGameRules() )
  1970. return;
  1971. ConVar *pCvar = static_cast<ConVar*>(var);
  1972. CUtlStringList pProhibitedWeapons( pCvar->GetString(), "," );
  1973. for( int i = 0; i < MAX_PROHIBITED_ITEMS; i++ )
  1974. {
  1975. if ( i < (pProhibitedWeapons.Count()) && ( GetItemSchema()->GetItemDefinition( i ) ) )
  1976. {
  1977. int nDefIndex = V_atoi( pProhibitedWeapons[ i ] );
  1978. CSGameRules()->m_arrProhibitedItemIndices.Set( i, nDefIndex );
  1979. DevMsg( "Prohibiting %s\n", GetItemSchema()->GetItemDefinition( nDefIndex )->GetDefinitionName() );
  1980. }
  1981. else
  1982. {
  1983. CSGameRules()->m_arrProhibitedItemIndices.Set( i, 0 );
  1984. }
  1985. }
  1986. #endif // GAME_DLL
  1987. }
  1988. ConVar mp_items_prohibited(
  1989. "mp_items_prohibited",
  1990. "",
  1991. FCVAR_REPLICATED | FCVAR_RELEASE,
  1992. "Set this convar to a comma-delimited list of definition indices of weapons that should be prohibited from use.",
  1993. ProhibitedItemsCallback );
  1994. void RespawnWaveTimeCTCallback( IConVar *var, const char *pOldValue, float flOldValue );
  1995. void RespawnWaveTimeTCallback( IConVar *var, const char *pOldValue, float flOldValue );
  1996. void RespawnWaveTimeCTCallback( IConVar *var, const char *pOldValue, float flOldValue )
  1997. {
  1998. #ifdef CLIENT_DLL
  1999. extern ConVar mp_respawnwavetime_ct;
  2000. float flTime = mp_respawnwavetime_ct.GetFloat();
  2001. if ( CSGameRules() )
  2002. {
  2003. float flNextRespawn = gpGlobals->curtime + flTime;
  2004. CSGameRules()->SetNextTeamRespawnWaveDelay( TEAM_CT, flNextRespawn );
  2005. }
  2006. #endif
  2007. }
  2008. void RespawnWaveTimeTCallback( IConVar *var, const char *pOldValue, float flOldValue )
  2009. {
  2010. #ifdef CLIENT_DLL
  2011. extern ConVar mp_respawnwavetime_t;
  2012. float flTime = mp_respawnwavetime_t.GetFloat();
  2013. if ( CSGameRules() )
  2014. {
  2015. float flNextRespawn = gpGlobals->curtime + flTime;
  2016. CSGameRules()->SetNextTeamRespawnWaveDelay( TEAM_TERRORIST, flNextRespawn );
  2017. }
  2018. #endif
  2019. }
  2020. ConVar mp_respawnwavetime_ct( "mp_respawnwavetime_ct", "10.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Time between respawn waves for CTs.", RespawnWaveTimeCTCallback );
  2021. ConVar mp_respawnwavetime_t( "mp_respawnwavetime_t", "10.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Time between respawn waves for Terrorists.", RespawnWaveTimeTCallback );
  2022. #ifndef CLIENT_DLL
  2023. // Announcing is always on in REL build
  2024. // training
  2025. ConVar tr_completed_training(
  2026. "tr_completed_training",
  2027. "0",
  2028. FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS,
  2029. "Whether the local player has completed the initial training portion of the training map" );
  2030. ConVar tr_best_course_time(
  2031. "tr_best_course_time",
  2032. "0",
  2033. FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS,
  2034. "The player's best time for the timed obstacle course" );
  2035. ConVar tr_valve_course_time(
  2036. "tr_valve_course_time",
  2037. "352",
  2038. FCVAR_DEVELOPMENTONLY,
  2039. "Valve's best time for the timed obstacle course" );
  2040. ConVar mp_competitive_endofmatch_extra_time(
  2041. "mp_competitive_endofmatch_extra_time",
  2042. "15",
  2043. FCVAR_RELEASE,
  2044. "After a competitive match finishes rematch voting extra time is given for rankings." );
  2045. #endif
  2046. ConVar mp_endmatch_votenextmap(
  2047. "mp_endmatch_votenextmap",
  2048. "1",
  2049. FCVAR_REPLICATED | FCVAR_RELEASE,
  2050. "Whether or not players vote for the next map at the end of the match when the final scoreboard comes up" );
  2051. ConVar mp_endmatch_votenextmap_keepcurrent(
  2052. "mp_endmatch_votenextmap_keepcurrent",
  2053. "1",
  2054. FCVAR_REPLICATED | FCVAR_RELEASE,
  2055. "If set, keeps the current map in the list of voting options. If not set, the current map will not appear in the list of voting options." );
  2056. ConVar mp_endmatch_votenextleveltime(
  2057. "mp_endmatch_votenextleveltime",
  2058. "20",
  2059. FCVAR_RELEASE,
  2060. "If mp_endmatch_votenextmap is set, players have this much time to vote on the next map at match end." );
  2061. // music controls
  2062. ConVar snd_music_boost(
  2063. "snd_music_boost",
  2064. "0",
  2065. FCVAR_REPLICATED,
  2066. "Specifies an amount to boost music volume by" );
  2067. #ifdef CLIENT_DLL
  2068. ConVar snd_music_selection(
  2069. "snd_music_selection",
  2070. "1",
  2071. FCVAR_ARCHIVE,
  2072. "Tracking rotating music for players with no music packs equipped.");
  2073. extern ConVar cl_borrow_music_from_player_index;
  2074. #endif
  2075. ConVar sv_endmatch_item_drop_interval(
  2076. "sv_endmatch_item_drop_interval",
  2077. "1.0",
  2078. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  2079. "The time between drops on the end match scoreboard " );
  2080. ConVar sv_endmatch_item_drop_interval_rare(
  2081. "sv_endmatch_item_drop_interval_rare",
  2082. "1.0",
  2083. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  2084. "The time between drops on the end match scoreboard for rare items " );
  2085. ConVar sv_endmatch_item_drop_interval_mythical(
  2086. "sv_endmatch_item_drop_interval_mythical",
  2087. "1.25",
  2088. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  2089. "The time between drops on the end match scoreboard for mythical items " );
  2090. ConVar sv_endmatch_item_drop_interval_legendary(
  2091. "sv_endmatch_item_drop_interval_legendary",
  2092. "2.0",
  2093. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  2094. "The time between drops on the end match scoreboard for legendary items " );
  2095. ConVar sv_endmatch_item_drop_interval_ancient(
  2096. "sv_endmatch_item_drop_interval_ancient",
  2097. "3.5",
  2098. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  2099. "The time between drops on the end match scoreboard for ancient items " );
  2100. // bot difficulty tracking per user input device
  2101. ConVar sv_compute_per_bot_difficulty(
  2102. "sv_compute_per_bot_difficulty",
  2103. "0",
  2104. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  2105. "0 = compute all bot difficulties equally, 1 = compute unique bot difficulty for each bot " );
  2106. ConVar sv_show_bot_difficulty_in_name(
  2107. "sv_show_bot_difficulty_in_name",
  2108. "0",
  2109. FCVAR_REPLICATED,
  2110. "0 = hide bot difficulty in bot name, 1 = show bot difficulty in bot name" );
  2111. ConVar sv_bot_difficulty_kbm(
  2112. "sv_bot_difficulty_kbm",
  2113. "0",
  2114. FCVAR_REPLICATED | FCVAR_HIDDEN,
  2115. "Bot difficulty while playing with Keyboard/Mouse device" );
  2116. ConVar sv_bot_difficulty_gamepad(
  2117. "sv_bot_difficulty_gamepad",
  2118. "0",
  2119. FCVAR_REPLICATED | FCVAR_HIDDEN,
  2120. "Bot difficulty while playing with Gamepad device" );
  2121. ConVar sv_bot_difficulty_ps3move(
  2122. "sv_bot_difficulty_ps3move",
  2123. "0",
  2124. FCVAR_REPLICATED | FCVAR_HIDDEN,
  2125. "Bot difficulty while playing with PS3Move device" );
  2126. ConVar sv_bot_difficulty_hydra(
  2127. "sv_bot_difficulty_hydra",
  2128. "0",
  2129. FCVAR_REPLICATED | FCVAR_HIDDEN,
  2130. "Bot difficulty while playing with Hydra device" );
  2131. ConVar sv_bot_difficulty_sharpshooter(
  2132. "sv_bot_difficulty_sharpshooter",
  2133. "0",
  2134. FCVAR_REPLICATED | FCVAR_HIDDEN,
  2135. "Bot difficulty while playing with SharpShooter device" );
  2136. ConVar sv_competitive_official_5v5( "sv_competitive_official_5v5",
  2137. "0",
  2138. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2139. "Enable to force the server to show 5v5 scoreboards and allows spectators to see characters through walls." );
  2140. ConVar sv_kick_ban_duration( "sv_kick_ban_duration",
  2141. "15",
  2142. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2143. "How long should a kick ban from the server should last (in minutes)" );
  2144. ConVar sv_disable_immunity_alpha( "sv_disable_immunity_alpha",
  2145. "0",
  2146. FCVAR_REPLICATED | FCVAR_RELEASE,
  2147. "If set, clients won't slam the player model render settings each frame for immunity [mod authors use this]" );
  2148. #ifdef CLIENT_DLL
  2149. ConVar cl_bot_difficulty_kbm(
  2150. "cl_bot_difficulty_kbm",
  2151. "0",
  2152. FCVAR_HIDDEN,
  2153. "Bot difficulty while playing with Keyboard/Mouse device" );
  2154. ConVar cl_bot_difficulty_gamepad(
  2155. "cl_bot_difficulty_gamepad",
  2156. "0",
  2157. FCVAR_HIDDEN,
  2158. "Bot difficulty while playing with Gamepad device" );
  2159. ConVar cl_bot_difficulty_ps3move(
  2160. "cl_bot_difficulty_ps3move",
  2161. "0",
  2162. FCVAR_HIDDEN,
  2163. "Bot difficulty while playing with PS3Move device" );
  2164. ConVar cl_bot_difficulty_hydra(
  2165. "cl_bot_difficulty_hydra",
  2166. "0",
  2167. FCVAR_HIDDEN,
  2168. "Bot difficulty while playing with Hydra device" );
  2169. ConVar cl_bot_difficulty_sharpshooter(
  2170. "cl_bot_difficulty_sharpshooter",
  2171. "0",
  2172. FCVAR_HIDDEN,
  2173. "Bot difficulty while playing with SharpShooter device" );
  2174. #endif
  2175. // Set game rules to allow all clients to talk to each other.
  2176. // Muted players still can't talk to each other.
  2177. ConVar sv_alltalk( "sv_alltalk", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Deprecated. Replaced with sv_talk_enemy_dead and sv_talk_enemy_living." );
  2178. // [jason] Can the dead speak to the living?
  2179. ConVar sv_deadtalk( "sv_deadtalk", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Dead players can speak (voice, text) to the living" );
  2180. // [jason] Override that removes all chat restrictions, including those for spectators
  2181. ConVar sv_full_alltalk( "sv_full_alltalk", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Any player (including Spectator team) can speak to any other player" );
  2182. ConVar sv_talk_enemy_dead( "sv_talk_enemy_dead", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Dead players can hear all dead enemy communication (voice, chat)" );
  2183. ConVar sv_talk_enemy_living( "sv_talk_enemy_living", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Living players can hear all living enemy communication (voice, chat)" );
  2184. #ifdef GAME_DLL
  2185. ConVar sv_auto_full_alltalk_during_warmup_half_end( "sv_auto_full_alltalk_during_warmup_half_end", "1", FCVAR_RELEASE, "When enabled will automatically turn on full all talk mode in warmup, at halftime and at the end of the match" );
  2186. #endif
  2187. ConVar sv_spec_hear( "sv_spec_hear", "1", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Determines who spectators can hear: 0: only spectators; 1: all players; 2: spectated team; 3: self only; 4: nobody" );
  2188. ConVar mp_c4timer(
  2189. "mp_c4timer",
  2190. "40",
  2191. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2192. "how long from when the C4 is armed until it blows",
  2193. true, 10, // min value
  2194. false, 0 // max value
  2195. );
  2196. namespace SpecHear
  2197. {
  2198. enum Type
  2199. {
  2200. OnlySpectators = 0,
  2201. AllPlayers = 1,
  2202. SpectatedTeam = 2,
  2203. Self = 3,
  2204. Nobody = 4,
  2205. };
  2206. }
  2207. // NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
  2208. char *sTeamNames[] =
  2209. {
  2210. "Unassigned",
  2211. "Spectator",
  2212. "TERRORIST",
  2213. "CT"
  2214. };
  2215. #ifdef CLIENT_DLL
  2216. ConVar cl_autowepswitch(
  2217. "cl_autowepswitch",
  2218. "1",
  2219. FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE| FCVAR_SS | FCVAR_USERINFO,
  2220. "Automatically switch to picked up weapons (if more powerful)" );
  2221. ConVar cl_use_opens_buy_menu(
  2222. "cl_use_opens_buy_menu",
  2223. "1",
  2224. FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE| FCVAR_SS | FCVAR_USERINFO,
  2225. "Pressing the +use key will open the buy menu if in a buy zone (just as if you pressed the 'buy' key)." );
  2226. ConVar cl_autohelp(
  2227. "cl_autohelp",
  2228. "1",
  2229. FCVAR_ARCHIVE | FCVAR_USERINFO,
  2230. "Auto-help" );
  2231. #else
  2232. // longest the intermission can last, in seconds
  2233. #define MAX_INTERMISSION_TIME 120
  2234. // Falling damage stuff.
  2235. #define CS_PLAYER_FATAL_FALL_SPEED 1000 // approx 60 feet
  2236. #define CS_PLAYER_MAX_SAFE_FALL_SPEED 580 // approx 20 feet
  2237. #define CS_DAMAGE_FOR_FALL_SPEED ((float)100 / ( CS_PLAYER_FATAL_FALL_SPEED - CS_PLAYER_MAX_SAFE_FALL_SPEED )) // damage per unit per second.
  2238. // These entities are preserved each round restart. The rest are removed and recreated.
  2239. static const char *s_PreserveEnts[] =
  2240. {
  2241. "ai_network",
  2242. "ai_hint",
  2243. "cs_gamerules",
  2244. "cs_team_manager",
  2245. "cs_player_manager",
  2246. "env_soundscape",
  2247. "env_soundscape_proxy",
  2248. "env_soundscape_triggerable",
  2249. "env_sun",
  2250. "env_wind",
  2251. "env_fog_controller",
  2252. "env_tonemap_controller",
  2253. "env_cascade_light",
  2254. "func_brush",
  2255. "func_wall",
  2256. "func_buyzone",
  2257. "func_illusionary",
  2258. "func_hostage_rescue",
  2259. "func_bomb_target",
  2260. "infodecal",
  2261. "info_projecteddecal",
  2262. "info_node",
  2263. "info_target",
  2264. "info_node_hint",
  2265. "info_player_counterterrorist",
  2266. "info_player_terrorist",
  2267. "info_enemy_terrorist_spawn",
  2268. "info_deathmatch_spawn",
  2269. "info_armsrace_counterterrorist",
  2270. "info_armsrace_terrorist",
  2271. "info_map_parameters",
  2272. "keyframe_rope",
  2273. "move_rope",
  2274. "info_ladder",
  2275. "player",
  2276. "point_viewcontrol",
  2277. "point_viewcontrol_multiplayer",
  2278. "scene_manager",
  2279. "shadow_control",
  2280. "sky_camera",
  2281. "soundent",
  2282. "trigger_soundscape",
  2283. "viewmodel",
  2284. "predicted_viewmodel",
  2285. "worldspawn",
  2286. "point_devshot_camera",
  2287. "logic_choreographed_scene",
  2288. "cfe_player_decal", // persistent player spray decals must be preserved
  2289. //"logic_auto", // preserving this will break all of the maps who currently rely on it getting destroyed each time the map entities are recreated
  2290. "info_bomb_target_hint_A",
  2291. "info_bomb_target_hint_B",
  2292. "info_hostage_rescue_zone_hint",
  2293. // for the training map
  2294. "generic_actor",
  2295. "vote_controller",
  2296. "wearable_item",
  2297. "point_hiding_spot",
  2298. "game_coopmission_manager",
  2299. "chicken",
  2300. "", // END Marker
  2301. };
  2302. // --------------------------------------------------------------------------------------------------- //
  2303. // Voice helper
  2304. // --------------------------------------------------------------------------------------------------- //
  2305. class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
  2306. {
  2307. public:
  2308. virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity )
  2309. {
  2310. if ( pListener == NULL || pTalker == NULL )
  2311. return false;
  2312. if ( !CSGameRules() )
  2313. return false;
  2314. return CSGameRules()->CanPlayerHearTalker( pListener, pTalker, false );
  2315. }
  2316. };
  2317. CVoiceGameMgrHelper g_VoiceGameMgrHelper;
  2318. IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
  2319. // --------------------------------------------------------------------------------------------------- //
  2320. // Globals.
  2321. // --------------------------------------------------------------------------------------------------- //
  2322. ConVar dev_reportmoneychanges(
  2323. "dev_reportmoneychanges",
  2324. "0",
  2325. FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY,
  2326. "Displays money account changes for players in the console" );
  2327. ConVar mp_roundtime(
  2328. "mp_roundtime",
  2329. "5",
  2330. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2331. "How many minutes each round takes.",
  2332. true, 1, // min value
  2333. true, 60 // max value
  2334. );
  2335. ConVar mp_roundtime_deployment(
  2336. "mp_roundtime_deployment",
  2337. "5",
  2338. FCVAR_RELEASE,
  2339. "How many minutes deployment for coop mission takes.",
  2340. true, 1, // min value
  2341. true, 15 // max value
  2342. );
  2343. ConVar mp_roundtime_hostage(
  2344. "mp_roundtime_hostage",
  2345. "0",
  2346. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2347. "How many minutes each round of Hostage Rescue takes. If 0 then use mp_roundtime instead.",
  2348. true, 0, // min value
  2349. true, 60 // max value
  2350. );
  2351. ConVar mp_roundtime_defuse(
  2352. "mp_roundtime_defuse",
  2353. "0",
  2354. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2355. "How many minutes each round of Bomb Defuse takes. If 0 then use mp_roundtime instead.",
  2356. true, 0, // min value
  2357. true, 60 // max value
  2358. );
  2359. ConVar mp_freezetime(
  2360. "mp_freezetime",
  2361. "6",
  2362. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2363. "how many seconds to keep players frozen when the round starts",
  2364. true, 0, // min value
  2365. true, 60 // max value
  2366. );
  2367. ConVar mp_limitteams(
  2368. "mp_limitteams",
  2369. "2",
  2370. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  2371. "Max # of players 1 team can have over another (0 disables check)",
  2372. true, 0, // min value
  2373. true, 30 // max value
  2374. );
  2375. ConVar mp_tkpunish(
  2376. "mp_tkpunish",
  2377. "0",
  2378. FCVAR_REPLICATED | FCVAR_RELEASE,
  2379. "Will TK'ers and team damagers be punished in the next round? {0=no, 1=yes}" );
  2380. ConVar mp_autokick(
  2381. "mp_autokick",
  2382. "1",
  2383. FCVAR_REPLICATED | FCVAR_RELEASE,
  2384. "Kick idle/team-killing/team-damaging players" );
  2385. ConVar mp_spawnprotectiontime(
  2386. "mp_spawnprotectiontime",
  2387. "5",
  2388. FCVAR_REPLICATED | FCVAR_RELEASE,
  2389. "Kick players who team-kill within this many seconds of a round restart." );
  2390. ConVar mp_td_spawndmgthreshold(
  2391. "mp_td_spawndmgthreshold",
  2392. "50",
  2393. FCVAR_REPLICATED | FCVAR_RELEASE,
  2394. "The damage threshold players have to exceed at the start of the round to be warned/kick." );
  2395. ConVar mp_td_dmgtowarn(
  2396. "mp_td_dmgtowarn",
  2397. "200",
  2398. FCVAR_REPLICATED | FCVAR_RELEASE,
  2399. "The damage threshhold players have to exceed in a match to get warned that they are about to be kicked." );
  2400. ConVar mp_td_dmgtokick(
  2401. "mp_td_dmgtokick",
  2402. "300",
  2403. FCVAR_REPLICATED | FCVAR_RELEASE,
  2404. "The damage threshhold players have to exceed in a match to get kicked." );
  2405. ConVar mp_humanteam(
  2406. "mp_humanteam",
  2407. "any",
  2408. FCVAR_REPLICATED | FCVAR_RELEASE,
  2409. "Restricts human players to a single team {any, CT, T}" );
  2410. ConVar mp_guardian_special_kills_needed(
  2411. "mp_guardian_special_kills_needed",
  2412. "10",
  2413. FCVAR_REPLICATED | FCVAR_RELEASE,
  2414. "The number of kills needed with a specific weapon." );
  2415. ConVar mp_guardian_special_weapon_needed(
  2416. "mp_guardian_special_weapon_needed",
  2417. "awp",
  2418. FCVAR_REPLICATED | FCVAR_RELEASE,
  2419. "The weapon that needs to be used to increment the kills needed to complete the mission." );
  2420. ConVar mp_guardian_player_dist_min(
  2421. "mp_guardian_player_dist_min",
  2422. "1300",
  2423. FCVAR_REPLICATED | FCVAR_RELEASE,
  2424. "The distance at which we start to warn a player when they are too far from the guarded bombsite." );
  2425. ConVar mp_guardian_player_dist_max(
  2426. "mp_guardian_player_dist_max",
  2427. "2000",
  2428. FCVAR_REPLICATED | FCVAR_RELEASE,
  2429. "The maximum distance a player is allowed to get from the bombsite before they're killed." );
  2430. ConVar mp_guardian_bot_money_per_wave(
  2431. "mp_guardian_bot_money_per_wave",
  2432. "800",
  2433. FCVAR_REPLICATED | FCVAR_RELEASE,
  2434. "The amount of money bots get time each wave the players complete. This # is absolute and not additive, the money is set to (this)x(wave#) for each bot on each wave." );
  2435. ConVar mp_ignore_round_win_conditions(
  2436. "mp_ignore_round_win_conditions",
  2437. "0",
  2438. FCVAR_REPLICATED | FCVAR_RELEASE,
  2439. "Ignore conditions which would end the current round" );
  2440. ConVar mp_dm_time_between_bonus_min(
  2441. "mp_dm_time_between_bonus_min",
  2442. "30",
  2443. FCVAR_REPLICATED | FCVAR_RELEASE,
  2444. "Minimum time a bonus time will start after the round start or after the last bonus (in seconds)" );
  2445. ConVar mp_dm_time_between_bonus_max(
  2446. "mp_dm_time_between_bonus_max",
  2447. "40",
  2448. FCVAR_REPLICATED | FCVAR_RELEASE,
  2449. "Maximum time a bonus time will start after the round start or after the last bonus (in seconds)" );
  2450. ConVar mp_dm_bonus_length_min(
  2451. "mp_dm_bonus_length_min",
  2452. "30",
  2453. FCVAR_REPLICATED | FCVAR_RELEASE,
  2454. "Minimum time the bonus time will last (in seconds)" );
  2455. ConVar mp_dm_bonus_length_max(
  2456. "mp_dm_bonus_length_max",
  2457. "30",
  2458. FCVAR_REPLICATED | FCVAR_RELEASE,
  2459. "Maximum time the bonus time will last (in seconds)" );
  2460. ConVar mp_damage_scale_ct_body(
  2461. "mp_damage_scale_ct_body",
  2462. "1.0",
  2463. FCVAR_REPLICATED,
  2464. "Scales the damage a CT player takes by this much when they take damage in the body. (1 == 100%, 0.5 == 50%)" );
  2465. ConVar mp_damage_scale_ct_head(
  2466. "mp_damage_scale_ct_head",
  2467. "1.0",
  2468. FCVAR_REPLICATED,
  2469. "Scales the damage a CT player takes by this much when they take damage in the head (1 == 100%, 0.5 == 50%). REMEMBER! headshots do 4x the damage of the body before this scaler is applied." );
  2470. ConVar mp_damage_scale_t_body(
  2471. "mp_damage_scale_t_body",
  2472. "1.0",
  2473. FCVAR_REPLICATED,
  2474. "Scales the damage a T player takes by this much when they take damage in the body. (1 == 100%, 0.5 == 50%)" );
  2475. ConVar mp_damage_scale_t_head(
  2476. "mp_damage_scale_t_head",
  2477. "1.0",
  2478. FCVAR_REPLICATED,
  2479. "Scales the damage a T player takes by this much when they take damage in the head (1 == 100%, 0.5 == 50%). REMEMBER! headshots do 4x the damage of the body before this scaler is applied." );
  2480. ConVar mp_player_healthbuffer_decay_rate(
  2481. "mp_player_healthbuffer_decay_rate",
  2482. "0",
  2483. FCVAR_REPLICATED,
  2484. "When a player has buffer health, this is how fast it ticks down." );
  2485. ConCommand EndRound( "endround", &CCSGameRules::EndRound, "End the current round.", FCVAR_CHEAT );
  2486. void cc_ReportEntitiesInEntList( const CCommand& args )
  2487. {
  2488. //int nNumEnts = gEntList.NumberOfEntities();
  2489. for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt( pClass ) )
  2490. {
  2491. if ( pClass /*&& !pClass->IsDormant()*/ )
  2492. {
  2493. Msg( "%s\n", pClass->GetClassname() );
  2494. }
  2495. }
  2496. }
  2497. static ConCommand ent_list_report( "ent_list_report", cc_ReportEntitiesInEntList, "Reports all list of all entities in a map, one by one" );
  2498. //number_of_entities = gEntList.NumberOfEntities();
  2499. CON_COMMAND_F ( tv_time_remaining, "Print remaining tv broadcast time", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS )
  2500. {
  2501. #ifdef GAME_DLL
  2502. if ( HLTVDirector() && HLTVDirector()->IsActive() )
  2503. {
  2504. CEngineHltvInfo_t engineHltv;
  2505. if ( engine->GetEngineHltvInfo( engineHltv ) &&
  2506. engineHltv.m_bBroadcastActive && ( engineHltv.m_numClients > 0 ) )
  2507. {
  2508. if ( CSGameRules()->GetMatch()->GetPhase() != GAMEPHASE_MATCH_ENDED )
  2509. {
  2510. ConMsg( "GOTV spectators are attached. Match is still in progress.\n" );
  2511. }
  2512. else
  2513. {
  2514. float flTimeRemaining = ( CSGameRules()->GetIntermissionStartTime() + HLTVDirector()->GetDelay() + 5.0 ) - gpGlobals->curtime;
  2515. if ( flTimeRemaining > 0 )
  2516. {
  2517. ConMsg("GOTV spectators are attached. %f seconds remaining to broadcast.\n", ( CSGameRules()->GetIntermissionStartTime() + HLTVDirector()->GetDelay() + 5.0 ) - gpGlobals->curtime );
  2518. }
  2519. else
  2520. {
  2521. ConMsg( "GOTV spectators are attached. GOTV Broadcast is complete.\n" );
  2522. }
  2523. }
  2524. }
  2525. else
  2526. {
  2527. ConMsg( "There are no GOTV spectators attached.\n" );
  2528. }
  2529. }
  2530. else
  2531. #endif
  2532. ConMsg( "GOTV is not active.\n" );
  2533. }
  2534. CON_COMMAND_F ( reset_expo, "Reset player scores, player controls, team scores, and end the round", FCVAR_CHEAT | FCVAR_GAMEDLL )
  2535. {
  2536. CSGameRules()->ResetForTradeshow();
  2537. }
  2538. CON_COMMAND_F ( tweak_ammo_impulses, "Allow real-time tweaking of the ammo impulse values.", FCVAR_CHEAT | FCVAR_GAMEDLL)
  2539. {
  2540. for (int ii=0; ii<MAX_AMMO_TYPES; ++ii )
  2541. {
  2542. GetCSAmmoDef()->m_AmmoType[ii].pPhysicsForceImpulse = USE_CVAR;
  2543. }
  2544. }
  2545. // --------------------------------------------------------------------------------------------------- //
  2546. // Contribution score control values
  2547. // --------------------------------------------------------------------------------------------------- //
  2548. ConVar score_default(
  2549. "score_default",
  2550. "1000",
  2551. FCVAR_NONE,
  2552. "Default points for a new user" );
  2553. ConVar score_kill_enemy_bonus(
  2554. "score_kill_enemy_bonus",
  2555. "0",
  2556. FCVAR_NONE,
  2557. "Points awarded for killing an enemy" );
  2558. ConVar score_damage(
  2559. "score_damage",
  2560. "1",
  2561. FCVAR_NONE,
  2562. "Points awarded for each point of damage to an enemy" );
  2563. ConVar score_ff_damage(
  2564. "score_ff_damage",
  2565. "1",
  2566. FCVAR_NONE,
  2567. "Penalty awarded for each point of damage to a teammate" );
  2568. ConVar score_team_damage_bonus(
  2569. "score_team_damage_bonus",
  2570. "1",
  2571. FCVAR_NONE,
  2572. "Points awarded for each point of damage a nearby (in same zone) teammate does to enemies" );
  2573. ConVar score_planted_bomb_proximity_damage_bonus(
  2574. "score_planted_bomb_proximity_damage_bonus",
  2575. "1",
  2576. FCVAR_NONE,
  2577. "Points awarded for damaging enemy near planted bomb" );
  2578. ConVar score_planted_bomb_proximity_damage_radius_inner(
  2579. "score_planted_bomb_proximity_damage_radius_inner",
  2580. "120",
  2581. FCVAR_NONE,
  2582. "Inner radius (full bonus) for doing damage near planted bomb" );
  2583. ConVar score_planted_bomb_proximity_damage_radius_outer(
  2584. "score_planted_bomb_proximity_damage_radius_outer",
  2585. "600",
  2586. FCVAR_NONE,
  2587. "Outer radius (zero bonus) for doing damage near planted bomb" );
  2588. ConVar score_hostage_proximity_damage_bonus(
  2589. "score_hostage_proximity_damage_bonus",
  2590. "1",
  2591. FCVAR_NONE,
  2592. "Points awarded for damaging enemy near live hostage" );
  2593. ConVar score_hostage_proximity_damage_radius_inner(
  2594. "score_hostage_proximity_damage_radius_inner",
  2595. "120",
  2596. FCVAR_NONE,
  2597. "Inner radius (full bonus) for doing damage near hostage" );
  2598. ConVar score_hostage_proximity_damage_radius_outer(
  2599. "score_hostage_proximity_damage_radius_outer",
  2600. "600",
  2601. FCVAR_NONE,
  2602. "Outer radius (zero bonus) for doing damage near hostage" );
  2603. ConVar score_dropped_bomb_proximity_damage_bonus(
  2604. "score_dropped_bomb_proximity_damage_bonus",
  2605. "1",
  2606. FCVAR_NONE,
  2607. "Points awarded for damaging enemy near dropped bomb" );
  2608. ConVar score_dropped_bomb_proximity_damage_bonus_radius_inner(
  2609. "score_dropped_bomb_proximity_damage_bonus_radius_inner",
  2610. "120",
  2611. FCVAR_NONE,
  2612. "Inner radius (full bonus) for doing damage near dropped bomb" );
  2613. ConVar score_dropped_bomb_proximity_damage_bonus_radius_outer(
  2614. "score_dropped_bomb_proximity_damage_bonus_radius_outer",
  2615. "600",
  2616. FCVAR_NONE,
  2617. "Outer radius (zero bonus) for doing damage near dropped bomb" );
  2618. ConVar score_dropped_defuser_proximity_damage_bonus(
  2619. "score_dropped_defuser_proximity_damage_bonus",
  2620. "1",
  2621. FCVAR_NONE,
  2622. "Points awarded for damaging enemy near dropped defuser" );
  2623. ConVar score_dropped_defuser_proximity_damage_radius_inner(
  2624. "score_dropped_defuser_proximity_damage_radius_inner",
  2625. "120",
  2626. FCVAR_NONE,
  2627. "Inner radius (full bonus) for doing damage near dropped defuser" );
  2628. ConVar score_dropped_defuser_proximity_damage_radius_outer(
  2629. "score_dropped_defuser_proximity_damage_radius_outer",
  2630. "600",
  2631. FCVAR_NONE,
  2632. "Outer radius (zero bonus) for doing damage near dropped defuser" );
  2633. ConVar score_bomb_plant_bonus(
  2634. "score_bomb_plant_bonus",
  2635. "200",
  2636. FCVAR_NONE,
  2637. "Points awarded for planting or assisting with planting the bomb" );
  2638. ConVar score_bomb_plant_radius_inner(
  2639. "score_bomb_plant_radius_inner",
  2640. "120",
  2641. FCVAR_NONE,
  2642. "Inner radius (full bonus) for planting or assisting with planting the bomb" );
  2643. ConVar score_bomb_plant_radius_outer(
  2644. "score_bomb_plant_radius_outer",
  2645. "600",
  2646. FCVAR_NONE,
  2647. "Outer radius (zero bonus) for planting or assisting with planting the bomb" );
  2648. ConVar score_bomb_defuse_bonus(
  2649. "score_bomb_defuse_bonus",
  2650. "400",
  2651. FCVAR_NONE,
  2652. "Points awarded for defusing or assisting with defuse of bomb" );
  2653. ConVar score_bomb_defuse_radius_inner(
  2654. "score_bomb_defuse_radius_inner",
  2655. "120",
  2656. FCVAR_NONE,
  2657. "Inner radius (full bonus) for defusing or assisting with defusing the bomb" );
  2658. ConVar score_bomb_defuse_radius_outer(
  2659. "score_bomb_defuse_radius_outer",
  2660. "600",
  2661. FCVAR_NONE,
  2662. "Outer radius (zero bonus) for defusing or assisting with defseing the bomb" );
  2663. ConVar score_hostage_rescue_bonus(
  2664. "score_hostage_rescue_bonus",
  2665. "100",
  2666. FCVAR_NONE,
  2667. "Points awarded for rescuing a hostage" );
  2668. ConVar score_hostage_rescue_radius_inner(
  2669. "score_hostage_rescue_radius_inner",
  2670. "120",
  2671. FCVAR_NONE,
  2672. "Inner radius (full bonus) for rescuing hostage" );
  2673. ConVar score_hostage_rescue_radius_outer(
  2674. "score_hostage_rescue_radius_outer",
  2675. "600",
  2676. FCVAR_NONE,
  2677. "Outer radius (zero bonus) for rescuing hostage" );
  2678. ConVar score_hostage_damage_penalty(
  2679. "score_hostage_damage_penalty",
  2680. "2",
  2681. FCVAR_NONE,
  2682. "Penalty for damaging a hostage" );
  2683. ConVar score_blind_enemy_bonus(
  2684. "score_blind_enemy_bonus",
  2685. "10",
  2686. FCVAR_NONE,
  2687. "Bonus for blinding enemy players" );
  2688. ConVar score_blind_friendly_penalty(
  2689. "score_blind_friendly_penalty",
  2690. "10",
  2691. FCVAR_NONE,
  2692. "Penalty for blinding friendly players" );
  2693. ConVar score_typical_good_score(
  2694. "score_typical_good_score",
  2695. "5",
  2696. FCVAR_NONE,
  2697. "An average good score for use in funfacts" );
  2698. ConVar contributionscore_assist(
  2699. "contributionscore_assist",
  2700. "1",
  2701. FCVAR_NONE,
  2702. "amount of contribution score added for an assist" );
  2703. ConVar contributionscore_kill(
  2704. "contributionscore_kill",
  2705. "2",
  2706. FCVAR_NONE,
  2707. "amount of contribution score added for a kill" );
  2708. ConVar contributionscore_objective_kill(
  2709. "contributionscore_objective_kill",
  2710. "3",
  2711. FCVAR_NONE,
  2712. "amount of contribution score added for an objective related kill" );
  2713. ConVar contributionscore_hostage_rescue_minor(
  2714. "contributionscore_hostage_rescue_minor",
  2715. "1",
  2716. FCVAR_NONE,
  2717. "amount of contribution score added to all alive CTs per hostage rescued" );
  2718. ConVar contributionscore_hostage_rescue_major(
  2719. "contributionscore_hostage_rescue_major",
  2720. "3",
  2721. FCVAR_NONE,
  2722. "amount of contribution score added to rescuer per hostage rescued" );
  2723. ConVar contributionscore_bomb_defuse_minor(
  2724. "contributionscore_bomb_defuse_minor",
  2725. "1",
  2726. FCVAR_NONE,
  2727. "amount of contribution score for defusing a bomb after eliminating enemy team" );
  2728. ConVar contributionscore_bomb_defuse_major(
  2729. "contributionscore_bomb_defuse_major",
  2730. "3",
  2731. FCVAR_NONE,
  2732. "amount of contribution score for defusing a bomb while at least one enemy remains alive" );
  2733. ConVar contributionscore_bomb_planted(
  2734. "contributionscore_bomb_planted",
  2735. "2",
  2736. FCVAR_NONE,
  2737. "amount of contribution score for planting a bomb" );
  2738. ConVar contributionscore_bomb_exploded(
  2739. "contributionscore_bomb_exploded",
  2740. "1",
  2741. FCVAR_NONE,
  2742. "amount of contribution score awarded to bomb planter and terrorists remaining alive if bomb explosion wins the round" );
  2743. ConVar contributionscore_suicide(
  2744. "contributionscore_suicide",
  2745. "-2",
  2746. FCVAR_NONE,
  2747. "amount of contribution score for a suicide, normally negative" );
  2748. ConVar contributionscore_team_kill(
  2749. "contributionscore_team_kill",
  2750. "-2",
  2751. FCVAR_NONE,
  2752. "amount of contribution score for a team kill, normally negative" );
  2753. ConVar contributionscore_hostage_kill(
  2754. "contributionscore_hostage_kill",
  2755. "-2",
  2756. FCVAR_NONE,
  2757. "amount of contribution score for killing a hostage, normally negative" );
  2758. // --------------------------------------------------------------------------------------------------- //
  2759. // Global helper functions.
  2760. // --------------------------------------------------------------------------------------------------- //
  2761. void InitBodyQue(void)
  2762. {
  2763. // FIXME: Make this work
  2764. }
  2765. Vector DropToGround(
  2766. CBaseEntity *pMainEnt,
  2767. const Vector &vPos,
  2768. const Vector &vMins,
  2769. const Vector &vMaxs )
  2770. {
  2771. trace_t trace;
  2772. UTIL_TraceHull( vPos, vPos + Vector( 0, 0, -500 ), vMins, vMaxs, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &trace );
  2773. return trace.endpos;
  2774. }
  2775. //-----------------------------------------------------------------------------
  2776. // Purpose: This function can be used to find a valid placement location for an entity.
  2777. // Given an origin to start looking from and a minimum radius to place the entity at,
  2778. // it will sweep out a circle around vOrigin and try to find a valid spot (on the ground)
  2779. // where mins and maxs will fit.
  2780. // Input : *pMainEnt - Entity to place
  2781. // &vOrigin - Point to search around
  2782. // fRadius - Radius to search within
  2783. // nTries - Number of tries to attempt
  2784. // &mins - mins of the Entity
  2785. // &maxs - maxs of the Entity
  2786. // &outPos - Return point
  2787. // Output : Returns true and fills in outPos if it found a spot.
  2788. //-----------------------------------------------------------------------------
  2789. bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround, unsigned int mask, ITraceFilter *pFilter )
  2790. {
  2791. // This function moves the box out in each dimension in each step trying to find empty space like this:
  2792. //
  2793. // X
  2794. // X X
  2795. // Step 1: X Step 2: XXX Step 3: XXXXX
  2796. // X X
  2797. // X
  2798. //
  2799. CTraceFilterSimple defaultFilter( pMainEnt, COLLISION_GROUP_NONE );
  2800. if ( !pFilter )
  2801. {
  2802. pFilter = &defaultFilter;
  2803. }
  2804. Vector mins, maxs;
  2805. if ( pMainEnt )
  2806. {
  2807. pMainEnt->CollisionProp()->WorldSpaceAABB( &mins, &maxs );
  2808. mins -= pMainEnt->GetAbsOrigin();
  2809. maxs -= pMainEnt->GetAbsOrigin();
  2810. }
  2811. else
  2812. {
  2813. mins = VEC_HULL_MIN;
  2814. maxs = VEC_HULL_MAX;
  2815. }
  2816. // Put some padding on their bbox.
  2817. float flPadSize = 5;
  2818. Vector vTestMins = mins - Vector( flPadSize, flPadSize, flPadSize );
  2819. Vector vTestMaxs = maxs + Vector( flPadSize, flPadSize, flPadSize );
  2820. // First test the starting origin.
  2821. if ( UTIL_IsSpaceEmpty( pMainEnt, vOrigin + vTestMins, vOrigin + vTestMaxs, mask, pFilter ) )
  2822. {
  2823. if ( bDropToGround )
  2824. {
  2825. outPos = DropToGround( pMainEnt, vOrigin, vTestMins, vTestMaxs );
  2826. }
  2827. else
  2828. {
  2829. outPos = vOrigin;
  2830. }
  2831. return true;
  2832. }
  2833. Vector vDims = vTestMaxs - vTestMins;
  2834. // Keep branching out until we get too far.
  2835. int iCurIteration = 0;
  2836. int nMaxIterations = 15;
  2837. int offset = 0;
  2838. do
  2839. {
  2840. for ( int iDim=0; iDim < 3; iDim++ )
  2841. {
  2842. float flCurOffset = offset * vDims[iDim];
  2843. for ( int iSign=0; iSign < 2; iSign++ )
  2844. {
  2845. Vector vBase = vOrigin;
  2846. vBase[iDim] += (iSign*2-1) * flCurOffset;
  2847. if ( UTIL_IsSpaceEmpty( pMainEnt, vBase + vTestMins, vBase + vTestMaxs, mask, pFilter ) )
  2848. {
  2849. // Ensure that there is a clear line of sight from the spawnpoint entity to the actual spawn point.
  2850. // (Useful for keeping things from spawning behind walls near a spawn point)
  2851. trace_t tr;
  2852. UTIL_TraceLine( vOrigin, vBase, mask, pFilter, &tr );
  2853. if ( tr.fraction != 1.0 )
  2854. {
  2855. continue;
  2856. }
  2857. if ( bDropToGround )
  2858. outPos = DropToGround( pMainEnt, vBase, vTestMins, vTestMaxs );
  2859. else
  2860. outPos = vBase;
  2861. return true;
  2862. }
  2863. }
  2864. }
  2865. ++offset;
  2866. } while ( iCurIteration++ < nMaxIterations );
  2867. // Warning( "EntityPlacementTest for ent %d:%s failed!\n", pMainEnt->entindex(), pMainEnt->GetClassname() );
  2868. return false;
  2869. }
  2870. // Returns the number of human spectators in the game
  2871. int UTIL_SpectatorsInGame( void )
  2872. {
  2873. int iCount = 0;
  2874. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2875. {
  2876. CCSPlayer *entity = CCSPlayer::Instance( i );
  2877. if ( entity && !FNullEnt( entity->edict() ) )
  2878. {
  2879. if ( FStrEq( entity->GetPlayerName(), "" ) )
  2880. continue;
  2881. if ( FBitSet( entity->GetFlags(), FL_FAKECLIENT ) )
  2882. continue;
  2883. if ( entity->IsBot() )
  2884. continue;
  2885. if ( entity->GetTeamNumber() == TEAM_SPECTATOR )
  2886. {
  2887. iCount++;
  2888. }
  2889. }
  2890. }
  2891. return iCount;
  2892. }
  2893. int UTIL_HumansInGame( bool ignoreSpectators, bool ignoreUnassigned )
  2894. {
  2895. int iCount = 0;
  2896. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2897. {
  2898. CCSPlayer *entity = CCSPlayer::Instance( i );
  2899. if ( entity && !FNullEnt( entity->edict() ) )
  2900. {
  2901. if ( FStrEq( entity->GetPlayerName(), "" ) )
  2902. continue;
  2903. if ( FBitSet( entity->GetFlags(), FL_FAKECLIENT ) )
  2904. continue;
  2905. if ( ignoreSpectators && entity->GetTeamNumber() != TEAM_TERRORIST && entity->GetTeamNumber() != TEAM_CT )
  2906. continue;
  2907. if ( ignoreSpectators && entity->State_Get() == STATE_PICKINGCLASS )
  2908. continue;
  2909. if ( ignoreUnassigned && entity->GetTeamNumber() == TEAM_UNASSIGNED )
  2910. continue;
  2911. iCount++;
  2912. }
  2913. }
  2914. return iCount;
  2915. }
  2916. #if defined ( GAME_DLL )
  2917. bool CCSGameRules::CheckGotGuardianModeSpecialKill( CWeaponCSBase* pAttackerWeapon )
  2918. {
  2919. if ( IsPlayingCoopGuardian() == false )
  2920. return false;
  2921. if ( IsWarmupPeriod() )
  2922. return false;
  2923. if ( m_nGuardianModeSpecialWeaponNeeded != 0 )
  2924. {
  2925. if ( !pAttackerWeapon )
  2926. return false;
  2927. const CEconItemView *pEconItemViewWeapon = pAttackerWeapon->GetEconItemView();
  2928. if ( !pEconItemViewWeapon || !pEconItemViewWeapon->GetItemDefinition() )
  2929. return false;
  2930. if ( m_nGuardianModeSpecialWeaponNeeded != pEconItemViewWeapon->GetItemDefinition()->GetDefinitionIndex() )
  2931. return false;
  2932. }
  2933. // reduce # of kills needed
  2934. m_nGuardianModeSpecialKillsRemaining = MAX( m_nGuardianModeSpecialKillsRemaining - 1, 0 );
  2935. // REI: Should we send a game event message here? Right now we rely on network synchronization of
  2936. // m_nGuardianModeSpecialKillsRemaining and notice when it changes on the client to update
  2937. // their UI.
  2938. if ( m_nGuardianModeSpecialKillsRemaining <= 0 )
  2939. GuardianAllKillsAchievedCheck();
  2940. return true;
  2941. }
  2942. #endif
  2943. #if CS_CONTROLLABLE_BOTS_ENABLED
  2944. // DK TODO: Make a similar method run AFTER all loops of this to look for orphaned bots that think they are still player controlled
  2945. class RevertBotsFunctor
  2946. {
  2947. public:
  2948. bool operator()( CBasePlayer *basePlayer )
  2949. {
  2950. CCSPlayer *pPlayer = ToCSPlayer( basePlayer );
  2951. if ( !pPlayer )
  2952. return true;
  2953. if ( !pPlayer->IsControllingBot() )
  2954. return true;
  2955. // this will properly handle restoring money, frag counts, etc
  2956. pPlayer->ReleaseControlOfBot();
  2957. return true;
  2958. }
  2959. };
  2960. #endif
  2961. CCSMatch::CCSMatch()
  2962. {
  2963. Reset();
  2964. }
  2965. void CCSMatch::Reset( void )
  2966. {
  2967. m_actualRoundsPlayed = 0;
  2968. CSGameRules()->SetTotalRoundsPlayed( 0 );
  2969. m_nOvertimePlaying = 0;
  2970. CSGameRules()->SetOvertimePlaying( 0 );
  2971. m_ctScoreFirstHalf = 0;
  2972. m_ctScoreSecondHalf = 0;
  2973. m_ctScoreOvertime = 0;
  2974. m_ctScoreTotal = 0;
  2975. m_terroristScoreFirstHalf = 0;
  2976. m_terroristScoreSecondHalf = 0;
  2977. m_terroristScoreOvertime = 0;
  2978. m_terroristScoreTotal = 0;
  2979. if ( CSGameRules()->HasHalfTime() )
  2980. {
  2981. SetPhase( GAMEPHASE_PLAYING_FIRST_HALF );
  2982. }
  2983. else
  2984. {
  2985. SetPhase( GAMEPHASE_PLAYING_STANDARD );
  2986. }
  2987. UpdateTeamScores();
  2988. }
  2989. void CCSMatch::SetPhase( GamePhase phase )
  2990. {
  2991. CCSGameRules *pRules = CSGameRules();
  2992. if ( ( m_phase == GAMEPHASE_HALFTIME ) && mp_halftime_pausematch.GetInt() && pRules )
  2993. { // when halftime is over, we pause the match if needed
  2994. if ( !pRules->IsMatchWaitingForResume() )
  2995. {
  2996. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" );
  2997. }
  2998. pRules->SetMatchWaitingForResume( true );
  2999. }
  3000. m_phase = phase;
  3001. // When going to overtime halftime pause the timer if requested
  3002. if ( ( m_phase == GAMEPHASE_HALFTIME ) && m_nOvertimePlaying && mp_overtime_halftime_pausetimer.GetInt() )
  3003. mp_halftime_pausetimer.SetValue( mp_overtime_halftime_pausetimer.GetInt() );
  3004. EnableFullAlltalk( CSGameRules()->IsWarmupPeriod() || m_phase == GAMEPHASE_HALFTIME || m_phase == GAMEPHASE_MATCH_ENDED );
  3005. CSGameRules()->SetGamePhase( phase );
  3006. }
  3007. void CCSMatch::AddTerroristWins( int numWins )
  3008. {
  3009. m_actualRoundsPlayed += numWins;
  3010. CSGameRules()->SetTotalRoundsPlayed( m_actualRoundsPlayed );
  3011. AddTerroristScore( numWins );
  3012. }
  3013. void CCSMatch::AddCTWins( int numWins )
  3014. {
  3015. m_actualRoundsPlayed += numWins;
  3016. CSGameRules()->SetTotalRoundsPlayed( m_actualRoundsPlayed );
  3017. AddCTScore( numWins );
  3018. }
  3019. void CCSMatch::IncrementRound( int nNumRounds )
  3020. {
  3021. m_actualRoundsPlayed += nNumRounds;
  3022. CSGameRules()->SetTotalRoundsPlayed( m_actualRoundsPlayed );
  3023. }
  3024. void CCSMatch::AddTerroristBonusPoints( int points )
  3025. {
  3026. AddTerroristScore( points );
  3027. }
  3028. void CCSMatch::AddCTBonusPoints( int points)
  3029. {
  3030. AddCTScore( points );
  3031. }
  3032. void CCSMatch::AddTerroristScore( int score )
  3033. {
  3034. m_terroristScoreTotal += score;
  3035. if ( m_nOvertimePlaying > 0 )
  3036. {
  3037. m_terroristScoreOvertime += score;
  3038. }
  3039. else if ( m_phase == GAMEPHASE_PLAYING_FIRST_HALF )
  3040. {
  3041. m_terroristScoreFirstHalf += score;
  3042. }
  3043. else if ( m_phase == GAMEPHASE_PLAYING_SECOND_HALF )
  3044. {
  3045. m_terroristScoreSecondHalf += score;
  3046. }
  3047. UpdateTeamScores();
  3048. }
  3049. void CCSMatch::AddCTScore( int score )
  3050. {
  3051. m_ctScoreTotal += score;
  3052. if ( m_nOvertimePlaying > 0 )
  3053. {
  3054. m_ctScoreOvertime += score;
  3055. }
  3056. else if ( m_phase == GAMEPHASE_PLAYING_FIRST_HALF )
  3057. {
  3058. m_ctScoreFirstHalf += score;
  3059. }
  3060. else if ( m_phase == GAMEPHASE_PLAYING_SECOND_HALF )
  3061. {
  3062. m_ctScoreSecondHalf += score;
  3063. }
  3064. UpdateTeamScores();
  3065. }
  3066. void CCSMatch::GoToOvertime( int numOvertimesToAdd )
  3067. {
  3068. m_nOvertimePlaying += numOvertimesToAdd;
  3069. CSGameRules()->SetOvertimePlaying( m_nOvertimePlaying );
  3070. }
  3071. void CCSMatch::SwapTeamScores( void )
  3072. {
  3073. short temp = m_terroristScoreFirstHalf;
  3074. m_terroristScoreFirstHalf = m_ctScoreFirstHalf;
  3075. m_ctScoreFirstHalf = temp;
  3076. temp = m_terroristScoreSecondHalf;
  3077. m_terroristScoreSecondHalf = m_ctScoreSecondHalf;
  3078. m_ctScoreSecondHalf = temp;
  3079. temp = m_terroristScoreOvertime;
  3080. m_terroristScoreOvertime = m_ctScoreOvertime;
  3081. m_ctScoreOvertime = temp;
  3082. temp = m_terroristScoreTotal;
  3083. m_terroristScoreTotal = m_ctScoreTotal;
  3084. m_ctScoreTotal = temp;
  3085. UpdateTeamScores();
  3086. }
  3087. void CCSMatch::UpdateTeamScores( void )
  3088. {
  3089. CTeam *pTerrorists = GetGlobalTeam( TEAM_TERRORIST );
  3090. CTeam *pCTs = GetGlobalTeam( TEAM_CT );
  3091. if ( pTerrorists )
  3092. {
  3093. pTerrorists->SetScore( m_terroristScoreTotal );
  3094. pTerrorists->SetScoreFirstHalf( m_terroristScoreFirstHalf );
  3095. pTerrorists->SetScoreSecondHalf( m_terroristScoreSecondHalf );
  3096. pTerrorists->SetScoreOvertime( m_terroristScoreOvertime );
  3097. }
  3098. if ( pCTs )
  3099. {
  3100. pCTs->SetScore( m_ctScoreTotal);
  3101. pCTs->SetScoreFirstHalf( m_ctScoreFirstHalf );
  3102. pCTs->SetScoreSecondHalf( m_ctScoreSecondHalf );
  3103. pCTs->SetScoreOvertime( m_ctScoreOvertime );
  3104. }
  3105. }
  3106. void CCSMatch::EnableFullAlltalk( bool bEnable )
  3107. {
  3108. if ( !sv_auto_full_alltalk_during_warmup_half_end.GetBool() )
  3109. bEnable = false;
  3110. static ConVarRef sv_full_alltalk( "sv_full_alltalk" );
  3111. sv_full_alltalk.SetValue( bEnable );
  3112. }
  3113. int CCSMatch::GetWinningTeam( void )
  3114. {
  3115. CTeam *pTerrorists = GetGlobalTeam( TEAM_TERRORIST );
  3116. CTeam *pCTs = GetGlobalTeam( TEAM_CT );
  3117. if ( pTerrorists && pTerrorists->m_bSurrendered )
  3118. {
  3119. return TEAM_CT;
  3120. }
  3121. else if ( pCTs && pCTs->m_bSurrendered )
  3122. {
  3123. return TEAM_TERRORIST;
  3124. }
  3125. else if ( m_terroristScoreTotal > m_ctScoreTotal )
  3126. {
  3127. return TEAM_TERRORIST;
  3128. }
  3129. else if ( m_terroristScoreTotal < m_ctScoreTotal )
  3130. {
  3131. return TEAM_CT;
  3132. }
  3133. else
  3134. {
  3135. return WINNER_NONE;
  3136. }
  3137. }
  3138. template < class T > void VectorShuffle( CUtlVector< T > &arrayToShuffle )
  3139. {
  3140. int numEntries = arrayToShuffle.Count();
  3141. // Shuffle entries
  3142. for ( int i = 0; i < numEntries - 1; ++i )
  3143. {
  3144. int randVal = RandomInt( i, numEntries - 1 );
  3145. if ( randVal != i )
  3146. {
  3147. // Swap values
  3148. V_swap( arrayToShuffle[ i ], arrayToShuffle[ randVal ] );
  3149. }
  3150. }
  3151. }
  3152. // --------------------------------------------------------------------------------------------------- //
  3153. // CCSGameRules implementation.
  3154. // --------------------------------------------------------------------------------------------------- //
  3155. CCSGameRules::GcBanInformationMap_t CCSGameRules::sm_mapGcBanInformation;
  3156. CCSGameRules::CCSGameRules()
  3157. {
  3158. m_flLastThinkTime = gpGlobals->curtime;
  3159. m_iRoundTime = 0;
  3160. m_gamePhase = GAMEPHASE_PLAYING_STANDARD;
  3161. m_iRoundWinStatus = WINNER_NONE;
  3162. m_eRoundWinReason = RoundEndReason_StillInProgress;
  3163. m_iFreezeTime = 0;
  3164. m_totalRoundsPlayed = 0;
  3165. m_nOvertimePlaying = 0;
  3166. m_fMatchStartTime = gpGlobals->curtime;
  3167. m_fRoundStartTime = 0;
  3168. m_bAllowWeaponSwitch = true;
  3169. m_bFreezePeriod = true;
  3170. m_bMatchWaitingForResume = false;
  3171. m_nTerroristTimeOuts = mp_team_timeout_max.GetInt();
  3172. m_nCTTimeOuts = mp_team_timeout_max.GetInt();
  3173. m_flTerroristTimeOutRemaining = mp_team_timeout_time.GetInt();
  3174. m_flCTTimeOutRemaining = mp_team_timeout_time.GetInt();
  3175. m_bTerroristTimeOutActive = false;
  3176. m_bCTTimeOutActive = false;
  3177. m_iNumTerrorist = m_iNumCT = 0; // number of players per team
  3178. m_flRestartRoundTime = cInitialRestartRoundTime; // restart first round as soon as possible
  3179. m_timeUntilNextPhaseStarts = 0.0f;
  3180. m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0;
  3181. m_bFirstConnected = false;
  3182. m_bCompleteReset = true;
  3183. m_bPickNewTeamsOnReset = true;
  3184. m_bScrambleTeamsOnRestart = false;
  3185. m_bSwapTeamsOnRestart = false;
  3186. m_iAccountTerrorist = m_iAccountCT = 0;
  3187. m_iNumConsecutiveCTLoses = 0;
  3188. m_iNumConsecutiveTerroristLoses = 0;
  3189. m_bTargetBombed = false;
  3190. m_bBombDefused = false;
  3191. m_iTotalRoundsPlayed = -1;
  3192. m_endMatchOnRoundReset = false;
  3193. m_endMatchOnThink = false;
  3194. m_iUnBalancedRounds = 0;
  3195. m_flGameStartTime = 0;
  3196. m_iHostagesRemaining = 0;
  3197. m_bAnyHostageReached = false;
  3198. m_bLevelInitialized = false;
  3199. m_flCoopRespawnAndHealTime = -1;
  3200. m_bLogoMap = false;
  3201. m_tmNextPeriodicThink = 0;
  3202. m_bPlayerItemsHaveBeenDisplayed = false;
  3203. m_flDMBonusStartTime = 0;
  3204. m_flDMBonusTimeLength = 0;
  3205. m_unDMBonusWeaponLoadoutSlot = 0;
  3206. m_bDMBonusActive = false;
  3207. m_bIsDroppingItems = false;
  3208. m_iActiveAssassinationTargetMissionID = 0;
  3209. m_flGuardianBuyUntilTime = -1;
  3210. m_bCTCantBuy = false;
  3211. m_bTCantBuy = false;
  3212. m_bForceTeamChangeSilent = false;
  3213. m_bLoadingRoundBackupData = false;
  3214. m_pfnCalculateEndOfRoundMVPHook = NULL;
  3215. m_bMapHasBombTarget = false;
  3216. m_bMapHasRescueZone = false;
  3217. m_iSpawnPointCount_Terrorist = 0;
  3218. m_iSpawnPointCount_CT = 0;
  3219. m_bBuyTimeEnded = false;
  3220. m_bHasMatchStarted = false;
  3221. m_nLastFreezeEndBeep = -1;
  3222. m_iMaxNumTerrorists = 0;
  3223. m_iMaxNumCTs = 0;
  3224. m_bMapHasBuyZone = false;
  3225. m_nNextMapInMapgroup = -1;
  3226. m_bVoiceWonMatchBragFired = false;
  3227. m_iLoserBonus = 0;
  3228. m_iHostagesRescued = 0;
  3229. m_iHostagesTouched = 0;
  3230. m_flNextHostageAnnouncement = 0.0f;
  3231. m_MatchDevice = BotProfileInputDevice::KB_MOUSE;
  3232. // map vote for "season" map groups
  3233. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_MatchInProgress;
  3234. // clear the next level
  3235. nextlevel.SetValue( "" );
  3236. // Set the bestof maps state
  3237. m_numBestOfMaps = mp_teamscore_max.GetInt();
  3238. // Set global gifts state
  3239. m_numGlobalGiftsGiven = 0;
  3240. m_numGlobalGifters = 0;
  3241. m_numGlobalGiftsPeriodSeconds = 0;
  3242. for ( int j = 0; j < MAX_GIFT_GIVERS_FEATURED_COUNT; ++ j )
  3243. {
  3244. m_arrFeaturedGiftersAccounts.Set( j, 0 );
  3245. m_arrFeaturedGiftersGifts.Set( j, 0 );
  3246. }
  3247. CheckForGiftsLeaderboardUpdate();
  3248. for ( int j = 0; j < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; ++ j )
  3249. {
  3250. m_arrTournamentActiveCasterAccounts.Set( j, 0 );
  3251. }
  3252. // Configure QMM settings
  3253. m_bIsQuestEligible = IsQuestEligible();
  3254. m_bIsQueuedMatchmaking = IsQueuedMatchmaking();
  3255. m_bIsValveDS = IsValveDS();
  3256. m_pQueuedMatchmakingReservationString = NULL;
  3257. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_MatchInProgress;
  3258. m_bNeedToAskPlayersForContinueVote = false;
  3259. m_pQueuedMatchmakingReportedRoundStats = NULL;
  3260. m_numTotalTournamentDrops = 0;
  3261. m_numSpectatorsCountMax = 0;
  3262. m_numSpectatorsCountMaxTV = 0;
  3263. m_numSpectatorsCountMaxLnk = 0;
  3264. m_numQueuedMatchmakingAccounts = 0;
  3265. m_szTournamentEventName.GetForModify()[0] = 0;
  3266. m_szTournamentEventStage.GetForModify()[0] = 0;
  3267. Q_strncpy( m_szTournamentPredictionsTxt.GetForModify(), mp_teamprediction_txt.GetString(), MAX_PATH );
  3268. Q_strncpy( m_szMatchStatTxt.GetForModify(), mp_teammatchstat_txt.GetString(), MAX_PATH );
  3269. m_nTournamentPredictionsPct = 0;
  3270. m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None;
  3271. m_flMatchInfoDecidedTime = gpGlobals->curtime;
  3272. m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements();
  3273. for ( int j = 0; j < MAX_MATCH_STATS_ROUNDS; ++ j )
  3274. {
  3275. m_iMatchStats_PlayersAlive_T.GetForModify(j) = 0x3F;
  3276. m_iMatchStats_PlayersAlive_CT.GetForModify(j) = 0x3F;
  3277. }
  3278. // Halloween
  3279. m_nHalloweenMaskListSeed = RandomInt( 0, 30 );
  3280. #if 0
  3281. //
  3282. // Setting a bunch of overrides
  3283. //
  3284. sm_QueuedServerReservation.mutable_tournament_event()->set_event_name( "ESL One Katowice 2015 Vitaliy Test Championship" );
  3285. sm_QueuedServerReservation.mutable_tournament_event()->set_event_stage_name( "Group Stage | Decider Match" );
  3286. TournamentTeam *pTeam;
  3287. pTeam = sm_QueuedServerReservation.add_tournament_teams();
  3288. pTeam->set_team_tag( "NiP" );
  3289. pTeam->set_team_flag( "SE" );
  3290. pTeam->set_team_name( "Ninjas in Pyjamas" );
  3291. // pTeam->set_team_clantag( "NiP.Trig" );
  3292. if ( TournamentPlayer *pPlayer = pTeam->add_players() )
  3293. {
  3294. pPlayer->set_account_id( 102003 );
  3295. pPlayer->set_player_nick( "GeT_RiGhT" );
  3296. pPlayer->set_player_name( "Vitaliy Genkin" );
  3297. }
  3298. pTeam = sm_QueuedServerReservation.add_tournament_teams();
  3299. pTeam->set_team_tag( "NAVI" );
  3300. pTeam->set_team_flag( "UA" );
  3301. pTeam->set_team_name( "Natus Vincere" );
  3302. // pTeam->set_team_clantag( "NA'VI" );
  3303. if ( TournamentPlayer *pPlayer = pTeam->add_players() )
  3304. {
  3305. pPlayer->set_account_id( 102003 );
  3306. pPlayer->set_player_nick( "GeT_RiGhT" );
  3307. pPlayer->set_player_name( "Vitaliy Genkin" );
  3308. }
  3309. sm_QueuedServerReservation.mutable_pre_match_data()->set_predictions_pct( 72 );
  3310. CPreMatchInfoData_TeamStats *pTS;
  3311. #if 0 // Group A | Decider Match
  3312. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3313. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_GroupA}" );
  3314. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" );
  3315. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" );
  3316. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3317. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_GroupA}" );
  3318. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossVs{team=#CSGO_TeamID_24}" );
  3319. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossVs{team=#CSGO_TeamID_31}" );
  3320. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3321. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Group2{name=#CSGO_MatchInfo_Stage_GroupA}" );
  3322. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" );
  3323. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" );
  3324. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3325. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Group2{name=#CSGO_MatchInfo_Stage_GroupA}" );
  3326. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" );
  3327. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" );
  3328. #endif
  3329. #if 0 // Quarterfinal | Match 1 of 3
  3330. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3331. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Series2{name=#CSGO_MatchInfo_Stage_Quarterfinal}{idx=1}{count=3}" );
  3332. pTS->add_match_info_teams()->assign( "0" );
  3333. pTS->add_match_info_teams()->assign( "0" );
  3334. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3335. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" );
  3336. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos1{name=#CSGO_MatchInfo_Stage_GroupA}" );
  3337. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos2{name=#CSGO_MatchInfo_Stage_GroupB}" );
  3338. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3339. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" );
  3340. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" );
  3341. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" );
  3342. #endif
  3343. #if 0 // Quarterfinal | Match 2 of 3
  3344. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3345. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Series2{name=#CSGO_MatchInfo_Stage_Quarterfinal}{idx=2}{count=3}" );
  3346. pTS->add_match_info_teams()->assign( "1" );
  3347. pTS->add_match_info_teams()->assign( "0" );
  3348. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3349. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_JustPlayedMap{map=#SFUI_Map_de_cbble}" );
  3350. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinScoreMap{map=#SFUI_Map_de_cbble}{high=16}{low=3}" );
  3351. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_Loss{map=#SFUI_Map_de_cbble}{high=16}{low=3}" );
  3352. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3353. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" );
  3354. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos1{name=#CSGO_MatchInfo_Stage_GroupA}" );
  3355. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos2{name=#CSGO_MatchInfo_Stage_GroupB}" );
  3356. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3357. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" );
  3358. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" );
  3359. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" );
  3360. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3361. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Series2{name=#CSGO_MatchInfo_Stage_Quarterfinal}{idx=2}{count=3}" );
  3362. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" );
  3363. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" );
  3364. #endif
  3365. #if 1 // Quarterfinal | Match 3 of 3
  3366. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3367. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_BracketDecider{name=#CSGO_MatchInfo_Stage_Quarterfinal}" );
  3368. pTS->add_match_info_teams()->assign( "1" );
  3369. pTS->add_match_info_teams()->assign( "1" );
  3370. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3371. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_JustPlayedMaps" );
  3372. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinScoreMap{map=#SFUI_Map_de_cbble}{high=16}{low=3}" );
  3373. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinScoreMap{map=#SFUI_Map_de_mirage}{high=23}{low=21}" );
  3374. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3375. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" );
  3376. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos1{name=#CSGO_MatchInfo_Stage_GroupA}" );
  3377. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos2{name=#CSGO_MatchInfo_Stage_GroupB}" );
  3378. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3379. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" );
  3380. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" );
  3381. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" );
  3382. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3383. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_BracketDecider{name=#CSGO_MatchInfo_Stage_Quarterfinal}" );
  3384. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" );
  3385. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" );
  3386. pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats();
  3387. pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_BracketDecider{name=#CSGO_MatchInfo_Stage_Quarterfinal}" );
  3388. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" );
  3389. pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" );
  3390. #endif
  3391. #endif
  3392. if ( m_bIsQueuedMatchmaking )
  3393. {
  3394. static ConVarRef sv_mmqueue_reservation( "sv_mmqueue_reservation" );
  3395. m_pQueuedMatchmakingReservationString = new char[ Q_strlen( sv_mmqueue_reservation.GetString() ) + 1 ];
  3396. Q_strcpy( m_pQueuedMatchmakingReservationString, sv_mmqueue_reservation.GetString() );
  3397. int iDraftIndex = 0;
  3398. for ( char const *pszPrev = sv_mmqueue_reservation.GetString(), *pszNext = pszPrev;
  3399. ( pszNext = strchr( pszPrev, '[' ) ) != NULL; ( pszPrev = pszNext + 1 ), ( ++ iDraftIndex ) )
  3400. {
  3401. uint32 uiAccountId = 0;
  3402. sscanf( pszNext, "[%x]", &uiAccountId );
  3403. if ( uiAccountId )
  3404. {
  3405. ++ m_numQueuedMatchmakingAccounts;
  3406. CQMMPlayerData_t *pqmmPlayerData = new CQMMPlayerData_t;
  3407. pqmmPlayerData->m_uiPlayerAccountId = uiAccountId;
  3408. pqmmPlayerData->m_iDraftIndex = iDraftIndex;
  3409. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( uiAccountId ) )
  3410. delete pQMM;
  3411. m_mapQueuedMatchmakingPlayersData.InsertOrReplace( uiAccountId, pqmmPlayerData );
  3412. }
  3413. }
  3414. // Set next level to the current level for potential rematch
  3415. nextlevel.SetValue( STRING( gpGlobals->mapname ) );
  3416. // Set the tournament settings
  3417. if ( sm_QueuedServerReservation.has_tournament_event() &&
  3418. sm_QueuedServerReservation.tournament_event().has_event_name() )
  3419. Q_strncpy( m_szTournamentEventName.GetForModify(), sm_QueuedServerReservation.tournament_event().event_name().c_str(), MAX_PATH );
  3420. if ( sm_QueuedServerReservation.has_tournament_event() &&
  3421. sm_QueuedServerReservation.tournament_event().has_event_stage_name() )
  3422. Q_strncpy( m_szTournamentEventStage.GetForModify(), sm_QueuedServerReservation.tournament_event().event_stage_name().c_str(), MAX_PATH );
  3423. }
  3424. // [tj] reset flawless and lossless round related flags
  3425. m_bNoTerroristsKilled = true;
  3426. m_bNoCTsKilled = true;
  3427. m_bNoTerroristsDamaged = true;
  3428. m_bNoCTsDamaged = true;
  3429. m_bNoEnemiesKilled = true;
  3430. m_pFirstKill = NULL;
  3431. m_firstKillTime = 0;
  3432. // [menglish] Reset fun fact values
  3433. m_pFirstBlood = NULL;
  3434. m_firstBloodTime = 0;
  3435. m_pMVP = NULL;
  3436. m_bCanDonateWeapons = true;
  3437. // [dwenger] Reset rescue-related achievement values
  3438. m_arrRescuers.RemoveAll();
  3439. m_hostageWasInjured = false;
  3440. m_hostageWasKilled = false;
  3441. m_pFunFactManager = new CCSFunFactMgr();
  3442. m_pFunFactManager->Init();
  3443. m_iHaveEscaped = 0;
  3444. m_bMapHasEscapeZone = false;
  3445. m_iNumEscapers = 0;
  3446. m_iNumEscapeRounds = 0;
  3447. m_bMapHasBombZone = false;
  3448. m_bBombDropped = false;
  3449. m_bBombPlanted = false;
  3450. m_bHasHostageBeenTouched = false;
  3451. m_bDontIncrementCoopWave = false;
  3452. m_bGunGameRespawnWithBomb = false;
  3453. m_fGunGameBombRespawnTimer = 0.0f;
  3454. m_bRoundTimeWarningTriggered = false;
  3455. m_iNumGunGameProgressiveWeaponsCT = 0;
  3456. m_iNumGunGameProgressiveWeaponsT = 0;
  3457. m_bAllowWeaponSwitch = true;
  3458. m_iSpectatorSlotCount = 0;
  3459. m_nGuardianModeWaveNumber = 1;
  3460. m_nGuardianModeSpecialKillsRemaining = -1;
  3461. m_nGuardianModeSpecialWeaponNeeded = -1;
  3462. m_nGuardianGrenadesToGiveBots = 0;
  3463. m_nNumHeaviesToSpawn = 0;
  3464. m_flNextHostageAnnouncement = gpGlobals->curtime; // asap.
  3465. m_phaseChangeAnnouncementTime = 0.0f;
  3466. m_fNextUpdateTeamClanNamesTime = 0.0f;
  3467. // Create the team managers
  3468. for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ )
  3469. {
  3470. CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "cs_team_manager" ));
  3471. pTeam->Init( sTeamNames[i], i );
  3472. g_Teams.AddToTail( pTeam );
  3473. }
  3474. m_bHasTriggeredRoundStartMusic = false;
  3475. m_bHasTriggeredCoopSpawnReset = false;
  3476. InitializeGameTypeAndMode();
  3477. #ifndef VALVE_DEDICATED_SERVER
  3478. if ( const char* szMapNameBase = V_GetFileName( STRING(gpGlobals->mapname) ) )
  3479. {
  3480. if ( (IsPlayingCustomGametype() )
  3481. && filesystem->FileExists( UTIL_VarArgs( "maps/cfg/%s.cfg", szMapNameBase ) ) )
  3482. {
  3483. // Execute a map specific cfg file to define the rules
  3484. engine->ServerCommand( UTIL_VarArgs( "execwithwhitelist %s.cfg */maps\n", szMapNameBase ) );
  3485. engine->ServerExecute();
  3486. }
  3487. else if ( IsPlayingCoopMission() )
  3488. {
  3489. // Execute a map specific cfg file to define the rules
  3490. int nMissionNumber = 1;
  3491. for ( KeyValues *kvLaunchOptions = engine->GetLaunchOptions()->GetFirstSubKey(); kvLaunchOptions; kvLaunchOptions = kvLaunchOptions->GetNextKey() )
  3492. {
  3493. if ( char const *szValue = StringAfterPrefix( kvLaunchOptions->GetString(), "mission" ) )
  3494. {
  3495. nMissionNumber = V_atoi( szValue );
  3496. DevMsg( "Coop mission number = %d (parsed from %s)\n", nMissionNumber, kvLaunchOptions->GetString() );
  3497. break;
  3498. }
  3499. }
  3500. engine->ServerCommand( UTIL_VarArgs( "execwithwhitelist %s_%d.cfg */maps\n", szMapNameBase, nMissionNumber ) );
  3501. engine->ServerExecute();
  3502. }
  3503. else if ( IsPlayingCoopGuardian() )
  3504. {
  3505. char szCfgName[ MAX_PATH ];
  3506. if ( filesystem->FileExists( UTIL_VarArgs( "maps/cfg/guardian_%s.cfg", szMapNameBase ) ) )
  3507. V_snprintf( szCfgName, sizeof( szCfgName ), "guardian_%s", szMapNameBase );
  3508. else
  3509. V_snprintf( szCfgName, sizeof( szCfgName ), "guardian_defaultmap" );
  3510. // Execute a map specific cfg file to define the rules
  3511. engine->ServerCommand( UTIL_VarArgs( "execwithwhitelist %s.cfg */maps\n", szCfgName ) );
  3512. engine->ServerExecute();
  3513. }
  3514. }
  3515. #endif
  3516. ReadMultiplayCvars();
  3517. m_bVoteCalled = false;
  3518. m_bServerVoteOnReset = false;
  3519. m_flVoteCheckThrottle = 0;
  3520. m_bGameRestart = false;
  3521. m_bSwitchingTeamsAtRoundReset = false;
  3522. m_fAutobalanceDisplayTime = 0.0f;
  3523. m_AutobalanceStatus = AutobalanceStatus::NONE;
  3524. m_iNextCTSpawnPoint = 0;
  3525. m_iNextTerroristSpawnPoint = 0;
  3526. m_iMaxGunGameProgressiveWeaponIndex = 0;
  3527. // m_flDeferredCallDispatchTime = 0.0f;
  3528. m_bWarmupPeriod = mp_do_warmup_period.GetBool();
  3529. m_fWarmupNextChatNoticeTime = 0;
  3530. m_fWarmupPeriodStart = gpGlobals->curtime;
  3531. m_coopMissionManager = NULL;
  3532. for ( int i = 0; i < MAX_TEAMS; i++ )
  3533. {
  3534. m_flNextRespawnWave.Set( i, 0 );
  3535. m_TeamRespawnWaveTimes.Set( i, -1.0f );
  3536. }
  3537. for ( int iVoteOption = 0; iVoteOption < MAX_ENDMATCH_VOTE_PANELS; ++ iVoteOption )
  3538. m_nEndMatchMapGroupVoteOptions.Set( iVoteOption, -1 );
  3539. m_coopBonusCoinsFound = 0;
  3540. m_coopBonusPistolsOnly = true;
  3541. m_coopPlayersInDeploymentZone = false;
  3542. //m_pSun = NULL;
  3543. }
  3544. //-----------------------------------------------------------------------------
  3545. // Purpose:
  3546. //-----------------------------------------------------------------------------
  3547. CCSGameRules::~CCSGameRules()
  3548. {
  3549. // Note, don't delete each team since they are in the gEntList and will
  3550. // automatically be deleted from there, instead.
  3551. g_Teams.Purge();
  3552. delete m_pFunFactManager;
  3553. m_pFunFactManager = NULL;
  3554. delete m_pQueuedMatchmakingReportedRoundStats;
  3555. m_pQueuedMatchmakingReportedRoundStats = NULL;
  3556. delete m_pQueuedMatchmakingReservationString;
  3557. m_pQueuedMatchmakingReservationString = NULL;
  3558. m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements();
  3559. ClearItemsDroppedDuringMatch();
  3560. }
  3561. void CCSGameRules::RefreshSkillData( bool /* forceUpdate */ )
  3562. {
  3563. // NOTE[pmf]: The base class loads skill configuration files which we
  3564. // do not want for CS:GO
  3565. }
  3566. //-----------------------------------------------------------------------------
  3567. // Purpose:
  3568. //-----------------------------------------------------------------------------
  3569. void CCSGameRules::UpdateClientData( CBasePlayer *player )
  3570. {
  3571. }
  3572. int CCSGameRules::GetMaxHumanPlayers() const
  3573. {
  3574. int iGameType = g_pGameTypes->GetCurrentGameType();
  3575. int iGameMode = g_pGameTypes->GetCurrentGameMode();
  3576. return g_pGameTypes->GetMaxPlayersForTypeAndMode( iGameType, iGameMode );
  3577. }
  3578. void CCSGameRules::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
  3579. {
  3580. CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pEntity ) );
  3581. if ( !pPlayer )
  3582. return;
  3583. #if 0
  3584. // If a client sends up their medal rankings (based on achievements completed so far)
  3585. // store them in CSPlayer for display on the scoreboard.
  3586. if ( 0 == Q_strcmp( pKeyValues->GetName(), "player_medal_ranking" ) )
  3587. {
  3588. pPlayer->UpdateRankFromKV( pKeyValues );
  3589. }
  3590. else
  3591. #endif
  3592. if ( ( 0 == Q_strcmp( pKeyValues->GetName(), "ClanTagChanged" ) ) &&
  3593. // When we have tournament system enabled then players cannot change their clan tags from client
  3594. CanClientCustomizeOwnIdentity() )
  3595. {
  3596. pPlayer->SetClanTag( pKeyValues->GetString( "tag", "" ) );
  3597. const char *szClanName = pKeyValues->GetString( "name", "" );
  3598. pPlayer->SetClanName( szClanName );
  3599. //if ( !V_strcmp( team->Get_Name(), CSGameRules()->GetDefaultTeamName(TEAM_TERRORIST) ) )
  3600. UpdateTeamClanNames( TEAM_TERRORIST );
  3601. UpdateTeamClanNames( TEAM_CT );
  3602. UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"clantag\" (value \"%s\")\n",
  3603. pPlayer->GetPlayerName(),
  3604. pPlayer->GetUserID(),
  3605. pPlayer->GetNetworkIDString(),
  3606. pPlayer->GetTeam() ? pPlayer->GetTeam()->GetName() : "UNKNOWN",
  3607. pKeyValues->GetString( "tag", "unknown" ) );
  3608. }
  3609. else if ( 0 == Q_strcmp( pKeyValues->GetName(), "InvalidSteamLogon" ) )
  3610. {
  3611. if ( IsWarmupPeriod() || IsFreezePeriod() || !pPlayer->IsAlive() )
  3612. pKeyValues->SetBool( "disconnect", true );
  3613. else
  3614. pPlayer->m_bInvalidSteamLogonDelayed = true;
  3615. }
  3616. }
  3617. static bool Helper_CheckFieldAppliesToTeam( char const *szField, int nTeam )
  3618. {
  3619. if ( StringHasPrefix( szField, "#CSGO_MatchInfoTeam_WinAdvan" ) )
  3620. {
  3621. // Make sure that this team is winning
  3622. int nOtherTeam = ( ( nTeam == TEAM_CT ) ? TEAM_TERRORIST : ( ( nTeam == TEAM_TERRORIST ) ? TEAM_CT : nTeam ) );
  3623. return ( GetGlobalTeam( nTeam ) ? GetGlobalTeam( nTeam )->GetScore() : 0 ) > ( GetGlobalTeam( nOtherTeam ) ? GetGlobalTeam( nOtherTeam )->GetScore() : 0 );
  3624. }
  3625. else if ( StringHasPrefix( szField, "#CSGO_MatchInfoTeam_LossElim" ) )
  3626. {
  3627. // Make sure that this team is losing
  3628. int nOtherTeam = ( ( nTeam == TEAM_CT ) ? TEAM_TERRORIST : ( ( nTeam == TEAM_TERRORIST ) ? TEAM_CT : nTeam ) );
  3629. return ( GetGlobalTeam( nTeam ) ? GetGlobalTeam( nTeam )->GetScore() : 0 ) < ( GetGlobalTeam( nOtherTeam ) ? GetGlobalTeam( nOtherTeam )->GetScore() : 0 );
  3630. }
  3631. else if ( ( szField[0] >= '0' ) && ( szField[0] <= '9' ) && ( szField[1] == 0 ) &&
  3632. CSGameRules() && ( ( CSGameRules()->m_match.GetPhase() == GAMEPHASE_HALFTIME ) || ( CSGameRules()->m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) ) )
  3633. return false;
  3634. else
  3635. return true;
  3636. }
  3637. void CCSGameRules::UpdateTeamPredictions()
  3638. {
  3639. int nWantPrediction = 0;
  3640. if ( ( sm_QueuedServerReservation.pre_match_data().predictions_pct() >= 1 ) &&
  3641. ( sm_QueuedServerReservation.pre_match_data().predictions_pct() <= 99 ) )
  3642. nWantPrediction = int( sm_QueuedServerReservation.pre_match_data().predictions_pct() ); // but convar can override
  3643. if ( ( mp_teamprediction_pct.GetInt() >= 1 ) &&
  3644. ( mp_teamprediction_pct.GetInt() <= 99 ) )
  3645. nWantPrediction = mp_teamprediction_pct.GetInt();
  3646. // Prediction UI component cannot be displayed at certain times
  3647. if ( ( m_match.GetPhase() == GAMEPHASE_HALFTIME ) || ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) )
  3648. nWantPrediction = 0;
  3649. if ( Q_strncmp( m_szTournamentPredictionsTxt, mp_teamprediction_txt.GetString(), MAX_PATH - 1 ) )
  3650. Q_strncpy( m_szTournamentPredictionsTxt.GetForModify(), mp_teamprediction_txt.GetString(), MAX_PATH );
  3651. char const *szWantMatchStatTxt = mp_teammatchstat_txt.GetString(); // can override from reservation later
  3652. if ( sm_QueuedServerReservation.pre_match_data().stats().size() )
  3653. szWantMatchStatTxt = sm_QueuedServerReservation.pre_match_data().stats( 0 ).match_info_txt().c_str(); // to ensure that it is eligible for a pick
  3654. //
  3655. // Here we must determine which statistics we are going to be showing
  3656. //
  3657. if ( IsWarmupPeriod() )
  3658. {
  3659. // we are going to show the draft here
  3660. if ( sm_QueuedServerReservation.pre_match_data().stats().size() )
  3661. m_nMatchInfoShowType = 0;
  3662. else if ( *szWantMatchStatTxt )
  3663. m_nMatchInfoShowType = 0;
  3664. else if ( nWantPrediction )
  3665. m_nMatchInfoShowType = k_MapMatchInfoShownCounts_Predictions;
  3666. else
  3667. m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None;
  3668. m_flMatchInfoDecidedTime = gpGlobals->curtime;
  3669. }
  3670. else if ( IsFreezePeriod() || ( m_iRoundWinStatus != WINNER_NONE ) )
  3671. {
  3672. if ( ( m_nMatchInfoShowType == k_MapMatchInfoShownCounts_None ) ||
  3673. ( gpGlobals->curtime - m_flMatchInfoDecidedTime > mp_teammatchstat_cycletime.GetFloat() ) )
  3674. {
  3675. m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None;
  3676. CUtlVectorFixedGrowable< int32, 10 > arrOptions;
  3677. bool bTeamsAreSwitched = AreTeamsPlayingSwitchedSides();
  3678. if ( nWantPrediction )
  3679. arrOptions.AddToTail( k_MapMatchInfoShownCounts_Predictions );
  3680. for ( int j = 0; j < sm_QueuedServerReservation.pre_match_data().stats().size(); ++ j )
  3681. {
  3682. if ( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams().size() < 2 ) continue;
  3683. if ( ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) && ( m_nMatchInfoShowType == k_MapMatchInfoShownCounts_None ) )
  3684. {
  3685. if (
  3686. ( ( StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), "#CSGO_MatchInfoTeam_WinAdvan" ) ||
  3687. StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), "#CSGO_MatchInfoTeam_LossElim" ) ) &&
  3688. Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), bTeamsAreSwitched ? TEAM_TERRORIST : TEAM_CT ) )
  3689. ||
  3690. ( ( StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), "#CSGO_MatchInfoTeam_WinAdvan" ) ||
  3691. StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), "#CSGO_MatchInfoTeam_LossElim" ) ) &&
  3692. Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), bTeamsAreSwitched ? TEAM_CT : TEAM_TERRORIST ) )
  3693. )
  3694. { // always conclude the match with advances/eliminated notification if such is applicable
  3695. m_nMatchInfoShowType = j;
  3696. }
  3697. }
  3698. if ( Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), bTeamsAreSwitched ? TEAM_TERRORIST : TEAM_CT ) ||
  3699. Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), bTeamsAreSwitched ? TEAM_CT : TEAM_TERRORIST ) )
  3700. {
  3701. arrOptions.AddToTail( j );
  3702. }
  3703. }
  3704. if ( ( !arrOptions.Count() || !sm_QueuedServerReservation.pre_match_data().stats().size() ) && *szWantMatchStatTxt )
  3705. arrOptions.AddToTail( 0 );
  3706. if ( arrOptions.Count() && ( m_nMatchInfoShowType == k_MapMatchInfoShownCounts_None ) )
  3707. {
  3708. // Pick the option that was shown the least number of times
  3709. uint32 nLeastNumberOfTimesShown = ~uint32( 0 );
  3710. FOR_EACH_VEC( arrOptions, iOption )
  3711. {
  3712. MapMatchInfoShownCounts::IndexType_t lookupIdx = m_mapMatchInfoShownCounts.Find( arrOptions[iOption] );
  3713. uint32 nThisNumberOfTimesShown = 0;
  3714. if ( lookupIdx != m_mapMatchInfoShownCounts.InvalidIndex() )
  3715. nThisNumberOfTimesShown = m_mapMatchInfoShownCounts.Element( lookupIdx );
  3716. if ( nThisNumberOfTimesShown < nLeastNumberOfTimesShown )
  3717. {
  3718. nLeastNumberOfTimesShown = nThisNumberOfTimesShown;
  3719. m_nMatchInfoShowType = arrOptions[iOption];
  3720. }
  3721. }
  3722. }
  3723. if ( m_nMatchInfoShowType != k_MapMatchInfoShownCounts_None )
  3724. {
  3725. // Even if some stat was never picked prevent it from showing more than twice in a row
  3726. uint32 numShown = 1;
  3727. MapMatchInfoShownCounts::IndexType_t lookupIdx = m_mapMatchInfoShownCounts.Find( m_nMatchInfoShowType );
  3728. if ( lookupIdx != m_mapMatchInfoShownCounts.InvalidIndex() )
  3729. numShown = m_mapMatchInfoShownCounts.Element( lookupIdx ) + 1;
  3730. FOR_EACH_MAP_FAST( m_mapMatchInfoShownCounts, iSCFast )
  3731. {
  3732. uint32 numOtherShown = m_mapMatchInfoShownCounts.Element( iSCFast );
  3733. if ( numOtherShown > numShown + 1 )
  3734. numShown = numOtherShown - 1;
  3735. }
  3736. // Track the option that was picked
  3737. m_mapMatchInfoShownCounts.InsertOrReplace( m_nMatchInfoShowType, numShown );
  3738. }
  3739. m_flMatchInfoDecidedTime = gpGlobals->curtime;
  3740. }
  3741. }
  3742. else if ( m_nMatchInfoShowType != k_MapMatchInfoShownCounts_None )
  3743. {
  3744. if ( m_flMatchInfoDecidedTime <= gpGlobals->curtime )
  3745. { // hold it a little longer to cover brief intermittent gamestate blips
  3746. m_flMatchInfoDecidedTime = gpGlobals->curtime + mp_teammatchstat_holdtime.GetFloat();
  3747. }
  3748. else if ( m_flMatchInfoDecidedTime <= gpGlobals->curtime + 1.0f )
  3749. { // reset when < 1 sec remaining on hold timer
  3750. m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None;
  3751. }
  3752. }
  3753. //
  3754. // Kill the desire for things that weren't picked
  3755. //
  3756. if ( m_nMatchInfoShowType != k_MapMatchInfoShownCounts_Predictions )
  3757. nWantPrediction = 0;
  3758. if ( m_nMatchInfoShowType >= k_MapMatchInfoShownCounts_None )
  3759. szWantMatchStatTxt = "";
  3760. //
  3761. // Set desired values
  3762. //
  3763. if ( nWantPrediction != m_nTournamentPredictionsPct )
  3764. m_nTournamentPredictionsPct = nWantPrediction;
  3765. if ( m_nMatchInfoShowType < k_MapMatchInfoShownCounts_None )
  3766. {
  3767. if ( sm_QueuedServerReservation.pre_match_data().stats().size() > m_nMatchInfoShowType )
  3768. {
  3769. int idxMatchTxt = m_nMatchInfoShowType;
  3770. if ( sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).has_match_info_idxtxt() )
  3771. idxMatchTxt = sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_idxtxt();
  3772. szWantMatchStatTxt = sm_QueuedServerReservation.pre_match_data().stats( idxMatchTxt ).match_info_txt().c_str();
  3773. }
  3774. }
  3775. if ( Q_strncmp( m_szMatchStatTxt, szWantMatchStatTxt, MAX_PATH - 1 ) )
  3776. Q_strncpy( m_szMatchStatTxt.GetForModify(), szWantMatchStatTxt, MAX_PATH );
  3777. }
  3778. void CCSGameRules::UpdateTeamClanNames( int nTeam )
  3779. {
  3780. Assert( ( nTeam == TEAM_CT ) || ( nTeam == TEAM_TERRORIST ) );
  3781. CTeam *pTeam = GetGlobalTeam( nTeam );
  3782. //pTeam->SetName( GetDefaultTeamName(nTeam) );
  3783. bool bTeamsAreSwitched = AreTeamsPlayingSwitchedSides();
  3784. const char *(pTeamNames[ 2 ]) = { mp_teamname_2.GetString(), mp_teamname_1.GetString() };
  3785. // If we have a competitive reservation then override team names from it
  3786. if ( ( sm_QueuedServerReservation.tournament_teams().size() > 0 ) &&
  3787. sm_QueuedServerReservation.tournament_teams(0).has_team_name() &&
  3788. * sm_QueuedServerReservation.tournament_teams(0).team_name().c_str() )
  3789. pTeamNames[1] = sm_QueuedServerReservation.tournament_teams(0).team_name().c_str();
  3790. if ( ( sm_QueuedServerReservation.tournament_teams().size() > 1 ) &&
  3791. sm_QueuedServerReservation.tournament_teams(1).has_team_name() &&
  3792. * sm_QueuedServerReservation.tournament_teams(1).team_name().c_str() )
  3793. pTeamNames[0] = sm_QueuedServerReservation.tournament_teams(1).team_name().c_str();
  3794. int nTeamIndex = ( nTeam - TEAM_TERRORIST ); // nTeamIndex == 0 if Terrorist, 1 if CT
  3795. const char *pClanName = "";
  3796. uint32 uiClanID = 0;
  3797. // Set the team names to the convars depending on what half phase it is.
  3798. if ( !bTeamsAreSwitched )
  3799. pClanName = pTeamNames[ nTeamIndex ];
  3800. else
  3801. pClanName = pTeamNames[ 1 - nTeamIndex ];
  3802. // The teamname convar was empty so differ to the team's clan name, if it exists.
  3803. if ( StringIsEmpty( pClanName ) && IsClanTeam( pTeam ) )
  3804. {
  3805. for ( int iPlayer = 0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  3806. {
  3807. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  3808. if ( pPlayer && !pPlayer->IsBot() )
  3809. {
  3810. pClanName = pPlayer->GetClanName();
  3811. const char *pClanID = engine->GetClientConVarValue( pPlayer->entindex(), "cl_clanid" );
  3812. uiClanID = Q_atoi( pClanID );
  3813. break;
  3814. }
  3815. }
  3816. }
  3817. pTeam->SetClanName( pClanName );
  3818. pTeam->SetClanID( uiClanID );
  3819. //
  3820. // Team flags processing
  3821. //
  3822. const char *pFlag = "";
  3823. const char *(pTeamFlags[ 2 ]) = { mp_teamflag_2.GetString(), mp_teamflag_1.GetString() };
  3824. const char *pLogo = "";
  3825. const char *(pTeamLogos[ 2 ]) = { mp_teamlogo_2.GetString(), mp_teamlogo_1.GetString() };
  3826. int numMapsWon = 0;
  3827. int arrMapsWon[ 2 ] = { mp_teamscore_2.GetInt(), mp_teamscore_1.GetInt() };
  3828. // If we have a competitive reservation then override team flags from it
  3829. if ( ( sm_QueuedServerReservation.tournament_teams().size() > 0 ) &&
  3830. sm_QueuedServerReservation.tournament_teams(0).has_team_flag() &&
  3831. * sm_QueuedServerReservation.tournament_teams(0).team_flag().c_str() )
  3832. pTeamFlags[1] = sm_QueuedServerReservation.tournament_teams(0).team_flag().c_str();
  3833. if ( ( sm_QueuedServerReservation.tournament_teams().size() > 1 ) &&
  3834. sm_QueuedServerReservation.tournament_teams(1).has_team_flag() &&
  3835. * sm_QueuedServerReservation.tournament_teams(1).team_flag().c_str() )
  3836. pTeamFlags[0] = sm_QueuedServerReservation.tournament_teams(1).team_flag().c_str();
  3837. // get the logos
  3838. if ( ( sm_QueuedServerReservation.tournament_teams().size() > 0 ) &&
  3839. sm_QueuedServerReservation.tournament_teams( 0 ).has_team_tag() &&
  3840. * sm_QueuedServerReservation.tournament_teams( 0 ).team_tag().c_str() )
  3841. pTeamLogos[1] = sm_QueuedServerReservation.tournament_teams( 0 ).team_tag().c_str() ;
  3842. if ( ( sm_QueuedServerReservation.tournament_teams().size() > 01 ) &&
  3843. sm_QueuedServerReservation.tournament_teams( 1 ).has_team_tag() &&
  3844. * sm_QueuedServerReservation.tournament_teams( 1 ).team_tag().c_str() )
  3845. pTeamLogos[0] = sm_QueuedServerReservation.tournament_teams( 1 ).team_tag().c_str();
  3846. // Set the team names to the convars depending on what half phase it is.
  3847. if ( !bTeamsAreSwitched )
  3848. {
  3849. pFlag = pTeamFlags[ nTeamIndex ];
  3850. pLogo = pTeamLogos[ nTeamIndex ];
  3851. numMapsWon = arrMapsWon[ nTeamIndex ];
  3852. }
  3853. else
  3854. {
  3855. pFlag = pTeamFlags[ 1 - nTeamIndex ];
  3856. pLogo = pTeamLogos[ 1 - nTeamIndex ];
  3857. numMapsWon = arrMapsWon[ 1 - nTeamIndex ];
  3858. }
  3859. pTeam->SetFlagImageString( pFlag );
  3860. pTeam->SetLogoImageString( pLogo );
  3861. pTeam->SetNumMapVictories( numMapsWon );
  3862. //
  3863. // Team match stat processing
  3864. //
  3865. const char *pMatchStat = "";
  3866. const char *(pTeamMatchStats[ 2 ]) = { mp_teammatchstat_2.GetString(), mp_teammatchstat_1.GetString() };
  3867. // If we have a competitive reservation then override team match stats from it
  3868. bool bUsingReservation = false;
  3869. if ( m_nMatchInfoShowType < k_MapMatchInfoShownCounts_None )
  3870. {
  3871. if ( ( sm_QueuedServerReservation.pre_match_data().stats().size() > m_nMatchInfoShowType ) &&
  3872. ( sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_teams().size() >= 2 ) )
  3873. {
  3874. bUsingReservation = true;
  3875. pTeamMatchStats[1] = sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_teams(0).c_str();
  3876. pTeamMatchStats[0] = sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_teams(1).c_str();
  3877. }
  3878. }
  3879. else
  3880. {
  3881. pTeamMatchStats[0] = pTeamMatchStats[1] = "";
  3882. }
  3883. // Set the team match stats to the convars depending on what half phase it is.
  3884. if ( !bTeamsAreSwitched )
  3885. pMatchStat = pTeamMatchStats[ nTeamIndex ];
  3886. else
  3887. pMatchStat = pTeamMatchStats[ 1 - nTeamIndex ];
  3888. if ( bUsingReservation && !Helper_CheckFieldAppliesToTeam( pMatchStat, nTeam ) )
  3889. pMatchStat = "";
  3890. Q_strncpy( pTeam->m_szTeamMatchStat.GetForModify(), pMatchStat, MAX_PATH );
  3891. }
  3892. // registered VSCRIPT functions
  3893. void CCSGameRules::SetPlayerCompletedTraining( bool bCompleted )
  3894. {
  3895. #ifndef CLIENT_DLL
  3896. int nComplete = 0;
  3897. if ( bCompleted )
  3898. nComplete = 1;
  3899. tr_completed_training.SetValue( nComplete );
  3900. #endif
  3901. }
  3902. bool CCSGameRules::GetPlayerCompletedTraining( void )
  3903. {
  3904. #ifndef CLIENT_DLL
  3905. return tr_completed_training.GetBool();
  3906. #else
  3907. return 0;
  3908. #endif
  3909. }
  3910. void CCSGameRules::SetBestTrainingCourseTime( int nTime )
  3911. {
  3912. #ifndef CLIENT_DLL
  3913. tr_best_course_time.SetValue( nTime );
  3914. #endif
  3915. }
  3916. int CCSGameRules::GetBestTrainingCourseTime( void )
  3917. {
  3918. #ifndef CLIENT_DLL
  3919. return tr_best_course_time.GetInt();
  3920. #else
  3921. return 0;
  3922. #endif
  3923. }
  3924. int CCSGameRules::GetValveTrainingCourseTime( void )
  3925. {
  3926. #ifndef CLIENT_DLL
  3927. return tr_valve_course_time.GetInt();
  3928. #else
  3929. return 0;
  3930. #endif
  3931. }
  3932. bool CCSGameRules::IsLocalPlayerUsingController( void )
  3933. {
  3934. CCSPlayer *pPlayer = static_cast<CCSPlayer*>( UTIL_PlayerByIndex(1) );
  3935. if ( pPlayer )
  3936. {
  3937. if ( pPlayer->GetPlayerInputDevice() == INPUT_DEVICE_GAMEPAD )
  3938. {
  3939. return true;
  3940. }
  3941. }
  3942. return false;
  3943. }
  3944. void CCSGameRules::TrainingGivePlayerAmmo( void )
  3945. {
  3946. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  3947. if ( pPlayer )
  3948. {
  3949. CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
  3950. if( pWeapon && pWeapon->UsesPrimaryAmmo() )
  3951. {
  3952. pWeapon->SetReserveAmmoCount( AMMO_POSITION_PRIMARY, pWeapon->GetReserveAmmoMax( AMMO_POSITION_PRIMARY ) );
  3953. }
  3954. }
  3955. }
  3956. void CCSGameRules::TrainingSetRadarHidden( bool bHide )
  3957. {
  3958. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  3959. if ( pPlayer )
  3960. {
  3961. CCSPlayer *pCSPlayer = static_cast<CCSPlayer *>( pPlayer );
  3962. if ( pCSPlayer )
  3963. pCSPlayer->SetRadarHidden( bHide );
  3964. }
  3965. }
  3966. void CCSGameRules::TrainingSetMiniScoreHidden( bool bHide )
  3967. {
  3968. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  3969. if ( pPlayer )
  3970. {
  3971. CCSPlayer *pCSPlayer = static_cast<CCSPlayer *>( pPlayer );
  3972. if ( pCSPlayer )
  3973. pCSPlayer->SetMiniScoreHidden( bHide );
  3974. }
  3975. }
  3976. // not used yet because this relied on vgui panels, scaleform isn't supported yet
  3977. void CCSGameRules::TrainingHighlightAmmoCounter( void )
  3978. {
  3979. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  3980. if ( pPlayer )
  3981. {
  3982. #ifndef CLIENT_DLL
  3983. IGameEvent * event = gameeventmanager->CreateEvent( "tr_highlight_ammo", true );
  3984. if ( event )
  3985. {
  3986. event->SetInt( "userid", pPlayer->GetUserID() );
  3987. gameeventmanager->FireEvent( event );
  3988. }
  3989. #endif
  3990. }
  3991. }
  3992. void CCSGameRules::TrainingShowFinishMsgBox( void )
  3993. {
  3994. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  3995. if ( pPlayer )
  3996. {
  3997. IGameEvent * event = gameeventmanager->CreateEvent( "tr_show_finish_msgbox", true );
  3998. if ( event )
  3999. {
  4000. gameeventmanager->FireEvent( event );
  4001. }
  4002. }
  4003. }
  4004. void CCSGameRules::TrainingShowExitDoorMsg( void )
  4005. {
  4006. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  4007. if ( pPlayer )
  4008. {
  4009. IGameEvent * event = gameeventmanager->CreateEvent( "tr_show_exit_msgbox", true );
  4010. if ( event )
  4011. {
  4012. gameeventmanager->FireEvent( event );
  4013. }
  4014. }
  4015. }
  4016. void ScriptCoopResetRoundStartTime( void )
  4017. {
  4018. CCSGameRules *pRules = CSGameRules();
  4019. if ( !pRules )
  4020. return;
  4021. pRules->CoopResetRoundStartTime();
  4022. }
  4023. void ScriptCoopGiveC4sToCTs( int nNumC4ToGive )
  4024. {
  4025. CCSGameRules *pRules = CSGameRules();
  4026. if ( !pRules )
  4027. return;
  4028. pRules->CoopGiveC4sToCTs( nNumC4ToGive );
  4029. }
  4030. void ScriptSetPlayerCompletedTraining( bool bCompleted )
  4031. {
  4032. CCSGameRules *pRules = CSGameRules();
  4033. if ( !pRules )
  4034. return;
  4035. pRules->SetPlayerCompletedTraining( bCompleted );
  4036. }
  4037. bool ScriptGetPlayerCompletedTraining( void )
  4038. {
  4039. CCSGameRules *pRules = CSGameRules();
  4040. if ( !pRules )
  4041. return false;
  4042. return pRules->GetPlayerCompletedTraining();
  4043. }
  4044. void ScriptSetBestTrainingCourseTime( int nTime )
  4045. {
  4046. CCSGameRules *pRules = CSGameRules();
  4047. if ( !pRules )
  4048. return;
  4049. pRules->SetBestTrainingCourseTime( nTime );
  4050. }
  4051. int ScriptGetBestTrainingCourseTime( void )
  4052. {
  4053. CCSGameRules *pRules = CSGameRules();
  4054. if ( !pRules )
  4055. return false;
  4056. return pRules->GetBestTrainingCourseTime();
  4057. }
  4058. int ScriptGetValveTrainingCourseTime( void )
  4059. {
  4060. CCSGameRules *pRules = CSGameRules();
  4061. if ( !pRules )
  4062. return false;
  4063. return pRules->GetValveTrainingCourseTime();
  4064. }
  4065. void ScriptTrainingGivePlayerAmmo( void )
  4066. {
  4067. CCSGameRules *pRules = CSGameRules();
  4068. if ( !pRules || !pRules->IsPlayingTraining() )
  4069. return;
  4070. pRules->TrainingGivePlayerAmmo();
  4071. }
  4072. void ScriptSetMiniScoreHidden( bool nHide )
  4073. {
  4074. CCSGameRules *pRules = CSGameRules();
  4075. if ( !pRules || !pRules->IsPlayingTraining() )
  4076. return;
  4077. pRules->TrainingSetMiniScoreHidden( nHide );
  4078. }
  4079. void ScriptSetRadarHidden( bool nHide )
  4080. {
  4081. CCSGameRules *pRules = CSGameRules();
  4082. if ( !pRules || !pRules->IsPlayingTraining() )
  4083. return;
  4084. pRules->TrainingSetRadarHidden( nHide );
  4085. }
  4086. void ScriptHighlightAmmoCounter( void )
  4087. {
  4088. CCSGameRules *pRules = CSGameRules();
  4089. if ( !pRules || !pRules->IsPlayingTraining() )
  4090. return;
  4091. pRules->TrainingHighlightAmmoCounter();
  4092. }
  4093. void ScriptShowFinishMsgBox( void )
  4094. {
  4095. CCSGameRules *pRules = CSGameRules();
  4096. if ( !pRules || !pRules->IsPlayingTraining() )
  4097. return;
  4098. pRules->TrainingShowFinishMsgBox();
  4099. }
  4100. void ScriptShowExitDoorMsg( void )
  4101. {
  4102. CCSGameRules *pRules = CSGameRules();
  4103. if ( !pRules || !pRules->IsPlayingTraining() )
  4104. return;
  4105. pRules->TrainingShowExitDoorMsg();
  4106. }
  4107. bool ScriptIsLocalPlayerUsingController( void )
  4108. {
  4109. CCSGameRules *pRules = CSGameRules();
  4110. if ( !pRules )
  4111. return false;
  4112. return pRules->IsLocalPlayerUsingController();
  4113. }
  4114. void ScriptPrintMessageCenterAll( const char *pszMessage )
  4115. {
  4116. UTIL_ClientPrintAll( HUD_PRINTCENTER, pszMessage );
  4117. }
  4118. void ScriptPrintMessageChatAll( const char *pszMessage )
  4119. {
  4120. UTIL_ClientPrintAll( HUD_PRINTTALK, pszMessage );
  4121. }
  4122. void ScriptPrintMessageCenterTeam( int nTeamNumber, const char *pszMessage )
  4123. {
  4124. CRecipientFilter filter;
  4125. filter.MakeReliable();
  4126. filter.AddRecipientsByTeam( GetGlobalTeam(nTeamNumber) );
  4127. UTIL_ClientPrintFilter( filter, HUD_PRINTCENTER, pszMessage );
  4128. }
  4129. void ScriptPrintMessageChatTeam( int nTeamNumber, const char *pszMessage )
  4130. {
  4131. CRecipientFilter filter;
  4132. filter.MakeReliable();
  4133. filter.AddRecipientsByTeam( GetGlobalTeam(nTeamNumber) );
  4134. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, pszMessage );
  4135. }
  4136. int ScriptGetGameMode( void )
  4137. {
  4138. return g_pGameTypes->GetCurrentGameMode();
  4139. }
  4140. int ScriptGetGameType( void )
  4141. {
  4142. return g_pGameTypes->GetCurrentGameType();
  4143. }
  4144. int ScriptGetRoundsPlayed( void )
  4145. {
  4146. CCSGameRules *pRules = CSGameRules();
  4147. if ( !pRules )
  4148. return false;
  4149. return pRules->GetRoundsPlayed();
  4150. }
  4151. bool ScriptIsWarmupPeriod( void )
  4152. {
  4153. CCSGameRules *pRules = CSGameRules();
  4154. if ( !pRules )
  4155. return false;
  4156. return pRules->IsWarmupPeriod();
  4157. }
  4158. void ScriptCoopSetBotQuotaAndRefreshSpawns( int nMaxEnemiesToSpawn )
  4159. {
  4160. CCSGameRules *pRules = CSGameRules();
  4161. if ( !pRules )
  4162. return;
  4163. return pRules->CoopSetBotQuotaAndRefreshSpawns( nMaxEnemiesToSpawn );
  4164. }
  4165. void ScriptCoopMissionSpawnFirstEnemies( int nMaxEnemiesToSpawn )
  4166. {
  4167. CCSGameRules *pRules = CSGameRules();
  4168. if ( !pRules )
  4169. return;
  4170. return pRules->CoopMissionSpawnFirstEnemies( nMaxEnemiesToSpawn );
  4171. }
  4172. void ScriptCoopMissionSetNextRespawnIn( float flSeconds, bool bIncrementWaveNumber )
  4173. {
  4174. CCSGameRules *pRules = CSGameRules();
  4175. if ( !pRules )
  4176. return;
  4177. return pRules->CoopMissionSetNextRespawnIn( flSeconds, bIncrementWaveNumber );
  4178. }
  4179. void ScriptCoopMissionSpawnNextWave( int nMaxEnemiesToSpawn )
  4180. {
  4181. CCSGameRules *pRules = CSGameRules();
  4182. if ( !pRules )
  4183. return;
  4184. return pRules->CoopMissionSpawnNextWave( nMaxEnemiesToSpawn );
  4185. }
  4186. void ScriptCoopMissionRespawnDeadPlayers( void )
  4187. {
  4188. CCSGameRules *pRules = CSGameRules();
  4189. if ( !pRules )
  4190. return;
  4191. return pRules->CoopMissionRespawnDeadPlayers();
  4192. }
  4193. int ScriptCoopMissionGetMissionNumber( void )
  4194. {
  4195. return mp_coopmission_mission_number.GetInt();
  4196. }
  4197. void ScriptCoopCollectBonusCoin( void )
  4198. {
  4199. CCSGameRules *pRules = CSGameRules();
  4200. if ( !pRules )
  4201. return;
  4202. return pRules->CoopCollectBonusCoin();
  4203. }
  4204. void CCSGameRules::RegisterScriptFunctions( void )
  4205. {
  4206. ScriptRegisterFunction( g_pScriptVM, ScriptSetPlayerCompletedTraining, "Sets whether the player has completed the initial portion of the training map." );
  4207. ScriptRegisterFunction( g_pScriptVM, ScriptGetPlayerCompletedTraining, "Returns true if the player has completed the initial portion of the training map." );
  4208. ScriptRegisterFunction( g_pScriptVM, ScriptSetBestTrainingCourseTime, "Sets the player's best time for completing the timed course." );
  4209. ScriptRegisterFunction( g_pScriptVM, ScriptGetBestTrainingCourseTime, "Gets the player's best time for completing the timed course." );
  4210. ScriptRegisterFunction( g_pScriptVM, ScriptGetValveTrainingCourseTime, "Gets Valve's best time for completing the timed course." );
  4211. ScriptRegisterFunction( g_pScriptVM, ScriptTrainingGivePlayerAmmo, "Refills ammo to max for all weapons the player has (only works in training)." );
  4212. ScriptRegisterFunction( g_pScriptVM, ScriptSetMiniScoreHidden, "Toggles the visibility of the miniscoreboard hud element." );
  4213. ScriptRegisterFunction( g_pScriptVM, ScriptSetRadarHidden, "Toggles the visibility of the radar hud element." );
  4214. ScriptRegisterFunction( g_pScriptVM, ScriptHighlightAmmoCounter, "Sends an event that is just used by the instructor system to show a hint highlighting the ammo counter." );
  4215. ScriptRegisterFunction( g_pScriptVM, ScriptShowFinishMsgBox, "Shows a message box to let players know what to do next after finishing the training course." );
  4216. ScriptRegisterFunction( g_pScriptVM, ScriptShowExitDoorMsg, "Shows a message box in trainign when the player exits through the exit door" );
  4217. ScriptRegisterFunction( g_pScriptVM, ScriptIsLocalPlayerUsingController, "Returns whether the player is playing with a controller or not." );
  4218. ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageCenterAll, "Prints an alert message in the center print method to all players." );
  4219. ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageChatAll, "Prints a message in chat to all players." );
  4220. ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageCenterTeam, "Prints an alert message in the center print method to the specified team." );
  4221. ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageChatTeam, "Prints a message in chat to the specified team." );
  4222. ScriptRegisterFunction( g_pScriptVM, ScriptGetGameMode, "Gets the current game mode." );
  4223. ScriptRegisterFunction( g_pScriptVM, ScriptGetGameType, "Gets the current game type." );
  4224. ScriptRegisterFunction( g_pScriptVM, ScriptGetRoundsPlayed, "Get the number of rounds played so far." );
  4225. ScriptRegisterFunction( g_pScriptVM, ScriptIsWarmupPeriod, "Is it warmup or not." );
  4226. ScriptRegisterFunction( g_pScriptVM, ScriptCoopSetBotQuotaAndRefreshSpawns, "Sets the bot quota considering the # of players connected and refreshes the spawns." );
  4227. ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionSpawnFirstEnemies, "Spawns the first wave of enemies in coop." );
  4228. ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionSetNextRespawnIn, "Set the next respawn wave to happen in this many seconds." );
  4229. ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionSpawnNextWave, "Tells the next wave of enemies to spawn in coop. Also respawns player." );
  4230. ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionRespawnDeadPlayers, "Respawns players only." );
  4231. ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionGetMissionNumber, "Gets the mission number for the current map - maps can have multiple missions on them." );
  4232. ScriptRegisterFunction( g_pScriptVM, ScriptCoopResetRoundStartTime, "Resets the round time and starts the mission." );
  4233. ScriptRegisterFunction( g_pScriptVM, ScriptCoopGiveC4sToCTs, "Will give the number of specified C4s to all alive CT players." );
  4234. ScriptRegisterFunction( g_pScriptVM, ScriptCoopCollectBonusCoin, "Marks one of the bonus coins as collected." );
  4235. }
  4236. //-----------------------------------------------------------------------------
  4237. // Purpose: TF2 Specific Client Commands
  4238. // Input :
  4239. // Output :
  4240. //-----------------------------------------------------------------------------
  4241. bool CCSGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  4242. {
  4243. CCSPlayer *pPlayer = ToCSPlayer( pEdict );
  4244. if ( FStrEq( args[0], "changeteam" ) )
  4245. {
  4246. return true;
  4247. }
  4248. else if ( FStrEq( args[0], "nextmap" ) )
  4249. {
  4250. // catch corrupted command
  4251. if ( pPlayer == NULL )
  4252. return true;
  4253. extern ConVar nextmap_print_enabled;
  4254. if ( !nextmap_print_enabled.GetBool() )
  4255. return true;
  4256. if ( pPlayer->m_iNextTimeCheck < gpGlobals->curtime )
  4257. {
  4258. char szNextMap[MAX_PATH];
  4259. if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
  4260. {
  4261. Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
  4262. }
  4263. else
  4264. {
  4265. GetNextLevelName( szNextMap, sizeof( szNextMap ) );
  4266. }
  4267. if ( IsGameConsole() || engine->IsDedicatedServerForXbox() || engine->IsDedicatedServerForPS3() )
  4268. {
  4269. // we know all the map names on console so we can safely assume we have a token for all maps
  4270. const int nMaxLength = MAX_MAP_NAME+10+1;
  4271. char szToken[nMaxLength];
  4272. CreateFriendlyMapNameToken(szNextMap, szToken, nMaxLength);
  4273. ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szToken );
  4274. }
  4275. else
  4276. {
  4277. ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", V_GetFileName( szNextMap ) );
  4278. }
  4279. pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1;
  4280. }
  4281. return true;
  4282. }
  4283. else if ( FStrEq( args[0], "tr_map_show_exit_door_msg" ) )
  4284. {
  4285. IGameEvent * event = gameeventmanager->CreateEvent( "tr_exit_hint_trigger" );
  4286. if ( event )
  4287. {
  4288. gameeventmanager->FireEvent( event );
  4289. }
  4290. //engine->ServerCommand( "ent_fire @tr_exit_hint trigger\n" );
  4291. return true;
  4292. }
  4293. else if( pPlayer != NULL && pPlayer->ClientCommand( args ) )
  4294. {
  4295. return true;
  4296. }
  4297. else if( BaseClass::ClientCommand( pEdict, args ) )
  4298. {
  4299. return true;
  4300. }
  4301. else if ( TheBots->ServerCommand( args.GetCommandString() ) )
  4302. {
  4303. return true;
  4304. }
  4305. else
  4306. {
  4307. return TheBots->ClientCommand( pPlayer, args );
  4308. }
  4309. }
  4310. //-----------------------------------------------------------------------------
  4311. // Purpose: Player has just spawned. Equip them.
  4312. //-----------------------------------------------------------------------------
  4313. void CCSGameRules::PlayerSpawn( CBasePlayer *pBasePlayer )
  4314. {
  4315. CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer );
  4316. if ( !pPlayer )
  4317. Error( "PlayerSpawn" );
  4318. if ( pPlayer->State_Get() != STATE_ACTIVE )
  4319. return;
  4320. if ( CSGameRules()->IsPlayingCoopGuardian()/* || CSGameRules()->IsPlayingCoopMission()*/ )
  4321. CSGameRules()->GuardianUpdateBotAccountAndWeapons( pPlayer );
  4322. pPlayer->EquipSuit();
  4323. // remove any defusers left over from previous random if there is just one random one
  4324. if ( mp_defuser_allocation.GetInt() == DefuserAllocation::Random )
  4325. pPlayer->RemoveDefuser();
  4326. bool addDefault = (pPlayer->GetTeamNumber() > TEAM_SPECTATOR);
  4327. CBaseEntity *pWeaponEntity = NULL;
  4328. while ( ( pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL )
  4329. {
  4330. CGamePlayerEquip *pEquip = dynamic_cast<CGamePlayerEquip*>( pWeaponEntity );
  4331. if ( pEquip && !pEquip->UseOnly() )
  4332. {
  4333. if ( addDefault && pEquip->StripFirst() )
  4334. {
  4335. // remove all our weapons and armor before touching the first game_player_equip
  4336. pPlayer->RemoveAllItems( true );
  4337. }
  4338. pWeaponEntity->Touch( pPlayer );
  4339. addDefault = false;
  4340. }
  4341. }
  4342. if ( addDefault )
  4343. pPlayer->GiveDefaultItems();
  4344. if ( !IsWarmupPeriod() && IsPlayingCooperativeGametype() && !IsQueuedMatchmaking() )
  4345. {
  4346. switch ( pBasePlayer->GetTeamNumber() )
  4347. {
  4348. case TEAM_CT:
  4349. case TEAM_TERRORIST:
  4350. // Ensure that all QMM structures are always initialized for players in coop game type
  4351. ( void ) QueuedMatchmakingPlayersDataFindOrCreate( pPlayer );
  4352. break;
  4353. }
  4354. }
  4355. }
  4356. void CCSGameRules::BroadcastSound( const char *sound, int team )
  4357. {
  4358. CBroadcastRecipientFilter filter;
  4359. filter.MakeReliable();
  4360. if( team != -1 )
  4361. {
  4362. filter.RemoveAllRecipients();
  4363. filter.AddRecipientsByTeam( GetGlobalTeam(team) );
  4364. }
  4365. CCSUsrMsg_SendAudio msg;
  4366. msg.set_radio_sound( sound ) ;
  4367. SendUserMessage( filter, CS_UM_SendAudio, msg );
  4368. }
  4369. //-----------------------------------------------------------------------------
  4370. // Purpose: Player has just spawned. Equip them.
  4371. //-----------------------------------------------------------------------------
  4372. // return a multiplier that should adjust the damage done by a blast at position vecSrc to something at the position
  4373. // vecEnd. This will take into account the density of an entity that blocks the line of sight from one position to
  4374. // the other.
  4375. //
  4376. // this algorithm was taken from the HL2 version of RadiusDamage.
  4377. float CCSGameRules::GetExplosionDamageAdjustment(Vector & vecSrc, Vector & vecEnd, CBaseEntity *pEntityToIgnore)
  4378. {
  4379. float retval = 0.0;
  4380. trace_t tr;
  4381. UTIL_TraceLine(vecSrc, vecEnd, MASK_SHOT, pEntityToIgnore, COLLISION_GROUP_NONE, &tr);
  4382. if (tr.fraction == 1.0)
  4383. {
  4384. retval = 1.0;
  4385. }
  4386. else if (!(tr.DidHitWorld()) && (tr.m_pEnt != NULL) && (tr.m_pEnt != pEntityToIgnore) && (tr.m_pEnt->GetOwnerEntity() != pEntityToIgnore))
  4387. {
  4388. // if we didn't hit world geometry perhaps there's still damage to be done here.
  4389. CBaseEntity *blockingEntity = tr.m_pEnt;
  4390. // check to see if this part of the player is visible if entities are ignored.
  4391. UTIL_TraceLine(vecSrc, vecEnd, CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &tr);
  4392. if (tr.fraction == 1.0)
  4393. {
  4394. if ((blockingEntity != NULL) && (blockingEntity->VPhysicsGetObject() != NULL))
  4395. {
  4396. int nMaterialIndex = blockingEntity->VPhysicsGetObject()->GetMaterialIndex();
  4397. float flDensity;
  4398. float flThickness;
  4399. float flFriction;
  4400. float flElasticity;
  4401. physprops->GetPhysicsProperties( nMaterialIndex, &flDensity,
  4402. &flThickness, &flFriction, &flElasticity );
  4403. const float DENSITY_ABSORB_ALL_DAMAGE = 3000.0;
  4404. float scale = flDensity / DENSITY_ABSORB_ALL_DAMAGE;
  4405. if ((scale >= 0.0) && (scale < 1.0))
  4406. {
  4407. retval = 1.0 - scale;
  4408. }
  4409. else if (scale < 0.0)
  4410. {
  4411. // should never happen, but just in case.
  4412. retval = 1.0;
  4413. }
  4414. }
  4415. else
  4416. {
  4417. 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.
  4418. }
  4419. }
  4420. }
  4421. return retval;
  4422. }
  4423. // returns the percentage of the player that is visible from the given point in the world.
  4424. // return value is between 0 and 1.
  4425. float CCSGameRules::GetAmountOfEntityVisible(Vector & vecSrc, CBaseEntity *entity)
  4426. {
  4427. float retval = 0.0;
  4428. const float damagePercentageChest = 0.40;
  4429. const float damagePercentageHead = 0.20;
  4430. const float damagePercentageFeet = 0.20;
  4431. const float damagePercentageRightSide = 0.10;
  4432. const float damagePercentageLeftSide = 0.10;
  4433. if (!(entity->IsPlayer()))
  4434. {
  4435. // the entity is not a player, so the damage is all or nothing.
  4436. Vector vecTarget;
  4437. vecTarget = entity->BodyTarget(vecSrc, false);
  4438. return GetExplosionDamageAdjustment(vecSrc, vecTarget, entity);
  4439. }
  4440. CCSPlayer *player = (CCSPlayer *)entity;
  4441. // check what parts of the player we can see from this point and modify the return value accordingly.
  4442. float chestHeightFromFeet;
  4443. float armDistanceFromChest = HalfHumanWidth;
  4444. // calculate positions of various points on the target player's body
  4445. Vector vecFeet = player->GetAbsOrigin();
  4446. Vector vecChest = player->BodyTarget(vecSrc, false);
  4447. chestHeightFromFeet = vecChest.z - vecFeet.z; // compute the distance from the chest to the feet. (this accounts for ducking and the like)
  4448. Vector vecHead = player->GetAbsOrigin();
  4449. vecHead.z += HumanHeight;
  4450. Vector vecRightFacing;
  4451. AngleVectors(player->GetAbsAngles(), NULL, &vecRightFacing, NULL);
  4452. vecRightFacing.NormalizeInPlace();
  4453. vecRightFacing = vecRightFacing * armDistanceFromChest;
  4454. Vector vecLeftSide = player->GetAbsOrigin();
  4455. vecLeftSide.x -= vecRightFacing.x;
  4456. vecLeftSide.y -= vecRightFacing.y;
  4457. vecLeftSide.z += chestHeightFromFeet;
  4458. Vector vecRightSide = player->GetAbsOrigin();
  4459. vecRightSide.x += vecRightFacing.x;
  4460. vecRightSide.y += vecRightFacing.y;
  4461. vecRightSide.z += chestHeightFromFeet;
  4462. // check chest
  4463. float damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecChest, entity);
  4464. retval += (damagePercentageChest * damageAdjustment);
  4465. // check top of head
  4466. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecHead, entity);
  4467. retval += (damagePercentageHead * damageAdjustment);
  4468. // check feet
  4469. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecFeet, entity);
  4470. retval += (damagePercentageFeet * damageAdjustment);
  4471. // check left "edge"
  4472. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecLeftSide, entity);
  4473. retval += (damagePercentageLeftSide * damageAdjustment);
  4474. // check right "edge"
  4475. damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecRightSide, entity);
  4476. retval += (damagePercentageRightSide * damageAdjustment);
  4477. return retval;
  4478. }
  4479. void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity * pEntityIgnore )
  4480. {
  4481. RadiusDamage( info, vecSrcIn, flRadius, iClassIgnore, false );
  4482. }
  4483. // Add the ability to ignore the world trace
  4484. void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld )
  4485. {
  4486. CBaseEntity *pEntity = NULL;
  4487. trace_t tr;
  4488. float falloff, damagePercentage;
  4489. Vector vecSpot;
  4490. Vector vecToTarget;
  4491. Vector vecEndPos;
  4492. // [tj] The number of enemy players this explosion killed
  4493. int numberOfEnemyPlayersKilledByThisExplosion = 0;
  4494. // [tj] who we award the achievement to if enough players are killed
  4495. CCSPlayer* pCSExplosionAttacker = ToCSPlayer(info.GetAttacker());
  4496. // [tj] used to determine which achievement to award for sufficient kills
  4497. CBaseEntity* pInflictor = info.GetInflictor();
  4498. bool isGrenade = dynamic_cast< CHEGrenadeProjectile* >( pInflictor ) != NULL;
  4499. bool isBomb = pInflictor && V_strcmp(pInflictor->GetClassname(), "planted_c4") == 0;
  4500. vecEndPos.Init();
  4501. Vector vecSrc = vecSrcIn;
  4502. damagePercentage = 1.0;
  4503. if ( flRadius )
  4504. falloff = info.GetDamage() / flRadius;
  4505. else
  4506. falloff = 1.0;
  4507. //int bInWater = (UTIL_PointContents ( vecSrc, MASK_WATER ) & MASK_WATER) ? true : false;
  4508. vecSrc.z += 1;// in case grenade is lying on the ground
  4509. // iterate on all entities in the vicinity.
  4510. for ( CEntitySphereQuery sphere( vecSrc, flRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  4511. {
  4512. // [tj] We have to save whether or not the player is killed so we don't give credit
  4513. // for pre-dead players.
  4514. bool wasAliveBeforeExplosion = false;
  4515. CCSPlayer* pCSExplosionVictim = ToCSPlayer(pEntity);
  4516. if (pCSExplosionVictim)
  4517. {
  4518. wasAliveBeforeExplosion = pCSExplosionVictim->IsAlive();
  4519. }
  4520. bool bIgnoreDamageToThisEntity = ( pEntity->m_takedamage == DAMAGE_NO ) ||
  4521. ( pCSExplosionVictim && !wasAliveBeforeExplosion );
  4522. if ( !bIgnoreDamageToThisEntity )
  4523. {
  4524. // UNDONE: this should check a damage mask, not an ignore
  4525. if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  4526. {// houndeyes don't hurt other houndeyes with their attack
  4527. continue;
  4528. }
  4529. //// blasts used to not travel into or out of water, users assumed it was a bug. Fix is not to run this check -wills
  4530. //if ( !bIgnoreWorld )
  4531. //{
  4532. // if (bInWater && pEntity->GetWaterLevel() == WL_NotInWater)
  4533. // continue;
  4534. // if (!bInWater && pEntity->GetWaterLevel() == WL_Eyes)
  4535. // continue;
  4536. //}
  4537. // radius damage can only be blocked by the world
  4538. vecSpot = pEntity->BodyTarget( vecSrc );
  4539. bool bHit = false;
  4540. if( bIgnoreWorld )
  4541. {
  4542. vecEndPos = vecSpot;
  4543. bHit = true;
  4544. }
  4545. else
  4546. {
  4547. // get the percentage of the target entity that is visible from the
  4548. // explosion position.
  4549. damagePercentage = GetAmountOfEntityVisible(vecSrc, pEntity);
  4550. if (damagePercentage > 0.0)
  4551. {
  4552. vecEndPos = vecSpot;
  4553. bHit = true;
  4554. }
  4555. }
  4556. if ( bHit )
  4557. {
  4558. // the explosion can 'see' this entity, so hurt them!
  4559. //vecToTarget = ( vecSrc - vecEndPos );
  4560. vecToTarget = ( vecEndPos - vecSrc );
  4561. // use a Gaussian function to describe the damage falloff over distance, with flRadius equal to 3 * sigma
  4562. // this results in the following values:
  4563. //
  4564. // Range Fraction Damage
  4565. // 0.0 100%
  4566. // 0.1 96%
  4567. // 0.2 84%
  4568. // 0.3 67%
  4569. // 0.4 49%
  4570. // 0.5 32%
  4571. // 0.6 20%
  4572. // 0.7 11%
  4573. // 0.8 6%
  4574. // 0.9 3%
  4575. // 1.0 1%
  4576. float fDist = vecToTarget.Length();
  4577. float fSigma = flRadius / 3.0f; // flRadius specifies 3rd standard deviation (0.0111 damage at this range)
  4578. float fGaussianFalloff = exp(-fDist * fDist / (2.0f * fSigma * fSigma));
  4579. float flAdjustedDamage = info.GetDamage() * fGaussianFalloff * damagePercentage;
  4580. if ( flAdjustedDamage > 0 )
  4581. {
  4582. CTakeDamageInfo adjustedInfo = info;
  4583. adjustedInfo.SetRadius( flRadius );
  4584. adjustedInfo.SetDamage( flAdjustedDamage );
  4585. Vector dir = vecToTarget;
  4586. VectorNormalize( dir );
  4587. // If we don't have a damage force, manufacture one
  4588. if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
  4589. {
  4590. CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc, 1.5 /* explosion scale! */ );
  4591. }
  4592. else
  4593. {
  4594. // Assume the force passed in is the maximum force. Decay it based on falloff.
  4595. float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
  4596. adjustedInfo.SetDamageForce( dir * flForce );
  4597. adjustedInfo.SetDamagePosition( vecSrc );
  4598. }
  4599. Vector vecTarget;
  4600. vecTarget = pEntity->BodyTarget(vecSrc, false);
  4601. UTIL_TraceLine(vecSrc, vecTarget, MASK_SHOT, NULL, COLLISION_GROUP_NONE, &tr);
  4602. // blasts always hit chest
  4603. tr.hitgroup = HITGROUP_GENERIC;
  4604. if (tr.fraction != 1.0)
  4605. {
  4606. // this has to be done to make breakable glass work.
  4607. ClearMultiDamage( );
  4608. pEntity->DispatchTraceAttack( adjustedInfo, dir, &tr );
  4609. ApplyMultiDamage();
  4610. }
  4611. else
  4612. {
  4613. pEntity->TakeDamage( adjustedInfo );
  4614. }
  4615. // Now hit all triggers along the way that respond to damage...
  4616. pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, vecEndPos, dir );
  4617. // [sbodenbender] Increment grenade damage stat
  4618. if (pCSExplosionVictim &&
  4619. pCSExplosionAttacker &&
  4620. isGrenade &&
  4621. pCSExplosionVictim->GetTeamNumber() != pCSExplosionAttacker->GetTeamNumber() )
  4622. {
  4623. CCS_GameStats.IncrementStat(pCSExplosionAttacker, CSSTAT_GRENADE_DAMAGE, static_cast<int>(adjustedInfo.GetDamage()));
  4624. }
  4625. }
  4626. }
  4627. }
  4628. // [tj] Count up victims of area of effect damage for achievement purposes
  4629. if ( pCSExplosionVictim )
  4630. {
  4631. //If the bomb is exploding, set the attacker to the planter (we can't put this in the CTakeDamageInfo, since
  4632. //players aren't supposed to get credit for bomb kills)
  4633. if ( isBomb )
  4634. {
  4635. CPlantedC4* bomb = static_cast<CPlantedC4*>( pInflictor );
  4636. if ( bomb )
  4637. {
  4638. pCSExplosionAttacker = bomb->GetPlanter();
  4639. }
  4640. }
  4641. //Count check to make sure we killed an enemy player
  4642. if( pCSExplosionAttacker &&
  4643. !pCSExplosionVictim->IsAlive() &&
  4644. wasAliveBeforeExplosion &&
  4645. pCSExplosionVictim->GetTeamNumber() != pCSExplosionAttacker->GetTeamNumber())
  4646. {
  4647. numberOfEnemyPlayersKilledByThisExplosion++;
  4648. }
  4649. }
  4650. }
  4651. // [tj] Depending on which type of explosion it was, award the appropriate achievement.
  4652. if ( pCSExplosionAttacker && isGrenade && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::GrenadeMultiKill_MinKills )
  4653. {
  4654. pCSExplosionAttacker->AwardAchievement( CSGrenadeMultikill );
  4655. pCSExplosionAttacker->CheckMaxGrenadeKills( numberOfEnemyPlayersKilledByThisExplosion );
  4656. }
  4657. if ( pCSExplosionAttacker && isBomb && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::BombMultiKill_MinKills )
  4658. {
  4659. pCSExplosionAttacker->AwardAchievement( CSBombMultikill );
  4660. }
  4661. }
  4662. CCSPlayer* CCSGameRules::CheckAndAwardAssists( CCSPlayer* pCSVictim, CCSPlayer* pKiller )
  4663. {
  4664. CUtlLinkedList< CDamageRecord *, int >& victimDamageTakenList = pCSVictim->GetDamageList();
  4665. float maxDamage = 0.0f;
  4666. CCSPlayer* maxDamagePlayer = NULL;
  4667. FOR_EACH_LL( victimDamageTakenList, ii )
  4668. {
  4669. if ( victimDamageTakenList[ii]->GetPlayerRecipientPtr() == pCSVictim )
  4670. {
  4671. CCSPlayer* pAttackerPlayer = victimDamageTakenList[ii]->GetPlayerDamagerPtr();
  4672. if ( pAttackerPlayer )
  4673. {
  4674. if ( (victimDamageTakenList[ii]->GetDamage() > maxDamage) && (pAttackerPlayer != pKiller) && ( pAttackerPlayer != pCSVictim ) )
  4675. {
  4676. maxDamage = victimDamageTakenList[ii]->GetDamage();
  4677. maxDamagePlayer = pAttackerPlayer;
  4678. }
  4679. }
  4680. }
  4681. }
  4682. // note, only the highest damaging player can be awarded an assist
  4683. if ( maxDamagePlayer && (maxDamage > cs_AssistDamageThreshold.GetFloat( )) )
  4684. {
  4685. ScorePlayerAssist( maxDamagePlayer, pCSVictim );
  4686. return maxDamagePlayer;
  4687. }
  4688. return NULL;
  4689. }
  4690. //-----------------------------------------------------------------------------
  4691. // Purpose:
  4692. // Input : *pVictim -
  4693. // *pKiller -
  4694. // *pInflictor -
  4695. //-----------------------------------------------------------------------------
  4696. IGameEvent * CCSGameRules::CreateWeaponKillGameEvent( char const *szEventName, const CTakeDamageInfo &info )
  4697. {
  4698. IGameEvent * event = gameeventmanager->CreateEvent( szEventName );
  4699. if ( !event )
  4700. return NULL;
  4701. // Work out what killed the player, and prepare to send a message to all clients about it
  4702. const char *killer_weapon_name = "world"; // by default, the player is killed by the world
  4703. int killer_ID = 0;
  4704. char killer_XUID[64] = { '\0' };
  4705. char killer_weapon_itemid[64] = { '\0' };
  4706. char killer_weapon_fauxitemid[64] = { '\0' };
  4707. char kill_weapon_originalowner_xuid[64] = { '\0' };
  4708. // Find the killer & the scorer
  4709. CBaseEntity *pInflictor = info.GetInflictor();
  4710. CBaseEntity *pKiller = info.GetAttacker();
  4711. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
  4712. bool bHeadshot = false;
  4713. if ( pScorer ) // Is the killer a client?
  4714. {
  4715. killer_ID = pScorer->GetUserID();
  4716. V_sprintf_safe( killer_XUID, "%llu", pScorer->GetSteamIDAsUInt64() );
  4717. if( info.GetDamageType() & DMG_HEADSHOT )
  4718. {
  4719. //to enable drawing the headshot icon as well as the weapon icon,
  4720. bHeadshot = true;
  4721. }
  4722. if ( pInflictor )
  4723. {
  4724. if ( pInflictor == pScorer )
  4725. {
  4726. // If the inflictor is the killer, then it must be their current weapon doing the damage
  4727. if ( pScorer->GetActiveWeapon() )
  4728. {
  4729. CEconItemView *pItem = pScorer->GetActiveWeapon()->GetEconItemView();
  4730. killer_weapon_name = ( ( pItem && pItem->IsValid() && pItem->GetItemIndex() && pItem->GetItemDefinition() )
  4731. ? pItem->GetItemDefinition()->GetDefinitionName()
  4732. : pScorer->GetActiveWeapon()->GetClassname() ); //GetDeathNoticeName();
  4733. if ( pItem && pItem->IsValid() )
  4734. {
  4735. V_sprintf_safe( killer_weapon_itemid, "%llu", pItem->GetItemID() );
  4736. V_sprintf_safe( killer_weapon_fauxitemid, "%llu", CombinedItemIdMakeFromDefIndexAndPaint( pItem->GetItemDefinition()->GetDefinitionIndex(), pItem->GetCustomPaintKitIndex() ) );
  4737. }
  4738. //the default weapon knife looks different in the kill feed depending on faction
  4739. if ( !V_strcmp( killer_weapon_name, "weapon_knife" ) )
  4740. {
  4741. killer_weapon_name = "weapon_knife_default_t";
  4742. if ( pScorer->GetTeamNumber() == TEAM_CT )
  4743. killer_weapon_name = "weapon_knife_default_ct";
  4744. }
  4745. }
  4746. }
  4747. else
  4748. {
  4749. killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy
  4750. }
  4751. }
  4752. }
  4753. else
  4754. {
  4755. killer_weapon_name = STRING( pInflictor->m_iClassname );
  4756. }
  4757. // strip the NPC_* or weapon_* from the inflictor's classname
  4758. if ( IsWeaponClassname( killer_weapon_name ) )
  4759. {
  4760. killer_weapon_name += WEAPON_CLASSNAME_PREFIX_LENGTH;
  4761. }
  4762. else if ( StringHasPrefix( killer_weapon_name, "NPC_" ) )
  4763. {
  4764. killer_weapon_name += V_strlen( "NPC_" );
  4765. }
  4766. else if ( StringHasPrefixCaseSensitive( killer_weapon_name, "func_" ) )
  4767. {
  4768. killer_weapon_name += V_strlen( "func_" );
  4769. }
  4770. else if( StringHasPrefixCaseSensitive( killer_weapon_name, "hegrenade" ) ) //"hegrenade_projectile"
  4771. {
  4772. killer_weapon_name = "hegrenade";
  4773. }
  4774. else if( StringHasPrefixCaseSensitive( killer_weapon_name, "flashbang" ) ) //"flashbang_projectile"
  4775. {
  4776. killer_weapon_name = "flashbang";
  4777. }
  4778. else if( StringHasPrefixCaseSensitive( killer_weapon_name, "decoy" ) ) //"decoy_projectile"
  4779. {
  4780. killer_weapon_name = "decoy";
  4781. }
  4782. else if( StringHasPrefixCaseSensitive( killer_weapon_name, "smokegrenade" ) ) //"smokegrenade_projectile"
  4783. {
  4784. killer_weapon_name = "smokegrenade";
  4785. }
  4786. event->SetInt("attacker", killer_ID );
  4787. event->SetString("weapon", killer_weapon_name );
  4788. event->SetString("weapon_itemid", killer_weapon_itemid );
  4789. event->SetString("weapon_fauxitemid", killer_weapon_fauxitemid );
  4790. event->SetBool("headshot", bHeadshot );
  4791. event->SetString("weapon_originalowner_xuid", kill_weapon_originalowner_xuid );
  4792. // If the weapon has a silencer but it isn't currently attached, add "_off" suffix to the weapon name so hud can find an alternate icon
  4793. if ( pInflictor && pScorer && (pInflictor == pScorer) )
  4794. {
  4795. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pScorer->GetActiveWeapon() );
  4796. if ( pWeapon && pWeapon->HasSilencer() && !pWeapon->IsSilenced() )
  4797. {
  4798. if ( V_strEndsWith( killer_weapon_name, "silencer" ) )
  4799. {
  4800. char szTempWeaponNameWithOFFsuffix[64];
  4801. V_snprintf( szTempWeaponNameWithOFFsuffix, sizeof(szTempWeaponNameWithOFFsuffix), "%s_off", killer_weapon_name );
  4802. event->SetString("weapon", szTempWeaponNameWithOFFsuffix );
  4803. }
  4804. }
  4805. }
  4806. event->SetInt( "penetrated", info.GetObjectsPenetrated() );
  4807. return event;
  4808. }
  4809. //-----------------------------------------------------------------------------
  4810. // Purpose:
  4811. // Input : *pVictim -
  4812. // *pKiller -
  4813. // *pInflictor -
  4814. //-----------------------------------------------------------------------------
  4815. void CCSGameRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  4816. {
  4817. // Find the killer & the scorer
  4818. CBaseEntity *pInflictor = info.GetInflictor();
  4819. CBaseEntity *pKiller = info.GetAttacker();
  4820. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
  4821. CCSPlayer *pCSVictim = (CCSPlayer*)(pVictim);
  4822. CCSPlayer *pAssiter = CheckAndAwardAssists( pCSVictim, (CCSPlayer *)pKiller );
  4823. if ( IGameEvent * event = CreateWeaponKillGameEvent( "player_death", info ) )
  4824. {
  4825. event->SetInt("userid", pVictim->GetUserID() );
  4826. event->SetInt("assister", pAssiter ? pAssiter->GetUserID() : 0 );
  4827. if( !OnReplayPrompt( pVictim, pScorer ) )
  4828. event->SetBool( "noreplay", true ); // prevent UI display of "press to replay" message if HLTV data is unavailable
  4829. bool bHeadshot = ( pScorer ) && ( info.GetDamageType() & DMG_HEADSHOT );
  4830. int priority = bHeadshot ? 8 : 7;
  4831. #ifdef GAME_DLL
  4832. CCSPlayer* player = ( dynamic_cast< CCSPlayer* >(pScorer) );
  4833. if ( player )
  4834. {
  4835. priority += player->GetKillStreak();
  4836. }
  4837. #endif
  4838. event->SetInt( "priority", priority ); // player_death
  4839. if ( pCSVictim->GetDeathFlags() & CS_DEATH_DOMINATION )
  4840. {
  4841. event->SetInt( "dominated", 1 );
  4842. }
  4843. else if ( pCSVictim->GetDeathFlags() & CS_DEATH_REVENGE )
  4844. {
  4845. event->SetInt( "revenge", 1 );
  4846. }
  4847. gameeventmanager->FireEvent( event );
  4848. }
  4849. }
  4850. bool EconEntity_OnOwnerKillEaterEvent( CEconItemView *pEconItemView, CCSPlayer *pOwner, CCSPlayer *pVictim, kill_eater_event_t eEventType, int iAmount /*= 1*/, uint32 *pNewValue /* = NULL */ )
  4851. {
  4852. if ( pNewValue )
  4853. *pNewValue = 0;
  4854. // Kill-eater weapons.
  4855. if ( !pEconItemView )
  4856. return false;
  4857. if ( !pOwner )
  4858. return false;
  4859. // Ignore events where we're affecting ourself.
  4860. if ( pOwner == pVictim )
  4861. return false;
  4862. // Always require that we have at least the base kill eater attribute before sending any messages
  4863. // to the GC.
  4864. static CSchemaAttributeDefHandle pAttr_KillEater( "kill eater" );
  4865. if ( !pEconItemView->FindAttribute( pAttr_KillEater ) )
  4866. return false;
  4867. // Don't bother sending a message to the GC if either party is a bot, unless we're tracking events against
  4868. // bots specifically.
  4869. CSteamID KillerSteamID, VictimSteamID;
  4870. if ( !pOwner->GetSteamID( &KillerSteamID ) )
  4871. return false;
  4872. // comment this out to have StatTrak count bot kills
  4873. if ( pVictim && !pVictim->GetSteamID( &VictimSteamID ) )
  4874. return false;
  4875. // we don't want to increment the killeater if the weapon owner is not the killer
  4876. if ( pEconItemView->GetAccountID() != KillerSteamID.GetAccountID() )
  4877. return false;
  4878. // comment this out to have StatTrak count bot kills
  4879. if ( pVictim && pVictim->IsFakeClient() )
  4880. return false;
  4881. // Also require that we have whatever event type we're looking for, unless we're looking for regular
  4882. // player kills in which case we may or may not have a field to describe that.
  4883. AssertMsg( GetKillEaterAttrPairCount() == 1, "This function is now assuming there is only one stattrak value being updated when called. If we're adding multiple killeaters per item, this needs to be revisited." );
  4884. const CEconItemAttributeDefinition *pScoreAttribDef = GetKillEaterAttrPair_Score(0);
  4885. if ( !pScoreAttribDef )
  4886. return false;
  4887. // If we don't have this attribute, move on. It's possible to be missing this attribute but still
  4888. // have the next one in the list if we have user-customized tracking types.
  4889. uint32 unCurrent;
  4890. if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItemView, pScoreAttribDef, &unCurrent ) )
  4891. return false;
  4892. const CEconItemAttributeDefinition *pScoreTypeAttribDef = GetKillEaterAttrPair_Type(0);
  4893. if ( !pScoreTypeAttribDef )
  4894. return false;
  4895. unCurrent += iAmount;
  4896. pEconItemView->UpdateNetworkedDynamicAttributesForDemos( pScoreAttribDef->GetDefinitionIndex(), *(float*)&unCurrent );
  4897. if ( pNewValue )
  4898. *pNewValue = unCurrent;
  4899. return true;
  4900. }
  4901. //=========================================================
  4902. //=========================================================
  4903. void CCSGameRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  4904. {
  4905. CBaseEntity *pInflictor = info.GetInflictor();
  4906. CBaseEntity *pKiller = info.GetAttacker();
  4907. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
  4908. CCSPlayer *pCSVictim = (CCSPlayer *)pVictim;
  4909. CCSPlayer *pCSScorer = (CCSPlayer *)pScorer;
  4910. // determine whether this is a positive or negative kill
  4911. int numPointsForPlayerKill = IPointsForKill( pScorer, pVictim );
  4912. CWeaponCSBase* pWeapon = dynamic_cast<CWeaponCSBase *>( info.GetWeapon() );
  4913. // [tj] Flag the round as non-lossless for the appropriate team.
  4914. // [menglish] Set the death flags depending on a nemesis system
  4915. if ( pVictim->GetTeamNumber() == TEAM_TERRORIST )
  4916. {
  4917. m_bNoTerroristsKilled = false;
  4918. m_bNoTerroristsDamaged = false;
  4919. }
  4920. if ( pVictim->GetTeamNumber() == TEAM_CT )
  4921. {
  4922. m_bNoCTsKilled = false;
  4923. m_bNoCTsDamaged = false;
  4924. }
  4925. m_bCanDonateWeapons = false;
  4926. if ( m_pFirstKill == NULL && pCSScorer != pVictim )
  4927. {
  4928. m_pFirstKill = pCSScorer;
  4929. m_firstKillTime = gpGlobals->curtime - m_fRoundStartTime;
  4930. }
  4931. // determine if this kill affected a nemesis relationship
  4932. int iDeathFlags = 0;
  4933. if ( pScorer )
  4934. {
  4935. CCS_GameStats.CalculateOverkill( pCSScorer, pCSVictim);
  4936. CCS_GameStats.CalcDominationAndRevenge( pCSScorer, pCSVictim, &iDeathFlags );
  4937. }
  4938. pCSVictim->SetDeathFlags( iDeathFlags );
  4939. pCSVictim->SetLastKillerIndex( pCSScorer ? pCSScorer->entindex() : 0 );
  4940. // If we're killed by the C4, we do a subset of BaseClass::PlayerKilled()
  4941. // Specifically, we shouldn't lose any points or show death notices, to match goldsrc
  4942. if ( Q_strcmp(pKiller->GetClassname(), "planted_c4" ) == 0 )
  4943. {
  4944. // dvsents2: uncomment when removing all FireTargets
  4945. // variant_t value;
  4946. // g_EventQueue.AddEvent( "game_playerdie", "Use", value, 0, pVictim, pVictim );
  4947. FireTargets( "game_playerdie", pVictim, pVictim, USE_TOGGLE, 0 );
  4948. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" [%.0f %.0f %.0f] was killed by the bomb.\n",
  4949. pCSVictim->GetPlayerName(),
  4950. pCSVictim->GetUserID(),
  4951. pCSVictim->GetNetworkIDString(),
  4952. pCSVictim->GetTeam()->GetName(),
  4953. pCSVictim->GetAbsOrigin().x,
  4954. pCSVictim->GetAbsOrigin().y,
  4955. pCSVictim->GetAbsOrigin().z );
  4956. }
  4957. else
  4958. {
  4959. if ( ( !pCSScorer && !dynamic_cast< CBaseGrenade * >( pInflictor ) && !dynamic_cast< CInferno * >( pInflictor ) ) || // special case for suicide tracking: exclude grenade exploding when thrower disconnected
  4960. ( pCSVictim == pCSScorer ) )
  4961. {
  4962. if ( UseSuicidePenalty() &&
  4963. !pCSVictim->IsControllingBot() &&
  4964. mp_autokick.GetBool() &&
  4965. !CSGameRules()->IsPlayingOffline() &&
  4966. !CSGameRules()->IsWarmupPeriod() &&
  4967. ( pVictim->GetPendingTeamNumber() == pVictim->GetTeamNumber() ) ) // don't punish suicides due to team change
  4968. {
  4969. pVictim->m_nSuicides++;
  4970. if ( pVictim->m_nSuicides >= 5 )
  4971. {
  4972. if ( sv_kick_ban_duration.GetInt() > 0 )
  4973. {
  4974. engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pVictim->GetUserID() ) );
  4975. }
  4976. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For suiciding too many times\n", pVictim->GetUserID(), 1 ) );
  4977. }
  4978. }
  4979. }
  4980. if( pCSVictim == pCSScorer )
  4981. {
  4982. CSGameRules()->ScorePlayerSuicide( pCSVictim );
  4983. }
  4984. else if ( numPointsForPlayerKill >= 0 )
  4985. {
  4986. // Track kills per weapon before increment frag count call
  4987. if ( pWeapon && pCSScorer )
  4988. {
  4989. switch ( pWeapon->GetWeaponType() )
  4990. {
  4991. case WEAPONTYPE_PISTOL:
  4992. {
  4993. int numPistolKills, numSniperKills;
  4994. pCSScorer->GetEnemyWeaponKills( numPistolKills, numSniperKills );
  4995. pCSScorer->SetEnemyWeaponKills( numPistolKills + 1, numSniperKills );
  4996. }
  4997. break;
  4998. case WEAPONTYPE_SNIPER_RIFLE:
  4999. {
  5000. int numPistolKills, numSniperKills;
  5001. pCSScorer->GetEnemyWeaponKills( numPistolKills, numSniperKills );
  5002. pCSScorer->SetEnemyWeaponKills( numPistolKills, numSniperKills + 1 );
  5003. }
  5004. break;
  5005. }
  5006. }
  5007. }
  5008. BaseClass::PlayerKilled( pVictim, info );
  5009. }
  5010. // check for team-killing, and give monetary rewards/penalties
  5011. // Find the killer & the scorer
  5012. if ( !pScorer )
  5013. {
  5014. pCSVictim->SetLastConcurrentKilled( 0 );
  5015. return;
  5016. }
  5017. if ( numPointsForPlayerKill < 0 )
  5018. {
  5019. // team-killer!
  5020. pCSScorer->AddAccountAward( PlayerCashAward::KILL_TEAMMATE );
  5021. // m_DeferredCallQueue.QueueCall( pCSScorer, &CCSPlayer::AddAccountAward, PlayerCashAward::KILL_TEAMMATE );
  5022. pCSScorer->IncrementTeamKillsCount( 1 );
  5023. pCSScorer->m_bJustKilledTeammate = true;
  5024. ClientPrint( pCSScorer, HUD_PRINTCENTER, "#SFUI_Notice_Killed_Teammate" );
  5025. if ( mp_autokick.GetBool() )
  5026. {
  5027. if ( !IsPlayingOffline() )
  5028. {
  5029. char strTeamKills[64];
  5030. Q_snprintf( strTeamKills, sizeof( strTeamKills ), "%d", (3 - pCSScorer->m_iTeamKills) );
  5031. ClientPrint( pCSScorer, HUD_PRINTTALK, "#SFUI_Notice_Game_teammate_kills", strTeamKills ); // this includes a " of 3" in it
  5032. }
  5033. if ( pCSScorer->m_iTeamKills >= 3 )
  5034. {
  5035. if ( !IsPlayingOffline() )
  5036. {
  5037. ClientPrint( pCSScorer, HUD_PRINTTALK, "#SFUI_Notice_Banned_For_Killing_Teammates" );
  5038. if ( sv_kick_ban_duration.GetInt() > 0 )
  5039. {
  5040. SendKickBanToGC( pCSScorer, k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_TKLimit );
  5041. // don't roll the kick command into this, it will fail on a lan, where kickid will go through
  5042. engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pCSScorer->GetUserID() ) );
  5043. }
  5044. }
  5045. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For killing too many teammates\n", pCSScorer->GetUserID(), IsPlayingOffline() ? 0 : 1 ) );
  5046. }
  5047. else if ( ( mp_spawnprotectiontime.GetInt() > 0 ) && ( GetRoundElapsedTime() < mp_spawnprotectiontime.GetInt() ) )
  5048. {
  5049. const CCSWeaponInfo* pWeaponInfo = CCSPlayer::GetWeaponInfoFromDamageInfo( info );
  5050. // Special case here: TK at round start using some weapons* only counts towards team damage
  5051. // and team kills amount, but doesn't instantly ban the player due to common occurrence
  5052. // of teammates crossing in front of AWP'er on de_dust2
  5053. // Weapons that will not instantly ban for TK at round start must have zoom levels and
  5054. // team damage from headshot must kill with a single kill
  5055. extern ConVar ff_damage_reduction_bullets;
  5056. if ( !pWeaponInfo || !pWeapon ||
  5057. ( pWeapon->GetZoomLevels() < 2 ) || // weapon doesn't have 2 zoom levels, worth the ban
  5058. ( float( pWeapon->GetDamage() ) * 4.0f * ff_damage_reduction_bullets.GetFloat() <= 99.0f ) || // single friendly headshot will not kill, worth the ban
  5059. !pCSVictim || ( pCSVictim->GetNumAttackersFromDamageList() > 1 ) || // victim already took damage from multiple players, TK should be banned
  5060. ( pCSVictim->GetMostNumHitsDamageRecordFrom(pCSScorer) > 1 ) // TK'er shot the victim multiple times, worth the ban
  5061. )
  5062. {
  5063. if ( !IsPlayingOffline() )
  5064. {
  5065. ClientPrint( pCSScorer, HUD_PRINTTALK, "#SFUI_Notice_Banned_For_TK_Start" );
  5066. if ( sv_kick_ban_duration.GetInt() > 0 )
  5067. {
  5068. SendKickBanToGC( pCSScorer, k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_TKSpawn );
  5069. // don't roll the kick command into this, it will fail on a lan, where kickid will go through
  5070. engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pCSScorer->GetUserID() ) );
  5071. }
  5072. }
  5073. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For killing a teammate at round start\n", pCSScorer->GetUserID(), IsPlayingOffline() ? 0 : 1 ) );
  5074. }
  5075. }
  5076. }
  5077. if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) )
  5078. {
  5079. pCSScorer->m_iDisplayHistoryBits |= DHF_FRIEND_KILLED;
  5080. pCSScorer->HintMessage( "#SFUI_Notice_Hint_careful_around_teammates", false );
  5081. }
  5082. }
  5083. else
  5084. {
  5085. // [tj] Added a check to make sure we don't get money for suicides.
  5086. if (pCSScorer != pCSVictim)
  5087. {
  5088. // Get the cash award from the weapon and scale it by the gamemode factor
  5089. // const CCSWeaponInfo* pWeaponInfo = CCSPlayer::GetWeaponInfoFromDamageInfo(info);
  5090. if ( pWeapon )
  5091. {
  5092. pCSScorer->AddAccountAward( PlayerCashAward::KILLED_ENEMY, pWeapon->GetKillAward(), pWeapon );
  5093. // m_DeferredCallQueue.QueueCall( pCSScorer, &CCSPlayer::AddAccountAward, PlayerCashAward::KILLED_ENEMY, award, pWeaponInfo );
  5094. }
  5095. else
  5096. {
  5097. pCSScorer->AddAccountAward( PlayerCashAward::KILLED_ENEMY );
  5098. // m_DeferredCallQueue.QueueCall( pCSScorer, &CCSPlayer::AddAccountAward, PlayerCashAward::KILLED_ENEMY );
  5099. }
  5100. }
  5101. CWeaponCSBase* pWeapon = dynamic_cast<CWeaponCSBase *>( info.GetWeapon() );
  5102. CEconItemView *pEconWeapon = pWeapon ? pWeapon->GetEconItemView() : NULL ;
  5103. EconEntity_OnOwnerKillEaterEvent( pEconWeapon, pCSScorer, pCSVictim, kKillEaterEvent_PlayerKill );
  5104. /*
  5105. if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_ENEMY_KILLED) )
  5106. {
  5107. pCSScorer->m_iDisplayHistoryBits |= DHF_ENEMY_KILLED;
  5108. pCSScorer->HintMessage( "#SFUI_Notice_Hint_win_round_by_killing_enemy", false );
  5109. }
  5110. */
  5111. }
  5112. if ( CSGameRules()->IsPlayingCoopMission() && mp_use_respawn_waves.GetInt() == 2 )
  5113. {
  5114. if ( pCSVictim->IsBot() && pCSVictim->GetTeamNumber() == TEAM_TERRORIST )
  5115. {
  5116. // get the number of enemies remaining and have one of the surviving CTs call it out
  5117. int count = 0;
  5118. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  5119. {
  5120. CBaseEntity *player = UTIL_PlayerByIndex( i );
  5121. if ( player == NULL || player->GetTeamNumber() == TEAM_CT || !player->IsAlive() )
  5122. continue;
  5123. count++;
  5124. }
  5125. CCSPlayer *pCTPlayer = NULL;
  5126. for ( int i = 1; i <= gpGlobals->maxClients; ++i )
  5127. {
  5128. pCTPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  5129. if ( pCTPlayer && pCTPlayer->GetTeamNumber() == TEAM_CT )
  5130. break;
  5131. }
  5132. if ( pCTPlayer )
  5133. {
  5134. switch ( count )
  5135. {
  5136. case 3:
  5137. pCTPlayer->Radio("ThreeEnemiesLeft", "", true);
  5138. break;
  5139. case 2:
  5140. pCTPlayer->Radio("TwoEnemiesLeft", "", true);
  5141. break;
  5142. case 1:
  5143. pCTPlayer->Radio("OneEnemyLeft", "", true);
  5144. break;
  5145. }
  5146. }
  5147. }
  5148. }
  5149. }
  5150. void CCSGameRules::SendKickBanToGC( CCSPlayer *pPlayer, EMsgGCCStrike15_v2_MatchmakingKickBanReason_t eReason )
  5151. {
  5152. /** Removed for partner depot **/
  5153. }
  5154. void CCSGameRules::SendKickBanToGCforAccountId( uint32 uiAccountId, EMsgGCCStrike15_v2_MatchmakingKickBanReason_t eReason )
  5155. {
  5156. /** Removed for partner depot **/
  5157. }
  5158. void CCSGameRules::InitDefaultAIRelationships()
  5159. {
  5160. // Allocate memory for default relationships
  5161. CBaseCombatCharacter::AllocateDefaultRelationships();
  5162. // --------------------------------------------------------------
  5163. // First initialize table so we can report missing relationships
  5164. // --------------------------------------------------------------
  5165. int i, j;
  5166. int iNumClasses = GameRules() ? GameRules()->NumEntityClasses() : LAST_SHARED_ENTITY_CLASS;
  5167. for (i=0;i<iNumClasses;i++)
  5168. {
  5169. for (j=0;j<iNumClasses;j++)
  5170. {
  5171. // By default all relationships are neutral of priority zero
  5172. CBaseCombatCharacter::SetDefaultRelationship( (Class_T)i, (Class_T)j, D_NU, 0 );
  5173. }
  5174. }
  5175. }
  5176. //------------------------------------------------------------------------------
  5177. // Purpose : Return classify text for classify type
  5178. //------------------------------------------------------------------------------
  5179. const char *CCSGameRules::AIClassText(int classType)
  5180. {
  5181. switch (classType)
  5182. {
  5183. case CLASS_NONE: return "CLASS_NONE";
  5184. case CLASS_PLAYER: return "CLASS_PLAYER";
  5185. default: return "MISSING CLASS in ClassifyText()";
  5186. }
  5187. }
  5188. //-----------------------------------------------------------------------------
  5189. // Purpose: When gaining new technologies in TF, prevent auto switching if we
  5190. // receive a weapon during the switch
  5191. // Input : *pPlayer -
  5192. // *pWeapon -
  5193. // Output : Returns true on success, false on failure.
  5194. //-----------------------------------------------------------------------------
  5195. bool CCSGameRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
  5196. {
  5197. bool bIsBeingGivenItem = false;
  5198. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  5199. if ( pCSPlayer && pCSPlayer->IsBeingGivenItem() )
  5200. bIsBeingGivenItem = true;
  5201. if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() && !bIsBeingGivenItem )
  5202. {
  5203. // Player has an active item, so let's check cl_autowepswitch.
  5204. const char *cl_autowepswitch = engine->GetClientConVarValue( ENTINDEX( pPlayer->edict() ), "cl_autowepswitch" );
  5205. if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 )
  5206. {
  5207. return false;
  5208. }
  5209. }
  5210. if ( pPlayer->IsBot() && !bIsBeingGivenItem )
  5211. {
  5212. return false;
  5213. }
  5214. if ( !GetAllowWeaponSwitch() )
  5215. {
  5216. return false;
  5217. }
  5218. return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon );
  5219. }
  5220. //-----------------------------------------------------------------------------
  5221. // Purpose:
  5222. // Input : allow -
  5223. //-----------------------------------------------------------------------------
  5224. void CCSGameRules::SetAllowWeaponSwitch( bool allow )
  5225. {
  5226. m_bAllowWeaponSwitch = allow;
  5227. }
  5228. //-----------------------------------------------------------------------------
  5229. // Purpose:
  5230. // Output : Returns true on success, false on failure.
  5231. //-----------------------------------------------------------------------------
  5232. bool CCSGameRules::GetAllowWeaponSwitch()
  5233. {
  5234. return m_bAllowWeaponSwitch;
  5235. }
  5236. //-----------------------------------------------------------------------------
  5237. // Purpose:
  5238. // Input : *pPlayer -
  5239. // Output : const char
  5240. //-----------------------------------------------------------------------------
  5241. const char *CCSGameRules::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
  5242. {
  5243. Assert( pPlayer );
  5244. return BaseClass::SetDefaultPlayerTeam( pPlayer );
  5245. }
  5246. void CCSGameRules::LevelInitPreEntity()
  5247. {
  5248. BaseClass::LevelInitPreEntity();
  5249. // TODO for CZ-style hostages: TheHostageChatter->Precache();
  5250. }
  5251. void CCSGameRules::LevelInitPostEntity()
  5252. {
  5253. BaseClass::LevelInitPostEntity();
  5254. m_bLevelInitialized = false; // re-count CT and T start spots now that they exist
  5255. // Figure out from the entities in the map what kind of map this is (bomb run, prison escape, etc).
  5256. CheckMapConditions();
  5257. }
  5258. float CCSGameRules::FlPlayerFallDamage( CBasePlayer *pPlayer )
  5259. {
  5260. float fFallVelocity = pPlayer->m_Local.m_flFallVelocity - CS_PLAYER_MAX_SAFE_FALL_SPEED;
  5261. float fallDamage = fFallVelocity * CS_DAMAGE_FOR_FALL_SPEED;
  5262. if ( fallDamage > 0.0f )
  5263. {
  5264. // let the bots know
  5265. IGameEvent * event = gameeventmanager->CreateEvent( "player_falldamage" );
  5266. if ( event )
  5267. {
  5268. event->SetInt( "userid", pPlayer->GetUserID() );
  5269. event->SetFloat( "damage", fallDamage );
  5270. event->SetInt( "priority", 4 ); // player_falldamage
  5271. gameeventmanager->FireEvent( event );
  5272. }
  5273. }
  5274. return fallDamage;
  5275. }
  5276. bool CCSGameRules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
  5277. {
  5278. if ( !BaseClass::ClientConnected( pEntity, pszName, pszAddress, reject, maxrejectlen ) )
  5279. return false;
  5280. return true;
  5281. }
  5282. void CCSGameRules::ClientDisconnected( edict_t *pClient )
  5283. {
  5284. // [vitaliy] Players who disconnect while alive and reconnect in competitive mode before the end of the round will not receive win money just like suicide
  5285. if ( CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pClient ) ) )
  5286. {
  5287. if ( pPlayer->IsAlive() )
  5288. {
  5289. pPlayer->ProcessSuicideAsKillReward();
  5290. }
  5291. }
  5292. BaseClass::ClientDisconnected( pClient );
  5293. // [tj] Clear domination data when a player disconnects
  5294. if ( CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pClient ) ) )
  5295. {
  5296. pPlayer->RemoveNemesisRelationships();
  5297. }
  5298. UpdateTeamClanNames( TEAM_TERRORIST );
  5299. UpdateTeamClanNames( TEAM_CT );
  5300. if ( !IsPlayingCoopMission() )
  5301. CheckWinConditions();
  5302. }
  5303. // Called when game rules are destroyed by CWorld
  5304. void CCSGameRules::LevelShutdown()
  5305. {
  5306. BaseClass::LevelShutdown();
  5307. }
  5308. //---------------------------------------------------------------------------------------------------
  5309. /**
  5310. * Check if the scenario has been won/lost.
  5311. * Return true if the scenario is over, false if the scenario is still in progress
  5312. */
  5313. bool CCSGameRules::CheckWinConditions( void )
  5314. {
  5315. if ( mp_ignore_round_win_conditions.GetBool() || m_bLoadingRoundBackupData )
  5316. return false;
  5317. // If a winner has already been determined.. then get the heck out of here
  5318. if ( IsWarmupPeriod() || ( m_iRoundWinStatus != WINNER_NONE ) )
  5319. {
  5320. // still check if we lost players to where we need to do a full reset next round...
  5321. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  5322. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  5323. bool bNeededPlayers = false;
  5324. NeededPlayersCheck( bNeededPlayers );
  5325. return true;
  5326. }
  5327. // Initialize the player counts..
  5328. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  5329. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  5330. /*********************************** GUN GAME PROGRESSIVE CHECK *******************************************************/
  5331. if ( IsPlayingGunGame() )
  5332. {
  5333. if ( GunGameProgressiveEndCheck( ) )
  5334. {
  5335. return true;
  5336. }
  5337. if ( IsPlayingGunGameProgressive() )
  5338. {
  5339. return false;
  5340. }
  5341. }
  5342. /***************************** OTHER PLAYER's CHECK *********************************************************/
  5343. bool bNeededPlayers = false;
  5344. if ( NeededPlayersCheck( bNeededPlayers ) )
  5345. return false;
  5346. /****************************** PRISON ESCAPE CHECK *******************************************************/
  5347. if ( PrisonRoundEndCheck() )
  5348. return true;
  5349. /****************************** BOMB CHECK ********************************************************/
  5350. if ( BombRoundEndCheck( bNeededPlayers ) )
  5351. return true;
  5352. /***************************** TEAM EXTERMINATION CHECK!! *********************************************************/
  5353. // CounterTerrorists won by virture of elimination
  5354. if ( TeamExterminationCheck( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT, bNeededPlayers ) )
  5355. return true;
  5356. /******************************** HOSTAGE RESCUE CHECK ******************************************************/
  5357. if ( HostageRescueRoundEndCheck( bNeededPlayers ) )
  5358. return true;
  5359. /******************************** GUARDIAN MODE CHECK ******************************************************/
  5360. if ( IsPlayingCoopGuardian() && (
  5361. BombPlantedRoundEndCheck()
  5362. || CTsReachedHostageRoundEndCheck()
  5363. ) )
  5364. return true;
  5365. // scenario not won - still in progress
  5366. return false;
  5367. }
  5368. bool CCSGameRules::NeededPlayersCheck( bool &bNeededPlayers )
  5369. {
  5370. if ( IsPlayingTraining() )
  5371. return false;
  5372. // When we silently are performing team manipulations then
  5373. // don't run this players check logic which might cause
  5374. // round to restart ignoring all team scores and player stats
  5375. // assignments.
  5376. if ( m_bLoadingRoundBackupData )
  5377. return false;
  5378. // Run this check differently in queue matchmaking mode
  5379. if ( IsQueuedMatchmaking() )
  5380. {
  5381. if ( !m_bFirstConnected &&
  5382. ( m_iNumSpawnableTerrorist || m_iNumSpawnableCT ) )
  5383. {
  5384. m_bFreezePeriod = false; //Make sure we are not on the FreezePeriod.
  5385. m_bCompleteReset = true;
  5386. TerminateRound( 0.5f, Game_Commencing );
  5387. m_bFirstConnected = true;
  5388. return true;
  5389. }
  5390. return false;
  5391. }
  5392. // We needed players to start scoring
  5393. // Do we have them now?
  5394. if( !m_iNumSpawnableTerrorist && !m_iNumSpawnableCT )
  5395. {
  5396. Msg( "Game will not start until both teams have players.\n" );
  5397. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#SFUI_Notice_Game_scoring" );
  5398. bNeededPlayers = true;
  5399. m_bFirstConnected = false;
  5400. }
  5401. if ( !m_bFirstConnected &&
  5402. ( m_iNumSpawnableTerrorist || m_iNumSpawnableCT ) )
  5403. {
  5404. // Start the round immediately when the first person joins
  5405. // UTIL_LogPrintf( "World triggered \"Game_Commencing\"\n" );
  5406. m_bFreezePeriod = false; //Make sure we are not on the FreezePeriod.
  5407. m_bCompleteReset = true;
  5408. TerminateRound( 0.5f, Game_Commencing );
  5409. m_bFirstConnected = true;
  5410. return true;
  5411. }
  5412. return false;
  5413. }
  5414. void CCSGameRules::InitializePlayerCounts(
  5415. int &NumAliveTerrorist,
  5416. int &NumAliveCT,
  5417. int &NumDeadTerrorist,
  5418. int &NumDeadCT
  5419. )
  5420. {
  5421. NumAliveTerrorist = NumAliveCT = NumDeadCT = NumDeadTerrorist = 0;
  5422. m_iNumTerrorist = m_iNumCT = m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0;
  5423. m_iHaveEscaped = 0;
  5424. // Set team filters
  5425. m_filterCT.Reset();
  5426. m_filterCT.MakeReliable();
  5427. m_filterCT.AddRecipientsByTeam( GetGlobalTeam(TEAM_CT) );
  5428. m_filterTerrorist.Reset();
  5429. m_filterTerrorist.MakeReliable();
  5430. m_filterTerrorist.AddRecipientsByTeam( GetGlobalTeam(TEAM_TERRORIST) );
  5431. // Count how many dead players there are on each team.
  5432. for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ )
  5433. {
  5434. CTeam *pTeam = GetGlobalTeam( iTeam );
  5435. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  5436. {
  5437. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  5438. Assert( pPlayer );
  5439. if ( !pPlayer )
  5440. continue;
  5441. Assert( pPlayer->GetTeamNumber() == pTeam->GetTeamNumber() );
  5442. switch ( pTeam->GetTeamNumber() )
  5443. {
  5444. case TEAM_CT:
  5445. m_iNumCT++;
  5446. if ( pPlayer->State_Get() != STATE_PICKINGCLASS )
  5447. m_iNumSpawnableCT++;
  5448. if ( pPlayer->m_lifeState != LIFE_ALIVE )
  5449. NumDeadCT++;
  5450. else
  5451. NumAliveCT++;
  5452. break;
  5453. case TEAM_TERRORIST:
  5454. m_iNumTerrorist++;
  5455. if ( pPlayer->State_Get() != STATE_PICKINGCLASS )
  5456. m_iNumSpawnableTerrorist++;
  5457. if ( pPlayer->m_lifeState != LIFE_ALIVE )
  5458. NumDeadTerrorist++;
  5459. else
  5460. NumAliveTerrorist++;
  5461. // Check to see if this guy escaped.
  5462. if ( pPlayer->m_bEscaped == true )
  5463. m_iHaveEscaped++;
  5464. break;
  5465. }
  5466. }
  5467. }
  5468. }
  5469. void CCSGameRules::AddHostageRescueTime( void )
  5470. {
  5471. if ( m_bAnyHostageReached )
  5472. return;
  5473. m_bAnyHostageReached = true;
  5474. // If the round is already over don't add additional time
  5475. bool roundIsAlreadyOver = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
  5476. if ( roundIsAlreadyOver )
  5477. return;
  5478. m_iRoundTime += (int)( mp_hostages_rescuetime.GetFloat() * 60 );
  5479. UTIL_ClientPrintAll( HUD_PRINTTALK, "#hostagerescuetime" );
  5480. }
  5481. bool CCSGameRules::HostageRescueRoundEndCheck( bool bNeededPlayers )
  5482. {
  5483. // Check to see if 50% of the hostages have been rescued.
  5484. CHostage* hostage = NULL;
  5485. int iNumHostages = g_Hostages.Count();
  5486. int iNumLeftToRescue = 0;
  5487. int i;
  5488. for ( i=0; i<iNumHostages; i++ )
  5489. {
  5490. hostage = g_Hostages[i];
  5491. if ( hostage->m_iHealth > 0 && !hostage->IsRescued() ) // We've found a live hostage. don't end the round
  5492. iNumLeftToRescue++;
  5493. }
  5494. // the number of hostages that can be left un rescued, but still win
  5495. int iNumRescuedToWin = mp_hostages_rescuetowin.GetInt() == 0 ? iNumHostages : MIN( iNumHostages, mp_hostages_rescuetowin.GetInt() );
  5496. int iNumLeftCanWin = MAX( 0, iNumHostages - iNumRescuedToWin );
  5497. m_iHostagesRemaining = iNumLeftToRescue;
  5498. if ( (iNumLeftToRescue >= iNumLeftCanWin) && (iNumHostages > 0) )
  5499. {
  5500. if ( m_iHostagesRescued >= iNumRescuedToWin )
  5501. {
  5502. if ( !bNeededPlayers )
  5503. {
  5504. m_match.AddCTWins( 1 );
  5505. }
  5506. AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_HOSTAGE_RESCUE );
  5507. CCS_GameStats.Event_AllHostagesRescued();
  5508. // tell the bots all the hostages have been rescued
  5509. IGameEvent * event = gameeventmanager->CreateEvent( "hostage_rescued_all" );
  5510. if ( event )
  5511. {
  5512. gameeventmanager->FireEvent( event );
  5513. }
  5514. CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST );
  5515. if ( IsPlayingCoopMission() && pTeam )
  5516. pTeam->MarkSurrendered();
  5517. TerminateRound( mp_round_restart_delay.GetFloat(), All_Hostages_Rescued );
  5518. return true;
  5519. }
  5520. }
  5521. return false;
  5522. }
  5523. bool CCSGameRules::PrisonRoundEndCheck()
  5524. {
  5525. //MIKETODO: get this working when working on prison escape
  5526. /*
  5527. if (m_bMapHasEscapeZone == true)
  5528. {
  5529. float flEscapeRatio;
  5530. flEscapeRatio = (float) m_iHaveEscaped / (float) m_iNumEscapers;
  5531. if (flEscapeRatio >= m_flRequiredEscapeRatio)
  5532. {
  5533. BroadcastSound( "Event.TERWin" );
  5534. m_iAccountTerrorist += 3150;
  5535. if ( !bNeededPlayers )
  5536. {
  5537. m_iNumTerroristWins ++;
  5538. // Update the clients team score
  5539. UpdateTeamScores();
  5540. }
  5541. EndRoundMessage( "#Terrorists_Escaped", Terrorists_Escaped );
  5542. TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_TER );
  5543. return;
  5544. }
  5545. else if ( NumAliveTerrorist == 0 && flEscapeRatio < m_flRequiredEscapeRatio)
  5546. {
  5547. BroadcastSound( "Event.CTWin" );
  5548. m_iAccountCT += (1 - flEscapeRatio) * 3500; // CTs are rewarded based on how many terrorists have escaped...
  5549. if ( !bNeededPlayers )
  5550. {
  5551. m_iNumCTWins++;
  5552. // Update the clients team score
  5553. UpdateTeamScores();
  5554. }
  5555. EndRoundMessage( "#CTs_PreventEscape", CTs_PreventEscape );
  5556. TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT );
  5557. return;
  5558. }
  5559. else if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && m_iNumSpawnableCT > 0 )
  5560. {
  5561. BroadcastSound( "Event.CTWin" );
  5562. m_iAccountCT += (1 - flEscapeRatio) * 3250; // CTs are rewarded based on how many terrorists have escaped...
  5563. if ( !bNeededPlayers )
  5564. {
  5565. m_iNumCTWins++;
  5566. // Update the clients team score
  5567. UpdateTeamScores();
  5568. }
  5569. EndRoundMessage( "#Escaping_Terrorists_Neutralized", Escaping_Terrorists_Neutralized );
  5570. TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT );
  5571. return;
  5572. }
  5573. // else return;
  5574. }
  5575. */
  5576. return false;
  5577. }
  5578. bool CCSGameRules::BombPlantedRoundEndCheck( void )
  5579. {
  5580. if ( !IsPlayingCoopGuardian() )
  5581. return false;
  5582. bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE );
  5583. if ( roundIsAlreadyOver )
  5584. return false;
  5585. if ( m_bBombPlanted )
  5586. {
  5587. m_match.AddTerroristWins( 1 );
  5588. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Planted );
  5589. return true;
  5590. }
  5591. return false;
  5592. }
  5593. bool CCSGameRules::CTsReachedHostageRoundEndCheck( void )
  5594. {
  5595. if ( !IsPlayingCoopGuardian() )
  5596. return false;
  5597. bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE );
  5598. if ( roundIsAlreadyOver )
  5599. return false;
  5600. if ( m_bHasHostageBeenTouched )
  5601. {
  5602. m_match.AddCTWins( 1 );
  5603. TerminateRound( mp_round_restart_delay.GetFloat(), CTs_ReachedHostage );
  5604. return true;
  5605. }
  5606. return false;
  5607. }
  5608. bool CCSGameRules::GuardianAllKillsAchievedCheck( void )
  5609. {
  5610. if ( !IsPlayingCoopGuardian() )
  5611. return false;
  5612. bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE );
  5613. if ( roundIsAlreadyOver )
  5614. return false;
  5615. if ( m_nGuardianModeSpecialKillsRemaining <= 0 )
  5616. {
  5617. if ( IsHostageRescueMap() )
  5618. {
  5619. m_match.AddTerroristWins( 1 );
  5620. if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) )
  5621. pTeam->MarkSurrendered();
  5622. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  5623. }
  5624. else
  5625. {
  5626. m_match.AddCTWins( 1 );
  5627. if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) )
  5628. pTeam->MarkSurrendered();
  5629. TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win );
  5630. }
  5631. return true;
  5632. }
  5633. return false;
  5634. }
  5635. void CCSGameRules::IncrementGunGameTerroristWeapons( void )
  5636. {
  5637. if ( IsPlayingGunGameTRBomb() )
  5638. {
  5639. for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ )
  5640. {
  5641. CTeam *pTeam = GetGlobalTeam( iTeam );
  5642. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  5643. {
  5644. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  5645. Assert( pPlayer );
  5646. if ( !pPlayer )
  5647. continue;
  5648. if ( pTeam->GetTeamNumber() == TEAM_TERRORIST )
  5649. {
  5650. // Increment the player's gun game progressive weapon
  5651. pPlayer->IncrementGunGameProgressiveWeapon( mp_ggtr_bomb_detonation_bonus.GetInt() );
  5652. }
  5653. }
  5654. }
  5655. }
  5656. }
  5657. void CCSGameRules::IncrementGunGameCTWeapons( void )
  5658. {
  5659. if ( IsPlayingGunGameTRBomb() )
  5660. {
  5661. for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ )
  5662. {
  5663. CTeam *pTeam = GetGlobalTeam( iTeam );
  5664. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  5665. {
  5666. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  5667. Assert( pPlayer );
  5668. if ( !pPlayer )
  5669. continue;
  5670. if ( pTeam->GetTeamNumber() == TEAM_CT )
  5671. {
  5672. // Increment the player's gun game progressive weapon
  5673. pPlayer->IncrementGunGameProgressiveWeapon( mp_ggtr_bomb_defuse_bonus.GetInt() );
  5674. }
  5675. }
  5676. }
  5677. }
  5678. }
  5679. bool CCSGameRules::IsBotOnlyTeam( int nTeamNumber )
  5680. {
  5681. CTeam *pTeam = GetGlobalTeam( nTeamNumber );
  5682. if ( pTeam )
  5683. {
  5684. // Loop through all players on team and determine if they are all bots
  5685. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  5686. {
  5687. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  5688. if ( !pPlayer )
  5689. {
  5690. continue;
  5691. }
  5692. if ( !pPlayer->IsBot() )
  5693. {
  5694. return false;
  5695. }
  5696. }
  5697. }
  5698. return true;
  5699. }
  5700. bool CCSGameRules::GunGameProgressiveEndCheck( void )
  5701. {
  5702. bool bDidEnd = false;
  5703. if ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED )
  5704. {
  5705. // No need to perform the check if the match has ended
  5706. return false;
  5707. }
  5708. if ( IsPlayingGunGameTRBomb() && !mp_ggtr_last_weapon_kill_ends_half.GetBool() )
  5709. {
  5710. // ignore kills made with the final weapon and let the round end naturally.
  5711. return false;
  5712. }
  5713. if ( !IsPlayingGunGame() )
  5714. return false;
  5715. CCSPlayer *pWinner = NULL;
  5716. // Test if a player made a kill with the final gun game weapon
  5717. for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ )
  5718. {
  5719. CTeam *pTeam = GetGlobalTeam( iTeam );
  5720. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  5721. {
  5722. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  5723. Assert( pPlayer );
  5724. if ( !pPlayer )
  5725. continue;
  5726. if ( pPlayer->MadeFinalGunGameProgressiveKill() )
  5727. {
  5728. pWinner = pPlayer;
  5729. float delayTime;
  5730. if ( IsPlayingGunGameProgressive() || IsPlayingGunGameTRBomb() )
  5731. {
  5732. delayTime = mp_ggprogressive_round_restart_delay.GetFloat();
  5733. }
  5734. else
  5735. {
  5736. delayTime = mp_round_restart_delay.GetFloat();
  5737. }
  5738. // Award bonus points because the kill was made with the final progression weapon
  5739. if ( IsPlayingGunGameTRBomb() )
  5740. {
  5741. if ( pTeam->GetTeamNumber() == TEAM_CT )
  5742. {
  5743. m_match.AddCTWins( 1 );
  5744. m_match.AddCTBonusPoints( mp_ggtr_end_round_kill_bonus.GetInt() );
  5745. }
  5746. else if ( pTeam->GetTeamNumber() == TEAM_TERRORIST )
  5747. {
  5748. m_match.AddTerroristWins( 1 );
  5749. m_match.AddTerroristBonusPoints( mp_ggtr_end_round_kill_bonus.GetInt() );
  5750. }
  5751. }
  5752. if ( IsPlayingGunGameProgressive() )
  5753. {
  5754. m_bCompleteReset = true;
  5755. bDidEnd = true;
  5756. m_match.SetPhase( GAMEPHASE_MATCH_ENDED );
  5757. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  5758. GoToIntermission();
  5759. }
  5760. TerminateRound( delayTime, ( pTeam->GetTeamNumber() == TEAM_CT ) ? CTs_Win : Terrorists_Win );
  5761. break;
  5762. }
  5763. }
  5764. if ( bDidEnd )
  5765. break;
  5766. }
  5767. if ( bDidEnd && IsPlayingGunGameProgressive() )
  5768. {
  5769. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  5770. {
  5771. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  5772. if ( pPlayer )
  5773. {
  5774. pPlayer->AddFlag( FL_FROZEN );
  5775. pPlayer->Unblind();
  5776. }
  5777. }
  5778. }
  5779. return bDidEnd;
  5780. }
  5781. bool CCSGameRules::BombRoundEndCheck( bool bNeededPlayers )
  5782. {
  5783. // Check to see if the bomb target was hit or the bomb defused.. if so, then let's end the round!
  5784. if ( ( m_bTargetBombed == true ) && ( m_bMapHasBombTarget == true ) )
  5785. {
  5786. if ( !bNeededPlayers )
  5787. {
  5788. m_match.AddTerroristWins( 1 );
  5789. }
  5790. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::TERRORIST_WIN_BOMB );
  5791. TerminateRound( mp_round_restart_delay.GetFloat(), Target_Bombed );
  5792. return true;
  5793. }
  5794. else
  5795. if ( ( m_bBombDefused == true ) && ( m_bMapHasBombTarget == true ) )
  5796. {
  5797. if ( !bNeededPlayers )
  5798. {
  5799. m_match.AddCTWins( 1 );
  5800. }
  5801. AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_DEFUSING_BOMB );
  5802. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::PLANTED_BOMB_BUT_DEFUSED ); // give the T's a little bonus for planting the bomb even though it was defused.
  5803. TerminateRound( mp_round_restart_delay.GetFloat(), Bomb_Defused );
  5804. return true;
  5805. }
  5806. return false;
  5807. }
  5808. // [dkorus] note, this is the standard "end of round mvp" for the case where a more specific MVP condition has not been met
  5809. // examples of more specific conditions: Planting the bomb, defusing the bomb, rescuing the hostages, escaping as the VIP, etc
  5810. CCSPlayer * CCSGameRules::CalculateEndOfRoundMVP()
  5811. {
  5812. CCSPlayer* pMVP = NULL;
  5813. int maxKills = 0;
  5814. int maxDamage = 0;
  5815. CSMvpReason_t mvpReason = CSMVP_ELIMINATION;
  5816. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  5817. {
  5818. // Handle winner of gun game progressive
  5819. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  5820. {
  5821. CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  5822. if ( pPlayer )
  5823. {
  5824. if ( pPlayer->MadeFinalGunGameProgressiveKill() )
  5825. {
  5826. pMVP = pPlayer;
  5827. mvpReason = CSMVP_GUNGAMEWINNER;
  5828. break;
  5829. }
  5830. }
  5831. }
  5832. }
  5833. else
  5834. {
  5835. int nBestScore = 0;
  5836. // Handle non-gun game progressive mvp
  5837. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  5838. {
  5839. CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  5840. if ( pPlayer )
  5841. {
  5842. if ( pPlayer->HasBeenControlledThisRound() )
  5843. continue;
  5844. if ( CSGameRules()->IsPlayingGunGameDeathmatch() )
  5845. {
  5846. int nScore = pPlayer->GetScore();
  5847. int nEntindex = pPlayer->entindex();
  5848. if ( nScore > nBestScore || (nScore == nBestScore && nEntindex < maxKills) )
  5849. {
  5850. pMVP = pPlayer;
  5851. maxKills = nEntindex;
  5852. nBestScore = nScore;
  5853. mvpReason = CSMVP_GUNGAMEWINNER;
  5854. }
  5855. }
  5856. else
  5857. {
  5858. // only consider players on the winning team
  5859. if ( CSGameRules()->m_iRoundWinStatus != WINNER_DRAW && pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus )
  5860. continue;
  5861. int nKills = pPlayer->GetNumRoundKills(); // - pPlayer->m_iNumRoundTKs; - for most eliminations count only enemies killed
  5862. int nDamage = pPlayer->GetTotalActualHealthRemovedFromEnemies();
  5863. if ( nKills > maxKills || ( nKills == maxKills && nDamage > maxDamage ) )
  5864. {
  5865. pMVP = pPlayer;
  5866. maxKills = nKills;
  5867. maxDamage = nDamage;
  5868. }
  5869. }
  5870. }
  5871. }
  5872. }
  5873. if ( pMVP )
  5874. {
  5875. pMVP->IncrementNumMVPs( mvpReason );
  5876. }
  5877. return pMVP;
  5878. }
  5879. bool CCSGameRules::TeamExterminationCheck(
  5880. int NumAliveTerrorist,
  5881. int NumAliveCT,
  5882. int NumDeadTerrorist,
  5883. int NumDeadCT,
  5884. bool bNeededPlayers
  5885. )
  5886. {
  5887. bool bCTsRespawn = mp_respawn_on_death_ct.GetBool();
  5888. bool bTsRespawn = mp_respawn_on_death_t.GetBool();
  5889. if ( ( m_iNumCT > 0 && m_iNumSpawnableCT > 0 ) && ( m_iNumTerrorist > 0 && m_iNumSpawnableTerrorist > 0 ) )
  5890. {
  5891. // this checks for last man standing rules
  5892. if ( mp_teammates_are_enemies.GetBool() )
  5893. {
  5894. // last CT alive
  5895. if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && !bTsRespawn && NumAliveCT == 1 )
  5896. {
  5897. m_match.AddCTWins( 1 );
  5898. TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win );
  5899. return true;
  5900. }
  5901. if ( NumAliveCT == 0 && NumDeadCT != 0 && !bCTsRespawn && NumAliveTerrorist == 1 )
  5902. {
  5903. m_match.AddTerroristWins( 1 );
  5904. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  5905. return true;
  5906. }
  5907. if ( NumAliveCT == 0 && !bCTsRespawn && NumAliveTerrorist == 0 && !bTsRespawn && ( m_iNumTerrorist > 0 || m_iNumCT > 0 ) )
  5908. {
  5909. TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw );
  5910. return true;
  5911. }
  5912. }
  5913. else
  5914. {
  5915. // CTs WON (if they don't respawn)
  5916. if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && !bTsRespawn && m_iNumSpawnableCT > 0 )
  5917. {
  5918. bool nowin = false;
  5919. for ( int iGrenade=0; iGrenade < g_PlantedC4s.Count(); iGrenade++ )
  5920. {
  5921. CPlantedC4 *pC4 = g_PlantedC4s[iGrenade];
  5922. if ( pC4->IsBombActive() )
  5923. nowin = true;
  5924. }
  5925. if ( !nowin )
  5926. {
  5927. if ( !bNeededPlayers )
  5928. {
  5929. m_match.AddCTWins( 1 );
  5930. }
  5931. if ( m_bMapHasBombTarget )
  5932. AddTeamAccount( TEAM_CT, TeamCashAward::ELIMINATION_BOMB_MAP );
  5933. else
  5934. AddTeamAccount( TEAM_CT, TeamCashAward::ELIMINATION_HOSTAGE_MAP_CT );
  5935. TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win );
  5936. return true;
  5937. }
  5938. }
  5939. else if ( mp_use_respawn_waves.GetInt() == 2 && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) )
  5940. {
  5941. int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  5942. int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST;
  5943. if ( IsPlayingCoopMission() )
  5944. {
  5945. nTeam = TEAM_CT;
  5946. nOtherTeam = TEAM_TERRORIST;
  5947. }
  5948. if ( (IsHostageRescueMap() && NumAliveCT == 0 && NumDeadCT != 0 && bCTsRespawn) || ((IsBombDefuseMap() || IsPlayingCoopMission()) && NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && bTsRespawn) )
  5949. {
  5950. // HACK
  5951. //play a sound here for all players on other team
  5952. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  5953. {
  5954. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) );
  5955. if ( pPlayer && pPlayer->GetTeamNumber() == nTeam )
  5956. {
  5957. CSingleUserRecipientFilter filter( pPlayer );
  5958. pPlayer->EmitSound( filter, pPlayer->entindex(), "UI.ArmsRace.BecomeMatchLeader" );
  5959. ConVarRef mp_buy_anywhere( "mp_buy_anywhere" );
  5960. if ( IsPlayingCoopGuardian() && pPlayer->IsAlive() && mp_buy_anywhere.GetBool() )
  5961. {
  5962. // force the player to get more ammo
  5963. pPlayer->GuardianForceFillAmmo();
  5964. }
  5965. }
  5966. }
  5967. if ( IsPlayingCoopGuardian() )
  5968. {
  5969. // if T's are supposed to respawn
  5970. float flNextRespawn = gpGlobals->curtime + GetRespawnWaveMaxLength( nOtherTeam );
  5971. m_flNextRespawnWave.Set( nOtherTeam, flNextRespawn );
  5972. m_flCoopRespawnAndHealTime = gpGlobals->curtime + MIN( 2.0f, GetRespawnWaveMaxLength( nOtherTeam ) );
  5973. // decide if the players can buy now
  5974. int iBuyStatus = sv_buy_status_override.GetInt();
  5975. if ( iBuyStatus > 0 && ((nTeam == TEAM_CT && iBuyStatus != 1) || (nTeam == TEAM_TERRORIST && iBuyStatus != 2)) )
  5976. {
  5977. m_flGuardianBuyUntilTime = -1;
  5978. }
  5979. else
  5980. {
  5981. m_flGuardianBuyUntilTime = flNextRespawn + 7.0f;
  5982. }
  5983. char szSec[32];
  5984. Q_snprintf(szSec, sizeof(szSec), "%d", (int)GetRespawnWaveMaxLength( nOtherTeam ));
  5985. CBroadcastRecipientFilter filter;
  5986. UTIL_ClientPrintFilter(filter, HUD_PRINTCENTER, "#SFUI_Notice_NextWaveIn", szSec);
  5987. // give CTs a bonus for surving the round
  5988. AddTeamAccount( nTeam, TeamCashAward::SURVIVE_GUARDIAN_WAVE );
  5989. }
  5990. // COOP MISSION
  5991. if ( IsPlayingCoopMission() )
  5992. {
  5993. // output to the game entity here that says, we completed the room
  5994. // and open the door to the next room
  5995. CGameCoopMissionManager *pManager = GetCoopMissionManager();
  5996. if ( !pManager )
  5997. {
  5998. DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't keep track of completed waves without it!" );
  5999. }
  6000. else
  6001. {
  6002. pManager->SetWaveCompleted();
  6003. m_flCoopRespawnAndHealTime = gpGlobals->curtime + 3.0f;
  6004. }
  6005. }
  6006. // give Ts something so they aren't always using pistols
  6007. //AddTeamAccount( nOtherTeam, TeamCashAward::CUSTOM_AWARD, 1800, "" );
  6008. // Super-specific hack for coop. After CTs clear a round, the easiest bot on that team
  6009. // gets one level harder.
  6010. CTeam *pTeam = GetGlobalTeam( nOtherTeam );
  6011. if ( pTeam && sv_bots_get_harder_after_each_wave.GetInt() > 0 )
  6012. {
  6013. // increase difficulty of 2 bots
  6014. for ( int i=0; i < sv_bots_get_harder_after_each_wave.GetInt(); i++ )
  6015. {
  6016. CUtlVector < CCSBot* > vecBotsOnTeam;
  6017. if ( pTeam->GetBotMembers( &vecBotsOnTeam ) )
  6018. {
  6019. CCSBot *pLeadBot = NULL;
  6020. BotDifficultyType botHighDifficulty = BOT_EXPERT;
  6021. FOR_EACH_VEC( vecBotsOnTeam, i )
  6022. {
  6023. const BotProfile *pProfile = vecBotsOnTeam[i]->GetProfile();
  6024. if ( pProfile->GetMaxDifficulty() < botHighDifficulty )
  6025. {
  6026. pLeadBot = vecBotsOnTeam[i];
  6027. botHighDifficulty = ( BotDifficultyType )pProfile->GetMaxDifficulty();
  6028. }
  6029. }
  6030. if ( pLeadBot )
  6031. {
  6032. const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( MIN( ( BotDifficultyType )( botHighDifficulty + 1 ), BOT_EXPERT ), pLeadBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true );
  6033. if ( pNewProfileData )
  6034. {
  6035. pLeadBot->Initialize( pNewProfileData, pLeadBot->GetTeamNumber() );
  6036. }
  6037. }
  6038. }
  6039. }
  6040. // now just randomize all of the profiles
  6041. CUtlVector < CCSBot* > vecBotsOnTeam;
  6042. if ( pTeam->GetBotMembers( &vecBotsOnTeam ) )
  6043. {
  6044. FOR_EACH_VEC( vecBotsOnTeam, i )
  6045. {
  6046. const BotProfile *pProfile = vecBotsOnTeam[i]->GetProfile();
  6047. const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )( pProfile->GetMaxDifficulty() ), vecBotsOnTeam[i]->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true );
  6048. if ( pNewProfileData )
  6049. {
  6050. vecBotsOnTeam[i]->Initialize( pNewProfileData, vecBotsOnTeam[i]->GetTeamNumber() );
  6051. }
  6052. }
  6053. }
  6054. }
  6055. // // let players buy
  6056. // int iBuyStatus = -1;
  6057. // if ( sv_buy_status_override.GetInt() >= 0 )
  6058. // {
  6059. // iBuyStatus = sv_buy_status_override.GetInt();
  6060. // }
  6061. // else if ( g_pMapInfo )
  6062. // {
  6063. // // Check to see if there's a mapping info parameter entity
  6064. // iBuyStatus = g_pMapInfo->m_iBuyingStatus;
  6065. // }
  6066. //
  6067. // if ( iBuyStatus >= 0 )
  6068. // {
  6069. // switch ( iBuyStatus )
  6070. // {
  6071. // case 1:
  6072. // m_bCTCantBuy = false;
  6073. // m_bTCantBuy = true;
  6074. // Msg( "Only CT's can buy!!\n" );
  6075. // break;
  6076. //
  6077. // case 2:
  6078. // m_bCTCantBuy = true;
  6079. // m_bTCantBuy = false;
  6080. // Msg( "Only T's can buy!!\n" );
  6081. // break;
  6082. //
  6083. // case 3:
  6084. // m_bCTCantBuy = true;
  6085. // m_bTCantBuy = true;
  6086. // Msg( "No one can buy!!\n" );
  6087. // break;
  6088. //
  6089. // default:
  6090. // m_bCTCantBuy = false;
  6091. // m_bTCantBuy = false;
  6092. // break;
  6093. // }
  6094. // }
  6095. //
  6096. // // if ( nTeam == TEAM_CT )
  6097. // // m_bCTCantBuy = false;
  6098. // // else
  6099. // // m_bTCantBuy = false;
  6100. }
  6101. }
  6102. // Terrorists WON (if they don't respawn)
  6103. if ( NumAliveCT == 0 && NumDeadCT != 0 && !bCTsRespawn && m_iNumSpawnableTerrorist > 0 )
  6104. {
  6105. if ( !bNeededPlayers )
  6106. {
  6107. m_match.AddTerroristWins( 1 );
  6108. }
  6109. if ( m_bMapHasBombTarget )
  6110. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::ELIMINATION_BOMB_MAP );
  6111. else
  6112. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::ELIMINATION_HOSTAGE_MAP_T );
  6113. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  6114. return true;
  6115. }
  6116. else if ( mp_use_respawn_waves.GetInt() == 2 && NumAliveCT == 0 && NumDeadCT != 0 && bCTsRespawn )
  6117. {
  6118. // if T's are supposed to respawn
  6119. m_flNextRespawnWave.Set( TEAM_CT, gpGlobals->curtime + GetRespawnWaveMaxLength( TEAM_CT ) );
  6120. }
  6121. }
  6122. }
  6123. else if ( NumAliveCT == 0 && !bCTsRespawn && NumAliveTerrorist == 0 && !bTsRespawn && ( m_iNumTerrorist > 0 || m_iNumCT > 0 ) )
  6124. {
  6125. TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw );
  6126. return true;
  6127. }
  6128. return false;
  6129. }
  6130. void CCSGameRules::ReadMultiplayCvars()
  6131. {
  6132. float flRoundTime = 0;
  6133. if ( IsPlayingClassic() && IsHostageRescueMap() && ( mp_roundtime_hostage.GetFloat() > 0 ) )
  6134. {
  6135. flRoundTime = mp_roundtime_hostage.GetFloat();
  6136. }
  6137. else if ( IsPlayingClassic() && IsBombDefuseMap() && ( mp_roundtime_defuse.GetFloat() > 0 ) )
  6138. {
  6139. flRoundTime = mp_roundtime_defuse.GetFloat();
  6140. }
  6141. else if ( IsPlayingCoopMission() )
  6142. {
  6143. flRoundTime = mp_roundtime_deployment.GetFloat();
  6144. }
  6145. else
  6146. {
  6147. flRoundTime = mp_roundtime.GetFloat();
  6148. }
  6149. m_iRoundTime = IsWarmupPeriod() ? 999 : (int)( flRoundTime * 60 );
  6150. m_iFreezeTime = IsWarmupPeriod() ? 2 : mp_freezetime.GetInt();
  6151. }
  6152. static int BombSortPredicate(CCSPlayer * const *left, CCSPlayer * const *right)
  6153. {
  6154. // should we prioritize humans over bots?
  6155. if (cv_bot_defer_to_human_items.GetBool() )
  6156. {
  6157. if ( (*left)->IsBot() && !(*right)->IsBot() )
  6158. return 1;
  6159. if ( !(*left)->IsBot() && (*right)->IsBot() )
  6160. return -1;
  6161. }
  6162. if ( (*left)->m_fLastGivenBombTime < (*right)->m_fLastGivenBombTime )
  6163. return -1;
  6164. if ( (*left)->m_fLastGivenBombTime > (*right)->m_fLastGivenBombTime )
  6165. return +1;
  6166. return 0;
  6167. }
  6168. static int SpawnPointSortFunction( SpawnPoint* const *left, SpawnPoint* const *right )
  6169. {
  6170. // Sort 2 spawn points against each other using their priority values
  6171. return ( *left )->m_iPriority - ( *right )->m_iPriority;
  6172. }
  6173. static int CoopEnemySpawnSortFunction( SpawnPoint* const *left, SpawnPoint* const *right )
  6174. {
  6175. if ( !CSGameRules() )
  6176. return 0;
  6177. // Sort the spawn point lists
  6178. // just use the first overlapping nav area as a reasonable approximation
  6179. ShortestPathCost cost = ShortestPathCost();
  6180. float dist_left = NavAreaTravelDistance( TheNavMesh->GetNearestNavArea( ( *left )->GetAbsOrigin() ),
  6181. TheNavMesh->GetNearestNavArea( CSGameRules()->m_vecMainCTSpawnPos ),
  6182. cost );
  6183. float dist_right = NavAreaTravelDistance( TheNavMesh->GetNearestNavArea( ( *right )->GetAbsOrigin() ),
  6184. TheNavMesh->GetNearestNavArea( CSGameRules()->m_vecMainCTSpawnPos ),
  6185. cost );
  6186. return dist_left - dist_right;
  6187. }
  6188. static int ArmsRaceSpawnPointSortFunction( SpawnPoint* const *left, SpawnPoint* const *right )
  6189. {
  6190. if ( ( *left )->m_nType != SpawnPoint::ArmsRace && ( *right )->m_nType == SpawnPoint::ArmsRace )
  6191. return 1;
  6192. if ( ( *left )->m_nType == SpawnPoint::ArmsRace && ( *right )->m_nType != SpawnPoint::ArmsRace )
  6193. return -1;
  6194. return 0;
  6195. }
  6196. void CCSGameRules::ResetMasterSpawnPointsForCoop( void )
  6197. {
  6198. RefreshCurrentSpawnPointLists();
  6199. }
  6200. // Perform round-related processing at the point when there is less than 1 second of "free play" to go before the round officially ends
  6201. // At this point the round winner has been determined and displayed to the players
  6202. void CCSGameRules::PreRestartRound( void )
  6203. {
  6204. IGameEvent *restartEvent = gameeventmanager->CreateEvent( "cs_pre_restart" );
  6205. gameeventmanager->FireEvent( restartEvent );
  6206. m_bHasTriggeredRoundStartMusic = true;
  6207. if ( IsPlayingCoopMission() )
  6208. {
  6209. // make sure all active spawn points are enabled again
  6210. ResetMasterSpawnPointsForCoop();
  6211. }
  6212. else
  6213. {
  6214. // reshuffle spawns and then sort by priority for the next round.
  6215. ShuffleSpawnPointLists();
  6216. SortSpawnPointLists();
  6217. }
  6218. // TEMP
  6219. // for ( int i=0; i < WEAPON_LAST; i++ )
  6220. // {
  6221. // const CCSWeaponInfo *pCSWeaponInfo = GetWeaponInfo( (CSWeaponID)i );
  6222. // if ( pCSWeaponInfo )
  6223. // Msg( "%s is worth %d points.\n", pCSWeaponInfo->szPrintName, GetWeaponScoreForDeathmatch( (CSWeaponID)i ) );
  6224. // }
  6225. }
  6226. // Perform round-related processing at the point where the round winner has been determined, and free-play has begun
  6227. void CCSGameRules::RoundWin( void )
  6228. {
  6229. // Update accounts based on number of hostages remaining..
  6230. int iRescuedHostageBonus = 0;
  6231. for ( int iHostage=0; iHostage < g_Hostages.Count(); iHostage++ )
  6232. {
  6233. CHostage *pHostage = g_Hostages[iHostage];
  6234. if( pHostage->IsRescuable() ) //Alive and not rescued
  6235. {
  6236. iRescuedHostageBonus += TeamCashAwardValue( TeamCashAward::HOSTAGE_ALIVE );
  6237. }
  6238. if ( iRescuedHostageBonus >= 2000 )
  6239. break;
  6240. }
  6241. // Super-specific hack for coop. After a team wins, the hardest bot on that team
  6242. // gets easier.
  6243. if ( m_iRoundWinStatus == TEAM_TERRORIST || m_iRoundWinStatus == TEAM_CT )
  6244. {
  6245. CTeam *pTeam = GetGlobalTeam( m_iRoundWinStatus );
  6246. if ( pTeam && IsPlayingCoopGuardian() )
  6247. {
  6248. for ( int j = 0; j < m_nGuardianModeWaveNumber; j++ )
  6249. {
  6250. for ( int i = 0; i < sv_bots_get_easier_each_win.GetInt(); i++ )
  6251. {
  6252. CUtlVector < CCSBot* > vecBotsOnTeam;
  6253. if ( pTeam->GetBotMembers( &vecBotsOnTeam ) )
  6254. {
  6255. CCSBot *pLeadBot = NULL;
  6256. BotDifficultyType botHighDifficulty = BOT_EASY;
  6257. FOR_EACH_VEC( vecBotsOnTeam, i )
  6258. {
  6259. const BotProfile *pProfile = vecBotsOnTeam[i]->GetProfile();
  6260. if ( pProfile->GetMaxDifficulty() > botHighDifficulty )
  6261. {
  6262. pLeadBot = vecBotsOnTeam[i];
  6263. botHighDifficulty = ( BotDifficultyType )pProfile->GetMaxDifficulty();
  6264. }
  6265. }
  6266. if ( pLeadBot )
  6267. {
  6268. // dont go lower than the map set bot difficulty
  6269. ConVarRef bot_difficulty( "bot_difficulty" );
  6270. const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( MAX( ( BotDifficultyType )( botHighDifficulty - 1 ), ( BotDifficultyType )bot_difficulty.GetInt() ), pLeadBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true );
  6271. if ( pNewProfileData )
  6272. {
  6273. pLeadBot->Initialize( pNewProfileData, pLeadBot->GetTeamNumber() );
  6274. }
  6275. }
  6276. }
  6277. }
  6278. }
  6279. int nRoundsPlayed = GetTotalRoundsPlayed();
  6280. if ( nRoundsPlayed > 3 )
  6281. {
  6282. int nShouldReport = nRoundsPlayed % 2;
  6283. if ( nShouldReport == 0 )
  6284. {
  6285. int nHumanGuardianTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  6286. if ( m_iRoundWinStatus != nHumanGuardianTeam )
  6287. { // Only print the message if the humans didn't complete the mission yet
  6288. CBroadcastRecipientFilter filter;
  6289. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_GuardianModeLowerDifficultyNextRound" );
  6290. }
  6291. ConVarRef sv_bots_get_harder_after_each_wave( "sv_bots_get_harder_after_each_wave" );
  6292. sv_bots_get_harder_after_each_wave.SetValue( MAX( 1, sv_bots_get_harder_after_each_wave.GetInt()-1) );
  6293. ConVarRef bot_difficulty( "bot_difficulty" );
  6294. int nBotDifficulty = bot_difficulty.GetInt();
  6295. if ( nRoundsPlayed > 9 )
  6296. nBotDifficulty = 0;
  6297. else if ( nRoundsPlayed > 5 )
  6298. nBotDifficulty = MIN( nBotDifficulty, 1 );
  6299. else if ( nRoundsPlayed > 3 )
  6300. nBotDifficulty = MIN( nBotDifficulty, 2 );
  6301. bot_difficulty.SetValue( MAX( 0, sv_bots_get_harder_after_each_wave.GetInt()-1) );
  6302. }
  6303. }
  6304. }
  6305. }
  6306. //*******Catch up code by SupraFiend. Scale up the loser bonus when teams fall into losing streaks
  6307. if (m_iRoundWinStatus == WINNER_TER) // terrorists won
  6308. {
  6309. //check to see if they just broke a losing streak
  6310. if ( m_iNumConsecutiveTerroristLoses > 0 )
  6311. {
  6312. // reset the loser bonus
  6313. m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS );
  6314. m_iNumConsecutiveTerroristLoses = 0;
  6315. }
  6316. m_iNumConsecutiveCTLoses++;//increment the number of wins the CTs have had
  6317. }
  6318. else if (m_iRoundWinStatus == WINNER_CT) // CT Won
  6319. {
  6320. //check to see if they just broke a losing streak
  6321. if ( m_iNumConsecutiveCTLoses > 0 )
  6322. {
  6323. // reset the loser bonus
  6324. m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS );
  6325. m_iNumConsecutiveCTLoses = 0;
  6326. }
  6327. m_iNumConsecutiveTerroristLoses++;//increment the number of wins the Terrorists have had
  6328. }
  6329. //check if the losing team is in a losing streak & that the loser bonus hasn't maxed out.
  6330. if((m_iNumConsecutiveTerroristLoses > 1) && (m_iLoserBonus < 3000))
  6331. m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak
  6332. else
  6333. if((m_iNumConsecutiveCTLoses > 1) && (m_iLoserBonus < 3000))
  6334. m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak
  6335. // assign the wining and losing bonuses
  6336. if (m_iRoundWinStatus == WINNER_TER) // terrorists won
  6337. {
  6338. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus );
  6339. AddTeamAccount( TEAM_CT, TeamCashAward::LOSER_BONUS, m_iLoserBonus );
  6340. }
  6341. else if (m_iRoundWinStatus == WINNER_CT) // CT Won
  6342. {
  6343. AddTeamAccount( TEAM_CT, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus);
  6344. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::LOSER_BONUS, m_iLoserBonus );
  6345. }
  6346. //Update CT account based on number of hostages rescued
  6347. AddTeamAccount( TEAM_CT, TeamCashAward::RESCUED_HOSTAGE, m_iHostagesRescued * TeamCashAwardValue( TeamCashAward::RESCUED_HOSTAGE ));
  6348. // store win reason in match stats
  6349. if ( ShouldRecordMatchStats() )
  6350. {
  6351. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  6352. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  6353. bool bCTsRespawn = mp_respawn_on_death_ct.GetBool();
  6354. bool bTsRespawn = mp_respawn_on_death_t.GetBool();
  6355. int iNumRescuedToWin = mp_hostages_rescuetowin.GetInt() == 0 ? g_Hostages.Count() : MIN( g_Hostages.Count(), mp_hostages_rescuetowin.GetInt() );
  6356. int nStatToModify = GetTotalRoundsPlayed() - 1;
  6357. if ( nStatToModify >= 0 )
  6358. {
  6359. nStatToModify = MIN( nStatToModify, m_iMatchStats_RoundResults.Count() - 1 );
  6360. m_iMatchStats_PlayersAlive_T.GetForModify( nStatToModify ) = NumAliveTerrorist;
  6361. DevMsg( "NumAliveTerrorist = %d", NumAliveTerrorist );
  6362. m_iMatchStats_PlayersAlive_CT.GetForModify( nStatToModify ) = NumAliveCT;
  6363. DevMsg( "NumAliveCT = %d", NumAliveCT );
  6364. if (m_iRoundWinStatus == WINNER_TER) // terrorists won
  6365. {
  6366. // T_WIN_BOMB
  6367. if ( m_bTargetBombed == true )
  6368. {
  6369. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::T_WIN_BOMB;
  6370. DevMsg( "Ts won by BOMB\n" );
  6371. }
  6372. // T_WIN_ELIMINATION
  6373. else if ( NumAliveCT == 0 && NumDeadCT != 0 && !bCTsRespawn && m_iNumSpawnableTerrorist > 0 )
  6374. {
  6375. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::T_WIN_ELIMINATION;
  6376. DevMsg( "Ts won by ELIMINATION\n" );
  6377. }
  6378. // T_WIN_TIME
  6379. else if ( GetRoundRemainingTime() <= 0 )
  6380. {
  6381. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::T_WIN_TIME;
  6382. DevMsg( "Ts won by TIME\n" );
  6383. }
  6384. else
  6385. {
  6386. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::UNKNOWN;
  6387. DevMsg( "Ts won, but the reason is UNKNOWN!!!\n" );
  6388. }
  6389. }
  6390. else if (m_iRoundWinStatus == WINNER_CT) // cts won
  6391. {
  6392. // CT_WIN_DEFUSE
  6393. if ( m_bBombDefused == true )
  6394. {
  6395. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_DEFUSE;
  6396. DevMsg( "CTs won by DEFUSE\n" );
  6397. }
  6398. // CT_WIN_ELIMINATION
  6399. else if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && !bTsRespawn && m_iNumSpawnableCT > 0 )
  6400. {
  6401. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_ELIMINATION;
  6402. DevMsg( "CTs won by ELIMINATION\n" );
  6403. }
  6404. // CT_WIN_TIME
  6405. else if ( GetRoundRemainingTime() <= 0 )
  6406. {
  6407. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_TIME;
  6408. DevMsg( "CTs won by TIME\n" );
  6409. }
  6410. // CT_WIN_RESCUE
  6411. else if ( iNumRescuedToWin > 0 && m_iHostagesRescued >= iNumRescuedToWin )
  6412. {
  6413. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_RESCUE;
  6414. DevMsg( "CTs won by RESCUE\n" );
  6415. }
  6416. else
  6417. {
  6418. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::UNKNOWN;
  6419. DevMsg( "CTs won, but the reason is UNKNOWN!!!\n" );
  6420. }
  6421. }
  6422. else
  6423. {
  6424. m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::UNKNOWN;
  6425. }
  6426. }
  6427. }
  6428. }
  6429. // Perform round-related processing at the official end of round
  6430. void CCSGameRules::RoundEnd( void )
  6431. {
  6432. IGameEvent * event = gameeventmanager->CreateEvent( "round_officially_ended" );
  6433. if( event )
  6434. {
  6435. gameeventmanager->FireEvent( event );
  6436. }
  6437. if ( m_bPlayerItemsHaveBeenDisplayed )
  6438. ClearItemsDroppedDuringMatch();
  6439. }
  6440. // Perform round-related processing at the point when the next round is beginning
  6441. void CCSGameRules::RestartRound()
  6442. {
  6443. // save off how many players were alive at the very end of this round
  6444. if ( ShouldRecordMatchStats() )
  6445. {
  6446. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  6447. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  6448. int nStatToModify = GetTotalRoundsPlayed() - 1;
  6449. if ( nStatToModify >= 0 )
  6450. {
  6451. nStatToModify = MIN( nStatToModify, m_iMatchStats_PlayersAlive_T.Count() - 1 );
  6452. m_iMatchStats_PlayersAlive_T.GetForModify( nStatToModify ) = NumAliveTerrorist;
  6453. DevMsg( "NumAliveTerrorist = %d", NumAliveTerrorist );
  6454. m_iMatchStats_PlayersAlive_CT.GetForModify( nStatToModify ) = NumAliveCT;
  6455. DevMsg( "NumAliveCT = %d", NumAliveCT );
  6456. }
  6457. }
  6458. m_iNextCTSpawnPoint = 0;
  6459. m_iNextTerroristSpawnPoint = 0;
  6460. // fire a round_prestart event
  6461. IGameEvent * event = gameeventmanager->CreateEvent( "round_prestart" );
  6462. if( event )
  6463. {
  6464. gameeventmanager->FireEvent( event );
  6465. }
  6466. // FIXME[pmf]: move this up to the pre-restart?
  6467. // [tj] Notify players that the round is about to be reset
  6468. int playerCount = 0;
  6469. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
  6470. {
  6471. CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex );
  6472. if ( pPlayer )
  6473. {
  6474. playerCount++;
  6475. pPlayer->OnPreResetRound();
  6476. }
  6477. }
  6478. GetGlobalTeam( TEAM_CT )->ResetTeamLeaders();
  6479. GetGlobalTeam( TEAM_TERRORIST )->ResetTeamLeaders();
  6480. // If the pre-reset round was causing team changes then make sure we stop silencing them from now on
  6481. m_bForceTeamChangeSilent = false;
  6482. // Also after the round restarts we mark ourselves as no longer loading round backup data
  6483. m_bLoadingRoundBackupData = false;
  6484. // Kick any bots flagged for removal
  6485. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
  6486. {
  6487. CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex );
  6488. if ( pPlayer && pPlayer->IsBot() && ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR || pPlayer->GetPendingTeamNumber() == TEAM_SPECTATOR ) )
  6489. {
  6490. engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", engine->GetPlayerUserId( pPlayer->edict() ) ) );
  6491. }
  6492. }
  6493. // Perform any queued team moves
  6494. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
  6495. {
  6496. CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex );
  6497. if ( pPlayer && ( pPlayer->GetPendingTeamNumber() != pPlayer->GetTeamNumber() ) )
  6498. {
  6499. pPlayer->HandleCommand_JoinTeam( pPlayer->GetPendingTeamNumber() ) ;
  6500. }
  6501. }
  6502. // Team pre-round setup. Do this after players have finished switching teams.
  6503. GetGlobalCSTeam( TEAM_CT )->OnRoundPreStart();
  6504. GetGlobalCSTeam( TEAM_TERRORIST )->OnRoundPreStart();
  6505. if ( m_endMatchOnRoundReset )
  6506. {
  6507. m_endMatchOnRoundReset = false;
  6508. m_endMatchOnThink = true;
  6509. }
  6510. #if defined ( _GAMECONSOLE )
  6511. bool isReallyEndOfRound = false;
  6512. if ((m_iRoundWinStatus == WINNER_TER) || (m_iRoundWinStatus == WINNER_CT))
  6513. isReallyEndOfRound = true;
  6514. if (playerCount > 1 && isReallyEndOfRound)
  6515. {
  6516. IGameEvent * updateMatchStatsEvent = gameeventmanager->CreateEvent( "update_matchmaking_stats" );
  6517. if (updateMatchStatsEvent)
  6518. {
  6519. gameeventmanager->FireEvent( updateMatchStatsEvent);
  6520. }
  6521. IGameEvent * writeProfileEvent = gameeventmanager->CreateEvent( "write_profile_data" );
  6522. if ( writeProfileEvent )
  6523. {
  6524. gameeventmanager->FireEvent( writeProfileEvent );
  6525. }
  6526. }
  6527. #endif
  6528. if ( !IsFinite( gpGlobals->curtime ) )
  6529. {
  6530. Warning( "NaN curtime in RestartRound\n" );
  6531. gpGlobals->curtime = 0.0f;
  6532. }
  6533. // Brock H. - TR - 03/31/09
  6534. // Revert any player controlled bots
  6535. #if CS_CONTROLLABLE_BOTS_ENABLED
  6536. RevertBotsFunctor revertBots;
  6537. ForEachPlayer( revertBots );
  6538. #endif
  6539. m_iTotalRoundsPlayed++;
  6540. //ClearBodyQue();
  6541. // Tabulate the number of players on each team.
  6542. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
  6543. InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
  6544. m_bBombDropped = false;
  6545. m_bBombPlanted = false;
  6546. m_bHasHostageBeenTouched = false;
  6547. if ( GetHumanTeam() != TEAM_UNASSIGNED )
  6548. {
  6549. MoveHumansToHumanTeam();
  6550. }
  6551. #if !defined ( _GAMECONSOLE )
  6552. ProcessAutoBalance();
  6553. #endif
  6554. //If this is the first restart since halftime, do the appropriate bookkeeping.
  6555. bool bClearAccountsAfterHalftime = false;
  6556. if ( IsPlayingGunGameProgressive() )
  6557. {
  6558. ClearGunGameData();
  6559. }
  6560. else if ( m_match.GetPhase() == GAMEPHASE_HALFTIME )
  6561. {
  6562. if ( GetOvertimePlaying() && ( m_match.GetRoundsPlayed() <= ( mp_maxrounds.GetInt() + ( GetOvertimePlaying() - 1 )*mp_overtime_maxrounds.GetInt() ) ) )
  6563. {
  6564. // This is the overtime halftime at the end of a tied regulation time or at the end of a previous overtime that
  6565. // failed to determine the winner, we will not be switching teams at this time and we proceed into first half
  6566. // of next overtime period
  6567. m_match.SetPhase( GAMEPHASE_PLAYING_FIRST_HALF );
  6568. }
  6569. else
  6570. {
  6571. // Regulation halftime or 1st half of overtime finished, swap the CT and T scores so the scoreboard will be correct
  6572. m_match.SwapTeamScores();
  6573. m_match.SetPhase( GAMEPHASE_PLAYING_SECOND_HALF );
  6574. }
  6575. if ( IsPlayingGunGameTRBomb() )
  6576. {
  6577. ClearGunGameData();
  6578. }
  6579. else
  6580. {
  6581. // Ensure everyone is given only the starting money
  6582. bClearAccountsAfterHalftime = true;
  6583. }
  6584. // Remove all items at halftime or before overtime when teams aren't switching sides
  6585. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  6586. {
  6587. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  6588. if ( !pPlayer )
  6589. continue;
  6590. pPlayer->RemoveAllItems( true );
  6591. }
  6592. }
  6593. // Check to see if TR Gun Game match has ended, and player data should be reset
  6594. else if ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED )
  6595. {
  6596. if ( IsPlayingGunGameTRBomb() )
  6597. {
  6598. ClearGunGameData();
  6599. }
  6600. }
  6601. if ( m_bCompleteReset )
  6602. {
  6603. // reset timeouts
  6604. EndTerroristTimeOut();
  6605. EndCTTimeOut();
  6606. m_nTerroristTimeOuts = mp_team_timeout_max.GetInt();
  6607. m_nCTTimeOuts = mp_team_timeout_max.GetInt();
  6608. m_flTerroristTimeOutRemaining = mp_team_timeout_time.GetInt();
  6609. m_flCTTimeOutRemaining = mp_team_timeout_time.GetInt();
  6610. // bounds check
  6611. if ( mp_timelimit.GetInt() < 0 )
  6612. {
  6613. mp_timelimit.SetValue( 0 );
  6614. }
  6615. if ( m_bScrambleTeamsOnRestart )
  6616. {
  6617. HandleScrambleTeams();
  6618. m_bScrambleTeamsOnRestart = false;
  6619. if ( IsPlayingGunGameTRBomb() )
  6620. {
  6621. ClearGunGameData();
  6622. }
  6623. }
  6624. if ( m_bSwapTeamsOnRestart )
  6625. {
  6626. HandleSwapTeams();
  6627. m_bSwapTeamsOnRestart = false;
  6628. }
  6629. m_flGameStartTime = gpGlobals->curtime;
  6630. if ( !IsFinite( m_flGameStartTime.Get() ) )
  6631. {
  6632. Warning( "Trying to set a NaN game start time\n" );
  6633. m_flGameStartTime.GetForModify() = 0.0f;
  6634. }
  6635. m_iTotalRoundsPlayed = 0;
  6636. // Reset score info
  6637. m_match.Reset();
  6638. m_iNumConsecutiveTerroristLoses = 0;
  6639. m_iNumConsecutiveCTLoses = 0;
  6640. m_iAccountTerrorist = m_iAccountCT = 0; //No extra cash!.
  6641. //We are starting fresh. So it's like no one has ever won or lost.
  6642. m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS );
  6643. }
  6644. if ( m_bCompleteReset || // player scores and QMM fully reset when players lost coop mission round!
  6645. IsPlayingCoopMission() )
  6646. {
  6647. // Reset the player stats
  6648. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  6649. {
  6650. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  6651. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  6652. pPlayer->Reset( true );
  6653. }
  6654. // For queued matchmaking mode reset KDA/MVPs as well and initialize money for start
  6655. if ( IsQueuedMatchmaking() )
  6656. {
  6657. FOR_EACH_MAP( m_mapQueuedMatchmakingPlayersData, idxQueuedPlayer )
  6658. {
  6659. m_mapQueuedMatchmakingPlayersData.Element( idxQueuedPlayer )->Reset();
  6660. m_mapQueuedMatchmakingPlayersData.Element( idxQueuedPlayer )->m_cash = GetStartMoney();
  6661. }
  6662. }
  6663. else
  6664. {
  6665. m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements();
  6666. }
  6667. // Save a zeroeth round backup
  6668. if ( Helper_mp_backup_round_IsEnabled() && !IsWarmupPeriod() )
  6669. {
  6670. SaveRoundDataInformation();
  6671. }
  6672. }
  6673. m_bFreezePeriod = true;
  6674. m_bGameRestart = false;
  6675. UTIL_LogPrintf( "Starting Freeze period\n" );
  6676. ReadMultiplayCvars();
  6677. int iBuyStatus = -1;
  6678. if ( sv_buy_status_override.GetInt() >= 0 )
  6679. {
  6680. iBuyStatus = sv_buy_status_override.GetInt();
  6681. }
  6682. else if ( g_pMapInfo )
  6683. {
  6684. // Check to see if there's a mapping info parameter entity
  6685. iBuyStatus = g_pMapInfo->m_iBuyingStatus;
  6686. }
  6687. if ( iBuyStatus >= 0 )
  6688. {
  6689. switch ( iBuyStatus )
  6690. {
  6691. case 0:
  6692. m_bCTCantBuy = false;
  6693. m_bTCantBuy = false;
  6694. Msg( "EVERYONE CAN BUY!\n" );
  6695. break;
  6696. case 1:
  6697. m_bCTCantBuy = false;
  6698. m_bTCantBuy = true;
  6699. Msg( "Only CT's can buy!!\n" );
  6700. break;
  6701. case 2:
  6702. m_bCTCantBuy = true;
  6703. m_bTCantBuy = false;
  6704. Msg( "Only T's can buy!!\n" );
  6705. break;
  6706. case 3:
  6707. m_bCTCantBuy = true;
  6708. m_bTCantBuy = true;
  6709. Msg( "No one can buy!!\n" );
  6710. break;
  6711. default:
  6712. m_bCTCantBuy = false;
  6713. m_bTCantBuy = false;
  6714. break;
  6715. }
  6716. }
  6717. else
  6718. {
  6719. // by default everyone can buy
  6720. m_bCTCantBuy = false;
  6721. m_bTCantBuy = false;
  6722. }
  6723. // Check to see if this map has a bomb target in it
  6724. if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) )
  6725. {
  6726. // this is a bit hacky, but it makes it so the bomb stuff only shows up on mission 3 of the coop mission
  6727. if ( IsPlayingCoopMission() && mp_anyone_can_pickup_c4.GetBool() == false )
  6728. {
  6729. m_bMapHasBombTarget = false;
  6730. m_bMapHasBombZone = false;
  6731. }
  6732. else
  6733. {
  6734. m_bMapHasBombTarget = true;
  6735. m_bMapHasBombZone = true;
  6736. }
  6737. }
  6738. else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) )
  6739. {
  6740. m_bMapHasBombTarget = true;
  6741. m_bMapHasBombZone = false;
  6742. }
  6743. else
  6744. {
  6745. m_bMapHasBombTarget = false;
  6746. m_bMapHasBombZone = false;
  6747. }
  6748. // Check to see if this map has hostage rescue zones
  6749. if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) )
  6750. m_bMapHasRescueZone = true;
  6751. else
  6752. m_bMapHasRescueZone = false;
  6753. // See if the map has func_buyzone entities
  6754. // Used by CBasePlayer::HandleSignals() to support maps without these entities
  6755. if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) )
  6756. m_bMapHasBuyZone = true;
  6757. else
  6758. m_bMapHasBuyZone = false;
  6759. // GOOSEMAN : See if this map has func_escapezone entities
  6760. if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) )
  6761. {
  6762. m_bMapHasEscapeZone = true;
  6763. m_iHaveEscaped = 0;
  6764. m_iNumEscapers = 0; // Will increase this later when we count how many Ts are starting
  6765. if (m_iNumEscapeRounds >= 3)
  6766. {
  6767. SwapAllPlayers();
  6768. m_iNumEscapeRounds = 0;
  6769. }
  6770. m_iNumEscapeRounds++; // Increment the number of rounds played... After 8 rounds, the players will do a whole sale switch..
  6771. }
  6772. else
  6773. m_bMapHasEscapeZone = false;
  6774. /*
  6775. // Update accounts based on number of hostages remaining..
  6776. int iRescuedHostageBonus = 0;
  6777. for ( int iHostage=0; iHostage < g_Hostages.Count(); iHostage++ )
  6778. {
  6779. CHostage *pHostage = g_Hostages[iHostage];
  6780. if( pHostage->IsRescuable() ) //Alive and not rescued
  6781. {
  6782. iRescuedHostageBonus += TeamCashAwardValue( TeamCashAward::HOSTAGE_ALIVE );
  6783. }
  6784. if ( iRescuedHostageBonus >= 2000 )
  6785. break;
  6786. }
  6787. // *******Catch up code by SupraFiend. Scale up the loser bonus when teams fall into losing streaks
  6788. if (m_iRoundWinStatus == WINNER_TER) // terrorists won
  6789. {
  6790. //check to see if they just broke a losing streak
  6791. if ( m_iNumConsecutiveTerroristLoses > 0 )
  6792. {
  6793. // reset the loser bonus
  6794. m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS );
  6795. m_iNumConsecutiveTerroristLoses = 0;
  6796. }
  6797. m_iNumConsecutiveCTLoses++;//increment the number of wins the CTs have had
  6798. }
  6799. else if (m_iRoundWinStatus == WINNER_CT) // CT Won
  6800. {
  6801. //check to see if they just broke a losing streak
  6802. if ( m_iNumConsecutiveCTLoses > 0 )
  6803. {
  6804. // reset the loser bonus
  6805. m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS );
  6806. m_iNumConsecutiveCTLoses = 0;
  6807. }
  6808. m_iNumConsecutiveTerroristLoses++;//increment the number of wins the Terrorists have had
  6809. }
  6810. //check if the losing team is in a losing streak & that the loser bonus hasn't maxed out.
  6811. if((m_iNumConsecutiveTerroristLoses > 1) && (m_iLoserBonus < 3000))
  6812. m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak
  6813. else
  6814. if((m_iNumConsecutiveCTLoses > 1) && (m_iLoserBonus < 3000))
  6815. m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak
  6816. // assign the wining and losing bonuses
  6817. if (m_iRoundWinStatus == WINNER_TER) // terrorists won
  6818. {
  6819. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus );
  6820. AddTeamAccount( TEAM_CT, TeamCashAward::LOSER_BONUS, m_iLoserBonus );
  6821. }
  6822. else if (m_iRoundWinStatus == WINNER_CT) // CT Won
  6823. {
  6824. AddTeamAccount( TEAM_CT, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus);
  6825. if (m_bMapHasEscapeZone == false) // only give them the bonus if this isn't an escape map
  6826. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::LOSER_BONUS, m_iLoserBonus);
  6827. }
  6828. //Update CT account based on number of hostages rescued
  6829. AddTeamAccount( TEAM_CT, TeamCashAward::RESCUED_HOSTAGE, m_iHostagesRescued * TeamCashAwardValue( TeamCashAward::RESCUED_HOSTAGE ));
  6830. */
  6831. // Update individual players accounts and respawn players
  6832. //**********new code by SupraFiend
  6833. //##########code changed by MartinO
  6834. //the round time stamp must be set before players are spawned
  6835. m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime;
  6836. if ( !IsFinite( m_fRoundStartTime.Get() ) )
  6837. {
  6838. Warning( "Trying to set a NaN round start time\n" );
  6839. m_fRoundStartTime.GetForModify() = 0.0f;
  6840. }
  6841. m_bRoundTimeWarningTriggered = false;
  6842. if ( IsPlayingCoopGuardian() )
  6843. {
  6844. if ( CanSpendMoneyInMap() )
  6845. m_flGuardianBuyUntilTime = GetRoundStartTime();
  6846. }
  6847. //Adrian - No cash for anyone at first rounds! ( well, only the default. )
  6848. // Get the cash bonus awarded for completing the round
  6849. int RoundBonus = m_bCompleteReset ? 0 : mp_afterroundmoney.GetInt();
  6850. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  6851. {
  6852. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  6853. if ( !pPlayer )
  6854. continue;
  6855. pPlayer->m_iNumSpawns = 0;
  6856. pPlayer->m_bTeamChanged = false;
  6857. // Award between round auto bonuses
  6858. if ( RoundBonus > 0 )
  6859. {
  6860. pPlayer->AddAccount( RoundBonus, false );
  6861. }
  6862. // tricky, make players non solid while moving to their spawn points
  6863. if ( (pPlayer->GetTeamNumber() == TEAM_CT) || (pPlayer->GetTeamNumber() == TEAM_TERRORIST) )
  6864. {
  6865. pPlayer->AddSolidFlags( FSOLID_NOT_SOLID );
  6866. }
  6867. }
  6868. // Respawn entities (glass, doors, etc..). NOTE this also KILLS any map entities that the players happen to be pointing to.
  6869. // For this reason, there is a UpdateMapEntityPointers() call to each player after the restart_round event is issued and processed.
  6870. CleanUpMap();
  6871. if ( IsPlayingCoopMission() )
  6872. {
  6873. m_coopPlayersInDeploymentZone = true;
  6874. // output to the game entity here that says, we completed the room
  6875. // and open the door to the next room
  6876. CGameCoopMissionManager *pManager = GetCoopMissionManager();
  6877. // we really dont have one
  6878. if ( !pManager )
  6879. {
  6880. DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't keep track of completed waves without it!\n" );
  6881. }
  6882. else
  6883. {
  6884. pManager->SetRoundReset();
  6885. // we do it here after we set a bunch or things for the map,
  6886. // because coop might want a certain map type even if they aren't spawned right now
  6887. }
  6888. }
  6889. // Reduce hostage count to desired number
  6890. int iHostageCount = mp_hostages_max.GetInt();
  6891. if ( ( mp_hostages_max.GetInt() != atoi( mp_hostages_max.GetDefault() ) ) &&
  6892. ( g_pMapInfo ) &&
  6893. ( g_pMapInfo->m_iHostageCount != 0 ) )
  6894. {
  6895. iHostageCount = g_pMapInfo->m_iHostageCount;
  6896. }
  6897. if ( g_Hostages.Count() > iHostageCount )
  6898. {
  6899. CUtlVector< CHostage * > arrCopyOfOriginalHostageIndices;
  6900. arrCopyOfOriginalHostageIndices.AddMultipleToTail( g_Hostages.Count(), g_Hostages.Base() );
  6901. if ( !mp_hostages_spawn_same_every_round.GetBool() )
  6902. m_arrSelectedHostageSpawnIndices.RemoveAll();
  6903. if ( m_arrSelectedHostageSpawnIndices.Count() )
  6904. {
  6905. // We have pre-selected hostage indices, keep only them
  6906. FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage )
  6907. {
  6908. if ( m_arrSelectedHostageSpawnIndices.Find( idxGlobalHostage ) != m_arrSelectedHostageSpawnIndices.InvalidIndex() )
  6909. continue;
  6910. CHostage *pHostage = g_Hostages[ idxGlobalHostage ];
  6911. UTIL_Remove( pHostage );
  6912. g_Hostages.Remove( idxGlobalHostage );
  6913. }
  6914. }
  6915. else if ( mp_hostages_spawn_force_positions.GetString()[0] )
  6916. {
  6917. CUtlVector< int > arrBestHostageIdx;
  6918. CUtlVector< char* > tagStrings;
  6919. V_SplitString( mp_hostages_spawn_force_positions.GetString(), ",", tagStrings );
  6920. arrBestHostageIdx.EnsureCapacity( tagStrings.Count() );
  6921. FOR_EACH_VEC( tagStrings, iTagString )
  6922. {
  6923. arrBestHostageIdx.AddToTail( Q_atoi( tagStrings[iTagString] ) );
  6924. }
  6925. tagStrings.PurgeAndDeleteElements();
  6926. // Now we have selected best hostage indices, keep only them
  6927. FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage )
  6928. {
  6929. if ( arrBestHostageIdx.Find( idxGlobalHostage ) != arrBestHostageIdx.InvalidIndex() )
  6930. continue;
  6931. CHostage *pHostage = g_Hostages[ idxGlobalHostage ];
  6932. UTIL_Remove( pHostage );
  6933. g_Hostages.Remove( idxGlobalHostage );
  6934. }
  6935. }
  6936. else if ( mp_hostages_spawn_farthest.GetBool() )
  6937. {
  6938. CUtlVector< int > arrBestHostageIdx;
  6939. vec_t bestMetric = 0;
  6940. CUtlVector< int > arrTryHostageIdx;
  6941. for ( int iStartIdx = 0; iStartIdx < mp_hostages_max.GetInt(); ++ iStartIdx )
  6942. arrTryHostageIdx.AddToTail( iStartIdx );
  6943. arrBestHostageIdx.AddMultipleToTail( arrTryHostageIdx.Count(), arrTryHostageIdx.Base() );
  6944. while ( 1 )
  6945. {
  6946. vec_t metricThisCombo = 0;
  6947. for ( int iFirstHostage = 0; iFirstHostage < arrTryHostageIdx.Count(); ++ iFirstHostage )
  6948. {
  6949. for ( int iSecondHostage = iFirstHostage + 1; iSecondHostage < arrTryHostageIdx.Count(); ++ iSecondHostage )
  6950. {
  6951. vec_t len2Dsq = ( g_Hostages[ arrTryHostageIdx[iFirstHostage] ]->GetAbsOrigin() - g_Hostages[ arrTryHostageIdx[iSecondHostage] ]->GetAbsOrigin() ).Length2DSqr();
  6952. metricThisCombo += len2Dsq;
  6953. }
  6954. }
  6955. if ( metricThisCombo > bestMetric )
  6956. {
  6957. arrBestHostageIdx.RemoveAll();
  6958. arrBestHostageIdx.AddMultipleToTail( arrTryHostageIdx.Count(), arrTryHostageIdx.Base() );
  6959. bestMetric = metricThisCombo;
  6960. }
  6961. // Advance to next permutation
  6962. int iAdvanceIdx = 0;
  6963. while ( ( iAdvanceIdx < arrTryHostageIdx.Count() ) && ( arrTryHostageIdx[ arrTryHostageIdx.Count() - 1 - iAdvanceIdx ] >= g_Hostages.Count() - 1 - iAdvanceIdx ) )
  6964. iAdvanceIdx ++;
  6965. if ( iAdvanceIdx >= arrTryHostageIdx.Count() )
  6966. break; // Cannot set a valid permutation
  6967. // Increment the index
  6968. arrTryHostageIdx[ arrTryHostageIdx.Count() - 1 - iAdvanceIdx ] ++;
  6969. // Set all the following indices
  6970. for ( int iFollowingIdx = arrTryHostageIdx.Count() - iAdvanceIdx; iFollowingIdx < arrTryHostageIdx.Count(); ++ iFollowingIdx )
  6971. arrTryHostageIdx[iFollowingIdx] = arrTryHostageIdx[ arrTryHostageIdx.Count() - 1 - iAdvanceIdx ] + ( iFollowingIdx - ( arrTryHostageIdx.Count() - iAdvanceIdx ) + 1 );
  6972. }
  6973. // Now we have selected best hostage indices, keep only them
  6974. FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage )
  6975. {
  6976. if ( arrBestHostageIdx.Find( idxGlobalHostage ) != arrBestHostageIdx.InvalidIndex() )
  6977. continue;
  6978. CHostage *pHostage = g_Hostages[ idxGlobalHostage ];
  6979. UTIL_Remove( pHostage );
  6980. g_Hostages.Remove( idxGlobalHostage );
  6981. }
  6982. }
  6983. else
  6984. {
  6985. // Enforce spawn exclusion groups
  6986. CUtlVector< CHostage * > arrSelectedSpawns;
  6987. while ( ( arrSelectedSpawns.Count() < mp_hostages_max.GetInt() ) && g_Hostages.Count() )
  6988. {
  6989. uint32 uiTotalSpawnWeightFactor = 0;
  6990. FOR_EACH_VEC( g_Hostages, idxGlobalHostage )
  6991. {
  6992. if ( CHostage *pCheckHostage = g_Hostages[idxGlobalHostage] )
  6993. uiTotalSpawnWeightFactor += pCheckHostage->GetHostageSpawnRandomFactor();
  6994. }
  6995. if ( !uiTotalSpawnWeightFactor )
  6996. break;
  6997. uint32 iKeepHostage = ( uint32 ) RandomInt( 0, uiTotalSpawnWeightFactor - 1 );
  6998. CHostage *pKeepHostage = NULL;
  6999. FOR_EACH_VEC( g_Hostages, idxGlobalHostage )
  7000. {
  7001. if ( CHostage *pCheckHostage = g_Hostages[idxGlobalHostage] )
  7002. {
  7003. uint32 uiThisFactor = pCheckHostage->GetHostageSpawnRandomFactor();
  7004. if ( iKeepHostage < uiThisFactor )
  7005. {
  7006. pKeepHostage = pCheckHostage;
  7007. g_Hostages.Remove( idxGlobalHostage );
  7008. break;
  7009. }
  7010. else
  7011. {
  7012. iKeepHostage -= uiThisFactor;
  7013. }
  7014. }
  7015. }
  7016. if ( !pKeepHostage )
  7017. break;
  7018. uint32 uiHostageSpawnExclusionGroup = pKeepHostage->GetHostageSpawnExclusionGroup();
  7019. arrSelectedSpawns.AddToTail( pKeepHostage );
  7020. if ( uiHostageSpawnExclusionGroup )
  7021. {
  7022. FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage )
  7023. {
  7024. CHostage *pCheckHostage = g_Hostages[ idxGlobalHostage ];
  7025. if ( ( pCheckHostage != pKeepHostage ) && !!( pCheckHostage->GetHostageSpawnExclusionGroup() & uiHostageSpawnExclusionGroup ) )
  7026. { // They share the same exclusion group
  7027. UTIL_Remove( pCheckHostage );
  7028. g_Hostages.Remove( idxGlobalHostage );
  7029. }
  7030. }
  7031. }
  7032. }
  7033. // Remove all the remaining hostages that we didn't pick
  7034. while ( g_Hostages.Count() )
  7035. {
  7036. CHostage *pHostage = g_Hostages.Tail();
  7037. UTIL_Remove( pHostage );
  7038. g_Hostages.RemoveMultipleFromTail( 1 );
  7039. }
  7040. // Add back the hostages that we decided to keep
  7041. g_Hostages.AddMultipleToTail( arrSelectedSpawns.Count(), arrSelectedSpawns.Base() );
  7042. }
  7043. // Keep removing randomly now until we reach needed number of hostages remaining
  7044. while ( g_Hostages.Count() > mp_hostages_max.GetInt() )
  7045. {
  7046. int randHostage = RandomInt( 0, g_Hostages.Count() - 1 );
  7047. CHostage *pHostage = g_Hostages[ randHostage ];
  7048. UTIL_Remove( pHostage );
  7049. g_Hostages.Remove( randHostage );
  7050. }
  7051. // Remember which spots ended up picked, so that players could disable randomization and keep the spots
  7052. if ( !m_arrSelectedHostageSpawnIndices.Count() )
  7053. {
  7054. FOR_EACH_VEC( g_Hostages, iPickedHostage )
  7055. {
  7056. int idxOriginalSpawnPoint = arrCopyOfOriginalHostageIndices.Find( g_Hostages[iPickedHostage] );
  7057. Assert( idxOriginalSpawnPoint != arrCopyOfOriginalHostageIndices.InvalidIndex() );
  7058. m_arrSelectedHostageSpawnIndices.AddToTail( idxOriginalSpawnPoint );
  7059. }
  7060. }
  7061. // Show information about which hostage positions were selected for the round
  7062. CFmtStr fmtHostagePositions;
  7063. fmtHostagePositions.AppendFormat( "Selected %d hostage positions '", g_Hostages.Count() );
  7064. FOR_EACH_VEC( g_Hostages, iPickedHostage )
  7065. {
  7066. int idxOriginalSpawnPoint = arrCopyOfOriginalHostageIndices.Find( g_Hostages[iPickedHostage] );
  7067. Assert( idxOriginalSpawnPoint != arrCopyOfOriginalHostageIndices.InvalidIndex() );
  7068. fmtHostagePositions.AppendFormat( "%d,", idxOriginalSpawnPoint );
  7069. }
  7070. fmtHostagePositions.Access()[ fmtHostagePositions.Length() - 1 ] = '\'';
  7071. fmtHostagePositions.AppendFormat( "\n" );
  7072. ConMsg( "%s", fmtHostagePositions.Access() );
  7073. }
  7074. // this needs to happen before we cleanup the map or we won't have a mission manager!
  7075. if ( IsPlayingCoopMission() )
  7076. {
  7077. m_nGuardianModeWaveNumber = 1;
  7078. m_nGuardianGrenadesToGiveBots = 0;
  7079. m_coopPlayersInDeploymentZone = true;
  7080. m_coopBonusCoinsFound = 0;
  7081. m_coopBonusPistolsOnly = true;
  7082. }
  7083. if ( IsPlayingCoopGuardian() )
  7084. {
  7085. m_nGuardianModeWaveNumber = 1;
  7086. m_nGuardianGrenadesToGiveBots = 0;
  7087. extern ConVar sv_guardian_heavy_count;
  7088. m_nNumHeaviesToSpawn = sv_guardian_heavy_count.GetInt();
  7089. m_nGuardianModeSpecialKillsRemaining = mp_guardian_special_kills_needed.GetInt();
  7090. char szWepShortName[MAX_PATH];
  7091. V_strcpy_safe( szWepShortName, mp_guardian_special_weapon_needed.GetString() );
  7092. if ( V_strcmp( szWepShortName, "any" ) == 0 )
  7093. {
  7094. m_nGuardianModeSpecialWeaponNeeded = 0;
  7095. }
  7096. else
  7097. {
  7098. char pszWeaponClassname[MAX_PATH];
  7099. V_sprintf_safe( pszWeaponClassname, "weapon_%s", szWepShortName );
  7100. CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pszWeaponClassname );
  7101. if ( pItemDef && pItemDef->GetDefinitionIndex() != 0 )
  7102. {
  7103. m_nGuardianModeSpecialWeaponNeeded = pItemDef->GetDefinitionIndex();
  7104. }
  7105. else
  7106. {
  7107. // REI: This code-path doesn't seem to be used in the latest operation, and I'm not sure this is the behavior we want.
  7108. // The quest HUD doesn't handle this path, so leave a message in chat for it in case it is used.
  7109. // But I suggest that maybe in this case we should just fall back on the 'any weapon' code.
  7110. // we didn't find the weapon specified or it was intentially left blank
  7111. // send a message instead that says we need to survive the round
  7112. CBroadcastRecipientFilter filter;
  7113. if ( IsHostageRescueMap() )
  7114. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_GuardianModeSurviveRoundHostage" );
  7115. else
  7116. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_GuardianModeSurviveRound" );
  7117. }
  7118. }
  7119. }
  7120. // [tj] Keep track of number of players per side and if they have the same uniform
  7121. int terroristUniform = -1;
  7122. bool allTerroristsWearingSameUniform = true;
  7123. int numberOfTerrorists = 0;
  7124. int ctUniform = -1;
  7125. bool allCtsWearingSameUniform = true;
  7126. int numberOfCts = 0;
  7127. // Now respawn all players
  7128. CUtlVector< CCSPlayer* > respawningPlayersList;
  7129. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7130. {
  7131. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7132. if ( !pPlayer )
  7133. continue;
  7134. if ( pPlayer->GetTeamNumber() == TEAM_CT && PlayerModelInfo::GetPtr()->IsCTClass( pPlayer->PlayerClass() ) )
  7135. {
  7136. // [tj] Increment CT count and check CT uniforms.
  7137. numberOfCts++;
  7138. if ( ctUniform == -1 )
  7139. {
  7140. ctUniform = pPlayer->PlayerClass();
  7141. }
  7142. else if ( pPlayer->PlayerClass() != ctUniform )
  7143. {
  7144. allCtsWearingSameUniform = false;
  7145. }
  7146. respawningPlayersList.AddToTail( pPlayer );
  7147. }
  7148. else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && PlayerModelInfo::GetPtr()->IsTClass( pPlayer->PlayerClass() ) )
  7149. {
  7150. // [tj] Increment terrorist count and check terrorist uniforms
  7151. numberOfTerrorists++;
  7152. if ( terroristUniform == -1 )
  7153. {
  7154. terroristUniform = pPlayer->PlayerClass();
  7155. }
  7156. else if ( pPlayer->PlayerClass() != terroristUniform )
  7157. {
  7158. allTerroristsWearingSameUniform = false;
  7159. }
  7160. respawningPlayersList.AddToTail( pPlayer );
  7161. }
  7162. else
  7163. {
  7164. pPlayer->ObserverRoundRespawn();
  7165. }
  7166. }
  7167. // Shuffle spawning players list (this is done to ensure that spawning players don't always spawn in at the same spawn point)
  7168. ShufflePlayerList( respawningPlayersList );
  7169. // [menglish] reset per-round achievement variables for each player
  7170. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7171. {
  7172. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7173. if( pPlayer )
  7174. {
  7175. pPlayer->ResetRoundBasedAchievementVariables();
  7176. }
  7177. }
  7178. // Spawn the players
  7179. for ( int i = 0; i < respawningPlayersList.Count(); ++i )
  7180. {
  7181. respawningPlayersList[ i ]->RoundRespawn();
  7182. }
  7183. // move follower chickens
  7184. CBaseEntity *pNextChicken = NULL;
  7185. while ( ( pNextChicken = gEntList.FindEntityByClassname( pNextChicken, "chicken" ) ) != NULL )
  7186. {
  7187. CChicken * pChicken = dynamic_cast< CChicken* >( pNextChicken );
  7188. if ( pChicken && pChicken->GetLeader( ) )
  7189. {
  7190. if ( TheNavMesh )
  7191. {
  7192. CNavArea *pPlayerNav = TheNavMesh->GetNearestNavArea( pChicken->GetLeader( ) );
  7193. const float tooSmall = 15.0f;
  7194. if ( pPlayerNav && pPlayerNav->GetSizeX() > tooSmall && pPlayerNav->GetSizeY() > tooSmall )
  7195. {
  7196. {
  7197. pChicken->SetAbsOrigin( pPlayerNav->GetRandomPoint() );
  7198. }
  7199. }
  7200. }
  7201. pChicken->GetLeader( )->IncrementNumFollowers( ); // redo since this got cleared on player respawn
  7202. }
  7203. }
  7204. // Mark all QMM records as eligible for receiving money next round
  7205. FOR_EACH_MAP_FAST( m_mapQueuedMatchmakingPlayersData, idxQMMEntry )
  7206. {
  7207. m_mapQueuedMatchmakingPlayersData.Element( idxQMMEntry )->m_bReceiveNoMoneyNextRound = false;
  7208. }
  7209. // [tj] Award same uniform achievement for qualifying teams
  7210. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7211. {
  7212. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7213. if ( !pPlayer )
  7214. continue;
  7215. #if(ALL_WEARING_SAME_UNIFORM_ACHIEVEMENT)
  7216. if ( pPlayer->GetTeamNumber() == TEAM_CT && allCtsWearingSameUniform && numberOfCts >= AchievementConsts::SameUniform_MinPlayers)
  7217. {
  7218. pPlayer->AwardAchievement(CSSameUniform);
  7219. }
  7220. if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && allTerroristsWearingSameUniform && numberOfTerrorists >= AchievementConsts::SameUniform_MinPlayers)
  7221. {
  7222. pPlayer->AwardAchievement(CSSameUniform);
  7223. }
  7224. #endif
  7225. }
  7226. // [pfreese] Reset all round or match stats, depending on type of restart
  7227. if ( m_bCompleteReset )
  7228. {
  7229. CCS_GameStats.ResetAllStats();
  7230. CCS_GameStats.ResetPlayerClassMatchStats();
  7231. }
  7232. else
  7233. {
  7234. CCS_GameStats.ResetRoundStats();
  7235. }
  7236. // reset Match Stats
  7237. if ( m_bCompleteReset )
  7238. {
  7239. for ( int r = 0; r < MAX_MATCH_STATS_ROUNDS; r++ )
  7240. {
  7241. m_iMatchStats_RoundResults.GetForModify( r ) = 0;
  7242. m_iMatchStats_PlayersAlive_T.GetForModify( r ) = 0x3F;
  7243. m_iMatchStats_PlayersAlive_CT.GetForModify( r ) = 0x3F;
  7244. }
  7245. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7246. {
  7247. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7248. if( pPlayer )
  7249. {
  7250. for ( int r = 0; r < MAX_MATCH_STATS_ROUNDS; r++ )
  7251. {
  7252. pPlayer->m_iMatchStats_Damage.GetForModify( r ) = 0;
  7253. pPlayer->m_iMatchStats_EquipmentValue.GetForModify( r ) = 0;
  7254. pPlayer->m_iMatchStats_MoneySaved.GetForModify( r ) = 0;
  7255. pPlayer->m_iMatchStats_KillReward.GetForModify( r ) = 0;
  7256. pPlayer->m_iMatchStats_LiveTime.GetForModify( r ) = 0;
  7257. pPlayer->m_iMatchStats_Deaths.GetForModify( r ) = 0;
  7258. pPlayer->m_iMatchStats_Assists.GetForModify( r ) = 0;
  7259. pPlayer->m_iMatchStats_HeadShotKills.GetForModify( r ) = 0;
  7260. pPlayer->m_iMatchStats_Objective.GetForModify( r ) = 0;
  7261. pPlayer->m_iMatchStats_CashEarned.GetForModify( r ) = 0;
  7262. pPlayer->m_iMatchStats_UtilityDamage.GetForModify( r ) = 0;
  7263. pPlayer->m_iMatchStats_EnemiesFlashed.GetForModify( r ) = 0;
  7264. }
  7265. }
  7266. }
  7267. // Log the match starting event into server log
  7268. UTIL_LogPrintf( "World triggered \"Match_Start\" on \"%s\"\n", STRING( gpGlobals->mapname ) );
  7269. if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) )
  7270. {
  7271. char const *szName = pTeam->GetClanName();
  7272. if ( szName && *szName )
  7273. {
  7274. UTIL_LogPrintf( "Team playing \"CT\": %s\n", szName );
  7275. }
  7276. }
  7277. if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) )
  7278. {
  7279. char const *szName = pTeam->GetClanName();
  7280. if ( szName && *szName )
  7281. {
  7282. UTIL_LogPrintf( "Team playing \"TERRORIST\": %s\n", szName );
  7283. }
  7284. }
  7285. }
  7286. // now run a tkpunish check, after the map has been cleaned up
  7287. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7288. {
  7289. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7290. if ( !pPlayer )
  7291. continue;
  7292. if ( ( pPlayer->GetTeamNumber() == TEAM_CT && PlayerModelInfo::GetPtr()->IsCTClass( pPlayer->PlayerClass() )) ||
  7293. ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && PlayerModelInfo::GetPtr()->IsTClass( pPlayer->PlayerClass() ) ) )
  7294. {
  7295. pPlayer->CheckTKPunishment();
  7296. }
  7297. }
  7298. if (m_bMapHasBombTarget == true )
  7299. {
  7300. if ( !IsPlayingTraining() && !IsWarmupPeriod() && !IsPlayingGunGameProgressive()
  7301. && !IsPlayingGunGameDeathmatch() && !CSGameRules()->IsPlayingCoopMission() )
  7302. {
  7303. GiveC4ToRandomPlayer(); // Give C4 to the terrorists
  7304. if ( mp_defuser_allocation.GetInt() == DefuserAllocation::Random )
  7305. GiveDefuserToRandomPlayer();
  7306. }
  7307. }
  7308. // Reset game variables
  7309. m_flIntermissionStartTime = 0;
  7310. m_flRestartRoundTime = 0.0;
  7311. m_timeUntilNextPhaseStarts = 0.0f;
  7312. m_iAccountTerrorist = m_iAccountCT = 0;
  7313. m_iHostagesRescued = 0;
  7314. m_iHostagesTouched = 0;
  7315. m_flCMMItemDropRevealStartTime = 0;
  7316. m_flCMMItemDropRevealEndTime = 0;
  7317. // [tj] reset flawless and lossless round related flags
  7318. m_bNoTerroristsKilled = true;
  7319. m_bNoCTsKilled = true;
  7320. m_bNoTerroristsDamaged = true;
  7321. m_bNoCTsDamaged = true;
  7322. m_bNoEnemiesKilled = true;
  7323. m_pFirstKill = NULL;
  7324. m_pFirstBlood = NULL;
  7325. m_pMVP = NULL;
  7326. m_bCanDonateWeapons = true;
  7327. // [dwenger] Reset rescue-related achievement values
  7328. m_iHostagesRemaining = 0;
  7329. m_bAnyHostageReached = false;
  7330. m_arrRescuers.RemoveAll();
  7331. m_hostageWasInjured = false;
  7332. m_hostageWasKilled = false;
  7333. m_iRoundWinStatus = WINNER_NONE;
  7334. m_eRoundWinReason = RoundEndReason_StillInProgress;
  7335. m_bTargetBombed = m_bBombDefused = false;
  7336. m_bCompleteReset = false;
  7337. m_flNextHostageAnnouncement = gpGlobals->curtime;
  7338. m_iHostagesRemaining = g_Hostages.Count();
  7339. m_flDMBonusStartTime = gpGlobals->curtime + random->RandomFloat( mp_dm_time_between_bonus_min.GetFloat(), mp_dm_time_between_bonus_max.GetFloat() );
  7340. m_flDMBonusTimeLength = random->RandomFloat( mp_dm_bonus_length_min.GetFloat(), mp_dm_bonus_length_max.GetFloat() );
  7341. m_unDMBonusWeaponLoadoutSlot = PickRandomWeaponForDMBonus();
  7342. m_bDMBonusActive = false;
  7343. m_bIsDroppingItems = false;
  7344. // fire global game event
  7345. event = gameeventmanager->CreateEvent( "round_start" );
  7346. if ( event )
  7347. {
  7348. event->SetInt("timelimit", m_iRoundTime );
  7349. event->SetInt("fraglimit", 0 );
  7350. event->SetInt( "priority", 6 ); // round_start
  7351. if ( m_bMapHasRescueZone )
  7352. {
  7353. event->SetString("objective","HOSTAGE RESCUE");
  7354. }
  7355. else if ( m_bMapHasEscapeZone )
  7356. {
  7357. event->SetString("objective","PRISON ESCAPE");
  7358. }
  7359. else if ( m_bMapHasBombTarget || m_bMapHasBombZone )
  7360. {
  7361. event->SetString("objective","BOMB TARGET");
  7362. }
  7363. else
  7364. {
  7365. event->SetString("objective","DEATHMATCH");
  7366. }
  7367. gameeventmanager->FireEvent( event );
  7368. }
  7369. // clear out hits data
  7370. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7371. {
  7372. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7373. if ( pPlayer )
  7374. pPlayer->m_totalHitsOnServer = 0;
  7375. }
  7376. if ( IsWarmupPeriod() )
  7377. {
  7378. IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_warmup" );
  7379. if ( event )
  7380. gameeventmanager->FireEvent( event );
  7381. #ifndef CLIENT_DLL
  7382. CheckForGiftsLeaderboardUpdate();
  7383. #endif
  7384. }
  7385. #ifndef CLIENT_DLL
  7386. if ( m_match.GetRoundsPlayed() <= 0 )
  7387. {
  7388. CheckForGiftsLeaderboardUpdate();
  7389. }
  7390. #endif
  7391. // We need to reassign the player's pointers to entities that were killed during the map clean up but have been recreated since the round_start event was called.
  7392. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7393. {
  7394. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7395. if ( !pPlayer )
  7396. continue;
  7397. pPlayer->UpdateMapEntityPointers();
  7398. }
  7399. m_iActiveAssassinationTargetMissionID = 0;
  7400. // [pfreese] I commented out this call to CreateWeaponManager, as the
  7401. // CGameWeaponManager object doesn't appear to be actually used by the CSS
  7402. // code, and in any case, the weapon manager does not support wildcards in
  7403. // entity names (as seemingly indicated) below. When the manager fails to
  7404. // create its factory, it removes itself in any case.
  7405. // CreateWeaponManager( "weapon_*", gpGlobals->maxClients * 2 );
  7406. g_pPlayerResource->UpdatePlayerData();
  7407. g_EntitySpotting->UpdateSpottedEntities();
  7408. if ( bClearAccountsAfterHalftime && IsPlayingClassic() && HasHalfTime() )
  7409. {
  7410. AssignStartingMoneyToAllPlayers();
  7411. m_iNumConsecutiveTerroristLoses = 0;
  7412. m_iNumConsecutiveCTLoses = 0;
  7413. m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS );
  7414. }
  7415. if ( !IsPlayingTraining() )
  7416. {
  7417. // should we show an announcement to declare that this round might be the last round?
  7418. if ( IsLastRoundBeforeHalfTime() )
  7419. {
  7420. IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_last_round_half" );
  7421. if ( event )
  7422. gameeventmanager->FireEvent( event );
  7423. }
  7424. else if ( IsLastRoundOfMatch() )
  7425. {
  7426. // don't send the final round event if one of the teams just won the round by clinching
  7427. int iNumWinsToClinch = GetNumWinsToClinch();
  7428. if ( m_match.GetCTScore() != iNumWinsToClinch && m_match.GetTerroristScore() != iNumWinsToClinch )
  7429. {
  7430. IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_final" );
  7431. if ( event )
  7432. gameeventmanager->FireEvent( event );
  7433. }
  7434. }
  7435. else if ( IsMatchPoint() )
  7436. {
  7437. IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_match_point" );
  7438. if ( event )
  7439. gameeventmanager->FireEvent( event );
  7440. }
  7441. }
  7442. // if a team voted to surrender and it passed at the end of a round and we went into halftime,
  7443. // switch the teams that need to surrender
  7444. if ( m_bSwitchingTeamsAtRoundReset )
  7445. {
  7446. OnTeamsSwappedAtRoundReset();
  7447. }
  7448. m_bSwitchingTeamsAtRoundReset = false;
  7449. // Unfreeze all players now that the round is starting
  7450. UnfreezeAllPlayers();
  7451. if ( m_bPlayerItemsHaveBeenDisplayed )
  7452. ClearItemsDroppedDuringMatch();
  7453. // Perform round-related processing at the point when the next round has just restarted
  7454. // (This line should be last in this function)
  7455. PostRestartRound();
  7456. }
  7457. // Perform round-related processing at the point when the next round has just restarted
  7458. void CCSGameRules::PostRestartRound( void )
  7459. {
  7460. if ( m_match.GetRoundsPlayed() < 1 )
  7461. {
  7462. // Ensure all spectating players are in correct mode at the beginning of the match
  7463. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  7464. {
  7465. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  7466. if ( pPlayer && pPlayer->IsObserver() )
  7467. {
  7468. // Only process players in observer mode
  7469. int nMode = pPlayer->GetObserverMode();
  7470. if ( nMode != OBS_MODE_CHASE && nMode != OBS_MODE_IN_EYE )
  7471. {
  7472. // If the player is not in the chase or in-eye mode then force them to chase mode
  7473. nMode = OBS_MODE_CHASE;
  7474. }
  7475. // Build and send the command to ensure player is in a valid observer mode
  7476. char szCommand[ 32 ];
  7477. V_snprintf( szCommand, sizeof( szCommand ), "spec_mode %i", nMode );
  7478. CCommand cmd;
  7479. cmd.Tokenize( szCommand, kCommandSrcCode );
  7480. ClientCommand( pPlayer, cmd );
  7481. }
  7482. }
  7483. }
  7484. IGameEvent * event = gameeventmanager->CreateEvent( "round_poststart" );
  7485. if ( event )
  7486. {
  7487. gameeventmanager->FireEvent( event );
  7488. }
  7489. }
  7490. void CCSGameRules::UnfreezeAllPlayers( void )
  7491. {
  7492. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  7493. {
  7494. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  7495. if ( pPlayer )
  7496. {
  7497. pPlayer->RemoveFlag( FL_FROZEN );
  7498. }
  7499. }
  7500. }
  7501. loadout_positions_t CCSGameRules::PickRandomWeaponForDMBonus( void )
  7502. {
  7503. return LOADOUT_POSITION_INVALID;
  7504. }
  7505. void CCSGameRules::AssignStartingMoneyToAllPlayers( void )
  7506. {
  7507. // Loop through all players and give them only the starting money
  7508. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7509. {
  7510. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7511. if ( !pPlayer )
  7512. continue;
  7513. if ( pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  7514. {
  7515. int amount_to_assign = -pPlayer->GetAccountBalance() + GetStartMoney();
  7516. pPlayer->AddAccount( amount_to_assign, false );
  7517. }
  7518. }
  7519. }
  7520. void CCSGameRules::GiveC4ToRandomPlayer()
  7521. {
  7522. // Don't give C4 if not everyone is in the game, we are going to restart or if the convar says we should not
  7523. bool bNeeded = false;
  7524. NeededPlayersCheck( bNeeded );
  7525. float timeToRestart = GetRoundRestartTime() - gpGlobals->curtime;
  7526. if ( !mp_give_player_c4.GetBool() || timeToRestart > 0.001f )
  7527. {
  7528. return;
  7529. }
  7530. CUtlVector<CCSPlayer*> candidates;
  7531. candidates.EnsureCapacity(MAX_PLAYERS);
  7532. // add all eligible terrorist candidates to a list
  7533. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7534. {
  7535. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  7536. if ( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  7537. {
  7538. candidates.AddToTail(pPlayer);
  7539. }
  7540. }
  7541. // randomly shuffle the list; this will keep the selection random in case of ties
  7542. FOR_EACH_VEC(candidates, i)
  7543. {
  7544. V_swap(candidates[i], candidates[random->RandomInt( 0, candidates.Count() - 1)] );
  7545. }
  7546. // now sort the list
  7547. candidates.Sort(BombSortPredicate);
  7548. // give bomb to the first candidate
  7549. if ( candidates.Count() > 0 )
  7550. {
  7551. CCSPlayer *pPlayer = candidates[0];
  7552. Assert( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->IsAlive() );
  7553. pPlayer->GiveNamedItem( WEAPON_C4_CLASSNAME );
  7554. pPlayer->SelectItem( "weapon_c4" );
  7555. pPlayer->m_fLastGivenBombTime = gpGlobals->curtime;
  7556. //ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_Have_Bomb" );
  7557. }
  7558. m_bBombDropped = false;
  7559. }
  7560. static int DefuserSortPredicate(CCSPlayer * const *left, CCSPlayer * const *right)
  7561. {
  7562. // should we prioritize humans over bots?
  7563. if (cv_bot_defer_to_human_items.GetBool() )
  7564. {
  7565. if ( (*left)->IsBot() && !(*right)->IsBot() )
  7566. return 1;
  7567. if ( !(*left)->IsBot() && (*right)->IsBot() )
  7568. return -1;
  7569. }
  7570. if ( (*left)->m_fLastGivenDefuserTime < (*right)->m_fLastGivenDefuserTime )
  7571. return -1;
  7572. if ( (*left)->m_fLastGivenDefuserTime > (*right)->m_fLastGivenDefuserTime )
  7573. return +1;
  7574. return 0;
  7575. }
  7576. void CCSGameRules::GiveDefuserToRandomPlayer()
  7577. {
  7578. int iDefusersToGive = 2;
  7579. CUtlVector<CCSPlayer*> candidates;
  7580. candidates.EnsureCapacity(MAX_PLAYERS);
  7581. // add all CT candidates to a list
  7582. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7583. {
  7584. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  7585. if ( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == TEAM_CT )
  7586. {
  7587. candidates.AddToTail(pPlayer);
  7588. }
  7589. }
  7590. // randomly shuffle the list; this will keep the selection random in case of ties
  7591. FOR_EACH_VEC(candidates, i)
  7592. {
  7593. V_swap(candidates[i], candidates[random->RandomInt( 0, candidates.Count() - 1)] );
  7594. }
  7595. // now sort the shuffled list into subgroups
  7596. candidates.Sort(DefuserSortPredicate);
  7597. // give defusers to the first N candidates
  7598. for ( int i = 0; i < iDefusersToGive && i < candidates.Count(); ++i )
  7599. {
  7600. CCSPlayer *pPlayer = candidates[i];
  7601. Assert( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->IsAlive() );
  7602. pPlayer->GiveDefuser(false);
  7603. pPlayer->HintMessage( "#Hint_you_have_the_defuser", false, true );
  7604. }
  7605. }
  7606. void CCSGameRules::Think()
  7607. {
  7608. // NOTE: We are skipping calling CTeamplayRules::Think() and CMultiplayRules::Think()
  7609. // by calling CGameRules directly. Be aware of this.
  7610. CGameRules::Think();
  7611. // if ( m_DeferredCallQueue.Count() > 0 && gpGlobals->curtime > m_flDeferredCallDispatchTime )
  7612. // {
  7613. // m_DeferredCallQueue.CallQueued();
  7614. // }
  7615. //
  7616. // Check if connected players have bans on record
  7617. //
  7618. int nCooldownMode = sv_kick_players_with_cooldown.GetInt();
  7619. if ( ( nCooldownMode <= 0 ) && steamgameserverapicontext && steamgameserverapicontext->SteamGameServer() && steamgameserverapicontext->SteamGameServer()->BSecure() )
  7620. nCooldownMode = 1; // On VAC secure servers enforce global cooldowns
  7621. if ( ( nCooldownMode > 0 ) && CCSGameRules::sm_mapGcBanInformation.Count() )
  7622. {
  7623. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7624. {
  7625. CBasePlayer *pBasePlayer = UTIL_PlayerByIndex( i );
  7626. if ( !pBasePlayer )
  7627. continue;
  7628. CSteamID steamID;
  7629. if ( pBasePlayer->GetSteamID( &steamID ) && steamID.IsValid() &&
  7630. steamID.GetAccountID() )
  7631. {
  7632. CCSGameRules::GcBanInformationMap_t::IndexType_t idx = CCSGameRules::sm_mapGcBanInformation.Find( steamID.GetAccountID() );
  7633. if ( idx != CCSGameRules::sm_mapGcBanInformation.InvalidIndex() )
  7634. {
  7635. CCSGameRules::CGcBanInformation_t &banInfo = CCSGameRules::sm_mapGcBanInformation.Element( idx );
  7636. if ((banInfo.m_dblExpiration > Plat_FloatTime()) && !EMsgGCCStrike15_v2_MatchmakingKickBanReason_IsGreen(banInfo.m_uiReason) &&
  7637. ( ( nCooldownMode > 1 ) || EMsgGCCStrike15_v2_MatchmakingKickBanReason_IsGlobal( banInfo.m_uiReason ) ) )
  7638. {
  7639. // Kick this guy
  7640. Msg( "Kicking user %s (sv_kick_players_with_cooldown=%d)\n", pBasePlayer->GetPlayerName(), nCooldownMode );
  7641. if ( sv_kick_ban_duration.GetInt() > 0 )
  7642. {
  7643. // don't roll the kick command into this, it will fail on a lan, where kickid will go through
  7644. engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pBasePlayer->GetUserID() ) );
  7645. }
  7646. char const *szReasonForKick = "Player has competitive matchmaking cooldown";
  7647. switch ( banInfo.m_uiReason )
  7648. {
  7649. case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_OfficialBan:
  7650. case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_ChallengeNotification:
  7651. szReasonForKick = "Account is Untrusted";
  7652. break;
  7653. case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_GsltViolation:
  7654. case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_ConvictedForBehavior:
  7655. case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_ConvictedForCheating:
  7656. szReasonForKick = "Account is Convicted";
  7657. break;
  7658. case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_NoUserSession:
  7659. szReasonForKick = INVALID_STEAM_TICKET;
  7660. break;
  7661. }
  7662. engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d %s\n", pBasePlayer->GetUserID(), ( g_pGameRules && ((CCSGameRules *) g_pGameRules)->IsPlayingOffline() ) ? 0 : 1,
  7663. szReasonForKick ) );
  7664. }
  7665. }
  7666. }
  7667. }
  7668. }
  7669. extern void ServerThinkReplayUploader();
  7670. ServerThinkReplayUploader();
  7671. if ( IsQueuedMatchmaking() )
  7672. {
  7673. CEngineHltvInfo_t engineHltv;
  7674. if ( engine->GetEngineHltvInfo( engineHltv ) && engineHltv.m_bBroadcastActive && engineHltv.m_bMasterProxy )
  7675. {
  7676. int numCurrentSpectators = engineHltv.m_numClients - engineHltv.m_numProxies + engineHltv.m_numExternalTotalViewers;
  7677. int numCurrentSpectatorsTV = engineHltv.m_numClients - engineHltv.m_numProxies;
  7678. int numCurrentSpectatorsLnk = engineHltv.m_numExternalLinkedViewers;
  7679. if ( numCurrentSpectators > int( m_numSpectatorsCountMax ) )
  7680. m_numSpectatorsCountMax = numCurrentSpectators;
  7681. if ( numCurrentSpectatorsTV > int( m_numSpectatorsCountMaxTV ) )
  7682. m_numSpectatorsCountMaxTV = numCurrentSpectatorsTV;
  7683. if ( numCurrentSpectatorsLnk > int( m_numSpectatorsCountMaxLnk ) )
  7684. m_numSpectatorsCountMaxLnk = numCurrentSpectatorsLnk;
  7685. }
  7686. }
  7687. // This fires begin_new_match once when a new match starts... there are other similar game events
  7688. // but they all get fired multiple times between ending and starting a new match. Since we're using
  7689. // this event to restart an OGS session we can't let it spam like that.
  7690. if ( !m_bHasMatchStarted && // Turned off when we go to intermission, turned on once we fire the even
  7691. m_match.GetPhase() != GAMEPHASE_MATCH_ENDED && // Don't try to run this while we're still in intermission
  7692. !IsWarmupPeriod() && // Don't start the match until the first round after warmup
  7693. m_flRestartRoundTime == 0.0f && // This awkwardly ensures we've done a RestartRound() and are currently still waiting on one
  7694. UTIL_HumansInGame( true, true ) > 0 ) // don't start until there is somebody on a team
  7695. {
  7696. IGameEvent * newMatch = gameeventmanager->CreateEvent( "begin_new_match" );
  7697. if( newMatch )
  7698. {
  7699. gameeventmanager->FireEvent( newMatch );
  7700. }
  7701. m_bHasMatchStarted = true;
  7702. m_fMatchStartTime = gpGlobals->curtime;
  7703. if ( m_bPlayerItemsHaveBeenDisplayed )
  7704. ClearItemsDroppedDuringMatch();
  7705. CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource );
  7706. if ( pResource )
  7707. pResource->ForcePlayersPickColors();
  7708. }
  7709. // Display autobalance messages if necessary
  7710. if ( m_fAutobalanceDisplayTime > 0.0f && gpGlobals->curtime > m_fAutobalanceDisplayTime )
  7711. {
  7712. if ( m_AutobalanceStatus == AutobalanceStatus::THIS_ROUND )
  7713. {
  7714. UTIL_ClientPrintFilter( m_AutoBalanceTraitors, HUD_PRINTCENTER, "#SFUI_Notice_Player_Balanced" );
  7715. UTIL_ClientPrintFilter( m_AutoBalanceLoyalists, HUD_PRINTCENTER, "#SFUI_Notice_Teams_Balanced" );
  7716. m_AutoBalanceTraitors.RemoveAllRecipients();
  7717. m_AutoBalanceLoyalists.RemoveAllRecipients();
  7718. }
  7719. else if ( m_AutobalanceStatus == AutobalanceStatus::NEXT_ROUND )
  7720. {
  7721. UTIL_ClientPrintAll( HUD_PRINTCENTER,"#SFUI_Notice_Auto_Team_Balance_Next_Round");
  7722. }
  7723. m_fAutobalanceDisplayTime = 0.0f;
  7724. m_AutobalanceStatus = AutobalanceStatus::NONE;
  7725. }
  7726. //Update replicated variable for time till next match or half
  7727. if ( m_match.GetPhase() == GAMEPHASE_HALFTIME )
  7728. {
  7729. if ( mp_halftime_pausetimer.GetBool() )
  7730. {
  7731. //Delay m_flRestartRoundTime for as long as we're paused.
  7732. m_flRestartRoundTime += gpGlobals->curtime - m_flLastThinkTime;
  7733. }
  7734. m_timeUntilNextPhaseStarts = m_flRestartRoundTime - gpGlobals->curtime;
  7735. m_bIsDroppingItems = false;
  7736. // Can also implement mp_halftime_pausematch here
  7737. }
  7738. else if ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED )
  7739. {
  7740. float flIntermissionDuration = IsQueuedMatchmaking() ? MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() ) : GetIntermissionDuration();
  7741. if ( m_ItemsPtrDroppedDuringMatch.Count() > 0 )
  7742. {
  7743. // synch up the server's list of items recieved during this match to the ones on every client
  7744. if ( !m_bPlayerItemsHaveBeenDisplayed && ( m_phaseChangeAnnouncementTime > 0 && gpGlobals->curtime > m_phaseChangeAnnouncementTime ) )
  7745. {
  7746. SendPlayerItemDropsToClient();
  7747. IGameEvent * event = gameeventmanager->CreateEvent( "endmatch_cmm_start_reveal_items" );
  7748. if( event )
  7749. {
  7750. gameeventmanager->FireEvent( event );
  7751. }
  7752. //now delay the rematch/failed vote/etc stuff until we are done revealing the items dropped
  7753. // 1 second per drop + 2 extra seconds for looking
  7754. m_flCMMItemDropRevealStartTime = gpGlobals->curtime;
  7755. }
  7756. // make sure that the intermission time accounts for the number of items we're giving out
  7757. if ( m_flIntermissionStartTime &&
  7758. ( (m_flIntermissionStartTime + flIntermissionDuration) < m_flCMMItemDropRevealStartTime + (GetCMMItemDropRevealDuration() + 4.0f) ) )
  7759. {
  7760. m_flIntermissionStartTime = ( m_flCMMItemDropRevealStartTime + (GetCMMItemDropRevealDuration() + 4.0f) ) - flIntermissionDuration;
  7761. }
  7762. }
  7763. if (m_bIsDroppingItems && (m_flIntermissionStartTime + mp_win_panel_display_time.GetInt() + 5.0f + sv_reward_drop_delay.GetFloat()) < gpGlobals->curtime && m_flCMMItemDropRevealEndTime < gpGlobals->curtime)
  7764. {
  7765. m_bIsDroppingItems = false;
  7766. CheckSetVoteTime();
  7767. }
  7768. if ( IsPlayingGunGameProgressive() )
  7769. {
  7770. m_timeUntilNextPhaseStarts = (m_flRestartRoundTime + flIntermissionDuration + GetCMMItemDropRevealDuration()) - gpGlobals->curtime;
  7771. }
  7772. else
  7773. {
  7774. m_timeUntilNextPhaseStarts = m_flIntermissionStartTime + flIntermissionDuration - gpGlobals->curtime;
  7775. }
  7776. if ( m_flRestartRoundTime > 0.0f && ( ( m_flRestartRoundTime - 3.0 ) <= gpGlobals->curtime ) && !m_bVoiceWonMatchBragFired )
  7777. {
  7778. // [tj] Inform players that the round is over
  7779. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7780. {
  7781. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  7782. if( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == m_match.GetWinningTeam() )
  7783. {
  7784. // have someone on the winning team brag about winning over the radio
  7785. pPlayer->Radio( "WonRound", "", true );
  7786. break;
  7787. }
  7788. }
  7789. m_bVoiceWonMatchBragFired = true;
  7790. }
  7791. }
  7792. else
  7793. {
  7794. m_timeUntilNextPhaseStarts = 0.0f;
  7795. m_bVoiceWonMatchBragFired = false;
  7796. m_bIsDroppingItems = false;
  7797. }
  7798. //Check if it is time to make the phase change announcement
  7799. if ( m_phaseChangeAnnouncementTime > 0 && gpGlobals->curtime > m_phaseChangeAnnouncementTime )
  7800. {
  7801. IGameEvent * event = gameeventmanager->CreateEvent( "announce_phase_end" );
  7802. if ( event )
  7803. {
  7804. gameeventmanager->FireEvent( event );
  7805. }
  7806. m_phaseChangeAnnouncementTime = 0.0f;
  7807. }
  7808. //Let all teams process
  7809. for ( int i = 0; i < GetNumberOfTeams(); i++ )
  7810. {
  7811. GetGlobalTeam( i )->Think();
  7812. }
  7813. // Update Team Clan Names periodically
  7814. if ( m_fNextUpdateTeamClanNamesTime <= gpGlobals->curtime )
  7815. {
  7816. m_fNextUpdateTeamClanNamesTime = gpGlobals->curtime + 2;
  7817. UpdateTeamPredictions();
  7818. UpdateTeamClanNames( TEAM_CT );
  7819. UpdateTeamClanNames( TEAM_TERRORIST );
  7820. }
  7821. ///// Check game rules /////
  7822. if ( CheckGameOver() )
  7823. {
  7824. return;
  7825. }
  7826. // did somebody hit the fraglimit ?
  7827. if ( CheckFragLimit() )
  7828. {
  7829. return;
  7830. }
  7831. //Restart if we were flagged to do so
  7832. if ( m_endMatchOnThink )
  7833. {
  7834. m_endMatchOnThink = false;
  7835. GoToIntermission();
  7836. return;
  7837. }
  7838. if ( !m_bBuyTimeEnded && IsBuyTimeElapsed() )
  7839. {
  7840. m_bBuyTimeEnded = true;
  7841. IGameEvent * event = gameeventmanager->CreateEvent( "buytime_ended" );
  7842. if ( event )
  7843. {
  7844. gameeventmanager->FireEvent( event );
  7845. }
  7846. }
  7847. //Check for clinch
  7848. int iNumWinsToClinch = GetNumWinsToClinch();
  7849. bool bTeamHasClinchedVictory = false;
  7850. bool bMatchHasEnded = false;
  7851. //Check for halftime switching
  7852. if ( m_match.GetPhase() == GAMEPHASE_PLAYING_FIRST_HALF )
  7853. {
  7854. //The number of rounds before halftime depends on the mode and the associated convar
  7855. int numRoundsBeforeHalftime =
  7856. GetOvertimePlaying()
  7857. ? ( mp_maxrounds.GetInt() + ( 2*GetOvertimePlaying() - 1 )*( mp_overtime_maxrounds.GetInt() / 2 ) )
  7858. : ( mp_maxrounds.GetInt() / 2 );
  7859. //Finally, check for halftime
  7860. bool bhalftime = false;
  7861. if ( numRoundsBeforeHalftime > 0 )
  7862. {
  7863. if ( m_match.GetRoundsPlayed() >= numRoundsBeforeHalftime )
  7864. {
  7865. bhalftime = true;
  7866. }
  7867. }
  7868. // if maxrounds is 0 then the server is relying on mp_timelimit rather than mp_maxrounds.
  7869. else if ( ( GetMapRemainingTime() <= ( ( mp_timelimit.GetInt() * 60 ) / 2 ) ) && IsRoundOver() )
  7870. {
  7871. bhalftime = true;
  7872. }
  7873. if ( bhalftime )
  7874. {
  7875. m_match.SetPhase( GAMEPHASE_HALFTIME );
  7876. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  7877. m_flRestartRoundTime = gpGlobals->curtime + mp_halftime_duration.GetFloat();
  7878. SwitchTeamsAtRoundReset();
  7879. FreezePlayers();
  7880. }
  7881. }
  7882. //Check for end of half-time match
  7883. else if ( m_match.GetPhase() == GAMEPHASE_PLAYING_SECOND_HALF )
  7884. {
  7885. //Check for clinch
  7886. if ( iNumWinsToClinch > 0 && ( HasHalfTime() && !IsPlayingTraining()) )
  7887. {
  7888. bTeamHasClinchedVictory = ( m_match.GetCTScore() >= iNumWinsToClinch ) || ( m_match.GetTerroristScore() >= iNumWinsToClinch );
  7889. }
  7890. //Finally, if there have enough rounds played, end the match
  7891. bool bEndMatch = false;
  7892. int numRoundToEndMatch = mp_maxrounds.GetInt() + GetOvertimePlaying()*mp_overtime_maxrounds.GetInt();
  7893. if ( numRoundToEndMatch > 0 )
  7894. {
  7895. if ( m_match.GetRoundsPlayed() >= numRoundToEndMatch || bTeamHasClinchedVictory )
  7896. {
  7897. bEndMatch = true;
  7898. }
  7899. }
  7900. else if ( GetMapRemainingTime() <= 0 && IsRoundOver() )
  7901. {
  7902. bEndMatch = true;
  7903. }
  7904. // Check if the match ended in a tie and needs overtime
  7905. if ( bEndMatch && mp_overtime_enable.GetBool() && !bTeamHasClinchedVictory )
  7906. {
  7907. bEndMatch = false;
  7908. m_match.GoToOvertime( 1 );
  7909. m_match.SetPhase( GAMEPHASE_HALFTIME );
  7910. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  7911. m_flRestartRoundTime = gpGlobals->curtime + mp_halftime_duration.GetFloat();
  7912. // SwitchTeamsAtRoundReset(); -- don't switch teams, only switch at true halftimes
  7913. FreezePlayers();
  7914. }
  7915. if ( bEndMatch )
  7916. {
  7917. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  7918. GoToIntermission();
  7919. bMatchHasEnded = true;
  7920. if ( bTeamHasClinchedVictory && m_match.GetRoundsPlayed() < numRoundToEndMatch )
  7921. {
  7922. // Send chat message to let players know why match is ending early
  7923. CRecipientFilter filter;
  7924. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  7925. {
  7926. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  7927. if ( pPlayer && ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR || pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST ) )
  7928. {
  7929. filter.AddRecipient( pPlayer );
  7930. }
  7931. }
  7932. filter.MakeReliable();
  7933. if ( m_match.GetCTScore() > m_match.GetTerroristScore() )
  7934. {
  7935. // CTs have clinched the match
  7936. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_CTs_Clinched_Match" );
  7937. }
  7938. else
  7939. {
  7940. // Ts have clinched the match
  7941. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Ts_Clinched_Match" );
  7942. }
  7943. }
  7944. }
  7945. }
  7946. //If playing a non-halftime game, check the max rounds
  7947. else if ( m_match.GetPhase() == GAMEPHASE_PLAYING_STANDARD )
  7948. {
  7949. // Check for a clinch
  7950. if ( mp_maxrounds.GetInt() > 0 && !IsPlayingTraining() && mp_match_can_clinch.GetBool() )
  7951. {
  7952. bTeamHasClinchedVictory = ( m_match.GetCTScore() >= iNumWinsToClinch ) || ( m_match.GetTerroristScore() >= iNumWinsToClinch );
  7953. }
  7954. // End the match if ( ( maxrounds are used ) and ( we've reached maxrounds or clinched the game ) ) or ( we've exceeded timelimit )
  7955. if ( mp_maxrounds.GetInt() > 0 && !IsPlayingTraining() )
  7956. {
  7957. if ( m_match.GetRoundsPlayed() >= mp_maxrounds.GetInt() || bTeamHasClinchedVictory )
  7958. {
  7959. bMatchHasEnded = true;
  7960. }
  7961. }
  7962. else if ( GetMapRemainingTime() <= 0 && !IsPlayingTraining() && IsRoundOver() )
  7963. {
  7964. bMatchHasEnded = true;
  7965. }
  7966. if ( bMatchHasEnded == true )
  7967. {
  7968. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  7969. GoToIntermission();
  7970. }
  7971. }
  7972. if ( IsWarmupPeriod() )
  7973. {
  7974. #ifdef GAME_DLL
  7975. if ( IsQueuedMatchmaking() )
  7976. {
  7977. // if all humans are present and warmup time left is greater than mp_warmuptime_all_players_connected, reduce warmup time to mp_warmuptime_all_players_connected
  7978. if ( ( UTIL_HumansInGame( true, false ) == ( int ) MatchmakingGameTypeGameMaxPlayers( MatchmakingGameTypeToGame( sm_QueuedServerReservation.game_type() ) ) )
  7979. && ( mp_warmuptime_all_players_connected.GetFloat() > 0 ) && ( GetWarmupPeriodEndTime() - mp_warmuptime_all_players_connected.GetFloat() >= gpGlobals->curtime ) )
  7980. {
  7981. m_fWarmupPeriodStart = gpGlobals->curtime;
  7982. mp_warmuptime.SetValue( mp_warmuptime_all_players_connected.GetFloat() );
  7983. // notify players
  7984. CBroadcastRecipientFilter filter;
  7985. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_All_Players_Connected", mp_warmuptime_all_players_connected.GetString() );
  7986. }
  7987. }
  7988. if ( IsWarmupPeriodPaused() && ( GetWarmupPeriodEndTime() - 6 >= gpGlobals->curtime) ) // Ignore warmup pause if within 6s of end.
  7989. {
  7990. // push out the timers indefinitely.
  7991. m_fWarmupPeriodStart += gpGlobals->curtime - m_flLastThinkTime;
  7992. m_fWarmupNextChatNoticeTime += gpGlobals->curtime - m_flLastThinkTime;
  7993. }
  7994. if ( m_fWarmupNextChatNoticeTime < gpGlobals->curtime )
  7995. {
  7996. m_fWarmupNextChatNoticeTime = gpGlobals->curtime + 10;
  7997. CBroadcastRecipientFilter filter;
  7998. if ( IsQueuedMatchmaking() && ( UTIL_HumansInGame(true, false) < ( int ) MatchmakingGameTypeGameMaxPlayers( MatchmakingGameTypeToGame( sm_QueuedServerReservation.game_type() ) ) ) )
  7999. {
  8000. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Match_Will_Start_Waiting_Chat" );
  8001. }
  8002. else
  8003. {
  8004. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Match_Will_Start_Chat" );
  8005. }
  8006. if ( !m_numGlobalGiftsGiven )
  8007. CheckForGiftsLeaderboardUpdate();
  8008. }
  8009. #endif
  8010. //bool bIsPlayingProgressive = CSGameRules() && CSGameRules()->IsPlayingGunGameProgressive();
  8011. extern ConVar mp_do_warmup_period;
  8012. if ( ( UTIL_HumansInGame( true, true ) > 0 && ( GetWarmupPeriodEndTime() - 5 < gpGlobals->curtime) ) || !mp_do_warmup_period.GetBool() )
  8013. {
  8014. extern ConVar mp_warmup_pausetimer;
  8015. mp_warmup_pausetimer.SetValue( 0 ); // Timer is unpausable within 5 seconds of its end.
  8016. if (GetWarmupPeriodEndTime() <= gpGlobals->curtime)
  8017. {
  8018. // when the warmup period ends, set the round to restart in 3 seconds
  8019. if (!m_bCompleteReset && !m_bGameRestart)
  8020. {
  8021. GetGlobalTeam( TEAM_CT )->ResetTeamLeaders();
  8022. GetGlobalTeam( TEAM_TERRORIST )->ResetTeamLeaders();
  8023. m_flRestartRoundTime = gpGlobals->curtime + g_flWarmupToFreezetimeDelay;/*4.0*/;
  8024. m_bCompleteReset = true;
  8025. m_bGameRestart = true;
  8026. FreezePlayers();
  8027. CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource );
  8028. if ( pResource )
  8029. pResource->ForcePlayersPickColors();
  8030. {
  8031. CReliableBroadcastRecipientFilter filter;
  8032. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Warmup_Has_Ended" );
  8033. m_fWarmupNextChatNoticeTime = gpGlobals->curtime + 10;
  8034. }
  8035. {
  8036. CReliableBroadcastRecipientFilter filter;
  8037. CCSUsrMsg_WarmupHasEnded msg;
  8038. SendUserMessage( filter, CS_UM_WarmupHasEnded, msg );
  8039. }
  8040. }
  8041. // when the round resets, turn off the warmup period
  8042. if ( m_flRestartRoundTime <= gpGlobals->curtime )
  8043. {
  8044. m_bWarmupPeriod = false;
  8045. }
  8046. }
  8047. }
  8048. }
  8049. // Check for the end of the round.
  8050. if ( IsFreezePeriod() )
  8051. {
  8052. CheckFreezePeriodExpired();
  8053. }
  8054. else
  8055. {
  8056. CheckRoundTimeExpired();
  8057. }
  8058. CheckLevelInitialized();
  8059. if ( !m_bRoundTimeWarningTriggered && GetRoundRemainingTime() < ROUND_END_WARNING_TIME && !IsPlayingTraining( ) )
  8060. {
  8061. m_bRoundTimeWarningTriggered = true;
  8062. IGameEvent * event = gameeventmanager->CreateEvent( "round_time_warning" );
  8063. if ( event )
  8064. {
  8065. gameeventmanager->FireEvent( event );
  8066. }
  8067. }
  8068. if ( IsPlayingCoopMission() &&
  8069. m_flRestartRoundTime > 0.0f && ( ( m_flRestartRoundTime - 0.5 ) <= gpGlobals->curtime ) &&
  8070. !IsWarmupPeriod() && !m_bHasTriggeredCoopSpawnReset )
  8071. {
  8072. // we have to reset the spawns, BEFORE the PreRestartRound because that's where the spawns are initially set up
  8073. // and we have to let anought time for map logic to set them all up
  8074. CGameCoopMissionManager *pManager = GetCoopMissionManager();
  8075. // we really dont have one
  8076. if ( !pManager )
  8077. {
  8078. DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't keep track of completed waves without it!\n" );
  8079. }
  8080. else
  8081. {
  8082. pManager->SetSpawnsReset();
  8083. }
  8084. m_bHasTriggeredCoopSpawnReset = true;
  8085. }
  8086. if (m_flRestartRoundTime > 0.0f && ((m_flRestartRoundTime - 0.3) <= gpGlobals->curtime) && !m_bHasTriggeredRoundStartMusic)
  8087. {
  8088. // Perform round-related processing at the point when there is less than 1 second of "free play" to go before the round officially ends
  8089. // At this point the round winner has been determined and displayed to the players
  8090. PreRestartRound();
  8091. }
  8092. if ( m_flRestartRoundTime > 0.0f && m_flRestartRoundTime <= gpGlobals->curtime )
  8093. {
  8094. if ( IsWarmupPeriod() && m_match.GetPhase() != GAMEPHASE_MATCH_ENDED && GetWarmupPeriodEndTime() <= gpGlobals->curtime && UTIL_HumansInGame( false, true ) && m_flGameStartTime != 0 )
  8095. {
  8096. m_bCompleteReset = true;
  8097. m_flRestartRoundTime = gpGlobals->curtime + 1;
  8098. mp_restartgame.SetValue( 5 );
  8099. m_bWarmupPeriod = false;
  8100. }
  8101. else
  8102. {
  8103. bool botSpeaking = false;
  8104. for ( int i=1; i <= gpGlobals->maxClients; ++i )
  8105. {
  8106. CBasePlayer *player = UTIL_PlayerByIndex( i );
  8107. if (player == NULL)
  8108. continue;
  8109. if (!player->IsBot())
  8110. continue;
  8111. CCSBot *bot = dynamic_cast< CCSBot * >(player);
  8112. if ( !bot )
  8113. continue;
  8114. if ( bot->IsUsingVoice() )
  8115. {
  8116. if ( gpGlobals->curtime > m_flRestartRoundTime + 10.0f )
  8117. {
  8118. Msg( "Ignoring speaking bot %s at round end\n", bot->GetPlayerName() );
  8119. }
  8120. else
  8121. {
  8122. botSpeaking = true;
  8123. break;
  8124. }
  8125. }
  8126. }
  8127. // restart only if no bots are speaking, and if no people are watching replay.
  8128. // But still restart even when people are watching replay, if they've been delaying restart for over 10 seconds; we don't want a bug where someone can indefinitely delay a round by watching replays over and over
  8129. float flMaxRoundDelayDueToReplay = spec_replay_round_delay.GetFloat();
  8130. if ( !botSpeaking && ( gpGlobals->curtime > m_flRestartRoundTime + flMaxRoundDelayDueToReplay || !engine->AnyClientsInHltvReplayMode() ) )
  8131. {
  8132. m_bHasTriggeredRoundStartMusic = false;
  8133. m_bHasTriggeredCoopSpawnReset = false;
  8134. // Don't call RoundEnd() before the first round of a match
  8135. if (m_match.GetRoundsPlayed() > 0)
  8136. {
  8137. if (IsWarmupPeriod() &&
  8138. (GetWarmupPeriodEndTime() <= gpGlobals->curtime) &&
  8139. UTIL_HumansInGame(false, true))
  8140. {
  8141. m_bCompleteReset = true;
  8142. m_flRestartRoundTime = gpGlobals->curtime + 1;
  8143. mp_restartgame.SetValue(5);
  8144. m_bWarmupPeriod = false;
  8145. return;
  8146. }
  8147. else
  8148. {
  8149. // Perform round-related processing at the official end of round
  8150. RoundEnd();
  8151. }
  8152. }
  8153. if (IsPlayingGunGameTRBomb() && m_bGameRestart)
  8154. {
  8155. m_bGameRestart = false;
  8156. // Reset all the gun game demolition mode data
  8157. ClearGunGameData();
  8158. // Destroy primary and secondary weapons, as well as grenades
  8159. for (int i = 1; i <= gpGlobals->maxClients; i++)
  8160. {
  8161. CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex(i);
  8162. if (pPlayer)
  8163. {
  8164. pPlayer->DestroyWeapons(false);
  8165. pPlayer->GiveDefaultItems();
  8166. }
  8167. }
  8168. }
  8169. if ( IsPlayingCoopMission() )
  8170. {
  8171. // Destroy primary and secondary weapons, as well as grenades
  8172. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  8173. {
  8174. CCSPlayer *pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i );
  8175. if ( pPlayer )
  8176. {
  8177. pPlayer->DestroyWeapons( true );
  8178. pPlayer->GiveDefaultItems();
  8179. pPlayer->ResetAccount();
  8180. pPlayer->AddAccount( mp_startmoney.GetInt(), false, false, NULL );
  8181. }
  8182. }
  8183. }
  8184. IGameEvent * leaderboardEvent = gameeventmanager->CreateEvent("round_end_upload_stats");
  8185. if (leaderboardEvent)
  8186. {
  8187. gameeventmanager->FireEvent(leaderboardEvent);
  8188. }
  8189. // Save the round data information one more time when the round officially ends (overwriting the previously saved file)
  8190. if ( ( ( m_iRoundWinStatus == WINNER_TER ) || ( m_iRoundWinStatus == WINNER_CT ) )
  8191. && Helper_mp_backup_round_IsEnabled() )
  8192. {
  8193. SaveRoundDataInformation( mp_backup_round_file_last.GetString() );
  8194. }
  8195. else
  8196. {
  8197. mp_backup_round_file_last.SetValue( "" );
  8198. }
  8199. // Perform round-related processing at the point when the next round is beginning
  8200. RestartRound();
  8201. }
  8202. }
  8203. }
  8204. if ( gpGlobals->curtime > m_tmNextPeriodicThink )
  8205. {
  8206. CheckRestartRound();
  8207. CheckRespawnWaves();
  8208. m_tmNextPeriodicThink = gpGlobals->curtime + 1.0;
  8209. }
  8210. if ( IsPlayingGunGameProgressive() )
  8211. {
  8212. // Perform any queued team moves
  8213. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
  8214. {
  8215. CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex );
  8216. if ( pPlayer )
  8217. {
  8218. if ( ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR ) &&
  8219. ( pPlayer->GetTeamNumber() != TEAM_UNASSIGNED ) &&
  8220. ( pPlayer->GetPendingTeamNumber() != pPlayer->GetTeamNumber() ) )
  8221. {
  8222. pPlayer->HandleCommand_JoinTeam( pPlayer->GetPendingTeamNumber() ) ;
  8223. }
  8224. }
  8225. }
  8226. }
  8227. if ( IsPlayingCoopGuardian() )
  8228. {
  8229. if ( m_bBombPlanted )
  8230. {
  8231. //AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB );
  8232. //TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  8233. }
  8234. else if ( m_flCoopRespawnAndHealTime > -1 && m_flCoopRespawnAndHealTime <= gpGlobals->curtime )
  8235. {
  8236. // HACK
  8237. int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  8238. //int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST;
  8239. // give health to all of the players on the other team
  8240. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  8241. {
  8242. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) );
  8243. if ( pPlayer && pPlayer->GetTeamNumber() == nTeam )
  8244. {
  8245. if ( pPlayer->IsAlive() )
  8246. {
  8247. pPlayer->GiveHealthAndArmorForGuardianMode( true );
  8248. }
  8249. else
  8250. {
  8251. // respawn any dead CTs as well
  8252. if ( pPlayer->IsAlive() == false )
  8253. {
  8254. pPlayer->State_Transition( STATE_GUNGAME_RESPAWN );
  8255. }
  8256. }
  8257. }
  8258. }
  8259. m_flCoopRespawnAndHealTime = -1;
  8260. }
  8261. }
  8262. // if ( IsPlayingCoopMission() )
  8263. // {
  8264. // if ( m_flCoopRespawnAndHealTime > -1 && m_flCoopRespawnAndHealTime <= gpGlobals->curtime )
  8265. // {
  8266. // int nTeam = TEAM_CT;
  8267. // // give health to all of the players on the other team
  8268. // for ( int j = 1; j <= MAX_PLAYERS; j++ )
  8269. // {
  8270. // CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) );
  8271. // if ( pPlayer && pPlayer->GetTeamNumber() == nTeam )
  8272. // {
  8273. // // respawn any dead CTs as well
  8274. // if ( pPlayer->IsAlive() == false )
  8275. // {
  8276. //
  8277. // pPlayer->State_Transition( STATE_GUNGAME_RESPAWN );
  8278. // }
  8279. // }
  8280. // }
  8281. //
  8282. // m_flCoopRespawnAndHealTime = -1;
  8283. // }
  8284. // }
  8285. if (IsPlayingGunGameDeathmatch())
  8286. {
  8287. if ( m_flDMBonusStartTime != -1 && gpGlobals->curtime >= m_flDMBonusStartTime )
  8288. {
  8289. if ( m_bDMBonusActive && gpGlobals->curtime > (m_flDMBonusStartTime + m_flDMBonusTimeLength) )
  8290. {
  8291. // bonus time ended.....
  8292. m_bDMBonusActive = false;
  8293. m_unDMBonusWeaponLoadoutSlot = PickRandomWeaponForDMBonus();
  8294. // pick the new one if we have enough time in the round
  8295. if ( GetRoundRemainingTime() > (mp_dm_time_between_bonus_max.GetFloat() + mp_dm_bonus_length_max.GetFloat()) )
  8296. {
  8297. m_flDMBonusStartTime = gpGlobals->curtime + random->RandomFloat( mp_dm_time_between_bonus_min.GetFloat(), mp_dm_time_between_bonus_max.GetFloat() );
  8298. m_flDMBonusTimeLength = random->RandomFloat( mp_dm_bonus_length_min.GetFloat(), mp_dm_bonus_length_max.GetFloat() );
  8299. }
  8300. else if ( GetRoundRemainingTime() > (mp_dm_time_between_bonus_min.GetFloat() + mp_dm_bonus_length_min.GetFloat()) )
  8301. {
  8302. m_flDMBonusTimeLength = mp_dm_bonus_length_min.GetFloat();
  8303. m_flDMBonusStartTime = gpGlobals->curtime + (GetRoundRemainingTime() - m_flDMBonusTimeLength);
  8304. }
  8305. else
  8306. {
  8307. // we're not going to hit a bonus, just disable it
  8308. m_flDMBonusStartTime = -1;
  8309. }
  8310. }
  8311. // BONUS TIME!!!!
  8312. else if ( !m_bDMBonusActive && m_flDMBonusStartTime > 0 )
  8313. {
  8314. m_bDMBonusActive = true;
  8315. // reset bonus dm suicide limiter.
  8316. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  8317. {
  8318. CCSPlayer *pPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ));
  8319. if( pPlayer )
  8320. pPlayer->m_bHasUsedDMBonusRespawn = false;
  8321. }
  8322. // CSWeaponID wepID = WEAPON_NONE;
  8323. //
  8324. // for ( int i = 0; i < GetItemSchema()->GetItemDefinitionCount(); i++ )
  8325. // {
  8326. // CCStrike15ItemDefinition *pItemDef = ( CCStrike15ItemDefinition * )GetItemSchema()->GetItemDefinitionByMapIndex( i );
  8327. //
  8328. // if ( pItemDef->GetDefaultLoadoutSlot() == m_unDMBonusWeaponLoadoutSlot )
  8329. // {
  8330. // wepID = WeaponIdFromString( pItemDef->GetDefinitionName() );
  8331. // break;
  8332. // }
  8333. // }
  8334. //
  8335. // const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo(wepID);
  8336. // const char *szWeaponName = pWeaponInfo ? pWeaponInfo->szPrintName : "";
  8337. //
  8338. // char szSec[32];
  8339. // Q_snprintf(szSec, sizeof(szSec), "%d", (int)m_flDMBonusTimeLength);
  8340. //
  8341. // CReliableBroadcastRecipientFilter filter;
  8342. // UTIL_ClientPrintFilter(filter, HUD_PRINTTALK, "#SFUI_Notice_DM_BonusWeaponText", szSec, szWeaponName);
  8343. IGameEvent *bonusWeaponEvent = gameeventmanager->CreateEvent( "dm_bonus_weapon_start" );
  8344. if( bonusWeaponEvent )
  8345. {
  8346. bonusWeaponEvent->SetInt("time", (int)m_flDMBonusTimeLength);
  8347. // bonusWeaponEvent->SetInt("wepID", wepID);
  8348. bonusWeaponEvent->SetInt("Pos", m_unDMBonusWeaponLoadoutSlot );
  8349. gameeventmanager->FireEvent(bonusWeaponEvent);
  8350. }
  8351. }
  8352. }
  8353. }
  8354. m_flLastThinkTime = gpGlobals->curtime;
  8355. }
  8356. // The bots do their processing after physics simulation etc so their visibility checks don't recompute
  8357. // bone positions multiple times a frame.
  8358. void CCSGameRules::EndGameFrame( void )
  8359. {
  8360. TheBots->StartFrame();
  8361. BaseClass::EndGameFrame();
  8362. }
  8363. void ServerThinkReplayUploader()
  8364. {
  8365. }
  8366. bool CCSGameRules::CheckGameOver()
  8367. {
  8368. if ( g_fGameOver ) // someone else quit the game already
  8369. {
  8370. // [Forrest] Calling ChangeLevel multiple times was causing IncrementMapCycleIndex
  8371. // to skip over maps in the list. Avoid this using a technique from CTeamplayRoundBasedRules::Think.
  8372. //
  8373. // In queue matchmaking mode we let users vote for rematch
  8374. //
  8375. if ( IsQueuedMatchmaking() )
  8376. {
  8377. extern ConVar mp_match_restart_delay;
  8378. switch ( m_eQueuedMatchmakingRematchState )
  8379. {
  8380. case k_EQueuedMatchmakingRematchState_MatchInProgress:
  8381. // give the clients several seconds for the scoreboard to be displayed
  8382. if ( gpGlobals->curtime < m_flIntermissionStartTime + MAX( mp_match_restart_delay.GetInt(), ( m_flCMMItemDropRevealStartTime - m_flIntermissionStartTime + (GetCMMItemDropRevealDuration() + 4.0f) ) ) )
  8383. return true;
  8384. // else fallthrough
  8385. case k_EQueuedMatchmakingRematchState_VoteStarting:
  8386. {
  8387. #if VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED
  8388. char const *szCannotRematchExplanation = "#SFUI_vote_failed_rematch_explain";
  8389. static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL );
  8390. if ( m_pQueuedMatchmakingReportedRoundStats )
  8391. {
  8392. uint32 numHumansPresent = 0;
  8393. for ( int k = 0; k < m_pQueuedMatchmakingReportedRoundStats->pings().size(); ++ k )
  8394. {
  8395. if ( m_pQueuedMatchmakingReportedRoundStats->pings(k) > 0 )
  8396. ++ numHumansPresent;
  8397. }
  8398. if ( numHumansPresent == m_numQueuedMatchmakingAccounts )
  8399. szCannotRematchExplanation = NULL;
  8400. }
  8401. szCannotRematchExplanation = "#SFUI_vote_failed_rematch_mmsvr";
  8402. if ( !s_pchTournamentServer && !szCannotRematchExplanation )
  8403. {
  8404. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteStarting;
  8405. engine->ServerCommand( "callvote rematch;\n" );
  8406. }
  8407. else
  8408. {
  8409. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed;
  8410. if ( !s_pchTournamentServer )
  8411. {
  8412. // Send chat message to let players know why match cannot proceed
  8413. CRecipientFilter filter;
  8414. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  8415. {
  8416. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  8417. if ( pPlayer && ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR || pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST ) )
  8418. {
  8419. filter.AddRecipient( pPlayer );
  8420. }
  8421. }
  8422. filter.MakeReliable();
  8423. // CTs have surrendered
  8424. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, szCannotRematchExplanation );
  8425. }
  8426. }
  8427. #else // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED
  8428. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed;
  8429. #endif // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED
  8430. }
  8431. return true;
  8432. case k_EQueuedMatchmakingRematchState_VoteToRematchInProgress:
  8433. // waiting for the vote outcome
  8434. return true;
  8435. case k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender:
  8436. case k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender:
  8437. case k_EQueuedMatchmakingRematchState_VoteToRematch_Aborted:
  8438. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed;
  8439. // fall through:
  8440. case k_EQueuedMatchmakingRematchState_VoteToRematchFailed:
  8441. // This condition will then cause all people to disconnect
  8442. if ( m_flIntermissionStartTime + MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() ) < gpGlobals->curtime + mp_competitive_endofmatch_extra_time.GetFloat() )
  8443. m_flIntermissionStartTime = ( gpGlobals->curtime + mp_competitive_endofmatch_extra_time.GetFloat() ) - MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() );
  8444. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done;
  8445. {
  8446. CReliableBroadcastRecipientFilter filter;
  8447. CCSUsrMsg_ServerRankRevealAll msg;
  8448. static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL );
  8449. if ( s_pchTournamentServer )
  8450. msg.set_seconds_till_shutdown( MAX( 0, mp_competitive_endofmatch_extra_time.GetInt() ) );
  8451. SendUserMessage( filter, CS_UM_ServerRankRevealAll, msg );
  8452. }
  8453. break;
  8454. case k_EQueuedMatchmakingRematchState_VoteToRematchSucceeded:
  8455. #if VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED
  8456. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematch_Done;
  8457. #else // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED
  8458. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed;
  8459. #endif // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED
  8460. break; // let the normal logic proceed and change level
  8461. case k_EQueuedMatchmakingRematchState_VoteToRematch_Done:
  8462. break; // let the normal logic proceed and change level
  8463. case k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done:
  8464. if ( IsQueuedMatchmaking() && ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done )
  8465. && m_flIntermissionStartTime && ( m_flIntermissionStartTime + MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() ) < gpGlobals->curtime ) )
  8466. {
  8467. // Tell all clients to return to the lobby
  8468. CReliableBroadcastRecipientFilter filter;
  8469. CCSUsrMsg_DisconnectToLobby msg;
  8470. SendUserMessage( filter, CS_UM_DisconnectToLobby, msg );
  8471. }
  8472. break; // let the normal logic proceed and change level
  8473. }
  8474. }
  8475. // if we're in a "season" map group, let players vote to pick the next map at the end of the match
  8476. if ( IsEndMatchVotingForNextMap() )
  8477. {
  8478. extern ConVar mp_match_restart_delay;
  8479. switch ( m_eEndMatchMapVoteState )
  8480. {
  8481. case k_EEndMatchMapVoteState_MatchInProgress:
  8482. // give the clients several seconds for the scoreboard to be displayed
  8483. if ( gpGlobals->curtime < m_flIntermissionStartTime + 5.0f )
  8484. return true;
  8485. // else fallthrough
  8486. case k_EEndMatchMapVoteState_VoteInProgress:
  8487. {
  8488. // waiting for the vote outcome
  8489. CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource );
  8490. if ( pResource && pResource->EndMatchNextMapAllVoted() )
  8491. {
  8492. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_AllPlayersVoted;
  8493. }
  8494. if ( (m_flIntermissionStartTime + GetIntermissionDuration()) < gpGlobals->curtime )
  8495. {
  8496. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteTimeEnded;
  8497. }
  8498. return true;
  8499. }
  8500. case k_EEndMatchMapVoteState_VoteTimeEnded:
  8501. case k_EEndMatchMapVoteState_AllPlayersVoted:
  8502. {
  8503. // first find out if we have a tie in the vote
  8504. // this is done in three different places
  8505. // TODO: make a function out of this
  8506. typedef int NumberOfVotes_t;
  8507. typedef int MapInCollection_t;
  8508. CUtlMap< MapInCollection_t, NumberOfVotes_t > mapMaps2Votes;
  8509. mapMaps2Votes.SetLessFunc( DefLessFunc( int ) );
  8510. // ask all players what they voted for and store it
  8511. // someone voted for a map in the mapgroup, see if we've recorded a vote for this map yet
  8512. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  8513. {
  8514. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  8515. #if 0 // #ifdef _DEBUG // cause everybody even bots that didn't vote to random vote in debug
  8516. if ( pPlayer )
  8517. {
  8518. int nVoteNum = pPlayer->GetEndMatchNextMapVote();
  8519. if ( nVoteNum < 0 )
  8520. nVoteNum = RandomInt( 0, MAX_ENDMATCH_VOTE_PANELS - 1 );
  8521. #else
  8522. if ( pPlayer && !pPlayer->IsBot() && ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR ) )
  8523. {
  8524. // players store the keybind list index that displays on the client
  8525. int nVoteNum = pPlayer->GetEndMatchNextMapVote();
  8526. #endif
  8527. if ( (nVoteNum < 0) || (nVoteNum >= MAX_ENDMATCH_VOTE_PANELS) || (m_nEndMatchMapGroupVoteOptions[nVoteNum] < 0) )
  8528. {
  8529. if ( mp_verbose_changelevel_spew.GetInt() >= 2 )
  8530. {
  8531. Msg( "NEXTLEVELVOTE: player#%d (%s) voted %d which is ignored\n", pPlayer->entindex(), pPlayer->GetPlayerName(), nVoteNum );
  8532. }
  8533. continue;
  8534. }
  8535. // convert the player vote to the map index
  8536. int nDesiredMapIndex = m_nEndMatchMapGroupVoteOptions[nVoteNum];
  8537. if ( nDesiredMapIndex < 0 )
  8538. continue;
  8539. if ( mp_verbose_changelevel_spew.GetInt() >= 2 )
  8540. {
  8541. Msg( "NEXTLEVELVOTE: player#%d (%s) voted %d (map=%d)\n", pPlayer->entindex(), pPlayer->GetPlayerName(), nVoteNum, nDesiredMapIndex );
  8542. }
  8543. // store the number of votes sorted by the map index
  8544. // someone voted for a map in the mapgroup, see if we've recorded a vote for this map yet
  8545. CUtlMap< MapInCollection_t, NumberOfVotes_t >::IndexType_t iMapEntry = mapMaps2Votes.Find( nDesiredMapIndex );
  8546. if ( iMapEntry == mapMaps2Votes.InvalidIndex() )
  8547. mapMaps2Votes.Insert( nDesiredMapIndex, 1 ); // we don't have a vote for this map, so insert one
  8548. else
  8549. mapMaps2Votes.Element( iMapEntry ) ++; // someone voted for this map already so increment it by one
  8550. }
  8551. }
  8552. // now takes the votes and do this so we can sort them my the total number of votes for each map in ascending order
  8553. CUtlMap< NumberOfVotes_t, MapInCollection_t > mapVotesInOrder;
  8554. mapVotesInOrder.SetLessFunc( DefLessFunc( int ) );
  8555. FOR_EACH_MAP( mapMaps2Votes, itMap2Vote )
  8556. {
  8557. mapVotesInOrder.Insert( mapMaps2Votes.Element( itMap2Vote ), mapMaps2Votes.Key( itMap2Vote ) );
  8558. if ( mp_verbose_changelevel_spew.GetInt() >= 2 )
  8559. {
  8560. Msg( "NEXTLEVELVOTE: Total %d votes for map %d\n", mapMaps2Votes.Element( itMap2Vote ), mapMaps2Votes.Key( itMap2Vote ) );
  8561. }
  8562. }
  8563. // clear the tie votes in our global list
  8564. m_nEndMatchTiedVotes.RemoveAll();
  8565. for ( CUtlMap< NumberOfVotes_t, MapInCollection_t >::IndexType_t idxVotesInOrder = mapVotesInOrder.LastInorder();
  8566. idxVotesInOrder != mapVotesInOrder.InvalidIndex(); idxVotesInOrder = mapVotesInOrder.PrevInorder( idxVotesInOrder ) )
  8567. {
  8568. // compare the first to the last in the list and add any to it that matches because we have a tie
  8569. if ( mapVotesInOrder.Key( idxVotesInOrder ) == mapVotesInOrder.Key( mapVotesInOrder.LastInorder() ) )
  8570. {
  8571. m_nEndMatchTiedVotes.AddToTail( mapVotesInOrder.Element( idxVotesInOrder ) );
  8572. if ( mp_verbose_changelevel_spew.GetInt() >= 2 )
  8573. {
  8574. Msg( "NEXTLEVELVOTE: Considering best map %d with %d votes\n", mapVotesInOrder.Element( idxVotesInOrder ), mapVotesInOrder.Key( idxVotesInOrder ) );
  8575. }
  8576. }
  8577. else
  8578. break;
  8579. }
  8580. m_nEndMatchMapVoteWinner = -1;
  8581. // if we don't have any ties at all, it means no one voted, so add all of the maps to the "tie" list
  8582. if ( !m_nEndMatchTiedVotes.Count() )
  8583. {
  8584. for ( int iVoteOption = 0; iVoteOption < MAX_ENDMATCH_VOTE_PANELS; ++ iVoteOption )
  8585. {
  8586. if ( m_nEndMatchMapGroupVoteOptions[iVoteOption] >= 0 )
  8587. {
  8588. m_nEndMatchTiedVotes.AddToTail( m_nEndMatchMapGroupVoteOptions[iVoteOption] );
  8589. if ( mp_verbose_changelevel_spew.GetInt() >= 2 )
  8590. {
  8591. Msg( "NEXTLEVELVOTE: No votes considering map %d\n", m_nEndMatchMapGroupVoteOptions[iVoteOption] );
  8592. }
  8593. }
  8594. }
  8595. }
  8596. // if there is only one in the list, it means at least one player votes, but one of the maps is a clear winner
  8597. if ( m_nEndMatchTiedVotes.Count() == 1 )
  8598. {
  8599. m_nEndMatchMapVoteWinner = m_nEndMatchTiedVotes.Head();
  8600. if ( mp_verbose_changelevel_spew.GetInt() >= 2 )
  8601. {
  8602. Msg( "NEXTLEVELVOTE: Absolute winner map %d\n", m_nEndMatchMapVoteWinner );
  8603. }
  8604. }
  8605. if ( m_nEndMatchMapVoteWinner == -1 )
  8606. {
  8607. // if we have a tie, goto SelectingWinner and tell the client to do something
  8608. m_flIntermissionStartTime = MAX( m_flIntermissionStartTime, ( (gpGlobals->curtime + 4.0f) - (GetIntermissionDuration()) ) );
  8609. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_SelectingWinner;
  8610. IGameEvent * event = gameeventmanager->CreateEvent( "endmatch_mapvote_selecting_map" );
  8611. if( event )
  8612. {
  8613. // we want to send all of the "ties" to the client. We send ten because technically we could have a 10 person server with each player voting for different maps
  8614. event->SetInt( "count", m_nEndMatchTiedVotes.Count() );
  8615. char sz[16];
  8616. for ( int i = 0 ; i < m_nEndMatchTiedVotes.Count() ; ++i)
  8617. {
  8618. V_sprintf_safe( sz, "slot%d", i+1 );
  8619. // since we store the index in the map group, we need to translate that to the index in the voting panel for the client before we send it down
  8620. for ( int j = 0 ; j < MAX_ENDMATCH_VOTE_PANELS ; ++j)
  8621. {
  8622. if ( m_nEndMatchTiedVotes[i] == m_nEndMatchMapGroupVoteOptions[j] )
  8623. {
  8624. event->SetInt( sz, j );
  8625. break;
  8626. }
  8627. }
  8628. }
  8629. gameeventmanager->FireEvent( event );
  8630. }
  8631. }
  8632. else
  8633. {
  8634. // else just pick the winner and move on
  8635. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_SettingNextLevel;
  8636. }
  8637. }
  8638. return true;
  8639. case k_EEndMatchMapVoteState_SelectingWinner:
  8640. {
  8641. if ( m_flIntermissionStartTime + GetIntermissionDuration() < gpGlobals->curtime )
  8642. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_SettingNextLevel;
  8643. }
  8644. return true;
  8645. case k_EEndMatchMapVoteState_SettingNextLevel:
  8646. {
  8647. m_flIntermissionStartTime = MAX( m_flIntermissionStartTime, ( (gpGlobals->curtime + 5.0f) - GetIntermissionDuration() ) );
  8648. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteAllDone;
  8649. if ( g_pGameTypes )
  8650. {
  8651. const char* mapGroupName = gpGlobals->mapGroupName.ToCStr();
  8652. const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName );
  8653. if ( mapsInGroup )
  8654. {
  8655. int nNumMaps = mapsInGroup->Count();
  8656. // we don't have a clear winner
  8657. if ( m_nEndMatchMapVoteWinner == -1 )
  8658. {
  8659. // if we have a tie for first place, randomly pick between the ones in the lead
  8660. m_nEndMatchMapVoteWinner = m_nEndMatchTiedVotes[ RandomInt( 0, (m_nEndMatchTiedVotes.Count()-1) ) ];
  8661. if ( mp_verbose_changelevel_spew.GetInt() >= 2 )
  8662. {
  8663. Msg( "NEXTLEVELVOTE: Randomly picked winner map=%d from %d tied for nextlevel\n", m_nEndMatchMapVoteWinner, m_nEndMatchTiedVotes.Count() );
  8664. }
  8665. }
  8666. if ( m_nEndMatchMapVoteWinner < 0 || m_nEndMatchMapVoteWinner >= nNumMaps )
  8667. return true;
  8668. const char* internalMapName = (*mapsInGroup)[m_nEndMatchMapVoteWinner];
  8669. if ( internalMapName && V_strcmp( "undefined", internalMapName ) != 0 && V_strlen( internalMapName ) > 0 )
  8670. {
  8671. engine->ServerCommand( CFmtStr( "nextlevel %s;", internalMapName ) );
  8672. extern ConVar nextmap_print_enabled;
  8673. if ( nextmap_print_enabled.GetBool() )
  8674. {
  8675. UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_nextmap", V_GetFileName( internalMapName ) );
  8676. }
  8677. }
  8678. }
  8679. }
  8680. }
  8681. return true;
  8682. case k_EEndMatchMapVoteState_VoteAllDone:
  8683. {
  8684. }
  8685. break;
  8686. }
  8687. }
  8688. // check to see if we should change levels now
  8689. if ( m_flIntermissionStartTime && ( m_flIntermissionStartTime + GetIntermissionDuration() < gpGlobals->curtime ) )
  8690. {
  8691. IGameEvent * leaderboardEvent = gameeventmanager->CreateEvent( "round_end_upload_stats" );
  8692. if( leaderboardEvent )
  8693. {
  8694. gameeventmanager->FireEvent( leaderboardEvent );
  8695. }
  8696. if ( !IsPlayingTraining() )
  8697. {
  8698. // Perform round-related processing at the official end of round
  8699. RoundEnd();
  8700. }
  8701. // get the next map name
  8702. char szNextMap[MAX_PATH];
  8703. if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
  8704. {
  8705. if ( mp_verbose_changelevel_spew.GetBool() )
  8706. {
  8707. Msg( "CHANGELEVEL: ConVar '%s' is set, next map will be '%s'\n", nextlevel.GetName(), nextlevel.GetString() );
  8708. }
  8709. Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
  8710. }
  8711. else
  8712. {
  8713. GetNextLevelName( szNextMap, sizeof(szNextMap) );
  8714. }
  8715. // intermission is over
  8716. // check to see if we should restart or change maps
  8717. // mp_match_end_restart trumps everything and just restarts the current map regardless of next map
  8718. // mp_match_end_changelevel will force a changelevel even if next map matches current map
  8719. // otherwise if the next map is the same one we are on, don't reload it, just restart the map instead
  8720. // if the next map is different then changelevel
  8721. bool bNextLevelDiffers = !!Q_strcmp( szNextMap, STRING( gpGlobals->mapname ) );
  8722. bool bWantsChangeLevel = !mp_match_end_restart.GetBool() && ( mp_match_end_changelevel.GetBool() || bNextLevelDiffers );
  8723. if ( bWantsChangeLevel == false && mp_verbose_changelevel_spew.GetBool() )
  8724. {
  8725. Msg( "CHANGELEVEL: Not changing level, %s is false, %s is %s %s\n", mp_match_end_restart.GetName(), mp_match_end_changelevel.GetName(), mp_match_end_changelevel.GetBool() ? "true" : "false", !bNextLevelDiffers ? "and next is the same" : "" );
  8726. }
  8727. #if defined GAME_DLL
  8728. if ( engine->IsDedicatedServer() && DedicatedServerWorkshop().CurrentLevelNeedsUpdate() )
  8729. {
  8730. bWantsChangeLevel = true; // if current level is out of date, we need to change level no matter the settings.
  8731. }
  8732. #endif
  8733. if ( IsQueuedMatchmaking() && ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done ) )
  8734. {
  8735. // Tell all clients to return to the lobby
  8736. CReliableBroadcastRecipientFilter filter;
  8737. CCSUsrMsg_DisconnectToLobby msg;
  8738. SendUserMessage( filter, CS_UM_DisconnectToLobby, msg );
  8739. }
  8740. else if ( bWantsChangeLevel )
  8741. {
  8742. ChangeLevel();
  8743. }
  8744. else
  8745. {
  8746. //Sometimes we don't want the new match the allow players to pick teams, such as in the case of a vote to scramble or swap teams.
  8747. if ( m_bPickNewTeamsOnReset )
  8748. {
  8749. // Clear the players to unassigned teams.
  8750. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
  8751. {
  8752. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( clientIndex );
  8753. if(pPlayer)
  8754. {
  8755. pPlayer->ChangeTeam( TEAM_UNASSIGNED );
  8756. }
  8757. }
  8758. }
  8759. // Restart the round with a complete reset that will clear all the match status such as team scores.
  8760. m_bCompleteReset = true;
  8761. // clear the next level
  8762. nextlevel.SetValue( "" );
  8763. RestartRound();
  8764. g_fGameOver = false;
  8765. // Send an event that clients can key off of to update their UI state.
  8766. IGameEvent *restartEvent = gameeventmanager->CreateEvent( "cs_match_end_restart" );
  8767. if( m_bPickNewTeamsOnReset && restartEvent )
  8768. {
  8769. gameeventmanager->FireEvent( restartEvent );
  8770. }
  8771. m_bPickNewTeamsOnReset = true;
  8772. #if defined ( GAME_DLL )
  8773. // If we're taking this path, tell the server ugc manager to check for updates now and force a real change level should we need one.
  8774. if ( engine->IsDedicatedServer() )
  8775. {
  8776. DedicatedServerWorkshop().CheckIfCurrentLevelNeedsUpdate();
  8777. }
  8778. #endif
  8779. }
  8780. // Don't run this code again
  8781. m_flIntermissionStartTime = 0.f;
  8782. }
  8783. return true;
  8784. }
  8785. return false;
  8786. }
  8787. bool CCSGameRules::CheckFragLimit()
  8788. {
  8789. if ( fraglimit.GetInt() <= 0 )
  8790. return false;
  8791. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  8792. {
  8793. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  8794. if ( pPlayer && pPlayer->FragCount() >= fraglimit.GetInt() )
  8795. {
  8796. const char *teamName = "UNKNOWN";
  8797. if ( pPlayer->GetTeam() )
  8798. {
  8799. teamName = pPlayer->GetTeam()->GetName();
  8800. }
  8801. UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"Intermission_Kill_Limit\"\n",
  8802. pPlayer->GetPlayerName(),
  8803. pPlayer->GetUserID(),
  8804. pPlayer->GetNetworkIDString(),
  8805. teamName
  8806. );
  8807. GoToIntermission();
  8808. return true;
  8809. }
  8810. }
  8811. return false;
  8812. }
  8813. void CCSGameRules::CheckFreezePeriodExpired()
  8814. {
  8815. float startTime = m_fRoundStartTime;
  8816. if ( !IsFinite( startTime ) )
  8817. {
  8818. Warning( "Infinite round start time!\n" );
  8819. m_fRoundStartTime.GetForModify() = gpGlobals->curtime;
  8820. }
  8821. float flStartTime = m_fRoundStartTime;
  8822. if( IsFinite( startTime ) && ( gpGlobals->curtime < flStartTime ) )
  8823. {
  8824. if ( CSGameRules() && CSGameRules()->IsMatchWaitingForResume() )
  8825. {
  8826. m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime;
  8827. }
  8828. // TIMEOUTS
  8829. if ( m_bTerroristTimeOutActive )
  8830. {
  8831. m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime;
  8832. m_flTerroristTimeOutRemaining -= ( gpGlobals->curtime - m_flLastThinkTime );
  8833. if ( m_flTerroristTimeOutRemaining <= 0 )
  8834. {
  8835. EndTerroristTimeOut();
  8836. }
  8837. }
  8838. else if ( m_bCTTimeOutActive )
  8839. {
  8840. m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime;
  8841. m_flCTTimeOutRemaining -= ( gpGlobals->curtime - m_flLastThinkTime );
  8842. if ( m_flCTTimeOutRemaining <= 0 )
  8843. {
  8844. EndCTTimeOut();
  8845. }
  8846. }
  8847. #ifndef CLIENT_DLL
  8848. else
  8849. {
  8850. int nTimeToStart = (int) ((flStartTime - gpGlobals->curtime) + 1.0);
  8851. if( nTimeToStart <= 3 && m_nLastFreezeEndBeep != nTimeToStart )
  8852. {
  8853. m_nLastFreezeEndBeep = nTimeToStart;
  8854. IGameEvent *pRoundStartBeepEvent = gameeventmanager->CreateEvent( "cs_round_start_beep" );
  8855. if( pRoundStartBeepEvent )
  8856. {
  8857. gameeventmanager->FireEvent( pRoundStartBeepEvent );
  8858. }
  8859. }
  8860. }
  8861. #endif
  8862. return; // not time yet to start round
  8863. }
  8864. #ifndef CLIENT_DLL
  8865. IGameEvent *pRoundStartBeepEvent = gameeventmanager->CreateEvent( "cs_round_final_beep" );
  8866. if( pRoundStartBeepEvent )
  8867. {
  8868. gameeventmanager->FireEvent( pRoundStartBeepEvent );
  8869. }
  8870. // Mark all alive players as receiving money, they could join/reconnect at freezetime
  8871. for ( int iAlivePlayer = 1; iAlivePlayer <= MAX_PLAYERS; ++ iAlivePlayer )
  8872. {
  8873. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iAlivePlayer ) );
  8874. if ( pPlayer ) // mark all players to have all cash available
  8875. pPlayer->m_iAccountMoneyEarnedForNextRound = 0;
  8876. if ( pPlayer && pPlayer->IsConnected() && !pPlayer->IsBot() && pPlayer->IsAlive() && !pPlayer->IsControllingBot() )
  8877. {
  8878. switch ( pPlayer->GetTeamNumber() )
  8879. {
  8880. case TEAM_TERRORIST:
  8881. case TEAM_CT:
  8882. pPlayer->MarkAsNotReceivingMoneyNextRound( true ); // allow money next round
  8883. break;
  8884. }
  8885. }
  8886. }
  8887. #endif
  8888. // Log this information
  8889. UTIL_LogPrintf("World triggered \"Round_Start\"\n");
  8890. char CT_sentence[40];
  8891. char T_sentence[40];
  8892. switch ( random->RandomInt( 0, 3 ) )
  8893. {
  8894. case 0:
  8895. Q_strncpy(CT_sentence,"radio.moveout", sizeof( CT_sentence ) );
  8896. Q_strncpy(T_sentence ,"radio.moveout", sizeof( T_sentence ) );
  8897. break;
  8898. case 1:
  8899. Q_strncpy(CT_sentence, "radio.letsgo", sizeof( CT_sentence ) );
  8900. Q_strncpy(T_sentence , "radio.letsgo", sizeof( T_sentence ) );
  8901. break;
  8902. case 2:
  8903. Q_strncpy(CT_sentence , "radio.locknload", sizeof( CT_sentence ) );
  8904. Q_strncpy(T_sentence , "radio.locknload", sizeof( T_sentence ) );
  8905. break;
  8906. default:
  8907. Q_strncpy(CT_sentence , "radio.go", sizeof( CT_sentence ) );
  8908. Q_strncpy(T_sentence , "radio.go", sizeof( T_sentence ) );
  8909. break;
  8910. }
  8911. // More specific radio commands for the new scenarios : Prison & Assasination
  8912. if (m_bMapHasEscapeZone == TRUE)
  8913. {
  8914. Q_strncpy(CT_sentence , "radio.elim", sizeof( CT_sentence ) );
  8915. Q_strncpy(T_sentence , "radio.getout", sizeof( T_sentence ) );
  8916. }
  8917. // Freeze period expired: kill the flag
  8918. m_bFreezePeriod = false;
  8919. IGameEvent * event = gameeventmanager->CreateEvent( "round_freeze_end" );
  8920. if ( event )
  8921. {
  8922. gameeventmanager->FireEvent( event );
  8923. }
  8924. if ( m_match.GetRoundsPlayed() == 0 && !IsWarmupPeriod() )
  8925. {
  8926. IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_match_start" );
  8927. if ( event )
  8928. gameeventmanager->FireEvent( event );
  8929. }
  8930. if ( !IsPlayingGunGameTRBomb() || ( IsPlayingGunGameTRBomb() && m_match.GetPhase() != GAMEPHASE_MATCH_ENDED ) )
  8931. {
  8932. // Update the timers for all clients and play a sound
  8933. bool bCTPlayed = false;
  8934. bool bTPlayed = false;
  8935. // Don't play start round VO for terrorists in coop
  8936. if ( IsPlayingCoopMission() )
  8937. bTPlayed = true;
  8938. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  8939. {
  8940. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  8941. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  8942. {
  8943. if ( pPlayer->State_Get() == STATE_ACTIVE )
  8944. {
  8945. if ( (pPlayer->GetTeamNumber() == TEAM_CT) && !bCTPlayed )
  8946. {
  8947. pPlayer->Radio( CT_sentence );
  8948. bCTPlayed = true;
  8949. }
  8950. else if ( (pPlayer->GetTeamNumber() == TEAM_TERRORIST) && !bTPlayed )
  8951. {
  8952. pPlayer->Radio( T_sentence );
  8953. bTPlayed = true;
  8954. }
  8955. pPlayer->UpdateFreezetimeEndEquipmentValue();
  8956. }
  8957. //pPlayer->SyncRoundTimer();
  8958. }
  8959. }
  8960. }
  8961. if ( mp_use_respawn_waves.GetInt() == 2 && IsPlayingCoopGuardian() )
  8962. {
  8963. int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  8964. //int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST;
  8965. // if ( nTeam == TEAM_CT )
  8966. // m_bCTCantBuy = true;
  8967. // else
  8968. // m_bTCantBuy = true;
  8969. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  8970. {
  8971. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  8972. if ( pPlayer && pPlayer->GetTeamNumber() == nTeam && !IsWarmupPeriod() )
  8973. {
  8974. CRecipientFilter filter;
  8975. filter.AddRecipient( pPlayer );
  8976. const char *szTeam = (nTeam == TEAM_CT) ? "CT" : "T";
  8977. char szNotice[512];
  8978. Q_snprintf( szNotice, sizeof( szNotice ), "#SFUI_Notice_NewWaveBegun_%s0", szTeam );
  8979. char szWave[32];
  8980. Q_snprintf( szWave, sizeof( szWave ), "%d", ( int )m_nGuardianModeWaveNumber );
  8981. UTIL_ClientPrintFilter( filter, HUD_PRINTCENTER, szNotice, szWave );
  8982. }
  8983. }
  8984. }
  8985. }
  8986. void CCSGameRules::CheckRoundTimeExpired()
  8987. {
  8988. if ( mp_ignore_round_win_conditions.GetBool() || IsWarmupPeriod() )
  8989. return;
  8990. if ( GetRoundRemainingTime() > 0 || m_iRoundWinStatus != WINNER_NONE )
  8991. return; //We haven't completed other objectives, so go for this!.
  8992. if( !m_bFirstConnected )
  8993. return;
  8994. // New code to get rid of round draws!!
  8995. if ( IsPlayingGunGameDeathmatch() )
  8996. {
  8997. // TODO: make this a shared function so playercount runs the same code
  8998. CCSPlayer *pWinner = NULL;
  8999. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  9000. {
  9001. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  9002. if ( pPlayer )
  9003. {
  9004. if ( pWinner == NULL )
  9005. pWinner = pPlayer;
  9006. if ( pWinner != pPlayer )
  9007. {
  9008. if ( pWinner->GetScore() > pPlayer->GetScore() )
  9009. continue;
  9010. else if ( pWinner->GetScore() < pPlayer->GetScore() )
  9011. pWinner = pPlayer;
  9012. else
  9013. pWinner = (pWinner->entindex() > pPlayer->entindex()) ? pWinner : pPlayer;
  9014. }
  9015. }
  9016. }
  9017. if ( pWinner )
  9018. {
  9019. if ( pWinner->GetTeamNumber() == TEAM_CT )
  9020. TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win );
  9021. else
  9022. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  9023. }
  9024. else
  9025. {
  9026. TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw );
  9027. }
  9028. }
  9029. else if ( IsPlayingCoopMission() )
  9030. {
  9031. // When playing coop mission Terrorists win if timer runs out
  9032. bool bCTsFailedToDeploy = m_coopPlayersInDeploymentZone;
  9033. // However if CTs even failed to deploy in time kick them from the server
  9034. m_match.AddTerroristWins( 1 );
  9035. if ( bCTsFailedToDeploy )
  9036. {
  9037. if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) )
  9038. pTeam->MarkSurrendered();
  9039. CReliableBroadcastRecipientFilter filter;
  9040. UTIL_ClientPrintFilter(filter, HUD_PRINTCENTER, "#SFUIHUD_InfoPanel_Coop_DeployMissionBust" );
  9041. UTIL_ClientPrintAll( HUD_PRINTTALK, "#SFUIHUD_InfoPanel_Coop_DeployMissionBust" );
  9042. }
  9043. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  9044. if ( bCTsFailedToDeploy )
  9045. { // Mission is a bust
  9046. m_match.SetPhase( GAMEPHASE_MATCH_ENDED );
  9047. m_phaseChangeAnnouncementTime = gpGlobals->curtime + 1.5;
  9048. GoToIntermission();
  9049. }
  9050. }
  9051. else if ( mp_default_team_winner_no_objective.GetInt() != -1 )
  9052. {
  9053. int nTeam = mp_default_team_winner_no_objective.GetInt();
  9054. if ( nTeam == TEAM_CT )
  9055. {
  9056. m_match.AddCTWins( 1 );
  9057. if ( IsPlayingCoopGuardian() && !IsHostageRescueMap() )
  9058. {
  9059. if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) )
  9060. pTeam->MarkSurrendered();
  9061. }
  9062. else
  9063. {
  9064. MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound( TEAM_TERRORIST );
  9065. AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB );
  9066. }
  9067. TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win );
  9068. }
  9069. else if ( nTeam == TEAM_TERRORIST )
  9070. {
  9071. m_match.AddTerroristWins( 1 );
  9072. if ( IsPlayingCoopGuardian() && IsHostageRescueMap() )
  9073. {
  9074. if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) )
  9075. pTeam->MarkSurrendered();
  9076. }
  9077. else
  9078. {
  9079. MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound( TEAM_CT );
  9080. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE );
  9081. }
  9082. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
  9083. }
  9084. else
  9085. {
  9086. TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw );
  9087. }
  9088. }
  9089. else if ( m_bMapHasBombTarget )
  9090. {
  9091. //If the bomb is planted, don't let the round timer end the round.
  9092. //keep going until the bomb explodes or is defused
  9093. if( !m_bBombPlanted )
  9094. {
  9095. m_match.AddCTWins( 1 );
  9096. MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_TERRORIST);
  9097. AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB );
  9098. TerminateRound( mp_round_restart_delay.GetFloat(), Target_Saved );
  9099. }
  9100. }
  9101. else if ( m_bMapHasRescueZone )
  9102. {
  9103. MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_CT);
  9104. m_match.AddTerroristWins( 1 );
  9105. AddTeamAccount( TEAM_TERRORIST, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE );
  9106. TerminateRound( mp_round_restart_delay.GetFloat(), Hostages_Not_Rescued );
  9107. }
  9108. else if ( m_bMapHasEscapeZone )
  9109. {
  9110. m_match.AddCTWins( 1 );
  9111. TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Not_Escaped );
  9112. }
  9113. //If there is no scenario-specific winner when the time runs out, just declare a draw
  9114. else
  9115. {
  9116. TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw );
  9117. }
  9118. }
  9119. bool CCSGameRules::ShouldGunGameSpawnBomb( void )
  9120. {
  9121. return ( m_bGunGameRespawnWithBomb && ( m_fGunGameBombRespawnTimer <= gpGlobals->curtime ) );
  9122. }
  9123. void CCSGameRules::SetGunGameSpawnBomb( bool allow )
  9124. {
  9125. m_bGunGameRespawnWithBomb = allow;
  9126. m_fGunGameBombRespawnTimer = gpGlobals->curtime + mp_ggtr_bomb_respawn_delay.GetFloat();
  9127. }
  9128. void CCSGameRules::RewardMatchEndDrops( bool bAbortedMatch )
  9129. {
  9130. }
  9131. void CCSGameRules::GoToIntermission( bool bAbortedMatch )
  9132. {
  9133. Msg( "Going to intermission...\n" );
  9134. bool bAnnounceNextMap = true;
  9135. bool bDoGenericRewardMatchEndDrops = true;
  9136. // Do old style match end drops (community and official non-competitive)
  9137. if ( bDoGenericRewardMatchEndDrops )
  9138. {
  9139. RewardMatchEndDrops( bAbortedMatch );
  9140. }
  9141. else
  9142. {
  9143. ClearItemsDroppedDuringMatch();
  9144. }
  9145. m_bIsDroppingItems = true; // Always wait in case items drop
  9146. // generate the map list that players will be voting on
  9147. CreateEndMatchMapGroupVoteOptions();
  9148. m_match.SetPhase( GAMEPHASE_MATCH_ENDED );
  9149. m_bHasMatchStarted = false;
  9150. IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" );
  9151. if ( winEvent )
  9152. {
  9153. gameeventmanager->FireEvent( winEvent );
  9154. }
  9155. BaseClass::GoToIntermission();
  9156. //
  9157. // Tell everyone what the next map is
  9158. //
  9159. bool bNextMapAlreadySet = false;
  9160. char szNextMap[ MAX_PATH ] = { 0 };
  9161. if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
  9162. {
  9163. bNextMapAlreadySet = true;
  9164. Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
  9165. }
  9166. else
  9167. {
  9168. GetNextLevelName( szNextMap, sizeof( szNextMap ) );
  9169. }
  9170. // if we're running map group, let players vote to pick the next map at the end of the match
  9171. if ( IsEndMatchVotingForNextMap() && !bNextMapAlreadySet && GetCMMItemDropRevealEndTime() < gpGlobals->curtime )
  9172. {
  9173. bAnnounceNextMap = !CheckSetVoteTime();
  9174. }
  9175. else if ( !m_bIsDroppingItems )
  9176. {
  9177. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteAllDone;
  9178. }
  9179. // In queued matchmaking or during a "season" map group, don't message the next map
  9180. if ( IsQueuedMatchmaking() || bAnnounceNextMap == false )
  9181. {
  9182. szNextMap[ 0 ] = 0;
  9183. }
  9184. //Clear various states from all players and freeze them in place
  9185. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  9186. {
  9187. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  9188. if ( pPlayer )
  9189. {
  9190. pPlayer->Unblind();
  9191. pPlayer->AddFlag( FL_FROZEN );
  9192. pPlayer->SetPendingTeamNum( TEAM_UNASSIGNED );
  9193. UpdateMatchStats( pPlayer, m_match.GetWinningTeam() );
  9194. extern ConVar nextmap_print_enabled;
  9195. if ( szNextMap[ 0 ] && nextmap_print_enabled.GetBool() )
  9196. {
  9197. if ( IsGameConsole() || engine->IsDedicatedServerForXbox() || engine->IsDedicatedServerForPS3() )
  9198. {
  9199. // we know all the map names on console so we can safely assume we have a token for all maps
  9200. const int nMaxLength = MAX_MAP_NAME + 10 + 1;
  9201. char szToken[ nMaxLength ];
  9202. CreateFriendlyMapNameToken( szNextMap, szToken, nMaxLength );
  9203. ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szToken );
  9204. }
  9205. else
  9206. {
  9207. ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", V_GetFileName( szNextMap ) );
  9208. }
  9209. }
  9210. }
  9211. }
  9212. // freeze players while in intermission
  9213. m_bFreezePeriod = true;
  9214. // When not queued matchmaking that is driven by rematch endmatch vote machine reveal all ranks now
  9215. if ( !IsQueuedMatchmaking() )
  9216. {
  9217. CReliableBroadcastRecipientFilter filter;
  9218. CCSUsrMsg_ServerRankRevealAll msg;
  9219. SendUserMessage( filter, CS_UM_ServerRankRevealAll, msg );
  9220. }
  9221. }
  9222. bool CCSGameRules::CheckSetVoteTime()
  9223. {
  9224. int numMaps = 0;
  9225. if ( g_pGameTypes )
  9226. {
  9227. const char* mapGroupName = gpGlobals->mapGroupName.ToCStr();
  9228. const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName );
  9229. if ( mapsInGroup )
  9230. numMaps = mapsInGroup->Count();
  9231. }
  9232. if ( numMaps > 1 )
  9233. {
  9234. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteInProgress;
  9235. m_nEndMatchTiedVotes.RemoveAll();
  9236. // artificially set the m_flIntermissionStartTime
  9237. if ( m_flIntermissionStartTime &&
  9238. ( gpGlobals->curtime + mp_endmatch_votenextleveltime.GetFloat() > m_flIntermissionStartTime + GetIntermissionDuration() ) )
  9239. {
  9240. m_flIntermissionStartTime = gpGlobals->curtime + mp_endmatch_votenextleveltime.GetFloat() - GetIntermissionDuration();
  9241. }
  9242. return true;
  9243. }
  9244. m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteAllDone;
  9245. return false;
  9246. }
  9247. void CCSGameRules::GoToMatchRestartIntermission()
  9248. {
  9249. //We use our own intermission steps here to bypass the match win panel (AKA scoreboard)
  9250. BaseClass::GoToIntermission();
  9251. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  9252. {
  9253. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  9254. if ( pPlayer )
  9255. {
  9256. pPlayer->Unblind();
  9257. // set all players to FL_FROZEN
  9258. pPlayer->AddFlag( FL_FROZEN );
  9259. }
  9260. }
  9261. // freeze players while in intermission
  9262. m_bFreezePeriod = true;
  9263. }
  9264. bool CCSGameRules::GameModeSupportsHealthBuffer( void )
  9265. {
  9266. return false;
  9267. }
  9268. void CCSGameRules::UpdateMatchStats( CCSPlayer* pPlayer, int winnerIndex )
  9269. {
  9270. if( pPlayer->GetTeamNumber() == winnerIndex)
  9271. {
  9272. CCS_GameStats.IncrementStat( pPlayer, CSSTAT_MATCHES_WON, 1, false, true );
  9273. if( IsPlayingGunGame() )
  9274. {
  9275. if( ( IsPlayingGunGameProgressive() && pPlayer->MadeFinalGunGameProgressiveKill() ) ||
  9276. ( IsPlayingGunGameTRBomb() ) )
  9277. {
  9278. CCS_GameStats.IncrementStat(pPlayer,CSSTAT_GUN_GAME_MATCHES_WON,1);
  9279. }
  9280. if( IsPlayingGunGameProgressive() )
  9281. {
  9282. CCS_GameStats.IncrementStat(pPlayer,CSSTAT_GUN_GAME_PROGRESSIVE_MATCHES_WON,1);
  9283. // check for a rampage: See if we made it thru gun game progressive without dying once
  9284. if( pPlayer->WasKilledThisRound() == false && pPlayer->MadeFinalGunGameProgressiveKill() )
  9285. {
  9286. pPlayer->AwardAchievement(CSGunGameProgressiveRampage);
  9287. }
  9288. // see if we won a progressive match without reloading
  9289. if( !pPlayer->HasReloaded() )
  9290. {
  9291. pPlayer->AwardAchievement(CSGunGameConservationist);
  9292. }
  9293. }
  9294. else if( IsPlayingGunGameTRBomb())
  9295. {
  9296. CCS_GameStats.IncrementStat(pPlayer,CSSTAT_GUN_GAME_TRBOMB_MATCHES_WON,1);
  9297. }
  9298. }
  9299. int mapIndex = GetCSLevelIndex(gpGlobals->mapname.ToCStr());
  9300. if ( mapIndex != -1 )
  9301. {
  9302. CSStatType_t matchStatId = MapName_StatId_Table[mapIndex].matchesWonId;
  9303. // look up our map information and increment the matches won stat. Rounds and other stats are done elsewhere
  9304. if(matchStatId != CSSTAT_UNDEFINED)
  9305. CCS_GameStats.IncrementStat( pPlayer,matchStatId, 1, false, true );
  9306. }
  9307. }
  9308. else if( winnerIndex == WINNER_DRAW )
  9309. {
  9310. CCS_GameStats.IncrementStat( pPlayer, CSSTAT_MATCHES_DRAW, 1, false, true );
  9311. }
  9312. CCS_GameStats.IncrementStat( pPlayer, CSSTAT_MATCHES_PLAYED, 1, false, true );
  9313. if( IsPlayingGunGame() )
  9314. {
  9315. CCS_GameStats.IncrementStat( pPlayer, CSSTAT_GUN_GAME_MATCHES_PLAYED, 1, false, true );
  9316. }
  9317. // CSN-8618 and others - Make sure these get sent before stats are reset.
  9318. // End rounds stats are sent BEFORE this call, and the match ends before the next send happens
  9319. // (resetting what we're recording above). Send these now so they don't get lost.
  9320. CCS_GameStats.SendStatsToPlayer( pPlayer, CSSTAT_PRIORITY_ENDROUND );
  9321. }
  9322. // --------------------------------------------------------------------------------------------------- //
  9323. // Contribution score helper functions
  9324. // --------------------------------------------------------------------------------------------------- //
  9325. inline float FalloffWeight( float fDistance, float fRangeInner, float fRangeOuter )
  9326. {
  9327. if ( fDistance > fRangeInner )
  9328. return (fRangeOuter - fDistance) / (fRangeOuter - fRangeInner);
  9329. else
  9330. return 1.0f;
  9331. }
  9332. // splits point amongst all players in zone
  9333. void CCSGameRules::SplitScoreAmongPlayersInZone( int iPoints, int iTeam, CCSPlayer* pExcludePlayer, uint iPlace )
  9334. {
  9335. float fWeights[MAX_PLAYERS+1];
  9336. memset( fWeights, 0, sizeof( fWeights ) );
  9337. float fWeightSum = 0.0f;
  9338. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  9339. {
  9340. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  9341. if ( !pPlayer )
  9342. continue;
  9343. if ( pPlayer == pExcludePlayer )
  9344. continue;
  9345. if ( pPlayer->GetTeamNumber() != iTeam )
  9346. continue;
  9347. if ( pPlayer->GetLastKnownArea() && pPlayer->GetLastKnownArea()->GetPlace() == iPlace )
  9348. {
  9349. fWeightSum += 1.0f;
  9350. fWeights[i] = 1.0f;
  9351. }
  9352. }
  9353. if ( fWeightSum == 0.0f )
  9354. return;
  9355. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  9356. {
  9357. if ( fWeights[i] > 0.0f )
  9358. {
  9359. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  9360. ASSERT( pPlayer );
  9361. int score = RoundFloatToInt(iPoints * fWeights[i] / fWeightSum );
  9362. pPlayer->AddScore( score );
  9363. pPlayer->AddRoundProximityScore( score );
  9364. }
  9365. }
  9366. }
  9367. void CCSGameRules::SplitScoreAmongPlayersInRange( int iPoints, int iTeam, CCSPlayer* pExcludePlayer, const Vector& center, float fRangeInner, float fRangeOuter )
  9368. {
  9369. float fMaxRangeSquared = fRangeOuter * fRangeOuter;
  9370. float fWeights[MAX_PLAYERS+1];
  9371. memset( fWeights, 0, sizeof( fWeights ) );
  9372. float fWeightSum = 0.0f;
  9373. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  9374. {
  9375. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  9376. if ( !pPlayer )
  9377. continue;
  9378. if ( pPlayer == pExcludePlayer )
  9379. continue;
  9380. if ( pPlayer->GetTeamNumber() != iTeam )
  9381. continue;
  9382. if ( center.DistToSqr( pPlayer->GetAbsOrigin() ) <= fMaxRangeSquared )
  9383. {
  9384. float fWeight = FalloffWeight( center.DistTo( pPlayer->GetAbsOrigin()), fRangeInner, fRangeOuter );
  9385. fWeightSum += fWeight;
  9386. fWeights[i] = fWeight;
  9387. }
  9388. }
  9389. if ( fWeightSum == 0.0f )
  9390. return;
  9391. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  9392. {
  9393. if ( fWeights[i] > 0.0f )
  9394. {
  9395. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  9396. int score = RoundFloatToInt( iPoints * fWeights[i] / fWeightSum );
  9397. pPlayer->AddScore( score );
  9398. pPlayer->AddRoundProximityScore( score );
  9399. }
  9400. }
  9401. }
  9402. void CCSGameRules::ScorePlayerKill( CCSPlayer* pPlayer )
  9403. {
  9404. ASSERT( pPlayer != NULL );
  9405. if ( pPlayer )
  9406. {
  9407. if ( IsPlayingGunGameDeathmatch() && pPlayer->GetActiveCSWeapon() )
  9408. {
  9409. CEconItemView* pItem = pPlayer->GetActiveCSWeapon()->GetEconItemView();
  9410. CSWeaponID wepID = ( pItem && pItem->IsValid() ) ? (CSWeaponID)(pItem->GetItemIndex()) : pPlayer->GetActiveCSWeapon()->GetCSWeaponID();
  9411. int iWepSlot = ( pItem && pItem->GetItemDefinition() ) ? pItem->GetItemDefinition()->GetDefaultLoadoutSlot() : LOADOUT_POSITION_INVALID;
  9412. int nScore = GetWeaponScoreForDeathmatch( iWepSlot );
  9413. if ( pPlayer->AddDeathmatchKillScore( nScore, wepID, iWepSlot ) <= 0 )
  9414. {
  9415. pPlayer->AddContributionScore( contributionscore_kill.GetInt() );
  9416. }
  9417. }
  9418. else
  9419. {
  9420. pPlayer->AddContributionScore( contributionscore_kill.GetInt() );
  9421. pPlayer->AddScore( RoundFloatToInt( score_kill_enemy_bonus.GetFloat() ) );
  9422. }
  9423. }
  9424. }
  9425. void CCSGameRules::ScorePlayerAssist( CCSPlayer* pPlayer, CCSPlayer* pCSVictim )
  9426. {
  9427. ASSERT( pPlayer != NULL );
  9428. if ( pPlayer )
  9429. {
  9430. if ( IsPlayingGunGameDeathmatch() && pPlayer->GetActiveCSWeapon() )
  9431. {
  9432. CEconItemView* pItem = pPlayer->GetActiveCSWeapon()->GetEconItemView();
  9433. CSWeaponID wepID = ( pItem && pItem->IsValid() ) ? (CSWeaponID)(pItem->GetItemIndex()) : pPlayer->GetActiveCSWeapon()->GetCSWeaponID();
  9434. //int nScore = GetWeaponScoreForDeathmatch( wepID );
  9435. // we don't store what weapon the player did the assist damage with and we can't guarantee the player has a weapon
  9436. // when the assist is awarded, so we just give them and average of half the points awarded
  9437. int nScore = 6;
  9438. pPlayer->AddDeathmatchKillScore( nScore, wepID, pItem->GetItemDefinition()->GetDefaultLoadoutSlot(), true, pCSVictim->GetPlayerName() );
  9439. pPlayer->IncrementAssistsCount( 1 );
  9440. }
  9441. else if ( IPointsForKill( pPlayer, pCSVictim ) > 0 ) // this ensures that only assists on enemies are recorded, but "assists" for teammate kills are not
  9442. {
  9443. pPlayer->AddContributionScore( contributionscore_assist.GetInt() );
  9444. pPlayer->IncrementAssistsCount( 1 );
  9445. }
  9446. }
  9447. }
  9448. void CCSGameRules::ScorePlayerObjectiveKill( CCSPlayer* pPlayer )
  9449. {
  9450. ASSERT( pPlayer != NULL );
  9451. if ( pPlayer )
  9452. {
  9453. pPlayer->AddContributionScore( contributionscore_objective_kill.GetInt() );
  9454. }
  9455. }
  9456. void CCSGameRules::ScorePlayerTeamKill( CCSPlayer* pPlayer )
  9457. {
  9458. ASSERT( pPlayer != NULL );
  9459. if ( pPlayer )
  9460. {
  9461. pPlayer->AddContributionScore( contributionscore_team_kill.GetInt( ) );
  9462. }
  9463. }
  9464. void CCSGameRules::ScorePlayerSuicide( CCSPlayer* pPlayer )
  9465. {
  9466. ASSERT( pPlayer != NULL );
  9467. if ( pPlayer )
  9468. {
  9469. pPlayer->AddContributionScore( contributionscore_suicide.GetInt( ) );
  9470. if ( !m_bLoadingRoundBackupData )
  9471. pPlayer->ProcessSuicideAsKillReward();
  9472. }
  9473. }
  9474. void CCSGameRules::ScorePlayerDamage( CCSPlayer* pPlayer, float fDamage )
  9475. {
  9476. pPlayer->AddScore( RoundFloatToInt( fDamage * score_damage.GetFloat() ) );
  9477. Place iPlace = pPlayer->GetLastKnownArea() ? pPlayer->GetLastKnownArea()->GetPlace() : UNDEFINED_PLACE;
  9478. if ( iPlace != UNDEFINED_PLACE )
  9479. SplitScoreAmongPlayersInZone( RoundFloatToInt( fDamage * score_team_damage_bonus.GetFloat() ), pPlayer->GetTeamNumber(), pPlayer, iPlace );
  9480. // award bonus for being near planted bomb
  9481. {
  9482. const float fRadiusInner = score_planted_bomb_proximity_damage_radius_inner.GetFloat();
  9483. const float fRadiusOuter = score_planted_bomb_proximity_damage_radius_outer.GetFloat();
  9484. const float fRadiusOuterSquared = fRadiusOuter * fRadiusOuter;
  9485. FOR_EACH_VEC( g_PlantedC4s, iBomb )
  9486. {
  9487. CPlantedC4 *pC4 = g_PlantedC4s[iBomb];
  9488. if ( pC4 && pC4->IsBombActive() )
  9489. {
  9490. Vector bombPos = pC4->GetAbsOrigin();
  9491. Vector playerToBomb = pPlayer->GetAbsOrigin() - bombPos;
  9492. if ( playerToBomb.LengthSqr() < fRadiusOuterSquared )
  9493. {
  9494. float fWeight = FalloffWeight( playerToBomb.Length(), fRadiusInner, fRadiusOuter );
  9495. int score = RoundFloatToInt( fWeight * score_planted_bomb_proximity_damage_bonus.GetFloat() );
  9496. pPlayer->AddScore( score );
  9497. pPlayer->AddRoundProximityScore( score );
  9498. break;
  9499. }
  9500. }
  9501. }
  9502. }
  9503. // bonus for being near hostage
  9504. const float fRadiusInner = score_hostage_proximity_damage_radius_inner.GetFloat();
  9505. const float fRadiusOuter = score_hostage_proximity_damage_radius_outer.GetFloat();
  9506. float fClosestSquared = FLT_MAX;
  9507. FOR_EACH_VEC( g_Hostages, iHostage )
  9508. {
  9509. CHostage* pHostage = g_Hostages[iHostage];
  9510. if ( pHostage->m_iHealth > 0 && !pHostage->IsRescued() )
  9511. {
  9512. float fDistanceSquared = pHostage->GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() );
  9513. if ( fDistanceSquared < fClosestSquared )
  9514. {
  9515. fClosestSquared = fDistanceSquared;
  9516. }
  9517. }
  9518. }
  9519. if ( fClosestSquared < fRadiusOuter * fRadiusOuter )
  9520. {
  9521. float fWeight = FalloffWeight( sqrtf( fClosestSquared ), fRadiusInner, fRadiusOuter );
  9522. int score = RoundFloatToInt( fDamage * fWeight * score_hostage_proximity_damage_bonus.GetFloat() );
  9523. pPlayer->AddScore( score );
  9524. pPlayer->AddRoundProximityScore( score );
  9525. }
  9526. // damage near dropped bomb bonus
  9527. {
  9528. const float fRadiusInner = score_dropped_bomb_proximity_damage_bonus_radius_inner.GetFloat();
  9529. const float fRadiusOuter = score_dropped_bomb_proximity_damage_bonus_radius_outer.GetFloat();
  9530. CBaseEntity* pC4 = gEntList.FindEntityByClassnameNearest( "weapon_c4", pPlayer->GetAbsOrigin(), fRadiusOuter );
  9531. if ( pC4 && pC4->GetOwnerEntity() == NULL )
  9532. {
  9533. float fWeight = FalloffWeight( pC4->GetAbsOrigin().DistTo( pPlayer->GetAbsOrigin()), fRadiusInner, fRadiusOuter );
  9534. int score = RoundFloatToInt( fDamage * fWeight * score_dropped_bomb_proximity_damage_bonus.GetFloat() );
  9535. pPlayer->AddScore( score );
  9536. pPlayer->AddRoundProximityScore( score );
  9537. }
  9538. }
  9539. // damage near dropped defuser
  9540. {
  9541. const float fRadiusInner = score_dropped_defuser_proximity_damage_radius_inner.GetFloat();
  9542. const float fRadiusOuter = score_dropped_defuser_proximity_damage_radius_outer.GetFloat();
  9543. CBaseEntity* pDefuser = gEntList.FindEntityByClassnameNearest( "item_defuser", pPlayer->GetAbsOrigin(), fRadiusOuter );
  9544. if ( pDefuser && pDefuser->GetOwnerEntity() == NULL )
  9545. {
  9546. float fWeight = FalloffWeight( pDefuser->GetAbsOrigin().DistTo( pPlayer->GetAbsOrigin() ), fRadiusInner, fRadiusOuter );
  9547. int score = RoundFloatToInt( fDamage * fWeight * score_dropped_defuser_proximity_damage_bonus.GetFloat() );
  9548. pPlayer->AddScore( score );
  9549. pPlayer->AddRoundProximityScore( score );
  9550. }
  9551. }
  9552. }
  9553. void CCSGameRules::ScoreBombPlant( CCSPlayer* pPlayer )
  9554. {
  9555. ASSERT( pPlayer != NULL );
  9556. if ( pPlayer )
  9557. {
  9558. pPlayer->AddContributionScore( contributionscore_bomb_planted.GetInt() );
  9559. }
  9560. CPlantedC4 *pC4 = g_PlantedC4s[0];
  9561. ASSERT( pC4 != NULL );
  9562. if ( pC4 )
  9563. {
  9564. SplitScoreAmongPlayersInRange( score_bomb_plant_bonus.GetInt(), TEAM_TERRORIST, NULL, pC4->GetAbsOrigin(),
  9565. score_bomb_plant_radius_inner.GetFloat(), score_bomb_plant_radius_outer.GetFloat() );
  9566. }
  9567. }
  9568. void CCSGameRules::ScoreBombExploded( CCSPlayer* pPlayer )
  9569. {
  9570. ASSERT( pPlayer != NULL );
  9571. if ( pPlayer )
  9572. {
  9573. pPlayer->AddContributionScore( contributionscore_bomb_exploded.GetInt() );
  9574. }
  9575. }
  9576. void CCSGameRules::ScoreBombDefuse( CCSPlayer* pPlayer, bool bMajorEvent )
  9577. {
  9578. ASSERT( pPlayer != NULL );
  9579. if ( pPlayer )
  9580. {
  9581. pPlayer->AddContributionScore( bMajorEvent ? contributionscore_bomb_defuse_major.GetInt() : contributionscore_bomb_defuse_minor.GetInt() );
  9582. }
  9583. m_bBombDefused = true;
  9584. CPlantedC4 *pC4 = g_PlantedC4s[0];
  9585. ASSERT( pC4 != NULL );
  9586. if ( pC4 && bMajorEvent )
  9587. {
  9588. SplitScoreAmongPlayersInRange( score_bomb_defuse_bonus.GetInt(), TEAM_CT, NULL, pC4->GetAbsOrigin(),
  9589. score_bomb_defuse_radius_inner.GetFloat(), score_bomb_defuse_radius_outer.GetFloat() );
  9590. }
  9591. }
  9592. void CCSGameRules::ScoreHostageRescue( CCSPlayer* pPlayer, CHostage* pHostage, bool bMajorEvent )
  9593. {
  9594. ASSERT( pPlayer != NULL );
  9595. if ( pPlayer )
  9596. {
  9597. pPlayer->AddContributionScore( bMajorEvent ? contributionscore_hostage_rescue_major.GetInt() : contributionscore_hostage_rescue_minor.GetInt() );
  9598. }
  9599. ASSERT( pHostage != NULL );
  9600. if ( pHostage && bMajorEvent )
  9601. {
  9602. SplitScoreAmongPlayersInRange( score_hostage_rescue_bonus.GetInt(), TEAM_CT, NULL, pHostage->GetAbsOrigin(),
  9603. score_hostage_rescue_radius_inner.GetFloat(), score_hostage_rescue_radius_outer.GetFloat() );
  9604. }
  9605. }
  9606. void CCSGameRules::ScoreHostageKilled( CCSPlayer* pPlayer )
  9607. {
  9608. ASSERT( pPlayer != NULL );
  9609. if ( pPlayer )
  9610. {
  9611. pPlayer->AddContributionScore( contributionscore_hostage_kill.GetInt() );
  9612. }
  9613. }
  9614. void CCSGameRules::ScoreHostageDamage( CCSPlayer* pPlayer, float fDamage )
  9615. {
  9616. ASSERT( pPlayer != NULL );
  9617. if ( pPlayer )
  9618. {
  9619. pPlayer->AddScore( -RoundFloatToInt( fDamage * score_hostage_damage_penalty.GetInt() ) );
  9620. }
  9621. }
  9622. void CCSGameRules::ScoreFriendlyFire( CCSPlayer* pPlayer, float fDamage )
  9623. {
  9624. ASSERT( pPlayer != NULL );
  9625. if ( pPlayer )
  9626. {
  9627. pPlayer->AddScore( -RoundFloatToInt( fDamage * score_ff_damage.GetFloat() ) );
  9628. }
  9629. }
  9630. void CCSGameRules::ScoreBlindEnemy( CCSPlayer* pPlayer )
  9631. {
  9632. ASSERT( pPlayer != NULL );
  9633. if ( pPlayer )
  9634. {
  9635. pPlayer->AddScore( score_blind_enemy_bonus.GetInt() );
  9636. if ( ShouldRecordMatchStats() )
  9637. {
  9638. // match stats for blinding enemy
  9639. pPlayer->m_iMatchStats_EnemiesFlashed.GetForModify( GetRoundsPlayed( ) ) ++; // record in MatchStats'
  9640. // Keep track in QMM data
  9641. if ( pPlayer->GetHumanPlayerAccountID() )
  9642. {
  9643. if ( CCSGameRules::CQMMPlayerData_t *pQMM = QueuedMatchmakingPlayersDataFind( pPlayer->GetHumanPlayerAccountID() ) )
  9644. {
  9645. pQMM->m_iMatchStats_EnemiesFlashed[ GetRoundsPlayed( ) ] = pPlayer->m_iMatchStats_EnemiesFlashed.Get( GetRoundsPlayed( ) );
  9646. }
  9647. }
  9648. }
  9649. }
  9650. }
  9651. void CCSGameRules::ScoreBlindFriendly( CCSPlayer* pPlayer )
  9652. {
  9653. ASSERT( pPlayer != NULL );
  9654. if ( pPlayer )
  9655. {
  9656. pPlayer->AddScore( -score_blind_friendly_penalty.GetInt() );
  9657. }
  9658. }
  9659. // --------------------------------------------------------------------------------------------------- //
  9660. // End Contribution score functions
  9661. // --------------------------------------------------------------------------------------------------- //
  9662. static void PrintToConsole( CBasePlayer *player, const char *text )
  9663. {
  9664. if ( player )
  9665. {
  9666. ClientPrint( player, HUD_PRINTCONSOLE, text );
  9667. }
  9668. else
  9669. {
  9670. Msg( "%s", text );
  9671. }
  9672. }
  9673. void CCSGameRules::DumpTimers( void ) const
  9674. {
  9675. extern ConVar bot_join_delay;
  9676. CBasePlayer *player = UTIL_GetCommandClient();
  9677. CFmtStr str;
  9678. PrintToConsole( player, str.sprintf( "Timers and related info at %f:\n", gpGlobals->curtime ) );
  9679. PrintToConsole( player, str.sprintf( "m_bCompleteReset: %d\n", m_bCompleteReset ) );
  9680. PrintToConsole( player, str.sprintf( "m_iTotalRoundsPlayed: %d\n", m_iTotalRoundsPlayed ) );
  9681. PrintToConsole( player, str.sprintf( "m_iRoundTime: %d\n", m_iRoundTime.Get() ) );
  9682. PrintToConsole( player, str.sprintf( "m_iRoundWinStatus: %d\n", m_iRoundWinStatus.Get() ) );
  9683. PrintToConsole( player, str.sprintf( "first connected: %d\n", m_bFirstConnected ) );
  9684. PrintToConsole( player, str.sprintf( "intermission start time: %f\n", m_flIntermissionStartTime ) );
  9685. PrintToConsole( player, str.sprintf( "intermission duration: %f\n", GetIntermissionDuration() ) );
  9686. PrintToConsole( player, str.sprintf( "freeze period: %d\n", m_bFreezePeriod.Get() ) );
  9687. PrintToConsole( player, str.sprintf( "round restart time: %f\n", m_flRestartRoundTime.Get() ) );
  9688. PrintToConsole( player, str.sprintf( "game start time: %f\n", m_flGameStartTime.Get() ) );
  9689. PrintToConsole( player, str.sprintf( "m_fMatchStartTime: %f\n", m_fMatchStartTime.Get() ) );
  9690. PrintToConsole( player, str.sprintf( "m_fRoundStartTime: %f\n", m_fRoundStartTime.Get() ) );
  9691. PrintToConsole( player, str.sprintf( "freeze time: %d\n", m_iFreezeTime ) );
  9692. PrintToConsole( player, str.sprintf( "next think: %f\n", m_tmNextPeriodicThink ) );
  9693. PrintToConsole( player, str.sprintf( "fraglimit: %d\n", fraglimit.GetInt() ) );
  9694. PrintToConsole( player, str.sprintf( "mp_maxrounds: %d\n", mp_maxrounds.GetInt() ) );
  9695. PrintToConsole( player, str.sprintf( "mp_winlimit: %d\n", mp_winlimit.GetInt() ) );
  9696. PrintToConsole( player, str.sprintf( "bot_quota: %d\n", cv_bot_quota.GetInt() ) );
  9697. PrintToConsole( player, str.sprintf( "bot_quota_mode: %s\n", cv_bot_quota_mode.GetString() ) );
  9698. PrintToConsole( player, str.sprintf( "bot_join_after_player: %d\n", cv_bot_join_after_player.GetInt() ) );
  9699. PrintToConsole( player, str.sprintf( "bot_join_delay: %d\n", bot_join_delay.GetInt() ) );
  9700. PrintToConsole( player, str.sprintf( "nextlevel: %s\n", nextlevel.GetString() ) );
  9701. int humansInGame = UTIL_HumansInGame( true );
  9702. int botsInGame = UTIL_BotsInGame();
  9703. PrintToConsole( player, str.sprintf( "%d humans and %d bots in game\n", humansInGame, botsInGame ) );
  9704. PrintToConsole( player, str.sprintf( "num CTs (spawnable): %d (%d)\n", m_iNumCT, m_iNumSpawnableCT ) );
  9705. PrintToConsole( player, str.sprintf( "num Ts (spawnable): %d (%d)\n", m_iNumTerrorist, m_iNumSpawnableTerrorist ) );
  9706. if ( g_fGameOver )
  9707. {
  9708. PrintToConsole( player, str.sprintf( "Game is over!\n" ) );
  9709. }
  9710. PrintToConsole( player, str.sprintf( "\n" ) );
  9711. }
  9712. CON_COMMAND( mp_dump_timers, "Prints round timers to the console for debugging" )
  9713. {
  9714. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  9715. return;
  9716. if ( CSGameRules() )
  9717. {
  9718. CSGameRules()->DumpTimers();
  9719. }
  9720. }
  9721. // living players on the given team need to be marked as not receiving any money
  9722. // next round.
  9723. void CCSGameRules::MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int team)
  9724. {
  9725. int playerNum;
  9726. for (playerNum = 1; playerNum <= gpGlobals->maxClients; ++playerNum)
  9727. {
  9728. CCSPlayer *player = (CCSPlayer *)UTIL_PlayerByIndex(playerNum);
  9729. if (player == NULL)
  9730. {
  9731. continue;
  9732. }
  9733. if ((player->GetTeamNumber() == team) && (player->IsAlive()))
  9734. {
  9735. // Exception here: only here and not inside "MarkAsNotReceivingMoneyNextRound"
  9736. // to not affect team-wide money management, round backups, etc.
  9737. // When a player is alive and controlling a bot then the monetary punishment should go
  9738. // to the bot being controlled
  9739. if ( player->IsControllingBot() )
  9740. {
  9741. if ( CCSPlayer* controlledBotPlayer = player->GetControlledBot() )
  9742. player = controlledBotPlayer;
  9743. }
  9744. player->MarkAsNotReceivingMoneyNextRound();
  9745. }
  9746. }
  9747. }
  9748. void CCSGameRules::CheckLevelInitialized( void )
  9749. {
  9750. if ( !m_bLevelInitialized )
  9751. {
  9752. // Count the number of spawn points for each team
  9753. // This determines the maximum number of players allowed on each
  9754. m_iSpawnPointCount_Terrorist = 0;
  9755. m_iSpawnPointCount_CT = 0;
  9756. m_iMaxNumTerrorists = 0;
  9757. m_iMaxNumCTs = 0;
  9758. m_flCoopRespawnAndHealTime = -1;
  9759. const char * szMapName = STRING( gpGlobals->mapname );
  9760. int nNumSlots = 2;
  9761. uint32 dwRichPresenceContext = 0xFFFF;
  9762. if ( szMapName )
  9763. {
  9764. g_pGameTypes->GetMapInfo( szMapName, dwRichPresenceContext );
  9765. }
  9766. int iGameType = g_pGameTypes->GetCurrentGameType();
  9767. int iGameMode = g_pGameTypes->GetCurrentGameMode();
  9768. nNumSlots = g_pGameTypes->GetMaxPlayersForTypeAndMode( iGameType, iGameMode );
  9769. // for the training map, our max is 1 CT and 0 T's, diving 1 in half doesn't work for us in this case, so make sure our min is 1
  9770. m_iMaxNumTerrorists = MAX( nNumSlots / 2, 1 );
  9771. m_iMaxNumCTs = MAX( nNumSlots / 2, 1 );
  9772. m_iSpectatorSlotCount = mp_spectators_max.GetInt();
  9773. m_nGuardianModeWaveNumber = 1;
  9774. m_nGuardianModeSpecialKillsRemaining = mp_guardian_special_kills_needed.GetInt();
  9775. // create the spawn point lists here
  9776. GenerateSpawnPointListsFirstTime();
  9777. // Is this a logo map?
  9778. if ( gEntList.FindEntityByClassname( NULL, "info_player_logo" ) )
  9779. m_bLogoMap = true;
  9780. m_bLevelInitialized = true;
  9781. }
  9782. }
  9783. void CCSGameRules::DoCoopSpawnAndNavInit( void )
  9784. {
  9785. // TODO: here is where we should pick the spawn points we will use for coop and delete the rest
  9786. if ( m_vecMainCTSpawnPos == Vector( 0, 0, 0 ) )
  9787. {
  9788. CBaseEntity* ent = NULL;
  9789. // we need to find at least one CT spawn point to figure out where the "start" of the map is
  9790. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL )
  9791. {
  9792. if ( IsSpawnPointValid( ent, NULL ) )
  9793. {
  9794. SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent );
  9795. if ( pSpawnPoint )
  9796. {
  9797. m_vecMainCTSpawnPos = pSpawnPoint->GetAbsOrigin();
  9798. break;
  9799. }
  9800. }
  9801. }
  9802. }
  9803. if ( TheNavMesh && !TheNavMesh->IsLoaded() && !TheNavMesh->IsAnalyzed() && !TheNavMesh->IsGenerating() )
  9804. {
  9805. // If there isn't a Navigation Mesh in memory, create one
  9806. // we do this here because we need to generate the nav each time we create a new map
  9807. // and generated it when a bot join is too late
  9808. ConVarRef nav_generate_fast_for_tilegen( "nav_generate_fast_for_tilegen" );
  9809. if ( nav_generate_fast_for_tilegen.IsValid() )
  9810. nav_generate_fast_for_tilegen.SetValue( 1 );
  9811. TheNavMesh->BeginGeneration();
  9812. }
  9813. else
  9814. {
  9815. ConVarRef bot_quota( "bot_quota" );
  9816. bot_quota.SetValue( 20 );
  9817. }
  9818. }
  9819. void CCSGameRules::AddSpawnPointToMasterList( SpawnPoint* pSpawnPoint )
  9820. {
  9821. //if classname == T
  9822. if ( FClassnameIs( pSpawnPoint, "info_player_terrorist" ) ||
  9823. FClassnameIs( pSpawnPoint, "info_enemy_terrorist_spawn" ) ||
  9824. FClassnameIs( pSpawnPoint, "info_armsrace_terrorist" ) )
  9825. {
  9826. // check to make sure it isn't already in the list
  9827. if ( m_TerroristSpawnPointsMasterList.Find( pSpawnPoint ) != m_TerroristSpawnPointsMasterList.InvalidIndex() )
  9828. {
  9829. AssertMsg( false, "AddSpawnPointToMasterList tried to add a spawn point to the list, but it already exists in the list!" );
  9830. return;
  9831. }
  9832. m_TerroristSpawnPointsMasterList.AddToTail( pSpawnPoint );
  9833. }
  9834. else if ( FClassnameIs( pSpawnPoint, "info_player_counterterrorist" ) ||
  9835. FClassnameIs( pSpawnPoint, "info_armsrace_counterterrorist" ) )
  9836. {
  9837. if ( m_CTSpawnPointsMasterList.Find( pSpawnPoint ) != m_CTSpawnPointsMasterList.InvalidIndex() )
  9838. {
  9839. AssertMsg( false, "AddSpawnPointToMasterList tried to add a spawn point to the list, but it already exists in the list!" );
  9840. return;
  9841. }
  9842. m_CTSpawnPointsMasterList.AddToTail( pSpawnPoint );
  9843. }
  9844. else if ( FClassnameIs( pSpawnPoint, "info_deathmatch_spawn" ) )
  9845. {
  9846. // No team specific spawns in this mode
  9847. }
  9848. else
  9849. {
  9850. // doesn't match any classes we are aware of!
  9851. AssertMsg( false, "AddSpawnPointToMasterList is looking to adda class to the master spawn list, but it doesn't recognize the class type!" );
  9852. }
  9853. RefreshCurrentSpawnPointLists();
  9854. }
  9855. void CCSGameRules::GenerateSpawnPointListsFirstTime( void )
  9856. {
  9857. //CUtlVector< SpawnPoint* > m_CTSpawnPointsMasterList; // The master list of CT spawn points (contains all points whether enabled or disabled)
  9858. //CUtlVector< SpawnPoint* > m_TerroristSpawnPointsMasterList; // The master list of Terrorist spawn points (contains all points whether enabled or disabled)
  9859. // Clear out existing spawn point lists
  9860. m_TerroristSpawnPointsMasterList.RemoveAll();
  9861. m_CTSpawnPointsMasterList.RemoveAll();
  9862. const char* szTSpawnEntName = "info_player_terrorist";
  9863. CBaseEntity* ent = NULL;
  9864. if ( IsPlayingCoopMission() )
  9865. szTSpawnEntName = "info_enemy_terrorist_spawn";
  9866. while ( ( ent = gEntList.FindEntityByClassname( ent, szTSpawnEntName ) ) != NULL )
  9867. {
  9868. if ( IsSpawnPointValid( ent, NULL ) )
  9869. {
  9870. SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent );
  9871. if ( pSpawnPoint )
  9872. {
  9873. // Store off the terrorist spawn point
  9874. m_TerroristSpawnPointsMasterList.AddToTail( pSpawnPoint );
  9875. }
  9876. }
  9877. else
  9878. {
  9879. Warning("Invalid terrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
  9880. ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[1],ent->GetAbsOrigin()[2] );
  9881. }
  9882. }
  9883. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL )
  9884. {
  9885. if ( IsSpawnPointValid( ent, NULL ) )
  9886. {
  9887. SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent );
  9888. if ( pSpawnPoint )
  9889. {
  9890. // Store off the CT spawn point
  9891. m_CTSpawnPointsMasterList.AddToTail( pSpawnPoint );
  9892. }
  9893. }
  9894. else
  9895. {
  9896. Warning("Invalid counterterrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
  9897. ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[1],ent->GetAbsOrigin()[2] );
  9898. }
  9899. }
  9900. ent = NULL;
  9901. // if we're playing armsrace, add the armsrace spawns to the list as well
  9902. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  9903. {
  9904. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_terrorist" ) ) != NULL )
  9905. {
  9906. if ( IsSpawnPointValid( ent, NULL ) )
  9907. {
  9908. SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent );
  9909. if ( pSpawnPoint )
  9910. {
  9911. pSpawnPoint->m_nType = SpawnPoint::ArmsRace;
  9912. // Store off the terrorist spawn point
  9913. m_TerroristSpawnPointsMasterList.AddToTail( pSpawnPoint );
  9914. }
  9915. }
  9916. else
  9917. {
  9918. Warning( "Invalid terrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
  9919. ent->GetAbsOrigin()[0], ent->GetAbsOrigin()[1], ent->GetAbsOrigin()[2] );
  9920. }
  9921. }
  9922. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_counterterrorist" ) ) != NULL )
  9923. {
  9924. if ( IsSpawnPointValid( ent, NULL ) )
  9925. {
  9926. SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent );
  9927. if ( pSpawnPoint )
  9928. {
  9929. pSpawnPoint->m_nType = SpawnPoint::ArmsRace;
  9930. // Store off the CT spawn point
  9931. m_CTSpawnPointsMasterList.AddToTail( pSpawnPoint );
  9932. }
  9933. }
  9934. else
  9935. {
  9936. Warning( "Invalid counterterrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
  9937. ent->GetAbsOrigin()[0], ent->GetAbsOrigin()[1], ent->GetAbsOrigin()[2] );
  9938. }
  9939. }
  9940. }
  9941. // we want the arms race spawns to shuffle with the regular spawns
  9942. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  9943. ShuffleMasterSpawnPointLists();
  9944. // sort them to ensure the priority ones are up front
  9945. SortMasterSpawnPointLists();
  9946. RefreshCurrentSpawnPointLists();
  9947. }
  9948. void CCSGameRules::RefreshCurrentSpawnPointLists( void )
  9949. {
  9950. // Clear out existing spawn point lists
  9951. m_TerroristSpawnPoints.RemoveAll();
  9952. m_CTSpawnPoints.RemoveAll();
  9953. m_iSpawnPointCount_Terrorist = 0;
  9954. m_iSpawnPointCount_CT = 0;
  9955. FOR_EACH_VEC( m_TerroristSpawnPointsMasterList, i )
  9956. {
  9957. SpawnPoint* pSpawnPoint = m_TerroristSpawnPointsMasterList[i];
  9958. if ( pSpawnPoint && pSpawnPoint->IsEnabled() )
  9959. {
  9960. m_iSpawnPointCount_Terrorist++;
  9961. // Store off the terrorist spawn point
  9962. m_TerroristSpawnPoints.AddToTail( pSpawnPoint );
  9963. }
  9964. }
  9965. FOR_EACH_VEC( m_CTSpawnPointsMasterList, i )
  9966. {
  9967. SpawnPoint* pSpawnPoint = m_CTSpawnPointsMasterList[i];
  9968. if ( pSpawnPoint && pSpawnPoint->IsEnabled() )
  9969. {
  9970. m_iSpawnPointCount_CT++;
  9971. // Store off the CT spawn point
  9972. m_CTSpawnPoints.AddToTail( pSpawnPoint );
  9973. }
  9974. }
  9975. if ( IsPlayingCoopMission() )
  9976. {
  9977. // reset so that T spawns always try to start from 0
  9978. m_iNextTerroristSpawnPoint = 0;
  9979. // make sure that there are always enough bots
  9980. ConVarRef bot_quota( "bot_quota" );
  9981. int nQuota = UTIL_HumansInGame( true, true ) + MIN( m_iMaxNumTerrorists, m_iSpawnPointCount_Terrorist );
  9982. bot_quota.SetValue( nQuota );
  9983. //DevMsg( ">> Setting bot_quota to %d\n", nQuota );
  9984. }
  9985. else
  9986. {
  9987. // Shuffle the spawn points
  9988. ShuffleSpawnPointLists();
  9989. // Sort the list now that the spawn points have been shuffled
  9990. SortSpawnPointLists();
  9991. }
  9992. }
  9993. void CCSGameRules::SortSpawnPointLists( void )
  9994. {
  9995. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  9996. {
  9997. // Sort the spawn point lists
  9998. m_TerroristSpawnPoints.Sort( ArmsRaceSpawnPointSortFunction );
  9999. m_CTSpawnPoints.Sort( ArmsRaceSpawnPointSortFunction );
  10000. }
  10001. else
  10002. {
  10003. // Sort the spawn point lists
  10004. m_TerroristSpawnPoints.Sort( SpawnPointSortFunction );
  10005. m_CTSpawnPoints.Sort( SpawnPointSortFunction );
  10006. }
  10007. }
  10008. void CCSGameRules::ShuffleSpawnPointLists( void )
  10009. {
  10010. // Shuffle terrorist spawn points
  10011. VectorShuffle( m_TerroristSpawnPoints );
  10012. // Shuffle CT spawn points
  10013. VectorShuffle( m_CTSpawnPoints );
  10014. }
  10015. void CCSGameRules::ShuffleMasterSpawnPointLists( void )
  10016. {
  10017. // Shuffle terrorist spawn points
  10018. VectorShuffle( m_TerroristSpawnPointsMasterList );
  10019. // Shuffle CT spawn points
  10020. VectorShuffle( m_CTSpawnPointsMasterList );
  10021. }
  10022. void CCSGameRules::SortMasterSpawnPointLists( void )
  10023. {
  10024. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  10025. {
  10026. // Sort the spawn point lists
  10027. m_TerroristSpawnPointsMasterList.Sort( ArmsRaceSpawnPointSortFunction );
  10028. m_CTSpawnPointsMasterList.Sort( ArmsRaceSpawnPointSortFunction );
  10029. int nSpots = 0;
  10030. // disable all spawns over 10 because we already shoved teh arms race ones up front and
  10031. // if there are enough, disable the ones that aren't flagged as arms race
  10032. FOR_EACH_VEC( m_TerroristSpawnPointsMasterList, i )
  10033. {
  10034. SpawnPoint* pSpawnPoint = m_TerroristSpawnPointsMasterList[i];
  10035. if ( pSpawnPoint && pSpawnPoint->IsEnabled() )
  10036. {
  10037. if ( IsSpawnPointValid( pSpawnPoint, NULL ) == false )
  10038. {
  10039. pSpawnPoint->m_bEnabled = false;
  10040. continue;
  10041. }
  10042. nSpots++;
  10043. if ( nSpots > 12 )
  10044. pSpawnPoint->m_bEnabled = false;
  10045. }
  10046. }
  10047. nSpots = 0;
  10048. // disable for the CTs as well
  10049. FOR_EACH_VEC( m_CTSpawnPointsMasterList, i )
  10050. {
  10051. SpawnPoint* pSpawnPoint = m_CTSpawnPointsMasterList[i];
  10052. if ( pSpawnPoint && pSpawnPoint->IsEnabled() )
  10053. {
  10054. if ( IsSpawnPointValid( pSpawnPoint, NULL ) == false )
  10055. {
  10056. pSpawnPoint->m_bEnabled = false;
  10057. continue;
  10058. }
  10059. nSpots++;
  10060. if ( nSpots > 12 )
  10061. pSpawnPoint->m_bEnabled = false;
  10062. }
  10063. }
  10064. }
  10065. else
  10066. {
  10067. if ( !IsPlayingCoopMission() )
  10068. m_TerroristSpawnPointsMasterList.Sort( SpawnPointSortFunction );
  10069. m_CTSpawnPointsMasterList.Sort( SpawnPointSortFunction );
  10070. }
  10071. }
  10072. void CCSGameRules::ShufflePlayerList( CUtlVector< CCSPlayer* > &playersList )
  10073. {
  10074. // Shuffle players
  10075. VectorShuffle( playersList );
  10076. // shuffle and sort the T spawn points here too
  10077. if ( IsPlayingCoopMission() )
  10078. {
  10079. ShuffleSpawnPointLists();
  10080. SortSpawnPointLists();
  10081. }
  10082. }
  10083. CBaseEntity*CCSGameRules::GetNextSpawnpoint( int teamNumber )
  10084. {
  10085. CBaseEntity* pRetVal = NULL;
  10086. if ( teamNumber == TEAM_CT )
  10087. {
  10088. if ( m_iNextCTSpawnPoint >= m_CTSpawnPoints.Count() )
  10089. {
  10090. m_iNextCTSpawnPoint = 0;
  10091. }
  10092. if ( m_iNextCTSpawnPoint < m_CTSpawnPoints.Count() )
  10093. {
  10094. pRetVal = m_CTSpawnPoints[ m_iNextCTSpawnPoint ];
  10095. m_iNextCTSpawnPoint++;
  10096. }
  10097. }
  10098. else if ( teamNumber == TEAM_TERRORIST )
  10099. {
  10100. if ( m_iNextTerroristSpawnPoint >= m_TerroristSpawnPoints.Count() )
  10101. {
  10102. m_iNextTerroristSpawnPoint = 0;
  10103. }
  10104. if ( m_iNextTerroristSpawnPoint < m_TerroristSpawnPoints.Count() )
  10105. {
  10106. pRetVal = m_TerroristSpawnPoints[ m_iNextTerroristSpawnPoint ];
  10107. m_iNextTerroristSpawnPoint++;
  10108. }
  10109. }
  10110. return pRetVal;
  10111. }
  10112. void CCSGameRules::ShowSpawnPoints( int duration )
  10113. {
  10114. CBaseEntity* ent = NULL;
  10115. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL )
  10116. {
  10117. if ( IsSpawnPointValid( ent, NULL ) == false )
  10118. {
  10119. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration );
  10120. }
  10121. else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) )
  10122. {
  10123. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration );
  10124. }
  10125. else
  10126. {
  10127. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration );
  10128. }
  10129. }
  10130. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL )
  10131. {
  10132. if ( IsSpawnPointValid( ent, NULL ) == false )
  10133. {
  10134. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration );
  10135. }
  10136. else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) )
  10137. {
  10138. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration );
  10139. }
  10140. else
  10141. {
  10142. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration );
  10143. }
  10144. }
  10145. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_deathmatch_spawn" ) ) != NULL )
  10146. {
  10147. if ( IsSpawnPointValid( ent, NULL ) )
  10148. {
  10149. if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) )
  10150. {
  10151. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration );
  10152. }
  10153. else if ( IsSpawnPointHiddenFromOtherPlayers( ent, NULL ) )
  10154. {
  10155. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration );
  10156. }
  10157. else
  10158. {
  10159. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration );
  10160. }
  10161. }
  10162. else
  10163. {
  10164. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration );
  10165. }
  10166. }
  10167. if ( CSGameRules()->IsPlayingGunGameProgressive() )
  10168. {
  10169. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_terrorist" ) ) != NULL )
  10170. {
  10171. if ( IsSpawnPointValid( ent, NULL ) == false )
  10172. {
  10173. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration );
  10174. }
  10175. else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) )
  10176. {
  10177. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration );
  10178. }
  10179. else
  10180. {
  10181. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration );
  10182. }
  10183. }
  10184. while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_counterterrorist" ) ) != NULL )
  10185. {
  10186. if ( IsSpawnPointValid( ent, NULL ) == false )
  10187. {
  10188. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration );
  10189. }
  10190. else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) )
  10191. {
  10192. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration );
  10193. }
  10194. else
  10195. {
  10196. NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration );
  10197. }
  10198. }
  10199. }
  10200. }
  10201. void CCSGameRules::CheckRestartRound( void )
  10202. {
  10203. // Restart the game if specified by the server
  10204. int iRestartDelay = mp_restartgame.GetInt();
  10205. if ( iRestartDelay > 0 )
  10206. {
  10207. if ( iRestartDelay > 60 )
  10208. iRestartDelay = 60;
  10209. // log the restart
  10210. UTIL_LogPrintf( "World triggered \"Restart_Round_(%i_%s)\"\n", iRestartDelay, iRestartDelay == 1 ? "second" : "seconds" );
  10211. UTIL_LogPrintf( "Team \"CT\" scored \"%i\" with \"%i\" players\n", m_match.GetCTScore(), m_iNumCT );
  10212. UTIL_LogPrintf( "Team \"TERRORIST\" scored \"%i\" with \"%i\" players\n", m_match.GetTerroristScore(), m_iNumTerrorist );
  10213. // let the players know
  10214. char strRestartDelay[64];
  10215. Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
  10216. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "#SFUI_Second" : "#SFUI_Seconds" );
  10217. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#SFUI_Notice_Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "#SFUI_Second" : "#SFUI_Seconds" );
  10218. m_flRestartRoundTime = gpGlobals->curtime + iRestartDelay;
  10219. m_bCompleteReset = true;
  10220. m_bGameRestart = true;
  10221. m_bHasMatchStarted = false;
  10222. mp_restartgame.SetValue( 0 );
  10223. }
  10224. }
  10225. void cc_ScrambleTeams( const CCommand& args )
  10226. {
  10227. if ( UTIL_IsCommandIssuedByServerAdmin() )
  10228. {
  10229. CCSGameRules *pRules = dynamic_cast<CCSGameRules*>( GameRules() );
  10230. if ( pRules )
  10231. {
  10232. pRules->SetScrambleTeamsOnRestart( true );
  10233. mp_restartgame.SetValue( 1 );
  10234. }
  10235. }
  10236. }
  10237. static ConCommand mp_scrambleteams( "mp_scrambleteams", cc_ScrambleTeams, "Scramble the teams and restart the game" );
  10238. void cc_SwapTeams( const CCommand& args )
  10239. {
  10240. if ( UTIL_IsCommandIssuedByServerAdmin() )
  10241. {
  10242. CCSGameRules *pRules = dynamic_cast<CCSGameRules*>( GameRules() );
  10243. if ( pRules )
  10244. {
  10245. pRules->SetSwapTeamsOnRestart( true );
  10246. mp_restartgame.SetValue( 1 );
  10247. }
  10248. }
  10249. }
  10250. static ConCommand mp_swapteams( "mp_swapteams", cc_SwapTeams, "Swap the teams and restart the game" );
  10251. // sort function for the list of players that we're going to use to scramble the teams
  10252. int ScramblePlayersSort( CCSPlayer* const *p1, CCSPlayer* const *p2 )
  10253. {
  10254. CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource );
  10255. if ( pResource )
  10256. {
  10257. // check the priority
  10258. if ( p1 && p2 && (*p1) && (*p2) && (*p1)->GetScore() > (*p2)->GetScore() )
  10259. {
  10260. return 1;
  10261. }
  10262. }
  10263. return -1;
  10264. }
  10265. //////// PAUSE
  10266. void cc_PauseMatch( const CCommand& args )
  10267. {
  10268. if ( UTIL_IsCommandIssuedByServerAdmin() )
  10269. {
  10270. CCSGameRules *pRules = dynamic_cast<CCSGameRules*>( GameRules() );
  10271. if ( pRules )
  10272. {
  10273. if ( !pRules->IsMatchWaitingForResume() )
  10274. {
  10275. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" );
  10276. }
  10277. pRules->SetMatchWaitingForResume( true );
  10278. }
  10279. }
  10280. }
  10281. static ConCommand mp_pause_match( "mp_pause_match", cc_PauseMatch, "Pause the match in the next freeze time" );
  10282. //////// RESUME
  10283. void cc_ResumeMatch( const CCommand& args )
  10284. {
  10285. if ( UTIL_IsCommandIssuedByServerAdmin() )
  10286. {
  10287. CCSGameRules *pRules = dynamic_cast<CCSGameRules*>( GameRules() );
  10288. if ( pRules && pRules->IsMatchWaitingForResume() )
  10289. {
  10290. pRules->SetMatchWaitingForResume( false );
  10291. if ( !pRules->IsMatchWaitingForResume() )
  10292. {
  10293. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Resume" );
  10294. }
  10295. else
  10296. {
  10297. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" );
  10298. }
  10299. }
  10300. }
  10301. }
  10302. static ConCommand mp_unpause_match( "mp_unpause_match", cc_ResumeMatch, "Resume the match" );
  10303. ///////////
  10304. class SetHumanTeamFunctor
  10305. {
  10306. public:
  10307. SetHumanTeamFunctor( int targetTeam )
  10308. {
  10309. m_targetTeam = targetTeam;
  10310. m_sourceTeam = ( m_targetTeam == TEAM_CT ) ? TEAM_TERRORIST : TEAM_CT;
  10311. m_traitors.MakeReliable();
  10312. m_loyalists.MakeReliable();
  10313. m_loyalists.AddAllPlayers();
  10314. }
  10315. bool operator()( CBasePlayer *basePlayer )
  10316. {
  10317. CCSPlayer *player = ToCSPlayer( basePlayer );
  10318. if ( !player )
  10319. return true;
  10320. if ( player->IsBot() )
  10321. return true;
  10322. if ( player->GetTeamNumber() != m_sourceTeam )
  10323. return true;
  10324. if ( player->State_Get() == STATE_PICKINGCLASS )
  10325. return true;
  10326. if ( CSGameRules()->TeamFull( m_targetTeam ) )
  10327. return false;
  10328. if ( CSGameRules()->TeamStacked( m_targetTeam, m_sourceTeam ) )
  10329. return false;
  10330. player->SwitchTeam( m_targetTeam );
  10331. m_traitors.AddRecipient( player );
  10332. m_loyalists.RemoveRecipient( player );
  10333. return true;
  10334. }
  10335. void SendNotice( void )
  10336. {
  10337. if ( m_traitors.GetRecipientCount() > 0 )
  10338. {
  10339. UTIL_ClientPrintFilter( m_traitors, HUD_PRINTCENTER, "#SFUI_Notice_Player_Balanced" );
  10340. UTIL_ClientPrintFilter( m_loyalists, HUD_PRINTCENTER, "#SFUI_Notice_Teams_Balanced" );
  10341. }
  10342. }
  10343. private:
  10344. int m_targetTeam;
  10345. int m_sourceTeam;
  10346. CRecipientFilter m_traitors;
  10347. CRecipientFilter m_loyalists;
  10348. };
  10349. void CCSGameRules::MoveHumansToHumanTeam( void )
  10350. {
  10351. int targetTeam = GetHumanTeam();
  10352. if ( targetTeam != TEAM_TERRORIST && targetTeam != TEAM_CT )
  10353. return;
  10354. SetHumanTeamFunctor setTeam( targetTeam );
  10355. ForEachPlayer( setTeam );
  10356. setTeam.SendNotice();
  10357. }
  10358. void CCSGameRules::AddDroppedWeaponToList( CWeaponCSBase *pWeapon )
  10359. {
  10360. static ConVarRef weapon_max_before_cleanup( "weapon_max_before_cleanup" );
  10361. // NOTE: dont check is removeable here because weapons just thrown aren't removeable, just clean up when we actually do the removals
  10362. if ( pWeapon /*&& pWeapon->IsRemoveable()*/ && weapon_max_before_cleanup.GetInt() > 0 )
  10363. {
  10364. for ( int i = 0; i < m_weaponsDroppedInWorld.Count(); i++ )
  10365. {
  10366. if ( pWeapon == m_weaponsDroppedInWorld[i] )
  10367. return;
  10368. }
  10369. m_weaponsDroppedInWorld.AddToTail( pWeapon );
  10370. }
  10371. while ( weapon_max_before_cleanup.GetInt() > 0 && m_weaponsDroppedInWorld.Count() > weapon_max_before_cleanup.GetInt() )
  10372. {
  10373. //CUtlVector< float> m_weaponsDist;
  10374. // get the oldest 5 (or max) weapons
  10375. int nMaxIndex = Min( 5, m_weaponsDroppedInWorld.Count() );
  10376. // for ( int i = 0; i < nMaxIndex; i++ )
  10377. // {
  10378. // m_weaponsDist.AddToTail( 0 );
  10379. // }
  10380. int index = 0;
  10381. for ( int i = 0; i < nMaxIndex; i++ )
  10382. {
  10383. CWeaponCSBase *pWepTemp = m_weaponsDroppedInWorld[i].Get();
  10384. if ( pWepTemp && pWepTemp->IsRemoveable() )
  10385. {
  10386. // find the first weapon to be far enough away
  10387. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  10388. {
  10389. CBasePlayer *pPlayer = UTIL_PlayerByIndex( j );
  10390. if ( !pPlayer )
  10391. continue;
  10392. float flDist = ( pWepTemp->GetAbsOrigin() - pPlayer->GetAbsOrigin() ).Length();
  10393. if ( flDist > 1200 )
  10394. {
  10395. index = i;
  10396. break;
  10397. }
  10398. }
  10399. if ( index > 0 )
  10400. break;
  10401. }
  10402. }
  10403. CWeaponCSBase *pWep = m_weaponsDroppedInWorld[index].Get();
  10404. if ( pWep && pWep->IsRemoveable() )
  10405. {
  10406. UTIL_Remove( pWep );
  10407. }
  10408. m_weaponsDroppedInWorld.Remove( index );
  10409. }
  10410. }
  10411. void CCSGameRules::RemoveDroppedWeaponFromList( CWeaponCSBase *pWeapon )
  10412. {
  10413. if ( !pWeapon )
  10414. return;
  10415. for ( int i = 0; i < m_weaponsDroppedInWorld.Count(); i++ )
  10416. {
  10417. if ( pWeapon == m_weaponsDroppedInWorld[i] )
  10418. {
  10419. m_weaponsDroppedInWorld.Remove( i );
  10420. return;
  10421. }
  10422. }
  10423. }
  10424. void CCSGameRules::ProcessAutoBalance( void )
  10425. {
  10426. // No autobalancing for people who abandon games in queued matchmaking
  10427. if ( IsQueuedMatchmaking() )
  10428. return;
  10429. /*************** AUTO-BALANCE CODE *************/
  10430. if ( mp_autoteambalance.GetInt() != 0 &&
  10431. (m_iUnBalancedRounds >= 1) )
  10432. {
  10433. if ( GetHumanTeam() == TEAM_UNASSIGNED )
  10434. {
  10435. BalanceTeams();
  10436. }
  10437. }
  10438. if ( ((m_iNumSpawnableCT - m_iNumSpawnableTerrorist) >= 2) ||
  10439. ((m_iNumSpawnableTerrorist - m_iNumSpawnableCT) >= 2) )
  10440. {
  10441. m_iUnBalancedRounds++;
  10442. }
  10443. else
  10444. {
  10445. m_iUnBalancedRounds = 0;
  10446. }
  10447. // Warn the players of an impending auto-balance next round...
  10448. if ( mp_autoteambalance.GetInt() != 0 &&
  10449. (m_iUnBalancedRounds == 1) )
  10450. {
  10451. if ( GetHumanTeam() == TEAM_UNASSIGNED )
  10452. {
  10453. // Queue up impending auto-balance display text
  10454. m_fAutobalanceDisplayTime = gpGlobals->curtime + AUTOBALANCE_TEXT_DELAY;
  10455. m_AutobalanceStatus = AutobalanceStatus::NEXT_ROUND;
  10456. }
  10457. }
  10458. }
  10459. void CCSGameRules::BalanceTeams( void )
  10460. {
  10461. // No autobalancing for people who abandon games in queued matchmaking
  10462. if ( IsQueuedMatchmaking() )
  10463. return;
  10464. int iTeamToSwap = TEAM_UNASSIGNED;
  10465. int iNumToSwap;
  10466. if (m_iNumCT > m_iNumTerrorist)
  10467. {
  10468. iTeamToSwap = TEAM_CT;
  10469. iNumToSwap = (m_iNumCT - m_iNumTerrorist)/2;
  10470. }
  10471. else if (m_iNumTerrorist > m_iNumCT)
  10472. {
  10473. iTeamToSwap = TEAM_TERRORIST;
  10474. iNumToSwap = (m_iNumTerrorist - m_iNumCT)/2;
  10475. }
  10476. else
  10477. {
  10478. return; // Teams are even.. Get out of here.
  10479. }
  10480. if (iNumToSwap > 3) // Don't swap more than 3 players at a time.. This is a naive method of avoiding infinite loops.
  10481. iNumToSwap = 3;
  10482. int iTragetTeam = TEAM_UNASSIGNED;
  10483. if ( iTeamToSwap == TEAM_CT )
  10484. {
  10485. iTragetTeam = TEAM_TERRORIST;
  10486. }
  10487. else if ( iTeamToSwap == TEAM_TERRORIST )
  10488. {
  10489. iTragetTeam = TEAM_CT;
  10490. }
  10491. else
  10492. {
  10493. // no valid team to swap
  10494. return;
  10495. }
  10496. m_AutoBalanceTraitors.MakeReliable();
  10497. m_AutoBalanceLoyalists.MakeReliable();
  10498. m_AutoBalanceLoyalists.AddAllPlayers();
  10499. for (int i = 0; i < iNumToSwap; i++)
  10500. {
  10501. // last person to join the server
  10502. int iHighestUserID = -1;
  10503. CCSPlayer *pPlayerToSwap = NULL;
  10504. // check if target team is full, exit if so
  10505. if ( TeamFull(iTragetTeam) )
  10506. break;
  10507. // search for player with highest UserID = most recently joined to switch over
  10508. for ( int j = 1; j <= gpGlobals->maxClients; j++ )
  10509. {
  10510. CCSPlayer *pPlayer = (CCSPlayer *)UTIL_PlayerByIndex( j );
  10511. if ( !pPlayer )
  10512. continue;
  10513. CCSBot *bot = dynamic_cast< CCSBot * >(pPlayer);
  10514. if ( bot )
  10515. continue; // don't swap bots - the bot system will handle that
  10516. if ( pPlayer &&
  10517. ( pPlayer->GetTeamNumber() == iTeamToSwap ) &&
  10518. ( engine->GetPlayerUserId( pPlayer->edict() ) > iHighestUserID ) &&
  10519. ( pPlayer->State_Get() != STATE_PICKINGCLASS ) )
  10520. {
  10521. iHighestUserID = engine->GetPlayerUserId( pPlayer->edict() );
  10522. pPlayerToSwap = pPlayer;
  10523. }
  10524. }
  10525. if ( pPlayerToSwap != NULL )
  10526. {
  10527. m_AutoBalanceTraitors.AddRecipient( pPlayerToSwap );
  10528. m_AutoBalanceLoyalists.RemoveRecipient( pPlayerToSwap );
  10529. pPlayerToSwap->SwitchTeam( iTragetTeam );
  10530. }
  10531. }
  10532. if ( m_AutoBalanceTraitors.GetRecipientCount() > 0 )
  10533. {
  10534. // Queue up traitor and loyalist display text
  10535. m_fAutobalanceDisplayTime = gpGlobals->curtime + AUTOBALANCE_TEXT_DELAY;
  10536. m_AutobalanceStatus = AutobalanceStatus::THIS_ROUND;
  10537. }
  10538. }
  10539. void CCSGameRules::HandleScrambleTeams( void )
  10540. {
  10541. CCSPlayer *pCSPlayer = NULL;
  10542. CUtlVector<CCSPlayer *> pListPlayers;
  10543. // add all the players (that are on CT or Terrorist) to our temp list
  10544. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  10545. {
  10546. pCSPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  10547. if ( pCSPlayer && ( pCSPlayer->GetTeamNumber() == TEAM_TERRORIST || pCSPlayer->GetTeamNumber() == TEAM_CT ) )
  10548. {
  10549. pListPlayers.AddToHead( pCSPlayer );
  10550. }
  10551. }
  10552. // sort the list
  10553. pListPlayers.Sort( ScramblePlayersSort );
  10554. int team = TEAM_INVALID;
  10555. bool assignToOpposingTeam = false;
  10556. for ( int i = 0 ; i < pListPlayers.Count() ; i++ )
  10557. {
  10558. pCSPlayer = pListPlayers[i];
  10559. if ( pCSPlayer )
  10560. {
  10561. //First assignment goes to random team
  10562. //Second assignment goes to the opposite
  10563. //Keep alternating until out of players.
  10564. if ( !assignToOpposingTeam )
  10565. {
  10566. team = ( rand() % 2 ) ? TEAM_TERRORIST : TEAM_CT;
  10567. }
  10568. else
  10569. {
  10570. team = ( team == TEAM_TERRORIST ) ? TEAM_CT : TEAM_TERRORIST;
  10571. }
  10572. pCSPlayer->SwitchTeam( team );
  10573. assignToOpposingTeam = !assignToOpposingTeam;
  10574. }
  10575. }
  10576. }
  10577. void CCSGameRules::OnTeamsSwappedAtRoundReset()
  10578. {
  10579. //
  10580. // This function is called both when the halftime ends and teams swap
  10581. // and when cc_SwapTeams was called on the server and m_bCompleteReset is being performed
  10582. //
  10583. if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender ||
  10584. m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender )
  10585. {
  10586. if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender )
  10587. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender;
  10588. else if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender )
  10589. m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender;
  10590. }
  10591. //
  10592. // Flip the timeouts as well
  10593. //
  10594. bool bTemp;
  10595. bTemp = m_bTerroristTimeOutActive;
  10596. m_bTerroristTimeOutActive = m_bCTTimeOutActive;
  10597. m_bCTTimeOutActive = bTemp;
  10598. float flTemp;
  10599. flTemp = m_flTerroristTimeOutRemaining;
  10600. m_flTerroristTimeOutRemaining = m_flCTTimeOutRemaining;
  10601. m_flCTTimeOutRemaining = flTemp;
  10602. int nTemp = m_nTerroristTimeOuts;
  10603. m_nTerroristTimeOuts = m_nCTTimeOuts;
  10604. m_nCTTimeOuts = nTemp;
  10605. g_voteControllerT->EndVoteImmediately();
  10606. g_voteControllerCT->EndVoteImmediately();
  10607. }
  10608. void CCSGameRules::HandleSwapTeams( void )
  10609. {
  10610. CCSPlayer *pCSPlayer = NULL;
  10611. CUtlVector<CCSPlayer *> pListPlayers;
  10612. CUtlVector<CCSPlayer *> pListCoaches;
  10613. // add all the players (that are on CT or Terrorist) to our temp list
  10614. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  10615. {
  10616. pCSPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  10617. if ( pCSPlayer && ( pCSPlayer->GetTeamNumber() == TEAM_TERRORIST || pCSPlayer->GetTeamNumber() == TEAM_CT ) )
  10618. {
  10619. pListPlayers.AddToHead( pCSPlayer );
  10620. }
  10621. else if ( pCSPlayer && pCSPlayer->IsCoach() )
  10622. {
  10623. pListCoaches.AddToHead( pCSPlayer );
  10624. }
  10625. }
  10626. for ( int i = 0 ; i < pListPlayers.Count() ; i++ )
  10627. {
  10628. pCSPlayer = pListPlayers[i];
  10629. if ( pCSPlayer )
  10630. {
  10631. int currentTeam = pCSPlayer->GetTeamNumber();
  10632. int newTeam = ( currentTeam == TEAM_TERRORIST ) ? TEAM_CT : TEAM_TERRORIST;
  10633. pCSPlayer->SwitchTeam( newTeam );
  10634. }
  10635. }
  10636. //coaches last
  10637. for ( int i = 0 ; i < pListCoaches.Count() ; i++ )
  10638. {
  10639. pCSPlayer = pListCoaches[i];
  10640. if ( pCSPlayer )
  10641. {
  10642. int currentTeam = pCSPlayer->GetAssociatedTeamNumber();
  10643. int newTeam = ( currentTeam == TEAM_TERRORIST ) ? TEAM_CT : TEAM_TERRORIST;
  10644. pCSPlayer->m_iCoachingTeam = newTeam;
  10645. }
  10646. }
  10647. //
  10648. // Flip the convars for custom team names and flags as well
  10649. //
  10650. CUtlString sTemp;
  10651. sTemp = mp_teamname_1.GetString();
  10652. mp_teamname_1.SetValue( mp_teamname_2.GetString() );
  10653. mp_teamname_2.SetValue( sTemp.Get() );
  10654. sTemp = mp_teamflag_1.GetString();
  10655. mp_teamflag_1.SetValue( mp_teamflag_2.GetString() );
  10656. mp_teamflag_2.SetValue( sTemp.Get() );
  10657. sTemp = mp_teamlogo_1.GetString();
  10658. mp_teamlogo_1.SetValue( mp_teamlogo_2.GetString() );
  10659. mp_teamlogo_2.SetValue( sTemp.Get() );
  10660. sTemp = mp_teammatchstat_1.GetString();
  10661. mp_teammatchstat_1.SetValue( mp_teammatchstat_2.GetString() );
  10662. mp_teammatchstat_2.SetValue( sTemp.Get() );
  10663. sTemp = mp_teamscore_1.GetString();
  10664. mp_teamscore_1.SetValue( mp_teamscore_2.GetString() );
  10665. mp_teamscore_2.SetValue( sTemp.Get() );
  10666. if ( ( mp_teamprediction_pct.GetInt() >= 1 ) && ( mp_teamprediction_pct.GetInt() <= 99 ) )
  10667. mp_teamprediction_pct.SetValue( 100 - mp_teamprediction_pct.GetInt() );
  10668. OnTeamsSwappedAtRoundReset();
  10669. }
  10670. // the following two functions cap the number of players on a team to five instead of basing it on the number of spawn points
  10671. int CCSGameRules::MaxNumPlayersOnTerrTeam()
  10672. {
  10673. if ( IsPlayingCoopMission() )
  10674. return m_iSpawnPointCount_Terrorist;
  10675. bool bRandomTSpawn = mp_randomspawn.GetInt() == 1 || mp_randomspawn.GetInt() == TEAM_TERRORIST;
  10676. return MIN(m_iMaxNumTerrorists, bRandomTSpawn ? MAX_PLAYERS : m_iSpawnPointCount_Terrorist);
  10677. }
  10678. int CCSGameRules::MaxNumPlayersOnCTTeam()
  10679. {
  10680. bool bRandomCTSpawn = mp_randomspawn.GetInt() == 1 || mp_randomspawn.GetInt() == TEAM_CT;
  10681. return MIN(m_iMaxNumCTs, bRandomCTSpawn ? MAX_PLAYERS : m_iSpawnPointCount_CT);
  10682. }
  10683. bool CCSGameRules::TeamFull( int team_id )
  10684. {
  10685. CheckLevelInitialized();
  10686. switch ( team_id )
  10687. {
  10688. case TEAM_TERRORIST:
  10689. return m_iNumTerrorist >= MaxNumPlayersOnTerrTeam();
  10690. case TEAM_CT:
  10691. return m_iNumCT >= MaxNumPlayersOnCTTeam();
  10692. case TEAM_SPECTATOR:
  10693. return GetGlobalTeam( TEAM_SPECTATOR )->GetNumPlayers() >= m_iSpectatorSlotCount;
  10694. }
  10695. return false;
  10696. }
  10697. bool CCSGameRules::WillTeamHaveRoomForPlayer( CCSPlayer* pThePlayer, int newTeam )
  10698. {
  10699. int teamSize = 0;
  10700. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
  10701. {
  10702. CCSPlayer *pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( clientIndex );
  10703. if ( pPlayer && ( pPlayer != pThePlayer ) )
  10704. {
  10705. if ( pPlayer->GetPendingTeamNumber() == newTeam )
  10706. teamSize++;
  10707. }
  10708. }
  10709. bool result = false;
  10710. switch( newTeam )
  10711. {
  10712. case TEAM_TERRORIST:
  10713. result = ( teamSize < MaxNumPlayersOnTerrTeam() );
  10714. break;
  10715. case TEAM_CT:
  10716. result = ( teamSize < MaxNumPlayersOnCTTeam() );
  10717. break;
  10718. case TEAM_SPECTATOR:
  10719. result = ( teamSize < m_iSpectatorSlotCount );
  10720. break;
  10721. }
  10722. return result;
  10723. }
  10724. int CCSGameRules::GetHumanTeam()
  10725. {
  10726. if ( FStrEq( "CT", mp_humanteam.GetString() ) )
  10727. {
  10728. return TEAM_CT;
  10729. }
  10730. else if ( FStrEq( "T", mp_humanteam.GetString() ) )
  10731. {
  10732. return TEAM_TERRORIST;
  10733. }
  10734. return TEAM_UNASSIGNED;
  10735. }
  10736. int CCSGameRules::SelectDefaultTeam( bool ignoreBots /*= false*/ )
  10737. {
  10738. if ( ignoreBots && ( FStrEq( cv_bot_join_team.GetString(), "T" ) || FStrEq( cv_bot_join_team.GetString(), "CT" ) ) )
  10739. {
  10740. ignoreBots = false; // don't ignore bots when they can't switch teams
  10741. }
  10742. if ( ignoreBots && !mp_autoteambalance.GetBool() )
  10743. {
  10744. ignoreBots = false; // don't ignore bots when they can't switch teams
  10745. }
  10746. int team = TEAM_UNASSIGNED;
  10747. int numTerrorists = m_iNumTerrorist;
  10748. int numCTs = m_iNumCT;
  10749. if ( ignoreBots )
  10750. {
  10751. numTerrorists = UTIL_HumansOnTeam( TEAM_TERRORIST );
  10752. numCTs = UTIL_HumansOnTeam( TEAM_CT );
  10753. }
  10754. // Choose the team that's lacking players
  10755. if ( numTerrorists < numCTs )
  10756. {
  10757. team = TEAM_TERRORIST;
  10758. }
  10759. else if ( numTerrorists > numCTs )
  10760. {
  10761. team = TEAM_CT;
  10762. }
  10763. // Choose the team that's losing
  10764. else if ( m_match.GetTerroristScore() < m_match.GetCTScore() )
  10765. {
  10766. team = TEAM_TERRORIST;
  10767. }
  10768. else if ( m_match.GetCTScore() < m_match.GetTerroristScore() )
  10769. {
  10770. team = TEAM_CT;
  10771. }
  10772. else
  10773. {
  10774. // Teams and scores are equal, pick a random team
  10775. if ( random->RandomInt( 0, 1 ) == 0 )
  10776. {
  10777. team = TEAM_CT;
  10778. }
  10779. else
  10780. {
  10781. team = TEAM_TERRORIST;
  10782. }
  10783. }
  10784. if ( TeamFull( team ) )
  10785. {
  10786. // Pick the opposite team
  10787. if ( team == TEAM_TERRORIST )
  10788. {
  10789. team = TEAM_CT;
  10790. }
  10791. else
  10792. {
  10793. team = TEAM_TERRORIST;
  10794. }
  10795. // No choices left
  10796. if ( TeamFull( team ) )
  10797. return TEAM_UNASSIGNED;
  10798. }
  10799. return team;
  10800. }
  10801. //checks to see if the desired team is stacked, returns true if it is
  10802. bool CCSGameRules::TeamStacked( int newTeam_id, int curTeam_id )
  10803. {
  10804. //players are allowed to change to their own team
  10805. if(newTeam_id == curTeam_id)
  10806. return false;
  10807. // if mp_limitteams is 0, don't check
  10808. if ( mp_limitteams.GetInt() == 0 )
  10809. return false;
  10810. // Queued matchmaking code forces correct teams already, don't check stacked teams here
  10811. if ( IsQueuedMatchmaking() )
  10812. return false;
  10813. switch ( newTeam_id )
  10814. {
  10815. case TEAM_TERRORIST:
  10816. if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR)
  10817. {
  10818. if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt() - 1))
  10819. return true;
  10820. else
  10821. return false;
  10822. }
  10823. else
  10824. {
  10825. if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt()))
  10826. return true;
  10827. else
  10828. return false;
  10829. }
  10830. break;
  10831. case TEAM_CT:
  10832. if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR)
  10833. {
  10834. if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt() - 1))
  10835. return true;
  10836. else
  10837. return false;
  10838. }
  10839. else
  10840. {
  10841. if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt()))
  10842. return true;
  10843. else
  10844. return false;
  10845. }
  10846. break;
  10847. }
  10848. return false;
  10849. }
  10850. //=========================================================
  10851. //=========================================================
  10852. bool CCSGameRules::FPlayerCanRespawn( CBasePlayer *pBasePlayer )
  10853. {
  10854. CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer );
  10855. if ( !pPlayer )
  10856. Error( "FPlayerCanRespawn: pPlayer=0" );
  10857. int nTeamNum = pPlayer->GetTeamNumber();
  10858. // Player cannot respawn twice in a round
  10859. if ( !pPlayer->IsAbleToInstantRespawn() && !IsWarmupPeriod() )
  10860. {
  10861. if ( pPlayer->m_iNumSpawns > 0 && m_bFirstConnected )
  10862. return false;
  10863. }
  10864. // If they're dead after the map has ended, and it's about to start the next round,
  10865. // wait for the round restart to respawn them.
  10866. if ( ( m_flRestartRoundTime - gpGlobals->curtime ) > MAX_TIME_TO_WAIT_BEFORE_ENTERING )
  10867. return false;
  10868. // Only valid team members can spawn
  10869. if ( nTeamNum != TEAM_CT && nTeamNum != TEAM_TERRORIST )
  10870. return false;
  10871. // Only players with a valid class can spawn
  10872. if ( pPlayer->GetClass() == CS_CLASS_NONE )
  10873. return false;
  10874. //if ( !IsPlayingGunGameProgressive() && !IsPlayingGunGameDeathmatch() && !IsWarmupPeriod() )
  10875. if ( !pPlayer->IsAbleToInstantRespawn() && !IsWarmupPeriod() )
  10876. {
  10877. // Player cannot respawn until next round if more than 20 seconds in
  10878. // Tabulate the number of players on each team.
  10879. m_iNumCT = GetGlobalTeam( TEAM_CT )->GetNumPlayers();
  10880. m_iNumTerrorist = GetGlobalTeam( TEAM_TERRORIST )->GetNumPlayers();
  10881. if ( m_iNumTerrorist > 0 && m_iNumCT > 0 )
  10882. {
  10883. // gurjeets - Between rounds m_fRoundStartTime gets set to cur_time + freeze_time ie
  10884. // sometime in the future. Thus, when we get here, players are still within the
  10885. // round restart time
  10886. // First time into a game, however, m_fRoundStartTime is 0. On this code path,
  10887. // the code that sets this properly only kicks in *after* players are spawned
  10888. // This results in the msg "player_spawned" not getting sent.
  10889. // Hence, added the check for m_fRoundStartTime being 0
  10890. if ( (m_fRoundStartTime != 0) &&
  10891. (gpGlobals->curtime > (m_fRoundStartTime + mp_join_grace_time.GetFloat() )) )
  10892. {
  10893. //If this player just connected and fadetoblack is on, then maybe
  10894. //the server admin doesn't want him peeking around.
  10895. color32_s clr = {0,0,0,255};
  10896. if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE )
  10897. {
  10898. UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT );
  10899. }
  10900. return false;
  10901. }
  10902. }
  10903. }
  10904. // Player cannot respawn while in the Choose Appearance menu
  10905. //if ( pPlayer->m_iMenu == Menu_ChooseAppearance )
  10906. // return false;
  10907. return true;
  10908. }
  10909. void CCSGameRules::LoadRoundDataInformation( char const *szFilename )
  10910. {
  10911. #if BACKUPSUPPORTZEROZERO
  10912. //
  10913. // Custom handling for loading 0:0 backup
  10914. //
  10915. if ( !V_strcmp( szFilename, g_szRoundBackupZeroZeroFileName ) )
  10916. { // Ensure that the warmup starts and stays paused
  10917. extern ConVar mp_warmup_pausetimer;
  10918. mp_warmup_pausetimer.SetValue( 1 );
  10919. StartWarmup();
  10920. return;
  10921. }
  10922. #endif
  10923. KeyValues *kvSaveFile = new KeyValues( "" );
  10924. KeyValues::AutoDelete autodelete_kvSaveFile( kvSaveFile );
  10925. autodelete_kvSaveFile->UsesEscapeSequences( true );
  10926. if ( !kvSaveFile->LoadFromFile( filesystem, szFilename ) )
  10927. {
  10928. Warning( "Failed to load file: %s\n", szFilename );
  10929. return;
  10930. }
  10931. UTIL_ClientPrintAll( HUD_PRINTTALK, CFmtStr( "Restoring match backup %s, score %d:%d after round %d\n",
  10932. kvSaveFile->GetString( "timestamp" ),
  10933. kvSaveFile->GetInt( "FirstHalfScore/team1" ) + kvSaveFile->GetInt( "SecondHalfScore/team1" ) + kvSaveFile->GetInt( "OvertimeScore/team1" ),
  10934. kvSaveFile->GetInt( "FirstHalfScore/team2" ) + kvSaveFile->GetInt( "SecondHalfScore/team2" ) + kvSaveFile->GetInt( "OvertimeScore/team2" ),
  10935. kvSaveFile->GetInt( "round" ) ) );
  10936. int numRoundsPlayed = kvSaveFile->GetInt( "round" );
  10937. int numOvertimePlaying = kvSaveFile->GetInt( "OvertimeScore/OvertimeID" );
  10938. bool bResetPlayerAccounts =
  10939. ( ( numRoundsPlayed < mp_maxrounds.GetInt() ) || !mp_overtime_maxrounds.GetInt() )
  10940. ? ( numRoundsPlayed == ( mp_maxrounds.GetInt() / 2 ) )
  10941. : !( ( numRoundsPlayed - mp_maxrounds.GetInt() ) % ( mp_overtime_maxrounds.GetInt() / 2 ) );
  10942. // Send all CT and T players to spectators for now
  10943. int nSavedSpectatorSlotsCount = m_iSpectatorSlotCount;
  10944. m_iSpectatorSlotCount = INT_MAX;
  10945. m_bForceTeamChangeSilent = true; // silence all the messages about players changing teams
  10946. m_bLoadingRoundBackupData = true; // make sure the game rules don't do any additional round restarts
  10947. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  10948. {
  10949. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  10950. if ( pPlayer && pPlayer->IsConnected() && !pPlayer->IsBot() &&
  10951. ( ( pPlayer->GetTeamNumber() == TEAM_CT ) || ( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) ||
  10952. ( pPlayer->GetPendingTeamNumber() == TEAM_CT ) || ( pPlayer->GetPendingTeamNumber() == TEAM_TERRORIST ) ) )
  10953. {
  10954. pPlayer->HandleCommand_JoinTeam( TEAM_SPECTATOR );
  10955. pPlayer->SetPendingTeamNum( TEAM_UNASSIGNED );
  10956. }
  10957. }
  10958. // Wipe cached accounts information and team assignments
  10959. if ( !IsQueuedMatchmaking() )
  10960. {
  10961. m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements();
  10962. }
  10963. //
  10964. // Reset match and streak data
  10965. //
  10966. {
  10967. m_iTotalRoundsPlayed = 0;
  10968. // Reset score info
  10969. m_match.Reset();
  10970. m_iNumConsecutiveTerroristLoses = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/NumConsecutiveTerroristLoses" );
  10971. m_iNumConsecutiveCTLoses = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/NumConsecutiveCTLoses" );
  10972. m_iAccountTerrorist = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/ExtraAccountTerrorist" );
  10973. m_iAccountCT = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/ExtraAccountCT" );
  10974. m_iLoserBonus = bResetPlayerAccounts ? TeamCashAwardValue( TeamCashAward::LOSER_BONUS ) : kvSaveFile->GetInt( "History/LoserBonus" );
  10975. // Reset hostage spawn indices
  10976. m_arrSelectedHostageSpawnIndices.RemoveAll();
  10977. if ( char const *szHostageSpawnIndices = kvSaveFile->GetString( "History/HostageSpawnIndices", NULL ) )
  10978. {
  10979. CUtlVector< char* > tagStrings;
  10980. V_SplitString( szHostageSpawnIndices, ",", tagStrings );
  10981. m_arrSelectedHostageSpawnIndices.EnsureCapacity( tagStrings.Count() );
  10982. FOR_EACH_VEC( tagStrings, iTagString )
  10983. {
  10984. m_arrSelectedHostageSpawnIndices.AddToTail( Q_atoi( tagStrings[iTagString] ) );
  10985. }
  10986. tagStrings.PurgeAndDeleteElements();
  10987. }
  10988. }
  10989. //
  10990. // Process save data, disregarding team assignment
  10991. // we'll figure out which team players are supposed to be on later
  10992. //
  10993. m_match.AddCTWins( kvSaveFile->GetInt( "FirstHalfScore/team1" ) );
  10994. m_match.AddTerroristWins( kvSaveFile->GetInt( "FirstHalfScore/team2" ) );
  10995. if ( numRoundsPlayed >= ( mp_maxrounds.GetInt() / 2 ) )
  10996. {
  10997. m_match.SetPhase( GAMEPHASE_PLAYING_SECOND_HALF );
  10998. m_match.AddCTWins( kvSaveFile->GetInt( "SecondHalfScore/team1" ) );
  10999. m_match.AddTerroristWins( kvSaveFile->GetInt( "SecondHalfScore/team2" ) );
  11000. }
  11001. if ( ( numRoundsPlayed >= mp_maxrounds.GetInt() ) || numOvertimePlaying )
  11002. {
  11003. int nOvertimeBasedOnRoundsPlayed = mp_overtime_maxrounds.GetInt() ? ( numRoundsPlayed - mp_maxrounds.GetInt() ) / mp_overtime_maxrounds.GetInt() : 0;
  11004. numOvertimePlaying = MAX( numOvertimePlaying, nOvertimeBasedOnRoundsPlayed + 1 );
  11005. m_match.GoToOvertime( numOvertimePlaying );
  11006. if ( mp_overtime_maxrounds.GetInt() &&
  11007. ( ( ( numRoundsPlayed - mp_maxrounds.GetInt() ) % mp_overtime_maxrounds.GetInt() ) < ( mp_overtime_maxrounds.GetInt() / 2 ) ) )
  11008. m_match.SetPhase( GAMEPHASE_PLAYING_FIRST_HALF );
  11009. m_match.AddCTWins( kvSaveFile->GetInt( "OvertimeScore/team1" ) );
  11010. m_match.AddTerroristWins( kvSaveFile->GetInt( "OvertimeScore/team2" ) );
  11011. }
  11012. // Switch team scores if they are supposed to be switched now
  11013. bool bAreTeamsPlayingSwitchedSides = AreTeamsPlayingSwitchedSides();
  11014. if ( bAreTeamsPlayingSwitchedSides )
  11015. {
  11016. m_match.SwapTeamScores();
  11017. }
  11018. for ( int r = 0; r < m_iMatchStats_RoundResults.Count(); r++ )
  11019. {
  11020. char szKey[30];
  11021. V_sprintf_safe( szKey, "RoundResults/round%d", r + 1 );
  11022. m_iMatchStats_RoundResults.GetForModify( r ) = kvSaveFile->GetInt( szKey );
  11023. }
  11024. for ( int r = 0; r < m_iMatchStats_PlayersAlive_T.Count(); r++ )
  11025. {
  11026. char szKey[30];
  11027. V_sprintf_safe( szKey, "PlayersAliveT/round%d", r + 1 );
  11028. m_iMatchStats_PlayersAlive_T.GetForModify( r ) = kvSaveFile->GetInt( szKey );
  11029. }
  11030. for ( int r = 0; r < m_iMatchStats_PlayersAlive_CT.Count(); r++ )
  11031. {
  11032. char szKey[30];
  11033. V_sprintf_safe( szKey, "PlayersAliveCT/round%d", r + 1 );
  11034. m_iMatchStats_PlayersAlive_CT.GetForModify( r ) = kvSaveFile->GetInt( szKey );
  11035. }
  11036. // Now assign the players to correct teams with correct stats
  11037. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11038. {
  11039. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  11040. if ( pPlayer && pPlayer->IsConnected() && !pPlayer->IsBot() )
  11041. {
  11042. CSteamID steamIdHumanPlayer;
  11043. if ( pPlayer->GetSteamID( &steamIdHumanPlayer ) )
  11044. {
  11045. int iTeamOrder = 0;
  11046. KeyValues *kvPlayerData = NULL;
  11047. for ( ; iTeamOrder < 2; ++ iTeamOrder )
  11048. {
  11049. kvPlayerData = kvSaveFile->FindKey( CFmtStr( "PlayersOnTeam%d/%u", iTeamOrder + 1, steamIdHumanPlayer.GetAccountID() ), false );
  11050. if ( kvPlayerData )
  11051. break;
  11052. }
  11053. if ( !kvPlayerData )
  11054. {
  11055. Warning( "Server checkpoint has no information for player %u: %s\n", steamIdHumanPlayer.GetAccountID(), pPlayer->GetPlayerName() );
  11056. }
  11057. else
  11058. {
  11059. int iRulesTeam = bAreTeamsPlayingSwitchedSides ? ( (iTeamOrder == 0)?TEAM_TERRORIST:TEAM_CT ) : ( (iTeamOrder == 0)?TEAM_CT:TEAM_TERRORIST );
  11060. pPlayer->HandleCommand_JoinTeam( iRulesTeam ); // join the correct team
  11061. pPlayer->HandleCommand_JoinClass(); // force join class now
  11062. pPlayer->MarkAsNotReceivingMoneyNextRound(); // after loading backup we shouldn't give out extra end round money
  11063. pPlayer->ResetFragCount();
  11064. pPlayer->SetEnemyKillTrackInfo( kvPlayerData->GetInt( "enemyKs" ), kvPlayerData->GetInt( "enemyHSs" ), kvPlayerData->GetInt( "enemy3Ks" ), kvPlayerData->GetInt( "enemy4Ks" ), kvPlayerData->GetInt( "enemy5Ks" ), kvPlayerData->GetInt( "enemyKAg" ) );
  11065. pPlayer->SetEnemyFirstKills( kvPlayerData->GetInt( "firstKs" ), kvPlayerData->GetInt( "clutchKs" ) );
  11066. pPlayer->SetEnemyWeaponKills( kvPlayerData->GetInt( "kills_weapon_pistol" ), kvPlayerData->GetInt( "kills_weapon_sniper" ) );
  11067. pPlayer->IncrementFragCount( kvPlayerData->GetInt( "kills" ), -1 ); // -1 headshots is a special flag indicating that we are faking the call here, but it will still record kill info in QMM
  11068. pPlayer->ResetAssistsCount();
  11069. pPlayer->IncrementAssistsCount( kvPlayerData->GetInt( "assists" ) );
  11070. pPlayer->ResetDeathCount();
  11071. pPlayer->IncrementDeathCount( kvPlayerData->GetInt( "deaths" ) );
  11072. pPlayer->SetNumMVPs( kvPlayerData->GetInt( "mvps" ) );
  11073. pPlayer->ClearContributionScore();
  11074. pPlayer->AddContributionScore( kvPlayerData->GetInt( "score" ) );
  11075. pPlayer->ResetNumRoundKills();
  11076. int iCashAccount = kvPlayerData->GetInt( "cash" );
  11077. if ( bResetPlayerAccounts )
  11078. {
  11079. iCashAccount = GetOvertimePlaying() ? mp_overtime_startmoney.GetInt() : mp_startmoney.GetInt();
  11080. }
  11081. pPlayer->AddAccount( iCashAccount - pPlayer->GetAccountBalance(), true, false, NULL );
  11082. pPlayer->RemoveAllItems( true );
  11083. if ( !bResetPlayerAccounts )
  11084. {
  11085. for ( KeyValues *kvItem = kvPlayerData->FindKey( "Items" )->GetFirstValue(); kvItem; kvItem = kvItem->GetNextValue() )
  11086. {
  11087. pPlayer->GiveNamedItem( kvItem->GetString() );
  11088. }
  11089. pPlayer->SetArmorValue( kvPlayerData->GetInt( "armor" ) );
  11090. pPlayer->m_bHasHelmet = kvPlayerData->GetBool( "helmet" );
  11091. if ( kvPlayerData->GetBool( "defusekit" ) && ( pPlayer->GetTeamNumber() == TEAM_CT ) )
  11092. pPlayer->GiveDefuser();
  11093. }
  11094. for ( int r = 0; r < MAX_MATCH_STATS_ROUNDS; r++ )
  11095. {
  11096. char szKey[64] = {};
  11097. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Kills/round%d", r + 1 );
  11098. pPlayer->m_iMatchStats_Kills.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11099. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Damage/round%d", r + 1 );
  11100. pPlayer->m_iMatchStats_Damage.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11101. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/EquipmentValue/round%d", r + 1 );
  11102. pPlayer->m_iMatchStats_EquipmentValue.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11103. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/MoneySaved/round%d", r + 1 );
  11104. pPlayer->m_iMatchStats_MoneySaved.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11105. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/KillReward/round%d", r + 1 );
  11106. pPlayer->m_iMatchStats_KillReward.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11107. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/LiveTime/round%d", r + 1 );
  11108. pPlayer->m_iMatchStats_LiveTime.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11109. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Deaths/round%d", r + 1 );
  11110. pPlayer->m_iMatchStats_Deaths.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11111. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Assists/round%d", r + 1 );
  11112. pPlayer->m_iMatchStats_Assists.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11113. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/HeadShotKills/round%d", r + 1 );
  11114. pPlayer->m_iMatchStats_HeadShotKills.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11115. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Objective/round%d", r + 1 );
  11116. pPlayer->m_iMatchStats_Objective.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11117. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/CashEarned/round%d", r + 1 );
  11118. pPlayer->m_iMatchStats_CashEarned.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11119. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/UtilityDamage/round%d", r + 1 );
  11120. pPlayer->m_iMatchStats_UtilityDamage.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11121. V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/EnemiesFlashed/round%d", r + 1 );
  11122. pPlayer->m_iMatchStats_EnemiesFlashed.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0;
  11123. }
  11124. }
  11125. }
  11126. }
  11127. }
  11128. // Restore spectators
  11129. m_iSpectatorSlotCount = nSavedSpectatorSlotsCount;
  11130. m_bForceTeamChangeSilent = false;
  11131. // Tell that we have loaded the checkpoint
  11132. Warning( "Loaded server checkpoint %s, starting match with score %d:%d after round %d\n",
  11133. kvSaveFile->GetString( "timestamp" ),
  11134. kvSaveFile->GetInt( "FirstHalfScore/team1" ) + kvSaveFile->GetInt( "SecondHalfScore/team1" ),
  11135. kvSaveFile->GetInt( "FirstHalfScore/team2" ) + kvSaveFile->GetInt( "SecondHalfScore/team2" ),
  11136. kvSaveFile->GetInt( "round" ) );
  11137. // Make sure we restart without warmup if warmup was active
  11138. m_bWarmupPeriod = false;
  11139. m_bCompleteReset = false;
  11140. m_fWarmupPeriodStart = -1;
  11141. m_flRestartRoundTime = 0;
  11142. // Notify clients of potential phase change
  11143. if ( IGameEvent * event = gameeventmanager->CreateEvent( "announce_phase_end" ) )
  11144. {
  11145. gameeventmanager->FireEvent( event );
  11146. }
  11147. m_phaseChangeAnnouncementTime = 0.0f;
  11148. if ( mp_backup_restore_load_autopause.GetBool() )
  11149. {
  11150. // pause the match
  11151. SetMatchWaitingForResume( true );
  11152. }
  11153. // Restart round
  11154. EndRound();
  11155. }
  11156. static void Helper_CleanupStringForSaveRoundDataInformation( char *pch )
  11157. {
  11158. int nLength = Q_strlen( pch );
  11159. char *pchStart = pch;
  11160. for ( ; *pch; ++pch )
  11161. {
  11162. if (
  11163. ( ( pch[ 0 ] >= 'a' ) && ( pch[ 0 ] <= 'z' ) ) ||
  11164. ( ( pch[ 0 ] >= 'A' ) && ( pch[ 0 ] <= 'Z' ) ) ||
  11165. ( ( pch[ 0 ] >= '0' ) && ( pch[ 0 ] <= '9' ) )
  11166. )
  11167. ;
  11168. else
  11169. {
  11170. Q_memmove( pch, pch + 1, nLength - ( pch - pchStart ) );
  11171. --pch;
  11172. }
  11173. }
  11174. }
  11175. void CCSGameRules::SaveRoundDataInformation( char const *szFilenameOverride )
  11176. {
  11177. /** Removed for partner depot **/
  11178. }
  11179. CCSGameRules::CQMMPlayerData_t * CCSGameRules::QueuedMatchmakingPlayersDataFindOrCreate( CCSPlayer *pPlayer )
  11180. {
  11181. CSteamID steamIdHumanPlayer;
  11182. if ( !pPlayer->GetSteamID( &steamIdHumanPlayer ) )
  11183. return NULL;
  11184. if ( !steamIdHumanPlayer.IsValid() || !steamIdHumanPlayer.BIndividualAccount() )
  11185. return NULL;
  11186. if ( steamIdHumanPlayer.GetAccountID() != pPlayer->GetHumanPlayerAccountID() )
  11187. return NULL;
  11188. CCSGameRules::CQMMPlayerData_t *pQMM = QueuedMatchmakingPlayersDataFind( steamIdHumanPlayer.GetAccountID() );
  11189. if ( pQMM ) // Already exists
  11190. return pQMM;
  11191. // There are some cases where we cannot create the player data
  11192. // or are supposed to have all entries previously created upon GC request
  11193. if ( IsQueuedMatchmaking() )
  11194. return NULL;
  11195. // Prepare for creating the entry
  11196. bool bControllingBot = pPlayer->IsControllingBot();
  11197. bool bTeamsArePlayingSwitchedSides = AreTeamsPlayingSwitchedSides();
  11198. int iTeamOrder = 0; // see "CCSGameRules::SaveRoundDataInformation" processing
  11199. switch ( pPlayer->GetTeamNumber() )
  11200. {
  11201. case TEAM_CT:
  11202. case TEAM_TERRORIST:
  11203. iTeamOrder = ( ( pPlayer->GetTeamNumber() == TEAM_CT ) == bTeamsArePlayingSwitchedSides ) ? 1 : 0;
  11204. break;
  11205. default:
  11206. return NULL;
  11207. }
  11208. //
  11209. // Otherwise it is valid to create a new entry
  11210. //
  11211. CQMMPlayerData_t &qmmPlayerData = *new CQMMPlayerData_t;
  11212. qmmPlayerData.m_uiPlayerAccountId = steamIdHumanPlayer.GetAccountID();
  11213. qmmPlayerData.m_iDraftIndex = iTeamOrder * 5;
  11214. Q_strncpy( qmmPlayerData.m_chPlayerName, pPlayer->GetPlayerName(), sizeof( qmmPlayerData.m_chPlayerName ) );
  11215. qmmPlayerData.m_numKills = bControllingBot ? pPlayer->GetBotPreControlData().m_iFrags : pPlayer->FragCount();
  11216. qmmPlayerData.m_numAssists = bControllingBot ? pPlayer->GetBotPreControlData().m_iAssists : pPlayer->AssistsCount();
  11217. qmmPlayerData.m_numDeaths = bControllingBot ? pPlayer->GetBotPreControlData().m_iDeaths : pPlayer->DeathCount();
  11218. qmmPlayerData.m_numScorePoints = pPlayer->GetScore();
  11219. qmmPlayerData.m_numMVPs = pPlayer->GetNumMVPs();
  11220. qmmPlayerData.m_cash = bControllingBot ? pPlayer->GetBotPreControlData().m_iAccount : pPlayer->GetAccountBalance();
  11221. qmmPlayerData.m_bReceiveNoMoneyNextRound = !pPlayer->DoesPlayerGetRoundStartMoney();
  11222. int nKills, iEnemyKillHeadshots, iEnemy3Ks, iEnemy4Ks, iEnemy5Ks, iEnemyKillsAgg;
  11223. pPlayer->GetEnemyKillTrackInfo( nKills, iEnemyKillHeadshots, iEnemy3Ks, iEnemy4Ks, iEnemy5Ks, iEnemyKillsAgg );
  11224. qmmPlayerData.m_numEnemyKills = nKills;
  11225. qmmPlayerData.m_numEnemyKillHeadshots = iEnemyKillHeadshots;
  11226. qmmPlayerData.m_numEnemy3Ks = iEnemy3Ks;
  11227. qmmPlayerData.m_numEnemy4Ks = iEnemy4Ks;
  11228. qmmPlayerData.m_numEnemy5Ks = iEnemy5Ks;
  11229. qmmPlayerData.m_numEnemyKillsAgg = iEnemyKillsAgg;
  11230. qmmPlayerData.m_numRoundsWon = pPlayer->m_iRoundsWon;
  11231. for ( int i = 0; i < MAX_MATCH_STATS_ROUNDS; i++ )
  11232. {
  11233. qmmPlayerData.m_iMatchStats_Kills[ i ] = pPlayer->m_iMatchStats_Kills.Get( i );
  11234. qmmPlayerData.m_iMatchStats_Damage[ i ] = pPlayer->m_iMatchStats_Damage.Get( i );
  11235. qmmPlayerData.m_iMatchStats_MoneySaved[ i ] = pPlayer->m_iMatchStats_MoneySaved.Get( i );
  11236. qmmPlayerData.m_iMatchStats_EquipmentValue[ i ] = pPlayer->m_iMatchStats_EquipmentValue.Get( i );
  11237. qmmPlayerData.m_iMatchStats_KillReward[ i ] = pPlayer->m_iMatchStats_KillReward.Get( i );
  11238. qmmPlayerData.m_iMatchStats_LiveTime[ i ] = pPlayer->m_iMatchStats_LiveTime.Get( i );
  11239. qmmPlayerData.m_iMatchStats_Deaths[ i ] = pPlayer->m_iMatchStats_Deaths.Get( i );
  11240. qmmPlayerData.m_iMatchStats_Assists[ i ] = pPlayer->m_iMatchStats_Assists.Get( i );
  11241. qmmPlayerData.m_iMatchStats_HeadShotKills[ i ] = pPlayer->m_iMatchStats_HeadShotKills.Get( i );
  11242. qmmPlayerData.m_iMatchStats_Objective[ i ] = pPlayer->m_iMatchStats_Objective.Get( i );
  11243. qmmPlayerData.m_iMatchStats_CashEarned[ i ] = pPlayer->m_iMatchStats_CashEarned.Get( i );
  11244. qmmPlayerData.m_iMatchStats_UtilityDamage[ i ] = pPlayer->m_iMatchStats_UtilityDamage.Get( i );
  11245. qmmPlayerData.m_iMatchStats_EnemiesFlashed[ i ] = pPlayer->m_iMatchStats_EnemiesFlashed.Get( i );
  11246. }
  11247. m_mapQueuedMatchmakingPlayersData.InsertOrReplace( qmmPlayerData.m_uiPlayerAccountId, &qmmPlayerData );
  11248. return &qmmPlayerData;
  11249. }
  11250. void CCSGameRules::IncrementAndTerminateRound( float tmDelay, int reason )
  11251. {
  11252. bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE );
  11253. if ( roundIsAlreadyOver )
  11254. return;
  11255. if ( reason == Round_Draw )
  11256. {
  11257. m_match.IncrementRound( 1 );
  11258. }
  11259. else if ( reason == Terrorists_Win )
  11260. {
  11261. m_match.AddTerroristWins( 1 );
  11262. }
  11263. else if ( reason == CTs_Win )
  11264. {
  11265. m_match.AddCTWins( 1 );
  11266. if ( IsPlayingCoopMission() )
  11267. {
  11268. if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) )
  11269. pTeam->MarkSurrendered();
  11270. }
  11271. }
  11272. else
  11273. {
  11274. Assert( !"IncrementAndTerminateRound not implemented for the reason passed" );
  11275. return;
  11276. }
  11277. TerminateRound( tmDelay, reason );
  11278. }
  11279. static inline int GetNumPlayers( CTeam *pTeam )
  11280. {
  11281. return pTeam ? pTeam->GetNumPlayers() : 0;
  11282. }
  11283. void CCSGameRules::TerminateRound(float tmDelay, int iReason )
  11284. {
  11285. if ( m_iRoundWinStatus != WINNER_NONE )
  11286. return;
  11287. variant_t emptyVariant;
  11288. int iWinnerTeam = WINNER_NONE;
  11289. const char *text = "UNKNOWN";
  11290. // extend the time a bit if we have items to display
  11291. float flItemDelay = tmDelay;
  11292. // if ( m_ItemsPtrDroppedDuringMatch.Count() > 0 && !IsPlayingAnyCompetitiveStrictRuleset() )
  11293. // flItemDelay += MIN(5, m_ItemsPtrDroppedDuringMatch.Count())*0.75;
  11294. // UTIL_ClientPrintAll( HUD_PRINTCENTER, sentence );
  11295. if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender )
  11296. {
  11297. iReason = Terrorists_Surrender;
  11298. if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) )
  11299. pTeam->MarkSurrendered();
  11300. }
  11301. else if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender )
  11302. {
  11303. iReason = CTs_Surrender;
  11304. if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) )
  11305. pTeam->MarkSurrendered();
  11306. }
  11307. switch ( iReason )
  11308. {
  11309. // Terror wins:
  11310. case Target_Bombed:
  11311. text = "#SFUI_Notice_Target_Bombed";
  11312. iWinnerTeam = WINNER_TER;
  11313. break;
  11314. case Terrorists_Escaped:
  11315. text = "#SFUI_Notice_Terrorists_Escaped";
  11316. iWinnerTeam = WINNER_TER;
  11317. break;
  11318. case Terrorists_Win:
  11319. text = "#SFUI_Notice_Terrorists_Win";
  11320. iWinnerTeam = WINNER_TER;
  11321. break;
  11322. case Hostages_Not_Rescued:
  11323. text = "#SFUI_Notice_Hostages_Not_Rescued";
  11324. iWinnerTeam = WINNER_TER;
  11325. break;
  11326. case Terrorists_Planted:
  11327. text = "#SFUI_Notice_Terrorists_Planted";
  11328. iWinnerTeam = WINNER_TER;
  11329. break;
  11330. case CTs_ReachedHostage:
  11331. text = "#SFUI_Notice_CTs_ReachedHostage";
  11332. iWinnerTeam = WINNER_CT;
  11333. break;
  11334. case CTs_PreventEscape:
  11335. text = "#SFUI_Notice_CTs_PreventEscape";
  11336. iWinnerTeam = WINNER_CT;
  11337. break;
  11338. case Escaping_Terrorists_Neutralized:
  11339. text = "#SFUI_Notice_Escaping_Terrorists_Neutralized";
  11340. iWinnerTeam = WINNER_CT;
  11341. break;
  11342. case Bomb_Defused:
  11343. text = "#SFUI_Notice_Bomb_Defused";
  11344. iWinnerTeam = WINNER_CT;
  11345. break;
  11346. case CTs_Win:
  11347. text = "#SFUI_Notice_CTs_Win";
  11348. iWinnerTeam = WINNER_CT;
  11349. break;
  11350. case All_Hostages_Rescued:
  11351. text = "#SFUI_Notice_All_Hostages_Rescued";
  11352. iWinnerTeam = WINNER_CT;
  11353. break;
  11354. case Target_Saved:
  11355. text = "#SFUI_Notice_Target_Saved";
  11356. iWinnerTeam = WINNER_CT;
  11357. break;
  11358. case Terrorists_Not_Escaped:
  11359. text = "#SFUI_Notice_Terrorists_Not_Escaped";
  11360. iWinnerTeam = WINNER_CT;
  11361. break;
  11362. // no winners:
  11363. case Game_Commencing:
  11364. text = "#SFUI_Notice_Game_Commencing";
  11365. iWinnerTeam = WINNER_DRAW;
  11366. break;
  11367. case Round_Draw:
  11368. text = "#SFUI_Notice_Round_Draw";
  11369. iWinnerTeam = WINNER_DRAW;
  11370. break;
  11371. case Terrorists_Surrender:
  11372. text = "#SFUI_Notice_Terrorists_Surrender";
  11373. iWinnerTeam = WINNER_CT;
  11374. break;
  11375. case CTs_Surrender:
  11376. text = "#SFUI_Notice_CTs_Surrender";
  11377. iWinnerTeam = WINNER_TER;
  11378. break;
  11379. default:
  11380. DevMsg("TerminateRound: unknown round end ID %i\n", iReason );
  11381. break;
  11382. }
  11383. m_iRoundWinStatus = iWinnerTeam;
  11384. m_eRoundWinReason = iReason;
  11385. m_flRestartRoundTime = gpGlobals->curtime + flItemDelay;
  11386. if ( ( m_iRoundWinStatus == WINNER_TER ) || ( m_iRoundWinStatus == WINNER_CT ) )
  11387. { // If the round is ending and there's a pending hook for MVP calculation
  11388. // and player scoring then run these calculations here before the round
  11389. // officially ends and the game goes on to save round stats or goes to
  11390. // intermission and submits match outcome
  11391. if ( m_pfnCalculateEndOfRoundMVPHook )
  11392. m_pfnCalculateEndOfRoundMVPHook->CalculateEndOfRoundMVP();
  11393. else // run default MVP rules without any special hooks
  11394. CalculateEndOfRoundMVP();
  11395. }
  11396. if ( ( ( m_iRoundWinStatus == WINNER_TER ) || ( m_iRoundWinStatus == WINNER_CT ) )
  11397. && Helper_mp_backup_round_IsEnabled() )
  11398. {
  11399. SaveRoundDataInformation();
  11400. }
  11401. if ( iWinnerTeam == WINNER_CT )
  11402. {
  11403. for( int i=0;i<g_Hostages.Count();i++ )
  11404. g_Hostages[i]->AcceptInput( "CTsWin", NULL, NULL, emptyVariant, 0 );
  11405. }
  11406. else if ( iWinnerTeam == WINNER_TER )
  11407. {
  11408. for( int i=0;i<g_Hostages.Count();i++ )
  11409. g_Hostages[i]->AcceptInput( "TerroristsWin", NULL, NULL, emptyVariant, 0 );
  11410. }
  11411. else
  11412. {
  11413. Assert( iWinnerTeam == WINNER_NONE || iWinnerTeam == WINNER_DRAW );
  11414. }
  11415. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11416. {
  11417. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  11418. if (pPlayer)
  11419. {
  11420. // have all players do any end of round bookkeeping
  11421. pPlayer->HandleEndOfRound();
  11422. }
  11423. }
  11424. // [tj] Check for any non-player-specific achievements.
  11425. ProcessEndOfRoundAchievements(iWinnerTeam, iReason);
  11426. if( iReason != Game_Commencing )
  11427. {
  11428. // [pfreese] Setup and send win panel event (primarily funfact data)
  11429. FunFact funfact;
  11430. funfact.szLocalizationToken = "";
  11431. funfact.iPlayer = 0;
  11432. funfact.iData1 = 0;
  11433. funfact.iData2 = 0;
  11434. funfact.iData3 = 0;
  11435. m_pFunFactManager->GetRoundEndFunFact( iWinnerTeam, (e_RoundEndReason)iReason, funfact);
  11436. //Send all the info needed for the win panel
  11437. IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_round" );
  11438. if ( winEvent )
  11439. {
  11440. // determine what categories to send
  11441. if ( GetRoundRemainingTime() <= 0 )
  11442. {
  11443. // timer expired, defenders win
  11444. // show total time that was defended
  11445. winEvent->SetBool( "show_timer_defend", true );
  11446. winEvent->SetInt( "timer_time", m_iRoundTime );
  11447. }
  11448. else
  11449. {
  11450. // attackers win
  11451. // show time it took for them to win
  11452. winEvent->SetBool( "show_timer_attack", true );
  11453. int iTimeElapsed = GetRoundElapsedTime();
  11454. winEvent->SetInt( "timer_time", iTimeElapsed );
  11455. }
  11456. winEvent->SetInt( "final_event", iReason );
  11457. // Set the fun fact data in the event
  11458. winEvent->SetString( "funfact_token", funfact.szLocalizationToken);
  11459. winEvent->SetInt( "funfact_player", funfact.iPlayer );
  11460. winEvent->SetInt( "funfact_data1", funfact.iData1 );
  11461. winEvent->SetInt( "funfact_data2", funfact.iData2 );
  11462. winEvent->SetInt( "funfact_data3", funfact.iData3 );
  11463. gameeventmanager->FireEvent( winEvent );
  11464. }
  11465. }
  11466. // [tj] Inform players that the round is over
  11467. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11468. {
  11469. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  11470. if(pPlayer)
  11471. {
  11472. pPlayer->OnRoundEnd(iWinnerTeam, iReason);
  11473. if ( ( pPlayer->GetTeamNumber() == iWinnerTeam ) && ( IsPlayingClassic() ) )
  11474. {
  11475. ++ pPlayer->m_iRoundsWon;
  11476. // Keep track in QMM data
  11477. if ( pPlayer->GetHumanPlayerAccountID() )
  11478. {
  11479. if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( pPlayer->GetHumanPlayerAccountID() ) )
  11480. {
  11481. pQMM->m_numRoundsWon = pPlayer->m_iRoundsWon;
  11482. }
  11483. }
  11484. }
  11485. if ( IsPlayingCoopGuardian() &&
  11486. (iReason == CTs_ReachedHostage || iReason == Terrorists_Planted) &&
  11487. (pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST) )
  11488. {
  11489. color32_s clr = {0,0,0,255};
  11490. UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT );
  11491. }
  11492. if ( IsPlayingCoopMission() )
  11493. {
  11494. color32_s clr = { 0, 0, 0, 255 };
  11495. UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT );
  11496. }
  11497. }
  11498. }
  11499. if ( IsPlayingCoopGuardian() && ( iReason == CTs_ReachedHostage || iReason == Terrorists_Planted ) )
  11500. FreezePlayers();
  11501. if ( IsPlayingCoopMission() )
  11502. {
  11503. CGameCoopMissionManager *pManager = GetCoopMissionManager();
  11504. // we really dont have one
  11505. if ( !pManager )
  11506. DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't fire round ended outputs without it!\n" );
  11507. else if ( iReason == Terrorists_Win )
  11508. {
  11509. if ( GetRoundRemainingTime() <= 0 )
  11510. pManager->SetRoundLostTime();
  11511. else
  11512. pManager->SetRoundLostKilled();
  11513. }
  11514. }
  11515. IGameEvent * event = gameeventmanager->CreateEvent( "round_end" );
  11516. if ( event )
  11517. {
  11518. event->SetInt( "winner", iWinnerTeam );
  11519. event->SetInt( "reason", iReason );
  11520. event->SetString( "message", text );
  11521. event->SetInt( "priority", 6 ); // round_end
  11522. event->SetInt( "player_count", GetNumPlayers( GetGlobalTeam( TEAM_CT ) ) + GetNumPlayers( GetGlobalTeam( TEAM_TERRORIST ) ) );
  11523. if ( ( iWinnerTeam == TEAM_CT ) && ( iReason == Bomb_Defused ) )
  11524. {
  11525. // Check if there are Terrorists alive
  11526. bool bTerroristsAlive = false;
  11527. float flSecondsTillDetonationRemaining = 0.0f;
  11528. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  11529. {
  11530. CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  11531. if ( !pCheckPlayer )
  11532. continue;
  11533. if ( ( pCheckPlayer->GetTeamNumber() == TEAM_TERRORIST )
  11534. && pCheckPlayer->IsAlive() )
  11535. {
  11536. bTerroristsAlive = true;
  11537. break;
  11538. }
  11539. if ( pCheckPlayer->GetTeamNumber() == TEAM_CT )
  11540. {
  11541. float flTimeTillDetonation = pCheckPlayer->GetDefusedBombWithThisTimeRemaining();
  11542. if ( flTimeTillDetonation > flSecondsTillDetonationRemaining )
  11543. flSecondsTillDetonationRemaining = flTimeTillDetonation;
  11544. }
  11545. }
  11546. if ( !bTerroristsAlive && ( flSecondsTillDetonationRemaining > 0 ) )
  11547. { // Can only play legacy radio if no terrorists are alive (so all players are focused on the defuse)
  11548. if ( flSecondsTillDetonationRemaining < 1.0f )
  11549. { // Always play the coolest brag for a close defuse
  11550. event->SetInt( "legacy", 3 );
  11551. }
  11552. else if ( flSecondsTillDetonationRemaining < 3.0f )
  11553. {
  11554. if ( RandomFloat() < 0.75f ) // 75% chance for a medium tier brag
  11555. event->SetInt( "legacy", 2 );
  11556. }
  11557. else if ( flSecondsTillDetonationRemaining < 7.0f )
  11558. {
  11559. if ( RandomFloat() < 0.5f ) // 50% chance for a low tier brag
  11560. event->SetInt( "legacy", 1 );
  11561. }
  11562. }
  11563. }
  11564. gameeventmanager->FireEvent( event );
  11565. }
  11566. if ( ( iReason == CTs_Surrender || iReason == Terrorists_Surrender ) && !IsQueuedMatchmaking() )
  11567. {
  11568. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  11569. GoToIntermission();
  11570. }
  11571. if ( ( GetMapRemainingTime() == 0.0f ) && !IsQueuedMatchmaking() )
  11572. {
  11573. UTIL_LogPrintf("World triggered \"Intermission_Time_Limit\"\n");
  11574. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  11575. GoToIntermission();
  11576. }
  11577. // Only update bot difficulty if playing in an Online-game (In offline mode, bot difficulty is set by user)
  11578. if ( !IsPlayingOffline() )
  11579. {
  11580. // Determine the difficulty level that the bots should be at to compete
  11581. ModifyRealtimeBotDifficulty();
  11582. }
  11583. if ( ( static_cast< e_RoundEndReason > ( iReason ) != Game_Commencing ) && !m_bLoadingRoundBackupData )
  11584. {
  11585. // Perform round-related processing at the point when a round winner has been determined
  11586. RoundWin();
  11587. }
  11588. // This is a fantastic opportunity to submit round results to GC
  11589. if ( iReason != Game_Commencing )
  11590. {
  11591. ReportRoundEndStatsToGC();
  11592. if ( Helper_ShouldBroadcastCoopScoreLeaderboardData() )
  11593. {
  11594. CReliableBroadcastRecipientFilter filter;
  11595. CCSUsrMsg_ScoreLeaderboardData msg;
  11596. Helper_FillScoreLeaderboardData( *msg.mutable_data() );
  11597. SendUserMessage( filter, CS_UM_ScoreLeaderboardData, msg );
  11598. }
  11599. if ( IsPlayingCoopGuardian()
  11600. && ( iWinnerTeam == ( IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT ) ) )
  11601. {
  11602. // If human team wins then end the match right now!
  11603. m_match.SetPhase( GAMEPHASE_MATCH_ENDED );
  11604. m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt();
  11605. GoToIntermission();
  11606. }
  11607. else if ( IsPlayingCoopMission() && iWinnerTeam == TEAM_CT )
  11608. {
  11609. if ( IsPlayingCoopMission() )
  11610. {
  11611. CGameCoopMissionManager *pManager = GetCoopMissionManager();
  11612. // we really dont have one
  11613. if ( !pManager )
  11614. DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't fire MissionCompleted outputs without it!\n" );
  11615. else
  11616. pManager->SetMissionCompleted();
  11617. }
  11618. // If human team wins then end the match right now!
  11619. m_match.SetPhase( GAMEPHASE_MATCH_ENDED );
  11620. m_phaseChangeAnnouncementTime = gpGlobals->curtime + 1.5;
  11621. GoToIntermission();
  11622. }
  11623. }
  11624. if ( iReason == Game_Commencing )
  11625. {
  11626. m_bWarmupPeriod = true;
  11627. }
  11628. }
  11629. void CCSGameRules::CreateEndMatchMapGroupVoteOptions( void )
  11630. {
  11631. CUtlVector< int > arrVoteCandidates;
  11632. if ( g_pGameTypes )
  11633. {
  11634. const char* mapGroupName = gpGlobals->mapGroupName.ToCStr();
  11635. const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName );
  11636. if ( mapsInGroup )
  11637. {
  11638. int nCount = mapsInGroup->Count();
  11639. int nCurrentMapIndex = -1;
  11640. for ( int iMap = 0; iMap < nCount; ++ iMap )
  11641. {
  11642. arrVoteCandidates.AddToTail( iMap );
  11643. if ( !V_stricmp( mapsInGroup->Element( iMap ), STRING( gpGlobals->mapname ) ) )
  11644. nCurrentMapIndex = iMap;
  11645. }
  11646. // Remove current map from pool if mp_endmatch_votenextmap_keepcurrent is set to 0.
  11647. if ( !mp_endmatch_votenextmap_keepcurrent.GetBool() && nCurrentMapIndex >= 0 )
  11648. arrVoteCandidates.Remove( nCurrentMapIndex );
  11649. while ( arrVoteCandidates.Count() > 10 )
  11650. {
  11651. int nRemoveIndex = RandomInt( 0, arrVoteCandidates.Count() - 1 );
  11652. if ( mp_endmatch_votenextmap_keepcurrent.GetBool() && ( arrVoteCandidates[nRemoveIndex] == nCurrentMapIndex ) )
  11653. {
  11654. nRemoveIndex ++;
  11655. nRemoveIndex %= arrVoteCandidates.Count();
  11656. }
  11657. arrVoteCandidates.Remove( nRemoveIndex );
  11658. }
  11659. }
  11660. }
  11661. for ( int iVoteOption = 0; iVoteOption < MAX_ENDMATCH_VOTE_PANELS; ++ iVoteOption )
  11662. m_nEndMatchMapGroupVoteOptions.Set( iVoteOption, arrVoteCandidates.IsValidIndex( iVoteOption ) ? arrVoteCandidates[iVoteOption] : -1 );
  11663. }
  11664. void CCSGameRules::ReportRoundEndStatsToGC( CMsgGCCStrike15_v2_MatchmakingServerRoundStats **ppAllocateStats )
  11665. {
  11666. /** Removed for partner depot **/
  11667. }
  11668. // Helper to determine if all players on a team are playing for the same clan
  11669. bool CCSGameRules::IsClanTeam( CTeam *pTeam )
  11670. {
  11671. uint32 iTeamClan = 0;
  11672. bool bTeamInitialized = false;
  11673. for ( int iPlayer = 0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  11674. {
  11675. CBasePlayer *pPlayer = pTeam->GetPlayer( iPlayer );
  11676. if ( !pPlayer )
  11677. return false;
  11678. if ( pPlayer->IsBot() )
  11679. continue;
  11680. const char *pClanID = engine->GetClientConVarValue( pPlayer->entindex(), "cl_clanid" );
  11681. uint32 iPlayerClan = atoi( pClanID );
  11682. // Initialize the team clan
  11683. if ( !bTeamInitialized )
  11684. {
  11685. iTeamClan = iPlayerClan;
  11686. bTeamInitialized = true;
  11687. }
  11688. if ( iPlayerClan != iTeamClan || iPlayerClan == 0 )
  11689. return false;
  11690. }
  11691. return iTeamClan != 0;
  11692. }
  11693. void CCSGameRules::ModifyRealtimeBotDifficulty( CCSPlayer* pOnlyBotToProcess /* = NULL */ )
  11694. {
  11695. if ( !sv_auto_adjust_bot_difficulty.GetBool() )
  11696. return;
  11697. float fAvgPlayerCS = CalculateAveragePlayerContributionScore();
  11698. float fAvgBotCS;
  11699. if ( !sv_compute_per_bot_difficulty.GetBool() )
  11700. {
  11701. fAvgBotCS = CalculateAverageBotContributionScore();
  11702. }
  11703. else
  11704. {
  11705. fAvgBotCS = 0.0f;
  11706. }
  11707. float fLowWindowExtent = fAvgPlayerCS + bot_autodifficulty_threshold_low.GetFloat();
  11708. float fHighWindowExtent = fAvgPlayerCS + bot_autodifficulty_threshold_high.GetFloat();
  11709. // Ensure the high/low window extents are accurate
  11710. if ( fLowWindowExtent > fHighWindowExtent )
  11711. {
  11712. float swapval = fLowWindowExtent;
  11713. fLowWindowExtent = fHighWindowExtent;
  11714. fHighWindowExtent = swapval;
  11715. }
  11716. int nextBotDifficulty = -1;
  11717. if ( sv_compute_per_bot_difficulty.GetBool() )
  11718. {
  11719. // Compare incoming target contribution score to levels of difficulty, and choose a level for each bot
  11720. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11721. {
  11722. CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i );
  11723. if ( !pPlayer )
  11724. {
  11725. continue;
  11726. }
  11727. if ( pOnlyBotToProcess && pPlayer != pOnlyBotToProcess )
  11728. {
  11729. continue;
  11730. }
  11731. if ( pPlayer->IsBot() )
  11732. {
  11733. CCSBot* pBot = dynamic_cast< CCSBot* >( pPlayer );
  11734. if ( pBot )
  11735. {
  11736. const BotProfile* pProfile = pBot->GetProfile();
  11737. if ( pProfile && TheBotProfiles )
  11738. {
  11739. float cScore = ( float )pPlayer->GetContributionScore();
  11740. nextBotDifficulty = -1;
  11741. DevMsg( "+++++Bot %s has CS = %f. Low = %f, High = %f\n", pPlayer->GetPlayerName(), cScore, fLowWindowExtent, fHighWindowExtent );
  11742. if ( cScore < fLowWindowExtent )
  11743. {
  11744. // Bot should have higher difficulty
  11745. DevMsg( "+++++Bot %s with CS = %f, should increase its difficulty\n", pPlayer->GetPlayerName(), cScore );
  11746. nextBotDifficulty = pProfile->GetMaxDifficulty() + 1;
  11747. // Ensure new difficulty level is valid
  11748. if ( nextBotDifficulty >= NUM_DIFFICULTY_LEVELS )
  11749. {
  11750. nextBotDifficulty = NUM_DIFFICULTY_LEVELS - 1;
  11751. }
  11752. }
  11753. else if ( cScore > fHighWindowExtent )
  11754. {
  11755. // Bot should have lower difficulty
  11756. DevMsg( "+++++Bot %s with CS = %f, should decrease its difficulty\n", pPlayer->GetPlayerName(), cScore );
  11757. nextBotDifficulty = pProfile->GetMaxDifficulty() - 1;
  11758. // Ensure new difficulty level is valid
  11759. if ( nextBotDifficulty < BOT_EASY )
  11760. {
  11761. nextBotDifficulty = BOT_EASY;
  11762. }
  11763. }
  11764. // In queue matchmaking mode bots are always obeying bot manager difficulty
  11765. if ( IsQueuedMatchmaking() )
  11766. nextBotDifficulty = CCSBotManager::GetDifficultyLevel();
  11767. if ( nextBotDifficulty >= BOT_EASY )
  11768. {
  11769. // Change the bot's new profile based on desired difficulty
  11770. const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )nextBotDifficulty, pBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true );
  11771. if ( NULL == pNewProfileData )
  11772. {
  11773. Warning( "-----No profile found to match search criteria. Not updating this bot's difficulty.");
  11774. }
  11775. else
  11776. {
  11777. // Change the parameters of the bot's profile to match the new difficulty settings
  11778. DevMsg( "+++++Bot %s with Max Difficulty %d will become Bot %s with Max Difficulty %d\n", pProfile->GetName(), pProfile->GetMaxDifficulty(), pNewProfileData->GetName(), pNewProfileData->GetMaxDifficulty() );
  11779. pBot->Initialize( pNewProfileData, pBot->GetTeamNumber() );
  11780. }
  11781. }
  11782. }
  11783. }
  11784. }
  11785. }
  11786. }
  11787. else
  11788. {
  11789. // Handle the alternate case for all bots changing difficulty as a group (all bots will have identical difficulty)
  11790. // DevMsg( "+++++Average Bot Contribution Score = %f\n", fAvgBotCS );
  11791. if ( fAvgBotCS < fLowWindowExtent )
  11792. {
  11793. // Bots should have higher difficulty
  11794. nextBotDifficulty = -1;
  11795. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11796. {
  11797. CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i );
  11798. if ( pOnlyBotToProcess && pPlayer != pOnlyBotToProcess )
  11799. {
  11800. continue;
  11801. }
  11802. if ( pPlayer && pPlayer->IsBot() )
  11803. {
  11804. CCSBot* pBot = dynamic_cast< CCSBot* >( pPlayer );
  11805. if ( pBot )
  11806. {
  11807. const BotProfile* pProfile = pBot->GetProfile();
  11808. if ( pProfile && TheBotProfiles && nextBotDifficulty == -1 )
  11809. {
  11810. nextBotDifficulty = pProfile->GetMaxDifficulty() + 1;
  11811. if ( nextBotDifficulty > BOT_EXPERT )
  11812. {
  11813. nextBotDifficulty = BOT_EXPERT;
  11814. }
  11815. }
  11816. // In queue matchmaking mode bots are always obeying bot manager difficulty
  11817. if ( IsQueuedMatchmaking() )
  11818. nextBotDifficulty = CCSBotManager::GetDifficultyLevel();
  11819. if ( nextBotDifficulty > -1 )
  11820. {
  11821. // Have a valid difficulty level, so apply it to each bot
  11822. const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )nextBotDifficulty, pBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true );
  11823. if ( NULL == pNewProfileData )
  11824. {
  11825. Warning( "-----No profile found to match search criteria. Not updating this bot's difficulty.");
  11826. }
  11827. else
  11828. {
  11829. // Change the parameters of the bot's profile to match the new difficulty settings
  11830. DevMsg( "+++++Bot %s with Max Difficulty %d will become Bot %s with Max Difficulty %d\n", pProfile->GetName(), pProfile->GetMaxDifficulty(), pNewProfileData->GetName(), pNewProfileData->GetMaxDifficulty() );
  11831. pBot->Initialize( pNewProfileData, pBot->GetTeamNumber() );
  11832. }
  11833. }
  11834. }
  11835. }
  11836. }
  11837. }
  11838. else if ( fAvgBotCS > fHighWindowExtent )
  11839. {
  11840. // Bots should have lower difficulty
  11841. nextBotDifficulty = -1;
  11842. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11843. {
  11844. CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i );
  11845. if ( pOnlyBotToProcess && pPlayer != pOnlyBotToProcess )
  11846. {
  11847. continue;
  11848. }
  11849. if ( pPlayer && pPlayer->IsBot() )
  11850. {
  11851. CCSBot* pBot = dynamic_cast< CCSBot* >( pPlayer );
  11852. if ( pBot )
  11853. {
  11854. const BotProfile* pProfile = pBot->GetProfile();
  11855. if ( pProfile && TheBotProfiles && nextBotDifficulty == -1 )
  11856. {
  11857. nextBotDifficulty = pProfile->GetMaxDifficulty() - 1;
  11858. // Ensure new difficulty level is valid
  11859. if ( nextBotDifficulty < BOT_EASY )
  11860. {
  11861. nextBotDifficulty = BOT_EASY;
  11862. }
  11863. }
  11864. // In queue matchmaking mode bots are always obeying bot manager difficulty
  11865. if ( IsQueuedMatchmaking() )
  11866. nextBotDifficulty = CCSBotManager::GetDifficultyLevel();
  11867. if ( nextBotDifficulty > -1 && ( pProfile->GetMaxDifficulty() != nextBotDifficulty ) )
  11868. {
  11869. // Have a valid difficulty level, so apply it to each bot
  11870. const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )nextBotDifficulty, pBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true );
  11871. if ( NULL == pNewProfileData )
  11872. {
  11873. Warning( "-----No profile found to match search criteria. Not updating this bot's difficulty.");
  11874. }
  11875. else
  11876. {
  11877. // Change the parameters of the bot's profile to match the new difficulty settings
  11878. DevMsg( "+++++Bot %s with Max Difficulty %d will become Bot %s with Max Difficulty %d\n", pProfile->GetName(), pProfile->GetMaxDifficulty( ), pNewProfileData->GetName(), pNewProfileData->GetMaxDifficulty() );
  11879. pBot->Initialize( pNewProfileData, pBot->GetTeamNumber() );
  11880. }
  11881. }
  11882. }
  11883. }
  11884. }
  11885. }
  11886. }
  11887. if ( !IsPlayingOffline() )
  11888. {
  11889. fAvgBotCS = CalculateAverageBotContributionScore();
  11890. //DevMsg( "Average Bot Difficulty = %f\n", fAvgBotCS );
  11891. // Store max bot difficulty in the convar representing the player's input device
  11892. sv_bot_difficulty_kbm.SetValue( fAvgBotCS );
  11893. }
  11894. }
  11895. float CCSGameRules::CalculateAveragePlayerContributionScore( void )
  11896. {
  11897. // Loop through all players and get average human contribution score
  11898. int cscoreTotal = 0;
  11899. int numHumanPlayers = 0;
  11900. float avgHumanContributionScore = 0.0f;
  11901. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11902. {
  11903. CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i );
  11904. if ( !pPlayer )
  11905. {
  11906. continue;
  11907. }
  11908. if ( !pPlayer->IsBot() )
  11909. {
  11910. cscoreTotal += pPlayer->GetContributionScore();
  11911. numHumanPlayers++;
  11912. }
  11913. }
  11914. if ( numHumanPlayers > 0 )
  11915. {
  11916. avgHumanContributionScore = ( float )cscoreTotal / ( float )numHumanPlayers;
  11917. }
  11918. return avgHumanContributionScore;
  11919. }
  11920. float CCSGameRules::CalculateAverageBotContributionScore( void )
  11921. {
  11922. // Loop through all players and get average bot contribution score
  11923. int cscoreTotal = 0;
  11924. int numBots = 0;
  11925. float avgBotContributionScore = 0.0f;
  11926. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11927. {
  11928. CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i );
  11929. if ( !pPlayer )
  11930. {
  11931. continue;
  11932. }
  11933. if ( pPlayer->IsBot() )
  11934. {
  11935. cscoreTotal += pPlayer->GetContributionScore();
  11936. numBots++;
  11937. }
  11938. }
  11939. if ( numBots > 0 )
  11940. {
  11941. avgBotContributionScore = ( float )cscoreTotal / ( float )numBots;
  11942. }
  11943. return avgBotContributionScore;
  11944. }
  11945. // [tj] This is where we check non-player-specific that occur at the end of the round
  11946. void CCSGameRules::ProcessEndOfRoundAchievements(int iWinnerTeam, int iReason)
  11947. {
  11948. if (iWinnerTeam == WINNER_CT || iWinnerTeam == WINNER_TER)
  11949. {
  11950. int losingTeamId = (iWinnerTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
  11951. CTeam* losingTeam = GetGlobalTeam(losingTeamId);
  11952. //Check for players we should ignore when checking team size.
  11953. int ignoreCount = 0;
  11954. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  11955. {
  11956. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  11957. if (pPlayer)
  11958. {
  11959. if( IsPlayingGunGameProgressive() )
  11960. UpdateMatchStats(pPlayer,iWinnerTeam);
  11961. int teamNum = pPlayer->GetTeamNumber();
  11962. if ( teamNum == losingTeamId )
  11963. {
  11964. if (pPlayer->WasNotKilledNaturally())
  11965. {
  11966. ignoreCount++;
  11967. }
  11968. }
  11969. }
  11970. }
  11971. // [tj] Check extermination with no losses achievement
  11972. if ((iReason == CTs_Win && m_bNoCTsKilled || iReason == Terrorists_Win && m_bNoTerroristsKilled)
  11973. && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement)
  11974. {
  11975. CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
  11976. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  11977. {
  11978. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  11979. Assert( pPlayer );
  11980. if ( !pPlayer )
  11981. continue;
  11982. pPlayer->AwardAchievement(CSLosslessExtermination);
  11983. }
  11984. }
  11985. // [tj] Check flawless victory achievement - currently requiring extermination
  11986. if ((iReason == CTs_Win && m_bNoCTsDamaged || iReason == Terrorists_Win && m_bNoTerroristsDamaged)
  11987. && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement
  11988. && !CSGameRules()->IsPlayingGunGameProgressive() )
  11989. {
  11990. CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
  11991. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  11992. {
  11993. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  11994. Assert( pPlayer );
  11995. if ( !pPlayer )
  11996. continue;
  11997. pPlayer->AwardAchievement(CSFlawlessVictory);
  11998. }
  11999. }
  12000. // [tj] Check bloodless victory achievement
  12001. if ((iWinnerTeam == TEAM_TERRORIST && m_bNoCTsKilled || iWinnerTeam == TEAM_CT && m_bNoTerroristsKilled)
  12002. && losingTeam && losingTeam->GetNumPlayers() >= AchievementConsts::DefaultMinOpponentsForAchievement)
  12003. {
  12004. CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
  12005. for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
  12006. {
  12007. CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
  12008. Assert( pPlayer );
  12009. if ( !pPlayer )
  12010. continue;
  12011. pPlayer->AwardAchievement(CSBloodlessVictory);
  12012. }
  12013. }
  12014. }
  12015. }
  12016. //[tj] Counts the number of players in each category in the struct (dead, alive, etc...)
  12017. void CCSGameRules::GetPlayerCounts(TeamPlayerCounts teamCounts[TEAM_MAXCOUNT])
  12018. {
  12019. memset(teamCounts, 0, sizeof(TeamPlayerCounts) * TEAM_MAXCOUNT);
  12020. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12021. {
  12022. CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
  12023. if (pPlayer)
  12024. {
  12025. int iTeam = pPlayer->GetTeamNumber();
  12026. if (iTeam >= 0 && iTeam < TEAM_MAXCOUNT)
  12027. {
  12028. ++teamCounts[iTeam].totalPlayers;
  12029. if (pPlayer->IsAlive())
  12030. {
  12031. ++teamCounts[iTeam].totalAlivePlayers;
  12032. }
  12033. else
  12034. {
  12035. ++teamCounts[iTeam].totalDeadPlayers;
  12036. //If the player has joined a team bit isn't in the game yet
  12037. if (pPlayer->State_Get() == STATE_PICKINGCLASS)
  12038. {
  12039. ++teamCounts[iTeam].unenteredPlayers;
  12040. }
  12041. else if (pPlayer->WasNotKilledNaturally())
  12042. {
  12043. ++teamCounts[iTeam].suicidedPlayers;
  12044. }
  12045. else
  12046. {
  12047. ++teamCounts[iTeam].killedPlayers;
  12048. }
  12049. }
  12050. }
  12051. }
  12052. }
  12053. }
  12054. void CCSGameRules::CheckMapConditions()
  12055. {
  12056. // Check to see if this map has a bomb target in it
  12057. if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) )
  12058. {
  12059. // this is a bit hacky, but it makes it so the bomb stuff only shows up on mission 3 of the coop mission
  12060. if ( IsPlayingCoopMission() && mp_anyone_can_pickup_c4.GetBool() == false )
  12061. {
  12062. m_bMapHasBombTarget = false;
  12063. m_bMapHasBombZone = false;
  12064. }
  12065. else
  12066. {
  12067. m_bMapHasBombTarget = true;
  12068. m_bMapHasBombZone = true;
  12069. }
  12070. }
  12071. else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) )
  12072. {
  12073. m_bMapHasBombTarget = true;
  12074. m_bMapHasBombZone = false;
  12075. }
  12076. else
  12077. {
  12078. m_bMapHasBombTarget = false;
  12079. m_bMapHasBombZone = false;
  12080. }
  12081. // See if the map has func_buyzone entities
  12082. // Used by CBasePlayer::HandleSignals() to support maps without these entities
  12083. if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) )
  12084. {
  12085. m_bMapHasBuyZone = true;
  12086. }
  12087. else
  12088. {
  12089. m_bMapHasBuyZone = false;
  12090. }
  12091. // Check to see if this map has hostage rescue zones
  12092. if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) )
  12093. {
  12094. m_bMapHasRescueZone = true;
  12095. }
  12096. else
  12097. {
  12098. m_bMapHasRescueZone = false;
  12099. }
  12100. // GOOSEMAN : See if this map has func_escapezone entities
  12101. if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) )
  12102. {
  12103. m_bMapHasEscapeZone = true;
  12104. }
  12105. else
  12106. {
  12107. m_bMapHasEscapeZone = false;
  12108. }
  12109. }
  12110. void CCSGameRules::SwapAllPlayers()
  12111. {
  12112. // MOTODO we have to make sure that enought spaning points exits
  12113. Assert ( 0 );
  12114. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12115. {
  12116. /* CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  12117. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  12118. pPlayer->SwitchTeam(); */
  12119. }
  12120. m_match.SwapTeamScores();
  12121. }
  12122. // reset player scores, team scores, and player controls, restart round; useful for running a demo
  12123. void CCSGameRules::ResetForTradeshow( void )
  12124. {
  12125. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12126. {
  12127. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  12128. if ( pPlayer )
  12129. {
  12130. pPlayer->Reset( true );
  12131. }
  12132. }
  12133. m_match.Reset();
  12134. IGameEvent * event = gameeventmanager->CreateEvent( "reset_player_controls" );
  12135. if ( event )
  12136. {
  12137. gameeventmanager->FireEvent( event );
  12138. }
  12139. ClearGunGameData();
  12140. EndRound();
  12141. }
  12142. bool CS_FindInList( const char **pStrings, const char *pToFind )
  12143. {
  12144. return FindInList( pStrings, pToFind );
  12145. }
  12146. void CCSGameRules::CleanUpMap()
  12147. {
  12148. if (IsLogoMap())
  12149. return;
  12150. // Recreate all the map entities from the map data (preserving their indices),
  12151. // then remove everything else except the players.
  12152. // first go through the current players and see if they are parented to something
  12153. // if so, unparent them before we might remove their parent and cause a crash
  12154. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12155. {
  12156. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  12157. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  12158. {
  12159. if ( pPlayer->GetParent() )
  12160. pPlayer->SetParent( NULL );
  12161. }
  12162. }
  12163. // Get rid of all entities except players.
  12164. CBaseEntity *pCur = gEntList.FirstEnt();
  12165. while ( pCur )
  12166. {
  12167. CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pCur );
  12168. CBombTarget *pBombTarg = dynamic_cast< CBombTarget* >( pCur );
  12169. CHostageRescueZone *pRescueZone = dynamic_cast< CHostageRescueZone* >( pCur );
  12170. // Weapons with owners don't want to be removed..
  12171. if ( pWeapon )
  12172. {
  12173. // [dwenger] Handle round restart processing for the weapon.
  12174. pWeapon->OnRoundRestart();
  12175. if ( pWeapon->ShouldRemoveOnRoundRestart() )
  12176. {
  12177. UTIL_Remove( pCur );
  12178. }
  12179. }
  12180. // bomb targets have a re-init function to call
  12181. else if ( pBombTarg )
  12182. {
  12183. pBombTarg->ReInitOnRoundStart();
  12184. }
  12185. else if ( pRescueZone )
  12186. {
  12187. pRescueZone->ReInitOnRoundStart();
  12188. }
  12189. // remove entities that has to be restored on roundrestart (breakables etc)
  12190. else if ( !CS_FindInList( s_PreserveEnts, pCur->GetClassname() ) )
  12191. {
  12192. if( !Commentary_IsCommentaryEntity( pCur ) ) //leave commentary alone
  12193. {
  12194. UTIL_Remove( pCur );
  12195. }
  12196. }
  12197. pCur = gEntList.NextEnt( pCur );
  12198. }
  12199. // Really remove the entities so we can have access to their slots below.
  12200. gEntList.CleanupDeleteList();
  12201. // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that
  12202. // could kill respawning CTs
  12203. g_EventQueue.Clear();
  12204. // Now reload the map entities.
  12205. class CCSMapEntityFilter : public IMapEntityFilter
  12206. {
  12207. public:
  12208. virtual bool ShouldCreateEntity( const char *pClassname )
  12209. {
  12210. // Don't recreate the preserved entities.
  12211. if ( !CS_FindInList( s_PreserveEnts, pClassname ) )
  12212. {
  12213. return true;
  12214. }
  12215. else
  12216. {
  12217. // Increment our iterator since it's not going to call CreateNextEntity for this ent.
  12218. if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
  12219. m_iIterator = g_MapEntityRefs.Next( m_iIterator );
  12220. return false;
  12221. }
  12222. }
  12223. virtual CBaseEntity* CreateNextEntity( const char *pClassname )
  12224. {
  12225. if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
  12226. {
  12227. // This shouldn't be possible. When we loaded the map, it should have used
  12228. // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list
  12229. // with the same list of entities we're referring to here.
  12230. Assert( false );
  12231. return NULL;
  12232. }
  12233. else
  12234. {
  12235. CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
  12236. m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
  12237. if ( ref.m_iEdict == -1 || INDEXENT( ref.m_iEdict ) )
  12238. {
  12239. // Doh! The entity was delete and its slot was reused.
  12240. // Just use any old edict slot. This case sucks because we lose the baseline.
  12241. return CreateEntityByName( pClassname );
  12242. }
  12243. else
  12244. {
  12245. // Cool, the slot where this entity was is free again (most likely, the entity was
  12246. // freed above). Now create an entity with this specific index.
  12247. return CreateEntityByName( pClassname, ref.m_iEdict );
  12248. }
  12249. }
  12250. }
  12251. public:
  12252. int m_iIterator; // Iterator into g_MapEntityRefs.
  12253. };
  12254. CCSMapEntityFilter filter;
  12255. filter.m_iIterator = g_MapEntityRefs.Head();
  12256. // DO NOT CALL SPAWN ON info_node ENTITIES!
  12257. MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
  12258. // this kind of sucks. on ps3, we need to delete all physics props that are not set to debris to fix a crash,
  12259. // but we don't know if the prop has the debris flag until after it's been removed and then recreated again.
  12260. // that's why we have to loop through all of the entities again here.
  12261. if ( IsPS3() || engine->IsDedicatedServerForPS3() )
  12262. {
  12263. int nNumPhysPropsDeleted = 0;
  12264. CBaseEntity *pPhysCur = gEntList.FirstEnt();
  12265. while ( pPhysCur )
  12266. {
  12267. CPhysicsProp *pPhysProp = dynamic_cast< CPhysicsProp* >( pPhysCur );
  12268. if ( pPhysProp && !pPhysProp->HasSpawnFlags( SF_PHYSPROP_DEBRIS ) )
  12269. {
  12270. UTIL_Remove( pPhysCur );
  12271. nNumPhysPropsDeleted++;
  12272. }
  12273. pPhysCur = gEntList.NextEnt( pPhysCur );
  12274. }
  12275. if ( nNumPhysPropsDeleted > 0 )
  12276. Msg( "DELETED %d prop_physics or prop_physics_multiplayer that were not set to debris!!\n", nNumPhysPropsDeleted );
  12277. }
  12278. }
  12279. CCSPlayer *CCSGameRules::IsThereABomber()
  12280. {
  12281. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12282. {
  12283. CCSPlayer *pPlayer = CCSPlayer::Instance( i );
  12284. if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
  12285. {
  12286. if ( pPlayer->GetTeamNumber() == TEAM_CT )
  12287. continue;
  12288. if ( pPlayer->HasC4() )
  12289. return pPlayer; //There you are.
  12290. }
  12291. }
  12292. //Didn't find a bomber.
  12293. return NULL;
  12294. }
  12295. void CCSGameRules::EndRound()
  12296. {
  12297. // fake a round end
  12298. CSGameRules()->TerminateRound( 0.0f, Round_Draw );
  12299. }
  12300. CBaseEntity *CCSGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
  12301. {
  12302. // we don't want to get a spawn if we are already in the process of spawning with one because
  12303. // it increments the spawn number without actually spawning and messes up the spawn priority
  12304. CCSPlayer* pCSPlayer = ToCSPlayer( pPlayer );
  12305. if ( !pCSPlayer || pCSPlayer->IsPlayerSpawning() )
  12306. return NULL;
  12307. pCSPlayer->SetPlayerSpawning( true );
  12308. // get valid spawn point
  12309. CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
  12310. if ( pPlayer->IsBot() && pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  12311. {
  12312. // // disable the spawn after someone spawn here in coop
  12313. SpawnPointCoopEnemy *pEnemySpawnSpot = dynamic_cast< SpawnPointCoopEnemy* >( pSpawnSpot );
  12314. if ( pEnemySpawnSpot )
  12315. {
  12316. if ( IsPlayingCoopMission() )
  12317. {
  12318. CCSBot* pBot = static_cast< CCSBot* >( pPlayer );
  12319. if ( pBot )
  12320. {
  12321. pBot->SetLastCoopSpawnPoint( pEnemySpawnSpot );
  12322. }
  12323. }
  12324. }
  12325. }
  12326. // drop down to ground
  12327. Vector GroundPos = DropToGround( pPlayer, pSpawnSpot->GetAbsOrigin() + Vector( 0, 0, 16 ), VEC_HULL_MIN + Vector( -4, -4, 0 ), VEC_HULL_MAX + Vector( 4, 4, 0 ) );
  12328. // Move the player to the place it said.
  12329. pPlayer->Teleport( &GroundPos, &pSpawnSpot->GetLocalAngles(), &vec3_origin );
  12330. pPlayer->m_Local.m_viewPunchAngle = vec3_angle;
  12331. return pSpawnSpot;
  12332. }
  12333. // checks if the spot is clear of players
  12334. bool CCSGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer )
  12335. {
  12336. if ( !pSpot )
  12337. return false;
  12338. if ( !pSpot->IsTriggered( pPlayer ) )
  12339. return false;
  12340. Vector mins = GetViewVectors()->m_vHullMin;
  12341. Vector maxs = GetViewVectors()->m_vHullMax;
  12342. Vector vTestMins = pSpot->GetAbsOrigin() + mins;
  12343. Vector vTestMaxs = pSpot->GetAbsOrigin() + maxs;
  12344. // First test the starting origin.
  12345. CTraceFilterSimple traceFilter( pPlayer, COLLISION_GROUP_PLAYER );
  12346. if ( !UTIL_IsSpaceEmpty( pPlayer, vTestMins, vTestMaxs, MASK_SOLID, &traceFilter ) )
  12347. return false;
  12348. // Test against other players potentially occupying this spot
  12349. for ( int k = 1; k <= gpGlobals->maxClients; ++ k )
  12350. {
  12351. CBasePlayer *pOther = UTIL_PlayerByIndex( k );
  12352. if ( !pOther ) continue;
  12353. if ( pOther == pPlayer ) continue;
  12354. if ( ( pOther->GetTeamNumber() != TEAM_TERRORIST )
  12355. && ( pOther->GetTeamNumber() != TEAM_CT ) )
  12356. continue;
  12357. if ( ( pOther->GetAbsOrigin().AsVector2D() - pSpot->GetAbsOrigin().AsVector2D() ).IsZero() )
  12358. return false;
  12359. }
  12360. return true;
  12361. }
  12362. bool CCSGameRules::IsSpawnPointHiddenFromOtherPlayers( CBaseEntity *pSpot, CBasePlayer *pPlayer, int nHideFromTeam )
  12363. {
  12364. Vector vecSpot = pSpot->GetAbsOrigin() + Vector( 0, 0, 32 );
  12365. if ( nHideFromTeam > 0 )
  12366. {
  12367. if ( nHideFromTeam == TEAM_CT && UTIL_IsVisibleToTeam( vecSpot, TEAM_CT ) )
  12368. return false;
  12369. else if ( nHideFromTeam == TEAM_TERRORIST && UTIL_IsVisibleToTeam( vecSpot, TEAM_TERRORIST ) )
  12370. return false;
  12371. }
  12372. else if ( nHideFromTeam == 0 && ( UTIL_IsVisibleToTeam( vecSpot, TEAM_CT ) ) ||
  12373. ( UTIL_IsVisibleToTeam( vecSpot, TEAM_TERRORIST ) ) )
  12374. return false;
  12375. return true;
  12376. }
  12377. bool CCSGameRules::IsThereABomb()
  12378. {
  12379. bool bBombFound = false;
  12380. /* are there any bombs, either laying around, or in someone's inventory? */
  12381. if( gEntList.FindEntityByClassname( NULL, WEAPON_C4_CLASSNAME ) != 0 )
  12382. {
  12383. bBombFound = true;
  12384. }
  12385. /* what about planted bombs!? */
  12386. else if( gEntList.FindEntityByClassname( NULL, PLANTED_C4_CLASSNAME ) != 0 )
  12387. {
  12388. bBombFound = true;
  12389. }
  12390. return bBombFound;
  12391. }
  12392. void CCSGameRules::HostageTouched()
  12393. {
  12394. if( gpGlobals->curtime > m_flNextHostageAnnouncement && m_iRoundWinStatus == WINNER_NONE )
  12395. {
  12396. //BroadcastSound( "Event.HostageTouched" );
  12397. m_flNextHostageAnnouncement = gpGlobals->curtime + 60.0;
  12398. }
  12399. m_bHasHostageBeenTouched = true;
  12400. if ( IsPlayingCoopGuardian() )
  12401. CTsReachedHostageRoundEndCheck();
  12402. }
  12403. void CCSGameRules::CreateStandardEntities()
  12404. {
  12405. // Create the player resource
  12406. g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "cs_player_manager", vec3_origin, vec3_angle );
  12407. // Create the entity that will send our data to the client.
  12408. #ifdef DBGFLAG_ASSERT
  12409. CBaseEntity *pEnt =
  12410. #endif
  12411. CBaseEntity::Create( "cs_gamerules", vec3_origin, vec3_angle );
  12412. Assert( pEnt );
  12413. g_voteControllerGlobal = static_cast< CVoteController *>( CBaseEntity::Create("vote_controller", vec3_origin, vec3_angle) );
  12414. g_voteControllerCT = static_cast< CVoteController *>( CBaseEntity::Create("vote_controller", vec3_origin, vec3_angle) );
  12415. g_voteControllerT = static_cast< CVoteController *>( CBaseEntity::Create("vote_controller", vec3_origin, vec3_angle) );
  12416. // Vote Issue classes are handled/cleaned-up by g_voteControllers
  12417. NewTeamIssue< CKickIssue >();
  12418. NewGlobalIssue< CRestartGameIssue >();
  12419. NewGlobalIssue< CChangeLevelIssue >();
  12420. NewGlobalIssue< CNextLevelIssue >();
  12421. if ( IsPlayingAnyCompetitiveStrictRuleset() )
  12422. {
  12423. NewTeamIssue< CStartTimeOutIssue >();
  12424. }
  12425. static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL );
  12426. if ( s_pchTournamentServer && IsQueuedMatchmaking() )
  12427. {
  12428. NewGlobalIssue< CPauseMatchIssue >();
  12429. NewGlobalIssue< CUnpauseMatchIssue >();
  12430. NewGlobalIssue< CLoadBackupIssue >();
  12431. NewGlobalIssue< CReadyForMatchIssue >();
  12432. NewGlobalIssue< CNotReadyForMatchIssue >();
  12433. }
  12434. if ( IsQueuedMatchmaking() )
  12435. {
  12436. // new CQueuedMatchmakingRematch;
  12437. // new CQueuedMatchmakingContinue;
  12438. NewTeamIssue< CSurrender >();
  12439. }
  12440. else
  12441. {
  12442. NewGlobalIssue< CScrambleTeams >();
  12443. NewGlobalIssue< CSwapTeams >();
  12444. // new CSurrender;
  12445. }
  12446. }
  12447. #endif // CLIENT_DLL
  12448. ConVar cash_team_terrorist_win_bomb(
  12449. "cash_team_terrorist_win_bomb",
  12450. "3500",
  12451. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12452. ConVar cash_team_elimination_hostage_map_t(
  12453. "cash_team_elimination_hostage_map_t",
  12454. "1000",
  12455. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12456. ConVar cash_team_elimination_hostage_map_ct(
  12457. "cash_team_elimination_hostage_map_ct",
  12458. "2000",
  12459. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12460. ConVar cash_team_elimination_bomb_map(
  12461. "cash_team_elimination_bomb_map",
  12462. "3250",
  12463. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12464. ConVar cash_team_survive_guardian_wave(
  12465. "cash_team_survive_guardian_wave",
  12466. "1000",
  12467. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY );
  12468. ConVar cash_team_win_by_time_running_out_hostage(
  12469. "cash_team_win_by_time_running_out_hostage",
  12470. "3250",
  12471. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12472. ConVar cash_team_win_by_time_running_out_bomb(
  12473. "cash_team_win_by_time_running_out_bomb",
  12474. "3250",
  12475. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12476. ConVar cash_team_win_by_defusing_bomb(
  12477. "cash_team_win_by_defusing_bomb",
  12478. "3250",
  12479. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12480. ConVar cash_team_win_by_hostage_rescue(
  12481. "cash_team_win_by_hostage_rescue",
  12482. "3500",
  12483. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12484. ConVar cash_team_loser_bonus(
  12485. "cash_team_loser_bonus",
  12486. "1400",
  12487. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12488. ConVar cash_team_loser_bonus_consecutive_rounds(
  12489. "cash_team_loser_bonus_consecutive_rounds",
  12490. "500",
  12491. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12492. ConVar cash_team_rescued_hostage(
  12493. "cash_team_rescued_hostage",
  12494. "0",
  12495. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12496. ConVar cash_team_hostage_alive(
  12497. "cash_team_hostage_alive",
  12498. "0",
  12499. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12500. ConVar cash_team_planted_bomb_but_defused(
  12501. "cash_team_planted_bomb_but_defused",
  12502. "800",
  12503. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12504. ConVar cash_team_hostage_interaction(
  12505. "cash_team_hostage_interaction",
  12506. "500",
  12507. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12508. ConVar cash_player_killed_teammate(
  12509. "cash_player_killed_teammate",
  12510. "-300",
  12511. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12512. ConVar cash_player_killed_enemy_factor(
  12513. "cash_player_killed_enemy_factor",
  12514. "1",
  12515. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12516. ConVar cash_player_killed_enemy_default(
  12517. "cash_player_killed_enemy_default",
  12518. "300",
  12519. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12520. ConVar cash_player_bomb_planted(
  12521. "cash_player_bomb_planted",
  12522. "300",
  12523. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12524. ConVar cash_player_bomb_defused(
  12525. "cash_player_bomb_defused",
  12526. "300",
  12527. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12528. ConVar cash_player_rescued_hostage(
  12529. "cash_player_rescued_hostage",
  12530. "1000",
  12531. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12532. ConVar cash_player_interact_with_hostage(
  12533. "cash_player_interact_with_hostage",
  12534. "150",
  12535. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12536. ConVar cash_player_damage_hostage(
  12537. "cash_player_damage_hostage",
  12538. "-30",
  12539. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12540. ConVar cash_player_killed_hostage(
  12541. "cash_player_killed_hostage",
  12542. "-1000",
  12543. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12544. ConVar cash_player_respawn_amount(
  12545. "cash_player_respawn_amount",
  12546. "0",
  12547. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12548. ConVar cash_player_get_killed(
  12549. "cash_player_get_killed",
  12550. "0",
  12551. FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY);
  12552. int CCSGameRules::TeamCashAwardValue( TeamCashAward::Type reason)
  12553. {
  12554. switch( reason )
  12555. {
  12556. case TeamCashAward::TERRORIST_WIN_BOMB: return cash_team_terrorist_win_bomb.GetInt();
  12557. case TeamCashAward::ELIMINATION_HOSTAGE_MAP_T: return cash_team_elimination_hostage_map_t.GetInt();
  12558. case TeamCashAward::ELIMINATION_HOSTAGE_MAP_CT: return cash_team_elimination_hostage_map_ct.GetInt();
  12559. case TeamCashAward::ELIMINATION_BOMB_MAP: return cash_team_elimination_bomb_map.GetInt();
  12560. case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE:return cash_team_win_by_time_running_out_hostage.GetInt();
  12561. case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB: return cash_team_win_by_time_running_out_bomb.GetInt();
  12562. case TeamCashAward::WIN_BY_DEFUSING_BOMB: return cash_team_win_by_defusing_bomb.GetInt();
  12563. case TeamCashAward::WIN_BY_HOSTAGE_RESCUE: return cash_team_win_by_hostage_rescue.GetInt();
  12564. case TeamCashAward::LOSER_BONUS: return cash_team_loser_bonus.GetInt();
  12565. case TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS: return cash_team_loser_bonus_consecutive_rounds.GetInt();
  12566. case TeamCashAward::RESCUED_HOSTAGE: return cash_team_rescued_hostage.GetInt();
  12567. case TeamCashAward::HOSTAGE_ALIVE: return cash_team_hostage_alive.GetInt();
  12568. case TeamCashAward::PLANTED_BOMB_BUT_DEFUSED: return cash_team_planted_bomb_but_defused.GetInt();
  12569. case TeamCashAward::HOSTAGE_INTERACTION: return cash_team_hostage_interaction.GetInt();
  12570. case TeamCashAward::SURVIVE_GUARDIAN_WAVE: return cash_team_survive_guardian_wave.GetInt();
  12571. default:
  12572. AssertMsg( false, "Unhandled TeamCashAwardReason" );
  12573. return 0;
  12574. };
  12575. }
  12576. int CCSGameRules::PlayerCashAwardValue( PlayerCashAward::Type reason)
  12577. {
  12578. switch( reason )
  12579. {
  12580. case PlayerCashAward::NONE: return 0;
  12581. case PlayerCashAward::KILL_TEAMMATE: return cash_player_killed_teammate.GetInt();
  12582. case PlayerCashAward::KILLED_ENEMY: return cash_player_killed_enemy_default.GetInt();
  12583. case PlayerCashAward::BOMB_PLANTED: return cash_player_bomb_planted.GetInt();
  12584. case PlayerCashAward::BOMB_DEFUSED: return cash_player_bomb_defused.GetInt();
  12585. case PlayerCashAward::RESCUED_HOSTAGE: return cash_player_rescued_hostage.GetInt();
  12586. case PlayerCashAward::INTERACT_WITH_HOSTAGE: return cash_player_interact_with_hostage.GetInt();
  12587. case PlayerCashAward::DAMAGE_HOSTAGE: return cash_player_damage_hostage.GetInt();
  12588. case PlayerCashAward::KILL_HOSTAGE: return cash_player_killed_hostage.GetInt();
  12589. case PlayerCashAward::RESPAWN: return cash_player_respawn_amount.GetInt();
  12590. case PlayerCashAward::GET_KILLED: return cash_player_get_killed.GetInt();
  12591. default:
  12592. AssertMsg( false, "Unhandled PlayerCashAwardReason" );
  12593. return 0;
  12594. };
  12595. }
  12596. //////////////////////////////////////////////////////////////////////////
  12597. // Guardian mode getter functions for UI
  12598. //////////////////////////////////////////////////////////////////////////
  12599. int CCSGameRules::GetGuardianRequiredKills() const
  12600. {
  12601. #ifdef CLIENT_DLL
  12602. static ConVarRef mp_guardian_special_kills_needed( "mp_guardian_special_kills_needed" );
  12603. #endif
  12604. return mp_guardian_special_kills_needed.GetInt();
  12605. }
  12606. int CCSGameRules::GetGuardianKillsRemaining() const
  12607. {
  12608. return m_nGuardianModeSpecialKillsRemaining;
  12609. }
  12610. int CCSGameRules::GetGuardianSpecialWeapon() const
  12611. {
  12612. return m_nGuardianModeSpecialWeaponNeeded;
  12613. }
  12614. //////////////////////////////////////////////////////////////////////////
  12615. // Gun game getter functions
  12616. //////////////////////////////////////////////////////////////////////////
  12617. // Determines the highest weapon index of all players in the arms race match
  12618. void CCSGameRules::CalculateMaxGunGameProgressiveWeaponIndex( void )
  12619. {
  12620. m_iMaxGunGameProgressiveWeaponIndex = 0;
  12621. if ( IsPlayingGunGameProgressive() )
  12622. {
  12623. // Loop through all players and find the max progressive weapon index
  12624. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  12625. {
  12626. CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  12627. if ( pPlayer )
  12628. {
  12629. if ( pPlayer->GetPlayerGunGameWeaponIndex() > m_iMaxGunGameProgressiveWeaponIndex )
  12630. {
  12631. m_iMaxGunGameProgressiveWeaponIndex = pPlayer->GetPlayerGunGameWeaponIndex();
  12632. }
  12633. }
  12634. }
  12635. }
  12636. }
  12637. int CCSGameRules::GetCurrentGunGameWeapon ( int nCurrentWeaponIndex, int nTeamID )
  12638. {
  12639. if ( GetNumProgressiveGunGameWeapons( nTeamID ) == 0 )
  12640. {
  12641. // Don't process if weapon array is empty
  12642. return -1;
  12643. }
  12644. if ( nCurrentWeaponIndex < 0 || nCurrentWeaponIndex >= GetNumProgressiveGunGameWeapons( nTeamID ) )
  12645. {
  12646. // Don't process if out of the array bounds
  12647. return -1;
  12648. }
  12649. return GetProgressiveGunGameWeapon( nCurrentWeaponIndex, nTeamID );
  12650. }
  12651. int CCSGameRules::GetNextGunGameWeapon( int nCurrentWeaponIndex, int nTeamID )
  12652. {
  12653. if ( GetNumProgressiveGunGameWeapons( nTeamID ) == 0 )
  12654. {
  12655. // Don't process if weapon array is empty
  12656. return -1;
  12657. }
  12658. if ( nCurrentWeaponIndex < 0 || nCurrentWeaponIndex >= GetNumProgressiveGunGameWeapons( nTeamID ) - 1 )
  12659. {
  12660. // Don't process if already at last weapon or out of the array bounds
  12661. return -1;
  12662. }
  12663. return GetProgressiveGunGameWeapon( nCurrentWeaponIndex + 1, nTeamID );
  12664. }
  12665. int CCSGameRules::GetPreviousGunGameWeapon( int nCurrentWeaponIndex, int nTeamID )
  12666. {
  12667. if ( GetNumProgressiveGunGameWeapons( nTeamID ) == 0 )
  12668. {
  12669. // Don't process if weapon array is empty
  12670. return -1;
  12671. }
  12672. if ( nCurrentWeaponIndex <= 0 || nCurrentWeaponIndex >= GetNumProgressiveGunGameWeapons( nTeamID ) )
  12673. {
  12674. // Don't process if already at first weapon or out of the array bounds
  12675. return -1;
  12676. }
  12677. return GetProgressiveGunGameWeapon( nCurrentWeaponIndex - 1, nTeamID );
  12678. }
  12679. bool CCSGameRules::IsFinalGunGameProgressiveWeapon( int nCurrentWeaponIndex, int nTeamID )
  12680. {
  12681. // Determine if the current weapon is the last in the list of gun game weapons
  12682. if ( nCurrentWeaponIndex == GetNumProgressiveGunGameWeapons( nTeamID ) - 1 )
  12683. {
  12684. return true;
  12685. }
  12686. return false;
  12687. }
  12688. int CCSGameRules::GetGunGameNumKillsRequiredForWeapon( int nCurrentWeaponIndex, int nTeamID )
  12689. {
  12690. if ( nCurrentWeaponIndex < 0 || nCurrentWeaponIndex > GetNumProgressiveGunGameWeapons( nTeamID ) - 1 )
  12691. {
  12692. // Don't process if out of the array bounds
  12693. return -1;
  12694. }
  12695. return GetProgressiveGunGameWeaponKillRequirement( nCurrentWeaponIndex, nTeamID );
  12696. }
  12697. CBaseCombatWeapon *CCSGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
  12698. {
  12699. CBaseCombatWeapon *bestWeapon = NULL;
  12700. // search all the weapons looking for the closest next
  12701. for ( int i = 0; i < MAX_WEAPONS; i++ )
  12702. {
  12703. CBaseCombatWeapon *weapon = pPlayer->GetWeapon(i);
  12704. if ( !weapon )
  12705. continue;
  12706. if ( !weapon->CanBeSelected() || weapon == pCurrentWeapon )
  12707. continue;
  12708. #ifndef CLIENT_DLL
  12709. CCSPlayer *csPlayer = ToCSPlayer(pPlayer);
  12710. CWeaponCSBase *csWeapon = static_cast< CWeaponCSBase * >(weapon);
  12711. if ( csPlayer && csPlayer->IsBot() && !TheCSBots()->IsWeaponUseable( csWeapon ) )
  12712. continue;
  12713. #endif // CLIENT_DLL
  12714. if ( bestWeapon )
  12715. {
  12716. if ( weapon->GetSlot() < bestWeapon->GetSlot() )
  12717. {
  12718. int nAmmo = 0;
  12719. if ( weapon->UsesClipsForAmmo1() )
  12720. {
  12721. nAmmo = weapon->Clip1();
  12722. }
  12723. else
  12724. {
  12725. if ( pPlayer )
  12726. {
  12727. nAmmo = weapon->GetReserveAmmoCount( AMMO_POSITION_PRIMARY );
  12728. }
  12729. else
  12730. {
  12731. // No owner, so return how much primary ammo I have along with me.
  12732. nAmmo = weapon->GetPrimaryAmmoCount();
  12733. }
  12734. }
  12735. if ( nAmmo > 0 )
  12736. {
  12737. bestWeapon = weapon;
  12738. }
  12739. }
  12740. else if ( weapon->GetSlot() == bestWeapon->GetSlot() && weapon->GetPosition() < bestWeapon->GetPosition() )
  12741. {
  12742. bestWeapon = weapon;
  12743. }
  12744. }
  12745. else
  12746. {
  12747. bestWeapon = weapon;
  12748. }
  12749. }
  12750. return bestWeapon;
  12751. }
  12752. float CCSGameRules::GetMapRemainingTime()
  12753. {
  12754. // if timelimit is disabled, return -1
  12755. if ( mp_timelimit.GetInt() <= 0 )
  12756. return -1;
  12757. // timelimit is in minutes
  12758. float flTimeLeft = ( m_flGameStartTime + mp_timelimit.GetInt() * 60 ) - gpGlobals->curtime;
  12759. // never return a negative value
  12760. if ( flTimeLeft < 0 )
  12761. flTimeLeft = 0;
  12762. return flTimeLeft;
  12763. }
  12764. float CCSGameRules::GetMapElapsedTime( void )
  12765. {
  12766. return gpGlobals->curtime;
  12767. }
  12768. float CCSGameRules::GetRoundRemainingTime() const
  12769. {
  12770. return (float) (m_fRoundStartTime + m_iRoundTime) - gpGlobals->curtime;
  12771. }
  12772. float CCSGameRules::GetRoundStartTime()
  12773. {
  12774. return m_fRoundStartTime;
  12775. }
  12776. float CCSGameRules::GetRoundElapsedTime()
  12777. {
  12778. if ( IsPlayingTraining() )
  12779. return 0.0f;
  12780. float remainingTime = m_iRoundTime - GetRoundRemainingTime();
  12781. if ( remainingTime >= 0.0f )
  12782. return remainingTime;
  12783. else
  12784. return 0.0f;
  12785. }
  12786. bool CCSGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
  12787. {
  12788. if ( collisionGroup0 > collisionGroup1 )
  12789. {
  12790. // swap so that lowest is always first
  12791. V_swap(collisionGroup0,collisionGroup1);
  12792. }
  12793. //Don't stand on COLLISION_GROUP_WEAPONs
  12794. if( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT &&
  12795. collisionGroup1 == COLLISION_GROUP_WEAPON )
  12796. {
  12797. return false;
  12798. }
  12799. // TODO: make a CS-SPECIFIC COLLISION GROUP FOR PHYSICS PROPS THAT USE THIS COLLISION BEHAVIOR.
  12800. if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
  12801. collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
  12802. {
  12803. return false;
  12804. }
  12805. if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
  12806. {
  12807. // let debris and multiplayer objects collide
  12808. return true;
  12809. }
  12810. return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
  12811. }
  12812. bool CCSGameRules::IsFreezePeriod()
  12813. {
  12814. return m_bFreezePeriod;
  12815. }
  12816. bool CCSGameRules::IsWarmupPeriod() const
  12817. {
  12818. if ( IsPlayingTraining() )
  12819. return false;
  12820. if ( IsPlayingOffline() && !mp_do_warmup_offine.GetBool() )
  12821. return false;
  12822. return m_bWarmupPeriod;
  12823. }
  12824. bool CCSGameRules::AllowTaunts( void )
  12825. {
  12826. /** Removed for partner depot **/
  12827. return false;
  12828. }
  12829. #ifdef CLIENT_DLL
  12830. bool CCSGameRules::AllowThirdPersonCamera()
  12831. {
  12832. return sv_allow_thirdperson.GetBool();
  12833. }
  12834. bool CCSGameRules::IsGoodDownTime( void )
  12835. {
  12836. if ( CSGameRules()->IsGameRestarting() )
  12837. return true;
  12838. if ( CSGameRules()->IsIntermission() )
  12839. return true;
  12840. if ( CSGameRules()->InRoundRestart() )
  12841. return true;
  12842. if ( ( ( CSGameRules()->GetRoundRestartTime() - gpGlobals->curtime ) < 0.5f
  12843. && ( CSGameRules()->GetRoundRestartTime() - gpGlobals->curtime ) > 0.0f ) )
  12844. return true;
  12845. return false;
  12846. }
  12847. bool CCSGameRules::IsLoadoutAllowed( void )
  12848. {
  12849. // main menu
  12850. if ( !engine->IsConnected() )
  12851. return true;
  12852. C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  12853. if ( !pLocalPlayer )
  12854. return false;
  12855. // non players
  12856. if ( ( pLocalPlayer->IsSpectator() ) ||
  12857. ( pLocalPlayer->IsHLTV() ) ||
  12858. ( engine->IsPlayingDemo() ) )
  12859. {
  12860. return true;
  12861. }
  12862. // game mode specific rules
  12863. if ( IsPlayingAnyCompetitiveStrictRuleset() )
  12864. {
  12865. return ( IsWarmupPeriod() );
  12866. }
  12867. else if ( ( IsPlayingGunGameDeathmatch() || IsPlayingCooperativeGametype() ) )
  12868. {
  12869. return true;
  12870. }
  12871. else if ( IsWarmupPeriod() || !pLocalPlayer->IsAlive() )
  12872. {
  12873. return true;
  12874. }
  12875. return false;
  12876. }
  12877. #endif
  12878. float CCSGameRules::GetWarmupPeriodEndTime() const
  12879. {
  12880. return m_fWarmupPeriodStart + mp_warmuptime.GetFloat();
  12881. }
  12882. bool CCSGameRules::IsWarmupPeriodPaused()
  12883. {
  12884. return mp_warmup_pausetimer.GetBool();
  12885. }
  12886. bool CCSGameRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer )
  12887. {
  12888. #ifndef CLIENT_DLL
  12889. if ( pPlayer )
  12890. {
  12891. int iPlayerTeam = pPlayer->GetTeamNumber();
  12892. if ( ( iPlayerTeam == TEAM_TERRORIST ) || ( iPlayerTeam == TEAM_CT ) )
  12893. return false;
  12894. }
  12895. #else
  12896. int iLocalPlayerTeam = GetLocalPlayerTeam();
  12897. if ( ( iLocalPlayerTeam == TEAM_TERRORIST ) || ( iLocalPlayerTeam == TEAM_CT ) )
  12898. return false;
  12899. #endif
  12900. return true;
  12901. }
  12902. #ifndef CLIENT_DLL
  12903. void CCSGameRules::StartWarmup( void )
  12904. {
  12905. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  12906. return;
  12907. m_bWarmupPeriod = true;
  12908. m_bCompleteReset = true;
  12909. m_fWarmupPeriodStart = gpGlobals->curtime;
  12910. RestartRound();
  12911. }
  12912. void CCSGameRules::EndWarmup( void )
  12913. {
  12914. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  12915. return;
  12916. if ( !m_bWarmupPeriod )
  12917. return;
  12918. m_bWarmupPeriod = false;
  12919. m_bCompleteReset = true;
  12920. m_fWarmupPeriodStart = -1;
  12921. CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource );
  12922. if ( pResource )
  12923. pResource->ForcePlayersPickColors();
  12924. RestartRound();
  12925. }
  12926. void CCSGameRules::StartTerroristTimeOut( void )
  12927. {
  12928. if ( m_bTerroristTimeOutActive || m_bCTTimeOutActive )
  12929. return;
  12930. if ( m_nTerroristTimeOuts <= 0 )
  12931. return;
  12932. m_bTerroristTimeOutActive = true;
  12933. m_flTerroristTimeOutRemaining = mp_team_timeout_time.GetInt();
  12934. m_nTerroristTimeOuts--;
  12935. m_bMatchWaitingForResume = true;
  12936. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" );
  12937. }
  12938. void CCSGameRules::EndTerroristTimeOut( void )
  12939. {
  12940. if ( !m_bTerroristTimeOutActive )
  12941. return;
  12942. m_bTerroristTimeOutActive = false;
  12943. m_bMatchWaitingForResume = false;
  12944. }
  12945. void CCSGameRules::StartCTTimeOut( void )
  12946. {
  12947. if ( m_bCTTimeOutActive || m_bTerroristTimeOutActive )
  12948. return;
  12949. if ( m_nCTTimeOuts <= 0 )
  12950. return;
  12951. m_bCTTimeOutActive = true;
  12952. m_flCTTimeOutRemaining = mp_team_timeout_time.GetInt();
  12953. m_nCTTimeOuts--;
  12954. m_bMatchWaitingForResume = true;
  12955. UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" );
  12956. }
  12957. void CCSGameRules::EndCTTimeOut( void )
  12958. {
  12959. if ( !m_bCTTimeOutActive )
  12960. return;
  12961. m_bCTTimeOutActive = false;
  12962. m_bMatchWaitingForResume = false;
  12963. }
  12964. #endif
  12965. AcquireResult::Type CCSGameRules::IsWeaponAllowed( const CCSWeaponInfo *pWeaponInfo, int nTeamNumber, CEconItemView *pItem )
  12966. {
  12967. CSWeaponID weaponId = WEAPON_NONE;
  12968. CSWeaponType weaponType = WEAPONTYPE_UNKNOWN;
  12969. if ( pItem && pItem->IsValid() )
  12970. {
  12971. weaponId = WeaponIdFromString( pItem->GetStaticData()->GetItemClass() );
  12972. if ( pWeaponInfo )
  12973. weaponType = pWeaponInfo->GetWeaponType( pItem );
  12974. }
  12975. else if ( pWeaponInfo )
  12976. {
  12977. weaponId = pWeaponInfo->m_weaponId;
  12978. weaponType = pWeaponInfo->GetWeaponType( pItem );
  12979. }
  12980. // prohibited items. currently only supports schema items
  12981. //
  12982. //
  12983. if ( ( CSGameRules()->m_arrProhibitedItemIndices[ 0 ] != 0 ) && pItem && pItem->IsValid() )
  12984. {
  12985. int nPosition = pItem->GetItemDefinition()->GetLoadoutSlot( nTeamNumber );
  12986. const CEconItemView* pBaseItem = CSInventoryManager()->GetBaseItemForTeam( nTeamNumber, nPosition );
  12987. if ( pBaseItem && pBaseItem->IsValid() )
  12988. {
  12989. for ( int j = 0; j < MAX_PROHIBITED_ITEMS; j++ )
  12990. {
  12991. // if the base item is prohibited then the slot is prohibited
  12992. if ( CSGameRules()->m_arrProhibitedItemIndices[ j ] == pBaseItem->GetItemDefinition()->GetDefinitionIndex() )
  12993. {
  12994. return AcquireResult::NotAllowedByProhibition;
  12995. }
  12996. // if the base item is not prohibited then the alternate item might still be prohibited
  12997. if ( CSGameRules()->m_arrProhibitedItemIndices[ j ] == pItem->GetItemDefinition()->GetDefinitionIndex() )
  12998. {
  12999. return AcquireResult::NotAllowedByProhibition;
  13000. }
  13001. }
  13002. }
  13003. }
  13004. ///////////////////////////////
  13005. switch ( weaponId )
  13006. {
  13007. case ITEM_DEFUSER:
  13008. case ITEM_CUTTERS:
  13009. if ( nTeamNumber == TEAM_TERRORIST )
  13010. {
  13011. return AcquireResult::NotAllowedByTeam;
  13012. }
  13013. if ( !IsBombDefuseMap() )
  13014. {
  13015. return AcquireResult::NotAllowedByMap;
  13016. }
  13017. break;
  13018. case WEAPON_TASER:
  13019. // special case for limiting taser to classic casual; data drive this if it becomes more complex
  13020. if ( !mp_weapons_allow_zeus.GetBool() )
  13021. {
  13022. return AcquireResult::NotAllowedForPurchase;
  13023. }
  13024. break;
  13025. case WEAPON_C4:
  13026. if ( nTeamNumber != TEAM_TERRORIST && !IsPlayingTraining() && mp_anyone_can_pickup_c4.GetBool() == false )
  13027. {
  13028. return AcquireResult::NotAllowedByTeam;
  13029. }
  13030. break;
  13031. }
  13032. return AcquireResult::Allowed;
  13033. }
  13034. bool CCSGameRules::IsBombDefuseMap() const
  13035. {
  13036. return m_bMapHasBombTarget;
  13037. }
  13038. bool CCSGameRules::IsHostageRescueMap() const
  13039. {
  13040. return m_bMapHasRescueZone;
  13041. }
  13042. bool CCSGameRules::MapHasBuyZone() const
  13043. {
  13044. return m_bMapHasBuyZone;
  13045. }
  13046. bool CCSGameRules::CanSpendMoneyInMap()
  13047. {
  13048. if ( GetMaxMoney() <= 0 )
  13049. return false;
  13050. if ( PlayerCashAwardsEnabled() == false && TeamCashAwardsEnabled() == false )
  13051. return false;
  13052. if ( IsPlayingCoopMission() )
  13053. return false;
  13054. if ( IsPlayingCoopGuardian() )
  13055. {
  13056. int nTeam = CSGameRules()->IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  13057. ConVarRef sv_buy_status_override( "sv_buy_status_override" );
  13058. int iBuyStatus = sv_buy_status_override.GetInt();
  13059. if ( iBuyStatus > 0 && (( nTeam == TEAM_CT && iBuyStatus != 1 ) || ( nTeam == TEAM_TERRORIST && iBuyStatus != 2 )) )
  13060. return false;
  13061. }
  13062. return true;
  13063. }
  13064. bool CCSGameRules::IsLogoMap() const
  13065. {
  13066. return m_bLogoMap;
  13067. }
  13068. float CCSGameRules::GetBuyTimeLength()
  13069. {
  13070. if ( IsWarmupPeriod() )
  13071. {
  13072. if ( IsWarmupPeriodPaused( ) )
  13073. return GetWarmupPeriodEndTime( )-m_fWarmupPeriodStart;
  13074. if ( mp_buytime.GetFloat() < GetWarmupPeriodEndTime() )
  13075. return GetWarmupPeriodEndTime();
  13076. }
  13077. return mp_buytime.GetFloat();
  13078. }
  13079. bool CCSGameRules::IsBuyTimeElapsed()
  13080. {
  13081. if ( IsWarmupPeriod() && IsWarmupPeriodPaused() )
  13082. return false;
  13083. return ( GetRoundElapsedTime() > GetBuyTimeLength() );
  13084. }
  13085. bool CCSGameRules::IsMatchWaitingForResume()
  13086. {
  13087. if ( m_bMatchWaitingForResume )
  13088. return true;
  13089. if ( sv_matchpause_auto_5v5.GetBool() && !IsWarmupPeriod() )
  13090. {
  13091. #ifdef CLIENT_DLL
  13092. C_Team
  13093. #else
  13094. CTeam
  13095. #endif
  13096. *arrTeams[2] = { GetGlobalTeam( TEAM_CT ), GetGlobalTeam( TEAM_TERRORIST ) };
  13097. for ( int iTeam = 0; iTeam < Q_ARRAYSIZE( arrTeams ); ++ iTeam )
  13098. {
  13099. if ( !arrTeams[iTeam] ) continue;
  13100. int numPlayers = arrTeams[iTeam]->GetNumPlayers();
  13101. if ( numPlayers < 5 )
  13102. {
  13103. SetMatchWaitingForResume( true ) ;
  13104. return true;
  13105. }
  13106. int numHumans = 0;
  13107. for ( int iPlayer = 0; iPlayer < numPlayers; ++ iPlayer )
  13108. {
  13109. CBasePlayer *pPlayer = arrTeams[iTeam]->GetPlayer( iPlayer );
  13110. if ( !pPlayer ) continue;
  13111. #ifndef CLIENT_DLL
  13112. if ( !pPlayer->IsConnected() ) continue;
  13113. #endif
  13114. if ( pPlayer->IsBot() ) continue;
  13115. ++ numHumans;
  13116. }
  13117. if ( numHumans < 5 )
  13118. {
  13119. SetMatchWaitingForResume( true );
  13120. return true;
  13121. }
  13122. }
  13123. }
  13124. return false;
  13125. }
  13126. int CCSGameRules::DefaultFOV()
  13127. {
  13128. return 90;
  13129. }
  13130. const CViewVectors* CCSGameRules::GetViewVectors() const
  13131. {
  13132. return &g_CSViewVectors;
  13133. }
  13134. #if defined( GAME_DLL)
  13135. //=========================================================
  13136. //=========================================================
  13137. bool CCSGameRules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
  13138. {
  13139. CCSPlayer *pCSAttacker = ToCSPlayer( pAttacker );
  13140. if ( pCSAttacker && PlayerRelationship( pPlayer, pCSAttacker ) == GR_TEAMMATE && !pCSAttacker->IsOtherEnemy( pPlayer->entindex() ) )
  13141. {
  13142. // my teammate hit me.
  13143. if ( ( mp_friendlyfire.GetInt() == 0 ) && ( pCSAttacker != pPlayer ) )
  13144. {
  13145. // friendly fire is off, and this hit came from someone other than myself, then don't get hurt
  13146. return false;
  13147. }
  13148. }
  13149. return BaseClass::FPlayerCanTakeDamage( pPlayer, pCSAttacker );
  13150. }
  13151. //=========================================================
  13152. //=========================================================
  13153. int CCSGameRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
  13154. {
  13155. CCSPlayer *pCSAttacker = ToCSPlayer( pAttacker );
  13156. if ( !pKilled )
  13157. return 0;
  13158. if ( !pCSAttacker )
  13159. return 1;
  13160. if ( pCSAttacker != pKilled && PlayerRelationship( pCSAttacker, pKilled ) == GR_TEAMMATE && !pCSAttacker->IsOtherEnemy( pKilled->entindex() ) )
  13161. return -1;
  13162. return 1;
  13163. }
  13164. /*
  13165. Helper function which handles both voice and chat. The only difference is which convar to use
  13166. to determine whether enemies can be heard (sv_alltalk or sv_allchat).
  13167. */
  13168. bool CanPlayerHear( CBasePlayer* pListener, CBasePlayer *pSpeaker, bool bTeamOnly, bool bHearEnemies )
  13169. {
  13170. Assert(pListener != NULL && pSpeaker != NULL);
  13171. if ( pListener == NULL || pSpeaker == NULL )
  13172. return false;
  13173. // sv_full_alltalk lets everyone can talk to everyone else, except comms specifically flagged as team-only
  13174. if ( !bTeamOnly && sv_full_alltalk.GetBool() )
  13175. return true;
  13176. // if either speaker or listener are coaching then for intents and purposes treat them as teammates.
  13177. int iListenerTeam = pListener->GetAssociatedTeamNumber();
  13178. int iSpeakerTeam = pSpeaker->GetAssociatedTeamNumber();
  13179. // use the observed target's team when sv_spec_hear is mode 2
  13180. if ( iListenerTeam == TEAM_SPECTATOR && sv_spec_hear.GetInt() == SpecHear::SpectatedTeam &&
  13181. ( pListener->GetObserverMode() == OBS_MODE_IN_EYE || pListener->GetObserverMode() == OBS_MODE_CHASE ) )
  13182. {
  13183. CBaseEntity *pTarget = pListener->GetObserverTarget();
  13184. if ( pTarget && pTarget->IsPlayer() )
  13185. {
  13186. iListenerTeam = pTarget->GetTeamNumber();
  13187. }
  13188. }
  13189. if ( iListenerTeam == TEAM_SPECTATOR )
  13190. {
  13191. if ( sv_spec_hear.GetInt() == SpecHear::Nobody )
  13192. return false; // spectators are selected to not hear other spectators
  13193. if ( sv_spec_hear.GetInt() == SpecHear::Self )
  13194. return ( pListener == pSpeaker ); // spectators are selected to not hear other spectators
  13195. // spectators can always hear other spectators
  13196. if ( iSpeakerTeam == TEAM_SPECTATOR )
  13197. return true;
  13198. return !bTeamOnly && sv_spec_hear.GetInt() == SpecHear::AllPlayers;
  13199. }
  13200. // no one else can hear spectators
  13201. if ( ( iSpeakerTeam != TEAM_TERRORIST ) &&
  13202. ( iSpeakerTeam != TEAM_CT ) )
  13203. return false;
  13204. // are enemy teams prevented from hearing each other by sv_alltalk/sv_allchat?
  13205. if ( (bTeamOnly || !bHearEnemies) && iSpeakerTeam != iListenerTeam )
  13206. return false;
  13207. // living players can only hear dead players if sv_deadtalk is enabled
  13208. if ( pListener->IsAlive() && !pSpeaker->IsAlive() )
  13209. {
  13210. return sv_deadtalk.GetBool();
  13211. }
  13212. return true;
  13213. }
  13214. bool CCSGameRules::CanPlayerHearTalker( CBasePlayer* pListener, CBasePlayer *pSpeaker, bool bTeamOnly )
  13215. {
  13216. bool bHearEnemy = false;
  13217. if ( sv_talk_enemy_living.GetBool() && sv_talk_enemy_dead.GetBool() )
  13218. {
  13219. bHearEnemy = true;
  13220. }
  13221. else if ( !pListener->IsAlive() && !pSpeaker->IsAlive() )
  13222. {
  13223. bHearEnemy = sv_talk_enemy_dead.GetBool();
  13224. }
  13225. else if ( pListener->IsAlive() && pSpeaker->IsAlive() )
  13226. {
  13227. bHearEnemy = sv_talk_enemy_living.GetBool();
  13228. }
  13229. return CanPlayerHear( pListener, pSpeaker, bTeamOnly, bHearEnemy );
  13230. }
  13231. extern ConVar sv_allchat;
  13232. bool CCSGameRules::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker, bool bTeamOnly )
  13233. {
  13234. return CanPlayerHear( pListener, pSpeaker, bTeamOnly, sv_allchat.GetBool() );
  13235. }
  13236. int CCSGameRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
  13237. {
  13238. // half life multiplay has a simple concept of Player Relationships.
  13239. // you are either on another player's team, or you are not.
  13240. if ( !pPlayer || !pTarget || !pTarget->IsPlayer() )
  13241. return GR_NOTTEAMMATE;
  13242. // don't do string compares, just compare the team number
  13243. // if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
  13244. // {
  13245. // return GR_TEAMMATE;
  13246. // }
  13247. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  13248. CCSPlayer *pCSTarget = ToCSPlayer( pTarget );
  13249. if ( pCSPlayer->GetAssociatedTeamNumber() != TEAM_INVALID && pCSTarget->GetAssociatedTeamNumber() != TEAM_INVALID && pCSPlayer->GetAssociatedTeamNumber() == pCSTarget->GetAssociatedTeamNumber() )
  13250. {
  13251. return GR_TEAMMATE;
  13252. }
  13253. return GR_NOTTEAMMATE;
  13254. }
  13255. //-----------------------------------------------------------------------------
  13256. // Purpose:
  13257. //-----------------------------------------------------------------------------
  13258. void CCSGameRules::SetTeamRespawnWaveTime( int iTeam, float flValue )
  13259. {
  13260. if ( flValue < 0 )
  13261. {
  13262. flValue = 0;
  13263. }
  13264. // initialized to -1 so we can try to determine if this is the first spawn time we have received for this team
  13265. // if ( m_flOriginalTeamRespawnWaveTime[iTeam] < 0 )
  13266. // {
  13267. // m_flOriginalTeamRespawnWaveTime[iTeam] = flValue;
  13268. // }
  13269. m_TeamRespawnWaveTimes.Set( iTeam, flValue );
  13270. }
  13271. //-----------------------------------------------------------------------------
  13272. // Purpose:
  13273. //-----------------------------------------------------------------------------
  13274. void CCSGameRules::CheckRespawnWaves( void )
  13275. {
  13276. for ( int team = LAST_SHARED_TEAM+1; team < GetNumberOfTeams(); team++ )
  13277. {
  13278. if ( m_flNextRespawnWave[team] > gpGlobals->curtime )
  13279. continue;
  13280. if ( mp_use_respawn_waves.GetInt() == 2 )
  13281. {
  13282. if ( IsWarmupPeriod() == false && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) )
  13283. {
  13284. // we sometimes don't want to increment the wav number
  13285. if ( m_bDontIncrementCoopWave == false )
  13286. m_nGuardianModeWaveNumber++;
  13287. m_bDontIncrementCoopWave = false;
  13288. if ( IsPlayingCoopGuardian() )
  13289. {
  13290. int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT;
  13291. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  13292. {
  13293. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  13294. if ( pPlayer && pPlayer->GetTeamNumber() == nTeam )
  13295. {
  13296. //CBroadcastRecipientFilter filter;
  13297. CRecipientFilter filter;
  13298. filter.AddRecipient( pPlayer );
  13299. pPlayer->EmitSound( filter, pPlayer->entindex(), "UI.DeathMatchBonusAlertEnd" );
  13300. char szWave[32];
  13301. Q_snprintf( szWave, sizeof( szWave ), "%d", ( int )m_nGuardianModeWaveNumber );
  13302. const char *szTeam = (nTeam == TEAM_CT) ? "CT" : "T";
  13303. int nMsg = RandomInt( 1, 8 );
  13304. char szNotice[512];
  13305. Q_snprintf( szNotice, sizeof( szNotice ), "#SFUI_Notice_NewWaveBegun_%s%d", szTeam, ( int )nMsg );
  13306. // send the message
  13307. UTIL_ClientPrintFilter(filter, HUD_PRINTCENTER, szNotice, szWave );
  13308. }
  13309. }
  13310. }
  13311. }
  13312. // if this mode is set, we don't want to set the respawn time to anythign other than infinity, pretty much
  13313. m_flNextRespawnWave.Set( team, gpGlobals->curtime + 99999 );
  13314. }
  13315. else
  13316. m_flNextRespawnWave.Set( team, gpGlobals->curtime + GetRespawnWaveMaxLength( team ) );
  13317. // respawn all players who are able to respawn here
  13318. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  13319. {
  13320. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  13321. if ( pPlayer && pPlayer->PlayerClass() != 0 && pPlayer->GetTeamNumber() > TEAM_SPECTATOR && pPlayer->GetTeamNumber() == team && !pPlayer->IsAlive() )
  13322. {
  13323. bool bMatchRespawnGamePhase = false;
  13324. switch ( m_match.GetPhase() )
  13325. {
  13326. case GAMEPHASE_WARMUP_ROUND:
  13327. case GAMEPHASE_PLAYING_STANDARD:
  13328. case GAMEPHASE_PLAYING_FIRST_HALF:
  13329. case GAMEPHASE_PLAYING_SECOND_HALF:
  13330. bMatchRespawnGamePhase = true;
  13331. break;
  13332. }
  13333. if ( ( IsPlayingCoopGuardian() || IsPlayingCoopMission() ) && pPlayer->IsBot() && pPlayer->IsAbleToInstantRespawn() )
  13334. {
  13335. GuardianUpdateBotAccountAndWeapons( pPlayer );
  13336. // set the bot's name
  13337. CCSBot *bot = dynamic_cast< CCSBot * >( pPlayer );
  13338. if ( bot )
  13339. {
  13340. char botName[MAX_PLAYER_NAME_LENGTH];
  13341. UTIL_ConstructBotNetName( botName, MAX_PLAYER_NAME_LENGTH, bot->GetProfile() );
  13342. engine->SetFakeClientConVarValue( bot->edict(), "name", botName );
  13343. }
  13344. m_nGuardianGrenadesToGiveBots = RandomInt( 2, MIN( 5, m_nGuardianModeWaveNumber ) );
  13345. // respawn
  13346. pPlayer->State_Transition( STATE_GUNGAME_RESPAWN );
  13347. }
  13348. else if ( bMatchRespawnGamePhase && pPlayer->IsAbleToInstantRespawn() && pPlayer->GetObserverMode() > OBS_MODE_FREEZECAM )
  13349. {
  13350. // respawn
  13351. pPlayer->State_Transition( STATE_GUNGAME_RESPAWN );
  13352. }
  13353. }
  13354. }
  13355. }
  13356. }
  13357. void CCSGameRules::GuardianUpdateBotAccountAndWeapons( CCSPlayer *pBot )
  13358. {
  13359. if ( pBot->IsBot() && mp_use_respawn_waves.GetInt() == 2
  13360. && IsWarmupPeriod() == false && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) )
  13361. {
  13362. pBot->RemoveAllItems( true );
  13363. pBot->ResetAccount();
  13364. pBot->AddAccount( ( mp_guardian_bot_money_per_wave.GetInt()
  13365. * m_nGuardianModeWaveNumber ), false, false );
  13366. }
  13367. }
  13368. extern const char* Helper_PickBotGrenade();
  13369. void CCSGameRules::GiveGuardianBotGrenades( CCSPlayer *pBot )
  13370. {
  13371. if ( m_nGuardianGrenadesToGiveBots > 0 && pBot->IsBot() && mp_use_respawn_waves.GetInt() == 2
  13372. && IsWarmupPeriod() == false && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) )
  13373. {
  13374. const char* szGrenade = Helper_PickBotGrenade();
  13375. if ( szGrenade && pBot->GiveNamedItem( CFmtStr( "weapon_%s", szGrenade ).Access() ) != NULL )
  13376. m_nGuardianGrenadesToGiveBots--;
  13377. }
  13378. }
  13379. //-----------------------------------------------------------------------------
  13380. // Purpose: Is the player past the required delays for spawning
  13381. //-----------------------------------------------------------------------------
  13382. bool CCSGameRules::HasPassedMinRespawnTime( CBasePlayer *pPlayer )
  13383. {
  13384. float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer );
  13385. return ( gpGlobals->curtime > flMinSpawnTime );
  13386. }
  13387. #endif
  13388. //-----------------------------------------------------------------------------
  13389. // Purpose:
  13390. //-----------------------------------------------------------------------------
  13391. float CCSGameRules::GetRespawnTimeScalar( int iTeam )
  13392. {
  13393. if ( mp_use_respawn_waves.GetInt() == 2 )
  13394. return 1;
  13395. // For long respawn times, scale the time as the number of players drops
  13396. int iOptimalPlayers = 8; // 16 players total, 8 per team
  13397. int iNumPlayers = GetGlobalTeam(iTeam)->GetNumPlayers();
  13398. float flScale = RemapValClamped( iNumPlayers, 1, iOptimalPlayers, 0.25, 1.0 );
  13399. return flScale;
  13400. }
  13401. bool CCSGameRules::IsEndMatchVotingForNextMapEnabled()
  13402. {
  13403. if ( mp_endmatch_votenextmap.GetBool() )
  13404. {
  13405. //int numMaps = 0;
  13406. if ( g_pGameTypes )
  13407. {
  13408. #ifndef CLIENT_DLL
  13409. const char* mapGroupName = gpGlobals->mapGroupName.ToCStr();
  13410. #else
  13411. const char* mapGroupName = engine->GetMapGroupName();
  13412. #endif
  13413. const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName );
  13414. if ( mapsInGroup )
  13415. return (mapsInGroup->Count() > 1);
  13416. }
  13417. }
  13418. return false;
  13419. }
  13420. bool CCSGameRules::IsEndMatchVotingForNextMap()
  13421. {
  13422. if ( !IsEndMatchVotingForNextMapEnabled() || GetGamePhase() != GAMEPHASE_MATCH_ENDED )
  13423. return false;
  13424. if ( GetCMMItemDropRevealEndTime() > gpGlobals->curtime )
  13425. return false;
  13426. if ( m_bIsDroppingItems )
  13427. return false;
  13428. // // TODO: add a check to make sure items aren't dropping
  13429. // if ( IsEndMatchVotingForNextMapEnabled() && ( GetGamePhase() == GAMEPHASE_MATCH_ENDED ) &&
  13430. // ((gpGlobals->curtime+m_timeUntilNextPhaseStarts) >= gpGlobals->curtime+mp_win_panel_display_time.GetFloat()) && gpGlobals->curtime+GetCMMItemDropRevealEndTime() < gpGlobals->curtime )
  13431. // {
  13432. // return true;
  13433. // }
  13434. return true;
  13435. }
  13436. //-----------------------------------------------------------------------------
  13437. // Purpose: How long are the respawn waves for this team currently?
  13438. //-----------------------------------------------------------------------------
  13439. float CCSGameRules::GetRespawnWaveMaxLength( int iTeam, bool bScaleWithNumPlayers /* = true */ )
  13440. {
  13441. //Let's just turn off respawn times while players are messing around waiting for the tournament to start
  13442. if ( IsWarmupPeriod() || m_bFreezePeriod )
  13443. return 0.0f;
  13444. if ( mp_use_respawn_waves.GetInt() == 0 )
  13445. return 0.0f;
  13446. float flWaveTime = 0;
  13447. if ( iTeam == TEAM_TERRORIST )
  13448. flWaveTime = mp_respawnwavetime_t.GetFloat();
  13449. else if ( iTeam == TEAM_CT )
  13450. flWaveTime = mp_respawnwavetime_ct.GetFloat();
  13451. //float flTime = ( ( m_TeamRespawnWaveTimes[iTeam] >= 0 ) ? m_TeamRespawnWaveTimes[iTeam] : flWaveTime );
  13452. float flTime = flWaveTime;
  13453. // For long respawn times, scale the time as the number of players drops
  13454. if ( bScaleWithNumPlayers && flTime > 5 )
  13455. {
  13456. flTime = MAX( 5, flTime * GetRespawnTimeScalar(iTeam) );
  13457. }
  13458. return flTime;
  13459. }
  13460. //-----------------------------------------------------------------------------
  13461. // Purpose:
  13462. //-----------------------------------------------------------------------------
  13463. float CCSGameRules::GetMinTimeWhenPlayerMaySpawn( CBasePlayer *pPlayer )
  13464. {
  13465. // Min respawn time is the sum of
  13466. //
  13467. // a) the length of one full *unscaled* respawn wave for their team
  13468. // and
  13469. // b) death anim length + freeze panel length
  13470. float fMinDelay = 2.0f + GetRespawnWaveMaxLength( pPlayer->GetTeamNumber(), false );
  13471. return pPlayer->GetDeathTime() + fMinDelay;
  13472. }
  13473. void CCSGameRules::SetNextTeamRespawnWaveDelay( int iTeam, float flDelay )
  13474. {
  13475. float flNextRespawn = gpGlobals->curtime + flDelay;
  13476. m_flNextRespawnWave.Set( iTeam, flNextRespawn );
  13477. }
  13478. //-----------------------------------------------------------------------------
  13479. // Purpose: don't let us spawn before our freezepanel time would have ended, even if we skip it
  13480. //-----------------------------------------------------------------------------
  13481. float CCSGameRules::GetNextRespawnWave( int iTeam, CBasePlayer *pPlayer )
  13482. {
  13483. // if ( State_Get() == GR_STATE_STALEMATE )
  13484. // return 0;
  13485. // If we are purely checking when the next respawn wave is for this team
  13486. if ( pPlayer == NULL )
  13487. {
  13488. return m_flNextRespawnWave[iTeam];
  13489. }
  13490. // The soonest this player may spawn
  13491. float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer );
  13492. // the next scheduled respawn wave time
  13493. float flNextRespawnTime = m_flNextRespawnWave[iTeam];
  13494. // the length of one respawn wave. We'll check in increments of this
  13495. float flRespawnWaveMaxLen = GetRespawnWaveMaxLength( iTeam );
  13496. if ( flRespawnWaveMaxLen <= 0 )
  13497. {
  13498. return flNextRespawnTime;
  13499. }
  13500. // Keep adding the length of one respawn until we find a wave that
  13501. // this player will be eligible to spawn in.
  13502. while ( flNextRespawnTime < flMinSpawnTime )
  13503. {
  13504. flNextRespawnTime += flRespawnWaveMaxLen;
  13505. }
  13506. return flNextRespawnTime;
  13507. }
  13508. //-----------------------------------------------------------------------------
  13509. // Purpose: Init CS ammo definitions
  13510. //-----------------------------------------------------------------------------
  13511. // shared ammo definition
  13512. // JAY: Trying to make a more physical bullet response
  13513. #define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
  13514. #define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
  13515. static CCSAmmoDef ammoDef;
  13516. CCSAmmoDef* GetCSAmmoDef()
  13517. {
  13518. GetAmmoDef(); // to initialize the ammo info
  13519. return &ammoDef;
  13520. }
  13521. CAmmoDef* GetAmmoDef()
  13522. {
  13523. static bool bInitted = false;
  13524. if ( !bInitted )
  13525. {
  13526. bInitted = true;
  13527. ammoDef.AddAmmoType( BULLET_PLAYER_50AE, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_50AE_max", "ammo_50AE_impulse", 0, 10, 14 );
  13528. ammoDef.AddAmmoType( BULLET_PLAYER_762MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_762mm_max", "ammo_762mm_impulse", 0, 10, 14 );//Sniper ammo has little force since it just goes through the target.
  13529. ammoDef.AddAmmoType( BULLET_PLAYER_556MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_max", "ammo_556mm_impulse", 0, 10, 14 );
  13530. ammoDef.AddAmmoType( BULLET_PLAYER_556MM_SMALL, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_small_max","ammo_556mm_impulse", 0, 10, 14 ); // 15 round clip
  13531. ammoDef.AddAmmoType( BULLET_PLAYER_556MM_BOX, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_box_max","ammo_556mm_box_impulse", 0, 10, 14 );
  13532. ammoDef.AddAmmoType( BULLET_PLAYER_338MAG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_338mag_max", "ammo_338mag_impulse", 0, 12, 16 );
  13533. ammoDef.AddAmmoType( BULLET_PLAYER_9MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_9mm_max", "ammo_9mm_impulse", 0, 5, 10 );
  13534. ammoDef.AddAmmoType( BULLET_PLAYER_BUCKSHOT, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_buckshot_max", "ammo_buckshot_impulse", 0, 3, 6 );
  13535. ammoDef.AddAmmoType( BULLET_PLAYER_45ACP, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_45acp_max", "ammo_45acp_impulse", 0, 6, 10 );
  13536. ammoDef.AddAmmoType( BULLET_PLAYER_357SIG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_max", "ammo_357sig_impulse", 0, 4, 8 );
  13537. ammoDef.AddAmmoType( BULLET_PLAYER_357SIG_SMALL,DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_small_max", "ammo_357sig_impulse", 0, 4, 8 );
  13538. ammoDef.AddAmmoType( BULLET_PLAYER_357SIG_MIN, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_min_max", "ammo_357sig_impulse", 0, 4, 8 );
  13539. ammoDef.AddAmmoType( BULLET_PLAYER_57MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_57mm_max", "ammo_57mm_impulse", 0, 4, 8 );
  13540. ammoDef.AddAmmoType( AMMO_TYPE_HEGRENADE, DMG_BLAST, TRACER_LINE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 );
  13541. ammoDef.AddAmmoType( AMMO_TYPE_FLASHBANG, 0, TRACER_LINE, 0, 0, "ammo_grenade_limit_flashbang", 0, 0, 0 );
  13542. ammoDef.AddAmmoType( AMMO_TYPE_SMOKEGRENADE, 0, TRACER_LINE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 );
  13543. ammoDef.AddAmmoType( AMMO_TYPE_MOLOTOV, DMG_BURN, TRACER_NONE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 );
  13544. ammoDef.AddAmmoType( AMMO_TYPE_DECOY, 0, TRACER_NONE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 );
  13545. ammoDef.AddAmmoType( AMMO_TYPE_TASERCHARGE, DMG_SHOCK, TRACER_BEAM, 0, 0, 0, 0, 0, 0 );
  13546. ammoDef.AddAmmoType( BULLET_PLAYER_357SIG_P250, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_p250_max", "ammo_357sig_impulse", 0, 4, 8 );
  13547. ammoDef.AddAmmoType( AMMO_TYPE_HEALTHSHOT, 0, TRACER_LINE, 0, 0, "ammo_item_limit_healthshot", 0, 0, 0 );
  13548. ammoDef.AddAmmoType( AMMO_TYPE_TAGRENADE, 0, TRACER_NONE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 );
  13549. //Adrian: I set all the prices to 0 just so the rest of the buy code works
  13550. //This should be revisited.
  13551. ammoDef.AddAmmoCost( BULLET_PLAYER_50AE, 0, 7 );
  13552. ammoDef.AddAmmoCost( BULLET_PLAYER_762MM, 0, 30 );
  13553. ammoDef.AddAmmoCost( BULLET_PLAYER_556MM, 0, 30 );
  13554. ammoDef.AddAmmoCost( BULLET_PLAYER_556MM_SMALL, 0, 15 );
  13555. ammoDef.AddAmmoCost( BULLET_PLAYER_556MM_BOX, 0, 30 );
  13556. ammoDef.AddAmmoCost( BULLET_PLAYER_338MAG, 0, 10 );
  13557. ammoDef.AddAmmoCost( BULLET_PLAYER_9MM, 0, 30 );
  13558. ammoDef.AddAmmoCost( BULLET_PLAYER_BUCKSHOT, 0, 8 );
  13559. ammoDef.AddAmmoCost( BULLET_PLAYER_45ACP, 0, 25 );
  13560. ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG, 0, 13 );
  13561. ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG_P250, 0, 13 );
  13562. ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG_SMALL, 0, 13 );
  13563. ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG_MIN, 0, 12 );
  13564. ammoDef.AddAmmoCost( BULLET_PLAYER_57MM, 0, 50 );
  13565. }
  13566. return &ammoDef;
  13567. }
  13568. bool CCSGameRules::IsRoundOver() const
  13569. {
  13570. return m_iRoundWinStatus != WINNER_NONE;
  13571. }
  13572. void CCSGameRules::ClearItemsDroppedDuringMatch( void )
  13573. {
  13574. #ifndef CLIENT_DLL
  13575. m_bPlayerItemsHaveBeenDisplayed = false;
  13576. #endif
  13577. m_ItemsPtrDroppedDuringMatch.PurgeAndDeleteElements();
  13578. }
  13579. void CCSGameRules::RecordPlayerItemDrop( const CEconItemPreviewDataBlock &iteminfo )
  13580. {
  13581. if ( !iteminfo.accountid() )
  13582. return;
  13583. for ( int i = 0; i < m_ItemsPtrDroppedDuringMatch.Count(); i++ )
  13584. {
  13585. // if we've recorded this item already, don't record it again
  13586. if ( m_ItemsPtrDroppedDuringMatch[i]->itemid() == iteminfo.itemid() &&
  13587. m_ItemsPtrDroppedDuringMatch[i]->accountid() == iteminfo.accountid() )
  13588. return;
  13589. }
  13590. // on the client, we add all local player items to the top of the list
  13591. // server just adds them
  13592. #if defined( CLIENT_DLL )
  13593. // don't put the local player's at the top anymore - matt wood
  13594. // C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  13595. // C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR );
  13596. // if ( cs_PR && pLocalPlayer )
  13597. // {
  13598. // XUID localXuid = cs_PR->GetXuid( pLocalPlayer->entindex() );
  13599. // if (localXuid == steamOwnerID.ConvertToUint64())
  13600. // {
  13601. // m_ItemsPtrDroppedDuringMatch.AddToHead( event );
  13602. // return;
  13603. // }
  13604. // }
  13605. if ( CDemoPlaybackParameters_t const *pParams = engine->GetDemoPlaybackParameters() )
  13606. { // When playing back Overwatch with anonymous player identities don't collect drops on client
  13607. if ( pParams->m_bAnonymousPlayerIdentity )
  13608. return;
  13609. }
  13610. // check to see if this player is still connected
  13611. bool bFoundPlayer = false;
  13612. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  13613. {
  13614. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) );
  13615. if ( pPlayer )
  13616. {
  13617. CSteamID steamID;
  13618. pPlayer->GetSteamID( &steamID );
  13619. if ( steamID.GetAccountID() == iteminfo.accountid() )
  13620. {
  13621. bFoundPlayer = true;
  13622. break;
  13623. }
  13624. }
  13625. }
  13626. // check to see if this player is still connected
  13627. if ( bFoundPlayer )
  13628. m_ItemsPtrDroppedDuringMatch.AddToTail( new CEconItemPreviewDataBlock( iteminfo ) );
  13629. #else
  13630. m_ItemsPtrDroppedDuringMatch.AddToTail( new CEconItemPreviewDataBlock( iteminfo ) );
  13631. #endif
  13632. }
  13633. #ifndef CLIENT_DLL
  13634. const char *CCSGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
  13635. {
  13636. char *pszPrefix = NULL;
  13637. if ( !pPlayer ) // dedicated server output
  13638. {
  13639. pszPrefix = "";
  13640. }
  13641. else
  13642. {
  13643. // team only
  13644. if ( bTeamOnly == TRUE )
  13645. {
  13646. if ( pPlayer->GetTeamNumber() == TEAM_CT )
  13647. {
  13648. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  13649. {
  13650. pszPrefix = "(Counter-Terrorist)";
  13651. }
  13652. else
  13653. {
  13654. pszPrefix = "*DEAD*(Counter-Terrorist)";
  13655. }
  13656. }
  13657. else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  13658. {
  13659. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  13660. {
  13661. pszPrefix = "(Terrorist)";
  13662. }
  13663. else
  13664. {
  13665. pszPrefix = "*DEAD*(Terrorist)";
  13666. }
  13667. }
  13668. else if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
  13669. {
  13670. pszPrefix = "(Spectator)";
  13671. }
  13672. }
  13673. // everyone
  13674. else
  13675. {
  13676. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  13677. {
  13678. pszPrefix = "";
  13679. }
  13680. else
  13681. {
  13682. if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  13683. {
  13684. pszPrefix = "*DEAD*";
  13685. }
  13686. else
  13687. {
  13688. pszPrefix = "*SPEC*";
  13689. }
  13690. }
  13691. }
  13692. }
  13693. return pszPrefix;
  13694. }
  13695. const char *CCSGameRules::GetChatLocation( bool bTeamOnly, CBasePlayer *pPlayer )
  13696. {
  13697. if ( !pPlayer ) // dedicated server output
  13698. {
  13699. return NULL;
  13700. }
  13701. // only teammates see locations
  13702. if ( !bTeamOnly )
  13703. return NULL;
  13704. // only living players have locations
  13705. if ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
  13706. return NULL;
  13707. if ( !pPlayer->IsAlive() )
  13708. return NULL;
  13709. return pPlayer->GetLastKnownPlaceName();
  13710. }
  13711. const char *CCSGameRules::GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer )
  13712. {
  13713. if ( !pPlayer ) // dedicated server output
  13714. {
  13715. return NULL;
  13716. }
  13717. const char *pszFormat = NULL;
  13718. // team only
  13719. if ( bTeamOnly == TRUE )
  13720. {
  13721. if ( pPlayer->GetAssociatedTeamNumber() == TEAM_CT )
  13722. {
  13723. if ( ( pPlayer->m_lifeState == LIFE_ALIVE ) )
  13724. {
  13725. const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
  13726. if ( chatLocation && *chatLocation )
  13727. {
  13728. pszFormat = "Cstrike_Chat_CT_Loc";
  13729. }
  13730. else
  13731. {
  13732. pszFormat = "Cstrike_Chat_CT";
  13733. }
  13734. }
  13735. else
  13736. {
  13737. pszFormat = "Cstrike_Chat_CT_Dead";
  13738. }
  13739. }
  13740. else if ( pPlayer->GetAssociatedTeamNumber() == TEAM_TERRORIST )
  13741. {
  13742. if ( ( pPlayer->m_lifeState == LIFE_ALIVE ) )
  13743. {
  13744. const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
  13745. if ( chatLocation && *chatLocation )
  13746. {
  13747. pszFormat = "Cstrike_Chat_T_Loc";
  13748. }
  13749. else
  13750. {
  13751. pszFormat = "Cstrike_Chat_T";
  13752. }
  13753. }
  13754. else
  13755. {
  13756. pszFormat = "Cstrike_Chat_T_Dead";
  13757. }
  13758. }
  13759. else if ( pPlayer->IsSpectator() )
  13760. {
  13761. pszFormat = "Cstrike_Chat_Spec";
  13762. }
  13763. }
  13764. // everyone
  13765. else
  13766. {
  13767. if ( pPlayer->m_lifeState == LIFE_ALIVE )
  13768. {
  13769. pszFormat = "Cstrike_Chat_All";
  13770. }
  13771. else
  13772. {
  13773. if ( !pPlayer->IsSpectator() )
  13774. {
  13775. pszFormat = "Cstrike_Chat_AllDead";
  13776. }
  13777. else
  13778. {
  13779. pszFormat = "Cstrike_Chat_AllSpec";
  13780. }
  13781. }
  13782. }
  13783. return pszFormat;
  13784. }
  13785. void CCSGameRules::ClientSettingsChanged( CBasePlayer *pPlayer )
  13786. {
  13787. CCSPlayer *pCSPlayer = dynamic_cast<CCSPlayer*>( pPlayer );
  13788. if ( pCSPlayer )
  13789. {
  13790. // NOTE[pmf]: Before testing if the name has changed, we first copy it into a buffer of MAX_PLAYER_NAME_LENGTH length,
  13791. // trimming malformed utf8 (truncated) characters at the end (which we get from Steam); otherwise, when this
  13792. // correction happens as a result of CBasePlayer::SetPlayerName(), we'll always see the name as different,
  13793. // and continue to spam name change attempts.
  13794. if ( CSGameRules()->IsPlayingCoopMission() && pCSPlayer->IsBot() )
  13795. {
  13796. pCSPlayer->ChangeName( pCSPlayer->GetPlayerName() );
  13797. }
  13798. else if ( CanClientCustomizeOwnIdentity() )
  13799. {
  13800. char szNewName[MAX_PLAYER_NAME_LENGTH];
  13801. V_UTF8_strncpy( szNewName, engine->GetClientConVarValue( pCSPlayer->entindex(), "name" ), sizeof(szNewName) );
  13802. const char *pszOldName = pCSPlayer->GetPlayerName();
  13803. if ( pszOldName[0] != 0 && Q_strcmp( pszOldName, szNewName ) )
  13804. {
  13805. pCSPlayer->ChangeName( szNewName );
  13806. }
  13807. }
  13808. pCSPlayer->m_bShowHints = true;
  13809. if ( pCSPlayer->IsNetClient() )
  13810. {
  13811. const char *pShowHints = engine->GetClientConVarValue( ENTINDEX( pCSPlayer->edict() ), "cl_autohelp" );
  13812. if ( pShowHints && atoi( pShowHints ) <= 0 )
  13813. {
  13814. pCSPlayer->m_bShowHints = false;
  13815. }
  13816. }
  13817. //pCSPlayer->UpdateAppearanceIndex();
  13818. pCSPlayer->InitTeammatePreferredColor();
  13819. }
  13820. }
  13821. bool CCSGameRules::CanClientCustomizeOwnIdentity()
  13822. {
  13823. return !CCSGameRules::sm_QueuedServerReservation.tournament_teams().size();
  13824. }
  13825. bool CCSGameRules::FAllowNPCs( void )
  13826. {
  13827. if ( IsPlayingTraining() )
  13828. return true;
  13829. return true;
  13830. }
  13831. bool CCSGameRules::IsFriendlyFireOn( void ) const
  13832. {
  13833. return mp_friendlyfire.GetBool();
  13834. }
  13835. bool CCSGameRules::IsLastRoundBeforeHalfTime( void )
  13836. {
  13837. if ( HasHalfTime() )
  13838. {
  13839. int numRoundsBeforeHalftime = -1;
  13840. if ( m_match.GetPhase() == GAMEPHASE_PLAYING_FIRST_HALF )
  13841. numRoundsBeforeHalftime = GetOvertimePlaying()
  13842. ? ( mp_maxrounds.GetInt() + ( 2 * GetOvertimePlaying() - 1 ) * ( mp_overtime_maxrounds.GetInt() / 2 ) )
  13843. : ( mp_maxrounds.GetInt() / 2 );
  13844. if ( ( numRoundsBeforeHalftime > 0 ) && ( m_match.GetRoundsPlayed() == (numRoundsBeforeHalftime-1) ) )
  13845. {
  13846. return true;
  13847. }
  13848. }
  13849. return false;
  13850. }
  13851. CON_COMMAND( map_showspawnpoints, "Shows player spawn points (red=invalid). Optionally pass in the duration." )
  13852. {
  13853. int duration = 600;
  13854. if ( args.ArgC() == 2 )
  13855. {
  13856. duration = Q_atoi( args[1] );
  13857. }
  13858. CSGameRules()->ShowSpawnPoints( duration );
  13859. }
  13860. void DrawSphere( const Vector& pos, float radius, int r, int g, int b, float lifetime )
  13861. {
  13862. Vector edge, lastEdge;
  13863. NDebugOverlay::Line( pos, pos + Vector( 0, 0, 50 ), r, g, b, true, lifetime );
  13864. lastEdge = Vector( radius + pos.x, pos.y, pos.z );
  13865. float angle;
  13866. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  13867. {
  13868. edge.x = radius * BotCOS( angle ) + pos.x;
  13869. edge.y = pos.y;
  13870. edge.z = radius * BotSIN( angle ) + pos.z;
  13871. NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
  13872. lastEdge = edge;
  13873. }
  13874. lastEdge = Vector( pos.x, radius + pos.y, pos.z );
  13875. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  13876. {
  13877. edge.x = pos.x;
  13878. edge.y = radius * BotCOS( angle ) + pos.y;
  13879. edge.z = radius * BotSIN( angle ) + pos.z;
  13880. NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
  13881. lastEdge = edge;
  13882. }
  13883. lastEdge = Vector( pos.x, radius + pos.y, pos.z );
  13884. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  13885. {
  13886. edge.x = radius * BotCOS( angle ) + pos.x;
  13887. edge.y = radius * BotSIN( angle ) + pos.y;
  13888. edge.z = pos.z;
  13889. NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
  13890. lastEdge = edge;
  13891. }
  13892. }
  13893. CON_COMMAND_F( map_showbombradius, "Shows bomb radius from the center of each bomb site and planted bomb.", FCVAR_CHEAT )
  13894. {
  13895. float flBombDamage = 500.0f;
  13896. if ( g_pMapInfo )
  13897. flBombDamage = g_pMapInfo->m_flBombRadius;
  13898. float flBombRadius = flBombDamage * 3.5f;
  13899. Msg( "Bomb Damage is %.0f, Radius is %.0f\n", flBombDamage, flBombRadius );
  13900. CBaseEntity* ent = NULL;
  13901. while ( ( ent = gEntList.FindEntityByClassname( ent, "func_bomb_target" ) ) != NULL )
  13902. {
  13903. const Vector &pos = ent->WorldSpaceCenter();
  13904. DrawSphere( pos, flBombRadius, 255, 255, 0, 10 );
  13905. }
  13906. ent = NULL;
  13907. while ( ( ent = gEntList.FindEntityByClassname( ent, "planted_c4" ) ) != NULL )
  13908. {
  13909. const Vector &pos = ent->WorldSpaceCenter();
  13910. DrawSphere( pos, flBombRadius, 255, 0, 0, 10 );
  13911. }
  13912. }
  13913. CON_COMMAND_F( map_setbombradius, "Sets the bomb radius for the map.", FCVAR_CHEAT )
  13914. {
  13915. if ( args.ArgC() != 2 )
  13916. return;
  13917. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  13918. return;
  13919. if ( !g_pMapInfo )
  13920. CBaseEntity::Create( "info_map_parameters", vec3_origin, vec3_angle );
  13921. if ( !g_pMapInfo )
  13922. return;
  13923. g_pMapInfo->m_flBombRadius = atof( args[1] );
  13924. map_showbombradius( args );
  13925. }
  13926. //-----------------------------------------------------------------------------
  13927. // Purpose: Called when a player joins the game after it's started yet can still spawn in
  13928. //-----------------------------------------------------------------------------
  13929. void CCSGameRules::SpawningLatePlayer( CCSPlayer* pLatePlayer )
  13930. {
  13931. //Reset the round kills number of enemies for the opposite team
  13932. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  13933. {
  13934. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  13935. if(pPlayer)
  13936. {
  13937. if(pPlayer->GetTeamNumber() == pLatePlayer->GetTeamNumber())
  13938. {
  13939. continue;
  13940. }
  13941. pPlayer->m_NumEnemiesAtRoundStart++;
  13942. }
  13943. }
  13944. }
  13945. //-----------------------------------------------------------------------------
  13946. // Purpose: Test for "pistol" round, defined as the default starting round
  13947. // when players cannot purchase anything primary weapons
  13948. //-----------------------------------------------------------------------------
  13949. bool CCSGameRules::IsPistolRound()
  13950. {
  13951. if ( !IsPlayingClassic() )
  13952. return false;
  13953. return ( ( m_iTotalRoundsPlayed == 0 ) ||
  13954. ( HasHalfTime() && ( m_iTotalRoundsPlayed > 0 ) && ( m_iTotalRoundsPlayed == ( mp_maxrounds.GetInt() / 2 ) ) ) )
  13955. && ( GetStartMoney() <= 800 );
  13956. }
  13957. void CCSGameRules::PlayerTookDamage(CCSPlayer* player, const CTakeDamageInfo &damageInfo)
  13958. {
  13959. CBaseEntity *pInflictor = damageInfo.GetInflictor();
  13960. CBaseEntity *pAttacker = damageInfo.GetAttacker();
  13961. CCSPlayer *pCSScorer = (CCSPlayer *)(GetDeathScorer( pAttacker, pInflictor ));
  13962. if ( player && pCSScorer )
  13963. {
  13964. if (player->GetTeamNumber() == TEAM_CT)
  13965. {
  13966. m_bNoCTsDamaged = false;
  13967. }
  13968. if (player->GetTeamNumber() == TEAM_TERRORIST)
  13969. {
  13970. m_bNoTerroristsDamaged = false;
  13971. }
  13972. // set the first blood if this is the first and the victim is on a different team then the player
  13973. if ( m_pFirstBlood == NULL && pCSScorer != player && pCSScorer->GetTeamNumber() != player->GetTeamNumber() )
  13974. {
  13975. m_pFirstBlood = pCSScorer;
  13976. m_firstBloodTime = gpGlobals->curtime - m_fRoundStartTime;
  13977. }
  13978. }
  13979. }
  13980. CCSMatch* CCSGameRules::GetMatch( void )
  13981. {
  13982. return &m_match;
  13983. }
  13984. void CCSGameRules::SendPlayerItemDropsToClient()
  13985. {
  13986. CReliableBroadcastRecipientFilter broadcastFilter;
  13987. CCSUsrMsg_SendPlayerItemDrops msg;
  13988. // first randomize the list before we send it
  13989. VectorShuffle( m_ItemsPtrDroppedDuringMatch );
  13990. msg.mutable_entity_updates()->Reserve( m_ItemsPtrDroppedDuringMatch.Count() );
  13991. for ( int i = 0; i < m_ItemsPtrDroppedDuringMatch.Count(); i++ )
  13992. {
  13993. *msg.add_entity_updates() = *m_ItemsPtrDroppedDuringMatch[i];
  13994. }
  13995. SendUserMessage( broadcastFilter, CS_UM_SendPlayerItemDrops, msg );
  13996. m_bPlayerItemsHaveBeenDisplayed = true;
  13997. }
  13998. void CCSGameRules::FreezePlayers( void )
  13999. {
  14000. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  14001. {
  14002. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  14003. if ( pPlayer )
  14004. {
  14005. pPlayer->AddFlag( FL_FROZEN );
  14006. }
  14007. }
  14008. }
  14009. void CCSGameRules::ClearGunGameData( void )
  14010. {
  14011. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  14012. {
  14013. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  14014. if ( pPlayer )
  14015. {
  14016. // Reset all players' progressive weapon index values
  14017. pPlayer->ResetTRBombModeData();
  14018. pPlayer->ClearGunGameProgressiveWeaponIndex();
  14019. pPlayer->ResetTRBombModeWeaponProgressFlag();
  14020. pPlayer->ResetTRBombModeKillPoints();
  14021. pPlayer->ClearTRModeHEGrenade();
  14022. pPlayer->ClearTRModeFlashbang();
  14023. pPlayer->ClearTRModeMolotov();
  14024. pPlayer->ClearTRModeIncendiary();
  14025. }
  14026. }
  14027. m_iMaxGunGameProgressiveWeaponIndex = 0;
  14028. }
  14029. void CCSGameRules::SwitchTeamsAtRoundReset( void )
  14030. {
  14031. m_bForceTeamChangeSilent = true;
  14032. m_bSwitchingTeamsAtRoundReset = true;
  14033. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  14034. {
  14035. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  14036. if ( pPlayer )
  14037. {
  14038. pPlayer->SetPendingTeamNum( pPlayer->GetTeamNumber() ) ;
  14039. if ( pPlayer->GetAssociatedTeamNumber() == TEAM_CT || pPlayer->GetAssociatedTeamNumber() == TEAM_TERRORIST )
  14040. {
  14041. pPlayer->SwitchTeamsAtRoundReset();
  14042. }
  14043. }
  14044. }
  14045. // Swap custom teamnames
  14046. }
  14047. void CCSGameRules::AddTeamAccount( int team, TeamCashAward::Type reason )
  14048. {
  14049. int amount = TeamCashAwardValue( reason );
  14050. AddTeamAccount( team, reason, amount );
  14051. }
  14052. void CCSGameRules::AddTeamAccount( int team, TeamCashAward::Type reason, int amount, const char* szAwardText )
  14053. {
  14054. if ( !TeamCashAwardsEnabled() )
  14055. {
  14056. DevMsg( "WARNING: Trying to award %d to team %d, but mp_teamcashawards is 0!\n", amount, team );
  14057. return;
  14058. }
  14059. if ( amount == 0 )
  14060. return;
  14061. if ( IsPlayingCoopGuardian() || IsPlayingCoopMission() )
  14062. {
  14063. int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST;
  14064. // in this mode, bots are on the other side and we handle their money ourselves
  14065. if ( team == nOtherTeam )
  14066. return;
  14067. }
  14068. // float flDisplayedBonus = 1.0f;
  14069. //
  14070. // if ( CSGameRules() )
  14071. // {
  14072. // // Modify with slow money multiplier rules
  14073. // amount = CSGameRules()->ModifyMoneyRewardByMultiplier( true, amount, &flDisplayedBonus );
  14074. // }
  14075. char customAwardText[256];
  14076. bool bUsingCustom = false;
  14077. const char* awardReasonToken = NULL;
  14078. switch ( reason )
  14079. {
  14080. case TeamCashAward::TERRORIST_WIN_BOMB:
  14081. awardReasonToken = "#Team_Cash_Award_T_Win_Bomb";
  14082. break;
  14083. case TeamCashAward::ELIMINATION_HOSTAGE_MAP_T:
  14084. case TeamCashAward::ELIMINATION_HOSTAGE_MAP_CT:
  14085. awardReasonToken = "#Team_Cash_Award_Elim_Hostage";
  14086. break;
  14087. case TeamCashAward::ELIMINATION_BOMB_MAP:
  14088. awardReasonToken = "#Team_Cash_Award_Elim_Bomb";
  14089. break;
  14090. case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE:
  14091. case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB:
  14092. awardReasonToken = "#Team_Cash_Award_Win_Time";
  14093. break;
  14094. case TeamCashAward::WIN_BY_DEFUSING_BOMB:
  14095. awardReasonToken = "#Team_Cash_Award_Win_Defuse_Bomb";
  14096. break;
  14097. case TeamCashAward::WIN_BY_HOSTAGE_RESCUE:
  14098. if ( mp_hostages_rescuetowin.GetInt() == 1 )
  14099. awardReasonToken = "#Team_Cash_Award_Win_Hostage_Rescue";
  14100. else
  14101. awardReasonToken = "#Team_Cash_Award_Win_Hostages_Rescue";
  14102. break;
  14103. case TeamCashAward::LOSER_BONUS:
  14104. if ( amount > 0 )
  14105. awardReasonToken = "#Team_Cash_Award_Loser_Bonus";
  14106. else
  14107. awardReasonToken = "#Team_Cash_Award_Loser_Bonus_Neg";
  14108. break;
  14109. case TeamCashAward::RESCUED_HOSTAGE:
  14110. awardReasonToken = "#Team_Cash_Award_Rescued_Hostage";
  14111. break;
  14112. case TeamCashAward::HOSTAGE_INTERACTION:
  14113. awardReasonToken = "#Team_Cash_Award_Hostage_Interaction";
  14114. break;
  14115. case TeamCashAward::HOSTAGE_ALIVE:
  14116. awardReasonToken = "#Team_Cash_Award_Hostage_Alive";
  14117. break;
  14118. case TeamCashAward::PLANTED_BOMB_BUT_DEFUSED:
  14119. awardReasonToken = "#Team_Cash_Award_Planted_Bomb_But_Defused";
  14120. break;
  14121. case TeamCashAward::SURVIVE_GUARDIAN_WAVE:
  14122. awardReasonToken = "#Team_Cash_Award_Survive_GuardianMode_Wave";
  14123. break;
  14124. case TeamCashAward::CUSTOM_AWARD:
  14125. {
  14126. if ( szAwardText && szAwardText[0] != 0 )
  14127. {
  14128. awardReasonToken = "#Team_Cash_Award_Custom";
  14129. Q_snprintf( customAwardText, sizeof( customAwardText ), "%s", szAwardText );
  14130. bUsingCustom = true;
  14131. }
  14132. else
  14133. {
  14134. awardReasonToken = "#Team_Cash_Award_Generic";
  14135. }
  14136. break;
  14137. }
  14138. default:
  14139. break;
  14140. }
  14141. bool bTeamHasClinchedVictory = false;
  14142. if ( IsPlayingClassic() && !IsPlayingTraining() )
  14143. {
  14144. int iNumWinsToClinch = ( mp_maxrounds.GetInt() / 2 ) + 1 + GetOvertimePlaying() * ( mp_overtime_maxrounds.GetInt() / 2 );
  14145. bTeamHasClinchedVictory = mp_match_can_clinch.GetBool() && ( m_match.GetCTScore() >= iNumWinsToClinch ) || ( m_match.GetTerroristScore() >= iNumWinsToClinch );
  14146. }
  14147. char strAmount[64];
  14148. Q_snprintf( strAmount, sizeof( strAmount ), "%s$%d", amount >= 0 ? "+" : "-", abs( amount ));
  14149. // Update all QMM records for the teams
  14150. bool bTeamsArePlayingSwitchedSides = AreTeamsPlayingSwitchedSides();
  14151. FOR_EACH_MAP_FAST( m_mapQueuedMatchmakingPlayersData, idxQMMEntry )
  14152. {
  14153. CQMMPlayerData_t &qmmData = * m_mapQueuedMatchmakingPlayersData.Element( idxQMMEntry );
  14154. int iRulesTeam = bTeamsArePlayingSwitchedSides ? ( (qmmData.m_iDraftIndex < 5)?TEAM_TERRORIST:TEAM_CT ) : ( (qmmData.m_iDraftIndex < 5)?TEAM_CT:TEAM_TERRORIST );
  14155. if ( ( iRulesTeam == team ) && !qmmData.m_bReceiveNoMoneyNextRound )
  14156. { // we set all records even though alive players will stomp them when receiving money in the loop right below
  14157. qmmData.m_cash = clamp( (int)( qmmData.m_cash + amount ), 0, GetMaxMoney() );
  14158. }
  14159. }
  14160. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  14161. {
  14162. CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
  14163. if ( !pPlayer || pPlayer->GetTeamNumber() != team )
  14164. continue;
  14165. // hand out team cash awards from previous round
  14166. if ( pPlayer->DoesPlayerGetRoundStartMoney() )
  14167. {
  14168. pPlayer->AddAccountFromTeam( amount, true, reason );
  14169. if ( dev_reportmoneychanges.GetBool() )
  14170. Msg( "%s %d (total: %d) AddAccountFromTeam: REASON: %s\n", pPlayer->GetPlayerName(), amount, pPlayer->GetAccountBalance(), awardReasonToken );
  14171. if ( bUsingCustom )
  14172. {
  14173. ClientPrint( pPlayer, HUD_PRINTTALK, awardReasonToken, strAmount, customAwardText );
  14174. }
  14175. else if ( !IsLastRoundBeforeHalfTime() && ( m_match.GetPhase() != GAMEPHASE_HALFTIME ) &&
  14176. ( m_match.GetRoundsPlayed() != mp_maxrounds.GetInt() + GetOvertimePlaying() * mp_overtime_maxrounds.GetInt() ) && !bTeamHasClinchedVictory )
  14177. {
  14178. ClientPrint( pPlayer, HUD_PRINTTALK, awardReasonToken, strAmount );
  14179. }
  14180. }
  14181. else
  14182. {
  14183. if ( !IsLastRoundBeforeHalfTime() && ( m_match.GetPhase() != GAMEPHASE_HALFTIME ) &&
  14184. ( m_match.GetRoundsPlayed() != mp_maxrounds.GetInt() + GetOvertimePlaying() * mp_overtime_maxrounds.GetInt() ) && !bTeamHasClinchedVictory )
  14185. {
  14186. // TODO: This code assumes on there only being 2 possible reasons for DoesPlayerGetRoundStartMoney returning false: Suicide or Running down the clock as T.
  14187. // This code should not make that assumption and the awardReasonToken should probably be plumbed to express those properly.
  14188. if ( pPlayer->GetHealth() > 0 )
  14189. {
  14190. ClientPrint( pPlayer, HUD_PRINTTALK, "#Team_Cash_Award_No_Income", strAmount );
  14191. }
  14192. else
  14193. {
  14194. ClientPrint( pPlayer, HUD_PRINTTALK, "#Team_Cash_Award_No_Income_Suicide", strAmount );
  14195. }
  14196. }
  14197. }
  14198. }
  14199. }
  14200. #endif // !CLIENT_DLL
  14201. bool CCSGameRules::IsCSGOBirthday( void )
  14202. {
  14203. return sv_party_mode.GetBool();
  14204. }
  14205. int CCSGameRules::GetStartMoney( void )
  14206. {
  14207. return IsWarmupPeriod() ? GetMaxMoney() : ( GetOvertimePlaying() ? mp_overtime_startmoney.GetInt() : mp_startmoney.GetInt() );
  14208. }
  14209. int CCSGameRules::GetMaxMoney( void )
  14210. {
  14211. return mp_maxmoney.GetInt();
  14212. }
  14213. int CCSGameRules::GetBetweenRoundMoney( void )
  14214. {
  14215. return mp_afterroundmoney.GetInt();
  14216. }
  14217. bool CCSGameRules::PlayerCashAwardsEnabled( void )
  14218. {
  14219. return mp_playercashawards.GetBool();
  14220. }
  14221. bool CCSGameRules::TeamCashAwardsEnabled( void )
  14222. {
  14223. return mp_teamcashawards.GetBool();
  14224. }
  14225. bool CCSGameRules::IsPlayingGunGameProgressive( void ) const
  14226. {
  14227. return ( IsPlayingGunGame() &&
  14228. g_pGameTypes->GetCurrentGameMode() == CS_GameMode::GunGame_Progressive );
  14229. }
  14230. bool CCSGameRules::IsPlayingGunGameDeathmatch( void ) const
  14231. {
  14232. return ( IsPlayingGunGame() &&
  14233. g_pGameTypes->GetCurrentGameMode() == CS_GameMode::GunGame_Deathmatch );
  14234. }
  14235. bool CCSGameRules::IsPlayingGunGameTRBomb( void ) const
  14236. {
  14237. return ( IsPlayingGunGame() &&
  14238. g_pGameTypes->GetCurrentGameMode() == CS_GameMode::GunGame_Bomb );
  14239. }
  14240. bool CCSGameRules::IsPlayingClassicCasual( void ) const
  14241. {
  14242. return ( IsPlayingClassic() &&
  14243. g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Classic_Casual);
  14244. }
  14245. bool CCSGameRules::IsPlayingAnyCompetitiveStrictRuleset( void ) const
  14246. {
  14247. return ( IsPlayingClassic() && ( (g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Classic_Competitive) ) );
  14248. }
  14249. bool CCSGameRules::IsPlayingCoopGuardian( void ) const
  14250. {
  14251. return ( IsPlayingCooperativeGametype() &&
  14252. g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Cooperative_Guardian );
  14253. }
  14254. bool CCSGameRules::IsPlayingCoopMission( void ) const
  14255. {
  14256. return ( IsPlayingCooperativeGametype() &&
  14257. g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Cooperative_Mission );
  14258. }
  14259. bool CCSGameRules::ShouldRecordMatchStats( void ) const
  14260. {
  14261. return ( IsPlayingCoopMission() || (IsPlayingClassic() &&
  14262. ( g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Classic_Competitive )) &&
  14263. ( !IsWarmupPeriod() ) &&
  14264. ( m_nOvertimePlaying == 0 ) &&
  14265. ( GetTotalRoundsPlayed() < MAX_MATCH_STATS_ROUNDS ) );
  14266. }
  14267. bool CCSGameRules::IsQueuedMatchmaking() const
  14268. {
  14269. #ifndef CLIENT_DLL
  14270. static ConVarRef sv_mmqueue_reservation( "sv_mmqueue_reservation" );
  14271. return sv_mmqueue_reservation.GetString()[0] == 'Q';
  14272. #else
  14273. return m_bIsQueuedMatchmaking.Get();
  14274. #endif
  14275. }
  14276. bool CCSGameRules::IsValveDS() const
  14277. {
  14278. #ifndef CLIENT_DLL
  14279. return false;
  14280. #else
  14281. return m_bIsValveDS.Get();
  14282. #endif
  14283. }
  14284. bool CCSGameRules::IsQuestEligible() const
  14285. {
  14286. #ifndef CLIENT_DLL
  14287. return false;
  14288. #else
  14289. return m_bIsQuestEligible.Get();
  14290. #endif
  14291. }
  14292. bool CCSGameRules::IsPlayingOffline( void ) const
  14293. {
  14294. #ifndef CLIENT_DLL
  14295. if ( engine->IsDedicatedServer() )
  14296. return false;
  14297. #endif
  14298. extern ConVar game_online;
  14299. return !game_online.GetBool();
  14300. }
  14301. bool CCSGameRules::IsAwardsProgressAllowedForBotDifficulty( void ) const
  14302. {
  14303. return ( !IsPlayingOffline() || ( GetCustomBotDifficulty() >= CUSTOM_BOT_MIN_DIFFICULTY_FOR_AWARDS_PROGRESS ) );
  14304. }
  14305. bool CCSGameRules::IsPlayingCustomGametype( void ) const
  14306. {
  14307. return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Custom );
  14308. }
  14309. bool CCSGameRules::IsPlayingTraining( void ) const
  14310. {
  14311. return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Training );
  14312. }
  14313. bool CCSGameRules::IsPlayingClassic( void ) const
  14314. {
  14315. return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Classic );
  14316. }
  14317. bool CCSGameRules::IsPlayingGunGame( void ) const
  14318. {
  14319. return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_GunGame );
  14320. }
  14321. bool CCSGameRules::IsPlayingCooperativeGametype( void ) const
  14322. {
  14323. return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Cooperative );
  14324. }
  14325. int CCSGameRules::GetCustomBotDifficulty( void ) const
  14326. {
  14327. return ( g_pGameTypes->GetCustomBotDifficulty() );
  14328. }
  14329. bool CCSGameRules::ForceSplitScreenPlayersOnToSameTeam( void )
  14330. {
  14331. extern ConVar game_online;
  14332. return game_online.GetBool();
  14333. }
  14334. ConVar mp_solid_teammates("mp_solid_teammates", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether teammates are solid or not." );
  14335. ConVar mp_free_armor("mp_free_armor", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether armor and helmet are given automatically." );
  14336. ConVar mp_halftime("mp_halftime", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether the match switches sides in a halftime event.");
  14337. ConVar mp_randomspawn("mp_randomspawn", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether players are to spawn. 0 = default; 1 = both teams; 2 = Terrorists; 3 = CTs." );
  14338. ConVar mp_randomspawn_los("mp_randomspawn_los", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "If using mp_randomspawn, determines whether to test Line of Sight when spawning." );
  14339. ConVar mp_randomspawn_dist( "mp_randomspawn_dist", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If using mp_randomspawn, determines whether to test distance when selecting this spot." );
  14340. // Returns true if teammates are solid obstacles in the current game mode
  14341. bool CCSGameRules::IsTeammateSolid( void ) const
  14342. {
  14343. return mp_solid_teammates.GetBool();
  14344. }
  14345. // Returns true if armor is given automatically.
  14346. bool CCSGameRules::IsArmorFree( void ) const
  14347. {
  14348. return mp_free_armor.GetBool();
  14349. }
  14350. // Returns true if the game is to be split into two halves.
  14351. bool CCSGameRules::HasHalfTime( void ) const
  14352. {
  14353. return mp_halftime.GetBool();
  14354. }
  14355. int CCSGameRules::GetWeaponScoreForDeathmatch( int nPos )
  14356. {
  14357. int nScore = 1;
  14358. CSWeaponID wepID = WEAPON_NONE;
  14359. for ( int i = 0; i < GetItemSchema()->GetItemDefinitionCount(); i++ )
  14360. {
  14361. CCStrike15ItemDefinition *pItemDef = ( CCStrike15ItemDefinition * )GetItemSchema()->GetItemDefinitionByMapIndex( i );
  14362. if ( pItemDef->GetDefaultLoadoutSlot() == nPos )
  14363. {
  14364. wepID = WeaponIdFromString( pItemDef->GetItemClass() );
  14365. break;
  14366. }
  14367. }
  14368. if ( wepID == WEAPON_NONE )
  14369. return 0;
  14370. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( wepID );
  14371. if ( pWeaponInfo )
  14372. {
  14373. // float flScore1 = ((pWeaponInfo->m_flCycleTime / pWeaponInfo->m_iDamage)-0.001f) * 10;
  14374. // float flScore2 = ((pWeaponInfo->m_iKillAward / MAX( 500, pWeaponInfo->m_iWeaponPrice )) + flScore1) * 100;
  14375. // int nScore = MAX( 1, ceil(flScore2) );
  14376. // pPlayer->AddContributionScore( nScore );
  14377. if ( wepID == WEAPON_KNIFE )
  14378. {
  14379. nScore = 20;
  14380. }
  14381. else
  14382. {
  14383. int nPrice = MIN( 4500, MAX(100, pWeaponInfo->GetWeaponPrice() - (pWeaponInfo->GetKillAward()/2)) );
  14384. float flScore1 = MAX( 0.05f, ((1 - ((float)nPrice/4500.0f)) * 10)/5);
  14385. float flScore2 = flScore1 + MIN( 2.0f, (((pWeaponInfo->GetCycleTime() / pWeaponInfo->GetDamage() ) *10 ) + ( ( 2.0f-pWeaponInfo->GetArmorRatio() ) /2 ) ) / 4 );
  14386. float flFinal = ceil(flScore2-0.5f);//ceil((flScore2/6) * 10 );
  14387. nScore = MAX( 0, flFinal ) + 10;
  14388. }
  14389. }
  14390. return nScore;
  14391. }
  14392. float CCSGameRules::GetRestartRoundTime( void ) const
  14393. {
  14394. return m_fRoundStartTime;
  14395. }
  14396. #if !defined( CLIENT_DLL )
  14397. CGameCoopMissionManager *CCSGameRules::GetCoopMissionManager( void )
  14398. {
  14399. CGameCoopMissionManager *pManager = static_cast< CGameCoopMissionManager* >( m_coopMissionManager.Get() );
  14400. if ( !pManager )
  14401. {
  14402. CBaseEntity *ent = NULL;
  14403. do
  14404. {
  14405. ent = gEntList.FindEntityByClassname( ent, "game_coopmission_manager" );
  14406. if ( ent )
  14407. {
  14408. CSGameRules()->SetCoopMissionManager( ent );
  14409. pManager = GetCoopMissionManager();
  14410. }
  14411. } while ( ent );
  14412. }
  14413. return pManager;
  14414. }
  14415. void CCSGameRules::CoopSetBotQuotaAndRefreshSpawns( int nMaxEnemiesToSpawn )
  14416. {
  14417. RefreshCurrentSpawnPointLists();
  14418. m_iMaxNumTerrorists = nMaxEnemiesToSpawn;
  14419. ConVarRef bot_quota( "bot_quota" );
  14420. int nQuota = UTIL_HumansInGame( true, true ) + MIN( m_iMaxNumTerrorists, m_iSpawnPointCount_Terrorist );
  14421. bot_quota.SetValue( nQuota );
  14422. ShuffleSpawnPointLists();
  14423. SortSpawnPointLists();
  14424. }
  14425. void CCSGameRules::CoopMissionSpawnFirstEnemies( int nMaxEnemiesToSpawn )
  14426. {
  14427. // set to 0 so we start in wave 1
  14428. m_nGuardianModeWaveNumber = 0;
  14429. CoopMissionSpawnNextWave( nMaxEnemiesToSpawn );
  14430. }
  14431. void CCSGameRules::CoopMissionSetNextRespawnIn( float flSeconds, bool bIncrementWaveNumber )
  14432. {
  14433. m_bDontIncrementCoopWave = !bIncrementWaveNumber;
  14434. // if T's are supposed to respawn
  14435. float flNextRespawn = gpGlobals->curtime + flSeconds;
  14436. m_flNextRespawnWave.Set( TEAM_TERRORIST, flNextRespawn );
  14437. }
  14438. void CCSGameRules::CoopMissionSpawnNextWave( int nMaxEnemiesToSpawn )
  14439. {
  14440. // the CT spawn points for respawn are enabled, respawn now - we can work on the appropriate timings later
  14441. int nTeam = TEAM_CT;
  14442. // give health to all of the players on the other team
  14443. for ( int j = 1; j <= MAX_PLAYERS; j++ )
  14444. {
  14445. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) );
  14446. if ( pPlayer && pPlayer->GetTeamNumber() == nTeam )
  14447. {
  14448. // respawn any dead CTs as well
  14449. if ( pPlayer->IsAlive() == false )
  14450. {
  14451. pPlayer->State_Transition( STATE_GUNGAME_RESPAWN );
  14452. }
  14453. }
  14454. }
  14455. // make sure this is 0 so we start from the beginning
  14456. m_iNextTerroristSpawnPoint = 0;
  14457. // do a refresh here, but we don't refresh as frequently in coop mission
  14458. // we would normally refresh every time a spot enabled or disabled and a few other places
  14459. // just do it here once before we spawn new guys
  14460. RefreshCurrentSpawnPointLists();
  14461. m_iMaxNumTerrorists = nMaxEnemiesToSpawn;
  14462. ShuffleSpawnPointLists();
  14463. SortSpawnPointLists();
  14464. // make sure that there are always enough bots
  14465. ConVarRef bot_quota( "bot_quota" );
  14466. int nQuota = UTIL_HumansInGame( true, true ) + MIN( m_iMaxNumTerrorists, m_iSpawnPointCount_Terrorist );
  14467. bot_quota.SetValue( nQuota );
  14468. //DevMsg( ">> Setting bot_quota to %d\n", nQuota );
  14469. // if T's are supposed to respawn
  14470. CoopMissionSetNextRespawnIn( -1, true );
  14471. }
  14472. void CCSGameRules::CoopMissionRespawnDeadPlayers( void )
  14473. {
  14474. // do a refresh here, but we don't refresh as frequently in coop mission
  14475. // we would normally refresh every time a spot enabled or disabled and a few other places
  14476. // just do it here once before we spawn new guys
  14477. if ( !IsWarmupPeriod() )
  14478. RefreshCurrentSpawnPointLists();
  14479. // the CT spawn points for respawn are enabled, respawn now - we can work on the appropriate timings later
  14480. CTeam *pTeam = GetGlobalTeam( TEAM_CT );
  14481. CUtlVector < CCSPlayer* > vecPlayers;
  14482. if ( pTeam->GetHumanMembers( &vecPlayers ) )
  14483. {
  14484. FOR_EACH_VEC( vecPlayers, i )
  14485. {
  14486. // respawn any dead CTs as well
  14487. if ( vecPlayers[i]->IsAlive() == false )
  14488. {
  14489. vecPlayers[i]->State_Transition( STATE_GUNGAME_RESPAWN );
  14490. }
  14491. }
  14492. }
  14493. }
  14494. void CCSGameRules::CoopCollectBonusCoin( void )
  14495. {
  14496. m_coopBonusCoinsFound++;
  14497. CRecipientFilter filter;
  14498. filter.MakeReliable();
  14499. filter.AddRecipientsByTeam( GetGlobalTeam( TEAM_CT ) );
  14500. char szCoins[32];
  14501. Q_snprintf( szCoins, sizeof( szCoins ), "%d", ( int )m_coopBonusCoinsFound );
  14502. UTIL_ClientPrintFilter( filter, HUD_PRINTCENTER, "#SFUIHUD_InfoPanel_Coop_CollectCoin", szCoins );
  14503. }
  14504. #endif
  14505. #ifdef CLIENT_DLL
  14506. CCSGameRules::CCSGameRules()
  14507. {
  14508. // Reset borrow music convar because player indices scramble after level transition.
  14509. cl_borrow_music_from_player_index.SetValue( cl_borrow_music_from_player_index.GetDefault() );
  14510. InitializeGameTypeAndMode();
  14511. // Set the bestof maps
  14512. m_numBestOfMaps = 0;
  14513. // Set global gifts state
  14514. m_numGlobalGiftsGiven = 0;
  14515. m_numGlobalGifters = 0;
  14516. m_numGlobalGiftsPeriodSeconds = 0;
  14517. for ( int j = 0; j < MAX_GIFT_GIVERS_FEATURED_COUNT; ++ j )
  14518. {
  14519. m_arrFeaturedGiftersAccounts.Set( j, 0 );
  14520. m_arrFeaturedGiftersGifts.Set( j, 0 );
  14521. }
  14522. for ( int j = 0; j < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; ++ j )
  14523. {
  14524. m_arrTournamentActiveCasterAccounts.Set( j, 0 );
  14525. }
  14526. for ( int j = 0; j < MAX_MATCH_STATS_ROUNDS; ++j )
  14527. {
  14528. m_iMatchStats_PlayersAlive_T.GetForModify( j ) = 0x3F;
  14529. m_iMatchStats_PlayersAlive_CT.GetForModify( j ) = 0x3F;
  14530. }
  14531. ResetCasterConvars();
  14532. m_szTournamentEventName.GetForModify()[0] = 0;
  14533. m_szTournamentEventStage.GetForModify()[0] = 0;
  14534. m_szTournamentPredictionsTxt.GetForModify()[0] = 0;
  14535. m_szMatchStatTxt.GetForModify()[ 0 ] = 0;
  14536. m_nTournamentPredictionsPct = 0;
  14537. HOOK_MESSAGE( SendPlayerItemDrops );
  14538. HOOK_MESSAGE( SendPlayerItemFound );
  14539. m_bMarkClientStopRecordAtRoundEnd = false;
  14540. }
  14541. CCSGameRules::~CCSGameRules()
  14542. {
  14543. ResetCasterConvars();
  14544. for ( int j = 0; j < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; ++ j )
  14545. {
  14546. m_arrTournamentActiveCasterAccounts.Set( j, 0 );
  14547. }
  14548. }
  14549. // CLIENT
  14550. bool __MsgFunc_SendPlayerItemDrops( const CCSUsrMsg_SendPlayerItemDrops &msg )
  14551. {
  14552. //IViewPortPanel* panel = GetViewPortInterface()->FindPanelByName( PANEL_SCOREBOARD );
  14553. if ( CSGameRules() )
  14554. {
  14555. CSGameRules()->ClearItemsDroppedDuringMatch();
  14556. for ( int i = 0; i < msg.entity_updates_size(); i ++ )
  14557. {
  14558. const CEconItemPreviewDataBlock &update = msg.entity_updates(i);
  14559. CSGameRules()->RecordPlayerItemDrop( update );
  14560. }
  14561. }
  14562. return true;
  14563. }
  14564. void CCSGameRules::MarkClientStopRecordAtRoundEnd( bool bStop )
  14565. {
  14566. m_bMarkClientStopRecordAtRoundEnd = bStop;
  14567. }
  14568. void CCSGameRules::OpenBuyMenu( int nPlayerID )
  14569. {
  14570. // skip buy menu during demo playback
  14571. if ( engine->IsPlayingDemo() || engine->IsPlayingTimeDemo() )
  14572. return;
  14573. GetViewPortInterface()->ShowPanel( PANEL_BUY, true );
  14574. IGameEvent *pEvent = gameeventmanager->CreateEvent( "buymenu_open" );
  14575. if ( pEvent )
  14576. {
  14577. pEvent->SetInt("userid", nPlayerID );
  14578. gameeventmanager->FireEventClientSide( pEvent );
  14579. }
  14580. }
  14581. void CCSGameRules::CloseBuyMenu( int nPlayerID )
  14582. {
  14583. // skip buy menu during demo playback
  14584. if ( engine->IsPlayingDemo() || engine->IsPlayingTimeDemo() )
  14585. return;
  14586. GetViewPortInterface()->ShowPanel( PANEL_BUY, false );
  14587. IGameEvent *pEvent = gameeventmanager->CreateEvent( "buymenu_close" );
  14588. if ( pEvent )
  14589. {
  14590. pEvent->SetInt("userid", nPlayerID );
  14591. gameeventmanager->FireEventClientSide( pEvent );
  14592. }
  14593. // update the inventory
  14594. CCSPlayer *pPlayer = static_cast<CCSPlayer*>( UTIL_PlayerByIndex(engine->GetPlayerForUserID( nPlayerID )) );
  14595. C_WeaponCSBase *pWeapon = (pPlayer && pPlayer->IsLocalPlayer()) ? pPlayer->GetActiveCSWeapon() : NULL ;
  14596. if ( pWeapon )
  14597. {
  14598. CBaseHudWeaponSelection *pHudSelection = GetHudWeaponSelection();
  14599. if ( pHudSelection )
  14600. {
  14601. pHudSelection->OnWeaponSwitch( pWeapon );
  14602. }
  14603. }
  14604. }
  14605. const wchar_t* CCSGameRules::GetFriendlyMapName( const char* szShortName )
  14606. {
  14607. // Look up the nice version of the name.
  14608. char szToken[MAX_MAP_NAME+10+1]; // includes prefix size
  14609. szToken[0] = '\0';
  14610. V_snprintf( szToken, ARRAYSIZE( szToken ), "#SFUI_Map_%s", szShortName );
  14611. wchar_t* szTranslated = g_pVGuiLocalize->Find( szToken );
  14612. if ( szTranslated )
  14613. return szTranslated;
  14614. char szPath[MAX_PATH];
  14615. V_strcpy_safe( szPath, szShortName );
  14616. V_FixSlashes( szPath, '/' ); // internal path strings use forward slashes, make sure we compare like that.
  14617. if ( V_strstr( szPath, "workshop/" ) )
  14618. {
  14619. PublishedFileId_t ullId = GetMapIDFromMapPath( szPath );
  14620. const PublishedFileInfo_t *pInfo = WorkshopManager().GetPublishedFileInfoByID( ullId );
  14621. static wchar_t wszMapName[128];
  14622. if ( pInfo )
  14623. {
  14624. g_pVGuiLocalize->ConvertANSIToUnicode(pInfo->m_rgchTitle, wszMapName, sizeof(wszMapName));
  14625. return wszMapName;
  14626. }
  14627. else
  14628. {
  14629. g_pVGuiLocalize->ConvertANSIToUnicode( V_GetFileName( szShortName ), wszMapName, sizeof(wszMapName));
  14630. return wszMapName;
  14631. }
  14632. }
  14633. static wchar_t wszMapName[128];
  14634. g_pVGuiLocalize->ConvertANSIToUnicode(szShortName, wszMapName, sizeof(wszMapName));
  14635. return wszMapName;
  14636. }
  14637. bool CCSGameRules::GetFriendlyMapNameToken( const char* szShortName, char* szOutBuffer, int nBuffSize )
  14638. {
  14639. // Create the nice version of the name.
  14640. CreateFriendlyMapNameToken( szShortName, szOutBuffer, nBuffSize );
  14641. if ( g_pVGuiLocalize->Find( szOutBuffer ) )
  14642. return true;
  14643. return false;
  14644. }
  14645. char const * CCSGameRules::GetTournamentEventName() const
  14646. {
  14647. const char *sz = m_szTournamentEventName.Get();
  14648. if ( sz && *sz && ( sz[0] != '#' ) )
  14649. {
  14650. // Localize it:
  14651. static char const * const arrEvents[] = { ""
  14652. ,"The 2013 DreamHack SteelSeries CS:GO Championship"
  14653. ,"The Valve DPR Championship"
  14654. ,"The 2014 EMS One Katowice CS:GO Championship"
  14655. ,"ESL One Cologne 2014 CS:GO Championship"
  14656. ,"The 2014 DreamHack CS:GO Championship"
  14657. ,"ESL One Katowice 2015 CS:GO Championship"
  14658. ,"ESL One Cologne 2015 CS:GO Championship"
  14659. ,"DreamHack Cluj-Napoca 2015 CS:GO Championship"
  14660. ,"MLG Columbus 2016 CS:GO Championship"
  14661. ,"ESL One Cologne 2016 CS:GO Championship"
  14662. ,"ELEAGUE Atlanta 2017 CS:GO Championship"
  14663. };
  14664. for ( int k = Q_ARRAYSIZE( arrEvents ) - 1; k > 0; -- k )
  14665. {
  14666. if ( !V_strcmp( sz, arrEvents[k] ) )
  14667. {
  14668. V_snprintf( const_cast< char * >( sz ), MAX_PATH, "#CSGO_Tournament_Event_Name_%d", k );
  14669. return sz;
  14670. }
  14671. }
  14672. }
  14673. return m_szTournamentEventName;
  14674. }
  14675. char const * CCSGameRules::GetTournamentEventStage() const
  14676. {
  14677. const char *sz = m_szTournamentEventStage.Get();
  14678. if ( sz && *sz && ( sz[ 0 ] != '#' ) )
  14679. {
  14680. // Localize it:
  14681. static char const * const arrEvents[] = { ""
  14682. ,"Exhibition"
  14683. ,"Group Stage | First Stage"
  14684. ,"BYOC"
  14685. ,"Valve Pre-Event Test"
  14686. ,"Match 1 of 3 | Quarterfinal"
  14687. ,"Match 2 of 3 | Quarterfinal"
  14688. ,"Match 3 of 3 | Quarterfinal"
  14689. ,"Match 1 of 3 | Semifinal"
  14690. ,"Match 2 of 3 | Semifinal"
  14691. ,"Match 3 of 3 | Semifinal"
  14692. ,"Match 1 of 3 | Grand Final"
  14693. ,"Match 2 of 3 | Grand Final"
  14694. ,"Match 3 of 3 | Grand Final"
  14695. ,"All-Star"
  14696. ,"Group Stage | Winners Match"
  14697. ,"Group Stage | Elimination Match"
  14698. ,"Group Stage | Decider Match"
  14699. ,"Qualification"
  14700. ,"Match 2 of 3 | Qualification"
  14701. ,"Match 3 of 3 | Qualification"
  14702. ,"Group Stage | Decider Match 1 of 3"
  14703. ,"Group Stage | Decider Match 2 of 3"
  14704. ,"Group Stage | Decider Match 3 of 3"
  14705. ,"Group Stage | Second Stage (Upper Pool)"
  14706. ,"Group Stage | Second Stage (Lower Pool)"
  14707. ,"Group Stage | Third Stage"
  14708. };
  14709. for ( int k = Q_ARRAYSIZE( arrEvents ) - 1; k > 0; --k )
  14710. {
  14711. if ( !V_strcmp( sz, arrEvents[ k ] ) )
  14712. {
  14713. V_snprintf( const_cast< char * >( sz ), MAX_PATH, "#CSGO_Tournament_Event_Stage_Display_%d", k );
  14714. return sz;
  14715. }
  14716. }
  14717. }
  14718. return m_szTournamentEventStage;
  14719. }
  14720. //
  14721. // all of this is here to pick the first caster in the active caster list by default
  14722. // later when we have a "pick caster" dialog then this can go away
  14723. //
  14724. extern ConVar spec_autodirector_cameraman;
  14725. extern ConVar spec_cameraman_ui;
  14726. extern ConVar spec_cameraman_xray;
  14727. void CCSGameRules::RecvProxy_TournamentActiveCasterAccounts( const CRecvProxyData *pData, void *pStruct, void *pOut )
  14728. {
  14729. RecvProxy_Int32ToInt32( pData, pStruct, pOut );
  14730. // set convars here
  14731. if ( spec_autodirector_cameraman.GetInt() == -1 && CSGameRules() && CSGameRules()->m_arrTournamentActiveCasterAccounts[ 0 ] != 0 )
  14732. {
  14733. int casterID = ( int ) ( CSGameRules()->m_arrTournamentActiveCasterAccounts[ 0 ] );
  14734. spec_autodirector_cameraman.SetValue( casterID );
  14735. spec_cameraman_ui.SetValue( casterID );
  14736. spec_cameraman_xray.SetValue( casterID );
  14737. engine->SetVoiceCasterID( CSGameRules()->m_arrTournamentActiveCasterAccounts[ 0 ] );
  14738. }
  14739. }
  14740. void CCSGameRules::ResetCasterConvars()
  14741. {
  14742. spec_autodirector_cameraman.SetValue( -1 );
  14743. spec_cameraman_ui.SetValue( 0 );
  14744. spec_cameraman_xray.SetValue( 0 );
  14745. engine->SetVoiceCasterID( 0 );
  14746. }
  14747. #endif
  14748. CEconQuestDefinition* CCSGameRules::GetActiveAssassinationQuest( void ) const
  14749. {
  14750. if ( IsPlayingCooperativeGametype() )
  14751. return NULL;
  14752. // Skip the map lookup for invlaid quest id
  14753. if ( !m_iActiveAssassinationTargetMissionID )
  14754. return NULL;
  14755. else
  14756. return GetItemSchema()->GetQuestDefinition( m_iActiveAssassinationTargetMissionID );
  14757. }
  14758. float CCSGameRules::GetCMMItemDropRevealDuration()
  14759. {
  14760. if ( m_flCMMItemDropRevealEndTime == 0 && m_ItemsPtrDroppedDuringMatch.Count() <= 0 )
  14761. return 2;
  14762. if ( m_flCMMItemDropRevealEndTime == 0 && m_flCMMItemDropRevealStartTime != 0 )
  14763. {
  14764. float flItemDropTime = 2.0f;
  14765. for ( int i = 0; i < m_ItemsPtrDroppedDuringMatch.Count(); i++ )
  14766. {
  14767. switch ( m_ItemsPtrDroppedDuringMatch[i]->rarity() )
  14768. {
  14769. case 6:
  14770. case 5:
  14771. {
  14772. flItemDropTime += sv_endmatch_item_drop_interval_ancient.GetFloat();
  14773. break;
  14774. }
  14775. case 4:
  14776. {
  14777. flItemDropTime += sv_endmatch_item_drop_interval_legendary.GetFloat();
  14778. break;
  14779. }
  14780. case 3:
  14781. {
  14782. flItemDropTime += sv_endmatch_item_drop_interval_mythical.GetFloat();
  14783. break;
  14784. }
  14785. case 2:
  14786. {
  14787. flItemDropTime += sv_endmatch_item_drop_interval_rare.GetFloat();
  14788. break;
  14789. }
  14790. default:
  14791. {
  14792. flItemDropTime += sv_endmatch_item_drop_interval.GetFloat();
  14793. break;
  14794. }
  14795. }
  14796. }
  14797. m_flCMMItemDropRevealEndTime = (m_flCMMItemDropRevealStartTime + flItemDropTime);
  14798. }
  14799. return MAX( 2, (m_flCMMItemDropRevealEndTime - m_flCMMItemDropRevealStartTime) );
  14800. }
  14801. void CCSGameRules::CreateFriendlyMapNameToken( const char* szShortName, char* szOutBuffer, int nBuffSize )
  14802. {
  14803. // must be able to fit the token below
  14804. Assert( nBuffSize >= MAX_MAP_NAME+10+1 );
  14805. // Create the nice version of the name.
  14806. // right now we blindly create the token because we can assume (on consoles) that we don't have any maps
  14807. // that we don't know about. For PC, we'll need to fix this so all checks will happen on the client
  14808. V_snprintf( szOutBuffer, nBuffSize, "#SFUI_Map_%s", szShortName );
  14809. }
  14810. const char *CCSGameRules::GetDefaultTeamName( int nTeam )
  14811. {
  14812. Assert( nTeam >= 0 && nTeam < ARRAYSIZE( sTeamNames ) );
  14813. return sTeamNames[nTeam];
  14814. }
  14815. void CCSGameRules::AddGunGameWeapon( const char* pWeaponName, int nNumKillsToUpgrade, int nTeamID )
  14816. {
  14817. if ( !pWeaponName )
  14818. {
  14819. return;
  14820. }
  14821. Assert( nNumKillsToUpgrade > 0 );
  14822. if ( nNumKillsToUpgrade <= 0 )
  14823. {
  14824. Warning( "CCSGameRules: Invalid number of kills-to-upgrade (%d) for weapon %s.\n", nNumKillsToUpgrade, pWeaponName );
  14825. nNumKillsToUpgrade = 1;
  14826. }
  14827. char weaponName[MAX_WEAPON_STRING];
  14828. weaponName[0] = '\0';
  14829. V_snprintf( weaponName, sizeof( weaponName ), "weapon_%s", pWeaponName );
  14830. int nWeaponID = 0;
  14831. // Try to get the ID from the item name
  14832. const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( weaponName );
  14833. if ( pDef )
  14834. {
  14835. nWeaponID = pDef->GetDefinitionIndex();
  14836. }
  14837. if ( nWeaponID == 0 )
  14838. {
  14839. // Fall back to the older weapon id system for things like knifegg
  14840. nWeaponID = WeaponIdFromString( weaponName );
  14841. }
  14842. if ( nWeaponID != WEAPON_NONE )
  14843. {
  14844. if ( nTeamID == TEAM_CT )
  14845. {
  14846. m_GGProgressiveWeaponOrderCT.Set( m_iNumGunGameProgressiveWeaponsCT, nWeaponID );
  14847. m_GGProgressiveWeaponKillUpgradeOrderCT.Set( m_iNumGunGameProgressiveWeaponsCT, nNumKillsToUpgrade );
  14848. m_iNumGunGameProgressiveWeaponsCT++;
  14849. }
  14850. else if ( nTeamID == TEAM_TERRORIST )
  14851. {
  14852. m_GGProgressiveWeaponOrderT.Set( m_iNumGunGameProgressiveWeaponsT, nWeaponID );
  14853. m_GGProgressiveWeaponKillUpgradeOrderT.Set( m_iNumGunGameProgressiveWeaponsT, nNumKillsToUpgrade );
  14854. m_iNumGunGameProgressiveWeaponsT++;
  14855. }
  14856. #if defined ( GAME_DLL )
  14857. // PERF: Force creation of instance baselines so we don't take the hit upon creation.
  14858. // We're doing this because we get a huge network spike upon player death which causes hitches on PS3.
  14859. // Pre-creating baselines offloads a bit of that spike.
  14860. UTIL_EnsureInstanceBaseline( weaponName );
  14861. #endif
  14862. }
  14863. else
  14864. {
  14865. Warning( "CCSGameRules::AddGunGameWeapon: encountered an unknown weapon \"%s\".\n", pWeaponName );
  14866. }
  14867. }
  14868. //-----------------------------------------------------------------------------
  14869. // Purpose: Set up the convars and data associated with the current game type and mode
  14870. //-----------------------------------------------------------------------------
  14871. void CCSGameRules::InitializeGameTypeAndMode( void )
  14872. {
  14873. #if !defined( CLIENT_DLL )
  14874. bool isMultiplayer = !IsPlayingOffline();
  14875. const char* szMapNameFull = STRING(gpGlobals->mapname);
  14876. ///////////// load kv values from map sidecar file ( map.kv )
  14877. ////////
  14878. char filename[ MAX_PATH ];
  14879. V_StripExtension( V_UnqualifiedFileName( STRING( gpGlobals->mapname ) ), filename, MAX_PATH );
  14880. uint32 dwRichPresenceContext = 0xFFFF;
  14881. if ( !g_pGameTypes->GetMapInfo( filename, dwRichPresenceContext ) )
  14882. {
  14883. char kvFilename[ MAX_PATH ];
  14884. bool bLoadSideCar = true;
  14885. V_snprintf( kvFilename, sizeof( kvFilename ), "maps/%s.kv", filename );
  14886. if ( !g_pFullFileSystem->FileExists( kvFilename ) )
  14887. bLoadSideCar = false;
  14888. // Load the Map sidecar entry
  14889. if ( bLoadSideCar )
  14890. {
  14891. KeyValues *pkvMap = new KeyValues( "Map" );
  14892. KeyValues::AutoDelete autodelete_kvMap( pkvMap );
  14893. if ( pkvMap->LoadFromFile( g_pFullFileSystem, kvFilename ) )
  14894. {
  14895. g_pGameTypes->LoadMapEntry( pkvMap );
  14896. }
  14897. else
  14898. {
  14899. Warning( "Failed to load %s\n", kvFilename );
  14900. }
  14901. }
  14902. }
  14903. /////
  14904. ///////////////////
  14905. g_pGameTypes->CheckShouldSetDefaultGameModeAndType( szMapNameFull );
  14906. // Set the mode convars
  14907. if ( g_pGameTypes->ApplyConvarsForCurrentMode( isMultiplayer ) )
  14908. {
  14909. // Add the gun game weapons.
  14910. if ( IsPlayingGunGameProgressive() && mp_ggprogressive_use_random_weapons.GetBool() )
  14911. {
  14912. // this is where we build the list of GG progressive weapons
  14913. // collect all of the weapons here
  14914. CUtlVector< GGWeaponAliasName > pSMGs;
  14915. for ( int i=GGLIST_SMGS_START; i<(GGLIST_SMGS_LAST+1); i++ )
  14916. pSMGs.AddToTail(ggWeaponAliasNameList[i]);
  14917. CUtlVector< GGWeaponAliasName > pShotguns;
  14918. for ( int i=GGLIST_SGS_START; i<(GGLIST_SGS_LAST+1); i++ )
  14919. pShotguns.AddToTail(ggWeaponAliasNameList[i]);
  14920. CUtlVector< GGWeaponAliasName > pRifles;
  14921. for ( int i=GGLIST_RIFLES_START; i<(GGLIST_RIFLES_LAST+1); i++ )
  14922. pRifles.AddToTail(ggWeaponAliasNameList[i]);
  14923. CUtlVector< GGWeaponAliasName > pSnipers;
  14924. for ( int i=GGLIST_SNIPERS_START; i<(GGLIST_SNIPERS_LAST+1); i++ )
  14925. pSnipers.AddToTail(ggWeaponAliasNameList[i]);
  14926. CUtlVector< GGWeaponAliasName > pMGs;
  14927. for ( int i=GGLIST_MGS_START; i<(GGLIST_MGS_LAST+1); i++ )
  14928. pMGs.AddToTail(ggWeaponAliasNameList[i]);
  14929. CUtlVector< GGWeaponAliasName > pPistols;
  14930. for ( int i=GGLIST_PISTOLS_START; i<(GGLIST_PISTOLS_LAST+1); i++ )
  14931. pPistols.AddToTail(ggWeaponAliasNameList[i]);
  14932. int nNumSMGs = 3;
  14933. int nNumRifles = 4;
  14934. int nNumShotguns = 2;
  14935. int nNumSnipers = 2;
  14936. int nNumMGs = 1;
  14937. int nNumPistols = 4;
  14938. // this should total 16 weapons
  14939. int nKillsNeeded = MAX( 1, mp_ggprogressive_random_weapon_kills_needed.GetInt() );
  14940. // now pick a random one from the list we created above for each category
  14941. CUtlVector< GGWeaponAliasName > pWeaponProgression;
  14942. for ( int i=0; i<nNumSMGs; i++ )
  14943. {
  14944. int nPick = RandomInt( 0, pSMGs.Count()-1 );
  14945. pWeaponProgression.AddToTail(pSMGs[nPick]);
  14946. pSMGs.FastRemove( nPick );
  14947. }
  14948. for ( int i=0; i<nNumRifles; i++ )
  14949. {
  14950. int nPick = RandomInt( 0, pRifles.Count()-1 );
  14951. pWeaponProgression.AddToTail(pRifles[nPick]);
  14952. pRifles.FastRemove( nPick );
  14953. }
  14954. for ( int i=0; i<nNumShotguns; i++ )
  14955. {
  14956. int nPick = RandomInt( 0, pShotguns.Count()-1 );
  14957. pWeaponProgression.AddToTail(pShotguns[nPick]);
  14958. pShotguns.FastRemove( nPick );
  14959. }
  14960. for ( int i=0; i<nNumSnipers; i++ )
  14961. {
  14962. int nPick = RandomInt( 0, pSnipers.Count()-1 );
  14963. pWeaponProgression.AddToTail(pSnipers[nPick]);
  14964. pSnipers.FastRemove( nPick );
  14965. }
  14966. for ( int i=0; i<nNumMGs; i++ )
  14967. {
  14968. int nPick = RandomInt( 0, pMGs.Count()-1 );
  14969. pWeaponProgression.AddToTail(pMGs[nPick]);
  14970. pMGs.FastRemove( nPick );
  14971. }
  14972. for ( int i=0; i<nNumPistols; i++ )
  14973. {
  14974. int nPick = RandomInt( 0, pPistols.Count()-1 );
  14975. pWeaponProgression.AddToTail(pPistols[nPick]);
  14976. pPistols.FastRemove( nPick );
  14977. }
  14978. // go through the list we build and add them to the final list that will get used in the game
  14979. FOR_EACH_VEC( pWeaponProgression, iWeaponProgression )
  14980. {
  14981. const GGWeaponAliasName &wp = (pWeaponProgression)[iWeaponProgression];
  14982. AddGunGameWeapon( wp.aliasName, nKillsNeeded, TEAM_CT );
  14983. AddGunGameWeapon( wp.aliasName, nKillsNeeded, TEAM_TERRORIST );
  14984. }
  14985. // add the knife manually
  14986. AddGunGameWeapon( "knifegg", 1, TEAM_CT );
  14987. AddGunGameWeapon( "knifegg", 1, TEAM_TERRORIST );
  14988. }
  14989. // Add the gun game weapons.
  14990. else if ( IsPlayingGunGameTRBomb() || IsPlayingGunGameProgressive() )
  14991. {
  14992. const CUtlVector< IGameTypes::WeaponProgression > *pWeaponProgressionCT = g_pGameTypes->GetWeaponProgressionForCurrentModeCT();
  14993. if ( pWeaponProgressionCT )
  14994. {
  14995. FOR_EACH_VEC( *pWeaponProgressionCT, iWeaponProgression )
  14996. {
  14997. const IGameTypes::WeaponProgression &wp = (*pWeaponProgressionCT)[iWeaponProgression];
  14998. AddGunGameWeapon( wp.m_Name, wp.m_Kills, TEAM_CT );
  14999. }
  15000. }
  15001. const CUtlVector< IGameTypes::WeaponProgression > *pWeaponProgressionT = g_pGameTypes->GetWeaponProgressionForCurrentModeT();
  15002. if ( pWeaponProgressionT )
  15003. {
  15004. FOR_EACH_VEC( * pWeaponProgressionT, iWeaponProgression )
  15005. {
  15006. const IGameTypes::WeaponProgression &wp = (* pWeaponProgressionT )[iWeaponProgression];
  15007. AddGunGameWeapon( wp.m_Name, wp.m_Kills, TEAM_TERRORIST );
  15008. }
  15009. }
  15010. }
  15011. if ( CSGameRules()->HasHalfTime() )
  15012. {
  15013. m_match.SetPhase( GAMEPHASE_PLAYING_FIRST_HALF );
  15014. }
  15015. else
  15016. {
  15017. m_match.SetPhase( GAMEPHASE_PLAYING_STANDARD );
  15018. }
  15019. // Set the map convars
  15020. ConVarRef host_map( "host_map" );
  15021. g_pGameTypes->ApplyConvarsForMap( host_map.GetString(), engine->IsDedicatedServer() || isMultiplayer );
  15022. if ( CSGameRules()->IsPlayingCooperativeGametype() )
  15023. {
  15024. uint32 unQuestID = MatchmakingGameTypeToMapGroup(CCSGameRules::sm_QueuedServerReservation.game_type() );
  15025. const CEconQuestDefinition *pQuest = GetItemSchema()->GetQuestDefinition( unQuestID );
  15026. if ( pQuest && !StringIsEmpty ( pQuest->GetQuestConVars() ) )
  15027. {
  15028. m_iActiveAssassinationTargetMissionID = unQuestID;
  15029. engine->ServerCommand( CFmtStr( "%s\n", pQuest->GetQuestConVars() ) );
  15030. engine->ServerExecute();
  15031. }
  15032. }
  15033. }
  15034. #endif
  15035. #if ( CSTRIKE_DEMO_PRESSBUILD || CSTRIKE_E3_BUILD )
  15036. ConVarRef bot_quota( "bot_quota" );
  15037. bot_quota.SetValue( 8 );
  15038. ConVarRef bot_difficulty( "bot_difficulty" );
  15039. bot_difficulty.SetValue( 0 );
  15040. #endif
  15041. // this allows the weapon recoil tables to incorporate changes made to convars before the game launches
  15042. g_WeaponDatabase.RefreshAllWeapons();
  15043. }
  15044. int CCSGameRules::GetNumProgressiveGunGameWeapons( int nTeamID ) const
  15045. {
  15046. if ( nTeamID == TEAM_CT )
  15047. {
  15048. return m_iNumGunGameProgressiveWeaponsCT;
  15049. }
  15050. else
  15051. {
  15052. return m_iNumGunGameProgressiveWeaponsT;
  15053. }
  15054. }
  15055. int CCSGameRules::GetGunGameTRBonusGrenade( CCSPlayer *pPlayer )
  15056. {
  15057. if ( !pPlayer )
  15058. return 0;
  15059. int nNumTRKillPoints = pPlayer->GetNumGunGameTRKillPoints();
  15060. if ( nNumTRKillPoints >= mp_ggtr_bomb_pts_for_molotov.GetInt() )
  15061. {
  15062. if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  15063. return WEAPON_MOLOTOV;
  15064. else
  15065. return WEAPON_INCGRENADE;
  15066. }
  15067. else if ( nNumTRKillPoints >= mp_ggtr_bomb_pts_for_flash.GetInt() )
  15068. {
  15069. return WEAPON_FLASHBANG;
  15070. }
  15071. else if ( nNumTRKillPoints >= mp_ggtr_bomb_pts_for_he.GetInt() )
  15072. {
  15073. return WEAPON_HEGRENADE;
  15074. }
  15075. return 0;
  15076. }
  15077. bool CCSGameRules::IsIntermission( void ) const
  15078. {
  15079. #ifndef CLIENT_DLL
  15080. return m_flIntermissionStartTime + GetIntermissionDuration() > gpGlobals->curtime;
  15081. #endif
  15082. return false;
  15083. }
  15084. int CCSGameRules::GetMaxSpectatorSlots( void ) const
  15085. {
  15086. return m_iSpectatorSlotCount;
  15087. }
  15088. int CCSGameRules::GetMaxPlayers()
  15089. {
  15090. if ( sv_competitive_official_5v5.GetInt() )
  15091. return 10;
  15092. #ifdef CLIENT_DLL
  15093. if ( engine->IsPlayingDemo() || !engine->IsConnected() )
  15094. {
  15095. return 0;
  15096. }
  15097. #endif
  15098. return MIN( g_pGameTypes->GetCurrentServerNumSlots(), 24 );
  15099. }
  15100. void CCSGameRules::CoopResetRoundStartTime( void )
  15101. {
  15102. m_fRoundStartTime.GetForModify() = gpGlobals->curtime;
  15103. #ifdef GAME_DLL
  15104. m_coopPlayersInDeploymentZone = false;
  15105. if ( IsPlayingCoopMission() )
  15106. {
  15107. // Reset certain player stats when this happens
  15108. FOR_EACH_MAP( m_mapQueuedMatchmakingPlayersData, idxQueuedPlayer )
  15109. {
  15110. CQMMPlayerData_t &qmm = *m_mapQueuedMatchmakingPlayersData.Element( idxQueuedPlayer );
  15111. qmm.m_numHealthPointsRemovedTotal = 0;
  15112. qmm.m_numHealthPointsDealtTotal = 0;
  15113. qmm.m_numShotsFiredTotal = 0;
  15114. qmm.m_numShotsOnTargetTotal = 0;
  15115. }
  15116. // now the actual round is starting, adjust the duration here
  15117. m_iRoundTime.GetForModify() = int( mp_roundtime.GetFloat() * 60 );
  15118. }
  15119. // we also want to refresh the difficulty of the bots here
  15120. CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST );
  15121. CUtlVector < CCSBot* > vecBotsOnTeam;
  15122. if ( pTeam->GetBotMembers( &vecBotsOnTeam ) )
  15123. {
  15124. FOR_EACH_VEC( vecBotsOnTeam, i )
  15125. {
  15126. vecBotsOnTeam[i]->CoopInitialize();
  15127. }
  15128. }
  15129. #endif
  15130. }
  15131. void CCSGameRules::CoopGiveC4sToCTs( int nC4sToGive )
  15132. {
  15133. #ifdef GAME_DLL
  15134. CTeam *pTeam = GetGlobalTeam( TEAM_CT );
  15135. CUtlVector < CCSPlayer* > vecPlayers;
  15136. int nC4s = nC4sToGive;
  15137. if ( pTeam->GetHumanMembers( &vecPlayers ) )
  15138. {
  15139. FOR_EACH_VEC( vecPlayers, i )
  15140. {
  15141. if ( vecPlayers[i]->Weapon_OwnsThisType( WEAPON_C4_CLASSNAME ) )
  15142. nC4s--;
  15143. }
  15144. // don't give more C4s than players
  15145. if ( nC4s == 0 )
  15146. return;
  15147. FOR_EACH_VEC( vecPlayers, i )
  15148. {
  15149. if ( vecPlayers[i]->IsAlive() && !vecPlayers[i]->Weapon_OwnsThisType( WEAPON_C4_CLASSNAME ) )
  15150. vecPlayers[i]->GiveNamedItem( WEAPON_C4_CLASSNAME );
  15151. }
  15152. }
  15153. #endif
  15154. }
  15155. int CCSGameRules::GetNumWinsToClinch() const
  15156. {
  15157. int iNumWinsToClinch = (mp_maxrounds.GetInt() > 0 && mp_match_can_clinch.GetBool()) ? ( mp_maxrounds.GetInt() / 2 ) + 1 + GetOvertimePlaying() * ( mp_overtime_maxrounds.GetInt() / 2 ) : -1;
  15158. return iNumWinsToClinch;
  15159. }
  15160. bool CCSGameRules::IsLastRoundOfMatch() const
  15161. {
  15162. bool bLastRound = mp_maxrounds.GetInt() > 0 ? ( GetTotalRoundsPlayed() == ( mp_maxrounds.GetInt()-1 + GetOvertimePlaying()*mp_overtime_maxrounds.GetInt() ) ) : false;
  15163. return bLastRound;
  15164. }
  15165. bool CCSGameRules::IsMatchPoint() const
  15166. {
  15167. int iNumWinsToClinch = GetNumWinsToClinch();
  15168. bool bMatchPoint = false;
  15169. #ifdef CLIENT_DLL
  15170. if ( GetGamePhase() != GAMEPHASE_PLAYING_FIRST_HALF )
  15171. {
  15172. C_Team *pTerrorists = GetGlobalTeam( TEAM_TERRORIST );
  15173. C_Team *pCTs = GetGlobalTeam( TEAM_CT );
  15174. bMatchPoint = ( pCTs && ( pCTs->Get_Score() == iNumWinsToClinch-1 ) ) || ( pTerrorists && ( pTerrorists->Get_Score() == iNumWinsToClinch-1 ) );
  15175. }
  15176. #else
  15177. if ( m_match.GetPhase() != GAMEPHASE_PLAYING_FIRST_HALF )
  15178. {
  15179. bMatchPoint = ( m_match.GetCTScore() == iNumWinsToClinch-1 || m_match.GetTerroristScore() == iNumWinsToClinch-1);
  15180. }
  15181. #endif
  15182. return bMatchPoint;
  15183. }
  15184. // AreTeamsPlayingSwitchedSides() -- will return true when match is in second half, or in the half of overtime period where teams are switched.
  15185. // Overtime logic is as follows: TeamA plays CTs as first half of regulation, then Ts as second half of regulation,
  15186. // then if tied in regulation continues to play Ts as first half of 1st overtime, then switches to CTs for second half of 1st overtime,
  15187. // then if still tied after 1st OT they continue to play CTs as first half of 2nd overtime, then switch to Ts for second half of 2nd overtime,
  15188. // then if still tied after 2nd OT they continue to play Ts as first half of 3rd overtime, then switch to CTs for second half of 3rd overtime,
  15189. // and so on until the match determines a winner.
  15190. // So AreTeamsPlayingSwitchedSides will return true when TeamA is playing T-side and will return false when TeamA plays CT-side as they started match on CT
  15191. // in scenario outlined above.
  15192. bool CCSGameRules::AreTeamsPlayingSwitchedSides() const
  15193. {
  15194. if ( !GetOvertimePlaying() )
  15195. {
  15196. switch ( GetGamePhase() )
  15197. {
  15198. case GAMEPHASE_PLAYING_SECOND_HALF:
  15199. return true;
  15200. case GAMEPHASE_MATCH_ENDED:
  15201. return HasHalfTime() && ( GetTotalRoundsPlayed() > ( mp_maxrounds.GetInt() / 2 ) );
  15202. default:
  15203. return false;
  15204. }
  15205. }
  15206. else
  15207. {
  15208. switch ( GetGamePhase() )
  15209. {
  15210. case GAMEPHASE_PLAYING_SECOND_HALF:
  15211. // Playing 2nd half of 2nd half of every even OT, e.g. second OT, will result in switched teams
  15212. return ( GetOvertimePlaying() % 2 ) ? false : true;
  15213. case GAMEPHASE_MATCH_ENDED:
  15214. {
  15215. bool bEndedInSecondHalfOfOvertime = HasHalfTime() &&
  15216. ( GetTotalRoundsPlayed() > mp_maxrounds.GetInt() + ( 2*GetOvertimePlaying() - 1 ) * ( mp_overtime_maxrounds.GetInt() / 2 ) );
  15217. if ( GetOvertimePlaying() % 2 )
  15218. bEndedInSecondHalfOfOvertime = !bEndedInSecondHalfOfOvertime;
  15219. return bEndedInSecondHalfOfOvertime;
  15220. }
  15221. case GAMEPHASE_HALFTIME:
  15222. {
  15223. // halftime can also be at the end of regulation or at the end of both OT halves, in this case the overtime number has
  15224. // already been incremented into the next overtime
  15225. bool bSecondHalfOfOvertime = HasHalfTime() &&
  15226. ( GetTotalRoundsPlayed() <= ( mp_maxrounds.GetInt() + ( GetOvertimePlaying() - 1 )*mp_overtime_maxrounds.GetInt() ) );
  15227. int nOvertimeInWhichHalftimeIsActuallyReached = GetOvertimePlaying();
  15228. if ( bSecondHalfOfOvertime )
  15229. -- nOvertimeInWhichHalftimeIsActuallyReached; // this is the case when we already advanced the OT index and wait in intermission
  15230. if ( nOvertimeInWhichHalftimeIsActuallyReached % 2 )
  15231. bSecondHalfOfOvertime = !bSecondHalfOfOvertime;
  15232. return bSecondHalfOfOvertime;
  15233. }
  15234. break;
  15235. default:
  15236. // Playing 1st half, opposite of GAMEPHASE_PLAYING_SECOND_HALF state coded above
  15237. return ( GetOvertimePlaying() % 2 ) ? true : false;
  15238. }
  15239. }
  15240. }
  15241. // music selection
  15242. #ifdef CLIENT_DLL
  15243. const char *musicTypeStrings[] =
  15244. {
  15245. "NONE",
  15246. "Music.StartRound_GG",
  15247. "Music.StartRound",
  15248. "Music.StartAction",
  15249. "Music.DeathCam",
  15250. "Music.BombPlanted",
  15251. "Music.BombTenSecCount",
  15252. "Music.TenSecCount",
  15253. "Music.WonRound",
  15254. "Music.LostRound",
  15255. "Music.GotHostage",
  15256. "Music.MVPAnthem",
  15257. "Music.Selection",
  15258. "Musix.HalfTime",
  15259. };
  15260. void PlayMusicSelection( IRecipientFilter& filter, CsMusicType_t nMusicType , int nPlayerEntIndex /* = 0*/ , float flPreElapsedTime /*= 0.0*/ )
  15261. {
  15262. //////////////////////////////////////////////////////////////////////////////////////////
  15263. // test for between rounds and block incoming events until in round
  15264. //
  15265. static bool bBetweenRound = false;
  15266. if( nMusicType == CSMUSIC_LOSTROUND || nMusicType == CSMUSIC_WONROUND || nMusicType == CSMUSIC_MVP || nMusicType == CSMUSIC_HALFTIME )
  15267. {
  15268. bBetweenRound = true;
  15269. }
  15270. else if( nMusicType == CSMUSIC_START || nMusicType == CSMUSIC_ACTION || nMusicType == CSMUSIC_STARTGG )
  15271. {
  15272. bBetweenRound = false;
  15273. }
  15274. if( bBetweenRound && ( nMusicType == CSMUSIC_BOMB || nMusicType == CSMUSIC_BOMBTEN || nMusicType == CSMUSIC_ROUNDTEN ))
  15275. {
  15276. return;
  15277. }
  15278. const char *pEntry = musicTypeStrings[ nMusicType ];
  15279. ////////////////////////////////////////////////////////////////////////////////////////////
  15280. // this is to be used in the case that no music pack is equipped, mimicing original csgo behavior
  15281. // music selection switches on team select screen and halftime
  15282. static int nUpdatedMusic = snd_music_selection.GetInt();
  15283. // hack cause halftime music is getting called twice for some reason
  15284. if( nMusicType == CSMUSIC_SELECTION || nMusicType == CSMUSIC_HALFTIME )
  15285. {
  15286. nUpdatedMusic = (nUpdatedMusic == 2 ) ? 1 : 2;
  15287. }
  15288. if( nMusicType == CSMUSIC_SELECTION || nMusicType == CSMUSIC_START )
  15289. {
  15290. snd_music_selection.SetValue( nUpdatedMusic );
  15291. }
  15292. char pMusicExtensionBuf[64];
  15293. V_sprintf_safe( pMusicExtensionBuf, "%s_%02i", "valve_csgo", snd_music_selection.GetInt() );
  15294. const char *pMusicExtension = pMusicExtensionBuf;
  15295. /////////////////////////////////////////////////////////////////////////
  15296. // no halftime music in overwatch
  15297. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  15298. {
  15299. if(pParameters->m_bAnonymousPlayerIdentity && nMusicType == CSMUSIC_HALFTIME )
  15300. return; // or whatever is appropriate to not play the halftime music
  15301. }
  15302. /////////////////////////////////////////////////////////////////////////
  15303. // is there delay from switching spectated player?
  15304. // ********* not currently used ******
  15305. // engine->SOSSetOpvarFloat( "spectateMusicDelay", flPreElapsedTime );
  15306. // if( nMusicType == CSMUSIC_START )
  15307. // {
  15308. // engine->SOSSetOpvarFloat("csgo_roundstart_time", gpGlobals->curtime );
  15309. // }
  15310. ///////////////////////////////////////////////////////////////////////////////
  15311. // music source priority:
  15312. // passed in arg (if has music pack)> borrowed music > own music
  15313. int nPlayerIndex = 0;
  15314. // use a specified playerindex if passed in ( MVP )
  15315. if( ( nPlayerEntIndex != 0 ) && g_PR )
  15316. {
  15317. if ( g_PR->IsConnected( nPlayerEntIndex ) )
  15318. {
  15319. if ( C_CS_PlayerResource *cs_PR = dynamic_cast< C_CS_PlayerResource * >( g_PR ) )
  15320. {
  15321. uint32 unMusicID = cs_PR->GetMusicID( nPlayerEntIndex );
  15322. if( unMusicID > 1 )
  15323. nPlayerIndex = nPlayerEntIndex;
  15324. }
  15325. }
  15326. }
  15327. // if the passed in player didn't give us a music id, check for a borrowed kit.
  15328. if ( !nPlayerIndex )
  15329. {
  15330. int nBorrowPlIndex = cl_borrow_music_from_player_index.GetInt();
  15331. if ( nBorrowPlIndex )
  15332. {
  15333. if ( g_PR->IsConnected( nBorrowPlIndex ) && !g_PR->IsFakePlayer( nBorrowPlIndex ) )
  15334. {
  15335. nPlayerIndex = nBorrowPlIndex;
  15336. }
  15337. else
  15338. {
  15339. cl_borrow_music_from_player_index.SetValue( cl_borrow_music_from_player_index.GetDefault() );
  15340. }
  15341. }
  15342. }
  15343. // otherwise use our own
  15344. if ( !nPlayerIndex )
  15345. nPlayerIndex = GetLocalPlayerIndex();
  15346. {
  15347. uint32 unMusicID = 0;
  15348. if ( C_CS_PlayerResource *cs_PR = dynamic_cast< C_CS_PlayerResource * >( g_PR ) )
  15349. {
  15350. unMusicID = cs_PR->GetMusicID( nPlayerIndex );
  15351. }
  15352. if( unMusicID > 1 )
  15353. {
  15354. const CEconMusicDefinition *pMusicDef = GetItemSchema()->GetMusicDefinition(unMusicID);
  15355. if(pMusicDef)
  15356. pMusicExtension = pMusicDef->GetName();
  15357. }
  15358. }
  15359. if( pEntry )
  15360. {
  15361. char musicSelection[128];
  15362. int nExtLen = V_strlen( pMusicExtension );
  15363. int nStrLen = V_strlen( pEntry );
  15364. V_snprintf( musicSelection, nExtLen + nStrLen+2, "%s.%s", pEntry, pMusicExtension );
  15365. C_BaseEntity::EmitSound( filter, -1, musicSelection );
  15366. }
  15367. }
  15368. void PlayCustomDeathCamSelection( IRecipientFilter& filter, int nEntIndex, const char *pSoundEntry, int nDeathCamIndex )
  15369. {
  15370. char musicSelection[128];
  15371. V_snprintf( musicSelection, 128, "%s_%03i", pSoundEntry, nDeathCamIndex );
  15372. C_BaseEntity::EmitSound( filter, nEntIndex, musicSelection );
  15373. }
  15374. #endif
  15375. float CCSGameRules::CheckTotalSmokedLength( float flSmokeRadiusSq, Vector vecGrenadePos, Vector from, Vector to )
  15376. {
  15377. Vector sightDir = to - from;
  15378. float sightLength = sightDir.NormalizeInPlace();
  15379. // the detonation position is the actual position of the smoke grenade, but the smoke volume center is actually some number of units above that
  15380. Vector vecSmokeCenterOffset = Vector( 0, 0, 60 );
  15381. const Vector &smokeOrigin = vecGrenadePos + vecSmokeCenterOffset;
  15382. float flSmokeRadius = sqrt(flSmokeRadiusSq);
  15383. // if the start point or the end point is inside the radius of the smoke, then the line goes through the smoke
  15384. if ( (smokeOrigin - from).IsLengthLessThan( flSmokeRadius*0.95f ) || (smokeOrigin - to).IsLengthLessThan( flSmokeRadius ) )
  15385. return -1;
  15386. Vector toGrenade = smokeOrigin - from;
  15387. float alongDist = DotProduct( toGrenade, sightDir );
  15388. // compute closest point to grenade along line of sight ray
  15389. Vector close;
  15390. // constrain closest point to line segment
  15391. if (alongDist < 0.0f)
  15392. close = from;
  15393. else if (alongDist >= sightLength)
  15394. close = to;
  15395. else
  15396. close = from + sightDir * alongDist;
  15397. // if closest point is within smoke radius, the line overlaps the smoke cloud
  15398. Vector toClose = close - smokeOrigin;
  15399. float lengthSq = toClose.LengthSqr();
  15400. //float smokeRadius = (float)sqrt( flSmokeRadiusSq );
  15401. //NDebugOverlay::Sphere( smokeOrigin, smokeRadius, 0, 255, 0, true, 2.0f);
  15402. if (lengthSq < flSmokeRadiusSq)
  15403. {
  15404. // some portion of the ray intersects the cloud
  15405. // 'from' and 'to' lie outside of the cloud - the line of sight completely crosses it
  15406. // determine the length of the chord that crosses the cloud
  15407. float smokedLength = 2.0f * (float)sqrt( flSmokeRadiusSq - lengthSq );
  15408. return smokedLength;
  15409. }
  15410. return 0;
  15411. }
  15412. #if defined( CLIENT_DLL )
  15413. //-----------------------------------------------------------------------------
  15414. // Enforce certain values on the specified convar.
  15415. //-----------------------------------------------------------------------------
  15416. void EnforceCompetitiveCVar( const char *szCvarName, float fMinValue, float fMaxValue = FLT_MAX, int iArgs = 0, ... )
  15417. {
  15418. // Doing this check first because OK values might be outside the min/max range
  15419. ConVarRef competitiveConvar(szCvarName);
  15420. float fValue = competitiveConvar.GetFloat();
  15421. va_list vl;
  15422. va_start(vl, iArgs);
  15423. for( int i=0; i< iArgs; ++i )
  15424. {
  15425. if( (int)fValue == va_arg(vl,int) )
  15426. return;
  15427. }
  15428. va_end(vl);
  15429. if ( fValue < fMinValue || fValue > fMaxValue )
  15430. {
  15431. float fNewValue = MAX( MIN( fValue, fMaxValue ), fMinValue );
  15432. competitiveConvar.SetValue( fNewValue );
  15433. DevMsg( "Convar %s was out of range and forced to %.2f. Valid values are between %.2f and %.2f. To remove the restriction set sv_competitive_minspec 0 on the server.\n", szCvarName, fNewValue, fMinValue, fMaxValue );
  15434. }
  15435. else
  15436. {
  15437. competitiveConvar.SetValue( fValue );
  15438. }
  15439. }
  15440. //-----------------------------------------------------------------------------
  15441. // An interface used by ENABLE_COMPETITIVE_CONVAR macro that lets the classes
  15442. // defined in the macro to be stored and acted on.
  15443. //-----------------------------------------------------------------------------
  15444. class ICompetitiveConvar
  15445. {
  15446. public:
  15447. virtual void BackupConvar() = 0;
  15448. virtual void EnforceRestrictions() = 0;
  15449. virtual void RestoreOriginalValue() = 0;
  15450. virtual void InstallChangeCallback() = 0;
  15451. };
  15452. //-----------------------------------------------------------------------------
  15453. // A manager for all enforced competitive convars.
  15454. //-----------------------------------------------------------------------------
  15455. class CCompetitiveCvarManager : public CAutoGameSystem
  15456. {
  15457. public:
  15458. typedef CUtlVector<ICompetitiveConvar*> CompetitiveConvarList_t;
  15459. static void AddConvarToList( ICompetitiveConvar* pCVar )
  15460. {
  15461. GetConvarList()->AddToTail( pCVar );
  15462. }
  15463. static void BackupAllConvars()
  15464. {
  15465. FOR_EACH_VEC( *GetConvarList(), i )
  15466. {
  15467. (*GetConvarList())[i]->BackupConvar();
  15468. }
  15469. }
  15470. static void EnforceRestrictionsOnAllConvars()
  15471. {
  15472. FOR_EACH_VEC( *GetConvarList(), i )
  15473. {
  15474. (*GetConvarList())[i]->EnforceRestrictions();
  15475. }
  15476. }
  15477. static void RestoreAllOriginalValues()
  15478. {
  15479. FOR_EACH_VEC( *GetConvarList(), i )
  15480. {
  15481. (*GetConvarList())[i]->RestoreOriginalValue();
  15482. }
  15483. }
  15484. static CompetitiveConvarList_t* GetConvarList()
  15485. {
  15486. if( !s_pCompetitiveConvars )
  15487. {
  15488. s_pCompetitiveConvars = new CompetitiveConvarList_t();
  15489. }
  15490. return s_pCompetitiveConvars;
  15491. }
  15492. static KeyValues* GetConVarBackupKV()
  15493. {
  15494. if( !s_pConVarBackups )
  15495. {
  15496. s_pConVarBackups = new KeyValues("ConVarBackups");
  15497. }
  15498. return s_pConVarBackups;
  15499. }
  15500. virtual bool Init()
  15501. {
  15502. FOR_EACH_VEC( *GetConvarList(), i )
  15503. {
  15504. (*GetConvarList())[i]->InstallChangeCallback();
  15505. }
  15506. return true;
  15507. }
  15508. virtual void Shutdown()
  15509. {
  15510. FOR_EACH_VEC( *GetConvarList(), i )
  15511. {
  15512. delete (*GetConvarList())[i];
  15513. }
  15514. delete s_pCompetitiveConvars;
  15515. s_pCompetitiveConvars = 0;
  15516. s_pConVarBackups->deleteThis();
  15517. s_pConVarBackups = 0;
  15518. }
  15519. CCompetitiveCvarManager()
  15520. {
  15521. s_pCompetitiveConvars = 0;
  15522. s_pConVarBackups = 0;
  15523. }
  15524. private:
  15525. static CompetitiveConvarList_t* s_pCompetitiveConvars;
  15526. static KeyValues* s_pConVarBackups;
  15527. };
  15528. static CCompetitiveCvarManager *s_pCompetitiveCvarManager = new CCompetitiveCvarManager();
  15529. CCompetitiveCvarManager::CompetitiveConvarList_t* CCompetitiveCvarManager::s_pCompetitiveConvars = 0;
  15530. KeyValues* CCompetitiveCvarManager::s_pConVarBackups = 0;
  15531. //-----------------------------------------------------------------------------
  15532. // Macro to define restrictions on convars with "sv_competitive_minspec 1"
  15533. // Usage: ENABLE_COMPETITIVE_CONVAR( convarName, minValue, maxValue, optionalValues, opVal1, opVal2, ...
  15534. //-----------------------------------------------------------------------------
  15535. #define ENABLE_COMPETITIVE_CONVAR( convarName, ... ) \
  15536. class CCompetitiveMinspecConvar##convarName : public ICompetitiveConvar { \
  15537. public: \
  15538. CCompetitiveMinspecConvar##convarName(){ CCompetitiveCvarManager::AddConvarToList(this);} \
  15539. static void on_changed_##convarName( IConVar *var, const char *pOldValue, float flOldValue ){ \
  15540. if( sv_competitive_minspec.GetBool() ) { \
  15541. EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); }\
  15542. else {\
  15543. CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } } \
  15544. virtual void BackupConvar() { CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } \
  15545. virtual void EnforceRestrictions() { EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); } \
  15546. virtual void RestoreOriginalValue() { ConVarRef(#convarName).SetValue(CCompetitiveCvarManager::GetConVarBackupKV()->GetFloat( #convarName ) ); } \
  15547. virtual void InstallChangeCallback() { static_cast<ConVar*>(ConVarRef( #convarName ).GetLinkedConVar())->InstallChangeCallback( CCompetitiveMinspecConvar##convarName::on_changed_##convarName); } \
  15548. }; \
  15549. static CCompetitiveMinspecConvar##convarName *s_pCompetitiveConvar##convarName = new CCompetitiveMinspecConvar##convarName();
  15550. //-----------------------------------------------------------------------------
  15551. // Callback function for sv_competitive_minspec convar value change.
  15552. //-----------------------------------------------------------------------------
  15553. void sv_competitive_minspec_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
  15554. {
  15555. ConVar *pCvar = static_cast<ConVar*>(var);
  15556. if( pCvar->GetBool() == true && flOldValue == 0.0f )
  15557. {
  15558. // Backup the values of each cvar and enforce new ones
  15559. CCompetitiveCvarManager::BackupAllConvars();
  15560. CCompetitiveCvarManager::EnforceRestrictionsOnAllConvars();
  15561. }
  15562. else if( pCvar->GetBool() == false && flOldValue != 0.0f )
  15563. {
  15564. // If sv_competitive_minspec is disabled, restore old client values
  15565. CCompetitiveCvarManager::RestoreAllOriginalValues();
  15566. }
  15567. }
  15568. #endif
  15569. static ConVar sv_competitive_minspec( "sv_competitive_minspec",
  15570. "1",
  15571. FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE,
  15572. "Enable to force certain client convars to minimum/maximum values to help prevent competitive advantages."
  15573. #ifdef CLIENT_DLL
  15574. ,sv_competitive_minspec_changed_f
  15575. #endif
  15576. );
  15577. #ifdef CLIENT_DLL
  15578. #if defined( _GAMECONSOLE )
  15579. // ENABLE_COMPETITIVE_CONVAR( convar, range minimum, range maximum, number of additional distinct valid values, distinct valid values... );
  15580. ENABLE_COMPETITIVE_CONVAR( fps_max, 29, FLT_MAX, 1, 0 ); // force fps_max above 59. One additional value (0) works
  15581. #else
  15582. ENABLE_COMPETITIVE_CONVAR( fps_max, 59, FLT_MAX, 1, 0 ); // force fps_max above 59. One additional value (0) works
  15583. #endif
  15584. ENABLE_COMPETITIVE_CONVAR( cl_interp_ratio, 1, 2 ); // force cl_interp_ratio from 1 to 2
  15585. ENABLE_COMPETITIVE_CONVAR( cl_interp, 0, 0.031 ); // force cl_interp from 0.0152 to 0.031
  15586. ENABLE_COMPETITIVE_CONVAR( cl_updaterate, 10, 150 ); // force cl_updaterate from 10 to 150
  15587. ENABLE_COMPETITIVE_CONVAR( cl_cmdrate, 10, 150 ); // force cl_cmdrate from 10 to 150
  15588. ENABLE_COMPETITIVE_CONVAR( rate, 20480, 786432 ); // force rate above min rate and below max rate
  15589. ENABLE_COMPETITIVE_CONVAR( viewmodel_fov, 54, 68 ); // force viewmodel fov to be between 54 and 68
  15590. ENABLE_COMPETITIVE_CONVAR( viewmodel_offset_x, -2, 2.5 ); // restrict viewmodel positioning
  15591. ENABLE_COMPETITIVE_CONVAR( viewmodel_offset_y, -2, 2 );
  15592. ENABLE_COMPETITIVE_CONVAR( viewmodel_offset_z, -2, 2 );
  15593. ENABLE_COMPETITIVE_CONVAR( cl_bobcycle, 0.98, 0.98 ); // tournament standard
  15594. // replaced with sv_max_allowed_net_graph convar
  15595. //ENABLE_COMPETITIVE_CONVAR( net_graph, 0, 1 ) // tournament standard
  15596. #endif
  15597. #ifdef GAME_DLL
  15598. CCSPlayer* FindPlayerFromAccountID( uint32 account_id )
  15599. {
  15600. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  15601. {
  15602. CBasePlayer *pBasePlayer = UTIL_PlayerByIndex( i );
  15603. if ( !pBasePlayer )
  15604. continue;
  15605. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( pBasePlayer );
  15606. if ( !pPlayer || pPlayer->IsBot() || !pPlayer->IsConnected() )
  15607. continue;
  15608. CSteamID steamID;
  15609. pPlayer->GetSteamID( &steamID );
  15610. if ( steamID.GetAccountID() == account_id )
  15611. return pPlayer;
  15612. }
  15613. return NULL;
  15614. }
  15615. #endif
  15616. #ifdef GAME_DLL
  15617. class ClientJob_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty : public GCSDK::CGCClientJob
  15618. {
  15619. public:
  15620. ClientJob_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient )
  15621. {
  15622. }
  15623. virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket )
  15624. {
  15625. GCSDK::CProtoBufMsg<CMsgGCCStrike15_v2_ServerNotificationForUserPenalty> msg( pNetPacket );
  15626. DevMsg( "Notification about user penalty: %u/%u (%u sec)\n", msg.Body().account_id(), msg.Body().reason(), msg.Body().seconds() );
  15627. if ( !engine->IsDedicatedServer() || !msg.Body().account_id() )
  15628. return true;
  15629. if ( !CCSGameRules::sm_mapGcBanInformation.Count() )
  15630. SetDefLessFunc( CCSGameRules::sm_mapGcBanInformation );
  15631. {
  15632. CCSGameRules::CGcBanInformation_t baninfo = { msg.Body().reason(), Plat_FloatTime() + msg.Body().seconds() };
  15633. CCSGameRules::sm_mapGcBanInformation.InsertOrReplace( msg.Body().account_id(), baninfo );
  15634. }
  15635. return true;
  15636. }
  15637. };
  15638. GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty, k_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty );
  15639. class ClientJob_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification : public GCSDK::CGCClientJob
  15640. {
  15641. public:
  15642. ClientJob_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient )
  15643. {
  15644. }
  15645. virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket )
  15646. {
  15647. GCSDK::CProtoBufMsg<CMsgGCCStrike15_v2_MatchEndRewardDropsNotification> msg( pNetPacket );
  15648. if ( !msg.Body().has_iteminfo() )
  15649. return true;
  15650. DevMsg( "Notification about user drop: %u %llu (%u-%u-%u)\n", msg.Body().iteminfo().accountid(), msg.Body().iteminfo().itemid(),
  15651. msg.Body().iteminfo().defindex(), msg.Body().iteminfo().paintindex(), msg.Body().iteminfo().rarity() );
  15652. if ( msg.Body().iteminfo().accountid() && msg.Body().iteminfo().itemid() && CSGameRules() )
  15653. {
  15654. CSGameRules()->RecordPlayerItemDrop( msg.Body().iteminfo() );
  15655. }
  15656. return true;
  15657. }
  15658. };
  15659. GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification, k_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification );
  15660. static CMsgGCCStrike15_v2_GiftsLeaderboardResponse g_dataGiftsLeaderboard;
  15661. static double g_dblGiftsLeaderboardReceived = 0;
  15662. void CCSGameRules::CheckForGiftsLeaderboardUpdate()
  15663. {
  15664. }
  15665. class ClientJob_EMsgGCCStrike15_v2_GiftsLeaderboardResponse : public GCSDK::CGCClientJob
  15666. {
  15667. public:
  15668. ClientJob_EMsgGCCStrike15_v2_GiftsLeaderboardResponse( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient )
  15669. {
  15670. }
  15671. virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket )
  15672. {
  15673. GCSDK::CProtoBufMsg<CMsgGCCStrike15_v2_GiftsLeaderboardResponse> msg( pNetPacket );
  15674. if ( !msg.Body().has_servertime() )
  15675. return true;
  15676. // Set our cached structure
  15677. g_dataGiftsLeaderboard = msg.Body();
  15678. g_dblGiftsLeaderboardReceived = Plat_FloatTime();
  15679. if ( CCSGameRules *pCSGR = CSGameRules() )
  15680. { // Copy gifts
  15681. pCSGR->m_numGlobalGiftsGiven = g_dataGiftsLeaderboard.total_gifts_given();
  15682. pCSGR->m_numGlobalGifters = g_dataGiftsLeaderboard.total_givers();
  15683. pCSGR->m_numGlobalGiftsPeriodSeconds = g_dataGiftsLeaderboard.time_period_seconds();
  15684. for ( int j = 0; j < MAX_GIFT_GIVERS_FEATURED_COUNT; ++ j )
  15685. {
  15686. pCSGR->m_arrFeaturedGiftersAccounts.Set( j, ( j < g_dataGiftsLeaderboard.entries().size() ) ? g_dataGiftsLeaderboard.entries( j ).accountid() : 0 );
  15687. pCSGR->m_arrFeaturedGiftersGifts.Set( j, ( j < g_dataGiftsLeaderboard.entries().size() ) ? g_dataGiftsLeaderboard.entries( j ).gifts() : 0 );
  15688. }
  15689. }
  15690. return true;
  15691. }
  15692. };
  15693. GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_GiftsLeaderboardResponse, k_EMsgGCCStrike15_v2_GiftsLeaderboardResponse );
  15694. #endif // GAME_DLL
  15695. #ifndef CLIENT_DLL
  15696. bool CCSGameRules::OnReplayPrompt( CBasePlayer *pVictim, CBasePlayer *pScorer )
  15697. {
  15698. if ( m_iRoundWinStatus != WINNER_NONE )
  15699. {
  15700. // victim killed after the end of round: do not replay
  15701. return false;
  15702. }
  15703. return CTeamplayRules::OnReplayPrompt( pVictim, pScorer ); // delegate to the base class
  15704. }
  15705. #endif