Skip to content

Instantly share code, notes, and snippets.

@sigsegv-mvm
Created January 14, 2021 05:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sigsegv-mvm/020363706ec8673a88972d5c5572840a to your computer and use it in GitHub Desktop.
Save sigsegv-mvm/020363706ec8673a88972d5c5572840a to your computer and use it in GitHub Desktop.
A dense and probably horrendously difficult-to-understand description of how the bot_generator and bot_action_point entities work
CTFBotGenerator / bot_generator
================================================================================
bool m_bSuppressFire = false ; kv "suppressFire"
bool m_bDisableDodge = false ; kv "disableDodge"
bool m_bUseTeamSpawnpoint = false ; kv "useTeamSpawnPoint"
bool m_bRetainBuildings = false ; kv "retainBuildings"
int m_iOnDeathAction = 1 ; kv "actionOnDeath"
int m_spawnCount = 0 ; kv "count"
int m_maxActiveCount = 0 ; kv "maxActive"
float m_spawnInterval = 0.0f ; kv "interval"
char* m_className = "" ; kv "class"
char* m_teamName = "" ; kv "team"
char* m_actionPointName = "" ; kv "action_point"
char* m_initialCommand = "" ; kv "initial_command"
int m_difficulty = -1 ; kv "difficulty"
bool m_bSpawnOnlyWhenTriggered = false ; kv "spawnOnlyWhenTriggered"
================================================================================
InputEnable(void) ; kv "Enable"
InputDisable(void) ; kv "Disable"
InputSetSuppressFire(bool) ; kv "SetSuppressFire"
InputSetDisableDodge(bool) ; kv "SetDisableDodge"
InputSetDifficulty(int) ; kv "SetDifficulty"
InputCommandGotoActionPoint(char*) ; kv "CommandGotoActionPoint"
InputSetAttentionFocus(char*) ; kv "SetAttentionFocus"
InputClearAttentionFocus(char*) ; kv "ClearAttentionFocus"
InputSpawnBot(void) ; kv "SpawnBot"
InputRemoveBots(void) ; kv "RemoveBots"
================================================================================
COutputEvent m_onSpawned ; kv "OnSpawned"
COutputEvent m_onExpended ; kv "OnExpended"
COutputEvent m_onBotKilled ; kv "OnBotKilled"
================================================================================
CTFBotGenerator::Activate
- checks and caches whether class name is "auto"
- finds action_point named entity and saves a handle to it if found
CTFBotGenerator::GeneratorThink
- if game is in waiting-for-players mode or in any roundstate besides GR_STATE_PREROUND or GR_STATE_RND_RUNNING, delays for a bit
- otherwise: if !m_bSpawnOnlyWhenTriggered, calls SpawnBot
CTFBotGenerator::SpawnBot
- removes any invalid bot handles or valid handles to bots on spectator team
- if the number of bot handles in the vector is >= m_maxActiveCount, reschedules the think and returns
- attempts to get an available bot from the "pool"
- note that the separate tf_bot_quota logic also grabs bots from the "pool" (though with a lot of extra quota enforcement stuff)
- the "pool" is defined as all true TFBot players currently on the server who are on TEAM_UNASSIGNED or TEAM_SPECTATOR
- if there is at least one bot meeting those requirements, it has TFBot attribute QuoteManaged removed and is picked
- if no bots are available from the "pool", then an attempt will be made to create a new TFBot
- this could fail for reasons of e.g. maxplayers
- if a bot cannot be created, the SpawnBot function simply gives up and returns
- the bot, picked from the pool or newly created, has its handle added to the vector of bot handles
- if the generator is set not to use the team spawn point, the bot's spawn point will be set to the generator entity itself
- if the generator has suppress fire set, the bot will have TFBot attribute SuppressFire added to it
- if the generator has retain buildings set, the bot will have TFBot attribute RetainBuildings added to it
- if the generator has disable dodge set, the bot will have TFBot attribute DisableDodge added to it
- the bot's skill level will be set based on the generator's difficulty value
- 0 thru 3 correspond to the 4 difficulty levels
- negative 1 means the bot's skill level won't be messed with (so presumably it'll default to tf_bot_difficulty)
- TODO: m_nIgnoreMask / m_spawnflags stuff
- the bot's attributes will be modified based on the generator's on-death action setting:
- if 1: TFBot attribute RemoveOnDeath is added to the bot
- if 2: TFBot attribute BecomeSpectatorOnDeath is added to the bot
- else: neither attribute is added to the bot
- the bot's m_hMovementGoal is set to the generator's action point entity
- the bot is assigned to a team based on the generator's team setting
- normal team names are recognized: "unassigned", "spectator", "red", "blue"
- the string "spectate" is specially recognized
- the string "auto" is also specially recognized and will choose a team based on the game's usual auto-team-selection logic
- if the team name provided doesn't match anything recognized, it falls back to the equivalent of "auto"
- the bot is assigned a class based on the generator's class setting
- the string "auto" is specially recognized and uses the same semi-smart bot auto-class-selection logic seen elsewhere
- otherwise, the string given is passed directly into CTFPlayer::HandleCommand_JoinClass... so if it's invalid, dunno what happens
- if the bot is not alive, it is force-respawned
- the bot's aim angles are snapped to be equivalent to the orientation angle of the generator entity
- if the generator has an initial command string specified, it is passed along to CTFBotTacticalMonitor::OnCommandString
- the generator's OnSpawned output is fired
- if the generator has spawned the number of bots it was asked to, it will fire the OnExpended output and stop thinking
- otherwise, the generator will schedule another think for the future based on the spawn interval setting
CTFBotGenerator::OnBotKilled
- fires output OnBotKilled
CTFBotGenerator::InputEnable
- sets m_bEnabled
- if !m_bExpended:
- starts GeneratorThink
- if m_iSpawnsPendingBeforeExpended == 0:
- sets m_iSpawnsPendingBeforeExpended to m_spawnCount
- schedules an immediate think
CTFBotGenerator::InputDisable
- unsets m_bEnabled
- unschedules thinks
CTFBotGenerator::InputSetSuppressFire
- sets m_bSuppressFire to input value
CTFBotGenerator::InputSetDisableDodge
- sets m_bDisableDodge to input value
CTFBotGenerator::InputSetDifficulty
- sets m_difficulty to input value; clamped to range [-1,3]
CTFBotGenerator::InputCommandGotoActionPoint
- attempts to find bot_action_point entity with name equal to input value; if no matching entity is found, does nothing
- removes any invalid bot handles or valid handles to bots on spectator team
- for all valid bot handles:
- sets bot's m_hMovementGoal to the entity named by the input value
- invokes NextBot command string "goto action point" on the bot
CTFBotGenerator::InputSetAttentionFocus
- attempts to find entity with name equal to input value; if no matching entity is found, does nothing
- removes any invalid bot handles or valid handles to bots on spectator team
- for all valid bot handles: sets m_hAttentionFocus to the entity named by the input value
CTFBotGenerator::InputClearAttentionFocus
- IGNORES input data
- removes any invalid bot handles or valid handles to bots on spectator team
- for all valid bot handles: clears m_hAttentionFocus
CTFBotGenerator::InputSpawnBot
- calls SpawnBot if generator is enabled
CTFBotGenerator::InputRemoveBots
- for all valid bot handles:
- calls UTIL_Remove on them
- executes ServerCommand "kickid <userid_of_the_bot>"
- removes all bot handles from the vector
NOTE: logic for function CTFBotTacticalMonitor::OnCommandString:
- "cloak": if bot is a spy and is not cloaked, makes the bot press mouse2
- "uncloak": if bot is a spy and is cloaked, makes the bot press mouse2
- "disguise": if bot is a spy and is allowed to disguise, chooses a random class on the enemy team and disguises the bot
- "despawn": causes a SUSPEND_FOR transition to action CTFDespawn
- "taunt": causes a SUSPEND_FOR transition to action CTFBotTaunt
- "goto action point": causes a SUSPEND_FOR transition to action CTFGotoActionPoint
- "attack sentry at next action point": causes a SUSPEND_FOR transition to action CTFTrainingAttackSentryActionPoint
- "build sentry at nearest sentry hint":
- ignored if bot is not an engineer
- searches for the nearest bot_hint_sentrygun entity to the bot that is available:
- same teamnum as the bot
- not currently owned by another bot
- not disabled
- etc
- if no suitable hint entities are found, gives up
- gives bot ownership of the hint entity and causes a SUSPEND_FOR transition to action CTFBotEngineerBuilding
NOTE: logic for AI action CTFDespawn:
- if bot entity is a player, executes ServerCommand "kickid <userid_of_the_bot>"
- otherwise, calls UTIL_Remove
NOTE: logic for AI action CTFGotoActionPoint:
- if m_hMovementGoal is not a valid bot_action_point entity, the action does a DONE transition
- if bot is not within the "desired range" of the action point entity m_hMovementGoal, then path toward it
- note that at any time, the CTFBotTacticalMonitor AI action running concurrently might decide to SUSPEND_FOR CTFBotUseTeleporter
- once within range:
- if the action point has a command associated with it, that gets passed along to CTFBotTacticalMonitor::OnCommandString
- fires OnReachedActionPoint output of the action point
- waits for the action point's "stay time" as necessary
- once done:
- updates m_hMovementGoal to the action point's next action point (if it has one)
- will CHANGE_TO a new instance of CTFGotoActionPoint
- so, you go back to the start of this AI logic tree
- and if there was no next action point, m_hMovementGoal will be invalid, and so the action chain finally ends
NOTE: logic for AI action CTFTrainingAttackSentryActionPoint:
- if bot is not within the "desired range" of the action point entity m_hMovementGoal, then path toward it
- once within range, aim toward entity m_hTargetSentry and press mouse1
TODO:
- find all refs to 0x2AD8 NextBotPlayer::m_hSpawnPoint
- find all refs to 0x2B20 CTFBot::m_hMovementGoal
- find all refs to 0x2B28 CTFBot::m_hGenerator
- find all refs to 0x2B34 CTFBot::m_hTargetSentry
- find all refs to 0x2BE0 CTFBot::m_hAttentionFocus
- find all refs to 0x2BC8 CTFBot::m_nIgnoreMask
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment