Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1080 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. // tf_bot.h
  3. // Team Fortress NextBot
  4. // Michael Booth, February 2009
  5. #ifndef TF_BOT_H
  6. #define TF_BOT_H
  7. #include "Player/NextBotPlayer.h"
  8. #include "../nav_mesh/tf_nav_mesh.h"
  9. #include "tf_bot_vision.h"
  10. #include "tf_bot_body.h"
  11. #include "tf_bot_locomotion.h"
  12. #include "tf_player.h"
  13. #include "tf_bot_squad.h"
  14. #include "bot/map_entities/tf_bot_proxy.h"
  15. #include "tf_gamerules.h"
  16. #include "entity_capture_flag.h"
  17. #include "func_capture_zone.h"
  18. #include "nav_entities.h"
  19. #include "utlstack.h"
  20. #define TF_BOT_TYPE 1337
  21. class CTriggerAreaCapture;
  22. class CTFBotActionPoint;
  23. class CObjectSentrygun;
  24. class CTFBotGenerator;
  25. extern void BotGenerateAndWearItem( CTFPlayer *pBot, const char *itemName );
  26. //----------------------------------------------------------------------------
  27. // These must remain in sync with the bot_generator's spawnflags in tf.fgd:
  28. #define TFBOT_IGNORE_ENEMY_SCOUTS 0x0001
  29. #define TFBOT_IGNORE_ENEMY_SOLDIERS 0x0002
  30. #define TFBOT_IGNORE_ENEMY_PYROS 0x0004
  31. #define TFBOT_IGNORE_ENEMY_DEMOMEN 0x0008
  32. #define TFBOT_IGNORE_ENEMY_HEAVIES 0x0010
  33. #define TFBOT_IGNORE_ENEMY_MEDICS 0x0020
  34. #define TFBOT_IGNORE_ENEMY_ENGINEERS 0x0040
  35. #define TFBOT_IGNORE_ENEMY_SNIPERS 0x0080
  36. #define TFBOT_IGNORE_ENEMY_SPIES 0x0100
  37. #define TFBOT_IGNORE_ENEMY_SENTRY_GUNS 0x0200
  38. #define TFBOT_IGNORE_SCENARIO_GOALS 0x0400
  39. #define TFBOT_ALL_BEHAVIOR_FLAGS 0xFFFF
  40. #define TFBOT_MVM_MAX_PATH_LENGTH 0.0f // 7000.0f // in MvM, all pathfinds are limited to this (0 == no limit)
  41. //----------------------------------------------------------------------------
  42. class CTFBot: public NextBotPlayer< CTFPlayer >, public CGameEventListener
  43. {
  44. public:
  45. DECLARE_CLASS( CTFBot, NextBotPlayer< CTFPlayer > );
  46. CTFBot();
  47. virtual ~CTFBot();
  48. virtual void Spawn();
  49. virtual void FireGameEvent( IGameEvent *event );
  50. virtual void Event_Killed( const CTakeDamageInfo &info );
  51. virtual void PhysicsSimulate( void );
  52. virtual void Touch( CBaseEntity *pOther );
  53. virtual void AvoidPlayers( CUserCmd *pCmd ); // some game types allow players to pass through each other, this method pushes them apart
  54. virtual void UpdateOnRemove( void );
  55. virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo ) OVERRIDE;
  56. virtual void ChangeTeam( int iTeamNum, bool bAutoTeam, bool bSilent, bool bAutoBalance = false ) OVERRIDE;
  57. virtual bool ShouldGib( const CTakeDamageInfo &info ) OVERRIDE;
  58. virtual int DrawDebugTextOverlays(void);
  59. virtual bool IsAllowedToPickUpFlag( void ) const;
  60. virtual void InitClass( void ); // set health/etc
  61. void ModifyMaxHealth( int nNewMaxHealth, bool bSetCurrentHealth = true, bool bAllowModelScaling = true );
  62. virtual int GetBotType( void ) const; // return a unique int representing the type of bot instance this is
  63. virtual CTFNavArea *GetLastKnownArea( void ) const { return static_cast< CTFNavArea * >( BaseClass::GetLastKnownArea() ); } // return the last nav area the player occupied - NULL if unknown
  64. // NextBotPlayer
  65. static CBasePlayer *AllocatePlayerEntity( edict_t *pEdict, const char *playerName );
  66. virtual void PressFireButton( float duration = -1.0f ) OVERRIDE;
  67. virtual void PressAltFireButton( float duration = -1.0f ) OVERRIDE;
  68. virtual void PressSpecialFireButton( float duration = -1.0f ) OVERRIDE;
  69. // INextBot
  70. virtual CTFBotLocomotion *GetLocomotionInterface( void ) const { return m_locomotor; }
  71. virtual CTFBotBody *GetBodyInterface( void ) const { return m_body; }
  72. virtual CTFBotVision *GetVisionInterface( void ) const { return m_vision; }
  73. DECLARE_INTENTION_INTERFACE( CTFBot );
  74. virtual bool IsDormantWhenDead( void ) const; // should this player-bot continue to update itself when dead (respawn logic, etc)
  75. virtual void OnWeaponFired( CBaseCombatCharacter *whoFired, CBaseCombatWeapon *weapon ); // when someone fires their weapon
  76. virtual bool IsDebugFilterMatch( const char *name ) const; // return true if we match the given debug symbol
  77. virtual int GetAllowedTauntPartnerTeam() const OVERRIDE { return GetTeamNumber(); }
  78. // CTFBot specific
  79. CTeamControlPoint *GetMyControlPoint( void ) const; // return point we want to capture, or need to defend
  80. void ClearMyControlPoint( void );
  81. bool WasPointJustLost( void ) const; // return true if we just lost territory recently
  82. bool AreAllPointsUncontestedSoFar( void ) const; // return true if no enemy has contested any point yet
  83. bool IsPointBeingCaptured( CTeamControlPoint *point ) const; // return true if the given point is being captured
  84. bool IsAnyPointBeingCaptured( void ) const; // return true if any point is being captured
  85. bool IsNearPoint( CTeamControlPoint *point ) const; // return true if we are within a short travel distance of the current point
  86. float GetTimeLeftToCapture( void ) const; // return time left to capture the point before we lose the game
  87. CCaptureFlag *GetFlagToFetch( void ) const; // return flag we want to fetch
  88. CCaptureZone *GetFlagCaptureZone( void ) const; // return capture zone for our flag(s)
  89. struct SniperSpotInfo
  90. {
  91. CTFNavArea *m_vantageArea;
  92. Vector m_vantageSpot;
  93. CTFNavArea *m_theaterArea;
  94. Vector m_theaterSpot;
  95. float m_range;
  96. float m_advantage; // the difference in how long it takes us to reach our vantage spot vs them to reach the theater spot
  97. };
  98. void AccumulateSniperSpots( void ); // find good sniping spots and store them
  99. const CUtlVector< SniperSpotInfo > *GetSniperSpots( void ) const; // return vector of good sniping positions
  100. bool HasSniperSpots( void ) const;
  101. void ClearSniperSpots( void );
  102. // search outwards from startSearchArea and collect all reachable objects from the given list that pass the given filter
  103. void SelectReachableObjects( const CUtlVector< CHandle< CBaseEntity > > &candidateObjectVector, CUtlVector< CHandle< CBaseEntity > > *selectedObjectVector, const INextBotFilter &filter, CNavArea *startSearchArea, float maxRange = 2000.0f ) const;
  104. CBaseEntity *FindClosestReachableObject( const char *objectName, CNavArea *from, float maxRange = 2000.0f ) const;
  105. CTFNavArea *GetSpawnArea( void ) const; // get area where we spawned in
  106. bool IsAmmoLow( void ) const;
  107. bool IsAmmoFull( void ) const;
  108. void UpdateLookingAroundForEnemies( void ); // update our view to keep an eye on enemies, and where enemies come from
  109. #define LOOK_FOR_FRIENDS false
  110. #define LOOK_FOR_ENEMIES true
  111. void UpdateLookingAroundForIncomingPlayers( bool lookForEnemies ); // update our view to watch where friends or enemies will be coming from
  112. void StartLookingAroundForEnemies( void ); // enable updating view for enemy searching
  113. void StopLookingAroundForEnemies( void ); // disable updating view for enemy searching
  114. void SetAttentionFocus( CBaseEntity *focusOn ); // restrict bot's attention to only this entity (or radius around this entity) to the exclusion of everything else
  115. void ClearAttentionFocus( void ); // remove attention focus restrictions
  116. bool IsAttentionFocused( void ) const;
  117. bool IsAttentionFocusedOn( CBaseEntity *who ) const;
  118. void DelayedThreatNotice( CHandle< CBaseEntity > who, float noticeDelay ); // notice the given threat after the given number of seconds have elapsed
  119. void UpdateDelayedThreatNotices( void );
  120. CTFNavArea *FindVantagePoint( float maxTravelDistance = 2000.0f ) const; // return a nearby area where we can see a member of the enemy team
  121. const char *GetNextSpawnClassname( void ) const;
  122. float GetThreatDanger( CBaseCombatCharacter *who ) const; // return perceived danger of threat (0=none, 1=immediate deadly danger)
  123. float GetMaxAttackRange( void ) const; // return the max range at which we can effectively attack
  124. float GetDesiredAttackRange( void ) const; // return the ideal range at which we can effectively attack
  125. bool EquipRequiredWeapon( void ); // if we're required to equip a specific weapon, do it.
  126. void EquipBestWeaponForThreat( const CKnownEntity *threat ); // equip the best weapon we have to attack the given threat
  127. bool EquipLongRangeWeapon( void ); // equip a weapon that can damage far-away targets
  128. void PushRequiredWeapon( CTFWeaponBase *weapon ); // force us to equip and use this weapon until popped off the required stack
  129. void PopRequiredWeapon( void ); // pop top required weapon off of stack and discard
  130. #define MY_CURRENT_GUN NULL // can be passed as weapon to following queries
  131. bool IsCombatWeapon( CTFWeaponBase *weapon ) const; // return true if given weapon can be used to attack
  132. bool IsHitScanWeapon( CTFWeaponBase *weapon ) const; // return true if given weapon is a "hitscan" weapon (scattered tracelines with instant damage)
  133. bool IsContinuousFireWeapon( CTFWeaponBase *weapon ) const; // return true if given weapon "sprays" bullets/fire/etc continuously (ie: not individual rockets/etc)
  134. bool IsExplosiveProjectileWeapon( CTFWeaponBase *weapon ) const;// return true if given weapon launches explosive projectiles with splash damage
  135. bool IsBarrageAndReloadWeapon( CTFWeaponBase *weapon ) const; // return true if given weapon has small clip and long reload cost (ie: rocket launcher, etc)
  136. bool IsQuietWeapon( CTFWeaponBase *weapon ) const; // return true if given weapon doesn't make much sound when used (ie: spy knife, etc)
  137. bool IsEnvironmentNoisy( void ) const; // return true if there are/have been loud noises (ie: non-quiet weapons) nearby very recently
  138. enum WeaponRestrictionType
  139. {
  140. ANY_WEAPON = 0,
  141. MELEE_ONLY = 0x0001,
  142. PRIMARY_ONLY = 0x0002,
  143. SECONDARY_ONLY = 0x0004,
  144. };
  145. void ClearWeaponRestrictions( void );
  146. void SetWeaponRestriction( int restrictionFlags );
  147. bool HasWeaponRestriction( int restrictionFlags ) const;
  148. bool IsWeaponRestricted( CTFWeaponBase *weapon ) const;
  149. bool ShouldFireCompressionBlast( void );
  150. bool IsLineOfFireClear( const Vector &where ) const; // return true if a weapon has no obstructions along the line from our eye to the given position
  151. bool IsLineOfFireClear( CBaseEntity *who ) const; // return true if a weapon has no obstructions along the line from our eye to the given entity
  152. bool IsLineOfFireClear( const Vector &from, const Vector &to ) const; // return true if a weapon has no obstructions along the line between the given points
  153. bool IsLineOfFireClear( const Vector &from, CBaseEntity *who ) const; // return true if a weapon has no obstructions along the line between the given point and entity
  154. bool IsEntityBetweenTargetAndSelf( CBaseEntity *other, CBaseEntity *target ); // return true if "other" is positioned inbetween us and "target"
  155. class SuspectedSpyInfo_t
  156. {
  157. public:
  158. bool IsCurrentlySuspected();
  159. void Suspect(); // The verb form of the word, not the noun.
  160. bool TestForRealizing();
  161. CHandle< CTFPlayer > m_suspectedSpy;
  162. private:
  163. CUtlVector< int > m_touchTimes;
  164. };
  165. bool IsKnownSpy( CTFPlayer *player ) const; // return true if we are sure this player actually is an enemy spy
  166. SuspectedSpyInfo_t* IsSuspectedSpy( CTFPlayer *player ); // return true if we suspect this player might be an enemy spy
  167. void SuspectSpy( CTFPlayer *player ); // note that this player might be a spy
  168. void RealizeSpy( CTFPlayer *player ); // note that this player *IS* a spy
  169. void ForgetSpy( CTFPlayer *player ); // remove player from spy suspect system
  170. void StopSuspectingSpy( CTFPlayer *pPlayer );
  171. CTFPlayer *GetClosestHumanLookingAtMe( int team = TEAM_ANY ) const; // return the nearest human player on the given team who is looking directly at me
  172. enum AttributeType
  173. {
  174. REMOVE_ON_DEATH = 1<<0, // kick bot from server when killed
  175. AGGRESSIVE = 1<<1, // in MvM mode, push for the cap point
  176. IS_NPC = 1<<2, // a non-player support character
  177. SUPPRESS_FIRE = 1<<3,
  178. DISABLE_DODGE = 1<<4,
  179. BECOME_SPECTATOR_ON_DEATH = 1<<5, // move bot to spectator team when killed
  180. QUOTA_MANANGED = 1<<6, // managed by the bot quota in CTFBotManager
  181. RETAIN_BUILDINGS = 1<<7, // don't destroy this bot's buildings when it disconnects
  182. SPAWN_WITH_FULL_CHARGE = 1<<8, // all weapons start with full charge (ie: uber)
  183. ALWAYS_CRIT = 1<<9, // always fire critical hits
  184. IGNORE_ENEMIES = 1<<10,
  185. HOLD_FIRE_UNTIL_FULL_RELOAD = 1<<11, // don't fire our barrage weapon until it is full reloaded (rocket launcher, etc)
  186. PRIORITIZE_DEFENSE = 1<<12, // bot prioritizes defending when possible
  187. ALWAYS_FIRE_WEAPON = 1<<13, // constantly fire our weapon
  188. TELEPORT_TO_HINT = 1<<14, // bot will teleport to hint target instead of walking out from the spawn point
  189. MINIBOSS = 1<<15, // is miniboss?
  190. USE_BOSS_HEALTH_BAR = 1<<16, // should I use boss health bar?
  191. IGNORE_FLAG = 1<<17, // don't pick up flag/bomb
  192. AUTO_JUMP = 1<<18, // auto jump
  193. AIR_CHARGE_ONLY = 1<<19, // demo knight only charge in the air
  194. PREFER_VACCINATOR_BULLETS = 1<<20, // When using the vaccinator, prefer to use the bullets shield
  195. PREFER_VACCINATOR_BLAST = 1<<21, // When using the vaccinator, prefer to use the blast shield
  196. PREFER_VACCINATOR_FIRE = 1<<22, // When using the vaccinator, prefer to use the fire shield
  197. BULLET_IMMUNE = 1<<23, // Has a shield that makes the bot immune to bullets
  198. BLAST_IMMUNE = 1<<24, // "" blast
  199. FIRE_IMMUNE = 1<<25, // "" fire
  200. PARACHUTE = 1<<26, // demo/soldier parachute when falling
  201. PROJECTILE_SHIELD = 1<<27, // medic projectile shield
  202. };
  203. void SetAttribute( int attributeFlag );
  204. void ClearAttribute( int attributeFlag );
  205. void ClearAllAttributes();
  206. bool HasAttribute( int attributeFlag ) const;
  207. enum DifficultyType
  208. {
  209. UNDEFINED = -1,
  210. EASY = 0,
  211. NORMAL = 1,
  212. HARD = 2,
  213. EXPERT = 3,
  214. NUM_DIFFICULTY_LEVELS
  215. };
  216. DifficultyType GetDifficulty( void ) const;
  217. void SetDifficulty( DifficultyType difficulty );
  218. bool IsDifficulty( DifficultyType skill ) const;
  219. void SetHomeArea( CTFNavArea *area );
  220. CTFNavArea *GetHomeArea( void ) const;
  221. CObjectSentrygun *GetEnemySentry( void ) const; // if we've been attacked/killed by an enemy sentry, this will return it, otherwise NULL
  222. void RememberEnemySentry( CObjectSentrygun *sentry, const Vector &injurySpot );
  223. const Vector &GetSpotWhereEnemySentryLastInjuredMe( void ) const;
  224. void SetActionPoint( CTFBotActionPoint *point );
  225. CTFBotActionPoint *GetActionPoint( void ) const;
  226. bool HasProxy( void ) const;
  227. void SetProxy( CTFBotProxy *proxy ); // attach this bot to a bot_proxy entity for map I/O communications
  228. CTFBotProxy *GetProxy( void ) const;
  229. bool HasSpawner( void ) const;
  230. void SetSpawner( CTFBotGenerator *spawner );
  231. CTFBotGenerator *GetSpawner( void ) const;
  232. void JoinSquad( CTFBotSquad *squad ); // become a member of the given squad
  233. void LeaveSquad( void ); // leave our current squad
  234. void DeleteSquad( void );
  235. bool IsInASquad( void ) const;
  236. bool IsSquadmate( CTFPlayer *who ) const; // return true if given bot is in my squad
  237. CTFBotSquad *GetSquad( void ) const; // return squad we are in, or NULL
  238. float GetSquadFormationError( void ) const; // return normalized error term where 0 = in formation position and 1 = completely out of position
  239. void SetSquadFormationError( float error );
  240. bool HasBrokenFormation( void ) const; // return true if this bot is far out of formation, or has no path back
  241. void SetBrokenFormation( bool state );
  242. float TransientlyConsistentRandomValue( float period = 10.0f, int seedValue = 0 ) const; // compute a pseudo random value (0-1) that stays consistent for the given period of time, but changes unpredictably each period
  243. void SetBehaviorFlag( unsigned int flags );
  244. void ClearBehaviorFlag( unsigned int flags );
  245. bool IsBehaviorFlagSet( unsigned int flags ) const;
  246. bool FindSplashTarget( CBaseEntity *target, float maxSplashRadius, Vector *splashTarget ) const;
  247. void GiveRandomItem( loadout_positions_t loadoutPosition );
  248. enum MissionType
  249. {
  250. NO_MISSION = 0,
  251. MISSION_SEEK_AND_DESTROY, // focus on finding and killing enemy players
  252. MISSION_DESTROY_SENTRIES, // focus on finding and destroying enemy sentry guns (and buildings)
  253. MISSION_SNIPER, // maintain teams of snipers harassing the enemy
  254. MISSION_SPY, // maintain teams of spies harassing the enemy
  255. MISSION_ENGINEER, // maintain engineer nests for harassing the enemy
  256. MISSION_REPROGRAMMED, // MvM: robot has been hacked and will do bad things to their team
  257. };
  258. #define MISSION_DOESNT_RESET_BEHAVIOR_SYSTEM false
  259. void SetMission( MissionType mission, bool resetBehaviorSystem = true );
  260. void SetPrevMission( MissionType mission );
  261. MissionType GetMission( void ) const;
  262. MissionType GetPrevMission( void ) const;
  263. bool HasMission( MissionType mission ) const;
  264. bool IsOnAnyMission( void ) const;
  265. void SetMissionTarget( CBaseEntity *target );
  266. CBaseEntity *GetMissionTarget( void ) const;
  267. void SetMissionString( CUtlString string );
  268. CUtlString *GetMissionString( void );
  269. void SetTeleportWhere( const CUtlStringList& teleportWhereName );
  270. const CUtlStringList& GetTeleportWhere();
  271. void ClearTeleportWhere();
  272. void SetScaleOverride( float fScale );
  273. void SetMaxVisionRangeOverride( float range );
  274. float GetMaxVisionRangeOverride( void ) const;
  275. void DisguiseAsMemberOfEnemyTeam( void ); // set Spy disguise to be a class that someone on the enemy team is actually using
  276. CBaseObject *GetNearestKnownSappableTarget( void );
  277. void ClearTags( void );
  278. void AddTag( const char *tag );
  279. void RemoveTag( const char *tag );
  280. bool HasTag( const char *tag );
  281. Action< CTFBot > *OpportunisticallyUseWeaponAbilities( void );
  282. CTFPlayer *SelectRandomReachableEnemy( void ); // mostly for MvM - pick a random enemy player that is not in their spawn room
  283. float GetDesiredPathLookAheadRange( void ) const; // different sized bots used different lookahead distances
  284. void StartIdleSound( void );
  285. void StopIdleSound( void );
  286. bool ShouldQuickBuild() const { return m_bForceQuickBuild; }
  287. void SetShouldQuickBuild( bool bShouldQuickBuild ) { m_bForceQuickBuild = bShouldQuickBuild; }
  288. void SetAutoJump( float flAutoJumpMin, float flAutoJumpMax ) { m_flAutoJumpMin = flAutoJumpMin; m_flAutoJumpMax = flAutoJumpMax; }
  289. bool ShouldAutoJump();
  290. void SetFlagTarget( CCaptureFlag* pFlag );
  291. CCaptureFlag* GetFlagTarget() const { return m_hFollowingFlagTarget; }
  292. bool HasFlagTaget() const { return m_hFollowingFlagTarget != NULL; }
  293. struct EventChangeAttributes_t
  294. {
  295. EventChangeAttributes_t()
  296. {
  297. Reset();
  298. }
  299. EventChangeAttributes_t( const EventChangeAttributes_t& copy )
  300. {
  301. Reset();
  302. m_eventName = copy.m_eventName;
  303. m_skill = copy.m_skill;
  304. m_weaponRestriction = copy.m_weaponRestriction;
  305. m_mission = copy.m_mission;
  306. m_prevMission = copy.m_prevMission;
  307. m_attributeFlags = copy.m_attributeFlags;
  308. m_maxVisionRange = copy.m_maxVisionRange;
  309. for ( int i=0; i<copy.m_items.Count(); ++i )
  310. {
  311. m_items.CopyAndAddToTail( copy.m_items[i] );
  312. }
  313. m_itemsAttributes = copy.m_itemsAttributes;
  314. m_characterAttributes = copy.m_characterAttributes;
  315. for ( int i=0; i<copy.m_tags.Count(); ++i )
  316. {
  317. m_tags.CopyAndAddToTail( copy.m_tags[i] );
  318. }
  319. }
  320. void Reset()
  321. {
  322. m_eventName = "default";
  323. m_skill = CTFBot::EASY;
  324. m_weaponRestriction = CTFBot::ANY_WEAPON;
  325. m_mission = CTFBot::NO_MISSION;
  326. m_prevMission = m_mission;
  327. m_attributeFlags = 0;
  328. m_maxVisionRange = -1.f;
  329. m_items.RemoveAll();
  330. m_itemsAttributes.RemoveAll();
  331. m_characterAttributes.RemoveAll();
  332. m_tags.RemoveAll();
  333. }
  334. CUtlString m_eventName;
  335. DifficultyType m_skill;
  336. WeaponRestrictionType m_weaponRestriction;
  337. MissionType m_mission;
  338. MissionType m_prevMission;
  339. int m_attributeFlags;
  340. float m_maxVisionRange;
  341. CUtlStringList m_items;
  342. struct item_attributes_t
  343. {
  344. CUtlString m_itemName;
  345. CCopyableUtlVector< static_attrib_t > m_attributes;
  346. };
  347. CUtlVector< item_attributes_t > m_itemsAttributes;
  348. CUtlVector< static_attrib_t > m_characterAttributes;
  349. CUtlStringList m_tags;
  350. };
  351. void ClearEventChangeAttributes() { m_eventChangeAttributes.RemoveAll(); }
  352. void AddEventChangeAttributes( const EventChangeAttributes_t* newEvent );
  353. const EventChangeAttributes_t* GetEventChangeAttributes( const char* pszEventName ) const;
  354. void OnEventChangeAttributes( const CTFBot::EventChangeAttributes_t* pEvent );
  355. void AddItem( const char* pszItemName );
  356. int GetUberHealthThreshold();
  357. float GetUberDeployDelayDuration();
  358. private:
  359. CTFBotLocomotion *m_locomotor;
  360. CTFBotBody *m_body;
  361. CTFBotVision *m_vision;
  362. CountdownTimer m_lookAtEnemyInvasionAreasTimer;
  363. CTFNavArea *m_spawnArea; // where we spawned
  364. CountdownTimer m_justLostPointTimer;
  365. int m_weaponRestrictionFlags;
  366. int m_attributeFlags;
  367. DifficultyType m_difficulty;
  368. CTFNavArea *m_homeArea;
  369. CHandle< CTFBotActionPoint > m_actionPoint;
  370. CHandle< CTFBotProxy > m_proxy;
  371. CHandle< CTFBotGenerator > m_spawner;
  372. CTFBotSquad *m_squad;
  373. bool m_didReselectClass;
  374. CHandle< CObjectSentrygun > m_enemySentry;
  375. Vector m_spotWhereEnemySentryLastInjuredMe; // the last position where I was injured by an enemy sentry
  376. CUtlVector< SuspectedSpyInfo_t* > m_suspectedSpyVector;
  377. CUtlVector< CHandle< CTFPlayer > > m_knownSpyVector;
  378. CUtlVector< SniperSpotInfo > m_sniperSpotVector; // collection of good sniping spots for the current objective
  379. CUtlVector< CTFNavArea * > m_sniperVantageAreaVector;
  380. CUtlVector< CTFNavArea * > m_sniperTheaterAreaVector;
  381. CBaseEntity *m_snipingGoalEntity; // the entity we are guarding (control point, payload cart)
  382. Vector m_lastSnipingGoalEntityPosition;
  383. void SetupSniperSpotAccumulation( void ); // do internal setup when control point changes
  384. CountdownTimer m_retrySniperSpotSetupTimer;
  385. bool m_isLookingAroundForEnemies;
  386. unsigned int m_behaviorFlags; // spawnflags from the bot_generator that spawned us
  387. CUtlVector< CFmtStr > m_tags;
  388. CHandle< CBaseEntity > m_attentionFocusEntity;
  389. CTeamControlPoint *SelectPointToCapture( CUtlVector< CTeamControlPoint * > *captureVector ) const;
  390. CTeamControlPoint *SelectPointToDefend( CUtlVector< CTeamControlPoint * > *defendVector ) const;
  391. mutable CHandle< CTeamControlPoint > m_myControlPoint;
  392. mutable CountdownTimer m_evaluateControlPointTimer;
  393. float m_fModelScaleOverride;
  394. MissionType m_mission;
  395. MissionType m_prevMission;
  396. CHandle< CBaseEntity > m_missionTarget;
  397. CUtlString m_missionString;
  398. CUtlStack< CHandle<CTFWeaponBase> > m_requiredWeaponStack; // if non-empty, bot must equip the weapon on top of the stack
  399. CountdownTimer m_noisyTimer;
  400. struct DelayedNoticeInfo
  401. {
  402. CHandle< CBaseEntity > m_who;
  403. float m_when;
  404. };
  405. CUtlVector< DelayedNoticeInfo > m_delayedNoticeVector;
  406. float m_maxVisionRangeOverride;
  407. CountdownTimer m_opportunisticTimer;
  408. CSoundPatch *m_pIdleSound;
  409. float m_squadFormationError;
  410. bool m_hasBrokenFormation;
  411. CUtlStringList m_teleportWhereName; // spawn name an engineer mission teleporter will override
  412. bool m_bForceQuickBuild;
  413. float m_flAutoJumpMin;
  414. float m_flAutoJumpMax;
  415. CountdownTimer m_autoJumpTimer;
  416. CHandle< CCaptureFlag > m_hFollowingFlagTarget;
  417. CUtlVector< const EventChangeAttributes_t* > m_eventChangeAttributes;
  418. };
  419. inline void CTFBot::SetTeleportWhere( const CUtlStringList& teleportWhereName )
  420. {
  421. // deep copy strings
  422. for ( int i=0; i<teleportWhereName.Count(); ++i )
  423. {
  424. m_teleportWhereName.CopyAndAddToTail( teleportWhereName[i] );
  425. }
  426. }
  427. inline const CUtlStringList& CTFBot::GetTeleportWhere()
  428. {
  429. return m_teleportWhereName;
  430. }
  431. inline void CTFBot::ClearTeleportWhere()
  432. {
  433. m_teleportWhereName.RemoveAll();
  434. }
  435. inline void CTFBot::SetMissionString( CUtlString string )
  436. {
  437. m_missionString = string;
  438. }
  439. inline CUtlString *CTFBot::GetMissionString( void )
  440. {
  441. return &m_missionString;
  442. }
  443. inline void CTFBot::SetMissionTarget( CBaseEntity *target )
  444. {
  445. m_missionTarget = target;
  446. }
  447. inline CBaseEntity *CTFBot::GetMissionTarget( void ) const
  448. {
  449. return m_missionTarget;
  450. }
  451. inline float CTFBot::GetSquadFormationError( void ) const
  452. {
  453. return m_squadFormationError;
  454. }
  455. inline void CTFBot::SetSquadFormationError( float error )
  456. {
  457. m_squadFormationError = error;
  458. }
  459. inline bool CTFBot::HasBrokenFormation( void ) const
  460. {
  461. return m_hasBrokenFormation;
  462. }
  463. inline void CTFBot::SetBrokenFormation( bool state )
  464. {
  465. m_hasBrokenFormation = state;
  466. }
  467. inline void CTFBot::SetMaxVisionRangeOverride( float range )
  468. {
  469. m_maxVisionRangeOverride = range;
  470. }
  471. inline float CTFBot::GetMaxVisionRangeOverride( void ) const
  472. {
  473. return m_maxVisionRangeOverride;
  474. }
  475. inline void CTFBot::SetBehaviorFlag( unsigned int flags )
  476. {
  477. m_behaviorFlags |= flags;
  478. }
  479. inline void CTFBot::ClearBehaviorFlag( unsigned int flags )
  480. {
  481. m_behaviorFlags &= ~flags;
  482. }
  483. inline bool CTFBot::IsBehaviorFlagSet( unsigned int flags ) const
  484. {
  485. return ( m_behaviorFlags & flags ) ? true : false;
  486. }
  487. inline void CTFBot::StartLookingAroundForEnemies( void )
  488. {
  489. m_isLookingAroundForEnemies = true;
  490. }
  491. inline void CTFBot::StopLookingAroundForEnemies( void )
  492. {
  493. m_isLookingAroundForEnemies = false;
  494. }
  495. inline int CTFBot::GetBotType( void ) const
  496. {
  497. return TF_BOT_TYPE;
  498. }
  499. inline void CTFBot::RememberEnemySentry( CObjectSentrygun *sentry, const Vector &injurySpot )
  500. {
  501. m_enemySentry = sentry;
  502. m_spotWhereEnemySentryLastInjuredMe = injurySpot;
  503. }
  504. inline CObjectSentrygun *CTFBot::GetEnemySentry( void ) const
  505. {
  506. return m_enemySentry;
  507. }
  508. inline const Vector &CTFBot::GetSpotWhereEnemySentryLastInjuredMe( void ) const
  509. {
  510. return m_spotWhereEnemySentryLastInjuredMe;
  511. }
  512. inline CTFBot::DifficultyType CTFBot::GetDifficulty( void ) const
  513. {
  514. return m_difficulty;
  515. }
  516. inline void CTFBot::SetDifficulty( CTFBot::DifficultyType difficulty )
  517. {
  518. m_difficulty = difficulty;
  519. m_nBotSkill = m_difficulty;
  520. }
  521. inline bool CTFBot::IsDifficulty( DifficultyType skill ) const
  522. {
  523. return skill == m_difficulty;
  524. }
  525. inline bool CTFBot::HasProxy( void ) const
  526. {
  527. return m_proxy == NULL ? false : true;
  528. }
  529. inline void CTFBot::SetProxy( CTFBotProxy *proxy )
  530. {
  531. m_proxy = proxy;
  532. }
  533. inline CTFBotProxy *CTFBot::GetProxy( void ) const
  534. {
  535. return m_proxy;
  536. }
  537. inline bool CTFBot::HasSpawner( void ) const
  538. {
  539. return m_spawner == NULL ? false : true;
  540. }
  541. inline void CTFBot::SetSpawner( CTFBotGenerator *spawner )
  542. {
  543. m_spawner = spawner;
  544. }
  545. inline CTFBotGenerator *CTFBot::GetSpawner( void ) const
  546. {
  547. return m_spawner;
  548. }
  549. inline void CTFBot::SetActionPoint( CTFBotActionPoint *point )
  550. {
  551. m_actionPoint = point;
  552. }
  553. inline CTFBotActionPoint *CTFBot::GetActionPoint( void ) const
  554. {
  555. return m_actionPoint;
  556. }
  557. inline bool CTFBot::IsInASquad( void ) const
  558. {
  559. return m_squad == NULL ? false : true;
  560. }
  561. inline CTFBotSquad *CTFBot::GetSquad( void ) const
  562. {
  563. return m_squad;
  564. }
  565. inline void CTFBot::SetHomeArea( CTFNavArea *area )
  566. {
  567. m_homeArea = area;
  568. }
  569. inline CTFNavArea *CTFBot::GetHomeArea( void ) const
  570. {
  571. return m_homeArea;
  572. }
  573. inline void CTFBot::ClearWeaponRestrictions( void )
  574. {
  575. m_weaponRestrictionFlags = 0;
  576. }
  577. inline void CTFBot::SetWeaponRestriction( int restrictionFlags )
  578. {
  579. m_weaponRestrictionFlags |= restrictionFlags;
  580. }
  581. inline bool CTFBot::HasWeaponRestriction( int restrictionFlags ) const
  582. {
  583. return m_weaponRestrictionFlags & restrictionFlags ? true : false;
  584. }
  585. inline void CTFBot::SetAttribute( int attributeFlag )
  586. {
  587. m_attributeFlags |= attributeFlag;
  588. }
  589. inline void CTFBot::ClearAttribute( int attributeFlag )
  590. {
  591. m_attributeFlags &= ~attributeFlag;
  592. }
  593. inline void CTFBot::ClearAllAttributes()
  594. {
  595. m_attributeFlags = 0;
  596. }
  597. inline bool CTFBot::HasAttribute( int attributeFlag ) const
  598. {
  599. return m_attributeFlags & attributeFlag ? true : false;
  600. }
  601. inline CTFNavArea *CTFBot::GetSpawnArea( void ) const
  602. {
  603. return m_spawnArea;
  604. }
  605. inline bool CTFBot::WasPointJustLost( void ) const
  606. {
  607. return m_justLostPointTimer.HasStarted() && !m_justLostPointTimer.IsElapsed();
  608. }
  609. inline const CUtlVector< CTFBot::SniperSpotInfo > *CTFBot::GetSniperSpots( void ) const
  610. {
  611. return &m_sniperSpotVector;
  612. }
  613. inline bool CTFBot::HasSniperSpots( void ) const
  614. {
  615. return m_sniperSpotVector.Count() > 0 ? true : false;
  616. }
  617. inline CTFBot::MissionType CTFBot::GetMission( void ) const
  618. {
  619. return m_mission;
  620. }
  621. inline void CTFBot::SetPrevMission( MissionType mission )
  622. {
  623. m_prevMission = mission;
  624. }
  625. inline CTFBot::MissionType CTFBot::GetPrevMission( void ) const
  626. {
  627. return m_prevMission;
  628. }
  629. inline bool CTFBot::HasMission( MissionType mission ) const
  630. {
  631. return m_mission == mission ? true : false;
  632. }
  633. inline bool CTFBot::IsOnAnyMission( void ) const
  634. {
  635. return m_mission == NO_MISSION ? false : true;
  636. }
  637. inline void CTFBot::SetScaleOverride( float fScale )
  638. {
  639. m_fModelScaleOverride = fScale;
  640. SetModelScale( m_fModelScaleOverride > 0.0f ? m_fModelScaleOverride : 1.0f );
  641. }
  642. inline bool CTFBot::IsEnvironmentNoisy( void ) const
  643. {
  644. return !m_noisyTimer.IsElapsed();
  645. }
  646. //---------------------------------------------------------------------------------------------
  647. inline CTFBot *ToTFBot( CBaseEntity *pEntity )
  648. {
  649. if ( !pEntity || !pEntity->IsPlayer() || !ToTFPlayer( pEntity )->IsBotOfType( TF_BOT_TYPE ) )
  650. return NULL;
  651. Assert( "***IMPORTANT!!! DONT IGNORE ME!!!***" && dynamic_cast< CTFBot * >( pEntity ) != 0 );
  652. return static_cast< CTFBot * >( pEntity );
  653. }
  654. //---------------------------------------------------------------------------------------------
  655. inline const CTFBot *ToTFBot( const CBaseEntity *pEntity )
  656. {
  657. if ( !pEntity || !pEntity->IsPlayer() || !ToTFPlayer( const_cast< CBaseEntity * >( pEntity ) )->IsBotOfType( TF_BOT_TYPE ) )
  658. return NULL;
  659. Assert( "***IMPORTANT!!! DONT IGNORE ME!!!***" && dynamic_cast< const CTFBot * >( pEntity ) != 0 );
  660. return static_cast< const CTFBot * >( pEntity );
  661. }
  662. //--------------------------------------------------------------------------------------------------------------
  663. /**
  664. * Functor used with NavAreaBuildPath()
  665. */
  666. class CTFBotPathCost : public IPathCost
  667. {
  668. public:
  669. CTFBotPathCost( CTFBot *me, RouteType routeType )
  670. {
  671. m_me = me;
  672. m_routeType = routeType;
  673. m_stepHeight = me->GetLocomotionInterface()->GetStepHeight();
  674. m_maxJumpHeight = me->GetLocomotionInterface()->GetMaxJumpHeight();
  675. m_maxDropHeight = me->GetLocomotionInterface()->GetDeathDropHeight();
  676. }
  677. virtual float operator()( CNavArea *baseArea, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length ) const
  678. {
  679. VPROF_BUDGET( "CTFBotPathCost::operator()", "NextBot" );
  680. CTFNavArea *area = (CTFNavArea *)baseArea;
  681. if ( fromArea == NULL )
  682. {
  683. // first area in path, no cost
  684. return 0.0f;
  685. }
  686. else
  687. {
  688. if ( !m_me->GetLocomotionInterface()->IsAreaTraversable( area ) )
  689. {
  690. return -1.0f;
  691. }
  692. // in training, avoid capturing the point until the human trainee does so
  693. if ( TFGameRules()->IsInTraining() &&
  694. area->HasAttributeTF( TF_NAV_CONTROL_POINT ) &&
  695. !m_me->IsAnyPointBeingCaptured() &&
  696. !m_me->IsPlayerClass( TF_CLASS_ENGINEER ) ) // allow engineers to path so they can test travel distance for sentry placement
  697. {
  698. return -1.0f;
  699. }
  700. // don't path through enemy spawn rooms
  701. if ( ( m_me->GetTeamNumber() == TF_TEAM_RED && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE ) ) ||
  702. ( m_me->GetTeamNumber() == TF_TEAM_BLUE && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_RED ) ) )
  703. {
  704. if ( !TFGameRules()->RoundHasBeenWon() )
  705. {
  706. return -1.0f;
  707. }
  708. }
  709. // compute distance traveled along path so far
  710. float dist;
  711. if ( ladder )
  712. {
  713. dist = ladder->m_length;
  714. }
  715. else if ( length > 0.0 )
  716. {
  717. dist = length;
  718. }
  719. else
  720. {
  721. dist = ( area->GetCenter() - fromArea->GetCenter() ).Length();
  722. }
  723. // check height change
  724. float deltaZ = fromArea->ComputeAdjacentConnectionHeightChange( area );
  725. if ( deltaZ >= m_stepHeight )
  726. {
  727. if ( deltaZ >= m_maxJumpHeight )
  728. {
  729. // too high to reach
  730. return -1.0f;
  731. }
  732. // jumping is slower than flat ground
  733. const float jumpPenalty = 2.0f;
  734. dist *= jumpPenalty;
  735. }
  736. else if ( deltaZ < -m_maxDropHeight )
  737. {
  738. // too far to drop
  739. return -1.0f;
  740. }
  741. // add a random penalty unique to this character so they choose different routes to the same place
  742. float preference = 1.0f;
  743. if ( m_routeType == DEFAULT_ROUTE && !m_me->IsMiniBoss() )
  744. {
  745. // this term causes the same bot to choose different routes over time,
  746. // but keep the same route for a period in case of repaths
  747. int timeMod = (int)( gpGlobals->curtime / 10.0f ) + 1;
  748. preference = 1.0f + 50.0f * ( 1.0f + FastCos( (float)( m_me->GetEntity()->entindex() * area->GetID() * timeMod ) ) );
  749. }
  750. if ( m_routeType == SAFEST_ROUTE )
  751. {
  752. // avoid combat areas
  753. if ( area->IsInCombat() )
  754. {
  755. const float combatDangerCost = 4.0f;
  756. dist *= combatDangerCost * area->GetCombatIntensity();
  757. }
  758. // if this area exposes us to enemy sentry fire, avoid it
  759. const float sentryDangerCost = 5.0f;
  760. if ( ( m_me->GetTeamNumber() == TF_TEAM_RED && area->HasAttributeTF( TF_NAV_BLUE_SENTRY_DANGER ) ) ||
  761. ( m_me->GetTeamNumber() == TF_TEAM_BLUE && area->HasAttributeTF( TF_NAV_RED_SENTRY_DANGER ) ) )
  762. {
  763. dist *= sentryDangerCost;
  764. }
  765. }
  766. if ( m_me->IsPlayerClass( TF_CLASS_SPY ) )
  767. {
  768. int enemyTeam = GetEnemyTeam( m_me->GetTeamNumber() );
  769. // Since spies can get right up to enemy buildings, avoid them.
  770. for ( int oit = 0; oit < IBaseObjectAutoList::AutoList().Count(); ++oit )
  771. {
  772. CBaseObject *enemyObj = static_cast< CBaseObject* >( IBaseObjectAutoList::AutoList()[ oit ] );
  773. if ( ( enemyObj->ObjectType() == OBJ_SENTRYGUN ) &&
  774. ( enemyObj->GetTeamNumber() == enemyTeam ) )
  775. {
  776. enemyObj->UpdateLastKnownArea();
  777. if ( enemyObj->GetLastKnownArea() == area )
  778. {
  779. // There is an enemy building in this area - avoid it as a spy.
  780. const float enemyBuildingCost = 10.0f;
  781. dist *= enemyBuildingCost;
  782. }
  783. }
  784. }
  785. // Spies avoid teammates, since they draw attention and gunfire.
  786. const float teammateCost = 10.0f;
  787. dist += dist * teammateCost * area->GetPlayerCount( m_me->GetTeamNumber() );
  788. // We shouldn't be getting NaNs here. It will be handled when we return, but ideally
  789. // it should be fixed here and not just worked around in NavAreaBuildPath.
  790. DebuggerBreakOnNaN_StagingOnly( dist );
  791. }
  792. float cost = ( dist * preference );
  793. if ( area->HasAttributes( NAV_MESH_FUNC_COST ) )
  794. {
  795. cost *= area->ComputeFuncNavCost( m_me );
  796. DebuggerBreakOnNaN_StagingOnly( cost );
  797. }
  798. return cost + fromArea->GetCostSoFar();
  799. }
  800. }
  801. CTFBot *m_me;
  802. RouteType m_routeType;
  803. float m_stepHeight;
  804. float m_maxJumpHeight;
  805. float m_maxDropHeight;
  806. };
  807. //---------------------------------------------------------------------------------------------
  808. class CClosestTFPlayer
  809. {
  810. public:
  811. CClosestTFPlayer( const Vector &where, int team = TEAM_ANY )
  812. {
  813. m_where = where;
  814. m_closeRangeSq = FLT_MAX;
  815. m_closePlayer = NULL;
  816. m_team = team;
  817. }
  818. CClosestTFPlayer( CBaseEntity *entity, int team = TEAM_ANY )
  819. {
  820. m_where = entity->WorldSpaceCenter();
  821. m_closeRangeSq = FLT_MAX;
  822. m_closePlayer = NULL;
  823. m_team = team;
  824. }
  825. bool operator() ( CBasePlayer *player )
  826. {
  827. if ( !player->IsAlive() )
  828. return true;
  829. if ( player->GetTeamNumber() != TF_TEAM_RED && player->GetTeamNumber() != TF_TEAM_BLUE )
  830. return true;
  831. if ( m_team != TEAM_ANY && player->GetTeamNumber() != m_team )
  832. return true;
  833. CTFBot *bot = ToTFBot( player );
  834. if ( bot && bot->HasAttribute( CTFBot::IS_NPC ) )
  835. return true;
  836. float rangeSq = ( m_where - player->GetAbsOrigin() ).LengthSqr();
  837. if ( rangeSq < m_closeRangeSq )
  838. {
  839. m_closeRangeSq = rangeSq;
  840. m_closePlayer = player;
  841. }
  842. return true;
  843. }
  844. Vector m_where;
  845. float m_closeRangeSq;
  846. CBasePlayer *m_closePlayer;
  847. int m_team;
  848. };
  849. #endif // TF_BOT_H