Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1279 lines
32 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "hl2mp_gamerules.h"
  9. #include "viewport_panel_names.h"
  10. #include "gameeventdefs.h"
  11. #include <KeyValues.h>
  12. #include "ammodef.h"
  13. #ifdef CLIENT_DLL
  14. #include "c_hl2mp_player.h"
  15. #else
  16. #include "eventqueue.h"
  17. #include "player.h"
  18. #include "gamerules.h"
  19. #include "game.h"
  20. #include "items.h"
  21. #include "entitylist.h"
  22. #include "mapentities.h"
  23. #include "in_buttons.h"
  24. #include <ctype.h>
  25. #include "voice_gamemgr.h"
  26. #include "iscorer.h"
  27. #include "hl2mp_player.h"
  28. #include "weapon_hl2mpbasehlmpcombatweapon.h"
  29. #include "team.h"
  30. #include "voice_gamemgr.h"
  31. #include "hl2mp_gameinterface.h"
  32. #include "hl2mp_cvars.h"
  33. #ifdef DEBUG
  34. #include "hl2mp_bot_temp.h"
  35. #endif
  36. extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse);
  37. extern bool FindInList( const char **pStrings, const char *pToFind );
  38. ConVar sv_hl2mp_weapon_respawn_time( "sv_hl2mp_weapon_respawn_time", "20", FCVAR_GAMEDLL | FCVAR_NOTIFY );
  39. ConVar sv_hl2mp_item_respawn_time( "sv_hl2mp_item_respawn_time", "30", FCVAR_GAMEDLL | FCVAR_NOTIFY );
  40. ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY );
  41. extern ConVar mp_chattime;
  42. extern CBaseEntity *g_pLastCombineSpawn;
  43. extern CBaseEntity *g_pLastRebelSpawn;
  44. #define WEAPON_MAX_DISTANCE_FROM_SPAWN 64
  45. #endif
  46. REGISTER_GAMERULES_CLASS( CHL2MPRules );
  47. BEGIN_NETWORK_TABLE_NOBASE( CHL2MPRules, DT_HL2MPRules )
  48. #ifdef CLIENT_DLL
  49. RecvPropBool( RECVINFO( m_bTeamPlayEnabled ) ),
  50. #else
  51. SendPropBool( SENDINFO( m_bTeamPlayEnabled ) ),
  52. #endif
  53. END_NETWORK_TABLE()
  54. LINK_ENTITY_TO_CLASS( hl2mp_gamerules, CHL2MPGameRulesProxy );
  55. IMPLEMENT_NETWORKCLASS_ALIASED( HL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
  56. static HL2MPViewVectors g_HL2MPViewVectors(
  57. Vector( 0, 0, 64 ), //VEC_VIEW (m_vView)
  58. Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin)
  59. Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax)
  60. Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin)
  61. Vector( 16, 16, 36 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax)
  62. Vector( 0, 0, 28 ), //VEC_DUCK_VIEW (m_vDuckView)
  63. Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin)
  64. Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax)
  65. Vector( 0, 0, 14 ), //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight)
  66. Vector(-16, -16, 0 ), //VEC_CROUCH_TRACE_MIN (m_vCrouchTraceMin)
  67. Vector( 16, 16, 60 ) //VEC_CROUCH_TRACE_MAX (m_vCrouchTraceMax)
  68. );
  69. static const char *s_PreserveEnts[] =
  70. {
  71. "ai_network",
  72. "ai_hint",
  73. "hl2mp_gamerules",
  74. "team_manager",
  75. "player_manager",
  76. "env_soundscape",
  77. "env_soundscape_proxy",
  78. "env_soundscape_triggerable",
  79. "env_sun",
  80. "env_wind",
  81. "env_fog_controller",
  82. "func_brush",
  83. "func_wall",
  84. "func_buyzone",
  85. "func_illusionary",
  86. "infodecal",
  87. "info_projecteddecal",
  88. "info_node",
  89. "info_target",
  90. "info_node_hint",
  91. "info_player_deathmatch",
  92. "info_player_combine",
  93. "info_player_rebel",
  94. "info_map_parameters",
  95. "keyframe_rope",
  96. "move_rope",
  97. "info_ladder",
  98. "player",
  99. "point_viewcontrol",
  100. "scene_manager",
  101. "shadow_control",
  102. "sky_camera",
  103. "soundent",
  104. "trigger_soundscape",
  105. "viewmodel",
  106. "predicted_viewmodel",
  107. "worldspawn",
  108. "point_devshot_camera",
  109. "", // END Marker
  110. };
  111. #ifdef CLIENT_DLL
  112. void RecvProxy_HL2MPRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
  113. {
  114. CHL2MPRules *pRules = HL2MPRules();
  115. Assert( pRules );
  116. *pOut = pRules;
  117. }
  118. BEGIN_RECV_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
  119. RecvPropDataTable( "hl2mp_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2MPRules ), RecvProxy_HL2MPRules )
  120. END_RECV_TABLE()
  121. #else
  122. void* SendProxy_HL2MPRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  123. {
  124. CHL2MPRules *pRules = HL2MPRules();
  125. Assert( pRules );
  126. return pRules;
  127. }
  128. BEGIN_SEND_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
  129. SendPropDataTable( "hl2mp_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_HL2MPRules ), SendProxy_HL2MPRules )
  130. END_SEND_TABLE()
  131. #endif
  132. #ifndef CLIENT_DLL
  133. class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
  134. {
  135. public:
  136. virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity )
  137. {
  138. return ( pListener->GetTeamNumber() == pTalker->GetTeamNumber() );
  139. }
  140. };
  141. CVoiceGameMgrHelper g_VoiceGameMgrHelper;
  142. IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
  143. #endif
  144. // NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
  145. char *sTeamNames[] =
  146. {
  147. "Unassigned",
  148. "Spectator",
  149. "Combine",
  150. "Rebels",
  151. };
  152. CHL2MPRules::CHL2MPRules()
  153. {
  154. #ifndef CLIENT_DLL
  155. // Create the team managers
  156. for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ )
  157. {
  158. CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "team_manager" ));
  159. pTeam->Init( sTeamNames[i], i );
  160. g_Teams.AddToTail( pTeam );
  161. }
  162. m_bTeamPlayEnabled = teamplay.GetBool();
  163. m_flIntermissionEndTime = 0.0f;
  164. m_flGameStartTime = 0;
  165. m_hRespawnableItemsAndWeapons.RemoveAll();
  166. m_tmNextPeriodicThink = 0;
  167. m_flRestartGameTime = 0;
  168. m_bCompleteReset = false;
  169. m_bHeardAllPlayersReady = false;
  170. m_bAwaitingReadyRestart = false;
  171. m_bChangelevelDone = false;
  172. #endif
  173. }
  174. const CViewVectors* CHL2MPRules::GetViewVectors()const
  175. {
  176. return &g_HL2MPViewVectors;
  177. }
  178. const HL2MPViewVectors* CHL2MPRules::GetHL2MPViewVectors()const
  179. {
  180. return &g_HL2MPViewVectors;
  181. }
  182. CHL2MPRules::~CHL2MPRules( void )
  183. {
  184. #ifndef CLIENT_DLL
  185. // Note, don't delete each team since they are in the gEntList and will
  186. // automatically be deleted from there, instead.
  187. g_Teams.Purge();
  188. #endif
  189. }
  190. void CHL2MPRules::CreateStandardEntities( void )
  191. {
  192. #ifndef CLIENT_DLL
  193. // Create the entity that will send our data to the client.
  194. BaseClass::CreateStandardEntities();
  195. g_pLastCombineSpawn = NULL;
  196. g_pLastRebelSpawn = NULL;
  197. #ifdef DBGFLAG_ASSERT
  198. CBaseEntity *pEnt =
  199. #endif
  200. CBaseEntity::Create( "hl2mp_gamerules", vec3_origin, vec3_angle );
  201. Assert( pEnt );
  202. #endif
  203. }
  204. //=========================================================
  205. // FlWeaponRespawnTime - what is the time in the future
  206. // at which this weapon may spawn?
  207. //=========================================================
  208. float CHL2MPRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon )
  209. {
  210. #ifndef CLIENT_DLL
  211. if ( weaponstay.GetInt() > 0 )
  212. {
  213. // make sure it's only certain weapons
  214. if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
  215. {
  216. return 0; // weapon respawns almost instantly
  217. }
  218. }
  219. return sv_hl2mp_weapon_respawn_time.GetFloat();
  220. #endif
  221. return 0; // weapon respawns almost instantly
  222. }
  223. bool CHL2MPRules::IsIntermission( void )
  224. {
  225. #ifndef CLIENT_DLL
  226. return m_flIntermissionEndTime > gpGlobals->curtime;
  227. #endif
  228. return false;
  229. }
  230. void CHL2MPRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  231. {
  232. #ifndef CLIENT_DLL
  233. if ( IsIntermission() )
  234. return;
  235. BaseClass::PlayerKilled( pVictim, info );
  236. #endif
  237. }
  238. void CHL2MPRules::Think( void )
  239. {
  240. #ifndef CLIENT_DLL
  241. CGameRules::Think();
  242. if ( g_fGameOver ) // someone else quit the game already
  243. {
  244. // check to see if we should change levels now
  245. if ( m_flIntermissionEndTime < gpGlobals->curtime )
  246. {
  247. if ( !m_bChangelevelDone )
  248. {
  249. ChangeLevel(); // intermission is over
  250. m_bChangelevelDone = true;
  251. }
  252. }
  253. return;
  254. }
  255. // float flTimeLimit = mp_timelimit.GetFloat() * 60;
  256. float flFragLimit = fraglimit.GetFloat();
  257. if ( GetMapRemainingTime() < 0 )
  258. {
  259. GoToIntermission();
  260. return;
  261. }
  262. if ( flFragLimit )
  263. {
  264. if( IsTeamplay() == true )
  265. {
  266. CTeam *pCombine = g_Teams[TEAM_COMBINE];
  267. CTeam *pRebels = g_Teams[TEAM_REBELS];
  268. if ( pCombine->GetScore() >= flFragLimit || pRebels->GetScore() >= flFragLimit )
  269. {
  270. GoToIntermission();
  271. return;
  272. }
  273. }
  274. else
  275. {
  276. // check if any player is over the frag limit
  277. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  278. {
  279. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  280. if ( pPlayer && pPlayer->FragCount() >= flFragLimit )
  281. {
  282. GoToIntermission();
  283. return;
  284. }
  285. }
  286. }
  287. }
  288. if ( gpGlobals->curtime > m_tmNextPeriodicThink )
  289. {
  290. CheckAllPlayersReady();
  291. CheckRestartGame();
  292. m_tmNextPeriodicThink = gpGlobals->curtime + 1.0;
  293. }
  294. if ( m_flRestartGameTime > 0.0f && m_flRestartGameTime <= gpGlobals->curtime )
  295. {
  296. RestartGame();
  297. }
  298. if( m_bAwaitingReadyRestart && m_bHeardAllPlayersReady )
  299. {
  300. UTIL_ClientPrintAll( HUD_PRINTCENTER, "All players ready. Game will restart in 5 seconds" );
  301. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "All players ready. Game will restart in 5 seconds" );
  302. m_flRestartGameTime = gpGlobals->curtime + 5;
  303. m_bAwaitingReadyRestart = false;
  304. }
  305. ManageObjectRelocation();
  306. #endif
  307. }
  308. void CHL2MPRules::GoToIntermission( void )
  309. {
  310. #ifndef CLIENT_DLL
  311. if ( g_fGameOver )
  312. return;
  313. g_fGameOver = true;
  314. m_flIntermissionEndTime = gpGlobals->curtime + mp_chattime.GetInt();
  315. for ( int i = 0; i < MAX_PLAYERS; i++ )
  316. {
  317. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  318. if ( !pPlayer )
  319. continue;
  320. pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
  321. pPlayer->AddFlag( FL_FROZEN );
  322. }
  323. #endif
  324. }
  325. bool CHL2MPRules::CheckGameOver()
  326. {
  327. #ifndef CLIENT_DLL
  328. if ( g_fGameOver ) // someone else quit the game already
  329. {
  330. // check to see if we should change levels now
  331. if ( m_flIntermissionEndTime < gpGlobals->curtime )
  332. {
  333. ChangeLevel(); // intermission is over
  334. }
  335. return true;
  336. }
  337. #endif
  338. return false;
  339. }
  340. // when we are within this close to running out of entities, items
  341. // marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn
  342. #define ENTITY_INTOLERANCE 100
  343. //=========================================================
  344. // FlWeaponRespawnTime - Returns 0 if the weapon can respawn
  345. // now, otherwise it returns the time at which it can try
  346. // to spawn again.
  347. //=========================================================
  348. float CHL2MPRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon )
  349. {
  350. #ifndef CLIENT_DLL
  351. if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
  352. {
  353. if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) )
  354. return 0;
  355. // we're past the entity tolerance level, so delay the respawn
  356. return FlWeaponRespawnTime( pWeapon );
  357. }
  358. #endif
  359. return 0;
  360. }
  361. //=========================================================
  362. // VecWeaponRespawnSpot - where should this weapon spawn?
  363. // Some game variations may choose to randomize spawn locations
  364. //=========================================================
  365. Vector CHL2MPRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon )
  366. {
  367. #ifndef CLIENT_DLL
  368. CWeaponHL2MPBase *pHL2Weapon = dynamic_cast< CWeaponHL2MPBase*>( pWeapon );
  369. if ( pHL2Weapon )
  370. {
  371. return pHL2Weapon->GetOriginalSpawnOrigin();
  372. }
  373. #endif
  374. return pWeapon->GetAbsOrigin();
  375. }
  376. #ifndef CLIENT_DLL
  377. CItem* IsManagedObjectAnItem( CBaseEntity *pObject )
  378. {
  379. return dynamic_cast< CItem*>( pObject );
  380. }
  381. CWeaponHL2MPBase* IsManagedObjectAWeapon( CBaseEntity *pObject )
  382. {
  383. return dynamic_cast< CWeaponHL2MPBase*>( pObject );
  384. }
  385. bool GetObjectsOriginalParameters( CBaseEntity *pObject, Vector &vOriginalOrigin, QAngle &vOriginalAngles )
  386. {
  387. if ( CItem *pItem = IsManagedObjectAnItem( pObject ) )
  388. {
  389. if ( pItem->m_flNextResetCheckTime > gpGlobals->curtime )
  390. return false;
  391. vOriginalOrigin = pItem->GetOriginalSpawnOrigin();
  392. vOriginalAngles = pItem->GetOriginalSpawnAngles();
  393. pItem->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_item_respawn_time.GetFloat();
  394. return true;
  395. }
  396. else if ( CWeaponHL2MPBase *pWeapon = IsManagedObjectAWeapon( pObject ))
  397. {
  398. if ( pWeapon->m_flNextResetCheckTime > gpGlobals->curtime )
  399. return false;
  400. vOriginalOrigin = pWeapon->GetOriginalSpawnOrigin();
  401. vOriginalAngles = pWeapon->GetOriginalSpawnAngles();
  402. pWeapon->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_weapon_respawn_time.GetFloat();
  403. return true;
  404. }
  405. return false;
  406. }
  407. void CHL2MPRules::ManageObjectRelocation( void )
  408. {
  409. int iTotal = m_hRespawnableItemsAndWeapons.Count();
  410. if ( iTotal > 0 )
  411. {
  412. for ( int i = 0; i < iTotal; i++ )
  413. {
  414. CBaseEntity *pObject = m_hRespawnableItemsAndWeapons[i].Get();
  415. if ( pObject )
  416. {
  417. Vector vSpawOrigin;
  418. QAngle vSpawnAngles;
  419. if ( GetObjectsOriginalParameters( pObject, vSpawOrigin, vSpawnAngles ) == true )
  420. {
  421. float flDistanceFromSpawn = (pObject->GetAbsOrigin() - vSpawOrigin ).Length();
  422. if ( flDistanceFromSpawn > WEAPON_MAX_DISTANCE_FROM_SPAWN )
  423. {
  424. bool shouldReset = false;
  425. IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
  426. if ( pPhysics )
  427. {
  428. shouldReset = pPhysics->IsAsleep();
  429. }
  430. else
  431. {
  432. shouldReset = (pObject->GetFlags() & FL_ONGROUND) ? true : false;
  433. }
  434. if ( shouldReset )
  435. {
  436. pObject->Teleport( &vSpawOrigin, &vSpawnAngles, NULL );
  437. pObject->EmitSound( "AlyxEmp.Charge" );
  438. IPhysicsObject *pPhys = pObject->VPhysicsGetObject();
  439. if ( pPhys )
  440. {
  441. pPhys->Wake();
  442. }
  443. }
  444. }
  445. }
  446. }
  447. }
  448. }
  449. }
  450. //=========================================================
  451. //AddLevelDesignerPlacedWeapon
  452. //=========================================================
  453. void CHL2MPRules::AddLevelDesignerPlacedObject( CBaseEntity *pEntity )
  454. {
  455. if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) == -1 )
  456. {
  457. m_hRespawnableItemsAndWeapons.AddToTail( pEntity );
  458. }
  459. }
  460. //=========================================================
  461. //RemoveLevelDesignerPlacedWeapon
  462. //=========================================================
  463. void CHL2MPRules::RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity )
  464. {
  465. if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) != -1 )
  466. {
  467. m_hRespawnableItemsAndWeapons.FindAndRemove( pEntity );
  468. }
  469. }
  470. //=========================================================
  471. // Where should this item respawn?
  472. // Some game variations may choose to randomize spawn locations
  473. //=========================================================
  474. Vector CHL2MPRules::VecItemRespawnSpot( CItem *pItem )
  475. {
  476. return pItem->GetOriginalSpawnOrigin();
  477. }
  478. //=========================================================
  479. // What angles should this item use to respawn?
  480. //=========================================================
  481. QAngle CHL2MPRules::VecItemRespawnAngles( CItem *pItem )
  482. {
  483. return pItem->GetOriginalSpawnAngles();
  484. }
  485. //=========================================================
  486. // At what time in the future may this Item respawn?
  487. //=========================================================
  488. float CHL2MPRules::FlItemRespawnTime( CItem *pItem )
  489. {
  490. return sv_hl2mp_item_respawn_time.GetFloat();
  491. }
  492. //=========================================================
  493. // CanHaveWeapon - returns false if the player is not allowed
  494. // to pick up this weapon
  495. //=========================================================
  496. bool CHL2MPRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem )
  497. {
  498. if ( weaponstay.GetInt() > 0 )
  499. {
  500. if ( pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) )
  501. return false;
  502. }
  503. return BaseClass::CanHavePlayerItem( pPlayer, pItem );
  504. }
  505. #endif
  506. //=========================================================
  507. // WeaponShouldRespawn - any conditions inhibiting the
  508. // respawning of this weapon?
  509. //=========================================================
  510. int CHL2MPRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon )
  511. {
  512. #ifndef CLIENT_DLL
  513. if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) )
  514. {
  515. return GR_WEAPON_RESPAWN_NO;
  516. }
  517. #endif
  518. return GR_WEAPON_RESPAWN_YES;
  519. }
  520. //-----------------------------------------------------------------------------
  521. // Purpose: Player has just left the game
  522. //-----------------------------------------------------------------------------
  523. void CHL2MPRules::ClientDisconnected( edict_t *pClient )
  524. {
  525. #ifndef CLIENT_DLL
  526. // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" );
  527. CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
  528. if ( pPlayer )
  529. {
  530. // Remove the player from his team
  531. if ( pPlayer->GetTeam() )
  532. {
  533. pPlayer->GetTeam()->RemovePlayer( pPlayer );
  534. }
  535. }
  536. BaseClass::ClientDisconnected( pClient );
  537. #endif
  538. }
  539. //=========================================================
  540. // Deathnotice.
  541. //=========================================================
  542. void CHL2MPRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  543. {
  544. #ifndef CLIENT_DLL
  545. // Work out what killed the player, and send a message to all clients about it
  546. const char *killer_weapon_name = "world"; // by default, the player is killed by the world
  547. int killer_ID = 0;
  548. // Find the killer & the scorer
  549. CBaseEntity *pInflictor = info.GetInflictor();
  550. CBaseEntity *pKiller = info.GetAttacker();
  551. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
  552. // Custom kill type?
  553. if ( info.GetDamageCustom() )
  554. {
  555. killer_weapon_name = GetDamageCustomString( info );
  556. if ( pScorer )
  557. {
  558. killer_ID = pScorer->GetUserID();
  559. }
  560. }
  561. else
  562. {
  563. // Is the killer a client?
  564. if ( pScorer )
  565. {
  566. killer_ID = pScorer->GetUserID();
  567. if ( pInflictor )
  568. {
  569. if ( pInflictor == pScorer )
  570. {
  571. // If the inflictor is the killer, then it must be their current weapon doing the damage
  572. if ( pScorer->GetActiveWeapon() )
  573. {
  574. killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname();
  575. }
  576. }
  577. else
  578. {
  579. killer_weapon_name = pInflictor->GetClassname(); // it's just that easy
  580. }
  581. }
  582. }
  583. else
  584. {
  585. killer_weapon_name = pInflictor->GetClassname();
  586. }
  587. // strip the NPC_* or weapon_* from the inflictor's classname
  588. if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 )
  589. {
  590. killer_weapon_name += 7;
  591. }
  592. else if ( strncmp( killer_weapon_name, "npc_", 4 ) == 0 )
  593. {
  594. killer_weapon_name += 4;
  595. }
  596. else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 )
  597. {
  598. killer_weapon_name += 5;
  599. }
  600. else if ( strstr( killer_weapon_name, "physics" ) )
  601. {
  602. killer_weapon_name = "physics";
  603. }
  604. if ( strcmp( killer_weapon_name, "prop_combine_ball" ) == 0 )
  605. {
  606. killer_weapon_name = "combine_ball";
  607. }
  608. else if ( strcmp( killer_weapon_name, "grenade_ar2" ) == 0 )
  609. {
  610. killer_weapon_name = "smg1_grenade";
  611. }
  612. else if ( strcmp( killer_weapon_name, "satchel" ) == 0 || strcmp( killer_weapon_name, "tripmine" ) == 0)
  613. {
  614. killer_weapon_name = "slam";
  615. }
  616. }
  617. IGameEvent *event = gameeventmanager->CreateEvent( "player_death" );
  618. if( event )
  619. {
  620. event->SetInt("userid", pVictim->GetUserID() );
  621. event->SetInt("attacker", killer_ID );
  622. event->SetString("weapon", killer_weapon_name );
  623. event->SetInt( "priority", 7 );
  624. gameeventmanager->FireEvent( event );
  625. }
  626. #endif
  627. }
  628. void CHL2MPRules::ClientSettingsChanged( CBasePlayer *pPlayer )
  629. {
  630. #ifndef CLIENT_DLL
  631. CHL2MP_Player *pHL2Player = ToHL2MPPlayer( pPlayer );
  632. if ( pHL2Player == NULL )
  633. return;
  634. const char *pCurrentModel = modelinfo->GetModelName( pPlayer->GetModel() );
  635. const char *szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_playermodel" );
  636. //If we're different.
  637. if ( stricmp( szModelName, pCurrentModel ) )
  638. {
  639. //Too soon, set the cvar back to what it was.
  640. //Note: this will make this function be called again
  641. //but since our models will match it'll just skip this whole dealio.
  642. if ( pHL2Player->GetNextModelChangeTime() >= gpGlobals->curtime )
  643. {
  644. char szReturnString[512];
  645. Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pCurrentModel );
  646. engine->ClientCommand ( pHL2Player->edict(), szReturnString );
  647. Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch.\n", (int)(pHL2Player->GetNextModelChangeTime() - gpGlobals->curtime) );
  648. ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString );
  649. return;
  650. }
  651. if ( HL2MPRules()->IsTeamplay() == false )
  652. {
  653. pHL2Player->SetPlayerModel();
  654. const char *pszCurrentModelName = modelinfo->GetModelName( pHL2Player->GetModel() );
  655. char szReturnString[128];
  656. Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName );
  657. ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString );
  658. }
  659. else
  660. {
  661. if ( Q_stristr( szModelName, "models/human") )
  662. {
  663. pHL2Player->ChangeTeam( TEAM_REBELS );
  664. }
  665. else
  666. {
  667. pHL2Player->ChangeTeam( TEAM_COMBINE );
  668. }
  669. }
  670. }
  671. if ( sv_report_client_settings.GetInt() == 1 )
  672. {
  673. UTIL_LogPrintf( "\"%s\" cl_cmdrate = \"%s\"\n", pHL2Player->GetPlayerName(), engine->GetClientConVarValue( pHL2Player->entindex(), "cl_cmdrate" ));
  674. }
  675. BaseClass::ClientSettingsChanged( pPlayer );
  676. #endif
  677. }
  678. int CHL2MPRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
  679. {
  680. #ifndef CLIENT_DLL
  681. // half life multiplay has a simple concept of Player Relationships.
  682. // you are either on another player's team, or you are not.
  683. if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false )
  684. return GR_NOTTEAMMATE;
  685. if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
  686. {
  687. return GR_TEAMMATE;
  688. }
  689. #endif
  690. return GR_NOTTEAMMATE;
  691. }
  692. const char *CHL2MPRules::GetGameDescription( void )
  693. {
  694. if ( IsTeamplay() )
  695. return "Team Deathmatch";
  696. return "Deathmatch";
  697. }
  698. bool CHL2MPRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer )
  699. {
  700. return true;
  701. }
  702. float CHL2MPRules::GetMapRemainingTime()
  703. {
  704. // if timelimit is disabled, return 0
  705. if ( mp_timelimit.GetInt() <= 0 )
  706. return 0;
  707. // timelimit is in minutes
  708. float timeleft = (m_flGameStartTime + mp_timelimit.GetInt() * 60.0f ) - gpGlobals->curtime;
  709. return timeleft;
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose:
  713. //-----------------------------------------------------------------------------
  714. void CHL2MPRules::Precache( void )
  715. {
  716. CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" );
  717. }
  718. bool CHL2MPRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
  719. {
  720. if ( collisionGroup0 > collisionGroup1 )
  721. {
  722. // swap so that lowest is always first
  723. V_swap(collisionGroup0,collisionGroup1);
  724. }
  725. if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
  726. collisionGroup1 == COLLISION_GROUP_WEAPON )
  727. {
  728. return false;
  729. }
  730. return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
  731. }
  732. bool CHL2MPRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  733. {
  734. #ifndef CLIENT_DLL
  735. if( BaseClass::ClientCommand( pEdict, args ) )
  736. return true;
  737. CHL2MP_Player *pPlayer = (CHL2MP_Player *) pEdict;
  738. if ( pPlayer->ClientCommand( args ) )
  739. return true;
  740. #endif
  741. return false;
  742. }
  743. // shared ammo definition
  744. // JAY: Trying to make a more physical bullet response
  745. #define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
  746. #define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
  747. // exaggerate all of the forces, but use real numbers to keep them consistent
  748. #define BULLET_IMPULSE_EXAGGERATION 3.5
  749. // convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
  750. #define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
  751. CAmmoDef *GetAmmoDef()
  752. {
  753. static CAmmoDef def;
  754. static bool bInitted = false;
  755. if ( !bInitted )
  756. {
  757. bInitted = true;
  758. def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 60, BULLET_IMPULSE(200, 1225), 0 );
  759. def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, 3, 0, 0 );
  760. def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 150, BULLET_IMPULSE(200, 1225), 0 );
  761. def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 225, BULLET_IMPULSE(200, 1225), 0 );
  762. def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 12, BULLET_IMPULSE(800, 5000), 0 );
  763. def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, 0, 0, 10, BULLET_IMPULSE(800, 8000), 0 );
  764. def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, 0, 0, 30, BULLET_IMPULSE(400, 1200), 0 );
  765. def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 );
  766. def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 );
  767. def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 );
  768. def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 );
  769. }
  770. return &def;
  771. }
  772. #ifdef CLIENT_DLL
  773. ConVar cl_autowepswitch(
  774. "cl_autowepswitch",
  775. "1",
  776. FCVAR_ARCHIVE | FCVAR_USERINFO,
  777. "Automatically switch to picked up weapons (if more powerful)" );
  778. #else
  779. #ifdef DEBUG
  780. // Handler for the "bot" command.
  781. void Bot_f()
  782. {
  783. // Look at -count.
  784. int count = 1;
  785. count = clamp( count, 1, 16 );
  786. int iTeam = TEAM_COMBINE;
  787. // Look at -frozen.
  788. bool bFrozen = false;
  789. // Ok, spawn all the bots.
  790. while ( --count >= 0 )
  791. {
  792. BotPutInServer( bFrozen, iTeam );
  793. }
  794. }
  795. ConCommand cc_Bot( "bot", Bot_f, "Add a bot.", FCVAR_CHEAT );
  796. #endif
  797. bool CHL2MPRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
  798. {
  799. if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() )
  800. {
  801. // Player has an active item, so let's check cl_autowepswitch.
  802. const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" );
  803. if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 )
  804. {
  805. return false;
  806. }
  807. }
  808. return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon );
  809. }
  810. #endif
  811. #ifndef CLIENT_DLL
  812. void CHL2MPRules::RestartGame()
  813. {
  814. // bounds check
  815. if ( mp_timelimit.GetInt() < 0 )
  816. {
  817. mp_timelimit.SetValue( 0 );
  818. }
  819. m_flGameStartTime = gpGlobals->curtime;
  820. if ( !IsFinite( m_flGameStartTime.Get() ) )
  821. {
  822. Warning( "Trying to set a NaN game start time\n" );
  823. m_flGameStartTime.GetForModify() = 0.0f;
  824. }
  825. CleanUpMap();
  826. // now respawn all players
  827. for (int i = 1; i <= gpGlobals->maxClients; i++ )
  828. {
  829. CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i );
  830. if ( !pPlayer )
  831. continue;
  832. if ( pPlayer->GetActiveWeapon() )
  833. {
  834. pPlayer->GetActiveWeapon()->Holster();
  835. }
  836. pPlayer->RemoveAllItems( true );
  837. respawn( pPlayer, false );
  838. pPlayer->Reset();
  839. }
  840. // Respawn entities (glass, doors, etc..)
  841. CTeam *pRebels = GetGlobalTeam( TEAM_REBELS );
  842. CTeam *pCombine = GetGlobalTeam( TEAM_COMBINE );
  843. if ( pRebels )
  844. {
  845. pRebels->SetScore( 0 );
  846. }
  847. if ( pCombine )
  848. {
  849. pCombine->SetScore( 0 );
  850. }
  851. m_flIntermissionEndTime = 0;
  852. m_flRestartGameTime = 0.0;
  853. m_bCompleteReset = false;
  854. IGameEvent * event = gameeventmanager->CreateEvent( "round_start" );
  855. if ( event )
  856. {
  857. event->SetInt("fraglimit", 0 );
  858. event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
  859. event->SetString("objective","DEATHMATCH");
  860. gameeventmanager->FireEvent( event );
  861. }
  862. }
  863. void CHL2MPRules::CleanUpMap()
  864. {
  865. // Recreate all the map entities from the map data (preserving their indices),
  866. // then remove everything else except the players.
  867. // Get rid of all entities except players.
  868. CBaseEntity *pCur = gEntList.FirstEnt();
  869. while ( pCur )
  870. {
  871. CBaseHL2MPCombatWeapon *pWeapon = dynamic_cast< CBaseHL2MPCombatWeapon* >( pCur );
  872. // Weapons with owners don't want to be removed..
  873. if ( pWeapon )
  874. {
  875. if ( !pWeapon->GetPlayerOwner() )
  876. {
  877. UTIL_Remove( pCur );
  878. }
  879. }
  880. // remove entities that has to be restored on roundrestart (breakables etc)
  881. else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) )
  882. {
  883. UTIL_Remove( pCur );
  884. }
  885. pCur = gEntList.NextEnt( pCur );
  886. }
  887. // Really remove the entities so we can have access to their slots below.
  888. gEntList.CleanupDeleteList();
  889. // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that
  890. // could kill respawning CTs
  891. g_EventQueue.Clear();
  892. // Now reload the map entities.
  893. class CHL2MPMapEntityFilter : public IMapEntityFilter
  894. {
  895. public:
  896. virtual bool ShouldCreateEntity( const char *pClassname )
  897. {
  898. // Don't recreate the preserved entities.
  899. if ( !FindInList( s_PreserveEnts, pClassname ) )
  900. {
  901. return true;
  902. }
  903. else
  904. {
  905. // Increment our iterator since it's not going to call CreateNextEntity for this ent.
  906. if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
  907. m_iIterator = g_MapEntityRefs.Next( m_iIterator );
  908. return false;
  909. }
  910. }
  911. virtual CBaseEntity* CreateNextEntity( const char *pClassname )
  912. {
  913. if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
  914. {
  915. // This shouldn't be possible. When we loaded the map, it should have used
  916. // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list
  917. // with the same list of entities we're referring to here.
  918. Assert( false );
  919. return NULL;
  920. }
  921. else
  922. {
  923. CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
  924. m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
  925. if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) )
  926. {
  927. // Doh! The entity was delete and its slot was reused.
  928. // Just use any old edict slot. This case sucks because we lose the baseline.
  929. return CreateEntityByName( pClassname );
  930. }
  931. else
  932. {
  933. // Cool, the slot where this entity was is free again (most likely, the entity was
  934. // freed above). Now create an entity with this specific index.
  935. return CreateEntityByName( pClassname, ref.m_iEdict );
  936. }
  937. }
  938. }
  939. public:
  940. int m_iIterator; // Iterator into g_MapEntityRefs.
  941. };
  942. CHL2MPMapEntityFilter filter;
  943. filter.m_iIterator = g_MapEntityRefs.Head();
  944. // DO NOT CALL SPAWN ON info_node ENTITIES!
  945. MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
  946. }
  947. void CHL2MPRules::CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg )
  948. {
  949. if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_ready_signal.GetString() ) )
  950. {
  951. if( !pPlayer->IsReady() )
  952. {
  953. pPlayer->SetReady( true );
  954. }
  955. }
  956. }
  957. void CHL2MPRules::CheckRestartGame( void )
  958. {
  959. // Restart the game if specified by the server
  960. int iRestartDelay = mp_restartgame.GetInt();
  961. if ( iRestartDelay > 0 )
  962. {
  963. if ( iRestartDelay > 60 )
  964. iRestartDelay = 60;
  965. // let the players know
  966. char strRestartDelay[64];
  967. Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
  968. UTIL_ClientPrintAll( HUD_PRINTCENTER, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
  969. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
  970. m_flRestartGameTime = gpGlobals->curtime + iRestartDelay;
  971. m_bCompleteReset = true;
  972. mp_restartgame.SetValue( 0 );
  973. }
  974. if( mp_readyrestart.GetBool() )
  975. {
  976. m_bAwaitingReadyRestart = true;
  977. m_bHeardAllPlayersReady = false;
  978. const char *pszReadyString = mp_ready_signal.GetString();
  979. // Don't let them put anything malicious in there
  980. if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 )
  981. {
  982. pszReadyString = "ready";
  983. }
  984. IGameEvent *event = gameeventmanager->CreateEvent( "hl2mp_ready_restart" );
  985. if ( event )
  986. gameeventmanager->FireEvent( event );
  987. mp_readyrestart.SetValue( 0 );
  988. // cancel any restart round in progress
  989. m_flRestartGameTime = -1;
  990. }
  991. }
  992. void CHL2MPRules::CheckAllPlayersReady( void )
  993. {
  994. for (int i = 1; i <= gpGlobals->maxClients; i++ )
  995. {
  996. CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i );
  997. if ( !pPlayer )
  998. continue;
  999. if ( !pPlayer->IsReady() )
  1000. return;
  1001. }
  1002. m_bHeardAllPlayersReady = true;
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // Purpose:
  1006. //-----------------------------------------------------------------------------
  1007. const char *CHL2MPRules::GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer )
  1008. {
  1009. if ( !pPlayer ) // dedicated server output
  1010. {
  1011. return NULL;
  1012. }
  1013. const char *pszFormat = NULL;
  1014. // team only
  1015. if ( bTeamOnly == TRUE )
  1016. {
  1017. if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
  1018. {
  1019. pszFormat = "HL2MP_Chat_Spec";
  1020. }
  1021. else
  1022. {
  1023. const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
  1024. if ( chatLocation && *chatLocation )
  1025. {
  1026. pszFormat = "HL2MP_Chat_Team_Loc";
  1027. }
  1028. else
  1029. {
  1030. pszFormat = "HL2MP_Chat_Team";
  1031. }
  1032. }
  1033. }
  1034. // everyone
  1035. else
  1036. {
  1037. if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  1038. {
  1039. pszFormat = "HL2MP_Chat_All";
  1040. }
  1041. else
  1042. {
  1043. pszFormat = "HL2MP_Chat_AllSpec";
  1044. }
  1045. }
  1046. return pszFormat;
  1047. }
  1048. #endif