From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/client/physpropclientside.cpp | 2060 ++++++++++++++--------------- 1 file changed, 1030 insertions(+), 1030 deletions(-) (limited to 'mp/src/game/client/physpropclientside.cpp') diff --git a/mp/src/game/client/physpropclientside.cpp b/mp/src/game/client/physpropclientside.cpp index c516e810..3dd2d7b5 100644 --- a/mp/src/game/client/physpropclientside.cpp +++ b/mp/src/game/client/physpropclientside.cpp @@ -1,1031 +1,1031 @@ -//========= 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 list; - - BreakModelList( list, modelIndex, COLLISION_GROUP_NONE, 0 ); - return list.Count(); -} - -static CUtlVector s_PhysPropList; -static CUtlVector 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 ¶ms ) -{ - C_PhysPropClientside *pEntity = C_PhysPropClientside::CreateNew(); - - if ( !pEntity ) - return NULL; - - // UNDONE: Allow .qc to override spawnflags for child pieces - C_PhysPropClientside *pBreakableOwner = dynamic_cast(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)) ); +//========= 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 list; + + BreakModelList( list, modelIndex, COLLISION_GROUP_NONE, 0 ); + return list.Count(); +} + +static CUtlVector s_PhysPropList; +static CUtlVector 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 ¶ms ) +{ + C_PhysPropClientside *pEntity = C_PhysPropClientside::CreateNew(); + + if ( !pEntity ) + return NULL; + + // UNDONE: Allow .qc to override spawnflags for child pieces + C_PhysPropClientside *pBreakableOwner = dynamic_cast(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)) ); } \ No newline at end of file -- cgit v1.2.3