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.

880 lines
26 KiB

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