研究闭环:v1 三轮 28 局四死因(失衡/社交真空/交互摩擦/观光羞辱)→ 四视角分析 → synthesis S1-S9 裁定 → 实施 → 同 10 人配置复测两轮 20 局。依据 research/hunt-sim/ (synthesis.md / metrics-v2.md / report.md),设计登记 23-hunt.md §16。 服务端 hunt-room.js: - S5 协作人声包:真人座位角色接线(杀熊/三连杀/旋风斩≥3 播角色句、greet 全座位); 满编真人房呼喊 2.5s 无 bot 应答 → 邻座角色代答 help_ack;喊话预算 10s 窗 2→3 - S7 难度重构:waveCounts 4 人房 ×4/3;alignPincers 第 3-5 波齐压拍(跨座位同种类 同秒进内圈,种子确定性保持);mobYielded bot 对人类防区怪让位 2s(只约束 bot, 不拦真人避免挥空真话说谎);gradeForEx 双轴评级(S=耐久≥9 且 ≥2 人各 6 杀, 单人带飞局上限 A) 客户端 game.js: - S1 WAVE 下滑恒出击(无怪=空挥真话),菜单收窄到波间休整窗(E3 修订)—— v1 误触菜单单人单局最多 14 次清零 - S2 冷却首拦才震 + 就绪 beat 轻叮(v1 全场 666 次乱滑连震) - S3 0 杀不播 kill_band0(「你还没开张」下架;复测 P08 0杀→12杀) - S4 看门狗重定义:本波出过手一级语音不触发、语音闲置每局 ≤1、一级降纯震 - S5③ help_call/help_ack 永不超时丢弃 - S6 后半平面 loop 低通 750Hz 第二线索(N/S 判读 63/69%→75/81%) - S8 教学作答步(T1-T3)下滑不再误跳 - S9 状态链按变化播(「固若金汤」复读归零) 测试同步:hunt-sim A1/A2/A12 断言更新(真人 greet/波次缩放/预算 3)、 playtest Block 3 按 S1 新语义改写(战斗中下滑=空挥 + 波间菜单) 回归:hunt-sim 16/16 + playtest 213 + guide 132 + engine-voice 19 全绿; balance-scan:挂机局 5/8 落 C/失守带(v1 恒满血)、optimal 单人带飞封顶 A 遗留:营地零损耗未破(18/20 局余10)——v3 头号,参数方案 report.md §5
48 KiB
23 · 《围猎》完整设计(hunt / Night Hunt)
给开发者的实现要点摘要
- 2-4 人实时联机纯音频协作守卫,项目"纯音频 MMORPG"路线的第一阶段最小验证切片(§1.3)。本作只验证三件事:实时同步(零依赖 WebSocket + 服务端权威)、音频信道拥挤控制(§8,全文重点)、空间音频协作(方位=pan+防区分工)。其余一切从简。
- 圆形猎场,猎人位于营地中心。野兽从 8 罗盘方位边缘逼近(8-12s),四向滑=出击(滑向映射 90° 弧,击杀弧内内圈最近一只,冷却 2.5s/人);突入中心 = 共享营地耐久 -1(巨兽 -2,初始 10);单击=呼喊支援(队友收到你防区方位的空间化喊话 + warning 震,bot 补位);双击=旋风斩(清内圈全部,冷却 8s;冷却中双击=重听威胁播报,宪法双击语义由此保留)。上滑=状态链,下滑=菜单(不暂停,联机实时豁免 E3)。
- 服务端权威:怪物生成/移动/命中/冷却/评级全在
web/server/裁决,客户端只发意图(join/act/leave)。bot 由服务器驱动,与真人同一套事件协议。房间 3-4 座,开局空位由 bot 补至 ≥3 猎人;断线 30s 宽限重连回槽,超时转 bot。- 协议全文 §6(两条传输消息 + 三类服务端消息,JSON 字段表逐条给出);零依赖 WS 握手与帧解析规范 §6.1(Node 原生 http upgrade + 手写帧,不引 npm)。
- 拥挤控制硬规则 R1-R8(§8.1):旁白单句队列(voice 层)、bot 喊话 sfx 层同刻 ≤1、怪物移动 loop ≤3 路(最近 3 只)、非语音信息全部 <1s 短音效、听觉视野裁剪只报内圈(<4s 抵达)。
- 引擎改动唯一一处(后向兼容):
engine/audio.js的 sfx/voice 播放各加可选opts.pan(-1..1,StereoPannerNode 接入对应总线),缺省行为逐字节不变(§9)。怪物移动 loop 走游戏层句柄(abyss_startLoop先例 + 游戏层自建 panner),不占引擎改动面。- 台词表 23-hunt-voice-ids.md:旁白/系统音 60 句 ×2 性别(male=Uncle_Fu / female=Serena,预算打满)+ bot 喊话 3 角色 ×8 句 = 24 条固定单音色(阿岩=Uncle_Fu / 小筝=Serena / 老周=Eric;因走 sfx 层带 pan,落盘
audio/sfx/,异例说明见台词表头注)+ 键盘变体 11 对。数字信息全分档整句,无运行时拼接。- 设计裁决与对任务书核心案的修正记录(双击矛盾、出击冷却、防区模型、房码 UX 范围等 9 条)见 §15,实施前必读。断言清单住 13-playtest-criteria.md §24,本文 §13 只留摘要与指针。
1. 概述
1.1 一句话
夜幕降临,野兽从四面八方围上来——你和队友背背守住营地篝火,听声音的方向滑过去就是一刀,撑过五波,天亮就赢。
1.2 题材与纯音频相性
守夜围猎是"方位即生死"的题材:怪物唯一的接近预告就是它逼近的声像。视觉游戏里这是小地图+tower defense;纯音频里,声像本身就是小地图——题材与媒介互为 justification,与 dungeon「声音就是你的眼睛」同源,但本作是实时+协作,不是回合制探索。
1.3 定位:纯音频 MMORPG 路线 · 第一阶段最小验证切片
本项目长线方向是纯音频 MMORPG(持久世界、多玩家同图协作)。《围猎》是该路线的第一个可玩切片,刻意把范围切到最小,只验证三个技术-设计命题:
| 验证点 | 本文档落点 | 通过信号 |
|---|---|---|
| 实时同步:零依赖 WS、服务端权威战斗、事件流+快照兜底、断线重连 | §6 协议 | 局域网 2-4 端同局,事件一致,断线 30s 内重连无感回槽 |
| 音频信道拥挤控制:多源并发(旁白/bot 喊话/怪物 loop/定位音效)不糊 | §8 R1-R8 | 最坏并发 7 声流下威胁可辨、警报不丢(§8.4 验证口径) |
| 空间音频协作:靠 pan 的方位分工与支援是否成立 | §3 防区 + §7 bot 补位 | 玩家能用呼喊-补位闭环分担压力,无需任何视觉 |
明确不做(切片纪律):无持久世界、无跨房聊天、无跨玩家排行榜("排行榜"= 进度 API 的个人最佳桶 + 大厅卡片 summary,§12)、无房码音频录入 UX(协议保留,客户端 v1 只做快速匹配,§6.4)、无内购/社交。这些列 roadmap,不在本文档。
1.4 单局
5 波为一局,约 5 分钟(含 lobby ≈ 5.5 分钟;首局含教学 ≈ 6.5 分钟)。营地耐久初始 10、共享;耐久归零 = 营地失守(立即终局,突围结算,不入榜);撑过 5 波 = 胜利结算(评级 S~C + 个人击杀分档 + 排行榜写入)。
1.5 与现有 12 游戏的关系
实时方位感承接 space(呼啸来袭+同向反应),但改为协作分工;状态分档链承接 dungeon(上滑多段链);威胁裁剪承接 dungeon 脚步距离档音量规则并升级为显式"听觉视野"(§8.2);bot 喊话平铺资产承接 rpg/radio/detective NPC 先例,但首次走 sfx 层空间化(§8 R2);结算链死窗沿用 dungeon H1 先例(800ms 手势死窗);看门狗/向导层全照 00 §6。两个"第一":第一个联机游戏、第一个用到运行时 pan(引擎 opts.pan,§9)。
1.6 本作三大特有风险(均有显式对策)
- 多源并发糊耳(5 波峰值:旁白+喊话+3 路 loop+定位音效)→ §8 规则表 + §8.4 验证断言。
- 实时联机与纯音频教学的根本矛盾(不能暂停真人局教学)→ 首局教学房(§5):单人+bot、教学波、学期零惩罚,教完同场接续第 1 波。
- 协作流于形式(都能打所有方向=不需要队友)→ 出击冷却 2.5s 限单人吞吐 + 软防区 + bot 补位闭环(§3.3、§7)。
2. 手势映射与豁免登记
2.1 战斗态(WAVE,含教学局 TEACH 的战斗段)
| 手势 | 作用 | 反馈 |
|---|---|---|
| 四向滑(上/下/左/右) | 出击:滑向映射 90° 弧(上→{NW,N,NE}、右→{NE,E,SE}、下→{SE,S,SW}、左→{SW,W,NW}),击杀弧内内圈(eta≤4s)最近一只;冷却 2.5s | 引擎 swipe 震 + atk_whoosh;命中 kill_*+pickup 震;挥空 atk_swish+error 震 |
| 单击 | 呼喊支援:全队友收到你防区方位的空间化喊话 + warning 震;bot 朝你侧补位 10s(§7.3);节流 5s/人 | 引擎 confirm 震 + 本机播自己座位角色的 help_call(中央) |
| 双击 | 旋风斩就绪 → 放出:清全场内圈(eta≤4s)全部怪物,冷却 8s;冷却中 → 重听当前威胁播报(threat_*,宪法"双击=重听"落地) | 放出:success 震 + skill_spin;冷却中:引擎 confirm 震 + threat_* |
| 上滑 | 状态链:营地耐久档 → 波次档 → 在场猎人数 → 自己击杀档(4 段链播 queue:true) | hp_band_* → st_wave_* → team_* → kill_band_* |
| 下滑 | 模态菜单(不暂停,豁免 E3,§2.4) | menu earcon |
| 长按 | 非玩法输入:引擎旁白快进默认行为保留 | — |
- 出击冷却 2.5s 内四向滑 → 仅 error 震(无语音、不发 act;dungeon 脉冲冷却先例)。
- 单击呼喊是非即时操作,300ms tap 延迟无碍(escape 单击=检查先例)。
- 旋风斩就绪时刻播
skill_ready(queue:true,绝不顶句;每轮冷却完成只播一次)。 - 事件震动总表:命中 pickup / 挥空 error / 旋风斩放出 success / 突入 hit / 收到呼喊 warning / 波次开始(ev wave)heartbeat / 耐久首次 ≤2 danger(每局 1 次,配 danger_low)。手势识别震动(swipe/confirm)由引擎内置承担,游戏层不重复(00 §2.3)。
2.2 标题/匹配/大厅等待(TITLE / MATCHING / LOBBY)
| 手势 | TITLE | MATCHING | LOBBY |
|---|---|---|---|
| 单击 | 快速匹配(首局=教学房) | —(无效,error 震) | 立即开局(act start) |
| 双击 | 重听开场/提示 | 重听 | 重听 room_wait |
| 上滑 | 个人最佳播报(有档时:best 评级+击杀档) | error 震 | 在场猎人播报(team_*) |
| 下滑 | 菜单(帮助/回大厅) | 菜单(含"取消匹配") | 菜单(退出房间回大厅) |
| 左右滑 | error 震 | error 震 | error 震 |
2.3 结算态(END)
单击=再来一局(重新进匹配);双击=重听结算链;上滑=重听个人最佳段;下滑=菜单(回大厅)。结算链进入时盖 800ms 手势死窗(dungeon H1 先例:吞掉致命事件前后在途的 ghost 手势,死窗后手势照常)。
2.4 模态菜单(豁免 E3:联机实时不暂停)
下滑呼出 → 单击循环 → 双击确认 → 上滑=帮助(播 help_main)→ 下滑=关闭(00 §5.2 交互骨架不变)。菜单打开期间战斗照常进行(怪物照走、突入照扣)——联机不可单方暂停,这是与 00 §5.2"菜单打开时暂停计时类玩法"的显式豁免。菜单项固定 3 项:sys_menu_resume(回到猎场)/ sys_menu_help(重新听帮助)/ sys_menu_home(退出房间回大厅 = 发 leave,槽位按 §6.6 处理)。无"重新开始"(联机房局不可单方重开)。菜单打开即播 sys_menu + 首项;菜单内另播 settings_hint(设置只读、回大厅修改,00 §1.2)。
2.5 豁免登记(显式)
| # | 豁免条目 | 理由(先例) |
|---|---|---|
| E1 | 四向滑=出击(非选择/移动) | 以滑动为核心玩法(space 四向滑=闪避同款豁免);三件套(上滑/下滑/双击重听)全部保留 |
| E2 | 双击优先=旋风斩,仅冷却中=重听威胁 | 旋风斩是即时技,必须绑零延迟双击(00 §5.4:即时判定绑滑动或双击);冷却中重听保留宪法语义 |
| E3 | 菜单打开不暂停 | 联机实时,暂停不可能;菜单项压到 3 项、打开即播提示,把"逛菜单被咬"的代价做成可感知风险 |
| E4 | 单击=呼喊(非确认/继续) | 非即时操作,300ms 延迟无碍(escape 单击=检查先例) |
教学(TEACH)逐条讲清 E1/E2/E4(§5);不占用长按(快进保留,无需 rateBoost 豁免);不开 immediateTap。
3. 世界模型与数值
3.1 猎场与怪物
- 圆形猎场:猎人位于圆心营地,野兽从边缘(8 罗盘方位 N/NE/E/SE/S/SW/W/NW)沿直线匀速逼近圆心。逼近全程 8-12s(按波次表)。
- 怪物状态由服务器持有:
{ id, mkind, dir, eta }——eta = 距突入的秒数(服务端单调时钟)。客户端不推算位置,只按事件流+快照渲染(§6.7)。 - 怪物 2 型:
| 型 | mkind | 逼近 | 突入扣耐久 | 声音 |
|---|---|---|---|---|
| 饿狼 | wolf |
8-10s | -1 | howl_wolf 生成呼啸 + loop_wolf 移动 |
| 暴熊 | bear |
11-12s | -2 | howl_bear 低吼 + loop_bear 重脚步 |
- 距离分档(听觉视野,§8.2):外圈 eta>6s(仅生成呼啸一声);中圈 4-6s(移动 loop 可闻,低音量);内圈 eta≤4s(可击杀的近战窗,loop 升档,计入威胁播报)。
3.2 出击与技能(服务端裁决,§6.5)
- 出击:
act attack dir→ 弧 = dir±45°(3 个罗盘向)→ 弧内活着且 eta≤4.0 的怪中取 eta 最小者击杀;无 →miss。冷却 2.5s/座(服务器计时,违规err cooldown)。 - 旋风斩:
act skill→ 清除全场 eta≤4.0 全部怪(不分方向)。冷却 8s/座(违规err skill_cd)。 - 呼喊:
act callout→ 广播ev callout;bot 补位(§7.3)。节流 5s/座。 - 击杀归属 = act 来源槽位(结算个人击杀账)。
3.3 防区(软分工)
- 每座位固定主防区(连续罗盘弧)与防区中心(喊话 pan 用):
| 猎人数 | 座位 0 | 座位 1 | 座位 2 | 座位 3 |
|---|---|---|---|---|
| 3 | [N,NE,E],中心 NE | [SE,S,SW],中心 S | [W,NW],中心 WNW | — |
| 4 | [N,NE],中心 NNE | [E,SE],中心 ESE | [S,SW],中心 SSW | [W,NW],中心 WNW |
- 防区不是硬限制:任何玩家可打任何方向(弧映射见 §2.1)。防区用于:① bot 目标选择偏置(§7.2);② 呼喊/喊话的 pan 方位来源;③ 刷怪剧本的方位均衡(§3.5)。
- 座位角色(voice 身份):座 0=阿岩(yan)、座 1=小筝(zheng)、座 2=老周(zhou);座 3 仅 4 人局存在,无专属角色(呼喊=
call_whistle哨音,见 §15 裁决 6)。真人"扮演"座位角色:你的呼喊用你座位角色的预录声,断线转 bot 后声音身份无缝延续。
3.4 波次表(5 波,服务器剧本)
| 波 | 狼 | 熊 | 狼逼近 s | 熊逼近 s | 刷怪间隔 s | 波时长约 |
|---|---|---|---|---|---|---|
| 1 | 4 | 0 | 10.0 | — | 3.0 | ~21s |
| 2 | 6 | 0 | 9.5 | — | 2.5 | ~24s |
| 3 | 7 | 1 | 9.0 | 12.0 | 2.2 | ~29s |
| 4 | 8 | 1 | 8.5 | 11.5 | 1.8 | ~28s |
| 5 | 10 | 2 | 8.0 | 11.0 | 1.5 | ~30s |
- 波间休息 8s(
wave_clear→ 休整 → 号角 + 下一波播报)。波内怪物匀速逼近不止步,一波必在有限时间内出清(杀或漏),无需超时兜底。 - 每波开始播对应整句
wave_band1…wave_band5(台词表 C 组;先号角horn_wave后播报)。 - 全程怪物总数 39,耐久预算 10(熊 -2)。平衡目标:3 bot 水准下 solo 余耐久 6-9(评级 A/B),真人协作越好评级越高。
- 局时长核算:战斗 ≈ 132s + 休息 32s + lobby ≤12s ≈ 3 分钟战斗 + 首尾 ≈ 5 分钟 ✓(任务书"约 5 分钟")。
3.5 刷怪方位与种子
- 房间 seed 开局掷一次(
joined.seed),mulberry32 派生:rng(wave) = mulberry32((seed ^ (wave * 0x9E3779B9)) >>> 0)。 - 每波方位分配:把本波怪物按座位数轮转发牌到各座位防区内的随机方位(同一方位同刻最多 2 只在场,防叠声);同 seed → 同 spawn 剧本(服务器单测可复现断言)。
- 服务端战斗模拟 tick = 100ms;快照 2Hz(§6.3)。
3.6 耐久与评级
| 结算时剩余耐久 | 评级 |
|---|---|
| ≥9 | S |
| 7-8 | A |
| 4-6 | B |
| 1-3 | C |
| 0(波内归零) | 失守(立即终局,无评级,不入榜) |
评级由服务器算好在 ev end.grade 下发(客户端不算分,防双源)。
终局播报链:胜利 = end_win → grade_s/a/b/c → kill_band_*(自己击杀档)→ end_rank → end_again;失守 = end_lose → kill_band_* → end_again(失守不播评级、不播 end_rank、不写 best,§12)。进 END 盖 800ms 手势死窗(§2.3)。
4. 状态机(客户端)
4.1 状态列表
TITLE / MATCHING / LOBBY / TEACH / WAVE / MENU(overlay) / END / LOST。MENU 为挂起 overlay(记录返回态,不暂停战斗);LOST = 连接不可恢复(回大厅出口)。
4.2 转移图
TITLE ──单击──> MATCHING ──joined──> LOBBY ──act start / 12s 到 / 满4人──> (ev wave 1)
│ 首局(tutDone=false) → join teach=true → joined.state.phase='teach' → TEACH
MATCHING ──连接失败──> LOST(net_lost→net_gone) ──单击──> TITLE
TEACH ──教学完成 / 下滑跳过──> WAVE(wave 1)
WAVE ──ev end──> END ──单击──> MATCHING(再来一局)
WAVE/LOBBY ──下滑──> MENU ──返回──> 原态(战斗不暂停)
WAVE ──WS 断开──> 本地静默重连 ≤30s(net_lost) ──joined(resume)──> WAVE(net_back)
└──超时/房已灭──> LOST(net_gone)
4.3 每状态手势映射总表
| 手势 | TITLE | MATCHING | LOBBY | TEACH | WAVE | MENU | END |
|---|---|---|---|---|---|---|---|
| 四向滑 | error | error | error | 步骤作答/出击 | 出击 | error | error |
| 单击 | 快速匹配 | error | 立即开局 | 步骤作答/呼喊 | 呼喊 | 循环切项 | 再来一局 |
| 双击 | 重听 | 重听 | 重听 | 步骤作答/旋风斩 | 旋风斩/重听威胁 | 确认执行 | 重听结算链 |
| 上滑 | 最佳播报 | error | team_* | 状态链(T4 起) | 状态链 | 帮助 | 重听最佳段 |
| 下滑 | 菜单 | 菜单(取消匹配) | 菜单 | 跳过教学 | 菜单(不暂停) | 关闭 | 菜单 |
| 长按 | 快进 | 快进 | 快进 | 快进 | 快进(非玩法) | — | 快进 |
无效手势一律 error 震;full 档语音纠错 gd_invalid(onceKey 同局 1 次)。看门狗语音 idle-only。
5. 教学编排(TEACH,首局教学房)
矛盾与解法:实时联机局不能暂停教学 → 首次进入(进度 tutDone 为假)走教学房:单人 + 2 bot(教学段 bot AI 挂起)、先行一波教学波(3 狼,逼近 12s,学期零惩罚:教学波突入不扣耐久),教学完成或下滑跳过后,同一房间直接接续第 1 波(bot 激活)。教学即真联机局——这本身就是切片验证点(首局就跑了完整的 WS/事件流路径)。
| 步 | 旁白 | 等待动作 | 通过/反馈 |
|---|---|---|---|
| T0 | intro_1 + intro_2 + tut_rule |
单击 | confirm 震 |
| T1 | tut_attack(服务器放教学狼 1,方位 N) | 朝声音方向滑 | kill → tut_kill_ok;挥空/错向 → gd_idle 重教(≤2 次后自动放过该狼) |
| T2 | tut_callout | 单击呼喊 | callout ev → bot npc_*_help_ack 响应演示(教学 bot 必答)→ tut_callout_ok(呼喊-响应闭环点明) |
| T3 | tut_skill(服务器放教学狼 2、3,贴近 eta 5s) | 双击旋风斩 | cleared=2 → tut_skill_cd(讲冷却与冷却中双击=重听) |
| T4 | tut_status | 上滑(状态链) | 链播完 → tut_breach → tut_done → 进 WAVE 1 |
| 任意 | — | 下滑 | tut_skip → 进 WAVE 1(tutDone 写入进度) |
- 教学波零惩罚:教学狼突入不扣耐久(明说规则前不漏真话);T1 首输入 ≈ t+20s(<60s ✓);首个正反馈 = T1 首杀 ≈ t+40s(≤3min ✓)。
- 看门狗:每步 8s nudge 纯震(教学句不重播),T1/T3 卡 30s 自动放过当前狼推进(space 先例),不卡流程。
- 二周目起 TITLE 单击直达快速匹配(tutDone 存进度,§12)。
6. 多人协议(两个开发 agent 的实现契约)
6.1 传输:零依赖 WebSocket(server.js 增补)
- 端点:
GET /ws/hunt,与静态/REST 同一 HTTP 服务器、同一端口。http.createServer的'upgrade'事件接管:校验Upgrade: websocket、Sec-WebSocket-Key,回101+Sec-WebSocket-Accept = base64(sha1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))(node:crypto,无 npm)。 - 帧解析(手写,约 100 行,建议独立
web/server/ws.js便于单测):客户端→服务器帧必须带 mask(不带 → close 1002);支持 opcode 0(continuation 拼装)/1(text)/8(close)/9(ping→回 pong)/10(pong);payload 7/16/64 位长度全支持;单条消息上限 4KB(超限 close 1009);二进制帧 close 1003。服务器→客户端帧不 mask。 - 活性:服务器每 15s ping,45s 无 pong 判死(等同断线走 §6.6);每连接应用消息速率帽 20 条/s(超限 err
rate,再犯 close)。 - 发送背压:每连接一个待发队列;
snap可丢(新顶旧),ev/joined/err不丢;队列 >200 → close 1013。 - 每消息 = 1 条 text 帧 = 1 个 JSON 对象,必有
t字段。协议版本ver: 1(join 携带,不符 errbad_msg)。
6.2 客户端→服务器消息(3 条)
join(连接后首条,5s 内不发则 close):
| 字段 | 类型 | 含义 |
|---|---|---|
t |
"join" |
— |
ver |
number | 协议版本,恒 1 |
room |
string | "quick" = 快速匹配;或 4 位房码(大写无歧义字符 23456789ABCDEFGHJKMNPQRSTUVWXYZ) |
name |
string | 显示名(1-24 字符,取自档案;服务端截断兜底) |
teach |
bool? | true = 请求教学房(首局;服务端强制单人房+教学波) |
resume |
string? | 重连凭证(joined.resume 原值);携带时按 §6.6 回槽 |
act(对局意图;服务器按到达序裁决):
| 字段 | 类型 | 含义 |
|---|---|---|
t |
"act" |
— |
kind |
"attack" | "callout" | "skill" | "start" |
start 仅 LOBBY(立即开局) |
dir |
"N"|"NE"|"E"|"SE"|"S"|"SW"|"W"|"NW" |
attack 必填(客户端四向滑只发 N/E/S/W 四基向,弧解析在服务端;bot 可发 8 向) |
seq |
number | 客户端单调递增(去重/乱序检测;乱序不拒,仅日志) |
at |
number | 客户端发出时刻 ms(仅诊断用,裁决以服务端到达序为准) |
leave:{ "t": "leave" } —— 主动退出(菜单"回大厅"),槽位立即按 §6.6 处理(无 30s 宽限)。
6.3 服务器→客户端消息(joined / ev / snap / err)
joined(join 受理;resume 重连亦发):
| 字段 | 类型 | 含义 |
|---|---|---|
t |
"joined" |
— |
room |
string | 实际房码 |
slot |
number | 你的座位号 0-3 |
resume |
string | 重连凭证(16 hex,每槽位签发一次,连接存活期内不变) |
char |
"yan"|"zheng"|"zhou"|null |
座位角色(座 3 恒 null) |
sector |
string[] | 你的主防区方位集(§3.3) |
seed |
number | 房间种子(复现用) |
state |
object | { phase: "lobby"|"teach"|"wave"|"end", wave: number, dur: number } |
roster |
array | 全花名册:[{ slot, name, bot, char, sector }](bot=true 时 name=角色名) |
ev(事件流,即时下发;ts = 房间单调时钟 ms(开局为 0)):
| kind | 附加字段 | 含义 |
|---|---|---|
spawn |
id, mkind("wolf"|"bear"), dir, eta |
怪物生成(eta=逼近全程秒数) |
kill |
id, slot, dir, mkind |
出击命中击杀(归属 slot) |
miss |
slot, dir |
挥空(弧内无可打怪) |
skill |
slot, cleared: number[] |
旋风斩放出,cleared=被清怪 id 表 |
breach |
id, mkind, dir, dur |
突入:耐久新值 dur(wolf -1 / bear -2 已结算) |
wave |
wave |
第 wave 波开始(1-5) |
waveclear |
wave |
第 wave 波出清 |
callout |
slot |
求援呼喊(真人单击或 bot 压力呼救;接收端播该座角色 help_call + warning 震) |
botline |
slot, line |
bot 喊话(line ∈ greet/help_ack/cover/kill/breach/skill/wave;客户端按 char+line 播 sfx/npc_<char>_<line>) |
roster |
roster |
花名册变更(加入/离开/断线/转 bot;形状同 joined.roster) |
end |
win, dur, grade("S"|"A"|"B"|"C"|null), kills |
终局;kills={ "0": 5, ... } 按 slot 的个人击杀 |
snap(2Hz 兜底快照):
| 字段 | 类型 | 含义 |
|---|---|---|
t |
"snap" |
— |
ts |
number | 房间单调时钟 ms |
phase / wave / dur |
— | 同 joined.state |
mobs |
array | 在场怪物全量 [{ id, mkind, dir, eta }](eta 为 ts 时刻值) |
slots |
array | [{ slot, alive, bot, cd, skillCd }](cd/skillCd=剩余冷却秒,重连恢复冷却 UI/判定用) |
err:{ "t": "err", "code": "cooldown"|"skill_cd"|"bad_dir"|"not_ingame"|"room_full"|"room_gone"|"bad_msg"|"rate", "msg": "人话描述" }(cooldown/skill_cd 为正常违规拒绝,客户端不应惊扰玩家——客户端本地已先拦)。
6.4 房间管理与匹配
- 快速匹配:找
phase='lobby'且有空人类座的房 → 进;没有 → 建房(房码 4 位随机)。客户端 v1 只发room:"quick";显式房码 join 协议完整、服务端实现并单测,客户端录入 UX 列 roadmap(§15 裁决 5)。 - LOBBY 上限 12s:到点、任一人类
act start、或满 4 人 → 开局(播room_start,随后ev wave 1+wave_band1)。开局时空位由 bot 补至 max(3, 人类数) 猎人:1 人→2 bot、2 人→1 bot、3 人→0 bot(3 座)、4 人→0 bot(4 座)。第 4 座仅 4 人局启用。 - join 只在 lobby 受理;对局中 join → err
room_full(v1 不做中途加入)。 - 教学房:join
teach:true且本地 tutDone=false → 强制单人房(§5 剧本);教学波完直接续 WAVE 1。
6.5 服务端权威战斗(hunt-room.js,建议独立模块便于单测)
- 房间结构:
{ seed, phase, slots[≤4], mobs: Map, wave, dur, tickTimer, snapTimer };tick 100ms:eta 递减、突入结算、波剧本推进;snap 500ms。 - 裁决规则(§3.2 数值的权威实现):弧映射、eta≤4.0 近战窗、冷却 2.5/8s、呼喊节流 5s——全部只在服务端生效;客户端可做本地预扣(冷却 UI),但以 err/ev 为准。
- bot = 特殊 slot(bot:true,无连接):同一套裁决管线,由 §7 AI 产生 act。
- 时钟:ts 为房间单调 ms。客户端在 joined/snap 到达时记
offset = ts - performance.now(),渲染用nowServer = performance.now() + offset;局域网无需 RTT 补偿(逼近时长 8-12s,误差淹没在听觉分辨率内)。
6.6 断线与重连
- 连接断开(或 45s 无 pong)→ 槽位标记断线(roster 广播 alive=false),30s 宽限:槽位静默(无 act 产出,猎人"愣在原地"——这是真实的协作代价)。
- 宽限内带
resume重连 → 回槽:重发 joined(同 slot/seed/char)+ 紧随 snap 全量,客户端无缝续渲染(net_back)。 - 宽限超时:剩余人类 ≥3 → 槽位移除(roster);否则槽位转 bot(roster,bot=true,char 沿用座角色,补位 bot 播
npc_*_cover,其余客户端播net_to_bot)。转 bot 不可逆(该局内)。 - 最后一个人类断开:房间保留至其 30s 宽限耗尽后销毁(无人类即灭,bot 不续命)。房灭后迟到 resume → err
room_gone(客户端 net_gone → LOST)。 - 终局 end 后房间保留 30s(各端写排行榜)随后 close 1000。
6.7 客户端渲染模型(client agent 契约)
- 本地怪表 = ev 增删 + snap 校正:每 snap 对账——本地多出的 id 删、缺的按 snap 补、eta 偏差 >0.5s 校正。事件流为主、快照兜底,二者矛盾以 snap 为准。
- 每 500ms 重估渲染集:eta>6s 静默;≤6s 按 eta 升序取前 3 只起 loop(§8 R3);loop 音量按档(6-4s:0.30 / 4-2.5s:0.55 / <2.5s:0.80);pan 按方位角映射(§8.3)。
- 断网期间:本地怪表按最后已知 eta 继续衰减渲染(怪物照爬——戏剧张力保留),恢复后以 snap 校正。
7. Bot 设计(服务端驱动)
7.1 角色与声音资产
| 座位 | 角色 | char | speaker | instruct 常量 | 性格 |
|---|---|---|---|---|---|
| 0 | 阿岩 | yan | Uncle_Fu | 憨厚壮实的中年男声,猎户,话少踏实 |
憨厚 |
| 1 | 小筝 | zheng | Serena | 轻快爽利的年轻女声,猎手,干脆利落 |
轻快 |
| 2 | 老周 | zhou | Eric | 沙哑沉稳的老年男声,老猎手,带点调侃 |
沙哑 |
每角色 8 句固定短句(事件触发,全集见台词表 N 组):greet / help_call / help_ack / cover / kill / breach / skill / wave。固定单音色平铺、不做双性别(rpg NPC 先例);因经 sfx 层带 pan 播放,资产落 audio/sfx/npc_<char>_<line>.{ogg,m4a}(§15 裁决 7)。
7.2 战斗 AI(tick 500ms,确定性)
- 出击:冷却就绪且存在目标方向时出手。方向压力
P(d) = Σ(1/eta)(d 内活怪,仅计 eta≤4);主防区方向 ×1.5 偏置;取 max P 方向发act attack(dir=该方向精确罗盘向)。拟人反应带:只在最优目标 eta ≤ 2.0 + rng()×1.0 时才出手,且每次出手决策带 80% 空转概率(rng 走房间 seed,复现可断言),避免" eta 一进 4 秒就秒"的机械感(数值为 §15 裁决 10 调参后口径)。 - 旋风斩:全场 eta≤2.5 的怪 ≥3 只且技能就绪,且 60% 概率想起 →
act skill(裁决 10)。 - 呼救:主防区压力 ≥3(≥3 只 eta≤6)且 20s 节流 →
act callout。 - 难度校准:bot 不失误(挥空不存在——只打有目标的弧),但受 2.5s 冷却、反应带与空转概率上限;3 bot 局实测评级 A/B 为主(§3.4 平衡目标,多种子扫描见 §15 裁决 10),给真人留出"比 bot 强"的空间。
7.3 补位(呼喊响应)
收到 ev callout(真人或 bot 发出)后:空闲 bot(当前 500ms 无待发 act 者,多取座位号小者)播 help_ack,且其后 10s 内目标选择偏置改为呼喊者防区方向 ×2.0(覆盖主防区偏置)——即"朝你侧补位"。补位 bot 首次在新防区击杀后再无怪时播 cover。呼喊本身不改变 bot 的冷却/反应带(公平)。
7.4 喊话调度(与 §8 R2 联动的服务端纪律)
- botline 触发源:greet(开局补位完成,逐只间隔 ≥1.5s)/ cover(转 bot 接管、补位落定)/ kill(杀熊或三连杀,15s 节流)/ breach(本防区漏怪,20s 节流)/ skill(放旋风斩)/ wave(第 2 波起,轮转一只 bot)。
- 服务端对 botline/callout 全局节流:10s 窗口 ≤2 条,超出丢弃(喊话是氛围,不是信息主通道);help_call/help_ack 优先级最高(呼救语义不可被氛围句顶掉)。
8. 音频信道拥挤控制(本文档重点章节)
8.1 硬规则表 R1-R8
| # | 规则 | 数值 |
|---|---|---|
| R1 | 旁白/系统音单句队列(引擎 voice 层既有):同刻 1 句,链播 onComplete 串行;警报类(breach/breach_heavy/danger_low)可顶掉状态/播报类(上滑链、skill_ready),教学/结算链不可顶 | 同刻 voice=1 |
| R2 | bot 喊话走 sfx 层(带 pan,与旁白并发):同刻在播 ≤1 条;待播队列深 ≤1(新到顶掉未播的过期句);旁白在播时延迟投递 ≤2s,超时丢弃;服务端全局 10s ≤2 条(§7.4) | 同刻喊话=1 |
| R3 | 怪物移动 loop ≤3 路:只渲染 eta 最小的 3 只(500ms 重估),其余静默;loop 经游戏层句柄 + 自建 StereoPannerNode(abyss _startLoop 先例,不占引擎改动面) |
同刻 loop≤3 |
| R4 | 定位 one-shot ≤2 并发(howl/击杀/突入/旋风斩):同类间隔 ≥0.15s;波次生成潮的呼啸队列深 ≤2,超出丢弃(怪近后 loop 已接管信息,呼啸可弃) | 同刻 one-shot≤2 |
| R5 | 非语音信息全部 <1s 短音效:号角 0.9s、join_chime 0.4s、call_whistle 0.6s、kill 0.5s 量级;语音通道只给人声 | — |
| R6 | 听觉视野裁剪(§8.2):威胁播报只计内圈(eta≤4);状态链不报具体方位(方位由 loop pan 承载,语音永不逐只报方位——8 向 × 距离档的语音枚举被明确禁止) | — |
| R7 | 电平分层:voice 层播时 BGM 自动 duck 20%(引擎既有);bot 喊话(sfx 层)不触发 duck → 游戏层统一 vol 0.55 压一档,防压旁白;loop 音量档上限 0.80 | — |
| R8 | 最坏并发核算:旁白 1 + 喊话 1 + loop 3 + one-shot 2 = 7 声流;频带分离(loop 低频 100-300Hz / 人声 300Hz-3kHz / howl 中高 1-3kHz / BGM 垫底),引擎限幅器兜底。峰值场景(第 5 波 + 警报 + 呼喊)不得丢警报、不得叠爆 | ≤7 声流 |
8.2 听觉视野裁剪规则
8 方位 × 3 距离档:
| 档 | eta | 可听内容 | 可被打 |
|---|---|---|---|
| 外圈 | >6s | 仅生成呼啸一声(one-shot,pan+距离音量 0.4) | 否 |
| 中圈 | 4-6s | 移动 loop(vol 0.30;受 R3 路数上限约束) | 否 |
| 内圈 | ≤4s | loop 升档(0.55/0.80);计入 threat_* 威胁播报与 danger 评估 | 是(近战窗) |
- threat 口径:
threat_none(内圈 0 只)/threat_few(1-2 只)/threat_many(≥3 只)——只报数量档,不报方位;方位信息永远由 loop/呼啸的 pan 实时承载(这本身就是切片要验证的"空间音频协作"命题)。 - 播报裁剪:状态链/威胁播报里禁止出现逐方位枚举句;台词表不含此类 id(结构性杜绝)。
8.3 方位 → pan 映射(全作统一)
方位角 az(N=0°、E=90°、S=180°、W=270°):pan = sin(az)(N/S=0、NE/SE=±0.7、E/W=±1、NW/SW=∓0.7)。前/后歧义(StereoPanner 不能分前后):后半平面(90°<az<270°)的 loop/呼啸音量 ×0.6("身后发闷",dungeon"背后=中央减半"先例);教学 tut_rule 讲明此约定。呼喊/喊话的 pan 取座位防区中心方位(§3.3)。
8.4 验证口径(harness/测试 agent 记录点)
- 逐事件记录:任一时刻 voice 在播句数、sfx 层人声句数、loop 路数、one-shot 并发数——断言不超 R1-R4 上限。
- 警报可达性:第 5 波峰值剧本下,breach/danger_low/wave 播报零丢失(可被顶掉的状态类句不计)。
- 实机听测 checklist(roadmap 真机轮):戴耳机分辨"左前狼 + 正右熊 + 旁白警报"三源。
9. 引擎改动(唯一一处,后向兼容)
web/engine/audio.js:
_playSfx(id, { volume = 1, pan } = {}):typeof pan === 'number' && isFinite(pan)且pan !== 0时,clamp 到 [-1,1],链路改为source → gain → StereoPannerNode(pan) → sfxBus;pan 缺省或 0 → 原路径逐字节不变(不建 panner 节点)。ctx.createStereoPanner不存在(老 Safari)→ 静默忽略 pan(不炸、不报错音)。_playVoice同款:opts.pan 存在时把 panner 插在 voice gain 与 voiceBus 之间;缺省不变。(本作旁白不使用 voice pan——对称能力一次给足,后续游戏可用;见 §15 裁决 7。)- 回归断言(harness 增补):全部既有游戏 play() 调用(无 pan)行为不变;pan=0 与缺省等价;pan=-1/1 时 panner 值正确。
本作使用面:bot 喊话、生成呼啸、击杀/突入/技能音全部 one-shot 带 pan;怪物 loop 走游戏层句柄(§8 R3)。
10. 音频三层资产
10.1 BGM(2 首,Mureka,-20 LUFS,ogg+m4a,90s 无缝循环)
| id | 状态映射 | prompt 草稿(英文) |
|---|---|---|
bgm/menu |
TITLE / MATCHING / LOBBY / END(结算 vol 0.5) | "Night camp ambient pad, distant crickets and low wind, warm fireplace crackle subtle, calm anticipation, no vocals, seamless loop" |
bgm/hunt |
TEACH / WAVE | "Tense night hunt drums, low tribal percussion 100 bpm, ominous strings pulses, cold wind, no vocals, seamless loop" |
切波不改 BGM(波次张力靠号角+怪密度);失守/胜利走引擎 crossfade 到 menu。语音 duck 20% 引擎自动。
10.2 SFX(13 + 24 喊话;SYN=程序合成,SA3=Stable Audio 3;双格式、真峰值 ≤-3dBFS)
| id | 方式 | 说明 |
|---|---|---|
atk_whoosh |
SYN | 挥击风声 0.3s(带 pan=出击方位) |
atk_swish |
SYN | 挥空空荡风声 0.3s(归因真话:没打到) |
kill_wolf / kill_bear |
SA3 | 狼哀嚎倒地 0.5s / 熊闷重倒地 0.7s(带 pan) |
skill_spin |
SA3 | 旋风斩气旋 0.8s(中央,大动态) |
breach_thud |
SA3 | 营地被撞闷响+木栅裂 0.6s(中央) |
horn_wave |
SA3 | 波次号角 0.9s(中央) |
join_chime |
SYN | 队友加入 0.4s(双音上行) |
call_whistle |
SYN | 求援哨音 0.6s(座 3 呼喊用,带 pan) |
howl_wolf / howl_bear |
SA3 | 生成呼啸 0.8s / 低吼 1.0s(带 pan,后半平面 ×0.6) |
loop_wolf / loop_bear |
SA3 | 移动循环:狼碎步+低呜 / 熊重步+喘息(4s 无缝循环源,游戏层 loop,pan+距离档音量) |
npc_<char>_<line> ×24 |
TTS | bot 喊话(voice 管线生成、落盘 sfx/,峰值归一 0.89 沿用 voice 策略;游戏层 vol 0.55,§8 R7) |
10.3 voice(旁白/系统音)
60 句 ×2 性别(male=Uncle_Fu / female=Serena,全表同一 instruct 常量/性别),分组与全文见 23-hunt-voice-ids.md。数字全分档整句:耐久 4 档 / 波次 3 档 + 5 整句 / 在场猎人 2 句 / 击杀 3 档 / 威胁 3 档 / 评级 4 整句。键盘变体 11 对(V 组,gesture.inputMode() 选播)。
规模核算:voice 60×2×2fmt ≈ 12MB + npc 24×2 ≈ 2MB + key 变体 11×2×2 ≈ 2MB + sfx 13×2 ≈ 1MB + bgm 2×2 ≈ 6MB ≈ 23MB < 30MB 预算 ✓。
11. 向导层接入(engine/guide.js,不新增 earcon 词表)
| 状态 | guide.state() | 时点 |
|---|---|---|
| TITLE / MATCHING / LOBBY / MENU | menu |
进态即播 |
| TEACH / WAVE | battle |
开局/波开始 |
| END | settle |
结算进入 |
| danger | danger |
耐久首次 ≤2(每局 1 次,配 danger 震 + danger_low) |
- turn cue 不使用(无回合制);看门狗:WAVE/LOBBY 无操作 [10s, 25s] →
gd_idle/ null(第 2 级纯 nudge);TEACH 每步 [8s] 纯 nudge(教学句不重播)。玩家任意手势复位;guide=off 全静零定时器(既定契约)。 - 纠错:无效手势 error 震 +
gd_invalid(onceKey 同局 1 次,之后仅震)。 - 无卡关三级提示(本作无"卡住"形态:压力来自怪物而非谜题)。
12. 存档与排行榜(进度 API)
{
"schema": 1, "game": "hunt", "updatedAt": 0,
"summary": "最佳评级A · 击杀14 · 营地余8 · 共3局",
"best": { "grade": "A", "kills": 14, "durLeft": 8, "at": "<ISO>" },
"runs": 3, "totalKills": 31, "tutDone": true
}
- 写入时机:终局结算(每局一次,胜利/失守都写 runs/totalKills;
best只被胜利局按 评级 > 营地余耐久 > 击杀 的序更新)。客户端在收到ev end后自行 PUT(Bearer = 档案 token;离线只写 localStoragesoundgame.progress.hunt)。00 §4 约定遵守:data 自由格式 + 必带 summary(大厅卡片直接显示)。 - summary 生成规则:
最佳评级{grade} · 击杀{kills} · 营地余{durLeft} · 共{runs}局;无 best(全败)时共{runs}局 · 还没守到天亮过。 - 排行榜形态:本切片无跨玩家排行 API——"排行榜"= 个人最佳桶 + 大厅卡片 summary(00 §4.3 既有机制)。跨玩家榜列 roadmap。
- 读取:TITLE 有 best 时上滑播最佳段(评级+击杀档);tutDone 控制是否进教学房(§5)。
13. playtest 断言(摘要;全表住 13 号文档 §24)
- 关键路径:遮罩解锁 → TITLE → 快速匹配(首局教学房 T0-T4 逐步)→ LOBBY → 开局 bot 补位(roster 断言 3 猎人)→ 5 波(spawn/kill/breach/wave 事件序列与 §3.4 表逐波核对)→ 终局结算链(grade 与 dur 一致)→ PUT progress(summary 命中生成规则)→ 再来一局。
- 边界:断线 30s 转 bot / 宽限内 resume 回槽无缝 / 旋风斩冷却与冷却中双击=重听 / 呼喊→bot 补位(10s 内 bot act 方向落入呼喊者防区)/ 听觉裁剪(eta>6 静默、loop ≤3、threat 只计内圈)/ 攻击冷却服务端拒绝 / snap 校正 / 菜单不暂停照扣耐久。
- 失败即不通过:评级与 dur 不符(归因假话);服务器接受违规 act;ev 与 snap 矛盾不收敛;bot 喊话压过旁白(R1/R2 破);loop >3 路;教学狼扣耐久。
- 全表与全局通用断言见 13-playtest-criteria.md §24。
14. 自检清单
| # | 硬约束 | 自检 |
|---|---|---|
| 1 | FTUE 首输入 <60s / 首正反馈 ≤3min | T1 首滑 ≈t+20s;首杀 ≈t+40s ✓(§5) |
| 2 | 学习期零惩罚 | 教学波突入不扣耐久;做错重教 ≤2 次自动放过;挥空仅 error 震 ✓ |
| 3 | 手势宪法 | 上滑=状态、下滑=菜单、双击=重听(冷却中落地)保留;E1-E4 登记且教学讲清 ✓(§2.5) |
| 4 | 向导层 | 签名 4 态不新增词表;看门狗 [10/25]s 二级纯 nudge;纠错 onceKey;guide=off 全静 ✓(§11) |
| 5 | 旁白规范 | 60 句 ≤ 预算;整句无拼接;数字全分档;同 instruct 常量;无语速词;"重新听"规范写法 ✓(台词表头注) |
| 6 | 拥挤控制 | R1-R8 可断言;最坏并发 ≤7 声流;警报零丢失口径 ✓(§8) |
| 7 | 方位降维 | 8 方位 × 3 距离档,pan 七值 + 后半平面 ×0.6;无连续角度、无逐方位语音枚举 ✓(§8.2/§8.3) |
| 8 | 协议自洽 | C→S 3 条、S→C 4 类字段表全;服务端权威+快照兜底;断线/重连/房灭路径闭合 ✓(§6) |
| 9 | 引擎边界 | 引擎改动仅 opts.pan 一处且后向兼容;loop 走游戏层先例;30MB 预算内 ✓(§9/§10.3) |
| 10 | 服务器约束 | 零依赖(手写 WS 帧);数据落盘/REST 零改动;单实例约束不恶化(房间全内存) ✓(§6.1) |
| 11 | P1 视障 | 双击免费重听威胁;上滑一键复述;方位由声像承载且规则教学讲明;无倒计时焦虑(波次自然出清) ✓ |
| 12 | P3 通勤 | 单手免视;菜单不暂停但 3 项极简;局 5 分钟随玩随放 ✓ |
| 13 | 失败归因真话 | 评级=dur 服务端直发;结算击杀档=个人真账;挥空/失守有真话反馈(atk_swish/end_lose) ✓ |
| 14 | seed 可复现 | 房间 seed + mulberry32 派生;刷怪剧本/bot 反应带全走 seed;服务器单测可复现 ✓(§3.5/§7.2) |
15. 设计裁决与矛盾修正记录(实施前必读)
对任务书核心案的细化/修正,逐条登记(核心形态零改动):
- 双击矛盾修正:任务书"双击=旋风斩,8 秒冷却;无冷却时双击=重听威胁播报"自相矛盾(无冷却=就绪=放旋风斩)。裁定为:旋风斩就绪时双击=放出;冷却中双击=重听威胁播报——既保留技能,又让宪法"双击=重听"在全时段有定义(豁免 E2 登记)。
- 补出击冷却 2.5s/人:任务书未给出击冷却。无冷却则单人滑动吞吐无上限,协作失去必要性(§1.6 风险③)。2.5s 使单人峰值约 1 杀/窗,三人局才有分工意义。数值写在 §3.2,服务端裁决。
- 防区=软分工模型:任务书"玩家位于营地中心"与"bot 朝你侧补位"并存但无站位定义。裁定为软防区(§3.3):不设站位硬锁(任何方向都能打),防区只做 bot 偏置、喊话 pan 来源、刷怪均衡——保留"补位"语义且不引入连续几何。
- 四向滑 → 90° 弧映射:任务书"四向滑映射到最近罗盘方位"在 8 方位刷怪下不可玩(斜向怪永远打不到)。裁定:滑向映射 ±45° 弧,击杀弧内内圈 eta 最小者(§2.1/§3.2);协议 act.dir 仍用 8 罗盘枚举(bot 用满 8 向),客户端只发四基向。
- 房码 UX 范围:音频录入 4 位房码需数字语音资产(10 条)会击穿 60 句预算。裁定:协议 join.room 完整支持房码(服务端实现+单测),客户端 v1 只做快速匹配;音频录码 UX 列 roadmap(届时加数字整句资产)。
- 第 4 座无角色声音:bot 角色预算 3×8=24(任务书定死),而 4 人局第 4 座真人的呼喊无角色可用。裁定:座 3 呼喊=
call_whistle哨音(带 pan,<1s,R5 合规),bot 照常响应补位;哨音本身是有效求援信号,明示于 §3.3。 - bot 喊话落盘 sfx/(异例):任务书两处表述有张力——拥挤控制章"bot 喊话走 sfx 层空间化播放"与引擎章"voice 层同样支持 pan(bot 喊话用)"。裁定:喊话必须与旁白并发(R1/R2 的核心),voice 层单流做不到 → 走 sfx 层,引擎按 id 前缀分发故资产落
audio/sfx/npc_*(voice 管线生成不变);voice 层 opts.pan 能力同步提供但本作不使用。旁白 narrator 与 bot 同 speaker 的区分度风险(阿岩×male 旁白、小筝×female 旁白)按 abyss 先例标注"中",缓解=空间 pan + instruct 差异 + 语境(bot 只讲 1-2s 短喊话)。 - 巨兽 -2:任务书"突入=耐久-1"为默认口径;第 3 波起加入暴熊(-2)作为重威胁,给击杀优先级一个明确信号(重 loop/重倒地音可辨),数值与播报(breach_heavy)配套,不改核心形态。
- 失守与入榜:任务书"评级 S~C"隐含必完 5 波。裁定:耐久波内归零=失守终局(end_lose,无评级、不写 best,runs/totalKills 照记);胜利局才按 §3.6 评级写入 best——排行榜不被败局污染,失守有真话结算。
- bot 强度调参(§7.2 数值修订,协议字段不变):初版严格按 §7.2 公式(反应带 eta≤2.2+rng×1.2、无空转、旋风斩 eta≤3≥3 必放)实测零泄漏——多种子仿真 3 bot 水准局余耐久恒 10(S),甚至真人全程挂机 2 bot 独守也恒 10,§3.4"3 bot 水准余 6-9"与"协作必要性"(§1.6 风险③)落空。根因是吞吐超配:3 猎人 1.2 杀/s vs 峰值到达 0.67 杀/s,冷却 2.5s 不构成约束。裁定修订为:反应带 eta ≤ 2.0+rng×1.0;出手决策加 80% 空转概率(500ms tick 内愣神,等效出手周期 ~4.5s);旋风斩 eta≤2.5 ≥3 只且 60% 概率想起。调后 24 种子扫描(
web/server/test/hunt-balance-scan.mjs):挂机局 17/24 失守或余 ≤3(6 场失守,mean 3.3,C/失守口径 ✓);3 bot 水准局 17/24 余 6-9(mean 7.5,A/B 为主 ✓);真人最优仍 23/24 满耐久 S("比 bot 强"空间保留 ✓)。bot"不失误"语义保留(只打有目标的弧),弱化的是反应速度与出手频率(拟人迟钝),不是命中率。
16. v2 修订记录(2026-09-19,10 人并发模拟迭代;依据与数据见 research/hunt-sim/)
迭代章程 research/hunt-sim/00-plan.md;v1 诊断 analysis/01-04;裁定 synthesis.md(S1-S9); 复测对比 metrics-v2.md(去留 6/10→10/10,弧错 22%→11%,N/S 可命中 63/69%→75/81%)。
- E3 修订(S1):WAVE 战斗态下滑恒出击(弧内无怪=空挥真话 atk_swish); 模态菜单入口收窄到波间休整窗(waveclear 后 8s)与 LOBBY/END。原「无目标下滑= 菜单」的语境折中废止——v1 实测多人战斗中误触菜单(最多单局 14 次),弹菜单+语音 播报双重打断是退游死因之一。上滑无目标=状态链维持不变。
- 冷却反馈(S2):冷却内被拦只在每个冷却窗首次给 error 震(连拦静默);
本窗被拦过则就绪时刻补一记
beat轻震(无语音占用)。v1 实测全场 666 次乱滑连震。 - kill_band0 下架(S3):个人击杀 0 时状态链/结算链跳过「你还没开张」 (沉默优于羞辱;正向改写句列 TTS 批次)。0 杀玩家的去留反转(v2 P08:0杀→12杀) 是本项最直接证据。
- 看门狗重定义(S4):WAVE 内本波有出手/击杀 → 一级语音永不触发;一级降纯震; 语音闲置每局 ≤1 次;重教句只留在教学局。判定线=「耳朵在场」(等待是合法玩法)。
- 协作人声包(S5):①真人「扮演座位角色」(§3.3 既有设定)正式接线——杀熊/ 三连杀/旋风斩清 ≥3 时由该座角色播 kill/skill 句(服务端 creditKill/adjudicateSkill 放开 slot.bot 限制);开局 greet 覆盖全部有角色座位;②满编真人房呼喊无 bot 可应 → 2.5s 后由邻座真人角色代答 help_ack(服务端 pendingLines 兜底,v3 若加真人应答手势 则替换);③help_call/help_ack 客户端永不超时丢弃(氛围句才受 R2 ≤2s 上限); ④喊话全局预算 10s 窗 2→3 条。
- 后半平面第二线索(S6):身后 loop 在 ×0.6 响度之上叠加 750Hz 低通(音色 发闷 ≠ 响度发闷)。N/S 轴从单线索变双线索,模拟实测可命中 63/69%→75/81%,新手 自述「破案」。§8.3 的 pan 红线不变(方位仍由声像承载)。
- 难度与评级重构(S7):①波次量按座位缩放
waveCounts:4 人房 ×4/3(3 人房 原值,§3.4 表为 3 人基准);②第 3-5 波插入齐压拍alignPincers:1-2 组跨座位 同种类怪重定时到同秒进内圈(数量与 eta 不变、种子确定性保持;齐压在 startWave 层应用,buildWaveScript 纯函数不动);③bot 让位窗:怪进内圈头 2s,bot 不与 该防区人类座位抢(mobYielded,只约束 bot——对真人不可见不拦截,避免「挥空 真话说谎」的暗墙;真人房的参与真空 v3 用分层匹配解决);④评级双轴gradeForEx:S = 耐久 ≥9 且 ≥2 名猎人各 ≥6 个人击杀(单人带飞局上限 A)。 - 教学跳过保护(S8):作答步(T1/T2/T3)内下滑=答错处理,不再跳过教学; 跳过仅在 T0/T4/完成等待态(v1 实测 P04 教学局手滑跳过、记恨三局)。
- 状态链按变化播(S9):耐久档/波次档/猎人数/击杀档与上次播报一致的分段跳过; 全部无变化=仅 confirm 轻震(「营地固若金汤」复读投诉归零)。
v3 头号遗留(未修,如实登记):营地零损耗未破(v2 复测 18/20 局余 10)—— 4 人房 ×4/3 + 齐压拍不足以穿透专家房吞吐(单人最多 34/51 杀)。下一档参数与 分层匹配/保底猎物方案见 research/hunt-sim/report.md §5。