//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
# include "cbase.h"
# include "physpropclientside.h"
# include "vcollide_parse.h"
# include "mapentities_shared.h"
# include "gamestringpool.h"
# include "props_shared.h"
# include "c_te_effect_dispatch.h"
# include "datacache/imdlcache.h"
# include "view.h"
# include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# define FADEOUT_TIME 1.0f
ConVar cl_phys_props_max ( " cl_phys_props_max " , " 300 " , 0 , " Maximum clientside physic props " ) ;
ConVar r_propsmaxdist ( " r_propsmaxdist " , " 1200 " , 0 , " Maximum visible distance " ) ;
ConVar cl_phys_props_enable ( " cl_phys_props_enable " , " 1 " , 0 , " Disable clientside physics props (must be set before loading a level) . " ) ;
ConVar cl_phys_props_respawndist ( " cl_phys_props_respawndist " , " 1500 " , 0 , " Minimum distance from the player that a clientside prop must be before it's allowed to respawn. " ) ;
ConVar cl_phys_props_respawnrate ( " cl_phys_props_respawnrate " , " 60 " , 0 , " Time, in seconds, between clientside prop respawns. " ) ;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
static int PropBreakablePrecacheAll ( int modelIndex )
{
CUtlVector < breakmodel_t > list ;
BreakModelList ( list , modelIndex , COLLISION_GROUP_NONE , 0 ) ;
return list . Count ( ) ;
}
static CUtlVector < C_PhysPropClientside * > s_PhysPropList ;
static CUtlVector < C_FuncPhysicsRespawnZone * > s_RespawnZoneList ;
C_PhysPropClientside * C_PhysPropClientside : : CreateNew ( bool bForce )
{
if ( ( s_PhysPropList . Count ( ) > = cl_phys_props_max . GetInt ( ) ) & & ! bForce )
{
DevMsg ( " Warning! Client physic props overflow *max %i). \n " , cl_phys_props_max . GetInt ( ) ) ;
return NULL ;
}
return new C_PhysPropClientside ( ) ;
}
C_PhysPropClientside : : C_PhysPropClientside ( )
{
m_fDeathTime = - 1 ;
m_impactEnergyScale = 1.0f ;
m_iHealth = 0 ;
m_iPhysicsMode = PHYSICS_MULTIPLAYER_AUTODETECT ;
m_flTouchDelta = 0 ;
m_pRespawnZone = NULL ;
s_PhysPropList . AddToTail ( this ) ;
}
C_PhysPropClientside : : ~ C_PhysPropClientside ( )
{
if ( m_pRespawnZone )
{
m_pRespawnZone - > PropDestroyed ( this ) ;
}
PhysCleanupFrictionSounds ( this ) ;
VPhysicsDestroyObject ( ) ;
s_PhysPropList . FindAndRemove ( this ) ;
}
void C_PhysPropClientside : : SetPhysicsMode ( int iMode )
{
if ( m_iPhysicsMode = = PHYSICS_MULTIPLAYER_AUTODETECT )
m_iPhysicsMode = iMode ;
}
//-----------------------------------------------------------------------------
// Should we collide?
//-----------------------------------------------------------------------------
bool C_PhysPropClientside : : KeyValue ( const char * szKeyName , const char * szValue )
{
if ( FStrEq ( szKeyName , " physdamagescale " ) )
{
m_impactEnergyScale = atof ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " health " ) )
{
m_iHealth = Q_atoi ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " spawnflags " ) )
{
m_spawnflags = Q_atoi ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " model " ) )
{
SetModelName ( AllocPooledString ( szValue ) ) ;
}
else if ( FStrEq ( szKeyName , " fademaxdist " ) )
{
m_fadeMaxDist = Q_atof ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " fademindist " ) )
{
m_fadeMinDist = Q_atof ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " fadescale " ) )
{
m_flFadeScale = Q_atof ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " inertiaScale " ) )
{
m_inertiaScale = Q_atof ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " skin " ) )
{
m_nSkin = Q_atoi ( szValue ) ;
}
else if ( FStrEq ( szKeyName , " physicsmode " ) )
{
m_iPhysicsMode = Q_atoi ( szValue ) ;
}
else
{
if ( ! BaseClass : : KeyValue ( szKeyName , szValue ) )
{
// key hasn't been handled
return false ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void C_PhysPropClientside : : StartTouch ( C_BaseEntity * pOther )
{
// Limit the amount of times we can bounce
if ( m_flTouchDelta < gpGlobals - > curtime )
{
HitSurface ( pOther ) ;
m_flTouchDelta = gpGlobals - > curtime + 0.1f ;
}
BaseClass : : StartTouch ( pOther ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void C_PhysPropClientside : : HitSurface ( C_BaseEntity * pOther )
{
if ( HasInteraction ( PROPINTER_WORLD_BLOODSPLAT ) )
{
trace_t tr ;
tr = BaseClass : : GetTouchTrace ( ) ;
if ( tr . m_pEnt )
{
UTIL_BloodDecalTrace ( & tr , BLOOD_COLOR_RED ) ;
}
}
}
void C_PhysPropClientside : : RecreateAll ( )
{
DestroyAll ( ) ;
if ( cl_phys_props_enable . GetInt ( ) )
{
ParseAllEntities ( engine - > GetMapEntitiesString ( ) ) ;
InitializePropRespawnZones ( ) ;
}
}
void C_PhysPropClientside : : DestroyAll ( )
{
while ( s_PhysPropList . Count ( ) > 0 )
{
C_PhysPropClientside * p = s_PhysPropList [ 0 ] ;
p - > Release ( ) ;
}
while ( s_RespawnZoneList . Count ( ) > 0 )
{
C_FuncPhysicsRespawnZone * p = s_RespawnZoneList [ 0 ] ;
p - > Release ( ) ;
}
}
void C_PhysPropClientside : : SetRespawnZone ( C_FuncPhysicsRespawnZone * pZone )
{
m_pRespawnZone = pZone ;
}
//-----------------------------------------------------------------------------
// Purpose: Parse this prop's data from the model, if it has a keyvalues section.
// Returns true only if this prop is using a model that has a prop_data section that's invalid.
//-----------------------------------------------------------------------------
int C_PhysPropClientside : : ParsePropData ( void )
{
KeyValues * modelKeyValues = new KeyValues ( " " ) ;
if ( ! modelKeyValues - > LoadFromBuffer ( modelinfo - > GetModelName ( GetModel ( ) ) , modelinfo - > GetModelKeyValueText ( GetModel ( ) ) ) )
{
modelKeyValues - > deleteThis ( ) ;
return PARSE_FAILED_NO_DATA ;
}
// Do we have a props section?
KeyValues * pkvPropData = modelKeyValues - > FindKey ( " prop_data " ) ;
if ( ! pkvPropData )
{
modelKeyValues - > deleteThis ( ) ;
return PARSE_FAILED_NO_DATA ;
}
int iResult = g_PropDataSystem . ParsePropFromKV ( this , pkvPropData , modelKeyValues ) ;
modelKeyValues - > deleteThis ( ) ;
return iResult ;
}
bool C_PhysPropClientside : : Initialize ( )
{
if ( InitializeAsClientEntity ( STRING ( GetModelName ( ) ) , RENDER_GROUP_OPAQUE_ENTITY ) = = false )
{
return false ;
}
const model_t * mod = GetModel ( ) ;
if ( mod )
{
Vector mins , maxs ;
modelinfo - > GetModelBounds ( mod , mins , maxs ) ;
SetCollisionBounds ( mins , maxs ) ;
}
solid_t tmpSolid ;
// Create the object in the physics system
if ( ! PhysModelParseSolid ( tmpSolid , this , GetModelIndex ( ) ) )
{
DevMsg ( " C_PhysPropClientside::Initialize: PhysModelParseSolid failed for entity %i. \n " , GetModelIndex ( ) ) ;
return false ;
}
else
{
m_pPhysicsObject = VPhysicsInitNormal ( SOLID_VPHYSICS , 0 , m_spawnflags & SF_PHYSPROP_START_ASLEEP , & tmpSolid ) ;
if ( ! m_pPhysicsObject )
{
// failed to create a physics object
DevMsg ( " C_PhysPropClientside::Initialize: VPhysicsInitNormal() failed for %s. \n " , STRING ( GetModelName ( ) ) ) ;
return false ;
}
}
// We want touch calls when we hit the world
unsigned int flags = VPhysicsGetObject ( ) - > GetCallbackFlags ( ) ;
VPhysicsGetObject ( ) - > SetCallbackFlags ( flags | CALLBACK_GLOBAL_TOUCH_STATIC ) ;
if ( m_spawnflags & SF_PHYSPROP_MOTIONDISABLED )
{
m_pPhysicsObject - > EnableMotion ( false ) ;
}
Spawn ( ) ; // loads breakable & prop data
if ( m_iPhysicsMode = = PHYSICS_MULTIPLAYER_AUTODETECT )
{
m_iPhysicsMode = GetAutoMultiplayerPhysicsMode (
CollisionProp ( ) - > OBBSize ( ) , m_pPhysicsObject - > GetMass ( ) ) ;
}
if ( m_spawnflags & SF_PHYSPROP_FORCE_SERVER_SIDE )
{
// forced to be server-side by map maker
return false ;
}
if ( m_iPhysicsMode ! = PHYSICS_MULTIPLAYER_CLIENTSIDE )
{
// spawn only clientside entities
return false ;
}
else
{
if ( engine - > IsInEditMode ( ) )
{
// don't spawn in map edit mode
return false ;
}
}
if ( m_fadeMinDist < 0 )
{
// start fading out at 75% of r_propsmaxdist
m_fadeMaxDist = r_propsmaxdist . GetFloat ( ) ;
m_fadeMinDist = r_propsmaxdist . GetFloat ( ) * 0.75f ;
}
// player can push it away
SetCollisionGroup ( COLLISION_GROUP_PUSHAWAY ) ;
UpdatePartitionListEntry ( ) ;
CollisionProp ( ) - > UpdatePartition ( ) ;
SetBlocksLOS ( false ) ; // this should be a small object
// Set up shadows; do it here so that objects can change shadowcasting state
CreateShadow ( ) ;
UpdateVisibility ( ) ;
SetNextClientThink ( CLIENT_THINK_NEVER ) ;
return true ;
}
void C_PhysPropClientside : : Spawn ( )
{
// Initialize damage modifiers. Must be done before baseclass spawn.
m_flDmgModBullet = 1.0 ;
m_flDmgModClub = 1.0 ;
m_flDmgModExplosive = 1.0 ;
BaseClass : : Spawn ( ) ;
// we don't really precache models here, just checking how many we have:
m_iNumBreakableChunks = PropBreakablePrecacheAll ( GetModelIndex ( ) ) ;
ParsePropData ( ) ;
// If we have no custom breakable chunks, see if we're breaking into generic ones
if ( ! m_iNumBreakableChunks )
{
if ( GetBreakableModel ( ) ! = NULL_STRING & & GetBreakableCount ( ) )
{
m_iNumBreakableChunks = GetBreakableCount ( ) ;
}
}
// Setup takedamage based upon the health we parsed earlier
if ( m_iHealth = = 0 )
{
m_takedamage = DAMAGE_NO ;
}
else
{
m_takedamage = DAMAGE_YES ;
}
}
void C_PhysPropClientside : : OnTakeDamage ( int iDamage ) // very simple version
{
if ( m_takedamage = = DAMAGE_NO )
return ;
m_iHealth - = iDamage ;
if ( m_iHealth < = 0 )
{
Break ( ) ;
}
}
float C_PhysPropClientside : : GetMass ( )
{
if ( VPhysicsGetObject ( ) )
{
return VPhysicsGetObject ( ) - > GetMass ( ) ;
}
return 0.0f ;
}
bool C_PhysPropClientside : : IsAsleep ( )
{
if ( VPhysicsGetObject ( ) )
{
return VPhysicsGetObject ( ) - > IsAsleep ( ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_PhysPropClientside : : ClientThink ( void )
{
if ( m_fDeathTime < 0 )
{
SetNextClientThink ( CLIENT_THINK_NEVER ) ;
return ;
}
if ( m_fDeathTime < = gpGlobals - > curtime )
{
Release ( ) ; // Die
return ;
}
// fade out
float alpha = ( m_fDeathTime - gpGlobals - > curtime ) / FADEOUT_TIME ;
SetRenderMode ( kRenderTransTexture ) ;
SetRenderColorA ( alpha * 256 ) ;
SetNextClientThink ( CLIENT_THINK_ALWAYS ) ;
}
void C_PhysPropClientside : : StartFadeOut ( float fDelay )
{
m_fDeathTime = gpGlobals - > curtime + fDelay + FADEOUT_TIME ;
SetNextClientThink ( gpGlobals - > curtime + fDelay ) ;
}
void C_PhysPropClientside : : Break ( )
{
m_takedamage = DAMAGE_NO ;
IPhysicsObject * pPhysics = VPhysicsGetObject ( ) ;
Vector velocity ;
AngularImpulse angVelocity ;
Vector origin ;
QAngle angles ;
AddSolidFlags ( FSOLID_NOT_SOLID ) ;
if ( pPhysics )
{
pPhysics - > GetVelocity ( & velocity , & angVelocity ) ;
pPhysics - > GetPosition ( & origin , & angles ) ;
pPhysics - > RecheckCollisionFilter ( ) ;
}
else
{
velocity = GetAbsVelocity ( ) ;
QAngleToAngularImpulse ( GetLocalAngularVelocity ( ) , angVelocity ) ;
origin = GetAbsOrigin ( ) ;
angles = GetAbsAngles ( ) ;
}
breakablepropparams_t params ( origin , angles , velocity , angVelocity ) ;
params . impactEnergyScale = m_impactEnergyScale ;
params . defCollisionGroup = GetCollisionGroup ( ) ;
if ( params . defCollisionGroup = = COLLISION_GROUP_NONE )
{
// don't automatically make anything COLLISION_GROUP_NONE or it will
// collide with debris being ejected by breaking
params . defCollisionGroup = COLLISION_GROUP_INTERACTIVE ;
}
// no damage/damage force? set a burst of 100 for some movement
params . defBurstScale = 100 ;
// spwan break chunks
PropBreakableCreateAll ( GetModelIndex ( ) , pPhysics , params , this , - 1 , false ) ;
Release ( ) ; // destroy object
}
void C_PhysPropClientside : : Clone ( Vector & velocity )
{
C_PhysPropClientside * pEntity = C_PhysPropClientside : : CreateNew ( ) ;
if ( ! pEntity )
return ;
pEntity - > m_spawnflags = m_spawnflags ;
// We never want to be motion disabled
pEntity - > m_spawnflags & = ~ SF_PHYSPROP_MOTIONDISABLED ;
pEntity - > SetDmgModBullet ( GetDmgModBullet ( ) ) ;
pEntity - > SetDmgModClub ( GetDmgModClub ( ) ) ;
pEntity - > SetDmgModExplosive ( GetDmgModExplosive ( ) ) ;
pEntity - > SetModelName ( GetModelName ( ) ) ;
pEntity - > SetLocalOrigin ( GetLocalOrigin ( ) ) ;
pEntity - > SetLocalAngles ( GetLocalAngles ( ) ) ;
pEntity - > SetOwnerEntity ( this ) ;
pEntity - > SetPhysicsMode ( PHYSICS_MULTIPLAYER_CLIENTSIDE ) ;
if ( ! pEntity - > Initialize ( ) )
{
pEntity - > Release ( ) ;
return ;
}
pEntity - > m_nSkin = m_nSkin ;
pEntity - > m_iHealth = m_iHealth ;
if ( pEntity - > m_iHealth = = 0 )
{
// if no health, don't collide with player anymore, don't take damage
pEntity - > m_takedamage = DAMAGE_NO ;
pEntity - > SetCollisionGroup ( COLLISION_GROUP_NONE ) ;
}
IPhysicsObject * pPhysicsObject = pEntity - > VPhysicsGetObject ( ) ;
if ( pPhysicsObject )
{
// randomize velocity by 5%
float rndf = RandomFloat ( - 0.025 , 0.025 ) ;
Vector rndVel = velocity + rndf * velocity ;
pPhysicsObject - > AddVelocity ( & rndVel , NULL ) ;
}
else
{
// failed to create a physics object
pEntity - > Release ( ) ;
}
}
void C_PhysPropClientside : : ImpactTrace ( trace_t * pTrace , int iDamageType , const char * pCustomImpactName )
{
VPROF ( " C_PhysPropClientside::ImpactTrace " ) ;
IPhysicsObject * pPhysicsObject = VPhysicsGetObject ( ) ;
if ( ! pPhysicsObject )
return ;
Vector dir = pTrace - > endpos - pTrace - > startpos ;
int iDamage = 0 ;
if ( iDamageType = = DMG_BLAST )
{
iDamage = VectorLength ( dir ) ;
dir * = 500 ; // adjust impact strenght
// apply force at object mass center
pPhysicsObject - > ApplyForceCenter ( dir ) ;
}
else
{
Vector hitpos ;
VectorMA ( pTrace - > startpos , pTrace - > fraction , dir , hitpos ) ;
VectorNormalize ( dir ) ;
// guess avg damage
if ( iDamageType = = DMG_BULLET )
{
iDamage = 30 ;
}
else
{
iDamage = 50 ;
}
dir * = 4000 ; // adjust impact strenght
// apply force where we hit it
pPhysicsObject - > ApplyForceOffset ( dir , hitpos ) ;
// Build the impact data
CEffectData data ;
data . m_vOrigin = pTrace - > endpos ;
data . m_vStart = pTrace - > startpos ;
data . m_nSurfaceProp = pTrace - > surface . surfaceProps ;
data . m_nDamageType = iDamageType ;
data . m_nHitBox = pTrace - > hitbox ;
data . m_hEntity = GetRefEHandle ( ) ;
// Send it on its way
if ( ! pCustomImpactName )
{
DispatchEffect ( " Impact " , data ) ;
}
else
{
DispatchEffect ( pCustomImpactName , data ) ;
}
}
// Clone( dir ); // debug code
OnTakeDamage ( iDamage ) ;
}
const char * C_PhysPropClientside : : ParseEntity ( const char * pEntData )
{
CEntityMapData entData ( ( char * ) pEntData ) ;
char className [ MAPKEY_MAXLENGTH ] ;
MDLCACHE_CRITICAL_SECTION ( ) ;
if ( ! entData . ExtractValue ( " classname " , className ) )
{
Error ( " classname missing from entity! \n " ) ;
}
if ( ! Q_strcmp ( className , " prop_physics_multiplayer " ) )
{
// always force clientside entitis placed in maps
C_PhysPropClientside * pEntity = C_PhysPropClientside : : CreateNew ( true ) ;
if ( pEntity )
{ // Set up keyvalues.
pEntity - > ParseMapData ( & entData ) ;
if ( ! pEntity - > Initialize ( ) )
pEntity - > Release ( ) ;
return entData . CurrentBufferPosition ( ) ;
}
}
if ( ! Q_strcmp ( className , " func_proprrespawnzone " ) )
{
C_FuncPhysicsRespawnZone * pEntity = new C_FuncPhysicsRespawnZone ( ) ;
if ( pEntity )
{
// Set up keyvalues.
pEntity - > ParseMapData ( & entData ) ;
if ( ! pEntity - > Initialize ( ) )
pEntity - > Release ( ) ;
return entData . CurrentBufferPosition ( ) ;
}
}
// Just skip past all the keys.
char keyName [ MAPKEY_MAXLENGTH ] ;
char value [ MAPKEY_MAXLENGTH ] ;
if ( entData . GetFirstKey ( keyName , value ) )
{
do
{
}
while ( entData . GetNextKey ( keyName , value ) ) ;
}
//
// Return the current parser position in the data block
//
return entData . CurrentBufferPosition ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Only called on BSP load. Parses and spawns all the entities in the BSP.
// Input : pMapData - Pointer to the entity data block to parse.
//-----------------------------------------------------------------------------
void C_PhysPropClientside : : ParseAllEntities ( const char * pMapData )
{
int nEntities = 0 ;
char szTokenBuffer [ MAPKEY_MAXLENGTH ] ;
//
// Loop through all entities in the map data, creating each.
//
for ( ; true ; pMapData = MapEntity_SkipToNextEntity ( pMapData , szTokenBuffer ) )
{
//
// Parse the opening brace.
//
char token [ MAPKEY_MAXLENGTH ] ;
pMapData = MapEntity_ParseToken ( pMapData , token ) ;
//
// Check to see if we've finished or not.
//
if ( ! pMapData )
break ;
if ( token [ 0 ] ! = ' { ' )
{
Error ( " MapEntity_ParseAllEntities: found %s when expecting { " , token ) ;
continue ;
}
//
// Parse the entity and add it to the spawn list.
//
pMapData = ParseEntity ( pMapData ) ;
nEntities + + ;
}
}
CBaseEntity * BreakModelCreateSingle ( CBaseEntity * pOwner , breakmodel_t * pModel , const Vector & position ,
const QAngle & angles , const Vector & velocity , const AngularImpulse & angVelocity , int nSkin , const breakablepropparams_t & params )
{
C_PhysPropClientside * pEntity = C_PhysPropClientside : : CreateNew ( ) ;
if ( ! pEntity )
return NULL ;
// UNDONE: Allow .qc to override spawnflags for child pieces
C_PhysPropClientside * pBreakableOwner = dynamic_cast < C_PhysPropClientside * > ( pOwner ) ;
// Inherit the base object's damage modifiers
if ( pBreakableOwner )
{
pEntity - > SetEffects ( pBreakableOwner - > GetEffects ( ) ) ;
pEntity - > m_spawnflags = pBreakableOwner - > m_spawnflags ;
// We never want to be motion disabled
pEntity - > m_spawnflags & = ~ SF_PHYSPROP_MOTIONDISABLED ;
pEntity - > SetDmgModBullet ( pBreakableOwner - > GetDmgModBullet ( ) ) ;
pEntity - > SetDmgModClub ( pBreakableOwner - > GetDmgModClub ( ) ) ;
pEntity - > SetDmgModExplosive ( pBreakableOwner - > GetDmgModExplosive ( ) ) ;
// FIXME: If this was created from a client-side entity which was in the
// middle of ramping the fade scale, we're screwed.
pEntity - > CopyFadeFrom ( pBreakableOwner ) ;
}
pEntity - > SetModelName ( AllocPooledString ( pModel - > modelName ) ) ;
pEntity - > SetLocalOrigin ( position ) ;
pEntity - > SetLocalAngles ( angles ) ;
pEntity - > SetOwnerEntity ( pOwner ) ;
pEntity - > SetPhysicsMode ( PHYSICS_MULTIPLAYER_CLIENTSIDE ) ;
if ( ! pEntity - > Initialize ( ) )
{
pEntity - > Release ( ) ;
return NULL ;
}
pEntity - > m_nSkin = nSkin ;
pEntity - > m_iHealth = pModel - > health ;
# ifdef TF_CLIENT_DLL
pEntity - > SetCollisionGroup ( COLLISION_GROUP_DEBRIS ) ;
# endif
# ifdef DOD_DLL
pEntity - > SetCollisionGroup ( COLLISION_GROUP_DEBRIS ) ;
# endif
if ( pModel - > health = = 0 )
{
// if no health, don't collide with player anymore, don't take damage
pEntity - > m_takedamage = DAMAGE_NO ;
if ( pEntity - > GetCollisionGroup ( ) = = COLLISION_GROUP_PUSHAWAY )
{
pEntity - > SetCollisionGroup ( COLLISION_GROUP_NONE ) ;
}
}
if ( pModel - > fadeTime > 0 )
{
pEntity - > StartFadeOut ( pModel - > fadeTime ) ;
}
if ( pModel - > fadeMinDist > 0 & & pModel - > fadeMaxDist > = pModel - > fadeMinDist )
{
pEntity - > SetFadeMinMax ( pModel - > fadeMinDist , pModel - > fadeMaxDist ) ;
}
if ( pModel - > isRagdoll )
{
DevMsg ( " BreakModelCreateSingle: clientside doesn't support ragdoll breakmodels. \n " ) ;
}
IPhysicsObject * pPhysicsObject = pEntity - > VPhysicsGetObject ( ) ;
if ( pPhysicsObject )
{
// randomize velocity by 5%
float rndf = RandomFloat ( - 0.025 , 0.025 ) ;
Vector rndVel = velocity + rndf * velocity ;
pPhysicsObject - > AddVelocity ( & rndVel , & angVelocity ) ;
}
else
{
// failed to create a physics object
pEntity - > Release ( ) ;
return NULL ;
}
return pEntity ;
}
//======================================================================================================================
// PROP RESPAWN ZONES
//======================================================================================================================
C_FuncPhysicsRespawnZone : : C_FuncPhysicsRespawnZone ( void )
{
s_RespawnZoneList . AddToTail ( this ) ;
}
C_FuncPhysicsRespawnZone : : ~ C_FuncPhysicsRespawnZone ( void )
{
s_RespawnZoneList . FindAndRemove ( this ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_FuncPhysicsRespawnZone : : KeyValue ( const char * szKeyName , const char * szValue )
{
if ( FStrEq ( szKeyName , " model " ) )
{
SetModelName ( AllocPooledString ( szValue ) ) ;
}
else
{
if ( ! BaseClass : : KeyValue ( szKeyName , szValue ) )
{
// key hasn't been handled
return false ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_FuncPhysicsRespawnZone : : Initialize ( void )
{
if ( InitializeAsClientEntity ( STRING ( GetModelName ( ) ) , RENDER_GROUP_OPAQUE_ENTITY ) = = false )
return false ;
SetSolid ( SOLID_BSP ) ;
AddSolidFlags ( FSOLID_NOT_SOLID ) ;
AddSolidFlags ( FSOLID_TRIGGER ) ;
SetMoveType ( MOVETYPE_NONE ) ;
const model_t * mod = GetModel ( ) ;
if ( mod )
{
Vector mins , maxs ;
modelinfo - > GetModelBounds ( mod , mins , maxs ) ;
SetCollisionBounds ( mins , maxs ) ;
}
Spawn ( ) ;
AddEffects ( EF_NODRAW ) ;
UpdatePartitionListEntry ( ) ;
CollisionProp ( ) - > UpdatePartition ( ) ;
UpdateVisibility ( ) ;
SetNextClientThink ( gpGlobals - > curtime + ( cl_phys_props_respawnrate . GetFloat ( ) * RandomFloat ( 1.0 , 1.1 ) ) ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Iterate over all prop respawn zones and find the props inside them
//-----------------------------------------------------------------------------
void C_PhysPropClientside : : InitializePropRespawnZones ( void )
{
for ( int i = 0 ; i < s_RespawnZoneList . Count ( ) ; i + + )
{
C_FuncPhysicsRespawnZone * pZone = s_RespawnZoneList [ i ] ;
pZone - > InitializePropsWithin ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_FuncPhysicsRespawnZone : : InitializePropsWithin ( void )
{
// Find the props inside this zone
for ( int i = 0 ; i < s_PhysPropList . Count ( ) ; i + + )
{
C_PhysPropClientside * pProp = s_PhysPropList [ i ] ;
if ( CollisionProp ( ) - > IsPointInBounds ( pProp - > WorldSpaceCenter ( ) ) )
{
pProp - > SetRespawnZone ( this ) ;
// This is a crappy way to do this
int index = m_PropList . AddToTail ( ) ;
m_PropList [ index ] . iszModelName = pProp - > GetModelName ( ) ;
m_PropList [ index ] . vecOrigin = pProp - > GetAbsOrigin ( ) ;
m_PropList [ index ] . vecAngles = pProp - > GetAbsAngles ( ) ;
m_PropList [ index ] . iSkin = pProp - > m_nSkin ;
m_PropList [ index ] . iHealth = pProp - > m_iHealth ;
m_PropList [ index ] . iSpawnFlags = pProp - > m_spawnflags ;
m_PropList [ index ] . hClientEntity = pProp - > GetClientHandle ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_FuncPhysicsRespawnZone : : PropDestroyed ( C_PhysPropClientside * pProp )
{
for ( int i = 0 ; i < m_PropList . Count ( ) ; i + + )
{
if ( pProp - > GetClientHandle ( ) = = m_PropList [ i ] . hClientEntity )
{
m_PropList [ i ] . hClientEntity = INVALID_CLIENTENTITY_HANDLE ;
return ;
}
}
// We've got a clientside prop that thinks it belongs to a zone that doesn't recognise it. Shouldn't happen.
Assert ( 0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_FuncPhysicsRespawnZone : : CanMovePropAt ( Vector vecOrigin , const Vector & vecMins , const Vector & vecMaxs )
{
float flDist = cl_phys_props_respawndist . GetFloat ( ) ;
// Do a distance check first. We don't want to move props when the player is near 'em.
if ( ( MainViewOrigin ( ) - vecOrigin ) . LengthSqr ( ) < ( flDist * flDist ) )
return false ;
// Now make sure it's not in view
if ( engine - > IsBoxInViewCluster ( vecMins + vecOrigin , vecMaxs + vecOrigin ) )
return false ;
if ( ! engine - > CullBox ( vecMins + vecOrigin , vecMaxs + vecOrigin ) )
return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_FuncPhysicsRespawnZone : : RespawnProps ( void )
{
for ( int i = 0 ; i < m_PropList . Count ( ) ; i + + )
{
if ( m_PropList [ i ] . hClientEntity = = INVALID_CLIENTENTITY_HANDLE )
{
if ( ! CanMovePropAt ( m_PropList [ i ] . vecOrigin , - Vector ( 32 , 32 , 32 ) , Vector ( 32 , 32 , 32 ) ) )
continue ;
// This is a crappy way to do this
C_PhysPropClientside * pEntity = C_PhysPropClientside : : CreateNew ( ) ;
if ( pEntity )
{
pEntity - > m_spawnflags = m_PropList [ i ] . iSpawnFlags ;
pEntity - > SetModelName ( m_PropList [ i ] . iszModelName ) ;
pEntity - > SetAbsOrigin ( m_PropList [ i ] . vecOrigin ) ;
pEntity - > SetAbsAngles ( m_PropList [ i ] . vecAngles ) ;
pEntity - > SetPhysicsMode ( PHYSICS_MULTIPLAYER_CLIENTSIDE ) ;
pEntity - > m_nSkin = m_PropList [ i ] . iSkin ;
pEntity - > m_iHealth = m_PropList [ i ] . iHealth ;
if ( pEntity - > m_iHealth = = 0 )
{
pEntity - > m_takedamage = DAMAGE_NO ;
}
if ( ! pEntity - > Initialize ( ) )
{
pEntity - > Release ( ) ;
}
else
{
pEntity - > SetRespawnZone ( this ) ;
m_PropList [ i ] . hClientEntity = pEntity - > GetClientHandle ( ) ;
}
}
}
else
{
// If the prop has moved, bring it back
C_BaseEntity * pEntity = ClientEntityList ( ) . GetBaseEntityFromHandle ( m_PropList [ i ] . hClientEntity ) ;
if ( pEntity )
{
if ( ! CollisionProp ( ) - > IsPointInBounds ( pEntity - > WorldSpaceCenter ( ) ) )
{
Vector vecMins , vecMaxs ;
pEntity - > CollisionProp ( ) - > WorldSpaceSurroundingBounds ( & vecMins , & vecMaxs ) ;
if ( ! CanMovePropAt ( m_PropList [ i ] . vecOrigin , vecMins , vecMaxs ) | |
! CanMovePropAt ( pEntity - > GetAbsOrigin ( ) , vecMins , vecMaxs ) )
continue ;
pEntity - > SetAbsOrigin ( m_PropList [ i ] . vecOrigin ) ;
pEntity - > SetAbsAngles ( m_PropList [ i ] . vecAngles ) ;
IPhysicsObject * pPhys = pEntity - > VPhysicsGetObject ( ) ;
if ( pPhys )
{
pPhys - > SetPosition ( pEntity - > GetAbsOrigin ( ) , pEntity - > GetAbsAngles ( ) , true ) ;
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_FuncPhysicsRespawnZone : : ClientThink ( void )
{
RespawnProps ( ) ;
SetNextClientThink ( gpGlobals - > curtime + ( cl_phys_props_respawnrate . GetFloat ( ) * RandomFloat ( 1.0 , 1.1 ) ) ) ;
}