diff options
Diffstat (limited to 'game/server/cstrike/bot/cs_bot.h')
| -rw-r--r-- | game/server/cstrike/bot/cs_bot.h | 2004 |
1 files changed, 2004 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/cs_bot.h b/game/server/cstrike/bot/cs_bot.h new file mode 100644 index 0000000..e9530d5 --- /dev/null +++ b/game/server/cstrike/bot/cs_bot.h @@ -0,0 +1,2004 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// +// Author: Michael S. Booth ([email protected]), 2003 +// +// NOTE: The CS Bot code uses Doxygen-style comments. If you run Doxygen over this code, it will +// auto-generate documentation. Visit www.doxygen.org to download the system for free. +// + +#ifndef _CS_BOT_H_ +#define _CS_BOT_H_ + +#include "bot/bot.h" +#include "bot/cs_bot_manager.h" +#include "bot/cs_bot_chatter.h" +#include "cs_gamestate.h" +#include "cs_player.h" +#include "weapon_csbase.h" +#include "cs_nav_pathfind.h" +#include "cs_nav_area.h" + +class CBaseDoor; +class CBasePropDoor; +class CCSBot; +class CPushAwayEnumerator; + +//-------------------------------------------------------------------------------------------------------------- +/** + * For use with player->m_rgpPlayerItems[] + */ +enum InventorySlotType +{ + PRIMARY_WEAPON_SLOT = 1, + PISTOL_SLOT, + KNIFE_SLOT, + GRENADE_SLOT, + C4_SLOT +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * The definition of a bot's behavior state. One or more finite state machines + * using these states implement a bot's behaviors. + */ +class BotState +{ +public: + virtual void OnEnter( CCSBot *bot ) { } ///< when state is entered + virtual void OnUpdate( CCSBot *bot ) { } ///< state behavior + virtual void OnExit( CCSBot *bot ) { } ///< when state exited + virtual const char *GetName( void ) const = 0; ///< return state name +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * The state is invoked when a bot has nothing to do, or has finished what it was doing. + * A bot never stays in this state - it is the main action selection mechanism. + */ +class IdleState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual const char *GetName( void ) const { return "Idle"; } +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is actively searching for an enemy. + */ +class HuntState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "Hunt"; } + + void ClearHuntArea( void ) { m_huntArea = NULL; } + +private: + CNavArea *m_huntArea; ///< "far away" area we are moving to +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot has an enemy and is attempting to kill it + */ +class AttackState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "Attack"; } + + void SetCrouchAndHold( bool crouch ) { m_crouchAndHold = crouch; } + +protected: + enum DodgeStateType + { + STEADY_ON, + SLIDE_LEFT, + SLIDE_RIGHT, + JUMP, + + NUM_ATTACK_STATES + }; + DodgeStateType m_dodgeState; + float m_nextDodgeStateTimestamp; + + CountdownTimer m_repathTimer; + float m_scopeTimestamp; + + bool m_haveSeenEnemy; ///< false if we haven't yet seen the enemy since we started this attack (told by a friend, etc) + bool m_isEnemyHidden; ///< true we if we have lost line-of-sight to our enemy + float m_reacquireTimestamp; ///< time when we can fire again, after losing enemy behind cover + float m_shieldToggleTimestamp; ///< time to toggle shield deploy state + bool m_shieldForceOpen; ///< if true, open up and shoot even if in danger + + float m_pinnedDownTimestamp; ///< time when we'll consider ourselves "pinned down" by the enemy + + bool m_crouchAndHold; + bool m_didAmbushCheck; + bool m_shouldDodge; + bool m_firstDodge; + + bool m_isCoward; ///< if true, we'll retreat if outnumbered during this fight + CountdownTimer m_retreatTimer; + + void StopAttacking( CCSBot *bot ); + void Dodge( CCSBot *bot ); ///< do dodge behavior +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot has heard an enemy noise and is moving to find out what it was. + */ +class InvestigateNoiseState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "InvestigateNoise"; } + +private: + void AttendCurrentNoise( CCSBot *bot ); ///< move towards currently heard noise + Vector m_checkNoisePosition; ///< the position of the noise we're investigating + CountdownTimer m_minTimer; ///< minimum time we will investigate our current noise +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is buying equipment at the start of a round. + */ +class BuyState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "Buy"; } + +private: + bool m_isInitialDelay; + int m_prefRetries; ///< for retrying buying preferred weapon at current index + int m_prefIndex; ///< where are we in our list of preferred weapons + + int m_retries; + bool m_doneBuying; + bool m_buyDefuseKit; + bool m_buyGrenade; + bool m_buyShield; + bool m_buyPistol; +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is moving to a potentially far away position in the world. + */ +class MoveToState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "MoveTo"; } + void SetGoalPosition( const Vector &pos ) { m_goalPosition = pos; } + void SetRouteType( RouteType route ) { m_routeType = route; } + +private: + Vector m_goalPosition; ///< goal position of move + RouteType m_routeType; ///< the kind of route to build + bool m_radioedPlan; + bool m_askedForCover; +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a Terrorist bot is moving to pick up a dropped bomb. + */ +class FetchBombState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual const char *GetName( void ) const { return "FetchBomb"; } +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a Terrorist bot is actually planting the bomb. + */ +class PlantBombState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "PlantBomb"; } +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a CT bot is actually defusing a live bomb. + */ +class DefuseBombState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "DefuseBomb"; } +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is hiding in a corner. + * NOTE: This state also includes MOVING TO that hiding spot, which may be all the way + * across the map! + */ +class HideState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "Hide"; } + + void SetHidingSpot( const Vector &pos ) { m_hidingSpot = pos; } + const Vector &GetHidingSpot( void ) const { return m_hidingSpot; } + + void SetSearchArea( CNavArea *area ) { m_searchFromArea = area; } + void SetSearchRange( float range ) { m_range = range; } + void SetDuration( float time ) { m_duration = time; } + void SetHoldPosition( bool hold ) { m_isHoldingPosition = hold; } + + bool IsAtSpot( void ) const { return m_isAtSpot; } + + float GetHideTime( void ) const + { + if (IsAtSpot()) + { + return m_duration - m_hideTimer.GetRemainingTime(); + } + + return 0.0f; + } + +private: + CNavArea *m_searchFromArea; + float m_range; + + Vector m_hidingSpot; + bool m_isLookingOutward; + bool m_isAtSpot; + float m_duration; + CountdownTimer m_hideTimer; ///< how long to hide + + bool m_isHoldingPosition; + float m_holdPositionTime; ///< how long to hold our position after we hear nearby enemy noise + + bool m_heardEnemy; ///< set to true when we first hear an enemy + float m_firstHeardEnemyTime; ///< when we first heard the enemy + + int m_retry; ///< counter for retrying hiding spot + + Vector m_leaderAnchorPos; ///< the position of our follow leader when we decided to hide + + bool m_isPaused; ///< if true, we have paused in our retreat for a moment + CountdownTimer m_pauseTimer; ///< for stoppping and starting our pauses while we retreat +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is attempting to flee from a bomb that is about to explode. + */ +class EscapeFromBombState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "EscapeFromBomb"; } +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is following another player. + */ +class FollowState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "Follow"; } + + void SetLeader( CCSPlayer *player ) { m_leader = player; } + +private: + CHandle< CCSPlayer > m_leader; ///< the player we are following + Vector m_lastLeaderPos; ///< where the leader was when we computed our follow path + bool m_isStopped; + float m_stoppedTimestamp; + + enum LeaderMotionStateType + { + INVALID, + STOPPED, + WALKING, + RUNNING + }; + LeaderMotionStateType m_leaderMotionState; + IntervalTimer m_leaderMotionStateTime; + + bool m_isSneaking; + float m_lastSawLeaderTime; + CountdownTimer m_repathInterval; + + IntervalTimer m_walkTime; + bool m_isAtWalkSpeed; + + float m_waitTime; + CountdownTimer m_idleTimer; + + void ComputeLeaderMotionState( float leaderSpeed ); +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is actually using another entity (ie: facing towards it and pressing the use key) + */ +class UseEntityState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "UseEntity"; } + + void SetEntity( CBaseEntity *entity ) { m_entity = entity; } + +private: + EHANDLE m_entity; ///< the entity we will use +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * When a bot is opening a door + */ +class OpenDoorState : public BotState +{ +public: + virtual void OnEnter( CCSBot *bot ); + virtual void OnUpdate( CCSBot *bot ); + virtual void OnExit( CCSBot *bot ); + virtual const char *GetName( void ) const { return "OpenDoor"; } + + void SetDoor( CBaseEntity *door ); + + bool IsDone( void ) const { return m_isDone; } ///< return true if behavior is done + +private: + CHandle< CBaseDoor > m_funcDoor; ///< the func_door we are opening + CHandle< CBasePropDoor > m_propDoor; ///< the prop_door we are opening + bool m_isDone; + CountdownTimer m_timeout; +}; + + +//-------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------------- +/** + * The Counter-strike Bot + */ +class CCSBot : public CBot< CCSPlayer > +{ +public: + DECLARE_CLASS( CCSBot, CBot< CCSPlayer > ); + DECLARE_DATADESC(); + + CCSBot( void ); ///< constructor initializes all values to zero + virtual ~CCSBot(); + virtual bool Initialize( const BotProfile *profile, int team ); ///< (EXTEND) prepare bot for action + + virtual void Spawn( void ); ///< (EXTEND) spawn the bot into the game + virtual void Touch( CBaseEntity *other ); ///< (EXTEND) when touched by another entity + + virtual void Upkeep( void ); ///< lightweight maintenance, invoked frequently + virtual void Update( void ); ///< heavyweight algorithms, invoked less often + virtual void BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse ); + virtual float GetMoveSpeed( void ); ///< returns current movement speed (for walk/run) + + virtual void Walk( void ); + virtual bool Jump( bool mustJump = false ); ///< returns true if jump was started + + //- behavior properties ------------------------------------------------------------------------------------------ + float GetCombatRange( void ) const; + bool IsRogue( void ) const; ///< return true if we dont listen to teammates or pursue scenario goals + void SetRogue( bool rogue ); + bool IsHurrying( void ) const; ///< return true if we are in a hurry + void Hurry( float duration ); ///< force bot to hurry + bool IsSafe( void ) const; ///< return true if we are in a safe region + bool IsWellPastSafe( void ) const; ///< return true if it is well past the early, "safe", part of the round + bool IsEndOfSafeTime( void ) const; ///< return true if we were in the safe time last update, but not now + float GetSafeTimeRemaining( void ) const; ///< return the amount of "safe time" we have left + float GetSafeTime( void ) const; ///< return what we think the total "safe time" for this map is + virtual void Blind( float holdTime, float fadeTime, float startingAlpha = 255 ); // player blinded by a flashbang + bool IsUnhealthy( void ) const; ///< returns true if bot is low on health + + bool IsAlert( void ) const; ///< return true if bot is in heightened "alert" mode + void BecomeAlert( void ); ///< bot becomes "alert" for immediately nearby enemies + + bool IsSneaking( void ) const; ///< return true if bot is sneaking + void Sneak( float duration ); ///< sneak for given duration + + //- behaviors --------------------------------------------------------------------------------------------------- + void Idle( void ); + + void Hide( CNavArea *searchFromArea = NULL, float duration = -1.0f, float hideRange = 750.0f, bool holdPosition = false ); ///< DEPRECATED: Use TryToHide() instead + #define USE_NEAREST true + bool TryToHide( CNavArea *searchFromArea = NULL, float duration = -1.0f, float hideRange = 750.0f, bool holdPosition = false, bool useNearest = false ); ///< try to hide nearby, return false if cannot + void Hide( const Vector &hidingSpot, float duration = -1.0f, bool holdPosition = false ); ///< move to the given hiding place + bool IsHiding( void ) const; ///< returns true if bot is currently hiding + bool IsAtHidingSpot( void ) const; ///< return true if we are hiding and at our hiding spot + float GetHidingTime( void ) const; ///< return number of seconds we have been at our current hiding spot + + bool MoveToInitialEncounter( void ); ///< move to a hiding spot and wait for initial encounter with enemy team (return false if no spots are available) + + bool TryToRetreat( float maxRange = 1000.0f, float duration = -1.0f ); ///< retreat to a nearby hiding spot, away from enemies + + void Hunt( void ); + bool IsHunting( void ) const; ///< returns true if bot is currently hunting + + void Attack( CCSPlayer *victim ); + void FireWeaponAtEnemy( void ); ///< fire our active weapon towards our current enemy + void StopAttacking( void ); + bool IsAttacking( void ) const; ///< returns true if bot is currently engaging a target + + void MoveTo( const Vector &pos, RouteType route = SAFEST_ROUTE ); ///< move to potentially distant position + bool IsMovingTo( void ) const; ///< return true if we are in the MoveTo state + + void PlantBomb( void ); + + void FetchBomb( void ); ///< bomb has been dropped - go get it + bool NoticeLooseBomb( void ) const; ///< return true if we noticed the bomb on the ground or on radar + bool CanSeeLooseBomb( void ) const; ///< return true if we directly see the loose bomb + + void DefuseBomb( void ); + bool IsDefusingBomb( void ) const; ///< returns true if bot is currently defusing the bomb + bool CanSeePlantedBomb( void ) const; ///< return true if we directly see the planted bomb + + void EscapeFromBomb( void ); + bool IsEscapingFromBomb( void ) const; ///< return true if we are escaping from the bomb + + void RescueHostages( void ); ///< begin process of rescuing hostages + + void UseEntity( CBaseEntity *entity ); ///< use the entity + + void OpenDoor( CBaseEntity *door ); ///< open the door (assumes we are right in front of it) + bool IsOpeningDoor( void ) const; ///< return true if we are in the process of opening a door + + void Buy( void ); ///< enter the buy state + bool IsBuying( void ) const; + + void Panic( void ); ///< look around in panic + bool IsPanicking( void ) const; ///< return true if bot is panicked + void StopPanicking( void ); ///< end our panic + void UpdatePanicLookAround( void ); ///< do panic behavior + + void TryToJoinTeam( int team ); ///< try to join the given team + + void Follow( CCSPlayer *player ); ///< begin following given Player + void ContinueFollowing( void ); ///< continue following our leader after finishing what we were doing + void StopFollowing( void ); ///< stop following + bool IsFollowing( void ) const; ///< return true if we are following someone (not necessarily in the follow state) + CCSPlayer *GetFollowLeader( void ) const; ///< return the leader we are following + float GetFollowDuration( void ) const; ///< return how long we've been following our leader + bool CanAutoFollow( void ) const; ///< return true if we can auto-follow + + bool IsNotMoving( float minDuration = 0.0f ) const; ///< return true if we are currently standing still and have been for minDuration + + void AimAtEnemy( void ); ///< point our weapon towards our enemy + void StopAiming( void ); ///< stop aiming at enemy + bool IsAimingAtEnemy( void ) const; ///< returns true if we are trying to aim at an enemy + + float GetStateTimestamp( void ) const; ///< get time current state was entered + + bool IsDoingScenario( void ) const; ///< return true if we will do scenario-related tasks + + //- scenario / gamestate ----------------------------------------------------------------------------------------- + CSGameState *GetGameState( void ); ///< return an interface to this bot's gamestate + const CSGameState *GetGameState( void ) const; ///< return an interface to this bot's gamestate + + bool IsAtBombsite( void ); ///< return true if we are in a bomb planting zone + bool GuardRandomZone( float range = 500.0f ); ///< pick a random zone and hide near it + + bool IsBusy( void ) const; ///< return true if we are busy doing something important + + //- high-level tasks --------------------------------------------------------------------------------------------- + enum TaskType + { + SEEK_AND_DESTROY, + PLANT_BOMB, + FIND_TICKING_BOMB, + DEFUSE_BOMB, + GUARD_TICKING_BOMB, + GUARD_BOMB_DEFUSER, + GUARD_LOOSE_BOMB, + GUARD_BOMB_ZONE, + GUARD_INITIAL_ENCOUNTER, + ESCAPE_FROM_BOMB, + HOLD_POSITION, + FOLLOW, + VIP_ESCAPE, + GUARD_VIP_ESCAPE_ZONE, + COLLECT_HOSTAGES, + RESCUE_HOSTAGES, + GUARD_HOSTAGES, + GUARD_HOSTAGE_RESCUE_ZONE, + MOVE_TO_LAST_KNOWN_ENEMY_POSITION, + MOVE_TO_SNIPER_SPOT, + SNIPING, + + NUM_TASKS + }; + void SetTask( TaskType task, CBaseEntity *entity = NULL ); ///< set our current "task" + TaskType GetTask( void ) const; + CBaseEntity *GetTaskEntity( void ); + const char *GetTaskName( void ) const; ///< return string describing current task + + //- behavior modifiers ------------------------------------------------------------------------------------------ + enum DispositionType + { + ENGAGE_AND_INVESTIGATE, ///< engage enemies on sight and investigate enemy noises + OPPORTUNITY_FIRE, ///< engage enemies on sight, but only look towards enemy noises, dont investigate + SELF_DEFENSE, ///< only engage if fired on, or very close to enemy + IGNORE_ENEMIES, ///< ignore all enemies - useful for ducking around corners, running away, etc + + NUM_DISPOSITIONS + }; + void SetDisposition( DispositionType disposition ); ///< define how we react to enemies + DispositionType GetDisposition( void ) const; + const char *GetDispositionName( void ) const; ///< return string describing current disposition + + void IgnoreEnemies( float duration ); ///< ignore enemies for a short duration + + enum MoraleType + { + TERRIBLE = -3, + BAD = -2, + NEGATIVE = -1, + NEUTRAL = 0, + POSITIVE = 1, + GOOD = 2, + EXCELLENT = 3, + }; + MoraleType GetMorale( void ) const; + const char *GetMoraleName( void ) const; ///< return string describing current morale + void IncreaseMorale( void ); + void DecreaseMorale( void ); + + void Surprise( float duration ); ///< become "surprised" - can't attack + bool IsSurprised( void ) const; ///< return true if we are "surprised" + + + //- listening for noises ---------------------------------------------------------------------------------------- + bool IsNoiseHeard( void ) const; ///< return true if we have heard a noise + bool HeardInterestingNoise( void ); ///< return true if we heard an enemy noise worth checking in to + void InvestigateNoise( void ); ///< investigate recent enemy noise + bool IsInvestigatingNoise( void ) const; ///< return true if we are investigating a noise + const Vector *GetNoisePosition( void ) const; ///< return position of last heard noise, or NULL if none heard + CNavArea *GetNoiseArea( void ) const; ///< return area where noise was heard + void ForgetNoise( void ); ///< clear the last heard noise + bool CanSeeNoisePosition( void ) const; ///< return true if we directly see where we think the noise came from + float GetNoiseRange( void ) const; ///< return approximate distance to last noise heard + + bool CanHearNearbyEnemyGunfire( float range = -1.0f ) const;///< return true if we hear nearby threatening enemy gunfire within given range (-1 == infinite) + PriorityType GetNoisePriority( void ) const; ///< return priority of last heard noise + + //- radio and chatter-------------------------------------------------------------------------------------------- + void SendRadioMessage( RadioType event ); ///< send a radio message + void SpeakAudio( const char *voiceFilename, float duration, int pitch ); ///< send voice chatter + BotChatterInterface *GetChatter( void ); ///< return an interface to this bot's chatter system + bool RespondToHelpRequest( CCSPlayer *player, Place place, float maxRange = -1.0f ); ///< decide if we should move to help the player, return true if we will + bool IsUsingVoice() const; ///< new-style "voice" chatter gets voice feedback + + + //- enemies ------------------------------------------------------------------------------------------------------ + // BOTPORT: GetEnemy() collides with GetEnemy() in CBaseEntity - need to use different nomenclature + void SetBotEnemy( CCSPlayer *enemy ); ///< set given player as our current enemy + CCSPlayer *GetBotEnemy( void ) const; + int GetNearbyEnemyCount( void ) const; ///< return max number of nearby enemies we've seen recently + unsigned int GetEnemyPlace( void ) const; ///< return location where we see the majority of our enemies + bool CanSeeBomber( void ) const; ///< return true if we can see the bomb carrier + CCSPlayer *GetBomber( void ) const; + + int GetNearbyFriendCount( void ) const; ///< return number of nearby teammates + CCSPlayer *GetClosestVisibleFriend( void ) const; ///< return the closest friend that we can see + CCSPlayer *GetClosestVisibleHumanFriend( void ) const; ///< return the closest human friend that we can see + + bool IsOutnumbered( void ) const; ///< return true if we are outnumbered by enemies + int OutnumberedCount( void ) const; ///< return number of enemies we are outnumbered by + + #define ONLY_VISIBLE_ENEMIES true + CCSPlayer *GetImportantEnemy( bool checkVisibility = false ) const; ///< return the closest "important" enemy for the given scenario (bomb carrier, VIP, hostage escorter) + + void UpdateReactionQueue( void ); ///< update our reaction time queue + CCSPlayer *GetRecognizedEnemy( void ); ///< return the most dangerous threat we are "conscious" of + bool IsRecognizedEnemyReloading( void ); ///< return true if the enemy we are "conscious" of is reloading + bool IsRecognizedEnemyProtectedByShield( void ); ///< return true if the enemy we are "conscious" of is hiding behind a shield + float GetRangeToNearestRecognizedEnemy( void ); ///< return distance to closest enemy we are "conscious" of + + CCSPlayer *GetAttacker( void ) const; ///< return last enemy that hurt us + float GetTimeSinceAttacked( void ) const; ///< return duration since we were last injured by an attacker + float GetFirstSawEnemyTimestamp( void ) const; ///< time since we saw any enemies + float GetLastSawEnemyTimestamp( void ) const; + float GetTimeSinceLastSawEnemy( void ) const; + float GetTimeSinceAcquiredCurrentEnemy( void ) const; + bool HasNotSeenEnemyForLongTime( void ) const; ///< return true if we haven't seen an enemy for "a long time" + const Vector &GetLastKnownEnemyPosition( void ) const; + bool IsEnemyVisible( void ) const; ///< is our current enemy visible + float GetEnemyDeathTimestamp( void ) const; + bool IsFriendInLineOfFire( void ); ///< return true if a friend is in our weapon's way + bool IsAwareOfEnemyDeath( void ) const; ///< return true if we *noticed* that our enemy died + int GetLastVictimID( void ) const; ///< return the ID (entindex) of the last victim we killed, or zero + + bool CanSeeSniper( void ) const; ///< return true if we can see an enemy sniper + bool HasSeenSniperRecently( void ) const; ///< return true if we have seen a sniper recently + + float GetTravelDistanceToPlayer( CCSPlayer *player ) const; ///< return shortest path travel distance to this player + bool DidPlayerJustFireWeapon( const CCSPlayer *player ) const; ///< return true if the given player just fired their weapon + + //- navigation -------------------------------------------------------------------------------------------------- + bool HasPath( void ) const; + void DestroyPath( void ); + + float GetFeetZ( void ) const; ///< return Z of bottom of feet + + enum PathResult + { + PROGRESSING, ///< we are moving along the path + END_OF_PATH, ///< we reached the end of the path + PATH_FAILURE ///< we failed to reach the end of the path + }; + #define NO_SPEED_CHANGE false + PathResult UpdatePathMovement( bool allowSpeedChange = true ); ///< move along our computed path - if allowSpeedChange is true, bot will walk when near goal to ensure accuracy + + //bool AStarSearch( CNavArea *startArea, CNavArea *goalArea ); ///< find shortest path from startArea to goalArea - don't actually buid the path + bool ComputePath( const Vector &goal, RouteType route = SAFEST_ROUTE ); ///< compute path to goal position + bool StayOnNavMesh( void ); + CNavArea *GetLastKnownArea( void ) const; ///< return the last area we know we were inside of + const Vector &GetPathEndpoint( void ) const; ///< return final position of our current path + float GetPathDistanceRemaining( void ) const; ///< return estimated distance left to travel along path + void ResetStuckMonitor( void ); + bool IsAreaVisible( const CNavArea *area ) const; ///< is any portion of the area visible to this bot + const Vector &GetPathPosition( int index ) const; + bool GetSimpleGroundHeightWithFloor( const Vector &pos, float *height, Vector *normal = NULL ); ///< find "simple" ground height, treating current nav area as part of the floor + void BreakablesCheck( void ); + void DoorCheck( void ); ///< Check for any doors along our path that need opening + + virtual void PushawayTouch( CBaseEntity *pOther ); + + Place GetPlace( void ) const; ///< get our current radio chatter place + + bool IsUsingLadder( void ) const; ///< returns true if we are in the process of negotiating a ladder + void GetOffLadder( void ); ///< immediately jump off of our ladder, if we're on one + + void SetGoalEntity( CBaseEntity *entity ); + CBaseEntity *GetGoalEntity( void ); + + bool IsNearJump( void ) const; ///< return true if nearing a jump in the path + float GetApproximateFallDamage( float height ) const; ///< return how much damage will will take from the given fall height + + void ForceRun( float duration ); ///< force the bot to run if it moves for the given duration + virtual bool IsRunning( void ) const; + + void Wait( float duration ); ///< wait where we are for the given duration + bool IsWaiting( void ) const; ///< return true if we are waiting + void StopWaiting( void ); ///< stop waiting + + void Wiggle( void ); ///< random movement, for getting un-stuck + + bool IsFriendInTheWay( const Vector &goalPos ); ///< return true if a friend is between us and the given position + void FeelerReflexAdjustment( Vector *goalPosition ); ///< do reflex avoidance movements if our "feelers" are touched + + bool HasVisitedEnemySpawn( void ) const; ///< return true if we have visited enemy spawn at least once + bool IsAtEnemySpawn( void ) const; ///< return true if we are at the/an enemy spawn right now + + //- looking around ---------------------------------------------------------------------------------------------- + + // BOTPORT: EVIL VILE HACK - why is EyePosition() not const?!?!? + const Vector &EyePositionConst( void ) const; + + void SetLookAngles( float yaw, float pitch ); ///< set our desired look angles + void UpdateLookAngles( void ); ///< move actual view angles towards desired ones + void UpdateLookAround( bool updateNow = false ); ///< update "looking around" mechanism + void InhibitLookAround( float duration ); ///< block all "look at" and "looking around" behavior for given duration - just look ahead + + /// @todo Clean up notion of "forward angle" and "look ahead angle" + void SetForwardAngle( float angle ); ///< define our forward facing + void SetLookAheadAngle( float angle ); ///< define default look ahead angle + + /// look at the given point in space for the given duration (-1 means forever) + void SetLookAt( const char *desc, const Vector &pos, PriorityType pri, float duration = -1.0f, bool clearIfClose = false, float angleTolerance = 5.0f, bool attack = false ); + void ClearLookAt( void ); ///< stop looking at a point in space and just look ahead + bool IsLookingAtSpot( PriorityType pri = PRIORITY_LOW ) const; ///< return true if we are looking at spot with equal or higher priority + bool IsViewMoving( float angleVelThreshold = 1.0f ) const; ///< returns true if bot's view angles are rotating (not still) + bool HasViewBeenSteady( float duration ) const; ///< how long has our view been "steady" (ie: not moving) for given duration + + bool HasLookAtTarget( void ) const; ///< return true if we are in the process of looking at a target + + enum VisiblePartType + { + NONE = 0x00, + GUT = 0x01, + HEAD = 0x02, + LEFT_SIDE = 0x04, ///< the left side of the object from our point of view (not their left side) + RIGHT_SIDE = 0x08, ///< the right side of the object from our point of view (not their right side) + FEET = 0x10 + }; + + #define CHECK_FOV true + bool IsVisible( const Vector &pos, bool testFOV = false, const CBaseEntity *ignore = NULL ) const; ///< return true if we can see the point + bool IsVisible( CCSPlayer *player, bool testFOV = false, unsigned char *visParts = NULL ) const; ///< return true if we can see any part of the player + + bool IsNoticable( const CCSPlayer *player, unsigned char visibleParts ) const; ///< return true if we "notice" given player + + bool IsEnemyPartVisible( VisiblePartType part ) const; ///< if enemy is visible, return the part we see for our current enemy + const Vector &GetPartPosition( CCSPlayer *player, VisiblePartType part ) const; ///< return world space position of given part on player + + float ComputeWeaponSightRange( void ); ///< return line-of-sight distance to obstacle along weapon fire ray + + bool IsAnyVisibleEnemyLookingAtMe( bool testFOV = false ) const;///< return true if any enemy I have LOS to is looking directly at me + + bool IsSignificantlyCloser( const CCSPlayer *testPlayer, const CCSPlayer *referencePlayer ) const; ///< return true if testPlayer is significantly closer than referencePlayer + + //- approach points --------------------------------------------------------------------------------------------- + void ComputeApproachPoints( void ); ///< determine the set of "approach points" representing where the enemy can enter this region + void UpdateApproachPoints( void ); ///< recompute the approach point set if we have moved far enough to invalidate the current ones + void ClearApproachPoints( void ); + void DrawApproachPoints( void ) const; ///< for debugging + float GetHidingSpotCheckTimestamp( HidingSpot *spot ) const; ///< return time when given spot was last checked + void SetHidingSpotCheckTimestamp( HidingSpot *spot ); ///< set the timestamp of the given spot to now + + const CNavArea *GetInitialEncounterArea( void ) const; ///< return area where we think we will first meet the enemy + void SetInitialEncounterArea( const CNavArea *area ); + + //- weapon query and equip -------------------------------------------------------------------------------------- + #define MUST_EQUIP true + void EquipBestWeapon( bool mustEquip = false ); ///< equip the best weapon we are carrying that has ammo + void EquipPistol( void ); ///< equip our pistol + void EquipKnife( void ); ///< equip the knife + + #define DONT_USE_SMOKE_GRENADE true + bool EquipGrenade( bool noSmoke = false ); ///< equip a grenade, return false if we cant + + bool IsUsingKnife( void ) const; ///< returns true if we have knife equipped + bool IsUsingPistol( void ) const; ///< returns true if we have pistol equipped + bool IsUsingGrenade( void ) const; ///< returns true if we have grenade equipped + bool IsUsingSniperRifle( void ) const; ///< returns true if using a "sniper" rifle + bool IsUsing( CSWeaponID weapon ) const; ///< returns true if using the specific weapon + bool IsSniper( void ) const; ///< return true if we have a sniper rifle in our inventory + bool IsSniping( void ) const; ///< return true if we are actively sniping (moving to sniper spot or settled in) + bool IsUsingShotgun( void ) const; ///< returns true if using a shotgun + bool IsUsingMachinegun( void ) const; ///< returns true if using the big 'ol machinegun + void ThrowGrenade( const Vector &target ); ///< begin the process of throwing the grenade + bool IsThrowingGrenade( void ) const; ///< return true if we are in the process of throwing a grenade + bool HasGrenade( void ) const; ///< return true if we have a grenade in our inventory + void AvoidEnemyGrenades( void ); ///< react to enemy grenades we see + bool IsAvoidingGrenade( void ) const; ///< return true if we are in the act of avoiding a grenade + bool DoesActiveWeaponHaveSilencer( void ) const; ///< returns true if we are using a weapon with a removable silencer + bool CanActiveWeaponFire( void ) const; ///< returns true if our current weapon can attack + CWeaponCSBase *GetActiveCSWeapon( void ) const; ///< get our current Counter-Strike weapon + + void GiveWeapon( const char *weaponAlias ); ///< Debug command to give a named weapon + + virtual void PrimaryAttack( void ); ///< presses the fire button, unless we're holding a pistol that can't fire yet (so we can just always call PrimaryAttack()) + + enum ZoomType { NO_ZOOM, LOW_ZOOM, HIGH_ZOOM }; + ZoomType GetZoomLevel( void ); ///< return the current zoom level of our weapon + + bool AdjustZoom( float range ); ///< change our zoom level to be appropriate for the given range + bool IsWaitingForZoom( void ) const; ///< return true if we are reacquiring after our zoom + + bool IsPrimaryWeaponEmpty( void ) const; ///< return true if primary weapon doesn't exist or is totally out of ammo + bool IsPistolEmpty( void ) const; ///< return true if pistol doesn't exist or is totally out of ammo + + int GetHostageEscortCount( void ) const; ///< return the number of hostages following me + void IncreaseHostageEscortCount( void ); + float GetRangeToFarthestEscortedHostage( void ) const; ///< return euclidean distance to farthest escorted hostage + void ResetWaitForHostagePatience( void ); + + //------------------------------------------------------------------------------------ + // Event hooks + // + + /// invoked when injured by something (EXTEND) - returns the amount of damage inflicted + virtual int OnTakeDamage( const CTakeDamageInfo &info ); + + /// invoked when killed (EXTEND) + virtual void Event_Killed( const CTakeDamageInfo &info ); + + virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon ); ///< invoked when in contact with a CWeaponBox + + + /// invoked when event occurs in the game (some events have NULL entity) + void OnPlayerFootstep( IGameEvent *event ); + void OnPlayerRadio( IGameEvent *event ); + void OnPlayerDeath( IGameEvent *event ); + void OnPlayerFallDamage( IGameEvent *event ); + + void OnBombPickedUp( IGameEvent *event ); + void OnBombPlanted( IGameEvent *event ); + void OnBombBeep( IGameEvent *event ); + void OnBombDefuseBegin( IGameEvent *event ); + void OnBombDefused( IGameEvent *event ); + void OnBombDefuseAbort( IGameEvent *event ); + void OnBombExploded( IGameEvent *event ); + + void OnRoundEnd( IGameEvent *event ); + void OnRoundStart( IGameEvent *event ); + + void OnDoorMoving( IGameEvent *event ); + + void OnBreakProp( IGameEvent *event ); + void OnBreakBreakable( IGameEvent *event ); + + void OnHostageFollows( IGameEvent *event ); + void OnHostageRescuedAll( IGameEvent *event ); + + void OnWeaponFire( IGameEvent *event ); + void OnWeaponFireOnEmpty( IGameEvent *event ); + void OnWeaponReload( IGameEvent *event ); + void OnWeaponZoom( IGameEvent *event ); + + void OnBulletImpact( IGameEvent *event ); + + void OnHEGrenadeDetonate( IGameEvent *event ); + void OnFlashbangDetonate( IGameEvent *event ); + void OnSmokeGrenadeDetonate( IGameEvent *event ); + void OnGrenadeBounce( IGameEvent *event ); + + void OnNavBlocked( IGameEvent *event ); + + void OnEnteredNavArea( CNavArea *newArea ); ///< invoked when bot enters a nav area + +private: + #define IS_FOOTSTEP true + void OnAudibleEvent( IGameEvent *event, CBasePlayer *player, float range, PriorityType priority, bool isHostile, bool isFootstep = false, const Vector *actualOrigin = NULL ); ///< Checks if the bot can hear the event + +private: + friend class CCSBotManager; + + /// @todo Get rid of these + friend class AttackState; + friend class BuyState; + + // BOTPORT: Remove this vile hack + Vector m_eyePosition; + + void ResetValues( void ); ///< reset internal data to initial state + void BotDeathThink( void ); + + char m_name[64]; ///< copied from STRING(pev->netname) for debugging + void DebugDisplay( void ) const; ///< render bot debug info + + //- behavior properties ------------------------------------------------------------------------------------------ + float m_combatRange; ///< desired distance between us and them during gunplay + mutable bool m_isRogue; ///< if true, the bot is a "rogue" and listens to no-one + mutable CountdownTimer m_rogueTimer; + MoraleType m_morale; ///< our current morale, based on our win/loss history + bool m_diedLastRound; ///< true if we died last round + float m_safeTime; ///< duration at the beginning of the round where we feel "safe" + bool m_wasSafe; ///< true if we were in the safe time last update + void AdjustSafeTime( void ); ///< called when enemy seen to adjust safe time for this round + NavRelativeDirType m_blindMoveDir; ///< which way to move when we're blind + bool m_blindFire; ///< if true, fire weapon while blinded + CountdownTimer m_surpriseTimer; ///< when we were surprised + + bool m_isFollowing; ///< true if we are following someone + CHandle< CCSPlayer > m_leader; ///< the ID of who we are following + float m_followTimestamp; ///< when we started following + float m_allowAutoFollowTime; ///< time when we can auto follow + + CountdownTimer m_hurryTimer; ///< if valid, bot is in a hurry + CountdownTimer m_alertTimer; ///< if valid, bot is alert + CountdownTimer m_sneakTimer; ///< if valid, bot is sneaking + CountdownTimer m_panicTimer; ///< if valid, bot is panicking + + + // instances of each possible behavior state, to avoid dynamic memory allocation during runtime + IdleState m_idleState; + HuntState m_huntState; + AttackState m_attackState; + InvestigateNoiseState m_investigateNoiseState; + BuyState m_buyState; + MoveToState m_moveToState; + FetchBombState m_fetchBombState; + PlantBombState m_plantBombState; + DefuseBombState m_defuseBombState; + HideState m_hideState; + EscapeFromBombState m_escapeFromBombState; + FollowState m_followState; + UseEntityState m_useEntityState; + OpenDoorState m_openDoorState; + + /// @todo Allow multiple simultaneous state machines (look around, etc) + void SetState( BotState *state ); ///< set the current behavior state + BotState *m_state; ///< current behavior state + float m_stateTimestamp; ///< time state was entered + bool m_isAttacking; ///< if true, special Attack state is overriding the state machine + bool m_isOpeningDoor; ///< if true, special OpenDoor state is overriding the state machine + + TaskType m_task; ///< our current task + EHANDLE m_taskEntity; ///< an entity used for our task + + //- navigation --------------------------------------------------------------------------------------------------- + Vector m_goalPosition; + EHANDLE m_goalEntity; + void MoveTowardsPosition( const Vector &pos ); ///< move towards position, independant of view angle + void MoveAwayFromPosition( const Vector &pos ); ///< move away from position, independant of view angle + void StrafeAwayFromPosition( const Vector &pos ); ///< strafe (sidestep) away from position, independant of view angle + void StuckCheck( void ); ///< check if we have become stuck + CCSNavArea *m_currentArea; ///< the nav area we are standing on + CCSNavArea *m_lastKnownArea; ///< the last area we were in + EHANDLE m_avoid; ///< higher priority player we need to make way for + float m_avoidTimestamp; + bool m_isStopping; ///< true if we're trying to stop because we entered a 'stop' nav area + bool m_hasVisitedEnemySpawn; ///< true if we have been at the enemy spawn + IntervalTimer m_stillTimer; ///< how long we have been not moving + + //- path navigation data ---------------------------------------------------------------------------------------- + enum { MAX_PATH_LENGTH = 256 }; + struct ConnectInfo + { + CNavArea *area; ///< the area along the path + NavTraverseType how; ///< how to enter this area from the previous one + Vector pos; ///< our movement goal position at this point in the path + const CNavLadder *ladder; ///< if "how" refers to a ladder, this is it + } + m_path[ MAX_PATH_LENGTH ]; + int m_pathLength; + int m_pathIndex; ///< index of next area on path + float m_areaEnteredTimestamp; + void BuildTrivialPath( const Vector &goal ); ///< build trivial path to goal, assuming we are already in the same area + + CountdownTimer m_repathTimer; ///< must have elapsed before bot can pathfind again + + bool ComputePathPositions( void ); ///< determine actual path positions bot will move between along the path + void SetupLadderMovement( void ); + void SetPathIndex( int index ); ///< set the current index along the path + void DrawPath( void ); + int FindOurPositionOnPath( Vector *close, bool local = false ) const; ///< compute the closest point to our current position on our path + int FindPathPoint( float aheadRange, Vector *point, int *prevIndex = NULL ); ///< compute a point a fixed distance ahead along our path. + bool FindClosestPointOnPath( const Vector &pos, int startIndex, int endIndex, Vector *close ) const; ///< compute closest point on path to given point + bool IsStraightLinePathWalkable( const Vector &goal ) const; ///< test for un-jumpable height change, or unrecoverable fall + void ComputeLadderAngles( float *yaw, float *pitch ); ///< computes ideal yaw/pitch for traversing the current ladder on our path + + mutable CountdownTimer m_avoidFriendTimer; ///< used to throttle how often we check for friends in our path + mutable bool m_isFriendInTheWay; ///< true if a friend is blocking our path + CountdownTimer m_politeTimer; ///< we'll wait for friend to move until this runs out + bool m_isWaitingBehindFriend; ///< true if we are waiting for a friend to move + + #define ONLY_JUMP_DOWN true + bool DiscontinuityJump( float ground, bool onlyJumpDown = false, bool mustJump = false ); ///< check if we need to jump due to height change + + enum LadderNavState + { + APPROACH_ASCENDING_LADDER, ///< prepare to scale a ladder + APPROACH_DESCENDING_LADDER, ///< prepare to go down ladder + FACE_ASCENDING_LADDER, + FACE_DESCENDING_LADDER, + MOUNT_ASCENDING_LADDER, ///< move toward ladder until "on" it + MOUNT_DESCENDING_LADDER, ///< move toward ladder until "on" it + ASCEND_LADDER, ///< go up the ladder + DESCEND_LADDER, ///< go down the ladder + DISMOUNT_ASCENDING_LADDER, ///< get off of the ladder + DISMOUNT_DESCENDING_LADDER, ///< get off of the ladder + MOVE_TO_DESTINATION, ///< dismount ladder and move to destination area + } + m_pathLadderState; + bool m_pathLadderFaceIn; ///< if true, face towards ladder, otherwise face away + const CNavLadder *m_pathLadder; ///< the ladder we need to use to reach the next area + bool UpdateLadderMovement( void ); ///< called by UpdatePathMovement() + NavRelativeDirType m_pathLadderDismountDir; ///< which way to dismount + float m_pathLadderDismountTimestamp; ///< time when dismount started + float m_pathLadderEnd; ///< if ascending, z of top, if descending z of bottom + void ComputeLadderEndpoint( bool ascending ); + float m_pathLadderTimestamp; ///< time when we started using ladder - for timeout check + + CountdownTimer m_mustRunTimer; ///< if nonzero, bot cannot walk + CountdownTimer m_waitTimer; ///< if nonzero, we are waiting where we are + + void UpdateTravelDistanceToAllPlayers( void ); ///< periodically compute shortest path distance to each player + CountdownTimer m_updateTravelDistanceTimer; ///< for throttling travel distance computations + float m_playerTravelDistance[ MAX_PLAYERS ]; ///< current distance from this bot to each player + unsigned char m_travelDistancePhase; ///< a counter for optimizing when to compute travel distance + + //- game scenario mechanisms ------------------------------------------------------------------------------------- + CSGameState m_gameState; ///< our current knowledge about the state of the scenario + + byte m_hostageEscortCount; ///< the number of hostages we're currently escorting + void UpdateHostageEscortCount( void ); ///< periodic check of hostage count in case we lost some + float m_hostageEscortCountTimestamp; + + int m_desiredTeam; ///< the team we want to be on + bool m_hasJoined; ///< true if bot has actually joined the game + + bool m_isWaitingForHostage; + CountdownTimer m_inhibitWaitingForHostageTimer; ///< if active, inhibits us waiting for lagging hostages + CountdownTimer m_waitForHostageTimer; ///< stops us waiting too long + + //- listening mechanism ------------------------------------------------------------------------------------------ + Vector m_noisePosition; ///< position we last heard non-friendly noise + float m_noiseTravelDistance; ///< the travel distance to the noise + float m_noiseTimestamp; ///< when we heard it (can get zeroed) + CNavArea *m_noiseArea; ///< the nav area containing the noise + PriorityType m_noisePriority; ///< priority of currently heard noise + bool UpdateLookAtNoise( void ); ///< return true if we decided to look towards the most recent noise source + CountdownTimer m_noiseBendTimer; ///< for throttling how often we bend our line of sight to the noise location + Vector m_bentNoisePosition; ///< the last computed bent line of sight + bool m_bendNoisePositionValid; + + //- "looking around" mechanism ----------------------------------------------------------------------------------- + float m_lookAroundStateTimestamp; ///< time of next state change + float m_lookAheadAngle; ///< our desired forward look angle + float m_forwardAngle; ///< our current forward facing direction + float m_inhibitLookAroundTimestamp; ///< time when we can look around again + + enum LookAtSpotState + { + NOT_LOOKING_AT_SPOT, ///< not currently looking at a point in space + LOOK_TOWARDS_SPOT, ///< in the process of aiming at m_lookAtSpot + LOOK_AT_SPOT, ///< looking at m_lookAtSpot + NUM_LOOK_AT_SPOT_STATES + } + m_lookAtSpotState; + Vector m_lookAtSpot; ///< the spot we're currently looking at + PriorityType m_lookAtSpotPriority; + float m_lookAtSpotDuration; ///< how long we need to look at the spot + float m_lookAtSpotTimestamp; ///< when we actually began looking at the spot + float m_lookAtSpotAngleTolerance; ///< how exactly we must look at the spot + bool m_lookAtSpotClearIfClose; ///< if true, the look at spot is cleared if it gets close to us + bool m_lookAtSpotAttack; ///< if true, the look at spot should be attacked + const char *m_lookAtDesc; ///< for debugging + void UpdateLookAt( void ); + void UpdatePeripheralVision(); ///< update enounter spot timestamps, etc + float m_peripheralTimestamp; + + enum { MAX_APPROACH_POINTS = 16 }; + struct ApproachPoint + { + Vector m_pos; + CNavArea *m_area; + }; + + ApproachPoint m_approachPoint[ MAX_APPROACH_POINTS ]; + unsigned char m_approachPointCount; + Vector m_approachPointViewPosition; ///< the position used when computing current approachPoint set + + CBaseEntity * FindEntitiesOnPath( float distance, CPushAwayEnumerator *enumerator, bool checkStuck ); + + IntervalTimer m_viewSteadyTimer; ///< how long has our view been "steady" (ie: not moving) + + bool BendLineOfSight( const Vector &eye, const Vector &target, Vector *bend, float angleLimit = 135.0f ) const; ///< "bend" our line of sight until we can see the target point. Return bend point, false if cant bend. + bool FindApproachPointNearestPath( Vector *pos ); ///< find the approach point that is nearest to our current path, ahead of us + bool FindGrenadeTossPathTarget( Vector *pos ); ///< find spot to throw grenade ahead of us and "around the corner" along our path + enum GrenadeTossState + { + NOT_THROWING, ///< not yet throwing + START_THROW, ///< lining up throw + THROW_LINED_UP, ///< pause for a moment when on-line + FINISH_THROW, ///< throwing + }; + GrenadeTossState m_grenadeTossState; + CountdownTimer m_tossGrenadeTimer; ///< timeout timer for grenade tossing + const CNavArea *m_initialEncounterArea; ///< area where we think we will initially encounter the enemy + void LookForGrenadeTargets( void ); ///< look for grenade throw targets and throw our grenade at them + void UpdateGrenadeThrow( void ); ///< process grenade throwing + CountdownTimer m_isAvoidingGrenade; ///< if nonzero we are in the act of avoiding a grenade + + + SpotEncounter *m_spotEncounter; ///< the spots we will encounter as we move thru our current area + float m_spotCheckTimestamp; ///< when to check next encounter spot + + /// @todo Add timestamp for each possible client to hiding spots + enum { MAX_CHECKED_SPOTS = 64 }; + struct HidingSpotCheckInfo + { + HidingSpot *spot; + float timestamp; + } + m_checkedHidingSpot[ MAX_CHECKED_SPOTS ]; + int m_checkedHidingSpotCount; + + //- view angle mechanism ----------------------------------------------------------------------------------------- + float m_lookPitch; ///< our desired look pitch angle + float m_lookPitchVel; + float m_lookYaw; ///< our desired look yaw angle + float m_lookYawVel; + + //- aim angle mechanism ----------------------------------------------------------------------------------------- + Vector m_aimOffset; ///< current error added to victim's position to get actual aim spot + Vector m_aimOffsetGoal; ///< desired aim offset + float m_aimOffsetTimestamp; ///< time of next offset adjustment + float m_aimSpreadTimestamp; ///< time used to determine max spread as it begins to tighten up + void SetAimOffset( float accuracy ); ///< set the current aim offset + void UpdateAimOffset( void ); ///< wiggle aim error based on m_accuracy + Vector m_aimSpot; ///< the spot we are currently aiming to fire at + + struct PartInfo + { + Vector m_headPos; ///< current head position + Vector m_gutPos; ///< current gut position + Vector m_feetPos; ///< current feet position + Vector m_leftSidePos; ///< current left side position + Vector m_rightSidePos; ///< current right side position + int m_validFrame; ///< frame of last computation (for lazy evaluation) + }; + static PartInfo m_partInfo[ MAX_PLAYERS ]; ///< part positions for each player + void ComputePartPositions( CCSPlayer *player ); ///< compute part positions from bone location + + //- attack state data -------------------------------------------------------------------------------------------- + DispositionType m_disposition; ///< how we will react to enemies + CountdownTimer m_ignoreEnemiesTimer; ///< how long will we ignore enemies + mutable CHandle< CCSPlayer > m_enemy; ///< our current enemy + bool m_isEnemyVisible; ///< result of last visibility test on enemy + unsigned char m_visibleEnemyParts; ///< which parts of the visible enemy do we see + Vector m_lastEnemyPosition; ///< last place we saw the enemy + float m_lastSawEnemyTimestamp; + float m_firstSawEnemyTimestamp; + float m_currentEnemyAcquireTimestamp; + float m_enemyDeathTimestamp; ///< if m_enemy is dead, this is when he died + float m_friendDeathTimestamp; ///< time since we saw a friend die + bool m_isLastEnemyDead; ///< true if we killed or saw our last enemy die + int m_nearbyEnemyCount; ///< max number of enemies we've seen recently + unsigned int m_enemyPlace; ///< the location where we saw most of our enemies + + struct WatchInfo + { + float timestamp; ///< time we last saw this player, zero if never seen + bool isEnemy; + } + m_watchInfo[ MAX_PLAYERS ]; + mutable CHandle< CCSPlayer > m_bomber; ///< points to bomber if we can see him + + int m_nearbyFriendCount; ///< number of nearby teammates + mutable CHandle< CCSPlayer > m_closestVisibleFriend; ///< the closest friend we can see + mutable CHandle< CCSPlayer > m_closestVisibleHumanFriend; ///< the closest human friend we can see + + IntervalTimer m_attentionInterval; ///< time between attention checks + + mutable CHandle< CCSPlayer > m_attacker; ///< last enemy that hurt us (may not be same as m_enemy) + float m_attackedTimestamp; ///< when we were hurt by the m_attacker + + int m_lastVictimID; ///< the entindex of the last victim we killed, or zero + bool m_isAimingAtEnemy; ///< if true, we are trying to aim at our enemy + bool m_isRapidFiring; ///< if true, RunUpkeep() will toggle our primary attack as fast as it can + IntervalTimer m_equipTimer; ///< how long have we had our current weapon equipped + CountdownTimer m_zoomTimer; ///< for delaying firing immediately after zoom + bool DoEquip( CWeaponCSBase *gun ); ///< equip the given item + + void ReloadCheck( void ); ///< reload our weapon if we must + void SilencerCheck( void ); ///< use silencer + + float m_fireWeaponTimestamp; + + bool m_isEnemySniperVisible; ///< do we see an enemy sniper right now + CountdownTimer m_sawEnemySniperTimer; ///< tracking time since saw enemy sniper + + //- reaction time system ----------------------------------------------------------------------------------------- + enum { MAX_ENEMY_QUEUE = 20 }; + struct ReactionState + { + // NOTE: player position & orientation is not currently stored separately + CHandle<CCSPlayer> player; + bool isReloading; + bool isProtectedByShield; + } + m_enemyQueue[ MAX_ENEMY_QUEUE ]; ///< round-robin queue for simulating reaction times + byte m_enemyQueueIndex; + byte m_enemyQueueCount; + byte m_enemyQueueAttendIndex; ///< index of the timeframe we are "conscious" of + + CCSPlayer *FindMostDangerousThreat( void ); ///< return most dangerous threat in my field of view (feeds into reaction time queue) + + + //- stuck detection --------------------------------------------------------------------------------------------- + bool m_isStuck; + float m_stuckTimestamp; ///< time when we got stuck + Vector m_stuckSpot; ///< the location where we became stuck + NavRelativeDirType m_wiggleDirection; + CountdownTimer m_wiggleTimer; + CountdownTimer m_stuckJumpTimer; ///< time for next jump when stuck + + enum { MAX_VEL_SAMPLES = 10 }; + float m_avgVel[ MAX_VEL_SAMPLES ]; + int m_avgVelIndex; + int m_avgVelCount; + Vector m_lastOrigin; + + //- radio -------------------------------------------------------------------------------------------------------- + RadioType m_lastRadioCommand; ///< last radio command we recieved + float m_lastRadioRecievedTimestamp; ///< time we recieved a radio message + float m_lastRadioSentTimestamp; ///< time when we send a radio message + CHandle< CCSPlayer > m_radioSubject; ///< who issued the radio message + Vector m_radioPosition; ///< position referred to in radio message + void RespondToRadioCommands( void ); + bool IsRadioCommand( RadioType event ) const; ///< returns true if the radio message is an order to do something + + /// new-style "voice" chatter gets voice feedback + float m_voiceEndTimestamp; + + BotChatterInterface m_chatter; ///< chatter mechanism +}; + + +// +// Inlines +// + +inline float CCSBot::GetFeetZ( void ) const +{ + return GetAbsOrigin().z; +} + +inline const Vector *CCSBot::GetNoisePosition( void ) const +{ + if (m_noiseTimestamp > 0.0f) + return &m_noisePosition; + + return NULL; +} + +inline bool CCSBot::IsAwareOfEnemyDeath( void ) const +{ + if (GetEnemyDeathTimestamp() == 0.0f) + return false; + + if (m_enemy == NULL) + return true; + + if (!m_enemy->IsAlive() && gpGlobals->curtime - GetEnemyDeathTimestamp() > (1.0f - 0.8f * GetProfile()->GetSkill())) + return true; + + return false; +} + +inline void CCSBot::Panic( void ) +{ + // we are stunned for a moment + Surprise( RandomFloat( 0.2f, 0.3f ) ); + + const float panicTime = 3.0f; + m_panicTimer.Start( panicTime ); + + const float panicRetreatRange = 300.0f; + TryToRetreat( panicRetreatRange, 0.0f ); + + PrintIfWatched( "*** PANIC ***\n" ); +} + +inline bool CCSBot::IsPanicking( void ) const +{ + return !m_panicTimer.IsElapsed(); +} + +inline void CCSBot::StopPanicking( void ) +{ + m_panicTimer.Invalidate(); +} + +inline bool CCSBot::IsNotMoving( float minDuration ) const +{ + return (m_stillTimer.HasStarted() && m_stillTimer.GetElapsedTime() >= minDuration); +} + +inline CWeaponCSBase *CCSBot::GetActiveCSWeapon( void ) const +{ + return reinterpret_cast<CWeaponCSBase *>( GetActiveWeapon() ); +} + + +inline float CCSBot::GetCombatRange( void ) const +{ + return m_combatRange; +} + +inline void CCSBot::SetRogue( bool rogue ) +{ + m_isRogue = rogue; +} + +inline void CCSBot::Hurry( float duration ) +{ + m_hurryTimer.Start( duration ); +} + +inline float CCSBot::GetSafeTime( void ) const +{ + return m_safeTime; +} + +inline bool CCSBot::IsUnhealthy( void ) const +{ + return (GetHealth() <= 40); +} + +inline bool CCSBot::IsAlert( void ) const +{ + return !m_alertTimer.IsElapsed(); +} + +inline void CCSBot::BecomeAlert( void ) +{ + const float alertCooldownTime = 10.0f; + m_alertTimer.Start( alertCooldownTime ); +} + +inline bool CCSBot::IsSneaking( void ) const +{ + return !m_sneakTimer.IsElapsed(); +} + +inline void CCSBot::Sneak( float duration ) +{ + m_sneakTimer.Start( duration ); +} + +inline bool CCSBot::IsFollowing( void ) const +{ + return m_isFollowing; +} + +inline CCSPlayer *CCSBot::GetFollowLeader( void ) const +{ + return m_leader; +} + +inline float CCSBot::GetFollowDuration( void ) const +{ + return gpGlobals->curtime - m_followTimestamp; +} + +inline bool CCSBot::CanAutoFollow( void ) const +{ + return (gpGlobals->curtime > m_allowAutoFollowTime); +} + +inline void CCSBot::AimAtEnemy( void ) +{ + m_isAimingAtEnemy = true; +} + +inline void CCSBot::StopAiming( void ) +{ + m_isAimingAtEnemy = false; +} + +inline bool CCSBot::IsAimingAtEnemy( void ) const +{ + return m_isAimingAtEnemy; +} + +inline float CCSBot::GetStateTimestamp( void ) const +{ + return m_stateTimestamp; +} + +inline CSGameState *CCSBot::GetGameState( void ) +{ + return &m_gameState; +} + +inline const CSGameState *CCSBot::GetGameState( void ) const +{ + return &m_gameState; +} + +inline bool CCSBot::IsAtBombsite( void ) +{ + return m_bInBombZone; +} + +inline void CCSBot::SetTask( TaskType task, CBaseEntity *entity ) +{ + m_task = task; + m_taskEntity = entity; +} + +inline CCSBot::TaskType CCSBot::GetTask( void ) const +{ + return m_task; +} + +inline CBaseEntity *CCSBot::GetTaskEntity( void ) +{ + return static_cast<CBaseEntity *>( m_taskEntity ); +} + +inline CCSBot::MoraleType CCSBot::GetMorale( void ) const +{ + return m_morale; +} + +inline void CCSBot::Surprise( float duration ) +{ + m_surpriseTimer.Start( duration ); +} + +inline bool CCSBot::IsSurprised( void ) const +{ + return !m_surpriseTimer.IsElapsed(); +} + +inline CNavArea *CCSBot::GetNoiseArea( void ) const +{ + return m_noiseArea; +} + +inline void CCSBot::ForgetNoise( void ) +{ + m_noiseTimestamp = 0.0f; +} + +inline float CCSBot::GetNoiseRange( void ) const +{ + if (IsNoiseHeard()) + return m_noiseTravelDistance; + + return 999999999.9f; +} + +inline PriorityType CCSBot::GetNoisePriority( void ) const +{ + return m_noisePriority; +} + +inline BotChatterInterface *CCSBot::GetChatter( void ) +{ + return &m_chatter; +} + +inline CCSPlayer *CCSBot::GetBotEnemy( void ) const +{ + return m_enemy; +} + +inline int CCSBot::GetNearbyEnemyCount( void ) const +{ + return MIN( GetEnemiesRemaining(), m_nearbyEnemyCount ); +} + +inline unsigned int CCSBot::GetEnemyPlace( void ) const +{ + return m_enemyPlace; +} + +inline bool CCSBot::CanSeeBomber( void ) const +{ + return (m_bomber == NULL) ? false : true; +} + +inline CCSPlayer *CCSBot::GetBomber( void ) const +{ + return m_bomber; +} + +inline int CCSBot::GetNearbyFriendCount( void ) const +{ + return MIN( GetFriendsRemaining(), m_nearbyFriendCount ); +} + +inline CCSPlayer *CCSBot::GetClosestVisibleFriend( void ) const +{ + return m_closestVisibleFriend; +} + +inline CCSPlayer *CCSBot::GetClosestVisibleHumanFriend( void ) const +{ + return m_closestVisibleHumanFriend; +} + +inline float CCSBot::GetTimeSinceAttacked( void ) const +{ + return gpGlobals->curtime - m_attackedTimestamp; +} + +inline float CCSBot::GetFirstSawEnemyTimestamp( void ) const +{ + return m_firstSawEnemyTimestamp; +} + +inline float CCSBot::GetLastSawEnemyTimestamp( void ) const +{ + return m_lastSawEnemyTimestamp; +} + +inline float CCSBot::GetTimeSinceLastSawEnemy( void ) const +{ + return gpGlobals->curtime - m_lastSawEnemyTimestamp; +} + +inline float CCSBot::GetTimeSinceAcquiredCurrentEnemy( void ) const +{ + return gpGlobals->curtime - m_currentEnemyAcquireTimestamp; +} + +inline const Vector &CCSBot::GetLastKnownEnemyPosition( void ) const +{ + return m_lastEnemyPosition; +} + +inline bool CCSBot::IsEnemyVisible( void ) const +{ + return m_isEnemyVisible; +} + +inline float CCSBot::GetEnemyDeathTimestamp( void ) const +{ + return m_enemyDeathTimestamp; +} + +inline int CCSBot::GetLastVictimID( void ) const +{ + return m_lastVictimID; +} + +inline bool CCSBot::CanSeeSniper( void ) const +{ + return m_isEnemySniperVisible; +} + +inline bool CCSBot::HasSeenSniperRecently( void ) const +{ + return !m_sawEnemySniperTimer.IsElapsed(); +} + +inline float CCSBot::GetTravelDistanceToPlayer( CCSPlayer *player ) const +{ + if (player == NULL) + return -1.0f; + + if (!player->IsAlive()) + return -1.0f; + + return m_playerTravelDistance[ player->entindex() % MAX_PLAYERS ]; +} + +inline bool CCSBot::HasPath( void ) const +{ + return (m_pathLength) ? true : false; +} + +inline void CCSBot::DestroyPath( void ) +{ + m_isStopping = false; + m_pathLength = 0; + m_pathLadder = NULL; +} + +inline CNavArea *CCSBot::GetLastKnownArea( void ) const +{ + return m_lastKnownArea; +} + +inline const Vector &CCSBot::GetPathEndpoint( void ) const +{ + return m_path[ m_pathLength-1 ].pos; +} + +inline const Vector &CCSBot::GetPathPosition( int index ) const +{ + return m_path[ index ].pos; +} + +inline bool CCSBot::IsUsingLadder( void ) const +{ + return (m_pathLadder) ? true : false; +} + +inline void CCSBot::SetGoalEntity( CBaseEntity *entity ) +{ + m_goalEntity = entity; +} + +inline CBaseEntity *CCSBot::GetGoalEntity( void ) +{ + return m_goalEntity; +} + +inline void CCSBot::ForceRun( float duration ) +{ + Run(); + m_mustRunTimer.Start( duration ); +} + +inline void CCSBot::Wait( float duration ) +{ + m_waitTimer.Start( duration ); +} + +inline bool CCSBot::IsWaiting( void ) const +{ + return !m_waitTimer.IsElapsed(); +} + +inline void CCSBot::StopWaiting( void ) +{ + m_waitTimer.Invalidate(); +} + +inline bool CCSBot::HasVisitedEnemySpawn( void ) const +{ + return m_hasVisitedEnemySpawn; +} + +inline const Vector &CCSBot::EyePositionConst( void ) const +{ + return m_eyePosition; +} + +inline void CCSBot::SetLookAngles( float yaw, float pitch ) +{ + m_lookYaw = yaw; + m_lookPitch = pitch; +} + +inline void CCSBot::SetForwardAngle( float angle ) +{ + m_forwardAngle = angle; +} + +inline void CCSBot::SetLookAheadAngle( float angle ) +{ + m_lookAheadAngle = angle; +} + +inline void CCSBot::ClearLookAt( void ) +{ + //PrintIfWatched( "ClearLookAt()\n" ); + m_lookAtSpotState = NOT_LOOKING_AT_SPOT; + m_lookAtDesc = NULL; +} + +inline bool CCSBot::IsLookingAtSpot( PriorityType pri ) const +{ + if (m_lookAtSpotState != NOT_LOOKING_AT_SPOT && m_lookAtSpotPriority >= pri) + return true; + + return false; +} + +inline bool CCSBot::IsViewMoving( float angleVelThreshold ) const +{ + if (m_lookYawVel < angleVelThreshold && m_lookYawVel > -angleVelThreshold && + m_lookPitchVel < angleVelThreshold && m_lookPitchVel > -angleVelThreshold) + { + return false; + } + + return true; +} + +inline bool CCSBot::HasViewBeenSteady( float duration ) const +{ + return (m_viewSteadyTimer.GetElapsedTime() > duration); +} + +inline bool CCSBot::HasLookAtTarget( void ) const +{ + return (m_lookAtSpotState != NOT_LOOKING_AT_SPOT); +} + +inline bool CCSBot::IsEnemyPartVisible( VisiblePartType part ) const +{ + VPROF_BUDGET( "CCSBot::IsEnemyPartVisible", VPROF_BUDGETGROUP_NPCS ); + + if (!IsEnemyVisible()) + return false; + + return (m_visibleEnemyParts & part) ? true : false; +} + +inline bool CCSBot::IsSignificantlyCloser( const CCSPlayer *testPlayer, const CCSPlayer *referencePlayer ) const +{ + if ( !referencePlayer ) + return true; + + if ( !testPlayer ) + return false; + + float testDist = ( GetAbsOrigin() - testPlayer->GetAbsOrigin() ).Length(); + float referenceDist = ( GetAbsOrigin() - referencePlayer->GetAbsOrigin() ).Length(); + + const float significantRangeFraction = 0.7f; + if ( testDist < referenceDist * significantRangeFraction ) + return true; + + return false; +} + +inline void CCSBot::ClearApproachPoints( void ) +{ + m_approachPointCount = 0; +} + +inline const CNavArea *CCSBot::GetInitialEncounterArea( void ) const +{ + return m_initialEncounterArea; +} + +inline void CCSBot::SetInitialEncounterArea( const CNavArea *area ) +{ + m_initialEncounterArea = area; +} + +inline bool CCSBot::IsThrowingGrenade( void ) const +{ + return m_grenadeTossState != NOT_THROWING; +} + +inline bool CCSBot::IsAvoidingGrenade( void ) const +{ + return !m_isAvoidingGrenade.IsElapsed(); +} + +inline void CCSBot::PrimaryAttack( void ) +{ + if ( IsUsingPistol() && !CanActiveWeaponFire() ) + return; + + BaseClass::PrimaryAttack(); +} + +inline CCSBot::ZoomType CCSBot::GetZoomLevel( void ) +{ + if (GetFOV() > 60.0f) + return NO_ZOOM; + if (GetFOV() > 25.0f) + return LOW_ZOOM; + return HIGH_ZOOM; +} + +inline bool CCSBot::IsWaitingForZoom( void ) const +{ + return !m_zoomTimer.IsElapsed(); +} + +inline int CCSBot::GetHostageEscortCount( void ) const +{ + return m_hostageEscortCount; +} + +inline void CCSBot::IncreaseHostageEscortCount( void ) +{ + ++m_hostageEscortCount; +} + +inline void CCSBot::ResetWaitForHostagePatience( void ) +{ + m_isWaitingForHostage = false; + m_inhibitWaitingForHostageTimer.Invalidate(); +} + + +inline bool CCSBot::IsUsingVoice() const +{ + return m_voiceEndTimestamp > gpGlobals->curtime; +} + +inline bool CCSBot::IsOpeningDoor( void ) const +{ + return m_isOpeningDoor; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return true if the given weapon is a sniper rifle + */ +inline bool IsSniperRifle( CWeaponCSBase *weapon ) +{ + if (weapon == NULL) + return false; + + return weapon->IsKindOf(WEAPONTYPE_SNIPER_RIFLE); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Functor used with NavAreaBuildPath() + */ +class PathCost +{ +public: + PathCost( CCSBot *bot, RouteType route = SAFEST_ROUTE ) + { + m_bot = bot; + m_route = route; + } + + // HPE_TODO[pmf]: check that these new parameters are okay to be ignored + float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length ) + { + float baseDangerFactor = 100.0f; // 100 + + // respond to the danger modulated by our aggression (even super-aggressives pay SOME attention to danger) + float dangerFactor = (1.0f - (0.95f * m_bot->GetProfile()->GetAggression())) * baseDangerFactor; + + if (fromArea == NULL) + { + if (m_route == FASTEST_ROUTE) + return 0.0f; + + // first area in path, cost is just danger + return dangerFactor * area->GetDanger( m_bot->GetTeamNumber() ); + } + else if ((fromArea->GetAttributes() & NAV_MESH_JUMP) && (area->GetAttributes() & NAV_MESH_JUMP)) + { + // cannot actually walk in jump areas - disallow moving from jump area to jump area + return -1.0f; + } + if ( area->GetAttributes() & NAV_MESH_NO_HOSTAGES && m_bot->GetHostageEscortCount() ) + { + // if we're leading hostages, don't try to go where they can't + return -1.0f; + } + else + { + // compute distance from previous area to this area + float dist; + if (ladder) + { + // ladders are slow to use + const float ladderPenalty = 1.0f; // 3.0f; + dist = ladderPenalty * ladder->m_length; + + // if we are currently escorting hostages, avoid ladders (hostages are confused by them) + //if (m_bot->GetHostageEscortCount()) + // dist *= 100.0f; + } + else + { + dist = (area->GetCenter() - fromArea->GetCenter()).Length(); + } + + // compute distance travelled along path so far + float cost = dist + fromArea->GetCostSoFar(); + + // zombies ignore all path penalties + if (cv_bot_zombie.GetBool()) + return cost; + + // add cost of "jump down" pain unless we're jumping into water + if (!area->IsUnderwater() && area->IsConnected( fromArea, NUM_DIRECTIONS ) == false) + { + // this is a "jump down" (one way drop) transition - estimate damage we will take to traverse it + float fallDistance = -fromArea->ComputeGroundHeightChange( area ); + + // if it's a drop-down ladder, estimate height from the bottom of the ladder to the lower area + if ( ladder && ladder->m_bottom.z < fromArea->GetCenter().z && ladder->m_bottom.z > area->GetCenter().z ) + { + fallDistance = ladder->m_bottom.z - area->GetCenter().z; + } + + float fallDamage = m_bot->GetApproximateFallDamage( fallDistance ); + + if (fallDamage > 0.0f) + { + // if the fall would kill us, don't use it + const float deathFallMargin = 10.0f; + if (fallDamage + deathFallMargin >= m_bot->GetHealth()) + return -1.0f; + + // if we need to get there in a hurry, ignore minor pain + const float painTolerance = 15.0f * m_bot->GetProfile()->GetAggression() + 10.0f; + if (m_route != FASTEST_ROUTE || fallDamage > painTolerance) + { + // cost is proportional to how much it hurts when we fall + // 10 points - not a big deal, 50 points - ouch! + cost += 100.0f * fallDamage * fallDamage; + } + } + } + + // if this is a "crouch" or "walk" area, add penalty + if (area->GetAttributes() & (NAV_MESH_CROUCH | NAV_MESH_WALK)) + { + // these areas are very slow to move through + float penalty = (m_route == FASTEST_ROUTE) ? 20.0f : 5.0f; + + // avoid crouch areas if we are rescuing hostages + if ((area->GetAttributes() & NAV_MESH_CROUCH) && m_bot->GetHostageEscortCount()) + { + penalty *= 3.0f; + } + + cost += penalty * dist; + } + + // if this is a "jump" area, add penalty + if (area->GetAttributes() & NAV_MESH_JUMP) + { + // jumping can slow you down + //const float jumpPenalty = (m_route == FASTEST_ROUTE) ? 100.0f : 0.5f; + const float jumpPenalty = 1.0f; + cost += jumpPenalty * dist; + } + + // if this is an area to avoid, add penalty + if (area->GetAttributes() & NAV_MESH_AVOID) + { + const float avoidPenalty = 20.0f; + cost += avoidPenalty * dist; + } + + if (m_route == SAFEST_ROUTE) + { + // add in the danger of this path - danger is per unit length travelled + cost += dist * dangerFactor * area->GetDanger( m_bot->GetTeamNumber() ); + } + + if (!m_bot->IsAttacking()) + { + // add in cost of teammates in the way + + // approximate density of teammates based on area + float size = (area->GetSizeX() + area->GetSizeY())/2.0f; + + // degenerate check + if (size >= 1.0f) + { + // cost is proportional to the density of teammates in this area + const float costPerFriendPerUnit = 50000.0f; + cost += costPerFriendPerUnit * (float)area->GetPlayerCount( m_bot->GetTeamNumber() ) / size; + } + } + + return cost; + } + } + +private: + CCSBot *m_bot; + RouteType m_route; +}; + + +//-------------------------------------------------------------------------------------------------------------- +// +// Prototypes +// +extern int GetBotFollowCount( CCSPlayer *leader ); +extern const Vector *FindNearbyRetreatSpot( CCSBot *me, float maxRange = 250.0f ); +extern const HidingSpot *FindInitialEncounterSpot( CBaseEntity *me, const Vector &searchOrigin, float enemyArriveTime, float maxRange, bool isSniper ); + + +#endif // _CS_BOT_H_ + |