From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- game/server/NextBot/NextBotLocomotionInterface.cpp | 520 +++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 game/server/NextBot/NextBotLocomotionInterface.cpp (limited to 'game/server/NextBot/NextBotLocomotionInterface.cpp') diff --git a/game/server/NextBot/NextBotLocomotionInterface.cpp b/game/server/NextBot/NextBotLocomotionInterface.cpp new file mode 100644 index 0000000..9acc865 --- /dev/null +++ b/game/server/NextBot/NextBotLocomotionInterface.cpp @@ -0,0 +1,520 @@ +// NextBotLocomotionInterface.cpp +// Common functionality for all NextBot locomotors +// Author: Michael Booth, April 2005 +//========= Copyright Valve Corporation, All rights reserved. ============// + +#include "cbase.h" + +#include "BasePropDoor.h" + +#include "nav_area.h" +#include "NextBot.h" +#include "NextBotUtil.h" +#include "NextBotLocomotionInterface.h" +#include "NextBotBodyInterface.h" + +#include "tier0/vprof.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// how far a bot must move to not be considered "stuck" +#define STUCK_RADIUS 100.0f + + + +//---------------------------------------------------------------------------------------------------------- +/** + * Reset to initial state + */ +ILocomotion::ILocomotion( INextBot *bot ) : INextBotComponent( bot ) +{ + Reset(); +} + +ILocomotion::~ILocomotion() +{ +} + +void ILocomotion::Reset( void ) +{ + INextBotComponent::Reset(); + + m_motionVector = Vector( 1.0f, 0.0f, 0.0f ); + m_speed = 0.0f; + m_groundMotionVector = m_motionVector; + m_groundSpeed = m_speed; + + m_moveRequestTimer.Invalidate(); + + m_isStuck = false; + m_stuckTimer.Invalidate(); + m_stuckPos = vec3_origin; +} + + +//---------------------------------------------------------------------------------------------------------- +/** + * Update internal state + */ +void ILocomotion::Update( void ) +{ + StuckMonitor(); + + // maintain motion vector and speed values + const Vector &vel = GetVelocity(); + m_speed = vel.Length(); + m_groundSpeed = vel.AsVector2D().Length(); + + const float velocityThreshold = 10.0f; + if ( m_speed > velocityThreshold ) + { + m_motionVector = vel / m_speed; + } + + if ( m_groundSpeed > velocityThreshold ) + { + m_groundMotionVector.x = vel.x / m_groundSpeed; + m_groundMotionVector.y = vel.y / m_groundSpeed; + m_groundMotionVector.z = 0.0f; + } + + if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) + { + // show motion vector + NDebugOverlay::HorzArrow( GetFeet(), GetFeet() + 25.0f * m_groundMotionVector, 3.0f, 100, 255, 0, 255, true, 0.1f ); + NDebugOverlay::HorzArrow( GetFeet(), GetFeet() + 25.0f * m_motionVector, 5.0f, 255, 255, 0, 255, true, 0.1f ); + } +} + + +//---------------------------------------------------------------------------- +void ILocomotion::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; + + // + // Stand or crouch as needed + // + + // get bounding limits, ignoring step-upable height + const Vector &mins = body->GetHullMins() + Vector( 0, 0, GetStepHeight() ); + + const float halfSize = body->GetHullWidth()/2.0f; + Vector standMaxs( halfSize, halfSize, body->GetStandHullHeight() ); + + trace_t trace; + NextBotTraversableTraceFilter filter( GetBot(), ILocomotion::IMMEDIATELY ); + + // snap forward movement vector along floor + const Vector &groundNormal = GetGroundNormal(); + const Vector &feet = GetFeet(); + Vector moveDir = moveGoal - feet; + float moveLength = moveDir.NormalizeInPlace(); + Vector left( -moveDir.y, moveDir.x, 0.0f ); + Vector goal = feet + moveLength * CrossProduct( left, groundNormal ).Normalized(); + + TraceHull( feet, goal, mins, standMaxs, body->GetSolidMask(), &filter, &trace ); + + if ( trace.fraction >= 1.0f && !trace.startsolid ) + { + // no collision while standing + if ( body->IsActualPosture( IBody::CROUCH ) ) + { + body->SetDesiredPosture( IBody::STAND ); + } + return; + } + + if ( body->IsActualPosture( IBody::CROUCH ) ) + return; + + // crouch hull check + Vector crouchMaxs( halfSize, halfSize, body->GetCrouchHullHeight() ); + + TraceHull( feet, goal, mins, crouchMaxs, body->GetSolidMask(), &filter, &trace ); + + if ( trace.fraction >= 1.0f && !trace.startsolid ) + { + // no collision while crouching + body->SetDesiredPosture( IBody::CROUCH ); + } +} + + +//---------------------------------------------------------------------------------------------------------- +/** + * Move directly towards the given position + */ +void ILocomotion::Approach( const Vector &goalPos, float goalWeight ) +{ + // there is a desire to move + m_moveRequestTimer.Start(); +} + + +//---------------------------------------------------------------------------------------------------------- +/** + * Move the bot to the precise given position immediately + */ +void ILocomotion::DriveTo( const Vector &pos ) +{ + // there is a desire to move + m_moveRequestTimer.Start(); +} + + +//---------------------------------------------------------------------------------------------------------- +/** + * Return true if this locomotor could potentially move along the line given. + * If false is returned, fraction of walkable ray is returned in 'fraction' + */ +bool ILocomotion::IsPotentiallyTraversable( const Vector &from, const Vector &to, TraverseWhenType when, float *fraction ) const +{ + VPROF_BUDGET( "Locomotion::IsPotentiallyTraversable", "NextBotExpensive" ); + + // if 'to' is high above us, it's not directly traversable + // Adding a bit of fudge room to allow for floating point roundoff errors + if ( ( to.z - from.z ) > GetMaxJumpHeight() + 0.1f ) + { + Vector along = to - from; + along.NormalizeInPlace(); + if ( along.z > GetTraversableSlopeLimit() ) + { + if ( fraction ) + { + *fraction = 0.0f; + } + return false; + } + } + + trace_t result; + NextBotTraversableTraceFilter filter( GetBot(), when ); + + // use a small hull since we cannot simulate collision resolution and avoidance along the way + const float probeSize = 0.25f * GetBot()->GetBodyInterface()->GetHullWidth(); // Cant be TOO small, or open stairwells/grates/etc will cause problems + const float probeZ = GetStepHeight(); + + Vector hullMin( -probeSize, -probeSize, probeZ ); + Vector hullMax( probeSize, probeSize, GetBot()->GetBodyInterface()->GetCrouchHullHeight() ); + TraceHull( from, to, hullMin, hullMax, GetBot()->GetBodyInterface()->GetSolidMask(), &filter, &result ); + +/* + if ( result.DidHit() ) + { + NDebugOverlay::SweptBox( from, result.endpos, hullMin, hullMax, vec3_angle, 255, 0, 0, 255, 9999.9f ); + NDebugOverlay::SweptBox( result.endpos, to, hullMin, hullMax, vec3_angle, 255, 255, 0, 255, 9999.9f ); + } + else + { + NDebugOverlay::SweptBox( from, to, hullMin, hullMax, vec3_angle, 255, 255, 0, 255, 0.1f ); + } +*/ + + if ( fraction ) + { + *fraction = result.fraction; + } + + return ( result.fraction >= 1.0f ) && ( !result.startsolid ); +} + + +//---------------------------------------------------------------------------------------------------------- +/** + * Return true if there is a possible "gap" that will need to be jumped over + * If true is returned, fraction of ray before gap is returned in 'fraction' + */ +bool ILocomotion::HasPotentialGap( const Vector &from, const Vector &desiredTo, float *fraction ) const +{ + VPROF_BUDGET( "Locomotion::HasPotentialGap", "NextBot" ); + + // find section of this ray that is actually traversable + float traversableFraction; + IsPotentiallyTraversable( from, desiredTo, IMMEDIATELY, &traversableFraction ); + + // compute end of traversable ray + Vector to = from + ( desiredTo - from ) * traversableFraction; + + Vector forward = to - from; + float length = forward.NormalizeInPlace(); + + IBody *body = GetBot()->GetBodyInterface(); + + float step = body->GetHullWidth()/2.0f; + + // scan along the line checking for gaps + Vector pos = from; + Vector delta = step * forward; + for( float t = 0.0f; t < (length + step); t += step ) + { + if ( IsGap( pos, forward ) ) + { + if ( fraction ) + { + *fraction = ( t - step ) / ( length + step ); + } + + return true; + } + + pos += delta; + } + + if ( fraction ) + { + *fraction = 1.0f; + } + + return false; +} + + +//---------------------------------------------------------------------------------------------------------- +/** + * Return true if there is a "gap" here when moving in the given direction. + * A "gap" is a vertical dropoff that is too high to jump back up to. + */ +bool ILocomotion::IsGap( const Vector &pos, const Vector &forward ) const +{ + VPROF_BUDGET( "Locomotion::IsGap", "NextBotSpiky" ); + + IBody *body = GetBot()->GetBodyInterface(); + + //float halfWidth = ( body ) ? body->GetHullWidth()/2.0f : 1.0f; + + // can't really jump effectively when crouched anyhow + //float hullHeight = ( body ) ? body->GetStandHullHeight() : 1.0f; + + // use a small hull since we cannot simulate collision resolution and avoidance along the way + const float halfWidth = 1.0f; + const float hullHeight = 1.0f; + + unsigned int mask = ( body ) ? body->GetSolidMask() : MASK_PLAYERSOLID; + + trace_t ground; + + NextBotTraceFilterIgnoreActors filter( GetBot()->GetEntity(), COLLISION_GROUP_NONE ); + + TraceHull( pos + Vector( 0, 0, GetStepHeight() ), // start up a bit to handle rough terrain + pos + Vector( 0, 0, -GetMaxJumpHeight() ), + Vector( -halfWidth, -halfWidth, 0 ), Vector( halfWidth, halfWidth, hullHeight ), + mask, &filter, &ground ); + +// int r,g,b; +// +// if ( ground.fraction >= 1.0f && !ground.startsolid ) +// { +// r = 255, g = 0, b = 0; +// } +// else +// { +// r = 0, g = 255, b = 0; +// } +// +// NDebugOverlay::SweptBox( pos, +// pos + Vector( 0, 0, -GetStepHeight() ), +// Vector( -halfWidth, -halfWidth, 0 ), Vector( halfWidth, halfWidth, hullHeight ), +// vec3_angle, +// r, g, b, 255, 3.0f ); + + // if trace hit nothing, there's a gap ahead of us + return ( ground.fraction >= 1.0f && !ground.startsolid ); +} + + +//---------------------------------------------------------------------------------------------------------- +bool ILocomotion::IsEntityTraversable( CBaseEntity *obstacle, TraverseWhenType when ) const +{ + if ( obstacle->IsWorld() ) + return false; + + // assume bot will open a door in its path + if ( FClassnameIs( obstacle, "prop_door*" ) || FClassnameIs( obstacle, "func_door*" ) ) + { + CBasePropDoor *door = dynamic_cast< CBasePropDoor * >( obstacle ); + + if ( door && door->IsDoorOpen() ) + { + // open doors are obstacles + return false; + } + + return true; + } + + // if we hit a clip brush, ignore it if it is not BRUSHSOLID_ALWAYS + if ( FClassnameIs( obstacle, "func_brush" ) ) + { + CFuncBrush *brush = (CFuncBrush *)obstacle; + + switch ( brush->m_iSolidity ) + { + case CFuncBrush::BRUSHSOLID_ALWAYS: + return false; + case CFuncBrush::BRUSHSOLID_NEVER: + return true; + case CFuncBrush::BRUSHSOLID_TOGGLE: + return true; + } + } + + if ( when == IMMEDIATELY ) + { + // special rules in specific games can immediately break some breakables, etc. + return false; + } + + // assume bot will EVENTUALLY break breakables in its path + return GetBot()->IsAbleToBreak( obstacle ); +} + + +//-------------------------------------------------------------------------------------------------------------- +bool ILocomotion::IsAreaTraversable( const CNavArea *baseArea ) const +{ + return !baseArea->IsBlocked( GetBot()->GetEntity()->GetTeamNumber() ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Reset stuck status to un-stuck + */ +void ILocomotion::ClearStuckStatus( const char *reason ) +{ + if ( IsStuck() ) + { + m_isStuck = false; + + // tell other components we're no longer stuck + GetBot()->OnUnStuck(); + } + + // always reset stuck monitoring data in case we cleared preemptively are were not yet stuck + m_stuckPos = GetFeet(); + m_stuckTimer.Start(); + + if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) + { + DevMsg( "%3.2f: ClearStuckStatus: %s %s\n", gpGlobals->curtime, GetBot()->GetDebugIdentifier(), reason ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Stuck check + */ +void ILocomotion::StuckMonitor( void ) +{ + // a timer is needed to smooth over a few frames of inactivity due to state changes, etc. + // we only want to detect idle situations when the bot really doesn't "want" to move. + const float idleTime = 0.25f; + if ( m_moveRequestTimer.IsGreaterThen( idleTime ) ) + { + // we have no desire to move, and therefore cannot emit stuck events + + // prepare our internal state for when the bot starts to move next + m_stuckPos = GetFeet(); + m_stuckTimer.Start(); + + return; + } + +// if ( !IsOnGround() ) +// { +// // can't be stuck when in-air +// ClearStuckStatus( "Off the ground" ); +// return; +// } + +// if ( IsUsingLadder() ) +// { +// // can't be stuck when on a ladder (for now) +// ClearStuckStatus( "On a ladder" ); +// return; +// } + + if ( IsStuck() ) + { + // we are/were stuck - have we moved enough to consider ourselves "dislodged" + if ( GetBot()->IsRangeGreaterThan( m_stuckPos, STUCK_RADIUS ) ) + { + // we've just become un-stuck + ClearStuckStatus( "UN-STUCK" ); + } + else + { + // still stuck - periodically resend the event + if ( m_stillStuckTimer.IsElapsed() ) + { + m_stillStuckTimer.Start( 1.0f ); + + if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) + { + DevMsg( "%3.2f: %s STILL STUCK\n", gpGlobals->curtime, GetBot()->GetDebugIdentifier() ); + NDebugOverlay::Circle( m_stuckPos + Vector( 0, 0, 5.0f ), QAngle( -90.0f, 0, 0 ), 5.0f, 255, 0, 0, 255, true, 1.0f ); + } + + GetBot()->OnStuck(); + } + } + } + else + { + // we're not stuck - yet + + if ( /*IsClimbingOrJumping() || */GetBot()->IsRangeGreaterThan( m_stuckPos, STUCK_RADIUS ) ) + { + // we have moved - reset anchor + m_stuckPos = GetFeet(); + + if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) + { + NDebugOverlay::Cross3D( m_stuckPos, 3.0f, 255, 0, 255, true, 3.0f ); + } + + m_stuckTimer.Start(); + } + else + { + // within stuck range of anchor. if we've been here too long, we're stuck + if ( GetBot()->IsDebugging( NEXTBOT_LOCOMOTION ) ) + { + NDebugOverlay::Line( GetBot()->GetEntity()->WorldSpaceCenter(), m_stuckPos, 255, 0, 255, true, 0.1f ); + } + + float minMoveSpeed = 0.1f * GetDesiredSpeed() + 0.1f; + float escapeTime = STUCK_RADIUS / minMoveSpeed; + if ( m_stuckTimer.IsGreaterThen( escapeTime ) ) + { + // we have taken too long - we're stuck + m_isStuck = true; + + if ( GetBot()->IsDebugging( NEXTBOT_ERRORS ) ) + { + DevMsg( "%3.2f: %s STUCK at position( %3.2f, %3.2f, %3.2f )\n", gpGlobals->curtime, GetBot()->GetDebugIdentifier(), m_stuckPos.x, m_stuckPos.y, m_stuckPos.z ); + + NDebugOverlay::Circle( m_stuckPos + Vector( 0, 0, 15.0f ), QAngle( -90.0f, 0, 0 ), 3.0f, 255, 255, 0, 255, true, 1.0f ); + NDebugOverlay::Circle( m_stuckPos + Vector( 0, 0, 5.0f ), QAngle( -90.0f, 0, 0 ), 5.0f, 255, 0, 0, 255, true, 9999999.9f ); + } + + // tell other components we've become stuck + GetBot()->OnStuck(); + } + } + } +} + + +//-------------------------------------------------------------------------------------------------------------- +const Vector &ILocomotion::GetFeet( void ) const +{ + return GetBot()->GetEntity()->GetAbsOrigin(); +} + -- cgit v1.2.3