summaryrefslogtreecommitdiff
path: root/game/server/NextBot/Player/NextBotPlayer.h
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/NextBot/Player/NextBotPlayer.h')
-rw-r--r--game/server/NextBot/Player/NextBotPlayer.h910
1 files changed, 910 insertions, 0 deletions
diff --git a/game/server/NextBot/Player/NextBotPlayer.h b/game/server/NextBot/Player/NextBotPlayer.h
new file mode 100644
index 0000000..2032c72
--- /dev/null
+++ b/game/server/NextBot/Player/NextBotPlayer.h
@@ -0,0 +1,910 @@
+// NextBotPlayer.h
+// A CBasePlayer bot based on the NextBot technology
+// Author: Michael Booth, November 2005
+//========= Copyright Valve Corporation, 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, bool bReportFakeClient = true )
+{
+ /*
+ 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->CreateFakeClientEx( name, bReportFakeClient );
+
+ // 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;
+ }
+
+ 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 PressSpecialFireButton( float duration = -1.0f ) = 0;
+ virtual void ReleaseSpecialFireButton( 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 SetSpawnPoint( CBaseEntity *spawnPoint ); // define place in environment where bot will (re)spawn
+ virtual CBaseEntity *EntSelectSpawnPoint( 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 PressSpecialFireButton( float duration = -1.0f );
+ virtual void ReleaseSpecialFireButton( 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;
+
+ virtual void AvoidPlayers( CUserCmd *pCmd ) { } // some game types allow players to pass through each other, this method pushes them apart
+
+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_specialFireButtonTimer;
+ 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;
+ CHandle< CBaseEntity > m_spawnPointEntity;
+};
+
+
+template < typename PlayerType >
+inline void NextBotPlayer< PlayerType >::SetSpawnPoint( CBaseEntity *spawnPoint )
+{
+ m_spawnPointEntity = spawnPoint;
+}
+
+template < typename PlayerType >
+inline CBaseEntity *NextBotPlayer< PlayerType >::EntSelectSpawnPoint( void )
+{
+ if ( m_spawnPointEntity != NULL )
+ return m_spawnPointEntity;
+
+ return BaseClass::EntSelectSpawnPoint();
+}
+
+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 >::PressSpecialFireButton( float duration )
+{
+ m_inputButtons |= IN_ATTACK3;
+ m_specialFireButtonTimer.Start( duration );
+}
+
+template < typename PlayerType >
+inline void NextBotPlayer< PlayerType >::ReleaseSpecialFireButton( void )
+{
+ m_inputButtons &= ~IN_ATTACK3;
+ m_specialFireButtonTimer.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();
+ m_spawnPointEntity = NULL;
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+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_specialFireButtonTimer.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" );
+
+ // Make sure not to simulate this guy twice per frame
+ if ( PlayerType::m_nSimulationTick == gpGlobals->tickcount )
+ {
+ return;
+ }
+
+ 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_specialFireButtonTimer.IsElapsed() )
+ m_inputButtons |= IN_ATTACK3;
+
+ 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 );
+
+ AvoidPlayers( &userCmd );
+
+ // 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_