CD Code Digest Hub /CS:GO

Bot 决策:感知、瞄准、档案、路径跟随与团队沟通

53 分钟阅读 11 个章节 1 张图

Bot 页说明了 CCSBot 如何被每帧驱动、如何用状态机组织行为;本页深入真正决定 Bot“像不像人”的代码:它怎样看见和听见敌人、反应延迟如何实现、怎样瞄准与开火、BotProfile.db 的字段落到哪些公式上、如何沿导航网格移动并从卡住中恢复、如何购买,以及如何用语音/无线电与队友(包括人类)交换信息并响应炸弹、人质、枪声等战术事件。

相关页面:Bot · 导航网格 · 武器与射击 · 投掷物与 C4 · 玩家 · 比赛模式

相关源码文件#

  • 感知:game/server/cstrike15/bot/cs_bot_vision.cpp、game/server/cstrike15/bot/cs_bot_listen.cpp、game/server/cstrike15/bot/cs_bot_event.cpp、cs_bot_event_{weapon,player,bomb}.cpp、game/shared/cstrike15/bot/bot_manager.cpp(烟雾遮挡)
  • 交战与瞄准:game/server/cstrike15/bot/cs_bot_update.cpp、game/server/cstrike15/bot/cs_bot_weapon.cpp、game/server/cstrike15/bot/cs_bot_statemachine.cpp、game/server/cstrike15/bot/states/cs_bot_attack.cpp
  • 档案与难度:game/shared/cstrike15/bot/bot_profile.h、game/shared/cstrike15/bot/bot_profile.cpp、game/shared/cstrike15/bot/bot_constants.h、game/server/cstrike15/bot/cs_bot_init.cpp(ConVar)
  • 移动:game/server/cstrike15/bot/cs_bot_pathfind.cpp、game/server/cstrike15/bot/cs_bot_nav.cpp、game/server/cstrike15/bot/cs_bot.h(PathCost)
  • 购买:game/server/cstrike15/bot/states/cs_bot_buy.cpp
  • 沟通:game/server/cstrike15/bot/cs_bot_chatter.h、game/server/cstrike15/bot/cs_bot_chatter.cpp、game/server/cstrike15/bot/cs_bot_radio.cpp
  • 战术知识:game/server/cstrike15/bot/cs_gamestate.cpp、game/server/cstrike15/bot/cs_bot_manager.cpp、game/server/cstrike15/bot/states/cs_bot_idle.cpp

1. 一次更新中的决策顺序#

证据:Upkeep 在网格生成中或死亡时直接返回,瞄准敌人时每 tick 更新瞄准点,否则执行注视点状态机并叠加视线漂移,最后统一走 UpdateLookAngles:game/server/cstrike15/bot/cs_bot_update.cpp:29-142。Update:语音更新与死亡分支 game/server/cstrike15/bot/cs_bot_update.cpp:281-293;卡住/可破坏物/门检查 game/server/cstrike15/bot/cs_bot_update.cpp:310-317;遗忘 20 秒前的噪声、更新区域 game/server/cstrike15/bot/cs_bot_update.cpp:325-344;反应队列与开火决策 game/server/cstrike15/bot/cs_bot_update.cpp:372-492;无线电 game/server/cstrike15/bot/cs_bot_update.cpp:633-634;场景打断 game/server/cstrike15/bot/cs_bot_update.cpp:693-750;自动跟随 game/server/cstrike15/bot/cs_bot_update.cpp:755-790;执行状态机 game/server/cstrike15/bot/cs_bot_update.cpp:846-881。

节拍:g_BotUpdateSkipCount = 2,Update 在 (tickcount + entindex) % 2 == 0 时运行,即每 2 tick 一次;但 g_BotUpdateInterval 按 (g_BotUpdateSkipCount + 1) × tick = 3 tick 计算:game/shared/cstrike15/bot/bot_manager.h:17、game/shared/cstrike15/bot/bot_manager.cpp:104-107、game/shared/cstrike15/bot/bot_manager.cpp:134-144。反应队列步数和卡住检测的速度都用这个间隔换算:game/server/cstrike15/bot/cs_bot_vision.cpp:1730-1736、game/server/cstrike15/bot/cs_bot_nav.cpp:479-481。(推断:实际反应延迟约为档案 ReactionTime 的 2/3,卡住检测估出的速度也偏低 1/3;未运行验证。)

2. 视觉#

