aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/physics_bone_follower.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/physics_bone_follower.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/physics_bone_follower.cpp')
-rw-r--r--sp/src/game/server/physics_bone_follower.cpp970
1 files changed, 485 insertions, 485 deletions
diff --git a/sp/src/game/server/physics_bone_follower.cpp b/sp/src/game/server/physics_bone_follower.cpp
index 76630e3a..fc980cc3 100644
--- a/sp/src/game/server/physics_bone_follower.cpp
+++ b/sp/src/game/server/physics_bone_follower.cpp
@@ -1,485 +1,485 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "cbase.h"
-#include "bone_setup.h"
-#include "physics_bone_follower.h"
-#include "vcollide_parse.h"
-#include "saverestore_utlvector.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-BEGIN_SIMPLE_DATADESC( physfollower_t )
-DEFINE_FIELD( boneIndex, FIELD_INTEGER ),
-DEFINE_FIELD( hFollower, FIELD_EHANDLE ),
-END_DATADESC()
-
-BEGIN_SIMPLE_DATADESC( CBoneFollowerManager )
-DEFINE_GLOBAL_FIELD( m_iNumBones, FIELD_INTEGER ),
-DEFINE_GLOBAL_UTLVECTOR( m_physBones, FIELD_EMBEDDED ),
-END_DATADESC()
-
-//================================================================================================================
-// BONE FOLLOWER MANAGER
-//================================================================================================================
-CBoneFollowerManager::CBoneFollowerManager()
-{
- m_iNumBones = 0;
-}
-
-CBoneFollowerManager::~CBoneFollowerManager()
-{
- // if this fires then someone isn't destroying their bonefollowers in UpdateOnRemove
- Assert(m_iNumBones==0);
- DestroyBoneFollowers();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// iNumBones -
-// **pFollowerBoneNames -
-//-----------------------------------------------------------------------------
-void CBoneFollowerManager::InitBoneFollowers( CBaseAnimating *pParentEntity, int iNumBones, const char **pFollowerBoneNames )
-{
- m_iNumBones = iNumBones;
- m_physBones.EnsureCount( iNumBones );
-
- // Now init all the bones
- for ( int i = 0; i < iNumBones; i++ )
- {
- CreatePhysicsFollower( pParentEntity, m_physBones[i], pFollowerBoneNames[i], NULL );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBoneFollowerManager::AddBoneFollower( CBaseAnimating *pParentEntity, const char *pFollowerBoneName, solid_t *pSolid )
-{
- m_iNumBones++;
-
- int iIndex = m_physBones.AddToTail();
- CreatePhysicsFollower( pParentEntity, m_physBones[iIndex], pFollowerBoneName, pSolid );
-}
-
-// walk the hitboxes and find the first one that is attached to the physics bone in question
-// return the hitgroup of that box
-static int HitGroupFromPhysicsBone( CBaseAnimating *pAnim, int physicsBone )
-{
- CStudioHdr *pStudioHdr = pAnim->GetModelPtr( );
- mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnim->m_nHitboxSet );
- for ( int i = 0; i < set->numhitboxes; i++ )
- {
- if ( pStudioHdr->pBone( set->pHitbox(i)->bone )->physicsbone == physicsBone )
- {
- return set->pHitbox(i)->group;
- }
- }
-
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &follow -
-// *pBoneName -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBoneFollowerManager::CreatePhysicsFollower( CBaseAnimating *pParentEntity, physfollower_t &follow, const char *pBoneName, solid_t *pSolid )
-{
- CStudioHdr *pStudioHdr = pParentEntity->GetModelPtr();
- matrix3x4_t boneToWorld;
- solid_t solidTmp;
-
- Vector bonePosition;
- QAngle boneAngles;
-
- int boneIndex = Studio_BoneIndexByName( pStudioHdr, pBoneName );
-
- if ( boneIndex >= 0 )
- {
- mstudiobone_t *pBone = pStudioHdr->pBone( boneIndex );
-
- int physicsBone = pBone->physicsbone;
- if ( !pSolid )
- {
- if ( !PhysModelParseSolidByIndex( solidTmp, pParentEntity, pParentEntity->GetModelIndex(), physicsBone ) )
- return false;
- pSolid = &solidTmp;
- }
-
- // fixup in case ragdoll is assigned to a parent of the requested follower bone
- follow.boneIndex = Studio_BoneIndexByName( pStudioHdr, pSolid->name );
- if ( follow.boneIndex < 0 )
- {
- follow.boneIndex = boneIndex;
- }
-
- pParentEntity->GetBoneTransform( follow.boneIndex, boneToWorld );
- MatrixAngles( boneToWorld, boneAngles, bonePosition );
-
- follow.hFollower = CBoneFollower::Create( pParentEntity, STRING(pParentEntity->GetModelName()), *pSolid, bonePosition, boneAngles );
- follow.hFollower->SetTraceData( physicsBone, HitGroupFromPhysicsBone( pParentEntity, physicsBone ) );
- follow.hFollower->SetBlocksLOS( pParentEntity->BlocksLOS() );
- return true;
- }
- else
- {
- Warning( "ERROR: Tried to create bone follower on invalid bone %s\n", pBoneName );
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBoneFollowerManager::UpdateBoneFollowers( CBaseAnimating *pParentEntity )
-{
- if ( m_iNumBones )
- {
- matrix3x4_t boneToWorld;
- Vector bonePosition;
- QAngle boneAngles;
- for ( int i = 0; i < m_iNumBones; i++ )
- {
- if ( !m_physBones[i].hFollower )
- continue;
-
- pParentEntity->GetBoneTransform( m_physBones[i].boneIndex, boneToWorld );
- MatrixAngles( boneToWorld, boneAngles, bonePosition );
- m_physBones[i].hFollower->UpdateFollower( bonePosition, boneAngles, 0.1 );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBoneFollowerManager::DestroyBoneFollowers( void )
-{
- for ( int i = 0; i < m_iNumBones; i++ )
- {
- if ( !m_physBones[i].hFollower )
- continue;
-
- UTIL_Remove( m_physBones[i].hFollower );
- m_physBones[i].hFollower = NULL;
- }
-
- m_physBones.Purge();
- m_iNumBones = 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-physfollower_t *CBoneFollowerManager::GetBoneFollower( int iFollowerIndex )
-{
- Assert( iFollowerIndex >= 0 && iFollowerIndex < m_iNumBones );
- if ( iFollowerIndex >= 0 && iFollowerIndex < m_iNumBones )
- return &m_physBones[iFollowerIndex];
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Retrieve the index for a supplied bone follower
-// Input : *pFollower - Bone follower to look up
-// Output : -1 if not found, otherwise the index of the bone follower
-//-----------------------------------------------------------------------------
-int CBoneFollowerManager::GetBoneFollowerIndex( CBoneFollower *pFollower )
-{
- if ( pFollower == NULL )
- return -1;
-
- for ( int i = 0; i < m_iNumBones; i++ )
- {
- if ( !m_physBones[i].hFollower )
- continue;
-
- if ( m_physBones[i].hFollower == pFollower )
- return i;
- }
-
- return -1;
-}
-
-//================================================================================================================
-// BONE FOLLOWER
-//================================================================================================================
-
-//---------------------------------------------------------
-// Save/Restore
-//---------------------------------------------------------
-BEGIN_DATADESC( CBoneFollower )
-
- DEFINE_FIELD( m_modelIndex, FIELD_MODELINDEX ),
- DEFINE_FIELD( m_solidIndex, FIELD_INTEGER ),
- DEFINE_FIELD( m_physicsBone, FIELD_INTEGER ),
- DEFINE_FIELD( m_hitGroup, FIELD_INTEGER ),
-
-END_DATADESC()
-
-IMPLEMENT_SERVERCLASS_ST( CBoneFollower, DT_BoneFollower )
- SendPropModelIndex(SENDINFO(m_modelIndex)),
- SendPropInt(SENDINFO(m_solidIndex), 6, SPROP_UNSIGNED ),
-END_SEND_TABLE()
-
-
-bool CBoneFollower::Init( CBaseEntity *pOwner, const char *pModelName, solid_t &solid, const Vector &position, const QAngle &orientation )
-{
- SetOwnerEntity( pOwner );
- UTIL_SetModel( this, pModelName );
-
- AddEffects( EF_NODRAW ); // invisible
-
- m_modelIndex = modelinfo->GetModelIndex( pModelName );
- m_solidIndex = solid.index;
- SetAbsOrigin( position );
- SetAbsAngles( orientation );
- SetMoveType( MOVETYPE_PUSH );
- SetSolid( SOLID_VPHYSICS );
- SetCollisionGroup( pOwner->GetCollisionGroup() );
- AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
- solid.params.pGameData = (void *)this;
- IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false, &solid );
- if ( !pPhysics )
- return false;
-
- // we can't use the default model bounds because each entity is only one bone of the model
- // so compute the OBB of the physics model and use that.
- Vector mins, maxs;
- physcollision->CollideGetAABB( &mins, &maxs, pPhysics->GetCollide(), vec3_origin, vec3_angle );
- SetCollisionBounds( mins, maxs );
-
- pPhysics->SetCallbackFlags( pPhysics->GetCallbackFlags() | CALLBACK_GLOBAL_TOUCH );
- pPhysics->EnableGravity( false );
- // This is not a normal shadow controller that is trying to go to a space occupied by an entity in the game physics
- // This entity is not running PhysicsPusher(), so Vphysics is supposed to move it
- // This line of code informs vphysics of that fact
- if ( pOwner->IsNPC() )
- {
- pPhysics->GetShadowController()->SetPhysicallyControlled( true );
- }
-
- return true;
-}
-
-int CBoneFollower::UpdateTransmitState()
-{
- // Send to the client for client-side collisions and visualization
- return SetTransmitState( FL_EDICT_PVSCHECK );
-}
-
-void CBoneFollower::VPhysicsUpdate( IPhysicsObject *pPhysics )
-{
- Vector origin;
- QAngle angles;
-
- pPhysics->GetPosition( &origin, &angles );
-
- SetAbsOrigin( origin );
- SetAbsAngles( angles );
-}
-
-// a little helper class to temporarily change the physics object
-// for an entity - and change it back when it goes out of scope.
-class CPhysicsSwapTemp
-{
-public:
- CPhysicsSwapTemp( CBaseEntity *pEntity, IPhysicsObject *pTmpPhysics )
- {
- Assert(pEntity);
- Assert(pTmpPhysics);
- m_pEntity = pEntity;
- m_pPhysics = m_pEntity->VPhysicsGetObject();
- if ( m_pPhysics )
- {
- m_pEntity->VPhysicsSwapObject( pTmpPhysics );
- }
- else
- {
- m_pEntity->VPhysicsSetObject( pTmpPhysics );
- }
- }
- ~CPhysicsSwapTemp()
- {
- m_pEntity->VPhysicsSwapObject( m_pPhysics );
- }
-
-private:
- CBaseEntity *m_pEntity;
- IPhysicsObject *m_pPhysics;
-};
-
-
-void CBoneFollower::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
-{
- CBaseEntity *pOwner = GetOwnerEntity();
- if ( pOwner )
- {
- CPhysicsSwapTemp tmp(pOwner, pEvent->pObjects[index] );
- pOwner->VPhysicsCollision( index, pEvent );
- }
-}
-
-void CBoneFollower::VPhysicsShadowCollision( int index, gamevcollisionevent_t *pEvent )
-{
- CBaseEntity *pOwner = GetOwnerEntity();
- if ( pOwner )
- {
- CPhysicsSwapTemp tmp(pOwner, pEvent->pObjects[index] );
- pOwner->VPhysicsShadowCollision( index, pEvent );
- }
-}
-
-void CBoneFollower::VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit )
-{
- CBaseEntity *pOwner = GetOwnerEntity();
- if ( pOwner )
- {
- CPhysicsSwapTemp tmp(pOwner, pObject );
- pOwner->VPhysicsFriction( pObject, energy, surfaceProps, surfacePropsHit );
- }
-}
-
-bool CBoneFollower::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
-{
- vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() );
- Assert( pCollide && pCollide->solidCount > m_solidIndex );
-
- UTIL_ClearTrace( trace );
-
- physcollision->TraceBox( ray, pCollide->solids[m_solidIndex], GetAbsOrigin(), GetAbsAngles(), &trace );
-
- if ( trace.fraction >= 1 )
- return false;
-
- // return owner as trace hit
- trace.m_pEnt = GetOwnerEntity();
- trace.hitgroup = m_hitGroup;
- trace.physicsbone = m_physicsBone;
- return true;
-}
-
-void CBoneFollower::UpdateFollower( const Vector &position, const QAngle &orientation, float flInterval )
-{
- // UNDONE: Shadow update needs timing info?
- VPhysicsGetObject()->UpdateShadow( position, orientation, false, flInterval );
-}
-
-void CBoneFollower::SetTraceData( int physicsBone, int hitGroup )
-{
- m_hitGroup = hitGroup;
- m_physicsBone = physicsBone;
-}
-
-CBoneFollower *CBoneFollower::Create( CBaseEntity *pOwner, const char *pModelName, solid_t &solid, const Vector &position, const QAngle &orientation )
-{
- CBoneFollower *pFollower = (CBoneFollower *)CreateEntityByName( "phys_bone_follower" );
- if ( pFollower )
- {
- pFollower->Init( pOwner, pModelName, solid, position, orientation );
- }
- return pFollower;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CBoneFollower::ObjectCaps()
-{
- CBaseEntity *pOwner = GetOwnerEntity();
- if ( pOwner )
- {
- if( pOwner->m_iGlobalname != NULL_STRING )
- {
- int caps = BaseClass::ObjectCaps() | pOwner->ObjectCaps();
- caps &= ~FCAP_ACROSS_TRANSITION;
- return caps;
- }
- }
-
- return BaseClass::ObjectCaps();
-}
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBoneFollower::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
-{
- CBaseEntity *pOwner = GetOwnerEntity();
- if ( pOwner )
- {
- pOwner->Use( pActivator, pCaller, useType, value );
- return;
- }
-
- BaseClass::Use( pActivator, pCaller, useType, value );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Pass on Touch calls to the entity we're following
-//-----------------------------------------------------------------------------
-void CBoneFollower::Touch( CBaseEntity *pOther )
-{
- CBaseEntity *pOwner = GetOwnerEntity();
- if ( pOwner )
- {
- //TODO: fill in the touch trace with the hitbox number associated with this bone
- pOwner->Touch( pOther );
- return;
- }
-
- BaseClass::Touch( pOther );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Pass on trace attack calls to the entity we're following
-//-----------------------------------------------------------------------------
-void CBoneFollower::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
-{
- CBaseEntity *pOwner = GetOwnerEntity();
- if ( pOwner )
- {
- pOwner->DispatchTraceAttack( info, vecDir, ptr, pAccumulator );
- return;
- }
-
- BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
-}
-
-LINK_ENTITY_TO_CLASS( phys_bone_follower, CBoneFollower );
-
-
-
-// create a manager and a list of followers directly from a ragdoll
-void CreateBoneFollowersFromRagdoll( CBaseAnimating *pEntity, CBoneFollowerManager *pManager, vcollide_t *pCollide )
-{
- IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues );
- while ( !pParse->Finished() )
- {
- const char *pBlock = pParse->GetCurrentBlockName();
- if ( !strcmpi( pBlock, "solid" ) )
- {
- solid_t solid;
-
- pParse->ParseSolid( &solid, NULL );
- // collisions are off by default, turn them on
- solid.params.enableCollisions = true;
- solid.params.pName = STRING(pEntity->GetModelName());
-
- pManager->AddBoneFollower( pEntity, solid.name, &solid );
- }
- else
- {
- pParse->SkipBlock();
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "bone_setup.h"
+#include "physics_bone_follower.h"
+#include "vcollide_parse.h"
+#include "saverestore_utlvector.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+BEGIN_SIMPLE_DATADESC( physfollower_t )
+DEFINE_FIELD( boneIndex, FIELD_INTEGER ),
+DEFINE_FIELD( hFollower, FIELD_EHANDLE ),
+END_DATADESC()
+
+BEGIN_SIMPLE_DATADESC( CBoneFollowerManager )
+DEFINE_GLOBAL_FIELD( m_iNumBones, FIELD_INTEGER ),
+DEFINE_GLOBAL_UTLVECTOR( m_physBones, FIELD_EMBEDDED ),
+END_DATADESC()
+
+//================================================================================================================
+// BONE FOLLOWER MANAGER
+//================================================================================================================
+CBoneFollowerManager::CBoneFollowerManager()
+{
+ m_iNumBones = 0;
+}
+
+CBoneFollowerManager::~CBoneFollowerManager()
+{
+ // if this fires then someone isn't destroying their bonefollowers in UpdateOnRemove
+ Assert(m_iNumBones==0);
+ DestroyBoneFollowers();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity -
+// iNumBones -
+// **pFollowerBoneNames -
+//-----------------------------------------------------------------------------
+void CBoneFollowerManager::InitBoneFollowers( CBaseAnimating *pParentEntity, int iNumBones, const char **pFollowerBoneNames )
+{
+ m_iNumBones = iNumBones;
+ m_physBones.EnsureCount( iNumBones );
+
+ // Now init all the bones
+ for ( int i = 0; i < iNumBones; i++ )
+ {
+ CreatePhysicsFollower( pParentEntity, m_physBones[i], pFollowerBoneNames[i], NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneFollowerManager::AddBoneFollower( CBaseAnimating *pParentEntity, const char *pFollowerBoneName, solid_t *pSolid )
+{
+ m_iNumBones++;
+
+ int iIndex = m_physBones.AddToTail();
+ CreatePhysicsFollower( pParentEntity, m_physBones[iIndex], pFollowerBoneName, pSolid );
+}
+
+// walk the hitboxes and find the first one that is attached to the physics bone in question
+// return the hitgroup of that box
+static int HitGroupFromPhysicsBone( CBaseAnimating *pAnim, int physicsBone )
+{
+ CStudioHdr *pStudioHdr = pAnim->GetModelPtr( );
+ mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnim->m_nHitboxSet );
+ for ( int i = 0; i < set->numhitboxes; i++ )
+ {
+ if ( pStudioHdr->pBone( set->pHitbox(i)->bone )->physicsbone == physicsBone )
+ {
+ return set->pHitbox(i)->group;
+ }
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &follow -
+// *pBoneName -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBoneFollowerManager::CreatePhysicsFollower( CBaseAnimating *pParentEntity, physfollower_t &follow, const char *pBoneName, solid_t *pSolid )
+{
+ CStudioHdr *pStudioHdr = pParentEntity->GetModelPtr();
+ matrix3x4_t boneToWorld;
+ solid_t solidTmp;
+
+ Vector bonePosition;
+ QAngle boneAngles;
+
+ int boneIndex = Studio_BoneIndexByName( pStudioHdr, pBoneName );
+
+ if ( boneIndex >= 0 )
+ {
+ mstudiobone_t *pBone = pStudioHdr->pBone( boneIndex );
+
+ int physicsBone = pBone->physicsbone;
+ if ( !pSolid )
+ {
+ if ( !PhysModelParseSolidByIndex( solidTmp, pParentEntity, pParentEntity->GetModelIndex(), physicsBone ) )
+ return false;
+ pSolid = &solidTmp;
+ }
+
+ // fixup in case ragdoll is assigned to a parent of the requested follower bone
+ follow.boneIndex = Studio_BoneIndexByName( pStudioHdr, pSolid->name );
+ if ( follow.boneIndex < 0 )
+ {
+ follow.boneIndex = boneIndex;
+ }
+
+ pParentEntity->GetBoneTransform( follow.boneIndex, boneToWorld );
+ MatrixAngles( boneToWorld, boneAngles, bonePosition );
+
+ follow.hFollower = CBoneFollower::Create( pParentEntity, STRING(pParentEntity->GetModelName()), *pSolid, bonePosition, boneAngles );
+ follow.hFollower->SetTraceData( physicsBone, HitGroupFromPhysicsBone( pParentEntity, physicsBone ) );
+ follow.hFollower->SetBlocksLOS( pParentEntity->BlocksLOS() );
+ return true;
+ }
+ else
+ {
+ Warning( "ERROR: Tried to create bone follower on invalid bone %s\n", pBoneName );
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneFollowerManager::UpdateBoneFollowers( CBaseAnimating *pParentEntity )
+{
+ if ( m_iNumBones )
+ {
+ matrix3x4_t boneToWorld;
+ Vector bonePosition;
+ QAngle boneAngles;
+ for ( int i = 0; i < m_iNumBones; i++ )
+ {
+ if ( !m_physBones[i].hFollower )
+ continue;
+
+ pParentEntity->GetBoneTransform( m_physBones[i].boneIndex, boneToWorld );
+ MatrixAngles( boneToWorld, boneAngles, bonePosition );
+ m_physBones[i].hFollower->UpdateFollower( bonePosition, boneAngles, 0.1 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneFollowerManager::DestroyBoneFollowers( void )
+{
+ for ( int i = 0; i < m_iNumBones; i++ )
+ {
+ if ( !m_physBones[i].hFollower )
+ continue;
+
+ UTIL_Remove( m_physBones[i].hFollower );
+ m_physBones[i].hFollower = NULL;
+ }
+
+ m_physBones.Purge();
+ m_iNumBones = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+physfollower_t *CBoneFollowerManager::GetBoneFollower( int iFollowerIndex )
+{
+ Assert( iFollowerIndex >= 0 && iFollowerIndex < m_iNumBones );
+ if ( iFollowerIndex >= 0 && iFollowerIndex < m_iNumBones )
+ return &m_physBones[iFollowerIndex];
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieve the index for a supplied bone follower
+// Input : *pFollower - Bone follower to look up
+// Output : -1 if not found, otherwise the index of the bone follower
+//-----------------------------------------------------------------------------
+int CBoneFollowerManager::GetBoneFollowerIndex( CBoneFollower *pFollower )
+{
+ if ( pFollower == NULL )
+ return -1;
+
+ for ( int i = 0; i < m_iNumBones; i++ )
+ {
+ if ( !m_physBones[i].hFollower )
+ continue;
+
+ if ( m_physBones[i].hFollower == pFollower )
+ return i;
+ }
+
+ return -1;
+}
+
+//================================================================================================================
+// BONE FOLLOWER
+//================================================================================================================
+
+//---------------------------------------------------------
+// Save/Restore
+//---------------------------------------------------------
+BEGIN_DATADESC( CBoneFollower )
+
+ DEFINE_FIELD( m_modelIndex, FIELD_MODELINDEX ),
+ DEFINE_FIELD( m_solidIndex, FIELD_INTEGER ),
+ DEFINE_FIELD( m_physicsBone, FIELD_INTEGER ),
+ DEFINE_FIELD( m_hitGroup, FIELD_INTEGER ),
+
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST( CBoneFollower, DT_BoneFollower )
+ SendPropModelIndex(SENDINFO(m_modelIndex)),
+ SendPropInt(SENDINFO(m_solidIndex), 6, SPROP_UNSIGNED ),
+END_SEND_TABLE()
+
+
+bool CBoneFollower::Init( CBaseEntity *pOwner, const char *pModelName, solid_t &solid, const Vector &position, const QAngle &orientation )
+{
+ SetOwnerEntity( pOwner );
+ UTIL_SetModel( this, pModelName );
+
+ AddEffects( EF_NODRAW ); // invisible
+
+ m_modelIndex = modelinfo->GetModelIndex( pModelName );
+ m_solidIndex = solid.index;
+ SetAbsOrigin( position );
+ SetAbsAngles( orientation );
+ SetMoveType( MOVETYPE_PUSH );
+ SetSolid( SOLID_VPHYSICS );
+ SetCollisionGroup( pOwner->GetCollisionGroup() );
+ AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
+ solid.params.pGameData = (void *)this;
+ IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false, &solid );
+ if ( !pPhysics )
+ return false;
+
+ // we can't use the default model bounds because each entity is only one bone of the model
+ // so compute the OBB of the physics model and use that.
+ Vector mins, maxs;
+ physcollision->CollideGetAABB( &mins, &maxs, pPhysics->GetCollide(), vec3_origin, vec3_angle );
+ SetCollisionBounds( mins, maxs );
+
+ pPhysics->SetCallbackFlags( pPhysics->GetCallbackFlags() | CALLBACK_GLOBAL_TOUCH );
+ pPhysics->EnableGravity( false );
+ // This is not a normal shadow controller that is trying to go to a space occupied by an entity in the game physics
+ // This entity is not running PhysicsPusher(), so Vphysics is supposed to move it
+ // This line of code informs vphysics of that fact
+ if ( pOwner->IsNPC() )
+ {
+ pPhysics->GetShadowController()->SetPhysicallyControlled( true );
+ }
+
+ return true;
+}
+
+int CBoneFollower::UpdateTransmitState()
+{
+ // Send to the client for client-side collisions and visualization
+ return SetTransmitState( FL_EDICT_PVSCHECK );
+}
+
+void CBoneFollower::VPhysicsUpdate( IPhysicsObject *pPhysics )
+{
+ Vector origin;
+ QAngle angles;
+
+ pPhysics->GetPosition( &origin, &angles );
+
+ SetAbsOrigin( origin );
+ SetAbsAngles( angles );
+}
+
+// a little helper class to temporarily change the physics object
+// for an entity - and change it back when it goes out of scope.
+class CPhysicsSwapTemp
+{
+public:
+ CPhysicsSwapTemp( CBaseEntity *pEntity, IPhysicsObject *pTmpPhysics )
+ {
+ Assert(pEntity);
+ Assert(pTmpPhysics);
+ m_pEntity = pEntity;
+ m_pPhysics = m_pEntity->VPhysicsGetObject();
+ if ( m_pPhysics )
+ {
+ m_pEntity->VPhysicsSwapObject( pTmpPhysics );
+ }
+ else
+ {
+ m_pEntity->VPhysicsSetObject( pTmpPhysics );
+ }
+ }
+ ~CPhysicsSwapTemp()
+ {
+ m_pEntity->VPhysicsSwapObject( m_pPhysics );
+ }
+
+private:
+ CBaseEntity *m_pEntity;
+ IPhysicsObject *m_pPhysics;
+};
+
+
+void CBoneFollower::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
+{
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if ( pOwner )
+ {
+ CPhysicsSwapTemp tmp(pOwner, pEvent->pObjects[index] );
+ pOwner->VPhysicsCollision( index, pEvent );
+ }
+}
+
+void CBoneFollower::VPhysicsShadowCollision( int index, gamevcollisionevent_t *pEvent )
+{
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if ( pOwner )
+ {
+ CPhysicsSwapTemp tmp(pOwner, pEvent->pObjects[index] );
+ pOwner->VPhysicsShadowCollision( index, pEvent );
+ }
+}
+
+void CBoneFollower::VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit )
+{
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if ( pOwner )
+ {
+ CPhysicsSwapTemp tmp(pOwner, pObject );
+ pOwner->VPhysicsFriction( pObject, energy, surfaceProps, surfacePropsHit );
+ }
+}
+
+bool CBoneFollower::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
+{
+ vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() );
+ Assert( pCollide && pCollide->solidCount > m_solidIndex );
+
+ UTIL_ClearTrace( trace );
+
+ physcollision->TraceBox( ray, pCollide->solids[m_solidIndex], GetAbsOrigin(), GetAbsAngles(), &trace );
+
+ if ( trace.fraction >= 1 )
+ return false;
+
+ // return owner as trace hit
+ trace.m_pEnt = GetOwnerEntity();
+ trace.hitgroup = m_hitGroup;
+ trace.physicsbone = m_physicsBone;
+ return true;
+}
+
+void CBoneFollower::UpdateFollower( const Vector &position, const QAngle &orientation, float flInterval )
+{
+ // UNDONE: Shadow update needs timing info?
+ VPhysicsGetObject()->UpdateShadow( position, orientation, false, flInterval );
+}
+
+void CBoneFollower::SetTraceData( int physicsBone, int hitGroup )
+{
+ m_hitGroup = hitGroup;
+ m_physicsBone = physicsBone;
+}
+
+CBoneFollower *CBoneFollower::Create( CBaseEntity *pOwner, const char *pModelName, solid_t &solid, const Vector &position, const QAngle &orientation )
+{
+ CBoneFollower *pFollower = (CBoneFollower *)CreateEntityByName( "phys_bone_follower" );
+ if ( pFollower )
+ {
+ pFollower->Init( pOwner, pModelName, solid, position, orientation );
+ }
+ return pFollower;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBoneFollower::ObjectCaps()
+{
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if ( pOwner )
+ {
+ if( pOwner->m_iGlobalname != NULL_STRING )
+ {
+ int caps = BaseClass::ObjectCaps() | pOwner->ObjectCaps();
+ caps &= ~FCAP_ACROSS_TRANSITION;
+ return caps;
+ }
+ }
+
+ return BaseClass::ObjectCaps();
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneFollower::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if ( pOwner )
+ {
+ pOwner->Use( pActivator, pCaller, useType, value );
+ return;
+ }
+
+ BaseClass::Use( pActivator, pCaller, useType, value );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pass on Touch calls to the entity we're following
+//-----------------------------------------------------------------------------
+void CBoneFollower::Touch( CBaseEntity *pOther )
+{
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if ( pOwner )
+ {
+ //TODO: fill in the touch trace with the hitbox number associated with this bone
+ pOwner->Touch( pOther );
+ return;
+ }
+
+ BaseClass::Touch( pOther );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pass on trace attack calls to the entity we're following
+//-----------------------------------------------------------------------------
+void CBoneFollower::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if ( pOwner )
+ {
+ pOwner->DispatchTraceAttack( info, vecDir, ptr, pAccumulator );
+ return;
+ }
+
+ BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
+}
+
+LINK_ENTITY_TO_CLASS( phys_bone_follower, CBoneFollower );
+
+
+
+// create a manager and a list of followers directly from a ragdoll
+void CreateBoneFollowersFromRagdoll( CBaseAnimating *pEntity, CBoneFollowerManager *pManager, vcollide_t *pCollide )
+{
+ IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues );
+ while ( !pParse->Finished() )
+ {
+ const char *pBlock = pParse->GetCurrentBlockName();
+ if ( !strcmpi( pBlock, "solid" ) )
+ {
+ solid_t solid;
+
+ pParse->ParseSolid( &solid, NULL );
+ // collisions are off by default, turn them on
+ solid.params.enableCollisions = true;
+ solid.params.pName = STRING(pEntity->GetModelName());
+
+ pManager->AddBoneFollower( pEntity, solid.name, &solid );
+ }
+ else
+ {
+ pParse->SkipBlock();
+ }
+ }
+}