diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/shared/animation.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/shared/animation.cpp')
| -rw-r--r-- | mp/src/game/shared/animation.cpp | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/mp/src/game/shared/animation.cpp b/mp/src/game/shared/animation.cpp new file mode 100644 index 00000000..9d5183c8 --- /dev/null +++ b/mp/src/game/shared/animation.cpp @@ -0,0 +1,971 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "studio.h"
+#include "activitylist.h"
+#include "engine/IEngineSound.h"
+#include "ai_activity.h"
+#include "animation.h"
+#include "bone_setup.h"
+#include "scriptevent.h"
+#include "npcevent.h"
+#include "eventlist.h"
+#include "tier0/vprof.h"
+
+#if !defined( CLIENT_DLL ) && !defined( MAKEXVCD )
+#include "util.h"
+#include "enginecallback.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#pragma warning( disable : 4244 )
+#define iabs(i) (( (i) >= 0 ) ? (i) : -(i) )
+
+int ExtractBbox( CStudioHdr *pstudiohdr, int sequence, Vector& mins, Vector& maxs )
+{
+ if (! pstudiohdr)
+ return 0;
+
+ if (!pstudiohdr->SequencesAvailable())
+ return 0;
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
+
+ mins = seqdesc.bbmin;
+
+ maxs = seqdesc.bbmax;
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : *pstudiohdr -
+// iSequence -
+//
+// Output : mstudioseqdesc_t
+//-----------------------------------------------------------------------------
+
+extern int g_nActivityListVersion;
+extern int g_nEventListVersion;
+
+void SetEventIndexForSequence( mstudioseqdesc_t &seqdesc )
+{
+ if ( &seqdesc == NULL )
+ return;
+
+ seqdesc.flags |= STUDIO_EVENT;
+
+ if ( seqdesc.numevents == 0 )
+ return;
+
+ for ( int index = 0; index < (int)seqdesc.numevents; index++ )
+ {
+ mstudioevent_t *pevent = seqdesc.pEvent( index );
+
+ if ( !pevent )
+ continue;
+
+ if ( pevent->type & AE_TYPE_NEWEVENTSYSTEM )
+ {
+ const char *pEventName = pevent->pszEventName();
+
+ int iEventIndex = EventList_IndexForName( pEventName );
+
+ if ( iEventIndex == -1 )
+ {
+ pevent->event = EventList_RegisterPrivateEvent( pEventName );
+ }
+ else
+ {
+ pevent->event = iEventIndex;
+ pevent->type |= EventList_GetEventType( iEventIndex );
+ }
+ }
+ }
+}
+
+mstudioevent_t *GetEventIndexForSequence( mstudioseqdesc_t &seqdesc )
+{
+ if (!(seqdesc.flags & STUDIO_EVENT))
+ {
+ SetEventIndexForSequence( seqdesc );
+ }
+
+ return seqdesc.pEvent( 0 );
+}
+
+
+void BuildAllAnimationEventIndexes( CStudioHdr *pstudiohdr )
+{
+ if ( !pstudiohdr )
+ return;
+
+ if( pstudiohdr->GetEventListVersion() != g_nEventListVersion )
+ {
+ for ( int i = 0 ; i < pstudiohdr->GetNumSeq() ; i++ )
+ {
+ SetEventIndexForSequence( pstudiohdr->pSeqdesc( i ) );
+ }
+
+ pstudiohdr->SetEventListVersion( g_nEventListVersion );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ensures that activity / index relationship is recalculated
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void ResetEventIndexes( CStudioHdr *pstudiohdr )
+{
+ if (! pstudiohdr)
+ return;
+
+ pstudiohdr->SetEventListVersion( g_nEventListVersion - 1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+
+void SetActivityForSequence( CStudioHdr *pstudiohdr, int i )
+{
+ int iActivityIndex;
+ const char *pszActivityName;
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+
+ seqdesc.flags |= STUDIO_ACTIVITY;
+
+ pszActivityName = GetSequenceActivityName( pstudiohdr, i );
+ if ( pszActivityName[0] != '\0' )
+ {
+ iActivityIndex = ActivityList_IndexForName( pszActivityName );
+
+ if ( iActivityIndex == -1 )
+ {
+ // Allow this now. Animators can create custom activities that are referenced only on the client or by scripts, etc.
+ //Warning( "***\nModel %s tried to reference unregistered activity: %s \n***\n", pstudiohdr->name, pszActivityName );
+ //Assert(0);
+ // HACK: the client and server don't share the private activity list so registering it on the client would hose the server
+#ifdef CLIENT_DLL
+ seqdesc.flags &= ~STUDIO_ACTIVITY;
+#else
+ seqdesc.activity = ActivityList_RegisterPrivateActivity( pszActivityName );
+#endif
+ }
+ else
+ {
+ seqdesc.activity = iActivityIndex;
+ }
+ }
+}
+
+//=========================================================
+// IndexModelSequences - set activity and event indexes for all model
+// sequences that have them.
+//=========================================================
+
+void IndexModelSequences( CStudioHdr *pstudiohdr )
+{
+ int i;
+
+ if (! pstudiohdr)
+ return;
+
+ if (!pstudiohdr->SequencesAvailable())
+ return;
+
+ for ( i = 0 ; i < pstudiohdr->GetNumSeq() ; i++ )
+ {
+ SetActivityForSequence( pstudiohdr, i );
+ SetEventIndexForSequence( pstudiohdr->pSeqdesc( i ) );
+ }
+
+ pstudiohdr->SetActivityListVersion( g_nActivityListVersion );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ensures that activity / index relationship is recalculated
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void ResetActivityIndexes( CStudioHdr *pstudiohdr )
+{
+ if (! pstudiohdr)
+ return;
+
+ pstudiohdr->SetActivityListVersion( g_nActivityListVersion - 1 );
+}
+
+void VerifySequenceIndex( CStudioHdr *pstudiohdr )
+{
+ if ( !pstudiohdr )
+ {
+ return;
+ }
+
+ if( pstudiohdr->GetActivityListVersion( ) != g_nActivityListVersion )
+ {
+ // this model's sequences have not yet been indexed by activity
+ IndexModelSequences( pstudiohdr );
+ }
+}
+
+#if !defined( MAKEXVCD )
+bool IsInPrediction()
+{
+ return CBaseEntity::GetPredictionPlayer() != NULL;
+}
+
+int SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence )
+{
+ VPROF( "SelectWeightedSequence" );
+
+ if (! pstudiohdr)
+ return 0;
+
+ if (!pstudiohdr->SequencesAvailable())
+ return 0;
+
+ VerifySequenceIndex( pstudiohdr );
+
+#if STUDIO_SEQUENCE_ACTIVITY_LOOKUPS_ARE_SLOW
+ int weighttotal = 0;
+ int seq = ACTIVITY_NOT_AVAILABLE;
+ int weight = 0;
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ int curActivity = GetSequenceActivity( pstudiohdr, i, &weight );
+ if (curActivity == activity)
+ {
+ if ( curSequence == i && weight < 0 )
+ {
+ seq = i;
+ break;
+ }
+ weighttotal += iabs(weight);
+
+ int randomValue;
+
+ if ( IsInPrediction() )
+ randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1, i );
+ else
+ randomValue = RandomInt( 0, weighttotal - 1 );
+
+ if (!weighttotal || randomValue < iabs(weight))
+ seq = i;
+ }
+ }
+
+ return seq;
+#else
+ return pstudiohdr->SelectWeightedSequence( activity, curSequence );
+#endif
+}
+
+
+// Pick a sequence for the given activity. If the current sequence is appropriate for the
+// current activity, and its stored weight is negative (whatever that means), always select
+// it. Otherwise perform a weighted selection -- imagine a large roulette wheel, with each
+// sequence having a number of spaces corresponding to its weight.
+int CStudioHdr::CActivityToSequenceMapping::SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence )
+{
+ if (!ValidateAgainst(pstudiohdr))
+ {
+ AssertMsg1(false, "CStudioHdr %s has changed its vmodel pointer without reinitializing its activity mapping! Now performing emergency reinitialization.", pstudiohdr->pszName());
+ ExecuteOnce(DebuggerBreakIfDebugging());
+ Reinitialize(pstudiohdr);
+ }
+
+ // a null m_pSequenceTuples just means that this studio header has no activities.
+ if (!m_pSequenceTuples)
+ return ACTIVITY_NOT_AVAILABLE;
+
+ // is the current sequence appropriate?
+ if (curSequence >= 0)
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( curSequence );
+
+ if (seqdesc.activity == activity && seqdesc.actweight < 0)
+ return curSequence;
+ }
+
+ // get the data for the given activity
+ HashValueType dummy( activity, 0, 0, 0 );
+ UtlHashHandle_t handle = m_ActToSeqHash.Find(dummy);
+ if (!m_ActToSeqHash.IsValidHandle(handle))
+ {
+ return ACTIVITY_NOT_AVAILABLE;
+ }
+ const HashValueType * __restrict actData = &m_ActToSeqHash[handle];
+
+ int weighttotal = actData->totalWeight;
+ // generate a random number from 0 to the total weight
+ int randomValue;
+ if ( IsInPrediction() )
+ {
+ randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1 );
+ }
+ else
+ {
+ randomValue = RandomInt( 0, weighttotal - 1 );
+ }
+
+ // chug through the entries in the list (they are sequential therefore cache-coherent)
+ // until we run out of random juice
+ SequenceTuple * __restrict sequenceInfo = m_pSequenceTuples + actData->startingIdx;
+
+ const SequenceTuple *const stopHere = sequenceInfo + actData->count; // this is a backup
+ // in case the weights are somehow miscalculated -- we don't read or write through
+ // it (because it aliases the restricted pointer above); it's only here for
+ // the comparison.
+
+ while (randomValue >= sequenceInfo->weight && sequenceInfo < stopHere)
+ {
+ randomValue -= sequenceInfo->weight;
+ ++sequenceInfo;
+ }
+
+ return sequenceInfo->seqnum;
+
+}
+
+
+#endif
+
+int SelectHeaviestSequence( CStudioHdr *pstudiohdr, int activity )
+{
+ if ( !pstudiohdr )
+ return 0;
+
+ VerifySequenceIndex( pstudiohdr );
+
+ int maxweight = 0;
+ int seq = ACTIVITY_NOT_AVAILABLE;
+ int weight = 0;
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ int curActivity = GetSequenceActivity( pstudiohdr, i, &weight );
+ if (curActivity == activity)
+ {
+ if ( iabs(weight) > maxweight )
+ {
+ maxweight = iabs(weight);
+ seq = i;
+ }
+ }
+ }
+
+ return seq;
+}
+
+void GetEyePosition ( CStudioHdr *pstudiohdr, Vector &vecEyePosition )
+{
+ if ( !pstudiohdr )
+ {
+ Warning( "GetEyePosition() Can't get pstudiohdr ptr!\n" );
+ return;
+ }
+
+ vecEyePosition = pstudiohdr->eyeposition();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Looks up an activity by name.
+// Input : label - Name of the activity to look up, ie "ACT_IDLE"
+// Output : Activity index or ACT_INVALID if not found.
+//-----------------------------------------------------------------------------
+int LookupActivity( CStudioHdr *pstudiohdr, const char *label )
+{
+ VPROF( "LookupActivity" );
+
+ if ( !pstudiohdr )
+ {
+ return 0;
+ }
+
+ for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ )
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if ( stricmp( seqdesc.pszActivityName(), label ) == 0 )
+ {
+ return seqdesc.activity;
+ }
+ }
+
+ return ACT_INVALID;
+}
+
+#if !defined( MAKEXVCD )
+//-----------------------------------------------------------------------------
+// Purpose: Looks up a sequence by sequence name first, then by activity name.
+// Input : label - The sequence name or activity name to look up.
+// Output : Returns the sequence index of the matching sequence, or ACT_INVALID.
+//-----------------------------------------------------------------------------
+int LookupSequence( CStudioHdr *pstudiohdr, const char *label )
+{
+ VPROF( "LookupSequence" );
+
+ if (! pstudiohdr)
+ return 0;
+
+ if (!pstudiohdr->SequencesAvailable())
+ return 0;
+
+ //
+ // Look up by sequence name.
+ //
+ for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
+ if (stricmp( seqdesc.pszLabel(), label ) == 0)
+ return i;
+ }
+
+ //
+ // Not found, look up by activity name.
+ //
+ int nActivity = LookupActivity( pstudiohdr, label );
+ if (nActivity != ACT_INVALID )
+ {
+ return SelectWeightedSequence( pstudiohdr, nActivity );
+ }
+
+ return ACT_INVALID;
+}
+
+void GetSequenceLinearMotion( CStudioHdr *pstudiohdr, int iSequence, const float poseParameter[], Vector *pVec )
+{
+ if (! pstudiohdr)
+ {
+ Msg( "Bad pstudiohdr in GetSequenceLinearMotion()!\n" );
+ return;
+ }
+
+ if (!pstudiohdr->SequencesAvailable())
+ return;
+
+ if( iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
+ {
+ // Don't spam on bogus model
+ if ( pstudiohdr->GetNumSeq() > 0 )
+ {
+ static int msgCount = 0;
+ while ( ++msgCount <= 10 )
+ {
+ Msg( "Bad sequence (%i out of %i max) in GetSequenceLinearMotion() for model '%s'!\n", iSequence, pstudiohdr->GetNumSeq(), pstudiohdr->pszName() );
+ }
+ }
+ pVec->Init();
+ return;
+ }
+
+ QAngle vecAngles;
+ Studio_SeqMovement( pstudiohdr, iSequence, 0, 1.0, poseParameter, (*pVec), vecAngles );
+}
+#endif
+
+const char *GetSequenceName( CStudioHdr *pstudiohdr, int iSequence )
+{
+ if( !pstudiohdr || iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
+ {
+ if ( pstudiohdr )
+ {
+ Msg( "Bad sequence in GetSequenceName() for model '%s'!\n", pstudiohdr->pszName() );
+ }
+ return "Unknown";
+ }
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( iSequence );
+ return seqdesc.pszLabel();
+}
+
+const char *GetSequenceActivityName( CStudioHdr *pstudiohdr, int iSequence )
+{
+ if( !pstudiohdr || iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
+ {
+ if ( pstudiohdr )
+ {
+ Msg( "Bad sequence in GetSequenceActivityName() for model '%s'!\n", pstudiohdr->pszName() );
+ }
+ return "Unknown";
+ }
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( iSequence );
+ return seqdesc.pszActivityName( );
+}
+
+int GetSequenceFlags( CStudioHdr *pstudiohdr, int sequence )
+{
+ if ( !pstudiohdr ||
+ !pstudiohdr->SequencesAvailable() ||
+ sequence < 0 ||
+ sequence >= pstudiohdr->GetNumSeq() )
+ {
+ return 0;
+ }
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
+
+ return seqdesc.flags;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pstudiohdr -
+// sequence -
+// type -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool HasAnimationEventOfType( CStudioHdr *pstudiohdr, int sequence, int type )
+{
+ if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() )
+ return false;
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
+ if ( !&seqdesc )
+ return false;
+
+ mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
+ if ( !pevent )
+ return false;
+
+ if (seqdesc.numevents == 0 )
+ return false;
+
+ int index;
+ for ( index = 0; index < (int)seqdesc.numevents; index++ )
+ {
+ if ( pevent[ index ].event == type )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int GetAnimationEvent( CStudioHdr *pstudiohdr, int sequence, animevent_t *pNPCEvent, float flStart, float flEnd, int index )
+{
+ if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() || !pNPCEvent )
+ return 0;
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
+ if (seqdesc.numevents == 0 || index >= (int)seqdesc.numevents )
+ return 0;
+
+ // Msg( "flStart %f flEnd %f (%d) %s\n", flStart, flEnd, seqdesc.numevents, seqdesc.label );
+ mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
+ for (; index < (int)seqdesc.numevents; index++)
+ {
+ // Don't send client-side events to the server AI
+ if ( pevent[index].type & AE_TYPE_NEWEVENTSYSTEM )
+ {
+ if ( !(pevent[index].type & AE_TYPE_SERVER) )
+ continue;
+ }
+ else if ( pevent[index].event >= EVENT_CLIENT ) //Adrian - Support the old event system
+ continue;
+
+ bool bOverlapEvent = false;
+
+ if (pevent[index].cycle >= flStart && pevent[index].cycle < flEnd)
+ {
+ bOverlapEvent = true;
+ }
+ // FIXME: doesn't work with animations being played in reverse
+ else if ((seqdesc.flags & STUDIO_LOOPING) && flEnd < flStart)
+ {
+ if (pevent[index].cycle >= flStart || pevent[index].cycle < flEnd)
+ {
+ bOverlapEvent = true;
+ }
+ }
+
+ if (bOverlapEvent)
+ {
+ pNPCEvent->pSource = NULL;
+ pNPCEvent->cycle = pevent[index].cycle;
+#if !defined( MAKEXVCD )
+ pNPCEvent->eventtime = gpGlobals->curtime;
+#else
+ pNPCEvent->eventtime = 0.0f;
+#endif
+ pNPCEvent->event = pevent[index].event;
+ pNPCEvent->options = pevent[index].pszOptions();
+ pNPCEvent->type = pevent[index].type;
+ return index + 1;
+ }
+ }
+ return 0;
+}
+
+
+
+int FindTransitionSequence( CStudioHdr *pstudiohdr, int iCurrentSequence, int iGoalSequence, int *piDir )
+{
+ if ( !pstudiohdr )
+ return iGoalSequence;
+
+ if ( !pstudiohdr->SequencesAvailable() )
+ return iGoalSequence;
+
+ if ( ( iCurrentSequence < 0 ) || ( iCurrentSequence >= pstudiohdr->GetNumSeq() ) )
+ return iGoalSequence;
+
+ if ( ( iGoalSequence < 0 ) || ( iGoalSequence >= pstudiohdr->GetNumSeq() ) )
+ {
+ // asking for a bogus sequence. Punt.
+ Assert( 0 );
+ return iGoalSequence;
+ }
+
+
+ // bail if we're going to or from a node 0
+ if (pstudiohdr->EntryNode( iCurrentSequence ) == 0 || pstudiohdr->EntryNode( iGoalSequence ) == 0)
+ {
+ *piDir = 1;
+ return iGoalSequence;
+ }
+
+ int iEndNode;
+
+ // Msg( "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
+
+ // check to see if we should be going forward or backward through the graph
+ if (*piDir > 0)
+ {
+ iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
+ }
+ else
+ {
+ iEndNode = pstudiohdr->EntryNode( iCurrentSequence );
+ }
+
+ // if both sequences are on the same node, just go there
+ if (iEndNode == pstudiohdr->EntryNode( iGoalSequence ))
+ {
+ *piDir = 1;
+ return iGoalSequence;
+ }
+
+ int iInternNode = pstudiohdr->GetTransition( iEndNode, pstudiohdr->EntryNode( iGoalSequence ) );
+
+ // if there is no transitionial node, just go to the goal sequence
+ if (iInternNode == 0)
+ return iGoalSequence;
+
+ int i;
+
+ // look for someone going from the entry node to next node it should hit
+ // this may be the goal sequences node or an intermediate node
+ for (i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc(i );
+ if (pstudiohdr->EntryNode( i ) == iEndNode && pstudiohdr->ExitNode( i ) == iInternNode)
+ {
+ *piDir = 1;
+ return i;
+ }
+ if (seqdesc.nodeflags)
+ {
+ if (pstudiohdr->ExitNode( i ) == iEndNode && pstudiohdr->EntryNode( i ) == iInternNode)
+ {
+ *piDir = -1;
+ return i;
+ }
+ }
+ }
+
+ // this means that two parts of the node graph are not connected.
+ DevMsg( 2, "error in transition graph: %s to %s\n", pstudiohdr->pszNodeName( iEndNode ), pstudiohdr->pszNodeName( pstudiohdr->EntryNode( iGoalSequence ) ));
+ // Go ahead and jump to the goal sequence
+ return iGoalSequence;
+}
+
+
+
+
+
+
+bool GotoSequence( CStudioHdr *pstudiohdr, int iCurrentSequence, float flCurrentCycle, float flCurrentRate, int iGoalSequence, int &nNextSequence, float &flNextCycle, int &iNextDir )
+{
+ if ( !pstudiohdr )
+ return false;
+
+ if ( !pstudiohdr->SequencesAvailable() )
+ return false;
+
+ if ( ( iCurrentSequence < 0 ) || ( iCurrentSequence >= pstudiohdr->GetNumSeq() ) )
+ return false;
+
+ if ( ( iGoalSequence < 0 ) || ( iGoalSequence >= pstudiohdr->GetNumSeq() ) )
+ {
+ // asking for a bogus sequence. Punt.
+ Assert( 0 );
+ return false;
+ }
+
+ // bail if we're going to or from a node 0
+ if (pstudiohdr->EntryNode( iCurrentSequence ) == 0 || pstudiohdr->EntryNode( iGoalSequence ) == 0)
+ {
+ iNextDir = 1;
+ flNextCycle = 0.0;
+ nNextSequence = iGoalSequence;
+ return true;
+ }
+
+ int iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
+ // Msg( "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
+
+ // if we're in a transition sequence
+ if (pstudiohdr->EntryNode( iCurrentSequence ) != pstudiohdr->ExitNode( iCurrentSequence ))
+ {
+ // are we done with it?
+ if (flCurrentRate > 0.0 && flCurrentCycle >= 0.999)
+ {
+ iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
+ }
+ else if (flCurrentRate < 0.0 && flCurrentCycle <= 0.001)
+ {
+ iEndNode = pstudiohdr->EntryNode( iCurrentSequence );
+ }
+ else
+ {
+ // nope, exit
+ return false;
+ }
+ }
+
+ // if both sequences are on the same node, just go there
+ if (iEndNode == pstudiohdr->EntryNode( iGoalSequence ))
+ {
+ iNextDir = 1;
+ flNextCycle = 0.0;
+ nNextSequence = iGoalSequence;
+ return true;
+ }
+
+ int iInternNode = pstudiohdr->GetTransition( iEndNode, pstudiohdr->EntryNode( iGoalSequence ) );
+
+ // if there is no transitionial node, just go to the goal sequence
+ if (iInternNode == 0)
+ {
+ iNextDir = 1;
+ flNextCycle = 0.0;
+ nNextSequence = iGoalSequence;
+ return true;
+ }
+
+ int i;
+
+ // look for someone going from the entry node to next node it should hit
+ // this may be the goal sequences node or an intermediate node
+ for (i = 0; i < pstudiohdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc(i );
+ if (pstudiohdr->EntryNode( i ) == iEndNode && pstudiohdr->ExitNode( i ) == iInternNode)
+ {
+ iNextDir = 1;
+ flNextCycle = 0.0;
+ nNextSequence = i;
+ return true;
+ }
+ if (seqdesc.nodeflags)
+ {
+ if (pstudiohdr->ExitNode( i ) == iEndNode && pstudiohdr->EntryNode( i ) == iInternNode)
+ {
+ iNextDir = -1;
+ flNextCycle = 0.999;
+ nNextSequence = i;
+ return true;
+ }
+ }
+ }
+
+ // this means that two parts of the node graph are not connected.
+ DevMsg( 2, "error in transition graph: %s to %s\n", pstudiohdr->pszNodeName( iEndNode ), pstudiohdr->pszNodeName( pstudiohdr->EntryNode( iGoalSequence ) ));
+ return false;
+}
+
+void SetBodygroup( CStudioHdr *pstudiohdr, int& body, int iGroup, int iValue )
+{
+ if (! pstudiohdr)
+ return;
+
+ if (iGroup >= pstudiohdr->numbodyparts())
+ return;
+
+ mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
+
+ if (iValue >= pbodypart->nummodels)
+ return;
+
+ int iCurrent = (body / pbodypart->base) % pbodypart->nummodels;
+
+ body = (body - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
+}
+
+
+int GetBodygroup( CStudioHdr *pstudiohdr, int body, int iGroup )
+{
+ if (! pstudiohdr)
+ return 0;
+
+ if (iGroup >= pstudiohdr->numbodyparts())
+ return 0;
+
+ mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
+
+ if (pbodypart->nummodels <= 1)
+ return 0;
+
+ int iCurrent = (body / pbodypart->base) % pbodypart->nummodels;
+
+ return iCurrent;
+}
+
+const char *GetBodygroupName( CStudioHdr *pstudiohdr, int iGroup )
+{
+ if ( !pstudiohdr)
+ return "";
+
+ if (iGroup >= pstudiohdr->numbodyparts())
+ return "";
+
+ mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
+ return pbodypart->pszName();
+}
+
+int FindBodygroupByName( CStudioHdr *pstudiohdr, const char *name )
+{
+ if ( !pstudiohdr )
+ return -1;
+
+ int group;
+ for ( group = 0; group < pstudiohdr->numbodyparts(); group++ )
+ {
+ mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( group );
+ if ( !Q_strcasecmp( name, pbodypart->pszName() ) )
+ {
+ return group;
+ }
+ }
+
+ return -1;
+}
+
+int GetBodygroupCount( CStudioHdr *pstudiohdr, int iGroup )
+{
+ if ( !pstudiohdr )
+ return 0;
+
+ if (iGroup >= pstudiohdr->numbodyparts())
+ return 0;
+
+ mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
+ return pbodypart->nummodels;
+}
+
+int GetNumBodyGroups( CStudioHdr *pstudiohdr )
+{
+ if ( !pstudiohdr )
+ return 0;
+
+ return pstudiohdr->numbodyparts();
+}
+
+int GetSequenceActivity( CStudioHdr *pstudiohdr, int sequence, int *pweight )
+{
+ if (!pstudiohdr || !pstudiohdr->SequencesAvailable() )
+ {
+ if (pweight)
+ *pweight = 0;
+ return 0;
+ }
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
+
+ if (!(seqdesc.flags & STUDIO_ACTIVITY))
+ {
+ SetActivityForSequence( pstudiohdr, sequence );
+ }
+ if (pweight)
+ *pweight = seqdesc.actweight;
+ return seqdesc.activity;
+}
+
+
+void GetAttachmentLocalSpace( CStudioHdr *pstudiohdr, int attachIndex, matrix3x4_t &pLocalToWorld )
+{
+ if ( attachIndex >= 0 )
+ {
+ const mstudioattachment_t &pAttachment = pstudiohdr->pAttachment(attachIndex);
+ MatrixCopy( pAttachment.local, pLocalToWorld );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pstudiohdr -
+// *name -
+// Output : int
+//-----------------------------------------------------------------------------
+int FindHitboxSetByName( CStudioHdr *pstudiohdr, const char *name )
+{
+ if ( !pstudiohdr )
+ return -1;
+
+ for ( int i = 0; i < pstudiohdr->numhitboxsets(); i++ )
+ {
+ mstudiohitboxset_t *set = pstudiohdr->pHitboxSet( i );
+ if ( !set )
+ continue;
+
+ if ( !stricmp( set->pszName(), name ) )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pstudiohdr -
+// setnumber -
+// Output : char const
+//-----------------------------------------------------------------------------
+const char *GetHitboxSetName( CStudioHdr *pstudiohdr, int setnumber )
+{
+ if ( !pstudiohdr )
+ return "";
+
+ mstudiohitboxset_t *set = pstudiohdr->pHitboxSet( setnumber );
+ if ( !set )
+ return "";
+
+ return set->pszName();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pstudiohdr -
+// Output : int
+//-----------------------------------------------------------------------------
+int GetHitboxSetCount( CStudioHdr *pstudiohdr )
+{
+ if ( !pstudiohdr )
+ return 0;
+
+ return pstudiohdr->numhitboxsets();
+}
|