可见性。IsVisible(pos) 依次判断:失明 → 不可见;可选视锥测试;视线是否穿过足够厚的烟雾;MASK_VISIBLE_AND_NPCS | CONTENTS_BLOCKLOS 追踪是否被挡:game/server/cstrike15/bot/cs_bot_vision.cpp:334-360。烟雾判定累加视线落在每团烟里的长度,超过 0.7 × SmokeGrenadeRadius 或端点在烟内即视为被挡:game/shared/cstrike15/bot/bot_manager.cpp:292-323。IsVisible(player) 先以身体中心做视锥快速剔除和“地图最大视距”检查(g_pMapInfo->m_flBotMaxVisionDistance、bot_max_vision_distance_override,合作模式另有闲置视距),再依次追踪腹部、头顶、脚、左右两侧五个部位:game/server/cstrike15/bot/cs_bot_vision.cpp:362-500。

注意到(notice)。看见不等于注意到。对不是当前目标的敌人,IsNoticable 掷骰决定本次是否察觉:刚开过枪的敌人必被注意;跑动速度 > 200 必被注意;否则:

因素取值证据
可见部位权重(合计为覆盖率 %)腹 40、左/右侧各 20、头 10、脚 10game/server/cstrike15/bot/cs_bot_vision.cpp:1138-1169
距离插值300 以内取“近”概率,1000 以外取“远”概率,中间线性插值game/server/cstrike15/bot/cs_bot_vision.cpp:1172-1189
姿态 × 速度(近/远)行走站立 100/75、行走蹲伏 90/60、静止站立 100/10、静止蹲伏 80/5game/server/cstrike15/bot/cs_bot_vision.cpp:1196-1234
技能缩放× (0.5 + 0.5 × Skill)game/server/cstrike15/bot/cs_bot_vision.cpp:1242-1243
警觉加成IsAlert() 时 +50game/server/cstrike15/bot/cs_bot_vision.cpp:1245-1250
时间量子× 距上次检查的时间 / 0.25 秒,下限 0.1%game/server/cstrike15/bot/cs_bot_vision.cpp:1252-1264

选威胁。FindMostDangerousThreat 失明时返回空;遍历玩家,记录最近的可见队友,对敌人做可见与注意判定(当前威胁免掷骰),记下携弹者与狙击手,并按距离维护最多 16 个可见威胁的有序表:game/server/cstrike15/bot/cs_bot_vision.cpp:1273-1300、game/server/cstrike15/bot/cs_bot_vision.cpp:1404-1416、game/server/cstrike15/bot/cs_bot_vision.cpp:1485-1511。选择规则:仍能看见当前威胁且没有“显著更近”者则保持;自己是狙击手且看到敌方狙击手时优先对狙(除非 500 内有正对自己的敌人);否则取最近的正对自己的敌人;都没有则取最近者:game/server/cstrike15/bot/cs_bot_vision.cpp:1625-1683。

反应时间。威胁快照写入 20 格环形队列 m_enemyQueue,“被意识到”的敌人取的是 ReactionTime 之前的那一格;因此 Bot 总是对稍早的世界状态作出反应:game/server/cstrike15/bot/cs_bot.h:1304、game/server/cstrike15/bot/cs_bot_vision.cpp:1689-1743、game/server/cstrike15/bot/cs_bot_vision.cpp:1749-1757。

导航网格提供的视觉提示。UpdatePeripheralVision 每约 0.29 秒检查当前遭遇路径(m_spotEncounter)上的隐藏点是否进入视野并记录时间;UpdateLookAround 在埋伏或静止 2 秒以上时,以当前区域的 approach 信息计算接近点并盯防,忽略敌人按最早到达时间不可能已到达的点:game/server/cstrike15/bot/cs_bot_vision.cpp:703-736、game/server/cstrike15/bot/cs_bot_vision.cpp:783-800、game/server/cstrike15/bot/cs_bot_vision.cpp:837-851。

3. 听觉#

Bot 不直接监听引擎声音,而是监听游戏事件。CCSBotManager 用宏为 player_footstep、weapon_fire、bomb_beep 等事件(头文件中共 34 处声明)注册监听器,再用 CCSBOTMANAGER_ITERATE_BOTS 把事件分发给每个 Bot 的同名回调:game/server/cstrike15/bot/cs_bot_manager.h:54-97、game/server/cstrike15/bot/cs_bot_manager.h:105-115、game/server/cstrike15/bot/cs_bot_manager.h:383-420。

