|
|
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "gamerules.h"
#include "ammodef.h"
#include "tier0/vprof.h"
#include "keyvalues.h"
#include "usermessages.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 "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 );
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)
);
#ifdef PORTAL2
ConVar sv_portal_players( "sv_portal_players", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_HIDDEN ); bool IsGameRulesMultiplayer() { return ( sv_portal_players.GetInt() > 1 ); } #endif
// ------------------------------------------------------------------------------------ //
// 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; }
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(); }
//-----------------------------------------------------------------------------
// Precache game-specific resources
//-----------------------------------------------------------------------------
void CGameRules::Precache( void ) { // Used by particle property
PrecacheEffect( "ParticleEffect" ); PrecacheEffect( "ParticleEffectStop" );
// Used by default impact system
PrecacheEffect( "GlassImpact" ); PrecacheEffect( "Impact" ); PrecacheEffect( "RagdollImpact" ); PrecacheEffect( "gunshotsplash" ); PrecacheEffect( "TracerSound" ); PrecacheEffect( "Tracer" );
// Used by physics impacts
PrecacheEffect( "watersplash" ); PrecacheEffect( "waterripple" );
// Used by UTIL_BloodImpact, which is used in many places
PrecacheEffect( "bloodimpact" ); }
//-----------------------------------------------------------------------------
// 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, pPlayer );
// 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 ); if ( pSpawnSpot == NULL ) return NULL;
pPlayer->SetLocalOrigin( pSpawnSpot->GetAbsOrigin() + Vector(0,0,1) ); pPlayer->SetAbsVelocity( vec3_origin ); pPlayer->SetLocalAngles( pSpawnSpot->GetLocalAngles() ); pPlayer->m_Local.m_viewPunchAngle = vec3_angle; pPlayer->m_Local.m_aimPunchAngle = vec3_angle; pPlayer->m_Local.m_aimPunchAngleVel = 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 ); char szExec[256];
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
Q_snprintf( szExec,sizeof(szExec), "exec skill%d.cfg\n", GetSkillLevel() );
engine->ServerCommand( szExec ); engine->ServerExecute(); #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 ) & 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) & 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() == WL_NotInWater) continue;
if (!bInWater && pEntity->GetWaterLevel() == WL_Eyes) 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.
float flDistanceToEnt = ( vecSrc - tr.endpos ).Length(); flAdjustedDamage = flDistanceToEnt * 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.SetRadius( flRadius ); 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 ) && !defined( _GAMECONSOLE )
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" ); SNPROF( "CGameRules::FrameUpdatePostEntityThink" ); Think(); }
// Hook into the convar from the engine
ConVar skill( "skill", "1", FCVAR_ARCHIVE );
void CGameRules::Think() { GetVoiceGameMgr()->Update( gpGlobals->frametime );
SetSkillLevel( skill.GetInt() ); }
//-----------------------------------------------------------------------------
// 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 ) { #ifndef _GAMECONSOLE
gamestats->Event_IncrementCountedStatistic( vec3_origin, pchAchievementName, 1.0f ); #endif
}
//-----------------------------------------------------------------------------
// Purpose: Wrapper allowing gamerules to change the way client-finding in PVS works
//-----------------------------------------------------------------------------
edict_t *CGameRules::DoFindClientInPVS( edict_t *pEdict, unsigned char *pvs, unsigned pvssize ) { return UTIL_FindClientInPVSGuts( pEdict, pvs, pvssize ); }
#endif //} !CLIENT_DLL
// ----------------------------------------------------------------------------- //
// Shared CGameRules implementation.
// ----------------------------------------------------------------------------- //
CGameRules::~CGameRules() { Assert( g_pGameRules == this ); g_pGameRules = NULL; }
bool CGameRules::Init() { #ifndef CLIENT_DLL
RefreshSkillData( true ); #endif
return true; }
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); }
if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) && collisionGroup1 == COLLISION_GROUP_PUSHAWAY ) { return false; }
if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY ) { // let debris and multiplayer objects collide
return true; }
// Only let projectile blocking debris collide with projectiles
if ( collisionGroup0 == COLLISION_GROUP_PROJECTILE && collisionGroup1 == COLLISION_GROUP_DEBRIS_BLOCK_PROJECTILE ) return true;
if ( collisionGroup0 == COLLISION_GROUP_DEBRIS_BLOCK_PROJECTILE || collisionGroup1 == COLLISION_GROUP_DEBRIS_BLOCK_PROJECTILE ) return false; // --------------------------------------------------------------------------
// 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;
// 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;
#ifdef PORTAL2
// Only hit something of the same group
if ( collisionGroup0 == COLLISION_GROUP_CAMERA_SOLID || collisionGroup1 == COLLISION_GROUP_CAMERA_SOLID ) { if ( collisionGroup0 != COLLISION_GROUP_CAMERA_SOLID || collisionGroup1 != COLLISION_GROUP_CAMERA_SOLID ) return false; }
// Only hit something of the same group
if ( collisionGroup0 == COLLISION_GROUP_PLACEMENT_SOLID || collisionGroup1 == COLLISION_GROUP_PLACEMENT_SOLID ) { if ( collisionGroup0 != COLLISION_GROUP_PLACEMENT_SOLID || collisionGroup1 != COLLISION_GROUP_PLACEMENT_SOLID ) return false; }
// Held objects shouldn't collide with players
// BUG: Not sure if we want this in MP, intention is to not collide with the holding player, not necessarily all.
if ( collisionGroup1 == COLLISION_GROUP_PLAYER_HELD && collisionGroup0 == COLLISION_GROUP_PLAYER ) return false;
if ( collisionGroup1 == COLLISION_GROUP_PLAYER_HELD && collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT ) return false; #endif // PORTAL2
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 || collisionGroup0 == COLLISION_GROUP_WEAPON ) //don't let weapons collide with weapons, they can pile up and choke the server
{ 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::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, this is case sensitive
if ( ( Q_strcmp( pszOldName, pszName ) != 0 ) && CanClientCustomizeOwnIdentity() ) { if ( pszOldName[0] != '\0' ) { 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, 1, 90 ); pPlayer->SetDefaultFOV( iFov ); } }
#endif
|