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.
882 lines
23 KiB
882 lines
23 KiB
3 years ago
|
// 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|