aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/gamerules.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/gamerules.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/gamerules.cpp')
-rw-r--r--mp/src/game/shared/gamerules.cpp1760
1 files changed, 880 insertions, 880 deletions
diff --git a/mp/src/game/shared/gamerules.cpp b/mp/src/game/shared/gamerules.cpp
index b0be8603..81fa7bfa 100644
--- a/mp/src/game/shared/gamerules.cpp
+++ b/mp/src/game/shared/gamerules.cpp
@@ -1,880 +1,880 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "gamerules.h"
-#include "ammodef.h"
-#include "tier0/vprof.h"
-#include "KeyValues.h"
-#include "iachievementmgr.h"
-
-#ifdef CLIENT_DLL
-
- #include "usermessages.h"
-
-#else
-
- #include "player.h"
- #include "teamplay_gamerules.h"
- #include "game.h"
- #include "entitylist.h"
- #include "basecombatweapon.h"
- #include "voice_gamemgr.h"
- #include "globalstate.h"
- #include "player_resource.h"
- #include "tactical_mission.h"
- #include "gamestats.h"
-
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-ConVar g_Language( "g_Language", "0", FCVAR_REPLICATED );
-ConVar sk_autoaim_mode( "sk_autoaim_mode", "1", FCVAR_ARCHIVE | FCVAR_REPLICATED );
-
-#ifndef CLIENT_DLL
-ConVar log_verbose_enable( "log_verbose_enable", "0", FCVAR_GAMEDLL, "Set to 1 to enable verbose server log on the server." );
-ConVar log_verbose_interval( "log_verbose_interval", "3.0", FCVAR_GAMEDLL, "Determines the interval (in seconds) for the verbose server log." );
-#endif // CLIENT_DLL
-
-static CViewVectors g_DefaultViewVectors(
- Vector( 0, 0, 64 ), //VEC_VIEW (m_vView)
-
- Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin)
- Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax)
-
- Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin)
- Vector( 16, 16, 36 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax)
- Vector( 0, 0, 28 ), //VEC_DUCK_VIEW (m_vDuckView)
-
- Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin)
- Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax)
-
- Vector( 0, 0, 14 ) //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight)
-);
-
-
-// ------------------------------------------------------------------------------------ //
-// CGameRulesProxy implementation.
-// ------------------------------------------------------------------------------------ //
-
-CGameRulesProxy *CGameRulesProxy::s_pGameRulesProxy = NULL;
-
-IMPLEMENT_NETWORKCLASS_ALIASED( GameRulesProxy, DT_GameRulesProxy )
-
-// Don't send any of the CBaseEntity stuff..
-BEGIN_NETWORK_TABLE_NOBASE( CGameRulesProxy, DT_GameRulesProxy )
-END_NETWORK_TABLE()
-
-
-CGameRulesProxy::CGameRulesProxy()
-{
- // allow map placed proxy entities to overwrite the static one
- if ( s_pGameRulesProxy )
- {
-#ifndef CLIENT_DLL
- UTIL_Remove( s_pGameRulesProxy );
-#endif
- s_pGameRulesProxy = NULL;
- }
- s_pGameRulesProxy = this;
-}
-
-CGameRulesProxy::~CGameRulesProxy()
-{
- if ( s_pGameRulesProxy == this )
- {
- s_pGameRulesProxy = NULL;
- }
-}
-
-int CGameRulesProxy::UpdateTransmitState()
-{
-#ifndef CLIENT_DLL
- // ALWAYS transmit to all clients.
- return SetTransmitState( FL_EDICT_ALWAYS );
-#else
- return 0;
-#endif
-
-}
-
-void CGameRulesProxy::NotifyNetworkStateChanged()
-{
- if ( s_pGameRulesProxy )
- s_pGameRulesProxy->NetworkStateChanged();
-}
-
-
-
-ConVar old_radius_damage( "old_radiusdamage", "0.0", FCVAR_REPLICATED );
-
-#ifdef CLIENT_DLL //{
-
-bool CGameRules::IsBonusChallengeTimeBased( void )
-{
- return true;
-}
-
-bool CGameRules::IsLocalPlayer( int nEntIndex )
-{
- C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
- return ( pLocalPlayer && pLocalPlayer == ClientEntityList().GetEnt( nEntIndex ) );
-}
-
-CGameRules::CGameRules() : CAutoGameSystemPerFrame( "CGameRules" )
-{
- Assert( !g_pGameRules );
- g_pGameRules = this;
-}
-
-#else //}{
-
-// In tf_gamerules.cpp or hl_gamerules.cpp.
-extern IVoiceGameMgrHelper *g_pVoiceGameMgrHelper;
-
-
-CGameRules* g_pGameRules = NULL;
-extern bool g_fGameOver;
-
-//-----------------------------------------------------------------------------
-// constructor, destructor
-//-----------------------------------------------------------------------------
-CGameRules::CGameRules() : CAutoGameSystemPerFrame( "CGameRules" )
-{
- Assert( !g_pGameRules );
- g_pGameRules = this;
-
- GetVoiceGameMgr()->Init( g_pVoiceGameMgrHelper, gpGlobals->maxClients );
- ClearMultiDamage();
-
- m_flNextVerboseLogOutput = 0.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return true if the specified player can carry any more of the ammo type
-//-----------------------------------------------------------------------------
-bool CGameRules::CanHaveAmmo( CBaseCombatCharacter *pPlayer, int iAmmoIndex )
-{
- if ( iAmmoIndex > -1 )
- {
- // Get the max carrying capacity for this ammo
- int iMaxCarry = GetAmmoDef()->MaxCarry( iAmmoIndex );
-
- // Does the player have room for more of this type of ammo?
- if ( pPlayer->GetAmmoCount( iAmmoIndex ) < iMaxCarry )
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return true if the specified player can carry any more of the ammo type
-//-----------------------------------------------------------------------------
-bool CGameRules::CanHaveAmmo( CBaseCombatCharacter *pPlayer, const char *szName )
-{
- return CanHaveAmmo( pPlayer, GetAmmoDef()->Index(szName) );
-}
-
-//=========================================================
-//=========================================================
-CBaseEntity *CGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
-{
- CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
- Assert( pSpawnSpot );
-
- pPlayer->SetLocalOrigin( pSpawnSpot->GetAbsOrigin() + Vector(0,0,1) );
- pPlayer->SetAbsVelocity( vec3_origin );
- pPlayer->SetLocalAngles( pSpawnSpot->GetLocalAngles() );
- pPlayer->m_Local.m_vecPunchAngle = vec3_angle;
- pPlayer->m_Local.m_vecPunchAngleVel = vec3_angle;
- pPlayer->SnapEyeAngles( pSpawnSpot->GetLocalAngles() );
-
- return pSpawnSpot;
-}
-
-// checks if the spot is clear of players
-bool CGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer )
-{
- CBaseEntity *ent = NULL;
-
- if ( !pSpot->IsTriggered( pPlayer ) )
- {
- return false;
- }
-
- for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
- {
- // if ent is a client, don't spawn on 'em
- if ( ent->IsPlayer() && ent != pPlayer )
- return false;
- }
-
- return true;
-}
-
-//=========================================================
-//=========================================================
-bool CGameRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
-{
-/*
- if ( pWeapon->m_pszAmmo1 )
- {
- if ( !CanHaveAmmo( pPlayer, pWeapon->m_iPrimaryAmmoType ) )
- {
- // we can't carry anymore ammo for this gun. We can only
- // have the gun if we aren't already carrying one of this type
- if ( pPlayer->Weapon_OwnsThisType( pWeapon ) )
- {
- return FALSE;
- }
- }
- }
- else
- {
- // weapon doesn't use ammo, don't take another if you already have it.
- if ( pPlayer->Weapon_OwnsThisType( pWeapon ) )
- {
- return FALSE;
- }
- }
-*/
- // note: will fall through to here if GetItemInfo doesn't fill the struct!
- return TRUE;
-}
-
-//=========================================================
-// load the SkillData struct with the proper values based on the skill level.
-//=========================================================
-void CGameRules::RefreshSkillData ( bool forceUpdate )
-{
-#ifndef CLIENT_DLL
- if ( !forceUpdate )
- {
- if ( GlobalEntity_IsInTable( "skill.cfg" ) )
- return;
- }
- GlobalEntity_Add( "skill.cfg", STRING(gpGlobals->mapname), GLOBAL_ON );
-
-#if !defined( TF_DLL ) && !defined( DOD_DLL )
- char szExec[256];
-#endif
-
- ConVarRef skill( "skill" );
-
- SetSkillLevel( skill.IsValid() ? skill.GetInt() : 1 );
-
-#ifdef HL2_DLL
- // HL2 current only uses one skill config file that represents MEDIUM skill level and
- // synthesizes EASY and HARD. (sjb)
- Q_snprintf( szExec,sizeof(szExec), "exec skill_manifest.cfg\n" );
-
- engine->ServerCommand( szExec );
- engine->ServerExecute();
-#else
-
-#if !defined( TF_DLL ) && !defined( DOD_DLL )
- Q_snprintf( szExec,sizeof(szExec), "exec skill%d.cfg\n", GetSkillLevel() );
-
- engine->ServerCommand( szExec );
- engine->ServerExecute();
-#endif // TF_DLL && DOD_DLL
-
-#endif // HL2_DLL
-#endif // CLIENT_DLL
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool IsExplosionTraceBlocked( trace_t *ptr )
-{
- if( ptr->DidHitWorld() )
- return true;
-
- if( ptr->m_pEnt == NULL )
- return false;
-
- if( ptr->m_pEnt->GetMoveType() == MOVETYPE_PUSH )
- {
- // All doors are push, but not all things that push are doors. This
- // narrows the search before we start to do classname compares.
- if( FClassnameIs(ptr->m_pEnt, "prop_door_rotating") ||
- FClassnameIs(ptr->m_pEnt, "func_door") ||
- FClassnameIs(ptr->m_pEnt, "func_door_rotating") )
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Default implementation of radius damage
-//-----------------------------------------------------------------------------
-#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
-void CGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore )
-{
- const int MASK_RADIUS_DAMAGE = MASK_SHOT&(~CONTENTS_HITBOX);
- CBaseEntity *pEntity = NULL;
- trace_t tr;
- float flAdjustedDamage, falloff;
- Vector vecSpot;
-
- Vector vecSrc = vecSrcIn;
-
- if ( flRadius )
- falloff = info.GetDamage() / flRadius;
- else
- falloff = 1.0;
-
- int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER) ? true : false;
-
-#ifdef HL2_DLL
- if( bInWater )
- {
- // Only muffle the explosion if deeper than 2 feet in water.
- if( !(UTIL_PointContents(vecSrc + Vector(0, 0, 24)) & MASK_WATER) )
- {
- bInWater = false;
- }
- }
-#endif // HL2_DLL
-
- vecSrc.z += 1;// in case grenade is lying on the ground
-
- float flHalfRadiusSqr = Square( flRadius / 2.0f );
-
- // iterate on all entities in the vicinity.
- for ( CEntitySphereQuery sphere( vecSrc, flRadius ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
- {
- // This value is used to scale damage when the explosion is blocked by some other object.
- float flBlockedDamagePercent = 0.0f;
-
- if ( pEntity == pEntityIgnore )
- continue;
-
- if ( pEntity->m_takedamage == DAMAGE_NO )
- continue;
-
- // UNDONE: this should check a damage mask, not an ignore
- if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
- {// houndeyes don't hurt other houndeyes with their attack
- continue;
- }
-
- // blast's don't tavel into or out of water
- if (bInWater && pEntity->GetWaterLevel() == 0)
- continue;
-
- if (!bInWater && pEntity->GetWaterLevel() == 3)
- continue;
-
- // Check that the explosion can 'see' this entity.
- vecSpot = pEntity->BodyTarget( vecSrc, false );
- UTIL_TraceLine( vecSrc, vecSpot, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
-
- if( old_radius_damage.GetBool() )
- {
- if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
- continue;
- }
- else
- {
- if ( tr.fraction != 1.0 )
- {
- if ( IsExplosionTraceBlocked(&tr) )
- {
- if( ShouldUseRobustRadiusDamage( pEntity ) )
- {
- if( vecSpot.DistToSqr( vecSrc ) > flHalfRadiusSqr )
- {
- // Only use robust model on a target within one-half of the explosion's radius.
- continue;
- }
-
- Vector vecToTarget = vecSpot - tr.endpos;
- VectorNormalize( vecToTarget );
-
- // We're going to deflect the blast along the surface that
- // interrupted a trace from explosion to this target.
- Vector vecUp, vecDeflect;
- CrossProduct( vecToTarget, tr.plane.normal, vecUp );
- CrossProduct( tr.plane.normal, vecUp, vecDeflect );
- VectorNormalize( vecDeflect );
-
- // Trace along the surface that intercepted the blast...
- UTIL_TraceLine( tr.endpos, tr.endpos + vecDeflect * ROBUST_RADIUS_PROBE_DIST, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
- //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 255, 0, false, 10 );
-
- // ...to see if there's a nearby edge that the explosion would 'spill over' if the blast were fully simulated.
- UTIL_TraceLine( tr.endpos, vecSpot, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
- //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 10 );
-
- if( tr.fraction != 1.0 && tr.DidHitWorld() )
- {
- // Still can't reach the target.
- continue;
- }
- // else fall through
- }
- else
- {
- continue;
- }
- }
-
- // UNDONE: Probably shouldn't let children block parents either? Or maybe those guys should set their owner if they want this behavior?
- // HL2 - Dissolve damage is not reduced by interposing non-world objects
- if( tr.m_pEnt && tr.m_pEnt != pEntity && tr.m_pEnt->GetOwnerEntity() != pEntity )
- {
- // Some entity was hit by the trace, meaning the explosion does not have clear
- // line of sight to the entity that it's trying to hurt. If the world is also
- // blocking, we do no damage.
- CBaseEntity *pBlockingEntity = tr.m_pEnt;
- //Msg( "%s may be blocked by %s...", pEntity->GetClassname(), pBlockingEntity->GetClassname() );
-
- UTIL_TraceLine( vecSrc, vecSpot, CONTENTS_SOLID, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
-
- if( tr.fraction != 1.0 )
- {
- continue;
- }
-
- // Now, if the interposing object is physics, block some explosion force based on its mass.
- if( pBlockingEntity->VPhysicsGetObject() )
- {
- const float MASS_ABSORB_ALL_DAMAGE = 350.0f;
- float flMass = pBlockingEntity->VPhysicsGetObject()->GetMass();
- float scale = flMass / MASS_ABSORB_ALL_DAMAGE;
-
- // Absorbed all the damage.
- if( scale >= 1.0f )
- {
- continue;
- }
-
- ASSERT( scale > 0.0f );
- flBlockedDamagePercent = scale;
- //Msg(" Object (%s) weighing %fkg blocked %f percent of explosion damage\n", pBlockingEntity->GetClassname(), flMass, scale * 100.0f);
- }
- else
- {
- // Some object that's not the world and not physics. Generically block 25% damage
- flBlockedDamagePercent = 0.25f;
- }
- }
- }
- }
- // decrease damage for an ent that's farther from the bomb.
- flAdjustedDamage = ( vecSrc - tr.endpos ).Length() * falloff;
- flAdjustedDamage = info.GetDamage() - flAdjustedDamage;
-
- if ( flAdjustedDamage <= 0 )
- {
- continue;
- }
-
- // the explosion can 'see' this entity, so hurt them!
- if (tr.startsolid)
- {
- // if we're stuck inside them, fixup the position and distance
- tr.endpos = vecSrc;
- tr.fraction = 0.0;
- }
-
- CTakeDamageInfo adjustedInfo = info;
- //Msg("%s: Blocked damage: %f percent (in:%f out:%f)\n", pEntity->GetClassname(), flBlockedDamagePercent * 100, flAdjustedDamage, flAdjustedDamage - (flAdjustedDamage * flBlockedDamagePercent) );
- adjustedInfo.SetDamage( flAdjustedDamage - (flAdjustedDamage * flBlockedDamagePercent) );
-
- // Now make a consideration for skill level!
- if( info.GetAttacker() && info.GetAttacker()->IsPlayer() && pEntity->IsNPC() )
- {
- // An explosion set off by the player is harming an NPC. Adjust damage accordingly.
- adjustedInfo.AdjustPlayerDamageInflictedForSkillLevel();
- }
-
- Vector dir = vecSpot - vecSrc;
- VectorNormalize( dir );
-
- // If we don't have a damage force, manufacture one
- if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
- {
- if ( !( adjustedInfo.GetDamageType() & DMG_PREVENT_PHYSICS_FORCE ) )
- {
- CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc );
- }
- }
- else
- {
- // Assume the force passed in is the maximum force. Decay it based on falloff.
- float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
- adjustedInfo.SetDamageForce( dir * flForce );
- adjustedInfo.SetDamagePosition( vecSrc );
- }
-
- if ( tr.fraction != 1.0 && pEntity == tr.m_pEnt )
- {
- ClearMultiDamage( );
- pEntity->DispatchTraceAttack( adjustedInfo, dir, &tr );
- ApplyMultiDamage();
- }
- else
- {
- pEntity->TakeDamage( adjustedInfo );
- }
-
- // Now hit all triggers along the way that respond to damage...
- pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, tr.endpos, dir );
-
-#if defined( GAME_DLL )
- if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && ToBaseCombatCharacter( tr.m_pEnt ) )
- {
-
- // This is a total hack!!!
- bool bIsPrimary = true;
- CBasePlayer *player = ToBasePlayer( info.GetAttacker() );
- CBaseCombatWeapon *pWeapon = player->GetActiveWeapon();
- if ( pWeapon && FClassnameIs( pWeapon, "weapon_smg1" ) )
- {
- bIsPrimary = false;
- }
-
- gamestats->Event_WeaponHit( player, bIsPrimary, (pWeapon != NULL) ? player->GetActiveWeapon()->GetClassname() : "NULL", info );
- }
-#endif
- }
-}
-
-
-bool CGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
-{
- if( pEdict->IsPlayer() )
- {
- if( GetVoiceGameMgr()->ClientCommand( static_cast<CBasePlayer*>(pEdict), args ) )
- return true;
- }
-
- return false;
-}
-
-
-void CGameRules::FrameUpdatePostEntityThink()
-{
- VPROF( "CGameRules::FrameUpdatePostEntityThink" );
- Think();
-}
-
-// Hook into the convar from the engine
-ConVar skill( "skill", "1" );
-
-void CGameRules::Think()
-{
- GetVoiceGameMgr()->Update( gpGlobals->frametime );
- SetSkillLevel( skill.GetInt() );
-
- if ( log_verbose_enable.GetBool() )
- {
- if ( m_flNextVerboseLogOutput < gpGlobals->curtime )
- {
- ProcessVerboseLogOutput();
- m_flNextVerboseLogOutput = gpGlobals->curtime + log_verbose_interval.GetFloat();
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called at the end of GameFrame (i.e. after all game logic has run this frame)
-//-----------------------------------------------------------------------------
-void CGameRules::EndGameFrame( void )
-{
- // If you hit this assert, it means something called AddMultiDamage() and didn't ApplyMultiDamage().
- // The g_MultiDamage.m_hAttacker & g_MultiDamage.m_hInflictor should give help you figure out the culprit.
- Assert( g_MultiDamage.IsClear() );
- if ( !g_MultiDamage.IsClear() )
- {
- Warning("Unapplied multidamage left in the system:\nTarget: %s\nInflictor: %s\nAttacker: %s\nDamage: %.2f\n",
- g_MultiDamage.GetTarget()->GetDebugName(),
- g_MultiDamage.GetInflictor()->GetDebugName(),
- g_MultiDamage.GetAttacker()->GetDebugName(),
- g_MultiDamage.GetDamage() );
- ApplyMultiDamage();
- }
-}
-
-//-----------------------------------------------------------------------------
-// trace line rules
-//-----------------------------------------------------------------------------
-float CGameRules::WeaponTraceEntity( CBaseEntity *pEntity, const Vector &vecStart, const Vector &vecEnd,
- unsigned int mask, trace_t *ptr )
-{
- UTIL_TraceEntity( pEntity, vecStart, vecEnd, mask, ptr );
- return 1.0f;
-}
-
-
-void CGameRules::CreateStandardEntities()
-{
- g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "player_manager", vec3_origin, vec3_angle );
- g_pPlayerResource->AddEFlags( EFL_KEEP_ON_RECREATE_ENTITIES );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Inform client(s) they can mark the indicated achievement as completed (SERVER VERSION)
-// Input : filter - which client(s) to send this to
-// iAchievementID - The enumeration value of the achievement to mark (see TODO:Kerry, what file will have the mod's achievement enum?)
-//-----------------------------------------------------------------------------
-void CGameRules::MarkAchievement( IRecipientFilter& filter, char const *pchAchievementName )
-{
- gamestats->Event_IncrementCountedStatistic( vec3_origin, pchAchievementName, 1.0f );
-
- IAchievementMgr *pAchievementMgr = engine->GetAchievementMgr();
- if ( !pAchievementMgr )
- return;
- pAchievementMgr->OnMapEvent( pchAchievementName );
-}
-
-#endif //} !CLIENT_DLL
-
-
-// ----------------------------------------------------------------------------- //
-// Shared CGameRules implementation.
-// ----------------------------------------------------------------------------- //
-
-CGameRules::~CGameRules()
-{
- Assert( g_pGameRules == this );
- g_pGameRules = NULL;
-}
-
-bool CGameRules::SwitchToNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
-{
- return false;
-}
-
-CBaseCombatWeapon *CGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
-{
- return NULL;
-}
-
-bool CGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
-{
- if ( collisionGroup0 > collisionGroup1 )
- {
- // swap so that lowest is always first
- ::V_swap(collisionGroup0,collisionGroup1);
- }
-
-#ifndef HL2MP
- if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
- collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
- {
- return false;
- }
-#endif
-
- if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
- {
- // let debris and multiplayer objects collide
- return true;
- }
-
- // --------------------------------------------------------------------------
- // NOTE: All of this code assumes the collision groups have been sorted!!!!
- // NOTE: Don't change their order without rewriting this code !!!
- // --------------------------------------------------------------------------
-
- // Don't bother if either is in a vehicle...
- if (( collisionGroup0 == COLLISION_GROUP_IN_VEHICLE ) || ( collisionGroup1 == COLLISION_GROUP_IN_VEHICLE ))
- return false;
-
- if ( ( collisionGroup1 == COLLISION_GROUP_DOOR_BLOCKER ) && ( collisionGroup0 != COLLISION_GROUP_NPC ) )
- return false;
-
- if ( ( collisionGroup0 == COLLISION_GROUP_PLAYER ) && ( collisionGroup1 == COLLISION_GROUP_PASSABLE_DOOR ) )
- return false;
-
- if ( collisionGroup0 == COLLISION_GROUP_DEBRIS || collisionGroup0 == COLLISION_GROUP_DEBRIS_TRIGGER )
- {
- // put exceptions here, right now this will only collide with COLLISION_GROUP_NONE
- return false;
- }
-
- // Dissolving guys only collide with COLLISION_GROUP_NONE
- if ( (collisionGroup0 == COLLISION_GROUP_DISSOLVING) || (collisionGroup1 == COLLISION_GROUP_DISSOLVING) )
- {
- if ( collisionGroup0 != COLLISION_GROUP_NONE )
- return false;
- }
-
- // doesn't collide with other members of this group
- // or debris, but that's handled above
- if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && collisionGroup1 == COLLISION_GROUP_INTERACTIVE_DEBRIS )
- return false;
-
-#ifndef HL2MP
- // This change was breaking HL2DM
- // Adrian: TEST! Interactive Debris doesn't collide with the player.
- if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && ( collisionGroup1 == COLLISION_GROUP_PLAYER_MOVEMENT || collisionGroup1 == COLLISION_GROUP_PLAYER ) )
- return false;
-#endif
-
- if ( collisionGroup0 == COLLISION_GROUP_BREAKABLE_GLASS && collisionGroup1 == COLLISION_GROUP_BREAKABLE_GLASS )
- return false;
-
- // interactive objects collide with everything except debris & interactive debris
- if ( collisionGroup1 == COLLISION_GROUP_INTERACTIVE && collisionGroup0 != COLLISION_GROUP_NONE )
- return false;
-
- // Projectiles hit everything but debris, weapons, + other projectiles
- if ( collisionGroup1 == COLLISION_GROUP_PROJECTILE )
- {
- if ( collisionGroup0 == COLLISION_GROUP_DEBRIS ||
- collisionGroup0 == COLLISION_GROUP_WEAPON ||
- collisionGroup0 == COLLISION_GROUP_PROJECTILE )
- {
- return false;
- }
- }
-
- // Don't let vehicles collide with weapons
- // Don't let players collide with weapons...
- // Don't let NPCs collide with weapons
- // Weapons are triggers, too, so they should still touch because of that
- if ( collisionGroup1 == COLLISION_GROUP_WEAPON )
- {
- if ( collisionGroup0 == COLLISION_GROUP_VEHICLE ||
- collisionGroup0 == COLLISION_GROUP_PLAYER ||
- collisionGroup0 == COLLISION_GROUP_NPC )
- {
- return false;
- }
- }
-
- // collision with vehicle clip entity??
- if ( collisionGroup0 == COLLISION_GROUP_VEHICLE_CLIP || collisionGroup1 == COLLISION_GROUP_VEHICLE_CLIP )
- {
- // yes then if it's a vehicle, collide, otherwise no collision
- // vehicle sorts lower than vehicle clip, so must be in 0
- if ( collisionGroup0 == COLLISION_GROUP_VEHICLE )
- return true;
- // vehicle clip against non-vehicle, no collision
- return false;
- }
-
- return true;
-}
-
-
-const CViewVectors* CGameRules::GetViewVectors() const
-{
- return &g_DefaultViewVectors;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns how much damage the given ammo type should do to the victim
-// when fired by the attacker.
-// Input : pAttacker - Dude what shot the gun.
-// pVictim - Dude what done got shot.
-// nAmmoType - What been shot out.
-// Output : How much hurt to put on dude what done got shot (pVictim).
-//-----------------------------------------------------------------------------
-float CGameRules::GetAmmoDamage( CBaseEntity *pAttacker, CBaseEntity *pVictim, int nAmmoType )
-{
- float flDamage = 0;
- CAmmoDef *pAmmoDef = GetAmmoDef();
-
- if ( pAttacker->IsPlayer() )
- {
- flDamage = pAmmoDef->PlrDamage( nAmmoType );
- }
- else
- {
- flDamage = pAmmoDef->NPCDamage( nAmmoType );
- }
-
- return flDamage;
-}
-
-
-#ifndef CLIENT_DLL
-const char *CGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
-{
- if ( pPlayer && pPlayer->IsAlive() == false )
- {
- if ( bTeamOnly )
- return "*DEAD*(TEAM)";
- else
- return "*DEAD*";
- }
-
- return "";
-}
-
-void CGameRules::CheckHaptics(CBasePlayer* pPlayer)
-{
- // NVNT see if the client of pPlayer is using a haptic device.
- const char *pszHH = engine->GetClientConVarValue( pPlayer->entindex(), "hap_HasDevice" );
- if( pszHH )
- {
- int iHH = atoi( pszHH );
- pPlayer->SetHaptics( iHH != 0 );
- }
-}
-
-void CGameRules::ClientSettingsChanged( CBasePlayer *pPlayer )
-{
- const char *pszName = engine->GetClientConVarValue( pPlayer->entindex(), "name" );
-
- const char *pszOldName = pPlayer->GetPlayerName();
-
- // msg everyone if someone changes their name, and it isn't the first time (changing no name to current name)
- // Note, not using FStrEq so that this is case sensitive
- if ( pszOldName[0] != 0 && Q_strcmp( pszOldName, pszName ) )
- {
- char text[256];
- Q_snprintf( text,sizeof(text), "%s changed name to %s\n", pszOldName, pszName );
-
- UTIL_ClientPrintAll( HUD_PRINTTALK, text );
-
- IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
- if ( event )
- {
- event->SetInt( "userid", pPlayer->GetUserID() );
- event->SetString( "oldname", pszOldName );
- event->SetString( "newname", pszName );
- gameeventmanager->FireEvent( event );
- }
-
- pPlayer->SetPlayerName( pszName );
- }
-
- const char *pszFov = engine->GetClientConVarValue( pPlayer->entindex(), "fov_desired" );
- if ( pszFov )
- {
- int iFov = atoi(pszFov);
- iFov = clamp( iFov, 75, 90 );
- pPlayer->SetDefaultFOV( iFov );
- }
-
- // NVNT see if this user is still or has began using a haptic device
- const char *pszHH = engine->GetClientConVarValue( pPlayer->entindex(), "hap_HasDevice" );
- if( pszHH )
- {
- int iHH = atoi( pszHH );
- pPlayer->SetHaptics( iHH != 0 );
- }
-}
-
-CTacticalMissionManager *CGameRules::TacticalMissionManagerFactory( void )
-{
- return new CTacticalMissionManager;
-}
-
-#endif
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "gamerules.h"
+#include "ammodef.h"
+#include "tier0/vprof.h"
+#include "KeyValues.h"
+#include "iachievementmgr.h"
+
+#ifdef CLIENT_DLL
+
+ #include "usermessages.h"
+
+#else
+
+ #include "player.h"
+ #include "teamplay_gamerules.h"
+ #include "game.h"
+ #include "entitylist.h"
+ #include "basecombatweapon.h"
+ #include "voice_gamemgr.h"
+ #include "globalstate.h"
+ #include "player_resource.h"
+ #include "tactical_mission.h"
+ #include "gamestats.h"
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+ConVar g_Language( "g_Language", "0", FCVAR_REPLICATED );
+ConVar sk_autoaim_mode( "sk_autoaim_mode", "1", FCVAR_ARCHIVE | FCVAR_REPLICATED );
+
+#ifndef CLIENT_DLL
+ConVar log_verbose_enable( "log_verbose_enable", "0", FCVAR_GAMEDLL, "Set to 1 to enable verbose server log on the server." );
+ConVar log_verbose_interval( "log_verbose_interval", "3.0", FCVAR_GAMEDLL, "Determines the interval (in seconds) for the verbose server log." );
+#endif // CLIENT_DLL
+
+static CViewVectors g_DefaultViewVectors(
+ Vector( 0, 0, 64 ), //VEC_VIEW (m_vView)
+
+ Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin)
+ Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax)
+
+ Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin)
+ Vector( 16, 16, 36 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax)
+ Vector( 0, 0, 28 ), //VEC_DUCK_VIEW (m_vDuckView)
+
+ Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin)
+ Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax)
+
+ Vector( 0, 0, 14 ) //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight)
+);
+
+
+// ------------------------------------------------------------------------------------ //
+// CGameRulesProxy implementation.
+// ------------------------------------------------------------------------------------ //
+
+CGameRulesProxy *CGameRulesProxy::s_pGameRulesProxy = NULL;
+
+IMPLEMENT_NETWORKCLASS_ALIASED( GameRulesProxy, DT_GameRulesProxy )
+
+// Don't send any of the CBaseEntity stuff..
+BEGIN_NETWORK_TABLE_NOBASE( CGameRulesProxy, DT_GameRulesProxy )
+END_NETWORK_TABLE()
+
+
+CGameRulesProxy::CGameRulesProxy()
+{
+ // allow map placed proxy entities to overwrite the static one
+ if ( s_pGameRulesProxy )
+ {
+#ifndef CLIENT_DLL
+ UTIL_Remove( s_pGameRulesProxy );
+#endif
+ s_pGameRulesProxy = NULL;
+ }
+ s_pGameRulesProxy = this;
+}
+
+CGameRulesProxy::~CGameRulesProxy()
+{
+ if ( s_pGameRulesProxy == this )
+ {
+ s_pGameRulesProxy = NULL;
+ }
+}
+
+int CGameRulesProxy::UpdateTransmitState()
+{
+#ifndef CLIENT_DLL
+ // ALWAYS transmit to all clients.
+ return SetTransmitState( FL_EDICT_ALWAYS );
+#else
+ return 0;
+#endif
+
+}
+
+void CGameRulesProxy::NotifyNetworkStateChanged()
+{
+ if ( s_pGameRulesProxy )
+ s_pGameRulesProxy->NetworkStateChanged();
+}
+
+
+
+ConVar old_radius_damage( "old_radiusdamage", "0.0", FCVAR_REPLICATED );
+
+#ifdef CLIENT_DLL //{
+
+bool CGameRules::IsBonusChallengeTimeBased( void )
+{
+ return true;
+}
+
+bool CGameRules::IsLocalPlayer( int nEntIndex )
+{
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ return ( pLocalPlayer && pLocalPlayer == ClientEntityList().GetEnt( nEntIndex ) );
+}
+
+CGameRules::CGameRules() : CAutoGameSystemPerFrame( "CGameRules" )
+{
+ Assert( !g_pGameRules );
+ g_pGameRules = this;
+}
+
+#else //}{
+
+// In tf_gamerules.cpp or hl_gamerules.cpp.
+extern IVoiceGameMgrHelper *g_pVoiceGameMgrHelper;
+
+
+CGameRules* g_pGameRules = NULL;
+extern bool g_fGameOver;
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+CGameRules::CGameRules() : CAutoGameSystemPerFrame( "CGameRules" )
+{
+ Assert( !g_pGameRules );
+ g_pGameRules = this;
+
+ GetVoiceGameMgr()->Init( g_pVoiceGameMgrHelper, gpGlobals->maxClients );
+ ClearMultiDamage();
+
+ m_flNextVerboseLogOutput = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the specified player can carry any more of the ammo type
+//-----------------------------------------------------------------------------
+bool CGameRules::CanHaveAmmo( CBaseCombatCharacter *pPlayer, int iAmmoIndex )
+{
+ if ( iAmmoIndex > -1 )
+ {
+ // Get the max carrying capacity for this ammo
+ int iMaxCarry = GetAmmoDef()->MaxCarry( iAmmoIndex );
+
+ // Does the player have room for more of this type of ammo?
+ if ( pPlayer->GetAmmoCount( iAmmoIndex ) < iMaxCarry )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the specified player can carry any more of the ammo type
+//-----------------------------------------------------------------------------
+bool CGameRules::CanHaveAmmo( CBaseCombatCharacter *pPlayer, const char *szName )
+{
+ return CanHaveAmmo( pPlayer, GetAmmoDef()->Index(szName) );
+}
+
+//=========================================================
+//=========================================================
+CBaseEntity *CGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
+{
+ CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
+ Assert( pSpawnSpot );
+
+ pPlayer->SetLocalOrigin( pSpawnSpot->GetAbsOrigin() + Vector(0,0,1) );
+ pPlayer->SetAbsVelocity( vec3_origin );
+ pPlayer->SetLocalAngles( pSpawnSpot->GetLocalAngles() );
+ pPlayer->m_Local.m_vecPunchAngle = vec3_angle;
+ pPlayer->m_Local.m_vecPunchAngleVel = vec3_angle;
+ pPlayer->SnapEyeAngles( pSpawnSpot->GetLocalAngles() );
+
+ return pSpawnSpot;
+}
+
+// checks if the spot is clear of players
+bool CGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer )
+{
+ CBaseEntity *ent = NULL;
+
+ if ( !pSpot->IsTriggered( pPlayer ) )
+ {
+ return false;
+ }
+
+ for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
+ {
+ // if ent is a client, don't spawn on 'em
+ if ( ent->IsPlayer() && ent != pPlayer )
+ return false;
+ }
+
+ return true;
+}
+
+//=========================================================
+//=========================================================
+bool CGameRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
+{
+/*
+ if ( pWeapon->m_pszAmmo1 )
+ {
+ if ( !CanHaveAmmo( pPlayer, pWeapon->m_iPrimaryAmmoType ) )
+ {
+ // we can't carry anymore ammo for this gun. We can only
+ // have the gun if we aren't already carrying one of this type
+ if ( pPlayer->Weapon_OwnsThisType( pWeapon ) )
+ {
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ // weapon doesn't use ammo, don't take another if you already have it.
+ if ( pPlayer->Weapon_OwnsThisType( pWeapon ) )
+ {
+ return FALSE;
+ }
+ }
+*/
+ // note: will fall through to here if GetItemInfo doesn't fill the struct!
+ return TRUE;
+}
+
+//=========================================================
+// load the SkillData struct with the proper values based on the skill level.
+//=========================================================
+void CGameRules::RefreshSkillData ( bool forceUpdate )
+{
+#ifndef CLIENT_DLL
+ if ( !forceUpdate )
+ {
+ if ( GlobalEntity_IsInTable( "skill.cfg" ) )
+ return;
+ }
+ GlobalEntity_Add( "skill.cfg", STRING(gpGlobals->mapname), GLOBAL_ON );
+
+#if !defined( TF_DLL ) && !defined( DOD_DLL )
+ char szExec[256];
+#endif
+
+ ConVarRef skill( "skill" );
+
+ SetSkillLevel( skill.IsValid() ? skill.GetInt() : 1 );
+
+#ifdef HL2_DLL
+ // HL2 current only uses one skill config file that represents MEDIUM skill level and
+ // synthesizes EASY and HARD. (sjb)
+ Q_snprintf( szExec,sizeof(szExec), "exec skill_manifest.cfg\n" );
+
+ engine->ServerCommand( szExec );
+ engine->ServerExecute();
+#else
+
+#if !defined( TF_DLL ) && !defined( DOD_DLL )
+ Q_snprintf( szExec,sizeof(szExec), "exec skill%d.cfg\n", GetSkillLevel() );
+
+ engine->ServerCommand( szExec );
+ engine->ServerExecute();
+#endif // TF_DLL && DOD_DLL
+
+#endif // HL2_DLL
+#endif // CLIENT_DLL
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool IsExplosionTraceBlocked( trace_t *ptr )
+{
+ if( ptr->DidHitWorld() )
+ return true;
+
+ if( ptr->m_pEnt == NULL )
+ return false;
+
+ if( ptr->m_pEnt->GetMoveType() == MOVETYPE_PUSH )
+ {
+ // All doors are push, but not all things that push are doors. This
+ // narrows the search before we start to do classname compares.
+ if( FClassnameIs(ptr->m_pEnt, "prop_door_rotating") ||
+ FClassnameIs(ptr->m_pEnt, "func_door") ||
+ FClassnameIs(ptr->m_pEnt, "func_door_rotating") )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Default implementation of radius damage
+//-----------------------------------------------------------------------------
+#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
+void CGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore )
+{
+ const int MASK_RADIUS_DAMAGE = MASK_SHOT&(~CONTENTS_HITBOX);
+ CBaseEntity *pEntity = NULL;
+ trace_t tr;
+ float flAdjustedDamage, falloff;
+ Vector vecSpot;
+
+ Vector vecSrc = vecSrcIn;
+
+ if ( flRadius )
+ falloff = info.GetDamage() / flRadius;
+ else
+ falloff = 1.0;
+
+ int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER) ? true : false;
+
+#ifdef HL2_DLL
+ if( bInWater )
+ {
+ // Only muffle the explosion if deeper than 2 feet in water.
+ if( !(UTIL_PointContents(vecSrc + Vector(0, 0, 24)) & MASK_WATER) )
+ {
+ bInWater = false;
+ }
+ }
+#endif // HL2_DLL
+
+ vecSrc.z += 1;// in case grenade is lying on the ground
+
+ float flHalfRadiusSqr = Square( flRadius / 2.0f );
+
+ // iterate on all entities in the vicinity.
+ for ( CEntitySphereQuery sphere( vecSrc, flRadius ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
+ {
+ // This value is used to scale damage when the explosion is blocked by some other object.
+ float flBlockedDamagePercent = 0.0f;
+
+ if ( pEntity == pEntityIgnore )
+ continue;
+
+ if ( pEntity->m_takedamage == DAMAGE_NO )
+ continue;
+
+ // UNDONE: this should check a damage mask, not an ignore
+ if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
+ {// houndeyes don't hurt other houndeyes with their attack
+ continue;
+ }
+
+ // blast's don't tavel into or out of water
+ if (bInWater && pEntity->GetWaterLevel() == 0)
+ continue;
+
+ if (!bInWater && pEntity->GetWaterLevel() == 3)
+ continue;
+
+ // Check that the explosion can 'see' this entity.
+ vecSpot = pEntity->BodyTarget( vecSrc, false );
+ UTIL_TraceLine( vecSrc, vecSpot, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
+
+ if( old_radius_damage.GetBool() )
+ {
+ if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
+ continue;
+ }
+ else
+ {
+ if ( tr.fraction != 1.0 )
+ {
+ if ( IsExplosionTraceBlocked(&tr) )
+ {
+ if( ShouldUseRobustRadiusDamage( pEntity ) )
+ {
+ if( vecSpot.DistToSqr( vecSrc ) > flHalfRadiusSqr )
+ {
+ // Only use robust model on a target within one-half of the explosion's radius.
+ continue;
+ }
+
+ Vector vecToTarget = vecSpot - tr.endpos;
+ VectorNormalize( vecToTarget );
+
+ // We're going to deflect the blast along the surface that
+ // interrupted a trace from explosion to this target.
+ Vector vecUp, vecDeflect;
+ CrossProduct( vecToTarget, tr.plane.normal, vecUp );
+ CrossProduct( tr.plane.normal, vecUp, vecDeflect );
+ VectorNormalize( vecDeflect );
+
+ // Trace along the surface that intercepted the blast...
+ UTIL_TraceLine( tr.endpos, tr.endpos + vecDeflect * ROBUST_RADIUS_PROBE_DIST, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
+ //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 255, 0, false, 10 );
+
+ // ...to see if there's a nearby edge that the explosion would 'spill over' if the blast were fully simulated.
+ UTIL_TraceLine( tr.endpos, vecSpot, MASK_RADIUS_DAMAGE, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
+ //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 10 );
+
+ if( tr.fraction != 1.0 && tr.DidHitWorld() )
+ {
+ // Still can't reach the target.
+ continue;
+ }
+ // else fall through
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ // UNDONE: Probably shouldn't let children block parents either? Or maybe those guys should set their owner if they want this behavior?
+ // HL2 - Dissolve damage is not reduced by interposing non-world objects
+ if( tr.m_pEnt && tr.m_pEnt != pEntity && tr.m_pEnt->GetOwnerEntity() != pEntity )
+ {
+ // Some entity was hit by the trace, meaning the explosion does not have clear
+ // line of sight to the entity that it's trying to hurt. If the world is also
+ // blocking, we do no damage.
+ CBaseEntity *pBlockingEntity = tr.m_pEnt;
+ //Msg( "%s may be blocked by %s...", pEntity->GetClassname(), pBlockingEntity->GetClassname() );
+
+ UTIL_TraceLine( vecSrc, vecSpot, CONTENTS_SOLID, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
+
+ if( tr.fraction != 1.0 )
+ {
+ continue;
+ }
+
+ // Now, if the interposing object is physics, block some explosion force based on its mass.
+ if( pBlockingEntity->VPhysicsGetObject() )
+ {
+ const float MASS_ABSORB_ALL_DAMAGE = 350.0f;
+ float flMass = pBlockingEntity->VPhysicsGetObject()->GetMass();
+ float scale = flMass / MASS_ABSORB_ALL_DAMAGE;
+
+ // Absorbed all the damage.
+ if( scale >= 1.0f )
+ {
+ continue;
+ }
+
+ ASSERT( scale > 0.0f );
+ flBlockedDamagePercent = scale;
+ //Msg(" Object (%s) weighing %fkg blocked %f percent of explosion damage\n", pBlockingEntity->GetClassname(), flMass, scale * 100.0f);
+ }
+ else
+ {
+ // Some object that's not the world and not physics. Generically block 25% damage
+ flBlockedDamagePercent = 0.25f;
+ }
+ }
+ }
+ }
+ // decrease damage for an ent that's farther from the bomb.
+ flAdjustedDamage = ( vecSrc - tr.endpos ).Length() * falloff;
+ flAdjustedDamage = info.GetDamage() - flAdjustedDamage;
+
+ if ( flAdjustedDamage <= 0 )
+ {
+ continue;
+ }
+
+ // the explosion can 'see' this entity, so hurt them!
+ if (tr.startsolid)
+ {
+ // if we're stuck inside them, fixup the position and distance
+ tr.endpos = vecSrc;
+ tr.fraction = 0.0;
+ }
+
+ CTakeDamageInfo adjustedInfo = info;
+ //Msg("%s: Blocked damage: %f percent (in:%f out:%f)\n", pEntity->GetClassname(), flBlockedDamagePercent * 100, flAdjustedDamage, flAdjustedDamage - (flAdjustedDamage * flBlockedDamagePercent) );
+ adjustedInfo.SetDamage( flAdjustedDamage - (flAdjustedDamage * flBlockedDamagePercent) );
+
+ // Now make a consideration for skill level!
+ if( info.GetAttacker() && info.GetAttacker()->IsPlayer() && pEntity->IsNPC() )
+ {
+ // An explosion set off by the player is harming an NPC. Adjust damage accordingly.
+ adjustedInfo.AdjustPlayerDamageInflictedForSkillLevel();
+ }
+
+ Vector dir = vecSpot - vecSrc;
+ VectorNormalize( dir );
+
+ // If we don't have a damage force, manufacture one
+ if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
+ {
+ if ( !( adjustedInfo.GetDamageType() & DMG_PREVENT_PHYSICS_FORCE ) )
+ {
+ CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc );
+ }
+ }
+ else
+ {
+ // Assume the force passed in is the maximum force. Decay it based on falloff.
+ float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
+ adjustedInfo.SetDamageForce( dir * flForce );
+ adjustedInfo.SetDamagePosition( vecSrc );
+ }
+
+ if ( tr.fraction != 1.0 && pEntity == tr.m_pEnt )
+ {
+ ClearMultiDamage( );
+ pEntity->DispatchTraceAttack( adjustedInfo, dir, &tr );
+ ApplyMultiDamage();
+ }
+ else
+ {
+ pEntity->TakeDamage( adjustedInfo );
+ }
+
+ // Now hit all triggers along the way that respond to damage...
+ pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, tr.endpos, dir );
+
+#if defined( GAME_DLL )
+ if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && ToBaseCombatCharacter( tr.m_pEnt ) )
+ {
+
+ // This is a total hack!!!
+ bool bIsPrimary = true;
+ CBasePlayer *player = ToBasePlayer( info.GetAttacker() );
+ CBaseCombatWeapon *pWeapon = player->GetActiveWeapon();
+ if ( pWeapon && FClassnameIs( pWeapon, "weapon_smg1" ) )
+ {
+ bIsPrimary = false;
+ }
+
+ gamestats->Event_WeaponHit( player, bIsPrimary, (pWeapon != NULL) ? player->GetActiveWeapon()->GetClassname() : "NULL", info );
+ }
+#endif
+ }
+}
+
+
+bool CGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
+{
+ if( pEdict->IsPlayer() )
+ {
+ if( GetVoiceGameMgr()->ClientCommand( static_cast<CBasePlayer*>(pEdict), args ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+void CGameRules::FrameUpdatePostEntityThink()
+{
+ VPROF( "CGameRules::FrameUpdatePostEntityThink" );
+ Think();
+}
+
+// Hook into the convar from the engine
+ConVar skill( "skill", "1" );
+
+void CGameRules::Think()
+{
+ GetVoiceGameMgr()->Update( gpGlobals->frametime );
+ SetSkillLevel( skill.GetInt() );
+
+ if ( log_verbose_enable.GetBool() )
+ {
+ if ( m_flNextVerboseLogOutput < gpGlobals->curtime )
+ {
+ ProcessVerboseLogOutput();
+ m_flNextVerboseLogOutput = gpGlobals->curtime + log_verbose_interval.GetFloat();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called at the end of GameFrame (i.e. after all game logic has run this frame)
+//-----------------------------------------------------------------------------
+void CGameRules::EndGameFrame( void )
+{
+ // If you hit this assert, it means something called AddMultiDamage() and didn't ApplyMultiDamage().
+ // The g_MultiDamage.m_hAttacker & g_MultiDamage.m_hInflictor should give help you figure out the culprit.
+ Assert( g_MultiDamage.IsClear() );
+ if ( !g_MultiDamage.IsClear() )
+ {
+ Warning("Unapplied multidamage left in the system:\nTarget: %s\nInflictor: %s\nAttacker: %s\nDamage: %.2f\n",
+ g_MultiDamage.GetTarget()->GetDebugName(),
+ g_MultiDamage.GetInflictor()->GetDebugName(),
+ g_MultiDamage.GetAttacker()->GetDebugName(),
+ g_MultiDamage.GetDamage() );
+ ApplyMultiDamage();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// trace line rules
+//-----------------------------------------------------------------------------
+float CGameRules::WeaponTraceEntity( CBaseEntity *pEntity, const Vector &vecStart, const Vector &vecEnd,
+ unsigned int mask, trace_t *ptr )
+{
+ UTIL_TraceEntity( pEntity, vecStart, vecEnd, mask, ptr );
+ return 1.0f;
+}
+
+
+void CGameRules::CreateStandardEntities()
+{
+ g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "player_manager", vec3_origin, vec3_angle );
+ g_pPlayerResource->AddEFlags( EFL_KEEP_ON_RECREATE_ENTITIES );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Inform client(s) they can mark the indicated achievement as completed (SERVER VERSION)
+// Input : filter - which client(s) to send this to
+// iAchievementID - The enumeration value of the achievement to mark (see TODO:Kerry, what file will have the mod's achievement enum?)
+//-----------------------------------------------------------------------------
+void CGameRules::MarkAchievement( IRecipientFilter& filter, char const *pchAchievementName )
+{
+ gamestats->Event_IncrementCountedStatistic( vec3_origin, pchAchievementName, 1.0f );
+
+ IAchievementMgr *pAchievementMgr = engine->GetAchievementMgr();
+ if ( !pAchievementMgr )
+ return;
+ pAchievementMgr->OnMapEvent( pchAchievementName );
+}
+
+#endif //} !CLIENT_DLL
+
+
+// ----------------------------------------------------------------------------- //
+// Shared CGameRules implementation.
+// ----------------------------------------------------------------------------- //
+
+CGameRules::~CGameRules()
+{
+ Assert( g_pGameRules == this );
+ g_pGameRules = NULL;
+}
+
+bool CGameRules::SwitchToNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
+{
+ return false;
+}
+
+CBaseCombatWeapon *CGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
+{
+ return NULL;
+}
+
+bool CGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
+{
+ if ( collisionGroup0 > collisionGroup1 )
+ {
+ // swap so that lowest is always first
+ ::V_swap(collisionGroup0,collisionGroup1);
+ }
+
+#ifndef HL2MP
+ if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
+ collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
+ {
+ return false;
+ }
+#endif
+
+ if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
+ {
+ // let debris and multiplayer objects collide
+ return true;
+ }
+
+ // --------------------------------------------------------------------------
+ // NOTE: All of this code assumes the collision groups have been sorted!!!!
+ // NOTE: Don't change their order without rewriting this code !!!
+ // --------------------------------------------------------------------------
+
+ // Don't bother if either is in a vehicle...
+ if (( collisionGroup0 == COLLISION_GROUP_IN_VEHICLE ) || ( collisionGroup1 == COLLISION_GROUP_IN_VEHICLE ))
+ return false;
+
+ if ( ( collisionGroup1 == COLLISION_GROUP_DOOR_BLOCKER ) && ( collisionGroup0 != COLLISION_GROUP_NPC ) )
+ return false;
+
+ if ( ( collisionGroup0 == COLLISION_GROUP_PLAYER ) && ( collisionGroup1 == COLLISION_GROUP_PASSABLE_DOOR ) )
+ return false;
+
+ if ( collisionGroup0 == COLLISION_GROUP_DEBRIS || collisionGroup0 == COLLISION_GROUP_DEBRIS_TRIGGER )
+ {
+ // put exceptions here, right now this will only collide with COLLISION_GROUP_NONE
+ return false;
+ }
+
+ // Dissolving guys only collide with COLLISION_GROUP_NONE
+ if ( (collisionGroup0 == COLLISION_GROUP_DISSOLVING) || (collisionGroup1 == COLLISION_GROUP_DISSOLVING) )
+ {
+ if ( collisionGroup0 != COLLISION_GROUP_NONE )
+ return false;
+ }
+
+ // doesn't collide with other members of this group
+ // or debris, but that's handled above
+ if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && collisionGroup1 == COLLISION_GROUP_INTERACTIVE_DEBRIS )
+ return false;
+
+#ifndef HL2MP
+ // This change was breaking HL2DM
+ // Adrian: TEST! Interactive Debris doesn't collide with the player.
+ if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && ( collisionGroup1 == COLLISION_GROUP_PLAYER_MOVEMENT || collisionGroup1 == COLLISION_GROUP_PLAYER ) )
+ return false;
+#endif
+
+ if ( collisionGroup0 == COLLISION_GROUP_BREAKABLE_GLASS && collisionGroup1 == COLLISION_GROUP_BREAKABLE_GLASS )
+ return false;
+
+ // interactive objects collide with everything except debris & interactive debris
+ if ( collisionGroup1 == COLLISION_GROUP_INTERACTIVE && collisionGroup0 != COLLISION_GROUP_NONE )
+ return false;
+
+ // Projectiles hit everything but debris, weapons, + other projectiles
+ if ( collisionGroup1 == COLLISION_GROUP_PROJECTILE )
+ {
+ if ( collisionGroup0 == COLLISION_GROUP_DEBRIS ||
+ collisionGroup0 == COLLISION_GROUP_WEAPON ||
+ collisionGroup0 == COLLISION_GROUP_PROJECTILE )
+ {
+ return false;
+ }
+ }
+
+ // Don't let vehicles collide with weapons
+ // Don't let players collide with weapons...
+ // Don't let NPCs collide with weapons
+ // Weapons are triggers, too, so they should still touch because of that
+ if ( collisionGroup1 == COLLISION_GROUP_WEAPON )
+ {
+ if ( collisionGroup0 == COLLISION_GROUP_VEHICLE ||
+ collisionGroup0 == COLLISION_GROUP_PLAYER ||
+ collisionGroup0 == COLLISION_GROUP_NPC )
+ {
+ return false;
+ }
+ }
+
+ // collision with vehicle clip entity??
+ if ( collisionGroup0 == COLLISION_GROUP_VEHICLE_CLIP || collisionGroup1 == COLLISION_GROUP_VEHICLE_CLIP )
+ {
+ // yes then if it's a vehicle, collide, otherwise no collision
+ // vehicle sorts lower than vehicle clip, so must be in 0
+ if ( collisionGroup0 == COLLISION_GROUP_VEHICLE )
+ return true;
+ // vehicle clip against non-vehicle, no collision
+ return false;
+ }
+
+ return true;
+}
+
+
+const CViewVectors* CGameRules::GetViewVectors() const
+{
+ return &g_DefaultViewVectors;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns how much damage the given ammo type should do to the victim
+// when fired by the attacker.
+// Input : pAttacker - Dude what shot the gun.
+// pVictim - Dude what done got shot.
+// nAmmoType - What been shot out.
+// Output : How much hurt to put on dude what done got shot (pVictim).
+//-----------------------------------------------------------------------------
+float CGameRules::GetAmmoDamage( CBaseEntity *pAttacker, CBaseEntity *pVictim, int nAmmoType )
+{
+ float flDamage = 0;
+ CAmmoDef *pAmmoDef = GetAmmoDef();
+
+ if ( pAttacker->IsPlayer() )
+ {
+ flDamage = pAmmoDef->PlrDamage( nAmmoType );
+ }
+ else
+ {
+ flDamage = pAmmoDef->NPCDamage( nAmmoType );
+ }
+
+ return flDamage;
+}
+
+
+#ifndef CLIENT_DLL
+const char *CGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
+{
+ if ( pPlayer && pPlayer->IsAlive() == false )
+ {
+ if ( bTeamOnly )
+ return "*DEAD*(TEAM)";
+ else
+ return "*DEAD*";
+ }
+
+ return "";
+}
+
+void CGameRules::CheckHaptics(CBasePlayer* pPlayer)
+{
+ // NVNT see if the client of pPlayer is using a haptic device.
+ const char *pszHH = engine->GetClientConVarValue( pPlayer->entindex(), "hap_HasDevice" );
+ if( pszHH )
+ {
+ int iHH = atoi( pszHH );
+ pPlayer->SetHaptics( iHH != 0 );
+ }
+}
+
+void CGameRules::ClientSettingsChanged( CBasePlayer *pPlayer )
+{
+ const char *pszName = engine->GetClientConVarValue( pPlayer->entindex(), "name" );
+
+ const char *pszOldName = pPlayer->GetPlayerName();
+
+ // msg everyone if someone changes their name, and it isn't the first time (changing no name to current name)
+ // Note, not using FStrEq so that this is case sensitive
+ if ( pszOldName[0] != 0 && Q_strcmp( pszOldName, pszName ) )
+ {
+ char text[256];
+ Q_snprintf( text,sizeof(text), "%s changed name to %s\n", pszOldName, pszName );
+
+ UTIL_ClientPrintAll( HUD_PRINTTALK, text );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetString( "oldname", pszOldName );
+ event->SetString( "newname", pszName );
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlayer->SetPlayerName( pszName );
+ }
+
+ const char *pszFov = engine->GetClientConVarValue( pPlayer->entindex(), "fov_desired" );
+ if ( pszFov )
+ {
+ int iFov = atoi(pszFov);
+ iFov = clamp( iFov, 75, 90 );
+ pPlayer->SetDefaultFOV( iFov );
+ }
+
+ // NVNT see if this user is still or has began using a haptic device
+ const char *pszHH = engine->GetClientConVarValue( pPlayer->entindex(), "hap_HasDevice" );
+ if( pszHH )
+ {
+ int iHH = atoi( pszHH );
+ pPlayer->SetHaptics( iHH != 0 );
+ }
+}
+
+CTacticalMissionManager *CGameRules::TacticalMissionManagerFactory( void )
+{
+ return new CTacticalMissionManager;
+}
+
+#endif