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

//========= 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