From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/baseanimating.cpp | 7166 +++++++++++++++++----------------- 1 file changed, 3583 insertions(+), 3583 deletions(-) (limited to 'mp/src/game/server/baseanimating.cpp') diff --git a/mp/src/game/server/baseanimating.cpp b/mp/src/game/server/baseanimating.cpp index f1f2803d..2e1e3a22 100644 --- a/mp/src/game/server/baseanimating.cpp +++ b/mp/src/game/server/baseanimating.cpp @@ -1,3583 +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(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( 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 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( GetEffectEntity() ); - - if ( !pFlame ) - return; - - pFlame->SetLifetime( flFlameLifetime ); -} - -void CBaseAnimating::IgniteNumHitboxFires( int iNumHitBoxFires ) -{ - if( !IsOnFire() ) - Ignite( 30, false, 0.0f, true ); - - CEntityFlame *pFlame = dynamic_cast( GetEffectEntity() ); - - if ( !pFlame ) - return; - - pFlame->SetNumHitboxFires( iNumHitBoxFires ); -} - -void CBaseAnimating::IgniteHitboxFireScale( float flHitboxFireScale ) -{ - if( !IsOnFire() ) - Ignite( 30, false, 0.0f, true ); - - CEntityFlame *pFlame = dynamic_cast( 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; -} +//========= 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(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( 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 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( GetEffectEntity() ); + + if ( !pFlame ) + return; + + pFlame->SetLifetime( flFlameLifetime ); +} + +void CBaseAnimating::IgniteNumHitboxFires( int iNumHitBoxFires ) +{ + if( !IsOnFire() ) + Ignite( 30, false, 0.0f, true ); + + CEntityFlame *pFlame = dynamic_cast( GetEffectEntity() ); + + if ( !pFlame ) + return; + + pFlame->SetNumHitboxFires( iNumHitBoxFires ); +} + +void CBaseAnimating::IgniteHitboxFireScale( float flHitboxFireScale ) +{ + if( !IsOnFire() ) + Ignite( 30, false, 0.0f, true ); + + CEntityFlame *pFlame = dynamic_cast( 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; +} -- cgit v1.2.3