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/BaseAnimatingOverlay.cpp | 2284 +++++++++++++-------------- 1 file changed, 1142 insertions(+), 1142 deletions(-) (limited to 'mp/src/game/server/BaseAnimatingOverlay.cpp') diff --git a/mp/src/game/server/BaseAnimatingOverlay.cpp b/mp/src/game/server/BaseAnimatingOverlay.cpp index a1bb1a93..50c94361 100644 --- a/mp/src/game/server/BaseAnimatingOverlay.cpp +++ b/mp/src/game/server/BaseAnimatingOverlay.cpp @@ -1,1142 +1,1142 @@ -//========= 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_flKillRate = 100.0; - m_flKillDelay = 0.0; - m_flPlaybackRate = 1.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; -} - -//----------------------------------------------------------------------------- +//========= 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_flKillRate = 100.0; + m_flKillDelay = 0.0; + m_flPlaybackRate = 1.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; +} + +//----------------------------------------------------------------------------- -- cgit v1.2.3