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/ai_motor.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/ai_motor.cpp')
| -rw-r--r-- | mp/src/game/server/ai_motor.cpp | 2066 |
1 files changed, 1033 insertions, 1033 deletions
diff --git a/mp/src/game/server/ai_motor.cpp b/mp/src/game/server/ai_motor.cpp index 4c1e03b1..49f3d5f4 100644 --- a/mp/src/game/server/ai_motor.cpp +++ b/mp/src/game/server/ai_motor.cpp @@ -1,1033 +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 );
-}
-
-//=============================================================================
-
+//========= 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 ); +} + +//============================================================================= + |