summaryrefslogtreecommitdiff
path: root/game/server/NextBot/Player/NextBotPlayerBody.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/NextBot/Player/NextBotPlayerBody.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'game/server/NextBot/Player/NextBotPlayerBody.cpp')
-rw-r--r--game/server/NextBot/Player/NextBotPlayerBody.cpp881
1 files changed, 881 insertions, 0 deletions
diff --git a/game/server/NextBot/Player/NextBotPlayerBody.cpp b/game/server/NextBot/Player/NextBotPlayerBody.cpp
new file mode 100644
index 0000000..246b6fa
--- /dev/null
+++ b/game/server/NextBot/Player/NextBotPlayerBody.cpp
@@ -0,0 +1,881 @@
+// NextBotPlayerBody.cpp
+// Implementation of Body interface for CBasePlayer-derived classes
+// Author: Michael Booth, October 2006
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include "cbase.h"
+
+#include "NextBot.h"
+#include "NextBotPlayerBody.h"
+#include "NextBotPlayer.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+ConVar nb_saccade_time( "nb_saccade_time", "0.1", FCVAR_CHEAT );
+ConVar nb_saccade_speed( "nb_saccade_speed", "1000", FCVAR_CHEAT );
+ConVar nb_head_aim_steady_max_rate( "nb_head_aim_steady_max_rate", "100", FCVAR_CHEAT );
+ConVar nb_head_aim_settle_duration( "nb_head_aim_settle_duration", "0.3", FCVAR_CHEAT );
+ConVar nb_head_aim_resettle_angle( "nb_head_aim_resettle_angle", "100", FCVAR_CHEAT, "After rotating through this angle, the bot pauses to 'recenter' its virtual mouse on its virtual mousepad" );
+ConVar nb_head_aim_resettle_time( "nb_head_aim_resettle_time", "0.3", FCVAR_CHEAT, "How long the bot pauses to 'recenter' its virtual mouse on its virtual mousepad" );
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * A useful reply for IBody::AimHeadTowards. When the
+ * head is aiming on target, press the fire button.
+ */
+void PressFireButtonReply::OnSuccess( INextBot *bot )
+{
+ INextBotPlayerInput *playerInput = dynamic_cast< INextBotPlayerInput * >( bot->GetEntity() );
+ if ( playerInput )
+ {
+ playerInput->PressFireButton();
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * A useful reply for IBody::AimHeadTowards. When the
+ * head is aiming on target, press the alternate fire button.
+ */
+void PressAltFireButtonReply::OnSuccess( INextBot *bot )
+{
+ INextBotPlayerInput *playerInput = dynamic_cast< INextBotPlayerInput * >( bot->GetEntity() );
+ if ( playerInput )
+ {
+ playerInput->PressMeleeButton();
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * A useful reply for IBody::AimHeadTowards. When the
+ * head is aiming on target, press the jump button.
+ */
+void PressJumpButtonReply::OnSuccess( INextBot *bot )
+{
+ INextBotPlayerInput *playerInput = dynamic_cast< INextBotPlayerInput * >( bot->GetEntity() );
+ if ( playerInput )
+ {
+ playerInput->PressJumpButton();
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------
+//-----------------------------------------------------------------------------------------------
+PlayerBody::PlayerBody( INextBot *bot ) : IBody( bot )
+{
+ m_player = static_cast< CBasePlayer * >( bot->GetEntity() );
+}
+
+
+//-----------------------------------------------------------------------------------------------
+PlayerBody::~PlayerBody()
+{
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * reset to initial state
+ */
+void PlayerBody::Reset( void )
+{
+ m_posture = STAND;
+
+ m_lookAtPos = vec3_origin;
+ m_lookAtSubject = NULL;
+ m_lookAtReplyWhenAimed = NULL;
+ m_lookAtVelocity = vec3_origin;
+ m_lookAtExpireTimer.Invalidate();
+
+ m_lookAtPriority = BORING;
+ m_lookAtExpireTimer.Invalidate();
+ m_lookAtDurationTimer.Invalidate();
+ m_isSightedIn = false;
+ m_hasBeenSightedIn = false;
+ m_headSteadyTimer.Invalidate();
+ m_priorAngles = vec3_angle;
+ m_anchorRepositionTimer.Invalidate();
+ m_anchorForward = vec3_origin;
+}
+
+ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Update internal state.
+ * Do this every tick to keep head aims smooth and accurate
+ */
+void PlayerBody::Upkeep( void )
+{
+ // If mimicking the player, don't modify the view angles.
+ static ConVarRef bot_mimic( "bot_mimic" );
+ if ( bot_mimic.IsValid() && bot_mimic.GetBool() )
+ return;
+
+ const float deltaT = gpGlobals->frametime;
+ if ( deltaT < 0.00001f )
+ {
+ return;
+ }
+
+ CBasePlayer *player = ( CBasePlayer * )GetBot()->GetEntity();
+
+ // get current view angles
+ QAngle currentAngles = player->EyeAngles() + player->GetPunchAngle();
+
+ // track when our head is "steady"
+ bool isSteady = true;
+
+ float actualPitchRate = AngleDiff( currentAngles.x, m_priorAngles.x );
+ if ( abs( actualPitchRate ) > nb_head_aim_steady_max_rate.GetFloat() * deltaT )
+ {
+ isSteady = false;
+ }
+ else
+ {
+ float actualYawRate = AngleDiff( currentAngles.y, m_priorAngles.y );
+
+ if ( abs( actualYawRate ) > nb_head_aim_steady_max_rate.GetFloat() * deltaT )
+ {
+ isSteady = false;
+ }
+ }
+
+ if ( isSteady )
+ {
+ if ( !m_headSteadyTimer.HasStarted() )
+ {
+ m_headSteadyTimer.Start();
+ }
+ }
+ else
+ {
+ m_headSteadyTimer.Invalidate();
+ }
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ if ( IsHeadSteady() )
+ {
+ const float maxTime = 3.0f;
+ float t = GetHeadSteadyDuration() / maxTime;
+ t = clamp( t, 0.f, 1.0f );
+ NDebugOverlay::Circle( player->EyePosition(), t * 10.0f, 0, 255, 0, 255, true, 2.0f * deltaT );
+ }
+ }
+
+ m_priorAngles = currentAngles;
+
+
+ // if our current look-at has expired, don't change our aim further
+ if ( m_hasBeenSightedIn && m_lookAtExpireTimer.IsElapsed() )
+ {
+ return;
+ }
+
+ // simulate limited range of mouse movements
+ // compute the angle change from "center"
+ const Vector &forward = GetViewVector();
+ float deltaAngle = RAD2DEG( acos( DotProduct( forward, m_anchorForward ) ) );
+ if ( deltaAngle > nb_head_aim_resettle_angle.GetFloat() )
+ {
+ // time to recenter our 'virtual mouse'
+ m_anchorRepositionTimer.Start( RandomFloat( 0.9f, 1.1f ) * nb_head_aim_resettle_time.GetFloat() );
+ m_anchorForward = forward;
+ return;
+ }
+
+ // if we're currently recentering our "virtual mouse", wait
+ if ( m_anchorRepositionTimer.HasStarted() && !m_anchorRepositionTimer.IsElapsed() )
+ {
+ return;
+ }
+ m_anchorRepositionTimer.Invalidate();
+
+
+ // if we have a subject, update lookat point
+ CBaseEntity *subject = m_lookAtSubject;
+ if ( subject )
+ {
+ if ( m_lookAtTrackingTimer.IsElapsed() )
+ {
+ // update subject tracking by periodically estimating linear aim velocity, allowing for "slop" between updates
+ Vector desiredLookAtPos;
+
+ if ( subject->MyCombatCharacterPointer() )
+ {
+ desiredLookAtPos = GetBot()->GetIntentionInterface()->SelectTargetPoint( GetBot(), subject->MyCombatCharacterPointer() );
+ }
+ else
+ {
+ desiredLookAtPos = subject->WorldSpaceCenter();
+ }
+
+ desiredLookAtPos += GetHeadAimSubjectLeadTime() * subject->GetAbsVelocity();
+
+ Vector errorVector = desiredLookAtPos - m_lookAtPos;
+ float error = errorVector.NormalizeInPlace();
+
+ float trackingInterval = GetHeadAimTrackingInterval();
+ if ( trackingInterval < deltaT )
+ {
+ trackingInterval = deltaT;
+ }
+
+ float errorVel = error / trackingInterval;
+
+ m_lookAtVelocity = ( errorVel * errorVector ) + subject->GetAbsVelocity();
+
+ m_lookAtTrackingTimer.Start( RandomFloat( 0.8f, 1.2f ) * trackingInterval );
+ }
+
+ m_lookAtPos += deltaT * m_lookAtVelocity;
+ }
+
+
+ // aim view towards last look at point
+ Vector to = m_lookAtPos - GetEyePosition();
+ to.NormalizeInPlace();
+
+ QAngle desiredAngles;
+ VectorAngles( to, desiredAngles );
+
+ QAngle angles;
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ NDebugOverlay::Line( GetEyePosition(), GetEyePosition() + 100.0f * forward, 255, 255, 0, false, 2.0f * deltaT );
+
+ float thickness = isSteady ? 2.0f : 3.0f;
+ int r = m_isSightedIn ? 255 : 0;
+ int g = subject ? 255 : 0;
+ NDebugOverlay::HorzArrow( GetEyePosition(), m_lookAtPos, thickness, r, g, 255, 255, false, 2.0f * deltaT );
+ }
+
+
+ const float onTargetTolerance = 0.98f;
+ float dot = DotProduct( forward, to );
+ if ( dot > onTargetTolerance )
+ {
+ // on target
+ m_isSightedIn = true;
+
+ if ( !m_hasBeenSightedIn )
+ {
+ m_hasBeenSightedIn = true;
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ ConColorMsg( Color( 255, 100, 0, 255 ), "%3.2f: %s Look At SIGHTED IN\n",
+ gpGlobals->curtime,
+ m_player->GetPlayerName() );
+ }
+ }
+
+ if ( m_lookAtReplyWhenAimed )
+ {
+ m_lookAtReplyWhenAimed->OnSuccess( GetBot() );
+ m_lookAtReplyWhenAimed = NULL;
+ }
+ }
+ else
+ {
+ // off target
+ m_isSightedIn = false;
+ }
+
+
+ // rotate view at a rate proportional to how far we have to turn
+ // max rate if we need to turn around
+ // want first derivative continuity of rate as our aim hits to avoid pop
+ float approachRate = GetMaxHeadAngularVelocity();
+
+ const float easeOut = 0.7f;
+ if ( dot > easeOut )
+ {
+ float t = RemapVal( dot, easeOut, 1.0f, 1.0f, 0.02f );
+ const float halfPI = 1.57f;
+ approachRate *= sin( halfPI * t );
+ }
+
+ const float easeInTime = 0.25f;
+ if ( m_lookAtDurationTimer.GetElapsedTime() < easeInTime )
+ {
+ approachRate *= m_lookAtDurationTimer.GetElapsedTime() / easeInTime;
+ }
+
+ angles.y = ApproachAngle( desiredAngles.y, currentAngles.y, approachRate * deltaT );
+ angles.x = ApproachAngle( desiredAngles.x, currentAngles.x, 0.5f * approachRate * deltaT );
+ angles.z = 0.0f;
+
+ // back out "punch angle"
+ angles -= player->GetPunchAngle();
+
+ angles.x = AngleNormalize( angles.x );
+ angles.y = AngleNormalize( angles.y );
+
+ player->SnapEyeAngles( angles );
+}
+
+
+//-----------------------------------------------------------------------------------------------
+bool PlayerBody::SetPosition( const Vector &pos )
+{
+ m_player->SetAbsOrigin( pos );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return the eye position of the bot in world coordinates
+ */
+const Vector &PlayerBody::GetEyePosition( void ) const
+{
+ m_eyePos = m_player->EyePosition();
+ return m_eyePos;
+}
+
+
+CBaseEntity *PlayerBody::GetEntity( void )
+{
+ return m_player;
+}
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return the view unit direction vector in world coordinates
+ */
+const Vector &PlayerBody::GetViewVector( void ) const
+{
+ m_player->EyeVectors( &m_viewVector );
+ return m_viewVector;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Aim the bot's head towards the given goal
+ */
+void PlayerBody::AimHeadTowards( const Vector &lookAtPos, LookAtPriorityType priority, float duration, INextBotReply *replyWhenAimed, const char *reason )
+{
+ if ( duration <= 0.0f )
+ {
+ duration = 0.1f;
+ }
+
+ // don't spaz our aim around
+ if ( m_lookAtPriority == priority )
+ {
+ if ( !IsHeadSteady() || GetHeadSteadyDuration() < nb_head_aim_settle_duration.GetFloat() )
+ {
+ // we're still finishing a look-at at the same priority
+ if ( replyWhenAimed )
+ {
+ replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
+ }
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - previous aim not %s\n",
+ gpGlobals->curtime,
+ m_player->GetPlayerName(),
+ reason,
+ IsHeadSteady() ? "settled long enough" : "head-steady" );
+ }
+ return;
+ }
+ }
+
+ // don't short-circuit if "sighted in" to avoid rapid view jitter
+ if ( m_lookAtPriority > priority && !m_lookAtExpireTimer.IsElapsed() )
+ {
+ // higher priority lookat still ongoing
+ if ( replyWhenAimed )
+ {
+ replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
+ }
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - higher priority aim in progress\n",
+ gpGlobals->curtime,
+ m_player->GetPlayerName(),
+ reason );
+ }
+ return;
+ }
+
+ if ( m_lookAtReplyWhenAimed )
+ {
+ // in-process aim was interrupted
+ m_lookAtReplyWhenAimed->OnFail( GetBot(), INextBotReply::INTERRUPTED );
+ }
+
+ m_lookAtReplyWhenAimed = replyWhenAimed;
+ m_lookAtExpireTimer.Start( duration );
+
+ // if given the same point, just update priority
+ const float epsilon = 1.0f;
+ if ( ( m_lookAtPos - lookAtPos ).IsLengthLessThan( epsilon ) )
+ {
+ m_lookAtPriority = priority;
+ return;
+ }
+
+ // new look-at point
+
+ m_lookAtPos = lookAtPos;
+ m_lookAtSubject = NULL;
+
+ m_lookAtPriority = priority;
+ m_lookAtDurationTimer.Start();
+
+ // do NOT clear this here, or continuous calls to AimHeadTowards will keep IsHeadAimingOnTarget returning false all of the time
+ // m_isSightedIn = false;
+
+ m_hasBeenSightedIn = false;
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ NDebugOverlay::Cross3D( lookAtPos, 2.0f, 255, 255, 100, true, 2.0f * duration );
+
+ const char *priName = "";
+ switch( priority )
+ {
+ case BORING: priName = "BORING"; break;
+ case INTERESTING: priName = "INTERESTING"; break;
+ case IMPORTANT: priName = "IMPORTANT"; break;
+ case CRITICAL: priName = "CRITICAL"; break;
+ }
+
+ ConColorMsg( Color( 255, 100, 0, 255 ), "%3.2f: %s Look At ( %g, %g, %g ) for %3.2f s, Pri = %s, Reason = %s\n",
+ gpGlobals->curtime,
+ m_player->GetPlayerName(),
+ lookAtPos.x, lookAtPos.y, lookAtPos.z,
+ duration,
+ priName,
+ ( reason ) ? reason : "" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Aim the bot's head towards the given goal
+ */
+void PlayerBody::AimHeadTowards( CBaseEntity *subject, LookAtPriorityType priority, float duration, INextBotReply *replyWhenAimed, const char *reason )
+{
+ if ( duration <= 0.0f )
+ {
+ duration = 0.1f;
+ }
+
+ if ( subject == NULL )
+ {
+ return;
+ }
+
+ // don't spaz our aim around
+ if ( m_lookAtPriority == priority )
+ {
+ if ( !IsHeadSteady() || GetHeadSteadyDuration() < nb_head_aim_settle_duration.GetFloat() )
+ {
+ // we're still finishing a look-at at the same priority
+ if ( replyWhenAimed )
+ {
+ replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
+ }
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - previous aim not %s\n",
+ gpGlobals->curtime,
+ m_player->GetPlayerName(),
+ reason,
+ IsHeadSteady() ? "head-steady" : "settled long enough" );
+ }
+ return;
+ }
+ }
+
+ // don't short-circuit if "sighted in" to avoid rapid view jitter
+ if ( m_lookAtPriority > priority && !m_lookAtExpireTimer.IsElapsed() )
+ {
+ // higher priority lookat still ongoing
+ if ( replyWhenAimed )
+ {
+ replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
+ }
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - higher priority aim in progress\n",
+ gpGlobals->curtime,
+ m_player->GetPlayerName(),
+ reason );
+ }
+ return;
+ }
+
+ if ( m_lookAtReplyWhenAimed )
+ {
+ // in-process aim was interrupted
+ m_lookAtReplyWhenAimed->OnFail( GetBot(), INextBotReply::INTERRUPTED );
+ }
+
+ m_lookAtReplyWhenAimed = replyWhenAimed;
+ m_lookAtExpireTimer.Start( duration );
+
+ // if given the same subject, just update priority
+ if ( subject == m_lookAtSubject )
+ {
+ m_lookAtPriority = priority;
+ return;
+ }
+
+ // new subject
+ m_lookAtSubject = subject;
+
+#ifdef REFACTOR_FOR_CLIENT_SIDE_EYE_TRACKING
+ CBasePlayer *pMyPlayer = static_cast< CBasePlayer * >( GetEntity() );
+ if ( subject->IsPlayer() )
+ {
+ // looking at a player, look at their eye position
+ TerrorPlayer *pMyTarget = ToTerrorPlayer( subject );
+ m_lookAtPos = subject->EyePosition();
+ if(pMyPlayer)
+ {
+ pMyPlayer->SetLookatPlayer( pMyTarget );
+ }
+ }
+ else
+ {
+ // not looking at a player
+ m_lookAtPos = subject->WorldSpaceCenter();
+ if(pMyPlayer)
+ {
+ pMyPlayer->SetLookatPlayer( NULL );
+ }
+ }
+#endif
+
+ m_lookAtPriority = priority;
+ m_lookAtDurationTimer.Start();
+
+ // do NOT clear this here, or continuous calls to AimHeadTowards will keep IsHeadAimingOnTarget returning false all of the time
+ // m_isSightedIn = false;
+
+ m_hasBeenSightedIn = false;
+
+ if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
+ {
+ NDebugOverlay::Cross3D( m_lookAtPos, 2.0f, 100, 100, 100, true, duration );
+
+ const char *priName = "";
+ switch( priority )
+ {
+ case BORING: priName = "BORING"; break;
+ case INTERESTING: priName = "INTERESTING"; break;
+ case IMPORTANT: priName = "IMPORTANT"; break;
+ case CRITICAL: priName = "CRITICAL"; break;
+ }
+
+ ConColorMsg( Color( 255, 100, 0, 255 ), "%3.2f: %s Look At subject %s for %3.2f s, Pri = %s, Reason = %s\n",
+ gpGlobals->curtime,
+ m_player->GetPlayerName(),
+ subject->GetClassname(),
+ duration,
+ priName,
+ ( reason ) ? reason : "" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if head is not rapidly turning to look somewhere else
+ */
+bool PlayerBody::IsHeadSteady( void ) const
+{
+ return m_headSteadyTimer.HasStarted();
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return the duration that the bot's head has been on-target
+ */
+float PlayerBody::GetHeadSteadyDuration( void ) const
+{
+ // return ( IsHeadAimingOnTarget() ) ? m_headSteadyTimer.GetElapsedTime() : 0.0f;
+ return m_headSteadyTimer.HasStarted() ? m_headSteadyTimer.GetElapsedTime() : 0.0f;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+// Clear out currently pending replyWhenAimed callback
+void PlayerBody::ClearPendingAimReply( void )
+{
+ m_lookAtReplyWhenAimed = NULL;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+float PlayerBody::GetMaxHeadAngularVelocity( void ) const
+{
+ return nb_saccade_speed.GetFloat();
+}
+
+
+//-----------------------------------------------------------------------------------------------
+bool PlayerBody::StartActivity( Activity act, unsigned int flags )
+{
+ // player animation state is controlled on the client
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return currently animating activity
+ */
+Activity PlayerBody::GetActivity( void ) const
+{
+ return ACT_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if currently animating activity matches the given one
+ */
+bool PlayerBody::IsActivity( Activity act ) const
+{
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if currently animating activity has any of the given flags
+ */
+bool PlayerBody::HasActivityType( unsigned int flags ) const
+{
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Request a posture change
+ */
+void PlayerBody::SetDesiredPosture( PostureType posture )
+{
+ m_posture = posture;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Get posture body is trying to assume
+ */
+IBody::PostureType PlayerBody::GetDesiredPosture( void ) const
+{
+ return m_posture;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if body is trying to assume this posture
+ */
+bool PlayerBody::IsDesiredPosture( PostureType posture ) const
+{
+ return ( posture == m_posture );
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if body's actual posture matches its desired posture
+ */
+bool PlayerBody::IsInDesiredPosture( void ) const
+{
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return body's current actual posture
+ */
+IBody::PostureType PlayerBody::GetActualPosture( void ) const
+{
+ return m_posture;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if body is actually in the given posture
+ */
+bool PlayerBody::IsActualPosture( PostureType posture ) const
+{
+ return ( posture == m_posture );
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if body's current posture allows it to move around the world
+ */
+bool PlayerBody::IsPostureMobile( void ) const
+{
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if body's posture is in the process of changing to new posture
+ */
+bool PlayerBody::IsPostureChanging( void ) const
+{
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Arousal level change
+ */
+void PlayerBody::SetArousal( ArousalType arousal )
+{
+ m_arousal = arousal;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Get arousal level
+ */
+IBody::ArousalType PlayerBody::GetArousal( void ) const
+{
+ return m_arousal;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return true if body is at this arousal level
+ */
+bool PlayerBody::IsArousal( ArousalType arousal ) const
+{
+ return ( arousal == m_arousal );
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Width of bot's collision hull in XY plane
+ */
+float PlayerBody::GetHullWidth( void ) const
+{
+ return VEC_HULL_MAX_SCALED( m_player ).x - VEC_HULL_MIN_SCALED( m_player ).x;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Height of bot's current collision hull based on posture
+ */
+float PlayerBody::GetHullHeight( void ) const
+{
+ if ( m_posture == CROUCH )
+ {
+ return GetCrouchHullHeight();
+ }
+
+ return GetStandHullHeight();
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Height of bot's collision hull when standing
+ */
+float PlayerBody::GetStandHullHeight( void ) const
+{
+ return VEC_HULL_MAX_SCALED( m_player ).z - VEC_HULL_MIN_SCALED( m_player ).z;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Height of bot's collision hull when crouched
+ */
+float PlayerBody::GetCrouchHullHeight( void ) const
+{
+ return VEC_DUCK_HULL_MAX_SCALED( m_player ).z - VEC_DUCK_HULL_MIN_SCALED( m_player ).z;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return current collision hull minimums based on actual body posture
+ */
+const Vector &PlayerBody::GetHullMins( void ) const
+{
+ if ( m_posture == CROUCH )
+ {
+ m_hullMins = VEC_DUCK_HULL_MIN_SCALED( m_player );
+ }
+ else
+ {
+ m_hullMins = VEC_HULL_MIN_SCALED( m_player );
+ }
+
+ return m_hullMins;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return current collision hull maximums based on actual body posture
+ */
+const Vector &PlayerBody::GetHullMaxs( void ) const
+{
+ if ( m_posture == CROUCH )
+ {
+ m_hullMaxs = VEC_DUCK_HULL_MAX_SCALED( m_player );
+ }
+ else
+ {
+ m_hullMaxs = VEC_HULL_MAX_SCALED( m_player );
+ }
+
+ return m_hullMaxs;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+/**
+ * Return the bot's collision mask (hack until we get a general hull trace abstraction here or in the locomotion interface)
+ */
+unsigned int PlayerBody::GetSolidMask( void ) const
+{
+ return ( m_player ) ? m_player->PlayerSolidMask() : MASK_PLAYERSOLID;
+}
+
+
+
+
+