diff --git a/src/game/server/neo/bot/neo_bot_locomotion.cpp b/src/game/server/neo/bot/neo_bot_locomotion.cpp index 0ec6cc339a..08a107c364 100644 --- a/src/game/server/neo/bot/neo_bot_locomotion.cpp +++ b/src/game/server/neo/bot/neo_bot_locomotion.cpp @@ -6,6 +6,13 @@ extern ConVar falldamage; +// How far towards its move goal a bot checks its headroom: at least about a hull width, so it ducks at the +// obstacle rather than a long way before it, +static const float NEO_BOT_HEADROOM_LOOKAHEAD = 32.0f; +// and how often, while the crouch press of one check lasts until the next +static const float NEO_BOT_HEADROOM_CHECK_INTERVAL = 0.2f; +static const float NEO_BOT_HEADROOM_CROUCH_HOLD = 0.3f; + //----------------------------------------------------------------------------------------- void CNEOBotLocomotion::Update( void ) { @@ -56,6 +63,90 @@ void CNEOBotLocomotion::Approach( const Vector& pos, float goalWeight ) } +//----------------------------------------------------------------------------------------- +// Duck when a standing hull would hit something just ahead but a crouched one would not: a beam, +// light fitting or lintel at head height. The bot template this came from never crouched to navigate +void CNEOBotLocomotion::AdjustPosture( const Vector &moveGoal ) +{ + if ( !IsOnGround() ) + { + return; + } + + CNEOBot *me = ToNEOBot( GetBot()->GetEntity() ); + if ( !me || me->IsBotOnLadder() ) + { + return; + } + + // A crouch area already holds the duck, and a bot at a ladder's foot or top lines up with the ladder standing + const CNavArea *area = me->GetLastKnownArea(); + if ( area && ( area->HasAttributes( NAV_MESH_CROUCH ) || IsLadderEnd( area ) ) ) + { + return; + } + + if ( !m_headroomTimer.IsElapsed() ) + { + return; + } + + m_headroomTimer.Start( NEO_BOT_HEADROOM_CHECK_INTERVAL ); + + // look as far as the bot moves before the next check, plus a hull width + const float flReach = MAX( NEO_BOT_HEADROOM_LOOKAHEAD, + GetGroundSpeed() * NEO_BOT_HEADROOM_CHECK_INTERVAL + GetBot()->GetBodyInterface()->GetHullWidth() ); + if ( IsOnlyCrouchClearAhead( moveGoal, flReach ) ) + { + me->PressCrouchButton( NEO_BOT_HEADROOM_CROUCH_HOLD ); + } +} + + +//----------------------------------------------------------------------------------------- +bool CNEOBotLocomotion::IsLadderEnd( const CNavArea *area ) +{ + return area->GetLadders( CNavLadder::LADDER_UP )->Count() > 0 + || area->GetLadders( CNavLadder::LADDER_DOWN )->Count() > 0; +} + + +//----------------------------------------------------------------------------------------- +bool CNEOBotLocomotion::IsOnlyCrouchClearAhead( const Vector &moveGoal, float flReach ) const +{ + const Vector &feet = GetFeet(); + Vector toGoal = moveGoal - feet; + toGoal.z = 0.0f; + const float flGoalDist = toGoal.NormalizeInPlace(); + if ( flGoalDist < 1.0f ) + { + return false; + } + + // Sweep from step height up, so a step the bot can walk onto does not count as blocking + const IBody *body = GetBot()->GetBodyInterface(); + const float flStep = GetStepHeight(); + const float flHalfWidth = 0.5f * body->GetHullWidth(); + const Vector mins( -flHalfWidth, -flHalfWidth, 0.0f ); + const Vector standMaxs( flHalfWidth, flHalfWidth, body->GetStandHullHeight() - flStep ); + const Vector crouchMaxs( flHalfWidth, flHalfWidth, body->GetCrouchHullHeight() - flStep ); + const Vector from = feet + Vector( 0.0f, 0.0f, flStep ); + const Vector to = from + toGoal * Min( flGoalDist, flReach ); + + // Players move out of the way, so only the world and props count + NextBotTraceFilterIgnoreActors filter( GetBot()->GetEntity(), COLLISION_GROUP_NONE ); + trace_t trace; + TraceHull( from, to, mins, standMaxs, body->GetSolidMask(), &filter, &trace ); + if ( trace.fraction >= 1.0f && !trace.startsolid ) + { + return false; + } + + TraceHull( from, to, mins, crouchMaxs, body->GetSolidMask(), &filter, &trace ); + return trace.fraction >= 1.0f && !trace.startsolid; +} + + //----------------------------------------------------------------------------------------- // Distance at which we will die if we fall float CNEOBotLocomotion::GetDeathDropHeight( void ) const diff --git a/src/game/server/neo/bot/neo_bot_locomotion.h b/src/game/server/neo/bot/neo_bot_locomotion.h index 427d0e5e45..fa86811bd3 100644 --- a/src/game/server/neo/bot/neo_bot_locomotion.h +++ b/src/game/server/neo/bot/neo_bot_locomotion.h @@ -31,7 +31,13 @@ class CNEOBotLocomotion : public PlayerLocomotion virtual bool IsEntityTraversable( CBaseEntity *obstacle, TraverseWhenType when = EVENTUALLY ) const; protected: - virtual void AdjustPosture( const Vector &moveGoal ) { } // never crouch to navigate + virtual void AdjustPosture( const Vector &moveGoal ); // duck when only a crouched hull fits ahead + +private: + bool IsOnlyCrouchClearAhead( const Vector &moveGoal, float flReach ) const; + static bool IsLadderEnd( const CNavArea *area ); + + CountdownTimer m_headroomTimer; }; inline float CNEOBotLocomotion::GetMaxJumpHeight( void ) const