diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/movement.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-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.cpp | 1316 |
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; +} + |