merge(关卡节点图重构写卡批): 四张需求卡片+已写卡状态回写,机检 12 卡口径全过
This commit is contained in:
commit
e364814294
5 files changed
+1088
-1
No files matched your search
@@ -0,0 +1,244 @@
|
||||
# 需求卡片:信息迷雾可见性批(关卡节点图重构卡④——选路信息可见性落地)
|
||||
|
||||
- 版本:**v1.0**(2026-10-21 成文;grilling 复核结论见 §11)
|
||||
- 来源:[待办清单](../待办清单.md)「关卡节点图重构——可选节点太少」(2026-10-13 登记,设计面=文档轨)(唯一事实源:① [关卡节点地图](../design/gdd/systems/冒险循环/关卡节点地图.md) v2.1「选路信息可见性:近处见类型,远处起雾」节+同文调参表「信息可见层数 N」;② [关卡节点图生成](../design/gdd/systems/冒险循环/关卡节点图生成.md) v1.1「下一跳必在可见范围,是决策的可见锚」承诺;呈现口径权威=[level-map](../design/gdd/ux/level-map.md) 头部机制权威行+节点态呈现口径〔视觉终稿冻结件〕)
|
||||
- 归属:关卡节点图重构批**卡④**(可见性规则+呈现接线);卡①生成器/卡②行进换代/卡③敌人表不在本卡
|
||||
- 排期:未编入(2026-10-18 用户令:需求卡片不预占迭代号,批次归属按工作量另裁)
|
||||
- 开工门:U0(用户 `/iter` 发令)+ grilling 复核门过+零待裁点(§11)
|
||||
|
||||
## 0. 前置依赖与并行性
|
||||
|
||||
- 设计闭合凭据=关卡节点图生成.md v1.1+关卡节点地图.md v2.1(2026-10-21),06 条目状态「文档设计已闭合」——第〇步放行成立。
|
||||
- 本卡消费的节点图数据形态=现行 `level.tres` 模板池(type/node_depth/next 三键);卡①生成器落地图后数据形态不变(生成侧只换图的来源),本卡消费面零随动。
|
||||
- **06 状态回写呈报**:条目状态应推进为「已写卡(`reports/需求卡片-信息迷雾可见性批-2026-10-21.md`)」——本会话受「只许创建/修改本卡一个文件」约束未落,开工前须由用户补记或授权写卡方补记(登记不阻塞,防遗忘义务在此显式承载)。
|
||||
- 与在役结构的关系:`LevelRunner`(core)与 `LevelMapScreen`(presentation)均在役,本卡只扩只读出口与呈现决策面,不改 autoload 链、不改行进/兑付流程。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标**:把关卡节点地图 v2.1「选路信息可见性」三层语义落成可见性规则+最小显隐呈现——图结构全图明牌(节点、连线、入口、终点位置永远可见)/节点类型仅在邻近 N 层内可见、范围外信息迷雾只见结构不见类型/节点内详情(打谁、给什么)图上从不显示;Boss 标记与终点身份不入雾。N 进数据表(provisional 挂值,数值定值归定值批)。呈现接线三件:迷雾下节点显隐语义、多终点 Boss 标记、画布自适应(图变大布局跟上)。
|
||||
|
||||
**相对现状,新增了什么**:
|
||||
1. 可见性规则(现役图上类型全明牌、零过滤——见 F2):`LevelRunner` 只读出口 `type_visible_ids()`;
|
||||
2. N 参数落数据表(现役无此字段——见 F6):`LevelTable.info_visible_layers`(int ≥ 1,provisional 2);
|
||||
3. 迷雾呈现显隐接线(现役图标/类型文字无条件绘制——见 F2):`node_display` 决策面+`_draw_graph` 消费;
|
||||
4. 多终点 Boss 标记接线(渲染循环已通用,雾豁免与夹具断言补齐——见 F7);
|
||||
5. 画布自适应断言面(公式已通用,大图回归断言与探针常数锚同源补齐——见 F3)。
|
||||
|
||||
**完成定义**(全部满足才算本批完成):
|
||||
1. 邻近 N 层内节点类型可见;层距 > N 的非豁免节点只见结构(方块态色/编号/连线)不见类型(图标+类型文字);N 读数据表、int ≥ 1;
|
||||
2. Boss 标记与终点身份恒可见(多终点图每个终点均带标记且均不入雾);当前候选(下一跳)恒可见类型;
|
||||
3. 节点内详情图上零显示(回归断言在案:决策面不消费内容字段);
|
||||
4. 迷雾呈现=显隐语义,零新画法零灰化遮罩;冻结视觉件零改动;
|
||||
5. 单圈语义:可见性纯函数于当前锚点,已过节点出圈回雾(零累积记忆);
|
||||
6. 画布自适应对大图成立(包络覆盖最深节点、命中可达)+探针常数锚与屏内公式同源;
|
||||
7. 断言批全绿+变异验证记录齐;三份 `reports/` 产出物写齐(T5);批末代码域闸全绿+改前改后读数对拍。
|
||||
|
||||
## 2. 现状事实(2026-10-21 worktree 实查;开工日 T0 复核行号)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| F1 | 节点态四色+Boss 描边在 `_draw_graph`:已过=COL_ROOM_DONE(暗金 #68562C)/当前=COL_ROOM_CURRENT(金)/未达=COL_ROOM_FUTURE(暗);Boss=紫赤 2px 描边+外缘 1px 深墨环(灰度冗余通道);候选=亮金描边(悬停 4px/常态 2px) | `game/presentation/views/level_map_screen.gd:10-15`/`:450-464`(态色 `:450-454`、Boss `:457-461`、候选 `:462-464`) |
|
||||
| F2 | 节点类型三通道全明牌、零可见性过滤:字形回退 NODE_LABELS(战/赏/疗/王/商)、类型文字 NODE_TYPE_NAMES、五键图标 TEX_NODE_ICONS;每个节点无条件绘制图标(或字形)+编号+类型文字标签(当前位转「当前位置」、候选加「· 可选」) | `level_map_screen.gd:17-20`/渲染点 `:467-475`(图标/字形)、`:477-479`(编号)、`:482-489`(类型文字) |
|
||||
| F3 | 画布自适应已公式化通用:布局常数 NODE_HALF=36.5×25(52×36 的 1.4×)、LAYER_BASE_X=70/LAYER_STEP_X=122/SLOT_BASE_Y=52/SLOT_STEP_Y=58;`_fit_canvas` 包络=宽 70+最大层×122+NODE_HALF.x+8 × 高 52+最大槽×58+NODE_HALF.y+24,下限 620×256;`_layout` 与命中 `_candidate_at` 同源 | `level_map_screen.gd:25-29`/`:289-304`/`:492-501`/`:373-386` |
|
||||
| F4 | 呈现只读出口在役:`nodes()`/`next_candidate_ids()`/`current_node_id()`/`node_id_of()`/`node_out_ids()`/`visited_ids()`;无可见性出口(本卡新增) | `game/core/level_runner.gd:327`/`:388`/`:396`/`:401`/`:405`/`:410` |
|
||||
| F5 | 编号空间=图形态 node_depth/单链数组下标(`_node_id`);层=入口 BFS 深度(`_assign_layers` 分层=画布列同源),同层 slot 垂直排布 | `level_runner.gd:340-343`/`level_map_screen.gd:517-542` |
|
||||
| F6 | 数据面无 N 字段:LevelTable 仅 `rows`;rest_heal_pct 越域校验先例([0,100] domain 错)可循;level.tres 三行各带 rest_heal_pct=30 | `game/data/schemas/level_table.gd:7`/`:35-37`/`game/data/tables/level.tres` |
|
||||
| F7 | 图合法性现役「Boss 恰一」(boss_count != 1 → structural 错)——多终点图现役被加载拒;但渲染循环逐节点判 `type == "boss"` 已天然多 Boss 通用 | `game/data/schemas/level_table.gd:80-81`/`level_map_screen.gd:457-461` |
|
||||
| F8 | 冻结件现状:level-map 视觉终稿已冻结(2026-09-28 样张 v1 机检 11/11);画布冻结公式「宽 = 70+层深×122+34、高 = 52+槽位×58+26+8」与代码现值漂移(代码宽尾 44.5=NODE_HALF.x+8、高尾 49=NODE_HALF.y+24;节点 1.4× 化+标签行 +24 后所致,注自述「层距/布局公式零改」) | `design/gdd/ux/level-map.md:3`/`:68`/`level_map_screen.gd:25`/`:301-303` |
|
||||
| F9 | Boss 标记口径两文档不一致:关卡节点地图.md:67 写「Boss 封印黑描边(旗朱→封印紫→封印黑 #000000)」,level-map.md 写「紫赤描边+深墨环」;**代码实况=后者**(紫赤 BOSS_PURPLE 描边 2px+INK_ROUTE 深墨外环 1px) | `design/gdd/systems/冒险循环/关卡节点地图.md:67`/`design/gdd/ux/level-map.md:77`/`:113`/`:175`/`level_map_screen.gd:457-461`/`game/presentation/views/ui_kit.gd:19`(INK_ROUTE)、`:43`(BOSS_PURPLE) |
|
||||
| F10 | 节点内详情现状已零显示:`_draw_graph` 只消费 type/节点 id/访问态三类信息,图上无敌人行集、奖励内容渲染——「图上从不显示」现状成立,缺回归断言 | `level_map_screen.gd:420-489` |
|
||||
| F11 | 画布拟合探针常数锚手抄数字与屏内公式漂移:探针 `need_x2 = 70+maxl×122+26+8`,屏内公式尾为 NODE_HALF.x+8=44.5——探针断言被低报 10.5px 仍绿(自述「改常数须两侧同步」) | `game/smoke/ui_input_probe.gd:98-103`/`level_map_screen.gd:301` |
|
||||
|
||||
## 3. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 可见性三层语义不破**:图结构(节点、连线、入口、终点位置、编号)全图明牌;节点类型=邻近 N 层内可见、范围外只见结构不见类型;节点内详情图上从不显示(关卡节点地图.md:97-101 三层表逐行兑现)。
|
||||
|
||||
🔴 **契约 2 · Boss/终点/候选三豁免**:Boss 标记与终点身份不入雾(豁免集=type=="boss",终点恒为 Boss);当前候选(下一跳)恒可见类型——兑现生成.md:83「下一跳必在可见范围,是决策的可见锚」承诺(直接兑现、不押注图形态)。
|
||||
|
||||
🔴 **契约 3 · N 只进数据表**:`info_visible_layers`(int ≥ 1)落 LevelTable,provisional 挂值 2;代码禁硬编码 N、禁绕表读值;数值定值权归定值批(调参表「provisional,定值批落数据表」)。
|
||||
|
||||
🔴 **契约 4 · 显隐语义、零新画法**:迷雾节点=类型图标+类型文字不绘制(显隐),结构通道(方块态色/编号/连线)照常;**禁灰化遮罩、禁雾纹理、禁任何新色值/线宽/形制**——雾的正式视觉规格属冻结件域,须用户裁(呈报项 §5),本卡只做可见性语义的最小呈现。
|
||||
|
||||
🔴 **契约 5 · 单圈语义**:可见性是当前锚点的纯函数(零累积记忆)——已过节点出圈回雾;锚层=当前节点层,入口停点(node_index=-1)锚层=0。
|
||||
|
||||
🔴 **契约 6 · 画布自适应不动冻结件**:公式族自适应保留(图变大布局跟上=包络随层深/槽宽增长);完整可见性由生成参数档背书(图包络 ≤ 屏预算,进反哺点移交卡①参数档);**禁缩放/滚动等动冻结视觉件手段**(若定值批需要超预算图,另行走呈报由用户裁)。
|
||||
|
||||
🔴 **契约 7 · 呈现只读不破**:可见性一律经 `LevelRunner.type_visible_ids()` 只读出口(纯函数、零状态、断点快照零新增字段);呈现层禁自算层距、禁触碰 runner 写面(level-map.md 架构注记)。
|
||||
|
||||
🔴 **契约 8 · 范围锁**:不动生成器/生成参数档/图合法性放宽(卡①域)、不改行进(卡②域)、不改敌人表(卡③域)、不改 N 数值(定值批)、不改冻结视觉件(用户域)。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
**做**:可见性规则(层距 N+三豁免+单圈);N 落 LevelTable+level.tres;雾显隐呈现接线(`node_display` 决策面+`_draw_graph` 消费+探针);多终点 Boss 标记雾豁免接线+夹具断言;画布自适应大图断言+探针常数锚同源化;断言批(单测+冒烟+变异验证记录);反哺点清单。
|
||||
|
||||
**不做**(明示去向):
|
||||
- 生成器、生成参数档、图合法性「Boss 恰一→多终点」放宽 → 卡①(生成域规则语义以生成产出规格为准);本卡呈现面按多 Boss 通用实现+合成夹具断言,卡①数据到位即接通;
|
||||
- 行进换代(Peglin 式移动/选路方式) → 卡②;
|
||||
- 敌人表 (level_index, node_depth) 行集随动 → 卡③;
|
||||
- N 的数值定值 → 定值批(本卡只挂 provisional 2);
|
||||
- **雾的正式视觉规格**(雾纹理/灰化遮罩/明度层) → 呈报项,用户裁冻结件后另立(level-map 视觉终稿已冻结,禁自行重开);
|
||||
- level-map.md/GDD 正文改写(含 F8 公式漂移、F9 Boss 口径不一致的文档修正) → 文档批(走 §5 反哺点)。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
> **口径(2026-10-17 文档同步闸 v3)**:本批是代码实现批,禁写 `design/` 整目录与根规则文件,唯一可写目录 = `reports/`。下表「同步义务」一律执行为「列进 `reports/iter<N>-反哺点-<完成日>.md`:哪个文件哪一节、改成什么、依据,用户会话裁决后归文档批落文」。禁写「随批」。
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/gdd/systems/冒险循环/关卡节点地图.md` | :172 调参表 N「定值批落数据表」→ 本批已落表(provisional 2);:100「可见范围(邻近 N 层)」的实现语义锚定(层距口径=入口 BFS 层差、三豁免、单圈回雾)需入文固化 | 反哺点 · 列清单(调参表状态行改「已落表 provisional 2」+可见性节补实现语义注记) |
|
||||
| `design/gdd/systems/冒险循环/关卡节点图生成.md` | 调参表(:105-114)补「图包络 ≤ 屏预算」约束行(契约 6 依据:完整可见 AC 冻结 ⇒ 参数档背书);:83「下一跳必在可见范围」标注实现侧已兑现 | 反哺点 · 列清单 |
|
||||
| `design/gdd/ux/level-map.md` | :68 画布公式与代码现值漂移核对(34→44.5/26+8→25+24);Information Hierarchy 补「类型可见性(邻近 N 层显隐)」层级行;**雾的正式视觉规格待用户裁后另立(呈报项,禁本批落文)** | 反哺点 · 列清单+呈报项登记 |
|
||||
| `design/gdd/术语表.md` | 新词入册:「信息迷雾」(关卡节点图类型可见性)区别于「视野迷雾」(战斗棋盘,HUD框架/art-bible §6.5)——两系统同词根易混 | 反哺点 · 列清单 |
|
||||
| `design/gdd/systems/冒险循环/关卡节点地图.md` :67 | Boss 描边口径「封印黑 #000000」与 level-map.md/现役代码「紫赤+深墨环」不一致(F9)——文档批核对择一 | 反哺点 · 列清单 |
|
||||
| `待办清单.md` | 06 条目状态 →「已写卡(卡片路径)」 | **写卡段义务**(非实现批)——本会话单文件约束未落,开工前补记(§0 呈报) |
|
||||
| `roadmap.md` | 迭代行编入笔与批内状态刷新 | 反哺点 · 列清单 |
|
||||
| `game/core/level_runner.gd`、`game/data/schemas/level_table.gd`、`game/data/tables/level.tres`、`game/presentation/views/level_map_screen.gd`、`game/smoke/ui_input_probe.gd`、`game/smoke/smoke_all.gd`、`game/tests/**` | 实施本体 | 实施本体(可写) |
|
||||
|
||||
## 6. 任务卡
|
||||
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核本卡全部行号。
|
||||
|
||||
### 卡前·文件结构图
|
||||
|
||||
| 文件 | 职责 | 动作 |
|
||||
|---|---|---|
|
||||
| `game/data/schemas/level_table.gd` | N 字段承载+越域校验 | 改 |
|
||||
| `game/data/tables/level.tres` | N provisional 挂值 | 改 |
|
||||
| `game/core/level_runner.gd` | 可见性只读出口 `type_visible_ids()` | 改 |
|
||||
| `game/presentation/views/level_map_screen.gd` | 雾显隐决策面 `node_display`+`_draw_graph` 消费+`display_probe` | 改 |
|
||||
| `game/smoke/ui_input_probe.gd` | 拟合探针常数锚同源化(去手抄数字) | 改 |
|
||||
| `game/smoke/smoke_all.gd` | 真图可见性冒烟断言 | 改 |
|
||||
| `game/tests/unit/core/level_map_type_vis_test.gd` | 可见性规则单测 | 新建 |
|
||||
| `game/tests/unit/presentation/level_map_info_fog_test.gd` | 显隐呈现+多 Boss+详情不显示单测 | 新建 |
|
||||
| `game/tests/run_all.gd` | 两套件注册进 manifest | 改 |
|
||||
|
||||
### T0 事实坐实与基线读数(≈0.5–1d)
|
||||
|
||||
- [ ] 复核 §2 全部行号与读数,漂移即订正本卡(产出:本卡 §2 订正或维持记录)。
|
||||
- [ ] 跑 `godot --headless --path game --script res://tests/run_all.gd` 取改前基线读数(套件数/失败数)写进实现报告 T0 段。
|
||||
- [ ] 跑 `tools/run_all_gates.sh` 按改动域选档取改前闸读数写进实现报告 T0 段。
|
||||
|
||||
### T1 N 落表+核心可见性出口(≈1–1.5d)
|
||||
|
||||
**接口声明**——产出:`LevelTable.info_visible_layers: int`(默认 2,validate 越域 domain 错);`LevelRunner.type_visible_ids() -> Array[int]`(语义见下)。取用:无(首卡)。
|
||||
|
||||
- [ ] `level_table.gd` 在 `rows` 声明(:7)后新增 `@export var info_visible_layers: int = 2`——核查:字段在案、类型 int。
|
||||
- [ ] `level_table.gd` `validate()`(:20 起)增越域检查:`info_visible_layers < 1` 时 append `{"category": "domain", "message": "info_visible_layers 越域 int≥1: %d"}`——核查:单测断言面可判红。
|
||||
- [ ] `level.tres` 资源顶层挂 `info_visible_layers = 2`——核查:`db.load_tables` 零错+读数 2。
|
||||
- [ ] `level_runner.gd` 新增只读出口 `func type_visible_ids() -> Array[int]`——语义钉死:返回集 = Boss/终点 id(type=="boss")∪ `next_candidate_ids()` ∪ {id : |层(id) − 锚层| ≤ `db.level.info_visible_layers`};锚层=当前节点层,无当前节点(入口停点)=0;层=入口 BFS 深度(与 `level_map_screen._assign_layers` 同源口径);纯函数零状态、断点快照零新增字段——核查:套件可调用且边界断言绿。
|
||||
- [ ] 新建 `game/tests/unit/core/level_map_type_vis_test.gd`(extends SceneTree+Kit.check 断言,仿 curio_refresh_paid_test 样式;断言 desc 尾缀挂 §7.1 对照表:①⑤→〔fv1〕、②→〔fv4〕、③→〔fv3〕、④⑦→〔fv7〕、⑥→〔fv6〕),七组断言:① 层距 N 内可见/N+1 不可见(白盒注入 level_row+停点态,仿 smoke_all ⑫ 白盒先例);② 候选恒显(夹具含跨层跳边候选——候选层距恰 N+1 仍显);③ Boss 恒显(远距 Boss);④ 单圈回雾(已过远节点回雾、visited 不豁免);⑤ 入口停点锚层 0+**单链退化形态**(L1 现役无 `next` 键图上层距判定同成立——出边按 `_out_ids` 数组顺序退化口径);⑥ N 读表(改 `db.level.info_visible_layers` 为 1/3 → 可见窗随动;N ≥ 全图层高 → 全部非豁免节点可见)+越域 validate 判 domain 红;⑦ 续玩重建同值(`start_level_external` resume_state 重建停点后 `type_visible_ids()` 与同停点直入路径逐位一致)——核查:套件退出码 0。
|
||||
- [ ] `run_all.gd` `_suites()` 增一行 `{"path": "res://tests/unit/core/level_map_type_vis_test.gd", "expect": 0}`——核查:run_all 套件计数与 manifest 条目数一致。
|
||||
- [ ] 变异验证:把层距判定 `<= N` 改成 `< N` 跑该套件见边界断言红 → `git checkout` 精确还原 → 记录进实现报告变异表(注入形态/闸/红输出/还原结论)。
|
||||
|
||||
### T2 雾显隐呈现接线(≈1–1.5d)
|
||||
|
||||
**接口声明**——取用:T1 的 `type_visible_ids()`。产出:`LevelMapScreen.node_display(node, nid, type_vis, is_current, is_candidate) -> Dictionary`(static 纯函数)+`LevelMapScreen.display_probe() -> Dictionary`。
|
||||
|
||||
- [ ] `level_map_screen.gd` 新增 `static func node_display(node: Dictionary, nid: int, type_vis: Array, is_current: bool, is_candidate: bool) -> Dictionary`——返回 `{"draw_icon": bool, "draw_label": bool, "label_text": String, "boss_border": bool}`;语义钉死:`boss_border = (type == "boss")`;Boss 节点 icon/label 恒 true;非 Boss 节点 `draw_icon = draw_label = type_vis.has(nid)`;`label_text`=现役文案规则(当前位「当前位置」/候选「<类型名> · 可选」/余「<类型名>」)——核查:纯函数单测可直调。
|
||||
- [ ] `_draw_graph` 消费 `node_display`(每停点一次取用 `type_vis = _runner.type_visible_ids()`;图标/字形 `:467-475`、类型文字 `:482-489`、Boss 描边 `:457-461` 三处改读决策面输出);结构通道(方块态色/编号/连线)零改动——核查:现役图渲染冒烟不红。
|
||||
- [ ] `level_map_screen.gd` 新增 `func display_probe() -> Dictionary`(id → `node_display` 快照,只读断言面,仿 `canvas_probe` 先例)——核查:探针可调用。
|
||||
- [ ] 新建 `game/tests/unit/presentation/level_map_info_fog_test.gd`(断言 desc 尾缀挂 §7.1 对照表:①④→〔fv2〕、②→〔fv3〕、③→〔fv5〕、大图组→〔fv8〕),四组断言:① 显隐(雾下节点 draw_icon=false 且 draw_label=false,boss_border=false;可见节点全 true);② Boss 豁免+多 Boss 标记(双 Boss 夹具:两节点 boss_border=true 且均在 type_vis);③ 详情不显示(夹具节点额外携带内容键如 `enemy_id/reward_id`,断言 label_text 不含其值且决策面输出与不带内容键的同型节点逐键相等);④ label_text 文案三态(当前位置/· 可选/类型名)——核查:套件退出码 0。
|
||||
- [ ] `run_all.gd` 注册该套件(expect 0)——核查:manifest 计数一致。
|
||||
- [ ] 变异验证两发:① 雾下 `draw_icon` 强制 true → ①断言红;② label_text 拼接 `node.get("enemy_id")` → ③断言红;逐发「注入→最窄闸见红→checkout 还原→记录」。
|
||||
|
||||
### T3 画布自适应接线与断言(≈1d)
|
||||
|
||||
**接口声明**——取用:无新接口(断言面;大图夹具自建 stub runner)。产出:无新接口(探针常数锚同源化)。
|
||||
|
||||
- [ ] `ui_input_probe.gd:98-99` 常数锚去手抄数字,改引 `level_map_screen.gd` 导出常量(`LAYER_BASE_X`/`LAYER_STEP_X`/`NODE_HALF`)——核查:探针绿且 `need_x = LAYER_BASE_X + maxl×LAYER_STEP_X + NODE_HALF.x + 8` 与屏内 `_fit_canvas` 公式逐项同源。
|
||||
- [ ] `level_map_info_fog_test.gd` 增大图夹具断言组:合成 11 层×6 槽含双 Boss 图(stub runner 供 `node_out_ids`/`node_id_of`),断言 `canvas_probe()["min_size"].x ≥ 70+最大层×122+NODE_HALF.x+8`、`min_size.y ≥ 52+最大槽×58+NODE_HALF.y+24`,且 `layout_probe` 全节点位置落在画布矩形内(routebug 83c11a1 同类回归)——核查:断言绿。
|
||||
- [ ] 变异验证:`_fit_canvas` 宽公式去 `NODE_HALF.x` 项 → 大图断言红 → `git checkout` 还原 → 记录。
|
||||
|
||||
### T4 冒烟补断言+变异汇总(≈1–1.5d)
|
||||
|
||||
**接口声明**——取用:T1 `type_visible_ids()`、T2 `display_probe()`(真图消费面——补上探针的实机调用方)。产出:无新接口。
|
||||
|
||||
- [ ] `smoke_all.gd` `_smoke_level_graph` 段(:4552 起)末增真图可见性断言组(纯核心面,无需场景):L2 图入口停点 `type_visible_ids()` ⊇ `next_candidate_ids()`、boss id ∈ 集合、层距 > N 的非豁免 id ∉ 集合(N 取 `db.level.info_visible_layers`)——核查:冒烟绿。
|
||||
- [ ] `ui_input_probe.gd` 增 case⑱(图上可见性接线回归,挂在 ⑫ 图上命中之前的入口停点处):经 `ro.display_probe()` 断言真图雾下节点 draw_icon=false、Boss 节点 boss_border=true、候选节点 draw_icon=true;并以 push_input 点击一个雾下非候选节点,断言零提交(选路态不变——雾不扩大可点域,候选集即合法集纪律不破)——核查:探针绿。
|
||||
- [ ] T1–T3 变异验证记录表成文(每条新断言:注入实验/跑的闸/红输出/还原结论)进实现报告——核查:记录表覆盖全部新增断言、零「未证」条目。
|
||||
- [ ] 里程碑步出口跑一次代码域闸全套(冒烟+单元+探针+渲染/静态)+改前改后读数对拍——核查:全绿+对拍记录在案;**渲染闸含 shot_probe level 屏截图+shot_checker A-D 判据(屏级骨架/文字对比度/明度三档带),雾显隐后仍须全绿**;卡内修复只做出红最小单元的最窄确认。
|
||||
|
||||
### T5 产出物落位卡(≈0.5d,末卡——三份 `reports/` 文件写齐+独立验收审计通过才算收口)
|
||||
|
||||
- [ ] `reports/iter<N>-实现报告-<完成日>.md`:各卡状态+变异验证记录表+**实际改动—验收对照表**(§7.1 需求侧表的执行对账,另加「结果」格)+**实机目检总清单**(观测项三条:雾下可读性观感/多终点图 Boss 标记观感/大图完整可见观感——登记不阻塞,验收时间由用户自行安排)+遗留读数义务。
|
||||
- [ ] `reports/iter<N>-代拍板追认单-<完成日>.md`:§9 白名单内代拍项留痕(provisional N=2、API 命名等)。
|
||||
- [ ] `reports/iter<N>-反哺点-<完成日>.md`:§5 每条一落点(含呈报项「雾的正式视觉规格」+「超预算图策略」升级路径)。
|
||||
- [ ] 合并前置:独立验收审计(§7.1 末段声明)完成且报告 `reports/iter<N>-验收审计-<完成日>.md` 逐行「通过」——未通过不得合并,缺漏项回炉对应 T 卡。
|
||||
|
||||
**估时合计 ≈4.5–6d**。切分预案:T1/T2 可切(核心出口先落、呈现接线后落);T3 独立可切;T4/T5 须连跑——建议单批跑完。
|
||||
|
||||
## 7. 验证闸(逐需求点验收清单)
|
||||
|
||||
| # | 需求点 | 测什么(断言内容) | 落点 | 变异注入实验 |
|
||||
|---|---|---|---|---|
|
||||
| V1 | 邻近 N 层见类型 | 层距 ≤ N 的 id ∈ `type_visible_ids()`、层距 N+1 的非豁免 id ∉;入口停点锚层 0 | `level_map_type_vis_test` ①⑤ | 判定 `<=N` 改 `<N` → 边界断言红 |
|
||||
| V2 | 范围外只见结构 | 雾下节点 draw_icon=false、draw_label=false;方块/编号通道不受决策面约束(决策面不输出结构隐藏旗标) | `level_map_info_fog_test` ① | `draw_icon` 强制 true → 红 |
|
||||
| V3 | Boss/终点不入雾+多终点标记 | type=="boss" 节点恒 ∈ 可见集且 boss_border=true(双 Boss 夹具逐个验证) | 两套件各一组 | 删 Boss 豁免分支 → 红 |
|
||||
| V4 | 下一跳恒可见 | 候选集 ⊆ 可见集(含跨层跳边夹具);真图 L2 入口停点候选 ⊆ 可见集 | 单测 ②+冒烟 | 删候选豁免 → 跨层夹具红 |
|
||||
| V5 | 节点内详情不显示 | 携带内容键节点与不带者决策面输出逐键相等;label_text 不含内容值 | `level_map_info_fog_test` ③ | label 拼 content 字段 → 红 |
|
||||
| V6 | N 进数据表 | 字段在档读数 2;改表值可见窗随动;越域(0/负)validate 判 domain 红 | `level_map_type_vis_test` ⑥ | 删越域校验 → validate 断言红 |
|
||||
| V7 | 单圈语义 | 已过节点出圈回雾(visited 不豁免) | `level_map_type_vis_test` ④ | 改累积可见(visited 恒显)→ 红 |
|
||||
| V8 | 画布自适应 | 大图(11 层×6 槽含双 Boss)包络覆盖最深节点+全节点在画布内+探针常数锚与公式同源 | `level_map_info_fog_test` 大图组+`ui_input_probe` | 去 `NODE_HALF.x` 项 → 红 |
|
||||
| V9 | 反哺点清单 | §5 每条一落点+呈报项两条登记 | 件核对表 | —(件核) |
|
||||
|
||||
**测试方式两分法(2026-10-11 用户定)**:V1–V8 全机检自动化。**用户目检**(进实机目检总清单,登记不阻塞):雾下可读性观感(未知类型房间是否一眼可辨为「未知」)/多终点 Boss 标记观感/大图完整可见观感。**能力验证**可经调试直改数据表 `info_visible_layers`(改 1/3 观察可见窗变化,沿用现役调试门控,不走正常资源积累流程)。
|
||||
|
||||
**闸口径**:轻闸每任务卡出口自跑;批末重闸=`tools/run_all_gates.sh` 按改动域选档(本批改动域=代码域)全绿才合并,语义为「代码域闸全绿(冒烟+单元+探针+渲染/静态)+改前改后读数对拍」。测试执行两入口铁规不破(tests/run_all+smoke)。**回归措辞**:里程碑步(T 卡出口)跑一次全套闸;卡内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
|
||||
|
||||
### 7.1 改动—验收对照表(2026-10-21 验收闭合批新规,test-conventions §9.3)
|
||||
|
||||
> 各新增断言 desc 统一尾缀〔fv#〕(本表「验收测试」列可 grep 定位);撤销/变异证明逐条对应 §7 变异注入实验栏与 T1–T4 变异步(每张任务卡至少一条,test-conventions §9.4)。
|
||||
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+文件:行+尾缀) | 层级 | 真实入口 | 撤销或变异证明 |
|
||||
|---|---|---|---|---|---|
|
||||
| fv1 邻近 N 层见类型 | `game/core/level_runner.gd` 新增 `type_visible_ids()`(层距判定) | type_vis ①层距边界+⑤入口锚/单链退化〔fv1〕 `tests/unit/core/level_map_type_vis_test.gd` | 单元 | `LevelRunner.type_visible_ids()` 直调 | 变异:`<=N` 改 `<N` → 边界断言红 |
|
||||
| fv2 范围外只见结构 | `level_map_screen.gd:467-489` 图标/类型文字改读 `node_display` | info_fog ①显隐+④label_text 三态〔fv2〕 `tests/unit/presentation/level_map_info_fog_test.gd` | 单元 | `LevelMapScreen.node_display` 纯函数 | 变异:`draw_icon` 强制 true → 红 |
|
||||
| fv3 Boss/终点不入雾+多终点标记 | `level_runner.gd` 豁免集含 type=="boss";`level_map_screen.gd:457-461` boss_border 决策面 | info_fog ②〔fv3〕+type_vis ③〔fv3〕(两套件) | 单元 | `node_display`/`type_visible_ids()` 直调(双 Boss 夹具) | 变异:删 Boss 豁免分支 → 红 |
|
||||
| fv4 下一跳恒可见 | `type_visible_ids()` 候选集豁免 | type_vis ②〔fv4〕+smoke_all 真图断言〔fv4〕 | 单元+冒烟 | `type_visible_ids()`/`smoke_all.gd _smoke_level_graph` | 变异:删候选豁免 → 跨层跳边夹具红 |
|
||||
| fv5 节点内详情不显示 | `node_display` 只消费 type/状态(零内容键) | info_fog ③〔fv5〕 | 单元 | `node_display` 纯函数(携带内容键夹具) | 变异:label 拼 `enemy_id` → 红 |
|
||||
| fv6 N 进数据表 | `level_table.gd:7` 后 `info_visible_layers` 字段+validate 越域;`level.tres` 挂值 | type_vis ⑥〔fv6〕 | 单元 | `LevelTable.validate()`/`db.level.info_visible_layers` 读值 | 变异:删越域校验 → validate 断言红 |
|
||||
| fv7 单圈语义 | `type_visible_ids()` 纯函数于锚点(visited 不豁免) | type_vis ④〔fv7〕+⑦续玩重建同值〔fv7〕 | 单元 | `type_visible_ids()` 直调+`start_level_external` resume_state | 变异:改累积可见(visited 恒显)→ 红 |
|
||||
| fv8 画布自适应 | `level_map_screen.gd:289-304` 包络公式(现役保留);`ui_input_probe.gd:98` 常数锚同源化 | info_fog 大图组〔fv8〕+`ui_input_probe` ⑬ | 单元+探针 | `canvas_probe()`/`layout_probe()`(11 层×6 槽夹具)+真机 push_input | 变异:去 `NODE_HALF.x` 项 → 大图断言红 |
|
||||
| fv9 显隐接线(雾不扩可点域) | `level_map_screen.gd:355-386` 命中面零改动(候选即合法集) | `ui_input_probe` ⑱〔fv9〕 | 探针 | 真机 `push_input`+`display_probe()` | 变异:命中面放宽到全节点 → ⑱ 点击零提交断言红 |
|
||||
|
||||
**独立验收审计(test-conventions §9.7,2026-10-21 起)**:合并前由未参与实现的迭代 loop 内子代理(或用户点名会话)审计——只读本卡、实现报告实际对照表、diff 与测试文件,自跑最窄测试抽查,不采信「已全绿」;审计报告落 `reports/iter<N>-验收审计-<完成日>.md`(逐行「通过/缺漏/例外及依据」)。**审计未通过不得合并**(T5 合并前置)。
|
||||
|
||||
## 8. 异常分级
|
||||
|
||||
- **L1(自救)**:断言写法、命名、探针字段名、测试夹具规模微调。
|
||||
- **L2(记录继续)**:雾下编号保留/标签行占位的绘制细节、label_text 措辞微调(4 字上限内)、单测夹具的图形态选择。
|
||||
- **L3(停机呈报)**:任何触碰冻结视觉件的诉求(雾画法/灰化遮罩/缩放/滚动/线宽/色值/形制);N < 1 或改调参表语义;候选被雾遮(决策锚破);把生成器/行进/敌人表改动混入本卡;`type_visible_ids` 需要持久化状态(违反纯函数口径);等比缩放/滚动被要求时须用户裁(契约 6)。
|
||||
|
||||
## 9. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. provisional N=**2**(约束 int ≥ 1;依据:N≥1 是 GDD 硬下限,2=越过下一跳再看一层供岔路两跳规划,再多雾语义弱化;定值归定值批)。
|
||||
2. API/探针命名(`info_visible_layers`/`type_visible_ids`/`node_display`/`display_probe`)——约束:**禁 `fog_` 前缀**(与战斗棋盘「视野迷雾」撞词,code-conventions §2.2 跨层标识符语义单一)。
|
||||
3. 雾下节点编号保留、类型文字隐藏的具体落点写法(约束:结构通道零改动)。
|
||||
4. 大图断言夹具规模(约束:≥ 10 层 × ≥ 5 槽、含双 Boss、含跨层跳边)。
|
||||
5. `ui_input_probe` 常数锚同源化方式(内联引用 vs preload 常量——约束:两侧同源可核查、禁再手抄数字)。
|
||||
6. 提交切分与 worktree 分支命名。
|
||||
|
||||
## 10. 风险与异常处置(已知风险,不许绕过)
|
||||
|
||||
⚠️ **风险 1 · level-map 视觉终稿已冻结(2026-09-28 样张 v1 闸形 A)**:雾的**画法**(视觉规格)动冻结件须用户裁。本卡只做可见性语义的最小呈现(沿用现役明暗/显隐语言,契约 4),「雾的正式视觉规格」(雾纹理/灰化遮罩/明度层)列进 §5 同步面+T5 呈报项,**禁自行重开冻结件**。若实现期发现显隐语义在实机上不可读,登记进实机目检清单呈报,禁临时加视觉件。
|
||||
|
||||
⚠️ **风险 2 · 图超屏预算**:完整可见 AC(level-map.md:197)冻结,若卡①生成图超出公式包络屏预算(1280×800 下约 10 层 × 8 槽),参数限图是不动冻结件的唯一兑现路径(契约 6);若定值批需要更大图,等比缩放/滚动视口两案均动冻结件 → 走呈报由用户裁(T5 反哺点登记升级路径)。
|
||||
|
||||
⚠️ **风险 3 · 术语撞词**:「信息迷雾」(本卡,关卡节点图类型可见性)vs「视野迷雾」(战斗棋盘,board_view/board_cell_unit 在役)——代码命名禁 `fog_` 前缀(代拍板 2),文档侧入术语表反哺点。
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门 = U0。**
|
||||
|
||||
灰色地带均已在卡内定值并写明依据,经 grilling 设计树对抗复核后维持:
|
||||
|
||||
| # | 灰色地带 | 定值 | 依据(可推导) | 淘汰案 |
|
||||
|---|---|---|---|---|
|
||||
| G1 | 「邻近 N 层」度量 | \|入口 BFS 层(node) − 层(锚)\| ≤ N | 调参表量纲「可见距离(层)」+地图以层列组织(LAYER_STEP_X=层间步距) | 跳数距离(量纲不符);node_depth 差(node_depth=寻址 id 非布局层,L2 图 node 7 深度 7/层 3) |
|
||||
| G2 | 候选可见性 | 候选集强制豁免 | 生成.md:83「下一跳必在可见范围,是决策的可见锚」是语义承诺,直接兑现 | 依赖图形态保证(层跳边反例可破坏) |
|
||||
| G3 | 已过节点回雾 | 单圈、零记忆 | GDD 三层表无「已见例外」+「单圈雾」口径明示可见性只依赖当前位置;已过节点不可再达(无回头路),回雾零决策损失 | 已见恒显(累积揭示,非单圈语义) |
|
||||
| G4 | 显隐 or 灰化 | 显隐(类型图标+类型文字不绘制) | 用户范围注「沿用现役明暗/显隐语言」+「不见类型」语义=类型通道不出现;灰化仍泄类型形 | 灰化遮罩(属雾的正式视觉规格=冻结件域,呈报项) |
|
||||
| G5 | N 落位与取值 | LevelTable 表级字段,provisional 2 | GDD「本系统自有参数一项」单参数+AdventureTable.clear_heal_pct 表级先例;2=越过下一跳一层前瞻 | 逐关行字段(无逐关设计需求);硬编码(违反改表即改玩法) |
|
||||
| G6 | 超屏图策略 | 参数限图(生成参数档背书包络 ≤ 屏预算) | 完整可见 AC 冻结+「禁自行重开冻结件」⇒ 不动冻结件的唯一路径 | 等比缩放(动「20px 烘焙 1:1」「2/3/4px 折线」冻结值);滚动视口(新交互范式)——两案均须用户裁,列呈报升级路径 |
|
||||
| G7 | 多终点/多入口校验放宽与层语义归属 | 归卡①(生成域规则语义);本卡层口径按现役唯一入口实现(level_table.gd structural 拦截第二无入边节点),多入口层语义随卡①产出规格定、经反哺点随动 | 关卡节点地图.md:14「图合法性规则语义以关卡节点图生成产出规格为准」;本卡呈现面已多 Boss 通用(F7);现役数据与校验均单入口 | 本卡顺带放宽校验或预设多入口层算法(越界卡①域,违反契约 8) |
|
||||
| G8 | Boss 标记口径(F9 文档冲突) | 实现随现役代码+level-map.md(紫赤+深墨环) | 视觉终稿冻结在案(样张 v1)=实况权威;文档批核对走反哺点 | 按关卡节点地图.md:67「封印黑」改色(动冻结件且与样张冲突) |
|
||||
@@ -0,0 +1,311 @@
|
||||
# 需求卡片:敌人表深度带换代批(坐标寻址 → 深度带寻址)· 关卡节点图重构卡③
|
||||
|
||||
- 版本:**v1.0**(2026-10-21 成文;自审五查+grilling 复核结论见 §11)
|
||||
- 来源:[待办清单](../待办清单.md)「关卡节点图重构——可选节点太少」(2026-10-13 登记,来源=playnote,设计面=文档轨,状态=文档设计已闭合)(唯一事实源:[关卡节点图生成](../design/gdd/systems/冒险循环/关卡节点图生成.md) v1.1+[关卡节点地图](../design/gdd/systems/冒险循环/关卡节点地图.md) v2.1,2026-10-21 双定稿闭合凭据)
|
||||
- 归属:敌人系统数据面与遭遇寻址链(`game/data/schemas/enemy_table.gd`+`game/data/tables/enemy.tres`+`game/core/config_db_resolver.gd`);关卡节点图重构批卡③(卡①生成器/卡②地图行进/卡④迷雾同批)
|
||||
- 排期:未编入(2026-10-18 用户令:需求卡片不预占迭代号,批次归属按工作量另裁)
|
||||
- 开工门:U0(用户 `/iter` 发令)+ grilling 复核门过(零待裁点,见 §11)
|
||||
|
||||
## 0. 前置依赖与并行性
|
||||
|
||||
- **设计闭合(第〇步)**:06 条目「关卡节点图重构——可选节点太少」状态=文档设计已闭合,凭据=关卡节点图生成.md v1.1+关卡节点地图.md v2.1(各带路径+版本号)→ 放行写卡。
|
||||
- 本卡**不依赖**卡①(生成器)/卡②(地图行进)的实现:迁移期带切法=旧 ceil 等价切法(§4 契约 2),对现行 `level.tres` 模板逐位零行为变化,可独立交付独立验收。
|
||||
- 本卡**向卡① 输出**推论条款 A(多终点深度互异,§4 契约 6)——「Boss 行按终点配置、多终点各配一行(走哪条路决定打谁)」在定位信息只有两样时的前提条件,归卡①产出规格兑现,落文走反哺点(§6 ③)。
|
||||
- 与卡② 的接口零改动:定位信息契约=(关卡序号 + 节点深度) 两样(关卡节点地图.md:119),本卡只换消费侧寻址,不动传递侧。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标**:遭遇寻址从「(关卡序号 × 节点深度) 点坐标+向上取整兜底」换代为「(关卡序号 × 深度带) 带寻址」——深度带=节点深度的区间,配池按带不按点,难度坡随带递增;Boss 行按终点配置、多终点各配行集(走哪条路打谁)。敌人表 20 行数据随寻址同步换键,行集构成纪律与加载期校验清单随带寻址换代表述。
|
||||
|
||||
**相对现状,新增了什么**:
|
||||
1. 表键换代:`(level_index, node_depth, enemy_id)` → `(level_index, row_kind, depth_band, enemy_id)`——`depth_band`(深度带键)替代 `node_depth`,新列 `row_kind ∈ {"battle","boss"}` 承载「Boss 行按终点配置」;
|
||||
2. 带寻址单点:`EnemyTable.band_of`(带切法静态纯函数)+`ConfigDbResolver.resolve_enemy_rowset(L, D, row_kind)` 签名换代;
|
||||
3. 多 Boss 各配行集的 schema 与校验(同关 Boss 带键互异、每关 Boss 行集 ≥1、跨表校验多终点带互异);
|
||||
4. 首遇教学位的带下定位方式(首遇带=该敌种该关最低带;首遇行固定数量不加骰);
|
||||
5. 冒烟断言面与单测面的坐标换代(§8 S 清单)。
|
||||
|
||||
**完成定义**(全部满足才算本批完成):
|
||||
1. `enemy.tres` 20 行按 §4.2 迁移表完成键迁移,且逐行逐字段与迁移前对拍相等(数值零变更,数值只在 `data/tables`);
|
||||
2. `EnemyTable.validate()` 对迁移后生产表零错误;加载期拒绝清单换代后旧类别不失守(§4 契约 7);
|
||||
3. 带寻址单点成立:全仓遭遇行集解析只经 `resolve_enemy_rowset(L, D, row_kind)`,带切法只有 `EnemyTable.band_of` 一份实现;
|
||||
4. 生产路径行为零变化:现行三关模板任意节点开战,生成行集与迁移前逐位一致(同种子同遭遇逐位复现由既有回放面承接);
|
||||
5. 多 Boss 行集能力就位(schema+校验+跨表互异断言绿),且三关现有单终点 Boss 行迁移后仍各就其位;
|
||||
6. 首遇教学位带下定位断言绿:L2 巨蜘蛛首遇带行集={giant_spider} 恰一行、`unit_count=2` 且无 `unit_count_max`(固定 2 只不加骰逐字保持);
|
||||
7. §8 冒烟断言面随动清单逐条换代完毕、全绿;变异验证记录表进实现报告;
|
||||
8. 批末重闸按改动域选档全绿+改前改后读数对拍;三份 `reports/` 产出物写齐(T4)。
|
||||
|
||||
## 2. 设计事实源
|
||||
|
||||
| # | 出处 | 取用条款 |
|
||||
|---|---|---|
|
||||
| D1 | `design/gdd/systems/冒险循环/关卡节点图生成.md` v1.1(2026-10-21,06 闭合凭据之一) | 依赖与输出表敌人系统行(:96):「战斗节点遭遇按深度带配池,遭遇坐标=关卡序号+深度带;多终点各配 Boss 行」;内容归 owner(:86)「战斗节点打谁=敌人系统按深度带配池」 |
|
||||
| D2 | `design/gdd/systems/冒险循环/关卡节点地图.md` v2.1(2026-10-21,06 闭合凭据之一) | :65「遭遇坐标=关卡序号+节点深度,敌人表按**深度带**(深度区间)配池」+「终点恒在各路径最深处」;:119「定位信息只有两样:关卡序号 + 节点深度。战斗侧按深度带配池查表」 |
|
||||
| D3 | `design/gdd/systems/战斗循环/敌人系统.md`(状态:已定稿) | 开战生成节(:67)「遭遇坐标(关卡序号 + 深度带——节点深度所在的区间)」;行集构成纪律行(:74)「敌人表按(关卡序号 × 深度带)存放;深度带=节点深度的区间——逐局生成使单点深度不再固定,配池按带不按点,难度坡随带递增……Boss 行按终点配置,多终点各配一行(走哪条路决定打谁)。纪律三条……」;调参表敌人表行(:229);hobgoblin 缺口登记(:81);验收标准(:269/:278) |
|
||||
|
||||
闭合凭据一致性:D1/D2 与 06 条目状态内列的两份定稿逐字一致;D3 为敌人面语义源(开战生成+行集构成纪律),与 D1/D2 同向。
|
||||
|
||||
## 3. 现状事实(2026-10-21 worktree 实查;开工日 T0 复核行号)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| F1 | 敌人表按 (level_index, node_depth, enemy_id) 逐坐标存放、行集可多行混编——schema 自述「逐 (level_index, node_depth, enemy_id) 行集的类型化读取」+「行序即契约:同坐标多行必须文件内相邻」 | `game/data/schemas/enemy_table.gd:2-3`;首行键实样 `game/data/tables/enemy.tres:7-10` |
|
||||
| F2 | 按坐标取行集的代码=`ConfigDbResolver.resolve_enemy_rowset(L, D)`(ceil 语义:命中坐标返回全部行、按表文件行序;无精确键则向上取整保证偏难、越顶 clamp 取最大键+警告四元组去重);门面与消费面三处 | `game/core/config_db_resolver.gd:33-37`/:54-110(`_resolve_progress`)/:114-118(`_ceil_key`);门面 `game/core/config_db.gd:193-194`;消费面 `game/core/unit_container.gd:43`、`game/core/battle_orchestrator.gd:327`、`game/presentation/battle_narrator.gd:76-78` |
|
||||
| F3 | 敌人表现状 20 行=L1 五坐标((1,0)goblin/(1,1)goblin/(1,2)bugbear/(1,3)hobgoblin/(1,4)ogre_chief Boss)+L2 八行七坐标((2,0)(2,1)bugbear/(2,4)giant_spider/(2,5)bugbear+giant_spider 混编双行/(2,6)(2,7)giant_spider/(2,9)drider Boss)+L3 七坐标((3,0)duergar/(3,1)(3,4)(3,6)hook_horror/(3,5)(3,7)duergar/(3,9)duergar_king Boss);Boss 行每关恰一行、坐标=各关最深节点 | `game/data/tables/enemy.tres:7-505`(行起点 :8/:29/:50/:73/:97/:124/:147/:171/:194/:217/:244/:271/:298/:328/:354/:377/:401/:427/:451/:478) |
|
||||
| F4 | hobgoblin 缺口行:(1,3)「大地精」行在表在役(unit_count 2 / unit_count_max 3 / hp 14),缺口登记=「无立绘、不在本敌种表——补齐归敌种内容批(立绘+语义模板+敌种表行三件套)」 | `game/data/tables/enemy.tres:73-96`;`design/gdd/systems/战斗循环/敌人系统.md:81` |
|
||||
| F5 | 首遇教学位读表与断言面:L2 巨蜘蛛 (2,4) 行 `unit_count=2` 且无 `unit_count_max`(不加骰由「行有 unit_count_max 才掷骰」机制承载);GDD 定位=「L2 首遇教学位(固定 2 只——首遇位不加骰)」;断言三处=L1 D2 熊地精固定 1、(2,4) 巨蜘蛛恰一行固定 2、(3,0) 灰矮人固定 1 | 行值 `enemy.tres:171-192`;GDD `敌人系统.md:88`;机制 `game/core/battle_orchestrator.gd:316-346`(有 unit_count_max 才入 encounter 流掷骰);断言 `game/smoke/smoke_all.gd:3406-3411`/:4692-4697/:4747-4752 |
|
||||
| F6 | 混编行集与行序契约:(2,5) 双行(bugbear 重击手→giant_spider 巢群);同坐标多行必须文件内相邻(加载期 `rowset_rule` 拦截);生成序=表文件行序(逐行实例化);叙述取首行(boss_name 取 rowset[0]);行集实例总数 ≤4(`ROWSET_INSTANCE_MAX`,spawn 期守卫) | 行值 `enemy.tres:194-241`;校验 `enemy_table.gd:121-130`;生成 `game/core/unit_container.gd:85-120`;叙述 `battle_narrator.gd:68-79`;守卫 `enemy_table.gd:29`+`unit_container.gd:81-84` |
|
||||
| F7 | 表校验加载期拒绝清单(`EnemyTable.validate()` 本表侧)=empty_table/dup_key(键含 enemy_id)/domain(L≥1、D≥0、unit_count、hp、atk、pool 非空、权重、armor_class)/cross_field(unit_count_max 区间、atk_min/max 成对+中点恒等)/enum(armor_type、attack_type)/rowset_rule(同坐标相邻)/anchor(每关 depth-0 锚行);跨表侧=④ 战斗/Boss 节点 (L,D) 在 board_gen 与 enemy 双表精确行(缺行加载致命)+⑤ 技能池悬空/池构成纪律 | `enemy_table.gd:32-135`;`game/core/config_db_cross_validator.gd:54-85`(④)/:86-90+:152-190(⑤) |
|
||||
| F8 | 定位信息契约=(关卡序号 + 节点深度) 两样(图侧组装移交战斗结算);enc 已随行携带 `node_type`(battle/boss/…),现仅消费于变异池门控与叙述,未参与敌人行集寻址 | `design/gdd/systems/冒险循环/关卡节点地图.md:23/:111/:119`;`game/core/level_runner.gd:467-482`(begin_battle 组装) |
|
||||
| F9 | 既有解析单点纪律在案:「进度键解析单点收口(全仓统走 resolve_enemy_rowset ceil 版……禁双算法并存〔状态记录 312〕)」 | `enemy_table.gd:138-140` |
|
||||
| F10 | 改前读数基线=开工日 T0 采集(冒烟/单测通过数对拍面)——写卡会话不跑测(测试执行两入口铁规),本卡不预写数字 | — |
|
||||
|
||||
## 4. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 寻址换代与表键**:遭遇寻址=(关卡序号, 节点深度) 入 → (关卡序号, row_kind, depth_band) 命中;敌人表键=`(level_index, row_kind, depth_band, enemy_id)`。`row_kind ∈ {"battle","boss"}`(对齐节点 type 词表),`depth_band`=深度带键(int ≥ 0,带=节点深度的区间)。行类列是「Boss 行按终点配置」的表侧表达:战斗节点与终点同深度时两行集不串位,同关多 Boss 各占自己的带键。
|
||||
|
||||
🔴 **契约 2 · 迁移期行为零变化**:迁移期带切法取**旧 ceil 等价切法**(带键=旧 node_depth 值、请求落带=旧 ceil 命中键、越顶 clamp 顶带+警告去重沿 R1)——生产路径(level_runner 组装的遭遇)迁移前后生成行集逐位一致。数值单一事实源:迁移后数值只进 `data/tables`,20 行数值字段逐字段零变更,禁任何字面量改写。
|
||||
|
||||
🔴 **契约 3 · 带切法单点**:带切法唯一实现=`EnemyTable.band_of(band_keys: Array, D: int) -> int`(静态纯函数,签名与算法见 §7 接口块);`ConfigDbResolver` 与 `ConfigDbCrossValidator` 共用它,禁双算法并存(F9 状态记录 312 同源口径)。带的区间划分(切法粗细)属实现细节,但两语义不可破:D<0 → fatal `request_oob`(沿用)、D 高于该 (L,row_kind) 顶带 → clamp 顶带+warning(类别沿用 `resolve_depth_clamp`、四元组去重沿用)。
|
||||
|
||||
🔴 **契约 4 · 行集构成纪律带键新表述**(三条纪律在带寻址下不破):① 行集内 enemy_id 唯一(同一敌种在同一 (L,row_kind,depth_band) 行集仍只许一行);② 同带键多行必须表文件内相邻,行序=声明序=生成序(行序即契约——resolver 返回序/生成序/叙述取首行全依赖文件行序);③ 行集实例总数(各行骰后数量求和)≤ 4,单行数量域 [1,3] 不变。违规加载期返回错误并止动。
|
||||
|
||||
🔴 **契约 5 · 多 Boss 各配行集**:Boss 行按终点配置、多终点各配一行集(行集可多行=Boss+护卫混编,既有 P4 乙口径不破;同行集双 Boss 敌种属合法混编形态,内容裁量归难度曲线);同关多 Boss 行集带键互异、禁两终点共带(共带=两终点命中同一 Boss 行集,「走哪条路决定打谁」落空)。Boss 行集每关 ≥1。表侧投影=每关 boss 行集 ≥1(加载期);图侧投影=同关多 boss 节点解析带键互异(跨表校验,T2)。
|
||||
|
||||
🔴 **契约 6 · 推论条款 A:多终点深度互异**:定位信息只有 (关卡序号 + 节点深度) 两样(D2:119)且「多终点各配一行(走哪条路决定打谁)」(D3:74)⇒ 终点身份只能由节点深度区分 ⇒ **多终点必须落在互异深度带**(同深度多终点在现行定位信息下不可区分,属设计不可能态)。推论依据=上述两条款的逻辑合取(无第三通道:扩定位信息=改 D2 契约,归卡②/文档批,不在本卡)。表侧投影=契约 5 带键互异校验;图侧兑现=卡①产出规格(落文走反哺点 §6 ③)。执行中发现同深度多终点图样 → L3 呈报。
|
||||
|
||||
🔴 **契约 7 · 加载期拒绝清单换代全兜底**:旧类别(empty_table/dup_key/domain/cross_field/enum/rowset_rule/anchor)一个不失守,键面随契约 1 换代;新增:row_kind 枚举封闭(非法值拒)、每关 Boss 行集 ≥1、(跨表侧)boss 节点带互异+节点深度禁顶 clamp 消解(D 高于该 (L,row_kind) 顶带键=加载致命,替代旧「精确行」严格性——带内命中是设计语义、顶 clamp 是调参事故,分界就在这条)。board_gen 半边精确行校验不动(§5)。
|
||||
|
||||
🔴 **契约 8 · 首遇教学位语义不破**:新敌种首遇带行固定数量不加骰(无 `unit_count_max`);L2 巨蜘蛛首遇带行 `unit_count=2` 逐字保持。首遇带定位方式=数据驱动:该 (L, battle, enemy_id) 的最低 `depth_band` 行集(不写死坐标数字)。
|
||||
|
||||
🔴 **契约 9 · hobgoblin 缺口行不静默处置**:数据去向=随迁带键、行值零变更、留任在役;缺口登记(无立绘/不在敌种表)随行携带,补齐三件套(立绘+语义模板+敌种表行)归敌种内容批。禁借迁移删行、改行或补立绘(内容面归敌种内容批,本卡只定数据去向)。
|
||||
|
||||
### 4.2 数据迁移规则(20 行去向,逐行可核)
|
||||
|
||||
键迁移通则:`node_depth` 值照搬为 `depth_band` 值(迁移期带键=旧深度键,契约 2);`row_kind`="boss" 仅三行(旧坐标为 Boss 节点行者),其余 "battle";`enemy_id` 与全部数值/池字段逐字段零变更;文件内行序不变(行序即契约)。
|
||||
|
||||
| 旧键 (L, D, eid) | 新键 (L, row_kind, band, eid) | 备注 |
|
||||
|---|---|---|
|
||||
| (1,0,goblin) (1,1,goblin) (1,2,bugbear) | (1,battle,0/1/2,同 eid) | (1,2)=bugbear 首遇带行(固定 1 不加骰,F5) |
|
||||
| (1,3,hobgoblin) | (1,battle,3,hobgoblin) | **hobgoblin 缺口行:留任随迁(契约 9)** |
|
||||
| (1,4,ogre_chief) | (1,boss,4,ogre_chief) | L1 单终点 Boss 行 |
|
||||
| (2,0,bugbear) (2,1,bugbear) | (2,battle,0/1,同 eid) | |
|
||||
| (2,4,giant_spider) | (2,battle,4,giant_spider) | 巨蜘蛛首遇带行(固定 2 不加骰,契约 8) |
|
||||
| (2,5,bugbear) (2,5,giant_spider) | (2,battle,5,同 eid) | 混编双行保持相邻、行序不变(契约 4②) |
|
||||
| (2,6,giant_spider) (2,7,giant_spider) | (2,battle,6/7,同 eid) | |
|
||||
| (2,9,drider) | (2,boss,9,drider) | L2 单终点 Boss 行(多终点时各配行集归后续内容投放) |
|
||||
| (3,0,duergar) (3,1,hook_horror) | (3,battle,0/1,同 eid) | (3,0)=duergar 首遇带(固定 1);(3,1)=hook_horror 首遇带(固定 2) |
|
||||
| (3,4,hook_horror) (3,5,duergar) (3,6,hook_horror) (3,7,duergar) | (3,battle,4/5/6/7,同 eid) | |
|
||||
| (3,9,duergar_king) | (3,boss,9,duergar_king) | L3 单终点 Boss 行 |
|
||||
|
||||
行数核对:battle 行 17+boss 行 3=20;迁移后 dup_key(新键)零命中、anchor(battle band 0)三关各一、boss 行集三关各一。
|
||||
|
||||
## 5. 范围边界
|
||||
|
||||
**做**:遭遇寻址换代(带切法单点+resolver 签名);敌人表 20 行键迁移(§4.2);表 schema 与加载期校验清单换代;行集构成纪律带键新表述;多 Boss 各配行集的 schema/校验能力(数据投放归后续内容面);首遇教学位带下定位;冒烟断言面与单测面随动(§8 S 清单);反哺点清单。
|
||||
|
||||
**不做**(明示去向):
|
||||
- 生成器(节点图怎么长、多终点怎么切)→ **卡①**;推论条款 A 的产出规格落文 → 反哺点③,文档批落;
|
||||
- 地图行进与定位信息传递((关卡序号+节点深度) 契约本体)→ **卡②**,本卡零改动;
|
||||
- 迷雾与「走哪条=打谁」的呈现 → **卡④**;
|
||||
- 敌种新内容:hobgoblin 补齐(立绘+语义模板+敌种表行)→ **敌种内容批**;本卡只定数据去向(契约 9),不补立绘不删行;
|
||||
- `board_gen.tres`/`difficulty.tres` 的键带化(两表仍按 (level_index, node_depth) 点键+既有解析语义,`config_db_cross_validator.gd:54-85` 的 board_gen 半边精确行校验不动)——生成图深度扩面后两表的覆盖问题归卡①/后续表批登记,本卡不扩面;
|
||||
- 行值投值与难度坡数值(池权重/hp/atk/AC)→ 难度曲线与Boss(数值单一事实源=`data/tables`,本卡零数值);
|
||||
- 带切法未来粗化(带合并/区间调整)的具体切法——实现细节(契约 3 两语义不破即可),本卡迁移期钉旧 ceil 等价切法。
|
||||
|
||||
## 6. 关联文档同步面
|
||||
|
||||
> 本批是代码实现批,禁写 `design/` 整目录与根规则文件,唯一可写目录=`reports/`。下表「同步义务」一律执行为:逐条列进 `reports/iter<N>-反哺点-<完成日>.md`(哪个文件哪一节、改成什么、依据),用户会话裁决后归文档批落文。
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/gdd/systems/战斗循环/敌人系统.md` | :81 hobgoblin 缺口登记的坐标记法「(1,3),即第一关第四节点」→ 带键记法「(1, battle, 3)」(数据留任语义不变);:278 表校验行「行集内同敌种第二行 / 同坐标多行不相邻」→「同带键多行不相邻」措辞随键换代 | 反哺点 · 列清单(①记法与措辞换代,依据=本卡契约 1/4) |
|
||||
| `design/gdd/systems/冒险循环/关卡节点地图.md` | :146「本文递出关卡序号 + 节点编号,按编号生成单位归它」与 :119「关卡序号 + 节点深度……按深度带配池」两处表述不一致 → 统一为「关卡序号+节点深度;敌人侧按深度带配池生成单位」 | 反哺点 · 列清单(②用词统一,依据=:65/:119 既有定稿句) |
|
||||
| `design/gdd/systems/冒险循环/关卡节点图生成.md` | 产出规格缺「多终点深度互异」条款——本卡推论条款 A(契约 6)的图侧兑现处 | 反哺点 · 列清单(③产出规格补条款:多终点落在互异深度带;依据=D2:119+D3:74 合取) |
|
||||
| `待办清单.md`(06 登记处) | 本卡登记条状态推进「已写卡(`reports/需求卡片-敌人表深度带换代批-2026-10-21.md`)」(req-card 06 状态回写义务);实现批完成后按退役规则处理 | 反哺点 · 列清单(④状态回写与迭代行编入、条目退役记载——本会话文件面限于本卡一个文件,回写由批次收口/用户发令执行,防四卡并行互踩) |
|
||||
| `roadmap.md` | 迭代行编入笔与批内状态刷新 | 反哺点 · 列清单(⑤) |
|
||||
| `game/data/schemas/enemy_table.gd`、`game/data/tables/enemy.tres`、`game/core/config_db_resolver.gd`、`game/core/config_db.gd`、`game/core/unit_container.gd`、`game/core/battle_orchestrator.gd`、`game/core/config_db_cross_validator.gd`、`game/presentation/battle_narrator.gd` | 寻址与表承载改造本体 | 实施本体(可写) |
|
||||
| `game/smoke/*`、`game/tests/*` | 断言面随动(§8 S 清单) | 实施本体(可写) |
|
||||
|
||||
## 7. 任务卡
|
||||
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核本卡全部行号。回归措辞模板沿用:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
|
||||
|
||||
### 卡前 · 文件结构图
|
||||
|
||||
| 文件 | 职责 |
|
||||
|---|---|
|
||||
| `game/data/schemas/enemy_table.gd` | 敌人表 schema:键换代+加载期拒绝清单换代+带切法单点 `band_of` |
|
||||
| `game/data/tables/enemy.tres` | 敌人表数据:20 行键迁移+表尾迁移登记注释 |
|
||||
| `game/core/config_db_resolver.gd` | 带寻址解析(L 维沿旧 ceil/clamp、D 维走 `band_of`) |
|
||||
| `game/core/config_db.gd` | 门面 `resolve_enemy_rowset` 签名随动 |
|
||||
| `game/core/unit_container.gd` | `spawn` 增 `row_kind` 入参、内部寻址随动 |
|
||||
| `game/core/battle_orchestrator.gd` | 遭遇骰与 spawn 调用面随动(enc.node_type → row_kind) |
|
||||
| `game/core/config_db_cross_validator.gd` | ④ 敌侧半边带化+Boss 带互异+顶 clamp 禁消解;board_gen 半边不动 |
|
||||
| `game/presentation/battle_narrator.gd` | `boss_name` 行集寻址随动 |
|
||||
| `game/smoke/smoke_all.gd`、`game/smoke/skill_rhythm_probe.gd`、`game/smoke/damage_probe.gd` | 断言面与直呼寻址面随动(§8 S 清单) |
|
||||
| `game/tests/unit/core/config_db_resolver_test.gd`、`game/tests/unit/data_config/config_db_test.gd`、`game/tests/unit/core/config_db_cross_validator_test.gd` | 带寻址语义单测、夹具键改名、Boss 互异负向用例 |
|
||||
|
||||
### 接口块(全卡共用,后卡引用以此为准)
|
||||
|
||||
- **`EnemyTable.band_of(band_keys: Array, D: int) -> int`**(静态纯函数,带切法单点):`band_keys`=该 (level_index, row_kind) 的 depth_band 键升序数组;返回首个 ≥ D 的键;D 高于全部键 → 返回 -1(resolver 侧 clamp 顶带+warning,校验侧判「顶 clamp 消解」违例);`band_keys` 空 → 返回 -1 且 resolver 侧返回空行集(交消费侧既有「行集解析为空」fatal 路径,`battle_orchestrator.gd:328-330`)。算法体仅此一处(禁第二实现)。
|
||||
- **`EnemyTable.ROW_KINDS := ["battle", "boss"]`**(封闭枚举常量)。
|
||||
- **`ConfigDbResolver.resolve_enemy_rowset(L: int, D: int, row_kind: String) -> Array[Dictionary]`**:L<1 或 D<0 → `_diag` fatal `request_oob`(沿用);`row_kind` 非法 → `_diag` fatal(类别 `request_oob`);L 维 ceil/clamp 语义沿 `_resolve_progress` 现状;D 维=`band_of` 命中该 (L,row_kind) 带键行集、按表文件行序返回;`band_of` 返回 -1 → 取该 (L,row_kind) 顶带键+warning `resolve_depth_clamp`(四元组去重沿用)。
|
||||
- **`ConfigDb.resolve_enemy_rowset(L: int, D: int, row_kind: String) -> Array[Dictionary]`**(门面透传)。
|
||||
- **`UnitContainer.spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides: Array = [], atk_override: int = -1, max_hp_override: int = -1, enemy_atk_overrides: Array = [], hero_attack_type: String = "", hero_armor_type: String = "", row_kind: String = "battle") -> bool`**(尾参新增,缺省 "battle")。
|
||||
- **`ConfigDbCrossValidator.cross_validate(loaded: Dictionary, push_diag: Callable) -> void`**(签名不变;④ 敌侧判据换代)。
|
||||
|
||||
### T0 事实坐实与迁移表核对(≈0.5–1d)
|
||||
|
||||
- [ ] 复核 §3 全部 file:line 与 §4.2 迁移表 20 行(逐行对照 `enemy.tres` 实表:键、eid、数值),漂移即订正本卡——可核查结果:订正记录或「零漂移」声明进实现报告素材。
|
||||
- [ ] 改前读数基线采集(冒烟/单测通过数,两入口铁规下随 T1 迭代 loop 首跑取值)——可核查结果:基线读数记录在案。
|
||||
- [ ] 推论条款 A 与 §6 反哺点点①②③ 条目起草(一节一改一依据)——可核查结果:反哺点草稿段落在 T4 产物成文前就绪。
|
||||
|
||||
### T1 表 schema 换代+数据迁移+带寻址单点(≈1.5–2d)
|
||||
|
||||
- [ ] 实装 `EnemyTable.ROW_KINDS := ["battle", "boss"]` 封闭枚举常量(落点 `enemy_table.gd`)。可核查结果:常量在位且被 validate 判据引用。
|
||||
- [ ] 实装 `EnemyTable.band_of`(签名与语义=接口块;落点 `enemy_table.gd`)。可核查结果:`band_of` 全仓唯一实现(全仓检索零第二份带切法)。
|
||||
- [ ] 实装 `validate()` 键换代(`enemy_table.gd:32-135`):dup_key 键串改 `"%d_%s_%d_%s" % [L, row_kind, depth_band, enemy_id]`;domain 增 `row_kind ∈ ROW_KINDS`、`depth_band ≥ 0`(原 D 域判据换代);rowset_rule 键改 `"%d_%s_%d" % [L, row_kind, depth_band]`(相邻语义不变);anchor 改「每关存在 row_kind="battle" 且 depth_band=0 行」+新增「每关存在 row_kind="boss" 行 ≥1」(错误类别沿 `anchor`);其余判据(unit_count/unit_count_max/hp/atk/pool/armor/attack_type/armor_class)零改动。可核查结果:`validate()` 对旧格式夹具报键类错误、对新格式合法夹具零错。
|
||||
- [ ] 实装 `enemy.tres` 20 行键迁移(严格按 §4.2 表:depth_band 值=旧 node_depth 值、三行 row_kind="boss"、其余 "battle"、数值/池字段零变更、行序不变)。可核查结果:迁移后 20 行新键与 §4.2 逐行对上,且 `git diff` 仅键列+新增 kind 列、值列零变更(件核=V3)。
|
||||
- [ ] 实装 `enemy.tres` 表尾迁移登记注释一段(日期+键换代说明+hobgoblin 留任口径)。可核查结果:注释在位且与 §4.2/契约 9 同口径。
|
||||
- [ ] 验收步骤:跑 `godot --headless --path game --script res://tests/unit/data_config/config_db_test.gd`(先按 V2 改造夹具)与冒烟表校验段,期望「EnemyTable validate 零错误」断言绿。
|
||||
- [ ] 实装 `ConfigDbResolver.resolve_enemy_rowset(L, D, row_kind)` 换代+`EnemyTable.band_of` 接线(`config_db_resolver.gd:33-37` 重写;`_resolve_progress` 保留给 board_gen 零改动);`config_db.gd:193-194` 门面签名随动。可核查结果:`resolve_enemy_rowset(1, 3, "battle")` 返回 hobgoblin 行集、`resolve_enemy_rowset(1, 4, "boss")` 返回 ogre_chief 行集(夹具面)。
|
||||
- [ ] 验收步骤:跑 `godot --headless --path game --script res://tests/unit/core/config_db_resolver_test.gd`(按 V1 换代后),期望带寻址语义五断言全绿(见 §8 V1)。
|
||||
- [ ] 变异验证(注入→最窄闸见红→`git checkout` 还原→记录):把 `band_of` 改成「返回首个 ≤ D 的键」(floor 反向)→ 期望 V1「带命中偏难」断言红;把 `validate` 的 row_kind 枚举判据注释掉 → 期望 V2「非法 row_kind 被拒」断言红。记录表进实现报告。
|
||||
|
||||
### T2 消费面与跨表校验随动(≈1–1.5d)
|
||||
|
||||
- [ ] 实装 `unit_container.gd`:`spawn` 尾参增 `row_kind: String = "battle"`,内部改 `db.resolve_enemy_rowset(level_index, node_depth, row_kind)`(:43),行集为空的报错消息随键换代(:45)。可核查结果:既有 spawn 调用(缺省战斗)零改动可编译,传 "boss" 取 Boss 行集。
|
||||
- [ ] 实装 `battle_orchestrator.gd`:`begin_battle` 内取 `var rk := String(enc.get("node_type", "battle"))`(缺省 battle=§10 代拍板③),`erowset_pre`(:327)与 `_enemy_system.spawn`(:355)传 rk。可核查结果:enc 带 `node_type:"boss"` 时遭遇行集为 Boss 行集,缺省时为 battle 行集。
|
||||
- [ ] 实装 `battle_narrator.gd` `boss_name`(:76-78):寻址改 `db.resolve_enemy_rowset(int(runner.level_row.level_index), int(node.get("node_depth", -1)), "boss")`。可核查结果:Boss 节点叙述名取 Boss 行集首行 display_name。
|
||||
- [ ] 实装 `config_db_cross_validator.gd` ④ 敌侧半边换代(:63-65、:81-85):battle/boss 节点 (L,D) 判据改「(L,row_kind) 行集非空且 `EnemyTable.band_of(该类带键, D) != -1`(-1=顶 clamp 消解,报 `level_node_dangling`)」;新增「同关多 boss 节点解析带键互异(同带报错,类别 `rowset_rule`)」;board_gen 半边(:62、:82-83)零改动。可核查结果:负向夹具(顶 clamp/同带双 boss)各报一行错误。
|
||||
- [ ] 验收步骤:跑 `godot --headless --path game --script res://tests/unit/core/config_db_cross_validator_test.gd`(按 V4 改造后),期望既有三用例+新增两负向用例全绿。
|
||||
- [ ] 验收步骤:T 段出口跑一次全套闸(`tools/run_all_gates.sh` 按改动域选档,本批改动域=`game/` 代码与数据),期望代码域全绿、既有单测(settle_segments 等零改动面)零红。
|
||||
- [ ] 变异验证:删掉「boss 节点带互异」判据 → 期望 V4 同带双 boss 负向用例红;把 ④ 敌侧 `band_of != -1` 判据短路为恒真 → 期望顶 clamp 负向用例红。记录表进实现报告。
|
||||
|
||||
### T3 冒烟断言面随动+首遇带定位(≈1.5–2d)
|
||||
|
||||
- [ ] 按 §8 S 清单逐条换代 `smoke_all.gd` 直呼面(S1/S3/S4/S5/S16:`resolve_enemy_rowset` 三参化)——可核查结果:清单条目逐条勾销、冒烟该段绿。
|
||||
- [ ] 换代首遇教学位断言(S2/S9+契约 8 定位方式):改为数据驱动定位(扫表取该敌种该关最低 depth_band),断言=「L2 巨蜘蛛首遇带行集 = {giant_spider} 恰一行且 unit_count=2 且无 unit_count_max」+「L1 bugbear 首遇带行 unit_count=1 无 unit_count_max」+「L3 duergar 首遇带行 unit_count=1 无 unit_count_max」——可核查结果:三断言绿且不含写死坐标数字。
|
||||
- [ ] 换代混编与行序断言(S4):带键 (2,battle,5) 行集=双行、行序 [bugbear, giant_spider]、Σ(unit_count_max 缺省=unit_count) ≤ 4——可核查结果:断言绿。
|
||||
- [ ] 换代跨表覆盖与曲线断言(S6/S7):en_keys 键改 (L,row_kind,depth_band) 聚合、Boss 节点查 boss 键;L1 总量曲线断言改按 (1,battle) depth_band 升序相邻带总量比 ≤ 2.5(现值 12/10/22/35/72 口径不变)——可核查结果:断言绿。
|
||||
- [ ] 换代毒吐投放面断言(S8):判据改「pool 含 venom_spit 的行 ⇒ enemy_id=="giant_spider" 且 depth_band > 巨蜘蛛首遇带」+「巨蜘蛛首遇带行 pool 恒 {attack:6, bite:2, charge:1}」(去 `>=5` 写死)——可核查结果:断言绿。
|
||||
- [ ] 换代 Boss 坐标直呼面补 node_type(S10a:`smoke_all.gd:2022/:3025/:3090/:3177/:3231/:3271/:3301/:3474/:4503/:4933`、`damage_probe.gd:95` 各补 `"node_type": "boss"`)——可核查结果:各段绿、Boss 行集解析正确。
|
||||
- [ ] 换代 kind/坐标不一致直呼面(S10b:`smoke_all.gd:2621/:2646` 的 (2,4)+`node_type:"boss"` 改 (2,9)+`node_type:"boss"`——其断言语义「Boss 图回落/确定性」不变,棋盘载体改真 Boss 坐标)——可核查结果:该段绿、消息文本更新为新坐标。
|
||||
- [ ] 换代探针直呼面(S14:`skill_rhythm_probe.gd:17`、`damage_probe.gd:269/:420` 三参化)——可核查结果:探针可跑通。
|
||||
- [ ] 验收步骤:T 段出口跑一次全套闸,期望冒烟全绿、探针段无红。
|
||||
- [ ] 变异验证:给巨蜘蛛首遇带行加 `unit_count_max: 3` → 期望首遇「不加骰」断言红;把混编两行文件内对调 → 期望行序断言红;把 hobgoblin 行迁到错误带键 (1,battle,2) → 期望 V3 键位断言红。记录表进实现报告。
|
||||
|
||||
### T4 产出物落位卡(≈0.5d,末卡——三份 `reports/` 文件写齐+独立验收审计通过才算收口)
|
||||
|
||||
- [ ] 轻闸全绿后、合并前写齐三份:`reports/iter<N>-实现报告-<完成日>.md`(各卡状态+变异验证记录表+实机目检总清单+遗留读数义务)/`reports/iter<N>-代拍板追认单-<完成日>.md`(§10 白名单内代拍项留痕)/`reports/iter<N>-反哺点-<完成日>.md`(§6 五条每条一落点:①敌人系统.md 记法/措辞、②关卡节点地图.md :146 用词、③关卡节点图生成.md 推论条款 A、④06 状态回写与条目记载、⑤roadmap 编入笔)。可核查结果:三文件在 `reports/` 且 §6 同步面每条有落点。
|
||||
- [ ] 合并前置:独立验收审计(§8.2 末段声明)完成且报告 `reports/iter<N>-验收审计-<完成日>.md` 逐行「通过」——未通过不得合并,缺漏项回炉对应 T 卡。可核查结果:审计报告在 `reports/` 且逐行「通过」。
|
||||
|
||||
**估时合计 ≈5–6.5d**(T0 0.5–1+T1 1.5–2+T2 1–1.5+T3 1.5–2+T4 0.5)。切分预案:T1/T2 后可切(表与解析先落、消费面后落)——但 T1 出口到 T2 之间编译面是断的(签名换代),建议 T1+T2 一轮同落、T3 单独一轮;超软盒(>7d)预挂:T3 冒烟面清单若超一孔,先落 S1–S5+S9+S10a/S10b(寻址三参化/首遇带/Boss 直呼主链)保主链绿,S6–S8+S14–S16 次轮补,全清单绿才收口。
|
||||
|
||||
## 8. 验证闸(逐需求点验收清单)
|
||||
|
||||
| # | 需求点 | 测什么(断言/行为) | 落点/判定手段 |
|
||||
|---|---|---|---|
|
||||
| V1 | 带寻址语义 | ①带命中:请求 D 落带 i → 返回该带行集(夹具键 [0,5]:请求 4 → 命中带 5,偏难不回 0);②行序保序(行集按表文件行序返回);③顶 clamp:D 高于顶带 → 顶带行集+warning `resolve_depth_clamp` 一次(重复请求去重);④row_kind 路由:同 D 传 "battle"/"boss" 命中不同键域;⑤D<0/L<1 → fatal `request_oob` | 单测 `config_db_resolver_test.gd`(换代既有 `_test_ceil_progress_key`/`_test_rowset_order_preserved` 为带语义四断言+新增路由断言) |
|
||||
| V2 | 表 schema 与校验换代 | 迁移后生产表 `validate()` 零错误;非法 row_kind 被拒;dup_key 新键拦截(同 (L,kind,band,eid) 二行报 dup);rowset_rule 同带隔断报错;anchor(battle band 0+boss ≥1)缺任一报错 | 冒烟表校验段+单测夹具(`config_db_test.gd`) |
|
||||
| V3 | 数据迁移零变更 | 键位与结构:20 行计数、键序=§4.2 表序、hobgoblin 行在 (1,battle,3)、三 boss 行 kind/band 就位;**数值零变更不钉字面量**(AGENTS.md 数值单一事实源红线)——由 T1 迁移步骤「git diff 仅键列+新增 kind 列、值列零变更」件核承载 | 冒烟结构断言(键/计数/行序)+git diff 件核(T0/T1 清单) |
|
||||
| V4 | 多 Boss 各配行集 | 同关两 boss 节点解析带键互异(同带 → 报错);节点深度触发顶 clamp → 报错;单终点现状三关零误报 | 单测 `config_db_cross_validator_test.gd` 新增两负向+既有三用例回归 |
|
||||
| V5 | 行集构成纪律带键新表述 | 混编 (2,battle,5) 双行行序 [bugbear→giant_spider];Σ(unit_count_max 缺省=unit_count) ≤ 4;同带隔断加载拒 | 冒烟 S4+`enemy_table` 校验(V2 面) |
|
||||
| V6 | 首遇教学位带下定位 | 首遇带=数据驱动最低带;L2 巨蜘蛛首遇带行集恰一行、unit_count=2、无 unit_count_max;L1 bugbear/L3 duergar 首遇带固定数同款 | 冒烟 S2/S9 换代断言 |
|
||||
| V7 | 冒烟断言面随动完整 | §8.1 S 清单全部条目(S1–S16,S10 分 a/b 共 17 行)逐条换代、无一条留旧坐标语义 | 冒烟全绿+清单件核(实现报告逐条勾销) |
|
||||
| V8 | hobgoblin 缺口行去向 | (1,battle,3,hobgoblin) 在役(V3 结构断言面)、缺口登记随行(不删不补、不加立绘);数值相等归 V3 git diff 件核 | 冒烟结构断言+件核(敌种内容批接续登记在反哺点①) |
|
||||
| V9 | 反哺点清单 | §6 五条每条有落点 | 件核对表(T4) |
|
||||
|
||||
### 8.1 冒烟断言面随动清单(S 清单——按旧坐标写就、须换代的断言/直呼面,2026-10-21 实查)
|
||||
|
||||
| # | 断言/直呼面 | 位置 | 换代内容 |
|
||||
|---|---|---|---|
|
||||
| S1 | `resolve_enemy_rowset` 行集首行读数(遭遇扫描/D1 D2 掷骰段) | `game/smoke/smoke_all.gd:3115`/:3429/:4396 | 三参化(row_kind="battle"),带语义注释 |
|
||||
| S2 | 巨蜘蛛首遇行集 (2,4) 定点断言 | `smoke_all.gd:4692-4697` | 首遇带数据驱动定位+「恰一行、unit_count=2、无 unit_count_max」 |
|
||||
| S3 | L2 Boss 行集 (2,9) 断言 | `smoke_all.gd:4698-4702` | 三参化(row_kind="boss") |
|
||||
| S4 | 混编行集 (2,5) 行序/骰和/行值断言 | `smoke_all.gd:4706-4719` | 键改 (2,battle,5);行序契约断言保持 |
|
||||
| S5 | L3 首遇 (3,0) 与 Boss (3,9) 断言 | `smoke_all.gd:4747-4759` | 首遇带定位+三参化(boss) |
|
||||
| S6 | 跨表覆盖义务 en_keys 键聚合 | `smoke_all.gd:971-981`/:4677-4687/:5144-5147 | 键改 (L,row_kind,depth_band);Boss 节点查 boss 键域 |
|
||||
| S7 | Boss AC 台阶+(1,2) 锚定+L1 总量曲线 | `smoke_all.gd:2206-2244` | node_depth→depth_band;(1,2) 锚定改 bugbear 首遇带定位;曲线断言改按 (1,battle) 带键相邻比 ≤ 2.5 |
|
||||
| S8 | 毒吐投放面(venom_spit 专行检查+池权重图) | `smoke_all.gd:4178-4230` | 「`node_depth >= 5`」改「depth_band > 巨蜘蛛首遇带」;池键串改含 row_kind |
|
||||
| S9 | D2 熊地精固定 1(首遇教学位) | `smoke_all.gd:3406-3411` | (1,2) 改 bugbear 首遇带定位(固定 1 不加骰) |
|
||||
| S10a | Boss 坐标 `begin_battle` 直呼缺 node_type | `smoke_all.gd:2022`/:3025/:3090/:3177/:3231/:3271/:3301/:3474/:4503/:4933;`smoke/damage_probe.gd:95` | 各补 `"node_type": "boss"` |
|
||||
| S10b | kind/坐标不一致直呼((2,4)+boss) | `smoke_all.gd:2621`/:2646 | 改 (2,9)+boss(断言语义不变,载体改真 Boss 坐标) |
|
||||
| S11 | resolver 单测(ceil 语义/行序保序) | `game/tests/unit/core/config_db_resolver_test.gd:51-70` | 换代为 V1 带寻址语义断言 |
|
||||
| S12 | `resolve_enemy_rowset` 同通道单测+夹具键 | `game/tests/unit/data_config/config_db_test.gd:38-40`/:62-69/:201 | 夹具键改 row_kind/depth_band;签名三参化 |
|
||||
| S13 | 跨表校验单测夹具 | `game/tests/unit/core/config_db_cross_validator_test.gd:54`/:66/:79 | 夹具键改名+补 V4 两负向用例 |
|
||||
| S14 | 探针直呼面 | `game/smoke/skill_rhythm_probe.gd:17`;`game/smoke/damage_probe.gd:269`/:420 | 三参化 |
|
||||
| S15 | `boss_name` 叙述寻址 | `game/presentation/battle_narrator.gd:76-78` | 三参化(row_kind="boss") |
|
||||
| S16 | 全深度×多种子 spawn 扫描(erow2 首行行值读数) | `smoke_all.gd:3413-3433` | 首行 unit_count 读数随带键(三参化) |
|
||||
|
||||
清单口径:S11–S13 归单测面(T1/T2),S1–S10、S14–S16 归冒烟/探针面(T3);S15 归 T2(消费面)。无第 17 类遗漏面——`tools/` 下 node_depth 引用属关卡模板历史脚本(`t5_build_templates.py` 等),非敌人表键,不随动。
|
||||
|
||||
**测试方式两分法(2026-10-11 用户定)**:V1–V9 全部机检自动化(冒烟+单测);能力验证可经调试接口直改敌人表/遭遇坐标直呼开战(沿用现役 DebugOverlay/DS_DEBUG 门控)。**用户目检项**:仅「三关实机开战遭遇观感不回归」一条——观测项写进 `reports/iter<N>-实现报告-<完成日>.md` 实机目检总清单,登记不阻塞(验收时间用户自行安排)。
|
||||
|
||||
**闸口径**:轻闸每任务卡出口自跑;批末重闸=`tools/run_all_gates.sh` 按改动域选档(本批只碰 `game/` 代码与数据表 → 代码域档)全绿才合并+改前改后读数对拍。回归措辞:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
|
||||
|
||||
**变异验证伴生**(新增/改写验收函数逐条注入实验,记录表进实现报告):`band_of` 反向 floor(V1 红)/row_kind 枚举判据注释(V2 红)/boss 带互异判据删除(V4 红)/顶 clamp 判据短路(V4 红)/首遇行加 unit_count_max(V6 红)/混编行对调(V5 红)/hobgoblin 迁错带键(V3 红)。数值零变更不设数值字面量断言(数值单一事实源红线),改键误写由键位/行序/计数断言兜、值列漂移由 git diff 件核兜。变异不红则改判据或如实标注拦截边界。
|
||||
|
||||
### 8.2 改动—验收对照表(2026-10-21 验收闭合批新规,test-conventions §9.3)
|
||||
|
||||
> 各断言 desc 统一尾缀〔fv#〕(本表「验收测试」列可 grep 定位);撤销/变异证明逐条对应 §8 变异验证伴生清单与 T1–T3 变异步(每张 T 卡至少一条,test-conventions §9.4)——T0 为事实坐实卡(无行为改动),其迁移表核对红证并入 V3 行(hobgoblin 迁错变异);T4 产出物以件核撤销证明承载。
|
||||
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+文件:行+尾缀) | 层级 | 真实入口 | 撤销或变异证明 |
|
||||
|---|---|---|---|---|---|
|
||||
| V1① 带命中偏难 | `game/core/config_db_resolver.gd:33-37` D 维改走带寻址+`game/data/schemas/enemy_table.gd` 新增 `band_of` 纯函数(接口块签名) | 带命中偏难(夹具键 [0,5]:请求 4 命中带 5、偏难不回 0)〔fv1〕 `game/tests/unit/core/config_db_resolver_test.gd`(S11 换代) | 单元 | `EnemyTable.band_of()`/`ConfigDbResolver.resolve_enemy_rowset()` 直调 | 变异:`band_of` 改「返回首个 ≤ D 的键」(T1 变异步)→ 本断言红 |
|
||||
| V1② 行序保序 | `config_db_resolver.gd` 带命中行集按表文件行序返回(行序即契约) | 行序保序〔fv2〕 `config_db_resolver_test.gd`(S11 换代 `_test_rowset_order_preserved`) | 单元 | `resolve_enemy_rowset()` 直调 | 变异:混编两行文件内对调(T3 变异步)→ 行序断言红 |
|
||||
| V1③ 顶 clamp+warning 去重 | `config_db_resolver.gd` `band_of` 返回 -1 → 顶带键行集+warning `resolve_depth_clamp`(四元组去重沿用) | 顶 clamp+warning 一次去重〔fv3〕 `config_db_resolver_test.gd` | 单元 | `resolve_enemy_rowset()` 直调(D 高于顶带、重复请求两次) | 撤销证明:回退 D 维 clamp 接线 hunk → 本断言红(§9.4 既有行为换代) |
|
||||
| V1④ row_kind 路由 | `resolve_enemy_rowset(L, D, row_kind)` 三参签名(`config_db_resolver.gd:33-37`+`config_db.gd:193-194` 门面随动) | row_kind 路由:同 D 传 battle/boss 命中不同键域〔fv4〕 `config_db_resolver_test.gd` | 单元 | 同 D 分别传 "battle"/"boss" 直调 | 撤销证明:撤销第三参接线 hunk → 同 D 双 kind 不再分域、断言红 |
|
||||
| V1⑤ request_oob | `config_db_resolver.gd` L<1 或 D<0 → `_diag` fatal `request_oob`(沿用) | D<0/L<1 fatal `request_oob`〔fv5〕 `config_db_resolver_test.gd` | 单元 | `resolve_enemy_rowset(0, …)`/`(L, -1, …)` 直调 | 撤销证明:回退 OOB 判据 hunk → 断言红 |
|
||||
| V2① 生产表 validate 零错误 | `enemy_table.gd:32-135` validate 键换代+`game/data/tables/enemy.tres` 20 行键迁移 | 迁移后生产表 validate 零错误〔fv6〕 冒烟表校验段 `game/smoke/smoke_all.gd`+`config_db_test.gd` | 接口 | `EnemyTable.validate()` 对 `enemy.tres` | 撤销证明:回退键换代/迁移 hunk → validate 键类错误红 |
|
||||
| V2② 非法 row_kind 被拒 | `enemy_table.gd` domain 判据增 `row_kind ∈ ROW_KINDS`(`ROW_KINDS := ["battle","boss"]`) | 非法 row_kind 被拒〔fv7〕 `game/tests/unit/data_config/config_db_test.gd`(S12 夹具) | 接口 | `validate()` 非法枚举夹具 | 变异:`validate` 的 row_kind 枚举判据注释掉(T1 变异步)→ 本断言红 |
|
||||
| V2③ dup_key 新键拦截 | `enemy_table.gd` dup_key 键串改 `"%d_%s_%d_%s"`(L,row_kind,depth_band,enemy_id) | dup_key 新键拦截(同键二行报 dup)〔fv8〕 `config_db_test.gd` | 接口 | `validate()` 同键二行夹具 | 撤销证明:回退 dup_key 键串 hunk → 同键二行漏拦、断言红 |
|
||||
| V2④ rowset_rule 同带隔断 | `enemy_table.gd:121-130` rowset_rule 键改 `"%d_%s_%d"`(同带相邻语义不变) | rowset_rule 同带隔断报错〔fv9〕 `config_db_test.gd` | 接口 | `validate()` 隔断夹具 | 撤销证明:回退 rowset_rule 键串 hunk → 隔断漏拦、断言红 |
|
||||
| V2⑤ anchor 缺任一报错 | `enemy_table.gd` anchor 改「每关 battle band 0」+新增「每关 boss 行集 ≥1」(类别沿 `anchor`) | anchor(battle band 0+boss ≥1)缺任一报错〔fv10〕 `config_db_test.gd` | 接口 | `validate()` 缺锚/缺 boss 夹具 | 撤销证明:回退 anchor 判据 hunk → 缺任一漏报、断言红 |
|
||||
| V3① 20 行计数与键序 | `enemy.tres` 20 行键迁移(T0 逐行复核 §4.2 迁移表+T1 迁移步,文件行序不变) | 20 行计数+键序=§4.2 表序〔fv11〕 冒烟结构断言 `smoke_all.gd` | 接口 | 加载 `enemy.tres` 全表(冒烟表校验段) | 撤销证明:回退 `enemy.tres` 迁移 hunk(T0 核对面+T1 迁移面)→ 计数/键序断言红 |
|
||||
| V3② hobgoblin 键位 | (1,3,hobgoblin) → (1,battle,3,hobgoblin) 留任随迁(契约 9) | hobgoblin 行在 (1,battle,3)〔fv12〕 冒烟结构断言 `smoke_all.gd` | 接口 | 同 fv11 读表面 | 变异:hobgoblin 迁错带键 (1,battle,2)(T3 变异步)→ 键位断言红(红证覆盖 T0 §4.2 迁移表核对面) |
|
||||
| V3③ 三 boss 行 kind/band 就位 | 三行 `row_kind="boss"` 就位((1,boss,4)ogre_chief/(2,boss,9)drider/(3,boss,9)duergar_king,§4.2 表) | 三 boss 行 kind/band 就位〔fv13〕 冒烟结构断言 `smoke_all.gd` | 接口 | 同 fv11 读表面 | 撤销证明:回退任一 boss 行 kind 列 hunk → 断言红 |
|
||||
| V3④ 值列零变更件核 | `enemy.tres` 仅键列+新增 kind 列(T1 迁移步:值列零变更、禁字面量改写) | 值列零变更〔fv14〕 git diff 件核(T1 迁移步清单) | 件核 | `git diff game/data/tables/enemy.tres` 件核 | 撤销证明:改任一值列字面量(T1 件核步)→ git diff 件核红 |
|
||||
| V4① 同带双 boss 报错 | `config_db_cross_validator.gd:63-65/:81-85` ④ 敌侧带化+「同关多 boss 节点解析带键互异」判据(类别 `rowset_rule`) | 同带双 boss 负向报错〔fv15〕 `game/tests/unit/core/config_db_cross_validator_test.gd`(S13 新增负向) | 单元 | `ConfigDbCrossValidator.cross_validate()` 同带双 boss 夹具 | 变异:删「boss 节点带互异」判据(T2 变异步)→ 本用例红 |
|
||||
| V4② 顶 clamp 消解报错 | ④ 敌侧 `band_of != -1` 判据(-1=顶 clamp 消解,报 `level_node_dangling`) | 顶 clamp 负向报错〔fv16〕 `config_db_cross_validator_test.gd` | 单元 | `cross_validate()` 节点深度高于顶带夹具 | 变异:④ 敌侧判据短路为恒真(T2 变异步)→ 本用例红 |
|
||||
| V4③ 单终点三关零误报 | ④ 敌侧半边换代、board_gen 半边零改动(:62/:82-83) | 既有三用例回归零误报〔fv17〕 `config_db_cross_validator_test.gd` 既有三用例(S13 夹具键改名) | 单元 | `cross_validate()` 现役三关夹具 | 撤销证明:回退 ④ 敌侧换代 hunk → 改名后既有三用例红 |
|
||||
| V5① 混编行序 | (2,5) 双行迁 (2,battle,5)、行序 [bugbear→giant_spider] 与文件相邻不变(`enemy.tres`) | 混编 (2,battle,5) 双行行序〔fv18〕 冒烟 S4 `smoke_all.gd:4706-4719` 换代 | 冒烟 | 冒烟 S4 段读 (2,battle,5) 行集 | 变异:混编两行文件内对调(T3 变异步)→ 行序断言红 |
|
||||
| V5② 骰和 ≤ 4 | 行集实例总数纪律带键化(`enemy_table.gd:29` `ROWSET_INSTANCE_MAX`+`unit_container.gd:81-84` 守卫) | Σ(unit_count_max 缺省=unit_count) ≤ 4〔fv19〕 冒烟 S4 | 冒烟 | 同 fv18 读行集面 | 撤销证明:放宽 ≤4 判据/守卫 hunk → 断言红 |
|
||||
| V5③ 同带隔断加载拒 | 同 fv9 面(`enemy_table.gd:121-130` rowset_rule 带键化) | 同带隔断加载拒〔fv20〕 `config_db_test.gd`(V2④ 面合用) | 接口 | `validate()` 隔断夹具 | 撤销证明:同 fv9(回退 rowset_rule 键串 hunk → 断言红) |
|
||||
| V6① 首遇带数据驱动定位 | T3 首遇断言定位换代为扫表取该 (L,battle,eid) 最低 depth_band(禁写死坐标,契约 8) | 首遇带=数据驱动最低带〔fv21〕 冒烟 S2/S9 `smoke_all.gd:4692-4697`/`:3406-3411` 换代 | 冒烟 | 冒烟 S2/S9 段扫 `enemy.tres` | 撤销证明:撤销扫表定位 hunk(写回写死坐标)→ 三断言取键失配红;件核零写死坐标(契约 8) |
|
||||
| V6② L2 巨蜘蛛首遇带 | S2 换代:首遇带行集恰一行、`unit_count=2`、无 `unit_count_max`(S2 :4692-4697) | 巨蜘蛛首遇带恰一行固定 2 不加骰〔fv22〕 冒烟 S2 `smoke_all.gd:4692-4697` 换代 | 冒烟 | 同 fv21 扫表定位面 | 变异:巨蜘蛛首遇行加 `unit_count_max: 3`(T3 变异步)→「不加骰」断言红 |
|
||||
| V6③ L1/L3 首遇带固定数 | S9/S5 换代:L1 bugbear 首遇带固定 1、L3 duergar 首遇带固定 1、均无 `unit_count_max` | bugbear/duergar 首遇带固定 1 不加骰〔fv23〕 冒烟 S9 `smoke_all.gd:3406-3411`+S5 `:4747-4759` 换代 | 冒烟 | 同 fv21 扫表定位面 | 变异:首遇行加 `unit_count_max`(T3 变异步同法注入 L1/L3 首遇行)→ 断言红 |
|
||||
| V7 S 清单随动完整 | §8.1 S1–S16(17 行)逐条换代(`smoke_all.gd`/`skill_rhythm_probe.gd`/`damage_probe.gd`/单测三件) | S 清单 17 行无一留旧坐标语义〔fv24〕 清单件核(实现报告逐条勾销)+冒烟/探针全绿 | 冒烟+探针+单元 | 冒烟/探针/`tests/run_all` 全套入口 | 分证变异挂 fv1/fv2/fv7/fv18/fv22(T1–T3 变异步);整面撤销证明:任一 S 条留旧语义(撤销该 hunk)→ 对应断言红 |
|
||||
| V8 hobgoblin 缺口行去向 | `enemy.tres` (1,battle,3,hobgoblin) 留任随迁、行值零变更、缺口登记随行(契约 9:不删行不补立绘) | hobgoblin 在役+缺口登记随行〔fv25〕 冒烟结构断言(V3 面)+git diff 件核 | 接口+件核 | 同 fv11 读表面+`git diff` 件核 | 变异:hobgoblin 迁错带键(T3 变异步,同 fv12)→ 键位断言红;删/补件由 git diff 件核兜 |
|
||||
| V9 反哺点清单 | T4 `reports/iter<N>-反哺点-<完成日>.md` §6 五条各一落点(①敌人系统.md 记法措辞/②关卡节点地图.md :146/③关卡节点图生成.md 推论条款 A/④06 状态回写/⑤roadmap 编入笔) | §6 五条每条有落点〔fv26〕 件核对表(T4 出口) | 件核 | 反哺点清单对 §6 同步面表件核 | 撤销证明:删一落点(如⑤)→ T4 出口件核红 |
|
||||
|
||||
**独立验收审计(test-conventions §9.7,2026-10-21 起)**:合并前由未参与实现的迭代 loop 内子代理(或用户点名会话)审计——只读本卡、实现报告实际对照表、diff 与测试文件,自跑最窄测试抽查,不采信「已全绿」;审计报告落 `reports/iter<N>-验收审计-<完成日>.md`(逐行「通过/缺漏/例外及依据」)。**审计未通过不得合并**(T4 合并前置)。
|
||||
|
||||
## 9. 异常分级
|
||||
|
||||
- **L1(自救)**:断言措辞、命名、注释、文件落点;冒烟消息文本随坐标记法更新。
|
||||
- **L2(记录继续)**:带切法实现形态(ceil 式键表扫描 vs 显式区间表——接口块语义内);校验错误类别命名细节(沿既有词表增补);冒烟断言的扫描式/resolve 式写法选择(§10 ⑤)。
|
||||
- **L3(停机呈报)**:迁移后行值与 §4.2 表对不上(数据漂移);任何触碰定位信息契约(两样)的诉求;带切法出现第二实现;任何删/增敌人内容的诉求(hobgoblin 补齐或删行);执行中发现同深度多终点图样(推论条款 A 被打破);`validate()` 旧类别出现失守缺口且无法在键换代内兜住。
|
||||
|
||||
## 10. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. 带切法实现形态与带键编号规则——约束:迁移期与旧 ceil 逐位等价(契约 2)、单点收口(契约 3)、D<0 fatal 与顶 clamp+warning 去重两语义不破。
|
||||
2. schema 新字段/常量命名(`row_kind`/`depth_band`/`ROW_KINDS`)与枚举值字符串("battle"/"boss" 对齐节点 type 词表)。
|
||||
3. `resolve_enemy_rowset` 第三参命名 `row_kind`;`enc.node_type` 缺省映射 "battle"(level_runner 生产路径恒传值,缺省只服务测试直呼面)。
|
||||
4. 校验错误类别命名(沿 `dup_key`/`domain`/`rowset_rule`/`anchor` 词表;确需新类别时命名并留痕)。
|
||||
5. 冒烟断言的定位写法(表扫描式 vs resolve 式)与断言名措辞——约束:首遇定位禁写死坐标数字(契约 8)。
|
||||
6. 提交切分与 worktree 分支命名。
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门=U0。**
|
||||
|
||||
两处灰色地带已按设计事实推导定值(用户可否决,随 T4 反哺点清单呈报可见):
|
||||
- **推论条款 A(多终点深度互异,契约 6)**:备选「扩定位信息带终点标识」——淘汰依据=D2:119「定位信息只有两样」是既有定稿契约,扩它归卡②/文档批;「多终点共带共享 Boss」——淘汰依据=D3:74「多终点各配一行(走哪条路决定打谁)」明文;定值=多终点落互异深度带,表侧投影为 Boss 带键互异校验。
|
||||
- **hobgoblin 缺口行去向(契约 9)**:备选「迁移时退役隔离」——淘汰依据=用户范围句「本卡只定数据去向不补立绘」+迁移零行为变更原则(删内容属敌种内容批职权);定值=随迁留任、缺口登记随行、补齐归敌种内容批。
|
||||
|
||||
---
|
||||
|
||||
**自审五查记录(2026-10-21)**:①规格覆盖——§1 六项新增/八项完成定义逐条挂 T 卡(寻址=T1/T2、数据迁移=T1、校验=T1/T2、多 Boss=T2、首遇=T3、冒烟随动=T3、反哺点=T4),无漏项;②步骤扫描——全部步骤一步一动作一可核查结果,函数体仅 `band_of` 一处给语义、其余签名+断言;③类型一致性——接口块五签名与 T 卡引用逐字一致(`resolve_enemy_rowset(L, D, row_kind)`/`spawn` 尾参 `row_kind`/`band_of(band_keys, D)`/`ROW_KINDS`);④审查焦点——补挂三类失败模式:顶 clamp 静默消解(V4)、同带双终点(V4)、首遇行误加骰(V6),各带变异注入;⑤篇幅比例——规格≈1 屏、任务卡步骤≈实现面 1:1,无抄写体。
|
||||
|
||||
**grilling 复核记录(2026-10-21,设计树对抗复核,不采访用户——可推导定值写依据、真归用户列 §11)**:走查五支——带定义/切法(覆盖域与顶 clamp 归 §4 契约 3+契约 7,无缺口)、行类维度(battle/boss 同深度串位反证成立,row_kind 必要性已写进契约 1;node_type 非法值 fatal 已钉)、多 Boss 寻址(共带后果原文写错——「混编双 Boss」改为「两终点命中同一 Boss 行集」,契约 5 已订正;表侧/图侧投影分工已写清)、首遇定位(关卡节点地图:120「教学位同样掷骰、无结构豁免」与敌人系统「首遇不加骰」并存无冲突——前者是图侧不豁免、后者是行值无区间,契约 8 表述核对一致)、数据迁移对拍(原 V3 数值逐字段断言与 AGENTS.md:72 数值单一事实源红线冲突——改为键位/行序/计数结构断言+git diff 值列件核,变异面改钉 hobgoblin 错带)。AGENTS.md 硬红线逐条过:数值单点(本卡零数值)、测试两入口(T 卡只写断言不跑测)、实现批禁写 design/(§6 义务全走反哺点、零旧口径义务措辞)。结论:零待裁点,两处灰色推导已定值并留 §11 供用户否决。
|
||||
@@ -0,0 +1,268 @@
|
||||
# 需求卡片:节点图消费侧换代批(模板池消费 → 生成图消费)· 关卡节点图重构卡②
|
||||
|
||||
- 版本:**v1.0**(2026-10-21 成文;自审五查+grilling 复核结论见文末)
|
||||
- 来源:[待办清单](../待办清单.md)「关卡节点图重构——可选节点太少」(2026-10-13 登记,来源=playnote,设计面=文档轨,状态=文档设计已闭合)(唯一事实源:[关卡节点地图](../design/gdd/systems/冒险循环/关卡节点地图.md) v2.1+[关卡节点图生成](../design/gdd/systems/冒险循环/关卡节点图生成.md) v1.1+[冒险结构](../design/gdd/systems/冒险循环/冒险结构.md) 关卡行集与结构冻结行,2026-10-21 双定稿闭合凭据)
|
||||
- 归属:关卡节点地图消费侧(加载校验/行进语义/商店编排冒烟/定位信息/确定性承载/旧模板清理);关卡节点图重构批卡②(卡①生成器/卡③敌人表深度带〔已写卡〕/卡④迷雾同批)
|
||||
- 排期:未编入(2026-10-18 用户令:需求卡片不预占迭代号,批次归属按工作量另裁)
|
||||
- 开工门:U0(用户 `/iter` 发令)+ grilling 复核门过(零待裁点,见 §11)
|
||||
|
||||
## 0. 前置依赖与并行性
|
||||
|
||||
- **设计闭合(第〇步)**:06 条目「关卡节点图重构——可选节点太少」状态=文档设计已闭合,凭据=关卡节点图生成.md v1.1+关卡节点地图.md v2.1(各带路径+版本号)→ 放行写卡。
|
||||
- **硬依赖卡①(生成器)**:运行时图来源换成生成器产图(§7 取用块),卡①接口定稿+实现落地后本卡才可合入运行时切源;T0 与卡①接口对齐(签名/图数据形状/参数档 schema/复现语义四件)。
|
||||
- **硬交付序列(跨卡数据覆盖)**:敌人深度带数据对生成图深度域的覆盖(每 (关卡序号, row_kind) 顶带键 ≥ 该关生成图最大节点深度)须先落或与本卡同批落——归口=卡③ §5 已登记的「生成图深度扩面覆盖问题(卡①/后续表批)」。未覆盖时覆盖断言(§8 V5/S5)红、遭遇解析顶 clamp 消解,本卡不得单独合入(依据:冒险结构.md:293 覆盖义务+卡③契约 7 顶 clamp 禁消解+闸全绿要求)。
|
||||
- 与卡③接口零改动:定位信息契约=(关卡序号 + 节点深度) 两样(关卡节点地图.md:119;卡③ §0 同口径),本卡换传递侧值语义、不动消费侧寻址。
|
||||
- 与卡④(迷雾可见性)并行无依赖;迷雾呈现面(近处见类型/远处起雾)归卡④,本卡只保证图结构只读读面在位。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标**:节点图消费侧六件换代——① 加载校验换代(多入口/多终点、每路径恰从一入口到一终点、终点恒为 Boss、无环;规则语义以关卡节点图生成 v1.1 产出规格为准,校验动作在消费侧);② 行进语义换代(大岔口自 L2 起、多 Boss 兑付与关卡收束、单链退化形态仍合法但三关参数档都含岔口);③ 商店编排冒烟断言换代(每个终点前一位恰一家店、每局必经恰一家);④ 定位信息换代(关卡序号+节点深度,弃节点编号寻址语义);⑤ 确定性承载换代(固定种子+固定参数档承载首局教学图与存档复现,「模板 0=回滚档」语义废除);⑥ 旧 15 张模板 level.tres 数据清理(生成器产图经卡①接口取用)。
|
||||
|
||||
**相对现状,新增了什么**:
|
||||
1. 图来源换代:`level.tres` 模板池(3 关 × 5 张)→ 卡①生成器按(参数档,种子)产图;加载校验的对象从静态模板变为运行时生成图;
|
||||
2. 校验语义换代:唯一入口/唯一 Boss/编号最大/出度 ≤2/层宽 ≤5/单链前三纯战斗六项退役,换为多入口多终点合法+路径完整性+终点恒 Boss+无环四检;
|
||||
3. 行进面:多入口首跳(入口停点候选=全部入口)、任一终点 Boss 胜利即关卡收束(多 Boss 泛化)、单链退化过校验;
|
||||
4. 定位信息值语义:`node_depth` 从节点编号换为节点深度(自入口步数);图内行进/断点寻址改生成图节点句柄(`_node_id` 双编号空间退役);
|
||||
5. 确定性承载:`first_script`/`template_pick`/`active_template_*`/模板 0 回滚档全退役;首局教学图=种子 42+参数档生成图;断点复现=主种子+参数档重生成同图;
|
||||
6. 冒烟断言面从模板形状写死断言换为生成图性质断言(商店编排三条、含岔口、确定性三条)。
|
||||
|
||||
**完成定义**(全部满足才算本批完成):
|
||||
1. 加载校验四检+良构基底在位:违规生成图起关致命(无软回退、无兜底图);旧六判据零残留;
|
||||
2. 多入口首跳、多 Boss 兑付收束、单链退化合法三条行进语义各有验收断言绿;
|
||||
3. 商店编排冒烟性质断言(终点前恰一店/商店数=终点数/全路径恰穿一店)在三关参数档×种子扫描面全绿;
|
||||
4. 定位信息=(关卡序号, 节点深度):enc 键名不变、值语义换代,与卡③带寻址衔接正常;图内寻址=句柄(读面 API 签名不变);
|
||||
5. 确定性承载换代落地:同 (种子, 参数档) 两次产图逐位同图;首局种子 42 教学图恒定;断点存→恢复→再产图同图;`template_pick` 断点键退役;
|
||||
6. `level.tres` 三行 `templates` 键清出(15 张模板数据清空,行级字段保留);全仓零 `first_script`/`template_pick`/`active_template` 引用(件核);
|
||||
7. §8 断言批全绿+变异验证记录表进实现报告;
|
||||
8. 批末重闸按改动域选档全绿+改前改后读数对拍;三份 `reports/` 产出物写齐(T4)。
|
||||
|
||||
## 2. 设计事实源
|
||||
|
||||
| # | 出处 | 取用条款 |
|
||||
|---|---|---|
|
||||
| D1 | `design/gdd/systems/冒险循环/关卡节点地图.md` v2.1(2026-10-21,06 闭合凭据之一) | :51 结构铁律走图三句(连线只朝前进方向无环/每条路径恰从一入口出发恰到一终点/终点恒为 Boss 可多个)+「违反任何一条的行集是配置违约,加载期拦截」;:63 商店编排(每个终点前一位恰一家店、每名玩家一局必经恰一家店、**位置约束不由加载校验承载、由冒烟断言把关**);:65 节点深度=从入口沿路走过来的步数、遭遇坐标=关卡序号+节点深度、单链合法退化、三关参数档都含岔口;:71-91 行进纪律(兑付后确认、零自动推进、入口不自动进入);:119 定位信息只有两样(关卡序号 + 节点深度);:126-136 关卡收束点(先清库后宣布);:174-186 验收标准(图合法四语义/兑付移交/商店房编排/单链特例合法) |
|
||||
| D2 | `design/gdd/systems/冒险循环/关卡节点图生成.md` v1.1(2026-10-21,06 闭合凭据之一——产出规格/执行边界,本卡校验规则语义来源) | :41-51 产出约束五条(道路系统=除岔口外每节点恰一条去路、岔口去路 2–3;宽度=分叉生长;层高=沿路步数;出口归属锁定;战斗节点保底)+结构铁律(无环、入口终点可多个〔个数=参数档〕、每路径恰一入口到一终点);:53-60 执行边界(不合格产出禁止进玩法、无兜底图、参数无解即致命中止、同种子+同参数档产出同一张图、随机只经 RngService 统一流);:62-72 五旋钮与三套参数档(生成器只吃参数档不认关卡号;第一关即含岔口;单链是分支密度取下限的合法退化形态、不作为三关参数档取值);:78-84 摆放约束(商店位=每个终点前一位恰一家商店);:101-115 调参表(层深 L1 ≥6 已锁、出口个数 L1 恒 1/L2/L3 1–3 已锁、商店数=出口数已锁、其余 provisional);:117-128 验收标准 |
|
||||
| D3 | `design/gdd/systems/冒险循环/冒险结构.md`(状态:已定稿) | :264 关卡行集行(关卡表逐关行承载关数与图的参数档;「旧模板池随实现批清数据」——本卡即该清理义务;「确定性 = 固定种子+固定参数档(首局教学与存档复现的固定图由此承载,无兜底模板)」);:275 结构冻结(终点恒为 Boss、终点可多个各在路径最深处;收束点先清库后宣布的次序;一冒险一主种子);:293 覆盖义务(行集内每个战斗与关底节点(关,深)在敌人与棋盘生成的数值表中有行——措辞随深度带换代见 §6 反哺点②) |
|
||||
|
||||
闭合凭据一致性:D1/D2 与 06 条目状态内列的两份定稿逐字一致;D3 关卡行集行与结构冻结行同向,且明文把「旧模板池清理」预写为实现批义务。
|
||||
|
||||
## 3. 现状事实(2026-10-21 worktree 实查;开工日 T0 复核行号)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| F1 | 图合法性校验唯一落点=`LevelTable.validate()`(表加载期、ConfigDB 收敛 fail-fast):单链校验体 `_validate_level_nodes`(Boss 计数恰一 :80-81、战斗 ≥2 :82-83、单链末 Boss :84-87、单链前三纯战斗 U2 规则 :91-95);图合法性核心 `_validate_graph`(悬空出边+前进方向「目标编号严格递增 ⇒ 无环」:123-129、**出度上限 2** :130-135、**入口唯一〔无入边恰一〕** :136-146、**Boss 唯一终点+无出边+编号最大** :147-154、双向可达〔无孤儿+全员达 Boss〕:155-179、BFS 层宽 ≤5 U2 规则 :180-197);id 解析双编号空间(图=node_depth/单链=数组下标):106-122 | `game/data/schemas/level_table.gd:20-54`/:58-96/:101-198 |
|
||||
| F2 | 模板池加载路径:`level.tres` 三关各 5 张模板(15 张;L1 五张=单链排列变体、L2/L3 网状 10 节点);`LevelTable.validate` 池分支(池内全模板加载期校验);`LevelRunner._resolve_template_row`(template_pick ≥0 强制取模板/−1 经 `rng.open_stream("node_map")` 抽签;`active_template_name` 供 MC 分层出数、`active_template_index` 供断点备案);`start_level`/`start_level_external` 各带 `template_pick` 尾参;池解析在播种后(EC7) | 模板数据 `game/data/tables/level.tres:8-10`+表尾注释(t5/U2 生成史);校验分支 `level_table.gd:38-53`;解析 `game/core/level_runner.gd:299-324`(:304-305/:307/:317-318);尾参 :130/:186;播种序 :162 |
|
||||
| F3 | 首局/回滚档(模板 0)读取处:`main.gd` `first_script = not save_manager.is_first_adventure_done()` 传入冒险参数面;种子三分判定(①显式合法整数②**首局固定 42**〔教学重放模板 0 即在此种子下标定〕③非首局 randi());`AdventureRunner._first_script`(true=全关零抽签取池内模板 0)→ `template_pick = 0 if _first_script else -1`;`SaveManager.is_first_adventure_done/mark_first_adventure_done`(存档布尔位,置位只在冒险收束挂点);冒烟「模板 0 = 现行固定图(回滚档成员)」断言段(池=5/强制 0 逐位相等/N=1 回滚档形态/first_script 分支/收束置位) | `game/presentation/main.gd:591-595`/:1427-1439;`game/core/adventure_runner.gd:45`/:102/:194/:850;`game/core/save_manager.gd:136-144`/:181-182;`game/smoke/smoke_all.gd:705-773`(:716-718/:719-732/:744-748/:749-763/:764-773) |
|
||||
| F4 | 商店「Boss 前一位必经」冒烟断言:`_smoke_shop_graph` 硬编码旧模板形状(L2/L3 商店房恰 1 且 node_depth=8〔next=[9]〕:5133、Boss=9=全图最大编号 :5134、DFS 枚举全路径穿商店房 8 :5135-5140、跨表 (·,9)/(·,8) 行覆盖 :5141-5152);路径枚举 `_all_entry_boss_paths`(DFS 迭代栈);行进断言「L2 行进到商店房(节点 8,next=[9]——Boss 前必经位)」;模板感知取节点表 `_level_active_nodes`(恒取模板 0) | `game/smoke/smoke_all.gd:5114-5152`/:5313-5339/:5034/:3438-3445 |
|
||||
| F5 | Boss 编号最大语义:`_validate_graph` ⑤「Boss 恒唯一终点:无出边 + 编号最大」——`ids[i] != ids.max()` 报「终点编号最大」;连带 :80-81 Boss 计数恰一 | `game/data/schemas/level_table.gd:147-154`/:80-81 |
|
||||
| F6 | 定位信息与寻址现状:`begin_battle` 组装 enc=`level_index` + `node_depth`(**值实为节点编号**)+ `node_type`;`_node_id` 双编号空间(图形态=node_depth 当节点身份/单链=数组下标,混用注记在案);`_entry_node` 取无入边**唯一**节点;`pick_next_node(node_id)` 按编号选路;断点 `node_id`/`visited_ids` 同编号空间;战斗侧按 (level_index, node_depth) 解析敌/板行集 | 组装 `game/core/level_runner.gd:467-482`(:467/:479);`_node_id` :338-344;`_entry_node` :348-356;`pick_next_node` :573-581;断点 `game/core/adventure_runner.gd:1000`/:1017;消费 `game/core/battle_orchestrator.gd:327`/:355/:445/:1051-1052;跨表覆盖校验 `game/core/config_db_cross_validator.gd:54-85`(模板池感知分支 :68-75) |
|
||||
| F7 | 行进语义现状:defer_entry 入口停点(单候选=唯一入口)、`present_entry_choice`、兑付确认后 `_present_choices` 出边候选、零自动推进;`_on_battle_finished` 以「节点 type=="boss"」判关卡收束(Boss 胜=victory、任意败=defeat,多 Boss 场景本就泛化);单链隐式出边(无 next 键=数组序下一项)+`_graph_mode` 探测 | `level_runner.gd:184-233`(:219-223/:228-233)/:561-569/:510-535(:516)/:359-367/:156-159、:203-206 |
|
||||
| F8 | 断点与确定性承载现状:快照 28 键含 `template_pick`(=`active_template_index`)、`master_seed`、`rng_counter`、`node_id`、`visited_ids`;续玩 `resume_adventure` 回填 `rng.restore_adventure(master_seed, rng_counter)`+强制 `template_pick`(已抽模板续玩零重抽零 RNG 消耗);rng 快照契约=主种子+计数器 | `game/core/adventure_runner.gd:998-1031`(:1000/:1013/:1014/:1015/:1017)/:140-148/:851-854;`game/core/rng_service.gd:4`/:73-80 |
|
||||
| F9 | 模板/first_script 依赖面共 7 文件:`adventure_runner.gd` 7 处、`level_runner.gd` 17 处、`main.gd` 2 处、`level_autoplay.gd` 4 处(run_level 尾参 template_pick :37/:63、MC 分层出数字段 `level_templates` :141/:202、route 轴注释「候选节点编号最小/最大」:12/:377)、`smoke_all.gd` 28 处、`feat_vouchers_test.gd` 1 处、`pool_case_d_test.gd` 5 处(均传 `{"first_script": true}`) | 各文件如列;`game/tests/unit/core/feat_vouchers_test.gd:25`/`pool_case_d_test.gd:37/:68/:113/:142/:160` |
|
||||
| F10 | 形状写死的冒烟断言面(模板时代遗留):L2/L3「节点数 = 10」:4564/:4726、「L1 行集零改动(单链 Boss (1,4))」:5151-5152、逐节点扫描断言 :4567-4759、参数面断言「入口特有键(first_script/shop_mode)随 extra 原样并入」:5944-5952;历史生成工具 `tools/t5_build_templates.py`/`t17_fanout2_rebuild.py`(一次性内容批工具留档) | `game/smoke/smoke_all.gd` 如列;`tools/t5_build_templates.py:8` 等 |
|
||||
|
||||
## 4. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 加载校验换代与 fail-fast**:加载校验(生成图到手、进节点前执行)=四检+良构基底——① 多入口/多终点合法(入口〔无入边节点〕≥1、终点〔无出边节点〕≥1;入口/终点个数与参数档的一致性归卡①产出侧——D1:176 加载校验清单无此项,本卡不核个数);② 每条路径恰好从一个入口出发、恰好到达一个终点(全部节点从某入口可达+可达某终点,无孤儿无断头);③ 终点恒为 Boss(每个终点 type="boss" 且每个 boss 节点无出边);④ 无环(出边目标节点深度严格大于源——连线只朝前进方向);良构基底=节点句柄唯一、出边引用全部命中、type ∈ 五类枚举、battle/boss 节点带节点深度。违规=致命(push_error+起关拒绝,冒险启动失败走既有 fatal 路径),无软回退、无兜底图(D2 执行边界「无效图绝不上给玩家」)。
|
||||
|
||||
🔴 **契约 2 · 弃节点编号寻址**:定位信息=(关卡序号, 节点深度)——`node_depth` 键名保留、**值语义换代为自入口步数**(现值实为节点编号,F6);图内行进/断点/呈现读面的节点身份=生成图节点句柄(卡①产出的唯一 id),禁再以 `node_depth` 兼任节点身份(`_node_id` 双编号空间退役)。定位信息两样契约不破(D1:119;卡③ §0 同口径)。
|
||||
|
||||
🔴 **契约 3 · 确定性承载=固定种子+固定参数档**:图=(种子, 参数档) 的纯函数——同 (种子, 参数档) 两次产图逐位同图,复现不依赖统一流全局计数器位次(D3:264「首局教学与存档复现的固定图由此承载」+D2 执行边界「同种子+同参数档产出同一张图」合取);首局教学图=首局固定种子 42(main.gd 种子三分判定保持)+固定参数档生成图;存档复现=断点 `master_seed`+参数档重生成同图(断点零新增图身份键);「模板 0=回滚档」语义废除——模板池、`template_pick` 强制、`active_template_*`、`first_script` 参数键全退役(F3/F9 清单)。**旧断点代差显式处置**:断点快照扩列代差标记键(缺省 0/写 1,扩列不升版先例);续玩遇缺标记键的旧断点=`resume_adventure` 显式拒绝(返回 false+push_error,禁按句柄语义静默回放——旧 `node_id` 值与新句柄空间可碰撞错位),旧模板图已随模板池退役、无迁移动作可做(禁凭空捏图=无兜底图红线);处置留痕实现报告。
|
||||
|
||||
🔴 **契约 4 · 商店编排语义不破**:每个终点前一位恰一家店(终点直接前驱恰为一家 shop 节点)、商店总数=终点数、所有入口→终点路径穿过且恰穿过一家店(D2 调参表「商店数=出口数」已锁+D1:63)。位置约束**不由加载校验承载、由冒烟断言把关**(D1:63 明文),加载校验禁新增商店位检查。
|
||||
|
||||
🔴 **契约 5 · 行进纪律与收束次序不破**:兑付后确认+零自动推进不变;入口不自动进入(多入口时首跳=「从哪进场」的选择,入口停点候选=全部入口节点);任一终点 Boss 战胜利即关卡收束(多 Boss 泛化,走哪条路兑付哪个 Boss)、英雄死亡=失败;收束次序先调战斗结算清库、后宣布关卡结束(D3:275 结构冻结);单链退化形态合法(校验不设「必有岔口」),三关参数档都含岔口是参数/生成面事实(冒烟挂验)。
|
||||
|
||||
🔴 **契约 6 · 模板清理义务**:15 张模板数据与模板池代码路径(含冒烟/单测引用面,F9/F10)全部清退,禁留死代码死断言;参数档数据(五旋钮 schema 与初值)不归本卡——随卡①参数 schema 落位,消费侧只读不值(D2「生成器只吃参数档」+数值事实源=data/tables)。
|
||||
|
||||
🔴 **契约 7 · 接口纪律**:生成器产图取用卡①接口(§7 取用块;签名与图数据形状以卡①定稿为准,**两条硬语义不随签名浮动**:同 (种子, 参数档) ⇒ 同图;纯函数复现);与卡③的定位信息传递契约零改动(enc 键名 `level_index`/`node_depth`/`node_type` 保持);本卡不实现生成算法、不动敌人表深度带数据(卡③)、不做迷雾可见性(卡④)、不落参数档数值(定值批/难度曲线)。
|
||||
|
||||
🔴 **契约 8 · 多终点深度互异(跨卡推论条款 A,卡③契约 6 同源)**:定位信息只有两样且「多终点各配一行(走哪条路决定打谁)」⇒ 多终点必须落在互异深度(同深度多终点=设计不可能态);图侧兑现归卡①产出规格(卡③ §6 反哺点③已布),本卡不新增校验;执行中发现同深度多终点图样 → L3 呈报。
|
||||
|
||||
## 5. 范围边界
|
||||
|
||||
**做**:加载校验四检+良构(`level_table.gd` 换代);行进语义三件(多入口首跳/多 Boss 收束/单链退化合法);定位信息值语义换代+句柄寻址;确定性承载换代(first_script/template_pick 链路退役、断点键换代);旧 15 张模板 `level.tres` 数据清理+模板池代码路径清退;冒烟断言面换代(商店三条性质断言、含岔口、确定性三条);断言批与变异验证;反哺点清单。
|
||||
|
||||
**不做**(明示去向):
|
||||
- 生成器算法本体(图怎么长、多终点怎么切、产出约束五条的产出侧自证与测试)→ **卡①**;本卡只取用其产图接口;
|
||||
- 敌人表深度带数据、带切法、`resolve_enemy_rowset` 三参化 → **卡③**(已写卡);本卡只保证传递侧 (关卡序号, 节点深度) 值语义正确;
|
||||
- 迷雾可见性(近处见类型/远处起雾、Boss 标记不入雾)→ **卡④**;
|
||||
- 生成图深度扩面的敌/板表数据覆盖(深度带补行/带切法粗化、board_gen/difficulty 键带化)→ 卡③ §5 已登记归口(卡①/后续表批);本卡只把交付序列写死(§0),不扩数据;
|
||||
- 参数档数值(层深/分支密度/配比/入口个数/出口个数的取值)→ 定值批落表(D2 调参表状态列),本卡零数值;
|
||||
- 呈现层改版(level-map 屏面布局、节点编号显示、多终点标记画法)→ level-map/HUD 框架与卡④;本卡只保证 runner 只读读面签名不变;
|
||||
- 历史生成工具 `tools/t5_build_templates.py`/`t17_fanout2_rebuild.py` → 一次性内容批留档不动(`level.tres` 表尾注释已载明)。
|
||||
|
||||
## 6. 关联文档同步面
|
||||
|
||||
> 本批是代码实现批,禁写 `design/` 整目录与根规则文件,唯一可写目录=`reports/`。下表「同步义务」一律执行为:逐条列进 `reports/iter<N>-反哺点-<完成日>.md`(哪个文件哪一节、改成什么、依据),用户会话裁决后归文档批落文。
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/gdd/systems/冒险循环/关卡节点地图.md` | :57「战斗|携带节点编号」、:156「组装关卡序号 + 节点编号」、:178「每场开战调用携带(关卡序号 + 节点编号)」三处与 :23/:65/:111/:119「关卡序号 + 节点深度」不一致 → 统一为「关卡序号+节点深度(自入口步数)」(本卡契约 2 弃节点编号寻址的文档面) | 反哺点 · 列清单(①用词统一,依据=D1:65/:119 定稿句+用户卡②范围句) |
|
||||
| `design/gdd/systems/冒险循环/冒险结构.md` | :293 覆盖义务「每个战斗与关底节点(关,深)在敌人与棋盘生成的数值表中有精确行」→ 深度带换代后措辞改「敌侧深度带命中+板侧解析命中」;:264 关卡行集行「旧模板池随实现批清数据」括注在本卡完成后成既成事实(括注注销) | 反哺点 · 列清单(②措辞换代+括注注销,依据=卡③契约 1/本卡完成定义 6) |
|
||||
| `design/gdd/systems/冒险循环/关卡节点图生成.md` | 无改动(本卡消费其产出规格与执行边界)——但校验四检的实现措辞如与产出规格用语需对齐(「岔口」「终点」「路径」定义引用),T1 注释引用其节号 | 反哺点 · 列清单(③仅在实现注释发现用语漂移时列,无漂移则记「无」) |
|
||||
| `design/gdd/ux/level-map.md` | 图结构(多入口/多终点/含岔口)呈现面随动义务(06 条目连带面②)——本卡不改呈现代码,读面 API 签名不变(句柄化后编号显示语义漂移:显示的是句柄) | 反哺点 · 列清单(④呈现面随动需求登记归卡④/level-map 文档批) |
|
||||
| `待办清单.md`(06 登记处) | 本卡登记条状态推进「已写卡(`reports/需求卡片-节点图消费侧换代批-2026-10-21.md`)」(req-card 06 状态回写义务);实现批完成后按退役规则处理 | 反哺点 · 列清单(⑤状态回写与条目退役记载——本会话文件面限于本卡一个文件,回写由批次收口/用户发令执行,防四卡并行互踩) |
|
||||
| `roadmap.md` | 迭代行编入笔与批内状态刷新 | 反哺点 · 列清单(⑥) |
|
||||
| `game/data/schemas/level_table.gd`、`game/data/tables/level.tres`、`game/core/level_runner.gd`、`game/core/adventure_runner.gd`、`game/core/config_db_cross_validator.gd`、`game/presentation/main.gd`、`game/core/save_manager.gd` | 消费侧换代与模板清理本体 | 实施本体(可写) |
|
||||
| `game/smoke/*`、`game/tests/*` | 断言面换代(§8 S 清单) | 实施本体(可写) |
|
||||
|
||||
## 7. 任务卡
|
||||
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核本卡全部行号。回归措辞模板沿用:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
|
||||
|
||||
### 卡前 · 文件结构图
|
||||
|
||||
| 文件 | 职责 |
|
||||
|---|---|
|
||||
| `game/data/schemas/level_table.gd` | 图合法性校验换代:四检+良构基底(`validate_graph` 静态入口);旧六判据与模板池校验分支退役;行级校验保留 |
|
||||
| `game/data/tables/level.tres` | 15 张模板数据清理(三行 `templates` 键删除;行级字段保留;参数档字段随卡① schema 不归本卡) |
|
||||
| `game/core/level_runner.gd` | 图来源换代(卡①产图替代 `_resolve_template_row`)+起关校验调用+多入口首跳+句柄寻址(`_node_id` 双空间退役)+enc `node_depth` 值语义换代 |
|
||||
| `game/core/adventure_runner.gd` | `first_script`/`template_pick` 链路退役;断点键换代(`template_pick` 退役、`node_id`/`visited_ids` 句柄化) |
|
||||
| `game/core/config_db_cross_validator.gd` | ④ 关卡节点半边退役(模板池感知分支删除;敌侧带校验归卡③,本卡只删死分支) |
|
||||
| `game/presentation/main.gd` | `first_script` 参数构造退役(种子三分判定保持不动) |
|
||||
| `game/core/save_manager.gd` | `is_first_adventure_done` 注释语义换代(模板 0 教学重放 → 首局固定种子;键与行为不变) |
|
||||
| `game/smoke/smoke_all.gd`、`game/smoke/level_autoplay.gd`、`game/smoke/shot_probe.gd`、`game/smoke/ui_click_probe.gd` | 模板冒烟段退役+商店冒烟换代+形状断言性质化+生成图取数面(§8 S 清单) |
|
||||
| `game/tests/unit/core/level_table_validate_test.gd`(新建) | 四检+良构单测(正向/负向夹具图) |
|
||||
| `game/tests/unit/core/breakpoint_resume_test.gd`、`game/tests/unit/core/pool_case_d_test.gd`、`game/tests/unit/core/feat_vouchers_test.gd` | 断点键换代随动、`first_script` 键清退 |
|
||||
|
||||
### 接口块(全卡共用,后卡引用以此为准)
|
||||
|
||||
- **取用卡①产出:生成器产图接口**(期望签名,**以卡①定稿签名为准**):`generate(level_index: int, params: Dictionary, seed: int) -> Dictionary`。返回期望形状:`{"nodes": Array[Dictionary], "entries": Array[int], "terminals": Array[int]}`;节点期望字段:`{"id": int(图内唯一句柄), "type": String(battle/boss/reward/rest/shop), "depth": int(自入口步数), "next": Array[int](出边目标句柄)}`。**两条硬语义不随签名浮动**:① 同 (seed, params) ⇒ 逐位同图(D2 执行边界);② 纯函数复现——图=(seed, params) 的函数,复现不依赖统一流全局计数器位次(D3:264 存档复现承载合取)。生成器内部取数仍经 RngService(禁自建 RNG 红线不破)——纯函数复现与统一流的机制协调(derive_at 纯派生/号位内重放/RngService 纯流面)归卡①接口定稿,硬语义不变。图数据内部适配(若卡①定稿字段名为 `node_depth`/`depth` 或 id 载体不同)在 T0 对齐后按定稿接线,enc 键名不变。
|
||||
- **取用卡①产出:参数档 schema**(期望五旋钮行字段:层深/分支密度/配比/入口个数/出口个数,落 `level.tres` 关卡行;字段名与初值以卡①定稿为准,数值归定值批)——消费侧只读该参数档传给 `generate`,不自定值。
|
||||
- **产出(供战斗侧/卡③)**:`begin_battle` enc 保持 `level_index: int` + `node_depth: int`(**值=节点深度**)+ `node_type: String`;与卡③ `resolve_enemy_rowset(L, D, row_kind)` 衔接零改动。
|
||||
- **产出(供呈现层)**:runner 只读读面签名不变——`nodes() -> Array`、`node_id_of(node) -> int`(句柄)、`node_out_ids(node) -> Array`、`current_node_id() -> int`、`next_candidate_ids() -> Array`、`visited_ids() -> Array`、`pick_next_node(node_id: int) -> bool`。
|
||||
- **校验入口**:`LevelTable.validate_graph(nodes: Array) -> Array`(静态函数,返回错误字典列表——与现役 `validate()` 错误形态同构;落点 `level_table.gd`,校验体单一住所不拆)。
|
||||
|
||||
### T0 事实坐实与卡①接口对齐(≈0.5–1d)
|
||||
|
||||
- [ ] 复核 §3 全部 file:line 与 §2 取用条款行号,漂移即订正本卡——可核查结果:订正记录或「零漂移」声明进实现报告素材。
|
||||
- [ ] 与卡①接口对齐四件:产图签名、图数据形状(id/type/depth/next 字段名)、参数档 schema 字段名、复现语义(纯函数复现两条硬语义确认)——可核查结果:对齐纪要(定稿签名对照本卡接口块,漂移处回写本卡接口块)。
|
||||
- [ ] 生成图深度域实测:对 L1/L2/L3 参数档各以种子 42 生成,记录最大节点深度;对照敌/板表覆盖(敌侧顶带键、板侧键域)——可核查结果:覆盖实测表在案;缺覆盖(预期如此,§0 硬交付序列)则登记「数据覆盖批先行」开工前置,缺覆盖状态下不进 T2 切源合入。
|
||||
- [ ] 改前读数基线采集(冒烟/单测通过数,两入口铁规下随 T1 迭代 loop 首跑取值)——可核查结果:基线读数记录在案。
|
||||
- [ ] §6 反哺点①-⑥ 条目起草(一节一改一依据)——可核查结果:反哺点草稿段落在 T4 产物成文前就绪。
|
||||
|
||||
### T1 加载校验换代(≈1.5–2d)
|
||||
|
||||
- [ ] 实装 `LevelTable.validate_graph(nodes: Array) -> Array`(签名与判据=契约 1 四检+良构基底;落点 `level_table.gd`)。可核查结果:正向夹具图(多入口多终点/含岔口/单链)零错误;负向夹具图(非 Boss 终点/带出边的 Boss/环/孤儿节点/断头节点/悬空出边/句柄重复/缺 depth 的战斗节点)各报对应错误类别(沿 `structural`/`dangling_next`/`dup_key`/`enum`/`domain` 词表)。
|
||||
- [ ] 退役旧判据六项(`level_table.gd:80-81` Boss 恰一、:82-83 战斗 ≥2、:84-87 单链末 Boss、:91-95 前三纯战斗、:130-135 出度 ≤2、:153-154 编号最大)与模板池校验分支(:38-53),行级校验(level_index 唯一/display_name/rest_heal_pct 域)保留。可核查结果:`validate()` 对生产表行级零错误;全仓检索六判据消息文本零残留。
|
||||
- [ ] 实装无环判据=出边目标节点深度严格大于源(取代「目标编号严格递增」,:123-129 换代)。可核查结果:回边夹具(目标深度 ≤ 源)报「违反前进方向」错误。
|
||||
- [ ] 验收步骤:跑 `godot --headless --path game --script res://tests/unit/core/level_table_validate_test.gd`,期望四检正负向断言全绿。
|
||||
- [ ] 变异验证(注入→最窄闸见红→`git checkout` 还原→记录):注释掉「终点恒为 Boss」判据 → 期望非 Boss 终点负向用例红;把无环判据改成恒真 → 期望回边负向用例红。记录表进实现报告。
|
||||
|
||||
### T2 消费侧切源与行进/定位/确定性换代(≈2–2.5d)
|
||||
|
||||
- [ ] 实装图来源换代:`level_runner.gd` `_resolve_template_row`(:299-324)删除,起关路径改调卡① `generate(level_index, params, seed)`(params 读关卡行参数档、seed 取本局种子口径——单关自播种/冒险主种子派生,以卡①定稿为准)。可核查结果:起关拿到生成图(夹具参数产图可走通进节点)。
|
||||
- [ ] 实装起关校验接线:生成图到手、进节点前调用 `LevelTable.validate_graph`,违规 push_error+返回 false(起关拒绝,冒险启动失败走既有 fatal 路径——`main.gd:596` `_fatal_screen`)。可核查结果:违规夹具图起关被拒(返回 false)。
|
||||
- [ ] 实装多入口首跳:`_entry_node`(:348-356)换 `_entry_nodes` 返回全部无入边节点;`defer_entry`/`present_entry_choice` 入口停点候选=全部入口(单入口行为逐位不变)。可核查结果:双入口夹具图首跳候选数=2,pick 任一入口正确进入。
|
||||
- [ ] 实装句柄寻址:`_node_id`(:338-344)改返回节点句柄(卡① id 字段);`_out_ids`/`_out_nodes`/`visited_ids`/`pick_next_node`/`_restore_to_stop` 同空间随动;单链隐式出边(:359-367 数组序)退役(生成图恒带显式出边,单链=出度 ≤1 的图)。可核查结果:单链夹具图(显式出边)行进走通;`pick_next_node` 候选外句柄拒绝。
|
||||
- [ ] 实装定位信息值语义换代:`begin_battle` enc(:467)`node_depth` 取节点 `depth` 值(自入口步数);`level_finished` 载荷位置字段句柄化;enc 键名不变。可核查结果:载荷断言(level_index/depth 与图上节点一致)绿。
|
||||
- [ ] 实装 `adventure_runner.gd` 模板链路删除:`_first_script` 字段与参数读入(:45/:102/:194)、`template_pick` 计算与传递(:850/:851-854/:863)整体删除。可核查结果:全仓检索 `_first_script`/`template_pick` 零引用(`level_autoplay`/单测面随动见 T3)。
|
||||
- [ ] 实装断点键换代:`_snapshot_breakpoint` 的 `template_pick` 键(:1013)删除、扩列代差标记键(契约 3);`node_id`/`visited_ids`(:1000/:1017)句柄化落档与 `_restore_to_stop` 回读同空间;`resume_adventure` 遇缺标记键旧断点显式拒绝(返回 false+push_error)。可核查结果:断点写读往返无 `template_pick` 键、句柄往返一致;旧形态夹具断点被拒(`breakpoint_resume_test` 面)。
|
||||
- [ ] 实装 `main.gd` `first_script` 参数构造删除(:593-595);种子三分判定(:1427-1439)零改动。可核查结果:首局(`is_first_adventure_done==false`)种子=42、非首局随机两态行为不变。
|
||||
- [ ] 实装 `save_manager.gd:136` 注释语义换代(「模板 0 教学重放」→「首局固定种子」;键与行为零改动)。可核查结果:注释与契约 3 同口径。
|
||||
- [ ] 实装跨表校验死分支清理:`config_db_cross_validator.gd:54-85` ④ 关卡节点半边(模板池感知 :68-75 及其 node_lists 循环)删除(敌侧带校验归卡③、板侧覆盖随数据批,本卡只删静态节点校验死分支)。可核查结果:全仓检索 `node_lists`/「模板池感知」零残留。
|
||||
- [ ] 验收步骤:T 段出口跑一次全套闸(`tools/run_all_gates.sh` 按改动域选档),期望除 §8 S 清单未换代条目外零红(S 清单随 T3 收口)。
|
||||
- [ ] 变异验证:把多入口候选写死单入口 → 期望多入口首跳断言红;把 enc `node_depth` 值取句柄 → 期望定位信息载荷断言红;把断点 `template_pick` 键偷偷写回 → 期望断点键退役断言红。记录表进实现报告。
|
||||
|
||||
### T3 模板清理与冒烟断言换代(≈2–2.5d)
|
||||
|
||||
- [ ] 实装 `level.tres` 三行 `templates` 键删除(15 张模板数据清出;`level_index`/`display_name`/`rest_heal_pct` 保留)。可核查结果:`git diff` 仅删模板数据段+表尾注释一段(15 张模板去向+参数档字段指引——沿 `level.tres` 既有表尾注释体例);冒烟表校验段行级零错误。
|
||||
- [ ] 换代模板冒烟段(S1):`_smoke_node_map_templates`(`smoke_all.gd:705-773`)①-⑥ 退役(node_map 标签/池=5/模板 0=现行/强制 0/N=1 回滚档/first_script 分支),⑦ 收束置位断言保留改写(语义换「首局种子 42」注释);替代断言=确定性三条(S6)。可核查结果:该段绿且消息文本无「模板 0/回滚档」字样。
|
||||
- [ ] 换代生成图取数面(S2):`_level_active_nodes`(:3438-3445)改经卡①接口取生成图(固定种子+关卡行参数档);消费点(:977/:3452/:4564/:4567/:4682/:4726/:4729/:4738/:5126)随动。可核查结果:该段绿、取数面零模板引用。
|
||||
- [ ] 换代商店冒烟(S3,三条性质断言):① 每个终点的直接前驱恰为一家 shop 节点(终点入边恰一且 type=="shop");② 商店总数=终点数;③ 所有入口→终点路径穿过且恰穿过一家店(DFS 枚举换代 `_all_entry_boss_paths` 消费句柄/深度,判定=路径上 shop 计数恰 1);断言面=L1/L2/L3 参数档 × 种子扫描(42 与 range(42, 62) 全种子);硬编码「node_depth 8/9/穿节点 8」退役(:5133-5140);:5034 行进到商店房断言改性质(路径上恰一店)。可核查结果:三性质断言在扫描面全绿。
|
||||
- [ ] 换代形状写死断言(S4)::4564/:4726「节点数 = 10」改生成图读数性质(节点数 ≥ 层高下限+随图);:5151-5152「L1 行集零改动(单链 Boss (1,4))」退役(单链模板消亡,单链合法性改由 V2 夹具承载);逐节点扫描断言(:4567-4759)敌侧键面随卡③ S 清单、本卡只换取数面。可核查结果:清单条目逐条勾销、冒烟全绿。
|
||||
- [ ] 换代覆盖义务断言(S5):节点源换生成图后,敌侧覆盖判据=`band_of` 非 -1(禁顶 clamp)、板侧=解析命中非空;以 §0 硬交付序列(敌侧带数据覆盖)为绿灯前提。可核查结果:覆盖断言绿(数据批先行后)。
|
||||
- [ ] 新增确定性冒烟(S6):① 同 (种子, 参数档) 两次 `generate` 逐位同图;② 首局种子 42 两次开局(`is_first_adventure_done==false`)生成图逐位同图;③ 断点写→`restore_adventure`→再产图同图;④ `level.tres` 无 `templates` 键。可核查结果:四断言绿。
|
||||
- [ ] 换代驱动/工具面(S7):`level_autoplay.gd` `template_pick` 尾参退役(:37/:63)、MC 字段 `level_templates`(:141/:202)改生成图身份字段(`level_maps`,值=种子+参数档标识)、route min/max 轴(:12/:377)改句柄升/降序(确定性轴保持);探针注释与驱动假设(`shot_probe.gd:12-13`、`ui_click_probe.gd:281`「L2 商店节点 8」)性质化。可核查结果:autoplay/探针跑通、MC 出数字段名更新。
|
||||
- [ ] 换代单测随动(S8):`breakpoint_resume_test.gd`(断点无 `template_pick` 键、`node_id`/`visited_ids` 句柄往返、复现同图);`pool_case_d_test.gd`/`feat_vouchers_test.gd` `first_script` 键清退(:37/:68/:113/:142/:160/:25);`main.gd` 参数面断言(`smoke_all.gd:5944-5952` first_script extra 键样例换非 first_script 键,「入口特有键随 extra 原样并入」语义保持)。可核查结果:单测面全绿、全仓 `first_script`/`template_pick`/`active_template` 零引用(件核)。
|
||||
- [ ] 验收步骤:T 段出口跑一次全套闸,期望冒烟+单测全绿、探针段无红。
|
||||
- [ ] 变异验证:终点前驱改非店节点 → 期望商店断言①红;某路径加第二家店 → 期望③红;生成器夹具注一滴计数器依赖(同种子两次产图不同)→ 期望 S6① 红;`level.tres` 偷留一个 `templates` 键 → 期望 S6④ 红。记录表进实现报告。
|
||||
|
||||
### T4 产出物落位卡(≈0.5d,末卡——三份 `reports/` 文件写齐才算收口)
|
||||
|
||||
- [ ] 轻闸全绿后、合并前写齐三份:`reports/iter<N>-实现报告-<完成日>.md`(各卡状态+变异验证记录表+实机目检总清单+遗留读数义务)/`reports/iter<N>-代拍板追认单-<完成日>.md`(§10 白名单内代拍项留痕)/`reports/iter<N>-反哺点-<完成日>.md`(§6 六条每条一落点:①关卡节点地图.md 节点编号措辞、②冒险结构.md 覆盖义务措辞+括注注销、③关卡节点图生成.md 用语核对、④level-map.md 呈现面随动登记、⑤06 状态回写与条目记载、⑥roadmap 编入笔)。可核查结果:三文件在 `reports/` 且 §6 同步面每条有落点。
|
||||
|
||||
**估时合计 ≈6.5–8.5d**(T0 0.5–1+T1 1.5–2+T2 2–2.5+T3 2–2.5+T4 0.5)。切分预案:T1 可单独一轮(夹具驱动、不碰运行时);T2+T3 须同轮落地(切源与模板断言换代同拍,拆开中间态冒烟必红);超软盒(>9d)预挂:T3 冒烟面清单若超一孔,先落 S1–S6(模板退役/商店/确定性主链)保主链绿,S7/S8 的探针与单测随动次轮补,全清单绿才收口。
|
||||
|
||||
## 8. 验证闸(逐需求点验收清单)
|
||||
|
||||
| # | 需求点 | 测什么(断言/行为) | 落点/判定手段 |
|
||||
|---|---|---|---|
|
||||
| V1 | 加载校验四检+良构 | 正向:多入口多终点图/含岔口图/单链图过校验;负向:非 Boss 终点、带出边 Boss、环(回边)、孤儿节点、断头节点、悬空出边、句柄重复、战斗节点缺 depth——各报错误且起关拒绝 | 单测 `level_table_validate_test.gd`(新建)+起关拒绝冒烟 |
|
||||
| V2 | 多入口首跳 | 双入口图入口停点候选=2(全部入口),pick 任一正确进入;单入口行为逐位不变 | 单测(runner 注入图/生成器夹具参数驱动,形态以卡①接口定稿为准) |
|
||||
| V3 | 大岔口行进与多 Boss 兑付收束 | 多终点生成图(L2/L3 参数档出口 >1 时大岔口分区域)走不同区域各兑付各自 Boss(走哪条路决定打谁——A 区路径不误兑付 B 区 Boss);任一 Boss 胜=`level_finished` 恰一次(result=victory);倒下=defeat;收束次序先清库后宣布(既有断言保持) | 冒烟(生成图驱动全程) |
|
||||
| V4 | 单链退化合法+三关含岔口+大岔口自 L2 起 | 单链夹具图过校验(不报「必有岔口」类错误);L1/L2/L3 参数档生成图各含 ≥1 个岔口节点(出度 ≥2);L1 生成图终点恰 1(大岔口自 L2 起的图侧投影) | 单测+冒烟(生成图扫描) |
|
||||
| V5 | 商店编排三条 | 每终点直接前驱恰一店;商店总数=终点数;所有入口→终点路径恰穿一店(DFS);三关参数档×种子扫描(42+range(42,62))全绿 | 冒烟(S3 换代断言) |
|
||||
| V6 | 定位信息两样 | begin_battle 载荷 (level_index, node_depth) 与图上节点 depth 一致、enc 键名零变化;与卡③三参解析衔接(敌侧行集命中) | 冒烟+单测载荷断言 |
|
||||
| V7 | 确定性承载 | 同 (种子, 参数档) 两次产图逐位同图;首局种子 42 两次开局同图;断点存→恢复→再产图同图 | 冒烟(S6 ①②③) |
|
||||
| V8 | 模板 0 语义废除+旧断点代差 | `level.tres` 无 `templates` 键;全仓零 `first_script`/`template_pick`/`active_template` 引用;`is_first_adventure_done` 种子选择两态行为不变(42/随机);缺代差标记键的旧断点被显式拒绝续玩(禁静默回放) | 冒烟(S6④+收束置位改写断言)+单测(旧断点拒绝——`breakpoint_resume_test`)+件核 |
|
||||
| V9 | 15 张模板清理 | 三行 `templates` 键不在;模板池代码路径(`_resolve_template_row`/模板池校验分支/`_level_active_nodes` 模板读取)零残留;表尾去向注释在位 | 冒烟表断言+件核(git diff 仅删模板段) |
|
||||
| V10 | 反哺点清单 | §6 六条每条有落点 | 件核对表(T4) |
|
||||
|
||||
### 8.1 冒烟/断言面随动清单(S 清单——模板时代遗留、须换代的断言/直呼面,2026-10-21 实查)
|
||||
|
||||
| # | 断言/直呼面 | 位置 | 换代内容 |
|
||||
|---|---|---|---|
|
||||
| S1 | 模板池冒烟段 `_smoke_node_map_templates` | `game/smoke/smoke_all.gd:705-773` | ①-⑥ 退役(node_map 标签/池=5/模板 0=现行/强制 0/N=1 回滚档/first_script 分支);⑦ 收束置位保留改写(语义换首局种子) |
|
||||
| S2 | 模板感知取节点表 `_level_active_nodes` 及消费点 | `smoke_all.gd:3438-3445`;消费 :977/:3452/:4564/:4567/:4682/:4726/:4729/:4738/:5126 | 改经卡①接口取生成图(固定种子+参数档) |
|
||||
| S3 | 商店图不变式 `_smoke_shop_graph`+行进到商店房断言 | `smoke_all.gd:5114-5152`/:5034 | 换三条性质断言(终点前恰一店/商店数=终点数/全路径恰穿一店);硬编码 8/9/穿节点 8 退役 |
|
||||
| S4 | 形状写死断言 | `smoke_all.gd:4564`/:4726(节点数=10)/:5151-5152(L1 行集零改动)/:4567-4759(逐节点扫描) | 节点数改生成图读数性质;L1 单链断言退役;扫描断言敌侧键面随卡③ S 清单、本卡换取数面 |
|
||||
| S5 | 覆盖义务断言(en_keys 键聚合) | `smoke_all.gd:971-981`/:4677-4687/:5144-5147 | 节点源换生成图;敌侧判据=band_of 非 -1(禁顶 clamp)、板侧=解析命中非空;以 §0 数据覆盖为绿灯前提 |
|
||||
| S6 | 确定性冒烟(新增) | `smoke_all.gd` 新段 | 同图三断言+`level.tres` 无 templates 键(V7/V8 承载) |
|
||||
| S7 | 驱动/工具面 | `level_autoplay.gd:37/:63/:141/:202/:12/:377`;`shot_probe.gd:12-13`;`ui_click_probe.gd:281` | template_pick 尾参退役;MC 字段 `level_templates`→`level_maps`;route 轴句柄序;探针假设性质化 |
|
||||
| S8 | 单测随动面 | `breakpoint_resume_test.gd`;`pool_case_d_test.gd:37/:68/:113/:142/:160`;`feat_vouchers_test.gd:25`;`smoke_all.gd:5944-5952`(参数面 extra 键样例) | 断点键换代断言;first_script 键清退;extra 键样例换非 first_script 键(断言语义保持) |
|
||||
|
||||
清单口径:S1–S6 归 T3 冒烟面,S7 归 T3 驱动面,S8 归 T2/T3 单测面;`tools/` 下历史生成脚本不随动(§5 不做)。
|
||||
|
||||
### 8.2 改动—验收对照表
|
||||
|
||||
| 需求点 | 改动 | 验收测试 | 真实入口 | 撤销或变异证明 |
|
||||
|---|---|---|---|---|
|
||||
| 加载校验四检+良构 | `level_table.gd` 新增 `validate_graph`、旧六判据与模板池校验分支退役 | V1(`level_table_validate_test.gd` 正负向夹具+起关拒绝冒烟) | 起关真实入口 `LevelRunner.start_level_external`(违规图返回 false) | 变异:注释「终点恒为 Boss」判据→负向红;无环判据短路→回边红 |
|
||||
| 行进语义(大岔口/多 Boss/单链) | `level_runner.gd` 多入口首跳+句柄寻址+单链显式出边;收束泛化保持 | V2(双入口候选断言)/V3(多终点全程驱动)/V4(单链夹具+含岔口+L1 终点恰 1) | 冒烟生成图全程驱动(`level_autoplay.run_level` 真实入口)+runner 夹具单测 | 变异:候选写死单入口→V2 红 |
|
||||
| 商店编排冒烟换代 | `smoke_all.gd` `_smoke_shop_graph` 三条性质断言换代 | V5(终点前恰一店/商店数=终点数/全路径恰穿一店,种子扫描) | 冒烟商店段(`python tools/run_smoke.py`) | 变异:终点前驱改非店→①红;路径加第二店→③红 |
|
||||
| 定位信息两样 | `begin_battle` enc 值语义换代(node_depth=深度)+句柄寻址 | V6(载荷断言+卡③解析衔接) | 战斗真实入口 `begin_battle`(冒烟驱动开战) | 变异:enc node_depth 取句柄→V6 红 |
|
||||
| 确定性承载+旧断点代差 | `adventure_runner.gd`/`main.gd`/`save_manager.gd` 模板链路退役+断点键换代+代差标记键 | V7(S6 同图三断言)/V8(模板键零引用+旧断点拒绝) | 冒烟 S6+`breakpoint_resume_test.gd`+续玩真实入口 `resume_adventure` | 变异:生成器注计数器依赖→V7 红;旧断点放行回放→V8 红 |
|
||||
| 15 张模板清理 | `level.tres` 三行 `templates` 键删除+模板池代码路径清退 | V9(表断言+件核) | 冒烟表校验段+`git diff` 件核(仅删模板段) | 变异:留一个 `templates` 键→V9 红 |
|
||||
| 反哺点清单 | §6 六条落 `reports/iter<N>-反哺点-<完成日>.md` | V10(件核对表) | T4 产出物件核 | 撤销证明:缺任一落点→V10 件核红 |
|
||||
|
||||
**独立验收审计**(test-conventions §9.7):合并前由未参与实现的会话或子代理执行——审计入口=只读需求卡片+本对照表+diff+测试文件+实现报告,自跑最窄测试抽查(不采信「已全绿」自述);审计报告文件=`reports/iter<N>-验收审计-<日期>.md`,逐行写「通过/缺漏/例外及依据」,未通过不得合并。测试执行两入口铁规不变(审计在迭代 loop 内启动的子代理算 loop;另开会话须用户点名)。
|
||||
|
||||
**测试方式两分法(2026-10-11 用户定)**:V1–V10 全部机检自动化(冒烟+单测);能力验证可经调试接口直改种子/参数档驱动生成图(沿用现役 DebugOverlay/DS_DEBUG 门控)。**用户目检项**:「三关实机选路观感不回归(多入口首跳/大岔口取舍/多终点走向可见)」+「首局教学图体验(42 种子固定图)」两条——观测项写进 `reports/iter<N>-实现报告-<完成日>.md` 实机目检总清单,登记不阻塞(验收时间用户自行安排)。
|
||||
|
||||
**闸口径**:轻闸每任务卡出口自跑;批末重闸=`tools/run_all_gates.sh` 按改动域选档(本批只碰 `game/` 代码与数据表 → 代码域档)全绿才合并+改前改后读数对拍。回归措辞:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
|
||||
|
||||
**变异验证伴生**(新增/改写验收函数逐条注入实验,记录表进实现报告):终点恒 Boss 判据注释(V1 红)/无环判据短路(V1 红)/多入口候选写死单入口(V2 红)/终点前驱改非店(V5 红)/路径加第二店(V5 红)/enc node_depth 取句柄(V6 红)/生成器注计数器依赖(V7 红)/旧断点缺代差键却放行回放(V8 红)/level.tres 留 templates 键(V9 红)。变异不红则改判据或如实标注拦截边界。
|
||||
|
||||
## 9. 异常分级
|
||||
|
||||
- **L1(自救)**:断言措辞、命名、注释、消息文本;冒烟消息随语义换代更新。
|
||||
- **L2(记录继续)**:图数据内部适配形态(句柄载体/生成图在 runner 内的持有结构);校验实现形态(深度单调判据的实现写法);冒烟断言定位写法(生成图直取 vs 经 runner 读面);测试驱动形态(注入图 vs 生成器夹具参数);route 轴/MC 字段命名细节(§10 ⑤)。
|
||||
- **L3(停机呈报)**:卡①接口与两条硬语义(同图/纯函数复现)不兼容且超出适配面;执行中发现同深度多终点图样(推论条款 A 被破,契约 8);任何触碰定位信息两样契约、收束次序、商店编排语义的诉求;生成图深度域超出敌/板表覆盖且数据批无人接手(§0 硬交付序列失管);模板清理出现残留被外部引用且本卡兜不住;校验四检判据被要求放宽(违 D1:51「加载期拦截」)。
|
||||
|
||||
## 10. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. 图数据内部适配形态(节点句柄载体、生成图在 runner 内的持有结构、`generate` 返回值的内部解析)——约束:读面 API 签名不变、句柄唯一、禁 node_depth 兼身份(契约 2)。
|
||||
2. 校验实现形态(无环判据的深度单调写法 vs DFS 判环——契约 1 语义内)与校验错误类别命名(沿 `structural`/`dangling_next`/`dup_key`/`enum`/`domain` 词表,确需新类别时命名留痕)。
|
||||
3. 断点键换代细节(代差标记键命名、退役键清单的缺键容忍、句柄字段的断点序列化形态、旧断点拒绝后的盘面处置〔清/留〕)——约束:save 域扩列不升版先例、复现同图不破、旧断点禁静默回放(契约 3)。
|
||||
4. 冒烟断言定位写法(生成图直取 vs 经 runner 读面)与断言名措辞——约束:禁写死节点编号形状(契约 4)。
|
||||
5. `level_autoplay` route 轴实现(句柄升/降序的取数写法)与 MC 字段名 `level_maps` 的值形态(种子+参数档标识)。
|
||||
6. 提交切分与 worktree 分支命名。
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门=U0。**
|
||||
|
||||
四处灰色地带已按设计事实推导定值(用户可否决,随 T4 反哺点清单呈报可见):
|
||||
|
||||
- **确定性复现机制(契约 3)**:备选「断点存生成图快照」——淘汰依据=D3:264「固定图由此(固定种子+固定参数档)承载」明文,存图快照不是「由种子+参数档承载」;备选「统一流号位重放」——淘汰依据=复现依赖全局计数器位次,与 D2 执行边界「同种子+同参数档产出同一张图」冲突(同参数档不同位次会漂);定值=生成图=(种子, 参数档) 纯函数,断点零新增图身份键,续玩以主种子+参数档重生成同图。依据=D3:264+D2:60 合取。
|
||||
- **U2 两规则退役(单链前三纯战斗/层宽 ≤5)**:备选「保留作生成图加载校验」——淘汰依据=两规则的判据对象是旧模板形状(`level.tres` 表尾 U2 返工注记在案),D2 v1.1 产出规格无此约束(「宽度=分叉生长」新语义无层宽上限;「前三纯战斗」在含岔口图上不可定义);备选「移交卡①产出约束」——淘汰依据=产出规格五条已锁清单无此两条,扩产出规格归文档批非本卡;定值=随模板池退役,教学担子由首局种子 42 固定图+难度曲线数值责任承载(D1:120「教学位节点同样掷骰、没有结构豁免」)。依据=D2 产出规格+用户卡②范围句「规则语义以生成文档产出规格为准」。
|
||||
- **多终点深度互异不入本卡校验(契约 8)**:备选「消费侧加载校验加第五检」——淘汰依据=用户范围句明列四检「勿扩勿缩」,且卡③ §0 已把图侧兑现归口卡①产出规格;定值=本卡不新增校验、只留 L3 呈报口(同卡③契约 6 口径)。依据=用户范围句+卡③契约 6。
|
||||
- **旧断点代差处置(契约 3 代差句)**:备选「旧断点静默按新语义回放」——淘汰依据=旧 `node_id` 值与新句柄空间可碰撞错位(静默错节点=违反 fail-fast);备选「迁移旧断点」——淘汰依据=旧模板图已随模板池退役、旧 node_id 指向的图不复存在,无迁移动作可做(禁凭空捏图=无兜底图红线);定值=扩列代差标记键、缺键旧断点显式拒绝续玩(返回 false+push_error),盘面处置(清/留)落 §10 ③。依据=AGENTS.md fail-fast 红线+D2 无兜底图+迭代 21 T5 开发期破坏性先例。
|
||||
|
||||
---
|
||||
|
||||
**自审五查记录(2026-10-21)**:①规格覆盖——§1 六件目标/八项完成定义逐条挂 T 卡(校验=T1、行进/定位/确定性=T2、商店冒烟/模板清理=T3、反哺点=T4;确定性横跨 T2 链路退役+T3 断言补齐),无漏项;②步骤扫描——全部步骤一步一动作一可核查结果,实现体只给签名+判据名(`validate_graph` 判据清单、商店三性质断言、确定性三断言),无抄写体;③类型一致性——接口块签名与 T 卡引用逐字一致(`generate(level_index, params, seed)`/`validate_graph(nodes)`/enc 三键/runner 读面七个方法名);④审查焦点——补挂四类失败模式:违规图放行(V1 变异)、商店位漏店/双店(V5 变异)、复现漂移(V7 变异)、模板键残留(V9 变异);⑤篇幅比例——规格约 1 屏、任务卡步骤与实现面 1:1,函数体零展开。
|
||||
|
||||
**grilling 复核记录(2026-10-21,设计树对抗复核,不采访用户——可推导定值写依据、真归用户列 §11)**:按设计树走查八支——①校验语义边界(四检 vs 产出规格五条:五条是卡①产出侧自证〔D2:117 产出合格判据〕、四检是消费侧加载拦截〔D1:51/:176 明列清单〕,「个数=参数档」括注归产出侧、本卡不核个数——边界写进契约 1①);②节点深度歧义(合流/多入口下 depth 赋值一致性归卡①产出规格,消费侧以「出边目标深度严格大于源」兜前进方向);③多入口路径语义(「每条路径恰好从一个入口出发」=每个节点在某条入口→终点路径上〔无孤儿无断头〕+入口为无入边节点——形式化写进契约 1②);④确定性复现(三案淘汰两案、定值纯函数复现,§11 留否决口;RNG 机制协调归卡①接口定稿、硬语义不浮动);⑤U2 规则去留(判据对象消亡+产出规格无据 → 退役定值,§11 留否决口);⑥商店「前一位」形式化(D1:63「前一位恰一家店」+D1:182「恰穿一家」+D2「商店数=出口数」三者合取 ⇒ 终点入边恰一且前驱是店——契约 4 表述核对);⑦**旧断点代差(本轮新查出的真缺口)**——旧断点 node_id 与新句柄空间可碰撞静默错位,定值代差标记键+显式拒绝(§11 留否决口);⑧跨卡序列(生成图深度域 vs 敌侧带覆盖=真实缺口,卡③ §5 已登记归口——本卡把交付序列写死为 §0 硬前置而非待裁)。code-conventions 对照:§2.2「跨层标识符语义单一且成文」恰为契约 2 的规范依据(node_depth 双语义是现役违规态)、S9 类型闸与既有 `validate()` 同形、§4.11 新单测不经 autoload 无冲突。AGENTS.md 硬红线逐条过:数值单点(本卡零数值、参数档只读)、RNG 单点(生成器取数归卡①、本卡不自建 RNG)、测试两入口(T 卡只写断言不跑测)、实现批禁写 design/(§6 义务全走反哺点、零旧口径义务措辞)、fail-fast(违规图致命无软回退+旧断点显式拒绝)。结论:零待裁点,四处灰色推导已定值并留 §11 供用户否决。
|
||||
@@ -0,0 +1,260 @@
|
||||
# 需求卡片:节点图生成器核心批(参数化长图生成器)· 关卡节点图重构卡①
|
||||
|
||||
- 版本:**v1.0**(2026-10-21 成文;自审五查+grilling 复核结论见文末 §11)
|
||||
- 来源:[待办清单](../待办清单.md)「关卡节点图重构——可选节点太少」(2026-10-13 登记,来源=playnote,设计面=文档轨,状态=文档设计已闭合)(**唯一事实源**:[关卡节点图生成](../design/gdd/systems/冒险循环/关卡节点图生成.md) **v1.1**(2026-10-21 双定稿闭合凭据之一);协作凭据=[关卡节点地图](../design/gdd/systems/冒险循环/关卡节点地图.md) v2.1+[冒险结构](../design/gdd/systems/冒险循环/冒险结构.md) 关卡行集/结构冻结/覆盖义务行)
|
||||
- 归属:关卡节点图**生成侧**(参数化长图生成器算法本体+五旋钮参数档数据表+产出约束自证+执行边界+确定性承载);关卡节点图重构批**卡①**(卡②消费侧换代/卡③敌人表深度带/卡④迷雾可见性同批,均已写卡)
|
||||
- 排期:未编入(2026-10-18 用户令:需求卡片不预占迭代号,批次归属按工作量另裁)
|
||||
- 开工门:U0(用户 `/iter` 发令)+ grilling 复核门过(零待裁点,见 §11)
|
||||
|
||||
## 0. 前置依赖与并行性
|
||||
|
||||
- **设计闭合(第〇步)**:06 条目「关卡节点图重构——可选节点太少」状态=文档设计已闭合,凭据=关卡节点图生成.md v1.1+关卡节点地图.md v2.1(各带路径+版本号)→ 放行写卡。
|
||||
- **本卡是生成侧,被卡②硬依赖**:卡②(消费侧换代)运行时图来源换成生成器产图,其契约 7/接口块明文「签名与图数据形状**以卡①定稿为准**」;本卡 §7 接口块即该定稿,卡② T0 按本卡对齐(产图签名/图数据形状/参数档 schema 字段名/复现语义四件)。**并行无阻塞**:本卡不依赖卡②实现,自证产出合法图即可独立验收。
|
||||
- **输出推论条款 A 给卡③**:卡③(敌人表深度带)契约 6 推论条款 A「多终点深度互异」的**图侧兑现归本卡产出规格**(卡③ §6 反哺点③明文派发);本卡把「多终点落在互异节点深度」定为产出约束(§4 契约 3/§8 V6)。
|
||||
- **RNG/复现机制由本卡定稿**:卡② 契约 3 把「纯函数复现与统一流的机制协调(derive_at 纯派生/号位内重放/RngService 纯流面)归卡①接口定稿,硬语义不变」——本卡 §4 契约 1 定值为 RngService 纯流面(推导链见 §11)。
|
||||
- **深度赋值语义由本卡定稿**:卡② 契约 1④/grilling 注记「合流/多入口下 depth 赋值一致性归卡①产出规格」——本卡定值为「自入口最长路径步数」(§4 契约 2,推导链见 §11)。
|
||||
- **生成图深度扩面的敌/板表覆盖(遭遇坐标覆盖)不在本卡**:归卡③ §5 已登记的「生成图深度扩面覆盖问题(卡①/后续表批)」中的**后续表批**——本卡只产出深度值并登记交付序列依赖(§5 不做/§6 反哺点),敌/板表补行是数值/敌人表(卡③/后续表批),本卡零敌数值(用户范围句明示「敌人表深度带(卡③)」不做)。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标**:把关卡节点图生成 v1.1 的「参数化长图生成器」落成一个**纯函数生成器**——吃「参数档+种子」产出一张合法节点图,交付给消费侧(卡②)。四件本体:① 五旋钮(层深/分支密度/配比/入口个数/出口个数)+ L1/L2/L3 参数档数据表(schema+.tres,provisional 值);② 产出约束五条+结构铁律(生成器产出自证);③ 摆放约束三条(商店位/岔路取舍·下一跳/岔路取舍·支路构成);④ 执行边界四条(不合格禁止进玩法/无兜底图/参数无解即 fail-fast/同种子+同参数档=同图、随机只经 RngService)。
|
||||
|
||||
**相对现状,新增了什么**(现状=固定模板池,见 §3):
|
||||
1. 运行时参数化生成器:图从「固定 15 张模板池」换成「(参数档, 种子) 纯函数生成」——改形态=改参数不改代码;
|
||||
2. 五旋钮参数档数据表(schema+.tres,L1/L2/L3 三档,provisional 值)+ ConfigDB 注册与单点读取;
|
||||
3. 产出约束/结构铁律/摆放约束的**产出侧自证**(生成器交付前强制过一遍,不过即参数无解致命);
|
||||
4. 确定性承载:同(种子, 参数档)⇒ 逐位同图,纯函数复现、复现不依赖统一流全局计数器号位(RngService 纯流面);
|
||||
5. 多入口多终点/每路径恰一入一出/大岔口自 L2 起(L1 出口恒 1)/多终点深度互异 的图侧兑现。
|
||||
|
||||
**完成定义**(全部满足才算本批完成):
|
||||
1. `generate(params, seed)` 产出图**必过**产出约束五条+结构铁律+摆放约束三条(生成器产出自证在位,自证失败=参数无解致命中止);
|
||||
2. 参数档数据表(schema+.tres)落地 ConfigDB 单点读取,L1/L2/L3 五旋钮齐备(provisional 值,域校验通过);生成器只吃参数档、不认关卡号;
|
||||
3. 确定性:同(种子, 参数档)两次产图逐位同图;复现不依赖统一流计数器号位(counter 无关);随机只经 RngService(纯流面),零 `randomize()`/零自建 RandomNumberGenerator;
|
||||
4. 生长递增:L1/L2/L3 读数(最长路径步数、节点总数)逐关递增,且节点总数增幅 Δ(L2→L3)>Δ(L1→L2);
|
||||
5. 专项兑现:L1 出口恒 1、大岔口(多终点归属锁定)自 L2 起;任意岔口各去路下一节点类型互不相同、各支路构成互不相同;每条入口→终点路径恰过一家店;多终点落在互异节点深度;战斗节点数 ≥ 保底数;
|
||||
6. 参数无解(给不出合法图)=配置错误,致命中止(fail-fast),无兜底图、无软回退;
|
||||
7. §8 验收断言批全绿+变异验证记录表进实现报告;
|
||||
8. 批末重闸按改动域选档全绿+改前改后读数对拍;三份 `reports/` 产出物写齐(T4)。
|
||||
|
||||
## 2. 设计事实源
|
||||
|
||||
| # | 出处(**唯一事实源**) | 取用条款 |
|
||||
|---|---|---|
|
||||
| D1 | `design/gdd/systems/冒险循环/关卡节点图生成.md` **v1.1**(2026-10-21,06 闭合凭据之一——本卡 100% 以它为唯一设计依据) | :41-49 产出约束五条(道路系统=除岔口〔≥2 去路〕外每节点恰一条去路、岔口去路 2–3/宽度=分叉生长〔不摆平行层、同深度节点数由岔口自然长出 1→2→3→4〕/层高=沿路步数〔入口到终点最长路径步数、L1≥6、逐关递增且增幅变大〕/出口归属锁定〔大岔口分成互不连达区域、各区域只通向自己的终点、终点前一层去路集合恰含一个终点〕/战斗节点保底);:51 结构铁律(连线只朝前进方向无环/入口终点可多个〔个数=参数档〕/每条路径恰从一入口出发恰到一终点);:53-60 执行边界四条(不合格产出禁止进玩法/无兜底图〔软回退禁〕/参数无解即致命中止/同种子+同参数档产出同一张图、随机只经 RngService 统一流);:62-72 五旋钮与三套参数档(层深/分支密度/配比/入口个数/出口个数;**生成器只吃参数档、不认关卡号**;生长曲线层高宽度节点数路径长度逐关递增且增幅变大、决策点密度逐关递增、第一关即含岔口;单链是分支密度取下限合法退化形态、不作为三关取值;大岔口自第二关起、第一关出口恒 1);:74-88 配比与摆放约束三条(商店位=每终点前一位恰一店/岔路取舍·下一跳=一岔口各去路下一节点类型互不相同、下一跳必在可见范围/岔路取舍·支路构成=一岔口各支路节点类型构成互不相同、每支路至少一个别的支路没有的类型)+扩展节点类型位;:86 内容归 owner(战斗打谁=敌人系统深度带配池);:101-115 调参表(层深 L1≥6 已锁/L2>L1 增幅变大 provisional/岔口去路 2–3 已锁/分支密度 provisional〔一局十几次〕/入口 1 起 provisional/出口 L1 恒 1、L2/L3 1–3 已锁范围/配比 provisional/战斗保底数 provisional/商店数=出口数已锁);:117-128 验收标准(产出合格/生长递增〔L1/L2/L3 最长路径步数与节点总数递增、Δ(L2→L3)>Δ(L1→L2)〕/归属锁定/必经恰一店/岔路取舍/可复现/无软回退/战斗保底) |
|
||||
| D2 | `design/gdd/systems/冒险循环/关卡节点地图.md` v2.1(2026-10-21,06 闭合凭据之一——走图视角语义参照,本卡不实现走图) | :51 结构铁律走图三句(无环/每路径恰一入一出/终点恒为 Boss 可多个)+「规则语义以关卡节点图生成的产出规格为准」;:57-61 五类节点(battle/boss/reward/rest/shop,Boss 恒为终点);:65 节点深度=自入口步数、遭遇坐标=关卡序号+节点深度、终点恒在各路径最深处、单链合法退化、三关参数档都含岔口;:63 商店编排(每终点前一位恰一店、每局必经恰一店、位置约束由冒烟断言把关非加载校验);:103 下一跳必在可见范围(可见性规则归卡④);:119 定位信息只有两样(关卡序号+节点深度) |
|
||||
| D3 | `design/gdd/systems/冒险循环/冒险结构.md`(状态:已定稿) | :264 关卡行集行(关卡表逐关行承载关数与图的参数档;「旧模板池随实现批清数据」;「**确定性 = 固定种子+固定参数档(首局教学与存档复现的固定图由此承载,无兜底模板)**」);:275 结构冻结(终点恒为 Boss〔终点可多个、各在路径最深处〕、一冒险一主种子、收束点先清库后宣布);:293 覆盖义务(行集内每个战斗与关底节点〔关,深〕在敌人与棋盘生成的数值表中有行——**措辞随深度带换代**,敌/板表覆盖归卡③/后续表批) |
|
||||
|
||||
闭合凭据一致性:D1 与 06 条目状态内列的设计定稿逐字一致(`关卡节点图生成.md` v1.1);D2/D3 为协作语义参照,与 D1 同向(确定性、结构铁律、覆盖义务)。
|
||||
|
||||
## 3. 现状事实(2026-10-21 worktree 实查;开工日 T0 复核行号)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| F1 | **无运行时参数化生成器**;图来自固定模板池(15 张=3 关×5 张,L1 五张=单链排列变体 7 节点、L2/L3 网状 10 节点 node_depth 0–9);离线工具 `tools/t5_build_templates.py`/`t17_fanout2_rebuild.py` 一次性预生成入库(非运行时生成) | 模板数据 `game/data/tables/level.tres:8`(L1 五链)/:9(L2 五网)/:10(L3 五网);表尾生成史注释 `level.tres:13-19`(:19「生成器 = tools/t5_build_templates.py」);离线工具 `tools/t5_build_templates.py`/`tools/t17_fanout2_rebuild.py` 在盘 |
|
||||
| F2 | 节点图数据结构=`level_row.nodes: Array[Dictionary]`,节点=`{node_depth, type, next?}`;图形态=任一节点带 `next`(`_graph_mode` 探测);节点 id 双编号空间(图=`node_depth` 当节点身份/单链=数组下标,注记「混用两空间会碰撞」);入口取「无入边**唯一**节点」 | 结构与探测 `game/core/level_runner.gd:67`/:156-159/:203-206;`_node_id` 双空间 `level_runner.gd:338-344`;`_entry_node` 唯一 `level_runner.gd:348-356`;出边 `_out_ids`/`_out_nodes` `level_runner.gd:360-379` |
|
||||
| F3 | 现行图合法性校验约束与 D1 v1.1 冲突(旧模板形状约束):出度 ≤2(:130-135)、入口唯一〔无入边恰一〕(:136-146)、Boss 唯一终点+无出边+编号最大(:147-154)、Boss 计数恰一(:80-81)、单链末 Boss(:84-87)、单链前三纯战斗 U2(:91-95)、BFS 层宽 ≤5 U2(:180-197)——D1 v1.1 多入口多终点/岔口去路 2–3/大岔口自 L2 起 与之冲突;换代归卡②(消费侧加载校验),本卡产出侧按 D1 v1.1 | 校验体 `game/data/schemas/level_table.gd:80-81`/:84-87/:91-95/:130-135/:136-146/:147-154/:180-197 |
|
||||
| F4 | 模板抽取经 `node_map` 流(`rng.open_stream("node_map")` 抽签)/`template_pick` 强制取模板;`begin_battle` 组装 enc=`level_index`+`node_depth`(值实为节点编号)+`node_type` | 抽取 `level_runner.gd:307-324`(:317-318 抽签、:314-315 强制);enc 组装 `level_runner.gd:467-482`(:467/:479) |
|
||||
| F5 | RngService 统一随机流单点(禁 `randomize()`/自建 RNG):`open_stream(purpose)` 经全局计数器取号(计数器 +1)、`derive_at(scope)` 纯派生不推计数器、`PURPOSE_TAGS` 含 `node_map`;快照契约=主种子+计数器 | 类头纪律 `game/core/rng_service.gd:3`;`PURPOSE_TAGS` :9;`derive_at` :34-37;`set_master_seed` :52;`new_adventure` :67;`open_stream` :86-99;AGENTS.md:73「RNG 单点——禁 randomize()/自建 RandomNumberGenerator,随机只经 RngService 统一流」 |
|
||||
| F6 | ConfigDB 数据表注册模式:`_tables` 映射表名→.tres、`var level: LevelTable`、`load_tables` 装载、访问器单点读取(`level_row(level_index)`);数值单一事实源=`data/tables/*.tres` 经 ConfigDB 单点读 | 注册映射 `game/core/config_db.gd:20`;成员 `config_db.gd:40`;装载 `config_db.gd:142`;访问器 `config_db.gd:245-246`;AGENTS.md:72 数值单一事实源 |
|
||||
| F7 | 数据表 schema 范例(`rows: Array[Dictionary]`+`validate()` 域校验,空表=合法态):难度表 schema 同构可循 | `game/data/schemas/difficulty_table.gd:7`(rows)/:17-31(validate 域校验);同构参照 `game/data/schemas/level_table.gd:7/:20` |
|
||||
| F8 | Core 层纪律:完整玩法须可 `--headless --script` 跑通、零 UI 依赖;`--script` 模式禁裸 autoload 标识符(ConfigDB/RngService 经节点路径注入) | AGENTS.md:75;`code-conventions.md:71`(`--script` 禁裸 autoload,经 `get_node("/root/…")`);`level_runner.gd:2-3`(Core 零 UI、headless 可跑) |
|
||||
|
||||
## 4. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 生成器纯函数复现+RNG 单点(纯流面)**:`generate(params, seed)` 是 **(参数档, 种子) 的纯函数**——同(种子, 参数档)两次产图逐位同图,复现**不依赖**统一流全局计数器号位(D3:264「固定图由固定种子+固定参数档承载」+D1:60「同种子+同参数档产出同一张图」合取;断点续玩以「主种子+参数档」重生成同图、断点零新增图身份键——resume 在停点号位重生成,号位≠产图号位,故必须 counter 无关)。生成器取数**只经 RngService 纯流面**(新增 `pure_stream(seed_val, scope) -> RandomNumberGenerator` 或等价纯派生面:seed 键值、纯函数、**不推 `_counter`、不落事件日志**);**禁 `randomize()`/禁自建 RandomNumberGenerator/禁 `open_stream` 取数**(open_stream 推计数器=号位相关,破坏 counter 无关)。RNG 单点红线不破:随机仍出自 RngService(自建 RNG 才是红线违例,RngService 提供纯流面属服务内分工)。纯流面机制协调是本卡定稿(§11 推导),硬语义(纯函数/counter 无关/同图)不随机制实现浮动。
|
||||
|
||||
🔴 **契约 2 · 节点深度语义单一(id=句柄/depth=自入口步数)+多终点深度互异**:图数据节点字段拆两轴——`id: int`=图内唯一句柄(导航身份);`depth: int`=**自入口最长路径步数**(遭遇坐标取值,D2:65「节点深度=自入口步数」)。禁 `node_depth` 兼任身份与深度(code-conventions §2.2「位置/句柄/键三种不同、禁凭当前恰好相等传值」——现役 node_depth 双语义即违规态)。`depth` 赋值定值=**自入口最长路径步数**(多入口取「距任一入口最长路径」),此赋值使「出边目标 depth 严格大于源」成为有效无环判据(D1:51 无环+卡② 契约 1④)+与「层高=沿路最长步数」(D1:47)一致。**多终点深度互异**(推论条款 A,卡③ 契约 6 派发本卡兑现):多终点各落在互异 `depth`(同 depth 多终点在「定位信息只有两样〔关,深〕」下不可区分=设计不可能态,D2:119+D1:96「多终点各配 Boss 行」+「走哪条路决定打谁」合取);单终点(L1)不涉。
|
||||
|
||||
🔴 **契约 3 · 产出约束五条+结构铁律是产出规格唯一陈述处(产出自证)**:生成器交付前强制自证产出过 D1:41-51 全部——产出约束五条(道路系统〔除岔口外每节点恰一条去路、岔口去路 2–3〕/宽度=分叉生长〔不摆平行层、宽度源于岔口〕/层高=沿路步数〔L1≥6、逐关递增增幅变大〕/出口归属锁定〔大岔口分互不连达区域、各区域只通自己终点、终点前一层去路集合恰含一个终点〕/战斗节点保底)+结构铁律(无环〔连线只朝前进〕/入口终点可多个〔个数=参数档〕/每路径恰从一入口出发恰到一终点)。自证不过=**产出不合格**(执行边界①)。
|
||||
|
||||
🔴 **契约 4 · 执行边界四条(无兜底图、fail-fast)**:① 不合格产出禁止进玩法(自证不过的图绝不上给玩家);② 无兜底图(不设固定兜底图、软回退一律禁);③ 参数无解即配置错误、致命中止(fail-fast,push_error+assert/既有 fatal 路径,禁静默降级、禁空图/残图回退);④ 同种子+同参数档=同图(见契约 1)。禁任何「生成失败就用模板/用空图/用上一张」回退。
|
||||
|
||||
🔴 **契约 5 · 摆放约束三条**:① 商店位=每个终点前一位恰一家商店(每名玩家一局必经恰一家店)+商店总数=出口数(D1:115 已锁)+每条入口→终点路径恰过一家店;② 岔路取舍·下一跳=一个岔口分出的各去路,下一节点类型互不相同;③ 岔路取舍·支路构成=一个岔口分出的各支路,节点类型构成互不相同、每支路至少一个别的支路没有的类型。位置约束(①)由生成器产出侧兑现+冒烟/单测断言把关(D2:63「不由加载校验承载、由冒烟断言把关」——卡② 加载校验不查商店位,本卡产出侧必须产对)。
|
||||
|
||||
🔴 **契约 6 · 五旋钮+生成器只吃参数档不认关卡号**:生成器输入=参数档(五旋钮值)+种子,**不接收、不读取关卡序号**(D1:72「生成器只吃参数档、不认关卡号」);调用方(冒险结构/卡②)负责 level_index→参数档 解析后把参数档交给 `generate`。图=f(参数档, 种子),与第几关无关(同参数档+同种子⇒同图,即便来自不同关)。五旋钮=层深/分支密度/配比/入口个数/出口个数;配比四类占比合计 1、战斗保底优先;商店数=出口数(结构事实,非配比自由项)。
|
||||
|
||||
🔴 **契约 7 · 接口纪律与边界**:生成器产出图给卡②(§7 接口块,签名与图数据形状本卡定稿、两条硬语义〔同图/纯函数复现〕不随签名浮动);`depth` 喂遭遇坐标 (关,深) 交卡③ 带寻址(enc 键名 `level_index`/`node_depth`/`node_type` 保持,`node_depth` 值=`depth`);图结构只读读面供卡④ 可见性(可见性规则归卡④,本卡只保证类型数据在节点上)。本卡不实现生成算法之外的:走图/行进/兑付(卡②)、敌人表深度带与敌数值(卡③)、迷雾可见性(卡④)、参数档数值定值(定值批,数值全 provisional)。
|
||||
|
||||
## 5. 范围边界
|
||||
|
||||
**做**:参数化长图生成器本体(`generate(params, seed)` 产合法图);五旋钮参数档数据表(schema+.tres,L1/L2/L3,provisional 值)+ ConfigDB 注册单点读;产出约束五条+结构铁律+摆放约束三条的产出侧自证;执行边界四条(含 RNG 纯流面确定性承载、参数无解 fail-fast);验收断言批(产出合格/可复现/岔路取舍/必经恰一店/归属锁定/多终点深度互异/战斗保底/生长递增/L1 出口恒 1·大岔口 L2 起/参数无解)+变异验证设计。
|
||||
|
||||
**不做**(明示去向):
|
||||
- 消费侧行进(加载校验换代、行进语义、句柄寻址、模板清理、确定性承载链路退役)→ **卡②**(已写卡);本卡只交付产图接口;
|
||||
- 敌人表深度带(带切法、`resolve_enemy_rowset`、敌数值、多 Boss 行集)→ **卡③**(已写卡);本卡只产出 `depth` 值与多终点深度互异;
|
||||
- 迷雾可见性(近处见类型/远处起雾、可见层数 N、Boss 标记不入雾)→ **卡④**(已写卡);本卡只保证节点 `type` 数据在节点上;
|
||||
- **参数档数值定值**(层深/分支密度/配比/入口/出口/战斗保底的最终取值)→ 定值批落表(D1 调参表状态列),本卡 .tres 值全 **provisional**;
|
||||
- **生成图深度扩面的敌/板表覆盖**(遭遇坐标覆盖义务:生成深度域须被敌侧深度带、板侧键域覆盖)→ 卡③/后续表批(§0);本卡只产出深度、登记交付序列依赖,零敌数值零板数值;
|
||||
- 生成算法的具体长图步骤(怎么从旋钮长出一张合法图)→ 代码实现自由(D1:28「生成算法本身归代码——图怎么长出来是实现的事」);本卡只定产出规格与执行边界(WHAT,不写 HOW);
|
||||
- 旧 15 张模板 `level.tres` 清理与模板池代码路径退役 → **卡②**(契约 6 模板清理义务);本卡不删模板数据;
|
||||
- 呈现层(level-map 画布自适应、多终点标记画法)→ level-map/HUD 框架与卡④。
|
||||
|
||||
## 6. 关联文档同步面
|
||||
|
||||
> 本批是代码实现批,禁写 `design/` 整目录与根规则文件,唯一可写目录=`reports/`。下表「同步义务」一律执行为:逐条列进 `reports/iter<N>-反哺点-<完成日>.md`(哪个文件哪一节、改成什么、依据),用户会话裁决后归文档批落文。同步义务一律走反哺点清单,禁用旧口径的「随文档批一并落」式措辞(2026-10-17 起作废)。
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/gdd/systems/冒险循环/关卡节点图生成.md` | 产出规格「多终点深度互异」条款缺位——本卡契约 2 兑现推论条款 A(卡③ 契约 6 图侧);「深度=自入口最长路径步数」赋值语义宜在图规格节成文(消 node_depth 双语义歧义) | 反哺点 · 列清单(①产出规格补「多终点落在互异节点深度」+「节点深度=自入口最长路径步数」赋值句,依据=D2:119+D1:96+卡③ 契约 6) |
|
||||
| `design/gdd/systems/冒险循环/关卡节点地图.md` | :65「节点深度=从入口沿路走过来的步数」与「最长路径步数」在合流图上的赋值一致性宜钉成「自入口最长路径步数」(消歧) | 反哺点 · 列清单(②深度赋值消歧补注,依据=契约 2 推导;无歧义实现则记「无」) |
|
||||
| `design/gdd/systems/冒险循环/冒险结构.md` | :293 覆盖义务措辞「精确行」随深度带换代(归卡③ 反哺点);:264「旧模板池随实现批清数据」归卡② 完成后注销(本卡不动模板数据) | 反哺点 · 列清单(③仅登记本卡产出深度域供覆盖核对,覆盖措辞归卡③ 落——本卡零改动) |
|
||||
| `design/gdd/ux/level-map.md` | 图形态随参数变化(多入口/多终点/含岔口/深度域扩面)呈现面自适应义务(06 条目连带面②)——本卡不改呈现代码 | 反哺点 · 列清单(④呈现面随动需求登记归卡④/level-map 文档批) |
|
||||
| `待办清单.md`(06 登记处) | 本卡登记条状态推进「已写卡(`reports/需求卡片-节点图生成器核心批-2026-10-21.md`)」(req-card 06 状态回写义务) | 反哺点 · 列清单(⑤状态回写与条目退役记载——本会话文件面限于本卡一个文件,回写由批次收口/用户发令执行,防四卡并行互踩) |
|
||||
| `roadmap.md` | 迭代行编入笔与批内状态刷新 | 反哺点 · 列清单(⑥) |
|
||||
| `game/core/node_map_generator.gd`、`game/data/schemas/level_gen_params.gd`、`game/data/tables/level_gen_params.tres`、`game/core/rng_service.gd`、`game/core/config_db.gd` | 生成器本体+参数档数据表+RNG 纯流面+ConfigDB 注册 | 实施本体(可写) |
|
||||
| `game/tests/unit/core/node_map_generator_test.gd`、`game/tests/unit/data_config/level_gen_params_test.gd`、`game/smoke/smoke_all.gd` | 验收断言面(§8 V 清单) | 实施本体(可写) |
|
||||
|
||||
## 7. 任务卡
|
||||
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核本卡全部行号与取用条款行号。回归措辞模板沿用:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。生成算法长图步骤(HOW)归实现(D1:28),本卡只钉产出规格、签名、取值与断言。
|
||||
|
||||
### 卡前 · 文件结构图
|
||||
|
||||
| 文件 | 职责 |
|
||||
|---|---|
|
||||
| `game/data/schemas/level_gen_params.gd`(新建) | 参数档 schema:五旋钮+战斗保底+岔口去路数域,`rows: Array[Dictionary]`(键=level_index)+`validate()` 域校验+`row_by_level_index()` 单点读 |
|
||||
| `game/data/tables/level_gen_params.tres`(新建) | L1/L2/L3 三行参数档数据(provisional 值) |
|
||||
| `game/core/node_map_generator.gd`(新建) | 生成器核心:`generate(params, seed) -> Dictionary` 产合法图;产出侧自证(产出约束五条+结构铁律+摆放约束三条);确定性(RngService 纯流面);参数无解 fail-fast |
|
||||
| `game/core/rng_service.gd`(改) | 新增纯流面 `pure_stream(seed_val: int, scope: int) -> RandomNumberGenerator`(seed 键值、纯函数、不推 `_counter`、不落日志)——生成器结构取数唯一源 |
|
||||
| `game/core/config_db.gd`(改) | 注册 `level_gen_params` 表(_tables 映射+成员+装载)+访问器 `level_gen_params_row(level_index: int) -> Dictionary` |
|
||||
| `game/tests/unit/core/node_map_generator_test.gd`(新建) | 生成器验收断言(产出合格/可复现/岔路取舍/必经恰一店/归属锁定/多终点深度互异/战斗保底/L1 出口恒 1·大岔口 L2 起) |
|
||||
| `game/tests/unit/data_config/level_gen_params_test.gd`(新建) | 参数档 schema validate+值域断言(五旋钮/合计 1/出口范围/层深下限) |
|
||||
| `game/tests/unit/core/node_map_growth_test.gd`(新建) | 生长递增断言(L1/L2/L3 最长路径步数与节点总数递增、Δ(L2→L3)>Δ(L1→L2))+参数无解 fail-fast 断言 |
|
||||
|
||||
### 接口块(全卡共用,邻卡引用以此为准——卡② T0 按此对齐)
|
||||
|
||||
- **产出(供卡② 消费侧):生成器产图接口(本卡定稿)**——`generate(params: Dictionary, seed: int) -> Dictionary`。返回形状:`{"nodes": Array[Dictionary], "entries": Array[int], "terminals": Array[int]}`。节点字段:`{"id": int(图内唯一句柄), "type": String(battle/boss/reward/rest/shop), "depth": int(自入口最长路径步数), "next": Array[int](出边目标句柄)}`。`entries`=入口句柄数组(无入边节点,个数=params.entry_count);`terminals`=终点句柄数组(type=boss、无出边,个数=params.exit_count)。**两条硬语义不随签名浮动**:① 同(seed, params)⇒ 逐位同图;② 纯函数复现——图=(seed, params) 的函数,复现不依赖统一流全局计数器号位。**签名修正(对卡② 期望签名 `generate(level_index, params, seed)` 的定稿裁剪)**:`generate` **不收 `level_index`**(契约 6「生成器只吃参数档、不认关卡号」,D1:72);调用方自行 level_index→params 解析后调 `generate(params, seed)`。卡② T0 按本卡去掉 level_index 形参接线。
|
||||
- **产出(供卡②/卡③ 定位信息)**:节点 `depth`=遭遇坐标深度轴,喂 enc `node_depth`(键名不变、值=`depth`);多终点落在互异 `depth`(契约 2)。enc 三键 `level_index`/`node_depth`/`node_type` 与卡③ `resolve_enemy_rowset(L, D, row_kind)` 衔接零改动。
|
||||
- **产出(参数档数据表)**:`level_gen_params` schema+table;ConfigDB 访问器 `level_gen_params_row(level_index: int) -> Dictionary`(返回该关五旋钮行);卡② 只读该参数档传给 `generate`,不自定值。字段名(schema 键):`layers`(层深)、`branch_density`(分支密度)、`battle_ratio`/`reward_ratio`/`rest_ratio`/`shop_ratio`(配比四类)、`entry_count`(入口个数)、`exit_count`(出口个数)、`battle_floor`(战斗保底数)、`fork_ways_min`/`fork_ways_max`(岔口去路数,锁 2/3)。
|
||||
- **取用(RngService 纯流面)**:`RngService.pure_stream(seed_val: int, scope: int) -> RandomNumberGenerator`(本卡新增;生成器结构取数唯一源,禁 `open_stream`/`randomize()`/自建 RNG)。`scope`=生成器取数分域(代拍板 §10 ①:单流多次取数 vs 逐决策 scope,约束=纯函数、counter 无关、同图)。
|
||||
- **校验边界**:生成器产出侧自证(本卡)=产前强制过产出约束五条+结构铁律+摆放约束三条;卡② 消费侧加载校验(`validate_graph`)=独立防御(四检+良构),两者同源 D1 产出规格、分工不重叠(本卡不实现消费侧校验)。
|
||||
|
||||
### T0 事实坐实与跨卡接口对齐(≈0.5–1d)
|
||||
|
||||
- [ ] 复核 §3 全部 file:line 与 §2 取用条款行号(D1 v1.1 条款行、F1–F8 落点),漂移即订正本卡。可核查结果:订正记录或「零漂移」声明进实现报告素材。
|
||||
- [ ] 与卡② T0 对齐四件:产图签名(去 level_index 的 `generate(params, seed)`)、图数据形状(id/type/depth/next 字段名)、参数档 schema 字段名、复现语义(纯函数/counter 无关两条硬语义确认);与卡③ 对齐 `depth`=遭遇坐标深度轴+多终点深度互异。可核查结果:对齐纪要(定稿签名对照 §7 接口块,漂移处回写邻卡接口块或记「一致」)。
|
||||
- [ ] 定稿 RNG 纯流面机制(§10 ① 域内):`pure_stream(seed_val, scope)` 具体签名与 scope 分域策略(约束=纯函数/counter 无关/同图/禁自建 RNG)。可核查结果:机制定稿进实现报告(签名+scope 取值)。
|
||||
- [ ] 定稿「支路」度量口径(§10 ② 域内):岔口各出边支路的分解(自出边起至再汇合/终点止的节点子图)与类型构成度量(集合 vs 多重集)。可核查结果:度量口径定稿进实现报告。
|
||||
- [ ] 改前读数基线采集(冒烟/单测通过数,两入口铁规下随 T1 迭代 loop 首跑取值)。可核查结果:基线读数记录在案。
|
||||
- [ ] §6 反哺点①–⑥ 条目起草(一节一改一依据)。可核查结果:反哺点草稿段落在 T4 产物成文前就绪。
|
||||
|
||||
### T1 参数档数据表(schema+.tres+ConfigDB 注册)(≈1–1.5d)
|
||||
|
||||
- [ ] 实装 `game/data/schemas/level_gen_params.gd`:类 `LevelGenParams`(extends Resource),`rows: Array[Dictionary]`(键=level_index),`validate() -> Array`(域校验:五旋钮在域、配比四类合计 1、出口 L1=1/L2L3∈[1,3]、层深 L1≥6、岔口去路数 2/3、战斗保底≥0),`row_by_level_index(level_index: int) -> Dictionary` 单点读。可核查结果:schema 编译通过、validate 对合法行零错误对越域行报错。
|
||||
- [ ] 实装 `game/data/tables/level_gen_params.tres`:L1/L2/L3 三行五旋钮 provisional 值(取值照 §8.1 表逐行填)。可核查结果:表加载零诊断、三行值与 §8.1 provisional 表对上、validate 全绿。
|
||||
- [ ] 实装 `game/core/config_db.gd` 注册:`_tables` 映射加 `"level_gen_params": "level_gen_params.tres"`(对齐 :20 体例)、成员 `var level_gen_params`、装载(对齐 :142)、访问器 `level_gen_params_row(level_index: int) -> Dictionary`(对齐 :245 体例,缺行返回 `{}`)。可核查结果:ConfigDB 装载该表、访问器按 level_index 读回正确行、缺行返回空字典。
|
||||
- [ ] 验收步骤:跑 `godot --headless --path game --script res://tests/unit/data_config/level_gen_params_test.gd`,期望 schema validate+值域断言全绿。
|
||||
- [ ] 变异验证(注入→最窄闸见红→`git checkout` 还原→记录):把 L1 出口改 2(应=1)→ 期望值域断言红;把配比四类改合计 0.5 → 期望合计 1 断言红。记录表进实现报告。
|
||||
|
||||
### T2 生成器核心(产合法图+产出自证+RNG 纯流面确定性)(≈2.5–3.5d)
|
||||
|
||||
- [ ] 实装 `game/core/rng_service.gd` 纯流面 `pure_stream(seed_val: int, scope: int) -> RandomNumberGenerator`:seed 键值纯派生(复用 `_mix64`/`derive_at` 派生模型)、返回 RNG、**不推 `_counter`、不落事件日志**;类头纪律注释补「纯流面=结构生成专用(生成器),counter 无关、同 (seed,scope) 同流」。可核查结果:同 (seed_val, scope) 两次 `pure_stream` 返回同序列;`counter()` 前后不变;不同 seed 序列不同。
|
||||
- [ ] 实装 `game/core/node_map_generator.gd`:类 `NodeMapGenerator`(extends RefCounted,Core 零 UI、RngService 经构造注入〔`--script` 禁裸 autoload,code-conventions:71〕);`generate(params: Dictionary, seed: int) -> Dictionary` 产出 `{nodes, entries, terminals}`(节点字段 id/type/depth/next)。**长图算法(HOW)归实现**(D1:28),只钉产出必须满足 §4 契约 3 全部产出约束+结构铁律+摆放约束三条+契约 2 深度赋值。取数只经 `pure_stream`(契约 1)。可核查结果:对 L1/L2/L3 参数档各以固定种子产图,图非空、字段齐备。
|
||||
- [ ] 实装产出侧自证 `validate_output(graph: Dictionary) -> Array`(生成器内、产前强制):过产出约束五条+结构铁律+摆放约束三条(道路系统/宽度/层高/归属锁定/战斗保底/无环/多入口多终点/每路径恰一入一出/商店位/岔路取舍两条/多终点深度互异)。可核查结果:合法产出自证零错误;构造违规夹具图(环/断头/孤儿/双终点同深/缺店/岔口同类型下一跳)各报对应错误类别(沿 `structural`/`dangling_next`/`dup_key`/`enum`/`domain` 词表)。
|
||||
- [ ] 实装确定性:`generate` 同 (seed, params) 两次逐位同图(深比较 nodes/entries/terminals);counter 无关(不同 `_counter` 初值下同 (seed,params) 同图)。可核查结果:`node_map_generator_test` 可复现断言绿。
|
||||
- [ ] 验收步骤:跑 `godot --headless --path game --script res://tests/unit/core/node_map_generator_test.gd`,期望产出合格+可复现断言全绿。
|
||||
- [ ] 变异验证:生成器取数改 `open_stream`(推计数器)→ 期望 counter 无关断言红;把 depth 赋值改「最短路径」→ 期望「出边目标 depth>源」无环判据对某合流图误判/多终点深度互异断言红;产出自证注释「终点恒 Boss」判据 → 期望非 Boss 终点夹具红。记录表进实现报告。
|
||||
|
||||
### T3 验收断言批总装(生长递增/专项/参数无解 fail-fast)+变异验证(≈2–2.5d)
|
||||
|
||||
- [ ] 实装 `game/tests/unit/core/node_map_generator_test.gd` 专项断言:① 含岔口与出口结构(三关参数档生成图各含 ≥1 岔口节点〔出度≥2,第一关即含、非单链取值〕;L1 出口恒 1、大岔口〔多终点归属锁定〕自 L2 起——L2/L3 exit>1 时图含分区域大岔口);② 岔路取舍·下一跳(任意岔口各去路下一节点类型互不相同);③ 岔路取舍·支路构成(任意岔口各支路类型构成互不相同、每支路至少一个独有类型);④ 必经恰一店(任意入口→终点路径恰过一家店);⑤ 归属锁定(多出口图每终点前一层去路集合恰含一个终点、各区域互不连达);⑥ 多终点深度互异(L2/L3 多终点 depth 两两不同);⑦ 战斗保底(战斗节点数 ≥ battle_floor)。可核查结果:七类断言对 L1/L2/L3 参数档×固定种子扫描面全绿。
|
||||
- [ ] 实装 `game/tests/unit/core/node_map_growth_test.gd` 生长递增断言:同子下 L1/L2/L3 生成图的最长路径步数与节点总数均逐关递增,且节点总数增幅 Δ(L2→L3)>Δ(L1→L2)。可核查结果:三档读数表断言绿。
|
||||
- [ ] 实装参数无解 fail-fast 断言:构造无解参数档(如 layers=1 且 branch_density 强制零岔口而出口要求分区域、或必经店无法满足)→ 期望 `generate` 致命中止(push_error+assert/既有 fatal,返回前不产出残图),无兜底图回退。可核查结果:无解参数夹具触发致命、断言「无残图返回、无兜底图路径」。
|
||||
- [ ] 变异验证(新增/改写断言逐条注入实验,记录表进实现报告):终点前驱改非店 → 期望「必经恰一店」红;把两终点设同 depth → 期望多终点深度互异红;把岔口两去路设同类型 → 期望岔路取舍·下一跳红;把战斗节点砍到低于 battle_floor → 期望战斗保底红;生长断言把 L2/L3 参数调成节点数增幅倒挂 → 期望生长递增红;把 `generate` 无解路径改「返回空图」→ 期望 fail-fast 断言红。逐条执行「注入→最窄闸见红→`git checkout` 精确还原→记录」。
|
||||
- [ ] 验收步骤:T 段出口跑一次全套闸(`tools/run_all_gates.sh` 按改动域选档,本批碰 `game/` 代码与数据表+测试 → 代码域档),期望生成器/参数档断言全绿、探针段无红。
|
||||
- [ ] 件核:全仓 `generate` 调用点不传 level_index(契约 6);生成器内零 `randomize()`/零自建 RandomNumberGenerator/零 `open_stream`(契约 1,`pure_stream` 除外)。
|
||||
|
||||
### T4 产出物落位卡(≈0.5d,末卡——三份 `reports/` 文件写齐才算收口)
|
||||
|
||||
- [ ] 轻闸全绿后、合并前写齐三份:`reports/iter<N>-实现报告-<完成日>.md`(各卡状态+变异验证记录表+实机目检总清单+遗留读数义务)/`reports/iter<N>-代拍板追认单-<完成日>.md`(§10 白名单内代拍项留痕:纯流面 scope 分域、支路度量口径、图内部持有结构等)/`reports/iter<N>-反哺点-<完成日>.md`(§6 六条每条一落点:①生成规格补多终点深度互异+深度赋值句、②地图深度赋值消歧、③覆盖核对登记、④呈现面随动登记、⑤06 状态回写、⑥roadmap 编入笔)。可核查结果:三文件在 `reports/` 且 §6 同步面每条有落点。
|
||||
|
||||
**估时合计 ≈6.5–9d**(T0 0.5–1+T1 1–1.5+T2 2.5–3.5+T3 2–2.5+T4 0.5)。切分预案:T1 可单独一轮(数据层、不碰生成器);T2 为生成器主体(可再切「图数据结构+基本长图」/「约束自证+确定性」两小段,若一轮超 3.5d);T3 依赖 T2 产出(断言驱动生成器);超软盒(>9d)预挂:T2 若约束自证超一孔,先落产出约束五条+结构铁律+确定性主链,摆放约束三条+专项断言次轮补,全绿才收口。
|
||||
|
||||
## 8. 验证闸(逐需求点验收清单)
|
||||
|
||||
| # | 需求点 | 测什么(断言/行为) | 落点/判定手段 |
|
||||
|---|---|---|---|
|
||||
| V1 | 产出合格(产出约束五条+结构铁律) | 任意参数档产出图过:道路系统(除岔口外每节点恰一条去路、岔口去路 2–3)/宽度分叉生长(宽度源于岔口、不摆平行层)/层高(L1≥6)/出口归属锁定/战斗保底;结构铁律(无环、多入口多终点且入口数/终点数=params.entry_count/exit_count、每路径恰一入一出);入口=无入边节点、终点=type=boss 且无出边 | 单测 `node_map_generator_test.gd`(产出自证零错误+入口/终点计数断言+违规夹具各报对应错) |
|
||||
| V2 | 参数档 schema+.tres | 五旋钮在域、配比合计 1、出口 L1=1/L2L3∈[1,3]、层深 L1≥6、岔口 2/3、战斗保底≥0;L1/L2/L3 三行 provisional 值与 §8.1 对上;ConfigDB 单点读回正确 | 单测 `level_gen_params_test.gd`(validate+值域+访问器读回) |
|
||||
| V3 | 可复现(纯函数+counter 无关) | 同(seed, params)两次产图逐位同图;不同 `_counter` 初值同图;同 params 不同 seed 不同图 | 单测 `node_map_generator_test.gd`(深比较+counter 无关) |
|
||||
| V4 | 执行边界(无兜底图/fail-fast) | 参数无解 → 致命中止(push_error/assert),无残图返回、无兜底图/软回退路径;不合格产出不进玩法(自证不过不上图) | 单测(无解参数夹具触发致命)+件核(生成器零回退分支) |
|
||||
| V5 | 岔路取舍两条 | 任意岔口各去路下一节点类型互不相同;各支路节点类型构成互不相同、每支路至少一个独有类型 | 单测 `node_map_generator_test.gd`(岔口扫描) |
|
||||
| V6 | 必经恰一店+归属锁定+多终点深度互异 | 任意入口→终点路径恰过一家店;多出口图每终点前一层去路集合恰含一个终点、各区域互不连达;多终点 depth 两两不同 | 单测 `node_map_generator_test.gd`(DFS 路径枚举+区域可达+depth 比对) |
|
||||
| V7 | 战斗保底 | 任意合法产出战斗节点数 ≥ battle_floor | 单测 `node_map_generator_test.gd`(计数断言) |
|
||||
| V8 | 生长递增 | L1/L2/L3 最长路径步数与节点总数逐关递增;Δ(L2→L3)>Δ(L1→L2) | 单测 `node_map_growth_test.gd`(三档读数对拍) |
|
||||
| V9 | 含岔口与出口结构(第一关即含岔口/L1 出口恒 1/大岔口自 L2 起) | 三关参数档生成图各含 ≥1 岔口节点(出度 ≥2,第一关即含、非单链退化取值);L1 生成图终点恰 1(无多终点归属大岔口);L2/L3 exit>1 时含分区域大岔口(归属锁定) | 单测 `node_map_generator_test.gd`(岔口存在性+出口计数+区域结构) |
|
||||
| V10 | RNG 单点(纯流面) | 生成器取数只经 `pure_stream`;`counter()` 前后不变;零 `randomize()`/零自建 RNG/零 `open_stream` | 件核+单测(counter 不变断言)+静态扫描 |
|
||||
| V11 | 反哺点清单 | §6 六条每条有落点 | 件核对表(T4) |
|
||||
|
||||
### 8.1 参数档 provisional 取值表(provisional 占位,定值批落终值;数值全 provisional)
|
||||
|
||||
> 域校验(V2)对全部行强制:配比四类合计=1;出口 L1 恒 1、L2/L3∈[1,3];层深 L1≥6 且逐关递增增幅变大;岔口去路数恒 (2,3);战斗保底≥0。取值须使 V8 生长递增成立(L1/L2/L3 最长路径步数与节点总数递增、Δ(L2→L3)>Δ(L1→L2));provisional 可在域内微调(§10 ④),禁改已锁域(层深下限、出口 L1=1、岔口 2/3、商店数=出口数)。配比 `shop_ratio` 为名义值——商店实数由摆放约束①钉死=出口数(每终点前一店),生成器以「摆放优先、配比填余、战斗保底兜底」次序兑现。
|
||||
|
||||
| 参数(schema 键) | L1 | L2 | L3 | 域/状态 |
|
||||
|---|---|---|---|---|
|
||||
| `layers`(层深=最长路径步数) | 6 | 9 | 13 | ≥6,逐关递增增幅变大(L1 下限已锁,L2/L3 provisional) |
|
||||
| `branch_density`(分支密度) | 0.30 | 0.50 | 0.70 | [0,1],provisional;逐关递增(决策点密度递增);L1 取值须保证第一关即含 ≥1 岔口 |
|
||||
| `battle_ratio`/`reward_ratio`/`rest_ratio`/`shop_ratio`(配比) | .50/.20/.20/.10 | .50/.20/.15/.15 | .55/.15/.15/.15 | 四类合计 1,战斗保底优先;provisional |
|
||||
| `entry_count`(入口个数) | 1 | 2 | 2 | ≥1,provisional |
|
||||
| `exit_count`(出口个数) | 1 | 2 | 3 | L1 恒 1(已锁);L2/L3∈[1,3](已锁范围),provisional |
|
||||
| `battle_floor`(战斗保底数) | 4 | 6 | 8 | ≥0,provisional |
|
||||
| `fork_ways_min`/`fork_ways_max`(岔口去路数) | 2/3 | 2/3 | 2/3 | 恒 2/3(已锁) |
|
||||
|
||||
**测试方式两分法(2026-10-11 用户定)**:V1–V11 全部机检自动化(单测为主);能力验证可经调试接口直改参数档/种子驱动生成图(沿用现役 DebugOverlay/DS_DEBUG 门控,不走正常资源积累流程)。**用户目检项**:「三关生成图选路观感(多入口/大岔口取舍/多终点走向可见、第一关即有选择)」+「生成图规模随关卡推进变大(生长递增可感知)」两条——观测项写进 `reports/iter<N>-实现报告-<完成日>.md` 实机目检总清单,登记不阻塞(验收时间用户自行安排;呈现依赖卡②/卡④ 落地后目检)。
|
||||
|
||||
**闸口径**:轻闸每任务卡出口自跑;批末重闸=`tools/run_all_gates.sh` 按改动域选档(本批碰 `game/` 代码与数据表+测试 → 代码域档)全绿才合并+改前改后读数对拍。回归措辞:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
|
||||
|
||||
**变异验证伴生**(新增/改写验收函数逐条注入实验,记录表进实现报告):L1 出口改 2(V2 红)/配比合计 0.5(V2 红)/取数改 `open_stream` 推计数器(V3 counter 无关红)/depth 赋值改最短路径(V3/V6 多终点深度互异或无环红)/产出自证注「终点恒 Boss」(V1 红)/终点前驱改非店(V6 红)/两终点同 depth(V6 红)/岔口两去路同类型(V5 红)/战斗砍低于 battle_floor(V7 红)/生长倒挂(V8 红)/无解路径改返回空图(V4 红)。变异不红则改判据或如实标注拦截边界。
|
||||
|
||||
### 8.2 改动—验收对照表(2026-10-21 验收闭合批新规,test-conventions §9.3)
|
||||
|
||||
> 各新增断言 desc 统一尾缀〔fv#〕(本表「验收测试」列可 grep 定位);撤销/变异证明逐条对应 §8 变异验证伴生与 T1–T3 变异步、T4 件核(每张任务卡至少一条,test-conventions §9.4)。
|
||||
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+文件:行+尾缀) | 层级 | 真实入口 | 撤销或变异证明 |
|
||||
|---|---|---|---|---|---|
|
||||
| fv1 产出合格(产出约束五条+结构铁律) | `game/core/node_map_generator.gd`(新建)`validate_output()` 产出侧自证:道路系统/宽度/层高/归属锁定/战斗保底+结构铁律(无环/多入口多终点/每路径恰一入一出) | `node_map_generator_test` 产出合格+违规夹具〔fv1〕 `game/tests/unit/core/node_map_generator_test.gd` | 单元 | `NodeMapGenerator.validate_output(graph)`+`generate(params, seed)` 产前强制 | 变异:产出自证注「终点恒 Boss」→ 非 Boss 终点夹具红(T2) |
|
||||
| fv2 参数档 schema+.tres | `game/data/schemas/level_gen_params.gd`(新建)`validate()`/`row_by_level_index()`;`game/data/tables/level_gen_params.tres`(新建)三行;`game/core/config_db.gd` 注册+访问器 `level_gen_params_row()`(对齐 :20/:142/:245 体例) | `level_gen_params_test` validate+值域+读回〔fv2〕 `game/tests/unit/data_config/level_gen_params_test.gd` | 单元 | `LevelGenParams.validate()`/`ConfigDB.level_gen_params_row()` 直调 | 变异:L1 出口改 2/配比合计 0.5 → 值域、合计 1 断言红(T1) |
|
||||
| fv3 可复现(纯函数+counter 无关) | `node_map_generator.gd` 同(seed, params)逐位同图+counter 无关;`game/core/rng_service.gd` `pure_stream()` 不推 `_counter` | `node_map_generator_test` 可复现(深比较+counter 无关)〔fv3〕 `game/tests/unit/core/node_map_generator_test.gd` | 单元 | `generate(params, seed)` 同种子两次直调 | 变异:取数改 `open_stream` 推计数器 → counter 无关断言红(T2) |
|
||||
| fv4 执行边界(无兜底图/fail-fast) | `node_map_generator.gd` 参数无解致命中止(push_error/assert),无残图、无兜底回退分支 | `node_map_growth_test` 参数无解 fail-fast〔fv4〕 `game/tests/unit/core/node_map_growth_test.gd`+件核(生成器零回退分支) | 单元+件核 | 无解参数档调 `generate(params, seed)`(期望 fatal) | 变异:无解路径改「返回空图」→ fail-fast 断言红(T3) |
|
||||
| fv5 岔路取舍两条 | `node_map_generator.gd` `validate_output()` 岔路取舍判据(下一跳类型互异/支路构成互异) | `node_map_generator_test` 岔路取舍两条〔fv5〕 `game/tests/unit/core/node_map_generator_test.gd` | 单元 | `validate_output()` 岔口扫描直调 | 变异:岔口两去路设同类型 → 下一跳断言红(T3) |
|
||||
| fv6 必经恰一店+归属锁定+多终点深度互异 | `node_map_generator.gd` 商店位/归属锁定/多终点 depth 互异判据(契约 2/5) | `node_map_generator_test` 必经恰一店+归属锁定+多终点深度互异〔fv6〕 `game/tests/unit/core/node_map_generator_test.gd` | 单元 | `validate_output()`+DFS 路径枚举/区域可达/depth 比对 | 变异:终点前驱改非店→必经恰一店红、两终点同 depth→深度互异红(T3);depth 赋值改最短路径→互异或无环红(T2) |
|
||||
| fv7 战斗保底 | `node_map_generator.gd` 战斗保底判据(battle_floor) | `node_map_generator_test` 战斗保底〔fv7〕 `game/tests/unit/core/node_map_generator_test.gd` | 单元 | 产图战斗节点计数断言 | 变异:战斗节点砍低于 battle_floor → 战斗保底红(T3) |
|
||||
| fv8 生长递增 | 三档参数档产图读数(`level_gen_params.tres` provisional 域内取值) | `node_map_growth_test` 生长递增三档读数〔fv8〕 `game/tests/unit/core/node_map_growth_test.gd` | 单元 | L1/L2/L3 `generate` 读数对拍 | 变异:L2/L3 参数节点数增幅倒挂 → 生长递增红(T3) |
|
||||
| fv9 含岔口与出口结构(第一关即含岔口/L1 出口恒 1/大岔口自 L2 起) | `node_map_generator.gd` 道路系统/出口归属产出(契约 3) | `node_map_generator_test` 岔口存在性+出口计数+区域结构〔fv9〕 `game/tests/unit/core/node_map_generator_test.gd` | 单元 | 产图结构扫描(三关参数档×固定种子) | —(缺变异注入实验:§8 变异验证伴生与 T1–T3 变异步均未列,如实登记、实施期补) |
|
||||
| fv10 RNG 单点(纯流面) | `game/core/rng_service.gd`(改)新增 `pure_stream(seed_val, scope)` 纯流面(不推 `_counter`、不落日志),生成器取数唯一源 | `node_map_generator_test` counter 不变断言〔fv10〕 `game/tests/unit/core/node_map_generator_test.gd`+件核/静态扫描(零 `randomize()`/零自建 RNG/零 `open_stream`) | 单元+件核 | `RngService.pure_stream()` 直调+`counter()` 前后读数 | 变异:取数改 `open_stream` 推计数器 → counter 不变断言红(T2,与 fv3 同发注入) |
|
||||
| fv11 反哺点清单 | §6 六条反哺点落 `reports/iter<N>-反哺点-<完成日>.md`(T4) | 件核对表〔fv11〕(§6 六条每条一落点)`reports/iter<N>-反哺点-<完成日>.md` | 件核 | T4 产出物件核(六条落点逐条核对) | 撤销:反哺点缺条 → 件核对表不收口(T4,纯文档产物无代码变异) |
|
||||
|
||||
**独立验收审计(test-conventions §9.7,2026-10-21 起)**:合并前由未参与实现的迭代 loop 内子代理(或用户点名会话)审计——只读本卡、实现报告实际对照表、diff 与测试文件,自跑最窄测试抽查,不采信「已全绿」;审计报告落 `reports/iter<N>-验收审计-<完成日>.md`(逐行「通过/缺漏/例外及依据」)。**审计未通过不得合并**(T4 合并前置)。
|
||||
|
||||
## 9. 异常分级
|
||||
|
||||
- **L1(自救)**:断言措辞、命名、注释、错误类别命名沿用;参数档字段注释与 provisional 标注。
|
||||
- **L2(记录继续)**:RNG 纯流面机制细节(`pure_stream` 签名形态、scope 分域策略);「支路」度量口径(分解边界、集合 vs 多重集);生成图在生成器内的持有结构;长图算法具体步骤(D1:28 归实现);参数档 .tres 行键/字段名细节(§10 域内)。
|
||||
- **L3(停机呈报)**:纯流面机制与「随机只经 RngService 统一流」红线不可调和(若「统一流」被裁定仅指 counter 流、纯流面不算——须用户改契约 1);执行中发现同 depth 多终点图样(推论条款 A 被破,契约 2);任何触碰「生成器只吃参数档不认关卡号」「纯函数复现」「无兜底图/fail-fast」的诉求;生成图深度域超出敌/板表覆盖且数据批无人接手(§0 交付序列失管);产出约束/结构铁律/摆放约束被判据要求放宽(违 D1:41-51)。
|
||||
|
||||
## 10. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. RNG 纯流面机制细节(`pure_stream(seed_val, scope)` 具体签名、`scope` 分域策略=单流多次取数 vs 逐决策 scope、派生式复用 `_mix64`/`derive_at` 的哪种)——约束:纯函数、counter 无关(不推 `_counter`)、同(seed, params)同图、随机只经 RngService、禁自建 RNG/`randomize()`/`open_stream`(契约 1)。
|
||||
2. 「支路」度量口径(岔口各出边支路分解=自出边起至再汇合/终点止;类型构成度量=集合或多重集)——约束:满足「各支路构成互不相同、每支路至少一个独有类型」设计意图(D1:84),可被 V5 断言核查。
|
||||
3. 生成图在生成器内的持有结构、节点句柄载体(int id 的赋值序)、`generate` 返回字典的内部组装——约束:读面字段名 id/type/depth/next 不变(§7 接口块)、句柄唯一、depth=自入口最长路径步数。
|
||||
4. 参数档 .tres 行键/字段名内部细节(键=level_index 的行式 vs 其它)、provisional 取值微调(保持 §8 域与生长递增成立)——约束:五旋钮齐全、域校验过、生成器只读不改值、数值全 provisional(定值批落终值)。
|
||||
5. 长图算法具体步骤(怎么从旋钮长出一张合法图)——约束:产出必过 §4 契约 3 全部+确定性(D1:28「图怎么长出来是实现的事」)。
|
||||
6. 提交切分与 worktree 分支命名。
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门=U0。**
|
||||
|
||||
五处灰色地带已按「设计权威文档+硬纪律+跨卡派发」推导定值(用户可否决,随 T4 反哺点清单呈报可见):
|
||||
|
||||
- **RNG/复现机制(契约 1,卡② defer 给本卡)**:备选「`open_stream` 统一流取数」——淘汰依据=`open_stream` 推全局计数器(`rng_service.gd:86-99`),产图随号位漂移,与「同种子+同参数档=同图」(D1:60)+「断点停点号位重生成同图」(D3:264 存档复现)冲突(resume 在停点号位、非产图号位);备选「自建 RandomNumberGenerator」——淘汰依据=AGENTS.md:73「禁自建 RandomNumberGenerator」红线;**定值=RngService 纯流面** `pure_stream(seed, scope)`(seed 键值、纯函数、不推 counter),生成器取数唯一源。依据=D1:60+D3:264+AGENTS.md:73 合取,counter 无关是「存档复现重生成」的必然要求;**「统一流」口径加固**:RngService 本就含纯派生面 `derive_at(scope)`(`rng_service.gd:34-37`,不推计数器)+号位取号面 `open_stream` 双面,纯流面即 `derive_at` 的流化(同服务内既有分工、非另起第二套流),故「随机只经 RngService 统一流」=经 RngService 服务取数成立(自建 RNG 才是红线违例)。**唯一残余风险**:若用户裁定「统一流」严格专指 counter 取号流、纯流面不算,则契约 1 须改(L3 呈报口,§9 已列)——本卡按「RngService 服务内既有纯派生/取号双面」定值。
|
||||
- **节点深度赋值(契约 2,卡② grilling 注记「depth 赋值一致性归卡①」)**:备选「最短路径(BFS 距离)」——淘汰依据=合流/多入口下出边目标 depth 可 ≤ 源(如目标经更短入口可达),「出边目标 depth 严格大于源」无环判据(卡② 契约 1④)失效;**定值=自入口最长路径步数**(多入口取距任一入口最长)。依据=D1:47「层高=沿路步数(最长路径)」+D2:65「节点深度=自入口步数」+无环判据有效性(最长路径使每边 target>source)三者合取。
|
||||
- **多终点深度互异(契约 2,卡③ 契约 6 推论条款 A 派发本卡兑现)**:备选「同 depth 多终点」——淘汰依据=「定位信息只有两样(关,深)」(D2:119)下同 depth 多终点不可区分,「多终点各配 Boss 行、走哪条路决定打谁」(D1:96)落空;**定值=多终点落在互异 depth**(图侧,本卡产出约束)。依据=D2:119+D1:96+卡③ 契约 6 合取;(深度带互异的带切法归卡③ 契约 5,本卡不读敌表)。
|
||||
- **「支路」度量口径(摆放约束③)**:备选「全子图类型多重集」——淘汰依据=合流支路共享尾段使多重集趋同、判据过弱;**定值=岔口各出边支路=自出边起至再汇合/终点止的节点子图,类型构成按该互斥段度量**(集合或多重集落 §10 ②)。依据=D1:84「各支路构成互不相同、每支路至少一个独有类型」的设计意图(每条路通向不同的东西)+可核查性。
|
||||
- **generate 签名去 level_index(契约 6,对卡② 期望签名的裁剪)**:备选「保留 `generate(level_index, params, seed)`」——淘汰依据=D1:72「生成器只吃参数档、不认关卡号」明文,收 level_index 即违反;**定值=`generate(params: seed)`(去 level_index 形参),调用方 level_index→params 解析后调用**。依据=D1:72 直读;卡② 契约 7「签名以卡①定稿为准」授权本卡裁剪,卡② T0 对齐接线。
|
||||
|
||||
---
|
||||
|
||||
**自审五查记录(2026-10-21)**:① 规格覆盖——§1 四件本体/八项完成定义逐条挂 T 卡(参数档=T1、生成器产出合格+确定性=T2、专项+生长+fail-fast+变异=T3、反哺点=T4;确定性横跨 T2 纯流面+V3 断言),无漏项;② 步骤扫描——全部步骤一步一动作一可核查结果,实现体只给签名+判据名(`generate`/`pure_stream`/`validate_output` 签名、产出约束判据清单、七类专项断言名),长图算法(HOW)明写归实现(D1:28)不抄写、无占位符、无推诿措辞、无本批未定义的类型/函数名;③ 类型一致性——§7 接口块签名与 T 卡/邻卡引用逐字一致(`generate(params: Dictionary, seed: int) -> Dictionary`、返回 `{nodes,entries,terminals}`、节点 `{id,type,depth,next}`、`pure_stream(seed_val: int, scope: int) -> RandomNumberGenerator`、`level_gen_params_row(level_index: int) -> Dictionary`、字段名 layers/branch_density/battle_ratio/reward_ratio/rest_ratio/shop_ratio/entry_count/exit_count/battle_floor/fork_ways_min/fork_ways_max);④ 审查焦点——补挂失败模式:产图失败回退残图(V4 变异)、复现漂移(V3 变异)、多终点同深错兑付(V6 变异)、岔口同类型下一跳(V5 变异)、必经店漏店(V6 变异)、战斗被挤没(V7 变异)、生长倒挂(V8 变异)、取数推计数器(V3 counter 无关变异);⑤ 篇幅比例——规格约 2 屏、任务卡步骤与实现面 1:1,函数体零展开(长图算法不抄写),产出规格/执行边界照录 D1 不增删。
|
||||
|
||||
**grilling 复核记录(2026-10-21,设计树对抗复核,不采访用户——可推导定值写依据、真归用户列 §11)**:按设计树走查八支——① RNG/复现(三案淘汰两案定纯流面,§11 留统一流口径否决口);② 深度赋值(最长路径 vs 最短路径,最短路径破无环判据→定最长路径);③ 多终点深度互异(卡③ 推论条款 A 派发→定互异 depth,深度带归卡③);④ generate 签名(D1:72「不认关卡号」→去 level_index,卡② 授权裁剪);⑤ 支路度量(互斥段口径保设计意图+可核查);⑥ 产出约束五条 vs 结构铁律 vs 摆放约束的分工(五条=合法图定义、铁律=无环/多入多出/每路径恰一入一出、摆放=商店位/岔路取舍——三者产出自证全兜,卡② 加载校验是独立防御不重叠);⑦ 大岔口自 L2 起(D1:70「大岔口自第二关起、第一关出口恒 1」→L1 出口恒 1、多终点归属锁定 L2+,与「第一关即含岔口〔普通选择岔口〕」不矛盾——普通岔口 L1 有、归属大岔口 L1 无);⑧ 覆盖依赖(生成深度域 vs 敌/板表覆盖=真实跨卡缺口,卡③ §5 已登记归口后续表批——本卡写死交付序列依赖 §0,不扩敌数值)。code-conventions 对照:§2.2「位置/句柄/键语义单一」= 契约 2(id/depth 拆分)的规范依据(node_depth 双语义是现役违规态);§71 `--script` 禁裸 autoload=生成器 RngService 注入依据;§4.11 新单测不经 autoload 无冲突。AGENTS.md 硬红线逐条过:数值单点(参数档 .tres 经 ConfigDB、生成器零数值字面量定值)、RNG 单点(纯流面出自 RngService、禁自建 RNG/randomize——「统一流」口径留 §11 否决口)、测试两入口(T 卡只写断言不跑测)、实现批禁写 design/(§6 义务全走反哺点、零旧口径的同步措辞)、fail-fast(参数无解致命无兜底图)、数值全 provisional(定值批落终值)。**本轮对抗复核实改两处**:① 入口/终点数=参数档(D1:51「个数=参数档」)此前无验收断言兜住(入口/出口两旋钮兑现缺口)→ 补进 V1 结构铁律;② 纯流面「统一流」口径加固(援引 RngService 已有纯派生面 `derive_at`〔rng_service.gd:34〕,纯流面=其流化非第二套流,「只经 RngService 统一流」成立)。结论:零待裁点,五处灰色推导已定值并留 §11 供用户否决。
|
||||
@@ -104,6 +104,10 @@
|
||||
关联文档清单:
|
||||
- 设计定稿 design/gdd/systems/冒险循环/关卡节点图生成.md v1.1(2026-10-21)
|
||||
- 设计定稿 design/gdd/systems/冒险循环/关卡节点地图.md v2.1(2026-10-21)
|
||||
- 需求卡片 reports/需求卡片-节点图生成器核心批-2026-10-21.md(2026-10-21)
|
||||
- 需求卡片 reports/需求卡片-节点图消费侧换代批-2026-10-21.md(2026-10-21)
|
||||
- 需求卡片 reports/需求卡片-敌人表深度带换代批-2026-10-21.md(2026-10-21)
|
||||
- 需求卡片 reports/需求卡片-信息迷雾可见性批-2026-10-21.md(2026-10-21)
|
||||
|
||||
**现行实态**(`level.tres` 实查,三关各 5 张模板):**第一关五张模板全是单链排列变体**——7 个节点=战斗 ×4 + 奖励 + 休整 + Boss,战斗深度序单调保难度坡,**变化面只有奖励与休整两位的先后次序**,全链零分叉,玩家在选路上**没有任何选择**;第二/三关是网状图(10 节点,带 `next` 分支,最大层宽 4–5),有分叉,但用户仍判选择量不足。参考方向=Peglin(哥布林弹珠)的节点图移动与选路方式。
|
||||
|
||||
@@ -115,7 +119,7 @@
|
||||
|
||||
**归属**:[关卡节点地图](design/gdd/systems/冒险循环/关卡节点地图.md)(图结构与节点语义,主责)+ [关卡节点图生成](design/gdd/systems/冒险循环/关卡节点图生成.md)(产出规格与生成参数)+ [level-map](design/gdd/ux/level-map.md)(选路呈现)+ [敌人系统](design/gdd/systems/战斗循环/敌人系统.md)(遭遇行集坐标连带);版本标准侧已登记为 Alpha 待做项,见 [08-Alpha版本标准](design/gdd/08-Alpha版本标准.md)「一 · 系统 · 冒险循环」
|
||||
|
||||
**状态**:文档设计已闭合
|
||||
**状态**:文档设计已闭合+已写卡
|
||||
|
||||
#### 扩展节点类型——危险/精英节点与特殊地形节点(2026-10-21 登记,来源=会话提及,设计面=文档轨)
|
||||
|
||||
|
||||
Reference in new issue
Block a user