aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/ai_motor.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/ai_motor.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/ai_motor.cpp')
-rw-r--r--mp/src/game/server/ai_motor.cpp1033
1 files changed, 1033 insertions, 0 deletions
diff --git a/mp/src/game/server/ai_motor.cpp b/mp/src/game/server/ai_motor.cpp
new file mode 100644
index 00000000..4c1e03b1
--- /dev/null
+++ b/mp/src/game/server/ai_motor.cpp
@@ -0,0 +1,1033 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "animation.h" // for NOMOTION
+
+#include "ai_motor.h"
+#include "ai_navigator.h"
+#include "ai_basenpc.h"
+#include "ai_localnavigator.h"
+#include "ai_moveprobe.h"
+#include "saverestore_utlvector.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef DEBUG
+ConVar ai_draw_motor_movement( "ai_draw_motor_movement","0" );
+#endif
+
+extern float GetFloorZ(const Vector &origin);
+
+//-----------------------------------------------------------------------------
+
+// Use these functions to set breakpoints to find out where movement is failing
+#ifdef DEBUG
+void DebugNoteMovementFailure()
+{
+}
+
+// a place to put breakpoints
+#pragma warning(push)
+#pragma warning(disable:4189)
+AIMoveResult_t DbgResult( AIMoveResult_t result )
+{
+ if ( result < AIMR_OK )
+ {
+ int breakHere = 1;
+ }
+
+ switch ( result )
+ {
+ case AIMR_BLOCKED_ENTITY:
+ return AIMR_BLOCKED_ENTITY;
+ case AIMR_BLOCKED_WORLD:
+ return AIMR_BLOCKED_WORLD;
+ case AIMR_BLOCKED_NPC:
+ return AIMR_BLOCKED_NPC;
+ case AIMR_ILLEGAL:
+ return AIMR_ILLEGAL;
+ case AIMR_OK:
+ return AIMR_OK;
+ case AIMR_CHANGE_TYPE:
+ return AIMR_CHANGE_TYPE;
+ };
+ return AIMR_ILLEGAL;
+};
+#endif
+
+//-----------------------------------------------------------------------------
+//
+// class CAI_Motor
+//
+
+BEGIN_SIMPLE_DATADESC( CAI_Motor )
+ // m_flMoveInterval (think transient)
+ DEFINE_FIELD( m_IdealYaw, FIELD_FLOAT ),
+ DEFINE_FIELD( m_YawSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vecVelocity, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecAngularVelocity, FIELD_VECTOR ),
+ DEFINE_FIELD( m_nDismountSequence, FIELD_INTEGER ),
+ DEFINE_FIELD( m_vecDismount, FIELD_VECTOR ),
+ DEFINE_UTLVECTOR( m_facingQueue, FIELD_EMBEDDED ),
+ DEFINE_FIELD( m_bYawLocked, FIELD_BOOLEAN ),
+ // m_pMoveProbe
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+
+CAI_Motor::CAI_Motor(CAI_BaseNPC *pOuter)
+ : CAI_Component( pOuter )
+{
+ m_flMoveInterval = 0;
+
+ m_IdealYaw = 0;
+ m_YawSpeed = 0;
+ m_vecVelocity = Vector( 0, 0, 0 );
+ m_pMoveProbe = NULL;
+ m_bYawLocked = false;
+}
+
+//-----------------------------------------------------------------------------
+
+CAI_Motor::~CAI_Motor()
+{
+}
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::Init( IAI_MovementSink *pMovementServices )
+{
+ CAI_ProxyMovementSink::Init( pMovementServices );
+ m_pMoveProbe = GetOuter()->GetMoveProbe(); // @TODO (toml 03-30-03): this is a "bad" way to grab this pointer. Components should have an explcit "init" phase.
+}
+
+//-----------------------------------------------------------------------------
+// Step iteratively toward a destination position
+//-----------------------------------------------------------------------------
+AIMotorMoveResult_t CAI_Motor::MoveGroundStep( const Vector &newPos, CBaseEntity *pMoveTarget, float yaw, bool bAsFarAsCan, bool bTestZ, AIMoveTrace_t *pTraceResult )
+{
+ // By definition, this will produce different results than GroundMoveLimit()
+ // because there's no guarantee that it will step exactly one step
+
+ // See how far toward the new position we can step...
+ // But don't actually test for ground geometric validity;
+ // if it isn't valid, there's not much we can do about it
+ AIMoveTrace_t moveTrace;
+ unsigned testFlags = AITGM_IGNORE_FLOOR;
+
+ if ( !bTestZ )
+ testFlags |= AITGM_2D;
+
+#ifdef DEBUG
+ if ( ai_draw_motor_movement.GetBool() )
+ testFlags |= AITGM_DRAW_RESULTS;
+#endif
+
+ GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, MASK_NPCSOLID, testFlags, &moveTrace );
+ if ( pTraceResult )
+ {
+ *pTraceResult = moveTrace;
+ }
+
+ bool bHitTarget = (moveTrace.pObstruction && (pMoveTarget == moveTrace.pObstruction ));
+
+ // Move forward either if there was no obstruction or if we're told to
+ // move as far as we can, regardless
+ bool bIsBlocked = IsMoveBlocked(moveTrace.fStatus);
+ if ( !bIsBlocked || bAsFarAsCan || bHitTarget )
+ {
+#ifdef DEBUG
+ if ( GetMoveProbe()->CheckStandPosition( GetLocalOrigin(), MASK_NPCSOLID ) && !GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, MASK_NPCSOLID ) )
+ {
+ DevMsg( 2, "Warning: AI motor probably given invalid instructions\n" );
+ }
+#endif
+
+ // The true argument here causes it to touch all triggers
+ // in the volume swept from the previous position to the current position
+ UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true);
+
+ // check to see if our ground entity has changed
+ // NOTE: This is to detect changes in ground entity as the movement code has optimized out
+ // ground checks. So now we have to do a simple recheck to make sure we detect when we've
+ // stepped onto a new entity.
+ if ( GetOuter()->GetFlags() & FL_ONGROUND )
+ {
+ GetOuter()->PhysicsStepRecheckGround();
+ }
+
+ // skip tiny steps, but notify the shadow object of any large steps
+ if ( moveTrace.flStepUpDistance > 0.1f )
+ {
+ float height = clamp( moveTrace.flStepUpDistance, 0.f, StepHeight() );
+ IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ IPhysicsShadowController *pShadow = pPhysicsObject->GetShadowController();
+ if ( pShadow )
+ {
+ pShadow->StepUp( height );
+ }
+ }
+ }
+ if ( yaw != -1 )
+ {
+ QAngle angles = GetLocalAngles();
+ angles.y = yaw;
+ SetLocalAngles( angles );
+ }
+ if ( bHitTarget )
+ return AIM_PARTIAL_HIT_TARGET;
+
+ if ( !bIsBlocked )
+ return AIM_SUCCESS;
+
+ if ( moveTrace.fStatus == AIMR_BLOCKED_NPC )
+ return AIM_PARTIAL_HIT_NPC;
+
+ return AIM_PARTIAL_HIT_WORLD;
+ }
+ return AIM_FAILED;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Motion for climbing
+// Input :
+// Output : Returns bits (MoveStatus_b) regarding the move status
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw )
+{
+ // @Note (toml 06-11-02): the following code is somewhat suspect. It
+ // originated in CAI_BaseNPC::MoveClimb() from early June 2002
+ // At the very least, state should be restored to original, not
+ // slammed.
+ //
+ // -----Original Message-----
+ // From: Jay Stelly
+ // Sent: Monday, June 10, 2002 3:57 PM
+ // To: Tom Leonard
+ // Subject: RE:
+ //
+ // yes.
+ //
+ // Also, there is some subtlety to using movetype. I think in
+ // general we want to keep things in MOVETYPE_STEP because it
+ // implies a bunch of things in the external game physics
+ // simulator. There is a flag FL_FLY we use to
+ // disable gravity on MOVETYPE_STEP characters.
+ //
+ // > -----Original Message-----
+ // > From: Tom Leonard
+ // > Sent: Monday, June 10, 2002 3:55 PM
+ // > To: Jay Stelly
+ // > Subject:
+ // >
+ // > Should I worry at all that the following highlighted bits of
+ // > code are not reciprocal for all state, and furthermore, stomp
+ // > other state?
+
+ if ( fabsf( climbDir.z ) < .1 )
+ {
+ SetActivity( GetNavigator()->GetMovementActivity() );
+ }
+ else
+ {
+ SetActivity( (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN );
+ }
+
+ m_nDismountSequence = SelectWeightedSequence( ACT_CLIMB_DISMOUNT );
+ if (m_nDismountSequence != ACT_INVALID)
+ {
+ GetOuter()->GetSequenceLinearMotion( m_nDismountSequence, &m_vecDismount );
+ }
+ else
+ {
+ m_vecDismount.Init();
+ }
+
+ GetOuter()->AddFlag( FL_FLY ); // No gravity
+ SetSolid( SOLID_BBOX );
+ SetGravity( 0.0 );
+ SetGroundEntity( NULL );
+}
+
+AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft )
+{
+ if ( fabsf( climbDir.z ) > .1 )
+ {
+ if ( GetActivity() != ACT_CLIMB_DISMOUNT )
+ {
+ Activity desiredActivity = (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN;
+ if ( GetActivity() != desiredActivity )
+ {
+ SetActivity( desiredActivity );
+ }
+ }
+
+ if ( GetActivity() != ACT_CLIMB_UP && GetActivity() != ACT_CLIMB_DOWN && GetActivity() != ACT_CLIMB_DISMOUNT )
+ {
+ DevMsg( "Climber not in a climb activity!\n" );
+ return AIMR_ILLEGAL;
+ }
+
+ if (m_nDismountSequence != ACT_INVALID)
+ {
+ if (GetActivity() == ACT_CLIMB_UP )
+ {
+ if (climbNodesLeft <= 2 && climbDist < fabs( m_vecDismount.z ))
+ {
+ // fixme: No other way to force m_nIdealSequence?
+ GetOuter()->SetActivity( ACT_CLIMB_DISMOUNT );
+ GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
+ }
+ }
+ }
+ }
+
+ float climbSpeed = GetOuter()->GetInstantaneousVelocity();
+
+ if (m_nDismountSequence != ACT_INVALID)
+ {
+ // catch situations where the climb mount/dismount finished before reaching goal
+ climbSpeed = MAX( climbSpeed, 30.0 );
+ }
+ else
+ {
+ // FIXME: assume if they don't have a dismount animation then they probably don't really support climbing.
+ climbSpeed = 100.0;
+ }
+
+ SetSmoothedVelocity( climbDir * climbSpeed );
+
+ if ( climbDist < climbSpeed * GetMoveInterval() )
+ {
+ if (climbDist <= 1e-2)
+ climbDist = 0;
+
+ const float climbTime = climbDist / climbSpeed;
+
+ SetMoveInterval( GetMoveInterval() - climbTime );
+ SetLocalOrigin( climbDest );
+
+ return AIMR_CHANGE_TYPE;
+ }
+ else
+ {
+ SetMoveInterval( 0 );
+ }
+
+ // --------------------------------------------
+ // Turn to face the climb
+ // --------------------------------------------
+ SetIdealYawAndUpdate( yaw );
+
+ return AIMR_OK;
+}
+
+void CAI_Motor::MoveClimbStop()
+{
+ if ( GetNavigator()->GetMovementActivity() > ACT_RESET )
+ SetActivity( GetNavigator()->GetMovementActivity() );
+ else
+ SetActivity( ACT_IDLE );
+
+ GetOuter()->RemoveFlag( FL_FLY );
+ SetSmoothedVelocity( vec3_origin );
+ SetGravity( 1.0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Motion for jumping
+// Input :
+// Output : Returns bits (MoveStatus_b) regarding the move status
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::MoveJumpStart( const Vector &velocity )
+{
+ // take the npc off the ground and throw them in the air
+ SetSmoothedVelocity( velocity );
+ SetGravity( GetOuter()->GetJumpGravity() );
+ SetGroundEntity( NULL );
+
+ SetActivity( ACT_JUMP );
+
+ SetIdealYawAndUpdate( velocity );
+}
+
+int CAI_Motor::MoveJumpExecute( )
+{
+ // needs to detect being hit
+ UpdateYaw( );
+
+ if (GetOuter()->GetActivity() == ACT_JUMP && GetOuter()->IsActivityFinished())
+ {
+ SetActivity( ACT_GLIDE );
+ }
+
+ // use all the time
+ SetMoveInterval( 0 );
+
+ return AIMR_OK;
+}
+
+AIMoveResult_t CAI_Motor::MoveJumpStop()
+{
+ SetSmoothedVelocity( Vector(0,0,0) );
+
+ if (GetOuter()->GetActivity() == ACT_GLIDE)
+ {
+ float flTime = GetOuter()->GetGroundChangeTime();
+ GetOuter()->AddStepDiscontinuity( flTime, GetAbsOrigin(), GetAbsAngles() );
+
+ if ( SelectWeightedSequence( ACT_LAND ) == ACT_INVALID )
+ return AIMR_CHANGE_TYPE;
+
+ SetActivity( ACT_LAND );
+ // FIXME: find out why the client doesn't interpolate immediatly after sequence change
+ // GetOuter()->SetCycle( flTime - gpGlobals->curtime );
+ }
+ if (GetOuter()->GetActivity() != ACT_LAND || GetOuter()->IsActivityFinished())
+ {
+ return AIMR_CHANGE_TYPE;
+ }
+
+ SetMoveInterval( 0 );
+
+ SetGravity( 1.0f );
+
+ return AIMR_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+float CAI_Motor::GetIdealSpeed() const
+{
+ return GetOuter()->GetIdealSpeed();
+}
+
+
+float CAI_Motor::GetIdealAccel() const
+{
+ return GetOuter()->GetIdealAccel();
+}
+
+//-----------------------------------------------------------------------------
+
+// how far will I go?
+float CAI_Motor::MinStoppingDist( float flMinResult )
+{
+ // FIXME: should this be a constant rate or a constant time like it is now?
+ float flDecelRate = GetIdealAccel();
+
+ if (flDecelRate > 0.0)
+ {
+ // assuming linear deceleration, how long till my V hits 0?
+ float t = GetCurSpeed() / flDecelRate;
+ // and how far will I travel? (V * t - 1/2 A t^2)
+ float flDist = GetCurSpeed() * t - 0.5 * flDecelRate * t * t;
+
+ // this should always be some reasonable non-zero distance
+ if (flDist > flMinResult)
+ return flDist;
+ return flMinResult;
+ }
+ return flMinResult;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: how fast should I be going ideally
+//-----------------------------------------------------------------------------
+float CAI_Motor::IdealVelocity( void )
+{
+ // FIXME: this should be a per-entity setting so run speeds are not based on animation speeds
+ return GetIdealSpeed() * GetPlaybackRate();
+}
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::ResetMoveCalculations()
+{
+}
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::MoveStart()
+{
+}
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::MoveStop()
+{
+ memset( &m_vecVelocity, 0, sizeof(m_vecVelocity) );
+ GetOuter()->GetLocalNavigator()->ResetMoveCalculations();
+}
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::MovePaused()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: what linear accel/decel rate do I need to hit V1 in d distance?
+//-----------------------------------------------------------------------------
+float DeltaV( float v0, float v1, float d )
+{
+ return 0.5 * (v1 * v1 - v0 * v0 ) / d;
+}
+
+
+//-----------------------------------------------------------------------------
+float CAI_Motor::CalcIntervalMove()
+{
+ // assuming linear acceleration, how far will I travel?
+ return 0.5 * (GetCurSpeed() + GetIdealSpeed()) * GetMoveInterval();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the npc to the next location on its route.
+// Input : vecDir - Normalized vector indicating the direction of movement.
+// flDistance - distance to move
+// flInterval - Time interval for this movement.
+// flGoalDistance - target distance
+// flGoalVelocity - target velocity
+//-----------------------------------------------------------------------------
+
+AIMotorMoveResult_t CAI_Motor::MoveGroundExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult )
+{
+ // --------------------------------------------
+ // turn in the direction of movement
+ // --------------------------------------------
+ MoveFacing( move );
+
+ // --------------------------------------------
+ return MoveGroundExecuteWalk( move, GetIdealSpeed(), CalcIntervalMove(), pTraceResult );
+}
+
+
+AIMotorMoveResult_t CAI_Motor::MoveGroundExecuteWalk( const AILocalMoveGoal_t &move, float speed, float dist, AIMoveTrace_t *pTraceResult )
+{
+ bool bReachingLocalGoal = ( dist > move.maxDist );
+
+ // can I move farther in this interval than I'm supposed to?
+ if ( bReachingLocalGoal )
+ {
+ if ( !(move.flags & AILMG_CONSUME_INTERVAL) )
+ {
+ // only use a portion of the time interval
+ SetMoveInterval( GetMoveInterval() * (1 - move.maxDist / dist) );
+ }
+ else
+ SetMoveInterval( 0 );
+ dist = move.maxDist;
+ }
+ else
+ {
+ // use all the time
+ SetMoveInterval( 0 );
+ }
+
+ SetMoveVel( move.dir * speed );
+
+ // --------------------------------------------
+ // walk the distance
+ // --------------------------------------------
+ AIMotorMoveResult_t result = AIM_SUCCESS;
+ if ( dist > 0.0 )
+ {
+ Vector vecFrom = GetLocalOrigin();
+ Vector vecTo = vecFrom + move.dir * dist;
+
+ result = MoveGroundStep( vecTo, move.pMoveTarget, -1, true, bReachingLocalGoal, pTraceResult );
+
+ if ( result == AIM_FAILED )
+ MoveStop();
+ }
+ else if ( !OnMoveStalled( move ) )
+ {
+ result = AIM_FAILED;
+ }
+
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the npc to the next location on its route.
+// Input : pTargetEnt -
+// vecDir - Normalized vector indicating the direction of movement.
+// flInterval - Time interval for this movement.
+//-----------------------------------------------------------------------------
+
+AIMotorMoveResult_t CAI_Motor::MoveFlyExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult )
+{
+ // turn in the direction of movement
+ MoveFacing( move );
+
+ // calc accel/decel rates
+ float flNewSpeed = GetIdealSpeed();
+ SetMoveVel( move.dir * flNewSpeed );
+
+ float flTotal = 0.5 * (GetCurSpeed() + flNewSpeed) * GetMoveInterval();
+
+ float distance = move.maxDist;
+
+ // can I move farther in this interval than I'm supposed to?
+ if (flTotal > distance)
+ {
+ // only use a portion of the time interval
+ SetMoveInterval( GetMoveInterval() * (1 - distance / flTotal) );
+ flTotal = distance;
+ }
+ else
+ {
+ // use all the time
+ SetMoveInterval( 0 );
+ }
+
+ Vector vecStart, vecEnd;
+ vecStart = GetLocalOrigin();
+ VectorMA( vecStart, flTotal, move.dir, vecEnd );
+
+ AIMoveTrace_t moveTrace;
+ GetMoveProbe()->MoveLimit( NAV_FLY, vecStart, vecEnd, MASK_NPCSOLID, NULL, &moveTrace );
+ if ( pTraceResult )
+ *pTraceResult = moveTrace;
+
+ // Check for total blockage
+ if (fabs(moveTrace.flDistObstructed - flTotal) <= 1e-1)
+ {
+ // But if we bumped into our target, then we succeeded!
+ if ( move.pMoveTarget && (moveTrace.pObstruction == move.pMoveTarget) )
+ return AIM_PARTIAL_HIT_TARGET;
+
+ return AIM_FAILED;
+ }
+
+ // The true argument here causes it to touch all triggers
+ // in the volume swept from the previous position to the current position
+ UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true);
+
+ return (IsMoveBlocked(moveTrace.fStatus)) ? AIM_PARTIAL_HIT_WORLD : AIM_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: turn in the direction of movement
+// Output :
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::MoveFacing( const AILocalMoveGoal_t &move )
+{
+ if ( GetOuter()->OverrideMoveFacing( move, GetMoveInterval() ) )
+ return;
+
+ // required movement direction
+ float flMoveYaw = UTIL_VecToYaw( move.dir );
+
+ int nSequence = GetSequence();
+ float fSequenceMoveYaw = GetSequenceMoveYaw( nSequence );
+ if ( fSequenceMoveYaw == NOMOTION )
+ {
+ fSequenceMoveYaw = 0;
+ }
+
+ if (!HasPoseParameter( nSequence, GetOuter()->LookupPoseMoveYaw() ))
+ {
+ SetIdealYawAndUpdate( UTIL_AngleMod( flMoveYaw - fSequenceMoveYaw ) );
+ }
+ else
+ {
+ // FIXME: move this up to navigator so that path goals can ignore these overrides.
+ Vector dir;
+ float flInfluence = GetFacingDirection( dir );
+ dir = move.facing * (1 - flInfluence) + dir * flInfluence;
+ VectorNormalize( dir );
+
+ // ideal facing direction
+ float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) );
+
+ // FIXME: facing has important max velocity issues
+ SetIdealYawAndUpdate( idealYaw );
+
+ // find movement direction to compensate for not being turned far enough
+ float flDiff = UTIL_AngleDiff( flMoveYaw, GetLocalAngles().y );
+ SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), flDiff );
+ /*
+ if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
+ {
+ DevMsg( "move %.1f : diff %.1f : ideal %.1f\n", flMoveYaw, flDiff, m_IdealYaw );
+ }
+ */
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the ideal yaw and run the current or specified timestep
+// worth of rotation.
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::SetIdealYawAndUpdate( float idealYaw, float yawSpeed)
+{
+ SetIdealYaw( idealYaw );
+ if (yawSpeed == AI_CALC_YAW_SPEED)
+ RecalculateYawSpeed();
+ else if (yawSpeed != AI_KEEP_YAW_SPEED)
+ SetYawSpeed( yawSpeed );
+ UpdateYaw(-1);
+}
+
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::RecalculateYawSpeed()
+{
+ SetYawSpeed( CalcYawSpeed() );
+}
+
+//-----------------------------------------------------------------------------
+
+float AI_ClampYaw( float yawSpeedPerSec, float current, float target, float time )
+{
+ if (current != target)
+ {
+ float speed = yawSpeedPerSec * time;
+ float move = target - current;
+
+ if (target > current)
+ {
+ if (move >= 180)
+ move = move - 360;
+ }
+ else
+ {
+ if (move <= -180)
+ move = move + 360;
+ }
+
+ if (move > 0)
+ {// turning to the npc's left
+ if (move > speed)
+ move = speed;
+ }
+ else
+ {// turning to the npc's right
+ if (move < -speed)
+ move = -speed;
+ }
+
+ return UTIL_AngleMod(current + move);
+ }
+
+ return target;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turns a npc towards its ideal yaw.
+// Input : yawSpeed - Yaw speed in degrees per 1/10th of a second.
+// flInterval - Time interval to turn, -1 uses time since last think.
+// Output : Returns the number of degrees turned.
+//-----------------------------------------------------------------------------
+void CAI_Motor::UpdateYaw( int yawSpeed )
+{
+ // Don't do this if our yaw is locked
+ if ( IsYawLocked() )
+ return;
+
+ GetOuter()->SetUpdatedYaw();
+
+ float ideal, current, newYaw;
+
+ if ( yawSpeed == -1 )
+ yawSpeed = GetYawSpeed();
+
+ // NOTE: GetIdealYaw() will never exactly be reached because UTIL_AngleMod
+ // also truncates the angle to 16 bits of resolution. So lets truncate it here.
+ current = UTIL_AngleMod( GetLocalAngles().y );
+ ideal = UTIL_AngleMod( GetIdealYaw() );
+
+ // FIXME: this needs a proper interval
+ float dt = MIN( 0.2, gpGlobals->curtime - GetLastThink() );
+
+ newYaw = AI_ClampYaw( (float)yawSpeed * 10.0, current, ideal, dt );
+
+ if (newYaw != current)
+ {
+ QAngle angles = GetLocalAngles();
+ angles.y = newYaw;
+ SetLocalAngles( angles );
+ }
+}
+
+
+//=========================================================
+// DeltaIdealYaw - returns the difference ( in degrees ) between
+// npc's current yaw and ideal_yaw
+//
+// Positive result is left turn, negative is right turn
+//=========================================================
+float CAI_Motor::DeltaIdealYaw ( void )
+{
+ float flCurrentYaw;
+
+ flCurrentYaw = UTIL_AngleMod( GetLocalAngles().y );
+
+ if ( flCurrentYaw == GetIdealYaw() )
+ {
+ return 0;
+ }
+
+
+ return UTIL_AngleDiff( GetIdealYaw(), flCurrentYaw );
+}
+
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::SetIdealYawToTarget( const Vector &target, float noise, float offset )
+{
+ float base = CalcIdealYaw( target );
+ base += offset;
+ if ( noise > 0 )
+ {
+ noise *= 0.5;
+ base += random->RandomFloat( -noise, noise );
+ if ( base < 0 )
+ base += 360;
+ else if ( base >= 360 )
+ base -= 360;
+ }
+ SetIdealYaw( base );
+}
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::SetIdealYawToTargetAndUpdate( const Vector &target, float yawSpeed )
+{
+ SetIdealYawAndUpdate( CalcIdealYaw( target ), yawSpeed );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Keep track of multiple objects that the npc is interested in facing
+//-----------------------------------------------------------------------------
+void CAI_Motor::AddFacingTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp )
+{
+ m_facingQueue.Add( pTarget, flImportance, flDuration, flRamp );
+}
+
+
+void CAI_Motor::AddFacingTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
+{
+ m_facingQueue.Add( vecPosition, flImportance, flDuration, flRamp );
+}
+
+void CAI_Motor::AddFacingTarget( CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
+{
+ m_facingQueue.Add( pTarget, vecPosition, flImportance, flDuration, flRamp );
+}
+
+
+float CAI_Motor::GetFacingDirection( Vector &vecDir )
+{
+ float flTotalInterest = 0.0;
+ vecDir = Vector( 0, 0, 0 );
+
+ int i;
+
+ // clean up facing targets
+ for (i = 0; i < m_facingQueue.Count();)
+ {
+ if (!m_facingQueue[i].IsActive())
+ {
+ m_facingQueue.Remove( i );
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ for (i = 0; i < m_facingQueue.Count(); i++)
+ {
+ float flInterest = m_facingQueue[i].Interest( );
+ Vector tmp = m_facingQueue[i].GetPosition() - GetAbsOrigin();
+
+ // NDebugOverlay::Line( m_facingQueue[i].GetPosition(), GetAbsOrigin(), 255, 0, 0, false, 0.1 );
+
+ VectorNormalize( tmp );
+
+ vecDir = vecDir * (1 - flInterest) + tmp * flInterest;
+
+ flTotalInterest = (1 - (1 - flTotalInterest) * (1 - flInterest));
+
+ VectorNormalize( vecDir );
+ }
+
+ return flTotalInterest;
+}
+
+
+//-----------------------------------------------------------------------------
+
+AIMoveResult_t CAI_Motor::MoveNormalExecute( const AILocalMoveGoal_t &move )
+{
+ AI_PROFILE_SCOPE(CAI_Motor_MoveNormalExecute);
+
+ // --------------------------------
+
+ AIMotorMoveResult_t fMotorResult;
+ AIMoveTrace_t moveTrace;
+
+ if ( move.navType == NAV_GROUND )
+ {
+ fMotorResult = MoveGroundExecute( move, &moveTrace );
+ }
+ else
+ {
+ Assert( move.navType == NAV_FLY );
+ fMotorResult = MoveFlyExecute( move, &moveTrace );
+ }
+
+ static AIMoveResult_t moveResults[] =
+ {
+ AIMR_ILLEGAL, // AIM_FAILED
+ AIMR_OK, // AIM_SUCCESS
+ AIMR_BLOCKED_NPC, // AIM_PARTIAL_HIT_NPC
+ AIMR_BLOCKED_WORLD, // AIM_PARTIAL_HIT_WORLD
+ AIMR_BLOCKED_WORLD, // AIM_PARTIAL_HIT_TARGET
+ };
+ Assert( ARRAYSIZE( moveResults ) == AIM_NUM_RESULTS && fMotorResult >= 0 && fMotorResult <= ARRAYSIZE( moveResults ) );
+
+ AIMoveResult_t result = moveResults[fMotorResult];
+
+ if ( result != AIMR_OK )
+ {
+ OnMoveExecuteFailed( move, moveTrace, fMotorResult, &result );
+ SetMoveInterval( 0 ); // always consume interval on failure, even if overridden by OnMoveExecuteFailed()
+ }
+
+ return DbgResult( result );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Look ahead my stopping distance, or at least my hull width
+//-----------------------------------------------------------------------------
+float CAI_Motor::MinCheckDist( void )
+{
+ // Take the groundspeed into account
+ float flMoveDist = GetMoveInterval() * GetIdealSpeed();
+ float flMinDist = MAX( MinStoppingDist(), flMoveDist);
+ if ( flMinDist < GetHullWidth() )
+ flMinDist = GetHullWidth();
+ return flMinDist;
+}
+
+//-----------------------------------------------------------------------------
+
+CAI_Navigator *CAI_Motor::GetNavigator( void )
+{
+ return GetOuter()->GetNavigator();
+}
+
+int CAI_Motor::SelectWeightedSequence ( Activity activity )
+{
+ return GetOuter()->SelectWeightedSequence ( activity );
+}
+
+float CAI_Motor::GetSequenceGroundSpeed( int iSequence )
+{
+ return GetOuter()->GetSequenceGroundSpeed( iSequence );
+}
+
+
+//-----------------------------------------------------------------------------
+
+void CAI_Motor::SetSmoothedVelocity(const Vector &vecVelocity)
+{
+ GetOuter()->SetAbsVelocity(vecVelocity);
+}
+
+Vector CAI_Motor::GetSmoothedVelocity()
+{
+ return GetOuter()->GetSmoothedVelocity();
+}
+
+float CAI_Motor::StepHeight() const
+{
+ return GetOuter()->StepHeight();
+}
+
+bool CAI_Motor::CanStandOn( CBaseEntity *pSurface ) const
+{
+ return GetOuter()->CanStandOn( pSurface );
+}
+
+float CAI_Motor::CalcIdealYaw( const Vector &vecTarget )
+{
+ return GetOuter()->CalcIdealYaw( vecTarget );
+}
+
+float CAI_Motor::SetBoneController( int iController, float flValue )
+{
+ return GetOuter()->SetBoneController( iController, flValue );
+}
+
+float CAI_Motor::GetSequenceMoveYaw( int iSequence )
+{
+ return GetOuter()->GetSequenceMoveYaw( iSequence );
+}
+
+void CAI_Motor::SetPlaybackRate( float flRate )
+{
+ return GetOuter()->SetPlaybackRate( flRate );
+}
+
+float CAI_Motor::GetPlaybackRate()
+{
+ return GetOuter()->GetPlaybackRate();
+}
+
+float CAI_Motor::SetPoseParameter( const char *szName, float flValue )
+{
+ return GetOuter()->SetPoseParameter( szName, flValue );
+}
+
+float CAI_Motor::GetPoseParameter( const char *szName )
+{
+ return GetOuter()->GetPoseParameter( szName );
+}
+
+bool CAI_Motor::HasPoseParameter( int iSequence, const char *szName )
+{
+ return GetOuter()->HasPoseParameter( iSequence, szName );
+}
+
+float CAI_Motor::SetPoseParameter( int iParameter, float flValue )
+{
+ return GetOuter()->SetPoseParameter( iParameter, flValue );
+}
+
+bool CAI_Motor::HasPoseParameter( int iSequence, int iParameter )
+{
+ return GetOuter()->HasPoseParameter( iSequence, iParameter );
+}
+
+void CAI_Motor::SetMoveType( MoveType_t val, MoveCollide_t moveCollide )
+{
+ GetOuter()->SetMoveType( val, moveCollide );
+}
+
+//=============================================================================
+