aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/movement.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/movement.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/movement.cpp')
-rw-r--r--mp/src/game/server/movement.cpp658
1 files changed, 658 insertions, 0 deletions
diff --git a/mp/src/game/server/movement.cpp b/mp/src/game/server/movement.cpp
new file mode 100644
index 00000000..7f17d137
--- /dev/null
+++ b/mp/src/game/server/movement.cpp
@@ -0,0 +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;
+}
+