//========= 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: \n Target: %s \n Inflictor: %s \n Attacker: %s \n Damage: %.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 , 110 ) ;
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