声源事件可闻距离 / 优先级证据
weapon_fire投掷物与 C4 无声;刀 1000;AWP 99999;带消音 1000;其余 2000;PRIORITY_HIGHgame/server/cstrike15/bot/cs_bot_event_weapon.cpp:20-94
player_footstep1100,PRIORITY_LOWgame/server/cstrike15/bot/cs_bot_event_player.cpp:225-236
bomb_beep1500 内且尚不知道炸弹位置时,记下位置(CT 还会喊话报点)game/server/cstrike15/bot/cs_bot_event_bomb.cpp:81-113

OnAudibleEvent 的规则:忽略队友(诱饵弹 decoy_firing 除外);超出距离不闻;3 秒短期记忆内只接受更高优先级或更近的新声音;声源不在任何导航区域上则忽略;记录位置时加入随距离增大的误差(2000 单位处最大 ±400),并夹回声源所在区域;行进距离为节省开销直接用直线距离;1000 内的声音让 Bot 进入警觉:game/server/cstrike15/bot/cs_bot_event.cpp:23-121。

声音如何变成行动:噪声须在 ReactionTime 之后才算“听到”;HeardInterestingNoise 还要求未失明、已远离回合初的安全期、处于 ENGAGE_AND_INVESTIGATE,埋伏时只对 100 + 400 × Aggression 以内的声音起反应,最后按 GetNoiseInvestigateChance(距离越近、存活队友越少概率越高)掷骰:game/server/cstrike15/bot/cs_bot_listen.cpp:18-65、game/server/cstrike15/bot/cs_bot.cpp:762-782。搜寻、埋伏等状态据此切到 InvestigateNoise:game/server/cstrike15/bot/states/cs_bot_hunt.cpp:150-152、game/server/cstrike15/bot/states/cs_bot_hide.cpp:235-251。

4. 交战决策、瞄准与开火#

是否开火由 Disposition 决定(ENGAGE_AND_INVESTIGATE / OPPORTUNITY_FIRE / SELF_DEFENSE / IGNORE_ENEMIES):game/server/cstrike15/bot/cs_bot.h:634-639。

Disposition规则证据
IGNORE_ENEMIES从不开火game/server/cstrike15/bot/cs_bot_update.cpp:397-402
SELF_DEFENSE对方正对自己且刚开枪,或距离 < 500;合作模式狙击手总是开火game/server/cstrike15/bot/cs_bot_update.cpp:404-422
ENGAGE_AND_INVESTIGATE / OPPORTUNITY_FIRE2000 以内开火;更远时只有狙击手或“被对方开枪”才开火game/server/cstrike15/bot/cs_bot_update.cpp:424-449
任意(非忽略)1 秒内受过攻击则还击;持刀埋伏时等敌人进入 250 再出手game/server/cstrike15/bot/cs_bot_update.cpp:452-483

Attack() 在 bot_zombie 或换弹时不进入;持投掷物时直接朝敌人脚下投掷;在隐藏点上以 60% 概率“蹲守”;进入攻击时“作弊”地拿到敌人的初始位置,并把瞄准误差重置为 AimFocusInitial:game/server/cstrike15/bot/cs_bot_statemachine.cpp:368-420。

瞄准模型(每 tick,见第 1 节):

  1. PickNewAimSpot 每隔 AimFocusInterval × U(0.8, 1.2) 秒重选瞄准点。敌人可见时,代码先按武器与技能选部位(机枪、AWP、霰弹、后坐力大时打腹部,Skill ≥ 0.8 才打头),但紧接着一行注释为 temp test 的代码在这个分支里把目标无条件改回腹部。因此当前快照中,敌人可见时所有 Bot 都瞄腹部;敌人不可见时则瞄向最后看到敌人的位置:game/server/cstrike15/bot/cs_bot_weapon.cpp:185-268。
  2. 瞄准误差 m_aimFocus 按 AimFocusDecay 指数衰减,同时不低于“当前视角与目标的夹角(上限 60°)× AimFocusOffsetScale”,再在该半径的圆盘内随机取偏移:game/server/cstrike15/bot/cs_bot_weapon.cpp:270-295。
  3. UpdateAimPrediction 按目标速度外推位置、叠加误差,并扣除 aim punch × (1 + (1 − Skill) × 0.8 × 噪声) 做后坐补偿:game/server/cstrike15/bot/cs_bot_weapon.cpp:301-320。
  4. 视角由弹簧逼近目标角,刚度/阻尼/最大加速度取档案的 LookAngle*Normal 或攻击时的 LookAngle*Attacking:game/server/cstrike15/bot/cs_bot_vision.cpp:171-196。

