|
|
//========= Copyright � 1996-2005, 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;
char *pchHackBoolToInt = (char*)(&bTestZ); if ( *pchHackBoolToInt == 2 ) { testFlags |= AITGM_CRAWL_LARGE_STEPS; } else { if ( !bTestZ ) testFlags |= AITGM_2D; }
#ifdef DEBUG
if ( ai_draw_motor_movement.GetBool() ) testFlags |= AITGM_DRAW_RESULTS; #endif
GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, GetOuter()->GetAITraceMask(), 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(), GetOuter()->GetAITraceMask() ) && !GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, GetOuter()->GetAITraceMask() ) ) { 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, 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;
if ( move.navType == NAV_CRAWL ) { char *pchHackBoolToInt = (char*)(&bReachingLocalGoal); *pchHackBoolToInt = 2; } 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, GetOuter()->GetAITraceMask(), 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 || move.navType == NAV_CRAWL ) { 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() const { 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 ); }
//=============================================================================
|