Files
gplan-2/design/gdd/b2-enemy-system.md
T
DKChan 85841f52be docs(gdd): 13 份系统 GDD 迁移为 design/analysis 两件套 v2
- design 件只装结论:v2 骨架(设计目的/系统范围/核心循环/领域章节/依赖与输出/P0 闭环/P1·P2 后置/调参表/GWT 验收/版本历史),AC/EC/OQ/义务台账编号全部保留
- 新建 design/gdd/analysis/ 13 份推演件:候选取舍/公式推导/数值锚定论证/已裁决争议按问题节组织,文末附去向映射表(零丢失审计)
- v1 原文备份于 design/gdd/legacy-2026-09-14-v1/(13 份)
- systems-index 文档树补 analysis 目录导航行
- gdd_lint 全量 PASS(14 文件 0 不通过);迁移零裁决——19+1 项待拍板已登记各 analysis 开放问题区
2026-09-14 20:39:11 +08:00

363 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# B2 敌人系统(Enemy System / 战斗单位系统)
> **状态**:Approved · **版本**:v2 · 2026-09-14
> **一句话**:定义战斗双方是谁、多强、带什么词条、如何受伤与死亡——雷的代价的收件人。
> **推演件**:`design/gdd/analysis/b2-enemy-system.md`(本文件只收结论,理由在那里)
## 设计目的
B2 是战斗域单位层(Layer 2 Core · MVP):持有 EnemyTable/HeroTable 回填权,承载主角与敌人共用的战斗单位接口。缺本系统则踩雷优势无受益者、胜负判定无主体、难度曲线无施加对象。
1. 对面是个看得懂的对手——威胁可推测、可归因、可拆解,不是一个血条。
2. 它弓起背,下一拍是重击——读怪推理进入挖掘决策。
3. 它强是我喂的,它弱是我拆的——伏击与削弱落在同一个它身上。
4. 越下越险——深层敌人更凶,给下探氛围一个可见刻度。
反向护栏:威胁绝不能是无源暗账——玩家说不出"它为什么变强 / 这一拳为什么这么重"即归因链失职。
## 系统范围
负责:
- 单位状态(hp/max_hp/存活/行为环指针)与查询/受击/推进接口
- EnemyTable/HeroTable schema 与行回填(行键 (level_index, node_depth, enemy_id))
- 行为环语义与目标选择规则(固定顺位)
- 伏击量纲定稿与 advantage_per_round_cap 表值(B1 条款 3 对账供给)
不负责:
- 结算编排与伤害消费语义(B1 SettlementTable)
- 状态实例容器(B3)
- 奖励内容与掉落(B4)
- 棋盘规则(A1/A2)
- 单位成长曲线与难度修饰(C3)
- 遭遇构成与跨战斗 HP 持久性(C1/C2)
- 呈现(D1/B5/D5)
- 单位/词条类结构与事件载体实现(实现期 ADR 域)
硬约束:零随机(rng_service 流封闭)· 零 UI 依赖 · 零主动事件订阅 · 悬空引用致命、零软回退 · 行为环敌单位专属 · 单位接口调用权集中归 B1 · 胜利路径 = 基础攻击(恒在)+ B4 奖励侧规划面伤害共同承载——"敌人全灭 → 战斗胜利"可判定。
## 核心循环
```
战斗开始(B1 编排调用——B2 编排被动,调用权集中归 B1)
① 解析遭遇行 (level_index, node_depth) + enemy_id 精确匹配(悬空致命)
② 行值快照 → 实例化本战 n 个敌单位(行主序生成序)+ 主角(hp 入参);指针指向环首
每次结算(B1 驱动;每存活敌单位恰一次 advance,主角无环不在推进域)
⓪ 主角先手攻击 → apply_damage(顺位最前存活敌)
① 兑付读值 query_attack(u, L, m_amb) → {baseline, attach, total}
② 受击上报 apply_damage(hero, 终值)——调用序钉死:倒序会读到下一拍
③ 推进 advance(u)——存活才推进(先作用后推进)
当前拍作用:attack = atk 点伤害 · charge = 空拍不兑付 · heavy = atk × 倍率
轮到前被杀 → 本拍不打、指针冻结(MVP 无复活 → 永久冻结)
battle_over(B1 调 despawn_all)→ 全部单位消亡,不跨战斗
```
| 出口 | 触发 | 动作/去向 | 约束 |
|---|---|---|---|
| 单位死亡 | hp = 0(判定时机归 B1) | 移出顺位、不再作用、不计入"未灭" | 不可逆(MVP 无复活);Dead 句柄保留只读可查(query_beat 冻结值) |
| 敌全灭 | 存活敌集 = ∅ | "敌人全灭"成立 → B1 胜利判定素材;单体/AOE 无有效目标;敌侧伏击池随轮末衰减自然清空 | 判定逻辑归 B1 |
| 主角死亡 | hero hp = 0 | 战斗失败判定素材(判定时机归 B1) | battle_over → 全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态 |
| battle_over | B1 编排 despawn_all | 全部单位状态消亡、不跨战斗;唯一出口 = 重新 spawn | 重复 despawn_all → false + 诊断;Despawned 为逻辑失效态(查询空 + 诊断) |
## 单位与状态
| 状态 | 进入条件 | 行为/出口 |
|---|---|---|
| Unspawned | 构造后未实例化 | 无接口副作用;唯一出口 = B1 编排实例化 |
| Alive | 实例化(行值快照 / hp 入参) | 可被指定目标(顺位)、按行为环作用、计入全灭判定;受击 hp 递减 |
| Dead | hp = 0(判定时机归 B1) | 移出顺位、不再作用、不计入"未灭";不可逆;句柄保留只读可查(query_beat 冻结值) |
| Despawned | battle_over(B1 despawn_all) | 逻辑失效态——失效句柄查询返回空 + 诊断、作用接口拒绝;唯一出口 = 重新 spawn |
不变式(debug 断言口径):hp ∈ [0, max_hp];敌单位数 = 本战实例化数(战斗内不增);行为环指针 ∈ [0, 环长);违规 = debug 断言中止(进程致命保留给 D2 数据通道)。
规则:
1. **主角和敌人共用同一套受伤与死亡规则**:战斗场上恰两类单位(主角一个、敌人若干),共用同一套身体语义——每单位有当前血量与上限,挨打血量往下减,到 0 即死亡;超上限/负数/溢杀与"何时算死"归 B1 结算公式域裁决,B2 只提供受击上报与状态查询接口、如实记账。主角血量归零 = 失败判定素材、敌人全部归零 = 胜利判定素材,判定本身归 B1。
2. **敌人系统自己不主动做事,一切等结算编排来调**:B2 不产生随机数、不碰界面、不订阅任何事件——挖雷、插旗等棋盘事实经 B1 转化后由它调用;B2 只摆出查状态(血量/当前拍/顺位/存活)与受动作(受击/推进/消亡)两组能力,什么时候调、按什么顺序调全归 B1。
## 敌人构成与名录
规则:
1. **一场战斗 1–3 个同种敌人,敌方效果对全体生效**:MVP 每场战斗 1–3 个敌人,全部同种同属性——同一 EnemyTable 行实例化、按行主序排生成序;战斗中敌人只减不增(不做召唤/增援/分裂——EC5,出现即 debug 断言中止)。敌方增益/减益 side-wide:作用于所有存活敌人、层数进一个共享池(F2 池共享不消耗;B3 现接口零改动兼容);"只作用于某一个敌人"的单体寻址暂不开放,target_ref 登记为 B3 扩展位义务(首个单体效果上线时补列,随批 B1 归集语义/D1 呈现裁决)。
2. **敌种按关卡坐标存放,每个坐标只出一种**:EnemyTable 以(关卡序号 × 节点深度)为坐标存放敌人,每行集恰一敌种(行集内 enemy_id 唯一 = 表结构校验,配出两种加载期致命——既无"同点随机挑一种"的选择规则,也无引用悬空风险);同一关内随深度换敌种合法;"同深度多敌种怎么选"归 C1/C3(预留结构位);精英/Boss 修饰在现键结构下无编码位(预登记 OQ7,C1 定稿时强制核对);名录扩容归垂直切片/Alpha。
| 敌种 | 行为环 | armor_type | 说明 |
|---|---|---|---|
| goblin(地精) | [attack](环长 1,mod 1 恒等) | none | 每拍 attack、无自然空窗(兑付率 ρ=1——EC10) |
| bugbear(熊地精) | [attack, charge, heavy](三拍环) | a | heavy_multiplier = 2 |
| ogre_chief(食人魔酋长) | [attack, attack, charge, heavy](四拍 Boss 环) | b | heavy_multiplier = 2 |
现行名录 = {goblin, bugbear, ogre_chief} 三敌种(以 registry `enemy.mvp_content` 为准);各行 hp/atk/unit_count 数值全部 provisional、曲线调参所有权归 C3;遭遇分布调参所有权归难度域(防多样性无护栏,勿照抄任何示例分布)。设计意图:名录小、坐标单敌种——加载期就把配置错误杀死,选择规则留给遭遇域。
## 行为环
规则:
1. **敌人按排好的剧本轮流出招,走到哪一拍零随机**:每种敌人一条行为环——战斗开始指向第一步,之后每结算一次走一格、先把当前拍的招打出去再走格(先作用后推进);环尾回环首;跨轮次、跨扫雷图延续(EC11,除 despawn_all 外无任何重置入口);轮到它之前被打死 → 本拍不出手、指针就地冻结(死亡即不动手——EC1;MVP 无复活 → 永久冻结);行动完成后才死 → 已行动已推进;同种敌人永远同拍(同步性质——EC2);heavy 拍前被杀 → 重击永不兑现(抢杀窗口——EC9)。全程零随机,走到哪一拍由结算次数唯一决定。行为环敌单位专属:主角无行为环,对主角调环接口一律拒绝(false / 空 + 诊断)。MVP 只启用 attack/charge/heavy 三种拍,后四类为预留语义位、配进表会被校验拒绝(fail-fast;启用走本 GDD 修订非调参——枚举封闭性)。
| 值 | MVP 状态 | 语义 |
|---|---|---|
| attack | 实现 | 本结算对主角造成 atk 点伤害 |
| charge | 实现 | 本结算不作用——威胁空拍、规划窗口;不兑付伏击(不读池) |
| heavy | 实现 | atk × heavy_multiplier |
| guard | 预留(校验拒绝) | 自防 |
| boost | 预留(校验拒绝) | 自体强化 |
| hinder | 预留(校验拒绝) | 妨害雷图——敌方向棋盘施加变异类妨害 |
| summon | 预留(校验拒绝) | 召唤增援 |
| 项 | 细则 |
|---|---|
| 指针与推进 | 战斗开始 beat = pattern[0]、steps = 0;每次结算推进一格——先作用后推进(B3"先兑付后衰减"同款时序哲学);轮到该单位时仍存活才行动并推进(每结算恰一次 = B1 义务①) |
| 死亡口径 | 轮到前被杀 → 本拍不打、指针冻结(F1 承载);行动完成后才死 → 已行动已推进 |
| 回绕与延续 | 环尾回环首;指针 ∈ [0, 环长),环长 1(goblin)退化为恒等;跨轮次、跨扫雷图延续;battle_over 随单位消亡,除 despawn_all 外无重置入口 |
| 确定性与专属域 | 零随机——环推进是结算次数的确定函数(rng_service 流封闭);主角无行为环,advance/query_beat 等环接口对主角句柄一律拒绝(false / 空 + 诊断) |
| 实现域 | pattern 环长 [1, 8];MVP 校验域 = {attack, charge, heavy};EnemyTable.pattern 校验拒绝预留值(fail-fast,A1 solver_rules 冻结先例) |
设计意图:确定性环 = 可学习性来源——读怪收益(抢在重击前击杀)由此产生。
## 伏击
规则:
1. **每踩一颗雷敌方叠一层伏击——一轮封顶、超出可见不生效**:挖开雷格时敌方全体获得一层"伏击"——MVP 伏击清单只有伏击这一条(StatusEffectTable mine_hit 来源的唯一效果行;将来加新词条要 B3 加行并连锁更新锚点,不属于 MVP)。转化路径:棋盘上报"挖到雷"→ B1 转成一次伏击施加(作用全体敌人、共享层数池),每颗雷恰转化一次(B1 义务②)。单轮授予上限 = cap 行值:上限内逐颗叠加;达到上限后多踩的雷不生效,但逐颗记一行"溢出未生效"结算明细(B1 义务⑥)——惩罚不静默蒸发。
2. **一层伏击 = 敌人每次攻击多打 3 点固定伤害**:一层伏击 = 每次攻击类行为(普攻或重击)额外附加 m_amb(provisional 3)点伤害,按主角血量点数计;附加是后置加法、不进重击倍乘(倍率只乘基础伤害);蓄力拍不出手、不附加(charge 不读池——EC8:蓄力轮授予的伏击层当轮全部蒸发,合法推论 + 读怪奖励,D5 教学条目)。伏击只活一轮——每轮结算清零、没有跨轮存量(dur=1,池衰减归 B3),跨轮累计没有表值上界;同结算内授予先于兑付(EC17——本轮踩的雷当轮即兑付,若该轮有攻击拍;读值语义归 B2、次序归 B1 义务①②)。
| 单轮伏击上限(cap 行值) | 行为 |
|---|---|
| 默认 4 | 前 4 颗雷各叠一层;第 5 颗起每颗记一行"溢出未生效"明细 |
| 调到 8 及以上 | 单轮雷数至多 8 颗、达不到上限——溢出协议自然不可达 |
| 0 | 该行敌种免疫伏击——每颗雷都记"溢出未生效",全部可见、全部无效(EC6;该遭遇条款 3 右臂仅剩基线伤) |
设计意图:惩罚有源可溯、有顶可见——cap 默认 4 是单轮 5–8 雷灾难轮(EC7)的反灾难护栏。
## 目标选择与读怪
规则:
1. **敌方打主角不用挑,玩家侧永远先打排最前的敌人**:敌方一侧没有选择可言——所有存活敌人各自按当前拍作用于主角(己方唯一单位,天然唯一目标)。玩家/道具一侧用固定顺位:单体效果默认打生成序最前的存活敌人,它死了顺延到下一个;范围效果(AOE)打全体存活敌人,同样无需选择;unit_count = 1 时顺位顺延与 AOE 分流无消费者、AOE ≡ 单体——合法退化非错误(EC15)。道具自带"指定目标"语义的扩展归 B4/结算域;顺位必须对玩家可读——"下一个被打的是谁"要能推测出来(D1 义务③)。
2. **敌人要出什么招靠看图推测,不直接报数字**:界面不直接显示敌人下一拍的行为与数值——威胁靠看图推测(它弓起背,下一拍就是重击);即将执行的一拍在挖掘阶段全程亮出;映射每局恒定、禁止重掷(否则读怪退化成猜怪);精确数字只在结算明细兑现。线索清单与图像规格归 D1/B5,映射键 = enemy_id(B3 effect_id→icon 同款契约);MVP 读怪对象最长 3 拍环,学习成本低。
| # | 规格 | 内容 |
|---|---|---|
| ① | 图像↔行为映射稳定可学 | 同一敌种同一行为恒同一视觉状态(如 bugbear 蓄力拍 = 蓄力姿态) |
| ② | 形态暗示强度 | 敌种形态承载强度档位与环长的直觉编码("大个子多半有重击"式) |
| ③ | 映射每 run 恒定 | 禁止重掷——否则"读怪"退化"猜怪";映射键 = enemy_id |
| ④ | 精确数值在结算明细兑现 | 预告面是图像、数字面是明细 |
| ⑤ | 预告时序锁定 | 视觉状态呈现即将执行的一拍(当前拍预告)——挖掘阶段全程可见;结算时不另设独立意图播片(结算明细是数字兑现面、非意图预告面) |
设计意图:读怪是规划素材;映射重掷 = 读怪退化成猜怪(P1 裁剪判据)。
## 主角与实例化
规则:
1. **主角数值在英雄表里——血量上限加恒在的基础攻击**:主角战斗数值单独放一张全局单行的 HeroTable(B2 是回填 owner,走 D2 接口漂移规则回写),max_hp 与 atk 两字段。基础攻击恒存在、不许为 0——零基线乘任何百分比增益恒为零,这个死锁从数据层就进不来(atk = 0 加载期校验拒绝)。每次结算的战斗段主角先手攻击顺位最前的存活敌人——先于敌方行动;攻击是固定动作、不是行为环一拍;伤害算式/乘区/明细行协议归 B1 战斗段。attack_up(+50 percent)的唯一消费面就是这次先手攻击;道具伤害是恒定表值、不吃修正。战斗开始血量由编排方入参供给(越界拒绝——EC16:> max_hp 或 ≤ 0 → false + 诊断、不留半实例化;MVP 冒烟以血量上限入参),跨战斗是否保留归 C1/C2;解锁成长归 C4、曲线调参归 C3。胜利路径 = 基础攻击 + 奖励侧规划面伤害共同兑付"敌人全灭 → 战斗胜利",每一分伤害逐行标注来源(hero_attack / 道具逐件实例)。
2. **开战按坐标读表生成敌人,数值是生成那一刻的快照**:战斗开始时编排方给出遭遇坐标(关卡序号 + 节点深度),B2 据此从 EnemyTable 精确匹配一行(D2 R1 进度键 floor + enemy_id 精确匹配,悬空致命、坐标越界前置守卫——EC13/EC14 走 D2 通道),实例化"本战实际数量 n"个敌单位——n 由 B1 开战掷的 encounter 骰从数量区间 [unit_count, unit_count_max] 定出(定值行取表值);主角同时以血量入参生成。实例化拿到的是当时表值的快照:之后热重载改表,已生成单位一概不受影响(EC12,B3 EC9 先例)。难度成长走双通道:表值随坐标加深而变 + C3 整体修饰——修饰施加点 = 行值快照之前套乘,MVP 难度表为空、零修饰(registry `difficulty_table`,算式归 C3);B2 一律按实例化后的数值运作。
## 数据表 Schema
### EnemyTable(D2 承载,键 (level_index, node_depth, enemy_id),B2 回填 owner)
进度键 floor(D2 R1)+ 实体键精确匹配;每行集恰一敌种(表结构校验,违规加载期致命);本表是进度成长表(A1 BoardGenTable 同型,无 Rule 4 豁免);表非空(D2 Rule 6)。
| 字段 | 类型/范围 | 语义 |
|---|---|---|
| enemy_id | String(行集内主键) | 实体键精确匹配(D2 Rule 4);意图视觉映射键 |
| display_name | String | 图例/HUD/明细用名(本地化归 D6) |
| unit_count | int [1, 3] | 遭遇区间下界(knob;区间 [unit_count, unit_count_max] 经 B1 encounter 骰定本战实际数量) |
| unit_count_max | int [unit_count, 3],可省略 | 遭遇区间上界(缺省 = 定值,旧行零迁移) |
| armor_type | enum {none, a, b},缺省 none | 护甲类型(克制矩阵 = SettlementTable.armor_effectiveness;MVP 映射 goblin=none / bugbear=a / ogre_chief=b) |
| hp | int [1, 999] | 单位最大生命基线(knob;难度修饰前) |
| atk | int [0, 999] | 攻击基线(knob;0 合法 = 纯伏击伤敌种) |
| heavy_multiplier | int [1, 16] | heavy 倍率(knob;无 heavy 拍的敌种不消费——非校验项,B3 max_stack 先例) |
| pattern | StringList(环长 [1, 8]) | 行为环(元素 ∈ {attack, charge, heavy} 为 MVP 校验域;预留值校验拒绝) |
| advantage_per_round_cap | int [0, 16] | 单轮伏击授予上限(knob,默认 4 定稿;条款 3 供给表值;0 = 免疫伏击的合法极端) |
### HeroTable(D2 新增 schema 行,单行全局,B2 回填 owner)
主角不随关卡自动成长(成长 = 天赋 C4 / 奖励侧);Rule 4 豁免同 ActionEconomyTable 先例。
| 字段 | 类型/范围 | 语义 |
|---|---|---|
| max_hp | int [1, 999] | 主角最大生命(knob,MVP 值 provisional 出处待查) |
| atk | int [1, 999] | 基础攻击(knob,MVP = 6 原型锚 provisional 归 C3;0 = 加载期校验拒绝——零基线死锁自 schema 层杜绝) |
## 数值公式(F1–F4 结论)
全部公式零随机、同输入恒同输出(可回放前提)。术语:拍 = 行为环里的一格行动;伏击池 = 敌方侧共享的伏击层数(本轮踩几颗雷就几层,轮末清零);溢出 = 单轮踩雷超过 cap 的部分(转化为明细行可见、不生效)。推导与论证见 analysis 件。
### F1 行为环推进(ring_advance)
**结论**:beat(u) = pattern[steps_u mod 环长]、行动完成后 steps_u + 1;指针恒 ∈ [0, 环长)、环长 1 退化为恒等;死亡即不动手——轮到前被杀本拍不打、步数冻结(永久);同种单位同指环首且每结算各恰推进一次 ⇒ 存活者永远同拍(同步性质定理)。
```
beat(u) = pattern[steps_u mod 环长] 行动前读拍;战斗开始 steps_u = 0(指向环首)
steps_u' = steps_u + 1 行动完成后推进(先作用后推进)
轮到前被杀 → steps_u 冻结(死亡即不动手);行动完成后才死 → 已行动已推进
```
边界:归 B2——指针所有权、存活校验、冻结不变式、环首初始化、跨轮跨图延续;归 B1——死亡判定时机、结算内先后次序、每结算恰一次推进。
### F2 攻击行为裸伤害构成(raw_attack_damage)
**结论**:attack 拍 D = atk + m_amb × L、heavy 拍 D = atk × heavy_multiplier + m_amb × L、charge 拍 D = 0(不读伏击池);伏击附加是后置加法、不进倍乘;池共享不消耗(同结算多敌读同一 L,非均分、非先到先得);B2 向 B1 供(基线, 伏击)分量二元组(结算明细"伏击 +N"归因行的数据源)。
```
attack 拍:D = atk + m_amb × L m_amb = StatusEffectTable.ambush.magnitude(provisional 3)
heavy 拍:D = atk × heavy_multiplier + m_amb × L
charge 拍:D = 0 不读伏击池——空拍零兑付
```
读数:合成序判别例——bugbear(atk=4, mult=2)heavy 拍 L=4 → 4×2 + 12 = **20**(塞进倍乘得 (4+12)×2 = 32 ≠ 20);单结算单单位附加上界 = cap × 3 = 12(默认,L ≤ min(雷数, cap));无负值分支——clamp/地板/hero 侧减伤合成归 B1。
### F3 伏击授予与溢出(ambush_grant_overflow)
**结论**:granted_r = min(当轮新开雷数, cap)(cap 判定在授予前——B1 预截流);overflow_r = 当轮雷数 − granted_r(每颗溢出雷一行"溢出未生效"明细、含来源);L_r = granted_r(当轮兑付一次、轮末全清、无跨轮存量);守恒 granted + overflow = 当轮雷数、零静默蒸发;max_stack = 16 结构不可达(dur=1 ∧ AP ≤ 8 承载——死护栏非死字段)。
```
granted_r = min(当轮新开雷数, cap) cap 判定在授予前(B1 预截流)
overflow_r = 当轮新开雷数 − granted_r 每颗溢出雷一行"溢出未生效"明细(含来源)
L_r = granted_r 只活一结算:当轮兑付一次、轮末全清
```
变量域:mines_r ∈ [0, 8](结构界 = AP;复开不可能——A2 棋盘面语义);cap ∈ [0, 16] 默认 4(行值 knob;0 = 免疫伏击、全溢出可见);granted_r ∈ [0, 4](B1 编排期逐雷 apply_effect、每雷恰一次转化)。边界:归 B1——预截流编排、授予次序(行主序雷格序为建议序)、每雷恰一次、溢出行协议。
### F4 条款 3 供给口径(clause3_supply)
**结论**:每结算敌方伏击暴露 = k × L × m_amb(k = 本结算存活敌中攻击/重击拍的单位数,确定性可算);每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb(cap > AP 界时 cap 腿松弛);跨结算累计 = 轨迹量、非表值、不设上界(dur=1 每结算清零);B1 对账取"每结算"口径即可闭合、无需战斗长度假设。
```
每结算敌方伏击暴露(精确式)= k × L × m_amb
每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb
跨结算累计 = 轨迹量,非表值、不设上界
```
读数:默认满配上界 = 3 × min(8, 4) × 3 = **36**;cap ≥ 8 时真可达上界 72(而非 144);默认行值 unit_count = 1 下 = 12。
## 依赖与输出
B2 编排被动——调用权集中归 B1;A2/A3/A1 事实均经 B1 结算编排转化,零直接接口。registry / 接口漂移回写义务(R-1–R-4)与双向一致性核对记录见 analysis 附录。
| 方向 | 系统 | 性质 | 接口要点 |
|---|---|---|---|
| 上游 | D2 | hard | EnemyTable schema + 行回填(B2 为回填 owner)+ HeroTable 新增(走 D2 接口漂移规则回写)+ ConfigDB 单点读取(R1 行解析) |
| 下游 | B1 | hard(provisional) | 单位状态查询/受击/推进接口(冒烟 API 面)+ 义务①–⑥(缺项即 blocking,见下表) |
| 相邻 | B3 | 零运行时依赖 | ambush/weaken 量纲与作用域验收锚归 B2;side-wide MVP 兼容(B3 接口零变更);target_ref 扩展位义务 |
| 下游 | B4 | soft(provisional) | 伤害道具默认目标消费(固定顺位);伏击清单扩展联动修订;attack_up 消费面 = 主角战斗段先手攻击;道具/增益 = 基础攻击之外的规划面伤害 |
| 间接 | A2/A3 | 零直接接口 | 雷/旗事实经 B1 转化;AP 界(单轮开雷 ≤ 8)= cap 默认值结构来源;weaken 档位锚归 B4/B1 |
| 间接 | A1 | — | 雷数分布影响伏击吞吐——条款 3 对账消费 A1 actuals p10(B1 义务,非 B2 域) |
| 下游 | C3 | soft(provisional) | 难度修饰施加点/算式归 C3;两表曲线数值调参所有权归 C3;多敌种同深度选择规则共裁;新敌种投放节奏 = 关卡命脉(goblin 单拍环证伪项 OQ8) |
| 下游 | C1 | soft(provisional) | 遭遇构成(战斗触发、(L,D) 供给、unit_count 覆盖语义)归 C1;跨战斗 HP 持久性归 C1/C2 |
| 下游 | D1 | soft | 视觉推测型呈现义务①–④(缺项即 blocking);敌单位 HUD(血条/顺位/姿态)呈现 |
| 下游 | B5 | soft | 行为环视觉状态表现(蓄力姿态等——预告时序规格⑤);结算明细敌方行呈现(数据源 = B1 流水) |
| 下游 | D5 | soft | 读怪教学(敌种环模式可学、顺位规则)+ "蓄力拍前放心贪雷"教学 + 首次蓄力姿态一次性提示候选 |
### 义务台账(活契约,缺项即 blocking)
对外引用口径:B1 清单引用名「B2 义务①–⑥」(b1-settlement 上游表同口径);D1 清单引用名「D1 呈现义务①–④」。
| 编号 | 内容 | 对侧 | 状态 |
|---|---|---|---|
| B1-① | 单位接口调用集中在结算编排、行为环推进每结算恰一次(double-advance 无守卫——信任编排);结算内调用序成文:query_attack → apply_damage → advance(倒序会读到下一拍),B1 冒烟须回放结算步流 | B1 | 已登记 · 缺项即 blocking |
| B1-② | 雷→伏击事实转化恰一次(每颗雷格实例至多一次) | B1 | 已登记 · 缺项即 blocking |
| B1-③ | 伤害 clamp/负值/溢杀语义与死亡判定时机成文 | B1 | 已登记 · 缺项即 blocking |
| B1-④ | 条款 3 闭合对账:消费 B2 cap 表值 + 伏击量纲 + A1 actuals p10(非 target 参数) | B1 | 已登记 · 缺项即 blocking |
| B1-⑤ | 默认目标规则消费与单体/AOE 分流(顺位快照 = 生成序) | B1 | 已登记 · 缺项即 blocking |
| B1-⑥ | 伏击 cap 溢出的"溢出未生效"明细行协议(呈现归 B5/D1) | B1 | 已登记 · 缺项即 blocking |
| D1-① | enemy_id→图像线索映射清单成文(形态/姿态↔行为状态;预告时序 = 下一拍预告、挖掘期全程可见;含强度档位与环长直觉编码说明;形状+色相双通道,art-bible 约束) | D1 | 已登记 · 缺项即 blocking |
| D1-② | 线索映射全局恒定(映射键 = enemy_id——跨 run 恒定、禁止重掷) | D1 | 已登记 · 缺项即 blocking |
| D1-③ | 单位顺位与存活可读(固定顺位规则的推测前提;顺位标记资产需求见呈现契约 #3) | D1 | 已登记 · 缺项即 blocking |
| D1-④ | 敌方 side 池状态呈现口径(伏击层数经 B1 流水——B3 敌侧延伸) | D1 | 已登记 · 缺项即 blocking |
### 呈现契约(数据面义务——B2 无自有渲染面,呈现归 D1/B5)
| # | 数据面义务 | 归属 |
|---|---|---|
| 1 | 敌种视觉身份:双敌种剪影一眼可辨(形状+颜色双通道、色弱可用);形态承载强度档位直觉编码 | D1/B5,art-bible 约束 |
| 2 | 行为环视觉状态:每敌种每类行为(attack/charge/heavy)对应恒定且互异的视觉状态,映射每 run 恒定禁止重掷;MVP 清单 = goblin 1 态(攻击)、bugbear 3 态(攻击/蓄力/重击) | D1/B5 |
| 3 | 资产需求下限:受击反馈(多敌同屏"谁挨了打"归因前提——两敌种可共用通用一组、须显式登记)+ 死亡表现("重击被掐灭"兑付可见——死亡瞬间可辨识、不得直接消失)+ 顺位标记呈现元素;D1/B5 照单生产、不得隐式缺席 | D1/B5 |
| 4 | 单位呈现数据供给:hp/max_hp、顺位、存活态、当前拍视觉状态——数据源 = B2 单位状态查询(经 B1 编排流水) | D1 |
| 5 | 敌方增益呈现:伏击池层数 HUD 口径(数据源 = B1 流水经 B3 投影);结算明细敌方行使用分量二元组("攻击 13 = 基线 4 + 伏击 9") | D1/B5 |
| 6 | 视觉锚适用:血条/伤害数字暗色底高对比(锚 1);敌单位出现在棋盘上还是独立战斗场景 = B5 前"战斗表现美术测试"锁定,B2 只锁数据面不预设舞台;敌人层绘制优先级插入位置(姿态精灵与数字/旗/小人遮挡关系)随美术测试一并裁决 | D1/B5 / art-bible |
| 7 | UI 三项:敌单位 HUD、结算明细敌方行(分量二元组 + 伏击溢出行)、教学口径;MVP 无敌图鉴/敌人列表 UI(名录学习成本内,扩展时再议) | D1/B5/D5 |
| 8 | 音频:MVP 无需求(攻击/死亡/蓄力音效归 D4 Alpha);蓄力拍音频前摇 = 读招潜在第二通道,登记为垂直切片可选项 | D4 |
## P0 最小闭环
1. 战斗开始:B1 调 spawn——按遭遇坐标读表、行值快照实例化 n 个敌单位 + 主角(hp 入参),指针指向环首。
2. 玩家挖掘扫雷;踩雷事实经 B1 转化为伏击施加(每雷恰一次、cap 截流、溢出记账)。
3. 结算开始:主角先手攻击顺位最前的存活敌人。
4. 每个存活敌单位读当前拍:attack/heavy 兑付(基线 + 伏击附加),charge 空拍。
5. 受击上报、hp 递减;hp = 0 移出顺位(死亡即不动手)。
6. 存活敌单位各推进一格(先作用后推进,环尾回环首)。
7. 敌全灭 → "敌人全灭"成立,回流 B1 作胜利判定素材;英雄归零 → 失败判定素材。
8. battle_over:despawn_all 全部单位消亡,不跨战斗;下一场重新 spawn。
## P1 / P2 后置
| 优先级 | 内容 | 后置原因 |
|---|---|---|
| P1 | 预留行为拍 guard/boost/hinder/summon 启用 | 枚举封闭——启用 = 本 GDD 修订非调参;垂直切片/Alpha |
| P1 | 敌种名录扩容与新敌种投放节奏 | 投放节奏 = 关卡命脉(C3 所有权);垂直切片/Alpha |
| P1 | target_ref 单体寻址(B3 扩展位) | 首个单体效果上线时补列,随批 B1 归集语义/D1 呈现裁决 |
| P1 | 蓄力拍音频前摇(读招第二通道) | 垂直切片可选项,非 MVP 义务 |
| P2 | 敌图鉴/敌人列表 UI | 名录学习成本内;名录扩展时再议(垂直切片) |
| P2 | 精英/Boss 遭遇编码位(改键,如追加 encounter_class 维度) | 现键结构无编码位;C1 定稿强制核对,改键走 D2 Rule 4 接口漂移(OQ7) |
## 调参表
| 字段 | 域 | 默认 | owner | 说明 |
|---|---|---|---|---|
| EnemyTable.unit_count | int [1, 3] | 1(provisional) | C3 | 遭遇区间下界;B1 encounter 骰定本战实际数量 |
| EnemyTable.unit_count_max | int [unit_count, 3],可省略 | 缺省 = 定值 | C3 | 遭遇区间上界 |
| EnemyTable.hp | int [1, 999] | provisional(出处待查) | C3 | 难度修饰前基线 |
| EnemyTable.atk | int [0, 999] | provisional(出处待查) | C3 | 0 合法 = 纯伏击伤敌种 |
| EnemyTable.heavy_multiplier | int [1, 16] | 2(provisional) | C3 | 无 heavy 拍不消费、非校验项 |
| EnemyTable.advantage_per_round_cap | int [0, 16] | 4(本 GDD 定稿) | B2 | 条款 3 供给表值;0 = 免疫伏击合法极端 |
| HeroTable.max_hp | int [1, 999] | provisional(出处待查) | C3 | |
| HeroTable.atk | int [1, 999] | 6(原型锚 provisional) | C3 | 0 = 加载期校验拒绝(基础攻击恒在) |
- 非 knob(结构冻结):行为枚举七类封闭、死亡即不动手、先作用后推进、伏击池共享读法、合成序后置加法、伏击量纲 = HP 点、固定顺位规则、每行集恰一敌种、零随机——修订走本 GDD 流程。
- Knob 交互:cap × atk(支配边界 L* 联动)、unit_count × cap(单结算上界双线性)、heavy_multiplier × atk(heavy 拍支配)——禁止独立调参,联动评估归 B1 条款 3 闭合对账;pattern 内容(环长/charge 占比 → 兑付率 ρ)属内容行调参,所有权归 C3。
- 零伤害死局护栏:hero atk=0 已被 HeroTable 域下界结构杜绝;残余退化 = 行 atk=0 且 cap=0 → 无威胁敌(内容调参禁区注记,非死局——OQ9);跨表量级护栏(atk×mult 上界 vs max_hp 域)owner = 实现期 ADR/D2;混编遭遇下 side 池 cap 取哪行值未定义——现结构恒单行值,混编引入时随 OQ7 改键一并裁决。
- 调参所有权:MVP 全部默认值 provisional、曲线数值归 C3;B1 条款 3 对账落定前,cap 与伏击量纲相关联调须重跑方向性预检并同步 registry(接口漂移义务)。
## 验收标准
口径摘要:B2 是单位层纯逻辑类——冒烟载体 = `godot --headless --script`;B1 未实现期间冒烟脚本扮演编排方,按"实例化 → 查询 → 受击 → 推进 → 消亡"序列驱动(B1 就绪后改走真实调用方、断言口径不变);每用例全新实例;期望值按表读值推导不写死;返回 = 受理/拒绝布尔 + debug 诊断(进程致命归 D2 数据通道)。冒烟 API 面、测试口径、夹具策略、覆盖声明与覆盖矩阵见 analysis 附录 B。
### AC 清单(Given-When-Then,编号不变)
| AC | Given | When | Then | 覆盖 |
|---|---|---|---|---|
| AC-1 | 默认根加载完成、全新容器 | `spawn(goblin 坐标, M_h)`;Alive 相位再 spawn 同坐标 | 首次 true;敌恰 unit_count 个、同 enemy_id、投影五字段(hp/max_hp/atk/pattern/cap)= 行值快照;hero 恰 1、hp = 入参;敌投影序 = 行主序生成序;再 spawn → false + 诊断、容器不变(相位守卫) | Rule 2/9/10 |
| AC-2 | 默认根、全新实例 | hero_hp ∈ {M_h+1, 0, −1} 三腿;随后 hero_hp = 1 | 越界三腿均 false + 诊断且双侧恒空(拒绝原子性、无半实例化);域内下界 → true——拒绝域不扩大 | EC16 / Rule 9 |
| AC-3 | spawn 完成(bugbear 坐标) | 读初拍;连续 advance;goblin 坐标连续 3 次 advance;任意长 advance/受击序列 | 初拍 = p_e[0];轨迹 = p_e[k mod 环长]、环尾回环首;环长 1 恒 attack(前提 \|p_g\| = 1 按表读值核对,不满足显式 SKIP);任意序列后状态 = 驱动序列的确定函数、除 despawn_all 外无重置入口 | Rule 4 / EC10 / EC11 / F1 |
| AC-4 | 默认根;前提 p_e[1]=charge 且 p_e[2]=heavy(不满足显式 SKIP) | charge 拍后杀死再 advance;heavy 拍 advance 后才死;对 Dead 句柄 apply_damage | heavy 前被杀:hp = 0(溢杀 floor)、advance → false 无诊断、beat 冻结于 heavy 前一拍(重击永不兑现);行动后才死:已推进、指针回绕;Dead 受击 → false + 诊断(无复活) | EC1 / EC9 / F1 |
| AC-5 | 夹具根 enemy_multi_unit(u_c = 3,bugbear 行) | 三单位读初拍;某步 advance(u2) 前杀 u2;全程观测规模 | 三单位初拍恒等(同种同指环首);u2 本步不打、指针冻结,幸存者 beat 连续多步恒相同(同步性质定理——EC2);单位数 ∈ {3,2,1} 严格递减、无增员(EC5 负空间行为面) | Rule 2 / EC2 / F1 |
| AC-6 | 默认根 + AC-5 根 | 逐一致死;杀 hero 后 despawn_all、重复 despawn_all、重新 spawn | 全灭 → query_alive(enemy) = 空(胜利判定素材归 B1);hero hp = 0(失败判定素材);despawn → true、双侧空、失效句柄查询空 + 诊断、作用接口 false;重复 despawn → false + 诊断;重新 spawn → true(Despawned 唯一出口) | Rule 1 / EC3 / EC4 |
| AC-7 | 默认根 spawn(bugbear)、m_amb 表读值 | attack 拍 query_attack(L=2);heavy 拍 query_attack(L=4) 合成序判别;charge 拍 query_attack(L=c_r);多敌根同 L 重复/多单位 query | attack:{baseline = a_e, attach = 2×m_amb, total = a_e + 2×m_amb};heavy:total = a_e×m_e + 4×m_amb 且 ≠ (a_e + 4×m_amb)×m_e(后置加法判别;m_e = 1 时负断言恒等、显式 SKIP);charge:恒 {0,0,0}(不读池——EC8 的 B2 半);同 L 多读逐字相同、多单位读值一致(共享不消耗)、query 前后投影零变化(纯函数——EC17 读值半) | F2 / Rule 6 / EC8 / EC17 |
| AC-8 | 默认根、c_r = cap 表读值 | ambush_grant_plan 参数化:mines = c_r+2;mines ∈ {0, 1, c_r, c_r+1};mines ∈ [5, 8];cap = 0 | granted = min(mines, c_r)、守恒 granted + overflow = mines(零静默蒸发);灾难轮 granted = 4、overflow = mines − 4(EC7);cap = 0 → granted = 0、overflow = m(EC6 免疫全溢出可见) | F3 / EC6 / EC7 |
| AC-9 | 夹具根 enemy_multi_unit | advance 序构造混合拍读暴露;记号恒等式断言 | 暴露 = k × L × m_amb,k 经只读观测量导出(确定性可算、无隐藏状态);全 charge 轮 k = 0(EC8);结构上界 = u_c × min(AP_r, c_r) × m_amb 记号恒等式(全参表读值/夹具登记值,非定值断言);无跨结算累计观测量(负空间) | F4 / EC8 |
| AC-10 | 腿 1 默认根(u_c=1);腿 2/3 多敌根 | default_target();杀 [0] 后再查;连续杀至空 | u_c=1 → 唯一存活单位(合法退化——EC15);多敌 → query_alive(enemy)[0](顺位快照 = 生成序);杀最前后顺延;杀至空 → null | Rule 7 / EC3 / EC15 |
| AC-11 | 默认根 spawn 完成、存活与 Dead 并存 | 测试通道换根加载 enemy_reloaded(非 D2 reload() 路径);despawn 后同坐标重新 spawn;原地改写返回的 pattern 投影 | 已实例化单位快照五字段 + beat + hp 逐字不变、hero 投影不变(快照抗热重载——EC12);新实例 = 漂移后表值;投影为拷贝非活引用 | Rule 10 / EC12 |
| AC-12 | EnemyTable schema 类直引实例 | validate() 作用于越域代表值(unit_count {0,4}、hp {0,1000}、atk {−1,1000}、mult {0,17}、环长 {0,9}、预留值 "guard"、未知枚举、cap {−1,17})与域内代表值(含 atk=0、cap=0 两合法极端、环长两界);表级构造同坐标两行相异 enemy_id | 越域各含对应域错误类别(零静默);表级含每行集恰一敌种错误类别;域内零诊断(防过度约束锁死内容——atk=0 与 cap=0 是承重点) | Rule 3/4 / EC6 |
| AC-13 | HeroTable schema 类直引实例 | validate():max_hp {0,1000}、atk {−1,0,1000} 越域;max_hp/atk ∈ [1,999] 域内;表级构造两行 | 越域各含域错误类别(atk=0 拒绝 = 基础攻击恒在的 schema 承重腿);域内零诊断;两行 → 单行全局约束错误(落点 = schema 或可选根 hero_extra_row manifest,两处恰一承载) | Rule 9 |
| AC-14 | 默认根加载完成 | 读 EnemyTable;读 HeroTable | 敌种 id 集合 = {goblin, bugbear, ogre_chief}(定值断言);每行集恰一敌种;pattern 元素 ⊆ {attack, charge, heavy};goblin 行 pattern 不含 "heavy";unit_count_max 仅在区间行出现;display_name 非空(本地化归 D6);遭遇分布不作定值断言(C3 域 knob);HeroTable 恰 1 行、atk ∈ [1,999](MVP 值 6 provisional 不作定值断言)、max_hp ∈ [1,999] | Rule 3/9 |
| AC-15 | 全新实例 ×2(同默认根) | 同一定长序列驱动两遍(spawn → 受击含轮到前击杀 → 交替 advance → query 投影 → default_target) | 两遍的 query_alive 投影序列、逐单位 beat 轨迹、default_target 返回序列逐项相同——零随机行为级硬证据(rng_service 流封闭) | Rule 4/11 / F1 |
| AC-16 | 默认根加载完成 | 脚本解析 design/registry/entities.yaml 的 enemy 与 hero 条目逐字段对账 | ① enemy.fields 十键与 EnemyTable schema 字段名/类型/域逐一相等;② hero.fields 两键同上;③ enemy 注记物种集 = {goblin, bugbear, ogre_chief} 与表行集合相等;④ status_effect.ambush 含 B2 量纲定稿注记(活值 m_amb 不作定值对账——knob 纪律);⑤ hero"单行全局豁免"与 enemy"每行集恰一敌种校验"注记存在 | Rule 6 / registry |
| AC-17 | B2 文件集纳入 D2 AC-9 模式项目级扫描域 | 静态扫描 | randomize() / RandomNumberGenerator / ConfigDB 外 data 表加载 / extends Control / .tscn preload 零命中(schema 与容器 class_name 直引实例化豁免);事件订阅 API 零命中;B3 API 符号零引用(相邻零运行时依赖);零随机行为级证据 = AC-15 | Rule 11 / D2 AC-9 |
## 版本历史
| 版本 | 日期 | 改动 |
|---|---|---|
| v2 | 2026-09-14 | 两件套迁移:按模板 v2 重排——design 只收结论,公式推导、数值锚定论证、候选取舍、体验锚点论证移至 `design/gdd/analysis/b2-enemy-system.md`;规则/数值/AC/EC/F/OQ 编号与内容全保留,零设计变更 |
| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;规则/枚举/边界情况/义务清单/覆盖矩阵/AC 转表格;行为环转字符画;公式、Variables 与验算数据全保留;语义与 2026-09-09 定稿及 2026-09-11 基础攻击裁决对齐,零规则变更 |
| 基础攻击裁决批 | 2026-09-11 | HeroTable.atk = 基础攻击恒存在:域 [1,999]、0 加载期校验拒绝(零基线死锁自 schema 层杜绝)、MVP = 6(原型锚 provisional 归 C3);主角每结算战斗段先手攻击;B4 道具/增益转为规划面伤害(非唯一伤害来源);原"atk MVP=0 机制开关、胜利路径依赖 B4 先行"条款与 OQ3 左臂悬空投影退役;OQ9 恒零死局分支随之收窄 |
| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-7 回执,registry 同批):**spawn 增 override 参数 n**(B1 encounter 骰定本战实际数量——区间语义 [unit_count, unit_count_max] 实例化后不增,EC5 记号调和)· **Rule 10 DifficultyTable 接口已定**(registry `difficulty_table`——施加点 = 行值快照前套乘,MVP 空表零修饰)· **EnemyTable schema +2 字段**(unit_count_max / armor_type——护甲克制批,克制矩阵归 SettlementTable)· **物种集扩容三敌种 {goblin, bugbear, ogre_chief}**(AC-14/AC-16① 十键/AC-16③/结构定值断言随批;DND 更名批对齐)· 默认根遭遇分布 = C3 行值总表 v3(provisional) |
| Rev 1(full 评审批) | 2026-09-09 | lean 定稿同日 full 评审(六专员 + CD 终裁)8 项 blocking 同日核销后用户拍板收口(A3/A4/A1/B3 同日核销先例、免独立 re-review):① 胜利路径声明 ② AC-7 恒假判别前提 + SKIP ③ 多敌夹具根改 bugbear 行 ④ OQ6 事件源类更正 + F3 重验触发补支 ⑤ 精英/Boss 编码位预登记 OQ7 ⑥ Rule 8 预告时序 ⑦ 资产下限补受击/死亡/顺位 ⑧ 主角推进域排除;同批定稿四项口径:cap 默认 4(旧默认 8 使溢出协议不可达)、死亡即不动手、条款 3 单结算自然边界、C 节三处回写;F2 判别改域内值、F4 结构上界 min 收紧、方向性预检裕度改 2.3×——明细见 design/gdd/reviews/b2-enemy-system-review-log.md |
| v1(lean 定稿) | 2026-09-09 | 一次会话定稿:十一核心规则 + F1–F4 + 17 AC + 义务台账(B1×6 / D1×4);九项结构决策用户拍板(多敌 / HeroTable / 视觉推测型 / Peglin 行为环 / 每行集恰一敌种 / side-wide / 固定顺位 / unit_count 字段 / cap=4);lean 门禁:systems-designer 起草 Formulas、qa-lead 起草/验证 AC |