场景与响应规则:CS:GO 的无线电、Bot 语音与人质台词
响应规则(Response Rules)、编排场景(choreo .vcd / CSceneEntity)与场景缓存(scenefilecache)是从 HL2/L4D 的 NPC 对白系统继承来的。它们在 CS:GO 中有实际运行路径:玩家无线电命令、Bot 无线电闲聊(默认开启)和人质台词都通过 Speak(concept, criteria) 进入响应规则,选中的响应再以“场景”或直接发声的方式播放。本页说明这条链路如何装配、如何决策、声音如何受过滤器约束,以及哪些部分因内容文件缺失而无法核对。
相关页面:游戏 DLL 契约 · 实体系统 · Bot · 玩家 · 引擎音频管线 · 语音与视频
相关源码文件#
game/server/ai_speech.cpp、game/server/ai_speech.h:CAI_Expresser、CAI_ExpresserHost、CMultiplayer_Expressergame/server/ai_responsesystem.cpp:游戏侧默认响应系统(scripts/talker/response_rules.txt)responserules/runtime/response_system.cpp:规则匹配运行时(静态库responserules_runtime)game/server/sceneentity.cpp、game/client/c_sceneentity.cpp:场景实体choreoobjects/choreoobjects.vpc:编排对象库(源码位于game/shared/choreo*.cpp)scenefilecache/SceneFileCache.cpp:scenes.image缓存game/server/cstrike15/cs_player.cpp、game/server/cstrike15/bot/cs_bot_radio.cpp、game/server/cstrike15/bot/cs_bot_chatter.cpp、game/server/cstrike15/hostage/cs_simple_hostage.cpp:CS:GO 调用点
1. 构建与装配#
| 组件 | 形态 | 谁使用 | 证据 |
|---|---|---|---|
responserules_runtime | 静态库,在 csgo_partner 组 | server.dll 链接 | responserules/runtime/responserules_runtime.vpc:8、vpc_scripts/groups.vgc:57、game/server/server_base.vpc:1133 |
choreoobjects | 静态库,源码是 game/shared 下的 choreoactor/channel/event/scene.cpp 与 sceneimage.cpp | server.dll、client.dll 链接 | choreoobjects/choreoobjects.vpc:9、choreoobjects/choreoobjects.vpc:20-29、game/server/server_base.vpc:1125、game/client/client_base.vpc:1245 |
ai_speech、ai_responsesystem、ai_criteria 等 | server.dll 源文件 | 表达器与响应系统 | game/server/server_base.vpc:125-126、game/server/server_base.vpc:181-204 |
sceneentity.cpp / c_sceneentity.cpp | server / client 源文件 | 场景实体与客户端表演 | game/server/server_base.vpc:620-622、game/client/client_base.vpc:210 |
scenefilecache.dll | AppSystem,在 csgo_partner 组 | server 与 client 都声明依赖,缺失时 DLLInit/Init 失败 | vpc_scripts/groups.vgc:58、game/server/gameinterface.cpp:4036-4046、game/server/gameinterface.cpp:781-782、game/client/cdll_client_int.cpp:670、game/client/cdll_client_int.cpp:1516-1517 |
CSceneFileCache::Init 读取 scenes/scenes<平台后缀>.image:头部 ID/版本不符直接 Error;PC 上文件不存在时清空缓冲、仍返回 INIT_OK(仅 X360 严格模式报错):scenefilecache/SceneFileCache.cpp:76-114。responserules/responserules_cli 与 C# 的 ResponseEditor 是编辑工具,不在构建组中,本页不展开。
2. CS:GO 中的调用点#
| 调用点 | 触发 | 进入响应规则的方式 | 证据 |
|---|---|---|---|
CCSPlayer::Radio | 无线电菜单(如 HandleMenu_Radio1,1.5 秒冷却)与自动无线电(如发现掉落的炸弹) | bot_chatter_use_rr(默认 1,开发者 ConVar)为真时 Speak( concept=无线电声音名, criteria, filter );否则发 CS_UM_SendAudio 用户消息 | game/server/cstrike15/cs_player.cpp:8239-8253、game/server/cstrike15/cs_player.cpp:4241、game/server/cstrike15/cs_player.cpp:240、game/server/cstrike15/cs_player.cpp:8468-8528 |
CCSBot::SpeakAudioResponseRules | Bot 闲聊系统选中短语时,附加 count、place 等条件 | Speak( concept, criteria, filter );旧路径 SpeakAudio 改发 CS_UM_RawAudio | game/server/cstrike15/bot/cs_bot_chatter.cpp:1295-1310、game/server/cstrike15/bot/cs_bot_radio.cpp:354-378、game/server/cstrike15/bot/cs_bot_radio.cpp:325-348 |
CHostage | 受伤(Pain)、开始跟随(StartFollowing,修饰 leaderteam:CT)等 | 人质自带 CMultiplayer_Expresser;hostage_is_silent(作弊 ConVar,默认 0)可关闭 | game/server/cstrike15/hostage/cs_simple_hostage.cpp:195-203、game/server/cstrike15/hostage/cs_simple_hostage.cpp:444-453、game/server/cstrike15/hostage/cs_simple_hostage.cpp:1422-1427、game/server/cstrike15/hostage/cs_simple_hostage.cpp:56 |
无线电的接收者由 ConstructRadioFilter 构造:GOTV 只在 tv_relayradio(默认 0)开启时加入;忽略无线电的玩家跳过;其余玩家用 CanPlayerHearTalker( player, this, bTeamOnly ) 判定,合作任务外 bTeamOnly = true:game/server/cstrike15/cs_player.cpp:196、game/server/cstrike15/cs_player.cpp:8434-8466。可听规则本身见 语音与视频。无线电还会给队友发头顶图标临时实体(合作任务除外):game/server/cstrike15/cs_player.cpp:8525-8527。Bot 闲聊的整体结构见 Bot。
3. 决策链:Speak → 规则匹配 → 分派#
证据:宿主 Speak 收集条件后转给表达器 game/server/ai_speech.h:348-357;CAI_Expresser::Speak 依次补充条件、查找、分派 game/server/ai_speech.cpp:920-945;FindResponse 取实体的响应系统 game/server/ai_speech.cpp:451-454、game/server/ai_speech.cpp:518;默认响应系统 game/server/baseentity.cpp:7768-7774、game/server/ai_responsesystem.cpp:548;规则匹配 responserules/runtime/response_system.cpp:1198-1210;分派分支 game/server/ai_speech.cpp:714-749;场景函数 game/server/baseflex.cpp:2116-2134。
- 表达器宿主:
CCSPlayer继承CBaseMultiplayerPlayer,后者是CAI_ExpresserHost<CBasePlayer>,在PostConstructor中创建CMultiplayer_Expresser:game/server/cstrike15/cs_player.h:271、game/server/basemultiplayerplayer.h:22、game/server/basemultiplayerplayer.cpp:47-64。 - 条件(criteria):
CCSPlayer::ModifyOrAppendCriteria追加team、去掉_var后缀的model、liveallies、liveenemies;Radio对 Bot 额外加gamemode=coop、isheavy:game/server/cstrike15/cs_player.cpp:13268-13286、game/server/cstrike15/cs_player.cpp:8501-8516。人质追加rescued:game/server/cstrike15/hostage/cs_simple_hostage.cpp:436-441。 - 规则文件:默认系统是
CAutoGameSystem,脚本为scripts/talker/response_rules.txt,Init时LoadRuleSet,每关LevelInitPreEntity预缓存并重置响应组:game/server/ai_responsesystem.cpp:365-377、game/server/ai_responsesystem.cpp:797-810、game/server/ai_responsesystem.cpp:435-447。 - 匹配:按条件分区取候选规则桶,逐条打分,保留最高分(并列时随机选一条);再从该规则的响应组中挑选响应,
MatchOnce规则命中后被禁用:responserules/runtime/response_system.cpp:1140-1191、responserules/runtime/response_system.cpp:1223-1240。 - 分派:若该实体已在说话,先移出实例化场景并停止句子,若仍在脚本场景中则放弃本次发言:
game/server/ai_speech.cpp:693-712。RESPONSE_SPEAK默认生成一个只含该声音的内存场景,标了“不用场景”时直接EmitSound;RESPONSE_SCENE播放.vcd;RESPONSE_SENTENCE播放 VOX 句子:game/server/ai_speech.cpp:714-749、game/server/ai_speech.cpp:974-985。CMultiplayer_Expresser所继承的CAI_ExpresserWithFollowup只是在基类分派前后处理后续响应:game/server/ai_expresserfollowup.cpp:307-316。
4. 场景实体如何发声与联网#
- 创建:
InstancedScriptedScene生成instanced_scripted_scene实体,展开$gender,保存调用方的接收者过滤器,开始播放并返回估计时长:game/server/sceneentity.cpp:4905-4961。InstancedAutoGeneratedSoundScene为单个声音生成场景,签名中没有过滤器参数:game/server/sceneentity.cpp:4970-4998、game/server/sceneentity.h:25-26。 - 加载:场景文件内容从
scenefilecache取得;预缓存实例场景时,把缓存中记录的场景声音逐个PrecacheScriptSound:game/server/sceneentity.cpp:229-240、game/server/sceneentity.cpp:5104-5121。 - 发声:
DispatchStartSpeak先以说话者为中心建CPASAttenuationFilter,若场景带接收者过滤器则取两者交集,再按场景时间补偿soundtime,以CHAN_VOICE发声:game/server/sceneentity.cpp:1724-1800。之后的声音网络路径与普通EmitSound相同(见 引擎音频管线)。 - 联网:带过滤器的场景实体用
FL_EDICT_FULLCHECK,只发送给过滤器内的客户端:game/server/sceneentity.cpp:4636-4672。客户端C_SceneEntity接收场景字符串索引、播放/暂停状态,并通过scenefilecache读取同一场景以驱动口型等表演:game/client/c_sceneentity.cpp:46-50、game/client/c_sceneentity.cpp:776-781。 - 字幕:游戏声音系统发字幕时调用定义在
sceneentity.cpp中的AttenuateCaption做距离裁剪:game/shared/SoundEmitterSystem.cpp:1052-1072。
由此得到一个由代码推出的约束:只有 RESPONSE_SCENE 会把无线电的队伍过滤器传到场景;RESPONSE_SPEAK 的两条分支都不使用过滤器,发声时只受 PAS/衰减约束(推断,game/server/ai_speech.cpp:720-737)。因此 CS:GO 的无线电规则若要保持“仅队友可听”,响应应写成场景类型;规则文件不在仓库中,无法确认实际写法。
约束与边界#
- 为何保留为独立页面:三个模块都在
csgo_partner构建组中、被 server/client 强依赖,且玩家无线电在默认 ConVar 下必经响应规则,并非可忽略的 NPC 遗留代码。 - 失败路径:
scenefilecache接口缺失会导致游戏 DLL 初始化失败;scenes.image头部损坏直接Error;找不到匹配规则时Speak返回 false,不发声(game/server/ai_speech.cpp:930-933);IsSpeechGloballySuppressed为真时所有表达器静默(game/server/ai_speech.cpp:923-926)。 - 开关:
bot_chatter_use_rr为开发者 ConVar,改为 0 时无线电与 Bot 闲聊绕过响应规则,改走用户消息(game/server/cstrike15/cs_player.cpp:8518-8523、game/server/cstrike15/bot/cs_bot_chatter.cpp:1298)。 - 证据不足:仓库不含
scripts/talker/response_rules.txt、scenes.image、.vcd或无线电/人质的 game sound 脚本,具体概念名、条件与响应类型(scene 还是 speak)无法核对;客户端对CS_UM_SendAudio/CS_UM_RawAudio的处理未追踪。 - 本页未展开:响应规则脚本语法(
ParseRule/ParseCriterion/ParseResponse)、上下文与后续响应(followup)、CChoreoScene事件类型与口型(phoneme)数据、存档恢复、HL2 系 NPC AI 行为、responserules_cli与ResponseEditor工具。