CD Code Digest Hub /CS:GO

场景与响应规则:CS:GO 的无线电、Bot 语音与人质台词

20 分钟阅读 6 个章节 1 张图

响应规则(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_Expresser
  • game/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.cppserver.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.cppserver / client 源文件场景实体与客户端表演game/server/server_base.vpc:620-622、game/client/client_base.vpc:210
scenefilecache.dllAppSystem,在 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::SpeakAudioResponseRulesBot 闲聊系统选中短语时,附加 count、place 等条件Speak( concept, criteria, filter );旧路径 SpeakAudio 改发 CS_UM_RawAudiogame/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 工具。