扣扳机条件(FireWeaponAtEnemy):狙击枪须静止、已开镜且视角稳定 ReactionTime 秒;距锁定敌人已过 AttackDelay;未被惊吓;对方不在盾后、自己未换弹且弹匣非空、敌人可见;狙击枪还要求预计散布小于 AWP 50 / 其他 25;准星与目标夹角小于 atan(halfSize / range);开启友伤时队友不在火线上;刀只在 75 内挥砍并对背身目标重击:game/server/cstrike15/bot/cs_bot_weapon.cpp:23-120。

攻击状态内的勇气:每次进入攻击以 100 − 100 × Aggression 的概率成为“胆小”;被压制、看到狙击手(自己不是狙击手)、胆小且寡不敌众、或被 2 人以上压制且 Aggression < 1 时,只要有敌人正对自己就尝试撤退并喊话;丢失目标后等待 2 + 2 × (1 − Aggression) 秒(狙击 +3、蹲伏 +1)再追击;敌人从掩体后重新出现时再给一次 ReactionTime 的重新捕获延迟:game/server/cstrike15/bot/states/cs_bot_attack.cpp:146、game/server/cstrike15/bot/states/cs_bot_attack.cpp:356-383、game/server/cstrike15/bot/states/cs_bot_attack.cpp:625-660。

5. BotProfile 与难度#

加载:CCSBotManager::ServerActivate 先读语音库 BotChatter.db;若存在 BotPackList.db 则逐个加载其中列出的档案文件,否则加载 BotProfile.db(合作任务为 BotProfileCoop.db);随后为档案引用到的其他语音库各读一次:game/server/cstrike15/bot/cs_bot_manager.cpp:430-494。ConVar bot_profile_db 虽被定义和 extern 声明,但本次未检索到读取它的代码:game/server/cstrike15/bot/cs_bot_init.cpp:84、game/shared/cstrike15/bot/bot_util.h:48。文件打不开只打印警告并返回:game/shared/cstrike15/bot/bot_profile.cpp:213-224。

格式(按解析器还原的语法示意,不是仓库中的文件内容——仓库不含任何 .db):

text
Default            // 全局默认值,之后每个条目都先复制它
    Skill = 50
End
Template <模板名>  // 模板:只进入模板列表
    <属性> = <值>
End
Skin <皮肤名> Model = <模型> End
<模板1>+<模板2> <Bot 名>   // 先复制 Default,再按出现顺序继承各模板
    <属性> = <值>
End

证据:Default/Template/Skin 关键字与 End game/shared/cstrike15/bot/bot_profile.cpp:256-351;先复制 Default、以 + 分隔逐个继承模板,模板名不存在则报错并放弃整个文件的剩余部分 game/shared/cstrike15/bot/bot_profile.cpp:353-406;读名字 game/shared/cstrike15/bot/bot_profile.cpp:409-430;属性 = 值 循环,未知属性只打印错误 game/shared/cstrike15/bot/bot_profile.cpp:432-592;模板入模板表、其余入档案表 game/shared/cstrike15/bot/bot_profile.cpp:594-606。另有一条硬编码:名字首字符编码为奇数的 Bot 偏好消音器:game/shared/cstrike15/bot/bot_profile.cpp:421-429。

字段如何作用于行为:

