initial
This commit is contained in:
22
game/server/NextBot/Player/NextBotPlayer.cpp
Normal file
22
game/server/NextBot/Player/NextBotPlayer.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// NextBotPlayer.cpp
|
||||
// A CBasePlayer bot based on the NextBot technology
|
||||
// Author: Michael Booth, November 2005
|
||||
// Copyright (c) 2007 Turtle Rock Studios, Inc. - All Rights Reserved
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "nav_mesh.h"
|
||||
|
||||
#include "NextBot.h"
|
||||
#include "NextBotPlayer.h"
|
||||
|
||||
#include "in_buttons.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar NextBotPlayerStop( "nb_player_stop", "0", FCVAR_CHEAT, "Stop all NextBotPlayers from updating" );
|
||||
ConVar NextBotPlayerWalk( "nb_player_walk", "0", FCVAR_CHEAT, "Force bots to walk" );
|
||||
ConVar NextBotPlayerCrouch( "nb_player_crouch", "0", FCVAR_CHEAT, "Force bots to crouch" );
|
||||
ConVar NextBotPlayerMove( "nb_player_move", "1", FCVAR_CHEAT, "Prevents bots from moving" );
|
||||
|
||||
862
game/server/NextBot/Player/NextBotPlayer.h
Normal file
862
game/server/NextBot/Player/NextBotPlayer.h
Normal file
@@ -0,0 +1,862 @@
|
||||
// NextBotPlayer.h
|
||||
// A CBasePlayer bot based on the NextBot technology
|
||||
// Author: Michael Booth, November 2005
|
||||
// Copyright (c) 2007 Turtle Rock Studios, Inc. - All Rights Reserved
|
||||
|
||||
#ifndef _NEXT_BOT_PLAYER_H_
|
||||
#define _NEXT_BOT_PLAYER_H_
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gameinterface.h"
|
||||
|
||||
#include "NextBot.h"
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
//#include "NextBotPlayerBody.h"
|
||||
#include "NextBotBehavior.h"
|
||||
|
||||
#include "in_buttons.h"
|
||||
|
||||
extern ConVar NextBotPlayerStop;
|
||||
extern ConVar NextBotPlayerWalk;
|
||||
extern ConVar NextBotPlayerCrouch;
|
||||
extern ConVar NextBotPlayerMove;
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Instantiate a NextBot derived from CBasePlayer and spawn it into the environment.
|
||||
* Assumes class T is derived from CBasePlayer, and has the following method that
|
||||
* creates a new entity of type T and returns it:
|
||||
*
|
||||
* static CBasePlayer *T::AllocatePlayerEntity( edict_t *pEdict, const char *playerName )
|
||||
*
|
||||
*/
|
||||
template < typename T >
|
||||
T * NextBotCreatePlayerBot( const char *name )
|
||||
{
|
||||
/*
|
||||
if ( UTIL_ClientsInGame() >= gpGlobals->maxClients )
|
||||
{
|
||||
Msg( "CreatePlayerBot: Failed - server is full (%d/%d clients).\n", UTIL_ClientsInGame(), gpGlobals->maxClients );
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
|
||||
// This is a "back door" for allocating a custom player bot entity when
|
||||
// the engine calls ClientPutInServer (from CreateFakeClient)
|
||||
ClientPutInServerOverride( T::AllocatePlayerEntity );
|
||||
|
||||
// create the bot and spawn it into the environment
|
||||
edict_t *botEdict = engine->CreateFakeClient( name );
|
||||
|
||||
// close the "back door"
|
||||
ClientPutInServerOverride( NULL );
|
||||
|
||||
if ( botEdict == NULL )
|
||||
{
|
||||
Msg( "CreatePlayerBot: Unable to create bot %s - CreateFakeClient() returned NULL.\n", name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// create an instance of the bot's class and bind it to the edict
|
||||
T *bot = dynamic_cast< T * >( CBaseEntity::Instance( botEdict ) );
|
||||
|
||||
if ( bot == NULL )
|
||||
{
|
||||
Assert( false );
|
||||
Error( "CreatePlayerBot: Could not Instance() from the bot edict.\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// set the fakeclient's name
|
||||
// char trimmedName[ MAX_PLAYER_NAME_LENGTH ];
|
||||
// Q_strncpy( trimmedName, name, sizeof( trimmedName ) );
|
||||
// bot->PlayerData()->netname = AllocPooledString( trimmedName );
|
||||
bot->SetPlayerName( name );
|
||||
|
||||
// flag this as a fakeclient (bot)
|
||||
bot->ClearFlags();
|
||||
bot->AddFlag( FL_CLIENT | FL_FAKECLIENT );
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Interface to access player input buttons.
|
||||
* Unless a duration is given, each button is released at the start of the next frame.
|
||||
* The release methods allow releasing a button before its duration has elapsed.
|
||||
*/
|
||||
class INextBotPlayerInput
|
||||
{
|
||||
public:
|
||||
virtual void PressFireButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseFireButton( void ) = 0;
|
||||
|
||||
virtual void PressAltFireButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseAltFireButton( void ) = 0;
|
||||
|
||||
virtual void PressMeleeButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseMeleeButton( void ) = 0;
|
||||
|
||||
virtual void PressUseButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseUseButton( void ) = 0;
|
||||
|
||||
virtual void PressReloadButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseReloadButton( void ) = 0;
|
||||
|
||||
virtual void PressForwardButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseForwardButton( void ) = 0;
|
||||
|
||||
virtual void PressBackwardButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseBackwardButton( void ) = 0;
|
||||
|
||||
virtual void PressLeftButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseLeftButton( void ) = 0;
|
||||
|
||||
virtual void PressRightButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseRightButton( void ) = 0;
|
||||
|
||||
virtual void PressJumpButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseJumpButton( void ) = 0;
|
||||
|
||||
virtual void PressCrouchButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseCrouchButton( void ) = 0;
|
||||
|
||||
virtual void PressWalkButton( float duration = -1.0f ) = 0;
|
||||
virtual void ReleaseWalkButton( void ) = 0;
|
||||
|
||||
virtual void SetButtonScale( float forward, float right ) = 0;
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Drive a CBasePlayer-derived entity via NextBot logic
|
||||
*/
|
||||
template < typename PlayerType >
|
||||
class NextBotPlayer : public PlayerType, public INextBot, public INextBotPlayerInput
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( NextBotPlayer, PlayerType );
|
||||
|
||||
NextBotPlayer( void );
|
||||
virtual ~NextBotPlayer();
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void PhysicsSimulate( void );
|
||||
|
||||
virtual bool IsNetClient( void ) const { return false; } // Bots should return FALSE for this, they can't receive NET messages
|
||||
virtual bool IsFakeClient( void ) const { return true; }
|
||||
virtual bool IsBot( void ) const { return true; }
|
||||
virtual INextBot *MyNextBotPointer( void ) { return this; }
|
||||
|
||||
// this is valid because the templatized PlayerType must be derived from CBasePlayer, which is derived from CBaseCombatCharacter
|
||||
virtual CBaseCombatCharacter *GetEntity( void ) const { return ( PlayerType * )this; }
|
||||
|
||||
virtual bool IsRemovedOnReset( void ) const { return false; } // remove this bot when the NextBot manager calls Reset
|
||||
|
||||
virtual bool IsDormantWhenDead( void ) const { return true; } // should this player-bot continue to update itself when dead (respawn logic, etc)
|
||||
|
||||
// allocate a bot and bind it to the edict
|
||||
static CBasePlayer *AllocatePlayerEntity( edict_t *edict, const char *playerName );
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// utility methods
|
||||
float GetDistanceBetween( CBaseEntity *other ) const; // return distance between us and the given entity
|
||||
bool IsDistanceBetweenLessThan( CBaseEntity *other, float range ) const; // return true if distance between is less than the given value
|
||||
bool IsDistanceBetweenGreaterThan( CBaseEntity *other, float range ) const; // return true if distance between is greater than the given value
|
||||
|
||||
float GetDistanceBetween( const Vector &target ) const; // return distance between us and the given entity
|
||||
bool IsDistanceBetweenLessThan( const Vector &target, float range ) const; // return true if distance between is less than the given value
|
||||
bool IsDistanceBetweenGreaterThan( const Vector &target, float range ) const; // return true if distance between is greater than the given value
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// INextBotPlayerInput
|
||||
virtual void PressFireButton( float duration = -1.0f );
|
||||
virtual void ReleaseFireButton( void );
|
||||
|
||||
virtual void PressAltFireButton( float duration = -1.0f );
|
||||
virtual void ReleaseAltFireButton( void );
|
||||
|
||||
virtual void PressMeleeButton( float duration = -1.0f );
|
||||
virtual void ReleaseMeleeButton( void );
|
||||
|
||||
virtual void PressUseButton( float duration = -1.0f );
|
||||
virtual void ReleaseUseButton( void );
|
||||
|
||||
virtual void PressReloadButton( float duration = -1.0f );
|
||||
virtual void ReleaseReloadButton( void );
|
||||
|
||||
virtual void PressForwardButton( float duration = -1.0f );
|
||||
virtual void ReleaseForwardButton( void );
|
||||
|
||||
virtual void PressBackwardButton( float duration = -1.0f );
|
||||
virtual void ReleaseBackwardButton( void );
|
||||
|
||||
virtual void PressLeftButton( float duration = -1.0f );
|
||||
virtual void ReleaseLeftButton( void );
|
||||
|
||||
virtual void PressRightButton( float duration = -1.0f );
|
||||
virtual void ReleaseRightButton( void );
|
||||
|
||||
virtual void PressJumpButton( float duration = -1.0f );
|
||||
virtual void ReleaseJumpButton( void );
|
||||
|
||||
virtual void PressCrouchButton( float duration = -1.0f );
|
||||
virtual void ReleaseCrouchButton( void );
|
||||
|
||||
virtual void PressWalkButton( float duration = -1.0f );
|
||||
virtual void ReleaseWalkButton( void );
|
||||
|
||||
virtual void SetButtonScale( float forward, float right );
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Event hooks into NextBot system
|
||||
virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
virtual int OnTakeDamage_Dying( const CTakeDamageInfo &info );
|
||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||
virtual void HandleAnimEvent( animevent_t *event );
|
||||
virtual void OnNavAreaChanged( CNavArea *enteredArea, CNavArea *leftArea ); // invoked (by UpdateLastKnownArea) when we enter a new nav area (or it is reset to NULL)
|
||||
virtual void Touch( CBaseEntity *other );
|
||||
virtual void Weapon_Equip( CBaseCombatWeapon *weapon ); // for OnPickUp
|
||||
virtual void Weapon_Drop( CBaseCombatWeapon *weapon, const Vector *target, const Vector *velocity ); // for OnDrop
|
||||
virtual void OnMainActivityComplete( Activity newActivity, Activity oldActivity );
|
||||
virtual void OnMainActivityInterrupted( Activity newActivity, Activity oldActivity );
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
bool IsAbleToAutoCenterOnLadders( void ) const;
|
||||
|
||||
public:
|
||||
// begin INextBot ------------------------------------------------------------------------------------------------------------------
|
||||
virtual void Update( void ); // (EXTEND) update internal state
|
||||
|
||||
protected:
|
||||
int m_inputButtons; // this is still needed to guarantee each button press is captured at least once
|
||||
int m_prevInputButtons;
|
||||
CountdownTimer m_fireButtonTimer;
|
||||
CountdownTimer m_meleeButtonTimer;
|
||||
CountdownTimer m_useButtonTimer;
|
||||
CountdownTimer m_reloadButtonTimer;
|
||||
CountdownTimer m_forwardButtonTimer;
|
||||
CountdownTimer m_backwardButtonTimer;
|
||||
CountdownTimer m_leftButtonTimer;
|
||||
CountdownTimer m_rightButtonTimer;
|
||||
CountdownTimer m_jumpButtonTimer;
|
||||
CountdownTimer m_crouchButtonTimer;
|
||||
CountdownTimer m_walkButtonTimer;
|
||||
CountdownTimer m_buttonScaleTimer;
|
||||
IntervalTimer m_burningTimer; // how long since we were last burning
|
||||
float m_forwardScale;
|
||||
float m_rightScale;
|
||||
};
|
||||
|
||||
|
||||
template < typename PlayerType >
|
||||
inline float NextBotPlayer< PlayerType >::GetDistanceBetween( CBaseEntity *other ) const
|
||||
{
|
||||
return (this->GetAbsOrigin() - other->GetAbsOrigin()).Length();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline bool NextBotPlayer< PlayerType >::IsDistanceBetweenLessThan( CBaseEntity *other, float range ) const
|
||||
{
|
||||
return (this->GetAbsOrigin() - other->GetAbsOrigin()).IsLengthLessThan( range );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline bool NextBotPlayer< PlayerType >::IsDistanceBetweenGreaterThan( CBaseEntity *other, float range ) const
|
||||
{
|
||||
return (this->GetAbsOrigin() - other->GetAbsOrigin()).IsLengthGreaterThan( range );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline float NextBotPlayer< PlayerType >::GetDistanceBetween( const Vector &target ) const
|
||||
{
|
||||
return (this->GetAbsOrigin() - target).Length();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline bool NextBotPlayer< PlayerType >::IsDistanceBetweenLessThan( const Vector &target, float range ) const
|
||||
{
|
||||
return (this->GetAbsOrigin() - target).IsLengthLessThan( range );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline bool NextBotPlayer< PlayerType >::IsDistanceBetweenGreaterThan( const Vector &target, float range ) const
|
||||
{
|
||||
return (this->GetAbsOrigin() - target).IsLengthGreaterThan( range );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressFireButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_ATTACK;
|
||||
m_fireButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseFireButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_ATTACK;
|
||||
m_fireButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressAltFireButton( float duration )
|
||||
{
|
||||
PressMeleeButton( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseAltFireButton( void )
|
||||
{
|
||||
ReleaseMeleeButton();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressMeleeButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_ATTACK2;
|
||||
m_meleeButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseMeleeButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_ATTACK2;
|
||||
m_meleeButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressUseButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_USE;
|
||||
m_useButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseUseButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_USE;
|
||||
m_useButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressReloadButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_RELOAD;
|
||||
m_reloadButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseReloadButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_RELOAD;
|
||||
m_reloadButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressJumpButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_JUMP;
|
||||
m_jumpButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseJumpButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_JUMP;
|
||||
m_jumpButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressCrouchButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_DUCK;
|
||||
m_crouchButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseCrouchButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_DUCK;
|
||||
m_crouchButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressWalkButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_SPEED;
|
||||
m_walkButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseWalkButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_SPEED;
|
||||
m_walkButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressForwardButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_FORWARD;
|
||||
m_forwardButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseForwardButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_FORWARD;
|
||||
m_forwardButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressBackwardButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_BACK;
|
||||
m_backwardButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseBackwardButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_BACK;
|
||||
m_backwardButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressLeftButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_MOVELEFT;
|
||||
m_leftButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseLeftButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_MOVELEFT;
|
||||
m_leftButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PressRightButton( float duration )
|
||||
{
|
||||
m_inputButtons |= IN_MOVERIGHT;
|
||||
m_rightButtonTimer.Start( duration );
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::ReleaseRightButton( void )
|
||||
{
|
||||
m_inputButtons &= ~IN_MOVERIGHT;
|
||||
m_rightButtonTimer.Invalidate();
|
||||
}
|
||||
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::SetButtonScale( float forward, float right )
|
||||
{
|
||||
m_forwardScale = forward;
|
||||
m_rightScale = right;
|
||||
m_buttonScaleTimer.Start( 0.01 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline NextBotPlayer< PlayerType >::NextBotPlayer( void )
|
||||
{
|
||||
m_prevInputButtons = 0;
|
||||
m_inputButtons = 0;
|
||||
m_burningTimer.Invalidate();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline NextBotPlayer< PlayerType >::~NextBotPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::Spawn( void )
|
||||
{
|
||||
engine->SetFakeClientConVarValue( this->edict(), "cl_autohelp", "0" );
|
||||
|
||||
m_prevInputButtons = m_inputButtons = 0;
|
||||
m_fireButtonTimer.Invalidate();
|
||||
m_meleeButtonTimer.Invalidate();
|
||||
m_useButtonTimer.Invalidate();
|
||||
m_reloadButtonTimer.Invalidate();
|
||||
m_forwardButtonTimer.Invalidate();
|
||||
m_backwardButtonTimer.Invalidate();
|
||||
m_leftButtonTimer.Invalidate();
|
||||
m_rightButtonTimer.Invalidate();
|
||||
m_jumpButtonTimer.Invalidate();
|
||||
m_crouchButtonTimer.Invalidate();
|
||||
m_walkButtonTimer.Invalidate();
|
||||
m_buttonScaleTimer.Invalidate();
|
||||
m_forwardScale = m_rightScale = 0.04;
|
||||
m_burningTimer.Invalidate();
|
||||
|
||||
// reset first, because Spawn() may access various interfaces
|
||||
INextBot::Reset();
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
inline void _NextBot_BuildUserCommand( CUserCmd *cmd, const QAngle &viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse )
|
||||
{
|
||||
Q_memset( cmd, 0, sizeof( CUserCmd ) );
|
||||
|
||||
cmd->command_number = gpGlobals->tickcount;
|
||||
cmd->forwardmove = forwardmove;
|
||||
cmd->sidemove = sidemove;
|
||||
cmd->upmove = upmove;
|
||||
cmd->buttons = buttons;
|
||||
cmd->impulse = impulse;
|
||||
|
||||
VectorCopy( viewangles, cmd->viewangles );
|
||||
|
||||
cmd->random_seed = random->RandomInt( 0, 0x7fffffff );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::PhysicsSimulate( void )
|
||||
{
|
||||
VPROF( "NextBotPlayer::PhysicsSimulate" );
|
||||
|
||||
if ( engine->IsPaused() )
|
||||
{
|
||||
// We're paused - don't add new commands
|
||||
PlayerType::PhysicsSimulate();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ( IsDormantWhenDead() && PlayerType::m_lifeState == LIFE_DEAD ) || NextBotStop.GetBool() )
|
||||
{
|
||||
// death animation complete - nothing left to do except let PhysicsSimulate run PreThink etc
|
||||
PlayerType::PhysicsSimulate();
|
||||
return;
|
||||
}
|
||||
|
||||
int inputButtons;
|
||||
//
|
||||
// Update bot behavior
|
||||
//
|
||||
if ( BeginUpdate() )
|
||||
{
|
||||
Update();
|
||||
|
||||
// build button bits
|
||||
if ( !m_fireButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_ATTACK;
|
||||
|
||||
if ( !m_meleeButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_ATTACK2;
|
||||
|
||||
if ( !m_useButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_USE;
|
||||
|
||||
if ( !m_reloadButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_RELOAD;
|
||||
|
||||
if ( !m_forwardButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_FORWARD;
|
||||
|
||||
if ( !m_backwardButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_BACK;
|
||||
|
||||
if ( !m_leftButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_MOVELEFT;
|
||||
|
||||
if ( !m_rightButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_MOVERIGHT;
|
||||
|
||||
if ( !m_jumpButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_JUMP;
|
||||
|
||||
if ( !m_crouchButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_DUCK;
|
||||
|
||||
if ( !m_walkButtonTimer.IsElapsed() )
|
||||
m_inputButtons |= IN_SPEED;
|
||||
|
||||
m_prevInputButtons = m_inputButtons;
|
||||
inputButtons = m_inputButtons;
|
||||
|
||||
EndUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// HACK: Smooth out body animations
|
||||
GetBodyInterface()->Update();
|
||||
|
||||
// keep buttons pressed between Update() calls (m_prevInputButtons),
|
||||
// and include any button presses that occurred this tick (m_inputButtons).
|
||||
inputButtons = m_prevInputButtons | m_inputButtons;
|
||||
}
|
||||
|
||||
//
|
||||
// Convert NextBot locomotion and posture into
|
||||
// player commands
|
||||
//
|
||||
IBody *body = GetBodyInterface();
|
||||
ILocomotion *mover = GetLocomotionInterface();
|
||||
|
||||
if ( body->IsActualPosture( IBody::CROUCH ) )
|
||||
{
|
||||
inputButtons |= IN_DUCK;
|
||||
}
|
||||
|
||||
float forwardSpeed = 0.0f;
|
||||
float strafeSpeed = 0.0f;
|
||||
float verticalSpeed = ( m_inputButtons & IN_JUMP ) ? mover->GetRunSpeed() : 0.0f;
|
||||
|
||||
if ( inputButtons & IN_FORWARD )
|
||||
{
|
||||
forwardSpeed = mover->GetRunSpeed();
|
||||
}
|
||||
else if ( inputButtons & IN_BACK )
|
||||
{
|
||||
forwardSpeed = -mover->GetRunSpeed();
|
||||
}
|
||||
|
||||
if ( inputButtons & IN_MOVELEFT )
|
||||
{
|
||||
strafeSpeed = -mover->GetRunSpeed();
|
||||
}
|
||||
else if ( inputButtons & IN_MOVERIGHT )
|
||||
{
|
||||
strafeSpeed = mover->GetRunSpeed();
|
||||
}
|
||||
|
||||
if ( NextBotPlayerWalk.GetBool() )
|
||||
{
|
||||
inputButtons |= IN_SPEED;
|
||||
}
|
||||
|
||||
if ( NextBotPlayerCrouch.GetBool() )
|
||||
{
|
||||
inputButtons |= IN_DUCK;
|
||||
}
|
||||
|
||||
if ( !m_buttonScaleTimer.IsElapsed() )
|
||||
{
|
||||
forwardSpeed = mover->GetRunSpeed() * m_forwardScale;
|
||||
strafeSpeed = mover->GetRunSpeed() * m_rightScale;
|
||||
}
|
||||
|
||||
if ( !NextBotPlayerMove.GetBool() )
|
||||
{
|
||||
inputButtons &= ~(IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT | IN_JUMP );
|
||||
forwardSpeed = 0.0f;
|
||||
strafeSpeed = 0.0f;
|
||||
verticalSpeed = 0.0f;
|
||||
}
|
||||
|
||||
QAngle angles = this->EyeAngles();
|
||||
|
||||
#ifdef TERROR
|
||||
if ( IsStunned() )
|
||||
{
|
||||
inputButtons &= ~(IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT | IN_JUMP | IN_DUCK );
|
||||
}
|
||||
|
||||
// "Look" in the direction we're climbing/stumbling etc. We can't do anything anyway, and it
|
||||
// keeps motion extraction working.
|
||||
if ( IsRenderYawOverridden() && IsMotionControlledXY( GetMainActivity() ) )
|
||||
{
|
||||
angles[YAW] = GetOverriddenRenderYaw();
|
||||
}
|
||||
#endif
|
||||
|
||||
// construct a "command" to move the player
|
||||
CUserCmd userCmd;
|
||||
_NextBot_BuildUserCommand( &userCmd, angles, forwardSpeed, strafeSpeed, verticalSpeed, inputButtons, 0 );
|
||||
|
||||
#ifdef TERROR
|
||||
AvoidPlayers( &userCmd );
|
||||
#endif
|
||||
|
||||
// allocate a new command and add it to the player's list of command to process
|
||||
this->ProcessUsercmds( &userCmd, 1, 1, 0, false );
|
||||
|
||||
m_inputButtons = 0;
|
||||
|
||||
// actually execute player commands and do player physics
|
||||
PlayerType::PhysicsSimulate();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::OnNavAreaChanged( CNavArea *enteredArea, CNavArea *leftArea )
|
||||
{
|
||||
// propagate into NextBot responders
|
||||
INextBotEventResponder::OnNavAreaChanged( enteredArea, leftArea );
|
||||
|
||||
BaseClass::OnNavAreaChanged( enteredArea, leftArea );
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::Touch( CBaseEntity *other )
|
||||
{
|
||||
if ( ShouldTouch( other ) )
|
||||
{
|
||||
// propagate touch into NextBot event responders
|
||||
trace_t result;
|
||||
result = this->GetTouchTrace();
|
||||
OnContact( other, &result );
|
||||
}
|
||||
|
||||
BaseClass::Touch( other );
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::Weapon_Equip( CBaseCombatWeapon *weapon )
|
||||
{
|
||||
#ifdef TERROR
|
||||
// TODO: Reimplement GetDroppingPlayer() into GetLastOwner()
|
||||
OnPickUp( weapon, weapon->GetDroppingPlayer() );
|
||||
#else
|
||||
OnPickUp( weapon, NULL );
|
||||
#endif
|
||||
|
||||
BaseClass::Weapon_Equip( weapon );
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::Weapon_Drop( CBaseCombatWeapon *weapon, const Vector *target, const Vector *velocity )
|
||||
{
|
||||
OnDrop( weapon );
|
||||
|
||||
BaseClass::Weapon_Drop( weapon, target, velocity );
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::OnMainActivityComplete( Activity newActivity, Activity oldActivity )
|
||||
{
|
||||
#ifdef TERROR
|
||||
BaseClass::OnMainActivityComplete( newActivity, oldActivity );
|
||||
#endif
|
||||
OnAnimationActivityComplete( oldActivity );
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::OnMainActivityInterrupted( Activity newActivity, Activity oldActivity )
|
||||
{
|
||||
#ifdef TERROR
|
||||
BaseClass::OnMainActivityInterrupted( newActivity, oldActivity );
|
||||
#endif
|
||||
OnAnimationActivityInterrupted( oldActivity );
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::Update( void )
|
||||
{
|
||||
// don't spend CPU updating if this Survivor is dead
|
||||
if ( ( this->IsAlive() || !IsDormantWhenDead() ) && !NextBotPlayerStop.GetBool() )
|
||||
{
|
||||
INextBot::Update();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline bool NextBotPlayer< PlayerType >::IsAbleToAutoCenterOnLadders( void ) const
|
||||
{
|
||||
const ILocomotion *locomotion = GetLocomotionInterface();
|
||||
return locomotion && locomotion->IsAbleToAutoCenterOnLadder();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline int NextBotPlayer< PlayerType >::OnTakeDamage_Alive( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( info.GetDamageType() & DMG_BURN )
|
||||
{
|
||||
if ( !m_burningTimer.HasStarted() || m_burningTimer.IsGreaterThen( 1.0f ) )
|
||||
{
|
||||
// emit ignite event periodically as long as we are burning
|
||||
OnIgnite();
|
||||
m_burningTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
// propagate event to components
|
||||
OnInjured( info );
|
||||
|
||||
return BaseClass::OnTakeDamage_Alive( info );
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline int NextBotPlayer< PlayerType >::OnTakeDamage_Dying( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( info.GetDamageType() & DMG_BURN )
|
||||
{
|
||||
if ( !m_burningTimer.HasStarted() || m_burningTimer.IsGreaterThen( 1.0f ) )
|
||||
{
|
||||
// emit ignite event periodically as long as we are burning
|
||||
OnIgnite();
|
||||
m_burningTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
// propagate event to components
|
||||
OnInjured( info );
|
||||
|
||||
return BaseClass::OnTakeDamage_Dying( info );
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
// propagate event to my components
|
||||
OnKilled( info );
|
||||
|
||||
BaseClass::Event_Killed( info );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
template < typename PlayerType >
|
||||
inline void NextBotPlayer< PlayerType >::HandleAnimEvent( animevent_t *event )
|
||||
{
|
||||
// propagate event to components
|
||||
OnAnimationEvent( event );
|
||||
|
||||
BaseClass::HandleAnimEvent( event );
|
||||
}
|
||||
|
||||
|
||||
#endif // _NEXT_BOT_PLAYER_H_
|
||||
805
game/server/NextBot/Player/NextBotPlayerBody.cpp
Normal file
805
game/server/NextBot/Player/NextBotPlayerBody.cpp
Normal file
@@ -0,0 +1,805 @@
|
||||
// NextBotPlayerBody.cpp
|
||||
// Implementation of Body interface for CBasePlayer-derived classes
|
||||
// Author: Michael Booth, October 2006
|
||||
// Copyright (c) 2006 Turtle Rock Studios, Inc. - 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 NextBotSaccadeTime( "nb_saccade_time", "0.1", FCVAR_CHEAT );
|
||||
ConVar NextBotSaccadeSpeed( "nb_saccade_speed", "1000", FCVAR_CHEAT );
|
||||
ConVar NextBotHeadAimSteadyMaxRate( "nb_head_aim_steady_max_rate", "100", FCVAR_CHEAT );
|
||||
ConVar NextBotHeadAimSettleDuration( "nb_head_aim_settle_duration", "0.3", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* 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_lookAtPriority = BORING;
|
||||
m_lookAtExpireTimer.Invalidate();
|
||||
m_lookAtDurationTimer.Invalidate();
|
||||
m_isSightedIn = false;
|
||||
m_hasBeenSightedIn = false;
|
||||
m_headSteadyTimer.Invalidate();
|
||||
m_yawRate = 0.0f;
|
||||
m_pitchRate = 0.0f;
|
||||
m_priorAngles = vec3_angle;
|
||||
}
|
||||
|
||||
static 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 ) > NextBotHeadAimSteadyMaxRate.GetFloat() * deltaT )
|
||||
{
|
||||
isSteady = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float actualYawRate = AngleDiff( currentAngles.y, m_priorAngles.y );
|
||||
|
||||
if ( abs( actualYawRate ) > NextBotHeadAimSteadyMaxRate.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, 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;
|
||||
}
|
||||
|
||||
|
||||
// if we have a subject, update lookat point
|
||||
CBaseEntity *subject = m_lookAtSubject;
|
||||
if ( subject )
|
||||
{
|
||||
if ( subject->MyCombatCharacterPointer() )
|
||||
{
|
||||
m_lookAtPos = GetBot()->GetIntentionInterface()->SelectTargetPoint( GetBot(), subject->MyCombatCharacterPointer() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lookAtPos = subject->WorldSpaceCenter();
|
||||
}
|
||||
|
||||
m_lookAtPos += GetHeadAimSubjectLeadTime() * subject->GetAbsVelocity();
|
||||
}
|
||||
|
||||
// aim view towards last look at point
|
||||
Vector to = m_lookAtPos - GetEyePosition();
|
||||
to.NormalizeInPlace();
|
||||
|
||||
QAngle desiredAngles;
|
||||
VectorAngles( to, desiredAngles );
|
||||
|
||||
QAngle angles;
|
||||
|
||||
const Vector &forward = GetViewVector();
|
||||
|
||||
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 g = subject ? 255 : 0;
|
||||
NDebugOverlay::HorzArrow( GetEyePosition(), m_lookAtPos, thickness, 0, 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;
|
||||
m_hasBeenSightedIn = true;
|
||||
|
||||
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, 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() < NextBotHeadAimSettleDuration.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 rejected - previous aim not settled\n",
|
||||
gpGlobals->curtime,
|
||||
m_player->GetPlayerName() );
|
||||
}
|
||||
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 rejected - higher priority aim in progress\n",
|
||||
gpGlobals->curtime,
|
||||
m_player->GetPlayerName() );
|
||||
}
|
||||
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();
|
||||
m_isSightedIn = false;
|
||||
m_hasBeenSightedIn = false;
|
||||
|
||||
if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
|
||||
{
|
||||
NDebugOverlay::Cross3D( lookAtPos, 2.0f, 255, 255, 100, true, 2.0f * duration );
|
||||
|
||||
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() < NextBotHeadAimSettleDuration.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 rejected - previous aim not settled\n",
|
||||
gpGlobals->curtime,
|
||||
m_player->GetPlayerName() );
|
||||
}
|
||||
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 rejected - higher priority aim in progress\n",
|
||||
gpGlobals->curtime,
|
||||
m_player->GetPlayerName() );
|
||||
}
|
||||
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();
|
||||
m_isSightedIn = false;
|
||||
m_hasBeenSightedIn = false;
|
||||
|
||||
if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
|
||||
{
|
||||
NDebugOverlay::Cross3D( m_lookAtPos, 2.0f, 100, 100, 100, true, duration );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
float PlayerBody::GetMaxHeadAngularVelocity( void ) const
|
||||
{
|
||||
return NextBotSaccadeSpeed.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.x - VEC_HULL_MIN.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.z - VEC_HULL_MIN.z;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Height of bot's collision hull when crouched
|
||||
*/
|
||||
float PlayerBody::GetCrouchHullHeight( void ) const
|
||||
{
|
||||
return VEC_DUCK_HULL_MAX.z - VEC_DUCK_HULL_MIN.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;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hullMins = VEC_HULL_MIN;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hullMaxs = VEC_HULL_MAX;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
148
game/server/NextBot/Player/NextBotPlayerBody.h
Normal file
148
game/server/NextBot/Player/NextBotPlayerBody.h
Normal file
@@ -0,0 +1,148 @@
|
||||
// NextBotPlayerBody.h
|
||||
// Control and information about the bot's body state (posture, animation state, etc)
|
||||
// Author: Michael Booth, October 2006
|
||||
// Copyright (c) 2006 Turtle Rock Studios, Inc. - All Rights Reserved
|
||||
|
||||
#ifndef _NEXT_BOT_PLAYER_BODY_H_
|
||||
#define _NEXT_BOT_PLAYER_BODY_H_
|
||||
|
||||
#include "NextBotBodyInterface.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* A useful reply for IBody::AimHeadTowards. When the
|
||||
* head is aiming on target, press the fire button.
|
||||
*/
|
||||
class PressFireButtonReply : public INextBotReply
|
||||
{
|
||||
public:
|
||||
virtual void OnSuccess( INextBot *bot ); // invoked when process completed successfully
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* A useful reply for IBody::AimHeadTowards. When the
|
||||
* head is aiming on target, press the alt-fire button.
|
||||
*/
|
||||
class PressAltFireButtonReply : public INextBotReply
|
||||
{
|
||||
public:
|
||||
virtual void OnSuccess( INextBot *bot ); // invoked when process completed successfully
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* A useful reply for IBody::AimHeadTowards. When the
|
||||
* head is aiming on target, press the jump button.
|
||||
*/
|
||||
class PressJumpButtonReply : public INextBotReply
|
||||
{
|
||||
public:
|
||||
virtual void OnSuccess( INextBot *bot ); // invoked when process completed successfully
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* The interface for control and information about the bot's body state (posture, animation state, etc)
|
||||
*/
|
||||
class PlayerBody : public IBody
|
||||
{
|
||||
public:
|
||||
PlayerBody( INextBot *bot );
|
||||
virtual ~PlayerBody();
|
||||
|
||||
virtual void Reset( void ); // reset to initial state
|
||||
virtual void Upkeep( void ); // lightweight update guaranteed to occur every server tick
|
||||
|
||||
virtual bool SetPosition( const Vector &pos );
|
||||
|
||||
virtual const Vector &GetEyePosition( void ) const; // return the eye position of the bot in world coordinates
|
||||
virtual const Vector &GetViewVector( void ) const; // return the view unit direction vector in world coordinates
|
||||
|
||||
virtual void AimHeadTowards( const Vector &lookAtPos,
|
||||
LookAtPriorityType priority = BORING,
|
||||
float duration = 0.0f,
|
||||
INextBotReply *replyWhenAimed = NULL,
|
||||
const char *reason = NULL ); // aim the bot's head towards the given goal
|
||||
|
||||
virtual void AimHeadTowards( CBaseEntity *subject,
|
||||
LookAtPriorityType priority = BORING,
|
||||
float duration = 0.0f,
|
||||
INextBotReply *replyWhenAimed = NULL,
|
||||
const char *reason = NULL ); // continually aim the bot's head towards the given subject
|
||||
|
||||
virtual bool IsHeadAimingOnTarget( void ) const; // return true if the bot's head has achieved its most recent lookat target
|
||||
virtual bool IsHeadSteady( void ) const; // return true if head is not rapidly turning to look somewhere else
|
||||
virtual float GetHeadSteadyDuration( void ) const; // return the duration that the bot's head has been on-target
|
||||
|
||||
virtual float GetMaxHeadAngularVelocity( void ) const; // return max turn rate of head in degrees/second
|
||||
|
||||
virtual bool StartActivity( Activity act, unsigned int flags );
|
||||
virtual Activity GetActivity( void ) const; // return currently animating activity
|
||||
virtual bool IsActivity( Activity act ) const; // return true if currently animating activity matches the given one
|
||||
virtual bool HasActivityType( unsigned int flags ) const; // return true if currently animating activity has any of the given flags
|
||||
|
||||
virtual void SetDesiredPosture( PostureType posture ); // request a posture change
|
||||
virtual PostureType GetDesiredPosture( void ) const; // get posture body is trying to assume
|
||||
virtual bool IsDesiredPosture( PostureType posture ) const; // return true if body is trying to assume this posture
|
||||
virtual bool IsInDesiredPosture( void ) const; // return true if body's actual posture matches its desired posture
|
||||
|
||||
virtual PostureType GetActualPosture( void ) const; // return body's current actual posture
|
||||
virtual bool IsActualPosture( PostureType posture ) const; // return true if body is actually in the given posture
|
||||
|
||||
virtual bool IsPostureMobile( void ) const; // return true if body's current posture allows it to move around the world
|
||||
virtual bool IsPostureChanging( void ) const; // return true if body's posture is in the process of changing to new posture
|
||||
|
||||
virtual void SetArousal( ArousalType arousal ); // arousal level change
|
||||
virtual ArousalType GetArousal( void ) const; // get arousal level
|
||||
virtual bool IsArousal( ArousalType arousal ) const; // return true if body is at this arousal level
|
||||
|
||||
virtual float GetHullWidth( void ) const; // width of bot's collision hull in XY plane
|
||||
virtual float GetHullHeight( void ) const; // height of bot's current collision hull based on posture
|
||||
virtual float GetStandHullHeight( void ) const; // height of bot's collision hull when standing
|
||||
virtual float GetCrouchHullHeight( void ) const; // height of bot's collision hull when crouched
|
||||
virtual const Vector &GetHullMins( void ) const; // return current collision hull minimums based on actual body posture
|
||||
virtual const Vector &GetHullMaxs( void ) const; // return current collision hull maximums based on actual body posture
|
||||
|
||||
virtual unsigned int GetSolidMask( void ) const; // return the bot's collision mask (hack until we get a general hull trace abstraction here or in the locomotion interface)
|
||||
|
||||
virtual CBaseEntity *GetEntity( void ); // get the entity
|
||||
private:
|
||||
CBasePlayer *m_player;
|
||||
|
||||
PostureType m_posture;
|
||||
ArousalType m_arousal;
|
||||
|
||||
mutable Vector m_eyePos; // for use with GetEyePosition() ONLY
|
||||
mutable Vector m_viewVector; // for use with GetViewVector() ONLY
|
||||
mutable Vector m_hullMins; // for use with GetHullMins() ONLY
|
||||
mutable Vector m_hullMaxs; // for use with GetHullMaxs() ONLY
|
||||
|
||||
Vector m_lookAtPos; // if m_lookAtSubject is non-NULL, it continually overwrites this position with its own
|
||||
EHANDLE m_lookAtSubject;
|
||||
|
||||
LookAtPriorityType m_lookAtPriority;
|
||||
CountdownTimer m_lookAtExpireTimer; // how long until this lookat expired
|
||||
IntervalTimer m_lookAtDurationTimer; // how long have we been looking at this target
|
||||
INextBotReply *m_lookAtReplyWhenAimed;
|
||||
bool m_isSightedIn; // true if we are looking at our last lookat target
|
||||
bool m_hasBeenSightedIn; // true if we have hit the current lookat target
|
||||
float m_yawRate;
|
||||
float m_pitchRate;
|
||||
|
||||
IntervalTimer m_headSteadyTimer;
|
||||
QAngle m_priorAngles; // last update's head angles
|
||||
QAngle m_desiredAngles;
|
||||
};
|
||||
|
||||
inline bool PlayerBody::IsHeadAimingOnTarget( void ) const
|
||||
{
|
||||
return m_isSightedIn;
|
||||
}
|
||||
|
||||
|
||||
#endif // _NEXT_BOT_PLAYER_BODY_H_
|
||||
826
game/server/NextBot/Player/NextBotPlayerLocomotion.cpp
Normal file
826
game/server/NextBot/Player/NextBotPlayerLocomotion.cpp
Normal file
@@ -0,0 +1,826 @@
|
||||
// NextBotPlayerLocomotion.cpp
|
||||
// Implementation of Locomotion interface for CBasePlayer-derived classes
|
||||
// Author: Michael Booth, November 2005
|
||||
// Copyright (c) 2005 Turtle Rock Studios, Inc. - 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();
|
||||
}
|
||||
|
||||
219
game/server/NextBot/Player/NextBotPlayerLocomotion.h
Normal file
219
game/server/NextBot/Player/NextBotPlayerLocomotion.h
Normal file
@@ -0,0 +1,219 @@
|
||||
// NextBotPlayerLocomotion.h
|
||||
// Locomotor for CBasePlayer derived bots
|
||||
// Author: Michael Booth, November 2005
|
||||
// Copyright (c) 2005 Turtle Rock Studios, Inc. - All Rights Reserved
|
||||
|
||||
#ifndef _NEXT_BOT_PLAYER_LOCOMOTION_H_
|
||||
#define _NEXT_BOT_PLAYER_LOCOMOTION_H_
|
||||
|
||||
#include "NextBot.h"
|
||||
#include "NextBotLocomotionInterface.h"
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CBasePlayer;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Basic player locomotion implementation
|
||||
*/
|
||||
class PlayerLocomotion : public ILocomotion
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( PlayerLocomotion, ILocomotion );
|
||||
|
||||
PlayerLocomotion( INextBot *bot );
|
||||
virtual ~PlayerLocomotion() { }
|
||||
|
||||
virtual void Reset( void ); // reset to initial state
|
||||
virtual void Update( void ); // update internal state
|
||||
|
||||
virtual void Approach( const Vector &pos, float goalWeight = 1.0f ); // move directly towards the given position
|
||||
virtual void DriveTo( const Vector &pos ); // Move the bot to the precise given position immediately,
|
||||
|
||||
//
|
||||
// ILocomotion modifiers
|
||||
//
|
||||
virtual bool ClimbUpToLedge( const Vector &landingGoal, const Vector &landingForward, const CBaseEntity *obstacle ); // initiate a jump to an adjacent high ledge, return false if climb can't start
|
||||
virtual void JumpAcrossGap( const Vector &landingGoal, const Vector &landingForward ); // initiate a jump across an empty volume of space to far side
|
||||
virtual void Jump( void ); // initiate a simple undirected jump in the air
|
||||
virtual bool IsClimbingOrJumping( void ) const; // is jumping in any form
|
||||
virtual bool IsClimbingUpToLedge( void ) const; // is climbing up to a high ledge
|
||||
virtual bool IsJumpingAcrossGap( void ) const; // is jumping across a gap to the far side
|
||||
|
||||
virtual void Run( void ); // set desired movement speed to running
|
||||
virtual void Walk( void ); // set desired movement speed to walking
|
||||
virtual void Stop( void ); // set desired movement speed to stopped
|
||||
virtual bool IsRunning( void ) const;
|
||||
virtual void SetDesiredSpeed( float speed ); // set desired speed for locomotor movement
|
||||
virtual float GetDesiredSpeed( void ) const; // returns the current desired speed
|
||||
virtual void SetMinimumSpeedLimit( float limit ); // speed cannot drop below this
|
||||
virtual void SetMaximumSpeedLimit( float limit ); // speed cannot rise above this
|
||||
|
||||
virtual bool IsOnGround( void ) const; // return true if standing on something
|
||||
virtual CBaseEntity *GetGround( void ) const; // return the current ground entity or NULL if not on the ground
|
||||
virtual const Vector &GetGroundNormal( void ) const; // surface normal of the ground we are in contact with
|
||||
|
||||
virtual void ClimbLadder( const CNavLadder *ladder, const CNavArea *dismountGoal ); // climb the given ladder to the top and dismount
|
||||
virtual void DescendLadder( const CNavLadder *ladder, const CNavArea *dismountGoal ); // descend the given ladder to the bottom and dismount
|
||||
virtual bool IsUsingLadder( void ) const;
|
||||
virtual bool IsAscendingOrDescendingLadder( void ) const; // we are actually on the ladder right now, either climbing up or down
|
||||
virtual bool IsAbleToAutoCenterOnLadder( void ) const;
|
||||
|
||||
virtual void FaceTowards( const Vector &target ); // rotate body to face towards "target"
|
||||
|
||||
virtual void SetDesiredLean( const QAngle &lean ) { }
|
||||
virtual const QAngle &GetDesiredLean( void ) const { static QAngle junk; return junk; }
|
||||
|
||||
//
|
||||
// ILocomotion information
|
||||
//
|
||||
virtual const Vector &GetFeet( void ) const; // return position of "feet" - point below centroid of bot at feet level
|
||||
|
||||
virtual float GetStepHeight( void ) const; // if delta Z is greater than this, we have to jump to get up
|
||||
virtual float GetMaxJumpHeight( void ) const; // return maximum height of a jump
|
||||
virtual float GetDeathDropHeight( void ) const; // distance at which we will die if we fall
|
||||
|
||||
virtual float GetRunSpeed( void ) const; // get maximum running speed
|
||||
virtual float GetWalkSpeed( void ) const; // get maximum walking speed
|
||||
|
||||
virtual float GetMaxAcceleration( void ) const; // return maximum acceleration of locomotor
|
||||
virtual float GetMaxDeceleration( void ) const; // return maximum deceleration of locomotor
|
||||
|
||||
virtual const Vector &GetVelocity( void ) const; // return current world space velocity
|
||||
|
||||
protected:
|
||||
virtual void AdjustPosture( const Vector &moveGoal );
|
||||
|
||||
private:
|
||||
CBasePlayer *m_player; // the player we are locomoting
|
||||
|
||||
mutable bool m_isJumping;
|
||||
CountdownTimer m_jumpTimer;
|
||||
|
||||
bool m_isClimbingUpToLedge;
|
||||
bool m_isJumpingAcrossGap;
|
||||
Vector m_landingGoal;
|
||||
bool m_hasLeftTheGround;
|
||||
|
||||
float m_desiredSpeed;
|
||||
float m_minSpeedLimit;
|
||||
float m_maxSpeedLimit;
|
||||
|
||||
bool TraverseLadder( void ); // when climbing/descending a ladder
|
||||
|
||||
enum LadderState
|
||||
{
|
||||
NO_LADDER, // not using a ladder
|
||||
APPROACHING_ASCENDING_LADDER,
|
||||
APPROACHING_DESCENDING_LADDER,
|
||||
ASCENDING_LADDER,
|
||||
DESCENDING_LADDER,
|
||||
DISMOUNTING_LADDER_TOP,
|
||||
DISMOUNTING_LADDER_BOTTOM,
|
||||
};
|
||||
|
||||
LadderState m_ladderState;
|
||||
LadderState ApproachAscendingLadder( void );
|
||||
LadderState ApproachDescendingLadder( void );
|
||||
LadderState AscendLadder( void );
|
||||
LadderState DescendLadder( void );
|
||||
LadderState DismountLadderTop( void );
|
||||
LadderState DismountLadderBottom( void );
|
||||
|
||||
const CNavLadder *m_ladderInfo;
|
||||
const CNavArea *m_ladderDismountGoal;
|
||||
CountdownTimer m_ladderTimer; // a "give up" timer if things go awry
|
||||
|
||||
bool IsClimbPossible( INextBot *me, const CBaseEntity *obstacle ) const;
|
||||
};
|
||||
|
||||
|
||||
inline float PlayerLocomotion::GetStepHeight( void ) const
|
||||
{
|
||||
return 18.0f;
|
||||
}
|
||||
|
||||
|
||||
inline float PlayerLocomotion::GetMaxJumpHeight( void ) const
|
||||
{
|
||||
return 57.0f;
|
||||
}
|
||||
|
||||
|
||||
inline float PlayerLocomotion::GetDeathDropHeight( void ) const
|
||||
{
|
||||
return 200.0f;
|
||||
}
|
||||
|
||||
|
||||
inline float PlayerLocomotion::GetMaxAcceleration( void ) const
|
||||
{
|
||||
return 100.0f;
|
||||
}
|
||||
|
||||
inline float PlayerLocomotion::GetMaxDeceleration( void ) const
|
||||
{
|
||||
return 200.0f;
|
||||
}
|
||||
|
||||
inline void PlayerLocomotion::Run( void )
|
||||
{
|
||||
m_desiredSpeed = GetRunSpeed();
|
||||
}
|
||||
|
||||
inline void PlayerLocomotion::Walk( void )
|
||||
{
|
||||
m_desiredSpeed = GetWalkSpeed();
|
||||
}
|
||||
|
||||
inline void PlayerLocomotion::Stop( void )
|
||||
{
|
||||
m_desiredSpeed = 0.0f;
|
||||
}
|
||||
|
||||
inline bool PlayerLocomotion::IsRunning( void ) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void PlayerLocomotion::SetDesiredSpeed( float speed )
|
||||
{
|
||||
m_desiredSpeed = speed;
|
||||
}
|
||||
|
||||
inline float PlayerLocomotion::GetDesiredSpeed( void ) const
|
||||
{
|
||||
return clamp( m_desiredSpeed, m_minSpeedLimit, m_maxSpeedLimit );
|
||||
}
|
||||
|
||||
inline void PlayerLocomotion::SetMinimumSpeedLimit( float limit )
|
||||
{
|
||||
m_minSpeedLimit = limit;
|
||||
}
|
||||
|
||||
inline void PlayerLocomotion::SetMaximumSpeedLimit( float limit )
|
||||
{
|
||||
m_maxSpeedLimit = limit;
|
||||
}
|
||||
|
||||
inline bool PlayerLocomotion::IsAbleToAutoCenterOnLadder( void ) const
|
||||
{
|
||||
return IsUsingLadder() && (m_ladderState == ASCENDING_LADDER || m_ladderState == DESCENDING_LADDER);
|
||||
}
|
||||
|
||||
inline bool PlayerLocomotion::IsAscendingOrDescendingLadder( void ) const
|
||||
{
|
||||
switch( m_ladderState )
|
||||
{
|
||||
case ASCENDING_LADDER:
|
||||
case DESCENDING_LADDER:
|
||||
case DISMOUNTING_LADDER_TOP:
|
||||
case DISMOUNTING_LADDER_BOTTOM:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif // _NEXT_BOT_PLAYER_LOCOMOTION_H_
|
||||
Reference in New Issue
Block a user