You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1442 lines
38 KiB
1442 lines
38 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// NextBotGroundLocomotion.cpp |
|
// Basic ground-based movement for NextBotCombatCharacters |
|
// Author: Michael Booth, February 2009 |
|
// Note: This is a refactoring of ZombieBotLocomotion from L4D |
|
|
|
#include "cbase.h" |
|
|
|
#include "func_break.h" |
|
#include "func_breakablesurf.h" |
|
#include "activitylist.h" |
|
#include "BasePropDoor.h" |
|
|
|
#include "nav.h" |
|
#include "NextBot.h" |
|
#include "NextBotGroundLocomotion.h" |
|
#include "NextBotUtil.h" |
|
#include "functorutils.h" |
|
#include "SharedFunctorUtils.h" |
|
|
|
#include "tier0/vprof.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#pragma warning( disable : 4355 ) // warning 'this' used in base member initializer list - we're using it safely |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
NextBotGroundLocomotion::NextBotGroundLocomotion( INextBot *bot ) : ILocomotion( bot ) |
|
{ |
|
m_nextBot = NULL; |
|
m_ladder = NULL; |
|
m_desiredLean.x = 0.0f; |
|
m_desiredLean.y = 0.0f; |
|
m_desiredLean.z = 0.0f; |
|
|
|
m_bRecomputePostureOnCollision = false; |
|
m_ignorePhysicsPropTimer.Invalidate(); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
NextBotGroundLocomotion::~NextBotGroundLocomotion() |
|
{ |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Reset locomotor to initial state |
|
*/ |
|
void NextBotGroundLocomotion::Reset( void ) |
|
{ |
|
BaseClass::Reset(); |
|
m_bRecomputePostureOnCollision = false; |
|
m_ignorePhysicsPropTimer.Invalidate(); |
|
|
|
m_nextBot = static_cast< NextBotCombatCharacter * >( GetBot()->GetEntity() ); |
|
|
|
m_desiredSpeed = 0.0f; |
|
m_velocity = vec3_origin; |
|
m_acceleration = vec3_origin; |
|
|
|
m_desiredLean.x = 0.0f; |
|
m_desiredLean.y = 0.0f; |
|
m_desiredLean.z = 0.0f; |
|
|
|
m_ladder = NULL; |
|
|
|
m_isJumping = false; |
|
m_isJumpingAcrossGap = false; |
|
m_ground = NULL; |
|
m_groundNormal = Vector( 0, 0, 1.0f ); |
|
m_isClimbingUpToLedge = false; |
|
m_isUsingFullFeetTrace = false; |
|
|
|
m_moveVector = Vector( 1, 0, 0 ); |
|
|
|
m_priorPos = m_nextBot->GetPosition(); |
|
m_lastValidPos = m_nextBot->GetPosition(); |
|
|
|
m_inhibitObstacleAvoidanceTimer.Invalidate(); |
|
|
|
m_accumApproachVectors = vec3_origin; |
|
m_accumApproachWeights = 0.0f; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Move the bot along a ladder |
|
*/ |
|
bool NextBotGroundLocomotion::TraverseLadder( void ) |
|
{ |
|
// not climbing a ladder right now |
|
return false; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Update internal state |
|
*/ |
|
void NextBotGroundLocomotion::Update( void ) |
|
{ |
|
VPROF_BUDGET( "NextBotGroundLocomotion::Update", "NextBot" ); |
|
|
|
BaseClass::Update(); |
|
|
|
const float deltaT = GetUpdateInterval(); |
|
|
|
// apply accumulated position changes |
|
ApplyAccumulatedApproach(); |
|
|
|
// need to do this first thing, because ground constraints, etc, can change it |
|
Vector origPos = GetFeet(); |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
|
|
if ( TraverseLadder() ) |
|
{ |
|
// bot is climbing a ladder |
|
return; |
|
} |
|
|
|
if ( !body->IsPostureMobile() ) |
|
{ |
|
// sitting/lying on the ground - no slip |
|
m_acceleration.x = 0.0f; |
|
m_acceleration.y = 0.0f; |
|
m_velocity.x = 0.0f; |
|
m_velocity.y = 0.0f; |
|
} |
|
|
|
bool wasOnGround = IsOnGround(); |
|
|
|
if ( !body->HasActivityType( IBody::MOTION_CONTROLLED_Z ) ) |
|
{ |
|
// fall if in the air |
|
if ( !IsOnGround() ) |
|
{ |
|
// no ground below us - fall |
|
m_acceleration.z -= GetGravity(); |
|
} |
|
|
|
if ( !IsClimbingOrJumping() || m_velocity.z <= 0.0f ) |
|
{ |
|
// keep us on the ground |
|
UpdateGroundConstraint(); |
|
} |
|
} |
|
|
|
Vector newPos = GetFeet(); |
|
|
|
// |
|
// Update position physics |
|
// |
|
Vector right( m_moveVector.y, -m_moveVector.x, 0.0f ); |
|
|
|
if ( IsOnGround() ) // || m_isClimbingUpToLedge ) |
|
{ |
|
if ( IsAttemptingToMove() ) |
|
{ |
|
float forwardSpeed = DotProduct( m_velocity, m_moveVector ); |
|
Vector forwardVelocity = forwardSpeed * m_moveVector; |
|
Vector sideVelocity = DotProduct( m_velocity, right ) * right; |
|
|
|
Vector frictionAccel = vec3_origin; |
|
|
|
// only apply friction along forward direction if we are sliding backwards |
|
if ( forwardSpeed < 0.0f ) |
|
{ |
|
frictionAccel = -GetFrictionForward() * forwardVelocity; |
|
} |
|
|
|
// always apply lateral friction to counteract sideslip |
|
frictionAccel += -GetFrictionSideways() * sideVelocity; |
|
|
|
m_acceleration.x += frictionAccel.x; |
|
m_acceleration.y += frictionAccel.y; |
|
} |
|
else |
|
{ |
|
// come to a stop if we haven't been told to move |
|
m_acceleration = vec3_origin; |
|
m_velocity = vec3_origin; |
|
} |
|
} |
|
|
|
// compute new position, taking into account MOTION_CONTROLLED animations in progress |
|
if ( body->HasActivityType( IBody::MOTION_CONTROLLED_XY ) ) |
|
{ |
|
m_acceleration.x = 0.0f; |
|
m_acceleration.y = 0.0f; |
|
m_velocity.x = GetBot()->GetEntity()->GetAbsVelocity().x; |
|
m_velocity.y = GetBot()->GetEntity()->GetAbsVelocity().y; |
|
} |
|
else |
|
{ |
|
// euler integration |
|
m_velocity.x += m_acceleration.x * deltaT; |
|
m_velocity.y += m_acceleration.y * deltaT; |
|
|
|
// euler integration |
|
newPos.x += m_velocity.x * deltaT; |
|
newPos.y += m_velocity.y * deltaT; |
|
} |
|
|
|
if ( body->HasActivityType( IBody::MOTION_CONTROLLED_Z ) ) |
|
{ |
|
m_acceleration.z = 0.0f; |
|
m_velocity.z = GetBot()->GetEntity()->GetAbsVelocity().z; |
|
} |
|
else |
|
{ |
|
// euler integration |
|
m_velocity.z += m_acceleration.z * deltaT; |
|
|
|
// euler integration |
|
newPos.z += m_velocity.z * deltaT; |
|
} |
|
|
|
// move bot to new position, resolving collisions along the way |
|
UpdatePosition( newPos ); |
|
|
|
|
|
// set actual velocity based on position change after collision resolution step |
|
Vector adjustedVelocity = ( GetFeet() - origPos ) / deltaT; |
|
|
|
if ( !body->HasActivityType( IBody::MOTION_CONTROLLED_XY ) ) |
|
{ |
|
m_velocity.x = adjustedVelocity.x; |
|
m_velocity.y = adjustedVelocity.y; |
|
} |
|
|
|
if ( !body->HasActivityType( IBody::MOTION_CONTROLLED_Z ) ) |
|
{ |
|
m_velocity.z = adjustedVelocity.z; |
|
} |
|
|
|
|
|
// collision resolution may create very high instantaneous velocities, limit it |
|
Vector2D groundVel = m_velocity.AsVector2D(); |
|
m_actualSpeed = groundVel.NormalizeInPlace(); |
|
|
|
if ( IsOnGround() ) |
|
{ |
|
if ( m_actualSpeed > GetRunSpeed() ) |
|
{ |
|
m_actualSpeed = GetRunSpeed(); |
|
m_velocity.x = m_actualSpeed * groundVel.x; |
|
m_velocity.y = m_actualSpeed * groundVel.y; |
|
} |
|
|
|
// remove downward velocity when landing on the ground |
|
if ( !wasOnGround ) |
|
{ |
|
m_velocity.z = 0.0f; |
|
m_acceleration.z = 0.0f; |
|
} |
|
} |
|
else |
|
{ |
|
// we're falling. if our velocity has become zero for any reason, shove it forward |
|
const float epsilon = 1.0f; |
|
if ( m_velocity.IsLengthLessThan( epsilon ) ) |
|
{ |
|
m_velocity = GetRunSpeed() * GetGroundMotionVector(); |
|
} |
|
} |
|
|
|
// update entity velocity to that of locomotor |
|
m_nextBot->SetAbsVelocity( m_velocity ); |
|
|
|
|
|
#ifdef LEANING |
|
// lean sideways proportional to lateral acceleration |
|
QAngle lean = GetDesiredLean(); |
|
|
|
float sideAccel = DotProduct( right, m_acceleration ); |
|
float slide = sideAccel / GetMaxAcceleration(); |
|
|
|
// max lean depends on how fast we're actually moving |
|
float maxLeanAngle = NextBotLeanMaxAngle.GetFloat() * m_actualSpeed / GetRunSpeed(); |
|
|
|
// actual lean angle is proportional to lateral acceleration (sliding) |
|
float desiredSideLean = -maxLeanAngle * slide; |
|
|
|
lean.y += ( desiredSideLean - lean.y ) * NextBotLeanRate.GetFloat() * deltaT; |
|
|
|
SetDesiredLean( lean ); |
|
#endif // _DEBUG |
|
|
|
|
|
// reset acceleration accumulation |
|
m_acceleration = vec3_origin; |
|
|
|
// debug display |
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
// track position over time |
|
if ( IsOnGround() ) |
|
{ |
|
NDebugOverlay::Cross3D( GetFeet(), 1.0f, 0, 255, 0, true, 15.0f ); |
|
} |
|
else |
|
{ |
|
NDebugOverlay::Cross3D( GetFeet(), 1.0f, 0, 255, 255, true, 15.0f ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Move directly towards given position. |
|
* We need to do this in-air as well to land jumps. |
|
*/ |
|
void NextBotGroundLocomotion::Approach( const Vector &rawPos, float goalWeight ) |
|
{ |
|
BaseClass::Approach( rawPos ); |
|
|
|
m_accumApproachVectors += ( rawPos - GetFeet() ) * goalWeight; |
|
m_accumApproachWeights += goalWeight; |
|
m_bRecomputePostureOnCollision = true; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
void NextBotGroundLocomotion::ApplyAccumulatedApproach( void ) |
|
{ |
|
VPROF_BUDGET( "NextBotGroundLocomotion::ApplyAccumulatedApproach", "NextBot" ); |
|
|
|
Vector rawPos = GetFeet(); |
|
|
|
const float deltaT = GetUpdateInterval(); |
|
|
|
if ( deltaT <= 0.0f ) |
|
return; |
|
|
|
if ( m_accumApproachWeights > 0.0f ) |
|
{ |
|
Vector approachDelta = m_accumApproachVectors / m_accumApproachWeights; |
|
|
|
// limit total movement to our max speed |
|
float maxMove = GetRunSpeed() * deltaT; |
|
|
|
float desiredMove = approachDelta.NormalizeInPlace(); |
|
if ( desiredMove > maxMove ) |
|
{ |
|
desiredMove = maxMove; |
|
} |
|
|
|
rawPos += desiredMove * approachDelta; |
|
|
|
m_accumApproachVectors = vec3_origin; |
|
m_accumApproachWeights = 0.0f; |
|
} |
|
|
|
// can only move in 2D - geometry moves us up and down |
|
Vector pos( rawPos.x, rawPos.y, GetFeet().z ); |
|
|
|
if ( !GetBot()->GetBodyInterface()->IsPostureMobile() ) |
|
{ |
|
// body is not in a movable state right now |
|
return; |
|
} |
|
|
|
Vector currentPos = m_nextBot->GetPosition(); |
|
|
|
// compute unit vector to goal position |
|
m_moveVector = pos - currentPos; |
|
m_moveVector.z = 0.0f; |
|
float change = m_moveVector.NormalizeInPlace(); |
|
|
|
const float epsilon = 0.001f; |
|
if ( change < epsilon ) |
|
{ |
|
// no motion |
|
m_forwardLean = 0.0f; |
|
m_sideLean = 0.0f; |
|
return; |
|
} |
|
|
|
/* |
|
// lean forward/backward based on acceleration |
|
float desiredLean = m_acceleration / NextBotLeanForwardAccel.GetFloat(); |
|
|
|
QAngle lean = GetDesiredLean(); |
|
|
|
lean.x = NextBotLeanMaxAngle.GetFloat() * clamp( desiredLean, -1.0f, 1.0f ); |
|
|
|
SetDesiredLean( lean ); |
|
*/ |
|
|
|
Vector newPos; |
|
|
|
// if we just started a jump, don't snap to the ground - let us get in the air first |
|
if ( DidJustJump() || !IsOnGround() ) |
|
{ |
|
if ( false && m_isClimbingUpToLedge ) // causes bots to hang in air stuck against edges |
|
{ |
|
// drive towards the approach position in XY to help reach ledge |
|
m_moveVector = m_ledgeJumpGoalPos - currentPos; |
|
m_moveVector.z = 0.0f; |
|
m_moveVector.NormalizeInPlace(); |
|
|
|
m_acceleration += GetMaxAcceleration() * m_moveVector; |
|
} |
|
} |
|
else if ( IsOnGround() ) |
|
{ |
|
// on the ground - move towards the approach position |
|
m_isClimbingUpToLedge = false; |
|
|
|
// snap forward movement vector along floor |
|
const Vector &groundNormal = GetGroundNormal(); |
|
|
|
Vector left( -m_moveVector.y, m_moveVector.x, 0.0f ); |
|
m_moveVector = CrossProduct( left, groundNormal ); |
|
m_moveVector.NormalizeInPlace(); |
|
|
|
// limit maximum forward speed from self-acceleration |
|
float forwardSpeed = DotProduct( m_velocity, m_moveVector ); |
|
|
|
float maxSpeed = MIN( m_desiredSpeed, GetSpeedLimit() ); |
|
|
|
if ( forwardSpeed < maxSpeed ) |
|
{ |
|
float ratio = ( forwardSpeed <= 0.0f ) ? 0.0f : ( forwardSpeed / maxSpeed ); |
|
float governor = 1.0f - ( ratio * ratio * ratio * ratio ); |
|
|
|
// accelerate towards goal |
|
m_acceleration += governor * GetMaxAcceleration() * m_moveVector; |
|
} |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Move the bot to the precise given position immediately, |
|
*/ |
|
void NextBotGroundLocomotion::DriveTo( const Vector &pos ) |
|
{ |
|
BaseClass::DriveTo( pos ); |
|
m_bRecomputePostureOnCollision = true; |
|
UpdatePosition( pos ); |
|
} |
|
|
|
|
|
//-------------------------------------------------------------------------------------------- |
|
/* |
|
* Trace filter solely for use with DetectCollision() below. |
|
*/ |
|
class GroundLocomotionCollisionTraceFilter : public CTraceFilterSimple |
|
{ |
|
public: |
|
GroundLocomotionCollisionTraceFilter( INextBot *me, const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple( passentity, collisionGroup ) |
|
{ |
|
m_me = me; |
|
} |
|
|
|
virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) |
|
{ |
|
if ( CTraceFilterSimple::ShouldHitEntity( pServerEntity, contentsMask ) ) |
|
{ |
|
CBaseEntity *entity = EntityFromEntityHandle( pServerEntity ); |
|
|
|
// don't collide with ourself |
|
if ( entity && m_me->IsSelf( entity ) ) |
|
return false; |
|
|
|
return m_me->GetLocomotionInterface()->ShouldCollideWith( entity ); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
INextBot *m_me; |
|
}; |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Check for collisions during move and attempt to resolve them |
|
*/ |
|
bool NextBotGroundLocomotion::DetectCollision( trace_t *pTrace, int &recursionLimit, const Vector &from, const Vector &to, const Vector &vecMins, const Vector &vecMaxs ) |
|
{ |
|
IBody *body = GetBot()->GetBodyInterface(); |
|
|
|
CBaseEntity *ignore = m_ignorePhysicsPropTimer.IsElapsed() ? NULL : m_ignorePhysicsProp; |
|
GroundLocomotionCollisionTraceFilter filter( GetBot(), ignore, body->GetCollisionGroup() ); |
|
|
|
TraceHull( from, to, vecMins, vecMaxs, body->GetSolidMask(), &filter, pTrace ); |
|
|
|
if ( !pTrace->DidHit() ) |
|
return false; |
|
|
|
// |
|
// A collision occurred - resolve it |
|
// |
|
|
|
// bust through "flimsy" breakables and keep on going |
|
if ( pTrace->DidHitNonWorldEntity() && pTrace->m_pEnt != NULL ) |
|
{ |
|
CBaseEntity *other = pTrace->m_pEnt; |
|
|
|
if ( !other->MyCombatCharacterPointer() && IsEntityTraversable( other, IMMEDIATELY ) /*&& IsFlimsy( other )*/ ) |
|
{ |
|
if ( recursionLimit <= 0 ) |
|
return true; |
|
|
|
--recursionLimit; |
|
|
|
// break the weak breakable we collided with |
|
CTakeDamageInfo damageInfo( GetBot()->GetEntity(), GetBot()->GetEntity(), 100.0f, DMG_CRUSH ); |
|
CalculateExplosiveDamageForce( &damageInfo, GetMotionVector(), pTrace->endpos ); |
|
other->TakeDamage( damageInfo ); |
|
|
|
// retry trace now that the breakable is out of the way |
|
return DetectCollision( pTrace, recursionLimit, from, to, vecMins, vecMaxs ); |
|
} |
|
} |
|
|
|
/// @todo Only invoke OnContact() and Touch() once per collision pair |
|
// inform other components of collision |
|
if ( GetBot()->ShouldTouch( pTrace->m_pEnt ) ) |
|
{ |
|
GetBot()->OnContact( pTrace->m_pEnt, pTrace ); |
|
} |
|
|
|
INextBot *them = dynamic_cast< INextBot * >( pTrace->m_pEnt ); |
|
if ( them && them->ShouldTouch( m_nextBot ) ) |
|
{ |
|
/// @todo construct mirror of trace |
|
them->OnContact( m_nextBot ); |
|
} |
|
else |
|
{ |
|
pTrace->m_pEnt->Touch( GetBot()->GetEntity() ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
Vector NextBotGroundLocomotion::ResolveCollision( const Vector &from, const Vector &to, int recursionLimit ) |
|
{ |
|
VPROF_BUDGET( "NextBotGroundLocomotion::ResolveCollision", "NextBotExpensive" ); |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
if ( body == NULL || recursionLimit < 0 ) |
|
{ |
|
Assert( !m_bRecomputePostureOnCollision ); |
|
return to; |
|
} |
|
|
|
// Only bother to recompute posture if we're currently standing or crouching |
|
if ( m_bRecomputePostureOnCollision ) |
|
{ |
|
if ( !body->IsActualPosture( IBody::STAND ) && !body->IsActualPosture( IBody::CROUCH ) ) |
|
{ |
|
m_bRecomputePostureOnCollision = false; |
|
} |
|
} |
|
|
|
// get bounding limits, ignoring step-upable height |
|
bool bPerformCrouchTest = false; |
|
Vector mins; |
|
Vector maxs; |
|
if ( m_isUsingFullFeetTrace ) |
|
{ |
|
mins = body->GetHullMins(); |
|
} |
|
else |
|
{ |
|
mins = body->GetHullMins() + Vector( 0, 0, GetStepHeight() ); |
|
} |
|
if ( !m_bRecomputePostureOnCollision ) |
|
{ |
|
maxs = body->GetHullMaxs(); |
|
if ( mins.z >= maxs.z ) |
|
{ |
|
// if mins.z is greater than maxs.z, the engine will Assert |
|
// in UTIL_TraceHull, and it won't work as advertised. |
|
mins.z = maxs.z - 2.0f; |
|
} |
|
} |
|
else |
|
{ |
|
const float halfSize = body->GetHullWidth() / 2.0f; |
|
maxs.Init( halfSize, halfSize, body->GetStandHullHeight() ); |
|
bPerformCrouchTest = true; |
|
} |
|
|
|
trace_t trace; |
|
Vector desiredGoal = to; |
|
Vector resolvedGoal; |
|
IBody::PostureType nPosture = IBody::STAND; |
|
while( true ) |
|
{ |
|
bool bCollided = DetectCollision( &trace, recursionLimit, from, desiredGoal, mins, maxs ); |
|
if ( !bCollided ) |
|
{ |
|
resolvedGoal = desiredGoal; |
|
break; |
|
} |
|
|
|
// If we hit really close to our target, then stop |
|
if ( !trace.startsolid && desiredGoal.DistToSqr( trace.endpos ) < 1.0f ) |
|
{ |
|
resolvedGoal = trace.endpos; |
|
break; |
|
} |
|
|
|
// Check for crouch test, if it's necessary |
|
// Don't bother about checking for crouch if we hit an actor |
|
// Also don't bother checking for crouch if we hit a plane that pushes us upwards |
|
if ( bPerformCrouchTest ) |
|
{ |
|
// Don't do this work twice |
|
bPerformCrouchTest = false; |
|
|
|
nPosture = body->GetDesiredPosture(); |
|
|
|
if ( !trace.m_pEnt->MyNextBotPointer() && !trace.m_pEnt->IsPlayer() ) |
|
{ |
|
// Here, our standing trace hit the world or something non-breakable |
|
// If we're not currently crouching, then see if we could travel |
|
// the entire distance if we were crouched |
|
if ( nPosture != IBody::CROUCH ) |
|
{ |
|
trace_t crouchTrace; |
|
NextBotTraversableTraceFilter crouchFilter( GetBot(), ILocomotion::IMMEDIATELY ); |
|
Vector vecCrouchMax( maxs.x, maxs.y, body->GetCrouchHullHeight() ); |
|
TraceHull( from, desiredGoal, mins, vecCrouchMax, body->GetSolidMask(), &crouchFilter, &crouchTrace ); |
|
if ( crouchTrace.fraction >= 1.0f && !crouchTrace.startsolid ) |
|
{ |
|
nPosture = IBody::CROUCH; |
|
} |
|
} |
|
} |
|
else if ( nPosture == IBody::CROUCH ) |
|
{ |
|
// Here, our standing trace hit an actor |
|
|
|
// NOTE: This test occurs almost never, based on my tests |
|
// Converts from crouch to stand in the case where the player |
|
// is currently crouching, *and* his first trace (with the standing hull) |
|
// hits an actor *and* if he didn't hit that actor, he could have |
|
// moved standing the entire way to his desired endpoint |
|
trace_t standTrace; |
|
NextBotTraversableTraceFilter standFilter( GetBot(), ILocomotion::IMMEDIATELY ); |
|
TraceHull( from, desiredGoal, mins, maxs, body->GetSolidMask(), &standFilter, &standTrace ); |
|
if ( standTrace.fraction >= 1.0f && !standTrace.startsolid ) |
|
{ |
|
nPosture = IBody::STAND; |
|
} |
|
} |
|
|
|
// Our first trace was based on the standing hull. |
|
// If we need be crouched, the trace was bogus; we need to do another |
|
if ( nPosture == IBody::CROUCH ) |
|
{ |
|
maxs.z = body->GetCrouchHullHeight(); |
|
continue; |
|
} |
|
} |
|
|
|
if ( trace.startsolid ) |
|
{ |
|
// stuck inside solid; don't move |
|
|
|
if ( trace.m_pEnt && !trace.m_pEnt->IsWorld() ) |
|
{ |
|
// only ignore physics props that are not doors |
|
if ( dynamic_cast< CPhysicsProp * >( trace.m_pEnt ) != NULL && dynamic_cast< CBasePropDoor * >( trace.m_pEnt ) == NULL ) |
|
{ |
|
IPhysicsObject *physics = trace.m_pEnt->VPhysicsGetObject(); |
|
if ( physics && physics->IsMoveable() ) |
|
{ |
|
// we've intersected a (likely moving) physics prop - ignore it for awhile so we can move out of it |
|
m_ignorePhysicsProp = trace.m_pEnt; |
|
m_ignorePhysicsPropTimer.Start( 1.0f ); |
|
} |
|
} |
|
} |
|
|
|
// return to last known non-interpenetrating position |
|
resolvedGoal = m_lastValidPos; |
|
|
|
break; |
|
} |
|
|
|
if ( --recursionLimit <= 0 ) |
|
{ |
|
// reached recursion limit, no more adjusting allowed |
|
resolvedGoal = trace.endpos; |
|
break; |
|
} |
|
|
|
// never slide downwards/concave to avoid getting stuck in the ground |
|
if ( trace.plane.normal.z < 0.0f ) |
|
{ |
|
trace.plane.normal.z = 0.0f; |
|
trace.plane.normal.NormalizeInPlace(); |
|
} |
|
|
|
// slide off of surface we hit |
|
Vector fullMove = desiredGoal - from; |
|
Vector leftToMove = fullMove * ( 1.0f - trace.fraction ); |
|
|
|
// obey climbing slope limit |
|
if ( !body->HasActivityType( IBody::MOTION_CONTROLLED_Z ) && |
|
trace.plane.normal.z < GetTraversableSlopeLimit() && |
|
fullMove.z > 0.0f ) |
|
{ |
|
fullMove.z = 0.0f; |
|
trace.plane.normal.z = 0.0f; |
|
trace.plane.normal.NormalizeInPlace(); |
|
} |
|
|
|
float blocked = DotProduct( trace.plane.normal, leftToMove ); |
|
|
|
Vector unconstrained = fullMove - blocked * trace.plane.normal; |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::Line( trace.endpos, |
|
trace.endpos + 20.0f * trace.plane.normal, |
|
255, 0, 150, true, 15.0f ); |
|
} |
|
|
|
// check for collisions along remainder of move |
|
// But don't bother if we're not going to deflect much |
|
Vector remainingMove = from + unconstrained; |
|
if ( remainingMove.DistToSqr( trace.endpos ) < 1.0f ) |
|
{ |
|
resolvedGoal = trace.endpos; |
|
break; |
|
} |
|
|
|
desiredGoal = remainingMove; |
|
} |
|
|
|
if ( !trace.startsolid ) |
|
{ |
|
m_lastValidPos = resolvedGoal; |
|
} |
|
|
|
if ( m_bRecomputePostureOnCollision ) |
|
{ |
|
m_bRecomputePostureOnCollision = false; |
|
|
|
if ( !body->IsActualPosture( nPosture ) ) |
|
{ |
|
body->SetDesiredPosture( nPosture ); |
|
} |
|
} |
|
|
|
return resolvedGoal; |
|
} |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Collect the closest actors |
|
*/ |
|
class ClosestActorsScan |
|
{ |
|
public: |
|
ClosestActorsScan( const Vector &spot, int team, float maxRange = 0.0f, CBaseCombatCharacter *ignore = NULL ) |
|
{ |
|
m_spot = spot; |
|
m_team = team; |
|
m_close = NULL; |
|
|
|
if ( maxRange > 0.0f ) |
|
{ |
|
m_closeRangeSq = maxRange * maxRange; |
|
} |
|
else |
|
{ |
|
m_closeRangeSq = 999999999.9f; |
|
} |
|
|
|
m_ignore = ignore; |
|
} |
|
|
|
bool operator() ( CBaseCombatCharacter *actor ) |
|
{ |
|
if (actor == m_ignore) |
|
return true; |
|
|
|
if (actor->IsAlive() && (m_team == TEAM_ANY || actor->GetTeamNumber() == m_team)) |
|
{ |
|
Vector to = actor->WorldSpaceCenter() - m_spot; |
|
float rangeSq = to.LengthSqr(); |
|
if (rangeSq < m_closeRangeSq) |
|
{ |
|
m_closeRangeSq = rangeSq; |
|
m_close = actor; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
CBaseCombatCharacter *GetActor( void ) const |
|
{ |
|
return m_close; |
|
} |
|
|
|
bool IsCloserThan( float range ) |
|
{ |
|
return (m_closeRangeSq < (range * range)); |
|
} |
|
|
|
bool IsFartherThan( float range ) |
|
{ |
|
return (m_closeRangeSq > (range * range)); |
|
} |
|
|
|
Vector m_spot; |
|
int m_team; |
|
CBaseCombatCharacter *m_close; |
|
float m_closeRangeSq; |
|
CBaseCombatCharacter *m_ignore; |
|
}; |
|
|
|
|
|
#ifdef SKIPME |
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Push away zombies that are interpenetrating |
|
*/ |
|
Vector NextBotGroundLocomotion::ResolveZombieCollisions( const Vector &pos ) |
|
{ |
|
Vector adjustedNewPos = pos; |
|
|
|
Infected *me = m_nextBot->MyInfectedPointer(); |
|
const float hullWidth = me->GetBodyInterface()->GetHullWidth(); |
|
|
|
// only avoid if we're actually trying to move somewhere, and are enraged |
|
if ( me != NULL && !IsUsingLadder() && !IsClimbingOrJumping() && IsOnGround() && m_nextBot->IsAlive() && IsAttemptingToMove() /*&& GetBot()->GetBodyInterface()->IsArousal( IBody::INTENSE )*/ ) |
|
{ |
|
VPROF_BUDGET( "NextBotGroundLocomotion::ResolveZombieCollisions", "NextBot" ); |
|
|
|
const CUtlVector< CHandle< Infected > > &neighbors = me->GetNeighbors(); |
|
Vector avoid = vec3_origin; |
|
float avoidWeight = 0.0f; |
|
|
|
FOR_EACH_VEC( neighbors, it ) |
|
{ |
|
Infected *them = neighbors[ it ]; |
|
|
|
if ( them ) |
|
{ |
|
Vector toThem = them->GetAbsOrigin() - me->GetAbsOrigin(); |
|
toThem.z = 0.0f; |
|
|
|
float range = toThem.NormalizeInPlace(); |
|
|
|
if ( range < hullWidth ) |
|
{ |
|
// these two infected are in contact |
|
me->Touch( them ); |
|
|
|
// move out of contact |
|
float penetration = hullWidth - range; |
|
|
|
float weight = 1.0f + ( 2.0f * penetration/hullWidth ); |
|
avoid += -weight * toThem; |
|
avoidWeight += weight; |
|
} |
|
} |
|
} |
|
|
|
if ( avoidWeight > 0.0f ) |
|
{ |
|
adjustedNewPos += 3.0f * ( avoid / avoidWeight ); |
|
} |
|
} |
|
|
|
return adjustedNewPos; |
|
} |
|
#endif // _DEBUG |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Move to newPos, resolving any collisions along the way |
|
*/ |
|
void NextBotGroundLocomotion::UpdatePosition( const Vector &newPos ) |
|
{ |
|
VPROF_BUDGET( "NextBotGroundLocomotion::UpdatePosition", "NextBot" ); |
|
|
|
if ( NextBotStop.GetBool() || (m_nextBot->GetFlags() & FL_FROZEN) != 0 || newPos == m_nextBot->GetPosition() ) |
|
{ |
|
return; |
|
} |
|
|
|
// avoid very nearby Actors to simulate "mushy" collisions between actors in contact with each other |
|
//Vector adjustedNewPos = ResolveZombieCollisions( newPos ); |
|
Vector adjustedNewPos = newPos; |
|
|
|
// check for collisions during move and resolve them |
|
const int recursionLimit = 3; |
|
Vector safePos = ResolveCollision( m_nextBot->GetPosition(), adjustedNewPos, recursionLimit ); |
|
|
|
// set the bot's position |
|
if ( GetBot()->GetIntentionInterface()->IsPositionAllowed( GetBot(), safePos ) != ANSWER_NO ) |
|
{ |
|
m_nextBot->SetPosition( safePos ); |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Prevent bot from sliding through floor, and snap to the ground if we're very near it |
|
*/ |
|
void NextBotGroundLocomotion::UpdateGroundConstraint( void ) |
|
{ |
|
VPROF_BUDGET( "NextBotGroundLocomotion::UpdateGroundConstraint", "NextBotExpensive" ); |
|
|
|
// if we're up on the upward arc of our jump, don't interfere by snapping to ground |
|
// don't do ground constraint if we're climbing a ladder |
|
if ( DidJustJump() || IsAscendingOrDescendingLadder() ) |
|
{ |
|
m_isUsingFullFeetTrace = false; |
|
return; |
|
} |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
if ( body == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
float halfWidth = body->GetHullWidth()/2.0f; |
|
|
|
// since we only care about ground collisions, keep hull short to avoid issues with low ceilings |
|
/// @TODO: We need to also check actual hull height to avoid interpenetrating the world |
|
float hullHeight = GetStepHeight(); |
|
|
|
// always need tolerance even when jumping/falling to make sure we detect ground penetration |
|
// must be at least step height to avoid 'falling' down stairs |
|
const float stickToGroundTolerance = GetStepHeight() + 0.01f; |
|
|
|
trace_t ground; |
|
NextBotTraceFilterIgnoreActors filter( m_nextBot, body->GetCollisionGroup() ); |
|
|
|
TraceHull( m_nextBot->GetPosition() + Vector( 0, 0, GetStepHeight() + 0.001f ), |
|
m_nextBot->GetPosition() + Vector( 0, 0, -stickToGroundTolerance ), |
|
Vector( -halfWidth, -halfWidth, 0 ), |
|
Vector( halfWidth, halfWidth, hullHeight ), |
|
body->GetSolidMask(), &filter, &ground ); |
|
|
|
if ( ground.startsolid ) |
|
{ |
|
// we're inside the ground - bad news |
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) && !( gpGlobals->framecount % 60 ) ) |
|
{ |
|
DevMsg( "%3.2f: Inside ground, ( %.0f, %.0f, %.0f )\n", gpGlobals->curtime, m_nextBot->GetPosition().x, m_nextBot->GetPosition().y, m_nextBot->GetPosition().z ); |
|
} |
|
return; |
|
} |
|
|
|
if ( ground.fraction < 1.0f ) |
|
{ |
|
// there is ground below us |
|
m_groundNormal = ground.plane.normal; |
|
|
|
m_isUsingFullFeetTrace = false; |
|
|
|
// zero velocity normal to the ground |
|
float normalVel = DotProduct( m_groundNormal, m_velocity ); |
|
m_velocity -= normalVel * m_groundNormal; |
|
|
|
// check slope limit |
|
if ( ground.plane.normal.z < GetTraversableSlopeLimit() ) |
|
{ |
|
// too steep to stand here |
|
|
|
// too steep to be ground - treat it like a wall hit |
|
if ( ( m_velocity.x * ground.plane.normal.x + m_velocity.y * ground.plane.normal.y ) <= 0.0f ) |
|
{ |
|
GetBot()->OnContact( ground.m_pEnt, &ground ); |
|
} |
|
|
|
// we're contacting some kind of ground |
|
// zero accelerations normal to the ground |
|
|
|
float normalAccel = DotProduct( m_groundNormal, m_acceleration ); |
|
m_acceleration -= normalAccel * m_groundNormal; |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
DevMsg( "%3.2f: NextBotGroundLocomotion - Too steep to stand here\n", gpGlobals->curtime ); |
|
NDebugOverlay::Line( GetFeet(), GetFeet() + 20.0f * ground.plane.normal, 255, 150, 0, true, 5.0f ); |
|
} |
|
|
|
// clear out upward velocity so we don't walk up lightpoles |
|
m_velocity.z = MIN( 0, m_velocity.z ); |
|
m_acceleration.z = MIN( 0, m_acceleration.z ); |
|
|
|
return; |
|
} |
|
|
|
// inform other components of collision if we didn't land on the 'world' |
|
if ( ground.m_pEnt && !ground.m_pEnt->IsWorld() ) |
|
{ |
|
GetBot()->OnContact( ground.m_pEnt, &ground ); |
|
} |
|
|
|
// snap us to the ground |
|
m_nextBot->SetPosition( ground.endpos ); |
|
|
|
if ( !IsOnGround() ) |
|
{ |
|
// just landed |
|
m_nextBot->SetGroundEntity( ground.m_pEnt ); |
|
m_ground = ground.m_pEnt; |
|
|
|
// landing stops any jump in progress |
|
m_isJumping = false; |
|
m_isJumpingAcrossGap = false; |
|
|
|
GetBot()->OnLandOnGround( ground.m_pEnt ); |
|
} |
|
} |
|
else |
|
{ |
|
// not on the ground |
|
if ( IsOnGround() ) |
|
{ |
|
GetBot()->OnLeaveGround( m_nextBot->GetGroundEntity() ); |
|
if ( !IsClimbingUpToLedge() && !IsJumpingAcrossGap() ) |
|
{ |
|
m_isUsingFullFeetTrace = true; // We're in the air and there's space below us, so use the full trace |
|
m_acceleration.z -= GetGravity(); // start our gravity now |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/* |
|
void NextBotGroundLocomotion::StandUp( void ) |
|
{ |
|
// make sure there is room to stand |
|
trace_t result; |
|
const float halfSize = GetHullWidth()/3.0f; |
|
Vector standHullMin( -halfSize, -halfSize, GetStepHeight() + 0.1f ); |
|
Vector standHullMax( halfSize, halfSize, GetStandHullHeight() ); |
|
|
|
TraceHull( GetFeet(), GetFeet(), standHullMin, standHullMax, MASK_NPCSOLID, m_nextBot, MASK_DEFAULTPLAYERSOLID, &result ); |
|
|
|
if ( result.fraction >= 1.0f && !result.startsolid ) |
|
{ |
|
m_isCrouching = false; |
|
} |
|
} |
|
*/ |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Initiate a climb to an adjacent high ledge |
|
*/ |
|
bool NextBotGroundLocomotion::ClimbUpToLedge( const Vector &landingGoal, const Vector &landingForward, const CBaseEntity *obstacle ) |
|
{ |
|
return false; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Initiate a jump across an empty volume of space to far side |
|
*/ |
|
void NextBotGroundLocomotion::JumpAcrossGap( const Vector &landingGoal, const Vector &landingForward ) |
|
{ |
|
// can only jump if we're on the ground |
|
if ( !IsOnGround() ) |
|
{ |
|
return; |
|
} |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
if ( !body->StartActivity( ACT_JUMP ) ) |
|
{ |
|
// body can't jump right now |
|
return; |
|
} |
|
|
|
|
|
// scale impulse to land on target |
|
Vector toGoal = landingGoal - GetFeet(); |
|
|
|
// equation doesn't work if we're jumping upwards |
|
float height = toGoal.z; |
|
toGoal.z = 0.0f; |
|
|
|
float range = toGoal.NormalizeInPlace(); |
|
|
|
// jump out at 45 degree angle |
|
const float cos45 = 0.7071f; |
|
|
|
// avoid division by zero |
|
if ( height > 0.9f * range ) |
|
{ |
|
height = 0.9f * range; |
|
} |
|
|
|
// ballistic equation to find initial velocity assuming 45 degree inclination and landing at give range and height |
|
float launchVel = ( range / cos45 ) / sqrt( ( 2.0f * ( range - height ) ) / GetGravity() ); |
|
|
|
Vector up( 0, 0, 1 ); |
|
Vector ahead = up + toGoal; |
|
ahead.NormalizeInPlace(); |
|
|
|
//m_velocity = cos45 * launchVel * ahead; |
|
m_velocity = launchVel * ahead; |
|
m_acceleration = vec3_origin; |
|
|
|
m_isJumping = true; |
|
m_isJumpingAcrossGap = true; |
|
m_isClimbingUpToLedge = false; |
|
|
|
GetBot()->OnLeaveGround( m_nextBot->GetGroundEntity() ); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Initiate a simple undirected jump in the air |
|
*/ |
|
void NextBotGroundLocomotion::Jump( void ) |
|
{ |
|
// can only jump if we're on the ground |
|
if ( !IsOnGround() ) |
|
{ |
|
return; |
|
} |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
if ( !body->StartActivity( ACT_JUMP ) ) |
|
{ |
|
// body can't jump right now |
|
return; |
|
} |
|
|
|
// jump straight up |
|
m_velocity.z = sqrt( 2.0f * GetGravity() * GetMaxJumpHeight() ); |
|
|
|
m_isJumping = true; |
|
m_isClimbingUpToLedge = false; |
|
|
|
GetBot()->OnLeaveGround( m_nextBot->GetGroundEntity() ); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Set movement speed to running |
|
*/ |
|
void NextBotGroundLocomotion::Run( void ) |
|
{ |
|
m_desiredSpeed = GetRunSpeed(); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Set movement speed to walking |
|
*/ |
|
void NextBotGroundLocomotion::Walk( void ) |
|
{ |
|
m_desiredSpeed = GetWalkSpeed(); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Set movement speed to stopeed |
|
*/ |
|
void NextBotGroundLocomotion::Stop( void ) |
|
{ |
|
m_desiredSpeed = 0.0f; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Return true if standing on something |
|
*/ |
|
bool NextBotGroundLocomotion::IsOnGround( void ) const |
|
{ |
|
return (m_nextBot->GetGroundEntity() != NULL); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Invoked when bot leaves ground for any reason |
|
*/ |
|
void NextBotGroundLocomotion::OnLeaveGround( CBaseEntity *ground ) |
|
{ |
|
m_nextBot->SetGroundEntity( NULL ); |
|
m_ground = NULL; |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
DevMsg( "%3.2f: NextBotGroundLocomotion::OnLeaveGround\n", gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Invoked when bot lands on the ground after being in the air |
|
*/ |
|
void NextBotGroundLocomotion::OnLandOnGround( CBaseEntity *ground ) |
|
{ |
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
DevMsg( "%3.2f: NextBotGroundLocomotion::GetBot()->OnLandOnGround\n", gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Get maximum speed bot can reach, regardless of desired speed |
|
*/ |
|
float NextBotGroundLocomotion::GetSpeedLimit( void ) const |
|
{ |
|
// if we're crouched, move at reduced speed |
|
if ( !GetBot()->GetBodyInterface()->IsActualPosture( IBody::STAND ) ) |
|
{ |
|
return 0.75f * GetRunSpeed(); |
|
} |
|
|
|
// no limit |
|
return 99999999.9f; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Climb the given ladder to the top and dismount |
|
*/ |
|
void NextBotGroundLocomotion::ClimbLadder( const CNavLadder *ladder, const CNavArea *dismountGoal ) |
|
{ |
|
// if we're already climbing this ladder, don't restart |
|
if ( m_ladder == ladder && m_isGoingUpLadder ) |
|
{ |
|
return; |
|
} |
|
|
|
m_ladder = ladder; |
|
m_ladderDismountGoal = dismountGoal; |
|
m_isGoingUpLadder = true; |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
if ( body ) |
|
{ |
|
// line them up to climb in XY |
|
Vector mountSpot = m_ladder->m_bottom + m_ladder->GetNormal() * (0.75f * body->GetHullWidth()); |
|
mountSpot.z = GetBot()->GetPosition().z; |
|
|
|
UpdatePosition( mountSpot ); |
|
|
|
body->StartActivity( ACT_CLIMB_UP, IBody::MOTION_CONTROLLED_Z ); |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Descend the given ladder to the bottom and dismount |
|
*/ |
|
void NextBotGroundLocomotion::DescendLadder( const CNavLadder *ladder, const CNavArea *dismountGoal ) |
|
{ |
|
// if we're already descending this ladder, don't restart |
|
if ( m_ladder == ladder && !m_isGoingUpLadder ) |
|
{ |
|
return; |
|
} |
|
|
|
m_ladder = ladder; |
|
m_ladderDismountGoal = dismountGoal; |
|
m_isGoingUpLadder = false; |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
if ( body ) |
|
{ |
|
// line them up to climb in XY |
|
Vector mountSpot = m_ladder->m_top + m_ladder->GetNormal() * (0.75f * body->GetHullWidth()); |
|
mountSpot.z = GetBot()->GetPosition().z; |
|
|
|
UpdatePosition( mountSpot ); |
|
|
|
float ladderYaw = UTIL_VecToYaw( -m_ladder->GetNormal() ); |
|
|
|
QAngle angles = m_nextBot->GetLocalAngles(); |
|
angles.y = ladderYaw; |
|
|
|
m_nextBot->SetLocalAngles( angles ); |
|
|
|
body->StartActivity( ACT_CLIMB_DOWN, IBody::MOTION_CONTROLLED_Z ); |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
bool NextBotGroundLocomotion::IsUsingLadder( void ) const |
|
{ |
|
return ( m_ladder != NULL ); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* We are actually on the ladder right now, either climbing up or down |
|
*/ |
|
bool NextBotGroundLocomotion::IsAscendingOrDescendingLadder( void ) const |
|
{ |
|
return IsUsingLadder(); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Return position of "feet" - point below centroid of bot at feet level |
|
*/ |
|
const Vector &NextBotGroundLocomotion::GetFeet( void ) const |
|
{ |
|
return m_nextBot->GetPosition(); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
const Vector & NextBotGroundLocomotion::GetAcceleration( void ) const |
|
{ |
|
return m_acceleration; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
void NextBotGroundLocomotion::SetAcceleration( const Vector &accel ) |
|
{ |
|
m_acceleration = accel; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
void NextBotGroundLocomotion::SetVelocity( const Vector &vel ) |
|
{ |
|
m_velocity = vel; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Return current world space velocity |
|
*/ |
|
const Vector &NextBotGroundLocomotion::GetVelocity( void ) const |
|
{ |
|
return m_velocity; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Invoked when an bot reaches its MoveTo goal |
|
*/ |
|
void NextBotGroundLocomotion::OnMoveToSuccess( const Path *path ) |
|
{ |
|
// stop |
|
m_velocity = vec3_origin; |
|
m_acceleration = vec3_origin; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Invoked when an bot fails to reach a MoveTo goal |
|
*/ |
|
void NextBotGroundLocomotion::OnMoveToFailure( const Path *path, MoveToFailureType reason ) |
|
{ |
|
// stop |
|
m_velocity = vec3_origin; |
|
m_acceleration = vec3_origin; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
bool NextBotGroundLocomotion::DidJustJump( void ) const |
|
{ |
|
return IsClimbingOrJumping() && (m_nextBot->GetAbsVelocity().z > 0.0f); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------- |
|
/** |
|
* Rotate body to face towards "target" |
|
*/ |
|
void NextBotGroundLocomotion::FaceTowards( const Vector &target ) |
|
{ |
|
const float deltaT = GetUpdateInterval(); |
|
|
|
QAngle angles = m_nextBot->GetLocalAngles(); |
|
|
|
float desiredYaw = UTIL_VecToYaw( target - GetFeet() ); |
|
|
|
float angleDiff = UTIL_AngleDiff( desiredYaw, angles.y ); |
|
|
|
float deltaYaw = GetMaxYawRate() * deltaT; |
|
|
|
if (angleDiff < -deltaYaw) |
|
{ |
|
angles.y -= deltaYaw; |
|
} |
|
else if (angleDiff > deltaYaw) |
|
{ |
|
angles.y += deltaYaw; |
|
} |
|
else |
|
{ |
|
angles.y += angleDiff; |
|
} |
|
|
|
m_nextBot->SetLocalAngles( angles ); |
|
} |
|
|
|
|
|
|
|
|