字段(解析)行为上的读取点证据
Skill(÷100)注意概率缩放;瞄准部位选择(被 temp test 覆盖);后坐补偿精度game/shared/cstrike15/bot/bot_profile.cpp:486-489、game/server/cstrike15/bot/cs_bot_vision.cpp:1243、game/server/cstrike15/bot/cs_bot_weapon.cpp:210-219、game/server/cstrike15/bot/cs_bot_weapon.cpp:315-318
Aggression(÷100)胆小概率、追击等待、PathCost 危险系数、坠落容忍度、炸弹逃离时机、回合安全期 15 + 5a、让路耐心 5 − 3a、埋伏时的听觉半径game/shared/cstrike15/bot/bot_profile.cpp:482-485、game/server/cstrike15/bot/states/cs_bot_attack.cpp:146、game/server/cstrike15/bot/states/cs_bot_attack.cpp:640、game/server/cstrike15/bot/cs_bot.h:1972-1973、game/server/cstrike15/bot/cs_bot.h:2043、game/server/cstrike15/bot/cs_bot_update.cpp:706、game/server/cstrike15/bot/cs_bot_init.cpp:210、game/server/cstrike15/bot/cs_bot_pathfind.cpp:1712、game/server/cstrike15/bot/cs_bot_listen.cpp:53
Teamwork(÷100)bot_allow_rogues 开启时每 10–30 秒以 100 × (1 − t)% 概率变成“流氓”(ConVar 说明:不听无线电、不追求场景目标);自动跟随人类的门槛与概率game/shared/cstrike15/bot/bot_profile.cpp:499-502、game/server/cstrike15/bot/cs_bot.cpp:789-807、game/server/cstrike15/bot/cs_bot_init.cpp:66、game/server/cstrike15/bot/cs_bot_update.cpp:760-781
ReactionTime(秒)威胁队列回溯、听觉延迟、无线电响应 1 + 2r、狙击稳定时间、重新捕获延迟game/shared/cstrike15/bot/bot_profile.cpp:535-539、game/server/cstrike15/bot/cs_bot_vision.cpp:1730、game/server/cstrike15/bot/cs_bot_listen.cpp:24、game/server/cstrike15/bot/cs_bot_radio.cpp:78、game/server/cstrike15/bot/cs_bot_weapon.cpp:41、game/server/cstrike15/bot/states/cs_bot_attack.cpp:633
AttackDelay(秒)锁定敌人后到开第一枪的最短时间game/shared/cstrike15/bot/bot_profile.cpp:540-543、game/server/cstrike15/bot/cs_bot_weapon.cpp:47-49
AimFocusInitial/Decay/OffsetScale/Interval瞄准误差的初值、衰减、随转角增长、重选间隔game/shared/cstrike15/bot/bot_profile.cpp:503-518、game/server/cstrike15/bot/cs_bot_statemachine.cpp:417、game/server/cstrike15/bot/cs_bot_weapon.cpp:270-294
LookAngle{MaxAccel,Stiffness,Damping}{Normal,Attacking}视角弹簧参数game/shared/cstrike15/bot/bot_profile.cpp:564-587、game/server/cstrike15/bot/cs_bot_vision.cpp:185-196
WeaponPreference(可多次,none 清空)购买顺序;按首选武器类型筛选档案game/shared/cstrike15/bot/bot_profile.cpp:643-660、game/server/cstrike15/bot/states/cs_bot_buy.cpp:438-511、game/shared/cstrike15/bot/bot_profile.cpp:832-840
Difficulty(EASY+NORMAL+... 位集,覆盖继承值)档案可用于哪些难度game/shared/cstrike15/bot/bot_constants.h:21-35、game/shared/cstrike15/bot/bot_profile.cpp:612-641
Team(T / CT / 其他=任意)档案可用于哪一方game/shared/cstrike15/bot/bot_profile.cpp:549-563
VoicePitch、VoiceBank语音播放音调与所用语音库game/shared/cstrike15/bot/bot_profile.cpp:523-530、game/server/cstrike15/bot/cs_bot_chatter.cpp:1330-1334
Cost、Skin生涯模式身价、模型皮肤(非行为)game/shared/cstrike15/bot/bot_profile.h:139-140

难度如何选档案:bot_difficulty(默认 1)经 GetDifficultyLevel 映射为四档,排队匹配(sv_mmqueue_reservation 以 Q 开头)且非合作模式时强制 BOT_EASY:game/server/cstrike15/bot/cs_bot_init.cpp:60、game/server/cstrike15/bot/cs_bot_manager.h:146-158。添加 Bot 时按名字或模板找档案,否则 GetRandomProfile 在“难度位匹配、名字未被占用、阵营匹配、首选武器类型匹配”的档案中随机挑一个;合作模式会优先保证至少一名狙击手档案:game/server/cstrike15/bot/cs_bot_manager.cpp:1088-1197、game/shared/cstrike15/bot/bot_profile.cpp:802-851。除此之外,代码中直接读难度的只有购买逻辑(BOT_HARD 以上首轮优先买偏好武器):game/server/cstrike15/bot/states/cs_bot_buy.cpp:566-587。也就是说,难度差异主要通过“选哪一份档案”体现。

6. 路径跟随与卡住恢复#

计算路径(CCSBot::ComputePath):重算受 0.4–0.6 秒随机节流;起点是 m_lastKnownArea,若它高出脚下超过蹲跳高度(说明掉下了平台)则在 500 内重新找最近区域;终点区域取 GetNearestNavArea(goal);用 PathCost 跑 NavAreaBuildPath,不可达时退而走到最接近目标的区域;回溯父指针填入最多 256 格的 m_path;回合安全期内还会沿路径找出第一个“敌人可能先到”的区域作为初始遭遇区:game/server/cstrike15/bot/cs_bot_pathfind.cpp:1896-2049、game/server/cstrike15/bot/cs_bot.h:1057。A* 本身见导航网格。

PathCost(SAFEST_ROUTE 默认,FASTEST_ROUTE 用于赶时间):

