aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/movement.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/movement.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/movement.cpp')
-rw-r--r--mp/src/game/server/movement.cpp1316
1 files changed, 658 insertions, 658 deletions
diff --git a/mp/src/game/server/movement.cpp b/mp/src/game/server/movement.cpp
index 7f17d137..da12420e 100644
--- a/mp/src/game/server/movement.cpp
+++ b/mp/src/game/server/movement.cpp
@@ -1,658 +1,658 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: MOVEMENT ENTITIES TEST
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "entitylist.h"
-#include "entityoutput.h"
-#include "keyframe/keyframe.h" // BUG: this needs to move if keyframe is a standard thing
-
-#include "mathlib/mathlib.h" // FIXME: why do we still need this?
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-// Hack, sort of. These interpolators don't get to hold state, but the ones
-// that need state (like the rope simulator) should NOT be used as paths here.
-IPositionInterpolator *g_pPositionInterpolators[8] = {0,0,0,0,0,0,0,0};
-
-IPositionInterpolator* GetPositionInterpolator( int iInterp )
-{
- if( !g_pPositionInterpolators[iInterp] )
- g_pPositionInterpolators[iInterp] = Motion_GetPositionInterpolator( iInterp );
-
- return g_pPositionInterpolators[iInterp];
-}
-
-
-static float Fix( float angle )
-{
- while ( angle < 0 )
- angle += 360;
- while ( angle > 360 )
- angle -= 360;
-
- return angle;
-}
-
-void FixupAngles( QAngle &v )
-{
- v.x = Fix( v.x );
- v.y = Fix( v.y );
- v.z = Fix( v.z );
-}
-
-
-//-----------------------------------------------------------------------------
-//
-// Purpose: Contains a description of a keyframe
-// has no networked representation, so has to store origin, etc. itself
-//
-//-----------------------------------------------------------------------------
-class CPathKeyFrame : public CLogicalEntity
-{
-public:
- DECLARE_CLASS( CPathKeyFrame, CLogicalEntity );
-
- void Spawn( void );
- void Activate( void );
- void Link( void );
-
- Vector m_Origin;
- QAngle m_Angles; // euler angles PITCH YAW ROLL (Y Z X)
- Quaternion m_qAngle; // quaternion angle (generated from m_Angles)
-
- string_t m_iNextKey;
- float m_flNextTime;
-
- CPathKeyFrame *NextKey( int direction );
- CPathKeyFrame *PrevKey( int direction );
-
- float Speed( void ) { return m_flSpeed; }
- void SetKeyAngles( QAngle angles );
-
- CPathKeyFrame *InsertNewKey( Vector newPos, QAngle newAngles );
- void CalculateFrameDuration( void );
-
-protected:
- CPathKeyFrame *m_pNextKey;
- CPathKeyFrame *m_pPrevKey;
-
- float m_flSpeed;
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS( keyframe_track, CPathKeyFrame );
-
-BEGIN_DATADESC( CPathKeyFrame )
-
- DEFINE_FIELD( m_Origin, FIELD_VECTOR ),
- DEFINE_FIELD( m_Angles, FIELD_VECTOR ),
- DEFINE_FIELD( m_qAngle, FIELD_QUATERNION ),
-
- DEFINE_KEYFIELD( m_iNextKey, FIELD_STRING, "NextKey" ),
- DEFINE_FIELD( m_flNextTime, FIELD_FLOAT ), // derived from speed
- DEFINE_KEYFIELD( m_flSpeed, FIELD_FLOAT, "MoveSpeed" ),
- DEFINE_FIELD( m_pNextKey, FIELD_CLASSPTR ),
- DEFINE_FIELD( m_pPrevKey, FIELD_CLASSPTR ),
-
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Converts inputed euler angles to internal angle format (quaternions)
-//-----------------------------------------------------------------------------
-void CPathKeyFrame::Spawn( void )
-{
- m_Origin = GetLocalOrigin();
- m_Angles = GetLocalAngles();
-
- SetKeyAngles( m_Angles );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Adds the keyframe into the path after all the other keys have spawned
-//-----------------------------------------------------------------------------
-void CPathKeyFrame::Activate( void )
-{
- BaseClass::Activate();
-
- Link();
-
- CalculateFrameDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPathKeyFrame::CalculateFrameDuration( void )
-{
- // calculate time from speed
- if ( m_pNextKey && m_flSpeed > 0 )
- {
- m_flNextTime = (m_Origin - m_pNextKey->m_Origin).Length() / m_flSpeed;
-
- // couldn't get time from distance, get it from rotation instead
- if ( !m_flNextTime )
- {
- // speed is in degrees per second
- // find the largest rotation component and use that
- QAngle ang = m_Angles - m_pNextKey->m_Angles;
- FixupAngles( ang );
- float x = 0;
- for ( int i = 0; i < 3; i++ )
- {
- if ( abs(ang[i]) > x )
- {
- x = abs(ang[i]);
- }
- }
-
- m_flNextTime = x / m_flSpeed;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Links the key frame into the key frame list
-//-----------------------------------------------------------------------------
-void CPathKeyFrame::Link( void )
-{
- m_pNextKey = dynamic_cast<CPathKeyFrame*>( gEntList.FindEntityByName(NULL, m_iNextKey ) );
-
- if ( m_pNextKey )
- {
- m_pNextKey->m_pPrevKey = this;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : angles -
-//-----------------------------------------------------------------------------
-void CPathKeyFrame::SetKeyAngles( QAngle angles )
-{
- m_Angles = angles;
- AngleQuaternion( m_Angles, m_qAngle );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : direction -
-// Output : CPathKeyFrame
-//-----------------------------------------------------------------------------
-CPathKeyFrame* CPathKeyFrame::NextKey( int direction )
-{
- if ( direction == 1 )
- {
- return m_pNextKey;
- }
- else if ( direction == -1 )
- {
- return m_pPrevKey;
- }
-
- return this;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : direction -
-// Output : CPathKeyFrame
-//-----------------------------------------------------------------------------
-CPathKeyFrame *CPathKeyFrame::PrevKey( int direction )
-{
- if ( direction == 1 )
- {
- return m_pPrevKey;
- }
- else if ( direction == -1 )
- {
- return m_pNextKey;
- }
-
- return this;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Creates and insterts a new keyframe into the sequence
-// Input : newPos -
-// newAngles -
-// Output : CPathKeyFrame
-//-----------------------------------------------------------------------------
-CPathKeyFrame *CPathKeyFrame::InsertNewKey( Vector newPos, QAngle newAngles )
-{
- CPathKeyFrame *newKey = CREATE_ENTITY( CPathKeyFrame, "keyframe_track" );
-
- // copy data across
- newKey->SetKeyAngles( newAngles );
- newKey->m_Origin = newPos;
- newKey->m_flSpeed = m_flSpeed;
- newKey->SetEFlags( GetEFlags() );
- if ( m_iParent != NULL_STRING )
- {
- newKey->SetParent( m_iParent, NULL );
- }
-
- // link forward
- newKey->m_pNextKey = m_pNextKey;
- m_pNextKey->m_pPrevKey = newKey;
-
- // link back
- m_pNextKey = newKey;
- newKey->m_pPrevKey = this;
-
- // calculate new times
- CalculateFrameDuration();
- newKey->CalculateFrameDuration();
-
- return newKey;
-}
-
-
-//-----------------------------------------------------------------------------
-//
-// Purpose: Basic keyframed movement behavior
-//
-//-----------------------------------------------------------------------------
-class CBaseMoveBehavior : public CPathKeyFrame
-{
-public:
- DECLARE_CLASS( CBaseMoveBehavior, CPathKeyFrame );
-
- void Spawn( void );
- void Activate( void );
- void MoveDone( void );
- float SetObjectPhysicsVelocity( float moveTime );
-
- // methods
- virtual bool StartMoving( int direction );
- virtual void StopMoving( void );
- virtual bool IsMoving( void );
-
- // derived classes should override this to get notification of arriving at new keyframes
-// virtual void ArrivedAtKeyFrame( CPathKeyFrame * ) {}
-
- bool IsAtSequenceStart( void );
- bool IsAtSequenceEnd( void );
-
- // interpolation functions
-// int m_iTimeModifier;
- int m_iPositionInterpolator;
- int m_iRotationInterpolator;
-
- // animation vars
- float m_flAnimStartTime;
- float m_flAnimEndTime;
- float m_flAverageSpeedAcrossFrame; // for advancing time with speed (not the normal visa-versa)
- CPathKeyFrame *m_pCurrentKeyFrame; // keyframe currently moving from
- CPathKeyFrame *m_pTargetKeyFrame; // keyframe being moved to
- CPathKeyFrame *m_pPreKeyFrame, *m_pPostKeyFrame; // pre- and post-keyframe's for spline interpolation
- float m_flTimeIntoFrame;
-
- int m_iDirection; // 1 for forward, -1 for backward, and 0 for at rest
-
- float CalculateTimeAdvancementForSpeed( float moveTime, float speed );
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS( move_keyframed, CBaseMoveBehavior );
-
-BEGIN_DATADESC( CBaseMoveBehavior )
-
-// DEFINE_KEYFIELD( m_iTimeModifier, FIELD_INTEGER, "TimeModifier" ),
- DEFINE_KEYFIELD( m_iPositionInterpolator, FIELD_INTEGER, "PositionInterpolator" ),
- DEFINE_KEYFIELD( m_iRotationInterpolator, FIELD_INTEGER, "RotationInterpolator" ),
-
- DEFINE_FIELD( m_pCurrentKeyFrame, FIELD_CLASSPTR ),
- DEFINE_FIELD( m_pTargetKeyFrame, FIELD_CLASSPTR ),
- DEFINE_FIELD( m_pPreKeyFrame, FIELD_CLASSPTR ),
- DEFINE_FIELD( m_pPostKeyFrame, FIELD_CLASSPTR ),
-
- DEFINE_FIELD( m_flAnimStartTime, FIELD_FLOAT ),
- DEFINE_FIELD( m_flAnimEndTime, FIELD_FLOAT ),
- DEFINE_FIELD( m_flAverageSpeedAcrossFrame, FIELD_FLOAT ),
- DEFINE_FIELD( m_flTimeIntoFrame, FIELD_FLOAT ),
- DEFINE_FIELD( m_iDirection, FIELD_INTEGER ),
-
-END_DATADESC()
-
-
-void CBaseMoveBehavior::Spawn( void )
-{
- m_pCurrentKeyFrame = this;
- m_flTimeIntoFrame = 0;
- SetMoveType( MOVETYPE_PUSH );
-
- // a move behavior is also it's first keyframe
- m_Origin = GetLocalOrigin();
- m_Angles = GetLocalAngles();
-
- BaseClass::Spawn();
-}
-
-void CBaseMoveBehavior::Activate( void )
-{
- BaseClass::Activate();
-
- SetMoveDoneTime( 0.5 ); // start moving in 0.2 seconds time
-
- // if we are just the basic keyframed entity, cycle our animation
- if ( !stricmp(GetClassname(), "move_keyframed") )
- {
- StartMoving( 1 );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Checks to see if the we're at the start of the keyframe sequence
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseMoveBehavior::IsAtSequenceStart( void )
-{
- if ( !m_pCurrentKeyFrame )
- return true;
-
- if ( m_flAnimStartTime && m_flAnimStartTime >= GetLocalTime() )
- {
- if ( !m_pCurrentKeyFrame->PrevKey(1) && !m_pTargetKeyFrame )
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Checks to see if we're at the end of the keyframe sequence
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseMoveBehavior::IsAtSequenceEnd( void )
-{
- if ( !m_pCurrentKeyFrame )
- return false;
-
- if ( !m_pCurrentKeyFrame->NextKey(1) && !m_pTargetKeyFrame )
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseMoveBehavior::IsMoving( void )
-{
- if ( m_iDirection != 0 )
- return true;
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Starts the object moving from it's current position, in the direction indicated
-// Input : direction - 1 is forward through the sequence, -1 is backwards, and 0 is stop
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseMoveBehavior::StartMoving( int direction )
-{
- // 0 direction is to stop moving
- if ( direction == 0 )
- {
- StopMoving();
- return false;
- }
-
- // check to see if we should keep moving in the current direction
- if ( m_iDirection == direction )
- {
- // if we're at the end of the current anim key, move to the next one
- if ( GetLocalTime() >= m_flAnimEndTime )
- {
- m_pCurrentKeyFrame = m_pTargetKeyFrame;
- m_flTimeIntoFrame = 0;
-
- if ( !m_pTargetKeyFrame->NextKey(direction) )
- {
- // we've hit the end of the sequence
- m_flAnimEndTime = 0;
- m_flAnimStartTime = 0;
- StopMoving();
- return false;
- }
-
- // advance the target keyframe
- m_pTargetKeyFrame = m_pTargetKeyFrame->NextKey(direction);
- }
- }
- else
- {
- // we're changing direction
-
- // need to calculate current position in the frame
- // stop first, then start again
- if ( m_iDirection != 0 )
- {
- StopMoving();
- }
-
- m_iDirection = direction;
-
- // if we're going in reverse, swap the currentkey and targetkey (since we're going opposite dir)
- if ( direction == 1 )
- {
- m_pTargetKeyFrame = m_pCurrentKeyFrame->NextKey( direction );
- }
- else if ( direction == -1 )
- {
- if ( m_flTimeIntoFrame > 0 )
- {
- m_pTargetKeyFrame = m_pCurrentKeyFrame;
- m_pCurrentKeyFrame = m_pCurrentKeyFrame->NextKey( 1 );
- }
- else
- {
- m_pTargetKeyFrame = m_pCurrentKeyFrame->PrevKey( 1 );
- }
- }
-
- // recalculate our movement from the stored data
- if ( !m_pTargetKeyFrame )
- {
- StopMoving();
- return false;
- }
-
- // calculate the keyframes before and after the keyframes we're interpolating between
- m_pPostKeyFrame = m_pTargetKeyFrame->NextKey( direction );
- if ( !m_pPostKeyFrame )
- {
- m_pPostKeyFrame = m_pTargetKeyFrame;
- }
- m_pPreKeyFrame = m_pCurrentKeyFrame->PrevKey( direction );
- if ( !m_pPreKeyFrame )
- {
- m_pPreKeyFrame = m_pCurrentKeyFrame;
- }
- }
-
- // no target, can't move
- if ( !m_pTargetKeyFrame )
- return false;
-
- // calculate start/end time
- // ->m_flNextTime is the time to traverse to the NEXT key, so we need the opposite if travelling backwards
- if ( m_iDirection == 1 )
- {
- m_flAnimStartTime = GetLocalTime() - m_flTimeIntoFrame;
- m_flAnimEndTime = GetLocalTime() + m_pCurrentKeyFrame->m_flNextTime - m_flTimeIntoFrame;
- }
- else
- {
- // flip the timing, since we're in reverse
- if ( m_flTimeIntoFrame )
- m_flTimeIntoFrame = m_pTargetKeyFrame->m_flNextTime - m_flTimeIntoFrame;
-
- m_flAnimStartTime = GetLocalTime() - m_flTimeIntoFrame;
- m_flAnimEndTime = GetLocalTime() + m_pTargetKeyFrame->m_flNextTime - m_flTimeIntoFrame;
- }
-
- // calculate the average speed at which we cross
- float animDuration = (m_flAnimEndTime - m_flAnimStartTime);
- float dist = (m_pCurrentKeyFrame->m_Origin - m_pTargetKeyFrame->m_Origin).Length();
- m_flAverageSpeedAcrossFrame = animDuration / dist;
-
- SetMoveDoneTime( m_flAnimEndTime - GetLocalTime() );
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: stops the object from moving
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-void CBaseMoveBehavior::StopMoving( void )
-{
- // remember exactly where we are in the frame
- m_flTimeIntoFrame = 0;
-
- if ( m_iDirection == 1 )
- {
- // record the time if we're not at the end of the frame
- if ( GetLocalTime() < m_flAnimEndTime )
- {
- m_flTimeIntoFrame = GetLocalTime() - m_flAnimStartTime;
- }
- else
- {
- // we're actually at the end
- if ( m_pTargetKeyFrame )
- {
- m_pCurrentKeyFrame = m_pTargetKeyFrame;
- }
- }
- }
- else if ( m_iDirection == -1 )
- {
- // store it only as a forward movement
- m_pCurrentKeyFrame = m_pTargetKeyFrame;
-
- if ( GetLocalTime() < m_flAnimEndTime )
- {
- m_flTimeIntoFrame = m_flAnimEndTime - GetLocalTime();
- }
- }
-
- // stop moving totally
- SetMoveDoneTime( -1 );
- m_iDirection = 0;
- m_flAnimStartTime = 0;
- m_flAnimEndTime = 0;
- m_pTargetKeyFrame = NULL;
- SetAbsVelocity(vec3_origin);
- SetLocalAngularVelocity( vec3_angle );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: We have just arrived at a key, move onto the next keyframe
-//-----------------------------------------------------------------------------
-void CBaseMoveBehavior::MoveDone( void )
-{
- // if we're just a base then keep playing the anim
- if ( !stricmp(STRING(m_iClassname), "move_keyframed") )
- {
- int direction = m_iDirection;
- // start moving from the keyframe we've just reached
- if ( !StartMoving(direction) )
- {
- // try moving in the other direction
- StartMoving( -direction );
- }
- }
-
- BaseClass::MoveDone();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Calculates a new moveTime based on the speed and the current point
-// in the animation.
-// used to advance keyframed objects that have dynamic speeds.
-// Input : moveTime -
-// Output : float - the new time in the keyframing sequence
-//-----------------------------------------------------------------------------
-float CBaseMoveBehavior::CalculateTimeAdvancementForSpeed( float moveTime, float speed )
-{
- return (moveTime * speed * m_flAverageSpeedAcrossFrame);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// GetLocalTime() is the objects local current time
-// Input : destTime - new time that is being moved to
-// moveTime - amount of time to be advanced this frame
-// Output : float - the actual amount of time to move (usually moveTime)
-//-----------------------------------------------------------------------------
-float CBaseMoveBehavior::SetObjectPhysicsVelocity( float moveTime )
-{
- // make sure we have a valid set up
- if ( !m_pCurrentKeyFrame || !m_pTargetKeyFrame )
- return moveTime;
-
- // if we're not moving, we're not moving
- if ( !IsMoving() )
- return moveTime;
-
- float destTime = moveTime + GetLocalTime();
-
- // work out where we want to be, using destTime
- m_flTimeIntoFrame = destTime - m_flAnimStartTime;
- float newTime = (destTime - m_flAnimStartTime) / (m_flAnimEndTime - m_flAnimStartTime);
- Vector newPos;
- QAngle newAngles;
-
- IPositionInterpolator *pInterp = GetPositionInterpolator( m_iPositionInterpolator );
- if( pInterp )
- {
- // setup key frames
- pInterp->SetKeyPosition( -1, m_pPreKeyFrame->m_Origin );
- Motion_SetKeyAngles( -1, m_pPreKeyFrame->m_qAngle );
-
- pInterp->SetKeyPosition( 0, m_pCurrentKeyFrame->m_Origin );
- Motion_SetKeyAngles( 0, m_pCurrentKeyFrame->m_qAngle );
-
- pInterp->SetKeyPosition( 1, m_pTargetKeyFrame->m_Origin );
- Motion_SetKeyAngles( 1, m_pTargetKeyFrame->m_qAngle );
-
- pInterp->SetKeyPosition( 2, m_pPostKeyFrame->m_Origin );
- Motion_SetKeyAngles( 2, m_pPostKeyFrame->m_qAngle );
-
- // find new interpolated position & rotation
- pInterp->InterpolatePosition( newTime, newPos );
- }
- else
- {
- newPos.Init();
- }
-
- Quaternion qRot;
- Motion_InterpolateRotation( newTime, m_iRotationInterpolator, qRot );
- QuaternionAngles( qRot, newAngles );
-
- // find our velocity vector (newPos - currentPos) and scale velocity vector according to the movetime
- float oneOnMoveTime = 1 / moveTime;
- SetAbsVelocity( (newPos - GetLocalOrigin()) * oneOnMoveTime );
- SetLocalAngularVelocity( (newAngles - GetLocalAngles()) * oneOnMoveTime );
-
- return moveTime;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: MOVEMENT ENTITIES TEST
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "entitylist.h"
+#include "entityoutput.h"
+#include "keyframe/keyframe.h" // BUG: this needs to move if keyframe is a standard thing
+
+#include "mathlib/mathlib.h" // FIXME: why do we still need this?
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Hack, sort of. These interpolators don't get to hold state, but the ones
+// that need state (like the rope simulator) should NOT be used as paths here.
+IPositionInterpolator *g_pPositionInterpolators[8] = {0,0,0,0,0,0,0,0};
+
+IPositionInterpolator* GetPositionInterpolator( int iInterp )
+{
+ if( !g_pPositionInterpolators[iInterp] )
+ g_pPositionInterpolators[iInterp] = Motion_GetPositionInterpolator( iInterp );
+
+ return g_pPositionInterpolators[iInterp];
+}
+
+
+static float Fix( float angle )
+{
+ while ( angle < 0 )
+ angle += 360;
+ while ( angle > 360 )
+ angle -= 360;
+
+ return angle;
+}
+
+void FixupAngles( QAngle &v )
+{
+ v.x = Fix( v.x );
+ v.y = Fix( v.y );
+ v.z = Fix( v.z );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Contains a description of a keyframe
+// has no networked representation, so has to store origin, etc. itself
+//
+//-----------------------------------------------------------------------------
+class CPathKeyFrame : public CLogicalEntity
+{
+public:
+ DECLARE_CLASS( CPathKeyFrame, CLogicalEntity );
+
+ void Spawn( void );
+ void Activate( void );
+ void Link( void );
+
+ Vector m_Origin;
+ QAngle m_Angles; // euler angles PITCH YAW ROLL (Y Z X)
+ Quaternion m_qAngle; // quaternion angle (generated from m_Angles)
+
+ string_t m_iNextKey;
+ float m_flNextTime;
+
+ CPathKeyFrame *NextKey( int direction );
+ CPathKeyFrame *PrevKey( int direction );
+
+ float Speed( void ) { return m_flSpeed; }
+ void SetKeyAngles( QAngle angles );
+
+ CPathKeyFrame *InsertNewKey( Vector newPos, QAngle newAngles );
+ void CalculateFrameDuration( void );
+
+protected:
+ CPathKeyFrame *m_pNextKey;
+ CPathKeyFrame *m_pPrevKey;
+
+ float m_flSpeed;
+
+ DECLARE_DATADESC();
+};
+
+LINK_ENTITY_TO_CLASS( keyframe_track, CPathKeyFrame );
+
+BEGIN_DATADESC( CPathKeyFrame )
+
+ DEFINE_FIELD( m_Origin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Angles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_qAngle, FIELD_QUATERNION ),
+
+ DEFINE_KEYFIELD( m_iNextKey, FIELD_STRING, "NextKey" ),
+ DEFINE_FIELD( m_flNextTime, FIELD_FLOAT ), // derived from speed
+ DEFINE_KEYFIELD( m_flSpeed, FIELD_FLOAT, "MoveSpeed" ),
+ DEFINE_FIELD( m_pNextKey, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_pPrevKey, FIELD_CLASSPTR ),
+
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Converts inputed euler angles to internal angle format (quaternions)
+//-----------------------------------------------------------------------------
+void CPathKeyFrame::Spawn( void )
+{
+ m_Origin = GetLocalOrigin();
+ m_Angles = GetLocalAngles();
+
+ SetKeyAngles( m_Angles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the keyframe into the path after all the other keys have spawned
+//-----------------------------------------------------------------------------
+void CPathKeyFrame::Activate( void )
+{
+ BaseClass::Activate();
+
+ Link();
+
+ CalculateFrameDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPathKeyFrame::CalculateFrameDuration( void )
+{
+ // calculate time from speed
+ if ( m_pNextKey && m_flSpeed > 0 )
+ {
+ m_flNextTime = (m_Origin - m_pNextKey->m_Origin).Length() / m_flSpeed;
+
+ // couldn't get time from distance, get it from rotation instead
+ if ( !m_flNextTime )
+ {
+ // speed is in degrees per second
+ // find the largest rotation component and use that
+ QAngle ang = m_Angles - m_pNextKey->m_Angles;
+ FixupAngles( ang );
+ float x = 0;
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( abs(ang[i]) > x )
+ {
+ x = abs(ang[i]);
+ }
+ }
+
+ m_flNextTime = x / m_flSpeed;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Links the key frame into the key frame list
+//-----------------------------------------------------------------------------
+void CPathKeyFrame::Link( void )
+{
+ m_pNextKey = dynamic_cast<CPathKeyFrame*>( gEntList.FindEntityByName(NULL, m_iNextKey ) );
+
+ if ( m_pNextKey )
+ {
+ m_pNextKey->m_pPrevKey = this;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : angles -
+//-----------------------------------------------------------------------------
+void CPathKeyFrame::SetKeyAngles( QAngle angles )
+{
+ m_Angles = angles;
+ AngleQuaternion( m_Angles, m_qAngle );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : direction -
+// Output : CPathKeyFrame
+//-----------------------------------------------------------------------------
+CPathKeyFrame* CPathKeyFrame::NextKey( int direction )
+{
+ if ( direction == 1 )
+ {
+ return m_pNextKey;
+ }
+ else if ( direction == -1 )
+ {
+ return m_pPrevKey;
+ }
+
+ return this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : direction -
+// Output : CPathKeyFrame
+//-----------------------------------------------------------------------------
+CPathKeyFrame *CPathKeyFrame::PrevKey( int direction )
+{
+ if ( direction == 1 )
+ {
+ return m_pPrevKey;
+ }
+ else if ( direction == -1 )
+ {
+ return m_pNextKey;
+ }
+
+ return this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates and insterts a new keyframe into the sequence
+// Input : newPos -
+// newAngles -
+// Output : CPathKeyFrame
+//-----------------------------------------------------------------------------
+CPathKeyFrame *CPathKeyFrame::InsertNewKey( Vector newPos, QAngle newAngles )
+{
+ CPathKeyFrame *newKey = CREATE_ENTITY( CPathKeyFrame, "keyframe_track" );
+
+ // copy data across
+ newKey->SetKeyAngles( newAngles );
+ newKey->m_Origin = newPos;
+ newKey->m_flSpeed = m_flSpeed;
+ newKey->SetEFlags( GetEFlags() );
+ if ( m_iParent != NULL_STRING )
+ {
+ newKey->SetParent( m_iParent, NULL );
+ }
+
+ // link forward
+ newKey->m_pNextKey = m_pNextKey;
+ m_pNextKey->m_pPrevKey = newKey;
+
+ // link back
+ m_pNextKey = newKey;
+ newKey->m_pPrevKey = this;
+
+ // calculate new times
+ CalculateFrameDuration();
+ newKey->CalculateFrameDuration();
+
+ return newKey;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Basic keyframed movement behavior
+//
+//-----------------------------------------------------------------------------
+class CBaseMoveBehavior : public CPathKeyFrame
+{
+public:
+ DECLARE_CLASS( CBaseMoveBehavior, CPathKeyFrame );
+
+ void Spawn( void );
+ void Activate( void );
+ void MoveDone( void );
+ float SetObjectPhysicsVelocity( float moveTime );
+
+ // methods
+ virtual bool StartMoving( int direction );
+ virtual void StopMoving( void );
+ virtual bool IsMoving( void );
+
+ // derived classes should override this to get notification of arriving at new keyframes
+// virtual void ArrivedAtKeyFrame( CPathKeyFrame * ) {}
+
+ bool IsAtSequenceStart( void );
+ bool IsAtSequenceEnd( void );
+
+ // interpolation functions
+// int m_iTimeModifier;
+ int m_iPositionInterpolator;
+ int m_iRotationInterpolator;
+
+ // animation vars
+ float m_flAnimStartTime;
+ float m_flAnimEndTime;
+ float m_flAverageSpeedAcrossFrame; // for advancing time with speed (not the normal visa-versa)
+ CPathKeyFrame *m_pCurrentKeyFrame; // keyframe currently moving from
+ CPathKeyFrame *m_pTargetKeyFrame; // keyframe being moved to
+ CPathKeyFrame *m_pPreKeyFrame, *m_pPostKeyFrame; // pre- and post-keyframe's for spline interpolation
+ float m_flTimeIntoFrame;
+
+ int m_iDirection; // 1 for forward, -1 for backward, and 0 for at rest
+
+ float CalculateTimeAdvancementForSpeed( float moveTime, float speed );
+
+ DECLARE_DATADESC();
+};
+
+LINK_ENTITY_TO_CLASS( move_keyframed, CBaseMoveBehavior );
+
+BEGIN_DATADESC( CBaseMoveBehavior )
+
+// DEFINE_KEYFIELD( m_iTimeModifier, FIELD_INTEGER, "TimeModifier" ),
+ DEFINE_KEYFIELD( m_iPositionInterpolator, FIELD_INTEGER, "PositionInterpolator" ),
+ DEFINE_KEYFIELD( m_iRotationInterpolator, FIELD_INTEGER, "RotationInterpolator" ),
+
+ DEFINE_FIELD( m_pCurrentKeyFrame, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_pTargetKeyFrame, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_pPreKeyFrame, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_pPostKeyFrame, FIELD_CLASSPTR ),
+
+ DEFINE_FIELD( m_flAnimStartTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flAnimEndTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flAverageSpeedAcrossFrame, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flTimeIntoFrame, FIELD_FLOAT ),
+ DEFINE_FIELD( m_iDirection, FIELD_INTEGER ),
+
+END_DATADESC()
+
+
+void CBaseMoveBehavior::Spawn( void )
+{
+ m_pCurrentKeyFrame = this;
+ m_flTimeIntoFrame = 0;
+ SetMoveType( MOVETYPE_PUSH );
+
+ // a move behavior is also it's first keyframe
+ m_Origin = GetLocalOrigin();
+ m_Angles = GetLocalAngles();
+
+ BaseClass::Spawn();
+}
+
+void CBaseMoveBehavior::Activate( void )
+{
+ BaseClass::Activate();
+
+ SetMoveDoneTime( 0.5 ); // start moving in 0.2 seconds time
+
+ // if we are just the basic keyframed entity, cycle our animation
+ if ( !stricmp(GetClassname(), "move_keyframed") )
+ {
+ StartMoving( 1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if the we're at the start of the keyframe sequence
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseMoveBehavior::IsAtSequenceStart( void )
+{
+ if ( !m_pCurrentKeyFrame )
+ return true;
+
+ if ( m_flAnimStartTime && m_flAnimStartTime >= GetLocalTime() )
+ {
+ if ( !m_pCurrentKeyFrame->PrevKey(1) && !m_pTargetKeyFrame )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if we're at the end of the keyframe sequence
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseMoveBehavior::IsAtSequenceEnd( void )
+{
+ if ( !m_pCurrentKeyFrame )
+ return false;
+
+ if ( !m_pCurrentKeyFrame->NextKey(1) && !m_pTargetKeyFrame )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseMoveBehavior::IsMoving( void )
+{
+ if ( m_iDirection != 0 )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts the object moving from it's current position, in the direction indicated
+// Input : direction - 1 is forward through the sequence, -1 is backwards, and 0 is stop
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseMoveBehavior::StartMoving( int direction )
+{
+ // 0 direction is to stop moving
+ if ( direction == 0 )
+ {
+ StopMoving();
+ return false;
+ }
+
+ // check to see if we should keep moving in the current direction
+ if ( m_iDirection == direction )
+ {
+ // if we're at the end of the current anim key, move to the next one
+ if ( GetLocalTime() >= m_flAnimEndTime )
+ {
+ m_pCurrentKeyFrame = m_pTargetKeyFrame;
+ m_flTimeIntoFrame = 0;
+
+ if ( !m_pTargetKeyFrame->NextKey(direction) )
+ {
+ // we've hit the end of the sequence
+ m_flAnimEndTime = 0;
+ m_flAnimStartTime = 0;
+ StopMoving();
+ return false;
+ }
+
+ // advance the target keyframe
+ m_pTargetKeyFrame = m_pTargetKeyFrame->NextKey(direction);
+ }
+ }
+ else
+ {
+ // we're changing direction
+
+ // need to calculate current position in the frame
+ // stop first, then start again
+ if ( m_iDirection != 0 )
+ {
+ StopMoving();
+ }
+
+ m_iDirection = direction;
+
+ // if we're going in reverse, swap the currentkey and targetkey (since we're going opposite dir)
+ if ( direction == 1 )
+ {
+ m_pTargetKeyFrame = m_pCurrentKeyFrame->NextKey( direction );
+ }
+ else if ( direction == -1 )
+ {
+ if ( m_flTimeIntoFrame > 0 )
+ {
+ m_pTargetKeyFrame = m_pCurrentKeyFrame;
+ m_pCurrentKeyFrame = m_pCurrentKeyFrame->NextKey( 1 );
+ }
+ else
+ {
+ m_pTargetKeyFrame = m_pCurrentKeyFrame->PrevKey( 1 );
+ }
+ }
+
+ // recalculate our movement from the stored data
+ if ( !m_pTargetKeyFrame )
+ {
+ StopMoving();
+ return false;
+ }
+
+ // calculate the keyframes before and after the keyframes we're interpolating between
+ m_pPostKeyFrame = m_pTargetKeyFrame->NextKey( direction );
+ if ( !m_pPostKeyFrame )
+ {
+ m_pPostKeyFrame = m_pTargetKeyFrame;
+ }
+ m_pPreKeyFrame = m_pCurrentKeyFrame->PrevKey( direction );
+ if ( !m_pPreKeyFrame )
+ {
+ m_pPreKeyFrame = m_pCurrentKeyFrame;
+ }
+ }
+
+ // no target, can't move
+ if ( !m_pTargetKeyFrame )
+ return false;
+
+ // calculate start/end time
+ // ->m_flNextTime is the time to traverse to the NEXT key, so we need the opposite if travelling backwards
+ if ( m_iDirection == 1 )
+ {
+ m_flAnimStartTime = GetLocalTime() - m_flTimeIntoFrame;
+ m_flAnimEndTime = GetLocalTime() + m_pCurrentKeyFrame->m_flNextTime - m_flTimeIntoFrame;
+ }
+ else
+ {
+ // flip the timing, since we're in reverse
+ if ( m_flTimeIntoFrame )
+ m_flTimeIntoFrame = m_pTargetKeyFrame->m_flNextTime - m_flTimeIntoFrame;
+
+ m_flAnimStartTime = GetLocalTime() - m_flTimeIntoFrame;
+ m_flAnimEndTime = GetLocalTime() + m_pTargetKeyFrame->m_flNextTime - m_flTimeIntoFrame;
+ }
+
+ // calculate the average speed at which we cross
+ float animDuration = (m_flAnimEndTime - m_flAnimStartTime);
+ float dist = (m_pCurrentKeyFrame->m_Origin - m_pTargetKeyFrame->m_Origin).Length();
+ m_flAverageSpeedAcrossFrame = animDuration / dist;
+
+ SetMoveDoneTime( m_flAnimEndTime - GetLocalTime() );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: stops the object from moving
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CBaseMoveBehavior::StopMoving( void )
+{
+ // remember exactly where we are in the frame
+ m_flTimeIntoFrame = 0;
+
+ if ( m_iDirection == 1 )
+ {
+ // record the time if we're not at the end of the frame
+ if ( GetLocalTime() < m_flAnimEndTime )
+ {
+ m_flTimeIntoFrame = GetLocalTime() - m_flAnimStartTime;
+ }
+ else
+ {
+ // we're actually at the end
+ if ( m_pTargetKeyFrame )
+ {
+ m_pCurrentKeyFrame = m_pTargetKeyFrame;
+ }
+ }
+ }
+ else if ( m_iDirection == -1 )
+ {
+ // store it only as a forward movement
+ m_pCurrentKeyFrame = m_pTargetKeyFrame;
+
+ if ( GetLocalTime() < m_flAnimEndTime )
+ {
+ m_flTimeIntoFrame = m_flAnimEndTime - GetLocalTime();
+ }
+ }
+
+ // stop moving totally
+ SetMoveDoneTime( -1 );
+ m_iDirection = 0;
+ m_flAnimStartTime = 0;
+ m_flAnimEndTime = 0;
+ m_pTargetKeyFrame = NULL;
+ SetAbsVelocity(vec3_origin);
+ SetLocalAngularVelocity( vec3_angle );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: We have just arrived at a key, move onto the next keyframe
+//-----------------------------------------------------------------------------
+void CBaseMoveBehavior::MoveDone( void )
+{
+ // if we're just a base then keep playing the anim
+ if ( !stricmp(STRING(m_iClassname), "move_keyframed") )
+ {
+ int direction = m_iDirection;
+ // start moving from the keyframe we've just reached
+ if ( !StartMoving(direction) )
+ {
+ // try moving in the other direction
+ StartMoving( -direction );
+ }
+ }
+
+ BaseClass::MoveDone();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculates a new moveTime based on the speed and the current point
+// in the animation.
+// used to advance keyframed objects that have dynamic speeds.
+// Input : moveTime -
+// Output : float - the new time in the keyframing sequence
+//-----------------------------------------------------------------------------
+float CBaseMoveBehavior::CalculateTimeAdvancementForSpeed( float moveTime, float speed )
+{
+ return (moveTime * speed * m_flAverageSpeedAcrossFrame);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// GetLocalTime() is the objects local current time
+// Input : destTime - new time that is being moved to
+// moveTime - amount of time to be advanced this frame
+// Output : float - the actual amount of time to move (usually moveTime)
+//-----------------------------------------------------------------------------
+float CBaseMoveBehavior::SetObjectPhysicsVelocity( float moveTime )
+{
+ // make sure we have a valid set up
+ if ( !m_pCurrentKeyFrame || !m_pTargetKeyFrame )
+ return moveTime;
+
+ // if we're not moving, we're not moving
+ if ( !IsMoving() )
+ return moveTime;
+
+ float destTime = moveTime + GetLocalTime();
+
+ // work out where we want to be, using destTime
+ m_flTimeIntoFrame = destTime - m_flAnimStartTime;
+ float newTime = (destTime - m_flAnimStartTime) / (m_flAnimEndTime - m_flAnimStartTime);
+ Vector newPos;
+ QAngle newAngles;
+
+ IPositionInterpolator *pInterp = GetPositionInterpolator( m_iPositionInterpolator );
+ if( pInterp )
+ {
+ // setup key frames
+ pInterp->SetKeyPosition( -1, m_pPreKeyFrame->m_Origin );
+ Motion_SetKeyAngles( -1, m_pPreKeyFrame->m_qAngle );
+
+ pInterp->SetKeyPosition( 0, m_pCurrentKeyFrame->m_Origin );
+ Motion_SetKeyAngles( 0, m_pCurrentKeyFrame->m_qAngle );
+
+ pInterp->SetKeyPosition( 1, m_pTargetKeyFrame->m_Origin );
+ Motion_SetKeyAngles( 1, m_pTargetKeyFrame->m_qAngle );
+
+ pInterp->SetKeyPosition( 2, m_pPostKeyFrame->m_Origin );
+ Motion_SetKeyAngles( 2, m_pPostKeyFrame->m_qAngle );
+
+ // find new interpolated position & rotation
+ pInterp->InterpolatePosition( newTime, newPos );
+ }
+ else
+ {
+ newPos.Init();
+ }
+
+ Quaternion qRot;
+ Motion_InterpolateRotation( newTime, m_iRotationInterpolator, qRot );
+ QuaternionAngles( qRot, newAngles );
+
+ // find our velocity vector (newPos - currentPos) and scale velocity vector according to the movetime
+ float oneOnMoveTime = 1 / moveTime;
+ SetAbsVelocity( (newPos - GetLocalOrigin()) * oneOnMoveTime );
+ SetLocalAngularVelocity( (newAngles - GetLocalAngles()) * oneOnMoveTime );
+
+ return moveTime;
+}
+