//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //===========================================================================// #include "cbase.h" #include "animation.h" #include "studio.h" #include "bone_setup.h" #include "ai_basenpc.h" #include "npcevent.h" #include "saverestore_utlvector.h" #include "dt_utlvector_send.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern ConVar ai_sequence_debug; BEGIN_SIMPLE_DATADESC( CAnimationLayer ) // DEFINE_FIELD( m_pOwnerEntity, CBaseAnimatingOverlay ), DEFINE_FIELD( m_fFlags, FIELD_INTEGER ), DEFINE_FIELD( m_bSequenceFinished, FIELD_BOOLEAN ), DEFINE_FIELD( m_bLooping, FIELD_BOOLEAN ), DEFINE_FIELD( m_nSequence, FIELD_INTEGER ), DEFINE_FIELD( m_flCycle, FIELD_FLOAT ), DEFINE_FIELD( m_flPrevCycle, FIELD_FLOAT ), DEFINE_FIELD( m_flPlaybackRate, FIELD_FLOAT), DEFINE_FIELD( m_flWeight, FIELD_FLOAT), DEFINE_FIELD( m_flBlendIn, FIELD_FLOAT ), DEFINE_FIELD( m_flBlendOut, FIELD_FLOAT ), DEFINE_FIELD( m_flKillRate, FIELD_FLOAT ), DEFINE_FIELD( m_flKillDelay, FIELD_FLOAT ), DEFINE_CUSTOM_FIELD( m_nActivity, ActivityDataOps() ), DEFINE_FIELD( m_nPriority, FIELD_INTEGER ), DEFINE_FIELD( m_nOrder, FIELD_INTEGER ), DEFINE_FIELD( m_flLastEventCheck, FIELD_FLOAT ), DEFINE_FIELD( m_flLastAccess, FIELD_TIME ), DEFINE_FIELD( m_flLayerAnimtime, FIELD_FLOAT ), DEFINE_FIELD( m_flLayerFadeOuttime, FIELD_FLOAT ), END_DATADESC() BEGIN_DATADESC( CBaseAnimatingOverlay ) DEFINE_UTLVECTOR( m_AnimOverlay, FIELD_EMBEDDED ), // DEFINE_FIELD( m_nActiveLayers, FIELD_INTEGER ), // DEFINE_FIELD( m_nActiveBaseLayers, FIELD_INTEGER ), END_DATADESC() #define ORDER_BITS 4 #define WEIGHT_BITS 8 BEGIN_SEND_TABLE_NOBASE(CAnimationLayer, DT_Animationlayer) SendPropInt (SENDINFO(m_nSequence), ANIMATION_SEQUENCE_BITS,SPROP_UNSIGNED), SendPropFloat (SENDINFO(m_flCycle), ANIMATION_CYCLE_BITS, SPROP_ROUNDDOWN, 0.0f, 1.0f), SendPropFloat (SENDINFO(m_flPrevCycle), ANIMATION_CYCLE_BITS, SPROP_ROUNDDOWN, 0.0f, 1.0f), SendPropFloat (SENDINFO(m_flWeight), WEIGHT_BITS, 0, 0.0f, 1.0f), SendPropInt (SENDINFO(m_nOrder), ORDER_BITS, SPROP_UNSIGNED), END_SEND_TABLE() BEGIN_SEND_TABLE_NOBASE( CBaseAnimatingOverlay, DT_OverlayVars ) SendPropUtlVector( SENDINFO_UTLVECTOR( m_AnimOverlay ), CBaseAnimatingOverlay::MAX_OVERLAYS, // max elements SendPropDataTable( NULL, 0, &REFERENCE_SEND_TABLE( DT_Animationlayer ) ) ) END_SEND_TABLE() IMPLEMENT_SERVERCLASS_ST( CBaseAnimatingOverlay, DT_BaseAnimatingOverlay ) // These are in their own separate data table so CCSPlayer can exclude all of these. SendPropDataTable( "overlay_vars", 0, &REFERENCE_SEND_TABLE( DT_OverlayVars ) ) END_SEND_TABLE() CAnimationLayer::CAnimationLayer( ) { Init( NULL ); } void CAnimationLayer::Init( CBaseAnimatingOverlay *pOverlay ) { m_pOwnerEntity = pOverlay; m_fFlags = 0; m_flWeight = 0; m_flCycle = 0; m_flPrevCycle = 0; m_bSequenceFinished = false; m_nActivity = ACT_INVALID; m_nSequence = 0; m_nPriority = 0; m_nOrder.Set( CBaseAnimatingOverlay::MAX_OVERLAYS ); m_flBlendIn = 0.0; m_flBlendOut = 0.0; m_flKillRate = 100.0; m_flKillDelay = 0.0; m_flPlaybackRate = 1.0; m_flLastEventCheck = 0.0; m_flLastAccess = gpGlobals->curtime; m_flLayerAnimtime = 0; m_flLayerFadeOuttime = 0; } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CAnimationLayer::StudioFrameAdvance( float flInterval, CBaseAnimating *pOwner ) { float flCycleRate = pOwner->GetSequenceCycleRate( m_nSequence ); m_flPrevCycle = m_flCycle; m_flCycle += flInterval * flCycleRate * m_flPlaybackRate; if (m_flCycle < 0.0) { if (m_bLooping) { m_flCycle -= (int)(m_flCycle); } else { m_flCycle = 0; } } else if (m_flCycle >= 1.0) { m_bSequenceFinished = true; if (m_bLooping) { m_flCycle -= (int)(m_flCycle); } else { m_flCycle = 1.0; } } if (IsAutoramp()) { m_flWeight = 1; // blend in? if ( m_flBlendIn != 0.0f ) { if (m_flCycle < m_flBlendIn) { m_flWeight = m_flCycle / m_flBlendIn; } } // blend out? if ( m_flBlendOut != 0.0f ) { if (m_flCycle > 1.0 - m_flBlendOut) { m_flWeight = (1.0 - m_flCycle) / m_flBlendOut; } } m_flWeight = 3.0 * m_flWeight * m_flWeight - 2.0 * m_flWeight * m_flWeight * m_flWeight; if (m_nSequence == 0) m_flWeight = 0; } } //------------------------------------------------------------------------------ bool CAnimationLayer::IsAbandoned( void ) { if (IsActive() && !IsAutokill() && !IsKillMe() && m_flLastAccess > 0.0 && (gpGlobals->curtime - m_flLastAccess > 0.2)) return true; else return false; } void CAnimationLayer::MarkActive( void ) { m_flLastAccess = gpGlobals->curtime; } //------------------------------------------------------------------------------ void CBaseAnimatingOverlay::VerifyOrder( void ) { #ifdef _DEBUG int i, j; // test sorting of the layers int layer[MAX_OVERLAYS]; int maxOrder = -1; for (i = 0; i < MAX_OVERLAYS; i++) { layer[i] = MAX_OVERLAYS; } for (i = 0; i < m_AnimOverlay.Count(); i++) { if (m_AnimOverlay[ i ].m_nOrder < MAX_OVERLAYS) { j = m_AnimOverlay[ i ].m_nOrder; Assert( layer[j] == MAX_OVERLAYS ); layer[j] = i; if (j > maxOrder) maxOrder = j; } } // make sure they're sequential // Aim layers are allowed to have gaps, and we are moving aim blending to server // for ( i = 0; i <= maxOrder; i++ ) // { // Assert( layer[i] != MAX_OVERLAYS); // } /* for ( i = 0; i < MAX_OVERLAYS; i++ ) { int j = layer[i]; if (j != MAX_OVERLAYS) { char tempstr[512]; Q_snprintf( tempstr, sizeof( tempstr ),"%d : %d :%.2f :%d:%d:%.1f", j, m_AnimOverlay[ j ].m_nSequence, m_AnimOverlay[ j ].m_flWeight, m_AnimOverlay[ j ].IsActive(), m_AnimOverlay[ j ].IsKillMe(), m_AnimOverlay[ j ].m_flKillDelay ); EntityText( i, tempstr, 0.1 ); } } */ #endif } //------------------------------------------------------------------------------ // Purpose : advance the animation frame up to the current time // if an flInterval is passed in, only advance animation that number of seconds // Input : // Output : //------------------------------------------------------------------------------ void CBaseAnimatingOverlay::StudioFrameAdvance () { float flAdvance = GetAnimTimeInterval(); VerifyOrder(); BaseClass::StudioFrameAdvance(); for ( int i = 0; i < m_AnimOverlay.Count(); i++ ) { CAnimationLayer *pLayer = &m_AnimOverlay[i]; if (pLayer->IsActive()) { // Assert( !m_AnimOverlay[ i ].IsAbandoned() ); if (pLayer->IsKillMe()) { if (pLayer->m_flKillDelay > 0) { pLayer->m_flKillDelay -= flAdvance; pLayer->m_flKillDelay = clamp( pLayer->m_flKillDelay, 0.0f, 1.0f ); } else if (pLayer->m_flWeight != 0.0f) { // give it at least one frame advance cycle to propagate 0.0 to client pLayer->m_flWeight -= pLayer->m_flKillRate * flAdvance; pLayer->m_flWeight = clamp( (float) pLayer->m_flWeight, 0.0f, 1.0f ); } else { // shift the other layers down in order if (ai_sequence_debug.GetBool() == true && m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { Msg("removing %d (%d): %s : %5.3f (%.3f)\n", i, pLayer->m_nOrder.Get(), GetSequenceName( pLayer->m_nSequence ), pLayer->m_flCycle.Get(), pLayer->m_flWeight.Get() ); } FastRemoveLayer( i ); // needs at least one thing cycle dead to trigger sequence change pLayer->Dying(); continue; } } pLayer->StudioFrameAdvance( flAdvance, this ); if ( pLayer->m_bSequenceFinished && (pLayer->IsAutokill()) ) { pLayer->m_flWeight = 0.0f; pLayer->KillMe(); } } else if (pLayer->IsDying()) { pLayer->Dead(); } else if (pLayer->m_flWeight > 0.0) { // Now that the server blends, it is turning off layers all the time. Having a weight left over // when you're no longer marked as active is now harmless and commonplace. Just clean up. pLayer->Init( this ); pLayer->Dying(); } } if (ai_sequence_debug.GetBool() == true && m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { for ( int i = 0; i < m_AnimOverlay.Count(); i++ ) { if (m_AnimOverlay[ i ].IsActive()) { /* if (m_AnimOverlay[ i ].IsAbandoned()) { Msg(" %d abandoned %.2f (%.2f)\n", i, gpGlobals->curtime, m_AnimOverlay[ i ].m_flLastAccess ); } */ Msg(" %d (%d): %s : %5.3f (%.3f)\n", i, m_AnimOverlay[ i ].m_nOrder.Get(), GetSequenceName( m_AnimOverlay[ i ].m_nSequence ), m_AnimOverlay[ i ].m_flCycle.Get(), m_AnimOverlay[ i ].m_flWeight.Get() ); } } } VerifyOrder(); } //========================================================= // DispatchAnimEvents //========================================================= void CBaseAnimatingOverlay::DispatchAnimEvents ( CBaseAnimating *eventHandler ) { BaseClass::DispatchAnimEvents( eventHandler ); for ( int i = 0; i < m_AnimOverlay.Count(); i++ ) { if (m_AnimOverlay[ i ].IsActive()) { m_AnimOverlay[ i ].DispatchAnimEvents( eventHandler, this ); } } } void CAnimationLayer::DispatchAnimEvents( CBaseAnimating *eventHandler, CBaseAnimating *pOwner ) { animevent_t event; CStudioHdr *pstudiohdr = pOwner->GetModelPtr( ); if ( !pstudiohdr ) { Assert(!"CBaseAnimating::DispatchAnimEvents: model missing"); return; } if ( !pstudiohdr->SequencesAvailable() ) { return; } if ( m_nSequence >= pstudiohdr->GetNumSeq() ) return; // don't fire if here are no events if ( pstudiohdr->pSeqdesc( m_nSequence ).numevents == 0 ) { return; } // look from when it last checked to some short time in the future float flCycleRate = pOwner->GetSequenceCycleRate( m_nSequence ) * m_flPlaybackRate; float flStart = m_flLastEventCheck; float flEnd = m_flCycle; if (!m_bLooping) { // fire off events early float flLastVisibleCycle = 1.0f - (pstudiohdr->pSeqdesc( m_nSequence ).fadeouttime) * flCycleRate; if (flEnd >= flLastVisibleCycle || flEnd < 0.0) { m_bSequenceFinished = true; flEnd = 1.0f; } } m_flLastEventCheck = flEnd; /* if (pOwner->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { Msg( "%s:%s : checking %.2f %.2f (%d)\n", STRING(pOwner->GetModelName()), pstudiohdr->pSeqdesc( m_nSequence ).pszLabel(), flStart, flEnd, m_bSequenceFinished ); } */ // FIXME: does not handle negative framerates! int index = 0; while ( (index = GetAnimationEvent( pstudiohdr, m_nSequence, &event, flStart, flEnd, index ) ) != 0 ) { event.pSource = pOwner; // calc when this event should happen if (flCycleRate > 0.0) { float flCycle = event.cycle; if (flCycle > m_flCycle) { flCycle = flCycle - 1.0; } event.eventtime = pOwner->m_flAnimTime + (flCycle - m_flCycle) / flCycleRate + pOwner->GetAnimTimeInterval(); } // Msg( "dispatch %d (%d : %.2f)\n", index - 1, event.event, event.eventtime ); eventHandler->HandleAnimEvent( &event ); } } void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], int boneMask ) { if(!pStudioHdr) { Assert(!"CBaseAnimating::GetSkeleton() without a model"); return; } if (!pStudioHdr->SequencesAvailable()) { return; } IBoneSetup boneSetup( pStudioHdr, boneMask, GetPoseParameterArray() ); boneSetup.InitPose( pos, q ); boneSetup.AccumulatePose( pos, q, GetSequence(), GetCycle(), 1.0, gpGlobals->curtime, m_pIk ); // sort the layers int layer[MAX_OVERLAYS] = {}; int i; for (i = 0; i < m_AnimOverlay.Count(); i++) { layer[i] = MAX_OVERLAYS; } for (i = 0; i < m_AnimOverlay.Count(); i++) { CAnimationLayer &pLayer = m_AnimOverlay[i]; if( (pLayer.m_flWeight > 0) && pLayer.IsActive() && pLayer.m_nOrder >= 0 && pLayer.m_nOrder < m_AnimOverlay.Count()) { layer[pLayer.m_nOrder] = i; } } for (i = 0; i < m_AnimOverlay.Count(); i++) { if (layer[i] >= 0 && layer[i] < m_AnimOverlay.Count()) { CAnimationLayer &pLayer = m_AnimOverlay[layer[i]]; // UNDONE: Is it correct to use overlay weight for IK too? boneSetup.AccumulatePose( pos, q, pLayer.m_nSequence, pLayer.m_flCycle, pLayer.m_flWeight, 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() ); } //----------------------------------------------------------------------------- // Purpose: zero's out all non-restore safe fields // Output : //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::OnRestore( ) { int i; // force order of unused layers to current MAX_OVERLAYS // and Tracker 48843 (Alyx skating after restore) restore the owner entity ptr (otherwise the network layer won't get NetworkStateChanged signals until the layer is re-Init()'ed for (i = 0; i < m_AnimOverlay.Count(); i++) { m_AnimOverlay[i].m_pOwnerEntity = this; if ( !m_AnimOverlay[i].IsActive()) { m_AnimOverlay[i].m_nOrder.Set( MAX_OVERLAYS ); } } // get rid of all layers that shouldn't be restored for (i = 0; i < m_AnimOverlay.Count(); i++) { if ( ( m_AnimOverlay[i].IsActive() && (m_AnimOverlay[i].m_fFlags & ANIM_LAYER_DONTRESTORE) ) || ( GetModelPtr() && !IsValidSequence(m_AnimOverlay[i].m_nSequence) ) ) { FastRemoveLayer( i ); } } BaseClass::OnRestore(); } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- int CBaseAnimatingOverlay::AddGestureSequence( int sequence, bool autokill /*= true*/ ) { int i = AddLayeredSequence( sequence, 0 ); // No room? if ( IsValidLayer( i ) ) { SetLayerAutokill( i, autokill ); } return i; } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- int CBaseAnimatingOverlay::AddGestureSequence( int nSequence, float flDuration, bool autokill /*= true*/ ) { int iLayer = AddGestureSequence( nSequence, autokill ); Assert( iLayer != -1 ); if (iLayer >= 0 && flDuration > 0) { m_AnimOverlay[iLayer].m_flPlaybackRate = SequenceDuration( nSequence ) / flDuration; } return iLayer; } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ int CBaseAnimatingOverlay::AddGesture( Activity activity, bool autokill /*= true*/ ) { if ( IsPlayingGesture( activity ) ) { return FindGestureLayer( activity ); } int seq = SelectWeightedSequence( activity ); if ( seq <= 0 ) { const char *actname = CAI_BaseNPC::GetActivityName( activity ); DevMsg( "CBaseAnimatingOverlay::AddGesture: model %s missing activity %s\n", STRING(GetModelName()), actname ); return -1; } int i = AddGestureSequence( seq, autokill ); Assert( i != -1 ); if ( i != -1 ) { m_AnimOverlay[ i ].m_nActivity = activity; } return i; } int CBaseAnimatingOverlay::AddGesture( Activity activity, float flDuration, bool autokill /*= true*/ ) { int iLayer = AddGesture( activity, autokill ); SetLayerDuration( iLayer, flDuration ); return iLayer; } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerDuration( int iLayer, float flDuration ) { if (IsValidLayer( iLayer ) && flDuration > 0) { m_AnimOverlay[iLayer].m_flPlaybackRate = SequenceDuration( m_AnimOverlay[iLayer].m_nSequence ) / flDuration; } } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- float CBaseAnimatingOverlay::GetLayerDuration( int iLayer ) { if (IsValidLayer( iLayer )) { if (m_AnimOverlay[iLayer].m_flPlaybackRate != 0.0f) { return (1.0 - m_AnimOverlay[iLayer].m_flCycle) * SequenceDuration( m_AnimOverlay[iLayer].m_nSequence ) / m_AnimOverlay[iLayer].m_flPlaybackRate; } return SequenceDuration( m_AnimOverlay[iLayer].m_nSequence ); } return 0.0; } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- int CBaseAnimatingOverlay::AddLayeredSequence( int sequence, int iPriority ) { int i = AllocateLayer( iPriority ); // No room? if ( IsValidLayer( i ) ) { m_AnimOverlay[i].m_flCycle = 0; m_AnimOverlay[i].m_flPrevCycle = 0; m_AnimOverlay[i].m_flPlaybackRate = 1.0; m_AnimOverlay[i].m_nActivity = ACT_INVALID; m_AnimOverlay[i].m_nSequence = sequence; m_AnimOverlay[i].m_flWeight = 1.0f; m_AnimOverlay[i].m_flBlendIn = 0.0f; m_AnimOverlay[i].m_flBlendOut = 0.0f; m_AnimOverlay[i].m_bSequenceFinished = false; m_AnimOverlay[i].m_flLastEventCheck = 0; m_AnimOverlay[i].m_bLooping = ((GetSequenceFlags( GetModelPtr(), sequence ) & STUDIO_LOOPING) != 0); if (ai_sequence_debug.GetBool() == true && m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { Msg("%5.3f : adding %d (%d): %s : %5.3f (%.3f)\n", gpGlobals->curtime, i, m_AnimOverlay[ i ].m_nOrder.Get(), GetSequenceName( m_AnimOverlay[ i ].m_nSequence ), m_AnimOverlay[ i ].m_flCycle.Get(), m_AnimOverlay[ i ].m_flWeight.Get() ); } } return i; } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- bool CBaseAnimatingOverlay::IsValidLayer( int iLayer ) { return (iLayer >= 0 && iLayer < m_AnimOverlay.Count() && m_AnimOverlay[iLayer].IsActive()); } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- int CBaseAnimatingOverlay::AllocateLayer( int iPriority ) { int i; // look for an open slot and for existing layers that are lower priority int iNewOrder = 0; int iOpenLayer = -1; int iNumOpen = 0; for (i = 0; i < m_AnimOverlay.Count(); i++) { if ( m_AnimOverlay[i].IsActive() ) { if (m_AnimOverlay[i].m_nPriority <= iPriority) { iNewOrder = MAX( iNewOrder, m_AnimOverlay[i].m_nOrder + 1 ); } } else if (m_AnimOverlay[ i ].IsDying()) { // skip } else if (iOpenLayer == -1) { iOpenLayer = i; } else { iNumOpen++; } } if (iOpenLayer == -1) { if (m_AnimOverlay.Count() >= MAX_OVERLAYS) { return -1; } iOpenLayer = m_AnimOverlay.AddToTail(); m_AnimOverlay[iOpenLayer].Init( this ); } // make sure there's always an empty unused layer so that history slots will be available on the client when it is used if (iNumOpen == 0) { if (m_AnimOverlay.Count() < MAX_OVERLAYS) { i = m_AnimOverlay.AddToTail(); m_AnimOverlay[i].Init( this ); } } for (i = 0; i < m_AnimOverlay.Count(); i++) { if ( m_AnimOverlay[i].m_nOrder >= iNewOrder && m_AnimOverlay[i].m_nOrder < MAX_OVERLAYS) { m_AnimOverlay[i].m_nOrder++; } } m_AnimOverlay[iOpenLayer].m_fFlags = ANIM_LAYER_ACTIVE; m_AnimOverlay[iOpenLayer].m_nOrder = iNewOrder; m_AnimOverlay[iOpenLayer].m_nPriority = iPriority; m_AnimOverlay[iOpenLayer].MarkActive(); VerifyOrder(); return iOpenLayer; } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerPriority( int iLayer, int iPriority ) { if (!IsValidLayer( iLayer )) { return; } if (m_AnimOverlay[iLayer].m_nPriority == iPriority) { return; } // look for an open slot and for existing layers that are lower priority int i; for (i = 0; i < m_AnimOverlay.Count(); i++) { if ( m_AnimOverlay[i].IsActive() ) { if (m_AnimOverlay[i].m_nOrder > m_AnimOverlay[iLayer].m_nOrder) { m_AnimOverlay[i].m_nOrder--; } } } int iNewOrder = 0; for (i = 0; i < m_AnimOverlay.Count(); i++) { if ( i != iLayer && m_AnimOverlay[i].IsActive() ) { if (m_AnimOverlay[i].m_nPriority <= iPriority) { iNewOrder = MAX( iNewOrder, m_AnimOverlay[i].m_nOrder + 1 ); } } } for (i = 0; i < m_AnimOverlay.Count(); i++) { if ( i != iLayer && m_AnimOverlay[i].IsActive() ) { if ( m_AnimOverlay[i].m_nOrder >= iNewOrder) { m_AnimOverlay[i].m_nOrder++; } } } m_AnimOverlay[iLayer].m_nOrder = iNewOrder; m_AnimOverlay[iLayer].m_nPriority = iPriority; m_AnimOverlay[iLayer].MarkActive( ); VerifyOrder(); return; } //----------------------------------------------------------------------------- // Purpose: // Input : activity - //----------------------------------------------------------------------------- int CBaseAnimatingOverlay::FindGestureLayer( Activity activity ) { for (int i = 0; i < m_AnimOverlay.Count(); i++) { if ( !(m_AnimOverlay[i].IsActive()) ) continue; if ( m_AnimOverlay[i].IsKillMe() ) continue; if ( m_AnimOverlay[i].m_nActivity == ACT_INVALID ) continue; if ( m_AnimOverlay[i].m_nActivity == activity ) return i; } return -1; } //----------------------------------------------------------------------------- // Purpose: // Input : activity - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CBaseAnimatingOverlay::IsPlayingGesture( Activity activity ) { return FindGestureLayer( activity ) != -1 ? true : false; } //----------------------------------------------------------------------------- // Purpose: // Input : activity - //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::RestartGesture( Activity activity, bool addifmissing /*=true*/, bool autokill /*=true*/ ) { int idx = FindGestureLayer( activity ); if ( idx == -1 ) { if ( addifmissing ) { AddGesture( activity, autokill ); } return; } m_AnimOverlay[ idx ].m_flCycle = 0.0f; m_AnimOverlay[ idx ].m_flPrevCycle = 0.0f; m_AnimOverlay[ idx ].m_flLastEventCheck = 0.0f; } //----------------------------------------------------------------------------- // Purpose: // Input : activity - //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::RemoveGesture( Activity activity ) { int iLayer = FindGestureLayer( activity ); if ( iLayer == -1 ) return; RemoveLayer( iLayer ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::RemoveAllGestures( void ) { for (int i = 0; i < m_AnimOverlay.Count(); i++) { RemoveLayer( i ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerCycle( int iLayer, float flCycle ) { if (!IsValidLayer( iLayer )) return; if (!m_AnimOverlay[iLayer].m_bLooping) { flCycle = clamp( flCycle, 0.0f, 1.0f ); } m_AnimOverlay[iLayer].m_flCycle = flCycle; m_AnimOverlay[iLayer].MarkActive( ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerCycle( int iLayer, float flCycle, float flPrevCycle ) { if (!IsValidLayer( iLayer )) return; if (!m_AnimOverlay[iLayer].m_bLooping) { flCycle = clamp( flCycle, 0.0f, 1.0f ); flPrevCycle = clamp( flPrevCycle, 0.0f, 1.0f ); } m_AnimOverlay[iLayer].m_flCycle = flCycle; m_AnimOverlay[iLayer].m_flPrevCycle = flPrevCycle; m_AnimOverlay[iLayer].m_flLastEventCheck = flPrevCycle; m_AnimOverlay[iLayer].MarkActive( ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CBaseAnimatingOverlay::GetLayerCycle( int iLayer ) { if (!IsValidLayer( iLayer )) return 0.0; return m_AnimOverlay[iLayer].m_flCycle; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerPlaybackRate( int iLayer, float flPlaybackRate ) { if (!IsValidLayer( iLayer )) return; Assert( flPlaybackRate > -1.0 && flPlaybackRate < 40.0); m_AnimOverlay[iLayer].m_flPlaybackRate = flPlaybackRate; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerWeight( int iLayer, float flWeight ) { if (!IsValidLayer( iLayer )) return; flWeight = clamp( flWeight, 0.0f, 1.0f ); m_AnimOverlay[iLayer].m_flWeight = flWeight; m_AnimOverlay[iLayer].MarkActive( ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CBaseAnimatingOverlay::GetLayerWeight( int iLayer ) { if (!IsValidLayer( iLayer )) return 0.0; return m_AnimOverlay[iLayer].m_flWeight; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerBlendIn( int iLayer, float flBlendIn ) { if (!IsValidLayer( iLayer )) return; m_AnimOverlay[iLayer].m_flBlendIn = flBlendIn; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerBlendOut( int iLayer, float flBlendOut ) { if (!IsValidLayer( iLayer )) return; m_AnimOverlay[iLayer].m_flBlendOut = flBlendOut; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerAutokill( int iLayer, bool bAutokill ) { if (!IsValidLayer( iLayer )) return; if (bAutokill) { m_AnimOverlay[iLayer].m_fFlags |= ANIM_LAYER_AUTOKILL; } else { m_AnimOverlay[iLayer].m_fFlags &= ~ANIM_LAYER_AUTOKILL; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerLooping( int iLayer, bool bLooping ) { if (!IsValidLayer( iLayer )) return; m_AnimOverlay[iLayer].m_bLooping = bLooping; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerNoRestore( int iLayer, bool bNoRestore ) { if (!IsValidLayer( iLayer )) return; if (bNoRestore) { m_AnimOverlay[iLayer].m_fFlags |= ANIM_LAYER_DONTRESTORE; } else { m_AnimOverlay[iLayer].m_fFlags &= ~ANIM_LAYER_DONTRESTORE; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Activity CBaseAnimatingOverlay::GetLayerActivity( int iLayer ) { if (!IsValidLayer( iLayer )) { return ACT_INVALID; } return m_AnimOverlay[iLayer].m_nActivity; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CBaseAnimatingOverlay::GetLayerSequence( int iLayer ) { if (!IsValidLayer( iLayer )) { return ACT_INVALID; } return m_AnimOverlay[iLayer].m_nSequence; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::RemoveLayer( int iLayer, float flKillRate, float flKillDelay ) { if (!IsValidLayer( iLayer )) return; if (flKillRate > 0) { m_AnimOverlay[iLayer].m_flKillRate = m_AnimOverlay[iLayer].m_flWeight / flKillRate; } else { m_AnimOverlay[iLayer].m_flKillRate = 100; } m_AnimOverlay[iLayer].m_flKillDelay = flKillDelay; m_AnimOverlay[iLayer].KillMe(); } void CBaseAnimatingOverlay::FastRemoveLayer( int iLayer ) { if (!IsValidLayer( iLayer )) return; // shift the other layers down in order for (int j = 0; j < m_AnimOverlay.Count(); j++ ) { if ((m_AnimOverlay[ j ].IsActive()) && m_AnimOverlay[ j ].m_nOrder > m_AnimOverlay[ iLayer ].m_nOrder) { m_AnimOverlay[ j ].m_nOrder--; } } m_AnimOverlay[ iLayer ].Init( this ); VerifyOrder(); } CAnimationLayer *CBaseAnimatingOverlay::GetAnimOverlay( int iIndex ) { iIndex = clamp( iIndex, 0, m_AnimOverlay.Count()-1 ); return &m_AnimOverlay[iIndex]; } void CBaseAnimatingOverlay::SetNumAnimOverlays( int num ) { if ( m_AnimOverlay.Count() < num ) { m_AnimOverlay.AddMultipleToTail( num - m_AnimOverlay.Count() ); } else if ( m_AnimOverlay.Count() > num ) { m_AnimOverlay.RemoveMultiple( num, m_AnimOverlay.Count() - num ); } } bool CBaseAnimatingOverlay::HasActiveLayer( void ) { for (int j = 0; j < m_AnimOverlay.Count(); j++ ) { if ( m_AnimOverlay[ j ].IsActive() ) return true; } return false; } //-----------------------------------------------------------------------------