项规则证据
危险系数(1 − 0.95 × Aggression) × 100(枪械竞赛爆破为 × 0.25);起点代价即“危险 × 系数”,快速路线为 0game/server/cstrike15/bot/cs_bot.h:1967-1988
禁行跳跃区→跳跃区;护送人质时进入 NO_HOSTAGES 区;会致死的跳落game/server/cstrike15/bot/cs_bot.h:1989-1998、game/server/cstrike15/bot/cs_bot.h:2033-2040
基础距离区域中心距离或梯长(梯子罚分系数当前为 1)game/server/cstrike15/bot/cs_bot.h:2001-2019
跳落伤害100 × 伤害²;快速路线下低于 15 × Aggression + 10 的伤害忽略game/server/cstrike15/bot/cs_bot.h:2021-2051
属性罚分蹲伏/行走区 ×20(快速)或 ×5,护送人质时蹲伏再 ×3;跳跃 ×1;伤害区 ×100;AVOID ×20game/server/cstrike15/bot/cs_bot.h:2053-2089
危险度安全路线额外加 dist + dist × 系数 × 区域危险度game/server/cstrike15/bot/cs_bot.h:2091-2095
随机扰动按 Bot、区域地址与 10 秒时间片生成 1–293 的伪随机代价,使同一 Bot 隔段时间换路线、短期重算保持一致game/server/cstrike15/bot/cs_bot.h:2097-2104
队友拥挤非攻击时加 50000 × 本队人数 / 区域尺寸game/server/cstrike15/bot/cs_bot.h:2106-2120

沿路径移动(UpdatePathMovement,返回 PROGRESSING / END_OF_PATH / PATH_FAILURE):梯子移动优先接管;接近终点改为行走;向路径前方取前瞻点并把视线引向转角;前方 50 单位内有蹲伏区则下蹲;用脚踝高度的“触须”避开近处障碍;前方有队友挡路时礼让 5 − 3 × Aggression 秒后重算路径;移动后若处于卡住状态则 Wiggle;目标高出头顶判定为掉落;在同一区域停留超过 4 秒或掉落则放弃路径:game/server/cstrike15/bot/cs_bot_pathfind.cpp:1420-1505、game/server/cstrike15/bot/cs_bot_pathfind.cpp:1542-1580、game/server/cstrike15/bot/cs_bot_pathfind.cpp:1683-1750、game/server/cstrike15/bot/cs_bot_pathfind.cpp:1752-1856。

卡住检测与恢复:StuckCheck 每次 Update 记录位移速度,凑满 10 个样本后平均速度低于 10(跑动)或 5(行走)即判定卡住,离开卡住点 75 单位后解除:game/server/cstrike15/bot/cs_bot_nav.cpp:448-526、game/server/cstrike15/bot/cs_bot.h:1329。Wiggle 每 0.3–0.5 秒随机换一个左/右/前/后方向(先探测不会跌落),并按 1–2 秒间隔尝试跳跃,NAV_MESH_NO_JUMP 区域不跳:game/server/cstrike15/bot/cs_bot_nav.cpp:749-839。等待、换弹时会重置卡住监视器以免误判:game/server/cstrike15/bot/cs_bot_update.cpp:883-898。

7. 购买#

Bot 每次 Spawn 都重置状态并进入 BuyState:game/server/cstrike15/bot/cs_bot_init.cpp:499-509。

  • 进入时:余额低于 bot_eco_limit(默认 2000)则本回合不买(bot_loadout 作弊配置除外);CT 没有主武器且余额 > 2500 时以 33%(< 4000)或 10% 的概率决定买盾;按 sv_bot_buy_grenade_chance(默认 33%)决定是否买手雷,类型按 sv_bot_buy_*_weight 加权(HE 默认权重 6,其余 1);没有手枪必买,持默认手枪时以 75%(其他武器全被禁用或余额 < 1000)或 33% 的概率换一把:game/server/cstrike15/bot/cs_bot_init.cpp:86、game/server/cstrike15/bot/states/cs_bot_buy.cpp:19-37、game/server/cstrike15/bot/states/cs_bot_buy.cpp:125-216。
  • 每 0.02 秒尝试一次:等待导航网格加载与 0.25 秒初始延迟;不在购买区则放弃;按档案 WeaponPreference 顺序发 buy <alias>(每项最多重试 2 次,受 bot_allow_* 限制;若已持有当前偏好武器则结束偏好购买);仍无主武器时从内置表随机挑选;最后买弹药、头盔护甲、护甲、手枪与手雷:game/server/cstrike15/bot/states/cs_bot_buy.cpp:340-372、game/server/cstrike15/bot/states/cs_bot_buy.cpp:418-511、game/server/cstrike15/bot/states/cs_bot_buy.cpp:521-626、game/server/cstrike15/bot/states/cs_bot_buy.cpp:653-692。
  • 购买走的是 ClientCommand("buy ..."),即与人类相同的 CCSPlayer::ClientCommand → HandleCommand_Buy 路径(价格、购买区、时间限制由玩家层判定,见玩家):game/server/cstrike15/cs_player.cpp:9152-9158。
  • 买完后在冻结期内保持“装备最佳武器并换弹”,冻结结束转入 Idle:game/server/cstrike15/bot/states/cs_bot_buy.cpp:358-372。

