Bot 决策:感知、瞄准、档案、路径跟随与团队沟通
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、脚 10 | game/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/5 | game/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() 时 +50 | game/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_HIGH | game/server/cstrike15/bot/cs_bot_event_weapon.cpp:20-94 |
player_footstep | 1100,PRIORITY_LOW | game/server/cstrike15/bot/cs_bot_event_player.cpp:225-236 |
bomb_beep | 1500 内且尚不知道炸弹位置时,记下位置(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_FIRE | 2000 以内开火;更远时只有狙击手或“被对方开枪”才开火 | 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 节):
PickNewAimSpot每隔AimFocusInterval × U(0.8, 1.2)秒重选瞄准点。敌人可见时,代码先按武器与技能选部位(机枪、AWP、霰弹、后坐力大时打腹部,Skill ≥ 0.8才打头),但紧接着一行注释为temp test的代码在这个分支里把目标无条件改回腹部。因此当前快照中,敌人可见时所有 Bot 都瞄腹部;敌人不可见时则瞄向最后看到敌人的位置:game/server/cstrike15/bot/cs_bot_weapon.cpp:185-268。- 瞄准误差
m_aimFocus按AimFocusDecay指数衰减,同时不低于“当前视角与目标的夹角(上限 60°)×AimFocusOffsetScale”,再在该半径的圆盘内随机取偏移:game/server/cstrike15/bot/cs_bot_weapon.cpp:270-295。 UpdateAimPrediction按目标速度外推位置、叠加误差,并扣除aim punch × (1 + (1 − Skill) × 0.8 × 噪声)做后坐补偿:game/server/cstrike15/bot/cs_bot_weapon.cpp:301-320。- 视角由弹簧逼近目标角,刚度/阻尼/最大加速度取档案的
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):
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);起点代价即“危险 × 系数”,快速路线为 0 | game/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 ×20 | game/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_beep | 1500 内获知炸弹位置,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 按不完全信息校验人质位置,发现人质全失或被带走时喊话并回到 Idle | game/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与调试显示。