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.

908 lines
28 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "gamerules.h"
  8. #include "ammodef.h"
  9. #include "tier0/vprof.h"
  10. #include "keyvalues.h"
  11. #include "usermessages.h"
  12. #ifdef CLIENT_DLL
  13. #include "usermessages.h"
  14. #else
  15. #include "player.h"
  16. #include "teamplay_gamerules.h"
  17. #include "game.h"
  18. #include "entitylist.h"
  19. #include "basecombatweapon.h"
  20. #include "voice_gamemgr.h"
  21. #include "globalstate.h"
  22. #include "player_resource.h"
  23. #include "GameStats.h"
  24. #endif
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. ConVar g_Language( "g_Language", "0", FCVAR_REPLICATED );
  28. ConVar sk_autoaim_mode( "sk_autoaim_mode", "1", FCVAR_ARCHIVE | FCVAR_REPLICATED );
  29. static CViewVectors g_DefaultViewVectors(
  30. Vector( 0, 0, 64 ), //VEC_VIEW (m_vView)
  31. Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin)
  32. Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax)
  33. Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin)
  34. Vector( 16, 16, 36 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax)
  35. Vector( 0, 0, 28 ), //VEC_DUCK_VIEW (m_vDuckView)
  36. Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin)
  37. Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax)
  38. Vector( 0, 0, 14 ) //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight)
  39. );
  40. #ifdef PORTAL2
  41. ConVar sv_portal_players( "sv_portal_players", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_HIDDEN );
  42. bool IsGameRulesMultiplayer()
  43. {
  44. return ( sv_portal_players.GetInt() > 1 );
  45. }
  46. #endif
  47. // ------------------------------------------------------------------------------------ //
  48. // CGameRulesProxy implementation.
  49. // ------------------------------------------------------------------------------------ //
  50. CGameRulesProxy *CGameRulesProxy::s_pGameRulesProxy = NULL;
  51. IMPLEMENT_NETWORKCLASS_ALIASED( GameRulesProxy, DT_GameRulesProxy )
  52. // Don't send any of the CBaseEntity stuff..
  53. BEGIN_NETWORK_TABLE_NOBASE( CGameRulesProxy, DT_GameRulesProxy )
  54. END_NETWORK_TABLE()
  55. CGameRulesProxy::CGameRulesProxy()
  56. {
  57. // allow map placed proxy entities to overwrite the static one
  58. if ( s_pGameRulesProxy )
  59. {
  60. #ifndef CLIENT_DLL
  61. UTIL_Remove( s_pGameRulesProxy );
  62. #endif
  63. s_pGameRulesProxy = NULL;
  64. }
  65. s_pGameRulesProxy = this;
  66. }
  67. CGameRulesProxy::~CGameRulesProxy()
  68. {
  69. if ( s_pGameRulesProxy == this )
  70. {
  71. s_pGameRulesProxy = NULL;
  72. }
  73. }
  74. int CGameRulesProxy::UpdateTransmitState()
  75. {
  76. #ifndef CLIENT_DLL
  77. // ALWAYS transmit to all clients.
  78. return SetTransmitState( FL_EDICT_ALWAYS );
  79. #else
  80. return 0;
  81. #endif
  82. }
  83. void CGameRulesProxy::NotifyNetworkStateChanged()
  84. {
  85. if ( s_pGameRulesProxy )
  86. s_pGameRulesProxy->NetworkStateChanged();
  87. }
  88. ConVar old_radius_damage( "old_radiusdamage", "0.0", FCVAR_REPLICATED );
  89. #ifdef CLIENT_DLL //{
  90. bool CGameRules::IsBonusChallengeTimeBased( void )
  91. {
  92. return true;
  93. }
  94. CGameRules::CGameRules() : CAutoGameSystemPerFrame( "CGameRules" )
  95. {
  96. Assert( !g_pGameRules );
  97. g_pGameRules = this;
  98. }
  99. #else //}{
  100. // In tf_gamerules.cpp or hl_gamerules.cpp.
  101. extern IVoiceGameMgrHelper *g_pVoiceGameMgrHelper;
  102. CGameRules* g_pGameRules = NULL;
  103. extern bool g_fGameOver;
  104. //-----------------------------------------------------------------------------
  105. // constructor, destructor
  106. //-----------------------------------------------------------------------------
  107. CGameRules::CGameRules() : CAutoGameSystemPerFrame( "CGameRules" )
  108. {
  109. Assert( !g_pGameRules );
  110. g_pGameRules = this;
  111. GetVoiceGameMgr()->Init( g_pVoiceGameMgrHelper, gpGlobals->maxClients );
  112. ClearMultiDamage();
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Precache game-specific resources
  116. //-----------------------------------------------------------------------------
  117. void CGameRules::Precache( void )
  118. {
  119. // Used by particle property
  120. PrecacheEffect( "ParticleEffect" );
  121. PrecacheEffect( "ParticleEffectStop" );
  122. // Used by default impact system
  123. PrecacheEffect( "GlassImpact" );
  124. PrecacheEffect( "Impact" );
  125. PrecacheEffect( "RagdollImpact" );
  126. PrecacheEffect( "gunshotsplash" );
  127. PrecacheEffect( "TracerSound" );
  128. PrecacheEffect( "Tracer" );
  129. // Used by physics impacts
  130. PrecacheEffect( "watersplash" );
  131. PrecacheEffect( "waterripple" );
  132. // Used by UTIL_BloodImpact, which is used in many places
  133. PrecacheEffect( "bloodimpact" );
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose: Return true if the specified player can carry any more of the ammo type
  137. //-----------------------------------------------------------------------------
  138. bool CGameRules::CanHaveAmmo( CBaseCombatCharacter *pPlayer, int iAmmoIndex )
  139. {
  140. if ( iAmmoIndex > -1 )
  141. {
  142. // Get the max carrying capacity for this ammo
  143. int iMaxCarry = GetAmmoDef()->MaxCarry( iAmmoIndex, pPlayer );
  144. // Does the player have room for more of this type of ammo?
  145. if ( pPlayer->GetAmmoCount( iAmmoIndex ) < iMaxCarry )
  146. return true;
  147. }
  148. return false;
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: Return true if the specified player can carry any more of the ammo type
  152. //-----------------------------------------------------------------------------
  153. bool CGameRules::CanHaveAmmo( CBaseCombatCharacter *pPlayer, const char *szName )
  154. {
  155. return CanHaveAmmo( pPlayer, GetAmmoDef()->Index(szName) );
  156. }
  157. //=========================================================
  158. //=========================================================
  159. CBaseEntity *CGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
  160. {
  161. CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
  162. Assert( pSpawnSpot );
  163. if ( pSpawnSpot == NULL )
  164. return NULL;
  165. pPlayer->SetLocalOrigin( pSpawnSpot->GetAbsOrigin() + Vector(0,0,1) );
  166. pPlayer->SetAbsVelocity( vec3_origin );
  167. pPlayer->SetLocalAngles( pSpawnSpot->GetLocalAngles() );
  168. pPlayer->m_Local.m_viewPunchAngle = vec3_angle;
  169. pPlayer->m_Local.m_aimPunchAngle = vec3_angle;
  170. pPlayer->m_Local.m_aimPunchAngleVel = vec3_angle;
  171. pPlayer->SnapEyeAngles( pSpawnSpot->GetLocalAngles() );
  172. return pSpawnSpot;
  173. }
  174. // checks if the spot is clear of players
  175. bool CGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer )
  176. {
  177. CBaseEntity *ent = NULL;
  178. if ( !pSpot->IsTriggered( pPlayer ) )
  179. {
  180. return false;
  181. }
  182. for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
  183. {
  184. // if ent is a client, don't spawn on 'em
  185. if ( ent->IsPlayer() && ent != pPlayer )
  186. return false;
  187. }
  188. return true;
  189. }
  190. //=========================================================
  191. //=========================================================
  192. bool CGameRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
  193. {
  194. /*
  195. if ( pWeapon->m_pszAmmo1 )
  196. {
  197. if ( !CanHaveAmmo( pPlayer, pWeapon->m_iPrimaryAmmoType ) )
  198. {
  199. // we can't carry anymore ammo for this gun. We can only
  200. // have the gun if we aren't already carrying one of this type
  201. if ( pPlayer->Weapon_OwnsThisType( pWeapon ) )
  202. {
  203. return FALSE;
  204. }
  205. }
  206. }
  207. else
  208. {
  209. // weapon doesn't use ammo, don't take another if you already have it.
  210. if ( pPlayer->Weapon_OwnsThisType( pWeapon ) )
  211. {
  212. return FALSE;
  213. }
  214. }
  215. */
  216. // note: will fall through to here if GetItemInfo doesn't fill the struct!
  217. return TRUE;
  218. }
  219. //=========================================================
  220. // load the SkillData struct with the proper values based on the skill level.
  221. //=========================================================
  222. void CGameRules::RefreshSkillData ( bool forceUpdate )
  223. {
  224. #ifndef CLIENT_DLL
  225. if ( !forceUpdate )
  226. {
  227. if ( GlobalEntity_IsInTable( "skill.cfg" ) )
  228. return;
  229. }
  230. GlobalEntity_Add( "skill.cfg", STRING(gpGlobals->mapname), GLOBAL_ON );
  231. char szExec[256];
  232. ConVarRef skill( "skill" );
  233. SetSkillLevel( skill.IsValid() ? skill.GetInt() : 1 );
  234. #ifdef HL2_DLL
  235. // HL2 current only uses one skill config file that represents MEDIUM skill level and
  236. // synthesizes EASY and HARD. (sjb)
  237. Q_snprintf( szExec,sizeof(szExec), "exec skill_manifest.cfg\n" );
  238. engine->ServerCommand( szExec );
  239. engine->ServerExecute();
  240. #else
  241. Q_snprintf( szExec,sizeof(szExec), "exec skill%d.cfg\n", GetSkillLevel() );
  242. engine->ServerCommand( szExec );
  243. engine->ServerExecute();
  244. #endif // HL2_DLL
  245. #endif // CLIENT_DLL
  246. }
  247. //-----------------------------------------------------------------------------
  248. //-----------------------------------------------------------------------------
  249. bool IsExplosionTraceBlocked( trace_t *ptr )
  250. {
  251. if( ptr->DidHitWorld() )
  252. return true;
  253. if( ptr->m_pEnt == NULL )
  254. return false;
  255. if( ptr->m_pEnt->GetMoveType() == MOVETYPE_PUSH )
  256. {
  257. // All doors are push, but not all things that push are doors. This
  258. // narrows the search before we start to do classname compares.
  259. if( FClassnameIs(ptr->m_pEnt, "prop_door_rotating") ||
  260. FClassnameIs(ptr->m_pEnt, "func_door") ||
  261. FClassnameIs(ptr->m_pEnt, "func_door_rotating") )
  262. return true;
  263. }
  264. return false;
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Default implementation of radius damage
  268. //-----------------------------------------------------------------------------
  269. #define ROBUST_RADIUS_PROBE_DIST 16.0f // If a solid surface blocks the explosion, this is how far to creep along the surface looking for another way to the target
  270. void CGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore )
  271. {
  272. const int MASK_RADIUS_DAMAGE = MASK_SHOT&(~CONTENTS_HITBOX);
  273. CBaseEntity *pEntity = NULL;
  274. trace_t tr;
  275. float flAdjustedDamage, falloff;
  276. Vector vecSpot;
  277. Vector vecSrc = vecSrcIn;
  278. if ( flRadius )
  279. falloff = info.GetDamage() / flRadius;
  280. else
  281. falloff = 1.0;
  282. int bInWater = (UTIL_PointContents ( vecSrc, MASK_WATER ) & MASK_WATER) ? true : false;
  283. #ifdef HL2_DLL
  284. if( bInWater )
  285. {
  286. // Only muffle the explosion if deeper than 2 feet in water.
  287. if( !(UTIL_PointContents(vecSrc + Vector(0, 0, 24), MASK_WATER) & MASK_WATER) )
  288. {
  289. bInWater = false;
  290. }
  291. }
  292. #endif // HL2_DLL
  293. vecSrc.z += 1;// in case grenade is lying on the ground
  294. float flHalfRadiusSqr = Square( flRadius / 2.0f );
  295. // iterate on all entities in the vicinity.
  296. for ( CEntitySphereQuery sphere( vecSrc, flRadius ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
  297. {
  298. // This value is used to scale damage when the explosion is blocked by some other object.
  299. float flBlockedDamagePercent = 0.0f;
  300. if ( pEntity == pEntityIgnore )
  301. continue;
  302. if ( pEntity->m_takedamage == DAMAGE_NO )
  303. continue;
  304. // UNDONE: this should check a damage mask, not an ignore
  305. if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  306. {// houndeyes don't hurt other houndeyes with their attack
  307. continue;
  308. }
  309. // blast's don't tavel into or out of water
  310. if (bInWater && pEntity->GetWaterLevel() == WL_NotInWater)
  311. continue;
  312. if (!bInWater && pEntity->GetWaterLevel() == WL_Eyes)
  313. continue;
  314. // Check that the explosion can 'see' this entity.
  315. vecSpot = pEntity->BodyTarget( vecSrc, false );
  316. UTIL_TraceLine( vecSrc, vecSpot, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
  317. if( old_radius_damage.GetBool() )
  318. {
  319. if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
  320. continue;
  321. }
  322. else
  323. {
  324. if ( tr.fraction != 1.0 )
  325. {
  326. if ( IsExplosionTraceBlocked(&tr) )
  327. {
  328. if( ShouldUseRobustRadiusDamage( pEntity ) )
  329. {
  330. if( vecSpot.DistToSqr( vecSrc ) > flHalfRadiusSqr )
  331. {
  332. // Only use robust model on a target within one-half of the explosion's radius.
  333. continue;
  334. }
  335. Vector vecToTarget = vecSpot - tr.endpos;
  336. VectorNormalize( vecToTarget );
  337. // We're going to deflect the blast along the surface that
  338. // interrupted a trace from explosion to this target.
  339. Vector vecUp, vecDeflect;
  340. CrossProduct( vecToTarget, tr.plane.normal, vecUp );
  341. CrossProduct( tr.plane.normal, vecUp, vecDeflect );
  342. VectorNormalize( vecDeflect );
  343. // Trace along the surface that intercepted the blast...
  344. UTIL_TraceLine( tr.endpos, tr.endpos + vecDeflect * ROBUST_RADIUS_PROBE_DIST, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
  345. //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 255, 0, false, 10 );
  346. // ...to see if there's a nearby edge that the explosion would 'spill over' if the blast were fully simulated.
  347. UTIL_TraceLine( tr.endpos, vecSpot, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
  348. //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 10 );
  349. if( tr.fraction != 1.0 && tr.DidHitWorld() )
  350. {
  351. // Still can't reach the target.
  352. continue;
  353. }
  354. // else fall through
  355. }
  356. else
  357. {
  358. continue;
  359. }
  360. }
  361. // UNDONE: Probably shouldn't let children block parents either? Or maybe those guys should set their owner if they want this behavior?
  362. // HL2 - Dissolve damage is not reduced by interposing non-world objects
  363. if( tr.m_pEnt && tr.m_pEnt != pEntity && tr.m_pEnt->GetOwnerEntity() != pEntity )
  364. {
  365. // Some entity was hit by the trace, meaning the explosion does not have clear
  366. // line of sight to the entity that it's trying to hurt. If the world is also
  367. // blocking, we do no damage.
  368. CBaseEntity *pBlockingEntity = tr.m_pEnt;
  369. //Msg( "%s may be blocked by %s...", pEntity->GetClassname(), pBlockingEntity->GetClassname() );
  370. UTIL_TraceLine( vecSrc, vecSpot, CONTENTS_SOLID, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
  371. if( tr.fraction != 1.0 )
  372. {
  373. continue;
  374. }
  375. // Now, if the interposing object is physics, block some explosion force based on its mass.
  376. if( pBlockingEntity->VPhysicsGetObject() )
  377. {
  378. const float MASS_ABSORB_ALL_DAMAGE = 350.0f;
  379. float flMass = pBlockingEntity->VPhysicsGetObject()->GetMass();
  380. float scale = flMass / MASS_ABSORB_ALL_DAMAGE;
  381. // Absorbed all the damage.
  382. if( scale >= 1.0f )
  383. {
  384. continue;
  385. }
  386. ASSERT( scale > 0.0f );
  387. flBlockedDamagePercent = scale;
  388. //Msg(" Object (%s) weighing %fkg blocked %f percent of explosion damage\n", pBlockingEntity->GetClassname(), flMass, scale * 100.0f);
  389. }
  390. else
  391. {
  392. // Some object that's not the world and not physics. Generically block 25% damage
  393. flBlockedDamagePercent = 0.25f;
  394. }
  395. }
  396. }
  397. }
  398. // decrease damage for an ent that's farther from the bomb.
  399. float flDistanceToEnt = ( vecSrc - tr.endpos ).Length();
  400. flAdjustedDamage = flDistanceToEnt * falloff;
  401. flAdjustedDamage = info.GetDamage() - flAdjustedDamage;
  402. if ( flAdjustedDamage <= 0 )
  403. {
  404. continue;
  405. }
  406. // the explosion can 'see' this entity, so hurt them!
  407. if (tr.startsolid)
  408. {
  409. // if we're stuck inside them, fixup the position and distance
  410. tr.endpos = vecSrc;
  411. tr.fraction = 0.0;
  412. }
  413. CTakeDamageInfo adjustedInfo = info;
  414. //Msg("%s: Blocked damage: %f percent (in:%f out:%f)\n", pEntity->GetClassname(), flBlockedDamagePercent * 100, flAdjustedDamage, flAdjustedDamage - (flAdjustedDamage * flBlockedDamagePercent) );
  415. adjustedInfo.SetRadius( flRadius );
  416. adjustedInfo.SetDamage( flAdjustedDamage - (flAdjustedDamage * flBlockedDamagePercent) );
  417. // Now make a consideration for skill level!
  418. if( info.GetAttacker() && info.GetAttacker()->IsPlayer() && pEntity->IsNPC() )
  419. {
  420. // An explosion set off by the player is harming an NPC. Adjust damage accordingly.
  421. adjustedInfo.AdjustPlayerDamageInflictedForSkillLevel();
  422. }
  423. Vector dir = vecSpot - vecSrc;
  424. VectorNormalize( dir );
  425. // If we don't have a damage force, manufacture one
  426. if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
  427. {
  428. if ( !( adjustedInfo.GetDamageType() & DMG_PREVENT_PHYSICS_FORCE ) )
  429. {
  430. CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc );
  431. }
  432. }
  433. else
  434. {
  435. // Assume the force passed in is the maximum force. Decay it based on falloff.
  436. float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
  437. adjustedInfo.SetDamageForce( dir * flForce );
  438. adjustedInfo.SetDamagePosition( vecSrc );
  439. }
  440. if ( tr.fraction != 1.0 && pEntity == tr.m_pEnt )
  441. {
  442. ClearMultiDamage( );
  443. pEntity->DispatchTraceAttack( adjustedInfo, dir, &tr );
  444. ApplyMultiDamage();
  445. }
  446. else
  447. {
  448. pEntity->TakeDamage( adjustedInfo );
  449. }
  450. // Now hit all triggers along the way that respond to damage...
  451. pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, tr.endpos, dir );
  452. #if defined( GAME_DLL ) && !defined( _GAMECONSOLE )
  453. if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && ToBaseCombatCharacter( tr.m_pEnt ) )
  454. {
  455. // This is a total hack!!!
  456. bool bIsPrimary = true;
  457. CBasePlayer *player = ToBasePlayer( info.GetAttacker() );
  458. CBaseCombatWeapon *pWeapon = player->GetActiveWeapon();
  459. if ( pWeapon && FClassnameIs( pWeapon, "weapon_smg1" ) )
  460. {
  461. bIsPrimary = false;
  462. }
  463. gamestats->Event_WeaponHit( player, bIsPrimary, (pWeapon != NULL) ? player->GetActiveWeapon()->GetClassname() : "NULL", info );
  464. }
  465. #endif
  466. }
  467. }
  468. bool CGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  469. {
  470. if( pEdict->IsPlayer() )
  471. {
  472. if( GetVoiceGameMgr()->ClientCommand( static_cast<CBasePlayer*>(pEdict), args ) )
  473. return true;
  474. }
  475. return false;
  476. }
  477. void CGameRules::FrameUpdatePostEntityThink()
  478. {
  479. VPROF( "CGameRules::FrameUpdatePostEntityThink" );
  480. SNPROF( "CGameRules::FrameUpdatePostEntityThink" );
  481. Think();
  482. }
  483. // Hook into the convar from the engine
  484. ConVar skill( "skill", "1", FCVAR_ARCHIVE );
  485. void CGameRules::Think()
  486. {
  487. GetVoiceGameMgr()->Update( gpGlobals->frametime );
  488. SetSkillLevel( skill.GetInt() );
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose: Called at the end of GameFrame (i.e. after all game logic has run this frame)
  492. //-----------------------------------------------------------------------------
  493. void CGameRules::EndGameFrame( void )
  494. {
  495. // If you hit this assert, it means something called AddMultiDamage() and didn't ApplyMultiDamage().
  496. // The g_MultiDamage.m_hAttacker & g_MultiDamage.m_hInflictor should give help you figure out the culprit.
  497. Assert( g_MultiDamage.IsClear() );
  498. if ( !g_MultiDamage.IsClear() )
  499. {
  500. Warning("Unapplied multidamage left in the system:\nTarget: %s\nInflictor: %s\nAttacker: %s\nDamage: %.2f\n",
  501. g_MultiDamage.GetTarget()->GetDebugName(),
  502. g_MultiDamage.GetInflictor()->GetDebugName(),
  503. g_MultiDamage.GetAttacker()->GetDebugName(),
  504. g_MultiDamage.GetDamage() );
  505. ApplyMultiDamage();
  506. }
  507. }
  508. //-----------------------------------------------------------------------------
  509. // trace line rules
  510. //-----------------------------------------------------------------------------
  511. float CGameRules::WeaponTraceEntity( CBaseEntity *pEntity, const Vector &vecStart, const Vector &vecEnd,
  512. unsigned int mask, trace_t *ptr )
  513. {
  514. UTIL_TraceEntity( pEntity, vecStart, vecEnd, mask, ptr );
  515. return 1.0f;
  516. }
  517. void CGameRules::CreateStandardEntities()
  518. {
  519. g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "player_manager", vec3_origin, vec3_angle );
  520. g_pPlayerResource->AddEFlags( EFL_KEEP_ON_RECREATE_ENTITIES );
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose: Inform client(s) they can mark the indicated achievement as completed (SERVER VERSION)
  524. // Input : filter - which client(s) to send this to
  525. // iAchievementID - The enumeration value of the achievement to mark (see TODO:Kerry, what file will have the mod's achievement enum?)
  526. //-----------------------------------------------------------------------------
  527. void CGameRules::MarkAchievement( IRecipientFilter& filter, char const *pchAchievementName )
  528. {
  529. #ifndef _GAMECONSOLE
  530. gamestats->Event_IncrementCountedStatistic( vec3_origin, pchAchievementName, 1.0f );
  531. #endif
  532. }
  533. //-----------------------------------------------------------------------------
  534. // Purpose: Wrapper allowing gamerules to change the way client-finding in PVS works
  535. //-----------------------------------------------------------------------------
  536. edict_t *CGameRules::DoFindClientInPVS( edict_t *pEdict, unsigned char *pvs, unsigned pvssize )
  537. {
  538. return UTIL_FindClientInPVSGuts( pEdict, pvs, pvssize );
  539. }
  540. #endif //} !CLIENT_DLL
  541. // ----------------------------------------------------------------------------- //
  542. // Shared CGameRules implementation.
  543. // ----------------------------------------------------------------------------- //
  544. CGameRules::~CGameRules()
  545. {
  546. Assert( g_pGameRules == this );
  547. g_pGameRules = NULL;
  548. }
  549. bool CGameRules::Init()
  550. {
  551. #ifndef CLIENT_DLL
  552. RefreshSkillData( true );
  553. #endif
  554. return true;
  555. }
  556. bool CGameRules::SwitchToNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
  557. {
  558. return false;
  559. }
  560. CBaseCombatWeapon *CGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
  561. {
  562. return NULL;
  563. }
  564. bool CGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
  565. {
  566. if ( collisionGroup0 > collisionGroup1 )
  567. {
  568. // swap so that lowest is always first
  569. V_swap(collisionGroup0,collisionGroup1);
  570. }
  571. if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
  572. collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
  573. {
  574. return false;
  575. }
  576. if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
  577. {
  578. // let debris and multiplayer objects collide
  579. return true;
  580. }
  581. // Only let projectile blocking debris collide with projectiles
  582. if ( collisionGroup0 == COLLISION_GROUP_PROJECTILE && collisionGroup1 == COLLISION_GROUP_DEBRIS_BLOCK_PROJECTILE )
  583. return true;
  584. if ( collisionGroup0 == COLLISION_GROUP_DEBRIS_BLOCK_PROJECTILE || collisionGroup1 == COLLISION_GROUP_DEBRIS_BLOCK_PROJECTILE )
  585. return false;
  586. // --------------------------------------------------------------------------
  587. // NOTE: All of this code assumes the collision groups have been sorted!!!!
  588. // NOTE: Don't change their order without rewriting this code !!!
  589. // --------------------------------------------------------------------------
  590. // Don't bother if either is in a vehicle...
  591. if (( collisionGroup0 == COLLISION_GROUP_IN_VEHICLE ) || ( collisionGroup1 == COLLISION_GROUP_IN_VEHICLE ))
  592. return false;
  593. if ( ( collisionGroup1 == COLLISION_GROUP_DOOR_BLOCKER ) && ( collisionGroup0 != COLLISION_GROUP_NPC ) )
  594. return false;
  595. if ( ( collisionGroup0 == COLLISION_GROUP_PLAYER ) && ( collisionGroup1 == COLLISION_GROUP_PASSABLE_DOOR ) )
  596. return false;
  597. if ( collisionGroup0 == COLLISION_GROUP_DEBRIS || collisionGroup0 == COLLISION_GROUP_DEBRIS_TRIGGER )
  598. {
  599. // put exceptions here, right now this will only collide with COLLISION_GROUP_NONE
  600. return false;
  601. }
  602. // Dissolving guys only collide with COLLISION_GROUP_NONE
  603. if ( (collisionGroup0 == COLLISION_GROUP_DISSOLVING) || (collisionGroup1 == COLLISION_GROUP_DISSOLVING) )
  604. {
  605. if ( collisionGroup0 != COLLISION_GROUP_NONE )
  606. return false;
  607. }
  608. // doesn't collide with other members of this group
  609. // or debris, but that's handled above
  610. if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && collisionGroup1 == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  611. return false;
  612. // This change was breaking HL2DM
  613. // Adrian: TEST! Interactive Debris doesn't collide with the player.
  614. if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && ( collisionGroup1 == COLLISION_GROUP_PLAYER_MOVEMENT || collisionGroup1 == COLLISION_GROUP_PLAYER ) )
  615. return false;
  616. #ifdef PORTAL2
  617. // Only hit something of the same group
  618. if ( collisionGroup0 == COLLISION_GROUP_CAMERA_SOLID || collisionGroup1 == COLLISION_GROUP_CAMERA_SOLID )
  619. {
  620. if ( collisionGroup0 != COLLISION_GROUP_CAMERA_SOLID || collisionGroup1 != COLLISION_GROUP_CAMERA_SOLID )
  621. return false;
  622. }
  623. // Only hit something of the same group
  624. if ( collisionGroup0 == COLLISION_GROUP_PLACEMENT_SOLID || collisionGroup1 == COLLISION_GROUP_PLACEMENT_SOLID )
  625. {
  626. if ( collisionGroup0 != COLLISION_GROUP_PLACEMENT_SOLID || collisionGroup1 != COLLISION_GROUP_PLACEMENT_SOLID )
  627. return false;
  628. }
  629. // Held objects shouldn't collide with players
  630. // BUG: Not sure if we want this in MP, intention is to not collide with the holding player, not necessarily all.
  631. if ( collisionGroup1 == COLLISION_GROUP_PLAYER_HELD && collisionGroup0 == COLLISION_GROUP_PLAYER )
  632. return false;
  633. if ( collisionGroup1 == COLLISION_GROUP_PLAYER_HELD && collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT )
  634. return false;
  635. #endif // PORTAL2
  636. if ( collisionGroup0 == COLLISION_GROUP_BREAKABLE_GLASS && collisionGroup1 == COLLISION_GROUP_BREAKABLE_GLASS )
  637. return false;
  638. // interactive objects collide with everything except debris & interactive debris
  639. if ( collisionGroup1 == COLLISION_GROUP_INTERACTIVE && collisionGroup0 != COLLISION_GROUP_NONE )
  640. return false;
  641. // Projectiles hit everything but debris, weapons, + other projectiles
  642. if ( collisionGroup1 == COLLISION_GROUP_PROJECTILE )
  643. {
  644. if ( collisionGroup0 == COLLISION_GROUP_DEBRIS ||
  645. collisionGroup0 == COLLISION_GROUP_WEAPON ||
  646. collisionGroup0 == COLLISION_GROUP_PROJECTILE )
  647. {
  648. return false;
  649. }
  650. }
  651. // Don't let vehicles collide with weapons
  652. // Don't let players collide with weapons...
  653. // Don't let NPCs collide with weapons
  654. // Weapons are triggers, too, so they should still touch because of that
  655. if ( collisionGroup1 == COLLISION_GROUP_WEAPON )
  656. {
  657. if ( collisionGroup0 == COLLISION_GROUP_VEHICLE ||
  658. collisionGroup0 == COLLISION_GROUP_PLAYER ||
  659. collisionGroup0 == COLLISION_GROUP_NPC ||
  660. collisionGroup0 == COLLISION_GROUP_WEAPON ) //don't let weapons collide with weapons, they can pile up and choke the server
  661. {
  662. return false;
  663. }
  664. }
  665. // collision with vehicle clip entity??
  666. if ( collisionGroup0 == COLLISION_GROUP_VEHICLE_CLIP || collisionGroup1 == COLLISION_GROUP_VEHICLE_CLIP )
  667. {
  668. // yes then if it's a vehicle, collide, otherwise no collision
  669. // vehicle sorts lower than vehicle clip, so must be in 0
  670. if ( collisionGroup0 == COLLISION_GROUP_VEHICLE )
  671. return true;
  672. // vehicle clip against non-vehicle, no collision
  673. return false;
  674. }
  675. return true;
  676. }
  677. const CViewVectors* CGameRules::GetViewVectors() const
  678. {
  679. return &g_DefaultViewVectors;
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose: Returns how much damage the given ammo type should do to the victim
  683. // when fired by the attacker.
  684. // Input : pAttacker - Dude what shot the gun.
  685. // pVictim - Dude what done got shot.
  686. // nAmmoType - What been shot out.
  687. // Output : How much hurt to put on dude what done got shot (pVictim).
  688. //-----------------------------------------------------------------------------
  689. float CGameRules::GetAmmoDamage( CBaseEntity *pAttacker, CBaseEntity *pVictim, int nAmmoType )
  690. {
  691. float flDamage = 0;
  692. CAmmoDef *pAmmoDef = GetAmmoDef();
  693. if ( pAttacker->IsPlayer() )
  694. {
  695. flDamage = pAmmoDef->PlrDamage( nAmmoType );
  696. }
  697. else
  698. {
  699. flDamage = pAmmoDef->NPCDamage( nAmmoType );
  700. }
  701. return flDamage;
  702. }
  703. #ifndef CLIENT_DLL
  704. const char *CGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
  705. {
  706. if ( pPlayer && pPlayer->IsAlive() == false )
  707. {
  708. if ( bTeamOnly )
  709. return "*DEAD*(TEAM)";
  710. else
  711. return "*DEAD*";
  712. }
  713. return "";
  714. }
  715. void CGameRules::ClientSettingsChanged( CBasePlayer *pPlayer )
  716. {
  717. const char *pszName = engine->GetClientConVarValue( pPlayer->entindex(), "name" );
  718. const char *pszOldName = pPlayer->GetPlayerName();
  719. // msg everyone if someone changes their name, and it isn't the first time (changing no name to current name)
  720. // Note, this is case sensitive
  721. if ( ( Q_strcmp( pszOldName, pszName ) != 0 ) &&
  722. CanClientCustomizeOwnIdentity() )
  723. {
  724. if ( pszOldName[0] != '\0' )
  725. {
  726. IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
  727. if ( event )
  728. {
  729. event->SetInt( "userid", pPlayer->GetUserID() );
  730. event->SetString( "oldname", pszOldName );
  731. event->SetString( "newname", pszName );
  732. gameeventmanager->FireEvent( event );
  733. }
  734. }
  735. pPlayer->SetPlayerName( pszName );
  736. }
  737. const char *pszFov = engine->GetClientConVarValue( pPlayer->entindex(), "fov_desired" );
  738. if ( pszFov )
  739. {
  740. int iFov = atoi(pszFov);
  741. iFov = clamp( iFov, 1, 90 );
  742. pPlayer->SetDefaultFOV( iFov );
  743. }
  744. }
  745. #endif