8. 语音聊天与无线电#

短语库:BotPhraseManager::Initialize 解析 BotChatter.db,识别 Output、BaseDir、Place / Chatter 块,块内支持 Place、Count、Radio(等价的标准无线电)、Important 等属性:game/server/cstrike15/bot/cs_bot_chatter.cpp:524-620、game/server/cstrike15/bot/cs_bot_chatter.cpp:669-748。

说话的组织:每个 Bot 有一个 BotStatement 链表;BotChatterInterface::Update 先汇报敌情,在“还有敌人、无线电已静默 30 秒且 ShouldSpeak()(有存活队友且并非全员都在附近)”时请求队友报告,然后只播放全队当前唯一的活动语句(GetActiveStatement 扫描同队 Bot,返回正在说的或等待最久的那句),并删除失效、过时或被队友抢先说过的语句:game/server/cstrike15/bot/cs_bot_chatter.cpp:1695-1776、game/server/cstrike15/bot/cs_bot_chatter.cpp:1782-1830、game/server/cstrike15/bot/cs_bot_chatter.cpp:1835-1846。bot_chatter 可设为 off / radio / minimal / normal:game/server/cstrike15/bot/cs_bot_init.cpp:83、game/server/cstrike15/bot/cs_bot_chatter.h:508-513。

播放:同一地点 20 秒内不重复报;bot_chatter_use_rr 默认 1,此时短语作为概念交给响应规则系统(附带 place、count、合作模式、重甲等条件);否则从档案语音库取音频文件按 VoicePitch 播放;都失败时退化为等价的标准无线电消息:game/server/cstrike15/bot/cs_bot_chatter.cpp:1280-1366、game/server/cstrike15/cs_player.cpp:240。

Meme:语音携带的信息。语句播完时,附着在上面的 BotMeme 会被“传送”给所有存活的同队 Bot 解释执行,这是 Bot 之间共享知识的真实通道。例如炸弹点状态 meme 让收听者把该点标为已清/已下包,正在搜包的 Bot 立即重新选点并回答“收到”:game/server/cstrike15/bot/cs_bot_chatter.cpp:1161-1171、game/server/cstrike15/bot/cs_bot_chatter.cpp:68-106、game/server/cstrike15/bot/cs_bot_chatter.cpp:123-140。

响应无线电命令:player_radio 事件中来自同队、且不是“收到/否定/报告/欢呼/感谢/称赞”的命令被记下:game/server/cstrike15/bot/cs_bot_event_player.cpp:179-206。RespondToRadioCommands 在忙碌时丢弃命令(报告请求除外),等待 1 + 2 × ReactionTime(流氓再 +2)秒后响应,流氓只回答“否定”:game/server/cstrike15/bot/cs_bot_radio.cpp:63-96。

命令响应证据
报告情况ReportingIn()game/server/cstrike15/bot/cs_bot_radio.cpp:107-111
跟我来 / 掩护我 / 集合未跟随时开始跟随发令者game/server/cstrike15/bot/cs_bot_radio.cpp:113-125
发现敌人 / 需要支援 / 正在交火跟随并喊 “OnMyWay”game/server/cstrike15/bot/cs_bot_radio.cpp:127-137
撤退TryToRetreat()game/server/cstrike15/bot/cs_bot_radio.cpp:139-144
守住位置任务改为 HOLD_POSITION,在发令者位置附近隐藏game/server/cstrike15/bot/cs_bot_radio.cpp:146-155
冲 / 突击停止跟随,转入搜寻game/server/cstrike15/bot/cs_bot_radio.cpp:157-163
快离开已下包时逃离炸弹game/server/cstrike15/bot/cs_bot_radio.cpp:165-172
区域安全爆破模式 CT 已下包时把发令者最近的炸弹点标为已清game/server/cstrike15/bot/cs_bot_radio.cpp:174-197

