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.
826 lines
22 KiB
826 lines
22 KiB
// NextBotPlayerLocomotion.cpp |
|
// Implementation of Locomotion interface for CBasePlayer-derived classes |
|
// Author: Michael Booth, November 2005 |
|
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
|
|
#include "cbase.h" |
|
#include "nav_mesh.h" |
|
#include "in_buttons.h" |
|
#include "NextBot.h" |
|
#include "NextBotUtil.h" |
|
#include "NextBotPlayer.h" |
|
#include "NextBotPlayerLocomotion.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
ConVar NextBotPlayerMoveDirect( "nb_player_move_direct", "0" ); |
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
PlayerLocomotion::PlayerLocomotion( INextBot *bot ) : ILocomotion( bot ) |
|
{ |
|
m_player = NULL; |
|
Reset(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
/** |
|
* Reset locomotor to initial state |
|
*/ |
|
void PlayerLocomotion::Reset( void ) |
|
{ |
|
m_player = static_cast< CBasePlayer * >( GetBot()->GetEntity() ); |
|
|
|
m_isJumping = false; |
|
m_isClimbingUpToLedge = false; |
|
m_isJumpingAcrossGap = false; |
|
m_hasLeftTheGround = false; |
|
m_desiredSpeed = 0.0f; |
|
|
|
|
|
m_ladderState = NO_LADDER; |
|
m_ladderInfo = NULL; |
|
m_ladderDismountGoal = NULL; |
|
m_ladderTimer.Invalidate(); |
|
|
|
m_minSpeedLimit = 0.0f; |
|
m_maxSpeedLimit = 9999999.9f; |
|
|
|
BaseClass::Reset(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
bool PlayerLocomotion::TraverseLadder( void ) |
|
{ |
|
switch( m_ladderState ) |
|
{ |
|
case APPROACHING_ASCENDING_LADDER: |
|
m_ladderState = ApproachAscendingLadder(); |
|
return true; |
|
|
|
case APPROACHING_DESCENDING_LADDER: |
|
m_ladderState = ApproachDescendingLadder(); |
|
return true; |
|
|
|
case ASCENDING_LADDER: |
|
m_ladderState = AscendLadder(); |
|
return true; |
|
|
|
case DESCENDING_LADDER: |
|
m_ladderState = DescendLadder(); |
|
return true; |
|
|
|
case DISMOUNTING_LADDER_TOP: |
|
m_ladderState = DismountLadderTop(); |
|
return true; |
|
|
|
case DISMOUNTING_LADDER_BOTTOM: |
|
m_ladderState = DismountLadderBottom(); |
|
return true; |
|
|
|
case NO_LADDER: |
|
default: |
|
m_ladderInfo = NULL; |
|
|
|
if ( GetBot()->GetEntity()->GetMoveType() == MOVETYPE_LADDER ) |
|
{ |
|
// on ladder and don't want to be |
|
GetBot()->GetEntity()->SetMoveType( MOVETYPE_WALK ); |
|
} |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
/** |
|
* We're close, but not yet on, this ladder - approach it |
|
*/ |
|
PlayerLocomotion::LadderState PlayerLocomotion::ApproachAscendingLadder( void ) |
|
{ |
|
if ( m_ladderInfo == NULL ) |
|
{ |
|
return NO_LADDER; |
|
} |
|
|
|
// sanity check - are we already at the end of this ladder? |
|
if ( GetFeet().z >= m_ladderInfo->m_top.z - GetStepHeight() ) |
|
{ |
|
m_ladderTimer.Start( 2.0f ); |
|
return DISMOUNTING_LADDER_TOP; |
|
} |
|
|
|
// sanity check - are we too far below this ladder to reach it? |
|
if ( GetFeet().z <= m_ladderInfo->m_bottom.z - GetMaxJumpHeight() ) |
|
{ |
|
return NO_LADDER; |
|
} |
|
|
|
FaceTowards( m_ladderInfo->m_bottom ); |
|
|
|
// it is important to approach precisely, so use a very large weight to wash out all other Approaches |
|
Approach( m_ladderInfo->m_bottom, 9999999.9f ); |
|
|
|
if ( GetBot()->GetEntity()->GetMoveType() == MOVETYPE_LADDER ) |
|
{ |
|
// we're on the ladder |
|
return ASCENDING_LADDER; |
|
} |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::EntityText( GetBot()->GetEntity()->entindex(), 0, "Approach ascending ladder", 0.1f, 255, 255, 255, 255 ); |
|
} |
|
|
|
return APPROACHING_ASCENDING_LADDER; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
PlayerLocomotion::LadderState PlayerLocomotion::ApproachDescendingLadder( void ) |
|
{ |
|
if ( m_ladderInfo == NULL ) |
|
{ |
|
return NO_LADDER; |
|
} |
|
|
|
// sanity check - are we already at the end of this ladder? |
|
if ( GetFeet().z <= m_ladderInfo->m_bottom.z + GetMaxJumpHeight() ) |
|
{ |
|
m_ladderTimer.Start( 2.0f ); |
|
return DISMOUNTING_LADDER_BOTTOM; |
|
} |
|
|
|
Vector mountPoint = m_ladderInfo->m_top + 0.25f * GetBot()->GetBodyInterface()->GetHullWidth() * m_ladderInfo->GetNormal(); |
|
Vector to = mountPoint - GetFeet(); |
|
to.z = 0.0f; |
|
|
|
float mountRange = to.NormalizeInPlace(); |
|
Vector moveGoal; |
|
|
|
const float veryClose = 10.0f; |
|
if ( mountRange < veryClose ) |
|
{ |
|
// we're right at the ladder - just keep moving forward until we grab it |
|
const Vector &forward = GetMotionVector(); |
|
moveGoal = GetFeet() + 100.0f * forward; |
|
} |
|
else |
|
{ |
|
if ( DotProduct( to, m_ladderInfo->GetNormal() ) < 0.0f ) |
|
{ |
|
// approaching front of downward ladder |
|
// ## |
|
// ->+ ## |
|
// | ## |
|
// | ## |
|
// | ## |
|
// <-+ ## |
|
// ###### |
|
// |
|
moveGoal = m_ladderInfo->m_top - 100.0f * m_ladderInfo->GetNormal(); |
|
} |
|
else |
|
{ |
|
// approaching back of downward ladder |
|
// |
|
// ->+ |
|
// ##| |
|
// ##| |
|
// ##+--> |
|
// ###### |
|
// |
|
moveGoal = m_ladderInfo->m_top + 100.0f * m_ladderInfo->GetNormal(); |
|
} |
|
} |
|
|
|
FaceTowards( moveGoal ); |
|
|
|
// it is important to approach precisely, so use a very large weight to wash out all other Approaches |
|
Approach( moveGoal, 9999999.9f ); |
|
|
|
if ( GetBot()->GetEntity()->GetMoveType() == MOVETYPE_LADDER ) |
|
{ |
|
// we're on the ladder |
|
return DESCENDING_LADDER; |
|
} |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::EntityText( GetBot()->GetEntity()->entindex(), 0, "Approach descending ladder", 0.1f, 255, 255, 255, 255 ); |
|
} |
|
|
|
return APPROACHING_DESCENDING_LADDER; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
PlayerLocomotion::LadderState PlayerLocomotion::AscendLadder( void ) |
|
{ |
|
if ( m_ladderInfo == NULL ) |
|
{ |
|
return NO_LADDER; |
|
} |
|
|
|
if ( GetBot()->GetEntity()->GetMoveType() != MOVETYPE_LADDER ) |
|
{ |
|
// slipped off ladder |
|
m_ladderInfo = NULL; |
|
return NO_LADDER; |
|
} |
|
|
|
if ( GetFeet().z >= m_ladderInfo->m_top.z ) |
|
{ |
|
// reached top of ladder |
|
m_ladderTimer.Start( 2.0f ); |
|
return DISMOUNTING_LADDER_TOP; |
|
} |
|
|
|
// climb up this ladder - look up |
|
Vector goal = GetFeet() + 100.0f * ( -m_ladderInfo->GetNormal() + Vector( 0, 0, 2 ) ); |
|
|
|
GetBot()->GetBodyInterface()->AimHeadTowards( goal, IBody::MANDATORY, 0.1f, NULL, "Ladder" ); |
|
|
|
// it is important to approach precisely, so use a very large weight to wash out all other Approaches |
|
Approach( goal, 9999999.9f ); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::EntityText( GetBot()->GetEntity()->entindex(), 0, "Ascend", 0.1f, 255, 255, 255, 255 ); |
|
} |
|
|
|
return ASCENDING_LADDER; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
PlayerLocomotion::LadderState PlayerLocomotion::DescendLadder( void ) |
|
{ |
|
if ( m_ladderInfo == NULL ) |
|
{ |
|
return NO_LADDER; |
|
} |
|
|
|
if ( GetBot()->GetEntity()->GetMoveType() != MOVETYPE_LADDER ) |
|
{ |
|
// slipped off ladder |
|
m_ladderInfo = NULL; |
|
return NO_LADDER; |
|
} |
|
|
|
if ( GetFeet().z <= m_ladderInfo->m_bottom.z + GetBot()->GetLocomotionInterface()->GetStepHeight() ) |
|
{ |
|
// reached bottom of ladder |
|
m_ladderTimer.Start( 2.0f ); |
|
return DISMOUNTING_LADDER_BOTTOM; |
|
} |
|
|
|
// climb down this ladder - look down |
|
Vector goal = GetFeet() + 100.0f * ( m_ladderInfo->GetNormal() + Vector( 0, 0, -2 ) ); |
|
|
|
GetBot()->GetBodyInterface()->AimHeadTowards( goal, IBody::MANDATORY, 0.1f, NULL, "Ladder" ); |
|
|
|
// it is important to approach precisely, so use a very large weight to wash out all other Approaches |
|
Approach( goal, 9999999.9f ); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::EntityText( GetBot()->GetEntity()->entindex(), 0, "Descend", 0.1f, 255, 255, 255, 255 ); |
|
} |
|
|
|
return DESCENDING_LADDER; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
PlayerLocomotion::LadderState PlayerLocomotion::DismountLadderTop( void ) |
|
{ |
|
if ( m_ladderInfo == NULL || m_ladderTimer.IsElapsed() ) |
|
{ |
|
m_ladderInfo = NULL; |
|
return NO_LADDER; |
|
} |
|
|
|
IBody *body = GetBot()->GetBodyInterface(); |
|
Vector toGoal = m_ladderDismountGoal->GetCenter() - GetFeet(); |
|
toGoal.z = 0.0f; |
|
float range = toGoal.NormalizeInPlace(); |
|
toGoal.z = 1.0f; |
|
|
|
body->AimHeadTowards( body->GetEyePosition() + 100.0f * toGoal, IBody::MANDATORY, 0.1f, NULL, "Ladder dismount" ); |
|
|
|
// it is important to approach precisely, so use a very large weight to wash out all other Approaches |
|
Approach( GetFeet() + 100.0f * toGoal, 9999999.9f ); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::EntityText( GetBot()->GetEntity()->entindex(), 0, "Dismount top", 0.1f, 255, 255, 255, 255 ); |
|
NDebugOverlay::HorzArrow( GetFeet(), m_ladderDismountGoal->GetCenter(), 5.0f, 255, 255, 0, 255, true, 0.1f ); |
|
} |
|
|
|
// test 2D vector here in case nav area is under the geometry a bit |
|
const float tolerance = 10.0f; |
|
if ( GetBot()->GetEntity()->GetLastKnownArea() == m_ladderDismountGoal && range < tolerance ) |
|
{ |
|
// reached dismount goal |
|
m_ladderInfo = NULL; |
|
return NO_LADDER; |
|
} |
|
|
|
return DISMOUNTING_LADDER_TOP; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
PlayerLocomotion::LadderState PlayerLocomotion::DismountLadderBottom( void ) |
|
{ |
|
if ( m_ladderInfo == NULL || m_ladderTimer.IsElapsed() ) |
|
{ |
|
m_ladderInfo = NULL; |
|
return NO_LADDER; |
|
} |
|
|
|
if ( GetBot()->GetEntity()->GetMoveType() == MOVETYPE_LADDER ) |
|
{ |
|
// near the bottom - just let go |
|
GetBot()->GetEntity()->SetMoveType( MOVETYPE_WALK ); |
|
m_ladderInfo = NULL; |
|
} |
|
|
|
return NO_LADDER; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
/** |
|
* Update internal state |
|
*/ |
|
void PlayerLocomotion::Update( void ) |
|
{ |
|
if ( TraverseLadder() ) |
|
{ |
|
return BaseClass::Update(); |
|
} |
|
|
|
if ( m_isJumpingAcrossGap || m_isClimbingUpToLedge ) |
|
{ |
|
// force a run |
|
SetMinimumSpeedLimit( GetRunSpeed() ); |
|
|
|
Vector toLanding = m_landingGoal - GetFeet(); |
|
toLanding.z = 0.0f; |
|
toLanding.NormalizeInPlace(); |
|
|
|
if ( m_hasLeftTheGround ) |
|
{ |
|
// face into the jump/climb |
|
GetBot()->GetBodyInterface()->AimHeadTowards( GetBot()->GetEntity()->EyePosition() + 100.0 * toLanding, IBody::MANDATORY, 0.25f, NULL, "Facing impending jump/climb" ); |
|
|
|
if ( IsOnGround() ) |
|
{ |
|
// back on the ground - jump is complete |
|
m_isClimbingUpToLedge = false; |
|
m_isJumpingAcrossGap = false; |
|
SetMinimumSpeedLimit( 0.0f ); |
|
} |
|
} |
|
else |
|
{ |
|
// haven't left the ground yet - just starting the jump |
|
|
|
if ( !IsClimbingOrJumping() ) |
|
{ |
|
Jump(); |
|
} |
|
|
|
Vector vel = GetBot()->GetEntity()->GetAbsVelocity(); |
|
|
|
if ( m_isJumpingAcrossGap ) |
|
{ |
|
// cheat and max our velocity in case we were stopped at the edge of this gap |
|
vel.x = GetRunSpeed() * toLanding.x; |
|
vel.y = GetRunSpeed() * toLanding.y; |
|
// leave vel.z unchanged |
|
} |
|
|
|
GetBot()->GetEntity()->SetAbsVelocity( vel ); |
|
|
|
if ( !IsOnGround() ) |
|
{ |
|
// jump has begun |
|
m_hasLeftTheGround = true; |
|
} |
|
} |
|
|
|
|
|
Approach( m_landingGoal ); |
|
} |
|
|
|
BaseClass::Update(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
void PlayerLocomotion::AdjustPosture( const Vector &moveGoal ) |
|
{ |
|
// This function has no effect if we're not standing or crouching |
|
IBody *body = GetBot()->GetBodyInterface(); |
|
if ( !body->IsActualPosture( IBody::STAND ) && !body->IsActualPosture( IBody::CROUCH ) ) |
|
return; |
|
|
|
// not all games have auto-crouch, so don't assume it here |
|
BaseClass::AdjustPosture( moveGoal ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
/** |
|
* Build a user command to move this player towards the goal position |
|
*/ |
|
void PlayerLocomotion::Approach( const Vector &pos, float goalWeight ) |
|
{ |
|
VPROF_BUDGET( "PlayerLocomotion::Approach", "NextBot" ); |
|
|
|
BaseClass::Approach( pos ); |
|
|
|
AdjustPosture( pos ); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::Line( GetFeet(), pos, 255, 255, 0, true, 0.1f ); |
|
} |
|
|
|
INextBotPlayerInput *playerButtons = dynamic_cast< INextBotPlayerInput * >( GetBot() ); |
|
|
|
if ( !playerButtons ) |
|
{ |
|
DevMsg( "PlayerLocomotion::Approach: No INextBotPlayerInput\n " ); |
|
return; |
|
} |
|
|
|
Vector forward3D; |
|
m_player->EyeVectors( &forward3D ); |
|
|
|
Vector2D forward( forward3D.x, forward3D.y ); |
|
forward.NormalizeInPlace(); |
|
|
|
Vector2D right( forward.y, -forward.x ); |
|
|
|
// compute unit vector to goal position |
|
Vector2D to = ( pos - GetFeet() ).AsVector2D(); |
|
float goalDistance = to.NormalizeInPlace(); |
|
|
|
float ahead = to.Dot( forward ); |
|
float side = to.Dot( right ); |
|
|
|
#ifdef NEED_TO_INTEGRATE_MOTION_CONTROLLED_CODE_FROM_L4D_PLAYERS |
|
// If we're climbing ledges, we need to stay crouched to prevent player movement code from messing |
|
// with our origin. |
|
CTerrorPlayer *player = ToTerrorPlayer(m_player); |
|
if ( player && player->IsMotionControlledZ( player->GetMainActivity() ) ) |
|
{ |
|
playerButtons->PressCrouchButton(); |
|
return; |
|
} |
|
#endif |
|
|
|
if ( m_player->IsOnLadder() && IsUsingLadder() && ( m_ladderState == ASCENDING_LADDER || m_ladderState == DESCENDING_LADDER ) ) |
|
{ |
|
// we are on a ladder and WANT to be on a ladder. |
|
playerButtons->PressForwardButton(); |
|
|
|
// Stay in center of ladder. The gamemovement will autocenter us in most cases, but this is needed in case it doesn't. |
|
if ( m_ladderInfo ) |
|
{ |
|
Vector posOnLadder; |
|
CalcClosestPointOnLine( GetFeet(), m_ladderInfo->m_bottom, m_ladderInfo->m_top, posOnLadder ); |
|
|
|
Vector alongLadder = m_ladderInfo->m_top - m_ladderInfo->m_bottom; |
|
alongLadder.NormalizeInPlace(); |
|
|
|
Vector rightLadder = CrossProduct( alongLadder, m_ladderInfo->GetNormal() ); |
|
|
|
Vector away = GetFeet() - posOnLadder; |
|
|
|
// we only want error in plane of ladder |
|
float error = DotProduct( away, rightLadder ); |
|
away.NormalizeInPlace(); |
|
|
|
const float tolerance = 5.0f + 0.25f * GetBot()->GetBodyInterface()->GetHullWidth(); |
|
if ( error > tolerance ) |
|
{ |
|
if ( DotProduct( away, rightLadder ) > 0.0f ) |
|
{ |
|
playerButtons->PressLeftButton(); |
|
} |
|
else |
|
{ |
|
playerButtons->PressRightButton(); |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
const float epsilon = 0.25f; |
|
if ( NextBotPlayerMoveDirect.GetBool() ) |
|
{ |
|
if ( goalDistance > epsilon ) |
|
{ |
|
playerButtons->SetButtonScale( ahead, side ); |
|
} |
|
} |
|
|
|
if ( ahead > epsilon ) |
|
{ |
|
playerButtons->PressForwardButton(); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::HorzArrow( m_player->GetAbsOrigin(), m_player->GetAbsOrigin() + 50.0f * Vector( forward.x, forward.y, 0.0f ), 15.0f, 0, 255, 0, 255, true, 0.1f ); |
|
} |
|
} |
|
else if ( ahead < -epsilon ) |
|
{ |
|
playerButtons->PressBackwardButton(); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::HorzArrow( m_player->GetAbsOrigin(), m_player->GetAbsOrigin() - 50.0f * Vector( forward.x, forward.y, 0.0f ), 15.0f, 255, 0, 0, 255, true, 0.1f ); |
|
} |
|
} |
|
|
|
if ( side <= -epsilon ) |
|
{ |
|
playerButtons->PressLeftButton(); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::HorzArrow( m_player->GetAbsOrigin(), m_player->GetAbsOrigin() - 50.0f * Vector( right.x, right.y, 0.0f ), 15.0f, 255, 0, 255, 255, true, 0.1f ); |
|
} |
|
} |
|
else if ( side >= epsilon ) |
|
{ |
|
playerButtons->PressRightButton(); |
|
|
|
if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) |
|
{ |
|
NDebugOverlay::HorzArrow( m_player->GetAbsOrigin(), m_player->GetAbsOrigin() + 50.0f * Vector( right.x, right.y, 0.0f ), 15.0f, 0, 255, 255, 255, true, 0.1f ); |
|
} |
|
} |
|
} |
|
|
|
if ( !IsRunning() ) |
|
{ |
|
playerButtons->PressWalkButton(); |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
/** |
|
* Move the bot to the precise given position immediately, |
|
*/ |
|
void PlayerLocomotion::DriveTo( const Vector &pos ) |
|
{ |
|
BaseClass::DriveTo( pos ); |
|
|
|
Approach( pos ); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
bool PlayerLocomotion::IsClimbPossible( INextBot *me, const CBaseEntity *obstacle ) const |
|
{ |
|
// don't jump unless we have to |
|
const PathFollower *path = GetBot()->GetCurrentPath(); |
|
if ( path ) |
|
{ |
|
const float watchForClimbRange = 75.0f; |
|
if ( !path->IsDiscontinuityAhead( GetBot(), Path::CLIMB_UP, watchForClimbRange ) ) |
|
{ |
|
// we are not planning on climbing |
|
|
|
// always allow climbing over movable obstacles |
|
if ( obstacle && !const_cast< CBaseEntity * >( obstacle )->IsWorld() ) |
|
{ |
|
IPhysicsObject *physics = obstacle->VPhysicsGetObject(); |
|
if ( physics && physics->IsMoveable() ) |
|
{ |
|
// movable physics object - climb over it |
|
return true; |
|
} |
|
} |
|
|
|
if ( !GetBot()->GetLocomotionInterface()->IsStuck() ) |
|
{ |
|
// we're not stuck - don't try to jump up yet |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
bool PlayerLocomotion::ClimbUpToLedge( const Vector &landingGoal, const Vector &landingForward, const CBaseEntity *obstacle ) |
|
{ |
|
if ( !IsClimbPossible( GetBot(), obstacle ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
Jump(); |
|
|
|
m_isClimbingUpToLedge = true; |
|
m_landingGoal = landingGoal; |
|
m_hasLeftTheGround = false; |
|
|
|
return true; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
void PlayerLocomotion::JumpAcrossGap( const Vector &landingGoal, const Vector &landingForward ) |
|
{ |
|
Jump(); |
|
|
|
// face forward |
|
GetBot()->GetBodyInterface()->AimHeadTowards( landingGoal, IBody::MANDATORY, 1.0f, NULL, "Looking forward while jumping a gap" ); |
|
|
|
m_isJumpingAcrossGap = true; |
|
m_landingGoal = landingGoal; |
|
m_hasLeftTheGround = false; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
void PlayerLocomotion::Jump( void ) |
|
{ |
|
m_isJumping = true; |
|
m_jumpTimer.Start( 0.5f ); |
|
|
|
INextBotPlayerInput *playerButtons = dynamic_cast< INextBotPlayerInput * >( GetBot() ); |
|
if ( playerButtons ) |
|
{ |
|
playerButtons->PressJumpButton(); |
|
} |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
bool PlayerLocomotion::IsClimbingOrJumping( void ) const |
|
{ |
|
if ( !m_isJumping ) |
|
return false; |
|
|
|
if ( m_jumpTimer.IsElapsed() && IsOnGround() ) |
|
{ |
|
m_isJumping = false; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
bool PlayerLocomotion::IsClimbingUpToLedge( void ) const |
|
{ |
|
return m_isClimbingUpToLedge; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
bool PlayerLocomotion::IsJumpingAcrossGap( void ) const |
|
{ |
|
return m_isJumpingAcrossGap; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
/** |
|
* Return true if standing on something |
|
*/ |
|
bool PlayerLocomotion::IsOnGround( void ) const |
|
{ |
|
return (m_player->GetGroundEntity() != NULL); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
/** |
|
* Return the current ground entity or NULL if not on the ground |
|
*/ |
|
CBaseEntity *PlayerLocomotion::GetGround( void ) const |
|
{ |
|
return m_player->GetGroundEntity(); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
/** |
|
* Surface normal of the ground we are in contact with |
|
*/ |
|
const Vector &PlayerLocomotion::GetGroundNormal( void ) const |
|
{ |
|
static Vector up( 0, 0, 1.0f ); |
|
return up; |
|
|
|
// TODO: Integrate movehelper_server for this: return m_player->GetGroundNormal(); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
/** |
|
* Climb the given ladder to the top and dismount |
|
*/ |
|
void PlayerLocomotion::ClimbLadder( const CNavLadder *ladder, const CNavArea *dismountGoal ) |
|
{ |
|
// look up and push forward |
|
// Vector goal = GetBot()->GetPosition() + 100.0f * ( Vector( 0, 0, 1.0f ) - ladder->GetNormal() ); |
|
// Approach( goal ); |
|
// FaceTowards( goal ); |
|
|
|
m_ladderState = APPROACHING_ASCENDING_LADDER; |
|
m_ladderInfo = ladder; |
|
m_ladderDismountGoal = dismountGoal; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
/** |
|
* Descend the given ladder to the bottom and dismount |
|
*/ |
|
void PlayerLocomotion::DescendLadder( const CNavLadder *ladder, const CNavArea *dismountGoal ) |
|
{ |
|
// look down and push forward |
|
// Vector goal = GetBot()->GetPosition() + 100.0f * ( Vector( 0, 0, -1.0f ) - ladder->GetNormal() ); |
|
// Approach( goal ); |
|
// FaceTowards( goal ); |
|
|
|
m_ladderState = APPROACHING_DESCENDING_LADDER; |
|
m_ladderInfo = ladder; |
|
m_ladderDismountGoal = dismountGoal; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
bool PlayerLocomotion::IsUsingLadder( void ) const |
|
{ |
|
return ( m_ladderState != NO_LADDER ); |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- |
|
/** |
|
* Rotate body to face towards "target" |
|
*/ |
|
void PlayerLocomotion::FaceTowards( const Vector &target ) |
|
{ |
|
// player body follows view direction |
|
Vector look( target.x, target.y, GetBot()->GetEntity()->EyePosition().z ); |
|
|
|
GetBot()->GetBodyInterface()->AimHeadTowards( look, IBody::BORING, 0.1f, NULL, "Body facing" ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
/** |
|
* Return position of "feet" - point below centroid of bot at feet level |
|
*/ |
|
const Vector &PlayerLocomotion::GetFeet( void ) const |
|
{ |
|
return m_player->GetAbsOrigin(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
/** |
|
* Return current world space velocity |
|
*/ |
|
const Vector &PlayerLocomotion::GetVelocity( void ) const |
|
{ |
|
return m_player->GetAbsVelocity(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
float PlayerLocomotion::GetRunSpeed( void ) const |
|
{ |
|
return m_player->MaxSpeed(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------------------------------- |
|
float PlayerLocomotion::GetWalkSpeed( void ) const |
|
{ |
|
return 0.5f * m_player->MaxSpeed(); |
|
} |
|
|
|
|