aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/baseanimating.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /sp/src/game/server/baseanimating.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'sp/src/game/server/baseanimating.cpp')
-rw-r--r--sp/src/game/server/baseanimating.cpp3583
1 files changed, 3583 insertions, 0 deletions
diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp
new file mode 100644
index 00000000..f1f2803d
--- /dev/null
+++ b/sp/src/game/server/baseanimating.cpp
@@ -0,0 +1,3583 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Base class for all animating characters and objects.
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "baseanimating.h"
+#include "animation.h"
+#include "activitylist.h"
+#include "studio.h"
+#include "bone_setup.h"
+#include "mathlib/mathlib.h"
+#include "model_types.h"
+#include "datacache/imdlcache.h"
+#include "physics.h"
+#include "ndebugoverlay.h"
+#include "tier1/strtools.h"
+#include "npcevent.h"
+#include "isaverestore.h"
+#include "KeyValues.h"
+#include "tier0/vprof.h"
+#include "EntityFlame.h"
+#include "EntityDissolve.h"
+#include "ai_basenpc.h"
+#include "physics_prop_ragdoll.h"
+#include "datacache/idatacache.h"
+#include "smoke_trail.h"
+#include "props.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar ai_sequence_debug( "ai_sequence_debug", "0" );
+
+class CIKSaveRestoreOps : public CClassPtrSaveRestoreOps
+{
+ // save data type interface
+ void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
+ {
+ Assert( fieldInfo.pTypeDesc->fieldSize == 1 );
+ CIKContext **pIK = (CIKContext **)fieldInfo.pField;
+ bool bHasIK = (*pIK) != 0;
+ pSave->WriteBool( &bHasIK );
+ }
+
+ void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
+ {
+ Assert( fieldInfo.pTypeDesc->fieldSize == 1 );
+ CIKContext **pIK = (CIKContext **)fieldInfo.pField;
+
+ bool bHasIK;
+ pRestore->ReadBool( &bHasIK );
+ *pIK = (bHasIK) ? new CIKContext : NULL;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Relative lighting entity
+//-----------------------------------------------------------------------------
+class CInfoLightingRelative : public CBaseEntity
+{
+public:
+ DECLARE_CLASS( CInfoLightingRelative, CBaseEntity );
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+
+ virtual void Activate();
+ virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
+ virtual int UpdateTransmitState( void );
+
+private:
+ CNetworkHandle( CBaseEntity, m_hLightingLandmark );
+ string_t m_strLightingLandmark;
+};
+
+LINK_ENTITY_TO_CLASS( info_lighting_relative, CInfoLightingRelative );
+
+BEGIN_DATADESC( CInfoLightingRelative )
+ DEFINE_KEYFIELD( m_strLightingLandmark, FIELD_STRING, "LightingLandmark" ),
+ DEFINE_FIELD( m_hLightingLandmark, FIELD_EHANDLE ),
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST(CInfoLightingRelative, DT_InfoLightingRelative)
+ SendPropEHandle( SENDINFO( m_hLightingLandmark ) ),
+END_SEND_TABLE()
+
+
+//-----------------------------------------------------------------------------
+// Activate!
+//-----------------------------------------------------------------------------
+void CInfoLightingRelative::Activate()
+{
+ BaseClass::Activate();
+ if ( m_strLightingLandmark == NULL_STRING )
+ {
+ m_hLightingLandmark = NULL;
+ }
+ else
+ {
+ m_hLightingLandmark = gEntList.FindEntityByName( NULL, m_strLightingLandmark );
+ if ( !m_hLightingLandmark )
+ {
+ DevWarning( "%s: Could not find lighting landmark '%s'!\n", GetClassname(), STRING( m_strLightingLandmark ) );
+ }
+ else
+ {
+ // Set a force transmit because we do not have a model.
+ m_hLightingLandmark->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Force our lighting landmark to be transmitted
+//-----------------------------------------------------------------------------
+void CInfoLightingRelative::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
+{
+ // Are we already marked for transmission?
+ if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
+ return;
+
+ BaseClass::SetTransmit( pInfo, bAlways );
+
+ // Force our constraint entity to be sent too.
+ if ( m_hLightingLandmark )
+ {
+ if ( m_hLightingLandmark->GetMoveParent() )
+ {
+ // Set a full check because we have a move parent.
+ m_hLightingLandmark->SetTransmitState( FL_EDICT_FULLCHECK );
+ }
+ else
+ {
+ m_hLightingLandmark->SetTransmitState( FL_EDICT_ALWAYS );
+ }
+
+ m_hLightingLandmark->SetTransmit( pInfo, bAlways );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose Force our lighting landmark to be transmitted
+//-----------------------------------------------------------------------------
+int CInfoLightingRelative::UpdateTransmitState( void )
+{
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+static CIKSaveRestoreOps s_IKSaveRestoreOp;
+
+
+BEGIN_DATADESC( CBaseAnimating )
+
+ DEFINE_FIELD( m_flGroundSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flLastEventCheck, FIELD_TIME ),
+ DEFINE_FIELD( m_bSequenceFinished, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bSequenceLoops, FIELD_BOOLEAN ),
+
+// DEFINE_FIELD( m_nForceBone, FIELD_INTEGER ),
+// DEFINE_FIELD( m_vecForce, FIELD_VECTOR ),
+
+ DEFINE_INPUT( m_nSkin, FIELD_INTEGER, "skin" ),
+ DEFINE_KEYFIELD( m_nBody, FIELD_INTEGER, "body" ),
+ DEFINE_INPUT( m_nBody, FIELD_INTEGER, "SetBodyGroup" ),
+ DEFINE_KEYFIELD( m_nHitboxSet, FIELD_INTEGER, "hitboxset" ),
+ DEFINE_KEYFIELD( m_nSequence, FIELD_INTEGER, "sequence" ),
+ DEFINE_ARRAY( m_flPoseParameter, FIELD_FLOAT, CBaseAnimating::NUM_POSEPAREMETERS ),
+ DEFINE_ARRAY( m_flEncodedController, FIELD_FLOAT, CBaseAnimating::NUM_BONECTRLS ),
+ DEFINE_KEYFIELD( m_flPlaybackRate, FIELD_FLOAT, "playbackrate" ),
+ DEFINE_KEYFIELD( m_flCycle, FIELD_FLOAT, "cycle" ),
+// DEFINE_FIELD( m_flIKGroundContactTime, FIELD_TIME ),
+// DEFINE_FIELD( m_flIKGroundMinHeight, FIELD_FLOAT ),
+// DEFINE_FIELD( m_flIKGroundMaxHeight, FIELD_FLOAT ),
+// DEFINE_FIELD( m_flEstIkFloor, FIELD_FLOAT ),
+// DEFINE_FIELD( m_flEstIkOffset, FIELD_FLOAT ),
+// DEFINE_FIELD( m_pStudioHdr, CStudioHdr ),
+// DEFINE_FIELD( m_StudioHdrInitLock, CThreadFastMutex ),
+// DEFINE_FIELD( m_BoneSetupMutex, CThreadFastMutex ),
+ DEFINE_CUSTOM_FIELD( m_pIk, &s_IKSaveRestoreOp ),
+ DEFINE_FIELD( m_iIKCounter, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bClientSideAnimation, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bClientSideFrameReset, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_nNewSequenceParity, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nResetEventsParity, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nMuzzleFlashParity, FIELD_CHARACTER ),
+
+ DEFINE_KEYFIELD( m_iszLightingOriginRelative, FIELD_STRING, "LightingOriginHack" ),
+ DEFINE_KEYFIELD( m_iszLightingOrigin, FIELD_STRING, "LightingOrigin" ),
+
+ DEFINE_FIELD( m_hLightingOrigin, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hLightingOriginRelative, FIELD_EHANDLE ),
+
+ DEFINE_FIELD( m_flModelScale, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flDissolveStartTime, FIELD_TIME ),
+
+ // DEFINE_FIELD( m_boneCacheHandle, memhandle_t ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Ignite", InputIgnite ),
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "IgniteLifetime", InputIgniteLifetime ),
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "IgniteNumHitboxFires", InputIgniteNumHitboxFires ),
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "IgniteHitboxFireScale", InputIgniteHitboxFireScale ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "BecomeRagdoll", InputBecomeRagdoll ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "SetLightingOriginHack", InputSetLightingOriginRelative ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "SetLightingOrigin", InputSetLightingOrigin ),
+ DEFINE_OUTPUT( m_OnIgnite, "OnIgnite" ),
+
+ DEFINE_INPUT( m_fadeMinDist, FIELD_FLOAT, "fademindist" ),
+ DEFINE_INPUT( m_fadeMaxDist, FIELD_FLOAT, "fademaxdist" ),
+ DEFINE_KEYFIELD( m_flFadeScale, FIELD_FLOAT, "fadescale" ),
+
+ DEFINE_FIELD( m_fBoneCacheFlags, FIELD_SHORT ),
+
+ END_DATADESC()
+
+// Sendtable for fields we don't want to send to clientside animating entities
+BEGIN_SEND_TABLE_NOBASE( CBaseAnimating, DT_ServerAnimationData )
+ // ANIMATION_CYCLE_BITS is defined in shareddefs.h
+ SendPropFloat (SENDINFO(m_flCycle), ANIMATION_CYCLE_BITS, SPROP_CHANGES_OFTEN|SPROP_ROUNDDOWN, 0.0f, 1.0f)
+END_SEND_TABLE()
+
+void *SendProxy_ClientSideAnimation( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID );
+
+// SendTable stuff.
+IMPLEMENT_SERVERCLASS_ST(CBaseAnimating, DT_BaseAnimating)
+ SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ),
+ SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
+
+ SendPropInt ( SENDINFO(m_nSkin), ANIMATION_SKIN_BITS),
+ SendPropInt ( SENDINFO(m_nBody), ANIMATION_BODY_BITS),
+
+ SendPropInt ( SENDINFO(m_nHitboxSet),ANIMATION_HITBOXSET_BITS, SPROP_UNSIGNED ),
+
+ SendPropFloat ( SENDINFO(m_flModelScale) ),
+
+ SendPropArray3 ( SENDINFO_ARRAY3(m_flPoseParameter), SendPropFloat(SENDINFO_ARRAY(m_flPoseParameter), ANIMATION_POSEPARAMETER_BITS, 0, 0.0f, 1.0f ) ),
+
+ SendPropInt ( SENDINFO(m_nSequence), ANIMATION_SEQUENCE_BITS, SPROP_UNSIGNED ),
+ SendPropFloat ( SENDINFO(m_flPlaybackRate), ANIMATION_PLAYBACKRATE_BITS, SPROP_ROUNDUP, -4.0, 12.0f ), // NOTE: if this isn't a power of 2 than "1.0" can't be encoded correctly
+
+ SendPropArray3 (SENDINFO_ARRAY3(m_flEncodedController), SendPropFloat(SENDINFO_ARRAY(m_flEncodedController), 11, SPROP_ROUNDDOWN, 0.0f, 1.0f ) ),
+
+ SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_bClientSideFrameReset ), 1, SPROP_UNSIGNED ),
+
+ SendPropInt( SENDINFO( m_nNewSequenceParity ), EF_PARITY_BITS, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_nResetEventsParity ), EF_PARITY_BITS, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_nMuzzleFlashParity ), EF_MUZZLEFLASH_BITS, SPROP_UNSIGNED ),
+
+ SendPropEHandle( SENDINFO( m_hLightingOrigin ) ),
+ SendPropEHandle( SENDINFO( m_hLightingOriginRelative ) ),
+
+ SendPropDataTable( "serveranimdata", 0, &REFERENCE_SEND_TABLE( DT_ServerAnimationData ), SendProxy_ClientSideAnimation ),
+
+ // Fading
+ SendPropFloat( SENDINFO( m_fadeMinDist ), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO( m_fadeMaxDist ), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO( m_flFadeScale ), 0, SPROP_NOSCALE ),
+
+END_SEND_TABLE()
+
+
+CBaseAnimating::CBaseAnimating()
+{
+ m_vecForce.GetForModify().Init();
+ m_nForceBone = 0;
+
+ m_bResetSequenceInfoOnLoad = false;
+ m_bClientSideAnimation = false;
+ m_pIk = NULL;
+ m_iIKCounter = 0;
+
+ InitStepHeightAdjust();
+
+ m_flModelScale = 1.0f;
+ // initialize anim clock
+ m_flAnimTime = gpGlobals->curtime;
+ m_flPrevAnimTime = gpGlobals->curtime;
+ m_nNewSequenceParity = 0;
+ m_nResetEventsParity = 0;
+ m_boneCacheHandle = 0;
+ m_pStudioHdr = NULL;
+ m_fadeMinDist = 0;
+ m_fadeMaxDist = 0;
+ m_flFadeScale = 0.0f;
+ m_fBoneCacheFlags = 0;
+}
+
+CBaseAnimating::~CBaseAnimating()
+{
+ Studio_DestroyBoneCache( m_boneCacheHandle );
+ delete m_pIk;
+ UnlockStudioHdr();
+ delete m_pStudioHdr;
+}
+
+void CBaseAnimating::Precache()
+{
+#if !defined( TF_DLL )
+ // Anything derived from this class can potentially burn - true, but do we want it to!
+ PrecacheParticleSystem( "burning_character" );
+#endif
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Activate!
+//-----------------------------------------------------------------------------
+void CBaseAnimating::Activate()
+{
+ BaseClass::Activate();
+ SetLightingOrigin( m_iszLightingOrigin );
+ SetLightingOriginRelative( m_iszLightingOriginRelative );
+
+ // Scaled physics objects (re)create their physics here
+ if ( m_flModelScale != 1.0f && VPhysicsGetObject() )
+ {
+ // sanity check to make sure 'm_flModelScale' is in sync with the
+ Assert( m_flModelScale > 0.0f );
+
+ UTIL_CreateScaledPhysObject( this, m_flModelScale );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Force our lighting origin to be trasmitted
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
+{
+ // Are we already marked for transmission?
+ if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
+ return;
+
+ BaseClass::SetTransmit( pInfo, bAlways );
+
+ // Force our lighting entities to be sent too.
+ if ( m_hLightingOrigin )
+ {
+ m_hLightingOrigin->SetTransmit( pInfo, bAlways );
+ }
+ if ( m_hLightingOriginRelative )
+ {
+ m_hLightingOriginRelative->SetTransmit( pInfo, bAlways );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int CBaseAnimating::Restore( IRestore &restore )
+{
+ int result = BaseClass::Restore( restore );
+ if ( m_flModelScale <= 0.0f )
+ m_flModelScale = 1.0f;
+ LockStudioHdr();
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CBaseAnimating::OnRestore()
+{
+ BaseClass::OnRestore();
+
+ if ( m_nSequence != -1 && GetModelPtr() && !IsValidSequence( m_nSequence ) )
+ m_nSequence = 0;
+
+ m_flEstIkFloor = GetLocalOrigin().z;
+ PopulatePoseParameters();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CBaseAnimating::Spawn()
+{
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CBaseAnimating::UseClientSideAnimation()
+{
+ m_bClientSideAnimation = true;
+}
+
+#define MAX_ANIMTIME_INTERVAL 0.2f
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CBaseAnimating::GetAnimTimeInterval( void ) const
+{
+ float flInterval;
+ if (m_flAnimTime < gpGlobals->curtime)
+ {
+ // estimate what it'll be this frame
+ flInterval = clamp( gpGlobals->curtime - m_flAnimTime, 0.f, MAX_ANIMTIME_INTERVAL );
+ }
+ else
+ {
+ // report actual
+ flInterval = clamp( m_flAnimTime - m_flPrevAnimTime, 0.f, MAX_ANIMTIME_INTERVAL );
+ }
+ return flInterval;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseAnimating::StudioFrameAdvanceInternal( CStudioHdr *pStudioHdr, float flCycleDelta )
+{
+ float flNewCycle = GetCycle() + flCycleDelta;
+ if (flNewCycle < 0.0 || flNewCycle >= 1.0)
+ {
+ if (m_bSequenceLoops)
+ {
+ flNewCycle -= (int)(flNewCycle);
+ }
+ else
+ {
+ flNewCycle = (flNewCycle < 0.0f) ? 0.0f : 1.0f;
+ }
+ m_bSequenceFinished = true; // just in case it wasn't caught in GetEvents
+ }
+ else if (flNewCycle > GetLastVisibleCycle( pStudioHdr, GetSequence() ))
+ {
+ m_bSequenceFinished = true;
+ }
+
+ SetCycle( flNewCycle );
+
+ /*
+ if (!IsPlayer())
+ Msg("%s %6.3f : %6.3f %6.3f (%.3f) %.3f\n",
+ GetClassname(), gpGlobals->curtime,
+ m_flAnimTime.Get(), m_flPrevAnimTime, flInterval, GetCycle() );
+ */
+
+ m_flGroundSpeed = GetSequenceGroundSpeed( pStudioHdr, GetSequence() );
+
+ // Msg("%s : %s : %5.1f\n", GetClassname(), GetSequenceName( GetSequence() ), GetCycle() );
+ InvalidatePhysicsRecursive( ANIMATION_CHANGED );
+
+ InvalidateBoneCacheIfOlderThan( 0 );
+}
+
+void CBaseAnimating::InvalidateBoneCacheIfOlderThan( float deltaTime )
+{
+ CBoneCache *pcache = Studio_GetBoneCache( m_boneCacheHandle );
+ if ( !pcache || !pcache->IsValid( gpGlobals->curtime, deltaTime ) )
+ {
+ InvalidateBoneCache();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseAnimating::StudioFrameAdvanceManual( float flInterval )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( !pStudioHdr )
+ return;
+
+ UpdateModelScale();
+ m_flAnimTime = gpGlobals->curtime;
+ m_flPrevAnimTime = m_flAnimTime - flInterval;
+ float flCycleRate = GetSequenceCycleRate( pStudioHdr, GetSequence() ) * m_flPlaybackRate;
+ StudioFrameAdvanceInternal( GetModelPtr(), flInterval * flCycleRate );
+}
+
+
+//=========================================================
+// StudioFrameAdvance - advance the animation frame up some interval (default 0.1) into the future
+//=========================================================
+void CBaseAnimating::StudioFrameAdvance()
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+
+ if ( !pStudioHdr || !pStudioHdr->SequencesAvailable() )
+ {
+ return;
+ }
+
+ UpdateModelScale();
+
+ if ( !m_flPrevAnimTime )
+ {
+ m_flPrevAnimTime = m_flAnimTime;
+ }
+
+ // Time since last animation
+ float flInterval = gpGlobals->curtime - m_flAnimTime;
+ flInterval = clamp( flInterval, 0.f, MAX_ANIMTIME_INTERVAL );
+
+ //Msg( "%i %s interval %f\n", entindex(), GetClassname(), flInterval );
+ if (flInterval <= 0.001f)
+ {
+ // Msg("%s : %s : %5.3f (skip)\n", GetClassname(), GetSequenceName( GetSequence() ), GetCycle() );
+ return;
+ }
+
+ // Latch prev
+ m_flPrevAnimTime = m_flAnimTime;
+ // Set current
+ m_flAnimTime = gpGlobals->curtime;
+
+ // Drive cycle
+ float flCycleRate = GetSequenceCycleRate( pStudioHdr, GetSequence() ) * m_flPlaybackRate;
+
+ StudioFrameAdvanceInternal( pStudioHdr, flInterval * flCycleRate );
+
+ if (ai_sequence_debug.GetBool() == true && m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
+ {
+ Msg("%5.2f : %s : %s : %5.3f\n", gpGlobals->curtime, GetClassname(), GetSequenceName( GetSequence() ), GetCycle() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Set the relative lighting origin
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetLightingOriginRelative( string_t strLightingOriginRelative )
+{
+ if ( strLightingOriginRelative == NULL_STRING )
+ {
+ SetLightingOriginRelative( NULL );
+ }
+ else
+ {
+ CBaseEntity *pLightingOrigin = gEntList.FindEntityByName( NULL, strLightingOriginRelative );
+ if ( !pLightingOrigin )
+ {
+ DevWarning( "%s: Could not find info_lighting_relative '%s'!\n", GetClassname(), STRING( strLightingOriginRelative ) );
+ return;
+ }
+ else if ( !dynamic_cast<CInfoLightingRelative *>(pLightingOrigin) )
+ {
+ if( !pLightingOrigin )
+ {
+ DevWarning( "%s: Cannot find Lighting Origin named: %s\n", GetEntityName().ToCStr(), STRING(strLightingOriginRelative) );
+ }
+ else
+ {
+ DevWarning( "%s: Specified entity '%s' must be a info_lighting_relative!\n",
+ pLightingOrigin->GetClassname(), pLightingOrigin->GetEntityName().ToCStr() );
+ }
+ return;
+ }
+
+ SetLightingOriginRelative( pLightingOrigin );
+ }
+
+ // Save the name so that save/load will correctly restore it in Activate()
+ m_iszLightingOriginRelative = strLightingOriginRelative;
+}
+
+//-----------------------------------------------------------------------------
+// Set the lighting origin
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetLightingOrigin( string_t strLightingOrigin )
+{
+ if ( strLightingOrigin == NULL_STRING )
+ {
+ SetLightingOrigin( NULL );
+ }
+ else
+ {
+ CBaseEntity *pLightingOrigin = gEntList.FindEntityByName( NULL, strLightingOrigin );
+ if ( !pLightingOrigin )
+ {
+ DevWarning( "%s: Could not find lighting origin entity named '%s'!\n", GetClassname(), STRING( strLightingOrigin ) );
+ return;
+ }
+ else
+ {
+ SetLightingOrigin( pLightingOrigin );
+ }
+ }
+
+ // Save the name so that save/load will correctly restore it in Activate()
+ m_iszLightingOrigin = strLightingOrigin;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &inputdata -
+//-----------------------------------------------------------------------------
+void CBaseAnimating::InputSetLightingOriginRelative( inputdata_t &inputdata )
+{
+ // Find our specified target
+ string_t strLightingOriginRelative = MAKE_STRING( inputdata.value.String() );
+ SetLightingOriginRelative( strLightingOriginRelative );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &inputdata -
+//-----------------------------------------------------------------------------
+void CBaseAnimating::InputSetLightingOrigin( inputdata_t &inputdata )
+{
+ // Find our specified target
+ string_t strLightingOrigin = MAKE_STRING( inputdata.value.String() );
+ SetLightingOrigin( strLightingOrigin );
+}
+
+
+//=========================================================
+// SelectWeightedSequence
+//=========================================================
+int CBaseAnimating::SelectWeightedSequence ( Activity activity )
+{
+ Assert( activity != ACT_INVALID );
+ Assert( GetModelPtr() );
+ return ::SelectWeightedSequence( GetModelPtr(), activity, GetSequence() );
+}
+
+
+int CBaseAnimating::SelectWeightedSequence ( Activity activity, int curSequence )
+{
+ Assert( activity != ACT_INVALID );
+ Assert( GetModelPtr() );
+ return ::SelectWeightedSequence( GetModelPtr(), activity, curSequence );
+}
+
+//=========================================================
+// ResetActivityIndexes
+//=========================================================
+void CBaseAnimating::ResetActivityIndexes ( void )
+{
+ Assert( GetModelPtr() );
+ ::ResetActivityIndexes( GetModelPtr() );
+}
+
+//=========================================================
+// ResetEventIndexes
+//=========================================================
+void CBaseAnimating::ResetEventIndexes ( void )
+{
+ Assert( GetModelPtr() );
+ ::ResetEventIndexes( GetModelPtr() );
+}
+
+//=========================================================
+// LookupHeaviestSequence
+//
+// Get sequence with highest 'weight' for this activity
+//
+//=========================================================
+int CBaseAnimating::SelectHeaviestSequence ( Activity activity )
+{
+ Assert( GetModelPtr() );
+ return ::SelectHeaviestSequence( GetModelPtr(), activity );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Looks up an activity by name.
+// Input : label - Name of the activity, ie "ACT_IDLE".
+// Output : Returns the activity ID or ACT_INVALID.
+//-----------------------------------------------------------------------------
+int CBaseAnimating::LookupActivity( const char *label )
+{
+ Assert( GetModelPtr() );
+ return ::LookupActivity( GetModelPtr(), label );
+}
+
+//=========================================================
+//=========================================================
+int CBaseAnimating::LookupSequence( const char *label )
+{
+ Assert( GetModelPtr() );
+ return ::LookupSequence( GetModelPtr(), label );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+KeyValues *CBaseAnimating::GetSequenceKeyValues( int iSequence )
+{
+ const char *szText = Studio_GetKeyValueText( GetModelPtr(), iSequence );
+
+ if (szText)
+ {
+ KeyValues *seqKeyValues = new KeyValues("");
+ if ( seqKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), szText ) )
+ {
+ return seqKeyValues;
+ }
+ seqKeyValues->deleteThis();
+ }
+ return NULL;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : iSequence -
+//
+// Output : float -
+//-----------------------------------------------------------------------------
+float CBaseAnimating::GetSequenceMoveYaw( int iSequence )
+{
+ Vector vecReturn;
+
+ Assert( GetModelPtr() );
+ ::GetSequenceLinearMotion( GetModelPtr(), iSequence, GetPoseParameterArray(), &vecReturn );
+
+ if (vecReturn.Length() > 0)
+ {
+ return UTIL_VecToYaw( vecReturn );
+ }
+
+ return NOMOTION;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : iSequence -
+//
+// Output : float
+//-----------------------------------------------------------------------------
+float CBaseAnimating::GetSequenceMoveDist( CStudioHdr *pStudioHdr, int iSequence )
+{
+ Vector vecReturn;
+
+ ::GetSequenceLinearMotion( pStudioHdr, iSequence, GetPoseParameterArray(), &vecReturn );
+
+ return vecReturn.Length();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : iSequence -
+// *pVec -
+//
+//-----------------------------------------------------------------------------
+void CBaseAnimating::GetSequenceLinearMotion( int iSequence, Vector *pVec )
+{
+ Assert( GetModelPtr() );
+ ::GetSequenceLinearMotion( GetModelPtr(), iSequence, GetPoseParameterArray(), pVec );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : iSequence -
+//
+// Output : char
+//-----------------------------------------------------------------------------
+const char *CBaseAnimating::GetSequenceName( int iSequence )
+{
+ if( iSequence == -1 )
+ {
+ return "Not Found!";
+ }
+
+ if ( !GetModelPtr() )
+ return "No model!";
+
+ return ::GetSequenceName( GetModelPtr(), iSequence );
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : iSequence -
+//
+// Output : char
+//-----------------------------------------------------------------------------
+const char *CBaseAnimating::GetSequenceActivityName( int iSequence )
+{
+ if( iSequence == -1 )
+ {
+ return "Not Found!";
+ }
+
+ if ( !GetModelPtr() )
+ return "No model!";
+
+ return ::GetSequenceActivityName( GetModelPtr(), iSequence );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make this a client-side simulated entity
+// Input : force - vector of force to be exerted in the physics simulation
+// forceBone - bone to exert force upon
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::BecomeRagdollOnClient( const Vector &force )
+{
+ // If this character has a ragdoll animation, turn it over to the physics system
+ if ( CanBecomeRagdoll() )
+ {
+ VPhysicsDestroyObject();
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ m_nRenderFX = kRenderFxRagdoll;
+
+ // Have to do this dance because m_vecForce is a network vector
+ // and can't be sent to ClampRagdollForce as a Vector *
+ Vector vecClampedForce;
+ ClampRagdollForce( force, &vecClampedForce );
+ m_vecForce = vecClampedForce;
+
+ SetParent( NULL );
+
+ AddFlag( FL_TRANSRAGDOLL );
+
+ SetMoveType( MOVETYPE_NONE );
+ //UTIL_SetSize( this, vec3_origin, vec3_origin );
+ SetThink( NULL );
+
+ SetNextThink( gpGlobals->curtime + 2.0f );
+ //If we're here, then we can vanish safely
+ SetThink( &CBaseEntity::SUB_Remove );
+
+ // Remove our flame entity if it's attached to us
+ CEntityFlame *pFireChild = dynamic_cast<CEntityFlame *>( GetEffectEntity() );
+ if ( pFireChild )
+ {
+ pFireChild->SetThink( &CBaseEntity::SUB_Remove );
+ pFireChild->SetNextThink( gpGlobals->curtime + 0.1f );
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool CBaseAnimating::IsRagdoll()
+{
+ return ( m_nRenderFX == kRenderFxRagdoll ) ? true : false;
+}
+
+bool CBaseAnimating::CanBecomeRagdoll( void )
+{
+ MDLCACHE_CRITICAL_SECTION();
+ int ragdollSequence = SelectWeightedSequence( ACT_DIERAGDOLL );
+
+ //Can't cause we don't have a ragdoll sequence.
+ if ( ragdollSequence == ACTIVITY_NOT_AVAILABLE )
+ return false;
+
+ if ( GetFlags() & FL_TRANSRAGDOLL )
+ return false;
+
+ return true;
+}
+
+//=========================================================
+//=========================================================
+void CBaseAnimating::ResetSequenceInfo ( )
+{
+ if (GetSequence() == -1)
+ {
+ // This shouldn't happen. Setting m_nSequence blindly is a horrible coding practice.
+ SetSequence( 0 );
+ }
+
+ if ( IsDynamicModelLoading() )
+ {
+ m_bResetSequenceInfoOnLoad = true;
+ return;
+ }
+
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ m_flGroundSpeed = GetSequenceGroundSpeed( pStudioHdr, GetSequence() );
+ m_bSequenceLoops = ((GetSequenceFlags( pStudioHdr, GetSequence() ) & STUDIO_LOOPING) != 0);
+ // m_flAnimTime = gpGlobals->time;
+ m_flPlaybackRate = 1.0;
+ m_bSequenceFinished = false;
+ m_flLastEventCheck = 0;
+
+ m_nNewSequenceParity = ( m_nNewSequenceParity+1 ) & EF_PARITY_MASK;
+ m_nResetEventsParity = ( m_nResetEventsParity+1 ) & EF_PARITY_MASK;
+
+ // FIXME: why is this called here? Nothing should have changed to make this nessesary
+ if ( pStudioHdr )
+ {
+ SetEventIndexForSequence( pStudioHdr->pSeqdesc( GetSequence() ) );
+ }
+}
+
+//=========================================================
+//=========================================================
+bool CBaseAnimating::IsValidSequence( int iSequence )
+{
+ Assert( GetModelPtr() );
+ CStudioHdr* pstudiohdr = GetModelPtr( );
+ if (iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq())
+ {
+ return false;
+ }
+ return true;
+}
+
+//=========================================================
+//=========================================================
+void CBaseAnimating::SetSequence( int nSequence )
+{
+ Assert( nSequence == 0 || IsDynamicModelLoading() || ( GetModelPtr( ) && ( nSequence < GetModelPtr( )->GetNumSeq() ) && ( GetModelPtr( )->GetNumSeq() < (1 << ANIMATION_SEQUENCE_BITS) ) ) );
+ m_nSequence = nSequence;
+}
+
+//=========================================================
+//=========================================================
+float CBaseAnimating::SequenceDuration( CStudioHdr *pStudioHdr, int iSequence )
+{
+ if ( !pStudioHdr )
+ {
+ DevWarning( 2, "CBaseAnimating::SequenceDuration( %d ) NULL pstudiohdr on %s!\n", iSequence, GetClassname() );
+ return 0.1;
+ }
+ if ( !pStudioHdr->SequencesAvailable() )
+ {
+ return 0.1;
+ }
+ if (iSequence >= pStudioHdr->GetNumSeq() || iSequence < 0 )
+ {
+ DevWarning( 2, "CBaseAnimating::SequenceDuration( %d ) out of range\n", iSequence );
+ return 0.1;
+ }
+
+ return Studio_Duration( pStudioHdr, iSequence, GetPoseParameterArray() );
+}
+
+float CBaseAnimating::GetSequenceCycleRate( CStudioHdr *pStudioHdr, int iSequence )
+{
+ float t = SequenceDuration( pStudioHdr, iSequence );
+
+ if (t > 0.0f)
+ {
+ return 1.0f / t;
+ }
+ else
+ {
+ return 1.0f / 0.1f;
+ }
+}
+
+
+float CBaseAnimating::GetLastVisibleCycle( CStudioHdr *pStudioHdr, int iSequence )
+{
+ if ( !pStudioHdr )
+ {
+ DevWarning( 2, "CBaseAnimating::LastVisibleCycle( %d ) NULL pstudiohdr on %s!\n", iSequence, GetClassname() );
+ return 1.0;
+ }
+
+ if (!(GetSequenceFlags( pStudioHdr, iSequence ) & STUDIO_LOOPING))
+ {
+ return 1.0f - (pStudioHdr->pSeqdesc( iSequence ).fadeouttime) * GetSequenceCycleRate( iSequence ) * m_flPlaybackRate;
+ }
+ else
+ {
+ return 1.0;
+ }
+}
+
+
+float CBaseAnimating::GetSequenceGroundSpeed( CStudioHdr *pStudioHdr, int iSequence )
+{
+ float t = SequenceDuration( pStudioHdr, iSequence );
+
+ if (t > 0)
+ {
+ return ( GetSequenceMoveDist( pStudioHdr, iSequence ) / t );
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+float CBaseAnimating::GetIdealSpeed( ) const
+{
+ return m_flGroundSpeed;
+}
+
+float CBaseAnimating::GetIdealAccel( ) const
+{
+ // return ideal max velocity change over 1 second.
+ // tuned for run-walk range of humans
+ return GetIdealSpeed() + 50;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if the given sequence has the anim event, false if not.
+// Input : nSequence - sequence number to check
+// nEvent - anim event number to look for
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::HasAnimEvent( int nSequence, int nEvent )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr();
+ if ( !pstudiohdr )
+ {
+ return false;
+ }
+
+ animevent_t event;
+
+ int index = 0;
+ while ( ( index = GetAnimationEvent( pstudiohdr, nSequence, &event, 0.0f, 1.0f, index ) ) != 0 )
+ {
+ if ( event.event == nEvent )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//=========================================================
+// DispatchAnimEvents
+//=========================================================
+void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler )
+{
+ // don't fire events if the framerate is 0
+ if (m_flPlaybackRate == 0.0)
+ return;
+
+ animevent_t event;
+
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+
+ if ( !pstudiohdr )
+ {
+ Assert(!"CBaseAnimating::DispatchAnimEvents: model missing");
+ return;
+ }
+
+ if ( !pstudiohdr->SequencesAvailable() )
+ {
+ return;
+ }
+
+ // skip this altogether if there are no events
+ if (pstudiohdr->pSeqdesc( GetSequence() ).numevents == 0)
+ {
+ return;
+ }
+
+ // look from when it last checked to some short time in the future
+ float flCycleRate = GetSequenceCycleRate( GetSequence() ) * m_flPlaybackRate;
+ float flStart = m_flLastEventCheck;
+ float flEnd = GetCycle();
+
+ if (!m_bSequenceLoops && m_bSequenceFinished)
+ {
+ flEnd = 1.01f;
+ }
+ m_flLastEventCheck = flEnd;
+
+ /*
+ if (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
+ {
+ Msg( "%s:%s : checking %.2f %.2f (%d)\n", STRING(GetModelName()), pstudiohdr->pSeqdesc( GetSequence() ).pszLabel(), flStart, flEnd, m_bSequenceFinished );
+ }
+ */
+
+ // FIXME: does not handle negative framerates!
+ int index = 0;
+ while ( (index = GetAnimationEvent( pstudiohdr, GetSequence(), &event, flStart, flEnd, index ) ) != 0 )
+ {
+ event.pSource = this;
+ // calc when this event should happen
+ if (flCycleRate > 0.0)
+ {
+ float flCycle = event.cycle;
+ if (flCycle > GetCycle())
+ {
+ flCycle = flCycle - 1.0;
+ }
+ event.eventtime = m_flAnimTime + (flCycle - GetCycle()) / flCycleRate + GetAnimTimeInterval();
+ }
+
+ /*
+ if (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
+ {
+ Msg( "dispatch %i (%i) cycle %f event cycle %f cyclerate %f\n",
+ (int)(index - 1),
+ (int)event.event,
+ (float)GetCycle(),
+ (float)event.cycle,
+ (float)flCycleRate );
+ }
+ */
+ eventHandler->HandleAnimEvent( &event );
+
+ // FAILSAFE:
+ // If HandleAnimEvent has somehow reset my internal pointer
+ // to CStudioHdr to something other than it was when we entered
+ // this function, we will crash on the next call to GetAnimationEvent
+ // because pstudiohdr no longer points at something valid.
+ // So, catch this case, complain vigorously, and bail out of
+ // the loop.
+ CStudioHdr *pNowStudioHdr = GetModelPtr();
+ if ( pNowStudioHdr != pstudiohdr )
+ {
+ AssertMsg2(false, "%s has changed its model while processing AnimEvents on sequence %d. Aborting dispatch.\n", GetDebugName(), GetSequence() );
+ Warning( "%s has changed its model while processing AnimEvents on sequence %d. Aborting dispatch.\n", GetDebugName(), GetSequence() );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent )
+{
+ if ((pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER))
+ {
+ if ( pEvent->event == AE_SV_PLAYSOUND )
+ {
+ EmitSound( pEvent->options );
+ return;
+ }
+ else if ( pEvent->event == AE_RAGDOLL )
+ {
+ // Convert to ragdoll immediately
+ BecomeRagdollOnClient( vec3_origin );
+ return;
+ }
+#ifdef HL2_EPISODIC
+ else if ( pEvent->event == AE_SV_DUSTTRAIL )
+ {
+ char szAttachment[128];
+ float flDuration;
+ float flSize;
+ if (sscanf( pEvent->options, "%s %f %f", szAttachment, &flDuration, &flSize ) == 3)
+ {
+ CHandle<DustTrail> hDustTrail;
+
+ hDustTrail = DustTrail::CreateDustTrail();
+
+ if( hDustTrail )
+ {
+ hDustTrail->m_SpawnRate = 4; // Particles per second
+ hDustTrail->m_ParticleLifetime = 1.5; // Lifetime of each particle, In seconds
+ hDustTrail->m_Color.Init(0.5f, 0.46f, 0.44f);
+ hDustTrail->m_StartSize = flSize;
+ hDustTrail->m_EndSize = hDustTrail->m_StartSize * 8;
+ hDustTrail->m_SpawnRadius = 3; // Each particle randomly offset from the center up to this many units
+ hDustTrail->m_MinSpeed = 4; // u/sec
+ hDustTrail->m_MaxSpeed = 10; // u/sec
+ hDustTrail->m_Opacity = 0.5f;
+ hDustTrail->SetLifetime(flDuration); // Lifetime of the spawner, in seconds
+ hDustTrail->m_StopEmitTime = gpGlobals->curtime + flDuration;
+ hDustTrail->SetParent( this, LookupAttachment( szAttachment ) );
+ hDustTrail->SetLocalOrigin( vec3_origin );
+ }
+ }
+ else
+ {
+ DevWarning( 1, "%s unable to parse AE_SV_DUSTTRAIL event \"%s\"\n", STRING( GetModelName() ), pEvent->options );
+ }
+
+ return;
+ }
+#endif
+ }
+
+ // Failed to find a handler
+ const char *pName = EventList_NameForIndex( pEvent->event );
+ if ( pName)
+ {
+ DevWarning( 1, "Unhandled animation event %s for %s\n", pName, GetClassname() );
+ }
+ else
+ {
+ DevWarning( 1, "Unhandled animation event %d for %s\n", pEvent->event, GetClassname() );
+ }
+}
+
+// SetPoseParamater()
+
+//=========================================================
+//=========================================================
+float CBaseAnimating::SetPoseParameter( CStudioHdr *pStudioHdr, const char *szName, float flValue )
+{
+ int poseParam = LookupPoseParameter( pStudioHdr, szName );
+ AssertMsg2(poseParam >= 0, "SetPoseParameter called with invalid argument %s by %s", szName, GetDebugName());
+ return SetPoseParameter( pStudioHdr, poseParam, flValue );
+}
+
+float CBaseAnimating::SetPoseParameter( CStudioHdr *pStudioHdr, int iParameter, float flValue )
+{
+ if ( !pStudioHdr )
+ {
+ return flValue;
+ }
+
+ if (iParameter >= 0)
+ {
+ float flNewValue;
+ flValue = Studio_SetPoseParameter( pStudioHdr, iParameter, flValue, flNewValue );
+ m_flPoseParameter.Set( iParameter, flNewValue );
+ }
+
+ return flValue;
+}
+
+//=========================================================
+//=========================================================
+float CBaseAnimating::GetPoseParameter( const char *szName )
+{
+ return GetPoseParameter( LookupPoseParameter( szName ) );
+}
+
+float CBaseAnimating::GetPoseParameter( int iParameter )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+
+ if ( !pstudiohdr )
+ {
+ Assert(!"CBaseAnimating::GetPoseParameter: model missing");
+ return 0.0;
+ }
+
+ if ( !pstudiohdr->SequencesAvailable() )
+ {
+ return 0;
+ }
+
+ if (iParameter >= 0)
+ {
+ return Studio_GetPoseParameter( pstudiohdr, iParameter, m_flPoseParameter[ iParameter ] );
+ }
+
+ return 0.0;
+}
+
+bool CBaseAnimating::GetPoseParameterRange( int index, float &minValue, float &maxValue )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+
+ if (pStudioHdr)
+ {
+ if (index >= 0 && index < pStudioHdr->GetNumPoseParameters())
+ {
+ const mstudioposeparamdesc_t &pose = pStudioHdr->pPoseParameter( index );
+ minValue = pose.start;
+ maxValue = pose.end;
+ return true;
+ }
+ }
+ minValue = 0.0f;
+ maxValue = 1.0f;
+ return false;
+}
+
+//=========================================================
+//=========================================================
+int CBaseAnimating::LookupPoseParameter( CStudioHdr *pStudioHdr, const char *szName )
+{
+ if ( !pStudioHdr )
+ return 0;
+
+ if ( !pStudioHdr->SequencesAvailable() )
+ {
+ return 0;
+ }
+
+ for (int i = 0; i < pStudioHdr->GetNumPoseParameters(); i++)
+ {
+ if (Q_stricmp( pStudioHdr->pPoseParameter( i ).pszName(), szName ) == 0)
+ {
+ return i;
+ }
+ }
+
+ // AssertMsg( 0, UTIL_VarArgs( "poseparameter %s couldn't be mapped!!!\n", szName ) );
+ return -1; // Error
+}
+
+//=========================================================
+//=========================================================
+bool CBaseAnimating::HasPoseParameter( int iSequence, const char *szName )
+{
+ int iParameter = LookupPoseParameter( szName );
+ if (iParameter == -1)
+ {
+ return false;
+ }
+
+ return HasPoseParameter( iSequence, iParameter );
+}
+
+//=========================================================
+//=========================================================
+bool CBaseAnimating::HasPoseParameter( int iSequence, int iParameter )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+
+ if ( !pstudiohdr )
+ {
+ return false;
+ }
+
+ if ( !pstudiohdr->SequencesAvailable() )
+ {
+ return false;
+ }
+
+ if (iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq())
+ {
+ return false;
+ }
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( iSequence );
+ if (pstudiohdr->GetSharedPoseParameter( iSequence, seqdesc.paramindex[0] ) == iParameter ||
+ pstudiohdr->GetSharedPoseParameter( iSequence, seqdesc.paramindex[1] ) == iParameter)
+ {
+ return true;
+ }
+ return false;
+}
+
+
+//=========================================================
+// Each class that wants to use pose parameters should populate
+// static variables in this entry point, rather than calling
+// GetPoseParameter(const char*) every time you want to adjust
+// an animation.
+//
+// Make sure to call BaseClass::PopulatePoseParameters() at
+// the *bottom* of your function.
+//=========================================================
+void CBaseAnimating::PopulatePoseParameters( void )
+{
+
+}
+
+//=========================================================
+// Purpose: from input of 75% to 200% of maximum range, rescale smoothly from 75% to 100%
+//=========================================================
+float CBaseAnimating::EdgeLimitPoseParameter( int iParameter, float flValue, float flBase )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if ( !pstudiohdr )
+ {
+ return flValue;
+ }
+
+ if (iParameter < 0 || iParameter >= pstudiohdr->GetNumPoseParameters())
+ {
+ return flValue;
+ }
+
+ const mstudioposeparamdesc_t &Pose = pstudiohdr->pPoseParameter( iParameter );
+
+ if (Pose.loop || Pose.start == Pose.end)
+ {
+ return flValue;
+ }
+
+ return RangeCompressor( flValue, Pose.start, Pose.end, flBase );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns index number of a given named bone
+// Input : name of a bone
+// Output : Bone index number or -1 if bone not found
+//-----------------------------------------------------------------------------
+int CBaseAnimating::LookupBone( const char *szName )
+{
+ const CStudioHdr *pStudioHdr = GetModelPtr();
+ Assert( pStudioHdr );
+ if ( !pStudioHdr )
+ return -1;
+ return Studio_BoneIndexByName( pStudioHdr, szName );
+}
+
+
+//=========================================================
+//=========================================================
+void CBaseAnimating::GetBonePosition ( int iBone, Vector &origin, QAngle &angles )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if (!pStudioHdr)
+ {
+ Assert(!"CBaseAnimating::GetBonePosition: model missing");
+ return;
+ }
+
+ if (iBone < 0 || iBone >= pStudioHdr->numbones())
+ {
+ Assert(!"CBaseAnimating::GetBonePosition: invalid bone index");
+ return;
+ }
+
+ matrix3x4_t bonetoworld;
+ GetBoneTransform( iBone, bonetoworld );
+
+ MatrixAngles( bonetoworld, angles, origin );
+}
+
+
+
+//=========================================================
+//=========================================================
+
+void CBaseAnimating::GetBoneTransform( int iBone, matrix3x4_t &pBoneToWorld )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+
+ if (!pStudioHdr)
+ {
+ Assert(!"CBaseAnimating::GetBoneTransform: model missing");
+ return;
+ }
+
+ if (iBone < 0 || iBone >= pStudioHdr->numbones())
+ {
+ Assert(!"CBaseAnimating::GetBoneTransform: invalid bone index");
+ return;
+ }
+
+ CBoneCache *pcache = GetBoneCache( );
+
+ matrix3x4_t *pmatrix = pcache->GetCachedBone( iBone );
+
+ if ( !pmatrix )
+ {
+ MatrixCopy( EntityToWorldTransform(), pBoneToWorld );
+ return;
+ }
+
+ Assert( pmatrix );
+
+ // FIXME
+ MatrixCopy( *pmatrix, pBoneToWorld );
+}
+
+class CTraceFilterSkipNPCs : public CTraceFilterSimple
+{
+public:
+ CTraceFilterSkipNPCs( const IHandleEntity *passentity, int collisionGroup )
+ : CTraceFilterSimple( passentity, collisionGroup )
+ {
+ }
+
+ virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
+ {
+ if ( CTraceFilterSimple::ShouldHitEntity(pServerEntity, contentsMask) )
+ {
+ CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
+ if ( pEntity->IsNPC() )
+ return false;
+
+ return true;
+ }
+ return false;
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Receives the clients IK floor position
+//-----------------------------------------------------------------------------
+
+void CBaseAnimating::SetIKGroundContactInfo( float minHeight, float maxHeight )
+{
+ m_flIKGroundContactTime = gpGlobals->curtime;
+ m_flIKGroundMinHeight = minHeight;
+ m_flIKGroundMaxHeight = maxHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes IK floor position
+//-----------------------------------------------------------------------------
+
+void CBaseAnimating::InitStepHeightAdjust( void )
+{
+ m_flIKGroundContactTime = 0;
+ m_flIKGroundMinHeight = 0;
+ m_flIKGroundMaxHeight = 0;
+
+ // FIXME: not safe to call GetAbsOrigin here. Hierarchy might not be set up!
+ m_flEstIkFloor = GetAbsOrigin().z;
+ m_flEstIkOffset = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Interpolates client IK floor position and drops entity down so that the feet will reach
+//-----------------------------------------------------------------------------
+
+ConVar npc_height_adjust( "npc_height_adjust", "1", FCVAR_ARCHIVE, "Enable test mode for ik height adjustment" );
+
+void CBaseAnimating::UpdateStepOrigin()
+{
+ if (!npc_height_adjust.GetBool())
+ {
+ m_flEstIkOffset = 0;
+ m_flEstIkFloor = GetLocalOrigin().z;
+ return;
+ }
+
+ /*
+ if (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
+ {
+ Msg("%x : %x\n", GetMoveParent(), GetGroundEntity() );
+ }
+ */
+
+ if (m_flIKGroundContactTime > 0.2 && m_flIKGroundContactTime > gpGlobals->curtime - 0.2)
+ {
+ if ((GetFlags() & (FL_FLY | FL_SWIM)) == 0 && GetMoveParent() == NULL && GetGroundEntity() != NULL && !GetGroundEntity()->IsMoving())
+ {
+ Vector toAbs = GetAbsOrigin() - GetLocalOrigin();
+ if (toAbs.z == 0.0)
+ {
+ CAI_BaseNPC *pNPC = MyNPCPointer();
+ // FIXME: There needs to be a default step height somewhere
+ float height = 18.0f;
+ if (pNPC)
+ {
+ height = pNPC->StepHeight();
+ }
+
+ // debounce floor location
+ m_flEstIkFloor = m_flEstIkFloor * 0.2 + m_flIKGroundMinHeight * 0.8;
+
+ // don't let heigth difference between min and max exceed step height
+ float bias = clamp( (m_flIKGroundMaxHeight - m_flIKGroundMinHeight) - height, 0.f, height );
+ // save off reasonable offset
+ m_flEstIkOffset = clamp( m_flEstIkFloor - GetAbsOrigin().z, -height + bias, 0.0f );
+ return;
+ }
+ }
+ }
+
+ // don't use floor offset, decay the value
+ m_flEstIkOffset *= 0.5;
+ m_flEstIkFloor = GetLocalOrigin().z;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the origin to use for model rendering
+//-----------------------------------------------------------------------------
+
+Vector CBaseAnimating::GetStepOrigin( void ) const
+{
+ Vector tmp = GetLocalOrigin();
+ tmp.z += m_flEstIkOffset;
+ return tmp;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the origin to use for model rendering
+//-----------------------------------------------------------------------------
+
+QAngle CBaseAnimating::GetStepAngles( void ) const
+{
+ // TODO: Add in body lean
+ return GetLocalAngles();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find IK collisions with world
+// Input :
+// Output : fills out m_pIk targets, calcs floor offset for rendering
+//-----------------------------------------------------------------------------
+
+void CBaseAnimating::CalculateIKLocks( float currentTime )
+{
+ if ( m_pIk )
+ {
+ Ray_t ray;
+ CTraceFilterSkipNPCs traceFilter( this, GetCollisionGroup() );
+ Vector up;
+ GetVectors( NULL, NULL, &up );
+ // FIXME: check number of slots?
+ for (int i = 0; i < m_pIk->m_target.Count(); i++)
+ {
+ trace_t trace;
+ CIKTarget *pTarget = &m_pIk->m_target[i];
+
+ if (!pTarget->IsActive())
+ continue;
+
+ switch( pTarget->type )
+ {
+ case IK_GROUND:
+ {
+ Vector estGround;
+ estGround = (pTarget->est.pos - GetAbsOrigin());
+ estGround = estGround - (estGround * up) * up;
+ estGround = GetAbsOrigin() + estGround + pTarget->est.floor * up;
+
+ Vector p1, p2;
+ VectorMA( estGround, pTarget->est.height, up, p1 );
+ VectorMA( estGround, -pTarget->est.height, up, p2 );
+
+ float r = MAX(pTarget->est.radius,1);
+
+ // don't IK to other characters
+ ray.Init( p1, p2, Vector(-r,-r,0), Vector(r,r,1) );
+ enginetrace->TraceRay( ray, MASK_SOLID, &traceFilter, &trace );
+
+ /*
+ debugoverlay->AddBoxOverlay( p1, Vector(-r,-r,0), Vector(r,r,1), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 1.0f );
+ debugoverlay->AddBoxOverlay( trace.endpos, Vector(-r,-r,0), Vector(r,r,1), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 1.0f );
+ debugoverlay->AddLineOverlay( p1, trace.endpos, 255, 0, 0, 0, 1.0f );
+ */
+
+ if (trace.startsolid)
+ {
+ ray.Init( pTarget->trace.hip, pTarget->est.pos, Vector(-r,-r,0), Vector(r,r,1) );
+
+ enginetrace->TraceRay( ray, MASK_SOLID, &traceFilter, &trace );
+
+ p1 = trace.endpos;
+ VectorMA( p1, - pTarget->est.height, up, p2 );
+ ray.Init( p1, p2, Vector(-r,-r,0), Vector(r,r,1) );
+
+ enginetrace->TraceRay( ray, MASK_SOLID, &traceFilter, &trace );
+ }
+
+ if (!trace.startsolid)
+ {
+ if (trace.DidHitWorld())
+ {
+ pTarget->SetPosWithNormalOffset( trace.endpos, trace.plane.normal );
+ pTarget->SetNormal( trace.plane.normal );
+ }
+ else
+ {
+ pTarget->SetPos( trace.endpos );
+ pTarget->SetAngles( GetAbsAngles() );
+ }
+
+ }
+ }
+ break;
+ case IK_ATTACHMENT:
+ {
+ // anything on the server?
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Clear out animation states that are invalidated with Teleport
+//-----------------------------------------------------------------------------
+
+void CBaseAnimating::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
+{
+ BaseClass::Teleport( newPosition, newAngles, newVelocity );
+ if (m_pIk)
+ {
+ m_pIk->ClearTargets( );
+ }
+ InitStepHeightAdjust();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: build matrices first from the parent, then from the passed in arrays if the bone doesn't exist on the parent
+//-----------------------------------------------------------------------------
+
+void CBaseAnimating::BuildMatricesWithBoneMerge(
+ const CStudioHdr *pStudioHdr,
+ const QAngle& angles,
+ const Vector& origin,
+ const Vector pos[MAXSTUDIOBONES],
+ const Quaternion q[MAXSTUDIOBONES],
+ matrix3x4_t bonetoworld[MAXSTUDIOBONES],
+ CBaseAnimating *pParent,
+ CBoneCache *pParentCache
+ )
+{
+ CStudioHdr *fhdr = pParent->GetModelPtr();
+ mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
+
+ matrix3x4_t rotationmatrix; // model to world transformation
+ AngleMatrix( angles, origin, rotationmatrix);
+
+ for ( int i=0; i < pStudioHdr->numbones(); i++ )
+ {
+ // Now find the bone in the parent entity.
+ bool merged = false;
+ int parentBoneIndex = Studio_BoneIndexByName( fhdr, pbones[i].pszName() );
+ if ( parentBoneIndex >= 0 )
+ {
+ matrix3x4_t *pMat = pParentCache->GetCachedBone( parentBoneIndex );
+ if ( pMat )
+ {
+ MatrixCopy( *pMat, bonetoworld[ i ] );
+ merged = true;
+ }
+ }
+
+ if ( !merged )
+ {
+ // If we get down here, then the bone wasn't merged.
+ matrix3x4_t bonematrix;
+ QuaternionMatrix( q[i], pos[i], bonematrix );
+
+ if (pbones[i].parent == -1)
+ {
+ ConcatTransforms (rotationmatrix, bonematrix, bonetoworld[i]);
+ }
+ else
+ {
+ ConcatTransforms (bonetoworld[pbones[i].parent], bonematrix, bonetoworld[i]);
+ }
+ }
+ }
+}
+
+ConVar sv_pvsskipanimation( "sv_pvsskipanimation", "1", FCVAR_ARCHIVE, "Skips SetupBones when npc's are outside the PVS" );
+ConVar ai_setupbones_debug( "ai_setupbones_debug", "0", 0, "Shows that bones that are setup every think" );
+
+
+
+
+inline bool CBaseAnimating::CanSkipAnimation( void )
+{
+ if ( !sv_pvsskipanimation.GetBool() )
+ return false;
+
+ CAI_BaseNPC *pNPC = MyNPCPointer();
+ if ( pNPC && !pNPC->HasCondition( COND_IN_PVS ) && ( m_fBoneCacheFlags & (BCF_NO_ANIMATION_SKIP | BCF_IS_IN_SPAWN) ) == false )
+ {
+ // If we have a player as a child, then we better setup our bones. If we don't,
+ // the PVS will be screwy.
+ return !DoesHavePlayerChild();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void CBaseAnimating::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
+{
+ AUTO_LOCK( m_BoneSetupMutex );
+
+ VPROF_BUDGET( "CBaseAnimating::SetupBones", VPROF_BUDGETGROUP_SERVER_ANIM );
+
+ MDLCACHE_CRITICAL_SECTION();
+
+ Assert( GetModelPtr() );
+
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+
+ if(!pStudioHdr)
+ {
+ Assert(!"CBaseAnimating::GetSkeleton() without a model");
+ return;
+ }
+
+ Assert( !IsEFlagSet( EFL_SETTING_UP_BONES ) );
+
+ AddEFlags( EFL_SETTING_UP_BONES );
+
+ Vector pos[MAXSTUDIOBONES];
+ Quaternion q[MAXSTUDIOBONES];
+
+ // adjust hit boxes based on IK driven offset
+ Vector adjOrigin = GetAbsOrigin() + Vector( 0, 0, m_flEstIkOffset );
+
+ if ( CanSkipAnimation() )
+ {
+ IBoneSetup boneSetup( pStudioHdr, boneMask, GetPoseParameterArray() );
+ boneSetup.InitPose( pos, q );
+ // Msg( "%.03f : %s:%s not in pvs\n", gpGlobals->curtime, GetClassname(), GetEntityName().ToCStr() );
+ }
+ else
+ {
+ if ( m_pIk )
+ {
+ // FIXME: pass this into Studio_BuildMatrices to skip transforms
+ CBoneBitList boneComputed;
+ m_iIKCounter++;
+ m_pIk->Init( pStudioHdr, GetAbsAngles(), adjOrigin, gpGlobals->curtime, m_iIKCounter, boneMask );
+ GetSkeleton( pStudioHdr, pos, q, boneMask );
+
+ m_pIk->UpdateTargets( pos, q, pBoneToWorld, boneComputed );
+ CalculateIKLocks( gpGlobals->curtime );
+ m_pIk->SolveDependencies( pos, q, pBoneToWorld, boneComputed );
+ }
+ else
+ {
+ // Msg( "%.03f : %s:%s\n", gpGlobals->curtime, GetClassname(), GetEntityName().ToCStr() );
+ GetSkeleton( pStudioHdr, pos, q, boneMask );
+ }
+ }
+
+ CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );
+ if ( pParent )
+ {
+ // We're doing bone merging, so do special stuff here.
+ CBoneCache *pParentCache = pParent->GetBoneCache();
+ if ( pParentCache )
+ {
+ BuildMatricesWithBoneMerge(
+ pStudioHdr,
+ GetAbsAngles(),
+ adjOrigin,
+ pos,
+ q,
+ pBoneToWorld,
+ pParent,
+ pParentCache );
+
+ RemoveEFlags( EFL_SETTING_UP_BONES );
+ if (ai_setupbones_debug.GetBool())
+ {
+ DrawRawSkeleton( pBoneToWorld, boneMask, true, 0.11 );
+ }
+ return;
+ }
+ }
+
+ Studio_BuildMatrices(
+ pStudioHdr,
+ GetAbsAngles(),
+ adjOrigin,
+ pos,
+ q,
+ -1,
+ GetModelScale(), // Scaling
+ pBoneToWorld,
+ boneMask );
+
+ if (ai_setupbones_debug.GetBool())
+ {
+ // Msg("%s:%s:%s (%x)\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask );
+ DrawRawSkeleton( pBoneToWorld, boneMask, true, 0.11 );
+ }
+ RemoveEFlags( EFL_SETTING_UP_BONES );
+}
+
+//=========================================================
+//=========================================================
+int CBaseAnimating::GetNumBones ( void )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if(pStudioHdr)
+ {
+ return pStudioHdr->numbones();
+ }
+ else
+ {
+ Assert(!"CBaseAnimating::GetNumBones: model missing");
+ return 0;
+ }
+}
+
+
+//=========================================================
+//=========================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns index number of a given named attachment
+// Input : name of attachment
+// Output : attachment index number or -1 if attachment not found
+//-----------------------------------------------------------------------------
+int CBaseAnimating::LookupAttachment( const char *szName )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if (!pStudioHdr)
+ {
+ Assert(!"CBaseAnimating::LookupAttachment: model missing");
+ return 0;
+ }
+
+ // The +1 is to make attachment indices be 1-based (namely 0 == invalid or unused attachment)
+ return Studio_FindAttachment( pStudioHdr, szName ) + 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the world location and world angles of an attachment
+// Input : attachment name
+// Output : location and angles
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::GetAttachment( const char *szName, Vector &absOrigin, QAngle &absAngles )
+{
+ return GetAttachment( LookupAttachment( szName ), absOrigin, absAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the world location and world angles of an attachment
+// Input : attachment index
+// Output : location and angles
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::GetAttachment ( int iAttachment, Vector &absOrigin, QAngle &absAngles )
+{
+ matrix3x4_t attachmentToWorld;
+
+ bool bRet = GetAttachment( iAttachment, attachmentToWorld );
+ MatrixAngles( attachmentToWorld, absAngles, absOrigin );
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the world location and world angles of an attachment
+// Input : attachment index
+// Output : location and angles
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::GetAttachment( int iAttachment, matrix3x4_t &attachmentToWorld )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if (!pStudioHdr)
+ {
+ MatrixCopy(EntityToWorldTransform(), attachmentToWorld);
+ AssertOnce(!"CBaseAnimating::GetAttachment: model missing");
+ return false;
+ }
+
+ if (iAttachment < 1 || iAttachment > pStudioHdr->GetNumAttachments())
+ {
+ MatrixCopy(EntityToWorldTransform(), attachmentToWorld);
+// Assert(!"CBaseAnimating::GetAttachment: invalid attachment index");
+ return false;
+ }
+
+ const mstudioattachment_t &pattachment = pStudioHdr->pAttachment( iAttachment-1 );
+ int iBone = pStudioHdr->GetAttachmentBone( iAttachment-1 );
+
+ matrix3x4_t bonetoworld;
+ GetBoneTransform( iBone, bonetoworld );
+ if ( (pattachment.flags & ATTACHMENT_FLAG_WORLD_ALIGN) == 0 )
+ {
+ ConcatTransforms( bonetoworld, pattachment.local, attachmentToWorld );
+ }
+ else
+ {
+ Vector vecLocalBonePos, vecWorldBonePos;
+ MatrixGetColumn( pattachment.local, 3, vecLocalBonePos );
+ VectorTransform( vecLocalBonePos, bonetoworld, vecWorldBonePos );
+
+ SetIdentityMatrix( attachmentToWorld );
+ MatrixSetColumn( vecWorldBonePos, 3, attachmentToWorld );
+ }
+
+ return true;
+}
+
+// gets the bone for an attachment
+int CBaseAnimating::GetAttachmentBone( int iAttachment )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if (!pStudioHdr || iAttachment < 1 || iAttachment > pStudioHdr->GetNumAttachments() )
+ {
+ AssertOnce(pStudioHdr && "CBaseAnimating::GetAttachment: model missing");
+ return 0;
+ }
+
+ return pStudioHdr->GetAttachmentBone( iAttachment-1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the world location of an attachment
+// Input : attachment index
+// Output : location and angles
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::GetAttachment( const char *szName, Vector &absOrigin, Vector *forward, Vector *right, Vector *up )
+{
+ return GetAttachment( LookupAttachment( szName ), absOrigin, forward, right, up );
+}
+
+bool CBaseAnimating::GetAttachment( int iAttachment, Vector &absOrigin, Vector *forward, Vector *right, Vector *up )
+{
+ matrix3x4_t attachmentToWorld;
+
+ bool bRet = GetAttachment( iAttachment, attachmentToWorld );
+ MatrixPosition( attachmentToWorld, absOrigin );
+ if (forward)
+ {
+ MatrixGetColumn( attachmentToWorld, 0, forward );
+ }
+ if (right)
+ {
+ MatrixGetColumn( attachmentToWorld, 1, right );
+ }
+ if (up)
+ {
+ MatrixGetColumn( attachmentToWorld, 2, up );
+ }
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the attachment in local space
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::GetAttachmentLocal( const char *szName, Vector &origin, QAngle &angles )
+{
+ return GetAttachmentLocal( LookupAttachment( szName ), origin, angles );
+}
+
+bool CBaseAnimating::GetAttachmentLocal( int iAttachment, Vector &origin, QAngle &angles )
+{
+ matrix3x4_t attachmentToEntity;
+
+ bool bRet = GetAttachmentLocal( iAttachment, attachmentToEntity );
+ MatrixAngles( attachmentToEntity, angles, origin );
+ return bRet;
+}
+
+bool CBaseAnimating::GetAttachmentLocal( int iAttachment, matrix3x4_t &attachmentToLocal )
+{
+ matrix3x4_t attachmentToWorld;
+ bool bRet = GetAttachment(iAttachment, attachmentToWorld);
+ matrix3x4_t worldToEntity;
+ MatrixInvert( EntityToWorldTransform(), worldToEntity );
+ ConcatTransforms( worldToEntity, attachmentToWorld, attachmentToLocal );
+ return bRet;
+}
+
+
+//=========================================================
+//=========================================================
+void CBaseAnimating::GetEyeballs( Vector &origin, QAngle &angles )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if (!pStudioHdr)
+ {
+ Assert(!"CBaseAnimating::GetAttachment: model missing");
+ return;
+ }
+
+ for (int iBodypart = 0; iBodypart < pStudioHdr->numbodyparts(); iBodypart++)
+ {
+ mstudiobodyparts_t *pBodypart = pStudioHdr->pBodypart( iBodypart );
+ for (int iModel = 0; iModel < pBodypart->nummodels; iModel++)
+ {
+ mstudiomodel_t *pModel = pBodypart->pModel( iModel );
+ for (int iEyeball = 0; iEyeball < pModel->numeyeballs; iEyeball++)
+ {
+ mstudioeyeball_t *pEyeball = pModel->pEyeball( iEyeball );
+ matrix3x4_t bonetoworld;
+ GetBoneTransform( pEyeball->bone, bonetoworld );
+ VectorTransform( pEyeball->org, bonetoworld, origin );
+ MatrixAngles( bonetoworld, angles ); // ???
+ }
+ }
+ }
+}
+
+
+//=========================================================
+//=========================================================
+int CBaseAnimating::FindTransitionSequence( int iCurrentSequence, int iGoalSequence, int *piDir )
+{
+ Assert( GetModelPtr() );
+
+ if (piDir == NULL)
+ {
+ int iDir = 1;
+ int sequence = ::FindTransitionSequence( GetModelPtr(), iCurrentSequence, iGoalSequence, &iDir );
+ if (iDir != 1)
+ return -1;
+ else
+ return sequence;
+ }
+
+ return ::FindTransitionSequence( GetModelPtr(), iCurrentSequence, iGoalSequence, piDir );
+}
+
+
+bool CBaseAnimating::GotoSequence( int iCurrentSequence, float flCurrentCycle, float flCurrentRate, int iGoalSequence, int &nNextSequence, float &flNextCycle, int &iNextDir )
+{
+ return ::GotoSequence( GetModelPtr(), iCurrentSequence, flCurrentCycle, flCurrentRate, iGoalSequence, nNextSequence, flNextCycle, iNextDir );
+}
+
+
+int CBaseAnimating::GetEntryNode( int iSequence )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr();
+ if (! pstudiohdr)
+ return 0;
+
+ return pstudiohdr->EntryNode( iSequence );
+}
+
+
+int CBaseAnimating::GetExitNode( int iSequence )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr();
+ if (! pstudiohdr)
+ return 0;
+
+ return pstudiohdr->ExitNode( iSequence );
+}
+
+
+//=========================================================
+//=========================================================
+
+void CBaseAnimating::SetBodygroup( int iGroup, int iValue )
+{
+ // SetBodygroup is not supported on pending dynamic models. Wait for it to load!
+ // XXX TODO we could buffer up the group and value if we really needed to. -henryg
+ Assert( GetModelPtr() );
+ int newBody = m_nBody;
+ ::SetBodygroup( GetModelPtr( ), newBody, iGroup, iValue );
+ m_nBody = newBody;
+}
+
+int CBaseAnimating::GetBodygroup( int iGroup )
+{
+ Assert( IsDynamicModelLoading() || GetModelPtr() );
+ return IsDynamicModelLoading() ? 0 : ::GetBodygroup( GetModelPtr( ), m_nBody, iGroup );
+}
+
+const char *CBaseAnimating::GetBodygroupName( int iGroup )
+{
+ Assert( IsDynamicModelLoading() || GetModelPtr() );
+ return IsDynamicModelLoading() ? "" : ::GetBodygroupName( GetModelPtr( ), iGroup );
+}
+
+int CBaseAnimating::FindBodygroupByName( const char *name )
+{
+ Assert( IsDynamicModelLoading() || GetModelPtr() );
+ return IsDynamicModelLoading() ? -1 : ::FindBodygroupByName( GetModelPtr( ), name );
+}
+
+int CBaseAnimating::GetBodygroupCount( int iGroup )
+{
+ Assert( IsDynamicModelLoading() || GetModelPtr() );
+ return IsDynamicModelLoading() ? 0 : ::GetBodygroupCount( GetModelPtr( ), iGroup );
+}
+
+int CBaseAnimating::GetNumBodyGroups( void )
+{
+ Assert( IsDynamicModelLoading() || GetModelPtr() );
+ return IsDynamicModelLoading() ? 0 : ::GetNumBodyGroups( GetModelPtr( ) );
+}
+
+int CBaseAnimating::ExtractBbox( int sequence, Vector& mins, Vector& maxs )
+{
+ Assert( IsDynamicModelLoading() || GetModelPtr() );
+ return IsDynamicModelLoading() ? 0 : ::ExtractBbox( GetModelPtr( ), sequence, mins, maxs );
+}
+
+//=========================================================
+//=========================================================
+
+void CBaseAnimating::SetSequenceBox( void )
+{
+ Vector mins, maxs;
+
+ // Get sequence bbox
+ if ( ExtractBbox( GetSequence(), mins, maxs ) )
+ {
+ // expand box for rotation
+ // find min / max for rotations
+ float yaw = GetLocalAngles().y * (M_PI / 180.0);
+
+ Vector xvector, yvector;
+ xvector.x = cos(yaw);
+ xvector.y = sin(yaw);
+ yvector.x = -sin(yaw);
+ yvector.y = cos(yaw);
+ Vector bounds[2];
+
+ bounds[0] = mins;
+ bounds[1] = maxs;
+
+ Vector rmin( 9999, 9999, 9999 );
+ Vector rmax( -9999, -9999, -9999 );
+ Vector base, transformed;
+
+ for (int i = 0; i <= 1; i++ )
+ {
+ base.x = bounds[i].x;
+ for ( int j = 0; j <= 1; j++ )
+ {
+ base.y = bounds[j].y;
+ for ( int k = 0; k <= 1; k++ )
+ {
+ base.z = bounds[k].z;
+
+ // transform the point
+ transformed.x = xvector.x*base.x + yvector.x*base.y;
+ transformed.y = xvector.y*base.x + yvector.y*base.y;
+ transformed.z = base.z;
+
+ for ( int l = 0; l < 3; l++ )
+ {
+ if (transformed[l] < rmin[l])
+ rmin[l] = transformed[l];
+ if (transformed[l] > rmax[l])
+ rmax[l] = transformed[l];
+ }
+ }
+ }
+ }
+ rmin.z = 0;
+ rmax.z = rmin.z + 1;
+ UTIL_SetSize( this, rmin, rmax );
+ }
+}
+
+//=========================================================
+//=========================================================
+int CBaseAnimating::RegisterPrivateActivity( const char *pszActivityName )
+{
+ return ActivityList_RegisterPrivateActivity( pszActivityName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Notifies the console that this entity could not retrieve an
+// animation sequence for the specified activity. This probably means
+// there's a typo in the model QC file, or the sequence is missing
+// entirely.
+//
+//
+// Input : iActivity - The activity that failed to resolve to a sequence.
+//
+//
+// NOTE : IMPORTANT - Something needs to be done so that private activities
+// (which are allowed to collide in the activity list) remember each
+// entity that registered an activity there, and the activity name
+// each character registered.
+//-----------------------------------------------------------------------------
+void CBaseAnimating::ReportMissingActivity( int iActivity )
+{
+ Msg( "%s has no sequence for act:%s\n", GetClassname(), ActivityList_NameForIndex(iActivity) );
+}
+
+
+LocalFlexController_t CBaseAnimating::GetNumFlexControllers( void )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return LocalFlexController_t(0);
+
+ return pstudiohdr->numflexcontrollers();
+}
+
+
+const char *CBaseAnimating::GetFlexDescFacs( int iFlexDesc )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ mstudioflexdesc_t *pflexdesc = pstudiohdr->pFlexdesc( iFlexDesc );
+
+ return pflexdesc->pszFACS( );
+}
+
+const char *CBaseAnimating::GetFlexControllerName( LocalFlexController_t iFlexController )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( iFlexController );
+
+ return pflexcontroller->pszName( );
+}
+
+const char *CBaseAnimating::GetFlexControllerType( LocalFlexController_t iFlexController )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( iFlexController );
+
+ return pflexcontroller->pszType( );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Converts the ground speed of the animating entity into a true velocity
+// Output : Vector - velocity of the character at its current m_flGroundSpeed
+//-----------------------------------------------------------------------------
+Vector CBaseAnimating::GetGroundSpeedVelocity( void )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr();
+ if (!pstudiohdr)
+ return vec3_origin;
+
+ QAngle vecAngles;
+ Vector vecVelocity;
+
+ vecAngles.y = GetSequenceMoveYaw( GetSequence() );
+ vecAngles.x = 0;
+ vecAngles.z = 0;
+
+ vecAngles.y += GetLocalAngles().y;
+
+ AngleVectors( vecAngles, &vecVelocity );
+
+ vecVelocity = vecVelocity * m_flGroundSpeed;
+
+ return vecVelocity;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+float CBaseAnimating::GetInstantaneousVelocity( float flInterval )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ // FIXME: someone needs to check for last frame, etc.
+ float flNextCycle = GetCycle() + flInterval * GetSequenceCycleRate( GetSequence() ) * m_flPlaybackRate;
+
+ Vector vecVelocity;
+ Studio_SeqVelocity( pstudiohdr, GetSequence(), flNextCycle, GetPoseParameterArray(), vecVelocity );
+ vecVelocity *= m_flPlaybackRate;
+
+ return vecVelocity.Length();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+float CBaseAnimating::GetEntryVelocity( int iSequence )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ Vector vecVelocity;
+ Studio_SeqVelocity( pstudiohdr, iSequence, 0.0, GetPoseParameterArray(), vecVelocity );
+
+ return vecVelocity.Length();
+}
+
+float CBaseAnimating::GetExitVelocity( int iSequence )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ Vector vecVelocity;
+ Studio_SeqVelocity( pstudiohdr, iSequence, 1.0, GetPoseParameterArray(), vecVelocity );
+
+ return vecVelocity.Length();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::GetIntervalMovement( float flIntervalUsed, bool &bMoveSeqFinished, Vector &newPosition, QAngle &newAngles )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr || !pstudiohdr->SequencesAvailable())
+ return false;
+
+ float flComputedCycleRate = GetSequenceCycleRate( GetSequence() );
+
+ float flNextCycle = GetCycle() + flIntervalUsed * flComputedCycleRate * m_flPlaybackRate;
+
+ if ((!m_bSequenceLoops) && flNextCycle > 1.0)
+ {
+ flIntervalUsed = GetCycle() / (flComputedCycleRate * m_flPlaybackRate);
+ flNextCycle = 1.0;
+ bMoveSeqFinished = true;
+ }
+ else
+ {
+ bMoveSeqFinished = false;
+ }
+
+ Vector deltaPos;
+ QAngle deltaAngles;
+
+ if (Studio_SeqMovement( pstudiohdr, GetSequence(), GetCycle(), flNextCycle, GetPoseParameterArray(), deltaPos, deltaAngles ))
+ {
+ VectorYawRotate( deltaPos, GetLocalAngles().y, deltaPos );
+ newPosition = GetLocalOrigin() + deltaPos;
+ newAngles.Init();
+ newAngles.y = GetLocalAngles().y + deltaAngles.y;
+ return true;
+ }
+ else
+ {
+ newPosition = GetLocalOrigin();
+ newAngles = GetLocalAngles();
+ return false;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::GetSequenceMovement( int nSequence, float fromCycle, float toCycle, Vector &deltaPosition, QAngle &deltaAngles )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return false;
+
+ return Studio_SeqMovement( pstudiohdr, nSequence, fromCycle, toCycle, GetPoseParameterArray(), deltaPosition, deltaAngles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: find frame where they animation has moved a given distance.
+// Output :
+//-----------------------------------------------------------------------------
+float CBaseAnimating::GetMovementFrame( float flDist )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ float t = Studio_FindSeqDistance( pstudiohdr, GetSequence(), GetPoseParameterArray(), flDist );
+
+ return t;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: does a specific sequence have movement?
+// Output :
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::HasMovement( int iSequence )
+{
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return false;
+
+ // FIXME: this needs to check to see if there are keys, and the object is walking
+ Vector deltaPos;
+ QAngle deltaAngles;
+ if (Studio_SeqMovement( pstudiohdr, iSequence, 0.0f, 1.0f, GetPoseParameterArray(), deltaPos, deltaAngles ))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szModelName -
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetModel( const char *szModelName )
+{
+ MDLCACHE_CRITICAL_SECTION();
+
+ // delete exiting studio model container
+ UnlockStudioHdr();
+ delete m_pStudioHdr;
+ m_pStudioHdr = NULL;
+
+ if ( szModelName[0] )
+ {
+ int modelIndex = modelinfo->GetModelIndex( szModelName );
+ const model_t *model = modelinfo->GetModel( modelIndex );
+ if ( model && ( modelinfo->GetModelType( model ) != mod_studio ) )
+ {
+ Msg( "Setting CBaseAnimating to non-studio model %s (type:%i)\n", szModelName, modelinfo->GetModelType( model ) );
+ }
+ }
+
+ if ( m_boneCacheHandle )
+ {
+ Studio_DestroyBoneCache( m_boneCacheHandle );
+ m_boneCacheHandle = 0;
+ }
+
+ UTIL_SetModel( this, szModelName );
+
+ InitBoneControllers( );
+ SetSequence( 0 );
+
+ PopulatePoseParameters();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+//-----------------------------------------------------------------------------
+void CBaseAnimating::LockStudioHdr()
+{
+ AUTO_LOCK( m_StudioHdrInitLock );
+ const model_t *mdl = GetModel();
+ if (mdl)
+ {
+ MDLHandle_t hStudioHdr = modelinfo->GetCacheHandle( mdl );
+ if ( hStudioHdr != MDLHANDLE_INVALID )
+ {
+ const studiohdr_t *pStudioHdr = mdlcache->LockStudioHdr( hStudioHdr );
+ CStudioHdr *pStudioHdrContainer = NULL;
+ if ( !m_pStudioHdr )
+ {
+ if ( pStudioHdr )
+ {
+ pStudioHdrContainer = new CStudioHdr;
+ pStudioHdrContainer->Init( pStudioHdr, mdlcache );
+ }
+ }
+ else
+ {
+ pStudioHdrContainer = m_pStudioHdr;
+ }
+
+ Assert( ( pStudioHdr == NULL && pStudioHdrContainer == NULL ) || pStudioHdrContainer->GetRenderHdr() == pStudioHdr );
+
+ if ( pStudioHdrContainer && pStudioHdrContainer->GetVirtualModel() )
+ {
+ MDLHandle_t hVirtualModel = (MDLHandle_t)(int)(pStudioHdrContainer->GetRenderHdr()->virtualModel)&0xffff;
+ mdlcache->LockStudioHdr( hVirtualModel );
+ }
+ m_pStudioHdr = pStudioHdrContainer; // must be last to ensure virtual model correctly set up
+ }
+ }
+}
+
+void CBaseAnimating::UnlockStudioHdr()
+{
+ if ( m_pStudioHdr )
+ {
+ const model_t *mdl = GetModel();
+ if (mdl)
+ {
+ mdlcache->UnlockStudioHdr( modelinfo->GetCacheHandle( mdl ) );
+ if ( m_pStudioHdr->GetVirtualModel() )
+ {
+ MDLHandle_t hVirtualModel = (MDLHandle_t)(int)(m_pStudioHdr->GetRenderHdr()->virtualModel)&0xffff;
+ mdlcache->UnlockStudioHdr( hVirtualModel );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: return the index to the shared bone cache
+// Output :
+//-----------------------------------------------------------------------------
+CBoneCache *CBaseAnimating::GetBoneCache( void )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ Assert(pStudioHdr);
+
+ CBoneCache *pcache = Studio_GetBoneCache( m_boneCacheHandle );
+ int boneMask = BONE_USED_BY_HITBOX | BONE_USED_BY_ATTACHMENT;
+
+ // TF queries these bones to position weapons when players are killed
+#if defined( TF_DLL )
+ boneMask |= BONE_USED_BY_BONE_MERGE;
+#endif
+ if ( pcache )
+ {
+ if ( pcache->IsValid( gpGlobals->curtime ) && (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid <= gpGlobals->curtime)
+ {
+ // Msg("%s:%s:%s (%x:%x:%8.4f) cache\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask, pcache->m_boneMask, pcache->m_timeValid );
+ // in memory and still valid, use it!
+ return pcache;
+ }
+ // in memory, but missing some of the bone masks
+ if ( (pcache->m_boneMask & boneMask) != boneMask )
+ {
+ Studio_DestroyBoneCache( m_boneCacheHandle );
+ m_boneCacheHandle = 0;
+ pcache = NULL;
+ }
+ }
+
+ matrix3x4_t bonetoworld[MAXSTUDIOBONES];
+ SetupBones( bonetoworld, boneMask );
+
+ if ( pcache )
+ {
+ // still in memory but out of date, refresh the bones.
+ pcache->UpdateBones( bonetoworld, pStudioHdr->numbones(), gpGlobals->curtime );
+ }
+ else
+ {
+ bonecacheparams_t params;
+ params.pStudioHdr = pStudioHdr;
+ params.pBoneToWorld = bonetoworld;
+ params.curtime = gpGlobals->curtime;
+ params.boneMask = boneMask;
+
+ m_boneCacheHandle = Studio_CreateBoneCache( params );
+ pcache = Studio_GetBoneCache( m_boneCacheHandle );
+ }
+ Assert(pcache);
+ return pcache;
+}
+
+
+void CBaseAnimating::InvalidateBoneCache( void )
+{
+ Studio_InvalidateBoneCache( m_boneCacheHandle );
+}
+
+bool CBaseAnimating::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
+{
+ // Return a special case for scaled physics objects
+ if ( GetModelScale() != 1.0f )
+ {
+ IPhysicsObject *pPhysObject = VPhysicsGetObject();
+ Vector vecPosition;
+ QAngle vecAngles;
+ pPhysObject->GetPosition( &vecPosition, &vecAngles );
+ const CPhysCollide *pScaledCollide = pPhysObject->GetCollide();
+ physcollision->TraceBox( ray, pScaledCollide, vecPosition, vecAngles, &tr );
+
+ return tr.DidHit();
+ }
+
+ if ( IsSolidFlagSet( FSOLID_CUSTOMRAYTEST ))
+ {
+ if (!TestHitboxes( ray, fContentsMask, tr ))
+ return true;
+
+ return tr.DidHit();
+ }
+
+ // We shouldn't get here.
+ Assert(0);
+ return false;
+}
+
+bool CBaseAnimating::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if (!pStudioHdr)
+ {
+ Assert(!"CBaseAnimating::GetBonePosition: model missing");
+ return false;
+ }
+
+ mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet );
+ if ( !set || !set->numhitboxes )
+ return false;
+
+ CBoneCache *pcache = GetBoneCache( );
+
+ matrix3x4_t *hitboxbones[MAXSTUDIOBONES];
+ pcache->ReadCachedBonePointers( hitboxbones, pStudioHdr->numbones() );
+
+ if ( TraceToStudio( physprops, ray, pStudioHdr, set, hitboxbones, fContentsMask, GetAbsOrigin(), GetModelScale(), tr ) )
+ {
+ mstudiobbox_t *pbox = set->pHitbox( tr.hitbox );
+ mstudiobone_t *pBone = pStudioHdr->pBone(pbox->bone);
+ tr.surface.name = "**studio**";
+ tr.surface.flags = SURF_HITBOX;
+ tr.surface.surfaceProps = physprops->GetSurfaceIndex( pBone->pszSurfaceProp() );
+ }
+ return true;
+}
+
+void CBaseAnimating::InitBoneControllers ( void ) // FIXME: rename
+{
+ int i;
+
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+ if (!pStudioHdr)
+ return;
+
+ int nBoneControllerCount = pStudioHdr->numbonecontrollers();
+ if ( nBoneControllerCount > NUM_BONECTRLS )
+ {
+ nBoneControllerCount = NUM_BONECTRLS;
+
+#ifdef _DEBUG
+ Warning( "Model %s has too many bone controllers! (Max %d allowed)\n", pStudioHdr->pszName(), NUM_BONECTRLS );
+#endif
+ }
+
+ for (i = 0; i < nBoneControllerCount; i++)
+ {
+ SetBoneController( i, 0.0 );
+ }
+
+ Assert( pStudioHdr->SequencesAvailable() );
+
+ if ( pStudioHdr->SequencesAvailable() )
+ {
+ for (i = 0; i < pStudioHdr->GetNumPoseParameters(); i++)
+ {
+ SetPoseParameter( i, 0.0 );
+ }
+ }
+}
+
+//=========================================================
+//=========================================================
+float CBaseAnimating::SetBoneController ( int iController, float flValue )
+{
+ Assert( GetModelPtr() );
+
+ CStudioHdr *pmodel = (CStudioHdr*)GetModelPtr();
+
+ Assert(iController >= 0 && iController < NUM_BONECTRLS);
+
+ float newValue;
+ float retVal = Studio_SetController( pmodel, iController, flValue, newValue );
+ m_flEncodedController.Set( iController, newValue );
+
+ return retVal;
+}
+
+//=========================================================
+//=========================================================
+float CBaseAnimating::GetBoneController ( int iController )
+{
+ Assert( GetModelPtr() );
+
+ CStudioHdr *pmodel = (CStudioHdr*)GetModelPtr();
+
+ return Studio_GetController( pmodel, iController, m_flEncodedController[iController] );
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Returns velcocity of the NPC from it's animation.
+// If physically simulated gets velocity from physics object
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseAnimating::GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity)
+{
+ if ( GetMoveType() == MOVETYPE_VPHYSICS )
+ {
+ BaseClass::GetVelocity(vVelocity,vAngVelocity);
+ }
+ else if ( !(GetFlags() & FL_ONGROUND) )
+ {
+ BaseClass::GetVelocity(vVelocity,vAngVelocity);
+ }
+ else
+ {
+ if (vVelocity != NULL)
+ {
+ Vector vRawVel;
+
+ GetSequenceLinearMotion( GetSequence(), &vRawVel );
+
+ // Build a rotation matrix from NPC orientation
+ matrix3x4_t fRotateMatrix;
+ AngleMatrix(GetLocalAngles(), fRotateMatrix);
+ VectorRotate( vRawVel, fRotateMatrix, *vVelocity);
+ }
+ if (vAngVelocity != NULL)
+ {
+ QAngle tmp = GetLocalAngularVelocity();
+ QAngleToAngularImpulse( tmp, *vAngVelocity );
+ }
+ }
+}
+
+
+//=========================================================
+//=========================================================
+
+void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], int boneMask )
+{
+ if(!pStudioHdr)
+ {
+ Assert(!"CBaseAnimating::GetSkeleton() without a model");
+ return;
+ }
+
+ IBoneSetup boneSetup( pStudioHdr, boneMask, GetPoseParameterArray() );
+ boneSetup.InitPose( pos, q );
+
+ boneSetup.AccumulatePose( pos, q, GetSequence(), GetCycle(), 1.0, gpGlobals->curtime, m_pIk );
+
+ if ( m_pIk )
+ {
+ CIKContext auto_ik;
+ auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, boneMask );
+ boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik );
+ }
+ else
+ {
+ boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, NULL );
+ }
+ boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() );
+}
+
+int CBaseAnimating::DrawDebugTextOverlays(void)
+{
+ int text_offset = BaseClass::DrawDebugTextOverlays();
+
+ if (m_debugOverlays & OVERLAY_TEXT_BIT)
+ {
+ // ----------------
+ // Print Look time
+ // ----------------
+ char tempstr[1024];
+ Q_snprintf(tempstr, sizeof(tempstr), "Sequence: (%3d) %s",GetSequence(), GetSequenceName( GetSequence() ) );
+ EntityText(text_offset,tempstr,0);
+ text_offset++;
+ const char *pActname = GetSequenceActivityName(GetSequence());
+ if ( pActname && strlen(pActname) )
+ {
+ Q_snprintf(tempstr, sizeof(tempstr), "Activity %s", pActname );
+ EntityText(text_offset,tempstr,0);
+ text_offset++;
+ }
+
+ Q_snprintf(tempstr, sizeof(tempstr), "Cycle: %.5f (%.5f)", (float)GetCycle(), m_flAnimTime.Get() );
+ EntityText(text_offset,tempstr,0);
+ text_offset++;
+ }
+
+ // Visualize attachment points
+ if ( m_debugOverlays & OVERLAY_ATTACHMENTS_BIT )
+ {
+ CStudioHdr *pStudioHdr = GetModelPtr();
+
+ if ( pStudioHdr )
+ {
+ Vector vecPos, vecForward, vecRight, vecUp;
+ char tempstr[256];
+
+ // Iterate all the stored attachments
+ for ( int i = 1; i <= pStudioHdr->GetNumAttachments(); i++ )
+ {
+ GetAttachment( i, vecPos, &vecForward, &vecRight, &vecUp );
+
+ // Red - forward, green - right, blue - up
+ NDebugOverlay::Line( vecPos, vecPos + ( vecForward * 4.0f ), 255, 0, 0, true, 0.05f );
+ NDebugOverlay::Line( vecPos, vecPos + ( vecRight * 4.0f ), 0, 255, 0, true, 0.05f );
+ NDebugOverlay::Line( vecPos, vecPos + ( vecUp * 4.0f ), 0, 0, 255, true, 0.05f );
+
+ Q_snprintf( tempstr, sizeof(tempstr), " < %s (%d)", pStudioHdr->pAttachment(i-1).pszName(), i );
+ NDebugOverlay::Text( vecPos, tempstr, true, 0.05f );
+ }
+ }
+ }
+
+ return text_offset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Force a clientside-animating entity to reset it's frame
+//-----------------------------------------------------------------------------
+void CBaseAnimating::ResetClientsideFrame( void )
+{
+ // TODO: Once we can chain MSG_ENTITY messages, use one of them
+ m_bClientSideFrameReset = !(bool)m_bClientSideFrameReset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the origin at which to play an inputted dispatcheffect
+//-----------------------------------------------------------------------------
+void CBaseAnimating::GetInputDispatchEffectPosition( const char *sInputString, Vector &pOrigin, QAngle &pAngles )
+{
+ // See if there's a specified attachment point
+ int iAttachment;
+ if ( GetModelPtr() && sscanf( sInputString, "%d", &iAttachment ) )
+ {
+ if ( !GetAttachment( iAttachment, pOrigin, pAngles ) )
+ {
+ Msg( "ERROR: Mapmaker tried to spawn DispatchEffect %s, but %s has no attachment %d\n",
+ sInputString, STRING(GetModelName()), iAttachment );
+ }
+ return;
+ }
+
+ BaseClass::GetInputDispatchEffectPosition( sInputString, pOrigin, pAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : setnum -
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetHitboxSet( int setnum )
+{
+#ifdef _DEBUG
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( !pStudioHdr )
+ return;
+
+ if (setnum > pStudioHdr->numhitboxsets())
+ {
+ // Warn if an bogus hitbox set is being used....
+ static bool s_bWarned = false;
+ if (!s_bWarned)
+ {
+ Warning("Using bogus hitbox set in entity %s!\n", GetClassname() );
+ s_bWarned = true;
+ }
+ setnum = 0;
+ }
+#endif
+
+ m_nHitboxSet = setnum;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *setname -
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetHitboxSetByName( const char *setname )
+{
+ Assert( GetModelPtr() );
+ m_nHitboxSet = FindHitboxSetByName( GetModelPtr(), setname );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CBaseAnimating::GetHitboxSet( void )
+{
+ return m_nHitboxSet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : char const
+//-----------------------------------------------------------------------------
+const char *CBaseAnimating::GetHitboxSetName( void )
+{
+ Assert( GetModelPtr() );
+ return ::GetHitboxSetName( GetModelPtr(), m_nHitboxSet );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CBaseAnimating::GetHitboxSetCount( void )
+{
+ Assert( GetModelPtr() );
+ return ::GetHitboxSetCount( GetModelPtr() );
+}
+
+static Vector hullcolor[8] =
+{
+ Vector( 1.0, 1.0, 1.0 ),
+ Vector( 1.0, 0.5, 0.5 ),
+ Vector( 0.5, 1.0, 0.5 ),
+ Vector( 1.0, 1.0, 0.5 ),
+ Vector( 0.5, 0.5, 1.0 ),
+ Vector( 1.0, 0.5, 1.0 ),
+ Vector( 0.5, 1.0, 1.0 ),
+ Vector( 1.0, 1.0, 1.0 )
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Send the current hitboxes for this model to the client ( to compare with
+// r_drawentities 3 client side boxes ).
+// WARNING: This uses a ton of bandwidth, only use on a listen server
+//-----------------------------------------------------------------------------
+void CBaseAnimating::DrawServerHitboxes( float duration /*= 0.0f*/, bool monocolor /*= false*/ )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( !pStudioHdr )
+ return;
+
+ mstudiohitboxset_t *set =pStudioHdr->pHitboxSet( m_nHitboxSet );
+ if ( !set )
+ return;
+
+ Vector position;
+ QAngle angles;
+
+ int r = 0;
+ int g = 0;
+ int b = 255;
+
+ for ( int i = 0; i < set->numhitboxes; i++ )
+ {
+ mstudiobbox_t *pbox = set->pHitbox( i );
+
+ GetBonePosition( pbox->bone, position, angles );
+
+ if ( !monocolor )
+ {
+ int j = (pbox->group % 8);
+
+ r = ( int ) ( 255.0f * hullcolor[j][0] );
+ g = ( int ) ( 255.0f * hullcolor[j][1] );
+ b = ( int ) ( 255.0f * hullcolor[j][2] );
+ }
+
+ NDebugOverlay::BoxAngles( position, pbox->bbmin * GetModelScale(), pbox->bbmax * GetModelScale(), angles, r, g, b, 0 ,duration );
+ }
+}
+
+
+void CBaseAnimating::DrawRawSkeleton( matrix3x4_t boneToWorld[], int boneMask, bool noDepthTest, float duration, bool monocolor )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( !pStudioHdr )
+ return;
+
+ int i;
+ int r = 255;
+ int g = 255;
+ int b = monocolor ? 255 : 0;
+
+
+ for (i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ if (pStudioHdr->pBone( i )->flags & boneMask)
+ {
+ Vector p1;
+ MatrixPosition( boneToWorld[i], p1 );
+ if ( pStudioHdr->pBone( i )->parent != -1 )
+ {
+ Vector p2;
+ MatrixPosition( boneToWorld[pStudioHdr->pBone( i )->parent], p2 );
+ NDebugOverlay::Line( p1, p2, r, g, b, noDepthTest, duration );
+ }
+ }
+ }
+}
+
+
+int CBaseAnimating::GetHitboxBone( int hitboxIndex )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( pStudioHdr )
+ {
+ mstudiohitboxset_t *set =pStudioHdr->pHitboxSet( m_nHitboxSet );
+ if ( set && hitboxIndex < set->numhitboxes )
+ {
+ return set->pHitbox( hitboxIndex )->bone;
+ }
+ }
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a box that surrounds all hitboxes
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::ComputeHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
+{
+ // Note that this currently should not be called during Relink because of IK.
+ // The code below recomputes bones so as to get at the hitboxes,
+ // which causes IK to trigger, which causes raycasts against the other entities to occur,
+ // which is illegal to do while in the Relink phase.
+
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if (!pStudioHdr)
+ return false;
+
+ mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet );
+ if ( !set || !set->numhitboxes )
+ return false;
+
+ CBoneCache *pCache = GetBoneCache();
+
+ // Compute a box in world space that surrounds this entity
+ pVecWorldMins->Init( FLT_MAX, FLT_MAX, FLT_MAX );
+ pVecWorldMaxs->Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+
+ Vector vecBoxAbsMins, vecBoxAbsMaxs;
+ for ( int i = 0; i < set->numhitboxes; i++ )
+ {
+ mstudiobbox_t *pbox = set->pHitbox(i);
+ matrix3x4_t *pMatrix = pCache->GetCachedBone(pbox->bone);
+
+ if ( pMatrix )
+ {
+ TransformAABB( *pMatrix, pbox->bbmin * GetModelScale(), pbox->bbmax * GetModelScale(), vecBoxAbsMins, vecBoxAbsMaxs );
+ VectorMin( *pVecWorldMins, vecBoxAbsMins, *pVecWorldMins );
+ VectorMax( *pVecWorldMaxs, vecBoxAbsMaxs, *pVecWorldMaxs );
+ }
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Computes a box that surrounds all hitboxes, in entity space
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::ComputeEntitySpaceHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
+{
+ // Note that this currently should not be called during position recomputation because of IK.
+ // The code below recomputes bones so as to get at the hitboxes,
+ // which causes IK to trigger, which causes raycasts against the other entities to occur,
+ // which is illegal to do while in the computeabsposition phase.
+
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if (!pStudioHdr)
+ return false;
+
+ mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet );
+ if ( !set || !set->numhitboxes )
+ return false;
+
+ CBoneCache *pCache = GetBoneCache();
+ matrix3x4_t *hitboxbones[MAXSTUDIOBONES];
+ pCache->ReadCachedBonePointers( hitboxbones, pStudioHdr->numbones() );
+
+ // Compute a box in world space that surrounds this entity
+ pVecWorldMins->Init( FLT_MAX, FLT_MAX, FLT_MAX );
+ pVecWorldMaxs->Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+
+ matrix3x4_t worldToEntity, boneToEntity;
+ MatrixInvert( EntityToWorldTransform(), worldToEntity );
+
+ Vector vecBoxAbsMins, vecBoxAbsMaxs;
+ for ( int i = 0; i < set->numhitboxes; i++ )
+ {
+ mstudiobbox_t *pbox = set->pHitbox(i);
+
+ ConcatTransforms( worldToEntity, *hitboxbones[pbox->bone], boneToEntity );
+ TransformAABB( boneToEntity, pbox->bbmin, pbox->bbmax, vecBoxAbsMins, vecBoxAbsMaxs );
+ VectorMin( *pVecWorldMins, vecBoxAbsMins, *pVecWorldMins );
+ VectorMax( *pVecWorldMaxs, vecBoxAbsMaxs, *pVecWorldMaxs );
+ }
+ return true;
+}
+
+
+int CBaseAnimating::GetPhysicsBone( int boneIndex )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( pStudioHdr )
+ {
+ if ( boneIndex >= 0 && boneIndex < pStudioHdr->numbones() )
+ return pStudioHdr->pBone( boneIndex )->physicsbone;
+ }
+ return 0;
+}
+
+bool CBaseAnimating::LookupHitbox( const char *szName, int& outSet, int& outBox )
+{
+ CStudioHdr* pHdr = GetModelPtr();
+
+ outSet = -1;
+ outBox = -1;
+
+ if( !pHdr )
+ return false;
+
+ for( int set=0; set < pHdr->numhitboxsets(); set++ )
+ {
+ for( int i = 0; i < pHdr->iHitboxCount(set); i++ )
+ {
+ mstudiobbox_t* pBox = pHdr->pHitbox( i, set );
+
+ if( !pBox )
+ continue;
+
+ const char* szBoxName = pBox->pszHitboxName();
+ if( Q_stricmp( szBoxName, szName ) == 0 )
+ {
+ outSet = set;
+ outBox = i;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void CBaseAnimating::CopyAnimationDataFrom( CBaseAnimating *pSource )
+{
+ this->SetModelName( pSource->GetModelName() );
+ this->SetModelIndex( pSource->GetModelIndex() );
+ this->SetCycle( pSource->GetCycle() );
+ this->SetEffects( pSource->GetEffects() );
+ this->IncrementInterpolationFrame();
+ this->SetSequence( pSource->GetSequence() );
+ this->m_flAnimTime = pSource->m_flAnimTime;
+ this->m_nBody = pSource->m_nBody;
+ this->m_nSkin = pSource->m_nSkin;
+ this->LockStudioHdr();
+}
+
+int CBaseAnimating::GetHitboxesFrontside( int *boxList, int boxMax, const Vector &normal, float dist )
+{
+ int count = 0;
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( pStudioHdr )
+ {
+ mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet );
+ if ( set )
+ {
+ matrix3x4_t matrix;
+ for ( int b = 0; b < set->numhitboxes; b++ )
+ {
+ mstudiobbox_t *pbox = set->pHitbox( b );
+
+ GetBoneTransform( pbox->bone, matrix );
+ Vector center = (pbox->bbmax + pbox->bbmin) * 0.5;
+ Vector centerWs;
+ VectorTransform( center, matrix, centerWs );
+ if ( DotProduct( centerWs, normal ) >= dist )
+ {
+ if ( count < boxMax )
+ {
+ boxList[count] = b;
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+void CBaseAnimating::EnableServerIK()
+{
+ if (!m_pIk)
+ {
+ m_pIk = new CIKContext;
+ m_iIKCounter = 0;
+ }
+}
+
+void CBaseAnimating::DisableServerIK()
+{
+ delete m_pIk;
+ m_pIk = NULL;
+}
+
+Activity CBaseAnimating::GetSequenceActivity( int iSequence )
+{
+ if( iSequence == -1 )
+ {
+ return ACT_INVALID;
+ }
+
+ if ( !GetModelPtr() )
+ return ACT_INVALID;
+
+ return (Activity)::GetSequenceActivity( GetModelPtr(), iSequence );
+}
+
+void CBaseAnimating::ModifyOrAppendCriteria( AI_CriteriaSet& set )
+{
+ BaseClass::ModifyOrAppendCriteria( set );
+
+ // TODO
+ // Append any animation state parameters here
+}
+
+
+void CBaseAnimating::DoMuzzleFlash()
+{
+ m_nMuzzleFlashParity = (m_nMuzzleFlashParity+1) & ((1 << EF_MUZZLEFLASH_BITS) - 1);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : scale -
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetModelScale( float scale, float change_duration /*= 0.0f*/ )
+{
+ if ( change_duration > 0.0f )
+ {
+ ModelScale *mvs = ( ModelScale * )CreateDataObject( MODELSCALE );
+ mvs->m_flModelScaleStart = m_flModelScale;
+ mvs->m_flModelScaleGoal = scale;
+ mvs->m_flModelScaleStartTime = gpGlobals->curtime;
+ mvs->m_flModelScaleFinishTime = mvs->m_flModelScaleStartTime + change_duration;
+ }
+ else
+ {
+ m_flModelScale = scale;
+ RefreshCollisionBounds();
+
+ if ( HasDataObjectType( MODELSCALE ) )
+ {
+ DestroyDataObject( MODELSCALE );
+ }
+ }
+}
+
+void CBaseAnimating::UpdateModelScale()
+{
+ ModelScale *mvs = ( ModelScale * )GetDataObject( MODELSCALE );
+ if ( !mvs )
+ {
+ return;
+ }
+
+ float dt = mvs->m_flModelScaleFinishTime - mvs->m_flModelScaleStartTime;
+ Assert( dt > 0.0f );
+
+ float frac = ( gpGlobals->curtime - mvs->m_flModelScaleStartTime ) / dt;
+ frac = clamp( frac, 0.0f, 1.0f );
+
+ if ( gpGlobals->curtime >= mvs->m_flModelScaleFinishTime )
+ {
+ m_flModelScale = mvs->m_flModelScaleGoal;
+ DestroyDataObject( MODELSCALE );
+ }
+ else
+ {
+ m_flModelScale = Lerp( frac, mvs->m_flModelScaleStart, mvs->m_flModelScaleGoal );
+ }
+
+ RefreshCollisionBounds();
+}
+
+void CBaseAnimating::RefreshCollisionBounds( void )
+{
+ CollisionProp()->RefreshScaledCollisionBounds();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CBaseAnimating::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner )
+{
+ if( IsOnFire() )
+ return;
+
+ bool bIsNPC = IsNPC();
+
+ // Right now this prevents stuff we don't want to catch on fire from catching on fire.
+ if( bNPCOnly && bIsNPC == false )
+ {
+ return;
+ }
+
+ if( bIsNPC == true && bCalledByLevelDesigner == false )
+ {
+ CAI_BaseNPC *pNPC = MyNPCPointer();
+
+ if ( pNPC && pNPC->AllowedToIgnite() == false )
+ return;
+ }
+
+ CEntityFlame *pFlame = CEntityFlame::Create( this );
+ if (pFlame)
+ {
+ pFlame->SetLifetime( flFlameLifetime );
+ AddFlag( FL_ONFIRE );
+
+ SetEffectEntity( pFlame );
+
+ if ( flSize > 0.0f )
+ {
+ pFlame->SetSize( flSize );
+ }
+ }
+
+ m_OnIgnite.FireOutput( this, this );
+}
+
+void CBaseAnimating::IgniteLifetime( float flFlameLifetime )
+{
+ if( !IsOnFire() )
+ Ignite( 30, false, 0.0f, true );
+
+ CEntityFlame *pFlame = dynamic_cast<CEntityFlame*>( GetEffectEntity() );
+
+ if ( !pFlame )
+ return;
+
+ pFlame->SetLifetime( flFlameLifetime );
+}
+
+void CBaseAnimating::IgniteNumHitboxFires( int iNumHitBoxFires )
+{
+ if( !IsOnFire() )
+ Ignite( 30, false, 0.0f, true );
+
+ CEntityFlame *pFlame = dynamic_cast<CEntityFlame*>( GetEffectEntity() );
+
+ if ( !pFlame )
+ return;
+
+ pFlame->SetNumHitboxFires( iNumHitBoxFires );
+}
+
+void CBaseAnimating::IgniteHitboxFireScale( float flHitboxFireScale )
+{
+ if( !IsOnFire() )
+ Ignite( 30, false, 0.0f, true );
+
+ CEntityFlame *pFlame = dynamic_cast<CEntityFlame*>( GetEffectEntity() );
+
+ if ( !pFlame )
+ return;
+
+ pFlame->SetHitboxFireScale( flHitboxFireScale );
+}
+
+//-----------------------------------------------------------------------------
+// Fades out!
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::Dissolve( const char *pMaterialName, float flStartTime, bool bNPCOnly, int nDissolveType, Vector vDissolverOrigin, int iMagnitude )
+{
+ // Right now this prevents stuff we don't want to catch on fire from catching on fire.
+ if( bNPCOnly && !(GetFlags() & FL_NPC) )
+ return false;
+
+ // Can't dissolve twice
+ if ( IsDissolving() )
+ return false;
+
+ bool bRagdollCreated = false;
+ CEntityDissolve *pDissolve = CEntityDissolve::Create( this, pMaterialName, flStartTime, nDissolveType, &bRagdollCreated );
+ if (pDissolve)
+ {
+ SetEffectEntity( pDissolve );
+
+ AddFlag( FL_DISSOLVING );
+ m_flDissolveStartTime = flStartTime;
+ pDissolve->SetDissolverOrigin( vDissolverOrigin );
+ pDissolve->SetMagnitude( iMagnitude );
+ }
+
+ // if this is a ragdoll dissolving, fire an event
+ if ( ( CLASS_NONE == Classify() ) && ( ClassMatches( "prop_ragdoll" ) ) )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "ragdoll_dissolved" );
+ if ( event )
+ {
+ event->SetInt( "entindex", entindex() );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ return bRagdollCreated;
+}
+
+
+//-----------------------------------------------------------------------------
+// Make a model look as though it's burning.
+//-----------------------------------------------------------------------------
+void CBaseAnimating::Scorch( int rate, int floor )
+{
+ color32 color = GetRenderColor();
+
+ if( color.r > floor )
+ color.r -= rate;
+
+ if( color.g > floor )
+ color.g -= rate;
+
+ if( color.b > floor )
+ color.b -= rate;
+
+ SetRenderColor( color.r, color.g, color.b );
+}
+
+
+void CBaseAnimating::ResetSequence(int nSequence)
+{
+ if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
+ {
+ DevMsg("ResetSequence : %s: %s -> %s\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(nSequence));
+ }
+
+ if ( !SequenceLoops() )
+ {
+ SetCycle( 0 );
+ }
+
+ // Tracker 17868: If the sequence number didn't actually change, but you call resetsequence info, it changes
+ // the newsequenceparity bit which causes the client to call m_flCycle.Reset() which causes a very slight
+ // discontinuity in looping animations as they reset around to cycle 0.0. This was causing the parentattached
+ // helmet on barney to hitch every time barney's idle cycled back around to its start.
+ bool changed = nSequence != GetSequence() ? true : false;
+
+ SetSequence( nSequence );
+ if ( changed || !SequenceLoops() )
+ {
+ ResetSequenceInfo();
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CBaseAnimating::InputIgnite( inputdata_t &inputdata )
+{
+ Ignite( 30, false, 0.0f, true );
+}
+
+void CBaseAnimating::InputIgniteLifetime( inputdata_t &inputdata )
+{
+ IgniteLifetime( inputdata.value.Float() );
+}
+
+void CBaseAnimating::InputIgniteNumHitboxFires( inputdata_t &inputdata )
+{
+ IgniteNumHitboxFires( inputdata.value.Int() );
+}
+
+void CBaseAnimating::InputIgniteHitboxFireScale( inputdata_t &inputdata )
+{
+ IgniteHitboxFireScale( inputdata.value.Float() );
+}
+
+void CBaseAnimating::InputBecomeRagdoll( inputdata_t &inputdata )
+{
+ BecomeRagdollOnClient( vec3_origin );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseAnimating::SetFadeDistance( float minFadeDist, float maxFadeDist )
+{
+ m_fadeMinDist = minFadeDist;
+ m_fadeMaxDist = maxFadeDist;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Async prefetches all anim data used by a particular sequence. Returns true if all of the required data is memory resident
+// Input : iSequence -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::PrefetchSequence( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( !pStudioHdr )
+ return true;
+
+ return Studio_PrefetchSequence( pStudioHdr, iSequence );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseAnimating::IsSequenceLooping( CStudioHdr *pStudioHdr, int iSequence )
+{
+ return (::GetSequenceFlags( pStudioHdr, iSequence ) & STUDIO_LOOPING) != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: model-change notification. Fires on dynamic load completion as well
+//-----------------------------------------------------------------------------
+CStudioHdr *CBaseAnimating::OnNewModel()
+{
+ (void) BaseClass::OnNewModel();
+
+ // TODO: if dynamic, validate m_Sequence and apply queued body group settings?
+ if ( IsDynamicModelLoading() )
+ {
+ // Called while dynamic model still loading -> new model, clear deferred state
+ m_bResetSequenceInfoOnLoad = false;
+ return NULL;
+ }
+
+ CStudioHdr *hdr = GetModelPtr();
+
+ if ( m_bResetSequenceInfoOnLoad )
+ {
+ m_bResetSequenceInfoOnLoad = false;
+ ResetSequenceInfo();
+ }
+
+ return hdr;
+}