9. 战术事件响应#

场景与 Zone 由 Bot 管理器在地图激活时从实体推出(见导航网格):game/server/cstrike15/bot/cs_bot_manager.cpp:1651-1792。之后 Bot 对战术事件的反应由“事件回调 + CSGameState 局部知识 + Idle 状态的场景分支”共同完成:

事件 / 情境处理证据
炸弹被安放广播给每个 Bot 的 CSGameState:只有 T 立即知道位置;CT Bot 放下手头事务回到 Idle,跟随者停止跟随game/server/cstrike15/bot/cs_gamestate.cpp:123-135、game/server/cstrike15/bot/cs_bot_event_bomb.cpp:53-77
CT 在下包后知道确切位置:有人在拆则掩护,否则(允许做场景目标时)去拆,再否则守点;只知道炸弹点:前往该点;都不知道:选最近的未清除炸弹点(超过 2000 则按搜索顺序),设任务 FIND_TICKING_BOMB 前往其中随机位置game/server/cstrike15/bot/states/cs_bot_idle.cpp:244-360
T 不知炸弹位置喊话请求报点,按搜索顺序前往炸弹点game/server/cstrike15/bot/states/cs_bot_idle.cpp:152-168
听到 bomb_beep1500 内获知炸弹位置,CT 喊话报点game/server/cstrike15/bot/cs_bot_event_bomb.cpp:81-113
T 已安放设任务 GUARD_TICKING_BOMB,在炸弹点所在 Place 的随机隐藏点埋伏game/server/cstrike15/bot/states/cs_bot_idle.cpp:122-151
T 携带 C4到下包时机后用 PathCost 选最近炸弹点内的随机位置game/server/cstrike15/bot/states/cs_bot_idle.cpp:170-180
炸弹将爆剩余时间少于 8 − 2 × Aggression 秒(有拆弹器 × 0.66),且知道位置、未在拆弹/交火时逃离game/server/cstrike15/bot/cs_bot_update.cpp:702-723
人质图CT 统计护送人数;T 按不完全信息校验人质位置,发现人质全失或被带走时喊话并回到 Idlegame/server/cstrike15/bot/cs_bot_update.cpp:726-749
听到枪声 / 脚步见第 3 节:记录噪声、变警觉,按概率前往调查game/server/cstrike15/bot/cs_bot_event.cpp:23-121
自己阵亡喊话 OnDeath,在死亡点周围提高本队危险度,后续路线会绕开game/server/cstrike15/bot/cs_bot.cpp:187-196

约束与边界#

  • 内容依赖:BotProfile.db、BotPackList.db、BotChatter.db 与 .nav 均不在仓库中;档案文件缺失时只打印警告,此时档案表为空,GetRandomProfile 返回 NULL,BotAddCommand 不会加入 Bot:game/shared/cstrike15/bot/bot_profile.cpp:217-224、game/server/cstrike15/bot/cs_bot_manager.cpp:1186-1196。
  • 反应时间上限:ReactionTime 被夹在 20 × 更新间隔 − 0.01 以内(队列长度限制):game/server/cstrike15/bot/cs_bot_vision.cpp:1729-1733。
  • 后坐补偿依赖 aim punch(推断):UpdateAimPrediction 读 GetAimPunchAngle(),而武器后坐力函数体在 partner depot 中被删空(见武器与射击),本快照中该补偿项可能恒为 0。
  • 调试开关会改变行为:bot_zombie(不攻击、不感知威胁)、bot_dont_shoot、bot_stop、bot_flipout(随机乱跑)、bot_ignore_players:game/server/cstrike15/bot/cs_bot_init.cpp:56-90、game/server/cstrike15/bot/cs_bot_vision.cpp:422-423。
  • 合作模式(Guardian / Coop Mission)分支:视距、噪声调查概率(恒 100)、阻挡区域、睡眠唤醒、BotChatterCoop 等大量特殊逻辑本页只点到为止:game/server/cstrike15/bot/cs_bot_update.cpp:242-270、game/server/cstrike15/bot/cs_bot.cpp:764-765。
  • 本次在 Bot 与导航目录中未发现 Removed for partner depot 裁剪标记。
  • 本页未展开:投掷物的目标选择与投掷状态机(LookForGrenadeTargets、UpdateGrenadeThrow)、开门/破坏障碍、人质护送与跟随状态细节、BotStatement 的条件/上下文短语全集、IsSignificantlyCloser 等辅助判定、bot_mimic 与调试显示。