commit ddd2960fa6ff63d42ffad49ca1b83ff4e15f1e65 Author: DKChan <524912269@qq.com> Date: Mon Sep 14 18:18:49 2026 +0800 chore: initial commit diff --git a/.claude/docs/technical-preferences.md b/.claude/docs/technical-preferences.md new file mode 100644 index 00000000..d66adb47 --- /dev/null +++ b/.claude/docs/technical-preferences.md @@ -0,0 +1,71 @@ +# Technical Preferences + +## Engine & Language + +- **Engine**: Godot 4.7.2-stable(2026-09-07 锁定;4.7 分支最新维护版) +- **Language**: GDScript(唯一项目语言——与 `design/gdd/d2-data-config.md` 实现级规约一致:二补数回绕语义、`--headless --script` 冒烟、autoload/.tres 体系) +- **Renderer**: 2D 项目;Forward+ / gl_compatibility 的选择留实现期 ADR +- **Headless 约束**: Core 层玩法逻辑必须 `godot --headless --script` 可完整运行(D2 Rule 7 工程债) + +## Naming Conventions (GDScript) + +- Classes: PascalCase(`PlayerController`) +- Variables/functions: snake_case(`move_speed`) +- Signals: snake_case 过去式(`health_changed`) +- Files: snake_case 对应类名(`player_controller.gd`) +- Scenes: PascalCase 对应根节点(`PlayerController.tscn`) +- Constants: UPPER_SNAKE_CASE(`MAX_HEALTH`) +- Autoload 单例: PascalCase——**`ConfigDB`、`RngService` 已由 D2 GDD 定名,勿改** + +## Input & Platform + +- **Target Platforms**: PC (Steam) +- **Input Methods**: Keyboard/Mouse(主——挖/旗/移动为鼠标驱动),Gamepad(Partial) +- **Primary Input**: Keyboard/Mouse +- **Gamepad Support**: Partial(Steam 上架建议支持,非 MVP 门禁) +- **Touch Support**: None +- **Platform Notes**: 回合制可全点击驱动,无 hover-only 交互;UI 需兼容纯鼠标完成全部操作 + +## Performance Budgets(2D 回合制默认值,实现期可调) + +- Target FPS: 60 +- Frame Budget: 16.6 ms +- Draw Calls: < 200(棋盘格子 + 数字标签同屏;超出再谈合批) +- Memory: < 500 MB +- Headless 冒烟全套: < 30 s + +## Testing + +- **现行载体**: `godot --headless --script` 冒烟框架——脚本 extends SceneTree 以获得 `quit(code)`;该模式下 autoload 须由脚本显式实例化(D2 GDD 决策,AC 套件执行前提) +- **gdUnit4**: 是否引入并迁移冒烟用例 → Technical Setup 阶段决策(d2-data-config.md OQ1,未决) +- **测试通道**: `load_tables(root, collect_only=true)` + `--data-root=` 夹具注入(仅 debug 构建);致命类用例按独立进程"调用 → 期望退出码"枚举 + +## Forbidden Patterns + +[TO BE CONFIGURED] + +## Allowed Libraries + +[TO BE CONFIGURED——仅在实际集成时登记,不预填] +(gdUnit4 若在 Technical Setup 引入,届时登记于此。) + +## Engine Specialists + +- **Primary**: godot-specialist +- **Language/Code Specialist**: godot-gdscript-specialist(全部 .gd 文件) +- **Shader Specialist**: godot-shader-specialist(.gdshader、VisualShader) +- **UI Specialist**: godot-specialist(无专职 UI specialist) +- **Additional Specialists**: godot-gdextension-specialist(仅 GDExtension/原生插件) +- **Routing Notes**: 架构决策/ADR 校验/跨切面审查走 primary;.gd 代码质量、信号架构、静态类型、GDScript 惯用法走 GDScript 审查;材质/着色器走 shader specialist。 +- **本工具链映射**: 上述 specialist 以 general-purpose 子 agent + `godot-master` 技能承载(无独立 agent 类型注册)。 + +### File Extension Routing + +| 文件类型 | Specialist | +|---|---| +| 游戏代码(.gd) | godot-gdscript-specialist | +| 着色器(.gdshader / VisualShader) | godot-shader-specialist | +| UI(Control / CanvasLayer) | godot-specialist | +| 场景/资源(.tscn / .tres) | godot-specialist | +| 原生扩展(.gdextension / C++) | godot-gdextension-specialist | +| 架构审查 | godot-specialist | diff --git a/.claude/docs/templates/game-design-document.md b/.claude/docs/templates/game-design-document.md new file mode 100644 index 00000000..f072c7d3 --- /dev/null +++ b/.claude/docs/templates/game-design-document.md @@ -0,0 +1,152 @@ + + +# [系统名称] + +> **状态**:In Design · **版本**:v1 · [日期] +> **一句话**:[这个系统做什么,一句话,≤30 字] +> **推演件**:`design/gdd/analysis/[system-name].md`(本文件只收结论,理由在那里) + +## 设计目的 + + + +[一段定位:这个系统为什么存在,游戏少了它会失去什么。] + +1. [玩家视角体验承诺] +2. [体验承诺] +3. [体验承诺] + +## 系统范围 + + + +负责: + +- [能力/数据/规则] + +不负责: + +- [明确不做/划给谁的内容——括注归属系统] + +## 核心循环 + + + +```mermaid +flowchart TD + A["起点"] --> B["..."] +``` + +| 结局 | 触发 | 结果 | +|---|---|---| +| | | | + +## [领域章节](按系统自定义 2–6 节) + + + +### [小节名] + +| 状态 | 含义 | 玩家/系统可做 | +|---|---|---| +| | | | + +规则: + +1. [谁]在[什么情况]下[做什么];[边界:不做 X]。 +2. [规则二]。 + +设计意图:[一行;为什么是这条而不是别的——超过一行去 analysis。] + +## 依赖与输出 + + + +| 方向 | 系统 | 性质 | 接口要点 | +|---|---|---|---| +| 上游 | | | | +| 下游 | | | | + +## P0 最小闭环 + + + +1. [玩家/系统动作] +2. [……] +3. [结果回流到循环起点] + +## P1 / P2 后置 + + + +| 优先级 | 内容 | 后置原因 | +|---|---|---| +| P1 | [内容] | [为什么现在不做——通常是要先验证 P0 的某个前提] | +| P2 | [内容] | [理由] | + +## 调参表(无独立数值的系统可省) + + + +| 字段 | 域 | 默认 | owner | 说明 | +|---|---|---|---|---| +| | | | | | + +## 验收标准 + + + +| 目标 | 验证问题 | +|---|---| +| [P0 闭环成立] | [玩家能否……/当……时系统是否……] | +| | | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1 | [日期] | 初始版本 | + + diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..1e8687b2 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,16 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "python tools/hook_gdd_lint.py", + "timeout": 60 + } + ] + } + ] + } +} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..6650ab1a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,54 @@ +name: Automated Tests + +# 注意:本仓库 2026-09-12 尚未初始化 git——本 workflow 在首次 push 到 GitHub 后自动生效。 +# 载体 = ADR-0004(双载体):逻辑冒烟 + 场景探针;gdUnit4 deferred(不使用 gdUnit4-action)。 +# 外部 wall-clock 超时(timeout 命令)为 ADR-0004 硬性要求——引擎自身无死锁探测。 + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + static-scan: + name: Static scan (ADR-0004 S1–S8) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run static scan + run: bash tests/static_scan.sh + + smoke: + name: Headless smoke (Godot 4.7.2) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Godot 4.7.2 + uses: nikeee/godot-actions/setup-godot@v2 + with: + version: 4.7.2 + + # 逻辑冒烟(run_all.gd 内登记套件 manifest;正式工程落地后生效) + - name: Run smoke runner (external timeout per ADR-0004) + run: timeout 120s godot --headless --script tests/run_all.gd + + # 场景探针(正式工程落地后逐条登记,示例形态): + # - name: Run UI probes + # run: | + # timeout 60s godot --headless --path . res://tests/probes/ui_probe.tscn + # timeout 60s godot --headless --path . res://tests/probes/ui_click_probe.tscn + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: | + logs/ + reports/ + if-no-files-found: ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..28561964 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Python +__pycache__/ +*.py[cod] +*.egg-info/ + +# OS +Thumbs.db +.DS_Store + +# Editor +*.swp +.vscode/ +.idea/ + +# Logs & temp +*.log +*.tmp diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..82c43895 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,46 @@ +# Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike) + +扫雷推理层 + 自动战斗结算的两段式回合 Roguelike。个人开发,MVP 优先管线。 +设计入口(独立版 GDD):`design/gdd/gdd.md` · 概念:`design/gdd/game-concept.md` · 支柱:`design/gdd/game-pillars.md` · 系统索引:`design/gdd/systems-index.md` + +## Technology Stack + +- **Engine**: Godot 4.7.2 +- **Language**: GDScript +- **Build System**: SCons (engine), Godot Export Templates +- **Asset Pipeline**: Godot Import System + custom resource pipeline + +## Engine Version Reference + +@docs/engine-reference/godot/VERSION.md + +## Project Layout + +- `design/gdd/` — 独立版入口 `gdd.md`(6 节讲清游戏:概念/循环/系统清单/规模/美术/不做清单——人类读者与 30 秒 pitch 的入口层,只导航不另立事实)+ 概念、支柱、系统索引、逐系统 GDD(lean 模板:`.claude/docs/templates/game-design-document.md`;2026-09-13 前的旧版全文备份在 `design/gdd/legacy-2026-09-13/`) +- `design/registry/entities.yaml` — 跨系统事实登记表(已锁数值;仅 owner GDD 可修订,冲突须显式提出) +- `design/gdd/reviews/` — design-review 日志(含 delta 复核清单) +- `production/` — stage、session-state、gate-checks +- `prototypes/` — `dungeon-sweeper-vertical-slice/` 垂直切片(概念 demo 已删除,验证报告留档 `docs/prototype-report-minesweeper-battle-concept.md`) + +## Conventions(评审确立,勿违反) + +- **数值单一事实源**:全部玩法数值存 `data/tables/`(.tres),经 `ConfigDB` autoload 类型化单点读取;玩法代码禁止数值字面量与自建 `load()` 表路径(D2 Rule 1/2) +- **RNG 单点**:禁止 `randomize()`/自建 RandomNumberGenerator——随机只经 `RngService` 封闭命名流(D2 Rule 5) +- **Core 层零 UI 依赖**:完整战斗必须可 `godot --headless --script` 运行(D2 Rule 7) +- **fail-fast**:数据校验错误一律致命中止,无软回退(D2 Rule 6) +- **spec-AC 一致性**:规约与验收标准不得在同一输入上给出不同答案——D2 两轮评审确立的阻塞标准,所有 GDD 同尺度 +- GDD 评审纪律:`/design-review` 必须在独立会话跑(与 `/design-system` 隔离) +- **用语纪律(2026-09-09 评审确立,勿违反)**:GDD、评审输出、日志与一切沟通中**禁止自造黑话**——比喻性生造词、只有作者自己懂的缩略语(反例:"非雾腿""双闸")。只能使用(a)人人可懂的直白语言,或(b)游戏行业通用专业术语(如 flood fill、战争迷雾、连锁展开、结算、增益/减伤)。需要指代新概念时,用直白描述性短语(正例:"剔除雾格后的可推理率""两项达标判据");确需给新概念命名时,首次出现处必须附一句白话定义并登记 `design/registry` 术语条目——不得让读者猜测比喻含义。**(2026-09-14 增补)自造词的唯一例外 = 玩家在游戏里也会看到的专属名词**(道具名、界面用语,如"显影尘""探巢摆钟")——文档内部的机制概念不得造词,一律直白描述。 +- **文档形态纪律(2026-09-13 评审确立,勿违反)**:GDD 及一切设计文档遵循 lean 格式——模板 `.claude/docs/templates/game-design-document.md`,门禁 `tools/gdd_lint.py`(新文档与每次修订后必须 PASS)。六条硬规则:① 散文段 ≤240 字,全文表格占比 ≥60%;② 枚举、数值边界、边界情况、依赖一律表格;流程一律字符画(代码块)或出口分发表,禁止散文描述步骤;③ 体验锚点用关键词(支柱 | 关键词 | 机制承诺 表),禁长篇情绪描述;④ 公式**结论先行**——先一行加粗结论,再"推导"小标题放公式代码块、Variables 表与验算表,公式本体必须保留(推理证明不可省);⑤ 修订历史只写文末「版本历史」表——正文禁止 ~~删除线~~、"(作废)"、"(日期+评审/修订/拍板)"式注记;义务台账、拍板记录、registry 回执等审计元数据入 `design/registry` 或附录,不入正文。评审发现写 review log,正文只收干净的现行规则;⑥ **规则两段式:人话先行、举证在后(可读性承重条款)**:核心规则/玩法/系统按两段写——**人话阶段**:作用于哪个系统、在什么情况下、谁触发、触发后发生什么;效果与情况用枚举表列出;**举证阶段**:逐效果给出计算方法/公式(就地给出或指向公式节);纯流程规则明写"无计算"。读者测试:没读过本文档的人能否回答"管的是谁?什么时候?会发生什么?"——答不出就重写。规则名禁止纯名词短语(反例:"获取路由""编排被动性");编号引用(AC-x / F-x / EC-x / 义务×)不进人话,只进举证与依据。**(2026-09-14 用户拍板增补)**:R2 表格占比阈值已由 60% 放宽至 30%(防铺开职责由 R1/R9 承担)——既有 13 份 GDD 按旧口径继续成立,不回改。 +- **两件套纪律(2026-09-14 用户拍板,新系统 GDD 即日生效)**:新系统设计文档写一对——`design/gdd/[system].md` 只装**结论**(设计目的/系统范围[负责·不负责]/核心循环/领域章节×2–6/依赖与输出/P0 最小闭环/P1·P2 后置表/调参表/验收标准/版本历史),**设计推演**(候选取舍、机理、为什么)装 `design/gdd/analysis/[system].md`(每个问题四段:广度分析→深度分析→综合判断;不入 lint 门禁,钩子豁免)。判据:正文出现超过一行的"为什么"即违规——挪 analysis。优先级诚实条款:**后置 ≠ 删除**,复活后置行 = 修订 GDD,禁止静默加回。数值默认后置:正文定规则/域/单位,平衡值进数据表(provisional 必须标注)。既有 13 份 GDD 维持 lean v1 形态不回改;模板 v2 与 analysis 骨架见 `.claude/docs/templates/game-design-document.md`。 +⑦ **树形文档体系与一页纸节点(导航承重条款)**:全部设计文档构成一棵树——根索引 `systems-index` 承载整棵树的形状与每个节点的一句话概要,读者靠索引决定进入哪个节点,索引必须随文档增删同步维护。树内每个节点(GDD 内的每一节、每条规则、每张公式)内容控制在一页纸内(直接内容 ≤80 行,`tools/gdd_lint.py` R9 强制):装不下就拆成子节点,禁止铺开;节点内顺序 = 人话 → 表格枚举 → 字符流程图 → 举证公式。**session-state 条目纪律**:`production/session-state/active.md` 每条进度 ≤3 行(粗体键 + 短句),详情一律外链到对应 GDD/报告/日志,禁止把定稿摘要、义务清单、回写清单整段塞进条目。 + +## 代码规范(写代码前必读,2026-09-11 用户拍板成文) + +**在编写、修改任何代码之前,必须先通读 `docs/code-conventions.md`**——该文档由用户指出的真实陋习与缺陷逐条成文,含结构/契约/测试/语言/数值/流程六类硬性纪律与本项目反面案例对照表。摘要(全文以文档为准): + +- **结构**:禁止上帝对象;功能以类封装;views →(信号)→ 组合根 →(写调用)→ core 依赖单向;核心写调用只在组合根发生 +- **命名**:领域命名,禁止系统 ID 前缀(a1_/b1_/c1_)进入文件名与类名;核心文件头注必须带「实现 GDD」溯源行;禁止事实约定——未成文的约定当场补条文 +- **契约**:信号/事件契约成文,状态机每条迁移路径都必须发事件(缺事件 = 软锁);跨层标识符语义单一(位置 ≠ 句柄);模态覆盖层打开必须隐藏遮挡物 +- **测试**:每个可交互 UI 区域必须配套场景级探针(只有 core AC 不算有测试);缺陷修复必须沉淀回归断言;提交前 smoke + 全部场景探针全绿 +- **数值**:期望值必须标明尺度(每格/每板/每战/每关);调参先测量后复测;单尺度校准必须跨尺度推演;用户拍板外的细节零发挥,列"待拍板"等裁决 diff --git a/design/art/art-bible.md b/design/art/art-bible.md new file mode 100644 index 00000000..e7ce111a --- /dev/null +++ b/design/art/art-bible.md @@ -0,0 +1,311 @@ +# 美术圣经(Art Bible)— Dungeon Sweeper + +> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–4 节定稿**(2026-09-10 初稿;2026-09-13 增补批定稿——徽章栈位/雷格隐喻换轴同步/格子样张验收成文/图标键契约冻结/雾格占位);第 5–9 节(角色/环境/UI/资产标准/参考方向)待后续补全 +> +> **Art Director Sign-Off (AD-ART-BIBLE)**:SKIPPED — Lean mode(2026-09-10;非 PHASE-GATE,第 5–9 节补全或重大改版时建议跑 full 门禁);2026-09-13 增补批 SKIPPED — Solo mode(review-mode.txt = solo,出稿待人工复核) +> 风格基调(2026-09-10 用户定稿):地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身 +> 参考池(用户审阅选定,三者相融):**Into the Breach**(网格信息分层)× **Moonlighter**(明亮像素地牢质感)× **Peglin**(明快全年龄地牢色彩);参考截图存于 `design/art/references/` +> 约束(2026-09-10 用户强调):本圣经是**纯表现层文档**——明亮轻快是美术风格,不是玩法成分。圣经不得引入、暗示或预设任何玩法语义,不改变任何 GDD 已锁机制;凡表现层需要玩法钩子才能表达之处(如完美排雷检测、Boss 预警),一律标注「待 GDD 定夺」,圣经只规定"若有,则如何呈现"。GDD 机制与圣经冲突时,机制语义以 GDD 为准,呈现方案以本圣经为准 + +--- + +## 1. 视觉身份宣言 + +### 1.1 一句话视觉规则 + +**「明快的地牢,轻快的扫荡,仔细的推理。」** + +三拍定调:世界观(明快的地牢)× 玩法动作(轻快的扫荡)× 玩法心智(仔细的推理)。本句为气质宣言,不承担裁决;视觉决策歧义时,依 1.2 三条支撑原则依次裁决(每条附设计测试),原则全部不过的方案否决。本节是美术圣经的最高裁决层,后续章节与本节冲突时,以本节为准。 + +### 1.2 支撑原则 + +**原则一|预告上格**(取自 Into the Breach;锚定 P1 推理的掌控感、P2 规划的延迟兑现) + +继承其格内信息分层与"攻击预告画在目标格上"的范式:一切威胁、伤害与结算预告必须直接绘制在雷图格本体上,禁止只进侧栏或悬浮提示;格内绘制优先级固定:**数字 > 小人标记 > 旗 > 徽章 > 变异纹理 > 底色**,任何元素不得遮挡数字(徽章 = 格面身份小图标——类型/奖励身份预读用,键契约见 §4.6;2026-09-13 依极性提案 §0 栈位声明增补)。与 ITB 之别:只取其信息骨架,弃其冷暗军事色调。 + +*范围注(2026-09-10):本原则约束"已存在的信息往哪里画",不新增信息种类;预告类信息以战斗侧 GDD 实际定义的为准(如 P2 已锁的结算来源标注),GDD 未定义的预告不因本条而存在。* + +*设计测试:当某信息歧义于"画进格子"还是"收进面板"时,画进格子。* + +**原则二|明亮为皮,对比为骨**(取自 Moonlighter、Peglin;锚定 P1 可读性前提、P4 探险氛围) + +取 Moonlighter 的暖质地砖与亲和冒险者气质、Peglin 的明快全年龄洞穴色彩:全局高亮度、暖倾向、无压抑。但明亮不等于低对比——数字大而醒目,与所在格底色的对比度是硬指标,氛围色永远向可读色让路,故未开格底色须比两个参考都更素、更收。与参考之别:它们没有扫雷数字层,我们的对比预算优先归数字。 + +*设计测试:当氛围色与数字可读性冲突时,改氛围色,不改数字。* + +**原则三|一砖一世界**(融合 Moonlighter 地砖观;锚定 P3 风险与收益同源、P4 地牢下探隐喻) + +格子即地牢地砖:未开 = 覆尘完整砖,已开 = 碎裂裸露,雷 = 被惊动的巢穴(2026-09-12 隐喻换轴——雷格叙事属名 = 惩罚格、首发类型 = 怪物巢穴;旧称"已触发的机关"退役),biome = 同款地砖换色板。biome 与变异格的视觉差异一律先做色板变体,不做新资产;一切危险状态必须"形状 + 颜色"双通道冗余,色弱可辨。像 Moonlighter 之处在砖缝质感与暖色道具,但装饰不得伪装成逻辑差异——玩家须一眼分清"好看"与"有雷"。 + +*设计测试:当新 biome 或新变异格需要新视觉时,先换色板加形状,再谈新资产。* + +### 1.3 对 brainstorm 视觉锚的修订记录 + +1. 原锚句「地形是氛围,数字是主角——一切视觉服务'信息可读'而非'场景华丽'」退役:气质职责由新锦句承担;"信息可读"的裁决职责移入支撑原则一、二;"数字"不再作为最高口号,但数字对比硬指标在原则二中完整保留。 +2. 原原则 1 的"暗色地牢底色"假设反转为**明亮底色**(2026-09-10 用户定稿:轻快明亮全年龄幻想冒险,非暗黑风)。 +3. 原原则 2–5 实质不变,去向:绘制优先级并入原则一;变异格双通道、地砖隐喻、换色板优先并入原则三。 +4. **(2026-09-13 增补批)** 绘制优先级栈增补**徽章位**(旗之下、变异纹理之上,依据极性提案 §0 栈位声明);旗的玩家可见动词 = 「控制」(2026-09-12 换轴;对内文档与机制协议仍为"旗/插旗",本圣经行文沿用对内名)。 +5. **(2026-09-13 增补批)** 雷格隐喻换轴同步:原则三与 §3.2 的"机关"旧称退役,换为"被惊动的巢穴";§4.1 兑现档红更名**危险红**(旧称"机关红",色值不变)。巢感样张输入仍按 lore 记录③登记的时点另批执行。 +6. **(2026-09-13 增补批)** 新增 §1.4 格子样张测试验收标准与 §4.6 图标键契约(冻结)。 + +### 1.4 格子样张测试(验收标准,2026-09-13 成文) + +> 对应 game-concept"早期风险登记"第 1 条(扫雷数字可读性 × 暗色氛围的冲突 → 一次格子样张测试定风格)与视觉锚原则 5("具体形态由格子样张测试定稿")。**样张** = 一张按本圣经参数画成的格子风格试验图,是砖面形态的定稿闸——样张通过前,§3.2/§4.1 的一切参数只是候选,通过后冻结为制作标准。 + +**样张内容清单**(一图齐备,缺项即无效):未开格(覆尘完整砖)、已开格(碎裂裸露,含 0 格与 1–8 全档数字)、雷格(被惊动的巢穴)、旗、小人两态(独立占格 + 与数字同格——后者须验"边框高亮不覆盖文本",game-concept 原则 3)、biome 色板变体组(§4.3 四槽换色,至少两套)。雾格不在样张范围(§3.2 雾格资产占位——内容封禁期)。 + +**验收项按序判定,第一项不过 = 整张否决(数字可读性定风格,风格服从可读)**: + +1. **第一验收项|数字可读性(硬指标,可测)**:1–8 全档数字在其格底上 WCAG 对比 ≥4.5:1(§4.1 参数域:格底相对亮度 L≥0.30、HSV 饱和 ≤0.40、数字墨 L≤0.027);0.5× 缩放抽检数字仍可辨;纹带/裂纹/巢穴纹不得侵入数字字形占位区(砖心留白,§3.2)。 +2. **格态三形可辨(灰度通道)**:未开/已开/雷三态转灰度截图后仍可逐格分类(§3.2 完整度叙事);色弱模拟(去色相)下旗、巢穴纹不与裂纹混淆。 +3. **焦点预算成立(§3.4)**:任意一格至多一个完整凸形;数字所在格无第二焦点。 +4. **biome 换皮抽测(§4.3)**:变体组中数字墨三档、旗朱、危险红、奖励金、小人蓝恒定不动;变体格底仍过第 1 项指标。 + +**判定与回退**:第 1 项不过 → 按原则二设计测试回退——改氛围色,不改数字(降纹理密度/提墨色浓度皆可,禁止缩字号换字形逃避);第 2–4 项不过 → 修形不修数。样张通过 = 砖面形态与色板冻结;新增 biome/变异色板须重跑本测试(§4.3)。执行时机:正式砖面资产开工前;D1 GDD 以本节为引用依据(2026-09-07 门禁:样张测试并入 art-bible 执行)。 + +--- + +## 2. 氛围与情绪(Mood & Atmosphere) + +> 本节服从第 1 节宣言「明快的地牢,轻快的扫荡,仔细的推理」。能量值为全作统一 0–10 刻度:0 静谧 → 10 沸腾。 +> +> **明度恒定(硬规则)**:全作所有状态共用同一基准明度,禁止以变亮/变暗作为情绪表达或状态区分手段(roguelike 失败重开,世界无光暗记忆)。状态区分只允许四种手段:**色相、饱和、运动、元素**。 + +### 2.1 挖掘阶段(核心状态,占时最长) + +- **主情绪**:棋手式清明专注——视线始终被数字牵引,画面无一处杂音抢焦;久推不生视觉疲劳 +- **色彩性格**:全作基准明度;暖石色低饱和打底,高饱和只属于数字与旗 +- **氛围形容词**:清爽、井然、可读、耐心 +- **能量**:2/10 +- **承载元素**:悬停格的呼吸微光与准线——画面里唯一在动的,是玩家的思路 + +### 2.2 轮末自动战斗结算 + +- **主情绪**:延迟兑现的清算爽感——扫雷之因逐行兑现为战果;若 B1/B4 落实「完美排雷」类高光兑现(创意风险 #2,待 GDD 定夺),以其为全场喝彩级的最高反馈档 +- **色彩性格**:明度不变,色彩转向金橙主导、饱和拉高;数字色转译为弹道色沿行推进 +- **氛围形容词**:酣畅、递进、一气呵成 +- **能量**:7/10,随连击爬升 +- **承载元素**:兑现光带逐行横扫棋盘,开格化箭雨、旗化暴击砸向敌人,因果全程可见可归因 + +### 2.3 清图奖励与换图 + +- **主情绪**:短促丰收——宝箱揭盖式的喜悦,三五秒即收,不拖节奏 +- **色彩性格**:全屏饱和峰值:金粒喷泉与彩纸迸发,饱和瞬时拉满后回落,明度不变 +- **氛围形容词**:丰收、轻快、干脆 +- **能量**:8/10,尖峰后速降 +- **承载元素**:最后一格碎砖中喷出的金币喷泉,叮当落入 UI 钱袋 + +### 2.4 战斗胜利 + +- **主情绪**:从容自信——「我算对了」的盖章确认;克制而非狂欢,胜利属于头脑 +- **色彩性格**:暖白与金,不用红与爆炸 +- **氛围形容词**:笃定、舒展、体面 +- **能量**:6/10 +- **承载元素**:棋盘中央落下一枚金色蜡封,碎砖自动拼出胜利纹章 + +### 2.5 战斗失败(冒险终结) + +- **主情绪**:温柔的惋惜与「再来一次」的邀请——有分量、不惩罚,像合上一册冒险日志 +- **色彩性格**:明度不变,色相转暖灰掺暮蓝;全作禁血红与黑场,唯此一拍最静 +- **氛围形容词**:惋惜、安静、余温、可再来 +- **能量**:1/10 +- **承载元素**:小人坐在碎砖上摇头掸灰,插过的旗逐面化作光点收回行囊 + +### 2.6 Boss 与精英遭遇 + +- **主情绪**:被注视的郑重感——预告更华丽,但预告上格规则不变,掌控仍在玩家 +- **色彩性格**:格底保持基准,Boss 专属紫赤为全场唯一占用的色相——郑重感来自色相独占,不来自压暗 +- **氛围形容词**:郑重、锋利、仪式感 +- **能量**:5/10,静水深流,结算时才沸腾 +- **承载元素**:Boss 影子先于本体爬过棋盘,扫过处短暂留下紫赤足迹(纯视觉预告;是否绑定机制由战斗侧 GDD 定夺) + +### 2.7 下探过渡(关卡间) + +- **主情绪**:纵深的期待——「下面还有一层」的探险引力,如降落时一瞬失重 +- **色彩性格**:明度恒定,整层色板随 biome 递进换挡,以色相层进预告「更深更险」 +- **氛围形容词**:纵深、期待、换气、进阶 +- **能量**:4/10 +- **承载元素**:镜头沿竖井匀速下移,两侧砖层色板渐变,预演下一层主色 + +### 2.8 菜单与元进度(存档/天赋) + +- **主情绪**:可靠的营地感——成长可见、可点、可规划,归来即蓄势 +- **色彩性格**:全作最暖:烛光黄与木色,中等饱和,无棋盘式高对比 +- **氛围形容词**:温暖、笃实、归属 +- **能量**:3/10 +- **承载元素**:天赋树长在一面砖墙上,每点亮一格便嵌入一块发光新砖 + +### 状态间区分度自检 + +推理与兑现靠「谁在动 + 饱和」区分:挖掘时棋盘静止,唯光标微光在动、高饱和只在数字上;结算时棋盘自体运动,金色光带横扫、饱和扩散至全屏。胜利与失败以**色相**即判(金蜡封对暮蓝收旗),Boss 战以紫赤色相是否登场作标记。 + +(风险呼应:自动战斗过场化 → 2.2 可见兑现与爆发支点;「我算对了」缺高光 → 2.2 爆发支点;全年龄失败表现 → 2.5 有分量不压抑。) + +--- + +## 3. 形状语言(Shape Language) + +> 规则尾注「←」标注推导源:P1–P4=游戏支柱;原一/二/三=1.2 支撑原则;§2.x=第 2 节条目。本节只定画法;凡涉具体变异、状态种类之处一律待 GDD 定夺,本节只规定"若有,则如何画"。 + +**总纲|圆角为世,直线为信**:本作形状二分——一切**物件**用圆角方形(地砖、角色体块、UI 面板共用同一圆角率),一切**逻辑信息**用直线直角(数字框、准线、结算光带)。玩家凭"圆还是方"即分清世界与信息。←气质宣言;原一 + +### 3.1 角色剪影哲学 + +硬规则:**所有角色自带轮廓通道**——亮色描边+落影底盘,识别不依赖所在背景。敌人不占格,剪影不得借格底色成立;同一套剪影因此在棋盘、结算画面与任一战斗舞台通用,不为 B5 未决的舞台问题付重画成本。←已锁事实 + +五类 archetype 以高宽比/轮廓凹凸数/锐角数一眼区分: + +- **小人**:窄、直立、对称,凹凸 ≤2;剪影在 2~3 头身两档下均成立(比例第 5 节定);格内占位最窄,不吞数字位。←原一;§2.1 +- **普通怪**:近圆墩形,宽≥高,零锐角。取 Peglin 的胖体量,弃其色块依赖——先读轮廓,后读颜色。←P4 +- **精英**:普通怪形加恰一处垂直延伸(角/冠/棘),底盘不变。←§2.6 郑重感的低配档 +- **Boss**:敌类中唯一允许锐角与不对称者,体量最大,与紫赤色相独占并列"形状独占"。←§2.6 +- **道具点**:唯一非生物剪影——直线几何体(晶体/箱体),禁有机曲线;风险与收益在剪影层即为两类。←P3 + +### 3.2 环境几何 + +- **地砖**:一切环境体由同一圆角方形衍生:未开=完整凸砖,已开=同形缺角碎裂,雷=碎裂加巢穴纹(被惊动的巢穴——2026-09-12 换轴,旧称"机关纹"退役);只以"完整度"叙事,不为状态新造形。←原三;P4 +- **墙体**:圆角砌块堆叠,禁尖刺装饰——尖刺专属危险语义,装饰不得伪装逻辑差异。←原三 +- **变异格形状通道**(双通道之"形",与色无关):底砖圆角外轮廓不变,砖面内缘绘一圈定宽专属几何纹带(纹带形全作不与他类共用),砖心留白予数字与旗;验收标准=**灰度截图下仍可分类**。纹带对应哪些变异、种类清单,待 A4 GDD 定夺。←原一(纹理位次第 4,让位于数字/小人/旗);原三 +- **雾格资产规格**:内容封禁期暂缓,雾重设计批再启(占位一行,不展开设计)。 + +### 3.3 UI 形状语法 + +**半拟物:容器穿戏服,数据素面。**面板=石板圆角矩形(与地砖同圆角率),按钮=凸圆角的"可按的砖",仪式性确认(胜利等)=圆形蜡封(呼应 §2.4);数字框、准线、进度条一律直角细线、无纹理。判据:圆角=可触摸的物件,直角=须精确的信息。与参考分界:取 ITB 的信息分层,弃其冷硬斜切 HUD;取 Moonlighter 的木石亲和,不做全拟物。←原二;§2.4 + +### 3.4 主角形状与对比预算 + +显著度=完整度:**每格至多一个"完整凸形"**(数字/小人/旗,依 1.2 优先级让位;徽章为附属小图标——§4.6——不构成第二完整凸形,栈位在旗之下),底砖与装饰一律残缺、内凹、低对比而自然后退;危险用刺与缺(排斥形),收益用满与圆(吸引形),同表预算、此消彼长。与参考分界:ITB 以色块对比分配焦点;本作明度恒定(§2 硬规则),故改以"完整度"分配焦点,不靠明暗抢焦。←原二;P1;P3 + +--- + +## 4. 色彩系统(Color System) + +> 尾注「←」同第 3 节:P1–P4=支柱;原一/二/三=1.2 支撑原则;§x=对应节。本节只定用色;凡涉具体变异、状态种类之处一律「待 GDD 定夺」,只规定"若有,则如何用色"。 + +**总纲|彩为世,墨为信**:逻辑信息(数字、UI 数据、物件轮廓)一律用**近黑暖墨族**;世界与物件(砖、旗面、金、氛围)用彩。墨管可读,彩管气质。←原一/原二;§3.3 + +### 4.1 主色板 + +- **掩尘 `#C9AF83`**|未开格底色,覆尘完整砖:全作的"未知",画面最大沉默面。←原二;§2.1 +- **裸土 `#B39A78`**|已开格底色,碎裂裸露:更收更退,靠碎裂形分辨。←原三 +- **数字墨 `#33261B` 族**|1–8 **不做八色分色**,按密度分三档色温:1–3 青墨 `#132E36`、4–5 赭墨 `#3C250C`、6–8 锈墨 `#4A1E14`;**字形为唯一数值载体**,色温只作密度氛围(冷=稀疏,暖=密集)。八色必与红金蓝紫赤语义冲突,故弃。←原一/原二;P3 +- **旗朱 `#C63D22`**|旗色:玩家的宣告"此处有险",预告档的红。←原一;P2 +- **危险红 `#A8321F`**|危险色:雷=被惊动的巢穴,兑现档红,更深更沉(2026-09-13 更名,旧称"机关红"随隐喻换轴退役;色值不变)。←原三;P2 +- **奖励金 `#F0A828`**|已兑现的收益:金币/宝箱/蜡封,全作唯一亮金。←P2;§2.3/2.4 +- **小人蓝 `#2E6E8E`**|小人主色:棋盘唯一"己方",与光标准线同族。←P1;§2.1 + +**对比硬指标(可测)**:一切格底槽(含全部 biome)相对亮度 **L≥0.30**、HSV 饱和 **≤0.40**;一切数字墨 **L≤0.027**(WCAG 相对亮度)⇒ 任意底墨组合 **≥4.5:1 恒成立**(WCAG AA)。实测样例:#33261B / #C9AF83 = **6.9:1**;#4A1E14 / #B39A78 = **5.3:1**。物件轮廓墨对一切格底 ≥3:1;旗面、金色不作识别依赖,由 §3.1 轮廓通道承担。←原二;A4 AC-9 + +### 4.2 语义色彩规则 + +- **红只说危险**:旗朱=预告档,危险红=兑现档,同族两档。禁:红不得用于奖励、装饰与常规 UI。←原三;P2;§2.1 +- **金只说收益与兑现**(奖励、结算、胜利)。禁:金不得作危险障碍色、不上格底、不作日常 UI 强调(防通胀)。←P2;P3;§2.2–2.4 +- **蓝只说己方与冷静信息**(小人、光标、工具信息)。禁:蓝不得表示敌方危险;暮蓝 `#5A6C8C` 为失败独占(§2.5),只作全屏氛围,不作物件色。←P1;§2.5 +- **紫赤只说 Boss 与精英** `#8F2F6A`(色相 323°):郑重来自色相独占,不来自压暗。禁:**HSV 色相 300°–345° 为全场禁入带**,其余资产(含 UI 与粒子)及 biome 色板不得落入。←§2.6;原三 +- **绿只说自然生机,零逻辑语义**(环境植被;恢复类状态若有则用绿——待 B3 定夺)。禁:绿不得承载逻辑状态,不得与红构成唯一区分对。←原三 +- **金/红兼作极性色相槽(§4.6)**:极性(白话:同一次兑付的正负方向——正 = 收益侧,负 = 代价侧;行为×格型→符号矩阵见极性提案 R2)的视觉取色只在这两族间——有利兑付 = 金族,有害兑付 = 红族,不新增语义色。←§4.6;原二 + +### 4.3 biome 色板换皮规则 + +**换皮只换四槽,永不变信**。模板按 biome_tag 键索引,A1 产物 `board_biome` 标签直接消费;清单与词表 owner 待 C1/A4/B4,本节只定模板: + +- `tile_closed` 未开底 / `tile_open` 已开底:L 与饱和同 4.1 硬指标,避禁入色相带 +- `wall_ambient` 墙雾氛围 tint:可带主题色相,L≥0.30 +- `variant_tint` 变异纹带染槽:只改色相不改纹形(对应哪些变异待 A4) + +**不变槽**:数字墨三档、旗朱、危险红、奖励金、小人蓝、紫赤、绿——**信息与语义层全 biome 恒定**,换挡只动色相饱和、不动明度档(§2 硬规则)。新增色板须重跑 §1.4 格子样张(灰度可辨+对比抽测)。←原三/原二;A4 AC-9 + +### 4.4 UI 色板 + +**数据素面的墨来自世界的墨**:UI 数字、进度、准线一律主墨 `#33261B` 系——棋盘与面板读同一种墨;容器穿木石材质色,数据只穿墨,与 §3.3"容器穿戏服、数据素面"在色上闭环。←§3.3;原一 + +**营地与棋盘**:菜单容器背景用全作最暖的烛光黄 `#F6C667` 与木色 `#8A5A34`(§2.8);战斗容器转中性石色,防暖色渗入棋盘分走数字对比预算——氛围色向可读色让路。UI 不新增语义色,一切强调沿用 4.2。←原二;§2.8 + +### 4.5 色弱安全清单 + +依赖色相的语义对逐对点名(冗余通道:形/图标/位置/运动): + +1. 已开 vs 未开:§3.2 完整/碎裂形承载,无需另加 +2. 已开 vs 变异格:纹带形状通道,灰度截图可辨,无需另加 +3. 旗 vs 巢穴(雷格):旗形物件对砖面巢穴纹,无需另加 +4. 奖励金 vs 危险红(色弱下皆偏褐):§3.1 道具点两类剪影 + §3.4 满圆对刺缺 + 金粒运动,无需另加 +5. Boss 紫赤 vs 其他敌:§3.1 形状独占,无需另加 +6. 小人蓝 vs 青墨数字:明度悬殊 + 小人轮廓与底盘,无需另加 +7. **UI 红/金强调对:纹带不管面板——需补图标+文案冗余,且破坏性确认不得与常规确认同位(位置通道)** ←原三 +8. 失败暮蓝 vs 小人蓝:状态互斥,不承载区分,无需冗余 +9. 图标键族(§4.6):图标身份全靠纹样形状通道(一键一纹样,色相不承载身份),未开格预读徽章为墨族单色;极性金/红对由框形满/缺冗余——色相全失效时图标仍可逐一辨认与定极性。←原三;B3 D1×4② + +### 4.6 图标键契约(Icon Key Contract,2026-09-13 冻结) + +> **白话**:图标键 = "哪个数据行配哪枚图标"的固定对照表——效果、奖励、格面类型、敌种各自的数据表主键(effect_id / reward_id / cell_type / enemy_id)直接作图标键,一枚键配一枚专属纹样。**本节冻结的是键与通道的分配,不冻结终稿美术**:纹样造型、具体色值、动画帧归格子样张(§1.4)与 asset-spec 批。先行冻结是为了 D1/B5 开工不必等美术终稿;映射一经冻结**全局恒定、跨 run 禁重掷**(B2 Rule 8③ 与 B4 reward_id 键恒定同款纪律),增键走本节修订,禁运行时换图。 +> +> 承接义务:B3 D1×4②(effect_id→icon 映射键契约)· B4 D1×5①(奖励身份恒定可读)· B2 D1×4①②(敌种图像线索清单成文 + 全局恒定)· 极性提案 §3 D1(类型图标/极性视觉语言)。引用方:D1、B5、D5(教学)、B4 库存呈现。 + +**通道分配(全键族通用,双通道之"形+色")**: + +- **身份 = 形状纹样键**:一键一纹样,全作不复用;**色相不承载身份**(七效果不七色——防色相爆炸,亦防色弱失效)。 +- **极性 = 色相槽 + 框形**:有利兑付 = 金族(§4.2"金只说收益"),有害兑付 = 红族("红只说危险")——零新增语义色;框形冗余:满框对缺框(§3.4"收益用满与圆,危险用刺与缺")。极性判定的数据源 = B1 SettlementTable 效果语义(非 magnitude 符号——B3 D1×4③),本契约只锁视觉通道、不锁数据映射。 +- **徽章 = 附属小图标**:格面徽章(类型/奖励身份)为小尺寸记号,栈位在旗之下、变异纹理之上(§1.2 优先级栈),不得构成格内第二焦点(§3.4)。 +- **未开格预读徽章 = 墨族单色**(§4 总纲"墨为信"):类型徽章不上语义色相,防色相泄漏雷/安属性(极性提案 R3"不呈现极性、不呈现雷/安"的色彩侧延伸);兑付面与库存徽章可用语义族色,终稿归样张批。 + +#### 4.6.1 效果图标键(B3 数据六行 = 2026-09-13 切片数据批实态;B3 GDD 四行→六行入文随其修订批) + +| 键(effect_id) | display_name | 纹样语义锚(形状必须让人联想到) | 极性 | +|---|---|---|---| +| attack_up | 增伤 | 我方攻击增强——向上的力 | 有利 | +| defense_up | 减伤 | 我方承伤降低——护盾/屏障 | 有利 | +| ambush | 伏击 | 敌方暗袭加持——潜伏扑出 | 有害 | +| weaken | 削弱 | 敌方输出被削弱——钝化/蔫软 | 有利 | +| enrage | 暴走 | 敌方狂怒增伤——炸毛/赤目 | 有害 | +| off_guard | 失衡 | 我方失衡承伤增——踉跄/破绽 | 有害 | +| sapped | 乏力 | 我方输出被削弱——蔫软(weaken 的镜像) | 有害 | + +- **sapped = 极性提案 OQ-3 新增负向行**(提案 §2,已拍板待实施)——**键先冻结,行未入 StatusEffectTable 前禁引用**(B3 新数据行随提案实施批,悬空引用按 fail-fast 同款纪律处理)。 +- 呈现位 = HUD 状态槽(B3 D1×4①);堆叠层数呈现归 D1/B5,不入本契约。 + +#### 4.6.2 奖励徽章键(B4 GDD 默认根八行 = 2026-09-11 两级奖励批登记值) + +| 键(reward_id) | category | 徽章语义锚 | 备注 | +|---|---|---|---| +| attack_up_basic | buff(即时) | 增伤增益 | 与 attack_up 效果图标同语义域、键分立 | +| spark_aoe | instant_aoe(即时) | 爆破群伤 | blast 族 | +| heal_minor | instant_heal(即时) | 小额回复 | heal 族 | +| defense_up_basic | buff(即时) | 减伤增益 | defense 族 | +| knife_t1 | damage_item(可选) | 刀(基础档) | 刀形 + 档位记号 | +| knife_adv | damage_item(可选) | 刀(精工档) | 与 knife_t1 同族、档位可辨——库存常读条并排必须互辨 | +| heal_pack | heal_pack(可选) | 治疗包 | 稀缺强治疗(可选治疗唯一形态) | +| clear_knife | damage_item(清图槽) | 清图奖励刀 | **切片批已更替为 clear_blast**(一次性群伤 40,instant_aoe——B4 GDD 修订待批);clear_blast 键并列预留,启用随批 | + +- 即时类四行徽章只在已开格/结算明细呈现(零库存);可选类三件(knife_t1/knife_adv/heal_pack)为库存徽章,互辨性是硬需求。 +- 格面预读徽章按**家族类型**(§4.6.3)呈现、不按行(提案 R1"图标呈现家族类型");可选类格图标 = 家族类型 + 道具徽章语言区分(呈现细则归 D1 义务)。 +- **池归属(safe_cell / active_inject / clear_default)不影响键**——切片供给控制器批把三件可选类移注入池、清图行更替,徽章键照旧(该漂移已在交付报告冲突清单列示)。 + +#### 4.6.3 格面类型图标键(五类——极性提案 R1/§3 D1;只冻结键契约,不设计终稿) + +| 键(cell_type) | 类型 | 纹样语义锚 | +|---|---|---| +| attack | 增伤族 | 攻击/锋锐 | +| defense | 减伤族 | 护盾/壁垒 | +| heal | 回复族 | 修复/生机 | +| blast | 爆破族 | 迸裂/波及 | +| weaken | 削弱族 | 钝化/压制(提案新增正向类型) | + +- **呈现约束(提案 R3,冻结)**:全部未开格呈现类型徽章、不呈现极性、不呈现雷/安——推雷仍是扫雷本征推理;类型分布即先验(提案 R3 拍板),**徽章误读率 = 提案 §4 指标 3**,样张批须做误读抽测。 +- **状态声明**:提案 v3"已拍板待实施"——本契约先行冻结防 D1/B5 设计倒挂;weaken 入池、雷格 roll 等机制随提案实施批(B4 pool_tag/cell_type/polarity 扩列),键集五值封闭、扩键走本节修订。 +- 与 §4.6.1 的关系:同语义域的键(attack↔attack_up 等)可共族设计,键分立、纹样须可区分(格面小记号级对槽内图标级)。 + +#### 4.6.4 敌种线索键(B2 Rule 8 契约化;映射键 = enemy_id) + +| 线索通道 | 冻结内容 | +|---|---| +| 行为拍姿态 | 三行为各一恒定视觉姿态键:attack / charge / heavy;**charge 姿态必须含可辨识的"蓄力"记号**(读怪核心课:"它在蓄力,这轮踩雷不挨打"——charge 轮伏击池蒸发,B2 F2);同一敌种同一行为恒同一姿态(Rule 8①) | +| 环长直觉 | 敌种具备哪些姿态本身就是环长线索:有蓄力拍 = 环里空一拍(可以贪),没有蓄力态 = 每拍都动手(grub 单拍环)——MVP 双敌种双环最长 3 拍(Rule 8②) | +| 强度档位 | 体量通道:§3.1 archetype 体量/垂直延伸("大个子多半有重击") | +| 预告时序 | 呈现"即将执行的一拍",**挖掘期全程可见**,结算不另设独立意图播片(Rule 8⑤——B2 承重项) | +| 恒定义务 | enemy_id 键映射跨 run 恒定、禁重掷(Rule 8③);精确数值只在结算明细兑现(Rule 8④——数字归明细,图像不报数) | + +- MVP 键集 = {grub, ambusher}(B2 Rule 3 定稿;视觉状态清单:grub 1 态攻击 / ambusher 3 态攻击·蓄力·重击)。**资产需求下限三类**(B2 Visual Req 2,呈现归 D1/B5):受击反馈(两敌种可共用一组)、死亡表现(不得直接消失)、顺位标记(D1×4③,同等 blocking)。 +- 精英/Boss 编码位 = B2 OQ7 预登记(C1 定稿强制核对);表现侧现成约束:§2.6 紫赤色相独占 + §3.1 Boss 形状独占;键集扩容随 B2/C3 修订批入本表。 +- 剪影通用性:敌不占格,剪影不得借格底色成立(§3.1)——棋盘/结算/任一战斗舞台同一套。 + +--- diff --git a/design/art/references/catquest3_1.jpg b/design/art/references/catquest3_1.jpg new file mode 100644 index 00000000..797abb8a Binary files /dev/null and b/design/art/references/catquest3_1.jpg differ diff --git a/design/art/references/catquest3_2.jpg b/design/art/references/catquest3_2.jpg new file mode 100644 index 00000000..2ef10aa9 Binary files /dev/null and b/design/art/references/catquest3_2.jpg differ diff --git a/design/art/references/dicey_1.jpg b/design/art/references/dicey_1.jpg new file mode 100644 index 00000000..bb386c97 Binary files /dev/null and b/design/art/references/dicey_1.jpg differ diff --git a/design/art/references/dicey_2.jpg b/design/art/references/dicey_2.jpg new file mode 100644 index 00000000..18622485 Binary files /dev/null and b/design/art/references/dicey_2.jpg differ diff --git a/design/art/references/gungeon_1.jpg b/design/art/references/gungeon_1.jpg new file mode 100644 index 00000000..e2d4cdb7 Binary files /dev/null and b/design/art/references/gungeon_1.jpg differ diff --git a/design/art/references/gungeon_2.jpg b/design/art/references/gungeon_2.jpg new file mode 100644 index 00000000..10f5081d Binary files /dev/null and b/design/art/references/gungeon_2.jpg differ diff --git a/design/art/references/itb_1.jpg b/design/art/references/itb_1.jpg new file mode 100644 index 00000000..dd7aae3c Binary files /dev/null and b/design/art/references/itb_1.jpg differ diff --git a/design/art/references/itb_2.jpg b/design/art/references/itb_2.jpg new file mode 100644 index 00000000..4676bcbe Binary files /dev/null and b/design/art/references/itb_2.jpg differ diff --git a/design/art/references/moonlighter_1.jpg b/design/art/references/moonlighter_1.jpg new file mode 100644 index 00000000..cc392f28 Binary files /dev/null and b/design/art/references/moonlighter_1.jpg differ diff --git a/design/art/references/moonlighter_2.jpg b/design/art/references/moonlighter_2.jpg new file mode 100644 index 00000000..3e70005f Binary files /dev/null and b/design/art/references/moonlighter_2.jpg differ diff --git a/design/art/references/peglin_1.jpg b/design/art/references/peglin_1.jpg new file mode 100644 index 00000000..f8270962 Binary files /dev/null and b/design/art/references/peglin_1.jpg differ diff --git a/design/art/references/peglin_2.jpg b/design/art/references/peglin_2.jpg new file mode 100644 index 00000000..c4d00bfa Binary files /dev/null and b/design/art/references/peglin_2.jpg differ diff --git a/design/art/references/roundguard_1.jpg b/design/art/references/roundguard_1.jpg new file mode 100644 index 00000000..9f7cae28 Binary files /dev/null and b/design/art/references/roundguard_1.jpg differ diff --git a/design/art/references/roundguard_2.jpg b/design/art/references/roundguard_2.jpg new file mode 100644 index 00000000..82713100 Binary files /dev/null and b/design/art/references/roundguard_2.jpg differ diff --git a/design/art/references/tangledeep_1.jpg b/design/art/references/tangledeep_1.jpg new file mode 100644 index 00000000..5baada5e Binary files /dev/null and b/design/art/references/tangledeep_1.jpg differ diff --git a/design/art/references/tangledeep_2.jpg b/design/art/references/tangledeep_2.jpg new file mode 100644 index 00000000..ddf04eb6 Binary files /dev/null and b/design/art/references/tangledeep_2.jpg differ diff --git a/design/art/references/wonderboy_1.jpg b/design/art/references/wonderboy_1.jpg new file mode 100644 index 00000000..6b6d221f Binary files /dev/null and b/design/art/references/wonderboy_1.jpg differ diff --git a/design/art/references/wonderboy_2.jpg b/design/art/references/wonderboy_2.jpg new file mode 100644 index 00000000..778ad119 Binary files /dev/null and b/design/art/references/wonderboy_2.jpg differ diff --git a/design/gdd/a1-board-generation.md b/design/gdd/a1-board-generation.md new file mode 100644 index 00000000..f9a95a98 --- /dev/null +++ b/design/gdd/a1-board-generation.md @@ -0,0 +1,446 @@ +# A1 扫雷棋盘生成(Board Generation) + +> **状态**:In Revision · 待 delta 复核放行 · **版本**:v2 · 2026-09-14 +> **一句话**:从 (level_index, node_depth) 出发确定性生成完整扫雷棋盘,并在生成时刻承诺可推理率门槛。 +> **推演件**:`design/gdd/analysis/a1-board-generation.md`(本文件只收结论,理由在那里) + +## 设计目的 + +扫雷图的唯一生产者:五阶段确定性生成管线,为 A2/B1 提供自洽棋盘与生成期指标,为 P1"每一步都算数"提供生成端保障。缺位则游戏没有难度轴、A4 变异无布置载体、C3 曲线无落点。 + +1. 每张图在生成时刻承诺:从初始安全区出发、不靠赌能挖开的格子占比不低于该行及格线——玩家输给自选的赌(雾区/终局盲猜),不输给不可解的局。 +2. 落点安全:出生 3×3 与其生成期可达域恒无雷、恒无变异。 +3. 难度物质随 (level_index, node_depth) 上行——雷密、尺寸、雾占比由参数行直接雕刻。 +4. 同种子同参数必产同图——回放、测试、复盘精确复现。 +5. 参数组合退化时玩家永不卡死——降级出图并告警暴露,而非死锁。 + +## 系统范围 + +负责: + +- 五阶段生成算法与生成-验证验收算子 +- BoardGenTable schema 与数值回填(首个进度键表) +- 可推理率度量口径(F1)与双判据达标判定(F5) +- 雾参数域/可行性/成本谓词(F2/F2b/F2c)与加载期校验 +- 清图预算增量构成(F3)与批量统计断言口径(F4) +- A4 变异约束的生成期强制执行(A4×15 清单唯一执行落点) +- 八项生成期指标集的产出 + +不负责: + +- 挖掘执行(A2) +- 换图编排与 attempt 边界的编排侧断言(B1——A1 仅被调用) +- 难度曲线数值与调参(C3——仅类型依赖 schema,禁反向调用生成) +- 变异约束声明与候选池谓词出具(A4——A1 只消费 variant_pool_gate 并执行) +- dig_ap_cost 价目(A3——只读引用) +- "够不够"的判:清图总预算可用量与全量校验判据(A1/A3 契约与 C3);挖掘风险不等式与弃图支配性的闭合对账(B1 单一 owner——A1 只供给 actuals 分布锚值,非 target 参数) +- 推理器/布置算法实现选型(实现期 ADR——本 GDD 只冻结行为契约) +- 战斗侧/奖励定价/biome 词表(B1/B2/B3/B4/C1/D1——board_biome 仅为非空 StringSet 数据字段) +- 呈现(D1/A2/B5) + +## 核心循环 + +``` +generate(level_index, node_depth, rng) -> Board + 生成期指标集 + │ ① 参数解析 ── BoardGenTable 行(D2 R1 floor 解析;域/跨字段谓词加载期已校验) + ▼ + ② 布雷 ──────── N = round(mine_density × (W×H − 9)) (先定数,9 = 出生 3×3 恒排除) + │ 在"全格 − 出生 3×3"域经 board_gen 流均匀布雷 + │ → 计算 0 格 flood 闭包及外扩 1 环 → 落在环内的雷移除 + │ 【先布后删:只减不增、不重投、无迭代、确定性终止】 + │ 实际雷数 |M| ≤ N;初始 3×3 及可达域内恒无雷;N ≥ 1 由加载期谓词保证 + ▼ + ③ 变异布置 ──── 先布雷后布雾(雾的 cell_constraint = safe_nonzero 要求雷先就位) + │ 候选池门控 → cell_constraint 生成期强制 → 出生区及可达域排除 + │ → 雾形硬边界(厚度≤2【任意 3×3 窗口非全雾】 / f≤8×mine_density) + │ → 区域参数读行参数(region_count / target_ratio) + ▼ + ④ 生成-验证循环 ─ 布雾完成后测可推理率(F1) + │ 实测 < target → 图内重试(同图种子实例内继续取数,不重开 scope) + │ 上限 max_gen_attempts 次 → 三分支出口(见出口分发表) + ▼ + ⑤ 产出与上报 ── 棋盘 + 八项生成期指标集 +``` + +| 出口 | 触发 | 行为 | 埋点 | +|---|---|---|---| +| 达标即出 | 存在合法尝试满足两项判据(inferability ≥ target ∧ inferability_core ≥ target) | 出最早达标尝试 k*(非全局 argmax) | attempts_used 记录 | +| 接受最优 | attempts 用尽、无达标但有合法尝试 | 出合法集内 inferability 最高者(tie 取最早、零额外取数)+ 降级标记 | 强告警埋点——运行期不死锁、开发期暴露参数问题 | +| 接受最不坏 | attempts 用尽且零合法尝试 | 确定性劣度字典序(不可达安全格数, min_safe 缺口, 序号)最小者出图 + 非法产物标记 | 最高级告警——玩家永不卡死,红线由 F4 断言 A′ 批量守 | + +## 生成管线与随机流纪律 + +规则: + +1. 生成器在每次被调用时按固定五步跑完——读参数、布雷、布雾布变异、自检重试、交货;顺序冻结,不调换、不跳步。 +2. 布雷时先按密度定死雷数 N = round(mine_density × (W×H−9)),再在"全格 − 出生 3×3"域均匀落位;0 格 flood 闭包外扩 1 环内的雷落完后移除——只减不增、不重投、无迭代;实际雷数 |M| ≤ N,漂移量入指标集观测;加载期拒绝 N < 1 的零雷板。 +3. 生成一张图期间的每一次随机取数——布雷、雾形、选变异、重试——都从同一条 board_gen 流按顺序取;一张图只 open_stream 一次、用到底;流之外零随机,randomize() 禁用(静态扫描 AC-18);不重开 scope,attempt 边界 = 生成函数完整返回。 +4. 行参数在生成开始时单次读取快照,生成期内不重读;生成期间表热重载不影响进行中的生成(EC7)。 +5. 推理器/生成器内部异常(crash/不变式违例)一律 fatal 中止 + 告警——实现缺陷 fail-fast,不降级(EC10,与参数性降级区分)。 +6. 同帧两次生成请求顺序串行、各自 open_stream 取号,不引入锁(EC14)。 + +五阶段失败语义: + +| 阶段 | 失败条件 | 语义 | +|---|---|---| +| 参数解析 | 行缺失 / 参数域违例 | 加载期校验已拦(D2);运行期不失败 | +| 布雷 | N = 0 | 运行期不失败(域内 N ≥ 1 恒可布,零雷板加载期已拒) | +| 变异布置 | cell_constraint 无解 | 静默降级:跳过变异、产物合法、shortfall 上报 | +| 验证循环 | 三分支(F5) | 达标即出 / 接受最优 + 强告警 / 接受最不坏 + 最高级告警 + 非法产物标记 | +| 产出 | —— | 棋盘 + 八项指标集 | + +不变式:管线无状态、五阶段顺序冻结、生成期表值单次读取、attempt 边界 = scope 事件边界(重试不产生新取号);B1 编排不得把 attempt 边界当 scope 事件。 + +设计意图:无状态函数 + 单流取号是"同种子同参数必产同图"的根基。 + +## 出生区与可达域 + +规则: + +1. 开局站立的 3×3 出生区保证无雷;从出生区顺 0 格连锁能走到的地方再外扩一圈,构成生成期"可达域"(R₀ = 出生 3×3 ∪ 0 格 flood 闭包,外扩 1 环为保守上界),由生成器按固定定义确定性算出,不依赖挖掘系统运行时行为。 +2. 可达域内永远不放变异;外扩环内可有雷、只禁变异(验收口径 AC-6(a));雾区邻接出生 3×3 合法——约束谓词只管出生 3×3 格集及可达域内无变异格,邻接不构成违反(EC3)。 +3. 挖掘系统实际能展开的范围只会比可达域小、不会比它大——双包含回验登记 A2 GDD:① A2 实际展开域 ⊆ A1 排除域;② A1 求解器初始态 ⊆ A2 实际初始展开域(F1 注记④度量保真对偶;若 A2 定稿展开更小,A1 回退口径 = 出生 3×3 最小态)。 + +设计意图:可达域是"落点安全"与"变异不堵路"的保守几何保证,A4 Rule 7 的执行落点。 + +## 变异布置与雾形边界 + +规则: + +1. 生成器按固定顺序布置变异:先查候选池门控(消费 A4 variant_pool_gate),再把雾格放到允许的位置,最后守雾形硬边界。 +2. 候选池为空(雾未解锁或 biome_tag 全不匹配)时,这张图静默布置为无变异图——空池合法非错误(EC1)。 +3. 雾格只落在"8 邻含雷的非 0 安全格"(cell_constraint = safe_nonzero);出生区与可达域排除在外。 +4. 雾形硬边界:任何 3×3 窗口不许全雾(雾皮厚度 ≤2);雾总量 ≤ 雷数 × 8(f ≤ 8×mine_density);摆几个雾区、每区目标多大读行参数(region_count / target_ratio)。 +5. 布置无解(cell_constraint 连通分量不足)时静默降级:跳过变异、产物为合法无变异图;要求 N 区而实际达成 k Board`(+ 指标集);测试种子经 `derive_at("board_gen", σ)` 纯函数取号;夹具规格、黄金图回归向量协议、out-of-scope deferral 明细见推演件附录(本表沿用原 AC 编号,逐条完整判定口径同原文)。 + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | 干净检出(默认根 = MVP 参考行) | 冒烟加载 Core Rules-3 表清单 | ConfigDB Ready:BoardGenTable 含 (1,0)/(2,0) 锚行(不断言行总数);九字段类型域断言;solver_rules = single_subset 唯一值断言;其余八 knob 字段仅域断言(期望由表读值推导);诊断收集器零 error 零 warning | +| AC-2 | BoardGenTable schema class_name 直引程序构造行实例(每腿先 reset_diagnostics()) | validate() 作用于违例集(f>8d · count≥1∧f=0 · f≥1 · count=0∧f=正值 · f=0.30∧d=0.15 超 0.25 上限 · round 后布雷数=0 · attempts×W×H>16384 · F2b 违例 10×10/d=0.10/min_safe=92 · board_biome=∅)与合法集(count=0∧f=0 · f=8×d 恰界 · min_safe 恰界 · biome 占位词非空) | 违例集各含对应致命类别;合法集零诊断 | +| AC-3 | 夹具根 a1_fog_domain / a1_min_safe / a1_biome_empty | 各以独立 headless 进程跑游戏通道 load_tables(root, collect_only=false) | 三进程均非 0 退出且错误清单完整;runner manifest 登记三项 →≠0 | +| AC-4 | 默认根 L2 行与登记种子 σ₀ | generate(2, 0, rng) 以 derive_at("board_gen", σ₀) 播种流执行两遍;再以 σ₁≠σ₀ 同行生成 | 两遍产物格状态逐格相同 + 八项指标集全等(含 attempts_used);σ₁ 产物与 σ₀ 不全同 | +| AC-5 | set_master_seed(42) 后 RngService 全真路径;夹具根 a1_high_target@2 | 以第 1、2 次 open_stream("board_gen") 实例分别驱动 generate;前后读只读计数器;同帧连续两次 generate(EC14) | 产物与 derive_at("board_gen", 0/1) 播种产物逐格相同;计数器差恰 1(重试不重开取号);同帧计数器差 2、各产物与各自 σ 播种一致(串行无锁) | +| AC-6 | 缩样产物集(默认根 L1·D0/L2·D0 各 N=8;σᵢ = F4 公式缩样版 i∈[0,8)) | 对每板断言 | 全板全腿通过:(a) 出生 3×3 与 R₀ 芯(0 格 flood 闭包,测试侧朴素 BFS 重算)内零雷零变异;外扩 1 环内零变异、环内可有雷(禁断言环内零雷)(b) R₀ 芯内零变异(初始展开不触雾)(c) 雾格 ⊆ cell_constraint 合法集——非雷、非 0、8 邻含雷 (d) 任意 3×3 窗口非全雾 (e) 雾格数 ≤ 8×雷数 (f) \|S\| ≥ min_safe_count 表读值 (g) 非雷格数字 ≡ 8 邻雷数 (h) 全板零非法产物标记 | +| AC-7 | Board class_name 直引程序构造板 | 合法性谓词作用于:雷墙包围孤立安全格板;\|S\|0 搜索种子,协议同 AC-12) | (a) 检查指标集;(b) generate | (a) 指标集含实际达成区数(shortfall)字段且 ≤ fog_region_count 表读值;(b) 降级标记=雾布置降级、shortfall < count 表读值、产物仍过 AC-6 全腿、error 级诊断零新增(静默降级——warning/埋点级不算违静默) | +| AC-16 | 任意冒烟产物(AC-6/10/14/15 批次复用) | 指标完备性检查 + F1/F3 重算 | 八项指标字段完备(实际 inferability 含 metric_version=1 / inferability_core / 实际雾占比 = \|F\|÷(W×H) / 实际雷数与实际 \|S\| 并断言 \|M\| ≤ round(d×(W×H−9)) 表读值 / fog_burden_ratio / Δbudget_fog / attempts_used / 降级标记含非法产物标记+shortfall);inferability_core = \|R∞∖F\|÷max(1, \|S∖F\|) 重算一致;从产物重算 Chain₀/U → Δbudget_fog = \|U\|×dig_ap_cost(A3 表读值)、fog_burden_ratio 以整数对 (\|U\|, max(1, \|S∖Chain₀\|)) 比对;fog_region_count=0 行雾相关四值恒 0 且 core = inferability | +| AC-17 | ConfigDB 加载完成(registry board_gen_table 条目与本 AC 同批回填——条目缺位即失败,非条件豁免) | 脚本解析 design/registry/entities.yaml 的 board_gen_table 条目 | 与 BoardGenTable 九字段定义(字段名/类型/域/knob 标记)逐一相等;值对账仅限非 knob 字段(solver_rules=single_subset);八 knob 字段值不对账(C3 调参豁免) | +| AC-18 | A1 文件集(生成器/度量入口/谓词/schema/冒烟脚本) | 按 D2 AC-9 模式静态扫描 | 玩法代码文件集零 randomize()、零自建 RandomNumberGenerator 构造(tests/ 路径豁免,玩法文件集不豁免);零 ConfigDB 外 data 表加载(schema class_name 直引豁免);零 Control/场景类 import;AC-1 headless 实跑通过合成判定 | +| AC-19 | A1 文件集 | 纳入 D2 AC-14 项目级人工三要素字面量扫描 | 人工三要素口径——无未登记字面量 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v2 | 2026-09-14 | 两件套迁移:按模板 v2 重排为 design(只收结论)+ analysis(`design/gdd/analysis/a1-board-generation.md`,收全部推演)——规则/枚举/公式结论/AC/EC/依赖/义务台账全部保留原编号与原值;推导论证、候选取舍、体验锚点论证、测试口径详表与覆盖矩阵移入 analysis;零规则变更、零数值变更 | +| v1.1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/边界情况/义务台账/覆盖矩阵/AC 转表格;生成管线转字符画 + 出口分发表;F1–F5 公式本体、Variables 与验算数据全保留;语义与 2026-09-09 修订批对齐,零规则变更 | +| 2026-09-09 修订批 | 2026-09-09 | full /design-review 判 MAJOR REVISION NEEDED,8 条 blocking 同日修订:两项达标判据 + inferability_target 收窄 [0.85,1] 绝对锚;AC-6(a) 改写(环内可有雷);雷数取整钉死 + 布雷语义(先布后删)+ N≥1 零雷护栏;F2 ≡ Schema 跨字段校验合流(count=0 ⇒ f=0 腿);AC-11 前提腿(Lm(1)≠∅);断言 B 收紧为达成区占比;第三轴登记约定 + elite_/boss_ 后门;F2c 成本闸 16384 + 禁墙钟。cheap-now 包:σ 打包修复(10^9 + 2^20·L + 2^12·D)、F1 注记③改口(雾格入分子两通道)、\|U\|÷\|F\| 删承诺、monotone_in_depth + 断言 E、EC5 玩家侧语义登记。覆盖矩阵"无洞"自称修正为 5 处标注问题(已修 4 处,差分两项登记待核销)。日志:design/gdd/reviews/a1-board-generation-review-log.md | +| v1 定稿 | 2026-09-08/09 | 一次会话定稿:五阶段管线 + F1–F5 + 三轴分离口径 + 19 条 AC + A4×15 逐项映射;四结构决策(生成-验证循环 / 接受最优 + 零合法接受最不坏 / 推理域 single_subset 冻结 / 密度浮点)与 F4 严格零红线为用户拍板;lean 门禁 CD-GDD-ALIGN 跳过,生产前人工复核体验锚点情绪定位 | diff --git a/design/gdd/a2-minesweeper-rules.md b/design/gdd/a2-minesweeper-rules.md new file mode 100644 index 00000000..a8e50a85 --- /dev/null +++ b/design/gdd/a2-minesweeper-rules.md @@ -0,0 +1,504 @@ +# A2 排雷玩法规则(Minesweeper Rules) +> **状态**:Approved · **版本**:v2.0 · 2026-09-14 +> **一句话**:持有 A1 棋盘、执行全部棋盘面规则,把事实以恰好三种事件外发——全项目事件源头。 +> **推演件**:`design/gdd/analysis/a2-minesweeper-rules.md`(本文件只收结论,理由在那里) +> **承载支柱**:P1 主 · P4 · P3 · P2(机制承诺论证见推演件) +> **层级**:Layer 2 Core · Milestone MVP · 设计顺序第 6 位(上游 A1/A3/A4/D2 均 Approved) + +## 设计目的 +A2 是扫雷层的玩法规则引擎与全项目事件源头:接收玩家动作意图(挖/插旗/拔旗/移动/停手),执行全部棋盘面规则,把发生的事实以恰好三种事件外发;没有 A2,A1 的板、A3 的预算、B1 的结算都失去发生地。 + +1. P1"我算对了":数字恒真透传(雾格只是不显示,数据层永不说谎)、连锁传播与生成端度量同构、每个拒绝携带可辨原因——系统回应的每一格都与玩家推演一致。 +2. P4"走过的伤疤":小人从出生安全区向雷图边缘探索,点击已探明区瞬移穿行、在未开边界挖掘,已引爆雷格可通行——雷不是探索终点,是走过的伤疤。 +3. P3"赌与算都可见":挖开雷格是事实上报而非死亡,旗是零信息收益的纯粹承诺(雾内旗恒不中也被诚实判定)——A2 不裁决得失,只保证事实流真实。 +4. P2"结算可归因":结算明细每行归因(安全格增益/标中旗削弱/雷→伏击/清图奖励)都溯源到 A2 事件流——reward_eligible / channel / hit 是归因的字段落点。 + +反向护栏:任何"规则层说谎"形态——数字失真、连锁静默变异分支、拒绝原因含混——默认砍掉;呈现层的隐藏(雾格无字)必须以数据层恒真为前提。 + +## 系统范围 +负责: + +- 全部棋盘面规则:小人控制 · 8 向相邻判定 · 连锁展开 flood fill · 数字提示 · 旗子棋盘状态 · 清图判定 +- 三事件发射面(cell_revealed / flag_judged / round_end 棋盘路径)与"先旗后结束"事件序契约 +- 批次结算入口 settle_batch 与语义序仲裁;install 四条结构不变式校验与初始展开执行 +- F1–F4 四条行为契约(清图谓词 / 连锁闭包 / 初始展开域 / 批次仲裁) + +不负责: + +- 资源账本(A3——判棋盘面后调 try_spend,禁自行扣减)· 轮次编排(B1——轮次节拍权威在 A3/B1,A2 以 A3 相位观测量为闸、不自持轮次状态) +- 奖励内容(B4)· 棋盘生成(A1)· 变异规则(A4——只透传生成期快照标记)· 呈现(D1) +- 数值字面量 / 表读取 / RNG / UI 与输入设备依赖(规则 1 四零禁令) + +## 核心循环 +装板与挖掘期事件发射主链: + +``` +install_board(board) 受理域 = Idle/Waiting + Active·仅弃图刷新路径 + │ ① 持有产物快照(生成期变异快照,非活引用——抗表重载) + │ ② 校验结构不变式 ①–④ ── 违例 → 拒绝 + board_invalid + error 诊断(fail-fast) + │ ③ 初始展开 O₀ = R₀ → cell_revealed ×|R₀|,channel=initial,规范序 + │ ④ 小人落位 B3 中心格 + ▼ +Installed·Active ── 挖掘期:玩家意图经 D1 → settle_batch(intents)(A2 唯一结算入口) + │ dig 棋盘面判定 → A3 try_spend → 开格 + 连锁 → cell_revealed ×(1+chain_size) + │ flag try_spend → 棋盘旗标记(插/拔零事件外发,反馈经调用返回值命令式驱动) + │ move 免费瞬移(不经账本,零事件;compute_move_path 供 D1 路径预览) + │ stop 先发 flag_judged ×N → 委托 A3.request_stop() + ▼ +轮次结束事务(A3 仲裁:cleared > stopped > ap_exhausted——首次为真处定胜者,余意图 voided) + │ flag_judged ×N(判定基线 = 事务开始旗位,行主序;先于 round_end——事件序契约) + │ 棋盘路径:A2 读 last_round_end 恰一次 → round_end{ap_exhausted/cleared} 外发 + │ stopped 路径:A3 直发 round_end{stopped}(双发射源——A2 禁双发) + ▼ +Installed·Frozen ── Waiting:全态冻结;编排边界受理 install_board(换新图)/ retire_board +``` + +| 出口 | 触发 | 动作 | RAP | +|---|---|---|---| +| cleared | dig 胜 ∧ 结算时点 clears_map=true | A2 外发 `(cleared, new_board)` 恰一条(棋盘路径) | +0(剩余 AP 清零不入 RAP——A3 Rule 5) | +| stopped | C_stop 胜 | 先发 flag_judged ×N → 委托 A3.request_stop() → round_end`(stopped, same_board)` 由 A3 直发 | += remaining_ap(1:1) | +| ap_exhausted | 耗尽 AP 的 C_other | A2 外发 `(ap_exhausted, same_board)` 恰一条 | +0(不变式 remaining=0) | +| 继续 | 不结束轮次 | 无 round_end,继续下一意图 | — | + +## 事件面与枚举 +### 术语表(白话首定义集中于此;已登记 registry 术语条目) +| 术语 | 白话定义 | +|---|---| +| 双包含回验 | A1 与 A2 互检两条包含关系——臂① A2 实际初始展开域 ⊆ A1 生成期排除域(开局不触雷不触变异);臂② A1 求解器初始可见态 ⊆ A2 实际初始展开域(A1 度量不吃亏)。恰等 = 两集合逐格相等 | +| 零特判 | A2 代码不为变异写任何 if 分支——变异只是随格携带的只读标记透传,行为差异全部由生成端约束保证 | +| 规范序 | 事件发射的固定顺序(触发格 BFS、8 邻行主序)——同输入必得同顺序,禁实现自选遍历序 | +| 语义序仲裁 | 批次内不按玩家提交顺序、而按"清图挖 > 停手 > 其余"的固定类别顺序结算 | +| 双发射源 | round_end 有两个合法发射者——棋盘路径(ap_exhausted/cleared)由 A2 发、停手路径(stopped)由 A3 直发,payload 同构 | +| 三源拒绝 | 拒绝原因来自三个互异来源——棋盘面(board_face)/ 资源面(resource_face,AP 不足)/ 相位(phase,非 Active) | +| 单意图批 / 便捷形 | settle_batch 只装一个意图时 ≡ 直接调 try_dig 等便捷 API,行为逐项相同 | +| voided | 轮次结束后未获执行的批次意图——零棋盘效果、零扣费、零事件,区别于非法拒绝 | + +### 发言面规则 +规则: + +1. (规则 1)A2 引擎吃两样输入——A1 产出的棋盘、玩家提交的意图序列,吐一样输出——三种事件组成的事实流;同棋盘同意图序列必得逐位相同的事件流,为此守四零禁令:零随机(不掷骰子、不自建随机源)、零数值字面量(价格读账本、板面读生成产物、变异随格快照携带)、零表读取(参数全经上游)、零界面与输入设备依赖(唯一通道是意图对象,`--headless --script` 完整可驱动)。 +2. (规则 2)A2 对外只报三种事实——每挖开一格报一次 cell_revealed,轮次结束事务开始时对每面在场旗报一条 flag_judged(判中/判空),轮次结束报一次 round_end(停手路径由 A3 直发,A2 禁双发);多一个事件、少一个事件都是接口漂移;同一次收轮先把旗判定全部报完再报轮次结束,所有收轮路径统一(停手路径一样:先报旗判定、再委托收轮,停手不改旗位)。 + +举证:确定性、零字面量与 headless 可驱动依据 D2 Rule 5/1/7;静态扫描 AC-22、回放断言 AC-19/AC-20;payload 验收 AC-4/6/7/8/9,事件序契约 AC-9/AC-10;下游结算触发不依赖事件到达序(B1 强制项①)。 + +### 三事件回边(发射面封闭——增删事件 = 接口漂移修订) +| 事件 | 字段 | 发射点 | 语义要点 | +|---|---|---|---| +| cell_revealed | cell · kind ∈ {safe, mine} · digit · variant_ids · channel ∈ {initial, chain, dig} · reward_eligible | 开格事实 | digit 恒真透传(仅 kind=safe 携带;雾格携带真实数字、呈现隐藏归 D1、判定数据源 = variant_ids);reward_eligible:initial 恒 false / chain·dig ∧ safe 恒 true(含链开雾格)/ kind=mine 恒 false;发射序 = 规范序(初始/连锁 = 触发格 BFS、8 邻行主序固定序) | +| flag_judged | cell · hit | 轮末统一判定批 | 每轮次结束事务开始时对判定基线旗位逐旗一条(基线 = 轮次结束事务开始时的旗位;枚举序 = 行主序扫描,确定性) | +| round_end | path_tag ∈ {ap_exhausted, cleared, stopped} · continuation_hint ∈ {same_board, new_board} | 棋盘路径由 A2 外发 | 核心字段逐字段取自 A3 `last_round_end`(读取权为接口契约),与 A3 stopped 路径 payload 同构;stopped 路径由 A3 直发、A2 禁双发 | + +### 字段值封闭枚举 +| 字段 | 值域 | 语义 | +|---|---|---| +| kind | safe · mine | 开格种类;kind=mine 事件无 digit 字段 | +| channel | initial · chain · dig | initial=初始展开(恒无奖励)· chain=连锁 · dig=单格 | +| reward_eligible | bool | initial 恒 false;chain/dig ∧ kind=safe 恒 true(含链开雾格);kind=mine 恒 false | +| path_tag | ap_exhausted · cleared · stopped | 棋盘路径仅前两者由 A2 外发;stopped 由 A3 直发(双发射源) | +| continuation_hint | same_board · new_board | cleared → new_board;其余 same_board | +| hit | bool | [cell ∈ M];被拔的旗不判;雾内旗恒 false(零特判) | +| intent | dig · flag_place · flag_pull · move · stop | stop 执行 = 批事务内委托 A3.request_stop | +| reject_reason | board_face · resource_face · phase · board_invalid | 三源拒绝 + 装板违例 | + +### 三源拒绝与 board_face 拒绝目录 +| 源 | 判定 | 承载 | +|---|---|---| +| board_face | 挖:目标非 8 向相邻 / 目标已开 / 目标有旗(须先拔旗)· 插旗:非相邻 / 已开 / 已有旗 · 拔旗:非相邻 / 无旗 · 移动:目标未开 / 无 BFS 路径;目录外输入(越界坐标等)= out_of_catalog | sub_reason 封闭枚举(见下表) | +| resource_face | A3 try_spend 返回 false(AP 不足) | reject_reason=resource_face | +| phase | A3 非 Active 或无板态 | reject_reason=phase | + +### sub_reason(仅 board_face 携带,封闭枚举——"须先拔旗"与"非相邻"玩家语义截然不同,必须数据可分;批后修改 = 接口漂移) +| 值 | 语义 | 适用动作 | +|---|---|---| +| not_adjacent | 目标非 8 向相邻 | 挖 / 插旗 / 拔旗 | +| target_revealed | 目标已开 | 挖 / 插旗 | +| target_concealed | 目标未开 | 移动 | +| flag_present | 目标已有旗(挖 = 须先拔旗;插 = 已有旗) | 挖 / 插旗 | +| flag_absent | 目标无旗 | 拔旗 | +| no_path | 经已开区无 BFS 路径 | 移动 | +| out_of_catalog | 目录外输入(越界坐标等)——debug 级诊断,不与玩家语义混桶、禁静默吞 | 全部 | + +### 装板违例诊断类别(board_invalid 携带,封闭枚举) +| 值 | 对应不变式 | +|---|---| +| inv1_mine_in_spawn | ① B3 内无雷 | +| inv2a_variant_in_spawn | ② B3 内无变异格 | +| inv2b_variant_in_r0 | ② R₀ 内无变异格("R₀ 触雾"归此——②的 R₀ 子情形) | +| inv3_digit_invariant | ③ 数字恒真 | +| inv4_r0_equals_s | ④ R₀ ≠ S | + +### 对外 API 面(provisional,封闭枚举——新增入口 = 接口漂移) +| API | 签名 | 语义 | +|---|---|---| +| install_board | `install_board(board)` | 装板 + 校验四不变式 + 初始展开 | +| settle_batch | `settle_batch(intents)` | A2 唯一结算入口(D1 唯一提交入口;单意图批 = 平凡形) | +| try_dig / try_flag_place / try_flag_pull / try_move | `try_*(cell)` | 便捷形 ≡ 对应单意图批,行为逐项相同 | +| compute_move_path | `compute_move_path(cell)` | 只读——BFS 路径(8 邻行主序);不可达格返回空 | +| retire_board | `retire_board()` | 板实例与变异快照消亡(NoBoard 下幂等受理) | +| ActionOutcome | `{accepted, outcome, reject_reason, sub_reason}` | outcome ∈ {executed, voided, rejected};sub_reason 仅 board_face 携带 | + +## 棋盘状态·装板与初始展开 +### 板态 +| 板态 | 进入条件 | 行为/出口 | +|---|---|---| +| NoBoard | 冷启动 / `retire_board()` 后 | 全部动作 API 拒绝(phase 源);`install_board` 受理(Idle/Waiting);retire_board 幂等受理 | +| Installed·Active | 装板完成 ∧ A3 phase=Active | 接受全部动作意图;dig/旗经 `try_spend`,移动免费直达;弃图刷新 = retire + install(B1 `refresh_board` 编排,装板后回本板态);轮次结束事务 → Installed·Frozen | +| Installed·Frozen | A3 phase=Waiting(三路径任一收轮) | 全部动作意图拒绝(phase 源);棋盘/旗/小人全态冻结;`install_board`(换新图)/`retire_board` 受理(编排边界) | +| Retired | `retire_board()` 或战斗终结后编排调用 | 旗/小人/事件分段终结(事件捕获以 install 为段界)——板实例与变异快照一同消亡 | + +### 输入 × 相位 × 板态矩阵(动作意图 ∈ {dig, flag_place, flag_pull, move, stop};编排调用 ∈ {install_board, retire_board}) +| 输入 | NoBoard | Installed·Active | Installed·Frozen | +|---|---|---|---| +| 动作意图(含 stop) | 拒绝(phase) | 受理(经批次结算器) | 拒绝(phase;A3 侧按同一致相位矩阵同判——两侧判定一致) | +| install_board | 受理(Idle/Waiting) | 受理(仅弃图刷新路径——B1 `refresh_board` 编排;非刷新路径仍拒绝——防轮内换板) | 受理(换新图 = 全态重置 + 初始展开重放) | +| retire_board | 幂等受理 | 拒绝(板态) | 受理 | + +### 装板与初始展开规则 +规则: + +1. (规则 3)install_board 在 Idle/Waiting 相位受理,挖掘进行中只有"弃图刷新"这一条编排路径例外(以账本扣费成功为前提),其余在局内装板一律拒绝;受理后 A2 持有生成期变异快照(非数据表活引用,热重载改表不影响行为),当场验四条结构不变式,任一不过即拒绝装板并给出 board_invalid 错误级诊断,绝不带病运行;验过才执行初始展开、把小人放到出生 3×3 中心格。 +2. (规则 4)装板成功的那一次事务里,A2 免费翻开出生 3×3 加上与之连通的空地闭包(每次装板执行一次、跨轮不重放);这片初始展开域与 A1 生成器求解时的初始视野逐格相等——不多开不少开、不外扩一圈"安全边距";翻开守四条铁律:免费(不扣行动点、账本零写入)、计入清图进度(剩余安全格从这片开完后起算)、无奖励(这些格不发奖励、下游不得入库)、永不触雷触雾(出生区无雷 + 闭包只走安全格,在板上即可独立证明)。 + +举证:四条不变式与诊断类别见下表,受理域见上矩阵;"无奖励格不得入库"下游义务见 B1 强制项②;初始展开定义落点承接 A3 EC5 委派;恰等与双包含证明见 F3 与推演件;验收 AC-1/AC-1b/AC-2/AC-3/AC-16/AC-17。 + +### install 四不变式(violation = 拒绝 + `board_invalid` + error 级诊断,fail-fast 不静默;AC-1b/AC-2 按枚举名断言) +| # | 不变式 | 违例诊断(封闭枚举) | +|---|---|---| +| ① | B3 内无雷 | inv1_mine_in_spawn | +| ② | B3 ∪ R₀ 内无变异格(A4 Rule 7 消费侧兜底;"R₀ 触雾"归 R₀ 子情形) | inv2a_variant_in_spawn / inv2b_variant_in_r0 | +| ③ | 数字恒真——num(x) ≡ 8 邻雷数(A2 全部规则的地基) | inv3_digit_invariant | +| ④ | R₀ ≠ S——清图退化板拒绝(防"初始展开即整图"使轮次棋盘路径出口结构性不可达;生产面由 A1 legality 闸保证,本条为纵深防御、非 A1 clearable 闸复验——不可达安全格板 A2 照常运行) | inv4_r0_equals_s | + +补充不变式:板态仅上述四态;变体属性是格级只读快照(无独立运行期状态机分支于变异/雾——零特判);产物只能经程序构造或 A1 产生,运行期不存在该输入源;A2 以 A3 相位观测量为闸、不自持轮次状态。 + +## 动作规则 +符号约定:U / M / S = 全格集 / 雷集 M ⊆ U / 安全格全集 S = U ∖ M;F / G / O = 雾格集 F ⊆ S / 旗集 G / 已开集 O ⊆ U;B3 = 出生 3×3;num(x) = x 的真实数字(8 邻雷数,数字恒真)∈ {0..8};N₈(x) = x 的 8 邻。 + +规则: + +1. (规则 5)挖/插旗/拔旗三个付费动作,A2 先查棋盘面(目标相不相邻、开没开过、旗位对不对),查过再请资源账本扣行动点;任一边说不行,整个动作被拒绝——棋盘不变、不扣费、不发事件;每次拒绝携带三源之一的可辨原因码(棋盘面/资源面/相位,玩家面文案归呈现层);A2 自己绝不直接扣资源;挖成之后连锁规模与清图事实(chain_size, clears_map)由 A2 上报账本。 +2. (规则 6)挖开数字为 0 的格时,它周围 8 向相邻的未插旗安全格免费连锁全开,一路递推到没有新的 0 格为止;连锁格只花首格挖掘价、照常计入清图进度、奖励资格照常;旗格不入链也不当传播源——旗是玩家可用的"链阻断器"(花 1 AP 插旗保护一格不被误链);雾格可被链开但永不传播(生成端保证雾格数字恒非 0),代码里没有为变异写的分支。 +3. (规则 7)点击任何已开格,小人经已开区一次瞬移到位——逐格走的动画是呈现层的事,规则层一次事务完成落位;合法条件两条:目标格已开、且能只经过已开格 8 向连通走过去("已探明区"含已引爆雷格——走过的雷可以穿行站立);移动免费、不经账本、不发事件;只读查询 compute_move_path 返回同一条寻路路径(固定 8 邻行主序,确定性),既是合法性判定载体、也是呈现层路径预览的数据源。 +4. (规则 8)旗是棋盘状态的一部分:插上的旗跨轮保留、随图存续,换图/刷新/战斗结束随之消失;没有"口袋旗"库存、数量无上限、只能插在未开格上;轮次结束时 A2 对当时在场的每面旗报一条判定——旗下是雷判中、不是雷判空,雾里的旗恒判空(雾区无雷,判定零特判);旗留在雷上就每轮诚实重复报判中,去重是结算侧的事,A2 只报事实;插拔旗本身不发事件,界面反馈走调用返回值。 +5. (规则 11)挖开雷格报一条 kind=mine 开格事件,轮次不结束、格子算已开——从此可以通行:小人能站上去、能穿过它继续挖;雷格不计清图进度、不能再挖、不能再插旗;同一轮可连续挖开多颗雷,每颗都是独立事实;雷格通行是有价捷径:挖已知雷 = 花 1 AP + 给敌方送优势,是玩家主动抄近路的权衡与降级板上的兜底通路,不是免费通道。 +6. (规则 12)本作没有双击快开/数字和弦:点击已开数字格的唯一含义就是移动小人;对外接口是封闭枚举、里面没有快开入口,将来谁想加就是接口漂移修订。 + +举证: + +- 规则 5 依据 A3 Rule 9(禁自行扣减),验收 AC-4/AC-11/AC-21;规则 6 算子见 F2,遇旗逐格行为见 EC17,验收 AC-5/AC-6;规则 7 边界见 EC14,呈现归属归 D1,验收 AC-14。 +- 规则 8 价目见下表,判定字段见三事件回边,兑付去重归 A3 Rule 8/OQ5,挪旗价目见 A3 EC3/EC7,验收 AC-11/12/13;规则 11 验收 AC-15、边界 EC10;规则 12 负空间验收 = API 封闭枚举"无对应入口",chord 首遇提示归 D1 强制项③,行为验收 AC-4。 + +| 旗动作 | 花费 | 发生什么 | +|---|---|---| +| 插旗 | 1 AP | 未开格上立一面旗 | +| 拔旗 | 1 AP | 旗从棋盘移除 | +| 挪旗 | 2 AP(拔 + 插) | 旗换个位置 | +| 挖有旗的格 | 拒绝 | 须先拔旗(1 AP)再挖 | + +## 批次结算与收轮 +``` +settle_batch(intents) ── 单一结算事务(到达序 ⟨I₁,…,Iₙ⟩,n ≥ 1) + │ ① 分类 class(I)(批次初态求值):C_dig(执行后 clears_map=true 的 dig) + │ · C_stop(stop)· C_other(非清 dig / flag×2 / move)——真模拟 F2,禁计数器捷径 + │ ② 语义序 ≺:C_dig ≺ C_stop ≺ C_other;同类内按到达序(稳定序) + │ ③ 逐意图执行;轮次结束首次为真处定胜者 ω + │ 其后意图全部 voided——零棋盘效果 · 零 AP 扣减 · 零事件(区别于非法拒绝) + ▼ +胜者 ω → 出口分发(见核心循环节出口分发表) +``` + +| 类别 | 成员 | 结算次序 | +|---|---|---| +| C_dig | dig 且执行后 clears_map = true(真模拟 F2——禁"rem=1 则任意 dig 必清"计数器捷径,反例 = 挖雷不入 S) | 最先 | +| C_stop | stop | 次之 | +| C_other | 非清 dig / flag ×2 / move | 其余(同类内按到达序——稳定序,多 cleared-dig 时最先者执行胜出) | + +四组合真值表(≡ A3 Rule 3 优先级 cleared > stopped > ap_exhausted): + +| # | 批次(到达序) | 结果 | RAP | +|---|---|---|---| +| 1 | [dig清, stop] | (cleared, new_board),stop voided | +0(voided) | +| 2 | [stop, dig清] | 语义序重排 → 同 #1 | +0 | +| 3 | [stop, dig耗] | (stopped, same_board),dig voided | += remaining | +| 4 | [dig耗, stop] | 语义序重排 → 同 #3 | += remaining | + +规则: + +1. (规则 9)一帧多个指令全部经批次入口 settle_batch 提交——A2 唯一结算入口,单意图批等价于逐个调便捷接口;批次是整体事务:先按固定类别顺序重排(能清图的挖最优先、停手次之、其余殿后,同类内按提交顺序),再逐个执行,轮次结束条件第一次成立处定出胜者,其后意图全部 voided——零棋盘效果、零扣费、零事件;voided 不等于被拒绝——拒绝是"本来就不合法",voided 是"来不及了";停手指令经批次入口转交资源系统,停手轮的结束事件仍由资源系统直发。 +2. (规则 10)一次挖掘结算让资源系统仲裁出轮次结束(行动点耗尽或清图胜出)时,A2 读取资源系统的轮次结束记录、组装恰一条轮次结束事件外发,外发之前先把本轮旗判定全部报完;行动点归零的瞬间轮次即结束——不存在"0 行动点还能操作"的窗口。 + +举证: + +- 规则 9:优先级口径与 A3 Rule 3 逐项一致,承接 A3 EC4 deferral;RAP 记账随胜者(键 = 胜者意图类型 × 结算时点 clears_map 真值);stop 经 settle_batch 提交、不直调 A3 的接线义务见 D1 强制项①;验收 AC-9/AC-10,边界 EC6/EC13/EC15。 +- 规则 10:组包即字段转抄——核心字段逐字段取自 A3 `last_round_end`;验收 AC-7/AC-8;清图轮剩余 AP 清零不入 RAP 见 A3 Rule 5。 + +## 行为契约(F1–F4 数值结论) +口径总纲:F1 定终局判定谓词(状态轴)、F2 定连锁展开算子(执行轴)、F3 定初始展开域(对偶轴——A1 度量与 A2 行为的接缝)、F4 定批次仲裁(事务轴)——四条均为行为契约,禁互相折算;本节零平衡公式、零随机、零字面量、零 UI 依赖。变量域、验算数据与全部推导论证见推演件「数值锚定与验算记录」及各问题节。 + +### F1 清图判定谓词(clear_predicate) +**结论**:cleared ⇔ 已开集覆盖全部安全格(等价形:剩余安全格计数 rem = 0 ⟺ cleared);求值点穷举两处——install 初始展开事务尾(不变式④保证恒 false)与每次 dig 执行完成点;插旗/拔旗/移动不改已开集 → 结构上不可能触发 cleared;A3 `clears_map` ≡ 本谓词在该动作事务尾求值——单一谓词两个名字,禁第二套实现。 + +``` +cleared(σ) :⇔ O(σ) ∩ S(B) = S(B) 等价形:|S(B) ∖ O(σ)| = 0 +rem(σ) = |S(B) ∖ O(σ)| ——剩余安全格计数;rem = 0 ⟺ cleared +``` + +| 项 | 判定处理 | +|---|---| +| 雷格 | 恒 ∉ S(已引爆雷格入 O 作 BFS 通道但不入判定) | +| 旗 | 不入 O 不入 S(带旗未开格照常计入 rem——"全图标旗不挖"永不 cleared) | +| 雾格 | 照常计入(A4 Rule 4) | +| 求值点 1 | install 初始展开事务尾——不变式④保证此处恒 false | +| 求值点 2 | 每次 dig 执行完成点(连锁完成后)——批次内逐 dig 求值,与 A3 `request_dig` 按次上报契约一致 | + +### F2 连锁展开闭包(chain_closure)+ chain_size / clears_map 上报参数 +**结论**:连锁闭包 = 单调最小不动点,与 A1 F1 算子①在 G=∅ 域逐字同构;chain_size = 附加格数(不含首格);clears_map ≡ F1 本事务尾求值(禁第二套实现);连锁不越旗(旗格不入链、不作传播源——集合减法非分支);含链开雾格、雾格永不传播(零特判定理非分支);Cl 单调幂等 ⇒ 逐动作增量求值 ≡ 一次全量闭包。 + +``` +Chain(O, c) = μX.[ O ∪ {c} ⊆ X ∧ ∀x ∈ X:( x ∈ S ∧ num(x) = 0 ⇒ N₈(x) ∩ S ∖ G ⊆ X ) ] +dig(c) 的棋盘效果:O′ = Chain(O, c);事件:cell_revealed × |O′ ∖ O|(BFS 规范序) +chain_size(dig(c)) = |O′ ∖ (O ∪ {c})| ——附加格数,不含首格(A3 AC-2/AC-6b 期望依赖) +clears_map(dig(c)) = [ S ⊆ O′ ] ——≡ F1 本事务尾求值 +``` + +### F3 初始展开域(initial_expansion_domain)+双包含回验 +**结论**:O₀ = R₀ = B3 ∪ flood 闭包,与 A1 F1 求解器初始态恰等(同算子同板 ⇒ 同值),显式不含外扩 1 环;恒不开雷 R₀ ∩ M = ∅ 为 A2 侧独立可证定理;双包含两臂构造性成立 ⇒ A1 回退口径(出生 3×3 最小态)不触发、度量保真零降级;退化守卫 R₀ ≠ S——install 拒绝清图退化板(不变式④纵深防御,非 A1 clearable 闸复验)。|R₀| ∈ [9, |S|−1],域非空前提 |S| ≥ 10。 + +``` +O₀ = R₀(B) = μX.[ B3 ⊆ X ∧ ∀x ∈ X:( x ∈ S ∧ num(x) = 0 ⇒ N₈(x) ∩ S ⊆ X ) ] + ≡ A1 F1 "出生 3×3 ∪ flood 闭包" 逐字(同算子同板 ⇒ 同值);显式不含外扩 1 环。 +性质 1(恒不开雷,定理):R₀ ∩ M = ∅(B3 无雷 + 闭包只收安全格)。 +双包含回验:臂① R₀ ⊆ 排除域(以最终产物板计:出生 3×3 ∪ 闭包 ∪ 外扩 1 环)——构造性成立; + 臂② A1 求解器初始态 = R₀ ⊆ O₀ = R₀ ——恰等。 +退化守卫(不变式④):R₀ ≠ S——install 拒绝清图退化板(EC1;非 A1 clearable 闸复验)。 +``` + +### F4 批次仲裁(settle_batch) +**结论**:语义序 C_dig ≺ C_stop ≺ C_other(同类内按到达序稳定);轮次结束首次为真处定胜者、其后意图全部 voided;(path_tag, continuation_hint, AP/RAP 终值) 对任意到达序不变(全批域)——执行/voided 划分的无关性仅对异类竞争成立;同类多成员批内划分由类内稳定序决定(不同到达序 = 不同输入,均为合法确定行为);RAP 记账随胜者。 + +``` +类别函数(排序用,批次初始态求值): + class(I) = C_dig 若 I = dig(c) 且执行后 clears_map = true(真模拟 F2——禁计数器捷径: + "rem=1 则任意 dig 必清"为假,反例 = 挖雷不入 S) + = C_stop 若 I = stop + = C_other 其余(非清 dig / flag ×2 / move) +语义序 ≺:C_dig ≺ C_stop ≺ C_other;同类内按到达序(稳定序——多 cleared-dig 时最先者执行胜出, + 其余 voided;未钉死类内序会使事件流与奖励格归属漂移——规范性义务)。 +结算循环:按 ≺ 逐意图执行;轮次结束首次为真处定胜者 ω,其后意图全部 voided + (false + voided:零棋盘效果、零 AP 扣减、零事件——区别于非法拒绝)。 +胜者 → 出口映射见核心循环节出口分发表;棋盘路径 round_end 由 A2 恰一次外发; + stopped 由 A3 直发(双发射源纪律,A2 禁双发)。 +``` + +### 显式排除声明(归属表) +| 对象 | 归属 | +|---|---| +| 平衡公式 | 本 GDD 无——全部数值归位如下 | +| 价目、ap_per_round、RAP 规则 | A3(F4 只消费 try_spend/request_stop/last_round_end 行为契约,零数值所有权) | +| 雷布/尺寸/密度/可推理率/R₀ 与排除域定义版权 | A1(F3 是其运行期镜像;A1 侧定义修订 = 接口漂移 + metric_version 联动,A2 禁独立改动) | +| 变异约束 | A4(F2 只消费"雾格恒非 0"生成保证) | +| 奖励档位/标中旗判定/结算编排 | B 系(A2 只输出事件流) | +| 曲线 | C3 | +| A2 拥有 | 四条行为契约(F1–F4) | +| 概率/期望公式 | 零(零随机——确定性由 AC-19 行为断言承载:同板同意图序列 ⇒ 事件流逐位相同) | + +### 实现期红线(与 A1 度量的漂移风险清单) +| # | 红线 | +|---|---| +| 1 | 0 格判定必须读真实数字 num(x),禁读显示值 | +| 2 | 初始展开禁加外扩环(破臂①)、禁只开 3×3(破臂②) | +| 3 | clears_map 与 cleared 同一实现 | +| 4 | chain_size 不含首格、链不开雷、含链开雾格 | +| 5 | A1 R₀/算子修订 = metric_version 跳变 + 本 GDD 同步修订 | +| 6 | 双包含臂①排除域以最终产物板计(先布后删无迭代,placed 基准可小于最终 R₀——A1 Rule 4 措辞修订义务,EC1) | + +## 边界情况 +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 初始展开即整图(R₀ = S,清图退化板) | install 拒绝——不变式④ fail-fast(`board_invalid` + error 诊断),非合同内行为;生产面由 A1 legality 闸拦截;A2 侧不变式④为纵深防御,双包含两臂不受影响(跨 GDD 阻断缺口反例 B 与 A1 修订义务见依赖节登记义务表与推演件) | 不变式④ / systems-index 门禁块 / AC-1b | +| EC2 install 产物违例(B3 内雷 / B3∪R₀ 内变异 / 数字不恒真 / R₀=S) | 拒绝 + 状态不变 + error 恰一类别(不变式①–④)——信任边界 fail-fast;违例板只能由程序构造错误产生(A1 产物经生成期合法性闸),运行期不存在该输入源 | 规则 3 / AC-2 | +| EC3 挖已插旗格 | 拒绝(board_face,sub_reason=flag_present)——须先拔旗(1 AP)再挖;旗是"别挖这里"的承诺标记 | A3 EC11 棋盘面判定落点 / AC-11 | +| EC4 连锁传播开到雾格 | 雾格被免费开出(channel=chain)、digit 恒真透传 + variant_ids 非空(呈现隐藏归 D1)、级联自然停止(雾格非 0 不传播)、计入清图、reward_eligible=true 奖励照常入库——零特判的自然结果,代码零变异分支 | A2×4① / A4 EC4 / AC-6 | +| EC5 玩家在雾格插旗 | 合法、耗 1 AP、旗立在未开雾格上;轮末判定恒 hit=false(雾区无雷——判定逻辑零特判自然覆盖);标错惩罚上线后的一致性推论:雾内旗按"非雷格上的旗"恒受罚——无 A4/A2 特判;D5 教学须警示"雾内插旗纯损" | A2×4② / OQ1 前瞻登记 / AC-12 | +| EC6 同批次多结束路径竞争 | F4 语义序仲裁,四组合与 A3 Rule 3 逐项一致(真值表)、RAP 记账随胜者、voided 意图零副作用(零棋盘写/零扣减/零事件——被 voided 的旗动作不进轮末判定,无幽灵旗);胜出后余意图 voided,棋盘只读查询逐格不变。联合清图让位条款:批内个体非清的 dig 联合可清图时,stop 仍先执行并胜出(C_stop ≺ C_other)——联合清图高光被停手让位,为语义序的显式设计后果(单意图帧提交下生产面不可构造——D1 强制项⑥;F4 语义保留为防御性兜底,AC-10 义务不豁免) | F4 / AC-10 | +| EC7 最后 1 AP 挖开最后安全格 | cleared 与 ap_exhausted 同时成立 → A3 事务内 cleared 胜出 → A2 外发 (cleared, new_board) 恰一条、剩余 AP 清零不入 RAP——防"耗尽优先"实现把清图高光结算为最差结局 | A3 EC10 / AC-7 用例 1 | +| EC8 战斗中热重载修改 VariationTable | 板持生成期变异快照(非活引用)——行为零变化;A2 零表读取使抗重载为构造性;行为级代理 = 换根重放事件流逐位相同 | A2×4④ / A4 EC8 / AC-18 | +| EC9 拒绝目录外溢 | 目录外输入(越界坐标等)按 board_face 拒绝 + `sub_reason=out_of_catalog`(debug 级诊断,不与玩家语义混桶)+ debug 诊断禁静默吞;三源(board_face/resource_face/phase)互异可判定——玩家面文案归 D1(映射源 = sub_reason 枚举) | 规则 5 / AC-21 目录外用例 | +| EC10 挖开雷格 | `kind=mine` 事件(无 digit)、轮次不结束、reward_eligible=false、格子可通行(工程债)可经其 BFS 续挖;同轮连续挖多雷 = 每雷独立事实事件;rem 不变(雷 ∉ S) | 工程债(systems-index 门禁修正记录)/ AC-15 | +| EC11 换图/刷新/战斗结束 | `install_board`(新板)= 旗/小人/已开集全量重置 + 初始展开重放;`retire_board` = 板实例与变异快照消亡——旗随图存续语义(A3 Rule 8)的 A2 侧终点 | A3 Rule 8 / AC-13/17 | +| EC12 非 Active 相位动作请求 | 拒绝(phase 源)、零事件、全态不变——A2 与 A3 两侧按同一相位矩阵各自判定、结论一致;不存在 0-AP 窗口(A3 Rule 3 的对侧消费) | 状态矩阵 / AC-16 | +| EC13 批次内旗动作与终局意图竞争 | 旗动作 ∈ C_other → 被 cleared-dig/stop 压成 voided;判定基线 = 轮次结束事务开始时旗位(voided 旗动作不留痕)——语义序使基线与到达序无关 | F4 / AC-10c/d | +| EC14 移动边界 | 目标未开 / 无 BFS 路径 → board_face 拒绝;目标即当前格定义为零移动,幂等受理;`compute_move_path` 对不可达格返回空——预览与执行同源 | 规则 7 / AC-14 | +| EC15 同批次多个 cleared-dig | 类内稳定序——到达序最先者执行并胜出,其余 voided;(path_tag, RAP) 天然唯一,稳定序规范的是事件流与奖励格归属 | F4 / AC-10e | +| EC16 install 与 A3 相位组合非法(如非刷新路径的 Active 装板〔刷新路径已受理〕/ NoBoard 动作) | 编排面拒绝(板态/相位源)——B1 编排错误 fail-fast 暴露,禁静默(A3"拒绝 + debug 诊断"口径同族,进程致命保留给数据通道) | 状态矩阵 / AC-16 | +| EC17 连锁前沿遇旗(连锁不越旗) | 旗格不入链、不作传播源——被旗挡的格保持未开 + 带旗,仅经该格可达的后续格本轮不被链开(经其他路径可达者照常开出);chain_size 只计实开格;旗格轮末判定照常(hit 按其是否为雷——误标安全格留待玩家自拔,自己的承诺自己付 1 AP 撤回,与 OQ1 惩罚逻辑自洽);零特判合规:传播集 `∖ G` 是集合减法非分支(F2 注) | 规则 6 / F2 注 / AC-5 分支 2 | + +## 依赖与输出 +A2 不依赖任何下游——systems-index TD-SYSTEM-BOUNDARY 唯一回边:A2 只外发事件,B4/B1 订阅后回调;A2 为瓶颈系统(B4/D1/D5 挂其事件),规则一次定稳。 + +| 方向 | 系统 | 性质 | 接口要点 | +|---|---|---|---| +| 上游 | A1 | hard(产物消费) | 棋盘产物 = 格状态 + 变异标记 + 出生区(Board class_name 数据契约);双包含回验两臂落点在 F3(臂① 展开域 ⊆ 排除域、臂② 求解器初始态 ⊆ 展开域,恰等——A1 回退口径不触发,门禁缺项即 blocking);install 不变式①–④为消费侧信任边界(fail-fast);A1 修订义务 ×2 见跨 GDD 登记义务表 | +| 上游 | A3 | hard(账本) | `try_spend(action_kind)` 唯一扣减入口(A2 禁自行扣减);`request_dig(chain_size, clears_map)` 参数上报(A2 计算);stop 意图委托 `request_stop()`(A3 直发 stopped round_end);轮末读 `last_round_end` 组包棋盘路径 round_end(读取权 = 接口契约);相位观测量(Idle/Active/Waiting)= A2 动作闸的对侧来源;A3 API 面零改动(settle_batch 是 A2 侧入口) | +| 上游 | A4 | soft(快照透传) | 变异属性经 A1 产物快照只读携带、随 `cell_revealed.variant_ids` 透传——A2 执行逻辑零变异分支(零特判原则,义务是"不加分支");A2×4 清单缺项即 blocking | +| 上游 | D2 | 无直接依赖(登记事实) | A2 零表读取、零 ConfigDB 引用——参数经 A3 账本、板面经 A1 产物;D2 Interactions 表"A2 无直接依赖"由本 GDD 证实 | +| 下游 | B1 | hard(编排,未设计——provisional 登记) | 消费 round_end(棋盘路径)与全事件流结算;换图编排 = `install_board`/`retire_board` 的调用方(接线归 B1 GDD);B1 强制项 ×3 见下表(缺项即 blocking) | +| 下游 | B4 | soft(订阅,未设计) | 清图奖励触发(cleared 路径)与安全格奖励内容归 B4——A2 只上报棋盘面资格事实(`reward_eligible`/`channel`),内容与折算不经 A2;连锁批量授予的溢出吸收设计待 B1/B4 条目化(13 格链 × B3 max_stack=2 ⇒ 大量"溢出未生效"行进结算明细,P2 画面反向伤害连锁幻想——溢出转一次性资源/治疗或显式接受,裁决归 B1/B4) | +| 下游 | D1 | hard(呈现,未设计) | 棋盘/小人/旗/数字渲染;数字显示判定消费 `variant_ids`(雾格无字——digit 恒真透传是前提);三源拒绝呈现(映射源 = reject_reason + sub_reason);D1 强制项 ×6 见下表(缺项即 blocking),数据面义务见呈现与数据供给契约表 | +| 下游 | D5 | soft(Alpha) | 教学口径来源:小人三动作 + 连锁/旗/收轮以本 GDD 规则为准;教学条目:雾内旗纯损、雷格可通行、旗挡链(连锁止于旗属正常)、挖到雷 = 敌方优势而非死亡(经典扫雷玩家最大预期违背)、已开雾格无字属正常——挂 OQ1/EC5/EC17 | +| 下游 | D4 | soft(Alpha) | 挖掘/连锁/雷格/收轮音效挂三事件流;旗/移动反馈经调用返回值命令式驱动(两动作零事件外发——发射面封闭的已声明代价;systems-index 依赖分层 D4 ← A2,B1) | +| 下游 | A5 | soft(垂直切片预留) | 探测类道具的显影面可能挂 A2 查询接口(未开格数字显影)——接口归 A5 GDD 定稿,本 GDD 不预写(OQ4) | + +### B1 强制项(缺项即 blocking,systems-index 门禁块登记) +| # | 义务 | +|---|---| +| 1 | 结算触发 = Waiting 相位编排点、不依赖事件到达序(stopped 轮 A3 直发 round_end 与 A2 旗判定批的到达序不承载语义) | +| 2 | `reward_eligible=false`(initial 通道)的格不得进入奖励入库(A3 EC5 委派闭环) | +| 3 | 诚实重复上报的去重义务(资格以雷格实例为键) | + +### D1 强制项(缺项即 blocking,systems-index 门禁块登记) +| # | 义务 | +|---|---| +| 1 | stop 意图经 `settle_batch` 提交、不直调 A3(单一结算序保证) | +| 2 | 四种格态一眼可辨(已开 0 格地板 / 已开雾格无字 / 未开格 / 未开雾格——左键"已开=移动/未开=挖"消歧的感知前提;后果不对称:误挖 = 1 AP + 可能触雷且不可逆) | +| 3 | 悬停预承诺(光标区分移动/挖/旗目标语义)+ 可挖域(8 向相邻)常驻指示 + 怀旧扫雷玩家 chord 肌肉记忆首遇提示("点击数字格 = 移动小人"——非模态、每 run 一次,A4 先例) | +| 4 | 已引爆雷格"可通行可读"呈现 | +| 5 | `compute_move_path` 呈现语义定稿(合法性指示 vs 穿行动画轨道——P4 氛围载体;BFS 行主序路径可能视觉绕路,形态归 D1 裁决) | +| 6 | 单意图帧输入收集(stop 点击终结当帧收集;F4 语义序退为防御性兜底)+ 批次 voided 呈现义务(至少 stop 被 voided 须可感知——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP)+ 每次拒绝玩家可感知(文案归 D1,映射源 = reject_reason + sub_reason) | + +### 呈现与数据供给契约(A2 无自有渲染面与 UI——数据供给规格 + D1 呈现义务登记) +| # | 数据面义务 | 归属 | +|---|---|---| +| 1 | 数字可读性的数据前提:digit 恒真透传(含雾格)是"数字是主角"(视觉锚 1)的逻辑地基——呈现层任何隐藏/遮罩都不改数据层真值;已开雾格 = 地板无字(负空间,防误读为 bug) | D1 | +| 2 | 绘制优先级固定(视觉锚 3):数字 > 小人 > 旗 > 变异纹理 > 底色——小人所在格用边框高亮而非覆盖文本;A2 的事件流是 D1 分层绘制的输入 | D1 | +| 3 | 三 channel 驱动动效语义(D1/B5 消费):initial(开局整片亮起——"地牢初醒")· chain(连锁撕开——P1 高光时刻的动效载体;连锁止于旗的呈现已登记 D1/教学)· dig(单格回应) | D1/B5 | +| 4 | kind=mine 事件 = 已引爆雷格视觉态:已开底 + 雷图标 + 可通行可读——玩家必须看得出"这是走过的雷、能走"(D1 强制项④) | D1 | +| 5 | 变异标记保真:variant_ids 透传错误即呈现欺骗(雾格显影/隐藏全链路的数据源) | D1 | +| 6 | 无 MVP 音频:挖掘/连锁/旗/雷格/收轮音效挂 D4(Alpha,依赖分层 D4 ← A2,B1);事件流已含全部触发点 | D4 | +| 7 | 输入模型:鼠标驱动(PC 主输入)——左键已开格 = 移动 / 未开相邻格 = 挖、右键 = 插/拔旗、停手按钮 = `settle_batch([stop])`;全部意图经 `settle_batch` 提交(D1 强制项①)——单帧多意图时 A2 语义序仲裁兜底(F4);触屏/Gamepad 非 MVP 门禁(意图对象架构使后续接入零数据层障碍) | D1 | +| 8 | 单意图帧输入收集(D1 强制项⑥):stop 点击终结当帧收集——F4 语义序退为防御性兜底 | D1 | +| 9 | 输入消歧感知前提(D1 强制项②③):四种格态(已开 0 格地板/已开雾格无字/未开格/未开雾格)一眼可辨、悬停光标区分移动/挖/旗目标语义、可挖域(8 向相邻)常驻指示——硬前提(后果不对称:误挖 = 1 AP + 可能触雷且不可逆) | D1 | +| 10 | 三源拒绝呈现:board_face(sub_reason 封闭枚举)/ resource_face(AP 不足)/ phase(收轮中)——每次拒绝玩家可感知(AC-21 为数据源验收) | D1 | +| 11 | voided 呈现义务(D1 强制项⑥):被 voided 的意图(至少 stop)须玩家可感知——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP 的读感欺骗 | D1 | +| 12 | 移动路径预览:`compute_move_path` 返回 BFS 路径,呈现语义(合法性指示 vs 穿行动画轨道)归 D1 GDD 裁决定稿(D1 强制项⑤——systems-index 门禁登记项,悬案的数据面已备) | D1 | +| 13 | 输入摩擦护栏观测:每轮原始输入动作数(含被拒/voided)∶ 当轮 AP 消耗 ≤ 2(A3 P4 载体;"点击"以设备中立"输入动作数"计)——归 D1 遥测(A2 只收意图对象、不收原始输入),≤2 为 playtest 待验证假设(挂 A3 OQ3);远距目标两击摩擦的复合意图(点击远格 = 自动 move+dig)登记为 D1 评估项(MVP 可裁决不做、留痕) | D1 遥测 | +| 14 | 结算明细无 A2 附加项:效果归因全部经事件流字段由 B1 呈现(P2) | B1 | + +### 跨 GDD 新登记义务汇总(本 GDD 出具,随 systems-index 门禁块登记) +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| A1-1 | F5 legality 增 R₀ ≠ S(违例 = 该次尝试废弃重试,clearable 同款)——"初始展开即 cleared"反例 B 与 N=1 行整行退化阻断缺口 | A1 | 待回写 | +| A1-2 | F4 批量断言严格零 + Rule 4 排除域基准措辞(最终产物板计) | A1 | 待回写 | +| B1×3 | 结算触发相位编排点 · initial 格不入奖励库 · 兑付去重(明细见 B1 强制项表) | B1 | 已登记(缺项即 blocking) | +| D1×6 | stop 经 settle_batch · 格态可辨 · 悬停预承诺+可挖域+chord 首遇提示 · 雷格可通行可读 · 路径呈现语义定稿 · 单意图帧收集+voided 呈现+拒绝可感知(明细见 D1 强制项表) | D1 | 已登记(缺项即 blocking) | +| B1/B4-1 | 高光兑现节点条目化(含 chord 缺席的标中旗即时兑现补偿候选——清单外散文登记会丢失,B3 先例) | B1/B4 | 待条目化 | +| B1/B4-2 | 连锁批量授予溢出吸收设计(见依赖 B4 行) | B1/B4 | 待条目化 | + +## P0 最小闭环 +1. A1 产板 → `install_board` 受理:持有生成期快照 + 校验四不变式(违例 fail-fast)。 +2. 初始展开 R₀ 免费翻开(cell_revealed ×\|R₀\|,channel=initial,规范序),小人落位 B3 中心格。 +3. 玩家意图经 D1 → `settle_batch`(A2 唯一结算入口)。 +4. dig:棋盘面判定 → A3 `try_spend` → 开格 + 连锁(cell_revealed ×(1+chain_size)),A2 上报 `request_dig(chain_size, clears_map)`。 +5. flag/move:旗入棋盘(1 AP)/ 免费瞬移——零事件,反馈经调用返回值命令式驱动。 +6. 批次内轮次结束首次成立 → 定胜者,其后意图 voided(零效果、零扣费、零事件)。 +7. 收轮:`flag_judged ×N` 先于 `round_end`——棋盘路径 A2 读 `last_round_end` 组包外发,stopped 路径 A3 直发。 +8. 三事件流被 B4/B1/D1/D4/D5 消费(结算归因、呈现、音效)→ Waiting 相位编排受理 `install_board` 换新图,回到步骤 1。 + +## P1 / P2 后置 +后置以对侧登记为生效条件;复活任何一行 = 修订本 GDD,不许静默加回。 + +| 优先级 | 内容 | 后置原因 | +|---|---|---| +| P1 | B1 轮次编排接线(Waiting 出口/next_round_mode/request_refresh 代理)· 结算兑付与 P2 来源标注 · 兑付去重(诚实重复上报的对侧,A3 Rule 8/OQ5)· EC8 末轮判定次序 · 双发射源消费序 | B1 未设计——systems-index B1×8 义务清单 + 本 GDD B1×3,生效条件成立 | +| P1 | B4 奖励内容与差异化插槽 · debuff 档位 | B4 未设计——A4 三条定价硬约束 | +| P1 | D1 数字显示消费 variant_ids · 三源呈现 · 路径预览表现 · 点击摩擦观测 | D1 未设计——A4 D1×2 + 本 GDD D1 强制项 ×6 | +| P2 | A5 显影接口 | 接口归 A5 GDD 定稿(OQ4) | +| P1 | A1×A2 双包含端到端集成用例——"A1 真生成器产物 → A2 install"全链路回验 | 两系统实现后集成冒烟承载(本 GDD 冒烟用程序构造板) | +| P1 | 标错惩罚落地 | OQ1 登记——实测义务保留,不阻塞设计;惩罚前 AC-12 只断言 hit=false | + +## 调参表 +A2 零专属 knob——结构冻结项修订走本 GDD 修订流程非调参;禁止在 A2 侧复制任何参数(D2 Rule 1 单一事实源)。已锁数值以 design/registry 为准,本表只登记字段与归属,不复述锁值。 + +| 字段 | 域 | 默认 | owner | 说明 | +|---|---|---|---|---| +| ap_per_round · dig_ap_cost · flag_place_ap_cost · flag_pull_ap_cost · rap_refresh_cost | 见 registry action_economy | 以 registry 为准 | A3 | 节奏与价目——A2 只消费 try_spend 行为契约 | +| BoardGenTable 九字段 | 见 registry board_gen_table | 以 registry 为准 | A1 | 板面与推理保障 | +| unlock_level | 见 registry | 以 registry 为准 | A4 | 变异资格 | +| flag_pull_ap_cost 联动观测(引用不拥有) | — | — | A3 | 挪旗成本与 OQ1 标错惩罚的双重定价位(A3 OQ2 复核落点——若实测推翻拔旗 1 AP,A2 行为零改动、仅价目表值变化) | +| ap_per_round 联动观测(引用不拥有) | — | — | A3 | 决定每轮可兑现的确定动作供给(A3 OQ3 供给指标) | +| A2 侧可观测指标(非 knob,遥测域) | — | — | A2 | 每消耗 1 AP 输入动作数(≤ 2 护栏,P4 点击摩擦——D1 观测,provisional 待 playtest 验证)· 连锁规模分布 · 批次数/轮 | + +结构冻结清单(非 knob):三事件面封闭 · 初始展开 = R₀ 恰等 · 连锁免费 · 移动/初始展开不经账本 · 无 chord · 批次语义序 · 四条 install 不变式 · 瞬移移动语义。 + +## 验收标准 +### 测试口径 +| 口径 | 要点 | +|---|---| +| 测试载体 | `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化);A2 冒烟 = Core-Core 集成:被测面 = A2 规则引擎 + 真 A3 实例(资源全经真账本 `try_spend` 驱动——A2 不可自行扣减);冒烟另以 B1 编排角色最小形驱动 `notify_round_start()`/`notify_settlement_done()`(仅开轮/收轮;B1 就绪后改走真实调用方,断言口径不变) | +| 棋盘输入 | Board class_name 直引程序构造(不经生成器、不经 load()/preload()——A4 AC-5/A1 夹具先例;板规格即 A1 产物 schema 的坐标级实例化,A1 未实现不阻塞本冒烟) | +| API 面 | provisional 封闭枚举,见枚举节(install_board · settle_batch——D1 唯一提交入口,单意图批 = 平凡形 · try_* 便捷形 · compute_move_path 只读 · retire_board);无 chord 负空间:封闭枚举即"无对应 API"的验收形态,新增快开入口 = 接口漂移 | +| 事件捕获断言面 | B1/D1 未设计 → 以事件捕获列表断言,分订两发射面:A2 面(cell_revealed / round_end 棋盘路径 / flag_judged)+ A3 直发面(stopped round_end);payload 口径钉死:① digit 恒真透传(仅 kind=safe 携带;雾格携带真值——呈现隐藏归 D1,判定源 = variant_ids);② reward_eligible:initial 恒 false、chain/dig ∧ safe 恒 true(含链开雾格)、kind=mine 恒 false;③ round_end 核心字段 ≡ A3.last_round_end 逐字段(同构) | +| 只读观测量 | A2 侧 = 已开集/旗集/小人位/事件捕获;A3 侧 = ap/rap/phase/last_round_end;多步 GIVEN 均给出构造序列 | +| 夹具策略 | 程序构造小板、每用例先 `reset_diagnostics()`;行为夹具根 = 零(A2 无数据表、无加载期校验类别——`--data-root` 通道在本域无消费者;资源面非默认价目分支经 D2 R3 debug override 构造,A3 AC-7 同款);登记常数协议(A1 黄金向量同源):每板登记 (R₀ 成员集, \|R₀\|, \|S\|, 关键格 digit, 级联集) + 逐格手工推导记录入版本库;失效处置 = 先复核推导、推导无误即实现缺陷 | +| 期望值推导纪律 | 行为类 THEN 数值 = ConfigDB A3 表读值推导(ap_per_round/各价目经测试通道读取)⊕ 板登记常数;不写死玩法数值,唯一例外 = 登记常数;默认价目全 1 AP 下"AP 不足"分支不可构造(A3 EC2 同款)——资源面分支显式承载于 debug override 非默认表值(AC-21) | +| 致命类协议 | 零行 manifest(显式声明)——D2 AC-2b 协议适用前提 = 加载期数据通道致命;A2 无加载期校验,install 违例 = `board_invalid` 拒绝 + error 诊断(进程致命保留给数据通道;实现期 ADR 若裁决升格,回本节补 manifest 行) | +| out-of-scope | deferral 以对侧登记为生效条件——见 P1 / P2 后置表 | + +### 夹具板清单(程序构造小板) +spawn = 出生 3×3 中心格坐标,逐板显式登记:b_spawn / b_flag / b_batch / b_mine_walk / b_degenerate = (3,3),b_chain / b_fog = (3,2)——按默认"板中心"读法该两板 B3 = {2..4}² 会含 y=4 雷墙雷、违反不变式①、板不可安装(已修正并纳入 OQ3 复核范围)。 + +| 板 | 规格(坐标级要点) | 服务 | +|---|---|---| +| `b_spawn` | M = {(1,y): y∈[0,6]∖{3}} 竖墙 6 雷留缺口;R₀=35、\|S\|=43、未开 8 格;(1,3) digit=2 | 安装五属性/双包含/挖掘/移动/相位 | +| `b_chain` | spawn=(3,2);M = y=4 横墙 6 雷留 (3,4) 缺口;R₀=28(y∈[0,3] 全安全格);墙下封闭区 14 格;(3,6) 为 0 格、级联集 \|C\|=13 | 连锁/双包含/链不越旗 | +| `b_fog` | spawn=(3,2);b_chain 几何 + M += {(5,0),(6,1)};雾 F = {(0,5),(1,5)}(链可达)∪ {(6,0)}(链不可达);雾格 digit 2/3/2、厚度≤2 ✓;R₀ 常数随 M 变化按板重算((6,2)/(6,3) 退出 R₀) | 雾链两态/雾旗/快照/零特判/回放 | +| `b_fog_bare` | b_fog 同 M 同几何、variant 标记剥离 | AC-20 同构对板 | +| `b_flag` | b_spawn 同几何复用;(1,2)/(1,4) 雷 = hit 旗靶、(0,3) 安全 = miss 旗靶 | 旗生命周期/轮末判定/三源拒绝 | +| `b_batch` | b_spawn 同几何复用(未开恰 8 格支持构造) | 批次四组合/两收轮路径 | +| `b_mine_walk` | M = {(3,5)} 单雷;(3,4) digit=1 ∈ R₀ | 雷格挖开/已引爆可通行 | +| `b_degenerate` | 8×8、M = {(7,7)} 单雷角——R₀ = S(全 0 格连通,构造性退化) | 不变式④ install 拒绝 | +| `b_invalid_*` | ① mine_spawn:b_spawn+M+=(3,3) ② fog_spawn:b_fog+F+=(3,2) ③ fog_flood:b_fog+F+=(5,1)(R₀ 成员)④ digit:b_spawn+数字篡改 | 不变式①②③④ 各一(AC-2) | + +### AC 清单(Given-When-Then,编号不变,23 条) +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 真 A3 实例(phase=Idle)与 `b_spawn` | `install_board(b_spawn)`,随后 `notify_round_start()` → Active | accepted=true 且事件捕获**仅**含 cell_revealed 批,逐事件断言:① 免费——A3 ap 仍=表读值(零 try_spend)且诊断零新增;② 计入清图——构造序列挖尽未开 8 格(登记常数)时恰在第 8 次触发 `round_end{cleared, new_board}`(无超前/滞后);③ 无奖励——全部 reward_eligible=false(channel=initial);④ 永不触雷触雾——全部 kind=safe;⑤ 恰等 R₀(有序断言)——事件**有序列表** ≡ 测试侧朴素 BFS 重算 R₀ 的行主序枚举,逐格逐序相等(规范序钉死——DFS/列主序等"错误但确定"的实现不可通过;登记常数 \|R₀\|=35 为独立锚) | 规则 4 / A3 EC5 委派 | +| AC-1b | `b_degenerate`(R₀=S 构造板) | `install_board`;随后 `install_board(b_spawn)` | accepted=false、reject_reason=board_invalid、error 诊断恰一条类别=**inv4_r0_equals_s**(枚举见状态节);随后重装 accepted=true(拒绝原子性) | EC1 / 不变式④ | +| AC-2 | `b_invalid_mine_spawn` / `b_invalid_fog_spawn` / `b_invalid_fog_flood` / `b_invalid_digit` 各板(每用例先 reset_diagnostics) | 各 `install_board` | 均 accepted=false、board_invalid、error 恰一对应类别(**inv1_mine_in_spawn / inv2a_variant_in_spawn / inv2b_variant_in_r0**〔"R₀ 触雾"归此——②的 R₀ 子情形〕**/ inv3_digit_invariant**,封闭枚举见状态节) | 规则 3 / EC2 | +| AC-3 | `b_chain` 与 `b_fog` 各 install 后捕获的初始域格集 E(B);测试侧按 A1 F1/Rule 4 公开定义朴素重算 R₀(B) 与排除域 X(B)=R₀∪外扩 1 环(X(B) 以最终产物板计——先布后删后基准,A1 Rule 4 措辞修订义务 EC1) | 逐板比对 | **臂①** E(B) ⊆ X(B);**臂②** E(B) ≡ R₀(B) **逐格恰等**(不欠展开也不过度展开——A1 回退口径不触发的行为面);两板全用例通过 | 规则 4 / F3 / registry a2_dual_containment(门禁缺项即 blocking) | +| AC-4 | `b_spawn` Active、小人于 R₀ 内 | `try_dig(c)`(相邻未开)→ `try_dig`(非相邻 / 已开格 / 有旗格)→ 等价护栏:同构造态分别驱动 `try_dig(c)` 与 `settle_batch([dig c])`(各新轮) | 第一次 executed、A3 ap −dig_ap_cost(表读值,真 try_spend)、事件恰一条 `{cell=c, kind=safe, channel=dig, reward_eligible=true, digit ≡ 测试侧重算 8 邻雷数}`;拒绝分支 rejected+board_face(sub_reason 分别 = not_adjacent / target_revealed / flag_present)、零事件、ap 不变;等价护栏:事件流/ap/棋盘只读观测量/诊断逐项相同(单意图批 ≡ 便捷形) | 规则 5 / 规则 9 / A3 Rule 2 | +| AC-5 | `b_chain`;构造序列挖 (3,4)→移→挖 (3,5)→移→`try_dig((3,6))`(0 格);分支 2:构造至小人立于 (3,5) 后 `try_flag_place((2,5))`(未开相邻,1 AP)→ 再 `try_dig((3,6))` | 第三次挖掘(分支 1);插旗后再挖(分支 2) | 分支 1:executed 且 ap 总耗 = 3×dig_ap_cost(级联免费——非 3+13 格);事件 channel=chain 批恰 \|C\|=13 条(登记常数)且序 ≡ 测试侧 BFS 行主序枚举、全 reward_eligible=true、digit 恒真透传、事件数 = chain_size + 1(一致性恒等式);级联止于非 0 格(链只经 0 格扩);链格计入清图(挖尽封闭区后余格清图即 cleared)。分支 2:(2,5) 保持未开+带旗(零 cell_revealed)、chain_size=12(级联集减 1——(1,5)/(1,6) 经 (2,6) 照常开出,仅旗格本身被挡)、旗格轮末判定照常 | 规则 6 / F2 / EC17 | +| AC-6 | `b_fog`;同 AC-5 构造至 (3,6) 级联;另一用例:小人移至 (5,1) 后 `try_dig((6,0))` | 级联(链可达雾);全价挖链不可达雾 | 四项逐验:① 链可达雾 (0,5),(1,5) 免费开出(channel=chain、ap 无雾附加扣减);② variant_ids 含 "fog" 且 digit=2/3 恒真透传(呈现判定数据源——A2×4③ 同板证);③ 级联停止(级联集与 b_chain 同构板逐格同形——雾格只作叶);④ reward_eligible=true 且计入 cleared 判定。另一用例(链不可达全价):executed、channel=dig、耗 1 AP 全价、variant_ids 非空、digit=2 透传 | A2×4①③ / A4 EC4 / fog_ap_note 两态执行侧 | +| AC-7 | 用例 1(同帧竞争):`b_batch` 构造至"未开恰 1、ap=1"(cleared 与 ap_exhausted 同事务成立——"耗尽优先"缺陷仅在此构造可检出);用例 2:同构造 ap=k≥2 | `try_dig(末格)` | 用例 1:executed、A2 面 `round_end{cleared, new_board}` 恰一条(ap_exhausted 不触发)、其字段 ≡ A3 `last_round_end` 同帧读值(组包数据源行为面)、与 stopped 路径 payload 同构(字段集/类型一致)、rap 不变(剩余 AP 清零);用例 2(剩余清零非平凡分支):cleared 恰一条、rap 不变(k−1 > 0 清零非平凡) | 规则 10 / EC7 / A3 Rule 5/6/9/EC10 | +| AC-8 | `b_batch` 未开 ≥2、ap=1 | `try_dig(非末格)`;紧接任意 `try_*` | executed、`round_end{ap_exhausted, same_board}` 恰一条、A3 remaining_ap=0 不变式;紧接动作全部 rejected+phase、零事件 | 规则 10 / A3 Rule 3/EC1/EC14 | +| AC-9 | `b_batch` Active 未开完、ap=k≥1 | `settle_batch([stop])` | **A3 直发面**捕获 `round_end{stopped, same_board}` 恰一条(A2 面零 round_end——双发射源行为面)、rap = 前值 + k(1:1,表读值推导)、事件序 = flag_judged ×N 先于 round_end(事件序契约) | 规则 9 / A3 Rule 4/9 | +| AC-10 | `b_batch`(每用例新轮新构造)六场景:a〔dig清→stop〕· b〔stop→dig清〕· c〔dig→stop,ap=1 耗尽型构造:新轮 7 次非清挖〕· d〔stop→dig,同 ap=1 构造〕· e〔dig清₁→dig清₂ 同批〕· f〔非清 dig₁→非清 dig₂ 联合清图;多步 GIVEN:前轮挖雷 (1,2) 开道(雷格可通行的构造性利用)→ 挖尽 x=0 列余格 → 停手收轮;新轮未开恰 {(0,3),(1,3)}、ap=8〕 | 提交各批次 | a:cleared 胜出、rap 不变(stop 清零不入 RAP);b:与 a 同结果(同 path_tag/同 rap 终态/同事件数——异类序无关性);c:**语义序救援用例**——stopped 胜出(朴素到达序将产出 (ap_exhausted, RAP+0)——语义序校正为 (stopped, RAP+1))、rap += 1(表读值推导)、被弃 dig 格仍未开(续轮同图可证);d:与 c 同结果;e:类内稳定序——到达序最先者执行胜出、恰一条 cleared、第二 dig voided 零副作用;f:dig₁ 执行不清图(rem 2→1)、dig₂ 结算时点 clears_map=true 胜出 → cleared、两意图均执行、rap 不变(出口映射按结算时点真值键控——F4)。全用例断言:voided 意图零棋盘写/零扣减/零事件(棋盘只读查询逐格不变)、round_end 恰一条;c/d 断言 `flag_judged ×N` 先于 `round_end`(事件序契约) | 规则 9 / F4 / EC6/EC13/EC15 / A3 EC4 deferral 兑现(A3 冒烟不可构造用例在本节兑现,deferral 生效) | +| AC-11 | `b_flag`、小人于 (2,3) | 依次插 (1,4)、插 (1,2) 后拔 (1,2)(挪旗)→ `try_dig((1,4))`(有旗)→ 插非相邻/插已开/插已有旗/拔无旗/拔非相邻 | 全 executed、ap 合计 −(place+pull)×2(表读值)、旗集随动、零事件外发;挖旗格 rejected+board_face(EC11 落点);各拒绝分支 rejected+board_face | 规则 5/8 / A3 EC3/EC7/EC11 | +| AC-12 | 分支 1:`b_flag` 插 (1,4)(雷)与 (0,3)(安全,经挖 (1,3) 后站立可达),前置断言验"被拔的不判"(插 (1,2) 后拔,轮末无该条目);分支 2(雾旗):`b_fog` 小人相邻未开雾格插旗;分支 3(重复上报):分支 1 收轮后 `notify_settlement_done(same_board)`+`notify_round_start` 续轮旗仍在 | `settle_batch([stop])`(分支 1/3);轮末(分支 2);再停手(分支 3) | 分支 1:flag_judged 恰 2 条**集合断言**:{(1,4),true}、{(0,3),false}(行主序枚举);分支 2:`{cell,hit=false}` 恒(雾区无雷零特判;标错惩罚上线后"恒受罚"推论 = OQ1,惩罚前不可判定);分支 3:再次 {(1,4),true}——A2 逐轮诚实重复上报,**去重归 B1/B4(out-of-scope)** | A2×4② / A4 EC5 / A3 Rule 8/OQ5 上报半 | +| AC-13 | `b_flag` 已插 2 旗 stopped 收轮 | 续轮(same_board);随后 `retire_board()` + `install_board(b_fog)` | 旗集不变且**事件捕获零新增 cell_revealed**(初始展开板级一次、跨轮不重放——每轮重放的违例实现在此用例现形);重装后旗集 = 空、初始展开照常(按新板) | 规则 8 / EC11 / A3 Rule 8/EC9 | +| AC-14 | `b_spawn` 小人于 (3,3) | `compute_move_path((2,3))` 两次 → `try_move((2,3))` → 拒绝/幂等四对:① move 未开格 ② move 已开但 BFS 不可达格 ③ move 目标 = 当前格 ④ 正常移动分支 | 路径两次逐格相同(确定性)且全格 ∈ 已开集、相邻步 8 向合法;try_move executed、位置更新、**ap 不变**(不经账本)、零事件;① rejected+board_face(sub_reason=target_concealed);② rejected+board_face(sub_reason=no_path)**且** `compute_move_path` 对同格返回空(预览与执行同源,一用例双断言);③ executed 幂等(零移动:位置不变、零事件、ap 不变——EC14);④ 保持 | 规则 7 / EC14 / A3 Rule 2 移动行/P4 | +| AC-15 | `b_mine_walk`、小人于 (3,4) | `try_dig((3,5))` → `try_move((3,5))` → 经雷格续挖 (3,6) | executed、ap −1、事件恰一条 `{kind=mine, channel=dig, 无 digit 字段, reward_eligible=false}`、**轮次不结束**(两面零 round_end);try_move executed(**可通行**——工程债验收落点)、位置=(3,5);续挖正常受理 | 规则 11 / EC10 / 工程债登记 | +| AC-16 | NoBoard 或 Installed·Frozen(Idle/Waiting 两分支);另 GIVEN Active | 依次 dig/flag×2/move/`settle_batch([stop])`;Active 下 `install_board`/`retire_board` | 全部 rejected+phase、棋盘只读查询与事件面逐字不变;Active 下装板/退板拒绝(板态——EC16) | 状态矩阵 / EC12/EC16 / A3 输入×相位矩阵对侧 | +| AC-17 | `b_spawn` 已装且部分挖掘 | `retire_board()`;随后 `install_board(b_flag)` | 全动作拒绝(phase;无板态)、事件分段边界确立(跨段断言禁止——防前图泄漏);重装后初始展开按新板重算、无前板残留(旗/小人位/已开集) | 规则 3 / EC11 | +| AC-18 | `b_fog` 基线事件流 E₀ 已捕获 | 测试通道换根加载变异表(B3 AC-15 换根模式)后重放同一意图序列(新 A3 实例+新 install) | 事件流与 E₀ **逐位相同**(含 variant_ids)——生成期快照非活引用;A2 零表读取(AC-22③)使抗重载为构造性,本用例为行为级代理 | A2×4④ / A4 EC8 | +| AC-19 | 同坐标规格独立构造的 `b_fog` ×2(各配全新 A3 实例) | 同一定长意图序列驱动两遍(含挖/链/雾链/插拔旗/移动/多意图批/停手收轮——覆盖三 channel、flag_judged 与批次仲裁) | 两遍事件捕获列表**逐位相同**(数、序、全字段) | 规则 1 / D2 Rule 5 的 A2 层 | +| AC-20 | (`b_fog`, `b_fog_bare`) 同构对(同雷布同 digit、仅 variant 标记差异) | 同一意图序列各驱动一遍 | 两事件流**除 variant_ids 外逐位相同**(差分容许域 = 该字段且仅该字段)——连锁/清图/旗判定/价目/收轮全不感知变异 | A4 Rule 4 零特判 / 规则 1 / A2×4③ 反向证成 | +| AC-21 | `b_flag` + debug override `action_economy.flag_place_ap_cost=2`(D2 R3 域内值;承载声明:默认价目下资源面分支不可构造,同 A3 AC-7)构造 ap=1 | `try_flag_place`(棋盘面合法)→ `try_dig`(非相邻)→ `try_dig((1,4))`(有旗格)→ Waiting 相位任意动作 → `try_dig((-1,-1))`(越界,目录外用例) | rejected+**resource_face**、全态不变;**board_face + sub_reason=not_adjacent**;**board_face + sub_reason=flag_present**("须先拔旗"与"非相邻"数据可分);**phase**——三源互异可判定;目录外:rejected+board_face + **sub_reason=out_of_catalog** + 诊断收集器恰一条 debug(非静默吞) | 规则 5 / EC9 / A3 EC2 对侧 | +| AC-22 | A2 文件集(规则引擎/事件载荷/冒烟脚本/夹具规格) | 静态扫描 | ① 零 `randomize()`、零自建 RNG 构造(**tests/ 豁免不启用**——A2 确定性回放不需种子,严于 A1 先例);② 零 ConfigDB 外 data 表加载(Board class_name 直引豁免);③ **零 `ConfigDB.*` 属性读取**(A2 全部输入经 A3 账本与 A1 产物——设计决策;实现期 ADR 若裁决需读表,走接口漂移修订本条);④ 零 `extends Control/Node2D`、零 `.tscn` preload;⑤ AC-1 在 `godot --headless --script` 实跑通过(退出码 0 + 断言收集器零失败)作为本项判定(Rule 7);⑥ 纳入 D2 AC-14 项目级人工三要素字面量扫描(warning 级 + 人工复核,成员清单随实现建立并入——B3 AC-19 同款) | 规则 1 / D2 Rule 1/2/5/7 | + +### 覆盖矩阵(验收评审用) +| 类别 | 映射 | +|---|---| +| 规则 | 1→AC-19/20/22 · 2→AC-4/6/7/8/9(payload 前言钉死)· 3→AC-1b/2/17/18 · 4→AC-1/3(A3 EC5 委派 + 双包含)· 5→AC-4/11/21 · 6→AC-5/AC-6 · 7→AC-14 · 8→AC-11/12/13 · 9→AC-4 等价护栏/AC-9/10 · 10→AC-7/8 · 11→AC-15 · 12(无 chord 负空间)→API 封闭枚举 | +| Formulas | F1→AC-1②/5/7 · F2→AC-5/AC-6 · F3→AC-1⑤/3 · F4→AC-10 | +| EC | EC1→AC-1b · EC2→AC-2 · EC3→AC-11 · EC4→AC-6 · EC5→AC-12 · EC6→AC-10 · EC7→AC-7 用例 1 · EC8→AC-18 · EC9→AC-21 目录外用例 · EC10→AC-15 · EC11→AC-13/17 · EC12→AC-16 · EC13→AC-10c/d · EC14→AC-14 四对 · EC15→AC-10e · EC16→AC-16 · EC17→AC-5 分支 2 | +| 义务 | A2×4①→AC-6 · ②→AC-12 · ③→AC-6/AC-18/AC-20 · ④→AC-18 · 双包含①→AC-3 · ②→AC-3 · A3 EC4 deferral→AC-10 · 工程债→AC-15 · OQ1 在位→推演件开放问题区 | +| 规模 | 23 条 = AC-1…AC-22 + AC-1b(原"EC4 腿"编号为 AC-6,EC17 由 AC-5 分支 2 承载) | + +## 版本历史 +| 版本 | 日期 | 改动 | +|---|---|---| +| v2.0 | 2026-09-14 | 两件套迁移:按模板 v2 重排为 design 结论件(设计目的/系统范围/核心循环/领域章节×6/依赖与输出/P0 闭环/P1·P2 后置/调参表/验收标准),公式推导论证、候选取舍、体验锚点论证、已裁决争议、义务审计台账与全部 OQ 移至推演件 `design/gdd/analysis/a2-minesweeper-rules.md`;规则、数值、AC/EC/F/OQ 编号与内容零变更 | +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/边界情况/AC/义务台账/覆盖矩阵/开放悬案转表格,流程转字符画;F1–F4 公式本体、Variables 与验算数据全保留;术语统一以 voided 行文;语义与 2026-09-09 定稿及 2026-09-12 D4 修订对齐,零规则变更 | +| D4 修订 | 2026-09-12 | install_board 受理域扩 Active·仅弃图刷新路径:B1 `refresh_board` 编排内、A3 扣费成功为前提;其余 Active 装板仍拒绝(防轮内换板原则对非刷新路径不变),EC16 随之收窄 | +| v1 | 2026-09-09 | full /design-review 定稿 Approved:game-designer/systems-designer/qa-lead/ux-designer 四专员 + CD 终审六 blocking 同日修订核销、用户拍板放行(A3/A4/A1/B3 先例);systems-designer 起草 F1–F4 + 反例 B 阻断缺口(登记 A1 修订义务)、qa-lead 起草 23 条 AC(AC-1…22 + AC-1b + 覆盖矩阵 + 夹具坐标规格);本次定稿含:初始展开 R₀ 恰等 / 批次语义序仲裁 / 移动瞬移语义 / install 四不变式 / sub_reason 封闭枚举(UX-1)/ 连锁不越旗(GD-1)/ 雷格通行有价捷径(GD-2)/ 到达序无关性收窄 / 联合清图让位条款 / D1 强制项 ×1→×6 / B1 强制项 ×3 / spawn 坐标修正(b_chain、b_fog=(3,2))/ 已引爆雷格可通行(工程债);评审日志 design/gdd/reviews/a2-minesweeper-rules-review-log.md | diff --git a/design/gdd/a3-action-economy.md b/design/gdd/a3-action-economy.md new file mode 100644 index 00000000..564ba159 --- /dev/null +++ b/design/gdd/a3-action-economy.md @@ -0,0 +1,455 @@ +# A3 行动经济(Action Economy) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents(数值继承原型 v2/v3 实测结论与 game-concept 定稿) +> **承载支柱**:P1 主(每轮取舍的节拍器)· P2 次(规划的延迟兑现)· P3(旗稀缺性→AP 机会成本)· P4(移动免费的氛围归属) +> **层级**:Layer 1 Foundation · Milestone MVP · 设计顺序第 2 位(上游 D2 已 Approved) +> **一句话**:A3 是玩家每轮的行动预算账本——每轮固定 AP、统一价目、停手 1:1 转存 RAP / 清图奖励两条出口,全部收益以战斗效果直兑进轮次结算,自身不产生、不折算任何抽象资源点。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 玩家每轮行动预算与支出规则的账本——扫雷侧每个决策都花 AP,所有收益以战斗效果(安全格→主角 buff、标中旗→怪物 debuff、踩雷→敌方增益)直兑进轮次结算;纯资源账本,零随机、零表现面 | +| 拥有 | AP 资源账本独占(`try_spend` 唯一扣减入口)· 统一价目 · 三结束路径仲裁(cleared > stopped > ap_exhausted)· RAP 池生命周期 · `last_round_end` 记录 · F1 五条款下游契约 · ActionEconomyTable 回填 owner | +| 不拥有 | 棋盘面规则与执行(A2)· 轮次编排与结算(B1)· 奖励内容与档位(B4)· 数值表本体(D2 承载,A3 回填)· 呈现(D1)· 教学(D5) | +| 上游输入 | D2 ActionEconomyTable(ConfigDB 读取)· A2 的 try_spend 调用与动作参数上报(chain_size / clears_map)· B1 信号(notify_round_start / next_round_mode / battle_over)· 玩家停手与刷新输入(经 D1 / B1 编排) | +| 下游输出 | round_end `(path_tag, continuation_hint)`(棋盘路径经 A2 外发、stopped 由 A3 直发)· `last_round_end` 只读记录 · F1 五条款(B1 验收)· AP/RAP HUD 数值(D1)· 教学口径(D5) | +| 硬约束 | 零随机 · 零玩法数值字面量(D2 Rule 1)· 8 API 全 bool(受理/拒绝,拒绝附 debug 诊断、非进程致命)· 单一事务原子仲裁、禁双重折算 · Core 层零 UI 依赖(`--headless --script` 冒烟可完整驱动) | + +A3 是玩家 agency 的计量器:AP 不减少总点击量,而是把点击摊销进多个轮次并插入结算节拍;真正的决策压缩器是连锁展开(1 次点击兑现一片区域)与停手(放弃剩余格子换风险退出权)。没有 A3,挖/旗/停手就没有统一成本语言;执行归 A2、兑付归 B1、显示归 D1。 + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 "我算对了"的节拍器 | "推理免费,兑现花 AP" | AP 把推理的兑现切进轮次节拍——取舍落点在构成选择(确定动作供给 >8 时先兑现哪些)与轮末尾巴(赌不确定格 vs 停手攒 RAP);供给 ≤8 时确定动作可近乎无代价延后(棋盘跨轮存续);连锁免费是推理放大器——一次正确推断被免费兑现成一整片区域;终局碎片区的零风险收尾可能跨轮完成,是节奏阀的有意代价;决策密度由 A1 供给兜底、监测归 OQ3 | +| P2 "规划的延迟兑现" | "收手换退出权" | 每个安全格的 buff 当场入库、结算明细可见(B1 归因标注);停手改产风险退出权——面对纯盲猜,攒 RAP 换一张新图是"我不赌"的体面出口 | +| P3 "风险与收益同源" | "承诺有重量" | 三向效果映射使同源原则具象——标中旗削弱敌人、踩雷强化敌人、挖安全格强化自己;旗无数量上限,稀缺性由 AP 机会成本承载(8 向相邻限制约束的是当轮几何而非总量——跨轮小人可走遍全图;连锁富集区插旗成本放大、死 RAP 态碎片区趋零,条件性对冲见 OQ2);挖已插旗格须先拔旗(EC11) | +| P4 "地牢氛围" | "边缘探索免费" | 小人移动免费、挖/旗限 8 向相邻——服务"边缘探索"的地牢感而非经济杠杆,移动成本不是调参对象;点击摩擦护栏归 A2/D1 观测(每消耗 1 AP ≤ 2 次点击) | + +参考感受:预算把节奏从"点完为止"改为"每轮取舍";价目均匀分布是有意的 MVP 简化,异质性钩子(地形成本修饰)已为 A4 预留(Rule 2);无 chord(双击快开)是有意偏离经典扫雷——保护"每格兑现都花 AP"的预算意义,正确标雷缺一键兑现节拍已登记 CD 创意风险 #3(与 #2 高光缺失合并归 B1/B4)。 + +## 全局流程 + +### 轮次生命周期(AP 支出与轮次状态流转) + +``` +notify_round_start() [B1 编排;战斗首轮要求当前 Idle] + │ RoundStart(瞬态):AP←8 硬重置 · RAP 不动(跨轮保留) + ▼ +Active(挖掘期) + │ request_dig / request_flag_place / request_flag_pull ──┐ + │ request_stop ──────────────────────────────────────────┤ 各自进入 try_spend(action_kind) + │ request_refresh ───────────────────────────────────────┘ 单一原子事务:余量检查·扣减·结束仲裁 + │ │ + │ ├─ request_refresh 且 rap ≥ rap_refresh_cost 且未达每轮上限 + │ │ → 扣费 · 当场换新图继续挖(AP 不重置 · 无清图奖励 · 轮次不终结) + │ ▼ + │ 结束路径胜出(同帧/同批次仲裁:cleared > stopped > ap_exhausted) + ▼ +RoundEnd(瞬态) + │ stopped → RAP += remaining_ap(1:1) + │ ap_exhausted → 断言 remaining_ap = 0(违反即致命) + │ cleared → 剩余 AP 清零(不入 RAP)· 清图奖励(内容归 B4) + │ 写 last_round_end(单槽只读记录) + ▼ +Waiting + │ next_round_mode = same_board → RoundStart(同图续轮) + │ next_round_mode = new_board → RoundStart(新图;仅 cleared 轮;RAP 保留) + │ battle_over → Idle(RAP 清零 · ap=0;下一场战斗由 B1 发 round_start 重启) +``` + +### 出口分发 + +| 出口 | path_tag | continuation_hint | 剩余 AP / RAP | 换图 | +|---|---|---|---|---| +| 清图 | cleared | new_board | 剩余 AP 清零(不入 RAP)+ 清图奖励归 B4 | 下轮新图 | +| 停手 | stopped | same_board | RAP += remaining_ap(1:1,无乘数) | 同图续轮 | +| 耗尽 | ap_exhausted | same_board | 恒 0(不变式断言) | 同图续轮 | +| 挖掘期刷新 | 无 round_end——轮次不终结 | — | −rap_refresh_cost(每轮 ≤ refresh_cap_per_round) | 当场换图(无清图奖励;旧板旗失效) | +| 战斗结束 | — | battle_over 信号 | RAP 清零 | → Idle | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:新一轮开始行动点重置发满,上轮剩余清零、不结转 +**人话**:这条管每轮的资源发牌,由结算编排方开轮触发。轮次开始的一瞬,A3 把行动点写满 8(表值 `ap_per_round`)——上一轮用剩多少都清零,不累积、不结转,随后进入挖掘期。唯一跨轮带走的资源是 RAP 池——战斗域里停手攒下的换图资金,开轮时原样保留、不做任何加减。 + +**举证**:无计算(AP 数值读配置表)。字段 `ap_per_round` 见「Tuning Knobs」节 ActionEconomyTable schema;RAP 跨轮保留细则见下方「规则 11 的实现参照——RAP 池规则」;开轮受理与重置断言见 AC-1a/AC-1b。 + +#### 规则 2:挖、插旗、拔旗都按同一张价目表扣行动点 +**人话**:这条管"每个动作花多少":A3 定价、A2 执行,是全项目唯一的消耗口径。玩家在挖掘期的三件付费动作——挖开格、插旗、拔旗——各花 1 行动点,且只能作用于小人 8 向相邻的格子;移动小人与连锁展开免费(逐动作价目见下方实现参照表)。经典扫雷的"双击快开"(chord)被有意去掉——每格兑现都必须花行动点,保护预算的决策意义,由 A2 承载。 + +**举证**:无计算(价目数值全部来自配置表)。逐动作价目见下方「规则 2 的实现参照——统一价目表」;成本字段与默认值见「Tuning Knobs」节;挪旗合计 2 行动点的边界见 EC3/EC7;无 chord 的取舍依据见「体验锚点」节末参考感受行。 + +### 规则 2 的实现参照——统一价目表 + +| 动作 | 消耗 | 要点 | +|---|---|---| +| 挖开格 | `dig_ap_cost` = 1 AP | 仅限小人 8 向相邻格;若触发连锁展开(flood fill),连锁部分免费(原型 v2 定稿:推理的放大器),连锁开的格计入清图进度 | +| 插旗 | `flag_place_ap_cost` = 1 AP | 仅限相邻格(原型 v3 统一行动经济);无旗子实物资源——旗为棋盘标记,数量无上限 | +| 拔旗 | `flag_pull_ap_cost` = 1 AP | 仅限相邻格(与挖/插对称);移除棋盘标记、无返还物(无口袋旗可返);OQ2 挂原型补丁复核 | +| 移动小人 | 0(冻结) | 点击已开格移动,BFS 限已探明区(A2 承载);免费服务 P4 探索氛围,非经济杠杆、调参豁免 | +| 连锁展开 | 0(冻结) | 免费;连锁开的格计入清图进度 | + +#### 规则 3:行动点用到 0 的瞬间轮次就结束,几种结束同时发生时清图优先 +**人话**:这条管"轮次什么时候算完、几种结束方式打架时听谁的"。行动点扣到 0 的那一瞬间,轮次立即以"耗尽"结束——不存在 0 行动点还能操作的窗口。每个动作都在账本事务内结算并判定一次结束;多种结束方式同时成立时(比如最后 1 行动点挖开最后一块安全格),按固定优先级只裁出一个赢家——与哪种情况先到达无关,也禁止把同一份剩余行动点按两条路径重复折算。 + +| 同时成立时 | 优先级 | 胜出后发生什么 | +|---|---|---| +| 清图(cleared) | 最高 | 剩余行动点清零 + 清图奖励,下轮换新图 | +| 停手(stopped) | 居中 | 剩余行动点 1:1 存入 RAP 池,下轮同图 | +| 行动点耗尽(ap_exhausted) | 最低 | 剩余行动点恒为 0(实现层断言,违反即致命),下轮同图 | + +**举证**:无计算(优先级为固定常量序 `cleared > stopped > ap_exhausted`,单事务一次判定)。同帧/同批次多路径竞争的仲裁语义见 EC4("先停后挖"腿 deferral 至 A2 集成测试);最后 1 行动点同时清图与耗尽时清图胜出见 EC10;验收 AC-4/AC-4b/AC-8。 + +#### 规则 4:玩家随时可以主动停手,剩余行动点原值存进 RAP 池 +**人话**:这条管玩家的提前收工:挖掘期任意时刻,玩家可以直接停手——输入层直调 A3,不经棋盘。停手立即结束本轮(结束方式标记 `stopped`),剩余行动点按 1:1 原值转存 RAP 池,没有乘数加成;停手不是战斗收入流,是纯风险管理工具——"这轮我不想赌"的体面出口。停手时图没开完也没关系,下一轮继续在同一张图上挖。 + +**举证**:无计算(1:1 转存,无乘数、无公式)。停手与挖掘期刷新的逐维差异见下方「规则 4 与规则 11 的实现参照——停手与挖掘期刷新对照」;满 AP 停手恰可达一次刷新的基线节拍及其退化环分析见 EC6;验收 AC-5/AC-15。 + +#### 规则 5:安全格全部开完即清图——剩余行动点清零,另发清图奖励 +**人话**:这条管"把图开穿"的时刻:全部安全格开完(含连锁展开开的格),轮次以"清图"结束——剩余行动点直接清零,不折算、不进 RAP 池;同时发放清图奖励。奖励发什么、发多少归 B4 定,且是差异化插槽——B4 的奖励表须预留按图类型(A1 的 biome/tag 上下文)分支的结构位;A3 只上报"清图了"这一事实,不拥有奖励内容、不感知图类型。清图后下一轮换一张新图。 + +**举证**:无本地计算——奖励当量归 B4 定档,量级锚与下界见「数值结论与推导」节 F1 条款 4;奖励差异化插槽的上下文约束见 OQ6;连锁开完全图同样触发清图见 EC5;验收 AC-6/AC-6b。 + +#### 规则 6:轮次怎么结束只向外报一条消息,结算里不夹带任何折算数值 +**人话**:这条管轮次结束时对外的"发言":三种结束路径都只向结算编排方(B1)输出一条两字段消息——"怎么结束的 + 下轮用不用换图的提示"。这只是编排提示;权威的换图指令是 B1 之后回发的 `next_round_mode`。除此之外,消息不夹带任何折算数值——进结算明细的收益全部是具体战斗效果(主角 buff / 怪物 debuff / 清图奖励),来源归因标注由 B1 承担。 + +| 结束方式 | 发出的换图提示 | B1 权威回发(next_round_mode) | +|---|---|---| +| 清图 | new_board(建议换新图) | new_board——仅清图轮可换新图 | +| 停手 | same_board(建议同图续轮) | 恒 same_board(回发违约即 fail-fast) | +| 行动点耗尽 | same_board(建议同图续轮) | 恒 same_board(回发违约即 fail-fast) | + +**举证**:无计算。字段取值与产出点见「枚举」节 path_tag / continuation_hint 行;权威回发信号的消费位置见规则 9 与「状态」节 Waiting 行;结算明细归因标注义务见「呈现与 UI 契约」节第 5 条;验收 AC-4b/AC-5/AC-6。 + +#### 规则 7:全部数值存在配置表里,代码不写死一个数、不掷一次骰子 +**人话**:这条管数字的存放纪律:每轮发多少行动点、每个动作花多少、刷新门槛多高、每轮能刷几次——所有玩法数值都存在 D2 的 `ActionEconomyTable` 配置表里,经 ConfigDB 类型化读取。A3 的代码零玩法数值字面量:改数值=改表回填,不动代码;表不变,行为逐位相同。本系统也不涉及任何随机数——没有 RNG 流。 + +**举证**:无计算(本条是存放纪律,非数值规则)。字段清单、类型域与默认值见「Tuning Knobs」节 ActionEconomyTable schema;零字面量纪律依据 D2 Rule 1;零随机的行为级验收(同输入序列重放逐位相同)见 AC-17;表值与 registry 一致性见 AC-10a。 + +#### 规则 8:旗是棋盘上的标记不是背包道具,每颗雷的旗收益只能兑一次 +**人话**:这条管旗子的形态与兑付资格:旗没有"口袋/背包"这类实物资源——插上去就是棋盘上的一面标记,归棋盘系统(A2)所有;跨轮保留、随图存续,清图或刷新换图时随之消失。数量无上限,稀缺性全由"插旗要花行动点"承载:插旗=1 行动点+棋盘多一面标记,拔旗反向(标记移除)。标中旗的收益兑付有五条硬约束: + +| 兑付硬约束 | 意思 | +|---|---| +| 兑付一次性 | 每颗雷在本次战斗内至多兑付一次,资格随首次兑付消费 | +| 当轮结算 | 旗的放置实例只是当轮结算的兑付载体,不囤积到以后 | +| 资格以雷格实例为键 | 认雷不认旗——拔旗重插同一雷格,不恢复已消费的资格(堵死每轮 2 行动点的重插农场) | +| 禁止逐轮重复兑付 | 同一颗雷的收益不会逐轮反复发 | +| 换图资格重置 | 换图后新图的雷是新实例,资格自然重置 | + +**举证**:无计算。兑付硬约束的定稿依据见 OQ5(开放悬案节第 5 条);挖已插旗格须先拔旗见 EC11;同轮插了又拔、挪旗的消耗见 EC3/EC7;旧板旗随挖掘期刷新失效(零兑付)见下方「规则 11 的实现参照——RAP 池规则」"旧板旗"行;验收 AC-3a/AC-3b/AC-16。 + +#### 规则 9:扣行动点只认账本一个入口,"轮次结束"消息有两个合法发送者 +**人话**:这条管"谁有权扣行动点、谁有权宣布轮次结束"。全项目唯一的扣减入口是 A3 的账本接口——余量检查、扣减、结束仲裁在同一次事务内原子完成。棋盘系统(A2)只判棋盘面合法性(相邻、目标格状态、无双击快开)并执行棋盘效果:它是挖/插/拔的调用方,必须经账本扣费,自己无权扣资源。"轮次结束"消息有两个合法发射源,逐项见下表。 + +| 事项 | 责任方 | 运作方式 | +|---|---|---| +| 棋盘路径外发(耗尽 / 清图) | A2 | 账本扣费成功且本次事务内轮次结束时,A2 读取 `last_round_end` 组包外发——该读取权是接口契约的一部分,非仅测试设施 | +| 停手路径直发(stopped) | A3 | 停手是非棋盘输入,由输入层直调 A3,A3 仲裁后自行外发,payload 与 A2 路径同构——纯停手轮不存在"无发射者"漏洞 | +| `last_round_end` 只读记录 | A3 | 单槽只读记录;测试/审计断言落点,同时是 A2 组包 round_end payload 的合法数据源 | +| Waiting 出口 | A3 | 消费 B1 回发的 `next_round_mode ∈ {same_board, new_board}` 与 `battle_over` 信号 | + +**举证**:无计算。唯一扣减入口签名 `try_spend(action_kind) -> bool` 与事务边界见「全局流程」节轮次生命周期字符画与「状态」节状态机;账本资源面拒绝(AP 不足)见 EC2;Waiting 相位非法拒绝见 AC-9;停手 payload 同构与"恰一条输出"验收见 AC-5/AC-8;同值双发射不可区分的测试口径见「验收标准」节测试口径表 last_round_end 单槽行。 + +#### 规则 10:扫雷收益全部直接兑成战斗效果,没有中间货币 +**人话**:这条管"扫雷赚的东西以什么形式到手":全部收益——挖到安全格给主角上 buff、标中旗给怪物上 debuff、踩雷给敌方上增益、清图发奖励——都以战斗效果直兑,直接进轮次结算明细;不存在抽象点数、不存在折算乘数,折算公式根本不存在。这套对账契约共五条款,约束效果侧(结算系统),登记为 B1 GDD 验收项。 + +**举证**:五条款全文(单一当量对照 / 标中旗锚 / 挖掘风险不等式 / 清图奖励 / 弃图支配性)、推导与公式见「数值结论与推导」节 F1;playtest 复核归 OQ3、当量归一归 OQ1;systems-index 门禁 ①–⑤ 登记。 + +#### 规则 11:RAP 池只从停手进账、只为换新图花钱,战斗结束就清零 +**人话**:这条管"停手攒下来的钱":RAP 池的唯一进账是停手时剩余行动点的 1:1 转存;它属于整场战斗——战斗内跨轮次、跨扫雷图一直保留,战斗结束(进入 Idle)立刻清零。唯一花法是挖掘期换一张新图继续挖:玩家自己决定、经刷新入口发起、账本受理,每轮最多刷 2 次,禁止"攒满就自动刷"。RAP 不折算、不兑付任何战斗收益——纯风险退出资源。 + +**举证**:无独立公式(进账 1:1、扣费门槛与每轮上限均为配置表值/常量)。逐维细则、默认值与边界见下方「规则 11 的实现参照——RAP 池规则」;停手与刷新的逐维对照见下方「规则 4 与规则 11 的实现参照——停手与挖掘期刷新对照」;RAP 不足拒绝见 EC13、超限拒绝见 EC15、战斗末清零可见反馈见「呈现与 UI 契约」节第 3 条;验收 AC-5b/AC-11/AC-15。 + +### 规则 11 的实现参照——RAP 池规则 + +| 维度 | 细则 | +|---|---| +| 来源 | 唯一来源 = 停手转存(1:1,无乘数);清图与 ap_exhausted 的剩余 AP 均不入 RAP | +| 生命周期 | 战斗域:战斗内跨轮次、跨扫雷图累积;`battle_over` 进入 Idle 时清零(每场战斗清零;清零的可见反馈与教学落点见呈现与 UI 契约——防"攒的钱凭空没了"读感);MVP 无 RAP 上限;跨战斗携带为未来选项,若开放须修订本条并登记 D3/C2 持久化归属(OQ7) | +| 支出 | 唯一支出 = 刷新雷图;决策主体 = 玩家——经 D1 刷新入口在挖掘期(Active 相位)发起,B1 `refresh_board` 挖掘期刷新编排内调用 `request_refresh() -> bool`;禁止"RAP 满即自动刷" | +| 受理与换图 | `rap ≥ rap_refresh_cost`(默认 8)时扣减并当场换新图继续挖——AP 不重置、本轮剩余 AP 保留(A2 装板受理域已同步:Active·仅刷新路径);刷新获得的新图不产生清图奖励(它不是清图);不足则拒绝、状态不变(EC13) | +| 每轮上限 | `refresh_cap_per_round`(默认 2)——轮内计数、RoundStart 重置,超限拒绝(EC15);无冷却;每次独立检查 `rap ≥ rap_refresh_cost` 并扣减 | +| 旧板旗 | 随旧板失效——零兑付、旗 AP 不返还(与拔旗不返还同族;A2 换图全量重置承载) | +| 经济注记 | 刷新价格实际 = rap_refresh_cost + 每轮 2 次上限;挖掘期当场刷新无结算发生——敌方每轮照常结算的时间税不构成刷新本身的隐性成本;OQ3 弃图率/刷新率预警阈值联动复核 | +| 收益 | 无战斗收益——RAP 不折算、不兑付任何战斗效果,纯风险退出资源 | + +### 规则 4 与规则 11 的实现参照——停手与挖掘期刷新对照 + +| 维度 | 停手(规则 4) | 挖掘期刷新(规则 11) | +|---|---|---| +| 时点 | Active 任意时刻 | Active(挖掘期) | +| 发起 | 玩家输入层直调 A3 | 玩家经 D1 发起,B1 `refresh_board` 编排内调用 | +| 轮次 | 结束(path_tag=stopped),本图未开完 → 下轮同图 | 不终结——当场换新图继续挖 | +| 资源 | RAP += remaining_ap(1:1) | RAP −= rap_refresh_cost(每轮 ≤ 2 次) | +| 收益 | 风险退出权(无战斗收益) | 新图(无清图奖励;旧板旗失效零兑付) | + +## 枚举 + +### 值域封闭枚举(path_tag / 信号 / 动作类型 / 相位) + +| 值 | 字段 | 产出点 | 语义 | +|---|---|---|---| +| ap_exhausted | path_tag | AP 归 0 瞬间(try_spend 事务内,A2 外发) | 轮次耗尽;continuation_hint 恒 same_board;不变式 remaining_ap=0 | +| cleared | path_tag | 全部安全格开完(含连锁格;A2 外发) | 清图;恒 new_board;剩余 AP 清零 + 清图奖励(B4) | +| stopped | path_tag | 玩家主动停手(A3 直发) | 提前结束;恒 same_board;RAP += remaining_ap(1:1) | +| same_board | continuation_hint / next_round_mode | stopped/ap_exhausted 轮输出;B1 回发 | 下轮继续同图(stopped/ap_exhausted 轮恒 same_board——B1 回发违约 fail-fast) | +| new_board | continuation_hint / next_round_mode | cleared 轮输出;B1 回发 | 下轮换新图(A1 经 B1 编排生成) | +| battle_over | B1 → A3 信号 | 战斗终结 | → Idle,RAP 清零、ap=0 | +| dig / flag_place / flag_pull | action_kind(try_spend 参数) | A2 棋盘面判定通过后 | 账本扣减的三种动作;move/chain 0 成本不经账本(A2 承载) | +| Idle / Active / Waiting | phase(只读观测量) | 资源状态机 | 可观测相位域全集;RoundStart/RoundEnd 为事务内瞬态、不可观测 | + +### 对外 API 面(封闭枚举,8 入口全 bool——新增入口 = 接口漂移) + +| API | 签名 | 语义 | +|---|---|---| +| notify_round_start | `notify_round_start() -> bool` | 开轮(B1 编排) | +| request_dig | `request_dig(chain_size: int, clears_map: bool) -> bool` | 挖掘;chain_size = 连锁附加格数(不含首格,≥0;首格恒耗 1 AP);clears_map = 本次动作后全图安全格开完 | +| request_flag_place | `request_flag_place() -> bool` | 插旗 | +| request_flag_pull | `request_flag_pull() -> bool` | 拔旗 | +| request_stop | `request_stop() -> bool` | 停手(A3 直发 stopped round_end) | +| request_refresh | `request_refresh() -> bool` | 挖掘期刷新(Rule 11) | +| notify_settlement_done | `notify_settlement_done(next_round_mode) -> bool` | 记录编排意图(phase 停留 Waiting) | +| notify_combat_end | `notify_combat_end() -> bool` | 战斗终结 → Idle、RAP 清零 | + +## 数值结论与推导 + +### F1 下游经济契约(downstream_effect_contract) + +**结论**:全部扫雷收益以战斗效果直兑(安全格→主角 buff、标中旗→怪物 debuff、踩雷→敌方增益),无抽象点数、无折算乘数——折算公式不存在;契约为对效果侧的五条款,登记为 B1 GDD 验收项(systems-index 门禁 ①–⑤);playtest 复核归 OQ3,当量归一归 OQ1。 + +#### 推导 + +| # | 条款 | 内容 | 闭合 owner | +|---|---|---|---| +| 1 | 单一当量对照条款 | B1 须出效果档位对照表(1 最低档安全格 buff = ? 当量;debuff/清图奖励/敌方增益均对照归一),对全部收益来源单一换算——分源改率 = 契约失效,须回本 GDD 重议;归 OQ1(注意与 OQ6 清图奖励插槽区分) | B1 | +| 2 | 标中旗锚条款 | 标中旗的怪物 debuff 档位当量必须严格大于最低档安全格 buff 当量——否则插旗在最优情形下也只是打平,P3 旗循环经济空心;且对照基准须为连锁放大的边际挖掘收益(单 AP 平均挖掘当量,含连锁期望放大)——只对最低档比较会使旗理性萎缩到碎片区;每战标中旗总吞吐(N 旗 = N debuff)无单档上限可依恃,由 B1 按条款 1 对照表对账;插旗挤占清图进度的边际轮成本由 B4 定档时核算,或在其 GDD 声明 MVP 忽略并给理由 | B4 表设计 + B1 对照验收 | +| 3 | 挖掘风险条款 | 挖掘决策 EV 健康不等式(见公式):两端分别锚定 A1 可推理率生成参数(P1 下限,决定 P(安全) 的玩家可达上界)与 B2 惩罚上限表值(上限值登记为 B2 GDD 验收项);B1 为单一闭合对账 owner(A1/B2 供给锚值,B1 签字验收),登记为 B1 GDD 强制检查点;P1 支柱要求 A1 可推理率使玩家能推高 P(安全)——本条款是"可推理率"的经济侧验收形态(可判定谓词) | B1 | +| 4 | 清图奖励条款 | 清图奖励 > 0,量级与差异化分支归 B4(输入含 A1 图类型上下文) | B4 | +| 5 | 清图 vs 弃图刷新支配性条款 | 清图奖励当量下界须使"推到底清图"的期望当量 ≥ "确定格挖完即弃图刷新"的期望当量(见公式)——否则每图最优解退化为"捞完确定格就弃",清图高光节拍、B4 差异化插槽、game-concept"图完成"循环全部空心;该环强度与 A1 可推理率反向耦合——可推理率越低(迷雾/终局 50/50 越多),弃图越优,玩家对 P1 失败的理性回应会反过来饿死清图内容,故本条款是 P1 失败模式的经济对冲,与条款 3 共同登记 B1 强制检查点 | B1 | + +``` +条款 3(挖掘决策 EV 健康不等式): + P(安全) × V_safe_lb > P(雷) × V_mine_ub + P(安全) / P(雷) —— 挖掘目标格为安全格 / 为雷的概率 + V_safe_lb —— 最低档安全格 buff 当量下界 + V_mine_ub —— 敌方增益当量上界 + +条款 5(清图 vs 弃图刷新支配性): + C ≥ 弃图所节省的期望雷险当量 + Δ轮数 × 敌方结算强度当量 + C —— 清图奖励当量(B4,条款 4) + inputs —— B4 的 C、B2 惩罚上限、A1 雷密度/可推理率、B1 结算强度(B1 闭合对账) +``` + +**Variables:** + +| 变量 | 含义 | 锚定来源 | +|---|---|---| +| P(安全) / P(雷) | 挖掘目标格为安全格 / 为雷的概率 | A1 可推理率生成参数(P1 下限——决定 P(安全) 的玩家可达上界) | +| V_safe_lb | 最低档安全格 buff 当量下界 | B1 条款 1 效果档位对照表 | +| V_mine_ub | 敌方增益当量上界 | B2 惩罚上限表值(登记为 B2 GDD 验收项) | +| C | 清图奖励当量 | B4(条款 4 差异化插槽) | +| Δ轮数 | "推到底清图"与"确定格挖完即弃图刷新"的轮数差 | A1 雷密度/可推理率 × B1 结算强度 | + +| 验算项 | provisional 值 | 刷新时点 | +|---|---|---| +| 条款 2 对照基准 | 下限 1:1(单 AP 平均挖掘当量,含连锁期望放大) | B4/B1 定稿校准 | +| 条款 3 目标带 | 原型临时值(伏击 +3 / 敌 18HP/4ATK)代默认表给出首版示例 | 随 B2 表回填刷新 | +| 条款 4 清图奖励 | +10 临时锚(B4 插槽的 provisional 参考) | 量级归 B4 | +| 条款 5 支配性 | +10 锚对默认 8 AP 图形况粗验通过 | 正式锚随 B4/B2/B1 表值定稿 | + +## 状态 + +轮次内资源状态机(A3 资源侧视图;A3 只暴露资源余量、`last_round_end` 并消费 B1 信号): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| RoundStart(瞬态) | `notify_round_start()`(B1 编排;战斗首轮要求当前 Idle) | AP←8(硬重置);RAP 不动(跨轮保留)→ Active | +| Active | 预算就绪 | 接受耗 AP 动作并经 `try_spend` 原子扣减;每个动作结算事务内按优先级仲裁(cleared > stopped > ap_exhausted)→ RoundEnd(胜出 path_tag);挖掘期刷新在此相位受理(Rule 11) | +| RoundEnd(瞬态) | 任一结束路径胜出 | 停手:RAP += remaining_ap(1:1);ap_exhausted:不变式断言 remaining_ap=0,违反即致命;清图:剩余 AP 清零。写 `last_round_end` → Waiting | +| Waiting | 结算完成前不接受 round_start(AC-14) | 消费 B1 信号:`next_round_mode=same_board` → 下轮 RoundStart(同图——stopped/ap_exhausted 轮恒 same_board);`next_round_mode=new_board` → 下轮 RoundStart(新图,A1 经 B1 编排生成——仅 cleared 轮;RAP 保留);`battle_over` → Idle(RAP 清零)。request_refresh 不在此相位受理(刷新决策在挖掘期,Rule 11) | +| Idle | 战斗终结 | 不响应任何动作请求;RAP=0、ap=0;下一场战斗由 B1 发 round_start 重启(唯一出口) | + +- **不变式**:可观测相位域 = {Idle, Active, Waiting}——RoundStart 与 RoundEnd 均为单事务内瞬态,不可观测、不作为任何测试 GIVEN;ap_exhausted 轮恒 remaining_ap=0;RAP 只经停手增加、只经刷新扣减与 battle_over 清零;编排权威序列以 B1 轮次生命周期编排章节为准。 + +### 输入 × 相位矩阵 + +全部 8 个 API 返回 bool;非法输入 = 拒绝(false)+ 状态不变 + debug 诊断——fail-fast 不静默吞编排错误,但不采用进程致命(进程致命保留给 D2 数据通道)。 + +| 输入 | Idle | Active | Waiting | +|---|---|---|---| +| `notify_round_start` | 受理(RoundStart) | 拒绝 | 拒绝(settlement 消费后受理——AC-14 门) | +| `request_dig` / `request_flag_place` / `request_flag_pull` / `request_stop` | 拒绝 | 受理(经 `try_spend`) | 拒绝 | +| `request_refresh` | 拒绝 | 受理(Rule 11——挖掘期当场换图) | 拒绝 | +| `notify_settlement_done` | 拒绝 | 拒绝 | 受理(重复调用拒绝) | +| `notify_combat_end` | 拒绝 | 拒绝 | 受理(→ Idle,RAP 清零) | + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 AP=0 后仍有动作请求 | 全部拒绝(返回 false)——轮次已在归 0 瞬间结束,不存在 0-AP 窗口 | Rule 3 | +| EC2 AP 不足以支付动作(含插旗/拔旗——旗无实物资源,拒绝只看 AP) | 拒绝、状态不变;`last_round_end` 不变 | Rule 9 / AC-7 | +| EC3 玩家拔旗再插他格(挪旗) | 合法——插+拔合计耗 2 AP(插 1 + 拔 1,默认值下);相对"直接改插他格"多耗的 1 AP(拔旗)即"防免费重排"的有意代价;旗标记的移除/重建为 A2 棋盘操作,A3 只见 AP −2 | Rule 2 / AC-16 | +| EC4 同帧/同批次多结束路径竞争 | 按 Rule 3 总仲裁 cleared > stopped > ap_exhausted,与到达顺序无关、单次判定;"先停后挖"腿的批次语义在 A3 冒烟 API 上不可构造(同步单调用无事务批次概念)——该腿显式 deferral 至 A2 集成测试承载(systems-index 门禁登记,非静默豁免);A3 冒烟承载"先挖后停"腿(AC-8) | Rule 3 / AC-8 | +| EC5 连锁展开开完全图 | 连锁免费;清图判定以"安全格全部开完"为准(含连锁开的格)→ 正常触发 cleared;RoundStart 的初始 3×3 自动展开同理(退化小图属既有行为,不设独立分支);退化图刷清图奖励的钓鱼风险登记为 A1/B1 下游约束——最小安全格数下限归 A1 生成参数验收;初始展开的定义落点(免费、计入清图进度、无奖励)归 A2 GDD 承载,本 GDD 仅经 `chain_size`/`clears_map` 参数引用其结果 | Rule 2 / AC-6b | +| EC6 满 AP 停手 | 合法——RAP +8(= rap_refresh_cost,恰好可达一刷,Rule 11 基线节拍,有意;该节拍与 OQ3"纯停手轮占比"预警同源,调参时联动评估)。诚实声明(形态限定版):① 纯停手零挖掘环(停→刷→停)零战斗进账,唯一代价是敌方每轮照常结算(时间税自惩)——该退化形态结构自惩成立;② 但理性玩家的真实风险环是"挖完全部确定格 → 不确定即停 → 攒满刷新":该环有真实战斗进账、零踩雷风险,刹车只有三个——清图奖励弃失(下界由 F1 条款 5 守住)、8 RAP 成本、时间税;本条不声称该环"病灶不存在",其健康性由 F1 条款 5(支配性不等式)+ OQ3 弃图率/完成度指标守门 | Rule 4 / F1 条款 5 / OQ3 | +| EC7 同轮内插旗后又拔旗 | AP 合计消耗 2(插 1 + 拔 1,默认值下);旗标记由 A2 移除,A3 无旗状态可返 | Rule 2 | +| EC8 战斗最后一轮的收益兑现 | 敌人已全灭时 B1 结算先判胜,当轮 buff/debuff/清图奖励是否兑现 = B1 判定次序域,A3 无断言义务(out-of-scope);A3 义务仅为每轮无条件输出一条 `(path_tag, continuation_hint)` | B1 域 | +| EC9 跨轮/跨图资源携带 | RAP 是唯一跨轮携带量(战斗域,跨图也保留);停手不是战斗资源机制——无复利问题;AP 腿每轮硬重置;旗标记随图存续(A2 域);RAP 战斗结束清零(AC-11) | Rule 1 / Rule 11 / AC-11 | +| EC10 最后 1 AP 挖开最后安全格 | cleared 与 ap_exhausted 同时成立 → 清图胜出:输出 `(cleared, new_board)`、剩余 AP 清零 + 清图奖励 + 换图,ap_exhausted 不触发——防"耗尽优先"实现把清图高光结算为最差结局、训练玩家永远留最后一格停手 | Rule 3 / AC-4b | +| EC11 挖已插旗的格 | 拒绝——须先拔旗(1 AP,默认值下)再挖;旗是"别挖这里"的承诺标记,挖旗格合法化会催生零风险滥用;误标改主意的代价 = 拔旗 1 AP(P3 承诺重量);棋盘面判定归 A2 | A2 域 / AC 前言 out-of-scope | +| EC12 RAP 生命周期边界 | MVP 无上限;战斗结束(Idle)强制清零;跨战斗携带若未来开放,须修订本条与 Rule 11 并登记 D3 持久化归属 + C2 冒险域语义(OQ7)——禁止静默超域 | Rule 11 / OQ7 | +| EC13 RAP 不足时请求刷新 | `request_refresh()` 返回 false、RAP 不变、相位不变(Active 相位内的资源不足拒绝;与 AC-14 的相位非法拒绝区分——后者附 debug 诊断,两者均为 bool 口径) | Rule 11 / AC-5b | +| EC14 清图/耗尽轮次产生剩余 AP | 清图剩余 AP 清零(不入 RAP,Rule 5);ap_exhausted 恒 remaining_ap=0(不变式断言);两路径 RAP 增量恒为 0(AC-4/AC-6 断言);不变式断言的实现载体为 debug 断言(冒烟无 fault-injection 载体;行为级代理 = AC-4 的 rap 不变断言) | Rule 5 / AC-4 / AC-6 | +| EC15 同一轮内多次请求刷新 | 受理至每轮上限 `refresh_cap_per_round`(默认 2)为止,超限拒绝(resource_face;轮内计数、RoundStart 重置);每次独立检查 `rap ≥ rap_refresh_cost` 并扣减;无冷却;每次刷新都当场换新图且不产生清图奖励(Rule 11);挖掘期当场刷新无结算发生——OQ3 联动复核 | Rule 11 / AC-5b | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | hard | ActionEconomyTable schema 与 ConfigDB 类型化读取;本 GDD 为该表回填 owner(回写台账见对账义务登记表) | +| A1 | 间接事实依赖 | 新图生成经 B1 编排调用——A3 不直接消费 A1 接口,仅 Waiting 出口消费其结果信号 | + +无其他玩法上游(Foundation 层)。 + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| A2 | hard | 账本调用(`try_spend`)+ 价目表执行 + round_end 棋盘路径外发(Rule 9,payload 组包读取 `last_round_end`);A2 校验棋盘面合法性,资源合法性归 A3;旗标记棋盘状态归 A2;EC4"先停后挖"批次仲裁 deferral 至 A2 集成测试(对账义务登记表) | +| B1 | hard | RoundEnd 输出 `(path_tag, continuation_hint)`;挖掘期刷新编排(`refresh_board`)内调用 `request_refresh()`(Rule 11);回发 `next_round_mode`/`battle_over`;F1 契约验收项(效果档位对照表单一线性换算) | +| B4 | soft | 清图奖励差异化插槽(按图类型分支);标中旗 debuff 档位(OQ5/F1 条款 2 锚约束) | +| D1 | soft | AP/RAP HUD 常显;刷新入口与 RAP 清零反馈;非法原因双源呈现 | +| D5 | soft | 教学口径来源(含 RAP/刷新/RAP 战斗末清零) | + +### 对账义务登记表 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | ActionEconomyTable 首轮回填——值侧四项 + registry 登记 | D2 | 已完成 | +| 2 | schema 全字段交付(十字段,含 move/chain 冻结字段) | D2 | 已完成 | +| 3 | RAP 结构 delta——四字段删除(flags_per_round / flag_unit_cost / stop_multiplier / clear_multiplier)+ `rap_refresh_cost` 新增;D2 Core Rules 表行/Status 行、AC-7 示例值、registry 同步回写 | D2 | 已完成 | +| 4 | config 结构位 `refresh_cap_per_round` 预留;D2/registry 字段回写随实现批次(届时 AC-10a 七字段清单同步扩为八字段) | D2 | 待实现批次 | +| 5 | F1 五条款验收——效果档位对照表/标中旗锚/挖掘风险不等式/清图奖励/弃图支配性(条款 3/5 为 B1 强制检查点;systems-index 门禁 ①–⑤) | B1(供给锚值:A1/B2/B4) | 已登记 | +| 6 | EC4"先停后挖"批次仲裁腿 deferral 至 A2 集成测试(A3 冒烟 API 不可构造;A3 冒烟承载"先挖后停"腿 AC-8) | A2 | 已登记(systems-index 门禁) | +| 7 | 退化图刷清图奖励的钓鱼风险——最小安全格数下限归 A1 生成参数验收;初始展开定义落点(免费、计入清图进度、无奖励)归 A2 GDD 承载 | A1/A2/B1 | 已登记(EC5) | +| 8 | 图类型上下文在战斗域内恒定(biome 挂关卡层、不逐图重掷——防 `request_refresh` 退化为 biome 重掷钓鱼口) | A1/B4 共同验收 | 已登记(OQ6) | +| 9 | 刷新入口(玩家发起)与 battle_over 时 RAP 清零的可见反馈 | D1 | 已登记(D1 GDD 验收项) | + +## 呈现与 UI 契约 + +A3 不拥有 UI,对外暴露的状态与反馈义务: + +| # | 义务 | 归属 | +|---|---|---| +| 1 | 当前 AP 与当前 RAP 为 HUD 常显字段 | D1 | +| 2 | 刷新入口为玩家发起(Rule 11),入口可用性由 B1/D1 呈现 | B1/D1 | +| 3 | `battle_over` 时 RAP 清零须有可见反馈——防"攒的钱凭空没了"读感(对账义务登记表 #9) | D1 | +| 4 | 动作非法原因需可区分且双源——棋盘面非法(非相邻格/挖旗格等,A2 判定)/ 资源面非法(AP 不足 / RAP 不足,A3 判定),D1 呈现(原因读数的测试承载声明见验收标准前言 out-of-scope) | D1/A2 | +| 5 | 结算明细的效果项带来源标注(P2 归因) | B1 | +| 6 | 交互成本指标(每消耗 1 AP ≤ 2 次点击)归 A2/D1 观测(P4 免费移动的点击摩擦护栏) | A2/D1 | +| 7 | 无专属表现面、无音频需求;AP/RAP 余量变化与"资源不足"反馈的呈现归 D1/A2 | D1/A2 | +| 8 | AP/旗(价目)/停手/RAP 的教学口径以本 GDD 价目表为准,须含"RAP 战斗结束清零"条目(防"存的钱凭空没了"误读) | D5 | + +## Tuning Knobs + +### ActionEconomyTable schema(单行全局,int 类型,A3 回填 owner) + +| 字段 | 类型/范围 | 默认 | knob | 说明 | +|---|---|---|---|---| +| ap_per_round | int [1, 32] | 8 | ✓ | 每轮行动点(原型 v2 实测定稿;双重身份:节奏 knob 兼停手 RAP 转存规模 knob) | +| dig_ap_cost | int [1, 8] | 1 | ✓ | 挖单格成本(域下限 1:0 = 无预算系统失效,越域校验拒绝) | +| flag_place_ap_cost | int [0, 8] | 1 | ✓ | 插旗 AP 成本(旗无实物资源,仅此一项消耗) | +| flag_pull_ap_cost | int [0, 8] | 1 | ✓ | 拔旗 AP 成本(OQ2 复核落点) | +| rap_refresh_cost | int [1, 64] | 8 | ✓ | RAP 刷新门槛;默认 8 = ap_per_round,使"满 AP 一轮停手 = 恰好一刷"成为基线节拍;单字段域内任意值均可运作(仅节奏差异),跨字段约束见下 | +| move_ap_cost | int | 0 | ✗ 冻结 | 移动免费(P4 承载,调参豁免) | +| chain_ap_cost | int | 0 | ✗ 冻结 | 连锁展开免费(原型 v2 定稿) | +| refresh_cap_per_round | 结构位(int) | 2 | 结构位预留 | 每轮刷新上限(Rule 11/EC15;RoundStart 重置计数);D2/registry 字段回写随实现批次(届时 AC-10a 七字段清单同步扩为八字段) | + +- knob 列 = 是否开放调参;非 knob 字段仍入表——代码零字面量,修订须走本 GDD 修订流程。 +- **跨字段校验(D2 validate() 级)**:任一动作成本字段(`dig_ap_cost` / `flag_place_ap_cost` / `flag_pull_ap_cost`)≤ `ap_per_round`——违反即加载期致命(D2 Rule 6 fail-fast)。依据:dig=8 × ap=1 双双域内会使挖掘永久不可用、cleared/ap_exhausted 双不可达(软锁循环),属结构失效而非节奏差异,与本表"域下限 1 = 零预算系统失效"同族;归 ActionEconomyTable schema 类 `validate()` 跨字段类别(D2 既有"schema validate() 收集后致命"机制承载,无需新增 D2 校验通道);违规夹具登记为 D2 校验双通道条件性夹具(同悬空引用待遇)。 +- **域上界注记**:dig/flag 上限 8、refresh 上限 64 为 MVP 量级护栏(非推导界),随调参需要修订。 + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| ap_per_round | 8 | 决策带宽膨胀、节奏拖长(原型 v1 的 12 已实测否决);双重身份:满停 RAP 转存随之增大(8→12 使满停一刷后的富余 +4) | "想清楚的事做不完",违反 P1 | +| dig_ap_cost | 1 | ——(>1 使"连锁免费"的相对收益畸变,未实测) | 0 = 无预算系统失效(schema 域 [1,8] 拒绝) | +| flag_place_ap_cost | 1 | 插旗迟疑,P3 标雷策略萎缩 | 插旗无成本,旗标记泛滥(棋盘噪音) | +| flag_pull_ap_cost | 1 | 挪旗昂贵,误标惩罚过重 | 挪旗近免费,承诺无重量(OQ2 复核落点) | +| rap_refresh_cost | 8 | 刷新太贵,风险退出权萎缩、盲猜局被迫赌(违反 P1 兜底) | 刷新近免费,图来回刷节奏拖沓;敌方时间税仍自惩但体验烦躁 | + +- **Knob 交互**:`rap_refresh_cost : ap_per_round` 比值 = "停几轮换一刷"(默认 1:1 = 满停即一刷;该节拍与 OQ3 纯停手预警同源,联动评估);`ap_per_round × 插旗占比` 与 A1 布雷密度共同决定标雷全周期可行性(A1 定稿后强制复核);效果侧健康(F1 五条款)无单一系统 knob 可控——对照表/闭合对账归 B1、惩罚上限归 B2、奖励分支归 B4(对账归 OQ1);刷新率应急阈值(OQ3)的评估须与 B2 结算强度联动,勿独立调参。 +- **已冻结项**:AP 8 来自原型 v2 实测(12 点被否);移动/连锁 0 成本为结构冻结;RAP 1:1 转存与战斗域生命周期为结构(非 knob,修订走 GDD 流程;跨战斗携带 = OQ7 预留开关);旗数量无上限为结构。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | A3 是被动账本,挖/插/拔的棋盘面执行归 A2(设计顺序第 6 位)、轮次节拍归 B1(第 9 位)——均未实现;冒烟脚本(`--headless --script`,承 D2 冒烟框架)直接驱动 A3 最小 API 面;A2/B1 就绪后改走真实调用方,断言口径不变 | +| API 面 | 8 个 API 布尔返回 = 受理/拒绝——"拒绝"直接断言返回值(notify_* 与 request_* 同为 bool);相位非法拒绝附 debug 诊断、非进程致命;受理矩阵见状态节输入×相位表 | +| 参数语义 | `chain_size` = 连锁附加格数(不含首格,≥0;首格恒耗 1 AP);`clears_map` = 本次动作后全图安全格开完 | +| 只读观测量 | `ap` · `rap` · `phase` · `last_round_end`(记录 path_tag / continuation_hint,可空);相位观测域 = {Idle, Active, Waiting};RoundEnd 为事务内瞬态、不可观测,不作为 GIVEN | +| 口径声明 | `notify_settlement_done` 仅记录编排意图,phase 停留 Waiting(AC-11 断言);轮次开启仍由 `notify_round_start` 触发(AC-14 的门不被绕过);各 AC 的 THEN 具体数值按当前 ConfigDB 表值推导(默认 8/1/1/1/8 下的具体数见各 AC;测试读取表值计算期望,不写死) | +| 夹具一致性 | 冒烟经 `chain_size`/`clears_map` 参数注入棋盘事实(信任方向见依赖 A2 行)——各 AC 使用独立夹具图,`clears_map=false` 腿的安全格余量在夹具中显式登记(防"传 false 却实际清图"的夹具失配假失败);多步 GIVEN 均给出构造序列 | +| 覆盖声明(out-of-scope) | EC11 与挖/插/拔相邻性、旗标记棋盘状态与跨轮留旗(A2 棋盘面域,A2 GDD 承载 AC,systems-index 门禁登记);EC8 末轮判定次序(B1 域);移动/连锁 0 成本不经账本(A2 侧承载);刷新的换图生成与奖励内容(B1/A1/B4 域);资源面拒绝原因的读数区分(UI 要求"可区分",但冒烟 API 无原因观测量——bool + 构造上下文可推断,呈现归 D1/A2 集成测试);跨字段校验违规(`validate()` 级,归 D2 校验双通道条件性夹具) | +| last_round_end 单槽 | 同值双发射不可区分(跨值覆盖由 AC-8/AC-9 值断言承担,同值双发防御归 A2 集成) | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1a | phase=Idle(冷启动,rap=0) | `notify_round_start()` | 返回后 ap=8、rap=0、phase=Active | +| AC-1b | 上轮以 (stopped) 结束且 rap=3、`notify_settlement_done(same_board)` 已消费 | `notify_round_start()` | ap=8(硬重置)、rap=3 不变(RAP 跨轮保留)、phase=Active | +| AC-2 | Active ap=8 | `request_dig(chain_size, false)` 以 chain_size ∈ {0, 1, 5} 各在新轮次执行一次 | 每次返回 true 且扣减恒为 1(连锁免费:chain_size=5 时 ap 8→7 而非 8→2) | +| AC-3a | ap=8 | `request_flag_place()` | true、ap=7(旗标记为 A2 域,A3 无旗观测量) | +| AC-3b | ap≥1(AC-3a 产物 ap=7) | `request_flag_pull()` | true、ap=6 | +| AC-16 | AC-3a 产物(已插 1 旗、ap=7,旗构造经 `request_flag_place()`——A3 冒烟无棋盘观测量) | `request_flag_pull()` 后 `request_flag_place()` | 两调用均 true、AP 合计 −2(挪旗成本,EC3)、轮次不结束 | +| AC-4 | ap=1(构造:新轮 7 次 `request_dig(0,false)`)且 clears_map=false(前置:该格非最后安全格) | `request_dig(0, false)` | true、轮次立即结束、`last_round_end=(ap_exhausted, same_board)`、rap 不变(+0,EC14) | +| AC-4b | ap=1 且仅剩 1 个安全格(构造同上,digs 作用于预设夹具图) | `request_dig(0, true)` | true、`last_round_end` 值等于 `(cleared, new_board)`(清图胜出,EC10)、rap 不变;不存在第二条输出 | +| AC-5 | Active ap=3 且图未开完(构造:5 次非清图挖,8−5=3) | `request_stop()`;随后 `notify_settlement_done(same_board)` + `notify_round_start()` | true、`last_round_end=(stopped, same_board)`、rap=3(1:1 转存,恰一条输出);新轮 ap=8、rap=3 | +| AC-5b | 分支 1:Active 且 rap ≥ rap_refresh_cost(构造:满 AP 停手 → rap=8 → `notify_round_start()` 回 Active——RAP 跨轮保留);分支 2:Active 且 rap=3(构造:前轮挖 5 后 `request_stop()` → rap=3 → `notify_round_start()`) | `request_refresh()`(两分支各一次) | 分支 1:true、rap=0、phase 不变(Active——挖掘期当场换图,A2 装板受理联动);分支 2:false、rap=3、phase 不变(EC13) | +| AC-6 | ap=5(构造:3 次非清图挖) | `request_dig(0, true)`;随后 `notify_settlement_done(new_board)` + `notify_round_start()` | true、`last_round_end=(cleared, new_board)`、rap 不变(剩余 AP 清零,Rule 5/EC14);新轮 ap=8、rap 仍= 前值(RAP 跨图保留,EC9 的 cleared 腿) | +| AC-6b | 冷启动 ap=8、夹具图恰余 4 个安全格 | `request_dig(3, true)`(附加 3 格连锁开完全部余量;chain_size 不含首格) | true、ap=7、`last_round_end` 值等于 `(cleared, new_board)`、rap 不变——连锁免费且连锁格计入清图判定(EC5),单次判定 | +| AC-8 | Active、剩余安全格恰 1、ap≥1(先挖后停腿——A3 冒烟可测子集) | `request_dig(0, true)` 后 `request_stop()` | 第二调用返回 false、`last_round_end=(cleared, new_board)` 恰一条、phase=Waiting(EC4;stop-first 腿 deferral 至 A2 集成,门禁登记) | +| AC-15 | Active ap=8 且图未开完 | `request_stop()` | true、`last_round_end=(stopped, same_board)`、rap=8(满停 = 刷新门槛恰可达,Rule 11 基线节拍,EC6) | +| AC-7 | ap=1(构造:7 次非清图挖)且 debug override `flag_place_ap_cost=2`、`flag_pull_ap_cost=2`(域 [0,8] 内,经 D2 R3 注入);承载声明:默认价目下全动作 1 AP,"AP 不足"腿(EC2)在默认表值下不可构造(ap≥1 恒可支付、ap=0 即收轮),故本 AC 显式承载于非默认表值夹具 | 依次 `request_flag_place()`、`request_flag_pull()`、`request_dig(0,false)` | 前两者 false(AP 不足,EC2)、最后者 true(ap 1→0,触发 ap_exhausted);`last_round_end` 断言只含末次记录 | +| AC-9 | phase=Waiting(经 ap_exhausted 路径构造;RoundEnd 瞬态不可观测) | 依次 `request_dig(0,false)`、`request_flag_place()`、`request_flag_pull()`、`request_stop()` | 全部 false、ap/rap 不变、`last_round_end` 不变(无第二条输出) | +| AC-11 | phase=Waiting 且 rap>0(构造:停手轮后) | `notify_settlement_done(same_board)`;随后 `notify_combat_end()` | settlement_done 返回 true 且 phase 仍= Waiting(仅记录编排意图,AC-14 门不被绕过);combat_end 返回 true、phase=Idle、rap=0(战斗域清零,EC12)、ap=0 | +| AC-12 | phase=Idle | 任何动作/停手/刷新请求 | 全部 false、`last_round_end` 保持 null、phase 不变 | +| AC-13 | phase=Idle | `notify_round_start()`(新战斗首轮) | 返回 true、phase=Active、ap=8(Idle 唯一出口;RoundStart 瞬态不作断言落点) | +| AC-14 | phase=Waiting 且结算未完成(未消费 next_round_mode) | `notify_round_start()` | 返回 false、phase 停留 Waiting、`last_round_end` 不变(fail-fast = 拒绝 + debug 诊断,不静默吞编排错误;统一 bool 口径,非进程致命) | +| AC-18 | phase=Active(冷启动后、未挖任何格) | 依次 `notify_round_start()`、`notify_settlement_done(same_board)`、`notify_combat_end()` | 全部返回 false、ap/rap/phase/`last_round_end` 均不变(输入×相位矩阵 Active 列全覆盖;防 B1 双启动静默 AP←8 重置);`request_refresh` 于 Active 受理(见 AC-5b) | +| AC-17 | 全新 A3 实例(同一 ConfigDB 表;每遍以新实例构造,无跨实例状态) | 同一动作序列驱动两遍 | 两遍 `last_round_end` 序列与 rap 轨迹逐项相同(零随机的行为级证据,D2 AC-15 的 A3 层前身) | +| AC-10a | ConfigDB 加载完成 | 脚本解析 `design/registry/entities.yaml` 的 `action_economy` 条目 | 与 `ConfigDB.action_economy` 七字段(ap_per_round / dig_ap_cost / flag_place_ap_cost / flag_pull_ap_cost / rap_refresh_cost / move_ap_cost / chain_ap_cost)逐一相等;registry 缺任一字段即失败 | +| AC-10b | A3 文件集 | 纳入 D2 AC-14 项目级扫描域 | 引用其人工三要素口径,不另设自动化字面量硬 THEN——字面量判定误报率高,D2 已定性为 warning 级 + 人工复核 | + +## 开放悬案 + +| # | 悬案 | owner | 要点 | +|---|---|---|---| +| 1 | 效果档位对照表与闭合对账 | B1(第 9 位) | 安全格 buff / 标中旗 debuff / 清图奖励 / 敌方增益的统一当量对照与换算由 B1 GDD 定稿,须满足 F1 五条款;B1 兼任条款 3(挖掘风险不等式)与条款 5(弃图支配性)的单一闭合对账 owner(A1/B2/B4 供给锚值,B1 签字验收),两项均登记 B1 GDD 强制检查点;每战标中旗总吞吐对账归本条;注意与 OQ6 清图奖励插槽区分 | +| 2 | 拔旗耗 AP 复核 + 标错惩罚双重职责 | A2 前原型小补丁(与标错惩罚实测合并会话) | 本 GDD 按"拔旗 1 AP、移除标记"定稿;复核改动现已有 knob 落点(`flag_pull_ap_cost`),若实测推翻则调 knob 并回写 registry。标错惩罚现承担双重职责:误标税 + 停手轮/死 RAP 态下旗滥用的唯一价格——RAP 无上限 + 满溢态使碎片区插旗机会成本趋零,"可疑格全插旗、永不拔"的滑向由本惩罚定价;OQ3"停手轮旗动作占比"指标联动观测 | +| 3 | playtest 预注册指标 | 垂直切片前置 playtest | 指标:弃图率 = refresh/(clear+refresh)(>30% 观察、>50% 干预——provisional 预注册);刷新时已开格完成度直方图(完成度 <60% 的刷新占比 >50% 即信号);停手轮旗动作占比(>50% 即信号——投机旗/死 RAP 态检测,联动 OQ2);确定动作供给 >8 的轮占比(P1 决策密度兜底监测,与 A1 密度联合验收);刷新率/战斗(>3 次即观察信号,阈值评估与 B2 结算强度联动,勿独立调参);纯停手轮占比(>20–30% 即观察信号);停手时机分布、每轮犹豫时长、终局碎片区(清图完成度 >80%)轮次数。失明声明:刷新率/纯停手占比/碎片区轮次三项对"挖完全部确定格→停→刷"环结构性失明——该环停手轮先挖后停(非纯停手)、温和变体刷新率低、开到 >80% 即弃使碎片区读数反而变低(病态呈现为健康);故增弃图率/完成度直方图/停手轮旗占比前三项直测该环。应急方案(触发后启动):`rap_refresh_cost` 调参 / 刷新冷却(连续刷新须间隔 N 轮)/ 刷新新图保留已开格结构(重议级) | +| 4 | AP 耗尽的软提示 | D5(低优先) | 本 GDD 定"AP=0 即收轮";教学若发现新手困惑,加提示不改规则;可扩展覆盖"RAP 达标刷新引导"与"RAP 战斗末清零提示"(D1 清零反馈的对应教学条目) | +| 5 | 标中旗兑付语义细化 | B4/B1(A3 硬约束已定稿) | 标中旗 = 怪物 debuff(三向效果定稿);硬约束:兑付一次性、当轮结算、兑付资格以雷格实例为键(每雷本次战斗至多兑付一次,资格随首次兑付消费;放置实例仅为当轮结算载体)、拔旗重插同一雷格不恢复已消费资格(堵重插农场)、禁止逐轮重复兑付;换图后新雷为新实例;档位锚见 F1 条款 2(严格 > 最低档 + 连锁放大边际基准 + 总吞吐对账);标错惩罚归 game-concept 待定问题(OQ2 合并会话,双重职责见 OQ2) | +| 6 | 清图奖励差异化插槽 | B4(结构位预留 + A1 上下文) | 清图奖励未来按图类型(biome/tag)分支——B4 奖励表 schema 须预留图类型维度的结构位;A3 只上报 `cleared`,图上下文经 B1 传递、不经 A3;约束:图类型上下文在战斗域内恒定(biome 挂关卡层、不逐图重掷)——否则 `request_refresh` 退化为 biome 重掷钓鱼口;该约束登记 A1/B4 共同验收 | +| 7 | RAP 跨战斗生命周期开关 | 垂直切片 knob 复核 | MVP 每场战斗清零;若实测支持跨战斗存蓄(Boss 图连刷策略),须修订 Rule 11/EC12 + 登记 D3 持久化归属与 C2 冒险域语义 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/边界情况/AC/义务台账/OQ 转表格,流程转字符画;F1 五条款、公式与验算数据全保留;语义与 2026-09-07 三轮评审修订版及 2026-09-12 D4 修订对齐,零规则变更 | +| D4 修订 | 2026-09-12 | 刷新决策前移挖掘期:`request_refresh` 由 B1 `refresh_board` 挖掘期刷新编排内调用(原结算编排期 Waiting 相位代理调用退役);新增每轮刷新上限 `refresh_cap_per_round` 默认 2(RoundStart 重置、超限拒绝,EC15);旧板旗随旧板作废(零兑付、旗 AP 不返还,A2 换图全量重置承载);stopped/ap_exhausted 轮恒 same_board 钉入 Waiting 出口契约 | +| 三轮评审修订版 | 2026-09-07 | 三轮 full review(game-designer · systems-designer · qa-lead · economy-designer · creative-director)8 条文本级 blocking 同日全部核销,用户拍板接受修订并标记 Approved、免独立 delta 核销(不设第四轮全量评审):RAP 池新增(停手剩余 AP 1:1 转存,原 ×2 折算与 stop_multiplier 删除;战斗域生命周期、battle_over 清零);清图剩余 AP 作废(原 ×1 折算与 clear_multiplier 删除);F1 结构性改写为五条款并钉死 B1 闭合 owner(原 F1 折算公式与 F2 挖/停/清激励不等式整体作废——F2 被二轮评审击穿两条数学缺陷:博弈树错误、缺风险项,不再修复而是移除病灶);旗子单态(口袋旗双态删除,数量无上限,稀缺性由 AP 机会成本承载);round_end 双发射源(stopped 由 A3 直发);兑付资格以雷格实例为键(堵重插农场);ActionEconomyTable 四字段删除 + rap_refresh_cost 新增(同日三次回填);跨字段校验(动作成本 ≤ ap_per_round);输入×相位矩阵增补;评审日志 design/gdd/reviews/a3-action-economy-review-log.md | +| v1(初稿定稿) | 2026-09-07 前 | 数值继承原型 v2/v3 实测结论与 game-concept 定稿:AP=8 定稿(12 点否决);连锁免费(原型 v2 定稿);统一行动经济(原型 v3:挖/插/拔均限相邻、拔旗 1 AP 条文读法确认);三向效果与清图奖励结构落位 | diff --git a/design/gdd/a4-terrain-variation.md b/design/gdd/a4-terrain-variation.md new file mode 100644 index 00000000..f60ba421 --- /dev/null +++ b/design/gdd/a4-terrain-variation.md @@ -0,0 +1,517 @@ +# A4 地形变异(Terrain Variation) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents(lean 门禁:systems-designer 审查 Formulas · qa-lead 审查 Acceptance Criteria) +> **承载支柱**:P1 主(信息诚实边界——"不可推理"永不伪装成"可推理")· P3(信息维度变体——定价通货从雷险换信息+AP;迷雾仅其信息切面)· P2(暗面警示——雾区奖励可预期性切面)· P4(地牢氛围本体承载) +> **层级**:Layer 1 Foundation · Milestone MVP · 设计顺序第 3 位(上游 D2、A3 已 Approved) +> **一句话**:A4 定义扫雷图上的地形变异——基础规则之上的区域性规则偏离;MVP 唯一变异为迷雾(边界可见、内容隐蔽),并以变异框架承载岩浆/冰冻等后续变异逐个加入。 +> **复核注记**:art-director 未经咨询(Lean 模式)——生产前人工复核视觉规格与 art-bible 一致性(creative-director 已在两轮独立评审中终裁幻想节处置)。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 变异的定义与交互规则 owner——在基础扫雷规则上叠加区域性规则偏离的框架 + MVP 迷雾;玩家不"使用"A4,而是在挖掘中遭遇并应对它 | +| 拥有 | VariationTable 8 字段定义(D2 回填 owner)· F1 候选池门控谓词 · 信息剥夺边界与双向效应 · 生成期布置约束声明(执行归 A1)· 零特判原则的规则面(义务是"不加分支")· 未来变异扩展框架 | +| 不拥有 | 布置算法与区域参数(A1 BoardGenTable)· 挖/旗执行(A2)· 呈现(D1)· 教学文案(D5)· 奖励内容与定价(B4/B1)· 道具(A5)· 全局变异语义(C3)· 玩法数值字面量与随机 | +| 上游输入 | D2 VariationTable schema 承载 + ConfigDB 读取(hard——唯一玩法上游);A3 价目 `ap_per_round` / `dig_ap_cost` 文档层引用(零运行时接口消费) | +| 下游输出 | 候选池门控 + cell_constraint + 出生区排除(A1)· 变体属性只读查询契约(A2)· 呈现规格(D1)· 教学口径(D5)· 定价硬约束与条款 5 对账输入(B4/B1)· counter_item_ids 预留(A5)· scope=global 枚举位(C3) | +| 硬约束 | 生成期布置约束(雾区仅非 0 安全格——区内无雷、无 0 格)· 零特判原则 · fog unlock_level=2 · hide_numbers⇒safe_nonzero 跨字段校验致命 · 厚度 ≤2 / f ≤ 8×雷密度 硬边界 | + +A4 是定义层系统:变异由棋盘生成(A1)布置在图上,玩家在挖掘中遭遇并应对——看见迷雾边界那一刻决策就出现:如何安排挖掘顺序让 0 格连锁免费吃掉雾格(连锁路由),链不可达的雾残段按全价续付清图还是停手/弃图(A3 风险退出权衡;垂直切片起再加"是否用道具买情报",A5)。清图判定含雾格——雾是清图的必经段而非可绕过的支线,"绕开走"的真实形态是 A3 的停手/弃图风险退出。 + +没有 A4,游戏失去两样东西:难度只能靠雷数单调缩放、关卡间"解锁新变异"的推进感(P4 越下越险)消失;且"无法推理"的格子会以埋伏形式随机出现——迷雾把"你推不出来"从暗处的埋伏变成明处的选择,是 P3"风险与收益同源"在空间维度的载体,也是对 P1"我算对了"的诚实兜底:推不出的区域被显式标注、可绕行、可买情报,而不是让玩家在自以为可推理的局面上撞墙。 + +## 体验锚点 + +> 情绪目标一句话:**站在洞口前的权衡感**——迷雾不制造惊喜或惊吓,它制造一个看得见边界的承诺:"里面有价值,进去的代价是 AP 溢价与放弃雾格数字这份推理原料。"(玩家每轮都在看见并应对变异——直接感知) + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 我算对了的诚实边界 | "数字不再说话" | 迷雾是游戏对玩家的坦白——把"无法推理"从埋伏变成路标:雾外仍可用区外数字推理雾边缘的雷(跨雾推理——被雾边已知约束放大的常规推理体验,"隔着雾也算出来了",是常规推理在雾边的延伸、非独立机制);进雾每一步是承诺而非计算;基础区可推理率生成保证归 A1,A4 负责"不可推理"永不伪装成"可推理";"里面有价值"是契约而非愿望——由 B4 三条定价硬约束兜底(义务清单 B4-①②③) | +| P3 风险与收益同源(信息切面) | "不赌命,赌信息与行动力" | 雾区无雷是玩家可见知识——雾的"险"在信息与行动力的双重定价:盲区预算按链不可达雾格全价收紧,链可达雾格经邻接 0 格连锁免费开出(两态口径);变异框架是 P3 的长期载体,迷雾仅其信息切面,P3 雷险维度由岩浆/冰冻(垂直切片起)承接;真实耦合点在雾边(Rule 5 双向效应——A1 按双向净效应建模) | +| P2 规划的延迟兑现(暗面警示) | "边界可见、内容隐蔽" | 雾内奖励若不可见,规划退化成探索惊喜(创意风险 #1,CD 门禁登记 B4/A4 落实)——Core Rules 定迷雾信息呈现规则的精确切面,奖励可见性/探测机制的接口在此预留、内容归 B4/A5 | +| P4 地牢氛围的本体承载 | "趴在地图上的黑暗" | 迷雾是"下探黑暗"隐喻的直接具象——未探索的黑暗是有形状、有边界、趴在地图上的实体;后续变异(岩浆/冰冻)延续"越深越险的地貌"叙事(随关卡解锁) | + +反向护栏(裁剪判据):迷雾绝不能制造"被骗感"——若雾区边界不可见、或进洞无额外价值,迷雾退化为纯负体验税(纯 AP 税);任何让雾区"隐性化"的设计默认砍掉。"里面有价值"由 B4 三条定价硬约束兜底:两条防税下界 + 一条防福利上界——防 level 2 首发关(小图低密、链免开占比结构性最高)"链免开 + 锚上浮"双白拿的福利板化与"雾=免费奖励"先验(后者会在后期雾变厚、全价残段出现时恰触发被骗感护栏)。 + +- 参考感受:Into the Breach 的信息诚实(游戏从不用隐藏信息骗你——本作反其道而行,把"信息缺失本身"诚实标注)· 杀戮尖塔地图的路径承诺(看得见的未知,选定的风险)。 + +## 全局流程 + +### 地形布置流程(A1 生成期——A4 只声明约束与门控,算法归 A1) + +``` +A1 生成新扫雷图(雷图 · 尺寸 · biome,(level_index, node_depth) 维度) + │ ① 求布置候选池 pool = F1(level_index, board_biome) + │ pool = ∅(雾未解锁 / biome_tag 全不匹配)→ 静默布置普通图,不报错(EC1) + │ ② 池非空 → 逐候选按 cell_constraint=safe_nonzero 求可行域 + │ 非可行(非 0 安全格面积或连通分量不足)→ 跳过雾布置,本图为合法无变异图(EC2 降级) + │ 实际达成区数 k < 参数要求 N → 尽力布置可行分量,按 shortfall 分级上报降级率(禁放宽 Rule 3 硬塞) + │ ③ 可行域内布置雾区,硬边界逐项成立: + │ · 雾格 ∈ 非 0 安全格(区内无雷、无 0 格) + │ · 厚度 ≤2(≡ 任意 3×3 窗口非全雾)· f ≤ 8×雷密度 · 雾占比 < 全覆盖(EC10) + │ · 出生 3×3 及其初始展开可达域内无变异格(保守上界 = 出生 3×3 经 0 格最大 + │ flood 闭包外扩 1 格——任意 A2 展开定义的超集,Rule 7) + │ · 区域随机选取经 board_gen 流取号(回放确定性) + │ ④ 生成期计算并输出链不可达雾格占比(B1 条款 5 对账输入,验收⑤) + ▼ +棋盘产物(格状态 + 变异标记 + 出生区)→ A2 install_board 持有生成期快照 +``` + +### 雾揭示流程(运行期——零特判,A2 执行逻辑无变异分支) + +``` +玩家挖格 c(全价 dig_ap_cost)或 0 格连锁传播至 c(连锁展开零价) + │ A2 只读查询格变体属性(生成期快照) + ▼ +c 是雾格? + ├─ 否 → 正常开格:显示数字(8 邻雷数恒真,值不因雾改变) + └─ 是 → 开出、不显示数字(未开时专属纹理已标出区域边界) + · 来源 = 连锁 → 免费(链可达雾格——EC4;雾格恒非 0,级联止于雾格) + · 来源 = 单格挖掘 → 全价 dig_ap_cost(链不可达雾格) + · 计入清图进度 · 安全格奖励照常入库 · 雾内插旗合法但轮末判定恒不中 +``` + +### 出口分发 + +| 出口 | 触发 | 行为 | 依据 | +|---|---|---|---| +| 普通图(无变异) | 候选池为空(未解锁 / biome_tag 全不匹配) | 静默布置、不报错、无 warning——两种来源同待遇 | EC1 / F1 Output Range | +| 无雾图(降级产物) | cell_constraint 无解(面积或连通分量不足) | 跳过雾布置,图仍合法;shortfall 分级上报降级率 | EC2 | +| 合法雾图 | 可行域内布置成功 | 产物携带变异标记;占比参数域校验通过;输出链不可达占比 | Rule 3 / Rule 6 | +| 参数域拒绝 | 雾占比 ≥ 全覆盖(极端参数) | BoardGenTable 参数校验拒绝(雾占比上限 × 可推理率下限联动不等式) | EC10 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:每种变异都是配置表里的一行声明,不含任何专属代码 +**人话**:这条管变异怎么被定义。每种变异是配置表 VariationTable 里的一行具名声明,一共声明四件事——见下表。框架自身不含运行时逻辑:变异带来的行为改变,发生在"信息规则 × 既有系统行为"的交集里,定义行只声明交集参数,不写任何处理代码。 + +| 声明项 | 字段 | 回答的问题 | +|---|---|---| +| 身份 | variant_id / display_name / unlock_level / biome_tag | 叫什么、第几关解锁、在哪些地形主题出现 | +| 信息规则 | info_mask | 隐藏玩家的什么信息 | +| 布置约束 | cell_constraint | 允许落在什么样的格子上 | +| 作用域 | scope | 管一块区域(region),还是全局(global——为后续预留) | + +**举证**:无计算(定义框架)。字段 schema 定稿与默认值见「Tuning Knobs·VariationTable schema 定稿」表(本 GDD 回填 owner,承载归 D2)。 + +#### 规则 2:迷雾边界看得见,里面的数字看不见 +**人话**:这条管 MVP 唯一的变异——迷雾。它是作用域为 region 的信息剥夺变异,玩家在挖掘中遭遇它时,行为规则只有两条: + +| 情况 | 玩家看到什么 | +|---|---| +| 雾格还没开 | 专属纹理标出雾区范围——边界可见、内容隐蔽 | +| 雾格被开出(亲手挖或连锁带到) | 照常开出,但不显示数字 | + +**举证**:无计算(两条行为规则)。呈现规格(纹理、边界可读性、已开雾格样式)归 D1,见「呈现与 UI 契约」节与依赖节 D1 行。 + +#### 规则 3:雾只铺在贴着雷的安全格上——雾里永远没雷、没 0 格 +**人话**:这条管棋盘生成时雾往哪儿摆,是本 GDD 的承重规则(用户定稿)。生成器布置雾区只能选"非 0 安全格"——8 邻里至少含 1 雷的安全格,于是雾区里既无雷、也无 0 格。约束由本 GDD 声明、棋盘生成器在生成期强制执行,谁都不许为布置成功率放宽它。玩家侧的承诺是明牌:"雾区无雷"由教学与图例传达。正因雾里没雷没 0 格,既有规则才能对雾全面惰性化——下一条讲的就是这个。 + +**举证**:计算见「数值结论与推导·结构推论」——厚度 ≤2(可判定谓词 ≡ 任意 3×3 窗口非全雾)与面积必要上界 f ≤ 8×雷密度的推导与验算都在该节;约束逐项落点见下方实现参照表;执行义务登记 A1 验收⑫;数据载体 = cell_constraint 字段;"雾区无雷"等可教知识清单见「规则 3/5 的实现参照——雾元知识信息源」。 + +#### 规则 4:既有系统不为雾改任何行为,代码里零变异分支 +**人话**:这条管雾与游戏其余部分的关系,同为承重规则。连锁、数字计数、清图判定、插旗、行动价目、结算——既有系统一律不感知"这格是不是雾格":雾不改变它们任何行为,代码里也不许出现"如果是雾格就……"的分支。雾格能被如此"无视",靠的正是上一条"雾里没雷没 0 格"的布置约束。六个系统面在雾下的具体表现见下方实现参照表。 + +**举证**:无计算(架构纪律)。挖掘侧义务 = 执行逻辑零变异分支("不加分支"而非"加处理",见依赖节下游 A2 行);A4 文件集零随机、零字面量走静态扫描(AC-7/AC-10)。 + +#### 规则 5:雾只藏雾格的数字,藏的同时倒送两样补偿 +**人话**:这条管信息剥夺的边界——"雾到底藏了什么"的账目。藏的只有一样:未开雾格的数字;不藏的有三样,见下表。雾外数字对雾外雷的计数照常,雾边界推理仍可行、只是原料变少。剥夺之外并存增益,净效应双向、随雾形与雷密度变化——建模必须两边都算,不许只算一边: + +| 信息 | 雾下状态 | +|---|---| +| 未开雾格的数字 | 藏——唯一被剥夺的 | +| 雾区边界 | 不藏——专属纹理可见 | +| 已开格的全部信息 | 不藏 | +| 奖励入库过程 | 不藏 | + +| 效应方向 | 内容 | +|---|---| +| 剥夺(减项) | 雾格数字本可作区外雷的推理源,其寂静使雾边附近的线索变少 | +| 增益(加项) | 雾格保证无雷且必邻区外雷;雾形携带雷簇位置(k 格雾区外环必含 ≥⌈k/8⌉ 雷);雾边邻格雷候选域收缩、局部更易解——"雾边 = 推理锚" | + +**举证**:净效应双向且依赖雾形/雷密度——A1 可推理率参数须按生成后实测净效应建模、禁单向补偿(验收④);外环雷下界 ⌈k/8⌉ 的推导见「数值结论与推导·结构推论」;玩家可推理、可教的雾知识清单见「规则 3/5 的实现参照——雾元知识信息源」。 + +#### 规则 6:雾摆多大摆哪儿归棋盘生成器,A4 只管入场资格 +**人话**:这条管雾区摆放的决定权。A4 不拥有布置算法:摆几个雾区、每个多大、摆在哪,与雷图共生,由 A1 生成算法 + BoardGenTable 参数(按关卡×深度两维逐行,A1 回填)决定;布置随机从棋盘生成专用随机流(board_gen 流)取号,每次生成新扫雷图绑定一次,同种子必出同图。A4 对摆放的唯一参与是资格把关:变异的解锁关号不高于当前关卡序号,才进入这一关的布置候选池。 + +**举证**:门控的计算形式是集合谓词 F1(unlock_level 直接整数比较 + biome_tag 通配/交集),见「数值结论与推导·F1 布置候选门控」;取号机制 = RngService scope 绑定(每次生成新扫雷图);区域数量/尺寸/占比参数域见「枚举·板形参数」表。 + +#### 规则 7:出生区及其连锁可达范围永不长雾 +**人话**:这条管玩家的落点安全。开局站立的 3×3 出生区,连同从它出发顺 0 格连锁能自动展开到的范围,永不含任何变异——生成器布置时硬性排除。可达范围由生成期确定性算出(不依赖挖掘系统运行期的展开行为定义),取保守上界:从出生 3×3 经 0 格连锁能扩散到的最大范围(flood 闭包)再外扩 1 格,比挖掘系统实际能展开的范围只大不小。防的是"开局即瞎"与"开局预开雾格";叠加"初始展开无奖励"(定稿口径),初始展开触及雾格在构造上不可能——双保险。 + +**举证**:无计算(几何排除)。可达域保守上界构造见 A1 GDD;回验项(挖掘侧实际展开域 ⊆ 生成侧排除域——"构造上不可能"仅在超集关系下成立)登记 A2 GDD 门禁;"初始展开无奖励优先于连锁入库语义"保留(EC4);出生区排除登记验收③。 + +#### 规则 8:A4 的代码不写死数字、不掷随机 +**人话**:这条管数据纪律。变异的全部定义数据放配置表 VariationTable——本 GDD 是该表回填 owner,字段 schema 见 Tuning Knobs 节。A4 相关代码零玩法数值字面量、零随机:布置随机落在棋盘生成器的专用随机流里,A4 自己既不掷随机数也不写死玩法数字,一切读表。 + +**举证**:无计算(数据纪律)。schema 定稿与 knob 划分见「Tuning Knobs」节;机器验收 = 静态扫描零命中(AC-7)与字面量人工口径(AC-10)。 + +#### 规则 9:新变异将来按同一框架加行,不另起炉灶 +**人话**:这条管变异框架怎么长出新居民。岩浆、冰冻在 MVP 不建行(CD 门禁:无 MVP 容器),随垂直切片关卡解锁建行;将来某变异需要新规则字段时,走 D2 接口漂移规则同步回写本 GDD 与 D2,不在表外私加字段。精英/Boss 图的全局变异只预留一个"全局"作用域枚举位(scope=global),语义归 C3 GDD 定稿——本 GDD 只保证枚举位存在,不预写语义。 + +**举证**:无计算(扩展纪律)。MVP 行集内容事实(恰含 fog 一行)见「枚举·VariationTable 行集」;global 行出现时的 warning 校验见「枚举·VariationTable schema 校验类别」表;接口漂移规则归 D2。 + +### 规则 3 的实现参照——生成期布置约束(几何硬边界) + +| 约束项 | 内容 | 执行 / 落点 | +|---|---|---| +| 雾区格类 | 仅非 0 安全格——区内无雷、无 0 格(非 0 安全格 = 8 邻域含 ≥1 雷) | A1 生成期强制(A4 声明 cell_constraint 数据载体) | +| 玩家可见知识 | "雾区无雷" | D5 教学必授 + D1 图例条目(MVP 验收两条) | +| 几何硬边界 | 每个雾格必有非雾雷邻居 ⇒ 雾区任意处厚度 ≤2——可判定谓词 ≡ 任意 3×3 窗口非全雾(3×3 实心雾块中心必为 0 格,不可能成雾) | 登记 A1 验收⑫;推导与验算见数值节 | +| 面积必要上界 | 雾面积占比存在依赖雷密度的必要上界 f ≤ 8×雷密度——雾区恒为雷集切比雪夫 1-膨胀出的边界带 | 同上 | +| 参数域 | "区域数量/尺寸/占比"参数(A1 BoardGenTable 域)必须在此边界内定义 | OQ1 联动;验收⑫⑩ | +| 正常形态 | 1~2 格宽雾带是雾的正常形态 | 视觉规格须按最坏宽度验收(AC-9) | +| 可推导元知识 | "每个雾格必邻区外雷"由规则可推出 | 按 P1 诚实原则(可推导即可教)纳入教学候选,口径归 D5 裁定 | + +### 规则 4 的实现参照——零特判六面 + +| 系统行为 | 雾下的行为 | 性质 | +|---|---|---| +| 连锁展开 | 触发与传播只看真实数字;雾格恒非 0 → 雾格自身永不传播连锁(不开出新级联);但雾格可被邻接 0 格的连锁免费开出(EC4——"不传播"≠"不被开") | flood fill 代码零变异分支 | +| 数字计数 | 数字 = 周围 8 格雷数恒真;雷全在雾外,任何数字的值不因雾存在而改变 | 恒真定理 | +| 清图判定 | 雾格照常计入"安全格全开"判定——雾是清图必经段 | 恒等 | +| 旗 | 雾内插旗合法(价目照常)但恒标不中(无雷) | 合法、无害、无意义 | +| 行动价目 | 挖/旗/移动成本雾内外一致 | A3"地形成本修饰"钩子本 GDD 搁置——启用须回 A3 重议 per-cell 成本接口 | +| 结算 | 雾格安全格奖励照常入库与结算 | B1/B4 无需感知雾 | + +### 规则 3/5 的实现参照——雾元知识信息源(玩家可推理、可教的雾知识清单) + +| 元知识 | 信息源 | 消费方 | +|---|---|---| +| 雾区无雷 | 生成期布置约束(Rule 3)——生成保证而非运行期判定 | D5 教学必授 + D1 图例(MVP 验收两条) | +| 每雾格必邻区外雷 | Rule 3 结构推论(可推导即可教——P1 诚实原则) | D5 教学候选(口径归 D5 裁定) | +| 雾格恒非 0 · 级联止于雾界 | Rule 3 寄生结论 + hide_numbers⇒safe_nonzero 跨字段校验 | D5 教学(防"连锁莫名断了"的被骗感——D1 必含要点) | +| 雾边候选域收缩 · 局部更易解 | Rule 5 增益侧(外环雷下界 ⌈k/8⌉) | A1 可推理率建模(双向净效应) | +| 雾格成本两态(链可达免费 / 链不可达全价) | 数值节预算注记 | A3/B1 对账;D5 玩家侧保守启发式教学("雾格按全价做预算,连锁白开当惊喜"——链可达性取决于未开 0 格的隐藏拓扑,玩家决策时点不可求值,明教两态反生免费期待、恰触发反向护栏被骗感) | +| 雾内旗恒空 | Rule 4 旗面(雾区无雷) | D5 教学警示"雾内插旗纯损" | + +## 枚举 + +### VariationTable 行集(MVP 表内容——恰含 fog 一行,AC-1 内容事实断言) + +| 行 | 字段取值 | 产出点 | 语义 | +|---|---|---|---| +| "fog"(迷雾) | unlock_level=2 · scope=region · info_mask=hide_numbers · cell_constraint=safe_nonzero · biome_tag=[] · counter_item_ids=[] | game-concept 解锁序列:普通数字→迷雾→岩浆/冰冻;本 GDD 回填 D2 | MVP 唯一变异;岩浆/冰冻不建行(CD 门禁:无 MVP 容器),垂直切片随关卡解锁建行 | + +### 板形参数(A1 BoardGenTable 域——A4 引用不拥有,禁止两表间复制参数,D2 Rule 1) + +| 参数 | 维度 | 约束与义务 | 产出点 / 消费方 | +|---|---|---|---| +| 区域数量 N | (level_index, node_depth) | 要求 N 区而实际达成 k 口径总纲:本 GDD 无平衡公式——迷雾的全部数量参数(区域数/尺寸/位置/与雷密度及图尺寸的交互)归 A1 BoardGenTable,且须在 Rule 3 结构推论硬边界(厚度 ≤2【≡ 任意 3×3 窗口非全雾】/ f ≤ 8d)内定义。A4 侧定量内容仅三条:F1 门控谓词、结构推论承重数学、雾致预算增量注记。 + +### F1 布置候选门控(variant_pool_gate) + +**结论**:pool 是集合谓词(MVP 规模 ≤1)——unlock_level 直接整数比较(无 D2 R1 floor 解析:unlock_level 是定义表列,非进度表行键);biome_tag 空集 = 通配(全 biome 可用),非匹配失败;空池合法,A1 静默布置普通图不报错(验收⑥)。 + +#### 推导 + +``` +pool(level_index, board_biome) = { v ∈ VariationTable | + (v.unlock_level ≤ level_index) ∧ (v.biome_tag = ∅ ∨ v.biome_tag ∩ board_biome ≠ ∅) } +eligible(v, L, B) :⇔ v ∈ pool(L, B) ——成员判定形式(AC-3 消费) +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| level_index | int | ≥1 | 当前关卡序号;与 unlock_level 直接整数比较(无 D2 R1 floor 解析——unlock_level 是定义表列,非进度表行键) | +| v.unlock_level | int | [1, 999] | VariationTable 列(fog 默认 2) | +| v.biome_tag | StringSet | ∅ 或非空 | ∅ = 通配(全 biome 可用),非匹配失败 | +| board_biome | StringSet | 非空 | 当前图 biome 标签集;值域与来源归 A1(BoardGenTable biome 字段,A1 回填)——本 GDD 只声明集合类型语义 | + +- Output Range:pool 为集合(MVP 规模 ≤1);逐项判定输出 bool;空池(如 level 1 雾未解锁)合法——A1 静默布置普通图,不报错(验收⑥)。 + +| level_index | pool | 验算 | +|---|---|---| +| 1 | ∅ | 雾未解锁——空池合法,静默布置普通图 | +| 2 | {fog} | fog.unlock_level=2、biome_tag=∅ 通配任意 board_biome | +| 3(magma 建行后) | {fog, magma} | 未来 magma(unlock_level=3);A1 从池中选取时必须消费 board_gen 流(回放确定性——验收⑧) | + +### 结构推论(Rule 3 承重数学——厚度、面积上界、外环雷下界) + +**结论**:非 0 安全格 ⇒ 每雾格必有非雾雷邻居 ⇒ 雾区任意处厚度 ≤2,可判定谓词 ≡ 任意 3×3 窗口非全雾(度量钉死为窗口谓词而非"直线连续行程 ≤2"——宽 3 对角带无任何全雾 3×3 窗、完全合法,按行程读法会误拒);雾面积占比存在依赖雷密度的必要上界 f ≤ 8×雷密度;k 格雾区外环必含 ≥⌈k/8⌉ 雷(雾形携带雷簇位置信息——雾边 = 推理锚)。 + +#### 推导 + +``` +非 0 安全格 ⇔ 8 邻域含 ≥1 雷;雷不可能为雾格(区内无雷) + ⇒ 每个雾格 x:∃雷 m ∈ N₈(x)(非雾雷邻居) +① 厚度:3×3 全雾窗口的中心格 8 邻全雾 ⇒ 8 邻无雷 ⇒ 中心为 0 格, + 与"雾格 ∈ 非 0 安全格"矛盾 ⇒ 任意 3×3 窗口非全雾 ⟺ 厚度 ≤2 + (雾区恒为雷集切比雪夫 1-膨胀出的边界带) +② 面积上界:每雾格 ≥1 个雷邻居,每雷至多作为 8 个雾邻格的见证 + ⇒ f ≤ 8×|M|(f = 雾格数,|M| = 雷数) +③ 外环雷下界:k 格雾区的外环(雾邻界外格集)承载全部雷邻居见证, + 外环每格至多邻 8 个雾格 ⇒ |外环雷| ≥ ⌈k/8⌉ ——雾形本身携带雷簇位置信息(Rule 5 增益侧) +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| f | int | ≥0 | 雾格总数(雾面积占比的分子) | +| \|M\| | int | ≥0 | 雷数(雷密度分母 = 全格数) | +| k | int | ≥1 | 单个雾区格数 | +| N₈(x) | 格集 | —— | x 的 8 邻 | + +| 构型 | 谓词判定 | 验算 | +|---|---|---| +| 3×3 实心雾块 | 不可能成雾 | 中心格 8 邻全雾 ⇒ 8 邻无雷 ⇒ 0 格,与非 0 安全格矛盾 | +| 宽 3 对角雾带 | 合法 | 无任何全雾 3×3 窗;按"直线连续行程 ≤2"读法会误拒——度量钉死为窗口谓词 | +| f > 8×\|M\| | 违反必要上界 | 见②——参数域拒绝(EC10 联动) | +| k 格雾区 | \|外环雷\| ≥ ⌈k/8⌉ | 见③——雾边 = 推理锚,禁单向建模(验收④) | + +### 雾致预算增量(唯一 A4 侧定量注记) + +**结论**:雾不改变 AP 价目,但改变清图预算构成,雾格 AP 成本分两态——链可达免费开出(EC4)、链不可达按全价 dig_ap_cost 结算;雾致预算增量 = 链不可达雾格数 × dig_ap_cost,计入清图总预算需求,参与 A1 BoardGenTable 设计期聚合核算(非逐板运行期强制,违例动作 = 修正参数,验收⑤);该增量对 A3 F1 条款 5 仅是可行性输入、不提供雾成本显著性保证——局限如实登记。 + +#### 推导 + +``` +价目不变:挖/旗/移动成本雾内外一致(零特判,Rule 4) +雾格 AP 成本两态: + 链可达雾格 → 免费(邻接非雾 0 格连锁展开零价——EC4) + 链不可达雾格 → 全价 dig_ap_cost 结算 +雾致预算增量 = 链不可达雾格数 × dig_ap_cost (量纲闭合:格 × AP/格) +计入清图总预算需求 → 参与 A1 BoardGenTable 设计期核算 +总预算可用量与全量校验(全量待挖下界 ≤ 可用预算)口径归 A1/A3 契约,非本 GDD 定义 +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| 链不可达雾格数 | int | ≥0 | A1 生成期计算并输出(B1 条款 5 对账输入的生产者义务——验收⑤) | +| dig_ap_cost | int | A3 knob(=1) | 归 A3 所有,本 GDD 引用不拥有 | + +| 情形 | 预算增量 | 验算 / 说明 | +|---|---|---| +| 链可达雾格(邻接非雾 0 格) | 0 | 连锁展开零价——免费开出(EC4) | +| 链不可达雾格 | dig_ap_cost / 格 | 全价结算;增量 = 链不可达雾格数 × dig_ap_cost | +| 稀疏图(链可达占比高) | 全价假设高估 | 系统性高估雾成本——高估方向为预算安全侧(level-designer 评审发现) | + +### 对 A3 F1 条款 5 的对账(三腿入账——构成变化而非消解) + +| 腿 | 效应 | 方向 | +|---|---|---| +| 雷险腿 | 雾区无雷、不产生 50/50 赌雷 | 消解(消解声称仅对此腿成立) | +| 信息腿 | 雾边寂静减少推理原料(推高弃图)与雾边推理锚增益(Rule 5)方向相反——净效应实测、非定理("50/50 减少可能反向利好清图"成立,"必然推高弃图"不采纳) | 双向 | +| AP 腿 | 链不可达雾格全价无连锁折扣,抬高清图成本——敌方结算时间税随之上行 | 推高弃图 | + +- 雾面积占比与链不可达占比为 B1 条款 5 对账输入变量(链不可达占比的生产者义务 = A1 生成期计算输出——验收⑤,防输入变量有名无源)。 +- B1 闭合对账须雾板/无雾板两态分别验算支配性不等式(A3 provisional 验算只在无雾图形况做过)+ 两条方向性护栏:① 雾板净当量优势带上界(雾板 vs 无雾板 EV 差的容许带——防福利方向);② 防钓鱼方向(与 A3 OQ6 防钓鱼约束对账):雾板下界抬升不得使"弃图刷雾板"支配"即刷"——逐板奖励差分 × 最低雾出现率 × 8 RAP 刷新会构造出 A3 OQ6 明令防的刷新钓鱼环,解法(关卡层常量锚 / 防钓鱼不等式 / 与 OQ6 显式对账)归 B4/B1。 +- playtest 弃图率/完成度指标雾板分层观测(注记回执 A3 OQ3——聚合指标会被雾板污染或反向掩盖)。 + +## 状态 + +| 阶段 | 载体 | Owner | 行为 | +|---|---|---|---| +| 定义(构建期) | VariationTable .tres | D2 承载 / A4 回填 | ConfigDB 加载校验;热重载走 D2 既有门(战斗中拒绝,EC8) | +| 布置(生成期) | 棋盘格变体属性 | A1 | 生成新扫雷图时经 board_gen 流取号;unlock_level 门控候选池 + cell_constraint 强制 + 出生区排除 | +| 生效(执行期) | 格属性只读查询 | A2 / D1 消费 | 开格时读"是否雾格"决定数字显示与否;零运行时写入 | +| 消亡(随图) | 棋盘实例销毁 | A2 | 清图换新图 / 刷新 / 战斗结束——变异属性随棋盘实例消亡,无跨图存续 | + +不变式:A4 无运行时状态机——变异是棋盘格的只读属性,随图生成、随图消亡;生效期零运行时写入、无跨图存续。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 候选池为空(雾未解锁如 level 1,或 biome_tag 全不匹配) | A1 静默布置普通数字图——空池合法、非错误、无 warning(两种来源同待遇) | F1 Output Range / 验收⑥ | +| EC2 cell_constraint 无解(非 0 安全格面积或连通分量尺寸不足以布置任何雾区——图太小 / 雷太密 / 雷太稀致雷膨胀皮肤碎裂 / 参数要求过高;判据含连通性而非仅总面积——雾域恒为雷集边界带,总面积充足但碎裂成小分量时同样无解) | 本次布置雾的尝试失败 → 跳过雾布置,本图为合法无变异图(A1 生成算法的降级产物);禁止放宽约束硬塞——Rule 3 承重规则不为布置成功率让步;部分达成契约:BoardGenTable 要求 N 区而仅达成 k 小人 > 旗 > 变异纹理 > 底色——雾纹理不得干扰雾外数字辨识;已开雾格 = 地板无字(不留雾残留遮蔽) | D1 | +| 3 | 调色板交换优先(视觉锚 4) | 雾纹理须支持 biome 色板变体(biome_tag 字段的换皮对应)——新 biome 不新增雾资产,只换色板 | art-bible | +| 4 | 边界可读性 = 硬指标 | 未开雾格的区域边界在任何色板下必须可读——"明处的选择"幻想的视觉前提(P1 诚实边界的呈现面),与"数字可读性 × 暗色氛围"样张测试(game-concept 风险登记第 1 条)同批验证;原型 v3 紫色迷雾格已验证基线可读性,正式色值/形状归 art-bible 格子样张测试定稿 | art-bible / D1 | +| 5 | "雾区无雷"知识的静态传达 | D1 MVP 验收两条 + 必含要点枚举(义务清单 D1-①②③)——D5 里程碑在 Alpha,雾在 MVP level 2 即首遇,D1 是 MVP 唯一活载体(对比 A1×15/A2×4,本知识此前零验收落点) | D1 | +| 6 | 已开雾格负空间与结束拍 | 已开雾格 = 地板无字(负空间呈现,防误读为 bug)+ 底色层 tint 残留与雾区全开一次性完成信号(D1 验收候选——"站在洞口前"承诺的结束拍与清图进度可读;tint 处于视觉锚 3 最低层,与视觉锚 2"不留雾残留遮蔽"的冲突口径:遮蔽禁的是遮挡数字/实体的雾残留,tint 是底色层标记不犯此条;转正与否及完成信号粒度(视觉合并域 = 信号单元,禁实例级双响)归 OQ8 裁决) | D1 | +| 7 | 结算与 HUD 负空间 | 结算界面无 A4 附加项(雾格奖励 = 普通安全格奖励,无独立归因类别——P2 标注归 B1);无 HUD 常显字段(变异是棋盘属性非资源,与 A3 的 AP/RAP 不同) | B1 / D1 | +| 8 | 动效与音频 | 雾格开出瞬间的"雾散"过渡(可选,MVP 不承诺)归 B5/D1 表现里程碑,非 A4 义务;无音频需求(雾开出音效归 D4 Alpha 期) | B5 / D4 | + +## Tuning Knobs + +### VariationTable schema 定稿(D2 回写义务——本 GDD 为回填 owner) + +| 字段 | 类型/范围 | 默认(fog 行) | knob | 说明 | +|---|---|---|---|---| +| variant_id | String(主键) | "fog" | ✗ | 变异标识;A1 候选池与 A2 查询的键 | +| display_name | String | "迷雾" | ✗ | 图例/教学用名(本地化归 D6) | +| unlock_level | int [1, 999] | 2 | ✓ | 首次可布置关卡序号(game-concept 解锁序列:普通数字→迷雾→岩浆/冰冻);本表为定义表(数值恒定)非进度成长表,键 = variant_id,进度语义承载于此列——D2 Rule 4 豁免,语义写入 schema 注释 | +| scope | enum {region, global} | region | ✗ | 作用域;global 仅预留枚举位(语义归 C3,MVP 不产生 global 行) | +| info_mask | enum {none, hide_numbers} | hide_numbers | ✗ | 信息规则:该变异隐藏什么;新信息规则走接口漂移 | +| cell_constraint | enum {any, safe_nonzero} | safe_nonzero | ✗ | 布置约束:允许落在什么格子上——Rule 3 承重规则的数据载体,A1 生成期强制执行 | +| biome_tag | StringList 可空 | [] | ✓ | 关联 biome 主题(AD 门禁换皮约束);空 = 全 biome 可用 | +| counter_item_ids | StringList 默认空 | [] | ✗ | 可穿透该变异的 A5 道具清单(预留,A5 期回填——探测=雾内数字显影);非平衡 knob(内容回填字段,与"结构冻结"归类一致) | + +### knob 效应与真 owner + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| unlock_level(fog 行) | 2 | 变异内容推迟,关卡间"解锁新变异"的推进感断裂(每关至少引入一个新元素的节奏被打破) | 教学压力前移——玩家尚未在纯数字环境建立推理信任即遇信息剥夺(P1 落差语义丧失;D5 教学须跟随实际值动态适配,EC9) | +| biome_tag(fog 行) | [](全 biome 可用) | 收窄出现范围——某些 biome 无雾,多样性下降 | ——(空集即基线,无"更低") | + +- 非 knob 字段(结构冻结):scope(MVP 恒 region——开放 global 是 C3 的语义义务而非调参)、info_mask / cell_constraint(fog 行值即规则定义本身,修改 = 改规则,须走本 GDD 修订流程而非调参)、counter_item_ids(A5 期内容回填,非平衡 knob)。 +- Knob 交互与真 owner 指向:迷雾的实际手感参数——区域数量 / 区域尺寸 / 雾面积占比 / 与雷密度的联动——全部在 A1 BoardGenTable((level_index, node_depth) 维度,A1 回填),那是难度曲线的函数;A4 的表只管"资格";禁止在两表间复制参数(单一事实源,D2 Rule 1);unlock_level 与 BoardGenTable 的难度轴正交(门控定"谁入池",表参数定"摆多大/摆哪"——systems-designer 审查确认无冲突)。 +- 跨 GDD 引用:A3 的 `ap_per_round`(8)与 `dig_ap_cost`(1)均归 A3 所有,本 GDD 不重复列出;雾致预算增量构成仅消费 dig_ap_cost(数值节注记),清图总预算的可用量口径归 A1/A3 契约。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)——A4 是定义层系统,冒烟域 = 定义层(schema / 门控谓词 / 校验);布置层 = A1 集成(15 项,义务清单 A1-①…⑮)· 执行层 = A2 集成(4 项,义务清单 A2-①…④)——deferral 以对侧登记为生效条件,A1×15 / A2×4 清单已同步 systems-index 门禁记录 | +| 致命用例协议 | 独立进程协议承 D2 AC-2b(runner manifest 逐条登记"调用→期望退出码";单进程内只做测试通道收集断言) | +| 夹具注入 | 经 `--data-root` 注入,五根齐备(3 根致命错误夹具 + 1 根 warning 夹具 + 1 根合法数据变体夹具,见下表) | +| 期望值推导纪律 | 由表值推导不写死——unlock_level 与 biome_tag 均为 knob,推导纪律适用两者(防 EC9 及 biome_tag 调参假失败) | +| 诊断卫生 | 每用例先 `reset_diagnostics()`(承 D2 冒烟协议,全局声明) | +| runner manifest | 四行退出码登记:dup→≠0 · oob→≠0 · mask→≠0 · global→0(biome_tag 根仅进程内测试通道消费、无退出码行,AC-3 前置断言其零诊断加载) | +| 豁免声明 | 空表/缺表/类型错类别由 D2 AC-2 同类别夹具承载,A4 不设专属夹具(loader 级类别、与表内容无关,D2 夹具可承载;空表情形另由 AC-1"恰含一行"间接兜底) | +| 悬空引用处置 | counter_item_ids 为 D2 schema 首个引用型字段——MVP 显式声明不校验悬空引用(字段恒空、A5 未回填);A5 GDD 回填首个道具 id 时补悬空校验 + 条件性夹具(义务清单 A5-1) | + +### 夹具清单 + +| 夹具根 | 内容 | 级别 | 服务 | +|---|---|---|---| +| variation_dup_key | 重复 variant_id;与 D2 AC-2 重复键夹具独立同型(A4 表首用,避免跨表耦合) | 致命 | AC-4 / AC-4b | +| variation_unlock_oob | unlock_level=0 | 致命 | AC-4b | +| variation_mask_constraint | info_mask=hide_numbers + cell_constraint=any 跨字段违规(EC4"级联止于雾格"承重保证的测试落点) | 致命 | AC-4 / AC-4b | +| variation_global_scope | scope=global 行 | warning 级、非错误(游戏通道 0 退出) | AC-6 | +| variation_biome_tag | 合法门控测试行:biome_tag=[ice]、unlock_level=1 | 合法(无对应校验类别) | AC-3 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 干净检出(默认表根) | 冒烟加载 Core Rules-3 表清单 | VariationTable 加载成功、恰含 fog 一行(无岩浆/冰冻行——内容事实断言);fog 行非 knob 六字段(variant_id/display_name/scope/info_mask/cell_constraint/counter_item_ids)与 schema 定稿值逐一相符("fog"/"迷雾"/region/hide_numbers/safe_nonzero/∅);unlock_level 与 biome_tag 两个 knob 字段仅作类型域断言、期望 = 表读值(不作定值断言——合法调参不得假失败) | Core Rule 8 / 义务清单 | +| AC-2 | fog 行表值 (u, tag=∅)(默认行),board_biome 任意非空集(全腿作用于干净检出默认表根——仅 fog 一行) | 对 (level_index=u) 求 pool;仅当 u>1,再对 (level_index=u−1) 求 pool | level_index=u 时恒含 fog;u>1 时 u−1 腿 pool=∅(u=1 时 u−1=0 越出 level_index≥1 域、不测——F1 前置域);tag≠∅ 交集推导分支由 AC-3 夹具腿承载(默认表根仅 fog 一行、tag 恒 ∅,该分支在默认根不可构造;夹具根上 ice 行 unlock_level=1、u−1 腿不真) | F1 | +| AC-3 | 前置断言:夹具根零诊断加载(每用例 reset_diagnostics() 后加载,防 schema 演进劣化静默);eligible(v, L, B) := v ∈ pool(L, B)(F1 谓词的成员判定形式)。第一腿:fog.biome_tag=∅(默认行);第二腿:夹具根 variation_biome_tag(测试行 biome_tag=[ice]、unlock_level=1 写入夹具规格——防门控腿掩蔽 biome 腿,并承载 tag≠∅ 交集推导语义) | 第一腿在 level_index=u(fog.unlock_level 表读值)以任意非空 board_biome(单/多元素集各一)求 eligible;第二腿在 level_index=1 以 board_biome={dungeon} 与 {dungeon, ice} 分别求 eligible(v=测试行) | 第一腿恒 true(通配语义——∅ 不因 board_biome 取值失败);第二腿 false / true(单元素 {dungeon} 不交集 → false;{dungeon, ice} 交集 → true) | F1 通配与交集 | +| AC-4 | 夹具根 variation_dup_key 与 variation_mask_constraint | `load_tables(root, collect_only=true)`(测试通道) | 错误清单各含重复键类别 / 跨字段校验类别(hide_numbers⇒safe_nonzero) | schema 校验 | +| AC-4b | 夹具根 variation_dup_key、variation_unlock_oob(unlock_level=0)与 variation_mask_constraint | 各以独立 headless 进程跑游戏通道 collect_only=false | 三进程均非 0 退出且错误清单完整(D2 Rule 6 加载期致命——"游戏内真的会死") | schema 校验致命 | +| AC-5 | schema 实例构造(实例化一律走 class_name 直引、不经 load()/preload()——AC-7 扫描豁免条款;程序构造免夹具) | `validate()` 作用于 unlock_level ∈ {0, 1000};∈ {1, 999} | {0, 1000} 均域错误;{1, 999} 均合法 | 域校验 | +| AC-6 | 夹具根 variation_global_scope(每用例先 reset_diagnostics()) | 测试通道加载;同夹具游戏通道独立进程 | 诊断收集器 warning 恰一条且来源类别 = global 语义注记、其余表零诊断(防他表 schema 演进以无关诊断稀释断言);游戏通道以 0 退出(非致命半句) | EC7 | +| AC-7 | A4 文件集(schema/谓词/冒烟脚本) | 按 D2 AC-9 模式静态扫描(`randomize()` / `RandomNumberGenerator` / ConfigDB 外 data 表加载) | 零命中(扫描域扩入 A4;schema class_name 直引实例化豁免——AC-5 程序构造前提,承 D2 AC-9 模式) | Core Rule 8 | +| AC-8 | ConfigDB 加载完成 | 脚本解析 `design/registry/entities.yaml` 的 `variation_table` 条目 | 与 ConfigDB VariationTable 8 字段定义(字段名/类型/域/knob 标记)逐一相等;定值断言仅限非 knob 字段,knob 字段(unlock_level/biome_tag)对账口径 = registry 随调参同步或本 AC 豁免其值(防 EC9 调参经此后门假失败);对账域显式排除 `mvp_content` 与 `fog_ap_note`(前者含 knob 活值登记);缺任一即失败(仿 A3 AC-10a) | D2 回写义务 | +| AC-9 | (人工·视觉)边界可读 / 双通道 / 色板变体 | 归 art-bible 格子样张测试(AD 门禁),非冒烟域——阈值委托的排期门与违约判定挂 systems-index 门禁增补记录;art-bible 缺位期以原型 v3 紫色为临时明度基线,就位后强制复测 | 操作化口径("可读"须为可判定谓词,防形容词验收):① 形状通道 = 色板无关线稿资产 + 对全部底色的明度对比数值下限(阈值 art-bible 定、须存在);② 新增 biome 色板强制重跑样张清单(AD 门禁复测项);③ 样张须含最坏用例:宽度 1 雾带(每格即全边界——Rule 3 结构推论下的正常形态)、"旗 × 雾纹理 × 边界"三重叠加格、宽度 1 雾带两侧毗邻数字格(视觉锚 1"纹理不干扰数字辨识"的真实威胁面在邻格——雾格自身无数字) | 呈现契约 1/4 | +| AC-10 | (人工·字面量)A4 文件集 | 纳入 D2 AC-14 项目级扫描 | 人工三要素口径,引用不另设(与 A3 AC-10b 同型) | Core Rule 8 | + +## 开放问题 + +| # | 问题 | owner | 时机 | +|---|---|---|---| +| 1 | 雾面积/数量参数回填与 P1 验证:BoardGenTable 雾区域数/尺寸/占比默认值——须在厚度 ≤2(≡ 任意 3×3 窗口非全雾)/ f ≤ 8d 硬边界内定义,含最低雾出现率与降级率埋点口径;可推理率参数按雾边双向净效应建模;雾占比与可推理率的联动曲线(A1 的 P1 生成端保障核心 + 本 GDD 15 项验收的数值侧);占比参数双重身份:难度旋钮 × 情报旋钮——雾形携带雷簇位置信息(Rule 5 推理锚),占比/数量调参同时改变"雾 = 雷簇探测器"强度与首遇显著度(总占比/最大区域尺寸下限口径随 BoardGenTable 一并定,防首关雾低于注意阈值致 P4 解锁拍哑火) | A1 | 第 5 位 | +| 2 | 探测显影的时效与定价:EC6 留白——临时/永久显影、道具成本、counter_item_ids 首个回填 + 悬空引用校验补全(D2 条件性夹具义务兑现) | A5 | 第 11 位 | +| 3 | 创意风险 #1 的 B4 侧兑现:奖励预知的呈现方案(A4 侧切面已定——雾不额外隐藏内容);雾区奖励在效果当量对照表的落位——受三条定价硬约束(B4-①②③),成本口径按链不可达雾格计;①的 per-AP 平价与②③的定量谓词归 B4/B1 对账闭合 | B4 | 第 8 位 | +| 4 | scope=global 语义:精英/Boss 图全局变异的构成、施加时机、与零特判原则及 cell_constraint 覆盖的相容性重审(C3-1) | C3 | 第 12 位 | +| 5 | 岩浆/冰冻规则设计:变异框架的下一批居民、P3 雷险维度的承接者;需新字段时走接口漂移规则回写本 GDD 与 D2 | 垂直切片期 | 随关卡解锁建行 | +| 6 | 变异解锁曲线复核:"每关至少引入一个新元素"的推进节奏随 C3 难度曲线定稿复核;D5 教学动态适配表值(EC9),不写死解锁关号 | C3 + D5 | 低优先 | +| 7 | 标错惩罚与雾内旗:若惩罚上线,雾内旗恒受罚的一致性推论纳入实测观察项(EC5);EC5 推论在 A2 原型会话前置确认(防 D5 教学返工) | A2 前原型小补丁 | 联动 A3 OQ2 | +| 8 | 已开雾格 tint / 雾区全开完成信号的裁决:现处"验收候选"悬空态(本 OQ 登记终结悬空);裁决须同时覆盖——与 0 格的可判定区分(0 格 = "八邻无雷"推理信息,视觉混淆污染推理——tint 为当前唯一区分手段)与 EC11 合并呈现下的完成信号粒度(视觉合并域 = 信号单元,禁实例级双响) | D1 + CD | D1 GDD 前 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/义务台账/开放问题转表格,布置与揭示流程转字符画;F1 门控谓词、厚度 ≤2 / f ≤ 8d 结构推论、雾致预算增量公式本体与验算全保留;语义与 2026-09-08 定稿对齐,零规则变更 | +| rev2 | 2026-09-08 | 同日 re-review 六项 blocking 同日核销(CD 终裁建议 delta 核销免第三轮 full 评审,用户拍板照此放行——A3 先例):① systems-index 登记闭环补全(A1×15 全枚举——⑤扩链不可达占比生产者义务、⑬扩部分达成、⑭增批量统计断言形式条款,A2×4、D1×2、B4×3、B1 条款 5 输入,registry referenced_by 补 B1/B4);② knob 口径统一(counter_item_ids 非 knob 误标修正、AC-1 改双 knob 表读值断言、AC-8 排除 mvp_content/fog_ap_note);③ 校验与 AC 可执行性(新增第五夹具 variation_mask_constraint、manifest 第四行、AC-3 夹具根零诊断前置、AC-2 钉死默认表根);④ 修订残留失实清理("探路"改连锁路由 + 续付/退出两轴、雾格成本两态口径、厚度钉死 3×3 窗口谓词防对角带误拒、夹具计数改 3 致命 + 1 warning + 1 合法、预算改增量构成——原"≤ 轮数上限 × ap_per_round"不等式作废("轮数上限"在 A3 契约无定义、恒真空转)、Upstream 改零运行时接口消费);⑤ B4 约束簇(②改链不可达同分母口径——原"雾面积"分母脱钩作废、增③防福利上界、防钓鱼张力对账 A3 OQ6、①per-AP 平价注记);⑥ MVP 验收契约(D1 必含要点补"雾格不传播连锁"、首遇提示谓词改"首个含雾板进入可操作状态"——原"雾格首次与已开区相邻"作废(贴脸雾 t=0 即真、远雾图滞后)、一次性作用域 = 每 run、EC2 部分达成契约、A1⑤ 设计期聚合校验);顺带:D5 成本教学改玩家侧保守全价启发式、Rule 7 可达域保守上界 + A2 展开域 ⊆ A1 排除域回验、OQ1 占比双重身份、新增 OQ8、AC-9 补 art-bible 指针与宽度 1 雾带邻格用例——明细见 review log | +| rev1 | 2026-09-08 | full 独立评审(game-designer · systems-designer · qa-lead · economy-designer · level-designer · ux-designer 六专员 + creative-director 终裁)八项 blocking 同日核销:① 可推理率对账公理自相矛盾 + Rule 5 单向建模——改双向净效应、厚度 ≤2 / f ≤ 8d / ⌈k/8⌉ 结构推论入文(登记 A1 验收);② Player Fantasy"三选项"失实——改诚实版(雾 = 清图必经段、绕开 = A3 风险退出、买情报标注 A5 期)+ B4 两条定价硬约束 + P3 降格"信息切面"("清图判定豁免雾格"选项经裁决否决——违反零特判承重规则);③ "雾格全部全价"半假——改链不可达两态口径;④ EC2 判据扩连通分量 + 降级率埋点与最低雾出现率义务;⑤ EC3 预设 A2 未定义行为——Rule 7 可达域生成期确定性计算、邻接合法结论保留;⑥ AC 四处硬伤(AC-1 写死 unlock_level=2 等)+ deferral 闭环——A1 清单 11→15 扩容并登记 systems-index 门禁记录;⑦ EC11 定向粒度改 variant 行级、实例级定向归 A5 期新字段;⑧ "雾区无雷"教学零 MVP 落点——登记 D1 MVP 验收两条 + AC-9 操作化;顺带:hide_numbers⇒safe_nonzero 跨字段校验、EC10 升格参数域拒绝、已开雾格 tint 完成信号候选、"跨雾推理"措辞降调——明细见 review log | +| v1 | 2026-09-08 | 初稿定稿:承重规则"生成期布置约束(雾区仅非 0 安全格)+ 零特判原则 + fog unlock_level=2"用户拍板;lean 门禁 systems-designer 审查 Formulas(排除声明成立、谓词改集合语义、补下游验收项与预算构成注记)、qa-lead 审查 Acceptance Criteria;评审日志 design/gdd/reviews/a4-terrain-variation-review-log.md | diff --git a/design/gdd/a5-minesweeper-aux-items.md b/design/gdd/a5-minesweeper-aux-items.md new file mode 100644 index 00000000..7de48e65 --- /dev/null +++ b/design/gdd/a5-minesweeper-aux-items.md @@ -0,0 +1,472 @@ +# A5 扫雷辅助道具(Minesweeper Auxiliary Items) + +> **状态**:Approved · **版本**:v2 · 2026-09-14 +> **一句话**:挖掘期辅助消耗品——板级信息叠加层与 AP 增量,获取挂 B4、使用经 B1。 +> **推演件**:`design/gdd/analysis/a5-minesweeper-aux-items.md`(本文件只收结论,理由在那里) +> **承载支柱**:P1 主(信息道具 = 推理原料的恢复、替代推理窄域化)· P2 · P3 · P4 · **层级**:Layer 2.5 Core · Milestone 垂直切片(里程碑首位,设计顺序第 11 位) + +## 设计目的 + +A5 是挖掘期的信息与行动力市场:道具从 B4 奖励池获取(安全格奖励的一部分),在挖掘阶段由玩家主动消耗——雾区数字显影恢复推理原料、雷位真视与奖励预读把未知变成规划素材、AP 增量扩大当轮行动预算;效果即时可见(叠加层直接改变棋盘呈现),不进结算明细。没有 A5:雾区应对只剩停手/弃图两轴,创意风险 #1 缺未开格奖励身份的道具侧预读,"纯猜局面用道具缓解"无兑现通道;A5 同时是 B4 OQ2 与 A4 OQ2 的收口 owner。 + +1. 推不出的地方,我能买回线索——显影恢复雾格数字(恒真透传,非新信息源);真视把"代答"锁在 3×3 救急档;道具零随机、不引入新赌点。 +2. 这瓶显影尘是我上一轮捡到的——获取 = B4 奖励池 aux 行(来源 {cell, round} 逐件归因);使用 = 挖掘期自主时点、效果即时可见;经真视插旗兑付的 weaken 走既有 flag_hit 结算行——归因链不断。 +3. 买情报的代价是放弃一件战利品——aux 行挤占战斗奖励池权重;叠加层随图消亡,弃图刷新时已花显影沉没;真视≠自动兑付(插旗仍花 1 AP)。 +4. 地牢里前人留下的工具——主题命名(显影尘/探巢摆钟/地穴全图卷);item_id 直作图标键(形状+色相双通道),程序化占位先行。 + +## 系统范围 + +负责: + +- AuxItemTable schema(D2 承载、A5 回填 owner,键 = item_id——定义表非进度成长表) +- 五维度框架与覆盖断言(F2)· MVP 六行内容(值 provisional 归 C3) +- 板级叠加层容器(显影/真视/预读三集)与关卡域库存(消耗与库存规则) +- 使用目标校验规则(谓词表)· 获取与使用全流程规则 + +不负责: + +- 奖励池与权重所有权(B4——aux 行值 provisional 归 C3) +- 棋盘状态与数字(A2——恒真透传) +- AP 账本(A3——grant_ap 为接口漂移预授权) +- 变异定义(A4——counter_item_ids 为 A4 字段、A5 消费回填) +- 结算段序与使用编排调用权(B1) +- 呈现(D1)· 教学文案(D5)· 本地化(D6)· 图标美术(art-bible) + +全局硬约束:零随机(零 RNG 流消费)· 零特判延伸(A2 代码零 A5 分支——叠加层为派生信息层、零回写 A2/B4 状态)· 真视窄域护栏(雷位真视 ≤3×3、全图级仅限显影/预读类)· 无效/零信息目标不可用不消耗 · 库存关卡域 · Used 不可逆。 + +## 核心循环 + +``` +获取(install 期身份预分配——B4 域) + B4 roll_board → 格奖励身份含 aux 行(category=aux_item 激活后) + ▼ +挖掘期开出资格格 → 结算授予段 B1 grant(cell) + │ 返回 category=aux_item → B1 路由 A5.add_item(不入 B4 库存——两库存正交) + ▼ +A5 关卡域库存(跨轮/跨图/跨战斗延续;D1 常读条) + ▼ +使用(挖掘阶段·Active 相位,D1 使用意图 → 组合根 → B1 编排——唯一调用权) + │ ① 相位 gate(非 Active → 拒绝) + │ ② A5.validate_use(handle, target)——目标校验读 A2 只读面 + │ 失败 ⇒ 全链拒绝、零消耗(无效/零信息目标反馈 D1) + │ ③ 逐 kind 效果应用(顺序钉死:空间 kind 先、ap_grant 后): + │ 空间 kind → A5.apply_overlay(板级叠加层写入) + │ ap_grant → A3.grant_ap(当轮 remaining_ap += magnitude) + │ ④ A5.consume(handle)——Used 不可逆、回执 = 快照投影 + 来源 + ▼ +D1 命令式回执(重查 query_inventory / query_overlays 刷新呈现) + ▼ +消亡:board retire(cleared 换图 / RAP 刷新 / 战斗结束) + → B1 编排 A5.retire_overlays(与 B4 retire_mapping 同点) + 关卡结束 → B1 编排 A5.clear_inventory(与 B4 clear_inventory 同点 + 清空反馈计数) +``` + +| 出口 | 触发 | 动作序列 | 约束 | +|---|---|---|---| +| 正常使用 | Active 相位 + 有效目标 | validate → apply_overlay / grant_ap → consume → 回执 | 序即原子(apply/grant 无失败域 → consume 恒达) | +| 目标拒绝 | validate_use 失败(无雾可显/坐标越板/预读窗口零未开资格格等) | 全链拒绝 | 零消耗、库存不变、D1 反馈可辨 | +| 相位拒绝 | 非 Active(Waiting/结算中) | 相位 gate 拒绝 | 零消耗;与 B4 道具窗口两输入面互不竞争 | +| 随图消亡 | cleared 换图 / RAP 弃图刷新 / 战斗结束 | retire_overlays | 已花道具不返还(信息投资沉没——设计意图) | +| 关卡清空 | 关卡结束(victory/defeat 皆然) | clear_inventory | 恰一次幂等;清空前快照计数供反馈行 | + +## 道具定义(维度·schema·内容) + +规则: + +1. 规则 1:道具定义表里的每一件道具,必须标出它属于哪类帮助——情报、雷险、行动、规划、反制,至少标一类;五类每种至少有一件道具,游戏加载数据后自动检查;游戏运行时完全不读这个标签(代码零维度分支)。 + +### dimension_tags(五值封闭枚举) + +| 值 | 语义 | MVP 覆盖行 | +|---|---|---| +| info | 情报维度——推理原料的恢复(雾格数字) | probe_dust / catacomb_scroll | +| risk | 雷险维度——惩罚格真视(直接雷位信息) | nest_pendulum / blast_kit | +| action | 行动维度——当轮 AP 增量 | sapper_order / blast_kit | +| plan | 规划维度——奖励身份预读(寻路素材) | loot_compass / catacomb_scroll | +| counter | 变异对抗维度——counter_item_ids 消费(A4 框架) | probe_dust(fog 行回填) | + +### AuxItemTable schema(D2 新表,A5 回填 owner;键 = item_id) + +| 字段 | 类型/域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| item_id | String(主键唯一) | — | ✗ | 道具标识;B4 RewardTable.effect_id(aux 行)与 VariationTable.counter_item_ids 的引用键 | +| display_name | String 非空 | — | ✗ | 玩家可读名(D1 呈现数据源;本地化归 D6——B2/B3/B4 同库惯例) | +| dimension_tags | StringList ⊆ 五值枚举 非空 | — | ✗ | 维度覆盖断言载体(F2);组合维度 = 稀有多维行的声明面 | +| effect_kinds | StringList ⊆ 四值枚举 非空、无重复 | — | ✗ | 效果组合(MVP ≤2——多维度结合的 schema 承载) | +| target | enum {none, fog_region, area_3x3, board_wide} | — | ✗ | 目标形态;校验 = target ∈ 各空间 kind 支持域交集(全非空间 ⇒ 恒 none);area_3x3 运行时解析 = 窗口 ∩ 板内格(边缘裁剪——加载校验域仍为整窗) | +| magnitude | int [0,999] | — | ✓ | 仅 ap_grant 行消费(= AP 点数,加载校验 ≥1——B4 item 行先例);空间行不消费非校验项 | +| use_phase | enum {active_dig} | active_dig | ✗ | 使用时机(MVP 单值;扩枚举走本 GDD 修订——B4 use_timing 扩值同批对齐) | + +### MVP 内容行集(六行——目标值,回填生效以 B4 修订批为前提;值 provisional 归 C3) + +| item_id | display_name | dimension_tags | effect_kinds | target | magnitude | RewardTable 权重/tier | +|---|---|---|---|---|---|---| +| probe_dust | 显影尘 | [info, counter] | [fog_reveal] | fog_region | — | w5 / t2 | +| sapper_order | 工兵令 | [action] | [ap_grant] | none | 2 | w5 / t2 | +| loot_compass | 探宝罗盘 | [plan] | [reward_preview] | area_3x3 | — | w4 / t2 | +| nest_pendulum | 探巢摆钟 | [risk] | [mine_reveal] | area_3x3 | — | w4 / t2 | +| catacomb_scroll | 地穴全图卷 | [info, plan] | [fog_reveal, reward_preview] | board_wide | — | w1 / t3 | +| blast_kit | 爆破工兵包 | [action, risk] | [ap_grant, mine_reveal] | area_3x3 | 4 | w1 / t3 | + +- 常规四行 = 单维中数值(tier 2 与现行池主力同档);稀有两行 = 双维组合 + 高数值(tier 3 超现行池最高档、magnitude 4 = 半轮预算)——稀有道具设计指令的直接兑现。 +- 维度覆盖:五维度各 ≥1 行(counter 由 probe_dust 承载并回填 VariationTable.fog.counter_item_ids——义务 A4×①)。 +- 全图卷组合校验:fog_reveal ∩ reward_preview 支持域交集 = {board_wide} ✓;爆破包空间 kind 仅 mine_reveal → target=area_3x3 ✓。 +- 六行均 pool_tag=safe_cell(aux 行为格奖励;tier 2/3 ≥ T_fog_min=2 → 雾池档位过滤自然兼容;clear_default 池不入 aux 行)。 + +### schema 校验类别(诊断封闭枚举) + +| 类别 | 级别 | 触发条件 | 语义 | +|---|---|---|---| +| 重复键 | 致命 | item_id 重复 | D2 Rule 6 既有类别 | +| 维度域外 | 致命 | dimension_tags 空 / 含枚举外值 | F2 覆盖断言的前提 | +| 效果域外 | 致命 | effect_kinds 空 / 含枚举外值 / 含重复 | 规则 2 | +| 目标交集违例 | 致命 | target ∉ 各空间 kind 支持域交集(全非空间行 target ≠ none 同类) | 规则 3 真视窄域护栏的强制面 | +| magnitude 域外 | 致命 | magnitude ∉ [0,999];ap_grant 行 < 1 | B4 item 行 power ≥ 1 先例 | +| use_phase 域外 | 致命 | use_phase ∉ {active_dig} | 规则 6 | +| counter 悬空 | 致命 | VariationTable.counter_item_ids 含 AuxItemTable 无此行 id | A4 悬空引用校验激活(义务 A4×②——A5-1 兑现) | +| aux 引用悬空 | 致命 | RewardTable aux 行 effect_id 在 AuxItemTable 无此行 | B4 悬空校验双表扩展(义务 B4×②) | + +设计意图:维度是内容规划的自查清单——保证五种帮助每种至少一件,玩家不管遇到什么困境都有得买。 + +## 使用效果与目标域 + +规则: + +1. 规则 2:玩家在挖掘阶段选中一件道具使用,道具做四件事之一,稀有道具同时做两件——显影(雾格数字现形,到这张图被换掉为止)、真视(圈定范围里雷的位置现形,同上)、预读(没挖开的格子预告里面的奖励,同上)、加行动力(当轮行动点变多,只对当前这一轮);加行动力 = 行动点 + 道具表数值,其余三种只是把格子记进"要额外显示"的名单——数字永远读棋盘真值,奖励身份永远读奖励表。 +2. 规则 3:凡是"告诉你雷在哪"的效果,一次最多照亮 3×3 格,任何道具都不能全图看雷;看数字(显影)和看奖励(预读)允许全图;限制写进数据校验——任何看雷道具的范围字段超过 3×3,加载数据时直接报错拒绝。 + +### 效果明细(规则 2 的实现参照——字段名与数据源) + +| kind | 效果 | 目标域支持 | 时效 | 零信息判据(不可用条件) | 呈现数据源 | +|---|---|---|---|---|---| +| fog_reveal | 未开雾格数字显影(恢复推理原料——A4 EC6 语义定稿) | fog_region / board_wide | 本图持续(叠加层) | 全板无未开雾格(板级 → 图标灰) | A2 板面恒真数字 + digit_reveal 格集 | +| mine_reveal | 雷格真视(惩罚格现形——真视≠自动兑付,插旗仍花 1 AP) | area_3x3(护栏上限) | 本图持续 | 恒有信息(零雷 = "这 9 格没雷"——恒可用) | mine_reveal 格集(标记呈现,art-bible 键契约) | +| reward_preview | 未开资格格奖励身份预读(寻路素材——创意风险 #1 兑现) | area_3x3 / board_wide | 本图持续 | board_wide:全板无未开资格格(板级 → 灰);area_3x3:窗口内零未开资格格(点击级 → 拒绝) | B1 query_reward_identity 既有读面 + reward_preview 格集 | +| ap_grant | 当轮 AP 增量(remaining_ap += magnitude;溢出停手转存 RAP 走 A3 既有规则零特判) | none(非空间) | 当轮一次性 | 恒可用 | A3 AP 读数(既有命令式刷新面) | + +### 目标域开放表(规则 3 护栏) + +| 效果 | 允许的范围 | +|---|---| +| 显影(看数字) | 一个雾区,或全图 | +| 预读(看奖励) | 3×3,或全图 | +| 真视(看雷) | 只许 3×3,全图禁止 | + +设计意图:恢复推理原料的效果可以大,代替推理的效果必须小而贵——花钱全图看雷等于把游戏换成走格子。 + +## 获取与库存 + +规则: + +1. 规则 4:玩家挖到带道具的格子时,战斗结算系统(B1)把道具放进辅助道具的关卡背包;它和战斗道具背包(B4 管)是两个独立的包,互不混装,但寿命一样——都活到关卡结束。 +2. 规则 5:背包里的辅助道具跨轮次、跨地图、跨战斗都在,直到整个关卡打完(无论输赢)由 B1 一次性清空;每件都记录从哪个格子的哪一轮捡到,结算能归因来源;用一件少一件、不能反悔;背包不设上限,稀缺靠掉落权重控制(供给期望见 F1)。 + +### 状态 + +| 容器/条目 | 状态 | 进入 | 出口 | +|---|---|---|---| +| 库存条目 | InStock → Used → Cleared | add_item 授予 → InStock;consume → Used(不可逆) | 关卡结束 → Cleared(clear_inventory;跨战斗/跨图延续同 B4 库存) | +| 板级叠加层 | Empty → Active → Retired | 首次 apply_overlay → Active(三集条目累积) | retire_overlays → Retired(cleared 换图/RAP 刷新/战斗结束——随图消亡,不跨图) | + +不变式:Used 不可逆;库存条目仅存关卡域;叠加层随图消亡且零回写 A2/B4 状态(派生信息层);A5 全 API 零随机。 + +设计意图:信息投资随图沉没——已花道具不返还是设计意图而非疏漏。 + +## 使用规则与校验 + +规则: + +1. 规则 6:玩家只能在挖掘阶段(轮次进行中)使用辅助道具,任意时点可以,用一件消耗一件;结算阶段用不了——那个窗口属于战斗道具,两个"用道具"的入口在时间上错开,不会冲突。 +2. 规则 7:玩家发起使用后,B1 按固定顺序执行四步——相位检查 → 目标校验 → 产生效果 → 消耗;所有会失败的情况都在前两步被拦下,后两步在合法输入下不会失败,所以"产生效果"和"消耗道具"永远成对发生;带两个效果的稀有道具施加顺序钉死:先空间效果(显影/真视/预读)、后加行动力——定义表里效果的书写顺序不承载运行时语义。 +3. 规则 8:目标不合法时道具不白白消耗,分两级——整张图都没东西可看时(板级零信息),背包里这件道具直接置灰、根本选不了;单个点击位置上没东西可看时(点击级零信息),那一次点击被拒绝、给可辨认的提示;两种情况道具都留在背包。 +4. 规则 9:用道具不扣 AP——成本已经付过了(捡到它,那个格子就没出战斗奖励);挖格、插旗的价格不受任何影响。 +5. 规则 10:A5 不监听事件、不画界面、不用随机数、不改任何其他系统的数据;全部信息来自三个已经存在的事实来源:棋盘数字真值、奖励身份、行动点账本;其他系统的代码里不会有任何一行"为了 A5"而写的判断(机器验收见 AC-14)。 + +### 使用编排分支(规则 7 的实现参照) + +| 分支 | 条件 | 动作 | 拒绝语义 | +|---|---|---|---| +| validate 失败 | 无效目标/Used 句柄/Cleared 库存 | 全链终止 | 零消耗 + 诊断(D1 反馈可辨) | +| 空间 kind | resolved_target 域内 | apply_overlay 逐 kind 写入 | 域内输入恒受理(validate 前置保证) | +| ap_grant | magnitude ∈ [1,999] | A3.grant_ap(magnitude) → remaining_ap 增 | 域内恒受理(AP 无上限域——A3 漂移条款) | +| consume | validate 已过 | Used 标记 + 回执 | Used 不可逆(重复 → false + 诊断) | + +### 使用目标校验谓词(规则 8 的实现参照) + +| 谓词 | 条件 | 裁定 | +|---|---|---| +| 中心格越板 | area_3x3 目标中心格 ∉ 板 | 拒绝、零消耗 + 诊断 | +| 边缘窗口 | area_3x3 中心格贴边/角(窗口探出板外) | 照常受理——resolved_target = 窗口 ∩ 板内格(角 4 格 / 边 6 格);真视窄域护栏不破(≤3×3 只缩不扩) | +| 板级零信息——显影类 | 全板无未开雾格(含无雾板降级产物) | 不可用:库存投影 usable=false、D1 图标灰、使用入口禁用;强行调用 validate_use → false + 诊断零消耗(EC2 同判据) | +| 板级零信息——预读类 | 全板无未开资格格(board_wide) | 同上 | +| 点击级零信息——预读类 | 所选窗口 ∩ 板内格中零未开资格格(area_3x3) | 该次拒绝、零消耗、D1 反馈可辨(拾取预览高亮将受影响格,辅助避空) | +| 真视恒可用 | 零雷窗口 = "这 9 格没雷"(信息非空) | 无零信息拒绝——仅中心格越板拒绝 | +| 组合行可用性 | 各 kind 判据取 OR(任一 kind 有信息即可用) | 爆破包因 ap_grant 恒可用;全图卷 = 有未开雾格 ∨ 有未开资格格 | + +- 可用性预判数据源 = A2 只读面(板面/已开状态/雾区身份 + reward_eligible 资格标记——读接口白名单同批扩列,缺口预检 1);A5 零事件订阅 → 板级判据随挖掘进展变化,usable 按 query 时点现算(派生信息层零缓存义务)。 + +### 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 无效目标(fog_region target 时板内无未开雾区;area target 中心格越板;area 预读窗口内零未开资格格) | validate_use → false + 诊断,全链拒绝——零消耗、库存不变;板级判据另经库存投影 usable=false 前置呈现(图标灰) | 规则 8 · 谓词表 | +| EC2 无雾板(L1 或降级产物)上用显影尘/全图卷 | 同 EC1 拒绝不消耗——道具留存关卡域库存,后续雾板可用(雾 unlock_level=2 的跨关持有合法) | 规则 8 / A4 F1 | +| EC3 显影/真视后 RAP 弃图刷新 | 叠加层随旧板消亡(retire_overlays)、已花道具不返还——信息投资沉没(A3 旧板旗随旧板消亡同款先例);玩家侧含义 = 别在待弃板上花情报 | 状态表 / A3 rap_rules | +| EC4 显影格被开出 | 叠加层条目留存无害——已开格数字恒显示(A2 恒真),呈现自然合并、A5 零清理维护(零事件订阅的结构福利) | 规则 10 | +| EC5 真视雷格被踩(挖开) | 事实照旧——真视 = 信息非保护,雷→伏击照常;玩家侧价值 = 预先规避或插旗兑付 weaken | 规则 2(mine_reveal 语义) | +| EC6 真视格插旗 | 正常 1 AP 插旗、轮末判定标中——weaken 走既有 flag_hit 路径(P2 归因链完整) | A3 flag_rules | +| EC7 预读格的映射 Retired(换图/战斗结束) | 叠加层同批消亡(retire_overlays 与 B4 retire_mapping 同点编排)——预读徽章不会指向已消亡映射 | 状态表 / B4 E6 | +| EC8 Waiting 相位(结算中)使用意图 | 相位 gate 拒绝零消耗——A5 仅 Active;与 B4 结算窗口使用两输入面互不竞争 | 规则 6 | +| EC9 clear_inventory 后 use/validate | false + 诊断(Cleared 逻辑失效态——B4 E7 同款幂等守卫) | 状态表 | +| EC10 同一件道具重复选中 | validate_use 对 Used 句柄 → false + 诊断(Used 不可逆——B4 E9 同款) | 状态表 | +| EC11 组合行(爆破包/全图卷)部分效果失败 | 结构上不可达——validate 前置拦下全部可失败输入,apply_overlay/grant_ap 域内无失败路径;规范序(规则 7)即原子性证明 | 规则 7 | +| EC12 ap_grant 后停手 | 溢出 AP 按停手路径 1:1 转存 RAP(A3 既有规则零特判——道具增量与基础 AP 同待遇);cleared 轮剩余 AP 不折算同款(A3 既有清图规则零特判) | A3 rap_rules | +| EC13 VariationTable.counter_item_ids 悬空引用 | D2 加载期致命(校验随 AuxItemTable 建表激活——A5-1 兑现;条件性夹具 a5_dangling_counter) | 校验类别表 | +| EC14 RewardTable aux 行 effect_id 悬空 | D2 加载期致命(dangling_aux_item 新类别——B4 悬空校验双表扩展) | 校验类别表 | +| EC15 战斗中热重载 AuxItemTable/RewardTable | D2 既有门(战斗中 reload 拒绝);库存实例持行值快照(B4 E14 同款);叠加层为运行时态不涉表重载 | D2 / B4 E14 | +| EC16 aux 行在无雾板被 roll 中 | 正常入库——获取与可用性解耦(池不按雾有无分列,雾档过滤仅作用于雾格身份——B4 F2 域);EC2 承载使用侧 | B4 F2 / EC2 | +| EC17 area_3x3 中心格贴边/角(窗口探出板外) | 照常受理——resolved_target = 窗口 ∩ 板内格(角 4 格 / 边 6 格),道具照常消耗;真视窄域护栏不破(只缩不扩) | 规则 8 · 谓词表 | +| EC18 零信息可用性门(板级判据随挖掘进展翻转) | 板级判据(无未开雾格 / 无未开资格格)→ 库存投影 usable=false、D1 图标灰、使用入口禁用;点击级判据(area 预读窗口空)→ 拒绝零消耗;组合行判据取 OR——爆破包 ap_grant 恒可用、全图卷 = 有雾格 ∨ 有资格格;A5 零事件订阅,usable 按 query 时点现算 | 规则 8 · 谓词表 | + +设计意图:点错了不扣道具——挖掘期玩家可能手滑点错,不该为此受罚。 + +## 数值结论 + +口径总纲:A5 零平衡公式——池权重/tier/magnitude 全部 provisional 归 C3;本节三条 = 供给渗透(F1)、覆盖断言谓词(F2)、信息价值方向预检(F3,方向性非定量)。推导与论证见推演件。 + +### F1 aux 渗透供给(aux_supply_rate) + +**结论**:默认根下 aux 道具期望 ≈1.7 件/板(\|资格格\|=10 口径)、稀有件 ≈0.17 件/板(≈每 6 板一件)——渗透率 ≈16.7%(战斗类 83.3%),供给面有界。 + +``` +E[N_aux/板] = |U| × (Σw_aux / W_total) +E[N_rare/板] = |U| × (w_scroll + w_blast) / W_total +默认根:Σw_aux = 5+5+4+4+1+1 = 20;W_total = 100(B4 原池)+ 20 = 120 +|U| = 10(B4 direction_precheck 保守板口径) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 资格格数 | \|U\| | int | 板级 | install 期资格格集基数(B4 义务⑥供清单;分布缺口待 A1 actuals——B4 预检 11 同款) | +| aux 行权重和 | Σw_aux | int | — | 六行 w 之和(provisional 归 C3) | +| 池总权 | W_total | int | — | safe_cell 池全部活行权重和(aux 行入池后重算——B4 F1 联动重算义务) | + +### F2 维度覆盖谓词(dimension_coverage) + +**结论**:coverage(d) :⇔ ∃活行 aux(d ∈ dimension_tags ∧ RewardTable 行 weight>0)——五维度 coverage 恒真为加载后内容审计断言(AC-12);维度是内容标尺非运行时逻辑(代码零维度分支)。 + +``` +coverage(d) :⇔ ∃r ∈ AuxItemTable : d ∈ r.dimension_tags ∧ reward_row(r.item_id).weight > 0 +断言 ∀d ∈ {info, risk, action, plan, counter} : coverage(d) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 维度 | d | enum | 五值 | dimension_tags 值域 | +| 活行 | r | 表行 | — | AuxItemTable 行 ∧ 对应 RewardTable 行 weight>0 | + +### F3 信息价值方向预检(info_value_direction——方向性非定量) + +**结论**:显影的价值 = 残余盲猜端收缩——可推理格增加 → 玩家盲赌次数(暴露)下降 → 期望踩雷下降(P1 正向);引用式 P(雷) = \|M\| ÷ (\|M\| + (1−ι)×\|S\|) 对 ι 递增——收益在"少赌",不在"单赌更安全"。真视把 k 格从决策域移除(必避/必旗候选)。两者均为正向信息增量、定量当量换算归 B1 条款①对照表扩列(义务 B1×④注记,MVP 不入对照表)。 + +``` +显影:fog_reveal k 格 → 板级 ι(免赌开据占比)上移 + → 残余盲猜安全格 n_s = (1−ι)×|S| 收缩 → 盲赌次数下降 → 期望踩雷 ↓ + 单次盲赌条件概率 P(雷) = |M| ÷ (|M| + n_s) 随 n_s 收缩而上行 + (与 B1 F4 同式,B1 F4 另设 n_s < 1 适用域守卫) +真视:mine_reveal k 格 → 该 k 格移出"盲挖候选域" + 期望收益 = P(雷|盲) × (伏击暴露期望 − 标中 weaken 收益期望) 的差值锁定 + 精确当量归 B1 对照表(MVP 不闭合——间接效应如实登记) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 可推理率 | ι | float | [0,1] | A1 板级实际值(actuals p10 为对账口径——registry 既有) | +| 雷数/安全格数 | \|M\|/\|S\| | int | 板级 | A2 板面事实 | +| 真视格数 | k | int | ≤9 | area_3x3 护栏上界 | +| 残余盲猜安全格 | n_s | float | [0, \|S\|] | (1−ι)×\|S\|——B1 F4 同名变量(n_s < 1 适用域守卫同款) | + +## 依赖与输出 + +| 方向 | 系统 | 性质 | 接口要点 | +|---|---|---|---| +| 上游 | D2 | hard | AuxItemTable 新表 schema 承载与 ConfigDB 读取(本 GDD 回填 owner——走 D2 接口漂移规则回写 D2 表清单);校验类别族见道具定义节 | +| 上游 | A2 | hard | 只读面消费(板面/已开状态/雾区身份——目标校验与显影数据源);A2 侧零改动零义务(零特判延伸——A2 代码零 A5 分支) | +| 上游 | B4 | hard(修订批) | aux_item category 激活(预留枚举兑现)+ effect_id 双表校验 + use_timing 扩值 + 默认根六行 + 池总权重算——义务清单 B4×①…⑤ 缺项即 blocking | +| 上游 | A3 | soft(接口漂移预授权) | grant_ap(n) 新入口(第 9 API):Active 相位受理、remaining_ap += n(无上限域)、其他相位拒绝、零随机——A3 GDD 修订批登记(义务 A3×①) | +| 上游 | A4 | 内容契约 | counter_item_ids 消费与回填(fog 行 = [probe_dust]);未来变异建行时各配 ≥1 反制道具(框架级"每变异 ≥1 反制");A4 OQ2 由本 GDD 收口(时效 = 本图持续、定价 = 池权重) | +| 上游 | B1 | hard(编排) | use_aux 编排 + 生命周期编排 + 授予路由——义务清单 B1×①②③ 缺项即 blocking(B1 是全部战斗域状态变更的唯一编排方) | +| 下游 | D1 | soft | 呈现义务 4 项(缺项即 blocking,见义务清单)——aux 库存条/使用交互/叠加层三集呈现/清空反馈;叠加层数据源 = query_overlays 只读投影 | +| 下游 | D5 | soft | 教学候选:"买情报"闭环(捡到道具→需要时用→情报换安全)+ 真视≠保护的措辞口径 | +| 下游 | art-bible | soft | §4.6 图标键扩容:item_id 六键 + 叠加层标记三族(显影/真视/预读徽章——预读徽章与 B4 已开格徽章同族) | +| 下游 | B5/D4 | soft | 道具使用反馈(后置里程碑);D4 音频挂点(Alpha——使用音/无效反馈音) | +| 下游 | registry | 确认 | a5_aux_items 条目登记(本 GDD 定稿回填)+ variation_table.mvp_content counter_item_ids 注记更新 | + +### 跨 GDD 义务清单(对侧 GDD 修订批逐项核对落位,缺项即 blocking) + +> 义务编号以本 GDD 为源;跨 GDD 台账登记时采用「A5→对侧×①」前缀形式(B4→B1×①…⑥、B1 义务账本 B4×①…⑥ 为既有同形编号——前缀防混淆)。 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| B1×① | use_aux 编排入口(唯一调用权):Active 相位 gate(A3 phase getter)→ 规范序 validate→逐 kind 应用→consume;拒绝路径零消耗反馈 | B1 修订批 | 已登记 | +| B1×② | 生命周期编排:board retire(cleared 换图/RAP 刷新/战斗结束)→ A5.retire_overlays 与 B4 retire_mapping 同点;关卡末 → A5.clear_inventory 与 B4 clear_inventory 同点 + 清空反馈计数 | B1 修订批 | 已登记 | +| B1×③ | 授予路由:grant 返回 category=aux_item → A5.add_item(与 B4 stock 分流对偶——每格受理恰一次承接延伸) | B1 修订批 | 已登记 | +| B1×④ | 注记级:aux 信息价值不入条款①对照表(间接效应)——对照表扩列义务登记调参期(F3) | B1 修订批 | 已登记(注记) | +| B4×① | category 枚举扩值 aux_item(五值→六值——Rule 1 修订);交付轴归类 = A5 可选类(不入 B4 库存、经 B1 路由) | B4 修订批 | 已登记 | +| B4×② | effect_id 消费扩展:aux_item 行必填(AuxItemTable 精确引用)+ 悬空校验双表 + 条件性夹具 dangling_aux_item | B4 修订批 | 已登记 | +| B4×③ | use_timing 枚举扩值 {pre_settlement, during_dig}——aux 行 = during_dig(game-concept"使用时机"字段语义延伸) | B4 修订批 | 已登记 | +| B4×④ | 默认根行集 +6 aux 行(本 GDD MVP 行集表值回填;F1 池总权重算 + direction_precheck 联动重算;并按 B4 缺口预检 6 流程重制登记常数与受影响 AC——σ 黄金轨迹 Ψ/Φ、AC-18 category 值域内容断言等随批重推导,防静默假失败) | B4 修订批 | 已登记 | +| B4×⑤ | OQ2 收口注记:未开格身份呈现深度 = 道具驱动预读(A5 plan 维度)——未预读格仍不呈现、自动寻路不做(A5 侧裁决,D1 共担呈现) | B4 OQ2 | 已登记 | +| A3×① | grant_ap 接口漂移(第 9 API):`grant_ap(n) -> bool`——Active 相位受理、remaining_ap += n、无上限域、其他相位拒绝;8 API→9 API 封闭枚举扩容 | A3 修订批 | 已登记 | +| A4×① | VariationTable.fog.counter_item_ids = [probe_dust] 回填(D2 表值 + registry mvp_content 同批——A4 GDD 正文不改,A4 EC6/OQ2 收口) | D2/A4 修订批 | 已登记 | +| A4×② | counter_item_ids 悬空引用校验激活 + 条件性夹具 a5_dangling_counter(A4 测试口径"首个道具 id 回填时补校验"条款兑现——A5-1 义务闭环) | D2/A4 修订批 | 已登记 | +| A4×③ | 框架内容契约:未来变异建行时 counter_item_ids 各配 ≥1 反制道具("每变异 ≥1 反制"——岩浆/冰冻行随垂直切片批) | A4 后续批 | 已登记 | +| D1×① | aux 库存常读条(挖掘期规划面——与 B4 库存条并置或分区呈现归 D1 裁决;枚举序 = add_item 受理序) | D1 修订批 | 已登记 | +| D1×② | 使用交互:选中道具 + 目标拾取(雾格/中心格)或无目标确认;可用性门呈现(板级零信息 → 图标灰、入口禁用——usable 投影数据源,B4 usable_now 先例)+ 拾取预览(area_3x3 悬停高亮将受影响格);无效/零信息目标可辨反馈(零消耗语境);相位 gate 呈现 | D1 修订批 | 已登记 | +| D1×③ | 叠加层三集呈现:显影格数字(数据源 = A2 板面 + digit_reveal 集)/ 真视标记(形状+色相双通道)/ 预读徽章(数据源 = B1 query_reward_identity 既有读面——与已开格徽章同族) | D1 修订批 | 已登记 | +| D1×④ | 关卡末清空反馈:"剩余辅助道具 ×N 随关卡结束清空"(B4 义务⑤同款先例) | D1 修订批 | 已登记 | +| D2×① | AuxItemTable 新表登记(表清单 + schema 校验族——D2 GDD 接口漂移修订批) | D2 修订批 | 已登记 | +| D2×② | VariationTable/RewardTable 表值回填(counter_item_ids / 六行 aux)+ 两处悬空校验激活 | D2 修订批 | 已登记 | +| D5×① | 教学候选登记:"买情报"闭环 + 真视≠保护口径(非 blocking——D5 里程碑 Alpha) | D5 | 已登记(候选) | +| art-bible×① | §4.6 图标键扩容:item_id 六键 + 叠加层标记三族(键冻结流程同既有——终稿美术归样张批) | art-bible 后续批 | 已登记 | + +### 呈现数据面(A5 供给、D1 消费——呈现归 D1) + +| # | 数据面义务 | 归属 | +|---|---|---| +| 1 | 叠加层三集只读投影 query_overlays(digit_reveal/mine_reveal/reward_preview 三格集)——D1 呈现唯一数据源;数字/徽章本体读既有面(A2 板面、B1 query_reward_identity),A5 零数据复制 | D1 消费 | +| 2 | 库存投影 query_inventory(逐件 {item_id, source{cell, round}, 快照, 状态, usable};枚举序 = 受理序)——来源归因呈现保 P2;usable = 板级零信息判据预判(可用性门数据面) | D1 消费 | +| 3 | 使用回执 consume 返回快照投影——使用瞬间确认反馈数据源;无效目标拒绝附诊断类别(可辨反馈) | D1 消费 | +| 4 | 负空间:A5 效果零结算明细行(挖掘期即时可见性承载归因——与 B4 道具行协议区分);无 HUD 常显字段(库存条外);真视不自动标旗、显影不自动挖(玩家动作仍走既有输入面) | B1/D1 | +| 5 | icon 键契约:item_id 直作图标键(一键一专属纹样、全局恒定禁运行时换图——art-bible §4.6 惯例);叠加层标记三族视觉语言归 art-bible 样张批 | art-bible / D1 | +| 6 | 音频挂钩(D4,Alpha):使用确认音/无效反馈音/显影扩散音(可选);挂点事件 = consume 回执,A5 零音频依赖 | D4 | + +> 📌 **UX Flag — A5 扫雷辅助道具**:本系统有 UI 贡献(库存条/使用交互/叠加层呈现)。Phase 4 前置 `/ux-design` 产出使用交互 spec(design/ux/aux-items.md),stories 引用 ux spec 而非本 GDD。 +> 📌 **Asset Spec**:视觉需求已定义(icon 六键 + 标记三族)。art-bible 样张批后运行 `/asset-spec system:a5-minesweeper-aux-items` 产出逐资产规格与生成提示词。 + +## P0 最小闭环 + +1. B4 roll_board 在 install 期预分配格奖励身份(含 aux 行)。 +2. 挖掘期开出资格格 → B1 授予段 grant 返回 category=aux_item → 路由 A5.add_item 入关卡域库存。 +3. 玩家在 Active 相位选中道具与目标发起使用。 +4. B1 编排:相位 gate → validate_use 目标校验(读 A2 只读面)。 +5. 逐 kind 应用效果(叠加层写入 / A3.grant_ap)。 +6. consume 标记 Used、回执快照投影 + 来源。 +7. D1 重查 query_inventory / query_overlays 刷新呈现——玩家看见情报并继续挖掘决策。 +8. 换图或关卡末 B1 编排 retire_overlays / clear_inventory——状态回收,循环在下一张图重启。 + +## P1 / P2 后置 + +| 优先级 | 内容 | 后置原因 | +|---|---|---| +| P1 | B5/D4 道具使用反馈(动效/音频挂点) | 后置里程碑——D4 音频 Alpha | +| P1 | D5 教学"买情报"闭环 + 真视≠保护口径 | D5 里程碑 Alpha,非 blocking(义务 D5×① 候选) | +| P1 | aux 信息价值入 B1 条款①对照表 | 间接效应定量当量——调参期扩列(义务 B1×④ 注记,MVP 不入) | +| P2 | 岩浆/冰冻变异反制道具行 | 随变异建行批(义务 A4×③ 框架契约) | +| P2 | 自动寻路终态裁决(预读道具是否 MVP 终态) | 随 playtest——OQ6 | + +## 调参表 + +| 字段 | 域 | 默认 | owner | 调过高 | 调过低 | +|---|---|---|---|---|---| +| RewardTable.weight(六行,B4 侧 knob) | int >0 | 5/5/4/4/1/1(provisional 归 C3) | C3 | 渗透率↑ → 道具依赖症(预警①)、战斗奖励稀释 | 窗口长空转、五维度覆盖可能破缺(F2 断言拦截) | +| RewardTable.tier(六行) | tier 枚举 | 常规 t2 / 稀有 t3(provisional 归 C3) | C3 | 稀有件入雾池门槛失真(T_fog_min 联动) | 稀有件与常规档不可辨 | +| AuxItemTable.magnitude(ap_grant 行) | int [1,999](ap_grant 行加载校验 ≥1) | 工兵令 2 / 爆破包 4(provisional 归 C3) | C3 | +AP 过量 → 停手转存 RAP 泄漏(EC12)、清图节奏失真 | 行动维度名存实亡 | +| target(结构冻结非 knob) | enum 四值 | 见 MVP 行集 | 本 GDD 修订流程 | 修改 = 改规则(真视窄域护栏),走本 GDD 修订流程 | —— | +| use_phase(结构冻结) | enum {active_dig} 单值 | active_dig | 本 GDD 修订流程 | 扩枚举 = 修订(非调参) | —— | + +- knob 交互:aux 权重 × B4 战斗池权重(W_total 重算联动——B4×④;禁止两侧独立调参不看渗透率);magnitude × A3 ap_per_round(+4 = 半轮预算的锚定关系——ap_per_round 调参时联动复核);稀有 tier × T_fog_min(雾池准入联动)。 +- 非 knob(结构冻结):四值效果枚举与时效派生 · 真视窄域护栏(target 交集校验)· 无效目标不消耗 · 零 AP 使用成本 · 库存关卡域 · Used 不可逆 · 零随机。 +- 调参所有权:全部内容值 provisional 归 C3;锚验收权 = B4/B1(当量对账调参期扩列——B1×④)。 + +### 失衡预警(playtest 预注册,归 C3) + +| 编号 | 指标 | 阈值/读法 | +|---|---|---| +| ① | 道具依赖度:每轮使用率(用了道具的轮次 ÷ 总轮次) | >50% 即警——P1 观测面(扫雷决策外包给道具) | +| ② | plan 预读覆盖率:预读格 ÷ 开格前决策数 | 过低 = 道具浪费;过高 = 预读替代推理(与①联读) | +| ③ | AP 道具节奏:+AP 与停手率/RAP 转存量联动(A3 OQ3 阈值联读) | 转存率异常上行即警——+AP 沦为 RAP 农场 | +| ④ | 稀有感知:全图卷/爆破包单关出现件数分布 | 恒 0 = 稀有定位失效;恒 ≥2 = 渗透过高(F1 联动重跑) | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)——A5 是 Core 层状态容器 + 定义表,零场景依附;容器/schema 经 class_name 直引实例化(B3/B4 先例,AC 扫描豁免同款) | +| 被测面 | B1 编排未接线(垂直切片实现期)——冒烟以构造序扮演编排方(B4 先例);A2 只读面以桩投影构造(真集成 deferral 登记缺口预检 1);A3.grant_ap 桩断言调用序(真 A3 deferral 同批) | +| 零随机口径 | A5 全 API 零 RNG——行为级硬证据 = 同输入序列重放逐位相同(AC-9) | +| 期望值推导纪律 | 六行 weight/magnitude/tier 全 provisional(调参表)——THEN 按表读值推导不写死;结构定值断言合法:维度五值封闭、效果四值封闭、target 四值封闭、use_phase 单值、护栏谓词(mine_reveal 支持域) | +| 跨表断言生效前提 | AC-12(六行回填)与 AC-13(悬空校验激活)以义务 B4×①…⑤/D2×①② 兑现为前提——生效前登记 deferral(对侧义务登记即生效条件,A4 测试口径先例) | +| 诊断卫生 | 每用例先 reset_diagnostics()(D2 冒烟协议) | + +### API 面(provisional,封闭枚举——实现期 ADR 定名) + +| 接口 | 签名 | 语义与守卫 | +|---|---|---| +| add_item | `add_item(item_id, source) -> bool` | B1 授予路由调用;Cleared/未知 item_id → false + 诊断;逐件实例入关卡域库存 | +| query_inventory | `query_inventory() -> Array` | 逐件实例枚举(item_id/source/快照/状态/usable),枚举序 = 受理序;Used 保留只读;usable = 板级零信息判据现算投影 | +| validate_use | `validate_use(handle, target_cell?) -> Dictionary\|false` | 效果投影 {effect_kinds, magnitude, resolved_target};目标校验按使用目标校验谓词表(越板/点击级零信息 → false + 诊断零消耗;边缘窗口 → resolved_target = 窗口 ∩ 板内格);Used/Cleared/板级零信息 → false + 诊断;只读不消耗 | +| apply_overlay | `apply_overlay(kind, resolved_target, magnitude) -> bool` | 叠加层写入(单 kind 单次,B1 逐 kind 调用);域内输入恒受理 | +| consume | `consume(handle) -> Dictionary\|false` | Used 不可逆;回执 = {item_id, source, 快照投影};Used/Cleared → false + 诊断 | +| query_overlays | `query_overlays() -> Dictionary` | D1 只读投影 {digit_reveal, mine_reveal, reward_preview} 三格集;纯读零副作用 | +| retire_overlays | `retire_overlays() -> bool` | 板级叠加层清空(B1 编排——与 B4 retire_mapping 同点) | +| clear_inventory | `clear_inventory() -> bool` | 关卡末全清;重复 → false + 诊断(幂等守卫) | + +- 封闭枚举即负空间验收形态:无自动使用入口(真视不自动标旗、显影不自动挖)· 无重掷/升级 API · 无事件订阅 · 无 RNG 直连 · 无 A2/B4 状态写入口(apply_overlay 只写 A5 自持叠加层);新增任何入口 = 接口漂移(本 GDD 修订非调参)。 + +### 夹具与致命 manifest + +| 夹具根 | 内容 | 服务 | +|---|---|---| +| 默认根 | AuxItemTable 六行 + D2 全表(B4 修订批落地后)——结构断言与 F1/F2 验算 | AC-1/AC-11/AC-12 | +| a5_dup_key | 重复 item_id | 致命 | +| a5_dim_oob | dimension_tags 含 "luck"(枚举外) | 致命 | +| a5_target_violation | mine_reveal 行 target=board_wide(真视窄域护栏违例——承重校验的测试落点) | 致命 | +| a5_phase_oob | use_phase="settlement"(枚举外) | 致命 | +| a5_dangling_counter | VariationTable.fog.counter_item_ids=["ghost_item"](AuxItemTable 无此行) | 致命(A4×② 兑现载体) | +| b4_dangling_aux | RewardTable aux 行 effect_id="ghost_item" | 致命(B4×② 兑现载体) | + +| manifest 行 | 触发条件 | 断言 | +|---|---|---| +| a5_dup / a5_dim / a5_target / a5_phase / a5_dcounter / b4_daux | 各致命根独立进程游戏通道加载 | 非 0 退出且错误清单含对应类别 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 干净检出(默认根) | 冒烟加载 AuxItemTable | 加载成功、恰含六行(probe_dust/sapper_order/loot_compass/nest_pendulum/catacomb_scroll/blast_kit——内容事实断言);非 knob 字段(dimension_tags/effect_kinds/target/use_phase)与 MVP 行集逐一相符 | 规则 1/2 · 枚举节 | +| AC-2 | schema 类直引实例 | validate() 作用于越域代表值:dimension_tags 含枚举外值、effect_kinds 含重复、mine_reveal 行 target=board_wide、ap_grant 行 magnitude=0、use_phase 越域;域内代表值全组 | 越域各含对应域错误类别(零静默);域内零诊断——特别地 blast_kit(ap_grant+mine_reveal→area_3x3)与 catacomb_scroll(fog_reveal+reward_preview→board_wide)组合行合法通过 | 校验类别表 · 规则 3 | +| AC-3 | 全新容器 | add_item ×N(N=64)→ query_inventory | 恰 64 件、每件 {item_id, source{cell, round}, 快照}、全 InStock 零拒绝(数量无上限);枚举序 = 受理序 | 规则 5 | +| AC-4 | 库存含 probe_dust、桩面含雾区 R(2 未开雾格) | validate_use(handle, R 内雾格) | true + 投影 {effect_kinds=[fog_reveal], resolved_target=R}(只读——库存/叠加层零变化) | 规则 7② | +| AC-5 | 同 AC-4、库存计数 c | validate_use(handle, 已开非雾格) | false + 诊断(EC1);随后库存计数仍 = c(零消耗——照常消耗拍板不适用于 A5 的行为面) | 规则 8 · EC1 | +| AC-6 | 同 AC-4 | 编排序扮演:validate → apply_overlay(fog_reveal, R) → consume | apply 后 query_overlays.digit_reveal ⊇ R 两格;consume true + 回执含 source;实例 → Used | 规则 7 · EC4 | +| AC-7 | Used 句柄 | 再次 validate_use / consume | 均 false + 诊断(Used 不可逆) | EC10 | +| AC-8 | 库存含 sapper_order(magnitude=2)、A3 桩接线 | 编排序扮演 ap_grant 分支:validate → grant_ap 桩 → consume | 桩收 grant_ap(2) 恰一次且在 consume 前(序断言);consume true | 规则 7③ · B1×① | +| AC-9 | 固定输入序列(add_item ×3 → validate ×2 → 编排使用 ×2 → query 全读面) | 同序列重放两遍(全新容器) | 两遍的库存投影/叠加层投影/回执逐位相同(零随机的行为级硬证据——A5 全 API 无 RNG) | 规则 10 | +| AC-10 | 叠加层 Active(含条目) | retire_overlays → query_overlays | 返回三集全空;再次 retire → false + 诊断(幂等;重注入后可复用——板级生命周期) | 状态表 · EC3/EC7 | +| AC-11 | clear_inventory 后 | validate_use / consume / add_item / query_inventory | 均 false 或空 + 诊断(Cleared 逻辑失效态——B4 E7 同款) | EC9 | +| AC-12 | 默认根(生效前提 = B4×④/D2×② 兑现) | 读 RewardTable 六 aux 行 + F2 覆盖推导 | 六行存在且 weight>0、effect_id 精确指向 AuxItemTable 行;F1 渗透率 ≈1.7 件/板按表读值复算成立;五维度 coverage 全真(维度覆盖断言——"每维度都有对应道具"的机器可查形态) | F1/F2 · B4×④ | +| AC-13 | a5_dangling_counter / b4_daux 夹具根 | 独立进程游戏通道加载 | 各非 0 退出且错误清单含对应类别(悬空校验激活——A5-1/B4×② 兑现载体);对照腿:默认根干净通过 | EC13/EC14 · A4×② | +| AC-14 | A5 文件集(schema/容器/冒烟脚本) | 按 D2 AC-9 模式静态扫描 | randomize()/RandomNumberGenerator 零命中(零随机);extends Control / .tscn preload 零命中(Core 层零 UI 债);A2/A3/B4 写入类 API 符号零引用(只读面除外——A2 current_board/reveal_record 读接口白名单登记;B1 编排反向亦然) | 规则 10 | +| AC-15 | 库存含 nest_pendulum、桩面 3×3 窗(含 1 雷格)+ 板角中心格腿 + 零雷窗口腿 | 编排序扮演:validate → apply_overlay(mine_reveal, resolved_target) → consume | 常规腿:query_overlays.mine_reveal ⊇ 9 格(含雷格)、consume true;边缘腿:中心格 = 板角 → resolved_target = 4 格(窗口 ∩ 板内格)恒受理;零雷腿:恒受理(零雷 = 信息) | 规则 2/8 · EC17 · 谓词表 | +| AC-16 | 库存含 loot_compass(area_3x3)与 catacomb_scroll(board_wide)、桩面含未开资格格窗 R 与全开窗 E | 编排序两腿:validate → apply_overlay(reward_preview, resolved_target) → consume;零信息腿:validate_use(loot_compass, E 内格);板级腿:桩面全开 → query_inventory usable 投影 | 预读腿:query_overlays.reward_preview ⊇ R 中未开资格格、consume true;全图卷腿:digit_reveal ⊇ 全板未开雾格 ∧ reward_preview ⊇ 全板未开资格格;零信息腿:false + 诊断、库存计数不变(点击级零消耗);板级腿:usable=false(图标灰数据面)+ validate_use false | 规则 8 · EC1/EC18 · 谓词表 | + +### 缺口预检 + +| 编号 | 缺口与处置 | owner | 时机 | +|---|---|---|---| +| 1 | A2 真集成 deferral:目标校验/显影数据源以桩投影构造——真 A2 接线后 AC-4/AC-6/AC-15/AC-16 换真面重跑;雾区身份解析(fog_region target → 所属雾区)与零信息判据(已开态 + reward_eligible 资格标记——可用性门数据源)依赖 A2 板面变异快照读取(A4 生成期快照既有;A2 读接口白名单同批扩列) | A2 集成批 | 垂直切片实现期 | +| 2 | B1 编排 deferral:相位 gate/规范序/生命周期编排以构造序扮演——B1×①②③ 登记为生效条件(缺项即 blocking) | B1 修订批 | B1 GDD 修订 | +| 3 | B4/D2 修订批 deferral:六行回填 + 双悬空校验激活——AC-12/AC-13 生效前提 | B4/D2 修订批 | 义务清单兑现时 | +| 4 | A3 grant_ap 修订批:AC-8 桩换真面 + EC12 转存补腿(A3 侧 AC 扩一条) | A3 修订批 | A3 GDD 修订 | +| 5 | 扫描域成员清单:A5 GDScript 产物入 D2 AC-9 项目级 CI 扫描域(B3/B4 先例) | Technical Setup | 实现期 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-14 | 初稿定稿(solo 模式一次会话——专员/总监代理全部跳过,出稿待人工复核):五维度框架 + 真视窄域护栏 + MVP 六行(四常规单维 + 两稀有多维)+ AuxItemTable schema(D2 回填 owner)+ 获取路由(B4 aux_item 激活)+ 使用编排契约(B1 义务 ×4)+ 板级叠加层(零特判延伸)+ A3 grant_ap 接口漂移预授权 + 跨 GDD 义务清单 21 项 + F1–F3 + 14 AC;A4 EC6/OQ2 与 B4 OQ2 收口 | +| v1.1-lean | 2026-09-14 | 首轮 /design-review(solo 快速审核)3 blocking + 5 recommended 同日核销——用户拍板两项边缘规则:① area_3x3 边缘窗口照常受理、resolved_target = 窗口 ∩ 板内格;② 零信息目标不可用不消耗(板级判据图标灰/入口禁用、点击级拒绝,组合行判据取 OR,真视零雷恒有效)。F3 方向自相矛盾改写(P(雷) 对 ι 递增——收益在盲赌暴露下降非单赌命中率)· 使用目标校验谓词表新增(Core Rule 8 展开)· EC17/EC18 新增 · AC-15/AC-16 补齐(真视/预读运行时路径)· B4×④ 扩登记常数重制 · 义务编号源前缀注记 · aux 行 pool_tag 注记 · 应用顺序钉死(空间先、ap_grant 后)· query_inventory 投影增 usable 字段 · D1×② 可用性门呈现扩列;正文 Core Rules 表同批口语化重写(语义零变更);同日用户拍板免独立 re-review 直接放行【B2/B1/D1 先例】 | +| v2 | 2026-09-14 | 两件套迁移(格式迁移,语义零变更):按模板 v2 重写为 design 件(只装结论——设计目的/系统范围/核心循环/领域章节×5/依赖与输出/P0/P1·P2/调参表/验收标准),公式推导、数值锚定论证、候选取舍、体验锚点论证、已裁决争议、覆盖矩阵与全部 OQ 迁往 `design/gdd/analysis/a5-minesweeper-aux-items.md`;规则改编号人话句,AC/EC/F/义务/OQ 编号全部保留 | diff --git a/design/gdd/analysis/a1-board-generation.md b/design/gdd/analysis/a1-board-generation.md new file mode 100644 index 00000000..8c120bbe --- /dev/null +++ b/design/gdd/analysis/a1-board-generation.md @@ -0,0 +1,310 @@ +# A1 扫雷棋盘生成 设计推演 + +> 状态:concluded(除文末开放问题) · 本文只装"为什么"——`design/gdd/a1-board-generation.md` 只收本文的结论。 +> 修订时新增问题节,不删旧节(历史即依据)。 +> 来源:本文吸收原 lean 版(v1.1-lean)全部推导、论证、裁决记录与验收设施详表;公式结论与表达式在 design 件,推导与论证在此。 + +## 问题:可推理率该怎么度量(F1 的口径是怎么钉死的)? + +### 广度分析 + +- 候选 A:字面窄读"确定性推导格数"(仅数字推导入分子)——收益是口径最严;代价是任意好图都远低于常规门槛,度量与立法本意脱节,被裁掉。 +- 候选 B:免赌确定揭示格数(初始展开 ∪ 连锁揭示 ∪ 推导开据三通道全计入分子,初始展开格同时计入分母)——与 P1 承诺语句"从初始安全区出发,逻辑可推导的挖掘占比不低于门槛"对齐,定为现行口径。 +- 候选 C:单判据 inferability——被"雾区无雷"常识单独撑线的度量空心风险否掉;裁定加第二判据 inferability_core(分子分母都剔除雾格),与 F2 雾占比绝对上限 0.25 合围该风险。 + +### 深度分析 + +雾格入分子有两条通道:链可达雾格经算子①(0 格级联)入分子;与 R∞ 邻接的链不可达雾格经信息源(b)雾元知识入分子;恒无 R 邻格的雾格计入终局残余。雾格挖掘是零雷险行为(玩家可见知识),故计入"免赌"口径;其 AP 全价代价不在此度量、由 F3 单独承载——这是"风险轴与成本轴必须分开"的直接理由:雾板可推理率高而预算负担仍在,恰为两轴不可互相折算的例证。雾格入分子通道与挖掘相邻性(A3 限 8 向相邻)口径对齐,公式本身不变。 + +求解器初始可见态取 0 格 flood 闭包内芯而非保守可达域全体——"外扩 1 格"环非自动展开的必然产物,计入会高估玩家信息。由此派生度量保真对偶义务:既有"A2 实际展开域 ⊆ A1 排除域"之外,新增"A1 求解器初始态 ⊆ A2 实际初始展开域"(登记 A2 GDD);若 A2 定稿展开更小,A1 回退口径 = 出生 3×3 最小态。 + +次序无关性是度量不变式:两算子对 R 单调 ⇒ 最小不动点唯一、与开据次序无关——实现可任意定序而度量不变,兼防一类实现方差 bug;最小不动点可经单调 worklist 传播实现,不强制全板重扫描。每图指标输出携带 metric_version(当前 1)防推理器实现漂移静默改定义(联动 F4 基线);未来改 R∞/信息源域则 version=2 + 向量重制,仅增判据与指标集不动 R∞ 定义时 version 不跳变。 + +clearable 与 inferability 分两闸的理由:inferability 门槛不蕴含可清图——终局对赌残余(拉低可推理率)与矿壳包围的不可达安全格(使 cleared 永不可达)是正交缺陷(1 个不可达格在 126 安全格的板上仅拉低 0.008、0.95 门槛照过,而该板功能性残废);故清图可达性是合法性谓词(违规 = 该次尝试判废重试,非"接受最优"候选),与质量度量分属两闸(验收算子见 F5)。 + +### 综合判断 + +现行口径 = 免赌占比双判据 + 合法性闸分立 + 信息源穷尽枚举冻结 + metric_version 防漂移。改 R∞ 定义、信息源域或规则域 = GDD 修订,非调参。 + +### F1 完整验算例(design 件只留摘要) + +12×12 图(144 格),mine_density=0.15 → N = round(0.15×135) = 20(设环内移除 0,\|M\|=20)、S=124;雾 2 区共 9 格。R₀=21,全图 0 级联闭包 58(含 5 个链可达雾格);数字推导开据 51 + 雾元知识开据 4(链不可达雾格)+ 推导触发级联 8 = 63 → R∞=121;残余 3 格 = 一处终局非确定对 + 1 孤立残余格。inferability = 121/124 ≈ 0.976 ≥ target 0.95;inferability_core = (121−9)/(124−9) = 112/115 ≈ 0.974 ≥ 0.95 → 两项判据达标,attempts_used=1。 + +## 问题:参数域与可行性谓词怎么定(F2/F2b/F2c 的边界从哪来)? + +### 广度分析 + +- 雾占比上界候选:纯 f≤8d 推导界(必要上界,边计数)——不足:堵不住"极雾行以雾元知识撑满可推理率"的度量空心;故叠加 0.25 绝对上限(MVP 量级护栏,非推导界)。 +- 成本闸候选:运行期墙钟时间逃生——被否:耗时依赖输出即破同种子同图(文本级契约),确定性死线必须加载期可判定,故取 attempts×W×H ≤ C 的静态谓词形式,C = 16384 为 provisional 量级护栏,正式值归 OQ2 ADR。 +- 联合校验候选:给 inferability_target × 雾/密度参数加第三条静态跨字段校验——被裁决拒绝(见下裁决表)。 + +### 深度分析 + +必要上界推导(边计数):每雾格 ≥1 条到非雾雷的邻接边 ⇒ 邻接边数 ≥ \|F\|;每雷 ≤ 8 邻 ⇒ \|F\| ≤ 8\|M\| ⇒ f ≤ 8d。必要 ≠ 充分:连通性缺口(雷膨胀皮肤碎裂,A4 EC2)不由本式判定,由静默降级 + F4 兜底。极端行为:d=0.5 时 8d=4,f<1 成为绑定约束;d→0 时几乎任何正 f 均被拒(小雾量在稀雷图结构性不可布,属正确拒绝);f > 0.25 恒拒(与 d 无关);attempts×W×H > 16384 恒拒。 + +F2b sound 性论证:N = round(d×(W×H−9)) 先定数、先布后删只减不增 ⇒ \|M\| ≤ N ⇒ \|S\| = W×H − \|M\| ≥ W×H − round(d×(W×H−9))——round 取整钉死后恒成立(其他取整为非法实现);计数可行 ≠ 连通可行——clearable 生成期闸不可互替。F2b 为 Schema 校验行 delta:min_safe_count 原仅产物侧不变式(Rule 8),参数侧联合不可行域(小图×高密×高下限)经 F2b 于加载期拦截。 + +布雷数下限 round(d×(W×H−9)) ≥ 1 的动机:零雷板 = 初始展开即 cleared 的 A3 EC5 钓鱼活体 + 标中旗经济恒空 + F4 全断言隐形穿越。 + +与 Schema 校验的关系:F2 是 Tuning Knobs 跨字段谓词的规范化合取形,二者等价——Schema 校验行采本规范形消除分歧;(count=0, f>0) 角点已入 AC-2 违例集。 + +### 综合判断 + +三谓词加载期可判定、违规即致命;0.25 / 16384 / 布雷数下限与 F4 严格零阈值同属结构冻结(调整走 GDD 修订非调参)。 + +### 联合可行域裁决(不加第三条跨字段校验——裁决记录) + +| # | 裁决要点 | +|---|---| +| 1 | 无可判定静态界——inferability 由 0 格拓扑、雾形、出生位置主导,聚合参数不决定它,任何 sound 加载期联合谓词要么空洞要么误杀 | +| 2 | 架构既定——暴露机制就是"运行期接受最优不死锁 + 强告警埋点 + 调参期批量断言",第三闸重复且更弱 | +| 3 | 补偿保证(与 F4 绑定生效)——F4 红线 + 降级率埋点使联合不可行域在首次批量生成时必然现形 | +| 4 | 裁决边界——拒绝的是 inferability 可行性静态联合谓词(无可判定静态界);雾占比绝对上限 0.25(度量语义护栏)与 F2c 成本谓词(确定性成本死线)均有可判定形式且不声称预测 inferability,不在该裁决覆盖内 | +| 5 | 回写义务(advisory 非校验)——target、fog_target_ratio、mine_density 同向调高时降级率结构性上行,属参数域边界而非实现缺陷 | + +### F2/F2b/F2c 验算表 + +| 输入 | 判定 | 依据 | +|---|---|---| +| (d=0.15, f*=0.12, count=2) | ✓ 放行 | 域内 | +| (d=0.06, f*=0.50) | ✗ 致命 | 0.50 > 8d = 0.48 | +| (count=3, f*=0) | ✗ 致命 | 一致性腿(count ≥ 1 ⇒ f > 0) | +| (count=0, f*=0) | ✓ | level 1 无雾行标准形 | +| (count=0, f*=0.05) | ✗ 致命 | count=0 ⇒ f=0 腿(角点) | +| (d=0.15, f*=0.85, count=32) | ✗ 致命 | 0.85 > 0.25 雾占比绝对上限——极雾行加载期判死 | +| F2b:10×10、d=0.10、min_safe=50 | ✓ | 100 − round(0.10×91) = 91 ≥ 50 | +| F2c:10×10×16 = 1600;12×12×16 = 2304 | ✓ | ≤ 16384 | +| F2c:64×64×32 = 131072 | ✗ 致命 | 该组合换图相位同步卡顿秒级至百秒级 | + +## 问题:雾的成本怎么记(F3 的口径为什么是这样)? + +### 广度分析 + +- 分母候选 A:雾内部分母 \|U\|/\|F\|——被裁:不单列输出,可由指标集(实际雾占比 + Chain₀/U 重算)按需还原,不作对账输入。 +- 分母候选 B:付费格分母 \|U\| ÷ max(1, \|S ∖ Chain₀\|)——定为 B1 条款 5 对账精确口径(雾在最小清图 AP 负担中的权重)。 + +### 深度分析 + +分母 = \|S ∖ Chain₀\| = 最小清图付费挖掘格数(初始展开与连锁免费、其余每格 dig_ap_cost——A3 价目只读引用),分母下限 1 防全连锁退化板除零。链可达性取决于未开 0 格隐藏拓扑,玩家决策时点不可求值——本式是生成期事后度量;A4"玩家侧按全价做预算"的保守启发式不与本式冲突,两口径服务不同主体。聚合义务(A4 ⑤ 公式化):逐板计算入指标集;调参期按行批量聚合出核算报告供 C3 修正行参数;无运行期强制——A1 只拥有量的计算与输出,不拥有"够不够"的判(归 A1/A3 契约与 C3)。 + +边界形态的观测意义:U=F(雾区全部链不可达)→ 雾板退化为纯 AP 税——A4 反向护栏与 B4 定价约束的观测对象;U=∅(雾全链免开)→ 雾零成本——B4"糖果区"病理在此指标上直接现形。验算例:F1 例板 F=9、链可达雾 5 → U=4;dig_ap_cost=1 → Δbudget_fog = 4 AP;付费格 = 124 − 58 = 66 → fog_burden_ratio ≈ 0.061。 + +### 综合判断 + +Δbudget_fog 与 fog_burden_ratio 随图输出、生成期算账、调参期批量核对;运行期不强制,账对不上改的是难度参数行。 + +## 问题:调参期红线怎么守(F4 的阈值与样本量怎么定的)? + +### 广度分析 + +- 红线阈值候选:小容差(如允许 1% 失败)——被否:A1 反向护栏已定"跌破门槛 = 阻塞缺陷",任何一例失败即触发参数修正会话,迫使决策显式化;裁定严格零(用户定稿)。 +- 雾出现下限候选:1.0——被否:EC2 连通性降级是合法运气事件;裁定 0.95(收紧为达成区占比后,同时容纳完全降级与部分达成两类合法运气事件)。 + +### 深度分析 + +阈值裁定理由(五条): + +| # | 理由 | +|---|---| +| ① | 红线严格零(用户定稿):任何一例失败即触发参数修正会话,迫使决策显式化;运行期"接受最优/最不坏"是安全网,批量红线是调参期纪律 | +| ② | N=256:95% 下限的稳定通过要求真实出现率 ≥ 0.98(3σ 余量 ≈ 99.97%) | +| ③ | 雾出现下限 0.95 而非 1.0:5% 容差容纳自然方差;系统性病理(level 2 小图低密关雾成片失败)呈现 ~0% 出现率,与 0.95 相距悬殊,检出无歧义 | +| ④ | 断言 B′ 零成本反向杠杆:门控 bug(如 level 1 出雾)一例即现形 | +| ⑤ | 成本:行数 × 256 板 × ≤32 attempts 的生成+求解模拟,离线分钟级(成立域 = worklist 级求解实现 ∧ 大面积行稀少;naive 全板扫描实现 + 数行 48×64 板即小时级——分片续跑义务见 OQ2) | + +σ 基址打包论证:σ(L,D) = 10^9 + 2^20·L + 2^12·D——位打包使行区间 [σ, σ+255] 两两不交、远离运行期取号域;朴素 1000·L+D 打包在 D≥1000 时精确碰撞〔(1,1001) ≡ (2,1)〕且同 L 相邻深度行批量区间 255/256 种子重叠。登记种子按失效协议全部重搜——夹具 σ 为登记时值。derive_at 纯函数、不推运行期计数器;σ 基址清单入版本库,批量跨迭代可复现。target行 期望由表值推导,不写死。 + +### 综合判断 + +断言 A/A″/A′/B/B′/E + 报告 C + 诊断 D 现行口径冻结;严格零阈值与 0.85/0.25/16384 同属结构冻结。验算行:行 (2,0)(fog unlock 关)N=256 → 两项判据达标 256/256 → A 通过;达成区数 = count 的板 251/256 = 0.980 ≥ 0.95 → B 通过;重试率 3.1% → 诊断记录。行 (3,2):达成区数 = count 的板 220/256 = 0.859 < 0.95 → B 失败 → 修正行参数(区域数/尺寸/密度侧保证,禁放宽 A4 Rule 3)。极端行为:target 1.0 的行在断言 A 下大概率开火(合法、预期信号);断言 B′ 失败即实现缺陷而非参数问题。 + +## 问题:生成-验证循环与验收算子怎么取舍(F5 为什么是三分支)? + +### 广度分析 + +- 候选 A:一次性生成不重试——被否:雾布完才测可推理率,单次失败即废图过于浪费,且没有参数问题的暴露通道。 +- 候选 B:全局 argmax(跑满 attempts 取最高分)——被否:达标即出(最早达标尝试 k*)是重试语义的自然形式化,argmax 徒增耗时无品质收益。 +- 候选 C:attempts 用尽直接报错/卡死——被否:玩家永不卡死是硬承诺;裁定降级链 = 接受最优(k**,tie 取最早、零额外取数)+ 强告警 → 零合法时接受最不坏(k***,确定性劣度字典序)+ 最高级告警 + 非法产物标记(均为用户定稿)。 + +### 深度分析 + +合法性闸(clearable/min_safe)在质量评分之前的理由:不可清图产物若进入"接受最优"候选,质量分会掩盖功能残废——1 个不可达安全格只拉低 inferability 0.008,板却已功能性残废。三分支共用同一 board_gen 流实例(Rule 3/6),k*/k**/k*** 与 B_out 同为流取数的确定函数——回放语义闭合;scope 事件边界操作定义 = 生成函数完整返回(重试不产生新取号),D3 快照切点天然落在边界外(D2"消费侧遵守"条款的 A1 具体化)。生成期表值单次读取(行参数在生成开始时快照,生成期内不重读——防热重载撕裂 attempt 语义,A4 EC8 同族)。"接受最不坏"分支的确定性劣度 = (不可达安全格数, min_safe 缺口, 序号) 字典序——零额外取数、可重放。B1 编排不得把 attempt 边界当 scope 事件(登记 B1 接线注记)。 + +EC5 玩家侧语义登记:非法产物标记属遥测域、玩家不可见——零合法板在玩家侧表现为"看似可玩"的功能残废(不可清图板挖光可达域后 cleared 永不触发、清图奖励永不可得、无任何提示),GDD 如实登记该体验债;玩家侧诚实呈现(提示/补偿/回退)登记为 B1/D1 域承接项;运行期熔断候选(同 (L,D) 连续降级/非法 ≥ 阈值 → 确定性回退该行已知好种子重生成)不破"玩家永不卡死"与回放契约,方案裁决归实现期 ADR(OQ2 关联)。 + +### 综合判断 + +三分支 + 合法性闸前置 + 强告警分级是现行契约;降级率是参数问题的暴露通道,批量红线由 F4 断言 A′ 守。 + +## 问题:体验锚点为什么这样定(P1/P4/P3 的生成侧承诺)? + +### 广度分析 + +- P1 生成端对偶:D2 保"同种子同结果"的可复现,A1 保"同参数同品质"的可推理——可推理率生成参数 = 出题人义务;玩家输给的是自己选的赌(雾区/终局盲猜),不是不可解的局。 +- P4 难度物质:雷密、尺寸、雾占比随 (level_index, node_depth) 爬升——难度由 BoardGenTable 行参数直接雕刻,非文案。 +- P3 空间面:生成期布置约束兑现"雾区无雷、雾贴雷簇"(A4 Rule 3 结构推论)——洞口前的权衡是真实的信息权衡而非装饰。 + +### 深度分析 + +情绪目标:"这局能算"的底气——A1 不制造情绪,它制造情绪的前提条件(玩家永不看见生成器,但每轮都在体验它的产物)。参考感受:扫雷原版"每局必有解"的玩家信念(原版实际不保证——本作把这份信念变成生成端契约);Into the Breach 每张谜题可解的设计诚实。反向护栏(裁剪判据):"纯猜局"是 P1 的死亡体验——生成端若让玩家频繁坠入不可推理局面,结算端再华丽也救不回掌控感;红线 = 生成参数使可推理率跌破门槛即为阻塞缺陷(验收红线,非调参建议)。 + +### 综合判断 + +三条支柱承诺全部以生成端可判定契约承载(双判据、行参数雕刻、布置约束),无一依赖文案或呈现。 + +## 问题:核心结构决策的取舍记录(布雷、取整、密度形态、推理域)? + +### 广度分析 + +- 布雷语义候选:落选重投(落进环内就重取位置)vs 先布后删(落完再删、只减不增、不重投、无迭代)——裁定先布后删:确定性终止、无迭代、漂移量可观测(|M| ≤ N 入指标集)。 +- 雷数取整候选:round 钉死(其他取整为非法实现——F2b sound 性论证依赖此钉死)。 +- 雷量参数形态候选:定数 vs 密度浮点——裁定密度浮点(尺寸解耦;f≤8d 分母对齐),用户拍板四结构决策之一。 +- 推理域候选:开放信息源 vs 封闭枚举 single_subset——裁定冻结(用户拍板);改域 = 改可推理率定义 = GDD 修订非调参。 + +### 深度分析 + +四结构决策(生成-验证循环 / 接受最优 + 零合法接受最不坏 / 推理域 single_subset 冻结 / 密度浮点)与 F4 严格零红线为用户拍板——全部经 2026-09-09 full /design-review 8 条 blocking 修订后放行(日志:design/gdd/reviews/a1-board-generation-review-log.md)。雾边双向净效应建模(A4④):可推理率测量在布雾完成后进行,雾边寂静减项与雾锚增项(雾元知识开据格)同时内生于测量,禁单向补偿;AC-8 板ⅲ 为此的度量侧证据。 + +### 综合判断 + +结构决策全部冻结;任何一项的改动都走 GDD 修订。 + +## 问题:措辞与口径防漂移登记(为什么是 registry 级)? + +### 广度分析 + +inferability 与 P(安全) 是两个系统的两个量,混用即口径漂移。 + +### 深度分析 + +inferability 是盘面级免赌占比(生成侧);P(安全) 是单格认知概率(决策侧、经济口径)——免赌集内 P(安全) 恒 = 1(含雾格),风险讨论收缩到残余集(其安全格占比 ≤ 1 − inferability)。inferability 是 P(安全) 的生成端结构性上界代理之占比形式,不是 P(安全) 本身。B1 条款 3 对账输入 = actuals 分布 p10(F4 报告 C)而非 target 参数——防"拿参数当事实"。 + +### 综合判断 + +措辞登记为 registry 级事实(board_gen_table.inferability_vs_p_safe),B1 消费回执已登记。 + +## 问题:验收设施怎么设计(测试口径各条款的理由)? + +### 广度分析 + +- 冒烟载体:A1 是生成函数系统,`godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)。 +- 度量独立入口(qa-lead 裁决、分解面声明):推理器(R∞/inferability/metric_version)、F3 度量(Chain₀/U/Δbudget_fog/fog_burden_ratio)、合法性谓词(legality/clearable)以独立纯函数入口暴露——生成函数内嵌调用与独立入口同一实现,独立入口不引入新逻辑、仅暴露 F1/F3/F5 已定义的函数;独立入口的行为断言即内嵌调用的行为(次序无关性、防作弊等白盒性质测试以此为载体);若实现期 ADR 裁决推理器不可独立调用,AC-7/8/9/12 的单元腿须降级重议——登记缺口 G1,门禁评审列为 A1 实现契约条款。 +- 测试种子取号:经 `derive_at("board_gen", σ)` 纯函数(D2 R2 预览语义的测试侧合法用途,不推运行期计数器);测试脚本以该种子构造流实例属测试通道合法动作(AC-18 扫描豁免域);运行期 replay 语义(open_stream 全真路径)另测(AC-5)。 + +### 深度分析 + +夹具策略:默认根 = MVP 参考行 L1·D0 / L2·D0(干净检出)——参考行是 GDD 定稿的 schema 自洽性证明行,期望全部由表读值推导、对 C3 调参免疫;行为变体经 `--data-root` 夹具根注入(禁拷入 data/tables/);黄金图板与病态板经 Board class_name 直引程序构造(不经生成器、不经 load()/preload()——A4 AC-5 先例)。登记种子(σ 值)依赖管线确定性:一经验证恒成立,实现或定义变更致失效 → 按失效协议重搜并登记(与黄金向量失效协议同源)。 + +黄金图回归向量协议:五类手工核验小板,每板 = 显式坐标规格(尺寸/雷集/雾区格集/出生 3×3,程序构造不经生成器)+ 登记常数((\|R∞\|,\|S\|) 整数对、含雾板另登记 (\|R∞∖F\|, \|S∖F\|) core 整数对——由既有坐标规格派生、向量不重制、关键格 R∞ 成员表、clearable、Chain₀/U 格集、Δbudget_fog、fog_burden_ratio 比对形 = 整数对 (\|U\|, max(1,\|S∖Chain₀\|))、metric_version=1——整数对比对,禁浮点容差)+ 逐格手工推导记录(每格声明信息源 ∈ {数字约束(a)、雾元知识(b)、0 格级联①},入版本库随向量文件——手工常数的登记依据);比对内容 = 度量独立入口 + 合法性谓词输出与登记常数逐位相等;失效处置协议 = 失配先按冻结定义复核手工推导,推导无误即实现缺陷(修实现);solver_rules/合法信息源域/metric_version 变更 = 换 solver_rules 即失效,走 GDD 修订 + 向量重制(推理域冻结的非调参性质的行为侧兑现)。 + +期望值推导纪律:行为类 AC 期望 = ConfigDB/表读值推导(inferability_target/min_safe_count/max_gen_attempts/fog_region_count/unlock_level/dig_ap_cost/σ 公式全读值),不写死玩法数值;唯一例外 = 黄金图向量手工常数(依据 = 手工推导记录);参考行 GDD 自注期望(\|S\|≈91 等)为说明非断言,任何 AC 不采用。致命类独立进程协议:承 D2 AC-2b / A4 AC-4b——runner manifest 逐条登记"夹具根 → 期望退出码"(AC-3 三行,均 ≠0);单进程内只做测试通道收集断言;每用例先 `reset_diagnostics()`(承 D2 冒烟协议;warning/error 断言一律经诊断收集器)。 + +MVP 参考行自洽性验证:F2/F2b/F2c 全过(10×10×16 = 1600、12×12×16 = 2304 ≤ 16384);期望 \|S\| ≈ 91、R₀ ≈ 12–20 → inferability 期望 ≥ 0.95(L1 无雾 ⇒ core ≡ inferability;L2 雾 ~14/144 格、core 判据轻度收窄仍期望 ≥ 0.92——两项判据同时达标;attempts_used ≈ 1,F4 全绿理论位形)。参考行明细:L1·D0(10×10 / d=0.10 / target=0.95 / single_subset / min_safe=50 / attempts=16 / fog 0·0——level 1 候选池空标准形 / biome={"dungeon"} 占位);L2·D0(12×12 / d=0.12 / target=0.92 / 同冻结 / min_safe=60 / attempts=16 / fog 1 区·0.10〔≤8×0.12 ✓——F4 断言 B 首个真实考验行〕 / biome 同占位)。全部 provisional——仅 schema 自洽性证明,曲线数值归 C3 调参所有权。 + +### 综合判断 + +测试口径全部条款现行有效;缺口 G1/G2/G4 如实登记(见开放问题区)。 + +## 附录 A:夹具规格(qa-lead 交付,逐根原样保留) + +| 夹具根 | 内容(F2/F2b 均过,除违例根外) | 用途 | +|---|---|---| +| `a1_fog_domain` | f>8d 违例行 | AC-2/3(致命) | +| `a1_min_safe` | 10×10/d=0.10/min_safe=92(F2b 违例:100−round(0.10×91)=91<92) | AC-2/3(致命) | +| `a1_biome_empty` | board_biome=∅ 行(A4⑮) | AC-2/3(致命) | +| `a1_pool_empty` | VariationTable 测试行(biome_tag=[ice]、unlock_level=1,承 A4 variation_biome_tag 同型)+ BoardGenTable 两行:(1,0) board_biome={dungeon}(pool=∅)/ (1,1) board_biome={dungeon,ice}(pool 非空) | AC-13 | +| `a1_high_target@k` | 12×12/d=0.12/target=1.0/fog=1 区 0.05/min_safe=60/attempts=k∈{1,2} | AC-5/11 | +| `a1_ec5@k` | 10×10/d=0.45/min_safe=1/target=0.85(域内合法档)/fog=0/attempts=k∈{1,2,8,16};σ* 登记(@1 零合法验证种子) | AC-12 | +| `a1_two_regions` | 10×10/d=0.15/count=2/f=0.12/target=0.85/attempts=16;σ 登记 | AC-14 | +| `a1_ec2_press` | f := 8×d 计算值恰界行(程序内动态赋值);σ*′ 登记(shortfall>0 搜索种子) | AC-15 | + +黄金板设计要求:ⅰ 纯数字推理板(无雾;须含仅 single_subset 规则可开的推导格)· ⅱ 链密集板(0 格级联闭包主导)· ⅲ 含雾板(须同时含:链可达雾、链不可达雾、邻接出生 3×3 的雾区、寂静减项证据格、雾锚增项证据格【雾元知识开据格】)· ⅳ 终局对赌残余板(inferability 严格 ∈ (0,1) 的非确定残余)· ⅴ 不可达安全格板(clearable 违例形态)。 + +只读观测量清单:产物格状态(雷布/数字/0 格/雾区标记/出生区)+ 八项指标(实际 inferability【含 metric_version】/ inferability_core / 实际雾占比【= \|F\| ÷ (W×H)】/ 实际雷数与实际 \|S\| / fog_burden_ratio / Δbudget_fog / attempts_used / 降级标记【含非法产物标记与 shortfall】)。 + +## 附录 B:out-of-scope deferral 表(以对侧登记为生效条件) + +| 编号 | deferral 内容 | 对侧 / 生效条件 | +|---|---|---| +| O1 | B1 接线——attempt 边界非 scope 事件的编排侧断言、指标对账消费(actuals p10 非 target)归 B1 GDD AC(A1 侧生成半边已由 AC-5 计数器断言覆盖) | B1 GDD 登记 | +| O2 | F4 全量——N=256/行 × 全行的断言 A 严格零/B 0.95/报告 C/诊断 D = 调参期离线批量工具域(工具本体为 A1 交付物,登记 OQ;σ 基址清单入版本库),冒烟仅载缩样(AC-10) | 批量工具交付登记 | +| O3 | A2 集成——双包含回验(A2 实际展开域 ⊆ A1 排除域;A1 求解器初始态 ⊆ A2 实际初始展开域)登记 A2 GDD(A1 半边由 AC-6(a) 测试 BFS 对照承载) | A2 GDD 登记 | +| O4 | fault-injection 缺载体——EC7 生成中热重载(同步单调用无重入点,构造性成立且注入不可)、EC10 内部异常 fatal、EC4 tie-取最早(需精确同分尝试)deferral 至实现期注入设施或可选种子搜索会话 | 实现期登记 | +| O5 | 性能预算——生成耗时/换图相位无定稿预算值(Tuning Knobs OQ),perf-profile 期承载 | perf-profile 期 | +| O6 | 呈现域——EC6 雾区视觉合并归 D1、biome 色板换皮归 D1/art-bible | D1/art-bible 登记 | +| O7 | C3 全行审计——曲线导出工具全行矩阵 diff 为 C3 期交付物(D2 OQ5 既有排期) | C3 期 | + +## 附录 C:Knob 调参方向表(调过高/调过低的后果记录) + +| Knob | 参考(L1/L2) | 调过高 | 调过低 | +|---|---|---|---| +| board_width/height [8,64] | 10×10 / 12×12 | 决策带宽膨胀、清图时长拉长 | 逼近 min_safe 联合域(F2b 拦截线) | +| mine_density (0,0.5] | 0.10 / 0.12 | 推理窗收窄、可推理率难达标(F4 断言 A 开火);f≤8d 分母水涨船高 | 图面空旷、奖励密度稀 | +| inferability_target [0.85,1] | 0.95 / 0.92 | 重试率升、深行联合不可行(F2 裁决:无静态闸,F4 必然现形) | 域下限 0.85 = P1 绝对锚(加载期拒绝 + F4 断言 A″ 兜底)——"纯猜局混入"红线自此有可判定载体;0.85 本身的调整走 GDD 修订非调参 | +| min_safe_count | 50 / 60 | 小图联合无解(F2b 加载期拦截) | 退化板钓鱼复现(A3 EC5 对冲失效) | +| max_gen_attempts [1,32] | 16 | 生成耗时随 attempts×面积×传播深度超线性涨(对边长最高三次方量级;F2c 成本闸拦病理组合,OQ2) | 降级率上行、质量下行 | +| fog_region_count | 0 / 1 | 信息剥夺过重(P1 落差);F4 断言 B 压力 | 低于雾出现率下限 → A4"解锁新变异"推进感哑火(A4⑭ 义务) | +| fog_target_ratio [0,1) | 0 / 0.10 | 逼近 8d 上界 → 布置降级率升(连通性,EC12 两闸注记);> 0.25 加载期拒绝(F2 雾占比绝对上限腿) | 雾存在感弱、P4 推进感哑火 | +| board_biome | {"dungeon"} | ——(词表 owner 未定稿,暂只校验非空) | —— | + +## 附录 D:覆盖矩阵(qa-lead 交付,验收评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules | 1→AC-4/5/6(c)/16(布雷语义)· 2→AC-8/9/10(a)/16(两项判据)· 3→AC-10/11/12 · 4→AC-6(a)(b)/7(芯/环口径)· 5→AC-6(c)(d)(e)/13/14/15 · 6→AC-4/5/14/18 · 7→AC-16 · 8→AC-2/3/6(f) · 9→AC-1/2/3/13 · 10→AC-6(g)/17/18/19 | +| Formulas | F1→AC-8/9/7/16 · F2/F2b/F2c→AC-2/3(角点/上限/布雷数下限/成本上限)· F3→AC-16 · F4→AC-10(全量 O2;断言 A″/E 属 O2)· F5→AC-10(e)/11/12/7 | +| EC | EC1→13 · EC2→15 · EC3→7/8 · EC4→11(tie O4)· EC5→12(玩家侧语义登记 B1/D1 域)· EC6→14(合并 O6)· EC7→O4 · EC8→10(c) · EC9→2/11 · EC10→O4 · EC11(F2b)→2/3 · EC12→2/15 · EC13→2 · EC14→5 | +| A4 义务 | ①→13 · ②→6(c) · ③→6(a)(b) · ④→8/16(core 常数补强)· ⑤→16 · ⑥→13 · ⑦→15 · ⑧→4/14 · ⑨→7/8 · ⑩→2/3 · ⑪→14(间接承载——共置+确定性,无取号耦合差分)· ⑫→6(d)(e)/2 · ⑬→15(部分覆盖:面积判据实现同样通过 a1_ec2_press——取号耦合差分夹具登记待核销)· ⑭→10/O2(断言 B 收紧达成区占比)· ⑮→2/3 | +| 规模 | 19 条 AC + A4×15 全落位 + 黄金五板;⑪ 差分承载与 ⑬ 差分夹具登记待核销(间接承载已注明),5 处标注问题的修正明细见评审日志 | + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| OQ1 | F4 全量批量工具的 owner 排期:调参期红线工具(断言 A/A′/B/B′ + 报告 C + 诊断 D,N=256/行)——与 D2 OQ5 曲线导出工具同批登记排期(排期倒挂:A1 定稿→首行数值回填→即需抽查;临时载体 = 冒烟缩样 AC-6/10,统计意义有限,如实登记) | A1 交付物 / C3 消费 | C3 调参期前 | +| OQ2 | 生成性能预算与异步化选项:① 预算数值定稿——F2c 谓词形式已冻结(max_gen_attempts × W×H ≤ C),C = 16384 为 provisional 占位,ADR 裁定正式 C 与 headless debug 基准(如"64×64 单次求解 ≤ X ms");② 解空间圈定——死路二:attempts 并行化(毁 board_gen 流序即毁回放)、分帧/协程化(跨帧在飞 open_stream 实例与 D2 快照切点契约冲突,需 D3 扩容修订);活路四:同步 + 加载幕(B1 换图相位)、主线程分帧 + D3 快照扩容修订、WorkerThread + D2 EC13 单线程假设接口漂移修订(join 先于任何快照切出)、B1 预测性预生成(线性图结构的死时间生成——scope 计数器推进时点须入回放语义);③ 实现姿态约束——最小不动点可经单调 worklist 传播实现(次序无关性不强制全板重扫描——F1 注记⑤),禁全对全子集比较(ADR 验收底线);④ F4 全量工具须支持分片续跑("分钟级"成立域 = worklist 实现 ∧ 大面积行稀少,见 F4 阈值⑤) | 实现期 ADR / perf-profile | Technical Setup | +| OQ3 | 度量独立入口分解面确认:推理器/F3 度量/合法性谓词三入口暴露(AC-7/8/9/12 单元腿前提——缺口 G1);若 ADR 裁决不暴露,四条 AC 单元腿降级重议 | 实现期 ADR | 实现期 | +| OQ4 | biome 词表 owner 定稿:board_biome 值域现只校验非空(EC13);词表定稿后补域校验 + 参考行占位值替换(接口漂移) | B4/C1 域 + AD 门禁 | 垂直切片前 | +| OQ5 | 黄金向量手工推导第二人复核:五板逐格推导记录入版本库后安排复核(qa-lead 缺口登记——推导错误即假失配) | A1 定稿评审 | A1 /design-review | +| OQ6 | 精英/Boss 第三轴落点重审:节点形态定稿后强制重审——① Dmax 行约定是否覆盖全部 Boss 场景 ② 精英节点是否需第三键维 / elite_、boss_ 字段激活(分支地图同深度并存精英与普通节点时);激活即走 D2 Rule 4 改键通道 + 本表 schema 修订(接口漂移迁移,退路已登记) | C1/C3 GDD | C1 定稿时 | + +待拍板(格式迁移中发现的原文与 registry 落差,不自行裁决): + +| # | 事项 | 来源 | +|---|---|---| +| T1 | registry `a2_initial_expansion.degenerate_gap` 与 systems-index 登记了一条 A1 修订义务(反例 B / N=1 行 100% 退化):F5 legality 增 `R₀ ≠ S`(违例 = 尝试作废重试)+ F4 断言 A′ 族严格零覆盖 + Rule 4 排除域基准措辞钉死"以最终产物板计"。该义务未落入原 A1 GDD 任何版本(F5 legality 现形 = clearable ∧ \|S\| ≥ min_safe ∧ 结构不变式,R₀=S 的退化板 clearable 照过)。本次为格式迁移、禁止加规则,原样保留现形并在此登记——待用户/修订流程裁决是否按既有登记走接口漂移修订 | design/registry/entities.yaml + systems-index 门禁记录 | + +缺口登记(原测试口径/AC 内的 G 编号,保留): + +| # | 缺口 | 处置 | +|---|---|---| +| G1 | 度量独立入口分解面未确认(AC-7/8/9/12 单元腿前提) | 归 OQ3,实现期 ADR 裁决;不暴露则四条 AC 单元腿降级重议 | +| G2 | AC-9 输入表示序不变量不覆盖完全开据定序自由度的穷尽验证 | out-of-scope,如实登记 | +| G4 | a1_ec2_press 的 shortfall>0 搜索种子 σ*′ 若搜索无解,后备语义未定 | 登记待核销,协议同 AC-12 种子搜索 | + +## 去向映射(零丢失审计证据) + +| 原章节 | 落点 | +|---|---| +| 头部(状态/版本/支柱/层级/一句话/复核注记) | design 头部(状态/版本/一句话/推演件指针);承载支柱与层级定位并入 design 设计目的定位段;复核注记(CD/AD 未经咨询、生产前人工复核)→ 本文件开放问题区关联 OQ5 + 版本历史沿革 | +| 速览表 | 定位/拥有/不拥有 → design 系统范围;上游输入/下游输出 → design 依赖与输出;硬约束 → design 生成管线与随机流纪律规则 3 + schema 节;缺位后果 → design 设计目的 | +| 体验锚点(P1/P4/P3 表、情绪目标、参考感受、反向护栏) | 结论(五条体验承诺)→ design 设计目的;全部论证 → 本文件「体验锚点」问题节 | +| 全局流程(五阶段字符画 + 出口分发表) | design 核心循环(原样保留) | +| 核心规则 1–10(人话 + 举证) | 人话 → design 各领域章节编号规则(规则 1/6 → 生成管线与随机流纪律;规则 4 → 出生区与可达域;规则 5/9 → 变异布置与雾形边界;规则 2/3 → 可推理率度量与验收算子;规则 7 → 清图预算增量;规则 8/10 → 参数表节);举证中的计算指向保留为 F 编号引用 | +| 枚举(八项指标集 / solver_rules) | solver_rules → design 可推理率节规则 5 + schema 表;八项指标集 → design 依赖与输出产物行 + AC-16(逐字段枚举保留);指标逐项语义 → 本附录 A 只读观测量清单 + design 各 F 节 | +| F1 结论 + 推导 + 口径注记 + Variables + 验算 | 结论与公式本体 + 口径钉死表 + 变量表 + 值域边界 → design F1 小节;推导论证(两通道、初始态、次序无关、clearable 两闸理由、完整验算例)→ 本文件「可推理率」问题节 | +| F2/F2b/F2c 结论 + 推导 + 联合可行域裁决 + 验算 | 结论与判定式 → design 加载期校验表;边计数推导、sound 性论证、禁墙钟论证、裁决表、验算表、极端行为 → 本文件「参数域」问题节 | +| F3 结论 + 推导 + 口径注记 + Variables + 验算 | 结论与公式 + 口径表 + 变量表 → design 清图预算增量节;分母取舍、两主体口径、边界形态观测意义、验算例 → 本文件「雾的成本」问题节 | +| F4 结论 + 推导 + 阈值理由 + Variables + 验算 | 结论与断言表 → design F4 小节;阈值裁定理由五条、σ 打包论证、成本论证、验算行 → 本文件「调参期红线」问题节 | +| F5 结论 + 推导 + 注记 | 结论与公式 + 注记表 + EC4/EC5 出口表 → design F5 小节;三分支取舍、合法性闸前置理由、EC5 玩家侧语义论证 → 本文件「验收算子」问题节 | +| 显式排除声明(归属表) | design 系统范围不负责清单(括注归属)+ 依赖与输出 | +| 措辞登记 | design 依赖与输出节前注记;论证 → 本文件「措辞」问题节 | +| MVP 参考行 | design 调参表默认列(provisional 标注);参考行明细与自洽性验证 → 本文件「验收设施」问题节 | +| 状态(五阶段失败语义 + 不变式) | design 生成管线节失败语义表 + 不变式行 | +| 边界情况 EC1–EC14 | EC1/EC2/EC3/EC6/EC12/EC13 → design 变异布置节与出生区节编号规则;EC4/EC5/EC8/EC9 → design F5 小节出口表;EC7/EC10/EC14 → design 生成管线节规则;EC11 → design 加载期校验节规则;判据与依据编号全部保留 | +| 依赖(上游/下游/A4×15 落位表/跨系统对账登记/双向一致性核对) | design 依赖与输出(原样保留) | +| 呈现与 UI 契约(5 行数据面义务) | design 依赖与输出末表(原样保留) | +| Tuning Knobs(schema + 加载期校验 + 第三轴登记 + Knob 表 + 非 knob + 工具参数 + Knob 交互) | schema + 校验 + 第三轴 → design 参数表节;调参表(字段|域|默认|owner)→ design 调参表;调过高/调过低后果 → 本文件附录 C;非 knob 结构冻结清单与 Knob 交互 → design 调参表脚注 | +| 验收标准(测试口径 8 行 / 夹具规格 / out-of-scope deferral / AC-1–19 / 覆盖矩阵) | AC 19 条 GWT(编号不动、判定口径保留)→ design 验收标准;测试口径详表 + 夹具规格 + deferral 表 + 覆盖矩阵 → 本文件「验收设施」问题节 + 附录 A/B/D | +| 开放悬案 OQ1–6 | 本文件开放问题区(原编号保留) | +| 版本历史(3 行) | design 版本历史(原 3 行保留 + 追加 v2 迁移行) | diff --git a/design/gdd/analysis/a2-minesweeper-rules.md b/design/gdd/analysis/a2-minesweeper-rules.md new file mode 100644 index 00000000..8986ac30 --- /dev/null +++ b/design/gdd/analysis/a2-minesweeper-rules.md @@ -0,0 +1,302 @@ +# A2 排雷玩法规则 设计推演 + +> 状态:concluded · 本文只装"为什么"——`design/gdd/a2-minesweeper-rules.md`(design 件)只收本文的结论。 +> 修订时新增问题节,不删旧节(历史即依据)。OQ 编号与 design 件共用同一套。 + +## 问题:为什么数字与事实流必须恒真、拒绝必须可辨? + +### 广度分析 + +- 候选 A:信息诚实(数字恒真透传、拒绝携带可辨原因)——收益:玩家推理可兑现,"我算对了"成立;代价:呈现层不能以"隐藏"修补规则缺陷,规则层工作量前置。 +- 候选 B:战争迷雾式数据隐藏(数据层也不给真值)——收益:实现宽松;代价:P1"每一步都算数"直接死亡,连锁与推理失去地基。 +- 候选 C:弹性/含混提示(拒绝不给结构原因)——收益:文案自由;代价:玩家无法分辨"须先拔旗"与"非相邻",拒绝体验含混 = P1 死亡体验。 + +### 深度分析 + +本作 P1 支柱的执行面是"系统回应的每一格都与玩家推演的一致"。数字提示是扫雷推理的唯一原料,雾格只是呈现层不显示——数据层若说谎,推理兑现即坍塌。反向护栏由此确立:任何"规则层说谎"的形态(数字失真、连锁静默变异分支、拒绝原因含混)默认砍掉;呈现层的隐藏必须以数据层恒真为前提。 + +### 综合判断 + +采用候选 A:digit 恒真透传(含雾格,仅 kind=safe 携带)、三源拒绝 + sub_reason 封闭枚举、事件发射规范序钉死。参考感受:Into the Breach 的信息诚实——拒绝你的规则从不含糊其辞。结论落 design 件:规则 1/2、三事件回边表、sub_reason 枚举、AC-19/20/22。 + +## 问题:为什么连锁算子必须与 A1 生成端度量同构,且零特判可行? + +### 广度分析 + +- 候选 A:A2 连锁算子与 A1 F1 算子①逐字同构——收益:连锁放大推理的兑现与生成端可推理率度量同源,"连锁传播与生成端度量同构"成立;代价:两系统算子修订必须联动(metric_version 跳变)。 +- 候选 B:A2 自写一套近似 flood fill——收益:实现自由;代价:玩家实际连锁规模与 A1 度量脱节,P1 兑现失真。 +- 候选 C:为雾格/变异写运行期分支——收益:局部直观;代价:违反零特判,变异系统任何扩展都要回改 A2。 + +### 深度分析 + +同构论证逐点成立:R↔X 换名后触发谓词同(成员资格 ∧ 真实数字 = 0——禁读显示值,雾格显示空 ≠ 0 格)、传播目标同(8 邻安全格——雷永不入链)、封闭域同(S)、单调最小不动点语义同(次序无关性两侧成立——worklist 任意定序、结果唯一)。同构主张范围 = 算子①本身:A2 无算子②推导项,不主张 O = R∞。增量合法性引理:Cl 单调幂等 ⇒ 逐动作增量求值 ≡ 一次全量闭包(与 A1 生成期同值)。 + +零特判合规两臂:雾格恒非 0 由 VariationTable 跨字段校验承重 ⇒ "可被链开"与"永不传播"两个方向都是定理不是分支;旗格不入链是集合减法(`∖ G`)不是分支——旗是 A2 一等规则对象、非变异属性。A2 运行期旗非空时传播集排除旗格(连锁不越旗);双包含回验不受影响——回验只在 install 时点发生,彼时 G = ∅(同构域 = G=∅ 域的依据:A1 生成期与 A2 install/初始展开时旗集恒空,`S ∖ G = S`)。 + +### 综合判断 + +采用候选 A + C 的反面:F2 与 A1 F1 算子①在 G=∅ 域逐字同构,A2 代码零变异分支。结论落 design 件:F2 结论与公式、实现期红线 1/3/4。 + +## 问题:为什么初始展开必须恰等 R₀——不能外扩一环,也不能只开 3×3? + +### 广度分析 + +- 候选 A:恰等 R₀(B3 ∪ flood 闭包)——收益:与 A1 求解器初始视野逐格相等,度量保真;代价:定义版权在 A1,A2 禁独立改动。 +- 候选 B:外加一圈"安全边距"(外扩 1 环)——收益:手感更松;代价:破双包含臂①(展开域 ⊆ 排除域),可能开出变异格。 +- 候选 C:只开出生 3×3——收益:保守;代价:破臂②(求解器初始态 ⊆ 展开域),A1 度量吃亏、回退口径触发。 + +### 深度分析 + +双包含回验两臂构造性成立:臂① R₀ ⊆ 排除域(以最终产物板计:出生 3×3 ∪ 闭包 ∪ 外扩 1 环)——给"不触变异"保证;臂② A1 求解器初始态 = R₀ ⊆ O₀ = R₀——恰等,给"A1 度量不吃亏"保证。两义务来源分开陈述:性质 1(恒不开雷 R₀ ∩ M = ∅)由 B3 无雷 + 闭包只收安全格独立可证,环内可有雷;"不触变异"则经排除域给出。恰等 ⇒ A1 回退口径(出生 3×3 最小态)不触发、度量保真零降级。 + +退化守卫(不变式④ R₀ ≠ S)的必要性由反例 B 证明(跨 GDD 阻断缺口):A1 现行布雷数下限 N ≥ 1 必要不充分——8×8 板 8 雷 placed、先布后删幸存 1 颗距离-2 雷 → 最终板全 0 格连通 → R₀ = S,且 A1 F4 断言 A(inferability=1.0)零拦截、N=1 参数行 100% 退化。登记义务 = A1 F5 legality 增 R₀ ≠ S(违例 = 该次尝试废弃重试,clearable 同款)+ F4 批量断言严格零 + Rule 4 排除域基准措辞按最终产物板计;A2 侧不变式④为纵深防御,双包含两臂不受影响。 + +### 综合判断 + +采用候选 A:O₀ = R₀ 恰等,显式不含外扩 1 环;不变式④ install 拒绝清图退化板(纵深防御,非 A1 clearable 闸复验——不可达安全格板 A2 照常运行)。结论落 design 件:规则 4、F3、实现期红线 2/5/6、跨 GDD 登记义务 A1-1/A1-2。 + +## 问题:为什么批次按语义序(清图挖 > 停手 > 其余)而非到达序仲裁? + +### 广度分析 + +- 候选 A:朴素到达序——收益:直觉;代价:见下反例,直接违反 A3 Rule 3 优先级。 +- 候选 B:固定语义序 C_dig ≺ C_stop ≺ C_other——收益:四组合与 A3 Rule 3 逐项一致;代价:玩家提交顺序不再决定结算顺序,须以"批次 = 单帧整批"心智理解。 +- 候选 C:玩家指定优先级——收益:表达力;代价:UI 与规则复杂度爆炸,MVP 不需要。 + +### 深度分析 + +反例检验(语义序的存在性证明):朴素到达序下真值表 #4([dig耗, stop])产出 (ap_exhausted, RAP+0)——违反 stopped > ap_exhausted;#2([stop, dig清])吞掉清图高光——违反 cleared > stopped(EC10 同族缺陷)。语义序将两者校正为 (stopped, RAP+remaining) 与 (cleared, new_board)。 + +分类时点二态论证:排序用批次初态、胜者路径用结算时点真值;C_dig 成员最先执行 ⇒ 其二态恒重合;C_other dig 的二态可因前序 C_other dig 执行而分歧(其批内前缀不可能含 C_dig/stop——二者任一执行即收轮、C_other 全体 voided),分歧时按结算时点真值定胜者;该前缀无 stop ⇒ 与 A3 Rule 3(cleared > stopped)不可能冲突。 + +到达序无关性的收窄(定稿裁决):(path_tag, continuation_hint, AP/RAP 终值) 对任意到达序不变(全批域);执行/voided 划分的无关性仅对异类竞争成立(四组合真值表);同类多成员批内,执行成员、棋盘终态(旗位/小人位)与划分由类内稳定序(到达序)决定——不同到达序 = 不同输入,均为合法确定行为非缺陷(例:ap=1 下 [move, dig耗] 两意图均执行、[dig耗, move] 的 move voided 且小人位不同;[flag_place, dig耗] 旗入板、[dig耗, flag_place] 旗 voided);cell_revealed/旗事件流同理以到达序为规范序。类内序必须钉死的理由:未钉死会使事件流与奖励格归属漂移——规范性义务。 + +联合清图让位条款(显式设计后果):批内个体非清的 dig 联合可清图时,stop 仍先执行并胜出(C_stop ≺ C_other)——联合清图高光被停手让位。生产面在单意图帧提交下不可构造(D1 强制项⑥),F4 语义保留为防御性兜底,AC-10 义务不豁免。 + +### 综合判断 + +采用候选 B:语义序仲裁 + 类内稳定序 + 结算时点真值键控出口;voided(零棋盘效果/零扣费/零事件)与非法拒绝严格区分。结论落 design 件:规则 9、F4、四组合真值表、EC6/EC13/EC15。 + +## 问题:为什么挖开雷格不是死亡,而是可通行的"走过的伤疤"? + +### 广度分析 + +- 候选 A:挖雷 = 轮次结束/死亡——收益:经典扫雷直觉;代价:一次失误抹掉整轮规划,与 P4"地牢下探"氛围冲突,且降级板上无兜底通路。 +- 候选 B:挖雷 = 事实上报 + 格可通行——收益:雷成为"走过的伤疤",探索不被单点终结;代价:必须配套敌方优势计价,否则雷失去威慑。 +- 候选 C:挖雷后封锁该格——收益:保留惩罚感;代价:制造不可达死区,与连通性保障冲突。 + +### 深度分析 + +采用候选 B 的计价论证:雷格通行是有价捷径——挖已知雷 = 花 1 AP + 给敌方送优势,经风险收益管线计价;是玩家主动抄近路的权衡,也是降级板上的兜底通路,不是免费通道。"被迫挖雷过路"的构图被生成期拦截——A1 clearable 闸按"安全格 8 邻接图"连通计,挖雷通路不计入,故不存在结构性强制过路费板。 + +### 综合判断 + +采用候选 B(GD-2 裁决,登记为工程债):kind=mine 事件、轮次不结束、格可通行、不计清图、同轮多雷各自独立事实。结论落 design 件:规则 11、EC10、AC-15。 + +## 问题:为什么剥夺经典扫雷的 chord 双击快开? + +### 广度分析 + +- 候选 A:保留 chord——收益:肌肉记忆友好;代价:零 AP 快开稀释"每个决策都值得花一 AP"的预算意义。 +- 候选 B:剥夺 chord,已开数字格 = 移动——收益:预算意义完整,点击语义单一;代价:怀旧玩家首遇预期违背,须教学提示补偿。 +- 候选 C:chord 以道具/后期解锁形式后置——收益:两全;代价:引入新系统复杂度,MVP 不承载。 + +### 深度分析 + +本作保留经典扫雷连锁展开的"哗啦一片"的免费与瞬时(连锁本身仍免费),但剥夺 chord 双击——每个决策都值得花一 AP,快开是对预算意义的稀释。chord 缺席的兑付节拍补偿(标中旗即时兑现候选)归 B1/B4 高光兑现节点条目化(创意风险 #3)。这个"没有"是可验收的负空间:对外 API 封闭枚举里根本没有快开入口,将来谁想加就是接口漂移修订。 + +### 综合判断 + +采用候选 B(定稿):无 chord,点击已开格唯一含义 = 移动;chord 肌肉记忆首遇提示登记 D1 强制项③(非模态、每 run 一次,A4 先例)。结论落 design 件:规则 12、AC-4、覆盖矩阵"API 封闭枚举"行。 + +## 问题:为什么旗判定轮末统一批、且诚实重复上报? + +### 广度分析 + +- 候选 A:插旗即判——收益:即时反馈;代价:旗位在轮内可变(拔/挪),即时判定会产出与终局旗位脱节的事实流。 +- 候选 B:轮末统一判定批,基线 = 事务开始旗位——收益:判定与事件序契约(先旗后结束)统一,基线与到达序无关;代价:反馈延迟到收轮,须呈现层补偿。 +- 候选 C:A2 侧去重(每雷只报一次判中)——收益:下游简单;代价:A2 越权裁决结算语义,"每雷实例至多兑一次"的资格键在结算侧。 + +### 深度分析 + +判定基线取"轮次结束事务开始时的旗位":被拔的旗不判、被 voided 的旗动作不留痕(无幽灵旗);语义序使基线与到达序无关(EC13)。行主序枚举保证确定性。旗留在雷上每轮诚实重复报判中——A2 只报事实,去重是结算侧的事(资格以雷格实例为键,A3 Rule 8/OQ5;B1 强制项③)。雾内旗恒判空是零特判的自然结果(雾区无雷),不是特判分支。 + +### 综合判断 + +采用候选 B:轮末统一判定批 + 诚实重复上报,去重归 B1/B4。结论落 design 件:规则 8、三事件回边 flag_judged 行、EC13、AC-12。 + +## 问题:为什么拒绝要分三源并携带 sub_reason 封闭枚举? + +### 广度分析 + +- 候选 A:单一 reject 布尔——收益:实现最简;代价:玩家无法分辨"须先拔旗"与"非相邻",文案无从下手。 +- 候选 B:三源 reject_reason + board_face 内 sub_reason 封闭枚举——收益:玩家语义数据可分,呈现层直接映射;代价:枚举即接口契约,批后修改 = 接口漂移。 +- 候选 C:自由字符串原因——收益:灵活;代价:不可判定、不可本地化、不可测试。 + +### 深度分析 + +UX-1 裁决(批前免费窗口):"须先拔旗"与"非相邻"玩家语义截然不同的情形必须数据可分,故 board_face 携带 sub_reason 封闭枚举七值;目录外输入(越界坐标等)单列 out_of_catalog——debug 级诊断,不与玩家语义混桶、禁静默吞。三源(board_face/resource_face/phase)互异可判定,phase 源含装板违例 board_invalid 并列第四值。 + +### 综合判断 + +采用候选 B:reject_reason 四值 + sub_reason 七值封闭枚举;玩家面文案归 D1(映射源 = 枚举)。结论落 design 件:三源拒绝表、sub_reason 表、AC-21。 + +## 问题:为什么装板要四条不变式 fail-fast、且抗表重载靠生成期快照? + +### 广度分析 + +- 候选 A:装板信任输入、不做校验——收益:代码薄;代价:违例板带病运行,错误在挖掘期以诡异形态爆发。 +- 候选 B:装板当场验四条结构不变式、违例 fail-fast——收益:信任边界清晰,诊断恰一类别可测试;代价:产物构造方须守 schema。 +- 候选 C:持数据表活引用、运行时随表——收益:热重载即时生效;代价:战斗中改表改变进行中的板行为,回放与确定性破裂。 + +### 深度分析 + +不变式④与其余三条性质不同:①–③是产物良构性,④是 A2 状态机良构性守卫(防"初始展开即整图"使轮次棋盘路径出口结构性不可达);生产面由 A1 legality 闸保证,④为纵深防御、非 A1 clearable 闸复验。产物只能经程序构造或 A1 产生,运行期不存在该输入源——故违例 = 程序构造错误,fail-fast + error 诊断是正确姿态(进程致命保留给数据通道)。 + +快照 vs 活引用:板持生成期变异快照,热重载改表行为零变化;A2 零表读取使抗重载为构造性(不是运行时防护,而是架构上没有读取路径);行为级代理验收 = 换根重放事件流逐位相同(AC-18)。 + +### 综合判断 + +采用候选 B + 快照:四条不变式 fail-fast(诊断封闭枚举)、生成期快照抗重载。结论落 design 件:规则 3、install 不变式表、EC2/EC8、AC-1b/AC-2/AC-18。 + +## 数值锚定与验算记录 + +### F1 变量域与验算 + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| σ | 棋盘状态 | —— | (B, O, G) 三元组;B 由 A1 产物给定 | +| O(σ) | 格集 | R₀ ⊆ O ⊆ U | 已开集(含初始格、连锁格、已引爆雷格) | +| S(B) | 格集 | U ∖ M | 安全格全集(含雾格、含 R₀ 格、含不可达格) | +| rem(σ) | int | [0, \|S\|] | rem₀ = \|S ∖ R₀\|;rem=0 即 cleared 路径 | + +- Output Range:cleared ∈ bool。Example:10×10 板、|S|=88、R₀=20 → rem₀=68;挖开末 1 安全格(chain_size=3 连锁)→ O ∩ S = S → cleared,A2 经 `last_round_end` 外发 (cleared, new_board)。 +- 求值点注:"批次 = 单一结算事务"指批次作为原子性单位,不改逐 dig 粒度,与 A3 `request_dig` 按次调用上报的契约一致。 + +### F2 变量域与验算 + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| c | 格 | U ∖ O | 挖掘目标(棋盘面合法性先行,规则 5) | +| O / O′ | 格集 | 见 F1 | 挖前/挖后已开集 | +| num(x) | int {0..8} | x ∈ S | 真实数字(非显示值——漂移风险见 design 实现期红线①) | +| chain_size | int | [0, \|S\|−1] | A3 request_dig 第一参数 | +| clears_map | bool | —— | 第二参数;≡ F1 cleared | + +- Output Range:chain_size=0 ⟺ c 非 0 格或其安全邻已全开。Example:b_chain 板挖 (3,6)(0 格)→ 级联 13 格一次开尽墙下封闭区(雷墙与板底之间的口袋形安全区,下称"封闭区"),chain_size=13、clears_map 视余量而定;全程 ap 只扣首格 1。 +- 同构主张范围 = 算子①本身(A2 无算子②推导项,不主张 O = R∞);同构域 = G=∅ 域(A1 生成期与 A2 install/初始展开时旗集恒空,`S ∖ G = S`);双包含回验不受影响——回验只在 install 时点发生,彼时 G = ∅。 + +### F3 变量域与验算 + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| B3 | 格集 \|B3\|=9 | ⊆ U | 出生 3×3(产物携带);小人出生 = B3 中心格 | +| R₀ = O₀ | 格集 | B3 ⊆ R₀ ⊆ S | 初始展开域;下界 9(无 0 格传播时) | + +- Output Range:|R₀| ∈ [9, |S|−1](上端点被不变式④排除)。域非空前提:非空 ⟺ |S| ≥ 10——|S| = 9 的板必有 S = B3 ⇒ R₀ = S,由不变式④在 install 拒绝(EC1 同族),不产生运行态 R₀;现行 A1 参数域(W,H ≥ 8)下 |S| ≤ 9 仅程序构造板可达。 +- Example:b_spawn 板(7×7、竖墙雷 6 枚):R₀ = x≥2 全安全格 35 格,臂① R₀ ⊆ 排除域(35 + 域外安全 8 + 环内雷)✓、臂② 测试侧朴素 BFS 重算逐格恰等 ✓。 +- 义务来源分开陈述:臂①给"不触变异"保证(经排除域);"不触雷"由性质 1 独立给出(环内可有雷)。 + +### F4 变量域与验算 + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| ⟨I₁,…,Iₙ⟩ | 意图序列 | n ≥ 1 | 到达序;∈ {dig, flag_place, flag_pull, move, stop} | +| class(I) | enum 三类 | —— | 排序类别(批次初态求值) | +| ω | 意图 ∪ {∅} | —— | 胜者;∅ = 轮次继续 | + +- Output Range:(path_tag ∪ {无}, continuation_hint ∪ {无}, RAP 增量, 每意图 ∈ {执行, voided, 拒绝})。Example:ap=3、rem=1、批次 [stop, dig(末格)]:C_dig 先结算 → try_spend true、ap 3→2、事务内 cleared 胜出 → 外发 (cleared, new_board)、RAP+0;stop voided。到达序翻转结果逐项相同(真值表 #1/#2)。 +- 胜者出口映射键控细节:dig 胜 ∧ 结算时点 clears_map=true → (cleared, new_board)、RAP += 0(剩余 AP 清零,A3 Rule 5)——C_dig 胜者恒满足;非清 C_other dig 可因前序 C_other dig 执行变清图,按结算时点真值入此行。C_stop 胜 → 先发 flag_judged ×N(基线 = 事务开始旗位,停手不改旗)→ 委托 A3.request_stop() → (stopped, same_board)、RAP += remaining_ap(1:1)。耗尽 AP 的 C_other → (ap_exhausted, same_board)、RAP += 0(不变式 remaining=0)。 + +### 夹具登记常数记录 + +- spawn 坐标为新增登记项——b_chain/b_fog = (3,2);按默认"板中心"读法该两板 B3 = {2..4}² 会含 y=4 雷墙雷、违反不变式①、板不可安装(已修正,复核义务见 OQ3)。 + +## 已裁决设计争议(速查) + +| 争议 | 裁决 | 要点 | +|---|---|---| +| GD-1 连锁是否越旗 | 不越旗 | 传播集 `∖ G` 是集合减法非分支;旗 = 1 AP 链阻断器(经典 flood 惯例);G=∅ 域与 A1 算子①同构不变 | +| GD-2 挖雷后果 | 事实上报 + 可通行(有价捷径) | 1 AP + 敌方优势计价;A1 clearable 闸拦截"被迫挖雷"板;工程债登记 | +| UX-1 拒绝原因形态 | sub_reason 封闭枚举 | 批前免费窗口定稿;registry a2_event_surface.api_surface 已同步 | +| 到达序无关性范围 | 收窄 | (path_tag, hint, AP/RAP) 全批域不变;划分序无关仅异类四组合;同类批内由稳定序决定 | +| 联合清图 vs 停手 | 停手让位清图高光 | C_stop ≺ C_other 的显式设计后果;单意图帧下生产面不可构造,AC-10 义务不豁免 | +| round_end 发射者 | 双发射源 | 棋盘路径 A2 发、stopped 路径 A3 直发、payload 同构;A2 禁双发 | +| 事件序 | flag_judged ×N 先于 round_end | 全收轮路径统一;停手不改旗位故基线等价 | +| 已引爆雷格 | 可通行 | 工程债(systems-index 门禁修正记录),AC-15 承载 | +| D1 义务范围 | ×1 → ×6 扩容 | stop 经 settle_batch / 格态可辨 / 悬停预承诺+chord 提示 / 雷格可读 / 路径呈现 / 单意图帧+voided+拒绝可感知 | +| 移动语义 | 瞬移 | 规则层一次事务落位;逐格动画归呈现层;路径序固定 8 邻行主序 | + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | 标错惩罚(偏离登记——实测义务保留,不阻塞设计):候选 = 敌方伏击 +1(标错旗在轮末判定 hit=false 时为敌方追加 1 层伏击;量纲随 B2 ambush 定稿联动);兼任双重职责(A3 OQ2):误标税 + 停手轮/死 RAP 态下旗滥用的唯一价格;一致性推论(上线时生效,无特判):雾内旗恒受罚(A4 EC5)、判定载体 = design 件 `flag_judged.hit=false` 事实流(B1/B4 消费,A2 零改动);惩罚重复性裁决项(实测会话前钉死):hit=true 兑付有去重(每雷实例一次,B1 强制项③),hit=false 惩罚**无去重声明**——按现行轮末判定载体,忘拔的错旗默认每轮 +1 伏击(5 轮 = +5),与一次性收益严重不对称;候选 = 随旗实例去重 / 显式接受每轮累计("流血"设计),须在原型实测会话前落定,防实测数据混淆"一次性过重"与"累积过重";落地动作:原型 v3 补丁实测(可与 v3 终验合并会话)→ 实测支持则修订 design 件(判定批扩展)+ registry,否决则关闭 OQ 并在 A3 OQ2 另定价旗滥用;A2 /design-review 须核对本 OQ 在位,缺位即 blocking | 用户 + 原型小补丁会话 | 垂直切片前置 playtest 合并会话前 | +| 2 | 事件外发载体与捕获 API(实现期 ADR 确认项):signal vs 回调注册、事件捕获断言面的落地形态——依赖事件捕获列表的 THEN(AC-1/3/4/5/6/7–10/12/13/15–20 的事件序列断言)为纸面契约,ADR 未确认前该子集不视为可执行(A1 缺口 G1"度量独立入口"同型);返回值/诊断/静态扫描类断言(AC-1b/2/11/14/16/21/22 及各 AC 的 reject 分支)不在此限、现在即可执行 | 实现期 ADR | Technical Setup | +| 3 | 夹具登记常数第二人复核:各板(spawn 坐标、R₀ 成员集/级联集/digit)手工推导记录入版本库后安排复核——推导错误即假失配/假通过(A1 OQ5 同型);spawn 坐标为新增登记项(b_chain/b_fog=(3,2)——默认中心读法产出不变式①违例板) | A2 /design-review | A2 评审 | +| 4 | A5 探测道具接口预留:显影(未开雾格数字显影)对 A2 查询面的需求形态——接口归 A5 GDD 定稿,design 件不预写(见 design 件依赖 A5 行) | A5 | 第 11 位 | +| 5 | D1 输入接线确认:stop 意图经 `settle_batch` 提交的接线义务(D1 强制项①)与单帧多意图的真实构造性——单意图帧输入收集已升格为 D1 强制项⑥登记约束(stop 点击终结当帧收集或等效;若 D1 保证单意图帧,四组合仲裁退化为结构不可构造——义务仍由 AC-10 冒烟承载、不豁免) | D1 | 第 10 位 | +| 6 | 拔旗价目与旗滥用观测联动:`flag_pull_ap_cost` 复核与"停手轮旗动作占比"指标的 A2 侧观测量(旗动作批占比)——A2 零改动,遥测口径随 A3 复核会话对齐 | A3 OQ2/OQ3 | 低优先 | + +## 附录 A:义务台账与审计记录(历史审计,活契约见 design 件依赖与输出节) + +### 门禁义务落位表(门禁评审逐项核对用映射,缺项即 blocking;义务本体在 design 件) + +| 编号 | 义务 | 来源 | 承载位置 | +|---|---|---|---| +| 1 | A2×4 ① EC4 连锁开雾格(无数字+级联停止+计入清图+奖励入库) | A4 | 规则 6 + EC4 → AC-6 | +| 2 | A2×4 ② EC5 雾内旗恒不中(含标错惩罚一致性推论) | A4 | 规则 8 + EC5 → AC-12 | +| 3 | A2×4 ③ 开格上报携带变体标记 | A4 | 规则 2 + F2 注 → AC-6/18/20 | +| 4 | A2×4 ④ EC8 生成期变异快照抗表重载 | A4 | 规则 3 + EC8 → AC-18 | +| 5 | 双包含臂① A2 展开域 ⊆ A1 排除域 | A1 | 规则 4 + F3 → AC-3 | +| 6 | 双包含臂② A1 求解器初始态 ⊆ A2 展开域(恰等,回退口径不触发) | A1 | 规则 4 + F3 → AC-3 | +| 7 | 初始展开定义落点(免费/计入清图/无奖励) | A3 EC5 委派 | 规则 4 → AC-1 | +| 8 | round_end 棋盘路径外发 + payload 同构 | A3 Rule 9 | 规则 2/10 → AC-7/8/9 | +| 9 | EC4 批次四组合仲裁 + RAP 随胜者 | A3 EC4 deferral | 规则 9 + F4 + EC6 → AC-10 | +| 10 | 已引爆雷格可通行(工程债) | systems-index 门禁修正记录 | 规则 11 + EC10 → AC-15 | +| 11 | 标错惩罚以 OQ 登记(实测义务保留,不阻塞设计) | systems-index 偏离登记 | OQ1(/design-review 核对在位) | + +### 双向一致性核对(定稿时点审计) + +A2 ← A1, A3, A4 ✅(索引依赖分层)· B4 ← A2,B3,D2 ✅ · D1 ← A2,A3,B1 ✅ · D4 ← A2,B1 ✅ · D5 ← A2,A3,B1 ✅ · A5 ← A2,A4,B4 ✅ · D2 表"A2 无直接依赖" ✅——无单向遗漏。 + +### 定稿复核注记 + +lean 门禁下 systems-designer 起草 Formulas(F1–F4 + 反例 B 阻断缺口)、qa-lead 起草/验证 AC(23 条 + 覆盖矩阵 + 夹具坐标规格);creative-director 的体验锚点情绪定位与 art-director 的呈现一致性未经咨询——生产前人工复核(art-bible §1–4 须在 D1 GDD 前创建)。 + +## 附录 B:去向映射(零丢失可审计证据) + +| 原文节/表 | 落点 | +|---|---| +| 头部块(状态/版本/承载支柱/层级/一句话) | design 头部块 | +| 头部复核注记(起草分工 + CD/art-director 未咨询) | 本文附录 A「定稿复核注记」 | +| 速览表·定位行 | design 设计目的 + 依赖与输出引言(TD-SYSTEM-BOUNDARY 回边/瓶颈系统) | +| 速览表·拥有/不拥有行 | design 系统范围(负责/不负责) | +| 速览表·上游输入/下游输出行 | design 依赖与输出双向表 | +| 速览表·硬约束行 | design 规则 1(四零禁令 + headless 可驱动) | +| 术语表(8 条) | design 事件面与枚举·术语表 | +| 体验锚点表 + 反向护栏 | design 设计目的(4 条承诺 + 护栏);论证与参考感受 → 本文问题 1/问题 6 | +| 全局流程·装板与挖掘期字符画 | design 核心循环 | +| 全局流程·批次结算字符画 | design 批次结算与收轮 | +| 全局流程·出口分发表 | design 核心循环·出口分发表 | +| 规则 1/2(含三事件回边表) | design 事件面与枚举(规则 1/2 + 三事件回边表) | +| 规则 3/4 | design 棋盘状态·装板与初始展开(规则 3/4) | +| 规则 5/6/7/8/11/12(含旗价目表) | design 动作规则(规则 5/6/7/8/11/12 + 旗价目表) | +| 规则 9/10(含批次类别表 + 四组合真值表) | design 批次结算与收轮 | +| 枚举节 5 表(字段值/三源拒绝/sub_reason/装板违例诊断/API 面) | design 事件面与枚举 | +| F1–F4 结论/公式/Variables/Output Range/Example | design 行为契约节(Example 验算数据 design 与本文数值锚定节双录) | +| F1–F4 推导论证(同构论证/增量引理/双包含证明/反例检验/无关性收窄/让位条款论证/域非空推导) | 本文问题 2/3/4 + 数值锚定与验算记录 | +| 显式排除声明(归属表) | design 行为契约·显式排除声明 | +| 实现期红线 ×6 | design 行为契约·实现期红线 | +| 状态节(板态/矩阵/四不变式/补充不变式) | design 棋盘状态·装板与初始展开 | +| 边界情况 EC1–EC17 | design 边界情况表;EC1 反例 B 详述 → 本文问题 3 | +| 依赖节·上游/下游表 | design 依赖与输出双向表 | +| B1 强制项 ×3 / D1 强制项 ×6 | design 依赖与输出(同名两表) | +| 门禁义务落位表 ×11 / 双向一致性核对 | 本文附录 A(审计映射;义务本体散见 design 各节) | +| 跨 GDD 新登记义务汇总(A1-1/A1-2/B1×3/D1×6/B1/B4×2) | design 依赖与输出·跨 GDD 新登记义务汇总 | +| 呈现与 UI 契约 ×14 | design 依赖与输出·呈现与数据供给契约 | +| Tuning Knobs | design 调参表 | +| 验收标准·测试口径/夹具板清单/AC×23/覆盖矩阵 | design 验收标准(编号不动) | +| 验收标准·out-of-scope deferral | design P1 / P2 后置表 | +| 开放悬案 OQ1–OQ6 | 本文开放问题(编号不动) | +| 版本历史 ×3 行 | design 版本历史(原样保留 + 追加 v2.0 迁移行) | diff --git a/design/gdd/analysis/a5-minesweeper-aux-items.md b/design/gdd/analysis/a5-minesweeper-aux-items.md new file mode 100644 index 00000000..ae33b4c1 --- /dev/null +++ b/design/gdd/analysis/a5-minesweeper-aux-items.md @@ -0,0 +1,239 @@ +# A5 扫雷辅助道具 设计推演 + +> 状态:concluded · 本文只装"为什么"——`design/gdd/a5-minesweeper-aux-items.md` 只收本文的结论。 +> 修订时新增问题节,不删旧节(历史即依据)。 + +## 问题:为什么"看雷"必须窄域(≤3×3)而"看数字/看奖励"可以全图? + +### 广度分析 + +- 全图级真视常态化:扫雷退化为路径优化——P1"我算对了"塌方,推理玩法自废(反向对照 = 全图透视挂件)。 +- 全面禁止真视:雷险(risk)维度无道具承载,五维度覆盖破缺,"买情报"承诺对雷险困境无货可卖。 +- 窄域真视(≤3×3):代答锁在救急档——玩家买的是一次局部确定性,不是整图答案。 + +### 深度分析 + +信息效果分两级:恢复推理原料(雾格数字、奖励身份)放大玩家自己的推理,越大越好;替代推理(直接雷位)直接给答案,必须小而贵。全图看数字后玩家仍要自己推雷(推理面最大化而非代答),全图看雷则推理整段消失——两者的体验后果不对称。 + +护栏若只是设计倡议会被后续内容侵蚀,所以落成 schema 校验:target 必须 ∈ 各空间 kind 支持域交集,mine_reveal 行 target=board_wide 直接加载致命(夹具 a5_target_violation 为承重校验的测试落点)。 + +### 综合判断 + +真视上限 area_3x3、board_wide 仅对 fog_reveal/reward_preview 开放——恢复推理可全图、替代推理必窄域。design 的规则 3、目标域开放表、校验类别表只承载此结论。 + +## 问题:无效/零信息目标为什么不可用不消耗(可用性门)? + +### 广度分析 + +- 照常消耗(B4 E10 道具段拍板同款):防结算管线反抖动,但挖掘期玩家手滑点错白耗道具 = "烧包"挫败源(反向对照 = 无目标校验的"点了就没了")。 +- 一律点击级拒绝:板级零信息时(全图无雾)玩家可反复尝试同一件道具,每次弹拒绝 = 噪音,且把"这张图用不了"的发现成本转嫁给玩家。 +- 两级门:板级零信息 → 图标灰、入口禁用(B4 usable_now 先例前置呈现);点击级零信息/越板 → 该次拒绝零消耗 + 可辨提示。 + +### 深度分析 + +与 B4 E10 照常消耗是**故意的不同**,区分理由有三:挖掘期非结算管线(无反抖动压力)、可用性守卫在 A5 本地可判(读 A2 只读面即可,不依赖跨系统协调)、误操作代价不对称(消耗品稀缺,F1 供给有界)。A5 零事件订阅,板级判据随挖掘进展翻转,usable 按 query 时点现算——派生信息层零缓存义务。 + +两个附属拍板:① area_3x3 边缘窗口照常受理(resolved_target = 窗口 ∩ 板内格,角 4 格/边 6 格)——拒绝案被否决,因贴边拒绝会把烧包感反噬回护栏初衷,且护栏只缩不扩(≤3×3 不因裁剪放大);② 真视零雷恒有效——"这 9 格没雷"本身就是信息非空,不构成零信息拒绝;组合行各 kind 判据取 OR(爆破包因 ap_grant 恒可用;全图卷 = 有未开雾格 ∨ 有未开资格格)。 + +### 综合判断 + +零信息/无效目标不可用不消耗,板级判据走库存投影 usable=false 前置置灰、点击级判据拒绝零消耗。design 的规则 8、谓词表、EC17/EC18 承载此结论。 + +## 问题:辅助道具的供给渗透如何锚定?(F1 推导) + +### 广度分析 + +- 高渗透(道具管够):道具依赖症——每轮必用、扫雷决策外包给道具,P1 观测面恶化。 +- 零供给/纯战斗池:五维度承诺落空,A4"买情报"出口缺席。 +- 低渗透有界供给:消耗品 + 稀有档分层,供给面天然限流,道具是救急而非常态。 + +### 深度分析 + +推导与验算: + +``` +E[N_aux/板] = |U| × (Σw_aux / W_total) +E[N_rare/板] = |U| × (w_scroll + w_blast) / W_total +默认根:Σw_aux = 5+5+4+4+1+1 = 20;W_total = 100(B4 原池)+ 20 = 120 +|U| = 10(B4 direction_precheck 保守板口径) +``` + +| 情形 | 读数 | 验算 | +|---|---|---| +| 默认根 | E[N_aux] ≈ 10 × 20/120 ≈ 1.7 件/板 | 渗透率 ≈16.7%(战斗类 83.3%,约 17%——P3"买情报的代价是放弃一件战利品"的量化形态) | +| 稀有件 | E[N_rare] ≈ 10 × 2/120 ≈ 0.17 件/板 | ≈每 6 板一件稀有——"稀有"定位可感 | +| 输入缺口 | \|U\| 分布待 A1 actuals p10/p50 | 落地后重跑(B4 预检 11 同批) | + +\|U\| 取 10 是保守板口径(B4 direction_precheck 同款),并非实测分布——A1 actuals 落地后须重跑;aux 行入池后 W_total 由 100 变 120,B4 侧 F1 有联动重算义务(B4×④),禁止两侧独立调参不看渗透率。 + +### 综合判断 + +期望 ≈1.7 件/板、稀有 ≈0.17 件/板——渗透率有界是 P1 护栏的供给面(道具不主导挖掘决策)。六行权重 5/5/4/4/1/1 全 provisional 归 C3;稀有可感度由失衡预警④与 OQ5 在 playtest 验证。 + +## 问题:信息道具的价值方向如何判定?(F3 推导与修正史) + +### 广度分析 + +- 把显影价值读作"单次盲赌更安全":方向性错误——显影后残余池雷浓度反而升高。 +- 把显影价值读作"少赌":盲赌次数(暴露)下降 → 期望踩雷下降,方向正确且与 P1 支柱对齐。 +- 真视价值当量精确换算:涉及伏击暴露与 weaken 收益两期望差,MVP 不闭合、如实登记为间接效应。 + +### 深度分析 + +推导: + +``` +显影:fog_reveal k 格 → 板级 ι(免赌开据占比)上移 + → 残余盲猜安全格 n_s = (1−ι)×|S| 收缩 → 盲赌次数下降 → 期望踩雷 ↓ + 单次盲赌条件概率 P(雷) = |M| ÷ (|M| + n_s) 随 n_s 收缩而上行 + (池内雷浓度升高——安全格被挖走;与 B1 F4 同式, + B1 F4 另设 n_s < 1 适用域守卫 → 残余集空时主式不评估、信息端平凡腿接管) +真视:mine_reveal k 格 → 该 k 格移出"盲挖候选域" + 期望收益 = P(雷|盲) × (伏击暴露期望 − 标中 weaken 收益期望) 的差值锁定 + 精确当量归 B1 对照表(MVP 不闭合——间接效应如实登记) +``` + +| 情形 | 方向 | 说明 | +|---|---|---| +| 显影尘(一雾区) | n_s ↓ / 盲赌暴露 ↓ | 恢复推理原料——P1 正向;单赌 P(雷) 上行属正常(池雷浓度升高) | +| 全图卷(全雾显影) | n_s ↓↓ | 全图数字可读——推理面最大化而非代答(雾区无雷知识叠加) | +| 探巢摆钟(3×3 真视) | 决策域 −k 格 | 替代推理窄域化——稀有供给(F1)限流 | + +修正史:初稿曾把方向写成"P(雷)↓",与引用式 P(雷) = |M| ÷ (|M| + (1−ι)×|S|) 自相矛盾——该式对 ι **递增**;评审改为:显影挖走残余池中的安全格后池内雷浓度升高,单次盲赌命中率上行,收益在"少赌"而非"单赌更安全"(registry formulas 同批同步)。 + +### 综合判断 + +显影与真视均为正向信息增量;定量当量换算归 B1 条款①对照表扩列(义务 B1×④ 注记,MVP 不入对照表)。design 的 F3 节只承载结论与表达式。 + +## 问题:维度覆盖为什么做成加载断言而非内容建议?(F2) + +### 广度分析 + +- 口头建议"每维度都出道具":内容收窄时静默破缺,玩家某类困境无货可买时无人发现。 +- 加载后机器断言:coverage(d) 恒真是可执行的内容审计,破缺即加载期拦截。 + +### 深度分析 + +coverage(d) :⇔ ∃r ∈ AuxItemTable : d ∈ r.dimension_tags ∧ reward_row(r.item_id).weight > 0,断言 ∀d ∈ {info, risk, action, plan, counter}。注意"活行"双条件:定义表有行且 RewardTable 对应行 weight>0——光有定义没进池等于没有。维度本身是内容标尺非运行时逻辑,代码零维度分支;维度域外(空/枚举外值)为致命校验,因为那是 F2 的前提。 + +### 综合判断 + +五维度 coverage 恒真 = "每维度都有对应道具"的机器可查形态(AC-12);某维度零活行 = 内容收窄信号。design 的 F2 节与校验类别表承载结论。 + +## 问题:获取为什么挂 B4 奖励池、库存为什么与战斗背包正交? + +### 广度分析 + +- 独立掉落流(A5 自建 roll):违反 RNG 单点与预分配确定性脊柱,且奖励归因链断裂。 +- 入 B4 战斗库存混装:两域使用窗口/生命周期不同,混装会把挖掘期道具拖进结算窗口语义。 +- 挂 B4 奖励池 aux 行 + A5 自持关卡域库存:获取归因与供给定价归一处,使用与消亡归另一处。 + +### 深度分析 + +P3 风险收益同源的信息切面:aux 行挤占战斗奖励池权重(渗透率 ≈17%,F1),买情报的代价是放弃一件战利品——代价发生在获取时刻而非使用时刻(故使用零 AP 成本,规则 9)。P2 归因:来源 {cell, round} 逐件记录,"这瓶显影尘是我上一轮捡到的";经真视插旗兑付的 weaken 走既有 flag_hit 结算行,归因链不断。两库存正交但生命周期同构——都活到关卡结束、关卡末同点清空(与 B4 clear_inventory 同点编排)。 + +### 综合判断 + +获取 = B4 roll_board 身份预分配 + B1 授予路由 add_item;库存 = A5 关卡域容器,与 B4 库存互不混装。design 的规则 4/5 与依赖节承载结论。 + +## 问题:叠加层为什么随图消亡、已花道具不返还? + +### 广度分析 + +- 叠加层跨图保留:叠加层是板级派生信息层,格集离开棋盘事实即失效——跨图保留需要重映射,复杂度与误呈现风险双高。 +- 消亡但返还道具:信息已消费(玩家看见了),返还等于白看——沉没语义被破坏。 +- 随图消亡不返还:信息投资沉没,与既有先例一致。 + +### 深度分析 + +先例锚定:A3 旧板旗随旧板消亡同款——RAP 弃图刷新时玩家已理解"板上的投入随板走"。玩家侧含义直白:别在待弃板上花情报。叠加层零回写 A2/B4 状态、零事件订阅,消亡由 B1 在 board retire 点编排(与 B4 retire_mapping 同点),预读徽章因此绝不会指向已消亡映射(EC7)。 + +### 综合判断 + +叠加层随图消亡、不跨图、不返还(EC3/EC7)——design 的状态表与出口分发表承载结论。 + +## 体验锚点论证 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 我算对了 | "推不出的地方,我能买回线索" | 显影恢复雾格数字 = 推理原料回归(数字恒真透传,非新信息源);真视窄域护栏(≤3×3)把"代答"锁在救急档——恢复推理可全图、替代推理必窄域;道具零随机、不引入新赌点 | +| P2 扫雷的因战斗的果 | "这瓶显影尘是我上一轮捡到的" | 获取 = B4 奖励池 aux 行(来源 {cell, round} 逐件归因);使用 = 挖掘期自主时点、效果即时可见;经由真视插旗兑付的 weaken 走既有 flag_hit 结算行——归因链不断 | +| P3 风险收益同源(信息切面) | "买情报的代价是放弃一件战利品" | aux 行挤占战斗奖励池权重(渗透率 ≈17%,F1);叠加层随图消亡——弃图刷新时已花显影沉没(A3 旧板旗随旧板消亡同款先例);真视≠自动兑付(插旗仍花 1 AP) | +| P4 地牢探险氛围 | "地牢里前人留下的工具" | 主题命名(显影尘/探巢摆钟/地穴全图卷);icon 键入 art-bible §4.6 扩容(数据键 = item_id,形状+色相双通道);程序化占位先行(D1 既有裁决) | + +| 反向护栏 | 处置 | +|---|---| +| 全图级真视常态化 = P1 塌方(扫雷退化为路径优化) | 真视窄域承重护栏——board_wide 目标域仅对 fog_reveal/reward_preview 开放;mine_reveal 上限 area_3x3,schema 校验强制 | +| 道具使用变成"烧包"挫败源(无效目标白耗) | 可用性门:板级零信息 → 图标灰、入口禁用;点击级零信息/越板 → 拒绝零消耗——与 B4 E10 照常消耗拍板区分 | +| 道具依赖症(每轮必用、扫雷决策外包给道具) | 失衡预警①道具依赖度(playtest 预注册);真视/显影为消耗品 + 渗透率有界(F1),供给面天然限流 | + +参考感受:杀戮尖塔的消耗品(留着的牌永远是一份规划素材)+ Into the Breach 的信息工具(买的是确定性而非输出);反向对照 = 无目标校验的"点了就没了"(烧包挫败)与全图透视挂件(推理玩法自废)。 + +## 已裁决设计争议(速查) + +| 争议 | 裁决 | 理由 | +|---|---|---| +| area_3x3 边缘窗口拒绝还是受理 | 照常受理,resolved_target = 窗口 ∩ 板内格(角 4 格/边 6 格) | 用户拍板——拒绝案的烧包感反噬护栏初衷;护栏只缩不扩 | +| 零信息目标照常消耗还是不消耗 | 不可用不消耗(板级图标灰/入口禁用、点击级拒绝、组合行取 OR、真视零雷恒有效) | 用户拍板——挖掘期非结算管线、守卫 A5 本地可判、无反抖动压力 | +| F3 显影价值方向 | 收益在盲赌暴露下降,非单赌命中率(P(雷) 对 ι 递增) | 评审修正初稿自相矛盾;registry 同批同步 | +| A5 零消耗 vs B4 E10 照常消耗 | 两制并存、故意不同 | 结算管线反抖动压力 vs 挖掘期手滑代价不对称 | +| 生效前 aux 行入池 | 加载致命(B4 Rule 1 预留值拒绝 fail-fast),AC-12/13 登记 deferral | 对侧义务登记即生效条件(A4 测试口径先例) | + +## 开放问题(OQ) + +| 编号 | 问题 | owner | 时机 | +|---|---|---|---| +| 1 | 命名与 icon 终稿:六行 display_name 为机制名候选——地牢隐喻风味词复核 + art-bible §4.6 样张批定 icon(D1 程序化占位不阻塞) | art-bible/D1 | 样张批 | +| 2 | 预读呈现深度细则:徽章呈现信息量(仅身份 vs 身份+tier——query_identity 投影含 tier)与板面视觉密度(预读徽章 × 未开格 × 变异纹理叠加)——D1 裁决,与 B4 Rule 8 已开格呈现同族复用 | D1 | D1 修订批 | +| 3 | 真视与标错惩罚交互:A2 OQ1 惩罚上线后,真视价值重估(真视→必标中→零惩罚风险——价值上行)与护旗类道具(防惩罚)是否入池 | C3 | A2 OQ1 关闭后 | +| 4 | 未来变异反制扩展:岩浆/冰冻行建行时反制道具语义(A4×③ 框架契约——每个新变异建行批随批出反制行) | 垂直切片批 | 随变异建行 | +| 5 | 全图卷稀有度验证:F1 稀有读数 ≈0.17 件/板在 playtest 的"稀有可感"验证——过疏(恒不见)过密(常态全图)均触发权重复核 | C3 | 垂直切片 playtest | +| 6 | 自动寻路终态声明:预读道具为 MVP 终态(寻路始终玩家心智)还是中间态——B4×⑤ 已声明"自动寻路不做",升格为永久设计态与否随 playtest | C3/CD | playtest 后 | + +## 附录 A:验收覆盖矩阵(审计映射,自原 design 迁入) + +| 类别 | 映射 | +|---|---| +| Core Rules | 1→AC-1/AC-12(F2)· 2→AC-1/AC-2/AC-15/AC-16 · 3→AC-2(a5_target_violation 腿)· 4→AC-12(B4×①…⑤ 生效前提)· 5→AC-3/AC-11 · 6→AC-4(相位 deferral B1×①)· 7→AC-4/AC-6/AC-8/AC-15/AC-16 · 8→AC-5/AC-15/AC-16(可用性门谓词表)· 9→AC-8(零 try_spend = API 负空间)· 10→AC-9/AC-14 | +| Formulas | F1→AC-12 · F2→AC-12 · F3→方向预检(非 AC——定量归 B1×④调参期) | +| Edge Cases | EC1→AC-5 · EC2→AC-5(无雾桩面同构)· EC3/EC7→AC-10 · EC4→AC-6 注记 · EC5/EC6→规则语义(A2/A3 域 OOS——真集成 deferral)· EC9→AC-11 · EC10→AC-7 · EC11→规则 7 序证明(构造不可达——负空间)· EC12→OOS A3 既有 AC(grant_ap 后 request_stop 转存——A3 修订批补腿)· EC13/EC14→AC-13 · EC15→D2 既有门(OOS)· EC16→B4 F2 域(OOS)· EC17→AC-15(边缘腿)· EC18→AC-16(板级/点击级腿) | +| 上游承接 | A4 EC6(显影语义)→规则 2 fog_reveal 行 · A4 OQ2(时效/定价)→收口声明(依赖节)· B4 OQ2(呈现深度)→B4×⑤ · A5-1(counter 回填+悬空校验)→A4×①② / AC-13 · 创意风险 #1(预读)→规则 2 reward_preview / B4×⑤ | + +## 附录 B:双向一致性核对(历史审计,自原 design 迁入) + +systems-index 依赖分层表核对:索引分层 A5 ← A2, A4, B4 ✓(A2/A4/B4 三硬上游全落依赖表;B1 编排 = 层 4 Presentation 消费侧的既有编排唯一性原则——B4 先例:编排调用权不构成依赖分层边);D2 为全员 Foundation 数据边。A4/A5 关系 = counter_item_ids 消费(A4 声明字段、A5 回填 owner)——与索引"A5 ← A4"方向一致。 + +## 去向映射(零丢失审计) + +| 原文章节/表 | 落点 | +|---|---| +| 头部块(状态/版本/支柱/层级/一句话) | design 头部 | +| 速览表(定位/拥有/不拥有/上游/下游/硬约束) | design 设计目的 + 系统范围(含全局硬约束行)+ 依赖与输出 | +| 定位两段散文(信息与行动力市场/没有 A5) | design 设计目的 | +| 体验锚点支柱表 | design 设计目的(压缩为编号承诺)+ analysis 体验锚点论证(全文保留) | +| 反向护栏表 | analysis 体验锚点论证(护栏机制结论同时落 design 规则 3/8 与校验类别) | +| 参考感受行 | analysis 体验锚点论证 | +| 全局流程字符画 + 出口分发表 | design 核心循环 | +| 核心规则 1–10 | design 各领域章节编号人话句(规则编号保留;举证注记语义并入人话或各节表格) | +| 效果明细表 | design 使用效果与目标域 | +| 目标域允许范围表(规则 3 内) | design 使用效果与目标域·目标域开放表 | +| 使用编排分支表 | design 使用规则与校验 | +| 使用目标校验谓词表 + 可用性预判注记 | design 使用规则与校验 | +| 枚举·dimension_tags | design 道具定义 | +| AuxItemTable schema | design 道具定义 | +| MVP 内容行集 + 四条注记 | design 道具定义 | +| schema 校验类别表 | design 道具定义 | +| F1/F2/F3 结论 + 公式表达式 + 变量表 | design 数值结论 | +| F1/F2/F3 推导过程 + 情形/验算表 + F3 修正史 | analysis 问题三/四/五 | +| 状态表 + 不变式 | design 获取与库存·状态 | +| 边界情况 EC1–EC18 | design 使用规则与校验·边界情况(编号保留) | +| 依赖(上游/下游两表) | design 依赖与输出(合并为双向一张表) | +| 跨 GDD 义务清单 21 项 + 编号前缀注记 | design 依赖与输出·跨 GDD 义务清单(活契约,编号保留) | +| 双向一致性核对注记 | analysis 附录 B(历史审计) | +| 呈现与 UI 契约 6 行 + UX Flag + Asset Spec | design 依赖与输出·呈现数据面 | +| Tuning Knobs 表 + knob 交互/非 knob/调参所有权 | design 调参表 | +| 失衡预警 ①–④ | design 调参表·失衡预警 | +| 测试口径 / API 面 / 夹具与致命 manifest / AC 清单 AC-1–AC-16 / 缺口预检 1–5 | design 验收标准(编号保留) | +| 覆盖矩阵 | analysis 附录 A(审计映射) | +| 开放问题 OQ1–OQ6 | analysis 开放问题(编号保留) | +| 版本历史 v1.0-lean / v1.1-lean | design 版本历史(原行保留 + 追加 v2 迁移行) | diff --git a/design/gdd/analysis/b1-settlement.md b/design/gdd/analysis/b1-settlement.md new file mode 100644 index 00000000..d3891e41 --- /dev/null +++ b/design/gdd/analysis/b1-settlement.md @@ -0,0 +1,310 @@ +# B1 战斗结算规则 设计推演 + +> 状态:concluded · 本文只装"为什么"——`design/gdd/b1-settlement.md` 只收本文的结论。 +> 修订时新增问题节,不删旧节(历史即依据)。 +> 全部公式零随机(同输入恒同输出);期望值均为设计期统计量(对账输入 = A1 F4 报告 C 的 actuals,非 target 参数);数值默认 provisional(调参所有权归 C3)。 + +## 问题:结算系统如何兑现三根支柱的体验承诺? + +### 广度分析 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P2 因果归因 | "都是我算出来的" | 每行明细携带来源标注(伤害道具 / 安全格增益 / 标中旗削弱 / 清图奖励 / 雷→伏击);无来源行 = 断言失败(AC-42)——P2 呈现半边的最终兜底在 B1 流水协议 | +| P1 掌控感 | "挖下去值得" | 挖掘决策 EV 健康不等式(A3 条款③)由本系统闭合签字;条款⑤守"推到底"不在数学上吃亏 | +| P3 雷的两面 | "赌桌不是陷阱" | 敌方伏击行与标中旗削弱行同框、同呈现语言 | +| 高光节拍 | "完美被加倍兑现" | 完美排雷双份兑付 + 标中旗行高光(创意风险 #2/#3 机制本体) | + +- 候选 A(明细只记数值不记来源):实现最省,但 P2 归因链断——玩家无法验证"都是我算出来的",排除。 +- 候选 B(来源标注 + 封闭行型 + 无来源即断言):数据层把归因做成机器可判定的契约,呈现侧怎么优化都丢不了来源,采纳。 +- 候选 C(敌我增减益分开两套呈现语言):读屏成本更低,但 P3"雷的两面"需要同构对照,排除。 + +### 深度分析 + +P2 的呈现半边归 D1/B5,但呈现只能从数据里还原来源——来源若不在流水行里,任何呈现优化都救不回归因链。所以"无来源行 = 断言失败"必须是数据层硬断言,不能是呈现自觉。 + +P1 的两条不等式(条款③⑤)若只写进 A3 而没有闭合 owner,就是口号;B1 作为唯一同时握有板面事实流与战斗数值面的系统,是唯一能签字闭合的位置——判据函数角色定为"C3 调参约束生成器",使每次调参被迫重跑对账。 + +### 综合判断 + +反向护栏成文:明细绝不能有无来源的行(P2);判定绝不能不可归因(P1);敌我增减益同构呈现(P3)。任何"合并到看不出来源"的呈现优化,须先确认归因链在数据层仍可还原。design 的「设计目的」四条承诺与「明细流水协议」节只收本结论。 + +## 问题:weaken 锚的归一口径取"每结算瞬时当量"还是"每 AP 总暴露"?(F0 推导) + +### 广度分析 + +| 分支 | schema 可行性 | 后果 | 裁决 | +|---|---|---|---| +| 每结算瞬时当量 | 可行(m_w ∈ [51, 100] 域内) | F2 越域界随移(AP 界 8×m_w / 表界 16×m_w);地板交叉层数 = ⌈100/m_w⌉ = 2;B4 参照零漂移 | **采纳** | +| 每 AP 总暴露 | 不可行——需 m_w > 100,被 percent ∈ [−100, 100] schema 域禁止 | 现实解 = B4 重定最低档参照:safe_cell 活行集中无更弱 buff 行可指,需新增低档行或锚移道具档位——接口漂移 + A4 硬约束①连锁 + 条款①对照表全表重排 | 排除 | + +### 深度分析 + +- **同分母性**:瞬时口径下 weaken 与 defense_up 作用于同一伤害流,"严格大于"在百分点读法与 HP 点读法下等价,锚判定无量纲歧义;总暴露口径引入时长权重(2:1 不对称),把 duration knob 泄漏进锚判定。 +- **机制同构**:B3 本就按结算逐次兑付,瞬时口径是容器原生口径;总暴露需跨结算累计账本,与已锁的"单结算自然边界"(dur=1 每结算清零无存量)冲突。 +- **schema 禁止**:总暴露锚算术需 m_w > 100 越 percent 域,改域 = B3/B4/B1 三侧换算连锁,成本不成比例。 +- **值域依据(@D_ref=12;按现行三输入复核)**:腿一 m_w ≥ 51——65 > 50 严格大于 attack_up 当量 6.0(参照对象 registry 回迁口径,裕度 +30%;池内字面最低档 defense_up −30 当量 3.6 读法下裕度更宽 2.17×——两参照读法均通过,锚判据不受减伤墙批漂移影响);腿二 m_w/100 × D_ref ≥ E_dig → m_w ≥ 38(E_dig = 4.5 面值口径——C3 v3 曲线格子池加权重推;区间读法 4.3–5.1 → 36–43);可行交集下界 = 51(E_dig 口径修正后腿二放宽、腿一回绑);上界 85 = 超出仅在连锁贫瘠板增加裕度,而地板交叉层数恒为 2、边际层早已贬值;schema 顶 100 保留为绝对上界(对应腿二校准上界 ≈1.96:1);下界 60 维持(高于现行可行下界 51——F0 裁决值保守余量,值域修订走 B4/B1 共裁流程非重算批)。 +- **地板规则(B1 clamp 承载,F2 成文)**:每侧 percent 池 clamp ∈ [−100, +100] pp → 乘数域 [0, 2];交叉层数 = ⌈100/m_w⌉ = 2(值域全域恒 2)——第 3 层起边际瞬时当量 = 0;单结算 weaken 池 HP 当量上界 = min(L×m_w, 100)/100 × D_inst(该结算实际裸伤害敞口)。 +- **B3 红警"7 层即触地板"预读更正**:该预读按 −400% 合成带估读;本节地板钉在乘数零点(−100 pp)→ 精确层数 = ⌈100/m_w⌉ = 2——回执同步 B3 OQ1 与 registry `status_effect.anchor_warning`(对账清单 #1/#2);预读实质结论(上调 m_w 的连带成本 = 地板提前到达、边际层贬值)被本裁决吸收。 +- **修订效果量(锚两腿在默认 65 下的读数,provisional @ D_ref=12)**:腿一 7.8 vs 6.0 HP/结算(attack_up 当量——+30%,严格大于 ✓);腿二 7.8 vs E_dig 4.5 HP(+73% ✓);池内字面最低档读法(defense_up 3.6)裕度 2.17×——三读法均通过;历史 −25 值红警复算结论不变(25 vs 50 pp 不过锚 → 修订成立)。 +- **重算复核(B1 重算批)**:锚两腿按现行三输入(duration=1、m_d=−30、C3 v3 曲线 E_dig=4.5)复核通过——腿一 7.8 > 6.0(attack_up 当量,参照对象 registry 回迁口径;裕度 +30%)、腿二 7.8 ≥ 4.5(裕度 +73%);可行交集下界 = 51(腿一回绑),值域 [60,85]/默认 −65 维持(F0 裁决值,共裁修订流程非重算批回收)。 + +### 综合判断 + +采用"每结算瞬时当量"口径(每 AP 总暴露分支排除);m_w 值域 [60, 85]、默认 −65;地板交叉层数 = ⌈100/m_w⌉ = 2;B4 参照零漂移,B3 仅行值域修订(registry 回写)。design 的 F0 节只收此结论与连带界值。 + +| 连带后果项 | 内容 | +|---|---| +| B3 行值 | `StatusEffectTable.weaken.magnitude` −25 → 值域 [60, 85]/默认 −65(registry 回写);B3 AC-14 腿 3 非平凡性复查:16×65 = 1040 越出 percent 域——腿保持非平凡 ✓ | +| F2 越域界随移 | AP 界 = 8×65 = −520%;表界 = 16×65 = −1040%(合成值 B3 原样输出、B1 池 clamp 收口) | +| B4 侧 | 零漂移:defense_up 参照保持 | +| 时长不对称 | 显式接受:defense_up : weaken 全寿命 ≈2:1 不入锚判定——吸收通道 = m_w 上探值域段 + 高光 #3 即时节拍 + duration 为 knob(与 B3 OQ7 playtest 证伪项联动观测) | + +## 问题:四源效果档位如何放在同一把尺上对照?(F1 推导) + +### 广度分析 + +- 候选 A(分源换算率——buff/debuff/道具各一把尺):读数贴近直觉,但 A3 条款①契约要求单一线性换算,分源改率 = 条款①契约作废,排除。 +- 候选 B(单一线性换算全四源共用,E(·) 单位 = 主角 HP 点):一把尺量全部,锚判定无量纲歧义,采纳。 +- 候选 C(账面乘数表达完美翻倍——只记 ×2 不打第二份):账面好看但运行时无兑付载体,玩家实得与账面脱节,排除(见"完美翻倍"问题节)。 + +### 深度分析 + +- **统一当量定义**:E(·) ∈ 主角 HP 点(1 当量 = 1 点主角 HP 的期望净变化;与 B2 ambush 量纲、B4 F6 量纲建议同源)。flat/道具类 E = 值本身(ambush 按 k 个攻击拍线性展开);percent 类 E_inst = |L×m| ÷ 100 × D_exp;时长是显式乘子(E_life = E_inst × 兑付结算数)而非分源改率。 +- **D_ref 选取**:单行全局 knob,provisional 默认 12 = 单结算伏击结构上界 cap×m_amb(B2 F4 已登记值),兼 attack 拍带 [4,16] 与 heavy 带 [8,20] 的中位代表;变更触发全对照表 + 条款③⑤重跑(联动义务)。 +- **锚对照实物(条款②,B3 义务⑤)**:参照对象 = attack_up_basic 行(registry 回迁口径——B3 Rule 10 同款;RewardTable tier=2,effect_id=attack_up)→ 每结算瞬时当量 6.0 HP @ D_ref=12;weaken 修订后每旗 7.8 > 6.0(严格大于 ✓,裕度 +30%);腿二(连锁放大边际基准,provisional 1:1 零链下界):7.8 ≥ E_dig 4.5 ✓(面值口径,C3 v3 曲线重推)。池内字面最低 buff 当量 = defense_up_basic 3.6(−30 现行值)——"最低档"字面读法裕度更宽(2.17×),锚在两读法下均通过;2026-09-11 参照回迁后的预检重跑义务由本批兑现(registry anchor_warning 迁移注记关闭)。 +- **结构性边界注记**:腿二校准上探的可满足上界 = m_w=100 ↔ 相对每结算百分点档 ≈1.96:1——连锁期望放大超过此比值的板形上腿二结构性不可满足(percent 瞬时档被 schema 域封顶);处置 = C3 板形参数(密度/连锁富集)与 m_w 联动约束,非数值修订可解(对账清单 #3/#8——连锁规模分布无既定供给方)。 +- **Example**:一轮挖到 2 个 defense_up(L=2 同实例)→ E_inst = 2 × (0.30 × 12) = 7.2 HP/结算 × 1 结算 = 7.2 全寿命(dur=1——名义值,饱和折减见 F4 敏感性);同轮标中 1 旗 → weaken L=1 → 7.8 HP/结算;踩 1 雷(unit_count=1、attack 拍)→ 该结算敌方附加 +3 HP。 + +### 综合判断 + +单一线性换算全四源共用;E(·) 单位 = 主角 HP 点;Output Range E ∈ [0, +∞)、全表零负值(floor 语义见 F2——percent 池饱和后边际 E = 0);weaken 修订后每旗 7.8 HP > attack_up 6.0(条款②腿一通过)。归口:B1 管对照表与换算维护、D_ref 与 P_perfect 的 SettlementTable 承载;B4 管行值/权重/tier/C 供给(F6);C3 管全部 provisional 数值;B3 管 L×m 合成原样输出(对照表输入侧)。design 的 F1 节收结论、换算式、Variables 与全对照表。 + +## 问题:percent 池怎么合成、地板设在哪、免伤轮合不合法?(F2 推导) + +### 广度分析 + +- 候选 A(合成后统一 clamp 一次):两侧池互相污染,weaken 饱和会吃掉 defense_up 的账面贡献,归因不可读,排除。 +- 候选 B(每池独立 clamp [−100, +100] → 池间乘算 → floor 单一取整):每侧地板独立可读,乘数域 [0, 2],采纳。 +- 候选 C(clamp 域做成 knob):地板是结构语义不是平衡杆——放开 = 负乘数/超 2 倍乘数分支全要重写,定为结构性非 knob。 + +### 深度分析 + +- **三例逐一读法(B3 EC7 登记例的收口)**:weaken AP 界(L=8 × 65 = −520%)与表界(L=16 × 65 = −1040%)均 clamp 到 −100 → M=0——该结算全体存活敌输出归零,第 3 层起边际瞬时当量 = 0(地板吸收);明细行如实显示合成值与终值两端(P2)。defense_up 叠满(L=2 × −30 = −60%)在域内未触地板 → M=0.4。 +- **免伤合法性**:enemy 侧 weaken 饱和(单结算零输出)是乘数域 [0, 2] 自然边界内的设计内结果;hero 侧免伤在现行 −30×2 帽(−60%)下不再由 defense_up 单侧可达——免伤轮(D'=0)的现行可达路径 = weaken 饱和或两侧池叠加触底;强度评估(减伤窗口/免伤频率)归 C3 对账联动(B3 Knob 交互同口径),不属地板规则异常。 +- **Example**:D_raw=20、weaken L=2 → P=−130 → clamp −100 → M=0 → D'=0(零值伤害行,不调 apply_damage);weaken L=1 → 20 × 0.35 = 7;再叠 defense_up L=1 → 20 × 0.7 × 0.35 = 4.9 → floor 4。 +- **地板规则不区分越域来源**:越域深度仅入合成值显示与诊断计数;地板恰边界读法 = 判据函数对 −100 输入的断言腿,非现行行值产物。 + +### 综合判断 + +池内加算 → 每池独立 clamp [−100, +100] → 池间乘算 → floor 单一取整(结构性非 knob);免伤轮(D'=0)是合法设计态——不调 apply_damage、产零值伤害行;Output Range D' ∈ [0, ∞) 整数、零负值分支,MVP 实际域 [0, 20](B2 参考带)。归口:B3 管 L×m 原样输出不 clamp(B3 义务⑧/EC7 对侧);B1 管池归属映射、clamp、乘区次序、取整方向;C3 管池值强度评估。design 的 F2 节收结论、公式、Variables 与三例读法表。 + +## 问题:伤害全序、负值、溢杀与死亡判定边界怎么定?(F3 推导) + +### 广度分析 + +- 候选 A(敌方先手):扫雷的因(伏击池)已在敌侧,玩家没有先手兑现面,"我算出来的"节奏断裂,排除。 +- 候选 B(主角先手 → 逐敌循环 → 道具段三分支全序):玩家动作先兑现、敌方反应后跟,与授予段的归因序一致,采纳。 +- 候选 C(溢杀转移到下一个敌人):一刀清屏的隐性倍率,道具恒定表值的拍板被破坏,排除——溢杀丢弃。 + +### 深度分析 + +- **Example**:goblin(atk=4)L=2 → D_raw=10;weaken L=1 → floor(10 × 0.35) = 3;defense_up L=1 同结算 → floor(10 × 0.5 × 0.35) = 1;knife_adv(power=8)打 hp=6 的敌 → hp 归 0 → Dead、溢杀 2 丢弃、击杀标记进流水、下一件刀打顺位后继。 +- **双死不可达论证(判定段 hero 先判的防御性优先级)**:我方伤敌在道具段与战斗段主角先手攻击、均先于敌方行动兑付;敌方伤我在主角攻击之后——敌行动时点若已敌全灭则先判胜,"hero 与最后敌同时 hp=0"结构上不可达;hero 先判是不可达情形下的纵深防御,不是常态分支。 +- **负值分支禁令的实现面**:percent 池地板 [−100, +100](F2)、flat 值 ≥ 0、hp ∈ [0, max_hp]、治疗 clamp 上界 max_hp(B4 F4 对侧:hp' = min(hp + power, max_hp))、apply_damage 入参 n ≤ 0 = B2 API 拒绝 + 诊断——D'=0 腿由此改走零值行,免伤与零输出必须在明细里可见。 + +### 综合判断 + +三分支全序(主角先手 → 逐敌循环 → 道具段)+ 负值分支禁令(零负伤害/负治疗/负当量)+ 死亡判定:Dead ⇔ hp=0(无负 hp 中间态)、受击即死、hero 死亡战斗段短路、判定段 hero 先判;Output Range 双向伤害 D ∈ [0, ∞)、hp ∈ [0, max_hp]、零转移零存续。归口:B2 管递减/floor 0/Dead 翻转/指针冻结;B1 管全序、调用序、零值行、溢杀丢弃语义、死亡判定时机(B2 义务③的对侧兑现)。design 的 F3 节收结论、四分支公式与 Variables。 + +## 问题:条款③挖掘风险不等式在现行值链下成立吗?(F4 推导) + +### 广度分析 + +- 评估点候选 A(全板平均):被信息端稀释,残余盲猜端的真实风险被平均掉,排除。 +- 评估点候选 B(残余盲猜端最坏点,信息端平凡腿一并登记):风险最坏处配对,"推理有回报"面不丢,采纳。 +- 左臂口径候选:锚行(最低档 buff = defense_up_basic 满寿命当量,合同读法)vs 跨行期望(E_dig 面值口径)——双口径登记防虚高裕度误用。 + +### 深度分析 + +- **V_safe 注记**:原"= D_ref 恒等式"(0.5 × D_ref × 2)随 duration=1(两级奖励批)与 m_d=−30(减伤墙批)消解;现行 V_safe = 0.30 × 12 × 1 = 3.6——锚行满寿命当量,低于跨行期望口径的差距同步收窄(E_dig 现值 4.5)。 +- **V_mine 注记**:单雷边际敌方增益当量上界:1 雷 = 1 层伏击;cap 溢出使第 cap+1 颗雷边际当量 = 0 → 灾难轮不等式自收紧。 +- **ι 代理注记**:actuals p10 未交付前以行 target 代 = 判式面的保守代理(target ≥ 实际 p10 → P(雷) 偏高 → 主式更难过);低 ι 的真实代价不经本式入账——报告 C 交付按总暴露核对消费;重跑义务(对账清单 #6)。 +- **换锚注记**:旧验算的"L1 10×10 d0.10 / L2 12×12 d0.12"系 A1 schema 自洽参考行(registry mvp_reference_rows,非曲线行);本批两腿统一换锚 C3 MVP 行值总表 v3(L1 D0/D1 板 10×10、D2–D4 板 12×12,全轴 d0.25/t0.88)——L2 腿即 v3 曲线内最大常规板形;C3 L2 关卡行值填充批(OQ1)交付后随 actuals 重跑复核。 +- **双口径读数(防虚高裕度误用)**:验算表左臂按锚行当量 V_safe=3.6(A3 条款③"最低档 buff 当量下界"的合同读法);按跨行期望口径(左臂 = P(安全)×E_dig,E_dig=4.5 面值口径)复算:L1 ≈0.60×、L2 ≈0.58×——二元结论不变(两板形、两口径全破)。 +- **结论①(重算回执)**:现行值链下两参考板形破线——登记"通过"回执失效(旧读数 ≈2.0×/2.6× 的三个旧输入:duration=2 / m_d=−50 / v1 曲线参考行);信息端平凡腿不变(零赌博板形不产生破线误判——P(安全)=1 → V_safe > 0 恒成立)。 +- **与 clause3_supply 2.3× 预检的对齐**:该预检系"pp 代理左臂 × cap 右臂"的巧合近似——量纲混用且右臂取结算聚合上界与单格决策单位错配(多计 cap 倍);本闭合改统一 HP 当量 + 边际配对 → registry `clause3_supply.direction_precheck` 退役为历史记录、回执登记重算值(对账清单 #3)。 + +### 综合判断 + +现行值链(duration=1 × m_d=−30 × C3 v3 曲线 d0.25/t0.88)下两参考板形破线(锚行口径 L1 ≈0.48× / L2 ≈0.47×;期望口径 0.60×/0.58× 同向);满配 unit_count=3 深度破线;旧"通过"回执退役为历史记录;破线升格 C3 默认曲线级约束(候选杠杆 = m_d 回调 / duration 恢复跨轮 / D_ref 联动上调 / 板形侧密度×ι 联动对〔d0.28 回退先例〕/ m_amb 下调;全部处"禁独立调参"联动网络 m_w↔D_ref↔m_d↔duration_d,修正方向须 C3 提案 + B1 闭合对账共裁,本重算批如实登记读数、不代裁修正);actuals p10 交付后强制重跑。design 的 F4 节收结论、判据公式、Variables、验算表与结论①–④。 + +## 问题:条款⑤"推到底清图"支配"弃图刷新"吗?(F5 推导) + +### 广度分析 + +- 计价候选 A(逐格按初始比例独立重赌):漏计全池挖掘量——安全格被挖走后池内雷占比持续上升,模型漏项且 actuals 校准无法吸收,系统性偏乐观,排除。 +- 计价候选 B(挖穿残余池计价——均匀交错模型 E[踩雷] = |M|×n_s/(n_s+1)):清图必须挖穿残余盲猜区的真实成本入账,采纳。 +- RAP 计价候选:停手侧 RAP 机会收益按零计价(显式假设)——图源充裕时 RAP 边际低,零价假设对"推到底"偏利一分;本假设下 verdict 已为负,若 C3 为 RAP 计价则结论只会更负——模型边界成文,非遗漏。 + +### 深度分析 + +- **E_dig 推导口径**(2026-09-14 重算批):刀/强治疗已移出格子池入注入池,不摊入单格期望;E[战斗获取] ≈ 1 由注入机制另计;治疗按面值 = 上界读法(实现率折减入敏感性腿,同 F4③)。 +- **实现率折减口径**(E_dig′ = 2.70 provisional——B4 预警②④联动):L1 = −45.3 / −5.3;L2 = −91.2 / −51.2——同向更差(完美翻正腿亦被折减吞没)。 +- **雾态增量(fog_burden_ratio 口径)**:雾格 = 雾元知识可推理安全格(P(安全)=1)→ 属确定格、两策略同挖、不进残余集;雾的条款⑤效应 = 链不可达雾格的全价 AP 抬高轮数——Δr 增量 = |U| × dig_ap_cost ÷ AP_r(A1 例板 U=4 → 0.5 轮;v3 曲线复算:(29.99 + 4) ÷ 8 = 4.25 → 上取整 5——较无雾基线 Δr=4 **+1 轮,增量不再被同轮吸收**,登记读数变化);雾池档位加成对冲入 R_res 侧(E_fog 读数见护栏 A——本批读数失效待重推);actuals fog_burden_ratio p50/p90 交付后重跑(对账清单 #6)。 +- **结构性解读**:残余盲猜区越大(n_s ↑),清图的全池雷险越逼近"踩到全部残余雷"——"是否承担盲猜风险是策略点"(game-concept 设计本意)被本判式定量证实;v3 曲线(d0.25/t0.88)使残余集系统性变大、非完美态被支配加深——与 F4 破线同源(曲线加难侧未同步补偿结算侧奖励/防御杠杆);完美高光与清图支配性互 feed 的设计意图保留(L1 完美双份翻正 +11.3),但非完美态仍负 = 被支配。 +- **护栏 A 注记**:与残余池模型无关;登记读数 (11.1 − 7.2) − 1.5 = +2.4 ∈ (0, 3](v1 曲线/旧格子池口径);2026-09-14 重算批方向性复核:E_dig 修正 4.5 + 雾池构成变更(刀移出格子池——现行雾池加权当量 ≈4.9 面值口径)→ 读数 ≈ −1.1 < 0 越下界(雾纯税化风险——链不可达雾格全价 AP 成本超过当量增量);E_fog 现行池构成下的正式重推随 W2 批(C3 收口批三选一落档),本批登记方向性读数、不代裁。 +- **护栏 B 注记**:|S|_min = min_safe_count 是 A1 行级 knob,调参时按当期行集重算、禁写死 50;|R₀|_max = 20 系参考带经验值、无登记供给方 → 登记 A1 产出义务 R₀ 规模分布上界(F4 报告 C 扩列,缺口预检 9);DPS 期望严格读数的敌 DPS 参照系 v1 曲线 atk 值(4/4/6)——v3 atk(7/5/9/6/9)+50% 后 DPS 界随之上探,正式重推随 W2 批。 + +### 综合判断 + +条款⑤未闭合——挖穿残余池计价下主判式两态为负(L1 仅完美双份翻正,非完美态被支配),实现率折减与多敌语境更差;"推到底"的支配性只在残余集小时自然成立;判据函数角色 = C3/B4 调参约束生成器,AC-40 断言输出与登记约束包一致。C3 已裁约束包②(非支配设计态)+ 计价基元换位 clear_blast;B1 重算批维持裁决②。design 的 F5 节收结论、判据公式、Variables、四态验算表、约束包与两护栏。 + +**P_perfect 默认值建议与相容性(×2 的论证):** + +| 条 | 内容 | +|---|---| +| 建议 | ×2(SettlementTable.p_perfect = 200,provisional,调参所有权 C3;运行时载体域 {100, 200}) | +| ① | 单一线性换算不破坏——乘子作用于 C 当量换算之后、换算 ÷100 已成文(F1 对照表行内显式列),非新增分源换算率(条款①合规) | +| ② | 与条款⑤同向且是现参数下唯一使 L1 翻正的杠杆(+8.0)——完美高光与清图支配性互 feed;非完美态仍负 → C 量级上调约束不因 P_perfect 消解 | +| ③ | 触发谓词 = 板级 flag 记录(零误标 ∧ ≥1 标中),与踩雷正交(盲挖踩雷不破坏 perfect,标错旗才破坏——以"标旗未中"零值行留痕,Core Rule 5.2) | +| ④ | RAP 刷新路径零清图奖励 → 无 perfect farming 环(双份兑付不改此性质——刷新图无兑付点) | +| ⑤ | 与 F0 weaken 修订同向(标中旗价值 ↑ → perfect 可达性 ↑——高光 #2/#3 互 feed) | +| ⑥ | 上界受护栏 B 约束(登记读数 16 ≤ 24 系 v1 曲线 D_ref 口径;C=40/完美 80 越线裁决随 W2——完美加成不得触碰钓鱼线) | + +## 问题:完美排雷的"翻倍"是账面乘数还是真实双份交付? + +### 广度分析 + +- 候选 A(账面乘数——明细记 ×2 名义值):实现一行乘法,但玩家实得与账面脱节,高光沦为数字修饰,排除。 +- 候选 B(真实双份交付——第二份同身份兑付走既有授予通道):道具类 stock 第二次、buff 类 apply_effect 第二次(满层拒绝照旧),玩家实得两件,采纳。 +- 候选 C(任意整数倍乘数):非整数倍数乘数无运行时载体,域收窄为 {100, 200}、域外加载致命。 + +### 深度分析 + +高光行 value 记实际有效兑付(首份 + 第二份被接受部分)、禁记名义值——否则"翻倍"在满层/满血等合法例外下变成假账。首份即零兑付 → 高光行不发(完美谓词仍成立,对账入账 = 实际兑付 0);仅第二份被拒(首份落地)→ 高光行照发、value = 首份当量 + "奖励未生效"行一条——降级语义全覆盖。 + +呈现分工的取舍:事件型高光(完美排雷)发独立高光行——它是一次性事件,需要独立行锚定节拍;属性型强调(标中旗、清图奖励)用行字段标记——它们挂在既有兑付行上,独立行会双倍计数。#3 标中旗的 chord 缺席(MVP 无即时音画反馈)以"当轮兑付 + 明细强调"组合补偿——weaken dur=1 本就当轮生效,节拍缺失是呈现层债务(归 D1/垂直切片),不是兑付层缺陷。 + +### 综合判断 + +完美排雷落真实双份交付载体(P_perfect ∈ {100, 200},200 = 双份交付、100 = 仅高光无第二份);高光行记实际有效兑付、零兑付降级;事件型用独立高光行、属性型用行字段标记。design 的「规则 13 实现参照」表与「明细行类型」表只收此结论。 + +## 问题:伤害A/B 充能与护甲克制怎么结算?(F6 推导) + +### 广度分析 + +- 候选 A(充能吃 attack_up 等 percent 修正):与 B4 F4"刀不吃修正"同族拍板冲突——加法增量分量一律不吃 percent,排除。 +- 候选 B(flat 附加分量、逐分量查甲效表 floor 后相加):基础/伤害A/伤害B 三分量各自对甲型读数,对策面清晰(甲B 对策 = 伤害B 充能),采纳。 +- 候选 C(跨轮累积充能池):单轮生命周期(轮末清零)是"这轮挖得多 → 这刀重"的即时因果,跨轮存量会稀释归因,排除。 + +### 深度分析 + +- **验算(表读值记号,默认矩阵)**:对甲A 敌(bugbear)10 格挖掘轮——A 池 = min(10×1, 8) = 8 → dmg_a = floor(8×1.5) = 12;2 首标 B 池 = 4 → dmg_b = floor(4×0.5) = 2;base = 6(零 attack_up)→ D_hero = 20。对甲B 敌(ogre_chief):dmg_a = floor(8×0.5) = 4、dmg_b = floor(4×1.5) = 6 → D_hero = 16——甲B 对策 = 伤害B 充能(C3 联动网络 #6)。无甲敌(goblin):8 + 4 → D_hero = 18。 +- **cap 截断腿**:15 格连锁轮 A 池 = min(15, 8) = 8——溢出 7 格零充能、无"溢出未生效"行(充能非奖励、无兑付语义),截断仅入诊断计数;cap=0 = 不封顶结构位,damage_a_per_cell=0 = 机制关闭合法极端。 +- **与乘区关系**:A/B 分量在 percent 池乘区之外(敌→主乘区 F2 不作用于 hero→敌方向;hero_offense 池只乘基础分量);attack_up 只乘基础分量——B4 F4 同族拍板。 +- **P2 归因**:A 池来源 = 开格(逐格可归)、B 池来源 = 首次标中旗(与 weaken 同判定批——"标中旗双收益线"归 W12③ 对账清单);明细行 = hero 伤害行分量二元组 {baseline = 基础分量, attach = dmg_a + dmg_b 合计}(A/B 拆分经充能池公开读值可还原,P2 不丢失)。 + +### 综合判断 + +伤害A/B 为挖掘期充能、战斗段主角先手攻击一次性消费的 flat 附加分量——池内加算、不吃任何 percent 修正、逐分量查 armor_effectiveness 甲效表后 floor 相加;充能池单轮生命周期(累积 → 消费 → 轮末归零,跨轮零存量);Output Range D_hero ∈ [0, ∞) 整数、零负值分支。归属:机制与结算归 B1(F6 + F3⓪),四字段数值投值归 C3,甲型映射归 B2 EnemyTable。design 的 F6 节收结论、公式与 Variables。 + +## 开放问题 + +| 编号 | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | 事件总线载体:signal vs 回调注册 vs 直连调用;B1 触发机制(事件订阅 vs 相位轮询)与 A2 事件捕获断言面同批落地——AC-6/AC-33 相关 THEN 为纸面契约直至落地(缺口预检 1) | 实现期 ADR(与 A2 OQ2 同一裁决) | Technical Setup | +| 2 | m_w 修订终值共裁:F0 已裁值域 [60, 85]/默认 −65 与归一口径;终值确认 + B3 StatusEffectTable 行表/Tuning Knobs/registry 三处回写执行(缺口预检 3);B3 AC-14 腿 3 非平凡性随批复查 | B4/B1 锚 owner 共裁 + C3 调参 | B1 评审后的接口漂移回写批 | +| 3 | A1 actuals p10 交付后条款③⑤强制重跑:ι 以行 target 代 p10 为乐观代理——重跑 AC-39/40 + 同步 registry 重算值(对账清单 #6);连带 fog_burden_ratio p50/p90 | A1 交付 → B1 重跑 | A1 F4 报告 C 交付 | +| 4 | 连锁规模分布供给方悬空:条款②第二腿(连锁放大边际基准)校准需 c̄(每安全格连锁期望放大)——现无既定供给方;候选 = A1 F4 报告 C 扩列 or A2 遥测(对账清单 #8 附注);供给落定前第二腿以 1:1 零链下界运转 | A1/A2 + B1 共裁 | A1 报告 C 扩列或 A2 telemetry 定稿 | +| 5 | EC5 零合法板玩家侧呈现形态:提示/补偿/回退三形态选型——B1 数据面(非法产物标记上抛)已就绪;B1 域内不做补偿逻辑(候选"确定性回退已知好种子"归实现期 ADR——A1 EC5 登记对侧) | D1 裁决 | D1 GDD | +| 6 | C1/C2 encounter 供给接线:begin_battle 入参直供改真实遭遇上下文 + 跨战斗 HP 持久性(B2 hp_persistence 对侧承接);战斗触发(节点移动碰非奖励点)归 C1(缺口预检 5) | C1/C2 | C1/C2 GDD | +| 7 | 深度高光形态:#3 标中旗高光的二期形态(即时 HUD 反馈动画/音画同步)——MVP 已落数据面标记 + 当轮兑付;旗落格高亮/姿态反馈等表现形态随 art-bible 与战斗表现美术测试定 | D1/B5 | 垂直切片 | +| 8 | 标错惩罚 flag_miss 源路径:候选"敌方伏击 +1"上线时——flag_judged{hit=false} 转化路径 + B3 source_class 新枚举(结构冻结项走 GDD 修订)+ 惩罚重复性裁决(hit=false 无去重的不对称)联动关闭(缺口预检 12) | B1/A2/A3 联合 | A2 OQ1 实测采纳后修订批 | +| 9 | hero atk 激活——已闭合(2026-09-11 起"基础攻击恒存在":HeroTable.atk 域 [1,999]、0 加载拒绝、MVP=6;attack_up 复活、消费面 = 主角战斗段先手攻击);对照表回迁 + AC-36 死行断言改写随重算批兑现 | B4/B1 | 已闭合 | +| 10 | B2 OQ1 两 face 归属:query_attack/ambush_grant_plan 归 B2 供给面 vs B1 编排面——裁决归 B1 则 AC-14/22 相关腿迁移至 B1 冒烟(断言口径不变) | 实现期 ADR | Technical Setup | +| 11 | 零伤害死局遥测锚口径:战斗轮数异常遥测的阈值与埋点(EC 死局组合 B1 无自愈分支——护栏 owner = 实现期 ADR/D2 跨字段校验,B2 OQ9 并案)(缺口预检 11) | C3/playtest | 垂直切片 playtest | + +## 缺口预检(验收阻塞与后续义务登记) + +| 编号 | 缺口与处置 | 对侧 | 时机 | +|---|---|---|---| +| 1 | 【阻塞级】A2 事件载体 ADR 依赖:经真 A2 事件流驱动 B1 的 THEN 子集为纸面契约直至落地;A2 半真备选(注入面 + A3 真)已登记为过渡载体、断言口径不变 | 实现期 ADR(与 A2 OQ2 同一裁决) | Technical Setup | +| 2 | 【阻塞级】σ 登记轨迹与取号约定:身份依赖腿(双包/刀全灭/清图槽身份/完美板布置)依赖 σ 登记与取号约定(B4 缺口 8 同源);登记后须第二人独立推导复核 | qa / 实现期 ADR | 冒烟框架搭建 + ADR 定名 | +| 3 | m_w 修订回写——已闭合:registry 回执 #1/#2 于 2026-09-10 F0 批落地、2026-09-14 重算批更新腿读数(attack_up 参照回迁口径重跑兑现);B3 AC-14 腿 3 非平凡性复查通过(16×65 = 1040 越域) | B4/B1 共裁 + C3 | 已闭合 | +| 4 | A1 actuals p10 未交付:F4/F5 以行 target 代 p10 乐观代理——交付后强制重跑 AC-39/40 并同步 registry(对账清单 #6);连带 \|M\|/\|S\| 分布与 fog_burden_ratio p50/p90 | A1(交付)→ B1(重跑) | A1 F4 报告 C 交付 | +| 5 | C1/C2 encounter 供给:begin_battle 入参直供为 provisional——C1 定稿后接入真实遭遇上下文与跨战斗 HP 持久性,AC-5/24 的直供构造改为真实供给链 | C1/C2 | C1/C2 GDD | +| 6 | 【阻塞级】D1 呈现义务台账:A2×6/B2×4/B3×4/B4×5/B1×3(高光行强调/完美排雷呈现/EC5 零合法板提示)缺项即 blocking——design 验收标准节 AC 全部以数据面断言、呈现零覆盖 | D1 | D1 GDD 门禁评审 | +| 7 | SettlementTable registry 条目回填——已闭合:settlement_table 条目 2026-09-10 回填、2026-09-14 重算批随 W3 充能规格更新为十二字段口径;AC-41④ 按现行条目断言 | 本 GDD 定稿批 | 已闭合 | +| 8 | B1 扫描域成员清单登记:AC-43 单向扫描的 B2/B3/B4 API 符号清单与 B1 文件集须登记入 D2 AC-9 项目级 CI 扫描配置(B3/B4 先例) | Technical Setup | B1 GDScript 产物落地即登记 | +| 9 | C3 调参对账重跑流程:D_ref/P_pf/m_w 任一调参 → AC-36/39/40 + AC-2–4 夹具登记值连锁重跑;扩表:A1 行参数(mine_density/min_safe_count/ι target)与 B4 奖励权重/power 调参 → 同批重跑 AC-39/40;A1 新增产出义务 = R₀ 规模分布上界(F4 报告 C 扩列——护栏 B 的登记供给方) | C3(调参)+ qa(重制)+ A1(R₀ 产出) | 每次调参 / 报告 C 扩列交付 | +| 10 | A1 桩 → 真 A1 全链路:换图编排 generate 真实集成(含 attempt 重试不重开 scope 语义、非法产物真分布)登记端到端集成冒烟(与 A1×A2 双包含端到端同槽) | qa/A1 | A1 实现落地 | +| 11 | 零伤害死局遥测锚:EC 死局组合 B1 不设自愈分支——战斗轮数异常遥测的口径登记(进 C3/OQ 清单) | C3/playtest | 垂直切片 playtest | +| 12 | flag_miss 源路径预留:标错惩罚上线时补 `flag_judged{hit=false}` → 敌方伏击 +1 源路径 AC(source_class 新枚举走 B3 修订——A2 预告接口漂移) | B1/A2/A3 联合 | 惩罚实测采纳后修订批 | + +## 附录 A:审计元数据 + +### 复核注记(v1.1-lean 头部遗留) + +lean 门禁下 systems-designer / qa-lead 已委托起草(F0–F5 + 对账义务清单 / 43 条 AC + 覆盖矩阵);creative-director 与 art-director 未经咨询——生产前人工复核 Player Fantasy 情绪定位与 Visual/Audio 和 art-bible 一致性(§1–4 须在 D1 GDD 前创建)。 + +### 对账登记义务清单 + +| 编号 | 登记内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | registry `status_effect.anchor_warning` 裁决回执:归一口径 = 每结算瞬时当量(总暴露分支关闭——schema 域禁止 + 重定参照路径不采纳);m_w 修订值域 [60, 85]/默认 65;"7 层触地板"预读更正(地板 = 乘数零点 −100 pp,交叉层数 = ⌈100/m_w⌉ = 2);参照对象 = attack_up 当量(registry 回迁口径) | registry / B3 | 已回写(2026-09-10 回执;重算复核更新腿读数) | +| 2 | registry `status_effect.mvp_content.weaken` + B3 GDD 行表/Tuning Knobs:magnitude −25 → 值域 [60, 85]/默认 −65(接口漂移回写);B3 AC-14 腿 3 非平凡性复查回执(16×65 = 1040 越域 ✓);B3 OQ1/OQ6 关闭回执 | registry / B3 | 已回写(2026-09-10) | +| 3 | registry `clause3_supply.direction_precheck` 闭合回执:统一当量重算值双口径登记(边际配对),pp 代理退役为历史记录;破线 → C3 调参约束登记 | registry / C3 | 已回写(2026-09-10)+ 2026-09-14 重算回执(现行值链破线读数) | +| 4 | registry 新条目 `clause5_dominance`:F5 主判式(挖穿残余池计价)+ 未闭合登记与 C3/B4 约束包(C3 已裁②非支配设计态)+ 两护栏结论 + actuals 重跑义务 | registry / C3、B4 | 已回写(2026-09-10)+ 2026-09-14 重算回执(v3 曲线读数) | +| 5 | registry `reward_table`/`reward_config` 注记:C_eff 口径(P_perfect 入账规则——÷100 成文 + 真实双份交付、载体域 {100,200};当量 = power 恒等);attack_up 死行注记已随基础攻击恒存在批退役(对照表现行 = attack_up 活行) | registry / B4 | 已回写(2026-09-10 回执;死行注记随基础攻击复活退役) | +| 6 | registry `board_gen_table.inferability_vs_p_safe` 消费回执:P(安全) 代理公式(残余盲猜端上界式)+ actuals p10 输入接线路径(A1 F4 报告 C) | registry / A1 | 待回写 | +| 7 | D2 SettlementTable schema(B1 回填 owner)+ registry `settlement_table` 条目(十二字段 = 原八字段 + damage_a_per_cell / damage_a_cap / damage_b_per_flag / armor_effectiveness——护甲克制批) | D2 / registry | 已回写(2026-09-10 回填;2026-09-14 随 W3 字段更新) | +| 8 | systems-index 门禁记录:B1 强制检查点三项(条款①②③⑤)裁决落定登记;A3 OQ1(对照表)、B4 OQ5(T_fog_min 推导式——对照表已落定,改推导式与否由 B4 裁)/OQ6(weaken 联动)状态回写。附注(新缺口):第二腿校准所需的连锁规模分布(c̄)无既定供给方——登记 A1 报告 C 扩列或 A2 telemetry 的归属 OQ | systems-index / B3、B4 | 待回写 | + +### AC 覆盖矩阵 + +| 类别 | 映射 | +|---|---| +| Core Rules(16/16) | 1→AC-43(单向扫描)+ AC-33(零随机行为级)· 2→AC-6 · 3→AC-9 · 4→AC-7+AC-2 · 5→AC-9/10/11/12(腿 5 = 标旗未中行)/13 · 6→AC-14 · 7→AC-15/16 · 8→AC-17 · 9→AC-18/19/20 · 10→AC-21/22/23/24/25 · 11→AC-26 · 12→AC-24/27 · 13→AC-35(#2 真实双份)+ AC-12(#3+腿 5)+ AC-36(P_pf 入账)· 14→AC-42(13 类+字段分层)· 15→AC-33/34 · 16→AC-1/2/3/4/32/41 | +| Formulas | F0→AC-41(registry 对账形态)· F1→AC-36 · F2→AC-37+AC-22 腿 3 · F3→AC-38+AC-18/20/22/24 · F4→AC-39 · F5→AC-40 · F6→AC-44 | +| 上游义务 23 条 | B3①→AC-9 · ②→AC-9 · ③→AC-37/22 · ④→AC-26 腿 2 · ⑤→AC-36 · ⑥→AC-26 腿 1 · ⑦→AC-17 腿 1 · ⑧→AC-14 ∥ B2①→AC-21 · ②→AC-9/14 · ③→AC-24/38 · ④→AC-39 · ⑤→AC-18 · ⑥→AC-14 ∥ A2①→AC-6 · ②→AC-10 腿 2 · ③→AC-12 腿 2/3 ∥ B4①→AC-7 · ②→AC-9/10/11 · ③→AC-13 · ④→AC-15/16/18/19/20 · ⑤→AC-31 · ⑥→AC-5/28——23/23 全落位 | +| 强制检查点 5 项 | reload gate→AC-32 · 可回放→AC-33 · new_board 违约→AC-30 · roll_board 恰一次→AC-5/28(scope 计数器观测)· 衰减恰一次→AC-26 | +| SettlementTable 校验 | segment_order→AC-2 · 悬空 effect_id→AC-3 · p_perfect 域→AC-4 · schema 定稿→AC-1/41 | +| EC(26 条) | 末图清图→AC-13/26 腿 2/27/31 · hero 短路→AC-24 · 双死 hero 先判→AC-24 注(结构不可达不构造)· D'=0→AC-22 腿 3 · 溢杀→AC-18 腿 3 · charge 蒸发→AC-23 · 全体 charge→AC-23 腿 2 · L=0→AC-22 腿 1 · 同窗多包→AC-16 · 部分回复→AC-16/19 腿 2 · 刀全灭→AC-20 · 空库存→AC-15 腿 4 · RAP 不足→AC-29 腿 3 · 连环刷图→AC-29 腿 2 · 未刷新→AC-30 · 非法产物→AC-28 腿 2 · 清理被拒→AC-31 腿 2 · 战斗中 reload→AC-32 · 衰减空→AC-26 腿 3 · 清图 buff 满层→AC-13 腿 2(高光降级)· 完美刷新图→AC-35 腿 4 · 标中+误标→AC-35 腿 2+AC-12 腿 5 · 多敌共享 L→AC-25 · 死亡即不动手→AC-18 腿 2 · 零伤害死局→负空间+遥测锚 OOS(C3/playtest)· 结算开始 hp=0→负空间 debug 断言——呈现半边全部 OOS 至 D1/B5(生效条件 = D1 义务台账登记) | +| 无洞声明 | Core Rules 16/16、F1–F6、义务 23/23、检查点 5/5、SettlementTable 校验 3/3 全部映射 AC;EC 除 2 条负空间自守(不可由公开 API 构造)与呈现/playtest OOS 外全部 Given-When-Then 或对账断言承载 | + +## 去向映射 + +| 原文章节/表 | 落点 | +|---|---| +| 头部(状态/版本/承载支柱/一句话) | design 头部;复核注记 → 附录 A | +| 速览表 | design 系统范围(定位/拥有/不拥有/上下游/硬约束拆入负责·不负责清单与规则 1/15) | +| 体验锚点表 + 反向护栏 | design 设计目的(四条承诺);论证 → 问题"结算系统如何兑现三根支柱的体验承诺" | +| 全局流程(战斗开始/七段骨架/出口分发表) | design 核心循环 | +| 核心规则 1/2/3/4/16 | design 编排与触发(编号人话句);静态扫描/行为级举证 → design 验收标准 AC | +| 核心规则 5/6/7/13 + 三批转化表 + 奖励分流表 + 高光兑现节点表 | design 授予与高光;真实双份论证 → 问题"完美排雷的翻倍是账面乘数还是真实双份交付" | +| 核心规则 8/9/10/11/12 | design 合成、道具与战斗 | +| 核心规则 14/15 | design 明细流水协议 | +| 枚举(明细行类型/source_class/highlight) | design 明细流水协议 | +| 术语表 | design 当量换算与条款闭合(读公式所需的定义) | +| F0 结论 | design F0 节;分支表/同分母性/机制同构/schema 禁止/值域依据/红警更正/重算复核/连带后果 → 问题"weaken 锚的归一口径" | +| F1 结论/换算式/Variables/对照表/锚两腿/结构性边界/归口 | design F1 节;候选取舍/锚对照实物论证/Example → 问题"四源效果档位如何放在同一把尺上对照" | +| F2 结论/公式/Variables/三例读法表/归口 | design F2 节;候选取舍/免伤合法性论证/Example → 问题"percent 池怎么合成、地板设在哪、免伤轮合不合法" | +| F3 结论/四分支公式/Variables/归口 | design F3 节;候选取舍/Example/双死不可达论证 → 问题"伤害全序、负值、溢杀与死亡判定边界怎么定" | +| F4 结论/判据公式/Variables/验算表/结论①–④ | design F4 节;换锚注记/双口径/V_safe·V_mine·ι 注记/clause3_supply 对齐 → 问题"条款③挖掘风险不等式在现行值链下成立吗" | +| F5 结论/判据公式/Variables/四态验算表/折减·雾态 bullets/约束包/护栏表/护栏 B 输入登记/P_perfect 结论 | design F5 节;计价候选/显式假设论证/结构性解读/护栏 A·B 注记/P_perfect 建议六条 → 问题"条款⑤推到底清图支配弃图刷新吗" | +| F6 结论/公式/Variables/cap 截断腿/P2 归因 | design F6 节;候选取舍/验算/乘区关系 → 问题"伤害A/B 充能与护甲克制怎么结算" | +| 边界参数总表 | design 调参表(边界参数总表) | +| 显式排除声明(归属表) | design 系统范围(不负责清单括注归属)+ 各 F 节归口行 | +| 对账登记义务清单(8 条) | 活契约摘要 → design 依赖与输出(registry 回写义务随各上游行);全表 → 附录 A | +| 状态表 + 编排不变式 | design 编排与触发 | +| 边界情况表(26 条 EC) | design 边界情况(条件|行为两列,依据内联句尾) | +| 依赖(上游 9 行/下游 5 行/双向核对) | design 依赖与输出 | +| 呈现与 UI 契约(8 条数据面义务) | design 依赖与输出(呈现数据面义务) | +| Tuning Knobs(SettlementTable schema/Knob 表/非 knob/Knob 交互/调参所有权) | design 调参表 | +| 验收标准·测试口径 | design 验收标准(测试口径) | +| 验收标准·AC 清单(AC-1–AC-44) | design 验收标准(AC 清单,编号不变,覆盖列同步改"规则 N"称谓) | +| 验收标准·覆盖矩阵 | 附录 A(审计证据;design AC 表覆盖列已含规则级映射) | +| 验收标准·缺口预检(12 条) | analysis 缺口预检区(编号不变) | +| 开放悬案(OQ 1–11) | analysis 开放问题区(编号不变) | +| 版本历史(7 行) | design 版本历史(全保留 + 追加 v2 迁移行) | diff --git a/design/gdd/analysis/b4-reward-item-economy.md b/design/gdd/analysis/b4-reward-item-economy.md new file mode 100644 index 00000000..3eb6c42c --- /dev/null +++ b/design/gdd/analysis/b4-reward-item-economy.md @@ -0,0 +1,298 @@ +# B4 奖励与道具经济 设计推演 + +> 状态:concluded · 本文只装"为什么"——`design/gdd/b4-reward-item-economy.md` 只收本文的结论。 +> 修订时新增问题节,不删旧节(历史即依据)。 + +## 问题:奖励身份为什么 install 期预分配、Ready 后只读? + +### 广度分析 + +- 候选 A:授予期现 roll——收益:实现直观、无板级映射容器;代价:身份在开格与授予之间不恒定,预读(P1 规划素材)与回放确定性垮掉,随机入口散布到结算期。 +- 候选 B:install 期整板预分配(选定)——收益:身份恒定可读、A5 探测道具零成本桥接、零随机出 roll_board 一线、同 σ 同流消费轨迹 ⇒ 同映射的确定函数(AC-9);代价:需 F5"草稿-校验-提交"机制承载保底,多一张板级映射。 +- 候选 C:开格时才定身份——代价:探测桥接无结构前提,创意风险 #1(安全格奖励可预期性 = P2 前提)直接失败。 + +### 深度分析 + +RNG 单点纪律要求封闭命名流;registry `rng_service.scope_event_binding` 将 reward_roll 绑定为"每次 board install(格奖励批 + 清图奖励批)",与 board_gen"每次生成新图"同构——换图 = 新 scope 批 = 新身份(E6)。Ready 后只读 + 无重掷 API 构成负空间验收:没有 API 入口即"Ready 后零写入"的结构前提,实例不变式不可由封闭 API 构造违规路径。 + +F5 的草稿重 roll 全部发生在提交前(Unrolled 态),确定性 = 同 scope 消费轨迹的确定函数,与 Ready 只读不变式零冲突;每稿 = 格批 + 清图批尾一号,草稿弃用连清图一号一并弃用,scope 计数器每 install 恒 +1(重 roll 不开新 scope)。 + +### 综合判断 + +install 期预分配 + Ready 只读 + 零随机出 roll_board 一线(design 规则 2/8/10,F1)。创意风险 #1 维持 OPEN:结构前提落地 ≠ 风险闭合;面向特定道具的寻路("绕开两颗雷去拿那把刀")须以未开格身份可读为前提,MVP 未兑现——结构前提已备(身份已定、预读查询面就绪、A5 桥接零成本),呈现深度 = OQ2(垂直切片)。 + +## 问题:奖励分几类、按什么轴交付? + +### 广度分析 + +- 全即时:零决策、零窗口摩擦;代价:丢掉库存规划面——P2"囤刀待机"的长线形态无存身之处。 +- 全可选:规划面最大;代价:每轮都弹窗、结算明细淹没、拾取感过度延迟,拾取的糖被结算的实吞掉。 +- 两级交付轴(选定):即时类高频保底"开格即有反馈",可选类数量少但更强保住规划面;起步配比即时 75% / 可选 25%。 + +### 深度分析 + +可选类入库存带来的窗口摩擦由三个结构缓解:库存只收可选类(权重占比低)+ 窗口单件制(每次决策量最小)+ buff 时长改当次结算(D=1——浪费不再跨结算累积、保质期一次结算天然有界;同结算内批量授予的零兑付仍存在,故预警④仪表保留)。治疗两级稀缺化:instant_heal 小额常见零决策,heal_pack 稀缺强效——治疗整体供给变少、决策价值变高,"治疗是稀缺奖励";满血双态区分(instant_heal 满血零兑付记"奖励未生效"行 vs heal_pack 满血拒用不烧包)分别承载"白捡无感"与"误触防护"。 + +C3 修订批以供给控制器(RewardSupplyController——install 期战斗首件保证 + 指数递减注入 0.4→0.2→0.1→…,E[战斗获取] ≈ 1 标定目标)替代"格子权重 75/25"承载可选类供给:首件保证取代 K_dmg 保底的胜利路径语义,指数递减保稀缺、永不绝育防"可选类绝育"。clear_blast 换位(原 clear_knife 入库刀 → instant_aoe p40 当场兑付全体存活敌、不入库不占可选类供给、完美板 ×2 = 80)同理——清图槽的兑付不走库存通道,B1 条款⑤计价基元随换位重推(C = power = 40)。 + +### 综合判断 + +五值封闭枚举 + 两类交付轴 + active_inject 注入池(design「奖励类型与数据表」)。预留值(resource / aux_item)校验拒绝 fail-fast,启用走修订——机制与内容分离,名录扩展归垂直切片/Alpha。 + +## 问题:道具伤害吃不吃 percent 修正、attack_up 死行怎么办? + +### 广度分析 + +- 道具吃修正:与 attack_up 叠乘爆发高;代价:乘法爆炸面 + 囤刀无上限不再安全 + 玩家不可心算。 +- 恒定表值不吃修正(选定,用户拍板):可算性 + 囤刀无上限安全(无乘法爆炸,E17 的结构前提)。 + +### 深度分析 + +attack_up 曾为死行(HeroTable.atk 曾可为 0),A3 条款 2 锚参照对象一度迁 defense_up 当量。用户裁决"基础攻击恒存在"(atk 域 [1,999]、0 加载拒绝、MVP = 6 原型锚;主角每结算战斗段先手攻击顺位最前敌)后 attack_up 复活:唯一消费面 = 主角战斗段先手攻击,只乘基础攻击、不乘刀——"道具不吃修正"拍板不变。锚参照对象回迁 attack_up 当量(registry `status_effect.anchor_warning` / `clause3_supply.direction_precheck` / systems-index A3 OQ5 注记三处同批回写;B3 锚预警裁决前置不变)。 + +### 综合判断 + +恒定表值 + attack_up 复活口径见 design F4。残余 = attack_up 是否入默认根池(OQ1——复活批不擅自入池,随奖励改造批裁决,C3 内容所有权);入池时 tier_min_buff 重算、AC-19 对账对随批重制。 + +## 问题:道具段已选刀但无存活敌时,拒用还是照常消耗?(E10) + +### 广度分析 + +- 拒绝不消耗:玩家友好、无浪费感;代价:须读 B2 存活集才能判定,B4 零跨系统读纪律开口子。 +- 照常消耗零效果(选定):B4 保持零游戏态守卫,战斗轨迹判断留编排层;代价:玩家可能"烧刀",须明细行诚实呈现。 + +### 深度分析 + +选定理由两条:① 守卫所有权不对称(非信息不对称)——满血守卫是 B1 读 hero hp 的表局部判定(F4 已定),而零效果判定须读 B2 存活集,B4 零跨系统读纪律不为它开口子,选定即承诺把战斗轨迹判断留给编排层;② 反抖动——选定即承诺防窗口内反复重选拖垮结算节奏(B2 呈现规格供给 hp/max_hp 血条数字,敌我血量在选择时点均可读;窗口内可用性预判 usable_now〔B1 组装〕承载"误触防护"半句)。原"敌 HP 不可见"理由与 B2 血条数字可读矛盾,已更正为上述两条。 + +段序不截断:选定件全序执行 → 战斗段照常推进(死敌"死亡即不动手"空转)→ 衰减段 → 判定段判胜 → 关卡末 clear_inventory 全清(含未选件)。"零效果"是事实判定(B1 读存活集做判定)非拒绝——逐件进"已消耗未生效"明细行(B3 义务⑧"溢出未生效"行协议同款数据面)。 + +### 综合判断 + +照常消耗 + 逐件"已消耗未生效"明细行(design E10;AC-14 分支 3 承载拍板语义——不采信任何"拒绝不消耗"旧读法);监控锚 = 预警①浪费半。 + +## 问题:每板刀供给如何保底?(F5 与供给控制器) + +### 广度分析 + +- 候选 A:K_dmg 草稿-校验-提交重 roll(现行机制保留)——结构保证但引入概率性致命带。 +- 候选 B:清图槽保底(否决)——弃图流覆盖不到:RAP 刷图非必经清图;economy-designer 复核:清图免费换板且多清图槽,吞吐严格支配 RAP 刷新,无刷刀农场漏洞。 +- 候选 C:safe_cell 伤害权重下界(否决)——概率缓解非结构保证,留作 C3 伴生调参。 +- 候选 D:供给控制器战斗首件保证(选定承接)——机制保证每战至少一件可选类,K_dmg 降为 0。 + +### 深度分析 + +概率性致命带:合法权重下 N_draft 耗尽概率随 K/池构成可达非平凡值(同配置不同种子一个能玩一个崩——种子轮盘);处置 = C3 调参期批量仿真义务(刀供给分布 × A1 板分布的可行率),把配置错误暴露在调参台而非玩家局内。b4_reroll 夹具的可达性标定:首稿零刀 P ≈ (6/7)³ ≈ 0.63(重 roll 可达)而耗尽经验稀有 ≈ 4×10⁻⁷/σ。 + +保底仅计 damage_item 不计 instant_aoe——保底的意义在"每板至少有一把可囤可择时的刀"(玩家可控时点的伤害资源),即时类自动兑付无规划面。K_dmg=0 后 no_damage_row 后闸托底零活刀行世界:内容健康护栏、非胜利路径闸——基础攻击保证战斗可胜,本闸防"零伤害道具世界"经纯内容调参静默复活导致战斗拖长、奖励系统空转(与 B2 OQ9 并案登记同一 owner)。 + +### 综合判断 + +K_dmg=0 现行默认 + 供给控制器首件保证 + 双加载护栏 floor_supply_missing / no_damage_row(design F5)。"C3 禁区默认值"条款随 C3 修订批解除。 + +## 问题:清图与雾区奖励如何定价?(A4 三约束的 MVP 承载) + +### 广度分析 + +- ① 雾内档位下界:T_fog_min 表值 vs 推导式(= 最低档 buff 当量所在档)。 +- ② 清图锚随雾成本上浮:MVP 常量池 vs clear_ 差异化。 +- ③ 雾板净当量优势带上界:运行时校验 vs 验收锚 + 预警对冲。 + +### 深度分析 + +① T_fog_min 真推导式依赖 B1 条款①对照表(未设计),加载期循环依赖不可行——取表值(默认 2 provisional),A4① 登记为验收锚非运行时校验,对照表落定/调参时核对"雾池最低档行当量 ≥ 最低档安全格 buff 当量"。AC-19 档位序检查的已知失效面:tier 与当量零耦合(跨类别 tier 序不保证当量序——雾池 tier2 治疗包的当量可能高于 tier3 buff 行),故 AC-19 只作档位序检查非当量保证,升格运行时致命会锁死内容;单行雾池下检查恒平凡真(判别力零)——默认根已布置四行雾池规避。 + +② clear_default MVP 单默认槽 = 板无关常量池,清图奖励当量无法随雾负担变化——MVP 结构性不可闭合(非 deferral:B1 强制检查点只能对该常量判 pass/fail,改 power 会同步抬高无雾板、破坏无雾侧定价);激活载体 = clear_ 插槽(垂直切片,待 C1 biome 词表落定)。 + +③ ②缺位 + 雾格仅 tier 下界加成的 MVP 结构下近乎必然平凡通过——静默绿灯风险显式登记;休眠状态与接受风险(重雾板清图欠偿 → 弃图偏斜——A4 警告的 level 2 结构性高发位);对冲 = 预警⑤雾板分层观测(聚合指标会被雾板污染或反向掩盖)+ 预警⑥速杀退化监测。 + +### 综合判断 + +①锚侧承载 / ②休眠待 clear_ / ③显式接受风险 + 双预警对冲(design「A4 定价硬约束的 MVP 承载状态」表 + AC-19)。 + +## 问题:默认根数值如何锚定?(方向预检与口径) + +### 广度分析 + +- 供给面:刀供给期望 ≈ |资格格| × 19% × 9.7(加权刀力 = (15×8+4×16)/19 ≈ 9.7)≈ 1.8|资格格|/板,另加即时群伤与基础攻击分量。 +- 需求面:击杀需求 = 敌 hp 18(单敌档)/ 54(3 敌档,B2 EnemyTable 参考行)。 +- 对照基准:max_hp = 50(原型锚)为治疗供给对照基准。 + +### 深度分析 + +现值读数:"单敌无忧 / 2–3 敌贴近下界"有锚。预警联动:速杀退化预警⑥在"基础攻击 6/结算 + 即时类自动兑付"下压力进一步上升——敌 hp 曲线与奖励供给禁止独立调参(C3 联动重算义务)。输入缺口:|资格格| 分布待 A1 actuals p10/p50 → 落地后重跑;max_hp=50 原型锚出处建议随 B2/D2 回写补登。 + +buff 零兑付率推导(预警④连锁批单列的依据):buff 供给 λ = 8 挖/轮 × 44% ≈ 3.5 层/轮 vs 吸收上限 1 层/结算 × max_stack 2——批式授予下零兑付率远高于单格批,故连锁批量授予零兑付率单列(阈值 >50%)。F6 供给面口径 = 全活行投影(非仅活 buff 行清单)——承载 A4②③与 A3⑤ 对账,道具/清图行值在内(B1 直读 ConfigDB 本也可绕过,接口名实相符);量纲建议 = 主角 HP 点(与 m_amb 同量纲)。B2 OQ3 衔接:伤害道具作为主角规划面伤害令"安全格拾取 → 伤害当量"可落地(基础攻击为恒定基底、道具为可规划增量——部分缓解非闭合,窗口使用次数是战斗轨迹量归 B1)。 + +### 综合判断 + +方向预检登记对账记录(B2 clause3_supply 先例);数值全部 provisional 归 C3;锚验收权 B4/B1(B3 Rule 10 同款)。 + +## 问题:体验锚点如何论证?(拾取的糖、结算的实) + +### 广度分析 + +- 参考感受:Microsoft Minesweeper Adventure 的开格拾取(身份即在场的战利品,不是无名金币)+ 杀戮尖塔奖励节点(有名字、有用途、进构筑的战利品)。 +- 反向对照:无身份的纯数值掉落("你获得了 +5"式系统补偿)与开包式随机惊喜(拿到了也不知道是啥)——本作要避开的两种感受。 + +### 深度分析 + +玩家的每一轮都在预读本系统:已开格与库存的奖励身份恒定可读,叠加"每个安全格必有货"(F1 结构保证)与"雾格档位更高"(F2)两条全板先验——构成"往雾区推进、囤刀待机、叠层满载后收手"的路线与节奏决策素材。深度轴 MVP 零载体,深度/图类型差异化 = clear_/池变体结构位(垂直切片登记)。 + +E17 囤刀:合法策略且为设计意图(可选类道具是关内资产——"囤刀待机"的规划视野 = 整个关卡,P2 长线规划形态兑现);无运行时上限因伤害恒定表值 ⇒ 无乘法爆炸;失衡监控 = 预警①(浪费半)与⑥(速杀退化),修正入口 = C3 权重调参。E3 连锁批量溢出:13 格链 × max_stack=2 ⇒ 大量同文"奖励未生效"明细行——显式接受(A2 登记义务"连锁批量授予溢出吸收"的 B4 侧裁决落点);玩家侧缓解 = 叠层感知挖掘(HUD 满层后少开安全格/优先挖刀——弱自救;层数长在 B3 容器上不长在格子上,未开格身份不呈现、无格可绕);明细行数据面须携带可聚合计数/批次字段(是否合并呈现归 B1/B5 裁决,数据必须可聚合——防事后补字段 = 接口漂移)。 + +### 综合判断 + +拾取瞬间的糖与延迟兑现的实各管一半,后者是 P2 归因的奖励侧落点;反向护栏两条(身份永远在桌上 / 拾取不得改即时生效)入 design 设计目的。窗口触发政策:MVP 有库存恒弹,候选"仅可用品触发 / 常驻库存条非模态"归垂直切片(P4 节奏)。 + +## 开放问题 + +| 编号 | 问题 | owner | +|---|---|---| +| OQ1 | attack_up 入池裁决(复活已闭合,残余入池问题):基础攻击恒存在(B2 Rule 9)→ attack_up 复活、消费面 = 主角战斗段先手攻击;attack_up 行是否进入默认根 RewardTable 池随奖励改造批裁决(复活批不擅自入池);A3 条款 2 锚参照对象已回迁 attack_up 当量(F4 口径后果) | C3 | +| OQ2 | 未开格身份呈现深度:MVP 不呈现(规则 8);A5 探测道具零成本桥接(身份 install 期已定可查);呈现深度(类别/档位/身份)待 A5 定稿 | A5+D1(垂直切片) | +| OQ3 | items_per_settlement_cap 启用裁决:Tuning 结构位默认关;启用即限制(过低锁死 3 单位 54 击杀需求胜局——economy-designer 警告) | C3 playtest | +| OQ4 | 关卡末剩余道具折算(关内延续裁决后重述):"战斗结束库存清空"的体验负债主体闭合(库存关卡域:跨战斗延续、未选用件不再随战斗消亡;层间延续可见反馈已入切片);残余 = 关卡末剩余道具折算/继承结构位(转金币/继承——Alpha,C2;清空前快照计数已作为折算数据面预留) | C2/D5(Alpha) | +| OQ5 | T_fog_min 推导式:依赖条款①对照表(未设计,加载期循环依赖不可行);落定后改推导式或维持表值(F2,默认 2 provisional) | B1 | +| OQ6 | weaken 锚裁决联动:B3 OQ1 归一口径裁决前置(瞬时 vs 总暴露);若落"总暴露"读法则 B4 重定最低档参照(registry anchor_warning;F6 供给已备行值清单与量纲建议) | B1 | +| OQ7 | 失衡预警预注册归口:预警①–⑧ 阈值与样本量 provisional;与 A3 OQ3、原型 v3 终验 playtest 合并执行候选 | C3/playtest 会话 | + +### 待拍板(两件套迁移发现的原文内部 / 与 registry 冲突——迁移未裁决,design 按所注口径收录) + +| 编号 | 冲突 | 迁移处置 | +|---|---|---| +| NQ-1 | registry `reward_table` 已含 `power_min/power_max` 可选配对字段与"伤害量值区间批"五行区间化登记(heal_minor [2,6] / knife_t1 [5,11] / heal_pack [6,18] / knife_adv [10,22] / clear_blast [25,55],中点恒等原值),原文 schema 九字段与默认根行值未承接 | design 保留原文九字段与定值行;待拍板:GDD 补区间字段还是 registry 回退 | +| NQ-2 | AC-18 断言"category 值域 ⊆ {buff, damage_item, heal_pack}(三值封闭定值)"与规则 1 五值封闭枚举冲突(两级奖励批后未同步) | AC 原文保留未改(编号不动、不断然改 AC);待拍板改写为五值 | +| NQ-3 | 规则 7 举证"heal_pack 默认根权重 6%、power 12"与默认根行集 heal_pack w10(active_inject)冲突(C3 修订批迁注入池后未同步);失衡预警②"可选治疗稀缺化至 6% 后"同源 | design 按默认根行集与 registry 收 w10;预警②行原文保留;待拍板 | +| NQ-4 | F5 推导代码块注"K_dmg 默认 1 provisional"与 F5 结论/Output Range"K_dmg=0 现行默认"及 registry(默认 0)冲突 | design 按 0 收录(registry 已锁 + 原文结论口径一致);原"默认 1"视为残留;待拍板确认 | +| NQ-5 | 夹具"默认根"行原注 K_dmg=1 与行集列 clear_knife,同 NQ-4 / clear_blast 换位残留 | design 夹具行改写为 K_dmg=0 + 指向「默认根内容行」节八行;待拍板确认 | +| NQ-6 | N_draft 域原文 [0, 999] vs registry [1, 999] | design 按 registry [1,999] 收录;待拍板 | +| NQ-7 | registry `reward_table.direction_precheck` 仍为旧权重口径(\|资格格\| × 40% × 5 = 2\|资格格\|),原文方向预检已按新权重重算(19% × 9.7 ≈ 1.8\|资格格\|/板) | analysis 按新口径收录;registry 未随 C3 修订批重跑,待拍板回写 | + +## 附录 A:接口漂移登记(回写已兑现——delta 复核逐处对账) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | game-concept 待定问题"主动道具后续版本加入"于 MVP 激活(道具使用窗口)——use_timing 字段从预留转承重 | game-concept(第 144 行注记) | 已同步 | +| 2 | registry `rng_service.scope_event_binding` 的 reward_roll 批次语义改写为"每次 board install(格奖励批 + 清图奖励批)" | registry + D2 正文(Core Rule 5 / Interactions B4 行) | 已同步 | +| 3 | 锚参照迁移传播(attack_up 当量参照回迁) | systems-index A3 OQ5 注记 · registry `status_effect.anchor_warning` · registry `clause3_supply.direction_precheck` | 已同步(三处) | +| 4 | game-concept 创意风险 #1 与可证伪信号的 MVP 口径注记 | game-concept | 已同步 | +| 5 | 窗口锚点修订(授予段后) | systems-index 门禁块 | 已同步 | +| 6 | systems-index 分层行 B4 ← D2 更新 | systems-index | 已同步 | + +## 附录 B:缺口预检(11 项——每项标 owner 与时机) + +| 编号 | 缺口与处置 | owner | 时机 | +|---|---|---|---| +| 1 | 【阻塞级】B1 编排半整体 deferral:段序骨架(授予>窗口>增益/减益>道具>战斗>衰减>判定——窗口锚点 = 授予段后)、窗口选择序回放输入、满血守卫(拒绝不烧包)+ 同窗顺序多包逐件动态守卫、零效果判定与"已消耗未生效"行、击杀结果进流水、"奖励未生效"行 + 聚合计数、治疗 clamp、clear_inventory/retire/roll_board 恰一次编排——B4 冒烟全部以构造序扮演、编排正确性零断言;E10 的 B1 侧承接(读 B2 存活集做零效果判定)尤须逐字核对 | B1 GDD(义务清单 6 项,缺项即 blocking) | B1 门禁评审 | +| 2 | 【阻塞级】A4① 当量升级与②③状态:AC-19 分支 2 为档位序检查——真当量换算依赖 B1 条款①对照表(未设计);②在 MVP 结构性不可闭合(依赖节显式宣告——非常量池无法随雾负担上浮)、③静默绿灯风险;激活载体 = clear_ 插槽(垂直切片) | B1 | B1 GDD 强制检查点 + 对照表落定/每次调参时核对 + clear_ 激活时重启② | +| 3 | T_fog_min 推导式悬置(OQ5):表值默认 2 provisional——B1 对照表落定后改推导式或维持表值;若改动,AC-6 夹具(b4_fog_gate tier 布置与 override 分支)及登记轨迹重制 | B1 | OQ5 关闭 | +| 4 | registry/D2/索引/game-concept 回写(评审批已兑现):reward_roll scope 语义改写(registry + D2 正文两处补同步——AC-20① 生效前提)、锚参照迁移三处传播、game-concept 三处 MVP 口径注记、systems-index 分层行 B4←D2 + 门禁块同步——delta 复核时逐处对账 | 本 GDD 评审批 | 已办,delta 复核确认 | +| 5 | attack_up 复活连锁(OQ1)——已兑现:基础攻击恒存在(HeroTable.atk 域 [1,999]、0 加载拒绝)→ attack_up 复活,消费者 = 主角战斗段先手攻击(B1 GDD 同批修订);AC-18 改承载 attack_up 行合法性、AC-19 锚参照回迁 attack_up 当量、registry anchor_warning/direction_precheck 同批回写;残余 = attack_up 是否入默认根池(随奖励改造批,C3) | C3(残余入池裁决) | 已兑现;残余随奖励改造批 | +| 6 | 登记常数随调参重制流程:b4_golden 冻结权重与默认根解耦,但 T_fog_min/K_dmg/N_draft/P_min/默认根行值调参 → AC-19 对账(对账对 (knife_adv, attack_up_basic) 漂移检测)、AC-6 分支 4、AC-18 结构断言须重跑;C3 调参流程若无"登记常数与对账重制"步骤则相关 AC 假失败 | C3(调参)+ qa(重制) | 每次调参 | +| 7 | B4 扫描域成员清单登记:AC-21 分支 1 的生效载体 = D2 AC-9 项目级 CI 扫描域成员清单更新(B3 先例) | Technical Setup | B4 GDScript 产物落地即登记 | +| 8 | 参考算术工具锚定:期望映射 Ψ(σ)/Φ(σ)/重 roll 轨迹的手工推导需"播种生成器取号序列"参考算术——D2 仅登记 scope 种子向量(未登记逐号序列黄金向量);参考算术以冒烟脚本内置、版本库登记,正确性以 D2 AC-3 种子向量 + AC-5 区间算术手工核验锚定;生成器取号约定(如 `randi % W` vs `randi_range`)为实现期 ADR 事实,定名时同步登记;登记后第二人独立推导复核 + b4_reroll 可达性拆分复核(首稿零刀可达/耗尽稀有——失效即重制夹具) | qa/Technical Setup | 冒烟框架搭建 + 实现期 ADR | +| 9 | D1 呈现义务 deferral:预读呈现(含库存常读条)/使用窗口(含 usable_now 呈现与触发政策)/四类明细行呈现/H2 强调/清空反馈不可 G-W-T——deferral 生效条件 = D1 义务①–⑤登记(缺项即 blocking) | D1 | D1 GDD | +| 10 | C3 调参期批量仿真:F5 概率性致命带(合法权重下 N_draft 耗尽概率随 K/池构成可达非平凡值——种子轮盘)→ 调参流程须含批量仿真(刀供给分布 × A1 板分布的可行率),把配置错误暴露在调参台;与预警①–⑧预注册合并执行候选 | C3 | 每次调参 + playtest | +| 11 | 方向预检输入缺口:刀供给方向预检的 \|资格格\| 分布待 A1 actuals p10/p50 → 落地后重跑(方向预检表登记对账记录);max_hp=50 原型锚出处建议随 B2/D2 回写补登 | C3/B2 | A1 actuals 交付 | + +## 附录 C:AC 覆盖矩阵 + +| 类别 | 映射 | +|---|---| +| Core Rules | 1→AC-16/AC-18 · 2→AC-1/AC-9/AC-10(恰一次编排→OOS B1×⑥)· 3→AC-12(施加与满层零兑付协议→OOS B1×②+B3 跨引)· 4→AC-13 · 5→AC-14(段序/窗口时机/满血守卫/零效果判定→OOS B1×①④;窗口呈现→D1×②)· 6→AC-10/AC-11/AC-16(clear_ 拒绝)/AC-17 · 7→AC-12 分支 1(道具类路由)/AC-14 分支 3(满血守卫→OOS B1×④;clamp→E12 OOS)· 8→AC-2/AC-15(未开格呈现→OQ2/A5+D1)· 9→AC-16/AC-17/AC-18(接口漂移回写→AC-20①⑤)· 10→AC-9/AC-21(调用权集中→OOS B1×①–⑥) | +| Formulas | F1→AC-1/AC-5(+AC-9 轨迹)· F2→AC-6(+AC-19 锚侧)· F3→AC-10/AC-11/AC-17 · F4→AC-14(恒定表值供给面)+AC-18(attack_up 行合法性 = 复活口径)——施加/clamp/守卫/不吃修正纪律→OOS B1×④ · F5→AC-7/AC-8/AC-17(加载护栏 ×2)· F6→AC-19(闭合归 B1——OOS) | +| Edge Cases | E1→AC-4 · E2→AC-3 分支 2+OOS B1×⑤(零明细行半)· E3→AC-12(受理序半)+OOS B1×②(行协议 + 聚合计数半)+B3 AC 跨引(apply false 原子)· E4→AC-3 分支 1/2 · E5→AC-1 分支 3 · E6→AC-9 分支 2(roll 半)+AC-13 分支 2(库存半)· E7→AC-3 分支 3/4 · E8→API 负空间(无强制使用入口——不可 G-W-T,B3 Rule 8 先例)+OOS D1×② · E9→AC-14 分支 2 · E10〔照常消耗〕→AC-14 分支 3(B4 半)+OOS B1×④(零效果判定+"已消耗未生效"行+击杀结果流水)· E11〔清图 buff〕→AC-11(B4 半)+OOS B1×② · E12→OOS B1×④(clamp)+AC-14 分支 1(power 供给)· E13→AC-17(b4_dangling_effect 条件性夹具)· E14→AC-15 · E15→OOS B1×①(授予先于判定的段序半)+AC-10 分支 2/AC-13 分支 3(可构造序弱承载注记)+OOS D1×③⑤(消亡语境注记)· E16→AC-18 分支 2(attack_up 行合法——复活口径)· E17→AC-13 分支 1/2 · E18→AC-12 分支 3/AC-10 分支 3 · E19→API 面(清单外 null/false + 诊断)· E20→API 面(stock 受理域封闭)· E21→API 面(枚举序 = stock 受理序——D1 呈现序契约) | +| 上游承接 | A4①→AC-19 分支 2(夹具对账型,升级缺口预检 2)· A4②③→MVP 休眠显式宣告(依赖节)+ OOS B1 强制检查点 · A3 条款④恒非空→AC-10 分支 1(兑付面)+AC-17 分支 4(表侧)· B3 dangling_ref 首引激活→AC-17 分支 6+AC-20④ · 奖励身份预读→AC-2/AC-15(呈现→OOS D1×①)· attack_up 行合法性→AC-18 分支 2(复活口径)· A3 条款⑤支配性→OOS B1 强制检查点(预警⑤⑧观测) | +| 覆盖口径 | 21 条 EC 中 E8/E15 两处非 G-W-T 形态,分别以 API 负空间与 B1×① 段序 deferral 承载;其余全部映射 AC 或显式 OOS(deferral 生效条件 = B1×6/D1×5 义务清单登记);"负空间"学说的边界如实登记:封闭枚举防住"没有的入口",不自动防住"有入口、语义未裁"——E18–E21(重复受理族/清单外坐标/stock 域/枚举序)为评审批补裁的输入域语义;今后新增 API 入口须随附全输入域语义表(实现期 ADR 检查项) | + +## 附录 E:验收执行规格(测试口径 / 夹具与种子 / 致命 manifest——原文验收标准节执行 machinery,AC 表引用的夹具根名与 manifest 行名以此为准) + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | B4 是 Core 层数据/逻辑系统——非 UI、无场景依附;冒烟载体 = `godot --headless --script`(脚本 extends SceneTree 以获得 quit(code),ConfigDB/RngService autoload 由脚本显式实例化);映射容器/库存容器/RewardTable schema 经 class_name 直引实例化(每用例全新实例、无跨用例状态;AC-21 扫描对该路径豁免) | +| 被测面 | B1 未设计——冒烟直接驱动 B4 最小 API 面并扮演编排方(B2/B3 先例);B4 冒烟零 B2/B3 实例化(施加面/存活集/血量全部归 B1 OOS,StatusEffectTable/HeroTable 仅经 ConfigDB 读值);资格格清单程序构造(真实来源 = B1 义务⑥——冒烟构造等价,零 A1/A2 接线) | +| 时序口径 | "一次 board install" = roll_board 恰一次的构造窗口;"cleared 轮" = grant_clear 构造调用;"battle_over" = clear_inventory(+retire_mapping)构造调用;结算段序骨架与使用窗口编排 = B1 域——冒烟以构造序扮演、不断言编排正确性;σ 到达机制:D2 全新 RngService 首个 open_stream 恒为 scope 0——到达 scope=σ 须先 `open_stream("reward_roll")` ×σ 丢弃预推进(AC-7 分支 1 按此执行) | +| 纸面契约子集 | 凡依赖 Ψ(σ)/Φ(σ)/σ*/重 roll 轨迹登记值的 THEN,在取号约定实现期 ADR 落地前不视为可执行(受影响:AC-1 分支 1 非雾格值、AC-7 分支 2/3、AC-10 分支 1、AC-12 登记序列);雾格单行退化分支例外(r 无关,现即可执行) | +| 只读观测量 | 映射投影(reward_id/category——引用级)· 库存实例投影(item_id/来源/快照/状态)· 流 scope 计数器(install 恰一 scope 的观测面);相位无字段观测量:Unrolled/Ready/Retired 由受理返回值推断;Cleared = 逻辑失效态(失效句柄查询返回空 + 诊断、作用接口拒绝);实例不变式(Ready 后映射零写入 / Used 不可逆 / 库存条目仅存关卡域)= debug 断言自守——违规路径不可由封闭 API 构造,不经冒烟断言;行为级证据 = AC-1/AC-2/AC-9/AC-14 | +| 期望值推导纪律 | weight/power/tier/T_fog_min/K_dmg/N_draft 全 provisional——各 AC 的 THEN 具体数值按当前表读值/夹具登记值推导、不写死(记号:w_i/t_i/p_i/T_f/K/N);结构定值断言合法:category 五值封闭、pool_tag MVP 二值封闭、use_timing 单值、清图恒正当量校验存在性;权重比例与数值不作定值断言(provisional 归 C3) | +| 拍板语义承载 | 道具段照常消耗(AC-14 分支 3——不采信任何"拒绝不消耗"旧读法)· 清图槽允许 buff 行(AC-11)· 窗口锚点 = 授予段后(B4 冒烟不断言编排时点,构造序扮演) | +| 登记常数协议 | 期望格身份映射 Ψ(σ)、清图身份 Φ(σ)、重 roll 草稿数轨迹、A4① 对账对——入版本库;失效处置 = 先复核登记、登记无误即实现缺陷;登记后安排第二人独立推导复核 | +| 测试种子取号协议 | master=42;σ 取号 = 每 roll_board 调用恰一 scope(install 批含格批 + 清图批尾;retire 后新 install = 新 scope);期望轨迹经 `derive_at("reward_roll", σ)` 纯函数测试通道推导(不推计数器);黄金向量 m=42 scope0=7422916049646135537(D2 向量表复用——推导起点锚) | +| out-of-scope(覆盖声明) | deferral 生效条件 = B1 义务 6 项 + D1 义务 5 项登记(均缺项即 blocking):B1×① 段序骨架与调用序 ∥ B1×② 受理分流 + 满层"奖励未生效"行协议 + 聚合计数字段 ∥ B1×③ clear_reward 授予路径 ∥ B1×④ 窗口与道具段编排(usable_now 组装、满血守卫、零效果判定、逐件"已消耗未生效"行、击杀结果进流水、治疗 clamp、"恒定表值不吃修正"纪律)∥ B1×⑤ 生命周期编排恰一次 ∥ B1×⑥ roll_board 恰一次 ∥ D1×①…⑤ 呈现(不可 G-W-T)∥ 另有:B3 满层拒绝原子(B3 AC 跨引);A4 约束②③(MVP 休眠)与 A3 条款⑤对账闭合(B1 强制检查点);attack_up 复活联动(OQ1);C3 数值与失衡预警①–⑧(playtest 域);未开格身份呈现深度(OQ2/A5+D1) | + +### 夹具与种子策略 + +| 夹具根 | 登记内容 | +|---|---| +| 默认根 | B4 回填的 RewardTable MVP 行集(design「默认根内容行」节八行)+ RewardConfigTable T_fog_min=2 / N_draft=32 / P_min=8 / K_dmg=0(现行默认)——结构断言、F6 供给与方向预检 | +| b4_golden | D2 全表清单拷贝 + 权重/tier 冻结登记值(刀 w=2 t=1 / 治疗包 w=1 t=1 / defense_up w=1 t=3 / clear_default 单刀行 w=1)——黄金载体与默认根内容调参解耦(C3 重调默认权重不触动本根;雾池 = {defense_up t3} 单行退化——雾格分支 r 无关现即可执行) | +| b4_reroll | T_fog_min=1 / K_dmg=1 / N_draft=32 / p_min=4(刀 p=4 须过 F5 加载护栏 (i),随全局 P_min=8 生效);safe_cell = 刀 w=1 t=1 / 治疗包 w=3 t=1 / defense_up w=3 t=2;clear_default 单刀行 w=1 p=4——满足全部加载校验(活 buff 行 ✓ / 雾池非空 ✓ / 清图正当量 ✓);重 roll 可达(首稿零刀 P ≈ (6/7)³ ≈ 0.63)而耗尽经验稀有(≈4×10⁻⁷/σ)——取号约定 ADR 落地后复核可达性拆分、失效即重制 | +| b4_fog_gate | tier 布置使雾池唯一活行 = 高档行 H、非雾池含低档行 L(登记) | +| b4_fog_gate_hot | 同表 + T_fog_min=8 表值烤入——雾态池空加载致命(AC-6 分支 4 致命分支载体——R3 override 不触发加载校验,D2 边界⑤) | +| b4_clear_buff | clear_default 唯一活行 = defense_up | +| b4_reloaded | power/tier 各 +1(effect_id 不动——登记) | +| b4_nofloor | safe_cell 无刀行 + K_dmg=0 | +| b4_attackup_row | safe_cell 追加 attack_up 引用行(attack_up 复活后的正向夹具) | +| 致命根 ×8 | manifest(见下表;含 b4_floor_supply / b4_nodamage) | +| 程序构造 | RewardTable + RewardConfigTable schema 直引(AC-16);资格格清单 b4_list_A(3 格含 1 雾)/ b4_list_B(4 格)/ b4_list_single(1 格)坐标入版本库 | + +- 注入通道:行为夹具根经 `--data-root=` 注入(仅 debug、不入 `data/tables/`——D2 夹具纪律);标量 knob 单变分支(T_fog_min=1、K_dmg=2)经 R3 debug override(域内值、运行时态);加载致命分支一律烤入根承载、不经 override;两通道每夹具恰一登记。 + +### 致命类收窄豁免与 runner manifest + +| 类别 | manifest 行 | 触发条件 | 断言 | +|---|---|---|---| +| 加载期 ×8 | pool_empty_safe | safe_cell 全 0 权 | 独立 headless 进程非 0 退出 | +| 加载期 | pool_empty_fog | 全行 tier < T_fog_min(雾态空) | 非 0 退出 | +| 加载期 | clear_empty | clear_default 池空/全 0 权 | 非 0 退出 | +| 加载期 | clear_no_value | clear_default 唯一活行 = power 0 道具行(恒非空的可判定形式——A3 条款④表侧兑现) | 非 0 退出 | +| 加载期 | no_live_buff_row | safe_cell 有活行但零活 buff 行(tier_min_buff 无定义——F1 对偶致命) | 非 0 退出 | +| 加载期 | dangling_effect | buff 行 effect_id = "ghost_buff"(B3 StatusEffectTable 无此行;条件性夹具:默认根已含 effect_id 引用行 ⇒ 悬空校验已激活——dangling_ref_policy 首引兑现点) | 非 0 退出 | +| 加载期 | floor_supply_missing | K_dmg ≥ 1 ∧ safe_cell 池无 power ≥ P_min 活刀行(F5 加载护栏 (i)——根 b4_floor_supply) | 非 0 退出 | +| 加载期 | no_damage_row | K_dmg = 0 ∧ safe_cell ∪ clear_default 全池零活刀行(F5 加载护栏 (ii) 零伤害世界后闸——根 b4_nodamage,B2 OQ9 并案) | 非 0 退出 | +| 运行期 ×2 | floor_exhaust | roll_board 保底耗尽 | 调用后退出码非 0 | +| 运行期 | empty_eligible | roll_board(∅) debug 断言中止(release 构建语义 = push_error 致命路径——实现期 ADR 登记) | 非 0 退出 | + +- 其余致命类别(通用枚举域/域校验/R1 类)由 D2 AC-2 同类别 loader 夹具承载(表无关,豁免声明收窄);每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明)。 + +## 附录 D:去向映射(零丢失可审计证据) + +| 原文章节 | 落点 | +|---|---| +| 头部(状态/版本/支柱/一句话) | design 头部 | +| 速览(定位/拥有/不拥有/上游/下游/硬约束 + 缺位后果) | design 设计目的定位 + 系统范围 + 依赖与输出;缺位后果展开 → analysis「体验锚点」 | +| 体验锚点(四行 + 反向护栏 + 参考感受) | design 设计目的体验承诺 1–5 + 反向护栏表;参考感受与预读行为论证 → analysis「体验锚点」 | +| 全局流程(奖励授予字符画 + 出口分发表) | design 核心循环(字符画 + 出口分发表) | +| 全局流程(库存生命周期字符画) | design 规则 4 + 状态表 + E7/E15(事实全部保留,字符画不重复) | +| 核心规则 1–10(人话 + 举证) | 人话 → design 各领域章节编号规则(括注原规则号);举证中的公式/校验 → design 对应 F 节与表格;论证 → analysis 对应问题节 | +| 规则 2 实现参照(install 期预分配表) | 事实并入 design F1 表达式 + 边界表 + API 面 roll_board 行(表格不重复) | +| 规则 5 结算顺序表 | 事实并入 design 核心循环字符画 + 规则 5 + B1×① + 调参表非 knob 清单(表格不重复) | +| 规则 1/3 实现参照(两级奖励分流表) | design「奖励类型」节分流表(与 category 枚举表合并) | +| 规则 5/7 实现参照(道具使用窗口表) | design「道具使用窗口」节表格 | +| 枚举(category 五值 + 预留值) | design「奖励类型与数据表」 | +| RewardTable schema(九字段) | design「RewardTable schema」节 | +| 默认根内容行(八行 + 归一占比/雾池/对账对注记) | design「默认根内容行」节 | +| F1–F6 结论 + 推导代码块 + Variables + 验算边界(Example/Output Range/边界) | design 对应 F 节(结论先行 + 表达式 + Variables + 边界表) | +| F2 锚性质 / AC-19 失效面注记 | analysis「清图与雾区奖励如何定价」 | +| F4 attack_up 口径论证(复活史、锚参照迁移) | analysis「道具伤害吃不吃修正」 | +| F5 概率性致命带注记 / 否决案存档 / 保底仅计 damage_item 论证 | analysis「每板刀供给如何保底」 | +| F6 供给面口径 / 衔接注记(B2 OQ3) | analysis「默认根数值如何锚定」 | +| 方向预检(刀供给期望/击杀需求/预警联动/输入缺口) | analysis「默认根数值如何锚定」 | +| 状态(两容器状态表 + 不变式) | design「状态与边界情况」 | +| 边界情况 E1–E21 | design 边界情况表(编号 + 条件 + 行为);E10 选定理由、E3 缓解论证 → analysis | +| 依赖(上游/下游 + 双向一致性核对) | design 依赖与输出双向表 | +| B1 侧义务清单 ×6 / D1 侧义务清单 ×5 | design 依赖与输出(活契约) | +| A4 定价硬约束的 MVP 承载状态 | design 依赖与输出;承载论证 → analysis「清图与雾区奖励如何定价」 | +| 接口漂移登记 ×6 | analysis 附录 A(历史审计) | +| 呈现与 UI 契约 ×10 | design 依赖与输出「呈现数据面契约」 | +| Tuning Knobs(9 knob + 非 knob + 交互 + 所有权) | design 调参表 | +| 失衡预警清单 ①–⑧ | design 失衡预警表;④的 λ 推导 → analysis「默认根数值如何锚定」 | +| 验收标准:测试口径 / 夹具与种子 / 致命 manifest | analysis 附录 E(验收执行规格;夹具根名与 manifest 行名不变,AC 表引用不失联) | +| 验收标准:API 面 / AC ×21 | design 验收标准(AC 编号不动、全文保留) | +| 覆盖矩阵(Core Rules/Formulas/Edge Cases/上游承接/覆盖口径) | analysis 附录 C | +| 缺口预检 ×11 | analysis 附录 B | +| 开放问题 OQ1–OQ7 | analysis 开放问题(编号保留)+ NQ-1–NQ-7 待拍板 | +| 版本历史(六行) | design 版本历史(原六行 + v2 迁移行) | diff --git a/design/gdd/analysis/d1-hud-framework.md b/design/gdd/analysis/d1-hud-framework.md new file mode 100644 index 00000000..b036a144 --- /dev/null +++ b/design/gdd/analysis/d1-hud-framework.md @@ -0,0 +1,224 @@ +# D1 HUD 与界面框架 设计推演 + +> 状态:concluded(未闭合项见文末「开放问题」)· 本文只装"为什么"——`design/gdd/d1-hud-framework.md` 只收本文的结论。 +> 修订时新增问题节,不删旧节(历史即依据)。 + +## 问题 1:为什么 D1 必须作为独立的零玩法状态呈现层系统存在? + +### 广度分析 + +- 候选 A:不设独立呈现层系统,呈现义务散落在各玩法 GDD——收益:少一份文档;代价:27 项跨系统呈现义务没有单一兑付点,A2 的三事件流没有读者、B1 的流水协议没有呈现面,"自动战斗沦为无聊过场"(game-concept 已识别风险 #1)的防线无人承载。 +- 候选 B:呈现层自持部分玩法状态(如本地缓存棋盘、自判合法性)——收益:刷新链路短;代价:与核心双事实源,呈现失真(雾格显影 / 拒绝吞掉 / 格态混淆)直接杀死 P1"我算对了"的感知面。 +- 候选 C(采纳):独立 D1,零玩法状态、零写、零 RNG,只读经成文呈现面、写经组合根。 + +### 深度分析 + +垂直切片两起呈现层崩溃暴露"已定稿 AC 全覆盖 core、呈现层零测试承载"的系统性盲区:① 窗口载荷缺字段直接崩溃(信号参数无运行时类型检查);② battle_finished 后读已销毁对象。这两起不是偶发 bug,而是"呈现层没有成文契约"的结构性后果——信号载荷 schema、场景级测试、信号时点状态可见性必须升格为系统级硬性要求。 + +美术侧同理:art-bible §4.6 图标键契约已冻结(键与通道分配定稿、终稿美术归样张批),样张未制作不阻塞 D1 的前提,是呈现层以程序化占位先行并在契约层登记键位映射——这也只有独立系统文档能承载。 + +### 综合判断 + +D1 独立成系统,定位 Layer 4 Presentation、零玩法上游假设。design 件的系统范围、规则 1–4 / 17–20 与 27 项义务台账即本结论的落点。 + +## 问题 2:为什么界面只当翻译不当裁判、听消息只走中转、读数走白名单? + +### 广度分析 + +- 订阅形态候选:呈现层直订 A2/A3 事件(收益:链路短;代价:违反 ADR-0001,呈现层与玩法系统多点耦合,事件语义漂移直接打进视图)vs 只订 B1 步骤流 + runner 信号、其余经组合根转发(采纳)。 +- 刷新形态候选:纯事件驱动(代价:旗插拔/移动/AP/RAP/库存等动作零事件外发或零推送,纯事件驱动必然漏刷新)vs 命令式回执双通道(采纳:动作调用一返回,组合根立刻重查只读面刷新界面,不干等通知)。 +- 取数形态候选:自由读核心字段(代价:封装破碎、接口漂移不可追踪)vs 成文读数白名单、登记 registry `d1_presentation_surface`(采纳)。 + +### 深度分析 + +ADR-0001 明确"信号参数无运行时类型检查"——契约只能靠成文 schema + 消费侧逐字段断言兜底,这是规则 17 十三项载荷表双重用途(实现契约 + 消费断言)的直接成因。battle.md §Events Fired 的"呈现订阅 A2 三事件"表述与 ADR-0001 有措辞差,本系统按 ADR-0001 收窄为"经组合根转发或命令式回执"(冲突清单⑥,battle.md 正文不改动,复检批对齐)。 + +### 综合判断 + +规则 1–4、17、18 即本结论:零玩法数值字面量、零规则逻辑、零核心写调用(唯一发生在组合根)、零 RNG、零直订 A2/A3、禁回读核心生命周期状态。 + +## 问题 3:移动路径预览为什么是"合法性指示"而不是穿行动画? + +### 广度分析 + +- 候选 A:穿行动画轨道——收益:方向感强;代价:移动免费且高频,每次移动都付动画时间税,违反点击摩擦护栏(P4 ≤2 观测假设的承载面)。 +- 候选 B(采纳):悬停即画 `compute_move_path` BFS 路径蓝细线点迹(合法性指示)+ 执行时 ≤150ms 短滑。 +- 关联候选:点击远格自动"先移动再挖"复合意图——不采纳:移动免费高频,两击的摩擦符合观测假设;留 playtest 验证(A2 评估项留痕)。 + +### 深度分析 + +预览与执行必须同源(同一 `compute_move_path`,不可达返回空即预览不显示 + 禁止光标),否则"画出来的路"与"走出来的路"可能不一致,合法性指示反而成了谎言。F4 语义序仲裁退为防御性兜底,是因为单意图帧 + stop 终结当帧收集的纪律使生产面造不出会乱序的输入(联合清图让位条款生产面不可达)。 + +### 综合判断 + +规则 7、8 即本结论;短滑时长留痕 playtest 观测(battle.md OQ5),旋钮 = move_slide_ms,若反馈"移动太瞬"再议。 + +## 问题 4:棋盘格尺寸为什么是两档定值而不是一条闭式公式? + +### 广度分析 + +- 候选 A:单一闭式 floor(可用高度 ÷ N)——收益:一处公式;代价:固定边距下无法同时得出 36 与 45 两档(见深度分析)。 +- 候选 B(采纳,评审后用户裁决方案 A):cell(12)=36px、cell(10)=45px 两档定值(battle.md 拍板),公式退为装板期可行性校验器(违例 fail-fast)。 + +### 深度分析 + +反证:cell=36 要求可用高度 ∈ [432,443]、cell=45 要求 ∈ [450,459]——两区间不相交,任何固定 pad 的闭式都无法同时落进两个区间。两档值来源是 battle.md 拍板("格 36–45px 见方"两端),不是几何推导的产物;因此 F1 的正当形态是界限断言 + 可行性校验,而非闭式导出。 + +### 综合判断 + +design 件 F1 节即本结论:两档定值 + floor(min(D_w − pad_h, D_h − ap_bar_h − pad_v) ÷ N) ≥ cell(N) 且 cell(N) ∈ [30,45] 装板期断言;数字字号 = max(18, ceil(cell × 0.5))。 + +## 问题 5:结算明细为什么逐行念账、未生效行为什么不静默、极性为什么按键判? + +### 广度分析 + +- 明细形态候选:聚合摘要(收益:干净;代价:玩家无法把每一分收益对回自己哪一步扫雷决策,P2 规划延迟兑现的呈现半边断裂)vs 逐行流水 + 每行来源标注(采纳)。 +- 未生效行候选:静默吞掉(代价:voided 不提示 = 读感欺骗;零值行被吞 = 兑现不可对账)vs 聚合计数呈现 + 形状冗余(采纳——防逐行惩罚轰炸,且非色相独载)。 +- 极性判别候选:按 magnitude 符号(反例:负值增益存在)或按 pool 归属(反例:enemy_output 池两向并存)vs 键级极性表(采纳:attack_up/defense_up/weaken 有利、ambush 有害)。 + +### 深度分析 + +效果行 value 与 pool_value 必须两列分示、禁相加——否则"−130%×2 行 = −260%"的误读必然发生。P2 裁剪判据的最后半米由呈现层走完:行协议 13 类封闭枚举(B1 Core Rule 14)已经把归因字段备齐,呈现层唯一不能做的就是"替玩家省略"。 + +### 综合判断 + +规则 11、12 及其实现参照表即本结论;高光使用规则 = 事件型用独立高光行、属性型强调用行字段标记,庆祝不打在空兑现上(完美排雷首份即零兑付不发高光)。 + +## 问题 6:轮末道具窗口为什么在道具栏原地亮起、有库存恒弹? + +### 广度分析 + +- 候选 A:中央弹窗——代价:打断结算流的视线连续性,与"结算期原地展开明细"的双舞台常驻形态矛盾。 +- 候选 B:常驻库存条非模态——代价:窗口锚点的"至多选一件"承诺感消失,选用/跳过不成决策节点。 +- 候选 C:仅可用品触发——代价:usable_now 之外的库存信息(未选用件政策注记)失去例行曝光位。 +- 候选 D(采纳):E 区原位激活、MVP 有库存恒弹(battle.md 布局决策 D2);候选 B/C 归垂直切片留痕(B4 D1 义务②注记)。 + +### 深度分析 + +原位激活的配套纪律来自切片教训:窗口激活期间必须跳过常读条全量重建——全量刷新会冲掉窗口态(entry 36);空库存直接 skip 不弹窗、窗口标记照常登记(回放输入完整性归 B1),结算零停顿。去饱和 + 金橙色相偏移是 art-bible §2 明度恒定硬规则的合规化实例:锁输入的表达只经色相/饱和,不靠变暗。 + +### 综合判断 + +规则 13 即本结论:原位激活、至多选一件、提交窗口条目位置序(非库存句柄——切片缺陷④契约修正)、跳过恒在、政策注记常驻。 + +## 问题 7:零合法板为什么只做诚实提示? + +### 广度分析 + +- 候选 A:提示(采纳)——B1 上抛非法产物标记 → toast 诚实提示(非模态、不打断)。 +- 候选 B:补偿——补偿逻辑归 B1 域外,D1 无权发明玩法补偿。 +- 候选 C:确定性回退已知好种子——实现期 ADR 候选(A1 OQ2 关联),不是 MVP 面向玩家的形态。 + +### 深度分析 + +玩家永不卡死已由 A1 验收三分支保证,D1 面对的不是"救场"问题而是"诚实"问题:非法产物到达玩家眼前时,静默是读感欺骗,提示是体验债的诚实兜底(原"玩家不可见遥测域"口径由本条承接闭合)。 + +### 综合判断 + +边界情况表"零合法板"行即本结论:MVP 面向玩家的形态 = 仅提示(B1×3③ / A1 EC5,AC-19 注入腿承载)。 + +## 问题 8:体验锚点与美术解耦如何论证? + +### 广度分析 + +- 参考感受:Into the Breach 的格内信息分层(预告上格——威胁画在格子上)+ 杀戮尖塔的逐卡结算反馈。 +- 美术解耦候选:等终稿美术再开工(代价:样张批阻塞整个呈现层)vs 程序化占位先行(采纳:用代码按冻结色板与形状规则直接画出临时图形,终稿到位后原位替换、数据层零改动、零运行时换图)。 + +### 深度分析 + +P1 感知面:规则层的诚实(A2 数字恒真、三源拒绝)必须由呈现层原样送达——任何呈现失真(雾格显影 / 拒绝吞掉 / 格态混淆)= P1 的死亡体验。P2 呈现半边:逐行归因是"这个结果是我哪步扫雷带来的"的唯一兑现形态。P4 容器:挖掘期画面无杂音(能量 2/10,唯一在动 = 悬停呼吸微光)→ 结算期金橙光带逐行横扫(7/10)→ 清图金粒喷泉短促丰收(8/10)→ 失败暮蓝最静一拍(1/10),§2 能量档与明度恒定硬规则由 D1 的转场与反馈系统承载。高光节拍是 P2 爆发兑现节点(创意风险 #2 落点):完美排雷全屏喝彩 + 标中旗金框行 + H2 金框。 + +反向护栏:界面绝不替玩家思考(不预判合法点击以外的东西)、绝不静默改判(voided 不提示 = 读感欺骗)、绝不用装饰伪装逻辑差异(art-bible 原则三)——玩家对 D1 的正确感觉是"一块干净的玻璃":看穿它直接看到棋局与战果,玻璃本身不添乱。占位图同样过硬指标的理由:色板参数域从结构上保证(底足够亮、墨足够深),数字可读性 ≥4.5:1 对程序化占位同样适用;样张定稿 = 砖面形态与色板的冻结闸、终稿资产开工的前置,样张批不阻塞 D1 其余全部验收。 + +### 综合判断 + +design 件设计目的的五条体验承诺、规则 19、规则 22 与 Visual/Audio 执行锚点表即本结论。 + +## 附录 A:切片教训三条硬性要求落位表(entry 17——门禁评审核对项,缺项即 blocking) + +| # | 硬性要求 | 落位 | +|---|---|---| +| ① | 面向呈现的信号载荷成文 schema | 规则 17(13 项载荷表——A2 三事件/B1 行协议引用 + D1 自有遥测成文 + 零事件外发命令式刷新面成文) | +| ② | 场景级 headless 测试进 AC | AC-19 起场景级硬性块(真实实例化战斗屏场景 + 驱动完整对局 + 断言零脚本错误;载体 = 场景探针,ADR-0004 双载体、gdUnit4 deferred) | +| ③ | 信号时点状态可见性规则 | 规则 18(销毁前捕获 / 载荷自包含 / 禁回读核心生命周期态)+ AC-20 断言 | + +## 附录 B:覆盖矩阵(门禁评审用——编号沿用 design 件现行节名) + +| 类别 | 映射 | +|---|---| +| Core Rules | 1–4→AC-19/24/25 · 5→AC-1 · 6→AC-10 · 7→AC-6/7 · 8→AC-6 · 9→AC-4/5 · 10→AC-9 · 11→AC-12/15 · 12→AC-11 · 13→AC-13 · 14→AC-14 · 15→AC-4/11 · 16→AC-8/16/17 · 17→AC-24 · 18→AC-20 · 19→AC-15(键位)/样张引用 · 20→AC-25 · 21→AC-21 · 22→AC-23/26 | +| Formulas | F1→AC-2 · F2→AC-3 | +| Edge Cases | 全表→AC-4/6/8/10/11/13/14/16/17/21/22/23/15(拒绝与 voided→AC-8 及探针负例;零合法板→AC-19 注入腿;双雾区→AC-4/11 腿) | +| 义务台账 | 27 项→design 件「呈现义务台账逐项落位表」逐行 | +| 切片教训 | 3 项→AC-19/20/24 + 规则 17/18 | + +## 附录 C:发现冲突清单(列示待裁决——不改已定稿 GDD 正文与提案) + +| # | 冲突 / 待对齐项 | D1 侧处置 | +|---|---|---| +| ① | game-concept 视觉锚原则 1"暗色地牢底色" vs art-bible 明亮底色反转 | 在案(entry 41)——概念侧注记待概念层修订批;D1 全部取色按 art-bible | +| ② | B2 GDD 敌种键 {grub, ambusher} vs 切片 DND 更名 {goblin, bugbear, ogre_chief} | 键位映射表以 GDD 键集为锚(§4.6.4 冻结);切片更名随 B2/C3 修订批对齐 | +| ③ | B4 GDD 默认根 clear_knife vs 切片供给控制器批 clear_blast + active_inject 池 | 徽章键按 reward_id 冻结、池归属不影响键(§4.6.2 注记);clear_blast 键并列预留、启用随 B4 修订批 | +| ④ | 极性提案 §3.5 跨文档回写清单全部待实施批(B3 四行→七行、B4 schema 扩列、A2 OQ1 关闭、D1 义务条目正式登记〔未开格类型图标/极性视觉语言/负向文案族〕) | 本 GDD 已按 §4.6 冻结键预留槽位;义务条目登记随提案实施批入 systems-index D1 台账 | +| ⑤ | art-bible §4.6.3"全部未开格呈现类型徽章"(提案 R3)vs B4 Rule 8"未开格身份 MVP 不呈现" | D1 裁决:MVP 格面徽章只在已开格呈现(B4 Rule 8 为现行锁定规则);未开格预读随极性提案实施批(其机制前提 = cell_type/pool_tag 扩列)随批修订 | +| ⑥ | battle.md §Events Fired"呈现订阅 A2 三事件"表述 vs ADR-0001 订阅纪律(呈现层只订 B1/runner) | 本 GDD 按 ADR-0001 收窄(规则 2);battle.md 复检批对齐措辞(正文不改动) | +| ⑦ | A3 只读观测量(ap/rap/phase/last_round_end)缺轮内已用刷新次数——刷新按钮"本轮剩 N 次"读数无上游供给 | 接口漂移登记义务(OQ2):A3 随批补只读观测量;落地前按钮显示退化为"RAP−{rap_refresh_cost 表读值} · 每轮至多 2 次"静态文案 | +| ⑧ | 切片已实现的 B1/B2 呈现面 API 未在 owner GDD 登记契约:B1 面 `presentation_snapshot` / `is_battle_live` / `stock_count` / `query_reward_identity` / `submit_window_selection` + `settlement_snapshot` 信号、B2 面 `hero_unit` / `all_enemies`(registry `d1_presentation_surface` 已成文——owner=D1,切片事实转正) | OQ3:现行消费以 registry 条目为契约(D1 消费 = 步骤流判定行 + `presentation_snapshot()`,零阻塞);回 B1/B2 修订批补登记 API 面(接口漂移) | + +## 附录 D:审计注记(评审元数据,不入 design 正文) + +- 复核注记:solo 模式下 creative-director(体验锚点情绪定位——与 battle.md Purpose & Player Need 同源,design 件设计目的为其系统级转写)与 art-director(Visual/Audio 与 art-bible 一致性)未经咨询——生产前人工复核。呈现义务承载依据 = `design/ux/battle.md`(APPROVED,§13 台账 27 项经 ux-review 实存验证)+ `design/art/art-bible.md` §1–4。 +- 上游状态:A1–B4/B1 + D2 全部 Approved(C3 为 Designed 未经评审——D1 零玩法消费;battle.md APPROVED · art-bible §1–4 定稿)——项目首个零玩法上游假设的表现层系统。 +- 归属声明:屏级布局、组件形态、交互细节、动画时长的裁决权威 = `design/ux/battle.md`(APPROVED)——本 GDD 引用其裁决不重复论证,只做三件事:① 升格为系统级规则与 AC;② 补齐 spec 之外的系统级契约(信号载荷 schema / 订阅纪律 / 程序化占位 / 命名统一 / 遥测);③ 逐项落位 27 项门禁义务。 +- 命名统一核销 = session-state entry 35(AC-25 grep 断言承载);读接口白名单转正 = 封装收口批 entry 39/40;敌卡就地更新与窗口态 stomped 教训 = 切片 entry 36。 + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | _draw 迁移复议(ADR-0003 Decision 3 的 D1 裁决登记):MVP 维持 GridContainer + 逐格控件(裁决依据:呈现层程序化占位、样张未定稿、雾纹理/终稿表现力需求未冻结、三探针与现形态耦合〔改造 = 隐藏成本〕)——样张定稿后按表现力需求(biome 色板/雾纹理/动效复杂度)复议二选一;若迁移,`Viewport.push_input` 端到端探针从加分项变必需项(ADR-0004) | D1 复议批 | 样张定稿后 | +| 2 | A3 轮内刷新次数读数(接口漂移登记):刷新按钮"本轮剩 N 次"需要 A3 暴露轮内已用刷新计数(现只读观测量 ap/rap/phase/last_round_end 未含)——A3 修订批补只读观测量 + AC 前言扩列;落地前按钮退化为静态文案"RAP−{rap_refresh_cost 表读值} · 每轮至多 2 次" | A3 | 正式实现接线批 | +| 3 | B1/B2 呈现面 API 契约登记(冲突⑧扩条):切片已实现的 `presentation_snapshot` / `is_battle_live` / `stock_count` / `query_reward_identity` / `submit_window_selection`(B1 面)+ `settlement_snapshot` 信号 + `hero_unit` / `all_enemies`(B2 面)均未在 B1/B2 GDD 登记——registry `d1_presentation_surface` 为现行契约载体;回 B1/B2 修订批补登记 API 面,独立信号需求随批裁决(接口漂移) | B1 / B2 | B5 GDD 前 | +| 4 | battle.md 订阅表述对齐(冲突清单⑥):ADR-0001 订阅纪律(呈现层只订 B1/runner)与 battle.md §Events Fired"A2 三事件呈现订阅"的措辞差——本 GDD 按 ADR-0001 收窄;battle.md 复检批对齐 | battle.md 复检 | 下次 /ux-review | +| 5 | Gamepad 焦点序(battle.md OQ8 继承):Partial 支持非 MVP——焦点环顺序待 gamepad 批次 | gamepad 批次 | gamepad 批 | +| 6 | 相邻域屏 spec(battle.md OQ9 继承):`design/ux/node-reward.md` / `design/ux/level-clear.md`——本 GDD 只锁出口锚点(节点奖励覆盖层 / 关卡结算屏的完整形态归后续 spec) | 后续 spec | 垂直切片后 | +| 7 | accessibility-requirements.md(battle.md OQ2 继承):正式分层(文本缩放/色盲模式/按键重映射)待该文档定稿——gate-check Pre-Production 缺口在案 | 该文档定稿批 | 文档定稿 | +| 8 | 键集扩容流程锚定:新 effect_id / reward_id / enemy_id / cell_type 上线 → art-bible §4.6 修订 + SettlementTable effect_pool_map 随批登记(接口漂移双登记——本 GDD AC-15 fail-fast 强制暴露漏登记) | art-bible / D2 修订批 | 键集扩容时 | +| 9 | 极性提案实施批的 D1 义务条目(冲突清单④):未开格类型图标 / 极性视觉语言 / 负向文案族按提案 §3.5 正式登记入 systems-index D1 台账(本 GDD 已预留键槽位与裁决⑤) | 极性提案实施批 | 提案实施批 | +| 10 | 明细带挖掘期常驻摘要(battle.md OQ6 继承):常驻(现方案)vs 结算后收起——playtest 观测回看频率后裁决 | playtest 后裁决 | 垂直切片 playtest | +| 11 | D5 教学挂靠(battle.md OQ13 继承):挖到雷=敌方优势 / 旗挡链 / 首次蓄力提示 / 囤刀闭环——toast 体系为落点候选,D5 GDD 批定 | D5 | D5 GDD 批 | +| 12 | TunePanel 退役时点(battle.md OQ10 继承):C4 天赋树落位批退役(规则 21 已标注);Playtest 诊断按钮保留 | C4 | C4 落位批 | + +## 去向映射(零丢失可审计证据) + +| 原文章节 / 表 | 落点 | +|---|---| +| 头部块(状态/版本/支柱/层级/一句话) | design 头部块 | +| 头部复核注记 / 上游状态行 | analysis 附录 D | +| 速览表(定位/拥有/不拥有/上游输入/下游输出/硬约束) | design 设计目的 / 系统范围 / 依赖与输出 | +| D1 的存在理由(四条) | analysis 问题 1 | +| 体验锚点表 + 直接承载/参考感受/反向护栏 | design 设计目的(承诺形态)+ analysis 问题 8(论证) | +| 全局流程(呈现刷新链路字符画 + 出口分发表) | design 核心循环 | +| 核心规则归属声明 | analysis 附录 D(权威优先句入 design 依赖与输出引言) | +| 规则 1–4(边界/订阅/读面/写通道) | design「呈现边界与数据通道」 | +| 规则 17 / 18 / 20(载荷 schema / 快照纪律 / 命名统一) | design「呈现边界与数据通道」;切片崩溃机理与 ADR 论证 → analysis 问题 2 | +| 规则 5 / 6 / 21 + 五区布局表 + 敌卡六件套 + 意图线索映射目录 + 战斗屏状态机 | design「战斗屏布局与组件」 | +| 规则 7 / 8 / 9 / 15 + 四种格态 / 光标语义 / hint_id / 拒绝与 voided 映射 | design「挖掘输入与棋盘可读」;穿行动画与复合意图取舍 → analysis 问题 3 | +| 规则 10 / 11 / 12 / 13 + 三张实现参照表 | design「信息与归因呈现」;逐行归因/极性判别/窗口候选论证 → analysis 问题 5、6 | +| 规则 14 / 16 / 19 / 22 + 终局反馈表 + Visual/Audio 锚点表 + 边界情况表 | design「提示·终局反馈·美术与可访问性」;占位解耦论证 → analysis 问题 8 | +| 枚举·ux 遥测行族 | design 规则 17 载荷表 #13(字段与值域在该行展开) | +| 数值结论与推导(F1 / F2 / 显式排除声明) | design「呈现几何与可读性判据」;F1 定值 vs 闭式论证 → analysis 问题 4 | +| 状态(战斗屏状态机 + 不变式) | design「战斗屏布局与组件·战斗屏状态机」 | +| 边界情况表(20 行) | design「提示·终局反馈·美术与可访问性·边界情况」;零合法板三形态选型 → analysis 问题 7 | +| 依赖表 + 索引分层注记 | design 依赖与输出 | +| 呈现义务台账逐项落位表(27 项,活契约) | design 依赖与输出 | +| 意图线索映射目录(B2×4① 成文载体) | design「战斗屏布局与组件」 | +| 切片教训三条硬性要求落位表(审计映射) | analysis 附录 A | +| 发现冲突清单(8 项) | analysis 附录 C(未闭合项同时以 OQ2/3/4/9 入开放问题) | +| Tuning Knobs(8 knob + 非 knob + 性能预算 + 交互 + 调参流程) | design 调参表 | +| Visual/Audio 要求 + Asset Spec 注记 | design「Visual/Audio 执行锚点」 | +| UI 要求(权威 spec / UX Flag / pattern 库 / accessibility / 场景探针) | design 依赖与输出「屏级 spec 与配套文档」 | +| 验收标准(测试口径 + AC-1–26) | design 验收标准(编号不变) | +| 覆盖矩阵(门禁评审用) | analysis 附录 B | +| 开放问题 OQ1–12 | analysis 开放问题(编号不变) | +| 版本历史(v1 / v1.0-lean / v1.1) | design 版本历史(原行全保留,追加 v2 迁移行) | diff --git a/design/gdd/analysis/d2-data-config.md b/design/gdd/analysis/d2-data-config.md new file mode 100644 index 00000000..815b8b93 --- /dev/null +++ b/design/gdd/analysis/d2-data-config.md @@ -0,0 +1,196 @@ +# D2 数据配置与数值表 设计推演 + +> 状态:concluded(未闭合项见文末「开放问题」)· 本文只装"为什么"——`design/gdd/d2-data-config.md` 只收本文的结论。 +> 修订时新增问题节,不删旧节(历史即依据)。 + +## 问题 1:为什么要有 D2——数值集中一处、schema 先行、单点发放? + +### 广度分析 + +- 候选 A:参数随各系统代码硬编码——收益:少一层间接;代价:调一次难度要改五处代码、战斗无法用固定种子复现、headless 自动化测试无从谈起。 +- 候选 B:集中数值表但允许多入口读取——代价:副本绕过校验,热重载换新值时副本不知情,单一事实源名存实亡。 +- 候选 C(采纳):数值表单一事实源 + ConfigDB 单点类型化读取 + schema 先行、数值回填。 + +### 深度分析 + +schema 先行使 D2 能在设计顺序第 1 位交付而不阻塞下游:结构(自定义 Resource 类,字段带类型/范围/默认/注释)由 D2 定死,数值由 owner 系统 GDD 按设计顺序回填——先有格子、后填数字。本 GDD 只定数据结构、注册表与校验模式,不替任何系统定平衡。 + +### 综合判断 + +design 件设计目的、系统范围与规则 1/2/3 即本结论:玩法数值零代码字面量、取数只经 ConfigDB、全部表两件套交付。 + +## 问题 2:为什么 fail-fast 无软回退、唯一软着陆只给 debug 热重载? + +### 广度分析 + +- 候选 A:软回退(出错用默认值或上一好值带病运行)——代价:错误数值上线比崩溃更伤(P1/P2 信任底线),且放行带病数值会使 CI 冒烟失去意义。 +- 候选 B(采纳):加载期与运行期一律致命中止;唯一例外 = debug 热重载校验失败时保留旧表停留 Ready——不杀运行中的调试会话。 +- 校验通道候选:单通道(测试侧无法断言致命路径——Godot 无进程内拦截 push_error/push_warning 的 API)vs 校验双通道 collect_only(采纳:游戏通道致命、测试通道收集不升级,诊断收集器承载冒烟断言与四元组去重状态)。 + +### 深度分析 + +release 构建的致命表现(引擎默认错误路径的实际行为)尚未验证,显式 deferred 到首次 release 导出 checklist——设计顺序第 1 位时无导出模板可依赖。headless 下走 `--script` 协议独立进程非 0 退出,因为同阶段无主场景可依赖,脚本须 extends SceneTree 以获得 quit(code)、autoload 须由脚本显式实例化。 + +### 综合判断 + +规则 6、校验双通道表、状态机 Failed 两路径与 EC16 即本结论;校验严格度不设 knob(fatal 写死)。 + +## 问题 3:难度行解析为什么 floor + warning、无兜底无外推? + +### 广度分析 + +- 候选 A:default 行兜底——代价:配置缺口被静默吃掉,难度断档不可见。 +- 候选 B:clamp / 线性外推——代价:超表成长语义是 C3 owner 公式的职责,D2 越权定义成长。 +- 候选 C(采纳):floor 到不超过请求坐标的最近已有行 + 非精确命中记 warning(四元组去重);实体键精确匹配不近似;越域请求由前置守卫致命拦截。 + +### 深度分析 + +核心判据:进度坐标可以就近近似,"是哪种敌人"不能近似——身份近似会静默改变遭遇构成、损害 P2 归因。warning 定位是开发期异常信号而非审计工具(防刷屏去重是刻意的);难度审计由离线曲线导出工具承担(headless 导出 resolve(level 1..N × depth 0..M) 矩阵供 diff,C3 期交付——排期倒挂见开放问题 #5)。depth 分量永不 ceil 的保证来自 EC15 锚行校验:防"入口天灾"(入口请求静默 floor 到深行造成难度突刺),写死开、非 knob,重议须走本 GDD 显式修订流程。 + +### 综合判断 + +design 件「难度行解析」的 R1 结论与表达式、missing_type 枚举(base_ceil 恒记规则由本分类推导)与 EC4/EC5/EC14/EC15 即本结论。 + +## 问题 4:流种子派生为什么是纯函数、为什么不用运行时 hash()? + +### 广度分析 + +- 候选 A:运行时 `hash()` 混合流名——代价:Godot 不承诺 hash 跨版本稳定,黄金向量与跨版本复现失去意义。 +- 候选 B:scope 自增值直接作种子——代价:同 master_seed 下不同流的同序号 scope 互相碰撞。 +- 候选 C(采纳):`( SALT_s XOR wrap64(master_seed + scope × GOLDEN_RATIO) ) AND 0x7FFFFFFFFFFFFFFF`——每流互异奇盐、黄金比例乘数、wrap64 自然回绕、掩码收尾。 + +### 深度分析 + +中间积本就越界(乘数 0x9E3779B97F4A7C15 > 2^63,scope=1 即回绕)——这是设计依赖的语义而非缺陷,故禁止 float 中间量(53 位尾数必丢精度)、禁止对中间量用 `%`(带符号取余语义易误用)、禁止"主动修复溢出";三个常量均 ≥2^63,GDScript 十六进制字面量按二补数解析为负,故统一用分段表达式书写。跨 master_seed 的全局单射由鸽笼原理数学排除,不做声明。INT64_MIN 致命而非归一化:abs 在 int64 对 INT64_MIN 恒等回绕,输出掩码抹掉 bit 63 使其与 0 静默别名(实测与 0 在全流种子逐一相等——掩码本身是规约的一部分);负 scope 同理会与 scope+2^63 经掩码别名,故 debug 断言中止。计数器永不重置、不随关卡/图序重置——否则不同关卡的同序号图会得到相同种子;set_master_seed 仅许全流计数器全零时调用——冒险中途重播种会静默清零计数器,击穿同冒险无碰撞前提与 D3 快照语义。 + +### 综合判断 + +design 件 R2 结论、公式、实现级规约三行、黄金向量表(AC-3 常量依据,经独立复算验证)即本结论;状态线独立不变式(reload 不触碰 RNG——若按状态机对称性在 reload 时重置 RNG 将击穿 AC-4)同理成立:派生只依赖盐/master_seed/scope,与表数据无关。 + +## 问题 5:RNG 为什么封闭三流、增流为什么要过 P1 评审? + +### 广度分析 + +- 候选 A:各系统自建 RandomNumberGenerator / `randomize()`——代价:不可复现,回放/审计/自动化测试全废。 +- 候选 B:流自由注册——代价:流复用破坏可复现隔离,未知名流静默产生。 +- 候选 C(采纳):封闭具名流(board_gen / reward_roll / encounter),增流 = 改代码 + P1 支柱评审(随机须有界、可见或可推理)——有意设卡。 + +### 深度分析 + +三流的 scope 事件绑定是各自消费者契约的一部分:board_gen = 每次生成新扫雷图(A1 换图时);reward_roll = 每次 board install 批(格奖励批 + 清图奖励批,B4 AC-1 为契约源);encounter = 每场战斗实例化恰一 scope(begin_battle 实例化期、扫雷开始前——unit_count 区间均匀骰在挖掘决策域之外,P1 合规;C3 遭遇随机化批增流,registry 已登记)。`derive_at` 纯函数不推计数器——预览取数不污染正式序列,杜绝预览类取数的平衡 Heisenbug。同帧多系统请求同一流允许(EC13):Godot 主线程玩法逻辑单线程假设成立,`open_stream` 各自取号按调用顺序串行生效,不引入锁。玩法随机(如结算浮动)D2 不预置——任何系统若需要,自带方案走增流通道并通过 P1 评审(原 R4 显式排除裁决:玩法平衡公式与玩法随机都不在 D2,归 owner 系统 GDD)。 + +### 综合判断 + +规则 5、封闭流表、取数入口表与 EC12/EC13 即本结论;combat_var 类玩法随机预置流不设基础设施。 + +## 问题 6:debug override 链为什么是这五条边界? + +### 广度分析 + +- 覆盖位置候选:各系统自读 override 源——代价:gameplay 可见 override 来源、多点校验漂移;采纳 = 覆盖判定与存储内聚 ConfigDB(规则 2 单点)。 +- key 空间候选:任意路径含进度表行寻址(如 `difficulty.(2,0).hp`)——v1 收窄为仅单行全局表字段,行寻址语法登记为后续扩展。 +- release 形态候选:运行时碰巧不生效——采纳 = 结构性不存在(不加载、不注册任何 override 源,恒等是结构保证)。 + +### 深度分析 + +override 值类型与范围复用 schema 校验逻辑——防止 debug 会话注入越界值污染平衡基线(EC11);未知 key 同样 warning + 忽略。debug 谓词做成可注入形式(`OS.is_debug_build()` 之外引入 feature 标签闸,例 `sim_release`),AC-8 的模拟以此为基础;"release 不加载 override 源"的结构保证验证本身 deferred 到首次 release 导出 checklist(AC-18)。override 仅为运行时状态,永不回写 .tres。 + +### 综合判断 + +design 件 R3 结论与边界五条即本结论。 + +## 问题 7:体验锚点论证——"玩家永远不该注意到的地基"凭什么撑 P1/P2? + +### 广度分析 + +- D2 无专属玩家幻想:P1"我算对了"的掌控感来自 A1/A2 可推理设计,P2 的延迟兑现感来自 B1 可见结算;D2 只提供三种消极保障。 +- 手感稳定("改数字不改代码"):全部影响手感的数字集中一处可调 + 热重载即改即验——"测试数据驱动平衡"的开发方式,任何"这轮玩起来不对劲"都能快速修正且不引入新毛刺。 +- 公平可信("同决策必同结果"):可复现是开发侧信任底线(回放/审计/自动化测试),不等于玩家侧可推理;玩法随机零预置(走增流通道 + P1 评审)。 +- 难度可信(P4,"越下越险由结构保证"):数值表天生带(关卡序号, 节点深度)两维——难度曲线可审计、可回溯,而非散落各处的魔法数字。 + +### 深度分析 + +难度第三轴"精英/Boss 修饰"已定稿难度 = f(关卡序号, 节点深度, 精英/Boss 修饰)(game-concept),但修饰语义归 C1/C3——D2 只做 DifficultyTable 的 elite_/boss_ 前缀命名空间预留与 Boss=Dmax 行约定,不定义任何修饰数值、不预写修饰枚举;两键定不了精英性的节点形态(如分支地图同深度并存精英与普通节点)允许改键,按接口漂移修订规则回写(与 MetaTable 同等豁免待遇);A1 OQ6 是 C1 定稿时的强制重审门。DifficultyTable 乘数域 [0.1,10] 已 registry 预登记;越域 clamp+warning 是 registry 登记的显式例外——组合事故非配置事故,非 fail-fast 场景。 + +### 综合判断 + +design 件设计目的四条承诺、规则 4 轴归宿表与 DifficultyTable 占位行即本结论。 + +## 问题 8:已裁决争议与数值锚定论证汇总 + +### 广度分析 + +- 单行全局表先例链:ActionEconomyTable 立先例 → HeroTable / RewardConfigTable / SettlementTable 沿用(无成长语义的表允许只放一行);MetaTable 为元进度轴豁免,C4 可改键。 +- weaken = −65(值域 [60,85]——B1 F0 裁决,条款 2 锚闭合);defense_up = −30(减伤墙批 C3 投值,叠满两层 −60 帽——B3/registry 同批)。 +- HeroTable atk = 6 原型锚(provisional 归 C3);atk = 0 加载拒绝——"0 基线 × 百分比增益恒零"死锁自 schema 层杜绝(基础攻击恒存在,消费面 = 主角战斗段先手攻击)。 +- RewardConfigTable 标定:active_p0 = 0.4(供给控制器注入概率初值,首件之后每张新板——E[战斗获取] ≈ 1 标定目标,归 C3);active_decay = 0.5 指数递减 0.4→0.2→0.1→…(加难杠杆候选 0.6 随 C3 OQ7 扫描);p_min = 8(默认根最低刀 power——K≥1 供给闸刀力下界 floor_supply_missing);k_dmg = 0 = F5 断言关闭——刀保底语义由供给控制器战斗首件保证取代,no_damage_row 后闸托底。 + +### 深度分析 + +两处溯源缺口在案:EnemyTable MVP 行值(goblin 18HP / 4ATK 等)出自原型 REPORT、出处待查(义务 #5);HeroTable max_hp MVP 值出处待查(义务 #6)。provisional 值在 owner 定稿前不视为平衡事实;定稿后删除调参表清单对应条目,值的事实登记归 owner GDD 与 registry——已定稿占位参考值(标中旗 / 清图奖励 / 状态四行 / 奖励池等)已随各 owner 定稿清退。 + +### 综合判断 + +数值事实留 design 调参表与 registry,锚定论证收于本节;胜场成长机制去留(+25% HP / +1 ATK——反雪球问题)仍待 C3 裁决(义务 #7,在案未闭合)。 + +## 附录 A:评审与审计元数据 + +- 评审:lean 门禁 systems-designer / qa-lead;full 评审 + 同日 re-review 含 game-designer / creative-director,CD-GDD-ALIGN 终裁 Approved——评审日志 `design/gdd/reviews/d2-data-config-review-log.md`。 +- 双向一致性核对明细(与 systems-index 依赖分层表核对一致):A1←A4,D2 · B2←D2 · B4←D2 · B1←A3,B2,B3,B4,D2 · C2←C3,D2 · C4←D3,C2,D2 · C3←D2,B2 · Foundation 含 D2;C2 / B1 / B4 / C4 ← D2 四条边已补入索引依赖分层表;B4←D2 边已按 B4 定稿收窄——A2 事实经 B1 转化零直接接口、B3 为验收锚关系非运行时边。 + +已闭环对账义务(历史审计,design 不再列): + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | ActionEconomyTable 跨字段校验(任一动作成本 ≤ ap_per_round)条件性夹具登记 | A3 | 已登记(schema 类别承载,随 A3 冒烟) | +| 2 | VariationTable 跨字段校验 hide_numbers⇒safe_nonzero 夹具 variation_mask_constraint | A4 | 已激活 | +| 3 | StatusEffectTable effect_id 悬空引用校验(条件性夹具——首个含引用字段的 owner 回填时补) | B3 / B4 | 已激活(B4 defense_up 首引,夹具 b4_dangling_effect) | +| 12 | 双向一致性四条边补入索引依赖分层表(C2 / B1 / B4 / C4 ← D2) | systems-index | 已完成 | + +## 开放问题 + +| # | 问题 | owner | +|---|---|---| +| 1 | headless 测试载体终稿:MVP 用 `--headless --script` 冒烟(本 GDD 决策);gdUnit4 是否在 Technical Setup 引入并迁移冒烟用例 | Technical Setup 阶段(systems-index 门禁增补记录项) | +| 2 | 编辑器内表文件热重载自动化(当前仅 debug 键触发;低优先) | 未指派 | +| 3 | 数值表显示名的本地化 | D6 | +| 4 | C4 MetaTable 并入 ConfigDB 还是独立 autoload(倾向并入) | C4 GDD | +| 5 | 难度曲线导出工具(R1 审计的离线载体):headless 导出 resolve 矩阵脚本,C3 期交付——排期倒挂:BoardGenTable 可推理率曲线自 A1(第 5 位)起回填,A1 定稿前须有临时抽查载体(入 systems-index 门禁记录) | C3 GDD 登记排期 | +| 6 | systems-index 树形概要行仍写"九张表单一事实源",与本 GDD 现行十张表 schema 目录(AuxItemTable 转正后)不一致——索引行归 systems-index 维护批修订,本件不代改 | systems-index 维护批 | + +## 去向映射(零丢失可审计证据) + +| 原文章节 / 表 | 落点 | +|---|---| +| 头部块(状态/版本/支柱/层级/一句话) | design 头部块;评审行 → analysis 附录 A | +| 速览表(定位/拥有/不拥有/上游/下游/硬约束) | design 设计目的 / 系统范围(含硬约束行)/ 依赖与输出 | +| 存在理由·策略·定位三行 | design 设计目的 + analysis 问题 1、7 | +| 体验锚点表 | design 设计目的四条承诺;论证 → analysis 问题 7 | +| 全局流程(三链路字符画 + 出口分发表) | design 核心循环 | +| 核心规则 1 / 2 | design「统一取数与调试覆盖」规则 1 / 2 | +| 核心规则 3 / 4 + 表形状 / 轴归宿两表 | design「表结构与回填」 | +| 核心规则 5 + 流表 / 取数入口表 | design「随机数服务」 | +| 核心规则 6 / 7 / 8 + 错误两类表 | design「加载·校验·热重载」 | +| 规则 6 实现参照——校验双通道表 | design「加载·校验·热重载·校验双通道」 | +| 枚举·十张表目录 / 未回填占位表 / MVP 内容行 | design「表结构与回填」三个子节 | +| 枚举·RNG 流 | design「随机数服务·封闭流与取数入口」(与规则 5 流表去重合并,内容一致) | +| 枚举·missing_type | design「难度行解析·missing_type」 | +| 枚举·致命错误类别 | 并入 design 规则 6 错误两类表(段序违例/池映射悬空/p_perfect 越域三覆盖项并入加载期行) | +| 枚举·override key 空间 | 并入 design R3 边界②(key 形态与支持域内容完全重合) | +| 数值结论与推导 R1(结论/推导/变量/Example/去重键) | design「难度行解析」(结论 + 表达式 + 变量 + 验算);取舍与审计论证 → analysis 问题 3 | +| 数值结论与推导 R2(结论/公式/实现级规约/变量/黄金向量) | design「随机数服务·R2 流种子派生」;机理与候选论证 → analysis 问题 4 | +| 数值结论与推导 R3(结论 + 边界五条) | design「统一取数与调试覆盖·调试覆盖链」;候选取舍 → analysis 问题 6 | +| 数值结论与推导 R4 显式排除 | design 系统范围不负责清单 + 规则 5 增流通道;论证 → analysis 问题 5 | +| 状态(状态机表 + 不变式) | design「加载·校验·热重载·状态机」 | +| 状态·RNG 生命周期 / 快照三行 | design「随机数服务·RNG 生命周期与快照」 | +| 边界情况 EC1–EC16 | design「加载·校验·热重载·边界情况」(编号不变) | +| 依赖表 + 三行注记 | design 依赖与输出;双向一致性核对明细 → analysis 附录 A | +| 登记与对账义务 #4–#11(在案) | design 依赖与输出·登记与对账义务表 | +| 登记与对账义务 #1 / #2 / #3 / #12(已闭环) | analysis 附录 A | +| 呈现与 UI 面 | design「呈现与 UI 面」 | +| Tuning Knobs·D2 自身 knobs + 交互 / 清退注记 | design 调参表·D2 自身 knobs | +| Tuning Knobs·九张表逐表字段行 | design 调参表·表 schema 逐表字段行 | +| 验收标准·测试口径 + AC-1–AC-18 | design 验收标准(编号不变) | +| 开放问题 1–5 | analysis 开放问题(编号不变);#6 为本次迁移新登记的跨文档不一致 | +| 数值锚定论证(weaken/defense_up/atk/active_p0/p_min/k_dmg 等) | analysis 问题 8(数值事实本身留 design 调参表与 MVP 内容行表) | +| 版本历史全部行 | design 版本历史(原行全保留,追加 v2 迁移行) | diff --git a/design/gdd/b1-settlement.md b/design/gdd/b1-settlement.md new file mode 100644 index 00000000..ea8d02a2 --- /dev/null +++ b/design/gdd/b1-settlement.md @@ -0,0 +1,692 @@ +# B1 战斗结算规则(Combat Settlement Rules) + +> **状态**:Designed · **版本**:v2 · 2026-09-14 +> **一句话**:B1 把每轮扫雷的结果逐行兑现为战斗结果,并判定胜负。 +> **推演件**:`design/gdd/analysis/b1-settlement.md`(本文件只收结论,理由在那里) +> **承载支柱**:P2 主("扫雷的因、战斗的果"的兑现面)· P3(雷的双面兑付)· P1(条款③掌控感的经济侧验收) + +## 设计目的 + +战斗域编排与结算中枢、轮次生命周期唯一 owner。少了它,扫雷每一格的结果没有战斗兑付面,"扫雷的因、战斗的果"无从兑现。 + +1. 玩家能在结算明细里逐行看到每行来自哪格/哪旗/哪雷(P2"都是我算出来的");无来源行 = 断言失败。 +2. 挖掘决策的风险不等式与"推到底"支配性由本系统闭合签字(P1"挖下去值得")。 +3. 敌方伏击行与标中旗削弱行同框、同呈现语言(P3"赌桌不是陷阱")。 +4. 完美排雷双份兑付、标中旗行高光("完美被加倍兑现")。 + +## 系统范围 + +负责: + +- 轮次编排:战斗开始序列、每轮七段结算、出口分发(继续/换图/挖掘期刷新/战斗结束/关卡结束) +- 结算触发与段序;伤害消费侧语义(池合成、clamp、溢杀、死亡判定时机) +- 明细流水协议(13 类行 + 字段分层)——呈现/回放/导出共读的唯一数据源 +- 统一当量换算(F1)、乘区/地板/取整语义(F2)、clamp/溢杀/死亡判定数值边界(F3)、条款③⑤闭合判据与配对口径(F4/F5)、伤害A/B 充能与甲效结算(F6)、明细行协议的数值字段 +- 上游义务账本(B3×8 / B2×6 / A2×3 / B4×6);A3 条款①②③⑤闭合签字 + +不负责: + +- 单位状态、hp 递减与 floor 0、Dead 翻转、指针冻结(B2) +- 状态实例 L×m 合成——B3 原样输出不 clamp,B1 只做池 clamp 收口(B3) +- 奖励行值/权重/tier/C 量级与差异化分支(B4/C3);库存内容(B4) +- 行动价目与 RAP 经济(A3);棋盘规则、雷密度/可推理率/雾负担 actuals 分布(A1/A2) +- 敌我表值曲线 atk/hp/cap/unit_count/pattern(C3,B2 行值供给) +- 呈现与交互形态(D1/B5)、音频(D4)、教学(D5) +- 全部 provisional 数值调参(C3);m_w 终值(B4/B1 锚 owner 共裁);P_perfect 数值(C3) +- 清图总预算"够不够"的判、战斗长度/轮数曲线、遭遇构成(A1/A3 契约、C1/C3) + +## 核心循环 + +``` +begin_battle(encounter) + │ ① set_reload_gate(true) [D2] 调试门置位 + │ ② notify_battle_start() [B3] 容器开域 + │ ③ encounter 骰 [RngService] unit_count 区间均匀骰定本战实际数量(封闭流,恰一 scope) + │ ④ spawn(L, D, hero_hp, n) [B2] 行值快照实例化(n = 实际数量 override;定值行 n = unit_count) + │ ⑤ install_board + roll_board [A2/B4] 恰一次;资格格清单 = S ∖ R₀ + │ ⑥ notify_round_start() [A3] → Active + ▼ +RoundActive(挖掘期,B1 只缓冲事件流) + ▼ +轮末触发:Waiting 相位编排点(只读 A3 相位观测量,不依赖事件到达序) + ▼ +①授予段 ──── cell_revealed 批 → flag_judged 批 → 清图奖励(仅 cleared 轮) + ▼ +②道具使用窗口 ─ 玩家至多选一件或跳过(全流程唯一玩家输入点) + ▼ +③增益/减益段 ─ query_active 双侧读值 → 语义映射 → 效果明细行 + ▼ +④道具段 ───── 伤害道具:恒定表值打 default_target;治疗包:clamp 回复 + ▼ +⑤战斗段 ───── 主角先手攻击(D_hero 三分量——F3⓪/F6)→ 逐存活敌(行主序):query_attack → apply_damage → advance + │ ※ hero hp=0 → 立即短路,剩余敌不作用,直入 ⑦ + ▼ +⑥衰减段 ───── decay_round() 恰一次(含末轮;先兑付后衰减的时序契约) + ▼ +⑦判定段 ───── hero hp=0 → 失败 │ 存活敌=∅ → 胜利 │ 否则 → 下一轮 +``` + +| 结局 | 触发 | 动作序列 | 约束 | +|---|---|---|---| +| 继续 | 判定=下一轮 | `notify_settlement_done(same_board)` → `notify_round_start()` | 同图续轮 | +| 换图 | cleared 轮 | `retire_board` → `retire_mapping` → A1 `generate` → `install_board` → `roll_board` 恰一次 → `notify_settlement_done(new_board)` → `notify_round_start()` | stopped/ap_exhausted 恒 same_board,违约 fail-fast | +| 挖掘期刷新 | 玩家经 D1 发起 | A3 `request_refresh` 扣费 → 换图全序(无结算段、无 round_end、轮次不终结) | RAP 不足→拒绝上抛;**每轮 cap 2(RoundStart 重置)**——与 A3 `refresh_cap_per_round` / registry `rap_rules` / D1 同口径,非"每图";刷新图无清图奖励 | +| 战斗结束 | 判定=胜/负 | `retire_mapping` → `despawn_all` → `notify_battle_end` → `notify_combat_end` → `set_reload_gate(false)` | 库存不清(关内延续) | +| 关卡结束 | 上层显式调用 | `clear_inventory()` 恰一次,清空前快照计数返回 | victory/defeat 皆然 | + +## 编排与触发 + +### 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Uninit | 冷启动 / 清理完成 | 唯一出口 `begin_battle` | +| BattleSetup | `begin_battle` 受理 | 战斗开始序列;任一步拒绝 = fail-fast 上抛 | +| RoundActive | `notify_round_start` | 不干预挖掘,只缓冲事件流 | +| RoundWaiting | 轮次结束事务达成 | 执行七段结算;窗口段为唯一玩家输入点 | +| BattleOver | 判定段产胜负 | 战斗结束序列 → Uninit | + +编排不变式: + +- 同一时刻至多一个结算事务在飞(窗口为事务内暂停点非并发入口)。 +- 段序恒全序执行(战斗段短路是段内规则非跳段)。 +- 全部跨系统调用集中在 B1——任一下游被 B1 以外调用 = 架构违约(AC-43 静态扫描域)。 + +### 规则 + +- **规则 1**:B1 一家发起战斗域全部跨系统调用,B2/B3/B4 相互零直调,绕过 = 架构违约;结算本体三约束 = 零随机、零 UI 依赖、禁 await。唯一随机例外 = 战斗开始 encounter 骰(unit_count 区间均匀骰、封闭流恰一 scope,在结算步骤流之外,可回放性不受影响);将来结算本身若需随机数,必须走数据层新增增流通道并过评审(通道定义见 D2 Rule 5 与 D2 流枚举 encounter)。 +- **规则 2**:每轮挖掘结束、回合状态机进入轮末等待相位时,B1 自动发起一次整轮结算;触发只读 A3 相位读数与 last_round_end 标记,不依赖事件到达序;事件订阅 vs 相位轮询留实现期 ADR。 +- **规则 3**:挖掘期挖格/插旗事件不立即兑奖,按轮进缓冲区,轮末按 A2 事件规范序统一转化;每条事实从记录到授予恰好处理一次——B3 不去重、B4 幂等仅兜底,正常路径不重复发奖不漏发。 +- **规则 4**:每轮结算严格按 ①授予→②道具使用窗口→③增益/减益→④道具→⑤战斗→⑥衰减→⑦判定 全序执行;核心相对次序 = 增益/减益先于道具、道具先于战斗;段序结构冻结(改须走 GDD 修订,不属于调参);窗口每次选择是回放输入的一部分;段序常量存 SettlementTable `segment_order`,加载期恒等断言。 +- **规则 16**:结算用到的全部数值(当量、clamp 界、乘数、段序常量)都在 SettlementTable,B1 代码零数值字面量,调数值只改表不动代码;战斗开始 B1 落下热重载闸门、战斗结束抬起(B1 是唯一调用方),战斗中途的热重载请求被闸门拒绝。 + +设计意图:编排唯一 + 触发只读相位 + 段序冻结,是可回放性与跨系统归因的结构前提。 + +## 授予与高光 + +### 规则 + +- **规则 5**:授予段把挖掘期缓冲的事实分三批兑换,每格受理、每雷转化、每旗判定恰好一次:①挖格批——踩雷格逐颗转化为敌方伏击强化,带奖励资格的安全格按类别分流兑付;②旗判定批——判中 → 敌方削弱当轮生效(每颗雷资格只兑一次),判空 → 零兑付只记"标旗未中"归因行;③清图批——清图奖励恰好发一次,全板零误标且有标中 → 追加第二份同身份兑付。兑付数值读 D2 表值、无新公式。 +- **规则 6**:挖开雷格时 B1 向 B2 请求一次伏击授予计划 `ambush_grant_plan(mines_r, cap)`,恰一次调用;granted = min(mines_r, cap),超出的雷不生效只记"溢出未生效"行(含来源);伏击聚成一个池,同一结算多个敌人攻击读同一池值——共享、不均分也不消耗;伏击只活一轮,轮末随衰减清零。 +- **规则 7**:道具使用窗口是全流程唯一玩家输入点;窗口清单 = B4 库存投影 + B1 逐件可用性守卫预判;玩家至多选一件或跳过,空库存可整体不弹窗;完整选择序列(含跳过)记入回放;一次提交多件 → 只受理第一件、其余退回并记编排违例诊断。 +- **规则 13**:授予段埋两个强制高光检查点(见下表),条件满足必须兑现;呈现分工 = 事件型高光(完美排雷)发独立高光行,属性型强调(标中旗、清图奖励)用行字段标记。 + +| 窗口里看到 | 能否选用 | +|---|---| +| 伤害类道具 | 恒可选(结构前提 = 窗口只在仍有存活敌的轮打开) | +| 治疗包 | 满血禁选;未满血可选 | +| 空库存 | 可整体不弹窗,按跳过记 | + +### 规则 5 实现参照——三批转化 + +| 批 | 输入 | 转化 | 产出 | +|---|---|---|---| +| cell_revealed | `kind=mine` | 调 `ambush_grant_plan(mines_r, cap)`,事件规范序逐颗 `apply_effect(ambush, source={mine_hit, origin_cell, round_index})` | 伏击池 L;溢出颗产"溢出未生效"行(含来源) | +| cell_revealed | `kind=safe ∧ reward_eligible` | 按 category 分流(见下方奖励分流表) | 每格受理恰一次(B4 幂等仅兜底) | +| flag_judged | `hit=true` 且该雷实例资格未消费 | `apply_effect(weaken, source={flag_hit, origin_cell, round_index})` + 资格消费入集合 | 标中旗兑付行(highlight=`flag_hit`) | +| flag_judged | `hit=true` 且资格已消费 | 去重跳过(资格以雷格实例为键、跨轮累积;换图后新实例自然重置) | 零新增行(+debug 诊断可选) | +| flag_judged | `hit=false` | 零兑付;每雷实例一次去重(A2 逐轮重报零新增行,防惩罚轰炸) | "标旗未中"零值行(source=`flag_miss`、highlight=`perfect_broken`) | +| cleared 轮 | — | `grant_clear()` 恰一次;完美板(零误标 ∧ ≥1 标中)追加第二份同身份兑付(走既有授予通道) | 清图奖励行;完美高光行 | + +### 规则 5 实现参照——奖励分流 + +| category | 兑付方式 | 拒绝语义 | +|---|---|---| +| buff | `apply_effect`(入池,结算段生效) | 满层 → "奖励未生效"行(可聚合计数),无折算 | +| instant_aoe | 全体存活敌各 `power` 点(逐个 apply_damage、受击即死) | 当场兑付,不进库存不出窗口 | +| instant_heal | 主角回复 `power`(clamp) | 满血 → "奖励未生效"行零兑付 | +| damage_item / heal_pack | `stock()` 入关卡域库存 | 窗口选用时兑付 | + +- `reward_eligible=false`(initial 通道)格不入奖励库——B1 判定只读 reward_eligible、`channel` 字段零特判;连锁批量授予溢出 = 显式接受(无转换补偿)。 +- 标错惩罚未上线——标旗未中行是归因痕迹非惩罚;预留语义走接口漂移补(A2 实测后)。 + +### 规则 13 实现参照——高光兑现节点(强制检查点) + +| 节点 | 判定 | 兑付 | 呈现 | +|---|---|---|---| +| #2 完美排雷(perfect_sweep) | 板级谓词:本板自 install 起全部 flag_judged 中 hit=false 计数 = 0 ∧ hit=true ≥ 1(雾内插旗计入误标、零特判;计数随 install 重置) | 清图奖励身份第二份兑付:道具类 → `stock(同源身份快照)` 第二次;buff 类 → `apply_effect(同身份)` 第二次(满层拒绝照旧);`grant_clear` 本身仍恰一次;当量 = power × P_perfect ÷ 100(载体域 {100, 200}) | 独立高光行,value = 实际有效兑付(首份 + 第二份被接受部分)、禁记名义值;首份即零兑付 → 高光行不发(完美谓词仍成立) | +| #3 标中旗高光 | hit=true 且资格未消费(授予段判定) | weaken 当轮即兑付(dur=1 本就当轮生效)——chord 缺席的节拍缺失以"当轮兑付 + 明细强调"组合补偿 | 兑付行 highlight=`flag_hit` 字段标记;深度形态(即时 HUD 动画)归 D1/垂直切片 | + +设计意图:每格事实恰一次兑付 + 双高光强制检查点,是"扫雷的因"在战斗里可见可归因的兑付面。 + +## 合成、道具与战斗 + +### 规则 + +- **规则 8**:增益/减益段把主角与敌人身上活跃效果一次性合成——从 B3 读两侧活跃效果,按 SettlementTable 语义映射换算成各自修正池并池内加算,供道具段与战斗段使用;每条效果按授予流水逐条记明细行并标注来源授予;敌方侧减益作用于全体存活敌人(side-wide)。 +- **规则 9**:道具段执行窗口选中件:伤害道具按表恒定值打默认目标(顺位最前存活敌人),不吃任何池修正;治疗包按表值回复、超出上限 clamp(归 B1 执行);满血使用治疗包 → 拒绝、不消耗;施加时已无存活目标 → 照常消耗、记"已消耗未生效"行(存活判定读 B2 存活集)。 +- **规则 10**:战斗段主角先攻击一次(D_hero 三分量——F3⓪/F6),再按行序逐个存活敌人行动:每个敌人依次走 query_attack → apply_damage → advance 三步、恰好推进一次,主角不在推进范围;已死敌人不再行动;主角中途倒下 → 剩余敌人全部停手、直接跳到判定段;伏击池共享读值、多敌攻击互不消耗。 +- **规则 11**:衰减段调用一次 `decay_round()`,把到期临时效果移除并记衰减行;每轮恒定恰好一次,末轮也照跑——"先兑付清图奖励、后衰减"的时序契约由段序保证;容器已空时衰减返回零属合法结果(零明细行)。 +- **规则 12**:判定段防御性顺序——先判主角是否倒下(倒下即失败),再判存活敌人是否清空(清空即胜利),都不满足回发"继续"进下一轮;判定结果写成终局判定行(同时是胜负画面的数据锚)。 + +设计意图:先合成后动刀、主角先手、hero 先判——三步防御性次序让任何结局都可归因。 + +### F2 percent→乘数换算与合成地板(B3 义务③收口) + +**结论**:池内加算 → 每池独立 clamp [−100, +100] → 池间乘算 → floor 单一取整(结构性非 knob);免伤轮(D'=0)是合法设计态——不调 apply_damage、产零值伤害行;Output Range D' ∈ [0, ∞) 整数、零负值分支,MVP 实际域 [0, 20](B2 参考带)。 + +``` +结算语义映射(effect_pool_map,effect_id 为键): + attack_up → hero_offense(只乘主角先手攻击基础分量)· defense_up → hero_defense + weaken → enemy_output · ambush → flat 附加(后置加法、不入乘区) +敌→主单伤害实例(每结算、逐攻击行为): + P_pool = Σ(同池实例 L×m);P_pool ← clamp(P_pool, −100, +100);M_pool = 1 + P_pool ÷ 100(域 [0, 2]) + D' = floor( D_raw × M_hero_defense × M_enemy_output ) + (整数域 = D_raw × (100+P_h) × (100+P_e) ÷ 10000 向下取整) + D_raw = query_attack(u, L, m_amb).total(charge 拍 = 0;乘区作用于含伏击分量的全裸伤害) +``` + +| 例 | 合成值 | 池 clamp 后 | 乘数 | 读法 | +|---|---|---|---|---| +| weaken AP 界(L=8 × m_w,修订后 65) | −520% | −100 | **0** | 该结算全体存活敌输出归零;第 3–8 层边际瞬时当量 = 0(地板吸收)——明细行如实显示合成值与终值两端 | +| weaken 表界(L=16 × m_w) | −1040% | −100 | **0** | 同上——地板规则不区分越域来源,越域深度仅入合成值显示与诊断计数 | +| defense_up 叠满(L=2 × m_d,现行 −30) | −60% | −60(域内,未触地板) | **0.4** | 该结算 hero 承伤按 0.4× 结算——免伤轮不再由 defense_up 单侧可达 | + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 裸伤害 | D_raw | int | [0, ∞) | B2 F2 total 分量(charge 拍恒 0) | +| 池合成值 | P_pool | int(pp) | 越域可达 ±1040(修订后);clamp 后 [−100, +100] | B3 原样输出 → B1 clamp 收口 | +| 池乘数 | M_pool | 有理数 | [0, 2] | 1 + P_pool/100 | +| 终值 | D' | int | [0, D_raw × 4](结构上界,MVP 不可达) | floor 取整 | + +归口:B3 管 L×m 原样输出不 clamp;B1 管池归属映射、clamp、乘区次序、取整方向;C3 管池值强度评估。 + +### F3 伤害合成、clamp/溢杀与死亡判定(B2 义务③成文) + +**结论**:三分支全序(主角先手 → 逐敌循环 → 道具段)+ 负值分支禁令(零负伤害/负治疗/负当量)+ 死亡判定:Dead ⇔ hp=0(无负 hp 中间态)、受击即死、hero 死亡战斗段短路、判定段 hero 先判;Output Range 双向伤害 D ∈ [0, ∞)、hp ∈ [0, max_hp]、零转移零存续。 + +``` +⓪ 主→敌(战斗段段首,主角先手攻击——基础攻击恒存在〔HeroTable.atk 域 [1,999]、0 加载拒绝〕): + 目标 = B2 default_target()(固定顺位最前存活敌——与道具段同源) + D_hero_raw = HeroTable.atk ×(1 + attack_up 载荷) + F6 充能分量 + D_hero = 三分量逐项 floor 后相加(基础分量经 hero_offense 池 clamp 地板——F2);D_hero = 0 → 零值伤害行 + D_hero > 0 → apply_damage(target, D_hero);hp 归 0 → Dead(受击即死) + 明细行 source_class = hero_attack;存活敌清空 → 敌方循环空转(零行动行),径入衰减段 +① 敌→主(逐存活敌,行主序;调用序钉死 query_attack → apply_damage → advance): + D_raw = query_attack(u, L, m_amb).total(L = 本结算伏击池读值;charge 拍 = 0) + D' = F2 换算;D' = 0 → 不调 apply_damage,产零值伤害行(分量二元组 + 终值 0) + D' > 0 → apply_damage(hero, D');hp' = max(hp − D', 0) + 溢杀部分(D' − 受击前 hp)丢弃——无存续、无转移、无负 hp 态 + hero hp' = 0 → 战斗段立即短路(剩余敌不作用、不产行动明细行) +② 主→敌(道具段,窗口选定件——至多一件,单件制): + D = power(恒定表值,不吃任何 percent 修正;零乘区);目标 = B2 default_target()(死亡后顺延) + apply_damage(u, power);hp 归 0 → Dead;溢杀丢弃、不转移下一敌 + 施加时已无存活敌 → 照常消耗(Used 不可逆)+ "已消耗未生效"明细行 +③ 负值分支禁令:任何公式分支不得产生负伤害/负治疗/负当量——percent 池地板 [−100, +100]、 + flat 值 ≥ 0、hp ∈ [0, max_hp]、治疗 clamp 上界 max_hp(hp' = min(hp + power, max_hp))、 + apply_damage 入参 n ≤ 0 = B2 API 拒绝 + 诊断 +④ 死亡判定数值边界:Dead ⇔ hp = 0(无负 hp 中间态);时机 = 受击即时(敌)/ 战斗段短路(hero); + 判定段 = 防御性优先级 hero 先判(双死结构不可达的纵深防御) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 裸伤害 | D_raw | int | [0, ∞) | B2 F2 total;分量二元组 {baseline, attach} 进伤害行(P2 归因) | +| 乘区终值 | D' | int | [0, ∞) | F2 输出;floor 取整 | +| 目标 hp | hp | int | [0, max_hp] | B2 单位状态;递减与 floor 由 B2 承载 | +| 道具威力 | power | int | [1, 999] | RewardTable 授予时快照(恒定) | +| 溢杀量 | — | int | [0, D') | 丢弃(明细行 value = D'、击杀标记布尔——不记溢出量) | + +归口:B2 管递减/floor 0/Dead 翻转/指针冻结;B1 管本式全序、调用序、零值行、溢杀丢弃语义、死亡判定时机。 + +### F6 伤害A/B 充能与护甲克制结算(机制与结算归 B1,四字段数值投值归 C3) + +**结论**:伤害A/B 为挖掘期充能、战斗段主角先手攻击一次性消费的 flat 附加分量——池内加算、不吃任何 percent 修正、逐分量查 `armor_effectiveness` 甲效表后 floor 相加;充能池单轮生命周期(挖掘期累积 → 本轮战斗段消费 → 轮末归零,跨轮零存量);Output Range D_hero ∈ [0, ∞) 整数、零负值分支。 + +``` +累积(B1 facts 缓冲承载——零新事件、零特判): + A 池 = min(本轮 cell_revealed{kind=safe} 事件数 × damage_a_per_cell, damage_a_cap) + (initial/chain/dig 全通道同待遇;cap=0 = 不封顶结构位;damage_a_per_cell=0 = 机制关闭合法极端) + B 池 = 本轮 flag_judged{hit=true ∧ 资格未消费} 事件数 × damage_b_per_flag + (与 weaken 兑付同源判定、每雷实例一次去重;无 cap 字段——标中旗吞吐由 AP 预算天然限界) +消费(战斗段主角先手攻击,单结算一次性清池): + base = floor( atk × (1 + clamp(P_hero_offense, −100, +100)/100) ) + dmg_a = floor( A 池 × 甲效[dmg_a][target.armor_type] ÷ 100 ) + dmg_b = floor( B 池 × 甲效[dmg_b][target.armor_type] ÷ 100 ) + D_hero = base + dmg_a + dmg_b + 甲效表键集 {base, dmg_a, dmg_b} × {none, a, b} 结构冻结;armor_type 映射归 EnemyTable + 轮末:A/B 池清零(与 decay 段同拍——跨轮零存量) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| A 池 / B 池 | — | int | [0, damage_a_cap] / [0, ∞) | 本轮充能池读值(B1 自持,公开可读——P2 还原面) | +| 伤害A 三参数 | damage_a_per_cell / damage_a_cap | int ≥ 0 | 默认 1 / 8(provisional 归 C3) | SettlementTable knob;cap=0 = 不封顶 | +| 伤害B 参数 | damage_b_per_flag | int ≥ 0 | 默认 2(provisional 归 C3) | SettlementTable knob | +| 甲效值 | e | int percent | {50, 100, 150}(现行默认矩阵取值域) | armor_effectiveness 查表;base 行恒 100 | + +- cap 截断腿:连锁轮 A 池溢出零充能、无"溢出未生效"行(充能非奖励、无兑付语义),截断仅入诊断计数。 +- P2 归因:明细行 = hero 伤害行分量二元组 {baseline = 基础分量, attach = dmg_a + dmg_b 合计}(A/B 拆分经充能池公开读值可还原);A 池来源 = 开格、B 池来源 = 首次标中旗(与 weaken 同判定批)。 + +## 当量换算与条款闭合 + +### 术语表 + +| 术语 | 白话定义 | +|---|---| +| 乘区 | 伤害公式里相乘的各段修正(本作两段:主角减伤池 × 敌方削弱池) | +| 池 | 同一侧百分比修正的加总桶 | +| 地板 | 数值不许越过的下界——本节地板 = 池最低 −100 百分点,即乘数最低 0 | +| 免伤轮 | 主角该结算受到的伤害为 0 的结算 | +| 充能池 | 伤害A/B 的单轮加法分量来源——开格/首次标中雷按表值累积、战斗段主角先手攻击一次性消费、轮末清零 | +| 溢杀 | 一刀超出目标剩余 HP 的部分——直接丢弃,不转移到下一个敌人 | +| 当量 | 统一价值单位,1 当量 = 1 点主角 HP 的期望净变化 | +| 瞬时口径 | 只数一个结算里兑现的价值(裁决口径) | +| 全寿命口径 | 乘上它存活的结算次数(仅作派生量,不作锚) | +| 残余盲猜端 | 图上推理不出、只能赌的那一小块 | +| 完美排雷(P_perfect) | 一张板上没插错一面旗、且至少标中一面时,清图奖励翻倍的乘数 | +| 挖穿计价 | 把残余盲猜区全部安全格挖完时平均会踩到多少雷——按雷与安全格在池中均匀随机交错计算,不是每挖一格按初始比例独立重赌 | + +### F0 当量归一口径(weaken 锚裁决) + +- **结论**:采用"每结算瞬时当量"口径(每 AP 总暴露分支排除);m_w 值域 [60, 85]、默认 −65;地板交叉层数 = ⌈100/m_w⌉ = 2(值域全域恒 2);B4 参照零漂移,B3 仅行值域修订(registry 回写)。 +- 连带:F2 越域界 = AP 界 8×m_w / 表界 16×m_w(合成值 B3 原样输出、B1 池 clamp 收口);单结算 weaken 池 HP 当量上界 = min(L×m_w, 100)/100 × D_inst(该结算实际裸伤害敞口)。 +- 修订效果量(provisional @ D_ref=12):腿一 7.8 vs 6.0 HP/结算(attack_up 当量,+30%,严格大于 ✓);腿二 7.8 vs E_dig 4.5 HP(+73% ✓);池内字面最低档读法(defense_up 3.6)裕度 2.17×。 + +### F1 效果档位当量对照表(A3 条款①) + +**结论**:单一线性换算全四源共用(分源改率 = 条款①契约违约);E(·) 单位 = 主角 HP 点;Output Range E ∈ [0, +∞)、全表零负值(percent 池饱和后边际 E = 0);weaken 修订后每旗 7.8 HP > defense_up 6.0(条款②腿一通过)。 + +``` +flat / 道具类(刀、治疗包、ambush): E = 值本身(ambush 按 k 个攻击拍线性展开) +percent 类(每结算、每层): E_inst = |L × m| ÷ 100 × D_exp(对照表取参考敞口 D_ref) +完美乘数(显式 ÷100,运行时真实双份载体): E_clear(perfect) = power × P_perfect ÷ 100 +时长(显式乘子,非分源改率): E_life = E_inst × 兑付结算数 +D_ref = SettlementTable.equivalence_ref_damage(单行全局 knob,provisional 默认 12) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 参考敞口 | D_ref | int | [1, 999] | SettlementTable 配置值(D2 回写义务,本 GDD owner);provisional 12;变更触发全对照表 + 条款③⑤重跑 | +| 层数 | L | int | [1, max_stack] | B3 实例字段(授予快照) | +| 行值 | m | int | percent ∈ [−100,100]∧≠0 / flat ≥ 1 | StatusEffectTable 行值快照 | +| 兑付结算数 | — | int | [1, duration_settlements] | 剩余结算次数轨迹;对照表按满寿命取 | +| 道具威力 | power | int | [1, 999] | RewardTable 行值快照(恒定表值——已锁) | +| 攻击拍数 | k | int | [0, unit_count] | 该结算存活敌中 attack/heavy 拍单位数(B2 F4 供给,确定性可算) | +| 完美乘数 | P_perfect | percent | **{100, 200}**(域外加载致命) | SettlementTable 配置值;provisional 200(换算入账恒 ÷100) | + +| 行 | 源(source_class) | 类别 | 换算式 | E_inst(每结算/层) | 全寿命 E | 注 | +|---|---|---|---|---|---|---| +| defense_up_basic | safe_cell | buff → defense_up | E_inst = 0.30 × D_ref;E_life = ×duration 1 | **3.6** | **3.6** | 池内字面最低档 buff 当量(减伤墙批 −30 现行值) | +| attack_up_basic | safe_cell | buff → hero_offense | E_inst = 0.50 × D_ref | **6.0** | 6.0 | **锚对照实物(条款②参照对象——registry 回迁口径)**;消费面 = 主角战斗段先手攻击基础分量(F3⓪) | +| spark_aoe | safe_cell | instant_aoe | E = power | —(当场兑付) | 4 | 全体存活敌各 4 点 | +| heal_minor | safe_cell | instant_heal | E = min(power, max_hp − hp) | — | ≤ 4 | clamp 后真实当量;面值 4 = 上界读法 | +| knife_t1 | 注入池(active_inject) | damage_item | E = power | —(库存轨迹量) | 8 | 供给控制器注入——非单格期望摊入 | +| knife_adv | 注入池(active_inject) | damage_item | E = power | — | 16 | 雾档升级刀 | +| heal_pack | 注入池(active_inject) | heal_pack | E = min(power, max_hp − hp) | — | ≤ 12 | 注入池稀缺强治疗 | +| clear_blast | clear_default | instant_aoe | E = power ×(perfect 板 ? P_perfect ÷ 100 : 1) | —(当场兑付) | 40(完美板 80——**真实双份**) | 条款④⑤载体(F5 消费) | +| weaken | flag_hit | debuff → enemy_output | E_inst = m_w/100 × D_ref | 修订后 65 → **7.8** | 7.8(dur=1) | 修订值域/默认见 F0 | +| ambush | mine_hit | 敌方增益(flat) | E = m_amb × k | k=1 → 3;满配 k=3 → 9 | 同左(dur=1 单结算自然边界) | 量纲已定稿(registry `ambush_unit_definition`) | +| 伤害A 充能 | safe_cell(开格——SettlementTable knob,非 RewardTable 行) | flat 附加分量 | E = min(开格数 × damage_a_per_cell, damage_a_cap) × 甲效/100 | 每轮 ≤ 8 × 甲效/100(对甲A 12 / 无甲 8 / 对甲B 4) | 同左(单轮充能单轮消费) | 充能规格见 F6;逐分量查表后 floor | +| 伤害B 充能 | flag_hit(首次标中——与 weaken 同判定批) | flat 附加分量 | E = 首次标中雷数 × damage_b_per_flag × 甲效/100 | 每雷 2 × 甲效/100(对甲B 3 / 无甲 2 / 对甲A 1) | 同左 | 同 F6;"标中旗双收益线"归 W12③ 对账清单 | + +- 锚两腿:weaken 修订后每旗 7.8 > attack_up 6.0(严格大于 ✓,裕度 +30%);7.8 ≥ E_dig 4.5 ✓;池内字面最低档 defense_up_basic 3.6 读法裕度 2.17×——三读法均通过。 +- 结构性边界:腿二可满足上界 = m_w=100 ↔ 相对每结算百分点档 ≈1.96:1(percent 瞬时档被 schema 域封顶);处置 = C3 板形参数与 m_w 联动约束,非数值修订可解。 +- 归口:B1 管本表与换算维护、D_ref 与 P_perfect 承载;B4 管行值/权重/tier/C 供给;C3 管全部 provisional 数值;B3 管 L×m 合成原样输出。 + +### F4 条款③挖掘风险不等式闭合(B1 单一闭合 owner;B2 义务④兑现) + +**结论**:现行值链(duration=1 × m_d=−30 × C3 v3 曲线 d0.25/t0.88)下两参考板形破线(锚行口径 L1 ≈0.48× / L2 ≈0.47×;期望口径 0.60×/0.58× 同向);满配 unit_count=3 深度破线(≈0.16×);旧"通过"回执退役为历史记录;破线升格 C3 默认曲线级约束;actuals p10 交付后强制重跑。 + +``` +配对口径:决策单位 = 单格挖掘;评估点 = 残余盲猜端最坏点(信息端平凡通过,一并登记) +主式:P(安全) × V_safe > P(雷) × V_mine +适用域守卫:(1 − ι) × |S| < 1(残余盲猜集为空)⇒ 主式不评估、信息端平凡腿接管(P(雷) 式在空池退化,禁代入) +P(雷) = |M| ÷ (|M| + (1 − ι) × |S|) (残余盲猜端上界) +P(安全) = 1 − P(雷) +V_safe = E_life(最低档安全格 buff) = (|m_d|/100) × D_ref × duration_d(现行值链:0.30 × 12 × 1 = 3.6) +V_mine = k_max × m_amb = unit_count × m_amb(默认行值 1×3 = 3;cap 溢出使第 cap+1 颗雷边际当量 = 0) +信息端检查(平凡腿):P(安全) = 1 → V_safe > 0 恒成立 +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 实际雷数 | \|M\| | int | [1, W×H−9] | A1 产物指标/行参数推导 | +| 安全格数 | \|S\| | int | ≥ min_safe_count | 同上 | +| 可推理率 | ι | float | [0.85, 1] | **actuals p10(A1 F4 报告 C)**——未交付前以行 target 代 = 判式面的保守代理;重跑义务(对账清单 #6) | +| 防御行值/时长 | m_d / duration_d | int / int | −30 / 1(provisional——减伤墙批/两级奖励批现行值) | StatusEffectTable defense_up 行 | +| 参考敞口 | D_ref | int | [1, 999] | provisional 12 → V_safe = 3.6 | +| 伏击量纲/单位数 | m_amb / unit_count | int | 3(provisional)/ [1,3] | B2 行值 | + +| 板形 | ι(target 代 p10) | P(雷) | 左臂 = P(安全)×3.6 | 右臂 = P(雷)×3 | 裕度 | 判定 | +|---|---|---|---|---|---|---| +| L1(v3:10×10 d0.25,\|M\|=23,\|S\|=77) | 0.88 | 23/32.24 ≈ 0.713 | 1.03 | 2.14 | **≈0.48×** | **✗ 破线** | +| L2 腿(v3:12×12 d0.25,\|M\|=34,\|S\|=110) | 0.88 | 34/47.2 ≈ 0.720 | 1.01 | 2.16 | **≈0.47×** | **✗ 破线** | +| 满配语境(unit_count=3,V_mine=9,L1 板) | 0.88 | 0.713 | 1.03 | 6.42 | **≈0.16×** | **✗ 破线(深度)** | + +- 结论①:现行值链下两参考板形破线——旧"通过"回执(≈2.0×/2.6×,系 duration=2 / m_d=−50 / v1 曲线旧输入产物)失效;信息端平凡腿不变。 +- 结论②:满配语境 unit_count=3 深度破线(≈0.16×)——原 C3 调参约束维持并升格(见④)。 +- 结论③(敏感性):buff 饱和的结构性零兑付使 V_safe 名义值高估——按 0.6 折减(provisional)复算 L1:左 0.62 vs 右 2.14 → 裕度 ≈0.29×(更薄);折减系数与饱和率随 playtest 重校。 +- 结论④(破线升格——曲线级约束):约束升格为**默认曲线的 V_safe 侧杠杆重议**——候选杠杆 = m_d 回调 / duration 恢复跨轮 / D_ref 联动上调 / 板形侧密度×ι 联动对 / m_amb 下调;全部处"禁独立调参"联动网络(m_w↔D_ref↔m_d↔duration_d),修正方向须 C3 提案 + B1 闭合对账共裁。 + +### F5 条款⑤清图 vs 弃图支配性闭合(B1 单一闭合 owner) + +**结论**:条款⑤未闭合——挖穿残余池计价下主判式两态为负(L1 仅完美双份翻正 +11.3,非完美态被支配),实现率折减与多敌语境更差;"推到底"的支配性只在残余集小时自然成立;判据函数角色 = C3/B4 调参约束生成器,AC-40 断言输出与登记约束包一致。 + +``` +主判式(决策点 = "确定格挖完";策略对 = 推到底清图 vs 停手攒 RAP 刷新): + Δ_clear = [R_res + C_eff] − [X_res + Δr × s_ref] ≥ 0 + n_s = (1 − ι) × |S|(适用域守卫:n_s < 1 ⇒ 主判式不评估、信息端平凡腿接管) + E[踩雷] = |M| × n_s ÷ (n_s + 1)(均匀交错模型;n_s 增大时趋近 |M|) + R_res = n_s × E_dig + C_eff = C × (perfect ? P_perfect ÷ 100 : 1)(C = 清图奖励当量 = power 恒等〔clear_blast = 40〕) + X_res = E[踩雷] × V_mine(V_mine 同 F4) + Δr = ⌈(n_s + E[踩雷]) ÷ AP_r⌉(安全格 + 踩雷格均耗 AP;雾态增量叠算见注) + s_ref = D_ref(同一 D_ref,单一换算) + E_dig = (20×6.0 + 16×3.6 + 16×4 + 15×4) ÷ 67 ≈ 4.5 HP(格子池加权;刀/强治疗已移出格子池入注入池不摊入) +显式假设登记:停手侧 RAP 机会收益按零计价(零价假设对"推到底"偏利一分;本假设下 verdict 已为负) +保守地板式:C_eff ≥ X_res + Δr × s_ref +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 残余盲猜安全格 | n_s | float | [0, \|S\|] | (1−ι)×\|S\|;**域守卫:n_s < 1 ⇒ 主判式不评估**(零赌博板形不误判) | +| 挖穿期望踩雷 | E[踩雷] | float | [0, \|M\|] | 均匀交错模型 \|M\|×n_s/(n_s+1) | +| 清图奖励当量 | C | int | ≥ 1(A3 条款④) | B4 供给(clear_blast power=40,provisional) | +| 完美乘数 | P_perfect | percent | **{100, 200}** | SettlementTable 配置值;provisional 200(换算入账恒 ÷100) | +| 单格奖励期望 | E_dig | float | (0, ∞) | 格子池加权推导(现行 4.5;评审口径带 ≈4.3–5.1;实现率折减敏感性同 F4③) | +| 轮行动点 | AP_r | int | [1, 32] | A3 ActionEconomyTable(默认 8) | +| 雾负担比 | fog_burden_ratio | float | [0, 1] | A1 产物指标(付费格分母口径)——雾态 Δr 修正输入 | + +| 板形 | n_s | E[踩雷] | X_res | Δr(代价) | R_res | Δ_clear(C=40 / 完美 80) | 判定 | +|---|---|---|---|---|---|---|---| +| L1 无雾态(v3:10×10 d0.25,ι=0.88,\|M\|=23,\|S\|=77) | 9.24 | 20.8 | 62.3 | 4(48) | 41.6 | **−28.7 / +11.3** | ✗ / ✓ | +| L2 腿(v3:12×12 d0.25,ι=0.88,\|M\|=34,\|S\|=110) | 13.2 | 31.6 | 94.8 | 6(72) | 59.4 | **−67.4 / −27.4** | ✗ / ✗ | +| L1 × 满配语境(unit_count=3,V_mine=9) | 9.24 | 20.8 | 186.8 | 4(48) | 41.6 | **−153.2 / −113.2** | ✗ / ✗ | +| L2 腿 × 满配语境 | 13.2 | 31.6 | 284.5 | 6(72) | 59.4 | **−257.1 / −217.1** | ✗ / ✗ | + +- 实现率折减口径(E_dig′ = 2.70 provisional):L1 = −45.3 / −5.3;L2 = −91.2 / −51.2——同向更差(完美翻正腿亦被折减吞没)。 +- 雾态增量:链不可达雾格全价 AP 抬高轮数——Δr 增量 = \|U\| × dig_ap_cost ÷ AP_r(v3 曲线复算 (29.99 + 4) ÷ 8 = 4.25 → 上取整 5,较无雾基线 Δr=4 **+1 轮**);actuals fog_burden_ratio p50/p90 交付后重跑(对账清单 #6)。 + +| 项 | 约束 | 裁决方 | +|---|---|---| +| ① C 量级上调整向 | 保守地板需求 L1 ≥ 110.3 / L2 ≥ 166.8(v3 曲线重推;现值 C=40 / 完美 80——缺口 **70.3 / 126.8**;原 46.1/91.8 需求系 v1 曲线旧值链产物,退役为历史记录);任何上调与护栏 B 口径联动复核 | B4/C3 | +| ② 或裁"清图非严格支配"为设计态 | 与 A4 定价硬约束②同向;如裁设计态,A3 条款⑤注记的 P1 对冲弱化须同步声明,弃图率雾板分层观测加权监控 | C3(①②二选一) | +| ③ 多敌联动 | 满配语境深度为负与 F4 破线同向——unit_count > 1 行的放行判据 = 条款③⑤在全部登记行组合下同时成立(判据自动收紧至曲线级重议——与 F4 结论④并案) | C3(双条款并案) | +| C3 裁决回执 | **② 采信(非支配设计态)+ 计价基元换位重推**:clear_default 槽 clear_knife→clear_blast(instant_aoe 当场兑付全体存活敌,C = power = 40)——缺口重推 = 46.1−40 = **6.1**(L1)/ 91.8−40 = **51.8**(L2,v1 曲线口径);C 量级不上调 ✓;P1 对冲弱化同步声明已落 C3 OQ4、弃图率加权监控持续;actuals 交付后按 clear_blast 语义强制重跑(AC-40 同批口径,registry clause5_dominance.c3_ruling) | C3(已裁——①②二选一落定) | +| B1 重算批重推回执 | v3 曲线 × E_dig=4.5 重推:主判式 L1 −28.7/+11.3(完美翻正)、L2 −67.4/−27.4、满配 −153.2/−113.2 与 −257.1/−217.1;保守地板需求 110.3/166.8(缺口 70.3/126.8);**裁决② 维持**(非完美态负、完美态 L1 翻正、L2 全负——真人零弃图初裁 OQ4 不受读数漂移影响);护栏 A/B 读数挂旧值链——W2 复核随 C3 收口批三选一落档 | B1(重算回执登记) | + +| 护栏 | 判据 | 读数 | 突破条件 | +|---|---|---|---| +| A 雾板净当量优势带(B4 pricing_constraints③ 的 B1 判据) | 每链不可达雾格净优势 = (E_fog − E_dig) − s_ref ÷ AP_r | 登记读数 (11.1 − 7.2) − 1.5 = **+2.4 ∈ (0, 3]**(v1 曲线/旧格子池口径);重算批方向性复核:E_dig 修正 4.5 + 雾池构成变更 → 读数 ≈ **−1.1 < 0,越下界**(雾纯税化风险);E_fog 正式重推随 W2 批 | E_fog > 11.7 → C3 雾池联动禁区,禁独立调参(判据式不变——阈值随重推复核) | +| B 防钓鱼——退化板刷清图奖励 | 上界判据 = ⌈(\|S\|_min − \|R₀\|_max) ÷ AP_r⌉ × s_ref ÷ 2 ≈ ⌈(50−20)/8⌉ × 12 ÷ 2 = **24**(D_ref 口径——与敌 DPS 无关,读数不受 v3 换锚影响) | 登记双读数(v1 曲线口径):D_ref 口径上界 24 → C_eff = 16 ≤ 24 ✓ / DPS 严格读数上界 ≈8 → C_eff = 16 越(clear_knife C=8/完美 16 口径);**W2 复核事项**:C 提至 clear_blast 40/完美 80 后对两上界越线 1.7–10×——三选一裁决(上界改判/接受+强化监控/回调 C)随 C3 收口批落档 | 结构承载 = A1 min_safe_count + 布雷数下限 + A2 R₀≠S 三闸(B1 侧判据为对账复核,非运行期校验) | + +- 护栏 B 输入登记:\|S\|_min = min_safe_count 是 A1 行级 knob,调参按当期行集重算、禁写死 50;\|R₀\|_max = 20 系参考带经验值、无登记供给方 → 登记 A1 产出义务 R₀ 规模分布上界(F4 报告 C 扩列,缺口预检 9);DPS 严格读数参照系 v1 曲线 atk 值,v3 atk 上探后正式重推随 W2 批。 +- P_perfect 默认值:×2(SettlementTable.p_perfect = 200,provisional,调参所有权 C3;运行时载体域 {100, 200})——现参数下唯一使 L1 翻正的杠杆(+8.0);非完美态仍负 → C 量级上调约束不因 P_perfect 消解;上界受护栏 B 约束(完美加成不得触碰钓鱼线)。 + +设计意图:两个不等式判据函数是 C3 调参的约束生成器——把"挖下去值得/推到底不吃亏"从口号变成每笔调参必须重跑的对账。 + +## 明细流水协议 + +### 规则 + +- **规则 14**:结算过程中每一件事写一行明细流水;行型封闭为 13 类、不允许私自增加;每行带公共字段,各行型再补特有字段;这份流水是界面呈现、回放对账、数据导出共读的唯一数据源契约;任何一行找不到来源 = 断言失败(宁炸勿混)。 +- **规则 15**:结算完全可回放——同样的随机种子加同样的输入(事件序列 + 窗口选择序列),跑出的步骤流逐行相同;成立前提 = 零随机、确定性输入、数值全部来自表值快照、attempt 重试不额外取随机流号;录像回放、断言复测、切点续玩都建立在它之上(切点见 AC-34)。 + +### 明细行类型(封闭枚举,13 类) + +| 行类型 | 特有字段 | 产出点 | 说明 | +|---|---|---|---| +| 效果行 | `effect_id` `pool_value` | 增益/减益段 | value=该次授予贡献值;pool_value=池合成值(防 −130%×2 行误读为 −260%) | +| 伤害行 | `target` | 道具段 / 战斗段 | 分量二元组 + 减伤终值 + 击杀标记 | +| 治疗行 | — | 道具段 | clamp 后真实回复量 | +| 道具行 | `target` | 授予段 / 道具段 | 入库 / 生效 / 语境注记 | +| 奖励未生效行 | 聚合计数 | 授予段 | 满层拒绝、满血、雾板等合法例外 | +| 已消耗未生效行 | — | 道具段 | 施加时已无存活敌(Used 不可逆) | +| 使用被拒行 | — | 道具段 | 满血治疗包守卫 | +| 溢出未生效行 | — | 授予段 | 伏击/奖励族同名协议 | +| 标旗未中行 | highlight=`perfect_broken` | 授予段 | 零值归因行;每雷实例一次去重 | +| 蓄力行 | — | 战斗段 | charge 拍零作用 | +| 衰减行 | — | 衰减段 | 移除数 k(呈现聚合归 D1) | +| 判定行 | — | 判定段 | 胜负 / 继续 | +| 高光行 | — | 授予段(清图) | 完美排雷;value=实际有效兑付,禁记名义值 | + +公共字段(13 类全必有):`type` `source_class` `origin_cell`(板级行可空)`round_index` `display_name` `value` `highlight?`。断言:无来源行 = 失败(AC-42)。新增行类型/新字段均为输入的确定函数——回放逐行相等断言语义不受损。 + +### source_class(封闭枚举) + +| 值 | 来源 | +|---|---| +| safe_cell / flag_hit / mine_hit / clear_default | B3 既有四源 | +| 道具/判定扩展 | B4 交付语义 | +| enemy_action | 敌方行动行(伤害/蓄力) | +| flag_miss | 标旗未中零值行 | +| hero_attack | 主角先手攻击伤害行 | + +新增值 = 接口漂移,须同步 `source_label_map` 键集。 + +### highlight(可空枚举) + +| 值 | 含义 | +|---|---| +| `flag_hit` | 标中旗削弱兑付行强调 | +| `clear_reward_h2` | 清图奖励行强调 | +| `perfect_broken` | 本板完美谓词已破的归因锚 | + +新增值 = 接口漂移。 + +设计意图:13 类封闭行型 + 无来源即炸,让"合并到看不出来源"的呈现优化在数据层就过不了断言。 + +## 边界情况 + +| 条件 | 行为 | +|---|---| +| 末图清图即胜利(cleared 与 battle_over 同结算) | 授予段先兑付清图奖励(含完美判定)→ 窗口当轮可选用 → 道具段照常 → 战斗段空转(无行动行)→ 衰减照常执行 → 判胜 → battle_over 清理;未选用件随 clear_inventory 消亡 + 反馈行(依据:B4 E15) | +| 战斗段 hero hp=0 | 立即短路——剩余敌不作用、不产行动明细行,径入判定段判负;短路后衰减段照常执行(结算完整性优先)(依据:Core Rule 10) | +| 判定时 hero 与最后敌同时 hp=0 | 结构不可达(我方伤敌均先于敌方行动兑付);防御规则 = hero 先判 → 判负(纵深防御)(依据:Core Rule 12) | +| 减伤/削弱合成后 D'=0 | 不调 `apply_damage`(B2 对 n≤0 拒绝),改产零值伤害行(分量二元组 + 终值 0)——免伤与零输出必须可见(依据:负值分支禁令) | +| 伤害超过目标剩余 hp(溢杀) | 溢出丢弃——无存续、无转移、无负 hp 态;行 value=D'、击杀标记为布尔(不记溢出量)(依据:F3) | +| 敌为 charge 拍而本轮踩雷 | 伏击授予照常执行(授予段先于战斗段);query_attack 返回零作用 → 本结算伏击池全部蒸发(dur=1 轮末清零);战斗段零伤害;明细 = 授予行 + 蓄力行("这轮踩的雷白喂了"可对照)(依据:B2 EC8) | +| 全体存活敌同拍 charge | N 行蓄力行、零 apply_damage、hero 承伤 0——合法状态非错误(依据:B2 EC) | +| 伏击池 L=0 时敌攻击 | D_raw = 纯基线 atk(分量二元组 {atk, 0})——零伏击是 L=0 的自然读值非缺失分支(依据:B2 F2) | +| 同窗提交多件 | 仅首件受理生效,余件零调用 + 编排违例诊断;满血使用守卫保留为纵深防御(跨结算场景仍可达)(依据:Core Rule 7 / AC-16) | +| 治疗包 power 超过当前缺血 | hp' = min(hp + power, max_hp) 部分回复、不拒绝(满血 = 零收益拒、超量 = 部分收益——两分支语义互斥)(依据:F3) | +| 选定刀施加时已无存活敌 | 照常消耗(Used 不可逆)+ 逐件"已消耗未生效"明细行(零效果判定读 B2 存活集);单件制下不可达、逻辑保留为防御分支;战斗段照常空转推进(依据:B4 E10 / AC-20) | +| 窗口打开时库存为空 | 跳过恒合法;呈现面 D1 可整体不弹窗;结算段序零停顿继续(依据:B4 E8) | +| 挖掘期刷新 RAP 不足 | `request_refresh` 返回 false、状态不变 → `refresh_board` 原样上抛拒绝(刷新拒绝不是编排错误,是玩家决策的合法结果)(依据:A3 EC13) | +| 同轮多次刷新成功(cap 内) | 每次成功独立走 `refresh_board` 全序(retire → generate → install → roll_board 恰一次/图)——挖掘期当场连刷合法;每次 generate/roll_board 消费新 scope;第三次提交 → cap 拒绝(依据:A3 EC15) | +| stopped/ap_exhausted 轮 | 出口分发恒 same_board(本编排无刷新提交面);回发 new_board = B1 违约(绕过 RAP"支出唯一")→ fail-fast 上抛(依据:A3 Rule 11) | +| 换图生成产物带非法产物标记 | 编排不拒绝产物(玩家永不卡死),上抛玩家侧呈现请求(数据面 = 非法标记;提示/补偿/回退形态归 D1);A2 `install_board` 若拒绝(不变式违例)= 程序级错误 → fail-fast(非玩家域)(依据:A1 EC5) | +| battle_over 清理序列中某调用被拒 | debug 诊断 + 编排纪律修复;幂等守卫(各侧 false + 诊断)兜底,不中断清理序列剩余步骤(防半清理态跨战斗泄漏)(依据:编排纪律) | +| 战斗中表热重载请求 | `set_reload_gate(true)` 已置位 → D2 门拒绝返回 false(依据:D2 EC8) | +| 衰减段调用时容器已空 | `decay_round()` 返回 0——合法结果、零明细行或聚合零计数(依据:B3 EC13) | +| 清图槽身份为 buff 且授予时已满层 | 零兑付 + "奖励未生效"行(合法例外);完美板高光行降级不发(完美谓词仍成立,当量入账 = 实际兑付 0);若仅第二份被拒(首份已落地)→ 高光行照发、value = 首份当量 + "奖励未生效"行一条(依据:B4 E11 / Core Rule 13#2) | +| 完美排雷谓词在 RAP 刷新图上 | 刷新图不产生清图奖励(无兑付点)→ P_perfect 不触发;板级 flag 计数随每次 board install 重置(跨图不累积)(依据:Core Rule 13) | +| 板内既有标中又有误标 | 完美谓词不成立 → 正常兑付(无高光行)+ 本轮首个误标判定产"标旗未中"零值行(highlight=`perfect_broken`;重复上报去重)——零误标是全有或全无谓词、无部分完美档(依据:Core Rule 5.2) | +| 多敌(unit_count=3)同结算攻击 | 行主序逐敌执行循环、共享同一 L(池共享不消耗);任一敌作用后 hero 死亡 → 短路中止剩余敌(顺位即威胁序的可观测面)(依据:Core Rule 10) | +| 道具段击杀造成死亡即不动手 | 该敌 Dead 后战斗段 `query_alive` 已不含它——不出现行动行(缺席即指针冻结的明细面);抢杀兑付 = 击杀标记进流水(感知数据前置)(依据:Core Rule 9) | +| 零威胁组合(行 atk=0 ∧ cap=0) | 战斗可胜但零风险 = 内容态配置错误(hero atk=0 已被 HeroTable 域下界结构杜绝,战斗恒可定局);护栏 owner = 实现期 ADR/D2 跨字段校验;B1 观测锚 = 战斗轮数异常遥测(进 C3/OQ 清单)(依据:B2 OQ9 并案) | +| 结算开始时 hero hp=0 | 结构不可达(hp=0 只产生于战斗段 → 短路直入判定段);防御断言:窗口打开前置检查 hero 存活,违反 = debug 断言(编排违例显式暴露)(依据:编排自守) | + +## 依赖与输出 + +| 方向 | 系统 | 性质 | 接口要点 | 状态 | +|---|---|---|---|---| +| 上游 | D2 | hard | SettlementTable schema 承载(B1 回填 owner)+ ConfigDB 读取 + `set_reload_gate()` 唯一调用方;零结算随机流(流封闭不变) | Approved | +| 上游 | A3 | hard | F1 五条款下游契约(B1 = 条款①②③⑤闭合 owner)+ `refresh_board` 挖掘期刷新编排 + `notify_settlement_done`/`notify_combat_end` 回发 + `last_round_end` 读取 | Approved | +| 上游 | A2 | hard(订阅) | 三事件流订阅(facts 缓冲;A2 唯一回边的订阅端)+ `install_board`/`retire_board` 调用方 + Waiting 相位观测量 = 结算触发条件;事件到达序不承载语义 | Approved | +| 上游 | B2 | hard | 单位接口全调用面(spawn/query_*/apply_damage/advance/default_target/ambush_grant_plan/despawn_all)+ B2 义务①–⑥ | Approved | +| 上游 | B3 | hard | 状态容器五 API 全调用面 + B3 义务①–⑧;weaken 锚红警裁决 owner(F0 检查点) | Approved | +| 上游 | B4 | hard | 奖励/库存十 API 全调用面 + B4 义务①–⑥(roll_board 恰一次 / 关卡末清库快照计数 / usable_now 组装) | Approved | +| 上游 | A1 | 间接 hard 接线 | `generate(L, D)` 换图编排调用方 + actuals 分布 p10 对账输入(F4 报告 C)+ attempt 边界非 scope 事件 + EC5 零合法板 B1 半边上抛 | Approved | +| 上游 | A4 | 对账锚(零运行时接口) | 条款⑤三腿入账(雷险消解/信息双向/AP 腿)+ 两方向性护栏 + 雾格零特判(B1 无变异分支) | Approved | +| 上游 | C1/C2 | provisional | encounter 上下文 (L, D, hero_hp) 供给(begin_battle 入参);跨战斗 HP 持久性;战斗触发 | 未设计——MVP 冒烟直供(入参封装隔离) | +| 下游 | D1 | hard(数据供给,未设计——第 10 位) | 结算明细流水 = 统一数据源契约(规则 14 行协议);承接呈现义务台账:A2×6 / B2×4 / B3×4 / B4×5 / B1 新增 ×3(高光行强调、完美排雷呈现、EC5 零合法板提示)——缺项即 blocking,随 D1 GDD 门禁评审核对 | — | +| 下游 | B5 | soft(垂直切片) | 结算步骤流 = 战斗动画回放脚本源;溢出行/origin_cell 玩家可读形态(B3×B5×2 数据源) | — | +| 下游 | D4 | soft(Alpha) | 音频挂点 = 明细行类型流(B4 音频挂钩点事件源) | — | +| 下游 | D5 | soft(Alpha) | 教学口径数据源(来源标注/RAP 清零/库存清空/满层溢出/蓄力拍贪雷——经 B1 流水兑现) | — | +| 下游 | C3 | soft(provisional) | 条款③⑤对账结果 = 调参约束输入(unit_count>1 破线约束 / 保守地板缺口 / 护栏 A/B 突破条件);P_perfect/D_ref 调参所有权 | — | + +- 双向一致性核对(systems-index 依赖分层表):B1 ← A3,B2,B3,B4,D2(第 3 Feature 层)· D1 ← A2,A3,B1 · B5 ← B1,D1 · D4 ← A2,B1 · D5 ← A2,A3,B1 · A2 事件面 referenced_by 含 B1(订阅边)。 +- C3 消费 B1 对账结论的事实依赖成立——C3 GDD 定稿时须回写索引(对账输出 = 调参约束清单,非运行时接口)。 +- B1 是项目首个全部玩法上游均已 Approved 的系统(除 C1/C2 外),无 provisional 上游假设。 + +### 呈现数据面义务(B1 无自有渲染面,呈现归 D1/B5) + +| 编号 | 数据面义务 | 归属/呈现 | +|---|---|---| +| 1 | 结算明细屏 = MVP 战斗本体:数据源 = 规则 14 流水行协议;每行 `value` 数值在暗色底上须高对比(明细屏是 MVP 数字密度最高的屏) | 屏级布局/节奏/滚动形态归 D1/B5 | +| 2 | 行类型区分 = 形状+色相双通道:未生效四类行(奖励未生效/已消耗未生效/使用被拒/溢出未生效)必须携带形状冗余(icon/划线等),防色弱玩家把"没生效"误读为普通行 | D1/B5 | +| 3 | 高光行数据面:perfect_sweep 高光行、标中旗 `highlight` 标记、清图奖励行 H2 强调标记——B1 只锁行字段;未生效类行按聚合计数呈现(数据可聚合字段已入协议) | 强调形态归 D1;`target`/`effect_id` 是多敌归因呈现与 icon/极性映射的直接数据源 | +| 4 | 零值伤害行可见性:免伤轮/零输出轮的零值行不得被"零值不显示"优化吞掉(归因链断点) | D1/B5 | +| 5 | 音频挂钩点(D4,Alpha)= 明细行类型流:道具(入库)/效果/伤害/判定/高光行 + 负反馈聚合(未生效类行数 >0 时单次结算至多一次——防逐行惩罚轰炸) | 挂点事件源归 B1 流水 | +| 6 | 道具使用窗口载荷 = B4 `query_inventory` 投影 + B1 组装的 `usable_now` 预判字段(满血治疗包禁选);选择序收集/跳过/窗口时机指示/触发政策归 D1;窗口选择序列 = 回放输入事件;窗口期输入与棋盘不竞争(单意图帧先例) | D1 | +| 7 | 结算明细屏数据锚 = 终局判定行(胜负画面的数据锚) | D1 | +| 8 | UX Flag:本系统有 UI 贡献(结算明细屏 + 道具使用窗口)——Phase 4 运行 /ux-design 为其创建 UX 规格先于编写 epics,引用 design/ux/[screen].md 而非本 GDD | systems-index 备忘(Phase 5d) | + +## P0 最小闭环 + +1. `begin_battle(encounter)` 六步开战斗:gate 置位 → 容器开域 → encounter 骰 → spawn → install+roll 恰一次 → round_start。 +2. 挖掘期 B1 只缓冲 A2 三事件流,玩家挖格/插旗/停手。 +3. 轮末 Waiting 相位自动触发七段结算(触发只读相位与 last_round_end)。 +4. 授予段三批兑现:雷→伏击、安全格→奖励分流、旗→削弱/标旗未中行、清图→奖励(完美双份)。 +5. 窗口段玩家至多选一件道具或跳过(唯一输入点,选择入回放)。 +6. 增益合成 → 道具 → 战斗段主角先手 + 逐敌还手 → 衰减恰一次。 +7. 判定段:hero 先判负 / 敌清空判胜 / 否则回发 same_board 进下一轮;cleared 轮换图续轮。 +8. 胜负 → battle_over 清理序列(库存关内延续)→ 明细流水回流 D1 呈现与回放对账。 + +## P1 / P2 后置 + +| 优先级 | 内容 | 后置原因 | +|---|---|---| +| P1 | flag_miss 标错惩罚源路径(候选"敌方伏击 +1":flag_judged{hit=false} 转化 + B3 source_class 新枚举 + 惩罚重复性裁决) | A2 OQ1 实测采纳后才上;当前标旗未中行仅为归因痕迹(缺口预检 12 / OQ8) | +| P1 | 深度高光形态(#3 标中旗即时 HUD 反馈动画/音画同步;旗落格高亮/姿态反馈) | MVP 已落数据面标记 + 当轮兑付;形态随 art-bible 与战斗表现美术测试定(OQ7) | +| P1 | EC5 零合法板玩家侧呈现形态(提示/补偿/回退三选一;"确定性回退已知好种子"候选归实现期 ADR) | B1 数据面已就绪,形态归 D1 裁决(OQ5) | +| P1 | C1/C2 真实遭遇供给接线(begin_battle 入参改真实遭遇上下文 + 跨战斗 HP 持久性) | C1/C2 未设计,MVP 冒烟直供(缺口预检 5 / OQ6) | +| P2 | 回放切点续玩端到端(快照恢复驱动) | 生效条件 = D3 实现(AC-34 deferral) | +| P2 | 真 A1 端到端集成冒烟(换图 generate 真实集成、attempt 重试语义、非法产物真分布) | 生效条件 = A1 实现落地(缺口预检 10) | + +## 调参表 + +### SettlementTable schema(单行全局,B1 回填 owner——Rule 4 豁免:单行全局常量/结构表,HeroTable/ActionEconomyTable 先例) + +| 字段 | 类型/域 | 默认 | owner | 说明 | +|---|---|---|---|---| +| equivalence_ref_damage(D_ref) | int [1, 999] | 12 | C3(provisional) | 当量参考敞口(F1);变更触发全对照表 + 条款③⑤对账重跑(联动义务,禁独立调参) | +| p_perfect | percent **{100, 200}** | 200 | C3(provisional) | 完美排雷清图奖励第二份兑付触发(100 = 仅高光无第二份、200 = 真实双份交付);非整数倍数乘数无运行时载体,域外加载致命;上界受护栏 B 约束(双读数 24 / ≈8——裁决归 C3) | +| segment_order | StringList | [grant, item_window, buff_debuff, item, combat, decay, verdict] | ✗ 结构冻结 | 段序骨架的零字面量载体;加载期校验恒等于拍板段序,违例致命(防实现乱序静默漂移) | +| source_label_map | Dictionary(source_class → 文案键) | 封闭枚举全覆盖 | ✗ 结构冻结 | source_class → D1 呈现文案映射键(P2 归因呈现侧数据源);键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack};新增键 = 接口漂移 | +| effect_pool_map | Dictionary(effect_id → pool) | attack_up→hero_offense · defense_up→hero_defense · weaken→enemy_output · ambush→flat_attach | ✗ 结构冻结 | 效果语义映射(F2);新 effect_id 上线须随批登记映射,悬空 effect_id = 加载期致命(引用型字段 dangling_ref_policy) | +| pool_clamp_lo / pool_clamp_hi | int(pp) | −100 / +100 | ✗ 结构冻结 | percent 池地板域(F2);乘数域随之 [0, 2] | +| round_direction | enum {floor} | floor | ✗ 结构冻结 | 乘区取整方向(F2——单一取整规则,结构性非 knob) | +| damage_a_per_cell | int ≥ 0 | 1 | C3(provisional) | 伤害A 开格充能/格(护甲克制批——充能与结算归 B1 战斗段) | +| damage_a_cap | int ≥ 0 | 8 | C3(provisional) | 伤害A 单轮充能封顶(0 = 不封顶——防连锁轮失控) | +| damage_b_per_flag | int ≥ 0 | 2 | C3(provisional) | 伤害B 标雷充能/首次标中雷(每雷实例一次去重) | +| armor_effectiveness | Dictionary {base, dmg_a, dmg_b} × {none, a, b} | 特化 ×150 / 无甲 ×100 / 对位 ×50 | 键集 ✗ 结构冻结 / 数值 C3(provisional) | 伤害类型 × 护甲克制矩阵——主角攻击三分量各自查表后 floor 相加;敌种甲型映射归 EnemyTable.armor_type | + +- 加载校验:effect_id 主键唯一(ConfigDB 既有);effect_pool_map 键 ⊆ StatusEffectTable 活行集(跨表悬空致命——B4→B3 首引激活先例同款);segment_order 违例致命;p_perfect ∈ {100, 200}(载体域,域外加载致命;100 = 完美必须 ≥ 不加倍的机制语义下界)。 + +### Knob 表 + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| equivalence_ref_damage | 12 | 全当量表膨胀 → 条款③左臂虚高、锚裕度失真(重跑强制) | 当量坍缩 → weaken 锚第二腿破线(m_w ≥ 60 下界的前提失效) | +| p_perfect | 200 | 载体域封顶 200(C 量级上调走 B4 clear 池 knob,须与护栏 B 口径联动复核:防钓鱼线 D_ref 读数 24 / DPS 严格读数 ≈8) | =100 → 第二份兑付不触发(完美仅剩高光行、无实质增量——情绪奖励感最弱档,机制空转) | + +- 非 knob(结构冻结,修订走 GDD 流程非调参):段序骨架、乘区结构(两池乘算)、floor 取整、池 clamp 域、明细行协议、零随机、结算触发 = Waiting 相位、调用序(query_attack→apply_damage→advance)、衰减恒执行。 +- Knob 交互:D_ref × m_w × m_d × duration_d(锚两腿与条款③ V_safe 共用 D_ref——defense_up.magnitude/duration 已纳入"禁独立调参"联动网络;B4/B1 共裁 m_w、C3 投 m_d 时须同步核对 D_ref 并重跑条款②③⑤);p_perfect × C(clear 池当量)——护栏 B 联动;D_ref × 条款⑤ s_ref(同值引用——改 D_ref 即改结算强度参考,两条款重跑);damage_a/b × armor_effectiveness × EnemyTable.armor_type——C3 联动网络 #6(敌种甲型变更须复算 Boss/普通战 K 区间)。 +- 调参所有权:全部 knob provisional 归 C3;每次调参须重跑 F1 对照表 + 条款②③⑤预检并同步 registry(接口漂移义务——B2/B4 同款条款);actuals p10 交付后的强制重跑义务见对账清单 #6。 + +### 边界参数总表 + +| 参数 | 边界 | 默认 | 出处 | +|---|---|---|---| +| percent 池 clamp | [−100, +100] pp,每池独立收口 | — | F2 | +| 池乘数域 | [0, 2](= 1 + pp/100) | — | F2 | +| m_w(weaken 行值) | [60, 85] | −65 | F0 裁决(瞬时当量口径) | +| 地板交叉层数 | ⌈100/m_w⌉ = 2(值域内恒 2) | — | F0 | +| P_perfect 载体域 | **{100, 200}**,域外加载致命 | 200(×2 双份交付) | 规则 13 | +| D_ref(当量参考敞口) | [1, 999] | 12 | SettlementTable | +| HeroTable.atk | [1, 999],0 加载拒绝 | 6 | 2026-09-11 拍板 | +| 伤害 D' | 整数 [0, ∞),零负值分支 | — | F3 | +| D' = 0 | **不调** apply_damage,产零值伤害行 | — | 负值分支禁令 | +| 取整 | floor,单一规则(结构性,非 knob) | — | F2 | +| 溢杀 | 丢弃,不转移、无负 hp 态 | — | F3 | +| 治疗 | hp' = min(hp + power, max_hp) | — | F3 | +| 伤害A 充能 | 单轮池 ≤ damage_a_cap(0 = 不封顶);轮末清零 | 1/格 · cap 8 | F6 / SettlementTable | +| 伤害B 充能 | 首次标中雷每雷一次去重;无 cap(AP 预算天然限界);轮末清零 | 2/雷 | F6 / SettlementTable | +| 甲效矩阵 | 键集 {base, dmg_a, dmg_b} × {none, a, b} 结构冻结;逐分量查表 floor 后相加 | 特化 ×150 / 无甲 ×100 / 对位 ×50 | F6 / SettlementTable | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | Core-Battle 全集成冒烟:`godot --headless --script`(脚本 extends SceneTree 以获得 quit(code);ConfigDB/RngService autoload 由脚本显式实例化) | +| 真假声明 | 真 A3(相位机全 API)+ 真 B2/B3/B4(class_name 直引实例化;多敌腿复用 `enemy_multi_unit` 夹具根)+ 真 A2 规则引擎(板走既有登记规格 b_spawn/b_flag/b_batch/b_chain/b_fog/b_mine_walk;挖掘/插拔/停手经 `settle_batch` 真驱动——冒烟扮演 D1 提交意图) | +| A1 桩 | 程序构造产物桩:换图 generate 调用点按登记序列返回 schema 坐标级程序构造板;每次调用经 `open_stream("board_gen")` 取号恰一次(与真 A1 取号契约同构,scope 轨迹断言真实);attempt 重试不重开 scope 的语义归真 A1(deferral);真 A1×A2×B1 端到端集成冒烟与 A1×A2 双包含端到端同槽(生效条件 = A1 实现落地) | +| C1/C2 | provisional:`begin_battle(encounter)` 入参冒烟直供(encounter 坐标复用 B2 行集登记 (L_g,D_g)/(L_a,D_a);hero_hp 按腿直供 max_hp/低值/缺血压值) | +| D1/B5 断言面 | 未设计 → 步骤流捕获列表(主断言面)+ 回发捕获(notify_settlement_done/notify_combat_end)+ set_reload_gate 调用捕获;窗口选择与刷新意图 = 冒烟扮演 D1 经 B1 提交面注入 | +| A2 事件载体 | 事件外发载体(signal vs 回调)归实现期 ADR——依赖真 A2 事件流驱动的 THEN 子集在 ADR 落地前为纸面契约;备选 = A2 半真(事件注入面直喂 facts 缓冲 + A3 真,断言口径不变);本节优先真 A2 全集成,半真仅为 ADR 前过渡载体 | +| 被测面 | `begin_battle(encounter)` · 窗口选择提交面 · 刷新意图提交面(refresh_board 挖掘期刷新编排)· 结算事务(内部触发,经 A2/A3 驱动到达);段序断言 = 步骤流行序(授予类行 < 窗口标记 < 效果行 < 道具行 < 伤害/蓄力行 < 衰减行 < 判定行——全序断言) | +| 只读观测量 | A3 ap/rap/phase/last_round_end · B2 query_alive/query_beat/query_attack/default_target · B3 query_active(六归因字段+快照载荷)· B4 query_identity/query_inventory · RngService 公开 scope 计数器(board_gen/reward_roll 双流——roll_board/generate 恰一次的观测面)· 诊断收集器 | +| 期望值纪律 | 全部 knob 按当前 ConfigDB 表读值推导、不写死;记号:D_ref(equivalence_ref_damage)/ P_pf(p_perfect)/ m_w(weaken.magnitude)/ m_d/D_a/M_a(defense_up)/ m_amb(ambush.magnitude)/ c_r(所在行 cap)/ a_g/h_g(goblin)/ a_e/h_e/m_e/u_c_f(bugbear)/ M_h(HeroTable.max_hp)/ A_h(HeroTable.atk)/ p_i(RewardTable.power)/ AP_r | +| 结构定值断言 | 合法集:segment_order = 拍板序、pool_clamp_lo/hi = −100/+100、round_direction = floor、effect_pool_map 四键、armor_effectiveness 键集 {base, dmg_a, dmg_b} × {none, a, b}、行类型封闭枚举 13 类、公共字段 + 行类型特有字段分层、source_label_map 键集封闭(含 enemy_action/flag_miss/hero_attack) | +| SKIP 与生效前提 | 依赖修订后基线的腿(如 m_w ≤ −51 使 2 层即越域;m_d=−30 使 defense_up 叠满脱离地板边界读法)前提不满足时输出显式 SKIP 防 CI 静默、不假失败;registry 对账 AC(AC-41)在回执落地前失败 = 裁决未兑现的正确信号(不写软口径)——现行回执在案(F0 裁决批 + 重算批),AC-36/41 按现行值链断言 | +| 登记常数 | A2 板登记常数与 B2 行集登记坐标直接复用不重制;B1 新增 σ 登记轨迹(身份依赖腿——如"双治疗包""刀全灭敌""清图槽身份",master=42、经 derive_at 测试通道推导、预推进按 B4 时序口径)+ 登记构造序列(板 × 意图序 × 期望行数);登记后第二人独立推导复核,失效处置 = 先复核登记、无误即实现缺陷;每用例先 `reset_diagnostics()` | +| 夹具策略 | 默认根 b1_default = D2 全表清单 + SettlementTable 默认行(D_ref=12 / P_pf=200 / 拍板段序 / source_label_map 封闭全覆盖 / effect_pool_map 四键 / clamp −100/+100 / floor / 充能三参数 1·8·2 / 甲效矩阵默认值);致命根 ×3 独立进程:b1_segorder_bad(段序置换)/ b1_poolmap_dangling(ghost_effect 键)/ b1_pperfect_oob(p_perfect=99);SettlementTable schema 程序构造承载域校验全谱;板 = A2 既有板规格复用;行为夹具根经 `--data-root=` 注入(仅 debug、不入 data/tables/);各 AC 声明其板/根、用例间无顺序依赖 | +| 致命类收窄 | 恰三行致命 manifest(独立 headless 进程、游戏通道):segorder_violation → ≠0 · poolmap_dangling → ≠0 · pperfect_oob → ≠0;负空间声明(编排自守 fail-fast 断言——new_board 违约守卫、窗口打开前置 hero 存活断言、一事件双转化守卫、单一结算事务在飞——违规路径不可由公开 API 构造)不经冒烟断言、不入 manifest,行为级证据 = 正腿断言 + AC-43 静态扫描;其余致命类别由 D2 AC-2 同类别 loader 级夹具承载 | +| out-of-scope | D1 呈现全域(生效条件 = D1 义务台账 A2×6/B2×4/B3×4/B4×5/B1×3 登记,缺项即 blocking)· B5 消费与可读形态 · D4/D5 音频教学(数据源 = 本节流水行)· C3 全部 provisional 数值(每次调参重跑 AC-35/36/37/39/40/44 对账族——D_ref × m_w × m_d × duration_d 禁独立调参)· A1 actuals p10 重跑 · 条款⑤护栏 playtest 观测面(弃图率雾板分层/速杀退化/零伤害死局遥测——B1 仅供遥测锚)· m_w 修订终值 · flag_miss 惩罚源路径 · C1/C2 真实供给 · 深度高光形态 · 事件外发载体 ADR · attempt 重试细节 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | `b1_default` 经 `--data-root` 注入干净加载 | 冒烟脚本读 SettlementTable | 八字段在位:segment_order ≡ 拍板序 `[grant, item_window, buff_debuff, item, combat, decay, verdict]`(结构定值恒等断言);effect_pool_map 四键逐一 = 定稿映射;clamp −100/+100、round_direction=floor;D_ref 仅域断言 [1,999]、P_pf 域断言 {100,200};source_label_map 键集 ⊇ B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack};同根跑 B4/B3/B2 默认根加载零跨表冲突诊断 | 规则 16 / schema 定稿 | +| AC-2 | segment_order 置换根 `b1_segorder_bad`(如 item_window 与 combat 互换) | 独立 headless 进程游戏通道加载(collect_only=false) | 进程非 0 退出且错误清单含 segment_order 类别(防实现乱序静默漂移的进程级落点) | 规则 16/4 | +| AC-3 | 映射含 `ghost_effect` 键根 `b1_poolmap_dangling` | 独立进程游戏通道加载 | 非 0 退出、错误含跨表悬空类别(引用型字段 dangling_ref_policy) | 规则 16 | +| AC-4 | SettlementTable schema 类直引实例 | `validate()` 作用于 p_perfect ∈ {99, 150, 1000} | 各含域错误类别(载体域 {100,200}——非整数倍数无运行时载体);p_perfect = 100 与 200 零诊断;腿 2:`b1_pperfect_oob` 独立进程加载 → 非 0 退出(manifest `pperfect_oob`) | 规则 16/13 | +| AC-5 | 全新 B1(Uninit)+ 真 A3(Idle)+ 真 B2/B3/B4 全新实例 + A1 桩登记首板 b_spawn + master=42 | `begin_battle(encounter)` | 步骤流/调用捕获全序恰一次:set_reload_gate(true) → B3 notify_battle_start → **encounter 骰(unit_count 区间定本战实际数量——遭遇随机化增补)** → B2 spawn(override 数量)→ A2 install_board + B4 roll_board → A3 notify_round_start(phase=Active);**encounter scope = +1**、reward_roll scope = +1、board_gen scope = +1(各恰一 scope);query_alive(enemy) = 本战实际数量个行值快照单位(默认根 unit_count=1 定值行骰退化为 u_c);A2 事件捕获含初始展开批;B3 query_active 双侧空 | 编排专章战斗开始序列 / 规则 16① / B4 义务⑥ / C3 修订批 X-6 | +| AC-6 | 构造至 A3 Waiting 且 last_round_end 可用(stopped 轮——经真 A2 `settle_batch([stop])` 到达) | 腿 1:结算事务受理;腿 2:装载层置换 {flag_judged 批, round_end} 两组到达先后分别驱动(捕获-重放,不增设 B1 注入 API) | 腿 1:触发判定只读 A3 相位与 last_round_end(事件捕获序 = A2 管线序,结果正常);腿 2:两遍结算步骤流逐行相同(到达先后不承载语义) | 规则 2 / A2 义务① | +| AC-7 | b_flag 满构造结算(挖掘 ≥1 奖励格 + 插 (1,4) hit 旗 + 停手收轮;σ 登记使奖励格含道具类身份) | 结算事务解析 | 步骤流行序全序断言:授予段行 < 窗口标记(open——段界锚)< close/skip < 效果行 < 道具行 < 伤害/蓄力行 < 衰减行 < 判定行;任一段缺行以段界锚与行类型域断言序(空段合法);窗口标记恒存在(零选择 = skip 恒登记);segment_order 表值经 AC-2 恒等拍板序——实现读表驱动非硬编码 | 规则 4 / B4 义务① / 规则 14 | +| AC-8 | b_batch 部分挖掘后 stopped 收轮、结算完成(判定行 = 继续) | 出口分发 | 回发捕获恰一条 notify_settlement_done(same_board) → A3 notify_round_start(Active、ap=AP_r 硬重置、rap 保留);A2 板状态延续(旗集/已开集不变);board_gen/reward_roll scope 计数器零变化 | 编排专章继续腿 / A3 Rule 5 | +| AC-9 | b_flag 登记构造轮(挖掘 n 格含 m₀ 雷格 + 插 f 旗含 h₀ hit) | 轮末结算;同轮事件批重复投递(装载层故障注入) | 每格受理恰一次:B4 grant 恰 n_safe 次、B2 ambush_grant_plan 恰 1 次、apply_effect(weaken) 恰 h₀ 次、apply_effect(ambush) 恰 granted 次;处理序 ≡ A2 cell_revealed/flag_judged 行主序(B3 义务①落地);重复投递 → 步骤流与容器终态 = 单次投递结果(缓冲去重);B3 不去重、B4 幂等仅兜底(诊断零"重复受理") | 规则 3/5 / B3 义务①② / B2 义务② / B4 义务② | +| AC-10 | σ 登记挖掘格身份含 buff 类与道具类各 ≥1(腿 1);b_spawn install 后首轮(初始展开 35 格已发生——channel=initial、reward_eligible=false)(腿 2) | 授予段解析 | 腿 1:buff 格 → apply_effect(effect_id, source={safe_cell, origin_cell, round_index})(六归因字段逐一 = 调用实参);道具格 → stock(item)(库存逐件来源 {cell, round} 可归);即时类当场兑付:instant_aoe = 逐存活敌 apply_damage(u, power) + 单条伤害行(target=all_enemies + count 字段)、instant_heal = clamp 回复 + 治疗行(满血 → "奖励未生效"行零兑付)——零 stock 零窗口;B4 只定"给哪行"、层数语义归 B3 合并(同轮双 buff 格 → 单实例 L=2);腿 2:grant/stock/apply_effect 捕获全零(R₀ 不入奖励库);channel 字段零特判(同板 chain/dig 格正常入库成对照) | 规则 5.1 / A2 义务② | +| AC-11 | σ 登记使同轮/邻轮 defense_up 达 M_a 层满 | 第 M_a+1 层授予受理(B3 apply 返回 false) | 产"奖励未生效"行:零折算、携带可聚合计数/批次字段、source 归因完整;B3 容器逐字不变(拒绝原子);连锁批量授予溢出 = 显式接受(b_chain 13 格批量腿:溢出件逐件入行、零转换补偿) | 规则 5.1 / B4 义务② / B3 义务⑧邻接 | +| AC-12 | b_flag 插 (1,4) hit 收轮(腿 1);续轮再停手(A2 重报 {(1,4), true})(腿 2);拔 (1,4) 重插再收轮(腿 3);AC-28 换图后新板同坐标旗靶(腿 4);插 (0,3) miss 旗收轮(腿 5) | 结算 | 腿 1:apply_effect(weaken, source={flag_hit, (1,4), round_index}) 恰一次 + 效果行携带 highlight 标记 + 资格消费入集合;腿 2:零 weaken 兑付、零明细行(重复兑付从未发生)+ debug 诊断可选、资格集合不变;腿 3:仍零兑付(资格以雷格实例为键);腿 4:兑付恢复(新实例资格重置);腿 5:"标旗未中"零值行恰 1 条(source=flag_miss、value=0、highlight=perfect_broken、聚合计数)+ 续轮重报零新增行(每雷实例一次去重)+ 换图新板重新产行 | 规则 5.2 / A2 义务③ / 规则 13 | +| AC-13 | b_spawn 挖尽未开 8 格触发 cleared + σ 登记清图身份(腿 1);σ 登记清图身份 = defense_up 且已满层(腿 2);AC-28 场景 refresh 图(腿 3) | 结算授予段 | 腿 1:grant_clear() 恰一次(同批次重调幂等兜底——诊断零新增)、兑付分流同 AC-10(source=clear_reward)、当轮窗口即可选用;腿 2:零兑付 + "奖励未生效"行 + 完美板高光行捕获零次(首份即零兑付不发高光;完美谓词仍成立,对账入账 = 实际兑付 0);对照腿:仅第二份被拒(首份落地)→ 高光行在案且 value = 首份实际当量 + "奖励未生效"行一条;腿 3:refresh 图 grant_clear 捕获零次(roll 照常、兑付不调) | 规则 5.3 / B4 义务③ / B3 OQ4 | +| AC-14 | b_flag 登记构造轮:单轮挖掘雷格 mines_r = 6(登记走位序列;cap = c_r = 4 表读值) | 结算授予段 | ambush_grant_plan(6, c_r) 恰 1 次 → {granted=4, overflow=2}(守恒);前 4 颗(事件规范序)逐颗 apply_effect(ambush, source={mine_hit, origin_cell, round_index})(B3 伏击池 L=4);overflow 2 颗逐颗产"溢出未生效"行(含来源 + 聚合计数);战斗段各敌伤害行 attach 分量 = 4×m_amb(池读值即时性);轮末衰减行 k 含伏击实例(dur=1 蒸发);重复投递判别同 AC-9 | 规则 6 / B2 义务②⑥ / B3 义务⑧ | +| AC-15 | 战斗中 hero 满血 + σ 登记库存含治疗包(腿 1);扮演 D1 强制提交满血治疗包(腿 2);提交多件 [刀₂, 刀₁] / 单件 / 零选择(腿 3);无库存轮(腿 4) | 结算到达窗口段 | 腿 1:窗口清单 = query_inventory 投影 + B1 组装 usable_now(治疗包 false——B1 读 B2 hero hp 判定,B4 诊断零"血量查询"新增);伤害道具恒 true(窗口打开时存活敌 ≥1);腿 2:"使用被拒"行 + 包 InStock 不变(use 未调);腿 3:多件 → 仅首件受理(违例诊断、刀₂生效刀₁不消耗);单件 → 生效;skip → 窗口标记 close/skip、段序零停顿、库存不变;腿 4:窗口标记照常 skip(呈现政策归 D1——数据面 = 窗口清单空) | 规则 7 / B4 义务④前半 / D1 义务②对侧 | +| AC-16 | begin_battle hero_hp = M_h − 2 + σ 登记库存含治疗包 ×2(power = p_i) | 窗口提交 [包₁, 包₂] | 仅包₁受理生效(治疗行 hp' = min(M_h−2+p_i, M_h)——部分回复钳满)、包₂零调用(InStock 不变)+ 编排违例诊断("窗口至多选用一件");满血守卫保留为纵深防御(跨结算场景仍可达:本结算被拒后下一结算窗口再判) | 规则 7/EC / B4 义务④ | +| AC-17 | 同轮两次 weaken 授予(互异 origin_cell)(腿 1);defense_up 行与 weaken 行同轮(腿 2);`enemy_multi_unit` 根同轮 weaken L=1(腿 3) | 增益/减益段解析 | 腿 1:效果行恰 2 条(按 apply_effect 调用流水展开、非按容器单实例),每行 origin_cell 逐一 = 授予值、effect_id=weaken、value = 该次授予贡献值(L×m_w 投影)、pool_value = 池合成值(两行各 −65 贡献、pool_value = −130——合成值不入 value 字段);腿 2:效果行 pool 字段经 effect_pool_map 自 effect_id 派生 = hero_defense / enemy_output;腿 3:全体存活敌伤害行均含减伤合成且 target 逐一可归(side-wide 多敌兼容读法) | 规则 8 / B3 义务③⑤⑦ | +| AC-18 | 同结算 weaken L=1 存活 + σ 登记库存含刀(腿 1);`enemy_multi_unit` 根 + σ 登记刀 ×3(每结算至多一件、跨结算序列)(腿 2);hp=2 残敌 + p_i=4 刀(腿 3) | 道具段 | 腿 1:伤害行 value = p_i 恒定、零乘区(分量字段无池修正项——"不吃任何 percent 修正"的行级判别);腿 2:每条伤害行 target = default_target() 返回(顺位最前存活敌)、击杀标记出现于目标 hp ≤ p_i 的件、[0] 死后下一结算顺延 [1]→[2]、击杀标记共 3 个;query_beat 冻结(指针冻结进流水);后续结算战斗段 query_alive 不含它、零行动行;腿 3:死、行 value = p_i、击杀标记布尔、无溢出量字段、零转移(下一件打 default_target 后继) | 规则 9 / B2 义务⑤ / B4 义务④ | +| AC-19 | hero 满血 + 库存治疗包(腿 1);AC-20 场景敌已全灭 + 满血 + 提交治疗包(对照腿) | 提交选择 | "使用被拒"行、零 use 调用(不烧包);满血守卫 = 表局部判定(max_hp 对比)、敌全灭构造下仍拦截(守卫不读存活集的不对称可判定形式);clamp 部分回复见 AC-16 | 规则 9 / B4 义务④ / EC | +| AC-20 | 单件制 + 窗口结构推论(窗口打开时存活敌 ≥1 ∧ 单件恒打顺位最前存活敌) | 负空间声明(违约路径不可由公开 API 构造) | 窗口提交的刀恒有目标——"已消耗未生效"行在单件制下不可达;零效果判定逻辑保留为防御分支(读 B2 存活集),恢复多件制时本 AC 随批复活;击杀标记断言由 AC-18 腿 2 承载 | 规则 9 / B4 义务④ / EC | +| AC-21 | encounter=(L_g,D_g) 单 goblin(腿 1);(L_a,D_a) 连续结算(腿 2);全结算 advance 捕获(腿 3);`enemy_multi_unit` 根(腿 4) | 战斗段解析 | 腿 1:调用捕获序 = query_attack → apply_damage → advance(每存活敌恰一次 advance)+ 衰减行前完成;腿 2:beat 轨迹 = p_e 序、每结算 advance 恰 1;腿 3:advance 捕获实参 ∈ 敌句柄集、零 hero 句柄(B2 对 hero 句柄拒绝为兜底);腿 4:行动/伤害行序 = 生成序且每行 target 字段可归至对应单位 | 规则 10 / B2 义务① | +| AC-22 | b_mine_walk 单雷轮(L=1)+ goblin attack(腿 1);登记构造轮 L_amb=2 + weaken L=1 同轮(腿 2);b_flag 双 hit 旗同轮(weaken L=2;非平凡前提 m_w ≤ −51——F0 修订回写后恒满足,不满足时 SKIP 标记)(腿 3);defense_up 叠满同结算 P_pool = 2×m_d(腿 4) | 战斗段攻击拍 | 腿 1:伤害行携带 {baseline=a_g, attach=m_amb, 终值}、source_class=enemy_action、target = 受击单位句柄;L=0 轮 attach=0(自然读值);腿 2:D' = floor(D_raw × (100+P_e)/100 × …) 按表读值推导;腿 3:效果行显合成值 2×m_w 与终值两端、伤害行 value=0、apply_damage 捕获零次、B2 hero hp 不变、诊断零新增;weaken dur=1 轮末蒸发(下轮恢复常态伤害);腿 4:现行 −30 下 P_pool = −60 域内、乘数 0.4 无 clamp 腿——D' = floor(D_raw × 0.4 × …) 按表读值推导(免伤轮现行可达路径 = 腿 3 weaken 饱和;地板恰边界读法 P_pool = −100 由 F2 判据函数输入断言承载——AC-37) | 规则 10 / F2 / F3 / B3 义务③ / EC | +| AC-23 | (L_a,D_a) 结算 1 attack 后结算 2 构造挖雷 ×1(L_amb=1)(腿 1);`enemy_multi_unit` 根同步轮转至 charge 拍(腿 2) | 战斗段(charge 拍) | 腿 1:蓄力行(数字兑现面)+ query_attack 读值零作用 + 零伤害行、hero hp 不变;授予段伏击授予行在案(先授予后蒸发对照);轮末衰减清池(下轮 attach=0);腿 2:恰 u_c_f 条蓄力行、零 apply_damage、hero 承伤 0(合法状态非错误) | 规则 10/EC / B2 EC8 对侧 | +| AC-24 | `enemy_multi_unit` 根 + begin_battle hero_hp = 4 直供 | 首结算战斗段(敌[0] attack 分量 ≥ 4) | 敌[0] 伤害行后 hero hp=0 → 短路:敌[1]/[2] 零行动行、零 advance 捕获、径入衰减段(照常)→ 判定行 = 战斗失败(hero 先判防御腿——双死结构不可达不构造,判定行次序字段可读);battle_over 序列触发 | 规则 10/12 / EC / B2 义务③ | +| AC-25 | `enemy_multi_unit` 根 + 登记轮 L_amb = 2(双雷) | 战斗段逐敌解析 | 敌[0]/敌[1]/敌[2] 伤害行 attach 分量均为 2×m_amb(共享、非均分/消耗——每敌 query_attack 读同一 L);中途无池写捕获 | 规则 10 / B2 F2 | +| AC-26 | 任一 AC 场景(腿 1);末图清图胜利结算(AC-13 腿 1 场景 + 敌已全灭构造)(腿 2);全 dur=1 上轮清空后的结算(腿 3) | 结算 | 腿 1:衰减行恰 1 条/结算(k 记入行值)、decay_round 调用捕获恰 1 次;腿 2:判定行 = 胜利之前衰减行在案(先兑付后衰减时序契约;battle_end 全清兜底使两序同终态、次序钉死为段序);腿 3:衰减行 k=0(合法零行/聚合零计数) | 规则 11 / B3 义务④⑥ | +| AC-27 | 敌全灭结算(腿 1);失败(腿 2);继续(腿 3) | 判定段 | 腿 1:判定行 = 战斗胜利 → battle_over 序列(AC-31);腿 2/3:见 AC-24/AC-8;判定行公共字段 + 终局行在案(胜负画面数据锚) | 规则 12 | +| AC-28 | b_spawn cleared 判定 + A1 桩登记板序列 [板₂](程序构造、坐标级登记)(腿 1);桩登记返回带非法产物标记板(腿 2) | 出口分发 / install | 腿 1:调用捕获全序恰一次:A2 retire_board → B4 retire_mapping → A1 generate(board_gen scope +1——attempt 重试零额外取号)→ A2 install_board → B4 roll_board(reward_roll scope +1——每图恰一次)→ notify_settlement_done(new_board) → notify_round_start;新板资格格 roll 照常、grant_clear 零调用;完美计数随 install 重置;腿 2:install accepted(编排不拒产物)→ B1 上抛玩家侧呈现请求(回发捕获含呈现请求事件 + 数据面 = 非法标记,形态归 D1);A2 install 拒绝(不变式违例)= fail-fast 上抛(程序级非玩家域) | 编排专章换图腿 / B4 义务⑥ / EC | +| AC-29 | Active 且 rap ≥ rap_refresh_cost(腿 1);同轮第二次提交(腿 2);第三次提交(腿 3);rap < cost(腿 4) | 扮演 D1 经 `refresh_board` 提交刷新 | 腿 1:A3 request_refresh → true、rap=0、phase 不变(无结算段、无 round_end)→ 换图全序恰一次(A2 retire → A1 generate → A2 install → B4 roll_board;board_gen/reward_roll scope 各 +1);同轮续挖可证新图;腿 2:第二次完整编排;腿 3:拒绝(resource_face——refresh_cap_per_round=2);腿 4:false、状态不变(非编排错误) | 编排专章 / A3 Rule 11/EC13/EC15 | +| AC-30 | stopped 轮与 ap_exhausted 轮(各构造)(腿 1);cleared 轮(腿 2) | 出口分发 | 腿 1:notify_settlement_done(same_board) 恰一条、new_board 回发零次、retire/generate/roll 捕获全零(本编排无刷新提交面);fail-fast 守卫半 = 自守 debug 断言(内部违约路径不可由公开 API 构造——负空间,契约源 = registry action_economy.rap_rules,存在性由 AC-43 扫描域 + 代码评审承载);腿 2:cleared ⇒ new_board 恰一(AC-28 全序——两腿合成契约全貌) | 编排专章 / A3 rap_rules / 强制检查点③ | +| AC-31 | 判定行 = 胜/负任一(腿 1);末层 victory 或 defeat(腿 2);清理序列注入重复调用(诊断注入构造)(腿 3) | battle_over 序列 / `level_clear()` / 清理中 | 腿 1:捕获全序恰一次:B4 retire_mapping → B2 despawn_all(query 双侧空)→ B3 notify_battle_end(query 双侧空)→ A3 notify_combat_end(phase=Idle、rap=0)→ set_reload_gate(false);clear_inventory 不在 battle_over 序列中(缺席本身为断言腿:battle_over 后 query_inventory 逐字不变);腿 2:clear_inventory 恰一次 + 返回清空前快照计数("剩余道具 ×N 随关卡结束清空"反馈行数据面;折算结构位归 C2);此后 stock/use → false + 诊断;腿 3:debug 诊断 + 序列剩余步骤照常完成(幂等守卫兜底 false);后续 begin_battle 正常受理(防半清理态泄漏尾腿) | 编排专章 battle_over/level_clear 腿 / B4 义务⑤ / EC | +| AC-32 | AC-5 战斗开始序列完成后(gate 捕获 = true 在案)(腿 1);AC-31 battle_over 完成后(腿 2) | 经 ConfigDB `reload()`(debug 构建) | 腿 1:返回 false、ConfigDB 值不变、config_reloaded 不发("战斗中真的被置位"由编排真调用承载);战斗中经测试通道换根直调 load_tables(collect_only=true) 读值不受门约束(通道区分);腿 2:reload() → true 且 config_reloaded 恰一次 | 规则 16 / D2 EC8 / 强制检查点① | +| AC-33 | 两套全新全栈实例(同 master=42、同根、同板登记序列、同 encounter);判别腿:仅窗口选择序列不同(同意图) | 同一意图序列 + 窗口选择序列驱动两遍(定长场景:含挖掘/连锁/插旗/停手/窗口选用/刷新换图至少各一) | 两遍步骤流逐行相同(行数/行序/全字段含 value 与归因——target/effect_id/pool_value 均为输入的确定函数,逐行相等断言域含之);判别腿:仅窗口选择序列不同 → 步骤流在窗口标记处分歧、其前逐行相同(窗口选择序列确为回放输入);确定性来源行为面:B1 全程零 RNG 调用(AC-43)+ 表值快照 + attempt 非 scope 事件 | 规则 15 / 强制检查点② / D2 AC-15 | +| AC-34 | AC-29 腿 2 连环刷图场景 | 任一结算回放切点取快照(master_seed + 双流 scope 计数器 + 意图序列 + 窗口选择序列) | 无在飞流实例跨界(切点时刻双流计数器 = 已完成 install 数);续玩快照恢复驱动 = 端到端集成冒烟 deferral(生效条件 = D3 实现) | 编排专章回放切点 / 规则 15 | +| AC-35 | b_flag 构造零误标 ∧ ≥1 标中(仅插 (1,4) hit 旗)→ 挖尽 cleared(腿 1);同构造 + 插 (0,3) miss 旗(腿 2);全板零旗 cleared(腿 3);AC-28 换图后新板零 flag_judged(腿 4);b_fog 雾内插旗(腿 5) | 清图兑付 / 结算 | 腿 1:高光行在案(value = 实际有效兑付当量)+ 清图奖励身份第二份兑付捕获恰一次(道具类身份 → stock 恰 2 次 / buff 类身份 → apply_effect 恰 2 次——玩家实得两件;grant_clear 本身仍恰 1 次;等价入账 = power × P_pf ÷ 100 记号推导);腿 2:无高光行、正常兑付 + "标旗未中"零值行在案(highlight=perfect_broken)(全有或全无谓词、无部分完美档);腿 3:无高光行(hit=true = 0 不满足谓词);腿 4:完美谓词域 = 本板自 install 起(跨图不累积——构造前板含误标 + 换图后新板零误标有标中 → 新板 cleared 触发高光,重置判别正腿);刷新图本身零 grant_clear 无兑付点;腿 5:雾内插旗计入误标(hit=false)→ 破完美(无变异分支) | 规则 13 / EC×2 | +| AC-36 | 默认根 + 登记构造(每活行一 B3 实例:defense_up L=1 / weaken L=1 / ambush L=1×k=1;clear_blast 经 grant_clear 快照) | 结算兑付读值(query_active 快照载荷 / grant_clear power)经统一换算式 E(·)(SettlementTable D_ref 读值) | E_inst 结论与登记对照表一致(F1 重算回执版):E_defense = \|m_d\|/100 × D_ref、E_attack = 0.50 × D_ref、E_weaken = \|m_w\|/100 × D_ref、E_ambush(k=1) = m_amb、E_clear = power ×(perfect ? P_pf ÷ 100 : 1)(真实双份——完美板两件实得、总当量 = 2×power;单一线性换算——各行共用同一 D_ref 读值);锚两腿断言:E_weaken > E_attack ∧ E_weaken ≥ E_dig 推导值(登记常数——重算批回执值);attack_up 活行:hero_offense 池映射在位 ∧ 主角先手攻击伤害行 hero_offense 消费点在案(F3⓪ 基础分量——规则 10 / F3 对账;attack_up 活行口径) | F1 / 规则 13 / B3 义务⑤ | +| AC-37 | F2 闭合判据函数(语义映射 → 池加算 → clamp → 乘区 → floor)+ 登记构造 | 以登记构造跑 F2 三例读法(weaken AP 界 8×m_w / 表界 16×m_w / defense_up 叠满 2×m_d——全表读值记号) | 判据结论逐一与 F2 表一致:两越域例 clamp 后 M=0;叠满例 M = 1 + 2×m_d/100 域内(现行 −60 → 0.4,无 clamp 腿);地板恰边界读法(M=0 未越域)以判据函数对 −100 输入断言承载;地板交叉层数 = ⌈100/\|m_w\|⌉ = 2(表读值推导);取整方向 = floor 单一规则(半值例断言);行为级交叉锚 = AC-22 腿 3 | F2 / B3 义务③ | +| AC-38 | F3 判据函数(三分支全序 + 负值禁令 + 死亡边界)+ 登记例(goblin L=2 → weaken → floor 3 记号 / knife_adv p_i vs hp=6 残敌 / D'=0 分支) | 以登记例跑判据 | 结论与 F3 一致:零负值(任意输入组合判据函数输出域 ≥ 0——参数化扫描)、Dead ⇔ hp=0 零负 hp 中间态、hero 先判优先级;行为级交叉锚 = AC-18/20/22/24 | F3 / B2 义务③ | +| AC-39 | F4 闭合判据函数 + 登记常数(C3 v3 行值总表:10×10 d0.25 → \|M\|=23/\|S\|=77、12×12 d0.25 → \|M\|=34/\|S\|=110;ι = 行 target 代 p10 乐观代理;m_d/D_a、m_amb、D_ref 全表读值/登记推导) | 对 L1/L2 两板形与满配语境(unit_count=3)跑配对主式 | 结论与登记一致(双口径断言——重算批回执):L1 裕度 ≈0.48× ✗ / L2 ≈0.47× ✗(锚行口径;期望口径 E_dig=4.5 → ≈0.60×/0.58× 同向破线——双口径登记)/ 满配语境 ≈0.16× 深度破线 ✗ → C3 曲线级约束输出(V_safe 侧杠杆重议——联动网络,F4 结论④);适用域守卫腿:登记构造 n_s < 1(残余集为空)→ 主式不评估、信息端平凡腿接管;信息端平凡腿 P(安全)=1 → V_safe > 0 恒真;registry clause3_supply 重算回执存在性断言;A1 actuals p10 交付后强制重跑 | F4 / B2 义务④ / 强制检查点条款③ | +| AC-40 | F5 闭合判据函数(修后模型:n_s = (1−ι)×\|S\|、E[踩雷] = \|M\|×n_s/(n_s+1)、X_res = E[踩雷]×V_mine、Δr = ⌈(n_s+E[踩雷])÷AP_r⌉、C_eff = C×(perfect ? P_pf ÷ 100 : 1))+ 登记常数(v3 曲线板形同 AC-39 / ι / E_dig 格子池加权推导 / C=clear_blast power / P_pf / AP_r / fog_burden_ratio 登记代表值) | 主判式对 L1 无雾态/L2 两板形 × {unit_count=1, unit_count=3} 四态组合 × {完美/非完美} 分支跑 | 结论与登记约束包一致(重算批回执):L1 −28.7/+11.3、L2 −67.4/−27.4、L1 满配 −153.2/−113.2、L2 满配 −257.1/−217.1——条款⑤未闭合(verdict = 未证明的约束生成器;L1 完美翻正腿在案);实现率折减腿(E_dig×0.6)读数在案(L1 −45.3/−5.3、L2 −91.2/−51.2);保守地板需求输出(L1 ≥ 110.3 / L2 ≥ 166.8);停手侧 RAP 零价假设登记存在性(判据函数输入分解可读);护栏 A/B 行断言 = 登记存在性 + 旧值链待重推注记(护栏 A 方向性复核 −1.1 越下界 / 护栏 B C=40 越线——W2 三选一裁决随 C3 收口批,本 AC 不断言其数值);A4 三腿入账核对(雷险消解/信息双向/AP 腿)逐腿在输入分解中可读;全条为对账断言——非 playtest | F5 / 强制检查点条款⑤ | +| AC-41 | 默认根加载 | 脚本解析 `design/registry/entities.yaml` | ① status_effect.anchor_warning 含 F0 裁决回执注记(瞬时口径 / 值域 [60,85] 默认 65 / 交叉层数 2 / attack_up 参照回迁口径重跑兑现——重算批注记);② weaken 值域修订注记 + clause3_supply.direction_precheck 闭合回执注记(含 2026-09-14 重算回执——现行值链破线读数);③ clause5_dominance 注记存在(含 C3 裁决 + 重算回执);④ settlement_table 条目存在且十二字段名与 SettlementTable schema 逐一相等(pool_clamp_lo/hi 两字段 + 充能三参数与 armor_effectiveness——W3 字段);⑤ knob 活值(D_ref/P_pf/m_w/m_d)不作定值对账 | F0(registry 对账形态)/ 规则 16 / 对账清单 | +| AC-42 | 全部 AC 场景步骤流捕获全集(冒烟汇总断言) | 逐行校验 | type ∈ 封闭枚举 13 类(封闭枚举即负空间:新增行类型 = 接口漂移);每行公共字段 type/source_class/origin_cell/round_index/display_name/value/highlight? 全在(origin_cell 板级行可空;highlight 可空枚举 {flag_hit, clear_reward_h2, perfect_broken});行类型特有字段:伤害行/道具行(生效)携 target、效果行携 effect_id + pool_value 且 value = 单次授予贡献值语义;source_class 值域 ⊆ source_label_map 键集 ∪ 判定/道具扩展 ∪ {enemy_action, flag_miss}(新增 = 接口漂移的表侧闸);无来源行 = 断言失败(P2 兜底的机器可判定形式);未生效四类 + 标旗未中行各携带聚合计数/批次字段(非空) | 规则 14 | +| AC-43 | B1 文件集(编排器/判据函数/步骤流/冒烟脚本/夹具规格)与全项目非 tests/ 文件集 | 静态扫描 | ① 零 randomize()/RandomNumberGenerator/自建 RNG;② 零 extends Control/Node2D、零 .tscn preload + AC-5 headless 实跑通过(合成判定);③ 结算路径 await 零命中(逐字面 warning 级 + 人工复核);④ 编排唯一性单向扫描:全项目非 tests/ 文件集中 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集;⑤ D2 AC-14 人工三要素字面量扫描(行为级硬证据 = AC-22/36/39/40 全表读值推导 + AC-41 registry 对账);⑥ A2/B3/B4 相互直调零命中 | 规则 1 / 编排不变式③ | +| AC-44 | b1_default + 登记构造轮(挖掘 ≥10 安全格〔含连锁——cap 腿 >8〕+ 首次标中旗 2 枚 + 目标甲型三腿:goblin none / bugbear a / ogre_chief b;attack_up 授予 ×1 腿与非授予腿对照) | 结算战斗段主角先手攻击(表读值记号:da/ca/dbf/甲效矩阵) | 三分量读法逐腿与 F6 一致:A 池 = min(开格数×da, ca)、B 池 = 首标数×dbf;base = floor(atk×(1+P_offense/100))、dmg_a/b = floor(池×甲效/100)、D_hero = 三分量相加(表读值推导不写死);甲效三腿读数 = 特化 ×1.5 / 无甲 ×1.0 / 对位 ×0.5(甲B 对策 = dmg_b 腿——C3 联动网络 #6);attack_up 零泄漏腿:P_offense 变更只移动 base、A/B 分量逐字不变;敌方侧 percent 池零作用(hero→敌方向);cap 截断腿:15 格轮 A 池 = ca(截断入诊断计数、零明细行);轮末清零腿:下一结算 A/B 池 = 0;明细行 {baseline, attach} 分量可还原(充能池公开读值) | F6 / W3 / C3 联动网络 #6 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v2 | 2026-09-14 | 两件套迁移:按 v2 骨架重排(设计目的/系统范围/核心循环/领域章节/依赖与输出/P0 闭环/P1·P2 后置/调参表/验收标准)——设计推演(F0–F6 推导、候选取舍、体验锚点论证)与审计元数据(复核注记/对账登记义务清单/AC 覆盖矩阵/缺口预检/开放悬案)迁出至 `design/gdd/analysis/b1-settlement.md`;规则/数值/AC/EC/依赖零变更,AC 编号沿用 | +| 跨评审 B1 重算批 | 2026-09-14 | /review-all-gdds 🔴-1 后半(registry 同批重登回执):**F0/F1/F4/F5 按现行三输入重推**(duration=1〔两级奖励批〕、m_d=−30〔减伤墙批〕、C3 v3 曲线 d0.25/t0.88 换锚——原 A1 参考行退役)——**条款③两参考板形翻为破线**(锚行 ≈0.48×/0.47×、期望口径 E_dig=4.5 → 0.60×/0.58×;满配 ≈0.16×),旧"通过"回执(≈2.0×/2.6×)退役为历史记录、破线升格 C3 曲线级约束(V_safe 侧杠杆重议——禁独立调参联动网络);E_dig 重推 7.2→4.5(格子池加权,注入池不摊入)· 条款⑤四态重推(L1 −28.7/+11.3 等;保守地板需求 110.3/166.8,C3 裁决②维持)· **F1 对照表按现行行集重登**(两级行值 8/16/40 + 注入池 + clear_blast;attack_up 死行注记清除——与 Core Rule 10 先手攻击对账;锚对照实物改 attack_up 行〔registry 回迁口径重跑兑现〕)· **F6 充能与护甲克制结算规格**(W3——累积时点/单轮 cap/不吃 percent/逐分量甲效查表 floor + AC-44 + F1 对照两行)· W6 挖掘期刷新改**每轮 cap 2**(RoundStart 重置——A3/registry/D1 同口径)· defense_up.magnitude/duration 纳入禁独立调参联动网络 · AC-22/36/37/39/40/41 改写 · 护栏 A/B 读数挂旧值链注记(W2 复核随 C3 收口批)· 悬案 9/缺口预检 3·7 闭合 · 核心规则表增依据列(lint R8 引用列迁移) | +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-6 回执,registry 同批):战斗开始序列增 **encounter 掷骰步**(RngService 封闭流恰一 scope——unit_count 区间骰定本战实际数量,spawn 增 override 参数 n)· Core Rule 1 零随机扩界注记(实例化骰在结算步流之外,可回放性不受影响)· AC-5 增 encounter scope 腿 · SettlementTable +4 字段(damage_a_per_cell / damage_a_cap / damage_b_per_flag / armor_effectiveness——护甲克制批转正,机制与结算归 B1、C3 投值)· 条款⑤ C3 裁决回执行(约束包② 采信 + clear_blast C=40 缺口重推 6.1/51.8)· 敌种键更名 goblin/bugbear(DND 批对齐) | +| v1.1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;散文/枚举/边界情况/AC 转表格;流程转字符画;公式、Variables 与验算数据全保留;语义与 Rev 1 及后续拍板批对齐,零规则变更 | +| D4 修订 | 2026-09-12 | 刷新决策前移挖掘期:新增"挖掘期刷新"出口(refresh_board 编排——request_refresh 扣费 → 换图全序,无结算段、轮次不终结),原 Waiting 期刷新代理调用弃用 | +| 2026-09-11 拍板批 | 2026-09-11 | 主角先手攻击(hero_attack 来源 + F3⓪)、两级奖励裁决(instant_aoe/instant_heal 当场兑付)、道具窗口单件制(>1 件 = 编排违例取首件)、库存关内延续(battle_over 不清库、新增关卡末 level_clear 清库编排) | +| Rev 1 | 2026-09-10 | design-review 8 项 blocking 核销:完美排雷落真实双份交付载体 + 高光行有效兑付语义/零兑付降级;条款⑤计价模型重推为挖穿残余池口径、verdict 翻转 → C3/B4 约束包 + 多敌验算腿 + ι→1 域守卫 + 护栏 B 输入登记与双读数;流水协议 target/effect_id/pool_value 字段分层 + enemy_action/flag_miss 来源枚举;hit=false"标旗未中"零值行(每雷实例一次去重);P_perfect ÷100 换算成文 + 载体域收窄 {100,200};AC-18 腿 2 改写;F4 V_safe 标签更正 + 期望口径敏感性读数;clamp 统一八字段——明细见 reviews/b1-settlement-review-log.md | +| v1 | 2026-09-10 | 一次会话定稿:F0–F5 + 43 条 AC + 23 条上游义务落位;F0 归一口径裁决(每结算瞬时当量,m_w 值域 [60,85]/默认 −65)与高光双落(完美排雷 / 标中旗标记)即定 | diff --git a/design/gdd/b2-enemy-system.md b/design/gdd/b2-enemy-system.md new file mode 100644 index 00000000..588265d1 --- /dev/null +++ b/design/gdd/b2-enemy-system.md @@ -0,0 +1,605 @@ +# B2 敌人系统(Enemy System / 战斗单位系统) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents +> **承载支柱**:P3 主(雷→敌方优势的收件人与同源兑付面)· P2(敌方增益行可归因)· P1(视觉推测型读怪 = 规划素材)· P4 间接(地牢住户) +> **一句话**:B2 定义战斗双方是谁、多强、带什么词条、如何受伤与死亡——雷的代价的收件人。 +> **复核注记**:lean 门禁下 systems-designer(Formulas)/ qa-lead(AC + 缺口预检 7 项)已委托起草;creative-director 与 art-director 未经咨询——生产前人工复核体验锚点情绪定位与 Visual/Audio、art-bible 一致性(art-bible §1–4 须在 D1 GDD 前创建,本节线索清单是其输入之一)。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 战斗域单位层(Layer 2 Core · MVP · 设计顺序第 7 位):定义"战斗双方是谁、各自多强、带什么词条、怎么受伤与死亡";持有 D2 EnemyTable 回填权,承载主角与敌人共用的战斗单位接口。缺本系统则踩雷优势无受益者(P3 断)、胜负判定无主体、难度曲线无施加对象 | +| 拥有 | 单位状态(hp/max_hp/存活/行为环指针)与查询/受击/推进接口 · EnemyTable/HeroTable schema 与双敌种行回填(行键 (level_index, node_depth, enemy_id))· 行为环语义 · 目标选择规则(固定顺位)· 伏击量纲定稿与 advantage_per_round_cap 表值 | +| 不拥有 | 结算编排与伤害消费语义(B1 SettlementTable)· 状态实例容器(B3)· 奖励内容与掉落(B4)· 棋盘规则(A1/A2)· 单位成长曲线与难度修饰(C3)· 遭遇构成(C1)· 呈现(D1/B5/D5)· 单位/词条类结构与事件载体实现(实现期 ADR 域) | +| 上游输入 | D2 EnemyTable/HeroTable 行值(ConfigDB 单点读取、R1 行解析)· hero hp 入参(编排方供给)——A2/A3/A1 事实均经 B1 结算编排转化,零直接接口 | +| 下游输出 | 单位状态查询/受击/推进接口(B1 消费)· 意图视觉映射键 enemy_id 与视觉状态清单(D1/B5 消费)· 伏击量纲 + cap 表值 + k 可算性(B1 条款 3 对账输入)· 行为环教学口径(D5) | +| 硬约束 | 零随机(rng_service 流封闭)· 零 UI 依赖 · 零主动事件订阅 · 悬空引用致命、零软回退 · 行为环敌单位专属 · 单位接口调用权集中归 B1 · 胜利路径 = 基础攻击(恒在)+ B4 奖励侧规划面伤害共同承载——"敌人全灭 → 战斗胜利"可判定 | + +## 体验锚点 + +> 情绪目标一句话:**"对面是个看得懂的对手,不是一个血条"**——威胁可推测、可归因、可拆解;玩家不操作敌人,但每一轮都直接感受它。 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P3 同源的对面对象 | "赌桌对面的那个存在" | 结算明细"敌方获得伏击 +3(第 4 轮 · 雷)"的行可指认——敌人强度是扫雷决策的镜像,威胁有源可溯 | +| P1 可推测的威胁 | "它弓起背了,下一拍是重击" | 读怪推理进入挖掘决策(形态↔行为映射稳定可学,MVP 双环最长 3 拍);不预告精确数值——数字在结算明细兑现,结算前的世界靠观察而非读表 | +| P2/P3 的拆台兑现 | "它强是我喂的,它弱是我拆的" | 标中旗 weaken 与伏击行同框、同一呈现语言,落在同一个它身上 | +| P4 地牢住户 | "越下越险的刻度" | 深层敌人更凶(C3 施加),给下探氛围一个可见刻度 | + +- 反向护栏:威胁绝不能是无源暗账——玩家说不出"它为什么变强 / 这一拳为什么这么重"即归因链失职(P2/P3)。 +- 视觉推测型对偶护栏:线索必须稳定可学(形态↔行为映射每 run 恒定,D1 义务②);任何让线索重掷或不可学的呈现设计默认砍掉(P1)。 +- 血条/攻击力数字可读性适用视觉锚 1(暗色底上高对比)。 +- 参考感受合成:Peglin 固定招式轮替(威胁因可预测而可规划——确定性环是可学习性来源)× 怪物猎人读招(前摇姿态承载威胁信息、无数值面板——视觉推测的同构)→ 本作 = 确定性环 × 视觉推测;反向对照(要避开的感受)= 半随机、不可归因、重掷线索的"猜怪"呈现。 + +## 全局流程 + +B2 编排被动——调用权集中归 B1;下图从 B2 视角画单位级行为环与生命周期出口。 + +### 敌人行为环 + +``` +战斗开始(B1 编排调用,B2 侧语义) + │ ① 解析遭遇行 (level_index, node_depth) —— D2 R1 进度键 floor + enemy_id 精确匹配(悬空致命) + │ ② 行值快照 → 实例化本战实际数量 n 个敌单位(行主序生成序——B1 encounter 骰 override;定值行 n = unit_count)+ 主角(hp 入参) + │ ③ 全部指针指向环首:beat = pattern[0]、steps = 0 + ▼ +每次结算(B1 编排驱动——B2 零主动;推进域 = 每存活敌单位恰一次 advance,主角无行为环不在推进域) + │ ⓪ 主角先手攻击 → apply_damage(顺位最前存活敌, 伤害) [战斗段固定动作,非行为环] + │ ① 兑付读值:query_attack(u, L, m_amb) → {baseline, attach, total} + │ ② 受击上报:apply_damage(hero, 减伤后终值) —— 调用序钉死:倒序会读到下一拍(B1 义务①) + │ ③ 推进:advance(u) —— 存活才推进(先作用后推进) + ▼ +当前拍作用(每存活敌单位) + ├─ attack:本结算对主角造成 atk 点伤害 + ├─ charge:本结算不作用(威胁空拍、规划窗口;不兑付伏击、不读池) + └─ heavy :atk × heavy_multiplier 对主角 + ▼ +拍作用完成 → steps + 1 → beat = pattern[steps mod 环长](环尾回环首;跨轮次、跨扫雷图延续) +轮到前被杀 → 本拍不打、指针冻结(死亡即不动手;MVP 无复活 → 永久冻结) + ▼ +battle_over(B1 调 despawn_all)→ 全部单位 Despawned(状态消亡,不跨战斗) +``` + +### 出口分发 + +| 出口 | 触发 | 动作/去向 | 约束 | +|---|---|---|---| +| 单位死亡 | hp = 0(判定时机归 B1) | 移出顺位、不再作用、不计入"未灭";轮到前死 → 指针冻结、行动后死 → 已推进 | 不可逆(MVP 无复活);Dead 句柄保留只读可查(query_beat 冻结值) | +| 敌全灭 | 存活敌集 = ∅ | "敌人全灭"成立 → B1 胜利判定素材;单体/AOE 无有效目标;敌侧伏击池随轮末衰减自然清空 | 判定逻辑归 B1 | +| 主角死亡 | hero hp = 0 | 战斗失败判定素材(判定时机归 B1) | battle_over → 全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态 | +| battle_over | B1 编排 despawn_all | 全部单位状态消亡、不跨战斗;Despawned 后唯一出口 = 重新 spawn(新战斗) | 重复 despawn_all → false + 诊断;Despawned 为逻辑失效态(失效句柄查询空 + 诊断) | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:主角和敌人共用同一套受伤与死亡规则 +**人话**:这条管的是战斗里所有"单位"的通用身体规则。战斗场上恰有两类单位:主角一个、敌人若干,共用同一套身体语义——每个单位有当前血量与血量上限,挨打就是血量往下减,血量到 0 即死亡。超上限、负数、一刀砍超的部分怎么处理,以及"这一下究竟何时算死",都归战斗结算系统(B1)的结算公式域裁决。 + +B2 只提供受伤上报与状态查询接口、如实记账,"谁在何时对谁造成多少"的流水账本也在 B1。主角血量归零 = 战斗失败的判定素材,敌人全部归零 = 战斗胜利的判定素材——判定本身同样归 B1。 +**举证**:无计算。clamp/负值/溢杀语义与死亡判定时机的成文义务登记为 B1-③(B2 侧 out-of-scope);单位接口、胜负素材与越界拒绝验收见 AC-1/AC-2/AC-6。 + +#### 规则 2:一场战斗 1–3 个同种敌人,敌方效果对全体生效 +**人话**:这条定的是"一场战斗有几个敌人、效果怎么作用"。MVP 每场战斗 1–3 个敌人,全部是同一种敌、属性完全相同——由敌人表同一行实例化,按行序排出先后;数量是敌人表里的调节旋钮。战斗中敌人只减不增(没有召唤/增援/分裂)。 + +敌方吃到的增益/减益是"对全体生效":作用于所有存活敌人,层数进一个共享池——这是与状态系统现接口零改动兼容的 MVP 路径。"只作用于某一个敌人"的单体寻址暂不开放,登记为状态系统的扩展位,等第一个单体效果上线时补列。 +**举证**:无计算。构成/同质性/作用域/单调性细则见下方实现参照表;target_ref 扩展位义务登记为 B3(首个单体效果上线时补列,随批 B1 归集语义/D1 呈现裁决);战斗内不增员见 EC5;验收 AC-1/AC-5/AC-9。 + +### 规则 2 的实现参照——多敌模型 + +| 项 | 内容 | +|---|---| +| 构成 | 1–3 个同敌种单位(unit_count int [1,3] 表字段 knob);战斗开始实例化、行主序生成序 | +| 同质性 | 战斗内敌单位同种同属性——同一 EnemyTable 行实例化 | +| 效果作用域 | side-wide:enemy 侧效果作用全体存活敌单位——层数为 side 共享池(F2 池共享不消耗);B3 现接口零变更兼容 | +| target_ref | 单体寻址登记为 B3 扩展位义务(首个单体效果上线时补列;随批:B1 归集语义/D1 呈现裁决) | +| 单调性 | 战斗内敌单位数 = unit_count 不增(MVP 无 summon/增援/分裂——EC5) | + +#### 规则 3:MVP 就两种敌人,每个关卡坐标只出一种 +**人话**:这条定的是敌人名录。MVP 恰两种敌人:地精(goblin)与熊地精(bugbear),数值全部 provisional(临时占位),MVP 名录定稿走索引门禁。敌人在敌人表里按"关卡序号 × 节点深度"坐标存放:每个坐标的行集里只许出现一种敌人——这是表结构校验,配出两种就加载报错;由此既没有"同一点随机挑一种"的选择规则,也没有引用悬空的风险。 + +行集逐坐标判定,同一关内随深度换敌种合法。MVP 分布只是最平直的合法示例:第 1 关全深度地精、第 2 关全深度熊地精——占位示范、非范本,难度调参勿照抄(防多样性无护栏),遭遇分布调参所有权归难度域(C3)。名录扩容排垂直切片/Alpha;"同深度多敌种怎么选"归遭遇构成(C1)/难度域,目前只是预留结构位。 + +**举证**:无计算。"每行集恰一敌种"(加载期致命)与字段域校验验收见 AC-12/AC-14;敌种 id 集合定值断言 {goblin, bugbear, ogre_chief} 见 AC-14(C3 修订批扩容三敌种);registry 物种注记对账见 AC-16③。 + +#### 规则 4:敌人按排好的剧本轮流出招,走到哪一拍零随机 +**人话**:每种敌人有一条排定的行动剧本(行为环):战斗开始指向剧本第一步,之后每结算一次走一格——轮到谁,谁先把当前这一拍的招打出去、再走格;走到剧本末尾回到开头;跨轮次、跨扫雷图延续,战斗结束随单位一起消亡。轮到它之前被打死:这一拍不出手、指针就地冻结("死亡即不动手")。全程零随机——走到哪一拍由结算次数唯一决定,同种敌人因此永远同拍。行为环是敌人专属:主角没有行为环,对主角调环接口一律拒绝。MVP 只启用攻击/蓄力/重击三种拍,后四类是预留位、配进表会被校验拒绝。 + +**举证**:无计算(推进是确定性函数)。指针/回绕/冻结/同步性质公式见 F1;"每结算恰一次推进"与结算内调用序登记为 B1-①;推进细则与预留值拒绝(fail-fast)见下方实现参照表;验收 AC-3/AC-4/AC-5/AC-15(预留值致命 manifest pattern_reserved)。 + +### 规则 4 的实现参照——行为环推进细则 + +| 项 | 细则 | +|---|---| +| 指针初始化 | 战斗开始指向环首:beat = pattern[0] | +| 推进时机 | 每次结算推进一格——先作用后推进(B3"先兑付后衰减"同款时序哲学);轮到该单位行动时仍存活才行动并推进 | +| 死亡口径 | 轮到前被杀 → 本拍不打、指针冻结("死亡即不动手",F1 承载;MVP 无复活 → 永久冻结、随 battle_over 消亡);行动完成后才死 → 已行动已推进 | +| 回绕与域 | 环尾回环首;指针 ∈ [0, 环长);环长 1(goblin)退化为恒等 | +| 延续 | 跨轮次、跨扫雷图延续(战斗域);battle_over 随单位消亡;除 despawn_all 外无任何重置入口 | +| 确定性 | 零随机——环推进是结算次数的确定函数(rng_service 流封闭,P1) | +| 专属域 | 行为环为敌单位专属:主角无行为环——advance/query_beat 等环接口对主角句柄一律拒绝(false / 空 + 诊断) | +| 实现域 | MVP 恰前三类 {attack, charge, heavy};guard/boost/hinder/summon 为预留语义位(垂直切片/Alpha),EnemyTable.pattern 校验拒绝预留值(fail-fast,A1 solver_rules 冻结先例)——启用走本 GDD 修订(枚举封闭性)非调参 | + +#### 规则 5:每踩一颗雷敌方叠一层伏击——一轮封顶、超出可见不生效 +**人话**:这条定的是踩雷对敌方的反噬。挖开雷格时,敌方全体获得一层"伏击"强化——MVP 伏击清单只有伏击这一条(状态系统表里 mine_hit 雷击来源的唯一效果行);将来加新词条要状态系统加行并连锁更新锚点,不属于 MVP。转化路径:棋盘上报"挖到雷"→ 结算编排把它转成一次伏击施加(作用全体敌人、共享层数池),每颗雷恰转化一次。单轮授予有上限(敌人表行值,默认 4):上限之内逐颗叠加;达到上限后,多踩的雷不生效,但逐颗记一行"溢出未生效"结算明细——惩罚不静默蒸发。 + +| 单轮伏击上限(cap 行值) | 行为 | +|---|---| +| 默认 4 | 前 4 颗雷各叠一层;第 5 颗起每颗记一行"溢出未生效"明细 | +| 调到 8 及以上 | 单轮雷数至多 8 颗、达不到上限——溢出协议自然不可达 | +| 0 | 该行敌种免疫伏击——每颗雷都记"溢出未生效",全部可见、全部无效 | + +**举证**:计算见 F3:granted = min(当轮新开雷数, cap)、overflow = 差值(守恒、零静默蒸发)、L = granted(dur=1 轮末全清)。每雷恰一次转化登记为 B1-②、溢出行协议登记为 B1-⑥;cap 默认 4 定稿与反灾难护栏绑定域(单轮 5–8 雷灾难轮)见 F3;cap 表值同时是 A3 F1 条款 3 挖掘风险不等式的 B2 供给表值(单结算自然边界口径见 F4)。验收 AC-8。 + +#### 规则 6:一层伏击 = 敌人每次攻击多打 3 点固定伤害 +**人话**:这条定的是伏击到底多疼:一层伏击 = 敌人每次攻击类行为(普攻或重击)额外多打 3 点固定伤害,按主角血量点数计。结算读法:某个敌人出手时,附加伤害 = 敌方池当前层数 × 3。两个要点:附加是后置加法、不进重击倍乘(倍率只乘基础伤害);蓄力拍不出手、不附加。伏击只活一轮——每轮结算清零、没有跨轮存量,所以跨轮累计没有表值上界;单次结算有自然上界(见举证)。 + +**举证**:上界算式:单结算单单位附加 ≤ cap × 3 = 12(默认);敌方全体结构上界 = unit_count × min(单轮开雷上界 8, cap) × 3(满配 3×4×3 = 36;默认单敌 = 12);跨结算累计 = 轨迹量、非表值、不设上界(dur=1 每结算清零)。伤害构成与合成序见 F2;等量换算与不等式闭合归 B1(F4);量纲定稿注记按接口漂移回写 registry status_effect.ambush(AC-16④)。 + +#### 规则 7:敌方打主角不用挑,玩家侧永远先打排最前的敌人 +**人话**:这条定的是"打谁"。敌方一侧没有选择可言:所有存活敌人各自按自己行为环的当前拍作用于主角——主角是己方唯一单位,天然是唯一目标。玩家/道具一侧打敌人用固定顺位:单体效果默认打生成序最前的存活敌人,它死了顺延到下一个;范围效果(AOE)打全体存活敌人,同样无需选择。这套规则供结算编排消费;道具自带"指定目标"语义的扩展归道具系统(B4)/结算域。顺位必须对玩家可读——"下一个被打的是谁"要能推测出来,这是呈现层义务。 + +**举证**:无计算(纯规则)。固定顺位供给面、死亡顺延与 u_c=1 退化(AOE ≡ 单体、合法非错误)验收见 AC-10;顺位快照 = 生成序、单体/AOE 分流消费登记为 B1-⑤;顺位可读呈现义务登记为 D1-③(呈现契约表 #3)。 + +#### 规则 8:敌人要出什么招靠看图推测,不直接报数字 +**人话**:这条定的是玩家怎么"读"敌人。界面上不直接显示敌人下一拍的行为与数值——威胁靠看图推测:它弓起背,下一拍就是重击。两条承重规格:"预告时序"(即将执行的一拍在挖掘阶段全程亮出)与"映射每局恒定"(禁止重掷,否则读怪退化成猜怪)——否则"抢在重击前击杀"窗口、"蓄力轮放心贪雷"教学、读怪幻想三者同时落空。精确数字只在结算明细兑现。线索清单与图像规格归呈现层(D1/B5),映射键 = enemy_id;MVP 读怪对象 = 两条环(最长 3 拍),学习成本低。 + +**举证**:无计算(规格非算式)。五条规格全文见下方实现参照表;线索清单/图像规格/映射全局恒定呈现义务登记为 D1-①②(登记 systems-index;映射键 = enemy_id,B3 effect_id→icon 同款契约);"不直接显示数值"的负空间 = 冒烟 API 封闭枚举(无意图数值 API);行为环可学性行为级证据 AC-3/AC-15。 + +### 规则 8 的实现参照——意图可见性五条规格(承重项) + +| # | 规格 | 内容 | +|---|---|---| +| ① | 图像↔行为映射稳定可学 | 同一敌种同一行为恒同一视觉状态(如 bugbear 蓄力拍 = 蓄力姿态) | +| ② | 形态暗示强度 | 敌种形态承载强度档位与环长的直觉编码("大个子多半有重击"式) | +| ③ | 映射每 run 恒定 | 禁止重掷——否则"读怪"退化"猜怪"(违反 P1 裁剪判据);映射键 = enemy_id | +| ④ | 精确数值在结算明细兑现 | P2 归因不变——预告面是图像、数字面是明细 | +| ⑤ | 预告时序锁定 | 视觉状态呈现即将执行的一拍(当前拍预告)——挖掘阶段全程可见;结算时不另设独立意图播片(结算明细是数字兑现面、非意图预告面) | + +#### 规则 9:主角数值在英雄表里——血量上限加恒在的基础攻击 +**人话**:这条定的是主角这边怎么打。主角战斗数值单独放一张全局单行的"英雄表"(本 GDD 是回填 owner,走 D2 接口漂移规则回写),两个字段: + +| 字段 | 域(knob) | MVP 值 | +|---|---|---| +| max_hp 血量上限 | int 1–999 | provisional,出处待查 | +| atk 基础攻击 | int 1–999;0 加载期校验拒绝 | 6(原型锚 HERO_BASE_ATK provisional,归 C3) | + +基础攻击恒存在、不许为 0:零基线乘任何百分比增益恒为零,这个死锁从数据层就进不来、开发期任何内容态都不可能出现。战斗节奏:每次结算的战斗段,主角先手攻击顺位最前的存活敌人——先于敌方行动;攻击是固定动作、不是行为环一拍(主角无环),伤害算式/乘区/明细行协议归结算系统战斗段。攻击增益(attack_up,+50 percent)的唯一消费面就是这次先手攻击;道具伤害是恒定表值、不吃修正。 + +战斗开始时主角血量由编排方入参供给,跨战斗是否保留血量归关卡结构域、本文不定义(MVP 冒烟以血量上限入参);后续解锁成长归 C4、曲线调参归 C3。胜利路径 = 基础攻击 + 奖励侧规划面伤害(囤刀、抢杀、爆发时点的决策素材)共同兑付"敌人全灭 → 战斗胜利",每一分伤害逐行标注来源(hero_attack / 道具逐件实例)。 + +**举证**:无新计算——数值皆表值。schema 域/单行校验验收 AC-13/AC-14;hero hp 入参越界拒绝见 EC16(AC-2);attack_up 消费面口径(B4);跨战斗 HP 持久性归 C1/C2;atk 禁独立调参联动见 Tuning Knobs;HeroTable 新增的接口漂移回写义务 R-4 见依赖节。 + +#### 规则 10:开战按坐标读表生成敌人,数值是生成那一刻的快照 +**人话**:这条定的是敌人从哪来、数值从哪来。战斗开始时,编排方给出遭遇坐标(关卡序号 + 节点深度),敌人系统据此从敌人表精确匹配一行,实例化"本战实际数量 n"个敌单位——n 由编排方开战掷的遭遇骰从表里的数量区间 [unit_count, unit_count_max] 定出(定值行取表值);主角同时以血量入参生成。 + +实例化拿到的是当时表值的快照:之后热重载改表,已生成的单位一概不受影响。难度成长走双通道:表值本身随坐标加深而变 + 难度系统(C3)的整体修饰——修饰施加点已定为"行值快照之前套乘",MVP 难度表为空、零修饰;B2 一律按实例化后的数值运作。 + +**举证**:无计算(数值皆快照表值)。行解析 = D2 R1 进度键 floor + enemy_id 精确匹配、悬空致命(EC13/EC14 → D2 通道);坐标越界前置守卫见 EC14;快照语义同 B3 EC9 先例、热重载与投影拷贝判别验收 AC-11(EC12);难度修饰接口已定——registry `difficulty_table` 条目(算式归 C3)。 + +#### 规则 11:敌人系统自己不主动做事,一切等结算编排来调 +**人话**:这条管的是敌人系统的姿态:B2 不产生随机数、不碰界面、不订阅任何事件——挖雷、插旗等棋盘事实不直接通知 B2,而是经结算编排转化后由它来调用。B2 只摆出两组能力:查状态(血量、当前拍、顺位、存活),受动作(受击、推进、消亡);什么时候调、按什么顺序调,全部由结算编排(B1)说了算——与状态系统"授予集中在编排"是同一条原则。 + +**举证**:无计算。零随机行为级硬证据 = 同序列重放两遍逐位相同(AC-15);零 UI 依赖/零事件订阅/零 B3 符号引用静态扫描见 AC-17(A2 事件面 referenced_by 不含 B2 的代码侧对应);调用权集中与推进恰一次登记为 B1-①。 + +## 枚举 + +### 行为拍类型(封闭枚举,七类) + +| 值 | MVP 状态 | 语义 | +|---|---|---| +| attack | 实现 | 本结算对主角造成 atk 点伤害 | +| charge | 实现 | 本结算不作用——威胁空拍、规划窗口;不兑付伏击(不读池) | +| heavy | 实现 | atk × heavy_multiplier | +| guard | 预留(校验拒绝) | 自防 | +| boost | 预留(校验拒绝) | 自体强化 | +| hinder | 预留(校验拒绝) | 妨害雷图——敌方向棋盘施加变异类妨害(Peglin"干扰弹板"的本作转译) | +| summon | 预留(校验拒绝) | 召唤增援 | + +- pattern 环长 [1, 8];MVP 校验域 = {attack, charge, heavy};预留值校验拒绝(fail-fast);启用预留值走本 GDD 修订(枚举封闭性)非调参。 + +### MVP 双敌种内容行(值全部 provisional) + +| 敌种 | 行为环 | 基线(provisional,出处待查) | +|---|---|---| +| goblin(地精) | [attack](平凡环,环长 1) | hp/atk 暂按原型参考 18/4 占位 | +| bugbear(熊地精) | [attack, charge, heavy](三拍环) | heavy_multiplier = 2 | + +### EnemyTable schema(D2 回写义务——键 (level_index, node_depth, enemy_id)) + +- 进度键 floor(D2 R1)+ 实体键精确匹配;每行集恰一敌种(行集内 enemy_id 唯一 = 表结构校验,违规加载期致命);本表是进度成长表(A1 BoardGenTable 同型,无 Rule 4 豁免);表非空(D2 Rule 6);精英/Boss 编码位预登记 OQ7——难度三维的精英/Boss 修饰在本键结构下无编码位,C1 定稿时强制核对。 + +| 字段 | 类型/范围 | 语义 | +|---|---|---| +| enemy_id | String(行集内主键) | 实体键精确匹配(D2 Rule 4);意图视觉映射键(Rule 8③) | +| display_name | String | 图例/HUD/明细用名(本地化归 D6) | +| unit_count | int [1, 3] | **遭遇区间下界**(knob;同敌种——C3 遭遇随机化:区间 [unit_count, unit_count_max] 经 B1 encounter 骰定本战实际数量) | +| unit_count_max | int [unit_count, 3],可省略 | 遭遇区间上界(缺省 = 定值,旧行零迁移——C3 修订批新增字段) | +| armor_type | enum {none, a, b},缺省 none | 护甲类型(克制矩阵 = SettlementTable.armor_effectiveness——伤害A/B 充能的克制消费面;MVP 映射 goblin=none / bugbear=a / ogre_chief=b) | +| hp | int [1, 999] | 单位最大生命基线(knob;难度修饰前) | +| atk | int [0, 999] | 攻击基线(knob;attack 行为伤害基值;0 合法 = 纯伏击伤敌种) | +| heavy_multiplier | int [1, 16] | heavy 倍率(knob;无 heavy 拍的敌种不消费——非校验项,B3 max_stack 先例) | +| pattern | StringList(环长 [1, 8]) | 行为环(元素 ∈ {attack, charge, heavy} 为 MVP 校验域;guard/boost/hinder/summon 预留、校验拒绝) | +| advantage_per_round_cap | int [0, 16] | 单轮伏击授予上限(knob,默认 4 定稿;F1 条款 3 表值;0 = 该行敌种免疫伏击授予的合法极端) | + +### HeroTable schema(单行全局,D2 新增 schema 行——接口漂移回写义务见依赖节) + +- 主角不随关卡自动成长,成长 = 天赋 C4 / 奖励侧;Rule 4 豁免同 ActionEconomyTable 先例。 + +| 字段 | 类型/范围 | 语义 | +|---|---|---| +| max_hp | int [1, 999] | 主角最大生命(knob,MVP 值 provisional 出处待查) | +| atk | int [1, 999] | 基础攻击(knob,MVP = 6 原型锚 provisional 归 C3;0 = 加载期校验拒绝——零基线死锁自 schema 层杜绝) | + +## 数值结论与推导 + +> 全部公式零随机、同输入恒同输出(可回放前提);本节只含机制公式——平衡归属见文末显式排除声明。术语:拍 = 行为环里的一格行动(bugbear 环 [攻击, 蓄力, 重击] 有三拍);伏击池 = 敌方侧共享的伏击层数(本轮踩几颗雷就几层,轮末清零);溢出 = 单轮踩雷超过上限的部分,转化为明细行可见、不生效。 + +### F1 行为环推进(ring_advance) + +**结论**:beat(u) = pattern[steps_u mod 环长]、行动完成后 steps_u + 1;指针恒 ∈ [0, 环长)、环长 1(goblin)退化为恒等;死亡即不动手——轮到前被杀本拍不打、步数冻结(永久,随 battle_over 消亡);同种单位同指环首且每结算各恰推进一次 ⇒ 存活者永远同拍(同步性质定理——任何死亡时序都不会让幸存者乱拍)。 + +#### 推导 + +``` +beat(u) = pattern[steps_u mod 环长] 行动前读拍;战斗开始 steps_u = 0(指向环首) +steps_u' = steps_u + 1 行动完成后推进(先作用后推进) +轮到前被杀 → steps_u 冻结(死亡即不动手);行动完成后才死 → 已行动已推进 +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 行为环 | pattern | StringList | 环长 [1,8];MVP 元素 ∈ {attack, charge, heavy} | EnemyTable 行值快照(Rule 10) | +| 已行动次数 | steps_u | int | [0, ∞) | 结算次数的确定函数(零随机) | +| 当前拍 | beat | enum | pattern 元素 | 本结算该单位的行动内容 | + +- 同步性质(定理):同种单位同指环首、每结算各恰推进一次(B1 义务①)→ 存活者永远同拍——任何死亡时序都不会让幸存者乱拍。 +- Output Range:指针恒 ∈ [0, 环长);环长 1(goblin)退化为恒等。极端:heavy 拍前被杀 → 重击永不兑现(冻结于该拍)——玩家"抢在重击前击杀"的读怪收益由此产生(P1)。 +- Example:bugbear——结算 1 攻击(p→1)→ 结算 2 蓄力空拍(p→2)→ 结算 3 前被主角击杀 → 重击不兑现、指针冻结于 2;若结算 3 行动后才死 → 重击已兑现、p 回绕 0。 +- 边界注记:归 B2——指针所有权、存活校验、冻结不变式、环首初始化、跨轮跨图延续;归 B1——死亡判定时机、结算内先后次序、每结算恰一次推进(义务①)。 + +### F2 攻击行为裸伤害构成(raw_attack_damage) + +**结论**:attack 拍 D = atk + m_amb × L、heavy 拍 D = atk × heavy_multiplier + m_amb × L、charge 拍 D = 0(不读伏击池);伏击附加是后置加法、不进倍乘(合成序钉死);池共享不消耗(同结算多敌读同一 L);B2 向 B1 供 (基线, 伏击) 分量二元组。 + +#### 推导 + +``` +attack 拍:D = atk + m_amb × L 基线 + 伏击附加(m_amb = StatusEffectTable.ambush.magnitude,provisional 3) +heavy 拍:D = atk × heavy_multiplier + m_amb × L +charge 拍:D = 0 不读伏击池——空拍零兑付 +合成序:伏击附加 = 后置加法,不进倍乘 +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 攻击基线 | atk | int [0,999](快照) | EnemyTable 行值 | 0 合法(纯伏击伤敌种) | +| 重击倍率 | heavy_multiplier | int [1,16](快照) | 仅 heavy 拍消费 | 无 heavy 拍不读(非校验项) | +| 伏击层数 | L | int [0, min(雷数, cap)] | 兑付时读值(B1 经 query_active) | dur=1 → L = 本轮受理授予数,无跨轮存量 | +| 伏击量纲 | m_amb | int flat ≥ 1;provisional 3 | B3 行值 | 量纲 = 主角 HP 点数(本 GDD 定稿);锚验收归 B4/B1 | + +验算表: + +| 验算 | 读数 | 说明 | +|---|---|---| +| bugbear(atk=4, mult=2)heavy 拍 L=4 | 4×2 + 12 = **20** | 合成序判别非平凡:塞进倍乘得 (4+12)×2 = 32 ≠ 20;L=4 = cap=4 默认下游戏态上界(域内判别值);query_attack 的 L 为调用方注入的判别探针、B2 不校验其游戏态域(游戏态域由 F3 预截流承载——探针取值与游戏态域分离,实现期若给 query_attack 加 L 域守卫须回改 AC-7) | +| goblin(atk=4)本轮开雷 3 颗 | D = 4 + 9 = **13** | attack 拍基础例 | +| 满编 3 goblin、L=4 | 各 4+12 = 16、合计 48(伏击分量 36) | 多敌共享读值例 | + +- Output Range(默认表值,cap/atk/m_amb/mult 调参后随之重算):goblin attack ∈ [4, 16];bugbear attack ∈ [4, 16]、heavy ∈ [8, 20]、charge ≡ 0(cap=4 封顶 L=4)。 +- 池共享不消耗:同一结算多个敌人的攻击各自读同一个 L(共享、非均分、非先到先得);层数扣减唯一入口 = B3 轮末衰减(dur=1 轮末全清)。 +- 分量供给建议:B2 向 B1 供 (基线, 伏击) 二元组而非合后标量——结算明细"伏击 +N"的 P2 归因行需要分量边界。 +- 极端:charge 拍零兑付 → 蓄力轮授予的伏击层当轮全部蒸发(合法推论;反向 = 读怪奖励"蓄力拍前放心贪雷"——D5 教学条目);无负值分支(clamp/地板归 B1)。 +- 量级注记:"L≥2 伏击分量过半"仅 attack 拍成立(6/10);heavy 拍因基线 ×mult 过半点后移(6/14 < 半)——支配边界 L* = ⌈atk×mult/3⌉ 与"过半"口径按拍别分别读取。 +- 边界注记:归 B1——L 读值、clamp/负值/溢杀、hero 侧减伤合成次序、死亡判定与"这一下是否兑现"。 + +### F3 伏击授予与溢出(ambush_grant_overflow) + +**结论**:granted_r = min(当轮新开雷数, cap)(cap 判定在授予前——B1 预截流);overflow_r = 当轮雷数 − granted_r(每颗溢出雷一行"溢出未生效"明细、含来源);L_r = granted_r(debuff 只活一结算:当轮兑付一次、轮末全清、无跨轮存量);守恒 granted + overflow = 当轮雷数、零静默蒸发;max_stack = 16 结构不可达(双重不可达——死护栏非死字段)。 + +#### 推导 + +``` +granted_r = min(当轮新开雷数, cap) cap 判定在授予前(B1 预截流) +overflow_r = 当轮新开雷数 − granted_r 每颗溢出雷一行"溢出未生效"明细(含来源) +L_r = granted_r debuff 只活一结算:当轮兑付一次、轮末全清、无跨轮存量 +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 当轮新开雷数 | mines_r | int | [0, 8](结构界 = AP) | round facts 经 B1 收集;复开不可能(A2 棋盘面语义) | +| 单轮伏击授予上限 | cap | int(行值 knob) | [0,16],默认 4 | 0 = 该行敌种免疫伏击(全溢出可见);F1 条款 3 供给值的来源字段 | +| 当轮授予数 | granted_r | int | [0, 4] | B1 编排期逐雷 apply_effect;每雷恰一次转化(义务②) | +| 溢出量 | overflow_r | int | [0, 8] | 明细行数;呈现归 B5/D1(义务⑥) | + +验算表: + +| 场景 | granted | overflow | 说明 | +|---|---|---|---| +| cap=4、开雷 6 | 4 层 | 2 行 | 本结算每个攻击行为附加 +12 | +| cap=4、开雷 5–8(灾难轮) | 4 | 1–4 | cap 反灾难护栏绑定域 | +| cap=0、开雷 m | 0 | m | 免疫极端——全溢出、伏击全无效但全部可见(P2 诚实) | + +- cap 默认 4:反灾难护栏——绑定阈 = 单轮 5–8 雷灾难轮,溢出协议获真实消费域。 +- 结构不可达验证:单轮 L ≤ min(8, cap=4) = 4 < 16,且 dur=1 无跨轮存量 → B3 满层拒绝对 ambush 行双重不可达(不可达性由 dur=1 ∧ AP ≤ 8 承载、与 cap 无关——M=16 是死护栏非死字段)。 +- 重验触发:ambush.duration ≥ 2 ∨ AP 经济改动 ∨ ambush 新增非 mine_hit 授予源(旗源授予不计入 mines_r、cap 预截流漏账 → L 可达 16 触发 B3 满层拒绝、与 F3/B3 双溢出协议冲突——OQ6 标错惩罚采纳时强制重验)。 +- 边界注记:归 B1——预截流编排、授予次序(行主序雷格序为建议序)、每雷恰一次、溢出行协议。 + +### F4 条款 3 供给口径(clause3_supply) + +**结论**:每结算敌方伏击暴露 = k × L × m_amb(k = 本结算存活敌中攻击/重击拍的单位数,确定性可算);每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb(cap > AP 界时 cap 腿松弛);跨结算累计 = 轨迹量、非表值、不设上界(dur=1 每结算清零);B1 对账取"每结算"口径即可闭合、无需战斗长度假设。 + +#### 推导 + +``` +每结算敌方伏击暴露(精确式)= k × L × m_amb k = 本结算存活敌中攻击/重击拍的单位数(确定性可算) +每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb + cap > AP 界时 cap 腿松弛——默认 3×min(8,4)×3 = 36;cap≥8 时真可达上界 72 而非 144 +跨结算累计 = 轨迹量,非表值、不设上界 dur=1 每结算清零、无存量累积;战斗再长不叠加同一层 +``` + +- 上界读数:默认满配 3×min(8,4)×3 = 36、cap ≥ 8 时真可达上界 72 而非 144;默认行值 unit_count=1 下 = 12。 + +- 口径依据(用户定稿原话转写):"不设(每战)上界——踩雷的 debuff 只存在一回合:在一次结算中有效,之后就没有 debuff 了。"——debuff 单结算寿命 = 自然边界。 +- B3 红警闭环:ambush 量纲已由 F2 供给(registry OQ2 关闭条件);条款 3 左臂(安全格 buff 当量)载体 = 主角基础攻击(恒在,attack_up 复活)与 B4 奖励行值;闭合回执见 registry clause3_supply(B1 F4)。 + +方向性预检验算表(provisional——真实对账 = A1 actuals p10 + B1 归一表): + +| 项 | 读数 | 说明 | +|---|---|---| +| 盲猜端单格 P(雷) | ≈ 0.5–0.7 量级 | 有信息挖掘端趋 0(A1 参考:密度 0.10 / target 0.95)——不等式真实战场在残余盲猜集 | +| 右臂(cap=4) | 0.64 × 12 ≈ 7.7 | 条款 3 右臂代理读数 | +| 左臂(条款 2 最低档锚 +50pp) | 0.36 × 50 = 18 | 条款 3 左臂代理读数 | +| 方向稳健裕度 | ≈ 2.3× | registry clause3_supply.direction_precheck 同步回写 | + +- 量级注记(支配边界):atk=4 时 L ≥ 2 伏击分量即过半——常态支配的修正入口 = 敌种 atk 曲线(C3 所有权)或 m_amb 锚(B4/B1 验收),本 GDD 权限外、仅登记方向;cap=4 只治尾部灾难(5–8 雷轮)。 + +### 显式排除声明 + +| 对象 | 归属 | +|---|---| +| 伤害 clamp/地板/溢杀、乘区合成、减伤读法、死亡判定时机 | B1 SettlementTable | +| F1 条款 3 不等式闭合对账(输入 = A1 actuals p10,非 target 参数) | B1 单一 owner | +| m_amb(+3)行值锚验收、weaken 档位 | B4/B1(A3 条款 2/3) | +| 敌种 hp/atk/pattern 曲线数值、难度修饰 | C3 | +| 伤害道具/奖励池(hero 侧伤害来源) | B4 | + +B2 只对"套路怎么转、一下打多少的两块拼图、一轮吃几层、交给 B1 对账的口径"负责。 + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Unspawned | 构造后未实例化 | 无接口副作用;唯一出口 = B1 编排实例化 | +| Alive | 实例化(行值快照 / hp 入参) | 可被指定目标(顺位)、按行为环作用、计入全灭判定;受击 hp 递减 | +| Dead | hp = 0(判定时机归 B1) | 移出顺位、不再作用、不计入"未灭";不可逆(MVP 无复活);句柄保留只读可查(query_beat 冻结值) | +| Despawned | battle_over(B1 编排 despawn_all) | 全部单位状态消亡、不跨战斗;逻辑失效态——失效句柄查询返回空 + 诊断、作用接口拒绝;唯一出口 = 重新 spawn(新战斗) | + +不变式(debug 断言口径):hp ∈ [0, max_hp];敌单位数 = unit_count(战斗内不增——summon 未实现);行为环指针 ∈ [0, 环长)(Alive 期间按结算推进;charge 空过是环位置语义、非独立状态);违规 = debug 断言中止(进程致命保留给 D2 数据通道)。行为级证据 = AC-1(数)、AC-3(指针)、AC-4/AC-6(hp floor)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 敌人在轮到它行动前被击杀 | 本拍不打、指针冻结("死亡即不动手");行动完成后才死 → 已行动已推进(heavy 已兑现) | F1 / Rule 4 | +| EC2 同种单位部分死亡 | 存活者永远同拍(同步性质定理)——幸存者轮转不受任何死亡时序干扰 | F1 | +| EC3 全体敌单位死亡 | 存活敌集 = ∅、"敌人全灭"成立、胜负判定归 B1;单体/AOE 无有效目标;敌侧伏击池随轮末衰减自然清空(无兑付者) | Rule 1 | +| EC4 主角 hp = 0 | 战斗失败判定素材(判定时机归 B1);battle_over → 全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态 | Rule 1 | +| EC5 战斗中敌单位数 ≠ 本战实际数量 n | 结构不可能(MVP 无 summon/增援/分裂;n = B1 encounter 骰定值——区间语义下实例化后仍不增)——出现即 debug 断言中止(进程致命保留给 D2 数据通道) | 实例不变式 | +| EC6 敌种行 cap = 0(免疫伏击的合法极端) | granted ≡ 0、每颗雷各一行"溢出未生效"明细(全部可见、全部无效——P2 诚实);该遭遇条款 3 右臂仅剩基线伤 | Rule 5 / F3 | +| EC7 单轮踩雷 5–8 颗(灾难轮) | granted = 4、溢出 1–4 行;本结算每个攻击行为附加恒 +12 | F3(cap 反灾难护栏绑定域) | +| EC8 全部存活敌同拍蓄力(charge 轮) | 零兑付(k=0);该轮新踩的雷照常授予、轮末照常全部蒸发(合法推论 + 读怪奖励——D5 教学条目已登记) | F2/F3 | +| EC9 敌单位在 heavy 拍前被杀 | 重击永不兑现、指针冻结于 heavy 拍——"抢杀窗口"读怪收益(P1) | F1 | +| EC10 敌种 pattern 环长 = 1(goblin) | mod 1 恒等 → 每拍 attack、无自然空窗(兑付率 ρ=1——该敌种伏击威胁无衰减节拍) | F1 | +| EC11 战斗跨多张扫雷图 | 单位状态(hp/指针)与敌侧伏击池跨图延续(换图/刷新不清空——B3 EC8 同款);雷格兑付资格随图消亡、新图新雷新资格(A3/A2 域,B2 无从感知也无须感知) | Rule 4 | +| EC12 战斗中热重载 EnemyTable/HeroTable | D2 战斗中 reload 拒绝门(D2 EC8);已实例化单位不受任何表重载影响(行值快照) | Rule 10 | +| EC13 行集缺行 / 请求敌种悬空 | D2 R1 实体键精确匹配 → 悬空引用致命(D2 Rule 6 通道);B2 无软回退(错误配置上线比崩溃更伤——P1/P2 信任底线) | D2 R1/Rule 6 | +| EC14 遭遇坐标越界(level_index < 1 / node_depth < 0) | D2 R1 前置守卫致命(D2 EC14)——B2 不自行 clamp | D2 EC14 | +| EC15 unit_count = 1 | 固定顺位顺延与 AOE 分流无消费者(Rule 7 空转)、AOE ≡ 单体——合法退化,非错误 | Rule 7 | +| EC16 主角 hp 入参越界(> max_hp 或 ≤ 0) | 拒绝实例化(false + debug 诊断,承 A3 拒绝口径;进程致命保留给 D2 数据通道)——编排方供给异常显式暴露、不静默吸收 | Rule 9 | +| EC17 同结算内"踩雷授予"与"敌攻击兑付"的先后 | 授予在编排期先于兑付 → 本轮踩的雷当轮即兑付(若该轮有攻击拍);读值语义半句归 B2(无内部缓存、无时间维度——AC-7 腿4)、次序半句归 B1(义务①②编排域) | F2/F3 | + +## 依赖 + +**上游**:D2(hard)——EnemyTable schema + 双敌种行回填(本 GDD 为回填 owner)+ HeroTable 新增(走 D2 接口漂移规则回写其 Core Rules 表清单)+ ConfigDB 单点读取(R1 行解析)。无其他玩法上游;A2/A3/A1 的事实均经 B1 结算编排转化(零直接接口)。 + +**下游与邻接**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | hard(provisional——B1 未设计) | 单位状态查询/受击/推进接口(冒烟 API 面全表)+ B1 侧义务清单 6 项(缺项即 blocking,见义务台账):① 推进每结算恰一次 ② 雷→伏击恰一次 ③ clamp/死亡时机成文 ④ 条款 3 对账(cap 表值 + 量纲 + A1 actuals p10)⑤ 默认目标/AOE 分流 ⑥ 溢出行协议 | +| B3 | 相邻(零运行时依赖——验收锚关系非依赖边,B3 先例同款) | ambush/weaken 的量纲与作用域验收锚归 B2(Rule 6 兑现 registry OQ2);side-wide MVP 兼容(B3 接口零变更);target_ref 扩展位义务(首个单体效果上线时补列,随批:B1 归集/D1 呈现裁决) | +| B4 | soft(provisional) | 伤害道具默认目标消费(Rule 7 固定顺位);伏击清单扩展联动(新增 mine_hit 行时本 GDD 同步修订);B4 道具/增益 = 主角基础攻击之外的规划面伤害(非唯一伤害来源);attack_up 消费面 = 主角战斗段先手攻击(Rule 9 / B1 战斗段) | +| A2/A3 | 间接(零直接接口) | 雷/旗事实经 B1 转化;AP 界(单轮开雷 ≤ 8)= cap 默认值结构来源;weaken 档位锚归 B4/B1(A3 条款 2),B2 无表值 | +| A1 | 间接 | 雷数分布影响伏击吞吐——条款 3 对账消费 A1 actuals p10(B1 义务,非 B2 域) | +| C3 | 下游(soft,provisional) | 难度修饰施加点/算式归 C3 定稿(Rule 10);EnemyTable/HeroTable 曲线数值调参所有权归 C3(A1 参考行先例——MVP 默认值全部 provisional);多敌种同深度选择规则共裁;新敌种投放节奏 = 关卡命脉(MVP 敌信息总量 = 2 环 3 行为、L2 见底——每关须有新敌种或新环段投放;goblin 单拍环 playtest 证伪项 OQ8) | +| C1 | 下游(soft,provisional) | 遭遇构成(战斗触发、(L,D) 供给、unit_count 覆盖语义)归 C1;跨战斗 HP 持久性归 C1/C2 | +| D1 | soft | 视觉推测型呈现义务 4 项(缺项即 blocking,见义务台账);敌单位 HUD(血条/顺位/姿态)呈现;顺位标记资产需求(呈现契约表 #3) | +| B5 | soft | 行为环视觉状态表现(蓄力姿态等——预告时序 Rule 8⑤);结算明细敌方行呈现(数据源 = B1 流水;首个 heavy 兑付明细行分量高亮建议——首遇校准课,ux 注记非硬义务) | +| D5 | soft | 读怪教学(敌种环模式可学、顺位规则)+ "蓄力拍前放心贪雷"教学(charge 轮伏击池蒸发 = 读怪奖励的涌现素材——F2 极端行为)+ 首次蓄力姿态一次性提示候选("它在蓄力,这轮踩雷不挨打"——L1 goblin 无 charge 拍、首课在 L2;MVP 可先上一次性提示、不等垂直切片完整教学)——垂直切片期 | + +- 双向一致性核对(systems-index 依赖分层表):B2 ← D2 ✅(索引仅此一条硬边,其余为事实读取不列边——D2 注记口径);B1 ← …B2 ✅ · C3 ← D2,B2 ✅ · C1 ← C2,C3,B2,B4 ✅。 + +### 义务台账(本 GDD 登记,缺项即 blocking——B3 对 B1 登记先例) + +对外引用口径:B1 清单在 B1/registry 侧引用名为「B2 义务①–⑥」(b1-settlement 上游表同口径);D1 清单引用名为「D1 呈现义务①–④」。 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| B1-① | 单位接口调用集中在结算编排、行为环推进每结算恰一次(double-advance 无守卫——信任编排,B3 义务⑥同款);结算内调用序成文:兑付读值 query_attack → 受击上报 apply_damage → 推进 advance——倒序会读到下一拍(F2 依赖当前拍;比 double-advance 更常见的实现错误),B1 冒烟须回放结算步流 | B1 | 已登记 · 缺项即 blocking | +| B1-② | 雷→伏击事实转化恰一次(每颗雷格实例至多一次;B3 EC6 对侧同款) | B1 | 已登记 · 缺项即 blocking | +| B1-③ | 伤害 clamp/负值/溢杀语义与死亡判定时机成文(Rule 1 B1 域) | B1 | 已登记 · 缺项即 blocking | +| B1-④ | F1 条款 3 闭合对账:消费 B2 advantage_per_round_cap 表值 + 伏击量纲(Rule 6 换算)+ A1 actuals p10(非 target 参数——A1 登记口径) | B1 | 已登记 · 缺项即 blocking | +| B1-⑤ | 默认目标规则消费与单体/AOE 分流(Rule 7;顺位快照 = 生成序) | B1 | 已登记 · 缺项即 blocking | +| B1-⑥ | 伏击 cap 溢出的"溢出未生效"明细行协议(Rule 5 后半;呈现归 B5/D1) | B1 | 已登记 · 缺项即 blocking | +| D1-① | enemy_id→图像线索映射清单成文(形态/姿态↔行为状态;预告时序 = 下一拍预告、挖掘期全程可见〔Rule 8⑤〕;含强度档位与环长直觉编码说明〔Rule 8②〕;形状+色相双通道,art-bible 约束) | D1 | 已登记 · 缺项即 blocking | +| D1-② | 线索映射全局恒定(映射键 = enemy_id——跨 run 恒定、禁止重掷;Rule 8③ 的呈现侧承载) | D1 | 已登记 · 缺项即 blocking | +| D1-③ | 单位顺位与存活可读(固定顺位规则的推测前提——Rule 7;顺位标记资产需求见呈现契约表 #3) | D1 | 已登记 · 缺项即 blocking | +| D1-④ | 敌方 side 池状态呈现口径(伏击层数经 B1 流水——B3 敌侧延伸) | D1 | 已登记 · 缺项即 blocking | + +### registry / 接口漂移回写义务 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| R-1 | B3 单敌假设回验结果 = 不成立(定多敌)→ 触发 B3 预登记的接口漂移条款("定多敌即触发 target_ref 接口漂移补列"):registry status_effect.single_enemy_assumption 注记更新 + systems-index 门禁记录登记(B3 GDD 正文不改,以其"以 registry 为准"先例承载) | B3 / registry | 待回写(Phase 5b/5d) | +| R-2 | 索引 B2 行描述为窄口径("敌人类型/词条、目标选择规则")→ Phase 5d 更新进度行时补战斗单位系统身份(CD 门禁扩容口径) | systems-index | 待回写(Phase 5d) | +| R-3 | registry status_effect.ambush 量纲定稿注记回写(F2 定稿——B3 registry OQ2 关闭条件) | B3 / registry | 待回写(Phase 5b) | +| R-4 | HeroTable 新增 → D2 Core Rules 表清单 + registry 接口漂移回写 | D2 / registry | 待回写 | + +## 呈现与 UI 契约(数据面义务——B2 无自有渲染面,呈现归 D1/B5) + +| # | 数据面义务 | 归属/呈现 | +|---|---|---| +| 1 | 敌种视觉身份(形状双通道):双敌种剪影必须一眼可辨(视觉锚 2 形状+颜色双通道、色弱可用性);敌种形态承载强度档位直觉编码("大个子多半有重击"式——Rule 8② 的呈现侧) | D1/B5,art-bible 约束 | +| 2 | 行为环视觉状态(承重规格):每个敌种的每类行为(attack/charge/heavy)对应恒定且互异的视觉状态——bugbear 蓄力拍 = 可辨识的蓄力姿态;映射每 run 恒定、禁止重掷(Rule 8③ 护栏;线索清单成文义务 = D1×4①)。MVP 敌种视觉状态清单:goblin 1 态(攻击)、bugbear 3 态(攻击/蓄力/重击) | D1/B5 | +| 3 | 资产需求下限(存活意图态之外三类):受击反馈(多敌同屏归因"谁挨了打"的 P2 前提——两敌种可共用通用受击反馈一组、须显式登记)、死亡表现("重击被掐灭""抢杀窗口"兑付可见性是 P1 兑付点——死亡瞬间须可辨识、不得直接消失)、顺位标记呈现元素(数据面 = B2 顺位查询;资产与呈现归 D1×4③、同等 blocking)——合称资产需求下限,D1/B5 照单生产、不得隐式缺席 | D1/B5 | +| 4 | 单位呈现数据供给:hp/max_hp(血条数字)、顺位(固定顺位目标的可读前提)、存活态、当前拍视觉状态——数据源 = B2 单位状态查询(经 B1 编排流水) | D1 | +| 5 | 敌方增益呈现:伏击池层数的 HUD 呈现口径(D1×4④——数据源 = B1 流水经 B3 投影);结算明细敌方行使用分量二元组("攻击 13 = 基线 4 + 伏击 9"——F2 供给建议)——P2 归因的敌侧兑付面 | D1/B5 | +| 6 | 视觉锚适用与双舞台陷阱:血条/伤害数字暗色底高对比(锚 1);B2 无棋盘渲染面——敌单位出现在棋盘上还是独立战斗场景 = B5 前的"战斗表现美术测试"锁定(game-concept 双舞台资产管线陷阱),B2 只锁数据面与状态清单、不预设舞台;敌人层绘制优先级:锚 3 优先级表(数字>小人>旗>变异纹理>底色)不含敌单位——art-bible 创建前须裁决敌人层插入位置(姿态精灵与数字/旗/小人的遮挡关系),随"战斗表现美术测试"一并锁定,否则"哪些数字可信"的心智模型分裂 | D1/B5 / art-bible | +| 7 | UI 三项义务(均为已登记义务的数据面):① 敌单位 HUD(血条/顺位/姿态——D1×4③)② 结算明细敌方行(分量二元组 + 伏击溢出行——B1 义务⑥协议、B5 呈现)③ 教学口径(读怪 + 蓄力贪雷——D5);MVP 无敌图鉴/敌人列表 UI(敌种名录 2 个、学习成本内),名录扩展时再议(垂直切片) | D1/B5/D5 | +| 8 | 音频:MVP 无需求(攻击/死亡/蓄力音效归 D4 Alpha;临时 SFX 随垂直切片——AD 项先例);蓄力拍的音频前摇是"读招"的潜在第二通道——登记为垂直切片可选项(非 MVP 义务) | D4 | + +## Tuning Knobs + +| Knob(表行值,D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| EnemyTable.unit_count | 1(provisional) | 同步轮转下齐射爆发(全攻轮 side 伤 ×N,非平滑) | =1 时 AOE 分流/顺位顺延零消费者(Rule 7 空转) | +| EnemyTable.hp | 18(provisional,出处待查) | 战斗拉长 → 伏击跨轮累计放大 | 秒杀级 → 惩罚无兑现窗口、伏击机制名存实亡 | +| EnemyTable.atk | 4(provisional,出处待查) | 伏击稀释 → 踩雷无感(P3"风险与收益同源"断) | 支配边界 L*→1,惩罚接管战斗、敌种属性被 C3 曲线稀释 | +| EnemyTable.heavy_multiplier | 2(provisional) | heavy+伏击同拍成秒杀拍(读怪规避价值升、容错归零) | =1 时 heavy ≡ attack、三拍环退化两拍(读怪素材坍缩) | +| EnemyTable.advantage_per_round_cap | 4(本 GDD 定稿) | →8 时溢出协议死码回归、支配区扩大、条款 3 右臂膨胀 | ≤2 时 cap 常态绑定 → 边际惩罚归零("再多踩也不痛")、溢出行噪音、P3 归因梯度塌陷 | +| HeroTable.max_hp | provisional(出处待查) | 容错升、治疗道具贬值 | 灾难轮秒杀感(与 cap 交互) | +| HeroTable.atk | 6(原型锚 provisional 归 C3;域 [1,999]、0 加载拒绝) | 击杀预算贬值 → 道具供给/敌 hp 曲线联动重算(C3,禁止独立调参) | 战斗拖长、基础攻击存在感稀薄(道具侧支配) | + +- 非 knob(结构冻结):行为枚举七类封闭(扩展 = GDD 修订非调参)、死亡即不动手、先作用后推进、伏击池共享读法、合成序后置加法、伏击量纲 = HP 点、固定顺位规则、每行集恰一敌种、零随机——修订走本 GDD 流程。 +- Knob 交互:cap × atk(支配边界 L* = ⌈atk×mult/3⌉ 联动)禁止独立调参——联动评估归 B1 条款 3 闭合对账;unit_count × cap(单结算上界双线性);heavy_multiplier × atk(heavy 拍支配)。pattern 内容(环长/charge 占比 → 兑付率 ρ)属内容行调参,所有权归 C3。 +- 零伤害死局组合护栏(OQ9):hero atk=0 已被 HeroTable schema 域下界杜绝([1,999]、0 加载拒绝)——"全场恒零伤害、战斗永不定局"结构不可能;残余退化 = 行 atk=0 且 cap=0 → 无威胁敌(战斗可胜但零风险——内容调参禁区注记,非死局);跨表量级护栏(atk×mult 上界 vs max_hp 域)owner = 实现期 ADR/D2;混编遭遇(多敌种同战斗)下 side 池 cap 取哪行值未定义——当前结构(每行集恰一敌种)恒单行值,混编引入时随 OQ7 改键一并裁决。 +- 调参所有权:MVP 全部默认值 provisional、曲线数值归 C3(A1 参考行先例);B1 条款 3 对账落定前,cap 与伏击量纲相关联调须重跑方向性预检并同步 registry(接口漂移义务)。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | B2 是单位层纯逻辑类——非 autoload、无场景依附、无自有渲染面。冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code)、ConfigDB autoload 由脚本显式实例化);单位容器与两表 schema 经 class_name 直引实例化(每用例全新实例、无跨用例状态——AC-17 扫描对该实例化路径豁免);每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明) | +| 被测面与调用方 | B1 未实现(设计顺序第 9 位)——冒烟直接驱动 B2 最小 API 面(冒烟脚本扮演编排方,按"实例化 → 查询 → 受击 → 推进 → 消亡"结算编排序列驱动);B1 就绪后改走真实调用方(结算编排)、断言口径不变 | +| 时序口径 | B2 无结算 API——"一次结算"在冒烟中 = 调用方对每个存活敌单位恰一次 advance(含兑付读值与受击注入)的构造窗口(主角无行为环、不在推进域内——Rule 4);"每结算恰一次推进"是 B1 义务①——冒烟以构造序保证前提、不断言编排正确性;"轮到该单位之前被杀" = 同一构造窗口内 advance(u) 之前的 apply_damage(u, …);多步 GIVEN 均给出构造序列 | +| 只读观测量 | 敌单位投影七字段 enemy_id · hp · max_hp · atk · heavy_multiplier · pattern(快照)· advantage_per_round_cap(快照)+ beat(当前拍)+ 存活态;hero 投影 hp · max_hp · atk | +| 相位与投影 | Alive/Dead 由 query_alive 成员差分推断(Dead 句柄经 query_beat 冻结值可读);Despawned = 逻辑失效态(状态位实现——容器为 RefCounted 纯逻辑类、对象不提前释放;失效句柄上查询返回空 + 诊断、作用接口拒绝;瞬态纪律同 A3 RoundEnd);Unspawned 无观测量;投影一律为快照(pattern 等引用字段返回拷贝——原地改写返回值不影响容器,AC-11 腿 3);容器公开可写属性为零(hp 等仅经 apply_damage 变更——AC-17 扫描域);实例不变式(hp ∈ [0, max_hp]、敌单位数 = unit_count、指针 ∈ [0, 环长))为 debug 断言自守——违规路径不可由封闭 API 构造(负空间)、不经冒烟断言(进程致命保留给 D2 数据通道);行为级证据 = AC-1(数)、AC-3(指针)、AC-4/AC-6(hp floor) | +| 期望值纪律 | hp(18)/atk(4)/heavy_multiplier(2)/unit_count/max_hp 均为 provisional knob(Tuning Knobs 节)——各 AC 的 THEN 具体数值按当前 ConfigDB 表值推导、不写死;记号:h_g/a_g(goblin 行)、h_e/a_e/m_e(bugbear 行)、u_c、M_h、c_r(所在行 cap)、p_g/p_e(pattern 表读值)、m_amb = StatusEffectTable.ambush.magnitude 表读值(仅作 AC-7 注入值来源);pattern 环内容属内容行调参(C3 所有权)——轨迹断言一律按 p 表读值推导;L/mines_r/n 等入参为构造选择(显式登记)、期望值由表读值推导 | +| 结构定值断言 | 合法集:敌种 id 集 {goblin, bugbear, ogre_chief}(Rule 3 MVP 收窄——C3 修订批扩容三敌种)、pattern 元素域 {attack, charge, heavy}(枚举封闭)、每行集恰一敌种、HeroTable 单行、HeroTable.atk 域 [1,999](0 加载拒绝;MVP 值 6 provisional 不作定值断言) | +| 致命类收窄与 manifest | B2 冒烟设恰两根致命夹具(均为 B2 表特有校验类别):runner manifest 退出码行 rowset_dup→≠0 · pattern_reserved→≠0(独立 headless 进程、游戏通道,D2 AC-2b 协议同型);其余致命类别(D2 R1 悬空敌种/行集缺行〔EC13〕、遭遇坐标越界〔EC14〕、通用枚举域/域校验)由 D2 AC-2 同类别 loader 级夹具承载(表无关,豁免声明收窄保留——B2 冒烟仅以合法坐标驱动 spawn);HeroTable 单行类别条件性(可选根);schema 侧以程序构造断言域类别(AC-12/13) | + +### 冒烟 API 面(provisional,封闭枚举——实现期 ADR 定名) + +| API | 签名 | 语义与返回口径 | +|---|---|---| +| spawn | `spawn(level_index, node_depth, hero_hp, n) -> bool`(n = 本战实际数量 override——B1 encounter 骰;定值行/省略 n = unit_count 表值) | 解析遭遇行 → 行值快照 → 实例化 n 个敌单位(行主序生成序)+ 主角(hp 入参,MVP 冒烟以 max_hp 入参——Rule 9);Alive 相位重复 spawn → false + 诊断(相位守卫) | +| query_alive | `query_alive(side) -> Array` | side ∈ {hero, enemy};生成序稳定枚举;只读 | +| query_beat | `query_beat(u) -> String` | 当前拍 ∈ pattern 元素;Dead 句柄保留只读可查(冻结不变式的观测量);hero 句柄 → 空 + 诊断(主角无行为环——Rule 4);只读 | +| query_attack | `query_attack(u, L, m_amb) -> {baseline, attach, total}` | F2 分量供给面;L/m_amb 由调用方供值(B1 兑付读值的冒烟等价——B2 对 B3 零运行时依赖的边界由此保持);L 为判别探针入参、B2 不校验其游戏态域(游戏态域由 F3 预截流承载);Dead 句柄 → {0, 0, 0} + 诊断;只读 | +| apply_damage | `apply_damage(u, n) -> bool` | 受击上报;hp 递减、floor 0 → Dead;n ≤ 0 → false + 诊断(无伤害作用非法);Dead 句柄 → false + 诊断(MVP 无复活) | +| advance | `advance(u) -> bool` | 存活才推进("先作用后推进"的推进半);false 分两级:存活校验失败 = 轮到前被杀的预期冻结、无诊断(EC1 常态路径——抢杀窗口是设计卖点,诊断刷屏会淹没信号);相位/句柄失效 = false + 诊断;hero 句柄 → false + 诊断 | +| default_target | `default_target() -> u\|null` | 固定顺位供给面;只读 | +| ambush_grant_plan | `ambush_grant_plan(mines_r, cap) -> {granted, overflow}` | F3 机制算术纯函数——B1 预截流编排的消费面;只读 | +| despawn_all | `despawn_all() -> bool` | battle_over 消亡;重复调用 → false + 诊断(重复调用守卫) | + +- 返回口径:全部布尔/计数返回 = 受理/拒绝(A3 全 API 布尔口径同款)——"拒绝"直接断言返回值,附 debug 诊断、非进程致命(进程致命保留给 D2 数据通道);五个 query 面(query_alive / query_beat / query_attack / default_target / ambush_grant_plan)只读、无副作用。 +- 封闭枚举即负空间验收形态:无 summon/增援 API(EC5 负空间——战斗内不增员)、无复活 API(Dead 不可逆)、无重置/换图 API(EC11 跨图延续的结构前提)、无伏击池写 API(B2 无池——L 恒由调用方注入,EC17 读值语义);新增上述任何入口 = 接口漂移(GDD 修订非调参)。 +- ADR 前置裁决项:query_attack / ambush_grant_plan 两 face 若实现期裁决归 B1(F2"供给建议"与 F3"预截流归 B1"边界注记存在张力),对应 AC 腿迁移至 B1 冒烟、断言口径不变(OQ①)。 + +### 夹具策略 + +| 根 | 构成 | 服务 | +|---|---|---| +| 默认根 | 干净检出:EnemyTable 双敌种行 + HeroTable 单行 | 全部分支行为 AC | +| enemy_multi_unit | D2 全表清单拷贝 + EnemyTable 仅将 (L_a, D_a) 行(bugbear——三拍环)unit_count 置 3(其余字段域内合法;登记值 u_c_f = 3) | 多敌 AC(AC-5 / AC-7 腿4 / AC-9 / AC-10 腿2) | +| enemy_reloaded | D2 全表清单拷贝 + EnemyTable 数值漂移(登记:hp/atk/advantage_per_round_cap 各 +1、pattern 不动——快照差分判定用) | AC-11 | +| enemy_rowset_dup_species(致命) | D2 全表清单拷贝 + EnemyTable 默认行 + 追加一行与 (L_g, D_g) 行同 (level_index, node_depth) 而 enemy_id 相异("goblin_twin",其余字段域内合法)→ 每行集恰一敌种违规 | B2 表结构致命校验的进程级落点——manifest rowset_dup→≠0(B3 status_effect_dup_key 同型) | +| enemy_pattern_reserved(致命) | D2 全表清单拷贝 + EnemyTable (L_g, D_g) 行 pattern 追加预留值 "guard"(其余合法)→ pattern MVP 校验域拒绝预留值 | Rule 4 fail-fast 的 loader 集成缝落点——manifest pattern_reserved→≠0(A1 solver_rules 冻结先例) | +| hero_extra_row(可选根) | HeroTable 置两行(域内合法——单行全局违规) | 单行校验落点若实现期 ADR/D2 裁定在 loader 缝则启用(manifest 行 hero_rows>1→≠0),否则程序构造承载(AC-13)——可选、两处恰一承载 | + +- 行集登记常数协议(A1 黄金向量 / A2 板登记同源):(L_g, D_g) = goblin 行坐标、(L_a, D_a) = bugbear 行坐标,逐坐标手工推导记录入版本库;失效处置 = 先复核登记、登记无误即实现缺陷。 +- 多敌根选 bugbear 行(三拍环)保证判别力:goblin 行环长 1 恒 attack → 混合拍 GIVEN 不可构造、同步断言零判别力、双单位读值不可构造;改 bugbear 行后混合拍经对单单位重复 advance 构造(冒烟构造序可越出"每结算恰一次"契约——B2 无守卫、构造自由度成立)。 +- 显式声明:夹具值非 MVP 内容调参结论(Tuning Knobs 默认 unit_count = 1——多敌语义在默认根上零消费者,多敌根是 Rule 2 的测试承载、仅供冒烟)。 +- 换根加载经 `load_tables(root, collect_only=true)` 直调(非 D2 `reload()` 路径:不过 set_reload_gate、不发 config_reloaded——门语义归 D2 AC-16/B1,B3 AC-15 同款);程序构造(AC-12/AC-13):两 schema 类 class_name 直引实例、不经 load()/preload();行为夹具根经 `--data-root=` 注入(仅 debug、不入 data/tables/——D2 夹具纪律);各 AC 声明其表根、用例间无顺序依赖。 + +### 覆盖声明(out-of-scope——deferral 生效条件 = B1×6 + D1×4 义务清单,均缺项即 blocking) + +| deferral 项 | 生效条件 / 承载 | +|---|---| +| B1×① 行为环推进每结算恰一次与结算内先后次序(double-advance 无守卫——冒烟单调用方次序即构造序,B3 义务⑥同款) | B1 义务① | +| B1×② 雷→伏击事实转化恰一次 + 编排期授予/预截流调用次序(F3 编排半、EC17"先转化后兑付"次序半句) | B1 义务② | +| B1×③ 伤害 clamp/负值/溢杀语义与死亡判定时机(冒烟只断言 hp 递减、floor 0 与 Dead 翻转——"这一下是否兑现"归 B1) | B1 义务③ | +| B1×④ F4 条款 3 闭合对账(cap 表值 + 量纲 + A1 actuals p10——冒烟只供分量面与 k 可算性) | B1 义务④ | +| B1×⑤ 默认目标消费与单体/AOE 分流(冒烟只断言 default_target 供给面) | B1 义务⑤ | +| B1×⑥ 溢出"溢出未生效"明细行协议(呈现归 B5/D1——冒烟只断言 overflow 计数会计) | B1 义务⑥ | +| D1×①② 图像线索清单成文与映射每 run 恒定(Rule 8 视觉面——视觉映射不可 GIVEN-WHEN-THEN;验收形态 = 映射键契约〔enemy_id〕+ B2 侧行为级承载:行为环零随机可学〔AC-3/AC-15〕) | D1 义务①② | +| D1×③ 顺位与存活可读呈现 | D1 义务③ | +| D1×④ 敌方 side 池呈现口径(B3 敌侧延伸) | D1 义务④ | +| EC8 池蒸发腿(dur=1 轮末清池——B3 AC-8/AC-9 既有覆盖,跨引不重复;B2 侧零兑付半 = AC-7 腿3 / AC-9 腿1) | B3 AC 既有 | +| EC13/EC14 端到端致命(D2 R1/EC14 既有通道) | D2 AC-2(manifest 豁免) | +| Rule 10 后半难度修饰施加点(B2 以实例化后数值运作——修饰算式无表值可断) | C3 provisional | +| 跨战斗 HP 持久性 | C1/C2 | +| 曲线数值与遭遇分布定值(AC-14 显式不作分布定值断言) | C3 | +| atk 激活裁决——已退役:基础攻击恒存在,域断言由 AC-13/AC-14 承载 | 无遗留义务 | +| Rule 8"不直接显示数值"负空间(无意图数值 API——冒烟 API 封闭枚举承载) | API 封闭 | +| qa-lead 缺口预检 7 项处置:预检 1/2 入 OQ①②;预检 3/4 随批修正 F4 满配语境与 EC17 切分;预检 5–7 承载于 ADR 裁决项 / HeroTable 可选根 / 坐标登记协议 | 已处置 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | ConfigDB 默认根加载完成、全新 B2 容器(Unspawned) | `spawn(L_g, D_g, M_h)`(hero_hp = max_hp 表读值——MVP 冒烟口径);Alive 相位再 `spawn(同坐标, M_h)` | 首次 true;`query_alive(enemy)` 恰 u_c 个单位(u_c = unit_count 表读值)、全部同 enemy_id、每单位投影 hp = h_g、max_hp = h_g、atk = a_g、pattern = p_g、cap = c_r(行值快照五字段逐一 = 表读值——Rule 10 授予时快照基线);`query_alive(hero)` 恰 1、hp = 入参值;敌投影序 = 行主序生成序(默认根 u_c=1 平凡——多敌序断言由 AC-5 夹具根承载);再 spawn → false + 诊断、容器不变(相位守卫——B3 EC11 输入×相位矩阵同款) | Rule 2/9/10 | +| AC-2 | 默认根、全新实例 | `spawn(L_g, D_g, hero_hp)` 分别取 hero_hp ∈ {M_h+1, 0, −1}(越界三腿:> max_hp / ≤ 0——EC16 口径);随后 `spawn(L_g, D_g, 1)`(域内下界合法值) | 越界三腿均 false + 诊断且 `query_alive` 双侧恒空(拒绝原子性:无半实例化——失败腿不留任何单位);域内下界 → true——越界拒绝精确以域为界、不扩大拒绝域(编排方供给异常显式暴露、不静默吸收) | EC16 / Rule 9 | +| AC-3 | `spawn` 完成(bugbear 坐标 (L_a, D_a)) | 腿 1 读初拍;腿 2 连续 advance;腿 3(goblin 坐标)连续 3 次(构造选择)advance;腿 4 任意长 advance/受击序列 | 腿 1(环首初始化):`query_beat(u)` = p_e[0];腿 2(推进与回绕):均 true 且 beat 轨迹 = p_e[k mod \|p_e\|] 逐拍断言、\|p_e\| 次后回 p_e[0](环尾回环首——指针恒 ∈ [0, 环长) 的行为级证据);腿 3(环长 1 恒等):beat 恒 = p_g[0](mod 1 恒等——每拍 attack、无自然空窗、兑付率 ρ=1 的行为级形式;前提 \|p_g\| = 1 按表读值核对——pattern 内容属 C3 内容行调参,前提不满足时本腿显式 SKIP、不假失败);腿 4(跨轮跨图延续 + 无重置入口,EC11):hp 与 beat 投影 = 驱动序列的确定函数——除 despawn_all 外无任何 API 入口使单位状态重置(结构性裁决;"换图"事件对 B2 不可观测(零接口),端到端"真实换图下单位状态延续"归 B1 集成——编排契约仅 battle_over 调 despawn、非新增义务,B3 AC-10 裁决同款) | Rule 4 / EC10 / EC11 / F1 | +| AC-4 | 默认根;前提按 p_e 表读值核对:\|p_e\| ≥ 3 且 p_e[1] = charge 且 p_e[2] = heavy(默认表值满足;pattern 内容属内容行调参,前提不满足时本 AC 显式 SKIP、不假失败) | 腿 1 构造推进至 charge 拍后 `apply_damage(u, h_e + 5)` 再 `advance(u)`;腿 2 新实例构造至 heavy 拍 `advance(u)` 后 `apply_damage(u, h_e)`;腿 3 对 Dead 句柄 `apply_damage` | 腿 1(heavy 拍前被杀):apply_damage → true、hp 投影 = 0(溢杀 floor:hp ∈ [0, max_hp] 不变式自守——clamp/溢杀语义归 B1×③ 的声明不变);advance → false(预期冻结级——无诊断,API 面两级 false 口径)、`query_beat(u)` 仍 = p_e[1]——冻结于 heavy 前一拍、重击永不兑现("死亡即不动手"+"抢在重击前击杀"读怪收益的行为级证据);腿 2(行动后才死):advance → true、随后 Dead、`query_beat(u)` = p_e[0](已行动已推进——heavy 拍已消费、指针回绕);腿 3(不可逆):false + 诊断(MVP 无复活——States 不可逆) | EC1 / EC9 / F1 | +| AC-5 | 夹具根 enemy_multi_unit(u_c_f = 3 登记值,bugbear 行)`spawn(L_a, D_a, M_h)` 完成 | 腿 1 读三单位初拍;腿 2 定义结算步 = 按生成序对各存活敌单位 advance 恰一次(B1 义务①的冒烟构造等价——hero 无环不在推进域),在某步 advance(u2) 之前 `apply_damage(u2, h_e)` 杀之(h_e = bugbear 行 hp 表读值);腿 3 全程观测规模 | 腿 1(同拍基线):spawn 后三单位 `query_beat` 恒等(同种同指环首、同属性);腿 2(死亡时序不乱拍):u2 本步不打(advance false、指针冻结);步末 `query_beat(u1)` = `query_beat(u3)`、连续多步后幸存者 beat 恒相同(同步性质定理——任何死亡时序都不会让幸存者乱拍);腿 3(单位数单调递减):`query_alive(enemy)` 规模 ∈ {3, 2, 1} 严格递减、无增员(EC5 负空间的行为面——无 summon API) | Rule 2 / EC2 / F1 | +| AC-6 | 默认根 + AC-5 根 | 腿 1(默认根)u_c 个单位逐一 `apply_damage` 致死;腿 2(AC-5 根)`apply_damage(hero, M_h)` 后、`query_alive(enemy)` 仍含存活单位时 `despawn_all()`;随后重复 `despawn_all()`;随后重新 `spawn` | 腿 1(全灭,EC3):`query_alive(enemy)` = 空 Array(存活敌集 = ∅——"敌人全灭"成立,胜负判定归 B1);敌侧伏击池"无兑付者轮末清空"半 = B3 dur=1 decay(跨引 B3 AC,out-of-scope);腿 2(主角死亡与消亡,EC4):hero hp = 0(失败判定素材——判定时机归 B1);despawn_all → true、`query_alive` 双侧空(全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态);Despawned 句柄上 query_beat 返回空 + 诊断、advance/apply_damage → false(域闭);重复 despawn_all → false + 诊断(重复调用守卫——B3 EC11 同款);重新 spawn → true 且为新战斗(Despawned 后唯一出口——下一场重启、不跨战斗) | Rule 1 / EC3 / EC4 | +| AC-7 | 默认根 `spawn(L_a, D_a, M_h)` 完成、m_amb = StatusEffectTable.ambush.magnitude 表读值 | 腿 1 attack 拍 `query_attack(u, L=2, m_amb)`;腿 2 `query_attack(u, L=4, m_amb)` 合成序判别(判别非平凡前提 m_e ≥ 2——默认表值满足;m_e = 1〔合法 knob、heavy ≡ attack〕时负断言两边恒等、本腿显式 SKIP 标记输出——B3 AC-14 腿 3 先例);腿 3 beat 推进至 charge 拍后 `query_attack(u, L=c_r, m_amb)`(游戏态上界注入);腿 4(enemy_multi_unit 根承载——默认根 u_c=1 不可构造)同 L 连续两次 query + 两存活单位各自 query | 腿 1(attack 拍):{baseline = a_e, attach = 2×m_amb, total = a_e + 2×m_amb}(分量二元组——"攻击 13 = 基线 4 + 伏击 9"P2 归因行的数据源);腿 2(合成序判别):baseline = a_e×m_e、attach = 4×m_amb、total = a_e×m_e + 4×m_amb,且判别负断言 total ≠ (a_e + 4×m_amb)×m_e(伏击附加后置加法不进倍乘——默认表值 (4, 2, 3) 下 20 ≠ 32、非平凡性由表读值不等式保证;实现若把伏击分量塞进倍乘即在此暴露;L=4 为游戏态可达值〔cap=4 上界〕——探针不取域外值);腿 3(charge 零兑付——不读池,EC8 的 B2 半):{0, 0, 0}——L>0 下恒零 = "不读伏击池"的可判定形式(蓄力轮授予的伏击层当轮蒸发 = 读怪奖励;池蒸发本身归 B3 decay、out-of-scope 跨引);腿 4(共享不消耗 + 即时读值,EC17):同 L 连续两次 query 结果逐字相同、两存活单位同 L 各自 query 读值一致(共享、非均分、非先到先得);每次 query 前后 query_alive/query_beat 投影零变化(纯函数性——B2 无池写面、无内部缓存、无时间维度;"先转化后兑付"次序与每雷恰一次归 B1×①②、out-of-scope) | F2 / Rule 6 / EC8 / EC17 | +| AC-8 | 默认根、c_r = cap 表读值(全腿纯函数断言——编排期预截流调用与每雷恰一次授予归 B1×②,out-of-scope) | 腿 1 `ambush_grant_plan(mines_r = c_r + 2, c_r)`;腿 2 参数化对 mines_r ∈ {0, 1, c_r, c_r+1};腿 3 mines_r ∈ [5, 8];腿 4 `ambush_grant_plan(m, 0)` | 腿 1(守恒主式):{granted = c_r, overflow = 2}、恒等 granted + overflow = mines_r(零静默蒸发——P2 会计面;"溢出未生效"明细行呈现归 B1×⑥/B5);腿 2(预截流位):各腿 granted = min(mines_r, c_r)、overflow = mines_r − granted 恒成立(cap 判定在授予前);腿 3(灾难轮,EC7):默认 c_r=4 → granted = 4、overflow = mines_r − 4(cap 反灾难护栏绑定域);腿 4(cap=0 免疫,EC6):granted = 0、overflow = m(全溢出——免疫行全部可见、全部无效的会计半;cap=0 为域内合法行值的校验半归 AC-12) | F3 / EC6 / EC7 | +| AC-9 | 夹具根 enemy_multi_unit(u_c_f = 3) | 腿 1 以 advance 序构造混合拍(k 个单位处 attack/heavy 拍、其余 charge)并读暴露;腿 2 记号恒等式断言(AP_r = ActionEconomyTable.ap_per_round 表读值) | 腿 1(k 确定性可算):每结算敌方伏击暴露 = k × L × m_amb,k 经只读观测量导出(query_alive(enemy) ∩ query_beat ∈ {attack, heavy} 计数——确定性可算、无隐藏状态,B2 对 B1 条款 3 的供给面);全体 charge 轮 → k = 0、暴露 = 0(charge 轮零兑付——EC8 汇总);腿 2(结构上界记号):单结算 side 级结构上界 = u_c_f × min(AP_r, c_r) × m_amb(全参表读值/夹具登记值的记号恒等式断言——满配语境 3×min(8,4)×3=36 的记号化、默认行值 unit_count=1 下 = 12、非定值断言;min 项 = cap > AP 界时 cap 腿松弛);跨结算累计 = 轨迹量非表值——B2 投影字段无任何累计观测量(负空间,AC-1 观测量封闭枚举承载);不等式闭合对账(输入 = A1 actuals p10)归 B1×④、out-of-scope | F4 / EC8 | +| AC-10 | 腿 1 默认根(u_c=1)spawn 完成;腿 2 enemy_multi_unit 根;腿 3 承接腿 2 | 腿 1 `default_target()`;腿 2 `default_target()` 对照 `query_alive(enemy)[0]`;腿 3 杀 [0] 后再查、连续杀至空 | 腿 1(u_c=1 合法退化):default_target() = 该唯一存活单位(顺延与 AOE 分流无消费者、AOE ≡ 单体——合法退化非错误,Rule 7 空转);腿 2(行主序最前):default_target() = query_alive(enemy)[0](生成序最前——顺位快照 = 生成序);腿 3(死亡顺延):default_target() = 原 [1](顺延)、连续杀至空 → null(EC3 全灭腿在目标面的汇合);B1 消费侧单体/AOE 分流归 B1×⑤、顺位可读呈现归 D1×③、均 out-of-scope | Rule 7 / EC3 / EC15 | +| AC-11 | 默认根 `spawn(L_a, D_a, M_h)` 完成(快照字段 = 默认表值;坐标登记协议锚点)、存活与 Dead 单位并存 | 经测试通道换根加载 enemy_reloaded(load_tables(root, collect_only=true) 直调——非 D2 reload() 路径,门语义归 D2 AC-16/B1);随后 `despawn_all()` 后以同坐标重新 spawn;腿 3 对返回的 pattern 投影数组原地改写 | 存活与 Dead 单位的快照五字段 + beat + hp 逐字不变、hero 投影不变(授予时快照——实例不受任何表重载影响,B3 EC9 / A4 EC8 同款);新单位快照 = 漂移后表值(换根只影响后续实例化——"快照非活引用"的判别性后半);腿 3(拷贝判别):容器内单位 pattern 投影不变(快照拷贝非活引用——别名引用在换根加载下同样"逐字不变",本腿补足判别力) | Rule 10 / EC12 | +| AC-12 | EnemyTable schema 类(行级 + 表级校验)class_name 直引实例(不经 load()/preload()) | validate() 作用于越域代表值:unit_count ∈ {0, 4}、hp ∈ {0, 1000}、atk ∈ {−1, 1000}、heavy_multiplier ∈ {0, 17}、pattern 环长 ∈ {0, 9}、pattern 元素 = "guard"(预留值拒绝——MVP 校验域 fail-fast)、pattern 元素 = "nonsense"(未知枚举)、cap ∈ {−1, 17};表级构造同 (level_index, node_depth) 两行相异 enemy_id;域内代表值:unit_count ∈ {1, 3}、hp = 1、atk = 0(纯伏击伤敌种合法——F2 Variables)、atk = 999、heavy_multiplier ∈ {1, 16}、pattern ∈ {["attack"], ["attack","charge","heavy"]}(环长两界)、cap = 0(免疫极端合法——EC6)与 16 | 越域各含对应域错误类别(零静默);表级含每行集恰一敌种错误类别;域内零诊断(防过度约束锁死内容——atk=0 与 cap=0 两个合法极端是本断言承重点) | Rule 3/4 / EC6 | +| AC-13 | HeroTable schema 类直引实例 | validate() 作用于 max_hp ∈ {0, 1000}、atk ∈ {−1, 0, 1000};域内代表值 max_hp ∈ {1, 999}、atk ∈ {1, 999};表级构造两行(域内合法) | 越域各含对应域错误类别(atk=0 拒绝 = 基础攻击恒存在的 schema 承重腿——零基线死锁杜绝);域内零诊断;表级两行 → 单行全局约束错误类别(单行全局——Rule 4 豁免同 ActionEconomyTable 先例;若实现期 ADR/D2 裁决单行校验落 loader 缝,则本腿迁移至可选根 hero_extra_row 的 manifest 腿——两处恰一承载) | Rule 9 | +| AC-14 | ConfigDB 默认根加载完成 | 读 EnemyTable;读 HeroTable | EnemyTable 恰含三敌种行、enemy_id 集合 = {goblin, bugbear, ogre_chief}(定值——Rule 3 MVP 收窄,C3 修订批扩容);每 (level_index, node_depth) 行集恰一敌种(默认根数据通过其自身表级校验的行为断言);全部行 pattern 元素 ⊆ {attack, charge, heavy}(MVP 枚举封闭——结构定值);goblin 行 pattern 不含 "heavy"(heavy_multiplier 无 heavy 拍不消费的表数据前提——非校验项、B3 max_stack 先例);unit_count_max 仅在区间行出现(缺省 = 定值——C3 遭遇随机化字段语义);display_name 多值非空(精确值不作定值断言、本地化归 D6);遭遇分布(何 (L,D) 归何敌种)为 C3 域 knob——不作定值断言(provisional 示例非承诺);HeroTable 恰 1 行、atk ∈ [1,999] 域断言(MVP 值 6 = 原型锚 provisional 不作定值断言;0 = 加载拒绝)、max_hp ∈ [1, 999] 域断言(provisional knob 不写死) | Rule 3/9 | +| AC-15 | 全新 B2 实例 ×2(同 ConfigDB 默认根;每遍全新实例、无跨实例状态) | 同一定长序列驱动两遍(spawn → apply_damage(含一次"轮到前击杀"腿——冻结路径入回放)→ 交替 advance 全体存活敌单位 → query 投影(含生成序与逐单位 beat)→ apply_damage → advance → default_target → query 投影) | 两遍的 query_alive 投影序列(含生成序)、逐单位 beat 轨迹、default_target 返回序列逐项相同——零随机的行为级硬证据(rng_service 流封闭;D2 AC-15 的 B2 层前身,与 AC-3 腿 4"状态 = 驱动序列的确定函数"互证) | Rule 4/11 / F1 | +| AC-16 | ConfigDB 默认根加载完成 | 脚本解析 design/registry/entities.yaml 的 enemy 与 hero 条目(逐字段 YAML 键——机器对账前提,action_economy/status_effect 同构) | ① enemy.fields **十键**(enemy_id / display_name / unit_count / **unit_count_max / armor_type** / hp / atk / heavy_multiplier / pattern / advantage_per_round_cap——C3 修订批 +2)与 ConfigDB EnemyTable schema 字段名逐一相等、各键类型/域/枚举描述与 Schema 定稿相等(缺任一键即失败);② hero.fields 两键(max_hp / atk)同上;③ enemy 条目 MVP 内容注记物种 id 集合 = {goblin, bugbear, ogre_chief}、与表行 id 集合相等(非 knob 结构定值——C3 修订批扩容);④ status_effect.ambush 条目含 B2 量纲定稿注记(Rule 6 兑现 B3 registry OQ2 的接口漂移回写落点——注记存在性 + 键断言;量纲活值 m_amb 不作定值对账——knob 纪律,A4 AC-8 同款豁免);⑤ hero 条目"单行全局 Rule 4 豁免"注记与 enemy 条目"每行集恰一敌种校验"注记存在性断言;生效前提 = 本文定稿 Phase 5b/5d 回填完成(不写"若存在"软口径——条件化使一致性检查落空,B3 AC-18 同款) | Rule 6 / registry | +| AC-17 | B2 文件集(单位容器 / 两表 schema / 冒烟脚本 / 夹具规格)纳入 D2 AC-9 模式项目级扫描域 | 静态扫描 | randomize() / RandomNumberGenerator / ConfigDB 外 data 表加载 / extends Control / .tscn preload——零命中(schema 与容器的 class_name 直引实例化豁免——AC-12/13 前提,A4 AC-7 同款);另扫 B2 特有两项:事件订阅 API 零命中(Rule 11 零主动事件订阅——A2 事件面 referenced_by 不含 B2 的代码侧对应)与 B3 API 符号零引用(相邻零运行时依赖——import/call 面为零);D2 AC-14 人工三要素字面量扫描(引用不另设硬 THEN——F2 算式 a×m + 3L 类字面量随实现形态误报率高,D2 已定性 warning 级 + 人工复核;其"零玩法数值字面量"的行为级硬证据 = AC-1/AC-7 全表读值推导 + AC-16 registry 对账);零随机的行为级硬证据 = AC-15 | Rule 11 / D2 AC-9/10/14 | + +### 覆盖矩阵(qa-lead 交付,验收评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules(11/11) | 1→AC-1/AC-6/AC-2(clamp·溢杀·死亡时机→OOS B1×③)· 2→AC-1/AC-5/AC-9(side-wide 兼容与 target_ref 扩展位→B3 接口漂移义务随批 B1/D1、非冒烟域)· 3→AC-12/AC-14/AC-13 · 4→AC-3/AC-4/AC-5/AC-15(预留值拒绝→AC-12+manifest pattern_reserved)· 5→AC-8(每雷恰一次→OOS B1×②;溢出明细行→OOS B1×⑥+B5)· 6→AC-7/AC-16④(量纲锚验收→B4/B1,排除声明)· 7→AC-10(消费分流→OOS B1×⑤;顺位可读→D1×③)· 8→映射键契约(enemy_id)+AC-3/AC-15(行为环可学性承载)+OOS D1×①②、"不显示数值"负空间=API 封闭 · 9→AC-1/AC-2/AC-13/AC-14 · 10→AC-1/AC-11(D2 R1 解析致命→OOS D2 通道;难度修饰→C3 provisional)· 11→AC-15/AC-17(调用权集中→OOS B1×①) | +| Formulas | F1→AC-3/AC-4/AC-5 · F2→AC-7 · F3→AC-8 · F4→AC-9(闭合对账→OOS B1×④) | +| EC(17 条) | EC1→AC-4 腿1/腿2 · EC2→AC-5 腿2 · EC3→AC-6 腿1+AC-10 腿3 · EC4→AC-6 腿2 · EC5→冒烟 API 封闭(负空间——无 summon API 不可 G-W-T,B3 Rule 8 先例)+AC-5 腿3+AC-17 · EC6→AC-8 腿4+AC-12(cap=0 合法腿)· EC7→AC-8 腿3 · EC8→AC-7 腿3+AC-9 腿1(池蒸发腿→B3 AC 既有覆盖,OOS 跨引)· EC9→AC-4 腿1 · EC10→AC-3 腿3 · EC11→AC-3 腿4(跨图结构裁决)+AC-2/AC-6(相位重复守卫,B3 EC11 同款)· EC12→AC-11 · EC13→OOS(D2 R1 通道,D2 AC-2 同类别夹具,manifest 豁免声明)· EC14→OOS(D2 EC14 通道,同前)· EC15→AC-10 腿1 · EC16→AC-2 · EC17→AC-7 腿4(读值即时性/纯函数半)+OOS B1×①②(次序半) | +| 无洞声明 | 17 条 EC 中 EC5/EC13/EC14 三处非 G-W-T 形态,分别以 API 封闭负空间声明与 D2 通道豁免承载(B3 Rule 8 负空间 + 致命收窄豁免先例同款);其余全部映射到 AC 或显式 OOS(deferral 生效条件 = B1×6/D1×4 已登记缺项即 blocking) | + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | query_attack / ambush_grant_plan 两 face 归属——B2 供给面 vs B1 编排消费(F2"供给建议"与 F3"预截流归 B1"边界注记的张力落点);裁决归 B1 则 AC-7/8/9 对应腿迁移至 B1 冒烟(断言口径不变)、回写 AC 前言 | 实现期 ADR 定名时同步裁决 | Technical Setup / 实现期 | +| 2 | 多敌语义默认零消费者复验——默认 unit_count=1 下多敌路径仅测试根可达(死码风险);C1 落地 unit_count>1 真实遭遇后复验 AC-5/9/10 夹具根是否升级为默认根内容;C1 须真实消费 unit_count>1(Boss/精英战最廉价的构成表达),且 Boss 表达不能只靠 unit_count——同种同拍 ×3 = 伤害乘法、非 Boss 幻想 | C1 遭遇分布定稿 | C1 GDD | +| 3 | 条款 3 左臂载体——基础攻击恒存在后"载体悬空、内容态不可通关"事实已闭环;残余 = 左臂当量换算口径(锚参照回迁 attack_up 当量——B4 F4 / registry anchor_warning)随 B1 对照表对账 | B1 | B1 GDD 强制检查点 | +| 4 | 原型参考值溯源——敌 18HP/4ATK、伏击 +3、hero max_hp 均 provisional 且出处待查(D2 Tuning Knobs 既有登记义务);回填定值前补原型 REPORT 溯源,查无出处则按 C3 调参所有权重定 | 本 GDD 回填定稿时 | EnemyTable/HeroTable 定值回填 | +| 5 | HeroTable 单行校验落点——schema validate() vs loader 缝两处恰一承载(AC-13 腿3 / 可选根 hero_extra_row manifest 行) | 实现期 ADR/D2 | Technical Setup | +| 6 | 标错惩罚候选与伏击清单联动——候选"敌方伏击+1"若实测采纳:事件源 = flag_judged{hit=false}(旗落非雷格)、不在 Rule 5 cell_revealed{kind=mine} 转化路径内;B3 需新增行且 source_class 需新枚举值(如 flag_miss)——source_class 枚举属 B3 结构冻结项、须走 B3 GDD 修订(接口漂移)非"加行";B2 侧伏击清单联动修订 + F3 重验触发(非 mine_hit 源)强制重跑;若错标为 mine_hit:P2 归因明细行来源失实(标错被记成踩雷) | A2 OQ1 实测会话 | A2 /design-review 核对在位 | +| 7 | 精英/Boss 遭遇编码位预登记——难度 = f(关卡序号, 节点深度, 精英/Boss 修饰);A1 BoardGenTable 已预留 elite_/boss_ 命名空间与改键退路;本表键冻结 (level_index, node_depth, enemy_id) 且"每行集恰一敌种"入结构冻结——若 C1 定稿分支地图使同深度并存普通与精英节点,精英编码无键位(同深度第二敌种行触发行集校验致命);届时改键(如追加 encounter_class 维度)走 D2 Rule 4 接口漂移修订,schema/AC-12/夹具/registry 连锁回写(A1 OQ6 镜像) | C1/C3 | C1 GDD 定稿强制核对 | +| 8 | goblin 单拍环体验证伪——goblin 环长 1、无前摇无空窗,玩家可能将其感受为"自动平砍的血条"(恰为体验锚点反向对照);MVP 实际读怪内容仅剩 bugbear 一环;预注册证伪项:playtest 采集"goblin 是否被读"信号;证伪则两拍环备用案(如 [attack, charge])由 C3 内容所有权处置、本 GDD 结构不变 | C3/playtest | 垂直切片 playtest | +| 9 | 零伤害死局组合与跨表量级护栏——hero 侧恒零伤害分支已被 HeroTable.atk 域下界([1,999]、0 加载拒绝)结构杜绝;残余 = 行 atk=0 ∧ cap=0 → 无威胁敌(内容调参禁区、非死局);atk×mult 上界与 max_hp 域无跨表量级护栏——遭遇行配置期跨字段校验落点裁决(ActionEconomyTable 跨字段校验先例),详见 Tuning Knobs Knob 交互注记 | 实现期 ADR/D2 | Technical Setup | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;规则/枚举/边界情况/义务清单/覆盖矩阵/AC 转表格;行为环转字符画;公式、Variables 与验算数据全保留;语义与 2026-09-09 定稿及 2026-09-11 基础攻击裁决对齐,零规则变更 | +| 基础攻击裁决批 | 2026-09-11 | HeroTable.atk = 基础攻击恒存在:域 [1,999]、0 加载期校验拒绝(零基线死锁自 schema 层杜绝)、MVP = 6(原型锚 provisional 归 C3);主角每结算战斗段先手攻击;B4 道具/增益转为规划面伤害(非唯一伤害来源);原"atk MVP=0 机制开关、胜利路径依赖 B4 先行"条款与 OQ3 左臂悬空投影退役;OQ9 恒零死局分支随之收窄 | +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-7 回执,registry 同批):**spawn 增 override 参数 n**(B1 encounter 骰定本战实际数量——区间语义 [unit_count, unit_count_max] 实例化后不增,EC5 记号调和)· **Rule 10 DifficultyTable 接口已定**(registry `difficulty_table`——施加点 = 行值快照前套乘,MVP 空表零修饰)· **EnemyTable schema +2 字段**(unit_count_max / armor_type——护甲克制批,克制矩阵归 SettlementTable)· **物种集扩容三敌种 {goblin, bugbear, ogre_chief}**(AC-14/AC-16① 十键/AC-16③/结构定值断言随批;DND 更名批对齐)· 默认根遭遇分布 = C3 行值总表 v3(provisional) | +| Rev 1(full 评审批) | 2026-09-09 | lean 定稿同日 full 评审(六专员 + CD 终裁)8 项 blocking 同日核销后用户拍板收口(A3/A4/A1/B3 同日核销先例、免独立 re-review):① 胜利路径声明 ② AC-7 恒假判别前提 + SKIP ③ 多敌夹具根改 bugbear 行 ④ OQ6 事件源类更正 + F3 重验触发补支 ⑤ 精英/Boss 编码位预登记 OQ7 ⑥ Rule 8 预告时序 ⑦ 资产下限补受击/死亡/顺位 ⑧ 主角推进域排除;同批定稿四项口径:cap 默认 4(旧默认 8 使溢出协议不可达)、死亡即不动手、条款 3 单结算自然边界、C 节三处回写;F2 判别改域内值、F4 结构上界 min 收紧、方向性预检裕度改 2.3×——明细见 design/gdd/reviews/b2-enemy-system-review-log.md | +| v1(lean 定稿) | 2026-09-09 | 一次会话定稿:十一核心规则 + F1–F4 + 17 AC + 义务台账(B1×6 / D1×4);九项结构决策用户拍板(多敌 / HeroTable / 视觉推测型 / Peglin 行为环 / 每行集恰一敌种 / side-wide / 固定顺位 / unit_count 字段 / cap=4);lean 门禁:systems-designer 起草 Formulas、qa-lead 起草/验证 AC | diff --git a/design/gdd/b3-status-effect.md b/design/gdd/b3-status-effect.md new file mode 100644 index 00000000..25539911 --- /dev/null +++ b/design/gdd/b3-status-effect.md @@ -0,0 +1,533 @@ +# B3 增益/状态系统(Status Effect System) + +> **状态**:Approved · lean 格式 +> **版本**:v1.1-lean · 2026-09-13 · 作者:user + agents(systems-designer〔Formulas〕· qa-lead〔AC〕;三结构决策〔单一计数/按行叠层/四行定稿权收编〕为用户拍板) +> **承载支柱**:P2 主(可归因兑现的机制落点)· P3(敌方增益/debuff 的兑付面)· P1(时限透明的规划素材)· P4(间接) +> **Layer**: 1 Foundation · **Milestone**: MVP · **设计顺序**: 第 4 位 +> **一句话**:B3 是战斗域统一状态容器——三向效果同构实例的时限/叠层/衰减机制唯一 owner,兑付接口供 B1 结算逐行消费。 +> **复核注记**:lean 门禁下 systems-designer / qa-lead 已委托起草(Formulas / AC);creative-director 与 art-director 未经咨询——生产前人工复核体验锚点情绪定位与视觉数据面规格同 art-bible 的一致性。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 战斗域统一状态容器:不在当场一次性、而要在若干次结算里持续起作用的战斗效果(安全格增益 / 踩雷敌方增益 / 标中旗削弱 / 清图奖励 buff)作为同构状态实例的唯一存放与生命周期 owner——统一容器让"效果"成为一门单一语言:四源统一当量的对照载体(A3 条款 1)、B1 结算的单一对接面、可回放步骤流的状态侧前提 | +| 拥有 | 机制:时限/叠层/衰减语义、状态实例生命周期(战斗域)· 兑付接口五 API 与 P2 归因字段 · StatusEffectTable 回填 owner · MVP 四行初值定稿权(Rule 10 收编——锚验收权留 B4/B2/B1);机制与内容分离是结构原则——B3 只拥有机制,效果内容的经济归属归各锚 owner | +| 不拥有 | 效果内容经济归属(安全格 buff 与标中旗 debuff 档位锚验收归 B4、敌方增益上限归 B2)· 效果语义到结算公式的映射与兑付作用顺序(B1 SettlementTable)· 呈现(D1/B5)· 教学(D5)· 渲染面与事件外发(零) | +| 上游输入 | D2 StatusEffectTable(hard——数据通道,本 GDD 回填 owner);无其他玩法上游(Foundation 层) | +| 下游输出 | 五 API(B1 结算编排消费)· query_active 投影(D1 HUD 数据源)· 实例六字段 + display_name + 授予时快照(B1 结算明细 / B5 呈现数据源) | +| 硬约束 | 零随机(无 RNG 流消费)· 零 UI 依赖 · 零信号外发(纯被动容器)· 全 API 布尔/计数返回、无 await——可回放步骤流的状态侧前提 | + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P2 可归因的兑现感 | "我攒下的东西正在替我打架" | 每个状态实例携带来源标注(安全格/标中旗/清图/雷),结算明细逐行生效——P2 准话测试"这个结果是我哪步扫雷带来的?"在 B3 字段里就有答案;"规划的延迟兑现"从口号变成可见事实的机制落点 | +| P3 雷的两面 | "雷是赌桌不是陷阱" | 踩雷的敌方增益行与标中旗的削弱行在结算明细里使用同一呈现语言,玩家由此学会雷的两面;旗标对的即时回报部分补偿 chord 缺席的兑现节拍缺失(CD 创意风险 #3 的关联缓解,正式高光设计归 B1/B4) | +| P1 规划素材 | "还剩几次结算、何时到期" | 增益携带明确的剩余结算次数与到期时机,玩家才能把它纳入挖哪格的推理——时限语义的透明度是规划感的前提;满层溢出行可见("满层时别再拿同款") | + +反向护栏:状态效果绝不能是无感的暗账——玩家说不出一行结算"从哪来",来源标注即失职(违反 P2);任何让 buff 时限/剩余次数不可读的设计默认砍掉。参考感受:杀戮尖塔的增益图标(时机与层数一目了然,是决策素材而非装饰)、Peglin 神器在关键时刻叠加生效的"攒出来的强大"。 + +## 全局流程 + +### 效果生命周期(授予 → 存续 → 兑付/衰减 → 出口) + +``` +B1 结算编排(唯一授予入口 apply_effect——A2/B4 与 B3 零直接接口) + │ 事实来源:A2 开格/标中/开雷事件 → B1 收集 round facts → 转化授予(挖掘期不直接写容器) + ▼ +授予(编排期入容器,无 pending 态——首个结算就是它的第一次兑付) + │ merge(effect_id) 三分支:independent 新建实例 / stack L+1(L=M 拒绝) / refresh R←D + │ 实例载荷 = 六归因字段 + display_name + 授予时数值快照 (magnitude, unit) + ▼ +Active(存续——跨轮次、跨扫雷图延续;换图/刷新不清空,旗标记随图消亡不影响实例) + │ 每轮结算:B1 query_active(side) 只读兑付(作用顺序归 B1 SettlementTable) + │ → decay_round() 恰一次:全实例 R−1,R=0 销毁(先兑付后衰减) + ▼ +出口 ─┬ R=0 → Expired:实例销毁(明细流水已由 B1 记录,B3 不留历史) + ├ notify_battle_end → Cleared:全实例销毁、容器回空 → BattleClosed(不跨战斗) + └ 拒绝路径:域闭 / 未知 effect_id / 满层 → false + 容器零写(溢出行归 B1 结算明细) +``` + +### 出口分发 + +| 出口 | 触发 | 动作/去向 | 约束 | +|---|---|---|---| +| 受理入容器 | apply_effect 受理(新实例/叠层/刷新) | 即入 Active——无 pending 态,首个结算是第一次兑付 | 域闭 / 未知 id / 满层 → false 拒绝(见边界情况) | +| 到期移除 | decay_round 后 R=0 | 实例销毁 → Expired | 明细流水已由 B1 记录,B3 不留历史 | +| 战斗清空 | notify_battle_end() | 全实例销毁、容器回空 → BattleClosed | 不跨战斗;除本 API 外无任何清空入口 | +| 满层拒绝 | stack 行 L=M 再授予 | 返回 false、容器逐字不变;"溢出未生效"行归 B1 结算明细(含来源) | 拒绝原子性——无半写、无溢出落层 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:所有持续效果共用一个容器、一套机制 + +**人话**:战斗里不在当场一次性生效、要管上几回结算的效果——主角的增益、敌人的增益、敌人受到的削弱、清图奖励——全都作为同一种"状态实例"存进同一个容器,用同一套计时、叠层、衰减的规矩管理,没有哪类效果走特殊通道。容器自身有四条硬规矩: + +| 规矩 | 含义 | +|---|---| +| 寿命 = 一场战斗 | 战斗开始时容器为空,战斗结束全部清空——不跨到下一场战斗 | +| 跨轮、跨图延续 | 同一场战斗里跨轮次、跨扫雷图都在;换图/刷新不清空(旗标记随旧图消亡归棋盘系统 A2 管,状态实例不随图走) | +| 全程零随机 | 容器不掷任何骰子:授予、合并、衰减、列举顺序全部确定 | +| 敌方侧共享 | MVP 敌人为 1–3 个同种敌单位(敌情系统 B2 定):对敌状态一律对全体存活敌人生效,层数全体共享一份 | + +**举证**:无计算。"不跨战斗"为 game-concept 定稿口径;多敌共享读法按 registry single_enemy_assumption 登记,现接口零变更兼容。 + +#### 规则 2:状态的寿命按"还剩几次结算"倒数 + +**人话**:每个状态实例都自带一个倒数计数——剩余结算次数。它不承诺"活几轮",而是数"经历过几次结算":每轮结算先让状态起作用,随后计数减 1,减到 0 状态即被移除。因此一次性的状态(标中旗削弱、踩雷伏击这类,初始计数 = 1)恰好在授予它的那轮结算里生效一次、轮末消失,MVP 四行内容全部是一次性;将来若有跨轮持续的增益,寿命由表行的初始值表达——给几就活几次结算。停手不挖的轮、弃图刷新的轮也照常算一次结算、计数照减——不存在"不结算就不掉次数"的免费延时。 + +**举证**:计数量纲 = remaining_settlements(剩余结算次数),初始值 = 表行 duration_settlements(一次性 = 1);衰减的精确机制(每轮恰 −1、归 0 即销毁)与"先兑付后衰减"时序契约见「数值结论与推导」F3;时限语义逐要点见下方实现参照表。 + +### 规则 2 的实现参照——单一结算次数量纲(时限语义) + +| 要点 | 内容 | +|---|---| +| 计数量纲 | remaining_settlements(剩余结算次数)——全部状态唯一时限单位 | +| 初始值 | duration_settlements = 授予时初始剩余结算次数(实例持有快照、不受表重载影响——EC9) | +| 衰减 | 每轮结算兑付后统一 −1(先兑付后衰减——B1 兑付完才调 decay_round);归 0 即移除 | +| 一次性 | duration=1 → 授予当轮编排期入容器 → 同轮兑付恰一次 → 轮末移除(MVP 四行全部 D=1——当次结算语义) | + +#### 规则 3:状态只在结算时发放,每笔都记着从哪来 + +**人话**:挖掘期的挖格、插旗、踩雷不会直接改状态容器:棋盘(A2)只上报"发生了什么"的事实,战斗结算编排(B1)在每轮结算时统一把事实兑换成状态、发给容器。这是容器唯一的进货口,棋盘与奖励背包(B4)都没有直通容器的通道。每笔状态随身携带归因信息:哪类来源(安全格奖励/标中旗/踩雷/清图奖励四选一)、从哪个格子来、发生在第几轮——结算明细能逐行回答"这效果是我哪步扫雷带来的"。状态一经发入立即生效(没有"待生效"中间态):同一场结算就是它的第一次兑付。 + +**举证**:无计算。授予动作 = apply_effect(规则 6 五接口之一);四类来源的授予路径与目标侧逐源对照见下方实现参照表;来源取值封闭枚举见「枚举」节 source_class 表。 + +### 规则 3 的实现参照——三向效果映射(授予路径) + +| 事实来源 | source_class | 授予路径 | 目标侧 | MVP 行 | +|---|---|---|---|---| +| 安全格奖励 | safe_cell | A2 开格事实 → B1 转化 apply_effect | hero | attack_up / defense_up | +| 踩雷 | mine_hit | A2 开雷事实 → B1 转化 apply_effect | enemy(side-wide) | ambush | +| 标中旗 | flag_hit | A2 标中判定 → B1 转化 apply_effect;兑付资格(雷格实例键)判定在 A3/A2 域——资格判定不过 B3,B3 无从重复兑付同雷(每次 flag_hit 授予由 B1 保证资格合法) | enemy(side-wide) | weaken | +| 清图奖励 | clear_reward | B4/B1 清图槽含 buff 时授予(B4 期定稿) | 随行 | B4 差异化插槽(OQ4) | + +#### 规则 4:同名状态重复拿到,怎么合并由数据表说了算 + +**人话**:同一种状态还没消失时又拿到一份怎么办?答案不在代码里,而在数据表每行的"叠层策略"字段,共三种:叠加(层数 +1,寿命不动——只有第一次授予定时钟)、刷新(寿命重新计满、层数不变)、并存(各算各的一份、互不影响)。合并在授予那一刻由容器内部完成;多个状态在结算里按什么先后起作用,是结算编排的职责——两件事互不干扰。叠加的作用是纯加法:几层就是几倍的行值(单一线性换算)。MVP 四行内容全部用叠加;刷新与并存的语义已定义、实现已承载,未来内容行按需启用。 + +**举证**:三分支的逐分支语义(含满层/溢出行为与 MVP 消费)见下方实现参照表;合并执行的伪代码见「数值结论与推导」F1;"有效值 = 层数 × 行值"的加算合成见 F2。 + +### 规则 4 的实现参照——按行叠层三分支(stack_policy——授予时合并) + +| stack_policy | 重复授予行为 | 满层/溢出 | MVP 消费 | +|---|---|---|---| +| stack | 层数 +1(R 不动——只有初始授予定钟) | L=M → 拒绝 false + 容器零写,"溢出未生效"行归 B1 | 四行全用(attack_up/defense_up M=2、ambush/weaken M=16) | +| refresh | 剩余次数重置为实例授予时快照 D、层数不变(含 R 已 = D 的空刷新——恒返回 true,无满层/溢出语义、不消费 max_stack) | 无 | 语义已定义、实现承载——MVP 无内容行(零消费者) | +| independent | 并存独立实例(append 入列) | 无满层语义——实例数上界 = 受理授予数(有界确定) | 同上 | + +#### 规则 5:叠满了再给同款不生效,但会明说"这层浪费了" + +**人话**:一种叠加型状态已叠到上限(层数 = 上限值)时,再授予同类不生效:容器拒绝、内容一个字不变。但这份奖励不会被无声吞掉——结算明细会多一行"溢出未生效",连来源一起写明;玩家看得到"这层白拿了",下次就懂得满层时别再拿同款——奖励的浪费是诚实的、可规划的。无论这份重复来自玩家直接踩出的格子、结算转化、还是连锁展开自动开出的奖励格,都走同一个授予入口,满层时同样产生溢出行、同样留痕。 + +**举证**:无计算(判定 = 一次"层数 == 上限"比较)。拒绝原子性(false + 容器逐字不变、无半写无溢出落层)见 F1 推导极端行为段;"溢出未生效"行进结算明细协议由 B1 义务⑧承载、呈现归 B5/D1。 + +#### 规则 6:结算系统只用五个固定接口读写容器 + +**人话**:状态容器是纯被动的仓库,自己不主动做任何事;战斗结算编排(B1)要用它,只有五个固定接口: + +| 接口 | 干什么 | 返回 | +|---|---|---| +| 告知战斗开始 | 打开容器、进入可用状态 | 是/否 | +| 发一笔状态 | 唯一进货口:受理即新建实例/叠层/刷新 | 是/否 | +| 查询生效状态 | 某一方当前生效清单,按发放先后排列且顺序永远稳定 | 只读清单 | +| 轮末衰减 | 全部实例寿命 −1、归 0 即销毁 | 本轮移除数 | +| 告知战斗结束 | 全实例清空、容器回空 | 是/否 | + +**举证**:五接口 = notify_battle_start() / apply_effect(effect_id, source) → bool / query_active(side) → Array / decay_round() → int / notify_battle_end() → bool(签名见「验收标准」测试口径)。 + +- 查询返回 = 六归因字段 + display_name + 授予时数值快照 (magnitude, unit) 的只读投影(qa-lead 载荷口径)。 +- 结算作用顺序(哪个效果先乘)归 B1 SettlementTable,B3 只保证枚举确定性;最后一轮(敌人全灭)是否 decay 归 B1 判定次序域——A3 EC8 同款〔末轮 decay 判定次序域〕。 +- 全部 API 布尔/计数返回、无 await、无表现面——可回放步骤流的状态侧前提。 + +#### 规则 7:容器只管"有多少量、还剩几次",不管这量怎么用 + +**人话**:容器记录每个状态的量(幅度数值 + 量纲标签:百分比还是固定值)和它还剩几次结算,仅此而已。"增伤到底加在哪个算式上、先乘后乘、地板怎么收"这类"效果如何兑现"的问题,一律由结算编排的顺序映射表按状态 id 作答。一句话:容器知道"现在还有 +50、还剩 1 次结算",但不知道这 +50 会变成主角攻击里的什么。 + +**举证**:无计算。语义映射的承载面 = B1 SettlementTable(effect_id 为键);B3 只承载 magnitude/unit 两个通用数值字段。 + +#### 规则 8:已生效的状态玩家动不了——不能驱、不能延、不能转 + +**人话**:状态一旦生效,玩家没有任何手段把它摘掉、往后延、或挪给别人——容器没有提供这类接口。"不能做"和"能做"同样是明确的设计决定:省掉一整类界面与规则复杂度。将来若要加驱散/延时/转移,属于新增接口的修订,不是调参数。 + +**举证**:无计算。负空间规则——验收形态 = API 面封闭枚举 + 接口漂移修订门(见「验收标准」节测试口径 out-of-scope 行)。 + +#### 规则 9:所有数值写在数据表里,代码零写死 + +**人话**:容器本身不认识任何具体数值:哪个状态加多少、活几次、最多叠几层,全部写在状态表里(表结构由本文档定义、数据层 D2 承载),程序运行时统一从配置库 ConfigDB 读表。代码里不出现任何玩法数值字面量——调平衡只改表、不改代码。 + +**举证**:无计算。表 = StatusEffectTable(本 GDD 为回填 owner,schema 逐字段见「Tuning Knobs」节);依据 = D2 Rule 1〔数值字面量禁令〕。 + +#### 规则 10:四个 MVP 状态的初值由本文档定稿,验收权留在对口系统 + +**人话**:MVP 上线的四行内容(增伤、减伤、伏击、削弱)的初始数值由本文档定稿,值域快照见「枚举」节内容行表。但"值合不合适"的验收权留在对口系统(奖励经济 B4、敌情 B2、结算 B1):削弱档位按"必须严过最低档增益"的当量锚验收、伏击按敌方惩罚上限验收、对账由结算闭合。要改初值,走接口漂移规则回写登记库(registry),不是随手调参——初值定稿权归本文档、锚验收权仍归上述三方,这是对"档位归奖励表设计"的显式收编。 + +**举证**:无计算(定稿权条款非公式)。依据 = A3 F1 条款 2〔debuff 严格 > 最低档 buff 当量,参照对象 = attack_up 行当量〕· 条款 3〔B2 惩罚上限〕· A3 OQ5 收编〔registry ownership_note〕;A3 GDD 正文不改,以本条 + registry ownership_note 为准。 + +## 枚举 + +### source_class(封闭枚举——P2 归因字段) + +| 值 | 产出点 | 语义 | +|---|---|---| +| safe_cell | 安全格奖励授予 | 主角增益来源 | +| flag_hit | 标中旗授予 | 怪物削弱来源 | +| mine_hit | 踩雷授予 | 敌方增益来源 | +| clear_reward | 清图奖励授予 | 清图槽 buff 来源(B4 期定稿) | + +新增值 = 接口漂移。B3 不校验 source_class 与行的对应(dumb 账本——EC6 同族);内容行表"来源"列为内容档案(provenance),非 schema 字段、非运行时约束。 + +### target_side / unit / stack_policy(schema 枚举域) + +| 字段 | 值域 | 语义 | +|---|---|---| +| target_side | hero / enemy | 作用侧;enemy 侧 side-wide = 全体存活敌单位(层数 = side 共享池) | +| unit | percent / flat | percent = 百分比修正(百分点线性叠加);flat = 固定值(伏击类——量纲已定稿:每层伏击为每次攻击类行为附加 m_amb 点主角 HP 伤害、后置加法不进倍乘、charge 拍零兑付,B2 GDD 定稿) | +| stack_policy | stack / refresh / independent | 同 id 重复授予的合并策略(见核心规则子表) | + +### StatusEffectTable MVP 内容行(四行值域快照——值全部 provisional,验收锚见 Rule 10) + +| effect_id | 名称 | side | magnitude(现行值) | duration | stack/max | 来源 | provisional 依据 | +|---|---|---|---|---|---|---|---| +| attack_up | 增伤 | hero | +50 percent | 1(当次结算) | stack / 2 | safe_cell | 原型临时值 1.5× 换算(D2 knobs 占位);消费面 = 主角战斗段先手攻击(HeroTable.atk 基础攻击恒存在) | +| defense_up | 减伤 | hero | −30 percent | 1(当次结算) | stack / 2 | safe_cell | 减伤墙批 C3 投值(−50→−30,叠满两层 −60 帽——defense_scan 墙轮 39%/真人 12 轮零承伤定量坐实后回调) | +| ambush | 伏击(敌方增益) | enemy | +3 flat | 1 | stack / 16 | mine_hit | 原型临时值 +3;量纲已定稿(B2 GDD——registry ambush_unit_definition);条款 3 锚验收归 B2/B1 | +| weaken | 削弱(标中旗 debuff) | enemy | −65 percent(值域 [60, 85]) | 1 | stack / 16 | flag_hit | F0 裁决值——回执以 B1 GDD F0 为准(registry anchor_warning);须过条款 2 锚(严格 > 最低档 buff 当量,B1 对照验收) | + +## 数值结论与推导 + +> 章节纪律:结论先行(加粗一行),下接「推导」小标题给公式代码块、Variables 表与验算/示例。systems-designer 代理起草(lean 门禁):只含机制公式,平衡归属见显式排除声明。 + +### F0 weaken 锚裁决回执(m_w 值域与归一口径) + +**结论**:m_w 值域 [60, 85]、默认 −65;归一口径 = 每结算瞬时当量(每 AP 总暴露分支被 percent schema 域禁止,排除);地板交叉层数 = ⌈100/m_w⌉ = 2(值域内恒 2);B3 接口零修订、值变更走 registry 回写——**回执以 B1 GDD F0 为准**(registry status_effect.anchor_warning F0 裁决回执)。 + +#### 推导 + +| 分支 | schema 可行性 | 后果 | 裁决 | +|---|---|---|---| +| 每结算瞬时当量 | 可行(m_w ≥ 51 即过锚——percent 域内) | F2 越域界随移(AP 界 8×m_w / 表界 16×m_w);地板交叉层数 = ⌈100/m_w⌉ = 2;上调的连带成本(地板提前到达、边际层贬值)入裁决 | 采纳 | +| 每 AP 总暴露 | 不可行——需 m_w > 100,被 percent ∈ [−100, 100] schema 域禁止 | 现实解 = B4 重定最低档参照(非上调)——未采纳 | 排除 | + +- 同分母性:瞬时口径下 weaken 与 hero 侧 buff 作用于同一伤害流,"严格大于"在百分点读法与 HP 读法下等价,锚判定无量纲歧义;总暴露口径引入时长权重,把 duration knob 泄漏进锚判定。 +- 机制同构:B3 本就按结算逐次兑付,瞬时口径是容器原生口径;总暴露需跨结算累计账本,与已锁的"单结算自然边界"(dur=1 每结算清零无存量)冲突。 +- 值域依据(@D_ref=12):下界 60 = 条款 2 两腿可行交集(腿一 m_w ≥ 51——65 > 50 严格大于 attack_up 当量〔参照对象,registry 回迁口径〕;腿二随 E_dig 口径修正放宽——m_w ≥ 38,以 B1 GDD F0 重算回执为准);上界 85 = 超出仅在连锁贫瘠板增加裕度、地板交叉层数恒 2;schema 顶 100 保留为绝对上界。 +- 红警关闭:原 −25 值在两个独立线性代理下同向预检不过(条款 2 锚差约 4×,且"单一线性换算"堵死分源改率、"无单档上限"堵死 max_stack 豁免)——F0 修订后锚两腿通过(瞬时当量 7.8 > 6.0 严格大于 ✓ / 7.8 ≥ E_dig 4.5 ✓——E_dig 读数随重算批口径更新);终值共裁与 registry 终值确认联动 B1 OQ2(见开放问题 #1)。 + +### F1 同 id 叠层合并(same_id_merge,授予时执行) + +**结论**:授予时按行 stack_policy 三分支合并——stack 加层不触碰 R(只有初始授予定钟)、refresh 重置 R 取实例授予时快照 D、independent 并存新实例;满层拒绝原子(false + 容器逐字不变)。 + +#### 推导 + +``` +merge(effect_id) —— 前置拒绝(域闭 / 未知 effect_id)沿用 States 节 API 口径,不入分支。 +行 = StatusEffectTable[effect_id],按 行.stack_policy 三分支: + + independent → 恒新建实例 {L=1, R=D},append 入列(同 id 并存多实例) + + stack → 无存活实例 :新建 {L=1, R=D} + 有存活实例 ∧ L < M :L ← L+1(R 不动,见注) + 有存活实例 ∧ L = M :返回 false(满层拒绝;容器零写入,溢出行归 B1 结算明细) + + refresh → 无存活实例 :新建 {L=1, R=D} + 有存活实例 :R ← D(L 不变;含 R 已 = D 的空刷新,恒返回 true—— + refresh 无满层/溢出语义,不消费 max_stack) +``` + +注(spec 决议):stack 授予不触碰 R——duration_settlements 只有初始授予定钟;若 stack 也重置 R,则 stack ≡ refresh + 加层,三分支失去区分性。refresh 的重置目标 D 取实例持有的授予时快照(与 EC9 快照语义一致——实例不受表重载影响)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| effect_id | e | String | 表主键 | StatusEffectTable 行键;未知 = 拒绝 | +| stack_policy | P | enum {stack, refresh, independent} | 表列 | 合并分支选择器(Rule 4) | +| max_stack | M | int [1, 16] | 表列 | 仅 stack 分支消费;refresh/independent 不读(未来 refresh 行配 M>1 时行为 = 忽略) | +| duration_settlements | D | int [1, 99] | 表列 | 新建实例初始 R;refresh 的重置目标即此值 | +| layers | L | int | stack 行 [1, M];其余恒 1 | 实例层数(越界 = debug 断言,States 节不变式) | +| remaining | R | int | 存活态 [1, D] | 剩余结算次数;归 0 移除(F3) | + +- **Output Range:** 正常域——stack/refresh 行同 id 存活实例数 ≤ 1(不变式);independent 行实例数 = 受理授予数(有界确定);返回值 bool。极端行为——满层授予恒 false 且容器逐字不变(拒绝原子性:无半写、无溢出落层);同轮批量授予的先到先得次序由调用方保证(B1 编排须按确定次序调用 apply_effect——B3 只保证插入序枚举稳定,B1 验收项义务①)。 +- **Example:** attack_up(stack,D_a=1、M=2):轮 3 同轮授予批次两个安全格事实 → apply_effect×2 → L=2、R=1;同轮第 3 个授予 → L=2=M → **false**(明细"溢出未生效"行);兑付 +100%(F2)→ 轮末 decay R=0 移除。weaken(stack,D_w=1、M=16):轮 3 三旗命中三雷(雷格实例键,三次授予均合法)→ L=3、R=1;兑付 3×m_w(默认 65 → −195%)→ decay 移除;轮 4 一旗命中新雷 → 旧实例已销毁,**新建** {L=1, R=1}——dur=1 行的层只在单轮授予批次内累积,跨轮必为新实例。dur ≥ 2 行(策略探针 D_f=3):跨轮 R 逐轮 −1,refresh 腿见 AC-5。 + +### F2 有效值合成(effective_value_composition,兑付时只读) + +**结论**:有效值 = L × m 加算(unit 量纲标签原样随值输出、不进乘法);合成值不 clamp、可越出 percent 域——地板/clamp 归 B1 SettlementTable。 + +#### 推导 + +``` +effective_value(实例) = L × m (unit 随行声明,量纲标签原样随值输出、不进乘法) + stack 行:L ∈ [1, M];refresh / independent 行:L ≡ 1(每实例恒一倍) + 加算语义:percent 行的 L×m 是百分点线性叠加(+50 × 2 层 = +100 个百分点); + 乘算读法(1.5^L)在 B3 语义域外。 + 表注:行值出处(provenance,如"原型 1.5×")是数值来历标注,非合成语义。 +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| layers | L | int | 同 F1 | 叠层数 | +| magnitude | m | int | percent ∈ [-100,100] ∧ ≠0;flat ≥ 1(单行字段域,D2 校验) | 行值;合成值不继承此域(见 Output Range) | +| unit | u | enum {percent, flat} | 表列 | 量纲标签;percent = 百分比修正、flat = 固定值;消费语义(乘除侧、地板、单位定义)不在 B3(见排除声明) | + +- **Output Range:** 正常域——percent 行有效值 ∈ [1, 100×M],**可越出 [-100, 100],B3 不 clamp**(合成值地板/clamp 归 B1 SettlementTable——B1 验收项义务③);flat 行 effective ≥ 1。极端行为——weaken 合成下界两档并列:**AP 界 = 8×m_w**(默认 65 → −520%;默认经济下 flag_hit 单轮 ≤ ap_per_round=8 次授予、dur=1 层只在单轮累积 → L=8)与**表界 = 16×m_w**(默认 65 → −1040%;仅非 AP 源如 clear_reward 授予、或 ap_per_round 调参后可达 L=16)——两档均越域,均不在本系统内收口;m_w 值域见 F0。跨 effect_id 聚合、percent 与 flat 混算 = B1 SettlementTable 域(Rule 7)。 +- **Example:** attack_up L=2 → +100 percent(兑换成 2.0× 与否的读法归 B1);defense_up L=2 → −60 percent(B1 换算 0.4×——现行值链下不触地板,"归零"读法须 B1 定义);weaken L=8 → 8×m_w(默认 −520 percent);ambush L=3 → **+9 flat**(3×m_amb——B3 不解释单位)。 + +### F3 结算衰减(settlement_decay,轮末执行) + +**结论**:先兑付后衰减——每实例每轮恰 −1、归 0 即销毁(不留 0 值实例)、返回本轮移除数 k;无参数、无豁免实例。 + +#### 推导 + +``` +decay_round() -> k: + k ← 0 + for 实例 ∈ 容器(插入序枚举,零随机): + R ← R − 1 + if R = 0:销毁实例;k ← k + 1 + return k +``` + +时序契约:**先兑付后衰减**——B1 经 query_active(side) 完成本轮兑付后才调用;duration=1 实例的落点由此完整确定 = 授予当轮编排期入容器 → 同轮兑付恰一次 → 轮末 R=0 移除("一次性"语义的全部机制内容)。停手轮/弃图轮同为结算、照常衰减(Rule 2,无免费延时);最后一轮(敌人全灭)是否 decay 归 B1 判定次序域(Rule 6,A3 EC8 同款——battle_end 全清兜底,两种次序产生同终态)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 容器 | — | 实例多重集 | 存活实例全体 | 枚举序 = 插入序(Rule 1 确定性) | +| remaining | R | int | 存活态 [1, D];decay 后 ∈ [0, D−1] | 单调递减;refresh 的 R←D 发生在授予时(F1),不在本式内——两序分离 | +| 返回值 | k | int | [0, 容器规模] | 本轮移除数 | + +- **Output Range:** 正常域——每实例每轮恰 −1,无参数、无豁免实例;k ≤ 调用前容器规模。极端行为——空容器 → k=0;全 dur=1 轮 → k=容器规模(全清);R 恒不因 decay 变负(归 0 即销毁,不留 0 值实例——与不变式 remaining ∈ [0, D] 相容)。 +- **Example:** 轮 3:编排期授予 attack_up(L=1、R=D_a=1)→ 兑付 +50% → decay_round()=1(移除);轮 4 query_active(hero) 已不含 attack_up。dur ≥ 2 行(策略探针 D_f=3):授予后逐轮兑付、R 逐轮 −1、第 3 轮末移除——R 轨迹与 k 全由表读值推导。 + +### 显式排除声明(R4 式) + +本 GDD **无平衡公式**——F1–F3 只承载机制数学(合并/合成/衰减),不含任何"值该多大"的判断;四行 MVP 值全部 provisional(Rule 10)。平衡与语义归属逐一列明: + +| 对象 | 归属 | 依据 | +|---|---|---| +| 效果档位当量对照表(四源统一当量、单一线性换算) | **B1**(出表方兼闭合对账单一签字人) | A3 F1 条款 1 | +| weaken 档位锚(debuff 当量严格 > 最低档安全格 buff 当量,参照对象 = attack_up 行当量;基准 = 连锁放大边际挖掘收益,provisional 1:1) | **B4 表设计 + B1 对照验收** | A3 F1 条款 2 | +| 挖掘风险不等式(P(安全)×buff 当量下界 > P(雷)×敌方增益当量上界;B2 惩罚上限表值) | **B2 供给上限表值,B1 单一闭合对账** | A3 F1 条款 3 | +| 奖励定价(清图奖励及 B4 三硬约束:雾内档位下界 / 锚随雾成本上浮 / 净当量优势带上界) | **B4** | A4 pricing_constraints;A3 F1 条款 4/5 | +| percent 的消费侧换算与地板(乘除侧读法、合成值越域 clamp);flat 的量纲定义 | percent → **B1 SettlementTable**(Rule 7);flat 量纲 → **B2**(已定稿——registry ambush_unit_definition:每层伏击为每次攻击类行为附加 3 点主角 HP 伤害、后置加法) | Rule 7 + F2 Output Range;Schema 说明列 | +| MVP 四行初值定稿权 | **B3**(Rule 10);B4/B2/B1 持锚验收权,修订走接口漂移回写 registry | Rule 10 | + +B3 只对"L×m 线性合成 + R 计数"负责:合成值多大算强、多弱算废、floor 在哪、+3 打谁——均不在本 GDD 语义域。 + +## 状态 + +容器为**纯逻辑类**(非 autoload、无场景依附),每场战斗实例化一次(B1 编排前提;冒烟经 class_name 直引构造)。容器无运行时大状态机——实例生命周期四态(战斗域): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| BattleClosed(域闭) | 构造后未开域 / notify_battle_end() 后 | apply_effect 全拒绝(false);唯一出口 notify_battle_start() | +| Active(存续) | apply_effect 受理(新实例/叠层/刷新) | 每轮结算被 B1 兑付消费;decay_round() 后 remaining −1 | +| Expired(移除) | remaining_settlements = 0 | 实例销毁(明细流水已由 B1 记录,B3 不留历史) | +| Cleared(清空) | notify_battle_end() | 全实例销毁、容器回空 → BattleClosed(不跨战斗) | + +不变式(debug 断言口径):remaining ∈ [0, duration_settlements];stack 行 layers ∈ [1, max_stack];stack/refresh 策略下同 id 至多一个实例——违反即 debug 断言中止;API 违规(域闭授予、未知 effect_id、满层授予)= 拒绝(false)+ debug 诊断,不进程致命(承 A3 拒绝口径;进程致命保留给 D2 数据通道)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 域闭状态授予(BattleClosed 下 apply_effect) | 拒绝(false)、容器不变 + debug 诊断——B1 过早/过晚调用 = 编排错误显式暴露,不静默吞 | States 节 API 口径 | +| EC2 未知 effect_id | 拒绝(false)+ debug 诊断;表行删除/改名后旧引用的暴露点——悬空引用加载校验 = 首引激活条款:B4 RewardTable 首个 effect_id 引用已回填、校验已激活(registry dangling_ref_policy;条件性夹具 = B4 b4_dangling_effect) | A4 counter_item_ids 同款 · D2 首引兑现 | +| EC3 满层授予 | 恒 false 且容器逐字不变(拒绝原子性——无半写、无溢出落层);"溢出未生效"行归 B1 结算明细;同轮多授予谁被拒取决于 B1 授予次序——授予次序确定性是 B1 编排义务 | F1 Output Range · Rule 5 · 义务① | +| EC4 空刷新(refresh 行 R 已 = D 再授予) | 返回 true、无任何变化——合法空操作(refresh 无满层/溢出语义);MVP 无 refresh 内容行 = 该语义零消费者,首个 refresh 行上线时须补冒烟 AC;空刷新静默返 true 与 Rule 5"不静默蒸发"存在原则张力(stack 有溢出行、refresh 无对应物)——MVP 零消费者故休眠,首个 refresh 内容行上线时其授予可见性语义由 B4/B1 随 OQ3 一并裁决 | F1 决议 · systems-index 登记 | +| EC5 同轮 dur=1 行多次授予 | 层在单轮授予批次内累积(L=N)→ 同轮兑付一次(合成 L×m)→ 轮末全移除;跨轮必为新实例——"N 旗 = N debuff 总吞吐"在 B3 侧的呈现 = 单轮层和,对账归 B1 | F1 Example · A3 条款 2 | +| EC6 同一事实被重复授予(同 effect_id + origin_cell + round_index 两次 apply) | B3 不去重——容器是 dumb 账本,无从辨别事实合法性,两次授予 = 两层/两实例;事实→授予的恰一次转化归 B1 编排义务(round_end 双发射源同族问题,A3 先例) | 义务② | +| EC7 合成值越域(L×m 越出 [-100, 100] 或穿越地板) | B3 不 clamp、值 + unit 标签原样输出——收口归 B1 SettlementTable(B1 验收项:percent→乘数换算方向与地板规则须成文——weaken AP 界/表界、defense_up 叠满三例已在 F2 登记) | F2 Output Range · 义务③ | +| EC8 清图换图/弃图刷新 | 容器不清空——状态跨图延续(战斗域非图域);旗标记随图消亡(A2 域)不影响已授予实例;已消费兑付资格的雷格随图消亡(A3 域),新图新雷自然新资格——B3 无从感知也无须感知 | Rule 1 · AC-10 | +| EC9 战斗中热重载 StatusEffectTable | 走 D2 既有门(战斗中 reload 拒绝);已授予实例不受任何表重载影响——实例在授予时从表行拷贝数值快照(m/D/unit),热重载只影响后续授予 | A4 EC8"快照非活引用"同款 | +| EC10 refresh/independent 行配置 max_stack > 1 | 行为 = 忽略(F1 分支不读 M)——内容约定非校验项,validate() 不拒绝(防过度约束锁死未来内容) | F1 · AC-13 | +| EC11 notify_battle_start / notify_battle_end 重复或错序 | Active 下再 start → false + 诊断;BattleClosed 下再 end → false + 诊断(bool 口径统一,承 A3 输入×相位矩阵同款) | A3 同款 | +| EC12 query_active 空 side 或空容器 | 返回空 Array——合法状态非错误;D1 HUD 据此渲染"无状态" | D1 空态义务 | +| EC13 decay_round 在域闭时被调用 | 返回 0——域闭时容器恒空(battle_end 已清),0 是自然结果、无独立分支、不诊断(幂等安全的兜底读法) | F3 极端域 | +| EC14 independent 行被高频授予 | 实例数上界 = 受理授予数(有界、确定,无满层语义)——MVP 无此内容行;未来 DOT 类高频授予的容量语义归扩展(接口漂移),本 GDD 不预写 | F1 Output Range | + +## 依赖 + +**上游**(Foundation 层,无其他玩法上游): + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | hard(数据通道) | StatusEffectTable schema 承载 + ConfigDB 读取;本 GDD 为该表回填 owner(schema + 四行已定稿);新字段走接口漂移规则。systems-index 分层表按其惯例不列纯数据读取边(事实依赖同口径)——两处相容非矛盾 | + +**下游**: + +| 依赖方 | 性质 | 接口要点 | +|---|---|---| +| B1 | hard | 结算编排期调用五 API:notify_battle_start / apply_effect(三源授予——B1 从 A2/A3 事件收集 round facts 后转化)/ query_active(side) 兑付消费 / decay_round() 轮末衰减 / notify_battle_end() 清空;结算明细来源标注数据源 = 实例字段(P2);效果语义映射与结算作用顺序归 B1 SettlementTable(effect_id 键);F1 条款 1 对照表的"实物"供给方;最后一轮 decay 与否归 B1 判定次序域;义务清单 8 项见下表 | +| B4 | soft | 安全格 buff 行值与 weaken 档位的锚验收权(条款 2 + A4 三定价硬约束引用"最低档安全格 buff 当量"——参照对象 = attack_up 行当量);清图奖励若含 buff 经 apply_effect(source=clear_reward,B4 期定);RewardTable 引用 effect_id 的悬空校验已随首引激活(EC2) | +| B2 | soft | ambush 行量纲/上限验收锚(条款 3:B2 惩罚上限表 vs B3 行值,对账归 B1 闭合;量纲已定稿——registry ambush_unit_definition);未来敌人词条引用状态(免疫/抗性)为预留注记,不预写 | +| A3 | 契约相邻(零运行时接口) | A3 不调用 B3;条款 1 四源当量对照以 B3 实例为对照实物;标中旗兑付资格(雷格实例键)判定在 A3/A2 域,B3 只接收 B1 转化的授予——资格判定不过 B3 | +| A2 | 间接(零直接接口) | 开格/标中/开雷事实经事件 → B1 收集 → apply_effect;A2 不感知状态容器 | +| D1 | soft | query_active(hero) 为 HUD 状态栏数据源(display_name/层数/剩余结算次数常显;EC12 空态渲染);层数/到期变化反馈——义务清单 ×4 见下表 | +| B5 | soft | 结算明细/战斗动画的实例字段数据源(经 B1 结算流水;MVP 明细行即呈现,动画归垂直切片)——义务 ×2 见下表 | +| D5 | soft | 教学口径:按结算次数计时("还剩 N 次结算"式读法随行值)、战斗结束全清(防"我的 buff 哪去了"误读——A3 RAP 清零教学同款)、满层溢出浪费提示、"同轮拿满优于跨轮叠"教学项(stack 时序补偿——OQ7 证伪项配套,证伪成立前以中性提示形态) | + +### B1 侧义务清单(登记 B1 GDD 验收项,缺项即 blocking——A4 对 A1/A2 登记先例) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| ① | 同轮多授予按确定次序调用(EC3) | B1 | 已落位 B1 GDD(履行回执) | +| ② | 事实→授予恰一次转化(EC6) | B1 | 已落位 B1 GDD(履行回执) | +| ③ | percent→乘数换算方向与合成值地板成文(EC7/F2——AP/表界数值随 F0 修订更新为 −520%/−1040%) | B1 | 已落位 B1 GDD(履行回执) | +| ④ | 最后一轮 decay 判定(F3) | B1 | 已落位 B1 GDD(履行回执) | +| ⑤ | F1 条款 1 效果档位对照表以 B3 实例为对照实物 | B1 | 已落位 B1 GDD(履行回执) | +| ⑥ | 每结算编排恰一次 decay_round()——decay 基数 = 1 是 dur=1 一次性语义与全部衰减语义的先决条件;Active 相位 double-decay 在 B3 侧无守卫(信任 B1 编排,误编排经本义务验收与可回放步骤流暴露) | B1 | 已落位 B1 GDD(履行回执) | +| ⑦ | 结算明细按授予流水逐条标注来源(F1 合并坍缩归因的兑付面补偿——合并实例只持首授 origin_cell/round_index,per-layer 来源还原的数据源 = B1 授予流水) | B1 | 已落位 B1 GDD(履行回执) | +| ⑧ | "溢出未生效"行进结算明细协议(Rule 5 后半的 B1 侧承载;呈现归 B5/D1) | B1 | 已落位 B1 GDD(履行回执) | + +### 呈现与教学义务清单(已登记 systems-index,缺项即 blocking,随各 GDD 门禁评审核对) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| D1-① | HUD 常显 + EC12 空态规格定稿(隐藏栏 vs 空槽由 D1 裁决但须成文) | D1 | 已登记 | +| D1-② | effect_id→icon 映射键契约(art-bible 形状+色相双通道) | D1 | 已登记 | +| D1-③ | 有利/有害极性呈现映射(数据源 = B1 SettlementTable 效果语义,非 magnitude 符号——负值增益存在) | D1 | 已登记 | +| D1-④ | 战斗结束全清的状态栏去留 | D1 | 已登记 | +| B5-① | 溢出行呈现(数据源 = B1 义务⑧明细协议) | B5 | 已登记 | +| B5-② | origin_cell→玩家可读形态(缩略图/格子高亮映射——坐标值本身不直接呈现) | B5 | 已登记 | +| D5 | "同轮拿满优于跨轮叠"教学项(OQ7 证伪项配套) | D5 | 已登记 | +| B4 | 奖励身份预读(stack 时序规划的推理原料——创意风险 #1 切面,OQ7 配套补偿载体) | B4 | 已登记 | + +### 一致性核对与注记 + +| 项 | 内容 | +|---|---| +| 双向一致性 | B4 ← A2, B3, D2 ✓ · B1 ← A3, B2, B3, B4, D2 ✓ · B3 ∈ Foundation、无上游 ✓(systems-index 依赖分层表) | +| B2 无边说明 | 索引无 B2←B3 边与上表 B2 行不矛盾——敌方状态的承载在 B3,但授予路径经 B1,B2 与 B3 之间是验收锚关系非运行时依赖,故不列边 | +| D2 hard 措辞 | "hard"指 ConfigDB 读表的数据通道事实依赖(registry config_db referenced_by=[all-systems] 同口径);索引分层表不列纯数据读取边——相容非矛盾 | +| Rule 10 回写 | 对 A3 OQ5"档位归 B4 表设计"的收编(初值定稿权归 B3、锚验收权留 B4/B1)已回写 systems-index 门禁记录与 registry ownership_note | + +### 呈现与 UI 契约(数据面义务——B3 无自有渲染面,呈现归 D1/B5) + +| # | 数据面义务 | 归属 | +|---|---|---| +| 1 | HUD 状态栏数据源三要素:图标(effect_id→icon 映射归 D1/art-bible)、层数(L)、剩余结算次数(R)必须可读——时限/剩余次数不可读 = 违反 P1 规划素材与 P2 兑现可见性 | D1 | +| 2 | 结算明细行数据源:display_name + 有效值(L×m)+ unit 标签 + 来源标注(source_class→文案映射归 B1/D1,P2 归因)——"增伤 +100%(第 3 轮 · 安全格)"式逐行读法;origin_cell 玩家可读形态(缩略图/格子高亮)归 B5(坐标值本身不直接呈现);满层溢出提示 = "溢出未生效"行含来源(Rule 5 诚实语义) | B1/D1/B5 | +| 3 | 视觉身份锚适用(game-concept):数值可读性优先于氛围(锚 1)——状态数值在暗色底上须高对比;增益/削弱图标不得只靠色相区分(锚 2 形状+颜色双通道);有利/有害极性不得由 magnitude 符号推导(负值增益存在——数据源 = B1 SettlementTable 效果语义) | D1 | +| 4 | 零信号外发:纯被动容器不发任何事件——呈现刷新点 = B1 结算编排完成后的界面刷新(D1/B5 域);到期/层数变化反馈经 decay 前后查询差分或 B1 流水(D1 裁决);无音频需求(到期/叠加音效归 D4 Alpha 期) | D1/B5/D4 | + +## Tuning Knobs + +### StatusEffectTable schema(D2 承载,B3 回填 owner——Rule 4 豁免:定义表非进度成长表,无 (level_index, node_depth) 维度;未来随关卡成长的效果走接口漂移加列,A4 先例) + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| effect_id | String(主键唯一) | 状态标识;B1 SettlementTable 语义映射的键 | +| display_name | String | 图例/HUD/明细用名(本地化归 D6) | +| target_side | enum {hero, enemy} | 作用侧(enemy 侧 side-wide = 全体存活敌单位) | +| magnitude | int | 域随 unit:percent ∈ [-100, 100] 且 ≠0 / flat ≥ 1(跨字段校验) | +| unit | enum {percent, flat} | percent = 百分比修正;flat = 固定值(伏击类) | +| duration_settlements | int [1, 99] | 授予时初始剩余结算次数(一次性 = 1) | +| stack_policy | enum {stack, refresh, independent} | 同 id 重复授予的合并策略(Rule 4) | +| max_stack | int [1, 16] | 层数上限;仅 stack 消费——refresh/independent 行约定恒 1(内容约定,非校验项) | + +加载校验(表非空——D2 Rule 6,四行即满足):effect_id 唯一(重复键致命——本表唯一致命夹具 dup_key);枚举域校验(target_side/unit/stack_policy 出域致命);duration/max_stack 域校验;跨字段 percent ≠ 0 / flat ≥ 1。 + +**F0 裁决回执注记(m_w)**:weaken.magnitude 现行值域 [60, 85]、默认 −65——**回执以 B1 GDD F0 为准**(registry status_effect.anchor_warning);F2 越域界随移 AP 界 −520% / 表界 −1040%;AC-14 腿 3 非平凡性复查通过(16×65 = 1040 越出 percent 域)。原 −25 值的裁决关系与修订轨迹见版本历史。 + +### Knob 表 + +| Knob(表行值,D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| attack_up.magnitude | +50 | 叠满 +100%,敌方 HP 池相对贬值(战斗时长曲线压向 B1 对账) | 安全格奖励空心化——A3 条款 3 左臂(P(安全)×buff)塌方 | +| defense_up.magnitude | −30 | 叠满 −60%(免伤轮不再由 defense_up 单侧可达——B1 地板规则压力同步解除) | 减伤无感,防御型奖励萎缩(回调下界 = 减伤墙复发——观测锚 = C3 承伤带复验与 B1 条款③重跑) | +| weaken.magnitude | −65(值域 [60, 85]) | 越域界加深(表界 16×m_w)、地板交叉层数 ⌈100/m_w⌉ 恒 2、边际层贬值 | 值域下界 60 = 条款 2 锚两腿可行交集——越界即破锚(修订方向 B4/B1 裁决) | +| ambush.magnitude | +3 flat | 量纲已定稿(每攻击类行为 +3 HP)——踩雷痛感随值放大,条款 3 上限对账归 B1 | 踩雷无痛感——P3"风险与收益同源"失衡 | +| duration_settlements(四行) | 1/1/1/1(当次结算) | 窗口拉长恢复跨轮延续(规划变简单、衰减感消失) | 已在下界 1(一次性语义——再低无定义) | +| max_stack(四行) | 2/2/16/16 | 合成越域幅度增大(B1 地板压力↑) | 叠层机制边缘化(重复奖励变溢出浪费) | + +- **非 knob(结构冻结)**:stack_policy 三分支语义、unit 语义、source_class 枚举、时限量纲 = 剩余结算次数(game-concept 定稿)、先兑付后衰减时序、拒绝原子性、零随机——修订走本 GDD 流程非调参。 +- **Knob 交互**:attack_up 与 weaken 是条款 2/3 对账的**两端**——禁止独立调参,联动评估归 B1 闭合对账;A3 的 ap_per_round 是 weaken 单轮层和的经济上限(F2 AP 界 8×m_w 的来源);效果档位当量对照表(B1)落定前,四行 magnitude 的任何调整都须**重跑条款 2/3 锚预检**并同步 registry(接口漂移义务)。defense_up 叠满 −60% = 当次结算 0.4× 承伤(消费读法归 B1);减伤窗口强度与 attack_up 同入 B1 对账联动评估(B1 地板规则 × 战斗时长曲线 × 条款③ V_safe),禁止独立调参——defense_up.magnitude/duration 已纳入"禁独立调参"联动网络(m_w↔D_ref↔m_d↔duration_d,与 B1 Knob 交互同口径);减伤墙批(−50→−30)即该联动的实证回调(免伤轮现行可达路径 = weaken 饱和,见 F2)。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | B3 是纯行为容器——无场景、无表现面、无加载期自有校验类别。冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code)、ConfigDB autoload 由脚本显式实例化);容器经 class_name 直引实例化(每用例全新实例、无跨用例状态——A4 AC-5 直引先例同款;AC-19 扫描对该实例化路径豁免) | +| 被测面 | B1 就绪前冒烟直接驱动 B3 最小 API 面(承 A3"直接驱动最小 API 面"先例);B1 就绪后改走真实调用方(结算编排),断言口径不变。API 面:notify_battle_start() · apply_effect(effect_id, source) → bool · query_active(side) → Array · decay_round() → int · notify_battle_end() → bool | +| 返回口径 | 全部布尔/计数返回 = 受理/拒绝(A3 全 API 布尔口径同款)——"拒绝"直接断言返回值,附 debug 诊断、非进程致命(承 A3 拒绝口径;进程致命保留给 D2 数据通道);decay_round 返回值 = 本轮移除数断言;apply_effect 是唯一授予入口(Rule 3),冒烟直接构造合法 source = {source_class, origin_cell, round_index}(B1 转化产物的冒烟等价——信任方向同 A3 对 chain_size/clears_map 的处理);query_active 为只读、任意相位合法 | +| 只读观测量 | 实例六字段 effect_id · layers · remaining_settlements · source_class · origin_cell · round_index;query_active(side) 元素 = 结算面向载荷投影:六字段 + display_name + 授予时数值快照 (magnitude, unit)(EC9 快照语义 + B5/D1 数据源)——有效值 = layers × 快照 magnitude、unit 标签原样随值输出(F2);枚举序 = 插入序(Rule 6)。相位无字段观测量:BattleClosed/Active 由 API 返回值推断;Expired/Cleared 为销毁态不可观测(瞬态纪律同 A3 RoundEnd) | +| 时序口径 | B3 无 round_start/settlement API——"轮边界"在冒烟中 = 相邻两次 decay_round() 调用之间的授予/查询窗口(B1 编排在 B3 侧的可见面恰为这五个 API);多步 GIVEN 均给出构造序列 | +| 期望值纪律 | magnitude / duration_settlements / max_stack 均为 knob(Tuning Knobs 节)——各 AC 的 THEN 具体数值按当前 ConfigDB 表值推导、不写死(正文以 D_a / M_a / m_w 等表读值记号书写);结构字段(effect_id 集合、target_side、unit、stack_policy)非 knob、定值断言合法;行表"来源"列为内容档案(provenance)非 schema 字段、非运行时约束——B3 不校验 source_class 与行的对应(EC6 dumb 账本同族) | +| 夹具策略 | ① 默认根(干净检出四行 MVP 内容)承载全部分支行为 AC;② 程序构造(schema 类 class_name 直引实例)承载 AC-13 域校验——不经 load()/preload();③ 行为夹具根经 --data-root 注入(仅 debug、不入 data/tables/——D2 夹具纪律),承载 MVP 无行的语义(refresh/independent)。显式声明:夹具测试行非 MVP 内容(Rule 10 四行全 stack;测试行仅供冒烟)。选夹具根而非程序构造注入表的理由:容器经 ConfigDB 单点读表(Rule 2/9)、无表注入 API——--data-root 是 debug/test 构建的既定注入通道;各 AC 声明其表根(默认根/下列三根),用例间无顺序依赖、每用例先 reset_diagnostics()(D2 冒烟协议,全局声明) | +| 致命类收窄 | 恰一根致命夹具 status_effect_dup_key(重复键类别):runner manifest 退出码行 dup→≠0(独立 headless 进程、游戏通道,D2 AC-2b 协议同型)——"游戏内真的会死"在本表上的落点由此腿实证;其余致命类别(枚举域/域校验/跨字段)由 D2 AC-2 同类别 loader 级夹具承载(表无关,豁免声明收窄保留);schema 侧以程序构造断言域类别(AC-13) | +| out-of-scope | deferral 生效条件 = B1 义务清单(依赖节,均已落位):① 同轮多授予先到先得次序(EC3 后半——冒烟单调用方次序即构造序,B1 编排次序 = 义务①)② 事实→授予恰一次转化(EC6 后半——义务②)③ percent→乘数换算方向与合成值地板(EC7 收口半——义务③)④ 最后一轮 decay 判定次序(F3 时序契约的 B1 半——battle_end 全清兜底使两序同终态,冒烟不构造)⑤ 效果档位对照表以 B3 实例为对照实物(义务⑤,对账归 B1)。另有:结算明细"溢出未生效"行与来源标注呈现(冒烟无明细观测量——对侧承载已登记:B1 义务⑧ + B5×2,deferral 生效条件成立);D1 HUD 状态栏与 EC12 空态渲染(D1×4 已登记);效果语义到结算公式映射(Rule 7——B1 SettlementTable 域);Rule 8 负空间(无驱散/延时/转移 API——"无对应 API"不可 GIVEN-WHEN-THEN,验收形态 = 前言 API 面封闭枚举 + 接口漂移修订门);D2 schema 校验致命端到端(双通道协议承载);悬空引用校验(EC2——D2 双通道条件性夹具);EC9 reload 门腿(D2 AC-16 既有覆盖 + B1 是置位/复位唯一调用方——B3 无断言义务);EC8 端到端换图腿(B1 编排合规,AC-10 裁决) | + +夹具规格(登记值即相关 AC 的推导基准): + +| 根 | 内容 | 用途 | +|---|---|---| +| status_effect_policy_rows | refresh_probe(display_name="刷新探针" · hero · percent · magnitude=+10 · duration=3 · stack_policy=refresh · max_stack=4——故意违反"refresh 行恒 1"内容约定)+ independent_probe(display_name="独立探针" · enemy · flat · magnitude=+2 · duration=3 · stack_policy=independent · max_stack=1)——全字段域内合法数据 | AC-5 / AC-6(EC4 / EC10 / EC14 载体) | +| status_effect_policy_rows_reloaded | 仅 fresh_probe 一行(hero · percent · magnitude=+20 · duration=9 · stack · max_stack=1);根规格 = D2 全表清单拷贝 + 仅置换 StatusEffectTable 为本单行(缺任一表文件即触发 D2 EC1 文件缺失致命、GIVEN 不可构造——"仅一行"指该表内容非根内容) | AC-15 重载腿 | +| status_effect_dup_key | D2 全表清单拷贝 + StatusEffectTable 置换为默认四行 + 追加一行与 attack_up 同 effect_id 的重复键行(其余字段域内合法)——唯一致命夹具(A4 variation_dup_key 同型):loader 集成缝(loader 是否对本表调用校验)的进程级落点 | runner manifest dup→≠0 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | 全新容器(BattleClosed、空;表根 = 默认根) | ① 依次 apply_effect(attack_up, 合法 source)、query_active(hero)、query_active(enemy)、decay_round();② notify_battle_start();③ 再次 notify_battle_start() | ① apply 返回 **false**(EC1 域闭拒绝)、双侧 query 均 = **空 Array**(EC12)、decay 返回 **0** 且诊断收集器零新增(EC13——空容器域闭的兜底读法,非独立分支不诊断);② **true** 且 query 仍双侧空(战斗开始时为空——Rule 1);③ **false**、相位与容器不变(EC11 前半——Active 再 start) | +| AC-2 | Active 且已建账(构造:apply_effect(attack_up) ×1 + apply_effect(weaken) ×1,query 断言含两实例) | ① notify_battle_end();② 再次 notify_battle_end();③ decay_round();④ apply_effect(attack_up, …);⑤ notify_battle_start() | ① **true**、query 双侧空(Cleared 全清、不跨战斗——Rule 1);② **false**(EC11 后半——Closed 再 end);③ **0** 且诊断零新增(EC13 域闭腿);④ **false**(EC1 全场战后腿);⑤ **true** 且容器空(唯一出口——下一场战斗重启,States 表) | +| AC-3 | Active | apply_effect("attack_up", {safe_cell, (3,4), round_index=2}) | true 且 query_active(hero) 恰 1 实例,其六字段逐一相等:effect_id="attack_up"、layers=1、remaining=D_a(attack_up.duration_settlements 表读值)、source_class="safe_cell"、origin_cell=(3,4)、round_index=2(P2 归因字段授予即携带——Rule 3,结算明细逐行标注的数据源) | +| AC-4(stack 分支前两腿 + "R 不动" spec 决议) | Active;D_a / M_a 为表读值 | 腿 1:无存活实例时 apply_effect(attack_up, …);腿 2(前提 D_a ≥ 2 ∧ M_a ≥ 2——默认表值 D_a=1 不满足,本腿默认退化跳过 + 显式 SKIP 标记防 CI 静默、不假失败,前提满足时执行;构造 = 腿 1 实例经 decay_round() 一次至 R=D_a−1)再 apply_effect(attack_up, …) | 腿 1:true、新实例 **L=1、R=D_a**;腿 2:true 且 **L=2、R 仍 = D_a−1**——**stack 授予不触碰 R**(F1 注 spec 决议:只有初始授予定钟;实现若重置 R 即三分支失去区分性,本腿即其暴露点) | +| AC-5(refresh 分支 + EC4 + EC10;表根 = status_effect_policy_rows,测试行非 MVP 内容) | Active;D_f=3、m_f=+10(夹具规格登记值) | ① apply_effect(refresh_probe, …);② decay_round() 后 apply_effect(refresh_probe, …);③ R 已 = D_f 时再 apply_effect(refresh_probe, …);④ 再 apply_effect ×2;⑤ 该根经测试通道加载 | ① true、新实例 L=1、R=D_f、载荷 = 1×m_f(refresh 行 L≡1——F2);② 返回 0(R=D_f−1)后 **true、R 重置 = D_f、L 不变**(F1 refresh 正常腿);③ **true 且实例六字段 + 载荷逐字不变**(EC4 空刷新 = 合法空操作——refresh 无满层/溢出语义);④ 均 true(max_stack=4 从不被消费——**EC10 行为 = 忽略**);⑤ 诊断为零(内容约定非校验项——EC10 非校验半句) | +| AC-6(independent 分支 + EC14;同夹具根,测试行非 MVP 内容) | Active;D_i=3(夹具规格登记值) | 同轮授予批次内以互异 origin_cell((1,1)/(2,2)/(3,3)——插入序可观测的前提)apply_effect(independent_probe, …) ×3;再 decay_round() | 全部 true 且 query_active(enemy) 恰 **3 个并存独立实例**(各 L=1、R=D_i、**投影序列 = 授予序** (1,1)→(2,2)→(3,3)——互异 origin_cell 使插入序成为可判定断言,Rule 6 枚举确定性);decay 返回 0(D_i≥2 无移除)且三实例各自 R=D_i−1(F3 逐实例、无豁免);实例数上界 = 受理授予数(**EC14 有界确定**——无满层语义) | +| AC-7(EC6 不去重) | Active 且已有 attack_up 存活实例(L=1,构造同 AC-3) | 以完全相同的 (effect_id, source)(同 origin_cell、同 round_index——同一事实重复转化的模拟)再次 apply_effect | true 且同实例 **L=2**——B3 **不去重**(dumb 账本,无从辨别事实合法性,EC6);out-of-scope:事实→授予恰一次转化归 B1 编排(义务②,deferral 生效条件 = 依赖节清单) | +| AC-8(F3 主式) | Active(构造:apply_effect(weaken) ×2 → 单实例 L=2、R=1;apply_effect(attack_up) ×1 → R=D_a;前提 D_a ≥ 2——第二 WHEN 腿依赖,默认表值 D_a=1 不满足 → 本条退化跳过 + 显式 SKIP 标记(同 AC-4 口径、防 CI 静默),前提满足时执行) | ① decay_round();② 再 decay_round();③ 对空容器 decay_round() | ① 返回 **1**(= 构造中 dur=1 实例数,由表读值推导;weaken 实例移除)、attack_up R=D_a−1 仍在、容器插入序不变;② 返回 1(attack_up 移除——**R 归 0 即销毁,容器无 0 值实例**,与不变式 remaining ∈ [0, D] 相容);③ 0(F3 极端域)。R 轨迹与 k 全部由表值推导 | +| AC-9(EC5 dur=1 单轮叠层 + 一次性全落点) | Active(新轮) | 同轮授予批次内 apply_effect(weaken, …) ×3(N=3 为构造选择,≤M_w 表读值);decay_round();下一轮边界再授予 weaken ×1 | 全部 true、单实例 **L=3、R=1(=D_w 表读值)**、载荷 = 3×m_w(同轮兑付一次的合成输入;N 旗 = N debuff 总吞吐的对账归 B1 条款 2——义务⑤邻接,out-of-scope);decay 返回 1、实例移除;再授予 → **新建**实例 L=1——**跨轮必为新实例**(旧实例已销毁,F1 Example weaken 腿) | +| AC-10(EC8 跨轮/跨图延续——结构性裁决) | Active,attack_up 授予 ×1(R=D_a) | 连续多轮边界仅 decay、无新授予(停手轮/弃图轮的 B3 视角——B3 无图概念、换图/刷新零专用信号);逐步 query | 实例 R 逐轮 −1 **不停摆**(无豁免实例、无参数——**无"不结算就不掉次数"的免费延时**,Rule 2),且每步 query 断言容器非空直至 R 自然归 0——**除 notify_battle_end 外无任何 API 入口使容器清空**(先轮授予的实例按 R 存续至自然到期)。裁决:EC8 的"换图不清空"对 B3 的可测内容 = 本条两句(跨轮延续 + 唯一清空入口);"换图"事件对 B3 不可观测(零接口)——端到端"真实换图下容器仍存续"归 B1 集成(编排契约:仅 battle_over 调 end——B1 API 合规半句,B1 GDD 域,非新增义务) | +| AC-11(EC3 满层拒绝与原子性) | Active;M_a 为 attack_up.max_stack 表读值;构造叠满(M_a 次授予——次数由表值推导、不写死),前 M_a 次均 true、单实例 L=M_a | 第 M_a+1 次 apply_effect(attack_up, …) | **false** 且容器逐字不变——query_active(hero) 恰 1 实例,其六字段 + 载荷 + display_name 与授予前快照**逐项相等**(拒绝原子性:无半写、无溢出落层,F1 Output Range);"溢出未生效"明细行归 B1(out-of-scope,义务①邻接) | +| AC-12(EC2 未知 effect_id) | Active | apply_effect("no_such_effect", 合法 source);表行改名后的旧 id 同理 | **false**、容器不变。政策声明:悬空引用加载校验 = 首引激活条款——B4 RewardTable 首个 effect_id 引用已回填、校验已激活(registry dangling_ref_policy;条件性夹具 = B4 b4_dangling_effect;out-of-scope,D2 双通道) | +| AC-13(加载期域校验全谱——程序构造,class_name 直引,A4 AC-5 同型) | StatusEffectTable schema 类直引实例(不经 load()/preload()) | validate() 分别作用于非法组:percent=0(跨字段 percent≠0)/ percent=101 / percent=−101、flat=0(flat ≥ 1)、duration ∈ {0, 100}、max_stack ∈ {0, 17}、stack_policy/target_side/unit 各一出域枚举值、两行同 effect_id(重复键);再作用于域内代表值 percent ∈ {100, −100, 1, −1}、flat=1、duration ∈ {1, 99}、max_stack ∈ {1, 16} 及 stack_policy=refresh 且 max_stack=4 的行(EC10) | 非法组:各含对应域错误类别;合法组:零诊断(EC10——内容约定非校验项,防过度约束锁死未来内容) | +| AC-14(F2 合成 + EC7 不 clamp) | Active;m 各值为表读值 | 腿 1(percent·stack):apply_effect(attack_up) ×2(前提 M_a ≥ 2——默认满足,M_a=1 时退化为单层恒等断言 + SKIP 标记);腿 2(flat):apply_effect(ambush) ×3;腿 3(越域,EC7):同轮 apply_effect(weaken) ×M_w(M_w 表读值、≤16 同轮批授予合法);腿 4:refresh/independent 行载荷 L≡1 | 腿 1:载荷 = **2×m_a** 且 unit="percent" 标签随值输出(量纲标签不进乘法——F2);腿 2:载荷 = 3×m_amb、unit="flat"(B3 不解释单位——排除声明);腿 3:载荷 = **M_w×m_w 原样输出、无 clamp**("不 clamp"的可判定形式 = 载荷与表值推导值恒相等,任何地板都会使其不等;承载声明:本腿非平凡性依赖默认基线——M_w=16、m_w=−65 → −1040 越出 percent 域;knob 调参使 M_w×m_w 绝对值 ≤ 100 时退化为恒等断言、不假失败,退化时用例输出显式退化标记 SKIP 日志、CI 可见防静默失真;地板/clamp 与换算方向归 B1 SettlementTable——out-of-scope,义务③);腿 4:已由 AC-5①/AC-6 承载(引用不重复) | +| AC-15(EC9 快照 vs 表重载——机制口径) | 表根 = status_effect_policy_rows、Active、已授予 refresh_probe ×1 并 decay 一次(R=D_f−1、载荷 1×m_f) | 冒烟脚本经测试通道换根加载 status_effect_policy_rows_reloaded(load_tables(root, collect_only=true) 直调——非 D2 reload() 路径:不过 set_reload_gate、不发 config_reloaded、无 EC16 语义,门语义归 D2 AC-16/B1〔OQ5〕;reloaded 根规格 = D2 全表清单拷贝 + 仅置换 StatusEffectTable,见前言夹具规格);再 apply_effect(fresh_probe, …)(新 id、无存活实例——refresh 重置目标取实例快照 D 的口径见 F1 注,本腿不涉存活实例重置) | **已授予实例六字段与载荷逐字不变**(授予时拷贝 m/D/unit 数值快照——EC9"快照非活引用",A4 EC8 同款语义;实例为 B3 自持,故 B3 冒烟自测、无需 deferral);fresh_probe → true 且新实例 **R=9、载荷 1×(+20)**(夹具登记值)——**换根只影响后续授予** | +| AC-16(MVP 内容四行) | ConfigDB 默认根加载完成 | 读 StatusEffectTable | **恰含 4 行**、effect_id 集合 = {attack_up, defense_up, ambush, weaken}(定值);target_side:attack_up/defense_up=hero、ambush/weaken=enemy,unit:percent/percent/flat/percent,stack_policy=stack ×4(均非 knob 结构、定值断言合法——Rule 10"四行全 stack");display_name 四值非空(精确值对账归 AC-18);magnitude/duration_settlements/max_stack 仅作类型域断言、期望 = 表读值(knob 纪律——A4 AC-1 同款,防调参打假;行表"来源"列为 provenance 非表字段、不作断言) | +| AC-17(确定性——A3 AC-17 同型;可观测序腿) | 全新容器实例 ×2(同 ConfigDB 默认根;每遍新实例、无跨实例状态) | 同一定长序列驱动两遍:start → defense_up×1 → attack_up×1(同侧双实例、授予序 = 字典序**逆序**——区分"插入序实现"与"字母排序实现"的判别性前提)→ query 投影(hero 恰 2 实例,**投影序列 = [defense_up, attack_up] 定值断言**——插入序的行为级观测点)→ weaken×1 → decay → query 投影 → attack_up×1(并入腿——前提 D_a ≥ 2 成立时并入 L=2;默认表值 D_a=1 时旧实例已销毁,退化为新建 L=1 断言 + SKIP 标记,同 AC-4 口径)→ decay → end → query | 两遍的 query_active 投影序列(**含插入序**)与 decay_round 返回值序列**逐项相同**,且 hero 侧投影序恒 = 授予序(非字典序——零随机 + 插入序枚举的双重行为级证据——Rule 1;D2 AC-15 的 B3 层前身) | +| AC-18(registry 一致——A3 AC-10a 同型,无条件断言;registry 逐字段键) | ConfigDB 默认根加载完成 | 脚本解析 design/registry/entities.yaml 的 status_effect 条目(**逐字段 YAML 键**——机器对账前提,action_economy 同构) | ① fields 八键与 ConfigDB schema 字段名逐一相等、各键的类型/域/枚举描述与 schema 定稿相等(**缺任一键即失败**);② mvp_content 四键 id 集合与表行 id 集合相等,**非 knob 结构字段**(target_side/unit/stack_policy)逐一相等;③ rule4_exemption / dangling_ref_policy 注记存在性断言。knob 活值(magnitude/duration_settlements/max_stack)**不作定值对账**(A4 AC-8 同款——对账口径 = registry 随调参同步〔接口漂移义务〕,本 AC 豁免其值)。生效前提:本条目为定稿回填 + registry 逐字段键重构的产物,AC 生效前提 = 重构回填完成(不写"若存在"软口径——条件化使一致性检查落空,A3/A4 均无条件) | +| AC-19(静态扫描——D2 AC-9/AC-10 模式引用,A3 AC-10b 同型) | B3 文件集(容器/schema/冒烟脚本/夹具规格)纳入 D2 AC-9 模式项目级扫描域 | 静态扫描:randomize() / RandomNumberGenerator / ConfigDB 外 data 表加载 / extends Control / .tscn preload;+ D2 AC-14 人工三要素字面量扫描 | 全部**零命中**(schema 与容器的 class_name 直引实例化豁免——AC-13 前提,A4 AC-7 同款);字面量扫描引用不另设硬 THEN(字面量判定误报率高——D2 已定性 warning 级 + 人工复核);零随机的行为级硬证据 = AC-17 | + +### 覆盖矩阵(qa-lead 交付,验收评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules | Rule 1→AC-1/2/10/17/19 · Rule 2→AC-8/9/10 · Rule 3→AC-3+前言 API 封闭 · Rule 4→AC-4/5/6 · Rule 5→AC-11(明细行→B1 义务⑧+B5×2 已登记)· Rule 6→AC-1/2(decay 次序 OOS)· Rule 7→OOS(载荷只读面 AC-14)· Rule 8→前言 API 封闭+OOS · Rule 9→AC-13/16/19 · Rule 10→AC-16/18 | +| Formulas | F1→AC-4/11(stack)/AC-5(refresh)/AC-6(independent)· F2→AC-14+AC-5①/AC-6 · F3→AC-8/9/10 | +| 边界情况 | EC1→AC-1/2 · EC2→AC-12 · EC3→AC-11 · EC4→AC-5③ · EC5→AC-9 · EC6→AC-7 · EC7→AC-14 腿 3 · EC8→AC-10 · EC9→AC-15 · EC10→AC-5④+AC-13 · EC11→AC-1/2 · EC12→AC-1 · EC13→AC-1/2 · EC14→AC-6——无洞 | + +## 开放问题 + +| # | 问题 | owner | 状态/时机 | +|---|---|---|---| +| 1 | weaken 条款 2 锚裁决——归一口径已由 F0 裁决关闭(每结算瞬时当量;总暴露分支被 schema 域禁止排除),m_w 值域 [60, 85]/默认 −65 已入文。余项:终值共裁确认 + registry 终值回写,裁决时复查 AC-14 腿 3 非平凡性 | B4/B1(deadline:B1 GDD 强制检查点) | 余项随 B1 OQ2 联动 | +| 2 | ambush 量纲——已定稿(B2 GDD:每层伏击为每次攻击类行为附加 m_amb 点主角 HP 伤害、后置加法、charge 拍零兑付——registry ambush_unit_definition)。余项:B1 条款 3 上限检验 | B2 → B1 | 随 B1 对账闭合 | +| 3 | 首个 refresh/independent 内容行的 AC 补全——EC4 登记义务:奖励池引入非 stack 行时补真实内容行冒烟 AC,夹具探针行退役;其授予可见性语义(空刷新 vs Rule 5)随批由 B4/B1 裁决 | B4 | 奖励池引入非 stack 行时 | +| 4 | clear_reward 源授予——清图奖励若含 buff 的授予路径确认(apply_effect source=clear_reward;A3 条款 4 差异化插槽) | B4 | B4 期 | +| 5 | EC9 reload gate 的 B1 侧落点——防"战斗中真的被置位"零落点(qa-lead 缺口 #4) | B1 GDD 门禁评审 | 随 systems-index 门禁记录登记 | +| 6 | 效果当量对照表的预检重跑——条款 1 对照表落定时按预检重跑 weaken/ambush 方向性预检(正式对账载体);C3 首次调参即须按现行参照(attack_up 当量)复算条款 2 预检 | B1 | B1 对照表落定 / C3 首调 | +| 7 | stack-不重置 R 的 playtest 证伪项——F1 注决议(用户拍板、结构冻结)的体验面反例检验:"早拿必亏"(分轮拿满总暴露 150pp vs 同轮拿满 200pp——算例基线 = dur=2 行值;现行四行 D=1 下两读法总暴露相等、不对称休眠,dur ≥ 2 内容行上线时激活)叠加奖励身份不可预读(创意风险 #1)是否伤害"把 buff 纳入挖掘推理"的规划感(P1)。补偿载体先行:B4 奖励身份预读 + D5"同轮拿满"教学;证伪成立则走结构冻结修订流程回炉(合并/重定义 stack 分支)——非调参 | 垂直切片前置 playtest 批次 | playtest | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| 跨评审 B3 接口漂移批 | 2026-09-14 | /review-all-gdds 🔴-1 前半(减伤墙批回写缺口,registry 同批):defense_up.magnitude −50→**−30**(减伤墙批 C3 投值——叠满两层 −60 帽,defense_scan 墙轮 39%/真人 12 轮零承伤定量坐实)——MVP 内容行/Tuning Knobs/F2 Example 同步;免伤轮叙述同步(defense_up 单侧不可达 D'=0,现行可达路径 = weaken 饱和;F2 三例第三例改 defense_up 叠满读法——对侧 B1 F2 同批);Knob 交互:defense_up.magnitude/duration 纳入"禁独立调参"联动网络(m_w↔D_ref↔m_d↔duration_d);核心规则表增依据列(lint R8 引用列迁移) | +| v1.1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;散文/枚举/边界/AC 转表格;流程转字符画;公式本体、Variables 表与验算数据全保留;语义与 registry 现行值对齐(weaken −65 = F0 值域 [60,85] · 四行 duration 1/1/1/1 = 当次结算 · enemy 侧 side-wide 多敌读法),零规则变更 | +| 两级奖励批 | 2026-09-11 | attack_up/defense_up duration 2→1(当次结算语义:授予结算兑付后即衰减);attack_up 复活(HeroTable.atk 域 [1,999]、0 加载拒绝——基础攻击恒存在,消费面 = 主角战斗段先手攻击);条款 2 锚参照对象回迁 attack_up 当量;正文原 D=2 行注记与 Knob 表 2/2/1/1 旧行按本批现行值改写 | +| F0 裁决回执批 | 2026-09-10 | weaken −25 → 值域 [60, 85]/默认 −65(B1 GDD F0:归一口径 = 每结算瞬时当量,总暴露分支被 percent schema 域禁止排除——条款 2 锚红警关闭,锚两腿 7.8 > 6.0 / 7.8 ≥ E_dig 7.2 通过);B1 义务①–⑧履行回执落位;B3 正文回执以 B1 GDD F0 为准(registry status_effect.anchor_warning) | +| 单敌假设回验批 | 2026-09-09 | B2 定多敌模型(1–3 个同敌种敌单位)——Core Rule 1 单敌假设回验不成立,效果 side-wide 语义改"全体存活敌单位"(层数 = side 共享池,B3 现接口零变更兼容);target_ref 单体寻址 = 扩展位义务(首个单体效果上线时 B3 补列,随批 B1 归集语义/D1 呈现裁决)——registry single_enemy_assumption | +| Rev 1 | 2026-09-09 | design-review 7 项 blocking 同日核销:enemy 侧单敌假设入文(后经上批回验改写)· B1 义务 5→8 · D1/B5/D5 呈现义务登记 systems-index · AC-15 机制更正 + reloaded 根全清单规格 · 致命夹具补根 dup_key · AC-18 随 registry 逐字段键重构 · weaken 红警补修复空间不对称 + OQ1 归一口径前置 · stack-不重置 R 登记 OQ7 playtest 证伪项——明细见 reviews/b3-status-effect-review-log.md | +| v1 | 2026-09-08 | 一次会话定稿:三结构决策(单一计数/按行叠层/四行定稿权收编)用户拍板;lean 门禁 systems-designer 起草 Formulas · qa-lead 起草/验证 AC;CD-GDD-ALIGN 按 lean 跳过 | diff --git a/design/gdd/b4-reward-item-economy.md b/design/gdd/b4-reward-item-economy.md new file mode 100644 index 00000000..9573005e --- /dev/null +++ b/design/gdd/b4-reward-item-economy.md @@ -0,0 +1,549 @@ +# B4 奖励与道具经济(Reward & Item Economy) + +> **状态**:In Review → Revised(14 条 blocking 已核销,待 delta 复核放行) · **版本**:v2 · 2026-09-14 +> **一句话**:定义扫雷决策的兑现物——奖励是什么、值多少、怎么随机、怎么入库。 +> **推演件**:`design/gdd/analysis/b4-reward-item-economy.md`(本文件只收结论,理由在那里) +> **承载支柱**:P2 主(奖励是"扫雷的因"的兑现物)· P3(安全格价值侧)· P1(奖励身份可预读 = 规划素材) + +## 设计目的 + +B4 是战斗域奖励层——"奖励是什么、值多少、怎么 roll、怎么入库"的唯一 owner 与 D2 RewardTable 回填权持有方。缺位后果:P2"规划的延迟兑现"无兑现物;当前内容态敌人不可被击杀,胜利路径判定素材悬空;A4 三条定价硬约束(雾内档位下界 / 清图锚上浮 / 净当量带上界)无承载对象,安全格价值体系无从校准。 + +1. 每个安全格必有货,开格瞬间"这格有货"——我在地牢里淘东西,不是在领系统发放的补偿。 +2. 危险的地方货更好——雾区/高危区档位下界更高。 +3. 这格是增伤——看得见才留得住:已开格与库存的奖励身份恒定可读、不变卦。 +4. 先是一份捡到的战利品,其次才是一行结算数据——拾取入库,轮次结束在结算明细逐行兑付。 +5. 囤刀待机、看准重击拍再用——道具是整个关卡的资产,库存规划跨战斗延续。 + +| 反向护栏 | 处置 | +|---|---| +| 奖励的随机只发生在桌下(roll 决定这格是什么),身份永远在桌上(开格后恒定可读) | 任何"开格后仍不可读""结算时才揭示身份"的呈现设计默认砍掉 | +| 拾取的糖不得改成即时生效 | 绕过 B1 结算统一兑付即违反 P2 归因 | + +## 系统范围 + +负责: + +- 奖励类型封闭枚举(五值)与两级交付轴(即时类当场兑付 / 可选类入库存走窗口) +- RewardTable / RewardConfigTable 的 schema 与内容回填(D2 建表,本系统 owner) +- 供给控制器 RewardSupplyController——install 期战斗首件保证 + 指数递减注入 +- install 期奖励身份预分配(reward_roll 封闭流为唯一随机入口)与板级映射容器 +- 分流 / 入库 / 库存查询接口与关卡域库存容器 +- F1–F6 数值供给与失衡预警①–⑧ 预注册 + +不负责: + +- 何时兑付、结算顺序、明细行协议(B1) +- 棋盘资格事实(A2——零直接接口,经 B1 转化) +- buff 时限 / 叠层 / 衰减(B3) +- 棋盘侧功能道具(A5,垂直切片) +- 关卡大奖励 / 代币 / 天赋(C2 / C4) +- 伤害与治疗施加、守卫判定、敌存活集与血量读取(B1 编排) +- 数值调参所有权(C3——本系统只定结构、域与 provisional 默认) +- "关卡结束"事件定义(C1 / C2) +- 一切呈现(D1 / B5 / D4 / D5) + +## 核心循环 + +``` +board install(B1 编排) + │ ① roll_board(资格格清单 = S ∖ R₀) [B4] 恰一次;消费 reward_roll 封闭流恰一 scope 批 + │ ├ 行主序逐格取号 → 格奖励身份(雾格过 F2 档位过滤) + │ ├ 批尾一号 → 清图身份(F3) + │ └ F5 保底校验不过 → 草稿整批弃用、同 scope 续流重 roll(≤ N_draft 次,超出致命) + │ ② 板级双映射(格→reward_id;清图→reward_id)→ Ready(只读) + ▼ +挖掘期(身份恒定可读;A2 资格事实经 B1 转化——B4 零直接接口) + ▼ +轮次结束 → 结算编排(B1 段序骨架) + │ ③ 授予段:逐格 grant(cell) → 预分配身份快照,按 category 分流 + │ ├ buff 类 → B1 调 B3 apply_effect(满层拒绝 → "奖励未生效"行,零折算) + │ ├ instant_aoe → 当场兑付:全体存活敌各 power 点(不进库存不出窗口) + │ ├ instant_heal → 当场兑付:主角 +power(clamp;满血 → "奖励未生效"行零兑付) + │ └ damage_item / heal_pack → stock() 入关卡域库存 + │ ④ cleared 轮:grant_clear() → 预分配清图身份兑付(分流同上;恒非空) + ▼ +⑤ 道具使用窗口(授予段后、增益/减益段前——玩家至多选用一件或跳过) + ▼ +⑥ 道具段 → ⑦ 战斗段 → ⑧ 衰减段 → ⑨ 判定段 + ▼ +battle_over:retire_mapping(板级映射消亡,不跨图;库存不清——跨战斗延续) +``` + +| 时点 | 触发 | B4 接口 | 约束 | +|---|---|---|---| +| board install 后、挖掘前 | B1 编排恰一次 | `roll_board(资格格清单)` | 消费 reward_roll 恰一 scope 批(格批 + 清图批尾);Ready 后再调 → false + 诊断;空清单 → debug 断言中止 | +| 结算授予段 | 每格受理事实 | `grant(cell)` | 返回预分配快照;同格重复受理幂等(同快照 + 诊断);清单外 → false + 诊断 | +| cleared 轮授予段 | cleared 结算 | `grant_clear()` | 恒非空兑付;同批重复调用幂等(同身份 + 诊断) | +| 授予段(道具类) | grant 返回道具类后 | `stock(item)` | 仅受理 damage_item/heal_pack;来源真值归 B1、B4 不校验 | +| 道具使用窗口 | 玩家选用 | `use(handle)` | InStock→Used 不可逆;零游戏态前置守卫;Used 句柄 → false + 诊断 | +| battle_over | 战斗结束 | `retire_mapping()` | 板级映射消亡(下一场战斗新板新批);库存不清 | +| 关卡结束 | 上层显式编排 | `clear_inventory()` | 恰一次;重复 → false + 诊断(幂等守卫) | + +## 奖励类型与数据表 + +### 奖励类型(封闭枚举五值)与两级交付轴 + +规则: + +1. (规则 1)奖励系统发出的一切只从五种类型里出,数据表里出现别的类型直接拒绝加载;三种即时类在发奖那一刻当场兑现——不进背包、不用玩家选;两种可选类先放进关卡背包,由玩家在结算窗口里自己挑时机用,数量少但更强。 +2. 预留值(resource / aux_item 等)校验拒绝(fail-fast);启用走本 GDD 修订。想加第六种类型 = 修订本 GDD,不是调参数。 +3. (规则 7)治疗分两档:instant_heal 小额当场回(满血零兑付记"奖励未生效"行);heal_pack 稀缺强治疗进背包挑时机,满血使用被拒且不消耗——守卫由 B1 读主角血量判定,B4 零跨系统读;usable_now 可用性预判由 B1 组装进窗口数据供界面禁选/提示。 +4. buff 类 effect_id 为 B3 StatusEffectTable 精确引用;首个引用行回填激活 B3 悬空校验(此前不校验),悬空即加载期致命(D2 通道)。 + +| category | 交付类 | 玩家拿到什么 | 兑付方式 | 字段消费 | 拒绝语义 | +|---|---|---|---|---|---| +| buff | 即时 | 一层临时增益(增伤/减伤) | 授予段当场:B1 调 B3 apply_effect 入状态容器,时长 = 当次结算(attack_up/defense_up D=1) | effect_id 必填;power 不消费——非校验项 | 满层 → "奖励未生效"行(可聚合计数),无折算 | +| instant_aoe | 即时 | 群体攻击 | 授予段当场:全体存活敌各 power 点(逐个 apply_damage、受击即死;恒定表值) | power ≥ 1 | 零存活敌 → "奖励未生效"行零兑付 | +| instant_heal | 即时 | 小额即时回血 | 授予段当场:主角 +power(clamp 不超上限) | power ≥ 1 | 满血 → "奖励未生效"行零兑付(不进库存无消耗语义) | +| damage_item | 可选 | 伤害道具(刀) | stock 入关卡域库存 → 窗口选用、道具段固定顺位打敌 | power ≥ 1 | — | +| heal_pack | 可选 | 强效治疗包 | stock 入关卡域库存 → 窗口选用回血 | power ≥ 1 | 满 HP → B1 守卫拒绝(不烧包;usable_now 禁选/提示) | + +设计意图:可选类数量少但更强,治疗整体供给变少、决策价值变高——"治疗是稀缺奖励"。 + +### RewardTable schema(D2 承载,B4 回填 owner) + +规则: + +1. (规则 9)每条奖励是什么、值多少、多强全部来自 RewardTable——改奖励就是改表行,不用动代码;表身份 = 定义表(非进度成长表),主键 reward_id(Rule 4 豁免,B2/B3 同款声明纪律)。 +2. RewardConfigTable = 单行全局参数表(HeroTable 单行全局先例);两表由 D2 建表、本 GDD 回填,回填走 D2 接口漂移规则回写。 + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| reward_id | String(表内主键) | 实体键精确匹配(D2 Rule 4) | +| category | enum {buff, damage_item, heal_pack, instant_aoe, instant_heal} | MVP 校验域(五值);预留值拒绝(规则 1) | +| effect_id | String(可空) | buff 类必填——B3 StatusEffectTable 精确引用;非 buff 类恒空 | +| display_name | String(非空) | 玩家可读名(D1 义务①/窗口呈现数据源;本地化归 D6——B2/B3 同库惯例) | +| power | int [0, 999] | 伤害道具伤害值 / 治疗包回复值 / 即时类数值(knob);buff 类不消费——非校验项(B2 heavy_multiplier 先例) | +| weight | int [0, 999] | 所在池内 roll 权重(knob);0 = 该池不出此行(合法极端) | +| pool_tag | enum {safe_cell, active_inject, clear_default}(+ clear_ 预留) | roll 池分组——格奖励池 / 供给控制器注入池 / 清图默认槽;雾区档位过滤谓词见 F2 | +| tier | int [1, 8] | 强度档位(A4 雾内档位下界约束的载体;knob) | +| use_timing | enum {pre_settlement} | 使用时机(MVP 唯一值;game-concept 预留字段的兑现——扩枚举走修订) | + +RewardConfigTable(单行全局六配置): + +| 配置 | 域 | 默认 | 说明 | +|---|---|---|---| +| T_fog_min | int [1, 8] | 2(provisional) | F2 雾区档位过滤门限 | +| K_dmg | int [0, 999] | 0(provisional——现行默认 = 保底断言关闭,语义由供给控制器战斗首件保证取代;no_damage_row 加载后闸托底零活刀行世界) | F5 伤害道具保底下界,仅计 damage_item | +| N_draft | int [1, 999] | 32(provisional) | F5 草稿重 roll 上限 | +| P_min | int [1, 999] | 8(= 默认根最低刀 power) | K_dmg ≥ 1 供给闸刀力下界(floor_supply_missing) | +| active_p0 | float [0, 1] | 0.4(provisional) | 供给控制器首件后每张新板注入概率初值;E[战斗获取] ≈ 1 标定目标 | +| active_decay | float (0, 1] | 0.5(provisional) | 注入概率指数递减(0.4→0.2→0.1→…永不绝育);加难杠杆候选 0.6 随 C3 OQ7 扫描 | + +### 默认根内容行(值 provisional 归 C3;溯源 = 原型 v3 锚 HERO_MAX_HP=50 + 概念原型 HERO_BASE_ATK=6) + +| reward_id | category | power | weight | tier | pool_tag | 说明 | +|---|---|---|---|---|---|---| +| defense_up_basic | buff | —(不消费) | 16 | 2 | safe_cell | 当次受伤减免(B3 defense_up D=1)——减伤墙批 24→16(attack_up 升池内最高行) | +| attack_up_basic | buff | —(不消费) | 20 | 2 | safe_cell | 当次主角攻击增伤(B3 attack_up D=1——复活后首批入池) | +| spark_aoe | instant_aoe | 4 | 16 | 1 | safe_cell | 群体攻击:全体存活敌各 4 点 | +| heal_minor | instant_heal | 4 | 15 | 1 | safe_cell | 即时小额回血(满血零兑付行) | +| knife_t1 | damage_item | 8 | 60 | 1 | active_inject | 可囤可择时的基础刀(供给控制器注入池) | +| knife_adv | damage_item | 16 | 30 | 2 | active_inject | 雾档升级刀(注入池) | +| heal_pack | heal_pack | 12 | 10 | 2 | active_inject | 稀缺强治疗(注入池——可选治疗唯一形态) | +| clear_blast | instant_aoe | 40 | 1 | 2 | clear_default | 清障炸药:清图槽当场兑付全体存活敌,不入库不占可选类供给(完美板 ×2 = 80——原 clear_knife 入库刀由本行换位替代) | + +- 归一占比:格子池四行即时类合计 67;可选类三行移出格子权重池 → active_inject 注入池(供给控制器 install 期注入:战斗首件保证 + 指数递减 0.4→0.2→0.1→…,E[战斗获取] ≈ 1 标定目标)——原"格子权重 75/25 两类占比"由注入机制替代承载;治疗 = heal_minor(格子池)+ heal_pack(注入 10%)。 +- tier_min_buff = 2;T_fog_min = 2 → 雾池 = {defense_up_basic, attack_up_basic, knife_adv, heal_pack}(四行,非单一化)。 +- AC-19 对账对 = (knife_adv, attack_up_basic)(雾池最低活档行 ≠ tier_min_buff 行——非平凡判别保持)。 + +## install 期身份预分配(roll、过滤与保底) + +### 预分配规则 + +1. (规则 2)B1 把一张新扫雷图装好后、玩家挖第一下之前调一次"摇号":按行主序给这张图上所有够格出奖励的格子(开局已翻开的安全格除外)逐一摇出各自的奖励,同一次摇出清图奖励,写进这张图的映射表;同图跨轮不变,换图或战斗结束映射随之消亡。 +2. 随机只发生在这一次摇号里;之后发奖、用道具都不再掷任何骰子,道具威力就是表里写死的数值。 +3. (规则 8)已开格与背包里每件道具的身份任何时候可查——不是查询时现算;映射就绪后只读,不存在任何重摇接口,谁加重摇入口谁就是在改接口(走 GDD 修订非调参)。 +4. 没挖开的格子当前版本不显示里面是什么(结构上已定也查得到,留给探测类道具桥接与后续呈现决策)。 +5. (规则 10)B4 不订阅任何事件、不画任何界面、除开局那次摇号不用任何随机数;查询口与操作口摆在那里,什么时候调、按什么顺序调全由 B1 说了算;棋盘那边发生什么 B4 从不直接看——事实都经 B1 转化后递过来。 + +设计意图:身份在桌下摇定、在桌上恒定可读——随机只有一次,之后全是兑付。 + +### F1 身份 roll(weighted_pick,install 期执行) + +**结论**:每资格格恰一身份、清图槽恰一身份——weighted_pick 累积区间选行、0 权行取号前剔除、单行池退化合法;Ready 后映射只读。 + +``` +资格格集 = S ∖ R₀(安全格减初始展开域——initial/mine 通道恒不 eligible,install 期可精确枚举) +行主序逐格取 reward_roll 一号 r ∈ [0, W):身份 = 累积权重区间 [Σw_{0} ∩(雾格 ? {tier ≥ T_fog_min} : 全体);weight=0 行取号前剔除 +批尾取一号 roll 清图身份(F3;批尾 = 同一批取号中最后消费的一个号——registry terminology 已登记) +→ 双映射提交 Ready +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 取号 | r | int | [0, W) | reward_roll 流单号(F1/F3/F5 消费) | +| 权重 | w | int | [0, 999] | RewardTable.weight;0 = 不入池(取号前剔除) | +| 雾档门限 | T_fog_min | int | [1, 8] | F2;RewardConfigTable 配置值 | +| 资格格集 | S ∖ R₀ | 格集合 | 板级 | B1 供清单(义务⑥) | + +| 项 | 内容 | +|---|---| +| Example | 三行 w=2/1/1,W=4,r=3 → 第三行(区间 [3,4)) | +| 池空裁定 | 过滤后零行 = 结构性不可达——D2 加载期校验致命(每 pool_tag × 雾/非雾两态各 ≥1 行 weight>0);单行池退化为恒等选取(合法极端) | +| 边界 | 流 scope = 每次 board install 恰一批;消费序与恰一次归 B1;Ready 后只读(F5 草稿重 roll 发生在提交前,不变式零触碰) | + +### F2 雾区档位过滤(fog_tier_gate) + +**结论**:雾格池 = {tier ≥ T_fog_min}、非雾格池 = 全 tier(谓词恒真、零分支);T_fog_min = RewardConfigTable 表值(默认 2 provisional);T_fog_min=1 → 雾池=全池(过滤退化合法,A4 约束①仅剩锚侧兜底);=8 → 仅最高档(非空由 F1 校验保证)。 + +``` +雾格池 = {tier ≥ T_fog_min};非雾格池 = 全 tier(谓词恒真、零分支) +``` + +| 项 | 内容 | +|---|---| +| Example | T_fog_min=3 时雾格恒不出 tier≤2 行 | +| 边界 | 链开雾格同雾格待遇(A2 EC4);参数归 C3;A4 硬约束① = 验收锚非运行时校验——对照表落定/调参时核对"雾池最低档行当量 ≥ 最低档安全格 buff 当量" | + +### F3 清图奖励 roll(clear_reward_roll) + +**结论**:槽谓词 = clear_(板 biome 匹配 ∧ 池非空)否则 clear_default(MVP:clear_ 行校验拒绝 → 恒退化 clear_default,结构位保留);恒非空的可判定形式 = 恒有正当量行(power=0 道具行被拒——A3 条款④"清图奖励>0"的表侧兑现);空池致命(同 F1 裁定)。 + +``` +槽谓词 = clear_(板 biome 匹配 ∧ 池非空)否则 clear_default +MVP:clear_ 行校验拒绝 → 恒退化 clear_default(结构位保留) +恒非空 = 加载期校验:clear_default 池 ≥1 行 weight>0 ∧(buff 行 effect_id 非空 ∨ 道具行 power ≥ 1) +roll = install 批尾一号,选行算子同 F1 +``` + +| 项 | 内容 | +|---|---| +| Example | 命中 heal_pack(power=3) → cleared 轮 grant_clear 入库存 | +| 边界 | roll 在 install 批、兑付在 cleared 结算编排期(零随机出 roll_board 一线);图类型战斗域恒定、不逐图重掷 | + +### F5 伤害道具保底(damage_item_floor) + +**结论**:每板可选伤害保底 \|{c ∈ 资格格 \| 身份(c).category = damage_item}\| ≥ K_dmg——执行 = roll_board 内"草稿-校验-提交",不过则同 scope 续流重 roll、超 N_draft 致命;K_dmg=0 = 断言关闭(现行默认——刀保底语义由供给控制器战斗首件保证取代,no_damage_row 后闸托底零活刀行世界);基础攻击在场后本保底为内容健康护栏非胜利路径闸。 + +``` +断言 |{c ∈ 资格格 | 身份(c).category = damage_item}| ≥ K_dmg +执行 = roll_board 内"草稿-校验-提交":不过 → 草稿整批弃用、同 scope 续消费流重 roll(全序确定、可回放) +超 N_draft 次(RewardConfigTable,默认 32 provisional)→ 致命(权重配置错误,fail-fast) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 保底下界 | K_dmg | int | [0, 999] | RewardConfigTable 配置;默认 0;仅计 damage_item(保底对象 = 可选类伤害——玩家可控时点的伤害资源;即时类 instant_aoe 自动兑付无规划面,不计) | +| 重 roll 上限 | N_draft | int | [1, 999] | RewardConfigTable 配置;默认 32 | +| 供给闸刀力下界 | P_min | int | [1, 999] | RewardConfigTable 配置;默认 8 = 默认根最低刀 power | + +加载期护栏(两条致命): + +| 护栏 | 断言 | manifest 行(根) | +|---|---|---| +| (i) 供给闸 | K_dmg ≥ 1 ⇒ safe_cell 池存在 power ≥ P_min 的活 damage_item 行 | `floor_supply_missing`(根 `b4_floor_supply`) | +| (ii) 零伤害后闸 | K_dmg = 0(断言关闭)⇒ safe_cell ∪ clear_default 仍须含 ≥1 活 damage_item 行(内容健康护栏、非胜利路径闸;与 B2 OQ9 并案登记同一 owner) | `no_damage_row`(根 `b4_nodamage`) | + +| 项 | 内容 | +|---|---| +| Example | 12 格板首稿 0 件刀 → 弃稿重 roll,次稿 1 件 → 提交 Ready | +| Output Range | K_dmg=0 = 断言关闭(现行默认——no_damage_row 后闸托底);\|资格格\| < K_dmg 恒不过 → 重 roll 耗尽致命,暴露配置错误 | +| 边界 | 断言仅计格奖励身份(清图槽不计);归 B4 自身,B1 仅编排恰一次调用;重 roll 全部发生在提交前(Unrolled 态),确定性 = 同 scope 消费轨迹的确定函数 | + +### 供给控制器(RewardSupplyController) + +| 面 | 语义 | +|---|---| +| 机制 | install 期注入:战斗首件保证 + 指数递减——机制本体归本 GDD | +| 注入池 | pool_tag = active_inject:knife_t1 / knife_adv / heal_pack 三行可选类(由格子权重池移入) | +| 递减 | 首件之后每张新板注入概率 active_p0=0.4 起、按 active_decay=0.5 指数递减(0.4→0.2→0.1→…永不绝育) | +| 标定 | E[战斗获取] ≈ 1;加难杠杆候选 decay 0.6 随 C3 OQ7 扫描 | + +设计意图:可选类供给由机制保证而非格子概率——首件必有、后续递减、永不绝育。 + +## 结算分流与道具兑付 + +规则: + +1. (规则 3)轮次结束结算时,B1 对每个捡到奖励的格子来领一次奖,B4 交出早已定好的身份,按类型分路(分流表见「奖励类型」节)。 +2. 增益挂满层挂不进去时,这份奖励不予兑付、不给任何折算补偿,只在结算明细里明写一行"奖励未生效"——不静默蒸发。 +3. (规则 6)玩家把一张图的雷全部清完时,结算给一份清图奖励:身份早在图装上桌时与格子奖励同批摇好,按同样三条路兑付;它永远不会落空——数据加载时强制保证清图奖池里至少一行有实际价值的内容,"清图奖励 > 0"是定价约束钉死的。 +4. 按地形/图类型换不同奖励池是预留插槽,MVP 只有一个默认槽(clear_default);同一张图类型不变,清图奖励不重摇。 +5. 清图槽身份为 buff 行且授予时已满层 → 零兑付 + "奖励未生效"明细行(合法例外;清图槽允许 buff 行入 clear_default 池;监控 = 预警④清图槽 buff 零兑付单独计数)。 + +### F4 道具段与即时类效果式(item_and_instant_effects) + +**结论**:道具伤害恒定表值不吃任何 percent 修正;治疗 clamp hp' = min(hp + power, max_hp);满血拒绝/零兑付拦在消耗前;attack_up 唯一消费面 = 主角战斗段先手攻击(只乘基础攻击、不乘刀)。 + +``` +伤害道具(可选类,道具段):D = power(恒定表值——不吃任何 percent 修正) + 目标 = 固定顺位最前存活敌(B2 Rule 7),一次性消耗 +治疗包(可选类,道具段):可用 ⟺ hp < max_hp(满 → 拒绝、不消耗);hp' = min(hp + power, max_hp) +即时群伤 instant_aoe(即时类,授予段当场):对全体存活敌各 D = power(逐个 apply_damage、受击即死); + 零存活敌 → 零兑付"奖励未生效"行(结构性难达——授予段先于道具/战斗段,敌集空仅剩防御分支) +即时回血 instant_heal(即时类,授予段当场):可用 ⟺ hp < max_hp(满 → 零兑付"奖励未生效"行,不进库存无消耗语义); + hp' = min(hp + power, max_hp) +buff 类(即时类,授予段当场):B3 apply_effect;时长 = 当次结算(attack_up/defense_up D=1—— + buff 兑付窗口 = 授予段至本结算衰减段) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 道具威力 | power | int | [1, 999] | RewardTable.power(category ∈ {damage_item, heal_pack} ⇒ power ≥ 1 加载校验);恒定表值非 roll 值 | +| 主角血量 | hp / max_hp | int | [0, 999] | B2 查询(B1 编排读值做守卫——B4 零跨系统读) | + +| 项 | 内容 | +|---|---| +| attack_up 口径 | HeroTable.atk 基础攻击恒存在(域 [1,999]、0 加载拒绝——B2 Rule 9)→ attack_up(+50%)复活:唯一消费面 = 主角战斗段先手攻击(伤害算式归 B1 战斗段);"道具不吃修正"拍板不变——attack_up 只乘基础攻击、不乘刀;是否入默认根池随奖励改造批裁决(OQ1);A3 条款 2 锚参照对象 = attack_up 当量(换算归 B1 条款①对照表——B4 只供行值清单;B3 锚预警裁决前置不变) | +| 弱化面排除 | weaken/defense_up 不入本式(敌输出侧/主角承伤侧,B2/B1 域);读值时点 = 道具段、施加归 B1(B4 零跨系统读) | +| Output Range | D = power(恒定);hp' ∈ [hp, max_hp](clamp 式上界——满血分支被拒绝守卫拦在消耗前) | +| Example | power=6 伤害道具 → 顺位最前敌 −6;重击拍前用刀击杀 → 该敌重击永不兑现(B2 死亡即不动手的道具形态);满血(hp=max_hp)用治疗包 → 拒绝、库存不减 | +| 边界 | 取整无涉(全整数);使用守卫(满血)归 B1 编排(义务④);hp=0 在窗口时点结构不可达——不定义复活语义 | + +## 库存与道具使用窗口 + +规则: + +1. (规则 4)道具背包属于整个关卡,不属于某一场战斗:换了地图、打完一场战斗,包里的东西都还在,直到整个关卡打完(无论输赢)才由 B1 一次性清空;每一件都记着从哪个格子的哪一轮捡到,结算能追溯来源;数量没有上限——稀缺感靠掉落权重控制,不靠背包容量。 +2. (规则 5)每轮挖完,结算按固定顺序走:先发放这轮捡到的全部奖励,然后弹出道具使用窗口——玩家至多选一件背包道具用掉,或者跳过;之后才轮到增益减益、道具生效、战斗、衰减、判胜负。窗口一次只能用一件(想一次用多件 = 改规则,不是调参数)。 +3. 道具段排在战斗段之前——这轮刚捡的刀可以先削血、把敌人打进"死亡即不动手","重击拍前用刀"由此成立;伤害道具固定顺位 = B2 Rule 7 默认目标。 + +### 道具使用窗口 + +| 面 | 语义 | +|---|---| +| 时机 | 授予段之后、增益/减益段之前——当轮拾取的道具当轮即可用(末图清图奖励同结算窗口可选用) | +| 载荷 | B1 编排组装:`query_inventory` 逐件实例(item_id + 来源 {cell, round} + 行值快照投影 + 状态;枚举序 = stock 受理序)+ B1 侧守卫预判 usable_now(满血治疗包禁选/提示——B4 零跨系统读不变) | +| 选择 | 至多选用一件或跳过;选择序列 = 结算回放输入事件;窗口期选择收集与棋盘输入不竞争(A2 单意图帧先例) | +| 消耗 | 一次性 Used 不可逆;零效果判定(施加时无存活敌)归 B1 读 B2 存活集 → "已消耗未生效"行;B4 `use` 零游戏态前置守卫(存活集/血量对 B4 不可见) | +| 空库存 | 零使用恒合法(跳过恒合法);呈现归 D1(可整体不弹窗;窗口触发政策:MVP 有库存恒弹,候选"仅可用品触发/常驻库存条非模态"归垂直切片) | + +设计意图:可选类道具是"关内资产"——囤着一把刀等整个关卡里最好的时机,而不是"这场不花就没了"。 + +## 状态与边界情况 + +| 容器/条目 | 状态 | 进入 | 出口 | +|---|---|---|---| +| 板级奖励映射 | Unrolled → Ready → Retired | install 后 roll_board 恰一次 → Ready;Ready 期间只读 | retire_board/换图 → Retired(消亡,不跨图) | +| 库存条目 | InStock → Used → Cleared | stock 授予 → InStock;窗口选用消耗 → Used(不可逆) | 关卡结束 → Cleared(clear_inventory——库存跨战斗延续后于关卡末清空;battle_over 只退役映射不清库存) | + +不变式:Ready 后映射零写入;Used 不可逆;库存条目仅存在于关卡域(跨战斗延续)。违规 = debug 断言中止(进程致命保留给 D2 数据通道)。 + +| 编号 | 条件 | 行为 | +|---|---|---| +| E1 | 资格格集为空(S ∖ R₀ = ∅) | 三层防线——① A1 生产面 R₀ ≠ S 合法性闸(修订义务已登记、未落地);② A2 install 不变式④现行已生效(R₀=S 板装板即拒,B4 在其后序恒见非空集);③ B4 roll_board 致命(fail-fast 兜底,对偶于 F1 池空裁定)。B4 零静默回退 | +| E2 | 战斗结束时仍有未开资格格 | 其预分配身份随映射 Retired 消亡——零授予、零明细行(未拾取 ≠ 溢出:从未发生的事不进结算,非静默蒸发) | +| E3 | 同一结算多格授予同类 buff 超过 max_stack(连锁批量 13 格链 × max_stack=2) | 按规范序逐格受理,满层起 apply_effect 返回 false → 各产一行"奖励未生效"明细(零兑付、无折算——显式接受;A2"连锁批量授予溢出吸收"义务的 B4 侧落点);道具类无满层概念(库存无上限);玩家侧缓解 = 叠层感知挖掘(HUD 满层后少开安全格/优先挖刀——弱自救);结构位 = 未开格身份呈现深度(OQ2/A5,垂直切片);未生效类行携带可聚合计数/批次字段(合并呈现归 B1/B5 裁决,数据必须可聚合——防事后补字段 = 接口漂移);呈现归 D1 义务③,行协议 owner = B3 义务⑧ | +| E4 | roll_board 后映射 Ready、B1 未调而挖掘已开始 | grant 在 Unrolled 前/Retired 后调用 → false + 诊断(受理/拒绝口径,非进程致命——编排违约显式暴露) | +| E5 | roll_board 被二次调用 | Ready 相位再调 → false + 诊断(相位守卫) | +| E6 | 换图(retire + install)发生在战斗中 | 旧映射 Retired(未拾取身份消亡)、新图 install 后 roll 新批(reward_roll 流新 scope 批,与 board_gen"每次生成新图"同构);库存不受影响(关卡域跨图延续) | +| E7 | 关卡结束(clear_inventory)后仍调 grant/stock/use | Cleared 相位 → false + 诊断;clear_inventory 由 B1 编排于关卡末恰一次(重复调用 false + 诊断,幂等守卫) | +| E8 | 道具使用窗口打开时库存为空 | 零使用恒合法(跳过恒合法);呈现归 D1(可整体不弹窗;窗口触发政策:MVP 有库存恒弹,候选归垂直切片) | +| E9 | 同一件道具被选用两次 | `use` 对已 Used 实例 → false + 诊断(Used 不可逆);单帧选择收集口径归 D1 义务② | +| E10 | 道具段中伤害道具施加时已无存活敌(前序刀已全灭) | 照常消耗——标记 Used、零效果,逐件进结算明细"已消耗未生效"行;段序不截断(选定件全序执行 → 战斗段照常推进〔死敌"死亡即不动手"空转〕→ 衰减段 → 判定段判胜 → 关卡末 clear_inventory 全清含未选件);"零效果"事实判定归 B1 编排读 B2 存活集;监控锚 = 预警①(浪费半)。选定理由见 analysis | +| E11 | 清图槽身份为 buff 行且授予时已满层 | 零兑付 + "奖励未生效"明细行;A3 条款④"清图奖励>0"的兑现口径 = 行级恒有正当量(F3 加载期校验:power≥1 ∨ effect_id 非空),运行时满层拒绝为合法例外;监控锚 = 预警④(清图槽 buff 零兑付单独计数) | +| E12 | 治疗包 power 超过当前缺血 | hp' = min(hp + power, max_hp)——部分回复合法(clamp 归 B1),不拒绝(与满血拒绝相区分:满血 = 零收益守卫、超量 = 部分收益) | +| E13 | RewardTable buff 行的 effect_id 悬空(B3 无此行) | 首个 effect_id 引用行回填后,B3 悬空校验激活 → 加载期致命(D2 通道);此前不校验(dangling_ref_policy 既有口径) | +| E14 | 战斗中热重载 RewardTable | D2 战斗中 reload 拒绝门(D2 EC8,B1 经 set_reload_gate 置位);已授予实例携带行值快照(power/effect_id 授予时冻结——B2 行值快照同款),映射中未授予身份仅是 reward_id 引用、grant 时快照 | +| E15 | cleared 与 battle_over 同结算(末图清图即胜利) | 授予段先兑付清图奖励 → 同结算道具使用窗口即可选用(含当轮清图奖励件)→ 未选用件随库存关卡域延续(末图 = 关卡末图时未选用件至关卡末清空 + 反馈行归 D1 义务⑤);折算/继承结构位归 OQ4(Alpha,C2/D5——折算点 = 关卡末) | +| E16 | 池含 attack_up 行 | 授予走 B3 apply_effect 正常受理、消费于主角战斗段先手攻击;机制层不做校验(内容决策归 C3) | +| E17 | 玩家囤刀跨战斗/跨图累积(数量无上限) | 合法策略且为设计意图(可选类道具是关内资产——"囤刀待机"的规划视野 = 整个关卡);无运行时上限(伤害恒定表值 ⇒ 无乘法爆炸);失衡监控 = 预警①(浪费半)与⑥(速杀退化),修正入口 = C3 权重调参(items_per_settlement_cap 结构位默认关) | +| E18 | 同格重复 grant / 同一 cleared 结算重复 grant_clear(Ready 相位) | grant 同格重受理 → 返回同一预分配快照 + debug 诊断;grant_clear 重调 → 返回同一预分配身份 + debug 诊断(幂等受理族;受理/兑付恰一次归 B1×②③;对比:roll_board 二调 = 拒绝〔结构性相位违规,E5〕,重受理 = 编排瑕疵〔幂等兜底〕) | +| E19 | grant/query_identity 传入清单外坐标(R₀ 格/雷格/板外) | query_identity → null + 诊断、grant → false + 诊断(未分配非错误态;清单内未开格恒可查〔AC-2 分支 3〕、清单外 = 未分配) | +| E20 | stock() 入参越域 | 未知 item_id / buff 类条目 → false + 诊断(仅受理 damage_item/heal_pack);来源 {cell, round} 由调用方供给、B4 不校验真值(真值归 B1×②) | +| E21 | query_inventory 的枚举序 | = stock 受理序(插入序,B3 Rule 6 先例)——D1 窗口呈现序契约 | + +## 数值供给与失衡预警 + +### F6 当量换算供给(equivalence_supply,A3 条款①② 的 B4 侧输入) + +**结论**:供给 = RewardTable 全活行投影 + tier_min_buff(行集空 = F1 校验对偶致命);量纲建议 = 主角 HP 点;伤害道具当量 = power(恒定表值直接可算);单一线性换算与条款 1/2/3/5 的闭合归 B1 单一 owner,本式只供原材料与量纲建议。 + +``` +供给 = RewardTable 全活行投影 + (safe_cell ∪ clear_default 逐行 category/display_name/power/tier/effect_id/weight) +tier_min_buff = min{tier | safe_cell ∧ category=buff ∧ weight>0}(行集空 = F1 校验对偶致命) +量纲建议 = 主角 HP 点(与 m_amb 同量纲);伤害道具当量 = power(恒定表值直接可算) +``` + +| 项 | 内容 | +|---|---| +| 供给面口径 | 全活行投影(非仅活 buff 行清单)——承载 A4 约束②③与 A3 条款⑤对账,道具/清图行值在内 | +| 默认根 buff 活行 | defense_up_basic / attack_up_basic(attack_up 复活后首批入池;是否维持入池随奖励改造批,OQ1) | +| Output Range | tier_min_buff ∈ [1, 8];量纲仅为建议(B1 可另选) | +| 边界 | 行值 provisional 归 C3;锚验收权 B4/B1(B3 Rule 10 同款) | + +### 失衡预警清单(playtest 预注册) + +| 编号 | 指标 | 阈值/读法 | +|---|---|---| +| ① | 刀供给短缺/浪费双指标 | 短缺 = "获得刀伤供给 ÷ 击杀需求" <1.0 即警(供给面信号,按 unit_count {1, 2–3} 两档分设阈值);浪费 = "未用刀伤 ÷ 获得刀伤供给" >0.5 即警(效率面,与短缺分开读——供需贴近下界时高效用刀本就趋零) | +| ② | 治疗双尾 | 溢出 = HP 满拒绝率 >40%(instant_heal 满血零兑付并入本仪表——小额浪费可容忍、阈值放宽;heal_pack 满血拒绝维持 >40%);短缺 = "败北且库存有未用治疗包"占比 >20%(可选治疗稀缺化至 6% 后本仪表为主仪表——强治疗名存实亡的探针) | +| ③ | 雾区档位空窗 + 单一化 | 雾区拾取平均 tier ≤ 平原即警;增雾池单一化监测——雾区拾取 distinct reward_id 数 = 1 持续即警(雾池收窄 → "越险货越同",T_fog_min × tier 分布联动调参) | +| ④ | buff 零兑付双表 | 稳态(单格授予批)零兑付率 >25% 即警;连锁批量授予零兑付率单列(结构性偏高——推导见 analysis,阈值 >50% 即警);结构性缓解:buff 时长当次结算(D=1)使浪费不跨结算累积,同结算内批量授予的零兑付仍存在,仪表保留;清图槽 buff 零兑付单独计数维持 | +| ⑤ | 清图支配反转 | 弃图率 >30%(A3 OQ3 联动)——雾板/无雾板分层观测(聚合指标会被雾板污染或反向掩盖) | +| ⑥ | 速杀退化 | 战斗结束时未开资格格占比 >70% 即警(供给过剩 → 战斗提早结束 → 未挖身份零发放,奖励系统饿死自己的高光与雾内容) | +| ⑦ | 窗口摩擦 | 窗口跳过率、单结算平均明细行数(playtest 预注册,阈值归 C3);结构性缓解:库存只收可选类 + 窗口单件制——窗口频率与每次决策量双降,本预警预期大幅缓解、保留观测 | +| ⑧ | H2 曝光率 | 含清图奖励兑付的结算数 ÷ 战斗数(OQ4 折算裁决的数据输入) | + +## 依赖与输出 + +| 方向 | 系统 | 性质 | 接口要点 | +|---|---|---|---| +| 上游 | D2 | hard | RewardTable schema + 内容行回填(本 GDD owner,接口漂移回写 D2 表清单 + registry)+ RewardConfigTable 单行全局(T_fog_min/K_dmg/N_draft/P_min + active_p0/active_decay)+ pool_tag 枚举含 active_inject;读取经 ConfigDB 单点 | +| 上游 | A2 | 间接(零直接接口) | 资格事实(reward_eligible/channel)经 B1 转化;A2 只报资格、内容与折算不经 A2 | +| 上游 | A3/A4 | 对账/验收锚(非依赖边) | A3 条款④⑤与 A4 三条定价硬约束 = 对账/验收锚关系;锚验收权归 B4/B1(B3"相邻"口径同款) | +| 下游 | B1 | hard(provisional——B1 未设计) | 结算编排期调用:roll_board(install 后恰一次)/ grant / grant_clear / stock / use / query_inventory / clear_inventory;窗口锚点 = 授予段后、窗口选择序 = 回放输入、每格受理恰一次;义务清单 6 项缺项即 blocking | +| 下游 | B3 | 相邻(零运行时依赖——调用经 B1) | buff 类经 B1 调 apply_effect(source_class=safe_cell/clear_reward 已在 B3 枚举内);锚验收权归 B4/B1(weaken 档位 vs 最低档安全格 buff 当量——A3 条款 2);满层拒绝协议对侧;首个 effect_id 引用回填激活悬空校验 | +| 下游 | B2 | 相邻(零运行时依赖) | 伤害道具默认目标 = 固定顺位(规则 5 消费);HeroTable.atk 域 [1,999](基础攻击恒存在——attack_up 复活的供给前提);道具段先于战斗段 → 抢杀窗口的道具形态 | +| 下游 | C3 | soft(provisional) | 权重/档位/威力/池内容曲线调参所有权(默认根行集已登记);RewardConfigTable 六参数;调参期批量仿真义务(F5 可行率 + 登记常数重制联动);失衡预警①–⑧ playtest 归口;clear_blast 换位 + K_dmg=0 + 注入池结构转正与 attack_up 入池裁决(OQ1) | +| 下游 | C1 | soft(provisional) | biome/tag 图类型供给(clear_ 插槽差异化输入——插槽激活的前置) | +| 下游 | C2 | soft(provisional) | 关卡大奖励归 C2(B4 无关);关卡结构 = 库存关卡域的容器("关卡结束"事件归 C1/C2 定义,落位前 B1 暴露 level_clear 入口由上层显式调用);关卡末剩余道具折算结构位归 C2(OQ4 残余,Alpha) | +| 下游 | D1 | soft | 呈现义务 5 项(缺项即 blocking) | +| 下游 | B5/D5 | soft | 结算明细道具行(数据源 = B1 流水,含击杀结果字段——H3 击杀标记数据源);道具使用窗口教学(拾取→囤刀→窗口选用闭环)+ "关卡末库存清空"教学 | +| 下游 | A5 | 相邻(预留桥接位) | aux_item 预留枚举(校验拒绝——启用走修订);未开格奖励身份探测 = A5 探测道具零成本桥接(OQ2,垂直切片) | + +### B1 侧义务清单(缺项即 blocking) + +| 编号 | 内容 | 状态 | +|---|---|---| +| B1×① | 结算段序骨架成文:授予段(含 grant_clear 兑付与即时类当场兑付)→ 道具使用窗口(授予段后、增益段前)→ 增益/减益段 → 道具段 → 战斗段 → 衰减段 → 判定段;核心序 buff/debuff > 道具 > 战斗不变;窗口选择序列登记为结算回放输入事件 | 已登记,待 B1 落地 | +| B1×② | 奖励受理与分流编排:grant → buff 类 apply_effect / 即时类当场兑付(instant_aoe = 全体存活敌各 power、instant_heal = 主角回复 clamp)/ 道具类 stock;每格受理恰一次、stock 与 grant 返回一一对应;满层拒绝/即时类零兑付 = "奖励未生效"明细行、无折算——未生效类行携带可聚合计数/批次字段(合并呈现归 B1/B5 裁决,数据须可聚合) | 已登记,待 B1 落地 | +| B1×③ | clear_reward 授予路径:清图奖励 roll 身份 install 期已定、cleared 结算编排期兑付——兑付后同结算窗口即可选用;清图槽即时类同分支当场兑付(B3 OQ4 关闭条件) | 已登记,待 B1 落地 | +| B1×④ | 窗口与道具段编排:窗口时机 = 授予段后、增益段前;窗口数据组装 = B4 query_inventory 投影 + B1 侧守卫预判 usable_now(满血治疗包禁选/提示——呈现归 D1;B4 零跨系统读不变);窗口至多选用一件(单件施加、目标/数值/明细行来源标注);零效果判定读 B2 存活集 + "已消耗未生效"明细行(E10 承接);击杀结果进流水(H3 击杀标记数据源);治疗 clamp | 已登记,待 B1 落地 | +| B1×⑤ | 生命周期编排:关卡结束调 clear_inventory 恰一次;清空前后快照计数供"随关卡结束清空"反馈行(D1 义务⑤);battle_over 调映射退役(映射 = 板级,随战斗消亡——下一场战斗新板新批) | 已登记,待 B1 落地 | +| B1×⑥ | roll_board 调用时机:install 后、挖掘前恰一次(B4 侧相位守卫见 E5;B1 编排纪律承载调用时点——B4 不可观测) | 已登记,待 B1 落地 | + +### D1 侧义务清单(缺项即 blocking) + +| 编号 | 内容 | 状态 | +|---|---|---| +| D1×① | 已开格 + 库存奖励身份恒定可读:库存常读条 = 挖掘期规划面而非仅轮末窗口;关内延续后常读条跨战斗可见 = 关卡资产感的数据面 | 已登记,待 D1 落地 | +| D1×② | 道具使用窗口呈现:授予段后弹出;选用/跳过/单件选用、usable_now 可用性投影呈现(满血治疗包禁选或选中提示)、窗口时机指示、空库存可不弹窗(E8)、窗口触发政策注记(MVP 有库存恒弹;候选归垂直切片) | 已登记,待 D1 落地 | +| D1×③ | 结算明细四类行呈现:道具行(含入库/生效语境注记)/ "奖励未生效"行(按聚合计数呈现,数据源含可聚合字段)/ "已消耗未生效"行 / "使用被拒"行——数据源 = B1 流水,P2 归因 | 已登记,待 D1 落地 | +| D1×④ | H2 清图奖励行强调呈现(轮末仪式峰值) | 已登记,待 D1 落地 | +| D1×⑤ | 关卡末库存清空可见反馈:"剩余道具 ×N 随关卡结束清空"汇总;层间过渡"库存 ×N · HP 带入下一层"可见反馈 = 垂直切片候选 | 已登记,待 D1 落地 | + +### A4 定价硬约束的 MVP 承载状态(显式宣告) + +| 约束 | 状态 | 说明 | +|---|---|---| +| ① 雾内档位下界 ≥ 最低档安全格 buff 当量 | 锚侧承载 | AC-19 档位序检查(非运行时校验);真当量换算依赖 B1 条款①对照表(未设计) | +| ② 清图奖励锚随雾成本负担上浮 | MVP 结构性不可闭合 | clear_default MVP 单默认槽 = 板无关常量池,清图奖励当量无法随雾负担变化(非 deferral 而是本期不可闭合);激活载体 = clear_ 插槽(垂直切片,待 C1 biome 词表落定) | +| ③ 雾板净当量优势带上界 | 静默绿灯风险 | ②缺位 + 雾格仅 tier 下界加成的 MVP 结构下近乎必然平凡通过;休眠状态与接受风险(重雾板清图欠偿 → 弃图偏斜)在此显式登记;对冲 = 预警⑤雾板分层观测 + 预警⑥速杀退化监测 | + +### 呈现数据面契约(B4 零自有视觉/音频/UI 资产;呈现实现归 D1/B5/D4/D5) + +| 编号 | 数据面义务 | 归属 | +|---|---|---| +| 1 | H1 开格拾取瞬间:身份在场——"这格有货"是 P4 高频情绪节拍;触发数据 = cell_revealed + 身份查询;query_identity 投影含 tier——拾取瞬间可读"好货"档位 | D1 | +| 2 | H2 清图奖励兑付:轮末仪式峰值——clear_reward 明细行强调呈现(D1 义务④) | D1 | +| 3 | H3 道具抢杀窗口:"重击拍前用刀"的执行侧兑现;感知数据前置:B1 流水含击杀结果字段(B1 义务④)——无它则"敌灭、重击未落地"不可呈现 | B1 供给 / D1 | +| 4 | 创意风险登记:#2"完美排雷爆发结算"机制本体归 B1 强制检查点(B4 交付插槽与明细行数据面);#3 标中旗即时节拍补偿同归 B1(D1 义务④⑤为其 blocking 承载) | B1 / D1 | +| 5 | icon 映射键契约:reward_id(全局恒定、跨 run 不重掷);category 三类第一层区分 + 行级第二层;形状+色相双通道(art-bible 未建前键契约先冻结);名称数据源 = display_name 字段 | D1 / art-bible | +| 6 | 绘制优先级插入点:奖励徽章位于旗子之下、变异纹理之上,不得进入数字区域(数字 > 小人 > 旗 > [徽章] > 纹理 > 底色)——纳入格子样张测试验收项 | art-bible | +| 7 | 结算明细四类行协议(数据源 = B1 流水):道具行 / "奖励未生效"行(携带可聚合计数/批次字段——E3)/ "已消耗未生效"行(E10)/ "使用被拒"行(usable_now 预判外的残余路径);未选用件"随关卡结束清空"语境注记 | B5/D1 | +| 8 | 音频挂钩点(Alpha):开格拾取音(按 category 分色)/ 道具使用音 / 清图奖励音 / 负反馈聚合音("未生效/被拒"类行数 >0 时单次结算至多一次——防逐行惩罚轰炸或静默惩罚);挂点事件归 B1/D1,B4 零音频依赖 | D4 | +| 9 | 道具使用窗口数据契约:载荷 = B1 编排组装——query_inventory 逐件实例(枚举序 = stock 受理序〔E21〕)+ usable_now 预判;呈现归 D1;窗口期选择收集与棋盘输入不竞争;窗口选择序列 = 结算回放的输入事件 | D1 | +| 10 | UX 规格归 D1 GDD 前置 /ux-design——窗口是 D1 域屏,B4 只供数据契约;B4 无 D1 外新增 UI 义务 | D1 | + +## P0 最小闭环 + +1. B1 install 新扫雷图后、挖掘前恰一次调 `roll_board(资格格清单)`——全板格身份与清图身份摇定,映射 Ready 只读。 +2. 玩家挖掘,已开格奖励身份恒定可读("这格有货、是什么")。 +3. 轮次结束,B1 授予段逐格 `grant`:buff 挂状态、即时类当场兑付、道具类 `stock` 入库存。 +4. cleared 轮 `grant_clear` 兑付清图奖励(恒非空)。 +5. 道具使用窗口:玩家至多选用一件背包道具或跳过(当轮拾取当轮可用)。 +6. 道具段选用件生效:刀按固定顺位打敌、治疗包回血 clamp;战斗段"重击拍前用刀"成立。 +7. 衰减、判定走完;battle_over `retire_mapping`——映射消亡,库存跨战斗延续。 +8. 关卡结束 B1 调 `clear_inventory` 恰一次,"随关卡结束清空"反馈行闭环。 + +## P1 / P2 后置 + +| 优先级 | 内容 | 后置原因 | +|---|---|---| +| P1 | clear_ 清图池变体(图类型差异化) | 结构位已留(命名空间预留、校验拒绝);待 C1 biome 词表落定 | +| P1 | 未开格身份呈现深度(类别/档位/身份) | 结构前提已备(身份 install 期已定可查、预读查询面就绪、A5 零成本桥接);待 A5 定稿(OQ2,垂直切片) | +| P1 | items_per_settlement_cap 启用裁决 | 结构位默认关;启用即限制,过低锁死胜局——归 C3 playtest(OQ3) | +| P1 | attack_up 入默认根池裁决 | 复活已闭合,入池随奖励改造批(OQ1,C3——复活批不擅自入池) | +| P2 | 关卡末剩余道具折算/继承(转金币/继承) | 关内延续后体验负债主体已闭合;折算点 = 关卡末,归 C2/D5(OQ4,Alpha) | +| P2 | 窗口触发政策候选(仅可用品触发 / 常驻库存条非模态) | MVP 有库存恒弹;待 P0 窗口摩擦数据(预警⑦) | +| P2 | 音频挂钩(开格拾取音/道具使用音/清图奖励音/负反馈聚合音) | D4 域,Alpha | +| P2 | 层间过渡"库存 ×N · HP 带入下一层"可见反馈 | 垂直切片候选(D1 义务⑤注记) | +| P2 | 内容名录扩展(新奖励行) | 机制与内容分离——MVP 内容行收窄,扩展归垂直切片/Alpha | + +## 调参表 + +全部 knob 由 D2 承载(RewardTable 行值 / RewardConfigTable 配置值),provisional 归 C3。 + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| RewardTable.weight(逐行) | 格子池四行即时类(合计 67)+ 注入池三行可选类(knife 60 / adv 30 / heal_pack 10)+ clear_blast 单行槽 | 即时类过剩 → 结算明细淹没("奖励未生效"行刷屏)、可选类相对贬值 | 可选类过低 → 窗口长时空转(单件制下规划面稀薄)、重 roll 频发乃至致命 | +| RewardTable.tier | 默认根:knife_t1/spark_aoe/heal_minor t1、attack/defense/heal_pack/knife_adv/clear_knife t2 | 雾区档位上移 → 平原奖励贬值(预警③联动) | T_fog_min 过滤失效 → A4 硬约束①锚失守(对账兜底) | +| RewardTable.power | 默认根:刀 8/16、治疗包 12、即时类 4/4(可选类更强、治疗稀缺化配套) | 道具段秒杀 / 治疗溢出(HP 满拒绝率↑——预警②) | 刀供给名义达标实际不足(击杀需求多轮化) | +| T_fog_min(RewardConfigTable) | 2(provisional) | 雾池收窄 → 雾格奖励稀化、单一化(预警③)、池空致命风险↑ | =1 过滤退化——雾区档位下界仅剩锚兜底 | +| K_dmg(保底刀数) | 0(provisional——保底断言关闭,语义由供给控制器首件保证取代) | 重 roll 频发 → 流消费膨胀;> 资格格数恒致命 | =0 = 断言关闭(现行默认——no_damage_row 加载后闸托底零活刀行世界) | +| N_draft(重 roll 上限) | 32(provisional) | 坏权重多次过审(掩盖配置错误) | 合法低供给池被误杀致命(概率性致命带——C3 批量仿真义务) | +| 供给控制器 active_p0 / active_decay | 0.4 / 0.5(provisional) | 速杀+囤积回归(获取过量) | 可选类绝育(注入趋零)——E[战斗获取] ≈ 1 标定目标 | +| P_min(K≥1 供给闸刀力下界) | 8(= 默认根最低刀 power) | 闸收紧 → 低威力刀行被加载拒绝(内容收窄) | =1 → 闸空转(power ≥1 恒真) | +| items_per_settlement_cap(结构位) | 关(无上限) | 启用即限制——过高不构成保护 | 过低锁死胜局(击杀需求 ÷ 轮数——economy-designer 警告);启用裁决归 C3 playtest | + +- 非 knob(结构冻结,修订走本 GDD 流程非调参):奖励类型五值封闭枚举(两类交付轴)· install 期预分配 + Ready 只读(无重掷 API)· 道具伤害恒定表值不吃修正(用户拍板)· 满层零兑付无折算 · 库存关卡域(跨战斗延续、关卡末清空)· 窗口单件制 · 清图恒非空 · 结算段序骨架(buff/debuff > 道具 > 战斗)· 零随机出 roll_board 一线 · 逐件实例归因。 +- Knob 交互:K_dmg × safe_cell 伤害权重 × T_fog_min(雾池收窄挤压刀供给 → 重 roll 频发——三者禁止独立调参);T_fog_min × tier 分布 × 雾池行数(雾池单一化联动——预警③第二仪表,禁独立调参);power × B2 EnemyTable hp 曲线(击杀需求侧联动,C3 所有权);weight × B3 max_stack(buff 零兑付率联动——预警④)。 +- 调参所有权:内容/曲线全部 provisional 归 C3;A3 条款③⑤与 A4 硬约束①②③的对账闭合归 B1(锚验收权 B4/B1)。 + +## 验收标准 + +### 验收执行规格(测试口径 / 夹具 / 致命 manifest) + +收 `analysis/b4-reward-item-economy.md` 附录 E(验收执行规格)——夹具根名、manifest 行名与测试协议全文在彼,AC 表引用不失联。 + +### API 面(provisional,封闭枚举——实现期 ADR 定名) + +| 接口 | 签名 | 语义与守卫 | +|---|---|---| +| roll_board | `roll_board(eligible: Array[{cell, fog}]) -> bool` | install 期恰一次;消费 reward_roll 封闭流恰一 scope 批(格批 + 清图批尾一号 × F5 草稿数——每稿 = 格批 + 清图批尾一号,草稿弃用连清图一号一并弃用);Ready 相位再调 → false + 诊断(E5);空清单 → debug 断言中止(E1 兜底);清单含重复格 → 去重 + debug 诊断 | +| query_identity | `query_identity(cell) -> Dictionary\|null` | Ready 只读预读面——返回 {reward_id, category, tier} 引用级投影,非行值快照(快照时点 = grant,E10);Retired/Cleared → null + 诊断;清单外坐标 → null + 诊断(E19——A5 探测桥接的结构面) | +| grant | `grant(cell) -> Dictionary\|false` | 受理面——返回 {reward_id, category, effect_id?, power?} 行值快照;Unrolled/Retired/Cleared → false + 诊断(E4/E7);清单外 → false + 诊断(E19);同格重复受理 → 同一快照 + 诊断(幂等,E18) | +| grant_clear | `grant_clear() -> Dictionary\|false` | cleared 轮兑付——身份 = install 批尾预分配;同批次重调 → 同一身份 + 诊断(幂等,E18) | +| stock | `stock(item) -> Dictionary\|false` | 道具类入库——仅受理 category ∈ {damage_item, heal_pack},未知 item_id/buff 类 → false + 诊断(E20);逐件实例 {item_id, source{cell, round}〔调用方供给、B4 不校验真值——真值归 B1×②〕, 快照};Cleared → false + 诊断 | +| use | `use(handle) -> Dictionary\|false` | 窗口选用消耗——true ⇔ InStock→Used + 返回消耗回执(授予时快照投影 + 来源归因);Used 句柄 → false + 诊断(E9);零游戏态前置守卫——存活集/血量对 B4 不可见,E10 照常消耗与 F4 满血守卫归 B1 的结构前提 | +| query_inventory | `query_inventory() -> Array` | 逐件实例枚举含状态 InStock/Used,枚举序 = stock 受理序(E21)——Used 保留只读可查("已消耗未生效"行数据面);呈现过滤归 D1 | +| clear_inventory | `clear_inventory() -> bool` | 关卡末全清;重复 → false + 诊断(E7 幂等) | +| retire_mapping | `retire_mapping() -> bool` | board retire——B1 义务⑤ | +| equivalence_supply | `equivalence_supply() -> Dictionary` | F6 供给面 {全活行投影, tier_min_buff}——只读 | +| weighted_pick | `weighted_pick(rows, r) -> String\|null` | F1 选行算子纯函数供给面(B2 ambush_grant_plan 先例) | + +- 全部布尔/字典返回 = 受理/拒绝(附 debug 诊断、非进程致命——进程致命保留给 D2 数据通道与 fail-fast 兜底点)。 +- 封闭枚举即负空间验收形态:无重掷 API(规则 8 Ready 只读)· 无库存上限 API(规则 4/E17)· 无伤害/治疗/buff 施加 API(施加归 B1)· 无自主清空时点(clear_inventory 调用权归 B1)· 无事件订阅 · 无 RNG 直连(唯一入口 = roll_board 内 open_stream("reward_roll"));新增任何入口 = 接口漂移(GDD 修订非调参)。 + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | b4_golden 根加载、全新 B4 容器(Unrolled)、RngService master=42 已播种(scope 计数器 = 0)、清单 = b4_list_A | `roll_board(b4_list_A)`;分支 2:两套全新 RngService+B4(同 master=42、同 scope 起点)分别以规范序/乱序传入同一集合;分支 3:Ready 后再 `roll_board(任意清单)` | true(Unrolled→Ready);query_identity 逐格 = 登记映射 Ψ(0)(含雾格分支——雾格身份 ∈ 雾池行,登记值);reward_roll 流 scope 计数器 = 1(install 恰一 scope——registry 语义的行为面);分支 2:两映射逐格相同(行主序消费序 B4 侧规范化——F1"行主序逐格",排序责任不外推给 B1);分支 3:false + 诊断、映射与流计数器不变(相位守卫——B2 AC-1 同款;double-roll 无守卫的编排半归 B1×⑥) | 规则 2/8 · E5 | +| AC-2 | b4_golden 根、roll_board 完成(Ready) | 腿 1:对每格重复 query_identity ×3;腿 2:grant(c) 后 query_identity(c);腿 3:对未受理格 query_identity;腿 4:query 前后对照;腿 5:retire_mapping() 后 query_identity(c) | 腿 1:逐字相同(结构保证非授予期随机);腿 2:= 授予前值(身份恒定跨授予——同图跨轮恒定的观测半);腿 3:恒返回身份(映射无"已开才可查"谓词——A5 探测道具零成本桥接的结构前提;MVP 不呈现归 D1/OQ2);腿 4:映射/库存投影零变化(纯读);腿 5:null + 诊断(映射消亡的观测形式——E6 旧映射半) | 规则 8 | +| AC-3 | b4_golden 根 | 分支 1:全新容器(Unrolled)上 grant(c)/grant_clear();分支 2:roll_board → retire_mapping 后 grant/grant_clear;分支 3:roll_board → grant → stock → clear_inventory() 后 grant/stock/use、query_inventory、query_identity(c);分支 4:重复 clear_inventory() / 重复 retire_mapping() | 分支 1:false + 诊断(E4 roll 前受理);分支 2:false + 诊断(E4/E2——未拾取身份消亡零兑付);分支 3:均 false + 诊断(E7);query_inventory = 空(InStock 与 Used 均清——关卡域);query_identity(c) → null + 诊断(Cleared 逻辑失效态的观测分支);分支 4:false + 诊断(E7 幂等守卫) | E4/E7 | +| AC-4 | 全新容器、master 已播种(b4_golden;manifest 行 empty_eligible) | `roll_board([])`(S∖R₀=∅ 构造——真实流中 A2 install 不变式④前置,本 AC 直击 B4 兜底半) | debug 断言中止(fail-fast 兜底、零静默回退——F1 池空裁定的运行期对偶);测试通道诊断清单含类别;独立进程非 0 退出 | E1 | +| AC-5 | weighted_pick 纯函数面(行数组 + 取号 r 注入) | 分支 1:三行 w=2/1/1 逐一取 r=0/1/2/3;分支 2:行集 {w=0, w=2} 取 r ∈ {0,1};分支 3:单行 w=5 取 r ∈ [0,5);分支 4:r ≥ W | 分支 1:r=0/1 → 行 1(区间 [0,2))、r=2 → 行 2([2,3))、r=3 → 行 3([3,4)——W=4 边界号);分支 2:W=2、恒唯一非零行(0 权行取号前剔除、W 不含 0 权);分支 3:恒该行(合法极端——F1 裁定);分支 4:debug 断言(纯函数域;游戏通道内 r 恒 < W 由流构造保证) | F1 | +| AC-6 | b4_fog_gate 根(登记:雾池唯一活行 = 高档行 H〔tier ≥ T_fog_min〕、非雾池含低档行 L)+ b4_fog_gate_hot + R3 override | 分支 1:roll_board(清单含雾格);分支 2:对 σ = 0…31 逐一(scope 预推进)roll_board;分支 3:fog 布尔唯一输入构造(链开雾格);分支 4:T_fog_min=1 经 R3 override(运行时态、域内值)/ T_fog_min=8 且表无 tier8 行经烤入根 b4_fog_gate_hot | 分支 1:雾格身份 = H(单行退化判别——r 无关;雾格恒不出 tier < T_fog_min 行);分支 2:① 每稿雾格身份恒 = H(过滤谓词零泄漏——任何泄漏以 ≈w_H/W 概率现形);② 非雾格至少一次命中 tier < T_fog_min 行 L(证明非雾侧零过滤);登记值"非雾格 = L"降为诊断信息不作硬断言(加载规则强制 H 活 ⇒ 非雾池 ⊇ {H, L} 至少两行,单稿"= L"是概率断言,忠实实现也会假失败);分支 3:无 chain/dig 通道分支(A2 EC4 同待遇半——构造性);分支 4:T_fog_min=1 → 雾池 = 全池,雾格身份 ∈ 全体活行集(登记 σ 下 = 登记值——过滤退化合法,A4 约束①仅剩 AC-19 锚侧兜底);T_fog_min=8 → 加载致命(雾态池空——AC-17 pool_empty_fog 同类别;override 通道不触发加载校验) | F2 · A2 EC4 | +| AC-7 | b4_reroll 根(登记权重刀 w=1 / 其余 w=3;K=K_dmg、N=N_draft 表读值)+ b4_nofloor 根 + 登记 σ 参考轨迹表 | 分支 1:对 σ = 0…31 逐一(每 σ 全新服务+容器,先 `open_stream("reward_roll")` ×σ 丢弃预推进至 scope=σ——σ 到达机制)roll_board;分支 2:登记 σ*(参考算术判定首稿 0 刀)roll_board;分支 3:登记 σ**(首稿清图身份 = 刀 ∧ 格刀数 = 0)roll_board;分支 4:b4_nofloor 根(无刀行 ∧ K=0)roll_board | 分支 1:每提交板 \|刀身份格\| ≥ K(每板伤害供给下界保证的行为面);分支 2:提交映射 ≠ 首稿参考映射(草稿弃用、同 scope 续流重 roll);scope 计数器每 install 恒 +1(重 roll 不开新 scope——scope 语义 = install 批非草稿批);分支 3:仍重 roll(断言仅计格奖励身份——参考轨迹判别);分支 4:roll_board true、提交板 0 刀(K_dmg=0 = 关闭——现行默认的行为面) | F5 | +| AC-8 | b4_floor_exhaust 根(manifest;safe_cell 池无刀行 ∧ K=1——每稿恒 0 刀) | `roll_board(b4_list_A)`;变体分支:b4_list_single(1 格)∧ K=2(R3 override 域内值) | N 次草稿后致命(权重配置错误 fail-fast);诊断含耗尽类别与草稿计数(N 表读值不写死);独立进程非 0 退出;变体:\|资格格\| < K 恒不过 → 同致命(F5 Output Range) | F5 | +| AC-9 | 两套全新 RngService+B4(同 master=42、同根 b4_reroll) | 同一定长序列驱动两遍(roll_board → query_identity 全格 → grant×2 → stock → use×1 → retire_mapping → roll_board〔新清单,scope+1〕→ grant_clear → clear_inventory → 全 query 面);分支 2:σ 与 σ+1 两板映射对照 | 两遍的映射投影、库存实例投影(含来源 {cell, round})、use 回执、流 scope 计数器轨迹逐项相同——同 σ 同流消费轨迹 ⇒ 同身份映射(含 F5 重 roll 分支)的确定函数性(D2 AC-15 的 B4 层前身);分支 2:σ 与 σ+1 的映射 = 登记相异对(新图新身份——E6 roll 半;库存延续半 = AC-13 分支 2) | 规则 2/10 · E6 | +| AC-10 | b4_golden 根、roll_board 完成 | 腿 1:`grant_clear()`;腿 2:两套同 σ 容器、grant_clear 前置调用序列互异再 grant_clear;腿 3:同批次再 grant_clear() | 腿 1:返回 = 登记清图身份 Φ(σ)(install 批尾一号——登记值含批尾位次假设:实现改批首取号即失配暴露);返回身份满足正当量(道具类 power ≥ 1 表读值 ∨ buff 类 effect_id 非空——条款④"清图奖励>0"的行级兑现面);腿 2:返回恒 Φ(σ)(身份 install 期已定、兑付期零 roll;图类型战斗域恒定不逐图重掷的判别形式——兑付期重掷将随前置序列漂移);腿 3:同一身份 + 诊断(兑付恰一次归 B1×③——E18) | F3 · 规则 6 · A3 条款④ | +| AC-11 | b4_clear_buff 根(clear_default 唯一活行 = defense_up,effect_id 非空——正当量成立)加载通过(加载校验不拒 buff 行) | roll_board → `grant_clear()` | 返回 {category=buff, effect_id=defense_up}(分流数据面);B1 调 B3 apply_effect 与运行时满层拒绝(零兑付 + "奖励未生效"明细行,合法例外口径)归 B1×②/B3,OOS;失衡预警④清图 buff 零兑付单独计数 = playtest 域 OOS | E13 · F3 | +| AC-12 | b4_golden 根、roll_board(b4_list_B〔4 格〕) 完成 | 腿 1:按规范序逐格 grant(连锁批量受理的冒烟构造序——B1 义务①同侧等价);腿 2:grant 返回快照与 grant 时表值对照;腿 3:同格再 grant | 腿 1:逐格返回 = 登记身份序列(受理序 = 规范序);buff 类格返回 {category=buff, effect_id}(B4 只定"给哪行"——层数语义归 B3 合并产生,施加经 B1 调 B3,规则 3);道具类格(damage_item/heal_pack)返回 {category, power}(治疗包 = 道具类路由、非自动兑付——grant 不触发任何使用语义);即时类格(instant_aoe/instant_heal)同样返回 {category, power} 快照——grant 零路由差别,当场兑付的编排归 B1×②(B4 半 = 返回快照非库存语义);腿 2:返回的 power/effect_id = grant 时表读值(E10 快照时点 = grant——差分判别在 AC-15);腿 3:同一快照 + 诊断(受理恰一次归 B1×②——E18);满层起逐格"奖励未生效"行协议 = OOS B1×②(E3 后半) | 规则 3/7 · E3 前半 | +| AC-13 | b4_golden 根、roll_board + grant(道具格) 完成、冒烟(扮演 B1)stock ×N(N = 登记大数 64) | 腿 1:query_inventory;腿 2:retire_mapping() → roll_board(新清单) → query_inventory;battle_over(映射退役)后 query_inventory;腿 3:clear_inventory() → query → stock | 腿 1:恰 N 件、每件 {item_id, source{cell, round}, 快照投影}(来源归因保 P2——D1 窗口数据契约);N 件全部 InStock、零拒绝(数量无上限——规则 4/E17,稀缺性由权重承载;无 cap API 负空间);腿 2:query_inventory 逐字不变(含来源——库存关卡域跨图且跨战斗延续,与映射生命周期正交;跨战斗构造 = 共享容器重 roll,切片连续关卡模式同型);映射退役后逐字不变(库存不再随战斗清空——义务⑤ 改写的行为面);腿 3:true、query 空(不跨关卡);此后 stock → false + 诊断(AC-3 分支 3 已载——注记) | 规则 4/5 · E6/E17 | +| AC-14 | b4_golden 根、库存含刀(damage_item)与治疗包各 ≥2 件 | 腿 1:`use(刀)`;腿 2:同句柄再 use;腿 3:use(刀) 于任意游戏态前置(含"道具段无存活敌"等价构造——B4 无敌态输入面,实现若存在存活/血量守卫则本分支失败);对照腿:use(治疗包) 同前置;腿 4:任意序 use 多件 | 腿 1:true + 回执 {category, power=该实例授予时快照值〔E14〕, source{…}};同 reward_id 多件实例回执 power 逐字相同(恒定表值非 roll 值——F4);实例状态 → Used;腿 2:false + 诊断(Used 不可逆);Used 实例 query_inventory 保留只读可查("已消耗未生效"行的数据面——B2 Dead 句柄同款);腿 3:恒 true + 标记 Used、零效果由 B1 判定(选定即承诺:已选刀照常消耗、逐件"已消耗未生效"明细行归 B1 编排读 B2 存活集〔义务④〕+流水,OOS);相位守卫不在本分支豁免域——Unrolled/Cleared/Retired 相位的 use 照旧 false + 诊断(同输入同答案);use(治疗包) 同样无条件受理(满血守卫归 B1 编排读 B2 hero hp——F4/义务④;"拒绝不烧包"半 = B1 不调 use 的编排纪律 + usable_now 预判呈现,OOS);腿 4:每件独立受理、Used 序 = 调用序(窗口选用序的 B4 半——选择收集归 D1×②、段内细则归 B1×①;同窗顺序多包的逐件动态守卫归 B1×④) | 规则 5 · E9/E10 | +| AC-15 | b4_golden 根 roll_board + grant(道具格) + stock 完成(已授予实例)、另设未授予格(映射中 reward_id 引用) | 经测试通道换根加载 b4_reloaded(`load_tables(root, collect_only=true)` 直调——非 D2 `reload()` 路径,门语义归 D2 AC-16/B1,B2 AC-11 同款);再对未授予格 grant;分支 3:对返回快照投影原地改写;分支 4:换根后 use(已授予实例) | 已授予实例快照投影逐字不变(授予时冻结);未授予格 query_identity 的 reward_id 不变(映射仅持引用——热重载拒绝门 D2 EC8 的 B4 侧纵深);对未授予格 grant → 返回漂移后表值(快照时点 = grant——引用非活值的判别后半);分支 3:容器内实例投影不变(快照拷贝非活引用——B2 AC-11 分支 3 同款);分支 4:回执 = 授予时快照值非漂移值(AC-14 腿 1"快照值"措辞的行为级判别) | 规则 9 · E14 | +| AC-16 | RewardTable schema 类(行级 + 跨字段)class_name 直引实例(不经 load()/preload()) | `validate()` 作用于越域代表值:category ∈ {resource, aux_item}(预留值拒绝——B2 pattern 先例)、pool_tag = "clear_fog"(clear_ 命名空间预留拒绝——规则 6,A1 elite_ 同款)、pool_tag = "nonsense"(未知枚举)、use_timing ∉ {pre_settlement}、weight ∈ {−1, 1000}、tier ∈ {0, 9}、power ∈ {−1, 1000}、跨字段 buff 行 effect_id 空(buff⇒必填)、跨字段非 buff 行 effect_id 非空(非 buff⇒恒空)、跨字段 item 类 power=0(damage_item/heal_pack ⇒ power ≥ 1——F4 加载校验);再作用于域内代表值:weight=0、buff 行 power 任意、tier ∈ {1,8}、item 类 power ∈ {1,999} | 越域各含对应域错误类别(零静默);域内零诊断(防过度约束锁死内容——weight=0 与 buff 行 power 不校验是承重点) | 规则 1/9 | +| AC-17 | 致命根 ×8(独立进程、游戏通道——D2 AC-2b 协议):b4_pool_empty_safe(safe_cell 全 0 权)/ b4_pool_empty_fog(全行 tier < T_fog_min——雾态空)/ b4_clear_empty(clear_default 池空/全 0 权)/ b4_clear_no_value(clear_default 唯一活行 = power 0 道具行——恒非空的可判定形式,A3 条款④表侧兑现)/ b4_no_live_buff_row(safe_cell 有活行但零活 buff 行——tier_min_buff 无定义,F1 对偶致命)/ b4_dangling_effect(buff 行 effect_id = "ghost_buff"——B3 StatusEffectTable 无此行;条件性夹具:默认根已含 effect_id 引用行 ⇒ 悬空校验已激活——dangling_ref_policy 首引兑现点)/ b4_floor_supply(K_dmg ≥ 1 ∧ safe_cell 池无 power ≥ P_min 活刀行——F5 加载护栏 (i))/ b4_nodamage(K_dmg = 0 ∧ 全池零活刀行——F5 加载护栏 (ii) 零伤害世界后闸,B2 OQ9 并案) | 各根经 `--data-root` 注入加载(collect_only=false);对照腿:默认根加载 | 各进程非 0 退出且错误清单含对应类别;默认根干净通过(引用全解析——defense_up 存在) | F1/F3/F5/F6 · E13 | +| AC-18 | ConfigDB 默认根加载;分支 2:b4_attackup_row 根(safe_cell 追加 attack_up 引用行) | 读 RewardTable;分支 2:该根加载 → roll_board → grant(attack_up 格) | category 值域 ⊆ {buff, damage_item, heal_pack}(三值封闭定值);pool_tag 值域 ⊆ {safe_cell, clear_default}(MVP 二值封闭定值);clear_default 池 ≥1 正当量活行(默认根通过自身表级校验的行为断言——AC-17 对偶);权重比例与数值不作定值断言(provisional 归 C3);默认根当前不含 attack_up 行 = 内容态事实、非结构禁止(随奖励改造批重审);分支 2:加载通过、roll_board/grant 正常受理(授予 buff 行 = {category=buff, effect_id=attack_up} 快照——消费于主角战斗段先手攻击,B1 域 OOS;B2 AC-14 的 atk 域断言跨引) | 规则 1 · E16 · F4 | +| AC-19 | 默认根加载(或调参后根——对账随内容态重跑) | `equivalence_supply()`;分支 2:fog 池最低档活行 tier 推导对账 | 返回全活行投影(F6 扩面)与 tier_min_buff = min{活 buff 行 tier}(默认根 = defense_up_basic 行 t=2——登记对账常数;行集空 → 加载致命,AC-17 no_live_buff_row 已载);分支 2(A4① 锚对账——档位序检查):fog 池最低档活行 tier(min{tier : weight>0 ∧ tier ≥ T_fog_min},均表读值推导)≥ tier_min_buff(约束①"雾内奖励档位下界 ≥ 最低档安全格 buff 当量"的 B4 侧可判定形式 = 档位序检查——真当量换算归 B1 条款①对照表〔未设计〕,落定后本分支升级为当量对账;当前内容态不等式成立为登记对账记录,C3 调参后须重跑——登记对账对 (knife_adv, attack_up_basic) 漂移检测;锚参照对象 = attack_up 当量,attack_up 入池时 tier_min_buff 重算、对账对随批重制;两行互异 = 非平凡判别——判据名"档位序检查"已入 registry terminology;tier 与当量零耦合的假阳性边界见 analysis);A4 约束②③ = MVP 结构性不可闭合/静默绿灯风险——依赖节显式宣告 + B1 检查点——OOS | F6 · A4① | +| AC-20 | ConfigDB 默认根加载完成 | 脚本解析 `design/registry/entities.yaml` | ① rng_service.scope_event_binding 的 reward_roll 语义 = "每次 board install(格奖励批 + 清图奖励批)"(规则 2 的回写落点——AC-1 scope 观测面的登记侧);② stream_seed_derivation.golden_vectors 含 reward_roll m=42 scope0 向量(derive_at 测试通道的登记前提);③ variation_table.pricing_constraints 注记存在(A4 三约束为 B4 验收项的登记);④ status_effect.dangling_ref_policy 注记含 "B4 RewardTable 首个 effect_id 引用" 条款(AC-17 条件性夹具激活条件的登记侧)+ status_effect.anchor_warning / clause3_supply.direction_precheck 含 attack_up 参照回迁注记(registry 同批回写);⑤ reward_table 条目 fields 九键(reward_id/category/effect_id/display_name/power/weight/pool_tag/tier/use_timing)与 ConfigDB RewardTable schema 字段名逐一相等、MVP 内容注记与默认根行集一致(与 AC-18 对账);⑥ reward_config 条目存在,fields 四键(t_fog_min/k_dmg/n_draft/p_min)与 ConfigDB RewardConfigTable schema 字段名逐一相等;生效前提 = 回填完成(不写"若存在"软口径);边界:本 AC 断言 registry 登记面——D2 GDD 正文与 registry 的对齐 = delta 复核对账项 | 规则 9 · 接口漂移登记 | +| AC-21 | B4 文件集(映射容器/库存容器/RewardTable schema/roll 与保底逻辑/冒烟脚本/夹具规格)纳入 D2 AC-9 模式项目级扫描域成员清单(owner Technical Setup——B3 先例;本条分支 1 承载登记义务,清单更新 = Technical Setup 门禁项) | 静态扫描 | randomize() / RandomNumberGenerator / 自建 RNG 零命中(零随机出 roll_board 一线——唯一 RNG 入口 = open_stream("reward_roll"));ConfigDB 外 data 表加载零命中(schema 直引实例化豁免——AC-16 前提);extends Control / .tscn preload 零命中(Core 层零 UI 债——headless 实跑 AC-1 合成判定);A2 事件订阅 API 零命中(规则 10 零主动订阅);B2/B3 API 符号零引用(相邻零运行时依赖——含冒烟脚本;StatusEffectTable/HeroTable 经 ConfigDB 表读合法、API 引用非法);重掷类命名扫描(reroll/resign/rebind 等——规则 8 负空间,warning 级 + 人工复核);零随机的行为级硬证据 = AC-9 | 规则 10 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v2 | 2026-09-14 | 两件套迁移:按 v2 骨架重写——design 只收结论;公式推导论证、候选取舍、体验论证、审计登记(接口漂移/缺口预检/覆盖矩阵)与验收执行规格(测试口径/夹具/manifest)迁入 `analysis/b4-reward-item-economy.md`;规则/EC/AC/F/OQ 编号与全部数值不动,零规则变更 | +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-9 回执,registry 同批回写):**clear_blast 清图槽换位**(原 clear_knife 入库刀→instant_aoe p40 当场兑付全体存活敌、不入库不占可选类供给、完美 ×2)· **K_dmg 默认 1→0 + "C3 禁区默认值"条款解除**(刀保底语义由供给控制器战斗首件保证取代,no_damage_row 后闸保留)· **RewardConfigTable +2 参数**(active_p0=0.4 / active_decay=0.5)+ pool_tag 枚举增 active_inject · **供给控制器机制本体承接登记**(RewardSupplyController——install 期注入,E[战斗获取]≈1 标定目标)· 默认根行集重登记(减伤墙批 defense 24→16 + 注入池权重 60/30/10);B1 条款⑤计价基元随换位重推(C = power = 40——clause5_dominance.c3_ruling) | +| v1-lean | 2026-09-13 | lean 格式迁移:legacy 原稿按 B1 lean 样例重排——修订注记出正文收敛至本表;散文/枚举/边界情况/义务清单/失衡预警/覆盖矩阵/缺口预检/AC 全部转表格;流程转字符画;F1–F6 公式、Variables、验算数据与 21 条 AC 全保留;语义与 2026-09-12 对齐状态一致,零规则变更 | +| 摘要同步 | 2026-09-12 | 六处旧口径摘要对齐关卡域权威版(Interactions D1/B5 行、边界情况、依赖、Tuning Knobs、Visual/UI、覆盖声明)——零语义变更 | +| 两级奖励批 | 2026-09-11 | 用户裁决:奖励按交付轴两类(即时 75% / 可选 25%,枚举扩至五值);库存生命周期改关卡域(跨战斗延续、关卡末清空——原战斗域口径退役);治疗两级稀缺化(heal_pack 权重 6%、power 12);道具使用窗口单件制;buff 时长改当次结算(attack_up/defense_up D=1);attack_up 复活(HeroTable.atk 基础攻击恒存在、域 [1,999]、0 加载拒绝)——默认根八行重登记、F5 供给闸 P_min=8、AC-18/AC-19 改写、锚参照回迁 attack_up 当量 | +| full design-review 修订 | 2026-09-09 | 五专员对抗 + creative-director 终审 NEEDS REVISION——14 条 blocking 同日修订核销:道具使用窗口锚点移至授予段后(当轮拾取当轮可用)、B4 配置值落 RewardConfigTable、D1 义务 3→5 扩容、E18–E21 输入域语义补裁、reward_roll scope 语义改写回写(registry/D2 同步)、失衡预警重校准(①拆短缺/浪费双指标、④按吸收上限重推、⑤补雾板分层、新增⑥⑦⑧)、缺口预检 11 项;CD 情绪定位终审与预读/寻路 MVP 呈现面如实降级同批落位;待 delta 复核放行 | +| v1 | 2026-09-09 | lean 门禁一次会话定稿(systems-designer / qa-lead 代理起草):F1–F6 + 21 条 AC + B1/D1 义务清单 + 失衡预警预注册落位;核心拍板——奖励身份 install 期预分配、结算段序(增益/减益 > 道具 > 战斗)、奖励分流与幂等受理、RewardTable/RewardConfigTable schema | diff --git a/design/gdd/c2-adventure-structure.md b/design/gdd/c2-adventure-structure.md new file mode 100644 index 00000000..b7fbd148 --- /dev/null +++ b/design/gdd/c2-adventure-structure.md @@ -0,0 +1,316 @@ +# C2 冒险结构(Adventure Structure) + +> **状态**:Approved · lean 格式 +> **版本**:v1 · 2026-09-14 · 作者:DK + design-system +> **承载支柱**:P4 地牢下探的探险氛围(主——序列推进 = 不断下探更险的层)· P3(恢复点/囤积决策的冒险级容器)· P1("这局我到第几层"的冒险级对照物) +> **一句话**:C2 管一次冒险的骨架——关卡序列怎么排、通关给什么、死了怎么重来;位于 C1 关内行进之上、B1 战斗结算之下。 +> **复核注记**:v1 solo 出稿 → 经 /design-review(--solo 快速审批)APPROVED(0 blocking / 3 recommended——recommended 随下次修订批执行);专员/总监代理未咨询——生产前人工复核保留。评审日志:`design/gdd/reviews/c2-adventure-structure-review-log.md`。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 冒险/序列层编排:冒险生命周期、关卡序列、跨战斗 HP 账本、关卡大奖励、失败重开——C1 关内行进的上层结构,B1 战斗结算的入参供给方 | +| 拥有 | 冒险状态机与结果语义 · 关卡序列定义(LevelTable 行集内容 owner + 推进规则)· HP 账本(跨战斗持久性的 C2 半边兑现)· 关卡大奖励 · 失败重开语义 · master_seed 接线(冒险 = 种子域单位)· 冒险摘要 | +| 不拥有 | 关内节点行进与节点兑付触发(C1)· 战斗结算与关卡清库调用(B1)· 奖励内容与库存语义(B4/C3)· 数值曲线(C3)· 代币持久化与天赋(C4/D3)· 呈现(D1) | +| 上游输入 | C3 期望表与带锚(逐关通关率带 = 序列难度排布)· C3 L2 深度轴行值(序列行集扩展随批填充)· D2 LevelTable schema / ConfigDB / RngService · B1 出口分发语义(level_clear 上层调用面) | +| 下游输出 | 给 C1 的接口契约四件套 · begin_battle 入参供给(关卡序号 / hero_hp / 上限覆盖值)· 冒险摘要(C4 代币入账与 D3 快照的消费位) | +| 硬约束 | 零随机(零流消费、单一种子注入点)· 序列覆盖(冒险关数 ⊆ 表内容覆盖——越界关卡 = 内容生产缺口)· 死亡即冒险终(Roguelike 语义已锁)· 不碰库存域(关卡末清库由 C1 收束点触发 B1 level_clear) | + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P4 下探节奏 | "又清完一层,再往下" | 序列推进 + 关卡大奖励 = 下探途中的喘息台阶;每关一个可命名的新层(display_name),难度台阶可感知(雾解锁 / 数值跳升 / 通关率带下移) | +| P3 恢复点囤积 | "Boss 前把血囤满" | HP 账本跨战斗延续 + 恢复点节点按比例回血——血是冒险内唯一带得走的资源,恢复点位置就是决策压力点 | +| P1 冒险级掌控 | "这局我到第二层" | 冒险摘要(到达层 / 通关数 / 代币记账)给每次死亡一个可对照的坐标;重开 = 新种子新局面,不是同一关的复读 | +| 锚点时刻 | "这层清完了" | Boss 倒下 → 大奖励兑付 → 下一层开门的节拍;死亡 → 摘要结算 → 一键再来 | + +## 全局流程 + +### 冒险生命周期 + +``` +新冒险(Idle) + │ ① reseed(master_seed)——RngService 种子注入,冒险域单位恰一次 + │ ② HP 账本置满 · 序列位置 = 1 + ▼ +start_level(1) ──► 关内行进(C1 域:节点链逐节点推进) + ▲ │ + │ ├─ battle/boss 节点 ─► C1 组装 begin_battle(L, D, hero_hp, override) → B1 + │ ├─ reward 节点 ─► B4 节点奖励入库(关内域库存) + │ └─ rest 节点 ─► C2 账本恢复(比例 = rest_heal_pct) + │ │ + │ 关卡收束点 = Boss 战胜利 ∨ 英雄死亡 + │ │ C1:调 B1 level_clear(库存关卡域清空 + 快照计数) + │ ▼ + │ level_end{result: cleared | defeated}(C1 → C2) + ├────────────────────────────────────────────┐ + │ cleared 且当前关 < N │ defeated ∨ abandoned + │ ▼ │ ▼ + │ 关卡大奖励(上限提升 · 血回满 · 代币记账) │ 冒险失败结算 + │ ▼ │ + └─ start_level(L+1) │ + │ │ + └─ cleared 且当前关 = N ──► 冒险完成结算 │ + │ │ + ▼ ▼ + 冒险摘要 → Idle(重开 = 全新一轮) +``` + +| 出口 | 触发 | 动作序列 | 约束 | +|---|---|---|---| +| 下一关 | level_end(cleared) 且当前关 < N | 大奖励兑付 → start_level(L+1) | 大奖励在清库之后(C1 收束点已先调 B1 level_clear) | +| 冒险完成 | level_end(cleared) 且当前关 = N | 冒险结算(不再发上限提升——没有下一战可受益) | 摘要标记 run_completed | +| 冒险失败 | level_end(defeated) | 冒险结算 | 库存已由收束点清空;血条归零即终 | +| 冒险放弃 | 玩家发起、非战斗期受理 | 冒险结算(结果 = abandoned) | 已获大奖励不追回;与失败同型 | + +## 核心规则 + +| # | 规则 | 要点 | 依据 | +|---|---|---|---| +| 1 | 冒险从第一关开始,倒下即终、清完末关即成 | 冒险 = 从序列第 1 关起步的完整一轮;关卡通关(Boss 战胜利)→ 自动进下一关;末关通关 = 冒险完成;任意战斗死亡 = 冒险结束。MVP 线性序列——无跳关、无回头路 | game-concept 冒险语义;C3 Boss 通关编排归属(C1/C2 域) | +| 2 | 每关是一条预置节点链,冒险关数不得超出表内容覆盖 | C2 为 LevelTable 行集内容 owner——链 = 战斗/奖励/恢复/Boss 节点依序排列、Boss 恒为末节点;战斗节点深度 = 前序战斗节点计数(Boss = Dmax);冒险关数 = 行集关数,加关 = 内容批(链行 + 敌我数值 + 变异解锁同批落,禁止只加序列不加内容);序列难度排布消费 C3 通关率带(L1→L2 带下移 + 雾解锁台阶) | D2 跨表校验;C3 期望表与 OQ1;A4 unlock_level=2 | +| 3 | 主角一条血走完整个冒险,关卡只换地图不换血条 | 账本收支四口——冒险开始置满 / 每战开始交出 / 战后收回 / 恢复点与大奖励入账;关卡切换零结算(血条跨关延续);血 = 冒险内唯一跨战斗资源(buff 战斗域清、库存关卡域清、RAP 战斗域清) | B2 hp_persistence 归 C1/C2;B1 begin_battle 入参;B4 关内延续同构 | +| 4 | 清完一关,下探前发一次大奖励:上限提升加回满 | 兑付时点 = 关卡收束清库之后、下一关开始之前;构成三件——HP 上限提升(+level_clear_hp_bonus)、血回满、代币记账加一份;不进背包不占库存域;末关通关不兑付 | B4 库存关卡域裁决;C3 区间平移换算率;C4 记账位 | +| 5 | 主角死亡冒险即终,重开是全新冒险不是关卡重试 | 失败 = 冒险终(无关卡重试、无检查点——Roguelike 语义已锁);重开 = 全新一轮(新种子 / 第 1 关 / 满血 / 空手);中途放弃与失败同型结算(已到手的保留);保留面 = 冒险摘要,清零面 = 账本 / 库存 / 种子域 | game-concept"死亡则冒险结束";A3 RAP 战斗域生命周期 | +| 6 | 一次冒险一粒种子 | 种子注入时点 = 冒险开始恰一次;冒险内雷图生成 / 奖励抽取 / 遭遇掷骰全部派生自该种子;冒险内不重置(切片逐关播种为其无冒险层的历史简化,本条转正为冒险域单种子);冒险可复现域 = master_seed + 三流计数器 | registry rng_service 快照契约;D3 消费方 | +| 7 | 关卡收束在关内宣布,冒险层只接结果 | C1 拥有关内一切行进(节点移动 / 兑付触发 / 战斗发起——参数组装:深度取链位、关卡序号与血条取 C2 下放);收束点(Boss 胜或死亡)C1 先调 B1 level_clear、再向 C2 宣布结果;C2 零战斗域直调、零库存域直调 | B1 出口分发"关卡结束 = 上层显式调用";切片 level_runner 先例转正 | + +#### 规则 2 细则:深度标号与覆盖 + +| 项 | 语义 | +|---|---| +| 节点类型 | battle / boss / reward / rest——reward、rest 不占深度、不查战斗表;battle、boss 占深度 | +| 深度标号 | 战斗节点深度 = 该节点之前(含同链)已排 battle/boss 节点数,从 0 起计;Boss 恒为链末节点且深度 = Dmax | +| 链模板(MVP) | battle(D0) → battle(D1) → reward → battle(D2) → battle(D3) → rest → boss(D4)——每关 7 节点 5 战斗深度;L1/L2 同构复用,模板修订随内容批 | +| 覆盖义务 | 冒烟断言:行集内每个 battle/boss 节点 (L, D) 在 enemy / board_gen 两表存在精确行(D2 跨表校验复用 + 序列关数断言)——序列关数 ⊆ 内容覆盖,越界 = 内容生产缺口 | +| 生效前提 | N=2 行集扩展前提 = C3 L2 深度轴行值填充批落地;未填充期冒险以单关行集运行(N=1 合法态——切片现状) | + +#### 规则 4 细则:大奖励构成 + +| 件 | 内容 | 载体 | 数值 | +|---|---|---|---| +| 上限提升 | max_hp_eff += level_clear_hp_bonus(冒险内生效——经 begin_battle 覆盖值传入后续每战) | C2 账本 + AdventureTable | +10 provisional(C3 区间平移换算率锚:+10hp ≈ +1 轮) | +| 血回满 | HP 账本 = 提升后 max_hp_eff | C2 账本 | 结构定式(非 knob——与 C3 满血开局校准口径闭合) | +| 代币记账 | tokens += token_per_level | 冒险摘要 | 10/关 provisional(消费方 C4) | + +#### 规则 7 细则:C1/C2 编排边界(给 C1 的接口契约——C1 GDD 强制输入) + +| 接口 | 方向 | 载荷 | 时点 | +|---|---|---|---| +| start_level(level_index, ctx) | C2 → C1 | ctx = {level_index, hero_hp, hero_max_hp_eff} | 冒险开始 / 每次关卡推进 | +| report_battle_end(hp) | C1 → C2 | 战后英雄 HP 终值(胜负皆报) | 每场战斗结束后 | +| heal_clamped(pct) | C1 → C2 | rest 节点恢复请求(比例取关卡行 rest_heal_pct) | 恢复点节点兑付 | +| level_end{result, hp, node_index} | C1 → C2 | result ∈ {cleared, defeated}(C1 已先调 B1 level_clear) | 关卡收束点 | +| 冒险放弃(abandoned) | D1 → C2 | 无载荷 | 非战斗期受理(MVP 无 UI 入口——语义预留) | +| 战斗发起组装 | C1 编排 | begin_battle(L, D, hero_hp, hero_atk, hero_max_hp):L / hero_hp / hero_max_hp 取 C2 下放,D 取链位,hero_atk 传表值通道(MVP 冒险内不提升攻击) | battle/boss 节点行进至 | + +## 枚举 + +### 冒险结果(封闭枚举) + +| 值 | 触发 | 摘要标记 | +|---|---|---| +| completed | 末关 level_end(cleared) | run_completed = true | +| failed | level_end(defeated) | run_completed = false | +| abandoned | 玩家放弃(非战斗期) | run_completed = false | + +### 关卡收束结果(封闭枚举) + +| 值 | 触发 | 后续 | +|---|---|---| +| cleared | Boss 节点战斗胜利 | 末关判定 → 大奖励 + 推进 / 冒险完成 | +| defeated | 任意战斗英雄死亡 | 冒险失败结算 | + +### 冒险摘要(结算产出,消费方 C4 / D3 / playtest 日志) + +| 字段 | 类型 | 语义 | +|---|---|---| +| run_completed | bool | 冒险是否打穿序列 | +| levels_cleared | int | 已通关数(代币记账乘子) | +| level_reached | int | 止步关卡序号(失败 / 放弃时 = 当前关) | +| tokens_recorded | int | levels_cleared × token_per_level | +| master_seed | int64 | 本冒险种子(复盘 / 快照域标识) | + +## 数值结论与推导 + +> C2 是结构系统——运行时零新增公式,以下为账本结算式与设计期合成式;全部数值 provisional,调参所有权见 Tuning Knobs。 + +### F1 冒险通关率合成(设计期工具式) + +**结论**:P(冒险通关) = Π P(L_i 通关);MVP N=2 合成带 = [6%, 20%](L1 [30,50]% × L2 [20,40]%)——冒险长度即难度总闸。 + +#### 推导 + +``` +P_run = Π_{i=1..N} P(L_i) (逐关独立满血开局口径) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 各关通关率 | P(L_i) | float | (0,1) | C3 期望表逐关目标带(L1 [30,50]% / L2 [20,40]% / L3+ [10,25]% 预留) | +| 冒险关数 | N | int | [1, 行集关数] | LevelTable 行集关数——MVP 2 | + +| 验算 | 读数 | 判定 | +|---|---|---| +| N=2 合成 | [0.30×0.20, 0.50×0.40] = [6%, 20%] | 单次通关率带合理(Roguelike 体感:多数局止步中途) | +| 口径闭合 | C3 带按每关满血开局校准;大奖励血回满使该口径跨关成立 | ✓(若未来大奖励改部分恢复,本式失效须改口径) | + +### F2 HP 账本结算式 + +**结论**:账本三写口全部 clamp 于 [0, max_hp_eff];大奖励回满使每关开局恒满血——血条跨关只降不自动回,恢复只来自恢复点与大奖励两个显式口。 + +#### 推导 + +``` +战后回写: hp := report_battle_end(hp) (victory 腿 hp ∈ [1, max_hp_eff];defeat 腿 = 0 → 冒险终) +恢复点: hp := min(hp + floor(max_hp_eff × rest_heal_pct ÷ 100), max_hp_eff) +大奖励: max_hp_eff := max_hp_eff + level_clear_hp_bonus; hp := max_hp_eff +战斗输入: begin_battle(hero_hp = hp, hero_max_hp = max_hp_eff, hero_atk = 表值通道) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 账本血量 | hp | int | [0, max_hp_eff] | C2 冒险域持有;战斗内由 B2 承载 | +| 生效上限 | max_hp_eff | int | [表值, 表值+加成×已通关数] | HeroTable.max_hp + Σ大奖励提升 | +| 恢复比例 | rest_heal_pct | int | [0,100] | LevelTable 逐关行字段(切片 30 转正 provisional——数值复验归 C3) | +| 上限提升 | level_clear_hp_bonus | int | [0,999] | AdventureTable 单行全局(默认 10) | + +| 验算(示例轨迹,表值上限 50) | 账本读数 | +|---|---| +| 冒险开始 | 50/50 | +| D0–D3 逐战后(示例) | 43 → 31 → 24 → 17 | +| 恢复点(30% × 50 = 15) | min(17+15, 50) = 32 | +| Boss 胜后(假设余 8)→ 大奖励 | max_hp_eff = 60、HP = 60 | +| L2 开局 | 60/60 满血(C3 带口径闭合) | + +### F3 冒险代币记账式 + +**结论**:tokens = levels_cleared × token_per_level——通关与中途死亡同式,差异只在 run_completed 标记;通关加成位预留(MVP 不启用,裁决归 C4)。 + +#### 推导 + +``` +tokens_recorded = levels_cleared × token_per_level (token_per_level 默认 10,消费方 C4) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 通关数 | levels_cleared | int | [0, N] | 摘要字段 | +| 每关代币 | token_per_level | int | [0,999] | AdventureTable 单行全局——值归 C4 | + +| 验算 | levels_cleared | tokens | +|---|---|---| +| 死在 L1 | 0 | 0 | +| 清 L1 死在 L2 | 1 | 10 | +| 打穿 N=2 | 2 | 20 + run_completed 标记 | + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Idle | 冷启动 / 冒险结算完成 | 唯一出口 = 开始新冒险(reseed → 置账本 → start_level(1)) | +| RunActive | start_level(L) 受理 | 关内行进委托 C1;C2 只守账本写口与收束监听;放弃请求非战斗期受理 | +| Settling | level_end 或 abandoned 受理 | cleared 非末关 → 大奖励 → start_level(L+1) 回 RunActive;末关 cleared / defeated / abandoned → 摘要产出 → Idle | + +不变式:RunActive 期恰一关卡在飞;master_seed 冒险内恒定;HP 账本 ∈ [0, max_hp_eff];Settling 恰一次(重复宣布拒绝 + 诊断);冒险内零流消费(RngService open_stream 零命中)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| 末关 Boss 战胜利 | 冒险完成结算——不发上限提升(无下一战可受益)、无 start_level(N+1);代币照记 | 规则 1/4 | +| start_level 请求超出行集关数 | 防御 fail-fast(序列配置违约);运行时战斗表 floor 兜底属 D2 既有语义——C2 不产生越界请求,玩家不可达越界关 | 规则 2 / C3 边界情况表 | +| 行集战斗节点无对应 enemy/board_gen 行(内容缺口) | 冒烟断言拦截(覆盖义务);未填充期以单关行集运行(N=1 合法态) | 规则 2 细则 | +| level_end 重复宣布 | 幂等:已 Settling 的冒险拒绝重复兑付 + 诊断,账本与摘要零变化 | 状态不变式 | +| 恢复点满血触发 | 回复 0、合法(账本静默——无明细行协议,与战斗域"奖励未生效"族不同层) | F2 | +| 大奖励时上限已有加成 | 累加(每关一份;N=2 冒险至多一次) | 规则 4 | +| 放弃请求在战斗中 | 拒绝(战斗域单事务在飞;入口呈现仅非战斗期可达——MVP 无 UI 入口) | 规则 5 / 规则 7 细则 | +| 英雄死亡但战斗判定为胜利 | 结构不可达(B1 判定 hero 先判);防御断言:cleared 收束要求账本入账 hp > 0 | 规则 3 / B1 判定段 | +| 重开种子与上局相同 | 合法态(熵源小域);约束 = 种子生成禁走玩法随机通道、冒险内计数器随 reseed 归零——种子值生成源裁决归实现期 ADR | 规则 6 / 开放问题 | +| 冒险内跨关遗留物查询 | 无遗留——buff 战斗域清(B1)、库存关卡域清(C1 收束点)、RAP 战斗域清(A3);冒险层账本与摘要是唯一跨关状态 | 规则 3/7 | +| 关卡行缺失(LevelTable 无该 level_index 行) | fail-fast——序列配置事故非玩家可达态;与战斗表 floor 兜底分属两层 | 规则 2 | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| C3 | hard | 期望表逐关通关率带 = 序列难度排布输入;L2 深度轴行值(enemy / board_gen 的 (2, D1–D4))随批填充 = N=2 生效前提;大奖励 / 恢复点数值联动复验(区间平移换算率锚) | +| D2 | hard | LevelTable schema(切片建表——C2 为行集内容 owner)· AdventureTable 新表走 D2 接口漂移规则(单行全局,C2 回填 owner)· ConfigDB 单点读取 · RngService 种子注入入口(新面——义务登记)· 跨表校验复用 | +| B1 | hard(下游消费面) | begin_battle 入参 (L, D, hero_hp, hero_atk, hero_max_hp) 供给方转正 = C1 组装 / C2 供值;level_clear 调用方 = C1 收束点(C2 零直调);战后 HP 读回 = B1 呈现面英雄投影(载荷自包含) | +| B2 | 间接 | hero hp_persistence 归 C1/C2——本 GDD 兑现 C2 半边(账本);B2 单位接口零变更 | +| B4 | 间接 | 库存关卡域清空触发源 = C1 收束点;节点奖励兑付归 C1;大奖励不进库存域(零接触) | +| A4 | 间接 | 雾 unlock_level=2 = L2 台阶的内容面(序列难度排布消费) | + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| C1 | hard | 接口契约四件套 + 收束点语义 + 战斗发起组装权(规则 7 细则表全文)——C1 GDD 强制输入,门禁评审核对项;切片 level_runner 为其实现先例(冒险域职责切出后归 C2 侧新 owner) | +| B1 | 下游(消费 C2 供值) | begin_battle 入参面;B1 依赖表 C1/C2 provisional 行由本 GDD 收编转正 | +| C4 | 远 | 冒险摘要 = 代币入账位;冒险开始参数面 = 天赋注入预留位(MVP 空参数——开始新冒险时注入,结构同 begin_battle 覆盖值通道) | +| D3 | 远 | 快照契约域单位 = 冒险(master_seed + 三流计数器);摘要字段含 master_seed 供复盘 | +| 呈现层(D1) | soft | 冒险摘要结算画面 / 关卡推进指示 / 放弃入口(非战斗期)——数据源 = 本 GDD 摘要与状态投影 | + +**跨 GDD 义务清单(对侧修订批逐项核对,缺项即阻塞级按各自门禁)**: + +| 对侧 | 内容 | 状态 | +|---|---|---| +| C3 | L2 深度轴行值填充(enemy 四行 + board_gen 四行,(2, D1–D4))——OQ1"内容与数值同批"机制的兑现批 | N=2 生效前提 | +| D2 | RngService 种子注入入口登记(reseed——provisional 名,实现载体随 ADR) | 义务登记 | +| D2 | AdventureTable 建表(单行全局两字段——Rule 4 豁免,HeroTable/SettlementTable 先例;C2 回填 owner) | 接口漂移 | +| B1 | begin_battle 入参供给方转正(依赖表 C1/C2 行收编——本 GDD 为 C2 半边定义源) | 转正登记 | +| registry | level_table / adventure_table / hero_override_parameter_passing 三条目登记(本定稿批执行) | 本批 | +| C1 | 接口契约四件套 + 收束点语义全款承接(C1 GDD 门禁评审核对——C2 在 C1 之前设计,本表即其接口基线) | 待 C1 定稿核对 | +| C4 | token_per_level 记账消费 + 天赋注入位消费 | 远(C4 期) | + +## Tuning Knobs + +| 字段 | 类型/域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| LevelTable 行集(N 与链模板) | level.tres 逐关行 | L1 现行 + L2 随批 | ✓(结构) | 加关 = 内容批(链行 + C3 数值 + 变异解锁同批);过长 = 内容缺口,过短 = 冒险时长不足(愿景 20–40 分钟 vs MVP 两关,真人数据后定扩展节奏) | +| LevelTable.rest_heal_pct | int [0,100] 逐关行 | 30 | ✓ | 恢复点比例;数值复验归 C3;100 = 恢复点变全满泉、0 = 死节点 | +| AdventureTable.level_clear_hp_bonus | int [0,999] 单行全局 | 10 | ✓ | 大奖励上限提升;值锚 = C3 平移换算率(+10hp ≈ +1 轮);上调须 C3 重扫 L2 带 | +| AdventureTable.token_per_level | int [0,999] 单行全局 | 10 | ✓ | 代币记账;消费方 C4 | +| 链模板节点构成 | level.tres nodes 数组 | 战×2→奖励→战×2→恢复→Boss | 内容批 | 节点类型序列修订 = 内容批(随 C3 数值同批验带),非调参 | + +- 非 knob(结构冻结):序列线性无跳关、Boss 恒末节点、死亡即冒险终、大奖励三件构成、冒险内零流消费、收束点编排边界。 +- Knob 交互:level_clear_hp_bonus × C3 L2 通关率带(回满 + 上限共同抬开局强度——上调走 C3 重扫);rest_heal_pct × 通关余量带(C3 通关余量 p10 ≥ 3 的对冲杠杆)。 + +## 验收标准 + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | 冒险 Idle | 开始新冒险 | reseed 恰一次注入新 master_seed;HP 账本 = 表值上限满血;start_level(1) 受理;L1 行集加载零诊断 | +| AC-2 | RunActive 当前关 < N | level_end(cleared) | 大奖励恰一次:max_hp_eff += level_clear_hp_bonus、账本 = max_hp_eff、tokens += token_per_level;随后 start_level(L+1) | +| AC-3 | RunActive 当前关 = N | level_end(cleared) | 冒险完成结算:无上限提升、无 start_level(N+1);摘要 run_completed = true、tokens = N × token_per_level | +| AC-4 | RunActive 任意战斗 | 英雄死亡 → level_end(defeated) | 冒险失败结算;摘要 levels_cleared = 已通关数、level_reached = 当前关;账本封闭 | +| AC-5 | 冒险结算后 | 开始新冒险 | 全状态重置(账本 / 序列位置 / 库存域空 / 摘要新生成);reseed 新种子注入 | +| AC-6 | RunActive 全程 | 逐战核对入参 | begin_battle.hero_hp = 账本当前值、hero_max_hp = max_hp_eff;战后账本 = 英雄终值投影(载荷自包含) | +| AC-7 | MVP 全行集 | 冒烟跨表断言 | 每关 battle/boss 节点 (L,D) 在 enemy / board_gen 有精确行;序列关数 = 行集关数 | +| AC-8 | C2 文件集 | 静态扫描 | RngService 流消费(open_stream)零命中;reseed 每冒险恰一次调用 | +| AC-9 | Settling 已受理 | level_end 重复宣布 | 拒绝 + 诊断;账本与摘要零变化 | +| AC-10 | 生效前提未兑现(L2 行集缺行) | 冒烟运行 | 以单关行集载体运行(N=1 合法态);N=2 断言显式 SKIP 非 FAIL | +| AC-11 | RunActive 战斗中提交放弃请求 | 请求受理判定 | 拒绝;非战斗期受理 → abandoned 同失败型结算、已获大奖励不追回 | +| AC-12 | 大奖励后首战 | begin_battle 入参核对 | hero_max_hp = 旧上限 + level_clear_hp_bonus(覆盖值通道);hero_atk 走表值通道(MVP 恒不覆盖) | +| AC-13 | rest 节点兑付 | 满血 / 缺血两腿 | 恢复 = floor(max_hp_eff × rest_heal_pct ÷ 100) clamp 上限;满血腿回复 0 合法 | + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| OQ1 | 冒险长度扩展节奏——MVP N=2(约 15–25 分钟)vs 愿景单局 20–40 分钟;扩关 = 内容批,节奏按真人单关时长数据定 | C2 + C3 | playtest 批次 2 后 | +| OQ2 | 大奖励形态扩展(三选一 / 攻击型 / 通关加成)——AdventureTable 扩列结构位已留,设计随 C4 元进度 | C2 + C4 | C4 GDD | +| OQ3 | master_seed 生成源(熵源通道禁走玩法随机——种子值生成器归属) | D2 / 实现期 ADR | 实现期 | +| OQ4 | 放弃入口呈现与受理细节(仅语义已定:非战斗期受理、同失败型结算) | D1 | D1 / UX 批 | +| OQ5 | L2 display_name 表现层命名(provisional 名"第二层 · 雾漫巢径"——风格对齐 L1"苔藓回廊") | C2 表现层 | L2 内容批 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1 | 2026-09-14 | 初版 solo 出稿:七核心规则(冒险骨架 / 序列覆盖 / HP 账本 / 大奖励 / 失败重开 / 种子接线 / C1-C2 编排边界)+ F1–F3 + 13 AC + 给 C1 接口契约四件套;MVP N=2 + AdventureTable 单行全局 + N=2 生效前提登记;切片 level_runner 关卡域先例切分转正(冒险域归 C2、关内行进归 C1) | +| v1.1 | 2026-09-14 | 评审通过状态同步:状态头 In Design → Approved、复核注记更新(--solo APPROVED + 日志路径登记);零规则变更 | diff --git a/design/gdd/c3-difficulty-curve.md b/design/gdd/c3-difficulty-curve.md new file mode 100644 index 00000000..5a3f4b3a --- /dev/null +++ b/design/gdd/c3-difficulty-curve.md @@ -0,0 +1,526 @@ +# C3 难度曲线与精英/Boss(Difficulty Curve & Elite/Boss) + +> **状态**:In Design · lean 格式 +> **版本**:v2 · 2026-09-14 · 作者:DK + design-system +> **承载支柱**:P4 地牢下探的探险氛围(主)· P1 推理的掌控感 / P3 风险与收益同源(难度即压力源) +> **一句话**:C3 定义"难度 = f(关卡序号, 节点深度, 精英/Boss 修饰)"中每一项随进度如何变化——敌人强度曲线、雷图压力曲线、奖励供给曲线与精英/Boss 修饰语义的唯一数值所有者。 +> **复核注记**:v1 solo 出稿 → 经 /design-review(lean 单会话)NEEDS REVISION 8 blocking,同日修订核销(本 v2);专员/总监代理未咨询——生产前人工复核保留。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 数值 owner 而非机制 owner:表结构归 D2、生成算法归 A1、结算规则归 B1、敌种机制归 B2——C3 以行值与参数驱动已定稿机制,形成"越下越险"的进度体感(P4);玩家不直接感知本系统(Indirect),它是体感背后的统一坐标系 | +| 拥有 | EnemyTable / BoardGenTable / RewardTable / RewardConfigTable / HeroTable 行值与参数 · DifficultyTable 修饰结构位(MVP 空表)· 难度期望表(按 (L,D) 的期望胜轮带 / 期望承伤占 max_hp 比 / 通关率目标带 / 通关余量带——调参的"正确行为"基准)· **生存链区间模型**(替代关级供给带:每战 S/K 区间三档判定 + 关级通关恒等式 + 输出占比带)· 七步调参流程 · P_perfect 乘数调参权 · m_w 调参通道(终值共裁 B4/B1)· B1 对账约束清单的承接与消解 | +| 不拥有 | 表结构(D2)· 生成算法(A1)· 结算规则与护甲克制矩阵机制(B1——C3 投值)· 敌种机制(B2)· 变异机制框架(A4——C3 只投内容行)· 精英节点完整语义(C1 联动)· 元进度天赋数值(C4——C3 是其世界侧对照物)· 呈现(B2/B5/D1)· encounter 掷骰执行(B1 编排)· 供给控制器机制本体(B4——C3 投参数) | +| 上游输入 | D2 表 schema 与 R1 解析 · B1 条款③⑤对账输出(B1 F4/F5 = 约束生成器)· B2 字段语义与校验域 · B4 联动重算义务与供给控制器机制 · A1 F4 批量红线 · 切片 v3 行集实测数据(含减伤墙批) | +| 下游输出 | 各基表行值(经 ConfigDB 单点读取)· 每战 S/K 区间与三档判定数据面(level_monte_carlo actuals 生产,B4/B1 对账消费)· 难度期望表(C2 难度排布 / C4 天赋标定基准——区间平移换算率:+10hp ≈ +1 轮、atk 翻倍 ≈ K 减半)· unlock_level 投放计划(A4)· "何时变难"数据面(B2/B5/D1 呈现消费) | +| 硬约束 | 运行时零新增公式(C3 侧数值直填基表行;机制本体——encounter 掷骰 / 护甲克制 / 供给控制器——归各 owner GDD,C3 只投行值与参数)· 可推理率 target ∈ [0.85, 1] 绝对锚不可下探 · 条款③⑤与预警⑥约束内调参或显式扩界 · 联动网络六组禁独立调参 · 每次修订强制走七步流程 · 全部值 provisional 起步随 playtest 修正 | + +## 体验锚点 + +> Indirect 体验一句话:**玩家不操作本系统,但每一分钟都在感受它**——第二层的熊地精比第一层的地精更凶、关底 Boss 的压迫性血量、"这一层开始有雾了"的解锁节奏,本系统是这些体感背后的统一坐标系。 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P4 越下越险的压迫节奏 | "这一层开始有雾了" | 每层一个可感知难度台阶:新敌种登场 / 新行为环段 / 新变异解锁 + 基准数值跳升(L 轴)与温和加压(D 轴);台阶可感知但可解释——跃升可归因于可见变化而非纯数字膨胀(后者产生"被坑"感而非"变险"感);可量化形态 = S/K 区间随 (L,D) 的漂移轨迹(绿→黄→红空间分布) | +| P1 攻克的对照物 | "我变强了" | 首关基线明显吃紧但多数玩家能过(通关率目标带);后续关卡陡升使 C4 天赋提升产生可测量的通关期望改善——区间平移换算率(+10hp ≈ +1 轮、atk 翻倍 ≈ K 减半)使"我变强了"从体感变为可推导结论 | +| P3 压迫的顶点 | "Boss 房开门前" | 恢复点/囤刀决策压力顶点——供给稀缺性在此处兑现价值;Boss 血量压迫由 Boss 血量锚承载 | +| 锚点时刻 | "三拍 vs 单拍" | 第二层首遇熊地精——三拍行为环 vs 地精单拍攻击,难度跃升有具体面孔;击杀 Boss——"这层我算下来了"的完成感 | + +- 参考感受合成:Peglin 的地图难度爬升 × 杀戮尖塔的 Act 结构(普通→精英→Boss 三段节奏)→ 本作 = 关间大台阶 × 关内小坡度 × Boss=Dmax 关底压轴。 + +## 全局流程 + +### 难度曲线生成(设计期直填 → 运行时直读) + +``` +设计期(本 GDD 唯一产出物 = 表行值) + │ ① L 轴台阶:每关 ≥1 项新内容投放(新敌种/新环段/新变异)+ base 跳升 k_L ∈ [1.3, 1.6] + │ ② D 轴坡度:同敌种 hp 平缓上移(坡度域)+ 板参数温和加压(target ≥ 0.85 绝对锚) + │ ③ Boss 行:Dmax 直填行(数值大台阶 + 专属行为环 + 本关最大板形) + │ ④ 七步调参流程走查 → 行值写入 enemy / board_gen / reward / reward_config / hero .tres + ▼ +运行时(C3 零公式;机制归各 owner) + │ ⑤ B1.begin_battle 实例化期:unit_count 区间均匀骰(encounter 封闭流)→ 本战实际数量 + │ ⑥ B2.spawn(L, D):R1 floor 解析 → 行值快照 → DifficultyTable 修饰套乘(快照之前;MVP 空表无修饰) + │ ⑦ A1 读 BoardGenTable 行生成 → 挖掘期 → B1 结算(既有乘区:护甲克制 / 状态池 / 充能伤害) + ▼ +战斗/挖掘按既有系统运作(C3 运行时零公式) +``` + +### F7 调参流程(每次行值修订强制走完——七步) + +``` +① 改表值(C3 直辖 knob) + │ ② smoke_all 全绿(含 A1 生成质量闸——d0.28 回退先例的拦截点) + │ ③ level_monte_carlo 扫描(通关率带 + 三档判定 + 通关余量 + 输出占比) + │ ④ B4 登记常数与对账重制(AC-19 对账对漂移检测 / AC-6 分支 4 / AC-18 重跑——B4 义务 #10) + │ ⑤ 对账复算(条款③⑤ / 预警⑥) + │ ⑥ README 调参日志登记(含期望偏离) + ▼ +行值生效(任一步 FAIL → 回滚或登记 OQ——禁止带病前进) +``` + +### 出口分发 + +| 出口 | 触发 | 动作/去向 | 约束 | +|---|---|---|---| +| 调参通过 | 七步全 PASS | 行值生效 → 期望表复算 → 调参日志登记 | 期望偏离必须登记,禁止静默出带 | +| 调参失败 | 任一步 FAIL | 回滚行值或登记 OQ | 禁止带病前进 | +| 红档组合 | 任一战 S_max ≤ K_min | 扫描步拦截,行值禁止入表 | 配置事故——区间模型判定 | +| 期望表偏离 | 调参后越出通关率/余量带 | 回滚或 OQ 登记 | 禁止静默出带(状态表同名行) | +| 投放断档 | 某关无新敌种且无新环段 | 流程投放检查项拦截 | 内容生产缺口——C3 调参无法修补(数值不是内容) | + +## 核心规则 + +| # | 规则 | 要点 | 依据 | +|---|---|---|---| +| 1 | 难度承载结构 = 直填行值主体 + DifficultyTable 修饰结构位 | 全部 (level_index, node_depth) 维度最终数值**直填**各基表行(EnemyTable / BoardGenTable / RewardTable——切片已验证的工作方式:R1 floor 解析直读、零新机制);**DifficultyTable 仅承载"按坐标施加的全局修饰"结构位**(键 (L,D)、乘数域;施加点 = B2.spawn 实例化时套乘于行值快照之前)——MVP 不产生任何修饰行(空表结构位;首个使用场景 = 精英节点;Boss 走 Dmax 直填行、不走修饰表) | D2 接口漂移规则;registry `difficulty_table`;B2 实例化条款"接口未定"预留由本条兑现 | +| 2 | 难度 = f(L, D) 双轴语义 | **L 维(关间)= 大台阶**——每关至少一项新内容投放(新敌种 或 新行为环段 或 新变异解锁)+ 基准数值跳升;**D 维(关内)= 小坡度**——同敌种数值平缓上移(坡度域见关内深度坡度公式)、板参数温和加压(尺寸/密度小步上调,推理率 target 不低于 0.85 绝对锚);**Boss = 各关 Dmax 行**(A1 登记约定兑现)——数值大台阶 + 专属行为环 + 专属板形,见 Boss 遭遇语义条 | B2 投放节奏规则(OQ8);Boss 血量锚公式(F3);A1 第三轴登记约定 | +| 3 | 敌种投放节奏 | 每关至少一个新敌种或既有敌种新环段投放;MVP 投放 = L1 三段(goblin→bugbear→ogre_chief,行值总表);**DND 更名批生效**:grub→goblin、ambusher→bugbear、boss_guardian→ogre_chief——registry/B2/D1/art-bible 键集随本修订批对齐;多敌种同深度选择规则:MVP 每行集恰一敌种(B2 锁定),C1 分支地图定稿时若需同深度多敌种选择,走预登记改键通道——本 GDD 预留不定义 | B2 投放节奏规则(OQ8);B2 多敌种共裁项(OQ7);D1 键集冲突② | +| 4 | 切片行集转正为 MVP 默认曲线 | 垂直切片调参登记的行集有实测依据,转正为 MVP 默认值——**权威枚举 = 「MVP 行值总表」(v3,出处 = 切片 .tres)**,旧登记表(grub 时代值)由本表替代;后续 L2+ 曲线数值随关卡内容扩展填充(结构本条已定,数值行随内容扩展) | MVP 行值总表;L2+ 数值开放问题(OQ1) | +| 5 | Boss 遭遇语义 | ① Boss = 关卡末节点 = Dmax 行(LevelTable 末节点必须 Boss——schema 校验);② Boss 数值台阶 = hp 取本关普通战斗 hp 期望总和的 0.5–1.1 倍 + **atk ≥ 本关普通战最高行 atk**(本次重定义——原"高于均值一档"无档位定义;现值 Boss 9 ≥ bugbear 9 ✓)+ 专属行为环(含 charge 拍——玩家可规划的安全挖掘拍);③ Boss 板 = 本关最大板形 + 密度上探(受可推理率 target 绝对锚约束);④ Boss 战胜利 = 关卡通关(C1/C2 域编排) | Boss 血量锚公式(F3,加难走廊 [1.1, 1.35] 预登记);LevelTable schema 校验;A1 第三轴登记约定 | +| 6 | 精英节点(MVP 不实现,语义预留) | `elite_` / `boss_` 前缀字段命名空间预留保留;精英修饰完整语义(独立节点类型/词缀系统/出现规则)**归 C1 关卡节点地图定稿后的联动设计**——分支地图中"同深度并存精英与普通节点"触发棋盘生成端强制重审(改键走 D2 接口漂移规则);本 GDD 承诺:DifficultyTable 结构位 + 命名空间预留足以承载,不预写语义 | A1 第三轴登记约定(命名空间预留 + 定稿强制重审) | +| 7 | A4 全局变异语义(scope=global 兑现) | `scope=global` 行 = 该变异**施加于整板全部合格格**(区别于 region 的区域布置);**全局变异仅 Boss/精英图可产生**(普通图禁用——加载期校验:global 变异的布置行须伴随 elite/boss 标记;MVP 无标记字段前 = MVP 不产生 global 行,A4 既有 warning 承载);MVP 不定义任何具体全局变异行(岩浆/冰冻无 MVP 容器——A4 锁定),本条只锁定语义框架与禁用面 | A4 scope=global 预留位条款;A4 例外表 warning 行 | +| 8 | 遭遇随机化(区间 unit_count) | EnemyTable 行 `unit_count` 升级为**区间语义** [unit_count, unit_count_max](域 [unit_count, 3],B2 上界锁定不变);掷骰经 RngService `encounter` 封闭流——**接口漂移登记完成**:registry 流集合含 encounter、B1 begin_battle 掷骰增补、B2 spawn override 参数与 unit_count_max 字段增补、D2 schema 两字段增补;细则见下表 | registry `rng_service`(encounter 增流登记);B1/B2/D2 增补条款(X-6~X-8);遭遇随机化细则表 | +| 9 | 调参约束内工作纪律(B1 对账输出承接) | C3 每次行值修订必须走满七步调参流程,且不得触碰已锁约束边界——四条边界见下表(B1 条款③⑤对账输出与预警⑥ = "禁止独立调参"纪律的量化边界,C3 调参必须在其内工作或显式扩界;registry 回执见依赖节对账登记表) | F7 七步流程;B1 条款③⑤对账输出;对账登记表 | + +### MVP 行值总表(权威枚举,出处 = 切片 .tres v3) + +| 关卡 | 深度 | enemy_id | display_name | hp | atk(区间) | unit_count(_max) | pattern | armor_type | +|---|---|---|---|---|---|---|---|---| +| L1 | D0 | goblin | 地精 | 24 | 7 [4,10] | 1 (2) | [attack] | none | +| L1 | D1 | goblin | 地精一伙 | 13 | 5 [2,8] | 2 (3) | [attack] | none | +| L1 | D2 | bugbear | 熊地精 | 40 | 9 [6,12] | 1 | [attack, charge, heavy] | a | +| L1 | D3 | bugbear | 熊地精双雄 | 18 | 6 [3,9] | 2 (3) | [attack, charge, heavy] | a | +| L1 | D4 | ogre_chief | 食人魔酋长 | 150 | 9 [6,12] | 1 | [attack, attack, charge, heavy] | b | +| L2 | D0 | bugbear | 熊地精 | 30 | 7 [4,10] | 1 | [attack, charge, heavy] | a | + +| 表 | 转正行集(登记值) | +|---|---| +| BoardGenTable L1 深度轴 | D0/D1 10×10 · D2–D4 12×12 · d0.25/t0.88 · attempts 24 | +| HeroTable | atk = 6 · max_hp = 50 | +| RewardTable 要点 | 格子池四行即时类(attack_up 20 / defense_up 16 / spark_aoe 16 / heal_minor 15)· 注入池三行可选类(knife_t1 60 / knife_adv 30 / heal_pack 10——供给控制器注入)· clear_blast 40(clear_default 槽,instant_aoe 当场兑付,完美 ×2) | +| RewardConfigTable | t_fog_min=2 · k_dmg=0(保底语义由战斗首件保证取代)· n_draft=32 · p_min=8 · active_p0=0.4 · active_decay=0.5 | +| SettlementTable 增补字段 | damage_a_per_cell=1 · damage_a_cap=8 · damage_b_per_flag=2 · armor_effectiveness 克制矩阵(base/dmg_a/dmg_b × none/a/b——特化 ×150 / 无甲 ×100 / 对位 ×50)· p_perfect=200 · D_ref=12 | + +- 实测依据:期望扫描 N=15–30 × 多种子 + 8 种子密度实验 + 试玩批次 1 + 减伤墙批。 +- v3 数值批(试玩批次 1 裁决):atk 4/4/6/4/7 → 7/5/9/6/9(约 +50%,承伤侧主杠杆);D0 24 保持防秒杀(20hp 会被首轮盲挖粉尘秒杀——12+9);Boss 130→150(用户拍板可上调)。 +- **区间批(现行)**:奖励五行 power 区间化(heal_minor [2,6] / knife_t1 [5,11] / heal_pack [6,18] / knife_adv [10,22] / clear_blast [25,55]——身份批内 roll,中点恒等原值)+ 敌方 atk 区间化(上表 [min,max],遭遇骰同流 spawn 期整战持久,±3 provisional)——宽度均 provisional 归本 GDD 测量迭代;A3 条款①当量锚以中点口径维持;验收口径 = 完美策略档 0 加成通关 ≤40%(OQ7)。 +- **加难走廊(带锚定稿预登记)**:L1 基线通关率目标 [30%, 50%],现扫描 60% 越上界——杠杆候选 = r_boss 上探走廊(Boss 180–207,见 Boss 血量锚公式)+ 群战上探 + 供给 decay 0.5→0.6;**扫描验证待执行(OQ7——本会话无 godot 运行时),落带前 v3 值维持 provisional-pending-scan**。 + +### 遭遇随机化细则(Core Rule 8) + +| 项 | 语义 | +|---|---| +| 区间语义 | unit_count 升级为 [unit_count, unit_count_max];unit_count_max 可省略——缺省 = 定值,旧行零迁移 | +| 域 | [unit_count, 3](B2 unit_count 上界锁定不变) | +| 掷骰时点 | B1.begin_battle 实例化期(扫雷开始前)按区间均匀骰定本战实际数量(B1 GDD 增补条款) | +| 敌种不变 | 同敌种掷骰——不触碰 B2"每行集恰一敌种"锁定 | +| 随机通道 | RngService `encounter` 封闭流(registry 流集合已登记);scope_event_binding = 每次战斗实例化恰一 scope;发生在挖掘决策域之外(P1 合规) | +| 消费方式 | B1 掷骰 → B2.spawn 以 override 参数接收实际数量(B2 零随机纪律保持——收到的即确定值;B2 GDD 增补条款) | +| 教学位豁免 | 新敌种首遇节点固定数量不加骰(D0 goblin ×1、D2 bugbear ×1 固定——行值总表) | +| 可回放性 | 同种子全程可回放(流取号确定性不变) | +| 设计原则 | 数量是比单体质变更优先的深度表达(关内深度坡度公式 Example 延伸);期望总量对齐关级恒等式检算 | + +### 调参约束边界(Core Rule 9) + +| # | 约束边界 | 内容 | +|---|---|---| +| ① | 条款③满配破线 | unit_count>1 的行须满足"降密度或降 m_amb"补偿——registry clause3_supply 已登记;切片群战行 hp/atk 下调为合规先例 | +| ② | 条款⑤清图支配性 | 约束包①需求对 clear_blast(instant_aoe,C = power = 40)重推:缺口 = 46.1−40 = 6.1(L1)/ 91.8−40 = 51.8(L2)——登记为非支配设计态 + OQ4 playtest 观测弃图率(原 clear_knife C=8/16 模型由现行登记替代) | +| ③ | 可推理率绝对锚 | target ∈ [0.85, 1] 不可下探(P1 生成端保证) | +| ④ | 期望表对照 | 调参后期望表必须复算并在 README 调参日志登记偏离 | + +## 枚举 + +### DifficultyTable(新表,MVP 空表——结构位) + +| 项 | 值 | 语义 | +|---|---|---| +| 键 | (level_index, node_depth) | 按坐标施加的全局修饰 | +| 乘数域 | [0.1, 10](registry 预登记) | 套乘施加点 = B2.spawn 实例化时、行值快照之前;越域 → clamp 至域界 + warning(registry 登记的显式例外——组合事故非配置事故,非 fail-fast 场景) | +| MVP 行集 | 空(零修饰行) | 首个使用场景 = 精英节点;Boss 走 Dmax 直填行、不走修饰表;建表走 D2 接口漂移规则 | + +### EnemyTable.unit_count 区间语义(Core Rule 8) + +| 字段 | 域 | 语义 | +|---|---|---| +| unit_count | int [1, 3] | 区间下界;旧行语义 = 固定数量 | +| unit_count_max | int [unit_count, 3],可省略 | 区间上界——缺省 = 定值,旧行零迁移;掷骰 = B1 实例化期区间均匀骰 | + +### RngService 流集合(encounter——已登记) + +| 流 | scope 绑定 | 消费点 | 语义 | +|---|---|---|---| +| encounter | 每次战斗实例化恰一 scope | B1.begin_battle 实例化期(扫雷开始前) | unit_count 区间均匀骰——同敌种数量随机;发生在挖掘决策域之外(P1 合规);同种子全程可回放 | + +### A4 变异 unlock_level 门控序列(投放计划归 C3) + +| 序 | 内容 | 门控 | 说明 | +|---|---|---|---| +| 1 | 普通图(无变异) | 基线 | — | +| 2 | 雾 | unlock_level = 2(A4 锁定) | 雾参数 f≤8d / ≤0.25(P1 度量绝对上限) | +| 3 | 岩浆/冰冻 | 随关卡扩展 | 无 MVP 容器(A4 锁定)——投放计划归 C3 | + +## 数值结论与推导 + +> 定位声明:C3 是数据系统——运行时零新增公式(数值直填基表行,运行时仅经既有 B1 乘区 / B2 接口 / R1 直读运作)。本节全部公式为**设计期工具式**(曲线拟合基准、区间估算、对账复算),执行载体 = 调参期批量工具(level_monte_carlo.gd / reward_expectation.gd,见 F7),不进入游戏运行时。全部数值 provisional 起步,随 playtest 修正。 + +### F1 关内深度坡度(D 轴小坡度) + +**结论**:hp_D = round(hp_0 × (1 + s_hp × D))——每档 +5%–20%,Dmax=4 处 1.2–1.8 × D0、Dmax=5 处 1.25–2.0 × D0(同敌种内);MVP 直填行近似此式、不运行时计算;敌种切换(新敌种登场)不受本式约束——台阶值直接定。 + +#### 推导 + +``` +hp_D = round(hp_0 × (1 + s_hp × D)) +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 深度基准值 | hp_0 | int | [1,999] | 该敌种 D0 行 hp | +| 坡度系数 | s_hp | float | [0.05, 0.20] | 每深度档增幅 | +| 节点深度 | D | int | [0, Dmax] | 节点深度(Boss = Dmax) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Example | goblin 24hp:D1 理论 24×1.1 ≈ 26——切片实际以"群×2"(13hp ×2–3 只)表达深度压力:数量是比单体质变更优先的深度表达(保持单拍环低威胁读数、总量受控) | + +### F2 关间台阶(L 轴大台阶) + +**结论**:base(L+1) ≈ base(L).Dmax × k_L、k_L ∈ [1.3, 1.6]——k_L 作用于**同敌种基准**(敌种延续场景,如 L2 bugbear 延续至 L3);新敌种登场时基准按其环复杂度重定(三拍环单位 hp 低于单拍环同关值——切片先例:bugbear 40/18 < goblin 群总量但威胁更高)。 + +#### 推导 + +``` +base(L+1) ≈ base(L).Dmax × k_L k_L ∈ [1.3, 1.6] +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 关间系数 | k_L | float | [1.3, 1.6] | 下一关 D0 基准 = 本关 Dmax × k_L(数值跳升 + 新敌种/新变异共同构成台阶感) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Output Range | 相邻关数值倍率 1.3–1.6;叠加新敌种环复杂度,实际体感台阶更大 | +| 反例 | L1 Dmax 酋长 150hp → L2 D0 bugbear 基准 ≈ 210 的直推不成立——新敌种登场基准按环复杂度重定,k_L 仅适用于敌种延续场景(L2 锚行 30/7 为内容行非 k_L 直推) | + +### F3 Boss 血量锚 + +**结论**:boss_hp ≈ round(Σ普通战斗hp期望 × r_boss)、r_boss ∈ [0.5, 1.1]——"Boss 是本关最大的单场消耗",量级上探至全关消耗;**加难走廊 [1.1, 1.35] 预登记**(带锚定稿——L1 通关率压带首选杠杆,扫描终裁后落带)。 + +#### 推导 + +``` +boss_hp ≈ round(Σ普通战斗hp期望 × r_boss) r_boss ∈ [0.5, 1.1](走廊 [1.1, 1.35]) +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 本关普通战斗 hp 期望总和 | Σ普通战斗hp期望 | float | [1,999]×N | 本关全部非 Boss 节点的敌方 hp 期望合计(**含 unit_count 区间骰期望**——口径与击杀侧恒等式统一) | +| Boss 占比 | r_boss | float | [0.5, 1.1](走廊 [1.1, 1.35]) | Boss 相对全关消耗的血量锚 | + +**验算与边界:** + +| 验算 | 读数 | 判定 | +|---|---|---| +| L1 v3:Σ = 36(D0 [1,2]×24)+ 32.5(D1 [2,3]×13)+ 40(D2)+ 45(D3 [2,3]×18)= 153.5 → Boss 150/153.5 ≈ **0.98** | 带内 | ✓(口径更正:旧验算 Σ=142 为固定数量口径与旧 Boss 值混算,由本行替代;口径与击杀侧恒等式统一) | +| 走廊验算 | Boss 180/153.5 ≈ 1.17、200/153.5 ≈ 1.30 ∈ [1.1, 1.35] | 走廊内 ✓ | +| 败北分布 | 向 Boss 集中(4/6) | 符合压迫预期 ✓ | + +### F4 生存侧:期望承伤与 S 区间 + +**结论**:E[承伤/场] ≈ Σ_rounds Σ_enemies E[beat_dmg] × (1 − E[减伤] − weaken覆盖);对照带 ≤20%/普通场、≤50%/Boss 场。派生**生存区间** S = [(HP进战 + 治疗下界) ÷ 入伤峰值, (HP进战 + 治疗上界) ÷ 入伤谷值]——区间模型三档判定的生存侧输入。 + +#### 推导 + +``` +E[承伤/场] ≈ Σ_rounds Σ_enemies E[beat_dmg] × (1 − E[减伤] − E[weaken覆盖]) +S = [ (HP进战 + 治疗min) ÷ 入伤峰值 , (HP进战 + 治疗max) ÷ 入伤谷值 ] +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 每拍期望伤害 | E[beat_dmg] | float | [0,999] | 敌 atk × 拍别乘数(heavy=2)+ 伏击 flat(+3×层数,帽 4)——行为环确定性轮转 | +| 减伤期望 | E[减伤] | float | [0,0.6] | defense_up 授予率 × 当次减伤(−30×2 层帽 −60%——减伤墙批现行值) | +| weaken 覆盖 | E[weaken覆盖] | float | [0,0.65] | 标中旗 −65% 输出对战斗轮的覆盖期望——**生存侧最大单一杠杆**(P3 兑现点) | +| 入伤峰值/谷值 | d_max / d_min | float | [0,999] | 峰值 = heavy 齐射尖峰 + 伏击满层、零减伤;谷值 = charge 拍 + 全减伤 | + +**验算与边界:** + +| 验算 | 读数 | 判定 | +|---|---|---| +| D0 goblin ×1(7atk 单拍环)每结算恒 7 × (1−0.22 平均减伤) ≈ 5.5/轮 → 裸 R_survive ≈ 50/5.5 ≈ 9 轮 | — | L1 五战 S 区间手算见区间模型验算表 | +| 减伤墙批注 | defense_scan 实测墙轮 39%、真人 12/12 轮零承伤满血通关 → defense_up −50→−30、石肤权重 24→16 | 承伤带复验责任随批(B3 侧注记已挂) | + +### F5 通关率目标带(期望表核心) + +**结论**:通关率按 (L, 天赋档) 目标带验收——**L1 基线 30–50%**(带锚定稿,替换原 40–60%);区间机制解释:+100% atk → K 区间近似减半 → K_max ≈ S_min 转绿档 → ≥95%。**真人方向更正**:批次 1 实测真人显著强于模拟者(v2 真人 100% vs 模拟 80%)——原"真人预期低于模拟"方向声明由本结论更正;带按模拟口径定义,真人系数批次 2 标定(OQ3)。 + +#### 推导 + +**Variables(目标带):** + +| 目标带 | 基线(无天赋) | 中天赋(+100% atk) | 高天赋(+200%) | +|---|---|---|---| +| L1(首关) | **30–50%** | ≥95% | 100% | +| L2 | 20–40%(provisional,随 OQ1 校准) | 60–80% | ≥90% | +| L3+ | 10–25%(provisional) | 40–60% | 60–80% | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| v3 扫描 | 基线 60%——**越 [30,50] 上界** → 加难杠杆批内即调(带锚定稿杠杆:r_boss 走廊 + 群战上探 + decay 0.6 候选);扫描落带前登记 provisional-pending-scan(OQ7) | +| 完美策略档扫描(OQ7;工具 = smoke/auto_player.gd 完美档求解器 + level_monte_carlo 策略轴) | **区间批后复测(2026-09-14,N=30)**:0 加成通关仍 **100%**、剩余 HP 均 47.4/50、承伤均 3.9(区间前 3.5)、踩雷 0.00/局——+50%~+200% 恒 100%;**承伤带与 F5 带对技能轴上界零分辨力**(calibration_gap 定量读数)。实测结论:**奖励侧 + 敌方侧数值区间已全部落地仍压不动完美档**——其结构性优势(零踩雷直伤 + 全标旗 weaken 顶格 + 快杀)使 47/50 血缓冲吞掉全部值域方差;≤40% 验收线需结构性杠杆(承伤硬地板 / weaken 饱和语义 / 完美度定价)或极端宽度(毁基线与可读性,建议否决)——**待用户裁决(OQ7 待拍板主项)** | +| 败北分布 | 向 Boss 集中(4/6)✓;+200% 天赋 100% ✓ | +| 机制钩子 | 天赋列由区间位移承载(K 减半 → 绿档),非独立拍脑袋带 | + +### F6 生存链区间模型(替代关级供给带) + +**结论**:原关级供给带(比值 ∈ [1.1, 2.0])由本模型替代——其验算 0.54"短缺态"与实测 60% 通关矛盾,方向指引反指(对偏易的游戏报"加供给")。现行 = **生存链区间模型**:每战 S/K 区间三档判定(绿=稳定通过 / 黄=期望通过 / 红=配置事故禁止入表)+ 关级通关恒等式 + 输出占比带。设计期工具式,运行时零公式。 + +#### 推导 + +``` +S = [ (HP进战 + 治疗min) ÷ 入伤峰值 , (HP进战 + 治疗max) ÷ 入伤谷值 ] (生存区间——F4) +K = [ (Σ敌hp − 一次性伤害max) ÷ 输出峰值 , (Σ敌hp − 一次性伤害min) ÷ 输出谷值 ] (击杀区间) + +绿档: S_min > K_max —— 最坏也能过,与运气无关(教学位必须绿档) +黄档: E[S] ≥ E[K] + 1 且非绿 —— 有失败分支,期望占优(margin ≥ 1 轮最低阈值) +红档: S_max ≤ K_min —— 最好也打不过,配置事故(F7 第③步拦截) + +关级恒等式: atk × Σ R_survive(i) + Σ奖励输出 ≈ Σ敌hp期望 × (1 + 缓冲) + (actuals 逐项审计闭合——五项输出 × 三项输入全部入账) +``` + +**Variables:** + +| Variable | 来源 | Description | +|---|---|---| +| 入伤 | 表值 + actuals | 敌 atk × 拍别乘数 + 伏击 flat − 减伤 − weaken(F4) | +| 输出 | 表值 + actuals | 基础攻击 6×(1+attack_up) + 伤害A(开格充能 1/格 帽 8,甲A ×1.5/甲B ×0.5)+ 伤害B(标雷充能 2/雷,甲B ×1.5/甲A ×0.5)+ 刀(8/16,不吃 percent) | +| 一次性伤害 | 表值 + actuals | clear_blast 40×清图数(完美 ×2)、囤刀倾泻 | +| 治疗 | 表值 + actuals | heal_pack 12 + heal_minor 4(注入/格子池期望) | +| 数量骰 | 表值 | unit_count 区间 [unit_count, unit_count_max] | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| L1 五战区间手算(≈ 估算,p10–p90 口径) | D0 goblin×1(教学位固定):S≈[7,12] vs K≈[2,4] → **绿** ✓ · D1 ×[2,3]:S≈[4,10] vs K≈[2,6] → 黄 · D2 bugbear(首遇固定):S≈[4,12] vs K≈[2,6] → 黄 · D3 ×[2,3]:S≈[2,6] vs K≈[2,7] → 黄偏红 · Boss 酋长:S≈[4,15](旗覆盖率主导)vs K≈[7,18](囤刀+甲B 对策)→ 黄红边缘 | +| L1 形态带 | ≥1 绿(教学位)+ 中段黄 + Boss 黄不红——手算形态符合 ✓ | +| 关级恒等式漏项审计 | 原供给 180 的漏项:clear_blast 只算一次(实际 ×清图数 3–4 = 120–160)、伤害A/B 充能、attack_up 增量、RAP 刷新新板追加供给——逐项入账后闭合,"0.54 短缺"消失 | +| 输出占比带 | 奖励侧输出(刀+clear_blast+A/B 充能)÷ 总输出 ∈ **[15%, 40%]**——<15% 奖励摆设、>40% 供给依赖(AC-14) | +| 口径注记 | 区间两口径:公式包络(必过/必死结论)与 actuals p10–p90(日常判定——B1 actuals p10 纪律同构);HP 持久化耦合各战区间,联合分布由 monte_carlo 裁决 | +| C4 平移换算率 | +10hp ≈ +1 轮(÷d̄≈8–10);atk 翻倍 ≈ K 减半——区间位移机制承载通关率天赋列,非独立拍带 | + +### F7 调参流程(每次修订强制走完——七步) + +**结论**:改表值 → smoke_all 全绿 → level_monte_carlo 扫描(通关率带 + 三档判定 + 通关余量 + 占比)→ B4 登记常数与对账重制 → 对账复算(条款③⑤/预警⑥)→ README 调参日志登记——任一步 FAIL → 回滚或登记 OQ,禁止带病前进。 + +#### 推导 + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 批量样本量 | N | int | ≥256(正式)/ 15–30(切片临时) | 正式批量工具 = level_monte_carlo 扩展(逐行聚合 + N 参数化 + 区间分位/降级统计)——**扫描执行本体归 A1 F4 工具交付(A1 OQ1),C3 的 level_monte_carlo 扩展为消费方**(所有权澄清:A1 工具产出 actuals → C3 扫描消费,非替代) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Output Range | 任一步 FAIL → 回滚或登记 OQ,禁止带病前进 | +| Example | 切片 d0.28 回退先例:d0.28/t0.87 → best≈0.81 降级告警 → 回退 0.25/0.88——第 2 步(A1 生成质量闸)拦截的成功案例 | +| 第④步依据 | B4 义务 #10:"C3 调参流程若无'登记常数与对账重制'步骤则相关 AC 假失败"(T_fog_min/K_dmg/N_draft/P_min/默认根行值调参 → B4 AC-19 对账对 (knife_adv, attack_up_basic) 漂移检测、AC-6 分支 4、AC-18 重跑) | + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| 未调参行 | 新增 (L,D) 行仅有结构占位 | 经七步流程 → 已调参 | +| 已调参行 | 行值经批量仿真 + 期望表对照 + 对账复算 | 任何修订 → 重走七步流程 | +| SLICE-TUNED 转正 | 切片临时值经本 GDD 背书(行值总表 v3) | → 已调参(切片行集转正条款) | +| provisional-pending-scan | 加难杠杆候选值已登记、扫描步未执行 | 扫描落带 → 已调参;越带 → 回滚候选 | +| 期望表偏离 | 调参后期望值越出目标带 | → 回滚或 OQ 登记(禁止静默出带) | + +不变式:C3 无运行时状态机(数值系统);设计期任一行值恒处于上表五态之一;期望表偏离禁止静默出带。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| unit_count>1 行与高密度板组合 | 条款③满配语境破线(B1 F4 双轴约束)——该组合**禁止入表**;切片先例 = 群战行 hp/atk 下调补偿;对账复算在流程第 5 步强制执行 | Core Rule 9① / B1 F4 | +| 关卡深度轴缺行(如 D3 缺失、D4 存在) | **调参期冒烟断言拦截**(跨表存在性检查——C3 调参工具域载体;D2 R1 fail-fast 语义不动);R1 floor 解析静默兜底至低档(难度降而非崩)+ ConfigDB warning 四元组仅作运行时兜底——缺行根因是调参事故而非运行时事件 | AC-1 / D2 R1 | +| 请求越界关卡(level 999) | R1 floor 解析至表尾行(难度封顶 = 尾行值)+ warning——冒险结构(C2)应在其内容覆盖范围内生成关卡序列;越界 = 内容生产缺口,非玩家可达态 | D2 R1 | +| 玩家天赋拉满(atk+200%)打最低难度行 | 速杀退化(预警⑥——战斗 1–2 轮终局、奖励系统饿死自己的高光);处置 = 预警⑥仪表监控(战斗结束未开资格格占比 >70% 警)+ 接受——低难度行被高天赋速杀是 Roguelike 成长兑现的合法形态,无需修复(区间形态:K_min < 2 = 预警⑥观测位) | 区间模型 / 预警⑥ | +| 密度上探压垮推理率(d0.28 案例) | best inferability ≈0.81 < target 0.87 → 降级告警"接受最优出图";处置 = **回退**(切片先例:0.28/0.87 → 0.25/0.88)——密度与 target 是联动对(A1 求解器能力的天花板内联动调参),禁止为难度牺牲 P1 度量达标;流程第 2 步闸拦截 | F7 / A1 F4 | +| 条款⑤清图支配性反转(弃图率 >30%) | 玩家普遍发现"挖一半就弃图刷 RAP"优于"推到底清图"→ 清图奖励与清图体验被支配;处置 = OQ4 playtest 观测(自动模拟者不弃图,测不到真人策略);届时杠杆 = C 量级上调(B1 约束包①,现值 clear_blast 40)或弃图成本重定价(A3 域)——MVP 接受当前非支配设计态(缺口 6.1/51.8,实测后再裁) | B1 F5 / OQ4 | +| 投放节奏断档(某关无新敌种且无新环段) | OQ8 违例——敌信息总量见底,学习曲线枯竭;处置 = 流程投放检查项(每关至少一项新内容——敌种投放节奏条款);违规 = 内容生产缺口,C3 调参无法修补(数值不是内容) | B2 OQ8 / AC-6 | +| 全部行值调至域上限(hp 999 等) | 数值可行但可玩性崩塌;处置 = 通关率带对照拦截(期望表全面越带 = 配置事故)+ 域校验既有护栏;本场景属调参事故而非玩家可达态 | F5 / D2 域校验 | +| DifficultyTable 修饰行与直填行值叠加后越域(首个精英节点上线时) | B2.spawn 套乘后 hp >999 → **clamp 至域界 + warning**(fail-fast 不适用——修饰乘数是合法配置,越界是组合事故;域界 = 玩法硬顶);MVP 空表无此场景,规则预登记 | DifficultyTable 乘数域 | +| 通关余量越带 | p90 >13(赢得从容)→ 生存侧杠杆优先(治疗稀有度下调 / 敌 atk 上探),击杀侧杠杆次之;p10 <3(1–2 血惨胜成常态)→ 治疗供给上浮或 Boss 前恢复点增益——余量是形态指标,与通关率带联合判读 | AC-13 / 区间模型 | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | 上游(硬) | 全部基表 schema、R1 解析、ConfigDB 单点读取归 D2;C3 产出 = 行值;DifficultyTable 新表走 D2 接口漂移规则(registry `difficulty_table` 已登记);禁绕过 ConfigDB 自建 load(D2 Rule 2);RngService `encounter` 封闭流(registry 已登记);**EnemyTable schema +2 字段(unit_count_max、armor_type)——接口漂移已回写 D2** | +| B2 | 上游(硬) | EnemyTable/HeroTable 行值由 C3 填(B2 拥有字段语义与校验域);修饰施加点 = B2.spawn(实例化时套乘、行值快照之前);**spawn override 参数 + unit_count_max 字段语义 + 物种集 {goblin, bugbear, ogre_chief}——接口漂移已回写 B2**;投放节奏 OQ8 共担;多敌种同深度选择规则共裁(OQ7——C1 联动) | +| B1 | 上游(soft,事实依赖) | 条款③⑤对账输出 = 调参约束输入(B1 F4/F5 = 约束生成器);P_perfect 乘数调参权归 C3(B1 登记);**begin_battle encounter 掷骰增补 + 护甲克制结算(armor_effectiveness/damage_a/damage_b 字段)——接口漂移已回写 B1**;期望口径双读数纪律(锚行读数不得用于 unit_count=2 放行);对账复算义务随每次修订 | +| B4 | 上游(soft) | RewardTable/RewardConfigTable 参数值归 C3;**供给控制器机制本体归 B4(转正登记——首件保证 + 指数递减注入,active_p0/active_decay 参数归 C3)**;clear_blast 换位 + K_dmg=0 转正(B4 保底语义迁移);敌 hp 曲线与奖励供给禁止独立调参(registry 联动重算义务——区间模型恒等式为联合检算工具);调参期批量仿真义务载体 | +| A1 | 上游(类型依赖) | BoardGenTable 行值归 C3(含 max_gen_attempts 策略);禁反向调用生成(仅经 D2/编排器传值);F4 批量红线(严格零降级)= 调参闸;**F4 全量批量工具 = A1 交付物(A1 OQ1),C3 level_monte_carlo 扩展为消费方**;d0.28 回退先例 = 密度×target 联动调参的实证;黄金向量/solver_rules 冻结不经 C3 | +| A4 | 上游(soft) | scope=global 语义由本 GDD 定稿(Core Rule 7);unlock_level 门控序列投放计划归 C3(随关卡扩展);**AC-11 未来腿 = elite/boss 标记字段落地时 A4 warning 升闸——已登记为 A4 接口漂移义务** | +| A3 | 上游(soft) | ActionEconomyTable 联动观测(ap_per_round 双重身份——节奏兼 RAP 转存规模)——**联动评估归 C3,调参所有权归 A3**(措辞更正:A3 GDD 无调参权授予回执);跨字段校验(动作成本 ≤ ap_per_round)约束内观测 | +| B3 | 上游(soft) | StatusEffectTable 四行强度(attack_up +50 / defense_up −30 / weaken −65 / ambush +3)provisional 归 C3 联动评估(B1 免伤轮合法性注记——强度评估归 C3 对账联动);减伤墙批(−50→−30)为 B3 侧行值批,C3 承伤带复验随批 | + +**下游**: + +| 系统 | 接口要点 | +|---|---| +| C1 | 节点深度语义消费;精英节点形态联动(精英节点预留条款 / A1 定稿强制重审);未设计——Boss=Dmax 约定即为其预留的接口 | +| C2 | 关卡序列的难度排布消费本 GDD 期望表;越界关卡 = 内容生产缺口(边界情况表);未设计 | +| C4 | 下游(远)——元进度天赋数值的"世界侧对照物":通关率目标带(F5)是 C4 天赋强度的标定基准;**区间平移换算率(+10hp ≈ +1 轮、atk 翻倍 ≈ K 减半)为 C4 设计输入**;未设计 | +| 呈现层(B5/D1) | Boss 登场仪式感 / 难度台阶可感知性——视觉义务归 B2/B5,C3 仅提供"何时变难"的数据(见呈现与 UI 契约) | + +**双向一致性核对(registry / systems-index 回执):** + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| X-1 | systems-index 依赖表 C3 ← D2,B2;B1 块增补"C3 消费 B1 对账输出的事实依赖成立" | systems-index | ✅ | +| X-2 | "敌种 hp/atk/pattern 曲线数值、难度修饰 → C3" | B2 | ✅ | +| X-3 | "C3 = 条款③⑤约束输入方" | B1 | ✅ | +| X-4 | "权重/档位/威力曲线 + RewardConfigTable 参数归 C3" | B4 | ✅ | +| X-5 | "每行难度数值与曲线 → C3" | A1 | ✅ | +| X-6 | B1:begin_battle encounter 掷骰增补 + 扫描 scope 腿 + B1"零随机"边界扩界注记(encounter 流在结算步流之外) | B1 | ✅(本次修订批回写) | +| X-7 | B2:spawn override 参数、unit_count_max 字段语义与实例化记号调和、物种集 {goblin, bugbear, ogre_chief} | B2 | ✅(本次修订批回写) | +| X-8 | D2:EnemyTable schema +2 字段(unit_count_max、armor_type);SettlementTable +4 字段(damage_a_per_cell/damage_a_cap/damage_b_per_flag/armor_effectiveness) | D2 | ✅(本次修订批回写) | +| X-9 | B4:供给控制器机制承接 + clear_blast 换位 + K_dmg=0 禁区条款解除(保底语义迁移) | B4 | ✅(本次修订批回写) | +| X-10 | A4:elite/boss 标记字段落地时 warning 升闸(AC-11 未来腿) | A4 | ✅(义务登记) | + +**对账与义务登记(B1 对账输出承接——审计资产):** + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 条款③ | 满配破线两轴:unit_count>1 行须满足"降密度或降 m_amb"补偿(B1 F4 双轴约束)——registry clause3_supply 登记在案 | B1 / registry | 已登记——切片群战行 hp/atk 下调为合规先例 | +| 条款⑤ | 清图支配性:46.1/91.8 需求 vs **clear_blast C=40 现值(缺口 6.1/51.8,对 clear_blast 重推)**——登记为非支配设计态;预留杠杆 = C 量级上调(B1 约束包①)或弃图成本重定价(A3 域) | B1(B1 F5) | 已登记——OQ4 playtest 持续观测弃图率 | +| 预警⑥ | 快杀退化:战斗结束未开资格格占比 >70% 警——高天赋速杀低难度行 = 合法形态,仪表监控不修复;区间形态钩子 = K_min < 2 观测位 | B4 / B1 | 已登记 | +| unit_count 约束 | 区间 [unit_count, unit_count_max] ⊆ [1,3];>1 行联动条款③复算(流程第 5 步强制) | B1 / B2 | 已登记 | +| 期望口径双读数 | 锚行读数(2.0×/2.6× 类)不得作为 unit_count=2 的放行依据 | B1 | 已登记 | + +## 呈现与 UI 契约 + +> C3 无自有视觉/音频/UI 资产(数值系统)——义务转注如下;无新增资产需求、无 D1 新增义务登记。 + +| # | 义务 | 归属 | +|---|---|---| +| 1 | Boss 登场呈现(仪式感锚点——体验锚点"压迫的顶点");设计输入 = Boss=Dmax 行的数据面(本 GDD 提供) | B5 战斗表现 / D1 | +| 2 | 难度台阶可感知性:新敌种 = 新图像线索 = 台阶的可视面孔(意图推测型——图像线索档位编码,"这只比上层的硬"可读) | B2(D1×4 义务) | +| 3 | 变异解锁的"这一层有雾了":图例与首遇提示(已登记义务) | A4 / D1 | +| 4 | 期望表 / 批量仿真输出 = 调参期开发工具(headless——非游戏内 UI) | 开发工具 | +| 5 | 恢复点/奖励点呈现:C1-lite 切片已实现的节点覆盖层;正式形态归 D1/C1 | D1 / C1 | + +## Tuning Knobs + +> C3 是全项目 knob 保有量最大的系统(数值 owner)。全部值 provisional 起步;域与校验归各表 schema(D2);本表只登记所有权、联动与危险方向。 + +| Knob | 表 | 域 | 联动 | 过高 | 过低 | +|---|---|---|---|---|---| +| EnemyTable.hp/atk(逐行) | enemy.tres | [1,999] / [0,999] | **与 B4 供给禁独立调参**(区间模型恒等式联合检算);cap×atk 禁独立(B2) | 打不死、战斗冗长 | 快杀退化预警⑥ | +| EnemyTable.unit_count / unit_count_max(逐行) | enemy.tres | [1,3] / [unit_count,3] | **>1 行须降密度或降 m_amb**(条款③);区间骰经 encounter 流 | 条款③破线 | 压力不足/遭遇零随机 | +| EnemyTable.pattern(逐行) | enemy.tres | MVP 三值组合 | OQ8 投放节奏(每关新环段) | 信息过载 | 学习曲线枯竭 | +| EnemyTable.armor_type(逐行) | enemy.tres | {none, a, b} | **克制矩阵联动**(settlement armor_effectiveness);伤害A/B 充能消费面(B1 域,C3 投值) | 克制失配 | 特化对策失效 | +| HeroTable.atk/max_hp | hero.tres | [1,999] | 全部输出式的分母锚(F4/F6) | 奖励价值稀释 | 挫败感 | +| BoardGenTable 尺寸/密度/attempts(逐行) | board_gen.tres | 域见 schema | **密度×target 联动**(d0.28 回退先例);unit_count>1 行联动 | 推理率不达标降级 | 板退化(低密度先布后删) | +| BoardGenTable.inferability_target(逐行) | board_gen.tres | **[0.85,1] 绝对锚** | 与密度成反耦 | 生成耗时/降级 | P1 空心化 | +| BoardGenTable 雾参数 | board_gen.tres | f≤8d / ≤0.25 | unlock_level=2 锁定 | P1 度量空心(绝对上限) | 雾内容缺位 | +| RewardTable.weight/power/tier(逐行) | reward.tres | 域见 schema | 区间模型恒等式;攻击面稀释;clear_blast 单行槽 | 即时类淹没明细/可选类贬值 | 窗口空转/重 roll 频发 | +| RewardConfigTable.{t_fog_min,k_dmg,n_draft,p_min} | reward_config.tres | 域见 schema | k_dmg=0 = 保底断言关闭(禁区条款已解除——保底语义由首件保证取代) | 保底永不触发 | 种子轮盘致命 | +| 供给控制器 active_p0/active_decay | reward_config.tres | [0,1] / (0,1] | E[战斗获取]≈1 目标(B4 机制本体承接);**与敌 hp 联动** | 速杀+囤积回归 | 可选类绝育 | +| SettlementTable.{damage_a_per_cell,damage_a_cap,damage_b_per_flag,armor_effectiveness} | settlement.tres | 域见 schema | Boss 甲B 对策 = B 充能;A 克制甲A;联动网络 #6 | Boss 裸打过慢 | 充能失效 | +| ActionEconomyTable.{ap_per_round,rap_refresh_cost,refresh_max_per_round} | action_economy.tres | 域见 schema | ap 兼 RAP 转存规模;**调参所有权归 A3,C3 联动评估** | 轮次冗长 | 决策密度坍缩 | +| SettlementTable.P_perfect(乘数) | settlement.tres | {100,200} | 完美排雷×清图支配性互 feed | 钓鱼线(护栏 B) | 高光缺位 | +| DifficultyTable 修饰乘数(MVP 空表) | difficulty.tres(待建) | [0.1,10](registry 预登记) | 精英节点上线时启用 | 越域 clamp+warning | 无修饰 | + +### DifficultyTable schema(difficulty.tres 待建——D2 接口漂移建表) + +| 字段 | 类型/域 | 默认 | knob | +|---|---|---|---| +| level_index + node_depth(复合键) | int × int | — | ✗(结构键) | +| 修饰乘数 | float [0.1, 10](registry 预登记域) | —(MVP 空表无行) | ✓(精英节点上线时启用;越域 clamp + warning——域界 = 玩法硬顶) | +| elite_ / boss_ 前缀预留字段位 | 命名空间预留(D2 schema 断言期望暴露) | 无 | ✗(预留,随精英节点语义定稿) | + +### 联动网络(禁止独立调参清单) + +| # | 联动 | 纪律 | +|---|---|---| +| 1 | 敌 hp ↔ 奖励供给(registry 已锁) | 单边移动必走区间模型恒等式联合检算 | +| 2 | unit_count ↔ 密度/m_amb(条款③) | 群战行上线前的强制复算 | +| 3 | 密度 ↔ target ↔ attempts(A1) | d0.28 回退先例的三元组 | +| 4 | ap_per_round ↔ RAP 体系 ↔ refresh 上限(A3 + 轮内刷新批) | 跨字段校验闸承载 | +| 5 | m_w(weaken −65)↔ 锚对照表(B1 F0) | 终值共裁 B4/B1,调参走 C3 提案通道 | +| 6 | armor_type ↔ 伤害A/B 充能 ↔ 克制矩阵(B1 域) | 敌种甲型变更须复算 Boss/普通战 K 区间 | + +## 验收标准 + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | LevelTable 任一 battle/boss 节点 (L,D);ConfigDB 加载全部表 | 调参期冒烟断言逐节点查两表 | board_gen 与 enemy 两表存在该 (L,D) 精确行(跨表护栏⑧转正——载体 = C3 调参期冒烟断言;运行时 floor 兜底属 D2 R1 既有语义) | +| AC-2 | MVP 全部 (L,D) 行 | 逐行扫描 inferability(批量 N=256/行——A1 F4 工具交付后的正式红线) | 全部行 inferability ≥ target ≥ 0.85(P1 绝对锚)且 A1 断言族(退化板/R₀≠S)零违例 | +| AC-3 | EnemyTable 全部 unit_count>1 行 | 条款③复算(密度 × m_amb 组合) | 无破线组合存在(Core Rule 9①) | +| AC-4 | MVP 全部行集(区间模型) | level_monte_carlo 逐战 S/K 区间扫描 | **零红档组合**(S_max ≤ K_min 禁止入表);教学位绿档;L1 形态带 = D0 绿 + 中段黄 + Boss 黄不红(F6 三档判定) | +| AC-5 | 期望表目标带(F5) | level_monte_carlo 全带扫描(基线/+100%/+200%) | L1 基线通关率 ∈ [30, 50](现值 60% 越上界——加难杠杆批内执行,扫描落带前 provisional-pending-scan);越带须 OQ 登记——禁止静默出带 | +| AC-6 | L1 全部行集 | 枚举敌种与环段 | 每关至少一项新投放(新敌种或新环段——OQ8;L1 = goblin→bugbear→ogre_chief 三段 ✓) | +| AC-7 | Boss 行(Dmax) | F3 检算(期望口径 Σ=153.5) | r_boss 记录在案(现值 0.98 带内 ✓;越带须 OQ 登记——加难走廊 [1.1,1.35] 走 OQ2/扫描) | +| AC-8 | 每轮刷新 | 计数达 refresh_max_per_round | 第 N+1 次拒绝(表值驱动——A3 域行为,C3 侧验收 = 联动评估记录存在) | +| AC-9 | 任何 C3 直辖 knob 修订 | 检查其联动网络(Tuning Knobs 六组) | 对应联合检算记录存在于调参日志(禁止独立调参纪律的流程面) | +| AC-10 | DifficultyTable MVP 空表 | B2.spawn 任一 (L,D) | 无修饰行被消费(结构位不产生行为)且未来首行上线时套乘点 = 行值快照前 | +| AC-11 | scope=global 变异行 | MVP 腿:扫描 VariationTable 全行 | MVP 无 global 行存在(表级约束)且 A4 warning 通道在位;未来腿:elite/boss 标记字段落地时加载/生成期拒绝(A4 接口漂移义务——X-10) | +| AC-12 | 每次调参批 | 检查 README 调参日志 | 七步流程的执行记录齐备(流程纪律的可审计面) | +| AC-13 | 通关余量(新指标) | level_monte_carlo 通关轨迹采集 | E[HP_end \| 通关] 的 p90 ≤ 13 且 p10 ≥ 3("只剩一点血"量化带——越带按边界情况表杠杆处置) | +| AC-14 | 关级恒等式闭合 | actuals 逐项审计(流程第③步输出) | 输出侧五项(基础攻击/刀/clear_blast/A+B 充能/attack_up)与输入侧(承伤/治疗/减伤)合计闭合 Σ敌hp期望×(1+缓冲),残差 <5%(provisional 阈值)且奖励输出占比 ∈ [15%, 40%] | + +## 开放问题 + +| # | 悬案 | 进展 | owner | 时机 | +|---|---|---|---|---| +| OQ1 | L2+ 曲线数值——本 GDD 定结构/流程/目标带;具体数值行随关卡内容扩展填充(内容与数值同批——C2 关卡序列定稿后) | 结构已定 | C3 随批 | L2 内容扩展时 | +| OQ2 | Boss 血量占比——v3 记录值更正 0.98(153.5 期望口径,带内 ✓);加难走廊 [1.1, 1.35] 预登记(Boss 180–207 候选);真人实测 Boss 战时长后终裁 | 走廊预登记 | C3 | F7 扫描 + playtest 批次 2 | +| OQ3 | 真人修正系数——批次 1 方向确认:真人显著强于模拟者(v2 真人 100% vs 模拟 80%);F5 方向声明已更正落地;真人系数待 v3 试玩采集定量 | 方向已定 | C3 + playtest | playtest 批次 2 | +| OQ4 | 条款⑤支配性真人观测——批次 1 初步裁决:真人零弃图倾向 → 非支配设计态成立、C 量级不上调 ✓(B1 约束包②采信);**P1 对冲弱化同步声明(B1 ② 条件):本裁决弱化 A3 条款⑤对 P1 失败模式的经济对冲,弃图率观测加权监控随之**;维持登记:单一样本 | 初步裁决 | C3 + B4 | 持续 | +| OQ5 | 精英节点形态——完整语义(独立节点/词缀/出现规则)= C1 分支地图定稿后的联动设计(A1 定稿强制重审随之触发) | 未设计 | C3 + C1 | C1 GDD 定稿 | +| OQ6 | 轮内刷新对难度模型的冲击——批次 1 初步读数:刷新零使用,根因是盲猜墙不足(inferability 0.88);杠杆预登记:密度联动上探(d0.28/t0.85——A1 回退先例风险);保持开放 | 初步读数 | C3 | playtest 批次 2 | +| OQ7 | **加难杠杆扫描执行 + 完美策略档测量**——杠杆候选(r_boss 走廊 180–207 / 群战上探 / decay 0.6)已预登记;执行 = 七步流程第③步 level_monte_carlo 扫描(三档判定 + 通关率带 + 通关余量 + 占比四表对照)。**2026-09-14 完美策略档落地(用户拍板)**:零盲挖/全标旗/卡死刷新技能轴上界测量仪(godot 本机可用已验证)。**区间批已落地并复测(同日拍板②——奖励五行 power 区间 + 敌方 atk 区间 ±3)**:完美档 0 加成仍 100%(承伤 3.5→3.9)——**数值区间杠杆族对 ≤40% 验收线无效已实测坐实;主项待裁决 = 结构性杠杆三选**(承伤硬地板〔不可减免平伤,B1/B2 域〕/ weaken 饱和语义〔全标旗收益递减,B3/B1 域〕/ 极端区间宽度〔建议否决——毁基线与数字可读性锚〕);落带前 v3 值 provisional-pending-scan | 完美档已测;区间批无效性已证;结构杠杆待裁决 | C3 | 用户裁决后 | + +### 试玩批次记录 + +| 批次 | 日期 | 数值基线 | 读数 | 裁决产出 | +|---|---|---|---|---| +| 批次 1(用户本人) | 2026-09-12 | v2 | 全部满血通关(清图为主/优先标雷/不用道具三策略均通)——基线难度不足定量坐实;Boss 血量 OK 或可上调;刷新零使用;零弃图倾向;治疗包零使用(不掉血伴生);伤害道具无特色(暂保留) | v3 数值批(atk 4/4/6/4/7 → 7/5/9/6/9;D0 24 保持防秒杀、Boss 130→150)+ DND 命名批(goblin/bugbear/ogre_chief——用户指令)+ 护甲克制批(armor_type none/a/b + 克制矩阵,数值 provisional)+ F3 带修订 + OQ2/3/4/6 读数;v3 扫描:基线 60%、败北向 Boss 集中 4/6、+200% 100%;下一批 = v3 真人试玩(OQ3 定量 + OQ2 终裁) | +| 减伤墙批(defense_scan 触发) | 2026-09-13 | v3 + 减伤墙批 | defense_scan 实测墙轮 39%、真人局 12/12 轮零承伤满血通关——减伤墙过强定量坐实 | defense_up magnitude −50→−30(叠满两层 = −60 帽)+ 石肤权重 24→16(attack_up 升池内最高行)——B3 侧行值批,C3 承伤带(F4)复验责任随批 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v2 | 2026-09-14 | /design-review 修订批(8 blocking 核销 + 4 项用户拍板):**F6 供给带退役 → 生存链区间模型**(S/K 区间三档判定 + 关级恒等式 + 输出占比带 + C4 平移换算率——用户设计)· **带锚定稿**(通关率 [30,50] / 通关余量 p90≤13 & p10≥3 / L1 形态带)· F3 口径统一 Σ=153.5 / r_boss 0.98(旧验算 142 替代)+ 加难走廊预登记 · F4 升级生存侧(S 区间)· F5 带 [30,50] + 真人方向更正 · **MVP 行值总表 v3 转正**(切片 .tres 权威源,旧 SLICE-TUNED 表替代)· DND 更名生效(goblin/bugbear/ogre_chief)· **四批转正登记**(armor_type / K_dmg=0 / clear_blast 换位 / 供给控制器归 B4——用户拍板)· B1/B2/D2/registry 接口漂移回写(X-6~X-10)· F7 六步→七步(B4 对账重制步)· AC-4/5 重写 + AC-13/14 新增 · 条款⑤重推(C=40,缺口 6.1/51.8)· Boss atk 台阶重定义 · 减伤墙批补录 · OQ7 新增(扫描执行) | +| v1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;规则/枚举/边界情况/对账登记/AC 转表格;曲线生成与调参流程转字符画;公式、Variables 与验算数据全保留;F3 带按现行 [0.5, 1.1] 成文;语义零变更 | +| 试玩批次 1 / v3 数值批 | 2026-09-12 | 遭遇随机化引入(unit_count 区间语义 + RngService encounter 封闭流——Roguelike 局感引入批,Peglin 对比报告结论 2 的本切片落地项);F3 带修订 [0.35, 0.55] → [0.5, 1.1];v3 数值批(atk 7/5/9/6/9、Boss 130→150、D0 24 保持防秒杀)+ DND 命名批 + 护甲克制批;OQ2/3/4/6 读数登记(条款⑤非支配设计态采信) | +| 初稿 | 2026-09-12 | solo 模式出稿:九核心规则 + F1–F7 + 12 AC + 六 OQ;SLICE-TUNED 转正声明;Boss=Dmax 约定、DifficultyTable 结构位与 scope=global 语义定稿 | diff --git a/design/gdd/d1-hud-framework.md b/design/gdd/d1-hud-framework.md new file mode 100644 index 00000000..953cbd1b --- /dev/null +++ b/design/gdd/d1-hud-framework.md @@ -0,0 +1,681 @@ +# D1 HUD 与界面框架(HUD & UI Framework) +> **状态**:Designed · **版本**:v2 · 2026-09-14 · 作者:user + agents +> **承载支柱**:P1 主("我算对了"看得见的感知面)· P2 主(逐行归因的呈现半边)· P4(地牢氛围容器) · **层级**:Layer 4 Presentation · Milestone MVP(设计顺序第 10 位) +> **一句话**:D1 把 A1–B4 的全部玩法事实变成玩家看得见、点得动、归因得了的界面——零玩法状态的双层边界呈现框架。 +> **推演件**:`design/gdd/analysis/d1-hud-framework.md`(本文件只收结论,理由在那里) + +## 设计目的 +D1 是项目首个零玩法上游假设的表现层系统:战斗屏(MVP 唯一玩法屏,五区常驻双舞台布局,屏级 spec = `design/ux/battle.md`)+ 一族共享呈现设施——toast 提示体系、模态覆盖层家族、输入翻译、模态层级管理、面向呈现的信号载荷契约、ux 遥测。少了 D1,A2 的三事件流没有读者、B1 的流水协议没有呈现面、27 项上游呈现义务没有兑付点。 + +1. 「我看见的每一步都是真的」——四种格态一瞥可辨、每次拒绝可感知、结算逐行可归因(P1 感知面)。 +2. 「这个结果是我哪步扫雷带来的」——结算明细逐行兑现且每行带来源标注;四类未生效行不静默、零值行不被吞(P2 呈现半边)。 +3. 「挖掘期一块干净的玻璃,结算期一场丰收」——能量档转场由 D1 承载:挖掘 2/10 → 结算 7/10 → 清图 8/10 → 失败 1/10(P4 容器)。 +4. 「完美被加倍看见」——完美排雷全屏喝彩级反馈、标中旗与 H2 清图奖励金框行(高光节拍)。 +5. 界面绝不替玩家思考、绝不静默改判、绝不用装饰伪装逻辑差异——玩家对 D1 的正确感觉是"一块干净的玻璃"。 + +## 系统范围 +负责: + +- 五区布局落位与常驻组件呈现形态(战斗屏) +- 输入翻译(鼠标意图 → A2 意图对象)与单意图帧收集 +- toast 提示体系 / 图例面板 / 一次性提示 +- 模态覆盖层家族(图例 / 明细回看 / 胜负终局)与模态层级纪律 +- 13 项面向呈现的信号载荷 schema 与消费侧断言 +- 命令式回执刷新面(零事件外发动作的界面刷新契约) +- 程序化占位策略与 art-bible §4.6 键位映射表挂接 +- ux 遥测六行族(playtest JSONL `ux` 行) +- F1 呈现几何与 F2 可读性判据的执行 + +不负责: + +- 任何玩法状态与玩法数值(各玩法 GDD;数值表归 D2)——读经核心只读面、写经组合根 +- 合法性判定与机制语义裁定(A2 / A3 / B1 等上游系统) +- 屏级布局/组件/交互/动画时长的裁决权威(`design/ux/battle.md`) +- 美术裁决与终稿资产(art-bible / asset-spec 批);动画密度(B5) +- 音频(D4)· 教学内容(D5)· 本地化文本生产(D6——D1 仅消费契约) +- 任何随机消费(呈现层零 RNG) + +## 核心循环 +``` +玩家输入(D 区,仅挖掘期可交互;全部操作纯鼠标可完成——Gamepad Partial 非 MVP) + │ 左键已开格 = 移动 ─ 左键未开相邻格 = 挖 ─ 右键 = 插拔旗 ─ 停手按钮 = settle_batch([stop]) + ▼ +单意图帧收集(一帧内 collected 意图整批;stop 点击终结当帧收集;F4 语义序仲裁退为防御性兜底) + ▼ +组合根 ──► A2 settle_batch(intents) [唯一写路径;零直调 A3] + │ + ├─ 回执链(零事件外发动作:旗插拔 / 移动 / AP / RAP / 库存——规则 2 命令式回执) + │ settle_batch 返回 → 组合根立刻重查只读面(current_board / A3 getter / query_inventory) + │ → 视图 setter 只读注入 → 就地更新(字段级 setter,禁全量重建) + │ + └─ 事件链(A2 cell_revealed / flag_judged → B1 按轮缓冲) + ▼ + B1 结算编排(七段)→ 步骤流(13 类行协议——呈现层唯一订阅面之一) + │ + ├─ 明细带:逐行来源标注「{行为} {值} → {目标}(来源)」→ 84px 实时流(挖掘期收起为 24px 摘要) + ├─ 窗口锚点(授予段后、增益/减益段前):E 区原位激活(usable_now 投影;空库存 skip 不弹窗) + ├─ 高光:flag_hit 金框行 ─ clear_reward_h2 金框 ─ perfect 独立高光行(全屏喝彩级) + ├─ 状态栏刷新(B3 零信号外发):绑 B1 结算编排完成时点(命令式) + ▼ + 判定段 → 判定行(终局呈现数据锚)→ presentation_snapshot()(despawn 前捕获——规则 18①) + ▼ + 终局呈现(胜利金蜡封 / 失败暮蓝 overlay)→ 状态槽全清 + RAP 清零反馈(battle_over) + ▼ + level_clear(关卡末)→ 库存清空反馈「剩余道具 ×N」→ 下一节点 / 返回(runner 推进,写经组合根) +``` + +| 出口 | 触发 | 呈现动作 | 约束 | +|---|---|---|---| +| 继续挖掘 | settle_batch 回执、轮次未结束 | 命令式重查刷新对应面;明细带保持上轮摘要 1 行 | 每帧至多一批提交(单意图帧) | +| 结算展示 | `round_end`(A2 棋盘路径 / A3 stopped 直发——双发射源同构) | D 区锁输入(去饱和 + 金橙色相偏移、明度不变、数字保持全对比);明细带展开 84px 逐行兑现;阶段指示→"结算中";能量 7/10 | 呈现订阅面 = B1 步骤流;零直订 A2/A3 | +| 清图换图 | cleared 判定 | 金粒喷泉 + 彩纸 3–5s 即收;H2 清图奖励行金框强调;随后换新图 install(200ms 淡入) | 完美排雷高光叠加于清图瞬间之上 | +| 道具窗口 | B1 授予段完成(窗口锚点信号) | E 区原位激活(规则 13);选用/跳过 → 回结算流(结算阶段续) | 空库存直接 skip 不弹窗、结算零停顿 | +| 战斗终局 | 判定行 verdict=win / lose | 胜利 = 金蜡封 + 暖白金 overlay;失败 = 暮蓝 overlay(明度不变);终局数据全部来自流水行 + `presentation_snapshot()` | 出口 = 下一节点(win)/ 返回(lose)——写经组合根 | +| 关卡结束 | level_clear 编排 | 库存清空反馈「剩余道具 ×N 随关卡结束清空」(N = 清空前快照计数) | 与 battle_over 反馈不合并、时序不倒挂 | + +## 呈现边界与数据通道 +### 规则 1:界面只当翻译不当裁判——数值不写死、对错不自己判、核心数据不碰 +1. D1 显示任何玩法数字时,全部从上游系统只读接口现读现显;不写死玩法数值。 +2. D1 遇到"动作合不合法、结果算什么意思"时一律交上游裁定;只把裁定结果翻译成画面,不自行判对错。 +3. 玩家改变游戏状态的操作(挖格 / 插旗 / 选道具),D1 只递话;写调用唯一发生在组合根一处。 +4. 界面单元只做两件事:向外发信号、被 setter 注入数据后就地刷新(字段级 setter,禁全量重建)。 + +依据:视图单元九件套 = BoardView / HUDView / HudPresenter / BattleLogView / ItemWindowOverlay / NodeOverlay / LevelStripView / TunePanel / UiKit(切片九单元形态转正);读表口径 = D2 Rule 1 对呈现消费面的适用;写路径唯一入口 = 组合根(ADR-0003 Key Interfaces)。无计算。 + +### 规则 2:界面听消息只走中转,不私接玩法系统的线 +1. D1 永不直接订阅 A2 / A3 的通知;要听的消息全部由组合根接线转发,或直接来自 B1 步骤流信号与 runner 信号。 +2. 棋盘 / AP / RAP / 库存等"不发通知"的数据面,D1 在动作调用返回后由组合根立刻重查只读面刷新(命令式回执);不干等通知信号。 + +依据:订阅纪律固化自 ADR-0001;battle.md §Events Fired 的"呈现订阅 A2 三事件"表述按本条收窄为"经组合根转发或命令式回执"(冲突清单⑥,见 analysis 附录 C)。无计算。 + +### 规则 3:读数据只走登记过的口子,内部字段一律不碰 +1. D1 要显示的每个数据只允许从成文读数清单(下表)拿;清单之外的私有字段直读一律禁止。登记载体 = registry `d1_presentation_surface`。 + +| 数据从哪来 | 允许读的口子 | +|---|---| +| A2 扫雷 | `current_board()`(格态/旗/小人/变异只读投影)、`reveal_record()`、`compute_move_path(cell)`(不可达返回空——预览与执行同源) | +| A3 行动经济 | `phase / ap / rap / last_round_end`(getter-only) | +| B1 结算编排 | `presentation_snapshot() / is_battle_live() / stock_count() / query_reward_identity()` + 步骤流信号 | +| B2 战斗单位 | `hero_unit() / all_enemies()` 投影 | +| B3 / B4 | 零直读——经 B1 呈现面与流水投影(协作对象私有化) | +| runner | 节点链 / 当前节点 / HP 携带(A 区) | + +依据:读接口白名单 = 上表(封装收口批 entry 39/40 转正);接口漂移见 OQ3(analysis 开放问题)。无计算。 + +### 规则 4:界面的每个动作都走同一条提交通道,总闸只有组合根 +1. 玩家在界面上能发起的每类操作各只有一条提交通道,全部先过组合根这道总闸再转给对应系统;不存在绕过总闸的第二写入口。每帧至多提交一批意图(规则 7)。 + +| 玩家动作 | 提交通道 | +|---|---| +| 挖掘 / 插拔旗 / 移动 / 停手 | A2 `settle_batch(intents)`——停手也打包成 stop 走同一接口,不直调 A3 | +| 窗口里选道具 | B1 `submit_window_selection(窗口条目位置序)`——传窗口条目位置,不传库存句柄 | +| 弃图刷新 | 组合根提交刷新意图 → B1 `refresh_board` 编排:A3 扣费 → A2 retire → A1 generate → A2 install → B4 roll_board | +| 节点推进 | runner | + +依据:窗口选择传位置序 = 切片缺陷④的契约修正;停手不经 A3 直调 = 义务 A2×6①。无计算。 + +### 规则 17:界面收到的每份消息,字段清单先写成合同 +1. D1 消费的每一份跨系统载荷(事件 / 快照 / 回执 / 遥测行)都必须先把字段清单写成文(13 项,下表);清单既是实现契约也是消费侧断言——逐字段断言防缺字段、防多字段(信号参数没有运行时类型检查)。 + +| # | 载荷 | 来源 | 字段 schema | 状态 | +|---|---|---|---|---| +| 1 | `cell_revealed` | A2 | `{cell, kind ∈ {safe,mine}, digit(仅 safe 携带,恒真透传), variant_ids, channel ∈ {initial,chain,dig}, reward_eligible}` | A2 Core Rule 2 已 schema——引用 | +| 2 | `flag_judged` | A2 | `{cell, hit}`(轮末判定批、行主序) | 同上 | +| 3 | `round_end_board` / `round_end_stopped` | A2 / A3 | `{path_tag ∈ {ap_exhausted,cleared}, continuation_hint ∈ {same_board,new_board}}`(双发射源 payload 同构;信号实名 per ADR-0001) | 同上 + ADR-0001 | +| 4 | 流水行 ×13 类 | B1 | 公共 7 字段 `type / source_class / origin_cell(板级行可空)/ round_index / display_name / value / highlight?({flag_hit, clear_reward_h2, perfect_broken})` + 特有:伤害行/道具行(生效)→ `target`;效果行 → `effect_id` + `pool_value`;未生效四类 + 标旗未中行 → 聚合计数/批次字段;source_class 值域 ⊆ `source_label_map` 键集(B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack}) | B1 Core Rule 14 已 schema——引用+重述 | +| 5 | 结算快照 | B1 | `presentation_snapshot()` 返回(终局呈现数据面——despawn 前全量状态);独立 `settlement_snapshot` 信号系切片实现、B1 GDD 未登记契约——D1 现行消费 = 步骤流判定行 + `presentation_snapshot()`;若需独立信号回 B1 修订批补登记(OQ3) | 切片实现 / 契约待登记 | +| 6 | 意图回执 | A2 | `ActionOutcome{accepted, outcome ∈ {executed, voided, rejected}, reject_reason ∈ {board_face, resource_face, phase, board_invalid}, sub_reason(仅 board_face:not_adjacent / target_revealed / target_concealed / flag_present / flag_absent / no_path / out_of_catalog)}` | A2 已 schema——拒绝呈现数据源 | +| 7 | 资源面拒绝 | A3 | 无载荷(`try_spend` / `request_refresh` 仅 bool + debug 诊断)——呈现文案按动作上下文映射(诚实登记:资源面原因客户端推断,双源可辨由"动作种类 + 拒绝源"组合承载) | A3 AC 前言口径——引用 | +| 8 | 窗口载荷 | B1 组装 | `query_inventory` 逐件 `{item_id, source{cell,round}(不呈现于库存列表与 tooltip), 行值快照投影, 状态 InStock/Used}`(枚举序 = stock 受理序)+ `usable_now`(B1 守卫预判——满血治疗包 false) | B4/B1 已 schema——引用 | +| 9 | 奖励身份投影 | B1 呈现面 | `query_reward_identity(cell) → {reward_id, category, tier} \| null`(引用级投影;未开格可查但 MVP 不呈现) | B4 AC-2 已 schema——引用 | +| 10 | 状态投影 | B3 经 B1 | `query_active(side)` 元素 = 六归因字段 + `display_name` + 授予时快照 `(magnitude, unit)`;有效值 = L×快照 magnitude | B3 已 schema——引用 | +| 11 | 单位投影 | B2 | 敌单位七字段 `enemy_id/hp/max_hp/atk/heavy_multiplier/pattern/cap` + `beat` + 存活态;hero 投影 `hp/max_hp/atk`(经 `hero_unit()/all_enemies()`) | B2 已 schema——引用 | +| 12 | 零事件外发动作刷新面(命令式回执清单) | A2/A3/B3/B4 | 旗插/拔与移动成功(A2 零事件外发)→ `settle_batch` 回执后重查 `current_board()`;AP/RAP(A3 无推送)→ 每次写路径回执后重读 getter;状态效果(B3 零信号)→ B1 结算编排完成时点刷新;库存(B4 查询面)→ 写路径回执后重查 `query_inventory` / `stock_count()` | battle.md 更新触发注记——本行成文为契约 | +| 13 | D1 自有遥测 | D1 | `ux_round_input_ratio{ratio}` · `ux_round_exit{path_tag}` · `ux_refresh_attempt{result ∈ {accepted,rejected_rap,rejected_cap}, round_refresh_index ∈ [1,2]}` · `ux_window_choice{selected_item_id, usable_now, skip}` · `ux_reject{source ∈ {board,resource,phase}, sub_reason}` · `ux_hint_shown{hint_id}`——写 playtest JSONL `ux` 行族(playtest_logger 扩展) | 本 GDD 成文(新) | + +依据:口径对齐 A2 三事件先例;消费侧断言 = ADR-0001"信号参数无运行时类型检查"的缓解;AC-24 承载。无计算。 + +### 规则 18:对象销毁前先拍快照——界面永远不回读已销毁的东西 +1. 核心对象销毁前后,D1 不准"回头查"核心内部——只许用三样东西:消息载荷自带的数据、销毁前已取好的快照、封装好的只读投影。 + +| 细则 | 内容 | +|---|---| +| 销毁前捕获 | 终局与死亡的呈现数据(血条归零、卡体灰化、判定行)在核心对象销毁前已进流水行与快照 | +| 载荷自包含 | 每行流水自带来源 / 值 / 目标——界面不按行序反推目标状态机、不回查容器补数据 | +| 生命周期态禁读 | "战斗还能不能刷"只准问 `is_battle_live()` 与阶段投影,禁止读核心对象内部状态位 | + +2. 敌卡死亡灰化后仍留场的呈现 = 就地更新保留的最后已知投影(规则 6);不回读 Dead 句柄以外的任何东西。 + +依据:销毁时序 = B1 判定段先于 battle_over 清理序列(快照含 despawn 前终局);自包含字段 = `target` / `effect_id` / `pool_value` / `origin_cell`;生命周期观测口经组合根;AC-20 静态扫描承载。无计算。 + +### 规则 20:切片里的临时名字统一换成正式名 +1. D1 正式实现时,切片阶段的临时名换成本 GDD 正式名——只换名字、不改任何行为;`can_request_refresh` 不引入(上游 A3 没有这个接口)。 + +| 切片 provisional 名 | GDD 正式名 | 载体 | +|---|---|---| +| `request_refresh_active()` | `request_refresh` | A3 API(Active 相位受理——A3 Rule 11 现行口径) | +| `can_request_refresh()` | 不引入(A3 无此 API) | 入口可用性 = 组合根按只读观测量预判 + `request_refresh` 返回值兜底(规则 16) | +| `refresh_max_per_round` | `refresh_cap_per_round` | ActionEconomyTable 结构位(默认 2)——D2/registry 字段回写随实现批(A3 Schema 定稿节已预留) | +| `during_round`(A2 装板参数) | 无独立参数(受理域语义) | A2 `install_board` 受理域增 Active·仅弃图刷新路径(B1 `refresh_board` 编排内)——A2 Core Rule 3 | +| `round_end`(信号名) | 实名 `round_end_board`(A2 面)/ `round_end_stopped`(A3 面) | ADR-0001 信号实名映射 | + +依据:入口可用性 = 组合根按只读观测量预判 + `request_refresh` 返回值兜底(规则 16);AC-25 grep 断言承载。无计算。 + +设计意图:呈现层零玩法状态、零写、零 RNG,是切片两起呈现层崩溃之后的结构性防线。 + +## 战斗屏布局与组件 +### 规则 5:战斗画面分五区常驻同屏,棋盘与敌人不切场景 +1. 1280×800 基座下(canvas_items/expand 拉伸),战斗画面固定分成五块常驻区域;战斗画面与扫雷图同屏常驻是硬形态:挖掘期敌人在上方待机可见、结算期原地展开明细,全程不切场景。 + +| 区 | 位置 / 尺寸 | 内容 | 数据源 | +|---|---|---|---| +| A 关卡进度条 | 全宽 28px | 节点链步进、当前高亮、已过暗金(LevelStripView) | runner | +| B 战斗画面 | 全宽 252px | 英雄卡 + 敌人卡 ×1–3 + 轮次·阶段指示 + 结算明细带(24px 摘要 / 84px 实时流) | B2 投影 + B1 步骤流 | +| C 角色信息 | 左下 240×520 | HP / 攻击值含修正显示 / RAP 数据框(直角细线素面)+ 状态效果槽固定 4 槽 + 小人立绘占位 | A3 getter + B3 投影(经 B1) | +| D 扫雷图 | 中下 ~760×520 | AP 点阵条 44px(停手按钮居左、图例与弃图刷新按钮居右——刷新按钮置雷图右上角)+ BoardView 棋盘 + toast 槽(上缘内侧) | A2 呈现面 | +| E 道具栏 | 右下 280×520 | 库存常读列表 + 窗口态原位激活 | B4 库存载荷(经 B1) | + +依据:分区尺寸与组件落位的屏级权威 = `design/ux/battle.md`;拉伸模式 = ADR-0002;五区布局为结构冻结项(非调参 knob,修订走 GDD 流程)。无计算。 + +### 规则 6:敌人卡把"它是谁、下拍干什么、还剩几滴血"全写在脸上 +| 卡面元素 | 玩家读出什么 | +|---|---| +| 敌像 | 这是谁——姿态即线索 | +| 血条 | 还剩多少血(hp/max_hp 数字可读) | +| 顺位徽标 | 它第几个出手(固定顺位可读) | +| 意图徽标 | 下一拍它要干什么——把即将执行的一拍的视觉状态摆出来;挖掘期全程可见,结算期不另设播片 | +| 行为环点 | 行动周期多长(环点数 = 环长)、现在走到哪(当前位置高亮) | +| 状态槽 | 它身上挂着什么增益减益 | + +1. 伏击层数是全体敌人共享的一个数——各卡显示同一个值(数据源 = B1 流水经 B3 投影)。 +2. 敌人死亡 = 血条直角递减归零 → 卡体灰化 + 碎裂退场;禁止瞬间消失。 +3. 战斗段被短路掐灭的重击不单独产行动明细行(从行序可推断)。 +4. 敌卡刷新必须就地更新(字段级 setter),禁止逐行重建——全量重建会把死亡灰化态吞掉。 + +依据:敌像线索键 = art-bible §4.6.4;意图预告时序 = B2 Rule 8⑤;义务源 = B2×4④ / D1×4③④;禁瞬消 = B2 资产下限;就地更新 = 切片 entry 36 教训。无计算。 + +### 意图线索映射目录(B2×4① 成文载体——继承 battle.md 目录 + art-bible §4.6.4 键契约;资产定稿归 B5,本表为 D1 侧成文占位) +| enemy_id(§4.6.4 冻结键集) | 视觉状态(下一拍预告) | 强度档位编码 | 环长编码 | +|---|---|---|---| +| grub(蛆虫) | 攻击 ×1 态 | 小体量墩形 = 轻击(§3.1 普通怪) | 环点 1(单拍环——无蓄力态 = 每拍都动手) | +| ambusher(伏击兽) | 攻击 / 蓄力 / 重击 ×3 态 | 垂直延伸体量 = 重击(heavy_multiplier 直觉) | 环点 = 拍数;蓄力态在场 = 环里空一拍("可以贪") | + +- 预告时序 = 下一拍预告、挖掘期全程可见、结算期不另设意图播片(B2 Rule 8⑤);精确数值只在结算明细兑现(B2 Rule 8④——图像不报数)。 +- 映射键 = enemy_id,跨 run 恒定、禁重掷(B2 Rule 8③);形状 + 色相双通道;蓄力姿态必须含可辨识蓄力记号(§4.6.4)。 +- 资产需求下限三类(受击反馈 / 死亡表现 / 顺位标记)在程序化占位中先行(占位纹样挂键);终稿归 B5/样张批。 +- 键集注记:切片 DND 更名(goblin/bugbear/ogre_chief)与 GDD 键集 {grub, ambusher} 差异登记冲突清单②——键位映射表以 GDD 键集为锚,切片更名随 B2/C3 修订批对齐。 + +### 规则 21:弹层排队进出——打开哪层,哪层就真的能用 +1. 全部模态弹层(图例面板 / 明细回看 / 胜利 overlay / 失败 overlay)走同一张登记序栈:统一入栈出栈、后开置顶、关闭逐层恢复;任何一层打开时,被它遮住的东西要么隐藏、要么锁输入。 +2. 道具窗口不是模态:不入栈,但锁 D 区输入(规则 13)。 +3. TunePanel 显式初始化为不可见;它是切片调试入口,正式侧归 C4 天赋树——C4 落位时退役;Playtest 诊断按钮(卡死转储)保留为调试设施。 + +依据:退役时点 = battle.md OQ10 承接;AC-21 承载。无计算。 + +### 战斗屏状态机 +触发全部来自核心信号/回执——呈现层不自持玩法状态;"阶段"投影枚举 = {挖掘中, 结算中, 道具窗口}。 + +| 屏状态 | 进入触发 | 变化 / 出口 | +|---|---|---| +| 挖掘阶段(默认态) | 轮次开始 / 结算完毕回 Active | D 区可交互(AP 条点亮、可挖域呼吸微光);B 区敌人待机(意图徽标全程可见);明细带收起为上轮摘要 1 行;能量 2/10 | +| 结算阶段 | `round_end`(A2 棋盘路径 / A3 stopped 直发) | D 区锁输入(棋盘去饱和 + 金橙色相偏移,明度不变,数字保持全对比);明细带展开 84px 逐行滚动;阶段指示→"结算中";能量 7/10;B1 步骤流驱动逐行兑现 | +| 道具窗口态 | B1 授予段完成(窗口锚点信号) | E 区原位激活(规则 13);空库存直接 skip 不弹窗;选用/跳过 → 回结算流(结算阶段续) | +| 清图瞬间 | cleared 判定(round_end) | 金粒喷泉 + 彩纸 3–5s 即收(§2.3);H2 清图奖励行金框强调;随后换新图 install | +| 完美排雷高光 | B1 高光行(perfect_sweep) | 全屏喝彩级:金光带加倍扫射 + 「完美排雷 ×2」横幅(叠加于清图瞬间之上) | +| 标中旗高光 | 流水行 highlight=flag_hit | 该旗化暴击弹道加重 + 明细行金框(当轮即兑付) | +| 敌人死亡呈现 | 流水行击杀标记(target 字段) | 该敌卡血条归零 → 灰化 + 碎裂退场(禁瞬消、就地更新) | +| 胜利终局 | 判定行 verdict=win | 金蜡封落于扫雷图区中央 + 暖白金 overlay + 判定行 + 状态槽全清反馈 + RAP 清零反馈;出口 = 下一节点(runner) | +| 失败终局 | 判定行 verdict=lose | 暮蓝氛围 overlay(明度不变)+ 旗化光点收回 + 判定行 + 明细回看 + 状态槽全清反馈 + RAP 清零反馈;出口 = 返回 | +| Boss 遭遇变体 | encounter=boss | 战斗开始一次性紫赤影子爬棋盘 + 敌卡体量最大 + 阶段指示附 Boss 名 | +| 雾图变体 | 板含雾格 | 雾纹带 + 首遇提示(每 run 一次)+ 已开雾格 tint 残留 + 雾区全开合并域完成信号 | +| 空态:无状态效果 | query_active 双侧空 | 状态槽隐藏、数据框上移补位(规则 11) | +| 空态:零库存 | 关卡初 / 清空后 | E 区「道包空空——安全格里藏着补给」(指引性空态,非报错) | +| 调试变体 | 测试模式 | TunePanel 入口(正式侧归 C4 退役)+ Playtest 诊断按钮 | + +不变式:全部状态触发来自核心信号/回执——呈现层不自持玩法状态;模态子栈(规则 21 统一登记序)= 图例面板 / 明细回看 / 胜利 overlay / 失败 overlay——任一打开时遮挡物隐藏或输入锁定;非模态层 = toast(自动消退)与窗口态(锁 D 区)。 + +## 挖掘输入与棋盘可读 +### 规则 7:鼠标点哪就是哪个意思,一帧内的操作合成一批提交 +| 鼠标操作 | 翻译成 | +|---|---| +| 左键点已开格 | 移动(免费) | +| 左键点未开的相邻格 | 挖 | +| 右键 | 插旗 / 拔旗 | +| 点停手按钮 | 提交 stop——本轮结束 | + +1. 单意图帧:一帧内收集到的意图整批提交,点到停手当帧收集立即终结;F4 语义序仲裁只作防御性兜底(生产面造不出会乱序的输入)。 +2. 悬停预承诺:光标按目标切换三种语义、目标格边框高亮——点下去会发生什么,先看见再点。 +3. 可挖域常驻指示:小人八向相邻的未开格呼吸微光,是挖掘期唯一在动的元素。 +4. 悬停不可挖域 = 禁止光标 + 每 run 一次提示「需先走近(点击已开格移动,免费)」。 +5. 点击远格自动"先移动再挖"的复合意图本期不做(见 P1/P2 后置表)。 + +依据:光标三语义见「光标语义」;no_adjacent 提示见「一次性提示 hint_id」;义务源 = A2×6①③⑥。无计算。 + +### 规则 8:悬停已开的格,先画给你看小人会怎么走 +1. 玩家悬停任何已开格时,D1 立刻把小人到该格的合法路径用蓝色细线点迹画出——它是合法性指示,不是穿行动画。 +2. 移动执行用不超过 150 毫秒的短滑表现;不做"穿行动画轨道"。 +3. 路径数据 = `compute_move_path` 的 BFS 结果(不可达返回空)——预览与执行同源。 + +依据:短滑 ≤150ms 与再议挂 playtest 观测(battle.md OQ5,knob = move_slide_ms);义务源 = A2×6⑤(继承 battle.md 布局决策 D3)。无计算。 + +### 规则 9:四种格子一眼分得清,黑白截图也能逐格分类 +1. 四种格态必须一眼可辨,灰度截图下也能逐格分类——这是左键双义的消歧前提(点已开格 = 移动、点未开格 = 挖),误挖不可逆(花 1 AP、可能触雷)。 +2. 已引爆的雷格要"可通行可读":已开的底 + 巢穴纹(危险红族)+ 可作移动目标的视觉线索。 +3. 数字显示判定数据源 = `variant_ids`(digit 恒真透传、雾格隐藏呈现——A2 Core Rule 2);呈现层禁读真实数字自行判定。 + +依据:绘制优先级栈 = 数字 > 小人 > 旗 > 徽章 > 变异纹理 > 底色,任何元素不遮挡数字、小人与旗不得覆盖文本(art-bible §1.2);义务源 = A2×6②④。无计算。 + +### 四种格态(A2×6②——灰度截图逐格可分类) +| 格态 | 视觉构成 | 语义 | +|---|---|---| +| 已开 0 格 | 裸土地板 | 携带"八邻无雷"推理信息——与已开雾格必须灰度可判定区分(混淆即污染推理) | +| 已开雾格 | 地板无字 + 底色层淡雾 tint 残留 | 雾格只是不显示;tint 为残留视觉锚(battle.md OQ8 采纳) | +| 未开格 | 掩尘完整砖 | 左键 = 挖(1 AP) | +| 未开雾格 | 掩尘砖 + 雾纹带 | 左键 = 挖;雾语义 = 无雷盲区(图例恒在条目) | + +### 光标语义(A2×6③⑤——悬停预承诺) +| 光标 | 悬停目标 | 伴随指示 | +|---|---|---| +| 移动箭头 | 已开格 | `compute_move_path` 路径蓝细线点迹(规则 8)+ 目标格边框高亮 | +| 挖镐 | 可挖未开格 | 目标格边框高亮(可挖域另有常驻呼吸微光) | +| 旗形 | 旗插拔语义 | 目标格边框高亮 | +| 禁止 | 不可挖域 | + 首 run 一次 no_adjacent toast | + +### 规则 15:雾格有雾的样子,整片雾开完只报一次"开完了" +1. 未开的雾格 = 掩尘完整砖 + 雾纹带——雾身份走形状通道、色相不承载。 +2. 开过的雾格 = 地板无字 + 底色层淡雾 tint 残留——残留 tint 与"已开 0 格"必须灰度可判定区分(0 格携带"八邻无雷"推理信息,混淆即污染推理)。 +3. 整片连续雾区全开时,完成信号以视觉合并域为单位一次呈现——相邻两个雾区规则上是独立实例、呈现上合并成一片;禁止逐实例响两次。 + +依据:雾语义条目(无雷盲区)见图例(规则 14);tint 与完成信号随雾重设计批可复核(art-bible 雾格资产占位——内容封禁期);双雾区相邻 = A4 EC11(边界情况表);义务源 = A4×3③。无计算。 + +### 一次性提示 hint_id(封闭枚举——遥测值域与去重键) +| 值 | 触发 | 必含要点 | 频次 | +|---|---|---|---| +| `fog_first`(雾首遇——A4×3②) | 首个实际含雾格的板进入可操作状态(挖掘阶段开始;生成期一次查询即判定,免疫降级板误触发) | 无雷盲区不赌命 / 雾格不传播连锁(连锁止于雾界属正常)/ 雾内插旗纯损 | 每 run 一次 | +| `chord`(chord 肌肉记忆——A2×6③) | 经典扫雷玩家首日尝试双击快开 | 本作无双击快开——插旗后轮末自动判定标中;点数字格可移动小人 | 每 run 一次 | +| `no_adjacent`(不可挖域——规则 7) | 不可挖域悬停 | 需先走近(点击已开格移动,免费) | 每 run 一次 | + +- hint_id 封闭枚举 = 上表三值;拒绝 / voided / 零合法板 toast 族不占 hint_id(拒绝须每次可感知——见边界情况表)。 +- 文案定稿权归实现期文案 pass(本 GDD 只锁必含要点与形态约束)。 + +### 拒绝与 voided 呈现映射(A2×6⑥——映射源 = reject_reason + sub_reason) +| 拒绝源 | 触发 | 呈现动作 | 文案 | +|---|---|---|---| +| board_face·not_adjacent | 挖非相邻格 | 目标格旗朱 `#C63D22` 边框闪 + toast | 「需先走近」 | +| board_face·target_revealed | 目标已开 | 同上 | 「该格已翻开」 | +| board_face·target_concealed | 目标未探明 | 同上 | 「该格未探明」 | +| board_face·flag_present | 目标有旗 | 同上 | 「须先拔旗」 | +| board_face·flag_absent | 目标无旗 | 同上 | 「该格没有旗」 | +| board_face·no_path | 路径不通 | 同上 | 「路径不通」 | +| board_face·out_of_catalog | 调试态 | debug 级 | 不与玩家语义混桶 | +| resource_face(AP 不足 / RAP 不足——无上游载荷,按动作上下文映射) | 资源不足 | AP/RAP 数字脉冲 + toast | 「AP 不足 · 剩 0 点」 | +| phase | 非 Active | toast | 「结算进行中」 | + +- 图标 + 文案冗余(非色相独载)。 + +## 信息与归因呈现 +### 规则 10:奖励是什么、包里有什么,随时一眼可读 +1. 已开的格子里若藏着奖励,格面显示它的家族类型徽章、拾取瞬间档位可读。 +2. 右侧道具栏是挖掘期的规划面——背包每一件一行(下表);悬停看完整描述与触发注记,但看不到来源——来源归因是结算明细行的职责。 +3. 奖励身份键全局恒定、跨 run 禁重掷。 + +| 常读条每行显示 | 说明 | +|---|---| +| 图标 | 家族键定纹样(同族不同档可互辨——刀的两档并排分得清) | +| 名称 | 道具显示名 | +| 属性简述 | 行值快照派生的一句话(伤害 / 回复 / 增益) | +| 状态 | 在包 / 已用(已用的过滤出常读条) | + +实现参照——奖励徽章与库存常读条: + +| 口径 | 内容 | 依据 | +|---|---|---| +| 格面家族徽章 | 五键 attack/defense/heal/blast/weaken——键集冻结;五键槽位在程序化占位映射表预留、未启用键不呈现 | art-bible §4.6.3 | +| 拾取瞬间 | 格面徽章 tier 档位可读 | H1 节拍 | +| 库存徽章 | 八键、reward_id 键——knife_t1/knife_adv 同族互辨是硬需求 | art-bible §4.6.2 | +| MVP 呈现范围 | 格面徽章只在已开格呈现(未开格不预告身份) | B4 Rule 8 现行锁定;冲突清单⑤ | +| 徽章栈位 | 旗之下、变异纹理之上、不进数字区、不构成格内第二焦点 | art-bible §3.4 | +| 常读条枚举序 | 入包受理序 | B4 E21 | +| tooltip | 完整描述 + 触发注记、不含来源(来源归因由结算明细行承载) | battle.md 口径 | +| Used 过滤 | 已用件过滤出常读条——查询面保留只读、过滤归呈现 | query_inventory 保留只读 | +| 身份恒定 | reward_id / enemy_id / effect_id 三键全局恒定、跨 run 禁重掷 | B4×5① | + +依据:义务源 = B4×5①。无计算。 + +### 规则 11:身上的增益减益看得见、好坏分得清、没了有交代 +| 槽位元素 | 玩家读出什么 | +|---|---| +| 图标 | 哪种效果(形状纹样定身份,色相不承载身份) | +| 层数角标(L) | 叠了几层 | +| 剩余结算次数点(R) | 还会结算几次 | +| 悬停 tooltip | 来源 + 语义 + 剩余 | + +1. C 区固定 4 个状态槽;好坏一望即知:有利 = 金族满框、有害 = 红族缺框——判别按效果键的极性表,不看数值正负。 +2. 开局没有任何状态 → 整排槽隐藏、HP/攻击/RAP 数据框上移补位;图例面板载空态说明「战斗中拾取的增益在此显示,随战斗结束消散」。 +3. 状态系统不发任何信号——状态栏刷新绑定在结算编排完成的时点(命令式)。 + +| 口径 | 内容 | 依据 | +|---|---|---| +| 图标键 | 效果图标七键——形状纹样身份、色相不承载身份 | art-bible §4.6.1(七键冻结) | +| 极性判别 | 键级极性表:attack_up/defense_up/weaken 有利、ambush 有害——禁以 magnitude 符号判极性(负值增益存在;pool 亦不足以判——enemy_output 池两向并存) | B3×4③ | +| 极性键扩容 | 提案扩展键 enrage/off_guard/sapped 随极性提案实施批 | 极性提案实施批 | +| 语义归属 | 经 SettlementTable `effect_pool_map` 成文(作用面) | B3×4③ | +| 空态裁决 | 槽整体隐藏 + HP/攻击/RAP 上移补位(D1 定稿) | B3×4① | +| 数据源 | B3 投影:六归因字段 + display_name + 授予时数值快照(经 B1 编排面) | B3 已 schema——引用 | +| tooltip 文案 | source_class 文案经 B1 `source_label_map` | B1/B3 协作 | + +依据:义务源 = B3×4①②③。无计算。 + +### 规则 12:结算逐行念账——每笔收益标明来源,该高光的当场高光 +1. 结算推进时,明细带把 B1 流水逐行实时滚出,每行标明「做了什么、多少、到谁头上、因为什么」——玩家能把每一分收益对回自己哪一步扫雷决策。 +2. 高光分三档:标中旗金框、清图奖励金框、完美排雷全屏喝彩级——庆祝不打在空兑现上。 + +| 行的类型 | 呈现口径 | 依据 | +|---|---|---| +| 生效行(伤害 / 道具入库) | 实时滚出,逐行来源标注「{行为} {值} → {目标}(来源)」——模板插值,行宽预留 40% 膨胀 + 固定槽高 + clip_contents | 切片缺陷①回归 | +| 效果行 | 两列分示:value = 单次贡献值、pool_value = 池合成值——禁相加(防"−130%×2 行 = −260%"误读) | B1 Core Rule 14 字段 | +| 零值伤害行 | 照常可见、不被吞 | B4×5③ | +| 未生效四类行 + 标旗未中行 | 聚合计数呈现(防逐行惩罚轰炸)+ 形状冗余(图标 / 划线——非色相独载,色弱清单 #9) | B4×5③ | +| 四类未生效行划分 | 道具行(入库 / 生效语境注记)/「奖励未生效」(聚合计数)/「已消耗未生效」/「使用被拒」 | B4×5③ | +| 未选用件 | 语境注记「未选用件随关卡结束清空」(关卡域口径) | B4×5③ | +| 标中旗高光行 | 旗化暴击弹道加重 + 明细行金框(chord 缺席的即时兑现补偿) | B1×3① | +| H2 清图奖励行 | 金框高亮行(轮末仪式峰值) | B1×3① / B4×5④ | +| 完美排雷 | 全屏喝彩级:金光带加倍扫射 + 「完美排雷 ×2」横幅;高光行 value = 实际有效兑付当量;首份即零兑付不发高光 | B1×3② / art-bible §2.2 最高档(创意风险 #2 落点) | + +依据:行协议 = 13 类封闭枚举(B1 Core Rule 14——字段分层见规则 17 载荷表 #4);高光使用规则 = 事件型用独立高光行、属性型强调用行字段标记(B1);义务源 = B4×5③④ / B1×3①②。无计算。 + +### 规则 13:轮末道具窗口在道具栏原地亮起,至多选一件 +1. 结算推进到窗口锚点(授予段之后、增益减益段之前)时,E 区道具栏原地激活:面板点亮金描边、标题换成「轮末道具窗口 · 至多选用 1 件」,阶段指示同步标出窗口锚点位置;棋盘区去饱和 + 金橙色相偏移并锁输入(明度不变)。 +2. 满血的治疗包灰置、悬停说明禁选原因(「HP 已满 · 禁选」)。 +3. 选中即承诺——窗口关闭、回结算流,提交的是窗口条目位置;跳过按钮恒在;政策注记常驻「未选用件随关卡结束清空」。 +4. 背包为空就不弹窗(窗口标记照常登记——回放输入完整性归 B1),结算零停顿。 +5. 窗口期棋盘输入不竞争(结算期棋盘本就锁输入);窗口激活期间跳过常读条全量重建——全量刷新会冲掉窗口态。 + +依据:E 区原位激活 = battle.md 布局决策 D2;usable_now 投影 = B1 组装载荷;去饱和 + 金橙偏移 = art-bible §2 明度恒定硬规则的合规化实例;窗口触发政策 = MVP 有库存恒弹(候选取舍见 analysis);义务源 = B4×5②。无计算。 + +设计意图:本节每条呈现口径都是"把每一分收益对回玩家哪一步扫雷决策"的落点——P2 裁剪判据的最后半米由 D1 走完。 + +## 提示·终局反馈·美术与可访问性 +### 规则 14:新机制第一次遇到给提示,忘了随时查图例 +1. toast = 棋盘区上缘内侧浮出的非模态提示:自动消退 2.5 秒、不劫持输入、不打断挖掘流。 +2. 一次性提示共三种(雾首遇 / chord 肌肉记忆 / 不可挖域走近),每个整 run 只弹一次;拒绝、voided、零合法板的提示不属于一次性族——每次都要可感知。 +3. 图例面板 = 模态覆盖层:格态 / 变异 / 雾语义三组条目,「迷雾 = 无雷盲区」条目恒在、不设解锁门、可随时回看,并载状态槽空态说明(规则 11)。文案定稿权归实现期文案 pass——本规则只锁必含要点与形态。 + +依据:义务源 = A2×6③ / A4×3①②。无计算。 + +### 规则 16:一场打完,输了赢了、剩什么没了什么,样样有交代 +1. 六种反馈各有触发与呈现(下表)。 +2. 暮蓝 overlay 是全部界面里唯一的暮蓝档、明度不变——失败氛围靠色相与运动表达、不靠变暗。 +3. 不存在"RAP 满自动刷"——刷新的决策主体恒为玩家(A3 Rule 11)。 + +| 反馈 | 触发 | 呈现 | +|---|---|---| +| 胜利终局 | 判定行 verdict=win | 金蜡封落于扫雷图区中央 + 全屏暖白金 overlay + 判定行(终局判定行 = 胜负画面数据锚)+「下一节点」+ 明细回看入口 | +| 失败终局 | 判定行 verdict=lose | 明度不变的暮蓝 `#5A6C8C` 氛围 overlay(全作唯一暮蓝档)+ 小人坐砖掸灰 + 插过的旗逐面化光点收回 + 判定行 + 返回 | +| 状态槽全清 | battle_over(B3×4④) | 状态逐槽化光收回 + 微文案「状态随战斗结束消散」(防"我的 buff 哪去了"误读——胜负两终局同款) | +| RAP 清零 | battle_over(A3) | RAP 数字滚动归零 + 微文案(防"攒的钱凭空没了") | +| 库存清空 | level_clear(B4×5⑤——关卡域口径) | 道包逐件化光点飞出 +「剩余道具 ×N 随关卡结束清空」(N = clear_inventory 返回的清空前快照计数——B1 编排义务⑤数据面) | +| Boss 遭遇变体 | encounter=boss | 战斗开始一次性紫赤影子爬过棋盘(§2.6——纯视觉预告)+ 敌人卡体量最大 + 阶段指示附 Boss 名 | +| 弃图刷新确认 | 按钮点击(A3 契约的呈现半边) | 按钮置 AP 条右端,文案实时「RAP−{rap_refresh_cost 表读值} · 本轮剩 N 次」(剩余次数 = `refresh_cap_per_round` − 本轮已用;轮内已用次数读数 = A3 只读观测量缺口,接口漂移登记见 OQ2);入口可用性 = 组合根按只读观测量预判(rap ≥ rap_refresh_cost ∧ 剩余次数 > 0)+ `request_refresh` 返回值兜底(A3 无 `can_request_refresh` API——不引入);确认微交互显式提示代价与旧板旗失效;确认交互与常规确认不同位(位置通道——破坏性确认异位,色弱清单 #7);RAP<8 / cap 超限拒绝可见(边界情况表);换图后 AP 不重置、剩余次数扣减可见 | + +依据:battle_over 与 level_clear 两反馈不合并、时序不倒挂(边界情况表);义务源 = B3×4④ / A3 / B4×5⑤。无计算。 + +### 规则 19:美术没画好先用代码画的占位图顶着,占位图同样要过硬指标 +1. 全部等终稿美术的面(砖面 / 雾纹 / 敌像 / 图标 / 徽章 / 立绘 / 蜡封)以程序化占位先行:色值直接取冻结色板、形状按形状语言参数域用代码绘制(圆角砖 / 直角数据框 / 纹带形);图标、徽章、线索按键契约挂进键位映射表(数据键 → 占位纹样 ID,一键一槽位)——终稿美术到位时零数据层改动、零运行时换图。 +2. 占位图同样要过硬指标:数字可读性(1–8 全档对格底 ≥4.5:1、0.5× 缩放可辨、纹带不侵数字字形区)必须通过。 +3. 样张定稿 = 砖面形态与色板的冻结闸、终稿资产开工的前置;样张批不阻塞 D1 其余全部验收。 + +依据:判据 = F2;色板 = art-bible §4.1 冻结色板(底 L≥0.30 ∧ 饱和 ≤0.40 ∧ 墨 L≤0.027);形状语言 = §3;样张标准 = §1.4 第一验收项;键集扩容走 §4.6 修订;终稿开工 = asset-spec 批。 + +### 规则 22:中文不缺字、字看得清、颜色不独挑大梁、不用键盘也能玩 +| 基线 | 要求 | +|---|---| +| 中文字体 | 随包内置 + 设为主题默认字体——运行时默认字体无 CJK 字形,豆腐块 = 验收硬断言 | +| 按钮文案 | 4 汉字上限(「停手」「弃图刷新」「跳过」「选用」);译英 ≤1.6× 不破版;德语长词允许两行或图标+缩写 | +| 明细行宽 | 模板预留 40% 膨胀 | +| 对比度 | 数字对格底 ≥4.5:1(WCAG AA) | +| 色弱安全 | 无信息仅靠色相承载(九对语义对逐项成立)、灰度可辨 | +| 字号下限 | 棋盘数字 ≥18px / 明细正文 ≥14px / tooltip ≥13px(@1280×800) | +| 闪烁 | 无 >3 次/秒 | +| 减动效 | 全部动画时长减半或跳终态开关;结算逐行兑现是信息载体——不可整体跳过、可加速 | +| 输入 | 纯鼠标可完成全部操作 | + +依据:对比判据引用 = F2;本地化契约消费方 = D6(CJK / 按钮宽 / 术语表);正式分层(文本缩放 / 色盲模式 / 按键重映射)待 accessibility-requirements.md 定稿(OQ7);承载 AC-23 / AC-26。无计算。 + +### Visual/Audio 执行锚点(art-bible 逐条执行) +D1 是本体系中唯一的自有渲染面系统——视觉义务从"数据供给规格"反转为"执行 art-bible"。 + +| art-bible 锚点 | D1 执行 | +|---|---| +| §1.2 绘制优先级栈 | 数字 > 小人 > 旗 > 徽章 > 变异纹理 > 底色——BoardView 分层绘制/控件叠序按此栈实现,任何元素不遮挡数字(小人与旗不得覆盖文本;小人所在格边框高亮) | +| §1.4 格子样张(引用依据) | 样张 = 砖面形态定稿闸;D1 引用其验收标准(第一验收项 = 数字可读性硬指标)作为程序化占位与正式资产的共同判据;样张未制作不阻塞 D1(规则 19);样张通过后冻结为制作标准,终稿资产替换走 asset-spec 批 | +| §3.3 UI 形状语法 | 半拟物——面板 = 石板圆角矩形(与地砖同圆角率)、按钮 = 凸圆角"可按的砖"、仪式性确认(胜利蜡封)= 圆形;数据框/准线/进度条一律直角细线无纹理("圆角 = 可触摸的物件,直角 = 须精确的信息") | +| §4.1/§4.4 UI 色板 | 数据素面墨族(#33261B 系)——棋盘与面板读同一种墨;战斗容器中性石色(防暖色渗入分走数字对比预算);语义色规则(红只说危险 / 金只说收益 / 蓝只说己方 / 紫赤只说 Boss / 绿零逻辑语义 / 禁入色相带 300°–345°)逐条适用;明度恒定硬规则(状态区分只经色相/饱和/运动/元素——去饱和 + 金橙偏移即其合规化实例) | +| §4.5 色弱安全清单(逐对执行) | 九对语义对的冗余通道(形/图标/位置/运动)在对应组件落实——四格态靠完整/碎裂形、奖励金 vs 危险红靠剪影+运动、UI 红/金强调对加图标+文案冗余、破坏性确认异位(位置通道)、图标键族一键一纹样色相不承载身份 | +| §4.6 图标键契约(键位挂接) | 效果图标 §4.6.1(七键——sapped 键冻结未入表前禁引用)、奖励徽章 §4.6.2(八键 + clear_blast 并列预留)、格面类型 §4.6.3(五键——MVP 只挂已开格)、敌种线索 §4.6.4(行为拍姿态键)——键位映射表(规则 19)为唯一挂接点,键集扩容走 §4.6 修订、禁运行时换图 | +| §2 能量档承载 | 五区布局的动效与色彩转场按 §2.1–2.6 能量档实现(挖掘 2/10 唯一微光在动 → 结算 7/10 金橙光带 → 清图 8/10 金粒喷泉 → 胜利 6/10 克制 → 失败 1/10 暮蓝最静一拍 → Boss 5/10 紫赤)——"状态间区分度自检"三条(挖掘 vs 结算靠谁在动+饱和;胜负以色相即判;Boss 以紫赤登场标记)为转场验收锚 | +| 音频(D4,Alpha 占位) | MVP 无音频;挂点清单引用 B1/B4 音频挂钩点(明细行类型流 / 负反馈聚合单次 / 开格拾取按 category 分色 / 清图奖励音)+ toast 提示触发(本 GDD 新增挂点候选);临时 SFX 随垂直切片(AD 项先例) | + +> Asset Spec:Visual/Audio 义务已定义。art-bible 已定稿(§1–4),样张批后运行 `/asset-spec system:d1-hud-framework` 产出逐资产视觉描述/尺寸/生成提示词(样张先行——§1.4 为定稿闸)。 + +### 边界情况 +| 条件 | 行为 | 依据 | +|---|---|---| +| 每次拒绝(A2×6⑥) | 棋盘面拒绝(sub_reason 七值)→ 目标格旗朱 `#C63D22` 边框闪 + toast(文案映射见「拒绝与 voided 呈现映射」);资源面拒绝(AP 不足 / RAP 不足——无上游载荷,按动作上下文映射)→ AP/RAP 数字脉冲 + toast;相位拒绝 → toast「结算进行中」;图标+文案冗余(非色相独载) | 映射源 = reject_reason + sub_reason——每次拒绝玩家可感知 | +| 轮末意图 voided(至少 stop 被清零须可感知) | toast「本轮已清图,停手未执行 · 剩余 AP 已清零」——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP 的读感欺骗(F4 联合清图让位在生产面不可构造,本条为诚实兜底) | A2×6⑥ / A2 voided 语义 | +| 零合法板(A1 EC5 → B1×3③——三形态选型定稿) | B1 上抛非法产物标记 → toast 诚实提示(非模态、不打断);补偿与回退不提供(补偿逻辑归 B1 域外、确定性回退已知好种子 = 实现期 ADR 候选〔A1 OQ2 关联〕——MVP 面向玩家的形态 = 仅提示);玩家永不卡死由 A1 验收三分支保证,提示是体验债的诚实兜底(原"玩家不可见遥测域"口径由本条承接闭合) | B1×3③ / A1 EC5 | +| 弃图刷新被拒(RAP 不足) | 按钮 + RAP 读数脉冲 + toast | A3 EC13 | +| 弃图刷新被拒(本轮 cap=2 超限) | toast「本轮刷新次数已用完」 | A3 EC15 | +| 确认微交互缺一步 | 不提交(防误触——破坏性确认异位) | 规则 16 | +| 两个雾区相邻(A4 EC11) | 规则上独立布置实例、呈现合并为一片——雾区全开完成信号以视觉合并域为单元、禁实例级双响(信号只响一次);样张最坏用例含"宽度 1 雾带两侧毗邻数字格" | A4 EC11 / A4 AC-9 | +| 雾首遇触发边界 | 触发谓词 = 首个实际含雾格的板进入可操作状态——免疫 EC2 降级板(无变异)误触发、免疫贴脸雾 t=0 歧义;一次性作用域 = 每 run | A4 UI Requirements 修正口径 | +| 敌人死亡与终局同帧(despawn 时序) | 死亡呈现数据 = 流水行击杀标记 + 目标字段(despawn 前已产出——规则 18①);battle_over 清理序列后呈现层零核心回读;`is_battle_live()` false 后视图进入终局态只消费快照 | 规则 18① | +| 窗口打开时库存为空 | 不弹窗、直接 skip(结算零停顿);窗口标记照常登记(B1 回放输入完整性) | B4 E8 | +| 满血时窗口含治疗包 | usable_now=false 灰置 + 禁选原因 tooltip;强制提交(不可经 UI 达成——禁选态拦截)→「使用被拒」行兜底 | B1 守卫 | +| battle_over 与 level_clear 反馈叠加(关卡末战) | RAP 清零 + 状态槽全清反馈先行(battle_over),库存清空反馈随后(level_clear 编排)——两反馈不合并(防"N 道具消失"误读为战斗结算内容) | 规则 16 | +| 模态覆盖层叠开(图例上开明细回看) | 统一登记序栈——后开者置顶、关闭逐层恢复;任一层打开时其遮挡物隐藏/输入锁定断言成立 | 切片缺陷⑤回归 | +| 悬停文案换行 | 全部动态区固定槽高 + clip_contents——换行裁剪不推挤布局(明细行 40% 膨胀 + tooltip 独立浮层) | 切片缺陷①回归 | +| CJK 字形缺失 | 豆腐块 = AC 硬断言失败(字体随包 + theme 默认;首次导出 checklist 项) | ADR-0003 | +| 敌卡刷新与死亡灰化竞争 | 就地更新(字段级 setter)禁全量重建——死亡灰化态不被后续刷新吞掉;窗口激活期同理跳过常读重建 | entry 36 教训 | +| 减动效开启 | 全部转场/高光/演出时长减半或跳终态;结算逐行兑现降速不跳过(信息载体);无闪烁 >3 次/秒恒成立 | 规则 22 | +| 移动目标 = 当前格 | 幂等零移动——无动画、无 toast | A2 EC14(合法受理的呈现静默) | +| `compute_move_path` 返回空(不可达) | 路径预览不显示 + 禁止光标(预览与执行同源) | A2 AC-14② 对侧 | +| 键集扩容(新 effect_id / reward_id / enemy_id 上线) | 键槽映射表缺键 = 呈现层 fail-fast(debug 断言 + 占位"?"纹样 + 诊断,禁静默空图标)——扩容走 art-bible §4.6 修订(禁运行时换图) | 规则 19 / AC-15 | + +## 呈现几何与可读性判据 +D1 零玩法公式——本节仅承载呈现几何与可读性判据。全部取色禁新增色值,一切经 art-bible §4.1/§4.4 冻结色板。 + +### F1 棋盘格尺寸(cell_size) +**结论**:cell 为两档定值(battle.md 拍板)——12×12 → 36px、10×10 → 45px;F1 公式为可行性校验器而非闭式推导:floor(min(D_w − pad_h, D_h − ap_bar_h − pad_v) ÷ N) ≥ cell(N) 且 cell(N) ∈ [30, 45] 断言成立;数字字号 = max(18, ceil(cell × 0.5)),像素字体整数倍缩放。 + +``` +两档定值:cell(12) = 36px、cell(10) = 45px(battle.md 拍板值,非公式导出) +可行性校验(装板期断言,违例 fail-fast): + floor(min(D_w − pad_h, D_h − ap_bar_h − pad_v) / N) ≥ cell(N) + 且 cell(N) ∈ [30, 45] +N ∈ {10, 12}(A1 BoardGenTable 尺寸档);D 区基准 1280×800 下 D_w=760、D_h=520−44(AP 条) +@1280×800 实算(pad_h=pad_v=16):min(744, 460) = 460 + 460 ÷ 12 → 38 ≥ 36 ✓ 460 ÷ 10 → 46 ≥ 45 ✓ +数字字号 = max(18, ceil(cell × 0.5)),像素字体整数倍缩放(canvas_items stretch 保证清晰) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| D 区宽/高 | D_w / D_h | int px | 布局常量 | 五区布局基准(battle.md Layout) | +| AP 条高 | ap_bar_h | int px | 44 | D 区顶条 | +| 棋盘维数 | N | int | {10, 12} | A1 行值(MVP 两档) | +| 定值档 | cell(N) | int px | {36, 45} | battle.md 拍板两档;非闭式导出(为何定值而非闭式见 analysis 问题 4) | + +- Example:12×12 → floor(min(760−16, 476−16)/12) = 38 ≥ 36 ✓(定值取 36)→ 数字字号 = 18px;10×10 → floor(460/10) = 46 ≥ 45 ✓(定值取 45)。 + +设计意图:两档值来源 = battle.md 拍板("格 36–45px 见方"两端),公式只承载界限与可行性断言。 + +### F2 数字对比恒等式(引用 art-bible §4.1——D1 不新增参数) +**结论**:1–8 全档数字对所在格底 WCAG 对比 ≥4.5:1(§1.4 第一验收项,对程序化占位同样适用)——由色板参数域结构保证:格底相对亮度 L≥0.30 ∧ HSV 饱和 ≤0.40 ∧ 数字墨 L≤0.027 ⇒ 任意底墨组合 ≥4.5:1 恒成立。 + +| 实测锚(底 / 墨) | 对比 | +|---|---| +| #33261B / #C9AF83 | 6.9:1 | +| #4A1E14 / #B39A78 | 5.3:1 | + +- D1 侧判据 = 灰度截图分类 + 对比抽测进 AC;换 biome 色板须重跑 §1.4(换皮只换四槽、永不变信)。 + +### 显式排除声明 +| 对象 | 归属 | +|---|---| +| 伤害 / 经济 / 概率 / 生成公式 | A1–B4/B1/C3 | +| AP/RAP/价目显示值 | A3 表读值投影 | +| 伤害数字 | B1 流水行 value 投影 | +| D1 拥有的全部 | F1 几何换算、F2 可读性判据的引用执行、调参表呈现常量 | +| 随机 | 零——呈现层无任何 RNG 消费(S1/S2 扫描域适用) | + +## 依赖与输出 +权威分层:冲突时以 battle.md + art-bible 为呈现形态权威、以本 GDD 为系统契约权威、以各玩法 GDD 为机制语义权威。 + +| 系统 | 方向 / 性质 | 接口要点 | +|---|---|---| +| A2 | 上游 hard | 读:`current_board()` / `reveal_record()` / `compute_move_path(cell)`(呈现面,entry 39 E2 收口);订阅:零直订(ADR-0001——B1 facts 与组合根转发);写:`settle_batch(intents)` 经组合根(D1×6①);消费其三事件 schema(规则 17 载荷表 #1–3)与 ActionOutcome(#6);D1×6 六项义务全落位(台账表) | +| A3 | 上游 hard | 读:`phase / ap / rap / last_round_end`(getter-only——entry 39 E5);刷新意图经组合根 → B1 `refresh_board` 编排内 A3 `request_refresh`(挖掘期 Active 受理、每轮上限 `refresh_cap_per_round`=2);接口漂移登记:轮内已用刷新次数读数缺口(OQ2);AP/RAP 常显、RAP 清零反馈、双源拒绝呈现(A3 契约) | +| B1 | 上游 hard(数据供给主通道) | 读:步骤流信号(13 类行协议——载荷表 #4)、`presentation_snapshot()` / `is_battle_live()` / `stock_count()` / `query_reward_identity()`(切片实现、registry `d1_presentation_surface` 登记、B1 GDD 契约登记待批——冲突⑧/OQ3);写:`submit_window_selection(窗口位置序)` / 刷新提交面;level_clear 反馈行数据面(清空前快照计数);B1×3 + B4×5 + B3×4 呈现义务的统一数据源契约 = B1 Core Rule 14 | +| B2 | 上游投影 | `hero_unit() / all_enemies()` 只读投影(entry 39 E3;切片实现、B2 GDD 契约登记待批——冲突⑧);血条/顺位/姿态/存活数据面;despawn 前捕获纪律(规则 18);B2×4 四项义务落位(规则 6) | +| B3 | 上游投影(经 B1) | 零直读(B1 协作对象私有化)——状态投影经 B1 编排面(载荷表 #10);EC12 空态、极性映射、全清反馈(规则 11/16) | +| B4 | 上游投影(经 B1) | 零直读——`query_reward_identity()` / 库存载荷经 B1(载荷表 #8–9);奖励徽章键 §4.6.2、窗口呈现、四类行、H2、关卡末清空反馈(规则 10/12/13/16) | +| A4 | 上游 soft(呈现规格) | 雾纹理双通道、图例条目、首遇提示三要点、tint + 完成信号(A4×3——规则 9/14/15);A4 UI Requirements 为本 GDD 引用源 | +| A1 | 间接 | 零直接接口——EC5 零合法板数据面经 B1 上抛(非法产物标记 → toast 告知);biome 标签 = 换皮色板数据锚(程序化占位消费);产物字段保真是呈现诚实的数据前提 | +| D2 | 数据读取 | display_name 等本地化名经核心读表面;D1 自有常量不入 D2 玩法表(调参表载体声明) | +| C1-lite(level_runner) | 上游 hard | A 区节点链 / 当前节点 / HP 携带(关卡域);胜利 overlay「下一节点」→ runner 推进(写经组合根);level_clear 编排反馈;C1 GDD 未开始——本三项为切片代码契约,C1 GDD 定稿时回填(接口漂移回 C1 批) | +| C2 | 预留 | 关卡结算屏(关卡末清空反馈的完整屏形态)归后续 spec `design/ux/level-clear.md`(battle.md OQ9)——MVP 以 overlay 反馈承载 | +| B5 | 下游 hard | D1→B5 交付物:五区布局与双舞台裁决(第三形态)、B 区演出区结构、明细带呈现位、流水行协议呈现语义、敌人卡呈现语义(死亡灰化/禁瞬消)、art-bible §4.6 键位映射表——B5 只叠加动画密度与演出(受击反馈/弹道/退场动画),呈现语义已在本 GDD 定稿;"战斗表现美术测试"以本布局出样 | +| D4 | 下游 soft | 音频挂点事件源 = 明细行类型流 + toast 提示触发(B1/B4 音频挂钩点清单引用);MVP 无音频(Alpha) | +| D5 | 下游 soft | 教学承载位 = toast 体系 + 图例面板(雾三要点 / chord 提示 / 蓄力拍提示候选 / RAP 清零 / 库存清空教学条目)——D5 GDD 定稿口径 | +| A5 | 预留 | 探测类道具显影面(未开格身份呈现深度 OQ2)——接口归 A5 GDD,本 GDD 不预写 | +| D6 | 下游 | 本地化契约消费方(CJK/按钮宽/术语表——规则 22) | + +- 索引分层注记:systems-index 依赖分层 "D1 ← A2,A3,B1"——本 GDD 实际投影消费扩至 B2/B3/B4/A4/runner,全部经 B1 编排呈现面 + 封装收口批成文呈现面(entry 39/40),零新增运行时直调边;D1 无游戏状态、无 knob 联动、无 RNG——层 4 Presentation 定位不变。 + +### 呈现义务台账逐项落位表(门禁核对用——27 项,缺项即 blocking) +| 义务源 | 义务 | 承接方式(本 GDD 落点) | 状态 | +|---|---|---|---| +| A2×6 | ① stop 意图经 `settle_batch` 提交、不直调 A3 | 规则 7 + 写路径总表(规则 4)+ AC-13 | 落位 | +| A2×6 | ② 四种格态一眼可辨 | 规则 9 + AC-4 | 落位 | +| A2×6 | ③ 悬停预承诺 + 可挖域常驻指示 + chord 首遇提示 | 规则 7 / 14② + AC-5 / AC-14 | 落位 | +| A2×6 | ④ 已引爆雷格"可通行可读" | 规则 9 + AC-4 | 落位 | +| A2×6 | ⑤ `compute_move_path` 呈现语义 = 合法性指示(裁决继承 battle.md D3) | 规则 8 + AC-6 | 落位 | +| A2×6 | ⑥ 单意图帧收集 + voided 呈现 + 每次拒绝可感知 | 规则 7 + 边界情况表 + AC-7 / AC-8 | 落位 | +| B2×4 | ① enemy_id→图像线索映射清单成文(预告时序 = 下一拍、挖掘期全程可见;含强度档位/环长直觉编码;双通道) | 规则 6 + 意图线索映射目录 + AC-10 | 落位 | +| B2×4 | ② 线索映射全局恒定(enemy_id 键,跨 run 禁重掷) | 规则 6 / 10 + AC-10 | 落位 | +| B2×4 | ③ 单位顺位与存活可读 | 规则 6(顺位徽标)+ AC-10 | 落位 | +| B2×4 | ④ 敌方 side 池呈现口径(共享单值、各卡同值) | 规则 6 + AC-10 | 落位 | +| B3×4 | ① HUD 常显 + EC12 空态规格定稿(裁决:槽隐藏) | 规则 11 + AC-12 | 落位 | +| B3×4 | ② effect_id→icon 映射键契约 | 规则 11(引用 art-bible §4.6.1 七键冻结)+ AC-15 | 落位 | +| B3×4 | ③ 有利/有害极性呈现映射(非 magnitude 符号) | 规则 11(键级极性表 + pool 语义归属)+ AC-12 | 落位 | +| B3×4 | ④ 战斗结束全清的状态栏去留 | 规则 16 + 状态机胜负终局 + AC-16 | 落位 | +| B4×5 | ① 已开格 + 库存奖励身份恒定可读(常读条 = 挖掘期规划面) | 规则 10 + AC-9 | 落位 | +| B4×5 | ② 道具使用窗口呈现(原位激活/单件/usable_now/政策注记/空库存) | 规则 13 + AC-13 / AC-17 | 落位 | +| B4×5 | ③ 结算明细四类行呈现(聚合计数 + 语境注记) | 规则 12 + AC-11 | 落位 | +| B4×5 | ④ H2 清图奖励行强调呈现 | 规则 12 + 状态机清图瞬间 + AC-11 | 落位 | +| B4×5 | ⑤ 关卡末(level_clear)库存清空可见反馈 | 规则 16 + 边界情况表 + AC-16 | 落位 | +| B1×3 | ① 高光行强调呈现(标中旗金框 / H2 金框) | 规则 12 + AC-11 | 落位 | +| B1×3 | ② 完美排雷呈现形态 | 规则 12 + 状态机 + AC-11 | 落位 | +| B1×3 | ③ EC5 零合法板提示/补偿/回退形态(裁决:仅提示) | 边界情况表 + AC-19 | 落位 | +| A4×3 | ① 图例「迷雾 = 无雷盲区」条目(恒在/可回看) | 规则 14① + AC-14 | 落位 | +| A4×3 | ② 雾首遇提示(每 run 一次、三要点、非模态) | 规则 14② + AC-14 | 落位 | +| A4×3 | ③ 已开雾格 tint 残留 + 雾区全开合并域完成信号(battle.md OQ8 采纳) | 规则 9 / 15 + AC-4 | 落位 | +| A3 | AP/RAP 常显 + 刷新入口(玩家发起)+ RAP 清零反馈 + 双源拒绝 | 规则 16 + 边界情况表 + AC-8 / AC-16 | 落位 | +| A1 | EC5 零合法板诚实呈现(数据面经 B1×3③ 上抛) | 同 B1×3③ | 落位 | + +### 屏级 spec 与配套文档(UI 要求) +| 项 | 要点 | +|---|---| +| 权威屏级 spec | `design/ux/battle.md`(APPROVED,B1 UX Flag 的 /ux-design 产物——覆盖子屏:挖掘主界面 / 结算明细 / 道具窗口 / 终局 / 明细回看):本 GDD 为其系统级承接(布局/组件/交互裁决全部继承);本节不重复 spec 内容,stories 引用 `design/ux/battle.md` 而非本 GDD 直引组件细节 | +| UX Flag 处置 | B1 UX Flag(结算明细屏 + 道具窗口 → Phase 4 前置 /ux-design)已由 battle.md 兑现闭合;D1 自身无新增屏(战斗屏 = 唯一 MVP 玩法屏);相邻域屏 NodeOverlay / 关卡结算 = 后续 spec `design/ux/node-reward.md` / `design/ux/level-clear.md`(battle.md OQ9——本 GDD 只锁出口锚点) | +| pattern 库 | `design/ux/interaction-patterns.md` 已建(核心 8 条:双源拒绝反馈 / 原位窗口激活 / 逐行兑现明细带 / 悬停预承诺光标 / 点阵资源显示 / 模态 overlay 层级纪律 / 破坏性确认异位 / 禁用态即理由全文)——本 GDD 组件全部对号入座;12 目录占位(实现期回写义务) | +| accessibility | `design/accessibility-requirements.md` 未建(gate-check Pre-Production 缺口在案)——本 GDD 内建基线承诺(规则 22),正式分层(文本缩放/色盲模式/按键重映射)待该文档定稿 | +| 场景探针标配(code-conventions) | 每个可交互 UI 区域配套场景级探针——切片三探针(ui_probe / ui_click_probe / ui_tune_probe)扩展 + 战斗屏场景级探针(AC 硬性块);探针即呈现层契约的活文档 | + +## P0 最小闭环 +1. 玩家进入战斗屏——五区同屏就绪,≤500ms 内首格可点。 +2. 挖掘期鼠标操作经单意图帧整批提交 `settle_batch`(唯一写路径,经组合根)。 +3. 回执链:组合根立刻重查只读面 → 视图字段级 setter 就地刷新。 +4. 停手或 AP 尽 → `round_end` → D 区锁输入,明细带展开 84px 逐行兑现(B1 步骤流驱动)。 +5. 窗口锚点:E 区原位激活,选用/跳过 → 回结算流。 +6. 判定行 → 终局呈现(胜利金蜡封 / 失败暮蓝)+ 状态槽全清 + RAP 清零反馈。 +7. level_clear → 库存清空反馈「剩余道具 ×N」→ 下一节点 / 返回(runner 推进,写经组合根)。 + +## P1 / P2 后置 +| 优先级 | 内容 | 后置原因 | +|---|---|---| +| P1 | 远格点击自动"先移动再挖"复合意图 | 移动免费高频,两击摩擦符合观测假设——先经 playtest 验证 P4 ≤2 观测假设 | +| P1 | Gamepad 焦点序 | Partial 支持非 MVP(OQ5) | +| P1 | 相邻域屏 node-reward / level-clear 完整屏形态 | 归后续 spec(OQ6 / battle.md OQ9);MVP 以 overlay 反馈承载 | +| P1 | accessibility 正式分层(文本缩放/色盲模式/按键重映射) | 待 accessibility-requirements.md 定稿(OQ7) | +| P2 | 存档级"一生一次"提示作用域 | 归 D3/C4 后重议(hint_once_scope) | +| P2 | TunePanel 退役 | C4 天赋树落位批(OQ12);Playtest 诊断按钮保留 | +| P2 | 探测类道具显影面 | 接口归 A5 GDD,本 GDD 不预写 | +| P2 | 未开格预读徽章 | 随极性提案实施批(冲突⑤/OQ9)——B4 Rule 8 现行锁定 MVP 不呈现 | +| P2 | 音频挂点出声 | MVP 无音频(Alpha,归 D4) | + +## 调参表 +D1 knob = 呈现节奏与可读性常量(非玩法数值——D2 Rule 1"全部玩法数值入表"纪律不适用);单一来源登记防散落,载体 = presentation 主题资源/常量单点(实现期裁决载体形态);修订走本 GDD 流程非调参。 + +| 字段 | 域(尺度) | 默认 | owner | 调过高 / 调过低 | +|---|---|---|---|---| +| toast_display_ms | 每条提示 | 2500 | D1 | 信息滞留、遮挡节奏 / 读不完即消(拒绝原因漏读) | +| move_slide_ms | 每次移动 | ≤150 | D1 | 移动拖沓(违反 P4 摩擦护栏)/ "太瞬"无方向感(battle.md OQ5 playtest 观测调) | +| board_install_fade_ms | 每次装板 | 200 | D1 | 换图迟滞 / 闪切无地牢感 | +| clear_celebration_ms | 每次清图 | 3000–5000 | D1 | 拖节奏(§2.3"三五秒即收"上限约束)/ 丰收感不足 | +| font_min_board / detail / tooltip | @1280×800 基座 | 18 / 14 / 13 px | D1 | 挤占格内空间 / 可读性下限击穿(可访问性基线) | +| hint_once_scope | 每提示项 | per_run | D1 | —— / ——(存档级"一生一次"归 D3/C4 后重议) | +| reduce_motion_scale | 每动画 | 0.5(或跳终态) | D1 | 减动效失效 / 等效关闭动效 | +| detail_band_height | 摘要 / 实时流 | 24 / 84 px | D1 | 挤压 B 区演出区 / 行数不足(逐行归因截断) | + +- 非 knob(结构冻结,修订走 GDD 流程):五区布局与分区尺寸(battle.md 拍板)、双舞台常驻、窗口原位激活形态、单意图帧纪律、模态层级纪律、键位映射契约(§4.6)、程序化占位策略、订阅纪律(ADR-0001)。 +- 性能预算(引用非 knob):同屏 draw calls < 200、进入战斗屏 ≤500ms 可交互、headless 全套 < 30s(technical-preferences / ADR-0004)。 +- Knob 交互:font_min_board × cell 尺寸(F1 字号下限随格尺寸联动——12×12 板 36px 格是 18px 下限的绑定域);toast_display_ms × 一次性提示去重(提示频率观感)。 +- 调参流程:改先测量后复测(切片 mock 数据先行——code-conventions 数值纪律);playtest 观测项 = 移动手感(OQ5 关联 battle.md)/ 明细带常驻(OQ10)/ 窗口存在感。 + +## 验收标准 +### 测试口径 +| 口径 | 要点 | +|---|---| +| 测试载体 | ADR-0004 双载体定稿(gdUnit4 deferred——D2 OQ1 关闭形态):逻辑断言类 = `godot --headless --script` 冒烟扩展;场景级类 = 场景探针(`godot --headless --path . res://smoke/.tscn` 走正常项目引导——autoload 在位、真实 SceneTree;切片三探针为先例) | +| 期望值纪律 | 呈现断言引用表读值(AP=ap_per_round 表读值、RAP 门槛=rap_refresh_cost 表读值等)与 art-bible 冻结参数,不写死玩法数值;呈现常量(toast 时长等)为本 GDD 调参表登记值可直断;每用例先 `reset_diagnostics()` | +| 覆盖声明(out-of-scope) | 玩法行为正确性(上游 GDD AC 域——D1 只断言呈现投影与上游契约一致)· 美术终稿质量(样张/asset-spec 批)· 动画密度(B5)· 音频(D4)· gamepad 焦点序(OQ5) | + +### AC 清单(编号不变,Given-When-Then) +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 1280×800 与 16:9 全屏(canvas_items/expand) | 进入战斗屏(12×12 板) | 五区无遮挡/无重叠/无内容裁切(棋盘全部格子+数字完整可见)、分区尺寸 = battle.md 布局表值、同屏 draw calls < 200、进入后 ≤500ms 内首格可点 | 五区布局与基座 | +| AC-2 | 12×12 / 10×10 两档板 | 装板渲染 | cell = F1 定值档(36 / 45px @1280×800)且可行性校验通过、数字字号 ≥ max(18, cell/2)、像素字体整数倍缩放无模糊 | F1 格尺寸与字号 | +| AC-3 | 全部格底×数字组合 | 灰度截图 + 对比抽测 | 1–8 全档对比 ≥4.5:1(F2)、0.5× 缩放数字仍可辨、纹带不侵数字字形区;全部状态转场(去饱和+金橙偏移 / 暮蓝 / Boss 紫赤)明度恒定(无变亮/变暗表达) | 对比度与明度恒定(F2) | +| AC-4 | 含雾板 | 逐格渲染后灰度截图(+代码走查) | 四种格态一眼可辨;已引爆雷格呈"可通行可读"(已开底+巢穴纹+可作移动目标);已开雾格 = 地板无字 + 底色层 tint 残留且与已开 0 格灰度可判定区分;数字显示判定消费 variant_ids(呈现层无独立雾判定——代码走查) | A2×6②④ / A4×3③ | +| AC-5 | 小人在场 | 挖掘期任意时刻静止截图 | 8 向相邻未开格常驻呼吸微光(挖掘期唯一在动元素——其余静止截图断言);绘制优先级栈成立(徽章在旗之下纹理之上、不进数字区、无第二焦点) | 可挖域与绘制栈 | +| AC-6 | 悬停已开格 | 悬停 / 点击移动 | `compute_move_path` BFS 路径以小人蓝细线点迹高亮(合法性指示);移动执行 ≤150ms 短滑;不可达格返回空且禁止光标(预览执行同源——A2 AC-14② 对侧);点击远格无自动 move+dig 复合意图(负空间) | A2×6⑤ 定稿 | +| AC-7 | 一帧内多意图(含 stop) | 提交 | 整批经 `settle_batch` 单批到达(stop 终结当帧收集——探针断言单批);光标三语义(移动/挖/旗)随悬停目标正确切换 + 目标格边框高亮 | A2×6⑥③ | +| AC-8 | Active 挖掘期 | ① 点击停手 ② 弃图刷新成功 ③ RAP<8 或 cap 超限提交 | ① 意图经 `settle_batch([stop])` 提交(代码走查 + 探针:零直调 A3 符号)+ 按钮下压 + AP→RAP 飞行数字(RAP 增量 = 剩余 AP 读数投影)② RAP 扣 rap_refresh_cost、AP 不重置、剩余次数扣减可见、旧板旗失效提示可见 ③ 资源面拒绝可见(脉冲 + toast——EC13/EC15) | A2×6① / A3 | +| AC-9 | 已开格 + 库存 | 渲染已开格与库存常读条 | 已开格家族徽章(§4.6.3 键)+ tier 档位拾取瞬间可读;库存常读条逐件 icon+名称+属性简述、枚举序 = stock 受理序、不含来源信息(列表与 tooltip 均无);Used 件不在常读条;同族刀(knife_t1/knife_adv)并排互辨 | B4×5① | +| AC-10 | 双敌种遭遇 | 渲染敌卡 + 击杀驱动 | 意图徽标 = 下一拍预告且挖掘期全程可见、映射按 enemy_id 全局恒定(跨 run 重放同姿态)、含强度档位与环长编码(环点数 = 拍数)、蓄力态含蓄力记号;顺位徽标可读(固定顺位最前者可辨);血条 hp/max_hp 数字可读;多敌伏击层数各卡同值呈现;敌死亡 = 血条归零→灰化+碎裂退场(禁瞬消)、就地更新不丢灰化态(entry 36 回归) | B2×4①②③④ | +| AC-11 | 完整结算 | B1 步骤流驱动明细带 | 逐行明细实时可见且每行带来源标注(source_label_map 文案)、四类未生效行按聚合计数呈现且带形状冗余、零值行可见、效果行 value/pool_value 分示禁相加;标中旗行金框+弹道加重;H2 清图奖励行金框;完美排雷 = 金光带加倍+「完美排雷 ×2」横幅、value = 实际有效兑付当量、首份零兑付时高光降级不发;战斗后可经明细回看 overlay 复看完整流水 | B4×5③④ / B1×3①② | +| AC-12 | 状态在场/清空 | 渲染状态槽 | 槽显示 icon(§4.6.1 键)+层数+剩余结算次数点+tooltip(来源+语义);极性按键级表呈现(金满框/红缺框——非 magnitude 符号:负 magnitude 有利行测试例);空态 = 槽隐藏 + 数据框上移 + 图例载空态说明;battle_over 逐槽化光收回 + 微文案 | B3×4①③④ | +| AC-13 | 窗口触发 | 窗口交互 | E 区原位激活(非中央弹窗)+ 至多选 1 件 + usable_now=false 灰置带禁选原因 + 政策注记「未选用件随关卡结束清空」常驻 + 空库存不弹窗 + 选中提交 = 窗口条目位置(选中件库存态必变 Used——缺陷④回归探针)+ 窗口期不被常读刷新 stomped(entry 36 回归)+ D 区去饱和锁输入(明度不变) | B4×5② | +| AC-14 | 含雾板首遇 + 图例/提示系统在位 | 驱动各 hint_id 触发条件(含同 run 重复触发) | 图例面板「迷雾 = 无雷盲区」条目恒在可随时回看、载状态空态说明;雾首遇提示每 run 一次、非模态不劫持输入、必含三要点;chord 提示每 run 一次含「点数字格 = 移动小人」锚点;no_adjacent 首次提示;hint_id 去重生效(同 run 二次触发零 toast) | A2×6③ / A4×3①② | +| AC-15 | 程序化占位渲染全组件集 | 键位映射表挂接核查 + 未知键注入 | 全部 icon/徽章/线索经键位映射表挂接(数据键→纹样 ID);§4.6.1 七键/§4.6.2 八键/§4.6.3 五键/§4.6.4 姿态键逐一在位;未知键 = fail-fast 断言 + "?"占位 + 诊断(禁静默空图标);sapped 键未入 StatusEffectTable 前零引用 | 键契约执行 | +| AC-16 | battle_over 与 level_clear 信号(关卡末战) | 清空反馈逐项到达 | battle_over:RAP 数字滚动归零+微文案、状态槽逐槽化光+微文案;level_clear:库存逐件化光飞出+「剩余道具 ×N 随关卡结束清空」(N = 清空前快照计数);两反馈不合并、时序不倒挂 | A3 / B3×4④ / B4×5⑤ | +| AC-17 | 判定行 verdict=win/lose(Boss encounter 变体) | 终局呈现 | 胜利 = 金蜡封落于扫雷图区中央+暖白金 overlay+判定行+明细回看入口;失败 = 暮蓝 overlay(明度不变)+旗化光点收回+判定行;判定行内容 = B1 终局判定行投影(数据锚);Boss 战 = 紫赤影子爬棋盘一次性演出 + 阶段指示附 Boss 名 | 终局与变体 | +| AC-18 | 完整对局驱动 | 采集 playtest JSONL | 六类 ux 行按规则 17 载荷表 #13 schema 写入 playtest JSONL(字段名与值域逐项对表);ux_round_exit.path_tag 值域 = 上游封闭枚举 | 遥测 | +| AC-19 | 真实实例化战斗屏场景(探针场景加载、autoload 在位) | 驱动完整对局(两场遭遇 × 含雾板 × 窗口选用/跳过 × 刷新 × 胜负两终局) | 全程零脚本错误(stderr 归零)+ 五区断言 + 首格可点计时 ≤500ms(切片 ui_probe/click_probe 模式扩展) | 战斗屏场景级探针 | +| AC-20 | AC-19 对局至 despawn 与 battle_over 之后 | 静态扫描 views/ 文件集 + 终局后重放渲染比对 | 呈现层对核心生命周期状态的读数 = 零(静态扫描:views/ 文件集禁 `is_battle_live()` 以外的核心状态位读取、禁核心内部字段访问——只许成文呈现面);终局呈现数据全部来自流水行/快照(断言终局后重放渲染与实时渲染逐像素一致——快照自包含判据) | 信号时点状态可见性——缺陷②回归 | +| AC-21 | 逐一打开全部模态覆盖层(含叠开) | 逐层打开 / 关闭 | 每层打开时遮挡物隐藏/输入锁定断言成立、关闭逐层恢复;TunePanel 默认不可见(显式初始化) | 模态层级——缺陷⑤回归 | +| AC-22 | 悬停 tooltip 最长文案 / 明细行 40% 膨胀宽度构造 | 渲染后截图差分 | 全部动态区固定槽高 + clip_contents:布局零推挤(截图差分断言) | 布局稳定——缺陷①回归 | +| AC-23 | 全部中文文案界面 | 渲染 + 字形覆盖抽测 | 全部中文文案渲染非豆腐块(字形覆盖抽测——ADR-0003 硬性前置) | CJK 字体 | +| AC-24 | AC-19 对局全载荷流 + 六面回执 | 探针逐字段断言 + 代码走查 | 探针对规则 17 载荷表 #1–#9 载荷逐字段断言(消费侧校验——缺字段/多字段 = 失败,切片"窗口载荷缺 round_index"崩溃回归);#12 命令式刷新面 = 代码走查(旗插拔/移动/AP/RAP/状态/库存六面各一个回执后重查断言) | payload schema 消费断言——硬性要求①兑现 | +| AC-25 | 呈现层文件集 | 静态扫描 / grep | 零 `randomize()`/`randi()`/`randf*`(S1/S2)、零玩法数值字面量(期望值经只读面推导——人工三要素口径 warning 级 + AC-1/2/8 表读值推导合成判定)、核心写调用仅组合根(S7 呈现侧)、切片 provisional 名(`request_refresh_active` / `can_request_refresh` / `refresh_max_per_round` / `during_round`)零残留(grep 断言——entry 35 命名统一核销);每视图单元头注含「实现 GDD:design/gdd/d1-hud-framework.md(系统 ID D1)」溯源行 | 静态扫描与命名 | +| AC-26 | 1280×800 全量界面(含 AC-19 对局) | 截图抽测 + 静态扫描 + 探针 | 字号下限成立(棋盘数字 ≥18px / 明细正文 ≥14px / tooltip ≥13px);无闪烁 >3 次/秒(动画时长/频率参数扫描);减动效开启 = 全部转场/高光/演出减半或跳终态、结算逐行兑现降速不跳过(探针断言);AC-19 对局全程纯鼠标驱动(零键盘依赖) | 可访问性基线(规则 22) | + +## 版本历史 +| 版本 | 日期 | 改动 | +|---|---|---| +| v2 | 2026-09-14 | 两件套迁移:按模板 v2 重排——设计推演(存在理由/候选取舍/数值论证/体验锚点论证)与审计附录(切片教训落位表/覆盖矩阵/冲突清单/复核注记)迁至 `design/gdd/analysis/d1-hud-framework.md`;规则 1–22 / F1–F2 / AC-1–26 / OQ1–12 / 27 项义务台账编号全保留,零规则与数值变更 | +| v1.1 | 2026-09-14 | /design-review(lean)NEEDS REVISION 同日核销:F1 改两档定值 + 可行性校验式(原闭式在固定边距下无法同时得出 36/45——用户裁决方案 A);呈现面 API 接口漂移登记扩条(冲突⑧/OQ3 覆盖 B1×5 函数 + B2×2 投影 + settlement_snapshot 信号);状态头 C3 状态更正(Designed 非 Approved);C1-lite 切片契约注记(C1 GDD 定稿时回填);新增 AC-26 可访问性基线(字号下限/无闪烁/减动效/纯鼠标)并扩覆盖矩阵 Rule 22→AC-23/26;"尸位"用语替换为直白表述;RAP 文案模板改表读值占位 | +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;核心规则/枚举/边界情况/AC 转表格;呈现刷新链路转字符画 + 出口分发表;13 项载荷 schema、27 项呈现义务台账、意图线索目录、切片教训落位表、冲突清单、25 条 AC 全保留;语义与当日定稿版对齐,零规则变更 | +| v1 | 2026-09-13 | /design-system 一次会话定稿:27 项呈现义务落位 + 22 条核心规则 + 13 项载荷 schema 成文 + 25 条 AC + 三条切片硬性要求(solo 模式出稿,未经 /design-review) | diff --git a/design/gdd/d2-data-config.md b/design/gdd/d2-data-config.md new file mode 100644 index 00000000..be63596e --- /dev/null +++ b/design/gdd/d2-data-config.md @@ -0,0 +1,611 @@ +# D2 数据配置与数值表(Data Config & Balance Tables) + +> **状态**:Approved · **版本**:v2 · 2026-09-14 +> **承载支柱**:P1、P2(基础设施层,间接支撑全部四支柱) · **层级**:Layer 1 Foundation · Milestone MVP · 设计顺序第 1 位 +> **一句话**:D2 把全部影响手感与平衡的数字收进可配置、可校验的数值表,经 ConfigDB 单点发放、RngService 统一种子化——战斗可复现、平衡可热调、headless 可测的数据底座。 +> **推演件**:`design/gdd/analysis/d2-data-config.md`(本文件只收结论,理由在那里) + +## 设计目的 + +D2 是全部玩法数值的单一事实源:data/tables 的 .tres + autoload ConfigDB + autoload RngService——schema 先行、数值回填。没有 D2,参数散落硬编码:调一次难度改五处代码、战斗无法用固定种子复现、headless 自动化测试无从谈起。它是玩家永远不该注意到的地基:不制造情绪,但失职会直接摧毁 P1/P2 的信任底线。 + +1. 设计师"改数字不改代码"——全部影响手感的数字(AP、旗、折算比例、奖励值)集中一处可调,热重载即改即验,"这轮玩起来不对劲"能快速修正且不引入新毛刺。 +2. 开发与测试"同决策必同结果"——RNG 统一种子化,相同决策序列必然相同战斗结果,回放 / 审计 / 自动化测试可复现。 +3. 难度"越下越险由结构保证"——数值表天生带(关卡序号, 节点深度)两维,难度曲线可审计、可回溯,而非散落各处的魔法数字。 +4. CI 与无窗口环境照样跑完整战斗——数据层零界面依赖。 + +## 系统范围 + +负责: + +- 全部表 schema 类(data/schemas)与数据文件(data/tables) +- autoload ConfigDB / RngService · SCHEMA_VERSION 常量 +- 加载 / 校验 / 热重载状态机 · 校验双通道与诊断收集器 +- debug override 链 · 难度行解析 resolve +- 十张已定稿表 + 两张占位表的结构与键(数值由 owner 系统按设计顺序回填) + +不负责: + +- 平衡数值定值与玩法公式——伤害、掉落权重、成长曲线(owner 系统 GDD 与 registry:B1 / B4 / C3 等) +- 玩法随机预置——任何系统若需玩法随机(如结算浮动),自带方案走增流通道并通过 P1 评审(规则 5) +- 存档(D3)· 任何 UI 表现面 +- 精英 / Boss 修饰语义(C1 / C3——DifficultyTable 只预留 elite_ / boss_ 命名空间)· MetaTable 键结构(C4) + +硬约束:玩法数值零代码字面量 · 零 UI 依赖 · fail-fast 无软回退 · RNG 流集合封闭 · 表路径固定(debug 注入根除外)· release 无 override 源。 + +## 核心循环 + +### 数据加载与校验链路 + +``` +进程启动(Unloaded) + │ ConfigDB._ready() / load_tables(root, collect_only) + ▼ +Loading ──── 按表清单逐一 load() ── 文件缺失 → Failed + ▼ +Validating ── 单个资源加载成功 → schema validate(),错误入诊断收集器 + ▼ +┌ 校验双通道 ──────────────────────────────────────────┐ +│ 游戏通道 collect_only=false(默认): │ +│ 错误 → push_error 清单 → 致命错误路径 │ +│ editor / CI / headless / release 一律致命 │ +│ 测试通道 collect_only=true(仅 debug/测试构建): │ +│ 收集不升级 → 冒烟脚本逐夹具断言类别 → 一次性退出 │ +└──────────────────────────────────────────────────────┘ + ▼ +Ready ────── 全部通过 → 类型化属性可读 +``` + +### 热重载链路(仅 debug) + +``` +编辑器热重载 / 调试键 → Reloading + │ + ├─ 战斗中(set_reload_gate 已置位)→ 拒绝返回 false,停留 Ready + │ (战斗结束后手动再触发,不自动重放) + ▼ +重走 Loading → Validating + │ + ├─ 校验失败 → 保留旧表停留 Ready + debug 错误清单(唯一软着陆,不杀调试会话) + ▼ +校验通过 → Ready:新值经类型化属性可读 + config_reloaded 恰一次(仅 debug,D1 调试用) +※ RNG 状态线独立:Reloading / Failed 不触碰 RngService——计数器与 master_seed 不变(AC-4) +``` + +### RNG 取数链路 + +``` +进程启动 → 流待命(未播种取数 = 致命) + ▼ +冒险开始 → C2 set_master_seed()(仅全流计数器全零时受理) + ▼ +scope 事件:board_gen = 每次生成新扫雷图(A1 换图时) + reward_roll = 每次 board install 批(格奖励批 + 清图奖励批) + encounter = 每场战斗实例化(B1 begin_battle——unit_count 区间骰) + │ open_stream(stream):计数器 +1 → R2 派生 → 返回已播种实例(同一 scope 事件内取数共用) + ▼ +冒险结束 → 流保留供复盘;快照 = master_seed + 各流 scope 计数器 + (须在无在飞 open_stream 实例时切出——scope 事件边界之外) +``` + +### 出口分发 + +| 出口 | 触发 | 动作 | 约束 | +|---|---|---|---| +| 启动致命中止 | 启动加载任一致命错误 | 输出错误清单并中止构建;headless 独立进程非 0 退出 | 全构建一致,无软回退 | +| 热重载软着陆 | debug 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单 | 唯一例外 | +| 运行期致命 | 请求越域 / 未播种流取数 | 游戏内致命错误路径;headless quit 非 0 | 不分构建 | +| 热重载成功 | debug 且门未置位且校验通过 | 新值可读 + config_reloaded 恰一次 | 战斗中被门拒绝 | +| 存档拒读 | SCHEMA_VERSION 高于当前 | 拒绝读取 | 比较归 D3,常量归 D2 | + +## 表结构与回填(schema 先行) + +规则: + +1. 规则 3(结构先行、数值回填):全部数值表按"结构 + 数据"两件套交付——结构 = data/schemas/ 下一个自定义 Resource 类(每字段带类型、范围、默认值和注释),数据 = data/tables/ 下数据文件;D2 先交付全部 schema 与占位表(含键结构),具体数值由 owner 系统 GDD 回填,先有格子、后填数字。 + 举证:无计算。十张已回填表 schema 目录见本节,逐表字段行见调参表节。 +2. 规则 4(进度坐标查行):跟着冒险进度成长的数值表(难度、生成参数、敌人配置)每行带(关卡序号, 节点深度)两维坐标——系统按坐标取数,无精确命中时取不超过请求坐标的最近已有行;敌人等实体键在命中行内按名字精确匹配,那一行没有这个名字就是悬空引用、直接致命中止,不做身份近似。 + 举证:解析规则(floor 取行、warning 四元组去重、越域前置守卫)见「难度行解析」节;悬空引用 = 加载期致命(规则 6)。 + +| 表的形状 | 行键怎么定 | +|---|---| +| 进度成长表 | 行键 =(关卡序号 level_index, 节点深度 node_depth) | +| 实体复合键表 | 进度两键之后再复合实体主键列(如 EnemyTable 的 enemy_id;合成规则写入 schema 注释) | +| 单行全局常量表 | 无成长语义的表允许只放一行 | +| 元进度表 | 存档/天赋/代币归 MetaTable,键结构归 C4 定 | + +| 两维坐标之外的数值轴 | 归宿 | +|---|---| +| 战斗内轴(胜场/轮次等) | 由 owner 公式承载或作为表列;归属登记进 schema 注释(C3 定稿时声明) | +| 难度第三维"精英/Boss 修饰" | game-concept 定稿:难度 = f(关卡序号, 节点深度, 精英/Boss 修饰);DifficultyTable 预留 elite_ / boss_ 前缀扩展字段命名空间,语义由 C3 定稿——本 GDD 不定义任何修饰数值、不预写修饰枚举 | +| 两键定不了精英性的节点形态 | 如分支地图同深度并存精英与普通节点——允许改键,按接口漂移修订规则回写(与 MetaTable 同等豁免待遇) | + +设计意图:进度坐标可以就近近似,"是哪种敌人"不能近似——身份近似会静默改变遭遇构成、损害 P2 归因。 + +### 十张已回填表 schema 目录(逐表字段行见调参表节) + +| 表资源 | 键结构 | Rule 4 豁免 | 跨字段 / 结构校验(加载期致命) | 悬空引用政策 | Owner | +|---|---|---|---|---|---| +| ActionEconomyTable | 单行全局 | 单行全局定义表(先例) | 任一动作成本 ≤ ap_per_round(schema validate() 跨字段类别承载,条件性夹具登记) | — | A3 ✓ | +| VariationTable | 按 variant_id(含 unlock_level 列) | 定义表非进度成长表——进度语义承载于 unlock_level 列(schema 注释) | info_mask=hide_numbers ⇒ cell_constraint=safe_nonzero(夹具 variation_mask_constraint) | counter_item_ids MVP 不校验——A5 首个道具 id 回填时补校验 + 条件性夹具 | A4 ✓ | +| StatusEffectTable | 按 effect_id | 定义表非进度成长表(schema 注释承载) | magnitude:percent ∈ [-100,100] ∧ ≠0 / flat ≥ 1 跨字段校验 | effect_id 校验已激活(B4 RewardTable 首引 defense_up——夹具 b4_dangling_effect) | B3 ✓ | +| BoardGenTable | (level_index, node_depth)——首个进度键表(depth-0 锚行内建;第三轴落点 = Boss:Dmax 行约定 + elite 命名空间预留 + A1 OQ6 重审;改键退路 = 规则 4 接口漂移通道) | 无(进度成长表) | fog_domain_ok(F2)+ min_safe_feasible(F2b)+ cost_feasible(F2c:attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生) | — | A1 ✓ | +| EnemyTable | (level_index, node_depth, enemy_id)——每行集恰一敌种 | 无(进度成长表,A1 同型) | 每行集恰一敌种(行集内 enemy_id 唯一,违规加载期致命) | — | B2 ✓ | +| HeroTable | 单行全局 | 单行全局定义表(ActionEconomyTable 先例) | atk = 0 加载拒绝("0 基线 × 百分比增益恒零"死锁自 schema 层杜绝) | — | B2 ✓ | +| RewardTable | 按 reward_id(多行;pool_tag 分池不参与键) | 定义表非进度成长表(schema 注释承载) | 池空(pool_tag × 雾/非雾两态各 ≥1 活行)· clear_default 恒正当量 · buff⇒effect_id 必填 · safe_cell 活 buff 行集非空 · floor_supply_missing / no_damage_row(跨 RewardConfigTable) | effect_id 精确引用 StatusEffectTable(已激活) | B4 ✓ | +| RewardConfigTable | 单行全局 | 单行全局常量表(HeroTable / ActionEconomyTable 先例) | k_dmg=0 = F5 断言关闭(现行默认——刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸托底) | — | B4 ✓ | +| SettlementTable | 单行全局 | 单行全局常量/结构表(HeroTable / ActionEconomyTable 先例) | segment_order 恒等拍板段序校验违例致命 · effect_pool_map 键 ⊆ StatusEffectTable 活行集 · p_perfect 载体域 {100,200} 域外加载致命——三行致命 manifest:segorder_violation / poolmap_dangling / pperfect_oob | effect_pool_map 悬空加载期致命 | B1 ✓ | +| AuxItemTable | 按 item_id | 定义表非进度成长表(schema 注释承载) | target ∈ 各空间 effect_kind 支持域交集(A5 承载——真视窄域护栏校验,夹具 a5_target_violation) | counter_item_ids(VariationTable → item_id)首引已定稿〔probe_dust〕、悬空校验激活随 D2×② 对侧批 | A5 ✓(原 HelperItemTable 占位更名转正——A5 定稿登记) | + +### 未回填占位表 + +| 表资源 | 占位内容 | 键结构 | Owner | +|---|---|---|---| +| DifficultyTable | 成长参数 + 精英/Boss 修饰预留命名空间(乘数域 [0.1,10] registry 预登记;越域 clamp+warning = registry 登记的显式例外——组合事故非配置事故,非 fail-fast 场景) | (level_index, node_depth) | C3(第 12 位) | +| MetaTable | 代币/天赋字段名 | 单行全局(元进度轴豁免,C4 可改键) | C4(第 16 位) | + +### MVP 内容行(事实登记归 registry) + +| 表 | MVP 行集 | 值状态 | +|---|---|---| +| ActionEconomyTable | 单行:ap_per_round=8 · dig/插旗/拔旗=1 · rap_refresh_cost=8 · move/chain=0(冻结) | 定稿值,登记归 registry `action_economy` | +| VariationTable | 恰含 fog 行(unlock_level=2 · hide_numbers · safe_nonzero) | 岩浆/冰冻无 MVP 行(垂直切片建行) | +| StatusEffectTable | 四行 attack_up / defense_up / ambush / weaken(全 stack) | 值 provisional,锚验收归 B4/B2/B1;weaken = −65(值域 [60,85]——B1 F0 裁决,条款 2 锚闭合);defense_up = −30(减伤墙批 C3 投值,叠满两层 −60 帽——B3/registry 同批) | +| BoardGenTable | 参考行 L1 / L2 | provisional(C3 调参所有权) | +| EnemyTable | 三敌种行 goblin / bugbear / ogre_chief(C3 修订批转正——registry `enemy.mvp_content` 为准) | 值 provisional——溯源义务见登记与对账义务表 | +| HeroTable | 单行(max_hp / atk=6 原型锚) | max_hp 出处待查;atk provisional 归 C3 | +| RewardTable | 默认根行集八行:defense_up_basic / attack_up_basic / spark_aoe / heal_minor / knife_t1 / knife_adv / heal_pack / clear_blast | 值 provisional 归 C3;格子池四行即时类 + 注入池三行可选类(active_inject——供给控制器承载,原"即时 75% / 可选 25%"权重占比由注入机制替代)+ 清图槽单行 | +| RewardConfigTable | 单行 t_fog_min=2 · k_dmg=0 · n_draft=32 · p_min=8 · active_p0=0.4 · active_decay=0.5 | provisional 归 C3 | +| SettlementTable | 单行默认(D_ref=12 · p_perfect=200 · 拍板段序 · clamp −100/+100 · floor · +4 字段:damage_a_per_cell=1 / damage_a_cap=8 / damage_b_per_flag=2 / armor_effectiveness 克制矩阵——护甲克制批,C3 修订批接口漂移) | provisional 归 C3 | + +## 加载·校验·热重载 + +规则: + +1. 规则 6(出错当场中止):D2 在加载与运行期发现数据出错一律致命中止,不做软回退——错误数值上线比崩溃更伤(P1/P2 信任底线);错误分两类处置(下表)。唯一例外:debug 热重载校验失败时保留旧表停留 Ready、给出错误清单,不杀运行中的调试会话。 + 举证:未播种流取数见边界表 EC7;请求越域见难度行解析前置守卫。两类构建分流见本节「校验双通道」。 +2. 规则 7(零界面依赖):D2 自己的代码与测试一律不 import 任何 Control/场景类——纯数据层不碰表现;没有界面依赖,CI 与无窗口环境才能跑完整战斗。 + 举证:无计算。验收 = 静态扫描 + headless 实跑两项合成判定(AC-10)。 +3. 规则 8(版本拒读):版本常量 `SCHEMA_VERSION` 归 D2;表文件与存档(D3)携带版本号,存档带来的版本号高于当前支持时直接拒绝读取——旧程序不硬猜新格式的数据。 + 举证:无计算。拒读行为见边界表 EC9(比较逻辑归 D3,常量归 D2)。 + +| 错误类别 | 什么情况 | 处置 | +|---|---|---| +| 加载期校验错误 | 启动加载/热重载时查出:字段越界、类型错、NaN、表为空或某 level 无行、缺 depth-0 锚行、重复键、悬空引用、权重和 ≤0、段序违例、池映射悬空、p_perfect 越域 | 经校验双通道处理;启动路径在 editor、CI、headless、release 一律致命 | +| 运行期致命 | 运行中越界请求:关卡/深度坐标越域(解析入口前置守卫拦截)、未设种子就取随机数 | 不分构建一律致命:游戏内走致命错误路径,headless 冒烟进程 quit 非 0 | + +设计意图:MVP 有意不做软回退——带病数值上线比崩溃更伤信任底线。 + +### 校验双通道(规则 6 实现参照:ConfigDB.load_tables(root, collect_only)) + +| 通道 / 机制 | 要点 | +|---|---| +| 游戏通道(collect_only=false,默认) | 校验错误 → push_error 错误清单 → 走致命错误路径(游戏内中止构建;headless 下独立进程非 0 退出——`--script` 协议,设计顺序 #1 时无主场景可依赖);release 构建的致命表现(引擎默认错误路径的实际行为验证)显式 deferred→首次 release 导出 checklist(与 AC-18 同待遇) | +| 测试通道(collect_only=true,仅 debug/测试构建) | 收集错误不升级,供冒烟脚本逐夹具断言错误类别后一次性退出 | +| 夹具注入 | debug/test 构建允许经命令行 `--data-root=<路径>` 让 ConfigDB 从注入根加载(夹具放 tests/fixtures/);禁止把夹具拷进 data/tables/(污染 AC-1 的干净检出前提) | +| 诊断收集器 | resolve / 校验 / override 的 warning 与错误共用同一类目体系,统一经 ConfigDB 内部诊断收集器投递,同时镜像 push_warning 供开发期控制台(Godot 无进程内拦截 push_warning 的 API——冒烟断言必须走收集器);测试通道暴露只读诊断清单与 `reset_diagnostics()`;跨调用去重状态(R1 四元组注册表)随 `load_tables()` 或 `reset_diagnostics()` 清空(用例间无顺序依赖);游戏通道 warning 仅镜像控制台,不承诺收集 | + +### 状态机 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Unloaded | 进程启动前 | 磁盘 .tres 未加载 | +| Loading | `ConfigDB._ready()` 或 `load_tables()` | 按表清单逐一 `load()`;文件缺失即入 Failed | +| Validating | 单个资源加载成功 | 调 schema 的 `validate()`,错误入收集器 | +| Ready | 全部通过 | 类型化属性可读 | +| Failed(启动路径) | 启动加载任一致命错误 | 全构建致命中止并输出错误清单 | +| Failed(reload 路径) | 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单(不杀运行中的调试会话) | +| Reloading(仅 debug) | 编辑器热重载 / 调试键 | 战斗中拒绝(返回 false,战斗结束后手动再触发);否则重走 Loading → Ready | + +不变式:RNG 与 ConfigDB 状态线相互独立——Reloading / Failed(任一路径)不触碰 RngService,计数器与 master_seed 不变(派生只依赖盐/master_seed/scope,与表数据无关;若按状态机对称性在 reload 时重置 RNG 将击穿 AC-4)。 + +### 边界情况 + +| # | 条件 | 行为 | 依据 | +|---|---|---|---| +| EC1 | 表文件缺失或 load() 失败 | 致命中止,错误清单含路径;无软回退 | 规则 6 | +| EC2 | 字段越界 / NaN / 类型不符 | `validate()` 收集后致命 | 规则 6 | +| EC3 | 表为空或某 level 行集为空 | 校验阶段致命(R1 前置条件),不会进入运行期 | 规则 6 | +| EC4 | 请求 (level_index, node_depth) 无精确行 | 按 R1 floor 解析 + warning(按四元组去重)——预期行为,不是错误 | R1 | +| EC5 | level_index 超过表最大 level | 命中最大 level 行(floor 规则的自然结果,无独立 clamp 分支) | R1 | +| EC6 | master_seed 为负 | abs() 归一化 + warning,保证 ∈ [0, 2^63);恰为 INT64_MIN → 致命(abs 恒等回绕,与 0 静默别名——机理是输出掩码抹掉 bit 63) | R2 | +| EC7 | 未设 master_seed 就取随机数 | `push_error` 后显式中止(GDScript 的 push_error 本身不终止进程——游戏内走致命错误路径,冒烟脚本 `quit(1)`),取到的值不可用——C2 未接线的显式暴露,禁止 seed=0 软回退 | 规则 6 | +| EC8 | 战斗进行中请求热重载 | 拒绝并返回 false;战斗结束后手动再触发(不自动重放) | 状态机表 | +| EC9 | 存档携带的 SCHEMA_VERSION 高于当前 | 拒绝读取(比较逻辑归 D3,常量归 D2) | 规则 8 | +| EC10 | 奖励权重表权重和 ≤ 0 | validate 致命(B4 回填后必须 > 0) | 规则 6 | +| EC11 | debug override 类型或范围与 schema 字段不符 | warning + 忽略该条(仅 debug;release 无 override 源) | R3 | +| EC12 | 同一流名被重复注册或请求未知名流 | debug 断言中止(流名封闭) | 规则 5 | +| EC13 | 两个系统同帧请求同一流 | 允许——`open_stream` 各自取号(计数器各 +1)按调用顺序串行生效;单线程假设成立(Godot 主线程玩法逻辑),不引入锁 | 规则 5 | +| EC14 | 请求越域(level_index < 1 或 node_depth < 0) | 规则 6 致命(R1 前置守卫) | R1 | +| EC15 | 某 level 行集缺 depth-0 锚行 | validate 致命(防"入口天灾":入口请求静默 floor 到深行造成难度突刺);写死开、无运行期开关,C3 如需重议走本 GDD 显式修订流程(非 knob) | 规则 6 | +| EC16 | 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单 | 状态机 reload 路径 | + +## 统一取数与调试覆盖 + +规则: + +1. 规则 1(数字只写在表里):凡是设计师要调的数字(行动点、插旗成本、奖励值、敌人数值……)只存在于 data/tables/ 下的 .tres 数值表里,一处定义、处处引用;代码文件里禁止出现玩法数值字面量(引擎/工程常量除外),评审发现写死的玩法数值即评审阻塞项。 + 举证:无计算。违规检测 = 启发式扫描 + 人工复核(AC-14,随各 owner 回填定稿与里程碑 gate-check 执行)。 +2. 规则 2(取数只找 ConfigDB):全部 19 个系统读数值只有一条路——经 autoload ConfigDB 的类型化属性读取(例 `ConfigDB.action_economy.ap_per_round`);禁止绕过它自己 `load()` 表文件或长期缓存副本(副本绕过校验,热重载换新值时它也不知情)。唯一例外:debug/测试构建可从命令行注入的根目录加载。 + 举证:无计算。注入根机制见「校验双通道」夹具注入行。 + +设计意图:单点发放 + 单点读取,热调与校验才有意义。 + +### 调试覆盖链(R3 debug_override,仅 debug 构建) + +**结论**:`effective = override[key] if _has_override(key) else table_value`——仅 debug 构建加载 override 源;v1 仅支持单行全局表字段;类型/范围不符 → warning + 忽略;release 结构性不存在;永不回写 .tres。 + +| # | 边界 | +|---|---| +| ① | 覆盖判定与存储内聚于 ConfigDB(规则 2 单点),gameplay 不可见 override 来源 | +| ② | key = schema 字段点路径(与规则 2 属性路径同构,例 `action_economy.ap_per_round`);未知 key → debug warning + 忽略该条;v1 仅支持单行全局表字段——进度键表的行寻址语法(如 `difficulty.(2,0).hp`)登记为后续扩展,当前 override 对进度表无 key 空间 | +| ③ | override 值类型与范围必须与 schema 字段一致,不一致 → debug warning + 忽略该条(复用 schema 校验逻辑,防止 debug 会话注入越界值污染平衡基线) | +| ④ | release 构建不加载、不注册任何 override 源(恒等是结构保证而非"碰巧不生效");debug 谓词实现为可注入形式(`OS.is_debug_build()` 之外引入 feature 标签闸,例 `sim_release`)——AC-8 的模拟以此为基础 | +| ⑤ | override 仅为运行时状态,永不回写 .tres | + +## 随机数服务(RngService) + +规则: + +1. 规则 5(随机只从 RngService 按流取):全项目玩法代码的随机数只此一家——autoload RngService 持有封闭的三条具名流(下表),流集合封闭、只含具名消费者;要新增随机流(含 B1 若需结算随机)必须改代码走增流通道并通过 P1 支柱评审(随机须有界、可见或可推理);玩法代码禁止 `randomize()` 或自建 RandomNumberGenerator。master seed 由 C2 在冒险开始时设定——同一冒险内相同决策序列必然相同战斗结果。 + 举证:种子派生公式与黄金向量见本节「R2 流种子派生」;依据 = B4 契约源〔AC-1〕· R2 黄金向量。 + +### 封闭流与取数入口 + +| 流 | 消费者 | scope 取号事件 | +|---|---|---| +| board_gen | A1 布雷 | 每次生成新扫雷图(A1 换图时) | +| reward_roll | B4 掉落 | 每次 board install 批(格奖励批 + 清图奖励批)——B4 AC-1 为契约源 | +| encounter | B1 战斗实例化 | 每场战斗实例化恰一 scope(begin_battle 实例化期,扫雷开始前——unit_count 区间均匀骰,挖掘决策域之外 P1 合规;C3 遭遇随机化批增流,registry 已登记) | + +新增流 = 增流通道:改代码 + P1 支柱评审(随机须有界、可见或可推理)。 + +| 取数入口 | 给谁用 | 行为 | +|---|---|---| +| `open_stream(stream) -> RandomNumberGenerator` | 玩法代码(有状态流门面) | 每调用一次 = 一次 scope 取号:该流计数器 +1 → 派生 → 返回已播种实例;一个 scope 事件内所有取数共用该实例 | +| `derive_at(stream, scope)` | 测试/预览(纯函数) | 不推计数器——预览取数不污染正式序列(杜绝预览类取数的平衡 Heisenbug) | + +### R2 流种子派生(stream_seed_derivation) + +**结论**:纯派生函数——同一冒险内(master_seed 固定)同流各 scope 种子互不碰撞;同输入恒同输出;跨 Godot 版本恒定(不用运行时 `hash()`——其跨版本稳定性无承诺);输出 ∈ [0, 2^63)。跨 master_seed 的全局单射不做声明(数学上由鸽笼原理排除)。Output Range:[0, 2^63)。 + +``` +derive_at(stream, scope) = ( SALT_s XOR wrap64(master_seed + scope × GOLDEN_RATIO) ) AND 0x7FFFFFFFFFFFFFFF +wrap64 = GDScript int(64 位二补数)自然回绕 = MOD 2^64 +``` + +#### 实现级规约(必须逐条遵守,否则黄金向量失配) + +| 规约 | 要点 | +|---|---| +| wrap64 | GDScript int(64 位二补数)的自然回绕 = MOD 2^64;禁止 float 中间量(53 位尾数必丢精度)、禁止对中间量使用 `%`(带符号取余语义易误用)、禁止"主动修复溢出"(中间积本就越界——乘数 GOLDEN_RATIO = 0x9E3779B97F4A7C15 > 2^63,scope=1 即回绕,这是设计依赖的语义,不是缺陷) | +| 常量书写 | 三个常量(盐×2 + 乘数)均 ≥2^63,GDScript 十六进制字面量按二补数解析为负——统一用分段表达式书写(例 `(0x9E3779B9 << 32) \| 0x7F4A7C15`,显式括号、不依赖优先级记忆),禁止直接书写 ≥2^63 的完整字面量;注意 GDScript 的 `^` 是按位 XOR | +| 输出掩码 | 经 AND 掩码保证非负(∈ [0, 2^63))后再赋 `RandomNumberGenerator.seed` | + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| SALT_s | int64 | 编译期常量 | board_gen=0xA0761D6478BD642F · reward_roll=0xE7037ED1A0B428DB · encounter=0x8EBC6AF09C88C6E3(互异奇常数,均 ≠ 乘数) | +| master_seed | int64 | [0, 2^63) | 冒险主种子;`set_master_seed()` 对负输入 abs() 归一化并 warning;恰为 INT64_MIN → 致命(abs 在 int64 对 INT64_MIN 恒等回绕,实测与 0 在全流种子逐一相等——别名机理是输出掩码抹掉 bit 63,掩码本身是规约的一部分);仅许全流计数器全零时调用(冒险中途重播种会静默清零计数器,击穿同冒险无碰撞前提与 D3 快照语义 → debug 断言) | +| scope | int64 | ≥0 | 派生入参;有状态门面的取号值(每流独立计数器:set_master_seed() 时归零,每次 `open_stream` +1,永不重置、不随关卡/图序重置——否则不同关卡的同序号图会得到相同种子);负 scope → debug 断言中止(`derive_at` 是预览工具入口,静默负值会与 scope+2^63 经输出掩码别名) | + +**黄金向量(AC-3 的常量依据,经独立复算验证):** + +| 流 | master_seed | scope | stream_seed | +|---|---|---|---| +| board_gen | 42 | 0 | 2339089373825164293 | +| board_gen | 42 | 1 | 4485977604321908752 | +| board_gen | 42 | 2 | 2024629813010603131 | +| board_gen | 42 | 3 | 8849696722413293638 | +| reward_roll | 42 | 0 | 7422916049646135537 | +| encounter | 42 | 0 | 1061841193803499209 | + +- Example:同流同 scope 重复调用 `derive_at` 恒等(纯函数);同帧对同一流连续 `open_stream` 两次 → 计数器各 +1,得两个互异且顺序确定的种子。 + +### RNG 生命周期与快照 + +- RNG 生命周期:进程启动 → 流待命(未播种时取数即致命)→ 冒险开始设定 master_seed → 每次具名 scope 事件派生种子 → 冒险结束保留供复盘。 +- 快照 = master_seed + 各流 scope 计数器(仅记录计数器、缺 master_seed 不足以恢复序列——进程重启会从种子序列起点重放;见 D3 接口)。 +- 快照切出须发生在无在飞 `open_stream` 实例时(scope 事件边界之外):门面实例作用域限定于单次 scope 事件、消费者不得跨事件持有——在飞实例的内部取数进度不入快照,违反则续玩/复盘与实况分叉(AC-15 失效),该前提由 D3 消费侧遵守。 + +## 难度行解析(R1 difficulty_row_resolution) + +**结论**:resolve 恒返回已有行——进度键 floor 解析、实体键精确匹配;不存在 default 行兜底、不存在 clamp 外推;非精确命中记 warning(四元组去重);请求越域在解析前置守卫致命。Output Range:行资源必有值(前置条件由校验保证);极端输入(超表大关卡号)命中最大已有行。 + +``` +row = resolve(table, level_index, node_depth) (ConfigDB 公开解析方法,table 经类型化属性取得) +前置守卫:level_index < 1 ∨ node_depth < 0 → 规则 6 致命(请求越域,不进解析) +L* = max{ lv ∈ LevelKeys(table) | lv ≤ level_index };空集(请求低于表最小键,向上解析)→ L* = min LevelKeys(table) +D* = max{ dp ∈ DepthKeys(L*) | dp ≤ node_depth };空集 → D* = min DepthKeys(L*) +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| level_index | int | ≥1 | 当前关卡序号(1 起);越域 → 致命 | +| node_depth | int | ≥0 | 节点深度(0=关卡入口);越域 → 致命 | +| L* / D* | int | 表内键 | 命中行键 | + +- 无兜底 / 无外推的前置:表非空、每个 level 至少 1 行、每个 level 含 depth-0 锚行是规则 6 校验前置;超表成长外推是 C3 owner 公式的职责,D2 只解析到最近已有行。 +- warning 定位为开发期异常信号(防刷屏去重是刻意的),不是难度审计工具——离线审计由曲线导出工具承担(headless 脚本导出 resolve(level 1..N × depth 0..M) 矩阵供 diff,登记为 C3 期交付物)。 +- Example:表含行 (1,0)(1,4)(2,0)(2,6)——`resolve(3,3)` → L*=2、D*=0 → 返回 (2,0) 行,warning 一条(missing_type=both);`resolve(1,4)` → 精确命中零 warning;`resolve(99,99)` → 命中 (2,6),missing_type=both(与前者去重键不同,各自一条)。 +- 去重键 = (table_id, missing_type, L*, D*) 四元组,内容含请求 (level_index, node_depth)、解析结果 (L*, D*)、缺失类型;去重注册表随每次 `load_tables()` 或 `reset_diagnostics()` 清空。 + +### missing_type(R1 warning 分类枚举) + +| 值 | 判定 | +|---|---| +| base_ceil | level 分量向上解析(请求低于表最小键)——恒记 base_ceil(depth 分量缺失与否只写入 warning 内容,不改类型) | +| level_floor / depth_floor | 单维向下解析 | +| both | level_floor 与 depth_floor 同时发生 | + +- depth 分量只有精确命中或向下 floor 两种情形(EC15 锚行保证每个 level 含 depth-0,永不 ceil);AC-5 各用例的 missing_type 均由本规则推导。 + +设计意图:解析永不发明数据——floor 到已有行并留 warning,外推归 C3 owner 公式。 + +## 依赖与输出 + +**上游(hard)**:无——Foundation 层,D2 不依赖任何系统;master_seed 由 C2 冒险开始时设定。 + +| 方向 | 系统 | 性质 | 接口要点 | +|---|---|---|---| +| 下游 | 全部其他 19 系统 | hard(规则 1) | ConfigDB 类型化读取;禁止其他取数路径(A2 经 A3 表间接承载参数) | +| 下游 | A1 | hard | BoardGenTable 回填(首个进度键表)+ VariationTable schema(biome/tag 字段)+ `board_gen` 流(换图时取号) | +| 下游 | A3 | hard | ActionEconomyTable schema 回填 | +| 下游 | A4 | hard | VariationTable 回填 | +| 下游 | B2 | hard | EnemyTable 回填(十字段 + 三敌种行 + 每行集恰一敌种校验)+ HeroTable 新增(接口漂移回写) | +| 下游 | B3 | hard | StatusEffectTable 回填 | +| 下游 | B4 | hard | RewardTable 回填(九字段 + 跨字段校验族 + 默认根行集,use_timing 承重 MVP 单值)+ RewardConfigTable 新增(单行全局)+ `reward_roll` 流(scope = 每次 board install 批——格奖励批 + 清图奖励批,B4 规则 2 接口漂移) | +| 下游 | B1 | hard | SettlementTable 回填(可回放步骤流的数据前提)+ debug 热重载门标志 `set_reload_gate()`(战斗开始置位 / 结束复位,仅 debug 构建)+ `encounter` 流(战斗实例化期取号恰一 scope——unit_count 区间骰,C3 遭遇随机化批增流) | +| 下游 | C2 / C3 | hard | `RngService.set_master_seed()` + DifficultyTable 行解析 + 曲线导出工具交付物 | +| 下游 | D3 | hard | `SCHEMA_VERSION` + 存档内 master_seed + 各流 scope 计数器快照(续玩/复盘;含只读计数器接口,快照须在无在飞实例时切出) | +| 下游 | A5 / C4 | hard(垂直切片 / Alpha) | AuxItemTable 回填(A5 定稿登记——原 HelperItemTable 占位更名转正;counter_item_ids 表值回填与悬空校验激活随 D2×② 对侧批)/ MetaTable 回填 | +| 下游 | D1 / D4 / D5 / D6 | soft | 如需参数表再增 schema(现无占位);display_name 本地化归 D6 | +| 外发 | —— | —— | 不订阅任何玩法事件;仅 debug 构建发 `config_reloaded`(D1 调试用) | + +- 数据读取类事实依赖不逐条列边(完整清单以本表为准);A2 无直接依赖(只外发事件,参数经 A3 表承载),与 systems-index 的 A2 回边规则一致。 +- 接口漂移修订规则:owner GDD 定稿时可修改占位 schema 类(schema 与表同仓),但必须回写本 GDD 表清单与 `design/registry/entities.yaml`,并在本文件版本历史登记修订;回执登记粒度 = 受影响字段/枚举/流——逐表 diff schema 目录(schema 目录 + 逐表字段行 + MVP 内容行 + 依赖表),禁按"对侧 GDD"粒度整批登记;占位参考值定稿时必须删除本 GDD 调参表清单中的对应条目(值的事实登记归 owner GDD 与 registry)。 +- 双向一致性已与 systems-index 依赖分层表核对一致(核对明细见 analysis 附录);已知待定:C3 是否消费 B1 输出——见索引门禁增补记录,归 C3 GDD 定稿,不影响 D2。 + +### 登记与对账义务(在案项——已闭环项见 analysis 附录) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 4 | counter_item_ids 悬空引用(D2 首个引用型字段)——MVP 不校验,首个道具 id 回填时补校验 + 条件性夹具 | A5 | 条件性(未激活) | +| 5 | EnemyTable MVP 行值(goblin 18HP / 4ATK 等)溯源补登(原型 REPORT,出处待查) | B2 / C3 | 待补 | +| 6 | HeroTable max_hp MVP 值溯源补登(出处待查) | B2 / C3 | 待补 | +| 7 | 胜场成长机制去留(+25% HP / +1 ATK——反雪球问题) | C3 | 待裁决 | +| 8 | 曲线导出工具(headless 导出 resolve 矩阵供 diff)——R1 审计的离线载体 | C3 | C3 期交付物(排期倒挂注意见 analysis 开放问题 #5) | +| 9 | 规则 5 对全项目玩法代码的随机扫描 | Technical Setup | 项目级 CI 项 | +| 10 | release 构建致命表现验证(引擎默认错误路径实际行为) | 首次 release 导出 | deferred(AC-18 checklist) | +| 11 | 战斗级可回放集成测试(同 master_seed 同动作序列 → 步骤流逐行相同) | B1 | deferred(AC-15) | + +## P0 最小闭环 + +1. D2 交付全部 schema 类与占位表(含键结构)——先有格子。 +2. owner 系统 GDD 按设计顺序回填数值(十张已定稿 + 两张占位)。 +3. 进程启动:`ConfigDB.load_tables()` 逐一加载 + schema `validate()`,经校验双通道。 +4. 全部通过 → Ready:19 系统经类型化属性读数,无第二取数路径。 +5. C2 冒险开始 `set_master_seed()`;三条封闭流按各自 scope 事件取号。 +6. debug 构建热重载即改即验(战斗中被门拒绝,结束后手动再触发)。 +7. `godot --headless --script` 冒烟 `quit(0)`——加载、校验、取数、派生全链路绿。 + +## P1 / P2 后置 + +| 优先级 | 内容 | 后置原因 | +|---|---|---| +| P1 | 战斗级可回放集成测试(同 master_seed 同动作序列 → 步骤流逐行相同,AC-15) | D2 阶段无战斗载体,deferred→B1 | +| P1 | 难度曲线导出工具(headless 导出 resolve 矩阵供 diff——R1 审计的离线载体) | C3 期交付物;排期倒挂见 analysis 开放问题 #5 | +| P2 | release 构建致命表现 + override 结构不存在验证(AC-18) | deferred→首次 release 导出 checklist | +| P2 | 进度键表 override 行寻址语法(如 `difficulty.(2,0).hp`) | v1 仅支持单行全局表字段,登记为后续扩展 | +| P2 | 编辑器内表文件热重载自动化 | 低优先、未指派(analysis 开放问题 #2) | +| P2 | gdUnit4 引入与冒烟用例迁移 | Technical Setup 阶段裁决(analysis 开放问题 #1) | + +## 呈现与 UI 面 + +| 项 | 要点 | +|---|---| +| 玩家 UI | 无——MVP 无面向玩家的 UI;debug override 与夹具注入均经 Godot 编辑器 / 命令行参数设置(`--data-root=`,仅 debug),不做自定义面板 | +| 校验失败呈现 | editor / CI 输出控制台(错误清单格式见 AC-1);release 构建走引擎默认错误路径(实际表现验证 deferred→首次导出 checklist) | +| 开发侧可视化 | Godot 编辑器 Resource inspector(自带)+ 校验错误控制台输出 | +| 音频 | 无需求(纯数据层,无直接表现面) | + +## 调参表 + +### D2 自身 knobs(元参数) + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| floor 解析策略 | floor + warning(无外推) | —— | 引入外推/静默会掩盖 C3 难度断档 | +| rng_stream_names | 封闭 3 流(board_gen / reward_roll / encounter) | 增流需改代码 + P1 评审(有意设卡) | 流复用破坏可复现隔离 | +| debug_override_enabled | false(仅 debug 构建) | 污染测试基线 | —— | +| 表路径清单 | data/tables/ 固定(debug 注入根除外) | —— | 散装路径破坏单点访问 | +| depth-0 锚行校验 | 写死开(非 knob,规则 6) | —— | ——(重议须走本 GDD 显式修订,EC15) | + +- Knob 交互:floor 策略与 C3 的难度语义共享,改动需 C3 GDD 知情;校验严格度不设 knob(fatal 写死——放行带病数值会使 CI 冒烟失去意义)。 +- 未决占位与溯源义务(provisional——owner 定稿前不视为平衡事实;定稿后删除对应条目,值的事实登记归 owner GDD 与 registry):EnemyTable 行值溯源(义务 #5)· HeroTable max_hp 溯源(义务 #6)· 胜场成长机制去留(义务 #7)——见登记与对账义务表;已定稿占位参考值(标中旗 / 清图奖励 / 状态四行 / 奖励池等)已随各 owner 定稿清退出本清单。 + +### 表 schema 逐表字段行(D2 核心资产——字段全集以各 owner GDD Schema 节与 registry 为准) + +#### ActionEconomyTable(单行全局 · A3 回填 ✓) + +| 字段 | knob | 说明 | +|---|---|---| +| ap_per_round | ✓ | 每轮行动点 | +| dig_ap_cost | ✓ | 挖掘成本 | +| flag_place_ap_cost | ✓ | 插旗成本 | +| flag_pull_ap_cost | ✓ | 拔旗成本 | +| rap_refresh_cost | ✓ | RAP 刷新门槛 | +| move_ap_cost | ✗ 冻结 | 移动成本 | +| chain_ap_cost | ✗ 冻结 | 连锁展开成本 | +| 跨字段校验 | — | 任一动作成本 ≤ ap_per_round(违规加载期致命,条件性夹具登记) | + +#### VariationTable(按 variant_id · A4 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| variant_id | String 主键唯一 | — | 行键 | +| display_name | String | — | 本地化归 D6 | +| unlock_level | int [1,999] | ✓ | 进度语义承载列(规则 4 豁免载体) | +| scope | region / global | — | global 预留 C3 | +| info_mask | none / hide_numbers | — | 雾行 = hide_numbers | +| cell_constraint | any / safe_nonzero | — | 雾行 = safe_nonzero | +| biome_tag | StringList | ✓ | ∅=通配 | +| counter_item_ids | StringList | ✗ | A5 预留;悬空引用政策见目录表 | +| 承重规则 | — | — | 生成期布置约束:雾区仅非 0 安全格(区内无雷无 0 格,A1 生成期强制)+ 零特判原则 | + +#### StatusEffectTable(按 effect_id · B3 回填 ✓) + +| 字段 | 类型 / 域 | 说明 | +|---|---|---| +| effect_id | String 主键唯一 | 行键;引用校验已激活 | +| display_name | String | 本地化归 D6 | +| target_side | hero / enemy | 作用侧 | +| magnitude | percent ∈ [-100,100] ∧ ≠0 / flat ≥ 1 | 跨字段校验 | +| unit | percent / flat | 量纲 | +| duration_settlements | [1,99] | 持续结算次数 | +| stack_policy | stack / refresh / independent | 叠层策略 | +| max_stack | [1,16] | 仅 stack 消费 | + +#### BoardGenTable((level_index, node_depth) 首个进度键表 · A1 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| board_width / board_height | int [8,64] | ✓ | 板尺寸 | +| mine_density | float (0,0.5] | ✓ | 密度浮点(尺寸解耦);布雷数下限 round(d×(W×H−9)) ≥ 1 | +| inferability_target | float [0.85,1] | ✓ | P1 门槛承重参数 + 绝对锚 | +| solver_rules | single / single_subset | ✗ 冻结 single_subset | 推理定义域非调参 | +| min_safe_count | int [1,999] | ✓ | 退化板对冲 | +| max_gen_attempts | int [1,32] | ✓ | 生成-验证循环上限 | +| fog_region_count | int [0,32] | ✓ | 雾区数 | +| fog_target_ratio | float [0,1) | ✓ | ≤ min(8d, 0.25) 绝对上限 | +| board_biome | StringSet 非空 | — | 域校验致命(词表 owner 未定稿前只校验非空) | +| elite_ / boss_ 前缀 | 命名空间预留 | ✗ | 第三轴登记约定——语义归 C1/C3,本 GDD 不定义 | +| 跨字段谓词 | — | — | fog_domain_ok(F2)+ min_safe_feasible(F2b)+ cost_feasible(F2c:attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生)——加载期致命 | + +#### EnemyTable((level_index, node_depth, enemy_id) · B2 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| enemy_id | String(行集内主键) | — | 行集内唯一(每行集恰一敌种) | +| display_name | String | — | 本地化归 D6 | +| unit_count | int [1,3] | ✓ | 遭遇区间下界(C3 修订批——区间 [unit_count, unit_count_max] 经 B1 encounter 骰定本战实际数量;新敌种首遇节点固定不加骰) | +| unit_count_max | int [unit_count,3],可省略 | ✓ | 遭遇区间上界(缺省 = 定值,旧行零迁移——C3 修订批新增字段) | +| armor_type | enum {none, a, b},缺省 none | ✓ | 护甲类型(克制矩阵 = SettlementTable.armor_effectiveness;MVP 映射 goblin=none / bugbear=a / ogre_chief=b——护甲克制批) | +| hp | int [1,999] | ✓ | 难度修饰前基线 | +| atk | int [0,999] | ✓ | attack 行为伤害基值;0 = 纯伏击伤敌种合法 | +| heavy_multiplier | int [1,16] | ✓ | 无 heavy 拍敌种不消费(非校验项先例) | +| pattern | StringList,环长 [1,8] | — | MVP 校验域 {attack, charge, heavy}——guard/boost/hinder/summon 预留值校验拒绝 | +| advantage_per_round_cap | int [0,16] | ✓ | 默认 4 定稿(反灾难护栏);0 = 免疫伏击合法极端 | + +#### HeroTable(单行全局 · B2 回填 ✓ · 接口漂移新增) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| max_hp | int [1,999] | ✓ | MVP 值 provisional(出处待查) | +| atk | int [1,999] | — | 0 = 加载拒绝(基础攻击恒存在,"0 基线 × 百分比增益恒零"死锁自 schema 层杜绝);MVP = 6 原型锚 provisional 归 C3;消费面 = 主角战斗段先手攻击 | + +#### RewardTable(按 reward_id · B4 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| reward_id | String 主键 | — | 行键(多行;pool_tag 分池不参与键) | +| category | buff / damage_item / heal_pack / instant_aoe / instant_heal | — | 两类交付轴:即时类 {buff, instant_aoe, instant_heal} 当场兑付不进库存 / 可选类 {damage_item, heal_pack} 入关卡域库存;预留值校验拒绝 | +| effect_id | String 可空 | — | buff 类必填(StatusEffectTable 精确引用);非 buff 恒空 | +| display_name | String 非空 | — | D1 呈现数据源,本地化归 D6(B2/B3 同库惯例) | +| power | int [0,999] | ✓ | item 类加载校验 ≥1;buff 类不消费非校验 | +| weight | int [0,999] | ✓ | 0 = 不入池 | +| pool_tag | safe_cell / active_inject / clear_default | — | clear_\ 命名空间预留拒绝;active_inject = 供给控制器注入池(可选类三行——install 期注入,非逐格 roll) | +| tier | int [1,8] | ✓ | A4 雾档约束载体 | +| use_timing | pre_settlement(MVP 单值) | — | 承重——道具使用窗口 MVP 激活,扩枚举走修订 | + +#### RewardConfigTable(单行全局 · B4 回填 ✓ · 接口漂移新增) + +| 字段 | 类型 / 域 | 默认 | 说明 | +|---|---|---|---| +| t_fog_min | int [1,8] | 2 | F2 雾档过滤门限 | +| k_dmg | int [0,999] | 0 | 0 = F5 断言关闭(现行默认——刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸托底) | +| active_p0 | float [0,1] | 0.4 | 供给控制器注入概率初值(首件之后每张新板)——E[战斗获取] ≈ 1 标定目标,归 C3 | +| active_decay | float (0,1] | 0.5 | 注入概率指数递减 0.4→0.2→0.1→…(加难杠杆候选 0.6 随 C3 OQ7 扫描) | +| n_draft | int [1,999] | 32 | F5 草稿重 roll 上限 | +| p_min | int [1,999] | 8 | 默认根最低刀 power——K≥1 供给闸刀力下界(floor_supply_missing) | + +#### SettlementTable(单行全局 · B1 回填 ✓ · 共十二字段) + +| 字段 | 类型 / 域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| equivalence_ref_damage(D_ref) | int [1,999] | 12 | ✓ | 当量参考敞口;变更触发 B1 全对照表 + 条款③⑤对账重跑(禁独立调参) | +| p_perfect | percent {100,200} | 200 | ✓ | 完美排雷清图奖励第二份兑付触发——非整数倍数乘数无运行时载体,域外加载致命 | +| segment_order | StringList | 拍板段序 | ✗ 结构冻结 | 恒等拍板段序校验,违例致命(防实现乱序静默漂移) | +| source_label_map | Dictionary source_class→文案键 | 封闭枚举全覆盖 | ✗ 结构冻结 | P2 归因呈现映射;键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack};新增键 = 接口漂移 | +| effect_pool_map | Dictionary effect_id→{hero_offense, hero_defense, enemy_output, flat_attach} | 四键定稿映射 | ✗ 结构冻结 | 效果语义映射;键 ⊆ StatusEffectTable 活行集,悬空加载期致命;新 effect_id 上线须随批登记 | +| pool_clamp_lo | int pp | −100 | ✗ 结构冻结 | percent 池地板域下界(乘数域随之 [0,2]) | +| pool_clamp_hi | int pp | +100 | ✗ 结构冻结 | percent 池地板域上界 | +| round_direction | enum {floor} | floor | ✗ 结构冻结 | 单一取整规则 | +| damage_a_per_cell | int ≥ 0 | 1 | ✓ | 伤害A 开格充能/格(护甲克制批——充能与结算归 B1 战斗段〔F6〕,C3 投值) | +| damage_a_cap | int ≥ 0 | 8 | ✓ | 伤害A 单轮充能封顶(0 = 不封顶——防连锁轮失控) | +| damage_b_per_flag | int ≥ 0 | 2 | ✓ | 伤害B 标雷充能/首次标中雷(每雷实例一次去重) | +| armor_effectiveness | Dictionary {base, dmg_a, dmg_b} × {none, a, b} | 特化 ×150 / 无甲 ×100 / 对位 ×50 | 键集 ✗ 结构冻结 / 数值 ✓ | 伤害类型 × 护甲克制矩阵——主角攻击三分量(基础/伤害A/伤害B)各自查表后 floor 相加;敌种甲型映射归 EnemyTable.armor_type | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | `--headless --script` 冒烟框架(脚本 extends SceneTree 以获得 quit(code);该模式 autoload 须由脚本显式实例化——AC-1 前提声明;设计顺序 #1 时无主场景可依赖) | +| 覆盖 | 8/8 Core Rules;规则 6 加载期校验错误类别全部有夹具(悬空引用为条件性夹具,已随 B4 首引激活);运行期致命(请求越域 EC14、未播种 EC7)由 AC-5b / AC-17 行为级覆盖;R1 / R2 全部边界 | +| 致命类断言协议 | 致命用例(AC-2b、AC-5b 游戏通道半、AC-6 后半、AC-11、AC-17)各为独立进程调用,冒烟 runner 清单逐条登记"调用 → 期望退出码";单进程内只做测试通道收集断言 | +| warning 断言 | 一律经诊断收集器(见校验双通道),每用例先 `reset_diagnostics()` | +| 人工与延迟 | AC-14 人工 · AC-15 deferred→B1 · AC-18 deferred→首次 release 导出 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 干净检出 | `godot --headless --script tests/smoke_data.gd` | 核心规则 #3 表清单的全部表(以清单为准,不写死数量)加载且 data/tables/ 无清单外 .tres;脚本成功 `quit(0)`,任一失败 `quit(1)` 并输出错误清单(表路径 + 字段 + 错误类别) | 规则 3/6 | +| AC-2 | tests/fixtures/ 夹具集(越界字段 / 文件缺失 / 空表 / 某 level 无行 / 缺 depth-0 锚行 / 重复键 / 权重和 ≤0 各一;悬空引用为条件性夹具——已随 B4 首引激活) | 冒烟脚本经 `--data-root` 注入逐夹具运行 `load_tables(root, collect_only=true)`(测试通道,单进程逐个断言) | 每个夹具的错误清单含对应类别(EC1 / EC2 / EC3 / EC15 / EC10 及重复键类别) | 规则 6 / EC | +| AC-2b | 任一致命夹具经注入根加载 | 冒烟 runner 以独立 headless 进程执行游戏通道 `load_tables(root, collect_only=false)`(`--script` 协议,脚本显式实例化 autoload;不依赖主场景) | 进程非 0 退出且错误清单完整——验证"游戏内真的会死"而非仅"校验器能发现" | 规则 6 游戏通道 | +| AC-3 | R2 黄金向量表(6 常量) | 纯函数 `derive_at` 复算(board_gen master=42 scope 0–3、reward_roll master=42 scope=0、encounter master=42 scope=0);同流同 scope 重复调用 | 逐个等于向量表常量;重复调用恒等(纯函数无副作用) | R2 | +| AC-4 | master_seed=42 | `open_stream("board_gen")` 连续取号 ≥100 次(跨模拟"关卡切换");同期调用任意 `reload()` | 公开只读计数器单调不重置(各关同序号图种子互异);计数器不变(状态线独立) | R2 / 规则 5 / 状态机表 | +| AC-5 | 真实 DifficultyTable schema 构造的表含行 (1,0)(1,4)(2,0)(2,6)(每用例先 `reset_diagnostics()`) | 依次 `resolve(3,3)` → `resolve(3,4)` → `resolve(1,4)` → `resolve(99,99)`;单行表 {(2,0)} 上 `resolve(1,5)` | (3,3):返回 (2,0) 行且诊断收集器 warning 恰一条(missing_type=both,含请求/结果/类型);(3,4):warning 不新增(四元组去重键相同);(1,4):精确命中且零 warning;(99,99):命中 (2,6) 且新增恰一条(去重键含 D*,与首条不同);单行表:命中 (2,0) 且 missing_type=base_ceil(由 R1 分类规则推导:level 分量向上解析恒记 base_ceil) | R1 | +| AC-5b | 真实 DifficultyTable schema 构造的合法表 | `resolve(0, 3)`(level_index < 1)与 `resolve(2, -1)`(node_depth < 0);同用例经游戏通道独立进程复跑 | 测试通道诊断清单各含"请求越域"致命类别一条且不进入解析(EC14 / R1 前置守卫);独立进程非 0 退出 | R1 / EC14 | +| AC-6 | master_seed=-7;master_seed=INT64_MIN;`derive_at` 传入负 scope | `set_master_seed()`;`set_master_seed()`;`derive_at` | 归一化为 7 + 诊断收集器 warning 一条,后续种子 ∈ [0, 2^63)(EC6);INT64_MIN 致命(非 warning;独立进程断言退出码非 0);负 scope debug 断言中止(负值经输出掩码与 scope+2^63 别名) | R2 / EC6 | +| AC-7 | debug 构建 override{"action_economy.ap_per_round"=12}(域内值——A3 schema 域 [1,32];越域 override 会被 R3③ 忽略,出域示例会使本 AC 假失败) | 经 ConfigDB 读取;override key 为未知路径、类型或范围与 schema 不符;`reload()` 后 | effective=12;诊断收集器 warning + 忽略该条;.tres 文件内容不变(R3 边界①③⑤) | R3 | +| AC-8 | 非 debug 谓词模拟(feature 标签) | `_has_override(key)` | 恒 false——本条仅证明门存在性;"release 不加载 override 源"的结构保证验证 deferred→首次 release 导出 checklist | R3④ | +| AC-9 | D2 文件集(data/schemas、ConfigDB、RngService、tests) | 静态扫描(模式覆盖 `load(`、`preload(`、`ResourceLoader.load`、`"res://data/" +` 动态拼接;`res://tests/fixtures/` 前缀豁免——不在 `res://data/` 扫描域) | 无 `randomize()` 调用、无 ConfigDB 之外的 data 表加载(规则 2/5);规则 5 对全项目玩法代码的扫描登记为项目级 CI 项(义务 #9) | 规则 2/5 | +| AC-10 | D2 文件集 | 静态扫描 + AC-1 headless 实跑 | 无 `extends Control/Node2D`、无 `.tscn` preload,且 AC-1 headless 实跑通过——两项合成判定规则 7(扫描是启发式,硬证据是 headless 实跑) | 规则 7 | +| AC-11 | RngService(debug 构建) | 请求未知名流或重复注册流名 | debug 断言中止(EC12;独立进程退出码非 0) | 规则 5 / EC12 | +| AC-12 | 同帧对同一流 | 连续两次 `open_stream` | 两个互异且顺序确定的种子(EC13) | 规则 5 / EC13 | +| AC-13 | 冒烟脚本运行 | 反射断言 | `SCHEMA_VERSION` 常量存在且 ConfigDB 可读;全部 schema 携带版本字段;进度类表(BoardGen/Enemy/Difficulty)暴露 (level_index, node_depth[, enemy_id]) 键字段;DifficultyTable schema 暴露 `elite_` / `boss_` 前缀预留字段("高版本拒读"路径 deferred→D3) | 规则 8 + 规则 4 键契约 + 第三维命名空间 | +| AC-14(人工) | 每次回填定稿 | 验收人复核 | 规则 1 数值字面量禁令成立:启发式扫描(warning 级,误报难免)+ 人工复核三要素——验收人 = owner GDD 回填定稿评审 + 里程碑 gate-check;时机 = 每次回填定稿;证据 = 评审记录 / gate-check 登记 | 规则 1 | +| AC-15(deferred→B1) | 同 master_seed 同动作序列 | B1 集成测试重放 | 结算步骤流逐行相同(D2 阶段无战斗载体) | 规则 5 可回放 | +| AC-16 | debug 构建且 B1 侧经 `set_reload_gate(true)` 置位(模拟战斗中);门未置位;reload 后新表校验失败 | `reload()` | 置位:返回 false、ConfigDB 值不变、不发 `config_reloaded`;未置位:返回 true 且新值经类型化属性可读且 `config_reloaded` 发出恰一次(仅 debug 构建);校验失败:旧表保留、状态停留 Ready(EC16) | 状态机表 / EC16 / 规则 6 | +| AC-17 | 未设 master_seed | `open_stream` 或 `derive_at` | push_error + 冒烟通道断言中止路径(quit 非 0)(EC7) | 规则 6 / EC7 | +| AC-18(deferred→首次 release 导出) | release 模板构建 | 致命校验错误发生;查找 override 源 | 表现符合"引擎默认错误路径"预期;override 结构性不存在(R3 边界④、校验双通道 release 侧) | 规则 6 / R3④ | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v2 | 2026-09-14 | 两件套格式迁移:按模板 v2 骨架重排——头部收敛为状态/版本/一句话/推演件指针;设计推演(公式推导论证、数值锚定论证、候选取舍、体验锚点论证、已裁决争议)移至 `design/gdd/analysis/d2-data-config.md`;评审元数据与已闭环对账义务(#1/#2/#3/#12)入 analysis 附录;规则、数值、枚举、EC、AC、OQ 编号与内容零变更 | +| 跨评审 D2 漂移批 | 2026-09-14 | /review-all-gdds 🔴-2 五处补漂移修订批(X-8 遗漏项补登,registry 同批):**RNG 流封闭清单 2→3 流**(encounter——消费者 B1,scope = 每场战斗实例化恰一 scope;Core Rule 5 / 取数链路 / 盐值 0x8EBC6AF09C88C6E3 / 黄金向量 +1 常量〔AC-3 五→六〕/ set_master_seed 全流零计数前提)· **pool_tag 枚举增 active_inject** · **RewardConfigTable +2 字段**(active_p0=0.4 / active_decay=0.5)+ k_dmg 默认 1→0 且"C3 禁区默认值"条款解除 · **默认根行集 clear_knife→clear_blast**(注入池占比注记替代 75/25)· **HelperItemTable 更名 AuxItemTable**(A5 定稿转正,schema 目录九→十张)· X-8 补登:SettlementTable 逐表字段行补 4 字段(共十二字段)、EnemyTable MVP 行三敌种登记 · 接口漂移修订规则增"回执按受影响字段/枚举粒度逐表 diff schema 目录"流程纪律;核心规则表增依据列(lint R8 引用列迁移) | +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-8 回执,registry 同批):**EnemyTable schema +2 字段**(unit_count_max——遭遇区间上界 / armor_type——护甲类型枚举 {none,a,b},克制矩阵挂 SettlementTable)· **SettlementTable +4 字段**(damage_a_per_cell=1 / damage_a_cap=8 / damage_b_per_flag=2 / armor_effectiveness 克制矩阵——护甲克制批转正,机制与结算归 B1、C3 投值)· DifficultyTable 表清单行补乘数域与 clamp 例外注记(registry 登记的组合事故例外,非 fail-fast 场景)· 敌种键更名 goblin/bugbear/ogre_chief(DND 批对齐) | +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;核心规则 / 枚举 / 边界 / AC 转表格;加载校验与热重载链路转字符画;九张表 schema 逐表字段行入 Tuning Knobs;义务与夹具登记收敛为登记与对账义务表。正文残留旧称按 registry 与各 owner 成稿写现行值:SettlementTable 八字段口径(pool_clamp 拆 lo/hi)、p_perfect 载体域 {100,200}、source_label_map 键集含 hero_attack、RewardTable category 五值与默认根八行集——语义零变更 | +| 两级奖励批 + 基础攻击裁决 | 2026-09-11 | RewardTable category 三值扩为五值(即时 / 可选两类交付轴)+ 默认根行集重登记八行(原五行为集退役);RewardConfigTable p_min 默认 4→8;HeroTable atk 改域 [1,999] 且 0 = 加载拒绝(基础攻击恒存在),消费面 = 主角战斗段先手攻击 | +| B1 回填 + 评审批收编 | 2026-09-10 | SettlementTable 定稿回填(registry 原记七字段);同日评审批统一八字段口径——pool_clamp_lo_hi 拆 lo/hi 两字段、p_perfect 域 [100,999] 改载体域 {100,200}、source_label_map 键集补 enemy_action / flag_miss(hero_attack 随 B1 成稿) | +| B4 full 评审 delta | 2026-09-09 | RewardTable 增 display_name(九字段)+ 默认根行集登记 + RewardConfigTable 新增(单行全局四字段,接口漂移——B4 标量 knob 的 Rule 1 载体)+ Core Rule 5 reward_roll 语义正文补同步(每次 board install 批) | +| B4 回填定稿 | 2026-09-09 | RewardTable 八字段 + 跨字段校验族 + use_timing 占位转承重 + 首个 effect_id 引用回填激活 StatusEffectTable 悬空校验 + reward_roll scope 语义改写 + Tuning Knobs 奖励池占位条目清退 | +| B2 回填定稿 | 2026-09-09 | EnemyTable 八字段 + 双敌种行 + 每行集恰一敌种校验;HeroTable 新增(接口漂移回写) | +| A1 full 评审 delta | 2026-09-09 | BoardGenTable inferability_target 收窄 [0.85,1](P1 绝对锚)+ fog_target_ratio 绝对上限 min(8d, 0.25) + 布雷数下限 round(d×(W×H−9)) ≥ 1 + F2c 成本谓词(运行期禁墙钟逃生)+ elite_/boss_ 命名空间预留(第三轴登记约定,Boss=Dmax 行 + A1 OQ6 重审门) | +| A1 回填定稿 | 2026-09-09 | BoardGenTable 九字段 + F2/F2b 跨字段谓词 + solver_rules 冻结 + MVP 参考行(provisional,C3 调参所有权)——首个进度键表 | +| B3 回填定稿 | 2026-09-08 | StatusEffectTable schema 八字段 + 四行 MVP 内容(键 = effect_id,Rule 4 豁免注明;effect_id 悬空校验待首引激活) | +| A4 re-review delta | 2026-09-08 | VariationTable 增跨字段校验 hide_numbers⇒safe_nonzero(加载期致命,夹具 variation_mask_constraint)+ counter_item_ids knob 误标修正(非 knob) | +| A4 回填定稿 | 2026-09-08 | VariationTable 八字段 + fog 行 + Rule 4 豁免注明(registry 新条目 variation_table / variant_pool_gate) | +| A3 三轮评审 delta | 2026-09-07 | ActionEconomyTable 增跨字段 validate 规则(任一动作成本 ≤ ap_per_round,schema 类承载、违规致命、条件性夹具登记) | +| A3 RAP 结构修订 | 2026-09-07 | flags_per_round / flag_unit_cost / stop_multiplier / clear_multiplier 四字段删除(旗无上限、停手 / 清图折算乘数退役)、rap_refresh_cost 新增;AC-7 示例值域内修正(99 出域会使本 AC 假失败) | +| A3 回填定稿 | 2026-09-07 | ActionEconomyTable 字段清单交付 + 成本三字段入表 + 冻结字段(ap_per_round=8 等定稿值登记 registry) | +| 收口 | 2026-09-07 | 两轮评审 + delta 复核 5/5 PASS 后 Approved;CD-GDD-ALIGN 终裁 NEEDS REVISION → 修订落实 → 独立 delta 核对通过 | +| 原型占位值清退 | 2026-09-07 起随各 owner 定稿 | 标中旗 +5 / 面旗(量纲与语义经 A3 OQ5 重锚 → weaken 行)、清图 +10(→ B4 清图奖励插槽)、增伤 1.5× / 减伤 0.5× / 持续 2 次结算(→ StatusEffectTable 四行)、奖励池 5 类(→ RewardTable 五值)——机制与数值事实登记归 owner GDD 与 registry | diff --git a/design/gdd/game-concept.md b/design/gdd/game-concept.md new file mode 100644 index 00000000..9be3d4e4 --- /dev/null +++ b/design/gdd/game-concept.md @@ -0,0 +1,157 @@ +# 游戏概念:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike) + +> 参考《弹珠哥布林》(Peglin):两段式回合循环(小游戏 → 战斗结算)+ 道具跨循环累积(**B4 定稿口径 2026-09-09:战斗域内跨轮次跨图累积、battle_over 全清不跨战斗**——与 Peglin 跨战斗持有不同)+ Roguelike 闯关结构。(**2026-09-11 关内延续裁决改写**:库存改为关卡域——跨战斗延续、关卡末清空/折算;本注记前半作废) +> 我们的区别:小游戏层是"扫雷类推理玩法"(非物理弹珠),玩家 agency 全部前移到战斗前的排雷规划(2026-09-09 注:B4 激活轮末道具使用窗口后,agency 扩展至轮末资源决策——规划前移的主体定位不变)。 + +## 世界观与命名(定稿 2026-09-09;2026-09-12 概念层增补:隐喻换轴 + 惩罚格类型化) + +> **版本注记(2026-09-12 概念层增补,用户拍板;依据记录 `design/research/lore-nest-metaphor-2026-09-12.md` ⑤节六问)**:叙事属名换轴——雷格 = **惩罚格**(对外叙事称呼;机制名"雷/雷格"不变),惩罚格有不同类型,首发地牢类型 = **怪物巢穴**;插旗的玩家可见动词 = **控制**(类型无关;对内文档与机制协议仍为"旗/插旗");连锁展开 = **坍塌显形**;标错惩罚叙事 = **控制失败的失控反噬**(随机读作失控,重复性措辞随 A2 OQ1);宝箱呈现仅绑定可使用道具类奖励;世界观厚度 = 碎片点缀(不含前人日志)。原 2026-09-09 映射(雷 = 地牢陷阱、旗 = 探险者标记的已知陷阱)作废;"陷阱"降级为惩罚格类型词表的一员。本修订为表现层/命名层,不改变任何已锁机制语义、数值与接口(A1–B4、B1、C3 已定稿内容不受影响);惩罚格类型化的机制分化(陷阱→直接扣血、标中陷阱→怪物受伤害、岩浆/冰冻化身等)为**已登记的未来机制侧方向**(与现行"开雷→敌方优势非直接伤害"锁定规则的取舍归 A1/A2 修订裁决),不在本表承诺。 + +**产品名:Dungeon Sweeper**——命名取 minesweeper → dungeon sweeper 的双关:玩家不只是"扫雷者",更是"清扫地牢的人"(清巢者——逐层肃清巢穴侵染、夺回地牢)。"扫雷 × 自动战斗 闯关 Roguelike"保留为品类描述,用作各文档副标题。 + +**背景一句话**:玩家操纵一名地牢探险者,逐层下探一座被怪物巢穴侵染的古代地牢;每一层地牢就是一张"雷图"——惩罚格密布的古老地砖阵,探险者凭数字提示感知惩罚格方位,控制巢穴、搜刮前人遗落物,才能安全下探更深的层。 + +机制 ↔ 地牢隐喻映射(表现层包装,不改变任何已锁机制语义,A1–B3 已定稿数值/接口不受影响): + +| 玩法元素 | 地牢隐喻 | +|---|---| +| 扫雷图 | 地牢的一层(一间被巢穴侵染的厅室;清图 = 肃清本间厅室) | +| 雷 | 惩罚格(叙事属名;含不同类型,首发类型 = 怪物巢穴——惊动即怪物倾巢而出占得先机:伏击/增益,P3 风险同源;巢已空、残址可通行。陷阱/岩浆/冰冻等类型及其机制分化 = 未来方向归机制侧) | +| 旗 | 对惩罚格的**控制**(玩家可见动词,类型无关;对内仍为"旗/插旗")——标中 → 巢穴废置、巢主失庇护(怪物 debuff);标错 → 控制失败、反噬流向不可控(措辞细则随 A2 OQ1);"封印/镇/反制"为类型风味词;视觉形态换皮归 art-bible | +| 小人 | 地牢探险者 | +| 清图 | 本层探明、搜刮前人遗落物完毕(清图奖励),下探更深 | +| 关卡推进 | 不断下探更险的层(P4 支柱) | + +- 首发 biome = 地牢(与 A1 BoardGenTable 参考行占位值 `{"dungeon"}` 一致;biome 词表定稿权仍在 B4/C1+AD,此处仅为方向背书) + +## 目标受众(定稿 2026-09-06) + +- **核心受众**:策略推理玩家 × Roguelike 构建玩家的交集——喜欢杀戮尖塔/小丑牌的构筑决策,喜欢扫雷/数独的纯逻辑掌控感 +- **次级受众**:Peglin 玩家中觉得"弹珠太看脸"的群体;怀旧扫雷玩家中寻找进化的群体 +- **平台**:PC(Steam)先行;回合逻辑玩法,对配置无要求 +- **单局预期**:一场冒险 20–40 分钟,单关卡 5–10 分钟——碎片时间可完成一场战斗 + +## 视觉身份锚(Visual Identity Anchor,定稿 2026-09-06) + +**一句话视觉规则:地形是氛围,数字是主角——一切视觉服务"信息可读"而非"场景华丽"。** + +支撑原则: +1. **数字可读性硬指标优先于氛围**:暗色地牢底色上的数字必须达到高对比(具体色值在 art-bible 定),任何纹理不得干扰数字辨识 +2. **变异格形状+颜色双通道**:迷雾/岩浆/冰冻等变异不能只靠色相区分(色弱可用性),必须绑定图标或形状冗余 +3. **绘制优先级固定**:数字 > 小人标记 > 旗子 > 变异纹理 > 底色——小人与旗不得遮挡数字(小人所在格用边框高亮而非覆盖文本) +4. **调色板交换优先于新增资产**:关卡/biome 换皮优先做色板变体,控制个人开发资产成本;A1 棋盘生成须预留 biome/tag 数据字段 +5. **格子 = 地牢地砖(2026-09-09 用户定稿)**:扫雷格子的美术基底做成地牢一格一格的石砖/地砖样式(候选表现:未开 = 覆尘完整地砖、已开 = 碎裂/裸露地面、雷 = 被惊动的巢穴/崩塌的巢址【2026-09-12 隐喻换轴增补:原"触发的陷阱机关"为旧称;候选词与已定的"坍塌显形"连锁叙事对齐】);具体形态由"格子样张测试"定稿,样张仍以数字可读性为第一验收项(早期风险第 1 条不变);与原则 4 衔接——不同 biome = 同款地砖的不同色板 + +早期风险登记(详见 art-bible 任务): +- 扫雷数字可读性 × 地牢暗色氛围的冲突 → 需一次格子样张测试定风格 +- 格子信息密度:小人/旗/数字/变异同格叠加 → 按上述优先级规则处理;小人移动路径预览的表现归属在 D1 GDD 解决 +- **双舞台资产管线陷阱**:战斗发生在棋盘上还是独立场景,是 B5 最大的成本变量 → 必须在 B5 GDD 前的"战斗表现美术测试"(一屏样张)中锁定 + +## 术语层级(定稿 2026-09-06 · 2026-09-14 增补行动层) + +| 术语 | 定义 | +|---|---| +| **存档** | 元进度。所有"冒险"都在某个存档内进行。存档内可用代币解锁天赋等永久提升 | +| **冒险** | 开启进入的一次完整游戏(run)。包含多个"关卡"。死亡则冒险结束(Roguelike) | +| **关卡** | 冒险内的一个大节点。有一个关底 Boss。完成关卡 → 获得"冒险"内大奖励 + 解锁天赋用的"代币" | +| **战斗** | 关卡内移动时碰到非奖励点触发。一次完整的战斗遭遇,包含若干个"轮次"和若干张"扫雷图" | +| **轮次** | 战斗内的原子回合单位。玩家用本轮 AP 在扫雷图上挖掘 → 轮次结束 → 自动战斗结算。敌人未全灭且主角存活 → 下一轮次 | +| **扫雷图** | 跨轮次延续的棋盘状态。AP 用完但未开完 → 下一轮次继续同一张图(AP 重置,棋盘状态保留)。全部安全格开完 → 清图奖励 → 结算后换新图 | +| **行动** | 轮次内挖掘期的原子决策单位(2026-09-14 增补)。有价行动(挖 / 插旗 / 拔旗)各耗 1 AP、受每轮 AP 预算约束(现行表值 8);免费行动(移动 / 停手)不限次数。同一事件的三个视角:行动 = 玩家决策 · 意图 = 进入 A2 的数据形态(单意图帧整批提交)· 动作 = A3 计价类别(action_kind 统一价目) | + +### 对应关系(定稿) + +``` +存档 ⊃ 冒险 ⊃ 关卡 ⊃ 战斗 ⊃ { 轮次 ×N , 扫雷图 ×M } +轮次 ⊃ 行动 ×K(挖掘期) +``` + +- 战斗 : 轮次 = 1 : N —— 敌人未全灭且主角存活则继续 +- 扫雷图 : 轮次 = 1 : N —— 一张图跨越多个轮次,直到开完 +- 战斗 : 扫雷图 = 1 : M —— 每次清图后换新图,战斗内可经历多张图 +- 行动 : 轮次 = K : 1 —— 挖掘期原子决策;有价行动(挖/插旗/拔旗)各耗 1 AP、受每轮 AP 预算约束(现行表值 8),移动/停手免费不计 AP + +## 轮次流程(定稿) + +1. **挖掘阶段**:用本轮 AP 开格/标旗(棋盘为延续的旧图或刚换的新图) +2. **轮次结束**(三种方式任一触发): + - AP 用完(图未开完 → 下轮继续这张图) + - 主动停手(剩余 AP 1:1 转存 RAP 池——攒满可刷新雷图的风险退出资源,**不作战斗资源**【2026-09-07 A3 RAP 修订,原"折算战斗资源"作废】;图未开完 → 下轮继续这张图) + - 图开完 → 清图奖励(剩余 AP 作废不折算【同日修订】;下轮换新图) +3. **自动战斗结算**:每轮结束时都结算一次;开过的雷在此时兑现敌方优势 +4. **判定**:敌人全灭 → 战斗胜利;主角死亡 → 战斗失败;否则 → 下一轮次 + +## 核心循环 + +``` +存档(天赋/代币) + └─ 冒险(run,死亡即终) + └─ 关卡(节点地图移动,Boss 收尾) + └─ 战斗(碰到非奖励点触发) + └─ 轮次 ×N(敌未灭/己未亡则继续) + ├─ 挖掘期 = 行动 ×K(挖/旗 1 AP · 移动/停手免费) + └─ 扫雷图(有限 AP 挖掘) + ├─ 安全格 → 增益/道具(多为限时或一次性) + ├─ 雷 → 敌方获得优势(伏击/增益) + └─ 全部安全格点开 → 图完成 → 自动战斗结算 +``` + +## 扫雷层规则(当前共识) + +- **开格消耗 AP,连锁展开(flood fill)免费**;每轮 AP 固定(原型 v2 起 8 点,v1 的 12 点被测评为太多) +- **行动经济统一(v3,2026-09-07 A3 RAP 修订)**:插旗消耗 1 AP(**旗子上限取消**——旗为棋盘标记、数量不限,稀缺性由 AP 机会成本承载【原"每轮 4 面、每轮重置"作废】),仅限小人相邻格;拔旗移除标记、不返还 AP +- **标雷判定(v3,2026-09-07 三向效果定稿)**:轮次结算时统一判定——**标中雷 = 怪物获得 debuff(削弱)**(与"安全格→主角 buff、踩雷→怪物 buff"构成三向效果映射);一次性兑付、兑付资格以**雷格实例为键**(每雷本次战斗至多兑付一次,资格随首次兑付消费)、拔旗重插同一雷格不恢复资格、禁止逐轮重复兑付(A3 OQ5/F1 条款 2:debuff 档位锚 = 严格 > 最低档安全格 buff 当量,且以连锁放大的边际挖掘收益为对照基准——2026-09-07 三轮评审改定);标错暂无惩罚(候选惩罚:敌方伏击+1 等,待验证后决定;该惩罚兼具旗滥用定价职责,见 A3 OQ2) +- **小人控制(v2,参考 Microsoft Minesweeper Adventure 模式)**:棋盘上有小人,点击已开格=移动(免费,BFS 限已探明区);挖/插旗仅限小人 8 向相邻格。边缘探索的地牢氛围已被用户认可 +- **初始安全区**:每张图开局 3×3 无雷自动展开,小人出生于此(初始展开无奖励) +- **可随时停手**:剩余 AP 1:1 转存 RAP 池,攒满(默认 8)可刷新雷图(不给清图奖励)——"是否承担盲猜风险"是策略点【2026-09-07 A3 RAP 修订,原"转化为战斗资源"作废】 +- **奖励统一结算**:扫雷阶段奖励只入库(含治疗包),轮次结束进入独立战斗结算画面逐行生效(正式版将替换为战斗动画) +- **安全格 = 增益/道具**:减伤、增伤、范围伤害等;**有回合限制甚至一次性**(稀缺性来源) +- **雷 = 敌方优势**:开到雷不是死亡,而是敌人获得增益/伏击——风险与收益同源 +- 扫雷图非基础版:**地形变异**(迷雾区无数字、岩浆格等)+ **辅助道具**(探测、安全旗等) + +## 难度与惩罚机制(按层级) + +**难度不随战斗数增长,而是随关卡进程增长**:难度 = f(关卡序号, 节点深度, 精英/Boss 修饰) + +| 层级 | 惩罚/压力机制 | 说明 | +|---|---|---| +| 轮次内 | 开雷 → 敌方结算时获得优势 | 扫雷失误的代价,当场兑现 | +| 战斗内 | 轮次越多,敌方结算出手越多 | 长战斗的自然代价(己方 HP 消耗);单张图拖太久 = 敌人多结算但旧图剩余收益仍在,形成"续挖 vs 清图换新"决策 | +| 关卡内 | 节点越深越难;精英节点相对更难 | 越接近 Boss 越危险,精英给更高收益对冲 | +| 关卡间 | 下一关基础数值+新地形变异 | 变异种类随关卡解锁(普通数字 → 迷雾 → 岩浆/冰冻) | +| 冒险间 | 死亡重开,天赋追赶 | 元进度:代币解锁永久提升 | + +## 与 Peglin 的对比要点 + +- 玩家技巧:物理瞄准+概率赌博 → **信息推理+风险承诺** +- Agency 位置:战斗内 → **全部前移到排雷规划** +- 不确定性来源:弹射混沌 → **隐藏信息推理缺口** +- 核心情绪:老虎机开闸爽感 → **"我算对了"的掌控感 + 规划兑现的延迟满足** + +### 已识别的风险(原型验证目标) + +1. 自动战斗沦为无聊过场 → 兑现层必须可见、有反馈 +2. 扫雷后期纯猜格子 → 用地形变异和道具缓解"死局感" +3. 一格一格点节奏慢 → AP/连锁/停手机制压缩决策带宽 + +### 创意风险登记(2026-09-07,CD 门禁) + +1. **安全格奖励的可预期性**(P2 规划的前提):若道具掉落纯随机且不可见,玩家无法提前形成"我要去拿 X"的规划,"规划的延迟兑现"退化为探索惊喜 → B4/A4 GDD 落实奖励类型可见性或探测机制。**【2026-09-09 B4 评审口径注记】结构前提已落地**(奖励身份 install 期预分配 + 已开格/库存恒读 + 每格必有货 + 雾档先验——B4 Rule 2/8/F1/F2);**MVP 呈现面 = 已开格 + 库存,面向特定道具的寻路未兑现**——待 A5 探测道具/呈现深度(B4 OQ2,垂直切片);本风险维持 OPEN(结构前提落地 ≠ 风险闭合) +2. **"我算对了"缺高光兑现节点**:现有设计只有避损(雷→敌方优势)与常规奖励,缺"完美排雷"的爆发结算(候选:全图标中/零误标清图加倍结算)→ B1/B4 GDD 设计该兑现节点 +3. **chord 缺席的兑付节拍缺失**(2026-09-07 A3 评审登记):本作有意不做经典扫雷的 chord(双击快开),正确标雷后的即时大片兑现节拍随之缺席(标中旗兑付只剩当轮 debuff)→ 候选高光补偿与 #2 合并归 B1/B4 设计 + +## 可证伪假设(概念原型要回答的) + +> 如果玩家通过扫雷决策为自动战斗获取道具,他们会感受到"每一步排雷都在影响战斗结果"的策略联动感。 +> 验证信号:玩家在扫雷阶段主动规划目标(连续 3 轮中至少 2 次主动改变扫雷策略去拿特定道具),并主动想进入下一轮。 +> 【2026-09-09 MVP 口径注记(B4 评审)】未开格身份不可见(B4 Rule 8),"拿特定道具"的寻路决策待 B4 OQ2/A5 兑现后方可作为有效信号——MVP 期以"往雾区推进/囤刀待机/叠层满载后收手"类规划行为代观测,防止把缺失功能误判为设计失败。 + +## 待定问题 + +- [x] 扫雷后奖励的主动道具(影响战斗结算):**MVP 激活**(2026-09-09 B4 定稿,用户拍板——轮末结算前"道具使用窗口"玩家选用;use_timing 字段从预留转承重,MVP 单值 pre_settlement,扩枚举走 B4 修订;原"后续版本加入"作废) +- [x] 己方限时增益:**跨轮次延续(按剩余结算次数),不跨战斗**(定稿 2026-09-06) +- [x] 旗子:**无上限**,旗为棋盘标记不占轮次资源,稀缺性由 AP 价目承载(2026-09-07 A3 RAP 修订定稿;原"每轮重置 4 面"作废) +- [x] 停手/清图时剩余 AP 的去向:停手 1:1 转存 RAP(刷图风险资源)、清图作废——**折算乘数(×2/×1)整体作废**(2026-09-07 A3 RAP 结构修订;效果侧健康由 A3 F1 契约约束,OQ3 留 playtest 复核) +- [ ] **标错惩罚**(v3 遗留:现无惩罚;候选:敌方伏击+1~~ / 旗子数量减半~~——后者随 2026-09-07 旗上限取消作废)——在 A2 GDD 前用原型小补丁实测后关闭(CD-SYSTEMS 门禁补登记;兼具旗滥用定价职责,见 A3 OQ2) +- [ ] 关卡内节点地图的形态(线性/分支)——C1 GDD 解决;候选已登记(2026-09-09 用户提出):**四方格步进式前进**——关卡内地图为格网,每步从相邻四格中选一格推进(与地牢隐喻同构);C1 设计顺序第 14 位,此项可后置 +- [ ] 反雪球机制——MVP 不做,Alpha 用测试数据决定(C3 GDD 需先决定"每胜一场敌方成长"这一正雪球源的去留) diff --git a/design/gdd/game-pillars.md b/design/gdd/game-pillars.md new file mode 100644 index 00000000..494bb944 --- /dev/null +++ b/design/gdd/game-pillars.md @@ -0,0 +1,29 @@ +# 游戏支柱(Game Pillars) + +> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 定稿:2026-09-06 +> 来源:`game-concept.md` 概念定稿 + 概念原型 v1→v3 验证结论 + CD-SYSTEMS 门禁意见 +> 用途:所有 GDD 的评审尺度——每条设计决策必须至少服务一条支柱;与支柱冲突的设计默认砍掉 + +## P1 推理的掌控感 —— "我算对了" + +每一格挖掘都应该基于可推导的逻辑,而不是运气。生成端必须保证可推理率(A1 GDD 定最低门槛),后期"纯猜"局面用变异与道具缓解。这是本作与 Peglin(概率赌博)的根本区别。 + +**裁剪判据**:一个设计若让玩家频繁进入"无法推理只能赌"的状态,即违反本支柱。 + +## P2 规划的延迟兑现 —— 扫雷的因,战斗的果 + +扫雷阶段的每个决策(挖哪里、标哪面旗、何时停手)都在战斗结算中可见地兑现。兑现必须**可归因**:结算明细标注来源(伤害道具 / 安全格增益 / 标中旗削弱 / 清图奖励 / 雷→伏击;【2026-09-07 A3 RAP 修订】原"停手折算"来源作废——停手改产 RAP 风险资源,不再进战斗结算),MVP 就要做到,不等战斗动画。 + +**裁剪判据**:若玩家无法说出"这个结果是我哪步扫雷带来的",即违反本支柱。 + +## P3 风险与收益同源 —— 雷既是敌人的武器,也是你的赌注 + +开雷喂给敌人伏击(代价),标中雷削弱敌人(收益)【2026-09-07 三向效果定稿:标中旗 = 怪物 debuff】,危险区域的安全格价值更高。所有风险机制都从这个同源原则派生,不引入第二套随机惩罚。 + +**裁剪判据**:一个风险若与"雷/未知格"无关,需强理由才可保留。 + +## P4 地牢下探的探险氛围 —— 边缘探索,越深越险 + +小人从初始安全区向雷图边缘探索(Microsoft Minesweeper Adventure 式的地牢感),关卡推进 = 不断下探更险的层。视觉、音频、地图结构都服务这个隐喻。产品命名本身承载本支柱:**Dungeon Sweeper**(2026-09-09 定稿,minesweeper → dungeon sweeper 双关;世界观与机制隐喻映射见 game-concept"世界观与命名"节)。 + +**裁剪判据**:任何让棋盘体验退化为"平面对话框"的表现层简化,需评估对氛围的伤害。 diff --git a/design/gdd/gdd-cross-review-2026-09-14.md b/design/gdd/gdd-cross-review-2026-09-14.md new file mode 100644 index 00000000..5f44ed95 --- /dev/null +++ b/design/gdd/gdd-cross-review-2026-09-14.md @@ -0,0 +1,116 @@ +# 跨 GDD 评审报告(/review-all-gdds)— 2026-09-14 + +> **Verdict: FAIL**(2 blocking)· 覆盖 16 份文件(registry/concept/pillars/index + A1–A5/B1–B4/C3/D1/D2) +> 方法:Phase 2(一致性)/ Phase 3(设计理论)由两个独立上下文代理全文逐字通读,主审对两代理的分歧点逐条 grep 核实后和解。 +> ⚠️ **评审期间检测到并行会话正在修改 GDD**:C3 修订批(encounter 骰/护甲与伤害A/B 字段/clear_blast/K_dmg=0/B2 三敌种)在两代理先后读取之间落地——这正是两代理对 F1–F5 结论互斥的原因。本报告以主审最终 grep 时点盘面为准;**批收口前建议避免多会话并行修改设计文档**。 + +--- + +## 一、Blocking 🔴(进 /create-architecture 前必须闭合) + +### 🔴-1 减伤墙批回执链失效——defense_up 数值三重漂移,B1 条款③⑤闭合回执在现行值链下翻为破线 +- **事实面**:C3(F4/试玩批记录)按 **−30 × 2 层帽 −60%** 投值并声称"B3 侧行值批";对侧 B3 GDD(MVP 内容行/Knob 表)、registry `status_effect.mvp_content`、D2 全部仍为 **−50**,B3 无任何 −30 注记。B1 F4 的 V_safe 恒等式还叠着 **duration=2** 旧值(registry/B3 已锁 1——2026-09-11 两级奖励批)。 +- **量化后果**(C3 v3 曲线 d0.25/t0.88 口径):V_safe 12 → duration 修正 6 → 幅值修正 3.6;条款③ L1 锚行裕度从登记的 ≈2.0× 跌至 ≈0.5–1.0×——**登记的"通过"回执失效**。同链失真:B1 F0 值域 [60,85] 推导(E_dig 反推腿)、F1 对照表行值(knife_t1 4 / knife_adv 8 / clear_knife 8 / heal_t1 行已不存在——B4 现行 8/16/40 两级 + 注入池)、F5 E_dig=7.2 与条款⑤缺口读数、B1 F1 attack_up"死行"残留(与自身 Core Rule 10 先手攻击吃修正互斥)、B4 AC-18 若未随批改写则三值断言假失败(待复核)。 +- **修复批**:① B3/registry/D2 接口漂移批(−30 + duration 口径 + 免伤轮叙述同步);② B1 修订批按现行三输入(duration=1、m_d 终值、C3 v3 曲线)重推 F0/F1/F4/F5,重登 clause3_supply/clause5_dominance 回执,AC-36 死行断言改写;③ defense_up.magnitude/duration 纳入"禁独立调参"联动网络(现仅 m_w↔D_ref 有此待遇)。 +- **owner**:B3(行值批)+ B1(重算批)+ C3(兑现其"已回写"承诺) + +### 🔴-2 D2 修订批回写不完整——schema 单一事实源分裂,含可执行触雷点 +- X-8 回执只覆盖 EnemyTable +2 / SettlementTable +4 字段,以下五处漏掉(均经 grep 核实): + 1. **RNG 流仍"封闭 2 流"**,无 encounter(registry 已三流、B1 AC-5 已消费)——B1 `open_stream("encounter")` 按 D2 自己的 EC12 = 未知名流 **debug 断言中止**; + 2. **pool_tag 枚举无 `active_inject`**——B4 注入池三行(knife_t1/knife_adv/heal_pack)在 D2 现行校验下**加载期致命**; + 3. **RewardConfigTable 缺 active_p0/active_decay**;k_dmg 仍写默认 1 + "C3 禁区默认值"条款(B4/registry 已转正默认 0、禁区解除); + 4. **默认根行集仍列 clear_knife 八行**(已被 clear_blast 换位替代); + 5. 占位表 **HelperItemTable** 未随 A5 更名 **AuxItemTable**。 +- **修复批**:D2 补漂移修订批(五处 + X-8 回执补登遗漏项);建议增流程纪律——**修订批回执须逐表 diff schema 目录**(本次缺口正是按"对侧 GDD"粒度登记、未按"受影响字段/枚举"粒度 diff 所致)。 +- **owner**:D2(C3/B4 配合) + +## 二、Warnings ⚠️(16 条——不阻塞 architecture,但须入批) + +| # | 事项 | 涉及 | 建议落点 | +|---|---|---|---| +| W1 | 供给控制器机制规格缺位:注入时点/cadence、入库存 API、`stock()` 来源 {cell, round} 对注入件无 cell、明细行协议(P2 归因"我在地牢里淘东西"断链——注入流是唯一与扫雷决策解耦的奖励流) | B4/C3/registry | B4 修订批补专节 + 来源语义裁决(建议 `{source: injection, board_index}`)+ "注入依赖度"预警 | +| W2 | 护栏 B(防钓鱼)复核被跳过:clear_blast 把 C 提至 40/完美 80,对登记上界 24(D_ref 口径)/≈8(DPS 严格)越线 1.7–10×;B1 回执只重推了条款⑤缺口 | B1/C3/registry clause5_dominance | C3/B1 补护栏 B 重推记录,三选一落档(上界改判/接受+强化监控/回调 C) | +| W3 | 伤害A/B 充能与护甲克制:B1 有四字段无结算公式/无 AC/无 F1 对照行;A3 条款①扩列义务未登记 | B1/B2/A3/C3 | B1 修订批补充能机制段(累积时点/cap/与乘区关系/查表算式)+ A3 条款①扩列 | +| W4 | B2 正文 Rule 3 仍"恰 2 敌种(grub/ambusher 旧名)" vs 自身 AC-14/AC-16/结构断言三敌种 {goblin, bugbear, ogre_chief}(文内分裂) | B2 | B2 批随改正文(provisional 归 C3 口径保留) | +| W5 | A4 AC-1 的 counter_item_ids=∅ 定值断言 vs A5 回填 [probe_dust](回填批落地即假失败) | A4/D2 | A4 批改表读值断言或显式豁免 | +| W6 | B1 出口分发"每图 cap 2" vs A3/registry/D1 "每轮 2、RoundStart 重置"(同图跨轮语义实质不同;B1 自身 AC-29 按 per-round 断言) | B1 | B1 改"每轮" | +| W7 | registry `boss_hp_anchor` 带 [0.35,0.55] vs C3 F3 [0.5,1.1];`difficulty_expectation_table` 通关率/供给带已被 C3 替换——两处 owner(C3)未回写 | registry/C3 | C3 回写批 | +| W8 | A2 呈现面 current_board()/reveal_record() 在 A2 API 封闭枚举外被消费,A2 GDD 无接口漂移登记、D1 冲突⑧清单漏 A2 面 | A2/D1/A5 | A2 批补登记 | +| W9 | systems-index A5 分层行缺 A3 写边(grant_ap 是写入口非编排调用——A3 下游表亦无 A5) | systems-index/A3 | 索引补边 + A3 修订批 | +| W10 | A1→B4 board_biome 数据边单向(B4 依赖节无 A1 行;Phase 2 观察于 C3 批落地前,入批前 30 秒复核) | B4/A1 | B4 批复核补行 | +| W11 | C3 使用已退役名 refresh_max_per_round(正式名 refresh_cap_per_round——D1 命名统一表) | C3 | C3 批两处改名 | +| W12 | A5 交互面三项:① F1 渗透率锚定池构成已被供给控制器批改写(读数随 B4×④ 重算——义务已登记,此为交互面扩展:注入池不承载 aux,格池全即时类+aux 后分母口径变化);② ap_grant 无每轮总量结构位("满停一刷"基线节拍被 A3 不拥有的源破坏——建议 A3 批增结构位默认关或登记接受声明);③ 真视把 weaken 饱和与伤害B 充能两条收益线同时放大,免伤轮常态化未入任何对账清单(真视件 ≈0.42/板;A5 OQ3 扩面 + B1 对账清单补"真视供给 × weaken 饱和频率") | A5/A3/B1 | 各自修订批 | +| W13 | 挖掘期注意力预算 ≈6 并发决策域(A2 推理/A3 预算/A4 雾/A5 道具/B4 囤刀规划/B2 读怪);A5 后最坏格面 = 旗×雾纹×显影数字×真视标记×预读徽章五重叠加,A4 AC-9/D1 样张最坏用例清单未含 | A4/D1/A5 | 样张清单补五重叠加腿 + A5 遥测增"单轮 use_aux 次数" | +| W14 | A5 F3 修正后 registry 已同步;但 A5 GDD 正文 Core Rules 表口语化重写由并行会话完成——两处口径已在本次评审合并确认,无残留 | — | 无动作(记录) | +| W15 | D1 敌种图标键目录仍 {grub, ambusher} 冻结锚(D1 冲突清单②已登记"随 B2/C3 批对齐"——批未执行) | D1 | D1 批对齐 goblin/bugbear/ogre_chief | +| W16 | B4 AC-18 三值封闭定值断言与"默认根不含 attack_up 行"腿(Phase 2 发现;C3 批落地后未复核——若批已改写则解除,否则随 B4×④ 重制) | B4 | 复核 + B4 批 | + +## 三、设计整体性(Phase 3)结论 +无 blocking。**骨架健康**:主循环主张(扫雷推理为主、结算为兑现器)结构上成立;结算期单输入点(窗口单件制)、A5/B4 两使用入口相位错开、RNG 封闭流、义务/夹具/对账纪律密度为同类设计罕见。风险集中在**近三日三个"快批"的回写完整性**(减伤墙批、护甲克制批、A5 定稿)——修复模式一致:回执按对侧 GDD 粒度登记,未按受影响公式/枚举/字段粒度逐项 diff。四支柱映射与玩家幻想一致性(地牢探险者:算得对/捡的货/冒的险/探的深)核对通过,无新的反支柱违反。已登记风险(停手攒 RAP 环、囤刀、道具依赖症、标错惩罚等)经核对登记齐备,本报告仅在交互面新增处升级(W12③)。 + +## 四、跨系统场景推演(Phase 4,3 条) +1. **aux 授予→使用链**(A2→B1 授予段→B4 grant 路由→A5.add_item→下轮 Active use_aux→validate/apply/grant→consume→D1 回执):编排序原子性、相位互斥、零回写、两库存正交均闭合 ✓;唯一发现 = W12①(渗透率分母随池结构漂移)。 +2. **真视→插旗→双收益线**(A5→A2→B1→B3→B2):P2 归因链完整(flag_hit 既有路径)✓;发现 = 真视同时喂大 weaken 饱和与 damage_b 充能,归入 W12③/W3。 +3. **encounter 骰触发路径**(B1 begin_battle→RngService open_stream("encounter")):现行 D2 封闭清单下即 🔴-2 第 1 项的运行期触雷点。 + +## 五、格式门禁(tools/gdd_lint.py 全量) +11/12 FAIL:以 R8"说明/要点列混入编号引用(Rule 1/EC7/AC-1/义务/OQ8)"类为主——多为四列规则表形态与 linter 三列预期的结构性错配(D1/D2 最密集)+ D2 最长括号跨度 67 字。仅 A5 PASS。建议下一批做 lint 口径校准(四列表 依据列识别)或机械化引用列迁移,勿逐条手改。 + +## 六、修订标记(systems-index,2026-09-14 用户拍板"只标 blocking") +- B3、B1、D2 → **Needs Revision**(状态单元格按门禁精确匹配纪律置为裸值;历史放行记录保留在各 review log 与本报告附录) +- 其余 ⚠️ 项不降级已批免复评的 GDD 状态,挂各自修订批对账。 + +## 七、批调度建议(推荐顺序) +1. **B3/registry/D2 接口漂移批**(🔴-1 前半:−30 + duration)与 **D2 漂移批**(🔴-2 五处)可同一会话; +2. **B1 重算批**(🔴-1 后半:F0/F1/F4/F5 重推 + W3 充能规格 + W6 每轮 cap + F7 死行清理); +3. **B4 规格批**(W1 供给控制器 + W16 复核 + W10 复核); +4. **C3 收口批**(W2 护栏 B + W7 registry 回写 + W11 改名 + 兑现其余"已回写"承诺); +5. 零散:B2 正文(W4)、A4(W5)、A2(W8)、索引补边(W9)、D1(W15)、A5/A3(W12)。 +全部收口后跑 `/review-all-gdds since-last-review` 增量复核 → PASS 再进 `/create-architecture`。 + +--- + +## 附录 A:Phase 2 一致性代理原始发现(F1–F21)× 和解标注 + +> 代理读盘时点在 C3 修订批落地前,以下标注以主审最终 grep 为准。 + +- **F1 C3 敌种键集改名"已回写 ✅"失实** → **已解除**:B2 AC-14/AC-16/结构定值断言/版本历史 X-7 均已落地三敌种 {goblin, bugbear, ogre_chief};残余 = B2 正文 Rule 3 口语段(W4)+ D1 键集目录(W15)。 +- **F2 defense_up −30 未回写 B3/registry** → **维持 → 并入 🔴-1**。 +- **F3 K_dmg=0 禁区解除仅存于 C3** → **B4 侧已解除**(F5 现行默认 0 + 供给控制器首件保证语义已入文);**D2 侧残余**(k_dmg=1/禁区/缺 active_p0/decay)→ 并入 🔴-2。 +- **F4 clear_blast/注入池/供给控制器 vs B4 旧根** → **B4 侧已解除**(clear_blast 行/active_inject 池/拥有行均已入文);**D2 侧残余**(pool_tag 枚举/行集)→ 并入 🔴-2;A5 F1 分母 → 转 W12①;机制规格缺位 → W1。 +- **F5 C3 声称的 B1/B2/D2 机制扩容不存在** → **已解除(字段面)**:B1 encounter 骰(步骤序/AC-5/版本历史 X-6)、SettlementTable +4 字段、B2 spawn override/unit_count_max/armor_type、D2 EnemyTable +2 字段均已落地;**规格面残余** = W3(B1 充能结算公式/AC 缺位)。 +- **F6 B4 AC-18 三值断言互斥** → **待复核**(C3 批是否同步改写;未改则随 B4×④ 重制)→ W16。 +- **F7 B1 attack_up 死行残留** → **维持** → 并入 🔴-1 的 B1 重算批。 +- **F8 B1 数值链整体过期**(duration/行值/heal_t1/C=8)→ **维持** → 并入 🔴-1 的 B1 重算批。 +- **F9 A4 AC-1 ∅ 断言** → **维持** → W5。 +- **F10 每图 cap 2 vs 每轮 2** → **维持** → W6。 +- **F11 D2 封闭 2 流 vs registry 三流** → **维持** → 并入 🔴-2 第 1 项。 +- **F12/F13 registry 两处未随 C3 回写** → **维持** → W7。 +- **F14 A2 呈现面接口漂移登记缺** → **维持** → W8。 +- **F15 A5 义务对侧零落位 + 索引缺 A3 写边** → 落位半为**已登记 deferral**(不升级);索引写边半 → W9。 +- **F16 A1→B4 biome 边单向** → **维持(落地前观察,入批前复核)** → W10。 +- **F17 C3 退役名** → **维持** → W11。 +- **F18 头部状态漂移**(A1/B1/B4/D1)→ ℹ️ 维持(A5 已随本批修复)。 +- **F19 C3 引"B4 义务 #10"实为缺口预检 #6** → ℹ️ 维持。 +- **F20 HelperItemTable 占位名** → 并入 🔴-2 第 5 项。 +- **F21 game-concept"奖励只入库(含治疗包)"句未随两级奖励批更新** → ℹ️ 维持(概念层补注记)。 + +## 附录 B:Phase 3 设计整体性代理报告(原文,读盘时点在 C3 批落地后) + +**先行澄清**:经逐文件核对,encounter 掷骰/护甲/伤害A/B 的表字段与接口已同批落地 B1/B2/B4/D2/registry(X-6~X-10 回执在案);真实两态为:①减伤墙批(C3 意图 −30 vs B3/registry/B1 现行 −50);②A5 的 B4×①…⑤/D2×①②/A3×① 修订批(已登记 deferral,不升级);③供给控制器规格;④护栏 B 复核。另发现 D2 整批漂移(→ 本报告 🔴-2)。 + +**🔴-1 条款③/⑤闭合回执失效**(→ 本报告 🔴-1,该代理另贡献了 C3 v3 曲线 d0.25/t0.88 的量化:V_safe 12→6→3.6、条款③裕度 2.0×→1.02×→0.86×→0.52×、E_dig 修正后 ≈4.3–5.1);**🔴-2 D2 漂移**(→ 本报告 🔴-2)。 + +**⚠️-1 供给控制器规格缺位 + P2 归因断链**(注入流 ≈1 件/战斗与挖掘决策解耦;H1"开格拾取"高光从此只覆盖即时类;建议注入件来源语义裁决 + "注入依赖度"预警)→ W1。 +**⚠️-2 护栏 B 复核被跳过**(C=40/80 双读数越线;登记在案的强制检查点被静默跳过;结构承载 = A1 min_safe_count + 布雷下限 + R₀≠S 三闸仍在)→ W2。 +**⚠️-3 伤害A/B 充能无 owner 结算规格**(累积时点/单轮封顶/与 attack_up percent 池关系/护甲查表与乘区先后序/当量换算全部空白;43 条 AC 零覆盖;A3 条款①"分源改率须回 A3 重议"未走)→ W3。 +**⚠️-4 挖掘期注意力 ≈6 域 + 五重叠加样张缺口**(结算期无此问题——单输入点设计良好;建议 A4 AC-9 与 D1 样张补五重叠加腿 + "单轮辅助决策次数"聚合仪表)→ W13。 +**⚠️-5 B2 正文 vs 自身 AC 敌种口径分裂** → W4。 +**⚠️-6 ap_grant 两条未闭合口**(无每轮总量结构位破坏"满停一刷"标定前提;A5 AP 当量 ≈+5% 通胀未排期重算——B1×④ 扩列时入表并联动条款⑤ Δr 口径)→ W12②。 +**⚠️-7 真视 × weaken 池 → 免伤轮常态化未入对账**(mine_reveal 使"单轮 8 标中"从尾部事件趋向可靠操作;L=8 → clamp −100 → 该结算敌方输出归零;修正入口 = 真视供给权重而非地板规则)→ W12③。 +**ℹ️-1 文头状态漂移**(A1/B1/B4 文头 vs 索引;A5 已随本批修复)→ 附录 A F18。 +**ℹ️-2 两态盘点修正**(encounter/护甲/伤害A/B/clear_blast/K_dmg=0 均已双侧落地——与 Phase 2 代理结论互斥的根源即读取时点差;本附录为其原文判断)。 +**ℹ️-3 已登记项确认**(停手攒 RAP 环/囤刀/道具依赖症/C3 通关率越带/标错惩罚/R₀ 分布义务等四方登记齐备,不升级;主循环对其余面映射一致)。 + +## 和解记录(供复核) +两代理对"C3 回写批是否落地"结论互斥 → 主审对 B1/B2/D2/B4/B3/registry 六文件逐条 grep(encounter/damage_a/unit_count_max/ogre_chief/active_inject/clear_blast/defense_up 值域)→ 判定:批在两代理读取之间落地,Phase 2 的 F1/F3/F4/F5 字段面已解除、F2 维持;Phase 3 的 🔴-1/🔴-2 与全部 ⚠️ 采信为现行盘面。 diff --git a/design/gdd/gdd.md b/design/gdd/gdd.md new file mode 100644 index 00000000..2385ed53 --- /dev/null +++ b/design/gdd/gdd.md @@ -0,0 +1,137 @@ +# Dungeon Sweeper — GDD(独立版) + +> **这是什么**:全部设计文档的入口层——先用 6 节把游戏讲清楚,需要细节再顺着链接进明细层。 +> **写给谁**:未来的你自己、朋友、合作者——假设读者没读过任何其他文档。 +> **效力声明**:本文是导航与共识层,不是数值权威;规则与数值一律以各 owner GDD 和 `design/registry/entities.yaml` 为准,冲突时以后者为准。 +> **明细层入口**:系统树根 `design/gdd/systems-index.md` · 概念层 `game-concept.md` / `game-pillars.md` + +## 1. 一句话概念 + +**一句话**:扫雷的推理 × 自动战斗的兑现——你在战斗前做的每一次排雷决策,决定战斗的胜负。 + +**30 秒版本**:你是一名地牢探险者,逐层下探被怪物巢穴侵染的地牢。每层是一张"雷图":挖开安全格给自己拿增益,标中雷削弱敌人,踩雷则喂强敌人。行动点用尽,轮末自动结算——你扫雷攒下的因,逐行兑现成战斗的果。清了图换新图继续,杀穿 Boss 下探更深;死了,整个冒险从头再来。像《Peglin》的"小游戏 → 战斗结算"循环,但把弹珠的运气换成了扫雷的推理。 + +**为什么好玩**(四支柱,一针见血版): + +| 你会爱上它的点 | 靠什么兑现 | +|---|---| +| "我算对了"的掌控感 | 每一格都靠数字推理,不靠赌——棋盘生成端保证可推理率 | +| 扫雷的因,战斗的果 | 结算明细逐行标注来源:这行伤害是你哪步棋换来的 | +| 风险与收益同源 | 同一颗雷两面:标中 = 削弱敌人,踩中 = 敌人变强 | +| 下探的地牢氛围 | 小人从安全区向边缘探索,关卡推进 = 越深越险 | + +**给谁玩**:喜欢杀戮尖塔/小丑牌式构筑决策 × 喜欢扫雷式纯逻辑掌控感的玩家;PC(Steam)单机,回合制无配置要求。 + +## 2. 核心循环 + +一条线画完: + +``` +下探地牢 → 遭遇战 → 扫雷规划(挖安全格=增益 · 标中雷=削敌 · 踩雷=喂敌) +→ 行动点用尽 / 主动停手 / 清图 → 轮末自动结算兑现 → 敌未灭则下一轮 +→ 清图换新图(或攒"换图资金"避开盲猜)→ 杀 Boss 通关拿大奖励 → 更深一层 → 死亡则整个冒险重来 +``` + +外面套的壳就是游戏结构: + +``` +存档(天赋/代币 —— Alpha) + └─ 冒险(一条命,死亡即终) + └─ 关卡 ×2(节点链:4 战 + 恢复 + Boss) + └─ 战斗(碰非奖励点触发) + └─ 轮次 ×N ── 扫雷图 ×M(跨轮延续,清图换新) +``` + +每轮的取舍只有三个出口——这是全部策略的落点: + +| 出口 | 触发 | 结果 | +|---|---|---| +| 清图 | 安全格全开完 | 发清图奖励,下轮换新图 | +| 停手 | 玩家主动收手 | 剩余行动点 1:1 攒成换图资金,下轮同图 | +| 耗尽 | 行动点扣到 0 | 下轮同图继续,一无所得 | + +## 3. 系统清单(只列真的要做的) + +明细层 13 份系统 GDD,已全部定稿——每行一句话 + 原文链接: + +| 模块 | 系统 | 一句话 | 详规 | +|---|---|---|---| +| 扫雷侧 | A1 棋盘生成 | 布雷与雾区的确定性生成,保证"可推理率" | `design/gdd/a1-board-generation.md` | +| 扫雷侧 | A2 排雷玩法 | 小人移动、挖/旗/连锁展开的棋盘面规则 | `design/gdd/a2-minesweeper-rules.md` | +| 扫雷侧 | A3 行动经济 | 每轮行动点账本、统一价目、三出口仲裁 | `design/gdd/a3-action-economy.md` | +| 扫雷侧 | A4 地形变异 | 迷雾等变异的布置约束与代价模型 | `design/gdd/a4-terrain-variation.md` | +| 扫雷侧 | A5 辅助道具 | 显影/真视/加行动点等信息与行动力道具 | `design/gdd/a5-minesweeper-aux-items.md` | +| 战斗侧 | B1 战斗结算 | 七段结算编排、明细流水、胜负判定 | `design/gdd/b1-settlement.md` | +| 战斗侧 | B2 敌人系统 | 三敌种行为环、图像线索读怪、伏击 | `design/gdd/b2-enemy-system.md` | +| 战斗侧 | B3 增益状态 | 全部 buff/debuff 的统一容器与叠层规则 | `design/gdd/b3-status-effect.md` | +| 经济侧 | B4 奖励经济 | 掉落池、即时/可选两级奖励、清图奖励 | `design/gdd/b4-reward-item-economy.md` | +| 结构侧 | C2 冒险结构 | 关卡序列、HP 账本、关卡大奖励、死亡重开 | `design/gdd/c2-adventure-structure.md` | +| 结构侧 | C3 难度曲线 | 敌我数值成长、Boss 台阶、通关率带 | `design/gdd/c3-difficulty-curve.md` | +| 支撑侧 | D1 HUD 框架 | 棋盘/队伍/明细/结算五区界面布局 | `design/gdd/d1-hud-framework.md` | +| 支撑侧 | D2 数据配置 | 全部数值进配置表,代码零字面量 | `design/gdd/d2-data-config.md` | + +### 还没设计的(排在后面,不是不做) + +| 系统 | 是什么 | 计划 | +|---|---|---| +| C1 关卡节点地图 | 关内行进与节点兑付(切片暂用线性链) | 垂直切片 | +| B5 战斗表现 | 结算画面 → 战斗动画、伤害数字 | 垂直切片 | +| C4 元进度 | 存档/天赋/代币 | Alpha | +| D3 存档序列化 | 持久化框架 | Alpha | +| D4 音频 | 挖掘/结算/胜负反馈音效 | Alpha | +| D5 教学引导 | 新手引导 | Alpha | +| D6 设置/本地化 | 常规打磨项 | 完整愿景 | + +## 4. 内容规模 + +做多少、砍多少,心里有数——以下是 MVP 口径的全部内容面: + +| 内容 | MVP 数量 | 备注 | +|---|---|---| +| 冒险 | 2 关:L1 地牢基线 → L2 解锁迷雾 + 数值台阶 | 加关 = 序列+数值+变异同批,禁止只加序列 | +| 每关节点 | 7 节点:4 战 + 恢复 + Boss | L1/L2 同构复用 | +| 敌种 | 3:地精 / 熊地精 / 食人魔酋长(Boss) | 行为拍 3 种:攻击/蓄力/重击 | +| 状态效果 | 6 种:增伤/减伤/伏击/削弱/暴走/失衡 | 另有 1 种已登记待实施 | +| 奖励 | 8 种:即时 4 + 可选 3 + 清图 1 | 可选类入背包、轮末窗口选用 | +| 辅助道具 | 6 种,覆盖信息/风险/行动/规划/反制五维度 | 含 2 件稀有多维道具(如地穴全图卷) | +| 地形变异 | 迷雾 1 种 | 岩浆/冰冻等留后续关卡解锁 | +| 生物群系 | 地牢 1 种 | 换皮 = 色板变体,不做新资产 | +| 雷图 | 10×10 起步、随深度增大(参考至 12×12) | 每轮行动点 8;开局 3×3 安全区 | +| 单局时长 | 冒险 20–40 分钟 / 单关 5–10 分钟 | 单场战斗 1–3 分钟,碎片时间友好 | +| 平台 | PC(Steam)先行 | 回合逻辑,无配置门槛 | + +## 5. 美术/音频方向 + +| 项 | 定调 | +|---|---| +| 风格一句话 | 轻快明亮的像素幻想地牢——非暗黑风、全年龄、角色 2–3 头身 | +| 参考池 | Into the Breach(网格信息分层)× Moonlighter(明亮地砖质感)× Peglin(明快全年龄色彩);截图在 `design/art/references/` | +| 一条硬规则 | 数字是主角:数字与格底对比度 ≥4.5:1,氛围永远给可读性让路 | +| 风险闸 | 正式砖面资产开工前跑一次"格子样张测试",数字可读性是第一验收项 | +| 详细 | `design/art/art-bible.md`(视觉身份/氛围/形状/色彩四节已定稿) | +| 音频 | MVP 不做——临时音效挂垂直切片,正式音频排 Alpha | + +## 6. 不做清单 ★ + +**本节最重要**——写下来,防止半夜加戏。每条都有已拍板的理由;真想推翻,走对应 owner 文档的修订流程,不许静默加回来: + +| 不做的事 | 为什么 | 真想做时先动谁 | +|---|---|---| +| 经典扫雷的 chord(双击快开) | 每格兑现都必须花行动点——chord 会让预算失去意义 | A3(高光补偿候选已登记,归 B1/B4) | +| 抽象资源点数 / 折算货币 | 扫雷收益全部直兑战斗效果,中间货币稀释"因果可见" | A3 + B1 当量对照表 | +| 旗做成背包道具或限量 | 旗只是棋盘标记,稀缺性由行动点价目承载 | A3 | +| 停手奖励折算成战斗力 | 停手是风险管理工具,不是第二收入流 | A3 | +| 换图资金跨战斗携带 | 每场战斗清零,防滚雪球;连刷策略等实测后议 | A3 开放悬案 | +| 未开格显示奖励身份 | 推雷是本征玩法,预读会拆掉推理 | B4 锁定规则,随极性提案批再议 | +| 替代推理的全图侦察道具 | 全图级只给"恢复推理原料"类(显影/预读);替代推理限 3×3 窄域 | A5 护栏 | +| 第二套随机惩罚 | 所有风险从雷/未知格派生,不引入平行的随机惩罚 | 支柱 P3 裁剪判据 | +| 反雪球机制 | MVP 不做——Alpha 用真实测试数据决定 | C3 | +| 重叙事(前人日志等) | 世界观厚度 = 碎片点缀,卖点在玩法不在文本 | game-concept 表现层 | +| 多人 / 在线要素 | 单机单人为前提,服务核心受众定位 | game-concept 目标受众 | +| MVP 内做:音频/教学/天赋存档/战斗动画/关内自由地图 | 先验证核心循环,打磨项一律排后 | systems-index 里程碑表 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1 | 2026-09-14 | 建立独立版入口层(6 节结构):素材取自 game-concept / game-pillars / systems-index / art-bible 与各系统 GDD 定稿;本文只做导航与共识,不另立事实 | diff --git a/design/gdd/legacy-2026-09-13/a1-board-generation.md b/design/gdd/legacy-2026-09-13/a1-board-generation.md new file mode 100644 index 00000000..954b8c60 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/a1-board-generation.md @@ -0,0 +1,416 @@ +# A1 扫雷棋盘生成(Board Generation) + +> **Status**: In Revision(2026-09-09 full /design-review:MAJOR REVISION NEEDED——8 blocking 同日修订【B1 P1 可推理率两项达标判据+target 绝对锚 · B2 AC-6(a) 改写 · B3 雷数取整+布雷语义钉死 · B4 F2≡Schema 合流 · B5 AC-11 前提腿 · B6 断言 B 收紧达成区占比 · B7 第三轴登记约定 · B8 F2c 成本闸+禁墙钟】+ cheap-now 包【σ 打包修复 · 注记③改口 · |U|÷|F| 删承诺 · monotone_in_depth+断言 E · EC5 玩家侧语义 · N≥1 零雷护栏】;用户拍板三决策:两项判据+限雾 0.25 / target 域 [0.85,1] / 第三轴登记约定+后门。日志:`design/gdd/reviews/a1-board-generation-review-log.md`)· 原 Designed(2026-09-08/09 一次会话定稿;lean 门禁:systems-designer 起草 Formulas〔F1–F5 + 三轴分离口径〕· qa-lead 起草/验证 AC〔19 条 + A4×15 逐项映射;"覆盖矩阵无洞"自称经 2026-09-09 评审修正为 5 处标注问题,见日志〕;CD-GDD-ALIGN 按 lean 跳过,生产前人工复核 Player Fantasy) +> **Author**: user + agents(systems-designer〔Formulas〕· qa-lead〔AC〕;四结构决策〔生成-验证循环 / 接受最优+零合法接受最不坏 / 推理域 single_subset 冻结 / 密度浮点〕与 F4 严格零红线为用户拍板) +> **Last Updated**: 2026-09-09 +> **Implements Pillar**: P1(主——可推理率生成端保障,"出题人义务")· P4(越下越险的难度物质)· P3(雾成本空间面,间接) +> **Layer**: 2 Core · **Milestone**: MVP · **设计顺序**: 第 5 位 + +## Overview + +A1 是**确定性棋盘生成器**:每当战斗编排需要一张新扫雷图,A1 从 `(level_index, node_depth)` 难度参数(BoardGenTable)出发,一次性生成完整棋盘——尺寸、雷的布设、初始 3×3 安全区及其展开可达域、以及(该关已解锁的)变异布置。全部随机取数只经 `board_gen` 流(scope = 每次生成新图),同种子同参数必产同图——可回放是生成侧的硬承诺。玩家永不看见生成器,但每一轮都在体验它的产物:**P1"我算对了"的生成端保障就落在这里**——可推理率最低门槛是 A1 的承重设计,保证出题人出的题在生成时刻就可解;玩家感受到的不是"生成算法",而是"连续挖掘不坠入纯赌局、雾区边界诚实、越深越险"的棋盘品质。 + +所有权边界:A1 拥有**生成算法与 BoardGenTable 回填**(D2 Rule 3,schema 先行数值回填的 A1 兑现);变异的**约束声明归 A4、生成期强制归 A1**(候选池门控、cell_constraint、出生区排除、厚度/占比硬边界——A4×15 验收清单的执行主体);挖掘执行归 A2、换图编排归 B1、难度曲线调参归 C3(C3 仅依赖 A1 的**配置 schema**——类型依赖,难度参数经编排器/D2 传递,禁止反向调用生成)。没有 A1,游戏没有难度轴(关卡×深度)、没有 P1 的源头保证、A4 的变异无布置载体、C3 的曲线无落点——它是 Core 层的地基,也是全项目验收债务最集中的系统(A4×15 清单 + 最小安全格数下限 + 链不可达占比生产者义务都在此落位)。 + +## Player Fantasy + +**间接承重**(Section A 定稿:玩家永不看见生成器,但每轮都在体验它的产物)。A1 的情绪目标一句话:**"这局能算"的底气**——玩家不知道生成器的存在,但每一次"我算对了"都踩在它铺的地基上。A1 不制造情绪,它制造情绪的**前提条件**: + +- **P1 的源头保证**:可推理率生成参数 = **出题人义务**——每张图在生成时刻就承诺"从初始安全区出发,逻辑可推导的挖掘占比不低于门槛"。玩家输给的是自己选的赌(雾区/终局盲猜),而不是不可解的局。这是 D2"公平可信"的玩法侧对偶:D2 保证同种子同结果(可复现),A1 保证同参数同品质(可推理)。 +- **P4 的难度物质**:越下越险不是文案,是 BoardGenTable 的 (level_index, node_depth) 两轴数据——雷密、尺寸、雾占比随深度爬升,"下探"的体感由生成参数直接雕刻。 +- **P3 的空间面**:雾区"里面有价值"的承诺由生成期布置约束兑现——雾区无雷、雾贴雷簇(A4 Rule 3 结构推论),玩家在洞口前的权衡是真实的信息权衡而非装饰。 + +参考感受:扫雷原版"每局必有解"的玩家信念(原版实际不保证——本作把这份信念变成生成端契约);Into the Breach 每张谜题可解的设计诚实。**反向护栏(裁剪判据)**:**"纯猜局"是 P1 的死亡体验**——生成端若让玩家频繁坠入不可推理局面,结算端再华丽也救不回掌控感;A1 的红线 = 生成参数使可推理率跌破门槛即为**阻塞缺陷**(验收红线,非调参建议)。 + +> 注:`creative-director` 未经咨询——Lean 模式(仅 D/H 两节委托专家),生产前请人工复核本节情绪定位。 + +## Detailed Design + +### Core Rules + +1. **五阶段确定性生成管线**:① **参数解析**(BoardGenTable 行,D2 R1 floor 解析)→ ② **布雷(取整与落位钉死——B3 修订 2026-09-09)**:雷数 N = `round(mine_density × (W×H − 9))`(先定数,9 = 出生 3×3 恒排除);落位 = 在"全格 − 出生 3×3"域经 board_gen 流均匀布雷后,计算 0 格 flood 闭包及外扩 1 环,**落在环内的雷移除(只减不增——"先布后删",不重投、无迭代、确定性终止)**;初始 3×3 及可达域内恒无雷;N ≥ 1 由加载期谓词保证(R3 修订——零雷板拒绝)。实际雷数 |M| ≤ N,漂移 = 环内移除量、入指标集观测→ ③ **变异布置**(先布雷后布雾——雾的 cell_constraint=safe_nonzero 要求雷先就位)→ ④ **生成-验证循环**(布雾完成后测可推理率——A4 要求的雾边双向净效应因此内生于测量;不达标重试)→ ⑤ **产出与上报**(棋盘 + 生成期指标)。五阶段顺序冻结;每阶段失败语义见 States 表。 +2. **可推理率定义(承重,P1)**:`inferability = 求解器确定性推导格数 ÷ 总安全格数`。推理器 = **单点规则 + 子集消减**(MVP 冻结此域——改域 = 改可推理率定义,走 GDD 修订非调参);模拟从初始展开后的可见状态出发,与玩家信息严格同步(未开格不可见、禁作弊读取雷位)。行参数 `inferability_target ∈ [0.85,1]`(B1 修订收窄——域下限即"纯猜局红线"的**绝对锚**可判定载体)是 P1 门槛的可判定形式;每图输出实际 inferability 与 inferability_core(剔除雾格后的可推理率,B1 修订——生成期指标,审计与批量断言的数据源)。 +3. **生成-验证循环与降级**:实测 inferability < target → 重试(同图种子实例内继续取数,**不重开 scope**——回放序列仍是 master_seed + scope 计数器的确定函数);上限 `max_gen_attempts` 次后**接受可推理率最高的尝试出图 + 强告警埋点**(用户定稿)——运行期不死锁、开发期暴露参数问题;降级率按 (level_index, node_depth) 埋点 + 调参期批量统计断言(A4 ⑭ 同款,红线:inferability < target 的图批量占比须为 0 或趋零,阈值与样本量本 GDD 定——见 Tuning Knobs)。 +4. **初始安全区与可达域**:3×3 无雷;可达域 = 从出生区经 0 格的 flood 闭包**外扩 1 格**(保守上界,任意 A2 展开定义的超集),生成期确定性计算(不依赖 A2 运行期行为);可达域内永无变异(A4 Rule 7);**A2 实际展开域 ⊆ A1 排除域**(A2 GDD 回验项)。 +5. **变异布置执行(A4 约束的执行侧)**:候选池门控(消费 `variant_pool_gate` 谓词)→ `cell_constraint` 生成期强制(雾格只落 8 邻含雷的非 0 安全格)→ 出生区及可达域排除 → **雾形硬边界**(厚度≤2【≡任意 3×3 窗口非全雾】/ f≤8×mine_density)→ 区域参数来自行参数(region_count / target_ratio);多变异选取消费 board_gen 流、**双实例布置身份与取号独立**(A4 EC11);布置无解 → 降级(跳过变异、产物合法无变异图、实际达成数按 shortfall 分级上报——A4 EC2 契约)。 +6. **board_gen 流纪律**:一次 `open_stream("board_gen")` = 一张图的全部取数(布雷/雾形/变异选取/重试共用该实例);流外零随机、`randomize()` 禁用(D2 Rule 5)。 +7. **清图预算构成(设计期聚合义务,A4 ⑤)**:雾致预算增量 = 链不可达雾格数 × `dig_ap_cost`(A3 价目,只读引用);生成期**计算并输出链不可达雾格占比**(B1 条款 5 对账输入的生产者义务);调参期聚合校验违例 = 修正行参数(非运行期强制)。 +8. **最小安全格数下限**:`min_safe_count`——安全格总数低于下限的参数组合在加载期参数域校验拒绝(A3 EC5 退化图刷清图奖励的钓鱼对冲;生成期产物不变式断言)。 +9. **biome 数据字段**:BoardGenTable 行携带 `board_biome`(StringSet **非空**,域校验致命——A4 ⑮);biome 挂行参数、**战斗域恒定**(同 (level,depth) 行的 biome 静态恒定——A3 OQ6 防钓鱼的结构保证,无逐图重掷接口存在)。 +10. **零特判产物 + 数值承载**:生成器不产生需要既有系统特判的棋盘(数字计数恒真、清图判定含雾格——A4 Rule 4 的产物侧兑现);BoardGenTable 为**首个进度键表回填**(键 (level_index, node_depth),depth-0 锚行前置——D2 Rule 4/6);A1 代码零字面量、读取经 ConfigDB。 + +### BoardGenTable Schema 定稿(D2 回写义务) + +| 字段 | 类型/范围 | knob | 说明 | +|---|---|---|---| +| board_width / board_height | int [8, 64] | ✓ | 图尺寸(MVP 量级护栏,非推导界) | +| mine_density | float (0, 0.5] | ✓ | 雷密度(尺寸解耦;f≤8d 分母对齐;上界为 MVP 护栏) | +| inferability_target | float [0, 1] | ✓ | **P1 门槛**——可推理率下限(承重 knob) | +| solver_rules | enum {single, single_subset} | ✗ 冻结 | 推理规则域(MVP 恒 single_subset——定义域非调参) | +| min_safe_count | int [1, 999] | ✓ | 最小安全格数下限(A3 EC5 对冲) | +| max_gen_attempts | int [1, 32] | ✓ | 生成-验证循环重试上限 | +| fog_region_count | int [0, 32] | ✓ | 雾区域数参数(0 = 本行无雾) | +| fog_target_ratio | float [0, 1) | ✓ | 雾面积占比参数(跨字段校验见下) | +| board_biome | StringSet 非空 | ✓ | 图类型标签(F1 输入前提;域校验致命) | + +**校验**:R1 键结构((level_index, node_depth) + depth-0 锚行,D2 既有);域校验全谱(inferability_target 域 [0.85,1]——B1 修订收窄,P1 绝对锚);**跨字段可判定谓词(A4 ⑩ 参数域化;本行 = F2 规范合取形的展开——B4 修订 2026-09-09:原三条款速记缺 count=0 ⇒ f=0 腿、在 (count=0, f>0) 角点上与 F2 答案相反,已废)**:`(fog_region_count = 0 ⇒ fog_target_ratio = 0)` ∧ `fog_region_count ≥ 1 ⇒ (0 < fog_target_ratio ≤ 8 × mine_density ∧ fog_target_ratio < 1 ∧ fog_target_ratio ≤ 0.25)`(f≤8d / 禁全覆盖 / **雾占比绝对上限 0.25——B1 修订:MVP 量级护栏非推导界,堵"极雾行以雾元知识撑满可推理率"的度量空心**);**布雷数下限(R3 修订)**:`round(mine_density × (W×H − 9)) ≥ 1`(零雷板拒绝);**min_safe 可行性谓词(F2b,2026-09-08 增补)**:`W×H − round(mine_density × (W×H − 9)) ≥ min_safe_count`(sound 保守、论证见 F2;计数可行 ≠ 连通可行,clearable 生成期闸不可互替);**成本可行性谓词(F2c,B8 修订)**:`max_gen_attempts × W × H ≤ 16384`(provisional 量级护栏,正式值归 OQ2 ADR;**运行期禁墙钟时间逃生——文本级契约**:耗时依赖输出即破同种子同图);**字段标记 `monotone_in_depth`(可选,非 knob——R2 修订)**:难度方向声明(size/density/target 类标记;fog_region_count 允许教学性非单调、不标),F4 断言 E 载体,只校验方向不校验数值、数值所有权仍归 C3——以上违规均加载期致命。 + +**第三轴落点登记(B7 修订 2026-09-09,用户拍板:登记约定 + 预留后门)**:game-concept 定稿难度 = f(关卡序号, 节点深度, **精英/Boss 修饰**),本表冻结键 (level_index, node_depth) 对第三轴的约定如下——① **Boss 图 = 各 level 最大深度(Dmax)行**:关底 Boss 恒为该关最后节点,Dmax 行即 Boss 行(线性关零成本表达);② **精英节点**:schema 预留 `elite_` / `boss_` 前缀字段命名空间(与 D2 DifficultyTable Rule 4 对称;本 GDD 不定义任何修饰数值、不预写修饰枚举);③ **C1/C3 定稿强制重审(OQ6)**:若节点形态使精英性无法由 (level_index, node_depth) 唯一确定(分支地图同深度并存精英与普通节点),走 D2 Rule 4 既有改键通道(接口漂移迁移)——迁移退路已在冻结当下登记,不再是无后门的死冻结。 + +### States and Transitions + +生成管线为无状态函数(每次调用独立、可重放)——五阶段失败语义: + +| 阶段 | 失败条件 | 语义 | +|---|---|---| +| 参数解析 | 行缺失(R1 保证有行)/ 参数域违例 | 加载期校验已拦(D2);运行期不失败 | +| 布雷 | N = 0(round(d×(W×H−9)) = 0——零雷板加载期拒绝,R3 修订:A3 EC5 钓鱼活体 + F4 全断言隐形穿越) | 运行期不失败(域内 N ≥ 1 恒可布;先布后删见 Core Rule 1②) | +| 变异布置 | cell_constraint 无解(连通分量不足——A4 EC2) | **静默降级**:跳过变异、产物合法、shortfall 上报(⑦⑬) | +| 验证循环 | 三分支(F5 验收算子):达标尝试存在 → **达标即出**(k*,非全局 argmax;达标 = F5 两项判据:inferability 与 inferability_core 各 ≥ target——B1 修订);attempts 用尽无达标但有合法 → **接受最优 + 强告警埋点**(Rule 3);attempts 用尽且**零合法** → **接受最不坏(确定性劣度字典序)+ 最高级告警 + 非法产物标记**(用户定稿 2026-09-08——玩家永不卡死,红线由 F4 断言 A′ 批量守) | +| 产出 | —— | 棋盘 + 八项指标集(实际 inferability【含 metric_version】 / inferability_core【剔除雾格后的可推理率,B1 修订】 / 实际雾占比【定式 = \|F\| ÷ (W×H)——与 mine_density 同分母族、f≤8d 口径自洽,qa-lead G3 补全 2026-09-09】 / 实际雷数与实际 \|S\|【B3 修订——先布后删漂移观测量,|M| ≤ N 断言载体】 / 链不可达占比 fog_burden_ratio / Δbudget_fog / attempts_used / 降级标记【含非法产物标记】) | + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| D2 | 双向 | BoardGenTable schema + 数值回填(本 GDD 义务,首个进度键表);R1 行解析;`board_gen` 流(换图时 open_stream 一次) | +| A4 | 执行主体 | **A4×15 验收清单逐项落位**(Dependencies 节全表引用,本文是 15 项的唯一执行落点);消费 VariationTable / variant_pool_gate | +| A2 | A1 → A2 | 棋盘产物(格状态 + 变异标记 + 出生区);初始展开执行归 A2(免费/计入清图/无奖励——A3 定);**A2 实际展开域 ⊆ A1 排除域**(A2 GDD 回验项);挖开上报携带变体标记(A2×4 之一) | +| B1 | 间接 | 换图编排为调用方(编排器经 B1 触发,B1 GDD 定接线);生成期指标输出 = 条款 5 对账输入(链不可达占比生产者义务) | +| C3 | 类型依赖 | BoardGenTable schema 消费方(曲线调参经 D2/编排器,禁反向调用生成——索引 C3←A1 方向说明);曲线导出工具(C3 期交付物)+ **A1 定稿前的临时抽查载体**(D2 OQ5 排期倒挂,本 GDD OQ 承接) | +| B4 | 间接 | board_biome = 图类型上下文(差异化插槽输入;行参数静态恒定——防钓鱼结构保证) | +| D1/B5 | 无直接 | 棋盘呈现归 D1/A2;biome 色板变体的数据源(AD 换皮) | + +## Formulas + +> systems-designer 代理起草(lean 门禁,2026-09-08;2026-09-09 full 评审 B1/B3/B4/B6/B8/R2/R4/R5 修订)。**口径总纲**:F1 度量**风险轴**(免赌开据占比 + 剔除雾格的第二判据)、F3 度量**成本轴**(AP 负担)、F2/F2b/F2c 守**参数域**(加载期可判定——F2c 成本上限为确定性死线,禁墙钟逃生)、F5 定**验收算子**(循环三分支 + 两项判据达标)、F4 守**批量红线**(调参期)——三轴分离,禁互相折算:雾板可推理率高而预算负担仍在,恰为两轴必须分开的理由。推理器与布置算法的实现选型不在本节(ADR 期事务),本节只定行为契约。 + +### F1 可推理率度量(inferability_measure) + +``` +inferability(B) = |R∞(B)| ÷ |S(B)| +inferability_core(B) = |R∞(B) ∖ F(B)| ÷ max(1, |S(B) ∖ F(B)|) ——剔除雾格后的可推理率(B1 修订 2026-09-09:防雾元知识单独撑满门槛) + +R₀ = 出生 3×3 ∪ (出生 3×3 经 0 格的 flood 闭包) ——生成期确定计算,剔除"外扩 1 格"环 +R∞ = R₀ 在下列两条闭包算子下的最小不动点: + ① 连锁:x ∈ R 且 x 为 0 格 ⇒ x 的 8 邻安全格全部入 R + (真实级联——雾格可被链开但不传播,A4 Rule 4) + ② 推导开据:c ∈ S∖R 且 c 与 R 8 邻接,且求解器在可见信息态上 + 能以合法信息源唯一判定 c 非雷 ⇒ c 入 R +合法信息源(穷尽枚举;新增信息源 = 改可推理率定义 = GDD 修订,非调参): + (a)数字约束——可见数字格上以冻结规则域 solver_rules(single_subset)推理 + (b)雾元知识——c 属于可见雾区 ⇒ c 非雷("雾区无雷"为玩家可见知识, + A4 Rule 3 / D5 教学义务——建模知情玩家,非作弊读取雷位) +``` + +**口径钉死注记(承重,Core Rule 2 的精确化)**:① Core Rule 2"确定性推导格数"钉死为**免赌确定揭示格数**——初始展开 ∪ 连锁揭示 ∪ 推导开据三通道全部计入分子。字面窄读(仅数字推导)会使任意好图远低于常规门槛,非立法本意。② **初始展开格计入分子与分母**(S 含初始格、R₀ ⊆ R∞)——与 P1 承诺语句"从初始安全区出发,逻辑可推导的挖掘占比不低于门槛"对齐。③ **雾格入分子的两通道(R5 修订改口——原"两通道全覆盖"在算子邻接要求下非定理:恒无 R 邻格的雾格不入分子)**:链可达雾格经算子①入分子;**与 R∞ 邻接的**链不可达雾格经信息源(b)入分子;恒无 R 邻格的雾格计入终局残余——与挖掘相邻性(A3 限 8 向相邻)口径对齐,公式本身不变。雾格挖掘是**零雷险**行为(玩家可见知识),故计入"免赌"口径;其 AP 全价代价不在此度量、由 F3 单独承载(两轴分离);"免赌 ≠ 可推理"的度量空心风险由第二达标判据(inferability_core,B1 修订)与 F2 雾占比绝对上限 0.25 合围。④ 求解器初始可见态取 **0 格 flood 闭包内芯**而非保守可达域全体("外扩 1 格"环非自动展开的必然产物,计入会高估玩家信息);须与 A2 立对偶回验:既有"A2 实际展开域 ⊆ A1 排除域"之外,新增"**A1 求解器初始态 ⊆ A2 实际初始展开域**"(度量保真回验,登记 A2 GDD;若 A2 定稿展开更小,回退口径 = 出生 3×3 最小态)。⑤ **次序无关性(度量不变式)**:两算子对 R 单调 ⇒ 最小不动点唯一、与开据次序无关——实现可任意定序而度量不变(行为契约,兼防一类实现方差 bug)。⑥ 每图指标输出携带 `metric_version`(当前 1)——防推理器实现漂移静默改定义(联动 F4 基线)。(B1 修订未动 R∞ 定义——度量口径不变、仅验收判据增列与指标集扩容,version 不跳变;未来改 R∞/信息源域则 version=2 + 向量重制。) + +**附带合法性不变式(clearable,新增)**: + +``` +clearable(B) :⇔ ∀c ∈ S:c 与出生 3×3 在"安全格 8 邻接图"上连通 +``` + +理由:inferability 门槛**不蕴含**可清图——终局对赌残余(拉低可推理率)与矿壳包围的不可达安全格(使 `cleared` 永不可达)是正交缺陷;1 个不可达格在 126 安全格的板上仅拉低 0.008,0.95 门槛照过,而该板是功能性残废。故清图可达性是**合法性**谓词(违规 = 尝试作废重试,非"接受最优"候选),与质量度量分属两闸(验收算子见 F5)。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| B | 棋盘实例 | —— | 一次尝试的产物(含雷布、雾布、出生区) | +| S(B) | 格集 | ⊆ 全格 | 安全格全集(分母;含雾格、含初始格、含不可达格) | +| F(B) | 格集 | ⊆ S | 雾格集(雾区无雷,由布置约束保证) | +| R₀ / R∞ | 格集 | R₀ ⊆ R∞ ⊆ S | 求解器初始可见态 / 免赌揭示闭包 | +| solver_rules | enum | {single_subset} 冻结 | 推理规则域(定义域非调参;信息源(a)的边界) | + +**Output Range:** inferability ∈ [0, 1]、inferability_core ∈ [0, 1](无雾板两值恒等)。正常域:合法产物下下界 ≥ 9/|S|。极端行为:`target = 1` 要求全图零非确定残余(小图高密下结构性难达——合法参数,由 F4 红线暴露、不加载期拒绝);`target < 0.85` 随 B1 域收窄加载期拒绝(原"`inferability_target = 0` 恒过"形态消失)。不可达安全格若存在则先触发 clearable 违例(合法性闸),不会以"拉低质量分"方式静默出图。 +**Example:** 12×12 图(144 格),mine_density=0.15 → N = round(0.15×135) = 20(B3 修订钉死口径;设环内移除 0,|M|=20)、S=124;雾 2 区共 9 格。R₀=21,全图 0 级联闭包 58(含 5 个链可达雾格);数字推导开据 51 + 雾元知识开据 4(链不可达雾格)+ 推导触发级联 8 = 63 → R∞=121;残余 3 格 = 一处终局非确定对 + 1 孤立残余格。inferability = 121/124 ≈ **0.976** ≥ target 0.95;inferability_core = (121−9)/(124−9) = 112/115 ≈ **0.974** ≥ 0.95 → 两项判据达标,attempts_used=1。 + +### F2 雾占比参数域谓词(fog_domain_ok)+ F2b min_safe 可行性谓词 + F2c 成本可行性谓词 + +``` +fog_domain_ok(r) :⇔ (r.fog_region_count = 0 ∧ r.fog_target_ratio = 0) + ∨ (r.fog_region_count ≥ 1 ∧ 0 < r.fog_target_ratio ≤ min(8 × r.mine_density, 0.25) + ∧ r.fog_target_ratio < 1) (0.25 = 雾占比绝对上限——B1 修订 2026-09-09) + +min_safe_feasible(r) :⇔ r.board_width × r.board_height + − round(r.mine_density × (r.board_width × r.board_height − 9)) + ≥ r.min_safe_count (9 = 出生 3×3 恒排除的下界;sound 保守) + +布雷数下限(R3 修订 2026-09-09):round(r.mine_density × (r.board_width × r.board_height − 9)) ≥ 1 + (零雷板拒绝——初始展开即 cleared 的 A3 EC5 钓鱼活体、标中旗经济恒空、F4 全断言隐形穿越) + +cost_feasible(r) :⇔ r.max_gen_attempts × r.board_width × r.board_height ≤ 16384 + (F2c——B8 修订 2026-09-09;C = 16384 为 provisional 量级护栏,正式值归 OQ2 ADR; + 运行期无墙钟时间逃生【文本级契约:耗时依赖输出即破同种子同图】,确定性死线必须加载期可判定) +``` + +**与 Schema 节既定校验的关系**:F2 是 Schema 节跨字段谓词的**规范化合取形**,等价(B4 修订 2026-09-09:原三条款速记缺 `fog_region_count = 0 ⇒ fog_target_ratio = 0` 腿,在 (count=0, f>0) 角点上与 F2 答案相反——Schema 节已改采本规范形消除分歧;该角点已入 AC-2 违例集)。**必要上界推导(边计数)**:每雾格 ≥1 条到非雾雷的邻接边 ⇒ 邻接边数 ≥ |F|;每雷 ≤ 8 邻 ⇒ |F| ≤ 8|M| ⇒ f ≤ 8d。**必要 ≠ 充分**:连通性缺口(雷膨胀皮肤碎裂,A4 EC2)不由本式判定,由静默降级 + F4 兜底。**F2b 为 Schema 校验行 delta(2026-09-08 本节同批回写)**——min_safe_count 原仅产物侧不变式(Rule 8),参数侧联合不可行域(小图×高密×高下限)现于加载期拦截;**sound 性论证(B3 修订补)**:N = round(d×(W×H−9)) 先定数、先布后删只减不增 ⇒ |M| ≤ N ⇒ |S| = W×H − |M| ≥ W×H − round(d×(W×H−9))——钉死 round 取整后恒成立(其他取整为非法实现)。**注记**:计数可行 ≠ 连通可行——clearable 生成期闸不可互替。违规均加载期致命。**F2 裁决边界注记(B1/B8 修订)**:下文"不加第三条跨字段校验"的裁决拒绝的是 *inferability 可行性* 静态联合谓词(无可判定静态界)——雾占比绝对上限 0.25(度量语义护栏)与 F2c 成本谓词(确定性成本死线)均有可判定形式且不声称预测 inferability,不在该裁决覆盖内。 + +**与 inferability_target 的联合可行域——裁决:不加第三条跨字段校验。** 理由:① **无可判定静态界**——inferability 由 0 格拓扑、雾形、出生位置主导,聚合参数不决定它,任何 sound 加载期联合谓词要么空洞要么误杀;② 架构既定——暴露机制就是"运行期接受最优不死锁 + 强告警埋点 + 调参期批量断言",第三闸重复且更弱;③ **补偿保证(与 F4 绑定生效)**:F4 红线 + 降级率埋点使联合不可行域在首次批量生成时必然现形。**回写义务(advisory,非校验)**:Tuning Knobs 注记——target、fog_target_ratio、mine_density 同向调高时降级率**结构性**上行,属参数域边界而非实现缺陷。 + +**Output Range:** bool,加载期判定;违规 = 致命(D2 Rule 6 通道)。极端:d=0.5 时 8d=4,`f<1` 成为绑定约束;d→0 时几乎任何正 f 均被拒——小雾量在稀雷图结构性不可布,属正确拒绝;f > 0.25 恒拒(B1 上限腿,与 d 无关);attempts×W×H > 16384 恒拒(F2c,B8)。 +**Example:** (d=0.15, f*=0.12, count=2) ✓ 放行;(d=0.06, f*=0.50):0.50 > 0.48 ✗ 致命;(count=3, f*=0) 一致性腿 ✗;(count=0, f*=0) ✓(level 1 无雾行标准形);(count=0, f*=0.05) ✗ 致命(count=0 ⇒ f=0 腿——B4 修订角点,原三条款速记误放行);(d=0.15, f*=0.85, count=32) ✗ 致命(0.85 > 0.25 雾占比绝对上限——B1 修订,极雾行加载期判死)。F2b:10×10、d=0.10、min_safe=50 → 100 − round(0.10×91) = 91 ≥ 50 ✓。F2c:10×10×16 = 1600 ✓、12×12×16 = 2304 ✓、64×64×32 = 131072 ✗ 致命(B8——该组合原可合法入库,GDScript 估算换图相位同步卡顿秒级至百秒级)。 + +### F3 清图预算增量构成(fog_budget_increment) + +``` +U(B) = F(B) ∖ Chain₀(B) Chain₀(B) = R₀ 经算子①(0 格级联)单独闭包的可达集 +Δbudget_fog(B) = |U(B)| × dig_ap_cost (量纲闭合:格 × AP/格) +fog_burden_ratio(B) = |U(B)| ÷ max(1, |S(B) ∖ Chain₀(B)|) +``` + +**口径钉死注记**:① A4 排除声明与 A1 Rule 7 所称"链不可达雾格占比"的 **B1 条款 5 对账精确口径 = fog_burden_ratio**(付费格分母——雾在最小清图 AP 负担中的权重);雾内部分母版本 |U|/|F| **不单列输出**——可由指标集(实际雾占比 + Chain₀/U 重算)按需还原〔R9 修订:原"仅作调参诊断附输出"承诺零落点,删承诺〕,不作对账输入。② 分母 = |S ∖ Chain₀| = 最小清图付费挖掘格数(初始展开与连锁免费、其余每格 dig_ap_cost——A3 价目只读引用),分母下限 1 防全连锁退化板除零。③ 链可达性取决于未开 0 格隐藏拓扑,玩家决策时点不可求值——本式是**生成期**事后度量(A4"玩家侧按全价做预算"的保守启发式不与本式冲突,两口径服务不同主体)。**聚合义务(A4 ⑤ 公式化)**:逐板计算入指标集;调参期按行批量聚合出核算报告供 C3 修正行参数。**无运行期强制**;A1 只拥有**量**的计算与输出,不拥有"够不够"的判(归 A1/A3 契约与 C3)。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| U(B) | 格集 | ⊆ F | 链不可达雾格集(全价段) | +| Chain₀(B) | 格集 | R₀ ⊆ Chain₀ ⊆ S | 纯 0 级联可达集(免费段,含初始展开) | +| dig_ap_cost | int [1,8] | 当前 1 | A3 价目,只读引用(knob 归 A3) | + +**Output Range:** Δbudget_fog ∈ [0, |F| × dig_ap_cost];fog_burden_ratio ∈ [0, 1]。fog_region_count=0 → 两值恒 0。极端:U=F(雾区全部链不可达)→ 雾板退化为纯 AP 税——A4 反向护栏与 B4 定价约束③的观测对象;U=∅(雾全链免开)→ 雾零成本——B4"糖果区"病理在此指标上直接现形。 +**Example:** F1 例板:F=9、链可达雾 5 → U=4;dig_ap_cost=1 → **Δbudget_fog = 4 AP**;付费格 = 124 − 58 = 66 → **fog_burden_ratio ≈ 0.061**。 + +### F4 批量统计断言(batch_statistical_assertions,调参期离线) + +``` +批量域 = BoardGenTable 全部行键 (L, D)(R1 floor 解析保证任意请求点归行) +批(L,D) = { generate(L, D, derive_at("board_gen", σ(L,D) + i)) : i ∈ [0, N) } + (derive_at 纯函数、不推运行期计数器;σ 基址清单入版本库,批量跨迭代可复现) + +断言 A(可推理率红线,用户定稿):∀行:|{ b : inferability(b) < target行 ∨ inferability_core(b) < target行 }| = 0 ——严格零(B1 修订:两项判据批量对照) +断言 A″(target 绝对下限,B1 修订 2026-09-09):∀行:target行 ≥ 0.85(provisional 锚——加载期域 [0.85,1] 之外的调参期兜底) +断言 A′(非法产物零容忍,F5 配套):∀行:|{ b : 非法产物标记 }| = 0 +断言 B(雾出现下限 + 降级率上限,仅 count ≥ 1 行):实际达成区数 = fog_region_count 的板占比 ≥ 0.95(B6 修订收紧——原"含雾板占比 ≥ 0.95"对部分达成 shortfall 失明〔count=2 全板只达成 1 区时原式满分〕;收紧后一并承载 A4⑭"出现率下限/降级率上限"两件套) +断言 E(难度方向 advisory,R2 修订):对 schema 标记 monotone_in_depth 的字段,同 level 沿 depth 弱递增——违例 warning 级开火(不拦合法呼吸位设计,调参期人工复核) +断言 B′(反向断言,仅 count = 0 行):含雾板数 = 0(门控泄漏 = bug 信号,转 bug 流程非调参) +报告 C:按行 p10/p50/p90(实际 inferability)、p50/p90(Δbudget_fog 与 fog_burden_ratio) + ——p10 供 B1 条款 3 对账(消费 actuals 分布而非 target 参数) +诊断 D(非断言):重试率(attempts_used ≥ 2 占比)、attempts_used 均值、EC2 shortfall 分级分布 + +MVP 默认:N = 256/行;σ(L,D) = 10^9 + 2^20·L + 2^12·D(R4 修订:原 1000·L+D 打包在 D≥1000 时精确碰撞〔(1,1001) ≡ (2,1)〕且同 L 相邻深度行批量区间 255/256 种子重叠;新打包使行区间 [σ, σ+255] 两两不交;远离运行期取号域不变。登记种子按失效协议全部重搜——夹具 σ 为登记时值) +``` + +**阈值裁定理由**:① **红线严格零**(用户定稿):A1 反向护栏已定"跌破门槛 = 阻塞缺陷"——任何一例失败即触发参数修正会话,迫使决策显式化;运行期"接受最优/最不坏"是安全网,批量红线是调参期纪律。② **N=256**:95% 下限的稳定通过要求真实出现率 ≥ 0.98(3σ 余量 ≈ 99.97%)。③ **雾出现下限 0.95 而非 1.0**:EC2 连通性降级是合法运气事件,5% 容差容纳自然方差;系统性病理(level 2 小图低密关雾成片失败)呈现 ~0% 出现率,与 0.95 相距悬殊,检出无歧义。(B6 修订:断言 B 收紧为达成区占比后,本容差同时容纳完全降级与部分达成两类合法运气事件。)④ 断言 B′ 零成本反向杠杆:门控 bug(如 level 1 出雾)一例即现形。⑤ 成本:行数 × 256 板 × ≤32 attempts 的生成+求解模拟,离线分钟级(成立域 = worklist 级求解实现 ∧ 大面积行稀少;naive 全板扫描实现 + 数行 48×64 板即小时级——B8 修订条件化,分片续跑义务见 OQ2)。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| N | int | 256(MVP 默认) | 每行批量样本量 | +| σ(L,D) | int | ≥0 | 批量种子基址(版本库清单) | +| target行 | float [0,1] | 行读值 | 该行 inferability_target(期望由表值推导,不写死) | + +**Output Range:** 各断言 bool;报告 C 为数值表。极端:target 1.0 的行在断言 A 下大概率开火(合法、预期信号);断言 B′ 失败即实现缺陷而非参数问题。 +**Example:** 行 (2,0)(fog unlock 关)N=256:inferability 两项判据达标 256/256 → A 通过;达成区数 = count 的板 251/256 = 0.980 ≥ 0.95 → B 通过;重试率 3.1% → 诊断记录。行 (3,2) 达成区数 = count 的板 220/256 = 0.859 < 0.95 → B 失败 → 修正行参数(区域数/尺寸/密度侧保证,禁放宽 A4 Rule 3)。 + +### F5 生成-验证验收算子(attempt_acceptance) + +``` +合法尝试集 Lm = { B_k : legality(B_k), 1 ≤ k ≤ max_gen_attempts } +legality(B) :⇔ clearable(B) ∧ ( |S(B)| ≥ min_safe_count ) + ∧ 结构不变式全过(雾约束 cell_constraint / 出生区及可达域排除 / + 厚度≤2【任意 3×3 窗口非全雾】/ f≤8d / 数字计数恒真 / 初始展开不触雾) + +B_out = B_{k*},k* = min{ k : B_k ∈ Lm ∧ inferability(B_k) ≥ target ∧ inferability_core(B_k) ≥ target }(达标须两项判据同时满足——B1 修订:剔除雾格判据防止"雾区无雷"知识单独撑线) + (达标即出、非全局 argmax——重试语义的自然形式化) +若 k* 不存在且 Lm ≠ ∅:B_out = B_{k**},k** = min{ k : inferability(B_k) = max_{j∈Lm} inferability(B_j) } + + 强告警埋点 + 降级标记(tie 取最早,零额外取数) +若 Lm = ∅(attempts 耗尽零合法):接受最不坏尝试出图(用户定稿 2026-09-08)——确定性劣度字典序 + k*** = min{ k : (不可达安全格数, max(0, min_safe_count − |S(B_k)|), k) 字典序最小 } + + 最高级告警 + 非法产物标记(玩家永不卡死;批量红线由 F4 断言 A′ 守) +``` + +**注记**:① 全部尝试共用同一 board_gen 流实例(Rule 3/6),k*、k**、k*** 与 B_out 同为流取数的确定函数——回放语义闭合。**scope 事件边界操作定义 = 生成函数完整返回**(重试不产生新取号)——D3 快照切点天然落在边界外(D2"消费侧遵守"条款的 A1 具体化);**生成期表值单次读取**(行参数在生成开始时快照,生成期内不重读——防热重载撕裂 attempt 语义,A4 EC8 同族)。② 合法性闸(clearable/min_safe)在质量评分**之前**:不可清图产物不进入"接受最优"候选——否则质量分会掩盖功能残废。③ "接受最不坏"分支的确定性劣度 = (不可达安全格数, min_safe 缺口, 序号) 字典序——零额外取数、可重放。④ B1 编排不得把 attempt 边界当 scope 事件(登记 B1 接线注记)。 + +### 显式排除声明(R4 式) + +本 GDD **无平衡曲线公式**。BoardGenTable 每行数值(尺寸/密度/门槛/雾参数的具体取值及其随 (level_index, node_depth) 的曲线)归 **C3 调参**(经 D2 R1 解析/编排器传递,禁反向调用生成);A1 只拥有 schema 域、跨字段可判定谓词(F2/F2b/F2c)、生成算法行为契约与度量口径(F1/F3/F5)。逐项排除清单: + +- **每行难度数值与曲线** → C3(含 max_gen_attempts 取值策略)。本 GDD 的 MVP 参考行仅为 schema 自洽性证明,非平衡事实、非回填默认值。 +- **挖掘风险不等式**(A3 F1 条款 3)→ **B1 单一闭合对账 owner**。A1 义务止于供给锚值 = **actuals 分布(F4 报告 C 的 p10)而非 target 参数**。措辞登记(registry 级,防口径漂移):"inferability 是盘面级免赌占比(生成侧);P(安全) 是单格认知概率(决策侧、经济口径)——免赌集内 P(安全) 恒 = 1(含雾格),风险讨论收缩到残余集(其安全格占比 ≤ 1 − inferability)。inferability 是 P(安全) 的**生成端结构性上界代理之占比形式**,不是 P(安全) 本身。" +- **弃图支配性**(条款 5)→ **B1 闭合**。A1 供给锚值 = 实际 inferability 分布 + fog_burden_ratio(生产者义务);两态验算与两条方向性护栏归 B1/B4。 +- **清图总预算可用量与全量校验判据** → A1/A3 契约(A4 排除声明既定);A1 只算**量**(F3),不拥有"够不够"的判。 +- **推理器实现**(single_subset 具体规则集、数据结构、传播算法)与**布置算法** → ADR 期事务;GDD 只冻结行为契约(合法信息源穷举、禁作弊读取、次序无关性、规则域枚举冻结、metric_version)。 +- **战斗侧 / 奖励定价 / biome 词表** → B1/B2/B3/B4/C1/D1 域;A1 的 board_biome 仅是非空 StringSet 数据字段。 + +### MVP 参考行(provisional——仅 schema 自洽性证明,C3 持调参所有权) + +| 字段 | L1·D0 参考行 | L2·D0 形状校验行 | +|---|---|---| +| board_width / board_height | 10 / 10 | 12 / 12 | +| mine_density | 0.10 | 0.12 | +| inferability_target | 0.95 | 0.92 | +| solver_rules | single_subset(冻结) | 同左 | +| min_safe_count | 50 | 60 | +| max_gen_attempts | 16 | 16 | +| fog_region_count / fog_target_ratio | 0 / 0(level 1 候选池空标准形——A4 F1) | 1 / 0.10(≤8×0.12 ✓——F4 断言 B 首个真实考验行) | +| board_biome | {"dungeon"}(词表 owner 未定稿,仅占位) | 同左 | + +自洽性验证:F2/F2b/F2c 全过(10×10×16 = 1600、12×12×16 = 2304 ≤ 16384);期望 |S| ≈ 91、R₀ ≈ 12–20 → inferability 期望 ≥ 0.95(L1 无雾 ⇒ core ≡ inferability;L2 雾 ~14/144 格、core 判据轻度收窄仍期望 ≥ 0.92——两项判据同时达标,B1 修订;attempts_used ≈ 1,F4 全绿理论位形)。 + +## Edge Cases + +- **EC1 If 候选池为空**(雾未解锁如 level 1,或 biome_tag 全不匹配):静默布置无变异图——空池合法非错误(A4 EC1/F1 的 A1 执行语义;参考行 L1 即此形态)。 +- **EC2 If cell_constraint 无解**(非 0 安全格面积或**连通分量尺寸**不足——A4 EC2 判据含连通性):本次布置尝试失败 → 跳过变异、产物为合法无变异图;BoardGenTable 要求 N 区而实际达成 k 0.25 加载期拒绝(F2 雾占比绝对上限腿,B1 修订)** | 雾存在感弱、P4 推进感哑火 | +| board_biome | {"dungeon"} | ——(词表 owner 未定稿,暂只校验非空) | —— | + +- **非 knob(结构冻结)**:solver_rules(定义域)、键结构与 depth-0 锚(第三轴登记约定见 Schema 节——B7:Boss=Dmax 行约定 + elite_/boss_ 命名空间预留 + OQ6 重审门)、五阶段顺序、合法信息源穷举枚举、F4 断言 A/A′ 严格零阈值(用户定稿——改阈值走 GDD 修订)、先布雷后布雾顺序、attempt 边界 = scope 事件边界、target 域下限 0.85 / 雾占比上限 0.25 / F2c 常数 16384(provisional 量级护栏——调整走 GDD 修订非调参,B1/B8 修订)。 +- **工具参数(登记默认非玩法 knob)**:N=256/行、σ 基址公式(版本库清单)。 +- **Knob 交互**:density × fog_target_ratio 同向调高 → 降级率结构性上行(F2 advisory);target × density 联合可行域无静态闸(F2 裁决,F4 兜底);尺寸 × min_safe 联合(F2b)。**跨 GDD 引用**:`ap_per_round`/`dig_ap_cost` 归 A3、`unlock_level`/`cell_constraint` 归 A4——禁止两表间复制参数(D2 Rule 1)。 + +## Visual/Audio Requirements + +A1 无自有渲染面——视觉义务是**数据供给规格**,呈现归 D1/A2/B5: + +1. **biome/tag = 换皮数据锚**(AD 门禁,game-concept 视觉锚 4):BoardGenTable.board_biome 与产物携带的 biome 标签是新 biome 色板变体的唯一数据源——新 biome 不新增资产只换色板,前提是 A1 产物正确携带标签。 +2. **变异格标记保真**:产物的雾格标记是 D1"边界可见"呈现(A4 D1 行规格)与"已开雾格 = 地板无字"的数据前提——标记错误即呈现欺骗(P1 诚实边界的生成侧根基)。 +3. **雾区连通域信息**:EC6 双雾区呈现合并归 D1 渲染域,A1 只保证实例身份与取号独立(AC-14),不保证视觉分离——D1 侧规格不受 A1 约束。 +4. **数字可读性的生成侧前提**:数字 = 8 邻雷数恒真(AC-6(g) 断言)——数字可信是"数字可读性"样张测试(game-concept 风险 #1)的逻辑前提。 + +无音频需求。 + +> 注:`art-director` 未经咨询——Lean 模式,生产前请人工复核本节与 art-bible 的一致性。 + +## UI Requirements + +A1 不拥有 UI。呈现义务全部经下游:棋盘渲染与开格反馈归 A2/D1、雾纹理规格归 A4 D1 行、生成期指标不面向玩家(遥测/审计域)。无 HUD 常显字段、无结算面——玩家对 A1 的全部感知都经"棋盘本体"。 + +## Acceptance Criteria + +> **测试载体与口径声明**:A1 是生成函数系统——冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)。**被测函数面**:`generate(level_index, node_depth, rng) -> Board`(+ 生成期指标集);**度量独立入口(qa-lead 裁决,分解面声明)**:推理器(R∞/inferability/metric_version)、F3 度量(Chain₀/U/Δbudget_fog/fog_burden_ratio)、合法性谓词(legality/clearable)以**独立纯函数入口**暴露——生成函数内嵌调用与独立入口**同一实现**,独立入口不引入新逻辑、仅暴露 F1/F3/F5 已定义的函数;独立入口的行为断言即内嵌调用的行为(次序无关性、防作弊等白盒性质测试以此为载体;若实现期 ADR 裁决推理器不可独立调用,AC-7/8/9/12 的单元腿须降级重议——登记缺口 G1,门禁评审列为 A1 实现契约条款)。**只读观测量**:产物格状态(雷布/数字/0 格/雾区标记/出生区)+ 八项指标(实际 inferability【含 metric_version】/inferability_core【B1 修订】/实际雾占比【= |F| ÷ (W×H)】/实际雷数与实际 |S|【B3 修订】/fog_burden_ratio/Δbudget_fog/attempts_used/降级标记【含非法产物标记与 shortfall】)。**测试种子取号**:经 `derive_at("board_gen", σ)` 纯函数(D2 R2 预览语义的测试侧合法用途,不推运行期计数器);测试脚本以该种子构造流实例属测试通道合法动作(AC-18 扫描豁免域);运行期 replay 语义(open_stream 全真路径)另测(AC-5)。 +> +> **夹具策略(裁决)**:默认根 = **MVP 参考行 L1·D0 / L2·D0**(干净检出)——参考行是 GDD 定稿的 schema 自洽性证明行,期望全部由表读值推导,对 C3 调参免疫;行为变体经 `--data-root` 夹具根注入(禁拷入 `data/tables/`),黄金图板与病态板经 Board class_name 直引**程序构造**(不经生成器、不经 load()/preload()——A4 AC-5 先例)。夹具规格登记: +> +> | 夹具根 | 内容(F2/F2b 均过,除违例根外) | 用途 | +> |---|---|---| +> | `a1_fog_domain` | f>8d 违例行 | AC-2/3(致命) | +> | `a1_min_safe` | 10×10/d=0.10/min_safe=92(F2b 违例:100−round(0.10×91)=91<92) | AC-2/3(致命) | +> | `a1_biome_empty` | board_biome=∅ 行(A4⑮) | AC-2/3(致命) | +> | `a1_pool_empty` | VariationTable 测试行(biome_tag=[ice]、unlock_level=1,承 A4 `variation_biome_tag` 同型)+ BoardGenTable 两行:(1,0) board_biome={dungeon}(pool=∅)/ (1,1) board_biome={dungeon,ice}(pool 非空) | AC-13 | +> | `a1_high_target@k` | 12×12/d=0.12/target=1.0/fog=1 区 0.05/min_safe=60/attempts=k∈{1,2} | AC-5/11 | +> | `a1_ec5@k` | 10×10/d=0.45/min_safe=1/target=0.85(B1 域收窄后合法档——原 target=0 已越域)/fog=0/attempts=k∈{1,2,8,16};σ* 登记(@1 零合法验证种子) | AC-12 | +> | `a1_two_regions` | 10×10/d=0.15/count=2/f=0.12/target=0.85/attempts=16;σ 登记 | AC-14 | +> | `a1_ec2_press` | f := 8×d 计算值恰界行(程序内动态赋值);σ*′ 登记(shortfall>0 搜索种子) | AC-15 | +> +> 登记种子(σ 值)依赖管线确定性:一经验证恒成立;实现或定义变更致失效 → 按失效协议重搜并登记(与黄金向量失效协议同源)。 +> +> **黄金图回归向量协议(D2 R2 先例同源)**:五类手工核验小板,每板 = **显式坐标规格**(尺寸/雷集/雾区格集/出生 3×3,程序构造不经生成器)+ **登记常数**((|R∞|,|S|) 整数对、**含雾板另登记 (|R∞∖F|, |S∖F|) core 整数对——B1 修订,由既有坐标规格派生、向量不重制**、关键格 R∞ 成员表、clearable、Chain₀/U 格集、Δbudget_fog、fog_burden_ratio 比对形 = 整数对 (|U|, max(1,|S∖Chain₀|))、metric_version=1——整数对比对,禁浮点容差)+ **逐格手工推导记录**(每格声明信息源 ∈ {数字约束(a)、雾元知识(b)、0 格级联①},入版本库随向量文件——此即手工常数的登记依据)。**板设计要求**:ⅰ 纯数字推理板(无雾;须含仅 single_subset 规则可开的推导格);ⅱ 链密集板(0 格级联闭包主导);ⅲ 含雾板(须同时含:链可达雾、链不可达雾、邻接出生 3×3 的雾区、寂静减项证据格、雾锚增项证据格【雾元知识开据格】);ⅳ 终局对赌残余板(inferability 严格 ∈ (0,1) 的非确定残余);ⅴ 不可达安全格板(clearable 违例形态)。**比对内容**:度量独立入口 + 合法性谓词输出与登记常数逐位相等。**失效处置协议**:失配 → 先按冻结定义复核手工推导 → 推导无误即实现缺陷(修实现);`solver_rules`/合法信息源域/metric_version 变更 = **换 solver_rules 即失效,走 GDD 修订 + 向量重制**(推理域冻结的非调参性质的行为侧兑现)。 +> +> **期望值推导纪律**:行为类 AC 期望 = ConfigDB/表读值推导(inferability_target/min_safe_count/max_gen_attempts/fog_region_count/unlock_level/dig_ap_cost/σ 公式全读值),不写死玩法数值;**唯一例外** = 黄金图向量手工常数(依据 = 上述手工推导记录);参考行 GDD 自注期望(|S|≈91 等)为说明非断言,任何 AC 不采用。 +> +> **致命类独立进程协议**:承 D2 AC-2b / A4 AC-4b——runner manifest 逐条登记"夹具根 → 期望退出码"(AC-3 三行,均 ≠0);单进程内只做测试通道收集断言。每用例先 `reset_diagnostics()`(承 D2 冒烟协议;warning/error 断言一律经诊断收集器)。 +> +> **out-of-scope 覆盖声明**(deferral 以对侧登记为生效条件):**O1 B1 接线**——attempt 边界非 scope 事件的编排侧断言、指标对账消费(actuals p10 非 target)归 B1 GDD AC(A1 侧生成半边已由 AC-5 计数器断言覆盖);**O2 F4 全量**——N=256/行 × 全行的断言 A 严格零/B 0.95/报告 C/诊断 D = 调参期离线批量工具域(工具本体为 A1 交付物,登记 OQ;σ 基址清单入版本库),冒烟仅载缩样(AC-10),生效条件 = 批量工具交付登记;**O3 A2 集成**——双包含回验(A2 实际展开域 ⊆ A1 排除域;A1 求解器初始态 ⊆ A2 实际初始展开域)登记 A2 GDD(A1 半边由 AC-6(a) 测试 BFS 对照承载);**O4 fault-injection 缺载体**——EC7 生成中热重载(同步单调用无重入点,构造性成立且注入不可)、EC10 内部异常 fatal、EC4 tie-取最早(需精确同分尝试)deferral 至实现期注入设施或可选种子搜索会话;**O5 性能预算**——生成耗时/换图相位无定稿预算值(Tuning Knobs OQ),perf-profile 期承载;**O6 呈现域**——EC6 雾区视觉合并归 D1、biome 色板换皮归 D1/art-bible;**O7 C3 全行审计**——曲线导出工具全行矩阵 diff 为 C3 期交付物(D2 OQ5 既有排期)。 + +**Schema 与加载期校验** + +- **AC-1(干净检出加载)** GIVEN 干净检出(默认根 = MVP 参考行),WHEN 冒烟加载 Core Rules-3 表清单,THEN ConfigDB Ready:BoardGenTable 含 (1,0)/(2,0) 锚行(R1 结构义务;**不断言行总数**——C3 加行免疫)、九字段类型域断言(域 = Schema 表范围)、`solver_rules` 定值 = single_subset(非 knob 冻结——唯一值断言)、其余八 knob 字段仅域断言(参考行值 provisional,期望由表读值推导);诊断收集器零 error 零 warning(域内零诊断)。 +- **AC-2(F2/F2b/biome 谓词·测试通道·程序构造)**(每腿先 `reset_diagnostics()`)GIVEN BoardGenTable schema **class_name 直引程序构造行实例**(A4 AC-5 先例),WHEN `validate()` 作用于违例集 {f>8d;fog_region_count≥1 ∧ f=0(一致性腿);f≥1(禁全覆盖腿);**fog_region_count=0 ∧ f=计算正值(count=0 ⇒ f=0 腿——B4 修订角点,原三条款速记对该输入误放行)**;**f=0.30 ∧ d=0.15(> 0.25 绝对上限腿,8d=1.2 未绑定——B1 修订)**;**mine_density := 0.4/(W×H−9) 计算值(round = 0 → 布雷数下限腿,R3 修订)**;**attempts×W×H > 16384(F2c 成本上限——B8 修订)**;F2b 违例(10×10/d=0.10/min_safe=92——边界值由 F2b 公式对构造参数求值导出);board_biome=∅},THEN 各含对应**致命**类别;WHEN 作用于合法集 {count=0∧f=0(L1 标准形);f := 8×d **计算值**恰界(防浮点字面量歧义);min_safe := W×H−round(d×(W×H−9)) **计算值**恰界;board_biome={未定稿占位词}(EC13——只校验非空、值域不校验)},THEN 零诊断。 +- **AC-3(致命·独立进程)** GIVEN 夹具根 `a1_fog_domain`/`a1_min_safe`/`a1_biome_empty`,WHEN 各以独立 headless 进程跑游戏通道 `load_tables(root, collect_only=false)`,THEN 三进程均非 0 退出且错误清单完整。**runner manifest 登记**:fog_domain→≠0 · min_safe→≠0 · biome_empty→≠0。 + +**生成确定性与回放** + +- **AC-4(生成确定性)** GIVEN 默认根 L2 行与登记种子 σ₀,WHEN `generate(2, 0, rng)` 以 `derive_at("board_gen", σ₀)` 播种之流实例执行两遍,THEN 两遍产物格状态**逐格相同** + 八项指标集全等(含 attempts_used);WHEN 以 σ₁≠σ₀ 同行生成,THEN 格状态与 σ₀ 产物不全同(种子接入退化护栏)。 +- **AC-5(运行期 replay 与 scope 边界)** GIVEN `set_master_seed(42)` 后的 RngService(全真路径),WHEN 以第 1、2 次 `open_stream("board_gen")` 实例分别驱动 generate,THEN 产物与 `derive_at("board_gen", 0/1)` 播种产物逐格相同(生成侧无流外取数);GIVEN 夹具根 `a1_high_target@2`(target=1.0 必触发重试),WHEN generate 前后读 RngService 只读计数器,THEN 差**恰 1**(attempts>1 共用一次 open_stream——attempt 边界非 scope 事件、重试不重开取号);WHEN 同帧连续两次 generate(EC14),THEN 计数器差 2、各产物与各自 σ 播种产物一致(串行生效、无锁语义)。 + +**产物合法性不变式** + +- **AC-6(产物合法性不变式套件)** GIVEN 缩样产物集(默认根 L1·D0/L2·D0 各 N=8;σᵢ = 10⁹+2²⁰·L+2¹²·D+i【F4 公式缩样版——R4 修订同步】i∈[0,8)),WHEN 对每板断言,THEN 全板全腿通过:(a) 出生 3×3 内零雷零变异;R₀ 芯(0 格 flood 闭包——**测试侧朴素 BFS 重算**,仅依赖"0 格 8 邻传播"公开定义)内零雷零变异;闭包外扩 1 格环内零变异(Rule 4/A4③——**环内可有雷**:芯内数字格的邻雷恒在环内,禁断言环内零雷〔B2 修订 2026-09-09:原"可达域内零雷零变异"为构造性不可满足超额断言——所引义务原文只禁变异不禁雷〕);(b) R₀ 芯(去外扩环)内零变异(初始展开不触雾);(c) 雾格 ⊆ cell_constraint 合法集——非雷、非 0、8 邻含雷(A4②);(d) 任意 3×3 窗口非全雾(厚度≤2 窗口谓词,A4⑫);(e) 雾格数 ≤ 8×雷数(f≤8d 产物侧);(f) |S| ≥ min_safe_count(表读值,Rule 8);(g) 非雷格数字值 ≡ 8 邻雷数(数字计数恒真,Rule 10);(h) 全板零非法产物标记。 +- **AC-7(合法性谓词·程序构造病态板)** GIVEN Board class_name 直引程序构造板(与黄金向量同批构造设施),WHEN 合法性谓词作用于:雷墙包围孤立安全格板 → clearable=false(违例类别=不可达安全格);|S|0 搜索,协议同 AC-12;搜索无解的后备语义见缺口 G4),WHEN generate,THEN 降级标记=雾布置降级、shortfall < count 表读值、产物仍过 AC-6 全腿(合法少变异/无变异图)、error 级诊断零新增(静默降级——warning/埋点级不算违静默)。 + +**指标与一致性** + +- **AC-16(指标完备与 F1/F3 一致性·A4⑤)** GIVEN 任意冒烟产物(AC-6/10/14/15 批次复用),THEN 八项指标字段完备(实际 inferability【含 metric_version=1——F1 注记⑥登记值】/inferability_core【B1 修订】/实际雾占比【= |F| ÷ (W×H)】/实际雷数与实际 |S|【B3 修订——并断言 |M| ≤ round(d×(W×H−9)) 表读值】/fog_burden_ratio/Δbudget_fog/attempts_used/降级标记【含非法产物标记+shortfall】);F1/F5 两项判据重算一致:inferability_core = |R∞∖F| ÷ max(1, |S∖F|);F3 重算一致:从产物重算 Chain₀/U → Δbudget_fog = |U|×dig_ap_cost(A3 表读值)、fog_burden_ratio 以整数对 (|U|, max(1, |S∖Chain₀|)) 比对(付费格分母口径——B1 条款 5 对账精确形);fog_region_count=0 行(L1)雾相关四值恒 0 且 core = inferability。 +- **AC-17(registry 对账·B3 AC-18 先例)** **生效前提注明:registry `board_gen_table` 条目与本 AC Phase 5b 同批回填——无条件断言,条目缺位即失败(非条件豁免)**。GIVEN ConfigDB 加载完成,WHEN 脚本解析 `design/registry/entities.yaml` 的 `board_gen_table` 条目,THEN 与 BoardGenTable 九字段**定义**(字段名/类型/域/knob 标记)逐一相等;值对账仅限非 knob 字段(solver_rules=single_subset);八 knob 字段值不对账(参考行 provisional——C3 调参豁免,同 A4 AC-8 口径)。 +- **AC-18(静态扫描·零随机/单点访问/零 UI)** GIVEN A1 文件集(生成器/度量入口/谓词/schema/冒烟脚本),WHEN 按 D2 AC-9 模式静态扫描,THEN 玩法代码文件集零 `randomize()`、零自建 RandomNumberGenerator 构造(**tests/ 路径豁免**——derive_at 取号需以种子构造流实例,D2 预览语义的测试侧落地;玩法文件集不豁免)、零 ConfigDB 外 data 表加载(schema class_name 直引豁免——A4 AC-5 先例)、零 Control/场景类 import;AC-1 headless 实跑通过合成判定(Rule 7)。 +- **AC-19(人工·字面量)** A1 文件集纳入 D2 AC-14 项目级扫描(人工三要素口径,引用不另设——A4 AC-10 同型)。 + +**覆盖矩阵(qa-lead 交付,验收评审用;2026-09-09 full 评审修正——原"无洞"自称不成立:5 处标注问题见日志;③ 经 B2 修复、⑭ 经 B6 修复、⑩ 角点经 B4 修复、④ 减项归因与 F3 注记① 经 B1 core 常数/删承诺修复,⑬ 差分夹具与 ⑪ 差分承载仍登记 R9 未核销)**:Core Rule 1→AC-4/5/6(c)/16(布雷语义 B3) · 2→AC-8/9/10(a)/16(两项判据 B1) · 3→AC-10/11/12 · 4→AC-6(a)(b)/7(芯/环口径 B2) · 5→AC-6(c)(d)(e)/13/14/15 · 6→AC-4/5/14/18 · 7→AC-16 · 8→AC-2/3/6(f) · 9→AC-1/2/3/13 · 10→AC-6(g)/17/18/19 · F1→AC-8/9/7/16 · F2/F2b/F2c→AC-2/3(角点/上限/布雷数下限/成本上限——B1/B4/B8/R3) · F3→AC-16 · F4→AC-10(全量 O2;断言 A″/E 属 O2) · F5→AC-10(e)/11/12/7 · EC1→13 · EC2→15 · EC3→7/8 · EC4→11(tie O4)· EC5→12(玩家侧语义登记 B1/D1 域)· EC6→14(合并 O6)· EC7→O4 · EC8→10(c) · EC9→2/11 · EC10→O4 · EC11(F2b)→2/3 · EC12→2/15 · EC13→2 · EC14→5 · A4①→13 · ②→6(c) · ③→6(a)(b)(B2 修复)· ④→8/16(core 常数补强)· ⑤→16 · ⑥→13 · ⑦→15 · ⑧→4/14 · ⑨→7/8 · ⑩→2/3(B4 修复)· ⑪→14(间接承载——共置+确定性,无取号耦合差分)· ⑫→6(d)(e)/2 · ⑬→15(部分覆盖:面积判据实现同样通过 a1_ec2_press——差分夹具 R9 未核销)· ⑭→10/O2(B6 修复:断言 B 收紧达成区占比)· ⑮→2/3。 + +## Open Questions + +1. **F4 全量批量工具的 owner 排期**(owner:A1 交付物 / C3 消费;deadline:C3 调参期前):调参期红线工具(断言 A/A′/B/B′ + 报告 C + 诊断 D,N=256/行)——qa-lead G7;**与 D2 OQ5 曲线导出工具同批登记排期**(排期倒挂:A1 定稿→首行数值回填→即需抽查;临时载体 = 冒烟缩样 AC-6/10,统计意义有限,如实登记)。 +2. **生成性能预算与异步化选项(B8 修订扩写 2026-09-09)**(owner:实现期 ADR / perf-profile;deadline:Technical Setup):① **预算数值定稿**——F2c 谓词形式已冻结(`max_gen_attempts × W×H ≤ C`),C = 16384 为 provisional 占位,ADR 裁定正式 C 与 headless debug 基准(如"64×64 单次求解 ≤ X ms");② **解空间圈定**——死路二:attempts 并行化(毁 board_gen 流序即毁回放)、分帧/协程化(跨帧在飞 open_stream 实例与 D2 快照切点契约冲突,需 D3 扩容修订);活路四:同步 + 加载幕(B1 换图相位)、主线程分帧 + D3 快照扩容修订、WorkerThread + D2 EC13 单线程假设接口漂移修订(join 先于任何快照切出)、B1 预测性预生成(线性图结构的死时间生成——scope 计数器推进时点须入回放语义);③ **实现姿态约束**:最小不动点可经单调 worklist 传播实现(次序无关性不强制全板重扫描——F1 注记⑤),禁全对全子集比较(ADR 验收底线);④ **F4 全量工具须支持分片续跑**("分钟级"成立域 = worklist 实现 ∧ 大面积行稀少,见 F4⑤)。 +3. **度量独立入口分解面确认**(owner:实现期 ADR;deadline:实现期):推理器/F3 度量/合法性谓词三入口暴露(AC-7/8/9/12 单元腿前提——qa-lead G1);若 ADR 裁决不暴露,四条 AC 单元腿降级重议。 +4. **biome 词表 owner 定稿**(owner:B4/C1 域 + AD 门禁;deadline:垂直切片前):board_biome 值域现只校验非空(EC13);词表定稿后补域校验 + 参考行占位值替换(接口漂移)。 +5. **黄金向量手工推导第二人复核**(owner:A1 定稿评审;deadline:A1 /design-review):五板逐格推导记录入版本库后安排复核(qa-lead G8——推导错误即假失配)。 +6. **精英/Boss 第三轴落点重审(B7 修订登记 2026-09-09)**(owner:C1/C3 GDD;deadline:C1 定稿时):节点形态定稿后强制重审——① Dmax 行约定是否覆盖全部 Boss 场景 ② 精英节点是否需第三键维 / `elite_`/`boss_` 字段激活(分支地图同深度并存精英与普通节点时);激活即走 D2 Rule 4 改键通道 + 本表 schema 修订(接口漂移迁移,退路已登记 Schema 节)。 diff --git a/design/gdd/legacy-2026-09-13/a2-minesweeper-rules.md b/design/gdd/legacy-2026-09-13/a2-minesweeper-rules.md new file mode 100644 index 00000000..ac40ce41 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/a2-minesweeper-rules.md @@ -0,0 +1,379 @@ +# A2 排雷玩法规则(Minesweeper Rules) + +> **Status**: **Approved**(2026-09-09 full /design-review:四专员〔game-designer/systems-designer/qa-lead/ux-designer〕+ CD 终审 NEEDS REVISION 六 blocking 同日修订核销,用户拍板放行【A3/A4/A1/B3 先例】;日志 `design/gdd/reviews/a2-minesweeper-rules-review-log.md`);lean 门禁:systems-designer 起草 Formulas〔F1–F4 + 反例 B 阻断缺口〕· qa-lead 起草/验证 AC〔23 条 = AC-1…22 + AC-1b + 覆盖矩阵 + 夹具坐标规格〕 +> **Author**: user + agents(systems-designer〔Formulas〕· qa-lead〔AC〕;初始展开=R₀ 恰等 / 批次语义序仲裁 / 移动瞬移语义 / install 四不变式 为本会话定稿;2026-09-09 full 评审修订:game-designer / systems-designer / qa-lead / ux-designer 四专员 + creative-director 终审) +> **Last Updated**: 2026-09-09 +> **Implements Pillar**: P1(主——"每一步都算数"的执行面:连锁放大推理兑现、数字恒真、拒绝可辨)· P4(小人边缘探索的氛围本体)· P3(旗承诺与雷险的规则执行面)· P2(三事件流 = 结算归因的数据源) +> **Layer**: 2 Core · **Milestone**: MVP · **设计顺序**: 第 6 位(上游 A1/A3/A4/D2 均 Approved) + +> **术语与约定**(2026-09-09 评审增补——按 CLAUDE.md 用语纪律豁免通道,白话首定义集中于此;已登记 registry 术语条目): +> - **双包含回验**:A1 与 A2 互检两条包含关系——**臂①** A2 实际初始展开域 ⊆ A1 生成期排除域(保证开局不触雷不触变异);**臂②** A1 求解器初始可见态 ⊆ A2 实际初始展开域(保证 A1 的度量不吃亏)。**恰等** = 两集合逐格相等(不多开不少开)。 +> - **零特判**:A2 代码不为变异写任何 if 分支——变异只是随格携带的只读标记透传,行为差异全部由生成端约束保证。 +> - **规范序**:事件发射的固定顺序(触发格 BFS、8 邻行主序)——同输入必得同顺序,禁实现自选遍历序。 +> - **语义序仲裁**:批次内不按玩家提交顺序、而按"清图挖 > 停手 > 其余"的固定类别顺序结算。 +> - **双发射源**:`round_end` 有两个合法发射者——棋盘路径(ap_exhausted/cleared)由 A2 发、停手路径(stopped)由 A3 直发,payload 同构。 +> - **三源拒绝**:拒绝原因来自三个互异来源——棋盘面(board_face)/ 资源面(resource_face,AP 不足)/ 相位(phase,非 Active)。 +> - **单意图批 / 便捷形**:`settle_batch` 只装一个意图时 ≡ 直接调 `try_dig` 等便捷 API,行为逐项相同。 +> - **作废(voided)**:轮次结束后未获执行的批次意图——零棋盘效果、零扣费、零事件,区别于非法拒绝。 + +## Overview + +A2 是扫雷层的**玩法规则引擎**:它持有 A1 产出的棋盘(格状态 + 变异标记 + 出生区),接收玩家的动作意图(挖 / 插旗 / 拔旗 / 移动 / 停手),执行全部棋盘面规则——小人控制、8 向相邻判定、连锁展开(flood fill)、数字提示、旗子棋盘状态、清图判定——并把发生的事实以**恰好三种事件**外发:`cell_revealed`(开格事实)、`flag_judged`(轮末标旗判定)、`round_end`(轮次结束·棋盘路径)。资源账本归 A3(A2 判棋盘面合法性后调用 `try_spend`,禁止自行扣减)、轮次编排归 B1、奖励内容归 B4、呈现归 D1——A2 不依赖任何下游(TD-SYSTEM-BOUNDARY 唯一回边),是全项目的**事件源头系统**(B4/D1/D5 挂其事件,瓶颈系统,规则一次定稳)。 + +玩家感知到的 A2 是"扫雷本身":点开一格、连锁撕开一片、数字逐格亮起、旗子立上可疑格——但 A2 自身不含任何数值与随机:价目读 A3、板面读 A1、变异只透传快照标记(零特判)、零 RNG 流、零 UI 依赖,`--headless --script` 可完整驱动(D2 Rule 7 工程债的规则层落点:**棋盘规则层与输入/呈现解耦**——输入是意图对象、输出是事件流,D1 与输入设备皆可替换)。没有 A2,游戏没有可玩的扫雷:A1 的板、A3 的预算、B1 的结算都失去了发生地。初始展开的定义落点(免费、计入清图进度、无奖励——A3 EC5 委派)与"标错惩罚"悬案(OQ1 登记)由本 GDD 承载。 + +## Player Fantasy + +**直接承载**(玩家每一次点击都在与之互动——Section A 定稿:直接感知)。A2 的情绪目标一句话:**"每一步都算数"的手感**——按下去的每一格都立刻、诚实地回应:数字亮起是推理原料的兑现,连锁展开是一次正确推断被免费放大成一整片区域,旗子立上是"我赌这里"的承诺落地。 + +- **P1"我算对了"的执行面**:A1 保证题出得可解,A2 保证解的过程不被规则欺骗——数字恒真透传(雾格只是不显示,数据层永不说谎)、连锁传播与生成端度量同构(F2 ≡ A1 F1 算子①)、每个拒绝都携带可辨原因(棋盘面/资源面/相位三源)。玩家的掌控感在规则层的对偶是:**系统回应的每一格都与玩家推演的一致**。 +- **P4 地牢下探的氛围本体**:小人从出生安全区向雷图边缘探索,点击已探明区瞬移穿行、在未开边界挖掘——Microsoft Minesweeper Adventure 式的"边缘探索"由 A2 的移动规则直接承载;**已引爆雷格可通行**(工程债定稿)让雷不再是探索的终点,而是走过的伤疤。 +- **P3 风险与收益同源的规则面**:挖开雷格不是死亡而是事实上报(`kind=mine`,敌方优势在轮末结算兑现);旗子插上可疑格是零信息收益的纯粹承诺(雾内旗恒不中也被诚实判定)——A2 不裁决得失,只保证事实流真实,让"赌"与"算"的结果都可见。 +- **P2 规划延迟兑现的数据源**:结算明细的每一行归因(安全格增益/标中旗削弱/雷→伏击/清图奖励)都溯源到 A2 的事件流——`reward_eligible`、`channel`、`hit` 字段是 P2"可归因"的字段落点。 + +参考感受:经典扫雷连锁展开的"哗啦一片"爽感(本作保留其免费与瞬时,剥夺其 chord 双击——每个决策都值得花一 AP,快开是对预算意义的稀释,已登记 CD 创意风险 #3 的兑付节拍补偿归 B1/B4);Into the Breach 的信息诚实(拒绝你的规则从不含糊其辞)。**反向护栏(裁剪判据)**:任何"规则层说谎"的形态——数字失真、连锁静默变异分支、拒绝原因含混——都是 P1 的死亡体验,默认砍掉;呈现层的隐藏(雾格无字)必须以数据层恒真为前提。 + +> 注:`creative-director` 未经咨询——Lean 模式(仅 D/H 两节委托专家),生产前请人工复核本节情绪定位。 + +## Detailed Design + +### Core Rules + +> 符号约定(全 GDD 通用):U = 全格集;M ⊆ U 雷集;S = U ∖ M 安全格全集;F ⊆ S 雾格集;G 旗集;O ⊆ U 已开集;B3 = 出生 3×3;num(x) ∈ {0..8} 为 x 的真实数字(8 邻雷数,A1 AC-6(g) 数字恒真);N₈(x) 为 8 邻。 + +1. **系统形态与解耦义务(承重)**:A2 是纯规则引擎——输入 = A1 棋盘产物 + 玩家意图序列,输出 = 三事件流;**零随机**(不消费任何 RNG 流:给定棋盘与意图序列,事件流逐位确定,D2 Rule 5)、**零数值字面量**(D2 Rule 1)、**零表读取**(价目经 A3 账本、板面经 A1 产物、变异经产物内快照——A2 玩法文件集禁现 `ConfigDB.*` 引用,D2 登记的"A2 无直接依赖"由此成立)、**零 UI/输入设备依赖**(D2 Rule 7:规则层与输入/呈现解耦,`--headless --script` 可完整运行;输入设备 → D1 → 意图 → A2 是唯一通路)。 +2. **三事件回边契约(发射面封闭,systems-index 回边裁决)**:A2 只外发 `cell_revealed` / `flag_judged` / `round_end` 三事件,B4/B1/D1 订阅——增删事件 = 接口漂移修订。payload 定稿: + - `cell_revealed{cell, kind ∈ {safe, mine}, digit, variant_ids, channel ∈ {initial, chain, dig}, reward_eligible}`——**digit 恒真透传**(仅 kind=safe 携带;雾格携带真实数字、呈现隐藏归 D1,判定数据源 = `variant_ids`——A2×4 之三);**reward_eligible**:channel=initial 恒 false(初始展开无奖励)、channel ∈ {chain, dig} ∧ kind=safe 恒 true(含链开雾格——A4 EC4)、kind=mine 恒 false;事件发射序 = 规范序(初始/连锁 = 触发格 BFS、8 邻行主序固定序——确定性规范)。 + - `flag_judged{cell, hit}`——轮末统一判定批,每轮末对**判定基线旗位**逐旗一条(基线 = 轮次结束事务开始时的旗位;枚举序 = 行主序扫描,确定性)。 + - `round_end{path_tag ∈ {ap_exhausted, cleared}, continuation_hint ∈ {same_board, new_board}}`——**棋盘路径由 A2 外发**(Rule 9),核心字段逐字段取自 A3 `last_round_end`(读取权为接口契约),与 A3 stopped 路径 payload **同构**;stopped 路径由 A3 直发、A2 禁双发(Rule 10)。 + - **事件序契约**:同一轮次结束事务内 `flag_judged ×N` **先于** `round_end`(全路径统一——stop 意图由 A2 在委托 A3 前先发旗判定,因停手不改旗位,基线等价);B1 结算触发 = Waiting 相位编排点,不依赖事件到达序(登记 B1 强制项)。 +3. **棋盘产物消费与 install 校验域**:`install_board(board)` 装板(受理域 = {Idle, Waiting} 编排边界相位 + **Active·仅弃图刷新路径**〔2026-09-12 D4 修订:B1 `refresh_board` 编排内、A3 扣费成功为前提;其余 Active 装板仍拒绝——防轮内换板原则对非刷新路径不变〕)。装板 = 持有产物快照(变异标记为**生成期快照**,非 VariationTable 活引用——A2×4 之四:抗表重载)→ 校验四条**结构不变式**(violation = 拒绝 + `board_invalid` + error 级诊断,fail-fast 不静默;**诊断类别封闭枚举:`{inv1_mine_in_spawn, inv2a_variant_in_spawn, inv2b_variant_in_r0, inv3_digit_invariant, inv4_r0_equals_s}`**——AC-1b/AC-2 按枚举名断言,2026-09-09 评审补;产物只能经程序构造或 A1 产生,运行期不存在该输入源):① B3 内无雷;② B3 ∪ R₀ 内无变异格(A4 Rule 7 消费侧兜底);③ 数字恒真(num(x) ≡ 8 邻雷数——A2 全部规则的地基);④ **R₀ ≠ S**(清图退化板拒绝——防"初始展开即整图"使轮次棋盘路径出口结构性不可达,A2 状态机良构性守卫;生产面由 A1 legality 闸保证,本条为纵深防御,**非 A1 clearable 闸的复验**——不可达安全格板 A2 照常运行)。校验过后执行初始展开(Rule 4)+ 小人落位 B3 中心格。 +4. **初始展开(A3 EC5 委派的定义落点,本 GDD 承载)**:`O₀ = R₀ = B3 ∪ (B3 经 0 格的 flood 闭包)`——**与 A1 F1 求解器初始态恰等**(同算子同板同值;显式**不含**"外扩 1 环"——环是 A1 排除域的生成期安全边距,多开破**恰等**(臂②)与恒不开雷定理(环内可有雷)、少开破臂②)。四属性:**免费**(零 `try_spend` 调用、账本零写入);**计入清图进度**(rem₀ = |S ∖ O₀|);**无奖励**(channel=initial、`reward_eligible=false`——game-concept 定稿,优先于 EC4 入库语义;下游 B1/B4 不得入库,登记验收);**永不触雷触雾**(R₀ ∩ M = ∅ 是 A2 侧可独立证明的定理——B3 无雷 + 闭包只经安全格;触雾由 A1 排除域保证 + 本侧不变式②兜底)。执行时点 = install 事务内一次(板级一次,跨轮不重放)。**双包含回验(A1 登记,门禁缺项即 blocking)**:臂① `R₀ ⊆ A1 排除域`(构造性成立——两个集合项均为排除域子项);臂② `A1 求解器初始态 = R₀ ⊆ O₀`(**恰等**)→ **A1 回退口径(出生 3×3 最小态)不触发**,度量保真零降级。 +5. **动作价目执行与棋盘面合法性**:A2 是挖/插/拔的调用方——先判棋盘面合法性,后调 A3 `try_spend(action_kind)`,任一环节拒绝即整体拒绝(false + 状态不变 + 诊断)。**棋盘面拒绝目录(board_face 全枚举)**:挖——目标非 8 向相邻 / 目标已开 / 目标有旗(EC11:须先拔旗);插旗——目标非相邻 / 目标已开 / 目标已有旗;拔旗——目标非相邻 / 目标无旗;移动——目标未开 / 无 BFS 路径。**资源面拒绝(resource_face)= A3 try_spend 返回 false**(AP 不足);**相位拒绝(phase)= A3 非 Active 或无板态**。三源枚举携于 `ActionOutcome{accepted, reject_reason, sub_reason}`,D1 拒绝呈现的数据源(A3 双源契约的扩容版——三源)。**board_face 拒绝携带 sub_reason 封闭枚举**(2026-09-09 评审 UX-1 定稿——"须先拔旗"与"非相邻"等玩家语义截然不同的情形必须数据可分;本枚举为 A2 自有 provisional API 面,批后修改 = 接口漂移):`sub_reason ∈ {not_adjacent, target_revealed, target_concealed, flag_present, flag_absent, no_path, out_of_catalog}`——挖:not_adjacent/target_revealed/flag_present;插旗:not_adjacent/target_revealed/flag_present(已有旗);拔旗:not_adjacent/flag_absent;移动:target_concealed/no_path;**目录外输入(越界坐标等)= out_of_catalog**(debug 级诊断,不与玩家语义混桶——EC9)。**D1 呈现义务:每次拒绝须玩家可感知呈现**(文案归 D1,映射源 = 本枚举;登记 D1×6 强制项)。**A2 不可自行扣减资源**(A3 Rule 9);`try_spend` 返回 true 后若本次事务内轮次结束,A2 读 `last_round_end` 组包外发(Rule 10)。连锁规模与清图事实由 A2 作为动作参数上报:`request_dig(chain_size, clears_map)`(F2)。 +6. **连锁展开(flood fill)**:dig 开出 num=0 的格 → 8 邻**未插旗**安全格免费连锁全开,逐格递推至无非 0 新格(F2,与 A1 F1 算子①在 G=∅ 域逐字同构——install 时旗集恒空,见 F2 注)。**连锁不越旗**:旗格不入链、不作传播源(经典扫雷 flood 惯例——系统不替玩家毁约,EC3"旗是承诺标记"的反向保证;旗由此成为玩家可用的链阻断器:花 1 AP 保护一格不被误链——2026-09-09 评审 GD-1 定稿)。连锁格:免费(仅首格耗 `dig_ap_cost`)、计入清图进度、`channel=chain`、`reward_eligible=true`、**含链开雾格**(雾格可被链开、永不传播——生成约束恒非 0,代码零变异分支,A2×4 之一)。chain_size 上报 = 附加格数(不含首格);clears_map 上报 ≡ F1 谓词本事务尾求值(单一谓词两个名字,禁第二套实现)。 +7. **小人移动**:点击已开格 → 小人**瞬移**至该格(规则层一次事务完成位置更新;逐格动画是呈现语义,归 D1)。合法条件 = 目标已开 ∧ 经已开区的 8 邻 BFS 连通("已探明区" = O 全体,**含已引爆雷格**);`compute_move_path(cell)` 返回该 BFS 路径(固定 8 邻行主序序,确定性)——**路径 = 合法性载体 + D1 移动路径预览的数据源**(表现归属归 D1 GDD,systems-index 2026-09-07 门禁登记项)。移动免费(`move_ap_cost=0` 冻结,**不经账本**——A3 AC 前言的 A2 侧承载)、零事件外发。 +8. **旗子系统(棋盘单态,A3 Rule 8 的 A2 落点)**:已插旗 = 棋盘状态归 A2——跨轮保留、随图存续,清图/刷新换图/战斗结束随之消失(retire/重装即清空);无口袋旗资源、数量无上限;**旗恒在未开格上**(插旗目标须未开 + 连锁不越旗——Core Rule 6,两路共同保证的结构不变式)。插旗 = 1 AP + 棋盘 +1 标记;拔旗 = 1 AP + 标记移除;挪旗 = 拔 + 插(2 AP,A3 EC3/EC7);挖已插旗格拒绝(EC11)。**轮末统一判定**:每轮结束事务开始时对旗位逐旗判定 `flag_judged{cell, hit = [cell ∈ M]}`——被拔的旗不判;雾内旗合法且恒 `hit=false`(判定逻辑零特判自然覆盖,A2×4 之二;标错惩罚上线后的一致性推论见 OQ1)。**诚实重复上报**:旗留雷上跨轮则每轮末重复 `hit=true`——**兑付去重归 B1/B4**(资格以雷格实例为键、每雷至多兑付一次、拔旗重插不恢复资格——A3 Rule 8/OQ5,A2 是纯事实源,不做任何去重)。旗动作(插/拔)零事件外发(非开格非判定;D1/D4 反馈经调用返回值命令式驱动——发射面封闭的已声明代价)。 +9. **轮次出口与批次仲裁(A3 EC4 deferral 的承接落点)**:玩家意图经 D1 提交至 **`settle_batch(intents)`**(A2 唯一结算入口;单意图批 = 平凡形,语义 ≡ 逐意图结算——等价性护栏;`try_dig` 等单动作 API 为便捷形)。stop 意图**经批次入口委托** `A3.request_stop()`(A3 仍为 stopped 轮 round_end 的发射者——Rule 9 双发射源裁决不变;D1 不直调 A3,登记 D1 接线强制项)。批次 = 单一结算事务,语义序仲裁(F4):**cleared 类 dig 先结算 → stop 次之 → 其余意图按到达序**;轮次结束首次为真处确定胜者,其后意图全部**作废**(false + voided 标记:零棋盘效果、零 AP 扣减、零事件——区别于非法拒绝)。四组合 {cleared×stopped、stopped×ap_exhausted} × 两到达序与 A3 Rule 3 优先级逐项一致、RAP 记账随胜者(批次 cleared 胜出 → 剩余 AP 作废不入 RAP)。 +10. **round_end 棋盘路径外发**:触发 = dig 结算事务内 A3 仲裁轮次结束(ap_exhausted 或 cleared 胜出);A2 读 `last_round_end` 恰一次组包外发(Rule 2 payload);外发前先发本轮 `flag_judged ×N`(事件序契约)。**零窗口**:AP 归 0 瞬间轮次即结束(A3 Rule 3),A2 无 0-AP 操作窗口可构造。 +11. **已引爆雷格(工程债定稿,systems-index 门禁修正记录)**:挖开雷格 → `cell_revealed{kind=mine, channel=dig, reward_eligible=false}`(无 digit 字段)、**轮次不结束**、格子入 O(**可通行**——此后成为移动 BFS 合法通道,小人可站立、可经其续挖);不计清图进度(恒 ∉ S)、不可再挖、不可插旗(已开格);同轮可连续挖开多雷(每雷独立事实)。**雷格通行 = 有价捷径**(2026-09-09 评审 GD-2 注记):挖已知雷 = 1 AP + 雷→敌方优势,经 P3 风险管线计价、非免费通道;A1 clearable 闸按"安全格 8 邻接图"连通计(挖雷通路不计入),合法板上"被迫挖雷过路"的构图被生成期拦截——本条服务的是玩家**主动**抄近路的权衡与降级板的兜底通路。 +12. **无 chord(有意排除,game-concept 定稿的 A2 承载)**:不存在双击快开/数字和弦——点击已开数字格 = 移动。验收形态 = API 面封闭枚举"无对应入口"(AC 前言),新增快开入口 = 接口漂移修订。 + +### States and Transitions + +A2 的板级生命周期(轮次节拍权威在 A3/B1——A2 以 A3 相位观测量为闸,不自持轮次状态): + +| 板态 | 进入条件 | 行为/出口 | +|---|---|---| +| NoBoard | 冷启动 / `retire_board()` 后 | 全部动作 API 拒绝(phase 源);`install_board` 受理(Idle/Waiting) | +| Installed·Active | 装板完成 ∧ A3 phase=Active | 接受全部动作意图;dig/旗经 `try_spend`,移动免费直达;**弃图刷新 = retire + install(D4 2026-09-12——B1 `refresh_board` 编排,装板后回本板态)**;轮次结束事务(Rule 10)→ Installed·Frozen | +| Installed·Frozen | A3 phase=Waiting(三路径任一收轮) | 全部动作意图拒绝(phase 源);棋盘/旗/小人**全态冻结**;`install_board`(换新图)/`retire_board` 受理(编排边界) | +| Retired | `retire_board()` 或战斗终结后编排调用 | 旗/小人/事件分段终结(事件捕获以 install 为段界)——板实例与变异快照一同消亡 | + +**输入 × 相位 × 板态矩阵**(动作意图 ∈ {dig, flag_place, flag_pull, move, stop};编排调用 ∈ {install_board, retire_board}): + +| 输入 | NoBoard | Installed·Active | Installed·Frozen | +|---|---|---|---| +| 动作意图(含 stop) | 拒绝(phase) | 受理(经批次结算器) | 拒绝(phase;A3 侧按同一致相位矩阵同判——两侧判定一致) | +| install_board | 受理(Idle/Waiting) | **受理(仅弃图刷新路径——D4 2026-09-12,B1 `refresh_board` 编排;非刷新路径仍拒绝——防轮内换板)** | 受理(换新图 = 全态重置 + 初始展开重放) | +| retire_board | 幂等受理 | 拒绝(板态) | 受理 | + +无独立运行期状态机分支于变异/雾(零特判)——板态只有上述四态,变体属性是格级只读快照。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| A1 | 上游 hard(产物消费) | 棋盘产物 = 格状态 + 变异标记 + 出生区(Board class_name 数据契约);**双包含回验两臂落点在本 GDD**(Rule 4:臂① 展开域 ⊆ 排除域、臂② 求解器初始态 ⊆ 展开域【恰等,A1 回退口径不触发】——门禁缺项即 blocking);install 结构不变式①②③④为消费侧信任边界(fail-fast);**A1 修订义务登记**:F5 legality 增 `R₀ ≠ S` + F4 批量断言严格零("初始展开即 cleared"反例 B 与 N=1 行整行退化——本 GDD EC1,systems-index 门禁块登记) | +| A3 | 上游 hard(账本) | `try_spend(action_kind)` 唯一扣减入口(A2 禁自行扣减);`request_dig(chain_size, clears_map)` 参数上报(A2 计算);stop 意图委托 `request_stop()`(A3 直发 stopped round_end);轮末读 `last_round_end` 组包棋盘路径 round_end(读取权 = 接口契约);相位观测量(Idle/Active/Waiting)= A2 动作闸的对侧来源;**A3 API 面零改动**(settle_batch 是 A2 侧入口,P4 注记) | +| A4 | 上游 soft(快照透传) | 变异属性经 A1 产物快照只读携带、随 `cell_revealed.variant_ids` 透传——A2 执行逻辑零变异分支(零特判原则,义务是"不加分支");**A2×4 清单落位(缺项即 blocking)**:EC4 连锁开雾格(Core Rule 6)· EC5 雾内旗恒不中(Core Rule 8)· 开格上报携带变体标记(Core Rule 2)· EC8 生成期快照抗表重载(Core Rule 3) | +| D2 | **无直接依赖(登记事实)** | A2 零表读取、零 ConfigDB 引用(Core Rule 1)——参数经 A3 账本、板面经 A1 产物;D2 Interactions 表"A2 无直接依赖"行为本 GDD 证实 | +| B1 | 下游 hard(编排,未设计——provisional 登记) | 消费 `round_end`(棋盘路径)与全事件流结算;**B1 强制项三条(systems-index 门禁块登记)**:① 结算触发 = Waiting 相位编排点、不依赖事件到达序(stopped 轮 A3 直发 round_end 与 A2 旗判定批的到达序不承载语义)② `reward_eligible=false`(initial 通道)的格不得进入奖励入库(A3 EC5 委派闭环)③ 诚实重复上报的去重义务(资格以雷格实例为键);换图编排 = `install_board`/`retire_board` 的调用方(接线归 B1 GDD) | +| B4 | 下游 soft(订阅,未设计) | 清图奖励触发(cleared 路径)与安全格奖励内容归 B4——A2 只上报棋盘面资格事实(`reward_eligible`/`channel`),内容与折算不经 A2;**连锁批量授予的溢出吸收设计**(2026-09-09 评审登记:13 格链 × B3 max_stack=2 ⇒ 大量"溢出未生效"行进结算明细,P2 画面反向伤害连锁幻想——溢出转一次性资源/治疗或显式接受,裁决归 B1/B4,义务条目化进 systems-index) | +| D1 | 下游 hard(呈现,未设计) | 棋盘/小人/旗/数字渲染;数字显示判定消费 `variant_ids`(雾格无字——digit 恒真透传是前提);**D1×6 强制项(缺项即 blocking,systems-index 门禁块登记;2026-09-09 评审由 D1×1 扩容)**:① stop 意图经 `settle_batch` 提交、不直调 A3(单一结算序保证)② 四种格态一眼可辨(已开 0 格地板 / 已开雾格无字 / 未开格 / 未开雾格——左键"已开=移动/未开=挖"消歧的感知前提;后果不对称:误挖 = 1 AP + 可能触雷且不可逆)③ 悬停预承诺(光标区分移动/挖/旗目标语义)+ 可挖域(8 向相邻)常驻指示 + 怀旧扫雷玩家 chord 肌肉记忆首遇提示("点击数字格 = 移动小人"——非模态、每 run 一次,A4 先例)④ 已引爆雷格"可通行可读"呈现(Visual/Audio §3 升入本台账)⑤ `compute_move_path` 呈现语义定稿(合法性指示 vs 穿行动画轨道——P4 氛围载体;BFS 行主序路径可能视觉绕路,形态归 D1 裁决)⑥ **单意图帧输入收集**(stop 点击终结当帧收集;F4 语义序退为防御性兜底)+ 批次 voided 呈现义务(至少 stop 被作废须可感知——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP)+ **每次拒绝玩家可感知**(文案归 D1,映射源 = reject_reason + sub_reason) | +| D5 | 下游 soft(Alpha) | 小人三动作 + 连锁/旗/收轮的教学口径以本 GDD 规则为准;教学条目:雾内旗纯损、雷格可通行、**旗挡链(连锁止于旗属正常——GD-1 配套)**、**挖到雷 = 敌方优势而非死亡**(经典扫雷玩家最大预期违背)、**已开雾格无字属正常**(A4 D1 条目的 D5 侧交叉引用)——挂 OQ1/EC5/EC17 | +| D4 | 下游 soft(Alpha) | 挖掘/连锁/雷格/收轮音效挂三事件流;**旗/移动反馈经调用返回值命令式驱动**(两动作零事件外发——发射面封闭的已声明代价,Core Rule 7/8)(systems-index 依赖分层 D4 ← A2,B1) | +| A5 | 预留(垂直切片) | 探测类道具的显影面可能挂 A2 查询接口(未开格数字显影)——接口归 A5 GDD 定稿,本 GDD 不预写(OQ4) | + +## Formulas + +> systems-designer 代理起草(lean 门禁,2026-09-09)。**口径总纲**:F1 定**终局判定谓词**(状态轴)、F2 定**连锁展开算子**(执行轴)、F3 定**初始展开域**(对偶轴——A1 度量与 A2 行为的接缝)、F4 定**批次仲裁**(事务轴)——四条均为行为契约,禁互相折算。本节零平衡公式(见排除声明)、零随机、零字面量、零 UI 依赖。 + +### F1 清图判定谓词(clear_predicate) + +``` +cleared(σ) :⇔ O(σ) ∩ S(B) = S(B) 等价形:|S(B) ∖ O(σ)| = 0 +rem(σ) = |S(B) ∖ O(σ)| ——剩余安全格计数;rem = 0 ⟺ cleared +``` + +**求值时点(穷举,此外零求值点)**:① install 初始展开事务尾(不变式④保证此处恒 false);② **每次 dig 执行完成点**(连锁完成后;批次内即逐 dig 求值——F4"批次 = 单一结算事务"说的是批次作为原子性单位,不改本条逐 dig 粒度,与 A3 `request_dig` 按次调用上报的契约一致)。插旗/拔旗/移动不改 O → 结构上不可能触发 cleared。**不计项(零特判推论)**:雷格恒 ∉ S(已引爆雷格入 O 作 BFS 通道但不入判定);旗不入 O 不入 S(带旗未开格照常计入 rem——"全图标旗不挖"永不 cleared);雾格照常计入(A4 Rule 4)。**A3 对偶**:`clears_map` 参数 ≡ 本谓词在该动作事务尾的求值——单一谓词两个名字,禁第二套实现。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| σ | 棋盘状态 | —— | (B, O, G) 三元组;B 由 A1 产物给定 | +| O(σ) | 格集 | R₀ ⊆ O ⊆ U | 已开集(含初始格、连锁格、已引爆雷格) | +| S(B) | 格集 | U ∖ M | 安全格全集(含雾格、含 R₀ 格、含不可达格) | +| rem(σ) | int | [0, \|S\|] | rem₀ = \|S ∖ R₀\|;rem=0 即 cleared 路径 | + +**Output Range:** cleared ∈ bool。**Example:** 10×10 板、|S|=88、R₀=20 → rem₀=68;挖开末 1 安全格(chain_size=3 连锁)→ O ∩ S = S → cleared,A2 经 `last_round_end` 外发 `(cleared, new_board)`。 + +### F2 连锁展开闭包(chain_closure)+ chain_size / clears_map 上报参数 + +``` +Chain(O, c) = μX.[ O ∪ {c} ⊆ X ∧ ∀x ∈ X:( x ∈ S ∧ num(x) = 0 ⇒ N₈(x) ∩ S ∖ G ⊆ X ) ] +dig(c) 的棋盘效果:O′ = Chain(O, c);事件:cell_revealed × |O′ ∖ O|(BFS 规范序) +chain_size(dig(c)) = |O′ ∖ (O ∪ {c})| ——附加格数,不含首格(A3 AC-2/AC-6b 期望依赖) +clears_map(dig(c)) = [ S ⊆ O′ ] ——≡ F1 本事务尾求值 +``` + +**与 A1 F1 算子①逐字同构(G = ∅ 域)**:A1 生成期与 A2 install/初始展开时旗集恒空,`S ∖ G = S`,两算子逐项相等(R↔X 换名后:触发谓词同〔成员资格 ∧ **真实数字**=0——禁读显示值,雾格显示空 ≠ 0 格〕;传播目标同〔8 邻**安全格**——雷永不入链〕;封闭域同〔S〕;单调最小不动点语义同〔次序无关性两侧成立——worklist 任意定序、结果唯一〕)。**A2 运行期旗非空时传播集排除旗格**(连锁不越旗,Core Rule 6——经典 flood 惯例;双包含回验不受影响:回验只在 install 时点发生,彼时 G = ∅)。**增量合法性引理**:Cl 单调幂等 ⇒ 逐动作增量求值 ≡ 一次全量闭包(与 A1 生成期同值)。同构主张范围 = 算子①本身(A2 无算子②推导项,不主张 O = R∞)。**零特判**:雾格恒非 0(VariationTable 跨字段校验承重)⇒ 可被链开、永不传播——两个方向都是定理不是分支;旗格不入链是集合减法(`∖ G`)不是分支,GD-1 评审确认不违零特判纪律(旗是 A2 一等规则对象,非变异属性)。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| c | 格 | U ∖ O | 挖掘目标(棋盘面合法性先行,Core Rule 5) | +| O / O′ | 格集 | 见 F1 | 挖前/挖后已开集 | +| num(x) | int {0..8} | x ∈ S | 真实数字(非显示值——漂移风险 D1,见注) | +| chain_size | int | [0, \|S\|−1] | A3 request_dig 第一参数 | +| clears_map | bool | —— | 第二参数;≡ F1 cleared | + +**Output Range:** chain_size=0 ⟺ c 非 0 格或其安全邻已全开。**Example:** b_chain 板挖 (3,6)(0 格)→ 级联 13 格一次开尽墙下封闭区(雷墙与板底之间的口袋形安全区,下称"封闭区"),chain_size=13、clears_map 视余量而定;全程 ap 只扣首格 1。 + +### F3 初始展开域(initial_expansion_domain)+双包含回验 + +``` +O₀ = R₀(B) = μX.[ B3 ⊆ X ∧ ∀x ∈ X:( x ∈ S ∧ num(x) = 0 ⇒ N₈(x) ∩ S ⊆ X ) ] + ≡ A1 F1 "出生 3×3 ∪ flood 闭包" 逐字(同算子同板 ⇒ 同值);显式不含外扩 1 环。 + +性质 1(恒不开雷,定理):R₀ ∩ M = ∅(B3 无雷 + 闭包只收安全格)——初始展开零雷险由 A2 独立可证。 +双包含回验(registry board_gen_table.a2_dual_containment 的 A2 落点): + 臂① R₀ ⊆ 排除域(以最终产物板计:出生 3×3 ∪ 闭包 ∪ 外扩 1 环)——构造性成立; + 臂② A1 求解器初始态 = R₀ ⊆ O₀ = R₀ ——恰等; + ⇒ A1 回退口径(出生 3×3 最小态)不触发,度量保真零降级。 + 注:臂①给"不触变异"保证(经排除域);"不触雷"由性质 1 独立给出(环内可有雷,两义务来源分开陈述)。 +退化守卫(不变式④):R₀ ≠ S——install 拒绝清图退化板(EC1;非 A1 clearable 闸复验)。 +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| B3 | 格集 \|B3\|=9 | ⊆ U | 出生 3×3(产物携带);小人出生 = B3 中心格 | +| R₀ = O₀ | 格集 | B3 ⊆ R₀ ⊆ S | 初始展开域;下界 9(无 0 格传播时) | + +**Output Range:** |R₀| ∈ [9, |S|−1](上端点被不变式④排除)。**域非空前提**:非空 ⟺ |S| ≥ 10——|S| = 9 的板必有 S = B3 ⇒ R₀ = S,由不变式④在 install 拒绝(EC1 同族),不产生运行态 R₀;现行 A1 参数域(W,H ≥ 8)下 |S| ≤ 9 仅程序构造板可达。**Example:** b_spawn 板(7×7、竖墙雷 6 枚):R₀ = x≥2 全安全格 35 格,臂① R₀ ⊆ 排除域(35 + 域外安全 8 + 环内雷)✓、臂② 测试侧朴素 BFS 重算逐格恰等 ✓。 + +### F4 批次仲裁(settle_batch) + +``` +类别函数(排序用,批次初始态求值): + class(I) = C_dig 若 I = dig(c) 且执行后 clears_map = true(真模拟 F2——禁计数器捷径: + "rem=1 则任意 dig 必清"为假,反例 = 挖雷不入 S) + = C_stop 若 I = stop + = C_other 其余(非清 dig / flag ×2 / move) +语义序 ≺:C_dig ≺ C_stop ≺ C_other;同类内按到达序(稳定序——多 cleared-dig 时最先者执行胜出, + 其余作废;未钉死类内序会使事件流与奖励格归属漂移——规范性义务)。 +结算循环:按 ≺ 逐意图执行;轮次结束首次为真处定胜者 ω,其后意图全部【作废】 + (false + voided:零棋盘效果、零 AP 扣减、零事件——区别于非法拒绝)。 +胜者 → 出口映射(RAP 记账随胜者;键 = 胜者意图类型 × **结算时点** clears_map 真值): + dig 胜 ∧ 结算时点 clears_map=true → (cleared, new_board);RAP += 0(剩余作废,A3 Rule 5) + ——C_dig 胜者恒满足;非清 C_other dig 可因前序 C_other dig 执行变清图,按结算时点真值入此行 + C_stop 胜 → 先发 flag_judged ×N(基线 = 事务开始旗位,停手不改旗)→ 委托 + A3.request_stop() → (stopped, same_board);RAP += remaining_ap(1:1) + 耗尽 AP 的 C_other → (ap_exhausted, same_board);RAP += 0(不变式 remaining=0) + 不结束轮次 → 无 round_end,继续下一意图 +棋盘路径 round_end 由 A2 恰一次外发;stopped 由 A3 直发(双发射源纪律,A2 禁双发)。 +``` + +**四组合真值表(≡ A3 Rule 3 优先级 cleared > stopped > ap_exhausted)**: + +| # | 批次(到达序) | 结果 | RAP | +|---|---|---|---| +| 1 | [dig清, stop] | (cleared, new_board),stop 作废 | +0(作废) | +| 2 | [stop, dig清] | 语义序重排 → 同 #1 | +0 | +| 3 | [stop, dig耗] | (stopped, same_board),dig 作废 | += remaining | +| 4 | [dig耗, stop] | 语义序重排 → 同 #3 | += remaining | + +**到达序无关性(精确口径,2026-09-09 评审收窄)**:**(path_tag, continuation_hint, AP/RAP 终值) 对任意到达序不变(全批域)**。执行/作废划分的到达序无关性**仅对异类竞争成立**({C_dig, C_stop} 与耗尽的组合——四组合真值表);**同类多成员批(C_dig 或 C_other)内,执行成员、棋盘终态(旗位/小人位)与划分由类内稳定序(到达序)决定——不同到达序 = 不同输入**,均为合法确定行为非缺陷(例:ap=1 下 [move, dig耗] 两意图均执行、[dig耗, move] 的 move 作废且小人位不同;[flag_place, dig耗] 旗入板、[dig耗, flag_place] 旗作废)。cell_revealed/旗事件流同理以到达序为规范序。**反例检验(语义序的存在性证明)**:朴素到达序下 #4 产出 (ap_exhausted, RAP+0)——违反 stopped > ap_exhausted;#2 吞掉清图高光——违反 cleared > stopped(EC10 同族缺陷)。**分类时点**:排序用批次初态、胜者路径用结算时点真值。C_dig 成员最先执行 ⇒ 其二态恒重合;C_other dig 的二态可因**前序 C_other dig** 执行而分歧(其批内前缀不可能含 C_dig/stop——二者任一执行即收轮、C_other 全体作废),分歧时按结算时点真值定胜者;该前缀无 stop ⇒ 与 A3 Rule 3(cleared > stopped)不可能冲突。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| ⟨I₁,…,Iₙ⟩ | 意图序列 | n ≥ 1 | 到达序;∈ {dig, flag_place, flag_pull, move, stop} | +| class(I) | enum 三类 | —— | 排序类别(批次初态求值) | +| ω | 意图 ∪ {∅} | —— | 胜者;∅ = 轮次继续 | + +**Output Range:** (path_tag ∪ {无}, continuation_hint ∪ {无}, RAP 增量, 每意图 ∈ {执行, 作废, 拒绝})。**Example:** ap=3、rem=1、批次 [stop, dig(末格)]:C_dig 先结算 → try_spend true、ap 3→2、事务内 cleared 胜出 → 外发 (cleared, new_board)、RAP+0;stop 作废。到达序翻转结果逐项相同(#1/#2)。 + +### 显式排除声明(R4 式) + +本 GDD **无平衡公式**。全部数值归位:**A3**(价目、ap_per_round、RAP 规则——F4 只消费 try_spend/request_stop/last_round_end 行为契约,零数值所有权);**A1**(雷布/尺寸/密度/可推理率/R₀ 与排除域定义版权——F3 是其运行期镜像,A1 侧定义修订 = 接口漂移 + metric_version 联动,A2 禁独立改动);**A4**(变异约束——F2 只消费"雾格恒非 0"生成保证);**B 系**(奖励档位/标中旗判定/结算编排——A2 只输出事件流);**C3**(曲线)。A2 拥有的全部 = 四条行为契约。零概率/期望公式(零随机——确定性由 AC-19 行为断言承载:同板同意图序列 ⇒ 事件流逐位相同)。**与 A1 度量的漂移风险清单(实现期红线)**:① 0 格判定必须读真实数字 num(x) 禁读显示值;② 初始展开禁加外扩环(破臂①)、禁只开 3×3(破臂②);③ clears_map 与 cleared 同一实现;④ chain_size 不含首格、链不开雷、含链开雾格;⑤ A1 R₀/算子修订 = metric_version 跳变 + 本 GDD 同步修订;⑥ 双包含臂①排除域以**最终产物板**计(先布后删无迭代,placed 基准可小于最终 R₀——A1 Rule 4 措辞修订义务,EC1)。 + +## Edge Cases + +- **EC1 If 初始展开即整图(R₀ = S,清图退化板)**:install 拒绝——不变式④ fail-fast(`board_invalid` + error 诊断),非合同内行为。**理由**:此类板使轮次棋盘路径出口结构性不可达,破坏 A2 状态机良构性;生产面由 A1 legality 闸拦截。**跨 GDD 阻断缺口(systems-designer 反例 B,主会话独立复算证实,登记 A1 修订义务)**:A1 现行"布雷数下限 N ≥ 1"必要不充分——8×8 板 8 雷 placed、先布后删幸存 1 颗距离-2 雷,最终板全 0 格连通 → R₀ = S,且 F4 断言 A(inferability=1.0)零拦截、N=1 参数行 100% 退化;**义务 = A1 F5 legality 增 `R₀ ≠ S`(违例 = 尝试作废重试,clearable 同款)+ F4 批量断言严格零 + Rule 4 排除域基准措辞(最终板计)**;A2 侧不变式④为纵深防御,双包含两臂不受影响。 +- **EC2 If install 产物违例**(B3 内雷 / B3∪R₀ 内变异 / 数字不恒真 / R₀=S):拒绝 + 状态不变 + error 恰一类别(不变式①–④)——信任边界 fail-fast;违例板只能由程序构造错误产生(A1 产物经生成期合法性闸),运行期不存在该输入源。 +- **EC3 If 挖已插旗格**:拒绝(board_face)——须先拔旗(1 AP)再挖;旗是"别挖这里"的承诺标记(A3 EC11 的棋盘面判定落点)。 +- **EC4 If 连锁传播开到雾格**(A2×4①):雾格被**免费**开出(channel=chain)、digit 恒真透传 + variant_ids 非空(呈现隐藏归 D1)、级联自然停止(雾格非 0 不传播)、计入清图、`reward_eligible=true` 奖励照常入库——零特判的自然结果,代码零变异分支。 +- **EC5 If 玩家在雾格插旗**(A2×4②):合法、耗 1 AP、旗立在未开雾格上;轮末判定恒 `hit=false`(雾区无雷——判定逻辑零特判自然覆盖)。**标错惩罚上线后的一致性推论(OQ1 前瞻登记)**:雾内旗按"非雷格上的旗"恒受罚——无 A4/A2 特判;D5 教学须警示"雾内插旗纯损"。 +- **EC6 If 同批次多结束路径竞争**:F4 语义序仲裁,四组合与 A3 Rule 3 逐项一致(真值表)、RAP 记账随胜者、作废意图零副作用(零棋盘写/零扣减/零事件——被作废的旗不进轮末判定,无幽灵旗);胜出后余意图作废,棋盘只读查询逐格不变。**联合清图让位条款(2026-09-09 评审登记)**:批内个体非清的 dig 联合可清图时,stop 仍先执行并胜出(C_stop ≺ C_other)——联合清图高光被停手让位,为语义序的显式设计后果(单意图帧提交下生产面不可构造——D1×6⑥ 单意图帧约束;F4 语义保留为防御性兜底,AC-10 义务不豁免)。 +- **EC7 If 最后 1 AP 挖开最后安全格**:cleared 与 ap_exhausted 同时成立 → A3 事务内 cleared 胜出(EC10)→ A2 外发 `(cleared, new_board)` 恰一条、剩余 AP 作废不入 RAP——防"耗尽优先"实现把清图高光结算为最差结局。 +- **EC8 If 战斗中热重载修改 VariationTable**(A2×4④):板持**生成期变异快照**(非活引用)——行为零变化;A2 零表读取使抗重载为构造性,行为级代理 = 换根重放事件流逐位相同(AC-18)。 +- **EC9 If 拒绝目录外溢**:board_face 全枚举见 Core Rule 5——目录外输入(越界坐标等)按 board_face 拒绝 + `sub_reason=out_of_catalog`(debug 级诊断,不与玩家语义混桶)+ debug 诊断禁静默吞;三源(board_face/resource_face/phase)互异可判定(D1 呈现数据源;玩家面文案归 D1,映射源 = sub_reason 枚举)。 +- **EC10 If 挖开雷格**:`kind=mine` 事件(无 digit)、轮次不结束、reward_eligible=false、格子可通行(工程债)可经其 BFS 续挖;同轮连续挖多雷 = 每雷独立事实事件;rem 不变(雷 ∉ S)。 +- **EC11 If 换图/刷新/战斗结束**:`install_board`(新板)= 旗/小人/已开集全量重置 + 初始展开重放;`retire_board` = 板实例与变异快照消亡——旗标记随图存续语义(A3 Rule 8)的 A2 侧终点。 +- **EC12 If 非 Active 相位动作请求**:拒绝(phase 源)、零事件、全态不变——A2 与 A3 两侧按同一相位矩阵各自判定、结论一致;不存在 0-AP 窗口(A3 Rule 3 的对侧消费)。 +- **EC13 If 批次内旗动作与终局意图竞争**:旗动作 ∈ C_other → 被 cleared-dig/stop 压新作废;判定基线 = **轮次结束事务开始时旗位**(作废旗动作不留痕)——语义序使基线与到达序无关。 +- **EC14 If 移动边界**:目标未开 / 无 BFS 路径 / 目标即当前格 → board_face 拒绝或幂等受理(目标格 = 当前格定义为零移动,合法受理);`compute_move_path` 对不可达格返回空——预览与执行同源。 +- **EC15 If 同批次多个 cleared-dig**:类内稳定序——到达序最先者执行并胜出,其余作废;(path_tag, RAP) 天然唯一,稳定序规范的是事件流与奖励格归属(F4 注 T1,AC-10 用例 e)。 +- **EC16 If install 与 A3 相位组合非法**(如非刷新路径的 Active 装板〔刷新路径已受理——D4 2026-09-12〕/ NoBoard 动作):编排面拒绝(板态/相位源)——B1 编排错误 fail-fast 暴露,禁静默(A3"拒绝 + debug 诊断"口径同族,进程致命保留给数据通道)。 +- **EC17 If 连锁前沿遇旗(GD-1 定稿,连锁不越旗)**:旗格不入链、不作传播源——被旗挡的格保持未开 + 带旗,仅经该格可达的后续格本轮不被链开(经其他路径可达者照常开出);chain_size 只计实开格;旗格轮末判定照常(`hit` 按其是否为雷——误标安全格留待玩家自拔,自己的承诺自己付 1 AP 撤回,与 OQ1 惩罚逻辑自洽)。零特判合规:传播集 `∖ G` 是集合减法非分支(F2 注)。 + +## Dependencies + +**Upstream**:A1(hard——棋盘产物与双包含回验出据方)· A3(hard——账本与轮次出口仲裁)· A4(soft——变异快照经 A1 产物透传,零运行时分支)。D2 为**登记的无直接依赖**(Core Rule 1 零表读取——D2 Interactions 表事实的 A2 侧证实)。无其他上游。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | hard(未设计,provisional) | 消费 round_end(棋盘路径)+ 三事件流;换图编排调 install_board/retire_board;**B1×3 强制项(缺项即 blocking,systems-index 门禁块登记)**:① 结算触发 = Waiting 相位编排点、不依赖事件到达序 ② reward_eligible=false(initial)格不入奖励库 ③ 标中旗兑付去重(资格以雷格实例为键——A2 诚实重复上报的对侧) | +| B4 | soft(未设计) | 清图奖励/安全格奖励内容(消费 reward_eligible/channel 事实,内容不经 A2);标中旗 debuff 兑付(经 B1) | +| D1 | hard(未设计) | 渲染 + 数字显示判定消费 variant_ids + 路径预览(compute_move_path)+ 三源拒绝呈现(含 sub_reason 玩家语义映射)+ 格态可辨/悬停预承诺/voided 呈现/单意图帧收集(详见 Interactions 表);**D1×6 强制项(缺项即 blocking,systems-index 门禁块登记)**——① stop 意图经 settle_batch 提交、不直调 A3(原 D1×1)② 格态一眼可辨 ③ 悬停预承诺+可挖域指示+chord 首遇提示 ④ 雷格可通行可读 ⑤ 路径呈现语义定稿 ⑥ 单意图帧收集 + voided 呈现 + 拒绝可感知 | +| D5 | soft(Alpha) | 教学口径来源(三动作/连锁免费/雾内旗纯损/雷格可通行) | +| D4 | soft(Alpha) | 音效挂三事件流 | +| A5 | soft(垂直切片) | 探测显影查询接口预留(OQ4,本 GDD 不预写) | + +**门禁义务落位表(本 GDD 门禁评审逐项核对,缺项即 blocking)**: + +| 义务 | 来源 | 承载位置 | +|---|---|---| +| A2×4 ① EC4 连锁开雾格(无数字+级联停止+计入清图+奖励入库) | A4 | Core Rule 6 + EC4 → AC-6 | +| A2×4 ② EC5 雾内旗恒不中(含标错惩罚一致性推论) | A4 | Core Rule 8 + EC5 → AC-12 | +| A2×4 ③ 开格上报携带变体标记 | A4 | Core Rule 2 + F2 注 → AC-6/18/20 | +| A2×4 ④ EC8 生成期变异快照抗表重载 | A4 | Core Rule 3 + EC8 → AC-18 | +| 双包含臂① A2 展开域 ⊆ A1 排除域 | A1 | Core Rule 4 + F3 → AC-3 | +| 双包含臂② A1 求解器初始态 ⊆ A2 展开域(恰等,回退口径不触发) | A1 | Core Rule 4 + F3 → AC-3 | +| 初始展开定义落点(免费/计入清图/无奖励) | A3 EC5 委派 | Core Rule 4 → AC-1 | +| round_end 棋盘路径外发 + payload 同构 | A3 Rule 9 | Core Rule 2/10 → AC-7/8/9 | +| EC4 批次四组合仲裁 + RAP 随胜者 | A3 EC4 deferral | Core Rule 9 + F4 + EC6 → AC-10 | +| 已引爆雷格可通行(工程债) | systems-index 门禁修正记录 | Core Rule 11 + EC10 → AC-15 | +| 标错惩罚以 OQ 登记(实测义务保留,不阻塞设计) | systems-index 2026-09-09 偏离登记 | OQ1(/design-review 核对在位) | + +**跨 GDD 新登记义务汇总**(本 GDD 出具,随 systems-index 门禁块登记):A1 修订义务 ×2(F5 legality 增 R₀≠S + F4 断言严格零;Rule 4 排除域基准措辞——EC1)· B1×3 · D1×6(2026-09-09 评审由 D1×1 扩容,见上表)· **B1/B4 增补两条(同批登记)**:高光兑现节点条目化(创意风险 #2/#3 合并——含 chord 缺席的标中旗即时兑现补偿候选,清单外散文登记会丢失——B3 先例)· 连锁批量授予溢出吸收设计(见 B4 行)。 + +**双向一致性核对**:A2 ← A1, A3, A4 ✅(索引依赖分层)· B4 ← A2,B3,D2 ✅ · D1 ← A2,A3,B1 ✅ · D4 ← A2,B1 ✅ · D5 ← A2,A3,B1 ✅ · A5 ← A2,A4,B4 ✅ · D2 表"A2 无直接依赖" ✅——无单向遗漏。 + +## Tuning Knobs + +**A2 零专属 knob(结构冻结系统,显式排除声明)**。A2 的全部手感参数都在邻接系统:节奏与价目归 A3(`ap_per_round`/`dig_ap_cost`/`flag_place_ap_cost`/`flag_pull_ap_cost`/`rap_refresh_cost`)、板面与推理保障归 A1(BoardGenTable 九字段)、变异资格归 A4(unlock_level)——**禁止在 A2 侧复制任何参数**(D2 Rule 1 单一事实源)。A2 拥有的是**规则结构**,修订走本 GDD 修订流程非调参:三事件面封闭、初始展开=R₀ 恰等、连锁免费、移动/初始展开不经账本、无 chord、批次语义序、四条 install 不变式、瞬移移动语义。 + +- **跨 GDD knob 联动观测**(引用不拥有):`flag_pull_ap_cost`(A3)= 挪旗成本与 OQ1 标错惩罚的双重定价位(A3 OQ2 复核落点——若实测推翻拔旗 1 AP,A2 行为零改动、仅价目表值变化);`ap_per_round` 决定每轮可兑现的确定动作供给(A3 OQ3 供给指标)。 +- **A2 侧可观测指标(非 knob,遥测域)**:每消耗 1 AP 点击数(≤ 2 护栏,P4 点击摩擦——D1 观测)、连锁规模分布、批次数/轮。 + +## Visual/Audio Requirements + +A2 无自有渲染面——视觉义务是**数据供给规格 + D1 呈现义务登记**: + +1. **数字可读性的数据前提**:digit 恒真透传(含雾格)是"数字是主角"(视觉锚 1)的逻辑地基——呈现层任何隐藏/遮罩都不改数据层真值;已开雾格 = 地板无字(负空间,防误读为 bug)。 +2. **绘制优先级固定**(视觉锚 3):数字 > 小人 > 旗 > 变异纹理 > 底色——小人所在格用边框高亮而非覆盖文本;A2 的事件流是 D1 分层绘制的输入。 +3. **三 channel 驱动动效语义**(D1/B5 消费):initial(开局整片亮起——"地牢初醒")、chain(连锁撕开——P1 高光时刻的动效载体;**连锁止于旗的呈现已登记 D1/教学**——GD-1 配套)、dig(单格回应);kind=mine 事件 = 已引爆雷格视觉态(已开底 + 雷图标 + **可通行可读**——工程债的呈现半边:玩家必须看得出"这是走过的雷、能走";**已升入 D1×6④ 义务台账**——单节登记不生效,2026-09-09 评审修正)。 +4. **变异标记保真**:variant_ids 透传错误即呈现欺骗(雾格显影/隐藏全链路的数据源)。 +5. **无 MVP 音频**:挖掘/连锁/旗/雷格/收轮音效挂 D4(Alpha,依赖分层 D4 ← A2,B1);事件流已含全部触发点。 + +> 注:`art-director` 未经咨询——Lean 模式,生产前请人工复核本节与 art-bible(排期门禁:D1 GDD 前创建 §1–4)的一致性。 + +## UI Requirements + +A2 不拥有 UI。登记 D1 义务(承接 + 新增): + +- **输入模型**:鼠标驱动(PC 主输入)——左键已开格 = 移动 / 未开相邻格 = 挖、右键 = 插/拔旗、停手按钮 = `settle_batch([stop])`;**全部意图经 `settle_batch` 提交**(D1×6① 强制项,Interactions 表)——单帧多意图时 A2 语义序仲裁兜底(F4);**D1×6⑥:输入收集以单意图帧为单位**(stop 点击终结当帧收集),F4 语义序退为防御性兜底。触屏/Gamepad 非 MVP 门禁(意图对象架构使后续接入零数据层障碍)。 +- **输入消歧的感知前提(D1×6②③)**:左键双义的消歧完全依赖玩家一瞥判对已开/未开——四种格态(已开 0 格地板/已开雾格无字/未开格/未开雾格)一眼可辨、悬停光标区分移动/挖/旗目标语义、可挖域(8 向相邻)常驻指示为硬前提(后果不对称:误挖 = 1 AP + 可能触雷且不可逆)。 +- **三源拒绝呈现**:board_face(sub_reason 封闭枚举——Core Rule 5)/ resource_face(AP 不足)/ phase(收轮中)——D1 可辨呈现,每次拒绝玩家可感知(AC-21 为数据源验收)。 +- **voided 呈现义务(D1×6⑥)**:作废意图(至少 stop 被作废)须玩家可感知——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP 的读感欺骗。 +- **移动路径预览**:`compute_move_path` 返回 BFS 路径,呈现语义(合法性指示 vs 穿行动画轨道)归 D1 GDD 裁决定稿(D1×6⑤;systems-index 2026-09-07 门禁登记项——悬案的数据面已备)。 +- **输入摩擦护栏观测(2026-09-09 评审规格化)**:每轮原始输入动作数(含被拒/作废)∶ 当轮 AP 消耗 ≤ 2(A3 P4 载体;"点击"以设备中立"输入动作数"计)——归 D1 遥测(A2 只收意图对象、不收原始输入),≤2 为 playtest 待验证假设(挂 A3 OQ3);远距目标两击摩擦的复合意图(点击远格 = 自动 move+dig)登记为 D1 评估项(MVP 可裁决不做、留痕)。 +- 结算明细无 A2 附加项(效果归因全部经事件流字段由 B1 呈现,P2)。 + +## Acceptance Criteria + +> **测试载体与口径声明**:A2 是纯规则引擎——冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)。**A2 冒烟 = Core-Core 集成**:被测面 = A2 规则引擎 + **真 A3 实例**(资源全经真账本 `try_spend` 驱动——A2 不可自行扣减;冒烟另以 B1 编排角色最小形驱动 `notify_round_start()`/`notify_settlement_done()`,仅开轮/收轮;B1 就绪后改走真实调用方,断言口径不变)。棋盘输入 = Board class_name 直引**程序构造**(不经生成器、不经 load()/preload()——A4 AC-5/A1 夹具先例;板规格即 A1 产物 schema 的坐标级实例化,A1 未实现不阻塞本冒烟)。**API 面(provisional,封闭枚举)**:`install_board(board)` · `settle_batch(intents)`(**D1 唯一提交入口**;单意图批 = 平凡形)· `try_dig/try_flag_place/try_flag_pull/try_move(cell)`(便捷形 ≡ 对应单意图批)· `compute_move_path(cell)`(只读)· `retire_board()`;intent ∈ {dig, flag_place, flag_pull, move, **stop**}(stop 执行 = 批事务内委托 A3.request_stop)。`ActionOutcome{accepted, outcome ∈ {executed, voided, rejected}, reject_reason ∈ {board_face, resource_face, phase, board_invalid}, sub_reason(仅 board_face 携带——封闭枚举见 Core Rule 5:not_adjacent/target_revealed/target_concealed/flag_present/flag_absent/no_path/out_of_catalog)}`。**无 chord 负空间**:封闭枚举即"无对应 API"的验收形态,新增快开入口 = 接口漂移。 +> **事件捕获断言面**:B1/D1 未设计 → 以事件捕获列表断言,分订两发射面——A2 面(cell_revealed / round_end 棋盘路径 / flag_judged)+ A3 直发面(stopped round_end)。**payload 口径钉死**:① digit 恒真透传(仅 kind=safe 携带;雾格携带真值——呈现隐藏归 D1,判定源 = variant_ids,A2×4③);② reward_eligible:channel=initial 恒 false、chain/dig ∧ safe 恒 true(含链开雾格)、kind=mine 恒 false;③ round_end 核心字段 ≡ A3.last_round_end 逐字段(同构)。**只读观测量**:A2 侧 = 已开集/旗集/小人位/事件捕获;A3 侧 = ap/rap/phase/last_round_end。多步 GIVEN 均给出构造序列。 +> **夹具策略(程序构造小板,每用例先 `reset_diagnostics()`)**:**行为夹具根 = 零**(A2 无数据表、无加载期校验类别——`--data-root` 通道在本域无消费者;资源面非默认价目分支经 D2 R3 debug override 构造,A3 AC-7 同款)。**登记常数协议(A1 黄金向量同源)**:每板登记 (R₀ 成员集, |R₀|, |S|, 关键格 digit, 级联集) + 逐格手工推导记录入版本库;失效处置 = 先复核推导、推导无误即实现缺陷。板清单(W=H=7 或 8;**spawn = 出生 3×3 中心格坐标,逐板显式登记**:b_spawn / b_flag / b_batch / b_mine_walk / b_degenerate = (3,3),b_chain / b_fog = **(3,2)**——按默认"板中心"读法该两板 B3 = {2..4}² 会含 y=4 雷墙雷、违反不变式①,板不可安装;2026-09-09 评审修正并纳入 OQ3 复核范围): +> +> | 板 | 规格(坐标级要点) | 服务 | +> |---|---|---| +> | `b_spawn` | M = {(1,y): y∈[0,6]∖{3}} 竖墙 6 雷留缺口;R₀=35、\|S\|=43、未开 8 格;(1,3) digit=2 | 安装五属性/双包含/挖掘/移动/相位 | +> | `b_chain` | spawn=(3,2);M = y=4 横墙 6 雷留 (3,4) 缺口;R₀=28(y∈[0,3] 全安全格);墙下封闭区 14 格;(3,6) 为 0 格、级联集 \|C\|=13 | 连锁/双包含/链不越旗 | +> | `b_fog` | spawn=(3,2);b_chain 几何 + M += {(5,0),(6,1)};雾 F = {(0,5),(1,5)}(链可达)∪ {(6,0)}(链不可达);雾格 digit 2/3/2、厚度≤2 ✓;R₀ 常数随 M 变化按板重算((6,2)/(6,3) 退出 R₀) | 雾链两态/雾旗/快照/零特判/回放 | +> | `b_fog_bare` | b_fog 同 M 同几何、variant 标记剥离 | AC-20 同构对板 | +> | `b_flag` | b_spawn 同几何复用;(1,2)/(1,4) 雷 = hit 旗靶、(0,3) 安全 = miss 旗靶 | 旗生命周期/轮末判定/三源拒绝 | +> | `b_batch` | b_spawn 同几何复用(未开恰 8 格支持构造) | 批次四组合/两收轮路径 | +> | `b_mine_walk` | M = {(3,5)} 单雷;(3,4) digit=1 ∈ R₀ | 雷格挖开/已引爆可通行 | +> | `b_degenerate` | 8×8、M = {(7,7)} 单雷角——R₀ = S(全 0 格连通,构造性退化) | 不变式④ install 拒绝 | +> | `b_invalid_*` | ① mine_spawn:b_spawn+M+=(3,3) ② fog_spawn:b_fog+F+=(3,2) ③ fog_flood:b_fog+F+=(5,1)(R₀ 成员)④ digit:b_spawn+数字篡改 | 不变式①②③④ 各一(AC-2) | +> +> **期望值推导纪律**:行为类 THEN 数值 = ConfigDB A3 表读值推导(ap_per_round/各价目经测试通道读取)⊕ 板登记常数;不写死玩法数值;唯一例外 = 登记常数。默认价目全 1 AP 下"AP 不足"分支不可构造(A3 EC2 同款)——资源面分支显式承载于 debug override 非默认表值(AC-21)。 +> **致命类独立进程协议:零行 manifest(显式声明)**——D2 AC-2b 协议适用前提 = 加载期数据通道致命;A2 无加载期校验,install 违例 = `board_invalid` 拒绝 + error 诊断(进程致命保留给数据通道;实现期 ADR 若裁决升格,回本节补 manifest 行)。 +> **out-of-scope 覆盖声明(deferral 以对侧登记为生效条件)**:**B1**——轮次编排接线(Waiting 出口/next_round_mode/request_refresh 代理)、结算兑付与 P2 来源标注、**兑付去重**(诚实重复上报的对侧,A3 Rule 8/OQ5)、EC8 末轮判定次序、双发射源消费序(对侧登记:systems-index B1×8 义务清单 + 本 GDD B1×3,生效条件成立);**B4**——奖励内容与差异化插槽、debuff 档位(对侧登记:A4 三条定价硬约束);**D1**——数字显示消费 variant_ids、三源呈现、路径预览表现、点击摩擦观测(对侧登记:A4 D1×2 + 本 GDD D1×6);**A5**——显影接口(OQ4);**A1×A2 双包含端到端集成用例**——"A1 真生成器产物 → A2 install"全链路回验两系统实现后集成冒烟承载(本 GDD 冒烟用程序构造板,登记 deferral,2026-09-09 评审补);**标错惩罚**——OQ1 登记,本节仅承一致性推论预告(惩罚前 AC-12 只断言 hit=false)。 + +**安装与初始展开** + +- **AC-1(安装 + 初始展开五属性 + A3 EC5 定义落点)** GIVEN 真 A3 实例(phase=Idle)与 `b_spawn`,WHEN `install_board(b_spawn)`,THEN accepted=true 且事件捕获**仅**含 cell_revealed 批,逐事件断言:① **免费**——A3 ap 仍=表读值(零 try_spend)且诊断零新增;② **计入清图**——构造序列挖尽未开 8 格(登记常数)时恰在第 8 次触发 `round_end{cleared, new_board}`(无超前/滞后);③ **无奖励**——全部 reward_eligible=false(channel=initial);④ **永不触雷触雾**——全部 kind=safe;⑤ **恰等 R₀(有序断言)**——事件**有序列表** ≡ 测试侧朴素 BFS 重算 R₀ 的行主序枚举,逐格逐序相等(规范序钉死——DFS/列主序等"错误但确定"的实现不可通过;登记常数 |R₀|=35 为独立锚)。随后 `notify_round_start()` → Active。〔覆盖 Core Rule 4;A3 EC5 委派〕 +- **AC-1b(不变式④·清图退化板拒绝)** GIVEN `b_degenerate`(R₀=S 构造板),WHEN `install_board`,THEN accepted=false、reject_reason=board_invalid、error 诊断恰一条类别=**inv4_r0_equals_s**(枚举见 Core Rule 3);WHEN 随后 `install_board(b_spawn)`,THEN accepted=true(拒绝原子性)。〔覆盖 EC1/不变式④〕 +- **AC-2(不变式①–③·违例板 fail-fast)** GIVEN `b_invalid_mine_spawn`/`b_invalid_fog_spawn`/`b_invalid_fog_flood`/`b_invalid_digit` 各板(每用例先 reset_diagnostics),WHEN 各 `install_board`,THEN 均 accepted=false、board_invalid、error 恰一对应类别(**inv1_mine_in_spawn / inv2a_variant_in_spawn / inv2b_variant_in_r0〔"R₀ 触雾"归此——②的 R₀ 子情形〕/ inv3_digit_invariant**,封闭枚举见 Core Rule 3)。〔覆盖 Core Rule 3/EC2〕 +- **AC-3(双包含回验两臂——门禁缺项即 blocking)** GIVEN `b_chain` 与 `b_fog` 各 install 后捕获的初始域格集 E(B),测试侧按 A1 F1/Rule 4 公开定义朴素重算 R₀(B) 与排除域 X(B)=R₀∪外扩 1 环(**X(B) 以最终产物板计**——先布后删后基准,A1 Rule 4 措辞修订义务 EC1),THEN **臂①** E(B) ⊆ X(B);**臂②** E(B) ≡ R₀(B) **逐格恰等**(不欠展开也不过度展开——A1 回退口径不触发的行为面)。两板全用例通过。〔覆盖 Core Rule 4/F3;registry a2_dual_containment〕 + +**挖掘、连锁与雾** + +- **AC-4(挖掘基元 + 数字恒真透传 + 单意图批等价护栏)** GIVEN `b_spawn` Active、小人于 R₀ 内,WHEN `try_dig(c)`(相邻未开),THEN executed、A3 ap −dig_ap_cost(表读值,真 try_spend)、事件恰一条 `{cell=c, kind=safe, channel=dig, reward_eligible=true, digit ≡ 测试侧重算 8 邻雷数}`;WHEN `try_dig`(非相邻 / 已开格 / 有旗格),THEN rejected+board_face(sub_reason 分别 = not_adjacent / target_revealed / flag_present)、零事件、ap 不变;**等价护栏用例**:同构造态分别驱动 `try_dig(c)` 与 `settle_batch([dig c])`(各新轮),THEN 事件流/ap/棋盘只读观测量/诊断逐项相同(Core Rule 9 单意图批 ≡ 便捷形)。〔覆盖 Core Rule 5/9;A3 Rule 2〕 +- **AC-5(连锁展开 + 链不越旗)** GIVEN `b_chain`,WHEN 构造序列挖 (3,4)→移→挖 (3,5)→移→`try_dig((3,6))`(0 格),THEN 第三次 executed 且 ap 总耗 = 3×dig_ap_cost(级联免费——非 3+13 格);事件:channel=chain 批恰 |C|=13 条(登记常数)**且序 ≡ 测试侧 BFS 行主序枚举**、全 reward_eligible=true、digit 恒真透传、事件数 = chain_size + 1(一致性恒等式);级联止于非 0 格(链只经 0 格扩);链格计入清图(挖尽封闭区后余格清图即 cleared)。**分支 2(链不越旗——EC17)**:构造至小人立于 (3,5) 后 `try_flag_place((2,5))`(未开相邻,1 AP)→ 再 `try_dig((3,6))`,THEN (2,5) 保持未开 + 带旗(零 cell_revealed)、chain_size=12(级联集减 1——(1,5)/(1,6) 经 (2,6) 照常开出,仅旗格本身被挡)、旗格轮末判定照常。〔覆盖 Core Rule 6/F2/EC17〕 +- **AC-6(连锁开雾格——A2×4①③ 承载;原 EC4 专项断言编号化,2026-09-09 评审)** GIVEN `b_fog`,WHEN 同 AC-5 构造至 (3,6) 级联,THEN 四项逐验:① 链可达雾 (0,5),(1,5) 免费开出(channel=chain、ap 无雾附加扣减);② variant_ids 含 "fog" 且 digit=2/3 恒真透传(呈现判定数据源——A2×4③ 同板证);③ 级联停止(级联集与 b_chain 同构板逐格同形——雾格只作叶);④ reward_eligible=true 且计入 cleared 判定。**另一用例(链不可达全价)**:小人移至 (5,1) 后 `try_dig((6,0))` → executed、channel=dig、耗 1 AP 全价、variant_ids 非空、digit=2 透传。〔覆盖 A2×4①③;A4 EC4/fog_ap_note 两态执行侧〕 + +**结束路径与批次仲裁** + +- **AC-7(cleared 路径 + payload 同构 + 同帧竞争)** 用例 1(同帧竞争,EC7 钉死):GIVEN `b_batch` 构造至"未开恰 1、**ap=1**"(cleared 与 ap_exhausted 同事务成立——"耗尽优先"缺陷仅在此构造可检出),WHEN `try_dig(末格)`,THEN executed、A2 面 `round_end{cleared, new_board}` 恰一条(ap_exhausted 不触发)、其字段 ≡ A3 `last_round_end` 同帧读值(组包数据源行为面)、与 stopped 路径 payload 同构(字段集/类型一致)、rap 不变(剩余作废);用例 2(剩余作废非平凡分支):同构造 ap=k≥2,WHEN `try_dig(末格)`,THEN cleared 恰一条、rap 不变(k−1 > 0 作废非平凡)。〔覆盖 Core Rule 10/EC7;A3 Rule 5/6/9/EC10〕 +- **AC-8(ap_exhausted 路径 + 零窗口)** GIVEN `b_batch` 未开 ≥2、ap=1,WHEN `try_dig(非末格)`,THEN executed、`round_end{ap_exhausted, same_board}` 恰一条、A3 remaining_ap=0 不变式;WHEN 紧接任意 `try_*`,THEN 全部 rejected+phase、零事件。〔覆盖 Core Rule 10;A3 Rule 3/EC1/EC14〕 +- **AC-9(stopped 路径 + 直发面捕获)** GIVEN `b_batch` Active 未开完、ap=k≥1,WHEN `settle_batch([stop])`,THEN **A3 直发面**捕获 `round_end{stopped, same_board}` 恰一条(A2 面零 round_end——双发射源行为面)、rap = 前值 + k(1:1,表读值推导)、事件序 = flag_judged ×N 先于 round_end(事件序契约)。〔覆盖 Core Rule 9;A3 Rule 4/9〕 +- **AC-10(批次四组合仲裁 + 耗尽型救援 + 联合清图——A3 EC4 deferral 义务兑现)** GIVEN `b_batch`(每用例新轮新构造):**a**〔dig清→stop〕→ cleared 胜出、rap 不变(作废);**b**〔stop→dig清〕→ 与 a 同结果(同 path_tag/同 rap 终态/同事件数——异类序无关性);**c**〔dig→stop,ap=1 耗尽型构造:新轮 7 次非清挖〕→ **语义序救援用例**:stopped 胜出(朴素到达序将产出 (ap_exhausted, RAP+0)——语义序校正为 (stopped, RAP+1))、rap += 1(表读值推导)、被弃 dig 格仍未开(续轮同图可证);**d**〔stop→dig,同 ap=1 构造〕→ 与 c 同结果;**e**〔dig清₁→dig清₂ 同批〕→ 类内稳定序:到达序最先者执行胜出、恰一条 cleared、第二 dig 作废零副作用;**f**〔非清 dig₁→非清 dig₂ 联合清图;多步 GIVEN:前轮挖雷 (1,2) 开道(雷格可通行的构造性利用)→ 挖尽 x=0 列余格 → 停手收轮;新轮未开恰 {(0,3),(1,3)}、ap=8〕→ dig₁ 执行不清图(rem 2→1)、dig₂ 结算时点 clears_map=true 胜出 → cleared、两意图均执行、rap 不变(出口映射按结算时点真值键控——F4)。全用例断言:作废意图零棋盘写/零扣减/零事件(棋盘只读查询逐格不变)、round_end 恰一条;c/d 断言 `flag_judged ×N` 先于 `round_end`(事件序契约)。〔覆盖 Core Rule 9/F4/EC6/EC13/EC15;**A3 冒烟不可构造用例在本节兑现,deferral 生效**〕 + +**旗** + +- **AC-11(旗插拔挪 + 挖旗格拒绝)** GIVEN `b_flag`、小人于 (2,3),WHEN 依次插 (1,4)、插 (1,2) 后拔 (1,2)(挪旗),THEN 全 executed、ap 合计 −(place+pull)×2(表读值)、旗集随动、零事件外发;WHEN `try_dig((1,4))`(有旗),THEN rejected+board_face(EC11 落点);WHEN 插非相邻/插已开/插已有旗/拔无旗/拔非相邻,THEN 各 rejected+board_face。〔覆盖 Core Rule 5/8;A3 EC3/EC7/EC11〕 +- **AC-12(轮末统一判定 + 雾旗恒不中 + 诚实重复上报——A2×4②)** 分支 1:GIVEN `b_flag` 插 (1,4)(雷)与 (0,3)(安全,经挖 (1,3) 后站立可达),前置断言验"被拔的不判"(插 (1,2) 后拔,轮末无该条目),WHEN `settle_batch([stop])`,THEN flag_judged 恰 2 条**集合断言**:{(1,4),true}、{(0,3),false}(行主序枚举);分支 2(雾旗):GIVEN `b_fog` 小人相邻未开雾格插旗,WHEN 轮末,THEN `{cell,hit=false}` 恒(雾区无雷零特判;标错惩罚上线后"恒受罚"推论 = OQ1,惩罚前不可判定);分支 3(重复上报):分支 1 收轮后 `notify_settlement_done(same_board)`+`notify_round_start` 续轮旗仍在,WHEN 再停手,THEN 再次 `{(1,4),true}`——A2 逐轮诚实重复上报,**去重归 B1/B4(out-of-scope)**。〔覆盖 A2×4②;A4 EC5;A3 Rule 8/OQ5 上报半〕 +- **AC-13(旗随图存续 + 初始展开不重放)** GIVEN `b_flag` 已插 2 旗 stopped 收轮,WHEN 续轮(same_board),THEN 旗集不变且**事件捕获零新增 cell_revealed**(初始展开板级一次、跨轮不重放——每轮重放的违例实现在此用例现形);WHEN `retire_board()` + `install_board(b_fog)`,THEN 旗集 = 空、初始展开照常(按新板)。〔覆盖 Core Rule 8/EC11;A3 Rule 8/EC9〕 + +**移动与雷格** + +- **AC-14(移动 BFS + 确定性 + 免费)** GIVEN `b_spawn` 小人于 (3,3),WHEN `compute_move_path((2,3))` 两次,THEN 逐格相同(确定性)且全格 ∈ 已开集、相邻步 8 向合法;WHEN `try_move((2,3))`,THEN executed、位置更新、**ap 不变**(不经账本)、零事件。**拒绝/幂等四对(输入-输出显式配对)**:① move 未开格 → rejected+board_face(sub_reason=target_concealed);② move 已开但 BFS 不可达格 → rejected+board_face(sub_reason=no_path)**且** `compute_move_path` 对同格返回空(预览与执行同源,一用例双断言);③ move 目标 = 当前格 → executed 幂等(零移动:位置不变、零事件、ap 不变——EC14);④ 正常移动分支保持。〔覆盖 Core Rule 7/EC14;A3 Rule 2 移动行/P4〕 +- **AC-15(雷格挖开 + 已引爆可通行——工程债)** GIVEN `b_mine_walk`、小人于 (3,4),WHEN `try_dig((3,5))`,THEN executed、ap −1、事件恰一条 `{kind=mine, channel=dig, 无 digit 字段, reward_eligible=false}`、**轮次不结束**(两面零 round_end);WHEN `try_move((3,5))`,THEN executed(**可通行**——工程债验收落点)、位置=(3,5);WHEN 经雷格续挖 (3,6),THEN 正常受理。〔覆盖 Core Rule 11/EC10;systems-index 工程债登记〕 + +**相位、重置、快照** + +- **AC-16(相位拒绝)** GIVEN NoBoard 或 Installed·Frozen(Idle/Waiting 两分支),WHEN 依次 dig/flag×2/move/`settle_batch([stop])`,THEN 全部 rejected+phase、棋盘只读查询与事件面逐字不变;GIVEN Active,WHEN `install_board`/`retire_board`,THEN 拒绝(板态——EC16)。〔覆盖 States 矩阵/EC12/EC16;A3 输入×相位矩阵对侧〕 +- **AC-17(retire/重装重置)** GIVEN `b_spawn` 已装且部分挖掘,WHEN `retire_board()`,THEN 全动作拒绝(phase;无板态)、事件分段边界确立(跨段断言禁止——防前图泄漏);WHEN `install_board(b_flag)`,THEN 初始展开按新板重算、无前板残留(旗/小人位/已开集)。〔覆盖 Core Rule 3/EC11〕 +- **AC-18(快照抗表重载——A2×4④)** GIVEN `b_fog` 基线事件流 E₀ 已捕获,WHEN 测试通道换根加载变异表(B3 AC-15 换根模式)后重放同一意图序列(新 A3 实例+新 install),THEN 事件流与 E₀ **逐位相同**(含 variant_ids)——生成期快照非活引用;A2 零表读取(AC-22③)使抗重载为构造性,本用例为行为级代理。〔覆盖 A2×4④;A4 EC8〕 + +**确定性与零特判** + +- **AC-19(确定性回放)** GIVEN 同坐标规格独立构造的 `b_fog` ×2(各配全新 A3 实例),WHEN 同一定长意图序列驱动两遍(含挖/链/雾链/插拔旗/移动/多意图批/停手收轮——覆盖三 channel、flag_judged 与批次仲裁),THEN 两遍事件捕获列表**逐位相同**(数、序、全字段)。〔覆盖 Core Rule 1;D2 Rule 5 的 A2 层〕 +- **AC-20(零特判行为代理——A2×4③ 反向证成)** GIVEN (`b_fog`, `b_fog_bare`) 同构对(同雷布同 digit、仅 variant 标记差异),WHEN 同一意图序列各驱动一遍,THEN 两事件流**除 variant_ids 外逐位相同**(差分容许域 = 该字段且仅该字段)——连锁/清图/旗判定/价目/收轮全不感知变异。〔覆盖 A4 Rule 4 零特判;Core Rule 1〕 + +**拒绝原因与扫描** + +- **AC-21(三源拒绝区分 + sub_reason 玩家语义)** GIVEN `b_flag` + debug override `action_economy.flag_place_ap_cost=2`(D2 R3 域内值;**承载声明**:默认价目下资源面分支不可构造,同 A3 AC-7)构造 ap=1,WHEN `try_flag_place`(棋盘面合法),THEN rejected+**resource_face**、全态不变;WHEN `try_dig`(非相邻),THEN **board_face + sub_reason=not_adjacent**;WHEN `try_dig((1,4))`(有旗格),THEN **board_face + sub_reason=flag_present**("须先拔旗"与"非相邻"数据可分——UX-1 定稿验收);WHEN Waiting 相位任意动作,THEN **phase**——三源互异可判定;**目录外用例(EC9)**:WHEN `try_dig((-1,-1))`(越界),THEN rejected+board_face + **sub_reason=out_of_catalog** + 诊断收集器恰一条 debug(非静默吞)。〔覆盖 Core Rule 5/EC9;A3 EC2 对侧〕 +- **AC-22(静态扫描·零随机/零表读取/零 UI——承 D2 AC-9/10 模式)** GIVEN A2 文件集(规则引擎/事件载荷/冒烟脚本/夹具规格),THEN:① 零 `randomize()`、零自建 RNG 构造(**tests/ 豁免不启用**——A2 确定性回放不需种子,严于 A1 先例);② 零 ConfigDB 外 data 表加载(Board class_name 直引豁免);③ **零 `ConfigDB.*` 属性读取**(A2 全部输入经 A3 账本与 A1 产物——设计决策;实现期 ADR 若裁决需读表,走接口漂移修订本条);④ 零 `extends Control/Node2D`、零 `.tscn` preload;⑤ AC-1 在 `godot --headless --script` 实跑通过(退出码 0 + 断言收集器零失败)作为本项判定(Rule 7);⑥ 纳入 D2 AC-14 项目级人工三要素字面量扫描(warning 级 + 人工复核,成员清单随实现建立并入——B3 AC-19 同款)。〔覆盖 Core Rule 1;D2 Rule 1/2/5/7〕 + +**覆盖矩阵(验收评审用)**:Core Rule 1→AC-19/20/22 · 2→AC-4/6/7/8/9(payload 前言钉死)· 3→AC-1b/2/17/18 · 4→AC-1/3(A3 EC5 委派 + 双包含)· 5→AC-4/11/21 · 6→AC-5/AC-6 · 7→AC-14 · 8→AC-11/12/13 · 9→AC-4 等价护栏/AC-9/10 · 10→AC-7/8 · 11→AC-15 · 12(无 chord 负空间)→API 封闭枚举 · F1→AC-1②/5/7 · F2→AC-5/AC-6 · F3→AC-1⑤/3 · F4→AC-10 · EC1→AC-1b · EC2→AC-2 · EC3→AC-11 · EC4→AC-6 · EC5→AC-12 · EC6→AC-10 · EC7→AC-7 用例 1 · EC8→AC-18 · EC9→AC-21 目录外用例 · EC10→AC-15 · EC11→AC-13/17 · EC12→AC-16 · EC13→AC-10c/d · EC14→AC-14 四对 · EC15→AC-10e · EC16→AC-16 · EC17→AC-5 分支 2 · **A2×4①→AC-6 · ②→AC-12 · ③→AC-6/AC-18/AC-20 · ④→AC-18 · 双包含①→AC-3 · ②→AC-3 · A3 EC4 deferral→AC-10 · 工程债→AC-15 · OQ1 在位→OQ 节**。(23 条 = AC-1…AC-22 + AC-1b;2026-09-09 评审将原"EC4 腿"编号为 AC-6 并补 EC17。) + +## Open Questions + +1. **标错惩罚(已拍板偏离登记,2026-09-09 用户拍板——systems-index 门禁留档)**(owner:用户 + 原型小补丁会话;deadline:垂直切片前置 playtest 合并会话前):门禁原要求"标错惩罚用原型小补丁实测后再写本 GDD"(2026-09-07 CD/PR 门禁),现改为以本 OQ 形式登记、不阻塞设计——**候选方案 = 敌方伏击 +1**(标错旗在轮末判定 `hit=false` 时为敌方追加 1 层伏击;量纲随 B2 ambush 定稿联动),**实测义务随本 OQ 保留**、实测依赖 playtest 会话与先推进设计主链的偏离理由已留档。本惩罚兼任**双重职责**(A3 OQ2):误标税 + 停手轮/死 RAP 态下旗滥用的唯一价格。**一致性推论(上线时生效,无特判)**:雾内旗恒受罚(A4 EC5)、判定载体 = 本 GDD `flag_judged.hit=false` 事实流(B1/B4 消费,A2 零改动)。**惩罚重复性裁决项(2026-09-09 评审增补,实测会话前钉死)**:hit=true 兑付有去重(每雷实例一次,B1×3③),hit=false 惩罚**无去重声明**——按现行轮末判定载体,忘拔的错旗默认每轮 +1 伏击(5 轮 = +5),与一次性收益严重不对称;候选 = 随旗实例去重 / 显式接受每轮累计("流血"设计),须在原型实测会话前落定,防实测数据混淆"一次性过重"与"累积过重"。落地动作:原型 v3 补丁实测(可与 v3 终验合并会话)→ 实测支持则修订本 GDD(判定批扩展)+ registry;否决则关闭 OQ 并在 A3 OQ2 另定价旗滥用。**A2 /design-review 须核对本 OQ 在位,缺位即 blocking。** +2. **事件外发载体与捕获 API(实现期 ADR 确认项)**(owner:实现期 ADR):signal vs 回调注册、事件捕获断言面的落地形态——**依赖事件捕获列表的 THEN(AC-1/3/4/5/6/7–10/12/13/15–20 的事件序列断言)**为纸面契约,ADR 未确认前该子集不视为可执行(A1 缺口 G1"度量独立入口"同型);返回值/诊断/静态扫描类断言(AC-1b/2/11/14/16/21/22 及各 AC 的 reject 分支)不在此限、现在即可执行(2026-09-09 评审范围收窄)。 +3. **夹具登记常数第二人复核**(owner:A2 /design-review):各板 (spawn 坐标、R₀ 成员集/级联集/digit) 手工推导记录入版本库后安排复核——推导错误即假失配/假通过(A1 OQ5 同型);**spawn 坐标为 2026-09-09 评审新增登记项**(b_chain/b_fog=(3,2),默认中心读法产出不变式①违例板)。 +4. **A5 探测道具接口预留**(owner:A5,第 11 位):显影(未开雾格数字显影)对 A2 查询面的需求形态——接口归 A5 GDD 定稿,本 GDD 不预写(Interactions A5 行)。 +5. **D1 输入接线确认**(owner:D1,第 10 位):stop 意图经 `settle_batch` 提交的接线义务(D1×6① 强制项)与单帧多意图的真实构造性——**2026-09-09 评审升格**:单意图帧输入收集已从本 OQ 升格为 D1×6⑥ 登记约束(stop 点击终结当帧收集或等效;若 D1 保证单意图帧,四组合仲裁退化为结构不可构造——义务仍由 AC-10 冒烟承载,不豁免)。 +6. **拔旗价目与旗滥用观测联动**(owner:A3 OQ2/OQ3,低优先):`flag_pull_ap_cost` 复核与"停手轮旗动作占比"指标的 A2 侧观测量(旗动作批占比)——A2 零改动,遥测口径随 A3 复核会话对齐。 diff --git a/design/gdd/legacy-2026-09-13/a3-action-economy.md b/design/gdd/legacy-2026-09-13/a3-action-economy.md new file mode 100644 index 00000000..b4e4c86e --- /dev/null +++ b/design/gdd/legacy-2026-09-13/a3-action-economy.md @@ -0,0 +1,220 @@ +# A3 行动经济(Action Economy) + +> **Status**: Approved(2026-09-07 三轮 re-review 8 条文本级 blocking 同日全部核销;用户拍板接受修订并标记 Approved、免独立 delta 核销——核销依据见评审日志第三轮对照表与放行记录;不设第四轮全量评审) +> **Author**: user + agents(数值继承原型 v2/v3 实测结论与 game-concept 定稿;2026-09-07 三轮 full review:game-designer · systems-designer · qa-lead · economy-designer · creative-director;本轮修订由三轮评审结论推导,跨文档回写见评审日志) +> **Last Updated**: 2026-09-07(三轮评审修订版 · 评审日志:`design/gdd/reviews/a3-action-economy-review-log.md`) +> **Implements Pillar**: P1(主)· P2(次)· P3(旗稀缺性→AP 机会成本)· P4(移动免费的氛围归属) +> **Layer**: 1 Foundation · **Milestone**: MVP · **设计顺序**: 第 2 位(上游 D2 已 Approved,本次修订含 D2 delta 回写) + +## Overview + +A3 定义玩家在每个轮次里的行动预算与支出规则:每轮固定 AP(行动点 8),每种动作的统一价目(挖 1 AP、插旗 1 AP、拔旗 1 AP、移动免费、连锁展开免费),以及两条轮次出口的资源去向——**主动停手**把剩余 AP 按 1:1 转存进 **RAP 池**(remain action point,战斗域风险资源,攒满 `rap_refresh_cost` 可刷新雷图),**清图**则剩余 AP 直接作废、只产出清图奖励(内容归 B4)。A3 是玩家 agency 的计量器:扫雷侧的每个决策都花 AP,所有收益以**战斗效果**(安全格→主角 buff、标中旗→怪物 debuff、踩雷→怪物 buff,2026-09-07 用户三向效果定稿)直接进入轮次结算——A3 自身不产生、不折算任何抽象"资源点"。 + +对节奏的真实作用:AP 不减少总点击量,它把点击**摊销**进多个轮次并插入结算节拍;真正的决策压缩器是**连锁展开**(1 次点击兑现一片区域)与**停手**(主动放弃剩余格子换取风险退出权)。停手已退出战斗经济(不产出任何战斗收益)——它是纯粹的风险管理工具:"这轮我不想赌"的出口,而非另一条收入流。没有 A3,挖/旗/停手就没有统一成本语言;A3 自身不含任何随机性与表现面——纯资源账本,执行归 A2、兑付归 B1、显示归 D1。 + +## Player Fantasy + +直接承载(玩家每轮都在与之互动)。A3 服务支柱的体验锚点: + +- **P1"我算对了"的节拍器**:AP 预算把推理的兑现切进轮次节拍——推理本身免费完成,AP 决定"本轮能把多少已推出的结论兑现";取舍的真实落点在**构成选择**(确定动作供给 >8 时先兑现哪些)与**轮末尾巴**(赌不确定格 vs 停手攒 RAP),供给 ≤8 时确定动作可近乎无代价延后(棋盘跨轮存续)——决策密度由 A1 供给兜底,监测归 OQ3 供给指标。连锁展开免费是推理的放大器——一次正确的推断被免费兑现成一整片区域,紧张但不窒息。终局碎片区的零风险收尾可能跨轮完成,是节奏阀的有意代价。 +- **P2"规划的延迟兑现"**:每个安全格的 buff 当场入库、结算明细可见;停手改产 RAP 后,"何时收手"不再兑换成战斗现金,而是兑换成**风险退出权**——面对纯盲猜时,攒 RAP 换一张新图是"我不赌"的体面出口(2026-09-07 结构修订,原"停手折算现金"作废)。 +- **P3"风险与收益同源"**:三向效果映射(用户定稿)使同源原则具象——标中旗削弱敌人、踩雷强化敌人、挖安全格强化自己;旗子无数量上限(2026-09-07 修订),稀缺性由 **AP 机会成本**承载(8 向相邻限制约束的是当轮几何而非总量——跨轮小人可走遍全图):插一面旗 = 放弃一次挖掘(连锁富集区该成本放大、死 RAP 态碎片区趋零,条件性与其对冲见 OQ2 双重职责);挖已插旗格须先拔旗(EC11)——承诺有重量。 +- **P4 地牢氛围的承载项**:小人移动免费、挖/旗限相邻,服务"边缘探索"的地牢感(P4)而非经济杠杆——移动成本不是调参对象,点击摩擦护栏归 A2/D1 观测(每消耗 1 AP ≤ 2 次点击)。 + +参考感受:扫雷原版的无限额被本作刻意打破,预算把节奏从"点完为止"改为"每轮取舍";价目均匀分布是有意的 MVP 简化,异质性钩子(地形成本修饰)已为 A4 预留(Rule 2)。无 chord(双击快开)是**有意偏离经典扫雷**——保护"每格兑现都花 AP"的预算意义;正确标雷缺一键兑现节拍已登记 CD 创意风险 #3(与 #2 高光缺失合并归 B1/B4)。 + +## Detailed Design + +### Core Rules + +1. **每轮硬重置(仅 AP)**:轮次开始时 AP=8(`ap_per_round`);上轮剩余不累积、不结转。**RAP 是唯一跨轮携带量**(战斗域,见 Rule 11)。 +2. **统一价目表**(A3 定价、A2 执行,全部动作的唯一消耗口径;挖/插/拔均限小人 8 向相邻格;**无 chord**——有意偏离经典扫雷的双击快开,保护 AP 预算的决策意义,A2 承载): + + | 动作 | 消耗 | 备注 | + |---|---|---| + | 挖开格 | `dig_ap_cost`(1 AP) | **仅限小人 8 向相邻格**;若触发连锁展开(flood fill),**连锁部分免费**(v2 定稿:推理的放大器),连锁开的格计入清图进度 | + | 插旗 | `flag_place_ap_cost`(1 AP) | 仅限相邻格(v3 统一行动经济);**无旗子实物资源**——旗为棋盘标记,数量无上限(2026-09-07 用户修订) | + | 拔旗 | `flag_pull_ap_cost`(1 AP) | 仅限相邻格(与挖/插对称);移除棋盘标记、无返还物(无口袋旗可返)——"拔旗耗 AP"为本 GDD 确认的 v3 条文读法(OQ2 挂原型补丁复核) | + | 移动小人 | 0(冻结) | 点击已开格移动,BFS 限已探明区(A2 承载);免费服务 P4 探索氛围,非经济杠杆、调参豁免 | + | 连锁展开 | 0(冻结) | 免费,含连锁开的格计入清图进度 | + +3. **资源耗尽语义与结束仲裁**:AP 归 0 的瞬间轮次满足 `ap_exhausted` 结束条件(path_tag)——不存在 0-AP 操作窗口。**三条结束路径同帧/同批次竞争时按固定优先级仲裁:`cleared` > `stopped` > `ap_exhausted`**,与到达顺序无关,单一动作结算事务内一次判定、禁止双重折算(EC4/EC10)。 +4. **停手(提前结束扫雷阶段)**:轮次中任意时刻可主动停手 → 轮次结束(path_tag=`stopped`),剩余 AP **1:1 转存 RAP 池**(无乘数、非战斗资源,2026-09-07 用户修订——原 ×2 折算作废);本图未开完 → 下轮继续同图。 +5. **清图**:全部安全格开完 → 轮次结束(path_tag=`cleared`),**剩余 AP 直接作废**(不折算、不入 RAP——2026-09-07 用户拍板)+ 清图奖励。清图奖励内容归 **B4**,且是**差异化插槽**:B4 奖励表须预留按图类型(A1 的 biome/tag 上下文)分支的结构位——A3 只上报 `cleared`,不拥有奖励内容、不感知图类型。下轮换新图。 +6. **单一出口**:三种结束路径都只向 B1 输出 `(path_tag, continuation_hint)`(`continuation_hint`:stopped/ap_exhausted→same_board、cleared→new_board),仅作编排提示;权威换图指令是 B1 回发的 `next_round_mode`(Rule 9)。无任何折算数值进入结算——结算明细的效果项全部为 buff/debuff/清图奖励(P2 归因标注归 B1)。 +7. **数值承载于 D2**:全部数值存 D2 `ActionEconomyTable`(字段清单见 Schema 定稿节),经 ConfigDB 类型化属性读取;A3 代码零玩法数值字面量(D2 Rule 1)、零随机(本系统不涉及任何 RNG 流,P1)。 +8. **旗子单态**(2026-09-07 修订,原双态作废):**已插旗 = 棋盘状态,归 A2**——跨轮保留、随图存续,清图或刷新换图时随之消失;**口袋旗资源不存在**(数量无上限,稀缺性由 AP 价目承载)。插旗 = 1 AP + 棋盘 +1 面标记;拔旗反向(标记移除)。已插旗的兑付语义见 OQ5——硬约束:兑付一次性、当轮结算、**兑付资格以雷格实例为键**(每颗雷在本次战斗内至多兑付一次,资格随首次兑付消费;放置实例仅为当轮结算的兑付载体)、拔旗重插同一雷格**不恢复已消费的资格**(堵死 2 AP/轮的重插农场,2026-09-07 三轮评审改定)、禁止逐轮重复兑付;换图后新图的雷为新实例,资格自然重置。 +9. **账本独占与事件搭载**(二轮评审修订):A3 独占资源账本,唯一扣减入口 `try_spend(action_kind) -> bool`——余量检查、扣减、结束仲裁在同一事务内原子完成;A2 只判**棋盘面合法性**(相邻、目标格状态、无 chord)并执行棋盘效果,是挖/插/拔的调用方。**`round_end` 双发射源**(2026-09-07 修订,systems-index 回边裁决已同步):棋盘路径(ap_exhausted/cleared)由 A2 外发(原裁决不变);**停手路径(stopped)由 A3 直发**——停手为非棋盘输入,由输入层直调 A3,A3 仲裁后自行外发,payload 与 A2 路径同构 `(path_tag, continuation_hint)`(纯停手轮不存在"无发射者"漏洞)。A3 另存 `last_round_end` 只读记录(测试/审计断言落点,**同时是 A2 组包 round_end payload 的合法数据源**:A2 调用 `try_spend` 返回 true 后,若本次事务内轮次结束,A2 读取 `last_round_end` 组包外发——该读取权为接口契约的一部分,非仅测试设施)。Waiting 出口消费 B1 回发的 `next_round_mode ∈ {same_board, new_board}` 与 `battle_over` 信号。 +10. **下游经济契约**(F1,2026-09-07 结构性改写):全部扫雷收益以战斗效果直兑,无抽象点数;契约条款见 Formulas 节 F1,登记为 B1 GDD 验收项。 +11. **RAP 池(remain action point)**(2026-09-07 新增;决策主体与多次受理三轮评审钉死): + - **来源唯一**:停手转存(1:1,无乘数);清图与 ap_exhausted 的剩余 AP 均不入 RAP。 + - **生命周期 = 战斗域**:战斗内跨轮次、跨扫雷图累积;`battle_over` 进入 Idle 时清零(用户拍板:每场战斗清零;清零的可见反馈与教学落点见 UI Requirements——防"攒的钱凭空没了"读感)。跨战斗携带为未来选项,若开放须修订本条 + 登记 D3/C2 持久化归属(OQ7)。 + - **支出唯一**:刷新雷图。**决策主体 = 玩家**(经 D1 刷新入口在**挖掘期(Active 相位)**发起;**B1 `refresh_board` 挖掘期刷新编排内调用** `request_refresh() -> bool`——2026-09-12 D4 修订:刷新决策前移挖掘期,原"结算编排期 Waiting 相位代理调用"退役;禁止"RAP 满即自动刷")。`rap ≥ rap_refresh_cost`(默认 8)时扣减并**当场换新图继续挖**(AP 不重置、本轮剩余 AP 保留;A2 装板受理域已同步修订——Active·仅刷新路径;**刷新获得的新图不产生清图奖励**(它不是清图));不足则拒绝、状态不变(EC13)。**每轮上限 `refresh_cap_per_round`(默认 2)**——2026-09-12 D1 提案采纳(原"无结构性次数上限/冷却"作废;RoundStart 重置计数,超限拒绝,EC15)。**旧板旗随旧板作废**(零兑付、旗 AP 不返还——拔旗不返还同族;A2 EC11 换图全量重置承载;D4 修订裁决)。**经济注记(修订预警)**:原定价"8 RAP + 敌方时间税"的时间税半句对当场刷新**失效**(无结算发生)——价格实际 = 8 RAP + 每轮 2 次上限;OQ3 弃图率/刷新率预警阈值联动复核。MVP 无 RAP 上限。 + - **无战斗收益**:RAP 不折算、不兑付任何战斗效果——纯风险退出资源。 + +### ActionEconomyTable Schema 定稿(D2 回写义务,2026-09-07 三次回填) + +> 2026-09-07 RAP 结构修订 delta:删除 `flags_per_round` / `flag_unit_cost` / `stop_multiplier` / `clear_multiplier` 四字段(旗上限与折算乘数均作废),新增 `rap_refresh_cost`。全部字段单行全局、int 类型;knob 列 = 是否开放调参(非 knob 字段仍入表——代码零字面量,修订须走本 GDD 修订流程)。D2 Core Rules 表行、Status 行与 registry 已同步回写。**2026-09-12 D4 修订 delta**:新增每轮刷新上限规则(默认 2——Rule 11/EC15);config 结构位 `refresh_cap_per_round` 预留,**D2/registry 字段回写随实现批次**(届时 AC-10a 七字段清单同步扩为八字段)。 + +| 字段 | 类型/范围 | 默认 | knob | 说明 | +|---|---|---|---|---| +| ap_per_round | int [1, 32] | 8 | ✓ | 每轮行动点(v2 实测定稿;双重身份:节奏 knob 兼停手 RAP 转存规模 knob) | +| dig_ap_cost | int [1, 8] | 1 | ✓ | 挖单格成本(域下限 1:0 = 无预算系统失效,越域校验拒绝) | +| flag_place_ap_cost | int [0, 8] | 1 | ✓ | 插旗 AP 成本(旗无实物资源,仅此一项消耗) | +| flag_pull_ap_cost | int [0, 8] | 1 | ✓ | 拔旗 AP 成本(OQ2 复核落点) | +| rap_refresh_cost | int [1, 64] | 8 | ✓ | RAP 刷新门槛;默认 8 = `ap_per_round`,使"满 AP 一轮停手 = 恰好一刷"成为基线节拍;单字段域内任意值均可运作(仅节奏差异),跨字段约束见下 | +| move_ap_cost | int | 0 | ✗ 冻结 | 移动免费(P4 承载,调参豁免) | +| chain_ap_cost | int | 0 | ✗ 冻结 | 连锁展开免费(v2 定稿) | + +**跨字段校验(2026-09-07 三轮评审增补,D2 validate() 级)**:任一动作成本字段(`dig_ap_cost` / `flag_place_ap_cost` / `flag_pull_ap_cost`)**≤ `ap_per_round`**——违反即 Rule 6 加载期致命。依据:dig=8 × ap=1 双双域内会使挖掘永久不可用、cleared/ap_exhausted 双不可达(软锁循环),属结构失效而非节奏差异,与本表"域下限 1 = 零预算系统失效"同族。归 ActionEconomyTable schema 类 `validate()` 跨字段类别(D2 既有"schema validate() 收集后致命"机制承载,无需新增 D2 校验通道);违规夹具登记为 D2 校验双通道条件性夹具(同悬空引用待遇)。域上界注记:dig/flag 上限 8、refresh 上限 64 为 MVP 量级护栏(非推导界),随调参需要修订。 + +### States and Transitions + +轮次内资源状态机(**本表为 A3 资源侧视图,编排权威序列以 B1 轮次生命周期编排章节为准**;A3 只暴露资源余量、`last_round_end` 并消费 B1 信号;**可观测相位域 = {Idle, Active, Waiting}**——RoundEnd 与 RoundStart 均为单事务内的瞬态,不可观测、不作为任何测试 GIVEN): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| RoundStart | `notify_round_start()`(B1 编排;战斗首轮要求当前 Idle) | AP←8(硬重置);RAP 不动(跨轮保留)→ Active | +| Active | 预算就绪 | 接受耗 AP 动作并经 `try_spend` 原子扣减;每个动作结算事务内按优先级仲裁(cleared > stopped > ap_exhausted)→ RoundEnd(胜出 path_tag) | +| RoundEnd(瞬态) | 任一结束路径胜出 | 停手:RAP += remaining_ap(1:1);ap_exhausted:不变式断言 remaining_ap=0,违反即致命;清图:剩余 AP 作废。写 `last_round_end` → Waiting | +| Waiting | 结算完成前不接受 round_start(AC-14) | 消费 B1 信号:`next_round_mode=same_board` → 下轮 RoundStart(同图——stopped/ap_exhausted 轮**恒 same_board**,2026-09-12 D4 修订);`new_board` → 下轮 RoundStart(新图,A1 经 B1 编排生成——仅 cleared 轮)→ RAP 保留;`battle_over` → Idle(RAP 清零)。~~`request_refresh()` 在此相位受理~~(作废——刷新决策前移挖掘期,2026-09-12 D4 修订) | +| Idle | 战斗终结 | 不响应任何动作请求;RAP=0、ap=0;下一场战斗由 B1 发 round_start 重启(唯一出口) | + +**输入 × 相位矩阵(2026-09-07 三轮评审增补)**:全部 8 个 API 返回 bool;非法输入 = **拒绝(false)+ 状态不变 + debug 诊断**——fail-fast 不静默吞编排错误,但不采用进程致命(D2 语义的致命保留给数据通道)。 + +| 输入 | Idle | Active | Waiting | +|---|---|---|---| +| `notify_round_start` | 受理(RoundStart) | 拒绝 | 拒绝(settlement 消费后受理——AC-14 门) | +| `request_dig` / `request_flag_place` / `request_flag_pull` / `request_stop` | 拒绝 | 受理(经 `try_spend`) | 拒绝 | +| `request_refresh` | 拒绝 | **受理(Rule 11——挖掘期当场换图,2026-09-12 D4 修订)** | ~~受理(Rule 11)~~ 拒绝(原受理相位——刷新决策前移挖掘期) | +| `notify_settlement_done` | 拒绝 | 拒绝 | 受理(重复调用拒绝) | +| `notify_combat_end` | 拒绝 | 拒绝 | 受理(→ Idle,RAP 清零) | + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| A2 | 双向 | A2 判棋盘面合法性(相邻、目标格状态、无 chord)并执行棋盘效果,调用 `try_spend`;**A2 不可自行扣减资源**;`round_end` 由 A2 外发**棋盘路径**(ap_exhausted/cleared),停手路径由 A3 直发(Rule 9,systems-index 已同步);连锁规模与"图已开完"由 A2 作为动作参数上报;旗标记的棋盘状态归 A2 | +| B1 | 双向 | B1 结算消费 round_end payload;**挖掘期刷新编排(`refresh_board`)内调用** `request_refresh()`(玩家经 D1 发起,Rule 11——2026-09-12 D4 修订,原结算编排期代理退役);向 A3 回发 `next_round_mode`/`battle_over`(stopped/ap_exhausted 轮**恒 same_board**、cleared 轮恒 new_board——回发违约 = fail-fast);结算明细来源标注(安全格增益/标中旗削弱/清图奖励/雷→伏击,P2 归因);EC8 判定次序归 B1;**F1 五条款验收方兼闭合对账 owner**(对照表 + 挖掘风险不等式 + 弃图支配条款的单一签字人,强制检查点) | +| B4 | 相邻 | 清图奖励归 B4(差异化插槽:按图类型分支,结构位预留);标中旗 = 怪物 debuff 档位归 B4(OQ5 锚约束)——均不经 A3 账本 | +| D2 | 双向 | `ActionEconomyTable` schema 字段清单已三次回写 D2(最后一次 2026-09-07 RAP 修订 delta);读取经 ConfigDB | +| D1 | A3 → D1 | 当前 AP 与 **RAP** 是 HUD 常显字段;**刷新入口(玩家发起,Rule 11)**与 `battle_over` 时 RAP 清零的可见反馈(登记 D1 GDD 验收项);动作非法原因双源:棋盘面(A2 判)/ 资源面(A3 判),D1 呈现 | +| D5 | A3 → D5 | AP/旗(价目)/停手/RAP 的教学口径以本 GDD 价目表为准;须含"RAP 战斗结束清零"条目(防"存的钱凭空没了"误读) | + +**定稿回写义务**(D2 接口漂移规则,已执行三轮):① 首轮回填(2026-09-07)——值侧四项 + registry 登记;② 二次回填(同日)——schema 十字段交付;③ 本轮 RAP 修订 delta——四字段删除 + `rap_refresh_cost` 新增,D2 Core Rules 表行/Status 行、AC-7 示例值、registry 同步回写。 + +## Formulas + +### F1 下游经济契约(downstream_effect_contract,2026-09-07 结构性改写;三轮评审增补条款 4/5 并钉死闭合 owner) + +> 原 F1(停手 ×2 / 清图 ×1 折算)与原 F2(挖/停/清激励不等式)**整体作废**:停手改产 RAP 后退出战斗经济,"挖 vs 停"在同一量纲下的比较不复存在——二轮评审击穿 F2 的两条数学缺陷(②博弈树错误、①缺风险项)连同其修正方案一并作废,不再修复而是移除病灶。折算公式不存在(无乘数、无点数),`clear_multiplier < stop_multiplier` 结构不变量随之删除。契约为对效果侧的**五条款**(systems-index 门禁记录 ①–⑤ 已同步): + +1. **单一当量对照条款**:B1 须出**效果档位对照表**(1 最低档安全格 buff = ? 当量;debuff/清图奖励/敌方增益均对照归一),对全部收益来源**单一换算**——分源改率 = 契约作废,须回本 GDD 重议。此为原"线性率条款"的存续形态(归 OQ1;注意与现行 OQ6 清图奖励插槽区分)。 +2. **标中旗锚条款**(三轮评审补第二锚):标中旗的怪物 debuff 档位当量必须**严格大于**最低档安全格 buff 当量——否则插旗在最优情形下也只是打平,P3 旗循环经济空心;**且对照基准须为连锁放大的边际挖掘收益**(单 AP 平均挖掘当量,含连锁期望放大;provisional 下限 1:1,B4/B1 定稿校准)——只对最低档比较会使旗理性萎缩到碎片区。每战标中旗总吞吐(N 旗 = N debuff)无单档上限可依恃,由 B1 按条款 1 对照表对账;插旗挤占清图进度的边际轮成本由 B4 定档时核算,或在其 GDD 声明 MVP 忽略并给理由。owner:B4 表设计 + B1 对照验收。 +3. **挖掘风险条款**(三轮评审显式化,原"共同验收"改单一 owner):挖掘决策 EV 健康不等式 **P(安全) × 安全格 buff 当量下界 > P(雷) × 敌方增益当量上界**,两端分别锚定 **A1 可推理率生成参数**(P1 下限,决定 P(安全) 的玩家可达上界)与 **B2 惩罚上限表值**(上限值登记为 B2 GDD 验收项);**B1 为单一闭合对账 owner**(A1/B2 供给锚值,B1 签字验收),provisional 目标带以原型临时值(伏击 +3 / 敌 18HP/4ATK)代默认表给出首版示例、随 B2 表回填刷新;登记为 **B1 GDD 强制检查点**。P1 支柱要求 A1 可推理率使玩家能推高 P(安全)——本条款是"可推理率"的经济侧验收形态(二轮评审 #2 的存续形态收敛为可判定谓词)。 +4. **清图奖励条款**:清图奖励 > 0,量级与差异化分支归 B4(输入含 A1 图类型上下文;原 +10 临时锚降级为 B4 插槽的 provisional 参考)。 +5. **清图 vs 弃图刷新支配性条款**(三轮评审新增,原 F2 跨轮激励职责的认领):清图奖励当量下界须使 **"推到底清图"的期望当量 ≥ "确定格挖完即弃图刷新"的期望当量**,即 C ≥ 弃图所节省的期望雷险当量 + 轮数差 × 敌方结算强度当量(inputs:B4 的 C、B2 惩罚上限、A1 雷密度/可推理率、B1 结算强度;B1 闭合对账)——否则每图最优解退化为"捞完确定格就弃",清图高光节拍、B4 差异化插槽、game-concept"图完成"循环全部空心。**注记(原文登记)**:该环强度与 A1 可推理率**反向耦合**——可推理率越低(迷雾/终局 50/50 越多),弃图越优,玩家对 P1 失败的理性回应会反过来饿死清图内容;故本条款是 P1 失败模式的经济对冲,与条款 3 共同登记 B1 强制检查点。provisional 验算:+10 锚对默认 8 AP 图形况粗验通过,正式锚随 B4/B2/B1 表值定稿。 + +**登记为 B1 GDD 验收项**(systems-index 门禁已同步改写为 ①–⑤)。playtest 复核归 OQ3,当量归一归 OQ1。 + +## Edge Cases + +- **EC1 If AP=0 后仍有动作请求**:全部拒绝(返回 false)——轮次已在归 0 瞬间结束,不存在 0-AP 窗口(Rule 3)。 +- **EC2 If AP 不足以支付动作**(含插旗/拔旗——旗无实物资源,拒绝只看 AP):拒绝、状态不变;`last_round_end` 不变。 +- **EC3 If 玩家拔旗再插他格(挪旗)**:合法——插+拔合计耗 2 AP(插 1 + 拔 1,默认值下);相对"直接改插他格"多耗的 1 AP(拔旗)即"防免费重排"的有意代价。旗标记的移除/重建为 A2 棋盘操作,A3 只见 AP −2。 +- **EC4 If 同帧/同批次多结束路径竞争**:按 Rule 3 总仲裁 `cleared` > `stopped` > `ap_exhausted`,与到达顺序无关、单次判定。**"先停后挖"腿的批次语义在 A3 冒烟 API 上不可构造**(同步单调用无事务批次概念)——该腿显式 deferral 至 A2 集成测试承载(systems-index 门禁登记,非静默豁免);A3 冒烟承载"先挖后停"腿(AC-8)。 +- **EC5 If 连锁展开开完全图**:连锁免费;清图判定以"安全格全部开完"为准(含连锁开的格)→ 正常触发 `cleared`。RoundStart 的初始 3×3 自动展开同理(退化小图属既有行为,不设独立分支;**退化图刷清图奖励的钓鱼风险登记为 A1/B1 下游约束**——最小安全格数下限归 A1 生成参数验收;初始展开的定义落点——免费、计入清图进度、无奖励——归 A2 GDD 承载,本 GDD 仅经 `chain_size`/`clears_map` 参数引用其结果)。 +- **EC6 If 满 AP 停手**:合法——RAP +8(= `rap_refresh_cost`,恰好可达一刷,Rule 11 基线节拍,有意;该节拍与 OQ3"纯停手轮占比"预警同源,调参时联动评估)。**诚实声明(形态限定版,三轮评审改写——"病灶不存在"句式必须附形态限定与反例推演)**:①纯停手零挖掘环(停→刷→停)零战斗进账,唯一代价是敌方每轮照常结算(时间税自惩)——该退化形态结构自惩成立;②但理性玩家的真实风险环是"**挖完全部确定格 → 不确定即停 → 攒满刷新**":该环**有真实战斗进账、零踩雷风险**,刹车只有三个——清图奖励弃失(下界由 F1 条款 5 守住)、8 RAP 成本、时间税。本条不声称该环"病灶不存在";其健康性由 F1 条款 5(支配性不等式)+ OQ3 弃图率/完成度指标守门。 +- **EC7 If 同轮内插旗后又拔旗**:AP 合计消耗 2(插 1 + 拔 1,默认值下);旗标记由 A2 移除,A3 无旗状态可返。 +- **EC8 If 战斗最后一轮的收益兑现**:敌人已全灭时 B1 结算先判胜,当轮 buff/debuff/清图奖励是否兑现 = B1 判定次序域,A3 无断言义务(out-of-scope);A3 义务仅为每轮无条件输出一条 `(path_tag, continuation_hint)`。 +- **EC9 If 跨轮/跨图资源携带**:RAP 是**唯一**跨轮携带量(战斗域,跨图也保留);停手不是战斗资源机制——无复利问题;AP 腿每轮硬重置;旗标记随图存续(A2 域)。RAP 战斗结束清零(AC-11)。 +- **EC10 If 最后 1 AP 挖开最后安全格**:`cleared` 与 `ap_exhausted` 同时成立 → **清图胜出**:输出 `(cleared, new_board)`、剩余 AP 作废 + 清图奖励 + 换图,`ap_exhausted` 不触发。防"耗尽优先"实现把清图高光结算为最差结局、训练玩家永远留最后一格停手。 +- **EC11 If 挖已插旗的格**:拒绝——须先拔旗(1 AP,默认值下)再挖。旗是"别挖这里"的承诺标记,挖旗格合法化会催生零风险滥用;误标改主意的代价 = 拔旗 1 AP(P3 承诺重量)。棋盘面判定归 A2(out-of-scope 声明见 AC 前言)。 +- **EC12 If RAP 生命周期边界**:MVP 无上限;战斗结束(Idle)强制清零;跨战斗携带若未来开放,须修订本条与 Rule 11 并登记 D3 持久化归属 + C2 冒险域语义(OQ7)——禁止静默超域。 +- **EC13 If RAP 不足时请求刷新**:`request_refresh()` 返回 false、RAP 不变、相位不变(受理相位〔Active——2026-09-12 D4 修订〕内的资源不足拒绝;与 AC-14 的相位非法拒绝区分——后者附 debug 诊断,两者均为 bool 口径)。 +- **EC14 If 清图/耗尽轮次产生剩余 AP**:清图剩余 AP 作废(不入 RAP,Rule 5);ap_exhausted 恒 remaining_ap=0(不变式断言)。两路径 RAP 增量恒为 0(AC-4/AC-6 断言)。不变式断言的实现载体为 debug 断言(冒烟无 fault-injection 载体;行为级代理 = AC-4 的 rap 不变断言)。 +- **EC15 If 同一轮内多次请求刷新**(2026-09-12 D4 修订——原"同一 Waiting 相位内"):**受理至每轮上限 `refresh_cap_per_round`(默认 2)为止**,超限拒绝(resource_face;轮内计数、RoundStart 重置——原"无结构性次数上限/冷却"作废)。每次独立检查 `rap ≥ rap_refresh_cost` 并扣减;无冷却。每次刷新都当场换新图且不产生清图奖励(Rule 11)。**注**:挖掘期当场刷新无结算发生——原"敌方时间税"自惩半句失效(Rule 11 经济注记,OQ3 联动复核)。 + +## Dependencies + +**Upstream**:D2(hard——ActionEconomyTable schema 与 ConfigDB 读取;本 GDD 为该表回填 owner,三轮回填已完成)。A1 为**间接事实依赖**(新图生成经 B1 编排调用,A3 不直接消费 A1 接口,仅 Waiting 出口消费其结果信号)。无其他玩法上游(Foundation 层)。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| A2 | hard | 账本调用(`try_spend`)+ 价目表执行 + round_end 棋盘路径外发(Rule 9,payload 组包读取 `last_round_end`);A2 校验棋盘面合法性,资源合法性归 A3;旗标记棋盘状态归 A2;**EC4"先停后挖"批次仲裁 deferral 至 A2 集成测试(义务清单见 systems-index 门禁记录)** | +| B1 | hard | RoundEnd 输出 `(path_tag, continuation_hint)`;挖掘期刷新编排(`refresh_board`)内调用 `request_refresh()`(D4 2026-09-12);回发 `next_round_mode`/`battle_over`;**F1 契约验收项**(效果档位对照表单一线性换算) | +| B4 | soft | 清图奖励差异化插槽(按图类型分支);标中旗 debuff 档位(OQ5/F1 条款 2 锚约束) | +| D1 | soft | AP/RAP HUD 常显;刷新入口与 RAP 清零反馈;非法原因双源呈现 | +| D5 | soft | 教学口径来源(含 RAP/刷新/RAP 战斗末清零) | + +## Tuning Knobs + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| ap_per_round | 8 | 决策带宽膨胀、节奏拖长(v1 的 12 已实测否决);**双重身份**:满停 RAP 转存随之增大(8→12 使满停一刷后的富余 +4) | "想清楚的事做不完",违反 P1 | +| dig_ap_cost | 1 | ——(>1 使"连锁免费"的相对收益畸变,未实测) | 0 = 无预算系统失效(schema 域 [1,8] 拒绝) | +| flag_place_ap_cost | 1 | 插旗迟疑,P3 标雷策略萎缩 | 插旗无成本,旗标记泛滥(棋盘噪音) | +| flag_pull_ap_cost | 1 | 挪旗昂贵,误标惩罚过重 | 挪旗近免费,承诺无重量(OQ2 复核落点) | +| rap_refresh_cost | 8 | 刷新太贵,风险退出权萎缩、盲猜局被迫赌(违反 P1 兜底) | 刷新近免费,图来回刷节奏拖沓;敌方时间税仍自惩但体验烦躁 | + +- **Knob 交互**:`rap_refresh_cost : ap_per_round` 比值 = "停几轮换一刷"(默认 1:1 = 满停即一刷;该节拍与 OQ3 纯停手预警同源,联动评估);`ap_per_round × 插旗占比` 与 A1 布雷密度共同决定标雷全周期可行性(A1 定稿后强制复核);效果侧健康(F1 五条款)无单一系统 knob 可控——对照表/闭合对账归 B1、惩罚上限归 B2、奖励分支归 B4(对账归 OQ1);刷新率应急阈值(OQ3)的评估须与 B2 结算强度联动,勿独立调参。 +- **已冻结项**:AP 8 来自原型 v2 实测(12 点被否);移动/连锁 0 成本为结构冻结;**RAP 1:1 转存与战斗域生命周期为结构**(非 knob,修订走 GDD 流程;跨战斗携带 = OQ7 预留开关);旗数量无上限为结构(用户 2026-09-07 拍板)。 + +## Visual/Audio Requirements + +无专属表现面。AP/RAP 余量变化与"资源不足"反馈的呈现归 D1/A2;本系统无音频需求。 + +## UI Requirements + +A3 不拥有 UI,但对外暴露三个必须可读的状态:当前 AP、当前 RAP(D1 常显;**刷新入口为玩家发起**(Rule 11),入口可用性由 B1/D1 呈现);`battle_over` 时 RAP 清零须有可见反馈(登记 D1 GDD 验收项——防"攒的钱凭空没了"读感)。动作非法的原因需可区分且**双源**——棋盘面非法(非相邻格/挖旗格等,A2 判定)/ 资源面非法(AP 不足 / RAP 不足,A3 判定),D1 呈现(原因读数的测试承载声明见 AC 前言 out-of-scope)。结算明细的效果项带来源标注(B1 呈现,P2)。交互成本指标(每消耗 1 AP ≤ 2 次点击)归 A2/D1 观测(P4 免费移动的点击摩擦护栏)。 + +## Acceptance Criteria + +> **测试载体与注入口径**:A3 是被动账本,挖/插/拔的棋盘面执行归 A2(设计顺序第 6 位)、轮次节拍归 B1(第 9 位)——均未实现。冒烟脚本(`--headless --script`,承 D2 冒烟框架)**直接驱动 A3 最小 API 面**;A2/B1 就绪后改走真实调用方,断言口径不变。 +> **API 面**:`notify_round_start()` · `request_dig(chain_size: int, clears_map: bool) -> bool` · `request_flag_place() -> bool` · `request_flag_pull() -> bool` · `request_stop() -> bool` · `request_refresh() -> bool` · `notify_settlement_done(next_round_mode) -> bool` · `notify_combat_end() -> bool`。**全部 8 个 API 布尔返回 = 受理/拒绝**——"拒绝"直接断言返回值(三轮评审统一口径:notify_* 与 request_* 同为 bool;相位非法拒绝附 debug 诊断、非进程致命;受理矩阵见 States 节输入×相位表)。`chain_size` = 连锁**附加**格数(不含首格,≥0;首格恒耗 1 AP);`clears_map` = 本次动作后全图安全格开完。 +> **只读观测量**:`ap` · `rap` · `phase` · `last_round_end`(记录 path_tag / continuation_hint,可空)。**相位观测域 = {Idle, Active, Waiting}**;RoundEnd 为事务内瞬态、不可观测,不作为 GIVEN。 +> **口径声明**:`notify_settlement_done` 仅记录编排意图,phase 停留 Waiting(AC-11 断言);轮次开启仍由 `notify_round_start` 触发(AC-14 的门不被绕过)。各 AC 的 THEN 具体数值按**当前 ConfigDB 表值推导**(默认 8/1/1/1/8 下的具体数见各 AC;测试读取表值计算期望,不写死)。**夹具一致性(三轮评审增补)**:冒烟经 `chain_size`/`clears_map` 参数注入棋盘事实(信任方向见 Interactions A2 行)——各 AC 使用独立夹具图,`clears_map=false` 腿的安全格余量在夹具中显式登记(防"传 false 却实际清图"的夹具失配假失败)。**覆盖声明(out-of-scope)**:EC11 与挖/插/拔相邻性(A2 棋盘面域,A2 GDD 承载 AC,systems-index 门禁登记);旗标记棋盘状态与跨轮留旗(A2 域);EC8 末轮判定次序(B1 域);移动/连锁 0 成本不经账本(A2 侧承载);刷新的换图生成与奖励内容(B1/A1/B4 域);**资源面拒绝原因的读数区分**(UI 要求"可区分",但冒烟 API 无原因观测量——bool + 构造上下文可推断,呈现归 D1/A2 集成测试);跨字段校验违规(`validate()` 级,归 D2 校验双通道条件性夹具)。多步 GIVEN 均给出构造序列。`last_round_end` 为单槽记录:同值双发射不可区分(跨值覆盖由 AC-8/AC-9 值断言承担,同值双发防御归 A2 集成)。 + +**预算与价目** +- **AC-1a** GIVEN phase=Idle(冷启动,rap=0),WHEN `notify_round_start()`,THEN 返回后 ap=8、rap=0、phase=Active。 +- **AC-1b** GIVEN 上轮以 `(stopped)` 结束且 rap=3、`notify_settlement_done(same_board)` 已消费,WHEN `notify_round_start()`,THEN ap=8(硬重置)、**rap=3(不变——RAP 跨轮保留)**、phase=Active。 +- **AC-2** GIVEN Active ap=8,WHEN `request_dig(chain_size, false)` 以 chain_size ∈ {0, 1, 5} 各在新轮次执行一次,THEN 每次返回 true 且扣减恒为 1(连锁免费:chain_size=5 时 ap 8→7 而非 8→2)。 +- **AC-3a** GIVEN ap=8,WHEN `request_flag_place()`,THEN true、ap=7(旗标记为 A2 域,A3 无旗观测量)。 +- **AC-3b** GIVEN ap≥1(AC-3a 产物 ap=7),WHEN `request_flag_pull()`,THEN true、ap=6。 +- **AC-16** GIVEN AC-3a 产物(已插 1 旗、ap=7,旗构造经 `request_flag_place()`——A3 冒烟无棋盘观测量),WHEN `request_flag_pull()` 后 `request_flag_place()`,THEN 两调用均 true、AP 合计 −2(挪旗成本,EC3)、轮次不结束。 + +**结束路径与 RAP** +- **AC-4** GIVEN ap=1(构造:新轮 7 次 `request_dig(0,false)`)且 `clears_map=false`,WHEN `request_dig(0, false)`,THEN true、轮次立即结束,`last_round_end=(ap_exhausted, same_board)`、rap 不变(+0,EC14)。(前置:该格非最后安全格。) +- **AC-4b** GIVEN ap=1 且仅剩 1 个安全格(构造同上, digs 作用于预设夹具图),WHEN `request_dig(0, true)`,THEN true、`last_round_end` **值等于** `(cleared, new_board)`(清图胜出,EC10)、rap 不变;不存在第二条输出。 +- **AC-5** GIVEN Active ap=3 且图未开完(构造:5 次非清图挖,8−5=3),WHEN `request_stop()`,THEN true、`last_round_end=(stopped, same_board)`、**rap=3**(1:1 转存,恰一条输出);`notify_settlement_done(same_board)` 后 `notify_round_start()` → ap=8、rap=3。 +- **AC-5b** GIVEN Active 且 rap ≥ `rap_refresh_cost`(构造:满 AP 停手 → rap=8 → `notify_round_start()` 回 Active——RAP 跨轮保留),WHEN `request_refresh()`,THEN true、rap=0、**phase 不变(Active——2026-09-12 D4 修订:挖掘期当场换图,A2 装板受理联动)**。另 GIVEN Active 且 rap=3(构造:前轮挖 5 后 `request_stop()` → rap=3 → `notify_round_start()`),WHEN `request_refresh()`,THEN **false**、rap=3、phase 不变(EC13)。~~原 Waiting 期构造~~(作废——D4 修订)。 +- **AC-6** GIVEN ap=5(构造:3 次非清图挖),WHEN `request_dig(0, true)`,THEN true、`last_round_end=(cleared, new_board)`、**rap 不变(剩余 AP 作废,Rule 5/EC14)**;WHEN 随后 `notify_settlement_done(new_board)`、`notify_round_start()`,THEN 新轮 ap=8、**rap 仍= 前值(RAP 跨图保留,EC9 的 cleared 腿)**。 +- **AC-6b** GIVEN 冷启动 ap=8、夹具图恰余 4 个安全格,WHEN `request_dig(3, true)`(附加 3 格连锁开完全部余量;chain_size 不含首格),THEN true、ap=7、`last_round_end` **值等于** `(cleared, new_board)`、rap 不变——连锁免费且连锁格计入清图判定(EC5),单次判定。 +- **AC-8** GIVEN Active、剩余安全格恰 1、ap≥1(先挖后停腿——A3 冒烟可测子集),WHEN `request_dig(0, true)` 后 `request_stop()`,THEN 第二调用返回 **false**、`last_round_end=(cleared, new_board)` 恰一条、phase=Waiting(EC4;**stop-first 腿 deferral 至 A2 集成**,门禁登记)。 +- **AC-15** GIVEN Active ap=8 且图未开完,WHEN `request_stop()`,THEN true、`last_round_end=(stopped, same_board)`、**rap=8**(满停 = 刷新门槛恰可达,Rule 11 基线节拍,EC6)。 + +**非法与拒绝** +- **AC-7** GIVEN ap=1(构造:7 次非清图挖)且 debug override `flag_place_ap_cost=2`、`flag_pull_ap_cost=2`(域 [0,8] 内,经 D2 R3 注入)。**承载声明**:默认价目下全动作 1 AP,"AP 不足"腿(EC2)在默认表值下不可构造(ap≥1 恒可支付、ap=0 即收轮),故本 AC 显式承载于非默认表值夹具。WHEN 依次 `request_flag_place()`、`request_flag_pull()`、`request_dig(0,false)`,THEN 前两者 false(AP 不足,EC2)、最后者 true(ap 1→0,触发 ap_exhausted);`last_round_end` 断言只含末次记录。 +- **AC-9** GIVEN phase=Waiting(经 ap_exhausted 路径构造;RoundEnd 瞬态不可观测),WHEN 依次 `request_dig(0,false)`、`request_flag_place()`、`request_flag_pull()`、`request_stop()`,THEN 全部 **false**、ap/rap 不变、`last_round_end` 不变(无第二条输出)。 +- **AC-11** GIVEN phase=Waiting 且 rap>0(构造:停手轮后),WHEN `notify_settlement_done(same_board)`,THEN 返回 true 且 **phase 仍= Waiting**(仅记录编排意图,AC-14 门不被绕过);WHEN 随后 `notify_combat_end()`,THEN 返回 true、phase=Idle、**rap=0**(战斗域清零,EC12)、ap=0。 +- **AC-12** GIVEN phase=Idle,WHEN 任何动作/停手/刷新请求,THEN 全部 false、`last_round_end` 保持 null、phase 不变。 +- **AC-13** GIVEN phase=Idle,WHEN `notify_round_start()`(新战斗首轮),THEN 返回 true、phase=Active、ap=8(Idle 唯一出口;RoundStart 瞬态不作断言落点)。 +- **AC-14** GIVEN phase=Waiting 且结算未完成(未消费 next_round_mode),WHEN `notify_round_start()`,THEN 返回 **false**、phase 停留 Waiting、`last_round_end` 不变(fail-fast = 拒绝 + debug 诊断,不静默吞编排错误;统一 bool 口径,非进程致命)。 +- **AC-18** GIVEN phase=Active(冷启动后、未挖任何格),WHEN 依次 `notify_round_start()`、`notify_settlement_done(same_board)`、`notify_combat_end()`,THEN 全部返回 **false**、ap/rap/phase/`last_round_end` 均不变(输入×相位矩阵 Active 列全覆盖;防 B1 双启动静默 AP←8 重置)。`request_refresh` 于 Active **受理**已移入 AC-5b(2026-09-12 D4 修订——原 Active 拒绝腿作废)。 + +**确定性与配置** +- **AC-17** GIVEN **全新 A3 实例**(同一 ConfigDB 表;每遍以新实例构造,无跨实例状态),WHEN 同一动作序列驱动两遍,THEN 两遍 `last_round_end` 序列与 rap 轨迹逐项相同(零随机的行为级证据,D2 AC-15 的 A3 层前身)。 +- **AC-10a** GIVEN ConfigDB 加载完成,WHEN 脚本解析 `design/registry/entities.yaml` 的 `action_economy` 条目,THEN 与 `ConfigDB.action_economy` 七字段(ap_per_round / dig_ap_cost / flag_place_ap_cost / flag_pull_ap_cost / rap_refresh_cost / move_ap_cost / chain_ap_cost)逐一相等;registry 缺任一字段即失败。 +- **AC-10b** A3 文件集纳入 D2 AC-14 项目级扫描域(引用其人工三要素口径,不另设自动化字面量硬 THEN——字面量判定误报率高,D2 已定性为 warning 级 + 人工复核)。 + +## Open Questions + +1. **效果档位对照表与闭合对账**(owner:B1,第 9 位;原折算率与归一表义务合并存续于此——注意与现行 OQ6 清图奖励插槽区分):安全格 buff / 标中旗 debuff / 清图奖励 / 敌方增益的统一当量对照与换算由 B1 GDD 定稿,须满足 F1 五条款;**B1 兼任条款 3(挖掘风险不等式)与条款 5(弃图支配性)的单一闭合对账 owner**(A1/B2/B4 供给锚值,B1 签字验收),两项均登记 B1 GDD 强制检查点;每战标中旗总吞吐对账归本条。 +2. **拔旗耗 AP 复核 + 标错惩罚双重职责**(owner:A2 前原型小补丁,与标错惩罚实测合并会话):本 GDD 按"拔旗 1 AP、移除标记"定稿;复核改动现已有 knob 落点(`flag_pull_ap_cost`),若实测推翻则调 knob 并回写 registry。**标错惩罚现承担双重职责**(三轮评审登记):误标税 + 停手轮/死 RAP 态下旗滥用的唯一价格——RAP 无上限 + 满溢态使碎片区插旗机会成本趋零,"可疑格全插旗、永不拔"的滑向由本惩罚定价;OQ3"停手轮旗动作占比"指标联动观测。 +3. **playtest 预注册指标**(owner:垂直切片前置 playtest;三轮评审置换与增补):**弃图率 = refresh/(clear+refresh)**(>30% 观察、>50% 干预——provisional 预注册);**刷新时已开格完成度直方图**(完成度 <60% 的刷新占比 >50% 即信号);**停手轮旗动作占比**(>50% 即信号——投机旗/死 RAP 态检测,联动 OQ2);**确定动作供给 >8 的轮占比**(P1 决策密度兜底监测,与 A1 密度联合验收);刷新率/战斗(>3 次即观察信号,阈值评估与 B2 结算强度联动,勿独立调参);纯停手轮占比(**>20–30% 即观察信号**);停手时机分布、每轮犹豫时长、终局碎片区(清图完成度 >80%)轮次数。**失明声明(如实记录,三轮评审)**:原三指标(刷新率/纯停手占比/碎片区轮次)对"挖完全部确定格→停→刷"环结构性失明——该环停手轮先挖后停(非纯停手)、温和变体刷新率低、开到 >80% 即弃使碎片区读数反而变低(病态呈现为健康);故增前三项直测该环。应急方案(触发后启动):`rap_refresh_cost` 调参 / 刷新冷却(连续刷新须间隔 N 轮)/ 刷新新图保留已开格结构(重议级)。原"分级乘数/停手门槛"应急方案随折算作废而删除。 +4. **AP 耗尽的软提示**(owner:D5,低优先):本 GDD 定"AP=0 即收轮";教学若发现新手困惑,加提示不改规则。可扩展覆盖"RAP 达标刷新引导"与"RAP 战斗末清零提示"(D1 清零反馈的对应教学条目)。 +5. **标中旗兑付语义细化**(owner:B4/B1;A3 硬约束已定稿):标中旗 = **怪物 debuff**(2026-09-07 三向效果定稿,原"B4 最低档安全格同档"锚作废)。硬约束(兑付键三轮评审改定):兑付一次性、当轮结算、**兑付资格以雷格实例为键**(每雷本次战斗至多兑付一次,资格随首次兑付消费;放置实例仅为当轮结算载体)、拔旗重插同一雷格不恢复已消费资格(堵重插农场)、禁止逐轮重复兑付;换图后新雷为新实例。档位锚见 F1 条款 2(严格 > 最低档 + 连锁放大边际基准 + 总吞吐对账)。标错惩罚归 game-concept 待定问题(OQ2 合并会话,双重职责见 OQ2)。 +6. **清图奖励差异化插槽**(owner:B4,结构位预留 + A1 上下文):清图奖励未来按随机图类型(biome/tag)分支——B4 奖励表 schema 须预留图类型维度的结构位(2026-09-07 用户提出);A3 只上报 `cleared`,图上下文经 B1 传递,不经 A3。**约束(三轮评审登记)**:图类型上下文在战斗域内恒定(biome 挂关卡层、不逐图重掷)——否则 `request_refresh` 退化为 biome 重掷钓鱼口;该约束登记 A1/B4 共同验收。 +7. **RAP 跨战斗生命周期开关**(owner:垂直切片 knob 复核):MVP 每场战斗清零(用户拍板);若实测支持跨战斗存蓄(Boss 图连刷策略),须修订 Rule 11/EC12 + 登记 D3 持久化归属与 C2 冒险域语义。 diff --git a/design/gdd/legacy-2026-09-13/a4-terrain-variation.md b/design/gdd/legacy-2026-09-13/a4-terrain-variation.md new file mode 100644 index 00000000..a1be5045 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/a4-terrain-variation.md @@ -0,0 +1,223 @@ +# A4 地形变异(Terrain Variation) + +> **Status**: **Approved**(2026-09-08 两轮独立评审:full 8 阻塞 + 同日 re-review【六专员 + creative-director 终裁】6 阻塞均同日核销;CD 终裁建议 delta 核销免第三轮 full 评审,用户拍板照此放行【A3 先例】。日志与两轮阻塞→修复对照表:`design/gdd/reviews/a4-terrain-variation-review-log.md`。承重规则不变:生成期布置约束 + 零特判原则 + fog unlock_level=2) +> **Author**: user + agents(lean 门禁:systems-designer 审查 Formulas · qa-lead 审查 Acceptance Criteria;承重规则"生成期布置约束"为 2026-09-08 用户定稿;2026-09-08 full 独立评审与同日 re-review:game-designer · systems-designer · qa-lead · economy-designer · level-designer · ux-designer · creative-director 终裁——两轮修订全部由评审结论与终审裁决直接推导,无新增设计决策) +> **Last Updated**: 2026-09-08 +> **Implements Pillar**: P1(主——信息诚实边界,"不可推理"永不伪装成"可推理")· P3(信息维度变体——定价通货从雷险换信息+AP)· P2(暗面警示——雾区奖励可预期性切面)· P4(地牢氛围本体承载) +> **Layer**: 1 Foundation · **Milestone**: MVP · **设计顺序**: 第 3 位(上游 D2 已 Approved;A3 已 Approved) + +## Overview + +A4 定义扫雷图上的**地形变异**:在基础扫雷规则之上叠加的、区域性的规则偏离。基础版扫雷图上所有格子遵守同一套规则——开格出数字,数字即推理的原料;变异让某片区域的格子"不那么规矩"。MVP 唯一变异是**迷雾**:迷雾区的格子开出后**不显示数字**(未开时即以纹理标出区域范围,原型 v3 紫色迷雾格已验证可读性)——玩家看得见"哪里推不出来",但推不出区内的具体情况。变异框架同时保证岩浆、冰冻等后续变异(垂直切片起随关卡解锁)能在不推翻既有规则的前提下逐个加入。 + +玩家不"使用"A4:变异由棋盘生成(A1)布置在图上,玩家在挖掘中**遭遇并应对**它——看见迷雾边界那一刻,决策就出现了:如何安排挖掘顺序让 0 格连锁免费吃掉雾格(**连锁路由**)、链不可达的雾残段是按全价续付清图还是停手/弃图(A3 风险退出权衡)(垂直切片起再加"是否用道具买情报",A5)。诚实口径(2026-09-08 评审修正;同日 re-review 再修正"探路"残留——雾区无雷是玩家可见知识,探路零信息收益零命险,不构成选项,真实决策面 = 连锁路由 + 续付/退出):清图判定含雾格,**雾是清图的必经段而非可绕过的支线**;"绕开走"的真实形态是 A3 的停手/弃图风险退出——权衡发生在路由顺序、AP 预算与清图/弃图时机上。没有 A4,游戏失去两样东西:难度只能靠雷数单调缩放,关卡间"解锁新变异"的推进感(P4 越下越险)消失;且"无法推理"的格子会以埋伏形式随机出现——迷雾把"你推不出来"从暗处的埋伏变成明处的选择,是 P3"风险与收益同源"在空间维度的载体,也是对 P1"我算对了"的诚实兜底:推不出的区域被显式标注、可绕行、可买情报,而不是让玩家在自以为可推理的局面上撞墙。A4 只拥有变异的**定义与交互规则**:布置算法归 A1、挖/旗执行归 A2、呈现归 D1,数值落在 D2 的 VariationTable(本 GDD 为回填 owner)。 + +## Player Fantasy + +**直接承载**(玩家每轮都在看见并应对变异——Section A 定稿:直接感知)。A4 的情绪目标一句话:**站在洞口前的权衡感**——迷雾不制造惊喜或惊吓,它制造一个看得见边界的承诺:"里面有价值,进去的代价是 AP 溢价与放弃雾格数字这份推理原料。"**"里面有价值"不是愿望而是契约**——由 B4 三条定价硬约束兜底(两条防税下界:雾内奖励档位下界 ≥ 最低档安全格 buff 当量、清图奖励锚随雾成本负担联动上浮【口径 = 链不可达雾格,与定价成本同分母】;一条防福利上界:雾板净当量优势带上界——防"链免开 + 锚上浮"双白拿的福利板化,Interactions B4 行登记 B4 验收):防"进洞无额外价值"触发本节反向护栏、使迷雾退化为纯 AP 税,也防雾在 MVP 首发关(level 2 小图低密、链免开占比结构性最高)退化为零成本糖果区——后者会让玩家建立"雾=免费奖励"先验,待后期雾变厚全价残段出现时恰触发本节"被骗感"护栏(2026-09-08 评审修正;同日 re-review 增防福利腿与同分母口径)。 + +体验锚点(按支柱): + +- **P1"我算对了"的诚实边界**:迷雾是游戏对玩家的坦白——"从这里开始,数字不再说话"。它把"无法推理"从埋伏变成路标:玩家在雾外仍可用区外数字推理雾边缘的雷(**跨雾推理**——被雾边已知约束放大的常规推理体验,"隔着雾也算出来了";措辞按 2026-09-08 评审降调:非独立机制,是常规推理在雾边的延伸);进了雾,每一步是承诺而非计算。这份坦白本身就是掌控感的护城河:玩家输给的是自己选的赌,不是信息欺骗。基础区的可推理率生成保证归 A1,A4 负责的是"不可推理"**永不伪装成**"可推理"。 +- **P3"风险与收益同源"——降格声称(2026-09-08 评审修正:原"信息维度变体"强声称不过 P3 自家裁剪判据——雾 MVP 行对三向效果循环全惰性:无雷险入、无标中收益出)**:迷雾的"险"不在雷(**雾区无雷**——生成期约束的玩家可见知识),而在**信息与行动力的双重定价**:进雾不赌命,盲区预算按**链不可达雾格全价**收紧(链可达雾格经邻接 0 格连锁免费开出——两态口径,Formulas 注记;2026-09-08 re-review 修正本句"每格花 AP"全价残留)。诚实口径:**变异框架是 P3 的长期载体,迷雾仅是其信息切面;P3 的雷险维度由岩浆/冰冻(垂直切片起)承接**。迷雾真实的 P3 耦合点在雾边(Rule 5 双向效应)——雾边推理的原料与候选域变化由 A1 按双向净效应建模。 +- **P2"规划的延迟兑现"的暗面警示**:雾内奖励若不可见,规划退化成探索惊喜(创意风险 #1,CD 门禁登记 B4/A4 落实)——本系统在 Core Rules 定迷雾的**信息呈现规则**("边界可见、内容隐蔽"的精确切面),奖励可见性/探测机制的接口在此预留、内容归 B4/A5。 +- **P4 地牢氛围的本体承载**:迷雾是"下探黑暗"隐喻的直接具象——未探索的黑暗不再是抽象的未开格,而是有形状、有边界、趴在地图上的实体;后续变异(岩浆/冰冻)延续"越深越险的地貌"叙事(随关卡解锁)。 + +参考感受:Into the Breach 的信息诚实(游戏从不用隐藏信息骗你——本作反其道而行,把"信息缺失本身"诚实标注);杀戮尖塔地图的路径承诺(看得见的未知,选定的风险)。**反向护栏(裁剪判据)**:迷雾绝不能制造"被骗感"——若雾区边界不可见、或进洞无额外价值,迷雾退化为纯负体验税;任何让雾区"隐性化"的设计默认砍掉。 + +> ~~注:`creative-director` 未经咨询——Lean 模式(仅 D/H 两节委托专家),生产前请人工复核本节情绪定位。~~(2026-09-08 full 独立评审已咨询 creative-director 并终裁本节处置:幻想节按 MVP 事实诚实改写;"清图判定豁免雾格"选项经裁决**否决**——违反零特判承重规则。) + +## Detailed Design + +### Core Rules + +1. **变异定义框架**:变异 = 具名规则包(VariationTable 一行)。每个变异定义四件事:**身份**(variant_id / display_name / unlock_level / biome_tag)、**信息规则**(info_mask——隐藏什么)、**布置约束**(cell_constraint——允许落在什么格子上)、**作用域**(scope——region / global 预留)。框架自身不含运行时逻辑:逻辑存在于"信息规则 × 既有系统行为"的交集,变异定义只声明交集参数。 +2. **迷雾(MVP 唯一变异)**:region 作用域的信息剥夺变异,规则两条:未开雾格以专属纹理显示区域范围(边界可见);开出(无论挖掘或连锁)**不显示数字**。 +3. **生成期布置约束(承重规则,2026-09-08 用户定稿)**:迷雾区只能布置在**非 0 安全格**上——区内**无雷、无 0 格**。约束在 A1 生成期强制执行(A4 声明约束、A1 实现)。**"雾区无雷"是玩家可见知识**(教学与图例传达,D5 义务)。由此迷雾对既有规则全面惰性化——见 Rule 4。 + **结构推论(2026-09-08 评审增补:约束的几何硬边界,登记 A1 验收)**:非 0 安全格 = 8 邻域含 ≥1 雷 ⇒ 每个雾格必有**非雾雷邻居** ⇒ 雾区任意处**厚度 ≤2**(可判定谓词:≡ **任意 3×3 窗口非全雾**——3×3 实心雾块中心必为 0 格,不可能成雾。度量钉死为窗口谓词而非"直线连续行程 ≤2":宽 3 对角带无任何全雾 3×3 窗、完全合法,按行程读法会误拒——2026-09-08 re-review 钉死)——雾区恒为雷集切比雪夫 1-膨胀出的边界带,且雾面积占比存在依赖雷密度的必要上界(f ≤ 8×雷密度)。"区域数量/尺寸/占比"参数(A1 BoardGenTable 域)必须在此边界内定义(OQ1 联动);1~2 格宽雾带是雾的正常形态,视觉规格须按最坏宽度验收(AC-9)。可推导元知识:"每个雾格必邻区外雷"由规则可推出——按 P1 诚实原则(可推导即可教)纳入教学候选,口径归 D5 裁定。 +4. **零特判原则**:既有系统行为**一律不感知变异状态**—— + - **连锁展开**:触发与传播只看真实数字;雾格恒非 0 → 雾格自身**永不传播**连锁(不开出新级联);但雾格**可被邻接 0 格的连锁免费开出**(EC4——"不传播"≠"不被开",2026-09-08 评审澄清)。flood fill 代码零变异分支。 + - **数字计数**:数字 = 周围 8 格雷数恒真;雷全在雾外,任何数字的值不因雾存在而改变。 + - **清图判定**:雾格照常计入"安全格全开"判定。 + - **旗**:雾内插旗合法(价目照常)但**恒标不中**(无雷)——合法、无害、无意义。 + - **行动价目**:挖/旗/移动成本雾内外一致(A3"地形成本修饰"钩子**本 GDD 搁置**——启用须回 A3 重议 per-cell 成本接口)。 + - **结算**:雾格安全格奖励照常入库与结算(B1/B4 无需感知雾)。 +5. **信息剥夺边界**:迷雾隐藏的**仅有未开雾格的数字**。不隐藏:区域边界、已开格的全部信息、奖励入库过程。剥夺的实质是**推理原料**——雾格数字本可作为区外雷的推理源,其寂静使雾边界附近线索变少;区外数字对雾外雷的计数照常 → 雾边界推理仍可行但原料减少。**双向效应(2026-09-08 评审修正,禁单向建模)**:剥夺之外并存增益——雾格 = 已知无雷且必邻区外雷的非 0 格(Rule 3 结构推论),雾形本身携带雷簇位置信息(k 格雾区外环必含 ≥⌈k/8⌉ 雷;雾边邻格的雷候选域收缩、局部更易解),即"雾边 = 推理锚"。净效应双向且依赖雾形/雷密,A1 可推理率参数须按**生成后实测净效应**建模、禁止单向补偿(登记 A1 验收)。 +6. **布置权归 A1**:A4 不拥有布置算法。区域数量/尺寸/位置与雷图共生(约束执行)归 A1 生成算法 + BoardGenTable 参数((level_index, node_depth) 维度,A1 回填);布置随机经 `board_gen` 流取号(RngService scope 绑定:每次生成新扫雷图)。A4 对布置的唯一参与:**unlock_level 门控**——`level_index ≥ unlock_level` 的变异进入该关布置候选池。 +7. **出生区无雾**:初始 3×3 安全区及其自动展开范围永不含任何变异(A1 布置硬约束,登记 A1 验收项;**展开可达域由 A1 生成期确定性计算**——不依赖 A2 的运行期展开行为定义,2026-09-08 评审修正;**可达域取保守上界:从出生 3×3 经 0 格的最大 flood 闭包外扩 1 格**,任意 A2 展开定义的超集;A2 GDD 门禁登记回验项:**A2 实际展开域 ⊆ A1 排除域**——"构造上不可能"仅在超集关系下成立,2026-09-08 re-review 增)——防"开局即瞎"与"开局预开雾格"。初始展开**无奖励**(game-concept 定稿)优先于 EC4 入库语义;经本条生成期排除,初始展开触及雾格在构造上不可能发生(双保险)。 +8. **数值承载于 D2**:VariationTable(本 GDD 回填 owner,schema 见下节);A4 相关代码零玩法数值字面量、零随机(布置随机在 A1,经 board_gen 流)。 +9. **未来变异扩展规则**:岩浆/冰冻无 MVP 行(CD 门禁:无 MVP 容器),随垂直切片关卡解锁建行;新变异需新规则字段时走 D2 接口漂移规则回写本 GDD 与 D2;精英/Boss 图**全局变异**(C3 域)= scope=global 预留位,语义归 C3 GDD 定稿——本 GDD 只保证枚举位存在,不预写语义。 + +### VariationTable Schema 定稿(D2 回写义务) + +| 字段 | 类型/范围 | 默认(fog 行) | knob | 说明 | +|---|---|---|---|---| +| variant_id | String(主键) | `"fog"` | ✗ | 变异标识;A1 候选池与 A2 查询的键 | +| display_name | String | `"迷雾"` | ✗ | 图例/教学用名(本地化归 D6) | +| unlock_level | int [1, 999] | 2 | ✓ | 首次可布置关卡序号(game-concept 解锁序列:普通数字→迷雾→岩浆/冰冻);本表为定义表(数值恒定)非进度成长表,键 = variant_id(D2 占位已定),进度语义承载于此列——D2 Rule 4 豁免,语义写入 schema 注释 | +| scope | enum {region, global} | region | ✗ | 作用域;global 仅预留枚举位(语义归 C3,MVP 不产生 global 行) | +| info_mask | enum {none, hide_numbers} | hide_numbers | ✗ | 信息规则:该变异隐藏什么;新信息规则走接口漂移 | +| cell_constraint | enum {any, safe_nonzero} | safe_nonzero | ✗ | 布置约束:允许落在什么格子上——**Rule 3 承重规则的数据载体**,A1 生成期强制执行 | +| biome_tag | StringList 可空 | `[]` | ✓ | 关联 biome 主题(AD 门禁换皮约束);空 = 全 biome 可用 | +| counter_item_ids | StringList 默认空 | `[]` | ✗ | 可穿透该变异的 A5 道具清单(预留,A5 期回填——探测=雾内数字显影);**非平衡 knob**(内容回填字段,与 Tuning Knobs 节"结构冻结"归类一致——2026-09-08 re-review 修正原 ✓ 误标) | + +**校验**:variant_id 唯一(重复键致命,D2 Rule 6 既有类别);unlock_level ≥ 1(域校验);`scope=global` 行在 MVP 出现即 warning(C3 未定稿语义前不应出现——诊断收集器 warning 级承载);**跨字段校验 `info_mask=hide_numbers ⇒ cell_constraint=safe_nonzero`(2026-09-08 评审增补,违规致命)**——Rule 4"雾格恒非 0 / 级联止于雾格"是承重规则(Rule 3)的寄生结论而非 hide_numbers 的本性:无此校验,未来 hide_numbers + any 行将静默击穿 EC4(连锁不再止于雾格)。MVP 表内容恰含 fog 一行有效定义(内容事实,非 schema 校验;岩浆/冰冻不建行)。 + +### States and Transitions + +A4 无运行时状态机——变异是棋盘格的只读属性,生命周期四阶段: + +| 阶段 | 载体 | Owner | 行为 | +|---|---|---|---| +| 定义(构建期) | VariationTable .tres | D2 承载 / A4 回填 | ConfigDB 加载校验;热重载走 D2 既有门(战斗中拒绝,EC8) | +| 布置(生成期) | 棋盘格变体属性 | A1 | 生成新扫雷图时经 board_gen 流取号;unlock_level 门控候选池 + cell_constraint 强制 + 出生区排除 | +| 生效(执行期) | 格属性只读查询 | A2 / D1 消费 | 开格时读"是否雾格"决定数字显示与否;零运行时写入 | +| 消亡(随图) | 棋盘实例销毁 | A2 | 清图换新图 / 刷新 / 战斗结束——变异属性随棋盘实例消亡,无跨图存续 | + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| D2 | 双向 | VariationTable 8 字段回填(本 GDD 义务,schema 见上节);读取经 ConfigDB;新字段走接口漂移规则 | +| A1 | A4 → A1(定义消费) | 候选池门控(unlock_level)+ cell_constraint 生成期强制 + 出生区及其生成期可达域排除(登记 A1 验收);区域数量/尺寸/位置参数归 BoardGenTable(A1 回填,本 GDD 不定数值)且须在**厚度≤2 / f≤8d 硬边界**内定义(Rule 3 结构推论);布置随机经 board_gen 流;**可推理率生成参数按雾边双向净效应建模**(雾格数字不可作推理源 = 寂静减项;雾格已知无雷且必邻区外雷 = 推理锚增项——禁单向补偿,登记 A1 验收) | +| A2 | A2 → A4(只读查询) | 开格时查询格变体属性决定数字显示与否;**零特判原则**(A2 执行逻辑无变异分支——连锁/旗/清图行为照旧);雾格开启结果照常上报(安全格) | +| A3 | 契约相邻 | 价目不受变异影响(地形成本修饰钩子搁置,启用须回 A3 重议 per-cell 成本接口);**A3 F1 条款 5 对账注记(2026-09-08 评审修正——原"迷雾耦合消解"表述作废)**:雷险腿消解(雾区无雷不产生 50/50)+ 信息腿双向净效应 + AP 腿(链不可达雾格全价抬高清图成本与敌方结算时间税)三腿入账——见 Formulas 节对账注记,登记 B1 条款 5 强制检查点输入项 | +| B1 | 无感知(注记级) | 运行期结算不感知变异(雾格奖励 = 普通安全格奖励);设计期 F1 条款 5 对账注记同 A3 行(三腿入账,雾板/无雾板两态验算);**两态验算另须含两条方向性护栏(2026-09-08 re-review 登记,定量谓词归 B1/B4 对账闭合)**:① 雾板净当量优势带上界(雾板 vs 无雾板 EV 差的容许带——防福利方向);② 防钓鱼方向(与 A3 OQ6 防钓鱼约束对账):雾板下界抬升不得使"弃图刷雾板"支配"即刷"——逐板奖励差分 × 最低雾出现率 × 8 RAP 刷新会构造出 A3 OQ6 明令防的刷新钓鱼环,此处登记张力、解法(关卡层常量锚 / 防钓鱼不等式 / 与 OQ6 显式对账)归 B4/B1;playtest 弃图率/完成度指标**雾板分层**观测(注记回执 A3 OQ3——聚合指标会被雾板污染或反向掩盖,2026-09-08 评审增补) | +| B4 | 接口预留 | 雾区奖励内容/密度归 B4 表 + A1 参数。**三条定价硬约束(2026-09-08 评审登记两条 + 同日 re-review 增第三条;B4 GDD 验收项)**:① 雾内奖励档位下界 ≥ 最低档安全格 buff 当量——防雾格沦为"全图单位 AP 期望收益最低格"的纯税(注记:A3 条款 2 教义的对照基准为连锁放大边际收益,本锚仅是反淹没下界、不保证 per-AP 平价——per-AP 检查归 B1/B4 对账);② 清图奖励锚随**雾成本负担**联动上浮(口径 = 链不可达雾格数 × dig_ap_cost,与定价成本**同分母**——原"雾面积"口径与成本分母可双向脱钩、作废;上浮形态须为关卡层常量或受 B1 行②防钓鱼不等式约束,定量曲线归 B4);③ **雾板净当量优势带上界**(防福利方向——雾在 level 2 小图低密关链免开占比结构性最高,仅单向防税下界会使雾板退化为零成本糖果区,谓词定量归 B4)。定价成本口径按**链不可达雾格**计(Formulas 注记)。奖励预知方案(创意风险 #1)的 A4 侧切面已定:雾不额外隐藏内容,预知归 B4 呈现 + A5 探测 | +| A5 | A4 → A5(接口预留) | counter_item_ids 字段;探测类道具对雾的语义 = **雾内数字显影**(恢复推理原料,非排雷——雾内无雷);A5 期定稿 | +| C3 | 预留 | scope=global 枚举位(精英/Boss 图全局变异);global 行出现前须先定稿语义(MVP warning 校验) | +| D1 | A4 → D1(呈现规格) | 未开雾格专属纹理(区域边界可读);**形状+颜色双通道**(色弱冗余,视觉锚 2);绘制优先级 数字>小人>旗>变异纹理>底色(视觉锚 3);已开雾格 = 地板无字 | +| D5 | A4 → D5(教学口径) | **"雾区无雷"为必须教授的玩家可见知识**;教学要点:雾 = 盲区不赌命、雾内旗恒空、清图含雾格、**雾内无连锁折扣(连锁止于雾界属正常——防"连锁莫名断了"的被骗感)**、雾格成本口径(系统侧两态:链可达免费开出/链不可达全价;**玩家侧教学按保守启发式——"雾格按全价做预算,连锁白开当惊喜"**:链可达性取决于未开 0 格的隐藏拓扑,玩家决策时点不可求值,明教两态反生免费期待、恰触发反向护栏被骗感,2026-09-08 re-review 修正);可推导元知识"每雾格必邻区外雷"入教学候选(P1 诚实:可推导即可教,口径归 D5 裁定);教学文案不得写死解锁关号(EC9) | + +## Formulas + +> systems-designer 代理已审查(lean 门禁,2026-09-08):排除声明成立、谓词改集合语义、补四条下游验收项与预算构成注记,均已并入。**2026-09-08 full 独立评审**(systems-designer 重审 + level-designer 新发现):F1 边界代入无退化、谓词健壮性确认;lean 版"雾格全部全价"结论**部分作废**(改链不可达两态,见排除声明);条款 5"消解"改三腿入账。 + +### F1 布置候选门控(variant_pool_gate) + +`pool(level_index, board_biome) = { v ∈ VariationTable | (v.unlock_level ≤ level_index) ∧ (v.biome_tag = ∅ ∨ v.biome_tag ∩ board_biome ≠ ∅) }` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| level_index | int | ≥1 | 当前关卡序号;与 unlock_level **直接整数比较**(无 D2 R1 floor 解析——unlock_level 是定义表列,非进度表行键) | +| v.unlock_level | int | [1, 999] | VariationTable 列(fog 默认 2) | +| v.biome_tag | StringSet | ∅ 或非空 | **∅ = 通配**(全 biome 可用),非匹配失败 | +| board_biome | StringSet | 非空 | 当前图 biome 标签集;值域与来源归 A1(BoardGenTable biome 字段,A1 回填)——本 GDD 只声明集合类型语义 | + +**Output Range:** pool 为集合(MVP 规模 ≤1);逐项判定输出 bool。空池(如 level 1 雾未解锁)**合法**——A1 静默布置普通图,不报错(登记 A1 验收)。 +**Example:** fog(unlock_level=2, biome_tag=∅):level 1 → pool=∅;level 2 → pool={fog};未来 magma(unlock_level=3)在 level 3 起 pool={fog, magma},A1 从池中选取时**必须消费 board_gen 流**(回放确定性,登记 A1 验收)。 + +### 显式排除声明(R4 式) + +本 GDD **无平衡公式**——迷雾的全部数量参数(区域数/尺寸/位置/与雷密度及图尺寸的交互)归 A1 BoardGenTable,且须在 Rule 3 结构推论的硬边界(厚度≤2【≡任意 3×3 窗口非全雾】 / f≤8d)内定义。唯一 A4 侧定量注记(2026-09-08 full 评审改两态口径;同日 re-review 再改增量构成):**雾不改变 AP 价目,但改变清图预算构成,且雾格 AP 成本分两态**——雾格永不**传播**连锁,但**可被邻接非雾 0 格免费链开**(EC4):**链可达雾格免费开出、链不可达雾格按全价 `dig_ap_cost` 结算**。**雾致预算增量构成(2026-09-08 re-review 改写——原"≤ 轮数上限 × ap_per_round"不等式作废:"轮数上限"在 A3 契约中不存在【A3 轮次无界、AP 每轮硬重置】,该式恒真空转,且只约束雾增量却命名"清图预算校验"、诱导充分性误读)**:`雾致预算增量 = 链不可达雾格数 × dig_ap_cost`(量纲闭合:格 × AP/格),作为一项**计入清图总预算需求**参与 A1 BoardGenTable 的设计期核算;总预算可用量与全量校验(全量待挖下界 ≤ 可用预算)的口径归 A1/A3 契约,非本 GDD 定义。稀疏图链可达占比高,全价假设会系统性高估雾成本(level-designer 评审发现)。核算为**设计期(调参期)聚合义务**而非逐板运行期强制,违例动作 = 修正 BoardGenTable 参数(登记 A1 验收⑤)。**该增量对 A3 F1 条款 5 仅是可行性输入、不提供雾成本显著性保证——此局限如实登记**(2026-09-08 re-review)。 + +**对 A3 F1 条款 5 的对账注记(2026-09-08 full 评审修正——原"迷雾耦合消解"表述作废)**:雾对条款 5(清图 vs 弃图支配性)的影响是**构成变化而非消解**,三腿一并入账—— + +- **雷险腿(消解 ✓)**:雾区无雷、不产生 50/50 赌雷(原消解声称仅对此腿成立); +- **信息腿(仍在,双向)**:雾边寂静减少推理原料(推高弃图),与雾边推理锚增益(Rule 5)方向相反——净效应实测、非定理(终审裁决:game-designer"50/50 减少可能反向利好清图"成立,"必然推高弃图"不采纳); +- **AP 腿(新增,推高弃图)**:链不可达雾格全价无连锁折扣,抬高清图成本——敌方结算时间税随之上行。 + +雾面积占比与链不可达占比为 **B1 条款 5 对账输入变量**(链不可达占比的**生产者义务** = A1 生成期计算并输出,登记 A1 验收⑤——2026-09-08 re-review 补,防输入变量有名无源),B1 闭合对账须**雾板/无雾板两态分别验算**支配性不等式(A3 provisional 验算只在无雾图形况做过)+ **两条方向性护栏**(雾板净当量优势带 / 防钓鱼,见 Interactions B1 行——2026-09-08 re-review 登记)——登记 B1 强制检查点输入项。 + +### 下游定量验收项登记 + +- **A1**:清图预算按**增量构成**核算——雾致预算增量 = 链不可达雾格数 × dig_ap_cost,计入清图总预算需求参与**设计期(调参期)聚合校验**,违例 = 修正 BoardGenTable 参数(非逐板运行期强制;原"雾区全价、无 0 折扣"口径与"≤ 轮数上限"不等式均作废——链可达雾格经 0 格连锁免费开出、"轮数上限"无定义);且生成期须计算并输出**链不可达雾格占比**(B1 条款 5 对账输入的生产者义务——2026-09-08 re-review 补)。 +- **A1**:候选池为空(未解锁或 biome_tag 全不匹配)须静默跳过、不报错。 +- **A1**:池内多变异时的选取必须消费 RngService 的 board_gen 流(回放确定性)。 +- **C3**:scope=global 落地时须重审 cell_constraint 覆盖与零特判原则是否仍成立(含 `hide_numbers⇒safe_nonzero` 跨字段校验的相容性)。 + +## Edge Cases + +- **EC1 If 候选池为空**(雾未解锁如 level 1,或 biome_tag 全不匹配):A1 静默布置普通数字图——空池合法、非错误、无 warning(F1 Output Range 行为;两种来源同待遇)。 +- **EC2 If cell_constraint 无解**(非 0 安全格**面积或连通分量尺寸**不足以布置任何雾区——图太小/雷太密/雷太稀致雷膨胀皮肤碎裂/参数要求过高;判据含连通性而非仅总面积——Rule 3 结构推论使雾域恒为雷集边界带,总面积充足但碎裂成小分量时同样无解,2026-09-08 评审修正):本次布置雾的尝试失败 → **跳过雾布置,本图为合法无变异图**(A1 生成算法的降级产物)。禁止放宽约束硬塞——Rule 3 承重规则不为布置成功率让步。**部分达成契约(2026-09-08 re-review 增补)**:BoardGenTable 要求 N 区而生成仅达成 k 小人 > 旗 > **变异纹理** > 底色——雾纹理不得干扰雾外数字辨识;已开雾格 = 地板无字(不留雾残留遮蔽)。 + 3. **调色板交换优先**(视觉锚 4):雾纹理须支持 biome 色板变体(biome_tag 字段的换皮对应)——新 biome 不新增雾资产,只换色板。 +- **边界可读性 = 硬指标**:未开雾格的区域边界在任何色板下必须可读——这是"明处的选择"幻想的视觉前提(P1 诚实边界的呈现面),与"数字可读性 × 暗色氛围"样张测试(game-concept 风险登记第 1 条)同批验证。 +- 原型 v3 紫色迷雾格已验证基线可读性;正式色值/形状归 art-bible 格子样张测试定稿。 +- 动效(可选,MVP 不承诺):雾格开出瞬间的"雾散"过渡——归 B5/D1 表现里程碑,非 A4 义务。**无音频需求**(雾开出音效归 D4 Alpha 期)。 + +> 注:`art-director` 未经咨询——Lean 模式。生产前请人工复核本节与 art-bible 的一致性。 + +## UI Requirements + +A4 不拥有 UI。三个呈现义务归 D1:① 未开雾格纹理(区域边界可读——上述视觉规格,AC-9 操作化口径);② **"雾区无雷"知识的静态传达入口——登记 D1 MVP 验收两条(2026-09-08 评审登记:D5 里程碑在 Alpha,雾在 MVP level 2 即首遇,D1 是 MVP 唯一活载体;对比 A1×15/A2×4,本知识此前零验收落点):(a) 图例含"迷雾 = 无雷盲区"条目——**条目恒在、不设解锁门、可随时回看**;(b) 首遇提示触发 = **首个实际含雾格的板进入可操作状态(挖掘阶段开始)时一次性触发**——生成期一次查询即判定,免疫 EC2 降级板误触发、免疫 EC3 贴脸雾 t=0 歧义(原谓词"雾格首次与已开区相邻"在贴脸雾图生成瞬间即为真、在远雾图严重滞后,作废——2026-09-08 re-review 修正);"一次性"作用域 = **每 run 一次**(MVP 无持久化设施,存档级"一生一次"归 Alpha D3/C4 后重议);可在图例回看;**(c) 提示与图例的必含要点枚举(A4 持验收权的枚举对象——D1 文案缺任一要点即验收不通过)**:雾 = 无雷盲区不赌命、**雾格不传播连锁(连锁止于雾界属正常——防"连锁莫名断了"的被骗感;此为前次阻塞 8 第二知识源的 MVP 落点,2026-09-08 re-review 补)**、雾内插旗纯损;文案与呈现形态定稿归 D1 GDD(硬约束:非模态、不劫持输入、不打断挖掘流)**;③ 已开雾格 = 地板无字(负空间呈现,防误读为 bug)+ **底色层 tint 残留与雾区全开一次性完成信号(2026-09-08 评审采纳,登记 D1 验收候选——"站在洞口前"承诺的结束拍与清图进度可读;tint 处于视觉锚 3 最低层,与视觉锚 2"不留雾残留遮蔽"的冲突口径:遮蔽禁的是遮挡数字/实体的雾残留,tint 是底色层标记不犯此条;转正与否及完成信号粒度(视觉合并域 = 信号单元,禁实例级双响)登记 OQ8 裁决)**。结算界面无 A4 附加项(雾格奖励 = 普通安全格奖励,无独立归因类别——P2 标注归 B1)。无 HUD 常显字段(变异是棋盘属性非资源,与 A3 的 AP/RAP 不同)。 + +## Acceptance Criteria + +> **测试载体与口径声明**:A4 是定义层系统——冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)。**三层切分**:定义层(本 GDD 冒烟域 = schema/门控谓词/校验)· 布置层(A1 集成,**15 项**登记见 Dependencies)· 执行层(A2 集成,4 项登记见 Dependencies)——deferral 以对侧登记为生效条件;**A1×15/A2×4 清单已同步登记 systems-index 门禁记录(2026-09-08 评审增补)——A1/A2 GDD 门禁评审逐项核对落位,缺项即 blocking**。**致命用例独立进程协议**承 D2 AC-2b(runner 清单逐条登记"调用→期望退出码";单进程内只做测试通道收集断言)。夹具经 `--data-root` 注入,五根齐备(**3 根致命错误夹具 + 1 根 warning 夹具 + 1 根合法数据变体夹具**——2026-09-08 re-review 修正原"3 根错误夹具"计数失实:global_scope 为 warning 级非错误;并新增跨字段致命夹具):`variation_dup_key`(与 D2 AC-2 重复键夹具**独立同型**——A4 表首用,避免跨表耦合;致命)/ `variation_unlock_oob`(unlock_level=0,致命)/ `variation_mask_constraint`(info_mask=hide_numbers + cell_constraint=any 跨字段违规,**致命**——2026-09-08 re-review 新增:EC4"级联止于雾格"的承重保证此前零测试落点)/ `variation_global_scope`(**warning 级、非错误**——游戏通道 0 退出)/ `variation_biome_tag`(**合法门控测试行** biome_tag=[ice]、unlock_level=1,服务 AC-3,无对应校验类别)。期望值**由表值推导不写死**(unlock_level/biome_tag 为 knob,推导纪律适用两者——2026-09-08 re-review 修正原"唯一 knob"失称;防 EC9 及 biome_tag 调参打假)。每用例先 `reset_diagnostics()`(承 D2 冒烟协议,全局声明)。**runner manifest 退出码登记四行**:dup→≠0、oob→≠0、mask→≠0、global→0(biome_tag 根仅进程内测试通道消费、无退出码行,AC-3 前置断言其零诊断加载)。空表/缺表/类型错类别由 D2 AC-2 同类别夹具承载,A4 不设专属夹具(豁免声明:该三类为 loader 级类别、与表内容无关,D2 夹具可承载;空表情形另由 AC-1"恰含一行"间接兜底,2026-09-08 评审增补)。 + +**定义与内容** +- **AC-1** GIVEN 干净检出,WHEN 冒烟加载 Core Rules-3 表清单,THEN VariationTable 加载成功、**恰含 fog 一行**(无岩浆/冰冻行——内容事实断言)、fog 行**非 knob 六字段**(variant_id/display_name/scope/info_mask/cell_constraint/counter_item_ids)与 schema 定稿值逐一相符(`"fog"`/`"迷雾"`/region/hide_numbers/safe_nonzero/∅)、**unlock_level 与 biome_tag 两个 knob 字段仅作类型域断言、期望 = 表读值**(不作定值断言、不写死——EC9 及 biome_tag 调参防打假;biome_tag 定值断言系 2026-09-08 re-review 撤销:合法调参不得假失败)。 +- **AC-2(门控相对边界;全腿作用于干净检出默认表根——仅 fog 一行,2026-09-08 re-review 钉死载体根)** GIVEN fog 行表值 (u, tag=∅)(默认行),board_biome 取任意非空集,WHEN 对 (level_index=u) 求 pool,THEN 恒含 fog;**仅当 u>1**,GIVEN 同 board_biome 对 (level_index=u−1) 求 pool,THEN ∅(u=1 时 u−1=0 越出 level_index≥1 域、不测——F1 前置域,2026-09-08 评审修正。原 GIVEN 的 tag≠∅ 推导分支移交 AC-3 夹具腿——该分支在默认表根永不触发(死文本)、在夹具根上 u−1 腿不真(ice 行 u=1 入池),2026-09-08 re-review 修正)。 +- **AC-3(通配与交集,结构性断言;`eligible(v, L, B) := v ∈ pool(L, B)`——F1 谓词的成员判定形式,2026-09-08 评审补定义)** **前置断言:夹具根零诊断加载**(每用例 `reset_diagnostics()` 后加载,防 schema 演进劣化静默——2026-09-08 re-review 增)。第一腿 GIVEN fog.biome_tag=∅(默认行),WHEN 在 **level_index=u(fog.unlock_level 表读值)**以任意非空 board_biome(单/多元素集各一)求 eligible,THEN 恒 true(原句缺 L 参量——L=1 时按字面为假,2026-09-08 re-review 修正);第二腿 GIVEN 夹具根 variation_biome_tag(测试变异行 biome_tag=[ice]、**unlock_level=1 写入夹具规格——防门控腿掩蔽 biome 腿**;本腿同时承载 AC-2 移交的 tag≠∅ 交集推导语义),WHEN 在 level_index=1 以 board_biome={dungeon} 与 {dungeon, ice} 分别求 eligible(v=测试行),THEN false / true。 + +**校验与致命** +- **AC-4(重复键/跨字段·测试通道)** GIVEN 夹具根 variation_dup_key 与 variation_mask_constraint,WHEN `load_tables(root, collect_only=true)`,THEN 错误清单各含重复键类别 / 跨字段校验类别(`hide_numbers⇒safe_nonzero`——2026-09-08 re-review 增)。 +- **AC-4b(致命·独立进程)** GIVEN 夹具根 variation_dup_key、variation_unlock_oob(unlock_level=0)与 variation_mask_constraint,WHEN 各以独立 headless 进程跑游戏通道 `collect_only=false`,THEN 三进程均非 0 退出且错误清单完整(D2 Rule 6 加载期致命——"游戏内真的会死")。 +- **AC-5(域边界,程序构造免夹具;实例化一律走 class_name 直引、不经 load()/preload()——AC-7 扫描豁免条款,2026-09-08 评审增补)** GIVEN schema 实例构造,WHEN `validate()` 作用于 unlock_level ∈ {0, 1000},THEN 均域错误;∈ {1, 999} 均合法。 +- **AC-6(global warning·非致命)** GIVEN 夹具根 variation_global_scope,WHEN 测试通道加载,THEN 诊断收集器 warning 恰一条**且来源类别 = global 语义注记、其余表零诊断**(每用例先 `reset_diagnostics()`;防他表 schema 演进以无关诊断稀释断言——2026-09-08 评审增补);**同夹具游戏通道独立进程以 0 退出**(非致命半句)。 + +**静态与一致性** +- **AC-7(零随机与单点访问)** GIVEN A4 文件集(schema/谓词/冒烟脚本),WHEN 按 D2 AC-9 模式静态扫描(`randomize()`/`RandomNumberGenerator`/ConfigDB 外 data 表加载),THEN 零命中(扫描域扩入 A4;**schema class_name 直引实例化豁免**——AC-5 程序构造前提,承 D2 AC-9 模式)。 +- **AC-8(registry 一致)** GIVEN ConfigDB 加载完成,WHEN 脚本解析 `design/registry/entities.yaml` 的 `variation_table` 条目,THEN 与 ConfigDB VariationTable 8 字段**定义**(字段名/类型/域/knob 标记)逐一相等,**定值断言仅限非 knob 字段;knob 字段(unlock_level/biome_tag)对账口径 = registry 随调参同步或本 AC 豁免其值**(防 EC9 调参经此后门假失败);对账域**显式排除 `mvp_content` 与 `fog_ap_note`**(前者含 knob 活值登记——2026-09-08 re-review);缺任一即失败(仿 A3 AC-10a)。 +- **AC-9(人工·视觉)** 边界可读/双通道/色板变体验收归 art-bible 格子样张测试(AD 门禁),非冒烟域(阈值委托的排期门与违约判定 = systems-index 2026-09-07 门禁增补记录;**art-bible 缺位期以原型 v3 紫色为临时明度基线,就位后强制复测**——2026-09-08 re-review 增)。**操作化口径(2026-09-08 评审增补——"可读"须为可判定谓词,防形容词验收)**:① 形状通道 = 色板无关线稿资产 + 对全部底色的明度对比数值下限(具体阈值 art-bible 定,须存在);② 新增 biome 色板强制重跑样张清单(登记 AD 门禁复测项);③ 样张须含最坏用例:**宽度 1 雾带**(每格即全边界——Rule 3 结构推论下的正常形态)、"旗 × 雾纹理 × 边界"三重叠加格,与**宽度 1 雾带两侧毗邻数字格**(视觉锚 1"纹理不干扰数字辨识"的真实威胁面在邻格——雾格自身无数字,2026-09-08 re-review 增)。 +- **AC-10(人工·字面量)** A4 文件集纳入 D2 AC-14 项目级扫描(人工三要素口径,引用不另设——与 A3 AC-10b 同型)。 + +**悬空引用处置**(qa-lead 发现的 D2 条件性夹具义务首兑现点):counter_item_ids 为 D2 schema 中**首个引用型字段**——MVP 显式声明**不校验**悬空引用(字段恒空、A5 未回填);A5 GDD 回填首个道具 id 时补悬空校验 + 条件性夹具(已登记 Dependencies 节 A5 行)。 + +## Open Questions + +1. **雾面积/数量参数回填与 P1 验证**(owner:A1,第 5 位):BoardGenTable 的雾区域数/尺寸/占比默认值——**须在 Rule 3 结构推论硬边界(厚度≤2【≡任意 3×3 窗口非全雾】 / f≤8d)内定义**,含最低雾出现率与降级率埋点口径;可推理率参数按雾边**双向净效应**建模(寂静减项 × 推理锚增项);雾占比与可推理率的联动曲线(A1 的 P1 生成端保障核心 + 本 GDD 登记 15 项验收的数值侧);**占比参数双重身份声明(2026-09-08 re-review 登记):难度旋钮 × 情报旋钮——雾形携带雷簇位置信息(Rule 5 推理锚),占比/数量调参同时改变"雾 = 雷簇探测器"强度与首遇显著度(总占比/最大区域尺寸下限口径随 BoardGenTable 一并定,防首关雾低于注意阈值致 P4 解锁拍哑火)**。 +2. **探测显影的时效与定价**(owner:A5,第 11 位):EC6 留白——临时/永久显影、道具成本、counter_item_ids 首个回填 + 悬空引用校验补全(D2 条件性夹具义务兑现)。 +3. **创意风险 #1 的 B4 侧兑现**(owner:B4,第 8 位):奖励预知的呈现方案(A4 侧切面已定:雾不额外隐藏内容);雾区奖励在效果当量对照表的落位——**受三条定价硬约束(Interactions B4 行:① 档位下界 ≥ 最低档安全格 buff 当量;② 清图奖励锚随雾成本负担上浮【链不可达同分母口径】;③ 雾板净当量优势带上界——防福利,B4 GDD 验收项)**,成本口径按链不可达雾格计;①的 per-AP 平价与②③的定量谓词归 B4/B1 对账闭合。 +4. **scope=global 语义**(owner:C3,第 12 位):精英/Boss 图全局变异的构成、施加时机、与零特判原则及 cell_constraint 覆盖的相容性重审(Formulas 节 C3 验收项)。 +5. **岩浆/冰冻规则设计**(owner:垂直切片期,随关卡解锁建行):变异框架的下一批居民、P3 雷险维度的承接者;需新字段时走接口漂移规则回写本 GDD 与 D2。 +6. **变异解锁曲线复核**(owner:C3 + D5,低优先):"每关至少引入一个新元素"的推进节奏随 C3 难度曲线定稿复核;D5 教学动态适配表值(EC9),不写死解锁关号。 +7. **标错惩罚与雾内旗**(owner:A2 前原型小补丁,联动 A3 OQ2):若惩罚上线,雾内旗恒受罚的一致性推论纳入实测观察项(EC5);EC5 推论在 A2 原型会话**前置确认**(防 D5 教学返工——2026-09-08 评审增补)。 +8. **已开雾格 tint / 雾区全开完成信号的裁决**(owner:D1 + CD,deadline:D1 GDD 前):现处"验收候选"悬空态(无 owner 无裁决时点,2026-09-08 re-review 登记本 OQ 终结悬空);裁决须同时覆盖:与 0 格的可判定区分(0 格 = "八邻无雷"推理信息,视觉混淆污染推理——tint 为当前唯一区分手段)与 EC11 合并呈现下的完成信号粒度(视觉合并域 = 信号单元,禁实例级双响)。 diff --git a/design/gdd/legacy-2026-09-13/b1-settlement.md b/design/gdd/legacy-2026-09-13/b1-settlement.md new file mode 100644 index 00000000..def6580d --- /dev/null +++ b/design/gdd/legacy-2026-09-13/b1-settlement.md @@ -0,0 +1,675 @@ +# B1 战斗结算规则(Combat Settlement Rules) + +> **Status**: Designed(2026-09-10 一次会话定稿;lean 门禁:systems-designer 起草 Formulas〔F0–F5 + 对账义务清单〕· qa-lead 起草/验证 AC〔43 条 + 覆盖矩阵 + 缺口预检 12 项〕已咨询;CD-GDD-ALIGN 按 lean 跳过,生产前人工复核 Player Fantasy;art-director 按 lean 跳过,生产前人工复核 Visual/Audio 与 art-bible 一致性。**F0 归一口径裁决已定**〔瞬时当量 + m_w 值域 [60,85]/默认 −65——B3 红警裁决落定,回执走接口漂移〕· 条款①③⑤闭合首版验算完成〔actuals p10 交付后强制重跑〕· 高光双落〔#2 完美排雷 P_perfect=×2 / #3 标中旗高光标记〕· 23 条上游义务全落位) +> **Revision 1**(2026-09-10 /design-review NEEDS REVISION → 同日修订批:8 blocking 核销——①完美排雷 ×2 落真实双份交付载体〔用户拍板〕+ 高光行有效兑付语义/零兑付降级 ②条款⑤计价模型重推为挖穿残余池口径、verdict 翻转为未闭合→C3/B4 约束包 + 多敌验算腿 + ι→1 域守卫 + 护栏 B 输入登记与双读数 ③流水协议补 target/effect_id/pool_value 字段分层 + enemy_action/flag_miss 来源枚举 ④hit=false"标旗未中"零值行(每雷实例一次去重)⑤P_perfect ÷100 换算成文 + 载体域收窄 {100,200} ⑥AC-18 腿 2 改写 ⑦F4 V_safe 标签更正 + 期望口径敏感性读数 ⑧clamp 字段统一八字段;recommended 21 项中 19 项留后续批次;日志 `reviews/b1-settlement-review-log.md`) +> **Author**: user + agents +> **Last Updated**: 2026-09-10 +> **Implements Pillar**: P2(主——"扫雷的因、战斗的果"的兑现面)· P3(雷的双面兑付)· P1(条款③不等式 = 掌控感的经济侧验收) + +## Overview + +B1 是战斗域的**编排与结算中枢**:轮次生命周期的唯一 owner——A2/A3 的 `round_end`(双发射源:A2 棋盘路径 + A3 停手路径)到达后,由 B1 在 **Waiting 相位编排点**触发结算(不依赖事件到达序),按**段序骨架**驱动全部战斗域系统完成一次兑付:**授予段 → 道具使用窗口 → 增益/减益段 → 道具段 → 战斗段 → 衰减段 → 判定段**;判定产出三种结局(敌人全灭 → 胜利 / 主角死亡 → 失败 / 否则 → 下一轮次),并向 A3 回发 `next_round_mode` / `battle_over`、向 A1 发起换图生成、编排挖掘期弃图刷新(`refresh_board`——A3 扣费 + A2 retire + A1 generate + A2 install + B4 roll_board,2026-09-12 D4 修订;原结算编排期 Waiting 刷新代理作废)。它是全项目唯一有权调用 B2/B3/B4 战斗域接口的编排方,也是 A3 F1 五条款经济契约的验收方与条款①③⑤的**单一闭合对账 owner**。 + +**双层口径**:对内,B1 是纯逻辑编排账本——零随机(`rng_service` 流封闭不含结算流)、零 UI 依赖、结算逻辑禁 await/动画,输出契约为**可回放步骤流**(同种子同动作序列 ⇒ 结算步骤流逐行相同——D2 AC-15"战斗级可回放"deferred→B1 的兑现义务);对外,它是 MVP 战斗体验的**本体**:战斗动画归 B5(垂直切片),当前每轮结束时玩家面对的结算明细屏就是战斗本身——扫雷的因(挖格/标旗/停手)在这里逐行兑现为战斗的果,每行携带来源标注(伤害道具 / 安全格增益 / 标中旗削弱 / 清图奖励 / 雷→伏击),P2"可归因"的呈现半边由这份流水直接承载。 + +边界(B1 不拥有什么):单位状态与行为环归 B2、状态实例容器归 B3、奖励内容与 roll 归 B4、行动价目归 A3、棋盘面规则归 A1/A2、结算明细的呈现归 D1/B5。B1 拥有的是:段序、次序、判定时机、伤害公式的消费侧语义(clamp/地板/换算方向)、明细流水协议,以及全部上游义务账本(B3×8 / B2×6 / A2×3 / B4×6)的兑付落点——没有 B1,三套战斗域 API 面全部失联,胜负没有判定者,A2/A3 的事件没有消费者,P2 的兑现没有现场。 + +当前内容态注记(2026-09-11 用户裁决改写):主角伤害来源 = **基础攻击(HeroTable.atk 域 [1,999]、0 加载拒绝——恒在)+ B4 奖励侧增量(刀/attack_up 等)**;胜负判定消费 B2 存活集素材,接口就绪与胜利路径存在是两件事——本 GDD 战斗段含主角先手攻击步(Core Rule 10 / F3 ⓪)。 + +*(实现口径注:编排器类结构、事件总线载体〔autoload 单例 vs 直连——TD 门禁 2026-09-07 登记〕、步骤流的落地形态不在本 GDD——实现期 ADR 域;本 GDD 只定行为契约与次序。)* + +## Player Fantasy + +**直接承载**(Section A 定框答案:玩家不操作 B1 的任何编排逻辑,但每一轮都直接面对它的工作成果——结算明细屏)。B1 的情绪目标一句话:**"这一轮的一切,都是我算出来的"**——轮次结束、结算展开的那一刻,玩家看到的不是系统在打架,而是自己前几轮扫雷决策的逐行回放:第三轮挖到的刀正在削它的血(这一行是我挖的)、标中的旗还在削弱它(这是我赌对的)、上一轮踩的雷喂了它伏击(这是我贪的代价)。P2"扫雷的因、战斗的果"在 B1 从口号变成每轮一次的可见仪式。 + +体验锚点(按支柱): + +- **P2 可归因的兑现现场**:结算明细逐行携带来源标注(伤害道具 / 安全格增益 / 标中旗削弱 / 清图奖励 / 雷→伏击)——P2 裁剪判据"玩家能说出这个结果是我哪步扫雷带来的"的**最终兜底在 B1 流水协议**:若一行明细说不出来源,即 B1 失职(呈现归 D1/B5,数据源与协议归 B1)。 +- **P1 掌控感的经济侧信任**:"挖下去是值得的"——挖掘决策 EV 健康不等式(A3 F1 条款③)由 B1 闭合签字,玩家不需要知道不等式,但每一轮"继续挖比收手好"的隐性判断都踩在这份对账上;条款⑤(清图支配性)守的是"推到底"不会在数学上吃亏。 +- **P3 雷的两面同框**:敌方伏击行与标中旗削弱行在同一份明细、同一种呈现语言——玩家由此学会"雷是赌桌不是陷阱":我贪它就吃壮,我标中它就被拆。 +- **判定的可归因**:胜利/失败不是随机裁决——"它死了是因为我的刀攒够了"(抢杀窗口兑现)、"我死了是因为那一轮喂太多"(灾难轮代价),判定素材(存活集/HP)全部可回溯到此前明细行。 +- **高光兑现节点(情绪需求登记,机制裁决归 D 节检查点)**:game-concept 创意风险 #2("我算对了"缺爆发结算)与 #3(chord 缺席的标中旗节拍缺失)的机制本体归 B1 强制检查点——本节只锚定情绪目标:玩家需要"完美排雷被加倍兑现"与"标中的一瞬立刻有回报"两类高光节拍,缺失则长战斗退化为匀速流水账。 + +参考感受:杀戮尖塔的逐卡结算反馈(打出去的每张牌的效果逐个可见——延迟满足的兑付面);Into the Breach 回合解析的因果清晰。**反向对照**:game-concept 已识别风险 #1 的病灶本身——"自动战斗沦为无聊过场"——B1 的明细流水就是那个"兑现层必须可见、有反馈"的答案;若结算被做成黑箱数字雨,本作的 agency 前移设计即告失败。 + +**反向护栏**:结算明细绝不能有无来源的行——每行必须能回答"从哪来"(P2);判定绝不能不可归因(P1);敌我两侧的增减益必须同构呈现(P3)。任何让明细"合并到看不出来源"的呈现优化,须先确认归因链在数据层仍可还原(行协议归 B1,合并呈现归 D1/B5 裁决)。 + +> 注:`creative-director` 未经咨询——Lean 模式(仅 D/H 两节委托专家),生产前请人工复核本节情绪定位。 + +## Detailed Design + +### Core Rules + +1. **编排唯一性(调用权集中,B3/B2/B4"授予集中在编排"同款原则的总端点)**:B1 是战斗域全部跨系统调用的唯一发起方——B2(spawn/query_*/apply_damage/advance/despawn_all)、B3(notify_battle_start/apply_effect/query_active/decay_round/notify_battle_end)、B4(roll_board/grant/grant_clear/stock/use/query_inventory/clear_inventory/retire_mapping)、A3(`refresh_board` 编排内 `request_refresh` 调用〔D4 2026-09-12〕 + notify_settlement_done/notify_combat_end 回发)、A1(generate 换图编排)、A2(install_board/retire_board 调用方)。A2/B3/B4 零相互直调(各 GDD"零直接接口"声明的对侧);D1 经 `settle_batch` 提交意图、不直调 A3(A2 D1×6① 的对侧确认)。B1 自身**零随机**(`rng_service` 流封闭不含结算流;若未来需结算随机,走 D2 Rule 5 增流通道 + P1 评审——MVP 无此需求)、零 UI 依赖(headless 债)、结算逻辑禁 await/动画(可回放步骤流的承重约束)。 +2. **结算触发 = Waiting 相位编排点(A2 义务①落位)**:B1 的结算触发条件 = 轮次结束事务达成(`path_tag` 可用),**不依赖事件到达序**——stopped 轮 A3 直发 `round_end` 与 A2 `flag_judged` 批的到达先后不承载语义(事件序契约 `flag_judged` < `round_end` 全路径统一,仅为数据完整性服务)。实现载体(事件订阅 vs 相位轮询)归实现期 ADR;行为契约 = 触发判定只读 A3 相位观测量与 `last_round_end`。 +3. **轮 facts 批处理收集**:B1 订阅三事件流(A2 事件面 referenced_by 含 B1),按轮缓冲 `cell_revealed ×N` 与 `flag_judged ×N`,**轮末统一转化**(与 A2"轮末统一判定"同构——不在挖掘期即时写任何战斗域容器)。处理序 = 事件规范序(A2 发射序:cell_revealed 行主序 BFS 序、flag_judged 行主序)。**事实→授予恰一次转化是 B1 编排义务(B3 义务② / B2 义务② / B4 义务②——B3 不去重、B4 幂等仅兜底)**:每格受理、每雷转化、每旗判定各恰一次,由 B1 的轮 facts 缓冲保证(一事件只进一次转化)。 +4. **段序骨架(B4 义务①落位——用户 2026-09-09 拍板原文)**:每轮结算按固定段序解析——**授予段(含 grant_clear 兑付)→ 道具使用窗口(授予段后、增益/减益段前)→ 增益/减益段 → 道具段 → 战斗段 → 衰减段 → 判定段**。核心序拍板不变:buff/debuff > 道具 > 战斗。段序为**结构冻结**(修订走 GDD 流程非调参);段内细则归本 GDD 各段规则。**窗口选择序列 = 结算回放的输入事件**(可回放步骤流的两类输入 = 意图序列 + 窗口选择序列)。 +5. **授予段(次序规则 = B3 义务①"确定次序"的 B1 定稿)**:三批依次转化,批内按事件规范序—— + 1. **cell_revealed 批**:逐格受理——`kind=mine` → 伏击转化(见规则 6);`kind=safe ∧ reward_eligible=true` → 调 B4 `grant(cell)`,返回快照分流:**buff 类**调 B3 `apply_effect(effect_id, source={safe_cell, origin_cell, round_index})`;**即时类(instant_aoe / instant_heal——2026-09-11 两级奖励裁决)当场兑付**:`instant_aoe` = 对全体存活敌各 `power` 点(逐个 `apply_damage`、受击即死),`instant_heal` = 主角回复 `power`(clamp,满血 → "奖励未生效"行零兑付)——不调 stock、不进库存、不出窗口;**道具类**调 B4 `stock(item)` 入关卡域库存。**`reward_eligible=false`(initial 通道)格不入奖励库(A2 义务②落位——A3 EC5 委派闭环)**;`channel` 不入 B1 判定(eligibility 字段已由 A2 封装,零特判)。**每格受理恰一次**(B1 轮 facts 缓冲承载;B4 E18 幂等为编排瑕疵兜底)。满层拒绝(apply_effect 返回 false)= 零兑付 + "奖励未生效"明细行(无折算——用户拍板),**行携带可聚合计数/批次字段(B4 义务②)**;连锁批量授予溢出 = **显式接受**(B1/B4 共裁完成——B4 E3 已裁显式接受,B1 侧同款承接,无转换补偿)。 + 2. **flag_judged 批**:逐旗判定——`hit=true ∧ 该雷格实例资格未消费` → 调 B3 `apply_effect(weaken, source={flag_hit, origin_cell, round_index})` + 资格消费入集合;`hit=true ∧ 资格已消费` → **去重跳过(A2 义务③落位——A2 诚实重复上报的对侧)**,零明细行(重复兑付从未发生)或 debug 诊断;`hit=false` → 零兑付 + **"标旗未中"零值行**(source=flag_miss、value=0、可聚合计数字段、highlight=`perfect_broken` 标记——本板完美谓词已破的归因锚,Core Rule 13#2;**去重语义 = 每雷格实例一次**,与兑付资格同键——A2 逐轮对在场旗重报 hit=false 的重复上报零新增行,防逐轮惩罚轰炸)。标错惩罚未上线(A2 OQ1 实测后走接口漂移补惩罚语义——本行是归因痕迹非惩罚,预留注记保持)。**兑付资格以雷格实例为键,B1 持有已消费资格集合(战斗域跨轮累积)**;换图后新图新雷 = 新实例,资格自然重置(A3 Rule 8)。**标中旗兑付明细行携带高光标记(创意风险 #3 落地——数据面,呈现归 D1)**。 + 3. **cleared 轮清图奖励(B4 义务③落位——B3 OQ4 关闭条件)**:调 B4 `grant_clear()` → 预分配身份兑付,分流同上(source=clear_reward;B3 source_class 枚举已含)。兑付后同结算窗口即可选用(窗口锚点收益面)。**完美板(Core Rule 13#2)本段追加第二份同身份兑付**(道具类再 `stock` 一次 / buff 类再 `apply_effect` 一次——第二份走既有授予通道,`grant_clear` 本身仍恰一次)。**若清图槽为 buff 行且满层** → 零兑付 + "奖励未生效"行(合法例外,B4 E11 拍板对侧);完美板高光行降级不发(Core Rule 13#2 降级规则)。 +6. **伏击转化与 cap 预截流(B2 义务②⑥落位)**:`mines_r` = 本轮 cell_revealed 批中 `kind=mine` 计数 → 调 B2 `ambush_grant_plan(mines_r, cap)`(cap = EnemyTable 行值快照)→ 前 `granted_r` 颗雷(事件规范序——B2 建议序采纳)逐颗调 B3 `apply_effect(ambush, source={mine_hit, origin_cell, round_index})`;`overflow_r` 颗逐颗产"溢出未生效"明细行(含来源)。**每颗雷恰一次转化**(B1 缓冲 + B2 计划面双保险)。伏击池 L 为 side 共享(B2 F2),dur=1 → 本结算读值恒 = granted_r,轮末衰减清零。 +7. **道具使用窗口(B4 义务④前半落位;单件制 2026-09-11 用户拍板)**:授予段完成后打开——**窗口数据组装 = B4 `query_inventory` 投影 + B1 侧守卫预判 `usable_now`**(满血治疗包禁选——B1 读 B2 hero hp 判定,B4 零跨系统读纪律不破;伤害道具在窗口时点恒可用——结构推论:敌死亡只发生于道具段,窗口打开时存活敌 ≥1)。**玩家至多选用一件或跳过**(2026-09-11 用户拍板"先不需要多选"——原"任意多件 + 同窗顺序逐件动态重判"作废;>1 件的选择提交 = 编排违例,取首件 + 诊断)。零选择 = 跳过恒合法。窗口期选择收集与棋盘输入不竞争(单意图帧先例);呈现(窗口形态/时机指示/usable_now 禁选态/空库存不弹窗政策)归 D1(D1 义务②)。**窗口选择序列登记为结算回放输入事件**。 +8. **增益/减益段(B3 义务③⑤⑦落位)**:调 B3 `query_active(hero)` / `query_active(enemy)` 读值(插入序;六归因字段 + 授予时快照投影)→ 经 **SettlementTable 效果语义映射**(effect_id 为键——percent→乘数换算方向与合成值地板的成文落点在 Formulas F2)合成到攻防算式 → 逐条产效果明细行。**结算明细按授予流水逐条标注来源(B3 义务⑦——合并实例只持首授归因字段,per-layer 来源还原的数据源 = B1 授予流水;B1 按 apply_effect 调用流水展开,非按容器实例)**。敌方侧语义 = side-wide(全体存活敌单位——B3 单敌假设回验后的多敌兼容读法;target_ref 单体寻址扩展位上线时随批补归集语义——B3 接口漂移条款的 B1 随批义务)。 +9. **道具段(B4 义务④后半落位;单件制 2026-09-11)**:窗口选定件(至多一件)生效——**伤害道具**:D = power(恒定表值,不吃任何 percent 修正——B4 F4 拍板对侧),目标 = B2 `default_target()` 固定顺位最前存活敌,调 B2 `apply_damage(u, D)`,击杀结果(该敌 hp=0 → Dead)**进流水(H3 抢杀窗口感知数据前置)**;**治疗包**:满血守卫(读 hero hp,满 → 拒绝不烧包 + "使用被拒"行),`hp' = min(hp + power, max_hp)`(clamp 归 B1)。**零效果判定读 B2 存活集**(选定刀施加时已无存活敌——前序刀全灭情形:**照常消耗**(Used 不可逆)+ "已消耗未生效"明细行——B4 E10 拍板的 B1 侧承接,守卫所有权不对称:满血守卫是表局部判定、零效果判定须读 B2 存活集)。产道具明细行(含来源 {cell, round} 归因)。 +10. **战斗段(B2 义务①③⑤落位;2026-09-11 用户裁决增补主角先手攻击)**:段首**主角先手攻击**——对 `default_target()`(存活敌行主序最前者,与道具段同源)施加 **D_hero = HeroTable.atk ×(1 + attack_up 载荷)**(attack_up = 唯一吃修正的攻击增益——percent 池加算/clamp/地板走 F2/F3 既有语义;"道具伤害恒定表值不吃修正"的拍板不变,刀仍恒定表值);算式见 F3 ⓪;击杀 → 死亡即不动手(受击即死同款);产伤害明细行(**source_class = hero_attack**——2026-09-11 新增来源值,接口漂移随批登记,target 字段照常)。存活敌清空 → 敌方循环空转(零行动行)径入衰减段。随后对每个存活敌单位(行主序生成序)执行循环,**结算内调用序钉死:`query_attack` → `apply_damage` → `advance`**(倒序读到下一拍——B2 义务①;**每结算每存活敌恰一次 advance,hero 不在推进域**——主角无行为环,环接口对 hero 句柄拒绝): + - `query_attack(u, L, m_amb)` → 分量二元组 {baseline, attach}(L = 本结算伏击池层数读值——授予段后的当轮值);`charge` 拍 → 零作用,产"蓄力"明细行(数字兑现面——玩家把观察到的姿态与明细对上); + - `attack`/`heavy` 拍 → 敌方伤害 = F2 构成(后置加法),经 hero 侧减伤合成(SettlementTable 语义映射,Formulas F3)→ clamp/地板(Formulas)→ 调 B2 `apply_damage(hero, D')` → 产伤害明细行(分量二元组 + 减伤后终值——P2 归因的敌侧兑付面); + - **死亡判定时机(B2 义务③)**:敌 hp=0 在受击时即 Dead(道具段同款——死亡即不动手);**hero hp=0 → 战斗段立即短路中止**(剩余敌单位本结算不再作用、不再产行动明细行——用户拍板:死尸挨打行是归因噪音),径入判定段; + - 循环尾调 `advance(u)` 恰一次。 + 伏击池在敌作用中**共享不消耗**(同结算各敌读同一 L——B2 F2)。 +11. **衰减段(B3 义务④⑥落位)**:调 B3 `decay_round()` **恰一次,恒执行、含末轮**(先兑付后衰减的时序契约落点——dur=1 一次性语义的先决条件;最后一轮判胜后状态由 `notify_battle_end` 全清兜底,两种次序同终态,但次序钉死为段序骨架 = 衰减在判定前)。返回移除数 k 记入步骤流(衰减明细行聚合呈现归 D1 裁决)。 +12. **判定段(B2 义务③死亡时机 + game-concept 轮次流程 4)**:**hero hp=0 → 战斗失败**(防御性优先级先判——结构上敌我同结算双死不可达:我方伤敌在道具段与战斗段主角先手攻击〔均先于敌方行动兑付〕、敌方伤我全在主角攻击之后——敌行动时点若已敌全灭则先判胜;推理随 2026-09-11 主角先手攻击更新,本腿为纵深防御);否则**存活敌集 = ∅ → 战斗胜利**;否则 → 下一轮次。胜负判定明细行(终局行)。battle_over 触发清理编排(见编排专章)。 +13. **高光兑现节点(强制检查点——创意风险 #2/#3 机制本体,用户 2026-09-10 拍板双落;同日评审修订:×2 落真实双份交付载体——用户拍板)**: + - **#2 完美排雷(perfect_sweep)**:板级判定器——本板自 install 起全部 `flag_judged` 中 `hit=false` 计数 = 0 **且** `hit=true` 计数 ≥ 1 → 该板 cleared 轮的清图奖励兑付时追加"完美排雷"高光明细行 + **清图奖励身份第二份兑付(运行时真实双份——用户 2026-09-10 评审拍板)**:道具类身份 → B4 `stock(同源身份快照)` 第二次调用(玩家实得两件,如 clear_knife ×2);buff 类身份 → B3 `apply_effect(同身份)` 第二次授予(L+2,满层拒绝语义照旧——第二份被拒产"奖励未生效"行)。`grant_clear` 本身仍恰一次(B4 义务③不受影响——第二份走 stock/apply_effect 授予通道,第二份兑付恰一次 = B1 编排义务,可回放确定性函数)。**B4/B3 零接口修订**(两次调用均在既有受理域内)。当量语义 = F1 对照表 clear 行 × P_perfect÷100(**换算 ÷100 成文**;p_perfect 运行时载体域 = **{100, 200}**——100 = 仅高光无第二份、200 = 双份交付;非整数倍数乘数无运行时载体,域外值加载致命,Tuning Knobs 校验行)。**高光行 value = 实际有效兑付当量**(首份 + 第二份被接受部分;禁记名义值——"数字诚实可归因"反向护栏);**降级规则:清图槽首份即满层零兑付 → 高光行不发**(庆祝不打在空兑现上;完美谓词仍按板级事实成立,仅无兑付面)。零特判自然成立:雾内旗恒不中(A4 EC5)→ 雾板插雾旗计入误标(教学已警示"雾内插旗纯损",无变异分支)。 + - **#3 标中旗高光标记**:规则 5 已落(weaken 兑付行 highlight 标记);其即时节拍 = 标中旗当轮即兑付(weaken dur=1 本就当轮生效)+ 数据面标记供 D1 强调呈现——chord 缺席的兑付节拍缺失以"当轮兑付 + 明细强调"组合补偿,深度形态(即时 HUD 反馈动画)归 D1/垂直切片。**高光使用规则一句话:事件型高光(完美排雷)用独立高光行、属性型强调(标中旗/清图奖励 H2)用行字段标记**。 +14. **明细流水协议(输出契约——D1/B5/D4 消费面)**:步骤流 = 明细行序列 + 窗口输入事件标记,零 await 顺序写入。**行类型封闭枚举 13 类**:`效果行`(buff/debuff 兑付——effect_id/来源/单次授予贡献值/池合成值)、`伤害行`(分量二元组 + 减伤终值 + 击杀标记 + **target 目标字段**)、`治疗行`、`道具行`(入库/生效/语境注记;生效类携带 **target 目标字段**)、`奖励未生效行`(聚合计数字段)、`已消耗未生效行`、`使用被拒行`、`溢出未生效行`(伏击族;奖励族同名协议——B3 义务⑧/B2 义务⑥协议统一)、`标旗未中行`(零值归因行——Core Rule 5.2,可聚合 + perfect_broken 高光标记)、`蓄力行`、`衰减行`、`判定行`(胜负/继续)、`高光行`(完美排雷;清图奖励行 H2 强调标记由行字段承载)。**字段分层声明(公共 vs 行类型特有)**: + - **公共字段(13 类全必有)**:`type / source_class / origin_cell / round_index / display_name / value / highlight?`——P2 归因的字段落点。`origin_cell` 板级行(判定行/高光行/衰减行/清图奖励行)**可空**(空 = 板级事件,无格子归因);`highlight` 可空枚举,值集 = `{flag_hit, clear_reward_h2, perfect_broken}`(新增值 = 接口漂移)。 + - **行类型特有字段**:伤害行/道具行(生效)→ **`target`**(目标单位句柄 + display 名——"打到了谁、谁打的我"的显式数据源,禁消费方按行序重演 B2 目标状态机;多敌归因的字段级保障);效果行 → **`effect_id`**(D1 icon 映射与极性判别的键——B3 D1×4②③ 的桥,display_name 本地化后反查失效的防御)+ **`pool_value`**(池合成值);效果行/伤害行的 `value` 语义定死:**效果行 value = 该次授予的贡献值(L×m 投影)**,池合成值入 `pool_value`(同轮两旗各显贡献值 −65、pool_value = −130——防"−130% × 2 行 = −260%"误读;伤害行 value = 减伤终值 D' 不变);未生效四类 + 标旗未中行 → 可聚合计数/批次字段(既有)。**新字段均为输入的确定函数——回放逐行相等断言(AC-33)语义不受损**。 + - **source_class 封闭枚举 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack}**——`enemy_action` = 敌方行动行(战斗段伤害行/蓄力行)的来源标注(原枚举无值可填的缺口补齐);`hero_attack` = 主角先手攻击伤害行(2026-09-11 用户裁决新增——基础攻击恒存在的 P2 归因来源值,接口漂移随批登记);新增值 = 接口漂移,同步 source_label_map 键集。**未生效类行携带可聚合计数/批次字段**(合并呈现归 D1/B5 裁决,数据必须可聚合)。**无来源行 = 断言失败**(AC-42——P2 归因兜底)。本协议为 D1 各呈现义务清单(A2×6/B2×4/B3×4/B4×5)的统一数据源契约。 +15. **可回放性(D2 AC-15 deferred→B1 的兑现义务)**:同 master_seed 同意图序列同窗口选择序列 ⇒ 结算步骤流**逐行相同**。确定性来源:零随机(规则 1)+ 确定性输入(A2 事件流零随机 + 窗口选择为回放输入)+ 表值快照(B2 行值快照/B3 授予快照/B4 授予快照——热重载撕裂已被三侧快照纪律阻断)+ **attempt 边界非 scope 事件**(A1 F5 注记④——B1 编排不把生成重试当取号事件)。 +16. **数值承载于 D2(SettlementTable,B1 回填 owner——D2 Core Rules 占位行的兑现)**:SettlementTable(单行全局)承载段序字段名/来源标注映射/换算方向与地板规则标识(schema 定稿见 Tuning Knobs 节);B1 代码零玩法数值字面量(D2 Rule 1)、读取经 ConfigDB。**debug 热重载门:战斗开始 `set_reload_gate(true)`、battle_over 复位(D2 EC8——B1 是置位/复位唯一调用方;"战斗中真的被置位"的验收落点归本 GDD AC,D2 AC-16 只测门机制)**。 + +### 轮次生命周期编排(Round Lifecycle Orchestration) + +B1 是轮次节拍的**编排权威**(A3 States 表"编排权威序列以 B1 本章为准"的兑现)。可观测相位域复用 A3 {Idle, Active, Waiting};RoundStart/RoundEnd 为事务内瞬态(不作 GIVEN——A3 纪律同款)。 + +**战斗开始序列**(入口 = `begin_battle(encounter)`;encounter 上下文 (level_index, node_depth, hero_hp) 归 C1 供给〔未设计——MVP 冒烟直供,provisional〕): +① `set_reload_gate(true)`(D2 debug 门)→ ② B3 `notify_battle_start()`(容器开域)→ ③ B2 `spawn(L, D, hero_hp)`(行值快照实例化)→ ④ A2 `install_board(A1.generate(L, D))` + **B4 `roll_board(资格格清单)` 恰一次(B4 义务⑥——install 后、挖掘前;资格格清单 = S ∖ R₀,B1 从 A2 产物读取)** → ⑤ A3 `notify_round_start()` → Active。 + +**每轮结算序列**(Waiting 相位编排点触发——Core Rule 2): +① 轮 facts 收集(Core Rule 3)→ ② 段序骨架解析(Core Rule 4–12;窗口段暂停等玩家输入——窗口选择序列入回放)→ ③ 出口分发: +- **继续**:A3 `notify_settlement_done(same_board)` → `notify_round_start()`(同图续轮); +- **换图(new_board)**:A2 `retire_board()` + B4 `retire_mapping()` → A1 `generate(L, D)`(**attempt 边界非 scope 事件**)→ A2 `install_board(产物)` → B4 `roll_board(...)` 恰一次 → A3 `notify_settlement_done(new_board)` → `notify_round_start()`。**触发条件钉死(A3 契约义务——违约 fail-fast)**:`cleared` 轮恒走 new_board;`stopped`/`ap_exhausted` 轮**恒 same_board**(2026-09-12 D4 修订:刷新决策前移挖掘期——挖掘期弃图刷新走 **`refresh_board` 编排**〔玩家经 D1/组合根发起 → A3 `request_refresh()` 扣费 → 本换图全序,**无结算段、无 round_end、轮次不终结**〕;原 Waiting 期玩家刷新意图代理调用作废),回发 new_board 于 stopped/ap_exhausted = B1 违约(不变)。刷新图不产生清图奖励(A3 Rule 11——对挖掘期当场刷新同样成立:弃图非清图)。 +- **战斗结束(battle_over)**:B4 `retire_mapping()`(映射 = 板级、随战斗消亡——下一场战斗新板新 scope 批)→ B2 `despawn_all()` → B3 `notify_battle_end()`(状态全清)→ A3 `notify_combat_end()`(→ Idle,RAP 清零)→ `set_reload_gate(false)`。各调用**恰一次**(重复调用在各侧为拒绝 + 诊断——B1 编排纪律承载,幂等守卫兜底)。**库存不清**(2026-09-11 关内延续——库存关卡域跨战斗延续;原"battle_over 调 clear_inventory + 清空反馈行"作废)。 +- **关卡结束(level_clear——2026-09-11 新增编排入口;关卡结构归 C1/C2,落位前由上层显式调用)**:B4 `clear_inventory()` 恰一次(**清空前快照计数返回调用方**——"剩余道具 ×N 随关卡结束清空"反馈行〔D1 义务⑤〕/ 关卡末折算结构位〔OQ4 残余,C2〕的数据面)。victory(末层通关)与 defeat(关卡失败)皆然。 + +**回放切点**:快照语义 = master_seed + 各流 scope 计数器(D2 契约)+ 意图序列 + 窗口选择序列;scope 事件边界 = board install(A1 取号)/ board install(B4 取号)——B1 编排保证无在飞流实例跨界(D2 快照切点契约的消费侧遵守)。 + +### States and Transitions + +B1 战斗编排生命周期(容器级——B3/B2 状态容器跨图延续,由本表编排;战斗域生命周期 = 本机生命周期): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Uninit | 冷启动 / 战斗清理序列完成 | 唯一出口 `begin_battle(encounter)` | +| BattleSetup | `begin_battle` 受理 | 执行战斗开始序列(Core Rule 16 门置位 → 三容器开域 → 首图 install+roll)→ RoundActive;任一步拒绝 = fail-fast 上抛(编排违约显式暴露,不静默) | +| RoundActive | A3 `notify_round_start` 受理(A3 Active) | 不干预挖掘——A2 受理动作、A3 记账;B1 只缓冲事件流 | +| RoundWaiting | 轮次结束事务达成(A3 Waiting) | 执行每轮结算序列(facts 收集 → 段序 → 出口分发);窗口段为唯一玩家输入点 | +| BattleOver | 判定段产出胜/负 | 执行战斗结束序列 → Uninit | + +**编排不变式**:① 同一时刻至多一个结算事务在飞(窗口为事务内暂停点,非并发入口);② 段序恒全序执行(战斗段短路是段内规则、非跳段);③ 全部跨系统调用集中在 B1(Core Rule 1)——任一下游被 B1 以外语调用 = 架构违约(静态扫描域,Acceptance Criteria 节)。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| D2 | 双向(hard) | SettlementTable 回填(本 GDD owner,单行全局)+ ConfigDB 读取 + `set_reload_gate()` 唯一调用方(战斗开始置位/结束复位——D2 EC8 B1 落点,AC 节验收);零结算随机流(流封闭不变) | +| A2 | 下游 hard | 三事件流订阅(facts 缓冲);`install_board`/`retire_board` 调用方(换图编排);Waiting 相位观测量 = 结算触发条件;事件到达序不承载语义(A2 义务①);initial 格不入库由 eligibility 字段封装(A2 义务②) | +| A3 | 下游 hard | `request_refresh()` 于 `refresh_board` 挖掘期刷新编排内调用(玩家经 D1 发起——A3 Rule 11 玩家意图路径;D4 2026-09-12 前移挖掘期);回发 `notify_settlement_done(next_round_mode)` / `notify_combat_end()`;`last_round_end` 读取;**A3 F1 五条款验收方**(条款①②④⑤对账落 Formulas——B1 单一闭合 owner) | +| A1 | 下游 hard(间接接线) | `generate(L, D)` 换图编排调用方;**消费 actuals 分布(F4 报告 C p10)而非 target 参数(条款③⑤对账输入——A1 登记口径)**;attempt 边界非 scope 事件(编排注记);**EC5 零合法板玩家侧诚实呈现的 B1 半边**:非法产物标记 → 上抛呈现请求(提示/补偿/回退的形态归 D1 裁决——数据面 B1 供,登记 D1 义务) | +| B2 | 下游 hard | `spawn/query_alive/query_beat/query_attack/apply_damage/advance/default_target/ambush_grant_plan/despawn_all` 全调用面;调用序 query_attack→apply_damage→advance(B2 义务①);死亡判定时机(义务③——Core Rule 10);条款③闭合(义务④——Formulas);目标分流(义务⑤);伏击溢出行协议(义务⑥) | +| B3 | 下游 hard | 五 API 全调用面(编排期授予/兑付读值/轮末衰减/生命周期);义务①–⑧全落位(Core Rule 3/5/8/11 + Formulas F1/F2);**weaken 锚红警裁决 owner(Formulas 检查点——OQ1 归一口径前置)** | +| B4 | 下游 hard | 十 API 全调用面(roll_board 恰一次/grant/grant_clear/stock/use/query_inventory/**clear_inventory〔关卡末 level_clear 编排——2026-09-11〕**/retire_mapping/equivalence_supply);义务①–⑥全落位(Core Rule 4/5/7/9/编排专章;**2026-09-11 两级奖励批:授予段即时类当场兑付 + 窗口单件制 + battle_over 清库改关卡末**);usable_now 组装与守卫预判(义务④);关卡末清空快照计数(义务⑤) | +| D1 | 数据供给(hard,未设计——第 10 位) | 结算明细流水 = 统一数据源契约(Core Rule 14 行协议);**承接呈现义务台账**:A2×6(格态/悬停/voided/拒绝可感知等)、B2×4(血条/顺位/线索映射/伏击池)、B3×4(HUD 状态栏/icon/极性/全清去留)、B4×5(窗口/四类行/H2 强调/清空反馈)、B1 新增(高光行强调/完美排雷呈现/EC5 零合法板提示——本 GDD 登记,缺项即 blocking 随 D1 门禁评审) | +| B5 | 数据供给(soft,垂直切片) | 结算步骤流 = 战斗动画的回放脚本源(B5 ← B1 依赖的兑现);溢出行/origin_cell 可读形态呈现(B3×B5×2 义务的数据源) | +| D4 | 事件供给(soft,Alpha) | 音频挂点 = 明细行类型(拾取/生效/负反馈聚合——B4 音频挂钩点的事件源即 B1 流水行) | +| D5 | 口径供给(soft,Alpha) | 教学口径来源(结算来源标注/RAP 清零/库存清空/满层溢出——各上游教学条目的数据源经 B1 流水) | +| C1/C2 | 上游(provisional,未设计) | 遭遇上下文 (L, D, hero_hp) 供给(begin_battle 入参);跨战斗 HP 持久性归 C1/C2(B2 hp_persistence 对侧——MVP 冒烟以 max_hp 入参);战斗触发(节点移动碰非奖励点)归 C1 | +| C3 | 下游(soft,provisional) | 条款③⑤对账结果 = C3 调参约束输入(cap/atk 曲线 × B1 对账联动——B2 Tuning Knobs"禁止独立调参"的对账方);P_perfect 乘数调参所有权归 C3(provisional) | + +## Formulas + +> systems-designer 代理起草(lean 门禁,2026-09-10,受 B1 主会话委托,用户拍板全盘采纳)。本节为 A3 F1 五条款中 **①②③⑤ 的单一闭合对账落点**(条款④清图奖励量级归 B4——本节只消费其供给值 C)。全部公式**零随机**(同输入恒同输出——可回放步骤流的公式侧前提);期望值均为**设计期统计量**(对 A1 确定性流分布的批量统计,运行期无任何随机化——对账输入 = A1 F4 报告 C 的 actuals,非 target 参数)。数值默认全部 provisional(调参所有权归 C3);条款/义务引用带清单限定词。本节落定前,任何 m_w 数值修订被 B3 OQ1 前置步骤禁止——F0 即该裁决。 + +**术语白话**:**乘区** = 伤害公式里相乘的各段修正(本作两段:主角减伤池 × 敌方削弱池);**池** = 同一侧百分比修正的加总桶;**地板** = 数值不许越过的下界(本节地板 = 池最低 −100 百分点,即乘数最低 0);**免伤轮** = 主角该结算受到的伤害为 0 的结算;**溢杀** = 一刀超出目标剩余 HP 的部分——直接丢弃,不转移到下一个敌人;**当量** = 统一价值单位,1 当量 = 1 点主角 HP 的期望净变化;**瞬时口径** = 只数一个结算里兑现的价值(本节裁决口径);**全寿命口径** = 乘上它存活的结算次数(仅作派生量,不作锚);**残余盲猜端** = 图上推理不出、只能赌的那一小块;**完美排雷(P_perfect)** = 一张板上没插错一面旗、且至少标中一面时,清图奖励翻倍的乘数。 + +### F0 当量归一口径裁决(B3 OQ1 前置——本节钉死) + +**裁决**:条款② weaken 锚验收的当量归一口径采用 **"每结算瞬时当量"**(per-settlement instantaneous equivalence);**"每 AP 总暴露"分支排除**。 + +| 分支 | schema 可行性 | m_w 修订含义 | 连带后果 | 裁决 | +|---|---|---|---|---| +| **每结算瞬时当量** | 可行(m_w ∈ [51, 100] 域内) | 锚 = 每结算百分点档严格比较 | F2 越域界随移(AP 界 8×m_w / 表界 16×m_w);地板交叉层数 = ⌈100/m_w⌉ = 2;B4 参照零漂移 | **采纳** | +| 每 AP 总暴露 | **不可行**——需 m_w > 100,被 percent ∈ [−100, 100] schema 域禁止 | 无可行值 | 现实解 = B4 重定最低档参照——但 safe_cell 活行集 {knife_t1, knife_adv, heal_t1, defense_up_basic} 中无更弱 buff 行可指,需 B4 新增低档 buff 行或把锚从 buff 档位移到道具档位:接口漂移 + A4 硬约束①连锁 + 条款①对照表全表重排 | 排除 | + +**裁决理由(三条)**: + +1. **同分母性(数学面)**:瞬时口径下 weaken 与 defense_up 作用于**同一伤害流**(敌→主的同一批攻击实例,见 F2 乘区结构)——每结算当量比较 = 同分母的百分比比较,"严格大于"在百分点读法与 HP 点读法下**等价**,锚判定无量纲歧义。总暴露口径引入时长权重(defense_up dur=2 vs weaken dur=1 的 2:1 不对称),把内容调参 knob(duration)泄漏进锚判定——锚应只判档位,不判时长。 +2. **机制同构(架构面)**:B3 的兑付本来就按结算逐次进行(`query_active` 每结算读值、`decay_round` 每结算 −1)——瞬时口径是容器机制的**原生口径**。总暴露口径需要跨结算累计账本,而 B3 F2/B2 clause3_supply 已钉死"跨结算累计 = 轨迹量、非表值、不设上界"(dur=1 每结算清零无存量——**单结算自然边界**,用户 2026-09-09 定稿)——总暴露口径要求新建跨系统累计状态,与已锁事实冲突,直接排除。 +3. **schema 禁止(成本面)**:总暴露的锚算术需 m_w > 100 出 percent 域——改域 = percent 语义全表重定义(B3/B4/B1 三侧换算连锁、B3 AC-13/AC-16 校验域重写),接口漂移成本不成比例。 + +**推荐 m_w 修订值域与地板规则(可执行裁决)**: + +- **推荐值域 [60, 85],首版默认 −65**(provisional;终值 = 锚 owner B4/B1 共裁、调参所有权 C3,走接口漂移回写 registry)。 + - 下界 60 的依据:**条款②第一腿**(档位锚)要求 m_w ≥ 51(每结算 pp:65 > 50,严格大于 defense_up 的 |−50|,裕度 +30%);**条款②第二腿**(连锁放大边际基准,provisional 下限 1:1)在参考强度下要求每旗当量 ≥ 单安全格奖励期望当量 E_dig = 7.2 HP(F5 Variables),即 m_w/100 × D_ref ≥ 7.2 → **m_w ≥ 60**(零连锁板形)——两条腿的可行交集下界取 60。 + - 上界 85 的依据:> 85 仅在连锁贫瘠板增加裕度,而地板交叉层数恒为 2(见下)、边际层早已贬值;schema 顶 100 保留为绝对上界(对应第二腿校准上界 ≈ 1.96:1,见 F1 边界注记)。 +- **地板规则(B1 clamp 承载,F2 成文)**:每侧 percent 池 clamp ∈ [−100, +100] pp → 乘数域 [0, 2]。**地板交叉层数 = ⌈100/m_w⌉ = 2**(值域 [60, 85] 全域恒 2)——第 3 层起边际瞬时当量 = 0;单结算 weaken 池 HP 当量上界 = min(L×m_w, 100)/100 × D_inst(该结算实际裸伤害敞口)。 +- **B3 红警"7 层即触 B1 地板"预读更正**:该预读按 −400% 合成带估读(⌊400/51⌋ = 7);本节地板钉在**乘数零点(−100 pp)**→ 精确层数 = ⌈100/m_w⌉ = 2。裁决回执同步 B3 OQ1 与 registry `status_effect.anchor_warning`(见对账登记义务清单 #1/#2)——预读的实质结论(上调 m_w 的连带成本 = 地板提前到达、边际层贬值)不变且被本裁决吸收。 +- **连带后果(瞬时分支的义务清单)**:① B3 `StatusEffectTable.weaken.magnitude` −25 → 值域 [60, 85]/默认 −65,走接口漂移回写(B3 GDD 行表 + Tuning Knobs 联动行 + registry);B3 AC-14 腿 3 非平凡性复查:16 × 65 = 1040 越出 percent 域——腿保持非平凡 ✓。② F2 越域界随移:AP 界 = 8 × 65 = **−520%**、表界 = 16 × 65 = **−1040%**(合成值 B3 原样输出、B1 池 clamp 收口)。③ B4 侧零漂移:defense_up 参照保持("重定参照"是总暴露分支的现实解,未采纳)。④ **时长不对称显式接受**:瞬时口径下"每 AP 全寿命价值"defense_up(2 结算) : weaken(1 结算) ≈ 2:1 的不对称不入锚判定——吸收通道 = m_w 上探值域 60→85 段 + 高光 #3 即时节拍 + duration 为 knob;登记为裁决的显式接受项,与 B3 OQ7 playtest 证伪项联动观测。 +- **修订效果量(锚两腿在默认 65 下的读数,provisional @ D_ref=12)**:第一腿 = 7.8 vs 6.0 HP/结算(+30%,严格大于 ✓);第二腿(1:1 零链)= 7.8 vs 7.2 HP(+8% ✓)。历史红警算术(attack_up 口径 25 vs 100)随回执关闭:defense_up 口径复算 = 每结算 25 vs 50 pp(不过锚)→ 修订成立。 + +### F1 效果档位当量对照表(A3 F1 条款①——B3 义务⑤ 对照实物落点) + +``` +统一当量定义:E(·) ∈ 主角 HP 点(1 当量 = 1 点主角 HP 的期望净变化;与 B2 ambush 量纲、B4 F6 量纲建议同源) + +单一线性换算式(全部四源共用——分源改率 = A3 F1 条款①契约作废): + flat / 道具类(刀、治疗包、ambush): E = 值本身(HP 点恒等映射;ambush 按 k 个攻击拍线性展开) + percent 类(每结算、每层): E_inst = |L × m| ÷ 100 × D_exp + D_exp = 兑付结算的实际裸伤害敞口;对照表取参考敞口 D_ref + 完美乘数(显式 ÷100,运行时真实双份载体): E_clear(perfect) = power × P_perfect ÷ 100 + (P_perfect ∈ {100, 200}——载体 = 第二份兑付开关,Core Rule 13#2; + 200 ÷ 100 = ×2 = 两件实得,非账面乘数) + 时长(显式乘子,非分源改率): E_life = Σ结算 E_inst = E_inst × 兑付结算数 + +D_ref = SettlementTable.equivalence_ref_damage(单行全局 knob,provisional 默认 12 + = 单结算伏击结构上界 cap×m_amb〔B2 F4 已登记值〕,兼 attack 拍带 [4,16] 与 heavy 带 [8,20] 的中位代表) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 参考敞口 | D_ref | int | [1, 999] | SettlementTable 配置值(D2 回写义务,本 GDD owner);provisional 12;变更触发全对照表 + 条款③⑤重跑(联动义务) | +| 层数 | L | int | [1, max_stack] | B3 实例字段(授予快照) | +| 行值 | m | int | percent ∈ [−100,100]∧≠0 / flat ≥ 1 | StatusEffectTable 行值快照 | +| 兑付结算数 | — | int | [1, duration_settlements] | 剩余结算次数轨迹;对照表按满寿命取 | +| 道具威力 | power | int | [1, 999] | RewardTable 行值快照(恒定表值——已锁) | +| 攻击拍数 | k | int | [0, unit_count] | 该结算存活敌中 attack/heavy 拍单位数(B2 F4 供给,确定性可算) | +| 完美乘数 | P_perfect | percent | **{100, 200}**(载体域——非整数倍数无运行时载体,域外加载致命) | SettlementTable 配置值;provisional 200(×2 = 双份交付,Core Rule 13#2;换算入账恒 ÷100) | + +**Output Range:** E ∈ [0, +∞) HP 点(floor 语义见 F2——percent 池饱和后边际 E = 0);全表零负值(负值分支禁令,F3④)。 + +**默认根全部活行对照表(值全部 provisional,归 C3)**: + +| 行 | 源(source_class) | 类别 | 换算式 | E_inst(每结算/层) | 全寿命 E | 注 | +|---|---|---|---|---|---|---| +| knife_t1 | safe_cell | damage_item | E = power | —(库存轨迹量) | 4 | 窗口选用时点兑付 | +| knife_adv | safe_cell(雾池) | damage_item | E = power | — | 8 | | +| heal_t1 | safe_cell | heal_pack | E = min(power, max_hp − hp) | — | ≤ 4 | clamp 后真实当量;面值 4 = 上界读法(HP 满拒绝率——B4 预警②观测修正) | +| **defense_up_basic** | safe_cell | buff → defense_up | E_inst = 0.50 × D_ref;E_life = ×duration 2 | **6.0** | **12.0** | **最低档安全格 buff 当量 = 条款②锚对照实物**(tier_min_buff = 2 行——B4 F6 供给) | +| clear_knife | clear_default | damage_item | E = power ×(perfect 板 ? P_perfect ÷ 100 : 1) | — | 8(完美板 16——**真实双份**:两件实得,非账面乘数) | 条款④⑤载体(F5 消费) | +| attack_up(**死行**) | safe_cell(**未入池**——B4 F4 口径后果) | buff → hero_offense | E_inst = 0.50 × D_ref | (6.0) | (12.0) | **死行**:hero 伤害 = 道具恒定表值不吃修正 → 零消费点;换算式结构保留,复活走 B4 OQ1 连锁(锚参照回迁 + 全表重排) | +| weaken | flag_hit | debuff → enemy_output | E_inst = m_w/100 × D_ref | 修订后 65 → **7.8**(现值 −25 → 3.0,不过锚) | 7.8(dur=1) | 修订值域/默认见 F0 | +| ambush | mine_hit | 敌方增益(flat) | E = m_amb × k | k=1 → 3;满配 k=3 → 9 | 同左(dur=1 单结算自然边界) | 量纲已定稿(registry `ambush_unit_definition`) | + +**锚对照实物段(条款②用,B3 义务⑤)**:最低档安全格 buff = **defense_up_basic 行**(RewardTable tier=2,effect_id=defense_up)→ 每结算瞬时当量 **6.0 HP @ D_ref=12**;weaken 修订后每旗 **7.8 HP > 6.0**(严格大于 ✓,裕度 +30%)。第二腿(连锁放大边际基准,provisional 1:1):weaken 7.8 ≥ E_dig 7.2(零链板形 ✓,裕度 +8%)。**结构性边界注记**:第二腿在校准上探时的可满足上界 = m_w=100 ↔ 相对每结算百分点档 ≈ 1.96:1——连锁期望放大超过此比值的板形上,腿二不可满足为**结构性事实**(percent 瞬时档被 schema 域封顶),该情形的处置 = C3 板形参数(密度/连锁富集)与 m_w 联动约束,非数值修订可解——登记重跑义务(对账清单 #3/#8,连锁规模分布无既定供给方,见 #8 附注)。 + +**Example:** 一轮挖到 2 个安全格,均为 defense_up(L=2 同实例)→ E_inst = 2×50/100 × 12 = 12.0 HP/结算 × 2 结算 = 24.0 全寿命(名义值——饱和折减见 F4 敏感性)。同轮标中 1 旗 → weaken L=1 → 7.8 HP/结算。踩 1 雷(unit_count=1、attack 拍)→ 该结算敌方附加 +3 HP。 + +**边界注记**:归 B1——本表与单一线性换算的维护、D_ref 与 P_perfect 的 SettlementTable 承载;归 B4——行值/权重/tier/C 供给(F6);归 C3——全部 provisional 数值;归 B3——L×m 合成原样输出(本表输入侧)。 + +### F2 percent→乘数换算与合成地板(percent_to_multiplier——B3 义务③收口) + +``` +结算语义映射(SettlementTable 效果语义映射,effect_id 为键——B3 Rule 7 的 B1 侧成文): + attack_up → pool = hero_offense(MVP 无施加点 = 死行机制成因——B4 F4 口径后果;结构位保留) + defense_up → pool = hero_defense + weaken → pool = enemy_output + ambush → flat 附加(B2 F2 后置加法——不入乘区) + +敌→主单伤害实例(每结算、逐攻击行为): + P_pool = Σ(同池实例 L×m) (池内加算——B3 F2 加算语义的对侧承接) + P_pool ← clamp(P_pool, −100, +100) (合成地板:每池独立收口) + M_pool = 1 + P_pool ÷ 100 (换算方向:pp → 乘性因子;域 [0, 2]) + D' = floor( D_raw × M_hero_defense × M_enemy_output ) + (池间乘算;整数域 D' = D_raw × (100+P_h) × (100+P_e) ÷ 10000 向下取整——单一取整规则,结构性非 knob) + D_raw = query_attack(u, L, m_amb).total (B2 F2 分量合成 total;charge 拍 = 0; + 乘区作用于含伏击分量的全裸伤害——weaken 饱和轮伏击分量一并归零) +``` + +**三例逐一读法(B3 EC7 登记例的收口)**: + +| 例 | 合成值 | 池 clamp 后 | 乘数 | 读法 | +|---|---|---|---|---| +| weaken AP 界(L=8 × m_w,修订后 65) | −520% | −100 | **0** | 该结算全体存活敌输出归零;第 3–8 层边际瞬时当量 = 0(地板吸收)——明细行如实显示合成值与终值两端(P2) | +| weaken 表界(L=16 × m_w) | −1040% | −100 | **0** | 同上——地板规则不区分越域来源,越域深度仅入合成值显示与诊断计数 | +| defense_up 顶点(L=2 × −50) | −100% | −100(**恰在地板边界,未越域**) | **0** | 免伤轮(该结算 hero 承伤 = 0)——**合法设计顶点**,非 clamp 产物(B3 Tuning Knobs"连续两轮免伤读法"的对侧收口) | + +**免伤轮(×0)合法性结论**:**合法且为设计态**。乘数域 [0, 2] 的自然边界——hero 侧 defense_up 满层(连续两结算免伤)与 enemy 侧 weaken 饱和(单结算零输出)同为设计内结果;强度评估(免伤窗口是否过强)归 C3 对账联动(B3 Tuning Knobs 已登记禁独立调参),不属地板规则异常。D' = 0 时 B1 **不调 apply_damage**(B2 API 对 n ≤ 0 拒绝——负值分支禁令的实现面),改产**零值伤害行**(免伤/零输出轮必须可见——P2)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 裸伤害 | D_raw | int | [0, ∞) | B2 F2 total 分量(charge 拍恒 0) | +| 池合成值 | P_pool | int(pp) | 越域可达 ±1040(修订后);clamp 后 [−100, +100] | B3 原样输出 → B1 clamp 收口 | +| 池乘数 | M_pool | 有理数 | [0, 2] | 1 + P_pool/100 | +| 终值 | D' | int | [0, D_raw × 4](两池同 +100 的结构上界;MVP 不可达) | floor 取整 | + +**Output Range:** D' ∈ [0, ∞) 整数,零负值分支;MVP 实际域 [0, 20](B2 参考带)。**Example:** ambusher heavy(atk=4, mult=2)L=4 → D_raw = 20;weaken L=2(65)→ P = −130 → clamp −100 → M = 0 → **D' = 0**(零值伤害行,不调 apply_damage)。weaken L=1 → 20 × 0.35 = 7 → **D' = 7**。再叠 defense_up L=1 → 20 × 0.5 × 0.35 = 3.5 → floor **3**。**边界注记**:归 B3——L×m 原样输出不 clamp(B3 义务⑧/EC7 对侧);归 B1——池归属映射、clamp、乘区次序、取整方向;归 C3——池值强度评估。 + +### F3 伤害合成、clamp/溢杀与死亡判定(damage_resolution——B2 义务③成文) + +``` +⓪ 主→敌(战斗段段首,主角先手攻击——2026-09-11 用户裁决,基础攻击恒存在〔HeroTable.atk 域 [1,999]、0 加载拒绝〕): + 目标 = B2 default_target()(固定顺位最前存活敌——与道具段②同源) + D_hero_raw = HeroTable.atk ×(1 + attack_up 载荷) (attack_up = 唯一吃修正的攻击增益,percent 池加算——F2 语义; + 刀不吃修正的拍板不变——B4 F4) + D_hero = F2 换算(池加算 → clamp 地板 → floor);D_hero = 0 → 零值伤害行(同①零值口径) + D_hero > 0 → apply_damage(target, D_hero);hp 归 0 → Dead(受击即死——死亡即不动手) + 明细行 source_class = hero_attack(2026-09-11 新增来源值——接口漂移随批,target 字段照常) + 存活敌清空 → 敌方循环空转(零行动行),径入衰减段 +① 敌→主(战斗段,逐存活敌,行主序生成序;结算内调用序钉死 query_attack → apply_damage → advance——B2 义务①): + D_raw = query_attack(u, L, m_amb).total (L = 本结算伏击池读值——授予段后的当轮值;charge 拍 = 0) + D' = F2 换算(语义映射 → 池加算 → clamp 地板 → 乘区 → floor) + D' = 0 → 不调 apply_damage,产零值伤害行(分量二元组 + 终值 0) + D' > 0 → apply_damage(hero, D');hp' = max(hp − D', 0)(递减与 floor 0 由 B2 承载) + 溢杀部分(D' − 受击前 hp)丢弃——无存续、无转移、无负 hp 态 + hero hp' = 0 → 战斗段立即短路(剩余敌本结算不作用、不产行动明细行——Core Rule 10) +② 主→敌(道具段,窗口选定件——至多一件,2026-09-11 单件制): + D = power(恒定表值,不吃任何 percent 修正——已锁;零乘区) + 目标 = B2 default_target()(固定顺位最前存活敌;死亡后顺延) + apply_damage(u, power);hp 归 0 → Dead(受击即死——道具段与战斗段同款;指针冻结 = 抢杀窗口) + 溢杀丢弃、不转移下一敌(下一件道具才打下一个目标) + 施加时已无存活敌 → 照常消耗(Used 不可逆)+ "已消耗未生效"明细行(B4 E10 承接,守卫不对称——Core Rule 9) +③ 负值分支禁令:任何公式分支不得产生负伤害/负治疗/负当量——percent 池地板 [−100, +100](F2)、 + flat 值 ≥ 0、hp ∈ [0, max_hp]、治疗 clamp 上界 max_hp(B4 F4 对侧:hp' = min(hp + power, max_hp))、 + apply_damage 入参 n ≤ 0 = B2 API 拒绝 + 诊断——D'=0 腿由此改走零值行(①) +④ 死亡判定数值边界:Dead ⇔ hp = 0(无负 hp 中间态);时机 = 受击即时(敌)/ 战斗段短路(hero); + 判定段 = 防御性优先级 hero 先判(双死结构不可达的纵深防御——Core Rule 12:我方伤敌在道具段与 + 战斗段主角先手攻击、均先于敌方行动兑付;敌方伤我在主角攻击之后——敌行动时点若已敌全灭则先判胜) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 裸伤害 | D_raw | int | [0, ∞) | B2 F2 total;分量二元组 {baseline, attach} 进伤害行(P2 归因) | +| 乘区终值 | D' | int | [0, ∞) | F2 输出;floor 取整 | +| 目标 hp | hp | int | [0, max_hp] | B2 单位状态;递减与 floor 由 B2 承载 | +| 道具威力 | power | int | [1, 999] | RewardTable 授予时快照(恒定) | +| 溢杀量 | — | int | [0, D') | 丢弃(会计面:明细行 value = D',击杀标记布尔——不记溢出量) | + +**Output Range:** 双向伤害 D ∈ [0, ∞)、hp ∈ [0, max_hp];零负值、零转移、零存续。**Example:** grub(atk=4)L=2 → D_raw = 10;weaken L=1(65)→ floor(10 × 0.35) = **3**。defense_up L=1 同结算 → floor(10 × 0.5 × 0.35) = **1**。knife_adv(power=8)打 hp=6 的敌 → hp 归 0 → Dead、溢杀 2 丢弃、击杀标记进流水、下一件刀打顺位后继。**边界注记**:归 B2——递减/floor 0/Dead 翻转/指针冻结;归 B1——本式全序、调用序、零值行、溢杀丢弃语义、死亡判定时机(B2 义务③的对侧兑现)。 + +### F4 条款③挖掘风险不等式闭合(A3 F1 条款③——B1 单一闭合 owner;B2 义务④兑现) + +``` +配对口径(B1 闭合定义——B2 F4"等量换算与不等式闭合归 B1"的兑现): + 决策单位 = 单格挖掘;评估点 = 残余盲猜端最坏点(信息端平凡通过,一并登记) + 主式:P(安全) × V_safe > P(雷) × V_mine + + 适用域守卫(2026-09-10 评审补):(1 − ι) × |S| < 1(残余盲猜集为空)⇒ 主式不评估、 + 由信息端平凡腿接管——零赌博板形不产生破线误判(P(雷) 式在空池退化,禁代入) + + P(雷) = |M| ÷ (|M| + (1 − ι) × |S|) (残余盲猜端上界:全部雷对 (1−ι)|S| 个残余安全格的池) + P(安全) = 1 − P(雷) + V_safe = E_life(最低档安全格 buff) = (|m_d|/100) × D_ref × duration_d = D_ref + (defense_up 口径:0.5 × D_ref × 2 = D_ref——恒等式。**标签更正(2026-09-10 评审)**: + 这是"锚行(最低档 buff)满寿命当量",**高于跨行期望 E_dig = 7.2(刀/治疗占比 65%), + 不是期望值的保守下界**——按期望口径读数为敏感性腿,见验算表注) + V_mine = k_max × m_amb = unit_count × m_amb (单雷边际敌方增益当量上界:1 雷 = 1 层伏击; + 默认行值 1×3 = 3;cap 溢出使第 cap+1 颗雷边际当量 = 0 → 灾难轮不等式自收紧) + + 信息端检查(平凡腿):P(安全) = 1(免赌集内)→ V_safe > 0 恒成立——"推理有回报"的登记面 +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 实际雷数 | \|M\| | int | [1, W×H−9] | A1 产物指标/行参数推导 | +| 安全格数 | \|S\| | int | ≥ min_safe_count | 同上 | +| 可推理率 | ι | float | [0.85, 1] | **actuals p10(A1 F4 报告 C)**——未交付前以行 target 代 = **判式面的保守代理**(target ≥ 实际 p10 → P(雷) 偏高 → 主式更难过;对总暴露无效——低 ι 的真实代价不经本式入账,报告 C 交付按总暴露核对消费,非仅判式重跑),重跑义务(对账清单 #6) | +| 防御行值/时长 | m_d / duration_d | int / int | −50 / 2(provisional) | StatusEffectTable defense_up 行 | +| 参考敞口 | D_ref | int | [1, 999] | provisional 12 → V_safe = 12 | +| 伏击量纲/单位数 | m_amb / unit_count | int | 3(provisional)/ [1,3] | B2 行值 | + +**首版验算(A1 参考行参数代理;全部 provisional,actuals p10 交付后强制重跑)**: + +| 板形 | ι(target 代 p10) | P(雷) | 左臂 = P(安全)×12 | 右臂 = P(雷)×3 | 裕度 | 判定 | +|---|---|---|---|---|---|---| +| L1(10×10 d0.10,\|M\|=9,\|S\|=91) | 0.95 | 9/13.55 ≈ 0.664 | 4.03 | 1.99 | **≈2.0×** | ✓ | +| L2(12×12 d0.12,\|M\|=16,\|S\|=128) | 0.92 | 16/26.24 ≈ 0.610 | 4.68 | 1.83 | **≈2.6×** | ✓ | +| 满配语境(unit_count=3,V_mine=9,L1 板) | 0.95 | 0.664 | 4.03 | 5.98 | <1 | **✗ 破线** | + +- **双口径读数登记(2026-09-10 评审——防虚高裕度误用)**:上表左臂按锚行当量 V_safe=12(A3 条款③"最低档 buff 当量下界"的合同读法);**按跨行期望口径(左臂 = P(安全)×E_dig,E_dig = 7.2)复算:L1 裕度 ≈1.2×、L2 ≈1.5×**——二元结论不变(unit_count=1 过 / =3 破),但 C3 不得以 2.0×/2.6× 锚行读数作为 unit_count=2 放行依据;实现率折减(预警②④,0.6 provisional)下裕度进一步贴地。 + +**结论**:① 默认行值(unit_count=1)下两参考行**通过**(锚行口径 2.0–2.6× / 期望口径 1.2–1.5×——二元结论一致);信息端平凡通过(推理严格占优、盲挖不自杀——P1 经济侧验收形态成立)。② **满配语境(unit_count=3)最坏盲点破线**——登记为 C3 调参约束(B2 Tuning Knobs"cap × atk 禁独立调参"的扩展:unit_count > 1 的行须降密度或降 m_amb,联动条款⑤弃图压力——A3 条款⑤注记警示的 P1 失败模式;F5 侧多敌腿同批登记)。③ 敏感性:B4 预警④ buff 饱和的结构性零兑付使 V_safe 名义值高估——按 0.6 折减(provisional)复算 L1:左 2.42 vs 右 1.99 → 裕度 1.2×(仍过、变薄)——折减系数与饱和率随 playtest 重校。④ **破线参数区两轴登记(2026-09-10 评审补)**:unit_count 轴(②)与**板形密度 × 可推理率轴**(例:8×8 d0.5、ι=0.85 → P(雷) = 28/33.4 ≈ 0.838,左 1.95 < 右 2.51 破线)——C3 约束清单按两轴登记,密度调参入重跑义务(缺口预检 9 扩表义务)。 + +**与 clause3_supply 2.3× 预检的对齐说明**:registry 预检的 2.3× 系"pp 代理左臂(0.36×50)× cap 右臂(0.64×12)"的巧合近似——量纲混用(pp vs HP 经 1:1 百分点代理桥接)且右臂取结算聚合上界(cap×m_amb=12)与单格决策单位错配(多计 cap 倍)。本闭合改**统一 HP 当量 + 边际配对** → 严格裕度 ≈2.0×/2.6×——方向不变(同向通过),registry `clause3_supply.direction_precheck` 退役为历史记录、回执登记重算值(对账清单 #3)。 + +### F5 条款⑤清图 vs 弃图支配性闭合(A3 F1 条款⑤——B1 单一闭合 owner;2026-09-10 评审修订:计价模型改挖穿残余池口径,旧模型读数作废重推) + +> **模型修订缘由(评审阻塞项)**:旧模型 X_res/Δr 按每挖一格独立赌一次 P(雷) 计价——**漏计全池挖掘量**。清图必须挖穿残余盲猜区:随着安全格被挖走,池内雷占比持续上升,挖穿全部残余安全格的期望踩雷 ≈ |M|×n_s/(n_s+1)(均匀交错模型),远高于旧登记值(L1 旧 3.02 颗 vs 修后 7.38 颗)。旧模型系统性偏向"推到底",且该偏差是**模型漏项**(actuals 校准无法吸收)——旧登记读数(+19.7/+27.0 等)作废,verdict 翻转见下。 + +**术语白话(用语纪律)**:**挖穿计价** = 把残余盲猜区全部安全格挖完时平均会踩到多少雷,按"雷与安全格在池中均匀随机交错"计算——好比从一副混洗的牌里抽完所有安全牌,中途翻到雷牌的期望张数由牌组构成决定,不是每抽一张都按初始比例独立重赌。 + +``` +主判式(决策点 = "确定格挖完";策略对 = 推到底清图 vs 停手攒 RAP 刷新): + Δ_clear = [R_res + C_eff] − [X_res + Δr × s_ref] ≥ 0 + + n_s = (1 − ι) × |S| (残余盲猜安全格数——评估点同 F4; + 适用域守卫:n_s < 1(残余集为空)⇒ 主判式不评估、信息端平凡腿接管) + E[踩雷] = |M| × n_s ÷ (n_s + 1) (挖穿残余池期望踩雷——均匀交错模型; + n_s 增大时趋近 |M|:高盲板清图必然踩到几乎全部残余雷) + R_res = n_s × E_dig (残余安全格奖励期望当量) + C_eff = C × (perfect ? P_perfect ÷ 100 : 1) (C = 清图奖励当量 = power 恒等〔clear_knife = 8〕; + perfect 谓词 = 板级零误标 ∧ ≥1 标中——Core Rule 13;×2 = 真实双份交付〔Core Rule 13#2〕) + X_res = E[踩雷] × V_mine (挖穿全池雷险期望当量——V_mine 同 F4) + Δr = ⌈(n_s + E[踩雷]) ÷ AP_r⌉ (轮数差 = 残余区期望总挖掘次数上取整——安全格 + 踩雷格 + 均耗 AP;雾态增量叠算见注) + s_ref = D_ref (结算强度参考 = 当量参考敞口——同一 D_ref,单一换算) + E_dig = 0.40×E[刀] + 0.25×E[治疗] + 0.35×E_life(defense_up) + = 0.40×5 + 0.25×4 + 0.35×12 = 7.2 HP (默认根权重加权;治疗按面值 = 上界读法—— + 实现率折减入敏感性腿,同 F4③) + +**显式假设登记(2026-09-10 评审)**:停手侧的 RAP 机会收益按 **零** 计价(RAP 边际效用不入判式—— +图源充裕时 RAP 边际低,零价假设对"推到底"偏利一分;本假设下 verdict 已为负,若 C3 为 RAP +计价则结论只会更负——模型边界成文,非遗漏)。 + +保守地板式(A3 F1 条款⑤原文形式的可判定化——残余奖励不计入的下界要求): + C_eff ≥ X_res + Δr × s_ref +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 残余盲猜安全格 | n_s | float | [0, \|S\|] | (1−ι)×\|S\|;**域守卫:n_s < 1(残余集为空)⇒ 主判式不评估、信息端平凡腿接管**(零赌博板形不误判) | +| 挖穿期望踩雷 | E[踩雷] | float | [0, \|M\|] | 均匀交错模型 \|M\|×n_s/(n_s+1) | +| 清图奖励当量 | C | int | ≥ 1(A3 条款④) | B4 供给(clear_knife power=8,provisional) | +| 完美乘数 | P_perfect | percent | **{100, 200}** | SettlementTable 配置值;provisional 200(真实双份交付——Core Rule 13#2;换算入账恒 ÷100) | +| 单格奖励期望 | E_dig | float | (0, ∞) | 默认根加权推导;实现率折减敏感性同 F4③ | +| 轮行动点 | AP_r | int | [1, 32] | A3 ActionEconomyTable(默认 8) | +| 雾负担比 | fog_burden_ratio | float | [0, 1] | A1 产物指标(付费格分母口径)——雾态 Δr 修正输入 | + +**四态验算(面值口径 E_dig=7.2;含多敌腿——2026-09-10 模型修订后重推,全部 provisional)**: + +| 板形 | n_s | E[踩雷] | X_res | Δr(代价) | R_res | Δ_clear(C=8 / 完美 16) | 判定 | +|---|---|---|---|---|---|---|---| +| L1 无雾态(ι=0.95,\|M\|=9,\|S\|=91) | 4.55 | 7.38 | 22.1 | 2(24) | 32.8 | **−5.3 / +2.7** | ✗ / ✓ | +| L2 雾态(ι=0.92,\|M\|=16,\|S\|=128) | 10.24 | 14.58 | 43.8 | 4(48) | 73.7 | **−10.1 / −2.1** | ✗ / ✗ | +| L1 × 满配语境(unit_count=3,V_mine=9) | 4.55 | 7.38 | 66.4 | 2(24) | 32.8 | **−49.6 / −41.6** | ✗ / ✗ | +| L2 × 满配语境 | 10.24 | 14.58 | 131.2 | 4(48) | 73.7 | **−97.5 / −89.5** | ✗ / ✗ | + +实现率折减口径(E_dig′ = 4.32 provisional——B4 预警②④联动):L1 = −18.5 / −10.5;L2 = −39.5 / −31.5——同向更差。 + +- **雾态增量(fog_burden_ratio 口径)**:雾格 = 雾元知识可推理安全格(P(安全)=1)→ 属确定格、两策略同挖、**不进残余集**(口径不变);雾的条款⑤效应 = 链不可达雾格的全价 AP 抬高轮数——Δr 增量 = \|U\| × dig_ap_cost ÷ AP_r(A1 例板 U=4 → 0.5 轮;修后模型复算:(24.82 + 4) ÷ 8 = 3.60 → 上取整仍 4——同轮吸收,本表读数不变);雾池档位加成对冲入 R_res 侧(E_fog 读数见护栏 A)。actuals fog_burden_ratio p50/p90 交付后重跑(对账清单 #6)。 +- **verdict(2026-09-10 模型修订——自旧模型"两态通过"翻转)**:**条款⑤当前未闭合**。挖穿计价下主判式两态为负(L1 仅完美双份翻正 +2.7——完美高光与清图支配性互 feed 的设计意图保留,但非完美态仍负 = 被支配),实现率折减与多敌语境更差。结构性解读:残余盲猜区越大(n_s ↑),清图的全池雷险越逼近"踩到全部残余雷"——"是否承担盲猜风险是策略点"(game-concept 设计本意)被本判式定量证实:**"推到底"的支配性只在残余集小时自然成立**。判据函数的角色 = **C3/B4 调参约束生成器**(非一次性通过标记);AC-40 断言"输出与登记约束包一致"。 +- **C3/B4 约束包(取代旧"保守地板缺口如实登记非阻塞"——该登记方向倒置:主判式 ✓ 恰是乐观侧)**: + ① **C 量级上调整向**:保守地板需求 L1 ≥ 46.1 / L2 ≥ 91.8(现值 C=8/完美 16——缺口 −38.1/−75.8);C 量级归 B4/C3,任何上调与护栏 B 口径联动复核(见护栏 B 双读数)。 + ② **或裁"清图非严格支配"为设计态**(C3 裁决,①②二选一):与 A4 定价硬约束②(清图锚随雾负担上浮——B4 已登记 MVP 结构性不可闭合)同向;如裁设计态,A3 条款⑤注记的 P1 对冲弱化须同步声明,B4 预警⑤(弃图率雾板分层观测)加权监控。 + ③ **多敌联动**:满配语境深度为负(−49.6/−97.5)与 F4③ 破线同向——unit_count > 1 行的放行判据 = **条款③⑤在全部登记行组合下同时成立**(C3 调参约束双条款并案)。 + +**两条方向性护栏(2026-09-10 模型修订后复核)**: + +- **护栏 A(雾板净当量优势带上界——B4 pricing_constraints③ 的 B1 判据;与残余池模型无关,读数不变)**:每链不可达雾格净优势 = (E_fog − E_dig) − s_ref ÷ AP_r = (11.1 − 7.2) − 1.5 = **+2.4 ∈ (0, 3]** ✓(下界 > 0 = 雾非纯税——A4 约束①方向;上界 = s_ref/4 provisional——雾优势不得压倒主线推进。突破条件:E_fog > 11.7 → C3 雾池联动禁区,禁独立调参)。 +- **护栏 B(防钓鱼——退化板刷清图奖励;2026-09-10 输入登记更正 + 双读数)**:上界判据 = ⌈(\|S\|_min − \|R₀\|_max) ÷ AP_r⌉ × s_ref ÷ 2 ≈ ⌈(50−20)/8⌉ × 12 ÷ 2 = **24**。**输入登记更正(评审绑定项)**:① \|S\|_min = min_safe_count 是 A1 行级 knob(C3 可调——调参时本护栏按当期行集重算,禁写死 50);② \|R₀\|_max = 20 系 A1 参考带经验值、**无登记供给方**(A1 自家 12×12 例板 R₀ = 21 已越 20)→ **登记 A1 产出义务:R₀ 规模分布上界(F4 报告 C 扩列,缺口预检 9 扩表)**;③ s_ref = D_ref = 12 作每轮时间税比真实敌方 DPS 期望(grub 4 / ambusher 均值 ≈4)**松 2.5–3 倍**——偏松方向 = 反钓鱼线上界放松。**双读数登记**:D_ref 口径上界 24 → C_eff = 16 ≤ 24 ✓;DPS 期望严格读数上界 ≈8 → C_eff = 16 越。**两读数的裁决归 C3**(与约束包①联动:C 上调时防钓鱼口径同步收紧复核);结构承载 = A1 min_safe_count + 布雷数下限 + A2 R₀≠S 三闸(B1 侧判据为对账复核,非运行期校验)不变。 + +**P_perfect 默认值建议与相容性(六条,2026-09-10 修订)**:**建议 ×2**(SettlementTable.p_perfect = 200 percent,provisional,调参所有权 C3;**运行时载体域 {100, 200}**——Core Rule 13#2 真实双份交付)。相容性六条:① 单一线性换算不破坏——乘子作用于 C 当量换算**之后**、**换算 ÷100 已成文**(F1 对照表行内显式列),非新增分源换算率(A3 条款①合规);② 与条款⑤同向且是**现参数下唯一使 L1 翻正的杠杆**(+8.0)——完美高光与清图支配性互 feed;但非完美态仍负 → **C 量级上调约束不因 P_perfect 消解**;③ 触发谓词 = 板级 flag 记录(零误标 ∧ ≥1 标中),**与踩雷正交**(盲挖踩雷不破坏 perfect,标错旗才破坏——标错以"标旗未中"零值行留痕,Core Rule 5.2);④ RAP 刷新路径零清图奖励 → 无 perfect farming 环(双份兑付不改此性质——刷新图无兑付点);⑤ 与 F0 weaken 修订同向(标中旗价值 ↑ → perfect 可达性 ↑——高光 #2/#3 互 feed);⑥ 上界受护栏 B 约束(16 ≤ 24 D_ref 口径 ✓;DPS 严格读数越线的裁决联动 C3——完美加成不得触碰钓鱼线)。 + +### 显式排除声明(R4 式) + +| 对象 | 归属 | 依据 | +|---|---|---| +| P_perfect 乘数值(×2 provisional——真实双份交付载体,运行时域 {100,200})与其调参 | **C3** | B1 Core Rule 13(机制本体归 B1,数值归 C3) | +| D_ref、E_dig、裕度目标带等全部本节 provisional 数值 | **C3**(调参)+ B1(对账重跑义务的执行方) | 各式 Variables | +| m_w 修订终值 | **B4/B1 锚 owner 共裁**(值域与默认建议由本节 F0 给出),调参所有权 C3 | B3 Rule 10 / A3 条款② | +| 敌我表值曲线(atk/hp/cap/unit_count/pattern) | **C3**(B2 行值供给) | B2 Tuning Knobs | +| 奖励行值/权重/tier/C 量级与差异化分支 | **B4/C3** | B4 F6 / A3 条款④ | +| 雷密度/可推理率/雾负担 actuals 分布 | **A1**(F4 报告 C 生产者) | A1 登记口径 | +| 清图总预算"够不够"的判、战斗长度/轮数曲线、遭遇构成 | **A1/A3 契约、C1/C3** | A1 F3 排除声明 | +| RAP 经济(刷新价、转存) | **A3** | A3 Rule 11 | +| 结算明细的呈现(高光行/零值行/聚合计数的可见形态) | **D1/B5** | B1 Core Rule 14 | +| **B1 只拥有**:统一当量换算规则(F1)、乘区/地板/取整语义(F2)、clamp/溢杀/死亡判定数值边界(F3)、条款③⑤不等式的闭合判据与配对口径(F4/F5)、明细行协议的数值字段 | **B1** | A3 F1 五条款 + B2 义务③④ + B3 义务③⑤ | + +### 对账登记义务清单(本节落定后的回写义务) + +1. **registry `status_effect.anchor_warning` 裁决回执**:归一口径 = 每结算瞬时当量(总暴露分支关闭——schema 域禁止 + 重定参照路径不采纳);m_w 修订值域 [60, 85]/默认 65;"7 层触地板"预读更正(地板 = 乘数零点 −100 pp,交叉层数 = ⌈100/m_w⌉ = 2);defense_up 参照保持。 +2. **registry `status_effect.mvp_content.weaken` + B3 GDD 行表/Tuning Knobs**:magnitude −25 → 值域 [60, 85]/默认 −65(接口漂移回写);B3 AC-14 腿 3 非平凡性复查回执(16×65 = 1040 越域 ✓);B3 OQ1/OQ6 关闭回执。 +3. **registry `clause3_supply.direction_precheck` 闭合回执**:统一当量重算值(**双口径登记 2026-09-10 评审更正**:锚行口径 ≈2.0×/2.6×、跨行期望口径 ≈1.2×/1.5×——二元结论一致;边际配对),pp 代理退役为历史记录;满配语境(unit_count=3)最坏盲点破线 → C3 调参约束登记(unit_count 轴 + 密度×可推理率轴——F4 结论④)。 +4. **registry 新条目 `clause5_dominance`(或并入 clause3_supply)**:F5 主判式(**2026-09-10 模型修订:挖穿残余池计价——旧模型读数作废**)+ **未闭合登记(两态为负、含多敌腿与实现率折减口径)与 C3/B4 约束包**(C 量级上调 or "接受清图非严格支配"设计态,二选一归 C3 裁;护栏 B s_ref 双读数联动)+ 两护栏结论 + actuals 重跑义务。 +5. **registry `reward_table`/`reward_config` 注记**:C_eff 口径(P_perfect 入账规则——**÷100 成文 + 真实双份交付、载体域 {100,200}**;当量 = power 恒等);attack_up 死行注记的换算侧确认(对照表死行条目)。 +6. **registry `board_gen_table.inferability_vs_p_safe` 消费回执**:P(安全) 代理公式(残余盲猜端上界式)+ actuals p10 输入接线路径(A1 F4 报告 C)。 +7. **D2 SettlementTable schema(B1 回填 owner)+ registry 新条目 `settlement_table`**:字段 = equivalence_ref_damage(D_ref)/ p_perfect(**载体域 {100,200}**——2026-09-10 修订)/ 效果池映射(effect_id → {hero_offense, hero_defense, enemy_output, flat_attach})/ 取整方向(floor,结构性)/ **pool_clamp_lo + pool_clamp_hi 两字段(2026-09-10 统一"八字段"口径——原 registry/D2"七字段 pool_clamp_lo_hi"措辞作废:registry 同批改写,D2 GDD 行按收编先例以本条为准)** / source_label_map(**键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack}**——2026-09-10 修订;hero_attack 2026-09-11 用户裁决增补)。 +8. **systems-index 门禁记录**:B1 强制检查点三项(条款①②③⑤)裁决落定登记;A3 OQ1(对照表)、B4 OQ5(T_fog_min 推导式——对照表已落定,改推导式与否由 B4 裁)/OQ6(weaken 联动)状态回写。**附注(新缺口)**:第二腿校准所需的**连锁规模分布**(c̄)无既定供给方——登记 A1 报告 C 扩列或 A2 telemetry 的归属 OQ。 + +## Edge Cases + +- **If 末图清图即胜利(cleared 与 battle_over 同结算)**:授予段先兑付清图奖励(含完美排雷判定)→ 窗口当轮即可选用清图奖励件(窗口锚点收益面)→ 道具段照常 → 战斗段无存活敌 = 空转(无行动行)→ 衰减段照常执行(B3 义务⑥恰一次)→ 判定段判胜 → battle_over 清理序列;未选用件随 `clear_inventory` 消亡 + 反馈行(B4 E15 对侧承接)。 +- **If 战斗段 hero hp=0**:立即短路——剩余敌单位本结算不作用、不产生任何行动明细行,径入判定段判负(用户拍板:死尸挨打行是归因噪音);短路后衰减段照常执行(结算完整性优先于战斗中断)。 +- **If 判定时 hero 与最后敌同时 hp=0**(结构上不可达——我方伤敌在道具段与战斗段主角先手攻击、均先于敌方行动兑付;敌方伤我在主角攻击之后,敌行动时点若已敌全灭则先判胜;推理随 2026-09-11 主角先手攻击更新):防御性规则 = **hero 先判**,判负(纵深防御,Core Rule 12)。 +- **If 减伤/削弱合成后 D'=0**(免伤轮 / weaken 饱和轮):**不调 `apply_damage`**(B2 API 对 n≤0 拒绝——负值分支禁令的实现面),改产零值伤害行(分量二元组 + 终值 0)——免伤与零输出必须可见(P2)。 +- **If 伤害超过目标剩余 hp(溢杀)**:溢出部分丢弃——无存续、无转移、无负 hp 态;明细行 value = D'、击杀标记为布尔(不记溢出量——会计面最小完整)。 +- **If 敌为 charge 拍而本轮踩雷**:伏击授予照常执行(授予段先于战斗段)、`query_attack` 返回零作用 → **本结算伏击池全部蒸发**(dur=1 轮末衰减清零——B2 EC8"蓄力轮蒸发"读怪奖励的对侧:明细 = 授予行 + 蓄力行,玩家可对照"这轮踩的雷白喂了");战斗段零伤害。 +- **If 全体存活敌同拍 charge**:战斗段 = N 行蓄力行、零 apply_damage、hero 承伤 0——合法状态非错误(B2 EC"全体蓄力"对侧)。 +- **If 伏击池 L=0 时敌攻击**:D_raw = 纯基线 atk(分量二元组 {atk, 0})——零伏击不是缺失分支,是 L=0 的自然读值。 +- **If 同窗多件治疗包顺序选用**:逐件动态重判——第 1 件推满后第 2 件被满血守卫拦截(不烧包 + "使用被拒"行);usable_now 预判在窗口数据组装时同步刷新(B4 义务④腿)。 +- **If 治疗包 power 超过当前缺血**:`hp' = min(hp + power, max_hp)` 部分回复、不拒绝(满血 = 零收益守卫、超量 = 部分收益——两分支语义互斥)。 +- **If 选定刀施加时已无存活敌**(前序刀全灭):**照常消耗**(Used 不可逆)+ 逐件"已消耗未生效"明细行(B4 E10 拍板承接——零效果判定读 B2 存活集,Core Rule 9);战斗段照常空转推进。 +- **If 窗口打开时库存为空**:跳过恒合法;呈现面 D1 可整体不弹窗(B4 E8 对侧);结算段序零停顿继续。 +- **If 挖掘期刷新 RAP 不足**:A3 `request_refresh` 返回 false、状态不变 → `refresh_board` 原样上抛拒绝(A3 EC13 对侧;刷新拒绝不是编排错误,是玩家决策的合法结果——D4 2026-09-12:本 EC 原"停手/耗尽轮 Waiting 期请求刷新"构造随刷新前移作废)。 +- **If 同轮多次刷新成功**(cap=2 内,A3 EC15 对侧——D4 2026-09-12 改写):每次成功独立走 `refresh_board` 编排(retire → generate → install → roll_board 恰一次/图)——挖掘期当场连刷合法;每次 generate 消费 board_gen 流新 scope、每次 roll_board 消费 reward_roll 流新 scope。第三次提交 → cap 拒绝。~~原"同一 Waiting 相位连环刷图"构造~~(作废)。 +- **If stopped/ap_exhausted 轮**:出口分发**恒 same_board**(D4 2026-09-12——刷新已前移挖掘期,本编排无刷新提交面)——回发 new_board = B1 违约(绕过 RAP"支出唯一"),fail-fast 上抛(A3 Rule 11 契约义务的 B1 侧)。 +- **If 换图生成产物带非法产物标记**(A1 EC5 零合法降级):编排不拒绝产物(玩家永不卡死),**上抛玩家侧呈现请求**(数据面 = 非法产物标记;提示/补偿/回退形态归 D1 裁决——登记 D1 义务);A2 `install_board` 若拒绝(不变式违例)= 程序级错误 → fail-fast 上抛(非玩家域)。 +- **If battle_over 清理序列中某调用被拒**(重复/错序——如 B3 域闭后再 end):debug 诊断 + 编排纪律修复;幂等守卫(各侧 false + 诊断)兜底,不中断清理序列的剩余步骤(清理必须完成——防半清理态跨战斗泄漏)。 +- **If 战斗中表热重载请求**(D2 `reload()`):`set_reload_gate(true)` 已置位 → D2 门拒绝返回 false(D2 EC8——"战斗中真的被置位"的 B1 侧落点;AC 节承载验收)。 +- **If 衰减段调用时容器已空**(全 dur=1 上轮清空):`decay_round()` 返回 0——合法结果、零明细行或聚合零计数(B3 EC13 对侧)。 +- **If 清图槽身份为 buff 且授予时已满层**:零兑付 + "奖励未生效"行(合法例外——B4 E11 拍板对侧);**完美板高光行降级不发**(Core Rule 13#2 降级规则——庆祝不打在空兑现上;完美谓词仍按板级事实成立,仅无兑付面,当量入账 = 实际兑付 0);若仅**第二份**兑付被拒(首份已落地)→ 高光行照发、value = 实际有效兑付(首份)当量 + "奖励未生效"行一条(Core Rule 13#2)。 +- **If 完美排雷谓词在 RAP 刷新图上**:刷新图不产生清图奖励(无兑付点)→ P_perfect 不触发;**板级 flag 计数随每次 board install 重置**(完美判定域 = 本板自 install 起的 flag_judged 全集——跨图不累积)。 +- **If 板内既有标中又有误标**:`hit=false ≥ 1` → 完美谓词不成立 → 正常兑付(无高光行)+ **本轮首个误标判定产"标旗未中"零值行(highlight=`perfect_broken`——"为什么没拿到完美"的归因锚,Core Rule 5.2;重复上报去重)**——零误标是全有或全无谓词,无部分完美档(两档扩展被否,用户拍板单档)。 +- **If 多敌(unit_count=3)同结算攻击**:行主序逐敌执行循环、共享同一 L(池共享不消耗);任一敌作用后 hero 死亡 → 短路中止剩余敌(顺序 = 生成序,先位敌先作用——顺位即威胁序的可观测面)。 +- **If 道具段击杀造成死亡即不动手**:该敌 Dead 后,战斗段 `query_alive` 已不含它——不出现行动行(指针冻结的明细面 = 缺席);抢杀窗口的兑付 = 击杀标记 + "重击未落地"归玩家从行序列推断(H3 感知数据前置——击杀结果进流水)。 +- **If 零威胁组合**(行 atk=0 ∧ cap=0;原"零伤害死局组合"随 2026-09-11 用户裁决收窄——hero atk=0 已被 HeroTable 域下界结构杜绝,战斗恒可定局):战斗可胜但零风险——内容态配置错误,护栏 owner = 实现期 ADR/D2 跨字段校验(B2 OQ9 并案,内容禁区非死局);B1 侧观测锚 = 战斗轮数异常遥测(进 C3/OQ 清单)。 +- **If 结算开始时 hero hp=0**:结构不可达(hp=0 只产生于战斗段 → 短路直入判定段 → battle_over;窗口属于判定"继续"后的下一结算)——防御断言:窗口打开前置检查 hero 存活,违反 = debug 断言(编排违例显式暴露)。 + +## Dependencies + +**Upstream**: + +| 系统 | 性质 | 接口 | 状态 | +|---|---|---|---| +| D2 | hard | SettlementTable schema 承载 + ConfigDB 读取(B1 回填 owner)+ `set_reload_gate()` 调用面(D2 EC8 唯一调用方) | Approved ✓ | +| A3 | hard | F1 五条款下游契约(B1 = 条款①②③⑤闭合 owner)+ `refresh_board` 挖掘期刷新编排〔D4 2026-09-12,替代原代理调用〕 + `notify_settlement_done`/`notify_combat_end` 回发 + `last_round_end` 读取 | Approved ✓ | +| A2 | hard(订阅) | 三事件流(cell_revealed/flag_judged/round_end)+ `install_board`/`retire_board` 调用方 + Waiting 相位观测量(A2 唯一回边的订阅端——TD-SYSTEM-BOUNDARY 裁决) | Approved ✓ | +| B2 | hard | 单位接口全调用面(spawn/query_*/apply_damage/advance/default_target/ambush_grant_plan/despawn_all)+ B2 义务①–⑥ | Approved ✓ | +| B3 | hard | 状态容器五 API 全调用面 + B3 义务①–⑧ | Approved ✓ | +| B4 | hard | 奖励/库存十 API 全调用面 + B4 义务①–⑥ | Approved ✓ | +| A1 | 间接 hard 接线 | `generate(L, D)` 换图编排调用方 + actuals 分布 p10 对账输入(F4 报告 C)+ attempt 边界非 scope 事件 | Approved ✓ | +| A4 | 对账锚(零运行时接口) | 条款⑤三腿入账(雷险消解/信息双向/AP 腿)+ 两方向性护栏 + 雾格零特判(B1 无变异分支) | Approved ✓ | +| C1/C2 | **provisional** | encounter 上下文 (L, D, hero_hp) 供给(begin_battle 入参);跨战斗 HP 持久性;战斗触发 | **未设计**——MVP 冒烟直供 | + +**B1 是项目首个全部玩法上游均已 Approved 的系统**——无 provisional 上游假设(C1/C2 除外,其缺位由 begin_battle 入参封装隔离)。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| D1 | hard(数据供给,未设计——第 10 位) | 结算明细流水 = 统一数据源契约(Core Rule 14 行协议);承接呈现义务台账(A2×6 / B2×4 / B3×4 / B4×5 / **B1 新增 ×3:高光行强调呈现、完美排雷呈现、EC5 零合法板提示**——缺项即 blocking,随 D1 GDD 门禁评审核对) | +| B5 | soft(垂直切片) | 结算步骤流 = 战斗动画的回放脚本源;溢出行/origin_cell 玩家可读形态(B3×B5×2 数据源) | +| D4 | soft(Alpha) | 音频挂点 = 明细行类型流(B4 音频挂钩点事件源) | +| D5 | soft(Alpha) | 教学口径数据源(来源标注/RAP 清零/库存清空/满层溢出/蓄力拍贪雷——各上游教学条目经 B1 流水兑现) | +| C3 | soft(provisional) | 条款③⑤对账结果 = 调参约束输入(unit_count>1 破线约束 / 保守地板缺口 / 护栏 A/B 突破条件);P_perfect/D_ref 调参所有权 | + +**双向一致性核对**(systems-index 依赖分层表):B1 ← A3,B2,B3,B4,D2 ✅(第 3 Feature 层)· D1 ← A2,A3,B1 ✅ · B5 ← B1,D1 ✅ · D4 ← A2,B1 ✅ · D5 ← A2,A3,B1 ✅ · A2 事件面 referenced_by 含 B1 ✅(订阅边)。**注记一条**:C3 ← D2,B2 与"C3 是否消费 B1 输出"悬案(systems-index 2026-09-07 TD 门禁登记)——本 GDD 落定条款③⑤对账后,**C3 消费 B1 对账结论的事实依赖成立**,C3 GDD 定稿时须回写索引(对账输出 = 调参约束清单,非运行时接口)。 + +## Tuning Knobs + +### SettlementTable Schema 定稿(D2 回写义务——B1 回填 owner,单行全局,Rule 4 豁免:单行全局常量/结构表,HeroTable/ActionEconomyTable 先例) + +| 字段 | 类型/域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| equivalence_ref_damage(D_ref) | int [1, 999] | 12 | ✓ | 当量参考敞口(F1);**变更触发全对照表 + 条款③⑤对账重跑**(联动义务,禁独立调参) | +| p_perfect | int **{100, 200}**(percent) | 200 | ✓ | 完美排雷清图奖励**第二份兑付触发**(Core Rule 13 / F5——100 = 仅高光无第二份、200 = 真实双份交付;非整数倍数乘数无运行时载体,域外加载致命);上界受护栏 B 约束(防钓鱼线双读数:D_ref 口径 24 / DPS 严格读数 ≈8——裁决归 C3) | +| segment_order | StringList | [grant, item_window, buff_debuff, item, combat, decay, verdict] | ✗ 结构冻结 | 段序骨架的零字面量载体(B4 拍板序);**加载期校验恒等于拍板段序,违例致命**(防实现乱序静默漂移) | +| source_label_map | Dictionary(source_class → 文案键) | 封闭枚举全覆盖 | ✗ 结构冻结 | source_class → D1 呈现文案映射键(P2 归因的呈现侧数据源);**键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss}**(2026-09-10 修订;新增键 = 接口漂移) | +| effect_pool_map | Dictionary(effect_id → pool) | {attack_up→hero_offense, defense_up→hero_defense, weaken→enemy_output, ambush→flat_attach} | ✗ 结构冻结 | 效果语义映射(F2——B3 Rule 7 的 B1 侧载体);**新 effect_id 上线须随批登记映射,悬空 effect_id = 加载期致命**(引用型字段,B3/B4 dangling_ref_policy 同款) | +| pool_clamp_lo / pool_clamp_hi | int(pp) | −100 / +100 | ✗ 结构冻结 | percent 池地板域(F2);乘数域随之 [0, 2]。**字段口径注(2026-09-10 统一):本表为两字段、全表共八字段——registry/D2"七字段 pool_clamp_lo_hi"旧措辞已作废(registry 同批改写、D2 GDD 行按收编先例以本条为准)** | +| round_direction | enum {floor} | floor | ✗ 结构冻结 | 乘区取整方向(F2——单一取整规则,结构性非 knob) | + +**校验**:effect_id 主键唯一(ConfigDB 既有);effect_pool_map 键 ⊆ StatusEffectTable 活行集(跨表悬空致命——B4→B3 首引激活先例同款);segment_order 违例致命;**p_perfect ∈ {100, 200}**(运行时载体域——非整数倍数乘数无运行时载体,域外加载致命;100 = 完美必须 ≥ 不加倍的机制语义下界)。 + +### Knob 表 + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| equivalence_ref_damage | 12 | 全当量表膨胀 → 条款③左臂虚高、锚裕度失真(重跑强制) | 当量坍缩 → weaken 锚第二腿破线(m_w≥60 下界的前提失效) | +| p_perfect | 200 | 载体域封顶 200({100,200}——C 量级上调走 B4 clear 池 knob,须与护栏 B 口径联动复核:防钓鱼线 D_ref 读数 24 / DPS 严格读数 ≈8) | =100 → 第二份兑付不触发(完美仅剩高光行、无实质增量——情绪奖励感最弱档,机制空转) | + +- **非 knob(结构冻结,修订走 GDD 流程非调参)**:段序骨架、乘区结构(两池乘算)、floor 取整、池 clamp 域、明细行协议、零随机、结算触发 = Waiting 相位、调用序(query_attack→apply_damage→advance)、衰减恒执行。 +- **Knob 交互**:D_ref × m_w(锚两腿共用 D_ref——B4/B1 共裁 m_w 时须同步核对 D_ref,禁独立调参);p_perfect × C(clear 池当量)——护栏 B 联动;D_ref × 条款⑤ s_ref(同值引用——改 D_ref 即改结算强度参考,两条款重跑)。 +- **调参所有权**:全部 knob provisional 归 C3;**每次调参须重跑 F1 对照表 + 条款②③⑤预检并同步 registry**(接口漂移义务——B2/B4 同款条款);actuals p10 交付后的强制重跑义务见 Formulas 对账清单 #6。 + +## Visual/Audio Requirements + +B1 无自有渲染面(Core 层 headless 债,Core Rule 1)——视觉义务是**数据面供给规格**,呈现归 D1/B5: + +1. **结算明细屏 = MVP 战斗本体**(双层口径的呈现面):数据源 = Core Rule 14 流水行协议(行类型封闭枚举 + 公共字段);屏级布局/节奏/滚动形态归 D1/B5。**每行 `value` 数值在暗色底上须高对比**(视觉锚 1——明细屏是 MVP 数字密度最高的屏)。 +2. **行类型区分 = 形状+色相双通道**(视觉锚 2):伤害/治疗/增益/削弱/未生效类行的视觉编码不得只靠色相——未生效类行(奖励未生效/已消耗未生效/使用被拒/溢出未生效)必须携带形状冗余(icon/划线等),防色弱玩家把"没生效"误读为普通行。 +3. **高光行数据面(B1 新增 ×3 的 D1 义务输入)**:perfect_sweep 高光行、标中旗 `highlight` 标记、清图奖励行 H2 强调标记——B1 只锁行字段(highlight 可空枚举),强调形态(放大/描边/音画同步)归 D1;**未生效类行按聚合计数呈现**(D1 义务③——数据可聚合字段已入协议,防逐行惩罚轰炸)。**伤害行/道具行的 `target` 字段与效果行的 `effect_id` 是 D1 多敌归因呈现与 B3 D1×4②③ icon/极性映射的直接数据源**(Core Rule 14 桥字段——display_name 本地化不劣化映射)。 +4. **零值伤害行可见性**:免伤轮/零输出轮的零值行是 P2 诚实语义的呈现对象——不得被"零值不显示"优化吞掉(归因链断点)。 +5. **音频挂钩点**(D4,Alpha):挂点事件 = 明细行类型流——道具行(入库)/效果行/伤害行/判定行/高光行/负反馈聚合(未生效类行数 >0 时单次结算至多一次——B4 音频注记对侧,防逐行惩罚轰炸);挂点事件源归 B1 流水,B4 零音频依赖的对侧同款。 + +> 注:`art-director` 未经咨询——Lean 模式,生产前请人工复核本节与 art-bible(§1–4 须在 D1 GDD 前创建——既有排期义务)的一致性;本节行类型双通道编码是 art-bible 的输入之一。 + +## UI Requirements + +B1 不拥有 UI。两项界面贡献的数据契约: + +- **道具使用窗口**(D1 域屏):窗口载荷 = B4 `query_inventory` 投影 + **B1 侧组装的 `usable_now` 预判字段**(满血治疗包禁选——Core Rule 7);选择序收集/跳过/窗口时机指示/触发政策归 D1(B4 D1 义务②同款);窗口选择序列 = 回放输入事件(Core Rule 4);窗口期输入与棋盘不竞争(单意图帧先例)。 +- **结算明细屏**(D1 域屏):数据源 = 流水行协议(Core Rule 14);终局判定行 = 胜负画面的数据锚;呈现形态归 D1。 + +**📌 UX Flag — B1 战斗结算规则**:本系统有 UI 贡献(结算明细屏 + 道具使用窗口)。在 Phase 4(Pre-Production)运行 `/ux-design` 为结算屏与窗口创建 UX 规格**先于**编写 epics——引用 `design/ux/[screen].md` 而非本 GDD。已登记 systems-index 备忘(Phase 5d)。 + +## Acceptance Criteria + +> **qa-lead 代理起草(lean 门禁,2026-09-10,受 B1 主会话委托;四项口径经用户拍板全盘采纳——AC-36 生效前提口径 / A2 三层声明 / A1 桩 / 对账断言形态)**:覆盖 16/16 Core Rules(每条至少一个 GIVEN-WHEN-THEN)、F1–F5 全公式(可判定判据;F0 的 AC 形态 = registry 对账)、**23 条上游义务逐一落位**(B3×8 / B2×6 / A2×3 / B4×6,引用带清单限定词)、强制检查点 5 项(reload gate 战斗中置位 / 结算可回放 D2 AC-15 兑现 / new_board 违约 fail-fast / roll_board 恰一次 / 衰减恰一次)、SettlementTable 加载校验三类别、EC 节全部条目(呈现侧与 playtest 侧以 out-of-scope 声明承载,覆盖口径见覆盖矩阵)。F4/F5 条款闭合的 AC 形态 = **对账断言**(以登记常数跑闭合判据函数、断言结论与登记一致——非 playtest);条款③⑤为 B1 强制检查点,本节即其执行器。 +> +> **测试载体与集成口径(Core-Battle 全集成——项目首个全部玩法上游 Approved 的系统)**:冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code)、ConfigDB/RngService autoload 由脚本显式实例化)。各侧真假声明:**真 A3**(相位机全 API)+ **真 B2/B3/B4 容器实例**(class_name 直引实例化,B2/B3/B4 AC 前言先例;B2 多敌腿复用 `enemy_multi_unit` 夹具根)+ **真 A2 规则引擎**(程序构造板走 A2 既有板登记规格——b_spawn/b_flag/b_batch/b_chain/b_fog/b_mine_walk,登记常数直接复用 A2 AC 前言板清单;挖掘/插拔/停手经 `settle_batch` 真驱动——冒烟扮演 D1 提交意图,A2 D1×6① 同构)。**A1 侧 = 程序构造产物桩**(间接接线、非战斗域 Core 集成对象——换图 generate 调用点以 A1 产物 schema 坐标级程序构造板按登记序列返回;桩每次调用经 `open_stream("board_gen")` 取号恰一次、与真 A1 取号契约同构,scope 轨迹断言因此真实;attempt 重试不重开 scope 的语义归真 A1——登记 deferral);A1 真生成器 × A2 × B1 全链路集成 = 端到端集成冒烟,与 A2 OQ 登记的 A1×A2 双包含端到端同槽(生效条件 = A1 实现落地)。**C1/C2 provisional**:`begin_battle(encounter)` 入参冒烟直供(encounter 坐标复用 B2 行集登记 (L_g,D_g)/(L_a,D_a);hero_hp 按腿直供 max_hp/低值/缺血压值)。**D1/B5 未设计**:B1 输出契约断言面 = **步骤流捕获列表**(A2 事件捕获断言面先例)+ 回发捕获(`notify_settlement_done`/`notify_combat_end` 调用捕获)+ `set_reload_gate` 调用捕获;窗口选择与刷新意图 = 冒烟扮演 D1 经 B1 提交面注入(D1×② 的对侧)。**A2 OQ2 依赖声明**:A2 事件外发载体(signal vs 回调)归实现期 ADR——凡依赖经真 A2 事件流驱动 B1 的 THEN 子集,ADR 未落地前为纸面契约(A2 AC 前言收窄口径同款);备选登记 = **A2 半真(事件注入面直喂 B1 facts 缓冲 + A3 真)**,冒烟专设注入通道、不属产品 API,断言口径不变(B2/B3/B4"直接驱动最小 API 面→就绪后改走真实调用方"先例);本前言显式选择**优先真 A2 全集成**、半真仅为 ADR 前过渡载体。 +> +> **被测面与观测量**:B1 编排入口(provisional,实现期 ADR 定名——Overview 注记的 ADR 域):`begin_battle(encounter)` · 窗口选择提交面(扮演 D1)· 刷新意图提交面(扮演 D1——`refresh_board` 挖掘期刷新编排,D4 2026-09-12)· 结算事务(内部触发,冒烟经 A2/A3 驱动到达)。**步骤流 = 主断言面**:明细行序列 + 窗口输入事件标记(段界锚);**段序断言 = 步骤流行序**(授予类行 < 窗口标记 < 效果行 < 道具行 < 伤害/蓄力行 < 衰减行 < 判定行——全序断言)。上游只读观测量:A3 `ap/rap/phase/last_round_end` · B2 `query_alive/query_beat/query_attack/default_target` · B3 `query_active`(六归因字段+快照载荷)· B4 `query_identity/query_inventory` · RngService 公开只读 scope 计数器(board_gen/reward_roll 双流——roll_board/generate 恰一次的观测面,B4 AC-1 同款)· 诊断收集器。多步 GIVEN 均给出构造序列(板 + 意图序 + σ 起点)。 +> +> **期望值推导纪律**:全部 knob 按当前 ConfigDB 表读值推导、不写死——记号:`D_ref`(SettlementTable.equivalence_ref_damage)/ `P_pf`(p_perfect)/ `m_w`(weaken.magnitude)/ `m_d/D_a/M_a`(defense_up)/ `m_amb`(ambush.magnitude)/ `c_r`(所在行 cap)/ `a_g/h_g`〔grub〕/ `a_e/h_e/m_e/u_c_f`〔ambusher〕/ `M_h`(HeroTable.max_hp)、`A_h`(HeroTable.atk——主角先手攻击基值,2026-09-11 增补)/ `p_i`(RewardTable.power 读值)/ `AP_r`。**结构定值断言合法**:segment_order = 拍板序(结构冻结——加载校验恒等断言)、pool_clamp_lo/hi = −100/+100、round_direction = floor、effect_pool_map 四键(attack_up→hero_offense / defense_up→hero_defense / weaken→enemy_output / ambush→flat_attach)、行类型封闭枚举 13 类、公共字段全集 + 行类型特有字段分层(Core Rule 14——target/effect_id/pool_value)、source_label_map 封闭(键集含 enemy_action/flag_miss/hero_attack——2026-09-11 增补)。**非平凡性前提与 SKIP 纪律**(B3 AC-14 腿 3 先例):依赖修订后基线的腿(如 `m_w ≤ −51` 使 2 层即越域)前提不满足时输出显式 SKIP 标记防 CI 静默、不假失败;**两条"生效前提"AC**(F1 锚腿 / registry 对账)在接口漂移回写完成前失败 = 裁决未兑现的正确信号(B2 AC-16"不写软口径"先例)。**登记常数协议**(A1 黄金向量 / A2 板登记 / B4 Ψ·Φ 同源):① A2 板登记常数与 B2 行集登记坐标**直接复用不重制**(引用登记);② B1 新增 **σ 登记轨迹**(身份依赖腿——如"双治疗包同窗""刀全灭敌""清图槽身份"所需身份组合,master=42、经 `derive_at` 测试通道推导、预推进协议按 B4 时序口径)+ 登记构造序列(板 × 意图序 × 期望行数);**登记后安排第二人独立推导复核(A2 OQ3 / B4 同款义务)**;失效处置 = 先复核登记、登记无误即实现缺陷。每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明)。 +> +> **夹具策略**:① **默认根 `b1_default`** = D2 全表清单 + **SettlementTable 默认行**(D_ref=12 / P_pf=200 / segment_order=拍板序 / source_label_map 封闭全覆盖 / effect_pool_map 四键 / clamp −100/+100 / floor——B1 回填 owner 的首次表内容落地);② **致命根 ×3**(独立进程,manifest 见下):`b1_segorder_bad`(segment_order 置换两段——违例致命)、`b1_poolmap_dangling`(effect_pool_map 追加 ghost_effect 键——键 ⊆ StatusEffectTable 活行集跨表悬空致命)、`b1_pperfect_oob`(p_perfect=99——载体域 {100,200} 越下界致命);③ **SettlementTable schema 程序构造**(class_name 直引)承载域校验全谱(AC-4);④ 板 = A2 既有板规格复用、多敌根 = B2 `enemy_multi_unit` 复用、清图槽身份腿按 σ 登记轨迹(或 b4_golden 根复用);行为夹具根经 `--data-root=` 注入(仅 debug、不入 `data/tables/`——D2 夹具纪律);各 AC 声明其板/根,用例间无顺序依赖。 +> +> **致命类收窄豁免与 runner manifest**:B1 冒烟设**恰三行致命清单**(均为 SettlementTable 特有校验类别,独立 headless 进程、游戏通道,D2 AC-2b 协议同型)——runner manifest 退出码行:`segorder_violation→≠0` · `poolmap_dangling→≠0` · `pperfect_oob→≠0`。**负空间声明(B2 实例不变式先例)**:编排自守 fail-fast 断言——new_board 违约守卫、窗口打开前置 hero 存活断言、一事件双转化守卫、同一时刻至多一个结算事务在飞——**违规路径不可由公开 API 构造**(编排 API 单入口、单驱动方),不经冒烟断言、不入 manifest;其行为级证据 = 正腿断言(AC-30 腿1 未刷新恒 same_board / AC-15 腿1 窗口组装含存活前置观测)+ AC-43 静态扫描。其余致命类别(通用枚举域/域校验/R1 类)由 D2 AC-2 同类别 loader 级夹具承载(表无关,豁免声明收窄保留——B2/B3/B4 同款)。 +> +> **out-of-scope 覆盖声明(deferral 以对侧登记为生效条件)**:**D1 呈现全域**——结算明细屏布局/节奏/滚动、高光行强调形态(放大/描边/音画同步)、零值行可见性、未生效行聚合呈现、窗口呈现(形态/时机指示/usable_now 禁选态/触发政策/空库存不弹窗)、清空反馈可见性、H2 强调、EC5 零合法板提示补偿形态(deferral 生效条件 = Dependencies 节 D1 义务台账 A2×6/B2×4/B3×4/B4×5/B1×3 登记,缺项即 blocking);**B5**——步骤流作回放脚本源的消费、origin_cell/溢出行可读形态;**D4/D5**——音频挂点、教学口径(数据源 = 本节流水行,呈现/触发归对侧);**C3 数值**——全部 provisional(P_pf/D_ref/裕度带/E_dig 折减),**每次调参须重跑 AC-35/36/37/39/40 对账族**(D_ref × m_w 禁独立调参——Tuning Knobs 联动义务);**A1 actuals p10 重跑**——F4/F5 的 ι 以行 target 代 p10 为乐观代理,A1 F4 报告 C 交付后强制重跑(Formulas 对账清单 #6,登记于缺口预检 4);**条款⑤护栏 playtest 观测面**——预警⑤弃图率雾板分层/⑥速杀退化/零伤害死局战斗轮数遥测(playtest 域非冒烟;B1 侧仅供遥测锚);**m_w 修订终值**——B4/B1 锚 owner 共裁 + C3 调参(F0 推荐值域已裁,AC 以表读值记号承载);**attack_up 复活连锁**(B4 OQ1——hero_offense 池消费点上线时本节死行断言连锁回写);**标错惩罚 flag_miss 源路径**(A2 OQ1——惩罚上线时本节补 flag_judged{hit=false} → 伏击 +1 源路径 AC,A2 GDD 预告接口漂移);**C1/C2**——encounter 真实供给、跨战斗 HP 持久性、战斗触发(节点移动);**深度高光形态**(即时 HUD 反馈动画——D1/垂直切片);**事件外发载体形态**(实现期 ADR——B1 OQ 登记);**attempt 重试细节**(真 A1 域——B1 只保证 attempt 非 scope 事件,AC-33 以桩承载取号契约)。 + +**加载与 SettlementTable 校验(Core Rule 16)** + +- **AC-1(SettlementTable 加载基线与 schema 定稿——Core Rule 16;根 = b1_default)** GIVEN `b1_default` 经 `--data-root` 注入干净加载,WHEN 冒烟脚本读 SettlementTable,THEN **八字段**在位且:`segment_order` 读值 ≡ 拍板段序 `[grant, item_window, buff_debuff, item, combat, decay, verdict]`(**结构定值恒等断言——加载期校验的行为面**);`effect_pool_map` 四键逐一 = 定稿映射;`pool_clamp_lo/hi` = −100/+100、`round_direction` = "floor"(结构定值);`D_ref` 仅作域断言 [1,999]、`P_pf` 域断言 = {100,200}(knob 纪律不写死——载体域校验见 AC-4);`source_label_map` 键集 ⊇ B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack}(封闭枚举——Core Rule 14 来源标注的表侧承载;hero_attack = 2026-09-11 用户裁决增补)。WHEN 同根跑 B4/B3/B2 各自默认根加载行为(AC 跨引),THEN 零跨表冲突诊断(单根多系统共载——全集成冒烟的加载前提)。〔覆盖 Core Rule 16;Tuning Knobs schema 定稿〕 +- **AC-2(segment_order 违例致命;根 = `b1_segorder_bad`,manifest `segorder_violation`)** GIVEN 段序置换根(如 item_window 与 combat 互换),WHEN 冒烟 runner 以独立 headless 进程游戏通道加载(collect_only=false),THEN 进程非 0 退出且错误清单含 segment_order 类别——"防实现乱序静默漂移"的进程级落点。〔覆盖 Core Rule 16/Core Rule 4 结构冻结〕 +- **AC-3(effect_pool_map 悬空 effect_id 致命;根 = `b1_poolmap_dangling`,manifest `poolmap_dangling`)** GIVEN 映射含 `ghost_effect` 键根,WHEN 独立进程游戏通道加载,THEN 非 0 退出、错误含跨表悬空类别(B4→B3 首引激活先例同款——引用型字段 dangling_ref_policy)。〔覆盖 Core Rule 16;Tuning Knobs 校验行〕 +- **AC-4(p_perfect 域校验——程序构造 + 致命根双腿)** GIVEN SettlementTable schema 类直引实例,WHEN `validate()` 作用于 p_perfect ∈ {99, 150, 1000},THEN 各含域错误类别(**载体域 {100,200}**——150 等非整数倍数乘数无运行时载体,与 2026-09-10 修订同步);p_perfect = 100(仅高光)与 200(真实双份)零诊断。腿 2(loader 集成缝):`b1_pperfect_oob` 独立进程加载 → 非 0 退出(manifest `pperfect_oob`)。〔覆盖 Core Rule 16/Core Rule 13;Tuning Knobs 校验行〕 + +**生命周期编排与触发(编排专章 / Core Rule 1/2)** + +- **AC-5(战斗开始序列全序 + roll_board 恰一次 + gate 置位观测;板 = b_spawn)** GIVEN 全新 B1(Uninit)、真 A3(Idle)、真 B2/B3/B4 全新实例、A1 桩登记首板 = b_spawn、master=42。WHEN `begin_battle(encounter)`,THEN 步骤流/调用捕获流水**全序恰一次**:`set_reload_gate(true)` → B3 `notify_battle_start`(true)→ B2 `spawn`(true)→ A2 `install_board`(accepted)+ B4 `roll_board`(true)→ A3 `notify_round_start`(true、phase=Active);**reward_roll scope 计数器 = +1、board_gen scope = +1(各恰一 scope——B4 义务⑥/roll_board 恰一次的编排半,观测面 = D2 AC-4 公开计数器)**;`query_alive(enemy)` = u_c 个行值快照单位(B2 AC-1 跨引);A2 事件捕获含初始展开批(真 A2 install 行为);B3 `query_active` 双侧空。〔覆盖编排专章战斗开始序列;Core Rule 16①;B4 义务⑥〕 +- **AC-6(结算触发 = Waiting 相位编排点、不依赖事件到达序——A2 义务①)** 腿 1(触发只读观测量):GIVEN 构造至 A3 Waiting 且 `last_round_end` 可用(stopped 轮——经真 A2 `settle_batch([stop])` 到达),WHEN 结算事务受理,THEN 触发判定只读 A3 相位与 `last_round_end`(事件捕获序 = A2 管线序 flag_judged→round_end,结果正常)。腿 2(到达序置换判别):同一 stopped 轮捕获 {flag_judged 批, round_end} 两组消息,**装载层置换两组到达先后**分别驱动(冒烟捕获-重放,不增设 B1 注入 API),THEN 两遍结算步骤流**逐行相同**(到达先后不承载语义——事件序契约仅为数据完整性服务的可判定形式)。〔覆盖 Core Rule 2;A2×3 义务①;registry a2_event_surface.event_order〕 +- **AC-7(段序骨架全序断言——Core Rule 4 / B4 义务①)** GIVEN b_flag 满构造结算(登记构造序列:挖掘 ≥1 奖励格 + 插 (1,4) hit 旗 + 停手收轮;σ 登记使奖励格含道具类身份),WHEN 结算事务解析,THEN 步骤流行序全序断言:**授予段行**(入库道具行 / weaken 兑付归属效果段、溢出/未生效行若有)< **窗口标记**(open——段界锚)< 窗口标记(close/skip)< **效果行**(weaken/伏击授予的结算面读值行)< **道具行**(生效/使用被拒/已消耗未生效)< **伤害行/蓄力行** < **衰减行** < **判定行**(继续/胜负);任一段缺行时以段界锚与行类型域断言序(空段合法——结构冻结承载"恒全序执行");窗口标记**恒存在**(零选择 = skip 标记恒登记——回放输入完整性)。反向腿:segment_order 表值经 AC-2 恒等于拍板序——**段序为结构冻结、实现读表驱动非硬编码**(行序与表值一致性 = 本 AC 与 AC-2 合成判定)。〔覆盖 Core Rule 4;B4 义务①;Core Rule 14〕 +- **AC-8(继续出口 same_board 续轮)** GIVEN b_batch 部分挖掘后 stopped 收轮、结算完成(判定行 = 继续),WHEN 出口分发,THEN 回发捕获恰一条 `notify_settlement_done(same_board)` → A3 `notify_round_start`(true、phase=Active、**ap=AP_r 硬重置、rap 保留**——A3 AC-1b 跨引);A2 板状态延续(旗集/已开集不变——A2 AC-13 跨引);board_gen/reward_roll scope 计数器**零变化**(same_board 零生成零 roll)。〔覆盖编排专章继续腿;A3 Rule 5〕 + +**轮 facts、授予段与高光标记(Core Rule 3/5/13#3)** + +- **AC-9(facts 批处理 + 三恰一次转化——B3 义务② / B2 义务② / B4 义务②)** GIVEN b_flag 登记构造轮(挖掘 n 格含 m_0 雷格 + 插 f 旗含 h_0 hit),WHEN 轮末结算,THEN **每格受理恰一次**:B4 `grant` 调用捕获恰 n_safe 次(n_safe = 挖掘 safe 格数,登记推导)、B2 `ambush_grant_plan` 恰 1 次、B3 `apply_effect(weaken)` 恰 h_0 次、`apply_effect(ambush)` 恰 granted 次——**B1 轮 facts 缓冲保证一事件只进一次转化**(B3 不去重〔B3 义务②的对侧承载〕、B4 幂等仅为兜底:诊断收集器零"重复受理"诊断——幂等兜底未被触发的行为面);处理序 = 事件规范序(B3 义务①"确定次序"的 B1 落地:授予调用捕获序 ≡ A2 cell_revealed/flag_judged 行主序)。WHEN 同轮捕获事件批重复投递(装载层故障注入),THEN 步骤流与容器终态仍 = 单次投递结果(缓冲去重的判别腿)。〔覆盖 Core Rule 3/5;B3 义务①② / B2 义务② / B4 义务②〕 +- **AC-10(授予分流与归因 + initial 不入奖励库——A2 义务②;2026-09-11 增即时类腿)** 腿 1(分流):GIVEN σ 登记使挖掘格身份含 buff 类与道具类各 ≥1,WHEN 授予段解析,THEN buff 类格 → B3 `apply_effect(effect_id, source={safe_cell, origin_cell, round_index})`(B3 `query_active` 六归因字段逐一 = 调用实参——B3 AC-3 跨引);道具类格 → B4 `stock(item)`(B4 `query_inventory` 逐件来源 {cell, round} = 事件格与当轮——B4 AC-13 跨引);**即时类格(instant_aoe/instant_heal)→ 当场兑付:instant_aoe = 逐存活敌 `apply_damage(u, power)` + 单条伤害行(target = all_enemies + count 字段)、instant_heal = `b2.heal` clamp + 治疗行(满血 → "奖励未生效"行零兑付)——零 stock、零窗口(B4 Rule 1/3 两类交付轴的 B1 侧)**;**B4 只定"给哪行"、层数语义归 B3 合并**(同轮双 buff 格 → 单实例 L=2——B3 AC-9 同构)。腿 2(**initial 通道不入库**):GIVEN b_spawn install 后首轮(初始展开 35 格已发生——channel=initial、reward_eligible=false),WHEN 首结算,THEN **grant/stock/apply_effect 调用捕获全零**(R₀ 不入奖励库——A3 EC5 委派闭环的 B1 半边);`channel` 字段零特判(B1 判定只读 reward_eligible——eligibility 封装的行为面:同板 chain/dig 格正常入库形成对照)。〔覆盖 Core Rule 5.1;A2×3 义务②〕 +- **AC-11(满层零兑付 + "奖励未生效"行 + 聚合计数——B4 义务②后半)** GIVEN σ 登记轨迹使同轮/邻轮授予 defense_up 达 M_a 层满(B3 AC-11 构造的全集成版),WHEN 第 M_a+1 层授予受理(B3 apply 返回 false),THEN 步骤流产**"奖励未生效"行**:零折算(无补偿数值)、携带**可聚合计数/批次字段**(同结算多件可聚合——Core Rule 14 协议字段)、source 归因完整;B3 容器逐字不变(拒绝原子——B3 AC-11 跨引);**连锁批量授予溢出 = 显式接受**:b_chain 13 格批量授予腿(σ 登记)中溢出件逐件入行、零转换补偿(B4 E3 对侧)。〔覆盖 Core Rule 5.1;B4 义务②;B3 义务⑧邻接〕 +- **AC-12(标中旗兑付去重 + 高光标记——A2 义务③ / Core Rule 13#3)** 腿 1(兑付与高光):GIVEN b_flag 插 (1,4)(hit)收轮,WHEN 结算,THEN `apply_effect(weaken, source={flag_hit, (1,4), round_index})` 恰一次 + 效果行携带 **highlight 标记**(数据面——创意风险 #3 落地,呈现归 D1)+ 资格消费入集合(B1 自持)。腿 2(**诚实重复上报去重**):续轮(same_board)旗仍在,WHEN 再停手(A2 再次上报 {(1,4), true}——A2 AC-12 分支 3 同构造),THEN **零 weaken 兑付**(apply_effect(weaken) 捕获零新增、B3 weaken 实例数/L 不变)、**零明细行**(重复兑付从未发生)+ debug 诊断可选;资格集合不变。腿 3(拔旗重插不恢复):拔 (1,4) 重插 → 再收轮 → 仍零兑付(A3 Rule 8/OQ5——资格以雷格实例为键)。腿 4(换图重置):AC-28 换图后新板同坐标旗靶 → 兑付恢复(新实例资格自然重置)。腿 5(**误标零值行 + 每实例去重**——2026-09-10 修订):GIVEN 插 (0,3) miss 旗收轮,WHEN 结算,THEN **"标旗未中"零值行恰 1 条**(source=flag_miss、value=0、highlight=`perfect_broken`、可聚合计数字段——"为什么没拿到完美"的归因锚);续轮再停手(A2 重报同批 hit=false)→ **零新增行**(每雷格实例一次去重——防逐轮惩罚轰炸);换图后新板 miss 旗 → 新实例重新产行。〔覆盖 Core Rule 5.2;A2×3 义务③;Core Rule 13#2/#3〕 +- **AC-13(cleared 轮清图奖励——B4 义务③)** 腿 1(主路径):GIVEN b_spawn 挖尽未开 8 格触发 cleared(A2 AC-1② 同构造)、σ 登记清图身份,WHEN 结算授予段,THEN B4 `grant_clear()` **恰一次**(同批次重调幂等兜底——诊断零新增)、兑付分流同 AC-10(source=clear_reward)、**当轮窗口即可选用**(窗口清单含该件——Core Rule 7 窗口锚点收益面);完美板腿见 AC-35 腿1(× P_pf)。腿 2(清图槽 buff 满层 + **高光降级**——2026-09-10 修订):σ 登记清图身份 = defense_up 且已满层,THEN 零兑付 + "奖励未生效"行(合法例外——B4 E11 对侧);**完美板高光行捕获零次**(Core Rule 13#2 降级规则——首份即零兑付不发高光;完美谓词仍成立,对账入账 = 实际兑付 0);对照腿:仅第二份兑付被拒(首份落地)→ 高光行在案且 value = 首份实际当量 + "奖励未生效"行一条。腿 3(换图刷新图零 grant_clear):AC-28 场景中 refresh 图结算授予段 grant_clear 捕获零次(A3 Rule 11——roll 照常、兑付不调)。〔覆盖 Core Rule 5.3;B4 义务③;B3 OQ4 关闭条件〕 + +**伏击转化(Core Rule 6)** + +- **AC-14(雷→伏击恰一次 + cap 预截流 + 溢出行协议——B2 义务②⑥ / B3 义务⑧)** GIVEN b_flag 登记构造轮:单轮挖掘雷格 mines_r = 6(登记走位序列——walk 免费经已开格、6×dig ≤ AP_r;cap = c_r = 4 表读值),WHEN 结算授予段,THEN B2 `ambush_grant_plan(6, c_r)` 恰 1 次 → 返回 {granted=4, overflow=2}(守恒——B2 AC-8 跨引);**前 granted 颗雷(事件规范序)逐颗 `apply_effect(ambush, source={mine_hit, origin_cell, round_index})`**(捕获恰 4 次、B3 伏击池 L=4);**overflow 2 颗逐颗产"溢出未生效"行**(含来源 origin_cell + 可聚合计数字段——B2 义务⑥/B3 义务⑧协议统一);战斗段各敌伤害行 attach 分量 = 4×m_amb(池读值即时性);轮末衰减行 k 含伏击实例(dur=1 蒸发——B3 AC-8 跨引)。每雷恰一次:重复投递判别同 AC-9 腿 2。〔覆盖 Core Rule 6;B2 义务②⑥;B3 义务⑧〕 + +**道具使用窗口(Core Rule 7)** + +- **AC-15(窗口组装 + usable_now 预判 + 选择序 + 空库存跳过——B4 义务④前半)** 腿 1(组装与预判):GIVEN 战斗中 hero 满血(begin_battle 直供 M_h)、σ 登记库存含治疗包,WHEN 结算到达窗口段,THEN 窗口清单 = `query_inventory` 投影 + B1 组装 `usable_now` 字段:**治疗包 usable_now=false(满血禁选——B1 读 B2 hero hp 判定,B4 零跨系统读纪律不破的行为面:B4 诊断零"血量查询"类新增)**;伤害道具恒 true(结构推论——敌死亡只发生于道具段、窗口打开时存活敌 ≥1:本腿断言其投影真值)。腿 2(守卫双查):扮演 D1 强制提交满血治疗包选择,THEN "使用被拒"行 + 包 InStock 不变(B4 `use` 未被调——库存投影不变)。腿 3(**单件制——2026-09-11 拍板改写**,原"选择序 = 使用序"多件腿作废):提交多件 [刀₂, 刀₁] → **仅首件受理**(编排违例诊断 + 刀₂生效、刀₁不消耗——"窗口至多选用一件"的编排守卫面);单件提交 [刀₁],THEN 道具段生效;零选择提交(skip),THEN 窗口标记 close/skip、段序零停顿、库存不变。腿 4(空库存):无库存轮窗口标记照常(skip)、呈现面政策归 D1(数据面 = 窗口清单空)。〔覆盖 Core Rule 7;B4 义务④前半;D1 义务②对侧〕 +- **AC-16(同窗多件提交的编排守卫——B4 义务④腿;2026-09-11 单件制改写)** GIVEN begin_battle hero_hp = M_h − 2(登记直供偏移)、σ 登记库存含治疗包 ×2(power = p_i),WHEN 窗口提交 [包₁, 包₂],THEN **仅包₁ 受理生效**(治疗行 hp' = min(M_h−2+p_i, M_h)——**部分回复钳满**)、包₂ 零调用(InStock 不变)+ 编排违例诊断("窗口至多选用一件"——2026-09-11 单件制,原"逐件动态重判"腿随多件制作废;满血使用期守卫保留为纵深防御——跨结算场景仍可达:本结算被拒后下一结算窗口再判)。〔覆盖 Core Rule 7/EC;B4 义务④〕 + +**增益/减益段(Core Rule 8)** + +- **AC-17(语义映射 + 按授予流水逐条来源还原——B3 义务③⑦)** 腿 1(per-grant 展开):GIVEN 同轮两次 weaken 授予(互异 origin_cell 旗靶),WHEN 增益/减益段解析,THEN **效果行恰 2 条**(按 `apply_effect` 调用流水展开、非按容器单实例——合并实例只持首授归因,per-layer 来源还原的数据源 = B1 授予流水:每行 origin_cell 逐一 = 授予 origin_cell、每行携带 `effect_id` = weaken、**value = 该次授予贡献值(L×m_w 投影)**、**pool_value = 池合成值**(两行 value 各 −65 贡献、pool_value = −130——合成值不入 value 字段,防"两行各 −130%"叠加误读;Core Rule 14 字段分层));B3 `query_active` 读值(插入序+快照载荷)为行值来源(B3 AC-14 跨引)。腿 2(池归属映射):defense_up 行与 weaken 行同轮 → 效果行 pool 字段经 effect_pool_map 自 effect_id 派生 = hero_defense / enemy_output(映射消费的观测面)。腿 3(side-wide):`enemy_multi_unit` 根同轮 weaken L=1 → 战斗段**全体存活敌**伤害行均含减伤合成且 target 字段逐一可归(多敌兼容读法——B3 单敌假设回验后的 B1 侧);target_ref 单体寻址扩展位上线时随批补归集语义(接口漂移注记,非本节断言)。〔覆盖 Core Rule 8;B3 义务③⑤⑦〕 + +**道具段(Core Rule 9)** + +- **AC-18(刀恒定表值 + default_target 顺延 + 击杀进流水——B2 义务⑤ / B4 义务④)** 腿 1(恒定表值判别负断言):GIVEN 同结算 weaken L=1 存活(敌方减益在位——若刀伤吃 percent 修正即在此暴露)、σ 登记库存含刀,WHEN 道具段,THEN 伤害行 value = p_i **恒定**、零乘区(分量字段无池修正项——"不吃任何 percent 修正"的行级判别)。腿 2(目标 + 击杀标记 + target 字段——**2026-09-11 单件制改写**:原"同结算 σ 登记多件刀逐件顺延"随多件制作废——单件制下刀的目标顺延跨结算呈现):GIVEN `enemy_multi_unit` 根(ambusher 行 unit_count=3、余字段默认表值),σ 登记刀 ×3(每结算至多一件、跨结算序列),WHEN 连续结算各用一件,THEN 每条伤害行 target = `default_target()` 返回(顺位最前存活敌)、击杀标记布尔出现于目标 hp ≤ p_i 的件、[0] 死后下一结算顺延 [1]、再 [2] 同构,**击杀标记共 3 个**;被杀敌 Dead 即刻可观测(`query_beat` 冻结——**指针冻结进流水**,H3 抢杀窗口感知数据前置;死亡即不动手:后续结算战斗段 `query_alive` 不含它、零行动行)。腿 3(溢杀丢弃):hp=2 残敌 + p_i=4 刀 → 死、行 value = p_i、击杀标记布尔、无溢出量字段、零转移(下一件打 `default_target` 后继)。〔覆盖 Core Rule 9;B2 义务⑤;B4 义务④〕 +- **AC-19(满血守卫 + 守卫所有权不对称——B4 义务④)** 腿 1(表局部守卫):GIVEN hero 满血 + 库存治疗包(AC-15 腿 2 同构造),WHEN 提交选择,THEN "使用被拒"行、零 `use` 调用(**不烧包**)——满血守卫 = 表局部判定(max_hp 对比),**敌全灭构造下仍拦截**(对照腿:AC-20 场景敌已全灭 + 满血 + 提交治疗包 → 仍拒——守卫不读存活集的不对称可判定形式)。腿 2(clamp 归 B1):AC-16 已载部分回复钳满(hp' = min(hp+power, max_hp))。〔覆盖 Core Rule 9;B4 义务④;EC〕 +- **AC-20(零效果判定读存活集——B4 义务④ / B4 E10 承接;2026-09-11 单件制下改为负空间声明)** 原构造(同结算提交多件刀、末件零效果)随单件制作废——**结构不可达论证**:窗口打开时存活敌 ≥1(Core Rule 7 结构推论)∧ 单件恒打顺位最前存活敌 ⇒ 窗口提交的刀恒有目标,"已消耗未生效"行在单件制下不可达(零效果判定逻辑保留为防御分支——B1 读 B2 存活集;恢复多件制时本 AC 随批复活)。可达残余:多敌根下刀击杀标记断言由 AC-18 腿 2 承载。〔覆盖 Core Rule 9;B4 义务④;EC——负空间声明,B2 "违规路径不可由封闭 API 构造"先例同型〕 + +**战斗段(Core Rule 10)** + +- **AC-21(调用序钉死 + 每敌恰一次 advance + hero 不在推进域——B2 义务①)** 腿 1(grub 平凡环):GIVEN encounter=(L_g,D_g) 单 grub 结算,WHEN 战斗段解析,THEN 调用捕获序 = `query_attack` → `apply_damage` → `advance`(**每存活敌恰一次 advance**:捕获恰 1 次;伤害行 value = D′ 推导)+ 衰减行前完成。腿 2(ambusher 混合拍):encounter=(L_a,D_a) 连续结算 → beat 轨迹 = p_e 序(B2 AC-3 跨引)、每结算捕获 advance 恰 1。腿 3(**hero 不在推进域**):全结算 advance 捕获实参 ∈ 敌句柄集、零 hero 句柄(B2 侧对 hero 句柄拒绝为兜底——B2 AC 前言 API 口径跨引)。腿 4(行主序 + target 字段):`enemy_multi_unit` 根 → 行动/伤害行序 = 生成序且每行 target 字段可归至对应单位(顺位即威胁序的可观测面——Core Rule 14 目标字段)。〔覆盖 Core Rule 10;B2 义务①〕 +- **AC-22(F2/F3 行为级:分量行 / 减伤 / floor / 零值行 / 溢杀)** 腿 1(分量二元组):GIVEN b_mine_walk 单雷轮(L=1)+ grub attack,THEN 伤害行携带 {baseline=a_g, attach=m_amb, 终值} 且 **source_class = enemy_action、target = 受击单位句柄**(P2 归因敌侧兑付面——Core Rule 14 敌方行动来源与目标字段);L=0 轮(未踩雷)attach=0(自然读值非缺失分支)。腿 2(floor 取整):登记构造轮 L_amb=2 + weaken L=1(挖双雷 + 插一 hit 旗同轮——登记序列)→ D_raw = a_g+2×m_amb、D′ = floor(D_raw × (100+P_e)/100 × …) 按表读值推导(F3 Example:10→weaken→3 的记号化——非定值)。腿 3(**零值伤害行——不调 apply_damage**):GIVEN b_flag 双 hit 旗同轮(weaken L=2 → P_pool = 2×m_w;**非平凡前提 m_w ≤ −51**——F0 修订回写后恒满足,前提不满足时本腿 SKIP 标记),WHEN grub attack 拍结算,THEN 效果行显合成值 2×m_w 与终值两端、**伤害行 value=0、`apply_damage` 捕获零次、B2 hero hp 不变、诊断零新增**(负值分支禁令实现面——免伤/零输出必须可见);weaken dur=1 轮末蒸发(下轮恢复常态伤害——判别尾腿)。腿 4(defense_up 顶点恰在地板边界——判据函数腿):P_pool = 2×m_d = −100 恰边界未越域 → M=0 免伤轮合法设计顶点(B3 Tuning Knobs 对侧收口)——以 F2 判据函数对登记常数断言(行为级同路径由腿 3 承载,构造 σ 使双 defense_up 同格可达的成本显式豁免)。〔覆盖 Core Rule 10 / F2 / F3;B3 义务③收口;EC〕 +- **AC-23(charge 蓄力行 + 伏击蒸发——EC)** GIVEN (L_a,D_a) ambusher:结算 1 attack 后,结算 2 构造挖雷 ×1(L_amb=1),WHEN 战斗段(beat=p_e[1]=charge),THEN **蓄力行**(数字兑现面)+ `query_attack` 读值零作用 + **零伤害行**、hero hp 不变;授予段伏击授予行在案(先授予后蒸发的对照——玩家可对照"这轮雷白喂了");轮末衰减清池(下轮 attach=0)。腿 2(全体 charge):`enemy_multi_unit` 根同步轮转至 charge 拍结算 → 恰 u_c_f 条蓄力行、零 apply_damage、hero 承伤 0(合法状态非错误)。〔覆盖 Core Rule 10/EC;B2 EC8 对侧〕 +- **AC-24(hero 死亡短路 + 判定段 hero 先判——B2 义务③)** GIVEN `enemy_multi_unit` 根 + begin_battle hero_hp = 4(直供),WHEN 首结算战斗段(敌[0] attack 分量 ≥ 4),THEN 敌[0] 伤害行后 **hero hp=0 → 短路**:敌[1]/[2] **零行动行、零 advance 捕获**、径入衰减段(照常——结算完整性)→ 判定行 = 战斗失败(**hero 先判防御腿**——双死结构不可达:我方伤敌全在道具段先于战斗段,纵深防御不构造、判定行次序字段可读);battle_over 序列触发(AC-31)。〔覆盖 Core Rule 10/12;EC;B2 义务③〕 +- **AC-25(伏击池共享不消耗)** GIVEN `enemy_multi_unit` 根 + 登记轮 L_amb = 2(双雷),WHEN 战斗段逐敌解析,THEN 敌[0]/敌[1]/敌[2] 伤害行 attach 分量**均为 2×m_amb**(共享、非均分/消耗——每敌 `query_attack` 读同一 L);中途无池写捕获(B2 无池写 API——负空间跨引)。〔覆盖 Core Rule 10;B2 F2〕 + +**衰减段与判定段(Core Rule 11/12)** + +- **AC-26(衰减恰一次、恒执行含末轮——B3 义务④⑥)** 腿 1(每结算恰一):任一 AC 场景步骤流**衰减行恰 1 条/结算**(k 记入行值——B3 `decay_round` 返回值;调用捕获恰 1 次——double-decay 无守卫的对侧 = B1 编排纪律的行为面)。腿 2(**末轮恒执行**):末图清图胜利结算(AC-13 腿 1 场景 + 敌已全灭构造)→ 判定行 = 胜利**之前**衰减行在案(先兑付后衰减时序契约——"最后一轮 decay 判定"义务④的 B1 落点;battle_end 全清兜底使两序同终态、次序钉死为段序 = 行序可断言)。腿 3(空容器):全 dur=1 上轮清空后的结算 → 衰减行 k=0(B3 EC13 对侧——合法零行/聚合零计数)。〔覆盖 Core Rule 11;B3 义务④⑥〕 +- **AC-27(判定三出口)** 腿 1(胜利):敌全灭结算(AC-20 尾轮)→ 判定行 = 战斗胜利 → battle_over 序列(AC-31)。腿 2(失败):AC-24 已载。腿 3(继续):AC-8 已载。判定行公共字段 + 终局行在案(胜负画面数据锚)。〔覆盖 Core Rule 12〕 + +**出口分发、换图与战斗结束(编排专章)** + +- **AC-28(cleared 恒 new_board 换图编排全序——B4 义务⑥ + EC 非法产物腿)** GIVEN b_spawn cleared 判定、A1 桩登记板序列 [板₂](程序构造、坐标级登记),WHEN 出口分发,THEN 调用捕获**全序恰一次**:A2 `retire_board` → B4 `retire_mapping` → A1 `generate`(**board_gen scope +1——attempt 边界非 scope 事件:桩内重试零额外取号**)→ A2 `install_board` → B4 `roll_board`(**reward_roll scope +1——每图恰一次**)→ `notify_settlement_done(new_board)` → `notify_round_start`;新板资格格 roll 照常、grant_clear 零调用(AC-13 腿 3);完美计数随 install 重置(AC-35 腿 4 跨引)。腿 2(**非法产物上抛**):桩登记返回带非法产物标记板,WHEN install(accepted——编排不拒绝产物),THEN B1 **上抛玩家侧呈现请求**(回发捕获含呈现请求事件 + 数据面 = 非法标记;提示/补偿/回退形态归 D1——登记义务);A2 install 若拒绝(不变式违例)= fail-fast 上抛(A2 AC-1b/2 通道跨引,程序级非玩家域)。〔覆盖编排专章换图腿;B4 义务⑥;EC〕 +- **AC-29(挖掘期刷新编排 `refresh_board`——D4 2026-09-12 改写;A3 rap_rules)** 腿 1(当场换图):GIVEN Active 且 rap ≥ rap_refresh_cost,WHEN 扮演 D1 经 `refresh_board` 提交刷新,THEN A3 `request_refresh` → true、rap=0、**phase 不变(无结算段、无 round_end)** → 换图全序恰一次(A2 retire → A1 generate → A2 install → B4 roll_board;board_gen/reward_roll scope 各 +1——D2 快照切点观测面);同轮续挖可证新图。腿 2(cap 内连刷):同轮第二次提交 → 第二次完整编排;腿 3(cap 超限):第三次提交 → 拒绝(resource_face——`refresh_cap_per_round`=2)。腿 4(RAP 不足):rap < cost 提交 → false、状态不变(A3 EC13 对侧——非编排错误)。〔覆盖编排专章;A3 Rule 11/EC13/EC15;原 Waiting 期代理调用三腿随刷新前移作废〕 +- **AC-30(new_board 违约 fail-fast——强制检查点;D4 2026-09-12 改写)** 腿 1(**恒 same_board**):GIVEN stopped 轮与 ap_exhausted 轮(各构造),WHEN 出口分发,THEN 回发捕获 `notify_settlement_done(same_board)` 恰一条、**new_board 回发零次**、retire/generate/roll 捕获全零(刷新已前移挖掘期——本编排无刷新提交面,stopped/ap_exhausted 恒 same_board);**fail-fast 守卫半 = 自守 debug 断言**(内部违约路径不可由公开 API 构造——负空间声明,契约源 = registry `action_economy.rap_rules`,其存在性由 AC-43 扫描域 + 代码评审承载)。腿 2(反向闭合):cleared 轮 ⇒ new_board 恰一(AC-28 全序——两腿合成即契约全貌)。〔覆盖编排专章;A3 rap_rules;EC;强制检查点③〕 +- **AC-31(battle_over 清理全序恰一次 + 关卡末清库 + 拒绝不中断——B4 义务⑤;2026-09-11 关卡域改写)** 腿 1(battle_over 全序):GIVEN 判定行 = 胜/负任一,WHEN battle_over 序列,THEN 捕获**全序恰一次**:B4 `retire_mapping`(映射随战斗消亡——下一场战斗新板新批)→ B2 `despawn_all`(query 双侧空——B2 AC-6 跨引)→ B3 `notify_battle_end`(query 双侧空——B3 AC-2 跨引)→ A3 `notify_combat_end`(phase=Idle、**rap=0**——A3 AC-11 跨引)→ `set_reload_gate(false)`;**`clear_inventory` 不在 battle_over 序列中**(库存关卡域跨战斗延续——其缺席本身为断言腿:battle_over 后 query_inventory 逐字不变)。腿 2(关卡末清库):GIVEN 末层 victory 或 defeat,WHEN 编排方调 `level_clear()`,THEN `clear_inventory` 恰一次 + **返回清空前快照计数**("剩余道具 ×N 随关卡结束清空"反馈行——D1 义务⑤数据面;折算结构位归 C2);此后 stock/use → false + 诊断(B4 AC-3 分支 3 跨引)。腿 3(拒绝不中断):清理序列中注入重复调用(如 B3 域闭后再 end——诊断注入构造),THEN debug 诊断 + **序列剩余步骤照常完成**(幂等守卫兜底 false——防半清理态泄漏:后续 `begin_battle` 正常受理为尾腿)。〔覆盖编排专章 battle_over/level_clear 腿;B4 义务⑤;EC〕 +- **AC-32(reload gate"战斗中真的被置位"——强制检查点;D2 EC8 的 B1 侧)** 腿 1:GIVEN AC-5 战斗开始序列完成后(gate 捕获 = true 在案),WHEN 经 ConfigDB `reload()`(debug 构建),THEN **返回 false、ConfigDB 值不变、`config_reloaded` 不发**(D2 AC-16 的行为级 B1 侧——"战斗中真的被置位"由编排真调用承载,非模拟置位);战斗中经测试通道换根直调 `load_tables(collect_only=true)` 读值不受门约束(通道区分——D2 双通道口径注记)。腿 2(复位):AC-31 battle_over 完成后 `reload()` → true 且 `config_reloaded` 恰一次(D2 AC-16 跨引)。〔覆盖 Core Rule 16;D2 EC8;强制检查点①〕 + +**可回放(Core Rule 15)** + +- **AC-33(结算可回放——强制检查点;D2 AC-15 deferred→B1 兑现)** GIVEN 两套全新全栈实例(同 master=42、同根、同板登记序列、同 encounter),WHEN 同一**意图序列 + 窗口选择序列**驱动两遍(定长场景:含挖掘/连锁/插旗/停手/窗口选用/刷新换图至少各一——覆盖两类回放输入),THEN 两遍**步骤流逐行相同**(行数/行序/全字段含 value 与归因——同 master_seed 同意图序列同窗口选择序列 ⇒ 逐行相同;2026-09-10 注:新增 target/effect_id/pool_value 字段均为输入的确定函数,逐行相等断言域含之、回放语义不受损);判别腿:仅窗口选择序列不同(同意图)→ 步骤流在窗口标记处分歧、其前逐行相同(**窗口选择序列确为回放输入**的判别形式)。确定性来源行为面:B1 全程 `randomize`/RNG 零调用(AC-43)+ 表值快照(B2 AC-11/B3 AC-15/B4 AC-15 跨引)+ attempt 非 scope 事件(AC-28 腿 1)。〔覆盖 Core Rule 15;强制检查点②;D2 AC-15〕 +- **AC-34(scope 切点契约)** GIVEN AC-29 腿 2 连环刷图场景,WHEN 任一结算回放切点取快照(master_seed + 双流 scope 计数器 + 意图序列 + 窗口选择序列),THEN 无在飞流实例跨界(切点时刻双流计数器 = 已完成 install 数——桩与 B4 取号契约的观测面);续玩快照恢复驱动(D3 消费语义)= 端到端集成冒烟 deferral(生效条件 = D3 实现)。〔覆盖编排专章回放切点;Core Rule 15〕 + +**高光兑现节点(Core Rule 13)** + +- **AC-35(perfect_sweep 双向 + 真实双份兑付——强制检查点 #2)** 腿 1(触发 + **真实双份**——2026-09-10 修订):GIVEN b_flag 构造零误标 ∧ ≥1 标中(仅插 (1,4) hit 旗、无误标旗)→ 挖尽 cleared,WHEN 清图兑付,THEN **高光行**在案(value = 实际有效兑付当量)+ **清图奖励身份第二份兑付捕获恰一次**(道具类身份 → B4 `stock` 捕获恰 2 次 / buff 类身份 → B3 `apply_effect` 捕获恰 2 次——玩家实得两件;`grant_clear` 本身仍恰 1 次;等价入账 = power × P_pf ÷ 100 记号推导——**换算 ÷100 成文**,非新分源换算率)。腿 2(误标破完美):同构造 + 插 (0,3) miss 旗(hit=false ≥ 1)→ **无高光行**、正常兑付 + **"标旗未中"零值行在案(highlight=`perfect_broken`——AC-12 腿 5 同基)**(全有或全无谓词、无部分完美档)。腿 3(零标中):全板零旗 cleared → 无高光行(hit=true = 0 不满足谓词)。腿 4(刷新图不触发 + 计数重置):AC-28 换图后新板零 flag_judged → 完美谓词域 = 本板自 install 起(跨图不累积——前板误标不污染新板:构造前板含误标 + 换图后新板零误标有标中 → 新板 cleared 触发高光——**重置的判别正腿**);且刷新图本身零 grant_clear 无兑付点。腿 5(雾旗零特判):b_fog 雾内插旗计入误标(hit=false——A4 EC5)→ 破完美(无变异分支)。〔覆盖 Core Rule 13#2;EC×2〕 + +**公式闭合对账(F0–F5——B3 义务⑤ / B2 义务④)** + +- **AC-36(F1 对照表以 B3/B4 实例为对照实物——B3 义务⑤;条款①)** GIVEN 默认根 + 登记构造(每活行一 B3 实例:defense_up L=1 / weaken L=1 / ambush L=1×k=1;clear_knife 经 B4 grant_clear 快照),WHEN 结算兑付读值(query_active 快照载荷 / grant_clear power)经统一换算式 E(·)(SettlementTable `D_ref` 读值),THEN **E_inst 结论与登记对照表一致**:E_defense = |m_d|/100 × D_ref、E_weaken = |m_w|/100 × D_ref、E_ambush(k=1) = m_amb、E_clear = power ×(perfect ? P_pf ÷ 100 : 1)(**真实双份**——完美板两件实得、总当量 = 2×power;**单一线性换算**——全四源同式、零分源改率的行为面 = 各行共用同一 D_ref 读值);**锚两腿断言(B1 强制检查点执行器)**:E_weaken > E_defense(条款②腿一严格大于)∧ E_weaken ≥ E_dig 推导值(腿二 1:1 零链基准——登记常数);**生效前提 = registry 回执 #1/#2 落地(m_w 修订回写)**——现值 −25 下本断言失败即 F0 裁决未兑现的正确信号(非假失败;不写软口径)。**attack_up 死行**:hero_offense 池映射在位(结构)∧ 全结算伤害行/效果行零 hero_offense 消费点(MVP 内容态断言——复活走 B4 OQ1 连锁)。〔覆盖 F1;Core Rule 13;B3 义务⑤〕 +- **AC-37(F2 判据函数对账——B3 义务③收口)** GIVEN F2 闭合判据函数(语义映射 → 池加算 → clamp(W_d) → 乘区 → floor),WHEN 以登记构造跑 F2 三例读法(weaken AP 界 8×m_w / 表界 16×m_w / defense_up 顶点 2×m_d——全表读值记号),THEN 判据结论逐一与 F2 表一致:两越域例 clamp 后 M=0、顶点例恰边界未越域;**地板交叉层数 = ⌈100/|m_w|⌉ = 2**(F0 值域 [60,85] 全域恒 2——表读值推导);取整方向 = floor 单一规则(腿:半值例 D′ = floor 断言)。行为级交叉锚 = AC-22 腿 3。〔覆盖 F2;B3 义务③〕 +- **AC-38(F3 判据函数对账——B2 义务③成文)** GIVEN F3 判据函数(三分支全序 + 负值禁令 + 死亡边界),WHEN 以登记例跑(grub L=2 → weaken → floor 3 记号 / knife_adv p_i vs hp=6 残敌 → 死 + 溢杀丢弃 / D′=0 → 不调 apply_damage 分支),THEN 结论与 F3 一致:零负值(任意输入组合判据函数输出域 ≥ 0——参数化扫描)、Dead ⇔ hp=0 零负 hp 中间态、hero 先判优先级;行为级交叉锚 = AC-18/20/22/24。〔覆盖 F3;B2 义务③〕 +- **AC-39(F4 条款③闭合对账——B2 义务④;强制检查点)** GIVEN F4 闭合判据函数 + 登记常数(A1 参考行参数 |M|/|S|、ι = 行 target 代 p10 **乐观代理**、m_d/D_a、m_amb、D_ref 全表读值/登记推导),WHEN 闭合判据函数对 L1/L2 两板形与满配语境(unit_count=3)跑配对主式,THEN 结论与登记一致(**双口径登记 2026-09-10 评审**):L1 裕度 ≈2.0× ✓ / L2 ≈2.6× ✓(锚行口径;**期望口径 ≈1.2×/1.5× 同向通过——双口径断言,防虚高裕度误用**)/ **满配语境最坏盲点破线 ✗ → C3 调参约束输出**(对账结果 = 约束清单——C3 消费 B1 输出的事实依赖面);**适用域守卫腿(2026-09-10 补)**:登记构造 n_s < 1(残余集为空)→ 主式不评估、信息端平凡腿接管(零赌博板形不误判破线);信息端平凡腿 P(安全)=1 → V_safe > 0 恒真(登记面);registry `clause3_supply.direction_precheck` 退役注记 + 双口径重算值登记存在性(对账清单 #3 的 AC 侧)。**A1 actuals p10 交付后强制重跑**(缺口预检 4)。〔覆盖 F4;B2 义务④;强制检查点条款③〕 +- **AC-40(F5 条款⑤闭合对账——强制检查点;2026-09-10 模型修订:挖穿残余池口径)** GIVEN F5 闭合判据函数(**修后模型**:n_s = (1−ι)\|S\|、E[踩雷] = \|M\|×n_s/(n_s+1)、X_res = E[踩雷]×V_mine、Δr = ⌈(n_s+E[踩雷])÷AP_r⌉、C_eff = C×(perfect ? P_pf ÷ 100 : 1))+ 登记常数(ι/P(雷)/E_dig 权重推导/C=clear_knife power/P_pf/AP_r/fog_burden_ratio 登记代表值——全表读值/登记推导),WHEN 主判式对 **L1 无雾态/L2 雾态两板形 × {unit_count=1, unit_count=3} 四态组合 × {完美/非完美} 分支**跑,THEN 结论与**登记约束包**一致(2026-09-10 修订登记:L1 −5.3/+2.7、L2 −10.1/−2.1、L1 满配 −49.6/−41.6、L2 满配 −97.5/−89.5——**条款⑤未闭合,verdict = 未证明的约束生成器**);实现率折减腿(E_dig×0.6)同向更差读数在案;**保守地板需求输出**(L1 ≥ 46.1 / L2 ≥ 91.8——C 量级上调整向约束);**停手侧 RAP 零价假设登记存在性**(模型边界注记——判据函数输入分解可读);护栏 A(雾格净优势 +2.4 ∈ (0, 3]——B4 pricing_constraints③ 判据)✓ 与护栏 B **双读数**(D_ref 口径 C_eff = 16 ≤ 24 ✓ / DPS 严格读数越线——裁决项登记)双断言;**A4 三腿入账核对**:雷险消解腿(雾区无雷)/ 信息双向腿 / AP 腿(链不可达全价 Δr 增量)逐腿在判据函数输入分解中可读(variation_table.fog_ap_note 消费面)。全条为对账断言——**非 playtest**(弃图率等观测面归 C3 playtest,out-of-scope)。〔覆盖 F5;强制检查点条款⑤〕 +- **AC-41(F0 registry 对账 + SettlementTable registry 一致)** GIVEN 默认根加载,WHEN 脚本解析 `design/registry/entities.yaml`,THEN:① `status_effect.anchor_warning` 含 **F0 裁决回执注记**(归一口径 = 每结算瞬时当量 / 值域 [60,85] 默认 65 / 地板交叉层数 2 / defense_up 参照保持——对账清单 #1);② `status_effect.mvp_content.weaken` 值域修订注记 + `clause3_supply.direction_precheck` 闭合回执注记(#2/#3);③ `clause5_dominance`(或并入条目)注记存在(#4);④ **`settlement_table` 条目存在**且**八字段名**与 SettlementTable schema 逐一相等(pool_clamp_lo/hi 两字段——2026-09-10 统一口径)、effect_pool_map 四键一致、p_perfect 载体域 = {100,200}(#7——**生效前提 = Phase 5b/5d 回填完成**,不写软口径);⑤ knob 活值(D_ref/P_pf/m_w)不作定值对账(A4 AC-8 豁免同款)。〔覆盖 F0(裁决的 AC 形态 = registry 对账);Core Rule 16;对账清单 #1–#4/#7〕 + +**流水协议与静态扫描(Core Rule 14/1)** + +- **AC-42(行协议封闭枚举 + 字段分层 + 无来源行零容忍)** GIVEN 全部 AC 场景步骤流捕获全集(冒烟汇总断言——单用例跑全部行类型构造场景集),WHEN 逐行校验,THEN:`type` ∈ 封闭枚举 **13 类**(效果/伤害/治疗/道具/奖励未生效/已消耗未生效/使用被拒/溢出未生效/**标旗未中**/蓄力/衰减/判定/高光——封闭枚举即负空间:新增行类型 = 接口漂移);每行公共字段 `type/source_class/origin_cell/round_index/display_name/value/highlight?` 全在(**origin_cell 板级行可空**——Core Rule 14 可空声明;highlight 可空枚举 {flag_hit, clear_reward_h2, perfect_broken});**行类型特有字段断言**:伤害行/道具行(生效)携 `target`;效果行携 `effect_id` + `pool_value` 且 value = 单次授予贡献值语义;**source_class 值域 ⊆ source_label_map 键集 ∪ 判定/道具扩展 ∪ {enemy_action, flag_miss}**(`source_label_map` 表读值——新增 source_class = 接口漂移的表侧闸);**无来源行 = 断言失败**(P2"每行能回答从哪来"的兜底判据——Player Fantasy 反向护栏的机器可判定形式);未生效类四行 + 标旗未中行各携带聚合计数/批次字段(非空)。〔覆盖 Core Rule 14〕 +- **AC-43(静态扫描——编排唯一性单向检查 + 零随机/零 UI/禁 await;Core Rule 1)** GIVEN B1 文件集(编排器/判据函数/步骤流/冒烟脚本/夹具规格)与全项目非 tests/ 文件集,THEN:① **零 `randomize()`/`RandomNumberGenerator`/自建 RNG**(零随机——`rng_service` 流封闭不含结算流的代码侧对应;行为级硬证据 = AC-33);② 零 `extends Control/Node2D`、零 `.tscn` preload + AC-5 headless 实跑通过(合成判定——D2 AC-10 同款);③ **结算路径 `await` 零命中**(可回放步骤流承重约束——扫描域 = B1 结算/编排文件集,逐字面 warning 级 + 人工复核,D2 AC-14 纪律);④ **编排唯一性单向扫描**:全项目非 tests/ 文件集中 B2/B3/B4 API 符号(封闭枚举清单入扫描配置)命中文件 ⊆ B1 文件集("任一下游被 B1 以外语调用 = 架构违约"的机器可判定形式;B2/B3/B4 各侧 AC-17/19/21 的"相邻零引用"反向半句与此合成闭合);⑤ D2 AC-14 人工三要素字面量扫描(零玩法数值字面量的行为级硬证据 = AC-22/36/39/40 全表读值推导 + AC-41 registry 对账);⑥ A2/B3/B4 相互直调零命中(各侧"零直接接口"的对侧确认——扫描域同④)。〔覆盖 Core Rule 1;编排不变式③〕 + +**覆盖矩阵(qa-lead 交付,验收评审用)** + +- **Core Rules**:1→AC-43(单向扫描)+AC-33(零随机行为级)· 2→AC-6 · 3→AC-9 · 4→AC-7+AC-2 · 5→AC-9/10/11/12(腿5=标旗未中行)/13 · 6→AC-14 · 7→AC-15/16 · 8→AC-17 · 9→AC-18/19/20 · 10→AC-21/22/23/24/25 · 11→AC-26 · 12→AC-24/27 · 13→AC-35(#2 真实双份)+AC-12(#3+腿5)+AC-36(P_pf 入账)· 14→AC-42(13 类+字段分层)· 15→AC-33/34 · 16→AC-1/2/3/4/32/41 +- **Formulas**:F0→AC-41(registry 对账形态)· F1→AC-36 · F2→AC-37+AC-22 腿3 · F3→AC-38+AC-18/20/22/24 · F4→AC-39 · F5→AC-40 +- **上游义务 23 条**:B3①→AC-9 · ②→AC-9 · ③→AC-37/22 · ④→AC-26 腿2 · ⑤→AC-36 · ⑥→AC-26 腿1 · ⑦→AC-17 腿1 · ⑧→AC-14 ∥ B2①→AC-21 · ②→AC-9/14 · ③→AC-24/38 · ④→AC-39 · ⑤→AC-18 · ⑥→AC-14 ∥ A2①→AC-6 · ②→AC-10 腿2 · ③→AC-12 腿2/3 ∥ B4①→AC-7 · ②→AC-9/10/11 · ③→AC-13 · ④→AC-15/16/18/19/20 · ⑤→AC-31 · ⑥→AC-5/28——**23/23 全落位** +- **强制检查点 5 项**:reload gate→AC-32 · 可回放→AC-33 · new_board 违约→AC-30 · roll_board 恰一次→AC-5/28(scope 计数器观测)· 衰减恰一次→AC-26 +- **SettlementTable 校验**:segment_order→AC-2 · 悬空 effect_id→AC-3 · p_perfect 域→AC-4 · schema 定稿→AC-1/41 +- **EC(26 条)**:末图清图→AC-13/26 腿2/27/31 · hero 短路→AC-24 · 双死 hero 先判→AC-24 注(结构不可达不构造)· D'=0→AC-22 腿3 · 溢杀→AC-18 腿3 · charge 蒸发→AC-23 · 全体 charge→AC-23 腿2 · L=0→AC-22 腿1 · 同窗多包→AC-16 · 部分回复→AC-16/19 腿2 · 刀全灭→AC-20 · 空库存→AC-15 腿4 · RAP 不足→AC-29 腿3 · 连环刷图→AC-29 腿2 · 未刷新→AC-30 · 非法产物→AC-28 腿2 · 清理被拒→AC-31 腿2 · 战斗中 reload→AC-32 · 衰减空→AC-26 腿3 · 清图 buff 满层→AC-13 腿2(高光降级)· 完美刷新图→AC-35 腿4 · 标中+误标→AC-35 腿2+AC-12 腿5(标旗未中行)· 多敌共享 L→AC-25 · 死亡即不动手→AC-18 腿2 · 零伤害死局→负空间+遥测锚 OOS(C3/playtest)· 结算开始 hp=0→负空间 debug 断言(自守,前言声明)——**呈现半边全部 OOS 至 D1/B5(生效条件 = D1 义务台账登记)** +- **无洞声明**:Core Rules 16/16、F1–F5、义务 23/23、检查点 5/5、SettlementTable 校验 3/3 全部映射 AC;EC 除 2 条负空间自守(不可由公开 API 构造——B2 实例不变式先例)与呈现/playtest OOS 外全部 G-W-T 或对账断言承载。 + +**缺口预检(qa-lead;每项标 owner 与时机——B2/B4 先例)** + +1. **【阻塞级】A2 事件载体 ADR 依赖**:经真 A2 事件流驱动 B1 的 THEN 子集为纸面契约直至 A2 OQ2 落地;A2 半真备选(注入面+A3 真)已登记为过渡载体、断言口径不变。owner:实现期 ADR(与 A2 OQ2 同一裁决);时机:Technical Setup。 +2. **【阻塞级】σ 登记轨迹与取号约定**:身份依赖腿(双包/刀全灭/清图槽身份/完美板布置)依赖 σ 登记轨迹与生成器取号约定(B4 缺口 8 同源);登记后须第二人独立推导复核。owner:qa/实现期 ADR;时机:冒烟框架搭建 + ADR 定名。 +3. **m_w 修订回写未落地**:AC-36 锚腿为 F0 裁决执行器——registry 回执 #1/#2(接口漂移:B3 行表/Tuning Knobs/registry)完成前失败属正确信号;B3 AC-14 腿 3 非平凡性随批复查。owner:B4/B1 共裁 + C3;时机:接口漂移回写批(B1 定稿 Phase 5b 同批)。 +4. **A1 actuals p10 未交付**:F4/F5 以行 target 代 p10 乐观代理——交付后强制重跑 AC-39/40 并同步 registry(对账清单 #6);连带 |M|/|S| 分布与 fog_burden_ratio p50/p90。owner:A1(交付)→ B1(重跑);时机:A1 F4 报告 C 交付。 +5. **C1/C2 encounter 供给**:begin_battle 入参直供为 provisional——C1 定稿后接入真实遭遇上下文与跨战斗 HP 持久性,AC-5/24 的直供构造改为真实供给链。owner:C1/C2;时机:C1/C2 GDD。 +6. **【阻塞级】D1 呈现义务台账**:A2×6/B2×4/B3×4/B4×5/B1×3(高光行强调/完美排雷呈现/EC5 零合法板提示)缺项即 blocking——本节 AC 全部以数据面断言、呈现零覆盖。owner:D1;时机:D1 GDD 门禁评审。 +7. **SettlementTable registry 条目回填**:AC-41④ 生效前提 = Phase 5b/5d 回填 `settlement_table` + 对账清单 #1–#4 注记——回填未完成则 AC-41 失败(正确信号)。owner:本 GDD 定稿批;时机:定稿回填。 +8. **B1 扫描域成员清单登记**:AC-43 单向扫描的 B2/B3/B4 API 符号清单与 B1 文件集须登记入 D2 AC-9 项目级 CI 扫描配置(B3/B4 先例)。owner:Technical Setup;时机:B1 GDScript 产物落地即登记。 +9. **C3 调参对账重跑流程**:D_ref/P_pf/m_w 任一调参 → AC-36/39/40 + AC-2–4 夹具登记值连锁重跑(Tuning Knobs 联动义务的流程化);调参流程无此步骤则相关 AC 假失败。**2026-09-10 扩表(F4 结论④/护栏 B/条款⑤约束包联动)**:A1 行参数(mine_density/min_safe_count/ι target)与 B4 奖励权重/power 调参 → 同批重跑 AC-39/40(F4/F5 输入面);**A1 新增产出义务 = R₀ 规模分布上界(F4 报告 C 扩列)**——护栏 B \|R₀\|_max 的登记供给方。owner:C3(调参)+ qa(重制)+ A1(R₀ 分布产出);时机:每次调参 / 报告 C 扩列交付。 +10. **A1 桩 → 真 A1 全链路**:换图编排的 generate 真实集成(含 attempt 重试不重开 scope 语义、非法产物真分布)登记端到端集成冒烟(与 A1×A2 双包含端到端同槽)。owner:qa/A1;时机:A1 实现落地。 +11. **零伤害死局遥测锚**:EC 死局组合 B1 不设自愈分支——战斗轮数异常遥测的口径登记(进 C3/OQ 清单)。owner:C3/playtest;时机:垂直切片 playtest。 +12. **flag_miss 源路径预留**:A2 OQ1 标错惩罚上线时,本节补 `flag_judged{hit=false}` → 敌方伏击 +1 源路径 AC(A2 预告接口漂移——source_class 新枚举走 B3 修订)。owner:B1/A2/A3 联合;时机:惩罚实测采纳后修订批。 + +## Open Questions + +1. **事件总线载体**(owner:实现期 ADR——与 A2 OQ2 同一裁决;deadline:Technical Setup):signal vs 回调注册 vs 直连调用;B1 触发机制(事件订阅 vs 相位轮询)与 A2 事件捕获断言面同批落地。TD 门禁 2026-09-07 登记项的 B1 侧落点;AC-6/AC-33 相关 THEN 为纸面契约直至落地(缺口预检 1)。 +2. **m_w 修订终值共裁**(owner:B4/B1 锚 owner 共裁 + C3 调参;deadline:B1 /design-review 后的接口漂移回写批):F0 已裁值域 [60, 85]/默认 −65 与归一口径(瞬时);终值确认 + B3 StatusEffectTable 行表/Tuning Knobs/registry 三处回写执行(缺口预检 3);B3 AC-14 腿 3 非平凡性随批复查。 +3. **A1 actuals p10 交付后条款③⑤强制重跑**(owner:A1 交付 → B1 重跑;deadline:A1 F4 报告 C 交付):ι 以行 target 代 p10 为乐观代理——重跑 AC-39/40 + 同步 registry 重算值(对账清单 #6;缺口预检 4);连带 fog_burden_ratio p50/p90。 +4. **连锁规模分布供给方悬空**(owner:A1/A2 + B1 共裁;deadline:A1 报告 C 扩列或 A2 telemetry 定稿时):条款②第二腿(连锁放大边际基准)校准需要 c̄(每安全格连锁期望放大)——现无既定供给方;候选 = A1 F4 报告 C 扩列 or A2 遥测(Formulas 对账清单 #8 附注)。供给落定前第二腿以 1:1 零链下界运转。 +5. **EC5 零合法板玩家侧呈现形态**(owner:D1 裁决;deadline:D1 GDD):提示/补偿/回退三形态选型——B1 数据面(非法产物标记上抛)已就绪(AC-28 腿 2);B1 域内不做补偿逻辑(候选"确定性回退已知好种子"归实现期 ADR——A1 EC5 登记对侧)。 +6. **C1/C2 encounter 供给接线**(owner:C1/C2;deadline:C1/C2 GDD):begin_battle 入参直供(provisional)改真实遭遇上下文 + 跨战斗 HP 持久性(B2 hp_persistence 对侧承接);战斗触发(节点移动碰非奖励点)归 C1(缺口预检 5)。 +7. **深度高光形态**(owner:D1/B5;deadline:垂直切片):#3 标中旗高光的二期形态(即时 HUD 反馈动画/音画同步)——MVP 已落数据面标记 + 当轮兑付(Core Rule 13);旗落格高亮/姿态反馈等表现形态随 art-bible 与"战斗表现美术测试"定。 +8. **标错惩罚 flag_miss 源路径**(owner:B1/A2/A3 联合;deadline:A2 OQ1 实测采纳后修订批):候选"敌方伏击 +1"上线时——flag_judged{hit=false} 转化路径 + B3 source_class 新枚举(B3 结构冻结项走 GDD 修订)+ 惩罚重复性裁决(A2 OQ1 增补项:hit=false 无去重的不对称)联动关闭(缺口预检 12)。 +9. **hero atk 激活裁决参与**(owner:B4/B1;deadline:B4 OQ1 / C3 首调):激活须与 attack_up 死行复活联动(hero_offense 池消费点上线 → 对照表回迁 + AC-36/AC-18 死行断言连锁回写)——B2 Overview 胜利路径声明的后续;MVP 维持 atk=0。 +10. **B2 OQ1 两 face 归属**(owner:实现期 ADR;deadline:Technical Setup):`query_attack`/`ambush_grant_plan` 归 B2 供给面 vs B1 编排面——裁决归 B1 则 AC-14/22 相关腿迁移至 B1 冒烟(断言口径不变)。 +11. **零伤害死局遥测锚口径**(owner:C3/playtest;deadline:垂直切片 playtest):战斗轮数异常遥测的阈值与埋点(EC 死局组合 B1 无自愈分支——护栏 owner = 实现期 ADR/D2 跨字段校验,B2 OQ9 并案)(缺口预检 11)。 diff --git a/design/gdd/legacy-2026-09-13/b2-enemy-system.md b/design/gdd/legacy-2026-09-13/b2-enemy-system.md new file mode 100644 index 00000000..e6d8616e --- /dev/null +++ b/design/gdd/legacy-2026-09-13/b2-enemy-system.md @@ -0,0 +1,354 @@ +# B2 敌人系统(Enemy System / 战斗单位系统) + +> **Status**: Approved(2026-09-09 lean 定稿 + 同日 full 评审〔六专员 + CD 终裁〕NEEDS REVISION,8 blocking 同日核销后用户拍板收口——A3/A4/A1/B3 同日核销先例,免独立 re-review。评审日志:`design/gdd/reviews/b2-enemy-system-review-log.md`)。lean 门禁:systems-designer 起草 Formulas · qa-lead 起草/验证 AC;CD 视角随 full 评审补位。full 评审 8 blocking:当前内容态无胜利路径声明(Overview/Dependencies)· AC-7 恒假判别前提 + SKIP · 多敌夹具根改 ambusher 行 · OQ6 事件源类更正 + F3 重验触发补支 · 精英/Boss 编码位预登记 OQ7 · Rule 8 预告时序⑤ · 资产下限补受击/死亡/顺位 · 主角推进域排除(advance/query_beat 对 hero 拒绝)。CD 视角与 Player Fantasy/Visual/Audio 人工复核已随 full 评审补位 +> **Author**: user + agents(systems-designer〔Formulas〕· qa-lead〔AC + 缺口预检 7 项〕;九项结构决策〔多敌/HeroTable/视觉推测型/Peglin 行为环/每行集一敌种/side-wide/固定顺位/unit_count 字段/cap=4〕为用户拍板) +> **Last Updated**: 2026-09-09 +> **Implements Pillar**: P3(主——雷→敌方优势的收件人与同源兑付面)· P2(敌方增益行可归因)· P1(视觉推测型读怪 = 规划素材)· P4(间接——地牢住户) +> **Layer**: 2 Core · **Milestone**: MVP · **设计顺序**: 第 7 位 + +## Overview + +B2 是战斗域的**单位层**:定义"战斗双方是谁、各自有多强、带着什么词条、怎么受伤与死亡"。数据侧,它持有 D2 `EnemyTable` 的回填权——敌人类型(MVP 仅 1–2 种)、单位属性、雷触发的敌方优势词条(**伏击清单**)、意图可见性字段,行键 `(level_index, node_depth, enemy_id)`;行为侧,它承载 CD 门禁扩容的**战斗单位接口**——主角与敌人共用同一套受伤/死亡语义(扣血至 0 即倒下,双方同一判定),敌人以此成为自动战斗结算(B1)的操作对象。玩家不操作 B2 的任何部分——敌人由系统驱动、行为全确定(零随机,`rng_service` 流封闭,P1);但玩家每一轮都在**阅读**它:结算明细里"敌方获得伏击 +N"的行(P2 归因)、意图可见性预告的下轮威胁(P1 规划素材)。它是 P3 的主承载:**雷的代价需要一个收件人**——开雷喂给敌人的每份优势都落在 B2 定义的词条上,标中旗削弱的也是同一个它。 + +没有 B2,三件事失去落点:踩雷的敌方优势没有受益者(P3 断裂);胜负判定没有主体("敌人全灭 → 战斗胜利"无从判定);C3 的难度曲线没有施加对象("越下越难"成空话)。B2 的边界:它管单位"**是什么、多强、有什么词条、如何死**";"每轮按什么顺序怎么结算"归 B1 SettlementTable,"单位随冒险怎么变强"归 C3,"击杀后掉什么"归 B4。机制与内容分离(B3 先例):机制归本 GDD,内容行 MVP 收窄为 1–2 敌种 + 少量词条,名录扩展归垂直切片/Alpha。 + +MVP 战斗形态采**单敌人**假设(B3 Rule 1 回验义务的落点,见 Core Rules):一场战斗一个敌人单位,目标选择在 MVP 退化为恒定映射——但规则仍成文(目标选择规则是索引登记的 B2 职责),为多敌扩展预留语义位、不实现。 + +**基础攻击恒存在(2026-09-11 用户裁决——取代原"当前内容态无胜利路径"声明〔2026-09-09 full 评审 blocking ①,历史语境见 reviews/ 日志,不在此复述〕)**:HeroTable.atk = **基础攻击**,域 **[1,999]**——0 = 加载期校验拒绝,"0 基线 × 百分比增益恒零"的死锁自 schema 层杜绝,开发期任何内容态都不可能出现;MVP 值 = 6(原型锚 HERO_BASE_ATK,provisional 归 C3)。主角每结算战斗段**先手攻击**顺位最前存活敌(编排归 B1 战斗段;攻击是固定动作、非行为环——Rule 4 环仍为敌单位专属),attack_up 等百分比增益自此有恒定消费面。"敌人全灭 → 战斗胜利"(Rule 1 / game-concept 轮次流程 4)由**基础攻击 + 奖励侧增量**共同承载:B4 道具/增益是基础攻击之外的**规划面伤害**(囤刀、抢杀、爆发时点的决策素材),不再是唯一伤害来源;P2 归因不受损——每一分伤害仍逐行标注来源(hero_attack / 道具逐件实例)。原"atk MVP=0 机制开关、胜利路径依赖 B4 先行"条款与 OQ3"左臂载体悬空"的投影描述一并退役。 + +*(实现口径注:单位/词条的类结构与事件载体不在本 GDD——实现期 ADR 域。)* + +## Player Fantasy + +**直接承载**(Section A 定框答案:玩家不操作敌人,但每一轮都直接感受它)。B2 的情绪目标一句话:**"对面是个看得懂的对手,不是一个血条"**——它的威胁可推测(读姿态就知道下一拍是什么、大约多重)、可归因(它变强是因为我开错了雷)、可拆解(我标中的旗正在削弱它)。玩家对 B2 的正确感觉是"**赌桌对面的那个存在**":它不是环境伤害发生器,而是与我的排雷决策实时联动的一方——我谨慎,它就弱;我贪婪,它就一口一口吃壮。 + +体验锚点(按支柱): + +- **P3 同源的对面对象**:结算明细里"敌方获得伏击 +3(第 4 轮 · 雷)"的行,玩家能指认"这是我开的那颗雷喂的"——敌人的强度是玩家扫雷决策的**镜像**,威胁有源可溯。 +- **P1 可推测的威胁**:敌人不预告精确数值——"它弓起背了,下一拍是重击"式的读怪推理进入挖掘决策(形态↔行为映射稳定可学,MVP 双环最长 3 拍);精确数字在结算明细中兑现,结算前的世界靠观察而非读表。 +- **P2/P3 的拆台兑现**:标中旗的 weaken 落在同一个它身上——削弱行与伏击行在明细里使用同一呈现语言,玩家由此学会"它强是因为我喂的,它弱是因为我拆的"。 +- **P4 的地牢住户**:敌人是"越下越险"的具象——深层的敌人更凶(C3 施加),给下探氛围一个可见的刻度。 + +参考感受:Peglin(哥布林弹珠)的固定招式轮替(威胁因可预测而可规划——确定性环是可学习性的来源)+ 怪物猎人的读招(前摇姿态承载威胁信息、无数值面板——视觉推测的同构)。本作合成两者:**确定性环 × 视觉推测**。**反向对照**:半随机、不可归因的威胁与重掷线索的"猜怪"呈现——正是本作要避开的感受。 + +**反向护栏**:敌人的威胁绝不能是无源的暗账——若玩家说不出"它为什么变强 / 这一拳为什么这么重",归因链即失职(违反 P2/P3);视觉推测型下的对偶护栏:**线索必须稳定可学**(形态↔行为映射每 run 恒定,D1 义务②),任何让线索重掷或不可学的呈现设计默认砍掉(违反 P1)。血条/攻击力的数字可读性适用视觉锚 1(暗色底上高对比)。 + +> 注:`creative-director` 未经咨询——Lean 模式(仅 D/H 两节委托专家),生产前请人工复核本节情绪定位。 + +## Detailed Design + +> **决策记录(2026-09-09 用户拍板)**:多敌(同敌种 1–3 单位)· HeroTable 新增 · 意图视觉推测型(Peglin 行为类型学 + 确定性行为环,不搬其数字意图 UI)· 每 (L,D) 行集恰一敌种 · 效果 side-wide · 英雄侧固定顺位目标 · MVP 双敌种双环。 + +### Core Rules + +1. **战斗单位统一接口(CD 门禁扩容口径)**:战斗域恰两类单位——主角(hero)与敌人(enemy)——共用同一套受伤/死亡语义:单位持有 `hp / max_hp`,受伤 = hp 递减(clamp/负值/溢杀语义归 B1 结算公式域),`hp = 0` 即死亡;死亡判定**时机**归 B1 结算编排。主角死亡 = 战斗失败判定素材;敌单位全灭 = 战斗胜利判定素材(判定逻辑归 B1,game-concept 轮次流程 4)。B2 拥有单位状态与查询/受击接口;"谁在何时对谁造成多少"归 B1 SettlementTable。 +2. **敌人数模型(B3 Rule 1 单敌假设回验:不成立——定多敌,用户拍板)**:MVP 战斗 = **1–3 个同敌种敌人单位**(`unit_count` 表字段,knob),战斗内敌单位同种同属性(同一 EnemyTable 行实例化,行主序生成序)。回验结论触发 registry 预登记的接口漂移义务:**MVP 兼容路径 = 效果 side-wide**——enemy 侧 side-wide 从"该敌"改为"全体存活敌单位",B3 现接口**零变更兼容**(层数为 side 共享池,Rule 5);target_ref(单体寻址)登记为 **B3 扩展位义务**(首个单体效果上线时补列),B1 归集语义/D1 呈现裁决随批验收(义务清单见 Dependencies)。 +3. **敌种内容行(MVP 收窄:恰 2 敌种,索引 CD 门禁定稿)**:名录扩展归垂直切片/Alpha。每 (level_index, node_depth) 行集**恰一敌种**(用户拍板④——零选择规则、零实体键悬空风险);多敌种同深度的遭遇选择规则归 C1/C3(provisional 结构位)。MVP 双敌种(值全部 provisional): + - `grub`(蛆虫):行为环 [attack](平凡环);基线 hp/atk 暂按原型参考 18/4 占位(**出处待查**——D2 knobs 既有溯源义务) + - `ambusher`(伏击兽):行为环 [attack, charge, heavy](三拍环);heavy_multiplier provisional 2 + + MVP 分布 provisional 示例:L1 全深度 → grub、L2 全深度 → ambusher(曲线数值所有权归 C3,A1 参考行先例)。**注(2026-09-09 full 评审):行集按 (L,D) 逐坐标判定——同关内随深度切换敌种合法**(如 (2,0)→grub、(2,2)→ambusher);上例为最平合法特例、**非范本**,C3 调参勿默认照抄(防多样性无护栏)。 +4. **行为环(确定性节奏,Peglin 行为类型学转译)**:每敌种一个**行为环**——有序行为序列,战斗开始指向环首,**每次结算推进一格**(**轮到该单位行动时仍存活**才行动并推进;轮到它之前被击杀 → 本拍不打、指针冻结——"死亡即不动手"【2026-09-09 用户拍板,F1 承载】;**先作用后推进**,B3"先兑付后衰减"同款时序哲学),环尾回环首;跨轮次、跨扫雷图延续(战斗域),battle_over 随单位消亡。**行为类型封闭枚举(七类)**:`attack`(攻击:本结算对主角造成 atk 点伤害)/ `charge`(蓄力:本结算不作用——威胁空拍、规划窗口)/ `heavy`(重击:atk × heavy_multiplier)/ `guard`(自防)/ `boost`(自体强化)/ `hinder`(妨害雷图——Peglin"干扰弹板"的本作转译:敌方向棋盘施加变异类妨害)/ `summon`(召唤增援)。**MVP 实现恰前三类**;后四类为**预留语义位**(垂直切片/Alpha),EnemyTable.pattern 校验拒绝预留值(fail-fast,A1 solver_rules 冻结先例)——启用走本 GDD 修订(枚举封闭性),非调参。**零随机**:环推进是结算次数的确定函数(rng_service 流封闭,P1)。**行为环为敌单位专属(2026-09-09 full 评审 blocking ⑧)**:主角无行为环——advance/query_beat 等环接口对主角句柄一律拒绝(false / 空 + 诊断);"每结算恰一次推进"的编排口径相应为"每存活**敌**单位"(B1 义务①、AC 前言时序口径同款)。 +5. **雷触发的敌方优势(伏击清单,side-wide)**:MVP 伏击清单 = **{ambush}**(B3 表 mine_hit 源唯一效果行;新增词条须 B3 加行 + 锚连锁,非 MVP)。转化路径:A2 `cell_revealed{kind=mine}` 事实 → B1 结算编排期转化 → `apply_effect(ambush, source)`(side=enemy 共享池,B3 零接口变更)——**每颗雷恰一次转化**(B1 义务②,B3 EC6 对侧)。**惩罚上限表**:EnemyTable.`advantage_per_round_cap`(int [0,16],knob,**MVP 默认 4**【2026-09-09 D 节拍板——反灾难护栏定位:绑定阈 = 单轮 5–8 雷灾难轮;旧默认 8 使溢出协议死码(min(雷数≤8, 8) 恒等式),已废弃】)——单轮次内 side 池伏击授予上限;达限后转化改为**"溢出未生效"结算明细行**(B1 义务⑥协议,惩罚不静默蒸发,P2)。该字段是 A3 F1 条款 3 挖掘风险不等式的 **B2 供给表值**(口径见 F4——单结算自然边界,2026-09-09 用户定稿)。 +6. **伏击量纲定稿(B2 验收锚义务兑现——registry status_effect OQ2 关闭条件)**:`ambush.magnitude = +3 flat` 的单位 = **每层伏击为每次攻击类行为(attack/heavy)附加 3 点固定伤害**(flat = 主角 HP 点数;charge 拍不兑付伏击——空拍即无附加)。结算读法:B1 结算某敌单位攻击行为时,附加伤害 = side 池当前层数 L × 3。**条款 3 供给口径**(2026-09-09 用户定稿:单结算自然边界——debuff 一结算即逝,无每战累计上界):单结算单单位伏击附加上界 = cap × 3 = 12(MVP 默认);单结算 side 级结构上界 = unit_count × cap × 3(**满配语境 = unit_count 上界 3 → 3×4×3 = 36;默认行值 unit_count=1 下 = 12**——qa-lead 🔴-3 语境修正);跨结算累计 = 轨迹量、非表值、不设上界(dur=1 每结算清零无存量)——等量换算与不等式闭合归 B1(F4)。本条定稿后按接口漂移回写 registry `status_effect.ambush` 量纲注记(Phase 5b)。 +7. **目标选择规则(多敌下的真实规则)**:**敌 → 主**:全体存活敌单位按各自行为环当前拍对主角作用(主角是 hero 侧唯一单位,无选择)。**主 → 敌(用户定稿:固定顺位)**:单体作用默认作用于**存活敌单位中行主序(战斗开始生成序)最前者**,该单位死亡后顺延;AOE 作用于全体存活敌单位(无选择)。规则供 B1 结算消费;道具自带指定目标语义的扩展归 B4/B1 域。顺位可读性 = D1 呈现义务(玩家须能推测"下一个被打的是谁")。 +8. **意图可见性规格(视觉推测型,CD 门禁承重项定稿,用户拍板)**:**不直接显示下一轮结算的行为与数值**——威胁经敌种展示图像推测:① 图像↔行为映射**稳定可学**(同一敌种同一行为恒同一视觉状态,如 ambusher 蓄力拍 = 蓄力姿态);② 敌种形态暗示强度档位与环长;③ 映射**每 run 恒定、禁止重掷**(否则"读怪"退化"猜怪",违反 P1 裁剪判据);④ 精确数值在**结算明细中兑现**(P2 归因不变);⑤ **预告时序锁定(2026-09-09 full 评审 blocking ⑥)**:视觉状态呈现的是**即将执行的一拍**(当前拍预告)——**挖掘阶段全程可见**,结算时不另设独立意图播片(结算明细是数字兑现面、非意图预告面)。无挖掘期常驻预告,"下一拍是重击"的规划输入即不存在——抢杀窗口(F1 极端)、"蓄力拍前放心贪雷"教学(D5)、读怪幻想(本节)三者同时落空,故本条与 ①③ 同为承重项。线索清单与图像规格 = D1/B5 呈现义务(登记 systems-index,见 Dependencies);映射键 = enemy_id(B3 effect_id→icon 同款契约)。MVP 读怪对象 = 双敌种双环(最长 3 拍,学习成本低)。 +9. **主角战斗面(HeroTable 新增,用户拍板②;基础攻击定稿 2026-09-11 用户裁决)**:主角数值承载于 **HeroTable**(单行全局,D2 新增 schema 行,本 GDD 回填 owner,走 D2 接口漂移规则回写)。字段:`max_hp`(int [1,999],knob——MVP 值 provisional,出处待查)+ `atk`(int **[1,999]**,knob——**基础攻击恒存在**,MVP = 6(原型锚 HERO_BASE_ATK"每次结算的基础伤害",provisional 归 C3);**0 = 加载期校验拒绝**——原"机制开关/激活裁决归 B4/B1"条款作废:基础攻击不是开关,是恒在属性;后续解锁成长归 C4,曲线调参归 C3)。**消费面**:主角每结算战斗段**先手攻击**存活敌中行主序最前者(Rule 7 固定顺位;先于敌方行动——伤害算式/乘区/明细行协议归 B1 战斗段);attack_up(B3 +50 percent)的唯一消费面 = 本攻击——道具伤害恒定表值不吃修正(B4 F4 用户拍板)不受影响。**攻击无行为环**:主角攻击是战斗段固定动作,Rule 4"行为环为敌单位专属"不变。战斗开始时主角 hp 初始值由编排方入参供给(**跨战斗 HP 持久性归 C1/C2 域**,本 GDD 不定义;MVP 冒烟以 max_hp 入参)。主角与敌单位共用 Rule 1 接口。 +10. **实例化与属性基线**:战斗开始按遭遇坐标 (level_index, node_depth) 解析 EnemyTable 行(D2 R1:进度键 floor + enemy_id 精确匹配,悬空致命)→ 实例化 `unit_count` 个单位(行值快照——**授予时快照语义**,抗表热重载,B3 EC9 同款)。难度成长双通道:EnemyTable 行值随 (L,D) 变化 + C3 DifficultyTable 修饰(**接口未定,provisional**——修饰施加点与算式归 C3 定稿;B2 以实例化后数值运作)。 +11. **编排被动性**:B2 零随机、零 UI 依赖(Core 层 headless 债)、**零主动事件订阅**(A2 事件面 referenced_by 不含 B2——事实经 B1 转化驱动);B2 暴露单位状态查询与受击/推进接口,调用权集中归 B1 结算编排(B3"授予集中在编排"同款原则)。 + +### EnemyTable / HeroTable Schema 定稿(D2 回写义务) + +**EnemyTable**(键 `(level_index, node_depth, enemy_id)`——进度键 floor(D2 R1)+ 实体键精确匹配;**每行集恰一敌种**(行集内 enemy_id 唯一 = 表结构校验,违规加载期致命);本表**是**进度成长表(A1 BoardGenTable 同型,无 Rule 4 豁免);表非空(D2 Rule 6);**精英/Boss 编码位预登记 OQ7**——难度三维的精英/Boss 修饰在本键结构下无编码位,C1 定稿时强制核对): + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| enemy_id | String(行集内主键) | 实体键精确匹配(D2 Rule 4);意图视觉映射键(Rule 8④) | +| display_name | String | 图例/HUD/明细用名(本地化归 D6) | +| unit_count | int [1, 3] | 战斗构成单位数(knob;同敌种) | +| hp | int [1, 999] | 单位最大生命基线(knob;难度修饰前) | +| atk | int [0, 999] | 攻击基线(knob;attack 行为伤害基值) | +| heavy_multiplier | int [1, 16] | heavy 倍率(knob;无 heavy 拍的敌种不消费——非校验项,B3 max_stack 先例) | +| pattern | StringList | 行为环(元素 ∈ {attack, charge, heavy}——**MVP 校验域**;guard/boost/hinder/summon 预留、校验拒绝;环长 [1, 8]) | +| advantage_per_round_cap | int [0, 16] | 单轮伏击授予上限(knob;F1 条款 3 表值;0 = 该行敌种免疫伏击授予的合法极端) | + +**HeroTable**(单行全局——主角不随关卡自动成长,成长 = 天赋 C4 / 奖励侧;Rule 4 豁免同 ActionEconomyTable 先例;**D2 新增 schema 行,接口漂移回写义务见 Dependencies**): + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| max_hp | int [1, 999] | 主角最大生命(knob,MVP 值 provisional 出处待查) | +| atk | int [1, 999] | 基础攻击(knob,MVP = 6 原型锚 provisional;**0 = 加载拒绝**——零基线死锁 schema 层杜绝,2026-09-11 用户裁决) | + +### States and Transitions + +单位生命周期四态(战斗域;主角与敌单位同机): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Unspawned | 构造后未实例化 | 无接口副作用;唯一出口 = B1 编排实例化 | +| Alive | 实例化(行值快照 / hp 入参) | 可被指定目标(顺位)、按行为环作用、计入全灭判定;受击 hp 递减 | +| Dead | hp = 0(判定时机归 B1) | 移出顺位、不再作用、不计入"未灭";不可逆(MVP 无复活) | +| Despawned | battle_over(B1 编排) | 全部单位状态消亡,不跨战斗 | + +行为环指针 ∈ [0, 环长)——Alive 期间按结算推进(Rule 4);charge 空过是环位置语义、非独立状态。 + +实例不变式(debug 断言口径):hp ∈ [0, max_hp];敌单位数 = unit_count(战斗内不增——summon 未实现);指针 ∈ [0, 环长)。违规 = debug 断言中止(进程致命保留给 D2 数据通道,B3 同款)。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| D2 | 双向(hard) | EnemyTable schema + 双敌种行回填(本 GDD 义务)+ **HeroTable 新增**(接口漂移回写:D2 Core Rules 表清单 + registry);读取经 ConfigDB(R1 行解析:进度 floor + enemy_id 精确匹配) | +| B1 | 双向(hard,**provisional**——B1 未设计) | 结算编排期调用:存活集/顺位查询、行为环当前拍查询、属性快照、受击上报、环推进(每结算恰一次)、实例化/消亡通知;伤害公式/乘区/地板/死亡判定时机归 B1 SettlementTable;**B1 义务清单 6 项(缺项即 blocking,见 Dependencies)** | +| B3 | 相邻(零运行时依赖) | ambush/weaken 的量纲与作用域**验收锚归 B2**(Rule 6 兑现 registry OQ2);side-wide MVP 兼容声明(B3 零接口变更);**target_ref 扩展位义务**(首个单体效果上线时补列,随批:B1 归集/D1 呈现裁决) | +| B4 | soft(**provisional**) | 伤害道具默认目标消费(Rule 7);伏击清单扩展联动(新增 mine_hit 行时本 GDD 同步修订);B4 道具/增益 = 主角基础攻击之外的**规划面伤害**(2026-09-11 用户裁决后非唯一来源);attack_up 消费面 = 主角战斗段先手攻击(Rule 9 / B1 战斗段) | +| A2/A3 | 间接(零直接接口) | 雷/旗事实经 B1 转化;AP 界(单轮开雷 ≤ 8)= cap 默认值结构来源;weaken 档位锚归 B4/B1(A3 条款 2),B2 无表值 | +| A1 | 间接 | 雷数分布影响伏击吞吐——条款 3 对账消费 A1 actuals p10(B1 义务,非 B2 域) | +| C3 | 下游(soft,**provisional**) | 难度修饰施加点/算式归 C3 定稿(Rule 10);EnemyTable/HeroTable 曲线数值调参所有权归 C3(A1 参考行先例);多敌种同深度选择规则共裁;**新敌种投放节奏 = 关卡命脉**(MVP 敌信息总量 = 2 环 3 行为、L2 见底——每关须有新敌种或新环段投放;grub 单拍环 playtest 证伪项 OQ8) | +| C1 | 下游(soft,**provisional**) | 遭遇构成(战斗触发、(L,D) 供给、unit_count 覆盖)归 C1;跨战斗 HP 持久性归 C1/C2 | +| D1 | soft | **视觉推测型呈现义务 4 项(缺项即 blocking,见 Dependencies)**;敌单位 HUD(血条/顺位/姿态)呈现;顺位标记资产需求(Visual Req 2) | +| B5 | soft | 行为环视觉状态表现(蓄力姿态等——预告时序 Rule 8⑤);结算明细敌方行呈现(数据源 = B1 流水;**首个 heavy 兑付明细行分量高亮建议**——首遇校准课,ux 注记非硬义务) | +| D5 | soft | 读怪教学(敌种环模式可学、顺位规则)+ **"蓄力拍前放心贪雷"教学**(charge 轮伏击池蒸发 = 读怪奖励的涌现素材——F2 极端行为,2026-09-09 登记)+ **首次蓄力姿态一次性提示候选**("它在蓄力,这轮踩雷不挨打"——L1 grub 无 charge 拍、首课在 L2;MVP 可先上一次性提示、不等垂直切片完整教学。2026-09-09 full 评审增补)——垂直切片期 | + +**B1 侧义务清单(B2 GDD 门禁评审逐项核对落位,缺项即 blocking——B3 对 B1 登记先例)**:① 单位接口调用集中在结算编排、行为环推进**每结算恰一次**(double-advance 无守卫——信任编排,B3 义务⑥同款);**结算内调用序成文:兑付读值(query_attack)→ 受击上报(apply_damage)→ 推进(advance)**——倒序会读到下一拍(F2 依赖当前拍;2026-09-09 full 评审增补——比 double-advance 更常见的实现错误,B1 冒烟须回放结算步流)② 雷→伏击事实转化**恰一次**(每颗雷格实例至多一次;B3 EC6 对侧同款)③ 伤害 clamp/负值/溢杀语义与死亡判定时机成文(Rule 1 B1 域)④ F1 条款 3 闭合对账:消费 B2 `advantage_per_round_cap` 表值 + 伏击量纲(Rule 6 换算)+ **A1 actuals p10**(非 target 参数——A1 登记口径)⑤ 默认目标规则消费与单体/AOE 分流(Rule 7;顺位快照 = 生成序)⑥ 伏击 cap 溢出的"溢出未生效"明细行协议(Rule 5 后半;呈现归 B5/D1)。 + +**D1 侧义务清单(视觉推测型呈现,缺项即 blocking)**:① enemy_id→图像线索映射清单成文(形态/姿态↔行为状态;**预告时序 = 下一拍预告、挖掘期全程可见**〔Rule 8⑤〕;**含强度档位与环长直觉编码说明**〔Rule 8②——2026-09-09 full 评审补验收落点〕;形状+色相双通道,art-bible 约束)② 线索映射**全局恒定**(映射键 = enemy_id——跨 run 恒定、禁止重掷;Rule 8③ 的呈现侧承载)③ 单位顺位与存活可读(固定顺位规则的推测前提——Rule 7;顺位标记资产需求见 Visual Req 2)④ 敌方 side 池状态呈现口径(伏击层数经 B1 流水——B3 敌侧延伸)。 + +## Formulas + +> systems-designer 代理起草(lean 门禁,2026-09-09);四项口径经用户拍板:**cap 默认 4 · 死亡即不动手 · 条款 3 单结算自然边界 · C 节三处回写**。本节只含机制公式,平衡归属见文末显式排除声明。全部公式零随机、同输入恒同输出(可回放前提)。 + +**术语白话**:**拍** = 行为环里的一格行动(ambusher 的环 [攻击, 蓄力, 重击] 有三拍);**伏击池** = 敌方侧共享的伏击层数(本轮踩几颗雷就几层,轮末清零);**溢出** = 单轮踩雷超过上限的部分,转化为明细行可见、不生效。 + +### F1 行为环推进(ring_advance)——敌人按固定套路轮流出招 + +`beat(单位) = pattern[已行动次数 mod 环长];行动完成后 已行动次数 +1` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 行为环 | pattern | StringList | 环长 [1,8];MVP 元素 ∈ {attack, charge, heavy} | EnemyTable 行值快照(Rule 10) | +| 已行动次数 | steps_u | int | [0, ∞) | 结算次数的确定函数(零随机) | +| 当前拍 | beat | enum | pattern 元素 | 本结算该单位的行动内容 | + +**死亡口径(2026-09-09 用户拍板 A"死亡即不动手")**:轮到该单位行动时**仍存活**才行动并推进;在轮到它之前被击杀 → 本拍不打、步数**冻结**(死亡即退场,MVP 无复活 → 永久冻结,随 battle_over 消亡);行动完成后才死 → 已行动已推进。**同步性质(定理)**:同种单位同指环首、每结算各恰推进一次(B1 义务①)→ 存活者**永远同拍**——任何死亡时序都不会让幸存者乱拍。 + +**Output Range:** 指针恒 ∈ [0, 环长);环长 1(grub)退化为恒等。**极端**:heavy 拍前被杀 → 重击永不兑现(冻结于该拍)——玩家"抢在重击前击杀"的读怪收益由此产生(P1)。 +**Example:** ambusher:结算 1 攻击(p→1)→ 结算 2 蓄力空拍(p→2)→ 结算 3 前被主角击杀 → 重击不兑现、指针冻结于 2。若结算 3 行动后才死 → 重击已兑现、p 回绕 0。 +**边界注记:** 归 B2——指针所有权、存活校验、冻结不变式、环首初始化、跨轮跨图延续。归 B1——死亡判定时机、结算内先后次序、每结算恰一次推进(义务①)。 + +### F2 攻击行为的裸伤害构成(raw_attack_damage)——敌人一下打多少的两块拼图 + +``` +attack 拍:D = atk + m_amb × L (基线 + 伏击附加;m_amb = StatusEffectTable.ambush.magnitude,provisional 3) +heavy 拍:D = atk × heavy_multiplier + m_amb × L +charge 拍:D = 0 (不读伏击池——空拍零兑付) +``` + +**合成序钉死**:伏击附加是**后置加法**,不进倍乘——ambusher(atk=4, mult=2)heavy 拍 L=4(cap=4 默认下游戏态上界,判别非平凡:20 ≠ 32)→ **4×2 + 12 = 20**,不是 (4+12)×2 = 32。〔2026-09-09 full 评审更正:原示例 L=5 超出默认 cap 游戏态域〔L ≤ min(雷数, cap) = 4〕,改为域内判别值;query_attack 的 L 为调用方注入的判别探针、B2 不校验其游戏态域〔游戏态域由 F3 预截流承载〕——探针取值与游戏态域分离,实现期若给 query_attack 加 L 域守卫须回改 AC-7。〕**池共享不消耗**:同一结算多个敌人的攻击各自读**同一个** L(共享、非均分、非先到先得);层数扣减唯一入口 = B3 轮末衰减(dur=1 轮末全清)。**分量供给建议**:B2 向 B1 供 (基线, 伏击) 二元组而非合后标量——结算明细"伏击 +N"的 P2 归因行需要分量边界。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 攻击基线 | atk | int [0,999](快照) | EnemyTable 行值 | 0 合法(纯伏击伤敌种) | +| 重击倍率 | heavy_multiplier | int [1,16](快照) | 仅 heavy 拍消费 | 无 heavy 拍不读(非校验项) | +| 伏击层数 | L | int [0, min(雷数, cap)] | 兑付时读值(B1 经 query_active) | dur=1 → L = 本轮受理授予数,无跨轮存量 | +| 伏击量纲 | m_amb | int flat ≥1;provisional 3 | B3 行值(**量纲 = 主角 HP 点数,本 GDD 定稿**) | 锚验收归 B4/B1 | + +**Output Range(默认表值——cap/atk/m_amb/mult 调参后随之重算)**:grub attack ∈ [4, 16];ambusher attack ∈ [4, 16]、heavy ∈ [8, 20]、charge ≡ 0(cap=4 封顶 L=4)。**极端**:charge 拍零兑付 → 蓄力轮授予的伏击层当轮**全部蒸发**(合法推论,反向 = 读怪奖励:"蓄力拍前放心贪雷"——已登记 D5 教学条目);无负值分支(clamp/地板归 B1)。**量级注记补正(2026-09-09 full 评审)**:"L≥2 伏击分量过半"仅 **attack 拍**成立(6/10);heavy 拍因基线 ×mult 过半点后移(6/14 < 半)——支配边界 L* = ⌈atk×mult/3⌉ 与"过半"口径按拍别分别读取。 +**Example:** grub(atk=4)本轮开雷 3 颗 → D = 4+9 = **13**;满编 3 grub、L=4 → 各 4+12=16、合计 48(伏击分量 36)。 +**边界注记:** 归 B1——L 读值、clamp/负值/溢杀、hero 侧减伤合成次序、死亡判定与"这一下是否兑现"。 + +### F3 伏击授予与溢出(ambush_grant_overflow)——一轮踩 N 颗雷,敌人实际吃几层 + +``` +granted_r = min(当轮新开雷数, cap) —— cap 判定在授予前(B1 预截流) +overflow_r = 当轮新开雷数 − granted_r —— 每颗溢出雷一行"溢出未生效"明细(含来源) +L_r = granted_r —— debuff 只活一结算:当轮兑付一次、轮末全清、无跨轮存量 +``` + +**守恒**:granted + overflow = 当轮雷数,零静默蒸发(P2)。**cap=4 默认(2026-09-09 用户拍板)**:绑定阈 = 单轮 5–8 雷的灾难轮——反灾难护栏定位;溢出协议获得真实消费域(旧默认 8 = min(雷数≤8, 8) 恒等式 → 溢出协议死码,已废弃)。**max_stack=16 结构不可达验证**:单轮 L ≤ min(8, cap=4) = 4 < 16,且 dur=1 无跨轮存量 → B3 满层拒绝对 ambush 行**双重不可达**(M=16 是死护栏非死字段)。重验触发(2026-09-09 full 评审修正):ambush.duration ≥ 2 ∨ AP 经济改动 ∨ **ambush 新增非 mine_hit 授予源**(原"cap 域漂移 >16"在 schema 域 [0,16] 内为死条款,替换——不可达性实际由 dur=1 ∧ AP≤8 承载、与 cap 无关;旗源授予不计入 mines_r、cap 预截流漏账,L 可达 16 触发 B3 满层拒绝、与 F3/B3 双溢出协议冲突——OQ6 标错惩罚采纳时强制重验)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 当轮新开雷数 | mines_r | int [0, 8](结构界 = AP) | round facts 经 B1 收集 | 复开不可能(A2 棋盘面语义) | +| cap | int [0,16](行值 knob) | 默认 4 | 0 = 该行敌种免疫伏击(全溢出可见) | F1 条款 3 供给值的来源字段 | +| granted_r | int [0, 4] | B1 编排期逐雷 apply_effect | 每雷恰一次转化(义务②) | +| overflow_r | int [0, 8] | 明细行数 | 呈现归 B5/D1(义务⑥) | + +**Example:** cap=4、开雷 6 → 授 4 层 + 2 行溢出;本结算每个攻击行为 +12。cap=0 → 全溢出、伏击全无效但全部可见。 +**边界注记:** 归 B1——预截流编排、授予次序(行主序雷格序为建议序)、每雷恰一次、溢出行协议。 + +### F4 条款 3 供给口径(clause3_supply)——B2 交给 B1 对账的是什么(2026-09-09 用户定稿) + +**用户原话转写**:"不设(每战)上界——踩雷的 debuff 只存在一回合:在一次结算中有效,之后就没有 debuff 了。" + +``` +每结算敌方伏击暴露(精确式)= k × L × m_amb (k = 本结算存活敌中攻击/重击拍的单位数,确定性可算) +每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb + (cap > AP 界时 cap 腿松弛——默认 3×min(8,4)×3 = 36;cap≥8 时真可达上界 72 而非 144。2026-09-09 full 评审收紧) +跨结算累计 = 轨迹量,非表值、不设上界 —— dur=1 每结算清零,无存量累积;战斗再长不叠加同一层 +``` + +**口径依据**:debuff 单结算寿命 = 自然边界(用户定稿)——B1 对账取"每结算"口径即可闭合,无需战斗长度假设。**B3 红警闭环**:ambush 量纲已由 F2 供给(registry OQ2 关闭条件);条款 3 **左臂**(安全格 buff 当量)载体 = 主角基础攻击(2026-09-11 用户裁决后恒在,attack_up 复活)与 B4 奖励行值——原"载体悬空(hero atk=0、B4 未设计)"口径退役(闭合回执见 registry clause3_supply,2026-09-10 B1 F4)。 + +**方向性预检(provisional,真实对账 = A1 actuals p10 + B1 归一表)**:盲猜端单格 P(雷) ≈ 0.5–0.7 量级、有信息挖掘端趋 0(A1 参考:密度 0.10 / target 0.95)——不等式的真实战场在残余盲猜集;cap=4 下右臂 = 0.64×12 ≈ 7.7;左臂按条款 2 **最低档**锚(+50pp)计 = 0.36×50 = 18——方向稳健裕度 ≈ **2.3×**。〔2026-09-09 full 评审更正:原"裕度 ≈4.7×"按左臂 +100(两层叠满)取值、与"最低档下界"语言矛盾;registry clause3_supply.direction_precheck 同步回写。〕**量级注记(支配边界)**:atk=4 时 L ≥ 2 伏击分量即过半——常态支配的修正入口 = 敌种 atk 曲线(C3 所有权)或 m_amb 锚(B4/B1 验收),本 GDD 权限外、仅登记方向;cap=4 只治尾部灾难(5–8 雷轮)。 + +### 显式排除声明(R4 式) + +| 对象 | 归属 | +|---|---| +| 伤害 clamp/地板/溢杀、乘区合成、减伤读法、死亡判定时机 | **B1** SettlementTable | +| F1 条款 3 不等式闭合对账(输入 = A1 actuals p10,非 target 参数) | **B1** 单一 owner | +| m_amb(+3)行值锚验收、weaken 档位 | **B4/B1**(A3 条款 2/3) | +| 敌种 hp/atk/pattern 曲线数值、难度修饰 | **C3** | +| 伤害道具/奖励池(hero 侧伤害来源) | **B4** | + +B2 只对"套路怎么转、一下打多少的两块拼图、一轮吃几层、交给 B1 对账的口径"负责。 + +## Edge Cases + +- **If 敌人在轮到它行动前被击杀**:本拍不打、指针冻结("死亡即不动手",F1 用户拍板 A);若行动完成后才死 → 已行动已推进(heavy 已兑现)。 +- **If 同种单位部分死亡**:存活者永远同拍(同步性质定理,F1)——幸存者轮转不受任何死亡时序干扰。 +- **If 全体敌单位死亡**:存活敌集 = ∅,"敌人全灭"成立、胜负判定归 B1;单体/AOE 无有效目标;敌侧伏击池随轮末衰减自然清空(无兑付者)。 +- **If 主角 hp = 0**:战斗失败判定素材(判定时机归 B1);battle_over → 全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态。 +- **If 战斗中敌单位数 ≠ unit_count**:结构不可能(MVP 无 summon/增援/分裂)——出现即 debug 断言中止(实例不变式,B3 同款:进程致命保留给 D2 数据通道)。 +- **If 敌种行 cap = 0**(免疫伏击的合法极端):granted ≡ 0、每颗雷各一行"溢出未生效"明细(全部可见、全部无效——P2 诚实);该遭遇条款 3 右臂仅剩基线伤。 +- **If 单轮踩雷 5–8 颗(灾难轮)**:granted = 4、溢出 1–4 行;本结算每个攻击行为附加恒 +12(cap 反灾难护栏绑定域——F3)。 +- **If 全部存活敌同拍蓄力(charge 轮)**:零兑付(k=0);该轮新踩的雷照常授予、轮末照常全部蒸发(合法推论 + 读怪奖励,"蓄力拍前放心贪雷"——D5 教学条目已登记)。 +- **If 敌单位在 heavy 拍前被杀**:重击永不兑现、指针冻结于 heavy 拍——"抢杀窗口"读怪收益(P1)。 +- **If 敌种 pattern 环长 = 1(grub)**:mod 1 恒等 → 每拍 attack、无自然空窗(兑付率 ρ=1——该敌种伏击威胁无衰减节拍,量级注记见 Formulas)。 +- **If 战斗跨多张扫雷图**:单位状态(hp/指针)与敌侧伏击池**跨图延续**(战斗域,换图/刷新不清空——B3 EC8 同款);雷格兑付资格随图消亡,新图新雷新资格(A3/A2 域,B2 无从感知也无须感知)。 +- **If 战斗中热重载 EnemyTable/HeroTable**:D2 战斗中 reload 拒绝门(D2 EC8);已实例化单位不受任何表重载影响(行值快照,B3 EC9 同款)。 +- **If 行集缺行 / 请求敌种悬空**:D2 R1 实体键精确匹配 → 悬空引用**致命**(D2 Rule 6 通道);B2 无软回退(错误配置上线比崩溃更伤——P1/P2 信任底线)。 +- **If 遭遇坐标越界(level_index < 1 / node_depth < 0)**:D2 R1 前置守卫致命(D2 EC14)——B2 不自行 clamp。 +- **If unit_count = 1**:固定顺位顺延与 AOE 分流无消费者(Rule 7 空转)、AOE ≡ 单体——合法退化,非错误。 +- **If 主角 hp 入参越界(> max_hp 或 ≤ 0)**:拒绝实例化(false + debug 诊断,承 A3 拒绝口径;进程致命保留给 D2 数据通道)——编排方供给异常显式暴露,不静默吸收。 +- **If 同结算内"踩雷授予"与"敌攻击兑付"的先后**:授予在编排期先于兑付 → 本轮踩的雷当轮即兑付(若该轮有攻击拍);**读值语义半句归 B2**(AC-7 腿4——无内部缓存、无时间维度)、**次序半句归 B1**(义务①②编排域——qa-lead 🔴-4 切分)。 + +## Dependencies + +**Upstream(hard)**:D2 数据配置——EnemyTable schema + 双敌种行回填(本 GDD 为回填 owner)+ **HeroTable 新增**(走 D2 接口漂移规则回写其 Core Rules 表清单)+ ConfigDB 单点读取(R1 行解析)。无其他玩法上游;A2/A3/A1 的事实均经 B1 结算编排转化(零直接接口)。 + +**Downstream:** + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | **hard(provisional——B1 未设计)** | 单位状态查询/受击/推进接口 + **B1 侧义务清单 6 项**(Detailed Design 节,缺项即 blocking——B3 对 B1 登记先例):① 推进每结算恰一次 ② 雷→伏击恰一次 ③ clamp/死亡时机成文 ④ 条款 3 对账(cap 表值 + 量纲 + A1 actuals p10)⑤ 默认目标/AOE 分流 ⑥ 溢出行协议 | +| C3 | soft(**provisional**) | 难度修饰施加点/算式;EnemyTable/HeroTable 曲线数值调参所有权(MVP 默认值全部 provisional——A1 参考行先例);多敌种同深度选择规则共裁 | +| C1 | soft(**provisional**) | 遭遇构成(战斗触发、(L,D) 供给、unit_count 覆盖语义);跨战斗 HP 持久性(归 C1/C2 域) | +| B3 | 相邻(**零运行时依赖**——验收锚关系非依赖边,B3 先例同款) | ambush 量纲/作用域验收锚已由本 GDD 兑现(F2);side-wide MVP 兼容(B3 接口零变更);**target_ref 扩展位义务**(首个单体效果上线时补列) | +| B4 | soft(**provisional**) | 伤害道具默认目标消费(固定顺位);伏击清单扩展联动(新增 mine_hit 行时本 GDD 同步修订);B4 道具/增益 = 基础攻击之外的规划面伤害(2026-09-11 裁决后非唯一来源);attack_up 消费面 = 主角先手攻击(Rule 9) | +| D1 | soft | **视觉推测型呈现义务 4 项**(Detailed Design 节,缺项即 blocking):图像线索清单成文(含预告时序 + 强度档位说明)/ 映射全局恒定(跨 run 禁重掷)/ 顺位与存活可读 / 敌方 side 池呈现口径 | +| B5 | soft | 行为环视觉状态表现(蓄力姿态等);结算明细敌方行呈现(数据源 = B1 流水) | +| D5 | soft | 读怪教学 + "蓄力拍前放心贪雷"教学(charge 轮蒸发涌现素材)——垂直切片期 | + +**双向一致性核对**(systems-index 依赖分层表):B2 ← D2 ✅(索引仅此一条硬边,其余为事实读取不列边——D2 注记口径);B1 ← …B2 ✅ · C3 ← D2,B2 ✅ · C1 ← C2,C3,B2,B4 ✅。**两条修订注记**:① **B3 单敌假设回验结果 = 不成立(定多敌)**——触发 B3 预登记的接口漂移条款("定多敌即触发 target_ref 接口漂移补列"):registry `status_effect.single_enemy_assumption` 注记更新 + systems-index 门禁记录登记(Phase 5b/5d 执行;B3 GDD 正文不改,以其"以 registry 为准"先例承载);② 索引 B2 行描述为窄口径("敌人类型/词条、目标选择规则"),Phase 5d 更新进度行时补 CD 门禁扩容身份(战斗单位系统)。 + +## Tuning Knobs + +| Knob(表行值,D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| EnemyTable.unit_count | 1(provisional) | 同步轮转下**齐射爆发**(全攻轮 side 伤 ×N,非平滑) | =1 时 AOE 分流/顺位顺延零消费者(Rule 7 空转) | +| EnemyTable.hp | 18(provisional,出处待查) | 战斗拉长 → 伏击跨轮累计放大 | 秒杀级 → 惩罚无兑现窗口、伏击机制名存实亡 | +| EnemyTable.atk | 4(provisional,出处待查) | 伏击稀释 → 踩雷无感(P3"风险与收益同源"断) | 支配边界 L*→1,惩罚接管战斗、敌种属性被 C3 曲线稀释 | +| EnemyTable.heavy_multiplier | 2(provisional) | heavy+伏击同拍成秒杀拍(读怪规避价值升、容错归零) | =1 时 heavy ≡ attack、三拍环退化两拍(读怪素材坍缩) | +| EnemyTable.advantage_per_round_cap | **4(本 GDD 定稿)** | →8 时溢出协议死码回归、支配区扩大、条款 3 右臂膨胀 | ≤2 时 cap 常态绑定 → 边际惩罚归零("再多踩也不痛")、溢出行噪音、P3 归因梯度塌陷 | +| HeroTable.max_hp | provisional(出处待查) | 容错升、治疗道具贬值 | 灾难轮秒杀感(与 cap 交互) | +| HeroTable.atk | 6(原型锚,provisional 归 C3;**域 [1,999]、0 加载拒绝**——2026-09-11 用户裁决) | 击杀预算贬值 → 道具供给/敌 hp 曲线联动重算(C3,禁止独立调参) | 战斗拖长、基础攻击存在感稀薄(道具侧支配) | + +- **非 knob(结构冻结)**:行为枚举七类封闭(扩展 = GDD 修订非调参)、死亡即不动手、先作用后推进、伏击池共享读法、合成序后置加法、伏击量纲 = HP 点、固定顺位规则、每行集恰一敌种、零随机——修订走本 GDD 流程。 +- **Knob 交互**:cap × atk(支配边界 L* = ⌈atk×mult/3⌉ 联动)**禁止独立调参**——联动评估归 B1 条款 3 闭合对账;unit_count × cap(单结算上界双线性);heavy_multiplier × atk(heavy 拍支配)。pattern 内容(环长/charge 占比 → 兑付率 ρ)属内容行调参,所有权归 C3。**零伤害死局组合护栏(2026-09-09 full 评审登记,OQ9;2026-09-11 收窄)**:hero atk=0 已被 HeroTable schema 域下界杜绝([1,999],0 加载拒绝——2026-09-11 用户裁决),"全场恒零伤害、战斗永不定局"结构不可能;残余退化 = 行 atk=0 且 cap=0 → **无威胁敌**(战斗可胜但零风险——内容调参禁区注记,非死局);跨表量级护栏(atk×mult 上界 vs max_hp 域)owner = 实现期 ADR/D2 不变。混编遭遇(多敌种同战斗)下 side 池 cap 取哪行值未定义——当前结构(每行集恰一敌种)恒单行值,混编引入时随 OQ7 改键一并裁决。 +- **调参所有权注记**:MVP 全部默认值 provisional,曲线数值归 C3(A1 参考行先例);B1 条款 3 对账落定前,cap 与伏击量纲相关联调须重跑方向性预检并同步 registry(接口漂移义务)。 + +## Visual/Audio Requirements + +B2 无自有渲染面——视觉义务是**数据供给与规格锁定**,呈现归 D1/B5;但视觉推测型决策使本节的规格部分成为**承重项**(意图可见性的全部落点在图像线索): + +1. **敌种视觉身份(形状双通道)**:双敌种剪影必须一眼可辨(视觉锚 2:形状+颜色双通道,色弱可用性);敌种形态承载强度档位直觉编码("大个子多半有重击"式——Rule 8② 的呈现侧)。 +2. **行为环视觉状态(承重规格)**:每个敌种的每类行为(attack/charge/heavy)对应**恒定且互异**的视觉状态——ambusher 蓄力拍 = 可辨识的蓄力姿态;映射**每 run 恒定、禁止重掷**(Rule 8③ 护栏;线索清单成文义务 = D1×4①,art-bible 形状+色相双通道约束)。MVP 敌种视觉状态清单:grub 1 态(攻击)、ambusher 3 态(攻击/蓄力/重击)。**存活意图态之外,资产需求下限另含三类(2026-09-09 full 评审 blocking ⑦ 补齐)**:受击反馈(多敌同屏归因"谁挨了打"的 P2 前提——两敌种可共用通用受击反馈一组,须显式登记)、死亡表现("重击被掐灭""抢杀窗口"兑付可见性是 P1 兑付点——死亡瞬间须可辨识、不得直接消失)、顺位标记呈现元素(数据面 = B2 顺位查询;资产与呈现归 D1×4③、**同等 blocking**)。以上合称资产需求下限——D1/B5 照单生产,不得隐式缺席。 +3. **单位呈现数据供给**:hp/max_hp(血条数字)、顺位(固定顺位目标的可读前提,D1×4③)、存活态、当前拍视觉状态——数据源 = B2 单位状态查询(经 B1 编排流水)。 +4. **敌方增益呈现**:伏击池层数的 HUD 呈现口径(D1×4④——数据源 = B1 流水经 B3 投影);结算明细敌方行使用**分量二元组**("攻击 13 = 基线 4 + 伏击 9",F2 供给建议)——P2 归因的敌侧兑付面。 +5. **视觉锚适用与双舞台陷阱**:血条/伤害数字暗色底高对比(锚 1);**B2 无棋盘渲染面**——敌单位出现在棋盘上还是独立战斗场景 = B5 前的"战斗表现美术测试"锁定(game-concept 双舞台资产管线陷阱),B2 只锁上述数据面与状态清单,不预设舞台。**敌人层绘制优先级(2026-09-09 full 评审增补)**:锚 3 优先级表(数字>小人>旗>变异纹理>底色)不含敌单位——art-bible 创建前须裁决敌人层插入位置(姿态精灵与数字/旗/小人的遮挡关系),随"战斗表现美术测试"一并锁定;否则"哪些数字可信"的心智模型分裂。 +6. **音频**:MVP 无需求(攻击/死亡/蓄力音效归 D4 Alpha;临时 SFX 随垂直切片——AD 项先例);蓄力拍的音频前摇是"读招"的潜在第二通道,登记为垂直切片可选项(非 MVP 义务)。 + +> 注:`art-director` 未经咨询——Lean 模式(仅 D/H 两节委托专家),生产前请人工复核本节与 art-bible 的一致性(art-bible §1–4 须在 D1 GDD 前创建——索引既有排期义务,本节线索清单是其输入之一)。 + +## UI Requirements + +B2 不拥有 UI,有三项呈现义务(均为已登记义务的数据面):① 敌单位 HUD(血条/顺位/姿态——D1×4③);② 结算明细敌方行(分量二元组 + 伏击溢出行——B1 义务⑥协议、B5 呈现);③ 教学口径(读怪 + 蓄力贪雷——D5)。**MVP 无敌图鉴/敌人列表 UI**(敌种名录 2 个、学习成本内);名录扩展时再议(垂直切片)。 + +## Acceptance Criteria + +> **测试载体与注入口径**:B2 是单位层纯逻辑类——非 autoload、无场景依附、无自有渲染面。冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code)、ConfigDB autoload 由脚本显式实例化)。单位容器与两表 schema 经 **class_name 直引实例化**(每用例全新实例、无跨用例状态——B3/A4 直引先例同款;AC-17 扫描对该实例化路径豁免)。B1 未实现(设计顺序第 9 位),冒烟**直接驱动 B2 最小 API 面**(B3"直接驱动最小 API 面"先例)——冒烟脚本扮演编排方,按"实例化 → 查询 → 受击 → 推进 → 消亡"的结算编排序列驱动;B1 就绪后改走真实调用方(结算编排),断言口径不变。 +> **API 面(provisional,封闭枚举——实现期 ADR 定名)**:`spawn(level_index, node_depth, hero_hp) -> bool`(解析遭遇行 → 行值快照 → 实例化 `unit_count` 个敌单位〔行主序生成序〕+ 主角〔hp 入参,MVP 冒烟以 max_hp 入参——Rule 9〕)· `query_alive(side) -> Array`(side ∈ {hero, enemy};生成序稳定枚举)· `query_beat(u) -> String`(当前拍 ∈ pattern 元素;**Dead 句柄保留只读可查**——冻结不变式的观测量;**hero 句柄 → 空 + 诊断**——主角无行为环〔Rule 4,2026-09-09 full 评审 blocking ⑧〕)· `query_attack(u, L, m_amb) -> {baseline, attach, total}`(F2 分量供给面;L/m_amb 由调用方供值 = B1 兑付读值的冒烟等价——B2 对 B3 **零运行时依赖**的边界由此保持;**L 为判别探针入参、B2 不校验其游戏态域**〔游戏态域由 F3 预截流承载〕;Dead 句柄 → {0, 0, 0} + 诊断)· `apply_damage(u, n) -> bool`(受击上报;hp 递减、floor 0 → Dead;**n ≤ 0 → false + 诊断**——无伤害作用非法)· `advance(u) -> bool`(存活才推进——"先作用后推进"的推进半;**false 分两级**:存活校验失败 = 轮到前被杀的**预期冻结、无诊断**〔EC1 常态路径——抢杀窗口是设计卖点,诊断刷屏会淹没信号〕;相位/句柄失效 = **false + 诊断**;**hero 句柄 → false + 诊断**)· `default_target() -> u|null`(固定顺位供给面)· `ambush_grant_plan(mines_r, cap) -> {granted, overflow}`(F3 机制算术纯函数——B1 预截流编排的消费面)· `despawn_all() -> bool`(battle_over 消亡)。**全部布尔/计数返回 = 受理/拒绝**(A3 全 API 布尔口径同款)——"拒绝"直接断言返回值,附 debug 诊断、非进程致命(承 A3 拒绝口径;进程致命保留给 D2 数据通道)。五个 query 面(query_alive / query_beat / query_attack / default_target / ambush_grant_plan)只读、无副作用。**封闭枚举即负空间验收形态**:无 summon/增援 API(EC5 负空间——战斗内不增员)、无复活 API(Dead 不可逆)、无重置/换图 API(EC11 跨图延续的结构前提)、无伏击池写 API(B2 无池——L 恒由调用方注入,EC17 读值语义);新增上述任何入口 = 接口漂移(GDD 修订非调参)。**ADR 前置裁决项**:`query_attack` / `ambush_grant_plan` 两 face 若实现期裁决归 B1(F2"供给建议"与 F3"预截流归 B1"边界注记存在张力),对应 AC 腿迁移至 B1 冒烟、断言口径不变(Open Questions①)。 +> **时序口径**:B2 无结算 API——"一次结算"在冒烟中 = 调用方对每个存活**敌**单位**恰一次** `advance`(含兑付读值与受击注入)的构造窗口〔主角无行为环、不在推进域内——Rule 4;2026-09-09 full 评审 blocking ⑧ 更正,原"每个存活单位"字面含主角、照做即环长取模除零〕——"每结算恰一次推进"是 B1 义务①,冒烟以构造序保证前提、不断言编排正确性;"轮到该单位之前被杀"在冒烟中 = 同一构造窗口内 `advance(u)` 之前的 `apply_damage(u, …)`。多步 GIVEN 均给出构造序列。 +> **只读观测量**:敌单位投影七字段 `enemy_id` · `hp` · `max_hp` · `atk` · `heavy_multiplier` · `pattern`(快照)· `advantage_per_round_cap`(快照)+ `beat`(当前拍)+ 存活态;hero 投影 `hp` · `max_hp` · `atk`。**相位无字段观测量**:Alive/Dead 由 query_alive 成员差分推断(Dead 句柄经 query_beat 冻结值可读);Despawned = **逻辑失效态**(状态位实现——容器为 RefCounted 纯逻辑类、对象不提前释放;失效句柄上查询返回空 + 诊断、作用接口拒绝〔AC-6 同款〕;瞬态纪律同 A3 RoundEnd);Unspawned 无观测量。**投影一律为快照**(pattern 等引用字段返回拷贝——原地改写返回值不影响容器,AC-11 腿 3);容器公开可写属性为零(hp 等仅经 apply_damage 变更——AC-17 扫描域)。〔2026-09-09 full 评审:原"销毁态不可观测"与 AC-6"query_beat 返回空 + 诊断"不可同真,统一为逻辑失效口径,防实现者走 Node + free() 路线〕实例不变式(hp ∈ [0, max_hp]、敌单位数 = unit_count、指针 ∈ [0, 环长))为 debug 断言自守——**违规路径不可由封闭 API 构造**(负空间),不经冒烟断言(进程致命保留给 D2 数据通道,States 节口径);其行为级证据 = AC-1(数)、AC-3(指针)、AC-4/AC-6(hp floor)。 +> **期望值推导纪律**:`hp`(18)/`atk`(4)/`heavy_multiplier`(2)/`unit_count`/`max_hp` 均为 provisional knob(Tuning Knobs 节)——各 AC 的 THEN 具体数值按**当前 ConfigDB 表值推导、不写死**(正文以 `h_g`/`a_g`〔grub 行〕、`h_e`/`a_e`/`m_e`〔ambusher 行〕、`u_c`、`M_h`、`c_r`〔所在行 cap〕、`p_g`/`p_e`〔pattern 表读值〕、`m_amb` = StatusEffectTable.ambush.magnitude 表读值〔仅作 AC-7 注入值来源〕等记号书写);`pattern` 环内容属内容行调参(C3 所有权)——轨迹断言一律按 p 表读值推导。**结构字段定值断言合法**:敌种 id 集 {grub, ambusher}(Rule 3 MVP 收窄定稿)、pattern 元素域 {attack, charge, heavy}(枚举封闭)、每行集恰一敌种、HeroTable 单行、HeroTable.atk 域 [1,999](0 加载拒绝——2026-09-11 用户裁决;MVP 值 6 provisional 不作定值断言)。L / mines_r / n 等入参为构造选择(显式登记),期望值由表读值推导。 +> **夹具策略**:① **默认根**(干净检出:EnemyTable 双敌种行 + HeroTable 单行)承载全部分支行为 AC;**行集登记常数协议**(A1 黄金向量 / A2 板登记同源):(L_g, D_g) = grub 行坐标、(L_a, D_a) = ambusher 行坐标,逐坐标手工推导记录入版本库;失效处置 = 先复核登记、登记无误即实现缺陷。② 根 `enemy_multi_unit`:D2 全表清单拷贝 + EnemyTable 仅将 **(L_a, D_a) 行(ambusher——三拍环)** `unit_count` 置 3(其余字段域内合法;**登记值 u_c_f = 3**)——承载多敌 AC。〔2026-09-09 full 评审 blocking ③ 更正:原改 (L_g, D_g) grub 行——环长 1 恒 attack,AC-9 混合拍 GIVEN 不可构造、AC-5 同步断言零判别力、AC-7 腿 4 双单位读值不可构造,三处死测试;改 ambusher 行后混合拍经对单单位重复 advance 构造(冒烟构造序可越出"每结算恰一次"契约——B2 无守卫、构造自由度成立)〕**显式声明:夹具值非 MVP 内容调参结论**(Tuning Knobs 默认 unit_count = 1——多敌语义在默认根上零消费者,本根是 Rule 2 的测试承载,仅供冒烟)。③ 根 `enemy_reloaded`:D2 全表清单拷贝 + EnemyTable 数值漂移(**登记:hp/atk/advantage_per_round_cap 各 +1、pattern 不动**——快照差分判定用)——服务 AC-11;换根加载经 `load_tables(root, collect_only=true)` 直调(**非 D2 `reload()` 路径**:不过 set_reload_gate、不发 config_reloaded——门语义归 D2 AC-16/B1,B3 AC-15 同款)。④ **致命根 ×2**(独立进程,manifest 见下):`enemy_rowset_dup_species`——D2 全表清单拷贝 + EnemyTable 默认行 + **追加一行与 (L_g, D_g) 行同 (level_index, node_depth) 而 enemy_id 相异**("grub_twin",其余字段域内合法)→ **每行集恰一敌种违规**,B2 表结构致命校验的进程级落点(B3 `status_effect_dup_key` 同型);`enemy_pattern_reserved`——D2 全表清单拷贝 + EnemyTable (L_g, D_g) 行 pattern **追加预留值 "guard"**(其余合法)→ **pattern MVP 校验域拒绝预留值**(Rule 4 fail-fast,A1 solver_rules 冻结先例)的 loader 集成缝落点。⑤ **HeroTable 可选根** `hero_extra_row`(HeroTable 置两行、域内合法——单行全局违规):单行校验落点若实现期 ADR/D2 裁定在 loader 缝则启用(manifest 行 `hero_rows>1→≠0`),否则程序构造承载(AC-13)——**可选、两处恰一承载**。⑥ **程序构造**(AC-12/AC-13):两 schema 类 class_name 直引实例,不经 `load()`/`preload()`。行为夹具根(②③)经 `--data-root=` 注入(仅 debug、不入 `data/tables/`——D2 夹具纪律);各 AC 声明其表根,用例间无顺序依赖。 +> **致命类收窄豁免与 runner manifest**:B2 冒烟设**恰两根致命夹具**(上列④——均为 B2 表特有校验类别):runner manifest 退出码行 `rowset_dup→≠0` · `pattern_reserved→≠0`(独立 headless 进程、游戏通道,D2 AC-2b 协议同型)——"游戏内真的会死"在本表两类别上的落点由此实证;其余致命类别(**D2 R1 悬空敌种/行集缺行〔EC13〕、遭遇坐标越界〔EC14〕、通用枚举域/域校验**)由 D2 AC-2 同类别 loader 级夹具承载(表无关,豁免声明收窄保留——B2 冒烟仅以合法坐标驱动 spawn);HeroTable 单行类别条件性(⑤);schema 侧以程序构造断言域类别(AC-12/13)。每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明)。 +> **覆盖声明(out-of-scope)**——下列各项不在本冒烟域,deferral 以 **Dependencies 节 B1×6 义务清单 + D1×4 义务清单**为生效条件(两清单均"缺项即 blocking",生效条件成立):**B1×①** 行为环推进每结算恰一次与结算内先后次序(double-advance 无守卫——冒烟单调用方次序即构造序,B3 义务⑥同款);**B1×②** 雷→伏击事实转化恰一次 + 编排期授予/预截流调用次序(F3 编排半、EC17"先转化后兑付"次序半句);**B1×③** 伤害 clamp/负值/溢杀语义与死亡判定时机(Rule 1 后半——冒烟只断言 hp 递减、floor 0 与 Dead 翻转,"这一下是否兑现"归 B1);**B1×④** F4 条款 3 闭合对账(cap 表值 + 量纲 + A1 actuals p10——冒烟只供分量面与 k 可算性);**B1×⑤** 默认目标消费与单体/AOE 分流(冒烟只断言 default_target 供给面);**B1×⑥** 溢出"溢出未生效"明细行协议(呈现归 B5/D1——冒烟只断言 overflow 计数会计)。**D1×①②** 图像线索清单成文与映射每 run 恒定(Rule 8 视觉面——视觉映射不可 GIVEN-WHEN-THEN,其验收形态 = 本前言映射键契约〔映射键 = enemy_id〕+ B2 侧行为级承载:行为环零随机可学〔AC-3/AC-15〕);**D1×③** 顺位与存活可读呈现;**D1×④** 敌方 side 池呈现口径(B3 敌侧延伸)。另有:**EC8 池蒸发腿**(dur=1 轮末清池——B3 AC-8/AC-9 既有覆盖,跨引不重复;B2 侧零兑付半 = AC-7 腿3/AC-9 腿1);**EC13/EC14 端到端致命**(D2 R1/EC14 既有通道——见 manifest 豁免);**Rule 10 后半**难度修饰施加点(C3 provisional——B2 以实例化后数值运作,修饰算式无表值可断);跨战斗 HP 持久性(C1/C2);曲线数值与遭遇分布定值(C3——AC-14 显式不作分布定值断言);~~atk 激活裁决(B4/B1)~~(已随 2026-09-11 用户裁决退役——基础攻击恒存在,域断言由 AC-13/AC-14 承载);Rule 8"不直接显示数值"负空间(无意图数值 API——本前言 API 封闭枚举承载)。**qa-lead 缺口预检 7 项已处置**(🔴-1/2 入 Open Questions①②;🔴-3/4 随批修正 F4 满配语境与 EC17 切分;5–7 承载于前言 ADR 裁决项/HeroTable 可选根/坐标登记协议)。 + +**生命周期与轮转** + +- **AC-1(实例化与快照基线——Rule 2/9/10;表根 = 默认根)** GIVEN ConfigDB 默认根加载完成、全新 B2 容器(Unspawned),WHEN `spawn(L_g, D_g, M_h)`(hero_hp = max_hp 表读值——MVP 冒烟口径),THEN true;`query_alive(enemy)` 恰 **u_c** 个单位(u_c = unit_count 表读值)、全部同 enemy_id、每单位投影 **hp = h_g、max_hp = h_g、atk = a_g、pattern = p_g、cap = c_r**(行值快照五字段逐一 = 表读值——Rule 10 授予时快照基线);`query_alive(hero)` 恰 1、hp = 入参值;`query_alive(enemy)` 投影序 = 行主序生成序(默认根 u_c=1 时平凡,多敌序断言由 AC-5 夹具根承载);WHEN Alive 相位再 `spawn(同坐标, M_h)`,THEN **false + 诊断**、容器不变(相位守卫——B3 EC11 输入×相位矩阵同款)。 +- **AC-2(主角 hp 入参守卫——EC16;表根 = 默认根)** GIVEN 默认根、全新实例,WHEN `spawn(L_g, D_g, hero_hp)` 分别取 hero_hp ∈ {M_h+1, 0, −1}(越界三腿:> max_hp / ≤ 0——EC16 口径),THEN 均 **false + 诊断**且 `query_alive` 双侧恒空(**拒绝原子性:无半实例化**——失败腿不留任何单位);WHEN `spawn(L_g, D_g, 1)`(域内下界合法值),THEN true——越界拒绝精确以域为界、不扩大拒绝域(编排方供给异常显式暴露、不静默吸收)。 +- **AC-3(F1 轮转主式 + 环长 1 恒等 + 跨图延续结构裁决——Rule 4、EC10、EC11;表根 = 默认根)** GIVEN `spawn` 完成(ambusher 坐标 (L_a, D_a))。腿 1(环首初始化):`query_beat(u)` = **p_e[0]**(战斗开始指向环首)。腿 2(推进与回绕):连续 `advance` → 均 true 且 beat 轨迹 = **p_e[k mod |p_e|] 逐拍断言**,|p_e| 次后回 p_e[0](环尾回环首——指针恒 ∈ [0, 环长) 的行为级证据)。腿 3(环长 1 恒等,grub 坐标):连续 3 次(构造选择)`advance` → beat **恒 = p_g[0]**(mod 1 恒等——每拍 attack、无自然空窗,兑付率 ρ=1 的行为级形式;**前提 |p_g| = 1 按表读值核对**——pattern 内容属 C3 内容行调参,前提不满足时本腿显式 SKIP、不假失败〔2026-09-09 full 评审,AC-4 同款〕)。腿 4(跨轮跨图延续 + 无重置入口,EC11):任意长的 advance/受击序列后,hp 与 beat 投影 = 驱动序列的确定函数——**除 `despawn_all` 外无任何 API 入口使单位状态重置**(结构性裁决;"换图"事件对 B2 不可观测(零接口),端到端"真实换图下单位状态延续"归 B1 集成(编排契约:仅 battle_over 调 despawn——B1 API 合规半句,非新增义务,B3 AC-10 裁决同款)。 +- **AC-4(死亡即不动手 / 已行动已推进——EC1、EC9;表根 = 默认根,前提按 p_e 表读值核对:|p_e| ≥ 3 且 p_e[1] = charge 且 p_e[2] = heavy**——默认表值满足;pattern 内容属内容行调参〔C3 所有权〕,前提不满足时本 AC 显式 SKIP、不假失败〔2026-09-09 full 评审更正:原"结构定值满足"与期望值推导纪律自相矛盾〕)** 腿 1(heavy 拍前被杀):构造推进至 charge 拍(beat = p_e[1]),WHEN `apply_damage(u, h_e + 5)` → true、hp 投影 = **0**(**溢杀 floor:hp ∈ [0, max_hp] 不变式自守**——clamp/溢杀语义归 B1×③ 的声明不变);WHEN `advance(u)` → **false(预期冻结级——无诊断,API 面两级 false 口径)**,且 `query_beat(u)` **仍 = p_e[1]——冻结于 heavy 前一拍,重击永不兑现**("死亡即不动手"+"抢在重击前击杀"读怪收益的行为级证据)。腿 2(行动后才死):新实例构造至 heavy 拍(beat = p_e[2]),`advance(u)` → **true**;随后 `apply_damage(u, h_e)` → Dead,`query_beat(u)` = **p_e[0]**(**已行动已推进**——heavy 拍已消费、指针回绕)。腿 3(不可逆):对 Dead 句柄 `apply_damage` → **false + 诊断**(MVP 无复活——States 不可逆)。 +- **AC-5(多敌同步轮转与同步定理——Rule 2、EC2;表根 = `enemy_multi_unit`,u_c_f = 3 夹具登记值〔ambusher 行,2026-09-09 full 评审 blocking ③〕)** GIVEN 夹具根 `spawn(L_a, D_a, M_h)` 完成。腿 1(同拍基线):spawn 后三单位 `query_beat` **恒等**(同种同指环首、同属性)。腿 2(死亡时序不乱拍):定义结算步 = 按生成序对各存活**敌**单位 `advance` 恰一次(B1 义务①的冒烟构造等价——hero 无环、不在推进域,Rule 4);在某步中于 `advance(u2)` **之前** `apply_damage(u2, h_e)` 杀之(轮到它前被杀——h_e = ambusher 行 hp 表读值)→ u2 本步不打(advance false、指针冻结);THEN 步末 `query_beat(u1)` = `query_beat(u3)`,且连续多步后幸存者 beat **恒相同**(**同步性质定理——任何死亡时序都不会让幸存者乱拍**,F1)。腿 3(单位数单调递减):全程 `query_alive(enemy)` 规模 ∈ {3, 2, 1} 严格递减、无增员(EC5 负空间的行为面——无 summon API)。 +- **AC-6(死亡状态机与全灭消亡——Rule 1、EC3、EC4;表根 = 默认根 + AC-5 根)** 腿 1(全灭,EC3):u_c 个单位逐一 `apply_damage` 致死 → `query_alive(enemy)` = **空 Array**(存活敌集 = ∅,"敌人全灭"成立——胜负判定归 B1);敌侧伏击池"无兑付者轮末清空"半 = B3 dur=1 decay(跨引 B3 AC,out-of-scope)。腿 2(主角死亡与消亡,EC4):`apply_damage(hero, M_h)` → hero hp = 0(失败判定素材——判定时机归 B1);`query_alive(enemy)` 仍含存活单位时 `despawn_all()` → true、`query_alive` **双侧空**(**全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态**);Despawned 句柄上 `query_beat` 返回空 + 诊断、`advance`/`apply_damage` → false(域闭);WHEN 重复 `despawn_all()` → **false + 诊断**(重复调用守卫——B3 EC11 同款);WHEN 重新 `spawn` → true 且为新战斗(Despawned 后唯一出口——下一场重启、不跨战斗,States 表)。 + +**伤害构成与合成序** + +- **AC-7(F2 三拍构成 + 合成序判别 + charge 零兑付 + 池共享读值即时性——F2、Rule 6、EC8-B2 半、EC17;表根 = 默认根)** GIVEN `spawn(L_a, D_a, M_h)` 完成、m_amb = StatusEffectTable.ambush.magnitude 表读值。腿 1(attack 拍):`query_attack(u, L=2, m_amb)` → **{baseline = a_e, attach = 2×m_amb, total = a_e + 2×m_amb}**(分量二元组——"攻击 13 = 基线 4 + 伏击 9"P2 归因行的数据源,F2 供给建议)。腿 2(**合成序判别**;**判别非平凡前提:m_e ≥ 2**——默认表值满足;m_e = 1〔合法 knob,heavy ≡ attack〕时负断言两边恒等、本腿显式 SKIP 标记输出——B3 AC-14 腿 3 先例,2026-09-09 full 评审 blocking ②):L=4 → **baseline = a_e×m_e、attach = 4×m_amb、total = a_e×m_e + 4×m_amb**,且**判别负断言:total ≠ (a_e + 4×m_amb)×m_e**(伏击附加是后置加法、不进倍乘——默认表值 (4, 2, 3) 下 20 ≠ 32,非平凡性由表读值不等式保证;实现若把伏击分量塞进倍乘即在此暴露;L=4 为游戏态可达值〔cap=4 上界〕——探针不取域外值)。腿 3(charge 零兑付——不读池,EC8 的 B2 半):beat 推进至 charge 拍后 `query_attack(u, L=c_r, m_amb)`(游戏态上界注入)→ **{0, 0, 0}**——**L>0 下恒零 = "不读伏击池"的可判定形式**(蓄力轮授予的伏击层当轮蒸发 = 读怪奖励;池蒸发本身归 B3 decay,out-of-scope 跨引)。腿 4(共享不消耗 + 即时读值,EC17):同 L 连续两次 query 结果逐字相同;两存活单位同 L 各自 query 读值一致(共享、非均分、非先到先得;**本腿在 `enemy_multi_unit` 根承载**——默认根 u_c=1 不可构造,2026-09-09 full 评审);**每次 query 前后 `query_alive`/`query_beat` 投影零变化**(纯函数性——B2 无池写面、无内部缓存、无时间维度;"先转化后兑付"次序与每雷恰一次归 B1×①②,out-of-scope)。 + +**伏击授予与溢出** + +- **AC-8(F3 授予分解守恒 + 灾难轮 + cap=0 免疫——F3、EC6、EC7;表根 = 默认根,c_r = cap 表读值)** 全腿为纯函数断言(编排期预截流调用与每雷恰一次授予归 B1×②,out-of-scope)。腿 1(守恒主式):`ambush_grant_plan(mines_r = c_r + 2, c_r)` → **{granted = c_r, overflow = 2}**,恒等 granted + overflow = mines_r(**零静默蒸发**——P2 会计面;"溢出未生效"明细行呈现归 B1×⑥/B5)。腿 2(预截流位,参数化):对 mines_r ∈ {0, 1, c_r, c_r+1} 各腿 granted = **min(mines_r, c_r)**、overflow = mines_r − granted 恒成立(cap 判定在授予前)。腿 3(灾难轮,EC7):mines_r ∈ [5, 8](默认 c_r=4 → granted = 4、overflow = mines_r − 4——**cap 反灾难护栏绑定域**)。腿 4(cap=0 免疫,EC6):`ambush_grant_plan(m, 0)` → **granted = 0、overflow = m**(全溢出——免疫行全部可见、全部无效的会计半;cap=0 为域内合法行值的校验半归 AC-12)。 +- **AC-9(F4 k 可算性与结构上界——F4、EC8 汇总;表根 = `enemy_multi_unit`,u_c_f = 3)** 腿 1(k 确定性可算):以 advance 序构造混合拍(k 个单位处 attack/heavy 拍、其余 charge)→ 每结算敌方伏击暴露 = **k × L × m_amb**,其中 k 经只读观测量导出(`query_alive(enemy) ∩ query_beat ∈ {attack, heavy}` 计数——**确定性可算、无隐藏状态**,B2 对 B1 条款 3 的供给面);全体 charge 轮 → k = 0、暴露 = 0(**charge 轮零兑付**——EC8 汇总)。腿 2(结构上界记号):单结算 side 级结构上界 = **u_c_f × min(AP_r, c_r) × m_amb**(AP_r = ActionEconomyTable.ap_per_round 表读值;全参表读值/夹具登记值的**记号恒等式断言**——满配语境 3×min(8,4)×3=36 的记号化,默认行值 unit_count=1 下 = 12,非定值断言;min 项 = 2026-09-09 full 评审收紧——cap > AP 界时 cap 腿松弛);跨结算累计 = 轨迹量非表值——B2 投影字段无任何累计观测量(负空间,AC-1 观测量封闭枚举承载);不等式闭合对账(输入 = A1 actuals p10)归 B1×④,out-of-scope。 + +**目标选择** + +- **AC-10(固定顺位供给面——Rule 7、EC3、EC15)** 腿 1(u_c=1 合法退化,默认根):`spawn` 后 `default_target()` = 该唯一存活单位(**顺延与 AOE 分流无消费者、AOE ≡ 单体——合法退化非错误**,Rule 7 空转)。腿 2(行主序最前,`enemy_multi_unit` 根):`default_target()` = `query_alive(enemy)[0]`(**生成序最前——顺位快照 = 生成序**)。腿 3(死亡顺延):杀 [0] → `default_target()` = 原 [1](顺延);连续杀至空 → **null**(EC3 全灭腿在目标面的汇合)。B1 消费侧单体/AOE 分流归 B1×⑤、顺位可读呈现归 D1×③,均 out-of-scope。 + +**表校验与快照** + +- **AC-11(行值快照抗表重载——Rule 10、EC12;表根 = 默认根 → `enemy_reloaded`)** GIVEN 默认根 `spawn(L_a, D_a, M_h)` 完成(快照字段 = 默认表值;坐标登记协议锚点,2026-09-09 full 评审补)、存活与 Dead 单位并存。WHEN 经测试通道换根加载 `enemy_reloaded`(`load_tables(root, collect_only=true)` 直调——非 D2 `reload()` 路径,门语义归 D2 AC-16/B1),THEN 存活与 Dead 单位的**快照五字段 + beat + hp 逐字不变**、hero 投影不变(**授予时快照——实例不受任何表重载影响**,B3 EC9 / A4 EC8 同款);WHEN `despawn_all()` 后以同坐标重新 `spawn`,THEN 新单位快照 = **漂移后表值**(换根只影响后续实例化——"快照非活引用"的判别性后半)。腿 3(拷贝判别,2026-09-09 full 评审增补):对返回的 pattern 投影数组原地改写 → 容器内单位 pattern 投影不变(**快照拷贝非活引用**——别名引用在换根加载下同样"逐字不变",本腿补足判别力)。 +- **AC-12(EnemyTable schema 域校验全谱——程序构造,Rule 3/4)** GIVEN EnemyTable schema 类(行级 + 表级校验)class_name 直引实例(不经 load()/preload())。WHEN `validate()` 分别作用于越域代表值:unit_count ∈ {0, 4}、hp ∈ {0, 1000}、atk ∈ {−1, 1000}、heavy_multiplier ∈ {0, 17}、pattern 环长 ∈ {0, 9}、pattern 元素 = **"guard"(预留值拒绝——MVP 校验域 fail-fast,A1 solver_rules 冻结先例)**、pattern 元素 = "nonsense"(未知枚举)、cap ∈ {−1, 17},THEN 各含对应域错误类别(零静默);WHEN 表级构造同 (level_index, node_depth) 两行相异 enemy_id,THEN 含**每行集恰一敌种**错误类别;WHEN 作用于域内代表值:unit_count ∈ {1, 3}、hp = 1、**atk = 0(纯伏击伤敌种合法——F2 Variables)**、atk = 999、heavy_multiplier ∈ {1, 16}、pattern ∈ {["attack"], ["attack","charge","heavy"]}(环长两界)、**cap = 0(免疫极端合法——EC6)与 16**,THEN 零诊断(防过度约束锁死内容——atk=0 与 cap=0 两个合法极端是本断言承重点)。 +- **AC-13(HeroTable schema 域校验——程序构造,Rule 9)** GIVEN HeroTable schema 类直引实例。WHEN `validate()` 作用于 max_hp ∈ {0, 1000}、atk ∈ {−1, 0, 1000},THEN 各含对应域错误类别(**atk=0 拒绝 = 2026-09-11 用户裁决的 schema 承重腿——零基线死锁杜绝**);WHEN 域内代表值 max_hp ∈ {1, 999}、atk ∈ {1, 999},THEN 零诊断;WHEN 表级构造两行(域内合法),THEN **单行全局约束**错误类别(单行全局——Rule 4 豁免同 ActionEconomyTable 先例;若实现期 ADR/D2 裁决单行校验落 loader 缝,则本腿迁移至可选根 `hero_extra_row` 的 manifest 腿——前言⑤,两处恰一承载)。 +- **AC-14(MVP 内容行定值与结构断言——Rule 3/9;表根 = 默认根)** GIVEN ConfigDB 默认根加载完成。WHEN 读 EnemyTable,THEN 恰含双敌种行、**enemy_id 集合 = {grub, ambusher}(定值——Rule 3 MVP 收窄)**;每 (level_index, node_depth) 行集恰一敌种(默认根数据通过其自身表级校验的行为断言);全部行 pattern 元素 ⊆ **{attack, charge, heavy}(MVP 枚举封闭——结构定值)**;grub 行 pattern 不含 "heavy"(**heavy_multiplier 无 heavy 拍不消费**的表数据前提——非校验项、B3 max_stack 先例);display_name 双值非空(精确值不作定值断言,本地化归 D6);**遭遇分布(何 (L,D) 归何敌种)为 C3 域 knob——不作定值断言**(provisional 示例非承诺)。WHEN 读 HeroTable,THEN 恰 1 行、**atk ∈ [1,999] 域断言(MVP 值 6 = 原型锚 provisional,不作定值断言;0 = 加载拒绝——2026-09-11 用户裁决)**、max_hp ∈ [1, 999] 域断言(provisional knob 不写死)。 + +**确定性** + +- **AC-15(零随机双遍回放——Rule 4/11;表根 = 默认根)** GIVEN **全新 B2 实例 ×2**(同 ConfigDB 默认根;每遍全新实例、无跨实例状态)。WHEN 同一定长序列驱动两遍(spawn → `apply_damage`(含一次"轮到前击杀"腿——冻结路径入回放)→ 交替 `advance` 全体存活敌单位 → query 投影(含生成序与逐单位 beat)→ `apply_damage` → `advance` → `default_target` → query 投影),THEN 两遍的 `query_alive` 投影序列(**含生成序**)、逐单位 beat 轨迹、`default_target` 返回序列**逐项相同**——零随机的行为级硬证据(rng_service 流封闭;D2 AC-15 的 B2 层前身,与 AC-3 腿 4"状态 = 驱动序列的确定函数"互证)。 + +**registry 一致性** + +- **AC-16(registry 一致——Rule 6 回写义务;B3 AC-18 同型,无条件断言)** GIVEN ConfigDB 默认根加载完成。WHEN 脚本解析 `design/registry/entities.yaml` 的 `enemy` 与 `hero` 条目(**逐字段 YAML 键**——机器对账前提,action_economy/status_effect 同构),THEN:① `enemy.fields` 八键(enemy_id / display_name / unit_count / hp / atk / heavy_multiplier / pattern / advantage_per_round_cap)与 ConfigDB EnemyTable schema 字段名逐一相等、各键类型/域/枚举描述与 Schema 定稿相等(**缺任一键即失败**);② `hero.fields` 两键(max_hp / atk)同上;③ enemy 条目 MVP 内容注记物种 id 集合 = {grub, ambusher},与表行 id 集合相等(非 knob 结构定值);④ `status_effect.ambush` 条目含 **B2 量纲定稿注记**(Rule 6 兑现 B3 registry OQ2 的接口漂移回写落点——注记存在性 + 键断言;量纲活值 m_amb **不作定值对账**——knob 纪律,A4 AC-8 同款豁免);⑤ hero 条目"单行全局 Rule 4 豁免"注记与 enemy 条目"每行集恰一敌种校验"注记存在性断言。**生效前提**:本条目为本文定稿 Phase 5b/5d 回填的产物,AC 生效前提 = 回填完成(不写"若存在"软口径——条件化使一致性检查落空,B3 AC-18 同款)。 + +**静态扫描** + +- **AC-17(静态扫描——D2 AC-9/AC-10 模式引用;Rule 11)** B2 文件集(单位容器 / 两表 schema / 冒烟脚本 / 夹具规格)纳入 D2 AC-9 模式项目级扫描域:`randomize()` / `RandomNumberGenerator` / ConfigDB 外 data 表加载 / `extends Control` / `.tscn` preload——零命中(**schema 与容器的 class_name 直引实例化豁免**——AC-12/13 前提,A4 AC-7 同款);另扫 B2 特有两项:**事件订阅 API 零命中**(Rule 11 零主动事件订阅——A2 事件面 referenced_by 不含 B2 的代码侧对应)与 **B3 API 符号零引用**(相邻零运行时依赖——import/call 面为零);D2 AC-14 人工三要素字面量扫描(引用不另设硬 THEN——F2 算式 a×m + 3L 类字面量随实现形态误报率高,D2 已定性 warning 级 + 人工复核;其"零玩法数值字面量"的行为级硬证据 = AC-1/AC-7 全表读值推导 + AC-16 registry 对账)。零随机的行为级硬证据 = AC-15。 + +**覆盖矩阵(qa-lead 交付,验收评审用)** + +- Rule 1→AC-1/AC-6/AC-2(clamp·溢杀·死亡时机→OOS B1×③)· Rule 2→AC-1/AC-5/AC-9(side-wide 兼容与 target_ref 扩展位→B3 接口漂移义务随批 B1/D1,非冒烟域)· Rule 3→AC-12/AC-14/AC-13 · Rule 4→AC-3/AC-4/AC-5/AC-15(预留值拒绝→AC-12+manifest `pattern_reserved`)· Rule 5→AC-8(每雷恰一次→OOS B1×②;溢出明细行→OOS B1×⑥+B5)· Rule 6→AC-7/AC-16④(量纲锚验收→B4/B1,排除声明)· Rule 7→AC-10(消费分流→OOS B1×⑤;顺位可读→D1×③)· Rule 8→前言映射键契约(enemy_id)+AC-3/AC-15(行为环可学性承载)+OOS D1×①②、"不显示数值"负空间=API 封闭 · Rule 9→AC-1/AC-2/AC-13/AC-14 · Rule 10→AC-1/AC-11(D2 R1 解析致命→OOS D2 通道;难度修饰→C3 provisional)· Rule 11→AC-15/AC-17(调用权集中→OOS B1×①) +- F1→AC-3/AC-4/AC-5 · F2→AC-7 · F3→AC-8 · F4→AC-9(闭合对账→OOS B1×④) +- EC1→AC-4 腿1/腿2 · EC2→AC-5 腿2 · EC3→AC-6 腿1+AC-10 腿3 · EC4→AC-6 腿2 · EC5→前言 API 封闭(负空间——无 summon API 不可 G-W-T,B3 Rule 8 先例)+AC-5 腿3+AC-17 · EC6→AC-8 腿4+AC-12(cap=0 合法腿) · EC7→AC-8 腿3 · EC8→AC-7 腿3+AC-9 腿1(池蒸发腿→B3 AC 既有覆盖,OOS 跨引) · EC9→AC-4 腿1 · EC10→AC-3 腿3 · EC11→AC-3 腿4(跨图结构裁决)+AC-2/AC-6(相位重复守卫,B3 EC11 同款) · EC12→AC-11 · EC13→OOS(D2 R1 通道,D2 AC-2 同类别夹具,manifest 豁免声明) · EC14→OOS(D2 EC14 通道,同前) · EC15→AC-10 腿1 · EC16→AC-2 · EC17→AC-7 腿4(读值即时性/纯函数半)+OOS B1×①②(次序半) +- **无洞**:17 条 EC 中 EC5/EC13/EC14 三处非 G-W-T 形态,分别以 API 封闭负空间声明与 D2 通道豁免承载(B3 Rule 8 负空间 + 致命收窄豁免先例同款);其余全部映射到 AC 或显式 OOS(deferral 生效条件 = B1×6/D1×4 已登记缺项即 blocking)。 + +## Open Questions + +1. **`query_attack` / `ambush_grant_plan` 两 face 归属**(owner:实现期 ADR 定名时同步裁决;deadline:Technical Setup/实现期):B2 供给面 vs B1 编排消费——F2"供给建议"与 F3"预截流归 B1"边界注记的张力落点;裁决归 B1 则 AC-7/8/9 对应腿迁移至 B1 冒烟(断言口径不变),回写 AC 前言(qa-lead 缺口 🔴-1)。 +2. **多敌语义默认零消费者复验**(owner:C1 遭遇分布定稿;deadline:C1 GDD):默认 unit_count=1 下多敌路径仅测试根可达(死码风险)——C1 落地 unit_count>1 真实遭遇后复验 AC-5/9/10 夹具根是否升级为默认根内容(qa-lead 缺口 🔴-2);**C1 须真实消费 unit_count>1**(Boss/精英战最廉价的构成表达),且 Boss 表达不能只靠 unit_count——同种同拍 ×3 = 伤害乘法、非 Boss 幻想(2026-09-09 full 评审注记)。 +3. **条款 3 左臂载体(2026-09-09 full 评审扩项;2026-09-11 随基础攻击裁决收窄)**(owner:B1;deadline:B1 GDD 强制检查点):原"hero atk=0、B4 缺位 → 左臂(安全格 buff 当量)无伤害兑换载体 + 当前内容态结构性不可通关"事实已退役(基础攻击恒存在、attack_up 复活、B4 已设计并切片落地);残余 = 左臂**当量换算口径**(锚参照回迁 attack_up 当量——B4 F4 / registry anchor_warning)随 B1 对照表对账,B1 检查点核对。 +4. **原型参考值溯源**(owner:本 GDD 回填定稿时;deadline:EnemyTable/HeroTable 定值回填):敌 18HP/4ATK、伏击 +3、hero max_hp 均为 provisional 且**出处待查**(D2 Tuning Knobs 既有登记义务)——回填定值前补原型 REPORT 溯源,查无出处则按 C3 调参所有权重定。 +5. **HeroTable 单行校验落点**(owner:实现期 ADR/D2;deadline:Technical Setup):schema validate() vs loader 缝两处恰一承载(AC-13 腿3 / 可选根 `hero_extra_row` manifest 行)——实现期 ADR 定。 +6. **标错惩罚候选与伏击清单联动(2026-09-09 full 评审 blocking ④ 更正事件源类)**(owner:A2 OQ1 实测会话;deadline:A2 /design-review 核对在位):候选"敌方伏击+1"若实测采纳——其事件源为 **flag_judged{hit=false}(旗落非雷格),不在 Rule 5 cell_revealed{kind=mine} 转化路径内**;B3 需新增行且 source_class 需新枚举值(如 flag_miss)——**source_class 枚举属 B3 结构冻结项,须走 B3 GDD 修订(接口漂移),非"加行"**;B2 侧伏击清单联动修订 + F3 重验触发(非 mine_hit 源)强制重跑。若错标为 mine_hit:P2 归因明细行来源失实(标错被记成踩雷)。〔原文"B3 加 mine_hit 新行"为事件源类错误,废弃〕 +7. **精英/Boss 遭遇编码位预登记(2026-09-09 full 评审 blocking ⑤;owner:C1/C3;deadline:C1 GDD 定稿强制核对)**:难度 = f(关卡序号, 节点深度, 精英/Boss 修饰) 是 game-concept 定稿、A1 BoardGenTable 已预留 elite_/boss_ 命名空间与改键退路;本表键冻结 (level_index, node_depth, enemy_id) 且"每行集恰一敌种"入结构冻结——若 C1 定稿分支地图使**同深度并存普通与精英节点**,精英编码无键位(同深度第二敌种行触发行集校验致命)。届时改键(如追加 encounter_class 维度)走 D2 Rule 4 接口漂移修订,schema/AC-12/夹具/registry 连锁回写——预登记于此(A1 OQ6 镜像)。 +8. **grub 单拍环体验证伪(2026-09-09 full 评审登记;owner:C3/playtest;deadline:垂直切片 playtest)**:grub 环长 1、无前摇无空窗——玩家可能将其感受为"自动平砍的血条"(恰为 Player Fantasy 反向对照);MVP 实际读怪内容仅剩 ambusher 一环。预注册证伪项:playtest 采集"grub 是否被读"信号;证伪则两拍环备用案(如 [attack, charge])由 C3 内容所有权处置,本 GDD 结构不变(CD 终裁:登记风险、不改结构)。 +9. **零伤害死局组合与跨表量级护栏(2026-09-09 full 评审登记;2026-09-11 收窄;owner:实现期 ADR/D2;deadline:Technical Setup)**:hero 侧恒零伤害分支已被 HeroTable.atk 域下界([1,999],0 加载拒绝)结构杜绝;残余 = 行 atk=0 ∧ cap=0 → 无威胁敌(内容调参禁区、非死局);atk×mult 上界与 max_hp 域无跨表量级护栏——遭遇行配置期跨字段校验落点裁决(ActionEconomyTable 跨字段校验先例),详见 Tuning Knobs Knob 交互注记。 diff --git a/design/gdd/legacy-2026-09-13/b3-status-effect.md b/design/gdd/legacy-2026-09-13/b3-status-effect.md new file mode 100644 index 00000000..d2de673c --- /dev/null +++ b/design/gdd/legacy-2026-09-13/b3-status-effect.md @@ -0,0 +1,320 @@ +# B3 增益/状态系统(Status Effect System) + +> **Status**: Designed(2026-09-08 一次会话定稿;lean 门禁:systems-designer 起草 Formulas · qa-lead 起草/验证 AC;CD-GDD-ALIGN 按 lean 跳过,生产前人工复核 Player Fantasy)。**2026-09-09 full 评审 NEEDS REVISION,7 blocking 同日核销**:enemy 侧单敌假设入文〔用户拍板〕· B1 义务 5→8 · D1/B5/D5 呈现义务登记 systems-index · AC-15 机制更正 + reloaded 根全清单规格 · 致命夹具补根〔dup_key〕· AC-18 随 registry 逐字段键重构 · weaken 红警补修复空间不对称 + OQ1 归一口径前置 · stack-不重置 R 登记 OQ7 playtest 证伪项。**weaken 行条款 2 锚红色预警随文登记,B1 对照验收优先裁决** +> **Author**: user + agents(systems-designer〔Formulas〕· qa-lead〔AC〕;三结构决策〔单一计数/按行叠层/四行定稿权收编〕为用户拍板) +> **Last Updated**: 2026-09-08 +> **Implements Pillar**: P2(主——可归因兑现的机制落点)· P3(敌方增益/debuff 的兑付面)· P1(时限透明的规划素材)· P4(间接) +> **Layer**: 1 Foundation · **Milestone**: MVP · **设计顺序**: 第 4 位 + +## Overview + +B3 是战斗域的**统一状态容器**:凡是"不在当场一次性、而要在若干次轮次结算里持续起作用"的战斗效果——安全格给主角的增益、踩雷给敌人的增益、标中旗给怪物的削弱——都作为**状态实例**存放在这里,由同一套机制定义它们能活多久(己方限时增益**跨轮次延续**、时限按**剩余结算次数**计,每轮结算衰减一次;**不跨战斗**——game-concept 定稿)、同种效果如何叠加或刷新、何时到期消失。它同时是玩家最直接的兑现载体:扫雷阶段挣到的每一份效果都不当场生效,而是在轮次结束的战斗结算中逐条兑付、逐条可见(P2"扫雷的因,战斗的果"的机制落点)——玩家不操作 B3,但每一轮都能在结算明细里看到它的工作成果。 + +机制与内容分离是 B3 的结构原则:B3 只拥有**机制**——时限/叠层/衰减语义、状态实例生命周期(战斗域)、供 B1 结算消费的兑付接口与来源标注字段(P2 归因);效果**内容**的经济归属归各锚 owner(安全格 buff 与标中旗 debuff 档位锚验收归 B4、敌方增益上限归 B2),而**四行 MVP 初值的定稿权经 Rule 10 收编归本 GDD**(用户拍板;锚验收权仍留 B4/B2/B1),数值落在 D2 的 StatusEffectTable(本 GDD 为回填 owner),呈现归 D1/B5。没有 B3,三向效果映射的每一条都会各自手搓时限与叠加规则,A3 F1 要求的"四源统一当量、单一线性换算"失去对照载体,B1 结算要对接多套各异的效果语义,可回放步骤流(结算禁 await 的确定性要求)也无从保证——统一容器让"效果"成为一门单一语言。 + +## Player Fantasy + +**直接承载**(Section A 定稿:玩家不操作 B3,但直接感受其工作成果)。B3 的情绪目标一句话:**"我攒下的东西正在替我打架"**——结算画面逐行生效的那一刻,玩家看到的不只是伤害数字,而是自己前几轮规划的逐条回放:第三轮挖到的增伤还在(还剩 1 次结算)、标中的旗削弱了怪物(这一行是我标对了)、开错的那颗雷喂了敌人伏击(这是我赌输的代价)。B3 是 P2"规划的延迟兑现"从口号变成可见事实的机制落点。 + +体验锚点(按支柱): + +- **P2 可归因的兑现感**:每个状态实例携带来源标注(安全格/标中旗/清图/雷),结算明细逐行生效——P2 裁剪判据的准话测试"这个结果是我哪步扫雷带来的?"在 B3 的字段里就有答案。 +- **P3 同源的显影**:雷的两面都要看得见——踩雷的敌方增益行与标中旗的削弱行在结算明细里使用同一呈现语言,玩家由此学会"雷是赌桌不是陷阱";旗标对的即时回报部分补偿 chord 缺席的兑现节拍缺失(CD 创意风险 #3 的关联缓解,正式高光设计归 B1/B4)。 +- **P1 的规划素材**:增益有明确的剩余结算次数与到期时机,玩家才能把"还有 2 次结算的增伤"纳入挖哪格的推理——时限语义的透明度是规划感的前提。 + +参考感受:杀戮尖塔的增益图标(时机与层数一目了然,是决策素材而非装饰);Peglin 神器在关键时刻叠加生效的"攒出来的强大"。**反向护栏**:状态效果绝不能是无感的暗账——若玩家说不出一行结算"从哪来",来源标注即失职(违反 P2);任何让 buff 时限/剩余次数不可读的设计默认砍掉。 + +> 注:`creative-director` 未经咨询——Lean 模式(仅 D/H 两节委托专家),生产前请人工复核本节情绪定位。 + +## Detailed Design + +### Core Rules + +1. **统一状态容器(机制唯一)**:全部三向效果——安全格→主角 buff、踩雷→敌方增益、标中旗→怪物 debuff——都是同构的**状态实例**,存于同一容器、同一套时限/叠层/衰减机制。容器生命周期 = 战斗域:战斗开始时为空,`battle_over` 全清(不跨战斗,game-concept 定稿);**跨轮次、跨扫雷图延续**(换图/刷新不清空状态——旗标记随图消亡归 A2,状态实例不随图)。**零随机**:容器无任何 RNG 消费(rng_service 流封闭,P1)——授予、合并、衰减、枚举顺序全确定。**单敌假设(2026-09-09 评审定稿,用户拍板)**:MVP 战斗 = 单个敌人——target_side=enemy 在该前提下无歧义(enemy 侧 side-wide 即该敌全体)。B2 GDD 定敌人数模型时须回验本假设:定多敌即触发 target_ref 接口漂移补列(B1 归集语义/B2 敌人数模型/D1 呈现裁决随批验收,义务登记 systems-index)。 +2. **单一时限量纲(用户定稿)**:全部状态用 `remaining_settlements`(剩余结算次数)计数——持续型 = N(MVP 行默认 2),一次性(标中旗 debuff、伏击类)= 1。衰减时机 = **每轮结算兑付后统一 -1**(先兑付后衰减:duration=1 的状态恰好在授予它的那轮结算生效一次后移除);归 0 即移除。己方限时增益跨轮次延续由此自然成立(停手轮/弃图轮也是结算,照常衰减——不存在"不结算就不掉次数"的免费延时)。 +3. **授予集中在结算编排、来源全程携带**:挖掘期不直接写容器——A2 上报事实(开格/标中/开雷),**B1 在结算编排期统一调用 `apply_effect()`**(B3 唯一授予入口;A2/B4 与 B3 零直接接口)。每个实例携带 P2 归因字段:`source_class ∈ {safe_cell, flag_hit, mine_hit, clear_reward}` + `origin_cell` + `round_index`——结算明细逐行标注的数据源。授予即入容器(无 pending 态):首个结算就是它的第一次兑付。 +4. **叠层按行配置(用户定稿)**:同 `effect_id` 重复授予时按表行 `stack_policy` 处理——**stack**(层数 +1,封顶 `max_stack`)/ **refresh**(剩余次数重置为行默认、层数不变)/ **independent**(并存独立实例)。合并发生在授予时(容器内),结算作用顺序归 B1 SettlementTable——**两序分离**。stack 的作用数学 = **加算**(有效值 = 层数 × magnitude),即 F1 条款 1 单一线性换算的载体。MVP 四行全用 stack;refresh/independent 语义已定义、实现承载,内容行按需启用。 +5. **满层授予 = 拒绝 + 溢出可见**:stack 行已达 `max_stack` 时的授予返回 false,B1 结算明细标注“溢出未生效”行(含来源)——奖励不静默蒸发(P2 诚实),玩家由此获得“满层时别再拿同款”的规划素材(P1)。溢出判定与授予路径无关——直接事实、B1 转化、连锁展开自动开出的奖励格均经 `apply_effect` 同一入口,满层时同样产生溢出行(含来源)。 +6. **兑付接口(B1 消费契约,provisional 登记)**:`notify_battle_start()` / `apply_effect(effect_id, source) -> bool` / `query_active(side) -> Array`(插入序稳定枚举;返回元素 = 六归因字段 + `display_name` + **授予时数值快照 (magnitude, unit)** 的只读投影——qa-lead 载荷口径)/ `decay_round() -> int`(返回移除数)/ `notify_battle_end() -> bool`。B1 结算顺序(哪个效果先乘)归其 SettlementTable;B3 只保证枚举确定性。最后一轮(敌人全灭)是否 decay 归 B1 判定次序域(A3 EC8 同款)。**全部 API 布尔/计数返回、无 await、无表现面**——可回放步骤流的状态侧前提。 +7. **效果语义映射归 B1**:B3 只承载通用数值字段(magnitude/unit);"增伤到底加什么"的效果语义到结算公式的映射归 B1 SettlementTable(effect_id 为键)——B3 不知道增伤怎么算,只知道它还剩几次结算。 +8. **MVP 无驱散/无延时/无转移**:玩家不能移除、延长、转移任何状态(无对应 API)——"不能做"与"能做"同样明确;未来扩展走接口漂移。 +9. **数值承载于 D2**:StatusEffectTable(本 GDD 回填 owner,schema 见下节);B3 代码零玩法数值字面量(D2 Rule 1)、读取经 ConfigDB。 +10. **MVP 内容行四行定稿(用户拍板,B3 收编定稿权)**:四行初值由本 GDD 定稿(见下表);**B4/B2/B1 持验收与修订权**——F1 条款 2(debuff 档位严格 > 最低档 buff 当量)与条款 3(B2 惩罚上限)作为行值的**验收锚**登记,修订走接口漂移规则回写 registry。此为对 A3 OQ5"档位归 B4 表设计"的显式收编:**初值定稿权归 B3,锚验收权仍归 B4/B1**——登记为对 A3 OQ5 的修订注记(Phase 5 回写)。 + +### StatusEffectTable Schema 定稿(D2 回写义务) + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| effect_id | String(主键唯一) | 状态标识;B1 SettlementTable 语义映射的键 | +| display_name | String | 图例/HUD/明细用名(本地化归 D6) | +| target_side | enum {hero, enemy} | 作用侧(MVP 单敌假设:enemy 侧 side-wide = 该敌,见 Core Rule 1) | +| magnitude | int | 域随 unit:percent ∈ [-100, 100] 且 ≠0 / flat ≥ 1(跨字段校验) | +| unit | enum {percent, flat} | percent = 百分比修正;flat = 固定值(伏击类) | +| duration_settlements | int [1, 99] | 授予时初始剩余结算次数(一次性 = 1) | +| stack_policy | enum {stack, refresh, independent} | 同 id 重复授予的合并策略(Rule 4) | +| max_stack | int [1, 16] | 层数上限;仅 stack 消费——refresh/independent 行约定恒 1(内容约定,非校验项) | + +**MVP 内容行(四行,值全部 provisional——验收锚见 Rule 10)**: + +| effect_id | 名称 | side | magnitude | duration | stack/max | 来源 | provisional 依据 | +|---|---|---|---|---|---|---|---| +| attack_up | 增伤 | hero | +50 percent | **1**(2026-09-11 两级奖励批——当次结算语义:授予结算兑付于主角先手攻击后即衰减,原 D=2 作废) | stack / 2 | safe_cell | 原型临时值 1.5× 换算(D2 knobs 占位) | +| defense_up | 减伤 | hero | −50 percent | **1**(2026-09-11 两级奖励批——当次受伤减免,原 D=2 作废) | stack / 2 | safe_cell | 原型临时值 0.5× 换算 | +| ambush | 伏击(敌方增益) | enemy | +3 flat | 1 | stack / 16 | mine_hit | 原型临时值 +3(量纲归 B2 定稿验收,F1 条款 3 锚) | +| weaken | 削弱(标中旗 debuff) | enemy | −25 percent | 1 | stack / 16 | flag_hit | 无原型定值(+5 已作废);须过 F1 条款 2 锚(严格 > 最低档 buff 当量,B1 对照验收) | + +**校验**:effect_id 唯一(重复键致命);枚举域校验(target_side/unit/stack_policy 出域致命);duration/max_stack 域校验;跨字段 percent ≠ 0 / flat ≥ 1。表非空(D2 Rule 6,四行即满足)。键结构 = effect_id 定义表——**D2 Rule 4 豁免**(定义表非进度成长表,无 (level_index, node_depth) 维度;未来随关卡成长的效果走接口漂移加列,A4 先例)。 + +### States and Transitions + +容器无运行时大状态机——实例生命周期四态(战斗域)。容器为**纯逻辑类**(非 autoload、无场景依附),每场战斗实例化一次(B1 编排前提;冒烟经 class_name 直引构造): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| BattleClosed(域闭) | 构造后未开域 / notify_battle_end() 后 | apply_effect 全拒绝(false);唯一出口 notify_battle_start() | +| Active(存续) | apply_effect 受理(新实例/叠层/刷新) | 每轮结算被 B1 兑付消费;decay_round() 后 remaining −1 | +| Expired(移除) | remaining_settlements = 0 | 实例销毁(明细流水已由 B1 记录,B3 不留历史) | +| Cleared(清空) | notify_battle_end() | 全实例销毁、容器回空 → BattleClosed(不跨战斗) | + +**实例不变式(debug 断言口径)**:remaining ∈ [0, duration_settlements];stack 行 layers ∈ [1, max_stack];stack/refresh 策略下同 id 至多一个实例——违反即 debug 断言中止;API 违规(域闭授予、未知 effect_id、满层授予)= 拒绝(false)+ debug 诊断,不进程致命(承 A3 拒绝口径;进程致命保留给 D2 数据通道)。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| D2 | 双向 | StatusEffectTable schema + 四行 MVP 内容回填(本 GDD 义务);读取经 ConfigDB;新字段走接口漂移规则 | +| B1 | 双向(hard,**provisional**——B1 未设计) | 结算编排期调用:notify_battle_start / apply_effect(三源授予:B1 从 A2/A3 事件收集 round facts 后转化)/ query_active(side) 兑付消费 / decay_round() 轮末衰减 / notify_battle_end() 清空;结算明细来源标注数据源 = 实例字段(P2);效果语义到结算公式映射归 B1 SettlementTable(effect_id 键);F1 条款 1 对照表的“实物”供给方;最后一轮 decay 与否归 B1 判定次序域;**义务清单 8 项见 Dependencies 行(2026-09-09 评审 5→8)** | +| B4 | soft(**provisional**) | 安全格 buff 行值与 weaken 档位的**锚验收权**(F1 条款 2 + A4 三条定价硬约束引用"最低档安全格 buff 当量"——参照对象即 attack_up 行的当量);清图奖励若含 buff 经 apply_effect(source=clear_reward,B4 期定) | +| B2 | soft(**provisional**) | ambush 行量纲/上限的**验收锚**(F1 条款 3:B2 惩罚上限表 vs B3 行值,对账归 B1 闭合);未来敌人词条引用状态(免疫/抗性)为预留注记,不预写;**单敌假设对面义务(2026-09-09)**:B2 定敌人数模型时回验 Rule 1 单敌假设——定多敌即触发 target_ref 接口漂移(B1 归集语义/D1 呈现裁决随批) | +| A3 | 契约相邻(零运行时接口) | A3 不调用 B3;F1 条款 1 四源当量对照以 B3 实例为对照实物;标中旗兑付资格(雷格实例键)判定在 A3/A2 域,B3 只接收 B1 转化的授予——**资格判定不过 B3**(B3 无从重复兑付同雷:每次 flag_hit 授予由 B1 保证资格合法) | +| A2 | 间接(零直接接口) | 开格/标中/开雷事实经事件 → B1 收集 → apply_effect;A2 不感知状态容器 | +| D1 | soft | HUD 状态栏数据源:query_active(hero) 的 display_name/层数/剩余结算次数常显(可见性 = Player Fantasy 反向护栏);层数/到期变化反馈归 D1;**义务清单 D1×4 已登记 systems-index(2026-09-09:HUD 常显+EC12 空态规格 / icon 映射键契约 / 极性呈现映射 / 全清状态栏去留)** | +| B5/D1 结算画面 | soft | 结算明细行的数据源(display_name/magnitude/来源字段);呈现归 B1/D1/B5;**溢出行与 origin_cell 玩家可读形态的呈现义务已登记(B5×2,2026-09-09)** | +| D5 | soft | 教学口径:时限按结算次数计("还剩 2 次结算")、**战斗结束全清**(防"我的 buff 哪去了"误读——A3 RAP 清零教学同款)、满层溢出浪费提示 | + +## Formulas + +> systems-designer 代理起草(lean 门禁,2026-09-08):只含机制公式,平衡归属见显式排除声明。 + +### F1 同 id 叠层合并(same_id_merge,授予时执行) + +``` +merge(effect_id) —— 前置拒绝(域闭 / 未知 effect_id)沿用 States 节 API 口径,不入分支。 +行 = StatusEffectTable[effect_id],按 行.stack_policy 三分支: + + independent → 恒新建实例 {L=1, R=D},append 入列(同 id 并存多实例) + + stack → 无存活实例 :新建 {L=1, R=D} + 有存活实例 ∧ L < M :L ← L+1(R 不动,见注) + 有存活实例 ∧ L = M :返回 false(满层拒绝;容器零写入,溢出行归 B1 结算明细) + + refresh → 无存活实例 :新建 {L=1, R=D} + 有存活实例 :R ← D(L 不变;含 R 已 = D 的空刷新,恒返回 true—— + refresh 无满层/溢出语义,不消费 max_stack) +``` + +**注(spec 决议)**:stack 授予**不触碰 R**。依据:duration_settlements = **授予时**初始剩余结算次数——只有初始授予定钟;且若 stack 也重置 R,则 stack ≡ refresh + 加层,三分支失去区分性(refresh 的区分性特征正是"重置次数")。refresh 的重置目标 D 取**实例持有的授予时快照**(与 EC9 快照语义一致——实例不受表重载影响;qa-lead 缺口 #1 补全)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| effect_id | e | String | 表主键 | StatusEffectTable 行键;未知 = 拒绝 | +| stack_policy | P | enum {stack, refresh, independent} | 表列 | 合并分支选择器(Rule 4) | +| max_stack | M | int [1, 16] | 表列 | 仅 stack 分支消费;refresh/independent 不读(未来 refresh 行配 M>1 时行为 = 忽略) | +| duration_settlements | D | int [1, 99] | 表列 | 新建实例初始 R;refresh 的重置目标即此值 | +| layers | L | int | stack 行 [1, M];其余恒 1 | 实例层数(越界 = debug 断言,States 节不变式) | +| remaining | R | int | 存活态 [1, D] | 剩余结算次数;归 0 移除(F3) | + +**Output Range:** 正常域——stack/refresh 行同 id 存活实例数 ≤ 1(不变式);independent 行实例数 = 受理授予数(有界确定);返回值 bool。极端行为——满层授予恒 false 且容器逐字不变(拒绝原子性:无半写、无溢出落层);**同轮批量授予的先到先得次序由调用方保证**(B1 编排须按确定次序调用 apply_effect——B3 只保证插入序枚举稳定,登记 B1 验收项)。 + +**Example:** attack_up(stack, D=2, M=2):轮 3 两个安全格事实 → apply_effect×2 → L=2、R=2;兑付 +100%(F2)、decay 后 R=1。轮 4 第三个授予 → L=2=M → **false**(明细"溢出未生效"行);本实例仍兑付 +100%、R=0 移除。weaken(stack, D=1, M=16):轮 3 三旗命中三雷(雷格实例键,三次授予均合法)→ L=3、R=1;兑付 −75%、decay 移除。轮 4 一旗命中新雷 → 旧实例已销毁,**新建** {L=1, R=1}——dur=1 行的层只在单轮授予批次内累积,跨轮必为新实例。 + +### F2 有效值合成(effective_value_composition,兑付时只读) + +``` +effective_value(实例) = L × m (unit 随行声明,量纲标签原样随值输出、不进乘法) + stack 行:L ∈ [1, M];refresh / independent 行:L ≡ 1(每实例恒一倍) + 加算语义:percent 行的 L×m 是百分点线性叠加(+50 × 2 层 = +100 个百分点); + 乘算读法(1.5^L)在 B3 语义域外。 + 表注:四行 provisional 依据列的"原型 1.5× / 0.5×"是行值出处(provenance),非合成语义。 +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| layers | L | int | 同 F1 | 叠层数 | +| magnitude | m | int | percent ∈ [-100,100] ∧ ≠0;flat ≥ 1(单行字段域,D2 校验) | 行值;**合成值不继承此域**(见 Output Range) | +| unit | u | enum {percent, flat} | 表列 | 量纲标签;percent = 百分比修正、flat = 固定值;**消费语义(乘除侧、地板、单位定义)不在 B3**(见排除声明) | + +**Output Range:** 正常域——percent 行 |effective| ∈ [1, 100×M],**可越出 [-100, 100],B3 不 clamp**(合成值地板/clamp 归 B1 SettlementTable,登记 B1 验收项);flat 行 effective ≥ 1。极端行为——weaken 合成下界两档并列:**AP 界 −200%**(默认经济下 flag_hit 单轮 ≤ ap_per_round=8 次授予、dur=1 层只在单轮累积 → L=8)与**表界 −400%**(L=16,仅非 AP 源如 clear_reward 授予、或 ap_per_round 调参后可达)——两档均越域,均不在本系统内收口。跨 effect_id 聚合、percent 与 flat 混算 = B1 SettlementTable 域(Rule 7)。 + +**Example:** attack_up L=2 → +100 percent(兑换成 2.0× 与否的读法归 B1);defense_up L=2 → −100 percent(压地板线,“归零”读法须 B1 定义);weaken L=8 → −200 percent;ambush L=3 → **+9 flat**(3×m_amb——B3 不解释单位;2026-09-09 评审修正原"+3"算术笔误)。 + +### F3 结算衰减(settlement_decay,轮末执行) + +``` +decay_round() -> k: + k ← 0 + for 实例 ∈ 容器(插入序枚举,零随机): + R ← R − 1 + if R = 0:销毁实例;k ← k + 1 + return k +``` + +时序契约:**先兑付后衰减**——B1 经 query_active(side) 完成本轮兑付后才调用;duration=1 实例的落点由此完整确定 = 授予当轮编排期入容器 → 同轮兑付恰一次 → 轮末 R=0 移除("一次性"语义的全部机制内容)。停手轮/弃图轮同为结算、照常衰减(Rule 2,无免费延时);最后一轮(敌人全灭)是否 decay 归 B1 判定次序域(Rule 6,A3 EC8 同款——battle_end 全清兜底,两种次序产生同终态)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 容器 | — | 实例多重集 | 存活实例全体 | 枚举序 = 插入序(Rule 1 确定性) | +| remaining | R | int | 存活态 [1, D];decay 后 ∈ [0, D−1] | 单调递减;refresh 的 R←D 发生在授予时(F1),不在本式内——两序分离 | +| 返回值 | k | int | [0, \|容器\|] | 本轮移除数 | + +**Output Range:** 正常域——每实例每轮恰 −1,无参数、无豁免实例;k ≤ 调用前容器规模。极端行为——空容器 → k=0;全 dur=1 轮 → k=|容器|(全清);R 恒不因 decay 变负(归 0 即销毁,不留 0 值实例——与不变式 remaining ∈ [0, D] 相容)。 + +**Example:** 轮 3:编排期授予 attack_up×2(L=2、R=2)→ 兑付 +100% → decay_round()=0、R=1。轮 4:query_active(hero) 含同实例 → 兑付 +100% → decay_round()=1(移除)。轮 5:query_active(hero) 已不含 attack_up。 + +### 显式排除声明(R4 式) + +本 GDD **无平衡公式**——F1–F3 只承载机制数学(合并/合成/衰减),不含任何"值该多大"的判断;四行 MVP 值全部 provisional(Rule 10)。平衡与语义归属逐一列明: + +| 对象 | 归属 | 依据 | +|---|---|---| +| 效果档位当量对照表(四源统一当量、单一线性换算) | **B1**(出表方兼闭合对账单一签字人) | A3 F1 条款 1 | +| weaken 档位锚(debuff 当量严格 > 最低档安全格 buff 当量;基准 = 连锁放大边际挖掘收益,provisional 1:1) | **B4 表设计 + B1 对照验收** | A3 F1 条款 2 | +| 挖掘风险不等式(P(安全)×buff 当量下界 > P(雷)×敌方增益当量上界;B2 惩罚上限表值) | **B2 供给上限表值,B1 单一闭合对账** | A3 F1 条款 3 | +| 奖励定价(清图奖励及 B4 三硬约束:雾内档位下界 / 锚随雾成本上浮 / 净当量优势带上界) | **B4** | A4 pricing_constraints;A3 F1 条款 4/5 | +| percent 的消费侧换算与地板(乘除侧读法、合成值越域 clamp);flat 的量纲("+3"的 3 是什么单位) | percent → **B1 SettlementTable**(Rule 7);flat 量纲 → **B2**(ambush 行量纲定稿验收锚) | Rule 7 + F2 Output Range;Schema 说明列 + 表行 provisional 依据 | +| MVP 四行初值定稿权 | **B3**(Rule 10);B4/B2/B1 持锚验收权,修订走接口漂移回写 registry | Rule 10 | + +B3 只对"L×m 线性合成 + R 计数"负责:合成值多大算强、多弱算废、floor 在哪、+3 打谁——均不在本 GDD 语义域。 + +**🔴 条款 2 锚预警(systems-designer 预检登记,随定稿同步 B1/B4)**:weaken 行 vs 条款 2 锚在两个独立线性代理下同向预检不过(百分点·结算暴露:attack_up 100 vs weaken 25;原型战斗量级:≈4 vs ≈1 当量)——“单一线性换算”(条款 1)堵死分源改率翻案,“总吞吐无单档上限可依恃”(条款 2)堵死 max_stack 豁免。B1 对照验收须优先裁决;坐实违反则修订方向 = 上调 weaken magnitude 或 B4 重定最低档参照,**修订值由锚 owner(B4/B1)定**,走接口漂移回写 registry,本 GDD 不预设。**修复空间不对称(2026-09-09 评审增补,裁决前置约束)**:按“每结算瞬时当量”归一——m_w ≥ 51 即过锚(percent 域内可行,但 F2 越域界随之上移:AP 界 −408% / 表界 −816%,7 层即触 B1 地板——上调的连带成本须入裁决);按“每 AP 总暴露”归一——需 m_w > 100,被 percent ∈ [-100,100] schema 域**禁止,该分支不可行**。故 OQ1 增前置步骤:**B1 先钉死归一口径(瞬时 vs 总暴露),口径落定前禁止任何数值修订**;口径落总暴露读法,则现实解 = B4 重定最低档参照(非上调)。另两行注记:defense_up 顶点恰 −100% 压地板线(消费读法归 B1);ambush 当量预检挂起待 B2 量纲(B2 定单位后 B1 才能做条款 3 上限检验)。 + +## Edge Cases + +- **EC1 If 域闭状态授予**(BattleClosed 下 apply_effect):拒绝(false)、容器不变 + debug 诊断——B1 过早/过晚调用 = 编排错误显式暴露,不静默吞。 +- **EC2 If 未知 effect_id**:拒绝(false)+ debug 诊断。表行删除/改名后旧引用的暴露点;悬空引用校验政策承 A4 counter_item_ids 同款——**MVP 不校验**(本表暂无引用方),B4 RewardTable 首个 effect_id 引用回填时补校验 + 条件性夹具(D2"首个引用字段 owner 补"条款的 B3 侧兑现点)。 +- **EC3 If 满层授予**:恒 false 且容器逐字不变(拒绝原子性——无半写、无溢出落层,F1);溢出行归 B1 结算明细(Rule 5)。同轮多授予谁被拒取决于 B1 授予次序——**授予次序确定性是 B1 编排义务**(登记 B1 验收项,F1 Output Range)。 +- **EC4 If 空刷新**(refresh 行 R 已 = D 再授予):返回 true、无任何变化——合法空操作(refresh 无满层/溢出语义,F1 决议)。**MVP 无 refresh 内容行 = 该语义零消费者**;首个 refresh 行上线时须补冒烟 AC(登记,防三分支之二无测试承载)。**原则注记(2026-09-09 评审)**:空刷新静默返 true 与 Rule 5“不静默蒸发”存在原则张力(stack 有溢出行、refresh 无对应物)——MVP 零消费者故休眠;首个 refresh 内容行上线时,其授予可见性语义须由 B4/B1 随 OQ3 一并裁决(已登记 systems-index)。 +- **EC5 If 同轮 dur=1 行多次授予**:层在单轮授予批次内累积(L=N)→ 同轮兑付一次(合成 L×m)→ 轮末全移除;跨轮必为新实例(F1 Example weaken 例)——"N 旗 = N debuff 总吞吐"在 B3 侧的呈现 = 单轮层和,对账归 B1(A3 条款 2)。 +- **EC6 If 同一事实被重复授予**(同 effect_id+origin_cell+round_index 两次 apply):B3 **不去重**——容器是 dumb 账本,无从辨别事实合法性,两次授予 = 两层/两实例;**事实→授予的恰一次转化归 B1 编排义务**(登记 B1 验收项——round_end 双发射源同族问题,A3 先例)。 +- **EC7 If 合成值越域**(|L×m| 越 [-100, 100] 或穿越地板):B3 不 clamp、值 + unit 标签原样输出——收口归 B1 SettlementTable(F2 Output Range;登记 B1 验收项:percent→乘数换算方向与地板规则须成文——weaken AP 界 −200% / 表界 −400%、defense_up 顶点 −100% 三例已在 F2 登记)。 +- **EC8 If 清图换图/弃图刷新**:容器**不清空**——状态跨图延续(Rule 1,战斗域非图域);旗标记随图消亡(A2 域)不影响已授予实例;已消费兑付资格的雷格随图消亡(A3 域),新图新雷自然新资格——B3 无从感知也无须感知。 +- **EC9 If 战斗中热重载 StatusEffectTable**:走 D2 既有门(战斗中 reload 拒绝);**已授予实例不受任何表重载影响**——实例在授予时从表行**拷贝数值快照**(m/D/unit),热重载只影响后续授予(A4 EC8"快照非活引用"同款)。 +- **EC10 If refresh/independent 行配置 max_stack > 1**:行为 = 忽略(F1 分支不读 M)——内容约定非校验项,validate() 不拒绝(防过度约束锁死未来内容)。 +- **EC11 If notify_battle_start / notify_battle_end 重复或错序**:Active 下再 start → false + 诊断;BattleClosed 下再 end → false + 诊断(bool 口径统一,承 A3 输入×相位矩阵同款)。 +- **EC12 If query_active 空 side 或空容器**:返回空 Array——合法状态非错误;D1 HUD 据此渲染"无状态"。 +- **EC13 If decay_round 在域闭时被调用**:返回 0——域闭时容器恒空(battle_end 已清),0 是自然结果、无独立分支、不诊断(幂等安全的兜底读法)。 +- **EC14 If independent 行被高频授予**:实例数上界 = 受理授予数(有界、确定,无满层语义)——MVP 无此内容行;未来 DOT 类高频授予的容量语义归扩展(接口漂移),本 GDD 不预写。 + +## Dependencies + +**Upstream**:D2(hard——StatusEffectTable schema 承载与 ConfigDB 读取;本 GDD 为该表回填 owner,schema + 四行已定稿)。无其他玩法上游(Foundation 层)。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | hard(**provisional**——B1 未设计) | 五 API(notify_battle_start / apply_effect / query_active / decay_round / notify_battle_end);**B1 侧义务清单(登记 B1 GDD 验收项,缺项即 blocking——A4 对 A1/A2 登记先例)**:① 同轮多授予按确定次序调用(EC3)② 事实→授予恰一次转化(EC6)③ percent→乘数换算方向与合成值地板成文(EC7/F2)④ 最后一轮 decay 判定(F3)⑤ F1 条款 1 效果档位对照表以 B3 实例为对照实物 ⑥ **每结算编排恰一次 `decay_round()`**(decay 基数 = 1 是 dur=1 一次性语义与全部衰减语义的先决条件;Active 相位 double-decay 在 B3 侧无守卫——信任 B1 编排,误编排经本义务验收与可回放步骤流暴露;2026-09-09 评审增补)⑦ **结算明细按授予流水逐条标注来源**(F1 合并坍缩归因的兑付面补偿——合并实例只持首授 origin_cell/round_index,per-layer 来源还原的数据源 = B1 授予流水;2026-09-09 评审增补)⑧ **“溢出未生效”行进结算明细协议**(Rule 5 后半的 B1 侧承载;呈现归 B5/D1;2026-09-09 评审增补) | +| B4 | soft(**provisional**) | weaken 档位与安全格 buff 的**锚验收权**(F1 条款 2 + A4 三定价硬约束的参照对象);未来 RewardTable 引用 effect_id → 悬空校验 + 条件性夹具补全义务(EC2);clear_reward 源授予(条款 4 差异化插槽若含 buff) | +| B2 | soft(**provisional**) | ambush 行量纲/上限**验收锚**(F1 条款 3:B2 惩罚上限表 vs B3 行值,对账归 B1 闭合) | +| D1 | soft | query_active(hero) 为 HUD 状态栏数据源(display_name/层数/剩余结算次数常显;EC12 空态渲染);层数/到期变化反馈;**义务清单 D1×4 已登记 systems-index(2026-09-09)**:① HUD 常显 + EC12 空态规格定稿(隐藏栏 vs 空槽由 D1 裁决但须成文)② effect_id→icon 映射键契约(art-bible 形状+色相双通道)③ 有利/有害极性呈现映射(数据源 = B1 SettlementTable 效果语义,非 magnitude 符号——负值增益存在)④ 战斗结束全清的状态栏去留 | +| B5 | soft | 结算明细/战斗动画的实例字段数据源(经 B1 结算流水;MVP 明细行即呈现,动画归垂直切片);**义务 B5×2 已登记 systems-index(2026-09-09)**:① 溢出行呈现(数据源 = B1 义务⑧明细协议)② origin_cell→玩家可读形态(缩略图/格子高亮映射——坐标值本身不直接呈现) | +| D5 | soft | 教学口径:按结算次数计时、战斗结束全清、满层溢出浪费提示;**增补(2026-09-09)**:“同轮拿满优于跨轮叠”教学项(stack 时序补偿,OQ7 证伪项配套——证伪成立前以中性提示形态) | + +**双向一致性核对**(systems-index 依赖分层表):B4 ← A2, B3, D2 ✅ · B1 ← A3, B2, B3, B4, D2 ✅ · B3 ∈ Foundation、无上游 ✅。**注记三条**:① B2 ← D2(索引无 B2←B3 边)与 Interactions B2 行不矛盾——敌方状态的**承载**在 B3,但授予路径经 B1,B2 与 B3 之间是验收锚关系非运行时依赖,故不列边;② **对外修订义务**:Rule 10 对 A3 OQ5“档位归 B4 表设计”的收编(初值定稿权归 B3、锚验收权留 B4/B1)须回写 systems-index 门禁记录(本文定稿时执行);③ **措辞注记(2026-09-09 评审)**:Upstream D2 标"hard"指数据通道事实依赖(ConfigDB 读表——registry config_db referenced_by=[all-systems] 同口径);systems-index 分层表按其惯例不列纯数据读取边(“其余系统的数据读取为事实依赖,不逐条列边”注)——两处相容非矛盾。 + +## Tuning Knobs + +| Knob(表行值,D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| attack_up.magnitude | +50 | 叠满 +100%,敌方 HP 池相对贬值(战斗时长曲线压向 B1 对账) | 安全格奖励空心化——A3 条款 3 左臂(P(安全)×buff)塌方 | +| defense_up.magnitude | −50 | 顶点 −100% 免伤(B1 地板规则压力) | 减伤无感,防御型奖励萎缩 | +| weaken.magnitude | −25 | 单轮 −200% 内敌方输出蒸发(B1 地板) | **🔴 已预检不过条款 2 锚(差约 4×)——上调方向,B1/B4 裁决** | +| ambush.magnitude | +3 flat | 待 B2 量纲定稿后才有意义(B3 不解释单位) | 踩雷无痛感——P3"风险与收益同源"失衡 | +| duration_settlements(四行 2/2/1/1) | 2/2/1/1 | 持续型窗口拉长(规划变简单,衰减感消失) | 持续型名存实亡("跨轮延续"定稿语义失效) | +| max_stack(四行 2/2/16/16) | 2/2/16/16 | 合成越域幅度增大(B1 地板压力↑) | 叠层机制边缘化(重复奖励变溢出浪费) | + +- **非 knob(结构冻结)**:stack_policy 三分支语义、unit 语义、source_class 枚举、时限量纲 = 剩余结算次数(game-concept 定稿)、先兑付后衰减时序、拒绝原子性、零随机——修订走本 GDD 流程非调参。 +- **Knob 交互**:attack_up 与 weaken 是条款 2/3 对账的**两端**——禁止独立调参,联动评估归 B1 闭合对账;A3 的 `ap_per_round` 是 weaken 单轮层和的经济上限(F2 AP 界 −200% 的来源);效果档位当量对照表(B1)落定前,四行 magnitude 的任何调整都须**重跑条款 2/3 锚预检**并同步 registry(接口漂移义务)。defense_up 顶点 −100%×2 次结算 = 连续两轮免伤读法(2026-09-09 评审注记)——免伤窗口是否过强与 attack_up 同入 B1 对账联动评估(B1 地板规则 × 战斗时长曲线),禁止独立调参;若 B1 裁决过强,候选修正 = 行值降档或 schema 加 L×|m|<100 约束(走修订流程非调参)。 + +## Visual/Audio Requirements + +B3 无自有渲染面——视觉义务是**数据供给规格**,呈现归 D1/B5: + +1. **HUD 状态栏数据源**:query_active(hero) 投影的三要素——图标(effect_id→icon 映射归 D1/art-bible)、层数(L)、剩余结算次数(R)必须可读(Player Fantasy 反向护栏:时限/剩余次数不可读 = 违反 P1 的规划素材与 P2 的兑现可见性)。 +2. **结算明细行数据源**:display_name + 有效值(L×m)+ unit 标签 + 来源标注(source_class→文案映射归 B1/D1,P2 归因)——“增伤 +100%(第 3 轮 · 安全格)”式逐行读法;origin_cell 的玩家可读形态(缩略图/格子高亮)呈现义务登记 systems-index(B5×2,2026-09-09)——坐标值本身不直接呈现。 +3. **视觉身份锚适用**(game-concept):数值可读性优先于氛围(锚 1)——状态数值在暗色底上须高对比;增益/削弱图标不得只靠色相区分(锚 2 形状+颜色双通道)。有利/有害极性**不得由 magnitude 符号推导**(负值增益存在)——极性呈现映射的数据源 = B1 SettlementTable 效果语义,义务登记 systems-index(D1×4 之三,2026-09-09)。 +4. **零信号外发**:B3 是纯被动容器,不发任何事件——呈现刷新点 = B1 结算编排完成后的界面刷新(D1/B5 域);到期/层数变化反馈经 decay 前后查询差分或 B1 流水(D1 裁决)。 + +无音频需求(到期/叠加音效归 D4 Alpha 期)。 + +> 注:`art-director` 未经咨询——Lean 模式,生产前请人工复核本节与 art-bible 的一致性。 + +## UI Requirements + +B3 不拥有 UI,但有三个呈现义务(数据均已在 AC 前言投影口径中定义):① HUD 状态栏常显(hero 侧,含空态渲染——EC12);② 结算明细逐行来源标注(经 B1 流水,P2 归因硬要求);③ 满层溢出提示("溢出未生效"行,含来源——Rule 5 诚实语义)。教学口径三条归 D5(按结算次数计时 / 战斗结束全清 / 满层浪费)。 + +## Acceptance Criteria + +> **测试载体与注入口径**:B3 是纯行为容器——无场景、无表现面、无加载期自有校验类别。冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code)、ConfigDB autoload 由脚本显式实例化)。容器经 **class_name 直引实例化**(每用例全新实例、无跨用例状态——A4 AC-5 直引先例同款;AC-19 扫描对该实例化路径豁免)。B1 未实现(第 9 位),冒烟**直接驱动 B3 最小 API 面**(承 A3"直接驱动最小 API 面"先例);B1 就绪后改走真实调用方(结算编排),断言口径不变。 +> **API 面**:`notify_battle_start()` · `apply_effect(effect_id, source) -> bool` · `query_active(side) -> Array` · `decay_round() -> int` · `notify_battle_end() -> bool`。**全部布尔/计数返回 = 受理/拒绝**(A3 全 API 布尔口径同款)——"拒绝"直接断言返回值,附 debug 诊断、非进程致命(承 A3 拒绝口径;进程致命保留给 D2 数据通道);`decay_round` 返回值 = 本轮移除数断言。**`apply_effect` 是唯一授予入口**(Rule 3),冒烟直接构造合法 `source = {source_class, origin_cell, round_index}`(B1 转化产物的冒烟等价——信任方向同 A3 对 `chain_size`/`clears_map` 的处理)。`query_active` 为只读、任意相位合法。 +> **只读观测量**:实例六字段 `effect_id` · `layers` · `remaining_settlements` · `source_class` · `origin_cell` · `round_index`;`query_active(side)` 元素 = 结算面向载荷投影:上述六字段 + `display_name` + **授予时数值快照 (magnitude, unit)**(EC9 快照语义 + Interactions B5/D1 行的数据源)——有效值 = `layers × 快照 magnitude`、unit 标签原样随值输出(F2)。枚举序 = 插入序(Rule 6)。**相位无字段观测量**:BattleClosed/Active 由 API 返回值推断;Expired/Cleared 为销毁态不可观测(瞬态纪律同 A3 RoundEnd)。 +> **时序口径**:B3 无 round_start/settlement API——"轮边界"在冒烟中 = 相邻两次 `decay_round()` 调用之间的授予/查询窗口(B1 编排在 B3 侧的可见面恰为这五个 API)。多步 GIVEN 均给出构造序列。 +> **期望值推导纪律**:`magnitude` / `duration_settlements` / `max_stack` 均为 knob(Tuning Knobs 节)——各 AC 的 THEN 具体数值按**当前 ConfigDB 表值推导、不写死**(正文以 `D_a`/`M_a`/`m_w` 等表读值记号书写);结构字段(effect_id 集合、target_side、unit、stack_policy)非 knob、定值断言合法。行表"来源"列为内容档案(provenance),**非 schema 字段、非运行时约束**——B3 不校验 source_class 与行的对应(EC6 dumb 账本同族)。 +> **夹具策略**:① 默认根(干净检出四行 MVP 内容)承载全部分支行为 AC;② **程序构造**(schema 类 class_name 直引实例)承载 AC-13 域校验——不经 `load()`/`preload()`;③ 行为夹具根经 `--data-root` 注入(仅 debug、不入 `data/tables/`——D2 夹具纪律),承载 MVP 无行的语义(refresh/independent)。**显式声明:夹具测试行非 MVP 内容**(Rule 10 四行全 stack;测试行仅供冒烟)。选夹具根而非程序构造注入表的理由:容器经 ConfigDB 单点读表(Rule 2/9),无表注入 API——`--data-root` 是 debug/test 构建的既定注入通道。**夹具规格登记(登记值即相关 AC 的推导基准)**: +> - 根 `status_effect_policy_rows`:`refresh_probe`(display_name="刷新探针" · hero · percent · magnitude=+10 · duration=3 · stack_policy=refresh · **max_stack=4**——故意违反"refresh 行恒 1"内容约定,作 EC10 载体,validate() 须零诊断)+ `independent_probe`(display_name="独立探针" · enemy · flat · magnitude=+2 · duration=3 · stack_policy=independent · max_stack=1)——全字段域内合法数据。 +> - 根 `status_effect_policy_rows_reloaded`:仅 `fresh_probe` 一行(hero · percent · magnitude=+20 · duration=9 · stack · max_stack=1)——服务 AC-15 重载腿。**根规格 = D2 全表清单拷贝 + 仅置换 StatusEffectTable 为本单行**(2026-09-09 评审增补——缺任一表文件即触发 D2 EC1 文件缺失致命、GIVEN 不可构造;“仅一行”指该表内容非根内容)。 +> - 根 `status_effect_dup_key`:D2 全表清单拷贝 + StatusEffectTable 置换为默认四行 + 追加一行与 attack_up 同 effect_id 的重复键行(其余字段域内合法)——**B3 唯一致命夹具**(2026-09-09 评审增补,A4 `variation_dup_key` 同型):loader 集成缝(loader 是否对本表调用校验)的进程级落点。 +> **致命类收窄豁免与 runner manifest(2026-09-09 评审改定)**:B3 冒烟设**恰一根致命夹具**(`status_effect_dup_key`——重复键类别):runner manifest 退出码行 `dup→≠0`(独立 headless 进程、游戏通道,D2 AC-2b 协议同型)——“游戏内真的会死”在本表上的落点由此腿实证;其余致命类别(枚举域/域校验/跨字段)由 D2 AC-2 同类别 loader 级夹具承载(表无关,豁免声明收窄保留);schema 侧以程序构造断言域类别(AC-13)。每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明);各 AC 声明其表根(默认根/上列三根),用例间无顺序依赖。 +> **覆盖声明(out-of-scope)**——下列各项不在本冒烟域,deferral 以 **Dependencies 节 B1 五项义务清单**为生效条件:① 同轮多授予的先到先得次序(EC3 后半——冒烟单调用方次序即构造序,B1 编排次序 = 义务①);② 事实→授予恰一次转化(EC6 后半——义务②);③ percent→乘数换算方向与合成值地板(EC7 收口半——义务③);④ 最后一轮 decay 判定次序(F3 时序契约的 B1 半——义务④;battle_end 全清兜底使两序同终态,冒烟不构造);⑤ 效果档位对照表以 B3 实例为对照实物(义务⑤,对账归 B1)。另有:结算明细“溢出未生效”行与来源标注呈现(Rule 5 后半——冒烟无明细观测量;**对侧承载已登记:B1 义务⑧ + B5×2 呈现义务,2026-09-09,deferral 生效条件成立**);D1 HUD 状态栏与 EC12 空态渲染(**D1×4 已登记 systems-index,2026-09-09**);效果语义到结算公式映射(Rule 7——B1 SettlementTable 域);**Rule 8 负空间**(无驱散/延时/转移 API——"无对应 API"不可 GIVEN-WHEN-THEN,其验收形态 = 本前言 API 面封闭枚举 + 接口漂移修订门);D2 schema 校验致命端到端(双通道协议承载);悬空引用校验(EC2 政策半句——B4 RewardTable 首个 effect_id 引用回填时补,D2 双通道条件性夹具);EC9 reload 门腿(D2 AC-16 既有覆盖 + B1 是置位/复位唯一调用方——B3 无断言义务);EC8 端到端换图腿(B1 编排合规,AC-10 裁决)。 + +**域与生命周期** + +- **AC-1** GIVEN 全新容器(BattleClosed、空;表根 = 默认根),WHEN 依次 `apply_effect(attack_up, 合法source)`、`query_active(hero)`、`query_active(enemy)`、`decay_round()`,THEN apply 返回 **false**(EC1 域闭拒绝)、双侧 query 均 = **空 Array**(EC12)、decay 返回 **0** 且诊断收集器**零新增**(EC13——空容器域闭的兜底读法,非独立分支不诊断);WHEN `notify_battle_start()`,THEN true 且 query 仍双侧空(战斗开始时为空,Rule 1);WHEN 再次 `notify_battle_start()`,THEN **false**、相位与容器不变(EC11 前半——Active 再 start)。 +- **AC-2** GIVEN Active 且已建账(构造:`apply_effect(attack_up)` ×1 + `apply_effect(weaken)` ×1,query 断言含两实例),WHEN `notify_battle_end()`,THEN true、query 双侧空(Cleared 全清、不跨战斗——Rule 1);WHEN 再次 `notify_battle_end()`,THEN **false**(EC11 后半——Closed 再 end);WHEN `decay_round()`,THEN **0** 且诊断零新增(EC13 域闭腿);WHEN `apply_effect(attack_up, …)`,THEN **false**(EC1 全场战后腿);WHEN `notify_battle_start()`,THEN true 且容器空(唯一出口——下一场战斗重启,States 表)。 + +**授予与合并三分支** + +- **AC-3** GIVEN Active,WHEN `apply_effect("attack_up", {safe_cell, (3,4), round_index=2})`,THEN true 且 `query_active(hero)` 恰 1 实例,其 **effect_id="attack_up"、layers=1、remaining=D_a(attack_up.duration_settlements 表读值)、source_class="safe_cell"、origin_cell=(3,4)、round_index=2** 六字段逐一相等(P2 归因字段授予即携带——Rule 3,结算明细逐行标注的数据源)。 +- **AC-4(stack 分支前两腿 + “R 不动” spec 决议)** GIVEN Active,D_a/M_a 为表读值。腿 1 GIVEN 无存活实例,WHEN `apply_effect(attack_up, …)`,THEN true、新实例 **L=1、R=D_a**。腿 2(前提 **D_a ≥ 2 ∧ M_a ≥ 2**——默认表值 2/2 满足;前提不满足时本腿退化跳过不假失败,**跳过须输出显式 SKIP 标记防 CI 静默**——2026-09-09 评审增补,夹具一致性声明)GIVEN 腿 1 实例经 `decay_round()` 一次(R=D_a−1),WHEN 再 `apply_effect(attack_up, …)`,THEN true 且 **L=2、R 仍=D_a−1**——**stack 授予不触碰 R**(F1 注 spec 决议:只有初始授予定钟;实现若重置 R 即三分支失去区分性,本腿即其暴露点)。 +- **AC-5(refresh 分支 + EC4 + EC10;表根 = `status_effect_policy_rows`,测试行非 MVP 内容)** GIVEN Active,D_f=3、m_f=+10(夹具规格登记值)。WHEN 依次:① `apply_effect(refresh_probe, …)` → true、新实例 L=1、R=D_f、载荷 = 1×m_f(refresh 行 L≡1——F2);② `decay_round()`(返回 0,R=D_f−1)后 `apply_effect(refresh_probe, …)` → **true、R 重置=D_f、L 不变**(F1 refresh 正常腿);③ R 已=D_f 时 `apply_effect(refresh_probe, …)` → **true 且实例六字段 + 载荷逐字不变**(**EC4 空刷新 = 合法空操作**——refresh 无满层/溢出语义);④ 再 `apply_effect` ×2 → 均 true(max_stack=4 从不被消费——**EC10 行为 = 忽略**);⑤ 该根经测试通道加载诊断为零(内容约定非校验项——EC10 非校验半句)。 +- **AC-6(independent 分支 + EC14;同夹具根,测试行非 MVP 内容)** GIVEN Active,D_i=3(夹具规格登记值),WHEN 同轮授予批次内以**互异 origin_cell**((1,1)/(2,2)/(3,3)——插入序可观测的前提,2026-09-09 评审增补)`apply_effect(independent_probe, …)` ×3,THEN 全部 true 且 `query_active(enemy)` 恰 **3 个并存独立实例**(各 L=1、R=D_i、**投影序列 = 授予序**((1,1)→(2,2)→(3,3))——互异 origin_cell 使插入序成为可判定断言,Rule 6 枚举确定性);WHEN `decay_round()`,THEN 返回 0(D_i≥2 无移除)且三实例各自 R=D_i−1(F3 逐实例、无豁免);实例数上界 = 受理授予数(**EC14 有界确定**——无满层语义)。 +- **AC-7(EC6 不去重)** GIVEN Active 且已有 attack_up 存活实例(L=1,构造同 AC-3),WHEN 以**完全相同**的 (effect_id, source)(同 origin_cell、同 round_index——同一事实重复转化的模拟)再次 `apply_effect`,THEN true 且同实例 **L=2**——B3 **不去重**(dumb 账本,无从辨别事实合法性,EC6);**out-of-scope**:事实→授予恰一次转化归 B1 编排(义务②,deferral 生效条件 = Dependencies 清单)。 + +**衰减与时序** + +- **AC-8(F3 主式)** GIVEN Active(构造:`apply_effect(weaken)` ×2 → 单实例 L=2、R=1;`apply_effect(attack_up)` ×1 → R=D_a;**前提 D_a ≥ 2**——第二 WHEN 腿依赖,默认表值 2 满足,D_a=1 时该腿退化跳过 + SKIP 标记,同 AC-4 口径——2026-09-09 评审增补),WHEN `decay_round()`,THEN 返回 **1**(= 构造中 dur=1 实例数,由表读值推导;weaken 实例移除)、attack_up R=D_a−1 仍在、容器插入序不变;WHEN 再 `decay_round()`,THEN 返回 1(attack_up 移除——**R 归 0 即销毁,容器无 0 值实例**,与不变式 remaining ∈ [0, D] 相容);WHEN 对空容器 `decay_round()`,THEN 0(F3 极端域)。R 轨迹与 k 全部由表值推导。 +- **AC-9(EC5 dur=1 单轮叠层 + 一次性全落点)** GIVEN Active(新轮),WHEN 同轮授予批次内 `apply_effect(weaken, …)` ×3(N=3 为构造选择,≤M_w 表读值),THEN 全部 true、单实例 **L=3、R=1(=D_w 表读值)**、载荷 = 3×m_w(同轮兑付一次的合成输入;N 旗 = N debuff 总吞吐的对账归 B1 条款 2——义务⑤邻接,out-of-scope);WHEN `decay_round()`,THEN 返回 1、实例移除;WHEN 下一轮边界再授予 weaken ×1,THEN **新建**实例 L=1——**跨轮必为新实例**(旧实例已销毁,F1 Example weaken 腿)。 +- **AC-10(EC8 跨轮/跨图延续——结构性裁决)** GIVEN Active,attack_up 授予 ×1(R=D_a)。WHEN 连续多轮边界**仅 decay、无新授予**(停手轮/弃图轮的 B3 视角——B3 无图概念、换图/刷新零专用信号),THEN 实例 R 逐轮 −1 **不停摆**(无豁免实例、无参数——**无"不结算就不掉次数"的免费延时**,Rule 2),且每步 query 断言容器非空直至 R 自然归 0——**除 `notify_battle_end` 外无任何 API 入口使容器清空**(先轮授予的实例按 R 存续至自然到期)。**裁决**:EC8 的"换图不清空"对 B3 的可测内容 = 本条两句(跨轮延续 + 唯一清空入口);"换图"事件对 B3 不可观测(零接口)——端到端"真实换图下容器仍存续"归 B1 集成(编排契约:仅 battle_over 调 end——B1 API 合规半句,B1 GDD 域,非新增义务)。 + +**满层与拒绝** + +- **AC-11(EC3 满层拒绝与原子性)** GIVEN Active,M_a 为 attack_up.max_stack 表读值,构造叠满(M_a 次授予——次数由表值推导、不写死),THEN 前 M_a 次均 true、单实例 L=M_a;WHEN 第 M_a+1 次 `apply_effect(attack_up, …)`,THEN **false** 且容器逐字不变——query_active(hero) 恰 1 实例,其六字段 + 载荷 + display_name 与授予前快照**逐项相等**(拒绝原子性:无半写、无溢出落层,F1 Output Range);"溢出未生效"明细行归 B1(out-of-scope,义务①邻接)。 +- **AC-12(EC2 未知 effect_id)** GIVEN Active,WHEN `apply_effect("no_such_effect", 合法source)`,THEN **false**、容器不变;表行改名后的旧 id 同理。**政策声明**:悬空引用 MVP 不校验(本表暂无引用方,EC2 政策半句)——B4 RewardTable 首个 effect_id 引用回填时补校验 + 条件性夹具(out-of-scope,D2 双通道)。 +- **AC-13(加载期域校验全谱——程序构造,class_name 直引,A4 AC-5 同型)** GIVEN StatusEffectTable schema 类直引实例(不经 load()/preload()),WHEN `validate()` 分别作用于:percent=0(跨字段 percent≠0)/ percent=101 / percent=−101、flat=0(flat ≥ 1)、duration ∈ {0, 100}、max_stack ∈ {0, 17}、stack_policy/target_side/unit 各一出域枚举值、两行同 effect_id(重复键),THEN 各含对应域错误类别;WHEN 作用于域内代表值 percent ∈ {100, −100, 1, −1}、flat=1、duration ∈ {1, 99}、max_stack ∈ {1, 16} 及 **stack_policy=refresh 且 max_stack=4 的行**(EC10——内容约定非校验项,防过度约束锁死未来内容),THEN 零诊断。 + +**表值与一致性** + +- **AC-14(F2 合成 + EC7 不 clamp)** GIVEN Active,m 各值为表读值。腿 1(percent·stack):`apply_effect(attack_up)` ×2 → 载荷 = **2×m_a** 且 unit="percent" 标签随值输出(量纲标签不进乘法——F2;**前提 M_a ≥ 2**,默认满足,M_a=1 时退化为单层恒等断言 + SKIP 标记——2026-09-09 评审增补)。腿 2(flat):`apply_effect(ambush)` ×3 → 载荷 = 3×m_amb、unit="flat"(B3 不解释单位——排除声明)。腿 3(越域,EC7):同轮 `apply_effect(weaken)` ×M_w(M_w 表读值、≤16 同轮批授予合法)→ 载荷 = **M_w×m_w 原样输出、无 clamp**(“不 clamp”的可判定形式 = 载荷与表值推导值恒相等,任何地板都会使其不等);**承载声明**:本腿非平凡性依赖默认基线(M_w=16、m_w=−25 → −400,越出 percent 域)——knob 调参使 |M_w×m_w| ≤ 100 时退化为恒等断言、不假失败,**退化时用例输出显式退化标记(SKIP 日志),CI 可见、防静默失真**(2026-09-09 评审增补);地板/clamp 与换算方向归 B1 SettlementTable(out-of-scope,义务③)。腿 4:refresh/independent 行载荷 L≡1 已由 AC-5①/AC-6 承载(引用不重复)。 +- **AC-15(EC9 快照 vs 表重载——机制口径更正,2026-09-09 评审)** GIVEN 表根 = `status_effect_policy_rows`、Active、已授予 `refresh_probe` ×1 并 decay 一次(R=D_f−1、载荷 1×m_f)。WHEN 冒烟脚本经测试通道**换根加载** `status_effect_policy_rows_reloaded`(`load_tables(root, collect_only=true)` 直调——**非 D2 `reload()` 路径**:不过 set_reload_gate、不发 `config_reloaded`、无 EC16 语义,门语义归 D2 AC-16/B1〔OQ5〕;reloaded 根规格 = **D2 全表清单拷贝 + 仅置换 StatusEffectTable**,见前言夹具规格),THEN **已授予实例六字段与载荷逐字不变**(授予时拷贝 m/D/unit 数值快照——EC9“快照非活引用”,A4 EC8 同款语义;实例为 B3 自持,故 B3 冒烟自测,无需 deferral);WHEN `apply_effect(fresh_probe, …)`(新 id、无存活实例——refresh 重置目标取实例快照 D 的口径见 F1 注,本腿不涉存活实例重置),THEN true 且新实例 **R=9、载荷 1×(+20)**(夹具登记值)——**换根只影响后续授予**。 +- **AC-16(MVP 内容四行)** GIVEN ConfigDB 默认根加载完成,WHEN 读 StatusEffectTable,THEN **恰含 4 行**、effect_id 集合 = {attack_up, defense_up, ambush, weaken}(定值);target_side:attack_up/defense_up=hero、ambush/weaken=enemy,unit:percent/percent/flat/percent,stack_policy=stack ×4(均非 knob 结构、定值断言合法——Rule 10"四行全 stack");display_name 四值非空(精确值对账归 AC-18);**magnitude/duration_settlements/max_stack 仅作类型域断言、期望 = 表读值**(knob 纪律——A4 AC-1 同款,防调参打假;行表"来源"列为 provenance 非表字段、不作断言)。 +- **AC-17(确定性——A3 AC-17 同型;2026-09-09 评审增补可观测序腿)** GIVEN **全新容器实例 ×2**(同 ConfigDB 默认根;每遍新实例、无跨实例状态),WHEN 同一定长序列驱动两遍(start → **defense_up×1 → attack_up×1**(同侧双实例、授予序 = 字典序**逆序**——区分“插入序实现”与“字母排序实现”的判别性前提)→ query 投影(hero 恰 2 实例,**投影序列 = [defense_up, attack_up] 定值断言**——插入序的行为级观测点)→ weaken×1 → decay → query 投影 → attack_up×1(并入 L=2)→ decay → end → query),THEN 两遍的 query_active 投影序列(**含插入序**)与 decay_round 返回值序列**逐项相同**,且 hero 侧投影序恒 = 授予序(非字典序——零随机 + 插入序枚举的双重行为级证据——Rule 1;D2 AC-15 的 B3 层前身)。 +- **AC-18(registry 一致——A3 AC-10a 同型,无条件断言;2026-09-09 评审随 registry 逐字段键重构同步改写)** GIVEN ConfigDB 默认根加载完成,WHEN 脚本解析 `design/registry/entities.yaml` 的 `status_effect` 条目(**逐字段 YAML 键**——机器对账前提,action_economy 同构),THEN:① `fields` 八键与 ConfigDB schema 字段名逐一相等、各键的类型/域/枚举描述与 schema 定稿相等(**缺任一键即失败**);② `mvp_content` 四键 id 集合与表行 id 集合相等,**非 knob 结构字段**(target_side/unit/stack_policy)逐一相等;③ `rule4_exemption` / `dangling_ref_policy` 注记存在性断言。knob 活值(magnitude/duration_settlements/max_stack)**不作定值对账**(A4 AC-8 同款——对账口径 = registry 随调参同步〔接口漂移义务〕,本 AC 豁免其值)。**生效前提**:本条目为本文定稿 Phase 5b 回填 + 2026-09-09 评审逐字段键重构的产物,AC 生效前提 = 重构回填完成(不写“若存在”软口径——条件化使一致性检查落空,A3/A4 均无条件)。 +- **AC-19(静态扫描——D2 AC-9/AC-10 模式引用,A3 AC-10b 同型)** B3 文件集(容器/schema/冒烟脚本/夹具规格)纳入 D2 AC-9 模式项目级扫描域(`randomize()`/`RandomNumberGenerator`/ConfigDB 外 data 表加载/`extends Control`/`.tscn` preload——零命中;**schema 与容器的 class_name 直引实例化豁免**——AC-13 前提,A4 AC-7 同款)+ D2 AC-14 人工三要素字面量扫描(引用不另设硬 THEN——字面量判定误报率高,D2 已定性 warning 级 + 人工复核)。零随机的行为级硬证据 = AC-17。 + +**覆盖矩阵(qa-lead 交付,验收评审用)**:Core Rule 1→AC-1/2/10/17/19 · Rule 2→AC-8/9/10 · Rule 3→AC-3+前言 API 封闭 · Rule 4→AC-4/5/6 · Rule 5→AC-11(明细行→B1 义务⑧+B5×2 已登记,2026-09-09)· Rule 6→AC-1/2(decay 次序 OOS)· Rule 7→OOS(载荷只读面 AC-14)· Rule 8→前言 API 封闭+OOS · Rule 9→AC-13/16/19 · Rule 10→AC-16/18 · F1→AC-4/11(stack)/AC-5(refresh)/AC-6(independent)· F2→AC-14+AC-5①/AC-6 · F3→AC-8/9/10 · EC1→AC-1/2 · EC2→AC-12 · EC3→AC-11 · EC4→AC-5③ · EC5→AC-9 · EC6→AC-7 · EC7→AC-14 腿3 · EC8→AC-10 · EC9→AC-15 · EC10→AC-5④+AC-13 · EC11→AC-1/2 · EC12→AC-1 · EC13→AC-1/2 · EC14→AC-6——无洞。 + +## Open Questions + +1. **weaken 条款 2 锚正式裁决**(owner:B4/B1;deadline:B1 GDD 强制检查点):Formulas 红色预警的裁决落点——坐实违反则上调 magnitude 或重定最低档参照(锚 owner 定值),走接口漂移回写 registry;裁决时复查 AC-14 腿 3 非平凡性。**前置步骤(2026-09-09 评审增补):B1 先钉死归一口径(每结算瞬时当量 vs 每 AP 总暴露),口径落定前禁止任何数值修订**——两读法下修复空间不对称(瞬时读法 m_w≥51 可行〔越域界随移〕、总暴露读法被 schema 域禁止,见 Formulas 红警增补);口径落总暴露则现实解 = B4 重定参照。 +2. **ambush 量纲定稿**(owner:B2,设计顺序第 7 位;联动 F1 条款 3):flat 的"+3"的单位(候选"每颗已开雷的伤害加成");B2 定稿后 B1 才能做条款 3 上限检验。 +3. **首个 refresh/independent 内容行的 AC 补全**(owner:B4,第 8 位):EC4 登记义务——奖励池引入非 stack 行时补真实内容行冒烟 AC,夹具探针行退役。 +4. **clear_reward 源授予**(owner:B4,第 8 位):清图奖励若含 buff 的授予路径确认(apply_effect source=clear_reward;A3 F1 条款 4 差异化插槽)。 +5. **EC9 reload gate 的 B1 侧落点**(owner:B1 GDD 门禁评审;本文定稿时随 systems-index 门禁记录登记):防"战斗中真的被置位"零落点(qa-lead 缺口 #4)。 +6. **效果当量对照表的预检重跑**(owner:B1,第 9 位):条款 1 对照表落定时按 Formulas 预警重跑 weaken/ambush 方向性预检(正式对账载体)。 +7. **stack-不重置 R 的 playtest 证伪项**(2026-09-09 评审登记;owner:垂直切片前置 playtest 批次):F1 注决议(用户拍板、结构冻结)的体验面反例检验——“早拿必亏”(分轮拿满总暴露 150pp vs 同轮拿满 200pp)叠加奖励身份不可预读(创意风险 #1)是否伤害“把 buff 纳入挖掘推理”的规划感〔P1〕。补偿载体先行:B4 奖励身份预读(义务已登记 systems-index)+ D5“同轮拿满”教学;**证伪成立则走结构冻结修订流程回炉**(合并/重定义 stack 分支)——非调参。 diff --git a/design/gdd/legacy-2026-09-13/b4-reward-item-economy.md b/design/gdd/legacy-2026-09-13/b4-reward-item-economy.md new file mode 100644 index 00000000..39a55b63 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/b4-reward-item-economy.md @@ -0,0 +1,395 @@ +# B4 奖励与道具经济(Reward & Item Economy) + +> **Status**: In Review → Revised(2026-09-09 full /design-review:五专员〔game/systems/economy/qa/ux-designer〕+ creative-director 终审,NEEDS REVISION——14 条 blocking 同日修订核销,待 delta 复核放行;lean 定稿批的"CD-GDD-ALIGN skipped"已随本次终审补位) +> **Author**: user + agents +> **Last Updated**: 2026-09-12(旧口径摘要同步:L89/L90/L238/L267/L296/L324 对齐 L94 关卡域权威版) +> **Implements Pillar**: P2(主——奖励是"扫雷的因"的兑现物)· P3(安全格价值侧)· P1(奖励身份可预读 = 规划素材) + +## Overview + +B4 是战斗域的**奖励层**:定义"扫雷决策的兑现物是什么、值多少、怎么随机、怎么入库"。数据侧,它持有 D2 `RewardTable` 的回填权——奖励条目(buff 行引用 / 伤害道具 / 治疗包等,类型封闭枚举见 Core Rules)、权重、档位、清图奖励插槽行;随机侧,它消费 `reward_roll` 封闭流(registry 已登记 salt,"每次 board install(格奖励批 + 清图奖励批)"语义归 B4——2026-09-09 定稿批改写 registry;零自建 RNG,可回放前提)。行为侧,它承载三件事:**① 安全格奖励内容**——A2 `cell_revealed{reward_eligible=true}` 事实的兑现物:buff 类经 B3 `apply_effect` 入状态容器(B4 只定"给哪行、几层"),非 buff 类(伤害道具/治疗包)入道具库存;**② 清图奖励**——`round_end{path_tag=cleared}` 触发,A3 条款④ 钉死"清图奖励 > 0"且预留 biome/tag 差异化插槽结构位(图类型战斗域恒定、不逐图重掷);**③ 伤害道具体系**——**主角基础攻击之外的规划面伤害**(2026-09-11 用户裁决:HeroTable.atk 基础攻击恒存在、域 [1,999]、0 加载拒绝——原"唯一伤害来源 / atk=0 机制开关激活侧"口径退役;伤害道具默认经固定顺位作用于敌单位,囤刀/抢杀/爆发时点 = 其规划价值所在)。 + +没有 B4,三件事失去落点:P2"规划的延迟兑现"没有兑现物——扫雷的"因"找不到战斗的"果"(规划退化为空转);胜利路径断裂——B2 已声明当前内容态敌人不可被击杀,胜负判定素材永远悬空;A4 三条定价硬约束(雾内档位下界 / 清图锚上浮 / 净当量带上界)没有承载对象——安全格价值体系无从校准。B4 的边界:它管"奖励**是什么、值多少、怎么 roll、怎么入库**";"何时兑付、结算顺序、明细行协议"归 B1 SettlementTable;棋盘面资格事实归 A2(B4 与 A2 零直接接口——事实经 B1 结算编排转化,B2 同款);buff 的时限/叠层/衰减机制归 B3;棋盘侧功能道具(探测、安全旗)归 A5(垂直切片);关卡大奖励、代币、天赋归 C2/C4。机制与内容分离(B3 先例):机制归本 GDD,内容行(道具池成员与权重数值)MVP 收窄,名录扩展归垂直切片/Alpha。 + +玩家的每一轮都在**预读**它:已开格与库存的奖励身份**恒定可读**("这格是增伤"——Rule 8),叠加两条全板先验——**每个安全格必有货**(F1 结构保证)与**雾格档位更高**(F2)——共同构成 MVP 的规划素材:玩家据此做"往雾区推进、囤刀待机、叠层满载后收手"的路线与节奏决策。**如实口径(2026-09-09 评审改写)**:面向特定道具的寻路("绕开两颗雷去拿那把刀")须以未开格身份可读为前提,MVP 未兑现——结构前提已备(身份 install 期已定、预读查询面就绪、A5 探测零成本桥接),呈现深度 = OQ2(垂直切片);创意风险 #1 维持 OPEN(结构前提落地 ≠ 风险闭合)。可预读 → 入库可规划(何时用)→ 规划兑现可归因(P2 闭环)——这是 P1"推理的掌控感"在奖励侧的 MVP 形态,寻路形态随 OQ2 兑现(B3 presentation_obligations 的 B4 条目同步收窄)。 + +*(实现口径注:奖励容器/道具库存的类结构与事件载体不在本 GDD——实现期 ADR 域。)* + +## Player Fantasy + +**直接承载**(Section A 定框答案:玩家每一轮都在开格、都在捡东西、都在结算里看到自己捡的货)。B4 的情绪目标一句话:**"我在地牢里淘东西,不是在领系统发放的补偿"**——开格的瞬间,奖励身份就在场、可读、是我的探索所得;它先是一份捡到的战利品,其次才是一行结算数据。 + +体验锚点(按支柱): + +- **P4 地牢住户的遗物**:奖励给"下探"一个收集动机——**雾区/高危区的格子货更好**(F2 档位下界;原"越深的格子越可能有好货"的深度轴 MVP 零载体,2026-09-09 评审如实收窄为"越险货越好"的 P3 面;深度/图类型差异化 = clear_\/池变体结构位,垂直切片登记),"边探边捡"是地牢氛围的经济侧骨架(Microsoft Minesweeper Adventure 的开格出宝同源);开格瞬间的"这格有货"是本系统的高频情绪节拍。 +- **P2 拾取的糖、结算的实**:捡到的东西不当场生效——只入库,轮次结束在结算明细里逐行兑付("治疗包 ×1 入库(第 2 轮 · 安全格)",生效在其后的窗口选用/道具段——原示例"治疗包 +3"易误读为即时回血,2026-09-09 评审更正);拾取瞬间的糖与延迟兑现的实各管一半,后者是 P2 归因的奖励侧落点。 +- **P1 身份可读 = 拾取感的地基**:开格后这格是什么**恒定可读**(本格奖励身份不重掷、不明雾化)——玩家看得见才留得住:"囤刀待机、看准重击拍再用"的库存规划是拾取感的长线 MVP 形态(奖励身份预读义务——已开格 + 库存恒读;面向未开格的寻路 = OQ2/A5 兑现后的二期形态,Overview 如实分档)。 +- **P3 越险货越好**:雾区/高危区的奖励档位下界更高(A4 三条定价硬约束的体验面)——"危险的地方货更好"是风险收益同源在拾取侧的直觉。 + +参考感受:Microsoft Minesweeper Adventure 的开格拾取(身份即在场的战利品,不是无名金币)+ 杀戮尖塔奖励节点(有名字、有用途、进构筑的战利品)。**反向对照**:无身份的纯数值掉落("你获得了 +5"式系统补偿)与开包式随机惊喜(拿到了也不知道是啥)——正是本作要避开的两种感受。 + +**反向护栏**:奖励的随机只发生在**桌下**(roll 决定这格**是什么**),身份永远在**桌上**(开格后恒定可读)——任何"开格后仍不可读""结算时才揭示身份"的呈现设计默认砍掉(违反 P2 归因 + 掐死拾取感本身);拾取的糖不得改成即时生效(绕过 B1 结算即违反 P2 统一兑付,game-concept 定稿)。 + +> 注:`creative-director` 已于 2026-09-09 full /design-review 终审本节情绪定位(五专员对抗 + CD 合成);按 CD 裁决,本节预读/寻路声称已如实降级至 MVP 呈现面,创意风险 #1 保持 OPEN。 + +## Detailed Design + +> **决策记录(2026-09-09 用户拍板)**:奖励身份 = **install 期预分配**(板级结构事实)· 伤害道具/治疗包 = **结算中玩家选用**(道具使用窗口——概念文档"主动道具后续版本加入"的遗留问题在 MVP 激活,接口漂移登记 Dependencies)· 结算段序 = **增益/减益段 > 道具段 > 战斗段**(核心序)· 库存 = **全部战斗域**(battle_over 全清——与 Peglin 参照"道具跨循环累积"不同,概念层口径交 consistency-check 对齐)· 连锁溢出 = **直接算零、无折算补偿**。 +> **决策记录增补(2026-09-09 full 评审用户拍板)**:**窗口锚点由"结算解析前"移至"授予段之后、增益/减益段之前"**——当轮拾取的道具当轮即可用(消除"当轮拾取下轮才可用"的未陈述规则与末图清图奖励必然作废两个伪影;核心段序 buff/debuff > 道具 > 战斗不变,Rule 5)。 +> **决策记录增补(2026-09-11 用户拍板:两级奖励 + 关内延续 + 治疗稀缺化 + 窗口单件)**:① 奖励按**交付轴分两类**——即时类(buff/instant_aoe/instant_heal,授予段当场兑付、不进库存、零决策,默认根 75%)/ 可选类(damage_item/heal_pack,入库库存、窗口选用、效果更强,默认根 25%);② **库存生命周期 = 关卡域**(跨战斗延续、关卡末清空/折算——原"战斗域 battle_over 全清"拍板作废,OQ4 主体闭合);③ **治疗两级**——instant_heal(小额即时)+ heal_pack(稀缺强治疗,权重 25%→6%、power 4→12),治疗整体供给下调;④ **道具使用窗口至多选用一件**(原"任意多件"作废——先验证单件制,体验反馈后再议);⑤ buff 时长改当次结算(attack_up/defense_up D=1——即时类"当次"语义)。 + +### Core Rules + +1. **奖励类型封闭枚举(2026-09-11 用户裁决:两类奖励扩容)**:`{buff, damage_item, heal_pack, instant_aoe, instant_heal}`——MVP 恰五值,按**交付轴分两类**:**即时类** = `{buff, instant_aoe, instant_heal}`(授予段当场兑付、不进库存、零决策——多数,默认根 75% 权重)· **可选类** = `{damage_item, heal_pack}`(入库存、窗口选用——少数更强,默认根 25% 权重)。预留值(`resource` / `aux_item`〔A5 桥接〕等)校验拒绝(fail-fast,B2 pattern 预留先例);启用走本 GDD 修订。每条奖励 = RewardTable 一行;buff 类携带 B3 `effect_id` 精确引用——首个引用行回填时激活 B3 悬空校验 + 条件性夹具(dangling_ref_policy 兑现)。即时类 power ≥ 1 同道具类(加载校验)。 +2. **板级身份预分配(install 期 roll,用户拍板)**:board install 后、挖掘开始前,B1 编排调用 B4 `roll_board(资格格清单)` **恰一次**——B4 消费 `reward_roll` 封闭流,按规范序(行主序)对全部有资格格 roll 奖励身份,并同批 roll **清图奖励身份**(默认槽)→ 写入**板级映射容器**(格子→reward_id;清图→reward_id)。同图跨轮身份恒定(棋盘状态跨轮延续同款);换图/retire 即映射消亡。**零随机出此一线**:授予期与使用期零 roll,道具威力 = 恒定表值。*语义注记:registry `rng_service.scope_event_binding` 的 reward_roll 批次语义由"每次奖励结算"改写为"每次 board install(格奖励批 + 清图奖励批)"——接口漂移回写义务**已兑现**(2026-09-09 定稿批,registry/D2 同步;consistency-check 2026-09-09 修正条目名——原误引 board_gen)。* +3. **分流与入库(编排期转化,B2 同款;2026-09-11 增即时类当场兑付分支)**:B1 结算编排期对每格受理事实调 B4 `grant(cell)` → 返回预分配身份;**buff 类**由 B1 调 B3 `apply_effect(effect_id, source={safe_cell|clear_reward, origin_cell, round_index})`;**即时类**(instant_aoe / instant_heal)由 B1 授予段**当场兑付**——不调 stock、不进库存、不出窗口(`instant_aoe` = 对全体存活敌各 power 点,恒定表值;`instant_heal` = 主角 +power、clamp,满血 → "奖励未生效"行——零兑付诚实);**道具类**(damage_item / heal_pack)由 B1 调 B4 `stock(item)` 入关卡域库存。B4 与 B3/A2 零直接接口。**受理恰一次归 B1×②**(2026-09-09 评审补——对齐 B2 义务②/B3 EC6 先例);同格重复 grant = 幂等受理(同一快照 + debug 诊断,E18)。**buff 类满层拒绝(apply_effect 返回 false)→ 零兑付**(用户拍板:直接算零、无折算补偿)+"奖励未生效"明细行(B1 义务②〔本 GDD 清单〕/ B3 义务⑧ 同款协议——P2 诚实,不静默蒸发)。 +4. **道具库存(关卡域——2026-09-11 用户裁决"关内延续",原战斗域拍板作废)**:库存容器生命周期 = **关卡域**——跨轮次、跨扫雷图、**跨战斗延续**;**关卡结束**(victory/defeat 皆然——关卡结构归 C1/C2,落位前由上层显式编排调用)由 B1 编排调 `clear_inventory`(义务⑤ 改写——battle_over 只退役板级映射,不清库存)。条目 = 逐件实例(`item_id` + 来源 `{cell, round}` 保 P2 归因);数量无上限(旗无上限同款,稀缺性由 roll 权重承载)。**设计意图**:可选类道具是关内资产——"囤刀待机"的规划视野从一场战斗扩展到整个关卡(P2 长线形态的兑现);战斗末"不花就没了"的强制消费消除。 +5. **道具使用窗口与结算段序(窗口锚点 2026-09-09 评审修订;单件制 2026-09-11 用户拍板)**:轮次结束触发后,结算按**段序骨架**解析:**授予段(编排期转化,含 grant_clear 兑付与即时类当场兑付)→ 道具使用窗口 → 增益/减益段 → 道具段 → 战斗段 → 衰减段 → 判定段**(核心序拍板不变:buff/debuff > 道具 > 战斗)。**窗口**:D1 呈现,玩家**至多选用一件**库存道具(或跳过——2026-09-11 用户拍板"先不需要多选",避免结算复杂度;体验反馈后再议恢复多件,属本规则修订非调参);选择序语义随之退化为单件。**道具段**:至多一件生效——伤害道具按**固定顺位**作用于敌单位(B2 Rule 7 默认目标)、治疗包回血主角;一次性消耗(Used 不可逆)。**段序的战术面**:道具段先于战斗段 → **当轮拾取的刀**(伤害道具的项目行话,全文同义)即可削血触发 B2"死亡即不动手"(抢杀窗口——"重击拍前用刀"是读怪的执行手段)。 +6. **清图奖励(A3 条款④ 兑现)**:cleared 轮结算编排期,B1 调 B4 `grant_clear()` → 预分配身份兑付(分流同 Rule 3)——**恒非空**(结构保证:清图槽 roll 表禁止空结果行);biome/tag 差异化 = 插槽结构位(MVP 单默认槽;`clear_` 行命名空间预留、校验拒绝——A1 elite_/boss_ 同款);图类型战斗域恒定、不逐图重掷(install 批次承载)。 +7. **治疗两级语义(2026-09-11 用户裁决:治疗两类化 + 稀缺化)**:治疗拆两级——**instant_heal(即时类)**:授予段当场回复 `power` 点(clamp,满血 → "奖励未生效"行零兑付),小额常见、零决策零库存;**heal_pack(可选类)**:窗口选用、库存消耗,**稀缺且更强**(默认根权重 6%、power 12——原 25%/4 作废;治疗整体供给与决策价值双降,"治疗是稀缺奖励")。heal_pack 满 HP 使用 → **拒绝**(不烧包;守卫归 B1 编排读 B2 hero hp 判定——B4 零跨系统读;**可用性预判 usable_now 由 B1 编排组装进窗口数据**〔义务④〕供 D1 禁选/提示)。 +8. **奖励身份预读(P1/P2 承重)**:已开格 + 库存的奖励身份**恒定可读**(预分配的结构保证——身份自 install 即定,非授予期随机;**MVP 规划面的如实口径见 Overview——未开格不呈现,寻路待 OQ2**);未开格身份 MVP 不呈现(结构上已定且可查——A5 探测道具零成本桥接,OQ 登记);**映射 Ready 后只读**(负空间:无重掷 API——任何重掷 = 接口漂移)。 +9. **RewardTable schema(D2 新表,本 GDD 回填 owner,走 D2 接口漂移规则回写)**: + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| reward_id | String(表内主键) | 实体键精确匹配(D2 Rule 4) | +| category | enum {buff, damage_item, heal_pack, instant_aoe, instant_heal} | MVP 校验域(2026-09-11 两类奖励五值);预留值拒绝(Rule 1) | +| effect_id | String(可空) | buff 类必填——B3 StatusEffectTable 精确引用;非 buff 类恒空 | +| display_name | String(非空) | 玩家可读名(D1 义务①/窗口呈现数据源;本地化归 D6——B2 EnemyTable/B3 StatusEffectTable 同库惯例,2026-09-09 评审增补) | +| power | int [0, 999] | 伤害道具伤害值 / 治疗包回复值 / 即时类(aoe 全体各点·heal 回复值)(knob);buff 类不消费——非校验项(B2 heavy_multiplier 先例) | +| weight | int [0, 999] | 所在池内 roll 权重(knob);0 = 该池不出此行(合法极端) | +| pool_tag | enum {safe_cell, clear_default}(+ clear_\ 预留) | roll 池分组——格奖励池 / 清图默认槽;雾区档位过滤谓词见 Formulas | +| tier | int [1, 8] | 强度档位(A4 雾内档位下界约束的载体;knob) | +| use_timing | enum {pre_settlement} | 使用时机(MVP 唯一值;game-concept 预留字段的兑现——扩枚举走修订) | + +**表身份声明**:RewardTable 为**定义表**(非进度成长表),Rule 4 豁免、键 = reward_id(schema 注释承载——B2/B3 同款声明纪律,2026-09-09 评审补)。**B4 配置值载体(2026-09-09 评审增补)**:T_fog_min / K_dmg / N_draft / P_min 承载于**单行全局 RewardConfigTable**(D2 新表,本 GDD 回填 owner,HeroTable 单行全局接口漂移先例;字段域见 Tuning Knobs)——原"B4 配置值"无表落点、R3 override 无 key 空间的缺口由此关闭。 + +10. **编排被动性**:B4 零主动事件订阅(A2 事件面事实经 B1 转化)、零 UI 依赖(Core 层 headless 债)、零随机出 `roll_board` 一线(rng_service reward_roll 流封闭);B4 暴露映射/库存查询与授予/扣减接口,调用权集中归 B1 结算编排(B3"授予集中在编排"同款)。 + +### States and Transitions + +| 容器/条目 | 状态 | 进入 | 出口 | +|---|---|---|---| +| 板级奖励映射 | Unrolled → Ready → Retired | install 后 roll_board 恰一次 → Ready;Ready 期间**只读** | retire_board/换图 → Retired(消亡,不跨图) | +| 库存条目 | InStock → Used → Cleared | stock 授予 → InStock;窗口选用消耗 → Used(不可逆) | **关卡结束** → Cleared(clear_inventory——库存跨战斗延续后于关卡末清空,2026-09-11;battle_over 只退役映射不清库存) | + +实例不变式(debug 断言口径):Ready 后映射零写入;Used 不可逆;库存条目仅存在于关卡域(跨战斗延续——2026-09-11)。违规 = debug 断言中止(进程致命保留给 D2 数据通道,B2/B3 同款)。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| D2 | 双向(hard) | **RewardTable 新表 schema + 内容行回填**(本 GDD owner,接口漂移回写 D2 Core Rules 表清单 + registry)+ **RewardConfigTable 单行全局新增**(2026-09-09 评审——T_fog_min/K_dmg/N_draft/P_min 载体);读取经 ConfigDB 单点 | +| A2 | 间接(零直接接口) | 资格事实(reward_eligible/channel)经 B1 转化;A2 只报资格、内容与折算不经 A2(A2 Interactions B4 行对侧确认) | +| B1 | 下游 hard(**provisional**——B1 未设计) | 结算编排期调用:roll_board(install 后恰一次)/ grant / grant_clear / stock / use / query_inventory / clear_inventory;**B1 义务清单 6 项(缺项即 blocking,见 Dependencies)**——窗口锚点 = 授予段后、窗口选择序 = 回放输入、每格受理恰一次(2026-09-09 评审同步) | +| B3 | 相邻(零运行时依赖) | buff 类经 B1 调 apply_effect(source_class=safe_cell/clear_reward 已在 B3 枚举内);**锚验收权归 B4/B1**(weaken 档位 vs 最低档安全格 buff 当量——A3 条款 2);满层拒绝协议对侧;首个 effect_id 引用回填激活悬空校验 | +| B2 | 相邻(零运行时依赖) | 伤害道具默认目标 = 固定顺位(Rule 7 消费);atk 激活裁决参与方;**道具段先于战斗段 → 抢杀窗口的道具形态**(段序战术面);伏击清单扩展联动对侧 | +| C3 | 下游 soft(provisional) | 权重/档位/威力曲线数值调参所有权(默认根行集已登记、provisional);T_fog_min/K_dmg/N_draft/P_min 参数(RewardConfigTable);**调参期批量仿真义务**(刀供给分布 × A1 板分布的 F5 可行率——2026-09-09 评审登记,缺口预检);雾区档位过滤谓词的参数归 C3 | +| C1/C2 | 下游 soft(provisional) | biome/tag 图类型供给(插槽差异化输入);关卡大奖励归 C2(B4 无关);**关卡结构 = 库存关卡域的容器**(2026-09-11 关内延续——"关卡结束"事件归 C1/C2 定义,落位前 B1 暴露 level_clear 入口由上层显式调用);**关卡末剩余道具折算结构位归 C2**(OQ4 残余——转金币/继承候选) | +| D1 | soft | **呈现义务 5 项(缺项即 blocking;2026-09-09 评审 3→5 扩容)**:① 已开格+库存身份恒定可读(库存常读条 = 挖掘期规划面)② 道具使用窗口(授予段后弹出;选用/跳过/序/usable_now 呈现)③ 结算明细**四类行**呈现 ④ H2 清图奖励行强调呈现 ⑤ 关卡末(level_clear)库存清空可见反馈〔2026-09-11 关卡域改写对齐——权威版见下方 D1 侧义务清单⑤〕 | +| B5/D5 | soft | 结算明细道具行(数据源 = B1 流水,**含击杀结果字段**——H3 击杀标记数据源);道具使用窗口教学(拾取→囤刀→窗口选用的闭环)+ "关卡末库存清空"教学(RAP 清零同款先例;2026-09-11 关卡域改写对齐——原"战斗结束库存清空"作废) | + +**B1 侧义务清单(缺项即 blocking——B2/B3 对 B1 登记先例;2026-09-09 评审同步窗口/E10 拍板;2026-09-11 两级奖励批改写①②④⑤)**:① **结算段序骨架成文**:授予段(编排期转化,含 grant_clear 兑付与**即时类当场兑付**)→ **道具使用窗口**(授予段后、增益段前)→ 增益/减益段 → 道具段 → 战斗段 → 衰减段 → 判定段(用户拍板核心序 buff/debuff > 道具 > 战斗不变;B1 拥有段内细则,B2 义务①②③的转化与推进落点随段序定位;**窗口选择序列登记为结算回放输入事件**)② 奖励受理与分流编排(grant → **buff 类 apply_effect / 即时类当场兑付〔instant_aoe = 全体存活敌各 power、instant_heal = 主角回复 clamp〕/ 道具类 stock**;**每格受理恰一次、stock 与 grant 返回一一对应**〔2026-09-09 评审补——对齐 B2 义务②/B3 EC6 先例〕;**满层拒绝/即时类零兑付 = "奖励未生效"明细行、无折算**——用户拍板;未生效类行携带可聚合计数/批次字段——合并呈现归 B1/B5 裁决,数据须可聚合)③ **clear_reward 授予路径**(B3 OQ4 关闭条件:清图奖励 roll 身份 install 期已定、cleared 结算编排期兑付——**兑付后同结算窗口即可选用**;清图槽即时类同分支当场兑付)④ **窗口与道具段编排**(窗口时机 = 授予段后、增益段前;**窗口数据组装 = B4 query_inventory 投影 + B1 侧守卫预判 usable_now**〔满血治疗包禁选/提示——呈现归 D1;B4 零跨系统读不变〕;**窗口至多选用一件**〔2026-09-11 用户拍板——单件施加、目标/数值/明细行来源标注;原"任意多件 + 同窗顺序多包逐件动态守卫"作废,满血守卫拒绝不烧包保留〕;**零效果判定读 B2 存活集 + "已消耗未生效"明细行**〔E10 承接〕;**击杀结果进流水**〔H3 击杀标记数据源——B5/D1 呈现消费〕;治疗 clamp)⑤ 生命周期编排(**关卡结束调 clear_inventory 恰一次**——2026-09-11 关卡域拍板改写,原 battle_over 时点作废;**清空前后快照计数供"随关卡结束清空"反馈行**〔D1 义务⑤〕;battle_over 调映射退役〔映射 = 板级,随战斗消亡——下一场战斗新板新批〕)⑥ roll_board 调用时机(install 后、挖掘前**恰一次**——B4 侧相位守卫见 E5/AC-1 分支 3;B1 编排纪律承载调用时点半〔B4 不可观测〕)。 + +**D1 侧义务清单(缺项即 blocking;2026-09-09 评审 3→5 项扩容——H2/H3/清空反馈升义务,"清单外登记会丢失"先例;⑤ 随 2026-09-11 关卡域改写)**:① 已开格 + 库存奖励身份恒定可读(**库存常读条 = 挖掘期规划面**而非仅轮末窗口——Rule 8/Overview;**关内延续后常读条跨战斗可见** = 关卡资产感的数据面)② 道具使用窗口呈现(授予段后弹出;选用/跳过/**单件选用**〔2026-09-11 拍板〕、**usable_now 可用性投影呈现**〔满血治疗包禁选或选中提示〕、窗口时机指示、空库存可不弹窗〔E8〕、窗口触发政策注记〔MVP 有库存恒弹;候选"仅可用品触发/常驻库存条非模态"归垂直切片〕)③ 结算明细**四类行**呈现(道具行〔含入库/生效语境注记〕/ "奖励未生效"行〔**按聚合计数呈现**,数据源含可聚合字段〕/ "已消耗未生效"行 / "使用被拒"行——数据源 = B1 流水,P2 归因)④ H2 清图奖励行强调呈现(轮末仪式峰值)⑤ **关卡末库存清空可见反馈**("剩余道具 ×N 随关卡结束清空"汇总——2026-09-11 关卡域拍板改写,原 battle_over 时点作废;层间过渡"库存 ×N · HP 带入下一层"可见反馈 = 垂直切片候选)。 + +## Formulas + +> systems-designer 代理起草(lean 门禁,2026-09-09);两处口径经用户拍板:**道具伤害恒定表值不吃修正** · **保底 = 草稿重 roll**。除 F1/F3/F5 消费 `reward_roll` 封闭流外全部公式零随机;数值默认全部 provisional 归 C3。术语:**资格格** = 开出时 reward_eligible=true 的格(A2 事件面:chain/dig 通道的安全格)。 + +### F1 身份 roll(weighted_pick,install 期执行) + +``` +资格格集 = S ∖ R₀(安全格减初始展开域——initial/mine 通道恒不 eligible,install 期可精确枚举) +行主序逐格取 reward_roll 一号 r ∈ [0, W):身份 = 累积权重区间 [Σw_{0} ∩(雾格 ? {tier ≥ T_fog_min} : 全体);weight=0 行取号前剔除 +批尾取一号 roll 清图身份(F3;批尾 = 同一批取号中最后消费的一个号——registry terminology 已登记)→ 双映射提交 Ready +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 取号 | r | int | [0, W) | reward_roll 流单号(F1/F3/F5 消费) | +| 权重 | w | int | [0, 999] | RewardTable.weight;0 = 不入池(取号前剔除) | +| 雾档门限 | T_fog_min | int | [1, 8] | F2;RewardTable 配置值 | +| 资格格集 | S ∖ R₀ | 格集合 | 板级 | B1 供清单(义务⑥) | + +**裁定:池空(过滤后零行)= 结构性不可达**——D2 加载期校验致命(每 pool_tag × 雾/非雾两态各 ≥1 行 weight>0)。理由:身份 install 期预分配、授予期零随机零回退,运行时身份缺口无合法语义。单行池退化为恒等选取(合法极端)。 +**Output Range:** 每资格格恰一身份、清图槽恰一身份;Ready 后映射只读。 +**Example:** 三行 w=2/1/1,W=4,r=3 → 第三行(区间 [3,4))。 +**边界注记**:流 scope = 每次 board install 恰一批(registry 绑定改写义务,Rule 2);消费序与恰一次归 B1;Ready 后只读(无重掷 API——F5 草稿重 roll 发生在提交前,不变式零触碰)。 + +### F2 雾区档位过滤(fog_tier_gate) + +``` +雾格池 = {tier ≥ T_fog_min};非雾格池 = 全 tier(谓词恒真、零分支) +``` + +**T_fog_min = RewardConfigTable 表值(默认 2 provisional)**——推导式(=最低档 buff 当量所在档)依赖 B1 对照表(A3 条款①,未设计)、加载期循环依赖不可行;A4 硬约束①(雾内档位下界 ≥ 最低档安全格 buff 当量)登记为**验收锚**非运行时校验——对照表落定/调参时核对"雾池最低档行当量 ≥ 最低档安全格 buff 当量"。**AC-19 序数检查的已知失效面(2026-09-09 评审增补,防后人"顺手"升格致命)**:tier 与当量零耦合(跨类别 tier 序不保证当量序——雾池 tier2 治疗包的当量可能高于 tier3 buff 行),故 AC-19 只作**档位序检查**(原"档位序代理",registry terminology 已登记)非当量保证,升格运行时致命会锁死内容;单行雾池下检查恒平凡真(判别力零)——默认根已布置两行雾池规避(见 Formulas 末节)。 +**Output Range:** T_fog_min=1 → 雾池=全池(过滤退化合法,约束①仅剩锚侧兜底);=8 → 仅最高档(非空由 F1 校验保证)。 +**Example:** T_fog_min=3 时雾格恒不出 tier≤2 行。 +**边界注记**:链开雾格同雾格待遇(A2 EC4);参数归 C3。 + +### F3 清图奖励 roll(clear_reward_roll) + +``` +槽谓词 = clear_(板 biome 匹配 ∧ 池非空)否则 clear_default;MVP:clear_ 行校验拒绝 → 恒退化 clear_default(结构位保留) +恒非空 = 加载期校验:clear_default 池 ≥1 行 weight>0 ∧(buff 行 effect_id 非空 ∨ 道具行 power ≥ 1) +roll = install 批尾一号,选行算子同 F1 +``` + +**Output Range:** 恒非空的可判定形式 = **恒有正当量行**(power=0 道具行被拒——A3 条款④"清图奖励>0"的表侧兑现);空池致命(同 F1 裁定)。 +**Example:** 命中 heal_pack(power=3) → cleared 轮 grant_clear 入库存。 +**边界注记**:roll 在 install 批、兑付在 cleared 结算编排期(零随机出 roll_board 一线);图类型战斗域恒定、不逐图重掷。 + +### F4 道具段与即时类效果式(item_and_instant_effects)——用户拍板:恒定表值 + +``` +伤害道具(可选类,道具段):D = power(恒定表值——不吃任何 percent 修正,2026-09-09 用户拍板) + 目标 = 固定顺位最前存活敌(B2 Rule 7),一次性消耗 +治疗包(可选类,道具段):可用 ⟺ hp < max_hp(满 → 拒绝、不消耗);hp' = min(hp + power, max_hp) +即时群伤 instant_aoe(即时类,授予段当场):对全体存活敌各 D = power(逐个 apply_damage、受击即死); + 零存活敌 → 零兑付"奖励未生效"行(结构性难达——授予段先于道具/战斗段,敌集空仅剩防御分支) +即时回血 instant_heal(即时类,授予段当场):可用 ⟺ hp < max_hp(满 → 零兑付"奖励未生效"行,不进库存无消耗语义); + hp' = min(hp + power, max_hp) +buff 类(即时类,授予段当场):B3 apply_effect;时长 = 当次结算(attack_up/defense_up D=1——2026-09-09 的 D=2 作废, + 两级奖励裁决下 buff 兑付窗口 = 授予段至本结算衰减段) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 道具威力 | power | int | [1, 999] | RewardTable.power(category ∈ {damage_item, heal_pack} ⇒ power ≥ 1 加载校验);恒定表值非 roll 值 | +| 主角血量 | hp / max_hp | int | [0, 999] | B2 查询(B1 编排读值做守卫——B4 零跨系统读) | + +**口径后果(2026-09-11 用户裁决改写——attack_up 复活)**:HeroTable.atk 基础攻击恒存在(域 [1,999]、0 加载拒绝——B2 Rule 9),**attack_up(+50%)复活**:唯一消费面 = 主角战斗段先手攻击(伤害算式归 B1 战斗段;"道具不吃修正"的拍板不变——attack_up 只乘基础攻击、不乘刀)。原"0 × 50% = 0 死行、MVP 池不含 attack_up"口径作废;**attack_up 是否入默认根池**随奖励改造批裁决(C3 内容所有权)——复活批不擅自入池。**A3 条款 2 锚的参照对象回迁 attack_up 当量**(B3 Interactions 原文即 attack_up——2026-09-09 迁 defense_up 的注记退役;换算归 B1 条款①对照表——B4 只供行值清单;B3 锚预警裁决前置不变)。weaken/defense_up 不入本式(敌输出侧/主角承伤侧,B2/B1 域);读值时点 = 道具段、施加归 B1(B4 零跨系统读)。 +**Output Range:** D = power(恒定);hp' ∈ [hp, max_hp](clamp 式上界——满血分支被拒绝守卫拦在消耗前)。 +**Example:** power=6 伤害道具 → 顺位最前敌 −6;重击拍前用刀击杀 → 该敌重击永不兑现(B2 死亡即不动手的道具形态)。满血(hp=max_hp)用治疗包 → 拒绝、库存不减。 +**边界注记:** 取整无涉(全整数);使用守卫(满血)归 B1 编排(义务④);hp=0 在窗口时点**结构不可达**(败判定在判定段、先于本窗口不存在;上一次结算的判定段保证到本窗口时 hero 存活)——不定义复活语义(2026-09-09 评审补)。 + +### F5 伤害道具保底(damage_item_floor,**每板可选伤害保底**——仅计 damage_item 行;拾取与使用不在保证域)——用户拍板:草稿重 roll + +``` +断言 |{c ∈ 资格格 | 身份(c).category = damage_item}| ≥ K_dmg(RewardConfigTable [0,999],默认 1 provisional) +执行 = roll_board 内"草稿-校验-提交":不过 → 草稿作废、同 scope 续消费流重 roll(全序确定、可回放) +超 N_draft 次(RewardConfigTable,默认 32 provisional)→ 致命(权重配置错误,fail-fast) +``` + +**语义注记(2026-09-11 两级奖励改写)**:保底对象 = **可选类伤害**(damage_item——玩家可控时点的伤害资源),即时类(instant_aoe)不计——即时类自动兑付无规划面,保底的意义在"每板至少有一把可囤可择时的刀"。**基础攻击在场后本保底为内容健康护栏非胜利路径闸**(B2 Rule 9——战斗恒可胜)。 + +**加载期护栏(2026-09-09 评审增补,两条新致命——关闭"保底全绿、游戏不可胜"的威力空洞与零伤害世界后门)**: +- (i) `K_dmg ≥ 1 ⇒ safe_cell 池存在 power ≥ P_min 的活 damage_item 行`(P_min = RewardConfigTable 配置,默认 8 = 默认根最低刀 power——2026-09-11 批)——manifest 行 `floor_supply_missing`(根 `b4_floor_supply`); +- (ii) `K_dmg = 0(断言关闭)⇒ safe_cell ∪ clear_default 仍须含 ≥1 活 damage_item 行`(无条件后闸——**内容健康护栏、非胜利路径闸**:2026-09-11 用户裁决后基础攻击保证战斗可胜,本闸防"零伤害道具世界"经纯内容调参静默复活导致战斗拖长、奖励系统空转;**与 B2 OQ9 并案登记同一 owner**)——manifest 行 `no_damage_row`(根 `b4_nodamage`)。 +- **概率性致命带注记**:合法权重下 N_draft 耗尽概率随 K/池构成可达非平凡值(同配置不同种子一个能玩一个崩——种子轮盘);调参期批量仿真(刀供给分布 × A1 板分布的可行率)登记为 C3 调参流程义务(缺口预检),把配置错误暴露在调参台而非玩家局内。 + +**Ready 后只读不冲突**:重 roll 全部发生在提交前(Unrolled 态);确定性 = 同 scope 消费轨迹的确定函数。**否决案存档**:清图槽保底(弃图流覆盖不到——RAP 刷图非必经清图;economy-designer 复核:清图免费换板且多清图槽,吞吐严格支配 RAP 刷新,无刷刀农场漏洞);safe_cell 伤害权重下界(概率缓解非结构保证,留作 C3 伴生调参)。 +**Output Range:** K_dmg=0 = 断言关闭(合法极端,胜利路径无保证——登记 C3 禁区默认值);|资格格| < K_dmg 恒不过 → 重 roll 耗尽致命,暴露配置错误。 +**Example:** 12 格板首稿 0 件刀 → 作废重 roll,次稿 1 件 → 提交 Ready。 +**边界注记**:断言仅计格奖励身份(清图槽不计);归 B4 自身,B1 仅编排恰一次调用。 + +### F6 当量换算供给(equivalence_supply,A3 条款①② 的 B4 侧输入) + +``` +供给 = RewardTable 全活行投影(safe_cell ∪ clear_default 逐行 category/display_name/power/tier/effect_id/weight)+ tier_min_buff = min{tier | safe_cell ∧ category=buff ∧ weight>0}(行集空 = F1 校验对偶致命) +量纲建议 = 主角 HP 点(与 m_amb 同量纲);伤害道具当量 = power(恒定表值直接可算) +``` +**供给面扩注(2026-09-09 评审)**:原"活 buff 行清单"收窄不足以承载 A4 约束②③与 A3 条款⑤对账——扩为全活行投影,道具/清图行值在内(B1 直读 ConfigDB 本也可绕过,接口名实相符)。 + +**衔接注记(B2 OQ3)**:伤害道具作为主角**规划面**伤害令"安全格拾取 → 伤害当量"可落地(2026-09-11 后基础攻击为恒定基底、道具为可规划增量——**部分缓解非闭合**——窗口使用次数是战斗轨迹量,归 B1);单一线性换算与条款 1/2/3/5 的闭合归 B1 单一 owner,本式只供原材料与量纲建议。默认根 buff 活行 = defense_up(attack_up 已复活,默认根入池随奖励改造批——F4 复活口径)。 +**Output Range:** tier_min_buff ∈ [1, 8];量纲仅为建议(B3 锚预警"瞬时 vs 总暴露"归一口径裁决前置,B1 可另选)。 +**边界注记**:行值 provisional 归 C3;锚验收权 B4/B1(B3 Rule 10 同款)。 + +### 默认根内容行(2026-09-11 两级奖励批重登记,值 provisional 归 C3;溯源 = 原型 v3 锚 HERO_MAX_HP=50 + 概念原型 HERO_BASE_ATK=6;原 2026-09-09 五行集作废——two-tier 重构见下) + +| reward_id | category(交付类) | power | weight | tier | pool_tag | 说明 | +|---|---|---|---|---|---|---| +| defense_up_basic | buff(即时) | —(不消费) | 24 | 2 | safe_cell | 当次受伤减免(B3 defense_up D=1) | +| attack_up_basic | buff(即时) | —(不消费) | 20 | 2 | safe_cell | 当次主角攻击增伤(B3 attack_up D=1——复活后首批入池) | +| spark_aoe | instant_aoe(即时) | 4 | 16 | 1 | safe_cell | 群体攻击:全体存活敌各 4 点 | +| heal_minor | instant_heal(即时) | 4 | 15 | 1 | safe_cell | 即时小额回血(满血零兑付行) | +| knife_t1 | damage_item(可选) | 8 | 15 | 1 | safe_cell | 可囤可择时的基础刀 | +| knife_adv | damage_item(可选) | 16 | 4 | 2 | safe_cell | 雾档升级刀 | +| heal_pack | heal_pack(可选) | 12 | 6 | 2 | safe_cell | **稀缺强治疗**(可选治疗唯一形态) | +| clear_knife | damage_item(可选) | 16 | 1 | 2 | clear_default | 清图奖励刀 | + +归一占比:**即时类 75% / 可选类 25%**(2026-09-11 用户裁决配比——原刀 40/治疗 25/石肤 35 作废);治疗合计 21%(即时 15 + 可选 6)——可选治疗自 25% 降至 6%,"治疗是稀缺奖励";tier_min_buff = 2;T_fog_min = 2 → 雾池 = {defense_up_basic, attack_up_basic, knife_adv, heal_pack}(四行,非单一化 ✓);**AC-19 对账对更新 = (knife_adv, attack_up_basic)**(雾池最低活档行 ≠ tier_min_buff 行——非平凡判别保持)。 + +**方向预检(B2 clause3_supply 先例,登记对账记录;2026-09-11 两级奖励批重算——刀供给期望 ≈ |资格格| × 19% × 9.7(加权刀力 = (15×8+4×16)/19 ≈ 9.7)≈ 1.8|资格格|/板,另加即时群伤与基础攻击分量;击杀需求 = 敌 hp 18(单敌档)/ 54(3 敌档)——**速杀退化预警⑥在"基础攻击 6/结算 + 即时类自动兑付"下压力进一步上升,敌 hp 曲线与奖励供给禁止独立调参(C3 联动重算义务)**;输入缺口:|资格格| 分布待 A1 actuals p10/p50 → 落地后重跑;max_hp = 50(原型锚)为治疗供给对照基准)**: + +### 失衡预警清单(playtest 预注册,economy-designer;2026-09-09 评审重校准——①拆短缺/浪费、④按吸收上限重推、⑤补雾板分层,新增⑥⑦⑧) + +① **刀供给短缺/浪费双指标**:短缺 = "获得刀伤供给 ÷ 击杀需求" <1.0 即警(供给面信号,按 unit_count {1, 2–3} 两档分设阈值——原单一">2.0 上尾"口径横跨两档必然一头常鸣,作废);浪费 = "未用刀伤 ÷ 获得刀伤供给" >0.5 即警(效率面,与短缺分开读——供需贴近下界时高效用刀本就趋零,原口径在紧张局常态误报)。② **治疗双尾**:溢出 = HP 满拒绝率 >40%(**2026-09-11 两级治疗注记**:instant_heal 满血零兑付并入本仪表——小额浪费可容忍、阈值放宽;heal_pack 满血拒绝维持 >40%);短缺 = "败北且库存有未用治疗包"占比 >20%(**可选治疗稀缺化至 6% 后本仪表为主仪表**——强治疗名存实亡的探针)。③ **雾区档位空窗 + 单一化**:雾区拾取平均 tier ≤ 平原即警;增雾池单一化监测——雾区拾取 distinct reward_id 数 = 1 持续即警(雾池收窄 → "越险货越同",T_fog_min × tier 分布联动调参)。④ **buff 零兑付双表**:稳态(单格授予批)零兑付率 >25% 即警;**连锁批量授予零兑付率**单列(结构性偏高——登记推导:buff 供给 λ = 8 挖/轮 × 44% ≈ 3.5 层/轮 vs 吸收上限 1 层/结算 × max_stack 2,批式授予下零兑付率远高于单格批;阈值 >50% 即警);**结构性缓解(2026-09-11 两级奖励批)**:buff 时长改当次结算(D=1)——浪费不再跨结算累积、保质期一次结算天然有界,但同结算内批量授予的零兑付仍存在,仪表保留;清图槽 buff 零兑付单独计数维持。⑤ **清图支配反转**:弃图率 >30%(A3 OQ3 联动)——**雾板/无雾板分层观测**(A4 明文要求补登;聚合指标会被雾板污染或反向掩盖)。⑥ **速杀退化**:战斗结束时未开资格格占比 >70% 即警(供给过剩 → 战斗提早结束 → E2 未挖身份零发放,奖励系统饿死自己的高光与雾内容——2026-09-09 评审新增)。⑦ **窗口摩擦**:窗口跳过率、单结算平均明细行数(playtest 预注册,阈值归 C3);**结构性缓解(2026-09-11 两级奖励批)**:库存只收可选类(25% 权重)+ 窗口单件制——窗口频率与每次决策量双降,本预警预期大幅缓解、保留观测。⑧ **H2 曝光率**:含清图奖励兑付的结算数 ÷ 战斗数(OQ4 折算裁决的数据输入)。 + +## Edge Cases + +> 三项边界裁决〔2026-09-09 用户拍板;第三项随 full 评审拍板〕:**道具段照常消耗**(超额选刀烧刀 = 选定即承诺的成本)· **清图槽允许 buff 行**(A3 条款④按行级正当量兑现,运行时满层为合法例外)· **窗口锚点 = 授予段之后**(当轮拾取当轮可用——Rule 5)。 + +- **If 资格格集为空(S ∖ R₀ = ∅)**:三层防线——① A1 生产面 `R₀ ≠ S` 合法性闸(修订义务已登记、未落地);② A2 install 不变式④ **现行已生效**(R₀=S 板装板即拒,B4 在其后序恒见非空集);③ B4 roll_board 致命(fail-fast 兜底,对偶于 F1 池空裁定)。B4 零静默回退。 +- **If 战斗结束时仍有未开资格格**:其预分配身份随映射 Retired 消亡——**零授予、零明细行**(未拾取 ≠ 溢出:从未发生的事不进结算,非静默蒸发)。 +- **If 同一结算多格授予同类 buff 超过 max_stack(连锁批量 13 格链 × max_stack=2 ⇒ 大量同文"奖励未生效"明细行)**:按规范序逐格受理,满层起 apply_effect 返回 false → 各产一行"奖励未生效"明细(零兑付、无折算——用户拍板;A2 登记义务"连锁批量授予溢出吸收"的 B4 侧裁决落点:**显式接受**);道具类无满层概念(库存无上限)。**玩家侧缓解(2026-09-09 评审如实改写——原"下轮绕开满层格改道"不成立:层数长在 B3 容器上不长在格子上,且未开格身份不呈现,无格可绕)**= 叠层感知挖掘(HUD 满层后少开安全格/优先挖刀——弱自救);结构位 = 未开格身份呈现深度(OQ2/A5,垂直切片)。**明细行数据面**:未生效类行携带**可聚合的计数/批次字段**(同类聚合计数、来源批次——是否合并呈现归 B1/B5 裁决,数据必须可聚合,防事后补字段 = 接口漂移);呈现归 D1 义务③,行协议 owner = B3 义务⑧。 +- **If roll_board 后映射 Ready、B1 未调而挖掘已开始**:grant 在 Unrolled 前/Retired 后调用 → **false + 诊断**(受理/拒绝口径,非进程致命——编排违约显式暴露,B2 同款)。 +- **If roll_board 被二次调用**:Ready 相位再调 → **false + 诊断**(相位守卫,B2 AC-1 同款)。 +- **If 换图(retire + install)发生在战斗中**:旧映射 Retired(未拾取身份消亡)、新图 install 后 roll 新批(新图新身份 = reward_roll 流**新 scope 批**,与 board_gen"每次生成新图"同构——scope 语义改写义务见 Rule 2/Dependencies);**库存不受影响**(战斗域跨图延续,Rule 4)。 +- **If 关卡结束(clear_inventory)后仍调 grant/stock/use**:Cleared 相位 → **false + 诊断**;clear_inventory 由 B1 编排于**关卡末**恰一次(2026-09-11 关卡域拍板——原 battle_over 时点作废;重复调用 false + 诊断,幂等守卫)。 +- **If 道具使用窗口打开时库存为空**:零使用恒合法(跳过恒合法);呈现归 D1(可整体不弹窗——P4 节奏,B4 建议登记;窗口触发政策见 D1 义务②——MVP 有库存恒弹,候选"仅可用品触发/常驻库存条"归垂直切片)。 +- **If 同一件道具被选用两次**:`use` 对已 Used 实例 → **false + 诊断**(Used 不可逆);单帧选择收集口径归 D1(义务②)。 +- **If 道具段中伤害道具施加时已无存活敌(前序刀已全灭)**:**照常消耗——标记 Used、零效果**,逐件进结算明细"已消耗未生效"行(B3 义务⑧"溢出未生效"行协议同款数据面)。〔2026-09-09 用户拍板:选定即承诺;同日评审更正理由〕~~原理由"敌 HP 精确值玩家不可见(B2 视觉推测型)"~~ **作废**——B2 呈现规格供给 hp/max_hp 血条数字(视觉推测型只封锁下一拍意图数值,不封锁当前血量),敌我血量在选择时点**均**可读。**现行理由两条**:① **守卫所有权不对称**(非信息不对称)——满血守卫是 B1 读 hero hp 的表局部判定(F4 已定),而零效果判定须读 B2 存活集,B4 零跨系统读纪律不为它开口子;选定即承诺把战斗轨迹判断留给编排层;② **反抖动**——选定即承诺防窗口内反复重选拖垮结算节奏。窗口内可用性预判(usable_now,B1 组装)承载"误触防护"半句。段序不截断:选定件全序执行 → 战斗段照常推进(死敌"死亡即不动手"空转)→ 衰减段 → 判定段判胜 → 关卡末 clear_inventory 全清(含未选件——D1 义务⑤ 清空反馈行;2026-09-11 关卡域改写对齐,原 battle_over 时点作废)。"零效果"事实判定归 B1 编排读 B2 存活集(读值做判定,非拒绝)。监控锚 = 失衡预警①(浪费半)。 +- **If 清图槽身份为 buff 行且授予时已满层**:零兑付 + "奖励未生效"明细行(Rule 3 同款协议)。〔2026-09-09 用户拍板:允许 buff 行入 clear_default 池〕A3 条款④"清图奖励>0"的兑现口径 = **行级恒有正当量**(F3 加载期校验:power≥1 ∨ effect_id 非空),运行时满层拒绝为合法例外、P2 诚实由明细行承载;常态化监控锚 = 失衡预警④(清图槽 buff 零兑付单独计数)。 +- **If 治疗包 power 超过当前缺血**:`hp' = min(hp + power, max_hp)`——部分回复合法(clamp 归 B1),**不拒绝**(与满血拒绝相区分:满血 = 零收益守卫、超量 = 部分收益)。 +- **If RewardTable buff 行的 effect_id 悬空(B3 无此行)**:首个 effect_id 引用行回填后,B3 悬空校验激活 → 加载期致命(D2 通道);此前不校验(dangling_ref_policy 既有口径)。 +- **If 战斗中热重载 RewardTable**:D2 战斗中 reload 拒绝门(D2 EC8,B1 经 set_reload_gate 置位);**已授予实例携带行值快照**(power/effect_id 授予时冻结——B2 行值快照同款),映射中未授予身份仅是 reward_id 引用、grant 时快照。 +- **If cleared 与 battle_over 同结算(末图清图即胜利)**:授予段先兑付清图奖励 → **同结算道具使用窗口即可选用(含当轮清图奖励件——2026-09-09 评审窗口后移的收益面,原"先授予后销毁、任何情况下不可用"的必然作废已消解)** → 未选用件**随库存关卡域延续**(2026-09-11 关内延续——原"随 battle_over 全清消亡"作废;末图 = 关卡末图时未选用件至关卡末清空 + 反馈行归 D1 义务⑤)。折算/继承结构位归 OQ4(Alpha,C2/D5——折算点 = 关卡末)。 +- **If 池含 attack_up 行(2026-09-11 复活后为合法内容,原"死行"条目改写)**:授予走 B3 apply_effect 正常受理、消费于主角战斗段先手攻击——原"授予零消费、纯拾取感损伤、C3 禁区注记"作废;机制层不做校验(内容决策归 C3)。 +- **If 玩家囤刀跨战斗/跨图累积(数量无上限)**:合法策略且为**设计意图**(2026-09-11 关卡域拍板——可选类道具是关内资产,"囤刀待机"的规划视野 = 整个关卡,P2 长线规划形态兑现;原"幻想 scope = 单场战斗——战斗域拍板"注记作废);无运行时上限(伤害恒定表值 ⇒ 无乘法爆炸);失衡监控 = 预警①(浪费半)与⑥(速杀退化),修正入口 = C3 权重调参(items_per_settlement_cap 结构位默认关——Tuning Knobs)。 +- **If 同格重复 grant / 同一 cleared 结算重复 grant_clear(Ready 相位,2026-09-09 评审裁死语义〔E18〕)**:grant 同格重受理 → **返回同一预分配快照 + debug 诊断**;grant_clear 重调 → **返回同一预分配身份 + debug 诊断**(幂等受理族;受理/兑付恰一次归 B1×②③,重复调用的诊断面留开发期暴露;对比:roll_board 二调 = 拒绝〔结构性相位违规,E5〕,重受理 = 编排瑕疵〔幂等兜底〕)。 +- **If grant/query_identity 传入清单外坐标**(非末次 roll_board 资格清单——R₀ 格/雷格/板外,E19):query_identity → **null + 诊断**、grant → **false + 诊断**(未分配非错误态;A5 探测查询任意格时,清单内未开格恒可查〔AC-2 分支 3〕、清单外 = 未分配)。 +- **If stock() 入参越域(E20)**:未知 item_id / buff 类条目 → **false + 诊断**(仅受理道具类条目,category ∈ {damage_item, heal_pack});来源 {cell, round} 由调用方供给、**B4 不校验真值**(真值归 B1×②)。 +- **If query_inventory 的枚举序(E21)**:= stock 受理序(插入序,B3 Rule 6 先例)——D1 窗口呈现序契约。 + +## Dependencies + +**Upstream(hard)**:D2 数据配置——**RewardTable 新表 schema + 内容行回填**(本 GDD 为回填 owner,走 D2 接口漂移规则回写其 Core Rules 表清单 + registry)+ **RewardConfigTable 单行全局新增**(2026-09-09 评审)+ ConfigDB 单点读取。无其他玩法上游**运行时**依赖:A2 的资格事实经 B1 结算编排转化(零直接接口);A3 条款④⑤ 与 A4 三条定价硬约束 = **对账/验收锚关系,非依赖边**(B3"相邻"口径同款)。 + +**A4 定价硬约束的 MVP 承载状态(2026-09-09 评审显式宣告)**:**约束②(清图奖励锚随雾成本负担上浮)在 MVP 结构性不可闭合**——clear_default MVP 单默认槽 = 板无关常量池,清图奖励当量无法随雾负担变化(非 deferral 而是**本期不可闭合**;B1 强制检查点只能对该常量判 pass/fail,改 power 会同步抬高无雾板、破坏无雾侧定价);激活载体 = clear_\ 插槽(垂直切片,待 C1 biome 词表落定)。**约束③(雾板净当量优势带上界)在②缺位 + 雾格仅 tier 下界加成的 MVP 结构下近乎必然平凡通过(静默绿灯风险)**。两约束的休眠状态与接受风险(重雾板清图欠偿 → 弃图偏斜——A4 警告的 level 2 结构性高发位)在此显式登记;对冲 = 预警⑤雾板分层观测 + 预警⑥速杀退化监测。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | **hard(provisional——B1 未设计)** | 结算编排期调用:roll_board / grant / grant_clear / stock / use / query_inventory / clear_inventory + **B1 义务清单 6 项**(Detailed Design 节:① 段序骨架〔含窗口锚点 = 授予段后 + 回放输入登记〕② 受理分流与零兑付协议〔**每格恰一次** + 聚合计数字段〕③ clear_reward 授予路径〔B3 OQ4 关闭条件〕④ 窗口与道具段编排〔usable_now 组装 / 满血守卫 + 逐件动态守卫 / 零效果判定 + 已消耗未生效行 / 击杀结果进流水 / clamp〕⑤ 生命周期编排〔含清空反馈计数〕⑥ roll_board 恰一次) | +| C3 | soft(**provisional**) | 权重/档位/威力/池内容曲线数值调参所有权(默认根行集已登记);T_fog_min/K_dmg/N_draft/P_min 参数(RewardConfigTable);**调参期批量仿真义务**(F5 可行率 + 登记常数重制联动);失衡预警指标①–⑧ playtest 归口;attack_up 入池裁决(2026-09-11 复活后随奖励改造批,原死行禁区注记退役) | +| C1 | soft(**provisional**) | biome/tag 图类型供给(clear_\ 插槽差异化输入——插槽激活的前置) | +| C2 | soft(**provisional**) | **关卡末剩余道具折算结构位归属**(OQ4 残余——2026-09-11 关卡域拍板后折算点 = 关卡末,转金币/继承候选,Alpha);冒险域奖励(关卡大奖励)归 C2,与 B4 库存无交集 | +| B3 | 相邻(**零运行时依赖**——调用经 B1) | buff 类 effect_id 精确引用(首个引用行回填激活悬空校验 + 条件性夹具);**锚验收权归 B4/B1**(A3 条款 2 + A4 硬约束①,MVP 参照对象 = defense_up 当量——F4/F6 口径后果);满层拒绝协议对侧;source_class 枚举已含 clear_reward/safe_cell(零变更) | +| B2 | 相邻(**零运行时依赖**) | 伤害道具默认目标 = 固定顺位(Rule 7 消费);~~HeroTable.atk 激活裁决~~(2026-09-11 用户裁决已决——基础攻击恒存在、attack_up 复活);道具段先于战斗段的段序战术面(死亡即不动手的道具形态;战斗段新增主角先手攻击——B1 域);伏击清单扩展联动对侧 | +| D1 | soft | **呈现义务 5 项(缺项即 blocking;3→5 评审扩容)**:① 已开格+库存身份恒定可读〔库存常读条 = 挖掘期规划面〕② 道具使用窗口〔usable_now 呈现〕③ 结算明细**四类行**呈现〔含聚合计数与语境注记〕④ H2 清图奖励行强调呈现 ⑤ 关卡末(level_clear)库存清空可见反馈〔2026-09-11 关卡域改写对齐〕 | +| B5/D5 | soft | 结算明细道具行(数据源 = B1 流水,含击杀结果字段);道具使用窗口教学 + "关卡末库存清空"教学(RAP 清零同款先例;2026-09-11 关卡域改写对齐——原"战斗结束库存清空"作废) | +| A5 | 相邻(预留桥接位) | aux_item 预留枚举(校验拒绝——启用走修订);未开格奖励身份探测 = A5 探测道具零成本桥接(身份已 install 期预分配,OQ 登记,垂直切片) | + +**双向一致性核对**(systems-index 依赖分层表):索引分层"B4 ← A2,B3,D2"——本 GDD 定稿后**硬上游仅 D2**;A2 = 事实经 B1 转化零直接接口、B3 = 验收锚关系非运行时边(B2"其余为事实读取不列边"注记口径同款)——索引分层行 B4 ← D2 已更新(2026-09-09 定稿批)。**接口漂移登记(2026-09-09 评审批全面兑现——含正文补同步)**:① game-concept 待定问题"主动道具后续版本加入"于 MVP 激活(道具使用窗口——用户拍板),use_timing 字段从预留转承重——game-concept 第 144 行注记已同步;② registry `rng_service.scope_event_binding` 的 reward_roll 批次语义改写为"每次 board install(格奖励批 + 清图奖励批)"——**registry 与 D2 正文均已同步**(原定稿批只改了 registry 与 D2 Dependencies 行;D2 Core Rule 5 与 D2 Interactions B4 行两处旧语义漏网——2026-09-09 评审批补齐;consistency-check 修正原误引 `board_gen.scope_event_binding`);③ **锚参照迁移传播**(2026-09-09 评审批):systems-index A3 OQ5 注记、registry `status_effect.anchor_warning`、registry `clause3_supply.direction_precheck` 三处补 defense_up 迁移注记——原 attack_up 字样为历史口径,迁移后条款 2 预检未重跑的事实同批登记(见 F4/F6);④ game-concept 创意风险 #1 与可证伪信号的 MVP 口径注记已同步;⑤ 窗口锚点修订(授予段后)随本批同步 systems-index 门禁块。 + +## Tuning Knobs + +| Knob(RewardTable 行值 / RewardConfigTable 配置值,均 D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| RewardTable.weight(逐行) | **默认根行集已登记**(Formulas 节 2026-09-11 两级奖励批八行:即时类 75% / 可选类 25%——可选治疗 6% 稀缺化;clear_knife 单行槽;provisional 归 C3) | 即时类过剩 → 结算明细淹没("奖励未生效"行刷屏)、可选类相对贬值 | 可选类过低 → 窗口长时空转(单件制下规划面稀薄)、F5 重 roll 频发乃至致命 | +| RewardTable.tier | 默认根:knife_t1/spark_aoe/heal_minor t1、attack/defense/heal_pack/knife_adv/clear_knife t2 | 雾区档位上移 → 平原奖励贬值(预警③联动) | T_fog_min 过滤失效 → A4 硬约束①锚失守(对账兜底) | +| RewardTable.power | 默认根:刀 8/16、治疗包 12、即时类 4/4(2026-09-11 两级奖励批——可选类更强、治疗稀缺化配套) | 道具段秒杀 / 治疗溢出(HP 满拒绝率↑——预警②) | 刀供给名义达标实际不足(击杀需求多轮化) | +| T_fog_min(RewardConfigTable) | 2(provisional) | 雾池收窄 → 雾格奖励稀化、单一化(预警③)、池空致命风险↑ | =1 过滤退化——雾区档位下界仅剩锚兜底 | +| K_dmg(保底刀数) | 1(provisional;仅计 damage_item——可选伤害保底) | 重 roll 频发 → 流消费膨胀;> 资格格数恒致命 | **=0 = 断言关闭(C3 禁区默认值——no_damage_row 加载后闸托底)** | +| N_draft(重 roll 上限) | 32(provisional) | 坏权重多次过审(掩盖配置错误) | 合法低供给池被误杀致命(概率性致命带——C3 批量仿真义务) | +| P_min(K≥1 供给闸刀力下界) | 8(= 默认根最低刀 power,2026-09-11 批) | 闸收紧 → 低威力刀行被加载拒绝(内容收窄) | =1 → 闸空转(power ≥1 恒真) | +| items_per_settlement_cap(**结构位**) | **关(无上限)** | 启用即限制——过高不构成保护 | 过低锁死胜局(击杀需求 ÷ 轮数——economy-designer 警告);启用裁决归 C3 playtest | + +- **非 knob(结构冻结)**:奖励类型五值封闭枚举(两类交付轴)、install 期预分配 + Ready 只读(无重掷 API)、**道具伤害恒定表值不吃修正**(用户拍板)、满层零兑付无折算、**库存关卡域(跨战斗延续、关卡末清空——2026-09-11 修订原战斗域)**、**窗口单件制(2026-09-11 拍板——体验反馈后修订走 GDD 流程)**、清图恒非空、结算段序骨架(buff/debuff > 道具 > 战斗)、零随机出 roll_board 一线、逐件实例归因——修订走本 GDD 流程非调参。 +- **Knob 交互**:K_dmg × safe_cell 伤害权重 × T_fog_min(雾池收窄挤压刀供给 → 重 roll 频发——三者禁止独立调参);**T_fog_min × tier 分布 × 雾池行数**(雾池单一化联动——预警③第二仪表,禁独立调参);power × B2 EnemyTable hp 曲线(击杀需求侧联动,C3 所有权);weight × B3 max_stack(buff 零兑付率联动——预警④)。 +- **调参所有权注记**:内容/曲线全部 provisional 归 C3;A3 条款③⑤ 与 A4 硬约束①②③ 的对账闭合归 B1(锚验收权 B4/B1——B3 Rule 10 同款)。 + +## Visual/Audio Requirements + +> B4 = Core 层数据/逻辑系统,**零自有视觉/音频资产**(headless 债,Rule 10);本节定义**数据面供给责任**,呈现实现归 D1/B5/D4/D5(义务清单见 Interactions)。 + +- **高光兑现节点(B4 侧条目化;2026-09-09 评审诚实化改写)**:**H1 开格拾取瞬间**(身份在场——"这格有货"是 P4 高频情绪节拍;触发数据 = `cell_revealed` + 身份查询;`query_identity` 投影含 tier——拾取瞬间可读"好货"档位)· **H2 清图奖励兑付**(轮末仪式峰值——clear_reward 明细行强调呈现,**D1 义务④**)· **H3 道具抢杀窗口**("重击拍前用刀"——"我算对了"的执行侧兑现,段序战术面 Rule 5;**感知数据前置:B1 流水含击杀结果字段**〔B1 义务④〕——无它则"敌灭、重击未落地"不可呈现,兑现点缺席)。**如实口径**:H1/H2 为奖励侧常规节拍登记;**创意风险 #2 的"完美排雷爆发结算"(零误标/全标中加倍)机制本体归 B1 强制检查点**(game-concept 原文归属——B4 交付插槽与明细行数据面,机制设计未在本 GDD 兑现);风险 #3 的标中旗即时节拍补偿同归 B1(A2 登记义务"清单外登记会丢失"先例——本条目化即义务,D1 义务④⑤ 为其 blocking 承载)。 +- **icon 映射键契约**:`reward_id`(全局恒定、跨 run 不重掷——B2 D1×4② 同款口径);category 三类第一层区分(buff/伤害道具/治疗包)+ 行级第二层;形状+色相双通道(art-bible 约束——art-bible 未建前键契约先冻结);名称数据源 = `display_name` 字段(Rule 9)。**绘制优先级插入点(2026-09-09 评审意向冻结)**:奖励徽章位于**旗子之下、变异纹理之上**,不得进入数字区域(game-concept 绘制优先级栈 数字 > 小人 > 旗 > [徽章] > 纹理 > 底色——art-bible 定稿确认,纳入格子样张测试验收项)。 +- **结算明细四类行协议**(数据源 = B1 流水,呈现归 B5/D1 义务③):道具行(含入库/生效语境注记)/ "奖励未生效"行(**携带可聚合计数/批次字段**——E3)/ "已消耗未生效"行(E10)/ "使用被拒"行(满血守卫拒绝的兜底呈现——usable_now 预判外的残余路径);未选用件"随关卡结束清空"语境注记(2026-09-11 关卡域改写对齐——原"随战斗结束消亡"作废;D1 义务⑤)。 +- **音频挂钩点**(D4,Alpha):开格拾取音(按 category 分色)/ 道具使用音 / 清图奖励音 / **负反馈聚合音**("未生效/被拒"类行数 >0 时单次结算至多一次——防逐行惩罚轰炸或静默惩罚,2026-09-09 评审补)——挂点事件归 B1/D1,B4 零音频依赖。 + +## UI Requirements + +> B4 **零自有 UI**(headless 可跑全奖励流,D2 Rule 7 同款);界面贡献全部经 **D1 义务清单①–⑤** 承载(缺项即 blocking,Interactions 表)。 + +- **道具使用窗口数据契约**:窗口载荷 = **B1 编排组装**——`query_inventory` 逐件实例(`item_id` + 来源 `{cell, round}` + 行值快照投影 + 状态;**枚举序 = stock 受理序**〔E21〕)+ **B1 侧守卫预判 usable_now**(满血治疗包禁选/提示——B4 零跨系统读,字段由 B1 供给〔义务④〕);呈现(逐件列表/选用序收集/跳过/窗口时机指示/usable_now 禁选态/触发政策注记)归 D1;窗口期选择收集与棋盘输入不竞争(A2 单意图帧先例);**窗口选择序列 = 结算回放的输入事件**(B1 义务④,B3 可回放契约的窗口侧兑现)。 +- **UX 规格归 D1 GDD**(设计顺序第 10 位)前置 `/ux-design`——窗口是 D1 域屏,B4 只供数据契约;B4 无 D1 外新增 UI 义务。 + +## Acceptance Criteria + +> **qa-lead 代理起草(lean 门禁,2026-09-09;full 评审同日修订)**:覆盖 10/10 Core Rules、F1–F6 全公式、E 节 21 条(E8/E15 两处非 G-W-T 形态以负空间/B1 deferral 承载——覆盖口径见覆盖矩阵末行)、上游承接验收项全集(A4 定价硬约束①〔②③ MVP 休眠显式宣告〕、A3 条款④、B3 悬空首引、身份预读、~~attack_up 死行内容断言~~〔2026-09-11 复活后改承载 attack_up 行合法性——AC-18 分支 2〕)。三项最终拍板语义已按 E 节定稿承载:**道具段照常消耗**(AC-14 分支 3——不采信任何"拒绝不消耗"旧读法)与**清图槽允许 buff 行**(AC-11)、**窗口锚点 = 授予段后**(Rule 5——B4 冒烟不断言编排时点,构造序扮演)。 +> +> **测试载体与注入口径**:B4 是 Core 层数据/逻辑系统——非 UI、无场景依附。冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code),ConfigDB/RngService autoload 由脚本显式实例化)。映射容器/库存容器/RewardTable schema 经 **class_name 直引实例化**(每用例全新实例、无跨用例状态——B3/A4/B2 直引先例;AC-21 扫描对该路径豁免)。B1 未设计(设计顺序第 9 位),冒烟**直接驱动 B4 最小 API 面**并扮演编排方(B2/B3 先例);**B4 冒烟零 B2/B3 实例化**(相邻零运行时依赖的代码侧对应——施加面/存活集/血量全部归 B1 OOS,StatusEffectTable/HeroTable 仅经 ConfigDB 读值)。资格格清单程序构造(真实来源 = B1 义务⑥——冒烟构造等价,零 A1/A2 接线)。 +> +> **API 面(provisional,封闭枚举——实现期 ADR 定名;2026-09-09 评审补输入域语义)**:`roll_board(eligible: Array[{cell, fog}]) -> bool`(install 期恰一次;消费 reward_roll 封闭流恰一 scope 批〔格批 + 清图批尾一号 × F5 草稿数——**每稿 = 格批 + 清图批尾一号,草稿作废连清图一号一并作废**〕;Ready 相位再调 → false + 诊断〔E5〕;空清单 → debug 断言中止〔E1 兜底〕;**清单含重复格 → 去重 + debug 诊断**)· `query_identity(cell) -> Dictionary|null`(Ready 只读预读面——返回 {reward_id, category, tier} **引用级投影**,非行值快照〔快照时点 = grant,E10〕;Retired/Cleared → null + 诊断;**清单外坐标 → null + 诊断**〔E19——A5 探测桥接的结构面〕)· `grant(cell) -> Dictionary|false`(受理面——返回 {reward_id, category, effect_id?, power?} 行值快照;Unrolled/Retired/Cleared → false + 诊断〔E4/E7〕;清单外 → false + 诊断〔E19〕;**同格重复受理 → 同一快照 + 诊断〔幂等,E18〕**)· `grant_clear() -> Dictionary|false`(cleared 轮兑付——身份 = install 批尾预分配;**同批次重调 → 同一身份 + 诊断〔幂等,E18〕**)· `stock(item) -> Dictionary|false`(道具类入库——**仅受理 category ∈ {damage_item, heal_pack},未知 item_id/buff 类 → false + 诊断〔E20〕**;逐件实例 {item_id, source{cell, round}〔调用方供给、B4 不校验真值——真值归 B1×②〕, 快照};Cleared → false + 诊断)· `use(handle) -> Dictionary|false`(窗口选用消耗——true ⇔ InStock→Used + 返回消耗回执〔**授予时快照**投影 + 来源归因〕;Used 句柄 → false + 诊断〔E9〕;**零游戏态前置守卫**——存活集/血量对 B4 不可见,E10 照常消耗与 F4 满血守卫归 B1 的结构前提)· `query_inventory() -> Array`(逐件实例枚举含状态 InStock/Used,**枚举序 = stock 受理序**〔E21〕——Used 保留只读可查〔"已消耗未生效"行数据面,B2 Dead 句柄同款〕;呈现过滤归 D1)· `clear_inventory() -> bool`(**关卡末**全清——2026-09-11 关卡域,原 battle_over 时点作废;重复 → false + 诊断〔E7 幂等〕)· `retire_mapping() -> bool`(board retire——B1 义务⑤)· `equivalence_supply() -> Dictionary`(F6 供给面 {全活行投影, tier_min_buff}——只读)· `weighted_pick(rows, r) -> String|null`(F1 选行算子纯函数供给面——B2 ambush_grant_plan 先例)。**全部布尔/字典返回 = 受理/拒绝**(附 debug 诊断、非进程致命——进程致命保留给 D2 数据通道与 fail-fast 兜底点)。**封闭枚举即负空间验收形态**:无重掷 API(Rule 8 Ready 只读)、无库存上限 API(Rule 4/E17)、无伤害/治疗/buff 施加 API(施加归 B1——F4/Rule 3)、无自主清空时点(clear_inventory 调用权归 B1)、无事件订阅、无 RNG 直连(唯一入口 = roll_board 内 open_stream("reward_roll"))。新增任何入口 = 接口漂移(GDD 修订非调参)。 +> +> **时序口径**:"一次 board install" = roll_board 恰一次的构造窗口;"cleared 轮" = grant_clear 构造调用;"battle_over" = clear_inventory(+retire_mapping)构造调用。结算段序骨架(授予段→道具使用窗口→增益/减益段→道具段→战斗段→衰减段→判定段——窗口锚点 2026-09-09 评审拍板移至授予段后)与使用窗口编排 = B1 域——冒烟以构造序扮演、不断言编排正确性(B2 时序口径同款)。**σ 到达机制(2026-09-09 评审补)**:D2 全新 RngService 首个 open_stream 恒为 scope 0——到达 scope=σ 须先 `open_stream("reward_roll")` ×σ 丢弃预推进(AC-7 分支 1 按此执行)。**纸面契约子集声明(A2 OQ2 先例)**:凡依赖 Ψ(σ)/Φ(σ)/σ\*/重 roll 轨迹**登记值**的 THEN,在取号约定实现期 ADR 落地前**不视为可执行**(受影响:AC-1 分支 1 非雾格值、AC-7 分支 2/3、AC-10 分支 1、AC-12 登记序列);雾格单行退化分支例外(r 无关,现即可执行)。 +> +> **只读观测量**:映射投影(reward_id/category——引用级)· 库存实例投影(item_id/来源/快照/状态)· 流 scope 计数器(D2 AC-4 公开只读——install 恰一 scope 的观测面)。相位无字段观测量:Unrolled/Ready/Retired 由受理返回值推断;Cleared = 逻辑失效态(失效句柄查询返回空 + 诊断、作用接口拒绝——AC-3 域闭;B2 Despawned 逻辑失效口径同款)。实例不变式(Ready 后映射零写入 / Used 不可逆 / 库存条目仅存战斗域)= debug 断言自守——**违规路径不可由封闭 API 构造**(负空间:无重掷 API 即"Ready 后零写入"的结构前提),不经冒烟断言;行为级证据 = AC-1/AC-2/AC-9/AC-14。 +> +> **期望值推导纪律**:weight/power/tier/T_fog_min/K_dmg/N_draft 全 provisional(Tuning Knobs)——各 AC 的 THEN 具体数值按**当前表读值/夹具登记值推导、不写死**(记号:w_i/t_i/p_i/T_f/K/N)。**结构定值断言合法**:category 三值封闭、pool_tag MVP 二值封闭、use_timing 单值、清图恒正当量校验存在性。原"safe_cell 活行 category 收窄 {damage_item, heal_pack, defense_up}(attack_up 死行不入池)"定值断言随 2026-09-11 用户裁决退役——attack_up 为合法行(AC-18 分支 2 改承载其合法性)。资格格清单与期望映射为登记常数(下述协议)。 +> +> **夹具与种子策略**:① **默认根**(B4 回填的 RewardTable MVP 行集——Formulas 节 2026-09-11 两级奖励批八行为登记值 defense_up_basic/attack_up_basic/spark_aoe/heal_minor/knife_t1/knife_adv/heal_pack/clear_knife + RewardConfigTable T_fog_min=2/K_dmg=1/N_draft=32/P_min=8)——结构断言、F6 供给与方向预检。② 根 `b4_golden`:D2 全表清单拷贝 + 权重/tier **冻结登记值**(登记:刀 w=2 t=1 / 治疗包 w=1 t=1 / defense_up w=1 t=3 / clear_default 单刀行 w=1)——黄金载体与默认根内容调参**解耦**(C3 重调默认权重不触动本根;雾池 = {defense_up t3} 单行退化——雾格分支 r 无关现即可执行)。③ 根 `b4_reroll`(**2026-09-09 评审补全登记;2026-09-11 补 p_min=4 覆盖**——刀 p=4 须过 F5 加载护栏 (i),随全局 P_min=8 生效):T_fog_min=1 / K_dmg=1 / N_draft=32 / p_min=4;safe_cell = 刀 w=1 t=1 / 治疗包 w=3 t=1 / defense_up w=3 t=2;clear_default 单刀行 w=1 p=4——满足全部加载校验(活 buff 行 ✓ / 雾池非空 ✓ / 清图正当量 ✓);重 roll 可达(首稿零刀 P ≈ (6/7)³ ≈ 0.63)而耗尽经验稀有(≈4×10⁻⁷/σ)——取号约定 ADR 落地后按缺口预检 8 复核可达性拆分、失效即重制。④ 根 `b4_fog_gate`:tier 布置使雾池唯一活行 = 高档行 H、非雾池含低档行 L(登记);**`b4_fog_gate_hot`(同批新增)**:同表 + T_fog_min=8 **表值烤入**——雾态池空加载致命(AC-6 分支 4 致命分支载体——R3 override 不触发加载校验,D2 边界⑤,通道错置已更正)。⑤ 根 `b4_clear_buff`:clear_default 唯一活行 = defense_up。⑥ 根 `b4_reloaded`:power/tier 各 +1(effect_id 不动——登记)。⑦ 根 `b4_nofloor`:safe_cell 无刀行 + K_dmg=0。⑧ 根 `b4_attackup_row`(原 `b4_attackup_leak` 改名——2026-09-11 复活后为正向夹具):safe_cell 追加 attack_up 引用行。⑨ **致命根 ×8**(manifest,见下;新增 `b4_floor_supply` / `b4_nodamage`)。⑩ **程序构造**:RewardTable + RewardConfigTable schema 直引(AC-16);资格格清单 `b4_list_A`(3 格含 1 雾)/`b4_list_B`(4 格)/`b4_list_single`(1 格)坐标入版本库。行为夹具根经 `--data-root=` 注入(仅 debug、不入 `data/tables/`——D2 夹具纪律);标量 knob 单变分支(T_fog_min=1、K_dmg=2)经 **R3 debug override**(域内值、运行时态——D2 AC-7 先例;**加载致命分支一律烤入根承载,不经 override**——2026-09-09 评审更正),两通道每夹具恰一登记。**登记常数协议**(B2 行集协议同源):期望格身份映射 **Ψ(σ)**、清图身份 **Φ(σ)**、重 roll 草稿数轨迹、A4① 对账对——入版本库;失效处置 = 先复核登记、登记无误即实现缺陷;**登记后安排第二人独立推导复核(A2 OQ3 同型义务——2026-09-09 评审补)**。**测试种子取号协议**:master=42;**σ 取号 = 每 roll_board 调用恰一 scope**(install 批含格批 + 清图批尾;retire 后新 install = 新 scope——E6/registry 改写语义);期望轨迹经 `derive_at("reward_roll", σ)` **纯函数测试通道**推导(不推计数器——D2 R2 预览语义的测试侧合法用途,A1 同款先例;黄金向量 m=42 scope0=7422916049646135537 见 D2 R2 向量表,本节复用不重制——推导起点锚)。 +> +> **致命类收窄豁免与 runner manifest**:B4 冒烟设**恰十行致命清单**(独立 headless 进程;2026-09-09 评审增补两行加载护栏):加载期 ×8(游戏通道 load)——`pool_empty_safe→≠0` · `pool_empty_fog→≠0` · `clear_empty→≠0` · `clear_no_value→≠0` · `no_live_buff_row→≠0` · `dangling_effect→≠0` · **`floor_supply_missing→≠0`**(K_dmg ≥ 1 ∧ safe_cell 池无 power ≥ P_min 活刀行——根 `b4_floor_supply`)· **`no_damage_row→≠0`**(K_dmg = 0 ∧ safe_cell ∪ clear_default 全池零活刀行——零伤害世界后闸,根 `b4_nodamage`,B2 OQ9 并案);运行期 ×2(调用后退出码)——`floor_exhaust→≠0`(roll_board 保底耗尽)· `empty_eligible→≠0`(roll_board(∅) debug 断言中止;release 构建语义 = push_error 致命路径——D2 EC7"零静默回退"同款,实现期 ADR 登记)。其余致命类别(通用枚举域/域校验/R1 类)由 D2 AC-2 同类别 loader 夹具承载(表无关,豁免声明收窄——B2 同款)。每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明)。 +> +> **覆盖声明(out-of-scope)**——deferral 生效条件 = **B1 义务清单 6 项 + D1 义务清单 5 项**登记(均"缺项即 blocking";D1 3→5 扩容 2026-09-09 评审):**B1×①** 段序骨架与结算内调用序(含窗口锚点 + 窗口选择序回放输入);**B1×②** 受理分流调用(grant→apply_effect/stock;**每格恰一次**)与满层"奖励未生效"明细行协议 + 聚合计数字段(E3/E11 大量同文行半);**B1×③** clear_reward 授予路径;**B1×④** 窗口与道具段编排——usable_now 组装、满血守卫(拒绝不烧包)+ 逐件动态守卫(同窗顺序多包)、零效果判定(读 B2 存活集)、逐件"已消耗未生效"明细行、**击杀结果进流水**、治疗 clamp(E12)、伤害施加与"恒定表值不吃修正"纪律;**B1×⑤** 生命周期编排恰一次(**关卡末** clear_inventory 时点〔2026-09-11 关卡域改写——battle_over 只退役映射〕+ 清空反馈计数——E2"零明细行"= 编排不调即无行,B2"B1 API 合规半句"同款);**B1×⑥** roll_board 恰一次编排。**D1×①…⑤** 预读呈现(含库存常读条)/使用窗口(选用·跳过·序收集·usable_now·触发政策)/四类明细行呈现(聚合 + 语境注记)/H2 强调呈现/关卡末清空反馈(不可 G-W-T)〔2026-09-11 关卡域改写对齐〕。另有:B3 满层拒绝原子(B3 AC 既有——跨引不重复);A4 约束②③(MVP 休眠——Dependencies 显式宣告)与 A3 条款⑤ 对账闭合(B1 强制检查点);attack_up 复活联动(OQ1);C3 数值与失衡预警①–⑧(playtest 域);未开格身份呈现深度(OQ2/A5+D1)。 + +**映射生命周期与预读(Rule 2/8)** + +- **AC-1(install 主式与登记映射基线——Rule 2/8、E5;根 = b4_golden,清单 = b4_list_A)** GIVEN b4_golden 根加载、全新 B4 容器(Unrolled)、RngService master=42 已播种(scope 计数器 = 0)。WHEN `roll_board(b4_list_A)`,THEN true(Unrolled→Ready);`query_identity` 逐格 = **登记映射 Ψ(0)**(含雾格分支——雾格身份 ∈ 雾池行,登记值);reward_roll 流 scope 计数器 = **1**(install 恰一 scope——registry 改写语义的行为面)。分支 2(行主序规范化):GIVEN 两套全新 RngService+B4(同 master=42、同 scope 起点),WHEN 分别以规范序/乱序传入同一集合,THEN 两映射逐格相同(**行主序消费序 B4 侧规范化**——F1"行主序逐格";排序责任不外推给 B1)。分支 3(Ready 相位守卫,E5):WHEN Ready 后再 `roll_board(任意清单)`,THEN **false + 诊断**、映射与流计数器不变(相位守卫——B2 AC-1 同款;double-roll 无守卫的编排半归 B1×⑥)。 +- **AC-2(预读恒定与结构可查——Rule 8;根 = b4_golden)** GIVEN roll_board 完成(Ready)。分支 1(恒定可读):对每格重复 `query_identity` ×3 → 逐字相同(结构保证非授予期随机)。分支 2(授予不改身份):`grant(c)` 后 `query_identity(c)` = 授予前值(身份恒定跨授予——同图跨轮恒定的观测半)。分支 3(未开格结构可查):对未受理格 `query_identity` 恒返回身份(**映射无"已开才可查"谓词**——A5 探测道具零成本桥接的结构前提;MVP 不呈现归 D1/OQ2)。分支 4(纯读):query 前后映射/库存投影零变化。WHEN `retire_mapping()` 后 `query_identity(c)`,THEN null + 诊断(映射消亡的观测形式——E6 旧映射半)。 +- **AC-3(相位守卫全集——E4/E7;根 = b4_golden)** 分支 1(Unrolled):全新容器上 `grant(c)`/`grant_clear()` → **false + 诊断**(E4 roll 前受理)。分支 2(Retired):roll_board → retire_mapping 后 `grant`/`grant_clear` → **false + 诊断**(E4/E2——未拾取身份消亡零兑付)。分支 3(Cleared):roll_board → grant → stock → `clear_inventory()` 后 `grant`/`stock`/`use` → 均 **false + 诊断**(E7);`query_inventory` = 空(InStock 与 Used 均清——关卡域拍板,2026-09-11);`query_identity(c)` → **null + 诊断**(Cleared 逻辑失效态的观测分支——2026-09-09 评审补)。分支 4(幂等):重复 `clear_inventory()` → **false + 诊断**(E7 幂等守卫);重复 `retire_mapping()` → false + 诊断(同型守卫——B3 EC11 先例,provisional API 完整性分支)。 +- **AC-4(空资格集兜底致命——E1;根 = b4_golden,manifest 行 `empty_eligible`)** GIVEN 全新容器、master 已播种。WHEN `roll_board([])`(S∖R₀=∅ 构造——真实流中 A2 install 不变式④前置,本 AC 直击 B4 兜底半),THEN debug 断言中止(fail-fast 兜底、零静默回退——F1 池空裁定的运行期对偶);测试通道诊断清单含类别;独立进程非 0 退出。 + +**身份 roll 算术(F1/F2/F5)** + +- **AC-5(F1 选行算子纯函数——累积区间/0 权剔除/单行退化)** GIVEN `weighted_pick` 纯函数面(行数组 + 取号 r 注入)。分支 1(累积区间,F1 Example):三行 w=2/1/1 → r=0/1 → 行 1(区间 [0,2))、r=2 → 行 2([2,3))、r=3 → 行 3([3,4)——W=4 边界号)。分支 2(0 权剔除):行集 {w=0, w=2} → W=2,r ∈ {0,1} → 唯一非零行(**0 权行取号前剔除、W 不含 0 权**)。分支 3(单行退化):单行 w=5 → r ∈ [0,5) 恒该行(合法极端——F1 裁定)。分支 4(取号域守卫):r ≥ W → debug 断言(纯函数域;游戏通道内 r 恒 < W 由流构造保证)。 +- **AC-6(F2 雾档过滤——雾池谓词/退化/链开同待遇;根 = b4_fog_gate + b4_fog_gate_hot + R3 override 分支)** GIVEN b4_fog_gate 根(登记:雾池唯一活行 = 高档行 H〔tier ≥ T_fog_min〕、非雾池含低档行 L)。分支 1(雾格过滤):`roll_board`(清单含雾格)→ 雾格身份 = **H**(单行退化判别——r 无关;雾格恒不出 tier < T_fog_min 行)。分支 2(非雾全池,**32 σ 双重量化——2026-09-09 评审改写**):对 σ = 0…31 逐一(scope 预推进)roll_board,THEN ① 每稿雾格身份恒 = H(过滤谓词零泄漏——任何泄漏以 ≈w_H/W 概率现形);② 非雾格至少一次命中 tier < T_fog_min 行 L(证明非雾侧零过滤);**登记值"非雾格 = L"降为诊断信息不作硬断言**(加载规则强制 H 活 ⇒ 非雾池 ⊇ {H, L} 至少两行,单稿"= L"是概率断言,忠实实现也会假失败)。分支 3(链开雾格同待遇):fog 布尔是唯一输入、无 chain/dig 通道分支(A2 EC4 同待遇半——构造性)。分支 4(T_fog_min 边界):T_fog_min=1 经 R3 override(运行时态、域内值)→ 雾池 = 全池,雾格身份 ∈ 全体活行集(登记 σ 下 = 登记值——过滤退化合法,A4 约束①仅剩 AC-19 锚侧兜底);T_fog_min=8 且表无 tier8 行 → **经烤入根 `b4_fog_gate_hot` 加载致命**(雾态池空——AC-17 `pool_empty_fog` 同类别;override 通道不触发加载校验,原"override→加载致命"通道错置已更正)。 +- **AC-7(F5 保底与草稿重 roll——结构保证/重 roll 证据/清图槽不计/断言关闭;根 = b4_reroll + b4_nofloor)** GIVEN b4_reroll 根(登记权重刀 w=1 / 其余 w=3;K=K_dmg、N=N_draft 表读值)与登记 σ 参考轨迹表。分支 1(保底结构保证):对 σ = 0…31 逐一(每 σ 全新服务+容器,**先 `open_stream("reward_roll")` ×σ 丢弃预推进至 scope=σ**——时序口径 σ 到达机制)`roll_board` → **每提交板 |刀身份格| ≥ K**(每板伤害供给下界保证的行为面)。分支 2(重 roll 发生证据):登记 σ\*(参考算术判定首稿 0 刀)→ 提交映射 ≠ 首稿参考映射(**草稿作废、同 scope 续流重 roll**);scope 计数器每 install 恒 +1(重 roll 不开新 scope——scope 语义 = install 批非草稿批)。分支 3(清图槽不计):登记 σ\*\*(首稿清图身份 = 刀 ∧ 格刀数 = 0)→ 仍重 roll(断言仅计格奖励身份——参考轨迹判别)。分支 4(断言关闭合法极端,b4_nofloor 根):无刀行 ∧ K=0 → roll_board true、提交板 0 刀(K_dmg=0 = 关闭——C3 禁区默认值的行为面)。 +- **AC-8(F5 重 roll 耗尽致命;根 = `b4_floor_exhaust`〔manifest〕)** GIVEN b4_floor_exhaust 根(safe_cell 池无刀行 ∧ K=1——每稿恒 0 刀)。WHEN `roll_board(b4_list_A)`,THEN N 次草稿后**致命**(权重配置错误 fail-fast);诊断含耗尽类别与草稿计数(N 表读值不写死);独立进程非 0 退出。变体分支:`b4_list_single`(1 格)∧ K=2(R3 override 域内值)→ |资格格| < K 恒不过 → 同致命(F5 Output Range)。 + +**确定性与清图(Rule 2/10、F3)** + +- **AC-9(零随机封闭与确定性回放——含 F5 重 roll 分支;根 = b4_reroll)** GIVEN 两套全新 RngService+B4(同 master=42、同根)。WHEN 同一定长序列驱动两遍(roll_board → query_identity 全格 → grant×2 → stock → use×1 → retire_mapping → roll_board〔新清单,scope+1〕→ grant_clear → clear_inventory → 全 query 面),THEN 两遍的映射投影、库存实例投影(含来源 {cell, round})、use 回执、流 scope 计数器轨迹**逐项相同**——**同 σ 同流消费轨迹 ⇒ 同身份映射(含 F5 重 roll 分支)**的确定函数性(D2 AC-15 的 B4 层前身)。分支 2(换图新 scope):σ 与 σ+1 的映射 = 登记相异对(新图新身份——E6 roll 半;库存延续半 = AC-13 分支 2)。 +- **AC-10(清图身份 install 批尾 roll 与兑付恒正当量——F3、Rule 6、A3 条款④;根 = b4_golden)** GIVEN roll_board 完成。WHEN `grant_clear()`,THEN 返回 = **登记清图身份 Φ(σ)**(install 批尾一号——登记值含批尾位次假设:实现改批首取号即失配暴露);返回身份满足**正当量**(道具类 power ≥ 1 表读值 ∨ buff 类 effect_id 非空——条款④"清图奖励>0"的行级兑现面)。分支 2(兑付期零随机):两套同 σ 容器、grant_clear 前置调用序列互异 → 返回恒 Φ(σ)(**身份 install 期已定、兑付期零 roll**;图类型战斗域恒定不逐图重掷的判别形式——兑付期重掷将随前置序列漂移)。分支 3(重复兑付幂等,E18——2026-09-09 评审补):同批次再 `grant_clear()` → **同一身份 + 诊断**(兑付恰一次归 B1×③)。 +- **AC-11(清图槽允许 buff 行——E13 拍板 + F3 校验不拒;根 = b4_clear_buff)** GIVEN b4_clear_buff 根(clear_default 唯一活行 = defense_up,effect_id 非空——正当量成立)加载**通过**(加载校验不拒 buff 行——2026-09-09 用户拍板)。WHEN roll_board → `grant_clear()`,THEN 返回 {category=buff, effect_id=defense_up}(分流数据面——B1 调 B3 apply_effect 与运行时满层拒绝〔零兑付 + "奖励未生效"明细行,合法例外口径〕归 B1×②/B3,OOS;失衡预警④清图 buff 零兑付单独计数 = playtest 域 OOS)。 + +**授予分流与库存(Rule 3/4/5/7)** + +- **AC-12(授予受理序与分流数据面——Rule 3/7、E3 前半;根 = b4_golden,清单 = b4_list_B〔4 格〕)** GIVEN roll_board(b4_list_B) 完成。WHEN 按规范序逐格 `grant`(连锁批量受理的冒烟构造序——B1 义务①同侧等价),THEN 逐格返回 = 登记身份序列(受理序 = 规范序);buff 类格返回 {category=buff, effect_id}(**B4 只定"给哪行"**——层数语义归 B3 合并产生,施加经 B1 调 B3,Rule 3;2026-09-09 评审措辞收敛);道具类格(damage_item/heal_pack)返回 {category, power}(**治疗包 = 道具类路由、非自动兑付**——Rule 7 负面:grant 不触发任何使用语义);**即时类格(instant_aoe/instant_heal)同样返回 {category, power} 快照**——grant 零路由差别,当场兑付的编排归 B1×②(B4 半 = 返回快照非库存语义)。分支 2(快照时点):grant 返回的 power/effect_id = grant 时表读值(E10 快照时点 = grant——差分判别在 AC-15)。分支 3(重复受理幂等,E18——2026-09-09 评审补):同格再 `grant` → **同一快照 + 诊断**(受理恰一次归 B1×②)。满层起逐格"奖励未生效"行协议 = OOS B1×②(E3 后半)。 +- **AC-13(库存生命周期/归因/无上限/跨图延续——Rule 4/5、E6/E17;根 = b4_golden)** GIVEN roll_board + grant(道具格) 完成、冒烟(扮演 B1)`stock` ×N(N = 登记大数 64)。分支 1(逐件实例与归因):`query_inventory` 恰 N 件、每件 {item_id, source{cell, round}, 快照投影}(**来源归因保 P2——D1 窗口数据契约**);N 件全部 InStock、零拒绝(**数量无上限**——Rule 4/E17,稀缺性由权重承载;无 cap API 负空间)。分支 2(跨图/跨战斗延续,E6 + 2026-09-11 关卡域):`retire_mapping()` → `roll_board(新清单)` → `query_inventory` **逐字不变**(含来源——库存关卡域跨图且**跨战斗**延续,与映射生命周期正交;跨战斗构造 = 共享容器重 roll,切片连续关卡模式同型);battle_over(映射退役)后 query_inventory 逐字不变(**库存不再随战斗清空**——义务⑤ 改写的行为面)。分支 3(关卡末全清):`clear_inventory()` → true、query 空(不跨关卡);此后 stock → false + 诊断(AC-3 分支 3 已载——注记)。 +- **AC-14(use 消耗/不可逆/照常消耗拍板——Rule 5、E9、E10 拍板;根 = b4_golden)** GIVEN 库存含刀(damage_item)与治疗包各 ≥2 件。分支 1(消耗回执与恒定表值):`use(刀)` → true + 回执 {category, power=**该实例授予时快照值**〔E14——原"表读值"措辞作废,防"使用期读活表"实现全绿〕, source{…}};同 reward_id 多件实例回执 power **逐字相同**(恒定表值非 roll 值——F4);实例状态 → Used。分支 2(不可逆,E9):同句柄再 `use` → **false + 诊断**;Used 实例 `query_inventory` 保留只读可查("已消耗未生效"行的数据面——B2 Dead 句柄同款)。分支 3(**照常消耗——2026-09-09 最终语义;本分支豁免域收窄**):`use(刀)` 于任意**游戏态**前置(含"道具段无存活敌"等价构造——B4 无敌态输入面,实现若存在存活/血量守卫则本分支失败)→ **恒 true + 标记 Used、零效果由 B1 判定**(选定即承诺:已选刀照常消耗、逐件"已消耗未生效"明细行归 B1 编排读 B2 存活集〔义务④〕+流水,OOS);**相位守卫不在本分支豁免域**——Unrolled/Cleared/Retired 相位的 use 照旧 false + 诊断(AC-3 分支 1/3、E4/E7——同输入同答案,2026-09-09 评审消解 AC-AC 矛盾);**与治疗包满血拒绝的不对称**:`use(治疗包)` 同样无条件受理(满血守卫归 B1 编排读 B2 hero hp——F4/义务④;"拒绝不烧包"半 = B1 不调 use 的编排纪律 + usable_now 预判呈现,OOS)。分支 4(选择序 = 使用序):任意序 use 多件 → 每件独立受理、Used 序 = 调用序(窗口选用序的 B4 半——选择收集归 D1×②、段内细则归 B1×①;同窗顺序多包的逐件动态守卫归 B1×④)。 +- **AC-15(行值快照抗表重载——Rule 9、E14;根 = b4_golden → b4_reloaded)** GIVEN b4_golden 根 roll_board + grant(道具格) + stock 完成(已授予实例)、另设未授予格(映射中 reward_id 引用)。WHEN 经测试通道换根加载 b4_reloaded(`load_tables(root, collect_only=true)` 直调——非 D2 `reload()` 路径,门语义归 D2 AC-16/B1,B2 AC-11 同款),THEN 已授予实例快照投影**逐字不变**(授予时冻结);未授予格 `query_identity` 的 reward_id 不变(映射仅持引用——热重载拒绝门 D2 EC8 的 B4 侧纵深);WHEN 对未授予格 `grant`,THEN 返回**漂移后表值**(快照时点 = grant——引用非活值的判别后半)。分支 3(拷贝判别):对返回快照投影原地改写 → 容器内实例投影不变(快照拷贝非活引用——B2 AC-11 分支 3 同款)。分支 4(**使用期快照纪律——2026-09-09 评审补**):换根后 `use(已授予实例)` → 回执 = **授予时快照值**非漂移值(AC-14 分支 1"快照值"措辞的行为级判别)。 + +**表校验与内容(Rule 1/9)** + +- **AC-16(RewardTable schema 域校验全谱——程序构造,Rule 1/9)** GIVEN RewardTable schema 类(行级 + 跨字段)class_name 直引实例(不经 load()/preload())。WHEN `validate()` 作用于越域代表值:category ∈ {resource, aux_item}(**预留值拒绝——Rule 1 fail-fast,B2 pattern 先例**)、pool_tag = "clear_fog"(**clear_\ 命名空间预留拒绝——Rule 6,A1 elite_ 同款**)、pool_tag = "nonsense"(未知枚举)、use_timing ∉ {pre_settlement}、weight ∈ {−1, 1000}、tier ∈ {0, 9}、power ∈ {−1, 1000}、跨字段 buff 行 effect_id 空(buff⇒必填)、跨字段非 buff 行 effect_id 非空(非 buff⇒恒空)、**跨字段 item 类 power=0(damage_item/heal_pack ⇒ power ≥ 1——F4 加载校验)**,THEN 各含对应域错误类别(零静默)。WHEN 域内代表值:**weight=0(该池不出此行——合法极端)**、buff 行 power 任意(**不消费非校验——B2 heavy_multiplier 先例**)、tier ∈ {1,8}、item 类 power ∈ {1,999},THEN 零诊断(防过度约束锁死内容——weight=0 与 buff 行 power 不校验是承重点)。 +- **AC-17(表级加载校验致命夹具——F1 裁定/F3/F5 护栏/F6 对偶/effect_id 悬空;manifest ×8)** GIVEN 致命根 ×8(独立进程、游戏通道——D2 AC-2b 协议):`b4_pool_empty_safe`(safe_cell 全 0 权)、`b4_pool_empty_fog`(全行 tier < T_fog_min——雾态空)、`b4_clear_empty`(clear_default 池空/全 0 权)、`b4_clear_no_value`(clear_default 唯一活行 = power 0 道具行——**恒非空的可判定形式 = 恒有正当量行,A3 条款④表侧兑现**)、`b4_no_live_buff_row`(safe_cell 有活行但零活 buff 行——tier_min_buff 无定义,F1 对偶致命)、`b4_dangling_effect`(buff 行 effect_id = "ghost_buff"——B3 StatusEffectTable 无此行;**条件性夹具:默认根已含 effect_id 引用行 ⇒ 悬空校验已激活**——dangling_ref_policy 首引兑现点)、`b4_floor_supply`(**K_dmg ≥ 1 ∧ safe_cell 池无 power ≥ P_min 活刀行——F5 加载护栏 (i),floor_supply_missing**)、`b4_nodamage`(**K_dmg = 0 ∧ 全池零活刀行——F5 加载护栏 (ii) 零伤害世界后闸,no_damage_row,B2 OQ9 并案**)。WHEN 各根经 `--data-root` 注入加载(collect_only=false),THEN 各进程非 0 退出且错误清单含对应类别;WHEN 默认根加载,THEN 干净通过(引用全解析——defense_up 存在)。 +- **AC-18(MVP 内容行定值与结构断言——Rule 1 收窄/F4 复活口径/E16;根 = 默认根 + b4_attackup_row)** GIVEN ConfigDB 默认根加载。WHEN 读 RewardTable,THEN:category 值域 ⊆ {buff, damage_item, heal_pack}(三值封闭定值);pool_tag 值域 ⊆ {safe_cell, clear_default}(MVP 二值封闭定值);clear_default 池 ≥1 正当量活行(默认根通过自身表级校验的行为断言——AC-17 对偶);权重比例与数值**不作定值断言**(provisional 归 C3);~~全部行无 effect_id = attack_up 引用~~(退役——2026-09-11 用户裁决 attack_up 复活,默认根当前不含 attack_up 行为**内容态事实**、非结构禁止,随奖励改造批重审)。分支 2(attack_up 行合法性,b4_attackup_row 根):safe_cell 含 attack_up 引用行 → 加载**通过**、roll_board/grant 正常受理(授予 buff 行 = {category=buff, effect_id=attack_up} 快照——消费于主角战斗段先手攻击,B1 域 OOS;B2 AC-14 的 atk 域断言跨引)。 +- **AC-19(F6 当量供给面与 A4 硬约束① 验收锚对账——夹具对账型)** GIVEN 默认根加载(或调参后根——对账随内容态重跑)。WHEN `equivalence_supply()`,THEN 返回全活行投影(F6 扩面)与 tier_min_buff = min{活 buff 行 tier}(默认根 = defense_up_basic 行 t=2——登记对账常数;行集空 → 加载致命,AC-17 `no_live_buff_row` 已载)。分支 2(**A4① 锚对账——档位序检查**):fog 池最低档活行 tier(min{tier : weight>0 ∧ tier ≥ T_fog_min},均表读值推导)→ **≥ tier_min_buff**(约束①"雾内奖励档位下界 ≥ 最低档安全格 buff 当量"的 B4 侧可判定形式 = 档位序检查——**真当量换算归 B1 条款①对照表〔未设计〕,落定后本分支升级为当量对账〔缺口预检 2〕**;当前内容态不等式成立为登记对账记录,C3 调参后须重跑——登记对账对 **(knife_adv, defense_up_basic)** 漂移检测;**锚参照对象回迁 attack_up 当量(2026-09-11 用户裁决 attack_up 复活——2026-09-09 迁 defense_up 的注记退役;attack_up 入池时 tier_min_buff 重算、对账对随批重制)**,**两行互异 = 非平凡判别**〔默认根布置,2026-09-09 评审补——原"档位序代理"在金夹具下自指恒真、判别力零〕;tier 与当量零耦合的假阳性边界见 F2 注记)。A4 约束②③ = MVP 结构性不可闭合/静默绿灯风险——Dependencies 显式宣告 + B1 检查点——OOS。 + +**registry 一致性与静态扫描(Rule 10)** + +- **AC-20(registry 一致——接口漂移回写义务的验收;B2 AC-16 同型无条件断言)** GIVEN ConfigDB 默认根加载完成。WHEN 脚本解析 `design/registry/entities.yaml`,THEN:① `rng_service.scope_event_binding` 的 reward_roll 语义 = **"每次 board install(格奖励批 + 清图奖励批)"**(Rule 2 语义注记的回写落点——AC-1 scope 观测面的登记侧);② `stream_seed_derivation.golden_vectors` 含 reward_roll m=42 scope0 向量(derive_at 测试通道的登记前提);③ `variation_table.pricing_constraints` 注记存在(A4 三约束为 B4 验收项的登记);④ `status_effect.dangling_ref_policy` 注记含 "B4 RewardTable 首个 effect_id 引用" 条款(AC-17 条件性夹具激活条件的登记侧)+ `status_effect.anchor_warning` / `clause3_supply.direction_precheck` 含 **attack_up 参照回迁注记**(2026-09-11 用户裁决 attack_up 复活——原 defense_up 迁移注记退役为历史记录,registry 同批回写);⑤ `reward_table` 条目 fields **九键**(reward_id/category/effect_id/**display_name**/power/weight/pool_tag/tier/use_timing)与 ConfigDB RewardTable schema 字段名逐一相等、MVP 内容注记与默认根行集一致(与 AC-18 对账);⑥ **`reward_config` 条目存在**,fields 四键(t_fog_min/k_dmg/n_draft/p_min)与 ConfigDB RewardConfigTable schema 字段名逐一相等(2026-09-09 评审——三个标量 knob 的 Rule 1 载体)。**生效前提** = 回填完成(不写"若存在"软口径——条件化使一致性检查落空,B2 AC-16 同款)。**边界注记**:本 AC 断言 registry 登记面;**D2 GDD 正文(Core Rule 5 / Interactions B4 行)与 registry 的对齐 = delta 复核对账项**(缺口预检 4——GDD 散文不进自动化断言面,2026-09-09 评审批已补齐 D2 正文两处)。 +- **AC-21(静态扫描——Rule 10;D2 AC-9/AC-10 模式引用)** B4 文件集(映射容器/库存容器/RewardTable schema/roll 与保底逻辑/冒烟脚本/夹具规格)**纳入 D2 AC-9 模式项目级扫描域成员清单**(owner Technical Setup——B3 先例;本条分支 1 承载登记义务,清单更新 = Technical Setup 门禁项,缺口预检 7):`randomize()` / `RandomNumberGenerator` / 自建 RNG 零命中(**零随机出 roll_board 一线**——唯一 RNG 入口 = open_stream("reward_roll"));ConfigDB 外 data 表加载零命中(schema 直引实例化豁免——AC-16 前提);`extends Control` / `.tscn` preload 零命中(Core 层零 UI 债——headless 实跑 AC-1 合成判定);**A2 事件订阅 API 零命中**(Rule 10 零主动订阅);**B2/B3 API 符号零引用**(相邻零运行时依赖——含冒烟脚本;StatusEffectTable/HeroTable 经 ConfigDB 表读合法、API 引用非法);重掷类命名扫描(reroll/resign/rebind 等——Rule 8 负空间,warning 级 + 人工复核,D2 AC-14 纪律同款)。零随机的行为级硬证据 = AC-9。 + +**覆盖矩阵(qa-lead 交付,验收评审用)** + +- Rule 1→AC-16/AC-18 · Rule 2→AC-1/AC-9/AC-10(恰一次编排→OOS B1×⑥)· Rule 3→AC-12(施加与满层零兑付协议→OOS B1×②+B3 跨引)· Rule 4→AC-13 · Rule 5→AC-14(段序/窗口时机/满血守卫/零效果判定→OOS B1×①④;窗口呈现→D1×②)· Rule 6→AC-10/AC-11/AC-16(clear_\ 拒绝)/AC-17 · Rule 7→AC-12 分支 1(道具类路由)/AC-14 分支 3(满血守卫→OOS B1×④;clamp→E12 OOS)· Rule 8→AC-2/AC-15(未开格呈现→OQ2/A5+D1)· Rule 9→AC-16/AC-17/AC-18(接口漂移回写→AC-20①⑤)· Rule 10→AC-9/AC-21(调用权集中→OOS B1×①–⑥) +- F1→AC-1/AC-5(+AC-9 轨迹)· F2→AC-6(+AC-19 锚侧)· F3→AC-10/AC-11/AC-17 · F4→AC-14(恒定表值供给面)+AC-18(attack_up 行合法性 = 复活口径,2026-09-11)——施加/clamp/守卫/不吃修正纪律→OOS B1×④ · F5→AC-7/AC-8/**AC-17(加载护栏 ×2)** · F6→AC-19(闭合归 B1——OOS) +- E1→AC-4 · E2→AC-3 分支 2+OOS B1×⑤(零明细行半)· E3→AC-12(受理序半)+OOS B1×②(行协议 + 聚合计数半)+B3 AC 跨引(apply false 原子)· E4→AC-3 分支 1/2 · E5→AC-1 分支 3 · E6→AC-9 分支 2(roll 半)+AC-13 分支 2(库存半)· E7→AC-3 分支 3/4 · E8→前言 API 负空间(无强制使用入口——不可 G-W-T,B3 Rule 8 先例)+OOS D1×② · E9→AC-14 分支 2 · **E10〔照常消耗拍板〕→AC-14 分支 3(B4 半)+OOS B1×④(零效果判定+"已消耗未生效"行+击杀结果流水)**· E11〔清图 buff 拍板〕→AC-11(B4 半)+OOS B1×② · E12→OOS B1×④(clamp)+AC-14 分支 1(power 供给)· E13→AC-17 分支 6(条件性夹具)· E14→AC-15 · E15→OOS B1×①(授予先于判定的段序半)+AC-10 分支 2/AC-13 分支 3(可构造序弱承载注记)+OOS D1×③⑤(消亡语境注记)· E16→AC-18 分支 2(attack_up 行合法——2026-09-11 复活口径) · E17→AC-13 分支 1/2 · **E18〔评审新增〕→AC-12 分支 3/AC-10 分支 3** · **E19〔评审新增〕→前言 API 面(清单外 null/false + 诊断)** · **E20〔评审新增〕→前言 API 面(stock 受理域封闭)** · **E21〔评审新增〕→前言 API 面(枚举序 = stock 受理序——D1 呈现序契约)** +- 上游承接:A4①→AC-19 分支 2(夹具对账型,升级缺口预检 2)· A4②③→MVP 休眠显式宣告(Dependencies)+ OOS B1 强制检查点 · A3 条款④恒非空→AC-10 分支 1(兑付面)+AC-17 分支 4(表侧)· B3 dangling_ref 首引激活→AC-17 分支 6+AC-20④ · 奖励身份预读→AC-2/AC-15(呈现→OOS D1×①)· ~~attack_up 死行不入参考池~~(退役——attack_up 复活,AC-18 分支 2 改承载其合法性)· A3 条款⑤支配性→OOS B1 强制检查点(预警⑤⑧观测) +- **覆盖口径(2026-09-09 评审重述——原"无洞"声明撤回)**:21 条 EC 中 E8/E15 两处非 G-W-T 形态,分别以 API 负空间与 B1×① 段序 deferral 承载(B2 EC5/EC13/EC14 豁免先例同款);其余全部映射 AC 或显式 OOS(deferral 生效条件 = B1×6/D1×5 义务清单登记)。**"负空间"学说的边界如实登记**:封闭枚举防住"没有的入口",不自动防住"有入口、语义未裁"——E18–E21(重复受理族/清单外坐标/stock 域/枚举序)为评审批补裁的输入域语义;今后新增 API 入口须随附全输入域语义表(实现期 ADR 检查项)。 + +**缺口预检(qa-lead;lean 定稿 9 项 + full 评审 2 项 = 11 项——每项标 owner 与时机)** + +1. **【阻塞级】B1 编排半整体 deferral**:段序骨架(授予>窗口>增益/减益>道具>战斗>衰减>判定——窗口锚点 = 授予段后)、窗口选择序回放输入、满血守卫(拒绝不烧包)+ 同窗顺序多包逐件动态守卫、零效果判定与"已消耗未生效"行、击杀结果进流水、"奖励未生效"行 + 聚合计数、治疗 clamp、clear_inventory/retire/roll_board 恰一次编排——B4 冒烟全部以构造序扮演、编排正确性零断言;E10 拍板的 B1 侧承接(读 B2 存活集做零效果判定)尤须逐字核对。owner:B1 GDD(义务清单 6 项,缺项即 blocking);时机:B1 门禁评审。 +2. **【阻塞级】A4① 当量升级与②③状态**:AC-19 分支 2 为档位序检查——真当量换算依赖 B1 条款①对照表(未设计);**②在 MVP 结构性不可闭合(Dependencies 显式宣告——非常量池无法随雾负担上浮)、③静默绿灯风险**;激活载体 = clear_\ 插槽(垂直切片)。owner:B1;时机:B1 GDD 强制检查点 + 对照表落定/每次调参时核对(F2 验收锚口径)+ clear_\ 激活时重启②。 +3. **T_fog_min 推导式悬置(OQ5)**:表值默认 2 provisional——B1 对照表落定后改推导式或维持表值;若改动,AC-6 夹具(b4_fog_gate tier 布置与 override 分支)及登记轨迹重制。owner:B1;时机:OQ5 关闭。 +4. **registry/D2/索引/game-concept 回写**(2026-09-09 评审批已兑现):reward_roll scope 语义改写(registry + **D2 正文两处补同步**——AC-20① 生效前提)、锚参照迁移三处传播(systems-index 注记 / registry anchor_warning / clause3_supply.direction_precheck)、game-concept 三处 MVP 口径注记、systems-index 分层行 B4←D2 + 门禁块同步;**delta 复核时逐处对账**。owner:本 GDD 评审批;时机:已办,delta 复核确认。 +5. **attack_up 复活连锁(OQ1)——已兑现(2026-09-11 用户裁决批)**:基础攻击恒存在(HeroTable.atk 域 [1,999]、0 加载拒绝)→ attack_up 复活,消费者 = 主角战斗段先手攻击(B1 GDD 同批修订);AC-18 改承载 attack_up 行合法性、AC-19 锚参照回迁 attack_up 当量、registry anchor_warning/direction_precheck 同批回写;残余 = attack_up 是否入默认根池(随奖励改造批,C3)。 +6. **登记常数随调参重制流程**:b4_golden 冻结权重与默认根解耦,但 T_fog_min/K_dmg/N_draft/P_min/默认根行值调参 → AC-19 对账(对账对 (knife_adv, defense_up_basic) 漂移检测)、AC-6 分支 4、AC-18 结构断言须重跑;C3 调参流程若无"登记常数与对账重制"步骤则相关 AC 假失败。owner:C3(调参)+ qa(重制);时机:每次调参。 +7. **B4 扫描域成员清单登记**:AC-21 分支 1 的生效载体 = D2 AC-9 项目级 CI 扫描域成员清单更新(B3 先例)。owner:Technical Setup;时机:B4 GDScript 产物落地即登记。 +8. **参考算术工具锚定**:期望映射 Ψ(σ)/Φ(σ)/重 roll 轨迹的手工推导需"播种生成器取号序列"参考算术——D2 仅登记 scope 种子向量(未登记逐号序列黄金向量);参考算术以冒烟脚本内置、版本库登记,正确性以 D2 AC-3 种子向量 + AC-5 区间算术手工核验锚定;生成器取号约定(如 `randi % W` vs `randi_range`)为实现期 ADR 事实,定名时同步登记;**登记后第二人独立推导复核(A2 OQ3 同型)+ b4_reroll 可达性拆分复核(首稿零刀可达/耗尽稀有——失效即重制夹具,2026-09-09 评审补)**。owner:qa/Technical Setup;时机:冒烟框架搭建 + 实现期 ADR。 +9. **D1 呈现义务 deferral**:预读呈现(含库存常读条)/使用窗口(含 usable_now 呈现与触发政策)/四类明细行呈现/H2 强调/清空反馈不可 G-W-T——deferral 生效条件 = D1 义务①–⑤登记(缺项即 blocking)。owner:D1;时机:D1 GDD。 +10. **【新增】C3 调参期批量仿真**:F5 概率性致命带(合法权重下 N_draft 耗尽概率随 K/池构成可达非平凡值——种子轮盘)→ 调参流程须含批量仿真(刀供给分布 × A1 板分布的可行率),把配置错误暴露在调参台;与预警①–⑧预注册合并执行候选。owner:C3;时机:每次调参 + playtest。 +11. **【新增】方向预检输入缺口**:刀供给方向预检的 |资格格| 分布待 A1 actuals p10/p50 → 落地后重跑(Formulas 节登记对账记录);max_hp=50 原型锚出处建议随 B2/D2 回写补登。owner:C3/B2;时机:A1 actuals 交付。 + +## Open Questions + +1. **attack_up 入池裁决(原"死行复活条件"——2026-09-11 已复活闭合,残余入池问题)**(owner:C3):基础攻击恒存在(2026-09-11 用户裁决,B2 Rule 9)→ attack_up 复活、消费面 = 主角战斗段先手攻击;attack_up 行是否进入默认根 RewardTable 池随奖励改造批裁决(复活批不擅自入池);A3 条款 2 锚参照对象已回迁 attack_up 当量(F4 口径后果)。 +2. **未开格身份呈现深度**(owner:A5+D1,垂直切片)——MVP 不呈现(Rule 8);A5 探测道具零成本桥接(身份 install 期已定可查);呈现深度(类别/档位/身份)待 A5 定稿。 +3. **items_per_settlement_cap 启用裁决**(owner:C3 playtest)——Tuning Knobs 结构位默认关;启用即限制(过低锁死 3 单位 54 击杀需求胜局——economy-designer 警告)。 +4. **关卡末剩余道具折算(2026-09-11 关内延续裁决后重述)**(owner:C2/D5,Alpha)——"战斗结束库存清空"的体验负债**主体闭合**(库存关卡域:跨战斗延续、未选用件不再随战斗消亡;层间延续可见反馈已入切片);残余 = **关卡末**剩余道具折算/继承结构位(转金币/继承——Alpha,C2;清空前快照计数已作为折算数据面预留)。 +5. **T_fog_min 推导式**(owner:B1)——依赖条款①对照表(未设计,加载期循环依赖不可行);落定后改推导式或维持表值(F2,默认 2 provisional)。 +6. **weaken 锚裁决联动**(owner:B1)——B3 OQ1 归一口径裁决前置(瞬时 vs 总暴露);若落"总暴露"读法则 B4 重定最低档参照(registry anchor_warning;F6 供给已备行值清单与量纲建议)。 +7. **失衡预警预注册归口**(owner:C3/playtest 会话)——预警①–⑧ 阈值与样本量 provisional;与 A3 OQ3、原型 v3 终验 playtest 合并执行候选。 diff --git a/design/gdd/legacy-2026-09-13/c3-difficulty-curve.md b/design/gdd/legacy-2026-09-13/c3-difficulty-curve.md new file mode 100644 index 00000000..3be18511 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/c3-difficulty-curve.md @@ -0,0 +1,284 @@ +# C3 难度曲线与精英/Boss(Difficulty Curve & Elite/Boss) + +> **Status**: In Design +> **Author**: DK + design-system(solo 模式——专员/总监代理跳过,出稿后人工复核) +> **Last Updated**: 2026-09-12 +> **Implements Pillar**: P4 地牢下探的探险氛围(主);P1 推理的掌控感 / P3 风险与收益同源(难度即压力源) + +## Overview + +C3 难度曲线与精英/Boss 是全部进度维度数值的唯一所有者:它定义"难度 = f(关卡序号, 节点深度, 精英/Boss 修饰)"中每一项随进度如何变化——**敌人强度曲线**(EnemyTable 的 hp/atk/unit_count/行为环投放节奏)、**雷图压力曲线**(BoardGenTable 的尺寸/密度/可推理率目标/雾参数)、**奖励供给曲线**(RewardTable 权重与威力、RewardConfigTable 参数、可选类供给控制器参数),以及**精英/Boss 图的修饰语义**(Boss = 各关最大深度 Dmax 行——A1 登记约定;精英/Boss 全局变异走 A4 `scope=global` 枚举位;`elite_`/`boss_` 命名空间预留)。 + +C3 是**数值 owner 而非机制 owner**:表结构归 D2、生成算法归 A1、结算规则归 B1、敌种机制归 B2——C3 通过行值与参数驱动这些已定稿的机制,形成"越下越险"的进度体感(P4)。玩家不直接感知本系统(Indirect):他们感受到的是第二层的伏击兽比第一层的蛆虫更凶、关底 Boss 的压迫性血量、"这一层开始有雾了"的解锁节奏——本系统是这些体感背后的统一坐标系。 + +除曲线数值外,C3 的核心交付物包括:**难度期望表**(按 (L,D) 的期望胜轮带 / 期望承伤占 max_hp 比 / 通关率目标带——调参的"正确行为"基准,本项目缺失数值目标的正式落点);**调参期批量仿真流程**(消费切片已建的工具原型 `level_monte_carlo.gd` / `reward_expectation.gd`——B4 登记的调参期仿真义务载体);以及 **B1 对账约束清单的承接与消解**(条款③满配破线两轴、条款⑤清图支配性、快杀退化预警⑥——这些是"禁止独立调参"纪律的量化边界,C3 调参必须在其内工作或显式扩界)。 + +## Player Fantasy + +**Indirect 体验——玩家不操作本系统,但每一分钟都在感受它。** + +`creative-director` not consulted — Solo mode. Review manually before production. + +本系统服务两个情绪层: + +1. **越下越险的压迫节奏(P4 主承载)**:地牢下探的每一层都是一个可感知的难度台阶——新敌种登场("这长腿的东西是什么")、新变异解锁("这一层开始有雾了")、关底 Boss 的血量压迫。参照 Peglin 的地图难度爬升与杀戮尖塔的 Act 结构(普通→精英→Boss 的三段节奏)。台阶必须**可感知但可解释**:玩家应能把难度跃升归因于可见的变化(新敌人/新变异/更大的板),而非纯粹的数字膨胀——后者产生"被坑"感而非"变险"感。 + +2. **攻克关卡的征服感(P1 的兑现侧)**:难度台阶的意义在于被跨越。首关基线应"明显吃紧但多数玩家能过"(通关率目标带见 Formulas F5),后续关卡陡升使天赋提升(C4 元进度)产生可测量的通关期望改善——**"我变强了"必须有"世界也变难了"作对照物才成立**,本系统就是那个对照物。 + +**锚点时刻**:① 第二层第一次遭遇伏击兽——三拍行为环 vs 蛆虫的单拍攻击,难度跃升有具体面孔;② Boss 房开门前——恢复点/囤刀决策压力的顶点(供给稀缺性在此处兑现价值);③ 击杀 Boss——"这层我算下来了"的完成感。 + +## Detailed Design + +### Core Rules + +Specialist agents not consulted — Solo mode. Review manually before production. + +1. **难度承载结构 = 直填行值主体 + DifficultyTable 修饰结构位(MVP 空表)**:全部 (level_index, node_depth) 维度的最终数值**直填**于各基表行(EnemyTable/BoardGenTable/RewardTable——切片已验证的工作方式,R1 floor 解析直读、零新机制);**DifficultyTable 仅承载"按坐标施加的全局修饰"结构位**(键 (L,D)、乘数域,施加点 = B2.spawn 实例化时套乘于行值快照之前)——MVP 不产生任何修饰行(空表结构位,首个使用场景 = 精英节点;Boss 走 Dmax 直填行,不走修饰表)。B2 Rule 10"C3 DifficultyTable 接口未定"的兑现 = 本条(走 D2 接口漂移规则建表)。 + +2. **难度 = f(L, D) 双轴语义**:**L 维(关间)= 大台阶**——每关至少一项新内容投放(新敌种 或 新行为环段 或 新变异解锁,B2 OQ8 投放节奏规则)+ 基准数值跳升;**D 维(关内)= 小坡度**——同敌种数值平缓上移(坡度域见 Formulas F1),板参数温和加压(尺寸/密度小步上调,推理率 target 不低于 0.85 绝对锚)。**Boss = 各关 Dmax 行**(A1 登记约定兑现):数值大台阶 + 专属行为环 + 专属板形,见 Core Rule 5。 + +3. **敌种投放节奏(B2 OQ8 兑现)**:每关至少一个新敌种或既有敌种的新环段投放;MVP 投放表:L1 = grub(D0–D1 单拍环)→ 伏击兽(D2–D3 三拍环)→ 遗迹守卫 boss_guardian(D4,专属四拍环 [attack,attack,charge,heavy]);L2 = ambusher 主力 + 雾解锁(A4 unlock_level=2 锁定)。多敌种同深度选择规则(B2 OQ7 共裁项):MVP 每行集恰一敌种(B2 锁定),C1 分支地图定稿时若需同深度多敌种选择,走 B2 OQ6/7 预登记的改键通道——本 GDD 预留不定义。 + +4. **切片行集转正为 MVP 默认曲线**:垂直切片调参登记(SLICE-TUNED)的行集有实测依据(期望扫描 N=15–30 × 多种子 + 8 种子密度实验),转正为 MVP 默认值并移除 provisional 标记的责任随本 GDD——**EnemyTable L1 深度轴**(grub 24hp/4atk ×1→群 24×2、伏击兽 36hp/6atk ×1→群 26×2、遗迹守卫 130hp/7atk)**BoardGenTable L1 深度轴**(D0/D1 10×10、D2–D4 12×12,d0.25/t0.88,attempts 24)**供给参数**(active_p0=0.4/active_decay=0.5)**HeroTable**(atk=6/max_hp=50 原型锚转正)。后续 L2+ 曲线数值随关卡内容扩展填充(结构本节已定,数值行 OQ1)。 + +5. **Boss 遭遇语义**:① Boss = 关卡末节点 = Dmax 行(LevelTable 末节点必须 Boss——LevelTable schema 校验);② Boss 数值台阶 = hp 取本关普通战斗 hp 总和的 0.35–0.55 倍(Formulas F3)+ atk 高于本关均值一档 + 专属行为环(含 charge 拍——玩家可规划的安全挖掘拍);③ Boss 板 = 本关最大板形 + 密度上探(受可推理率 target 绝对锚约束);④ Boss 战胜利 = 关卡通关(C1/C2 域编排)。 + +6. **精英节点(MVP 不实现,语义预留)**:`elite_`/`boss_` 前缀字段命名空间(A1 B7 修订预留)保留;精英修饰的完整语义(独立节点类型/词缀系统/出现规则)**归 C1 关卡节点地图定稿后的联动设计**——分支地图中"同深度并存精英与普通节点"触发 A1 OQ6 强制重审(改键走 D2 Rule 4)。本 GDD 承诺:DifficultyTable 结构位 + 命名空间预留足以承载,不预写语义。 + +7. **A4 全局变异语义(scope=global 兑现)**:`scope=global` 行 = 该变异**施加于整板全部合格格**(区别于 region 的区域布置);语义归本 GDD 定稿:**全局变异仅 Boss/精英图可产生**(普通图禁用——加载期校验:global 变异的布置行须伴随 elite/boss 标记,MVP 无标记字段前 = MVP 不产生 global 行,A4 既有 warning 承载);MVP 不定义任何具体全局变异行(岩浆/冰冻无 MVP 容器——A4 锁定),本条只锁定语义框架与禁用面。 + +8. **遭遇随机化(2026-09-12 用户拍板——Roguelike 局感引入批;Peglin 对比报告结论 2 的本切片落地项)**:EnemyTable 行的 `unit_count` 升级为**区间语义** `[unit_count, unit_count_max]`(新字段 `unit_count_max` 可省略——缺省 = 定值,旧行零迁移;域 [unit_count, 3],B2 unit_count 上界锁定不变)。**掷骰规则**:B1.begin_battle 实例化期(扫雷开始前)按区间均匀骰决定本战实际数量——**同敌种,不触碰 B2"每行集恰一敌种"锁定**;随机经 RngService **新增 `encounter` 封闭流**(scope_event_binding = 每次战斗实例化恰一 scope;发生在挖掘决策域之外,P1 合规)——B1 掷骰、B2.spawn 以 override 参数接收实际数量(**B2 零随机纪律保持**——收到的即确定值)。同种子全程可回放(流取号确定性不变)。**区间设计原则**:数量是比单体质变更优先的深度表达(F1 Example 延伸);期望总量对齐 F6 平衡带检算;"新敌种首遇节点固定数量"(教学位不加骰——D2 熊地精单挑)。 + +9. **调参约束内工作纪律(B1 对账输出的承接)**:C3 的每次行值修订必须通过 Formulas F7 调参流程,且不得触碰以下已锁约束边界——① **条款③满配破线**:unit_count>1 的行须满足"降密度或降 m_amb"补偿(registry clause3_supply 已登记,切片群战行 hp/atk 下调为合规先例);② **条款⑤清图支配性**:C 量级暂不上调(46.1/91.8 需求 vs clear_blast 40 现值——登记为非支配设计态 + OQ3 playtest 观测弃图率,见 Edge Cases);③ **可推理率绝对锚**:target ∈ [0.85, 1] 不可下探(P1 生成端保证);④ **期望表对照**:调参后 F5 期望表必须复算并在 README 调参日志登记偏离。 + +### States and Transitions + +C3 无运行时状态机(数值系统)。设计期状态: + +| 状态 | 含义 | 转移 | +|---|---|---| +| 未调参行 | 新增 (L,D) 行仅有结构占位 | 经 F7 流程 → 已调参 | +| 已调参行 | 行值经批量仿真 + 期望表对照 + 对账复算 | 任何修订 → 重走 F7 | +| SLICE-TUNED 转正 | 切片临时值经本 GDD 背书 | → 已调参(Core Rule 4 一次性完成) | +| 期望表偏离 | 调参后期望值越出目标带 | → 回滚或 OQ 登记(禁止静默出带) | + +### Interactions with Other Systems + +| 系统 | 方向 | 接口 | +|---|---|---| +| D2 | 上游(硬) | 全部基表 schema 与 R1 解析归 D2;C3 产出 = 表行值(经 ConfigDB 单点读取);DifficultyTable 新表走 D2 接口漂移规则;**RngService 增 `encounter` 流**(2026-09-12 用户拍板——registry rng_service 条目接口漂移登记,流集合封闭更新) | +| B2 | 上游(硬) | EnemyTable/HeroTable 行值由 C3 填(B2 拥有字段语义与校验域);修饰施加点 = B2.spawn(DifficultyTable 消费面);投放节奏共担(OQ8) | +| B1 | 上游(soft,事实依赖) | 条款③⑤对账输出 = 调参约束输入(B1 F4/F5 = 约束生成器);P_perfect 乘数调参权归 C3;对账复算义务随每次修订 | +| B4 | 上游(soft) | RewardTable/RewardConfigTable 数值归 C3;刀供给方向预检 = 联动重算义务(敌 hp 曲线与奖励供给禁止独立调参——registry 已登记) | +| A1 | 上游(类型依赖) | BoardGenTable 行值归 C3;**禁反向调用生成**(仅经 D2/编排器传值);F4 批量红线(严格零降级)为调参闸;d0.28 回退先例 = 密度×target 联动调参的实证 | +| A4 | 上游(soft) | scope=global 语义由本 GDD 定稿(Core Rule 7);unlock_level 门控序列(普通→雾→岩浆/冰冻)的投放计划归 C3(随关卡扩展) | +| A3 | 上游(soft) | ActionEconomyTable 联动 knob(ap_per_round 兼 RAP 转存规模);跨字段校验约束内调参 | +| C1/C2 | 下游 | 消费难度模型(节点深度语义/关卡序列的难度排布);精英节点形态联动(Core Rule 6);未设计——本 GDD 的 Boss=Dmax 约定即为其预留的接口 | +| C4 | 下游(远) | 元进度天赋数值的"世界侧对照物"——通关率目标带是 C4 天赋强度的标定基准 | + + +## Formulas + +`systems-designer` not consulted — Solo mode. Review manually before production. + +**定位声明**:C3 是数据系统——**运行时零新增公式**(数值直填基表行,运行时仅经既有 B1 F2 乘区/B2 接口运作)。本节全部公式为**设计期工具式**:曲线拟合基准、期望估算、对账复算——它们的执行载体 = 调参期批量工具(level_monte_carlo.gd / reward_expectation.gd 扩展,见 F7),不进入游戏运行时。全部数值 provisional 起步,随 playtest 修正。 + +### F1 关内深度坡度(D 轴小坡度) + +The depth_scaling formula is defined as: + +`hp_D = round(hp_0 × (1 + s_hp × D))` + +**Variables:** +| Variable | Symbol | Type | Range | Description | +|----------|--------|------|-------|-------------| +| 深度基准值 | hp_0 | int | [1,999] | 该敌种 D0 行 hp | +| 坡度系数 | s_hp | float | [0.05, 0.20] | 每深度档增幅(MVP 直填行近似此式,不运行时计算) | +| 节点深度 | D | int | [0, Dmax] | 节点深度(Boss = Dmax) | + +**Output Range:** 每档 +5%–20%,Dmax 处 1.25–1.8 × D0(同敌种内);敌种切换(新敌种登场)不受本式约束——台阶值直接定。 +**Example:** grub 24hp:D1 理论 24×1.1 ≈ 26——切片实际以"群×2"(24×2=48 总量)表达深度压力,即**数量是比单体质变更优先的深度表达**(保持单拍环的低威胁读数、总量翻倍)。 + +### F2 关间台阶(L 轴大台阶) + +`base(L+1) ≈ base(L).Dmax × k_L`,`k_L ∈ [1.3, 1.6]` + +**Variables:** +| Variable | Symbol | Type | Range | Description | +|----------|--------|------|-------|-------------| +| 关间系数 | k_L | float | [1.3, 1.6] | 下一关 D0 基准 = 本关 Dmax × k_L(数值跳升 + 新敌种/新变异共同构成台阶感) | + +**Output Range:** 相邻关数值倍率 1.3–1.6;叠加新敌种环复杂度,实际体感台阶更大。 +**Example:** L1 Dmax 遗迹守卫 130hp → L2 D0 ambusher 基准 ≈ 130×1.4 ≈ 182?——**否**:k_L 作用于**同敌种基准**;新敌种登场时基准按其环复杂度重定(三拍环单位 hp 低于单拍环同关值——切片先例:伏击兽 36 < 群 48 总量但威胁更高)。k_L 适用于**敌种延续**场景(如 L2 ambusher 延续至 L3)。 + +### F3 Boss 血量锚 + +`boss_hp ≈ round(Σ普通战斗hp × r_boss)`,`r_boss ∈ [0.5, 1.1]`(**2026-09-12 试玩批次 1 修订**——原 [0.35,0.55];用户试玩裁决"Boss 血量 OK 或可上调"+ 主角必须会掉血的成就感诉求,偏重承诺保留;L1 首关偏上沿,后续关随玩家成长回落) + +**Variables:** +| Variable | Symbol | Type | Range | Description | +|----------|--------|------|-------|-------------| +| 本关普通战斗 hp 总和 | Σ普通战斗hp | int | [1,999]×N | 本关全部非 Boss 节点的敌方 hp 合计(含群战总量) | +| Boss 占比 | r_boss | float | [0.35, 0.55] | Boss 相对全关消耗的血量锚 | + +**Output Range:** Boss hp = 全关消耗的 35%–55%——"Boss 是本关最大的单场消耗"。 +**Example:** L1 v3:Σ = 24 + 26 + 40 + 52 = 142 → Boss 150/142 ≈ 1.06(上沿内 ✓——v3 数值批对齐修订后带)。历史:v2 130/160 ≈ 0.81 曾越旧带上界(OQ2 试玩裁决"OK 或可上调"后 F3 带修订,越带争议消解)。 + +### F4 期望承伤估算(设计期) + +`E[承伤/场] ≈ Σ_rounds Σ_enemies E[beat_dmg] × (1 − E[减伤])` + +**Variables:** +| Variable | Symbol | Type | Range | Description | +|----------|--------|------|-------|-------------| +| 每拍期望伤害 | E[beat_dmg] | float | [0,999] | 敌 atk × 拍别乘数 × 出现频率(行为环确定性轮转——纯算术) | +| 减伤期望 | E[减伤] | float | [0,1] | defense_up 授予率 × 当次减伤(切片实测供给 ~0.44/格 × 50%) | + +**Output Range:** 期望表"承伤占 max_hp 比"列的估算基础;对照目标带 ≤20%/普通场、≤50%/Boss 场。 +**Example:** grub(环 [attack],atk4)每结算恒 4 × (1−0.22 平均减伤) ≈ 3.1/轮 → 2 轮战斗 ≈ 6.2 = max_hp 的 12%——带内 ✓(切片实测 2.1–2.7/轮与此一致)。 + +### F5 通关率目标带(期望表核心) + +**Variables:** +| 目标带 | 基线(无天赋) | 中天赋(+100% atk) | 高天赋(+200%) | +|---|---|---|---| +| L1(首关) | **40–60%** | ≥95% | 100% | +| L2 | **20–40%** | 60–80% | ≥90% | +| L3+ | 10–25% | 40–60% | 60–80% | + +**Output Range:** 通关率 = level_monte_carlo 扫描输出(理性策略近似);**真人修正系数待 playtest 标定**(模拟者无避雷推理、无情绪化决策——真人基线通关率预期显著低于模拟值,OQ3 首要标定项)。 +**Example:** 切片实测基线 55–80%(N=15–30)——L1 带内偏上沿;供给控制器批后的 buff 频率副作用(44%→59%)是带内上移的已登记原因。 + +### F6 供给需求平衡式(B4 方向预检正式化) + +`Σ刀伤供给 + Σ即时伤害 + atk × E[轮数] ≥ Σ敌hp × (1 + 缓冲)` + +**Variables:** +| Variable | Symbol | Type | Range | Description | +|----------|--------|------|-------|-------------| +| 供给合计 | — | float | — | 每关可获取伤害总量(供给控制器 E[获取] × 刀力加权 + 清图 clear_blast + 基础攻击×轮数) | +| 缓冲 | 缓冲 | float | [1.1, 1.4] | 非完美执行余量(漏挖/错用/踩雷税) | + +**Output Range:** 比值 < 1.1 = 供给短缺(打不死);> 2.0 = 供给过剩(速杀退化预警⑥)。带内 [1.1, 2.0]。 +**Example:** L1 v3+遭遇区间(2026-09-12):Σ敌hp 期望 ≈ 36(D0 [1,2]×24)+ 32.5(D1 [2,3]×13)+ 40(D2)+ 45(D3 [2,3]×18)+ 150(Boss)≈ 304 × 1.1 = 334 vs 供给 ≈ 180——比值 ≈ 0.54,**短缺态**(基础攻击扛大梁:6×E[轮数 17.5] ≈ 105 是最大单一分量——这正是"基础攻击恒存在"的设计兑现:奖励是规划面增量,基础攻击是底盘)。 + +### F7 调参流程(每次修订强制走完) + +`改表值 → smoke_all 全绿 → level_monte_carlo 扫描(F5 对照)→ reward_expectation 供给对照(F6)→ 对账复算(条款③⑤/预警⑥)→ README 调参日志登记` + +**Variables:** +| Variable | Symbol | Type | Range | Description | +|----------|--------|------|-------|-------------| +| 批量样本量 | N | int | ≥256(正式)/ 15–30(切片临时) | A1 F4 排期倒挂的解除载体:正式批量工具 = level_monte_carlo 扩展(逐行聚合 + N 参数化 + inferability/降级统计并入 experiment_density 能力)——C3 期交付物,A1 F4 报告 C 的 actuals 消费方 | + +**Output Range:** 任一步 FAIL → 回滚或登记 OQ,禁止带病前进。 +**Example:** 切片 d0.28 回退先例:d0.28/t0.87 → best≈0.81 降级告警 → 回退 0.25/0.88——F7 第 2 步(A1 生成质量闸)拦截的成功案例。 + +## Edge Cases + +`systems-designer` not consulted — Solo mode. Review manually before production. + +- **If unit_count>1 行与高密度板组合**:条款③满配语境破线(B1 F4 双轴约束)——该组合**禁止入表**(调参约束内工作纪律 Core Rule 8①;切片先例 = 群战行 hp/atk 下调补偿)。对账复算在 F7 第 5 步强制执行。 + +- **If 关卡深度轴缺行(如 D3 缺失、D4 存在)**:R1 floor 解析静默兜底至低档(难度降而非崩)+ ConfigDB warning 四元组——但**加载期校验拦截本场景**:LevelTable 节点引用的 (L,D) 须在两表有精确行(切片跨表护栏⑧先例转正);缺行的根因是调参事故而非运行时事件。 + +- **If 请求越界关卡(level 999)**:R1 floor 解析至表尾行(难度封顶 = 尾行值)+ warning——冒险结构(C2)应在其内容覆盖范围内生成关卡序列;越界 = 内容生产缺口,非玩家可达态。 + +- **If 玩家天赋拉满(atk+300%)打最低难度行**:速杀退化(预警⑥——战斗 1–2 轮终局、奖励系统饿死自己的高光)。处置 = 预警⑥仪表监控(战斗结束未开资格格占比 >70% 警)+ 接受(低难度行被高天赋速杀是 Roguelike 成长兑现的合法形态,无需修复——目标带按"基线"定义,高天赋本就该碾压低关)。 + +- **If 密度上探压垮推理率(d0.28 案例)**:best inferability ≈0.81 < target 0.87 → 降级告警"接受最优出图"。处置 = **回退**(切片先例:0.28/0.87 → 0.25/0.88)——密度与 target 是联动对(A1 求解器能力的天花板内联动调参),禁止为难度牺牲 P1 度量达标;F7 第 2 步闸拦截。 + +- **If 条款⑤清图支配性反转(弃图率 >30%)**:玩家普遍发现"挖一半就弃图刷 RAP"优于"推到底清图"→ 清图奖励与清图体验被支配。处置 = OQ3 playtest 观测(自动模拟者不弃图,测不到真人策略);届时杠杆 = C 量级上调(B1 约束包①)或弃图成本重定价(A3 域)——**MVP 接受当前非支配设计态**(clear_blast 40 + 供给控制器首件保证已改善原判式,实测后再裁)。 + +- **If 投放节奏断档(某关无新敌种且无新环段)**:OQ8 违例——敌信息总量见底,学习曲线枯竭。处置 = F7 流程的投放检查项(每关至少一项新内容——Core Rule 3);违规 = 内容生产缺口,C3 调参无法修补(数值不是内容)。 + +- **If 全部行值调至域上限(hp 999 等)**:数值可行但可玩性崩塌。处置 = F5 目标带对照拦截(期望表全面越带 = 配置事故)+ 域校验既有护栏;本场景属调参事故而非玩家可达态。 + +- **If DifficultyTable 修饰行与直填行值叠加后越域**(首个精英节点上线时):B2.spawn 套乘后 hp >999 → **clamp 至域界 + warning**(fail-fast 不适用——修饰乘数是合法配置,越界是组合事故;域界 = 玩法硬顶)。MVP 空表无此场景,规则预登记。 + +## Dependencies + +| 系统 | 方向/硬度 | 接口与义务 | +|---|---|---| +| D2 | 上游(**硬**) | 基表 schema/R1 解析/ConfigDB 单点全归 D2;C3 产出 = 行值;DifficultyTable 新表走接口漂移(Core Rule 1);**禁绕过 ConfigDB 自建 load**(Rule 2) | +| B2 | 上游(**硬**) | EnemyTable/HeroTable 字段语义与校验域归 B2(值归 C3);修饰施加点 = B2.spawn(实例化时套乘、行值快照前);投放节奏 OQ8 共担;多敌种同深度选择规则共裁(OQ7——C1 联动) | +| B1 | 上游(soft,事实依赖) | 条款③⑤对账输出 = 调参约束输入(**C3 消费 B1 对账输出的事实依赖成立**——B1 GDD 2026-09-10 登记);P_perfect 乘数调参权归 C3;期望口径双读数纪律(锚行读数不得用于 unit_count=2 放行) | +| B4 | 上游(soft) | RewardTable/RewardConfigTable/供给控制器参数值归 C3;**敌 hp 曲线与奖励供给禁止独立调参**(registry 联动重算义务——F6 平衡式为联合检算工具);调参期批量仿真义务载体 | +| A1 | 上游(类型依赖) | BoardGenTable 行值归 C3(含 max_gen_attempts 策略);**禁反向调用生成**;F4 批量红线严格零降级 = 调参闸;黄金向量/solver_rules 冻结不经 C3 | +| A4 | 上游(soft) | scope=global 语义定稿(Core Rule 7);unlock_level 投放计划归 C3;变异机制框架归 A4(C3 只投内容行) | +| A3 | 上游(soft) | ActionEconomyTable 联动 knob(ap_per_round 双重身份——节奏兼 RAP 转存规模);跨字段校验(动作成本 ≤ ap_per_round)内调参 | +| B3 | 上游(soft) | StatusEffectTable 四行强度(attack_up +50 等)provisional 归 C3 联动评估(B1 免伤轮合法性注记——强度评估归 C3 对账联动) | +| C1 | 下游 | 节点深度语义/精英节点形态联动(Core Rule 6/OQ6 强制重审);未设计——Boss=Dmax 约定即预留接口 | +| C2 | 下游 | 关卡序列的难度排布消费本 GDD 期望表;未设计 | +| C4 | 下游(远) | 天赋强度的标定基准 = 通关率目标带(F5);未设计 | +| 呈现层(B5/D1) | 下游(soft) | Boss 登场仪式感/难度台阶可感知性(新敌种图像线索归 B2 意图推测型)——视觉义务归 B2/B5,C3 仅提供"何时变难"的数据 | + +**双向一致性**:systems-index 依赖表 C3 ← D2,B2 ✅(2026-09-10 B1 块增补"C3 消费 B1 对账输出的事实依赖成立"——本表扩展为正式登记)· B2"敌种 hp/atk/pattern 曲线数值、难度修饰 → C3" ✅ · B1"C3 = 条款③⑤约束输入方" ✅ · B4"权重/档位/威力曲线 + RewardConfigTable 参数归 C3" ✅ · A1"每行难度数值与曲线 → C3" ✅。 + +## Tuning Knobs + +C3 是全项目 knob 保有量最大的系统(数值 owner)。**全部值 provisional 起步;域与校验归各表 schema(D2);本表只登记所有权、联动与危险方向。** + +### C3 直辖 knob + +| Knob | 表 | 域 | 联动 | 过高 | 过低 | +|---|---|---|---|---|---| +| EnemyTable.hp/atk(逐行) | enemy.tres | [1,999]/[0,999] | **与 B4 供给禁独立调参**(F6 联合检算);cap×atk 禁独立(B2) | 速杀反面——打不死、战斗冗长 | 快杀退化预警⑥ | +| EnemyTable.unit_count / unit_count_max(逐行) | enemy.tres | [1,3] / [unit_count,3] | **>1 行须降密度或降 m_amb**(条款③);区间骰经 encounter 流(Core Rule 8) | 条款③破线 | 压力不足/遭遇零随机 | +| EnemyTable.pattern(逐行) | enemy.tres | MVP 三值组合 | OQ8 投放节奏(每关新环段) | 信息过载 | 学习曲线枯竭 | +| HeroTable.atk/max_hp | hero.tres | [1,999] | 全部供给式的分母锚(F6) | 奖励价值稀释 | 挫败感 | +| BoardGenTable 尺寸/密度/attempts(逐行) | board_gen.tres | 域见 schema | **密度×target 联动**(d0.28 回退先例);unit_count>1 行联动 | 推理率不达标降级 | 板退化(低密度先布后删) | +| BoardGenTable.inferability_target(逐行) | board_gen.tres | **[0.85,1] 绝对锚** | 与密度成反耦 | 生成耗时/降级 | P1 空心化 | +| BoardGenTable 雾参数 | board_gen.tres | f≤8d/≤0.25 | unlock_level=2 锁定 | P1 度量空心(绝对上限) | 雾内容缺位 | +| RewardTable.weight/power/tier(逐行) | reward.tres | 域见 schema | F6 供给式;攻击面稀释 | 即时类淹没明细/可选类贬值 | 窗口空转/F5 重 roll 频发 | +| RewardConfigTable.{t_fog_min,k_dmg,n_draft,p_min} | reward_config.tres | 域见 schema | 概率性致命带(批量仿真义务) | 保底永不触发 | 种子轮盘致命 | +| 供给控制器 active_p0/active_decay | reward_config.tres | [0,1]/(0,1] | E[获取]≈1 目标;**与敌 hp 联动** | 速杀+囤积回归 | 可选类绝育 | +| ActionEconomyTable.{ap_per_round,rap_refresh_cost,refresh_max_per_round} | action_economy.tres | 域见 schema | ap 兼 RAP 转存规模;跨字段校验闸 | 轮次冗长 | 决策密度坍缩 | +| SettlementTable.P_perfect(乘数) | settlement.tres | {100,200} | 完美排雷×清图支配性互 feed | 钓鱼线(护栏 B) | 高光缺位 | +| DifficultyTable 修饰乘数(MVP 空表) | difficulty.tres(待建) | [0.1,10](预登记) | 精英节点上线时启用 | 越域 clamp+warning | 无修饰 | + +### 联动网络(禁止独立调参清单) + +1. **敌 hp ↔ 奖励供给**(registry 已锁):单边移动必走 F6 联合检算。 +2. **unit_count ↔ 密度/m_amb**(条款③):群战行上线前的强制复算。 +3. **密度 ↔ target ↔ attempts**(A1):d0.28 回退先例的三元组。 +4. **ap_per_round ↔ RAP 体系 ↔ refresh 上限**(A3 + 轮内刷新批):跨字段校验闸承载。 +5. **m_w(weaken −65) ↔ 锚对照表**(B1 F0):终值共裁 B4/B1,调参走 C3。 + +## Visual/Audio Requirements + +C3 无自有视觉/音频资产(数值系统)。**义务转注**:① Boss 登场呈现(仪式感锚点——Player Fantasy ③)归 **B5 战斗表现/D1**(其设计输入 = Boss=Dmax 行的数据面本 GDD 提供);② 难度台阶可感知性归 **B2 意图推测型**(敌种图像线索稳定可学——新敌种 = 新图像线索 = 台阶的可视面孔);③ 变异解锁的"这一层有雾了"归 **A4/D1 图例与首遇提示**(已登记义务)。本节无新增资产需求。 + +## UI Requirements + +无自有 UI。**义务转注**:① 期望表/批量仿真输出 = 调参期开发工具(非游戏内 UI——headless 工具);② 敌人强度可读性("这只比上层的硬")归 B2 意图推测型 D1×4 义务(图像线索档位编码);③ 恢复点/奖励点呈现归 C1-lite 切片已实现的节点覆盖层(正式形态归 D1/C1)。无 D1 新增义务登记。 + +## Acceptance Criteria + +`qa-lead` not consulted — Solo mode. Review manually before production. + +- **AC-1** GIVEN LevelTable 任一 battle/boss 节点 (L,D),WHEN ConfigDB 加载全部表,THEN board_gen 与 enemy 两表存在该 (L,D) 精确行(跨表护栏⑧转正——floor 兜底静默降难度属调参事故)。 +- **AC-2** GIVEN MVP 全部 (L,D) 行,WHEN 逐行扫描 inferability,THEN 全部行 inferability ≥ target ≥ 0.85(P1 绝对锚)且 A1 断言族(退化板/R₀≠S)零违例——批量 N=256/行(F4 排期倒挂解除后的正式红线)。 +- **AC-3** GIVEN EnemyTable 全部 unit_count>1 行,WHEN 条款③复算(密度 × m_amb 组合),THEN 无破线组合存在(Core Rule 8①)。 +- **AC-4** GIVEN 供给控制器与敌 hp 曲线现值,WHEN F6 平衡式检算每关,THEN 比值 ∈ [1.1, 2.0](短缺/过剩双双报警)。 +- **AC-5** GIVEN 期望表目标带(F5),WHEN level_monte_carlo 全带扫描(基线/+100%/+200% × hp+100%),THEN 各 (L,天赋档) 通关率在带内或 OQ 登记偏离——**禁止静默出带**。 +- **AC-6** GIVEN L1 全部行集,WHEN 枚举敌种与环段,THEN 每关至少一项新投放(新敌种或新环段——OQ8;L1 = grub→ambusher→boss_guardian 三段 ✓)。 +- **AC-7** GIVEN Boss 行(Dmax),WHEN F3 检算,THEN r_boss 记录在案(越带须 OQ 登记——切片 0.81 越上界已登记 OQ2)。 +- **AC-8** GIVEN 每轮刷新,WHEN 计数达 refresh_max_per_round,THEN 第 N+1 次拒绝(表值驱动——2 为当前值非硬编码)。 +- **AC-9** GIVEN 任何 C3 直辖 knob 修订,WHEN 检查其联动网络(G 节五组),THEN 对应联合检算记录存在于调参日志(禁止独立调参纪律的流程面)。 +- **AC-10** GIVEN DifficultyTable MVP 空表,WHEN B2.spawn 任一 (L,D),THEN 无修饰行被消费(结构位不产生行为)且未来首行上线时套乘点 = 行值快照前。 +- **AC-11** GIVEN scope=global 变异行,WHEN 出现于非 Boss/精英图布置候选,THEN 加载/生成期拒绝(Core Rule 7 禁用面——A4 warning 通道升级为闸)。 +- **AC-12** GIVEN 每次调参批,WHEN 检查 README 调参日志,THEN F7 六步的执行记录齐备(流程纪律的可审计面)。 + +## Open Questions + +- **OQ1(L2+ 曲线数值)**:本 GDD 定结构/流程/目标带;L2+ 具体数值行随关卡内容扩展填充(内容与数值同批——C2 关卡序列定稿后)。Owner: C3 随批 · Target: L2 内容扩展时。 +- **OQ2(Boss 血量占比越带)**:**初步裁决(2026-09-12 试玩批次 1)**——用户反馈"Boss 血量目前 OK 或可以上调"→ F3 带修订为 [0.5,1.1]、v3 Boss 150HP 落带 ✓;败北分布向 Boss 集中(4/6)符合压迫预期。**维持开放**:v3 真人实测 Boss 战时长后再终裁(是否继续上调)。Owner: C3 · Target: playtest 批次 2。 +- **OQ3(真人修正系数)**:**方向确认(2026-09-12 试玩批次 1)**——v2 数值下模拟基线 80% vs 真人 100% 满血通关(清图/标雷/不用道具三策略均通):真人显著强于模拟者(推理避雷 + 标中旗 weaken + buff 组合利用)。v3 加难后模拟基线 60%;真人系数待 v3 试玩采集定量。Owner: C3 + playtest · Target: playtest 批次 2。 +- **OQ4(条款⑤支配性真人观测)**:**初步裁决(试玩批次 1)**——真人零弃图倾向("完全没有")→ 条款⑤非支配设计态成立、C 量级不上调 ✓(B1 约束包②采信)。维持登记:单一样本,后续批次持续观测。Owner: C3+B4 · Target: 持续。 +- **OQ5(精英节点形态)**:精英修饰的完整语义(独立节点/词缀/出现规则)= C1 分支地图定稿后的联动设计(OQ6 强制重审随之触发)。Owner: C3+C1 · Target: C1 GDD 定稿。 +- **OQ6(轮内刷新对难度模型的冲击)**:**初步读数(试玩批次 1)——刷新零使用**,根因不是免结算太顺滑而是**盲猜墙不足**(inferability 0.88 = 仅 ~12% 格不可推理,清图流几乎无赌点)。杠杆预登记:密度联动上探(d0.28/t0.85——A1 回退先例风险,attempts 24 下待实测);保持开放。Owner: C3 · Target: playtest 批次 2。 + +### 试玩批次记录 + +**批次 1(2026-09-12,v2 数值,用户本人)**:全部满血通关(清图为主/优先标雷/不用道具三策略均通)——基线难度不足定量坐实;Boss 血量 OK 或可上调;刷新零使用;零弃图倾向;治疗包零使用(不掉血伴生);伤害道具无特色(暂保留)。**裁决产出**:v3 数值批(atk 4/4/6/4/7 → 7/5/9/6/9;D0 24 保持防秒杀、Boss 130→150)+ DND 命名批(goblin/bugbear/ogre_chief)+ F3 带修订 + OQ2/3/4/6 读数。v3 扫描:基线 60%(带内上沿 ✓)、败北向 Boss 集中 4/6、+200% 100%——F5 带对照通过。下一批:v3 真人试玩(OQ3 定量 + OQ2 终裁)。 \ No newline at end of file diff --git a/design/gdd/legacy-2026-09-13/d1-hud-framework.md b/design/gdd/legacy-2026-09-13/d1-hud-framework.md new file mode 100644 index 00000000..068a4364 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/d1-hud-framework.md @@ -0,0 +1,375 @@ +# D1 HUD 与界面框架(HUD & UI Framework) + +> **Status**: Designed(2026-09-13 /design-system 一次会话定稿;**solo 模式——专员/总监代理全部跳过,出稿待人工复核**;未经 /design-review,独立评审须新会话 `/design-review design/gdd/d1-hud-framework.md`) +> **Author**: user + agents(呈现义务承载依据 = `design/ux/battle.md`(APPROVED 2026-09-12,§13 台账 27 项经 ux-review 实存验证)+ `design/art/art-bible.md` §1–4(定稿 2026-09-13)) +> **Last Updated**: 2026-09-13 +> **Implements Pillar**: P1(主——数字可读与拒绝诚实的感知面:"我算对了"看得见)· P2(主——逐行归因的呈现半边)· P4(地牢氛围容器:五区布局与能量档承载) +> **Layer**: 4 Presentation · **Milestone**: MVP · **设计顺序**: 第 10 位(MVP 最后一块) +> **上游全部 Approved**(A1–B4/B1/C3 + D2 + battle.md + art-bible §1–4)——D1 是项目首个**零玩法上游假设**的表现层系统 + +## Overview + +D1 是 Dungeon Sweeper 的**呈现层框架**:把 A1–B4 的全部玩法事实变成玩家看得见、点得动、归因得了的界面。它的承载物是**战斗屏**(MVP 唯一玩法屏,五区常驻双舞台布局——关卡进度条 / 战斗画面 / 角色信息 / 扫雷图 / 道具栏,规格见 `design/ux/battle.md`),外加一族共享呈现设施:toast 提示体系、模态覆盖层家族(图例 / 明细回看 / 胜负终局)、输入翻译(鼠标意图 → A2 意图对象)、模态层级管理、面向呈现的信号载荷契约与 ux 遥测。D1 不拥有任何玩法状态:全部读经核心只读面与封装呈现面,全部写经组合根(架构七原则"依赖单向组合根";ADR-0001/0003)。 + +D1 的存在理由有三层。第一,**义务承载**:systems-index 门禁体系向 D1 登记了 27 项呈现义务(A2×6 / B2×4 / B3×4 / B4×5 / B1×3 / A4×3 / A3 + A1 EC5——逐项落位表见 Dependencies,缺项即 blocking),battle.md 已逐项裁决呈现形态,本 GDD 将其升格为系统级验收。第二,**切片教训兜底**:垂直切片两起呈现层崩溃(窗口载荷缺字段、battle_finished 后读已销毁对象)暴露"已定稿 AC 全覆盖 core、呈现层零测试承载"的系统性盲区——本 GDD 落实三条硬性要求(信号载荷 schema 成文 / 场景级 headless 测试进 AC / 信号时点状态可见性规则,见 Core Rules 14–16 与 AC 硬性块)。第三,**美术解耦**:art-bible §4.6 图标键契约已冻结(键与通道分配定稿、终稿美术归样张批),样张未制作**不阻塞** D1——呈现层以**程序化占位**(白话:用代码按冻结色板与形状规则直接画出的临时图形,终稿美术到位后原位替换)先行,验收引用 art-bible §1.4 格子样张标准。 + +没有 D1,游戏没有可玩的表面:A2 的三事件流没有读者、B1 的流水协议没有呈现面、27 项上游义务没有兑付点——"自动战斗沦为无聊过场"(game-concept 已识别风险 #1)的防线就设在 D1 的逐行明细与高光反馈上。 + +## Player Fantasy + +**直接承载**(Section A 定框:玩家每一次点击、每一行结算、每一面旗都直接与 D1 互动)。D1 的情绪目标一句话:**"我看见的每一步都是真的"**——界面不说谎:数字是真实雷数(雾格只是不显示,数据层恒真)、拒绝有原因、作废有提示、结算逐行可归因。玩家对 D1 的正确感觉是"一块干净的玻璃":看穿它直接看到棋局与战果,玻璃本身不添乱。 + +体验锚点(按支柱): + +- **P1 推理掌控感的感知面**:四种格态一瞥可辨(左键双义的消歧前提——误挖不可逆)、悬停预承诺三态光标、可挖域常驻呼吸微光、每次拒绝可感知——规则层的诚实(A2 数字恒真、三源拒绝)必须由呈现层原样送达,任何呈现失真(雾格显影、拒绝吞掉、格态混淆)都是 P1 的死亡体验。 +- **P2 规划延迟兑现的呈现半边**:结算明细带逐行兑现、每行带来源标注(B1 流水行协议)、四类未生效行不静默、零值行不被吞——P2 裁剪判据"玩家能说出这个结果是我哪步扫雷带来的"的最后半米由 D1 走完。 +- **P4 地牢氛围容器**:挖掘期画面无杂音(能量 2/10,唯一在动的是悬停呼吸微光——art-bible §2.1)、结算期金橙光带逐行横扫(7/10)、清图金粒喷泉短促丰收(8/10)、失败暮蓝最静一拍(1/10)——art-bible §2 能量档与明度恒定硬规则由 D1 的转场与反馈系统承载。 + +参考感受:Into the Breach 的格内信息分层(预告上格——威胁画在格子上)+ 杀戮尖塔的逐卡结算反馈。**反向护栏**:界面绝不替玩家思考(不预判合法点击以外的东西)、绝不静默改判(voided 不提示 = 读感欺骗)、绝不用装饰伪装逻辑差异(art-bible 原则三)。 + +> 注:`creative-director` 未经咨询——Solo 模式,生产前请人工复核本节情绪定位(与 battle.md Purpose & Player Need 同源,本节为系统级转写)。 + +## Detailed Design + +> **归属声明**:屏级布局、组件形态、交互细节、动画时长的**裁决权威** = `design/ux/battle.md`(APPROVED)——本 GDD 引用其裁决不重复论证,只做三件事:① 升格为系统级 Core Rules 与 AC;② 补齐 spec 之外的系统级契约(信号载荷 schema / 订阅纪律 / 程序化占位 / 命名统一 / 遥测);③ 逐项落位 27 项门禁义务。冲突时以 battle.md + art-bible 为呈现形态权威、以本 GDD 为系统契约权威,机制语义以各玩法 GDD 为准。 + +### Core Rules + +**一、系统形态与架构边界(承重)** + +1. **零玩法状态、双层边界**:D1 代码零玩法数值字面量(期望值全部经核心只读面读表值推导——D2 Rule 1 对呈现消费面的适用口径)、零规则逻辑(不判合法性、不裁语义——一切判交上游,呈现只翻译结果)、零核心写调用(`settle_batch` / `submit_window_selection` / `request_refresh`〔经 B1 `refresh_board`〕 / runner 推进唯一发生在组合根——ADR-0003 Key Interfaces)。视图单元 = 信号外发 + setter 只读注入(切片九单元形态转正:BoardView / HUDView / HudPresenter / BattleLogView / ItemWindowOverlay / NodeOverlay / LevelStripView / TunePanel / UiKit)。 +2. **订阅纪律(ADR-0001 固化)**:呈现层**永不直接订阅 A2/A3**——只订 B1 步骤流 / 结算快照信号与 runner 信号;接线全部发生在组合根(core 信号 → 组合根 → 视图 setter)。**棋盘等"零事件外发"面的刷新 = 命令式回执驱动**(白话:动作调用返回后,组合根立刻重查一次只读面来刷新界面,而不是等一个通知信号——因为部分动作本来就不发信号)。battle.md §Events Fired 的"呈现订阅 A2 三事件"表述按本条收窄为"经组合根转发或命令式回执"(措辞差登记冲突清单⑥,battle.md 正文不改动)。 +3. **呈现面读图(封装收口批 entry 39/40 的 GDD 转正)**:D1 全部读经成文呈现面,禁私有字段直读——A2:`current_board()`(格态/旗/小人/变异只读投影)/ `reveal_record()` / `compute_move_path(cell)`(不可达返回空——预览与执行同源);A3:`phase / ap / rap / last_round_end`(getter-only);B1:`presentation_snapshot() / is_battle_live() / stock_count() / query_reward_identity()`(B1 成文呈现面)+ 步骤流信号;B2:`hero_unit() / all_enemies()` 投影;B3/B4:零直读(B1 协作对象私有化——经 B1 呈现面与流水投影);runner:节点链 / 当前节点 / HP 携带(A 区)。 +4. **写路径总表(全部经组合根)**:挖掘/旗/移动/停手 → A2 `settle_batch(intents)`(stop 亦经此——A2 D1×6①:不直调 A3);窗口选择 → B1 `submit_window_selection(窗口条目位置序)`(**传窗口条目位置、非库存句柄**——切片缺陷④契约);弃图刷新 → 组合根提交刷新意图 → B1 `refresh_board` 编排(A3 扣费 → A2 retire → A1 generate → A2 install → B4 roll_board);节点推进 → runner。**每帧至多一批提交(单意图帧)**,见 Rule 7。 + +**二、五区布局与常驻组件(继承 battle.md 布局决策 D1——常驻双舞台分屏)** + +5. **五区常驻布局(1280×800 基座,ADR-0002 canvas_items/expand)**:A 关卡进度条(全宽 28px,LevelStripView——节点链步进、当前高亮、已过暗金,数据源 runner)/ B 战斗画面(全宽 252px:英雄卡 + 敌人卡 ×1–3 + 轮次·阶段指示 + 结算明细带 24px 摘要 / 84px 实时流)/ C 角色信息(左下 240×520:HP / 攻击值含修正显示 / RAP 数据框直角细线素面 + 状态效果槽固定 4 槽 + 小人立绘占位)/ D 扫雷图(中下 ~760×520:AP 点阵条 44px〔停手按钮居左、图例与弃图刷新按钮居右——刷新按钮置雷图右上角〕+ BoardView 棋盘 + toast 槽上缘内侧)/ E 道具栏(右下 280×520:库存常读列表 + 窗口态原位激活)。战斗画面与扫雷图**同屏常驻**:挖掘期敌人待机可见、结算期不切场景(对 B5"双舞台陷阱"的裁决输入 = 第三形态)。 +6. **敌人卡(B 区,B2×4 承载)**:逐卡 = 敌像(§4.6.4 线索键姿态)+ 血条(hp/max_hp 数字可读)+ 顺位徽标(固定顺位目标可读——D1×4③)+ 意图徽标(**下一拍预告**:呈现即将执行的一拍的视觉状态,挖掘期全程可见,结算期不另设意图播片——B2 Rule 8⑤)+ 行为环点(环点数 = 环长编码,当前位置高亮)+ 状态槽。**敌方 side 池呈现口径(D1×4④)**:伏击层数为全体敌人共享单值、各卡**同值**呈现(数据源 = B1 流水经 B3 投影)。敌人死亡呈现:血条直角递减归零 → 卡体灰化 + 碎裂退场(**禁瞬消**——B2 资产下限);战斗段短路被掐灭的重击不产行动明细行、由行序可推断(B1)。敌人卡刷新 = **就地更新**禁逐行重建(切片教训:全量重建吞掉死亡灰化态——entry 36)。 +7. **输入翻译与单意图帧(A2×6①⑥ 承载)**:鼠标驱动(全部操作纯鼠标可完成——Gamepad Partial 非 MVP):左键已开格 = 移动 / 左键未开相邻格 = 挖 / 右键 = 插拔旗 / 停手按钮 = `settle_batch([stop])`。**单意图帧收集**:一帧内 collected 意图整批提交,stop 点击终结当帧收集;F4 语义序仲裁退为防御性兜底(生产面不可构造——联合清图让位条款不可达)。**悬停预承诺(A2×6③)**:光标三语义(移动箭头 / 挖镐 / 旗形)+ 目标格边框高亮;**可挖域常驻指示**:小人 8 向相邻未开格呼吸微光(挖掘期唯一在动元素);不可挖域悬停 = 禁止光标 + 首 run 一次 toast「需先走近(点击已开格移动,免费)」。**复合意图(点击远格 = 自动 move+dig)MVP 不做**(A2 评估项留痕:免费移动高频,两击摩擦符合 P4 ≤2 观测假设)。 +8. **移动路径预览 = 合法性指示(A2×6⑤ 悬案的 D1 定稿,继承 battle.md 布局决策 D3)**:悬停已开格 → `compute_move_path` BFS 路径以小人蓝细线点迹高亮(**合法性指示**——告诉玩家"点这里小人会怎么走",不是穿行动画);移动执行 = ≤150ms 短滑。备选"穿行动画轨道"登记不采纳:移动免费高频,动画时间税违反 P4 点击摩擦护栏(每轮输入动作数∶AP 消耗 ≤2 观测假设);playtest 反馈"移动太瞬"再议(留痕)。 +9. **四种格态一眼可辨(A2×6②)**:已开 0 格(裸土地板)/ 已开雾格(地板无字 + 底色层淡雾 tint 残留——OQ8 裁决采纳)/ 未开格(掩尘完整砖)/ 未开雾格(掩尘砖 + 雾纹带)——灰度截图下可逐格分类(左键"已开=移动 / 未开=挖"消歧的感知前提,后果不对称:误挖 = 1 AP + 可能触雷且不可逆)。**已引爆雷格"可通行可读"(A2×6④)**:已开底 + 巢穴纹(危险红族)+ 可作移动目标的视觉线索——"走过的雷、能走"。数字显示判定数据源 = `variant_ids`(digit 恒真透传、雾格隐藏呈现——A2 Core Rule 2,呈现层禁读真实数字自行判定)。绘制优先级栈(art-bible §1.2):数字 > 小人 > 旗 > 徽章 > 变异纹理 > 底色——任何元素不遮挡数字,小人与旗不得覆盖文本。 +10. **格面奖励徽章与奖励身份恒定可读(B4×5①)**:已开格显示家族类型徽章(§4.6.3 五键 attack/defense/heal/blast/weaken——**键集冻结**;拾取瞬间 tier 档位可读 = H1 节拍);库存徽章按行(reward_id 键,§4.6.2 八键——knife_t1/knife_adv 同族互辨是硬需求)。**MVP 格面徽章只在已开格呈现**(B4 Rule 8"未开格身份不呈现"为现行锁定规则——art-bible §4.6.3"未开格预读徽章"的呈现前提属极性提案实施批,随批登记;五键槽位在程序化占位映射表中预留、未启用键不呈现,见冲突清单⑤)。徽章栈位 = 旗之下、变异纹理之上、不进数字区、不构成格内第二焦点(§3.4)。**库存常读条(E 区,挖掘期规划面)**:逐件 icon + 名称(display_name)+ 属性简述(行值快照投影派生一句话:伤害/回复/增益)+ 状态;枚举序 = stock 受理序(B4 E21);悬停 tooltip = 完整描述 + 触发注记,**不含来源**(来源归因职责由结算明细行承载——battle.md 用户拍板 2026-09-12);Used 件过滤出常读条(query_inventory 保留只读、呈现过滤归 D1)。奖励身份键全局恒定、跨 run 禁重掷(reward_id / enemy_id / effect_id 三键同纪律)。 +11. **状态效果呈现(B3×4 承载)**:C 区状态槽固定 4 槽 = icon(§4.6.1 效果键——形状纹样身份、色相不承载身份)+ 层数角标(L)+ 剩余结算次数点(R)+ 悬停 tooltip(来源 + 语义 + 剩余——source_class 文案经 B1 `source_label_map`)。**极性呈现映射(B3×4③)**:有利 = 金族满框 / 有害 = 红族缺框(art-bible §4.6 通道分配);判别 = **键级极性表**(§4.6.1 冻结列:attack_up/defense_up/weaken 有利、ambush 有害——提案扩展键 enrage/off_guard/sapped 随提案实施批),语义归属经 SettlementTable `effect_pool_map` 成文(作用面),**禁止以 magnitude 符号判极性**(负值增益存在;pool 亦不足以判极性——enemy_output 池两向并存)。**空态规格(B3×4① D1 裁决定稿)**:query_active 返回空 → 状态槽**整体隐藏,HP/攻击/RAP 上移补位**(角色信息区纵向空间紧张,空槽占位浪费)——图例面板载空态说明("战斗中拾取的增益在此显示,随战斗结束消散")。数据源 = B3 投影(六归因字段 + display_name + 授予时数值快照,经 B1 编排面);B3 零信号外发 → 状态栏刷新绑 B1 结算编排完成时点(命令式)。 +12. **结算明细与高光(B4×5③④ / B1×3①② 承载)**:明细带数据源 = B1 Core Rule 14 流水行协议(13 类行封闭枚举,字段分层见 Core Rule 17);逐行来源标注格式「{行为} {值} → {目标}(来源)」(模板插值,行宽预留 40% 膨胀 + 固定槽高 + clip_contents——切片缺陷①回归);**效果行 value = 单次贡献值、pool_value = 池合成值,两列分示禁相加**(防"−130%×2 行 = −260%"误读);零值伤害行可见不被吞;**未生效四类行 + 标旗未中行按聚合计数呈现**(防逐行惩罚轰炸)且携带形状冗余(icon / 划线——非色相独载,色弱清单 #9);未选用件语境注记 =「未选用件随关卡结束清空」(关卡域口径)。**四类行呈现(B4×5③)**:道具行(入库/生效语境注记)/「奖励未生效」行(聚合计数)/「已消耗未生效」行 /「使用被拒」行。**高光行强调(B1×3①)**:标中旗 highlight 标记行 = 旗化暴击弹道加重 + 明细行金框(chord 缺席的即时兑现补偿);H2 清图奖励行 = 金框高亮行(轮末仪式峰值);**事件型高光用独立高光行、属性型强调用行字段标记**(B1 高光使用规则)。**完美排雷呈现形态(B1×3②)**:全屏喝彩级反馈(art-bible §2.2 最高档)——金光带加倍扫射 + 「完美排雷 ×2」横幅(P2 爆发兑现节点,创意风险 #2 落点);高光行 value 显示实际有效兑付当量(降级规则:首份即零兑付不发高光——庆祝不打在空兑现上)。 +13. **道具使用窗口(B4×5②,继承 battle.md 布局决策 D2——E 区原位激活)**:窗口触发(B1 授予段后、增益/减益段前——窗口锚点)时 E 区面板点亮金描边、标题切换「轮末道具窗口 · 至多选用 1 件」,D 区去饱和 + 金橙色相偏移锁输入(**明度不变**——art-bible §2 硬规则合规化);时机指示呈现窗口锚点位置;**usable_now 可用性投影**(B1 组装载荷):满血治疗包灰置 + 禁选原因 tooltip「HP 已满 · 禁选」;选用即承诺(选中 → 窗口关闭回结算流,提交 = 窗口条目位置);跳过按钮恒在;**政策注记常驻**「未选用件随关卡结束清空」;**空库存不弹窗**(窗口标记 skip 照常登记——回放输入完整性由 B1 承载,呈现零停顿)。窗口期棋盘输入不竞争(结算期棋盘本就锁输入——单意图帧先例)。窗口触发政策:MVP 有库存恒弹;候选"仅可用品触发 / 常驻库存条非模态"归垂直切片(B4 D1 义务②注记)。窗口态刷新纪律:窗口激活期间**跳过常读条全量重建**(切片教训:presenter 全量刷新 stomped 窗口态——entry 36)。 +14. **提示·图例·教学 toast 体系(A2×6③ / A4×3①② 承载)**:toast = D 区上缘内侧非模态浮出提示,自动消退 2.5s,**不劫持输入、不打断挖掘流**。提示族:① **雾首遇提示(A4×3②)**——触发 = 首个实际含雾格的板进入可操作状态(挖掘阶段开始;生成期一次查询即判定,免疫降级板误触发),**每 run 一次**,必含三要点(无雷盲区不赌命 / 雾格不传播连锁〔连锁止于雾界属正常〕/ 雾内插旗纯损);② **chord 肌肉记忆首遇提示(A2×6③)**——经典扫雷玩家首日尝试双击快开时每 run 一次:「本作无双击快开——插旗后轮末自动判定标中;点数字格可移动小人」;③ no_adjacent 提示(Rule 7);④ 拒绝与作废提示族(见 Edge Cases);⑤ **零合法板告知(B1×3③)**(见 Edge Cases)。**图例面板(A4×3①)**:模态 overlay,格态 / 变异 / 雾语义三组条目;「迷雾 = 无雷盲区」条目**恒在、不设解锁门、可随时回看**;载状态空态说明(Rule 11)。一次性提示去重作用域 = 每 run(hint_id 封闭枚举:`fog_first / chord / no_adjacent`——登记为遥测值域与去重键)。文案定稿权归实现期文案 pass(本 GDD 只锁必含要点与形态约束)。 +15. **雾图变体呈现(A4×3③,OQ8 已于 battle.md 裁决采纳 2026-09-12)**:未开雾格 = 掩尘砖 + 雾纹带(形状通道,色相不承载身份——色弱清单 #2);已开雾格 = 地板无字 + **底色层淡雾 tint 残留**(tint 处视觉锚 3 最低层、不遮挡数字——与已开 0 格灰度可判定区分:0 格携带"八邻无雷"推理信息,混淆即污染推理);**雾区全开一次性完成信号**:以**视觉合并域为信号单元**(连续雾区整域一次呈现,禁实例级双响——EC11:双雾区规则上独立实例、呈现合并);tint 与完成信号随雾重设计批可复核(art-bible 雾格资产占位——内容封禁期)。 +16. **终局与清空反馈(B3×4④ / A3 / B4×5⑤ 承载)**:**胜利终局**(verdict=win)——金蜡封落于扫雷图区中央 + 全屏暖白金 overlay + 判定行(终局判定行 = 胜负画面数据锚)+「下一节点」+ 明细回看入口;**失败终局**(verdict=lose)——明度不变的暮蓝 `#5A6C8C` 氛围 overlay(全作唯一暮蓝档)+ 小人坐砖掸灰 + 插过的旗逐面化光点收回 + 判定行 + 返回。**状态槽全清反馈(B3×4④)**:battle_over 时状态逐槽化光收回 + 微文案「状态随战斗结束消散」(防"我的 buff 哪去了"误读——胜负两终局同款)。**RAP 清零反馈(A3)**:battle_over 时 RAP 数字滚动归零 + 微文案(防"攒的钱凭空没了")。**库存清空反馈(B4×5⑤——关卡域口径)**:level_clear 时道包逐件化光点飞出 +「剩余道具 ×N 随关卡结束清空」(N = clear_inventory 返回的清空前快照计数——B1 编排义务⑤数据面)。**Boss 遭遇变体**:战斗开始一次性紫赤影子爬过棋盘(§2.6——纯视觉预告)+ 敌人卡体量最大 + 阶段指示附 Boss 名。**弃图刷新确认**(A3 契约的呈现半边):按钮置 AP 条右端,文案实时「RAP−8 · 本轮剩 N 次」(剩余次数 = `refresh_cap_per_round` − 本轮已用;**轮内已用次数读数 = A3 只读观测量缺口,接口漂移登记见 OQ2**);入口可用性 = 组合根按只读观测量预判(rap ≥ rap_refresh_cost ∧ 剩余次数 > 0)+ `request_refresh` 返回值兜底(**A3 无 `can_request_refresh` API——不引入**);确认微交互显式提示代价与旧板旗作废;**确认交互与常规确认不同位**(位置通道——破坏性确认异位,色弱清单 #7);RAP<8 / cap 超限拒绝可见(见 Edge Cases);换图后 AP 不重置、剩余次数扣减可见。**禁止"RAP 满自动刷"**(A3 Rule 11——刷新决策主体恒为玩家)。 +17. **面向呈现的信号载荷 schema 成文(切片教训硬性要求①,entry 17①——缺项即 blocking)**:D1 消费的全部跨系统载荷按下表成文,字段清单为消费侧断言与实现契约(对齐 A2 三事件先例;消费侧断言承载校验——ADR-0001"信号参数无运行时类型检查"的缓解): + +| # | 载荷 | 来源 | 字段 schema | 状态 | +|---|---|---|---|---| +| 1 | `cell_revealed` | A2 | `{cell, kind ∈ {safe,mine}, digit(仅 safe 携带,恒真透传), variant_ids, channel ∈ {initial,chain,dig}, reward_eligible}` | A2 Core Rule 2 已 schema——引用 | +| 2 | `flag_judged` | A2 | `{cell, hit}`(轮末判定批、行主序) | 同上 | +| 3 | `round_end_board` / `round_end_stopped` | A2 / A3 | `{path_tag ∈ {ap_exhausted,cleared}, continuation_hint ∈ {same_board,new_board}}`(双发射源 payload 同构;信号实名 per ADR-0001) | 同上 + ADR-0001 | +| 4 | 流水行 ×13 类 | B1 | 公共 7 字段 `type / source_class / origin_cell(板级行可空)/ round_index / display_name / value / highlight?({flag_hit, clear_reward_h2, perfect_broken})` + 特有:伤害行/道具行(生效)→ `target`;效果行 → `effect_id` + `pool_value`;未生效四类 + 标旗未中行 → 聚合计数/批次字段;source_class 值域 ⊆ `source_label_map` 键集(B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack}) | B1 Core Rule 14 已 schema——引用+重述 | +| 5 | 结算快照 | B1 | `presentation_snapshot()` 返回(终局呈现数据面——despawn 前全量状态);**独立 `settlement_snapshot` 信号系切片实现、B1 GDD 未登记契约**——D1 现行消费 = 步骤流判定行 + `presentation_snapshot()`;若需独立信号回 B1 修订批补登记(OQ3) | 切片实现 / 契约待登记 | +| 6 | 意图回执 | A2 | `ActionOutcome{accepted, outcome ∈ {executed, voided, rejected}, reject_reason ∈ {board_face, resource_face, phase, board_invalid}, sub_reason(仅 board_face:not_adjacent / target_revealed / target_concealed / flag_present / flag_absent / no_path / out_of_catalog)}` | A2 已 schema——拒绝呈现数据源 | +| 7 | 资源面拒绝 | A3 | **无载荷**(`try_spend` / `request_refresh` 仅 bool + debug 诊断)——呈现文案按动作上下文映射(诚实登记:资源面原因客户端推断,双源可辨由"动作种类 + 拒绝源"组合承载) | A3 AC 前言口径——引用 | +| 8 | 窗口载荷 | B1 组装 | `query_inventory` 逐件 `{item_id, source{cell,round}(不呈现于库存列表与 tooltip), 行值快照投影, 状态 InStock/Used}`(枚举序 = stock 受理序)+ `usable_now`(B1 守卫预判——满血治疗包 false) | B4/B1 已 schema——引用 | +| 9 | 奖励身份投影 | B1 呈现面 | `query_reward_identity(cell) → {reward_id, category, tier} \| null`(引用级投影;未开格可查但 MVP 不呈现) | B4 AC-2 已 schema——引用 | +| 10 | 状态投影 | B3 经 B1 | `query_active(side)` 元素 = 六归因字段 + `display_name` + 授予时快照 `(magnitude, unit)`;有效值 = L×快照 magnitude | B3 已 schema——引用 | +| 11 | 单位投影 | B2 | 敌单位七字段 `enemy_id/hp/max_hp/atk/heavy_multiplier/pattern/cap` + `beat` + 存活态;hero 投影 `hp/max_hp/atk`(经 `hero_unit()/all_enemies()`) | B2 已 schema——引用 | +| 12 | **零事件外发动作刷新面(命令式回执清单)** | A2/A3/B3/B4 | 旗插/拔与移动成功(A2 零事件外发)→ `settle_batch` 回执后重查 `current_board()`;AP/RAP(A3 无推送)→ 每次写路径回执后重读 getter;状态效果(B3 零信号)→ B1 结算编排完成时点刷新;库存(B4 查询面)→ 写路径回执后重查 `query_inventory` / `stock_count()` | battle.md 更新触发注记——本行成文为契约 | +| 13 | D1 自有遥测 | D1 | `ux_round_input_ratio{ratio}` · `ux_round_exit{path_tag}` · `ux_refresh_attempt{result ∈ {accepted,rejected_rap,rejected_cap}, round_refresh_index ∈ [1,2]}` · `ux_window_choice{selected_item_id, usable_now, skip}` · `ux_reject{source ∈ {board,resource,phase}, sub_reason}` · `ux_hint_shown{hint_id}`——写 playtest JSONL `ux` 行族(playtest_logger 扩展) | **本 GDD 成文**(新) | + +18. **信号时点状态可见性规则(切片教训硬性要求③,entry 17③——缺项即 blocking)**:呈现层只使用(a)信号载荷自带的数据、(b)销毁前已取到的快照、(c)封装只读投影——**禁止呈现层回读核心生命周期状态**。三条细则:① **销毁前捕获**——终局/死亡呈现的数据(敌人血条归零、卡体灰化、判定行)在核心对象 despawn 前已进入流水行与快照(B1 判定段先于 battle_over 清理序列;快照含 despawn 前终局——切片缺陷②教训:battle_finished 后读已销毁的 b2.hero 崩溃);② **载荷自包含**——B1 流水行每行自带来源/值/目标(`target` / `effect_id` / `pool_value` / `origin_cell`),呈现层不按行序反推目标状态机、不反查容器补数据;③ **生命周期态禁读**——`is_battle_live()` 与 phase 投影是呈现层唯一的生命周期观测口(经组合根),禁止读核心对象内部状态位判"还能不能刷"。敌卡死亡灰化等"尸位"呈现 = 就地更新保留的最后已知投影(Rule 6),不回读 Dead 句柄以外的任何东西。 +19. **程序化占位策略(art-bible §1.4 引用依据——样张未制作不阻塞)**:全部终稿美术依赖面(砖面/雾纹/敌像/图标/徽章/立绘/蜡封)以**程序化占位**先行:色值直接取 art-bible §4.1 冻结色板、形状按 §3 形状语言参数域程序化绘制(圆角砖/直角数据框/纹带形);icon/徽章/线索按 §4.6 键契约挂**键位映射表**(数据键 → 占位纹样 ID,一键一槽位)——终稿美术替换时零数据层改动、零运行时换图(键集扩容走 §4.6 修订)。**占位同样受 art-bible §1.4 第一验收项约束**:数字可读性硬指标(1–8 全档对格底 ≥4.5:1、0.5× 缩放可辨、纹带不侵数字字形区)对程序化占位可测且必须通过(色板参数域结构保证:底 L≥0.30 ∧ 饱和 ≤0.40 ∧ 墨 L≤0.027)。样张定稿 = 砖面形态与色板冻结闸,终稿资产开工前置(asset-spec 批);样张批不阻塞 D1 全部其余 AC。 +20. **切片 provisional 命名统一(session-state entry 35——正式 D1 批统一命名)**: + +| 切片 provisional 名 | GDD 正式名 | 载体 | +|---|---|---| +| `request_refresh_active()` | `request_refresh` | A3 API(Active 相位受理——A3 Rule 11,2026-09-12 D4 修订) | +| `can_request_refresh()` | **不引入**(A3 无此 API) | 入口可用性 = 组合根按只读观测量预判 + `request_refresh` 返回值兜底(Rule 16) | +| `refresh_max_per_round` | `refresh_cap_per_round` | ActionEconomyTable 结构位(默认 2)——D2/registry 字段回写随实现批(A3 Schema 定稿节已预留) | +| `during_round`(A2 装板参数) | (无独立参数——受理域语义) | A2 `install_board` 受理域增 Active·仅弃图刷新路径(B1 `refresh_board` 编排内)——A2 Core Rule 3 | +| `round_end`(信号名) | 实名 `round_end_board`(A2 面)/ `round_end_stopped`(A3 面) | ADR-0001 信号实名映射 | + +21. **模态层级管理(切片缺陷⑤纪律转正)**:全部模态覆盖层(图例面板 / 明细回看 / 胜利 overlay / 失败 overlay)经**统一登记序管理**入栈出栈;打开时**遮挡物隐藏或输入锁定**必须成立(TunePanel 家族教训:功能存在但被遮不可达 = 呈现层隐形缺陷高发区);窗口态为**原位激活非模态**(不入模态栈,但锁 D 区输入——Rule 13);TunePanel 显式初始化 `visible=false`(历史 quirk 转正)。**调试退役时点标注(battle.md OQ10 承接)**:TunePanel 为切片调试入口,正式侧归 C4 天赋树——C4 落位时退役,Playtest 诊断按钮(卡死转储)保留为调试设施。 +22. **本地化与可访问性基线**:CJK 字体随包 + 设为 theme 默认字体(ADR-0003 硬性前置——运行时默认字体无 CJK 字形,豆腐块 = AC 硬断言);按钮文案 4 汉字上限(「停手」「弃图刷新」「跳过」「选用」——译英 ≤1.6× 不破版,德语长词允许两行或图标+缩写);明细行模板预留 40% 膨胀。可访问性内建基线(`design/accessibility-requirements.md` 未建——battle.md OQ2 登记缺口,本 GDD 内建):数字对格底 ≥4.5:1(WCAG AA)、无信息仅靠色相承载(§4.5 清单九对逐项成立)、灰度可辨、字号下限(棋盘数字 ≥18px / 明细正文 ≥14px / tooltip ≥13px @1280×800)、无闪烁 >3 次/秒、**减动效选项**(全部动画时长减半或跳终态开关;结算逐行兑现为信息载体不可整体跳过、可加速)、纯鼠标可完成全部操作。 + +### States and Transitions + +战斗屏状态机(触发全部来自核心信号/回执——呈现层不自持玩法状态;"阶段"投影枚举 = {挖掘中, 结算中, 道具窗口}): + +| 屏状态 | 进入触发 | 变化 / 出口 | +|---|---|---| +| **挖掘阶段(默认态)** | 轮次开始 / 结算完毕回 Active | D 区可交互(AP 条点亮、可挖域呼吸微光);B 区敌人待机(意图徽标全程可见);明细带收起为上轮摘要 1 行;能量 2/10 | +| **结算阶段** | `round_end`(A2 棋盘路径 / A3 stopped 直发) | D 区锁输入(棋盘去饱和 + 金橙色相偏移,明度不变,数字保持全对比);明细带展开 84px 逐行滚动;阶段指示→"结算中";能量 7/10;B1 步骤流驱动逐行兑现 | +| **道具窗口态** | B1 授予段完成(窗口锚点信号) | E 区原位激活(Rule 13);空库存直接 skip 不弹窗;选用/跳过 → 回结算流(结算阶段续) | +| **清图瞬间** | cleared 判定(round_end) | 金粒喷泉 + 彩纸 3–5s 即收(§2.3);H2 清图奖励行金框强调;随后换新图 install | +| **完美排雷高光** | B1 高光行(perfect_sweep) | 全屏喝彩级:金光带加倍扫射 + 「完美排雷 ×2」横幅(叠加于清图瞬间之上) | +| **标中旗高光** | 流水行 highlight=flag_hit | 该旗化暴击弹道加重 + 明细行金框(当轮即兑付) | +| **敌人死亡呈现** | 流水行击杀标记(target 字段) | 该敌卡血条归零 → 灰化 + 碎裂退场(禁瞬消、就地更新) | +| **胜利终局** | 判定行 verdict=win | 金蜡封落于扫雷图区中央 + 暖白金 overlay + 判定行 + 状态槽全清反馈 + RAP 清零反馈;出口 = 下一节点(runner) | +| **失败终局** | 判定行 verdict=lose | 暮蓝氛围 overlay(明度不变)+ 旗化光点收回 + 判定行 + 明细回看 + 状态槽全清反馈 + RAP 清零反馈;出口 = 返回 | +| **Boss 遭遇变体** | encounter=boss | 战斗开始一次性紫赤影子爬棋盘 + 敌卡体量最大 + 阶段指示附 Boss 名 | +| **雾图变体** | 板含雾格 | 雾纹带 + 首遇提示(每 run 一次)+ 已开雾格 tint 残留 + 雾区全开合并域完成信号 | +| **空态:无状态效果** | query_active 双侧空 | 状态槽隐藏、数据框上移补位(裁决见 Rule 11) | +| **空态:零库存** | 关卡初 / 清空后 | E 区「道包空空——安全格里藏着补给」(指引性空态,非报错) | +| **调试变体** | 测试模式 | TunePanel 入口(正式侧归 C4 退役)+ Playtest 诊断按钮 | + +模态子栈(Rule 21 统一登记序):图例面板 / 明细回看 / 胜利 overlay / 失败 overlay——任一打开时遮挡物隐藏或输入锁定;非模态层:toast(自动消退)、窗口态(锁 D 区)。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| A2 | 上游 hard | 读:`current_board()` / `reveal_record()` / `compute_move_path(cell)`(呈现面,entry 39 E2 收口);订阅:零直订(ADR-0001——B1 facts 与组合根转发);写:`settle_batch(intents)` 经组合根(D1×6①)。消费其三事件 schema(Core Rule 17#1–3)与 ActionOutcome(#6)。**D1×6 六项义务全落位**(见 Dependencies 台账表) | +| A3 | 上游 hard | 读:`phase / ap / rap / last_round_end`(getter-only——entry 39 E5);刷新意图经组合根 → B1 `refresh_board` 编排内 A3 `request_refresh`(D4 2026-09-12——挖掘期 Active 受理、每轮上限 `refresh_cap_per_round`=2);**接口漂移登记:轮内已用刷新次数读数缺口(OQ2)**。AP/RAP 常显、RAP 清零反馈、双源拒绝呈现(A3 契约) | +| B1 | 上游 hard(数据供给主通道) | 读:步骤流信号(13 类行协议——Core Rule 17#4)、`presentation_snapshot()` / `is_battle_live()` / `stock_count()` / `query_reward_identity()`(B1 成文呈现面——entry 39 E1);写:`submit_window_selection(窗口位置序)` / 刷新提交面;level_clear 反馈行数据面(清空前快照计数)。**B1×3 + B4×5 + B3×4 呈现义务的统一数据源契约 = B1 Core Rule 14** | +| B2 | 上游投影 | `hero_unit() / all_enemies()` 只读投影(entry 39 E3);血条/顺位/姿态/存活数据面;despawn 前捕获纪律(Rule 18)。**B2×4 四项义务落位**(敌人卡 Rule 6) | +| B3 | 上游投影(经 B1) | 零直读(B1 协作对象私有化)——状态投影经 B1 编排面(Core Rule 17#10);EC12 空态、极性映射、全清反馈(Rule 11/16) | +| B4 | 上游投影(经 B1) | 零直读——`query_reward_identity()` / 库存载荷经 B1(Core Rule 17#8–9);奖励徽章键 §4.6.2、窗口呈现、四类行、H2、关卡末清空反馈(Rule 10/12/13/16) | +| A4 | 上游 soft(呈现规格) | 雾纹理双通道、图例条目、首遇提示三要点、tint + 完成信号(A4×3——Rule 9/14/15);A4 UI Requirements 为本 GDD 引用源 | +| A1 | 间接 | 零直接接口——EC5 零合法板数据面经 B1 上抛(非法产物标记 → toast 告知);biome 标签 = 换皮色板数据锚(程序化占位消费);产物字段保真是呈现诚实的数据前提 | +| D2 | 数据读取 | display_name 等本地化名经核心读表面;D1 自有常量不入 D2 玩法表(Tuning Knobs 载体声明) | +| C1-lite(level_runner) | 上游 hard | A 区节点链 / 当前节点 / HP 携带(关卡域);胜利 overlay「下一节点」→ runner 推进(写经组合根);level_clear 编排反馈 | +| C2 | 预留 | 关卡结算屏(关卡末清空反馈的完整屏形态)归后续 spec `design/ux/level-clear.md`(battle.md OQ9)——MVP 以 overlay 反馈承载 | +| B5 | 下游 hard | **D1→B5 交付物**:五区布局与双舞台裁决(第三形态)、B 区演出区结构、明细带呈现位、流水行协议呈现语义、敌人卡呈现语义(死亡灰化/禁瞬消)、art-bible §4.6 键位映射表——B5 只叠加动画密度与演出(受击反馈/弹道/退场动画),呈现语义已在本 GDD 定稿;"战斗表现美术测试"以本布局出样 | +| D4 | 下游 soft | 音频挂点事件源 = 明细行类型流 + toast 提示触发(B1/B4 音频挂钩点清单引用);MVP 无音频(Alpha) | +| D5 | 下游 soft | 教学承载位 = toast 体系 + 图例面板(雾三要点 / chord 提示 / 蓄力拍提示候选 / RAP 清零 / 库存清空教学条目)——D5 GDD 定稿口径 | +| A5 | 预留 | 探测类道具显影面(未开格身份呈现深度 OQ2)——接口归 A5 GDD,本 GDD 不预写 | +| D6 | 下游 | 本地化契约消费方(CJK/按钮宽/术语表——Rule 22) | + +## Formulas + +> D1 **零玩法公式**(显式排除声明见末尾)——本节仅承载呈现几何与可读性判据。全部取色禁新增色值,一切经 art-bible §4.1/§4.4 冻结色板。 + +### F1 棋盘格尺寸(cell_size) + +``` +cell = floor(min(D_w − pad_h, D_h − ap_bar_h − pad_v) / N) +N ∈ {10, 12}(A1 BoardGenTable 尺寸档);D 区基准 1280×800 下 D_w=760、D_h=520−44(AP 条)−pad +1280×800 实测:12×12 → 36px;10×10 → 45px +数字字号 = max(18, ceil(cell × 0.5)),像素字体整数倍缩放(canvas_items stretch 保证清晰) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| D 区宽/高 | D_w / D_h | int px | 布局常量 | 五区布局基准(battle.md Layout) | +| AP 条高 | ap_bar_h | int px | 44 | D 区顶条 | +| 棋盘维数 | N | int | {10, 12} | A1 行值(MVP 两档) | + +**Output Range:** cell ∈ [30, 45] px @1280×800。**Example:** 12×12 → floor(min(760−16, 476−16)/12) = 36px → 数字字号 ≥18px。 + +### F2 数字对比恒等式(引用 art-bible §4.1——D1 不新增参数) + +**数字可读性硬指标(§1.4 第一验收项,对程序化占位同样适用)**:1–8 全档数字对所在格底 WCAG 对比 ≥4.5:1——由色板参数域结构保证:格底相对亮度 L≥0.30 ∧ HSV 饱和 ≤0.40 ∧ 数字墨 L≤0.027 ⇒ 任意底墨组合 ≥4.5:1 恒成立(实测锚:#33261B/#C9AF83 = 6.9:1;#4A1E14/#B39A78 = 5.3:1)。D1 侧判据 = 灰度截图分类 + 对比抽测进 AC;换 biome 色板须重跑 §1.4(换皮只换四槽、永不变信)。 + +### 显式排除声明(R4 式) + +D1 无任何玩法数值所有权:伤害/经济/概率/生成公式全部归 A1–B4/B1/C3;AP/RAP/价目显示值 = A3 表读值投影;伤害数字 = B1 流水行 value 投影;D1 拥有的全部 = F1 几何换算、F2 可读性判据的引用执行、Tuning Knobs 节呈现常量。零随机(呈现层无任何 RNG 消费——S1/S2 扫描域适用)。 + +## Edge Cases + +- **If 每次拒绝(A2×6⑥——映射源 = reject_reason + sub_reason,每次拒绝玩家可感知)**:棋盘面拒绝(sub_reason 七值)→ 目标格旗朱 #C63D22 边框闪 + toast 文案(映射表:not_adjacent→「需先走近」/ target_revealed→「该格已翻开」/ target_concealed→「该格未探明」/ flag_present→「须先拔旗」/ flag_absent→「该格没有旗」/ no_path→「路径不通」/ out_of_catalog→debug 级、不与玩家语义混桶);资源面拒绝(AP 不足 / RAP 不足——无上游载荷,按动作上下文映射)→ AP/RAP 数字脉冲 + toast「AP 不足 · 剩 0 点」;相位拒绝 → toast「结算进行中」。图标+文案冗余(非色相独载)。 +- **If 作废(voided——至少 stop 被作废须可感知)**:toast「本轮已清图,停手未执行 · 剩余 AP 已作废」——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP 的读感欺骗(F4 联合清图让位在生产面不可构造,本条为诚实兜底)。 +- **If 零合法板(A1 EC5 → B1×3③——三形态选型定稿)**:B1 上抛非法产物标记 → **toast 诚实提示**(非模态、不打断);**补偿与回退不提供**(选型留痕 2026-09-12:补偿逻辑归 B1 域外、确定性回退已知好种子归实现期 ADR 候选〔A1 OQ2 关联〕——MVP 面向玩家的形态 = 仅提示)。玩家永不卡死由 A1 验收三分支保证,提示是体验债的诚实兜底(原"玩家不可见遥测域"口径由本条承接闭合)。 +- **If 弃图刷新被拒**:RAP 不足 → 按钮 + RAP 读数脉冲 + toast(EC13);本轮 cap(2 次)超限 → toast「本轮刷新次数已用完」(EC15);确认微交互缺一步 → 不提交(防误触——破坏性确认异位)。 +- **If 两个雾区相邻(A4 EC11)**:规则上独立布置实例、呈现合并为一片——**雾区全开完成信号以视觉合并域为单元、禁实例级双响**(信号只响一次);样张最坏用例含"宽度 1 雾带两侧毗邻数字格"(A4 AC-9)。 +- **If 雾首遇触发边界**:触发谓词 = 首个**实际含雾格**的板进入可操作状态——免疫 EC2 降级板(无变异)误触发、免疫贴脸雾 t=0 歧义(A4 UI Requirements 修正口径);一次性作用域 = 每 run。 +- **If 敌人死亡与终局同帧(despawn 时序)**:死亡呈现数据 = 流水行击杀标记 + 目标字段(despawn 前已产出——Rule 18①);battle_over 清理序列后呈现层零核心回读;`is_battle_live()` false 后视图进入终局态只消费快照。 +- **If 窗口打开时库存为空**:不弹窗、直接 skip(结算零停顿);窗口标记照常登记(B1 回放输入完整性)。 +- **If 满血时窗口含治疗包**:usable_now=false 灰置 + 禁选原因 tooltip;强制提交(不可经 UI 达成——禁选态拦截)→「使用被拒」行兜底(B1 守卫)。 +- **If battle_over 与 level_clear 反馈叠加(关卡末战)**:RAP 清零 + 状态槽全清反馈先行(battle_over),库存清空反馈随后(level_clear 编排)——两反馈不合并(防"N 道具消失"误读为战斗结算内容)。 +- **If 模态覆盖层叠开**(图例上开明细回看):统一登记序栈——后开者置顶、关闭逐层恢复;任一层打开时其遮挡物隐藏/输入锁定断言成立(缺陷⑤回归)。 +- **If 悬停文案换行**:全部动态区固定槽高 + clip_contents——换行裁剪不推挤布局(缺陷①回归;明细行 40% 膨胀 + tooltip 独立浮层)。 +- **If CJK 字形缺失**:豆腐块 = AC 硬断言失败(字体随包 + theme 默认——ADR-0003;首次导出 checklist 项)。 +- **If 敌卡刷新与死亡灰化竞争**:就地更新(字段级 setter)禁全量重建——死亡灰化态不被后续刷新吞掉(entry 36 教训);窗口激活期同理跳过常读重建(entry 36)。 +- **If 减动效开启**:全部转场/高光/演出时长减半或跳终态;结算逐行兑现降速不跳过(信息载体);无闪烁 >3 次/秒恒成立。 +- **If 移动目标 = 当前格**:幂等零移动——无动画、无 toast(A2 EC14 合法受理的呈现静默)。 +- **If compute_move_path 返回空**(不可达):路径预览不显示 + 禁止光标(预览与执行同源——AC-14② 对侧)。 +- **If 键集扩容**(新 effect_id / reward_id / enemy_id 上线):键槽映射表缺键 = 呈现层 fail-fast(debug 断言 + 占位"?"纹样 + 诊断,禁静默空图标)——扩容走 art-bible §4.6 修订(禁运行时换图)。 + +## Dependencies + +**Upstream**:A2(hard——棋盘呈现面与意图提交)· A3(hard——资源读数与刷新意图路径)· B1(hard——流水协议与编排呈现面,数据供给主通道)· B2(投影)/ B3(投影,经 B1)/ B4(投影,经 B1)· A4(soft——雾呈现规格)· A1(间接——EC5 数据面经 B1、biome 标签)· D2(读表)· C1-lite runner(hard——A 区与推进)。A5/D6 见 Interactions。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B5 | hard(垂直切片) | 布局裁决(双舞台第三形态)+ 演出区结构 + 行协议呈现语义 + 键位映射表(Interactions 表"交付物"清单)——B5 叠加动画密度,呈现语义不得二次裁决 | +| D4 | soft(Alpha) | 音频挂点事件源(明细行类型流 + toast 触发) | +| D5 | soft(Alpha) | 教学承载位(toast 体系 + 图例面板——各上游教学条目落点候选) | +| A5 | 预留(垂直切片) | 未开格身份显影呈现深度(OQ2) | +| D6 | 下游 | 本地化契约(CJK/按钮宽/术语表锚定——Rule 22) | +| C4 | 下游(Alpha) | TunePanel 退役接收方(Rule 21 时点标注) | + +**索引分层注记**:systems-index 依赖分层 "D1 ← A2,A3,B1"——本 GDD 实际投影消费扩至 B2/B3/B4/A4/runner,全部经 **B1 编排呈现面 + 封装收口批成文呈现面**(entry 39/40),**零新增运行时直调边**(B2/B3/B4 相互与对 D1 的"零直接接口"纪律保持;呈现面接口清单 = API Boundaries 登记载体)。D1 无游戏状态、无 knob 联动、无 RNG——层 4 Presentation 定位不变。 + +### 呈现义务台账逐项落位表(门禁核对用——27 项,缺项即 blocking) + +| 义务源 | 条目 | 本 GDD 承载落点 | +|---|---|---| +| A2×6 | ① stop 意图经 `settle_batch` 提交、不直调 A3 | Core Rule 7 + 写路径总表(Rule 4)+ AC-13 | +| A2×6 | ② 四种格态一眼可辨 | Core Rule 9 + AC-4 | +| A2×6 | ③ 悬停预承诺 + 可挖域常驻指示 + chord 首遇提示 | Core Rule 7 / 14② + AC-5 / AC-14 | +| A2×6 | ④ 已引爆雷格"可通行可读" | Core Rule 9 + AC-4 | +| A2×6 | ⑤ `compute_move_path` 呈现语义 = 合法性指示(裁决继承 battle.md D3) | Core Rule 8 + AC-6 | +| A2×6 | ⑥ 单意图帧收集 + voided 呈现 + 每次拒绝可感知 | Core Rule 7 + Edge Cases 1–2 + AC-7 / AC-8 | +| B2×4 | ① enemy_id→图像线索映射清单成文(预告时序 = 下一拍、挖掘期全程可见;含强度档位/环长直觉编码;双通道) | Core Rule 6 + 意图线索映射目录(下表)+ AC-10 | +| B2×4 | ② 线索映射全局恒定(enemy_id 键,跨 run 禁重掷) | Core Rule 6 / 10 + AC-10 | +| B2×4 | ③ 单位顺位与存活可读 | Core Rule 6(顺位徽标)+ AC-10 | +| B2×4 | ④ 敌方 side 池呈现口径(共享单值、各卡同值) | Core Rule 6 + AC-10 | +| B3×4 | ① HUD 常显 + EC12 空态规格定稿(裁决:槽隐藏) | Core Rule 11 + AC-12 | +| B3×4 | ② effect_id→icon 映射键契约 | Core Rule 11(引用 art-bible §4.6.1 七键冻结)+ AC-15 | +| B3×4 | ③ 有利/有害极性呈现映射(非 magnitude 符号) | Core Rule 11(键级极性表 + pool 语义归属)+ AC-12 | +| B3×4 | ④ 战斗结束全清的状态栏去留 | Core Rule 16 + States 胜负终局 + AC-16 | +| B4×5 | ① 已开格 + 库存奖励身份恒定可读(常读条 = 挖掘期规划面) | Core Rule 10 + AC-9 | +| B4×5 | ② 道具使用窗口呈现(原位激活/单件/usable_now/政策注记/空库存) | Core Rule 13 + AC-17 / AC-18 | +| B4×5 | ③ 结算明细四类行呈现(聚合计数 + 语境注记) | Core Rule 12 + AC-11 | +| B4×5 | ④ H2 清图奖励行强调呈现 | Core Rule 12 + States 清图瞬间 + AC-11 | +| B4×5 | ⑤ 关卡末(level_clear)库存清空可见反馈 | Core Rule 16 + Edge Cases + AC-16 | +| B1×3 | ① 高光行强调呈现(标中旗金框 / H2 金框) | Core Rule 12 + AC-11 | +| B1×3 | ② 完美排雷呈现形态 | Core Rule 12 + States + AC-11 | +| B1×3 | ③ EC5 零合法板提示/补偿/回退形态(裁决:仅提示) | Edge Cases 3 + AC-19 | +| A4×3 | ① 图例「迷雾 = 无雷盲区」条目(恒在/可回看) | Core Rule 14① + AC-14 | +| A4×3 | ② 雾首遇提示(每 run 一次、三要点、非模态) | Core Rule 14② + AC-14 | +| A4×3 | ③ 已开雾格 tint 残留 + 雾区全开合并域完成信号(OQ8 采纳) | Core Rule 9 / 15 + AC-4 | +| A3 | AP/RAP 常显 + 刷新入口(玩家发起)+ RAP 清零反馈 + 双源拒绝 | Core Rule 16 + Edge Cases 1/4 + AC-8 / AC-16 | +| A1 | EC5 零合法板诚实呈现(数据面经 B1×3③ 上抛) | 同 B1×3③ | + +**附:意图线索映射目录(B2×4① 成文载体——继承 battle.md 目录 + art-bible §4.6.4 键契约;资产定稿归 B5,本表为 D1 侧成文占位)** + +| enemy_id(§4.6.4 冻结键集) | 视觉状态(下一拍预告) | 强度档位编码 | 环长编码 | +|---|---|---|---| +| grub(蛆虫) | 攻击 ×1 态 | 小体量墩形 = 轻击(§3.1 普通怪) | 环点 1(单拍环——无蓄力态 = 每拍都动手) | +| ambusher(伏击兽) | 攻击 / 蓄力 / 重击 ×3 态 | 垂直延伸体量 = 重击(heavy_multiplier 直觉) | 环点 = 拍数;蓄力态在场 = 环里空一拍("可以贪") | + +- 预告时序 = 下一拍预告、挖掘期全程可见、结算期不另设意图播片(B2 Rule 8⑤);精确数值只在结算明细兑现(Rule 8④——图像不报数)。 +- 映射键 = enemy_id,跨 run 恒定、禁重掷(Rule 8③);形状 + 色相双通道;蓄力姿态必须含可辨识蓄力记号(§4.6.4——读怪核心课)。 +- 资产需求下限三类(受击反馈 / 死亡表现 / 顺位标记)在程序化占位中先行(占位纹样挂键);终稿归 B5/样张批。 +- 键集注记:切片 DND 更名(goblin/bugbear/ogre_chief)与 GDD 键集 {grub, ambusher} 差异登记冲突清单②——键位映射表以 GDD 键集为锚,切片更名随 B2/C3 修订批对齐。 + +**切片教训三条硬性要求落位表(entry 17——门禁评审核对项,缺项即 blocking)**: + +| # | 硬性要求 | 落位 | +|---|---|---| +| ① | 面向呈现的信号载荷成文 schema | Core Rule 17(13 项载荷表——A2 三事件/B1 行协议引用 + D1 自有遥测成文 + 零事件外发命令式刷新面成文) | +| ② | 场景级 headless 测试进 AC | AC「场景级 headless 测试」硬性块(真实实例化战斗屏场景 + 驱动完整对局 + 断言零脚本错误;载体 = 场景探针,ADR-0004 双载体、gdUnit4 deferred) | +| ③ | 信号时点状态可见性规则 | Core Rule 18(销毁前捕获 / 载荷自包含 / 禁回读核心生命周期态)+ AC 硬性块断言 | + +**发现冲突清单(不改已定稿 GDD 正文与提案——列示待裁决,2026-09-13 D1 会话登记)**: + +| # | 冲突 / 待对齐项 | D1 侧处置 | +|---|---|---| +| ① | game-concept 视觉锚原则 1"暗色地牢底色" vs art-bible 明亮底色反转 | 在案(entry 41)——概念侧注记待概念层修订批;D1 全部取色按 art-bible | +| ② | B2 GDD 敌种键 {grub, ambusher} vs 切片 DND 更名 {goblin, bugbear, ogre_chief} | 键位映射表以 GDD 键集为锚(§4.6.4 冻结);切片更名随 B2/C3 修订批对齐 | +| ③ | B4 GDD 默认根 clear_knife vs 切片供给控制器批 clear_blast + active_inject 池 | 徽章键按 reward_id 冻结、池归属不影响键(§4.6.2 注记);clear_blast 键并列预留、启用随 B4 修订批 | +| ④ | 极性提案 §3.5 跨文档回写清单全部待实施批(B3 四行→七行、B4 schema 扩列、A2 OQ1 关闭、**D1 义务条目正式登记**〔未开格类型图标/极性视觉语言/负向文案族〕) | 本 GDD 已按 §4.6 冻结键预留槽位;义务条目登记随提案实施批入 systems-index D1 台账 | +| ⑤ | art-bible §4.6.3"全部未开格呈现类型徽章"(提案 R3)vs B4 Rule 8"未开格身份 MVP 不呈现" | **D1 裁决:MVP 格面徽章只在已开格呈现**(B4 Rule 8 为现行锁定规则);未开格预读随极性提案实施批(其机制前提 = cell_type/pool_tag 扩列)随批修订 | +| ⑥ | battle.md §Events Fired"呈现订阅 A2 三事件"表述 vs ADR-0001 订阅纪律(呈现层只订 B1/runner) | 本 GDD 按 ADR-0001 收窄(Core Rule 2);battle.md 复检批对齐措辞(正文不改动) | +| ⑦ | A3 只读观测量(ap/rap/phase/last_round_end)缺**轮内已用刷新次数**——刷新按钮"本轮剩 N 次"读数无上游供给 | 接口漂移登记义务(OQ2):A3 随批补只读观测量;落地前按钮显示退化为"RAP−8 · 每轮至多 2 次"静态文案 | +| ⑧ | 切片已实现 `settlement_snapshot` 信号 vs B1 GDD 未登记该契约(battle.md 2026-09-12 评审修正口径) | OQ3:D1 现行消费 = 步骤流判定行 + `presentation_snapshot()`;若终局呈现需独立信号,回 B1 修订批补契约 | + +## Tuning Knobs + +> D1 knob = **呈现节奏与可读性常量**(非玩法数值——D2 Rule 1"全部玩法数值入表"纪律不适用;单一来源登记防散落,载体 = presentation 主题资源/常量单点,实现期裁决载体形态;修订走本 GDD 流程非调参)。全部标注尺度。 + +| Knob | 默认 | 尺度 | 调过高 | 调过低 | +|---|---|---|---|---| +| toast_display_ms | 2500 | 每条提示 | 信息滞留、遮挡节奏 | 读不完即消(拒绝原因漏读) | +| move_slide_ms | ≤150 | 每次移动 | 移动拖沓(违反 P4 摩擦护栏) | "太瞬"无方向感(battle.md OQ5 playtest 观测调) | +| board_install_fade_ms | 200 | 每次装板 | 换图迟滞 | 闪切无地牢感 | +| clear_celebration_ms | 3000–5000 | 每次清图 | 拖节奏(§2.3"三五秒即收"上限约束) | 丰收感不足 | +| font_min_board / detail / tooltip | 18 / 14 / 13 px | @1280×800 基座 | 挤占格内空间 | 可读性下限击穿(可访问性基线) | +| hint_once_scope | per_run | 每提示项 | —— | ——(存档级"一生一次"归 D3/C4 后重议) | +| reduce_motion_scale | 0.5(或跳终态) | 每动画 | 减动效失效 | 等效关闭动效 | +| detail_band_height | 24 / 84 px | 摘要 / 实时流 | 挤压 B 区演出区 | 行数不足(逐行归因截断) | + +- **非 knob(结构冻结,修订走 GDD 流程)**:五区布局与分区尺寸(battle.md 拍板)、双舞台常驻、窗口原位激活形态、单意图帧纪律、模态层级纪律、键位映射契约(§4.6)、程序化占位策略、订阅纪律(ADR-0001)。 +- **性能预算(引用非 knob)**:同屏 draw calls < 200、进入战斗屏 ≤500ms 可交互、headless 全套 < 30s(technical-preferences / ADR-0004)。 +- **Knob 交互**:font_min_board × cell 尺寸(F1 字号下限随格尺寸联动——12×12 板 36px 格是 18px 下限的绑定域);toast_display_ms × 一次性提示去重(提示频率观感)。 +- **调参流程**:改先测量后复测(切片 mock 数据先行——code-conventions 数值纪律);playtest 观测项 = 移动手感(OQ5)/ 明细带常驻(OQ6)/ 窗口存在感(OQ7)。 + +## Visual/Audio Requirements + +D1 是本 GDD 体系中唯一的**自有渲染面系统**——视觉义务从"数据供给规格"反转为"执行 art-bible"。逐条锚定: + +1. **§1.2 绘制优先级栈(执行)**:数字 > 小人 > 旗 > 徽章 > 变异纹理 > 底色——BoardView 分层绘制/控件叠序按此栈实现,任何元素不遮挡数字(小人与旗不得覆盖文本;小人所在格边框高亮)。 +2. **§1.4 格子样张(引用依据)**:样张 = 砖面形态定稿闸;D1 引用其验收标准(第一验收项 = 数字可读性硬指标)作为程序化占位与正式资产的共同判据;样张未制作不阻塞 D1(Core Rule 19);样张通过后冻结为制作标准,终稿资产替换走 asset-spec 批。 +3. **§3.3 UI 形状语法(执行)**:半拟物——面板 = 石板圆角矩形(与地砖同圆角率)、按钮 = 凸圆角"可按的砖"、仪式性确认(胜利蜡封)= 圆形;数据框/准线/进度条一律直角细线无纹理("圆角 = 可触摸的物件,直角 = 须精确的信息")。 +4. **§4.1/§4.4 UI 色板(执行)**:数据素面墨族(#33261B 系)——棋盘与面板读同一种墨;战斗容器中性石色(防暖色渗入分走数字对比预算);语义色规则(红只说危险 / 金只说收益 / 蓝只说己方 / 紫赤只说 Boss / 绿零逻辑语义 / 禁入色相带 300°–345°)逐条适用;明度恒定硬规则(状态区分只经色相/饱和/运动/元素——去饱和 + 金橙偏移即其合规化实例)。 +5. **§4.5 色弱安全清单(逐对执行)**:九对语义对的冗余通道(形/图标/位置/运动)在对应组件落实——四格态靠完整/碎裂形、奖励金 vs 危险红靠剪影+运动、UI 红/金强调对加图标+文案冗余、破坏性确认异位(位置通道)、图标键族一键一纹样色相不承载身份。 +6. **§4.6 图标键契约(键位挂接)**:效果图标 §4.6.1(七键——sapped 键冻结未入表前禁引用)、奖励徽章 §4.6.2(八键 + clear_blast 并列预留)、格面类型 §4.6.3(五键——MVP 只挂已开格)、敌种线索 §4.6.4(行为拍姿态键)——键位映射表(Core Rule 19)为唯一挂接点,键集扩容走 §4.6 修订、禁运行时换图。 +7. **能量档承载(§2 执行)**:五区布局的动效与色彩转场按 §2.1–2.6 能量档实现(挖掘 2/10 唯一微光在动 → 结算 7/10 金橙光带 → 清图 8/10 金粒喷泉 → 胜利 6/10 克制 → 失败 1/10 暮蓝最静一拍 → Boss 5/10 紫赤)——"状态间区分度自检"三条(挖掘 vs 结算靠谁在动+饱和;胜负以色相即判;Boss 以紫赤登场标记)为转场验收锚。 +8. **音频(D4,Alpha 占位)**:MVP 无音频;挂点清单引用 B1/B4 音频挂钩点(明细行类型流 / 负反馈聚合单次 / 开格拾取按 category 分色 / 清图奖励音)+ toast 提示触发(本 GDD 新增挂点候选);临时 SFX 随垂直切片(AD 项先例)。 + +> 注:`art-director` 未经咨询——Solo 模式,生产前请人工复核本节与 art-bible 的一致性。 + +> 📌 **Asset Spec**:Visual/Audio 义务已定义。art-bible 已定稿(§1–4),样张批后运行 `/asset-spec system:d1-hud-framework` 产出逐资产视觉描述/尺寸/生成提示词(样张先行——§1.4 为定稿闸)。 + +## UI Requirements + +- **权威屏级 spec = `design/ux/battle.md`**(APPROVED 2026-09-12,B1 UX Flag 的 /ux-design 产物——覆盖子屏:挖掘主界面 / 结算明细 / 道具窗口 / 终局 / 明细回看):本 GDD 为其系统级承接(布局/组件/交互裁决全部继承,见 Core Rules 二—七组;本节不重复 spec 内容,stories 引用 `design/ux/battle.md` 而非本 GDD 直引组件细节)。 +- **UX Flag 处置**:B1 UX Flag(结算明细屏 + 道具窗口 → Phase 4 前置 /ux-design)已由 battle.md 兑现闭合;D1 自身无新增屏(战斗屏 = 唯一 MVP 玩法屏)。相邻域屏 NodeOverlay / 关卡结算 = 后续 spec `design/ux/node-reward.md` / `design/ux/level-clear.md`(battle.md OQ9——本 GDD 只锁出口锚点)。 +- **pattern 库**:`design/ux/interaction-patterns.md` 已建(核心 8 条:双源拒绝反馈 / 原位窗口激活 / 逐行兑现明细带 / 悬停预承诺光标 / 点阵资源显示 / 模态 overlay 层级纪律 / 破坏性确认异位 / 禁用态即理由全文)——本 GDD 组件全部对号入座;12 目录占位(实现期回写义务)。 +- **accessibility**:`design/accessibility-requirements.md` 未建(gate-check Pre-Production 缺口在案)——本 GDD 内建基线承诺(Rule 22),正式分层(文本缩放/色盲模式/按键重映射)待该文档定稿。 +- **场景探针标配(code-conventions)**:每个可交互 UI 区域配套场景级探针——切片三探针(ui_probe / ui_click_probe / ui_tune_probe)扩展 + 战斗屏场景级探针(AC 硬性块);探针即呈现层契约的活文档。 + +## Acceptance Criteria + +> **测试载体(ADR-0004 双载体定稿;gdUnit4 deferred——D2 OQ1 关闭形态)**:逻辑断言类 = `godot --headless --script` 冒烟扩展;**场景级类 = 场景探针**(`godot --headless --path . res://smoke/.tscn` 走正常项目引导——autoload 在位、真实 SceneTree;切片三探针为先例)。期望值推导纪律:呈现断言引用表读值(AP=ap_per_round 表读值、RAP 门槛=rap_refresh_cost 表读值等)与 art-bible 冻结参数,不写死玩法数值;呈现常量(toast 时长等)为本 GDD Tuning Knobs 登记值可直断。每用例先 `reset_diagnostics()`。**覆盖声明(out-of-scope)**:玩法行为正确性(上游 GDD AC 域——D1 只断言呈现投影与上游契约一致);美术终稿质量(样张/asset-spec 批);动画密度(B5);音频(D4);gamepad 焦点序(OQ5)。 + +**布局与可读** + +- **AC-1(五区布局与基座)** GIVEN 1280×800 与 16:9 全屏(canvas_items/expand),WHEN 进入战斗屏(12×12 板),THEN 五区无遮挡/无重叠/无内容裁切(棋盘全部格子+数字完整可见)、分区尺寸 = battle.md 布局表值、同屏 draw calls < 200、进入后 ≤500ms 内首格可点。 +- **AC-2(F1 格尺寸与字号)** GIVEN 12×12 / 10×10 两档板,WHEN 装板渲染,THEN cell = F1 公式值(36 / 45px @1280×800)、数字字号 ≥ max(18, cell/2)、像素字体整数倍缩放无模糊。 +- **AC-3(对比度与明度恒定)** GIVEN 全部格底×数字组合,WHEN 灰度截图 + 对比抽测,THEN 1–8 全档对比 ≥4.5:1(F2)、0.5× 缩放数字仍可辨、纹带不侵数字字形区;全部状态转场(去饱和+金橙偏移 / 暮蓝 / Boss 紫赤)明度恒定(无变亮/变暗表达)。 +- **AC-4(四格态 + 雷格可读 + tint——A2×6②④ / A4×3③)** GIVEN 含雾板逐格渲染,THEN 四种格态灰度截图下一眼可辨;已引爆雷格呈"可通行可读"(已开底+巢穴纹+可作移动目标);已开雾格 = 地板无字 + 底色层 tint 残留且与已开 0 格灰度可判定区分;数字显示判定消费 variant_ids(呈现层无独立雾判定——代码走查)。 +- **AC-5(可挖域与绘制栈)** GIVEN 小人在场,THEN 8 向相邻未开格常驻呼吸微光(挖掘期唯一在动元素——其余静止截图断言);绘制优先级栈成立(徽章在旗之下纹理之上、不进数字区、无第二焦点)。 + +**输入与交互** + +- **AC-6(移动路径预览——A2×6⑤ 定稿)** GIVEN 悬停已开格,THEN `compute_move_path` BFS 路径以小人蓝细线点迹高亮(合法性指示);移动执行 ≤150ms 短滑;不可达格返回空且禁止光标(预览执行同源——A2 AC-14② 对侧);点击远格**无**自动 move+dig 复合意图(负空间)。 +- **AC-7(单意图帧 + 悬停预承诺——A2×6⑥③)** GIVEN 一帧内多意图(含 stop),WHEN 提交,THEN 整批经 `settle_batch` 单批到达(stop 终结当帧收集——探针断言单批);光标三语义(移动/挖/旗)随悬停目标正确切换 + 目标格边框高亮。 +- **AC-8(stop 路径与刷新——A2×6① / A3)** WHEN 点击停手,THEN 意图经 `settle_batch([stop])` 提交(代码走查 + 探针:零直调 A3 符号);按钮下压 + AP→RAP 飞行数字(RAP 增量 = 剩余 AP 读数投影);WHEN 弃图刷新成功,THEN RAP 扣 rap_refresh_cost、AP 不重置、剩余次数扣减可见、旧板旗作废提示可见;WHEN RAP<8 或 cap 超限提交,THEN 资源面拒绝可见(脉冲 + toast——EC13/EC15)。 +- **AC-9(奖励身份恒定可读——B4×5①)** GIVEN 已开格 + 库存,THEN 已开格家族徽章(§4.6.3 键)+ tier 档位拾取瞬间可读;库存常读条逐件 icon+名称+属性简述、枚举序 = stock 受理序、**不含来源信息**(列表与 tooltip 均无);Used 件不在常读条;同族刀(knife_t1/knife_adv)并排互辨。 + +**信息与归因** + +- **AC-10(敌人卡——B2×4①②③④)** GIVEN 双敌种遭遇,THEN 意图徽标 = 下一拍预告且挖掘期全程可见、映射按 enemy_id 全局恒定(跨 run 重放同姿态)、含强度档位与环长编码(环点数 = 拍数)、蓄力态含蓄力记号;顺位徽标可读(固定顺位最前者可辨);血条 hp/max_hp 数字可读;多敌伏击层数各卡同值呈现;敌死亡 = 血条归零→灰化+碎裂退场(禁瞬消)、就地更新不丢灰化态(entry 36 回归)。 +- **AC-11(结算明细与高光——B4×5③④ / B1×3①②)** GIVEN 完整结算,THEN 逐行明细实时可见且每行带来源标注(source_label_map 文案)、四类未生效行按聚合计数呈现且带形状冗余、零值行可见、效果行 value/pool_value 分示禁相加;标中旗行金框+弹道加重;H2 清图奖励行金框;完美排雷 = 金光带加倍+「完美排雷 ×2」横幅、value = 实际有效兑付当量、首份零兑付时高光降级不发;战斗后可经明细回看 overlay 复看完整流水。 +- **AC-12(状态槽——B3×4①③④)** GIVEN 状态在场/清空,THEN 槽显示 icon(§4.6.1 键)+层数+剩余结算次数点+tooltip(来源+语义);极性按键级表呈现(金满框/红缺框——**非 magnitude 符号**:负 magnitude 有利行测试例);空态 = 槽隐藏 + 数据框上移 + 图例载空态说明;battle_over 逐槽化光收回 + 微文案。 +- **AC-13(窗口——B4×5②)** GIVEN 窗口触发,THEN E 区原位激活(非中央弹窗)+ 至多选 1 件 + usable_now=false 灰置带禁选原因 + 政策注记「未选用件随关卡结束清空」常驻 + 空库存不弹窗 + 选中提交 = 窗口条目位置(选中件库存态必变 Used——缺陷④回归探针)+ 窗口期不被常读刷新 stomped(entry 36 回归)+ D 区去饱和锁输入(明度不变)。 +- **AC-14(提示与图例——A2×6③ / A4×3①②)** THEN 图例面板「迷雾 = 无雷盲区」条目恒在可随时回看、载状态空态说明;雾首遇提示每 run 一次、非模态不劫持输入、必含三要点;chord 提示每 run 一次含「点数字格 = 移动小人」锚点;no_adjacent 首次提示;hint_id 去重生效(同 run 二次触发零 toast)。 +- **AC-15(键契约执行)** THEN 全部 icon/徽章/线索经键位映射表挂接(数据键→纹样 ID);§4.6.1 七键/§4.6.2 八键/§4.6.3 五键/§4.6.4 姿态键逐一在位;未知键 = fail-fast 断言 + "?"占位 + 诊断(禁静默空图标);sapped 键未入 StatusEffectTable 前零引用。 + +**状态·资源·终局反馈** + +- **AC-16(清空反馈三件套——A3 / B3×4④ / B4×5⑤)** THEN battle_over:RAP 数字滚动归零+微文案、状态槽逐槽化光+微文案;level_clear:库存逐件化光飞出+「剩余道具 ×N 随关卡结束清空」(N = 清空前快照计数);两反馈不合并、时序不倒挂。 +- **AC-17(终局与变体)** THEN 胜利 = 金蜡封落于扫雷图区中央+暖白金 overlay+判定行+明细回看入口;失败 = 暮蓝 overlay(明度不变)+旗化光点收回+判定行;判定行内容 = B1 终局判定行投影(数据锚);Boss 战 = 紫赤影子爬棋盘一次性演出 + 阶段指示附 Boss 名。 +- **AC-18(遥测)** THEN 六类 ux 行按 Core Rule 17#13 schema 写入 playtest JSONL(字段名与值域逐项对表);ux_round_exit.path_tag 值域 = 上游封闭枚举。 + +**场景级 headless 测试(切片教训硬性要求②③——缺项即 blocking)** + +- **AC-19(战斗屏场景级探针)** GIVEN 真实实例化战斗屏场景(探针场景加载、autoload 在位),WHEN 驱动完整对局(两场遭遇 × 含雾板 × 窗口选用/跳过 × 刷新 × 胜负两终局),THEN 全程**零脚本错误**(stderr 归零)+ 五区断言 + 首格可点计时 ≤500ms(切片 ui_probe/click_probe 模式扩展)。 +- **AC-20(信号时点状态可见性——缺陷②回归)** THEN 敌人 despawn 与 battle_over 之后:呈现层对核心生命周期状态的读数 = 零(静态扫描:views/ 文件集禁 `is_battle_live()` 以外的核心状态位读取、禁核心内部字段访问——只许成文呈现面);终局呈现数据全部来自流水行/快照(断言终局后重放渲染与实时渲染逐像素一致——快照自包含判据)。 +- **AC-21(模态层级——缺陷⑤回归)** GIVEN 逐一打开全部模态覆盖层(含叠开),THEN 每层打开时遮挡物隐藏/输入锁定断言成立、关闭逐层恢复;TunePanel 默认不可见(显式初始化)。 +- **AC-22(布局稳定——缺陷①回归)** THEN 全部动态区固定槽高 + clip_contents:悬停 tooltip 最长文案 / 明细行 40% 膨胀宽度下布局零推挤(截图差分断言)。 +- **AC-23(CJK 字体)** THEN 全部中文文案渲染非豆腐块(字形覆盖抽测——ADR-0003 硬性前置)。 +- **AC-24(payload schema 消费断言——硬性要求①兑现)** THEN 探针对 Core Rule 17 表 #1–#9 载荷逐字段断言(消费侧校验——缺字段/多字段 = 失败,切片"窗口载荷缺 round_index"崩溃回归);#12 命令式刷新面 = 代码走查(旗插拔/移动/AP/RAP/状态/库存六面各一个回执后重查断言)。 +- **AC-25(静态扫描与命名)** THEN 呈现层文件集:零 `randomize()`/`randi()`/`randf*`(S1/S2)、零玩法数值字面量(期望值经只读面推导——人工三要素口径 warning 级 + AC-1/2/8 表读值推导合成判定)、核心写调用仅组合根(S7 呈现侧)、切片 provisional 名(`request_refresh_active` / `can_request_refresh` / `refresh_max_per_round` / `during_round`)**零残留**(grep 断言——entry 35 命名统一核销);每视图单元头注含「实现 GDD:design/gdd/d1-hud-framework.md(系统 ID D1)」溯源行。 + +**覆盖矩阵(门禁评审用)**:Core Rules 1–4→AC-19/24/25 · 5→AC-1 · 6→AC-10 · 7→AC-6/7 · 8→AC-6 · 9→AC-4/5 · 10→AC-9 · 11→AC-12/15 · 12→AC-11 · 13→AC-13 · 14→AC-14 · 15→AC-4/11 · 16→AC-8/16/17 · 17→AC-24 · 18→AC-20 · 19→AC-15(键位)/样张引用 · 20→AC-25 · 21→AC-21 · 22→AC-23 · F1→AC-2 · F2→AC-3 · Edge Cases 全表→AC-4/6/8/10/11/13/14/16/17/21/22/23/15(拒绝与 voided→AC-8 及探针负例;零合法板→AC-19 注入腿;双雾区→AC-4/11 腿)· 台账 27 项→上文落位表逐行 · 切片教训 3 项→AC-19/20/24 + Core Rules 17/18。 + +## Open Questions + +1. **_draw 迁移复议(ADR-0003 Decision 3 的 D1 裁决登记)**:**MVP 维持 GridContainer + 逐格控件**(裁决依据:呈现层程序化占位、样张未定稿、雾纹理/终稿表现力需求未冻结、三探针与现形态耦合〔改造 = 隐藏成本〕)——样张定稿后按表现力需求(biome 色板/雾纹理/动效复杂度)复议二选一;若迁移,`Viewport.push_input` 端到端探针从加分项变必需项(ADR-0004)。owner:D1 复议批;deadline:样张定稿后。 +2. **A3 轮内刷新次数读数(接口漂移登记)**:刷新按钮"本轮剩 N 次"需要 A3 暴露轮内已用刷新计数(现只读观测量 ap/rap/phase/last_round_end 未含)——A3 修订批补只读观测量 + AC 前言扩列;落地前按钮退化为静态文案"RAP−8 · 每轮至多 2 次"。owner:A3;deadline:正式实现接线批。 +3. **B1 `settlement_snapshot` 契约登记**:切片已实现该信号(每结算末全量快照、despawn 前终局),B1 GDD 未登记为契约——D1 现行消费 = 步骤流判定行 + `presentation_snapshot()`(零阻塞);若 D1/B5 终局呈现需独立信号契约,回 B1 修订批补登记(接口漂移)。owner:B1;deadline:B5 GDD 前。 +4. **battle.md 订阅表述对齐(冲突清单⑥)**:ADR-0001 订阅纪律(呈现层只订 B1/runner)与 battle.md §Events Fired"A2 三事件呈现订阅"的措辞差——本 GDD 按 ADR-0001 收窄;battle.md 复检批对齐。owner:battle.md 复检;deadline:下次 /ux-review。 +5. **Gamepad 焦点序**(battle.md OQ8 继承):Partial 支持非 MVP——焦点环顺序待 gamepad 批次。 +6. **相邻域屏 spec**(battle.md OQ9 继承):`design/ux/node-reward.md` / `design/ux/level-clear.md`——本 GDD 只锁出口锚点(节点奖励覆盖层 / 关卡结算屏的完整形态归后续 spec)。 +7. **accessibility-requirements.md**(battle.md OQ2 继承):正式分层待该文档定稿——gate-check Pre-Production 缺口在案。 +8. **键集扩容流程锚定**:新 effect_id / reward_id / enemy_id / cell_type 上线 → art-bible §4.6 修订 + SettlementTable effect_pool_map 随批登记(接口漂移双登记——本 GDD AC-15 fail-fast 强制暴露漏登记)。 +9. **极性提案实施批的 D1 义务条目**(冲突清单④):未开格类型图标 / 极性视觉语言 / 负向文案族按提案 §3.5 正式登记入 systems-index D1 台账(本 GDD 已预留键槽位与裁决⑤)。 +10. **明细带挖掘期常驻摘要**(battle.md OQ6 继承):常驻(现方案)vs 结算后收起——playtest 观测回看频率后裁决。 +11. **D5 教学挂靠**(battle.md OQ13 继承):挖到雷=敌方优势 / 旗挡链 / 首次蓄力提示 / 囤刀闭环——toast 体系为落点候选,D5 GDD 批定。 +12. **TunePanel 退役时点**(battle.md OQ10 继承):C4 天赋树落位批退役(Rule 21 已标注);Playtest 诊断按钮保留。 + diff --git a/design/gdd/legacy-2026-09-13/d2-data-config.md b/design/gdd/legacy-2026-09-13/d2-data-config.md new file mode 100644 index 00000000..9927d430 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/d2-data-config.md @@ -0,0 +1,274 @@ +# D2 数据配置与数值表(Data Config & Balance Tables) + +> **Status**: Approved(2026-09-07 两轮评审 + delta 复核 5/5 PASS 后收口;同日 A3 回填 ActionEconomyTable 定稿 AP 8/旗 4/×2/×1,Tuning Knobs 对应条目已删;同日 A3 评审修订二次回填——schema 字段清单交付、成本三字段入表 + 冻结字段;**同日三次回填(A3 RAP 结构修订 delta)**——`flags_per_round`/`flag_unit_cost`/`stop_multiplier`/`clear_multiplier` 删除、`rap_refresh_cost` 新增,AC-7 示例值域内修正,Core Rules 表行与 registry 已同步;**同日 A3 三轮评审 delta(schema 级,D2 自身规则零改动)**——ActionEconomyTable 增跨字段 validate 规则(任一动作成本 ≤ ap_per_round,schema 类承载、违规致命、条件性夹具登记),Core Rules 表行与 registry 已同步;**同日 A4 回填 VariationTable 定稿(2026-09-08)**——8 字段 + fog 行 + Rule 4 豁免注明,Core Rules 表行与 registry(variation_table / variant_pool_gate 新条目)已同步;**同日 A4 re-review delta(2026-09-08)**——VariationTable 增跨字段校验 `hide_numbers⇒safe_nonzero`(加载期致命,A4 夹具 variation_mask_constraint 承载)+ counter_item_ids knob 误标修正(✗),Core Rules 表行与 registry 已同步。**同日 B3 回填 StatusEffectTable 定稿(2026-09-08)**——schema 8 字段 + 四行 MVP 内容(Rule 4 豁免注明,键=effect_id),Core Rules 表行与 registry(status_effect 新条目)已同步;**同日 A1 回填 BoardGenTable 定稿(2026-09-09)**——九字段 schema + F2/F2b 跨字段谓词 + solver_rules 冻结 + MVP 参考行(provisional,C3 调参所有权),首个进度键表,Core Rules 表行与 registry(board_gen_table 新条目)已同步;**同日 A1 full 评审 delta(2026-09-09,owner=a1-board-generation.md,8 blocking 同日修订)**——BoardGenTable inferability_target 域收窄 [0.85,1](P1 绝对锚)+ fog_target_ratio 绝对上限 min(8d, 0.25) + 布雷数下限 round(d×(W×H−9)) ≥ 1 + F2c 成本谓词(attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生)+ elite_/boss_ 命名空间预留(第三轴登记约定,Boss=Dmax 行 + A1 OQ6 重审门),Core Rules 表行与 registry 已同步。**同日 B2 回填定稿(2026-09-09)**——EnemyTable 八字段 + 双敌种行(provisional)+ 每行集恰一敌种校验、HeroTable 新增(单行全局两字段,接口漂移),Core Rules 表行与 Dependencies 行已同步,registry(enemy / hero / clause3_supply 新条目)已登记。**同日 B4 回填定稿(2026-09-09)**——RewardTable 八字段 + 跨字段校验族 + MVP 内容行(attack_up 死行不入池)、use_timing 占位转承重、首个 effect_id 引用回填激活 StatusEffectTable 悬空校验、reward_roll scope 语义改写(每次 board install 批)、Tuning Knobs"奖励池 5 类"占位参考值删除、Dependencies B4 行与双向一致性 B4←D2 修订,Core Rules 表行与 registry(reward_table 新条目 + 三处漂移改写)已同步。**同日 B4 full 评审 delta(2026-09-09,owner=b4-reward-item-economy.md,14 blocking 同日修订)**——RewardTable 增 display_name(九字段)+ 默认根行集登记(五行为,provisional 归 C3)+ **RewardConfigTable 新增**(单行全局四字段 t_fog_min/k_dmg/n_draft/p_min,接口漂移——B4 标量 knob 的 Rule 1 载体)+ **Core Rule 5 reward_roll 语义正文补同步**(原定稿批仅 registry/Dependencies 行改写,正文两处漏网——实现者按旧正文接线会使 B4 σ 轨迹 AC 报废),Core Rules 表行与 Interactions/Dependencies 行与 registry(reward_config 新条目)已同步。D2 自身规则零改动,schema 类修订经接口漂移规则) +> **Author**: user + agents(lean 门禁:systems-designer、qa-lead;full 评审 + 同日 re-review 2026-09-07:game-designer、systems-designer、qa-lead、creative-director,修订均由评审结论直接推导) +> **Last Updated**: 2026-09-07(re-review 修订版 · 评审日志:`design/gdd/reviews/d2-data-config-review-log.md`) +> **Implements Pillar**: P1、P2(基础设施层,间接支撑全部四支柱) +> **Layer**: 1 Foundation · **Milestone**: MVP · **设计顺序**: 第 1 位 +> **Creative Director Review (CD-GDD-ALIGN)**: re-review 终裁 NEEDS REVISION(末轮小修)→ 修订落实 → 独立 delta 核对 5/5 PASS → Approved(2026-09-07) + +## Overview + +D2 是游戏的数据底座:把所有影响玩法手感与平衡的数字——行动点、旗子数、奖励数值、敌人属性、成长曲线、变异参数——从代码中搬出,变成可配置、可校验的数值表与配置资源;同时提供两样基础设施:统一的"数值从哪读"注册表(各系统只向 D2 要数据,不再各自硬编码),以及随机数种子服务(RNG 由 D2 统一发放,是战斗可复现、可回放的前提)。 + +玩家不会直接"使用"D2,但每一局都在感受它:难度能否做到"越下越难",靠它第一天就内置的关卡序号 / 节点深度两个维度;每次平衡调整无需改代码即可生效,让"测试数据驱动平衡"成为本作的开发方式——这是个人开发在资产与人力受限下仍能保证手感稳定的底牌。 + +D2 遵循既定的"**schema 先行、数值回填**"策略:本 GDD 只定数据结构、注册表与校验模式,具体数值留空位,由后续系统 GDD(A3 行动经济、B4 奖励经济、C3 难度曲线等)按设计顺序回填。没有 D2,参数将散落各处硬编码:调一次难度要改五处代码,战斗无法用固定种子复现,headless 自动化测试无从谈起。 + +## Player Fantasy + +D2 没有专属的玩家幻想——它是玩家永远不该注意到的地基。正面的情绪目标由其余系统承载:P1"我算对了"的掌控感来自 A1/A2 的可推理设计,P2 的延迟兑现感来自 B1 的可见结算。D2 对体验的贡献是三种**消极保障**——它不制造情绪,但它的失职会直接摧毁这些情绪: + +- **手感稳定**:所有影响手感的数字(AP、旗子、折算比例、奖励值)集中一处可调。任何"这轮玩起来不对劲"都能不改代码地快速修正、反复验证——玩家感受到的是"每次迭代都更顺",而不是改动引发的新毛刺。 +- **公平可信**:RNG 统一种子化,相同的决策序列必然得到相同的战斗结果。可复现是开发侧的信任底线(回放/审计/自动化测试),**不等于玩家侧可推理**——因此 Foundation 层不为任何玩法随机预置基础设施:玩法系统要引入随机,须走增流通道并通过 P1 评审(见 R4)。 +- **难度可信**:数值表天生带关卡序号/节点深度维度,"越下越险"(P4)由结构化数据保证,而非散落各处的魔法数字——难度曲线因此可审计、可回溯。 + +## Detailed Design + +### Core Rules + +1. **单一事实源**:所有设计师可调的玩法数值只存在于 `data/tables/` 下的 `.tres` 资源;代码中禁止出现玩法数值字面量(引擎/工程常量除外)。违反视为评审阻塞项。 +2. **单点访问**:各系统经 autoload **`ConfigDB`** 的类型化属性读取(例:`ConfigDB.action_economy.ap_per_round`);禁止绕过 ConfigDB 自行 `load()` 表文件或长期缓存副本(保证校验单点与热重载一致性)。唯一例外:debug/测试构建的**注入根**(见校验双通道)。 +3. **Schema 先行、数值回填**:每个数值表 = 一个自定义 Resource 类(`data/schemas/`,字段带类型/范围/默认值/注释)+ 数据文件(`data/tables/`)。本 GDD 交付全部 schema 与占位表(含键结构);数值由 owner 系统 GDD 回填: + +| 表资源 | Schema 占位内容 | 键结构 | Owner(回填 GDD,设计顺序位) | +|---|---|---|---| +| ActionEconomyTable | **已回填定稿**(2026-09-07 A3 三次回填(RAP 结构修订),字段全集/类型域见 A3 GDD "ActionEconomyTable Schema 定稿"节):ap_per_round / dig_ap_cost / flag_place_ap_cost / flag_pull_ap_cost / rap_refresh_cost(knob)+ move_ap_cost / chain_ap_cost(冻结)。~~flags_per_round / flag_unit_cost / stop_multiplier / clear_multiplier~~ 已随 A3 停手→RAP 修订删除(旗无上限、折算乘数作废);**跨字段校验(同日 A3 三轮 delta)**:任一动作成本 ≤ ap_per_round,schema validate() 跨字段类别承载、违规致命,条件性夹具登记校验双通道 | 单行全局 | A3(第 2 位)✓ | +| VariationTable | **已回填定稿**(2026-09-08 A4)——schema 8 字段:variant_id(主键)/ display_name / unlock_level(int [1,999],knob)/ scope(region/global,global 预留 C3)/ info_mask(none/hide_numbers)/ cell_constraint(any/safe_nonzero)/ biome_tag(∅=通配)/ counter_item_ids(A5 预留,非 knob);字段全集见 A4 GDD "VariationTable Schema 定稿"节。MVP 恰含 fog 行(unlock_level=2 · hide_numbers · safe_nonzero);岩浆/冰冻无 MVP 行(垂直切片建行)。**承重规则**:生成期布置约束——雾区仅非 0 安全格(区内无雷无 0 格,A1 生成期强制)+ 零特判原则。**Rule 4 豁免**:定义表非进度成长表,键=variant_id,进度语义承载于 unlock_level 列(schema 注释承载)。悬空引用政策:counter_item_ids MVP 不校验,A5 首个道具 id 回填时补校验 + 条件性夹具 | 按 variant_id(含 unlock_level 列)+ 跨字段 `info_mask=hide_numbers ⇒ cell_constraint=safe_nonzero`(加载期致命,2026-09-08 A4 re-review 增,夹具 `variation_mask_constraint` 承载)✓ | A4(第 3 位)✓ | +| StatusEffectTable | **已回填定稿**(2026-09-08 B3)——schema 8 字段:effect_id(String 主键唯一)/ display_name / target_side(hero/enemy)/ magnitude(percent ∈ [-100,100] ∧ ≠0 / flat ≥ 1,跨字段校验)/ unit(percent/flat)/ duration_settlements([1,99])/ stack_policy(stack/refresh/independent)/ max_stack([1,16],仅 stack 消费)。MVP 四行(attack_up/defense_up/ambush/weaken,全 stack,值 provisional——锚验收归 B4/B2/B1,weaken 有条款 2 锚红色预警登记 registry)。**Rule 4 豁免**:定义表非进度成长表,键=effect_id(schema 注释承载)。悬空引用政策:effect_id 校验**已激活**(2026-09-09 B4 RewardTable 首个引用行〔defense_up〕回填——条件性夹具 B4 b4_dangling_effect) | 按 effect_id | B3(第 4 位)✓ | +| BoardGenTable | **已回填定稿**(2026-09-09 A1;同日 full 评审 B1/B3/B4/B7/B8 delta 回写)——schema 九字段:board_width/height([8,64])/ mine_density((0,0.5],密度浮点;布雷数下限 round(d×(W×H−9)) ≥ 1)/ inferability_target(**[0.85,1]**,P1 门槛+绝对锚——B1 修订收窄)/ solver_rules(single\|single_subset,**冻结 single_subset**)/ min_safe_count([1,999])/ max_gen_attempts([1,32])/ fog_region_count([0,32])/ fog_target_ratio([0,1),**≤ min(8d, 0.25) 绝对上限——B1 修订**)/ board_biome(StringSet 非空)+ elite_/boss_ 前缀字段命名空间预留(第三轴登记约定 B7——语义归 C1/C3,本 GDD 不定义)。**跨字段谓词**:fog_domain_ok(F2)+ min_safe_feasible(F2b)+ **cost_feasible(F2c,attempts×W×H ≤ 16384 provisional——B8,运行期禁墙钟逃生)**加载期致命。MVP 参考行 L1/L2 provisional(C3 调参所有权)| (level_index, node_depth)——**首个进度键表**(depth-0 锚行,D2 Rule 4/6 内建;第三轴落点 = Boss:Dmax 行约定 + elite 命名空间预留 + A1 OQ6 重审,改键退路 = Rule 4 接口漂移通道)| A1(第 5 位)✓ | +| EnemyTable | **已回填定稿**(2026-09-09 B2)——schema 八字段:enemy_id(行集内主键)/ display_name / unit_count(int [1,3] knob)/ hp / atk / heavy_multiplier([1,16] knob)/ pattern(StringList,MVP 校验域 {attack, charge, heavy}——guard/boost/hinder/summon 预留值校验拒绝,环长 [1,8])/ advantage_per_round_cap(int [0,16] knob,**默认 4 定稿**);字段全集见 B2 GDD Schema 节。MVP 双敌种行 grub/ambusher(值 provisional,溯源义务见 D2 knobs)。**表结构校验:每行集恰一敌种**(行集内 enemy_id 唯一,违规加载期致命);本表为进度成长表(A1 BoardGenTable 同型,无 Rule 4 豁免) | (level_index, node_depth, enemy_id)——每行集恰一敌种 | B2(第 7 位)✓ | +| HeroTable | **新增回填定稿**(2026-09-09 B2,接口漂移;2026-09-11 基础攻击裁决改写)——schema 两字段:max_hp(int [1,999] knob,MVP 值 provisional 出处待查)/ atk(int **[1,999]**,**0 = 加载拒绝**——基础攻击恒存在、"0 基线 × 百分比增益恒零"死锁自 schema 层杜绝,MVP = 6 原型锚 provisional;消费面 = 主角战斗段先手攻击——B1 GDD 2026-09-11 增补)。**单行全局**(Rule 4 豁免,ActionEconomyTable 先例) | 单行全局 | B2(第 7 位)✓ | +| RewardTable | **已回填定稿**(2026-09-09 B4;同日 full 评审增补 display_name → 九字段;2026-09-11 两级奖励批 category 扩容)——schema 九字段:reward_id(主键)/ category(enum buff/damage_item/heal_pack/instant_aoe/instant_heal——两类交付轴:即时类 {buff, instant_aoe, instant_heal} 当场兑付不进库存 / 可选类 {damage_item, heal_pack} 入关卡域库存,预留值校验拒绝)/ effect_id(buff 类必填,StatusEffectTable 精确引用;非 buff 恒空)/ **display_name(String 非空——D1 呈现数据源,本地化归 D6;B2/B3 同库惯例)** / power(int [0,999] knob;item 类 ≥1 加载校验,buff 类不消费非校验)/ weight(int [0,999] knob;0 = 不入池)/ pool_tag(safe_cell/clear_default,clear_\ 命名空间预留拒绝)/ tier(int [1,8] knob,A4 雾档约束载体)/ use_timing(MVP 单值 pre_settlement,**承重**——道具使用窗口 MVP 激活,扩枚举走修订);字段全集见 B4 GDD Rule 9。MVP 内容:**默认根行集已登记**(knife_t1/knife_adv/heal_t1/defense_up_basic/clear_knife——B4 GDD Formulas 节,值 provisional 归 C3;attack_up/defense_up/spark_aoe/heal_minor/knife_t1/knife_adv/heal_pack 八行默认根 = 2026-09-11 两级奖励批登记——即时 75% / 可选 25%)。**跨字段校验族(加载期致命)**:池空(pool_tag × 雾/非雾两态各 ≥1 活行)· clear_default 恒正当量 · buff⇒effect_id 必填 · safe_cell 活 buff 行集非空。**Rule 4 豁免**:定义表非进度成长表,键=reward_id(schema 注释承载)。**首个 effect_id 引用已回填(defense_up)→ StatusEffectTable 悬空校验激活** | 按 reward_id(多行;pool_tag 分池不参与键) | B4(第 8 位)✓ | +| RewardConfigTable | **新增回填**(2026-09-09 B4 full 评审,接口漂移)——schema 四字段:t_fog_min(int [1,8],默认 2)/ k_dmg(int [0,999],默认 1;0 = F5 断言关闭,C3 禁区默认值)/ n_draft(int [1,999],默认 32)/ p_min(int [1,999],默认 8 = 默认根最低刀 power——2026-09-11 批)。**单行全局**(Rule 4 豁免,HeroTable/ActionEconomyTable 先例)——B4 三个标量 knob 的 Rule 1 单一事实源载体(R3 override key 空间随之存在) | 单行全局 | B4(第 8 位)✓ | +| SettlementTable | **已回填定稿**(2026-09-10 B1)——schema 七字段:equivalence_ref_damage(int [1,999] knob 默认 12——当量参考敞口 D_ref;变更触发 B1 全对照表 + 条款③⑤对账重跑)/ p_perfect(int [100,999] knob 默认 200——完美排雷乘数)/ segment_order(StringList 结构冻结——恒等拍板段序校验**违例致命**)/ source_label_map(Dictionary source_class→文案键,结构冻结)/ effect_pool_map(Dictionary effect_id→pool,结构冻结;**键 ⊆ StatusEffectTable 活行集、悬空加载期致命**)/ pool_clamp_lo_hi(−100/+100 结构冻结)/ round_direction(floor 结构冻结);字段全集见 B1 GDD Tuning Knobs 节。**Rule 4 豁免**:单行全局常量/结构表(HeroTable/ActionEconomyTable 先例)。三行致命 manifest:segorder_violation / poolmap_dangling / pperfect_oob | 单行全局 | B1(第 9 位)✓ | +| DifficultyTable | 成长参数 + **精英/Boss 修饰预留命名空间** | (level_index, node_depth) | C3(第 12 位) | +| HelperItemTable | 探测/安全旗参数 | 单行全局 | A5(第 11 位) | +| MetaTable | 代币/天赋字段名 | 单行全局(元进度轴豁免,C4 可改键) | C4(第 16 位) | + +4. **进度维度内建**:凡随**冒险内**进程成长的基础数值表,行键必须是 `(level_index, node_depth)`;实体类表在其后复合实体主键列(如 EnemyTable 的 enemy_id)——进度键 × 实体键的合成规则写入 schema 注释。**解析语义(同名规则写入 schema 注释)**:进度键按 R1 floor 解析;实体键在解析行内**精确匹配**、不参与 floor——解析行不存在该实体键 → Rule 6 悬空引用致命(floor 近似只适用于进度轴,身份轴近似会静默改变遭遇构成、损害 P2 归因)。**其他成长轴不在此体系内**:元进度(存档/天赋/代币)归 MetaTable(键归 C4 定);胜场成长、轮次成长等战斗内轴由 owner 公式承载或作为表列,归属须在 schema 注释中登记(C3 定稿时声明)。难度公式的第三维"**精英/Boss 修饰**"(game-concept 定稿:难度 = f(关卡序号, 节点深度, 精英/Boss 修饰))在 DifficultyTable 预留 `elite_` / `boss_` 前缀扩展字段命名空间,语义由 C3 GDD 定稿——本 GDD 不定义任何修饰数值、不预写修饰枚举。若 C1/C3 定稿的节点形态使精英性无法由 (level_index, node_depth) 唯一确定(如分支地图同深度并存精英与普通节点),允许改键,按接口漂移修订规则回写本表(与 MetaTable 同等豁免待遇)。无成长语义的全局常量表允许单行。 +5. **RNG 单点**:autoload **`RngService`**(D2 范围)持有封闭命名流:`board_gen`(A1 布雷)、`reward_roll`(B4 掉落)。**流集合当前封闭且只含具名消费者**;玩法系统要新增随机流(含 B1 若需结算随机)须改代码走增流通道,并通过 P1 支柱评审(随机须有界、可见或可推理)。玩法代码禁止 `randomize()` 或自建 RandomNumberGenerator。master seed 在冒险开始时由 C2 调用设定;取数走**有状态流门面** `open_stream(stream) -> RandomNumberGenerator`(每次调用 = 一次 scope 取号:计数器 +1 → 派生 → 返回已播种实例,一个 scope 事件内所有取数共用该实例);scope 取号事件唯一绑定:board_gen = 每次生成新扫雷图(A1 换图时)、**reward_roll = 每次 board install(格奖励批 + 清图奖励批)**(B4 Rule 2 接口漂移改写,2026-09-09 定稿——原"每次奖励结算 roll 批次"作废;B4 AC-1 为契约源;R2 黄金向量为 (stream, master, scope) 纯函数、不受取号时机变更影响,注记防误解)。测试/预览用**纯函数** `derive_at(stream, scope)`(不推计数器,杜绝预览类取数的平衡 Heisenbug)。 +6. **失败即响(fail fast)**:错误分两类,一律致命中止;MVP 有意不做软回退——错误数值上线比崩溃更伤(P1/P2 的信任底线)。 + - **加载期校验错误**(字段越界/类型错/NaN/表为空或某 level 无行/**缺 depth-0 锚行**/重复键/悬空引用/权重和 ≤0):经校验双通道处理,启动路径在 editor、CI、headless、release 一律致命;**唯一例外**:debug 热重载校验失败保留旧表停留 Ready(States 表 / EC16)。 + - **运行期致命**(**请求越域**=R1 前置守卫、**未播种流取数**=EC7):不分构建一律致命——游戏内走致命错误路径,headless 冒烟进程 quit 非 0。 +7. **零 UI 依赖**:D2 及其测试不 import 任何 Control/场景类;"Core 层 `--headless` 跑完整战斗"工程债在数据侧的落实。 +8. **版本化**:`SCHEMA_VERSION` 常量归 D2;表文件与存档(D3)携带版本号,高版本拒读。 + +**校验双通道(Rule 6 的执行规格)**:ConfigDB 暴露 `load_tables(root: String, collect_only: bool)`。 +- **游戏通道**(`collect_only=false`,默认):校验错误 → push_error 错误清单 → 走致命错误路径(游戏内中止构建;headless 下独立进程非 0 退出——`--script` 协议,设计顺序 #1 时无主场景可依赖)。release 构建的致命表现(引擎默认错误路径的实际行为验证)显式 deferred→首次 release 导出 checklist(与 AC-15 同待遇)。 +- **测试通道**(`collect_only=true`,仅 debug/测试构建):收集错误不升级,供冒烟脚本逐夹具断言错误类别后一次性退出。 +- **夹具注入**:debug/test 构建允许经命令行 `--data-root=<路径>` 让 ConfigDB 从注入根加载(夹具放 `tests/fixtures/`);**禁止**把夹具拷进 `data/tables/`(污染 AC-1 的干净检出前提)。 +- **诊断收集器(warning 可观测缝,re-review 增补)**:resolve / 校验 / override 的 warning 与错误共用同一类目体系,统一经 ConfigDB 内部**诊断收集器**投递,同时镜像 `push_warning` 供开发期控制台(Godot 无进程内拦截 `push_warning` 的 API,冒烟断言必须走收集器)。测试通道暴露只读诊断清单与 `reset_diagnostics()`;跨调用去重状态(R1 四元组注册表)随 `load_tables()` 或 `reset_diagnostics()` 清空。游戏通道 warning 仅镜像控制台,不承诺收集。 + +### States and Transitions + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Unloaded | 进程启动前 | 磁盘 `.tres` 未加载 | +| Loading | `ConfigDB._ready()` 或 `load_tables()` | 按表清单逐一 `load()`;文件缺失即入 Failed | +| Validating | 单个资源加载成功 | 调 schema 的 `validate()`,错误入收集器 | +| Ready | 全部通过 | 类型化属性可读 | +| Failed(启动路径) | 启动加载任一致命错误 | 全构建致命中止并输出错误清单(Rule 6) | +| Failed(reload 路径) | 热重载校验失败 | **保留旧表**停留 Ready + debug 错误清单(不杀运行中的调试会话) | +| Reloading(仅 debug) | 编辑器热重载/调试键 | 战斗中拒绝(返回 false,战斗结束后**手动**再触发);否则重走 Loading → Ready | + +**RNG 与 ConfigDB 状态线相互独立**:Reloading/Failed(任一路径)不触碰 RngService——计数器与 master_seed 不变(派生只依赖盐/master_seed/scope,与表数据无关;若按状态机对称性在 reload 时重置 RNG 将击穿 AC-4)。 + +RNG 生命周期:进程启动 → 流待命(**未播种时取数即致命**)→ 冒险开始设定 master_seed → 每次具名 scope 事件派生种子 → 冒险结束保留供复盘;**续玩/复盘快照 = master_seed + 各流 scope 计数器**(仅记录计数器、缺 master_seed 不足以恢复序列——进程重启会从种子序列起点重放;见 D3 接口)。**快照切出须发生在无在飞 `open_stream` 实例时**(scope 事件边界之外):门面实例作用域限定于单次 scope 事件、消费者不得跨事件持有——在飞实例的内部取数进度不入快照,违反则续玩/复盘与实况分叉(AC-15 失效),该前提由 D3 消费侧遵守。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| 全部其他 19 系统 | 它们 → D2 | ConfigDB 类型化读取;禁止其他取数路径(A2 经 A3 表间接承载参数) | +| A1 | 消费 | VariationTable schema(biome/tag 字段)+ `board_gen` 流(换图时取号) | +| A3 / A4 / B2 / B3 / A5 / D1 | 消费 | 各自表 schema 占位(见 Core Rules 表) | +| B4 | 消费 | RewardTable schema(九字段,use_timing 承重 MVP 单值)+ **RewardConfigTable**(单行全局,2026-09-09 B4 评审新增:t_fog_min/k_dmg/n_draft/p_min)+ `reward_roll` 流(scope = 每次 board install 批——格奖励批 + 清图奖励批,B4 Rule 2 改写) | +| B1 | 消费 | SettlementTable schema(可回放步骤流的数据前提);另有 debug 热重载门标志 `set_reload_gate()`(战斗开始置位/结束复位,仅 debug 构建) | +| C2 / C3 | 消费 | `RngService.set_master_seed()` + DifficultyTable 行解析 | +| D3 | 消费 | `SCHEMA_VERSION` + 存档内 **master_seed + 各流 scope 计数器** 快照(续玩/复盘;含只读计数器接口,快照须在无在飞实例时切出) | +| D2 外发 | → | 不订阅任何玩法事件;仅 debug 构建发 `config_reloaded`(D1 调试用) | + +双向一致性:systems-index 依赖表中显式以 D2 为依赖方的系统(A1、B2、C3)与本节一致;其余系统的数据读取为**事实依赖**(索引层表的"无"指玩法依赖)。已知口径待修 → **已修(2026-09-07 同日索引同步)**:C2←D2(master_seed 唯一接线方)、B1←D2(set_reload_gate 调用)、B4←D2(reward_roll 流)、C4←D2(MetaTable 消费)四条边已补入索引依赖分层表;数据读取类事实依赖不逐条列边(完整清单以本 Dependencies 表为准)。D2 不依赖任何玩法系统(依赖层 1:无)。 + +## Formulas + +> systems-designer 代理已复核(lean 门禁 + 2026-09-07 full 评审 + 同日 re-review);scope 重置撞种子、hash() 稳定性、溢出论证、INT64_MIN 别名等问题已修正并入;re-review 增补:missing_type 分类规则、负 scope / 重播种守卫、掩码别名机理。 + +### R1 难度行解析(difficulty_row_resolution) + +`row = resolve(table, level_index, node_depth)`(resolve 为 ConfigDB 公开解析方法,table 经类型化属性取得) + +- **前置守卫**:`level_index < 1` 或 `node_depth < 0` → Rule 6 致命(请求越域,不进解析)。 +- `L* = max{ lv ∈ LevelKeys(table) | lv ≤ level_index }`;空集(请求低于表最小键,向上解析)→ `L* = min LevelKeys(table)` +- `D* = max{ dp ∈ DepthKeys(L*) | dp ≤ node_depth }`;空集 → `D* = min DepthKeys(L*)` +- 非精确命中记 warning,**missing_type ∈ { level_floor, depth_floor, both, base_ceil }**。**分类规则**:level 分量为向上解析(请求低于表最小键)时恒记 `base_ceil`(depth 分量缺失与否只写入 warning 内容,不改类型);`both` 专指 level_floor 与 depth_floor 同时发生;`level_floor` / `depth_floor` 为单维向下解析;depth 分量只有精确命中或向下 floor 两种情形(EC15 锚行保证每个 level 含 depth-0,永不 ceil)。AC-5 各用例的 missing_type 均由本规则**推导**得出。**去重键 = (table_id, missing_type, L*, D*) 四元组**,内容含请求 (level_index, node_depth)、解析结果 (L*, D*)、缺失类型;**去重注册表随每次 `load_tables()` 或 `reset_diagnostics()` 清空**(用例间无顺序依赖)。 +- warning 定位为**开发期异常信号**(防刷屏去重是刻意的)——它不是难度审计工具;难度曲线的离线审计由 **曲线导出工具**(headless 脚本导出 resolve(level 1..N × depth 0..M) 矩阵供 diff)承担,登记为 C3 期交付物。 +- **不存在 default 行兜底、不存在 clamp 外推**:表非空、每个 level 至少 1 行、每个 level 含 depth-0 锚行是 Rule 6 校验前置;超表成长外推是 C3 owner 公式的职责,D2 只解析到最近已有行。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| level_index | int | ≥1 | 当前关卡序号(1 起);越域 → 致命 | +| node_depth | int | ≥0 | 节点深度(0=关卡入口);越域 → 致命 | +| L* / D* | int | 表内键 | 命中行键 | + +**Output Range:** 行资源必有值(前置条件由校验保证);极端输入(超表大关卡号)命中最大已有行。 +**Example:** 表含行 (1,0) (1,4) (2,0) (2,6);`resolve(t, 3, 3)` → L*=2、D*=0 → 返回 (2,0) 行,warning 一条(missing_type=both);`resolve(t, 1, 4)` → 精确命中,零 warning;`resolve(t, 99, 99)` → 命中 (2,6),missing_type=both(与前者去重键不同,各自一条)。 + +### R2 流种子派生(stream_seed_derivation) + +**纯派生函数**(可测试、可复现、供预览): + +``` +derive_at(stream, scope) = ( SALT_s XOR wrap64(master_seed + scope × GOLDEN_RATIO) ) AND 0x7FFFFFFFFFFFFFFF +``` + +**实现级规约(必须逐条遵守,否则黄金向量失配):** +- `wrap64` = GDScript int(64 位二补数)的**自然回绕**,即 MOD 2^64。**禁止 float 中间量**(53 位尾数必丢精度)、**禁止对中间量使用 `%`**(带符号取余语义易误用)、**禁止"主动修复溢出"**(中间积本就越界——乘数 GOLDEN_RATIO = 0x9E3779B97F4A7C15 > 2^63,scope=1 即回绕,这是设计依赖的语义,不是缺陷)。 +- 三个常量(盐 ×2 + 乘数)均 ≥2^63,GDScript 十六进制字面量按二补数解析为负——**统一用分段表达式书写**(例:`(0x9E3779B9 << 32) | 0x7F4A7C15`,显式括号、不依赖优先级记忆),禁止直接书写 ≥2^63 的完整字面量。注意 GDScript 的 `^` 是按位 XOR。 +- 输出经 AND 掩码保证非负(∈ [0, 2^63))后再赋 `RandomNumberGenerator.seed`。 +- 复现保证的准确表述:**同一冒险内(master_seed 固定)同流各 scope 种子互不碰撞**;同输入恒同输出;跨 Godot 版本恒定(因此不用运行时 `hash()`——其跨版本稳定性无承诺)。跨 master_seed 的全局单射不做声明(数学上由鸽笼原理排除)。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| SALT_s | int64 | 编译期常量 | board_gen=0xA0761D6478BD642F · reward_roll=0xE7037ED1A0B428DB(互异奇常数,均 ≠ 乘数) | +| master_seed | int64 | [0, 2^63) | 冒险主种子;`set_master_seed()` 对负输入 abs() 归一化并 warning;**恰为 INT64_MIN → 致命**(abs 在 int64 对 INT64_MIN 恒等回绕,实测与 0 在全流种子逐一相等——别名机理是输出掩码抹掉 bit 63,掩码本身是规约的一部分);**仅许两流计数器全零时调用**(冒险中途重播种会静默清零计数器,击穿同冒险无碰撞前提与 D3 快照语义 → debug 断言) | +| scope | int64 | ≥0 | 派生入参;有状态门面的取号值(每流独立计数器:set_master_seed() 时归零,每次 `open_stream` +1,永不重置、不随关卡/图序重置——否则不同关卡的同序号图会得到相同种子);**负 scope → debug 断言中止**(`derive_at` 是预览工具入口,静默负值会与 scope+2^63 经输出掩码别名) | + +**黄金向量(AC-3 的常量依据,经独立复算验证):** + +| 流 | master_seed | scope | stream_seed | +|---|---|---|---| +| board_gen | 42 | 0 | 2339089373825164293 | +| board_gen | 42 | 1 | 4485977604321908752 | +| board_gen | 42 | 2 | 2024629813010603131 | +| board_gen | 42 | 3 | 8849696722413293638 | +| reward_roll | 42 | 0 | 7422916049646135537 | + +**Output Range:** [0, 2^63)。 +**Example:** 同流同 scope 重复调用 `derive_at` 恒等(纯函数);同帧对同一流连续 `open_stream` 两次 → 计数器各 +1,得两个互异且顺序确定的种子。 + +### R3 调试覆盖链(debug_override,仅 debug 构建) + +`effective = override[key] if _has_override(key) else table_value` + +边界五条:① 覆盖判定与存储内聚于 ConfigDB(Rule 2 单点),gameplay 不可见 override 来源;② **key = schema 字段点路径**(与 Rule 2 属性路径同构,例 `action_economy.ap_per_round`),未知 key → debug warning + 忽略该条;**v1 仅支持单行全局表字段**——进度键表的行寻址语法(如 `difficulty.(2,0).hp`)登记为后续扩展,当前 override 对进度表无 key 空间;③ override 值**类型与范围**必须与 schema 字段一致,不一致 → debug warning + 忽略该条(复用 schema 校验逻辑,防止 debug 会话注入越界值污染平衡基线);④ release 构建不加载、不注册任何 override 源(恒等是结构保证而非"碰巧不生效");debug 谓词实现为可注入形式(`OS.is_debug_build()` 之外引入 feature 标签闸,例 `sim_release`),AC-8 的模拟以此为基础;⑤ override 仅为运行时状态,永不回写 `.tres`。 + +### R4 显式排除 + +玩法平衡公式(伤害、掉落权重、成长曲线)**不在 D2**——归 owner 系统 GDD(B1/B4/C3 等)。本 GDD 不含任何平衡数值公式,只负责取数与解析。**玩法随机同理**:任何系统若需玩法随机(如结算浮动),自带方案走 Rule 5 增流通道并通过 P1 评审,D2 不预置。 + +## Edge Cases + +- **EC1 If 表文件缺失或 load() 失败**:致命中止,错误清单含路径;无软回退(Rule 6)。 +- **EC2 If 字段越界 / NaN / 类型不符**:`validate()` 收集后致命。 +- **EC3 If 表为空或某 level 行集为空**:校验阶段致命(R1 前置条件),不会进入运行期。 +- **EC4 If 请求 (level_index, node_depth) 无精确行**:按 R1 floor 解析 + warning(按四元组去重)——预期行为,不是错误。 +- **EC5 If level_index 超过表最大 level**:命中最大 level 行(floor 规则的自然结果,无独立 clamp 分支)。 +- **EC6 If master_seed 为负**:abs() 归一化 + warning,保证 ∈ [0, 2^63);**恰为 INT64_MIN → 致命**(abs 恒等回绕,与 0 静默别名——机理是输出掩码抹掉 bit 63)。 +- **EC7 If 未设 master_seed 就取随机数**:`push_error` 后**显式中止**(GDScript 的 push_error 本身不终止进程——游戏内走致命错误路径,冒烟脚本 `quit(1)`),取到的值不可用——这是 C2 未接线的显式暴露,禁止 seed=0 软回退。 +- **EC8 If 战斗进行中请求热重载**:拒绝并返回 false;战斗结束后**手动**再触发(不自动重放)。 +- **EC9 If 存档携带的 SCHEMA_VERSION 高于当前**:拒绝读取(比较逻辑归 D3,常量归 D2)。 +- **EC10 If 奖励权重表权重和 ≤ 0**:validate 致命(B4 回填后必须 > 0)。 +- **EC11 If debug override 类型或范围与 schema 字段不符**:warning + 忽略该条(仅 debug;release 无 override 源)。 +- **EC12 If 同一流名被重复注册或请求未知名流**:debug 断言中止(流名封闭)。 +- **EC13 If 两个系统同帧请求同一流**:允许——`open_stream` 各自取号(计数器各 +1)按调用顺序串行生效,单线程假设成立(Godot 主线程玩法逻辑),不引入锁。 +- **EC14 If 请求越域(level_index < 1 或 node_depth < 0)**:Rule 6 致命(R1 前置守卫)。 +- **EC15 If 某 level 行集缺 depth-0 锚行**:validate 致命(防"入口天灾":入口请求静默 floor 到深行造成难度突刺)。**写死开、无运行期开关**;C3 如需重议,走对本 GDD 的显式修订流程(非 knob)。 +- **EC16 If 热重载校验失败**:保留旧表停留 Ready + debug 错误清单(见 States 表 reload 路径)。 + +## Dependencies + +**Upstream(hard)**:无。Foundation 层,D2 不依赖任何系统。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| 全部其他 19 系统 | hard(Rule 1 单一事实源;A2 经 A3 表间接) | ConfigDB 类型化读取 | +| A1 | hard | **BoardGenTable 回填(首个进度键表,2026-09-09 A1 定稿)** + VariationTable schema(biome/tag 字段)+ `board_gen` 流 | +| A3 | hard | ActionEconomyTable schema 回填 | +| A4 | hard | VariationTable 回填 | +| B2 | hard | EnemyTable 回填(**2026-09-09 定稿**:八字段 + 双敌种行 + 每行集恰一敌种校验)+ HeroTable 新增(接口漂移回写) | +| B3 | hard | StatusEffectTable 回填 | +| B4 | hard | **RewardTable 回填(2026-09-09 定稿:九字段〔评审增补 display_name〕+ 跨字段校验族 + 默认根行集)+ RewardConfigTable 新增(2026-09-09 评审,单行全局)** + `reward_roll` 流(scope 语义 = 每次 board install 批——B4 Rule 2 接口漂移,registry/D2 正文同步) | +| B1 | hard | SettlementTable 回填 + debug `set_reload_gate()` | +| C2 | hard | `set_master_seed()` 调用方(冒险开始) | +| C3 | hard | DifficultyTable 回填 + R1 行解析 + 曲线导出工具交付物 | +| A5 / C4 / D3 | hard(垂直切片 / Alpha) | HelperItemTable / MetaTable 回填;D3 另消费 SCHEMA_VERSION + master_seed/计数器快照 | +| D1 / D4 / D5 / D6 | soft | 如需参数表再增 schema(现无占位) | + +- A2 无直接依赖(只外发事件;其参数经 A3 表承载),与 systems-index 的 A2 回边规则一致。 +- **接口漂移修订规则**:owner GDD 定稿时可以修改占位 schema 类(schema 与表同仓),但必须回写本 GDD 的 Core Rules 表清单与 `design/registry/entities.yaml`,并在本文件 Status 行登记修订。**占位参考值定稿时必须删除本 GDD Tuning Knobs 清单中的对应条目**(值的事实登记归 owner GDD 与 registry)。 +- 双向一致性:与 systems-index 依赖分层表核对一致(A1←A4,D2 · B2←D2 · B4←D2〔2026-09-09 B4 定稿修订:原"A2,B3,D2"收窄——A2 事实经 B1 转化零直接接口、B3 为验收锚关系非运行时边,B4 GDD Dependencies 修订注记〕 · B1←A3,B2,B3,B4,D2 · C2←C3,D2 · C4←D3,C2,D2 · C3←D2,B2 · Foundation 含 D2);C2/B1/B4/C4 ← D2 四条边已于 2026-09-07 补齐(详见 Interactions 节)。已知待定:C3 是否消费 B1 输出(见索引门禁增补记录,C3 GDD 定稿,不影响 D2)。 + +## Tuning Knobs + +**D2 自身 knobs(元参数):** + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| floor 解析策略 | floor + warning(无外推) | —— | 引入外推/静默会掩盖 C3 难度断档 | +| rng_stream_names | 当前封闭 2 流 | 增流需改代码 + P1 评审(有意设卡) | 流复用破坏可复现隔离 | +| debug_override_enabled | false(仅 debug 构建) | 污染测试基线 | —— | +| 表路径清单 | `data/tables/` 固定(debug 注入根除外) | —— | 散装路径破坏单点访问 | +| depth-0 锚行校验 | **写死开(非 knob,Rule 6)** | —— | ——(重议须走本 GDD 显式修订,EC15) | + +- **Knob 交互**:floor 策略与 C3 的难度语义共享,改动需 C3 GDD 知情;校验严格度不设 knob(fatal 写死——放行带病数值会使 CI 冒烟失去意义)。 +- **占位表中的原型参考值**(provisional——owner GDD 定稿前不视为平衡事实;**定稿后删除对应条目**,事实登记归 owner): + - 出处=概念定稿/原型结论:标中 +5/面旗——**量纲与语义已由 A3 OQ5 重锚**(2026-09-07 两改:先"一次性兑付、当轮结算";同日三向效果定稿——**标中旗 = 怪物 debuff**,档位锚 = 严格 > 最低档安全格 buff 当量(A3 F1 条款 2);+5 为原型临时值作废),B4 定稿时登记;清图 +10 仍为临时锚(**用途已改**:A3 折算与 F2 不等式已随停手→RAP 修订作废,+10 降级为 B4 清图奖励插槽的 provisional 参考——A3 清图奖励为差异化插槽,按图类型分支归 B4) + - **出处待查(原型 REPORT,回填前须补溯源)**:伏击 +3(量纲待 B2 定:候选"每颗已开雷")· 清图 +10 · 敌 18HP / 4ATK + - **机制与数值均 provisional**:~~增伤 1.5× / 减伤 0.5× / 持续 2 次结算(B3)~~ **已随 B3 定稿删除(2026-09-08,同 A3 删除先例)**——StatusEffectTable 四行(attack_up/defense_up/ambush/weaken)事实登记归 registry `status_effect`(值仍 provisional,锚验收归 B4/B2/B1);胜场成长 +25% HP / +1 ATK(**机制去留本身待定**——反雪球问题,C3 须先决定)· ~~奖励池 5 类(伤害/增伤/减伤/AOE/治疗)~~ **已随 B4 定稿删除(2026-09-09)**——RewardTable 事实登记归 registry `reward_table`(category 三值封闭:buff/damage_item/heal_pack;AOE 类不入 MVP)。 + - owner:A3 / A4 / B3 ✓ / B4 / B1 / C3 按设计顺序回填。 + +## Visual/Audio Requirements + +无直接表现面(纯数据层)。仅两项开发侧"可视化":Godot 编辑器 Resource inspector(自带)与校验错误控制台输出(错误清单格式见 AC-1)。无音频需求。 + +## UI Requirements + +MVP 无面向玩家的 UI。debug override 与夹具注入均经 Godot 编辑器 / 命令行参数设置(`--data-root=`,仅 debug),不做自定义面板;校验失败在 editor/CI 输出控制台,release 构建走引擎默认错误路径(实际表现验证 deferred→首次导出 checklist)。 + +## Acceptance Criteria + +> qa-lead 代理复核(lean 门禁 + 2026-09-07 full 评审 + 同日 re-review):覆盖 8/8 Core Rules;Rule 6 **加载期**校验错误类别全部有夹具(悬空引用为条件性夹具),**运行期**致命(请求越域 EC14、未播种 EC7)由 AC-5b / AC-17 行为级覆盖;R1/R2 全部边界。除标注"人工/deferred"外全部可纳入 `--headless --script` 冒烟框架(脚本 extends SceneTree 以获得 quit(code);该模式下 autoload 须由脚本显式实例化——AC-1 前提声明)。**致命类断言协议统一**:致命用例(AC-2b、AC-5b 游戏通道半、AC-6 后半、AC-11、AC-17)各为**独立进程**调用,由冒烟 runner 清单逐条登记"调用 → 期望退出码";单进程内只做测试通道收集断言。warning 计数断言一律经诊断收集器(见校验双通道),每用例先 `reset_diagnostics()`。 + +**加载与校验(Rule 3/6)** +- **AC-1** GIVEN 干净检出,WHEN `godot --headless --script tests/smoke_data.gd`,THEN Core Rules-3 表清单的全部表(以清单为准,不写死数量)加载且 `data/tables/` 无清单外 .tres;脚本成功 `quit(0)`,任一失败 `quit(1)` 并输出错误清单(表路径 + 字段 + 错误类别)。 +- **AC-2** GIVEN `tests/fixtures/` 夹具集(越界字段/文件缺失/空表/某 level 无行/**缺 depth-0 锚行**/重复键/权重和 ≤0 各一,悬空引用为**条件性夹具**——首个含引用字段的 owner GDD 回填时补),WHEN 冒烟脚本经 `--data-root` 注入逐夹具运行 `load_tables(root, collect_only=true)`(测试通道,单进程逐个断言),THEN 每个夹具的错误清单含对应类别(EC1/EC2/EC3/EC15/EC10 及重复键类别)。 +- **AC-2b** GIVEN 任一致命夹具经注入根加载,WHEN 冒烟 runner 以**独立 headless 进程**执行游戏通道 `load_tables(root, collect_only=false)`(`--script` 协议,脚本显式实例化 autoload;设计顺序 #1 时无主场景,本 AC 不依赖主场景),THEN 进程非 0 退出且错误清单完整——验证"游戏内真的会死"而非仅"校验器能发现"。 + +**公式(R1/R2/R3)** +- **AC-3** GIVEN R2 黄金向量表(5 常量),WHEN 经纯函数 `derive_at` 复算(board_gen master=42 scope 0–3、reward_roll master=42 scope=0),THEN 逐个等于向量表常量;同流同 scope 重复调用 `derive_at` 恒等(纯函数无副作用)。 +- **AC-4** GIVEN master_seed=42,WHEN `open_stream("board_gen")` 连续取号 ≥100 次(跨模拟"关卡切换"),THEN 公开只读计数器单调不重置(各关同序号图种子互异);WHEN 同期调用任意 `reload()`,THEN 计数器不变(状态线独立)。 +- **AC-5**(每用例先 `reset_diagnostics()`)GIVEN **真实 DifficultyTable schema 构造**的表含行 (1,0)(1,4)(2,0)(2,6),WHEN `resolve(3,3)`,THEN 返回 (2,0) 行且诊断收集器 warning 恰一条(missing_type=both,含请求/结果/类型);WHEN `resolve(3,4)`,THEN warning 不新增(四元组去重键相同);WHEN `resolve(1,4)`,THEN 精确命中且零 warning;WHEN `resolve(99,99)`,THEN 命中 (2,6) 且新增恰一条(去重键含 D*,与首条不同);WHEN 单行表 {(2,0)} 上 `resolve(1,5)`,THEN 命中 (2,0) 且 missing_type=base_ceil(由 R1 分类规则推导:level 分量向上解析恒记 base_ceil)。 +- **AC-5b** GIVEN 真实 DifficultyTable schema 构造的合法表,WHEN `resolve(0, 3)`(level_index < 1)与 `resolve(2, -1)`(node_depth < 0),THEN 测试通道诊断清单各含"请求越域"致命类别一条且不进入解析(EC14 / R1 前置守卫);同用例经游戏通道独立进程复跑,THEN 进程非 0 退出。 +- **AC-6** GIVEN master_seed=-7,WHEN `set_master_seed()`,THEN 归一化为 7 + 诊断收集器 warning 一条,后续种子 ∈ [0, 2^63)(EC6);GIVEN master_seed=INT64_MIN(-2^63),THEN 致命(非 warning;独立进程断言退出码非 0);GIVEN `derive_at` 传入负 scope,THEN debug 断言中止(负值经输出掩码与 scope+2^63 别名)。 +- **AC-7** GIVEN debug 构建 override{"action_economy.ap_per_round"=12}(**域内值**——A3 schema 域 [1,32];原 99 示例出域,2026-09-07 A3 RAP 修订同批修正:R3 ③ 越域 override 会被忽略,99 会使本 AC 假失败),WHEN 经 ConfigDB 读取,THEN effective=12;GIVEN override key 为未知路径、类型或范围与 schema 不符,THEN 诊断收集器 warning + 忽略该条;GIVEN `reload()` 后,THEN .tres 文件内容不变(R3 边界①③⑤)。 +- **AC-8** GIVEN 非 debug 谓词模拟(feature 标签),WHEN `_has_override(key)`,THEN 恒 false——**本条仅证明门存在性**;"release 不加载 override 源"的结构保证验证 deferred→首次 release 导出 checklist。 + +**静态与架构(Rule 1/2/5/7/8)** +- **AC-9** GIVEN D2 文件集(data/schemas、ConfigDB、RngService、tests),WHEN 静态扫描(模式覆盖 `load(`、`preload(`、`ResourceLoader.load`、`"res://data/" +` 动态拼接;`res://tests/fixtures/` 前缀豁免——不在 `res://data/` 扫描域),THEN 无 `randomize()` 调用、无 ConfigDB 之外的 data 表加载(Rule 2/5)。**Rule 5 对全项目玩法代码的扫描登记为项目级 CI 项(Technical Setup 归口)**。 +- **AC-10** 同扫描无 `extends Control/Node2D`、无 `.tscn` preload,**且** AC-1 headless 实跑通过——两项合成判定 Rule 7(扫描是启发式,硬证据是 headless 实跑)。 +- **AC-11** WHEN 请求未知名流或重复注册流名,THEN debug 断言中止(EC12;独立进程退出码非 0)。 +- **AC-12** GIVEN 同帧对同一流连续两次 `open_stream`,THEN 两个互异且顺序确定的种子(EC13)。 +- **AC-13** WHEN 冒烟脚本运行,THEN 反射断言 `SCHEMA_VERSION` 常量存在且 ConfigDB 可读、全部 schema 携带版本字段、进度类表(BoardGen/Enemy/Difficulty)暴露 (level_index, node_depth[, enemy_id]) 键字段、DifficultyTable schema 暴露 `elite_`/`boss_` 前缀预留字段(Rule 8 + Rule 4 键契约 + 第三维命名空间;"高版本拒读"路径 deferred→D3)。 + +**人工与延迟项** +- **AC-14(人工)** Rule 1 数值字面量禁令:启发式扫描(warning 级,误报难免)+ **人工复核三要素**——验收人=owner GDD 回填定稿评审 + 里程碑 gate-check;时机=每次回填定稿;证据=评审记录/gate-check 登记。 +- **AC-15(deferred→B1)** 战斗级可回放:同 master_seed 同动作序列 → 结算步骤流逐行相同(B1 集成测试;D2 阶段无战斗载体)。 +- **AC-16** GIVEN debug 构建且 B1 侧经 `set_reload_gate(true)` 置位(模拟战斗中),WHEN `reload()`,THEN 返回 false、ConfigDB 值不变、不发 `config_reloaded`;GIVEN 门未置位,WHEN `reload()`,THEN 返回 true 且新值经类型化属性可读且 `config_reloaded` 发出恰一次(仅 debug 构建);GIVEN reload 后新表校验失败,THEN 旧表保留、状态停留 Ready(EC16)。 +- **AC-17** GIVEN 未设 master_seed,WHEN `open_stream` 或 `derive_at`,THEN push_error + 冒烟通道断言中止路径(quit 非 0)(EC7)。 +- **AC-18(deferred→首次 release 导出)** release 模板构建下:致命校验错误的表现符合"引擎默认错误路径"预期、override 结构性不存在(R3 边界④、校验双通道 release 侧)。 + +## Open Questions + +1. **headless 测试载体终稿**:MVP 用 `--headless --script` 冒烟(本 GDD 决策);gdUnit4 是否在 Technical Setup 引入并迁移冒烟用例——owner:Technical Setup 阶段(systems-index 门禁增补记录项)。 +2. 编辑器内表文件热重载自动化(当前仅 debug 键触发;低优先)。 +3. 数值表显示名的本地化——D6 归口。 +4. C4 MetaTable 并入 ConfigDB 还是独立 autoload——C4 GDD 定(倾向并入)。 +5. **难度曲线导出工具**(R1 审计的离线载体):headless 导出 resolve 矩阵脚本,C3 期交付——归 C3 GDD 登记排期;**注意排期倒挂**(re-review 登记):BoardGenTable 可推理率曲线自 A1(第 5 位)起回填,A1 定稿前须有临时抽查载体(入 systems-index 门禁记录)。 diff --git a/design/gdd/legacy-2026-09-13/game-concept.md b/design/gdd/legacy-2026-09-13/game-concept.md new file mode 100644 index 00000000..4a2bbfc2 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/game-concept.md @@ -0,0 +1,153 @@ +# 游戏概念:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike) + +> 参考《弹珠哥布林》(Peglin):两段式回合循环(小游戏 → 战斗结算)+ 道具跨循环累积(**B4 定稿口径 2026-09-09:战斗域内跨轮次跨图累积、battle_over 全清不跨战斗**——与 Peglin 跨战斗持有不同)+ Roguelike 闯关结构。(**2026-09-11 关内延续裁决改写**:库存改为关卡域——跨战斗延续、关卡末清空/折算;本注记前半作废) +> 我们的区别:小游戏层是"扫雷类推理玩法"(非物理弹珠),玩家 agency 全部前移到战斗前的排雷规划(2026-09-09 注:B4 激活轮末道具使用窗口后,agency 扩展至轮末资源决策——规划前移的主体定位不变)。 + +## 世界观与命名(定稿 2026-09-09;2026-09-12 概念层增补:隐喻换轴 + 惩罚格类型化) + +> **版本注记(2026-09-12 概念层增补,用户拍板;依据记录 `design/research/lore-nest-metaphor-2026-09-12.md` ⑤节六问)**:叙事属名换轴——雷格 = **惩罚格**(对外叙事称呼;机制名"雷/雷格"不变),惩罚格有不同类型,首发地牢类型 = **怪物巢穴**;插旗的玩家可见动词 = **控制**(类型无关;对内文档与机制协议仍为"旗/插旗");连锁展开 = **坍塌显形**;标错惩罚叙事 = **控制失败的失控反噬**(随机读作失控,重复性措辞随 A2 OQ1);宝箱呈现仅绑定可使用道具类奖励;世界观厚度 = 碎片点缀(不含前人日志)。原 2026-09-09 映射(雷 = 地牢陷阱、旗 = 探险者标记的已知陷阱)作废;"陷阱"降级为惩罚格类型词表的一员。本修订为表现层/命名层,不改变任何已锁机制语义、数值与接口(A1–B4、B1、C3 已定稿内容不受影响);惩罚格类型化的机制分化(陷阱→直接扣血、标中陷阱→怪物受伤害、岩浆/冰冻化身等)为**已登记的未来机制侧方向**(与现行"开雷→敌方优势非直接伤害"锁定规则的取舍归 A1/A2 修订裁决),不在本表承诺。 + +**产品名:Dungeon Sweeper**——命名取 minesweeper → dungeon sweeper 的双关:玩家不只是"扫雷者",更是"清扫地牢的人"(清巢者——逐层肃清巢穴侵染、夺回地牢)。"扫雷 × 自动战斗 闯关 Roguelike"保留为品类描述,用作各文档副标题。 + +**背景一句话**:玩家操纵一名地牢探险者,逐层下探一座被怪物巢穴侵染的古代地牢;每一层地牢就是一张"雷图"——惩罚格密布的古老地砖阵,探险者凭数字提示感知惩罚格方位,控制巢穴、搜刮前人遗落物,才能安全下探更深的层。 + +机制 ↔ 地牢隐喻映射(表现层包装,不改变任何已锁机制语义,A1–B3 已定稿数值/接口不受影响): + +| 玩法元素 | 地牢隐喻 | +|---|---| +| 扫雷图 | 地牢的一层(一间被巢穴侵染的厅室;清图 = 肃清本间厅室) | +| 雷 | 惩罚格(叙事属名;含不同类型,首发类型 = 怪物巢穴——惊动即怪物倾巢而出占得先机:伏击/增益,P3 风险同源;巢已空、残址可通行。陷阱/岩浆/冰冻等类型及其机制分化 = 未来方向归机制侧) | +| 旗 | 对惩罚格的**控制**(玩家可见动词,类型无关;对内仍为"旗/插旗")——标中 → 巢穴废置、巢主失庇护(怪物 debuff);标错 → 控制失败、反噬流向不可控(措辞细则随 A2 OQ1);"封印/镇/反制"为类型风味词;视觉形态换皮归 art-bible | +| 小人 | 地牢探险者 | +| 清图 | 本层探明、搜刮前人遗落物完毕(清图奖励),下探更深 | +| 关卡推进 | 不断下探更险的层(P4 支柱) | + +- 首发 biome = 地牢(与 A1 BoardGenTable 参考行占位值 `{"dungeon"}` 一致;biome 词表定稿权仍在 B4/C1+AD,此处仅为方向背书) + +## 目标受众(定稿 2026-09-06) + +- **核心受众**:策略推理玩家 × Roguelike 构建玩家的交集——喜欢杀戮尖塔/小丑牌的构筑决策,喜欢扫雷/数独的纯逻辑掌控感 +- **次级受众**:Peglin 玩家中觉得"弹珠太看脸"的群体;怀旧扫雷玩家中寻找进化的群体 +- **平台**:PC(Steam)先行;回合逻辑玩法,对配置无要求 +- **单局预期**:一场冒险 20–40 分钟,单关卡 5–10 分钟——碎片时间可完成一场战斗 + +## 视觉身份锚(Visual Identity Anchor,定稿 2026-09-06) + +**一句话视觉规则:地形是氛围,数字是主角——一切视觉服务"信息可读"而非"场景华丽"。** + +支撑原则: +1. **数字可读性硬指标优先于氛围**:暗色地牢底色上的数字必须达到高对比(具体色值在 art-bible 定),任何纹理不得干扰数字辨识 +2. **变异格形状+颜色双通道**:迷雾/岩浆/冰冻等变异不能只靠色相区分(色弱可用性),必须绑定图标或形状冗余 +3. **绘制优先级固定**:数字 > 小人标记 > 旗子 > 变异纹理 > 底色——小人与旗不得遮挡数字(小人所在格用边框高亮而非覆盖文本) +4. **调色板交换优先于新增资产**:关卡/biome 换皮优先做色板变体,控制个人开发资产成本;A1 棋盘生成须预留 biome/tag 数据字段 +5. **格子 = 地牢地砖(2026-09-09 用户定稿)**:扫雷格子的美术基底做成地牢一格一格的石砖/地砖样式(候选表现:未开 = 覆尘完整地砖、已开 = 碎裂/裸露地面、雷 = 被惊动的巢穴/崩塌的巢址【2026-09-12 隐喻换轴增补:原"触发的陷阱机关"为旧称;候选词与已定的"坍塌显形"连锁叙事对齐】);具体形态由"格子样张测试"定稿,样张仍以数字可读性为第一验收项(早期风险第 1 条不变);与原则 4 衔接——不同 biome = 同款地砖的不同色板 + +早期风险登记(详见 art-bible 任务): +- 扫雷数字可读性 × 地牢暗色氛围的冲突 → 需一次格子样张测试定风格 +- 格子信息密度:小人/旗/数字/变异同格叠加 → 按上述优先级规则处理;小人移动路径预览的表现归属在 D1 GDD 解决 +- **双舞台资产管线陷阱**:战斗发生在棋盘上还是独立场景,是 B5 最大的成本变量 → 必须在 B5 GDD 前的"战斗表现美术测试"(一屏样张)中锁定 + +## 术语层级(定稿 2026-09-06) + +| 术语 | 定义 | +|---|---| +| **存档** | 元进度。所有"冒险"都在某个存档内进行。存档内可用代币解锁天赋等永久提升 | +| **冒险** | 开启进入的一次完整游戏(run)。包含多个"关卡"。死亡则冒险结束(Roguelike) | +| **关卡** | 冒险内的一个大节点。有一个关底 Boss。完成关卡 → 获得"冒险"内大奖励 + 解锁天赋用的"代币" | +| **战斗** | 关卡内移动时碰到非奖励点触发。一次完整的战斗遭遇,包含若干个"轮次"和若干张"扫雷图" | +| **轮次** | 战斗内的原子回合单位。玩家用本轮 AP 在扫雷图上挖掘 → 轮次结束 → 自动战斗结算。敌人未全灭且主角存活 → 下一轮次 | +| **扫雷图** | 跨轮次延续的棋盘状态。AP 用完但未开完 → 下一轮次继续同一张图(AP 重置,棋盘状态保留)。全部安全格开完 → 清图奖励 → 结算后换新图 | + +### 对应关系(定稿) + +``` +存档 ⊃ 冒险 ⊃ 关卡 ⊃ 战斗 ⊃ { 轮次 ×N , 扫雷图 ×M } +``` + +- 战斗 : 轮次 = 1 : N —— 敌人未全灭且主角存活则继续 +- 扫雷图 : 轮次 = 1 : N —— 一张图跨越多个轮次,直到开完 +- 战斗 : 扫雷图 = 1 : M —— 每次清图后换新图,战斗内可经历多张图 + +## 轮次流程(定稿) + +1. **挖掘阶段**:用本轮 AP 开格/标旗(棋盘为延续的旧图或刚换的新图) +2. **轮次结束**(三种方式任一触发): + - AP 用完(图未开完 → 下轮继续这张图) + - 主动停手(剩余 AP 1:1 转存 RAP 池——攒满可刷新雷图的风险退出资源,**不作战斗资源**【2026-09-07 A3 RAP 修订,原"折算战斗资源"作废】;图未开完 → 下轮继续这张图) + - 图开完 → 清图奖励(剩余 AP 作废不折算【同日修订】;下轮换新图) +3. **自动战斗结算**:每轮结束时都结算一次;开过的雷在此时兑现敌方优势 +4. **判定**:敌人全灭 → 战斗胜利;主角死亡 → 战斗失败;否则 → 下一轮次 + +## 核心循环 + +``` +存档(天赋/代币) + └─ 冒险(run,死亡即终) + └─ 关卡(节点地图移动,Boss 收尾) + └─ 战斗(碰到非奖励点触发) + └─ 轮次 ×N(敌未灭/己未亡则继续) + └─ 扫雷图(有限 AP 挖掘) + ├─ 安全格 → 增益/道具(多为限时或一次性) + ├─ 雷 → 敌方获得优势(伏击/增益) + └─ 全部安全格点开 → 图完成 → 自动战斗结算 +``` + +## 扫雷层规则(当前共识) + +- **开格消耗 AP,连锁展开(flood fill)免费**;每轮 AP 固定(原型 v2 起 8 点,v1 的 12 点被测评为太多) +- **行动经济统一(v3,2026-09-07 A3 RAP 修订)**:插旗消耗 1 AP(**旗子上限取消**——旗为棋盘标记、数量不限,稀缺性由 AP 机会成本承载【原"每轮 4 面、每轮重置"作废】),仅限小人相邻格;拔旗移除标记、不返还 AP +- **标雷判定(v3,2026-09-07 三向效果定稿)**:轮次结算时统一判定——**标中雷 = 怪物获得 debuff(削弱)**(与"安全格→主角 buff、踩雷→怪物 buff"构成三向效果映射);一次性兑付、兑付资格以**雷格实例为键**(每雷本次战斗至多兑付一次,资格随首次兑付消费)、拔旗重插同一雷格不恢复资格、禁止逐轮重复兑付(A3 OQ5/F1 条款 2:debuff 档位锚 = 严格 > 最低档安全格 buff 当量,且以连锁放大的边际挖掘收益为对照基准——2026-09-07 三轮评审改定);标错暂无惩罚(候选惩罚:敌方伏击+1 等,待验证后决定;该惩罚兼具旗滥用定价职责,见 A3 OQ2) +- **小人控制(v2,参考 Microsoft Minesweeper Adventure 模式)**:棋盘上有小人,点击已开格=移动(免费,BFS 限已探明区);挖/插旗仅限小人 8 向相邻格。边缘探索的地牢氛围已被用户认可 +- **初始安全区**:每张图开局 3×3 无雷自动展开,小人出生于此(初始展开无奖励) +- **可随时停手**:剩余 AP 1:1 转存 RAP 池,攒满(默认 8)可刷新雷图(不给清图奖励)——"是否承担盲猜风险"是策略点【2026-09-07 A3 RAP 修订,原"转化为战斗资源"作废】 +- **奖励统一结算**:扫雷阶段奖励只入库(含治疗包),轮次结束进入独立战斗结算画面逐行生效(正式版将替换为战斗动画) +- **安全格 = 增益/道具**:减伤、增伤、范围伤害等;**有回合限制甚至一次性**(稀缺性来源) +- **雷 = 敌方优势**:开到雷不是死亡,而是敌人获得增益/伏击——风险与收益同源 +- 扫雷图非基础版:**地形变异**(迷雾区无数字、岩浆格等)+ **辅助道具**(探测、安全旗等) + +## 难度与惩罚机制(按层级) + +**难度不随战斗数增长,而是随关卡进程增长**:难度 = f(关卡序号, 节点深度, 精英/Boss 修饰) + +| 层级 | 惩罚/压力机制 | 说明 | +|---|---|---| +| 轮次内 | 开雷 → 敌方结算时获得优势 | 扫雷失误的代价,当场兑现 | +| 战斗内 | 轮次越多,敌方结算出手越多 | 长战斗的自然代价(己方 HP 消耗);单张图拖太久 = 敌人多结算但旧图剩余收益仍在,形成"续挖 vs 清图换新"决策 | +| 关卡内 | 节点越深越难;精英节点相对更难 | 越接近 Boss 越危险,精英给更高收益对冲 | +| 关卡间 | 下一关基础数值+新地形变异 | 变异种类随关卡解锁(普通数字 → 迷雾 → 岩浆/冰冻) | +| 冒险间 | 死亡重开,天赋追赶 | 元进度:代币解锁永久提升 | + +## 与 Peglin 的对比要点 + +- 玩家技巧:物理瞄准+概率赌博 → **信息推理+风险承诺** +- Agency 位置:战斗内 → **全部前移到排雷规划** +- 不确定性来源:弹射混沌 → **隐藏信息推理缺口** +- 核心情绪:老虎机开闸爽感 → **"我算对了"的掌控感 + 规划兑现的延迟满足** + +### 已识别的风险(原型验证目标) + +1. 自动战斗沦为无聊过场 → 兑现层必须可见、有反馈 +2. 扫雷后期纯猜格子 → 用地形变异和道具缓解"死局感" +3. 一格一格点节奏慢 → AP/连锁/停手机制压缩决策带宽 + +### 创意风险登记(2026-09-07,CD 门禁) + +1. **安全格奖励的可预期性**(P2 规划的前提):若道具掉落纯随机且不可见,玩家无法提前形成"我要去拿 X"的规划,"规划的延迟兑现"退化为探索惊喜 → B4/A4 GDD 落实奖励类型可见性或探测机制。**【2026-09-09 B4 评审口径注记】结构前提已落地**(奖励身份 install 期预分配 + 已开格/库存恒读 + 每格必有货 + 雾档先验——B4 Rule 2/8/F1/F2);**MVP 呈现面 = 已开格 + 库存,面向特定道具的寻路未兑现**——待 A5 探测道具/呈现深度(B4 OQ2,垂直切片);本风险维持 OPEN(结构前提落地 ≠ 风险闭合) +2. **"我算对了"缺高光兑现节点**:现有设计只有避损(雷→敌方优势)与常规奖励,缺"完美排雷"的爆发结算(候选:全图标中/零误标清图加倍结算)→ B1/B4 GDD 设计该兑现节点 +3. **chord 缺席的兑付节拍缺失**(2026-09-07 A3 评审登记):本作有意不做经典扫雷的 chord(双击快开),正确标雷后的即时大片兑现节拍随之缺席(标中旗兑付只剩当轮 debuff)→ 候选高光补偿与 #2 合并归 B1/B4 设计 + +## 可证伪假设(概念原型要回答的) + +> 如果玩家通过扫雷决策为自动战斗获取道具,他们会感受到"每一步排雷都在影响战斗结果"的策略联动感。 +> 验证信号:玩家在扫雷阶段主动规划目标(连续 3 轮中至少 2 次主动改变扫雷策略去拿特定道具),并主动想进入下一轮。 +> 【2026-09-09 MVP 口径注记(B4 评审)】未开格身份不可见(B4 Rule 8),"拿特定道具"的寻路决策待 B4 OQ2/A5 兑现后方可作为有效信号——MVP 期以"往雾区推进/囤刀待机/叠层满载后收手"类规划行为代观测,防止把缺失功能误判为设计失败。 + +## 待定问题 + +- [x] 扫雷后奖励的主动道具(影响战斗结算):**MVP 激活**(2026-09-09 B4 定稿,用户拍板——轮末结算前"道具使用窗口"玩家选用;use_timing 字段从预留转承重,MVP 单值 pre_settlement,扩枚举走 B4 修订;原"后续版本加入"作废) +- [x] 己方限时增益:**跨轮次延续(按剩余结算次数),不跨战斗**(定稿 2026-09-06) +- [x] 旗子:**无上限**,旗为棋盘标记不占轮次资源,稀缺性由 AP 价目承载(2026-09-07 A3 RAP 修订定稿;原"每轮重置 4 面"作废) +- [x] 停手/清图时剩余 AP 的去向:停手 1:1 转存 RAP(刷图风险资源)、清图作废——**折算乘数(×2/×1)整体作废**(2026-09-07 A3 RAP 结构修订;效果侧健康由 A3 F1 契约约束,OQ3 留 playtest 复核) +- [ ] **标错惩罚**(v3 遗留:现无惩罚;候选:敌方伏击+1~~ / 旗子数量减半~~——后者随 2026-09-07 旗上限取消作废)——在 A2 GDD 前用原型小补丁实测后关闭(CD-SYSTEMS 门禁补登记;兼具旗滥用定价职责,见 A3 OQ2) +- [ ] 关卡内节点地图的形态(线性/分支)——C1 GDD 解决;候选已登记(2026-09-09 用户提出):**四方格步进式前进**——关卡内地图为格网,每步从相邻四格中选一格推进(与地牢隐喻同构);C1 设计顺序第 14 位,此项可后置 +- [ ] 反雪球机制——MVP 不做,Alpha 用测试数据决定(C3 GDD 需先决定"每胜一场敌方成长"这一正雪球源的去留) diff --git a/design/gdd/legacy-2026-09-13/game-pillars.md b/design/gdd/legacy-2026-09-13/game-pillars.md new file mode 100644 index 00000000..494bb944 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/game-pillars.md @@ -0,0 +1,29 @@ +# 游戏支柱(Game Pillars) + +> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 定稿:2026-09-06 +> 来源:`game-concept.md` 概念定稿 + 概念原型 v1→v3 验证结论 + CD-SYSTEMS 门禁意见 +> 用途:所有 GDD 的评审尺度——每条设计决策必须至少服务一条支柱;与支柱冲突的设计默认砍掉 + +## P1 推理的掌控感 —— "我算对了" + +每一格挖掘都应该基于可推导的逻辑,而不是运气。生成端必须保证可推理率(A1 GDD 定最低门槛),后期"纯猜"局面用变异与道具缓解。这是本作与 Peglin(概率赌博)的根本区别。 + +**裁剪判据**:一个设计若让玩家频繁进入"无法推理只能赌"的状态,即违反本支柱。 + +## P2 规划的延迟兑现 —— 扫雷的因,战斗的果 + +扫雷阶段的每个决策(挖哪里、标哪面旗、何时停手)都在战斗结算中可见地兑现。兑现必须**可归因**:结算明细标注来源(伤害道具 / 安全格增益 / 标中旗削弱 / 清图奖励 / 雷→伏击;【2026-09-07 A3 RAP 修订】原"停手折算"来源作废——停手改产 RAP 风险资源,不再进战斗结算),MVP 就要做到,不等战斗动画。 + +**裁剪判据**:若玩家无法说出"这个结果是我哪步扫雷带来的",即违反本支柱。 + +## P3 风险与收益同源 —— 雷既是敌人的武器,也是你的赌注 + +开雷喂给敌人伏击(代价),标中雷削弱敌人(收益)【2026-09-07 三向效果定稿:标中旗 = 怪物 debuff】,危险区域的安全格价值更高。所有风险机制都从这个同源原则派生,不引入第二套随机惩罚。 + +**裁剪判据**:一个风险若与"雷/未知格"无关,需强理由才可保留。 + +## P4 地牢下探的探险氛围 —— 边缘探索,越深越险 + +小人从初始安全区向雷图边缘探索(Microsoft Minesweeper Adventure 式的地牢感),关卡推进 = 不断下探更险的层。视觉、音频、地图结构都服务这个隐喻。产品命名本身承载本支柱:**Dungeon Sweeper**(2026-09-09 定稿,minesweeper → dungeon sweeper 双关;世界观与机制隐喻映射见 game-concept"世界观与命名"节)。 + +**裁剪判据**:任何让棋盘体验退化为"平面对话框"的表现层简化,需评估对氛围的伤害。 diff --git a/design/gdd/legacy-2026-09-13/systems-index.md b/design/gdd/legacy-2026-09-13/systems-index.md new file mode 100644 index 00000000..82aaa246 --- /dev/null +++ b/design/gdd/legacy-2026-09-13/systems-index.md @@ -0,0 +1,284 @@ +# 系统索引(Systems Index) + +> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike) +> 生成:2026-09-06 · 分解来源:`design/gdd/game-concept.md`(概念定稿)+ `docs/prototype-report-minesweeper-battle-concept.md`(原型 v3 验证结论;demo 代码已于 2026-09-12 删除) +> Review mode:lean(TD-SYSTEM-BOUNDARY / PR-SCOPE / CD-SYSTEMS 门禁按 lean 跳过) + +## 系统枚举(20 个) + +### A. 核心玩法层(扫雷侧) + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| A1 | 扫雷棋盘生成 | 隐含 | 布雷、初始安全区、变异布置、按难度参数生成 | MVP | +| A2 | 排雷玩法规则 | 显式 | 小人控制、挖/旗/移、连锁展开、数字提示 | MVP | +| A3 | 行动经济 | 显式 | AP(每轮重置)、RAP 池、旗子(无上限)、停手/清图/耗尽三出口 | MVP | +| A4 | 地形变异 | 显式 | 迷雾/岩浆/冰冻等变异清单与交互规则 | MVP | +| A5 | 扫雷辅助道具 | 显式 | 探测、安全旗等;获取与使用规则 | 垂直切片 | + +### B. 战斗层 + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| B1 | 战斗结算规则 | 显式 | 伤害公式、结算顺序、胜负判定 | MVP | +| B2 | 敌人系统 | 隐含 | 敌人类型/词条、目标选择规则 | MVP | +| B3 | 增益/状态系统 | 显式 | buff 时限、叠层、衰减时机 | MVP | +| B4 | 奖励与道具经济 | 显式 | 奖励表、道具池、权重数值、拆弹奖励 | MVP | +| B5 | 战斗表现 | 隐含 | 结算画面 → 战斗动画、伤害数字 | 垂直切片 | + +### C. 结构与成长层 + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| C1 | 关卡节点地图 | 显式(形态待定) | 关卡内移动、遭遇点/奖励点分布;形态候选已登记(2026-09-09):四方格步进式前进——格网地图、每步从相邻四格选一格推进,可后置 | 垂直切片 | +| C2 | 冒险结构 | 显式 | 关卡序列、关卡大奖励、失败重开 | 垂直切片 | +| C3 | 难度曲线与精英/Boss | 显式 | 数值成长、精英图/Boss 图全局变异 | 垂直切片 | +| C4 | 元进度(存档/天赋/代币) | 显式 | 存档结构、天赋树、代币经济 | Alpha | + +### D. 表现与支撑层 + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| D1 | HUD 与界面框架 | 隐含 | 棋盘/队伍/日志/结算的 UI 容器(简版) | MVP | +| D2 | 数据配置与数值表 | 隐含 | 硬编码参数迁移为可调配置 | MVP | +| D3 | 存档序列化 | 隐含 | 元进度持久化框架 | Alpha | +| D4 | 音频 | 隐含 | 挖掘/踩雷/结算/胜利反馈 | Alpha | +| D5 | 教学引导 | 隐含 | 小人三动作 + 两层循环教学 | Alpha | +| D6 | 设置/本地化 | 隐含 | 常规打磨项 | 完整愿景 | + +## 依赖分层 + +| 层 | 系统 | 依赖 | +|---|---|---| +| 1 Foundation | A3, A4, B3, D2, D3, D6 | 无 | +| 2 Core | A1 ← A4,D2 · A2 ← A1,A3,A4 · B2 ← D2 · B4 ← D2(2026-09-09 B4 定稿收窄:原"A2,B3,D2"——A2 事实经 B1 转化零直接接口、B3 为验收锚关系非运行时边,见 B4 Dependencies 修订注记) | Foundation | +| 2.5 | **A5 ← A2,A4,B4**(辅助道具:依赖排雷规则与变异定义;奖励获取侧挂 B4) | Core | +| 3 Feature | B1 ← A3,B2,B3,B4,D2 · C2 ← C3,D2 · C3 ← D2,B2 · C4 ← D3,C2,D2 · C1 ← C2,C3,B2,B4 | Core | +| 4 Presentation | D1 ← A2,A3,B1 · B5 ← B1,D1 | Feature | +| 5 Polish | D4 ← A2,B1 · D5 ← A2,A3,B1 | Presentation | + +- **循环依赖:无**。全项目唯一允许的回边:A2 只外发事件(`cell_revealed` / `round_end` / `flag_judged`),B4/B1 订阅后回调——A2 不依赖任何人(TD-SYSTEM-BOUNDARY 定) +- **D2 接口依赖边补全(2026-09-07 re-review)**:B4(reward_roll 流)、B1(set_reload_gate 调用)、C2(master_seed 接线)、C4(MetaTable 消费)四条对 D2 的边已补入上表——此四者为 API/接线级依赖,非纯数据读取;其余系统的数据读取为事实依赖,不逐条列边(完整清单见 `design/gdd/d2-data-config.md` Dependencies 表) +- **C3←A1 方向说明**:C3 仅依赖 A1 的**配置 schema**(类型依赖);难度参数经编排器/D2 传递给 A1,禁止 C3 反向调用棋盘生成 +- **瓶颈系统(设计需一次定稳)**:A2 排雷玩法规则(B4/D1/D5 挂其事件)、B1 战斗结算(B5/D4/D5/C3 消费其输出)、D2 数据配置(全员依赖) +- **叶子系统(可晚设计)**:C1、C4、D4、D5、D6 + +## 门禁修正记录(2026-09-06,CD/TD/PR/AD 四总监 CONCERNS 汇总落实) + +- **B2 扩为"战斗单位系统"**:主角与敌人共用受伤/死亡接口;含**雷触发的敌方优势词条(伏击清单)**与**敌方意图可见性规格**(身份承重项,CD 门禁) +- **B1 GDD 须含"轮次生命周期编排"独立章节**(round_end 事件 → 结算 → 判定 → 重置/换图的 owner);输出契约为**可回放步骤流**(结算逻辑内禁止 await/动画);MVP 验收含结算明细来源标注(伤害道具/安全格增益/标中旗削弱/清图奖励/雷→伏击;~~停手折算~~ 已随 A3 RAP 修订作废——停手改产 RAP、退出战斗结算) +- **A1 GDD 须含**:可推理率生成参数(P1 支柱的生成端保障)+ biome/tag 数据字段(AD 门禁换皮约束) +- **A4 MVP 范围收窄**:变异框架 + 迷雾(岩浆/冰冻等无 MVP 容器,随关卡解锁归垂直切片后) +- **B2 MVP 范围收窄**:1–2 种敌人 + 少量词条(名录扩展归垂直切片/Alpha) +- **D2 GDD 策略**:只定 Resource schema + autoload 注册表 + 校验模式,数值表留空位随各系统 GDD 回填;**数值表结构第一天就带关卡序号/节点深度维度** +- **工程债登记**:Core 层必须可 `--headless` 跑完整战斗(不引用 Control/UI);RNG 种子服务归 D2(Foundation);已引爆雷格可通行、治疗不溢出等细节规则写入对应 GDD +- **AD 项**:B5 GDD 前插入"战斗表现美术测试"(一屏样张锁定角色剪影/动画方案/战斗空间);临时 SFX 从 D4 拆出挂垂直切片里程碑 +- **流程债已清**:game-pillars.md 已建(4 支柱);概念文档已增补 Visual Identity Anchor + 目标受众;原型 REPORT.md 已归档(PROCEED) + +## 门禁增补记录(2026-09-07,Concept → Systems Design 重审 CONCERNS 落实) + +> 完整报告:`production/gate-checks/gate-check-systems-design-2026-09-07.md` · 判定 CONCERNS(无阻塞),stage.txt 已写入 + +- **A2 前置**:标错惩罚(v3 现无惩罚)用原型小补丁实测后关闭;可与原型 v3 终验合并为一次会话(CD/PR 门禁) +- **art-bible 排期**:最晚在 D1 GDD(设计顺序第 10 位)前创建(至少 §1–4),"格子样张测试"(数字可读性定风格,game-concept 早期风险登记第 1 条)并入执行;垂直切片门禁硬性要求 art-bible(AD 门禁) +- **B4 GDD 须预留**道具 schema 的"使用时机"字段(主动战斗道具后续版本加入) +- **D1 GDD 须解决**"小人移动路径预览"的表现归属(来源:game-concept 视觉锚风险登记) +- **D2 GDD"校验模式"章节须定** headless 测试载体(`--script` 冒烟 vs gdUnit/GUT);**B1 GDD"轮次生命周期编排"章节须定**事件总线载体(autoload 单例 vs 直连)(TD 门禁) +- **C3 GDD 需定稿**"C3 是否消费 B1 输出"(依赖表 C3 ← D2,B2 与瓶颈注记口径不一)并回写本索引(TD 门禁,可延后) +- **创意风险登记**(CD 门禁,详情见 game-concept"创意风险登记"节):安全格奖励可预期性(P2 前提)→ B4/A4 落实;"我算对了"高光兑现节点 → B1/B4 设计 +- **垂直切片前置**:按预注册验证信号做一次正式 playtest(与标错惩罚实测合并)(CD 门禁) +- **A3 下游经济契约**(2026-09-07 A3 二轮评审登记,同日 RAP 结构修订改写,三轮评审增补条款⑤并钉死闭合 owner):B1 GDD 须承接并验收——① **效果档位对照表**:安全格 buff / 标中旗 debuff / 清图奖励 / 敌方增益统一当量、单一线性换算(分源改率须回 A3 重议);② 标中旗 debuff 档位锚:**严格 >** 最低档安全格 buff 当量,且以**连锁放大的边际挖掘收益**为对照基准(provisional 1:1)、每战标中旗总吞吐由 B1 对账;③ **挖掘风险不等式**:P(安全)×buff 当量下界 > P(雷)×敌方增益当量上界(锚 A1 可推理率参数 × B2 惩罚上限表值,**B1 单一闭合 owner**,B1 GDD 强制检查点);④ 清图奖励 > 0 且预留**按图类型(biome/tag)差异化分支**的结构位(B4 表设计,A3 只上报 cleared;图类型战斗域恒定、不逐图重掷,A1/B4 共同验收);⑤ **清图 vs 弃图刷新支配性**:清图奖励下界使"推到底清图"期望当量 ≥ "确定格挖完即弃图刷新"期望当量(B1 闭合对账,强制检查点——该环与 A1 可推理率反向耦合,是 P1 失败模式的经济对冲)。**request_refresh 契约**:玩家经 D1 发起、B1 结算编排期代理调用(B1 接口须含玩家意图路径,禁止 RAP 满自动刷);刷新成功后回发 `new_board` 为契约义务,未刷新的 stopped/ap_exhausted 轮回发 `new_board` = B1 违约 fail-fast。**【2026-09-12 D4 修订作废】refresh 前移挖掘期(Active 相位受理、B1 `refresh_board` 编排、无结算段无 round_end、每轮上限 2);new_board 回发义务仅限 cleared 轮,stopped/ap_exhausted 恒 same_board——现行契约见 A3 Rule 11 / B1 AC-29/AC-30 / registry `rap_rules` 与下方 2026-09-12 修订登记块】**round_end 双发射源**已登记:A2 外发棋盘路径、A3 直发停手路径(payload 同构);EC4"先停后挖"批次仲裁 deferral 至 A2 集成测试,**义务清单**:{cleared×stopped、stopped×ap_exhausted} × 两到达序共四组合 + RAP 记账随胜出 path_tag(批次 cleared 胜出 → 剩余 AP 作废不入 RAP)。B4 GDD 侧:标中旗一次性兑付、**资格以雷格实例为键**(A3 OQ5,三轮改定——拔旗重插不恢复已消费资格)+ 清图奖励插槽。D1 GDD 侧(三轮增补):RAP 战斗末清零可见反馈。原"stop_multiplier 不等式①②"随停手折算作废而删除。 + +## 门禁增补记录(2026-09-08,B3 定稿登记) + +> B3 GDD:`design/gdd/b3-status-effect.md`(Designed,待 /design-review)· registry 已登记 `status_effect` 条目 · D2 StatusEffectTable 已回填(接口漂移规则) + +- **B1×5 义务清单(B1 GDD 门禁评审逐项核对落位,缺项即 blocking——A4 对 A1/A2 登记先例)**:① 同轮多授予按确定次序调用 apply_effect(EC3 溢出归因依赖)② 事实→授予恰一次转化(EC6——B3 是 dumb 账本不去重)③ percent→乘数换算方向与合成值地板成文(EC7/F2——weaken AP 界 −200%/表界 −400%、defense_up 顶点 −100% 三例)④ 最后一轮 decay 判定(F3/A3 EC8 同款;battle_end 全清兜底)⑤ F1 条款 1 效果档位对照表以 B3 实例为对照实物 +- **🔴 weaken 锚预警(B1 强制检查点输入,registry status_effect.anchor_warning 已登记)**:weaken(−25%×d1)vs A3 F1 条款 2 锚(debuff 当量严格 > 最低档安全格 buff 当量)两独立线性代理同向预检不过(25 vs 100;≈1 vs ≈4)——B1 对照验收优先裁决;坐实违反则上调 magnitude 或 B4 重定参照,接口漂移回写 registry +- **A3 OQ5 收编注记(用户 2026-09-08 拍板)**:"标中旗 debuff 档位归 B4 表设计"修订为——四行 MVP 初值**定稿权归 B3**(已定稿),**锚验收权仍归 B4/B1**(条款 2 + A4 三定价硬约束引用的"最低档安全格 buff 当量"以 attack_up 行为参照对象);A3 GDD(Approved)不改,以本条 + registry ownership_note 为准。**【2026-09-09 B4 评审迁移注记 → 2026-09-11 回迁】**:参照对象曾迁 defense_up 当量(attack_up 时为死行)——2026-09-11 用户裁决"基础攻击恒存在"(HeroTable.atk 域 [1,999]、0 加载拒绝、MVP=6)后 **attack_up 复活,参照对象回迁 attack_up 当量**(消费面 = 主角战斗段先手攻击——B1 GDD 同批增补);本条"attack_up 行为参照对象"字样恢复现行,迁移期结束(历史见 registry status_effect.anchor_warning 回迁注记;C3 首次调参时按 attack_up 口径复算条款 2 预检) +- **EC8 reload gate B1 侧落点(qa-lead 缺口登记,2026-09-09 B4 定稿时错号修正:原记"EC9"——D2 实际编号为 EC8)**:set_reload_gate 置位/复位的真实调用方唯一落点 = B1——B1 GDD 须承载"战斗中真的被置位"的验收(D2 AC-16 只测门机制);防 gate 半句永久零落点 +- **ambush 量纲**:flat "+3" 的单位归 B2 定稿(候选"每颗已开雷伤害加成";联动 F1 条款 3 上限表值,B2 GDD 验收项) +- **B4 三项**:① 首个 refresh/independent 内容行上线时补真实内容行冒烟 AC + 夹具探针退役(B3 EC4/OQ3)② clear_reward 源授予路径确认(source=clear_reward,条款 4 插槽)③ weaken/attack_up 锚验收(条款 2) +- **StatusEffectTable 回填完成**:schema 8 字段 + Rule 4 豁免(定义表键=effect_id)+ 四行 MVP;悬空引用政策 = B4 首引时补校验(B3 EC2);D2 Core Rules 表行与 Status 行已同步回写 + +## 门禁增补记录(2026-09-09,A1 定稿登记) + +> A1 GDD:`design/gdd/a1-board-generation.md`(Designed,待 /design-review)· registry 已登记 `board_gen_table` 条目 + action_economy.referenced_by 增 A1 · D2 BoardGenTable 已回填(接口漂移规则) + +- **A2 双包含回验(A2 GDD 门禁评审缺项即 blocking)**:① A2 实际展开域 ⊆ A1 排除域(既有)② **A1 求解器初始态 ⊆ A2 实际初始展开域**(F1 注记④度量保真对偶——新增;若 A2 定稿展开更小,A1 回退出生 3×3 最小态口径) +- **B1 强制检查点输入口径(登记 B1 GDD)**:条款 3/5 对账消费 **actuals 分布(F4 报告 C 的 p10)而非 inferability_target 参数**——"拿参数当事实"对账空心化防御;fog_burden_ratio(付费格分母)= 条款 5 精确口径;attempt 边界非 scope 事件(B1 编排注记);inferability vs P(安全) 措辞登记 registry(非同义词——生成端结构性上界代理之占比形式) +- **F4 全量批量工具排期(qa-lead G7)**:调参期红线工具(N=256/行)owner = A1 交付物 / C3 消费——**与 D2 OQ5 曲线导出工具同批登记**(排期倒挂:A1 定稿→首行数值回填→即需抽查;临时载体 = 冒烟缩样 AC-6/10,统计意义有限) +- **参考行 provisional 声明**:BoardGenTable MVP 参考行(L1/L2)仅 schema 自洽性证明——曲线数值归 C3 调参所有权,禁止作为平衡事实引用;参考行 biome 占位值 {"dungeon"} 待词表 owner(B4/C1+AD)定稿替换(A1 OQ4) +- **黄金图回归向量协议**:五类手工核验小板 + 整数对比对 + 逐格信息源推导记录入版本库;换 solver_rules/信息源/metric_version = GDD 修订 + 向量重制(推理域冻结的行为侧兑现);**A1 /design-review 时安排手工推导第二人复核**(OQ5) +- **度量独立入口分解面(qa-lead G1,A1 实现契约条款)**:推理器/F3 度量/合法性谓词三入口暴露为独立纯函数(与内嵌调用同一实现)——实现期 ADR 确认;若不暴露,AC-7/8/9/12 单元腿降级重议 +- **A2 前置偏离登记(2026-09-09 用户拍板)**:门禁原要求"标错惩罚用原型小补丁实测后再写 A2 GDD"(2026-09-07 CD/PR 门禁;候选 = 敌方伏击+1;兼任 A3 OQ2 旗滥用定价职责)——现改为**标错惩罚以 OQ 形式登记进 A2 GDD**(候选方案与实测义务随 OQ 保留,不阻塞设计),偏离理由:实测依赖 playtest 会话、先推进设计主链;A2 /design-review 时须核对该 OQ 在位 +- **A1 full 评审终态(2026-09-09,用户拍板放行)**:8 blocking 同日核销(P1 两项达标判据 + target 域 [0.85,1] 绝对锚 / 雾占比上限 0.25 / F2c 成本闸 + 禁墙钟 / 雷数取整与布雷钉死 / F2≡Schema 合流 / AC-6(a) 芯环改写 / AC-11 前提腿 / 断言 B 收紧达成区占比)+ 第三轴登记约定(Boss=Dmax 行、elite_/boss_ 命名空间预留、OQ6 重审门)。**新登记下游承接项**:① EC5 零合法板玩家侧诚实呈现(提示/补偿/回退)→ B1/D1 域;② EC5 运行期熔断(同 (L,D) 连续降级/非法 → 确定性回退已知好种子)→ 实现期 ADR(OQ2 关联)。D2/registry 接口漂移已同步;recommended 9 项留档 A1 日志 + +## 门禁增补记录(2026-09-09,B3 full 评审登记) + +> B3 GDD full 评审:NEEDS REVISION(7 blocking),同日核销——日志 `design/gdd/reviews/b3-status-effect-review-log.md`。修订摘要:enemy 侧**单敌假设入文**〔用户拍板〕· B1 义务 5→8 · D1/B5/D5 呈现义务清单登记 · AC-15 机制更正 + reloaded 根全清单规格 · 致命夹具补根〔dup_key〕· AC-18 随 registry 逐字段键重构 · weaken 红警补修复空间不对称 + OQ1 归一口径前置 · stack-不重置 R 登记 OQ7 playtest 证伪项。 + +- **B1 义务清单 5→8(B1 GDD 门禁评审逐项核对落位,缺项即 blocking;①–⑤ 为 2026-09-08 原清单)**:⑥ **每结算编排恰一次 decay_round**(decay 基数=1 是 dur=1 一次性语义与全部衰减语义的先决条件;Active 相位 double-decay 在 B3 侧无守卫,信任 B1 编排)⑦ **结算明细按授予流水逐条标注来源**(F1 合并坍缩归因的兑付面补偿——合并实例只持首授 origin_cell/round_index)⑧ **“溢出未生效”行进结算明细协议**(Rule 5 后半承载;呈现归 B5/D1) +- **D1×4**:① HUD 常显 + EC12 空态规格定稿(隐藏栏 vs 空槽由 D1 裁决但须成文)② effect_id→icon 映射键契约(art-bible 形状+色相双通道)③ 有利/有害极性呈现映射(数据源 = B1 SettlementTable 效果语义,非 magnitude 符号——负值增益存在)④ 战斗结束全清的状态栏去留。**B5×2**:① 溢出行呈现(数据源 = B1 义务⑧)② origin_cell→玩家可读形态(缩略图/格子高亮;坐标不直接呈现)。**D5 增补**:“同轮拿满优于跨轮叠”教学(OQ7 配套)。**B4 增补**:奖励身份预读(stack 时序规划的推理原料,创意风险 #1 切面)——均缺项即 blocking,随各 GDD 门禁评审核对 +- **单敌假设(用户 2026-09-09 拍板,B3 Rule 1 入文)**:MVP 战斗 = 单个敌人,target_side 无歧义(enemy 侧 side-wide = 该敌);**B2 定敌人数模型时回验**——定多敌即触发 target_ref 接口漂移补列(B1 归集语义/B2 模型/D1 呈现裁决随批验收) +- **weaken 锚裁决前置(OQ1 增补,B1 强制检查点输入)**:B1 先钉死归一口径(每结算瞬时当量 vs 每 AP 总暴露)——瞬时读法 m_w≥51 可行(F2 越域界随移 AP 界 −408%/表界 −816%,7 层触 B1 地板);总暴露读法 m_w>100 被 percent 域**禁止(该分支不可行)**;口径落定前禁止数值修订,落总暴露则现实解 = B4 重定参照 +- **AC-19 扫描域登记(B3 文件集)**:B3 全部 GDScript 产物(状态容器类 + StatusEffectTable schema 类 + 冒烟脚本,文件名实现期定)纳入 D2 AC-9 模式项目级 CI 扫描域——owner:Technical Setup 归口随 B3 实现建立成员清单(qa-lead 评审缺口:此前三份 GDD 引用同一无载体域) +- **registry status_effect 条目逐字段键重构**(B3 owner 修订,2026-09-09):fields/mvp_content 由散文改逐字段 YAML 键(action_economy 同构)——AC-18 机器对账前提;knob 活值不作定值对账(A4 AC-8 同款);anchor_warning 增修复空间不对称;b1_obligations 同步 5→8 + +## 门禁增补记录(2026-09-09,A2 定稿登记) + +> A2 GDD:`design/gdd/a2-minesweeper-rules.md`(Designed,待 /design-review)· registry 已登记 `a2_event_surface` / `a2_initial_expansion` 两条件目 + +- **四件已登记事项逐项落位核销(用户 2026-09-09 开工简报)**:① **A2×4 清单**(A4 登记)+ **A1 双包含回验两臂**——落位 A2 GDD Dependencies 门禁义务表 + AC(A2×4①→EC4 腿 · ②→AC-12 · ③→EC4 腿/AC-18/20 · ④→AC-18;双包含①②→AC-3;**门禁评审缺项即 blocking**);② **A3 委派**——初始展开定义落点(免费/计入清图/无奖励 = Core Rule 4/AC-1)+ round_end 棋盘路径外发 payload 同构(Core Rule 2/10,AC-7/8/9);③ **标错惩罚以 OQ1 登记在位**(候选 = 敌方伏击+1,实测义务随 OQ 保留;**A2 /design-review 核对在位,缺位即 blocking**);④ **已引爆雷格可通行**(Core Rule 11/AC-15——工程债核销)。A3 EC4 批次四组合 deferral 已在 A2 冒烟兑现(AC-10 语义序仲裁 F4,含类内稳定序腿 e) +- **🔴 A1 修订义务(systems-designer 反例 B,主会话独立复算证实——阻断级,A1 定稿后首条)**:A1 布雷数下限 round(d×(W×H−9)) ≥ 1 拦不住"初始展开即 cleared"退化板——8×8 八雷先布后删幸存 1 颗距离-2 雷 → 最终板全 0 格连通 → R₀=S 开局免费清图;N=1 参数行 100% 退化、F4 断言 A(inferability=1.0)零拦截、min_safe_count 无效(|S| 被动满足)。**义务(A1 GDD 修订走接口漂移)**:F5 legality 增 `R₀ ≠ S`(违例 = 尝试作废重试,clearable 同款)+ F4 断言 A′ 族严格零覆盖 + Rule 4 排除域/可达域基准措辞钉死"以最终产物板计";A2 侧 install 不变式④纵深防御(b_degenerate 板拒绝,AC-1b)——双包含两臂不受影响 +- **B1×3 强制项(B1 GDD 门禁评审逐项核对落位,缺项即 blocking)**:① 结算触发 = Waiting 相位编排点、不依赖事件到达序(stopped 轮 A3 直发 round_end 与 A2 flag_judged 批的到达序不承载语义——事件序契约 flag_judged < round_end 全路径统一)② reward_eligible=false(initial 通道)格不得进入奖励入库(A3 EC5 委派闭环)③ 标中旗兑付去重(资格以雷格实例为键——A2 诚实重复上报的对侧) +- **D1×6 强制项(2026-09-09 A2 full 评审由 D1×1 扩容,缺项即 blocking)**:① stop 意图经 A2 `settle_batch` 提交、**不直调 A3**(单一结算序保证——A3 Rule 9"输入层直调"的调用方细化为 A2 批次入口,A3 GDD 不改、以本条 + registry a2_event_surface.api_surface 为准)② 四种格态一眼可辨(已开 0 格地板/已开雾格无字/未开格/未开雾格——左键移动 vs 挖消歧前提,误挖不可逆)③ 悬停预承诺(光标区分移动/挖/旗)+ 可挖域常驻指示 + chord 肌肉记忆首遇提示(非模态每 run 一次)④ 已引爆雷格"可通行可读"呈现 ⑤ `compute_move_path` 呈现语义定稿(合法性指示 vs 穿行动画——P4 载体)⑥ 单意图帧输入收集 + voided 呈现义务(至少 stop 被作废须可感知)+ 每次拒绝玩家可感知(映射源 = reject_reason + sub_reason);移动路径预览数据源 `compute_move_path` 已备(表现归属归 D1 GDD,2026-09-07 门禁项不变) +- **B1/B4 增补两条(2026-09-09 A2 full 评审登记,B1/B4 GDD 门禁评审核对落位,缺项即 blocking)**:① **高光兑现节点条目化**(创意风险 #2/#3 合并——"我算对了"爆发结算 + chord 缺席的标中旗即时兑现补偿候选;原散文登记升义务清单——B3 先例:清单外登记会丢失)② **连锁批量授予溢出吸收设计**(13 格链 × B3 max_stack=2 ⇒ 大量"溢出未生效"行进结算明细,P2 画面反向伤害连锁幻想——溢出转一次性资源/治疗或显式接受,裁决归 B1/B4) +- **A2 full 评审修订完成(2026-09-09,四专员 + CD 终审 NEEDS REVISION 六 blocking 同日核销)**:GD-1 连锁不越旗(传播集 ∖ G,经典 flood 惯例——旗 = 1 AP 链阻断器)· F4 到达序无关性声明收窄((path_tag, hint, AP/RAP) 全批域不变;划分序无关仅异类四组合;AC-10 增耗尽型救援/联合清图用例)· ActionOutcome 增 sub_reason 封闭枚举(UX-1——批前免费窗口,registry 已同步)· D1×6 义务包 · AC 修复包("EC4 腿"编号化 AC-6、b_chain/b_fog spawn=(3,2)、board_invalid 诊断枚举、AC-7 ap=1 同帧竞争、OQ2 范围收窄)· 用语纪律清扫(双闸/腿/袋区/合成判定 + registry terminology 条目);随批:Rule 11 雷格通行有价捷径注记(GD-2 降级 residual——A1 clearable 闸按安全格 8 邻连通计,强制过路费板生成期被拦截)· OQ1 增惩罚重复性裁决项(hit=true 有去重 / hit=false 惩罚无去重的不对称,实测会话前钉死) +- **A2 侧接口事实(provisional 登记)**:批次语义序仲裁(cleared-dig → stop → 其余,F4 四组合真值表 + RAP 记账随胜者);A2 零表读取零 ConfigDB 引用(D2"A2 无直接依赖"事实证实,AC-22③——若实现期 ADR 裁决需读表,走接口漂移) + +## 门禁增补记录(2026-09-09,B2 定稿登记) + +> B2 GDD:`design/gdd/b2-enemy-system.md`(**Approved**——2026-09-09 full 评审 8 blocking 同日核销收口,见下方增补块)· registry 已登记 enemy / hero / clause3_supply 三条目 + status_effect 两注记(single_enemy_assumption 回验改写 / ambush_unit_definition 量纲定稿)+ terminology 增 B2 六词条 · D2 EnemyTable 行回填 + HeroTable 行新增(接口漂移) + +- **B1×6 义务清单(B2 GDD 门禁评审逐项核对落位,缺项即 blocking——B3 对 B1 登记先例)**:① 单位接口调用集中在结算编排、行为环推进每结算恰一次(double-advance 无守卫)② 雷→伏击事实转化恰一次(每雷格实例至多一次)③ 伤害 clamp/负值/溢杀语义与死亡判定时机成文 ④ F1 条款 3 闭合对账(B2 cap 表值 + 量纲 + **A1 actuals p10 非 target 参数**)⑤ 默认目标规则消费与单体/AOE 分流(固定顺位)⑥ 伏击 cap 溢出"溢出未生效"明细行协议 +- **D1×4 义务清单(视觉推测型呈现,缺项即 blocking;2026-09-09 full 评审增补口径)**:① enemy_id→图像线索映射清单成文(**预告时序 = 下一拍预告、挖掘期全程可见**〔B2 Rule 8⑤〕;**含强度档位/环长直觉编码说明**;形状+色相双通道,art-bible 约束)② 线索映射**全局恒定**(enemy_id 键——跨 run 禁重掷)③ 单位顺位与存活可读(固定顺位推测前提;顺位标记资产入 B2 Visual Req 2 下限)④ 敌方 side 池呈现口径(B3 敌侧延伸) +- **B3 单敌假设回验结果**:不成立——定多敌(用户拍板;B3 预授权接口漂移条款兑现)——registry single_enemy_assumption 已改写(side-wide 兼容 + target_ref 扩展位);B3 GDD 正文不改,以 registry 为准 +- **ambush 量纲定稿(2026-09-08 B3 块"ambush 量纲"项兑现)**:+3 flat = 每次攻击类行为附加 3 点主角 HP(charge 拍零兑付)——registry OQ2 关闭条件;值仍 provisional,条款 3 锚验收归 B4/B1(B2 供给 clause3_supply 口径:单结算自然边界〔用户定稿〕+ cap 表值默认 4) +- **cap=4 定稿注记**:反灾难护栏定位(绑定阈 = 单轮 5–8 雷灾难轮);旧默认 8 使溢出协议死码(min(雷数≤8, 8) 恒等式)已废弃;量级注记——atk=4 时 L≥2 伏击分量过半(常态支配修正入口 = C3 atk 曲线 / B4·B1 m_amb 锚,登记方向非 B2 义务) +- **qa-lead 缺口预检 7 项**:🔴-1 query_attack/ambush_grant_plan face 归属(owner:实现期 ADR,B2 OQ1)· 🔴-2 多敌语义默认零消费者复验(owner:C1,B2 OQ2)· 🔴-3/4 已随批修正(F4 满配语境 / EC17 读写切分)· 5–7 承载于 AC 前言(信任编排 / HeroTable 单行落点 / 坐标登记协议) +- **视觉推测型注记**:意图可见性规格 = 不直接显示行为/数值,敌种图像承载稳定可学线索(用户拍板)——P1 护栏 = 线索映射每 run 恒定(D1×4②);结算明细数字兑现(P2 不变);双舞台资产管线陷阱不受影响(B5 前美术测试照旧,B2 只锁视觉状态清单数据面:grub 1 态 + ambusher 3 态) + +### 2026-09-09 B2 full 评审增补(六专员 + CD 终裁,8 blocking 同日核销 → Approved) + +- **胜利路径声明(CD 排序 #1;原文 2026-09-09,2026-09-11 用户裁决退役)**:原"hero atk=0 → 当前内容态结构性不可通关、B4 先行 = 任何可通关验证的先决"作废——**基础攻击恒存在**(HeroTable.atk 域 [1,999]、0 加载拒绝、MVP = 6 原型锚 provisional;主角每结算战斗段先手攻击顺位最前敌),胜利路径 = 基础攻击 + 奖励侧增量共同承载(B2 Overview 2026-09-11 改写;B1 战斗段同批增补主角先手攻击步 + hero_attack 来源值) +- **B1×6 之① 增补**:结算内调用序成文 **query_attack→apply_damage→advance**(倒序读到下一拍;B1 冒烟须回放结算步流);推进口径 = 每存活**敌**单位(主角无环,环接口对 hero 拒绝) +- **资产下限增补**:受击反馈/死亡表现/顺位标记三类入 B2 Visual Req 2 资产需求下限(顺位标记归 D1×4③、同等 blocking) +- **新增 OQ7/8/9**:精英/Boss 编码位(**C1 定稿强制核对**——键冻结下同深度第二敌种触发行集致命,改键走 D2 Rule 4 接口漂移)· grub 单拍环体验证伪(playtest 预注册,两拍环备用案归 C3)· 零伤害死局组合跨字段护栏(实现期 ADR/D2) +- **OQ6 更正**:标错惩罚事件源 = flag_judged{hit=false}(**非 mine_hit**)——B3 source_class 结构冻结,须 B3 GDD 修订非加行;B2 F3 重验触发补"新增非 mine_hit 授予源"支 +- **F4 供给口径修正**(registry clause3_supply 已同步):结构上界改 **min(AP 上界, cap)** 形(cap 腿松弛收紧)· direction_precheck 裕度 4.7×→**2.3×**(按条款 2 最低档锚 +50pp 实算) + +## 门禁增补记录(2026-09-09,B4 定稿登记) + +> B4 GDD:`design/gdd/b4-reward-item-economy.md`(**Approved**——2026-09-09 full 评审 14 blocking 同日修订核销后用户拍板放行;见下方 full 评审增补块)· registry 已登记 `reward_table` 条目 + 三处接口漂移改写(rng_service reward_roll scope 语义 = 每次 board install 批 / status_effect 悬空校验首引兑现 / clause3_supply 左臂载体 = B4 F6)+ action_economy.referenced_by 增 B4 · D2 六处回写(RewardTable 定稿行 / StatusEffectTable 注记 / Dependencies B4 行 / 双向一致性 B4←D2 / Tuning Knobs 占位删除 / Status 登记) + +- **B1×6 义务清单(B4 GDD Dependencies 全文,B1 GDD 门禁评审逐项核对落位,缺项即 blocking——B2/B3 对 B1 登记先例;2026-09-09 full 评审同步**:①段序含窗口锚点与回放输入 ②增"每格受理恰一次" ④增 usable_now 组装/逐件动态守卫/击杀结果进流水**)**:① **结算段序骨架成文**:授予段→**道具使用窗口(授予段后、增益/减益段前——full 评审用户拍板:当轮拾取当轮可用)**→增益/减益段→道具段→战斗段→衰减段→判定段(用户拍板核心序 buff/debuff > 道具 > 战斗不变;**窗口选择序列登记为结算回放输入事件**;B2 义务①②③的转化与推进落点随段序定位)② 奖励受理与分流编排(grant → buff 类 apply_effect / 道具类 stock;**每格受理恰一次、stock 与 grant 返回一一对应**;满层拒绝 = 零兑付 + "奖励未生效"明细行、无折算,**行携带可聚合计数/批次字段**)③ clear_reward 授予路径(**B3 OQ4 关闭条件**:install 期 roll、cleared 编排期兑付——兑付后同结算窗口即可选用)④ 道具段编排(窗口数据组装 = B4 query_inventory + **B1 侧守卫预判 usable_now**、选定序逐件施加、**满血守卫拒绝不烧包 + 同窗顺序多包逐件动态守卫**、**零效果判定读 B2 存活集 + "已消耗未生效"明细行〔E10 照常消耗拍板的 B1 侧承接——缺口预检 1 逐字核对〕**、**击杀结果进流水〔H3 感知数据前置〕**、治疗 clamp)⑤ 生命周期编排(battle_over 调 clear_inventory〔**清空前后快照计数供清空反馈行**〕;board retire 调映射消亡)⑥ roll_board 调用恰一次(B4 侧相位守卫见 E5/AC-1;B1 编排纪律承载调用时点半) +- **D1×5 义务清单(缺项即 blocking;2026-09-09 full 评审 3→5 扩容)**:① 已开格 + 库存奖励身份恒定可读(reward_id 键全局恒定、icon 形状+色相双通道——art-bible 约束;**库存常读条 = 挖掘期规划面**而非仅轮末窗口)② 道具使用窗口呈现(授予段后弹出;选用/跳过/选择序、窗口时机指示、**usable_now 可用性投影**〔满血治疗包禁选/提示〕、空库存建议不弹窗、窗口触发政策注记)③ 结算明细**四类行**呈现(道具行〔语境注记〕/ "奖励未生效"行〔**按聚合计数呈现**〕/ "已消耗未生效"行 / "使用被拒"行——数据源 B1 流水;未选用件"随战斗结束消亡"语境注记)④ **H2 清图奖励行强调呈现**(轮末仪式峰值)⑤ **battle_over 库存清空可见反馈**("剩余道具 ×N 随战斗结束清空"——RAP 清零可见反馈同款先例) +- **两项 E 节拍板(2026-09-09 用户裁决,其中第一项覆盖并行会话草稿)**:**道具段照常消耗**(选定即承诺——无存活敌的已选刀照常 Used + 零效果 + "已消耗未生效"行;不对称理由 2026-09-09 full 评审更正:原"敌 HP 不可见"与 B2 血条数字可读矛盾作废,现行理由 = 守卫所有权不对称〔满血守卫是表局部判定、零效果判定须读 B2 存活集〕+ 反抖动;误触防护由窗口 usable_now 预判承载)· **清图槽允许 buff 行**(A3 条款④兑现口径 = 行级恒有正当量〔F3 加载期校验〕,运行时满层零兑付为合法例外 + 明细行;预警④单独计数监控) +- **attack_up 复活 + 锚参照回迁(2026-09-11 用户裁决批——原"死行 + 锚参照迁移"条目〔2026-09-09,F4 口径后果〕退役为历史)**:基础攻击恒存在(HeroTable.atk 域 [1,999]、0 加载拒绝)→ attack_up 复活、唯一消费面 = 主角战斗段先手攻击(B1 同批增补);"道具伤害恒定表值不吃 percent 修正"拍板不变(attack_up 只乘基础攻击、不乘刀);A3 条款 2 锚参照对象**回迁 attack_up 当量**(换算归 B1 条款①对照表;B3 红警裁决前置不变);**attack_up 已随同日两级奖励批入默认根池**(attack_up_basic w20 t2——本条"入池随奖励改造批"残余已兑现) +- **两级奖励 + 关内延续批(2026-09-11 用户裁决,同批改写 B4/B1/B3/D2/registry + 切片落地)**:① 奖励按**交付轴两类**——即时类 {buff, instant_aoe, instant_heal}(授予段当场兑付、不进库存、零决策,默认根 75%)/ 可选类 {damage_item, heal_pack}(入库存、窗口选用、更强,25%)——category 五值枚举;② **库存生命周期 = 关卡域**(跨战斗延续、关卡末 clear_inventory——原战斗域拍板作废,"关卡结束"事件归 C1/C2、落位前上层显式调用 B1 level_clear);③ **窗口单件制**(至多选用一件——原任意多件作废);④ **治疗两级**:instant_heal 即时小额 + heal_pack 稀缺强治疗(w 25%→6%、p 4→12);⑤ buff 时长当次化(attack_up/defense_up D=1);⑥ **B1 义务⑤/D1 义务⑤改写**(battle_over 清库 → 关卡末 level_clear + 清空反馈);⑦ 同批登记:预警②④⑦结构性缓解注记、方向预检重算(刀供给 ≈1.8|资格格|/板 + 即时类与基础攻击分量)、F5 保底重锚(仅计 damage_item = 可选伤害保底)、P_min=8、AC-19 对账对更新 (knife_adv, attack_up_basic)。敌 hp / 奖励供给联动重算义务(预警⑥)归 C3 随批执行 +- **缺口预检 2 项阻塞级 deferral 至 B1**(qa-lead,共 11 项全文见 B4 GDD——lean 9 + full 评审 2):① B1 编排半整体 deferral(段序/窗口/守卫/四类明细行协议——冒烟以构造序扮演零断言)② A4① 当量升级与②③状态(AC-19 现为档位序检查——B1 对照表落定后升当量对账;**② MVP 结构性不可闭合〔clear_default 常量池,激活载体 = clear_\ 插槽〕、③静默绿灯风险——Dependencies 显式宣告**) +- **概念层口径交 consistency-check**(两处):主动道具 MVP 激活(game-concept"后续版本加入"→ use_timing 单值兑现)· 库存战斗域 vs Peglin"道具跨循环累积"参照差异 +- **索引错号修正顺带**:B3 块"EC9 reload gate"→"EC8"(D2 实际编号,上文已改) + +### 2026-09-09 B4 full 评审增补(五专员〔game/systems/economy/qa/ux-designer〕+ CD 终裁——NEEDS REVISION,14 blocking 同日修订核销,待 delta 复核放行) + +> 承重机制经对抗评审零动摇:install 期预分配确定性脊柱、封闭 RNG、F1–F6 边界实算、编排被动性、AC/夹具架构——CD 终裁"机械核心是管线迄今最扎实的,结构上没有需要推倒的东西";全部 blocking 为文本级/附表级(B2 先例)。日志 `design/gdd/reviews/b4-reward-item-economy-review-log.md`。 + +- **用户评审拍板:窗口锚点 = 授予段之后、增益/减益段之前(当轮拾取当轮可用)**——消除"当轮拾取下轮才可用"未陈述规则与末图清图奖励必然作废两伪影;核心段序(buff/debuff > 道具 > 战斗)不变。Rule 5/E10/E15/D1×②/B1×①④ 五处随批改写;E15/OQ4 主体消解(残余 = 未选用件消亡注记〔D1 义务③⑤〕+ 折算/继承归 Alpha C2) +- **跨文档真相分裂修复(三处)**:D2 Core Rule 5 与 D2 Interactions B4 行的 reward_roll 旧语义("每次奖励结算/每轮结算取号")补同步为 install 批语义(原定稿批只改了 registry 与 D2 Dependencies 行——B4"registry/D2 已同步"声明对 D2 正文失实,实现者按旧正文接线会使 B4 全部 σ 轨迹 AC 报废);systems-index A3 OQ5 注记、registry status_effect.anchor_warning、clause3_supply.direction_precheck 三处补 defense_up 锚参照迁移注记(迁移后预检未重跑事实同批登记);game-concept 创意风险 #1 / 可证伪信号 / agency 定位句三处 MVP 口径注记 +- **预读声称如实降级(四方独立确认的 overclaim)**:"奖励身份预读 = 我要去拿 X 路线规划/创意风险 #1 正面解"在 MVP 结构性不成立(reveal = 同轮授予、未开格不呈现)——四处承重位(Overview/Player Fantasy/E3/预警④)改写为真话:已开格+库存恒读 + 每格必有货 + 雾档先验 + 叠层感知挖掘;寻路 = OQ2/A5 二期形态;创意风险 #1 维持 OPEN;playtest 证伪信号按 MVP 口径读取(防"缺失功能被误判为设计失败") +- **结构新增**:**RewardConfigTable**(单行全局接口漂移——T_fog_min/K_dmg/N_draft/P_min 的 Rule 1 载体,registry `reward_config` 条目 + D2 表清单回填);**RewardTable 第九字段 display_name**;F5 加载护栏两条致命(floor_supply_missing / no_damage_row——后者关闭"K=0 零伤害世界静默复活不可通关"后门,B2 OQ9 并案);默认根内容行集登记(knife_t1/knife_adv/heal_t1/defense_up_basic/clear_knife + 刀供给方向预检——溯源原型锚);API 输入域语义补裁(E18 重复受理幂等/E19 清单外坐标/E20 stock 受理域/E21 枚举序)+ B1×②"每格恰一次" +- **AC 修复**:AC-14 分支 3 与 AC-3 分支 3 同输入矛盾消解(相位守卫不在豁免域);AC-6 分支 2 改 32σ 双重量化 + 分支 4 致命腿改烤入根(override 不触发加载校验);σ 协议补预推进机制 + 纸面契约子集声明(A2 OQ2 先例)+ b4_reroll 夹具补全登记 + 第二人复核义务;AC-15 增使用期快照分支;AC-19 默认根改非平凡对账对 (knife_adv, defense_up_basic);"无洞"声明撤回重述(覆盖口径 = EC 21/21 映射 + 输入域补裁,负空间学说边界如实登记) +- **E10 理由更正(拍板本体不变)**:原"敌 HP 精确值玩家不可见"与 B2 呈现规格(hp/max_hp 血条数字可读)矛盾——现行理由 = 守卫所有权不对称(零效果判定须读 B2 存活集,B4 零跨系统读不开口子)+ 反抖动;窗口 usable_now 预判承载误触防护 +- **经济侧登记**:失衡预警重校准扩容 ①–⑧(①拆短缺/浪费、④按吸收上限重推并拆稳态/连锁双表、⑤雾板分层〔A4 明文要求〕、新增⑥速杀退化/⑦窗口摩擦/⑧H2 曝光率);A4 硬约束②③ MVP 休眠显式宣告(②结构性不可闭合、③静默绿灯);C3 调参期批量仿真义务(F5 概率性致命带);H2/H3 升 D1 义务④⑤ + 创意风险 #2 爆发结算机制本体显式登记归 B1 强制检查点 +- **用语纪律清扫**:腿→分支(全文)、档位序代理→档位序检查、贴地/行洪水/自救面/换签/红警机械替换或加注,registry terminology 增 B4 六词条(刀/烧刀烧包/批尾/死行/档位序检查/预推进) + +## 门禁增补记录(2026-09-10,B1 定稿登记) + +> B1 GDD:`design/gdd/b1-settlement.md`(Designed,待 /design-review)· registry 新增 `settlement_table` / `clause5_dominance` 两条件目 + 四处接口漂移回写(weaken −25→−65 / anchor_warning F0 回执 / clause3_supply 闭合回执 / board_gen 消费回执)+ 三处 b1_obligations 履行回执(B3×8/B2×6/B4×6)+ terminology 五词条;D2 SettlementTable 已回填(接口漂移) + +- **F0 归一口径裁决(B3 红警关闭,B1 强制检查点兑现)**:条款②锚验收归一口径 = **每结算瞬时当量**(总暴露分支被 percent schema 域禁止 + 与 B2"单结算自然边界"冲突,排除);**m_w −25 → 值域 [60,85]/默认 −65**(终值 = B4/B1 共裁,B1 OQ2);地板 = 乘数零点 −100pp、交叉层数 ⌈100/m_w⌉ = 2(原"7 层触地板"预读更正);defense_up 参照保持 +- **条款①③⑤闭合首版验算完成(B1 单一闭合 owner)**:条款①对照表以 B3/B4 默认根活行为对照实物(D_ref=12 载体 = SettlementTable.equivalence_ref_damage;defense_up = 最低档安全格 buff 锚 6.0 HP/结算);条款③默认通过(L1 ≈2.0× / L2 ≈2.6× 边际配对)——**满配语境 unit_count=3 最坏盲点破线 → C3 调参约束**;条款⑤两态通过 + **保守地板缺口如实登记**(C 单独兜底不足,非阻塞)——**actuals p10 / fog_burden_ratio 交付后全部强制重跑(B1 AC-39/40)** +- **高光兑现节点双落(创意风险 #2/#3 机制本体,用户 2026-09-10 拍板——A2 登记义务条目化兑现)**:#2 **完美排雷**(板级零误标 ∧ ≥1 标中 → 清图奖励当量 ×P_perfect 默认 ×2;与踩雷正交、计数随 install 重置、RAP 刷新图无兑付点、上界受护栏 B 防钓鱼线 C_eff ≤ 24 约束);#3 **标中旗 highlight 标记**(数据面——当轮兑付 + 明细强调组合补偿 chord 缺席);呈现归 D1 +- **D1×3 义务清单(B1 出具,缺项即 blocking,并入 D1 既有台账 A2×6/B2×4/B3×4/B4×5)**:① 高光行强调呈现(perfect_sweep 高光行 / 标中旗 highlight / H2 清图奖励强调——数据源 = B1 Core Rule 14 流水行协议)② 完美排雷呈现形态 ③ EC5 零合法板提示/补偿/回退形态(数据面 = 非法产物标记上抛,A1 EC5 玩家侧体验债的 B1→D1 承接) +- **C3 消费 B1 对账输出的事实依赖成立**(2026-09-07 TD 门禁悬案回执)——对账输出 = 调参约束清单(满配破线 / 保守地板缺口 / 护栏 A/B 突破条件),非运行时接口;C3 GDD 定稿时回写本索引 +- **实现期 ADR 裁决项 ×2(B1 出具)**:事件总线载体(TD 门禁 2026-09-07 项的 B1 侧——与 A2 OQ2 同一裁决;AC-6/33 相关 THEN 为纸面契约直至落地)· B2 OQ1 两 face 归属(query_attack/ambush_grant_plan) +- **UX Flag**:B1 有 UI 贡献(结算明细屏 + 道具使用窗口)——Phase 4 前置 `/ux-design`,stories 引用 `design/ux/[screen].md` 非 GDD +- **B1 冒烟口径(项目首个全部玩法上游 Approved 的系统)**:真 A3/A2/B2/B3/B4 全集成 + A1 程序构造桩(取号契约同构,真 A1 全链路登记端到端 deferral)+ C1/C2 encounter 直供(provisional);可回放 AC-33 = D2 AC-15 deferred→B1 兑现;编排唯一性单向静态扫描(B2/B3/B4 API 符号命中文件 ⊆ B1 文件集) + +## 门禁增补记录(2026-09-12,战斗屏 UX 修订登记) + +> `design/ux/battle.md` /ux-review 完成:NEEDS REVISION 7 blocking 同日修订落稿(复检待跑)——B1 UX Flag 承载面即本 spec。同批用户拍板 **D4 弃图刷新前移挖掘期**,四处 GDD + registry 同步修订(接口漂移走各 GDD 修订流程,本块为索引登记): + +- **A3**:`request_refresh` Active 相位受理(原仅 Waiting)+ **每轮上限 2 次**(`refresh_cap_per_round` 结构位默认 2——D1 提案采纳,原"无结构性上限"作废;RoundStart 重置)+ Waiting 代理退役;**旧板旗随旧板作废**(零兑付、旗 AP 不返还——A2 EC11 全量重置承载);**经济预警**:挖掘期当场刷新无结算发生,原"敌方时间税"自惩半句失效——OQ3 弃图率/刷新率预警阈值联动复核(登记) +- **A2**:install_board 受理域增 **Active·仅弃图刷新路径**(B1 `refresh_board` 编排内、A3 扣费成功为前提;非刷新路径"防轮内换板"不变) +- **B1**:stopped/ap_exhausted 轮**恒 same_board**(原 Waiting 期刷新代理 + AC-29 退役/改写);新增**挖掘期刷新编排 `refresh_board`**(A3 扣费 → A2 retire → A1 generate → A2 install → B4 roll_board 恰一次;无结算段、无 round_end——编排唯一性原则保持);AC-30 改恒 same_board 口径 +- **systems-index 本体更正**:A4→D1 义务 ×2→×3(第③项 = 已开雾格 tint + 雾区全开完成信号——原索引漏记;OQ8 已于 battle.md 裁决采纳) +- **B4**:五处旧"battle_over 库存清空/随战斗结束消亡"摘要同步为关卡末 level_clear 口径(权威 = D1 侧义务清单⑤/L94) +- **D1 呈现义务台账状态**:battle.md 对 A2×6/A3/A4×3/B1×3/B2×4/B3×4/B4×5 的承接经本轮评审逐项核对(实质承接全齐;B1×3 映射错位、`settlement_snapshot`/`can_request_refresh` 自造接口引用已修正);entry 17①②③ 战斗屏落点齐备(场景级 AC 已入) + +## 门禁增补记录(2026-09-13,art-bible §1–4 定稿登记) + +> `design/art/art-bible.md` 已建成——2026-09-10 初稿(§1–4 核心方向)于本日增补批**定稿 §1–4**;2026-09-07 门禁排期项("最晚在 D1 GDD 前创建,至少 §1–4,'格子样张测试'并入执行")就此闭合。AD-ART-BIBLE solo 跳过(review-mode.txt = solo,出稿待人工复核)。 + +- **范围声明**:定稿范围 = §1–4(视觉身份宣言/氛围与情绪/形状语言/色彩系统);§5–9(角色/环境/UI/资产标准/参考方向)未建,**不阻塞 D1 GDD**——D1 消费面 = §1.2 绘制优先级栈 + §1.4 格子样张验收标准 + §3 形状语言 + §4 色彩系统 + §4.6 图标键契约。 +- **本批增补内容**:① 绘制优先级栈增补**徽章位**(数字 > 小人 > 旗 > 徽章 > 纹理 > 底色——极性提案 §0 栈位声明落位);② 雷格隐喻换轴同步(雷 = 被惊动的巢穴,"机关"旧称退役——game-concept 2026-09-12 换轴;兑现档红更名危险红,色值不变);③ **§1.4 格子样张测试验收标准成文**(第一验收项 = 数字可读性硬指标,game-concept 早期风险第 1 条;雾格不在样张范围);④ **§4.6 图标键契约冻结**(效果图标 B3 数据六行 + 提案新增 sapped;奖励徽章 B4 默认根八行 + clear_blast 并列预留;格面类型图标五类——极性提案 R1;敌种线索键 B2 Rule 8——键与通道分配冻结、终稿美术归样张/asset-spec 批);⑤ 雾格资产规格一行占位(内容封禁期暂缓,雾重设计批再启)。 +- **D1 GDD 引用义务**:D1 既有台账(A2×6/B2×4/B3×4/B4×5/B1×3)凡涉 icon/线索/徽章的呈现义务,一律以 art-bible §4.6 键契约为视觉侧依据;键集扩容(新效果/新奖励/新敌种/新类型)走 art-bible §4.6 修订,禁运行时换图。 +- **待随批事项**:极性提案实施批须按其 §3.5 正式登记 D1 义务条目入本索引 D1 台账(未开格类型图标/极性视觉语言/负向文案族);registry terminology 增 图标键/徽章/极性 三词条(本批已登记)。 + +## 门禁增补记录(2026-09-13,D1 定稿登记) + +> `design/gdd/d1-hud-framework.md` /design-system 完成:12 节 + 台账表/冲突清单零占位(solo 模式,出稿待人工复核;/design-review 须新会话)。registry 已登记 `d1_presentation_surface` 条目 + terminology 两词条(命令式回执驱动/程序化占位)。 + +- **义务台账核销(缺项即 blocking 项闭环)**:D1 既有台账(A2×6 / B2×4 / B3×4 / B4×5 / B1×3 / A4×3 / A3 + A1 EC5——battle.md §13 27 项)逐项落位表见 D1 GDD Dependencies(门禁评审核对用);已裁决项直接继承 battle.md(双舞台五区布局 / 路径预览=合法性指示 / 悬停预承诺 / 四格态 / 雾图例与首遇 / 四类明细行 / 窗口单件原位激活 / 高光行强调 / 库存关卡域清空反馈 / B3 空态=槽隐藏 / EC5 零合法板=仅提示)。 +- **切片教训三条硬性要求兑现(entry 17,D1 GDD 门禁评审核对项)**:① 信号载荷 schema 13 项成文表(Core Rule 17——A2 三事件/B1 行协议/B4 窗口载荷引用既有 schema + ux 遥测六行与命令式刷新面新成文)② 场景级 headless 测试进 AC(AC-19 战斗屏探针 + ADR-0004 双载体)③ 信号时点状态可见性规则(Core Rule 18:销毁前捕获/载荷自包含/禁回读核心生命周期态 + AC-20 静态扫描与像素重放断言)。 +- **D1→上游接口漂移登记 ×3**:① A3 只读观测量扩列轮内已用刷新次数(刷新按钮"本轮剩 N 次"读数缺口——OQ2)② B1 `settlement_snapshot` 契约登记(切片已实现、GDD 未登记——OQ3,D1 现行消费 = 步骤流判定行 + presentation_snapshot() 零阻塞)③ 极性提案实施批 D1 义务条目(冲突④)。 +- **D1 侧裁决两条**:① MVP 格面徽章只在已开格呈现(B4 Rule 8"未开格身份不呈现"为现行锁定规则——art-bible §4.6.3 未开格预读的机制前提随极性提案实施批,冲突⑤)② 订阅纪律按 ADR-0001 收窄(呈现层只订 B1/runner;battle.md §Events Fired 措辞差列冲突⑥待复检批对齐)。 +- **下游交付物(B5/D4/D5/A5/D6 输入)**:五区布局与双舞台裁决(第三形态)+ 键位映射表(§4.6 全键族)+ 呈现语义(敌人卡死亡禁瞬消/明细带/高光行)——B5 只叠加动画密度;D4 挂点 = 明细行类型流 + toast 触发;D5 承载位 = toast 体系 + 图例面板;C4 = TunePanel 退役接收方。 +- **命名统一核销(session-state entry 35)**:request_refresh / refresh_cap_per_round / install_board 受理域语义 / round_end_board·round_end_stopped 实名——D1 GDD Rule 20 对照表 + AC-25 grep 断言;切片四 provisional 名退役。 + +## GDD 设计顺序 + +1. D2 数据配置与数值表 +2. A3 行动经济 +3. A4 地形变异 +4. B3 增益/状态系统 +5. A1 扫雷棋盘生成 +6. A2 排雷玩法规则 +7. B2 敌人系统 +8. B4 奖励与道具经济 +9. B1 战斗结算规则 +10. D1 HUD 与界面框架 +11. A5 扫雷辅助道具 +12. C3 难度曲线与精英/Boss +13. C2 冒险结构 +14. C1 关卡节点地图 +15. B5 战斗表现 +16. C4 元进度 +17. D3 存档序列化 +18. D4 音频 +19. D5 教学引导 +20. D6 设置/本地化 + +## 进度追踪 + +| ID | 系统 | 状态 | GDD 路径 | +|---|---|---|---| +| D2 | 数据配置与数值表 | Approved(2026-09-07 两轮评审 + delta 复核收口) | `design/gdd/d2-data-config.md` | +| A3 | 行动经济 | Approved(2026-09-07 三轮 re-review 8 条文本级 blocking 同日核销;用户拍板免独立 delta 核销直接放行。F1 五条款契约与 refresh 玩家意图路径为 B1/B4/D1 GDD 的强制输入;日志 `design/gdd/reviews/a3-action-economy-review-log.md`) | `design/gdd/a3-action-economy.md` | +| A4 | 地形变异 | Approved(2026-09-08 两轮独立评审:full 8 阻塞 + 同日 re-review【六专员 + CD 终裁】6 阻塞均同日核销;CD 终裁 delta 核销免第三轮,用户拍板放行【A3 先例】;承重数学(厚度≤2【≡任意 3×3 窗口非全雾】/f≤8d/两态 AP/F1 谓词)经两轮对抗零动摇;日志含两轮阻塞→修复对照表:`design/gdd/reviews/a4-terrain-variation-review-log.md`)。**门禁登记(2026-09-08 re-review 补全——下游 GDD 门禁评审逐项核对落位,缺项即 blocking)**——**A1×15**:① 候选池门控(F1)② cell_constraint 生成期强制 ③ 出生区及生成期可达域永不含雾(可达域 A1 确定性计算)④ 可推理率按雾边双向净效应建模(寂静减项 × 推理锚增项,禁单向补偿)⑤ 清图预算增量构成核算(雾致预算增量 = 链不可达雾格数 × dig_ap_cost,设计期聚合校验、违例 = 调参修正)+ 链不可达雾格占比生成期计算输出(B1 对账输入生产者义务)⑥ 空池静默 ⑦ 降级静默 + 产物合法 + 部分达成按 shortfall 分级上报(EC2)⑧ 多变异选取消费 board_gen 流(回放确定性)⑨ EC3 邻接出生区合法(谓词 = 出生 3×3 及可达域内无变异格)⑩ EC10 雾占比参数域(< 全覆盖 × 可推理率联动,可判定谓词)⑪ EC11 双雾区实例独立(布置身份与 board_gen 取号独立)⑫ 厚度≤2【≡任意 3×3 窗口非全雾】/ f≤8d 参数域 ⑬ EC2 判据含连通分量尺寸 ⑭ 降级率按 (level_index, node_depth) 埋点 + 最低雾出现率义务【批量统计断言形式,阈值与样本量归 A1 GDD 定】+ 禁放宽 Rule 3 达成 ⑮ board_biome 非空域校验(fail-fast)。**A2×4**:EC4 连锁开雾格(无数字 + 级联停止 + 计入清图 + 奖励入库)· EC5 雾内旗恒不中(含标错惩罚上线后一致性推论)· 开格上报携带变体标记(数字显示判定数据源)· EC8 棋盘持有生成期变异快照、抗表重载。**D1×3**(2026-09-12 更正:原记 ×2 系索引漏记第③项——A4 GDD UI Requirements 明写"三个呈现义务"):① 图例"迷雾 = 无雷盲区"条目(恒在、不设解锁门、可回看)② 首遇提示(触发 = 首个实际含雾板进入可操作状态、每 run 一次;必含要点 = 无雷盲区 / 雾格不传播连锁 / 雾内旗纯损;非模态不劫持输入)③ 已开雾格 = 地板无字 + 底色层 tint 残留与雾区全开一次性完成信号(验收候选转正——OQ8 已于 design/ux/battle.md 裁决 2026-09-12:采纳 tint + 视觉合并域信号单元;详见 A4 UI Requirements③)。**B4×3 定价硬约束**:雾内奖励档位下界 ≥ 最低档安全格 buff 当量 · 清图奖励锚随雾成本负担(链不可达同分母)上浮 · 雾板净当量优势带上界(防福利)。**B1 条款 5 输入**:三腿入账两态验算 + 雾板净当量优势带 + 防钓鱼方向(对账 A3 OQ6)+ 链不可达占比(A1⑤ 生产者)。承重规则不变:生成期布置约束(雾区仅非 0 安全格【区内无雷无 0 格】)+ 零特判原则;"雾区无雷"为玩家可见知识;A3 F1 条款 5 对账注记三腿入账(非"消解");registry variation_table / variant_pool_gate / fog_ap_note 已同步改写 | `design/gdd/a4-terrain-variation.md` | +| B3 | 增益/状态系统 | Approved(2026-09-09 full 评审 NEEDS REVISION 7 blocking 同日核销:单敌假设入文〔用户拍板〕· B1 义务 5→8 · D1×4/B5×2/D5/B4 呈现义务登记 systems-index · AC-15/18 重写 + dup_key 致命夹具 · weaken 红警补修复空间不对称 + OQ1 归一口径前置;承重机制〔F1–F3 复算/AC 覆盖/先兑付后衰减〕经对抗评审零动摇,用户拍板免独立 re-review 直接放行【A3 先例】;**weaken 行条款 2 锚红色预警维持登记——B1 强制检查点优先裁决**;日志 `design/gdd/reviews/b3-status-effect-review-log.md`) | `design/gdd/b3-status-effect.md` | +| A1 | 扫雷棋盘生成 | Approved(2026-09-09 full 评审 MAJOR REVISION NEEDED——8 blocking 同日修订核销 + cheap-now 6 项顺路核销,用户拍板免独立 re-review 直接放行【A3/A4 先例】。承重修订:**P1 可推理率两项达标判据**(inferability_core = 剔除雾格后的可推理率,防止"雾区无雷"知识单独撑线)+ **target 域 [0.85,1] 绝对锚**(断言 A″)+ **雾占比绝对上限 0.25** + **F2c 成本闸**(attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生)+ 雷数取整/布雷钉死(round 先定数、先布后删)+ F2≡Schema 合流(count=0⇒f=0 腿)+ AC-6(a) 芯/环改写 + AC-11 前提腿 + **断言 B 收紧达成区占比**(A4⑭ 两件套落位)+ **第三轴登记约定**(Boss=Dmax 行 + elite_/boss_ 命名空间预留 + OQ6 C1 定稿强制重审);σ 打包防碰撞修复;D2/registry 接口漂移已同步。recommended 9 项未核销留档(AC-10(d) 条件化、⑬ 差分夹具、板ⅲ′ 对照板、O4 登记等——见日志);日志 `design/gdd/reviews/a1-board-generation-review-log.md`) | `design/gdd/a1-board-generation.md` | +| A2 | 排雷玩法规则 | **Approved**(2026-09-09 full 评审:四专员〔game-designer/systems-designer/qa-lead/ux-designer〕+ CD 终审 NEEDS REVISION 六 blocking 同日修订核销,用户拍板放行【A3/A4/A1/B3 先例】。承重修订:**连锁不越旗**(GD-1——传播集 ∖ G,旗 = 1 AP 链阻断器)· **F4 到达序声明收窄**(划分序无关仅异类四组合)+ AC-10 耗尽型救援/联合清图用例 · **ActionOutcome.sub_reason 封闭枚举**(拒绝可辨数据契约)· **D1×6 义务包**(格态可辨/悬停/雷格可读/路径语义/单意图帧/voided 呈现)· AC 修复包(AC-6 编号/b_chain·b_fog spawn=(3,2)/诊断五类枚举/ap=1 同帧竞争)· 用语纪律清扫 + registry terminology 条目;随批 Rule 11 有价捷径注记 / OQ1 惩罚重复性裁决项 / B1·B4 高光与溢出两条登记。承重结构〔确定性引擎/三事件面/F1–F4/零特判〕经对抗评审零动摇;日志 `design/gdd/reviews/a2-minesweeper-rules-review-log.md`。**下游义务见门禁增补记录 2026-09-09 A2 块:A1 修订义务(🔴 反例 B)、B1×3 + 高光/溢出两条、D1×6,缺项即 blocking**) | `design/gdd/a2-minesweeper-rules.md` | +| B2 | 敌人系统(战斗单位系统——CD 门禁扩容身份) | **Approved**(2026-09-09 一次会话定稿 + 同日 full 评审〔六专员 + CD 终裁〕NEEDS REVISION 8 blocking 同日核销、用户拍板免独立 re-review 直接放行【A3/A4/A1/B3 先例】。lean 定稿:lean 门禁 systems-designer〔Formulas〕/qa-lead〔AC 17 条 + 覆盖矩阵 + 缺口预检 7 项〕已咨询,CD 视角随 full 评审补位。定稿要点:**多敌模型**(1–3 同敌种单位——B3 单敌假设回验不成立,side-wide 零接口变更兼容 + target_ref 扩展位;registry single_enemy_assumption 已改写)· **HeroTable 新增**(接口漂移回写 D2,单行全局,atk=0 机制开关)· **意图视觉推测型**(不预告数值/行为,敌种图像线索稳定可学、全局恒定——CD 承重项定稿 + full 评审补预告时序⑤)· **Peglin 行为类型学转译**(行为环确定性轮转:attack/charge/heavy MVP,guard/boost/hinder/summon 预留校验拒绝;双敌种 grub/ambusher 双环;死亡即不动手 + 同步轮转定理)· **伏击量纲定稿**(每攻击类行为 +3 HP 附加、charge 零兑付——registry OQ2 关闭条件)· **cap 默认 4**(反灾难护栏;旧 8 溢出协议死码废弃)· 条款 3 供给 = 单结算自然边界(用户定稿)· EnemyTable 八字段回填 + 每行集恰一敌种校验。**下游义务 B1×6 + D1×4 缺项即 blocking**(① 增调用序成文;D1×4① 增预告时序/档位、② 全局恒定)。full 评审承重修订:**当前内容态无胜利路径声明**(B4 先行 = 可通关先决)· 资产下限补受击/死亡/顺位 · 多敌夹具根改 ambusher 行(三处死测试修复)· AC-7 恒假判别加 m_e≥2+SKIP · OQ6 事件源更正(flag_judged 非 mine_hit)· OQ7 精英/Boss 编码位预登记(C1 强制核对)· 主角推进域排除 · F4 裕度 2.3× + min(AP,cap) 收紧(registry 同步);**新增 OQ7/8/9**。承重机制〔F1–F4 实算/同步定理/side-wide 池〕经对抗评审零动摇;日志 `design/gdd/reviews/b2-enemy-system-review-log.md`) | `design/gdd/b2-enemy-system.md` | +| B4 | 奖励与道具经济 | **Approved**(2026-09-09 full 评审〔五专员 + CD 终裁〕NEEDS REVISION——14 blocking 同日修订核销,用户拍板免独立 re-review 直接放行【A3/A4/A1/B2/B3 先例】;核销质量依据 = 全部 blocking 为文本级/附表级、承重机制〔预分配确定性脊柱/封闭 RNG/F1–F6 边界实算/编排被动性/AC 架构〕经五方对抗零动摇。承重修订:**窗口锚点移授予段后**〔用户评审拍板:当轮拾取当轮可用〕· **跨文档真相分裂修复**(D2 正文两处旧 reward_roll 语义补同步 / 锚参照迁移三处传播 / game-concept 三处注记)· **预读声称如实降级**(创意风险 #1 维持 OPEN,寻路归 OQ2/A5)· **RewardConfigTable 新增**(Rule 1 载体)+ RewardTable 第九字段 display_name + F5 加载护栏两条致命(no_damage_row 关闭零伤害世界后门,B2 OQ9 并案)· 默认根行集登记 + 刀供给方向预检 · API 输入域补裁(E18–E21)+ AC-14/AC-3 矛盾消解 + σ 预推进/纸面契约声明 · E10 理由更正(拍板不变)· 失衡预警①–⑧ 重校准 · A4②③ MVP 休眠显式宣告 · 用语纪律清扫 + terminology 六词条。lean 定稿要点(2026-09-09):install 期身份预分配 / 库存战斗域 / 恒定表值(attack_up 死行)/ F5 草稿重 roll / 道具段照常消耗 + 清图槽允许 buff 行两拍板。registry `reward_table` + `reward_config` 两条目;**下游义务 B1×6 + D1×5 缺项即 blocking**(见门禁增补记录 B4 块 + full 评审增补块);缺口预检 2 项阻塞级 deferral 至 B1;日志 `design/gdd/reviews/b4-reward-item-economy-review-log.md`) | `design/gdd/b4-reward-item-economy.md` | +| B1 | 战斗结算规则 | **Approved**(2026-09-10 /design-review NEEDS REVISION 8 blocking → 同日修订批核销〔①完美排雷 ×2 真实双份交付载体 + p_perfect 载体域 {100,200} + 高光有效兑付/降级规则 ②F5 挖穿残余池模型重推——条款⑤ verdict 翻转为未闭合、登记 C3/B4 约束包(C 量级上调 or 接受非支配设计态,二选一归 C3)③F5/AC-40 增多敌腿 ④行协议字段分层(target/effect_id/pool_value + enemy_action/flag_miss)⑤hit=false"标旗未中"零值行 ⑥P_perfect ÷100 成文 ⑦AC-18 腿 2 改写 ⑧F4 双口径 + 域守卫 + 护栏 B 输入登记 + clamp 八字段统一〕,用户拍板免 re-review 直接放行【A3 先例】;**recommended 19 项留后续批次**(回放输入通道穷尽/AC-6 机制承重句/呈现宣称条件句/效价维度/正侧批次键/D1 义务扩容/调参治理完整依赖表等——owner/时机见原 recommended 清单);registry 三条目同批回写(settlement_table 八字段/clause5_dominance 重登/clause3_supply 双口径);下游义务 D1×3 维持缺项即 blocking;日志 `design/gdd/reviews/b1-settlement-review-log.md`。定稿要点存档:lean 门禁 systems-designer〔Formulas F0–F5〕/qa-lead〔AC 43 条 + 覆盖矩阵 + 缺口预检 12 项〕已咨询;CD-GDD-ALIGN/art-director 按 lean 跳过留人工复核注记。**编排唯一性**(B2/B3/B4 战斗域 API 唯一调用方 + 可回放步骤流〔结算禁 await,D2 AC-15 兑现落点〕+ 零随机)· **结算触发 = Waiting 相位编排点**(不依赖事件到达序)· **段序骨架**(授予→窗口〔授予段后〕→增益/减益→道具→战斗→衰减→判定)· **23 条上游义务全落位**(B3×8/B2×6/A2×3/B4×6——AC 覆盖矩阵 23/23)· **F0 归一口径裁决**(瞬时当量;m_w −25→值域 [60,85]/默认 −65——B3 红警关闭,接口漂移回写 registry)· **条款①③⑤闭合首版验算**(对照表以 B3/B4 实例为实物;条款③默认通过〔双口径:锚行 2.0–2.6×/期望 1.2–1.5×〕、满配 unit_count=3 破线→C3 约束;条款⑤未闭合〔挖穿池模型〕→C3/B4 约束包;actuals p10 交付后强制重跑)· **高光双落**(#2 完美排雷真实双份交付 / #3 标中旗 highlight 标记——护栏 B 防钓鱼双读数约束)· **战斗段短路**(hero hp=0 即中止剩余敌作用——用户拍板)· SettlementTable 八字段回填(三行致命 manifest)· 流水行协议 13 类 + 字段分层(target/effect_id/pool_value)。registry 新增 settlement_table/clause5_dominance + 四处接口漂移回写 + 三处履行回执 + terminology 五词条;缺口预检 3 项阻塞级(A2 事件载体 ADR / σ 登记轨迹 / D1 台账)) | `design/gdd/b1-settlement.md` | +| D1 | HUD 与界面框架 | **Designed**(2026-09-13 /design-system solo 模式一次会话定稿——**专员/总监代理全部跳过,出稿待人工复核**;未经 /design-review,独立评审须新会话。定稿要点:**呈现义务台账 27 项逐项落位**(A2×6/B2×4/B3×4/B4×5/B1×3/A4×3/A3+A1 EC5——GDD Dependencies 落位表,battle.md §13 承接升格系统级验收)· **切片教训三条硬性要求兑现**(entry 17:信号载荷 schema 13 项成文表 Core Rule 17 / 场景级 headless 探针 AC-19 / 信号时点状态可见性规则 Core Rule 18 + AC-20)· 订阅纪律按 ADR-0001 收窄(呈现层只订 B1/runner + 命令式回执驱动五面成文——registry `d1_presentation_surface` 登记)· **命名统一核销**(entry 35:request_refresh / refresh_cap_per_round 正式名,AC-25 grep 断言零残留)· 程序化占位策略(art-bible §1.4 引用、样张不阻塞、占位同受数字可读性硬指标)· _draw 维持 GridContainer 裁决(ADR-0003 Decision 3——复议留样张后,OQ1)· 冲突清单 ⑧ 项(含⑤ D1 裁决:MVP 格面徽章只在已开格呈现——B4 Rule 8 承接,未开格预读随极性提案实施批)· 接口漂移登记 ×3(A3 轮内刷新计数读数缺口 / B1 settlement_snapshot 契约 / 极性提案批 D1 条目) | `design/gdd/d1-hud-framework.md` | +| A5 | 扫雷辅助道具 | 未开始 | — | +| C3 | 难度曲线与精英/Boss | Designed(2026-09-12 /design-system solo 模式一次会话定稿——**专员/总监代理全部跳过,出稿待人工复核**。定稿要点:**直填行值主体 + DifficultyTable 修饰结构位(MVP 空表)**——B2 Rule 10 兑现走接口漂移 · 投放节奏 OQ8 兑现(L1 三段:grub→伏击兽→boss_guardian)· 切片 SLICE-TUNED 行集转正 MVP 默认曲线 · **难度期望表**(通关率目标带 L1 基线 40-60% / 供给平衡带 [1.1,2.0])· F1–F7 设计期工具式(运行时零新增公式)· 调参流程 F7 六步强制 · B1 条款③⑤/预警⑥约束承接(条款⑤ MVP 接受非支配设计态 + OQ4 playtest 观测)· A4 scope=global 语义定稿(仅 Boss/精英图)· 6 个 OQ。**注意:本 GDD 未经 /design-review——solo 模式出稿,独立评审待新会话**) | `design/gdd/c3-difficulty-curve.md` | +| C2 | 冒险结构 | 未开始 | — | +| C1 | 关卡节点地图 | 未开始 | — | +| B5 | 战斗表现 | 未开始 | — | +| C4 | 元进度 | 未开始 | — | +| D3 | 存档序列化 | 未开始 | — | +| D4 | 音频 | 未开始 | — | +| D5 | 教学引导 | 未开始 | — | +| D6 | 设置/本地化 | 未开始 | — | + +## 备注 + +- 原型结论已作为 GDD 素材:小人控制、AP 8、标雷判定、奖励统一结算、增益跨轮次不跨战斗等定稿见 `design/gdd/game-concept.md` +- 概念文档待定问题(主动战斗道具、标错惩罚、节点地图形态、反雪球)将在对应系统 GDD 中解决;~~停手折算比例~~ 已随 2026-09-07 A3 RAP 结构修订作废(停手 1:1 转存 RAP、清图作废) +- **概念层增补(2026-09-09,用户定稿)**:产品名 **Dungeon Sweeper** + 世界观背景(地牢下探叙事 + 机制隐喻映射)+ 美术方向"格子 = 地牢地砖"已写入 game-concept.md;P4 支柱增命名承载。均为表现层/命名层,不触碰任何已锁机制数值与接口(A1–B3 已定稿 GDD 不受影响);art-bible 任务照旧排期(最晚 D1 GDD 前),"格子样张测试"验收项不变 +- **概念层增补(2026-09-12,用户拍板)**:叙事属名换轴——雷格 = **惩罚格**(类型化属名:首发地牢类型 = 怪物巢穴;陷阱/岩浆/冰冻化身及各自机制分化登记为未来机制侧方向归 A1/A2——"陷阱→直接扣血 / 标中陷阱→怪物受伤害"与现行"开雷→敌方优势"锁定规则的取舍归机制侧,概念层不承诺)+ 插旗玩家可见动词 = **控制**(类型无关;对内文档与机制协议仍为"旗/插旗";"封印/镇/反制"降级为类型风味词)+ 连锁展开 = **坍塌显形**(B5 连锁动画 / D4 音效方向登记)+ 标错惩罚叙事 = **控制失败的失控反噬**(随机读作失控;重复性措辞随 A2 OQ1)+ 宝箱呈现仅绑定可使用道具类奖励(非道具奖励不摆箱;"搜刮/遗落物"为可选氛围框架登记不锁)+ 世界观厚度 = **碎片点缀**(不含前人日志)——已写入 game-concept.md 世界观与命名节(映射表 + 背景句,带版本注记)与视觉锚原则 5 表现措辞;均为表现层/命名层,不触碰任何已锁机制数值与接口(A1–B4/B1/C3 已定稿 GDD 不受影响);依据记录:`design/research/lore-nest-metaphor-2026-09-12.md`(⑤节 6 问同日拍板回写);D1"控制"动词手感、art-bible 旗形态/巢感样张、D5 教学措辞、D6 结算风味词输入只在记录③登记,各归各的时机 diff --git a/design/gdd/legacy-2026-09-14-v1/a1-board-generation.md b/design/gdd/legacy-2026-09-14-v1/a1-board-generation.md new file mode 100644 index 00000000..bc58da63 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/a1-board-generation.md @@ -0,0 +1,681 @@ +# A1 扫雷棋盘生成(Board Generation) + +> **状态**:In Revision · lean 格式——2026-09-09 full /design-review 判 MAJOR REVISION NEEDED,8 条 blocking 与 cheap-now 包同日修订完成,待 delta 复核放行 +> **版本**:v1.1-lean · 2026-09-13 · 作者:user + agents(systems-designer 起草 Formulas〔F1–F5 + 三轴分离口径〕· qa-lead 起草/验证 AC〔19 条 + A4×15 逐项映射〕) +> **承载支柱**:P1 主——可推理率生成端保障("出题人义务")· P4(越下越险的难度物质)· P3(雾成本空间面,间接) +> **层级**:Layer 2 Core · Milestone MVP · 设计顺序第 5 位 +> **一句话**:每当战斗编排需要一张新扫雷图,A1 从 (level_index, node_depth) 难度参数出发一次性生成完整棋盘——尺寸、雷布、初始 3×3 安全区及展开可达域、变异布置;同种子同参数必产同图,并在生成时刻承诺可推理率门槛——玩家永不看见生成器,但每一轮都踩在它铺的"我算对了"地基上。 +> **复核注记**:lean 门禁下 systems-designer / qa-lead 已委托起草;creative-director 的体验锚点情绪定位与 art-director 的呈现一致性未经咨询——生产前人工复核(体验锚点节 + 呈现与 UI 契约节 vs art-bible);评审日志 design/gdd/reviews/a1-board-generation-review-log.md + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 确定性棋盘生成器:生成管线为无状态函数(每次调用独立、可重放),从 BoardGenTable 行参数一次性产出完整棋盘 | +| 拥有 | 五阶段生成算法 · BoardGenTable schema 与数值回填(D2 Rule 3,首个进度键表)· 可推理率度量口径(F1)· 雾参数域/可行性/成本谓词(F2/F2b/F2c)· 清图预算增量构成(F3)· 批量统计断言口径(F4)· 验收算子(F5)· A4 变异约束的生成期强制执行(A4×15 清单唯一执行落点) | +| 不拥有 | 挖掘执行(A2)· 换图编排(B1)· 难度曲线数值与调参(C3——仅类型依赖 schema,禁反向调用生成)· 变异约束声明(A4)· dig_ap_cost 价目(A3——只读引用)· "够不够"的判(清图预算判归 A1/A3 契约与 C3)· 推理器/布置算法实现选型(ADR 期)· 呈现(D1/A2/B5) | +| 上游输入 | BoardGenTable 行(D2 R1 floor 解析)· VariationTable / variant_pool_gate 谓词(A4)· dig_ap_cost(A3 只读)· board_gen 流取号(D2 RngService) | +| 下游输出 | 棋盘产物(格状态 + 变异标记 + 出生区)+ 八项生成期指标集(A2 消费、B1 对账输入)· 曲线导出工具数据面(C3)· board_biome 图类型上下文(B4 差异化插槽输入) | +| 硬约束 | 全部随机取数只经 board_gen 流(一次 open_stream = 一张图全部取数;流外零随机、randomize() 禁用)——同种子同参数必产同图 · 零字面量(读取经 ConfigDB)· 零特判产物 · 加载期校验 fail-fast | + +- 缺位后果:游戏没有难度轴(关卡×深度)、没有 P1 的源头保证、A4 的变异无布置载体、C3 的曲线无落点——A1 是 Core 层的地基,也是全项目验收债务最集中的系统(A4×15 清单 + 最小安全格数下限 + 链不可达占比生产者义务都在此落位)。 + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 每一步都算数(生成端) | "这局能算"的底气 | 可推理率生成参数 = 出题人义务:每张图在生成时刻承诺"从初始安全区出发,逻辑可推导的挖掘占比不低于门槛"——D2"公平可信"的玩法侧对偶(D2 保同种子同结果的可复现,A1 保同参数同品质的可推理);玩家输给的是自己选的赌(雾区/终局盲猜),不是不可解的局 | +| P4 越下越险 | "下探的体感" | 雷密、尺寸、雾占比随 (level_index, node_depth) 爬升——难度物质由 BoardGenTable 行参数直接雕刻,非文案 | +| P3 风险与收益同源(空间面) | "雾里面有价值" | 生成期布置约束兑现:雾区无雷、雾贴雷簇(A4 Rule 3 结构推论)——洞口前的权衡是真实的信息权衡而非装饰 | + +- 情绪目标:"这局能算"的底气——A1 不制造情绪,它制造情绪的前提条件(玩家永不看见生成器,但每轮都在体验它的产物)。 +- 参考感受:扫雷原版"每局必有解"的玩家信念(原版实际不保证——本作把这份信念变成生成端契约)· Into the Breach 每张谜题可解的设计诚实。 +- 反向护栏(裁剪判据):"纯猜局"是 P1 的死亡体验——生成端若让玩家频繁坠入不可推理局面,结算端再华丽也救不回掌控感;红线 = 生成参数使可推理率跌破门槛即为阻塞缺陷(验收红线,非调参建议)。 + +## 全局流程 + +### 生成管线(五阶段,顺序冻结) + +``` +generate(level_index, node_depth, rng) -> Board + 生成期指标集 + │ ① 参数解析 ── BoardGenTable 行(D2 R1 floor 解析;域/跨字段谓词加载期已校验) + ▼ + ② 布雷 ──────── N = round(mine_density × (W×H − 9)) (先定数,9 = 出生 3×3 恒排除) + │ 在"全格 − 出生 3×3"域经 board_gen 流均匀布雷 + │ → 计算 0 格 flood 闭包及外扩 1 环 → 落在环内的雷移除 + │ 【先布后删:只减不增、不重投、无迭代、确定性终止】 + │ 实际雷数 |M| ≤ N(漂移 = 环内移除量,入指标集观测) + │ 初始 3×3 及可达域内恒无雷;N ≥ 1 由加载期谓词保证(零雷板拒绝) + ▼ + ③ 变异布置 ──── 先布雷后布雾(雾的 cell_constraint = safe_nonzero 要求雷先就位) + │ 候选池门控(消费 variant_pool_gate)→ cell_constraint 生成期强制 + │ → 出生区及可达域排除 → 雾形硬边界(厚度≤2【任意 3×3 窗口非全雾】 + │ / f≤8×mine_density)→ 区域参数来自行参数(region_count / target_ratio) + │ 多变异选取消费 board_gen 流、双实例布置身份与取号独立 + ▼ + ④ 生成-验证循环 ─ 布雾完成后测可推理率(A4 要求的雾边双向净效应因此内生于测量) + │ 实测 < target → 重试(同图种子实例内继续取数,不重开 scope) + │ 上限 max_gen_attempts 次 → 三分支出口(见出口分发表) + ▼ + ⑤ 产出与上报 ── 棋盘 + 八项生成期指标集(每阶段失败语义见状态表) +``` + +### 出口分发(验证循环三分支,F5 验收算子) + +| 出口 | 触发 | 行为 | 埋点 | +|---|---|---|---| +| 达标即出 | 存在合法尝试满足两项判据(inferability ≥ target ∧ inferability_core ≥ target) | 出最早达标尝试 k*(非全局 argmax) | attempts_used 记录 | +| 接受最优 | attempts 用尽、无达标但有合法尝试 | 出合法集内 inferability 最高者(tie 取最早、零额外取数)+ 降级标记 | 强告警埋点——运行期不死锁、开发期暴露参数问题 | +| 接受最不坏 | attempts 用尽且零合法尝试 | 确定性劣度字典序(不可达安全格数, min_safe 缺口, 序号)最小者出图 + 非法产物标记 | 最高级告警——玩家永不卡死,红线由 F4 断言 A′ 批量守 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:每张新图都按固定的五步流水线生成 +**人话**:这条管生成器本体:每当换图需要一张新扫雷图(生成函数被调用一次),生成器就按固定五步跑完——读难度参数、布雷、布雾布变异、自检重试、交货。五步的先后顺序冻结:不许调换、不许跳步。 + +| 阶段 | 做什么 | +|---|---| +| ① 参数解析 | 读难度参数表里这一关这一深度的行(尺寸、雷密度、及格线、雾参数) | +| ② 布雷 | 先按密度定死雷数、再落位;0 格展开环内的雷落完再删——只减不增、不重投 | +| ③ 变异布置 | 先布雷、后布雾(雾里不许有雷,雷必须先就位) | +| ④ 生成-验证循环 | 布完雾才测可推理率,不达标就在图内重试 | +| ⑤ 产出与上报 | 交出棋盘和八项生成期指标 | + +**举证**:无计算(纯流程规则)。五步字符画见「全局流程·生成管线」;每步失败语义见「状态」节状态表。 + +#### 规则 2:每张图生成就承诺:不靠赌能挖开的格子占比达标 +**人话**:这条管图的品质评分。生成器给每张图算一个"免赌占比":评分模拟从开局展开后玩家能看见的盘面出发,与玩家信息严格同步——没开的格看不见、雷位不许偷看;能靠确定性推理挖开的安全格数除以总安全格数,就是占比。每张图要同时过两条及格线:整图占比达标,剔除雾格后的占比也达标——防"雾区无雷"这条常识单独把分数撑上去。两个占比都随图输出,供审计与批量断言。 + +| 术语 | 人话含义 | +|---|---| +| 免赌占比 inferability | 确定性推理能挖开的安全格数 ÷ 总安全格数 | +| 雾外占比 inferability_core | 同上,但分子分母都剔除雾格——两条及格线的第二腿 | +| 及格线 inferability_target | 难度参数逐行设定,取值域 [0.85, 1];域下限 0.85 = "纯猜局红线"的绝对锚 | +| 推理器 | 单点规则 + 子集消减;MVP 冻结此域——改域 = 改可推理率定义,走 GDD 修订非调参 | + +**举证**:计算方法见 F1——分子分母口径、雾格入分子的通道、次序无关性、metric_version 都在其口径注记中钉死;两条及格线同时达标的判定见 F5;推理域封闭枚举见「枚举·solver_rules」表。 + +#### 规则 3:图不达标就在图内重试,重试到顶出最好的那张并报警 +**人话**:这条管生成器的自检与兜底。布完雾测出的免赌占比没到及格线时,生成器就在这一张图的随机数配额内继续重试——重试不开新图、不重新取号,回放时仍是同一种子的确定结果。重试有上限(难度表逐行的 max_gen_attempts);到顶仍不达标,就出已试图中得分最高的那张,并发强告警——玩家永不卡死,参数问题靠告警暴露。降级率按关卡×深度埋点;调参期批量统计断言守红线:低于及格线的图占比必须严格为零。 + +**举证**:重试语义的精确形式——达标即出/接受最优/接受最不坏三分支见 F5;批量红线的阈值与样本量见 F4 与 Tuning Knobs;降级率埋点与批量断言对应 A4 ⑭ 义务;"接受最优出图 + 强告警"为用户定稿。 + +#### 规则 4:出生区保底无雷,走得到的地方不藏变异 +**人话**:这条管玩家的落点安全。开局站立的 3×3 出生区保证无雷;从出生区顺着 0 格连锁能走到的地方再外扩一圈,构成生成期的"可达域"——生成器按固定定义确定性地算出它,不依赖挖掘系统运行时的行为。可达域内永远不放变异;挖掘系统实际能展开的范围只会比它小、不会比它大(包含关系由 A2 侧回验)。 + +**举证**:无计算(几何定义)。可达域的精确构造(R₀ = 出生 3×3 ∪ 0 格 flood 闭包,外扩 1 环为保守上界)见 F1 推导;环内可有雷、只禁变异的验收口径见 AC-6(a);可达域永无变异对应 A4 Rule 7 的执行;双包含回验(A2 实际展开域 ⊆ A1 排除域)登记 A2 GDD,见依赖节下游表。 + +#### 规则 5:雾和变异按固定顺序摆放,摆不下就安静地少摆 +**人话**:这条管雾和变异怎么落到图上。生成器按固定顺序布置:先查这个关卡的变异池有没有候选,再把雾格放到允许的位置,最后守住雾形硬边界。任何一步摆不下去就静默降级——跳过变异、照常交出一张合法的无变异图,实际摆成了几个如实上报。 + +| 布置步骤 | 玩家可感的效果 | +|---|---| +| 候选池门控 | 关卡未解锁雾或主题不匹配 → 这张图本来就该无雾,空池合法非错误 | +| 雾格位置 | 雾格只落"8 邻含雷的非 0 安全格";出生区与可达域排除在外 | +| 雾形硬边界 | 任何 3×3 窗口不许全雾(雾皮厚度 ≤2);雾总量 ≤ 雷数 × 8 | +| 区域参数 | 摆几个雾区、每区目标多大,读难度参数行(region_count / target_ratio) | +| 多雾区独立 | 两个雾区相邻也各是独立实例,布置身份与随机取号互不牵连 | +| 无解降级 | 布置无解 → 跳过变异、产物合法、实际达成数按 shortfall 分级上报 | + +**举证**:无总公式。雾占比参数域谓词见 F2、产物侧结构不变式见 F5 合法性闸;静默降级契约对应 A4 EC2(连通性判据见边界情况 EC2 行);双实例取号独立对应 A4 EC11,多变异选取取数在 board_gen 流内(规则 6);候选池谓词消费 A4 的 variant_pool_gate。 + +#### 规则 6:一张图的随机数全部取自同一条流水线 +**人话**:这条管随机数纪律。生成一张图期间的每一次随机取数——布雷、雾形、选变异、重试——都从同一条名为 board_gen 的随机流里按顺序取,一张图只开一次流、用到底;流之外零随机,全局洗牌函数(randomize())直接禁用。这是"同种子同参数必产同图"的根基:回放、测试、复盘都能精确复现。 + +**举证**:无计算(纪律规则)。流机制定义见 D2 Rule 5;行为级验收 = 同种子重放逐格相同(AC-4)、一次流贯通重试(AC-5)、多变异取号独立(AC-14);randomize() 禁用走静态扫描(AC-18)。 + +#### 规则 7:雾把清图变贵了多少,生成时就算好账 +**人话**:这条管"雾的成本"记账。有些雾格没法顺着 0 格连锁免费走到,进这些雾只能一格一格花行动力硬挖——生成器数出这些"链不可达雾格",格数乘以每格挖价(挖价是挖掘系统的牌价、此处只读),得出"雾致清图预算增量",连同占比一起输出,结算侧对账用的就是这两个数。本条只在生成时算账、调参期批量核对;运行时不强制,账对不上改的是难度参数行。 + +**举证**:算式 Δbudget_fog = 链不可达雾格数 × dig_ap_cost、占比 = 该格数 ÷ 付费格数(分母下限 1)——精确口径见 F3;对账消费与聚合义务对应 B1 条款 5、A4 ⑤(跨系统对账登记 B1-CP);dig_ap_cost 牌价归 A3、只读引用。 + +#### 规则 8:安全格太少的参数组合,加载时直接判死 +**人话**:这条管退化图的门槛。每行难度参数带一个"最少安全格数"下限(min_safe_count):按参数推算全图安全格总数低于下限的组合——典型如小图配高雷密——在加载数据时直接致命拒绝、进不了游戏,对冲"图上没几格安全、随便挖挖就清图领奖励"的刷奖励漏洞。生成完成后另有一道产物断言:实际图的安全格数不得低于下限。 + +**举证**:加载期判定式 W×H − round(mine_density × (W×H−9)) ≥ min_safe_count 见 F2b(sound 保守论证同节);产物侧不变式断言见 F5 合法性闸与 AC-6(f);对冲动机对应 A3 EC5(退化图刷清图奖励)。 + +#### 规则 9:每张图带地形主题标签,主题跟关卡走、不逐图重掷 +**人话**:这条管图的"主题"数据。难度参数表每行携带一个非空的 biome 标签集合(如"地牢"),生成时随参数行一起读入;同一关卡同一深度的所有图,主题静态恒定——不存在逐图重掷主题的接口,玩家不会在同一段路里忽而地牢忽而冰窟,这是防钓鱼的结构保证。标签集合为空属致命校验错误,加载数据时直接报错。 + +**举证**:无计算(数据字段规则)。非空校验 = 加载期致命,对应 A4 ⑮ 义务;主题恒定的防钓鱼裁决对应 A3 OQ6;词表 owner 未定稿前只校验非空、不校验值域(边界情况 EC13)。 + +#### 规则 10:交出的图不需要任何系统开后门,数值全部读配置表 +**人话**:这条管产物成色与数据纪律,两件事。其一,生成器交出的图不需要任何既有系统为它写特判:每个非雷格的数字恒等于 8 邻雷数、清图判定把雾格照常计入,挖掘、结算、呈现拿到的都是自洽的普通棋盘。其二,A1 自身零硬编码:全部难度数值读配置库,代码里不写死玩法数字。 + +| 数值承载 | 说明 | +|---|---| +| BoardGenTable | 全项目第一张按进度键回填的配置表 | +| 键结构 | (level_index, node_depth),每关 depth-0 锚行前置 | +| 零字面量 | A1 代码零硬编码,一切读取经 ConfigDB | + +**举证**:无计算(架构纪律)。数字计数恒真、清图判定含雾格对应 A4 Rule 4 的产物侧兑现(AC-6(g));进度键结构与锚行对应 D2 Rule 4/6;零字面量与表加载纪律走静态扫描(AC-18/19)。 + +## 枚举 + +### 生成期指标集(八项,封闭枚举) + +| 值 | 字段 | 产出点 | 语义 | +|---|---|---|---| +| 1 | 实际 inferability(含 metric_version,当前 1) | ⑤ 产出与上报 | 实际可推理率(F1 度量);metric_version 防推理器实现漂移静默改定义(联动 F4 基线) | +| 2 | inferability_core | ⑤ 产出与上报 | 剔除雾格后的可推理率 = \|R∞∖F\| ÷ max(1, \|S∖F\|)——防雾元知识单独撑满门槛;两项判据第二腿 | +| 3 | 实际雾占比 | ⑤ 产出与上报 | 定式 = \|F\| ÷ (W×H)——与 mine_density 同分母族、f≤8d 口径自洽 | +| 4 | 实际雷数与实际 \|S\| | ⑤ 产出与上报 | 先布后删漂移观测量(\|M\| ≤ N 断言载体) | +| 5 | fog_burden_ratio | ⑤ 产出与上报 | 链不可达雾格付费格分母占比(B1 条款 5 对账精确口径) | +| 6 | Δbudget_fog | ⑤ 产出与上报 | 雾致清图预算增量(\|U\| × dig_ap_cost) | +| 7 | attempts_used | ④⑤ | 生成-验证循环实际尝试次数 | +| 8 | 降级标记 | ④⑤ | 含非法产物标记与 shortfall(实际达成区数) | + +### solver_rules(推理规则域,封闭枚举) + +| 值 | 语义 | 状态 | +|---|---|---| +| single | 仅单点规则 | MVP 未启用 | +| single_subset | 单点规则 + 子集消减 | MVP 冻结值——定义域非调参(AC-1 唯一值断言) | + +- 新增信息源 / 改规则域 = 改可推理率定义 = GDD 修订,非调参。 + +## 数值结论与推导 + +> **口径总纲**:F1 度量风险轴(免赌开据占比 + 剔除雾格的第二判据)· F3 度量成本轴(AP 负担)· F2/F2b/F2c 守参数域(加载期可判定——F2c 成本上限为确定性死线,禁墙钟逃生)· F5 定验收算子(循环三分支 + 两项判据达标)· F4 守批量红线(调参期)——三轴分离,禁互相折算:雾板可推理率高而预算负担仍在,恰为两轴必须分开的理由。推理器与布置算法的实现选型不在本节(ADR 期事务),本节只定行为契约。 + +### F1 可推理率度量(inferability_measure) + +**结论**:inferability = 免赌确定揭示格数 ÷ 总安全格数——初始展开 ∪ 连锁揭示 ∪ 推导开据三通道全部计入分子,初始展开格同时计入分母;两算子对 R 单调 ⇒ 最小不动点唯一、与开据次序无关(度量不变式);inferability_core 剔除雾格防雾元知识单独撑线;两值域均 [0,1](无雾板恒等),每图输出携带 metric_version(当前 1)。 + +#### 推导 + +``` +inferability(B) = |R∞(B)| ÷ |S(B)| +inferability_core(B) = |R∞(B) ∖ F(B)| ÷ max(1, |S(B) ∖ F(B)|) ——剔除雾格后的可推理率(防雾元知识单独撑满门槛) + +R₀ = 出生 3×3 ∪ (出生 3×3 经 0 格的 flood 闭包) ——生成期确定计算,剔除"外扩 1 格"环 +R∞ = R₀ 在下列两条闭包算子下的最小不动点: + ① 连锁:x ∈ R 且 x 为 0 格 ⇒ x 的 8 邻安全格全部入 R + (真实级联——雾格可被链开但不传播,A4 Rule 4) + ② 推导开据:c ∈ S∖R 且 c 与 R 8 邻接,且求解器在可见信息态上 + 能以合法信息源唯一判定 c 非雷 ⇒ c 入 R +合法信息源(穷尽枚举;新增信息源 = 改可推理率定义 = GDD 修订,非调参): + (a)数字约束——可见数字格上以冻结规则域 solver_rules(single_subset)推理 + (b)雾元知识——c 属于可见雾区 ⇒ c 非雷("雾区无雷"为玩家可见知识, + A4 Rule 3 / D5 教学义务——建模知情玩家,非作弊读取雷位) +``` + +| # | 口径钉死注记(Core Rule 2 的精确化) | +|---|---| +| ① | "确定性推导格数"钉死为免赌确定揭示格数——字面窄读(仅数字推导)会使任意好图远低于常规门槛,非立法本意 | +| ② | 初始展开格计入分子与分母(S 含初始格、R₀ ⊆ R∞)——与 P1 承诺语句"从初始安全区出发,逻辑可推导的挖掘占比不低于门槛"对齐 | +| ③ | 雾格入分子的两通道:链可达雾格经算子①入分子;与 R∞ 邻接的链不可达雾格经信息源(b)入分子;恒无 R 邻格的雾格计入终局残余——与挖掘相邻性(A3 限 8 向相邻)口径对齐,公式本身不变。雾格挖掘是零雷险行为(玩家可见知识)故计入"免赌"口径;其 AP 全价代价不在此度量、由 F3 单独承载(两轴分离);"免赌 ≠ 可推理"的度量空心风险由第二达标判据(inferability_core)与 F2 雾占比绝对上限 0.25 合围 | +| ④ | 求解器初始可见态取 0 格 flood 闭包内芯而非保守可达域全体("外扩 1 格"环非自动展开的必然产物,计入会高估玩家信息);须与 A2 立对偶回验:既有"A2 实际展开域 ⊆ A1 排除域"之外,新增"A1 求解器初始态 ⊆ A2 实际初始展开域"(度量保真回验,登记 A2 GDD;若 A2 定稿展开更小,回退口径 = 出生 3×3 最小态) | +| ⑤ | 次序无关性(度量不变式):两算子对 R 单调 ⇒ 最小不动点唯一、与开据次序无关——实现可任意定序而度量不变(行为契约,兼防一类实现方差 bug;最小不动点可经单调 worklist 传播实现,不强制全板重扫描) | +| ⑥ | 每图指标输出携带 metric_version(当前 1)——防推理器实现漂移静默改定义(联动 F4 基线);未来改 R∞/信息源域则 version=2 + 向量重制,仅增判据与指标集不动 R∞ 定义时 version 不跳变 | + +**附带合法性不变式(clearable)**: + +``` +clearable(B) :⇔ ∀c ∈ S:c 与出生 3×3 在"安全格 8 邻接图"上连通 +``` + +- 理由:inferability 门槛不蕴含可清图——终局对赌残余(拉低可推理率)与矿壳包围的不可达安全格(使 cleared 永不可达)是正交缺陷(1 个不可达格在 126 安全格的板上仅拉低 0.008、0.95 门槛照过,而该板功能性残废);故清图可达性是合法性谓词(违规 = 该次尝试判废重试,非"接受最优"候选),与质量度量分属两闸(验收算子见 F5)。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| B | 棋盘实例 | —— | 一次尝试的产物(含雷布、雾布、出生区) | +| S(B) | 格集 | ⊆ 全格 | 安全格全集(分母;含雾格、含初始格、含不可达格) | +| F(B) | 格集 | ⊆ S | 雾格集(雾区无雷,由布置约束保证) | +| R₀ / R∞ | 格集 | R₀ ⊆ R∞ ⊆ S | 求解器初始可见态 / 免赌揭示闭包 | +| solver_rules | enum | {single_subset} 冻结 | 推理规则域(定义域非调参;信息源(a)的边界) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Example | 12×12 图(144 格),mine_density=0.15 → N = round(0.15×135) = 20(设环内移除 0,\|M\|=20)、S=124;雾 2 区共 9 格。R₀=21,全图 0 级联闭包 58(含 5 个链可达雾格);数字推导开据 51 + 雾元知识开据 4(链不可达雾格)+ 推导触发级联 8 = 63 → R∞=121;残余 3 格 = 一处终局非确定对 + 1 孤立残余格。inferability = 121/124 ≈ 0.976 ≥ target 0.95;inferability_core = (121−9)/(124−9) = 112/115 ≈ 0.974 ≥ 0.95 → 两项判据达标,attempts_used=1 | +| Output Range | inferability ∈ [0, 1]、inferability_core ∈ [0, 1](无雾板两值恒等);合法产物下下界 ≥ 9/\|S\| | +| 边界 | target = 1 要求全图零非确定残余(小图高密下结构性难达——合法参数,由 F4 红线暴露、不加载期拒绝);target < 0.85 加载期拒绝;不可达安全格若存在则先触发 clearable 违例(合法性闸),不会以"拉低质量分"方式静默出图 | + +### F2 雾占比参数域谓词(fog_domain_ok)+ F2b min_safe 可行性谓词 + F2c 成本可行性谓词 + +**结论**:三谓词加载期可判定、违规即致命——雾占比域 (count=0 ⇒ f=0) ∨ (count≥1 ∧ 0 < f ≤ min(8d, 0.25) ∧ f<1);min_safe 可行性 W×H − round(d×(W×H−9)) ≥ min_safe_count;布雷数下限 round(d×(W×H−9)) ≥ 1;成本可行性 attempts×W×H ≤ 16384。 + +#### 推导 + +``` +fog_domain_ok(r) :⇔ (r.fog_region_count = 0 ∧ r.fog_target_ratio = 0) + ∨ (r.fog_region_count ≥ 1 ∧ 0 < r.fog_target_ratio ≤ min(8 × r.mine_density, 0.25) + ∧ r.fog_target_ratio < 1) (0.25 = 雾占比绝对上限) + +min_safe_feasible(r) :⇔ r.board_width × r.board_height + − round(r.mine_density × (r.board_width × r.board_height − 9)) + ≥ r.min_safe_count (9 = 出生 3×3 恒排除的下界;sound 保守) + +布雷数下限:round(r.mine_density × (r.board_width × r.board_height − 9)) ≥ 1 + (零雷板拒绝——初始展开即 cleared 的 A3 EC5 钓鱼活体、标中旗经济恒空、F4 全断言隐形穿越) + +cost_feasible(r) :⇔ r.max_gen_attempts × r.board_width × r.board_height ≤ 16384 + (F2c——C = 16384 为 provisional 量级护栏,正式值归 OQ2 ADR; + 运行期无墙钟时间逃生【文本级契约:耗时依赖输出即破同种子同图】, + 确定性死线必须加载期可判定) +``` + +- **与 Schema 校验的关系**:F2 是 Tuning Knobs 节跨字段谓词的规范化合取形,等价——Schema 校验行采本规范形消除分歧,(count=0, f>0) 角点已入 AC-2 违例集。 +- **必要上界推导(边计数)**:每雾格 ≥1 条到非雾雷的邻接边 ⇒ 邻接边数 ≥ \|F\|;每雷 ≤ 8 邻 ⇒ \|F\| ≤ 8\|M\| ⇒ f ≤ 8d。必要 ≠ 充分:连通性缺口(雷膨胀皮肤碎裂,A4 EC2)不由本式判定,由静默降级 + F4 兜底。 +- **F2b sound 性论证**:N = round(d×(W×H−9)) 先定数、先布后删只减不增 ⇒ \|M\| ≤ N ⇒ \|S\| = W×H − \|M\| ≥ W×H − round(d×(W×H−9))——round 取整钉死后恒成立(其他取整为非法实现);计数可行 ≠ 连通可行——clearable 生成期闸不可互替。 +- **F2b 为 Schema 校验行 delta**:min_safe_count 原仅产物侧不变式(Rule 8),参数侧联合不可行域(小图×高密×高下限)现于加载期拦截。 + +**与 inferability_target 的联合可行域——裁决:不加第三条跨字段校验。** + +| # | 裁决要点 | +|---|---| +| 1 | 无可判定静态界——inferability 由 0 格拓扑、雾形、出生位置主导,聚合参数不决定它,任何 sound 加载期联合谓词要么空洞要么误杀 | +| 2 | 架构既定——暴露机制就是"运行期接受最优不死锁 + 强告警埋点 + 调参期批量断言",第三闸重复且更弱 | +| 3 | 补偿保证(与 F4 绑定生效)——F4 红线 + 降级率埋点使联合不可行域在首次批量生成时必然现形 | +| 4 | 裁决边界——拒绝的是 inferability 可行性静态联合谓词(无可判定静态界);雾占比绝对上限 0.25(度量语义护栏)与 F2c 成本谓词(确定性成本死线)均有可判定形式且不声称预测 inferability,不在该裁决覆盖内 | +| 5 | 回写义务(advisory 非校验)——target、fog_target_ratio、mine_density 同向调高时降级率结构性上行,属参数域边界而非实现缺陷 | + +**验算与边界:** + +| 输入 | 判定 | 依据 | +|---|---|---| +| (d=0.15, f*=0.12, count=2) | ✓ 放行 | 域内 | +| (d=0.06, f*=0.50) | ✗ 致命 | 0.50 > 8d = 0.48 | +| (count=3, f*=0) | ✗ 致命 | 一致性腿(count ≥ 1 ⇒ f > 0) | +| (count=0, f*=0) | ✓ | level 1 无雾行标准形 | +| (count=0, f*=0.05) | ✗ 致命 | count=0 ⇒ f=0 腿(角点) | +| (d=0.15, f*=0.85, count=32) | ✗ 致命 | 0.85 > 0.25 雾占比绝对上限——极雾行加载期判死 | +| F2b:10×10、d=0.10、min_safe=50 | ✓ | 100 − round(0.10×91) = 91 ≥ 50 | +| F2c:10×10×16 = 1600;12×12×16 = 2304 | ✓ | ≤ 16384 | +| F2c:64×64×32 = 131072 | ✗ 致命 | 该组合换图相位同步卡顿秒级至百秒级 | +| Output Range | bool,加载期判定;0.25 = 雾占比绝对上限(MVP 量级护栏非推导界,堵"极雾行以雾元知识撑满可推理率"的度量空心);F2b sound 保守、计数可行 ≠ 连通可行;F2c 禁墙钟时间逃生(耗时依赖输出即破同种子同图),正式值归 OQ2 ADR | —— | +| 极端行为 | d=0.5 时 8d=4,f<1 成为绑定约束;d→0 时几乎任何正 f 均被拒(小雾量在稀雷图结构性不可布,属正确拒绝);f > 0.25 恒拒(与 d 无关);attempts×W×H > 16384 恒拒 | —— | + +### F3 清图预算增量构成(fog_budget_increment) + +**结论**:U = F ∖ Chain₀(链不可达雾格全价段);Δbudget_fog = \|U\| × dig_ap_cost;fog_burden_ratio = \|U\| ÷ max(1, \|S ∖ Chain₀\|)(付费格分母——B1 条款 5 对账精确口径);无运行期强制——A1 只拥有量的计算与输出,不拥有"够不够"的判(归 A1/A3 契约与 C3)。 + +#### 推导 + +``` +U(B) = F(B) ∖ Chain₀(B) Chain₀(B) = R₀ 经算子①(0 格级联)单独闭包的可达集 +Δbudget_fog(B) = |U(B)| × dig_ap_cost (量纲闭合:格 × AP/格) +fog_burden_ratio(B) = |U(B)| ÷ max(1, |S(B) ∖ Chain₀(B)|) +``` + +| # | 口径钉死注记 | +|---|---| +| ① | A4 排除声明与 A1 Rule 7 所称"链不可达雾格占比"的 B1 条款 5 对账精确口径 = fog_burden_ratio(付费格分母——雾在最小清图 AP 负担中的权重);雾内部分母版本 \|U\|/\|F\| 不单列输出——可由指标集(实际雾占比 + Chain₀/U 重算)按需还原,不作对账输入 | +| ② | 分母 = \|S ∖ Chain₀\| = 最小清图付费挖掘格数(初始展开与连锁免费、其余每格 dig_ap_cost——A3 价目只读引用),分母下限 1 防全连锁退化板除零 | +| ③ | 链可达性取决于未开 0 格隐藏拓扑,玩家决策时点不可求值——本式是生成期事后度量(A4"玩家侧按全价做预算"的保守启发式不与本式冲突,两口径服务不同主体) | +| ④ | 聚合义务(A4 ⑤ 公式化):逐板计算入指标集;调参期按行批量聚合出核算报告供 C3 修正行参数;无运行期强制 | + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| U(B) | 格集 | ⊆ F | 链不可达雾格集(全价段) | +| Chain₀(B) | 格集 | R₀ ⊆ Chain₀ ⊆ S | 纯 0 级联可达集(免费段,含初始展开) | +| dig_ap_cost | int [1,8] | 当前 1 | A3 价目,只读引用(knob 归 A3) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Example | F1 例板:F=9、链可达雾 5 → U=4;dig_ap_cost=1 → Δbudget_fog = 4 AP;付费格 = 124 − 58 = 66 → fog_burden_ratio ≈ 0.061 | +| Output Range | Δbudget_fog ∈ [0, \|F\|×dig_ap_cost]、fog_burden_ratio ∈ [0,1](量纲闭合:格 × AP/格) | +| 边界 | fog_region_count=0 → 两值恒 0;U=F(雾区全部链不可达)→ 雾板退化为纯 AP 税——A4 反向护栏与 B4 定价约束的观测对象;U=∅(雾全链免开)→ 雾零成本——B4"糖果区"病理在此指标上直接现形 | + +### F4 批量统计断言(batch_statistical_assertions,调参期离线) + +**结论**:批量域 = BoardGenTable 全部行键,每行 N=256 样本(σ 基址打包使行区间两两不交);断言 A 两项判据严格零、A″ target 绝对下限 0.85、A′ 非法产物零容忍、B 达成区占比 ≥ 0.95(仅 count≥1 行)、B′ 无雾行零含雾板、E 难度方向 advisory——严格零阈值与 0.85/0.25/16384 同属结构冻结(调整走 GDD 修订非调参)。 + +#### 推导 + +``` +批量域 = BoardGenTable 全部行键 (L, D)(R1 floor 解析保证任意请求点归行) +批(L,D) = { generate(L, D, derive_at("board_gen", σ(L,D) + i)) : i ∈ [0, N) } + (derive_at 纯函数、不推运行期计数器;σ 基址清单入版本库,批量跨迭代可复现) + +断言 A(可推理率红线,用户定稿):∀行:|{ b : inferability(b) < target行 ∨ inferability_core(b) < target行 }| = 0 + ——严格零(两项判据批量对照) +断言 A″(target 绝对下限):∀行:target行 ≥ 0.85(provisional 锚——加载期域 [0.85,1] 之外的调参期兜底) +断言 A′(非法产物零容忍,F5 配套):∀行:|{ b : 非法产物标记 }| = 0 +断言 B(雾出现下限 + 降级率上限,仅 count ≥ 1 行):实际达成区数 = fog_region_count 的板占比 ≥ 0.95 + (一并承载 A4⑭"出现率下限/降级率上限"两件套) +断言 E(难度方向 advisory):对 schema 标记 monotone_in_depth 的字段,同 level 沿 depth 弱递增 + ——违例 warning 级开火(不拦合法呼吸位设计,调参期人工复核) +断言 B′(反向断言,仅 count = 0 行):含雾板数 = 0(门控泄漏 = bug 信号,转 bug 流程非调参) +报告 C:按行 p10/p50/p90(实际 inferability)、p50/p90(Δbudget_fog 与 fog_burden_ratio) + ——p10 供 B1 条款 3 对账(消费 actuals 分布而非 target 参数) +诊断 D(非断言):重试率(attempts_used ≥ 2 占比)、attempts_used 均值、EC2 shortfall 分级分布 + +MVP 默认:N = 256/行;σ(L,D) = 10^9 + 2^20·L + 2^12·D +(位打包使行区间 [σ, σ+255] 两两不交、远离运行期取号域不变——朴素 1000·L+D 打包在 D≥1000 时 + 精确碰撞〔(1,1001) ≡ (2,1)〕且同 L 相邻深度行批量区间 255/256 种子重叠; + 登记种子按失效协议全部重搜——夹具 σ 为登记时值) +``` + +| # | 阈值裁定理由 | +|---|---| +| ① | 红线严格零(用户定稿):A1 反向护栏已定"跌破门槛 = 阻塞缺陷"——任何一例失败即触发参数修正会话,迫使决策显式化;运行期"接受最优/最不坏"是安全网,批量红线是调参期纪律 | +| ② | N=256:95% 下限的稳定通过要求真实出现率 ≥ 0.98(3σ 余量 ≈ 99.97%) | +| ③ | 雾出现下限 0.95 而非 1.0:EC2 连通性降级是合法运气事件,5% 容差容纳自然方差(收紧为达成区占比后,同时容纳完全降级与部分达成两类合法运气事件);系统性病理(level 2 小图低密关雾成片失败)呈现 ~0% 出现率,与 0.95 相距悬殊,检出无歧义 | +| ④ | 断言 B′ 零成本反向杠杆:门控 bug(如 level 1 出雾)一例即现形 | +| ⑤ | 成本:行数 × 256 板 × ≤32 attempts 的生成+求解模拟,离线分钟级(成立域 = worklist 级求解实现 ∧ 大面积行稀少;naive 全板扫描实现 + 数行 48×64 板即小时级——分片续跑义务见 OQ2) | + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| N | int | 256(MVP 默认) | 每行批量样本量 | +| σ(L,D) | int | ≥0 | 批量种子基址(版本库清单) | +| target行 | float [0,1] | 行读值 | 该行 inferability_target(期望由表值推导,不写死) | + +**验算与边界:** + +| 行 | 断言结果 | +|---|---| +| 行 (2,0)(fog unlock 关)N=256 | 两项判据达标 256/256 → A 通过;达成区数 = count 的板 251/256 = 0.980 ≥ 0.95 → B 通过;重试率 3.1% → 诊断记录 | +| 行 (3,2) | 达成区数 = count 的板 220/256 = 0.859 < 0.95 → B 失败 → 修正行参数(区域数/尺寸/密度侧保证,禁放宽 A4 Rule 3) | +| 极端行为 | target 1.0 的行在断言 A 下大概率开火(合法、预期信号);断言 B′ 失败即实现缺陷而非参数问题 | + +### F5 生成-验证验收算子(attempt_acceptance) + +**结论**:合法性闸前置——legality = clearable ∧ \|S\| ≥ min_safe_count ∧ 结构不变式全过,不可清图产物不进"接受最优"候选(质量分不得掩盖功能残废);达标即出(k*,两项判据同时满足,非全局 argmax)→ 无达标有合法则接受最优(k**,tie 取最早)+ 强告警埋点 → 零合法则接受最不坏(k***,确定性劣度字典序)+ 最高级告警 + 非法产物标记——玩家永不卡死,批量红线由 F4 断言 A′ 守。 + +#### 推导 + +``` +合法尝试集 Lm = { B_k : legality(B_k), 1 ≤ k ≤ max_gen_attempts } +legality(B) :⇔ clearable(B) ∧ ( |S(B)| ≥ min_safe_count ) + ∧ 结构不变式全过(雾约束 cell_constraint / 出生区及可达域排除 / + 厚度≤2【任意 3×3 窗口非全雾】/ f≤8d / 数字计数恒真 / 初始展开不触雾) + +B_out = B_{k*},k* = min{ k : B_k ∈ Lm ∧ inferability(B_k) ≥ target ∧ inferability_core(B_k) ≥ target } + (达标须两项判据同时满足——剔除雾格判据防止"雾区无雷"知识单独撑线; + 达标即出、非全局 argmax——重试语义的自然形式化) +若 k* 不存在且 Lm ≠ ∅:B_out = B_{k**},k** = min{ k : inferability(B_k) = max_{j∈Lm} inferability(B_j) } + + 强告警埋点 + 降级标记(tie 取最早,零额外取数) +若 Lm = ∅(attempts 耗尽零合法):接受最不坏尝试出图(用户定稿)——确定性劣度字典序 + k*** = min{ k : (不可达安全格数, max(0, min_safe_count − |S(B_k)|), k) 字典序最小 } + + 最高级告警 + 非法产物标记(玩家永不卡死;批量红线由 F4 断言 A′ 守) +``` + +| # | 注记 | +|---|---| +| ① | 全部尝试共用同一 board_gen 流实例(Rule 3/6),k*、k**、k*** 与 B_out 同为流取数的确定函数——回放语义闭合;scope 事件边界操作定义 = 生成函数完整返回(重试不产生新取号)——D3 快照切点天然落在边界外(D2"消费侧遵守"条款的 A1 具体化);生成期表值单次读取(行参数在生成开始时快照,生成期内不重读——防热重载撕裂 attempt 语义,A4 EC8 同族) | +| ② | 合法性闸(clearable/min_safe)在质量评分之前:不可清图产物不进入"接受最优"候选——否则质量分会掩盖功能残废 | +| ③ | "接受最不坏"分支的确定性劣度 = (不可达安全格数, min_safe 缺口, 序号) 字典序——零额外取数、可重放 | +| ④ | B1 编排不得把 attempt 边界当 scope 事件(登记 B1 接线注记) | + +### 显式排除声明(归属表) + +| 排除对象 | 归属 | A1 边界 | +|---|---|---| +| 每行难度数值与曲线(尺寸/密度/门槛/雾参数取值及其随 (level_index, node_depth) 的曲线,含 max_gen_attempts 取值策略) | C3 调参(经 D2 R1 解析/编排器传递,禁反向调用生成) | MVP 参考行仅为 schema 自洽性证明,非平衡事实、非回填默认值 | +| 挖掘风险不等式(A3 F1 条款 3) | B1 单一闭合对账 owner | A1 义务止于供给锚值 = actuals 分布(F4 报告 C 的 p10)而非 target 参数 | +| 弃图支配性(条款 5) | B1 闭合 | A1 供给锚值 = 实际 inferability 分布 + fog_burden_ratio(生产者义务);两态验算与两条方向性护栏归 B1/B4 | +| 清图总预算可用量与全量校验判据 | A1/A3 契约(A4 排除声明既定) | A1 只算量(F3),不拥有"够不够"的判 | +| 推理器实现(single_subset 具体规则集、数据结构、传播算法)与布置算法 | ADR 期事务 | GDD 只冻结行为契约(合法信息源穷举、禁作弊读取、次序无关性、规则域枚举冻结、metric_version) | +| 战斗侧 / 奖励定价 / biome 词表 | B1/B2/B3/B4/C1/D1 域 | A1 的 board_biome 仅是非空 StringSet 数据字段 | + +**措辞登记(registry 级,防口径漂移)**:inferability 是盘面级免赌占比(生成侧);P(安全) 是单格认知概率(决策侧、经济口径)——免赌集内 P(安全) 恒 = 1(含雾格),风险讨论收缩到残余集(其安全格占比 ≤ 1 − inferability)。inferability 是 P(安全) 的生成端结构性上界代理之占比形式,不是 P(安全) 本身。 + +### MVP 参考行(provisional——仅 schema 自洽性证明,C3 持调参所有权) + +| 字段 | L1·D0 参考行 | L2·D0 形状校验行 | +|---|---|---| +| board_width / board_height | 10 / 10 | 12 / 12 | +| mine_density | 0.10 | 0.12 | +| inferability_target | 0.95 | 0.92 | +| solver_rules | single_subset(冻结) | 同左 | +| min_safe_count | 50 | 60 | +| max_gen_attempts | 16 | 16 | +| fog_region_count / fog_target_ratio | 0 / 0(level 1 候选池空标准形——A4 F1) | 1 / 0.10(≤8×0.12 ✓——F4 断言 B 首个真实考验行) | +| board_biome | {"dungeon"}(词表 owner 未定稿,仅占位) | 同左 | + +- 自洽性验证:F2/F2b/F2c 全过(10×10×16 = 1600、12×12×16 = 2304 ≤ 16384);期望 \|S\| ≈ 91、R₀ ≈ 12–20 → inferability 期望 ≥ 0.95(L1 无雾 ⇒ core ≡ inferability;L2 雾 ~14/144 格、core 判据轻度收窄仍期望 ≥ 0.92——两项判据同时达标;attempts_used ≈ 1,F4 全绿理论位形)。 + +## 状态 + +生成管线为无状态函数(每次调用独立、可重放)——五阶段失败语义: + +| 阶段 | 失败条件 | 语义 | +|---|---|---| +| 参数解析 | 行缺失(R1 保证有行)/ 参数域违例 | 加载期校验已拦(D2);运行期不失败 | +| 布雷 | N = 0(round(d×(W×H−9)) = 0——零雷板加载期拒绝:A3 EC5 钓鱼活体 + F4 全断言隐形穿越) | 运行期不失败(域内 N ≥ 1 恒可布;先布后删见 Core Rule 1②) | +| 变异布置 | cell_constraint 无解(连通分量不足——A4 EC2) | 静默降级:跳过变异、产物合法、shortfall 上报(⑦⑬) | +| 验证循环 | 三分支(F5 验收算子) | 达标尝试存在 → 达标即出(k*,两项判据);attempts 用尽无达标但有合法 → 接受最优 + 强告警埋点;attempts 用尽且零合法 → 接受最不坏(确定性劣度字典序)+ 最高级告警 + 非法产物标记——玩家永不卡死,红线由 F4 断言 A′ 批量守 | +| 产出 | —— | 棋盘 + 八项指标集(见枚举节) | + +不变式:管线无状态、五阶段顺序冻结、生成期表值单次读取、attempt 边界 = scope 事件边界(重试不产生新取号)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 候选池为空(雾未解锁如 level 1,或 biome_tag 全不匹配) | 静默布置无变异图——空池合法非错误(参考行 L1 即此形态) | A4 EC1/F1 的 A1 执行语义 | +| EC2 cell_constraint 无解(非 0 安全格面积或连通分量尺寸不足) | 本次布置尝试失败 → 跳过变异、产物为合法无变异图;BoardGenTable 要求 N 区而实际达成 k A1 无自有渲染面与 UI——视觉义务是数据供给规格,呈现归 D1/A2/B5;无音频需求。 + +| # | 数据面义务 | 归属 | +|---|---|---| +| 1 | biome/tag = 换皮数据锚:BoardGenTable.board_biome 与产物携带的 biome 标签是新 biome 色板变体的唯一数据源——新 biome 不新增资产只换色板,前提是 A1 产物正确携带标签 | D1/art-bible | +| 2 | 变异格标记保真:产物的雾格标记是 D1"边界可见"呈现(A4 D1 行规格)与"已开雾格 = 地板无字"的数据前提——标记错误即呈现欺骗(P1 诚实边界的生成侧根基) | D1 | +| 3 | 雾区连通域信息:EC6 双雾区呈现合并归 D1 渲染域,A1 只保证实例身份与取号独立(AC-14),不保证视觉分离——D1 侧规格不受 A1 约束 | D1 | +| 4 | 数字可读性的生成侧前提:数字 = 8 邻雷数恒真(AC-6(g) 断言)——数字可信是"数字可读性"样张测试(game-concept 风险 #1)的逻辑前提 | D1 | +| 5 | 无 HUD 常显字段、无结算面——玩家对 A1 的全部感知都经"棋盘本体";生成期指标不面向玩家(遥测/审计域) | A2/D1 | + +## Tuning Knobs + +### BoardGenTable schema(D2 承载,本 GDD 回填 owner——首个进度键表) + +| 字段 | 类型/范围 | knob | 说明 | +|---|---|---|---| +| board_width / board_height | int [8, 64] | ✓ | 图尺寸(MVP 量级护栏,非推导界) | +| mine_density | float (0, 0.5] | ✓ | 雷密度(尺寸解耦;f≤8d 分母对齐;上界为 MVP 护栏) | +| inferability_target | float [0.85, 1] | ✓ | P1 门槛——可推理率下限(承重 knob;域下限 = "纯猜局红线"绝对锚) | +| solver_rules | enum {single, single_subset} | ✗ 冻结 | 推理规则域(MVP 恒 single_subset——定义域非调参) | +| min_safe_count | int [1, 999] | ✓ | 最小安全格数下限(A3 EC5 对冲) | +| max_gen_attempts | int [1, 32] | ✓ | 生成-验证循环重试上限 | +| fog_region_count | int [0, 32] | ✓ | 雾区域数参数(0 = 本行无雾) | +| fog_target_ratio | float [0, 1) | ✓ | 雾面积占比参数(跨字段校验见下表) | +| board_biome | StringSet 非空 | ✓ | 图类型标签(F1 输入前提;域校验致命) | + +**加载期校验(违规均加载期致命,D2 Rule 6 通道)**: + +| 校验 | 判定式/内容 | 出处 | +|---|---|---| +| R1 键结构 | (level_index, node_depth) + depth-0 锚行 | D2 既有 | +| 字段域校验 | 九字段类型域全谱;inferability_target ∈ [0.85,1](P1 绝对锚) | 本表 | +| 跨字段雾参数谓词 | `(fog_region_count = 0 ⇒ fog_target_ratio = 0)` ∧ `fog_region_count ≥ 1 ⇒ (0 < fog_target_ratio ≤ 8 × mine_density ∧ fog_target_ratio < 1 ∧ fog_target_ratio ≤ 0.25)`——F2 规范合取形(Schema 校验行采本规范形);0.25 = 雾占比绝对上限,MVP 量级护栏非推导界,堵"极雾行以雾元知识撑满可推理率"的度量空心 | F2 | +| 布雷数下限 | `round(mine_density × (W×H − 9)) ≥ 1`(零雷板拒绝) | F2 | +| min_safe 可行性谓词 | `W×H − round(mine_density × (W×H − 9)) ≥ min_safe_count`(sound 保守、论证见 F2;计数可行 ≠ 连通可行——clearable 生成期闸不可互替) | F2b | +| 成本可行性谓词 | `max_gen_attempts × W × H ≤ 16384`(provisional 量级护栏,正式值归 OQ2 ADR;运行期禁墙钟时间逃生——耗时依赖输出即破同种子同图) | F2c | +| monotone_in_depth 字段标记(可选,非 knob) | 难度方向声明(size/density/target 类标记;fog_region_count 允许教学性非单调、不标)——F4 断言 E 载体,只校验方向不校验数值,数值所有权仍归 C3 | F4 断言 E | + +**第三轴落点登记**——game-concept 定稿难度 = f(关卡序号, 节点深度, 精英/Boss 修饰);下表为本表冻结键 (level_index, node_depth) 的约定: + +| # | 约定 | +|---|---| +| ① | Boss 图 = 各 level 最大深度(Dmax)行:关底 Boss 恒为该关最后节点,Dmax 行即 Boss 行(线性关零成本表达) | +| ② | 精英节点:schema 预留 elite_ / boss_ 前缀字段命名空间(与 D2 DifficultyTable Rule 4 对称;本 GDD 不定义任何修饰数值、不预写修饰枚举) | +| ③ | C1/C3 定稿强制重审(OQ6):若节点形态使精英性无法由 (level_index, node_depth) 唯一确定(分支地图同深度并存精英与普通节点),走 D2 Rule 4 既有改键通道(接口漂移迁移)——迁移退路已在冻结当下登记,非无后门的死冻结 | + +### Knob 表 + +| Knob(表行值,D2 承载) | 参考(L1/L2) | 调过高 | 调过低 | +|---|---|---|---| +| board_width/height [8,64] | 10×10 / 12×12 | 决策带宽膨胀、清图时长拉长 | 逼近 min_safe 联合域(F2b 拦截线) | +| mine_density (0,0.5] | 0.10 / 0.12 | 推理窗收窄、可推理率难达标(F4 断言 A 开火);f≤8d 分母水涨船高 | 图面空旷、奖励密度稀 | +| inferability_target [0.85,1] | 0.95 / 0.92 | 重试率升、深行联合不可行(F2 裁决:无静态闸,F4 必然现形) | 域下限 0.85 = P1 绝对锚(加载期拒绝 + F4 断言 A″ 兜底)——"纯猜局混入"红线自此有可判定载体;0.85 本身的调整走 GDD 修订非调参 | +| min_safe_count | 50 / 60 | 小图联合无解(F2b 加载期拦截) | 退化板钓鱼复现(A3 EC5 对冲失效) | +| max_gen_attempts [1,32] | 16 | 生成耗时随 attempts×面积×传播深度超线性涨(对边长最高三次方量级;F2c 成本闸拦病理组合,OQ2) | 降级率上行、质量下行 | +| fog_region_count | 0 / 1 | 信息剥夺过重(P1 落差);F4 断言 B 压力 | 低于雾出现率下限 → A4"解锁新变异"推进感哑火(A4⑭ 义务) | +| fog_target_ratio [0,1) | 0 / 0.10 | 逼近 8d 上界 → 布置降级率升(连通性,EC12 两闸注记);> 0.25 加载期拒绝(F2 雾占比绝对上限腿) | 雾存在感弱、P4 推进感哑火 | +| board_biome | {"dungeon"} | ——(词表 owner 未定稿,暂只校验非空) | —— | + +- **非 knob(结构冻结)**:solver_rules(定义域)、键结构与 depth-0 锚(第三轴登记约定见上表)、五阶段顺序、合法信息源穷举枚举、F4 断言 A/A′ 严格零阈值(改阈值走 GDD 修订)、先布雷后布雾顺序、attempt 边界 = scope 事件边界、target 域下限 0.85 / 雾占比上限 0.25 / F2c 常数 16384(provisional 量级护栏——调整走 GDD 修订非调参)。 +- **工具参数(登记默认非玩法 knob)**:N=256/行、σ 基址公式(版本库清单)。 +- **Knob 交互**:density × fog_target_ratio 同向调高 → 降级率结构性上行(F2 advisory);target × density 联合可行域无静态闸(F2 裁决,F4 兜底);尺寸 × min_safe 联合(F2b)。**跨 GDD 引用**:ap_per_round / dig_ap_cost 归 A3、unlock_level / cell_constraint 归 A4——禁止两表间复制参数(D2 Rule 1)。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | A1 是生成函数系统——冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化) | +| 被测函数面 | `generate(level_index, node_depth, rng) -> Board`(+ 生成期指标集) | +| 度量独立入口 | qa-lead 裁决、分解面声明:推理器(R∞/inferability/metric_version)、F3 度量(Chain₀/U/Δbudget_fog/fog_burden_ratio)、合法性谓词(legality/clearable)以独立纯函数入口暴露——生成函数内嵌调用与独立入口同一实现,独立入口不引入新逻辑、仅暴露 F1/F3/F5 已定义的函数;独立入口的行为断言即内嵌调用的行为(次序无关性、防作弊等白盒性质测试以此为载体);若实现期 ADR 裁决推理器不可独立调用,AC-7/8/9/12 的单元腿须降级重议——登记缺口 G1,门禁评审列为 A1 实现契约条款 | +| 只读观测量 | 产物格状态(雷布/数字/0 格/雾区标记/出生区)+ 八项指标(实际 inferability【含 metric_version】/ inferability_core / 实际雾占比【= \|F\| ÷ (W×H)】/ 实际雷数与实际 \|S\| / fog_burden_ratio / Δbudget_fog / attempts_used / 降级标记【含非法产物标记与 shortfall】) | +| 测试种子取号 | 经 `derive_at("board_gen", σ)` 纯函数(D2 R2 预览语义的测试侧合法用途,不推运行期计数器);测试脚本以该种子构造流实例属测试通道合法动作(AC-18 扫描豁免域);运行期 replay 语义(open_stream 全真路径)另测(AC-5) | +| 夹具策略 | 默认根 = MVP 参考行 L1·D0 / L2·D0(干净检出)——参考行是 GDD 定稿的 schema 自洽性证明行,期望全部由表读值推导、对 C3 调参免疫;行为变体经 `--data-root` 夹具根注入(禁拷入 data/tables/);黄金图板与病态板经 Board class_name 直引程序构造(不经生成器、不经 load()/preload()——A4 AC-5 先例);夹具规格见下表;登记种子(σ 值)依赖管线确定性:一经验证恒成立,实现或定义变更致失效 → 按失效协议重搜并登记(与黄金向量失效协议同源) | +| 黄金图回归向量协议 | 五类手工核验小板,每板 = 显式坐标规格(尺寸/雷集/雾区格集/出生 3×3,程序构造不经生成器)+ 登记常数((\|R∞\|,\|S\|) 整数对、含雾板另登记 (\|R∞∖F\|, \|S∖F\|) core 整数对——由既有坐标规格派生、向量不重制、关键格 R∞ 成员表、clearable、Chain₀/U 格集、Δbudget_fog、fog_burden_ratio 比对形 = 整数对 (\|U\|, max(1,\|S∖Chain₀\|))、metric_version=1——整数对比对,禁浮点容差)+ 逐格手工推导记录(每格声明信息源 ∈ {数字约束(a)、雾元知识(b)、0 格级联①},入版本库随向量文件——手工常数的登记依据);比对内容 = 度量独立入口 + 合法性谓词输出与登记常数逐位相等;失效处置协议 = 失配先按冻结定义复核手工推导,推导无误即实现缺陷(修实现);solver_rules/合法信息源域/metric_version 变更 = 换 solver_rules 即失效,走 GDD 修订 + 向量重制(推理域冻结的非调参性质的行为侧兑现) | +| 黄金板设计要求 | ⅰ 纯数字推理板(无雾;须含仅 single_subset 规则可开的推导格)· ⅱ 链密集板(0 格级联闭包主导)· ⅲ 含雾板(须同时含:链可达雾、链不可达雾、邻接出生 3×3 的雾区、寂静减项证据格、雾锚增项证据格【雾元知识开据格】)· ⅳ 终局对赌残余板(inferability 严格 ∈ (0,1) 的非确定残余)· ⅴ 不可达安全格板(clearable 违例形态) | +| 期望值推导纪律 | 行为类 AC 期望 = ConfigDB/表读值推导(inferability_target/min_safe_count/max_gen_attempts/fog_region_count/unlock_level/dig_ap_cost/σ 公式全读值),不写死玩法数值;唯一例外 = 黄金图向量手工常数(依据 = 手工推导记录);参考行 GDD 自注期望(\|S\|≈91 等)为说明非断言,任何 AC 不采用 | +| 致命类独立进程协议 | 承 D2 AC-2b / A4 AC-4b——runner manifest 逐条登记"夹具根 → 期望退出码"(AC-3 三行,均 ≠0);单进程内只做测试通道收集断言;每用例先 `reset_diagnostics()`(承 D2 冒烟协议;warning/error 断言一律经诊断收集器) | +| out-of-scope 覆盖声明 | deferral 以对侧登记为生效条件——见 out-of-scope deferral 表 | + +### 夹具规格 + +| 夹具根 | 内容(F2/F2b 均过,除违例根外) | 用途 | +|---|---|---| +| `a1_fog_domain` | f>8d 违例行 | AC-2/3(致命) | +| `a1_min_safe` | 10×10/d=0.10/min_safe=92(F2b 违例:100−round(0.10×91)=91<92) | AC-2/3(致命) | +| `a1_biome_empty` | board_biome=∅ 行(A4⑮) | AC-2/3(致命) | +| `a1_pool_empty` | VariationTable 测试行(biome_tag=[ice]、unlock_level=1,承 A4 variation_biome_tag 同型)+ BoardGenTable 两行:(1,0) board_biome={dungeon}(pool=∅)/ (1,1) board_biome={dungeon,ice}(pool 非空) | AC-13 | +| `a1_high_target@k` | 12×12/d=0.12/target=1.0/fog=1 区 0.05/min_safe=60/attempts=k∈{1,2} | AC-5/11 | +| `a1_ec5@k` | 10×10/d=0.45/min_safe=1/target=0.85(域内合法档)/fog=0/attempts=k∈{1,2,8,16};σ* 登记(@1 零合法验证种子) | AC-12 | +| `a1_two_regions` | 10×10/d=0.15/count=2/f=0.12/target=0.85/attempts=16;σ 登记 | AC-14 | +| `a1_ec2_press` | f := 8×d 计算值恰界行(程序内动态赋值);σ*′ 登记(shortfall>0 搜索种子) | AC-15 | + +### out-of-scope deferral(以对侧登记为生效条件) + +| 编号 | deferral 内容 | 对侧 / 生效条件 | +|---|---|---| +| O1 | B1 接线——attempt 边界非 scope 事件的编排侧断言、指标对账消费(actuals p10 非 target)归 B1 GDD AC(A1 侧生成半边已由 AC-5 计数器断言覆盖) | B1 GDD 登记 | +| O2 | F4 全量——N=256/行 × 全行的断言 A 严格零/B 0.95/报告 C/诊断 D = 调参期离线批量工具域(工具本体为 A1 交付物,登记 OQ;σ 基址清单入版本库),冒烟仅载缩样(AC-10) | 批量工具交付登记 | +| O3 | A2 集成——双包含回验(A2 实际展开域 ⊆ A1 排除域;A1 求解器初始态 ⊆ A2 实际初始展开域)登记 A2 GDD(A1 半边由 AC-6(a) 测试 BFS 对照承载) | A2 GDD 登记 | +| O4 | fault-injection 缺载体——EC7 生成中热重载(同步单调用无重入点,构造性成立且注入不可)、EC10 内部异常 fatal、EC4 tie-取最早(需精确同分尝试)deferral 至实现期注入设施或可选种子搜索会话 | 实现期登记 | +| O5 | 性能预算——生成耗时/换图相位无定稿预算值(Tuning Knobs OQ),perf-profile 期承载 | perf-profile 期 | +| O6 | 呈现域——EC6 雾区视觉合并归 D1、biome 色板换皮归 D1/art-bible | D1/art-bible 登记 | +| O7 | C3 全行审计——曲线导出工具全行矩阵 diff 为 C3 期交付物(D2 OQ5 既有排期) | C3 期 | + +### AC 清单(Given-When-Then,编号不变,19 条) + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | 干净检出(默认根 = MVP 参考行) | 冒烟加载 Core Rules-3 表清单 | ConfigDB Ready:BoardGenTable 含 (1,0)/(2,0) 锚行(R1 结构义务;不断言行总数——C3 加行免疫);九字段类型域断言(域 = Schema 表范围);solver_rules 定值 = single_subset(非 knob 冻结——唯一值断言);其余八 knob 字段仅域断言(参考行值 provisional,期望由表读值推导);诊断收集器零 error 零 warning(域内零诊断) | +| AC-2 | BoardGenTable schema class_name 直引程序构造行实例(每腿先 reset_diagnostics()) | validate() 作用于违例集:f>8d · fog_region_count≥1 ∧ f=0(一致性腿)· f≥1(禁全覆盖腿)· fog_region_count=0 ∧ f=计算正值(count=0 ⇒ f=0 腿)· f=0.30 ∧ d=0.15(> 0.25 绝对上限腿,8d=1.2 未绑定)· mine_density := 0.4/(W×H−9) 计算值(round = 0 → 布雷数下限腿)· attempts×W×H > 16384(F2c 成本上限)· F2b 违例(10×10/d=0.10/min_safe=92——边界值由 F2b 公式对构造参数求值导出)· board_biome=∅;再作用于合法集:count=0∧f=0(L1 标准形)· f := 8×d 计算值恰界(防浮点字面量歧义)· min_safe := W×H−round(d×(W×H−9)) 计算值恰界 · board_biome={未定稿占位词}(EC13——只校验非空、值域不校验) | 违例集各含对应致命类别;合法集零诊断 | +| AC-3 | 夹具根 a1_fog_domain / a1_min_safe / a1_biome_empty | 各以独立 headless 进程跑游戏通道 load_tables(root, collect_only=false) | 三进程均非 0 退出且错误清单完整;runner manifest 登记:fog_domain→≠0 · min_safe→≠0 · biome_empty→≠0 | +| AC-4 | 默认根 L2 行与登记种子 σ₀ | generate(2, 0, rng) 以 derive_at("board_gen", σ₀) 播种之流实例执行两遍;再以 σ₁≠σ₀ 同行生成 | 两遍产物格状态逐格相同 + 八项指标集全等(含 attempts_used);σ₁ 产物与 σ₀ 产物不全同(种子接入退化护栏) | +| AC-5 | set_master_seed(42) 后的 RngService(全真路径);夹具根 a1_high_target@2(target=1.0 必触发重试) | 以第 1、2 次 open_stream("board_gen") 实例分别驱动 generate;generate 前后读 RngService 只读计数器;同帧连续两次 generate(EC14) | 产物与 derive_at("board_gen", 0/1) 播种产物逐格相同(生成侧无流外取数);计数器差恰 1(attempts>1 共用一次 open_stream——attempt 边界非 scope 事件、重试不重开取号);同帧计数器差 2、各产物与各自 σ 播种产物一致(串行生效、无锁语义) | +| AC-6 | 缩样产物集(默认根 L1·D0/L2·D0 各 N=8;σᵢ = 10⁹+2²⁰·L+2¹²·D+i〔F4 公式缩样版〕i∈[0,8)) | 对每板断言 | 全板全腿通过:(a) 出生 3×3 内零雷零变异;R₀ 芯(0 格 flood 闭包——测试侧朴素 BFS 重算,仅依赖"0 格 8 邻传播"公开定义)内零雷零变异;闭包外扩 1 格环内零变异、环内可有雷(芯内数字格的邻雷恒在环内——所引义务原文只禁变异不禁雷,禁断言环内零雷)(b) R₀ 芯(去外扩环)内零变异(初始展开不触雾)(c) 雾格 ⊆ cell_constraint 合法集——非雷、非 0、8 邻含雷(A4②)(d) 任意 3×3 窗口非全雾(厚度≤2 窗口谓词,A4⑫)(e) 雾格数 ≤ 8×雷数(f≤8d 产物侧)(f) \|S\| ≥ min_safe_count(表读值,Rule 8)(g) 非雷格数字值 ≡ 8 邻雷数(数字计数恒真,Rule 10)(h) 全板零非法产物标记 | +| AC-7 | Board class_name 直引程序构造板(与黄金向量同批构造设施) | 合法性谓词作用于:雷墙包围孤立安全格板;\|S\|0 搜索,协议同 AC-12;搜索无解的后备语义见缺口 G4) | (a) 检查指标集;(b) generate | (a) 指标集含实际达成区数(shortfall)字段且 ≤ fog_region_count 表读值;(b) 降级标记=雾布置降级、shortfall < count 表读值、产物仍过 AC-6 全腿(合法少变异/无变异图)、error 级诊断零新增(静默降级——warning/埋点级不算违静默) | +| AC-16 | 任意冒烟产物(AC-6/10/14/15 批次复用) | 指标完备性检查 + F1/F3 重算 | 八项指标字段完备(实际 inferability【含 metric_version=1】/ inferability_core / 实际雾占比【= \|F\| ÷ (W×H)】/ 实际雷数与实际 \|S\|【并断言 \|M\| ≤ round(d×(W×H−9)) 表读值】/ fog_burden_ratio / Δbudget_fog / attempts_used / 降级标记【含非法产物标记+shortfall】);F1/F5 两项判据重算一致:inferability_core = \|R∞∖F\| ÷ max(1, \|S∖F\|);F3 重算一致:从产物重算 Chain₀/U → Δbudget_fog = \|U\|×dig_ap_cost(A3 表读值)、fog_burden_ratio 以整数对 (\|U\|, max(1, \|S∖Chain₀\|)) 比对(付费格分母口径——B1 条款 5 对账精确形);fog_region_count=0 行(L1)雾相关四值恒 0 且 core = inferability | +| AC-17 | ConfigDB 加载完成(生效前提注明:registry board_gen_table 条目与本 AC Phase 5b 同批回填——无条件断言,条目缺位即失败,非条件豁免) | 脚本解析 design/registry/entities.yaml 的 board_gen_table 条目 | 与 BoardGenTable 九字段定义(字段名/类型/域/knob 标记)逐一相等;值对账仅限非 knob 字段(solver_rules=single_subset);八 knob 字段值不对账(参考行 provisional——C3 调参豁免) | +| AC-18 | A1 文件集(生成器/度量入口/谓词/schema/冒烟脚本) | 按 D2 AC-9 模式静态扫描 | 玩法代码文件集零 randomize()、零自建 RandomNumberGenerator 构造(tests/ 路径豁免——derive_at 取号需以种子构造流实例,D2 预览语义的测试侧落地;玩法文件集不豁免);零 ConfigDB 外 data 表加载(schema class_name 直引豁免——A4 AC-5 先例);零 Control/场景类 import;AC-1 headless 实跑通过合成判定(Rule 7) | +| AC-19 | A1 文件集 | 纳入 D2 AC-14 项目级人工三要素字面量扫描 | 人工三要素口径,引用不另设(A4 AC-10 同型)——无未登记字面量 | + +### 覆盖矩阵(qa-lead 交付,验收评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules | 1→AC-4/5/6(c)/16(布雷语义)· 2→AC-8/9/10(a)/16(两项判据)· 3→AC-10/11/12 · 4→AC-6(a)(b)/7(芯/环口径)· 5→AC-6(c)(d)(e)/13/14/15 · 6→AC-4/5/14/18 · 7→AC-16 · 8→AC-2/3/6(f) · 9→AC-1/2/3/13 · 10→AC-6(g)/17/18/19 | +| Formulas | F1→AC-8/9/7/16 · F2/F2b/F2c→AC-2/3(角点/上限/布雷数下限/成本上限)· F3→AC-16 · F4→AC-10(全量 O2;断言 A″/E 属 O2)· F5→AC-10(e)/11/12/7 | +| EC | EC1→13 · EC2→15 · EC3→7/8 · EC4→11(tie O4)· EC5→12(玩家侧语义登记 B1/D1 域)· EC6→14(合并 O6)· EC7→O4 · EC8→10(c) · EC9→2/11 · EC10→O4 · EC11(F2b)→2/3 · EC12→2/15 · EC13→2 · EC14→5 | +| A4 义务 | ①→13 · ②→6(c) · ③→6(a)(b) · ④→8/16(core 常数补强)· ⑤→16 · ⑥→13 · ⑦→15 · ⑧→4/14 · ⑨→7/8 · ⑩→2/3 · ⑪→14(间接承载——共置+确定性,无取号耦合差分)· ⑫→6(d)(e)/2 · ⑬→15(部分覆盖:面积判据实现同样通过 a1_ec2_press——取号耦合差分夹具登记待核销)· ⑭→10/O2(断言 B 收紧达成区占比)· ⑮→2/3 | +| 规模 | 19 条 AC + A4×15 全落位 + 黄金五板;⑪ 差分承载与 ⑬ 差分夹具登记待核销(间接承载已注明),5 处标注问题的修正明细见评审日志 | + +## 开放悬案 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | F4 全量批量工具的 owner 排期:调参期红线工具(断言 A/A′/B/B′ + 报告 C + 诊断 D,N=256/行)——与 D2 OQ5 曲线导出工具同批登记排期(排期倒挂:A1 定稿→首行数值回填→即需抽查;临时载体 = 冒烟缩样 AC-6/10,统计意义有限,如实登记) | A1 交付物 / C3 消费 | C3 调参期前 | +| 2 | 生成性能预算与异步化选项:① 预算数值定稿——F2c 谓词形式已冻结(max_gen_attempts × W×H ≤ C),C = 16384 为 provisional 占位,ADR 裁定正式 C 与 headless debug 基准(如"64×64 单次求解 ≤ X ms");② 解空间圈定——死路二:attempts 并行化(毁 board_gen 流序即毁回放)、分帧/协程化(跨帧在飞 open_stream 实例与 D2 快照切点契约冲突,需 D3 扩容修订);活路四:同步 + 加载幕(B1 换图相位)、主线程分帧 + D3 快照扩容修订、WorkerThread + D2 EC13 单线程假设接口漂移修订(join 先于任何快照切出)、B1 预测性预生成(线性图结构的死时间生成——scope 计数器推进时点须入回放语义);③ 实现姿态约束——最小不动点可经单调 worklist 传播实现(次序无关性不强制全板重扫描——F1 注记⑤),禁全对全子集比较(ADR 验收底线);④ F4 全量工具须支持分片续跑("分钟级"成立域 = worklist 实现 ∧ 大面积行稀少,见 F4 阈值⑤) | 实现期 ADR / perf-profile | Technical Setup | +| 3 | 度量独立入口分解面确认:推理器/F3 度量/合法性谓词三入口暴露(AC-7/8/9/12 单元腿前提——缺口 G1);若 ADR 裁决不暴露,四条 AC 单元腿降级重议 | 实现期 ADR | 实现期 | +| 4 | biome 词表 owner 定稿:board_biome 值域现只校验非空(EC13);词表定稿后补域校验 + 参考行占位值替换(接口漂移) | B4/C1 域 + AD 门禁 | 垂直切片前 | +| 5 | 黄金向量手工推导第二人复核:五板逐格推导记录入版本库后安排复核(qa-lead 缺口登记——推导错误即假失配) | A1 定稿评审 | A1 /design-review | +| 6 | 精英/Boss 第三轴落点重审:节点形态定稿后强制重审——① Dmax 行约定是否覆盖全部 Boss 场景 ② 精英节点是否需第三键维 / elite_、boss_ 字段激活(分支地图同深度并存精英与普通节点时);激活即走 D2 Rule 4 改键通道 + 本表 schema 修订(接口漂移迁移,退路已登记 Tuning Knobs 节) | C1/C3 GDD | C1 定稿时 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/边界情况/义务台账/覆盖矩阵/AC 转表格;生成管线转字符画 + 出口分发表;F1–F5 公式本体、Variables 与验算数据全保留;语义与 2026-09-09 修订批对齐,零规则变更 | +| 2026-09-09 修订批 | 2026-09-09 | full /design-review 判 MAJOR REVISION NEEDED,8 条 blocking 同日修订:两项达标判据 + inferability_target 收窄 [0.85,1] 绝对锚;AC-6(a) 改写(环内可有雷);雷数取整钉死 + 布雷语义(先布后删)+ N≥1 零雷护栏;F2 ≡ Schema 跨字段校验合流(count=0 ⇒ f=0 腿);AC-11 前提腿(Lm(1)≠∅);断言 B 收紧为达成区占比;第三轴登记约定 + elite_/boss_ 后门;F2c 成本闸 16384 + 禁墙钟。cheap-now 包:σ 打包修复(10^9 + 2^20·L + 2^12·D)、F1 注记③改口(雾格入分子两通道)、\|U\|÷\|F\| 删承诺、monotone_in_depth + 断言 E、EC5 玩家侧语义登记。覆盖矩阵"无洞"自称修正为 5 处标注问题(已修 4 处,差分两项登记待核销)。日志:design/gdd/reviews/a1-board-generation-review-log.md | +| v1 定稿 | 2026-09-08/09 | 一次会话定稿:五阶段管线 + F1–F5 + 三轴分离口径 + 19 条 AC + A4×15 逐项映射;四结构决策(生成-验证循环 / 接受最优 + 零合法接受最不坏 / 推理域 single_subset 冻结 / 密度浮点)与 F4 严格零红线为用户拍板;lean 门禁 CD-GDD-ALIGN 跳过,生产前人工复核体验锚点情绪定位 | diff --git a/design/gdd/legacy-2026-09-14-v1/a2-minesweeper-rules.md b/design/gdd/legacy-2026-09-14-v1/a2-minesweeper-rules.md new file mode 100644 index 00000000..747154d6 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/a2-minesweeper-rules.md @@ -0,0 +1,704 @@ +# A2 排雷玩法规则(Minesweeper Rules) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents(systems-designer 起草 Formulas · qa-lead 起草/验证 AC) +> **承载支柱**:P1 主("每一步都算数"的执行面:连锁放大推理兑现、数字恒真、拒绝可辨)· P4(小人边缘探索的氛围本体)· P3(旗承诺与雷险的规则执行面)· P2(三事件流 = 结算归因的数据源) +> **层级**:Layer 2 Core · Milestone MVP · 设计顺序第 6 位(上游 A1/A3/A4/D2 均 Approved) +> **一句话**:A2 持有 A1 产出的棋盘、执行全部棋盘面规则,把发生的事实以恰好三种事件外发——全项目的事件源头系统。 +> **复核注记**:lean 门禁下 systems-designer 起草 Formulas(F1–F4 + 反例 B 阻断缺口)、qa-lead 起草/验证 AC(23 条 + 覆盖矩阵 + 夹具坐标规格);creative-director 的体验锚点情绪定位与 art-director 的呈现一致性未经咨询——生产前人工复核(art-bible §1–4 须在 D1 GDD 前创建)。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 扫雷层玩法规则引擎 · 全项目事件源头系统(A2 不依赖任何下游——systems-index TD-SYSTEM-BOUNDARY 唯一回边,B4/D1/D5 挂其事件、瓶颈系统规则一次定稳)· 初始展开定义落点(A3 EC5 委派)与标错惩罚悬案(OQ1)承载方 | +| 拥有 | 全部棋盘面规则:小人控制 · 8 向相邻判定 · 连锁展开 flood fill · 数字提示 · 旗子棋盘状态 · 清图判定;三事件发射面(cell_revealed / flag_judged / round_end 棋盘路径);install 四不变式;F1–F4 四条行为契约 | +| 不拥有 | 资源账本(A3——判棋盘面后调 try_spend,禁自行扣减)· 轮次编排(B1)· 奖励内容(B4)· 棋盘生成(A1)· 变异规则(A4——只透传生成期快照标记)· 呈现(D1)· 数值字面量 / 表读取 / RNG | +| 上游输入 | A1 棋盘产物(格状态 + 变异标记 + 出生区)· 玩家动作意图序列(经 D1 提交)· A3 相位观测量 + try_spend 结果 + last_round_end | +| 下游输出 | 三事件流(B4/B1/D1/D4/D5 消费)· request_dig(chain_size, clears_map) 上报(A3)· compute_move_path 路径预览数据(D1) | +| 硬约束 | 零随机 · 零数值字面量 · 零表读取 · 零 UI/输入设备依赖——给定棋盘与意图序列 ⇒ 事件流逐位确定;`--headless --script` 完整可驱动(规则层与输入/呈现解耦:输入是意图对象、输出是事件流,D1 与输入设备皆可替换) | + +A2 是扫雷层的玩法规则引擎与事件源头:接收玩家动作意图(挖 / 插旗 / 拔旗 / 移动 / 停手),执行全部棋盘面规则,把发生的事实以恰好三种事件外发;没有 A2,A1 的板、A3 的预算、B1 的结算都失去发生地。 + +### 术语表(白话首定义集中于此;已登记 registry 术语条目) + +| 术语 | 白话定义 | +|---|---| +| 双包含回验 | A1 与 A2 互检两条包含关系——臂① A2 实际初始展开域 ⊆ A1 生成期排除域(保证开局不触雷不触变异);臂② A1 求解器初始可见态 ⊆ A2 实际初始展开域(保证 A1 的度量不吃亏)。恰等 = 两集合逐格相等(不多开不少开) | +| 零特判 | A2 代码不为变异写任何 if 分支——变异只是随格携带的只读标记透传,行为差异全部由生成端约束保证 | +| 规范序 | 事件发射的固定顺序(触发格 BFS、8 邻行主序)——同输入必得同顺序,禁实现自选遍历序 | +| 语义序仲裁 | 批次内不按玩家提交顺序、而按"清图挖 > 停手 > 其余"的固定类别顺序结算 | +| 双发射源 | round_end 有两个合法发射者——棋盘路径(ap_exhausted/cleared)由 A2 发、停手路径(stopped)由 A3 直发,payload 同构 | +| 三源拒绝 | 拒绝原因来自三个互异来源——棋盘面(board_face)/ 资源面(resource_face,AP 不足)/ 相位(phase,非 Active) | +| 单意图批 / 便捷形 | settle_batch 只装一个意图时 ≡ 直接调 try_dig 等便捷 API,行为逐项相同 | +| voided | 轮次结束后未获执行的批次意图——零棋盘效果、零扣费、零事件,区别于非法拒绝 | + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 每一步都算数(执行面) | "我算对了" | 数字恒真透传(雾格只是不显示,数据层永不说谎);连锁传播与生成端度量同构(F2 ≡ A1 F1 算子①);每个拒绝携带可辨原因(board_face/resource_face/phase 三源)——系统回应的每一格都与玩家推演的一致 | +| P4 地牢下探氛围本体 | "走过的伤疤" | 小人从出生安全区向雷图边缘探索:点击已探明区瞬移穿行、在未开边界挖掘;已引爆雷格可通行——雷不是探索的终点,而是走过的伤疤 | +| P3 风险与收益同源(规则面) | "赌与算都可见" | 挖开雷格不是死亡而是事实上报(kind=mine,敌方优势在轮末结算兑现);旗子是零信息收益的纯粹承诺(雾内旗恒不中也被诚实判定)——A2 不裁决得失,只保证事实流真实 | +| P2 规划延迟兑现(数据源) | "结算可归因" | 结算明细每一行归因(安全格增益/标中旗削弱/雷→伏击/清图奖励)都溯源到 A2 事件流——reward_eligible / channel / hit 字段是"可归因"的字段落点 | + +反向护栏:任何"规则层说谎"的形态——数字失真、连锁静默变异分支、拒绝原因含混——都是 P1 的死亡体验,默认砍掉;呈现层的隐藏(雾格无字)必须以数据层恒真为前提。 + +- 参考感受:经典扫雷连锁展开的"哗啦一片"(本作保留其免费与瞬时,剥夺 chord 双击——每个决策都值得花一 AP,快开是对预算意义的稀释;CD 创意风险 #3 的兑付节拍补偿归 B1/B4)· Into the Breach 的信息诚实(拒绝你的规则从不含糊其辞)。 + +## 全局流程 + +### 装板与挖掘期(事件发射主链) + +``` +install_board(board) 受理域 = Idle/Waiting + Active·仅弃图刷新路径 + │ ① 持有产物快照(生成期变异快照,非活引用——抗表重载) + │ ② 校验结构不变式 ①–④ ── 违例 → 拒绝 + board_invalid + error 诊断(fail-fast) + │ ③ 初始展开 O₀ = R₀(免费 · 计入清图 · 无奖励 · 永不触雷触雾——Core Rule 4) + │ → cell_revealed ×|R₀|,channel=initial,规范序 = 触发格 BFS、8 邻行主序 + │ ④ 小人落位 B3 中心格 + ▼ +Installed·Active ── 挖掘期:玩家意图经 D1 → settle_batch(intents)(A2 唯一结算入口) + │ dig 棋盘面判定 → A3 try_spend → 开格 + 连锁(F2) + │ → cell_revealed ×(1+chain_size),channel=dig/chain,reward_eligible 见枚举 + │ flag try_spend → 棋盘旗标记(插/拔零事件外发,反馈经调用返回值命令式驱动) + │ move 免费瞬移(不经账本,零事件;compute_move_path 供 D1 路径预览) + │ stop 先发 flag_judged ×N → 委托 A3.request_stop() + ▼ +轮次结束事务(A3 仲裁:cleared > stopped > ap_exhausted——首次为真处定胜者,余意图 voided) + │ flag_judged ×N(判定基线 = 事务开始旗位,行主序;先于 round_end——事件序契约) + │ 棋盘路径:A2 读 last_round_end 恰一次 → round_end{ap_exhausted/cleared} 外发 + │ stopped 路径:A3 直发 round_end{stopped}(双发射源——A2 禁双发) + ▼ +Installed·Frozen ── Waiting:全态冻结;编排边界受理 install_board(换新图)/ retire_board +``` + +### 批次结算(settle_batch 语义序) + +``` +settle_batch(intents) ── 单一结算事务(到达序 ⟨I₁,…,Iₙ⟩,n ≥ 1) + │ ① 分类 class(I)(批次初态求值):C_dig(执行后 clears_map=true 的 dig) + │ · C_stop(stop)· C_other(非清 dig / flag×2 / move)——真模拟 F2,禁计数器捷径 + │ ② 语义序 ≺:C_dig ≺ C_stop ≺ C_other;同类内按到达序(稳定序) + │ ③ 逐意图执行;轮次结束首次为真处定胜者 ω + │ 其后意图全部 voided(作废)——零棋盘效果 · 零 AP 扣减 · 零事件(区别于非法拒绝) + ▼ +胜者 ω → 出口分发(见下表) +``` + +### 出口分发 + +| 出口 | 触发 | 动作 | RAP | +|---|---|---|---| +| cleared | dig 胜 ∧ 结算时点 clears_map=true | A2 外发 `(cleared, new_board)` 恰一条(棋盘路径) | +0(剩余 AP 清零不入 RAP——A3 Rule 5) | +| stopped | C_stop 胜 | 先发 flag_judged ×N → 委托 A3.request_stop() → round_end`(stopped, same_board)` 由 A3 直发 | += remaining_ap(1:1) | +| ap_exhausted | 耗尽 AP 的 C_other | A2 外发 `(ap_exhausted, same_board)` 恰一条 | +0(不变式 remaining=0) | +| 继续 | 不结束轮次 | 无 round_end,继续下一意图 | — | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +| 符号 | 含义 | +|---|---| +| U / M / S | 全格集 / 雷集 M ⊆ U / 安全格全集 S = U ∖ M | +| F / G / O | 雾格集 F ⊆ S / 旗集 G / 已开集 O ⊆ U | +| B3 | 出生 3×3 | +| num(x) | x 的真实数字(8 邻雷数,A1 AC-6(g) 数字恒真)∈ {0..8} | +| N₈(x) | x 的 8 邻 | + +#### 规则 1:A2 只按棋盘和指令算结果,不掷骰子、不写死数值、不碰界面 +**人话**:这条管 A2 规则引擎整体的姿态:它吃两样输入——A1 产出的棋盘、玩家提交的动作意图序列;吐一样输出——由三种事件组成的事实流。任何时候,同样的棋盘加同样的意图序列,必得逐位相同的事件流。为守住这一点,A2 有四条"零"禁令: + +| 禁什么 | 意思是什么 | +|---|---| +| 零随机 | 不掷骰子、不自建随机源——全程可回放 | +| 零数值字面量 | 价格读资源账本、板面读生成产物、变异随格快照携带 | +| 零表读取 | 不直接加载任何配置表,参数全经上游 | +| 零界面/输入设备依赖 | 进入 A2 的唯一通道是意图对象——界面与输入设备皆可整体替换 | + +**举证**:无计算(姿态约束非数值)。确定性、零字面量与 headless 可驱动的依据见 D2 Rule 5/1/7;A2 玩法文件集禁现 `ConfigDB.*` 引用——静态扫描验收 AC-22,同输入同输出的行为级回放断言 AC-19/AC-20。 + +#### 规则 2:A2 对外只报三种事实——开格、旗判定、轮次结束 +**人话**:这条管 A2 的对外发言面:A2 是全项目的事件源头,下游的结算、编排、呈现都只听这三种事件——挖开一格报一次"开格",轮次结束时对每面在场的旗报一条"判中/判空",轮次结束报一次"结束"。多一个事件、少一个事件都是接口漂移,必须走 GDD 修订。事件顺序有一条铁律:同一次轮次结束里,先把旗判定全部报完,再报轮次结束——所有收轮路径统一(停手路径也一样:A2 先报旗判定、再委托收轮;停手不改旗位)。 + +| 事件 | 什么时候发 | +|---|---| +| cell_revealed(开格) | 每挖开一格——初始展开、单格挖、连锁都算 | +| flag_judged(旗判定) | 轮次结束事务开始时,每面在场旗一条 | +| round_end(轮次结束) | 轮次结束时;停手路径由 A3 直发,A2 禁双发 | + +**举证**:三种事件的字段、发射点与语义要点见下方「规则 2 的实现参照——三事件回边」;下游结算触发不依赖事件到达序(B1 强制项①);payload 验收 AC-4/6/7/8/9,事件序契约验收 AC-9/AC-10。 + +### 规则 2 的实现参照——三事件回边(发射面封闭——增删事件 = 接口漂移修订) + +| 事件 | 字段 | 发射点 | 语义要点 | +|---|---|---|---| +| cell_revealed | cell · kind ∈ {safe, mine} · digit · variant_ids · channel ∈ {initial, chain, dig} · reward_eligible | 开格事实 | digit 恒真透传(仅 kind=safe 携带;雾格携带真实数字、呈现隐藏归 D1、判定数据源 = variant_ids);reward_eligible:initial 恒 false / chain·dig ∧ safe 恒 true(含链开雾格)/ kind=mine 恒 false;发射序 = 规范序(初始/连锁 = 触发格 BFS、8 邻行主序固定序) | +| flag_judged | cell · hit | 轮末统一判定批 | 每轮次结束事务开始时对判定基线旗位逐旗一条(基线 = 轮次结束事务开始时的旗位;枚举序 = 行主序扫描,确定性) | +| round_end | path_tag ∈ {ap_exhausted, cleared, stopped} · continuation_hint ∈ {same_board, new_board} | 棋盘路径由 A2 外发 | 核心字段逐字段取自 A3 `last_round_end`(读取权为接口契约),与 A3 stopped 路径 payload 同构;stopped 路径由 A3 直发、A2 禁双发 | + +#### 规则 3:装板先验货,验不过当场拒绝 +**人话**:这条管装板命令(install_board)什么时候被受理、受理后做什么。受理域:空闲/等待相位随时可以;挖掘进行中只有"弃图刷新"这一条编排路径例外(且以账本扣费成功为前提)——其余在局内装板一律拒绝。受理后 A2 持有生成期的变异快照(不是对数据表的活引用,热重载改表不影响行为),随后当场验四条结构不变式,任何一条不过就拒绝装板并给出错误级诊断,绝不带病运行;验过才执行初始展开、把小人放到出生 3×3 中心格。 + +| 验什么 | 过不了怎么办 | +|---|---| +| 出生 3×3 内无雷 | 拒绝装板 + 装板违例诊断 | +| 出生区与初始展开域内无变异格 | 同上 | +| 每格数字恒等于 8 邻雷数 | 同上 | +| 初始展开域不是全部安全格(防开局即清图) | 同上 | + +**举证**:无计算(结构校验非数值计算)。四条不变式与诊断类别封闭枚举见「状态」节不变式表;受理域矩阵见「状态」节输入×相位×板态矩阵;装板违例验收 AC-1b/AC-2、相位组合验收 AC-16、跨段隔离验收 AC-17;产物只能经程序构造或 A1 产生,运行期不存在该输入源。 + +#### 规则 4:开局先免费翻开一片安全区,这片区域有四条铁律 +**人话**:这条管开局时刻的棋盘:装板成功的那一次事务里,A2 免费翻开出生 3×3 加上与之连通的空地闭包(每次装板执行一次,跨轮不重放)。这片初始展开域与 A1 生成器求解时的初始视野逐格相等——不多开不少开,也不外加一圈"安全边距"。翻开时守四条铁律: + +| 铁律 | 意思 | +|---|---| +| 免费 | 不扣任何行动点,账本零写入 | +| 计入清图进度 | 剩余安全格从这片开完之后起算 | +| 无奖励 | 这些格不发奖励,下游不得入库 | +| 永不触雷触雾 | 出生区无雷 + 闭包只走安全格,在板上即可独立证明 | + +**举证**:展开域的精确定义、恰等证明与双包含回验两臂见 F3(A1 侧登记,门禁缺项即 blocking);"无奖励格不得入库"的下游义务见 B1 强制项②;初始展开的定义落点承接 A3 EC5 委派;验收 AC-1/AC-3。 + +#### 规则 5:挖/插/拔先过棋盘面检查,再扣行动点,任一步拒绝就整体不执行 +**人话**:这条管玩家三个付费动作——挖、插旗、拔旗——的执行顺序:A2 先查棋盘面(目标相不相邻、开没开过、旗位对不对),查过再请资源账本扣行动点;两边任何一边说不行,整个动作就被拒绝——棋盘不变、不扣费、不发事件。每次拒绝都带可辨认的原因,玩家一定看得到自己为什么被拒(文案归呈现层,数据源是原因码)。A2 自己绝不直接扣资源;挖成之后,连锁规模与清图事实由 A2 上报给账本。 + +| 拒绝原因来自 | 什么时候发生 | +|---|---| +| 棋盘面 | 目标不相邻、已开、有旗等棋盘上的原因 | +| 资源面 | 行动点不够,账本扣费被拒 | +| 相位 | 现在不可操作(收轮中或无板) | + +**举证**:棋盘面拒绝的完整目录与原因码封闭枚举见「枚举」节三源拒绝与 sub_reason 表;动作结果结构见「枚举」节对外 API 面 ActionOutcome 行;"禁自行扣减"见 A3 Rule 9;上报参数(chain_size, clears_map)的计算见 F2;验收 AC-4/AC-11/AC-21。 + +#### 规则 6:挖到空地,周围的空地连锁全开,不另花钱 +**人话**:这条管挖掘里的连锁:挖开一个数字为 0 的格时,它周围 8 向相邻的未插旗安全格免费连锁全开,一路递推到没有新的 0 格为止。连锁格只花首格的挖掘价、照常计入清图进度、奖励资格照常。连锁不越过旗子:旗格不入链、也不当传播源——旗由此成为玩家可用的"链阻断器",花 1 AP 插旗就能保护一格不被误链。雾格可以被链开但永不传播(生成端保证雾格数字恒非 0),代码里没有任何为变异写的分支。 + +**举证**:连锁闭包算子与两个上报参数的计算见 F2——与 A1 F1 算子①在同域逐字同构;chain_size = 附加格数(不含首格);clears_map 与清图判定是同一实现(禁第二套);连锁遇旗的逐格行为见 EC17;验收 AC-5/AC-6。 + +#### 规则 7:点击已探明的格子,小人瞬移过去,不花钱 +**人话**:这条管小人移动:点击任何已开格,小人经已开区一次瞬移到位——逐格走的动画是呈现层的事,规则层一次事务完成落位。合法条件两条:目标格已开,且能只经过已开格 8 向连通走过去("已探明区"包含已引爆的雷格——走过的雷可以穿行站立)。移动免费、不经账本、不发事件。配套的只读查询 compute_move_path 返回这条寻路路径,既是合法性判定的载体,也是呈现层画路径预览的数据源。 + +**举证**:无计算(纯流程规则;路径序为固定 8 邻行主序,确定性)。路径预览的表现归属归 D1 GDD(systems-index 门禁登记项);移动边界与幂等受理见 EC14;验收 AC-14。 + +#### 规则 8:旗插在棋盘上就跟着棋盘走,轮末统一判中没判中 +**人话**:这条管旗子:旗是棋盘状态的一部分,插上的旗跨轮保留、随图存续,换图/刷新/战斗结束随之消失;没有"口袋旗"库存,数量无上限,只能插在未开格上。轮次结束时,A2 对当时还在场上的每面旗报一条判定——旗下是雷判中、不是雷判空;雾里的旗恒判空(雾区无雷,判定零特判)。旗留在雷上就每轮诚实重复报判中,去重是结算侧的事,A2 只报事实。插拔旗本身不发事件,界面反馈走调用返回值。 + +| 旗动作 | 花费 | 发生什么 | +|---|---|---| +| 插旗 | 1 AP | 未开格上立一面旗 | +| 拔旗 | 1 AP | 旗从棋盘移除 | +| 挪旗 | 2 AP(拔 + 插) | 旗换个位置 | +| 挖有旗的格 | 拒绝 | 须先拔旗(1 AP)再挖 | + +**举证**:判定事件字段与行主序枚举见「规则 2 的实现参照——三事件回边」;判定基线 = 轮次结束事务开始时的旗位(被拔的旗不判);兑付去重与"每雷实例至多兑一次"归结算侧(A3 Rule 8/OQ5);挪旗价目见 A3 EC3/EC7、挖旗格拒绝见 A3 EC11;验收 AC-11/AC-12/AC-13。 + +#### 规则 9:一批指令按固定优先级结算,先达成结束条件的赢,其余 voided +**人话**:这条管一帧多个指令怎么结算:所有动作意图经批次入口提交——A2 唯一结算入口,单意图批等价于逐个调便捷接口。批次是一个整体事务:先按固定类别顺序重排——能清图的挖最优先,停手次之,其余殿后(同类内按提交顺序);再逐个执行,轮次结束条件第一次成立处定出胜者,其后意图全部 voided——零棋盘效果、零扣费、零事件;voided 不等于被拒绝——拒绝是"本来就不合法",voided 是"来不及了"。停手指令经批次入口转交资源系统,停手轮的结束事件仍由资源系统直发。 + +**举证**:类别定义、语义序与四组合真值表见 F4 与下方「规则 9 的实现参照——批次类别与语义序」;出口与 RAP 记账见「全局流程」节出口分发表;优先级口径与 A3 Rule 3 逐项一致;承接 A3 EC4 deferral;stop 经 settle_batch 提交、不直调 A3 的接线义务见 D1 强制项①;验收 AC-9/AC-10,边界情况 EC6/EC13/EC15。 + +### 规则 9 的实现参照——批次类别与语义序(F4) + +| 类别 | 成员 | 结算次序 | +|---|---|---| +| C_dig | dig 且执行后 clears_map = true(真模拟 F2——禁"rem=1 则任意 dig 必清"计数器捷径,反例 = 挖雷不入 S) | 最先 | +| C_stop | stop | 次之 | +| C_other | 非清 dig / flag ×2 / move | 其余(同类内按到达序——稳定序,多 cleared-dig 时最先者执行胜出) | + +- 结算循环:按语义序逐意图执行;轮次结束首次为真处定胜者 ω,其后意图全部 voided(零棋盘效果、零 AP 扣减、零事件——区别于非法拒绝);胜者 → 出口映射见全局流程出口分发表(RAP 记账随胜者,键 = 胜者意图类型 × 结算时点 clears_map 真值)。 +- 四组合 {cleared×stopped、stopped×ap_exhausted} × 两到达序与 A3 Rule 3 优先级逐项一致(真值表与到达序无关性精确口径见 F4);批次 cleared 胜出 → 剩余 AP 清零不入 RAP。 + +#### 规则 10:挖到耗尽或清图时,A2 报一次轮次结束——先报旗,再报结束 +**人话**:这条管挖掘期的两种收轮:一次挖掘结算让资源系统仲裁出轮次结束(行动点耗尽或清图胜出)时,A2 读取资源系统的轮次结束记录、组装恰一条轮次结束事件外发;外发之前先把本轮的旗判定全部报完(先旗后结束的铁律)。行动点归零的瞬间轮次即结束,不存在"0 行动点还能操作"的窗口。 + +**举证**:无计算——组包即字段转抄,核心字段逐字段取自 A3 `last_round_end`(读取权为接口契约),字段口径见「规则 2 的实现参照——三事件回边」round_end 行;验收 AC-7/AC-8;清图轮剩余行动点清零不入 RAP 见 A3 Rule 5。 + +#### 规则 11:挖到雷不是死——雷格变成可以走的路 +**人话**:这条管雷格挖开后的棋盘:挖开雷格报一条"这里是雷"的开格事件,轮次不结束、格子算已开——从此它可以通行:小人能站上去、能穿过它继续挖。雷格不计清图进度(不是安全格)、不能再挖、不能再插旗;同一轮可以连续挖开多颗雷,每颗都是独立事实。雷格通行是有价捷径:挖已知雷 = 花 1 AP + 给敌方送优势,经风险收益管线计价——是玩家主动抄近路的权衡,也是降级板上的兜底通路,不是免费通道。 + +**举证**:无计算(事件字段固定:kind=mine、无 digit 字段、reward_eligible=false)。"被迫挖雷过路"的构图被生成期拦截——A1 clearable 闸按"安全格 8 邻接图"连通计(挖雷通路不计入);工程债登记见 systems-index 门禁修正记录;验收 AC-15,边界情况 EC10。 + +#### 规则 12:没有双击快开——点已开的数字格就是移动 +**人话**:这条管一个刻意留白的操作:经典扫雷的"双击快开/数字和弦"在本作不存在,点击已开数字格的唯一含义就是移动小人——每个决策都值得花一次行动点,快开是对预算意义的稀释。这个"没有"是可验收的:对外接口是封闭枚举,里面根本没有快开入口;将来谁想加,就是接口漂移修订,必须走 GDD 修订流程。 + +**举证**:无计算。负空间验收形态 = 对外 API 面封闭枚举"无对应入口"(见「验收标准」节测试口径 API 面行与「枚举」节对外 API 面);chord 肌肉记忆首遇提示登记于 D1 强制项③;点击已开格 = 移动的行为验收 AC-4。 + +## 枚举 + +### 字段值封闭枚举 + +| 字段 | 值域 | 语义 | +|---|---|---| +| kind | safe · mine | 开格种类;kind=mine 事件无 digit 字段 | +| channel | initial · chain · dig | initial=初始展开(恒无奖励)· chain=连锁 · dig=单格 | +| reward_eligible | bool | initial 恒 false;chain/dig ∧ kind=safe 恒 true(含链开雾格);kind=mine 恒 false | +| path_tag | ap_exhausted · cleared · stopped | 棋盘路径仅前两者由 A2 外发;stopped 由 A3 直发(双发射源) | +| continuation_hint | same_board · new_board | cleared → new_board;其余 same_board | +| hit | bool | [cell ∈ M];被拔的旗不判;雾内旗恒 false(零特判) | +| intent | dig · flag_place · flag_pull · move · stop | stop 执行 = 批事务内委托 A3.request_stop | +| reject_reason | board_face · resource_face · phase · board_invalid | 三源拒绝 + 装板违例 | + +### 三源拒绝与 board_face 拒绝目录 + +| 源 | 判定 | 承载 | +|---|---|---| +| board_face | 挖:目标非 8 向相邻 / 目标已开 / 目标有旗(须先拔旗)· 插旗:非相邻 / 已开 / 已有旗 · 拔旗:非相邻 / 无旗 · 移动:目标未开 / 无 BFS 路径;目录外输入(越界坐标等)= out_of_catalog | sub_reason 封闭枚举(见下表) | +| resource_face | A3 try_spend 返回 false(AP 不足) | reject_reason=resource_face | +| phase | A3 非 Active 或无板态 | reject_reason=phase | + +### sub_reason(仅 board_face 携带,封闭枚举——"须先拔旗"与"非相邻"玩家语义截然不同的情形必须数据可分;批后修改 = 接口漂移) + +| 值 | 语义 | 适用动作 | +|---|---|---| +| not_adjacent | 目标非 8 向相邻 | 挖 / 插旗 / 拔旗 | +| target_revealed | 目标已开 | 挖 / 插旗 | +| target_concealed | 目标未开 | 移动 | +| flag_present | 目标已有旗(挖 = 须先拔旗;插 = 已有旗) | 挖 / 插旗 | +| flag_absent | 目标无旗 | 拔旗 | +| no_path | 经已开区无 BFS 路径 | 移动 | +| out_of_catalog | 目录外输入(越界坐标等)——debug 级诊断,不与玩家语义混桶、禁静默吞 | 全部 | + +### 装板违例诊断类别(board_invalid 携带,封闭枚举) + +| 值 | 对应不变式 | +|---|---| +| inv1_mine_in_spawn | ① B3 内无雷 | +| inv2a_variant_in_spawn | ② B3 内无变异格 | +| inv2b_variant_in_r0 | ② R₀ 内无变异格("R₀ 触雾"归此——②的 R₀ 子情形) | +| inv3_digit_invariant | ③ 数字恒真 | +| inv4_r0_equals_s | ④ R₀ ≠ S | + +### 对外 API 面(provisional,封闭枚举——新增入口 = 接口漂移) + +| API | 签名 | 语义 | +|---|---|---| +| install_board | `install_board(board)` | 装板 + 校验四不变式 + 初始展开(见状态节) | +| settle_batch | `settle_batch(intents)` | A2 唯一结算入口(D1 唯一提交入口;单意图批 = 平凡形) | +| try_dig / try_flag_place / try_flag_pull / try_move | `try_*(cell)` | 便捷形 ≡ 对应单意图批,行为逐项相同 | +| compute_move_path | `compute_move_path(cell)` | 只读——BFS 路径(8 邻行主序);不可达格返回空 | +| retire_board | `retire_board()` | 板实例与变异快照消亡(NoBoard 下幂等受理) | +| ActionOutcome | `{accepted, outcome, reject_reason, sub_reason}` | outcome ∈ {executed, voided, rejected};sub_reason 仅 board_face 携带 | + +## 数值结论与推导 + +> **口径总纲**:F1 定终局判定谓词(状态轴)、F2 定连锁展开算子(执行轴)、F3 定初始展开域(对偶轴——A1 度量与 A2 行为的接缝)、F4 定批次仲裁(事务轴)——四条均为行为契约,禁互相折算。本节零平衡公式(见显式排除声明)、零随机、零字面量、零 UI 依赖。 + +### F1 清图判定谓词(clear_predicate) + +**结论**:cleared ⇔ 已开集覆盖全部安全格(等价形:剩余安全格计数 rem = 0 ⟺ cleared);求值点穷举两处——install 初始展开事务尾(不变式④保证恒 false)与每次 dig 执行完成点;插旗/拔旗/移动不改已开集 → 结构上不可能触发 cleared;A3 `clears_map` ≡ 本谓词在该动作事务尾求值——单一谓词两个名字,禁第二套实现。 + +#### 推导 + +``` +cleared(σ) :⇔ O(σ) ∩ S(B) = S(B) 等价形:|S(B) ∖ O(σ)| = 0 +rem(σ) = |S(B) ∖ O(σ)| ——剩余安全格计数;rem = 0 ⟺ cleared +``` + +| # | 求值点 | 注 | +|---|---|---| +| 1 | install 初始展开事务尾 | 不变式④保证此处恒 false | +| 2 | 每次 dig 执行完成点(连锁完成后) | 批次内即逐 dig 求值——"批次 = 单一结算事务"指批次作为原子性单位,不改本条逐 dig 粒度,与 A3 `request_dig` 按次调用上报的契约一致 | + +| 项 | 判定处理 | +|---|---| +| 雷格 | 恒 ∉ S(已引爆雷格入 O 作 BFS 通道但不入判定) | +| 旗 | 不入 O 不入 S(带旗未开格照常计入 rem——"全图标旗不挖"永不 cleared) | +| 雾格 | 照常计入(A4 Rule 4) | + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| σ | 棋盘状态 | —— | (B, O, G) 三元组;B 由 A1 产物给定 | +| O(σ) | 格集 | R₀ ⊆ O ⊆ U | 已开集(含初始格、连锁格、已引爆雷格) | +| S(B) | 格集 | U ∖ M | 安全格全集(含雾格、含 R₀ 格、含不可达格) | +| rem(σ) | int | [0, \|S\|] | rem₀ = \|S ∖ R₀\|;rem=0 即 cleared 路径 | + +- Output Range:cleared ∈ bool。Example:10×10 板、|S|=88、R₀=20 → rem₀=68;挖开末 1 安全格(chain_size=3 连锁)→ O ∩ S = S → cleared,A2 经 `last_round_end` 外发 (cleared, new_board)。 + +### F2 连锁展开闭包(chain_closure)+ chain_size / clears_map 上报参数 + +**结论**:连锁闭包 = 单调最小不动点,与 A1 F1 算子①在 G=∅ 域逐字同构;chain_size = 附加格数(不含首格);clears_map ≡ F1 本事务尾求值(禁第二套实现);连锁不越旗(旗格不入链、不作传播源——集合减法非分支);含链开雾格、雾格永不传播(零特判定理非分支);Cl 单调幂等 ⇒ 逐动作增量求值 ≡ 一次全量闭包。 + +#### 推导 + +``` +Chain(O, c) = μX.[ O ∪ {c} ⊆ X ∧ ∀x ∈ X:( x ∈ S ∧ num(x) = 0 ⇒ N₈(x) ∩ S ∖ G ⊆ X ) ] +dig(c) 的棋盘效果:O′ = Chain(O, c);事件:cell_revealed × |O′ ∖ O|(BFS 规范序) +chain_size(dig(c)) = |O′ ∖ (O ∪ {c})| ——附加格数,不含首格(A3 AC-2/AC-6b 期望依赖) +clears_map(dig(c)) = [ S ⊆ O′ ] ——≡ F1 本事务尾求值 +``` + +- 与 A1 F1 算子①逐字同构(G = ∅ 域——A1 生成期与 A2 install/初始展开时旗集恒空,`S ∖ G = S`):R↔X 换名后触发谓词同(成员资格 ∧ 真实数字=0——禁读显示值,雾格显示空 ≠ 0 格)、传播目标同(8 邻安全格——雷永不入链)、封闭域同(S)、单调最小不动点语义同(次序无关性两侧成立——worklist 任意定序、结果唯一);同构主张范围 = 算子①本身(A2 无算子②推导项,不主张 O = R∞);增量合法性引理:Cl 单调幂等 ⇒ 逐动作增量求值 ≡ 一次全量闭包(与 A1 生成期同值)。 +- 零特判合规:雾格恒非 0(VariationTable 跨字段校验承重)⇒ 可被链开、永不传播——两个方向都是定理不是分支;旗格不入链是集合减法(`∖ G`)不是分支(旗是 A2 一等规则对象、非变异属性);A2 运行期旗非空时传播集排除旗格(连锁不越旗);双包含回验不受影响——回验只在 install 时点发生,彼时 G = ∅。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| c | 格 | U ∖ O | 挖掘目标(棋盘面合法性先行,Core Rule 5) | +| O / O′ | 格集 | 见 F1 | 挖前/挖后已开集 | +| num(x) | int {0..8} | x ∈ S | 真实数字(非显示值——漂移风险见实现期红线①) | +| chain_size | int | [0, \|S\|−1] | A3 request_dig 第一参数 | +| clears_map | bool | —— | 第二参数;≡ F1 cleared | + +- Output Range:chain_size=0 ⟺ c 非 0 格或其安全邻已全开。Example:b_chain 板挖 (3,6)(0 格)→ 级联 13 格一次开尽墙下封闭区(雷墙与板底之间的口袋形安全区,下称"封闭区"),chain_size=13、clears_map 视余量而定;全程 ap 只扣首格 1。 + +### F3 初始展开域(initial_expansion_domain)+双包含回验 + +**结论**:O₀ = R₀ = B3 ∪ flood 闭包,与 A1 F1 求解器初始态恰等(同算子同板 ⇒ 同值),显式不含外扩 1 环;恒不开雷 R₀ ∩ M = ∅ 为 A2 侧独立可证定理;双包含两臂构造性成立 ⇒ A1 回退口径(出生 3×3 最小态)不触发、度量保真零降级;退化守卫 R₀ ≠ S——install 拒绝清图退化板(不变式④纵深防御,非 A1 clearable 闸复验)。 + +#### 推导 + +``` +O₀ = R₀(B) = μX.[ B3 ⊆ X ∧ ∀x ∈ X:( x ∈ S ∧ num(x) = 0 ⇒ N₈(x) ∩ S ⊆ X ) ] + ≡ A1 F1 "出生 3×3 ∪ flood 闭包" 逐字(同算子同板 ⇒ 同值);显式不含外扩 1 环。 + +性质 1(恒不开雷,定理):R₀ ∩ M = ∅(B3 无雷 + 闭包只收安全格)——初始展开零雷险由 A2 独立可证。 +双包含回验(registry board_gen_table.a2_dual_containment 的 A2 落点): + 臂① R₀ ⊆ 排除域(以最终产物板计:出生 3×3 ∪ 闭包 ∪ 外扩 1 环)——构造性成立; + 臂② A1 求解器初始态 = R₀ ⊆ O₀ = R₀ ——恰等; + ⇒ A1 回退口径(出生 3×3 最小态)不触发,度量保真零降级。 + 注:臂①给"不触变异"保证(经排除域);"不触雷"由性质 1 独立给出(环内可有雷,两义务来源分开陈述)。 +退化守卫(不变式④):R₀ ≠ S——install 拒绝清图退化板(EC1;非 A1 clearable 闸复验)。 +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| B3 | 格集 \|B3\|=9 | ⊆ U | 出生 3×3(产物携带);小人出生 = B3 中心格 | +| R₀ = O₀ | 格集 | B3 ⊆ R₀ ⊆ S | 初始展开域;下界 9(无 0 格传播时) | + +- Output Range:|R₀| ∈ [9, |S|−1](上端点被不变式④排除)。域非空前提:非空 ⟺ |S| ≥ 10——|S| = 9 的板必有 S = B3 ⇒ R₀ = S,由不变式④在 install 拒绝(EC1 同族),不产生运行态 R₀;现行 A1 参数域(W,H ≥ 8)下 |S| ≤ 9 仅程序构造板可达。 +- Example:b_spawn 板(7×7、竖墙雷 6 枚):R₀ = x≥2 全安全格 35 格,臂① R₀ ⊆ 排除域(35 + 域外安全 8 + 环内雷)✓、臂② 测试侧朴素 BFS 重算逐格恰等 ✓。 + +### F4 批次仲裁(settle_batch) + +**结论**:语义序 C_dig ≺ C_stop ≺ C_other(同类内按到达序稳定);轮次结束首次为真处定胜者、其后意图全部 voided;(path_tag, continuation_hint, AP/RAP 终值) 对任意到达序不变(全批域)——执行/voided 划分的无关性仅对异类竞争成立;同类多成员批内划分由类内稳定序决定(不同到达序 = 不同输入,均为合法确定行为非缺陷);RAP 记账随胜者。 + +#### 推导 + +``` +类别函数(排序用,批次初始态求值): + class(I) = C_dig 若 I = dig(c) 且执行后 clears_map = true(真模拟 F2——禁计数器捷径: + "rem=1 则任意 dig 必清"为假,反例 = 挖雷不入 S) + = C_stop 若 I = stop + = C_other 其余(非清 dig / flag ×2 / move) +语义序 ≺:C_dig ≺ C_stop ≺ C_other;同类内按到达序(稳定序——多 cleared-dig 时最先者执行胜出, + 其余作废;未钉死类内序会使事件流与奖励格归属漂移——规范性义务)。 +结算循环:按 ≺ 逐意图执行;轮次结束首次为真处定胜者 ω,其后意图全部【作废】 + (false + voided:零棋盘效果、零 AP 扣减、零事件——区别于非法拒绝)。 +胜者 → 出口映射(RAP 记账随胜者;键 = 胜者意图类型 × **结算时点** clears_map 真值): + dig 胜 ∧ 结算时点 clears_map=true → (cleared, new_board);RAP += 0(剩余作废,A3 Rule 5) + ——C_dig 胜者恒满足;非清 C_other dig 可因前序 C_other dig 执行变清图,按结算时点真值入此行 + C_stop 胜 → 先发 flag_judged ×N(基线 = 事务开始旗位,停手不改旗)→ 委托 + A3.request_stop() → (stopped, same_board);RAP += remaining_ap(1:1) + 耗尽 AP 的 C_other → (ap_exhausted, same_board);RAP += 0(不变式 remaining=0) + 不结束轮次 → 无 round_end,继续下一意图 +棋盘路径 round_end 由 A2 恰一次外发;stopped 由 A3 直发(双发射源纪律,A2 禁双发)。 +``` + +**四组合真值表(≡ A3 Rule 3 优先级 cleared > stopped > ap_exhausted)**: + +| # | 批次(到达序) | 结果 | RAP | +|---|---|---|---| +| 1 | [dig清, stop] | (cleared, new_board),stop voided | +0(voided) | +| 2 | [stop, dig清] | 语义序重排 → 同 #1 | +0 | +| 3 | [stop, dig耗] | (stopped, same_board),dig voided | += remaining | +| 4 | [dig耗, stop] | 语义序重排 → 同 #3 | += remaining | + +- 到达序无关性(精确口径):**(path_tag, continuation_hint, AP/RAP 终值) 对任意到达序不变(全批域)**;执行/voided 划分的到达序无关性**仅对异类竞争成立**({C_dig, C_stop} 与耗尽的组合——四组合真值表);**同类多成员批(C_dig 或 C_other)内,执行成员、棋盘终态(旗位/小人位)与划分由类内稳定序(到达序)决定——不同到达序 = 不同输入**,均为合法确定行为非缺陷(例:ap=1 下 [move, dig耗] 两意图均执行、[dig耗, move] 的 move voided 且小人位不同;[flag_place, dig耗] 旗入板、[dig耗, flag_place] 旗 voided);cell_revealed/旗事件流同理以到达序为规范序。 +- 反例检验(语义序的存在性证明):朴素到达序下 #4 产出 (ap_exhausted, RAP+0)——违反 stopped > ap_exhausted;#2 吞掉清图高光——违反 cleared > stopped(EC10 同族缺陷)。分类时点:排序用批次初态、胜者路径用结算时点真值;C_dig 成员最先执行 ⇒ 其二态恒重合;C_other dig 的二态可因**前序 C_other dig** 执行而分歧(其批内前缀不可能含 C_dig/stop——二者任一执行即收轮、C_other 全体 voided),分歧时按结算时点真值定胜者;该前缀无 stop ⇒ 与 A3 Rule 3(cleared > stopped)不可能冲突。 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| ⟨I₁,…,Iₙ⟩ | 意图序列 | n ≥ 1 | 到达序;∈ {dig, flag_place, flag_pull, move, stop} | +| class(I) | enum 三类 | —— | 排序类别(批次初态求值) | +| ω | 意图 ∪ {∅} | —— | 胜者;∅ = 轮次继续 | + +- Output Range:(path_tag ∪ {无}, continuation_hint ∪ {无}, RAP 增量, 每意图 ∈ {执行, voided, 拒绝})。Example:ap=3、rem=1、批次 [stop, dig(末格)]:C_dig 先结算 → try_spend true、ap 3→2、事务内 cleared 胜出 → 外发 (cleared, new_board)、RAP+0;stop voided。到达序翻转结果逐项相同(#1/#2)。 + +### 显式排除声明(归属表) + +| 对象 | 归属 | +|---|---| +| 平衡公式 | 本 GDD 无——全部数值归位如下 | +| 价目、ap_per_round、RAP 规则 | A3(F4 只消费 try_spend/request_stop/last_round_end 行为契约,零数值所有权) | +| 雷布/尺寸/密度/可推理率/R₀ 与排除域定义版权 | A1(F3 是其运行期镜像;A1 侧定义修订 = 接口漂移 + metric_version 联动,A2 禁独立改动) | +| 变异约束 | A4(F2 只消费"雾格恒非 0"生成保证) | +| 奖励档位/标中旗判定/结算编排 | B 系(A2 只输出事件流) | +| 曲线 | C3 | +| A2 拥有 | 四条行为契约(F1–F4) | +| 概率/期望公式 | 零(零随机——确定性由 AC-19 行为断言承载:同板同意图序列 ⇒ 事件流逐位相同) | + +### 实现期红线(与 A1 度量的漂移风险清单) + +| # | 红线 | +|---|---| +| 1 | 0 格判定必须读真实数字 num(x),禁读显示值 | +| 2 | 初始展开禁加外扩环(破臂①)、禁只开 3×3(破臂②) | +| 3 | clears_map 与 cleared 同一实现 | +| 4 | chain_size 不含首格、链不开雷、含链开雾格 | +| 5 | A1 R₀/算子修订 = metric_version 跳变 + 本 GDD 同步修订 | +| 6 | 双包含臂①排除域以**最终产物板**计(先布后删无迭代,placed 基准可小于最终 R₀——A1 Rule 4 措辞修订义务,EC1) | + +## 状态 + +A2 的板级生命周期(轮次节拍权威在 A3/B1——A2 以 A3 相位观测量为闸,不自持轮次状态): + +| 板态 | 进入条件 | 行为/出口 | +|---|---|---| +| NoBoard | 冷启动 / `retire_board()` 后 | 全部动作 API 拒绝(phase 源);`install_board` 受理(Idle/Waiting);retire_board 幂等受理 | +| Installed·Active | 装板完成 ∧ A3 phase=Active | 接受全部动作意图;dig/旗经 `try_spend`,移动免费直达;弃图刷新 = retire + install(B1 `refresh_board` 编排,装板后回本板态);轮次结束事务 → Installed·Frozen | +| Installed·Frozen | A3 phase=Waiting(三路径任一收轮) | 全部动作意图拒绝(phase 源);棋盘/旗/小人全态冻结;`install_board`(换新图)/`retire_board` 受理(编排边界) | +| Retired | `retire_board()` 或战斗终结后编排调用 | 旗/小人/事件分段终结(事件捕获以 install 为段界)——板实例与变异快照一同消亡 | + +**输入 × 相位 × 板态矩阵**——动作意图 ∈ {dig, flag_place, flag_pull, move, stop};编排调用 ∈ {install_board, retire_board}: + +| 输入 | NoBoard | Installed·Active | Installed·Frozen | +|---|---|---|---| +| 动作意图(含 stop) | 拒绝(phase) | 受理(经批次结算器) | 拒绝(phase;A3 侧按同一致相位矩阵同判——两侧判定一致) | +| install_board | 受理(Idle/Waiting) | 受理(仅弃图刷新路径——B1 `refresh_board` 编排;非刷新路径仍拒绝——防轮内换板) | 受理(换新图 = 全态重置 + 初始展开重放) | +| retire_board | 幂等受理 | 拒绝(板态) | 受理 | + +**install 四不变式**(violation = 拒绝 + `board_invalid` + error 级诊断,fail-fast 不静默;AC-1b/AC-2 按枚举名断言): + +- 产物只能经程序构造或 A1 产生,运行期不存在该输入源。 + +| # | 不变式 | 违例诊断(封闭枚举) | +|---|---|---| +| ① | B3 内无雷 | inv1_mine_in_spawn | +| ② | B3 ∪ R₀ 内无变异格(A4 Rule 7 消费侧兜底;"R₀ 触雾"归 R₀ 子情形) | inv2a_variant_in_spawn / inv2b_variant_in_r0 | +| ③ | 数字恒真——num(x) ≡ 8 邻雷数(A2 全部规则的地基) | inv3_digit_invariant | +| ④ | R₀ ≠ S——清图退化板拒绝(防"初始展开即整图"使轮次棋盘路径出口结构性不可达;生产面由 A1 legality 闸保证,本条为纵深防御、非 A1 clearable 闸的复验——不可达安全格板 A2 照常运行) | inv4_r0_equals_s | + +不变式:板态仅上述四态、变体属性是格级只读快照(无独立运行期状态机分支于变异/雾——零特判);A2 以 A3 相位观测量为闸、不自持轮次状态。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 初始展开即整图(R₀ = S,清图退化板) | install 拒绝——不变式④ fail-fast(`board_invalid` + error 诊断),非合同内行为;生产面由 A1 legality 闸拦截。跨 GDD 阻断缺口(反例 B):A1 现行布雷数下限 N ≥ 1 必要不充分——8×8 板 8 雷 placed、先布后删幸存 1 颗距离-2 雷 → 最终板全 0 格连通 → R₀ = S,且 F4 断言 A(inferability=1.0)零拦截、N=1 参数行 100% 退化;义务 = A1 F5 legality 增 R₀ ≠ S(违例 = 该次尝试废弃重试,clearable 同款)+ F4 批量断言严格零 + Rule 4 排除域基准措辞(最终板计);A2 侧不变式④为纵深防御,双包含两臂不受影响 | 不变式④ / systems-index 门禁块登记(A1 修订义务)/ AC-1b | +| EC2 install 产物违例(B3 内雷 / B3∪R₀ 内变异 / 数字不恒真 / R₀=S) | 拒绝 + 状态不变 + error 恰一类别(不变式①–④)——信任边界 fail-fast;违例板只能由程序构造错误产生(A1 产物经生成期合法性闸),运行期不存在该输入源 | Core Rule 3 / AC-2 | +| EC3 挖已插旗格 | 拒绝(board_face,sub_reason=flag_present)——须先拔旗(1 AP)再挖;旗是"别挖这里"的承诺标记 | A3 EC11 棋盘面判定落点 / AC-11 | +| EC4 连锁传播开到雾格 | 雾格被免费开出(channel=chain)、digit 恒真透传 + variant_ids 非空(呈现隐藏归 D1)、级联自然停止(雾格非 0 不传播)、计入清图、reward_eligible=true 奖励照常入库——零特判的自然结果,代码零变异分支 | A2×4① / A4 EC4 / AC-6 | +| EC5 玩家在雾格插旗 | 合法、耗 1 AP、旗立在未开雾格上;轮末判定恒 hit=false(雾区无雷——判定逻辑零特判自然覆盖);标错惩罚上线后的一致性推论:雾内旗按"非雷格上的旗"恒受罚——无 A4/A2 特判;D5 教学须警示"雾内插旗纯损" | A2×4② / OQ1 前瞻登记 / AC-12 | +| EC6 同批次多结束路径竞争 | F4 语义序仲裁,四组合与 A3 Rule 3 逐项一致(真值表)、RAP 记账随胜者、voided 意图零副作用(零棋盘写/零扣减/零事件——被 voided 的旗动作不进轮末判定,无幽灵旗);胜出后余意图 voided,棋盘只读查询逐格不变。联合清图让位条款:批内个体非清的 dig 联合可清图时,stop 仍先执行并胜出(C_stop ≺ C_other)——联合清图高光被停手让位,为语义序的显式设计后果(单意图帧提交下生产面不可构造——D1 强制项⑥;F4 语义保留为防御性兜底,AC-10 义务不豁免) | F4 / AC-10 | +| EC7 最后 1 AP 挖开最后安全格 | cleared 与 ap_exhausted 同时成立 → A3 事务内 cleared 胜出 → A2 外发 (cleared, new_board) 恰一条、剩余 AP 清零不入 RAP——防"耗尽优先"实现把清图高光结算为最差结局 | A3 EC10 / AC-7 用例 1 | +| EC8 战斗中热重载修改 VariationTable | 板持生成期变异快照(非活引用)——行为零变化;A2 零表读取使抗重载为构造性;行为级代理 = 换根重放事件流逐位相同(AC-18) | A2×4④ / A4 EC8 / AC-18 | +| EC9 拒绝目录外溢 | 目录外输入(越界坐标等)按 board_face 拒绝 + `sub_reason=out_of_catalog`(debug 级诊断,不与玩家语义混桶)+ debug 诊断禁静默吞;三源(board_face/resource_face/phase)互异可判定——玩家面文案归 D1(映射源 = sub_reason 枚举) | Core Rule 5 / AC-21 目录外用例 | +| EC10 挖开雷格 | `kind=mine` 事件(无 digit)、轮次不结束、reward_eligible=false、格子可通行(工程债)可经其 BFS 续挖;同轮连续挖多雷 = 每雷独立事实事件;rem 不变(雷 ∉ S) | 工程债(systems-index 门禁修正记录)/ AC-15 | +| EC11 换图/刷新/战斗结束 | `install_board`(新板)= 旗/小人/已开集全量重置 + 初始展开重放;`retire_board` = 板实例与变异快照消亡——旗随图存续语义(A3 Rule 8)的 A2 侧终点 | A3 Rule 8 / AC-13/17 | +| EC12 非 Active 相位动作请求 | 拒绝(phase 源)、零事件、全态不变——A2 与 A3 两侧按同一相位矩阵各自判定、结论一致;不存在 0-AP 窗口(A3 Rule 3 的对侧消费) | States 矩阵 / AC-16 | +| EC13 批次内旗动作与终局意图竞争 | 旗动作 ∈ C_other → 被 cleared-dig/stop 压成 voided;判定基线 = 轮次结束事务开始时旗位(voided 旗动作不留痕)——语义序使基线与到达序无关 | F4 / AC-10c/d | +| EC14 移动边界 | 目标未开 / 无 BFS 路径 / 目标即当前格 → board_face 拒绝或幂等受理(目标格 = 当前格定义为零移动,合法受理);`compute_move_path` 对不可达格返回空——预览与执行同源 | Core Rule 7 / AC-14 | +| EC15 同批次多个 cleared-dig | 类内稳定序——到达序最先者执行并胜出,其余 voided;(path_tag, RAP) 天然唯一,稳定序规范的是事件流与奖励格归属 | F4 注 T1 / AC-10e | +| EC16 install 与 A3 相位组合非法(如非刷新路径的 Active 装板〔刷新路径已受理〕/ NoBoard 动作) | 编排面拒绝(板态/相位源)——B1 编排错误 fail-fast 暴露,禁静默(A3"拒绝 + debug 诊断"口径同族,进程致命保留给数据通道) | States 矩阵 / AC-16 | +| EC17 连锁前沿遇旗(连锁不越旗) | 旗格不入链、不作传播源——被旗挡的格保持未开 + 带旗,仅经该格可达的后续格本轮不被链开(经其他路径可达者照常开出);chain_size 只计实开格;旗格轮末判定照常(hit 按其是否为雷——误标安全格留待玩家自拔,自己的承诺自己付 1 AP 撤回,与 OQ1 惩罚逻辑自洽);零特判合规:传播集 `∖ G` 是集合减法非分支(F2 注) | Core Rule 6 / F2 注 / AC-5 分支 2 | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| A1 | hard(产物消费) | 棋盘产物 = 格状态 + 变异标记 + 出生区(Board class_name 数据契约);双包含回验两臂落点在本 GDD(F3:臂① 展开域 ⊆ 排除域、臂② 求解器初始态 ⊆ 展开域【恰等,A1 回退口径不触发】——门禁缺项即 blocking);install 结构不变式①②③④为消费侧信任边界(fail-fast);A1 修订义务登记 ×2 见义务表(F5 legality 增 R₀≠S + F4 批量断言严格零——"初始展开即 cleared"反例 B 与 N=1 行整行退化;Rule 4 排除域基准措辞按最终产物板计) | +| A3 | hard(账本) | `try_spend(action_kind)` 唯一扣减入口(A2 禁自行扣减);`request_dig(chain_size, clears_map)` 参数上报(A2 计算);stop 意图委托 `request_stop()`(A3 直发 stopped round_end);轮末读 `last_round_end` 组包棋盘路径 round_end(读取权 = 接口契约);相位观测量(Idle/Active/Waiting)= A2 动作闸的对侧来源;A3 API 面零改动(settle_batch 是 A2 侧入口) | +| A4 | soft(快照透传) | 变异属性经 A1 产物快照只读携带、随 `cell_revealed.variant_ids` 透传——A2 执行逻辑零变异分支(零特判原则,义务是"不加分支");A2×4 清单落位见义务表(缺项即 blocking) | +| D2 | 无直接依赖(登记事实) | A2 零表读取、零 ConfigDB 引用——参数经 A3 账本、板面经 A1 产物;D2 Interactions 表"A2 无直接依赖"由本 GDD 证实 | + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | hard(编排,未设计——provisional 登记) | 消费 round_end(棋盘路径)与全事件流结算;换图编排 = `install_board`/`retire_board` 的调用方(接线归 B1 GDD);B1 强制项 ×3 见下表(缺项即 blocking) | +| B4 | soft(订阅,未设计) | 清图奖励触发(cleared 路径)与安全格奖励内容归 B4——A2 只上报棋盘面资格事实(`reward_eligible`/`channel`),内容与折算不经 A2;连锁批量授予的溢出吸收设计待 B1/B4 条目化(13 格链 × B3 max_stack=2 ⇒ 大量"溢出未生效"行进结算明细,P2 画面反向伤害连锁幻想——溢出转一次性资源/治疗或显式接受,裁决归 B1/B4) | +| D1 | hard(呈现,未设计) | 棋盘/小人/旗/数字渲染;数字显示判定消费 `variant_ids`(雾格无字——digit 恒真透传是前提);三源拒绝呈现(映射源 = reject_reason + sub_reason);格态可辨/悬停预承诺/路径预览/单意图帧收集/voided 呈现——D1 强制项 ×6 见下表(缺项即 blocking),数据面义务详见呈现与 UI 契约节 | +| D5 | soft(Alpha) | 教学口径来源:小人三动作 + 连锁/旗/收轮以本 GDD 规则为准;教学条目:雾内旗纯损、雷格可通行、旗挡链(连锁止于旗属正常)、挖到雷 = 敌方优势而非死亡(经典扫雷玩家最大预期违背)、已开雾格无字属正常——挂 OQ1/EC5/EC17 | +| D4 | soft(Alpha) | 挖掘/连锁/雷格/收轮音效挂三事件流;旗/移动反馈经调用返回值命令式驱动(两动作零事件外发——发射面封闭的已声明代价;systems-index 依赖分层 D4 ← A2,B1) | +| A5 | soft(垂直切片预留) | 探测类道具的显影面可能挂 A2 查询接口(未开格数字显影)——接口归 A5 GDD 定稿,本 GDD 不预写(OQ4) | + +### B1 强制项(缺项即 blocking,systems-index 门禁块登记) + +| # | 义务 | +|---|---| +| 1 | 结算触发 = Waiting 相位编排点、不依赖事件到达序(stopped 轮 A3 直发 round_end 与 A2 旗判定批的到达序不承载语义) | +| 2 | `reward_eligible=false`(initial 通道)的格不得进入奖励入库(A3 EC5 委派闭环) | +| 3 | 诚实重复上报的去重义务(资格以雷格实例为键) | + +### D1 强制项(缺项即 blocking,systems-index 门禁块登记) + +| # | 义务 | +|---|---| +| 1 | stop 意图经 `settle_batch` 提交、不直调 A3(单一结算序保证) | +| 2 | 四种格态一眼可辨(已开 0 格地板 / 已开雾格无字 / 未开格 / 未开雾格——左键"已开=移动/未开=挖"消歧的感知前提;后果不对称:误挖 = 1 AP + 可能触雷且不可逆) | +| 3 | 悬停预承诺(光标区分移动/挖/旗目标语义)+ 可挖域(8 向相邻)常驻指示 + 怀旧扫雷玩家 chord 肌肉记忆首遇提示("点击数字格 = 移动小人"——非模态、每 run 一次,A4 先例) | +| 4 | 已引爆雷格"可通行可读"呈现 | +| 5 | `compute_move_path` 呈现语义定稿(合法性指示 vs 穿行动画轨道——P4 氛围载体;BFS 行主序路径可能视觉绕路,形态归 D1 裁决) | +| 6 | 单意图帧输入收集(stop 点击终结当帧收集;F4 语义序退为防御性兜底)+ 批次 voided 呈现义务(至少 stop 被 voided 须可感知——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP)+ 每次拒绝玩家可感知(文案归 D1,映射源 = reject_reason + sub_reason) | + +### 门禁义务落位表(本 GDD 门禁评审逐项核对,缺项即 blocking) + +| 编号 | 义务 | 来源 | 承载位置 | +|---|---|---|---| +| 1 | A2×4 ① EC4 连锁开雾格(无数字+级联停止+计入清图+奖励入库) | A4 | Core Rule 6 + EC4 → AC-6 | +| 2 | A2×4 ② EC5 雾内旗恒不中(含标错惩罚一致性推论) | A4 | Core Rule 8 + EC5 → AC-12 | +| 3 | A2×4 ③ 开格上报携带变体标记 | A4 | Core Rule 2 + F2 注 → AC-6/18/20 | +| 4 | A2×4 ④ EC8 生成期变异快照抗表重载 | A4 | Core Rule 3 + EC8 → AC-18 | +| 5 | 双包含臂① A2 展开域 ⊆ A1 排除域 | A1 | Core Rule 4 + F3 → AC-3 | +| 6 | 双包含臂② A1 求解器初始态 ⊆ A2 展开域(恰等,回退口径不触发) | A1 | Core Rule 4 + F3 → AC-3 | +| 7 | 初始展开定义落点(免费/计入清图/无奖励) | A3 EC5 委派 | Core Rule 4 → AC-1 | +| 8 | round_end 棋盘路径外发 + payload 同构 | A3 Rule 9 | Core Rule 2/10 → AC-7/8/9 | +| 9 | EC4 批次四组合仲裁 + RAP 随胜者 | A3 EC4 deferral | Core Rule 9 + F4 + EC6 → AC-10 | +| 10 | 已引爆雷格可通行(工程债) | systems-index 门禁修正记录 | Core Rule 11 + EC10 → AC-15 | +| 11 | 标错惩罚以 OQ 登记(实测义务保留,不阻塞设计) | systems-index 偏离登记 | OQ1(/design-review 核对在位) | + +### 跨 GDD 新登记义务汇总(本 GDD 出具,随 systems-index 门禁块登记) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| A1-1 | F5 legality 增 R₀ ≠ S(违例 = 该次尝试废弃重试,clearable 同款)——"初始展开即 cleared"反例 B 与 N=1 行整行退化阻断缺口 | A1 | 待回写 | +| A1-2 | F4 批量断言严格零 + Rule 4 排除域基准措辞(最终产物板计) | A1 | 待回写 | +| B1×3 | 结算触发相位编排点 · initial 格不入奖励库 · 兑付去重(明细见 B1 强制项表) | B1 | 已登记(缺项即 blocking) | +| D1×6 | stop 经 settle_batch · 格态可辨 · 悬停预承诺+可挖域+chord 首遇提示 · 雷格可通行可读 · 路径呈现语义定稿 · 单意图帧收集+voided 呈现+拒绝可感知(明细见 D1 强制项表) | D1 | 已登记(缺项即 blocking) | +| B1/B4-1 | 高光兑现节点条目化(含 chord 缺席的标中旗即时兑现补偿候选——清单外散文登记会丢失,B3 先例) | B1/B4 | 待条目化 | +| B1/B4-2 | 连锁批量授予溢出吸收设计(见依赖 B4 行) | B1/B4 | 待条目化 | + +双向一致性核对:A2 ← A1, A3, A4 ✅(索引依赖分层)· B4 ← A2,B3,D2 ✅ · D1 ← A2,A3,B1 ✅ · D4 ← A2,B1 ✅ · D5 ← A2,A3,B1 ✅ · A5 ← A2,A4,B4 ✅ · D2 表"A2 无直接依赖" ✅——无单向遗漏。 + +## 呈现与 UI 契约(A2 无自有渲染面与 UI——数据供给规格 + D1 呈现义务登记) + +| # | 数据面义务 | 归属 | +|---|---|---| +| 1 | 数字可读性的数据前提:digit 恒真透传(含雾格)是"数字是主角"(视觉锚 1)的逻辑地基——呈现层任何隐藏/遮罩都不改数据层真值;已开雾格 = 地板无字(负空间,防误读为 bug) | D1 | +| 2 | 绘制优先级固定(视觉锚 3):数字 > 小人 > 旗 > 变异纹理 > 底色——小人所在格用边框高亮而非覆盖文本;A2 的事件流是 D1 分层绘制的输入 | D1 | +| 3 | 三 channel 驱动动效语义(D1/B5 消费):initial(开局整片亮起——"地牢初醒")· chain(连锁撕开——P1 高光时刻的动效载体;连锁止于旗的呈现已登记 D1/教学)· dig(单格回应) | D1/B5 | +| 4 | kind=mine 事件 = 已引爆雷格视觉态:已开底 + 雷图标 + 可通行可读——玩家必须看得出"这是走过的雷、能走"(D1 强制项④) | D1 | +| 5 | 变异标记保真:variant_ids 透传错误即呈现欺骗(雾格显影/隐藏全链路的数据源) | D1 | +| 6 | 无 MVP 音频:挖掘/连锁/旗/雷格/收轮音效挂 D4(Alpha,依赖分层 D4 ← A2,B1);事件流已含全部触发点 | D4 | +| 7 | 输入模型:鼠标驱动(PC 主输入)——左键已开格 = 移动 / 未开相邻格 = 挖、右键 = 插/拔旗、停手按钮 = `settle_batch([stop])`;全部意图经 `settle_batch` 提交(D1 强制项①)——单帧多意图时 A2 语义序仲裁兜底(F4);触屏/Gamepad 非 MVP 门禁(意图对象架构使后续接入零数据层障碍) | D1 | +| 8 | 单意图帧输入收集(D1 强制项⑥):stop 点击终结当帧收集——F4 语义序退为防御性兜底 | D1 | +| 9 | 输入消歧感知前提(D1 强制项②③):四种格态(已开 0 格地板/已开雾格无字/未开格/未开雾格)一眼可辨、悬停光标区分移动/挖/旗目标语义、可挖域(8 向相邻)常驻指示——硬前提(后果不对称:误挖 = 1 AP + 可能触雷且不可逆) | D1 | +| 10 | 三源拒绝呈现:board_face(sub_reason 封闭枚举)/ resource_face(AP 不足)/ phase(收轮中)——每次拒绝玩家可感知(AC-21 为数据源验收) | D1 | +| 11 | voided 呈现义务(D1 强制项⑥):被 voided 的意图(至少 stop)须玩家可感知——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP 的读感欺骗 | D1 | +| 12 | 移动路径预览:`compute_move_path` 返回 BFS 路径,呈现语义(合法性指示 vs 穿行动画轨道)归 D1 GDD 裁决定稿(D1 强制项⑤——systems-index 门禁登记项,悬案的数据面已备) | D1 | +| 13 | 输入摩擦护栏观测:每轮原始输入动作数(含被拒/voided)∶ 当轮 AP 消耗 ≤ 2(A3 P4 载体;"点击"以设备中立"输入动作数"计)——归 D1 遥测(A2 只收意图对象、不收原始输入),≤2 为 playtest 待验证假设(挂 A3 OQ3);远距目标两击摩擦的复合意图(点击远格 = 自动 move+dig)登记为 D1 评估项(MVP 可裁决不做、留痕) | D1 遥测 | +| 14 | 结算明细无 A2 附加项:效果归因全部经事件流字段由 B1 呈现(P2) | B1 | + +## Tuning Knobs + +| 项 | 归属 | 说明 | +|---|---|---| +| 结构冻结(非 knob,修订走本 GDD 修订流程非调参) | A2 | 三事件面封闭 · 初始展开 = R₀ 恰等 · 连锁免费 · 移动/初始展开不经账本 · 无 chord · 批次语义序 · 四条 install 不变式 · 瞬移移动语义——A2 零专属 knob(显式排除声明),禁止在 A2 侧复制任何参数(D2 Rule 1 单一事实源) | +| 节奏与价目 | A3 | `ap_per_round` / `dig_ap_cost` / `flag_place_ap_cost` / `flag_pull_ap_cost` / `rap_refresh_cost` | +| 板面与推理保障 | A1 | BoardGenTable 九字段 | +| 变异资格 | A4 | unlock_level | +| `flag_pull_ap_cost` 联动观测(引用不拥有) | A3 | 挪旗成本与 OQ1 标错惩罚的双重定价位(A3 OQ2 复核落点——若实测推翻拔旗 1 AP,A2 行为零改动、仅价目表值变化) | +| `ap_per_round` 联动观测(引用不拥有) | A3 | 决定每轮可兑现的确定动作供给(A3 OQ3 供给指标) | +| A2 侧可观测指标(非 knob,遥测域) | A2 | 每消耗 1 AP 输入动作数(≤ 2 护栏,P4 点击摩擦——D1 观测)· 连锁规模分布 · 批次数/轮 | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化);A2 冒烟 = Core-Core 集成:被测面 = A2 规则引擎 + 真 A3 实例(资源全经真账本 `try_spend` 驱动——A2 不可自行扣减);冒烟另以 B1 编排角色最小形驱动 `notify_round_start()`/`notify_settlement_done()`(仅开轮/收轮;B1 就绪后改走真实调用方,断言口径不变) | +| 棋盘输入 | Board class_name 直引程序构造(不经生成器、不经 load()/preload()——A4 AC-5/A1 夹具先例;板规格即 A1 产物 schema 的坐标级实例化,A1 未实现不阻塞本冒烟) | +| API 面 | provisional 封闭枚举,见枚举节(install_board · settle_batch——D1 唯一提交入口,单意图批 = 平凡形 · try_* 便捷形 · compute_move_path 只读 · retire_board);无 chord 负空间:封闭枚举即"无对应 API"的验收形态,新增快开入口 = 接口漂移 | +| 事件捕获断言面 | B1/D1 未设计 → 以事件捕获列表断言,分订两发射面:A2 面(cell_revealed / round_end 棋盘路径 / flag_judged)+ A3 直发面(stopped round_end);payload 口径钉死:① digit 恒真透传(仅 kind=safe 携带;雾格携带真值——呈现隐藏归 D1,判定源 = variant_ids);② reward_eligible:initial 恒 false、chain/dig ∧ safe 恒 true(含链开雾格)、kind=mine 恒 false;③ round_end 核心字段 ≡ A3.last_round_end 逐字段(同构) | +| 只读观测量 | A2 侧 = 已开集/旗集/小人位/事件捕获;A3 侧 = ap/rap/phase/last_round_end;多步 GIVEN 均给出构造序列 | +| 夹具策略 | 程序构造小板、每用例先 `reset_diagnostics()`;行为夹具根 = 零(A2 无数据表、无加载期校验类别——`--data-root` 通道在本域无消费者;资源面非默认价目分支经 D2 R3 debug override 构造,A3 AC-7 同款);登记常数协议(A1 黄金向量同源):每板登记 (R₀ 成员集, \|R₀\|, \|S\|, 关键格 digit, 级联集) + 逐格手工推导记录入版本库;失效处置 = 先复核推导、推导无误即实现缺陷 | +| 期望值推导纪律 | 行为类 THEN 数值 = ConfigDB A3 表读值推导(ap_per_round/各价目经测试通道读取)⊕ 板登记常数;不写死玩法数值,唯一例外 = 登记常数;默认价目全 1 AP 下"AP 不足"分支不可构造(A3 EC2 同款)——资源面分支显式承载于 debug override 非默认表值(AC-21) | +| 致命类协议 | 零行 manifest(显式声明)——D2 AC-2b 协议适用前提 = 加载期数据通道致命;A2 无加载期校验,install 违例 = `board_invalid` 拒绝 + error 诊断(进程致命保留给数据通道;实现期 ADR 若裁决升格,回本节补 manifest 行) | +| out-of-scope | deferral 以对侧登记为生效条件——见 out-of-scope deferral 表 | + +### 夹具板清单(程序构造小板) + +spawn = 出生 3×3 中心格坐标,逐板显式登记:b_spawn / b_flag / b_batch / b_mine_walk / b_degenerate = (3,3),b_chain / b_fog = (3,2)——按默认"板中心"读法该两板 B3 = {2..4}² 会含 y=4 雷墙雷、违反不变式①、板不可安装(已修正并纳入 OQ3 复核范围)。 + +| 板 | 规格(坐标级要点) | 服务 | +|---|---|---| +| `b_spawn` | M = {(1,y): y∈[0,6]∖{3}} 竖墙 6 雷留缺口;R₀=35、\|S\|=43、未开 8 格;(1,3) digit=2 | 安装五属性/双包含/挖掘/移动/相位 | +| `b_chain` | spawn=(3,2);M = y=4 横墙 6 雷留 (3,4) 缺口;R₀=28(y∈[0,3] 全安全格);墙下封闭区 14 格;(3,6) 为 0 格、级联集 \|C\|=13 | 连锁/双包含/链不越旗 | +| `b_fog` | spawn=(3,2);b_chain 几何 + M += {(5,0),(6,1)};雾 F = {(0,5),(1,5)}(链可达)∪ {(6,0)}(链不可达);雾格 digit 2/3/2、厚度≤2 ✓;R₀ 常数随 M 变化按板重算((6,2)/(6,3) 退出 R₀) | 雾链两态/雾旗/快照/零特判/回放 | +| `b_fog_bare` | b_fog 同 M 同几何、variant 标记剥离 | AC-20 同构对板 | +| `b_flag` | b_spawn 同几何复用;(1,2)/(1,4) 雷 = hit 旗靶、(0,3) 安全 = miss 旗靶 | 旗生命周期/轮末判定/三源拒绝 | +| `b_batch` | b_spawn 同几何复用(未开恰 8 格支持构造) | 批次四组合/两收轮路径 | +| `b_mine_walk` | M = {(3,5)} 单雷;(3,4) digit=1 ∈ R₀ | 雷格挖开/已引爆可通行 | +| `b_degenerate` | 8×8、M = {(7,7)} 单雷角——R₀ = S(全 0 格连通,构造性退化) | 不变式④ install 拒绝 | +| `b_invalid_*` | ① mine_spawn:b_spawn+M+=(3,3) ② fog_spawn:b_fog+F+=(3,2) ③ fog_flood:b_fog+F+=(5,1)(R₀ 成员)④ digit:b_spawn+数字篡改 | 不变式①②③④ 各一(AC-2) | + +### out-of-scope deferral(以对侧登记为生效条件) + +| 对侧 | deferral 内容 | 对侧登记 | +|---|---|---| +| B1 | 轮次编排接线(Waiting 出口/next_round_mode/request_refresh 代理)· 结算兑付与 P2 来源标注 · 兑付去重(诚实重复上报的对侧,A3 Rule 8/OQ5)· EC8 末轮判定次序 · 双发射源消费序 | systems-index B1×8 义务清单 + 本 GDD B1×3,生效条件成立 | +| B4 | 奖励内容与差异化插槽 · debuff 档位 | A4 三条定价硬约束 | +| D1 | 数字显示消费 variant_ids · 三源呈现 · 路径预览表现 · 点击摩擦观测 | A4 D1×2 + 本 GDD D1 强制项 ×6 | +| A5 | 显影接口 | OQ4 | +| A1×A2 | 双包含端到端集成用例——"A1 真生成器产物 → A2 install"全链路回验,两系统实现后集成冒烟承载(本 GDD 冒烟用程序构造板) | 登记 deferral | +| 标错惩罚 | OQ1 登记,本节仅承一致性推论预告(惩罚前 AC-12 只断言 hit=false) | OQ1 | + +### AC 清单(Given-When-Then,编号不变,23 条) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 真 A3 实例(phase=Idle)与 `b_spawn` | `install_board(b_spawn)`,随后 `notify_round_start()` → Active | accepted=true 且事件捕获**仅**含 cell_revealed 批,逐事件断言:① 免费——A3 ap 仍=表读值(零 try_spend)且诊断零新增;② 计入清图——构造序列挖尽未开 8 格(登记常数)时恰在第 8 次触发 `round_end{cleared, new_board}`(无超前/滞后);③ 无奖励——全部 reward_eligible=false(channel=initial);④ 永不触雷触雾——全部 kind=safe;⑤ 恰等 R₀(有序断言)——事件**有序列表** ≡ 测试侧朴素 BFS 重算 R₀ 的行主序枚举,逐格逐序相等(规范序钉死——DFS/列主序等"错误但确定"的实现不可通过;登记常数 \|R₀\|=35 为独立锚) | Core Rule 4 / A3 EC5 委派 | +| AC-1b | `b_degenerate`(R₀=S 构造板) | `install_board`;随后 `install_board(b_spawn)` | accepted=false、reject_reason=board_invalid、error 诊断恰一条类别=**inv4_r0_equals_s**(枚举见状态节);随后重装 accepted=true(拒绝原子性) | EC1 / 不变式④ | +| AC-2 | `b_invalid_mine_spawn` / `b_invalid_fog_spawn` / `b_invalid_fog_flood` / `b_invalid_digit` 各板(每用例先 reset_diagnostics) | 各 `install_board` | 均 accepted=false、board_invalid、error 恰一对应类别(**inv1_mine_in_spawn / inv2a_variant_in_spawn / inv2b_variant_in_r0**〔"R₀ 触雾"归此——②的 R₀ 子情形〕**/ inv3_digit_invariant**,封闭枚举见状态节) | Core Rule 3 / EC2 | +| AC-3 | `b_chain` 与 `b_fog` 各 install 后捕获的初始域格集 E(B);测试侧按 A1 F1/Rule 4 公开定义朴素重算 R₀(B) 与排除域 X(B)=R₀∪外扩 1 环(X(B) 以最终产物板计——先布后删后基准,A1 Rule 4 措辞修订义务 EC1) | 逐板比对 | **臂①** E(B) ⊆ X(B);**臂②** E(B) ≡ R₀(B) **逐格恰等**(不欠展开也不过度展开——A1 回退口径不触发的行为面);两板全用例通过 | Core Rule 4 / F3 / registry a2_dual_containment(门禁缺项即 blocking) | +| AC-4 | `b_spawn` Active、小人于 R₀ 内 | `try_dig(c)`(相邻未开)→ `try_dig`(非相邻 / 已开格 / 有旗格)→ 等价护栏:同构造态分别驱动 `try_dig(c)` 与 `settle_batch([dig c])`(各新轮) | 第一次 executed、A3 ap −dig_ap_cost(表读值,真 try_spend)、事件恰一条 `{cell=c, kind=safe, channel=dig, reward_eligible=true, digit ≡ 测试侧重算 8 邻雷数}`;拒绝分支 rejected+board_face(sub_reason 分别 = not_adjacent / target_revealed / flag_present)、零事件、ap 不变;等价护栏:事件流/ap/棋盘只读观测量/诊断逐项相同(单意图批 ≡ 便捷形) | Core Rule 5/9 / A3 Rule 2 | +| AC-5 | `b_chain`;构造序列挖 (3,4)→移→挖 (3,5)→移→`try_dig((3,6))`(0 格);分支 2:构造至小人立于 (3,5) 后 `try_flag_place((2,5))`(未开相邻,1 AP)→ 再 `try_dig((3,6))` | 第三次挖掘(分支 1);插旗后再挖(分支 2) | 分支 1:executed 且 ap 总耗 = 3×dig_ap_cost(级联免费——非 3+13 格);事件 channel=chain 批恰 \|C\|=13 条(登记常数)且序 ≡ 测试侧 BFS 行主序枚举、全 reward_eligible=true、digit 恒真透传、事件数 = chain_size + 1(一致性恒等式);级联止于非 0 格(链只经 0 格扩);链格计入清图(挖尽封闭区后余格清图即 cleared)。分支 2:(2,5) 保持未开+带旗(零 cell_revealed)、chain_size=12(级联集减 1——(1,5)/(1,6) 经 (2,6) 照常开出,仅旗格本身被挡)、旗格轮末判定照常 | Core Rule 6 / F2 / EC17 | +| AC-6 | `b_fog`;同 AC-5 构造至 (3,6) 级联;另一用例:小人移至 (5,1) 后 `try_dig((6,0))` | 级联(链可达雾);全价挖链不可达雾 | 四项逐验:① 链可达雾 (0,5),(1,5) 免费开出(channel=chain、ap 无雾附加扣减);② variant_ids 含 "fog" 且 digit=2/3 恒真透传(呈现判定数据源——A2×4③ 同板证);③ 级联停止(级联集与 b_chain 同构板逐格同形——雾格只作叶);④ reward_eligible=true 且计入 cleared 判定。另一用例(链不可达全价):executed、channel=dig、耗 1 AP 全价、variant_ids 非空、digit=2 透传 | A2×4①③ / A4 EC4 / fog_ap_note 两态执行侧 | +| AC-7 | 用例 1(同帧竞争):`b_batch` 构造至"未开恰 1、ap=1"(cleared 与 ap_exhausted 同事务成立——"耗尽优先"缺陷仅在此构造可检出);用例 2:同构造 ap=k≥2 | `try_dig(末格)` | 用例 1:executed、A2 面 `round_end{cleared, new_board}` 恰一条(ap_exhausted 不触发)、其字段 ≡ A3 `last_round_end` 同帧读值(组包数据源行为面)、与 stopped 路径 payload 同构(字段集/类型一致)、rap 不变(剩余 AP 清零);用例 2(剩余清零非平凡分支):cleared 恰一条、rap 不变(k−1 > 0 清零非平凡) | Core Rule 10 / EC7 / A3 Rule 5/6/9/EC10 | +| AC-8 | `b_batch` 未开 ≥2、ap=1 | `try_dig(非末格)`;紧接任意 `try_*` | executed、`round_end{ap_exhausted, same_board}` 恰一条、A3 remaining_ap=0 不变式;紧接动作全部 rejected+phase、零事件 | Core Rule 10 / A3 Rule 3/EC1/EC14 | +| AC-9 | `b_batch` Active 未开完、ap=k≥1 | `settle_batch([stop])` | **A3 直发面**捕获 `round_end{stopped, same_board}` 恰一条(A2 面零 round_end——双发射源行为面)、rap = 前值 + k(1:1,表读值推导)、事件序 = flag_judged ×N 先于 round_end(事件序契约) | Core Rule 9 / A3 Rule 4/9 | +| AC-10 | `b_batch`(每用例新轮新构造)六场景:a〔dig清→stop〕· b〔stop→dig清〕· c〔dig→stop,ap=1 耗尽型构造:新轮 7 次非清挖〕· d〔stop→dig,同 ap=1 构造〕· e〔dig清₁→dig清₂ 同批〕· f〔非清 dig₁→非清 dig₂ 联合清图;多步 GIVEN:前轮挖雷 (1,2) 开道(雷格可通行的构造性利用)→ 挖尽 x=0 列余格 → 停手收轮;新轮未开恰 {(0,3),(1,3)}、ap=8〕 | 提交各批次 | a:cleared 胜出、rap 不变(stop 清零不入 RAP);b:与 a 同结果(同 path_tag/同 rap 终态/同事件数——异类序无关性);c:**语义序救援用例**——stopped 胜出(朴素到达序将产出 (ap_exhausted, RAP+0)——语义序校正为 (stopped, RAP+1))、rap += 1(表读值推导)、被弃 dig 格仍未开(续轮同图可证);d:与 c 同结果;e:类内稳定序——到达序最先者执行胜出、恰一条 cleared、第二 dig voided 零副作用;f:dig₁ 执行不清图(rem 2→1)、dig₂ 结算时点 clears_map=true 胜出 → cleared、两意图均执行、rap 不变(出口映射按结算时点真值键控——F4)。全用例断言:voided 意图零棋盘写/零扣减/零事件(棋盘只读查询逐格不变)、round_end 恰一条;c/d 断言 `flag_judged ×N` 先于 `round_end`(事件序契约) | Core Rule 9 / F4 / EC6/EC13/EC15 / A3 EC4 deferral 兑现(A3 冒烟不可构造用例在本节兑现,deferral 生效) | +| AC-11 | `b_flag`、小人于 (2,3) | 依次插 (1,4)、插 (1,2) 后拔 (1,2)(挪旗)→ `try_dig((1,4))`(有旗)→ 插非相邻/插已开/插已有旗/拔无旗/拔非相邻 | 全 executed、ap 合计 −(place+pull)×2(表读值)、旗集随动、零事件外发;挖旗格 rejected+board_face(EC11 落点);各拒绝分支 rejected+board_face | Core Rule 5/8 / A3 EC3/EC7/EC11 | +| AC-12 | 分支 1:`b_flag` 插 (1,4)(雷)与 (0,3)(安全,经挖 (1,3) 后站立可达),前置断言验"被拔的不判"(插 (1,2) 后拔,轮末无该条目);分支 2(雾旗):`b_fog` 小人相邻未开雾格插旗;分支 3(重复上报):分支 1 收轮后 `notify_settlement_done(same_board)`+`notify_round_start` 续轮旗仍在 | `settle_batch([stop])`(分支 1/3);轮末(分支 2);再停手(分支 3) | 分支 1:flag_judged 恰 2 条**集合断言**:{(1,4),true}、{(0,3),false}(行主序枚举);分支 2:`{cell,hit=false}` 恒(雾区无雷零特判;标错惩罚上线后"恒受罚"推论 = OQ1,惩罚前不可判定);分支 3:再次 {(1,4),true}——A2 逐轮诚实重复上报,**去重归 B1/B4(out-of-scope)** | A2×4② / A4 EC5 / A3 Rule 8/OQ5 上报半 | +| AC-13 | `b_flag` 已插 2 旗 stopped 收轮 | 续轮(same_board);随后 `retire_board()` + `install_board(b_fog)` | 旗集不变且**事件捕获零新增 cell_revealed**(初始展开板级一次、跨轮不重放——每轮重放的违例实现在此用例现形);重装后旗集 = 空、初始展开照常(按新板) | Core Rule 8 / EC11 / A3 Rule 8/EC9 | +| AC-14 | `b_spawn` 小人于 (3,3) | `compute_move_path((2,3))` 两次 → `try_move((2,3))` → 拒绝/幂等四对:① move 未开格 ② move 已开但 BFS 不可达格 ③ move 目标 = 当前格 ④ 正常移动分支 | 路径两次逐格相同(确定性)且全格 ∈ 已开集、相邻步 8 向合法;try_move executed、位置更新、**ap 不变**(不经账本)、零事件;① rejected+board_face(sub_reason=target_concealed);② rejected+board_face(sub_reason=no_path)**且** `compute_move_path` 对同格返回空(预览与执行同源,一用例双断言);③ executed 幂等(零移动:位置不变、零事件、ap 不变——EC14);④ 保持 | Core Rule 7 / EC14 / A3 Rule 2 移动行/P4 | +| AC-15 | `b_mine_walk`、小人于 (3,4) | `try_dig((3,5))` → `try_move((3,5))` → 经雷格续挖 (3,6) | executed、ap −1、事件恰一条 `{kind=mine, channel=dig, 无 digit 字段, reward_eligible=false}`、**轮次不结束**(两面零 round_end);try_move executed(**可通行**——工程债验收落点)、位置=(3,5);续挖正常受理 | Core Rule 11 / EC10 / 工程债登记 | +| AC-16 | NoBoard 或 Installed·Frozen(Idle/Waiting 两分支);另 GIVEN Active | 依次 dig/flag×2/move/`settle_batch([stop])`;Active 下 `install_board`/`retire_board` | 全部 rejected+phase、棋盘只读查询与事件面逐字不变;Active 下装板/退板拒绝(板态——EC16) | States 矩阵 / EC12/EC16 / A3 输入×相位矩阵对侧 | +| AC-17 | `b_spawn` 已装且部分挖掘 | `retire_board()`;随后 `install_board(b_flag)` | 全动作拒绝(phase;无板态)、事件分段边界确立(跨段断言禁止——防前图泄漏);重装后初始展开按新板重算、无前板残留(旗/小人位/已开集) | Core Rule 3 / EC11 | +| AC-18 | `b_fog` 基线事件流 E₀ 已捕获 | 测试通道换根加载变异表(B3 AC-15 换根模式)后重放同一意图序列(新 A3 实例+新 install) | 事件流与 E₀ **逐位相同**(含 variant_ids)——生成期快照非活引用;A2 零表读取(AC-22③)使抗重载为构造性,本用例为行为级代理 | A2×4④ / A4 EC8 | +| AC-19 | 同坐标规格独立构造的 `b_fog` ×2(各配全新 A3 实例) | 同一定长意图序列驱动两遍(含挖/链/雾链/插拔旗/移动/多意图批/停手收轮——覆盖三 channel、flag_judged 与批次仲裁) | 两遍事件捕获列表**逐位相同**(数、序、全字段) | Core Rule 1 / D2 Rule 5 的 A2 层 | +| AC-20 | (`b_fog`, `b_fog_bare`) 同构对(同雷布同 digit、仅 variant 标记差异) | 同一意图序列各驱动一遍 | 两事件流**除 variant_ids 外逐位相同**(差分容许域 = 该字段且仅该字段)——连锁/清图/旗判定/价目/收轮全不感知变异 | A4 Rule 4 零特判 / Core Rule 1 / A2×4③ 反向证成 | +| AC-21 | `b_flag` + debug override `action_economy.flag_place_ap_cost=2`(D2 R3 域内值;承载声明:默认价目下资源面分支不可构造,同 A3 AC-7)构造 ap=1 | `try_flag_place`(棋盘面合法)→ `try_dig`(非相邻)→ `try_dig((1,4))`(有旗格)→ Waiting 相位任意动作 → `try_dig((-1,-1))`(越界,目录外用例) | rejected+**resource_face**、全态不变;**board_face + sub_reason=not_adjacent**;**board_face + sub_reason=flag_present**("须先拔旗"与"非相邻"数据可分);**phase**——三源互异可判定;目录外:rejected+board_face + **sub_reason=out_of_catalog** + 诊断收集器恰一条 debug(非静默吞) | Core Rule 5 / EC9 / A3 EC2 对侧 | +| AC-22 | A2 文件集(规则引擎/事件载荷/冒烟脚本/夹具规格) | 静态扫描 | ① 零 `randomize()`、零自建 RNG 构造(**tests/ 豁免不启用**——A2 确定性回放不需种子,严于 A1 先例);② 零 ConfigDB 外 data 表加载(Board class_name 直引豁免);③ **零 `ConfigDB.*` 属性读取**(A2 全部输入经 A3 账本与 A1 产物——设计决策;实现期 ADR 若裁决需读表,走接口漂移修订本条);④ 零 `extends Control/Node2D`、零 `.tscn` preload;⑤ AC-1 在 `godot --headless --script` 实跑通过(退出码 0 + 断言收集器零失败)作为本项判定(Rule 7);⑥ 纳入 D2 AC-14 项目级人工三要素字面量扫描(warning 级 + 人工复核,成员清单随实现建立并入——B3 AC-19 同款) | Core Rule 1 / D2 Rule 1/2/5/7 | + +### 覆盖矩阵(验收评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules | 1→AC-19/20/22 · 2→AC-4/6/7/8/9(payload 前言钉死)· 3→AC-1b/2/17/18 · 4→AC-1/3(A3 EC5 委派 + 双包含)· 5→AC-4/11/21 · 6→AC-5/AC-6 · 7→AC-14 · 8→AC-11/12/13 · 9→AC-4 等价护栏/AC-9/10 · 10→AC-7/8 · 11→AC-15 · 12(无 chord 负空间)→API 封闭枚举 | +| Formulas | F1→AC-1②/5/7 · F2→AC-5/AC-6 · F3→AC-1⑤/3 · F4→AC-10 | +| EC | EC1→AC-1b · EC2→AC-2 · EC3→AC-11 · EC4→AC-6 · EC5→AC-12 · EC6→AC-10 · EC7→AC-7 用例 1 · EC8→AC-18 · EC9→AC-21 目录外用例 · EC10→AC-15 · EC11→AC-13/17 · EC12→AC-16 · EC13→AC-10c/d · EC14→AC-14 四对 · EC15→AC-10e · EC16→AC-16 · EC17→AC-5 分支 2 | +| 义务 | A2×4①→AC-6 · ②→AC-12 · ③→AC-6/AC-18/AC-20 · ④→AC-18 · 双包含①→AC-3 · ②→AC-3 · A3 EC4 deferral→AC-10 · 工程债→AC-15 · OQ1 在位→OQ 节 | +| 规模 | 23 条 = AC-1…AC-22 + AC-1b(原"EC4 腿"编号为 AC-6,EC17 由 AC-5 分支 2 承载) | + +## 开放悬案 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | 标错惩罚(偏离登记——实测义务保留,不阻塞设计):候选 = 敌方伏击 +1(标错旗在轮末判定 hit=false 时为敌方追加 1 层伏击;量纲随 B2 ambush 定稿联动);兼任双重职责(A3 OQ2):误标税 + 停手轮/死 RAP 态下旗滥用的唯一价格;一致性推论(上线时生效,无特判):雾内旗恒受罚(A4 EC5)、判定载体 = 本 GDD `flag_judged.hit=false` 事实流(B1/B4 消费,A2 零改动);惩罚重复性裁决项(实测会话前钉死):hit=true 兑付有去重(每雷实例一次,B1 强制项③),hit=false 惩罚**无去重声明**——按现行轮末判定载体,忘拔的错旗默认每轮 +1 伏击(5 轮 = +5),与一次性收益严重不对称;候选 = 随旗实例去重 / 显式接受每轮累计("流血"设计),须在原型实测会话前落定,防实测数据混淆"一次性过重"与"累积过重";落地动作:原型 v3 补丁实测(可与 v3 终验合并会话)→ 实测支持则修订本 GDD(判定批扩展)+ registry,否决则关闭 OQ 并在 A3 OQ2 另定价旗滥用;A2 /design-review 须核对本 OQ 在位,缺位即 blocking | 用户 + 原型小补丁会话 | 垂直切片前置 playtest 合并会话前 | +| 2 | 事件外发载体与捕获 API(实现期 ADR 确认项):signal vs 回调注册、事件捕获断言面的落地形态——依赖事件捕获列表的 THEN(AC-1/3/4/5/6/7–10/12/13/15–20 的事件序列断言)为纸面契约,ADR 未确认前该子集不视为可执行(A1 缺口 G1"度量独立入口"同型);返回值/诊断/静态扫描类断言(AC-1b/2/11/14/16/21/22 及各 AC 的 reject 分支)不在此限、现在即可执行 | 实现期 ADR | Technical Setup | +| 3 | 夹具登记常数第二人复核:各板(spawn 坐标、R₀ 成员集/级联集/digit)手工推导记录入版本库后安排复核——推导错误即假失配/假通过(A1 OQ5 同型);spawn 坐标为新增登记项(b_chain/b_fog=(3,2)——默认中心读法产出不变式①违例板) | A2 /design-review | A2 评审 | +| 4 | A5 探测道具接口预留:显影(未开雾格数字显影)对 A2 查询面的需求形态——接口归 A5 GDD 定稿,本 GDD 不预写(见依赖 A5 行) | A5 | 第 11 位 | +| 5 | D1 输入接线确认:stop 意图经 `settle_batch` 提交的接线义务(D1 强制项①)与单帧多意图的真实构造性——单意图帧输入收集已升格为 D1 强制项⑥登记约束(stop 点击终结当帧收集或等效;若 D1 保证单意图帧,四组合仲裁退化为结构不可构造——义务仍由 AC-10 冒烟承载、不豁免) | D1 | 第 10 位 | +| 6 | 拔旗价目与旗滥用观测联动:`flag_pull_ap_cost` 复核与"停手轮旗动作占比"指标的 A2 侧观测量(旗动作批占比)——A2 零改动,遥测口径随 A3 复核会话对齐 | A3 OQ2/OQ3 | 低优先 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/边界情况/AC/义务台账/覆盖矩阵/开放悬案转表格,流程转字符画;F1–F4 公式本体、Variables 与验算数据全保留;术语统一以 voided 行文;语义与 2026-09-09 定稿及 2026-09-12 D4 修订对齐,零规则变更 | +| D4 修订 | 2026-09-12 | install_board 受理域扩 Active·仅弃图刷新路径:B1 `refresh_board` 编排内、A3 扣费成功为前提;其余 Active 装板仍拒绝(防轮内换板原则对非刷新路径不变),EC16 随之收窄 | +| v1 | 2026-09-09 | full /design-review 定稿 Approved:game-designer/systems-designer/qa-lead/ux-designer 四专员 + CD 终审六 blocking 同日修订核销、用户拍板放行(A3/A4/A1/B3 先例);systems-designer 起草 F1–F4 + 反例 B 阻断缺口(登记 A1 修订义务)、qa-lead 起草 23 条 AC(AC-1…22 + AC-1b + 覆盖矩阵 + 夹具坐标规格);本次定稿含:初始展开 R₀ 恰等 / 批次语义序仲裁 / 移动瞬移语义 / install 四不变式 / sub_reason 封闭枚举(UX-1)/ 连锁不越旗(GD-1)/ 雷格通行有价捷径(GD-2)/ 到达序无关性收窄 / 联合清图让位条款 / D1 强制项 ×1→×6 / B1 强制项 ×3 / spawn 坐标修正(b_chain、b_fog=(3,2))/ 已引爆雷格可通行(工程债);评审日志 design/gdd/reviews/a2-minesweeper-rules-review-log.md | diff --git a/design/gdd/legacy-2026-09-14-v1/a3-action-economy.md b/design/gdd/legacy-2026-09-14-v1/a3-action-economy.md new file mode 100644 index 00000000..564ba159 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/a3-action-economy.md @@ -0,0 +1,455 @@ +# A3 行动经济(Action Economy) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents(数值继承原型 v2/v3 实测结论与 game-concept 定稿) +> **承载支柱**:P1 主(每轮取舍的节拍器)· P2 次(规划的延迟兑现)· P3(旗稀缺性→AP 机会成本)· P4(移动免费的氛围归属) +> **层级**:Layer 1 Foundation · Milestone MVP · 设计顺序第 2 位(上游 D2 已 Approved) +> **一句话**:A3 是玩家每轮的行动预算账本——每轮固定 AP、统一价目、停手 1:1 转存 RAP / 清图奖励两条出口,全部收益以战斗效果直兑进轮次结算,自身不产生、不折算任何抽象资源点。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 玩家每轮行动预算与支出规则的账本——扫雷侧每个决策都花 AP,所有收益以战斗效果(安全格→主角 buff、标中旗→怪物 debuff、踩雷→敌方增益)直兑进轮次结算;纯资源账本,零随机、零表现面 | +| 拥有 | AP 资源账本独占(`try_spend` 唯一扣减入口)· 统一价目 · 三结束路径仲裁(cleared > stopped > ap_exhausted)· RAP 池生命周期 · `last_round_end` 记录 · F1 五条款下游契约 · ActionEconomyTable 回填 owner | +| 不拥有 | 棋盘面规则与执行(A2)· 轮次编排与结算(B1)· 奖励内容与档位(B4)· 数值表本体(D2 承载,A3 回填)· 呈现(D1)· 教学(D5) | +| 上游输入 | D2 ActionEconomyTable(ConfigDB 读取)· A2 的 try_spend 调用与动作参数上报(chain_size / clears_map)· B1 信号(notify_round_start / next_round_mode / battle_over)· 玩家停手与刷新输入(经 D1 / B1 编排) | +| 下游输出 | round_end `(path_tag, continuation_hint)`(棋盘路径经 A2 外发、stopped 由 A3 直发)· `last_round_end` 只读记录 · F1 五条款(B1 验收)· AP/RAP HUD 数值(D1)· 教学口径(D5) | +| 硬约束 | 零随机 · 零玩法数值字面量(D2 Rule 1)· 8 API 全 bool(受理/拒绝,拒绝附 debug 诊断、非进程致命)· 单一事务原子仲裁、禁双重折算 · Core 层零 UI 依赖(`--headless --script` 冒烟可完整驱动) | + +A3 是玩家 agency 的计量器:AP 不减少总点击量,而是把点击摊销进多个轮次并插入结算节拍;真正的决策压缩器是连锁展开(1 次点击兑现一片区域)与停手(放弃剩余格子换风险退出权)。没有 A3,挖/旗/停手就没有统一成本语言;执行归 A2、兑付归 B1、显示归 D1。 + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 "我算对了"的节拍器 | "推理免费,兑现花 AP" | AP 把推理的兑现切进轮次节拍——取舍落点在构成选择(确定动作供给 >8 时先兑现哪些)与轮末尾巴(赌不确定格 vs 停手攒 RAP);供给 ≤8 时确定动作可近乎无代价延后(棋盘跨轮存续);连锁免费是推理放大器——一次正确推断被免费兑现成一整片区域;终局碎片区的零风险收尾可能跨轮完成,是节奏阀的有意代价;决策密度由 A1 供给兜底、监测归 OQ3 | +| P2 "规划的延迟兑现" | "收手换退出权" | 每个安全格的 buff 当场入库、结算明细可见(B1 归因标注);停手改产风险退出权——面对纯盲猜,攒 RAP 换一张新图是"我不赌"的体面出口 | +| P3 "风险与收益同源" | "承诺有重量" | 三向效果映射使同源原则具象——标中旗削弱敌人、踩雷强化敌人、挖安全格强化自己;旗无数量上限,稀缺性由 AP 机会成本承载(8 向相邻限制约束的是当轮几何而非总量——跨轮小人可走遍全图;连锁富集区插旗成本放大、死 RAP 态碎片区趋零,条件性对冲见 OQ2);挖已插旗格须先拔旗(EC11) | +| P4 "地牢氛围" | "边缘探索免费" | 小人移动免费、挖/旗限 8 向相邻——服务"边缘探索"的地牢感而非经济杠杆,移动成本不是调参对象;点击摩擦护栏归 A2/D1 观测(每消耗 1 AP ≤ 2 次点击) | + +参考感受:预算把节奏从"点完为止"改为"每轮取舍";价目均匀分布是有意的 MVP 简化,异质性钩子(地形成本修饰)已为 A4 预留(Rule 2);无 chord(双击快开)是有意偏离经典扫雷——保护"每格兑现都花 AP"的预算意义,正确标雷缺一键兑现节拍已登记 CD 创意风险 #3(与 #2 高光缺失合并归 B1/B4)。 + +## 全局流程 + +### 轮次生命周期(AP 支出与轮次状态流转) + +``` +notify_round_start() [B1 编排;战斗首轮要求当前 Idle] + │ RoundStart(瞬态):AP←8 硬重置 · RAP 不动(跨轮保留) + ▼ +Active(挖掘期) + │ request_dig / request_flag_place / request_flag_pull ──┐ + │ request_stop ──────────────────────────────────────────┤ 各自进入 try_spend(action_kind) + │ request_refresh ───────────────────────────────────────┘ 单一原子事务:余量检查·扣减·结束仲裁 + │ │ + │ ├─ request_refresh 且 rap ≥ rap_refresh_cost 且未达每轮上限 + │ │ → 扣费 · 当场换新图继续挖(AP 不重置 · 无清图奖励 · 轮次不终结) + │ ▼ + │ 结束路径胜出(同帧/同批次仲裁:cleared > stopped > ap_exhausted) + ▼ +RoundEnd(瞬态) + │ stopped → RAP += remaining_ap(1:1) + │ ap_exhausted → 断言 remaining_ap = 0(违反即致命) + │ cleared → 剩余 AP 清零(不入 RAP)· 清图奖励(内容归 B4) + │ 写 last_round_end(单槽只读记录) + ▼ +Waiting + │ next_round_mode = same_board → RoundStart(同图续轮) + │ next_round_mode = new_board → RoundStart(新图;仅 cleared 轮;RAP 保留) + │ battle_over → Idle(RAP 清零 · ap=0;下一场战斗由 B1 发 round_start 重启) +``` + +### 出口分发 + +| 出口 | path_tag | continuation_hint | 剩余 AP / RAP | 换图 | +|---|---|---|---|---| +| 清图 | cleared | new_board | 剩余 AP 清零(不入 RAP)+ 清图奖励归 B4 | 下轮新图 | +| 停手 | stopped | same_board | RAP += remaining_ap(1:1,无乘数) | 同图续轮 | +| 耗尽 | ap_exhausted | same_board | 恒 0(不变式断言) | 同图续轮 | +| 挖掘期刷新 | 无 round_end——轮次不终结 | — | −rap_refresh_cost(每轮 ≤ refresh_cap_per_round) | 当场换图(无清图奖励;旧板旗失效) | +| 战斗结束 | — | battle_over 信号 | RAP 清零 | → Idle | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:新一轮开始行动点重置发满,上轮剩余清零、不结转 +**人话**:这条管每轮的资源发牌,由结算编排方开轮触发。轮次开始的一瞬,A3 把行动点写满 8(表值 `ap_per_round`)——上一轮用剩多少都清零,不累积、不结转,随后进入挖掘期。唯一跨轮带走的资源是 RAP 池——战斗域里停手攒下的换图资金,开轮时原样保留、不做任何加减。 + +**举证**:无计算(AP 数值读配置表)。字段 `ap_per_round` 见「Tuning Knobs」节 ActionEconomyTable schema;RAP 跨轮保留细则见下方「规则 11 的实现参照——RAP 池规则」;开轮受理与重置断言见 AC-1a/AC-1b。 + +#### 规则 2:挖、插旗、拔旗都按同一张价目表扣行动点 +**人话**:这条管"每个动作花多少":A3 定价、A2 执行,是全项目唯一的消耗口径。玩家在挖掘期的三件付费动作——挖开格、插旗、拔旗——各花 1 行动点,且只能作用于小人 8 向相邻的格子;移动小人与连锁展开免费(逐动作价目见下方实现参照表)。经典扫雷的"双击快开"(chord)被有意去掉——每格兑现都必须花行动点,保护预算的决策意义,由 A2 承载。 + +**举证**:无计算(价目数值全部来自配置表)。逐动作价目见下方「规则 2 的实现参照——统一价目表」;成本字段与默认值见「Tuning Knobs」节;挪旗合计 2 行动点的边界见 EC3/EC7;无 chord 的取舍依据见「体验锚点」节末参考感受行。 + +### 规则 2 的实现参照——统一价目表 + +| 动作 | 消耗 | 要点 | +|---|---|---| +| 挖开格 | `dig_ap_cost` = 1 AP | 仅限小人 8 向相邻格;若触发连锁展开(flood fill),连锁部分免费(原型 v2 定稿:推理的放大器),连锁开的格计入清图进度 | +| 插旗 | `flag_place_ap_cost` = 1 AP | 仅限相邻格(原型 v3 统一行动经济);无旗子实物资源——旗为棋盘标记,数量无上限 | +| 拔旗 | `flag_pull_ap_cost` = 1 AP | 仅限相邻格(与挖/插对称);移除棋盘标记、无返还物(无口袋旗可返);OQ2 挂原型补丁复核 | +| 移动小人 | 0(冻结) | 点击已开格移动,BFS 限已探明区(A2 承载);免费服务 P4 探索氛围,非经济杠杆、调参豁免 | +| 连锁展开 | 0(冻结) | 免费;连锁开的格计入清图进度 | + +#### 规则 3:行动点用到 0 的瞬间轮次就结束,几种结束同时发生时清图优先 +**人话**:这条管"轮次什么时候算完、几种结束方式打架时听谁的"。行动点扣到 0 的那一瞬间,轮次立即以"耗尽"结束——不存在 0 行动点还能操作的窗口。每个动作都在账本事务内结算并判定一次结束;多种结束方式同时成立时(比如最后 1 行动点挖开最后一块安全格),按固定优先级只裁出一个赢家——与哪种情况先到达无关,也禁止把同一份剩余行动点按两条路径重复折算。 + +| 同时成立时 | 优先级 | 胜出后发生什么 | +|---|---|---| +| 清图(cleared) | 最高 | 剩余行动点清零 + 清图奖励,下轮换新图 | +| 停手(stopped) | 居中 | 剩余行动点 1:1 存入 RAP 池,下轮同图 | +| 行动点耗尽(ap_exhausted) | 最低 | 剩余行动点恒为 0(实现层断言,违反即致命),下轮同图 | + +**举证**:无计算(优先级为固定常量序 `cleared > stopped > ap_exhausted`,单事务一次判定)。同帧/同批次多路径竞争的仲裁语义见 EC4("先停后挖"腿 deferral 至 A2 集成测试);最后 1 行动点同时清图与耗尽时清图胜出见 EC10;验收 AC-4/AC-4b/AC-8。 + +#### 规则 4:玩家随时可以主动停手,剩余行动点原值存进 RAP 池 +**人话**:这条管玩家的提前收工:挖掘期任意时刻,玩家可以直接停手——输入层直调 A3,不经棋盘。停手立即结束本轮(结束方式标记 `stopped`),剩余行动点按 1:1 原值转存 RAP 池,没有乘数加成;停手不是战斗收入流,是纯风险管理工具——"这轮我不想赌"的体面出口。停手时图没开完也没关系,下一轮继续在同一张图上挖。 + +**举证**:无计算(1:1 转存,无乘数、无公式)。停手与挖掘期刷新的逐维差异见下方「规则 4 与规则 11 的实现参照——停手与挖掘期刷新对照」;满 AP 停手恰可达一次刷新的基线节拍及其退化环分析见 EC6;验收 AC-5/AC-15。 + +#### 规则 5:安全格全部开完即清图——剩余行动点清零,另发清图奖励 +**人话**:这条管"把图开穿"的时刻:全部安全格开完(含连锁展开开的格),轮次以"清图"结束——剩余行动点直接清零,不折算、不进 RAP 池;同时发放清图奖励。奖励发什么、发多少归 B4 定,且是差异化插槽——B4 的奖励表须预留按图类型(A1 的 biome/tag 上下文)分支的结构位;A3 只上报"清图了"这一事实,不拥有奖励内容、不感知图类型。清图后下一轮换一张新图。 + +**举证**:无本地计算——奖励当量归 B4 定档,量级锚与下界见「数值结论与推导」节 F1 条款 4;奖励差异化插槽的上下文约束见 OQ6;连锁开完全图同样触发清图见 EC5;验收 AC-6/AC-6b。 + +#### 规则 6:轮次怎么结束只向外报一条消息,结算里不夹带任何折算数值 +**人话**:这条管轮次结束时对外的"发言":三种结束路径都只向结算编排方(B1)输出一条两字段消息——"怎么结束的 + 下轮用不用换图的提示"。这只是编排提示;权威的换图指令是 B1 之后回发的 `next_round_mode`。除此之外,消息不夹带任何折算数值——进结算明细的收益全部是具体战斗效果(主角 buff / 怪物 debuff / 清图奖励),来源归因标注由 B1 承担。 + +| 结束方式 | 发出的换图提示 | B1 权威回发(next_round_mode) | +|---|---|---| +| 清图 | new_board(建议换新图) | new_board——仅清图轮可换新图 | +| 停手 | same_board(建议同图续轮) | 恒 same_board(回发违约即 fail-fast) | +| 行动点耗尽 | same_board(建议同图续轮) | 恒 same_board(回发违约即 fail-fast) | + +**举证**:无计算。字段取值与产出点见「枚举」节 path_tag / continuation_hint 行;权威回发信号的消费位置见规则 9 与「状态」节 Waiting 行;结算明细归因标注义务见「呈现与 UI 契约」节第 5 条;验收 AC-4b/AC-5/AC-6。 + +#### 规则 7:全部数值存在配置表里,代码不写死一个数、不掷一次骰子 +**人话**:这条管数字的存放纪律:每轮发多少行动点、每个动作花多少、刷新门槛多高、每轮能刷几次——所有玩法数值都存在 D2 的 `ActionEconomyTable` 配置表里,经 ConfigDB 类型化读取。A3 的代码零玩法数值字面量:改数值=改表回填,不动代码;表不变,行为逐位相同。本系统也不涉及任何随机数——没有 RNG 流。 + +**举证**:无计算(本条是存放纪律,非数值规则)。字段清单、类型域与默认值见「Tuning Knobs」节 ActionEconomyTable schema;零字面量纪律依据 D2 Rule 1;零随机的行为级验收(同输入序列重放逐位相同)见 AC-17;表值与 registry 一致性见 AC-10a。 + +#### 规则 8:旗是棋盘上的标记不是背包道具,每颗雷的旗收益只能兑一次 +**人话**:这条管旗子的形态与兑付资格:旗没有"口袋/背包"这类实物资源——插上去就是棋盘上的一面标记,归棋盘系统(A2)所有;跨轮保留、随图存续,清图或刷新换图时随之消失。数量无上限,稀缺性全由"插旗要花行动点"承载:插旗=1 行动点+棋盘多一面标记,拔旗反向(标记移除)。标中旗的收益兑付有五条硬约束: + +| 兑付硬约束 | 意思 | +|---|---| +| 兑付一次性 | 每颗雷在本次战斗内至多兑付一次,资格随首次兑付消费 | +| 当轮结算 | 旗的放置实例只是当轮结算的兑付载体,不囤积到以后 | +| 资格以雷格实例为键 | 认雷不认旗——拔旗重插同一雷格,不恢复已消费的资格(堵死每轮 2 行动点的重插农场) | +| 禁止逐轮重复兑付 | 同一颗雷的收益不会逐轮反复发 | +| 换图资格重置 | 换图后新图的雷是新实例,资格自然重置 | + +**举证**:无计算。兑付硬约束的定稿依据见 OQ5(开放悬案节第 5 条);挖已插旗格须先拔旗见 EC11;同轮插了又拔、挪旗的消耗见 EC3/EC7;旧板旗随挖掘期刷新失效(零兑付)见下方「规则 11 的实现参照——RAP 池规则」"旧板旗"行;验收 AC-3a/AC-3b/AC-16。 + +#### 规则 9:扣行动点只认账本一个入口,"轮次结束"消息有两个合法发送者 +**人话**:这条管"谁有权扣行动点、谁有权宣布轮次结束"。全项目唯一的扣减入口是 A3 的账本接口——余量检查、扣减、结束仲裁在同一次事务内原子完成。棋盘系统(A2)只判棋盘面合法性(相邻、目标格状态、无双击快开)并执行棋盘效果:它是挖/插/拔的调用方,必须经账本扣费,自己无权扣资源。"轮次结束"消息有两个合法发射源,逐项见下表。 + +| 事项 | 责任方 | 运作方式 | +|---|---|---| +| 棋盘路径外发(耗尽 / 清图) | A2 | 账本扣费成功且本次事务内轮次结束时,A2 读取 `last_round_end` 组包外发——该读取权是接口契约的一部分,非仅测试设施 | +| 停手路径直发(stopped) | A3 | 停手是非棋盘输入,由输入层直调 A3,A3 仲裁后自行外发,payload 与 A2 路径同构——纯停手轮不存在"无发射者"漏洞 | +| `last_round_end` 只读记录 | A3 | 单槽只读记录;测试/审计断言落点,同时是 A2 组包 round_end payload 的合法数据源 | +| Waiting 出口 | A3 | 消费 B1 回发的 `next_round_mode ∈ {same_board, new_board}` 与 `battle_over` 信号 | + +**举证**:无计算。唯一扣减入口签名 `try_spend(action_kind) -> bool` 与事务边界见「全局流程」节轮次生命周期字符画与「状态」节状态机;账本资源面拒绝(AP 不足)见 EC2;Waiting 相位非法拒绝见 AC-9;停手 payload 同构与"恰一条输出"验收见 AC-5/AC-8;同值双发射不可区分的测试口径见「验收标准」节测试口径表 last_round_end 单槽行。 + +#### 规则 10:扫雷收益全部直接兑成战斗效果,没有中间货币 +**人话**:这条管"扫雷赚的东西以什么形式到手":全部收益——挖到安全格给主角上 buff、标中旗给怪物上 debuff、踩雷给敌方上增益、清图发奖励——都以战斗效果直兑,直接进轮次结算明细;不存在抽象点数、不存在折算乘数,折算公式根本不存在。这套对账契约共五条款,约束效果侧(结算系统),登记为 B1 GDD 验收项。 + +**举证**:五条款全文(单一当量对照 / 标中旗锚 / 挖掘风险不等式 / 清图奖励 / 弃图支配性)、推导与公式见「数值结论与推导」节 F1;playtest 复核归 OQ3、当量归一归 OQ1;systems-index 门禁 ①–⑤ 登记。 + +#### 规则 11:RAP 池只从停手进账、只为换新图花钱,战斗结束就清零 +**人话**:这条管"停手攒下来的钱":RAP 池的唯一进账是停手时剩余行动点的 1:1 转存;它属于整场战斗——战斗内跨轮次、跨扫雷图一直保留,战斗结束(进入 Idle)立刻清零。唯一花法是挖掘期换一张新图继续挖:玩家自己决定、经刷新入口发起、账本受理,每轮最多刷 2 次,禁止"攒满就自动刷"。RAP 不折算、不兑付任何战斗收益——纯风险退出资源。 + +**举证**:无独立公式(进账 1:1、扣费门槛与每轮上限均为配置表值/常量)。逐维细则、默认值与边界见下方「规则 11 的实现参照——RAP 池规则」;停手与刷新的逐维对照见下方「规则 4 与规则 11 的实现参照——停手与挖掘期刷新对照」;RAP 不足拒绝见 EC13、超限拒绝见 EC15、战斗末清零可见反馈见「呈现与 UI 契约」节第 3 条;验收 AC-5b/AC-11/AC-15。 + +### 规则 11 的实现参照——RAP 池规则 + +| 维度 | 细则 | +|---|---| +| 来源 | 唯一来源 = 停手转存(1:1,无乘数);清图与 ap_exhausted 的剩余 AP 均不入 RAP | +| 生命周期 | 战斗域:战斗内跨轮次、跨扫雷图累积;`battle_over` 进入 Idle 时清零(每场战斗清零;清零的可见反馈与教学落点见呈现与 UI 契约——防"攒的钱凭空没了"读感);MVP 无 RAP 上限;跨战斗携带为未来选项,若开放须修订本条并登记 D3/C2 持久化归属(OQ7) | +| 支出 | 唯一支出 = 刷新雷图;决策主体 = 玩家——经 D1 刷新入口在挖掘期(Active 相位)发起,B1 `refresh_board` 挖掘期刷新编排内调用 `request_refresh() -> bool`;禁止"RAP 满即自动刷" | +| 受理与换图 | `rap ≥ rap_refresh_cost`(默认 8)时扣减并当场换新图继续挖——AP 不重置、本轮剩余 AP 保留(A2 装板受理域已同步:Active·仅刷新路径);刷新获得的新图不产生清图奖励(它不是清图);不足则拒绝、状态不变(EC13) | +| 每轮上限 | `refresh_cap_per_round`(默认 2)——轮内计数、RoundStart 重置,超限拒绝(EC15);无冷却;每次独立检查 `rap ≥ rap_refresh_cost` 并扣减 | +| 旧板旗 | 随旧板失效——零兑付、旗 AP 不返还(与拔旗不返还同族;A2 换图全量重置承载) | +| 经济注记 | 刷新价格实际 = rap_refresh_cost + 每轮 2 次上限;挖掘期当场刷新无结算发生——敌方每轮照常结算的时间税不构成刷新本身的隐性成本;OQ3 弃图率/刷新率预警阈值联动复核 | +| 收益 | 无战斗收益——RAP 不折算、不兑付任何战斗效果,纯风险退出资源 | + +### 规则 4 与规则 11 的实现参照——停手与挖掘期刷新对照 + +| 维度 | 停手(规则 4) | 挖掘期刷新(规则 11) | +|---|---|---| +| 时点 | Active 任意时刻 | Active(挖掘期) | +| 发起 | 玩家输入层直调 A3 | 玩家经 D1 发起,B1 `refresh_board` 编排内调用 | +| 轮次 | 结束(path_tag=stopped),本图未开完 → 下轮同图 | 不终结——当场换新图继续挖 | +| 资源 | RAP += remaining_ap(1:1) | RAP −= rap_refresh_cost(每轮 ≤ 2 次) | +| 收益 | 风险退出权(无战斗收益) | 新图(无清图奖励;旧板旗失效零兑付) | + +## 枚举 + +### 值域封闭枚举(path_tag / 信号 / 动作类型 / 相位) + +| 值 | 字段 | 产出点 | 语义 | +|---|---|---|---| +| ap_exhausted | path_tag | AP 归 0 瞬间(try_spend 事务内,A2 外发) | 轮次耗尽;continuation_hint 恒 same_board;不变式 remaining_ap=0 | +| cleared | path_tag | 全部安全格开完(含连锁格;A2 外发) | 清图;恒 new_board;剩余 AP 清零 + 清图奖励(B4) | +| stopped | path_tag | 玩家主动停手(A3 直发) | 提前结束;恒 same_board;RAP += remaining_ap(1:1) | +| same_board | continuation_hint / next_round_mode | stopped/ap_exhausted 轮输出;B1 回发 | 下轮继续同图(stopped/ap_exhausted 轮恒 same_board——B1 回发违约 fail-fast) | +| new_board | continuation_hint / next_round_mode | cleared 轮输出;B1 回发 | 下轮换新图(A1 经 B1 编排生成) | +| battle_over | B1 → A3 信号 | 战斗终结 | → Idle,RAP 清零、ap=0 | +| dig / flag_place / flag_pull | action_kind(try_spend 参数) | A2 棋盘面判定通过后 | 账本扣减的三种动作;move/chain 0 成本不经账本(A2 承载) | +| Idle / Active / Waiting | phase(只读观测量) | 资源状态机 | 可观测相位域全集;RoundStart/RoundEnd 为事务内瞬态、不可观测 | + +### 对外 API 面(封闭枚举,8 入口全 bool——新增入口 = 接口漂移) + +| API | 签名 | 语义 | +|---|---|---| +| notify_round_start | `notify_round_start() -> bool` | 开轮(B1 编排) | +| request_dig | `request_dig(chain_size: int, clears_map: bool) -> bool` | 挖掘;chain_size = 连锁附加格数(不含首格,≥0;首格恒耗 1 AP);clears_map = 本次动作后全图安全格开完 | +| request_flag_place | `request_flag_place() -> bool` | 插旗 | +| request_flag_pull | `request_flag_pull() -> bool` | 拔旗 | +| request_stop | `request_stop() -> bool` | 停手(A3 直发 stopped round_end) | +| request_refresh | `request_refresh() -> bool` | 挖掘期刷新(Rule 11) | +| notify_settlement_done | `notify_settlement_done(next_round_mode) -> bool` | 记录编排意图(phase 停留 Waiting) | +| notify_combat_end | `notify_combat_end() -> bool` | 战斗终结 → Idle、RAP 清零 | + +## 数值结论与推导 + +### F1 下游经济契约(downstream_effect_contract) + +**结论**:全部扫雷收益以战斗效果直兑(安全格→主角 buff、标中旗→怪物 debuff、踩雷→敌方增益),无抽象点数、无折算乘数——折算公式不存在;契约为对效果侧的五条款,登记为 B1 GDD 验收项(systems-index 门禁 ①–⑤);playtest 复核归 OQ3,当量归一归 OQ1。 + +#### 推导 + +| # | 条款 | 内容 | 闭合 owner | +|---|---|---|---| +| 1 | 单一当量对照条款 | B1 须出效果档位对照表(1 最低档安全格 buff = ? 当量;debuff/清图奖励/敌方增益均对照归一),对全部收益来源单一换算——分源改率 = 契约失效,须回本 GDD 重议;归 OQ1(注意与 OQ6 清图奖励插槽区分) | B1 | +| 2 | 标中旗锚条款 | 标中旗的怪物 debuff 档位当量必须严格大于最低档安全格 buff 当量——否则插旗在最优情形下也只是打平,P3 旗循环经济空心;且对照基准须为连锁放大的边际挖掘收益(单 AP 平均挖掘当量,含连锁期望放大)——只对最低档比较会使旗理性萎缩到碎片区;每战标中旗总吞吐(N 旗 = N debuff)无单档上限可依恃,由 B1 按条款 1 对照表对账;插旗挤占清图进度的边际轮成本由 B4 定档时核算,或在其 GDD 声明 MVP 忽略并给理由 | B4 表设计 + B1 对照验收 | +| 3 | 挖掘风险条款 | 挖掘决策 EV 健康不等式(见公式):两端分别锚定 A1 可推理率生成参数(P1 下限,决定 P(安全) 的玩家可达上界)与 B2 惩罚上限表值(上限值登记为 B2 GDD 验收项);B1 为单一闭合对账 owner(A1/B2 供给锚值,B1 签字验收),登记为 B1 GDD 强制检查点;P1 支柱要求 A1 可推理率使玩家能推高 P(安全)——本条款是"可推理率"的经济侧验收形态(可判定谓词) | B1 | +| 4 | 清图奖励条款 | 清图奖励 > 0,量级与差异化分支归 B4(输入含 A1 图类型上下文) | B4 | +| 5 | 清图 vs 弃图刷新支配性条款 | 清图奖励当量下界须使"推到底清图"的期望当量 ≥ "确定格挖完即弃图刷新"的期望当量(见公式)——否则每图最优解退化为"捞完确定格就弃",清图高光节拍、B4 差异化插槽、game-concept"图完成"循环全部空心;该环强度与 A1 可推理率反向耦合——可推理率越低(迷雾/终局 50/50 越多),弃图越优,玩家对 P1 失败的理性回应会反过来饿死清图内容,故本条款是 P1 失败模式的经济对冲,与条款 3 共同登记 B1 强制检查点 | B1 | + +``` +条款 3(挖掘决策 EV 健康不等式): + P(安全) × V_safe_lb > P(雷) × V_mine_ub + P(安全) / P(雷) —— 挖掘目标格为安全格 / 为雷的概率 + V_safe_lb —— 最低档安全格 buff 当量下界 + V_mine_ub —— 敌方增益当量上界 + +条款 5(清图 vs 弃图刷新支配性): + C ≥ 弃图所节省的期望雷险当量 + Δ轮数 × 敌方结算强度当量 + C —— 清图奖励当量(B4,条款 4) + inputs —— B4 的 C、B2 惩罚上限、A1 雷密度/可推理率、B1 结算强度(B1 闭合对账) +``` + +**Variables:** + +| 变量 | 含义 | 锚定来源 | +|---|---|---| +| P(安全) / P(雷) | 挖掘目标格为安全格 / 为雷的概率 | A1 可推理率生成参数(P1 下限——决定 P(安全) 的玩家可达上界) | +| V_safe_lb | 最低档安全格 buff 当量下界 | B1 条款 1 效果档位对照表 | +| V_mine_ub | 敌方增益当量上界 | B2 惩罚上限表值(登记为 B2 GDD 验收项) | +| C | 清图奖励当量 | B4(条款 4 差异化插槽) | +| Δ轮数 | "推到底清图"与"确定格挖完即弃图刷新"的轮数差 | A1 雷密度/可推理率 × B1 结算强度 | + +| 验算项 | provisional 值 | 刷新时点 | +|---|---|---| +| 条款 2 对照基准 | 下限 1:1(单 AP 平均挖掘当量,含连锁期望放大) | B4/B1 定稿校准 | +| 条款 3 目标带 | 原型临时值(伏击 +3 / 敌 18HP/4ATK)代默认表给出首版示例 | 随 B2 表回填刷新 | +| 条款 4 清图奖励 | +10 临时锚(B4 插槽的 provisional 参考) | 量级归 B4 | +| 条款 5 支配性 | +10 锚对默认 8 AP 图形况粗验通过 | 正式锚随 B4/B2/B1 表值定稿 | + +## 状态 + +轮次内资源状态机(A3 资源侧视图;A3 只暴露资源余量、`last_round_end` 并消费 B1 信号): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| RoundStart(瞬态) | `notify_round_start()`(B1 编排;战斗首轮要求当前 Idle) | AP←8(硬重置);RAP 不动(跨轮保留)→ Active | +| Active | 预算就绪 | 接受耗 AP 动作并经 `try_spend` 原子扣减;每个动作结算事务内按优先级仲裁(cleared > stopped > ap_exhausted)→ RoundEnd(胜出 path_tag);挖掘期刷新在此相位受理(Rule 11) | +| RoundEnd(瞬态) | 任一结束路径胜出 | 停手:RAP += remaining_ap(1:1);ap_exhausted:不变式断言 remaining_ap=0,违反即致命;清图:剩余 AP 清零。写 `last_round_end` → Waiting | +| Waiting | 结算完成前不接受 round_start(AC-14) | 消费 B1 信号:`next_round_mode=same_board` → 下轮 RoundStart(同图——stopped/ap_exhausted 轮恒 same_board);`next_round_mode=new_board` → 下轮 RoundStart(新图,A1 经 B1 编排生成——仅 cleared 轮;RAP 保留);`battle_over` → Idle(RAP 清零)。request_refresh 不在此相位受理(刷新决策在挖掘期,Rule 11) | +| Idle | 战斗终结 | 不响应任何动作请求;RAP=0、ap=0;下一场战斗由 B1 发 round_start 重启(唯一出口) | + +- **不变式**:可观测相位域 = {Idle, Active, Waiting}——RoundStart 与 RoundEnd 均为单事务内瞬态,不可观测、不作为任何测试 GIVEN;ap_exhausted 轮恒 remaining_ap=0;RAP 只经停手增加、只经刷新扣减与 battle_over 清零;编排权威序列以 B1 轮次生命周期编排章节为准。 + +### 输入 × 相位矩阵 + +全部 8 个 API 返回 bool;非法输入 = 拒绝(false)+ 状态不变 + debug 诊断——fail-fast 不静默吞编排错误,但不采用进程致命(进程致命保留给 D2 数据通道)。 + +| 输入 | Idle | Active | Waiting | +|---|---|---|---| +| `notify_round_start` | 受理(RoundStart) | 拒绝 | 拒绝(settlement 消费后受理——AC-14 门) | +| `request_dig` / `request_flag_place` / `request_flag_pull` / `request_stop` | 拒绝 | 受理(经 `try_spend`) | 拒绝 | +| `request_refresh` | 拒绝 | 受理(Rule 11——挖掘期当场换图) | 拒绝 | +| `notify_settlement_done` | 拒绝 | 拒绝 | 受理(重复调用拒绝) | +| `notify_combat_end` | 拒绝 | 拒绝 | 受理(→ Idle,RAP 清零) | + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 AP=0 后仍有动作请求 | 全部拒绝(返回 false)——轮次已在归 0 瞬间结束,不存在 0-AP 窗口 | Rule 3 | +| EC2 AP 不足以支付动作(含插旗/拔旗——旗无实物资源,拒绝只看 AP) | 拒绝、状态不变;`last_round_end` 不变 | Rule 9 / AC-7 | +| EC3 玩家拔旗再插他格(挪旗) | 合法——插+拔合计耗 2 AP(插 1 + 拔 1,默认值下);相对"直接改插他格"多耗的 1 AP(拔旗)即"防免费重排"的有意代价;旗标记的移除/重建为 A2 棋盘操作,A3 只见 AP −2 | Rule 2 / AC-16 | +| EC4 同帧/同批次多结束路径竞争 | 按 Rule 3 总仲裁 cleared > stopped > ap_exhausted,与到达顺序无关、单次判定;"先停后挖"腿的批次语义在 A3 冒烟 API 上不可构造(同步单调用无事务批次概念)——该腿显式 deferral 至 A2 集成测试承载(systems-index 门禁登记,非静默豁免);A3 冒烟承载"先挖后停"腿(AC-8) | Rule 3 / AC-8 | +| EC5 连锁展开开完全图 | 连锁免费;清图判定以"安全格全部开完"为准(含连锁开的格)→ 正常触发 cleared;RoundStart 的初始 3×3 自动展开同理(退化小图属既有行为,不设独立分支);退化图刷清图奖励的钓鱼风险登记为 A1/B1 下游约束——最小安全格数下限归 A1 生成参数验收;初始展开的定义落点(免费、计入清图进度、无奖励)归 A2 GDD 承载,本 GDD 仅经 `chain_size`/`clears_map` 参数引用其结果 | Rule 2 / AC-6b | +| EC6 满 AP 停手 | 合法——RAP +8(= rap_refresh_cost,恰好可达一刷,Rule 11 基线节拍,有意;该节拍与 OQ3"纯停手轮占比"预警同源,调参时联动评估)。诚实声明(形态限定版):① 纯停手零挖掘环(停→刷→停)零战斗进账,唯一代价是敌方每轮照常结算(时间税自惩)——该退化形态结构自惩成立;② 但理性玩家的真实风险环是"挖完全部确定格 → 不确定即停 → 攒满刷新":该环有真实战斗进账、零踩雷风险,刹车只有三个——清图奖励弃失(下界由 F1 条款 5 守住)、8 RAP 成本、时间税;本条不声称该环"病灶不存在",其健康性由 F1 条款 5(支配性不等式)+ OQ3 弃图率/完成度指标守门 | Rule 4 / F1 条款 5 / OQ3 | +| EC7 同轮内插旗后又拔旗 | AP 合计消耗 2(插 1 + 拔 1,默认值下);旗标记由 A2 移除,A3 无旗状态可返 | Rule 2 | +| EC8 战斗最后一轮的收益兑现 | 敌人已全灭时 B1 结算先判胜,当轮 buff/debuff/清图奖励是否兑现 = B1 判定次序域,A3 无断言义务(out-of-scope);A3 义务仅为每轮无条件输出一条 `(path_tag, continuation_hint)` | B1 域 | +| EC9 跨轮/跨图资源携带 | RAP 是唯一跨轮携带量(战斗域,跨图也保留);停手不是战斗资源机制——无复利问题;AP 腿每轮硬重置;旗标记随图存续(A2 域);RAP 战斗结束清零(AC-11) | Rule 1 / Rule 11 / AC-11 | +| EC10 最后 1 AP 挖开最后安全格 | cleared 与 ap_exhausted 同时成立 → 清图胜出:输出 `(cleared, new_board)`、剩余 AP 清零 + 清图奖励 + 换图,ap_exhausted 不触发——防"耗尽优先"实现把清图高光结算为最差结局、训练玩家永远留最后一格停手 | Rule 3 / AC-4b | +| EC11 挖已插旗的格 | 拒绝——须先拔旗(1 AP,默认值下)再挖;旗是"别挖这里"的承诺标记,挖旗格合法化会催生零风险滥用;误标改主意的代价 = 拔旗 1 AP(P3 承诺重量);棋盘面判定归 A2 | A2 域 / AC 前言 out-of-scope | +| EC12 RAP 生命周期边界 | MVP 无上限;战斗结束(Idle)强制清零;跨战斗携带若未来开放,须修订本条与 Rule 11 并登记 D3 持久化归属 + C2 冒险域语义(OQ7)——禁止静默超域 | Rule 11 / OQ7 | +| EC13 RAP 不足时请求刷新 | `request_refresh()` 返回 false、RAP 不变、相位不变(Active 相位内的资源不足拒绝;与 AC-14 的相位非法拒绝区分——后者附 debug 诊断,两者均为 bool 口径) | Rule 11 / AC-5b | +| EC14 清图/耗尽轮次产生剩余 AP | 清图剩余 AP 清零(不入 RAP,Rule 5);ap_exhausted 恒 remaining_ap=0(不变式断言);两路径 RAP 增量恒为 0(AC-4/AC-6 断言);不变式断言的实现载体为 debug 断言(冒烟无 fault-injection 载体;行为级代理 = AC-4 的 rap 不变断言) | Rule 5 / AC-4 / AC-6 | +| EC15 同一轮内多次请求刷新 | 受理至每轮上限 `refresh_cap_per_round`(默认 2)为止,超限拒绝(resource_face;轮内计数、RoundStart 重置);每次独立检查 `rap ≥ rap_refresh_cost` 并扣减;无冷却;每次刷新都当场换新图且不产生清图奖励(Rule 11);挖掘期当场刷新无结算发生——OQ3 联动复核 | Rule 11 / AC-5b | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | hard | ActionEconomyTable schema 与 ConfigDB 类型化读取;本 GDD 为该表回填 owner(回写台账见对账义务登记表) | +| A1 | 间接事实依赖 | 新图生成经 B1 编排调用——A3 不直接消费 A1 接口,仅 Waiting 出口消费其结果信号 | + +无其他玩法上游(Foundation 层)。 + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| A2 | hard | 账本调用(`try_spend`)+ 价目表执行 + round_end 棋盘路径外发(Rule 9,payload 组包读取 `last_round_end`);A2 校验棋盘面合法性,资源合法性归 A3;旗标记棋盘状态归 A2;EC4"先停后挖"批次仲裁 deferral 至 A2 集成测试(对账义务登记表) | +| B1 | hard | RoundEnd 输出 `(path_tag, continuation_hint)`;挖掘期刷新编排(`refresh_board`)内调用 `request_refresh()`(Rule 11);回发 `next_round_mode`/`battle_over`;F1 契约验收项(效果档位对照表单一线性换算) | +| B4 | soft | 清图奖励差异化插槽(按图类型分支);标中旗 debuff 档位(OQ5/F1 条款 2 锚约束) | +| D1 | soft | AP/RAP HUD 常显;刷新入口与 RAP 清零反馈;非法原因双源呈现 | +| D5 | soft | 教学口径来源(含 RAP/刷新/RAP 战斗末清零) | + +### 对账义务登记表 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | ActionEconomyTable 首轮回填——值侧四项 + registry 登记 | D2 | 已完成 | +| 2 | schema 全字段交付(十字段,含 move/chain 冻结字段) | D2 | 已完成 | +| 3 | RAP 结构 delta——四字段删除(flags_per_round / flag_unit_cost / stop_multiplier / clear_multiplier)+ `rap_refresh_cost` 新增;D2 Core Rules 表行/Status 行、AC-7 示例值、registry 同步回写 | D2 | 已完成 | +| 4 | config 结构位 `refresh_cap_per_round` 预留;D2/registry 字段回写随实现批次(届时 AC-10a 七字段清单同步扩为八字段) | D2 | 待实现批次 | +| 5 | F1 五条款验收——效果档位对照表/标中旗锚/挖掘风险不等式/清图奖励/弃图支配性(条款 3/5 为 B1 强制检查点;systems-index 门禁 ①–⑤) | B1(供给锚值:A1/B2/B4) | 已登记 | +| 6 | EC4"先停后挖"批次仲裁腿 deferral 至 A2 集成测试(A3 冒烟 API 不可构造;A3 冒烟承载"先挖后停"腿 AC-8) | A2 | 已登记(systems-index 门禁) | +| 7 | 退化图刷清图奖励的钓鱼风险——最小安全格数下限归 A1 生成参数验收;初始展开定义落点(免费、计入清图进度、无奖励)归 A2 GDD 承载 | A1/A2/B1 | 已登记(EC5) | +| 8 | 图类型上下文在战斗域内恒定(biome 挂关卡层、不逐图重掷——防 `request_refresh` 退化为 biome 重掷钓鱼口) | A1/B4 共同验收 | 已登记(OQ6) | +| 9 | 刷新入口(玩家发起)与 battle_over 时 RAP 清零的可见反馈 | D1 | 已登记(D1 GDD 验收项) | + +## 呈现与 UI 契约 + +A3 不拥有 UI,对外暴露的状态与反馈义务: + +| # | 义务 | 归属 | +|---|---|---| +| 1 | 当前 AP 与当前 RAP 为 HUD 常显字段 | D1 | +| 2 | 刷新入口为玩家发起(Rule 11),入口可用性由 B1/D1 呈现 | B1/D1 | +| 3 | `battle_over` 时 RAP 清零须有可见反馈——防"攒的钱凭空没了"读感(对账义务登记表 #9) | D1 | +| 4 | 动作非法原因需可区分且双源——棋盘面非法(非相邻格/挖旗格等,A2 判定)/ 资源面非法(AP 不足 / RAP 不足,A3 判定),D1 呈现(原因读数的测试承载声明见验收标准前言 out-of-scope) | D1/A2 | +| 5 | 结算明细的效果项带来源标注(P2 归因) | B1 | +| 6 | 交互成本指标(每消耗 1 AP ≤ 2 次点击)归 A2/D1 观测(P4 免费移动的点击摩擦护栏) | A2/D1 | +| 7 | 无专属表现面、无音频需求;AP/RAP 余量变化与"资源不足"反馈的呈现归 D1/A2 | D1/A2 | +| 8 | AP/旗(价目)/停手/RAP 的教学口径以本 GDD 价目表为准,须含"RAP 战斗结束清零"条目(防"存的钱凭空没了"误读) | D5 | + +## Tuning Knobs + +### ActionEconomyTable schema(单行全局,int 类型,A3 回填 owner) + +| 字段 | 类型/范围 | 默认 | knob | 说明 | +|---|---|---|---|---| +| ap_per_round | int [1, 32] | 8 | ✓ | 每轮行动点(原型 v2 实测定稿;双重身份:节奏 knob 兼停手 RAP 转存规模 knob) | +| dig_ap_cost | int [1, 8] | 1 | ✓ | 挖单格成本(域下限 1:0 = 无预算系统失效,越域校验拒绝) | +| flag_place_ap_cost | int [0, 8] | 1 | ✓ | 插旗 AP 成本(旗无实物资源,仅此一项消耗) | +| flag_pull_ap_cost | int [0, 8] | 1 | ✓ | 拔旗 AP 成本(OQ2 复核落点) | +| rap_refresh_cost | int [1, 64] | 8 | ✓ | RAP 刷新门槛;默认 8 = ap_per_round,使"满 AP 一轮停手 = 恰好一刷"成为基线节拍;单字段域内任意值均可运作(仅节奏差异),跨字段约束见下 | +| move_ap_cost | int | 0 | ✗ 冻结 | 移动免费(P4 承载,调参豁免) | +| chain_ap_cost | int | 0 | ✗ 冻结 | 连锁展开免费(原型 v2 定稿) | +| refresh_cap_per_round | 结构位(int) | 2 | 结构位预留 | 每轮刷新上限(Rule 11/EC15;RoundStart 重置计数);D2/registry 字段回写随实现批次(届时 AC-10a 七字段清单同步扩为八字段) | + +- knob 列 = 是否开放调参;非 knob 字段仍入表——代码零字面量,修订须走本 GDD 修订流程。 +- **跨字段校验(D2 validate() 级)**:任一动作成本字段(`dig_ap_cost` / `flag_place_ap_cost` / `flag_pull_ap_cost`)≤ `ap_per_round`——违反即加载期致命(D2 Rule 6 fail-fast)。依据:dig=8 × ap=1 双双域内会使挖掘永久不可用、cleared/ap_exhausted 双不可达(软锁循环),属结构失效而非节奏差异,与本表"域下限 1 = 零预算系统失效"同族;归 ActionEconomyTable schema 类 `validate()` 跨字段类别(D2 既有"schema validate() 收集后致命"机制承载,无需新增 D2 校验通道);违规夹具登记为 D2 校验双通道条件性夹具(同悬空引用待遇)。 +- **域上界注记**:dig/flag 上限 8、refresh 上限 64 为 MVP 量级护栏(非推导界),随调参需要修订。 + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| ap_per_round | 8 | 决策带宽膨胀、节奏拖长(原型 v1 的 12 已实测否决);双重身份:满停 RAP 转存随之增大(8→12 使满停一刷后的富余 +4) | "想清楚的事做不完",违反 P1 | +| dig_ap_cost | 1 | ——(>1 使"连锁免费"的相对收益畸变,未实测) | 0 = 无预算系统失效(schema 域 [1,8] 拒绝) | +| flag_place_ap_cost | 1 | 插旗迟疑,P3 标雷策略萎缩 | 插旗无成本,旗标记泛滥(棋盘噪音) | +| flag_pull_ap_cost | 1 | 挪旗昂贵,误标惩罚过重 | 挪旗近免费,承诺无重量(OQ2 复核落点) | +| rap_refresh_cost | 8 | 刷新太贵,风险退出权萎缩、盲猜局被迫赌(违反 P1 兜底) | 刷新近免费,图来回刷节奏拖沓;敌方时间税仍自惩但体验烦躁 | + +- **Knob 交互**:`rap_refresh_cost : ap_per_round` 比值 = "停几轮换一刷"(默认 1:1 = 满停即一刷;该节拍与 OQ3 纯停手预警同源,联动评估);`ap_per_round × 插旗占比` 与 A1 布雷密度共同决定标雷全周期可行性(A1 定稿后强制复核);效果侧健康(F1 五条款)无单一系统 knob 可控——对照表/闭合对账归 B1、惩罚上限归 B2、奖励分支归 B4(对账归 OQ1);刷新率应急阈值(OQ3)的评估须与 B2 结算强度联动,勿独立调参。 +- **已冻结项**:AP 8 来自原型 v2 实测(12 点被否);移动/连锁 0 成本为结构冻结;RAP 1:1 转存与战斗域生命周期为结构(非 knob,修订走 GDD 流程;跨战斗携带 = OQ7 预留开关);旗数量无上限为结构。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | A3 是被动账本,挖/插/拔的棋盘面执行归 A2(设计顺序第 6 位)、轮次节拍归 B1(第 9 位)——均未实现;冒烟脚本(`--headless --script`,承 D2 冒烟框架)直接驱动 A3 最小 API 面;A2/B1 就绪后改走真实调用方,断言口径不变 | +| API 面 | 8 个 API 布尔返回 = 受理/拒绝——"拒绝"直接断言返回值(notify_* 与 request_* 同为 bool);相位非法拒绝附 debug 诊断、非进程致命;受理矩阵见状态节输入×相位表 | +| 参数语义 | `chain_size` = 连锁附加格数(不含首格,≥0;首格恒耗 1 AP);`clears_map` = 本次动作后全图安全格开完 | +| 只读观测量 | `ap` · `rap` · `phase` · `last_round_end`(记录 path_tag / continuation_hint,可空);相位观测域 = {Idle, Active, Waiting};RoundEnd 为事务内瞬态、不可观测,不作为 GIVEN | +| 口径声明 | `notify_settlement_done` 仅记录编排意图,phase 停留 Waiting(AC-11 断言);轮次开启仍由 `notify_round_start` 触发(AC-14 的门不被绕过);各 AC 的 THEN 具体数值按当前 ConfigDB 表值推导(默认 8/1/1/1/8 下的具体数见各 AC;测试读取表值计算期望,不写死) | +| 夹具一致性 | 冒烟经 `chain_size`/`clears_map` 参数注入棋盘事实(信任方向见依赖 A2 行)——各 AC 使用独立夹具图,`clears_map=false` 腿的安全格余量在夹具中显式登记(防"传 false 却实际清图"的夹具失配假失败);多步 GIVEN 均给出构造序列 | +| 覆盖声明(out-of-scope) | EC11 与挖/插/拔相邻性、旗标记棋盘状态与跨轮留旗(A2 棋盘面域,A2 GDD 承载 AC,systems-index 门禁登记);EC8 末轮判定次序(B1 域);移动/连锁 0 成本不经账本(A2 侧承载);刷新的换图生成与奖励内容(B1/A1/B4 域);资源面拒绝原因的读数区分(UI 要求"可区分",但冒烟 API 无原因观测量——bool + 构造上下文可推断,呈现归 D1/A2 集成测试);跨字段校验违规(`validate()` 级,归 D2 校验双通道条件性夹具) | +| last_round_end 单槽 | 同值双发射不可区分(跨值覆盖由 AC-8/AC-9 值断言承担,同值双发防御归 A2 集成) | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1a | phase=Idle(冷启动,rap=0) | `notify_round_start()` | 返回后 ap=8、rap=0、phase=Active | +| AC-1b | 上轮以 (stopped) 结束且 rap=3、`notify_settlement_done(same_board)` 已消费 | `notify_round_start()` | ap=8(硬重置)、rap=3 不变(RAP 跨轮保留)、phase=Active | +| AC-2 | Active ap=8 | `request_dig(chain_size, false)` 以 chain_size ∈ {0, 1, 5} 各在新轮次执行一次 | 每次返回 true 且扣减恒为 1(连锁免费:chain_size=5 时 ap 8→7 而非 8→2) | +| AC-3a | ap=8 | `request_flag_place()` | true、ap=7(旗标记为 A2 域,A3 无旗观测量) | +| AC-3b | ap≥1(AC-3a 产物 ap=7) | `request_flag_pull()` | true、ap=6 | +| AC-16 | AC-3a 产物(已插 1 旗、ap=7,旗构造经 `request_flag_place()`——A3 冒烟无棋盘观测量) | `request_flag_pull()` 后 `request_flag_place()` | 两调用均 true、AP 合计 −2(挪旗成本,EC3)、轮次不结束 | +| AC-4 | ap=1(构造:新轮 7 次 `request_dig(0,false)`)且 clears_map=false(前置:该格非最后安全格) | `request_dig(0, false)` | true、轮次立即结束、`last_round_end=(ap_exhausted, same_board)`、rap 不变(+0,EC14) | +| AC-4b | ap=1 且仅剩 1 个安全格(构造同上,digs 作用于预设夹具图) | `request_dig(0, true)` | true、`last_round_end` 值等于 `(cleared, new_board)`(清图胜出,EC10)、rap 不变;不存在第二条输出 | +| AC-5 | Active ap=3 且图未开完(构造:5 次非清图挖,8−5=3) | `request_stop()`;随后 `notify_settlement_done(same_board)` + `notify_round_start()` | true、`last_round_end=(stopped, same_board)`、rap=3(1:1 转存,恰一条输出);新轮 ap=8、rap=3 | +| AC-5b | 分支 1:Active 且 rap ≥ rap_refresh_cost(构造:满 AP 停手 → rap=8 → `notify_round_start()` 回 Active——RAP 跨轮保留);分支 2:Active 且 rap=3(构造:前轮挖 5 后 `request_stop()` → rap=3 → `notify_round_start()`) | `request_refresh()`(两分支各一次) | 分支 1:true、rap=0、phase 不变(Active——挖掘期当场换图,A2 装板受理联动);分支 2:false、rap=3、phase 不变(EC13) | +| AC-6 | ap=5(构造:3 次非清图挖) | `request_dig(0, true)`;随后 `notify_settlement_done(new_board)` + `notify_round_start()` | true、`last_round_end=(cleared, new_board)`、rap 不变(剩余 AP 清零,Rule 5/EC14);新轮 ap=8、rap 仍= 前值(RAP 跨图保留,EC9 的 cleared 腿) | +| AC-6b | 冷启动 ap=8、夹具图恰余 4 个安全格 | `request_dig(3, true)`(附加 3 格连锁开完全部余量;chain_size 不含首格) | true、ap=7、`last_round_end` 值等于 `(cleared, new_board)`、rap 不变——连锁免费且连锁格计入清图判定(EC5),单次判定 | +| AC-8 | Active、剩余安全格恰 1、ap≥1(先挖后停腿——A3 冒烟可测子集) | `request_dig(0, true)` 后 `request_stop()` | 第二调用返回 false、`last_round_end=(cleared, new_board)` 恰一条、phase=Waiting(EC4;stop-first 腿 deferral 至 A2 集成,门禁登记) | +| AC-15 | Active ap=8 且图未开完 | `request_stop()` | true、`last_round_end=(stopped, same_board)`、rap=8(满停 = 刷新门槛恰可达,Rule 11 基线节拍,EC6) | +| AC-7 | ap=1(构造:7 次非清图挖)且 debug override `flag_place_ap_cost=2`、`flag_pull_ap_cost=2`(域 [0,8] 内,经 D2 R3 注入);承载声明:默认价目下全动作 1 AP,"AP 不足"腿(EC2)在默认表值下不可构造(ap≥1 恒可支付、ap=0 即收轮),故本 AC 显式承载于非默认表值夹具 | 依次 `request_flag_place()`、`request_flag_pull()`、`request_dig(0,false)` | 前两者 false(AP 不足,EC2)、最后者 true(ap 1→0,触发 ap_exhausted);`last_round_end` 断言只含末次记录 | +| AC-9 | phase=Waiting(经 ap_exhausted 路径构造;RoundEnd 瞬态不可观测) | 依次 `request_dig(0,false)`、`request_flag_place()`、`request_flag_pull()`、`request_stop()` | 全部 false、ap/rap 不变、`last_round_end` 不变(无第二条输出) | +| AC-11 | phase=Waiting 且 rap>0(构造:停手轮后) | `notify_settlement_done(same_board)`;随后 `notify_combat_end()` | settlement_done 返回 true 且 phase 仍= Waiting(仅记录编排意图,AC-14 门不被绕过);combat_end 返回 true、phase=Idle、rap=0(战斗域清零,EC12)、ap=0 | +| AC-12 | phase=Idle | 任何动作/停手/刷新请求 | 全部 false、`last_round_end` 保持 null、phase 不变 | +| AC-13 | phase=Idle | `notify_round_start()`(新战斗首轮) | 返回 true、phase=Active、ap=8(Idle 唯一出口;RoundStart 瞬态不作断言落点) | +| AC-14 | phase=Waiting 且结算未完成(未消费 next_round_mode) | `notify_round_start()` | 返回 false、phase 停留 Waiting、`last_round_end` 不变(fail-fast = 拒绝 + debug 诊断,不静默吞编排错误;统一 bool 口径,非进程致命) | +| AC-18 | phase=Active(冷启动后、未挖任何格) | 依次 `notify_round_start()`、`notify_settlement_done(same_board)`、`notify_combat_end()` | 全部返回 false、ap/rap/phase/`last_round_end` 均不变(输入×相位矩阵 Active 列全覆盖;防 B1 双启动静默 AP←8 重置);`request_refresh` 于 Active 受理(见 AC-5b) | +| AC-17 | 全新 A3 实例(同一 ConfigDB 表;每遍以新实例构造,无跨实例状态) | 同一动作序列驱动两遍 | 两遍 `last_round_end` 序列与 rap 轨迹逐项相同(零随机的行为级证据,D2 AC-15 的 A3 层前身) | +| AC-10a | ConfigDB 加载完成 | 脚本解析 `design/registry/entities.yaml` 的 `action_economy` 条目 | 与 `ConfigDB.action_economy` 七字段(ap_per_round / dig_ap_cost / flag_place_ap_cost / flag_pull_ap_cost / rap_refresh_cost / move_ap_cost / chain_ap_cost)逐一相等;registry 缺任一字段即失败 | +| AC-10b | A3 文件集 | 纳入 D2 AC-14 项目级扫描域 | 引用其人工三要素口径,不另设自动化字面量硬 THEN——字面量判定误报率高,D2 已定性为 warning 级 + 人工复核 | + +## 开放悬案 + +| # | 悬案 | owner | 要点 | +|---|---|---|---| +| 1 | 效果档位对照表与闭合对账 | B1(第 9 位) | 安全格 buff / 标中旗 debuff / 清图奖励 / 敌方增益的统一当量对照与换算由 B1 GDD 定稿,须满足 F1 五条款;B1 兼任条款 3(挖掘风险不等式)与条款 5(弃图支配性)的单一闭合对账 owner(A1/B2/B4 供给锚值,B1 签字验收),两项均登记 B1 GDD 强制检查点;每战标中旗总吞吐对账归本条;注意与 OQ6 清图奖励插槽区分 | +| 2 | 拔旗耗 AP 复核 + 标错惩罚双重职责 | A2 前原型小补丁(与标错惩罚实测合并会话) | 本 GDD 按"拔旗 1 AP、移除标记"定稿;复核改动现已有 knob 落点(`flag_pull_ap_cost`),若实测推翻则调 knob 并回写 registry。标错惩罚现承担双重职责:误标税 + 停手轮/死 RAP 态下旗滥用的唯一价格——RAP 无上限 + 满溢态使碎片区插旗机会成本趋零,"可疑格全插旗、永不拔"的滑向由本惩罚定价;OQ3"停手轮旗动作占比"指标联动观测 | +| 3 | playtest 预注册指标 | 垂直切片前置 playtest | 指标:弃图率 = refresh/(clear+refresh)(>30% 观察、>50% 干预——provisional 预注册);刷新时已开格完成度直方图(完成度 <60% 的刷新占比 >50% 即信号);停手轮旗动作占比(>50% 即信号——投机旗/死 RAP 态检测,联动 OQ2);确定动作供给 >8 的轮占比(P1 决策密度兜底监测,与 A1 密度联合验收);刷新率/战斗(>3 次即观察信号,阈值评估与 B2 结算强度联动,勿独立调参);纯停手轮占比(>20–30% 即观察信号);停手时机分布、每轮犹豫时长、终局碎片区(清图完成度 >80%)轮次数。失明声明:刷新率/纯停手占比/碎片区轮次三项对"挖完全部确定格→停→刷"环结构性失明——该环停手轮先挖后停(非纯停手)、温和变体刷新率低、开到 >80% 即弃使碎片区读数反而变低(病态呈现为健康);故增弃图率/完成度直方图/停手轮旗占比前三项直测该环。应急方案(触发后启动):`rap_refresh_cost` 调参 / 刷新冷却(连续刷新须间隔 N 轮)/ 刷新新图保留已开格结构(重议级) | +| 4 | AP 耗尽的软提示 | D5(低优先) | 本 GDD 定"AP=0 即收轮";教学若发现新手困惑,加提示不改规则;可扩展覆盖"RAP 达标刷新引导"与"RAP 战斗末清零提示"(D1 清零反馈的对应教学条目) | +| 5 | 标中旗兑付语义细化 | B4/B1(A3 硬约束已定稿) | 标中旗 = 怪物 debuff(三向效果定稿);硬约束:兑付一次性、当轮结算、兑付资格以雷格实例为键(每雷本次战斗至多兑付一次,资格随首次兑付消费;放置实例仅为当轮结算载体)、拔旗重插同一雷格不恢复已消费资格(堵重插农场)、禁止逐轮重复兑付;换图后新雷为新实例;档位锚见 F1 条款 2(严格 > 最低档 + 连锁放大边际基准 + 总吞吐对账);标错惩罚归 game-concept 待定问题(OQ2 合并会话,双重职责见 OQ2) | +| 6 | 清图奖励差异化插槽 | B4(结构位预留 + A1 上下文) | 清图奖励未来按图类型(biome/tag)分支——B4 奖励表 schema 须预留图类型维度的结构位;A3 只上报 `cleared`,图上下文经 B1 传递、不经 A3;约束:图类型上下文在战斗域内恒定(biome 挂关卡层、不逐图重掷)——否则 `request_refresh` 退化为 biome 重掷钓鱼口;该约束登记 A1/B4 共同验收 | +| 7 | RAP 跨战斗生命周期开关 | 垂直切片 knob 复核 | MVP 每场战斗清零;若实测支持跨战斗存蓄(Boss 图连刷策略),须修订 Rule 11/EC12 + 登记 D3 持久化归属与 C2 冒险域语义 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/边界情况/AC/义务台账/OQ 转表格,流程转字符画;F1 五条款、公式与验算数据全保留;语义与 2026-09-07 三轮评审修订版及 2026-09-12 D4 修订对齐,零规则变更 | +| D4 修订 | 2026-09-12 | 刷新决策前移挖掘期:`request_refresh` 由 B1 `refresh_board` 挖掘期刷新编排内调用(原结算编排期 Waiting 相位代理调用退役);新增每轮刷新上限 `refresh_cap_per_round` 默认 2(RoundStart 重置、超限拒绝,EC15);旧板旗随旧板作废(零兑付、旗 AP 不返还,A2 换图全量重置承载);stopped/ap_exhausted 轮恒 same_board 钉入 Waiting 出口契约 | +| 三轮评审修订版 | 2026-09-07 | 三轮 full review(game-designer · systems-designer · qa-lead · economy-designer · creative-director)8 条文本级 blocking 同日全部核销,用户拍板接受修订并标记 Approved、免独立 delta 核销(不设第四轮全量评审):RAP 池新增(停手剩余 AP 1:1 转存,原 ×2 折算与 stop_multiplier 删除;战斗域生命周期、battle_over 清零);清图剩余 AP 作废(原 ×1 折算与 clear_multiplier 删除);F1 结构性改写为五条款并钉死 B1 闭合 owner(原 F1 折算公式与 F2 挖/停/清激励不等式整体作废——F2 被二轮评审击穿两条数学缺陷:博弈树错误、缺风险项,不再修复而是移除病灶);旗子单态(口袋旗双态删除,数量无上限,稀缺性由 AP 机会成本承载);round_end 双发射源(stopped 由 A3 直发);兑付资格以雷格实例为键(堵重插农场);ActionEconomyTable 四字段删除 + rap_refresh_cost 新增(同日三次回填);跨字段校验(动作成本 ≤ ap_per_round);输入×相位矩阵增补;评审日志 design/gdd/reviews/a3-action-economy-review-log.md | +| v1(初稿定稿) | 2026-09-07 前 | 数值继承原型 v2/v3 实测结论与 game-concept 定稿:AP=8 定稿(12 点否决);连锁免费(原型 v2 定稿);统一行动经济(原型 v3:挖/插/拔均限相邻、拔旗 1 AP 条文读法确认);三向效果与清图奖励结构落位 | diff --git a/design/gdd/legacy-2026-09-14-v1/a4-terrain-variation.md b/design/gdd/legacy-2026-09-14-v1/a4-terrain-variation.md new file mode 100644 index 00000000..f60ba421 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/a4-terrain-variation.md @@ -0,0 +1,517 @@ +# A4 地形变异(Terrain Variation) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents(lean 门禁:systems-designer 审查 Formulas · qa-lead 审查 Acceptance Criteria) +> **承载支柱**:P1 主(信息诚实边界——"不可推理"永不伪装成"可推理")· P3(信息维度变体——定价通货从雷险换信息+AP;迷雾仅其信息切面)· P2(暗面警示——雾区奖励可预期性切面)· P4(地牢氛围本体承载) +> **层级**:Layer 1 Foundation · Milestone MVP · 设计顺序第 3 位(上游 D2、A3 已 Approved) +> **一句话**:A4 定义扫雷图上的地形变异——基础规则之上的区域性规则偏离;MVP 唯一变异为迷雾(边界可见、内容隐蔽),并以变异框架承载岩浆/冰冻等后续变异逐个加入。 +> **复核注记**:art-director 未经咨询(Lean 模式)——生产前人工复核视觉规格与 art-bible 一致性(creative-director 已在两轮独立评审中终裁幻想节处置)。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 变异的定义与交互规则 owner——在基础扫雷规则上叠加区域性规则偏离的框架 + MVP 迷雾;玩家不"使用"A4,而是在挖掘中遭遇并应对它 | +| 拥有 | VariationTable 8 字段定义(D2 回填 owner)· F1 候选池门控谓词 · 信息剥夺边界与双向效应 · 生成期布置约束声明(执行归 A1)· 零特判原则的规则面(义务是"不加分支")· 未来变异扩展框架 | +| 不拥有 | 布置算法与区域参数(A1 BoardGenTable)· 挖/旗执行(A2)· 呈现(D1)· 教学文案(D5)· 奖励内容与定价(B4/B1)· 道具(A5)· 全局变异语义(C3)· 玩法数值字面量与随机 | +| 上游输入 | D2 VariationTable schema 承载 + ConfigDB 读取(hard——唯一玩法上游);A3 价目 `ap_per_round` / `dig_ap_cost` 文档层引用(零运行时接口消费) | +| 下游输出 | 候选池门控 + cell_constraint + 出生区排除(A1)· 变体属性只读查询契约(A2)· 呈现规格(D1)· 教学口径(D5)· 定价硬约束与条款 5 对账输入(B4/B1)· counter_item_ids 预留(A5)· scope=global 枚举位(C3) | +| 硬约束 | 生成期布置约束(雾区仅非 0 安全格——区内无雷、无 0 格)· 零特判原则 · fog unlock_level=2 · hide_numbers⇒safe_nonzero 跨字段校验致命 · 厚度 ≤2 / f ≤ 8×雷密度 硬边界 | + +A4 是定义层系统:变异由棋盘生成(A1)布置在图上,玩家在挖掘中遭遇并应对——看见迷雾边界那一刻决策就出现:如何安排挖掘顺序让 0 格连锁免费吃掉雾格(连锁路由),链不可达的雾残段按全价续付清图还是停手/弃图(A3 风险退出权衡;垂直切片起再加"是否用道具买情报",A5)。清图判定含雾格——雾是清图的必经段而非可绕过的支线,"绕开走"的真实形态是 A3 的停手/弃图风险退出。 + +没有 A4,游戏失去两样东西:难度只能靠雷数单调缩放、关卡间"解锁新变异"的推进感(P4 越下越险)消失;且"无法推理"的格子会以埋伏形式随机出现——迷雾把"你推不出来"从暗处的埋伏变成明处的选择,是 P3"风险与收益同源"在空间维度的载体,也是对 P1"我算对了"的诚实兜底:推不出的区域被显式标注、可绕行、可买情报,而不是让玩家在自以为可推理的局面上撞墙。 + +## 体验锚点 + +> 情绪目标一句话:**站在洞口前的权衡感**——迷雾不制造惊喜或惊吓,它制造一个看得见边界的承诺:"里面有价值,进去的代价是 AP 溢价与放弃雾格数字这份推理原料。"(玩家每轮都在看见并应对变异——直接感知) + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 我算对了的诚实边界 | "数字不再说话" | 迷雾是游戏对玩家的坦白——把"无法推理"从埋伏变成路标:雾外仍可用区外数字推理雾边缘的雷(跨雾推理——被雾边已知约束放大的常规推理体验,"隔着雾也算出来了",是常规推理在雾边的延伸、非独立机制);进雾每一步是承诺而非计算;基础区可推理率生成保证归 A1,A4 负责"不可推理"永不伪装成"可推理";"里面有价值"是契约而非愿望——由 B4 三条定价硬约束兜底(义务清单 B4-①②③) | +| P3 风险与收益同源(信息切面) | "不赌命,赌信息与行动力" | 雾区无雷是玩家可见知识——雾的"险"在信息与行动力的双重定价:盲区预算按链不可达雾格全价收紧,链可达雾格经邻接 0 格连锁免费开出(两态口径);变异框架是 P3 的长期载体,迷雾仅其信息切面,P3 雷险维度由岩浆/冰冻(垂直切片起)承接;真实耦合点在雾边(Rule 5 双向效应——A1 按双向净效应建模) | +| P2 规划的延迟兑现(暗面警示) | "边界可见、内容隐蔽" | 雾内奖励若不可见,规划退化成探索惊喜(创意风险 #1,CD 门禁登记 B4/A4 落实)——Core Rules 定迷雾信息呈现规则的精确切面,奖励可见性/探测机制的接口在此预留、内容归 B4/A5 | +| P4 地牢氛围的本体承载 | "趴在地图上的黑暗" | 迷雾是"下探黑暗"隐喻的直接具象——未探索的黑暗是有形状、有边界、趴在地图上的实体;后续变异(岩浆/冰冻)延续"越深越险的地貌"叙事(随关卡解锁) | + +反向护栏(裁剪判据):迷雾绝不能制造"被骗感"——若雾区边界不可见、或进洞无额外价值,迷雾退化为纯负体验税(纯 AP 税);任何让雾区"隐性化"的设计默认砍掉。"里面有价值"由 B4 三条定价硬约束兜底:两条防税下界 + 一条防福利上界——防 level 2 首发关(小图低密、链免开占比结构性最高)"链免开 + 锚上浮"双白拿的福利板化与"雾=免费奖励"先验(后者会在后期雾变厚、全价残段出现时恰触发被骗感护栏)。 + +- 参考感受:Into the Breach 的信息诚实(游戏从不用隐藏信息骗你——本作反其道而行,把"信息缺失本身"诚实标注)· 杀戮尖塔地图的路径承诺(看得见的未知,选定的风险)。 + +## 全局流程 + +### 地形布置流程(A1 生成期——A4 只声明约束与门控,算法归 A1) + +``` +A1 生成新扫雷图(雷图 · 尺寸 · biome,(level_index, node_depth) 维度) + │ ① 求布置候选池 pool = F1(level_index, board_biome) + │ pool = ∅(雾未解锁 / biome_tag 全不匹配)→ 静默布置普通图,不报错(EC1) + │ ② 池非空 → 逐候选按 cell_constraint=safe_nonzero 求可行域 + │ 非可行(非 0 安全格面积或连通分量不足)→ 跳过雾布置,本图为合法无变异图(EC2 降级) + │ 实际达成区数 k < 参数要求 N → 尽力布置可行分量,按 shortfall 分级上报降级率(禁放宽 Rule 3 硬塞) + │ ③ 可行域内布置雾区,硬边界逐项成立: + │ · 雾格 ∈ 非 0 安全格(区内无雷、无 0 格) + │ · 厚度 ≤2(≡ 任意 3×3 窗口非全雾)· f ≤ 8×雷密度 · 雾占比 < 全覆盖(EC10) + │ · 出生 3×3 及其初始展开可达域内无变异格(保守上界 = 出生 3×3 经 0 格最大 + │ flood 闭包外扩 1 格——任意 A2 展开定义的超集,Rule 7) + │ · 区域随机选取经 board_gen 流取号(回放确定性) + │ ④ 生成期计算并输出链不可达雾格占比(B1 条款 5 对账输入,验收⑤) + ▼ +棋盘产物(格状态 + 变异标记 + 出生区)→ A2 install_board 持有生成期快照 +``` + +### 雾揭示流程(运行期——零特判,A2 执行逻辑无变异分支) + +``` +玩家挖格 c(全价 dig_ap_cost)或 0 格连锁传播至 c(连锁展开零价) + │ A2 只读查询格变体属性(生成期快照) + ▼ +c 是雾格? + ├─ 否 → 正常开格:显示数字(8 邻雷数恒真,值不因雾改变) + └─ 是 → 开出、不显示数字(未开时专属纹理已标出区域边界) + · 来源 = 连锁 → 免费(链可达雾格——EC4;雾格恒非 0,级联止于雾格) + · 来源 = 单格挖掘 → 全价 dig_ap_cost(链不可达雾格) + · 计入清图进度 · 安全格奖励照常入库 · 雾内插旗合法但轮末判定恒不中 +``` + +### 出口分发 + +| 出口 | 触发 | 行为 | 依据 | +|---|---|---|---| +| 普通图(无变异) | 候选池为空(未解锁 / biome_tag 全不匹配) | 静默布置、不报错、无 warning——两种来源同待遇 | EC1 / F1 Output Range | +| 无雾图(降级产物) | cell_constraint 无解(面积或连通分量不足) | 跳过雾布置,图仍合法;shortfall 分级上报降级率 | EC2 | +| 合法雾图 | 可行域内布置成功 | 产物携带变异标记;占比参数域校验通过;输出链不可达占比 | Rule 3 / Rule 6 | +| 参数域拒绝 | 雾占比 ≥ 全覆盖(极端参数) | BoardGenTable 参数校验拒绝(雾占比上限 × 可推理率下限联动不等式) | EC10 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:每种变异都是配置表里的一行声明,不含任何专属代码 +**人话**:这条管变异怎么被定义。每种变异是配置表 VariationTable 里的一行具名声明,一共声明四件事——见下表。框架自身不含运行时逻辑:变异带来的行为改变,发生在"信息规则 × 既有系统行为"的交集里,定义行只声明交集参数,不写任何处理代码。 + +| 声明项 | 字段 | 回答的问题 | +|---|---|---| +| 身份 | variant_id / display_name / unlock_level / biome_tag | 叫什么、第几关解锁、在哪些地形主题出现 | +| 信息规则 | info_mask | 隐藏玩家的什么信息 | +| 布置约束 | cell_constraint | 允许落在什么样的格子上 | +| 作用域 | scope | 管一块区域(region),还是全局(global——为后续预留) | + +**举证**:无计算(定义框架)。字段 schema 定稿与默认值见「Tuning Knobs·VariationTable schema 定稿」表(本 GDD 回填 owner,承载归 D2)。 + +#### 规则 2:迷雾边界看得见,里面的数字看不见 +**人话**:这条管 MVP 唯一的变异——迷雾。它是作用域为 region 的信息剥夺变异,玩家在挖掘中遭遇它时,行为规则只有两条: + +| 情况 | 玩家看到什么 | +|---|---| +| 雾格还没开 | 专属纹理标出雾区范围——边界可见、内容隐蔽 | +| 雾格被开出(亲手挖或连锁带到) | 照常开出,但不显示数字 | + +**举证**:无计算(两条行为规则)。呈现规格(纹理、边界可读性、已开雾格样式)归 D1,见「呈现与 UI 契约」节与依赖节 D1 行。 + +#### 规则 3:雾只铺在贴着雷的安全格上——雾里永远没雷、没 0 格 +**人话**:这条管棋盘生成时雾往哪儿摆,是本 GDD 的承重规则(用户定稿)。生成器布置雾区只能选"非 0 安全格"——8 邻里至少含 1 雷的安全格,于是雾区里既无雷、也无 0 格。约束由本 GDD 声明、棋盘生成器在生成期强制执行,谁都不许为布置成功率放宽它。玩家侧的承诺是明牌:"雾区无雷"由教学与图例传达。正因雾里没雷没 0 格,既有规则才能对雾全面惰性化——下一条讲的就是这个。 + +**举证**:计算见「数值结论与推导·结构推论」——厚度 ≤2(可判定谓词 ≡ 任意 3×3 窗口非全雾)与面积必要上界 f ≤ 8×雷密度的推导与验算都在该节;约束逐项落点见下方实现参照表;执行义务登记 A1 验收⑫;数据载体 = cell_constraint 字段;"雾区无雷"等可教知识清单见「规则 3/5 的实现参照——雾元知识信息源」。 + +#### 规则 4:既有系统不为雾改任何行为,代码里零变异分支 +**人话**:这条管雾与游戏其余部分的关系,同为承重规则。连锁、数字计数、清图判定、插旗、行动价目、结算——既有系统一律不感知"这格是不是雾格":雾不改变它们任何行为,代码里也不许出现"如果是雾格就……"的分支。雾格能被如此"无视",靠的正是上一条"雾里没雷没 0 格"的布置约束。六个系统面在雾下的具体表现见下方实现参照表。 + +**举证**:无计算(架构纪律)。挖掘侧义务 = 执行逻辑零变异分支("不加分支"而非"加处理",见依赖节下游 A2 行);A4 文件集零随机、零字面量走静态扫描(AC-7/AC-10)。 + +#### 规则 5:雾只藏雾格的数字,藏的同时倒送两样补偿 +**人话**:这条管信息剥夺的边界——"雾到底藏了什么"的账目。藏的只有一样:未开雾格的数字;不藏的有三样,见下表。雾外数字对雾外雷的计数照常,雾边界推理仍可行、只是原料变少。剥夺之外并存增益,净效应双向、随雾形与雷密度变化——建模必须两边都算,不许只算一边: + +| 信息 | 雾下状态 | +|---|---| +| 未开雾格的数字 | 藏——唯一被剥夺的 | +| 雾区边界 | 不藏——专属纹理可见 | +| 已开格的全部信息 | 不藏 | +| 奖励入库过程 | 不藏 | + +| 效应方向 | 内容 | +|---|---| +| 剥夺(减项) | 雾格数字本可作区外雷的推理源,其寂静使雾边附近的线索变少 | +| 增益(加项) | 雾格保证无雷且必邻区外雷;雾形携带雷簇位置(k 格雾区外环必含 ≥⌈k/8⌉ 雷);雾边邻格雷候选域收缩、局部更易解——"雾边 = 推理锚" | + +**举证**:净效应双向且依赖雾形/雷密度——A1 可推理率参数须按生成后实测净效应建模、禁单向补偿(验收④);外环雷下界 ⌈k/8⌉ 的推导见「数值结论与推导·结构推论」;玩家可推理、可教的雾知识清单见「规则 3/5 的实现参照——雾元知识信息源」。 + +#### 规则 6:雾摆多大摆哪儿归棋盘生成器,A4 只管入场资格 +**人话**:这条管雾区摆放的决定权。A4 不拥有布置算法:摆几个雾区、每个多大、摆在哪,与雷图共生,由 A1 生成算法 + BoardGenTable 参数(按关卡×深度两维逐行,A1 回填)决定;布置随机从棋盘生成专用随机流(board_gen 流)取号,每次生成新扫雷图绑定一次,同种子必出同图。A4 对摆放的唯一参与是资格把关:变异的解锁关号不高于当前关卡序号,才进入这一关的布置候选池。 + +**举证**:门控的计算形式是集合谓词 F1(unlock_level 直接整数比较 + biome_tag 通配/交集),见「数值结论与推导·F1 布置候选门控」;取号机制 = RngService scope 绑定(每次生成新扫雷图);区域数量/尺寸/占比参数域见「枚举·板形参数」表。 + +#### 规则 7:出生区及其连锁可达范围永不长雾 +**人话**:这条管玩家的落点安全。开局站立的 3×3 出生区,连同从它出发顺 0 格连锁能自动展开到的范围,永不含任何变异——生成器布置时硬性排除。可达范围由生成期确定性算出(不依赖挖掘系统运行期的展开行为定义),取保守上界:从出生 3×3 经 0 格连锁能扩散到的最大范围(flood 闭包)再外扩 1 格,比挖掘系统实际能展开的范围只大不小。防的是"开局即瞎"与"开局预开雾格";叠加"初始展开无奖励"(定稿口径),初始展开触及雾格在构造上不可能——双保险。 + +**举证**:无计算(几何排除)。可达域保守上界构造见 A1 GDD;回验项(挖掘侧实际展开域 ⊆ 生成侧排除域——"构造上不可能"仅在超集关系下成立)登记 A2 GDD 门禁;"初始展开无奖励优先于连锁入库语义"保留(EC4);出生区排除登记验收③。 + +#### 规则 8:A4 的代码不写死数字、不掷随机 +**人话**:这条管数据纪律。变异的全部定义数据放配置表 VariationTable——本 GDD 是该表回填 owner,字段 schema 见 Tuning Knobs 节。A4 相关代码零玩法数值字面量、零随机:布置随机落在棋盘生成器的专用随机流里,A4 自己既不掷随机数也不写死玩法数字,一切读表。 + +**举证**:无计算(数据纪律)。schema 定稿与 knob 划分见「Tuning Knobs」节;机器验收 = 静态扫描零命中(AC-7)与字面量人工口径(AC-10)。 + +#### 规则 9:新变异将来按同一框架加行,不另起炉灶 +**人话**:这条管变异框架怎么长出新居民。岩浆、冰冻在 MVP 不建行(CD 门禁:无 MVP 容器),随垂直切片关卡解锁建行;将来某变异需要新规则字段时,走 D2 接口漂移规则同步回写本 GDD 与 D2,不在表外私加字段。精英/Boss 图的全局变异只预留一个"全局"作用域枚举位(scope=global),语义归 C3 GDD 定稿——本 GDD 只保证枚举位存在,不预写语义。 + +**举证**:无计算(扩展纪律)。MVP 行集内容事实(恰含 fog 一行)见「枚举·VariationTable 行集」;global 行出现时的 warning 校验见「枚举·VariationTable schema 校验类别」表;接口漂移规则归 D2。 + +### 规则 3 的实现参照——生成期布置约束(几何硬边界) + +| 约束项 | 内容 | 执行 / 落点 | +|---|---|---| +| 雾区格类 | 仅非 0 安全格——区内无雷、无 0 格(非 0 安全格 = 8 邻域含 ≥1 雷) | A1 生成期强制(A4 声明 cell_constraint 数据载体) | +| 玩家可见知识 | "雾区无雷" | D5 教学必授 + D1 图例条目(MVP 验收两条) | +| 几何硬边界 | 每个雾格必有非雾雷邻居 ⇒ 雾区任意处厚度 ≤2——可判定谓词 ≡ 任意 3×3 窗口非全雾(3×3 实心雾块中心必为 0 格,不可能成雾) | 登记 A1 验收⑫;推导与验算见数值节 | +| 面积必要上界 | 雾面积占比存在依赖雷密度的必要上界 f ≤ 8×雷密度——雾区恒为雷集切比雪夫 1-膨胀出的边界带 | 同上 | +| 参数域 | "区域数量/尺寸/占比"参数(A1 BoardGenTable 域)必须在此边界内定义 | OQ1 联动;验收⑫⑩ | +| 正常形态 | 1~2 格宽雾带是雾的正常形态 | 视觉规格须按最坏宽度验收(AC-9) | +| 可推导元知识 | "每个雾格必邻区外雷"由规则可推出 | 按 P1 诚实原则(可推导即可教)纳入教学候选,口径归 D5 裁定 | + +### 规则 4 的实现参照——零特判六面 + +| 系统行为 | 雾下的行为 | 性质 | +|---|---|---| +| 连锁展开 | 触发与传播只看真实数字;雾格恒非 0 → 雾格自身永不传播连锁(不开出新级联);但雾格可被邻接 0 格的连锁免费开出(EC4——"不传播"≠"不被开") | flood fill 代码零变异分支 | +| 数字计数 | 数字 = 周围 8 格雷数恒真;雷全在雾外,任何数字的值不因雾存在而改变 | 恒真定理 | +| 清图判定 | 雾格照常计入"安全格全开"判定——雾是清图必经段 | 恒等 | +| 旗 | 雾内插旗合法(价目照常)但恒标不中(无雷) | 合法、无害、无意义 | +| 行动价目 | 挖/旗/移动成本雾内外一致 | A3"地形成本修饰"钩子本 GDD 搁置——启用须回 A3 重议 per-cell 成本接口 | +| 结算 | 雾格安全格奖励照常入库与结算 | B1/B4 无需感知雾 | + +### 规则 3/5 的实现参照——雾元知识信息源(玩家可推理、可教的雾知识清单) + +| 元知识 | 信息源 | 消费方 | +|---|---|---| +| 雾区无雷 | 生成期布置约束(Rule 3)——生成保证而非运行期判定 | D5 教学必授 + D1 图例(MVP 验收两条) | +| 每雾格必邻区外雷 | Rule 3 结构推论(可推导即可教——P1 诚实原则) | D5 教学候选(口径归 D5 裁定) | +| 雾格恒非 0 · 级联止于雾界 | Rule 3 寄生结论 + hide_numbers⇒safe_nonzero 跨字段校验 | D5 教学(防"连锁莫名断了"的被骗感——D1 必含要点) | +| 雾边候选域收缩 · 局部更易解 | Rule 5 增益侧(外环雷下界 ⌈k/8⌉) | A1 可推理率建模(双向净效应) | +| 雾格成本两态(链可达免费 / 链不可达全价) | 数值节预算注记 | A3/B1 对账;D5 玩家侧保守启发式教学("雾格按全价做预算,连锁白开当惊喜"——链可达性取决于未开 0 格的隐藏拓扑,玩家决策时点不可求值,明教两态反生免费期待、恰触发反向护栏被骗感) | +| 雾内旗恒空 | Rule 4 旗面(雾区无雷) | D5 教学警示"雾内插旗纯损" | + +## 枚举 + +### VariationTable 行集(MVP 表内容——恰含 fog 一行,AC-1 内容事实断言) + +| 行 | 字段取值 | 产出点 | 语义 | +|---|---|---|---| +| "fog"(迷雾) | unlock_level=2 · scope=region · info_mask=hide_numbers · cell_constraint=safe_nonzero · biome_tag=[] · counter_item_ids=[] | game-concept 解锁序列:普通数字→迷雾→岩浆/冰冻;本 GDD 回填 D2 | MVP 唯一变异;岩浆/冰冻不建行(CD 门禁:无 MVP 容器),垂直切片随关卡解锁建行 | + +### 板形参数(A1 BoardGenTable 域——A4 引用不拥有,禁止两表间复制参数,D2 Rule 1) + +| 参数 | 维度 | 约束与义务 | 产出点 / 消费方 | +|---|---|---|---| +| 区域数量 N | (level_index, node_depth) | 要求 N 区而实际达成 k 口径总纲:本 GDD 无平衡公式——迷雾的全部数量参数(区域数/尺寸/位置/与雷密度及图尺寸的交互)归 A1 BoardGenTable,且须在 Rule 3 结构推论硬边界(厚度 ≤2【≡ 任意 3×3 窗口非全雾】/ f ≤ 8d)内定义。A4 侧定量内容仅三条:F1 门控谓词、结构推论承重数学、雾致预算增量注记。 + +### F1 布置候选门控(variant_pool_gate) + +**结论**:pool 是集合谓词(MVP 规模 ≤1)——unlock_level 直接整数比较(无 D2 R1 floor 解析:unlock_level 是定义表列,非进度表行键);biome_tag 空集 = 通配(全 biome 可用),非匹配失败;空池合法,A1 静默布置普通图不报错(验收⑥)。 + +#### 推导 + +``` +pool(level_index, board_biome) = { v ∈ VariationTable | + (v.unlock_level ≤ level_index) ∧ (v.biome_tag = ∅ ∨ v.biome_tag ∩ board_biome ≠ ∅) } +eligible(v, L, B) :⇔ v ∈ pool(L, B) ——成员判定形式(AC-3 消费) +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| level_index | int | ≥1 | 当前关卡序号;与 unlock_level 直接整数比较(无 D2 R1 floor 解析——unlock_level 是定义表列,非进度表行键) | +| v.unlock_level | int | [1, 999] | VariationTable 列(fog 默认 2) | +| v.biome_tag | StringSet | ∅ 或非空 | ∅ = 通配(全 biome 可用),非匹配失败 | +| board_biome | StringSet | 非空 | 当前图 biome 标签集;值域与来源归 A1(BoardGenTable biome 字段,A1 回填)——本 GDD 只声明集合类型语义 | + +- Output Range:pool 为集合(MVP 规模 ≤1);逐项判定输出 bool;空池(如 level 1 雾未解锁)合法——A1 静默布置普通图,不报错(验收⑥)。 + +| level_index | pool | 验算 | +|---|---|---| +| 1 | ∅ | 雾未解锁——空池合法,静默布置普通图 | +| 2 | {fog} | fog.unlock_level=2、biome_tag=∅ 通配任意 board_biome | +| 3(magma 建行后) | {fog, magma} | 未来 magma(unlock_level=3);A1 从池中选取时必须消费 board_gen 流(回放确定性——验收⑧) | + +### 结构推论(Rule 3 承重数学——厚度、面积上界、外环雷下界) + +**结论**:非 0 安全格 ⇒ 每雾格必有非雾雷邻居 ⇒ 雾区任意处厚度 ≤2,可判定谓词 ≡ 任意 3×3 窗口非全雾(度量钉死为窗口谓词而非"直线连续行程 ≤2"——宽 3 对角带无任何全雾 3×3 窗、完全合法,按行程读法会误拒);雾面积占比存在依赖雷密度的必要上界 f ≤ 8×雷密度;k 格雾区外环必含 ≥⌈k/8⌉ 雷(雾形携带雷簇位置信息——雾边 = 推理锚)。 + +#### 推导 + +``` +非 0 安全格 ⇔ 8 邻域含 ≥1 雷;雷不可能为雾格(区内无雷) + ⇒ 每个雾格 x:∃雷 m ∈ N₈(x)(非雾雷邻居) +① 厚度:3×3 全雾窗口的中心格 8 邻全雾 ⇒ 8 邻无雷 ⇒ 中心为 0 格, + 与"雾格 ∈ 非 0 安全格"矛盾 ⇒ 任意 3×3 窗口非全雾 ⟺ 厚度 ≤2 + (雾区恒为雷集切比雪夫 1-膨胀出的边界带) +② 面积上界:每雾格 ≥1 个雷邻居,每雷至多作为 8 个雾邻格的见证 + ⇒ f ≤ 8×|M|(f = 雾格数,|M| = 雷数) +③ 外环雷下界:k 格雾区的外环(雾邻界外格集)承载全部雷邻居见证, + 外环每格至多邻 8 个雾格 ⇒ |外环雷| ≥ ⌈k/8⌉ ——雾形本身携带雷簇位置信息(Rule 5 增益侧) +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| f | int | ≥0 | 雾格总数(雾面积占比的分子) | +| \|M\| | int | ≥0 | 雷数(雷密度分母 = 全格数) | +| k | int | ≥1 | 单个雾区格数 | +| N₈(x) | 格集 | —— | x 的 8 邻 | + +| 构型 | 谓词判定 | 验算 | +|---|---|---| +| 3×3 实心雾块 | 不可能成雾 | 中心格 8 邻全雾 ⇒ 8 邻无雷 ⇒ 0 格,与非 0 安全格矛盾 | +| 宽 3 对角雾带 | 合法 | 无任何全雾 3×3 窗;按"直线连续行程 ≤2"读法会误拒——度量钉死为窗口谓词 | +| f > 8×\|M\| | 违反必要上界 | 见②——参数域拒绝(EC10 联动) | +| k 格雾区 | \|外环雷\| ≥ ⌈k/8⌉ | 见③——雾边 = 推理锚,禁单向建模(验收④) | + +### 雾致预算增量(唯一 A4 侧定量注记) + +**结论**:雾不改变 AP 价目,但改变清图预算构成,雾格 AP 成本分两态——链可达免费开出(EC4)、链不可达按全价 dig_ap_cost 结算;雾致预算增量 = 链不可达雾格数 × dig_ap_cost,计入清图总预算需求,参与 A1 BoardGenTable 设计期聚合核算(非逐板运行期强制,违例动作 = 修正参数,验收⑤);该增量对 A3 F1 条款 5 仅是可行性输入、不提供雾成本显著性保证——局限如实登记。 + +#### 推导 + +``` +价目不变:挖/旗/移动成本雾内外一致(零特判,Rule 4) +雾格 AP 成本两态: + 链可达雾格 → 免费(邻接非雾 0 格连锁展开零价——EC4) + 链不可达雾格 → 全价 dig_ap_cost 结算 +雾致预算增量 = 链不可达雾格数 × dig_ap_cost (量纲闭合:格 × AP/格) +计入清图总预算需求 → 参与 A1 BoardGenTable 设计期核算 +总预算可用量与全量校验(全量待挖下界 ≤ 可用预算)口径归 A1/A3 契约,非本 GDD 定义 +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| 链不可达雾格数 | int | ≥0 | A1 生成期计算并输出(B1 条款 5 对账输入的生产者义务——验收⑤) | +| dig_ap_cost | int | A3 knob(=1) | 归 A3 所有,本 GDD 引用不拥有 | + +| 情形 | 预算增量 | 验算 / 说明 | +|---|---|---| +| 链可达雾格(邻接非雾 0 格) | 0 | 连锁展开零价——免费开出(EC4) | +| 链不可达雾格 | dig_ap_cost / 格 | 全价结算;增量 = 链不可达雾格数 × dig_ap_cost | +| 稀疏图(链可达占比高) | 全价假设高估 | 系统性高估雾成本——高估方向为预算安全侧(level-designer 评审发现) | + +### 对 A3 F1 条款 5 的对账(三腿入账——构成变化而非消解) + +| 腿 | 效应 | 方向 | +|---|---|---| +| 雷险腿 | 雾区无雷、不产生 50/50 赌雷 | 消解(消解声称仅对此腿成立) | +| 信息腿 | 雾边寂静减少推理原料(推高弃图)与雾边推理锚增益(Rule 5)方向相反——净效应实测、非定理("50/50 减少可能反向利好清图"成立,"必然推高弃图"不采纳) | 双向 | +| AP 腿 | 链不可达雾格全价无连锁折扣,抬高清图成本——敌方结算时间税随之上行 | 推高弃图 | + +- 雾面积占比与链不可达占比为 B1 条款 5 对账输入变量(链不可达占比的生产者义务 = A1 生成期计算输出——验收⑤,防输入变量有名无源)。 +- B1 闭合对账须雾板/无雾板两态分别验算支配性不等式(A3 provisional 验算只在无雾图形况做过)+ 两条方向性护栏:① 雾板净当量优势带上界(雾板 vs 无雾板 EV 差的容许带——防福利方向);② 防钓鱼方向(与 A3 OQ6 防钓鱼约束对账):雾板下界抬升不得使"弃图刷雾板"支配"即刷"——逐板奖励差分 × 最低雾出现率 × 8 RAP 刷新会构造出 A3 OQ6 明令防的刷新钓鱼环,解法(关卡层常量锚 / 防钓鱼不等式 / 与 OQ6 显式对账)归 B4/B1。 +- playtest 弃图率/完成度指标雾板分层观测(注记回执 A3 OQ3——聚合指标会被雾板污染或反向掩盖)。 + +## 状态 + +| 阶段 | 载体 | Owner | 行为 | +|---|---|---|---| +| 定义(构建期) | VariationTable .tres | D2 承载 / A4 回填 | ConfigDB 加载校验;热重载走 D2 既有门(战斗中拒绝,EC8) | +| 布置(生成期) | 棋盘格变体属性 | A1 | 生成新扫雷图时经 board_gen 流取号;unlock_level 门控候选池 + cell_constraint 强制 + 出生区排除 | +| 生效(执行期) | 格属性只读查询 | A2 / D1 消费 | 开格时读"是否雾格"决定数字显示与否;零运行时写入 | +| 消亡(随图) | 棋盘实例销毁 | A2 | 清图换新图 / 刷新 / 战斗结束——变异属性随棋盘实例消亡,无跨图存续 | + +不变式:A4 无运行时状态机——变异是棋盘格的只读属性,随图生成、随图消亡;生效期零运行时写入、无跨图存续。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 候选池为空(雾未解锁如 level 1,或 biome_tag 全不匹配) | A1 静默布置普通数字图——空池合法、非错误、无 warning(两种来源同待遇) | F1 Output Range / 验收⑥ | +| EC2 cell_constraint 无解(非 0 安全格面积或连通分量尺寸不足以布置任何雾区——图太小 / 雷太密 / 雷太稀致雷膨胀皮肤碎裂 / 参数要求过高;判据含连通性而非仅总面积——雾域恒为雷集边界带,总面积充足但碎裂成小分量时同样无解) | 本次布置雾的尝试失败 → 跳过雾布置,本图为合法无变异图(A1 生成算法的降级产物);禁止放宽约束硬塞——Rule 3 承重规则不为布置成功率让步;部分达成契约:BoardGenTable 要求 N 区而仅达成 k 小人 > 旗 > 变异纹理 > 底色——雾纹理不得干扰雾外数字辨识;已开雾格 = 地板无字(不留雾残留遮蔽) | D1 | +| 3 | 调色板交换优先(视觉锚 4) | 雾纹理须支持 biome 色板变体(biome_tag 字段的换皮对应)——新 biome 不新增雾资产,只换色板 | art-bible | +| 4 | 边界可读性 = 硬指标 | 未开雾格的区域边界在任何色板下必须可读——"明处的选择"幻想的视觉前提(P1 诚实边界的呈现面),与"数字可读性 × 暗色氛围"样张测试(game-concept 风险登记第 1 条)同批验证;原型 v3 紫色迷雾格已验证基线可读性,正式色值/形状归 art-bible 格子样张测试定稿 | art-bible / D1 | +| 5 | "雾区无雷"知识的静态传达 | D1 MVP 验收两条 + 必含要点枚举(义务清单 D1-①②③)——D5 里程碑在 Alpha,雾在 MVP level 2 即首遇,D1 是 MVP 唯一活载体(对比 A1×15/A2×4,本知识此前零验收落点) | D1 | +| 6 | 已开雾格负空间与结束拍 | 已开雾格 = 地板无字(负空间呈现,防误读为 bug)+ 底色层 tint 残留与雾区全开一次性完成信号(D1 验收候选——"站在洞口前"承诺的结束拍与清图进度可读;tint 处于视觉锚 3 最低层,与视觉锚 2"不留雾残留遮蔽"的冲突口径:遮蔽禁的是遮挡数字/实体的雾残留,tint 是底色层标记不犯此条;转正与否及完成信号粒度(视觉合并域 = 信号单元,禁实例级双响)归 OQ8 裁决) | D1 | +| 7 | 结算与 HUD 负空间 | 结算界面无 A4 附加项(雾格奖励 = 普通安全格奖励,无独立归因类别——P2 标注归 B1);无 HUD 常显字段(变异是棋盘属性非资源,与 A3 的 AP/RAP 不同) | B1 / D1 | +| 8 | 动效与音频 | 雾格开出瞬间的"雾散"过渡(可选,MVP 不承诺)归 B5/D1 表现里程碑,非 A4 义务;无音频需求(雾开出音效归 D4 Alpha 期) | B5 / D4 | + +## Tuning Knobs + +### VariationTable schema 定稿(D2 回写义务——本 GDD 为回填 owner) + +| 字段 | 类型/范围 | 默认(fog 行) | knob | 说明 | +|---|---|---|---|---| +| variant_id | String(主键) | "fog" | ✗ | 变异标识;A1 候选池与 A2 查询的键 | +| display_name | String | "迷雾" | ✗ | 图例/教学用名(本地化归 D6) | +| unlock_level | int [1, 999] | 2 | ✓ | 首次可布置关卡序号(game-concept 解锁序列:普通数字→迷雾→岩浆/冰冻);本表为定义表(数值恒定)非进度成长表,键 = variant_id,进度语义承载于此列——D2 Rule 4 豁免,语义写入 schema 注释 | +| scope | enum {region, global} | region | ✗ | 作用域;global 仅预留枚举位(语义归 C3,MVP 不产生 global 行) | +| info_mask | enum {none, hide_numbers} | hide_numbers | ✗ | 信息规则:该变异隐藏什么;新信息规则走接口漂移 | +| cell_constraint | enum {any, safe_nonzero} | safe_nonzero | ✗ | 布置约束:允许落在什么格子上——Rule 3 承重规则的数据载体,A1 生成期强制执行 | +| biome_tag | StringList 可空 | [] | ✓ | 关联 biome 主题(AD 门禁换皮约束);空 = 全 biome 可用 | +| counter_item_ids | StringList 默认空 | [] | ✗ | 可穿透该变异的 A5 道具清单(预留,A5 期回填——探测=雾内数字显影);非平衡 knob(内容回填字段,与"结构冻结"归类一致) | + +### knob 效应与真 owner + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| unlock_level(fog 行) | 2 | 变异内容推迟,关卡间"解锁新变异"的推进感断裂(每关至少引入一个新元素的节奏被打破) | 教学压力前移——玩家尚未在纯数字环境建立推理信任即遇信息剥夺(P1 落差语义丧失;D5 教学须跟随实际值动态适配,EC9) | +| biome_tag(fog 行) | [](全 biome 可用) | 收窄出现范围——某些 biome 无雾,多样性下降 | ——(空集即基线,无"更低") | + +- 非 knob 字段(结构冻结):scope(MVP 恒 region——开放 global 是 C3 的语义义务而非调参)、info_mask / cell_constraint(fog 行值即规则定义本身,修改 = 改规则,须走本 GDD 修订流程而非调参)、counter_item_ids(A5 期内容回填,非平衡 knob)。 +- Knob 交互与真 owner 指向:迷雾的实际手感参数——区域数量 / 区域尺寸 / 雾面积占比 / 与雷密度的联动——全部在 A1 BoardGenTable((level_index, node_depth) 维度,A1 回填),那是难度曲线的函数;A4 的表只管"资格";禁止在两表间复制参数(单一事实源,D2 Rule 1);unlock_level 与 BoardGenTable 的难度轴正交(门控定"谁入池",表参数定"摆多大/摆哪"——systems-designer 审查确认无冲突)。 +- 跨 GDD 引用:A3 的 `ap_per_round`(8)与 `dig_ap_cost`(1)均归 A3 所有,本 GDD 不重复列出;雾致预算增量构成仅消费 dig_ap_cost(数值节注记),清图总预算的可用量口径归 A1/A3 契约。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)——A4 是定义层系统,冒烟域 = 定义层(schema / 门控谓词 / 校验);布置层 = A1 集成(15 项,义务清单 A1-①…⑮)· 执行层 = A2 集成(4 项,义务清单 A2-①…④)——deferral 以对侧登记为生效条件,A1×15 / A2×4 清单已同步 systems-index 门禁记录 | +| 致命用例协议 | 独立进程协议承 D2 AC-2b(runner manifest 逐条登记"调用→期望退出码";单进程内只做测试通道收集断言) | +| 夹具注入 | 经 `--data-root` 注入,五根齐备(3 根致命错误夹具 + 1 根 warning 夹具 + 1 根合法数据变体夹具,见下表) | +| 期望值推导纪律 | 由表值推导不写死——unlock_level 与 biome_tag 均为 knob,推导纪律适用两者(防 EC9 及 biome_tag 调参假失败) | +| 诊断卫生 | 每用例先 `reset_diagnostics()`(承 D2 冒烟协议,全局声明) | +| runner manifest | 四行退出码登记:dup→≠0 · oob→≠0 · mask→≠0 · global→0(biome_tag 根仅进程内测试通道消费、无退出码行,AC-3 前置断言其零诊断加载) | +| 豁免声明 | 空表/缺表/类型错类别由 D2 AC-2 同类别夹具承载,A4 不设专属夹具(loader 级类别、与表内容无关,D2 夹具可承载;空表情形另由 AC-1"恰含一行"间接兜底) | +| 悬空引用处置 | counter_item_ids 为 D2 schema 首个引用型字段——MVP 显式声明不校验悬空引用(字段恒空、A5 未回填);A5 GDD 回填首个道具 id 时补悬空校验 + 条件性夹具(义务清单 A5-1) | + +### 夹具清单 + +| 夹具根 | 内容 | 级别 | 服务 | +|---|---|---|---| +| variation_dup_key | 重复 variant_id;与 D2 AC-2 重复键夹具独立同型(A4 表首用,避免跨表耦合) | 致命 | AC-4 / AC-4b | +| variation_unlock_oob | unlock_level=0 | 致命 | AC-4b | +| variation_mask_constraint | info_mask=hide_numbers + cell_constraint=any 跨字段违规(EC4"级联止于雾格"承重保证的测试落点) | 致命 | AC-4 / AC-4b | +| variation_global_scope | scope=global 行 | warning 级、非错误(游戏通道 0 退出) | AC-6 | +| variation_biome_tag | 合法门控测试行:biome_tag=[ice]、unlock_level=1 | 合法(无对应校验类别) | AC-3 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 干净检出(默认表根) | 冒烟加载 Core Rules-3 表清单 | VariationTable 加载成功、恰含 fog 一行(无岩浆/冰冻行——内容事实断言);fog 行非 knob 六字段(variant_id/display_name/scope/info_mask/cell_constraint/counter_item_ids)与 schema 定稿值逐一相符("fog"/"迷雾"/region/hide_numbers/safe_nonzero/∅);unlock_level 与 biome_tag 两个 knob 字段仅作类型域断言、期望 = 表读值(不作定值断言——合法调参不得假失败) | Core Rule 8 / 义务清单 | +| AC-2 | fog 行表值 (u, tag=∅)(默认行),board_biome 任意非空集(全腿作用于干净检出默认表根——仅 fog 一行) | 对 (level_index=u) 求 pool;仅当 u>1,再对 (level_index=u−1) 求 pool | level_index=u 时恒含 fog;u>1 时 u−1 腿 pool=∅(u=1 时 u−1=0 越出 level_index≥1 域、不测——F1 前置域);tag≠∅ 交集推导分支由 AC-3 夹具腿承载(默认表根仅 fog 一行、tag 恒 ∅,该分支在默认根不可构造;夹具根上 ice 行 unlock_level=1、u−1 腿不真) | F1 | +| AC-3 | 前置断言:夹具根零诊断加载(每用例 reset_diagnostics() 后加载,防 schema 演进劣化静默);eligible(v, L, B) := v ∈ pool(L, B)(F1 谓词的成员判定形式)。第一腿:fog.biome_tag=∅(默认行);第二腿:夹具根 variation_biome_tag(测试行 biome_tag=[ice]、unlock_level=1 写入夹具规格——防门控腿掩蔽 biome 腿,并承载 tag≠∅ 交集推导语义) | 第一腿在 level_index=u(fog.unlock_level 表读值)以任意非空 board_biome(单/多元素集各一)求 eligible;第二腿在 level_index=1 以 board_biome={dungeon} 与 {dungeon, ice} 分别求 eligible(v=测试行) | 第一腿恒 true(通配语义——∅ 不因 board_biome 取值失败);第二腿 false / true(单元素 {dungeon} 不交集 → false;{dungeon, ice} 交集 → true) | F1 通配与交集 | +| AC-4 | 夹具根 variation_dup_key 与 variation_mask_constraint | `load_tables(root, collect_only=true)`(测试通道) | 错误清单各含重复键类别 / 跨字段校验类别(hide_numbers⇒safe_nonzero) | schema 校验 | +| AC-4b | 夹具根 variation_dup_key、variation_unlock_oob(unlock_level=0)与 variation_mask_constraint | 各以独立 headless 进程跑游戏通道 collect_only=false | 三进程均非 0 退出且错误清单完整(D2 Rule 6 加载期致命——"游戏内真的会死") | schema 校验致命 | +| AC-5 | schema 实例构造(实例化一律走 class_name 直引、不经 load()/preload()——AC-7 扫描豁免条款;程序构造免夹具) | `validate()` 作用于 unlock_level ∈ {0, 1000};∈ {1, 999} | {0, 1000} 均域错误;{1, 999} 均合法 | 域校验 | +| AC-6 | 夹具根 variation_global_scope(每用例先 reset_diagnostics()) | 测试通道加载;同夹具游戏通道独立进程 | 诊断收集器 warning 恰一条且来源类别 = global 语义注记、其余表零诊断(防他表 schema 演进以无关诊断稀释断言);游戏通道以 0 退出(非致命半句) | EC7 | +| AC-7 | A4 文件集(schema/谓词/冒烟脚本) | 按 D2 AC-9 模式静态扫描(`randomize()` / `RandomNumberGenerator` / ConfigDB 外 data 表加载) | 零命中(扫描域扩入 A4;schema class_name 直引实例化豁免——AC-5 程序构造前提,承 D2 AC-9 模式) | Core Rule 8 | +| AC-8 | ConfigDB 加载完成 | 脚本解析 `design/registry/entities.yaml` 的 `variation_table` 条目 | 与 ConfigDB VariationTable 8 字段定义(字段名/类型/域/knob 标记)逐一相等;定值断言仅限非 knob 字段,knob 字段(unlock_level/biome_tag)对账口径 = registry 随调参同步或本 AC 豁免其值(防 EC9 调参经此后门假失败);对账域显式排除 `mvp_content` 与 `fog_ap_note`(前者含 knob 活值登记);缺任一即失败(仿 A3 AC-10a) | D2 回写义务 | +| AC-9 | (人工·视觉)边界可读 / 双通道 / 色板变体 | 归 art-bible 格子样张测试(AD 门禁),非冒烟域——阈值委托的排期门与违约判定挂 systems-index 门禁增补记录;art-bible 缺位期以原型 v3 紫色为临时明度基线,就位后强制复测 | 操作化口径("可读"须为可判定谓词,防形容词验收):① 形状通道 = 色板无关线稿资产 + 对全部底色的明度对比数值下限(阈值 art-bible 定、须存在);② 新增 biome 色板强制重跑样张清单(AD 门禁复测项);③ 样张须含最坏用例:宽度 1 雾带(每格即全边界——Rule 3 结构推论下的正常形态)、"旗 × 雾纹理 × 边界"三重叠加格、宽度 1 雾带两侧毗邻数字格(视觉锚 1"纹理不干扰数字辨识"的真实威胁面在邻格——雾格自身无数字) | 呈现契约 1/4 | +| AC-10 | (人工·字面量)A4 文件集 | 纳入 D2 AC-14 项目级扫描 | 人工三要素口径,引用不另设(与 A3 AC-10b 同型) | Core Rule 8 | + +## 开放问题 + +| # | 问题 | owner | 时机 | +|---|---|---|---| +| 1 | 雾面积/数量参数回填与 P1 验证:BoardGenTable 雾区域数/尺寸/占比默认值——须在厚度 ≤2(≡ 任意 3×3 窗口非全雾)/ f ≤ 8d 硬边界内定义,含最低雾出现率与降级率埋点口径;可推理率参数按雾边双向净效应建模;雾占比与可推理率的联动曲线(A1 的 P1 生成端保障核心 + 本 GDD 15 项验收的数值侧);占比参数双重身份:难度旋钮 × 情报旋钮——雾形携带雷簇位置信息(Rule 5 推理锚),占比/数量调参同时改变"雾 = 雷簇探测器"强度与首遇显著度(总占比/最大区域尺寸下限口径随 BoardGenTable 一并定,防首关雾低于注意阈值致 P4 解锁拍哑火) | A1 | 第 5 位 | +| 2 | 探测显影的时效与定价:EC6 留白——临时/永久显影、道具成本、counter_item_ids 首个回填 + 悬空引用校验补全(D2 条件性夹具义务兑现) | A5 | 第 11 位 | +| 3 | 创意风险 #1 的 B4 侧兑现:奖励预知的呈现方案(A4 侧切面已定——雾不额外隐藏内容);雾区奖励在效果当量对照表的落位——受三条定价硬约束(B4-①②③),成本口径按链不可达雾格计;①的 per-AP 平价与②③的定量谓词归 B4/B1 对账闭合 | B4 | 第 8 位 | +| 4 | scope=global 语义:精英/Boss 图全局变异的构成、施加时机、与零特判原则及 cell_constraint 覆盖的相容性重审(C3-1) | C3 | 第 12 位 | +| 5 | 岩浆/冰冻规则设计:变异框架的下一批居民、P3 雷险维度的承接者;需新字段时走接口漂移规则回写本 GDD 与 D2 | 垂直切片期 | 随关卡解锁建行 | +| 6 | 变异解锁曲线复核:"每关至少引入一个新元素"的推进节奏随 C3 难度曲线定稿复核;D5 教学动态适配表值(EC9),不写死解锁关号 | C3 + D5 | 低优先 | +| 7 | 标错惩罚与雾内旗:若惩罚上线,雾内旗恒受罚的一致性推论纳入实测观察项(EC5);EC5 推论在 A2 原型会话前置确认(防 D5 教学返工) | A2 前原型小补丁 | 联动 A3 OQ2 | +| 8 | 已开雾格 tint / 雾区全开完成信号的裁决:现处"验收候选"悬空态(本 OQ 登记终结悬空);裁决须同时覆盖——与 0 格的可判定区分(0 格 = "八邻无雷"推理信息,视觉混淆污染推理——tint 为当前唯一区分手段)与 EC11 合并呈现下的完成信号粒度(视觉合并域 = 信号单元,禁实例级双响) | D1 + CD | D1 GDD 前 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例(docs/lean-gdd-sample-b1.md)重排——正文修订注记剥离收敛至本表;速览/体验锚点/枚举/义务台账/开放问题转表格,布置与揭示流程转字符画;F1 门控谓词、厚度 ≤2 / f ≤ 8d 结构推论、雾致预算增量公式本体与验算全保留;语义与 2026-09-08 定稿对齐,零规则变更 | +| rev2 | 2026-09-08 | 同日 re-review 六项 blocking 同日核销(CD 终裁建议 delta 核销免第三轮 full 评审,用户拍板照此放行——A3 先例):① systems-index 登记闭环补全(A1×15 全枚举——⑤扩链不可达占比生产者义务、⑬扩部分达成、⑭增批量统计断言形式条款,A2×4、D1×2、B4×3、B1 条款 5 输入,registry referenced_by 补 B1/B4);② knob 口径统一(counter_item_ids 非 knob 误标修正、AC-1 改双 knob 表读值断言、AC-8 排除 mvp_content/fog_ap_note);③ 校验与 AC 可执行性(新增第五夹具 variation_mask_constraint、manifest 第四行、AC-3 夹具根零诊断前置、AC-2 钉死默认表根);④ 修订残留失实清理("探路"改连锁路由 + 续付/退出两轴、雾格成本两态口径、厚度钉死 3×3 窗口谓词防对角带误拒、夹具计数改 3 致命 + 1 warning + 1 合法、预算改增量构成——原"≤ 轮数上限 × ap_per_round"不等式作废("轮数上限"在 A3 契约无定义、恒真空转)、Upstream 改零运行时接口消费);⑤ B4 约束簇(②改链不可达同分母口径——原"雾面积"分母脱钩作废、增③防福利上界、防钓鱼张力对账 A3 OQ6、①per-AP 平价注记);⑥ MVP 验收契约(D1 必含要点补"雾格不传播连锁"、首遇提示谓词改"首个含雾板进入可操作状态"——原"雾格首次与已开区相邻"作废(贴脸雾 t=0 即真、远雾图滞后)、一次性作用域 = 每 run、EC2 部分达成契约、A1⑤ 设计期聚合校验);顺带:D5 成本教学改玩家侧保守全价启发式、Rule 7 可达域保守上界 + A2 展开域 ⊆ A1 排除域回验、OQ1 占比双重身份、新增 OQ8、AC-9 补 art-bible 指针与宽度 1 雾带邻格用例——明细见 review log | +| rev1 | 2026-09-08 | full 独立评审(game-designer · systems-designer · qa-lead · economy-designer · level-designer · ux-designer 六专员 + creative-director 终裁)八项 blocking 同日核销:① 可推理率对账公理自相矛盾 + Rule 5 单向建模——改双向净效应、厚度 ≤2 / f ≤ 8d / ⌈k/8⌉ 结构推论入文(登记 A1 验收);② Player Fantasy"三选项"失实——改诚实版(雾 = 清图必经段、绕开 = A3 风险退出、买情报标注 A5 期)+ B4 两条定价硬约束 + P3 降格"信息切面"("清图判定豁免雾格"选项经裁决否决——违反零特判承重规则);③ "雾格全部全价"半假——改链不可达两态口径;④ EC2 判据扩连通分量 + 降级率埋点与最低雾出现率义务;⑤ EC3 预设 A2 未定义行为——Rule 7 可达域生成期确定性计算、邻接合法结论保留;⑥ AC 四处硬伤(AC-1 写死 unlock_level=2 等)+ deferral 闭环——A1 清单 11→15 扩容并登记 systems-index 门禁记录;⑦ EC11 定向粒度改 variant 行级、实例级定向归 A5 期新字段;⑧ "雾区无雷"教学零 MVP 落点——登记 D1 MVP 验收两条 + AC-9 操作化;顺带:hide_numbers⇒safe_nonzero 跨字段校验、EC10 升格参数域拒绝、已开雾格 tint 完成信号候选、"跨雾推理"措辞降调——明细见 review log | +| v1 | 2026-09-08 | 初稿定稿:承重规则"生成期布置约束(雾区仅非 0 安全格)+ 零特判原则 + fog unlock_level=2"用户拍板;lean 门禁 systems-designer 审查 Formulas(排除声明成立、谓词改集合语义、补下游验收项与预算构成注记)、qa-lead 审查 Acceptance Criteria;评审日志 design/gdd/reviews/a4-terrain-variation-review-log.md | diff --git a/design/gdd/legacy-2026-09-14-v1/a5-minesweeper-aux-items.md b/design/gdd/legacy-2026-09-14-v1/a5-minesweeper-aux-items.md new file mode 100644 index 00000000..a1dd49ae --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/a5-minesweeper-aux-items.md @@ -0,0 +1,534 @@ +# A5 扫雷辅助道具(Minesweeper Auxiliary Items) + +> **状态**:Approved · lean 格式——solo 快速审核 3 blocking 同日核销,用户拍板免独立 re-review 直接放行(B2/B1/D1 先例) +> **版本**:v1.1-lean · 2026-09-14 · 作者:user + main session(solo 定稿 + solo 快速审核修订;专员/总监代理按 review-mode.txt = solo 全部跳过) +> **承载支柱**:P1 主(信息道具 = 推理原料的恢复、替代推理窄域化)· P2(道具是扫雷决策的兑现物 + 来源归因)· P3(情报购买的信息与行动力定价切面)· P4(地牢探险者工具隐喻) +> **层级**:Layer 2.5 Core · Milestone 垂直切片(里程碑首位)· 设计顺序第 11 位(上游 D2/A3/A4/B3/A1/A2/B2/B4/B1/D1 全部 Approved) +> **一句话**:A5 定义挖掘期可用的辅助道具——获取挂 B4 奖励池、使用经 B1 挖掘期编排,效果 = 板级信息叠加层(零回写棋盘)与 AP 增量;五维度覆盖断言 + 稀有道具多维组合。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 挖掘期辅助道具的定义与使用规则 owner——道具内容(AuxItemTable)、获取路由(B4 aux_item 激活)、使用编排契约(B1 use_aux 规范序)、板级信息叠加层与 AP 增量的规则面;玩家在挖掘中以消耗品购买情报与行动力 | +| 拥有 | AuxItemTable schema(D2 回填 owner)· 五维度框架与覆盖断言 · MVP 六行内容(值 provisional 归 C3)· 板级叠加层容器(显影/真视/预读三集)· 使用目标校验规则 · 消耗与库存规则(关卡域)· 获取与使用全流程规则 | +| 不拥有 | 奖励池与权重所有权(B4——aux 行值 provisional 归 C3)· 棋盘状态与数字(A2——恒真透传)· AP 账本(A3——grant_ap 为接口漂移预授权)· 变异定义(A4——counter_item_ids 为 A4 字段、A5 消费回填)· 结算段序(B1——使用编排调用权归 B1)· 呈现(D1)· 教学文案(D5) | +| 上游输入 | A2 只读面(板面/已开状态——目标校验与数字数据源)· B4 Ready 映射身份(预读数据源——既有查询面零新增)· A3 相位与 AP 账本 · D2 AuxItemTable · VariationTable.counter_item_ids(A4 框架) | +| 下游输出 | 叠加层只读投影(D1 呈现数据源)· 使用意图编排契约(B1 义务清单)· counter_item_ids 首引兑现(A4 悬空校验激活)· 创意风险 #1 的道具侧兑现(plan 维度预读)· B4 OQ2 的 A5 侧裁决 | +| 硬约束 | 零随机(零 RNG 流消费)· 零特判延伸(A2 代码零 A5 分支——叠加层为派生信息层、零回写 A2/B4 状态)· 真视窄域护栏(雷位真视 ≤3×3、全图级仅限显影/预读类)· 无效/零信息目标不可用不消耗(可用性门:板级图标灰、点击级拒绝)· 库存关卡域 · Used 不可逆 | + +A5 是挖掘期的信息与行动力市场:道具从 B4 奖励池获取(安全格奖励的一部分),在挖掘阶段由玩家主动消耗——雾区数字显影恢复推理原料、巢位真视与奖励预读把未知变成规划素材、AP 增量扩大当轮行动预算。效果即时可见(叠加层直接改变棋盘呈现),不进结算明细。 + +没有 A5,三样东西缺席:A4 承诺的"买情报"出口(雾区应对只剩停手/弃图两轴)、创意风险 #1 的最后一块结构(未开格奖励身份的道具侧预读)、P1 支柱"纯猜局面用道具缓解"的兑现通道。A5 同时是 B4 OQ2(未开格身份呈现深度)与 A4 OQ2(探测显影时效与定价)的收口 owner。 + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 我算对了 | "推不出的地方,我能买回线索" | 显影恢复雾格数字 = 推理原料回归(数字恒真透传,非新信息源);真视窄域护栏(≤3×3)把"代答"锁在救急档——恢复推理可全图、替代推理必窄域;道具零随机、不引入新赌点 | +| P2 扫雷的因战斗的果 | "这瓶显影尘是我上一轮捡到的" | 获取 = B4 奖励池 aux 行(来源 {cell, round} 逐件归因);使用 = 挖掘期自主时点、效果即时可见;经由真视插旗兑付的 weaken 走既有 flag_hit 结算行——归因链不断 | +| P3 风险收益同源(信息切面) | "买情报的代价是放弃一件战利品" | aux 行挤占战斗奖励池权重(渗透率 ≈17%,F1);叠加层随图消亡——弃图刷新时已花显影沉没(A3 旧板旗随旧板消亡同款先例);真视≠自动兑付(插旗仍花 1 AP) | +| P4 地牢探险氛围 | "地牢里前人留下的工具" | 主题命名(显影尘/探巢摆钟/地穴全图卷);icon 键入 art-bible §4.6 扩容(数据键 = item_id,形状+色相双通道);程序化占位先行(D1 既有裁决) | + +| 反向护栏 | 处置 | +|---|---| +| 全图级真视常态化 = P1 塌方(扫雷退化为路径优化) | 真视窄域承重护栏入 Core Rule 3——board_wide 目标域仅对 fog_reveal/reward_preview 开放(恢复推理原料类);mine_reveal 上限 area_3x3,schema 校验强制 | +| 道具使用变成"烧包"挫败源(无效目标白耗) | 可用性门(Core Rule 8):板级零信息 → 图标灰、入口禁用(B4 usable_now 先例);点击级零信息/越板 → 拒绝零消耗——与 B4 E10 照常消耗拍板区分:挖掘期非结算管线、守卫在 A5 本地可判、无反抖动压力 | +| 道具依赖症(每轮必用、扫雷决策外包给道具) | 预警①道具依赖度(playtest 预注册);真视/显影为消耗品 + 渗透率有界(F1),供给面天然限流 | + +- 参考感受:杀戮尖塔的消耗品(留着的牌永远是一份规划素材)+ Into the Breach 的信息工具(买的是确定性而非输出);反向对照 = 无目标校验的"点了就没了"(烧包挫败)与全图透视挂件(推理玩法自废)。 + +## 全局流程 + +### 获取与使用主流程 + +``` +获取(install 期身份预分配——B4 域) + B4 roll_board → 格奖励身份含 aux 行(category=aux_item 激活后) + ▼ +挖掘期开出资格格 → 结算授予段 B1 grant(cell) + │ 返回 category=aux_item → B1 路由 A5.add_item(不入 B4 库存——两库存正交) + ▼ +A5 关卡域库存(跨轮/跨图/跨战斗延续;D1 常读条) + ▼ +使用(挖掘阶段·Active 相位,D1 使用意图 → 组合根 → B1 编排——唯一调用权) + │ ① 相位 gate(非 Active → 拒绝) + │ ② A5.validate_use(handle, target) —— 目标校验读 A2 只读面 + │ 失败 ⇒ 全链拒绝、零消耗(无效/零信息目标反馈 D1) + │ ③ 逐 kind 效果应用(顺序钉死:空间 kind 先、ap_grant 后): + │ 空间 kind → A5.apply_overlay(板级叠加层写入) + │ ap_grant → A3.grant_ap(当轮 remaining_ap += magnitude) + │ ④ A5.consume(handle) —— Used 不可逆、回执 = 快照投影 + 来源 + ▼ +D1 命令式回执(重查 query_inventory / query_overlays 刷新呈现) + ▼ +消亡:board retire(cleared 换图 / RAP 刷新 / 战斗结束) + → B1 编排 A5.retire_overlays(叠加层随图消亡——与 B4 retire_mapping 同点) + 关卡结束 → B1 编排 A5.clear_inventory(与 B4 clear_inventory 同点 + 清空反馈计数) +``` + +| 出口 | 触发 | 动作序列 | 约束 | +|---|---|---|---| +| 正常使用 | Active 相位 + 有效目标 | validate → apply_overlay / grant_ap → consume → 回执 | 序即原子(apply/grant 无失败域 → consume 恒达) | +| 目标拒绝 | validate_use 失败(无雾可显/坐标越板/预读窗口零未开资格格等) | 全链拒绝 | 零消耗、库存不变、D1 反馈可辨 | +| 相位拒绝 | 非 Active(Waiting/结算中) | 相位 gate 拒绝 | 零消耗;与 B4 道具窗口两输入面互不竞争 | +| 随图消亡 | cleared 换图 / RAP 弃图刷新 / 战斗结束 | retire_overlays | 已花道具不返还(信息投资沉没——设计意图) | +| 关卡清空 | 关卡结束(victory/defeat 皆然) | clear_inventory | 恰一次幂等;清空前快照计数供反馈行 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:每件道具标清它帮的是什么忙 +**人话**:道具定义表里的每一件道具,必须标出它属于哪类帮助——情报、雷险、行动、规划、反制,至少标一类。这是内容规划的自查清单:保证五种帮助每种至少有一件道具,玩家不管遇到什么困境都有得买。游戏运行时完全不读这个标签。 +**举证**:无计算。覆盖检查就是数表——五类各至少一行,游戏加载数据后自动检查(对应验收 AC-12)。 + +#### 规则 2:使用道具时会发生什么 +**人话**:玩家在挖掘阶段选中一件道具使用。道具做四件事之一,稀有道具同时做两件: + +| 效果 | 玩家看到什么 | 持续多久 | +|---|---|---| +| 显影 | 雾格里的数字现形 | 到这张图被换掉为止 | +| 真视 | 圈定范围里雷的位置现形 | 到这张图被换掉为止 | +| 预读 | 没挖开的格子预告里面的奖励 | 到这张图被换掉为止 | +| 加行动力 | 当轮行动点变多 | 只对当前这一轮 | + +**举证**:加行动力的计算:行动点 = 行动点 + 道具表里的数值(工兵令 +2、爆破包 +4)。其余三种效果没有计算——只是把格子记进"要额外显示"的名单;数字永远读棋盘真值,奖励身份永远读奖励表。 + +#### 规则 3:直接看雷的效果,一次最多 3×3 +**人话**:凡是"告诉你雷在哪"的效果,一次最多照亮 3×3 格,任何道具都不能全图看雷;看数字(显影)和看奖励(预读)允许全图。道理:扫雷的乐趣是自己推出雷在哪,花钱全图看雷等于把游戏换成走格子——所以"代替推理"的效果必须小而贵,"恢复推理原料"的效果可以大。 + +| 效果 | 允许的范围 | +|---|---| +| 显影(看数字) | 一个雾区,或全图 | +| 预读(看奖励) | 3×3,或全图 | +| 真视(看雷) | 只许 3×3,全图禁止 | + +**举证**:无计算。限制写进数据校验——表里任何看雷道具的范围字段超过 3×3,游戏加载数据时直接报错拒绝。 + +#### 规则 4:捡到的辅助道具进"关卡背包",不进战斗背包 +**人话**:玩家挖到带道具的格子时,战斗结算系统(B1)把道具放进辅助道具的关卡背包。它和战斗道具背包(B4 管)是两个独立的包,互不混装,但寿命一样——都活到关卡结束。 +**举证**:无计算。 + +#### 规则 5:背包里的道具,关卡结束才清空 +**人话**:背包里的辅助道具跨轮次、跨地图、跨战斗都在,直到整个关卡打完(无论输赢)由 B1 一次性清空。每件都记录从哪个格子的哪一轮捡到,结算能归因来源。用一件少一件、不能反悔;背包不设上限,稀缺靠掉落权重控制。 +**举证**:掉落概率的计算见「数值结论与推导·渗透供给」——期望每张图约 1.7 件辅助道具,稀有件约每 6 张图 1 件。 + +#### 规则 6:只能在挖掘阶段使用 +**人话**:玩家只能在挖掘阶段(轮次进行中)使用辅助道具,任意时点可以,用一件消耗一件。结算阶段用不了——那个窗口属于战斗道具。两个"用道具"的入口在时间上错开,不会冲突。 +**举证**:无计算。 + +#### 规则 7:使用后按固定四步执行 +**人话**:玩家发起使用后,B1 按固定顺序执行四步。顺序本身就是安全保障:所有会失败的情况都在第二步被拦下,后两步在合法输入下不会失败,所以"产生效果"和"消耗道具"永远成对发生。带两个效果的稀有道具,施加顺序也钉死:先空间效果(显影/真视/预读)、后加行动力——定义表里效果的书写顺序不承载运行时语义。 + +| 步骤 | 做什么 | 这一步会失败吗 | +|---|---|---| +| ① 阶段检查 | 现在是挖掘阶段吗 | 会——不在挖掘期:拒绝,不扣道具 | +| ② 目标校验 | 目标格合法吗(有东西可看、坐标在板内、道具没用过) | 会——不合法:拒绝,不扣道具 | +| ③ 产生效果 | 把格子写进显示名单,或加行动力 | 不会(会失败的都在前两步拦完了) | +| ④ 消耗 | 道具标记为已用 | 不会 | + +**举证**:无计算(纯流程规则)。 + +#### 规则 8:点错了不扣道具,没东西可看时用不了 +**人话**:目标不合法时(比如这张图没有雾,却用显影尘;或对已经全开过的区域用探宝罗盘),道具不会白白消耗。分两级:整张图都没东西可看时(板级零信息),背包里这件道具直接置灰、根本选不了;单个点击位置上没东西可看时(点击级零信息,比如 3×3 窗口里全是已开格),那一次点击被拒绝、给可辨认的提示。两种情况道具都留在背包。这和战斗道具"照常消耗"是故意的不同:挖掘期玩家可能手滑点错,不该为此受罚。 +**举证**:无计算。逐条判定规则见下方「使用目标校验谓词」表。 + +#### 规则 9:使用道具不花行动点 +**人话**:用道具不扣 AP。成本已经付过了——捡到它,那个格子就没出战斗奖励。挖格、插旗的价格不受任何影响。 +**举证**:无计算。 + +#### 规则 10:辅助系统自己不主动做任何事 +**人话**:A5 不监听事件、不画界面、不用随机数、不改任何其他系统的数据。它的全部信息来自三个已经存在的事实来源:棋盘数字真值、奖励身份、行动点账本。其他系统的代码里不会有任何一行"为了 A5"而写的判断。 +**举证**:无计算。这一条有机器验收:A5 的代码文件里搜不到随机调用、界面基类和任何其他系统的写入接口。 + +### 效果明细(规则 2 的实现参照——字段名与数据源) + +| kind | 效果 | 目标域支持 | 时效 | 零信息判据(不可用条件) | 呈现数据源 | +|---|---|---|---|---|---| +| fog_reveal | 未开雾格数字显影(恢复推理原料——A4 EC6 语义定稿) | fog_region / board_wide | 本图持续(叠加层) | 全板无未开雾格(板级 → 图标灰) | A2 板面恒真数字 + digit_reveal 格集 | +| mine_reveal | 雷格真视(惩罚格现形——真视≠自动兑付,插旗仍花 1 AP) | area_3x3(护栏上限) | 本图持续 | 恒有信息(零雷 = "这 9 格没雷"——恒可用) | mine_reveal 格集(标记呈现,art-bible 键契约) | +| reward_preview | 未开资格格奖励身份预读(寻路素材——创意风险 #1 兑现) | area_3x3 / board_wide | 本图持续 | board_wide:全板无未开资格格(板级 → 灰);area_3x3:窗口内零未开资格格(点击级 → 拒绝) | B1 query_reward_identity 既有读面 + reward_preview 格集 | +| ap_grant | 当轮 AP 增量(remaining_ap += magnitude;溢出停手转存 RAP 走 A3 既有规则零特判) | none(非空间) | 当轮一次性 | 恒可用 | A3 AP 读数(既有命令式刷新面) | + +### 使用编排分支(规则 7 的实现参照) + +| 分支 | 条件 | 动作 | 拒绝语义 | +|---|---|---|---| +| validate 失败 | 无效目标/Used 句柄/Cleared 库存 | 全链终止 | 零消耗 + 诊断(D1 反馈可辨) | +| 空间 kind | resolved_target 域内 | apply_overlay 逐 kind 写入 | 域内输入恒受理(validate 前置保证) | +| ap_grant | magnitude ∈ [1,999] | A3.grant_ap(magnitude) → remaining_ap 增 | 域内恒受理(AP 无上限域——A3 漂移条款) | +| consume | validate 已过 | Used 标记 + 回执 | Used 不可逆(重复 → false + 诊断) | + +### 使用目标校验谓词(规则 8 的实现参照) + +| 谓词 | 条件 | 裁定 | +|---|---|---| +| 中心格越板 | area_3x3 目标中心格 ∉ 板 | 拒绝、零消耗 + 诊断 | +| 边缘窗口 | area_3x3 中心格贴边/角(窗口探出板外) | 照常受理——resolved_target = 窗口 ∩ 板内格(角 4 格 / 边 6 格);真视窄域护栏不破(≤3×3 只缩不扩) | +| 板级零信息——显影类 | 全板无未开雾格(含无雾板降级产物) | 不可用:库存投影 usable=false、D1 图标灰、使用入口禁用;强行调用 validate_use → false + 诊断零消耗(EC2 同判据) | +| 板级零信息——预读类 | 全板无未开资格格(board_wide) | 同上 | +| 点击级零信息——预读类 | 所选窗口 ∩ 板内格中零未开资格格(area_3x3) | 该次拒绝、零消耗、D1 反馈可辨(拾取预览高亮将受影响格,辅助避空) | +| 真视恒可用 | 零雷窗口 = "这 9 格没雷"(信息非空) | 无零信息拒绝——仅中心格越板拒绝 | +| 组合行可用性 | 各 kind 判据取 OR(任一 kind 有信息即可用) | 爆破包因 ap_grant 恒可用;全图卷 = 有未开雾格 ∨ 有未开资格格 | + +- 可用性预判数据源 = A2 只读面(板面/已开状态/雾区身份 + reward_eligible 资格标记——读接口白名单同批扩列,缺口预检 1);A5 零事件订阅 → 板级判据随挖掘进展变化,usable 按 query 时点现算(派生信息层零缓存义务)。 + +## 枚举 + +### dimension_tags(五值封闭枚举) + +| 值 | 语义 | MVP 覆盖行 | +|---|---|---| +| info | 情报维度——推理原料的恢复(雾格数字) | probe_dust / catacomb_scroll | +| risk | 雷险维度——惩罚格真视(直接雷位信息) | nest_pendulum / blast_kit | +| action | 行动维度——当轮 AP 增量 | sapper_order / blast_kit | +| plan | 规划维度——奖励身份预读(寻路素材) | loot_compass / catacomb_scroll | +| counter | 变异对抗维度——counter_item_ids 消费(A4 框架) | probe_dust(fog 行回填) | + +### AuxItemTable schema(D2 新表,A5 回填 owner;定义表非进度成长表——Rule 4 豁免,键 = item_id) + +| 字段 | 类型/域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| item_id | String(主键唯一) | — | ✗ | 道具标识;B4 RewardTable.effect_id(aux 行)与 VariationTable.counter_item_ids 的引用键 | +| display_name | String 非空 | — | ✗ | 玩家可读名(D1 呈现数据源;本地化归 D6——B2/B3/B4 同库惯例) | +| dimension_tags | StringList ⊆ 五值枚举 非空 | — | ✗ | 维度覆盖断言载体(F2);组合维度 = 稀有多维行的声明面 | +| effect_kinds | StringList ⊆ 四值枚举 非空、无重复 | — | ✗ | 效果组合(MVP ≤2——多维度结合的 schema 承载) | +| target | enum {none, fog_region, area_3x3, board_wide} | — | ✗ | 目标形态;校验 = target ∈ 各空间 kind 支持域交集(全非空间 ⇒ 恒 none);area_3x3 运行时解析 = 窗口 ∩ 板内格(边缘裁剪——加载校验域仍为整窗) | +| magnitude | int [0,999] | — | ✓ | 仅 ap_grant 行消费(= AP 点数,加载校验 ≥1——B4 item 行先例);空间行不消费非校验项 | +| use_phase | enum {active_dig} | active_dig | ✗ | 使用时机(MVP 单值;扩枚举走本 GDD 修订——B4 use_timing 扩值同批对齐) | + +### MVP 内容行集(六行——目标值,回填生效以 B4 修订批为前提;值 provisional 归 C3) + +| item_id | display_name | dimension_tags | effect_kinds | target | magnitude | RewardTable 权重/tier | +|---|---|---|---|---|---|---| +| probe_dust | 显影尘 | [info, counter] | [fog_reveal] | fog_region | — | w5 / t2 | +| sapper_order | 工兵令 | [action] | [ap_grant] | none | 2 | w5 / t2 | +| loot_compass | 探宝罗盘 | [plan] | [reward_preview] | area_3x3 | — | w4 / t2 | +| nest_pendulum | 探巢摆钟 | [risk] | [mine_reveal] | area_3x3 | — | w4 / t2 | +| catacomb_scroll | 地穴全图卷 | [info, plan] | [fog_reveal, reward_preview] | board_wide | — | w1 / t3 | +| blast_kit | 爆破工兵包 | [action, risk] | [ap_grant, mine_reveal] | area_3x3 | 4 | w1 / t3 | + +- 常规四行 = 单维中数值(tier 2 与现行池主力同档);稀有两行 = 双维组合 + 高数值(tier 3 超现行池最高档、magnitude 4 = 半轮预算)——稀有道具设计指令的直接兑现。 +- 维度覆盖:五维度各 ≥1 行(counter 由 probe_dust 承载并回填 VariationTable.fog.counter_item_ids——义务 A4×①)。 +- 全图卷组合校验:fog_reveal ∩ reward_preview 支持域交集 = {board_wide} ✓;爆破包空间 kind 仅 mine_reveal → target=area_3x3 ✓。 +- 六行均 pool_tag=safe_cell(aux 行为格奖励;tier 2/3 ≥ T_fog_min=2 → 雾池档位过滤自然兼容;clear_default 池不入 aux 行)。 + +### schema 校验类别(诊断封闭枚举) + +| 类别 | 级别 | 触发条件 | 语义 | +|---|---|---|---| +| 重复键 | 致命 | item_id 重复 | D2 Rule 6 既有类别 | +| 维度域外 | 致命 | dimension_tags 空 / 含枚举外值 | F2 覆盖断言的前提 | +| 效果域外 | 致命 | effect_kinds 空 / 含枚举外值 / 含重复 | Core Rule 2 | +| 目标交集违例 | 致命 | target ∉ 各空间 kind 支持域交集(全非空间行 target ≠ none 同类) | Core Rule 3 真视窄域护栏的强制面 | +| magnitude 域外 | 致命 | magnitude ∉ [0,999];ap_grant 行 < 1 | B4 item 行 power ≥ 1 先例 | +| use_phase 域外 | 致命 | use_phase ∉ {active_dig} | Core Rule 6 | +| counter 悬空 | 致命 | VariationTable.counter_item_ids 含 AuxItemTable 无此行 id | A4 悬空引用校验激活(义务 A4×②——A5-1 兑现) | +| aux 引用悬空 | 致命 | RewardTable aux 行 effect_id 在 AuxItemTable 无此行 | B4 悬空校验双表扩展(义务 B4×②) | + +## 数值结论与推导 + +> 口径总纲:A5 零平衡公式——池权重/tier/magnitude 全部 provisional 归 C3;本节三条 = 供给渗透(F1)、覆盖断言谓词(F2)、信息价值方向预检(F3,方向性非定量)。 + +### F1 aux 渗透供给(aux_supply_rate) + +**结论**:默认根下 aux 道具期望 ≈1.7 件/板(|资格格|=10 口径)、稀有件 ≈0.17 件/板——渗透率有界是 P1 护栏的供给面(道具不主导挖掘决策)。 + +#### 推导 + +``` +E[N_aux/板] = |U| × (Σw_aux / W_total) +E[N_rare/板] = |U| × (w_scroll + w_blast) / W_total +默认根:Σw_aux = 5+5+4+4+1+1 = 20;W_total = 100(B4 原池)+ 20 = 120 +|U| = 10(B4 direction_precheck 保守板口径) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 资格格数 | \|U\| | int | 板级 | install 期资格格集基数(B4 义务⑥供清单;分布缺口待 A1 actuals——B4 预检 11 同款) | +| aux 行权重和 | Σw_aux | int | — | 六行 w 之和(provisional 归 C3) | +| 池总权 | W_total | int | — | safe_cell 池全部活行权重和(aux 行入池后重算——B4 F1 联动重算义务) | + +| 情形 | 读数 | 验算 | +|---|---|---| +| 默认根 | E[N_aux] ≈ 10 × 20/120 ≈ 1.7 件/板 | 渗透率 ≈16.7%(战斗类 83.3%) | +| 稀有件 | E[N_rare] ≈ 10 × 2/120 ≈ 0.17 件/板 | ≈每 6 板一件稀有——"稀有"定位可感 | +| 输入缺口 | \|U\| 分布待 A1 actuals p10/p50 | 落地后重跑(B4 预检 11 同批) | + +### F2 维度覆盖谓词(dimension_coverage) + +**结论**:coverage(d) :⇔ ∃活行 aux(d ∈ dimension_tags ∧ RewardTable 行 weight>0)——五维度 coverage 恒真为加载后内容审计断言(AC-12);维度是内容标尺非运行时逻辑(代码零维度分支)。 + +#### 推导 + +``` +coverage(d) :⇔ ∃r ∈ AuxItemTable : d ∈ r.dimension_tags ∧ reward_row(r.item_id).weight > 0 +断言 ∀d ∈ {info, risk, action, plan, counter} : coverage(d) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 维度 | d | enum | 五值 | dimension_tags 值域 | +| 活行 | r | 表行 | — | AuxItemTable 行 ∧ 对应 RewardTable 行 weight>0 | + +| 情形 | 谓词判定 | 验算 | +|---|---|---| +| 默认根 | 五维度全真 | 见 MVP 行集维度覆盖行 | +| 某维度零活行 | 断言失败 | 内容收窄信号(AC-12 拦截——"每维度都有对应道具"的机器可查形态) | + +### F3 信息价值方向预检(info_value_direction——方向性非定量) + +**结论**:显影的价值 = 残余盲猜端收缩——可推理格增加 → 玩家盲赌次数(暴露)下降 → 期望踩雷下降(P1 正向)。注意方向:引用式 P(雷) = |M| ÷ (|M| + (1−ι)×|S|) 对 ι **递增**——显影挖走残余池中的安全格后池内雷浓度升高,单次盲赌命中率上行;收益在"少赌",不在"单赌更安全"。真视把 k 格从决策域移除(必避/必旗候选)。两者均为正向信息增量、定量当量换算归 B1 条款①对照表扩列(义务 B1×④注记,MVP 不入对照表)。 + +#### 推导 + +``` +显影:fog_reveal k 格 → 板级 ι(免赌开据占比)上移 + → 残余盲猜安全格 n_s = (1−ι)×|S| 收缩 → 盲赌次数下降 → 期望踩雷 ↓ + 单次盲赌条件概率 P(雷) = |M| ÷ (|M| + n_s) 随 n_s 收缩而上行 + (池内雷浓度升高——安全格被挖走;与 B1 F4 同式, + B1 F4 另设 n_s < 1 适用域守卫 → 残余集空时主式不评估、信息端平凡腿接管) +真视:mine_reveal k 格 → 该 k 格移出"盲挖候选域" + 期望收益 = P(雷|盲) × (伏击暴露期望 − 标中 weaken 收益期望) 的差值锁定 + 精确当量归 B1 对照表(MVP 不闭合——间接效应如实登记) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 可推理率 | ι | float | [0,1] | A1 板级实际值(actuals p10 为对账口径——registry 既有) | +| 雷数/安全格数 | \|M\|/\|S\| | int | 板级 | A2 板面事实 | +| 真视格数 | k | int | ≤9 | area_3x3 护栏上界 | +| 残余盲猜安全格 | n_s | float | [0, \|S\|] | (1−ι)×\|S\|——B1 F4 同名变量(n_s < 1 适用域守卫同款) | + +| 情形 | 方向 | 说明 | +|---|---|---| +| 显影尘(一雾区) | n_s ↓ / 盲赌暴露 ↓ | 恢复推理原料——P1 正向;单赌 P(雷) 上行属正常(池雷浓度升高) | +| 全图卷(全雾显影) | n_s ↓↓ | 全图数字可读——推理面最大化而非代答(雾区无雷知识叠加) | +| 探巢摆钟(3×3 真视) | 决策域 −k 格 | 替代推理窄域化——稀有供给(F1)限流 | + +## 状态 + +| 容器/条目 | 状态 | 进入 | 出口 | +|---|---|---|---| +| 库存条目 | InStock → Used → Cleared | add_item 授予 → InStock;consume → Used(不可逆) | 关卡结束 → Cleared(clear_inventory;跨战斗/跨图延续同 B4 库存) | +| 板级叠加层 | Empty → Active → Retired | 首次 apply_overlay → Active(三集条目累积) | retire_overlays → Retired(cleared 换图/RAP 刷新/战斗结束——随图消亡,不跨图) | + +不变式:Used 不可逆;库存条目仅存关卡域;叠加层随图消亡且零回写 A2/B4 状态(派生信息层);A5 全 API 零随机。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 无效目标(fog_region target 时板内无未开雾区;area target 中心格越板;area 预读窗口内零未开资格格) | validate_use → false + 诊断,全链拒绝——零消耗、库存不变;板级判据另经库存投影 usable=false 前置呈现(图标灰) | Core Rule 8 · 谓词表 | +| EC2 无雾板(L1 或降级产物)上用显影尘/全图卷 | 同 EC1 拒绝不消耗——道具留存关卡域库存,后续雾板可用(雾 unlock_level=2 的跨关持有合法) | Core Rule 8 / A4 F1 | +| EC3 显影/真视后 RAP 弃图刷新 | 叠加层随旧板消亡(retire_overlays)、已花道具不返还——信息投资沉没(A3 旧板旗随旧板消亡同款先例);玩家侧含义 = 别在待弃板上花情报 | 状态表 / A3 rap_rules | +| EC4 显影格被开出 | 叠加层条目留存无害——已开格数字恒显示(A2 恒真),呈现自然合并、A5 零清理维护(零事件订阅的结构福利) | Core Rule 10 | +| EC5 真视雷格被踩(挖开) | 事实照旧——真视 = 信息非保护,雷→伏击照常;玩家侧价值 = 预先规避或插旗兑付 weaken | Core Rule 2(mine_reveal 语义) | +| EC6 真视格插旗 | 正常 1 AP 插旗、轮末判定标中——weaken 走既有 flag_hit 路径(P2 归因链完整) | A3 flag_rules | +| EC7 预读格的映射 Retired(换图/战斗结束) | 叠加层同批消亡(retire_overlays 与 B4 retire_mapping 同点编排)——预读徽章不会指向已消亡映射 | 状态表 / B4 E6 | +| EC8 Waiting 相位(结算中)使用意图 | 相位 gate 拒绝零消耗——A5 仅 Active;与 B4 结算窗口使用两输入面互不竞争 | Core Rule 6 | +| EC9 clear_inventory 后 use/validate | false + 诊断(Cleared 逻辑失效态——B4 E7 同款幂等守卫) | 状态表 | +| EC10 同一件道具重复选中 | validate_use 对 Used 句柄 → false + 诊断(Used 不可逆——B4 E9 同款) | 状态表 | +| EC11 组合行(爆破包/全图卷)部分效果失败 | 结构上不可达——validate 前置拦下全部可失败输入,apply_overlay/grant_ap 域内无失败路径;规范序(Core Rule 7)即原子性证明 | Core Rule 7 | +| EC12 ap_grant 后停手 | 溢出 AP 按停手路径 1:1 转存 RAP(A3 既有规则零特判——道具增量与基础 AP 同待遇);cleared 轮剩余 AP 不折算同款(A3 既有清图规则零特判) | A3 rap_rules | +| EC13 VariationTable.counter_item_ids 悬空引用 | D2 加载期致命(校验随 AuxItemTable 建表激活——A5-1 兑现;条件性夹具 a5_dangling_counter) | 枚举·校验类别表 | +| EC14 RewardTable aux 行 effect_id 悬空 | D2 加载期致命(dangling_aux_item 新类别——B4 悬空校验双表扩展) | 枚举·校验类别表 | +| EC15 战斗中热重载 AuxItemTable/RewardTable | D2 既有门(战斗中 reload 拒绝);库存实例持行值快照(B4 E14 同款);叠加层为运行时态不涉表重载 | D2 / B4 E14 | +| EC16 aux 行在无雾板被 roll 中 | 正常入库——获取与可用性解耦(池不按雾有无分列,雾档过滤仅作用于雾格身份——B4 F2 域);EC2 承载使用侧 | B4 F2 / EC2 | +| EC17 area_3x3 中心格贴边/角(窗口探出板外) | 照常受理——resolved_target = 窗口 ∩ 板内格(角 4 格 / 边 6 格),道具照常消耗;真视窄域护栏不破(只缩不扩) | Core Rule 8 · 谓词表 | +| EC18 零信息可用性门(板级判据随挖掘进展翻转) | 板级判据(无未开雾格 / 无未开资格格)→ 库存投影 usable=false、D1 图标灰、使用入口禁用;点击级判据(area 预读窗口空)→ 拒绝零消耗;组合行判据取 OR——爆破包 ap_grant 恒可用、全图卷 = 有雾格 ∨ 有资格格;A5 零事件订阅,usable 按 query 时点现算 | Core Rule 8 · 谓词表 | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | hard | AuxItemTable 新表 schema 承载与 ConfigDB 读取(本 GDD 回填 owner——走 D2 接口漂移规则回写 D2 表清单);校验类别族见枚举节 | +| A2 | hard | 只读面消费(板面/已开状态/雾区身份——目标校验与显影数据源);A2 侧零改动零义务(零特判延伸——A2 代码零 A5 分支) | +| B4 | hard(修订批) | aux_item category 激活(预留枚举兑现)+ effect_id 双表校验 + use_timing 扩值 + 默认根六行 + 池总权重算——义务清单 B4×①…⑤ 缺项即 blocking | +| A3 | soft(接口漂移预授权) | grant_ap(n) 新入口(第 9 API):Active 相位受理、remaining_ap += n(无上限域)、其他相位拒绝、零随机——A3 GDD 修订批登记(义务 A3×①) | +| A4 | 内容契约 | counter_item_ids 消费与回填(fog 行 = [probe_dust]);未来变异建行时各配 ≥1 反制道具(框架级"每变异 ≥1 反制");A4 OQ2 由本 GDD 收口(时效 = 本图持续、定价 = 池权重) | +| B1 | hard(编排) | use_aux 编排 + 生命周期编排 + 授予路由——义务清单 B1×①②③ 缺项即 blocking(B4 义务清单先例:B1 是全部战斗域状态变更的唯一编排方) | + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| D1 | soft | 呈现义务 4 项(缺项即 blocking,见义务清单)——aux 库存条/使用交互/叠加层三集呈现/清空反馈;叠加层数据源 = query_overlays 只读投影 | +| D5 | soft | 教学候选:"买情报"闭环(捡到道具→需要时用→情报换安全)+ 真视≠保护的措辞口径 | +| art-bible | soft | §4.6 图标键扩容:item_id 六键 + 叠加层标记三族(显影/真视/预读徽章——预读徽章与 B4 已开格徽章同族) | +| B5/D4 | soft | 道具使用反馈(后置里程碑);D4 音频挂点(Alpha——使用音/无效反馈音) | +| registry | 确认 | a5_aux_items 条目登记(本 GDD 定稿回填)+ variation_table.mvp_content counter_item_ids 注记更新 | + +### 跨 GDD 义务清单(对侧 GDD 修订批逐项核对落位,缺项即 blocking——B4 对 B1/D1 登记先例) + +> 义务编号以本 GDD 为源;跨 GDD 台账登记时采用「A5→对侧×①」前缀形式(B4→B1×①…⑥、B1 义务账本 B4×①…⑥ 为既有同形编号——前缀防混淆)。 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| B1×① | use_aux 编排入口(唯一调用权):Active 相位 gate(A3 phase getter)→ 规范序 validate→逐 kind 应用→consume;拒绝路径零消耗反馈 | B1 修订批 | 已登记 | +| B1×② | 生命周期编排:board retire(cleared 换图/RAP 刷新/战斗结束)→ A5.retire_overlays 与 B4 retire_mapping 同点;关卡末 → A5.clear_inventory 与 B4 clear_inventory 同点 + 清空反馈计数 | B1 修订批 | 已登记 | +| B1×③ | 授予路由:grant 返回 category=aux_item → A5.add_item(与 B4 stock 分流对偶——每格受理恰一次承接延伸) | B1 修订批 | 已登记 | +| B1×④ | 注记级:aux 信息价值不入条款①对照表(间接效应)——对照表扩列义务登记调参期(F3) | B1 修订批 | 已登记(注记) | +| B4×① | category 枚举扩值 aux_item(五值→六值——Rule 1 修订);交付轴归类 = A5 可选类(不入 B4 库存、经 B1 路由) | B4 修订批 | 已登记 | +| B4×② | effect_id 消费扩展:aux_item 行必填(AuxItemTable 精确引用)+ 悬空校验双表 + 条件性夹具 dangling_aux_item | B4 修订批 | 已登记 | +| B4×③ | use_timing 枚举扩值 {pre_settlement, during_dig}——aux 行 = during_dig(game-concept"使用时机"字段语义延伸) | B4 修订批 | 已登记 | +| B4×④ | 默认根行集 +6 aux 行(本 GDD MVP 行集表值回填;F1 池总权重算 + direction_precheck 联动重算;并按 B4 缺口预检 6 流程重制登记常数与受影响 AC——σ 黄金轨迹 Ψ/Φ、AC-18 category 值域内容断言等随批重推导,防静默假失败) | B4 修订批 | 已登记 | +| B4×⑤ | OQ2 收口注记:未开格身份呈现深度 = 道具驱动预读(A5 plan 维度)——未预读格仍不呈现、自动寻路不做(A5 侧裁决,D1 共担呈现) | B4 OQ2 | 已登记 | +| A3×① | grant_ap 接口漂移(第 9 API):`grant_ap(n) -> bool`——Active 相位受理、remaining_ap += n、无上限域、其他相位拒绝;8 API→9 API 封闭枚举扩容 | A3 修订批 | 已登记 | +| A4×① | VariationTable.fog.counter_item_ids = [probe_dust] 回填(D2 表值 + registry mvp_content 同批——A4 GDD 正文不改,A4 EC6/OQ2 收口) | D2/A4 修订批 | 已登记 | +| A4×② | counter_item_ids 悬空引用校验激活 + 条件性夹具 a5_dangling_counter(A4 测试口径"首个道具 id 回填时补校验"条款兑现——A5-1 义务闭环) | D2/A4 修订批 | 已登记 | +| A4×③ | 框架内容契约:未来变异建行时 counter_item_ids 各配 ≥1 反制道具("每变异 ≥1 反制"——岩浆/冰冻行随垂直切片批) | A4 后续批 | 已登记 | +| D1×① | aux 库存常读条(挖掘期规划面——与 B4 库存条并置或分区呈现归 D1 裁决;枚举序 = add_item 受理序) | D1 修订批 | 已登记 | +| D1×② | 使用交互:选中道具 + 目标拾取(雾格/中心格)或无目标确认;可用性门呈现(板级零信息 → 图标灰、入口禁用——usable 投影数据源,B4 usable_now 先例)+ 拾取预览(area_3x3 悬停高亮将受影响格);无效/零信息目标可辨反馈(零消耗语境);相位 gate 呈现 | D1 修订批 | 已登记 | +| D1×③ | 叠加层三集呈现:显影格数字(数据源 = A2 板面 + digit_reveal 集)/ 真视标记(形状+色相双通道)/ 预读徽章(数据源 = B1 query_reward_identity 既有读面——与已开格徽章同族) | D1 修订批 | 已登记 | +| D1×④ | 关卡末清空反馈:"剩余辅助道具 ×N 随关卡结束清空"(B4 义务⑤同款先例) | D1 修订批 | 已登记 | +| D2×① | AuxItemTable 新表登记(表清单 + schema 校验族——D2 GDD 接口漂移修订批) | D2 修订批 | 已登记 | +| D2×② | VariationTable/RewardTable 表值回填(counter_item_ids / 六行 aux)+ 两处悬空校验激活 | D2 修订批 | 已登记 | +| D5×① | 教学候选登记:"买情报"闭环 + 真视≠保护口径(非 blocking——D5 里程碑 Alpha) | D5 | 已登记(候选) | +| art-bible×① | §4.6 图标键扩容:item_id 六键 + 叠加层标记三族(键冻结流程同既有——终稿美术归样张批) | art-bible 后续批 | 已登记 | + +- 双向一致性核对(systems-index 依赖分层表):索引分层 A5 ← A2, A4, B4 ✓(A2/A4/B4 三硬上游全落上表;B1 编排 = 层 4 Presentation 消费侧的既有编排唯一性原则——B4 先例:编排调用权不构成依赖分层边);D2 为全员 Foundation 数据边。A4/A5 关系 = counter_item_ids 消费(A4 声明字段、A5 回填 owner)——与索引"A5 ← A4"方向一致。 + +## 呈现与 UI 契约 + +> A5 为玩家直接使用的道具系统——呈现归 D1(义务清单 D1×①…④),本节定义数据面供给与负空间。 + +| # | 数据面义务 | 归属 | +|---|---|---| +| 1 | 叠加层三集只读投影 query_overlays(digit_reveal/mine_reveal/reward_preview 三格集)——D1 呈现唯一数据源;数字/徽章本体读既有面(A2 板面、B1 query_reward_identity),A5 零数据复制 | D1 消费 | +| 2 | 库存投影 query_inventory(逐件 {item_id, source{cell, round}, 快照, 状态, usable};枚举序 = 受理序)——来源归因呈现保 P2;usable = 板级零信息判据预判(可用性门数据面) | D1 消费 | +| 3 | 使用回执 consume 返回快照投影——使用瞬间确认反馈数据源;无效目标拒绝附诊断类别(可辨反馈) | D1 消费 | +| 4 | 负空间:A5 效果零结算明细行(挖掘期即时可见性承载归因——与 B4 道具行协议区分);无 HUD 常显字段(库存条外);真视不自动标旗、显影不自动挖(玩家动作仍走既有输入面) | B1/D1 | +| 5 | icon 键契约:item_id 直作图标键(一键一专属纹样、全局恒定禁运行时换图——art-bible §4.6 惯例);叠加层标记三族视觉语言归 art-bible 样张批 | art-bible / D1 | +| 6 | 音频挂钩(D4,Alpha):使用确认音/无效反馈音/显影扩散音(可选);挂点事件 = consume 回执,A5 零音频依赖 | D4 | + +> 📌 **UX Flag — A5 扫雷辅助道具**:本系统有 UI 贡献(库存条/使用交互/叠加层呈现)。Phase 4 前置 `/ux-design` 产出使用交互 spec(design/ux/aux-items.md),stories 引用 ux spec 而非本 GDD。 +> 📌 **Asset Spec**:视觉需求已定义(icon 六键 + 标记三族)。art-bible 样张批后运行 `/asset-spec system:a5-minesweeper-aux-items` 产出逐资产规格与生成提示词。 + +## Tuning Knobs + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| RewardTable.weight(六行,B4 侧 knob) | 5/5/4/4/1/1(provisional 归 C3) | 渗透率↑ → 道具依赖症(预警①)、战斗奖励稀释 | 窗口长空转、五维度覆盖可能破缺(F2 断言拦截) | +| RewardTable.tier(六行) | 常规 t2 / 稀有 t3 | 稀有件入雾池门槛失真(T_fog_min 联动) | 稀有件与常规档不可辨 | +| AuxItemTable.magnitude(ap_grant 行) | 工兵令 2 / 爆破包 4 | +AP 过量 → 停手转存 RAP 泄漏(EC12)、清图节奏失真 | 行动维度名存实亡 | +| target(结构冻结非 knob) | 见 MVP 行集 | ——修改 = 改规则(真视窄域护栏),走本 GDD 修订流程 | —— | +| use_phase(结构冻结) | active_dig 单值 | ——扩枚举 = 修订(非调参) | —— | + +- knob 交互:aux 权重 × B4 战斗池权重(W_total 重算联动——B4×④;禁止两侧独立调参不看渗透率);magnitude × A3 ap_per_round(+4 = 半轮预算的锚定关系——ap_per_round 调参时联动复核);稀有 tier × T_fog_min(雾池准入联动)。 +- 非 knob(结构冻结):四值效果枚举与时效派生 · 真视窄域护栏(target 交集校验)· 无效目标不消耗 · 零 AP 使用成本 · 库存关卡域 · Used 不可逆 · 零随机。 +- 调参所有权:全部内容值 provisional 归 C3;锚验收权 = B4/B1(当量对账调参期扩列——B1×④)。 + +### 失衡预警(playtest 预注册,归 C3) + +| 编号 | 指标 | 阈值/读法 | +|---|---|---| +| ① | 道具依赖度:每轮使用率(用了道具的轮次 ÷ 总轮次) | >50% 即警——P1 观测面(扫雷决策外包给道具) | +| ② | plan 预读覆盖率:预读格 ÷ 开格前决策数 | 过低 = 道具浪费;过高 = 预读替代推理(与①联读) | +| ③ | AP 道具节奏:+AP 与停手率/RAP 转存量联动(A3 OQ3 阈值联读) | 转存率异常上行即警——+AP 沦为 RAP 农场 | +| ④ | 稀有感知:全图卷/爆破包单关出现件数分布 | 恒 0 = 稀有定位失效;恒 ≥2 = 渗透过高(F1 联动重跑) | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree、autoload 显式实例化)——A5 是 Core 层状态容器 + 定义表,零场景依附;容器/schema 经 class_name 直引实例化(B3/B4 先例,AC 扫描豁免同款) | +| 被测面 | B1 编排未接线(垂直切片实现期)——冒烟以构造序扮演编排方(B4 先例);A2 只读面以桩投影构造(真集成 deferral 登记缺口预检 1);A3.grant_ap 桩断言调用序(真 A3 deferral 同批) | +| 零随机口径 | A5 全 API 零 RNG——行为级硬证据 = 同输入序列重放逐位相同(AC-9) | +| 期望值推导纪律 | 六行 weight/magnitude/tier 全 provisional(Tuning Knobs)——THEN 按表读值推导不写死;结构定值断言合法:维度五值封闭、效果四值封闭、target 四值封闭、use_phase 单值、护栏谓词(mine_reveal 支持域) | +| 跨表断言生效前提 | AC-12(六行回填)与 AC-13(悬空校验激活)以义务 B4×①…⑤/D2×①② 兑现为前提——生效前登记 deferral(对侧义务登记即生效条件,A4 测试口径先例) | +| 诊断卫生 | 每用例先 reset_diagnostics()(D2 冒烟协议) | + +### API 面(provisional,封闭枚举——实现期 ADR 定名) + +| 接口 | 签名 | 语义与守卫 | +|---|---|---| +| add_item | `add_item(item_id, source) -> bool` | B1 授予路由调用;Cleared/未知 item_id → false + 诊断;逐件实例入关卡域库存 | +| query_inventory | `query_inventory() -> Array` | 逐件实例枚举(item_id/source/快照/状态/usable),枚举序 = 受理序;Used 保留只读;usable = 板级零信息判据现算投影 | +| validate_use | `validate_use(handle, target_cell?) -> Dictionary\|false` | 效果投影 {effect_kinds, magnitude, resolved_target};目标校验按使用目标校验谓词表(越板/点击级零信息 → false + 诊断零消耗;边缘窗口 → resolved_target = 窗口 ∩ 板内格);Used/Cleared/板级零信息 → false + 诊断;只读不消耗 | +| apply_overlay | `apply_overlay(kind, resolved_target, magnitude) -> bool` | 叠加层写入(单 kind 单次,B1 逐 kind 调用);域内输入恒受理 | +| consume | `consume(handle) -> Dictionary\|false` | Used 不可逆;回执 = {item_id, source, 快照投影};Used/Cleared → false + 诊断 | +| query_overlays | `query_overlays() -> Dictionary` | D1 只读投影 {digit_reveal, mine_reveal, reward_preview} 三格集;纯读零副作用 | +| retire_overlays | `retire_overlays() -> bool` | 板级叠加层清空(B1 编排——与 B4 retire_mapping 同点) | +| clear_inventory | `clear_inventory() -> bool` | 关卡末全清;重复 → false + 诊断(幂等守卫) | + +- 封闭枚举即负空间验收形态:无自动使用入口(真视不自动标旗、显影不自动挖)· 无重掷/升级 API · 无事件订阅 · 无 RNG 直连 · 无 A2/B4 状态写入口(apply_overlay 只写 A5 自持叠加层);新增任何入口 = 接口漂移(本 GDD 修订非调参)。 + +### 夹具与致命 manifest + +| 夹具根 | 内容 | 服务 | +|---|---|---| +| 默认根 | AuxItemTable 六行 + D2 全表(B4 修订批落地后)——结构断言与 F1/F2 验算 | AC-1/AC-11/AC-12 | +| a5_dup_key | 重复 item_id | 致命 | +| a5_dim_oob | dimension_tags 含 "luck"(枚举外) | 致命 | +| a5_target_violation | mine_reveal 行 target=board_wide(真视窄域护栏违例——承重校验的测试落点) | 致命 | +| a5_phase_oob | use_phase="settlement"(枚举外) | 致命 | +| a5_dangling_counter | VariationTable.fog.counter_item_ids=["ghost_item"](AuxItemTable 无此行) | 致命(A4×② 兑现载体) | +| b4_dangling_aux | RewardTable aux 行 effect_id="ghost_item" | 致命(B4×② 兑现载体) | + +| manifest 行 | 触发条件 | 断言 | +|---|---|---| +| a5_dup / a5_dim / a5_target / a5_phase / a5_dcounter / b4_daux | 各致命根独立进程游戏通道加载 | 非 0 退出且错误清单含对应类别 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 干净检出(默认根) | 冒烟加载 AuxItemTable | 加载成功、恰含六行(probe_dust/sapper_order/loot_compass/nest_pendulum/catacomb_scroll/blast_kit——内容事实断言);非 knob 字段(dimension_tags/effect_kinds/target/use_phase)与 MVP 行集逐一相符 | Core Rule 1/2 · 枚举节 | +| AC-2 | schema 类直引实例 | validate() 作用于越域代表值:dimension_tags 含枚举外值、effect_kinds 含重复、mine_reveal 行 target=board_wide、ap_grant 行 magnitude=0、use_phase 越域;域内代表值全组 | 越域各含对应域错误类别(零静默);域内零诊断——特别地 blast_kit(ap_grant+mine_reveal→area_3x3)与 catacomb_scroll(fog_reveal+reward_preview→board_wide)组合行合法通过 | 枚举·校验类别表 · Core Rule 3 | +| AC-3 | 全新容器 | add_item ×N(N=64)→ query_inventory | 恰 64 件、每件 {item_id, source{cell, round}, 快照}、全 InStock 零拒绝(数量无上限);枚举序 = 受理序 | Core Rule 5 | +| AC-4 | 库存含 probe_dust、桩面含雾区 R(2 未开雾格) | validate_use(handle, R 内雾格) | true + 投影 {effect_kinds=[fog_reveal], resolved_target=R}(只读——库存/叠加层零变化) | Core Rule 7② | +| AC-5 | 同 AC-4、库存计数 c | validate_use(handle, 已开非雾格) | false + 诊断(EC1);随后库存计数仍 = c(零消耗——照常消耗拍板不适用于 A5 的行为面) | Core Rule 8 · EC1 | +| AC-6 | 同 AC-4 | 编排序扮演:validate → apply_overlay(fog_reveal, R) → consume | apply 后 query_overlays.digit_reveal ⊇ R 两格;consume true + 回执含 source;实例 → Used | Core Rule 7 · EC4 | +| AC-7 | Used 句柄 | 再次 validate_use / consume | 均 false + 诊断(Used 不可逆) | EC10 | +| AC-8 | 库存含 sapper_order(magnitude=2)、A3 桩接线 | 编排序扮演 ap_grant 分支:validate → grant_ap 桩 → consume | 桩收 grant_ap(2) 恰一次且在 consume 前(序断言);consume true | Core Rule 7③ · B1×① | +| AC-9 | 固定输入序列(add_item ×3 → validate ×2 → 编排使用 ×2 → query 全读面) | 同序列重放两遍(全新容器) | 两遍的库存投影/叠加层投影/回执逐位相同(零随机的行为级硬证据——A5 全 API 无 RNG) | Core Rule 10 | +| AC-10 | 叠加层 Active(含条目) | retire_overlays → query_overlays | 返回三集全空;再次 retire → false + 诊断(幂等;重注入后可复用——板级生命周期) | 状态表 · EC3/EC7 | +| AC-11 | clear_inventory 后 | validate_use / consume / add_item / query_inventory | 均 false 或空 + 诊断(Cleared 逻辑失效态——B4 E7 同款) | EC9 | +| AC-12 | 默认根(生效前提 = B4×④/D2×② 兑现) | 读 RewardTable 六 aux 行 + F2 覆盖推导 | 六行存在且 weight>0、effect_id 精确指向 AuxItemTable 行;F1 渗透率 ≈1.7 件/板按表读值复算成立;五维度 coverage 全真(维度覆盖断言——"每维度都有对应道具"的机器可查形态) | F1/F2 · B4×④ | +| AC-13 | a5_dangling_counter / b4_daux 夹具根 | 独立进程游戏通道加载 | 各非 0 退出且错误清单含对应类别(悬空校验激活——A5-1/B4×② 兑现载体);对照腿:默认根干净通过 | EC13/EC14 · A4×② | +| AC-14 | A5 文件集(schema/容器/冒烟脚本) | 按 D2 AC-9 模式静态扫描 | randomize()/RandomNumberGenerator 零命中(零随机);extends Control / .tscn preload 零命中(Core 层零 UI 债);A2/A3/B4 写入类 API 符号零引用(只读面除外——A2 current_board/reveal_record 读接口白名单登记;B1 编排反向亦然) | Core Rule 10 | +| AC-15 | 库存含 nest_pendulum、桩面 3×3 窗(含 1 雷格)+ 板角中心格腿 + 零雷窗口腿 | 编排序扮演:validate → apply_overlay(mine_reveal, resolved_target) → consume | 常规腿:query_overlays.mine_reveal ⊇ 9 格(含雷格)、consume true;边缘腿:中心格 = 板角 → resolved_target = 4 格(窗口 ∩ 板内格)恒受理;零雷腿:恒受理(零雷 = 信息) | Core Rule 2/8 · EC17 · 谓词表 | +| AC-16 | 库存含 loot_compass(area_3x3)与 catacomb_scroll(board_wide)、桩面含未开资格格窗 R 与全开窗 E | 编排序两腿:validate → apply_overlay(reward_preview, resolved_target) → consume;零信息腿:validate_use(loot_compass, E 内格);板级腿:桩面全开 → query_inventory usable 投影 | 预读腿:query_overlays.reward_preview ⊇ R 中未开资格格、consume true;全图卷腿:digit_reveal ⊇ 全板未开雾格 ∧ reward_preview ⊇ 全板未开资格格;零信息腿:false + 诊断、库存计数不变(点击级零消耗);板级腿:usable=false(图标灰数据面)+ validate_use false | Core Rule 8 · EC1/EC18 · 谓词表 | + +### 覆盖矩阵 + +| 类别 | 映射 | +|---|---| +| Core Rules | 1→AC-1/AC-12(F2)· 2→AC-1/AC-2/AC-15/AC-16 · 3→AC-2(a5_target_violation 腿)· 4→AC-12(B4×①…⑤ 生效前提)· 5→AC-3/AC-11 · 6→AC-4(相位 deferral B1×①)· 7→AC-4/AC-6/AC-8/AC-15/AC-16 · 8→AC-5/AC-15/AC-16(可用性门谓词表)· 9→AC-8(零 try_spend = API 负空间)· 10→AC-9/AC-14 | +| Formulas | F1→AC-12 · F2→AC-12 · F3→方向预检(非 AC——定量归 B1×④调参期) | +| Edge Cases | EC1→AC-5 · EC2→AC-5(无雾桩面同构)· EC3/EC7→AC-10 · EC4→AC-6 注记 · EC5/EC6→规则语义(A2/A3 域 OOS——真集成 deferral)· EC9→AC-11 · EC10→AC-7 · EC11→Core Rule 7 序证明(构造不可达——负空间)· EC12→OOS A3 既有 AC(grant_ap 后 request_stop 转存——A3 修订批补腿)· EC13/EC14→AC-13 · EC15→D2 既有门(OOS)· EC16→B4 F2 域(OOS)· EC17→AC-15(边缘腿)· EC18→AC-16(板级/点击级腿) | +| 上游承接 | A4 EC6(显影语义)→Core Rule 2 fog_reveal 行 · A4 OQ2(时效/定价)→收口声明(依赖节)· B4 OQ2(呈现深度)→B4×⑤ · A5-1(counter 回填+悬空校验)→A4×①② / AC-13 · 创意风险 #1(预读)→Core Rule 2 reward_preview / B4×⑤ | + +### 缺口预检 + +| 编号 | 缺口与处置 | owner | 时机 | +|---|---|---|---| +| 1 | A2 真集成 deferral:目标校验/显影数据源以桩投影构造——真 A2 接线后 AC-4/AC-6/AC-15/AC-16 换真面重跑;雾区身份解析(fog_region target → 所属雾区)与零信息判据(已开态 + reward_eligible 资格标记——可用性门数据源)依赖 A2 板面变异快照读取(A4 生成期快照既有;A2 读接口白名单同批扩列) | A2 集成批 | 垂直切片实现期 | +| 2 | B1 编排 deferral:相位 gate/规范序/生命周期编排以构造序扮演——B1×①②③ 登记为生效条件(缺项即 blocking) | B1 修订批 | B1 GDD 修订 | +| 3 | B4/D2 修订批 deferral:六行回填 + 双悬空校验激活——AC-12/AC-13 生效前提 | B4/D2 修订批 | 义务清单兑现时 | +| 4 | A3 grant_ap 修订批:AC-8 桩换真面 + EC12 转存补腿(A3 侧 AC 扩一条) | A3 修订批 | A3 GDD 修订 | +| 5 | 扫描域成员清单:A5 GDScript 产物入 D2 AC-9 项目级 CI 扫描域(B3/B4 先例) | Technical Setup | 实现期 | + +## 开放问题(OQ) + +| 编号 | 问题 | owner | 时机 | +|---|---|---|---| +| 1 | 命名与 icon 终稿:六行 display_name 为机制名候选——地牢隐喻风味词复核 + art-bible §4.6 样张批定 icon(D1 程序化占位不阻塞) | art-bible/D1 | 样张批 | +| 2 | 预读呈现深度细则:徽章呈现信息量(仅身份 vs 身份+tier——query_identity 投影含 tier)与板面视觉密度(预读徽章 × 未开格 × 变异纹理叠加)——D1 裁决,与 B4 Rule 8 已开格呈现同族复用 | D1 | D1 修订批 | +| 3 | 真视与标错惩罚交互:A2 OQ1 惩罚上线后,真视价值重估(真视→必标中→零惩罚风险——价值上行)与护旗类道具(防惩罚)是否入池 | C3 | A2 OQ1 关闭后 | +| 4 | 未来变异反制扩展:岩浆/冰冻行建行时反制道具语义(A4×③ 框架契约——每个新变异建行批随批出反制行) | 垂直切片批 | 随变异建行 | +| 5 | 全图卷稀有度验证:F1 稀有读数 ≈0.17 件/板在 playtest 的"稀有可感"验证——过疏(恒不见)过密(常态全图)均触发权重复核 | C3 | 垂直切片 playtest | +| 6 | 自动寻路终态声明:预读道具为 MVP 终态(寻路始终玩家心智)还是中间态——B4×⑤ 已声明"自动寻路不做",升格为永久设计态与否随 playtest | C3/CD | playtest 后 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-14 | 初稿定稿(solo 模式一次会话——专员/总监代理全部跳过,出稿待人工复核):五维度框架 + 真视窄域护栏 + MVP 六行(四常规单维 + 两稀有多维)+ AuxItemTable schema(D2 回填 owner)+ 获取路由(B4 aux_item 激活)+ 使用编排契约(B1 义务 ×4)+ 板级叠加层(零特判延伸)+ A3 grant_ap 接口漂移预授权 + 跨 GDD 义务清单 21 项 + F1–F3 + 14 AC;A4 EC6/OQ2 与 B4 OQ2 收口 | +| v1.1-lean | 2026-09-14 | 首轮 /design-review(solo 快速审核)3 blocking + 5 recommended 同日核销——用户拍板两项边缘规则:① area_3x3 边缘窗口照常受理、resolved_target = 窗口 ∩ 板内格;② 零信息目标不可用不消耗(板级判据图标灰/入口禁用、点击级拒绝,组合行判据取 OR,真视零雷恒有效)。F3 方向自相矛盾改写(P(雷) 对 ι 递增——收益在盲赌暴露下降非单赌命中率)· 使用目标校验谓词表新增(Core Rule 8 展开)· EC17/EC18 新增 · AC-15/AC-16 补齐(真视/预读运行时路径)· B4×④ 扩登记常数重制 · 义务编号源前缀注记 · aux 行 pool_tag 注记 · 应用顺序钉死(空间先、ap_grant 后)· query_inventory 投影增 usable 字段 · D1×② 可用性门呈现扩列;正文 Core Rules 表同批口语化重写(语义零变更);同日用户拍板免独立 re-review 直接放行【B2/B1/D1 先例】 | diff --git a/design/gdd/legacy-2026-09-14-v1/b1-settlement.md b/design/gdd/legacy-2026-09-14-v1/b1-settlement.md new file mode 100644 index 00000000..bc6db45f --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/b1-settlement.md @@ -0,0 +1,945 @@ +# B1 战斗结算规则(Combat Settlement Rules) + +> **状态**:Designed · lean 格式 +> **版本**:v1.1-lean · 2026-09-13 · 作者:user + agents +> **承载支柱**:P2 主("扫雷的因、战斗的果"的兑现面)· P3(雷的双面兑付)· P1(条款③掌控感的经济侧验收) +> **一句话**:B1 把每轮扫雷的结果逐行兑现为战斗结果,并判定胜负。 +> **复核注记**:lean 门禁下 systems-designer / qa-lead 已委托起草(F0–F5 + 对账义务清单 / 43 条 AC + 覆盖矩阵);creative-director 与 art-director 未经咨询——生产前人工复核 Player Fantasy 情绪定位与 Visual/Audio 和 art-bible 一致性(§1–4 须在 D1 GDD 前创建)。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 战斗域编排与结算中枢;轮次生命周期唯一 owner | +| 拥有 | 段序、判定时机、伤害消费侧语义、明细流水协议、上游义务账本(B3×8 / B2×6 / A2×3 / B4×6) | +| 不拥有 | 单位状态(B2)· 状态实例(B3)· 奖励内容(B4)· 行动价目(A3)· 棋盘规则(A1/A2)· 呈现(D1/B5) | +| 上游输入 | A2 三事件流 · A3 相位观测量 · 各表值快照 | +| 下游输出 | 结算步骤流(D1/B5/D4 消费)· 条款③⑤对账结论(C3 约束输入) | +| 硬约束 | 零随机 · 零 UI 依赖 · 结算禁 await——同种子同输入 ⇒ 步骤流逐行相同 | + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P2 因果归因 | "都是我算出来的" | 每行明细携带来源标注(伤害道具 / 安全格增益 / 标中旗削弱 / 清图奖励 / 雷→伏击);无来源行 = 断言失败(AC-42)——P2 呈现半边的最终兜底在 B1 流水协议 | +| P1 掌控感 | "挖下去值得" | 挖掘决策 EV 健康不等式(A3 条款③)由本系统闭合签字;条款⑤守"推到底"不在数学上吃亏 | +| P3 雷的两面 | "赌桌不是陷阱" | 敌方伏击行与标中旗削弱行同框、同呈现语言 | +| 高光节拍 | "完美被加倍兑现" | 完美排雷双份兑付 + 标中旗行高光(创意风险 #2/#3 机制本体) | + +反向护栏:明细绝不能有无来源的行(P2);判定绝不能不可归因(P1);敌我增减益同构呈现(P3)。任何"合并到看不出来源"的呈现优化,须先确认归因链在数据层仍可还原。 + +## 全局流程 + +### 战斗开始 + +``` +begin_battle(encounter) + │ ① set_reload_gate(true) [D2] 调试门置位 + │ ② notify_battle_start() [B3] 容器开域 + │ ③ encounter 骰 [RngService] unit_count 区间均匀骰定本战实际数量(封闭流,恰一 scope——C3 遭遇随机化) + │ ④ spawn(L, D, hero_hp, n) [B2] 行值快照实例化(n = 实际数量 override;定值行 n = unit_count) + │ ⑤ install_board + roll_board [A2/B4] 恰一次;资格格清单 = S ∖ R₀ + │ ⑥ notify_round_start() [A3] → Active + ▼ +RoundActive(挖掘期,B1 只缓冲事件流) +``` + +### 每轮结算(七段骨架,全序执行、段序结构冻结) + +``` +轮末触发:Waiting 相位编排点(只读 A3 相位观测量,不依赖事件到达序) + │ + ▼ +①授予段 ──── cell_revealed 批 → flag_judged 批 → 清图奖励(仅 cleared 轮) + │ (转化规则见下方两表) + ▼ +②道具使用窗口 ─ 玩家至多选一件或跳过(全流程唯一玩家输入点) + │ 满血禁选治疗包;空库存可整体不弹窗 + ▼ +③增益/减益段 ─ query_active 双侧读值 → SettlementTable 语义映射 → 效果明细行 + │ + ▼ +④道具段 ───── 伤害道具:恒定表值打 default_target;治疗包:clamp 回复 + │ 施加时无存活敌 → 照常消耗 + "已消耗未生效"行 + ▼ +⑤战斗段 ───── 主角先手攻击(D_hero = 基础分量 atk×(1+attack_up) + 伤害A/B 充能分量——F6) + │ → 逐存活敌(行主序):query_attack → apply_damage → advance + │ ※ hero hp=0 → 立即短路,剩余敌不作用,直入 ⑦ + ▼ +⑥衰减段 ───── decay_round() 恰一次(含末轮;先兑付后衰减的时序契约) + │ + ▼ +⑦判定段 ───── hero hp=0 → 失败 │ 存活敌=∅ → 胜利 │ 否则 → 下一轮 +``` + +### 出口分发 + +| 结局 | 触发 | 动作序列 | 约束 | +|---|---|---|---| +| 继续 | 判定=下一轮 | `notify_settlement_done(same_board)` → `notify_round_start()` | 同图续轮 | +| 换图 | cleared 轮 | `retire_board` → `retire_mapping` → A1 `generate` → `install_board` → `roll_board` 恰一次 → `notify_settlement_done(new_board)` → `notify_round_start()` | stopped/ap_exhausted 恒 same_board,违约 fail-fast | +| 挖掘期刷新 | 玩家经 D1 发起 | A3 `request_refresh` 扣费 → 换图全序(无结算段、无 round_end、轮次不终结) | RAP 不足→拒绝上抛;**每轮 cap 2(RoundStart 重置)**——与 A3 `refresh_cap_per_round` / registry `rap_rules` / D1 同口径,非"每图";刷新图无清图奖励 | +| 战斗结束 | 判定=胜/负 | `retire_mapping` → `despawn_all` → `notify_battle_end` → `notify_combat_end` → `set_reload_gate(false)` | 库存不清(关内延续) | +| 关卡结束 | 上层显式调用 | `clear_inventory()` 恰一次,清空前快照计数返回 | victory/defeat 皆然 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:跨系统调用只由结算编排器一家发起 +**人话**:这条管 B1(战斗结算编排器)对外协作的姿态:战斗域所有跨系统调用都由 B1 一家发起,敌人、状态、库存三个系统(B2/B3/B4)相互之间零直调——任何一方绕过 B1 直接调别人都是架构违约。结算本体另有三条自守约束: + +| 约束 | 含义 | +|---|---| +| 零随机 | 结算路径不掷任何骰子 | +| 零 UI 依赖 | 结算逻辑不读不写界面 | +| 禁 await | 结算逻辑同步执行、不异步等待 | + +随机数只有一个例外,且在结算之外:战斗开始时本战敌人数量经 encounter 流掷骰决定,但那一下发生在结算步骤流之外(见「全局流程」节),结算的可回放性不受影响。将来若结算本身需要随机数,必须走数据层新增的增流通道并过评审。 + +**举证**:无计算。唯一发起方的静态扫描与零随机行为级断言见 AC-43/33;encounter 增流通道定义见 D2 Rule 5〔增流通道〕与 D2 流枚举〔encounter〕。 + +#### 规则 2:每轮轮末自动触发一次结算 +**人话**:每轮挖掘结束、回合状态机进入轮末等待相位时,B1 自动发起一次整轮结算。触发判定只读两样东西:回合经济系统 A3 暴露的相位读数、和"上一轮已结束"的标记(last_round_end),不依赖事件谁先到达——到达顺序被打乱,结算结果也一致。用事件订阅还是相位轮询来实现触发,留给实现期 ADR 定。 + +**举证**:无计算。触发不依赖到达序的行为级验收见 AC-6。 + +#### 规则 3:挖掘期只记账,轮末统一兑现 +**人话**:挖掘阶段玩家挖格、插旗产生的上报事件(挖开了什么、旗判中没判中)不立即兑奖,而是按轮先进缓冲区;轮末结算时按事件规范序——即棋盘系统 A2 发出事件的顺序——统一转化成奖励与效果。每条事实从记录到授予恰好处理一次:状态效果层不做去重、库存层只留幂等兜底,正常路径上不会重复发奖、也不会漏发。 + +**举证**:无计算。处理序与恰一次语义验收见 AC-9。 + +#### 规则 4:每轮结算按固定七段顺序执行 +**人话**:每轮结算严格按七个段依序跑完:①授予 → ②道具使用窗口 → ③增益/减益 → ④道具 → ⑤战斗 → ⑥衰减 → ⑦判定,全程图见「全局流程」节。核心相对次序:增益/减益先于道具、道具先于战斗。这个段序是结构冻结的——要改必须走 GDD 修订流程,不属于调参范围;玩家在窗口里的每一次选择也是回放输入的一部分。 + +**举证**:无计算(纯流程规则)。段序常量存于 SettlementTable `segment_order`,加载期恒等断言见 AC-2;段界行序验收见 AC-7。 + +#### 规则 5:结算第一段——把挖掘期的事实兑换成奖励 +**人话**:结算第一段是授予段,把挖掘期缓冲下来的事实分三批兑换成奖励与效果;每一格受理、每一雷转化、每一旗判定都恰好一次。三批概览: + +| 批次 | 处理什么 | 触发后发生什么 | +|---|---|---| +| 第一批(挖格上报) | 本轮挖开的格子 | 踩雷格逐颗转化为敌方伏击强化;带奖励资格的安全格按类别分流兑付 | +| 第二批(旗判定上报) | 本轮判定的旗 | 判中 → 敌方削弱当轮生效(每颗雷资格只兑一次);判空 → 零兑付、只记"标旗未中"归因行 | +| 第三批(清图) | 本轮把未开格挖尽 | 清图奖励恰好发一次;全板零误标且有标中 → 追加第二份同身份兑付 | + +**举证**:精确的输入/转化/产出与拒绝语义见下方两张实现参照表;兑付数值读 D2 表值、无新公式。三批的恰一次与守恒验收见 AC-9/10/11/12/13——AC 覆盖矩阵的引用记号 5.1/5.2/5.3 即此三批。 + +### 规则 5 的实现参照——三批转化 + +| 批 | 输入 | 转化 | 产出 | +|---|---|---|---| +| cell_revealed | `kind=mine` | 调 `ambush_grant_plan(mines_r, cap)`,事件规范序逐颗 `apply_effect(ambush, source={mine_hit, origin_cell, round_index})` | 伏击池 L;溢出颗产"溢出未生效"行(含来源) | +| cell_revealed | `kind=safe ∧ reward_eligible` | 按 category 分流(见下方「奖励分流」表) | 每格受理恰一次(B4 幂等仅兜底) | +| flag_judged | `hit=true` 且该雷实例资格未消费 | `apply_effect(weaken, source={flag_hit, origin_cell, round_index})` + 资格消费入集合 | 标中旗兑付行(highlight=`flag_hit`) | +| flag_judged | `hit=true` 且资格已消费 | 去重跳过(资格以雷格实例为键、跨轮累积;换图后新实例自然重置) | 零新增行(+debug 诊断可选) | +| flag_judged | `hit=false` | 零兑付;每雷实例一次去重(A2 逐轮重报零新增行,防惩罚轰炸) | "标旗未中"零值行(source=`flag_miss`、highlight=`perfect_broken`) | +| cleared 轮 | — | `grant_clear()` 恰一次;完美板(零误标 ∧ ≥1 标中)追加第二份同身份兑付(走既有授予通道) | 清图奖励行;完美高光行 | + +### 规则 5 的实现参照——奖励分流 + +| category | 兑付方式 | 拒绝语义 | +|---|---|---| +| buff | `apply_effect`(入池,结算段生效) | 满层 → "奖励未生效"行(可聚合计数),无折算 | +| instant_aoe | 全体存活敌各 `power` 点(逐个 apply_damage、受击即死) | 当场兑付,不进库存不出窗口 | +| instant_heal | 主角回复 `power`(clamp) | 满血 → "奖励未生效"行零兑付 | +| damage_item / heal_pack | `stock()` 入关卡域库存 | 窗口选用时兑付 | + +- `reward_eligible=false`(initial 通道)格不入奖励库——B1 判定只读 reward_eligible、`channel` 字段零特判;连锁批量授予溢出 = 显式接受(无转换补偿)。 +- 标错惩罚未上线——标旗未中行是归因痕迹非惩罚;预留语义走接口漂移补(A2 实测后)。 + +#### 规则 6:踩雷给敌人叠加伏击强化,有上限、只管一轮 +**人话**:挖开雷格时,B1 向敌人系统请求一次伏击授予计划,参数是本轮踩雷数和每轮上限。上限之内的雷按事件规范序逐颗叠加给敌方;超出的雷不生效,只记"溢出未生效"行留痕(含来源)。伏击聚成一个池:同一结算里多个敌人攻击读的是同一个池值——共享、不均分也不消耗;伏击只活一轮,轮末随衰减清零。 + +**举证**:恰一次调用 `ambush_grant_plan(mines_r, cap)`,granted = min(mines_r, cap)、差值计溢出(守恒算例见 AC-14);伏击池进入伤害合成的读法见 F3。 + +#### 规则 7:轮末窗口里你至多选一件道具,或者跳过 +**人话**:结算第二段是全流程唯一的玩家输入点:道具使用窗口。窗口清单 = 库存系统 B4 的库存投影,再由 B1 逐件做可用性守卫预判;玩家至多选一件使用、也可以跳过,库存为空时可以整体不弹窗。本轮的完整选择序列(含跳过)记入回放;若上游一次提交多件,只受理第一件、其余退回并记编排违例诊断。 + +| 窗口里看到 | 能否选用 | +|---|---| +| 伤害类道具 | 恒可选 | +| 治疗包 | 满血禁选;未满血可选 | +| 空库存 | 可整体不弹窗,按跳过记 | + +**举证**:无计算(纯流程规则)。"伤害道具恒可选"的结构前提 = 窗口只在仍有存活敌的轮打开(AC-15/20);多件提交违例诊断见 AC-16。 + +#### 规则 8:动刀之前,先把两侧的增益减益合成完毕 +**人话**:结算第三段把当前挂在主角和敌人身上的增益/减益一次性合成:从状态系统读出两侧活跃效果,按数据表的语义映射换算成各自的修正池,再做池内加算,供后面的道具段与战斗段使用。每条效果按授予流水逐条记明细行、标注它来自哪一次授予;敌方侧的减益作用于全体存活敌人(side-wide),不是只挂单个敌人。 + +**举证**:池加算、clamp 域与地板的计算见 F2(B3 义务③⑦ 的 B1 侧落点);效果行字段协议见「枚举」节;验收锚 AC-17。 + +#### 规则 9:道具伤害是表里的死数,不吃任何加成 +**人话**:结算第四段执行窗口选中的那件道具。伤害道具按表里的恒定值打向默认目标(顺位最前的存活敌人),不吃任何池修正;治疗包按表值回复、超出上限部分截断(clamp 归 B1 执行)。两个守卫在本段兜底:满血时使用治疗包 → 拒绝、不消耗;轮到施加时已无存活目标 → 道具照常消耗、记"已消耗未生效"行(存活判定读敌人系统的存活集)。 + +**举证**:无计算——数值恒读 D2 表值;默认目标与 clamp 口径见 F3;验收 AC-18/19/20。 + +#### 规则 10:主角先出手,再逐个存活敌人还手 +**人话**:结算第五段是战斗段。主角先攻击一次,伤害 = 基础攻击乘攻击增益 + 本轮攒下的伤害A/伤害B 充能分量——充能分量先按目标甲型查护甲克制表、逐分量取整后相加。然后按行序逐个存活敌人行动:每个敌人依次走"读攻击 → 结算伤害 → 推进状态"三步调用、恰好推进一次,主角不在推进范围内。战斗中有人死亡立即出局:已死敌人不再行动;主角若中途倒下,剩余敌人全部停手、直接跳到判定段。伏击池是共享读值,多敌攻击互不消耗。 + +**举证**:D_hero 三分量合成、clamp/溢杀与死亡判定公式见 F3;充能分量与甲效查表见 F6;调用序、短路、池共享验收 AC-21/22/24/25。 + +#### 规则 11:轮末必跑一次效果衰减,最后一轮也不省 +**人话**:结算第六段调用一次状态系统的衰减步,把到期的临时效果移除并记衰减行。这一步恒定执行、每轮恰好一次——哪怕本轮就是战斗的最后一轮也照跑:"先兑付清图奖励、后衰减"的时序契约由段序保证,胜利结算里的效果同样要走完这一轮的生命周期。 + +**举证**:无计算。恰一次与时序契约验收 AC-26;容器已空时衰减返回零属合法结果(零明细行,见边界情况节)。 + +#### 规则 12:最后一段定胜负:主角先判,再看敌人是否清空 +**人话**:结算第七段做胜负判定,顺序是防御性的:先判主角是否倒下(倒下即失败),再判存活敌人是否清空(清空即胜利),两者都不满足就回发"继续"、进入下一轮。判定结果写成终局判定行——它同时是胜负画面的数据锚。 + +**举证**:无计算。hero 先判的防御性优先级与双死不可达论证见 F3 ④;验收 AC-27。 + +#### 规则 13:完美排雷翻倍兑付,标中旗当场强调 +**人话**:授予段里埋着两个强制检查点(高光时刻),条件满足就必须兑现: + +| 高光 | 什么时候触发 | 触发后发生什么 | +|---|---|---| +| #2 完美排雷 | 清图时全板自布板起一次都没标错旗、且至少标中过一面 | 清图奖励追加第二份同身份兑付,玩家实得翻倍 | +| #3 标中旗 | 旗判中、且这颗雷的兑付资格还没消费过 | 敌方削弱当轮生效,兑付行打上高光标记 | + +呈现分工:事件型高光(完美排雷)发独立高光行;属性型强调(标中旗、清图奖励)用行字段标记。完整判定谓词与兑付/降级细节见下方实现参照表与「枚举」节。 + +**举证**:完美板当量 = power × P_perfect ÷ 100,载体域 {100, 200}(P_perfect 换算语义见 F1、参数归 Tuning Knobs 表);完美谓词与降级兑付验收 AC-35/36。 + +### 规则 13 的实现参照——高光兑现节点(强制检查点) + +| 节点 | 判定 | 兑付 | 呈现 | +|---|---|---|---| +| #2 完美排雷(perfect_sweep) | 板级谓词:本板自 install 起全部 flag_judged 中 hit=false 计数 = 0 ∧ hit=true ≥ 1(雾内插旗计入误标、零特判;计数随 install 重置) | 清图奖励身份第二份兑付:道具类 → `stock(同源身份快照)` 第二次;buff 类 → `apply_effect(同身份)` 第二次(满层拒绝照旧);`grant_clear` 本身仍恰一次;当量 = power × P_perfect ÷ 100(载体域 {100, 200}) | 独立高光行,value = 实际有效兑付(首份 + 第二份被接受部分)、禁记名义值;首份即零兑付 → 高光行不发(完美谓词仍成立) | +| #3 标中旗高光 | hit=true 且资格未消费(授予段判定) | weaken 当轮即兑付(dur=1 本就当轮生效)——chord 缺席的节拍缺失以"当轮兑付 + 明细强调"组合补偿 | 兑付行 highlight=`flag_hit` 字段标记;深度形态(即时 HUD 动画)归 D1/垂直切片 | + +高光使用规则:事件型高光(完美排雷)用独立高光行;属性型强调(标中旗 / 清图奖励)用行字段标记。 + +#### 规则 14:结算每一步都记一行流水,行型只有 13 种 +**人话**:结算过程中发生的每一件事都写一行明细流水。行型封闭为 13 类、不允许私自增加;每行都带公共字段(来源类别、格子、轮次、显示名、数值、高光标记等),各行型再补自己的特有字段。这份流水是界面呈现、回放对账、数据导出共读的唯一数据源契约。任何一行若找不到来源,直接断言失败——宁炸勿混。 + +**举证**:13 类行与公共/特有字段分层见「枚举」节「明细行类型」表;无来源行断言与逐行字段校验验收 AC-42。 + +#### 规则 15:同种子同输入,结算逐行复现 +**人话**:结算完全可回放:同样的随机种子加同样的输入(事件序列 + 玩家在道具窗口的选择序列),跑出的结算步骤流逐行相同——录像回放、断言复测、切点续玩都建立在它之上。成立前提正是前面几条规则:零随机、确定性输入、数值全部来自表值快照、重试换图(attempt 重试)不额外取随机流号。 + +**举证**:无计算。逐行相等断言见 AC-33、回放切点见 AC-34;随机流 scope 口径见 D2 流枚举。 + +#### 规则 16:所有数值都在数据表里,代码零写死 +**人话**:结算用到的每一个数值——当量、clamp 界、乘数、段序常量——全部放在数据层的结算表(D2 SettlementTable)里,B1 代码零数值字面量:调数值只改表、不动代码。配套一个调试门:战斗开始时 B1 把热重载闸门落下、战斗结束时抬起(B1 是唯一调用方);战斗中途的热重载请求会被闸门拒绝,防止数值换到一半把结算打断。 + +**举证**:无计算。表结构见「Tuning Knobs」节 SettlementTable schema;零字面量与闸门验收 AC-1/2/3/4/32/41。 + +## 枚举 + +### 明细行类型(封闭枚举,13 类) + +| 行类型 | 特有字段 | 产出点 | 说明 | +|---|---|---|---| +| 效果行 | `effect_id` `pool_value` | 增益/减益段 | value=该次授予贡献值;pool_value=池合成值(防 −130%×2 行误读为 −260%) | +| 伤害行 | `target` | 道具段 / 战斗段 | 分量二元组 + 减伤终值 + 击杀标记 | +| 治疗行 | — | 道具段 | clamp 后真实回复量 | +| 道具行 | `target` | 授予段 / 道具段 | 入库 / 生效 / 语境注记 | +| 奖励未生效行 | 聚合计数 | 授予段 | 满层拒绝、满血、雾板等合法例外 | +| 已消耗未生效行 | — | 道具段 | 施加时已无存活敌(Used 不可逆) | +| 使用被拒行 | — | 道具段 | 满血治疗包守卫 | +| 溢出未生效行 | — | 授予段 | 伏击/奖励族同名协议 | +| 标旗未中行 | highlight=`perfect_broken` | 授予段 | 零值归因行;每雷实例一次去重 | +| 蓄力行 | — | 战斗段 | charge 拍零作用 | +| 衰减行 | — | 衰减段 | 移除数 k(呈现聚合归 D1) | +| 判定行 | — | 判定段 | 胜负 / 继续 | +| 高光行 | — | 授予段(清图) | 完美排雷;value=实际有效兑付,禁记名义值 | + +**公共字段**(13 类全必有):`type` `source_class` `origin_cell`(板级行可空)`round_index` `display_name` `value` `highlight?` +**断言**:无来源行 = 失败(AC-42)。新增行类型/新字段均为输入的确定函数——回放逐行相等断言语义不受损。 + +### source_class(封闭枚举) + +| 值 | 来源 | +|---|---| +| safe_cell / flag_hit / mine_hit / clear_default | B3 既有四源 | +| 道具/判定扩展 | B4 交付语义 | +| enemy_action | 敌方行动行(伤害/蓄力) | +| flag_miss | 标旗未中零值行 | +| hero_attack | 主角先手攻击伤害行 | + +新增值 = 接口漂移,须同步 `source_label_map` 键集。 + +### highlight(可空枚举) + +| 值 | 含义 | +|---|---| +| `flag_hit` | 标中旗削弱兑付行强调 | +| `clear_reward_h2` | 清图奖励行强调 | +| `perfect_broken` | 本板完美谓词已破的归因锚 | + +新增值 = 接口漂移。 + +## 数值结论与推导 + +> 全部公式零随机(同输入恒同输出);期望值均为设计期统计量(对账输入 = A1 F4 报告 C 的 actuals,非 target 参数);数值默认 provisional(调参所有权归 C3)。 + +### 术语表 + +| 术语 | 白话定义 | +|---|---| +| 乘区 | 伤害公式里相乘的各段修正(本作两段:主角减伤池 × 敌方削弱池) | +| 池 | 同一侧百分比修正的加总桶 | +| 地板 | 数值不许越过的下界——本节地板 = 池最低 −100 百分点,即乘数最低 0 | +| 免伤轮 | 主角该结算受到的伤害为 0 的结算 | +| 充能池 | 伤害A/B 的单轮加法分量来源——开格/首次标中雷按表值累积、战斗段主角先手攻击一次性消费、轮末清零(F6 首次定义) | +| 溢杀 | 一刀超出目标剩余 HP 的部分——直接丢弃,不转移到下一个敌人 | +| 当量 | 统一价值单位,1 当量 = 1 点主角 HP 的期望净变化 | +| 瞬时口径 | 只数一个结算里兑现的价值(本节裁决口径) | +| 全寿命口径 | 乘上它存活的结算次数(仅作派生量,不作锚) | +| 残余盲猜端 | 图上推理不出、只能赌的那一小块 | +| 完美排雷(P_perfect) | 一张板上没插错一面旗、且至少标中一面时,清图奖励翻倍的乘数 | +| 挖穿计价 | 把残余盲猜区全部安全格挖完时平均会踩到多少雷——按雷与安全格在池中均匀随机交错计算,不是每挖一格按初始比例独立重赌 | + +### F0 当量归一口径(weaken 锚裁决) + +**结论**:采用"每结算瞬时当量"口径(每 AP 总暴露分支排除);m_w 值域 [60, 85]、默认 −65;地板交叉层数 = ⌈100/m_w⌉ = 2;B4 参照零漂移,B3 仅行值域修订(registry 回写)。 + +#### 推导 + +| 分支 | schema 可行性 | 后果 | 裁决 | +|---|---|---|---| +| 每结算瞬时当量 | 可行(m_w ∈ [51, 100] 域内) | F2 越域界随移(AP 界 8×m_w / 表界 16×m_w);地板交叉层数 = ⌈100/m_w⌉ = 2;B4 参照零漂移 | **采纳** | +| 每 AP 总暴露 | 不可行——需 m_w > 100,被 percent ∈ [−100, 100] schema 域禁止 | 现实解 = B4 重定最低档参照:safe_cell 活行集中无更弱 buff 行可指,需新增低档行或锚移道具档位——接口漂移 + A4 硬约束①连锁 + 条款①对照表全表重排 | 排除 | + +- **同分母性**:瞬时口径下 weaken 与 defense_up 作用于同一伤害流,"严格大于"在百分点读法与 HP 点读法下等价,锚判定无量纲歧义;总暴露口径引入时长权重(2:1 不对称),把 duration knob 泄漏进锚判定。 +- **机制同构**:B3 本就按结算逐次兑付,瞬时口径是容器原生口径;总暴露需跨结算累计账本,与已锁的"单结算自然边界"(dur=1 每结算清零无存量)冲突。 +- **schema 禁止**:总暴露锚算术需 m_w > 100 越 percent 域,改域 = B3/B4/B1 三侧换算连锁,成本不成比例。 +- **值域依据(@D_ref=12;按现行三输入复核)**:腿一 m_w ≥ 51——65 > 50 严格大于 attack_up 当量 6.0(参照对象 registry 回迁口径,裕度 +30%;池内字面最低档 defense_up −30 当量 3.6 读法下裕度更宽 2.17×——两参照读法均通过,锚判据不受减伤墙批漂移影响);腿二 m_w/100 × D_ref ≥ E_dig → m_w ≥ 38(E_dig = 4.5 面值口径——C3 v3 曲线格子池加权重推;区间读法 4.3–5.1 → 36–43);可行交集下界 = 51(E_dig 口径修正后腿二放宽、腿一回绑);上界 85 = 超出仅在连锁贫瘠板增加裕度,而地板交叉层数恒为 2、边际层早已贬值;schema 顶 100 保留为绝对上界(对应腿二校准上界 ≈1.96:1);下界 60 维持(高于现行可行下界 51——F0 裁决值保守余量,值域修订走 B4/B1 共裁流程非重算批)。 +- **地板规则(B1 clamp 承载,F2 成文)**:每侧 percent 池 clamp ∈ [−100, +100] pp → 乘数域 [0, 2];交叉层数 = ⌈100/m_w⌉ = 2(值域全域恒 2)——第 3 层起边际瞬时当量 = 0;单结算 weaken 池 HP 当量上界 = min(L×m_w, 100)/100 × D_inst(该结算实际裸伤害敞口)。 +- **B3 红警"7 层即触地板"预读更正**:该预读按 −400% 合成带估读;本节地板钉在乘数零点(−100 pp)→ 精确层数 = ⌈100/m_w⌉ = 2——回执同步 B3 OQ1 与 registry `status_effect.anchor_warning`(对账清单 #1/#2);预读实质结论(上调 m_w 的连带成本 = 地板提前到达、边际层贬值)被本裁决吸收。 +- **修订效果量(锚两腿在默认 65 下的读数,provisional @ D_ref=12)**:腿一 7.8 vs 6.0 HP/结算(attack_up 当量——+30%,严格大于 ✓);腿二 7.8 vs E_dig 4.5 HP(+73% ✓);池内字面最低档读法(defense_up 3.6)裕度 2.17×——三读法均通过;历史 −25 值红警复算结论不变(25 vs 50 pp 不过锚 → 修订成立)。 +- **重算复核(B1 重算批)**:锚两腿按现行三输入(duration=1、m_d=−30、C3 v3 曲线 E_dig=4.5)复核通过——腿一 7.8 > 6.0(attack_up 当量,参照对象 registry 回迁口径;裕度 +30%)、腿二 7.8 ≥ 4.5(裕度 +73%);可行交集下界 = 51(腿一回绑),值域 [60,85]/默认 −65 维持(F0 裁决值,共裁修订流程非重算批回收)。 + +| 连带后果项 | 内容 | +|---|---| +| B3 行值 | `StatusEffectTable.weaken.magnitude` −25 → 值域 [60, 85]/默认 −65(registry 回写);B3 AC-14 腿 3 非平凡性复查:16×65 = 1040 越出 percent 域——腿保持非平凡 ✓ | +| F2 越域界随移 | AP 界 = 8×65 = −520%;表界 = 16×65 = −1040%(合成值 B3 原样输出、B1 池 clamp 收口) | +| B4 侧 | 零漂移:defense_up 参照保持 | +| 时长不对称 | 显式接受:defense_up : weaken 全寿命 ≈2:1 不入锚判定——吸收通道 = m_w 上探值域段 + 高光 #3 即时节拍 + duration 为 knob(与 B3 OQ7 playtest 证伪项联动观测) | + +### F1 效果档位当量对照表(A3 条款①——B3 义务⑤ 对照实物落点) + +**结论**:单一线性换算全四源共用(分源改率 = 条款①契约违约);E(·) 单位 = 主角 HP 点;Output Range E ∈ [0, +∞)、全表零负值(floor 语义见 F2——percent 池饱和后边际 E = 0);weaken 修订后每旗 7.8 HP > defense_up 6.0(条款②腿一通过)。 + +#### 推导 + +``` +统一当量定义:E(·) ∈ 主角 HP 点(1 当量 = 1 点主角 HP 的期望净变化;与 B2 ambush 量纲、B4 F6 量纲建议同源) + +单一线性换算式(全部四源共用——分源改率 = A3 F1 条款①契约作废): + flat / 道具类(刀、治疗包、ambush): E = 值本身(HP 点恒等映射;ambush 按 k 个攻击拍线性展开) + percent 类(每结算、每层): E_inst = |L × m| ÷ 100 × D_exp + D_exp = 兑付结算的实际裸伤害敞口;对照表取参考敞口 D_ref + 完美乘数(显式 ÷100,运行时真实双份载体): E_clear(perfect) = power × P_perfect ÷ 100 + (P_perfect ∈ {100, 200}——载体 = 第二份兑付开关,Core Rule 13#2; + 200 ÷ 100 = ×2 = 两件实得,非账面乘数) + 时长(显式乘子,非分源改率): E_life = Σ结算 E_inst = E_inst × 兑付结算数 + +D_ref = SettlementTable.equivalence_ref_damage(单行全局 knob,provisional 默认 12 + = 单结算伏击结构上界 cap×m_amb〔B2 F4 已登记值〕,兼 attack 拍带 [4,16] 与 heavy 带 [8,20] 的中位代表) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 参考敞口 | D_ref | int | [1, 999] | SettlementTable 配置值(D2 回写义务,本 GDD owner);provisional 12;变更触发全对照表 + 条款③⑤重跑(联动义务) | +| 层数 | L | int | [1, max_stack] | B3 实例字段(授予快照) | +| 行值 | m | int | percent ∈ [−100,100]∧≠0 / flat ≥ 1 | StatusEffectTable 行值快照 | +| 兑付结算数 | — | int | [1, duration_settlements] | 剩余结算次数轨迹;对照表按满寿命取 | +| 道具威力 | power | int | [1, 999] | RewardTable 行值快照(恒定表值——已锁) | +| 攻击拍数 | k | int | [0, unit_count] | 该结算存活敌中 attack/heavy 拍单位数(B2 F4 供给,确定性可算) | +| 完美乘数 | P_perfect | percent | **{100, 200}**(载体域——非整数倍数无运行时载体,域外加载致命) | SettlementTable 配置值;provisional 200(×2 = 双份交付,Core Rule 13#2;换算入账恒 ÷100) | + +**默认根全部活行对照表(值全部 provisional,归 C3;按现行行集重登——两级奖励批八行 + 注入池 + 减伤墙批 m_d=−30):** + +| 行 | 源(source_class) | 类别 | 换算式 | E_inst(每结算/层) | 全寿命 E | 注 | +|---|---|---|---|---|---|---| +| defense_up_basic | safe_cell | buff → defense_up | E_inst = 0.30 × D_ref;E_life = ×duration 1 | **3.6** | **3.6** | 池内字面最低档 buff 当量(减伤墙批 −30 现行值) | +| attack_up_basic | safe_cell | buff → hero_offense | E_inst = 0.50 × D_ref | **6.0** | 6.0 | **锚对照实物(条款②参照对象——registry 回迁口径)**;消费面 = 主角战斗段先手攻击基础分量(F3⓪——attack_up 只乘基础分量、不乘刀与充能分量) | +| spark_aoe | safe_cell | instant_aoe | E = power | —(当场兑付) | 4 | 全体存活敌各 4 点 | +| heal_minor | safe_cell | instant_heal | E = min(power, max_hp − hp) | — | ≤ 4 | clamp 后真实当量;面值 4 = 上界读法(HP 满拒绝率——B4 预警②观测修正) | +| knife_t1 | 注入池(active_inject) | damage_item | E = power | —(库存轨迹量) | 8 | 供给控制器注入——非单格期望摊入(W1 来源语义裁决对侧登记) | +| knife_adv | 注入池(active_inject) | damage_item | E = power | — | 16 | 雾档升级刀 | +| heal_pack | 注入池(active_inject) | heal_pack | E = min(power, max_hp − hp) | — | ≤ 12 | 注入池稀缺强治疗 | +| clear_blast | clear_default | instant_aoe | E = power ×(perfect 板 ? P_perfect ÷ 100 : 1) | —(当场兑付) | 40(完美板 80——**真实双份**:两件实得,非账面乘数) | 条款④⑤载体(F5 消费) | +| weaken | flag_hit | debuff → enemy_output | E_inst = m_w/100 × D_ref | 修订后 65 → **7.8** | 7.8(dur=1) | 修订值域/默认见 F0 | +| ambush | mine_hit | 敌方增益(flat) | E = m_amb × k | k=1 → 3;满配 k=3 → 9 | 同左(dur=1 单结算自然边界) | 量纲已定稿(registry `ambush_unit_definition`) | +| 伤害A 充能 | safe_cell(开格——SettlementTable knob,非 RewardTable 行) | flat 附加分量 | E = min(开格数 × damage_a_per_cell, damage_a_cap) × 甲效/100 | 每轮 ≤ 8 × 甲效/100(对甲A 12 / 无甲 8 / 对甲B 4) | 同左(单轮充能单轮消费) | 充能规格见 F6(W3 补全);逐分量查表后 floor | +| 伤害B 充能 | flag_hit(首次标中——与 weaken 同判定批) | flat 附加分量 | E = 首次标中雷数 × damage_b_per_flag × 甲效/100 | 每雷 2 × 甲效/100(对甲B 3 / 无甲 2 / 对甲A 1) | 同左 | 同 F6;"标中旗双收益线"(weaken + 伤害B 充能)归 W12③ 对账清单 | + +- **锚对照实物(条款②,B3 义务⑤)**:参照对象 = attack_up_basic 行(registry 回迁口径——B3 Rule 10 同款;RewardTable tier=2,effect_id=attack_up)→ 每结算瞬时当量 6.0 HP @ D_ref=12;weaken 修订后每旗 7.8 > 6.0(严格大于 ✓,裕度 +30%);腿二(连锁放大边际基准,provisional 1:1 零链下界):7.8 ≥ E_dig 4.5 ✓(面值口径,C3 v3 曲线重推)。池内字面最低 buff 当量 = defense_up_basic 3.6(−30 现行值)——"最低档"字面读法裕度更宽(2.17×),锚在两读法下均通过;2026-09-11 参照回迁后的预检重跑义务由本批兑现(registry anchor_warning 迁移注记关闭)。 +- **结构性边界注记**:腿二校准上探的可满足上界 = m_w=100 ↔ 相对每结算百分点档 ≈1.96:1——连锁期望放大超过此比值的板形上腿二结构性不可满足(percent 瞬时档被 schema 域封顶);处置 = C3 板形参数(密度/连锁富集)与 m_w 联动约束,非数值修订可解(对账清单 #3/#8——连锁规模分布无既定供给方)。 +- **Example**:一轮挖到 2 个 defense_up(L=2 同实例)→ E_inst = 2 × (0.30 × 12) = 7.2 HP/结算 × 1 结算 = 7.2 全寿命(dur=1——名义值,饱和折减见 F4 敏感性);同轮标中 1 旗 → weaken L=1 → 7.8 HP/结算;踩 1 雷(unit_count=1、attack 拍)→ 该结算敌方附加 +3 HP。 +- **边界注记**:归 B1——本表与单一线性换算的维护、D_ref 与 P_perfect 的 SettlementTable 承载;归 B4——行值/权重/tier/C 供给(F6);归 C3——全部 provisional 数值;归 B3——L×m 合成原样输出(本表输入侧)。 + +### F2 percent→乘数换算与合成地板(B3 义务③收口) + +**结论**:池内加算 → 每池独立 clamp [−100, +100] → 池间乘算 → floor 单一取整(结构性非 knob);免伤轮(D'=0)是合法设计态——不调 apply_damage、产零值伤害行;Output Range D' ∈ [0, ∞) 整数、零负值分支,MVP 实际域 [0, 20](B2 参考带)。 + +#### 推导 + +``` +结算语义映射(SettlementTable 效果语义映射,effect_id 为键——B3 Rule 7 的 B1 侧成文): + attack_up → pool = hero_offense(消费点 = 主角战斗段先手攻击基础分量——F3⓪;attack_up 只乘基础分量) + defense_up → pool = hero_defense + weaken → pool = enemy_output + ambush → flat 附加(B2 F2 后置加法——不入乘区) + +敌→主单伤害实例(每结算、逐攻击行为): + P_pool = Σ(同池实例 L×m) (池内加算——B3 F2 加算语义的对侧承接) + P_pool ← clamp(P_pool, −100, +100) (合成地板:每池独立收口) + M_pool = 1 + P_pool ÷ 100 (换算方向:pp → 乘性因子;域 [0, 2]) + D' = floor( D_raw × M_hero_defense × M_enemy_output ) + (池间乘算;整数域 D' = D_raw × (100+P_h) × (100+P_e) ÷ 10000 向下取整——单一取整规则,结构性非 knob) + D_raw = query_attack(u, L, m_amb).total (B2 F2 分量合成 total;charge 拍 = 0; + 乘区作用于含伏击分量的全裸伤害——weaken 饱和轮伏击分量一并归零) +``` + +**三例逐一读法(B3 EC7 登记例的收口):** + +| 例 | 合成值 | 池 clamp 后 | 乘数 | 读法 | +|---|---|---|---|---| +| weaken AP 界(L=8 × m_w,修订后 65) | −520% | −100 | **0** | 该结算全体存活敌输出归零;第 3–8 层边际瞬时当量 = 0(地板吸收)——明细行如实显示合成值与终值两端(P2) | +| weaken 表界(L=16 × m_w) | −1040% | −100 | **0** | 同上——地板规则不区分越域来源,越域深度仅入合成值显示与诊断计数 | +| defense_up 叠满(L=2 × m_d,现行 −30) | −60% | −60(域内,未触地板) | **0.4** | 该结算 hero 承伤按 0.4× 结算——免伤轮(D'=0)不再由 defense_up 单侧可达(现行可达路径 = weaken 饱和;地板恰边界读法 = 判据函数对 −100 输入的断言腿,非现行行值产物) | + +- **免伤合法性**:enemy 侧 weaken 饱和(单结算零输出)是乘数域 [0, 2] 自然边界内的设计内结果;hero 侧免伤在现行 −30×2 帽(−60%)下不再由 defense_up 单侧可达——免伤轮(D'=0)的现行可达路径 = weaken 饱和或两侧池叠加触底;强度评估(减伤窗口/免伤频率)归 C3 对账联动(B3 Knob 交互同口径),不属地板规则异常。 +- **Example**:D_raw=20、weaken L=2 → P=−130 → clamp −100 → M=0 → D'=0(零值伤害行,不调 apply_damage);weaken L=1 → 20 × 0.35 = 7;再叠 defense_up L=1 → 20 × 0.7 × 0.35 = 4.9 → floor 4。 +- **边界注记**:归 B3——L×m 原样输出不 clamp(B3 义务⑧/EC7 对侧);归 B1——池归属映射、clamp、乘区次序、取整方向;归 C3——池值强度评估。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 裸伤害 | D_raw | int | [0, ∞) | B2 F2 total 分量(charge 拍恒 0) | +| 池合成值 | P_pool | int(pp) | 越域可达 ±1040(修订后);clamp 后 [−100, +100] | B3 原样输出 → B1 clamp 收口 | +| 池乘数 | M_pool | 有理数 | [0, 2] | 1 + P_pool/100 | +| 终值 | D' | int | [0, D_raw × 4](两池同 +100 的结构上界;MVP 不可达) | floor 取整 | + +### F3 伤害合成、clamp/溢杀与死亡判定(B2 义务③成文) + +**结论**:三分支全序(主角先手 → 逐敌循环 → 道具段)+ 负值分支禁令(零负伤害/负治疗/负当量)+ 死亡判定:Dead ⇔ hp=0(无负 hp 中间态)、受击即死、hero 死亡战斗段短路、判定段 hero 先判;Output Range 双向伤害 D ∈ [0, ∞)、hp ∈ [0, max_hp]、零转移零存续。 + +#### 推导 + +``` +⓪ 主→敌(战斗段段首,主角先手攻击——基础攻击恒存在〔HeroTable.atk 域 [1,999]、0 加载拒绝〕): + 目标 = B2 default_target()(固定顺位最前存活敌——与道具段②同源) + D_hero_raw = HeroTable.atk ×(1 + attack_up 载荷) + F6 充能分量 + (attack_up = 唯一吃 percent 修正的攻击增益、且只乘基础分量——percent 池加算 F2 语义; + 伤害A/B 为 flat 后置加法、不吃 percent、逐分量甲效查表 floor——F6; + 刀不吃修正的拍板不变——B4 F4) + D_hero = 三分量逐项 floor 后相加(基础分量经 hero_offense 池 clamp 地板——F2);D_hero = 0 → 零值伤害行(同①零值口径) + D_hero > 0 → apply_damage(target, D_hero);hp 归 0 → Dead(受击即死——死亡即不动手) + 明细行 source_class = hero_attack(新增来源值——接口漂移随批,target 字段照常) + 存活敌清空 → 敌方循环空转(零行动行),径入衰减段 +① 敌→主(战斗段,逐存活敌,行主序生成序;结算内调用序钉死 query_attack → apply_damage → advance——B2 义务①): + D_raw = query_attack(u, L, m_amb).total (L = 本结算伏击池读值——授予段后的当轮值;charge 拍 = 0) + D' = F2 换算(语义映射 → 池加算 → clamp 地板 → 乘区 → floor) + D' = 0 → 不调 apply_damage,产零值伤害行(分量二元组 + 终值 0) + D' > 0 → apply_damage(hero, D');hp' = max(hp − D', 0)(递减与 floor 0 由 B2 承载) + 溢杀部分(D' − 受击前 hp)丢弃——无存续、无转移、无负 hp 态 + hero hp' = 0 → 战斗段立即短路(剩余敌本结算不作用、不产行动明细行——Core Rule 10) +② 主→敌(道具段,窗口选定件——至多一件,单件制): + D = power(恒定表值,不吃任何 percent 修正——已锁;零乘区) + 目标 = B2 default_target()(固定顺位最前存活敌;死亡后顺延) + apply_damage(u, power);hp 归 0 → Dead(受击即死——道具段与战斗段同款;指针冻结 = 抢杀窗口) + 溢杀丢弃、不转移下一敌(下一件道具才打下一个目标) + 施加时已无存活敌 → 照常消耗(Used 不可逆)+ "已消耗未生效"明细行(B4 E10 承接,守卫不对称——Core Rule 9) +③ 负值分支禁令:任何公式分支不得产生负伤害/负治疗/负当量——percent 池地板 [−100, +100](F2)、 + flat 值 ≥ 0、hp ∈ [0, max_hp]、治疗 clamp 上界 max_hp(B4 F4 对侧:hp' = min(hp + power, max_hp))、 + apply_damage 入参 n ≤ 0 = B2 API 拒绝 + 诊断——D'=0 腿由此改走零值行(①) +④ 死亡判定数值边界:Dead ⇔ hp = 0(无负 hp 中间态);时机 = 受击即时(敌)/ 战斗段短路(hero); + 判定段 = 防御性优先级 hero 先判(双死结构不可达的纵深防御——Core Rule 12:我方伤敌在道具段与 + 战斗段主角先手攻击、均先于敌方行动兑付;敌方伤我在主角攻击之后——敌行动时点若已敌全灭则先判胜) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 裸伤害 | D_raw | int | [0, ∞) | B2 F2 total;分量二元组 {baseline, attach} 进伤害行(P2 归因) | +| 乘区终值 | D' | int | [0, ∞) | F2 输出;floor 取整 | +| 目标 hp | hp | int | [0, max_hp] | B2 单位状态;递减与 floor 由 B2 承载 | +| 道具威力 | power | int | [1, 999] | RewardTable 授予时快照(恒定) | +| 溢杀量 | — | int | [0, D') | 丢弃(会计面:明细行 value = D'、击杀标记布尔——不记溢出量) | + +- **Example**:goblin(atk=4)L=2 → D_raw=10;weaken L=1 → floor(10 × 0.35) = 3;defense_up L=1 同结算 → floor(10 × 0.5 × 0.35) = 1;knife_adv(power=8)打 hp=6 的敌 → hp 归 0 → Dead、溢杀 2 丢弃、击杀标记进流水、下一件刀打顺位后继。 +- **边界注记**:归 B2——递减/floor 0/Dead 翻转/指针冻结;归 B1——本式全序、调用序、零值行、溢杀丢弃语义、死亡判定时机(B2 义务③的对侧兑现)。 + +### F4 条款③挖掘风险不等式闭合(B1 单一闭合 owner;B2 义务④兑现) + +**结论**:**现行值链(duration=1 × m_d=−30 × C3 v3 曲线 d0.25/t0.88)下两参考板形破线**(锚行口径 L1 ≈0.48× / L2 ≈0.47×;期望口径 0.60×/0.58× 同向);满配 unit_count=3 深度破线;旧"通过"回执退役为历史记录(见结论①);破线升格 C3 默认曲线级约束;actuals p10 交付后强制重跑。 + +#### 推导 + +``` +配对口径(B1 闭合定义——B2 F4"等量换算与不等式闭合归 B1"的兑现): + 决策单位 = 单格挖掘;评估点 = 残余盲猜端最坏点(信息端平凡通过,一并登记) + 主式:P(安全) × V_safe > P(雷) × V_mine + + 适用域守卫:(1 − ι) × |S| < 1(残余盲猜集为空)⇒ 主式不评估、 + 由信息端平凡腿接管——零赌博板形不产生破线误判(P(雷) 式在空池退化,禁代入) + + P(雷) = |M| ÷ (|M| + (1 − ι) × |S|) (残余盲猜端上界:全部雷对 (1−ι)|S| 个残余安全格的池) + P(安全) = 1 − P(雷) + V_safe = E_life(最低档安全格 buff) = (|m_d|/100) × D_ref × duration_d + (现行值链:0.30 × 12 × 1 = 3.6。原"= D_ref 恒等式"(0.5 × D_ref × 2)随 duration=1 + 〔两级奖励批〕与 m_d=−30〔减伤墙批〕消解;V_safe = 锚行(最低档 buff = defense_up_basic) + 满寿命当量,低于跨行期望口径的差距同步收窄——E_dig 现值 4.5,见验算表注) + V_mine = k_max × m_amb = unit_count × m_amb (单雷边际敌方增益当量上界:1 雷 = 1 层伏击; + 默认行值 1×3 = 3;cap 溢出使第 cap+1 颗雷边际当量 = 0 → 灾难轮不等式自收紧) + + 信息端检查(平凡腿):P(安全) = 1(免赌集内)→ V_safe > 0 恒成立——"推理有回报"的登记面 +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 实际雷数 | \|M\| | int | [1, W×H−9] | A1 产物指标/行参数推导 | +| 安全格数 | \|S\| | int | ≥ min_safe_count | 同上 | +| 可推理率 | ι | float | [0.85, 1] | **actuals p10(A1 F4 报告 C)**——未交付前以行 target 代 = 判式面的保守代理(target ≥ 实际 p10 → P(雷) 偏高 → 主式更难过;低 ι 的真实代价不经本式入账——报告 C 交付按总暴露核对消费);重跑义务(对账清单 #6) | +| 防御行值/时长 | m_d / duration_d | int / int | −30 / 1(provisional——减伤墙批/两级奖励批现行值) | StatusEffectTable defense_up 行 | +| 参考敞口 | D_ref | int | [1, 999] | provisional 12 → V_safe = 3.6 | +| 伏击量纲/单位数 | m_amb / unit_count | int | 3(provisional)/ [1,3] | B2 行值 | + +**重算验算(C3 v3 行值总表代理:10×10 与 12×12 均 d0.25/t0.88;全部 provisional,actuals p10 交付后强制重跑):** + +| 板形 | ι(target 代 p10) | P(雷) | 左臂 = P(安全)×3.6 | 右臂 = P(雷)×3 | 裕度 | 判定 | +|---|---|---|---|---|---|---| +| L1(v3:10×10 d0.25,\|M\|=23,\|S\|=77) | 0.88 | 23/32.24 ≈ 0.713 | 1.03 | 2.14 | **≈0.48×** | **✗ 破线** | +| L2 腿(v3:12×12 d0.25,\|M\|=34,\|S\|=110——换锚注记见下) | 0.88 | 34/47.2 ≈ 0.720 | 1.01 | 2.16 | **≈0.47×** | **✗ 破线** | +| 满配语境(unit_count=3,V_mine=9,L1 板) | 0.88 | 0.713 | 1.03 | 6.42 | **≈0.16×** | **✗ 破线(深度)** | + +- **换锚注记**:旧验算的"L1 10×10 d0.10 / L2 12×12 d0.12"系 A1 schema 自洽参考行(registry mvp_reference_rows,非曲线行);本批两腿统一换锚 C3 MVP 行值总表 v3(L1 D0/D1 板 10×10、D2–D4 板 12×12,全轴 d0.25/t0.88)——L2 腿即 v3 曲线内最大常规板形;C3 L2 关卡行值填充批(OQ1)交付后随 actuals 重跑复核。 +- **双口径读数(防虚高裕度误用)**:上表左臂按锚行当量 V_safe=3.6(A3 条款③"最低档 buff 当量下界"的合同读法);按跨行期望口径(左臂 = P(安全)×E_dig,E_dig=4.5 面值口径)复算:L1 ≈0.60×、L2 ≈0.58×——二元结论不变(两板形、两口径全破)。 +- **结论①(重算回执)**:现行值链下两参考板形破线——登记"通过"回执失效(旧读数 ≈2.0×/2.6× 的三个旧输入:duration=2 / m_d=−50 / v1 曲线参考行);信息端平凡腿不变(零赌博板形不产生破线误判——P(安全)=1 → V_safe > 0 恒成立)。 +- **结论②(满配语境)**:unit_count=3 深度破线(≈0.16×)——原 C3 调参约束维持并升格(见④)。 +- **结论③(敏感性)**:B4 预警④ buff 饱和的结构性零兑付使 V_safe 名义值高估——按 0.6 折减(provisional)复算 L1:左 0.62 vs 右 2.14 → 裕度 ≈0.29×(更薄);折减系数与饱和率随 playtest 重校。 +- **结论④(破线升格——曲线级约束)**:基线板形已入破线区,约束从"unit_count>1 行放行判据"升格为**默认曲线的 V_safe 侧杠杆重议**——候选杠杆 = m_d 回调 / duration 恢复跨轮 / D_ref 联动上调 / 板形侧密度×ι 联动对(d0.28 回退先例)/ m_amb 下调;全部处"禁独立调参"联动网络(m_w↔D_ref↔m_d↔duration_d——🔴-1③ 同网),修正方向须 C3 提案 + B1 闭合对账共裁,本重算批如实登记读数、不代裁修正。 +- **与 clause3_supply 2.3× 预检的对齐**:该预检系"pp 代理左臂 × cap 右臂"的巧合近似——量纲混用且右臂取结算聚合上界与单格决策单位错配(多计 cap 倍);本闭合改统一 HP 当量 + 边际配对 → registry `clause3_supply.direction_precheck` 退役为历史记录、回执登记重算值(对账清单 #3)。 + +### F5 条款⑤清图 vs 弃图支配性闭合(B1 单一闭合 owner) + +**结论**:条款⑤未闭合——挖穿残余池计价下主判式两态为负(L1 仅完美双份翻正 +2.7,非完美态被支配),实现率折减与多敌语境更差;"推到底"的支配性只在残余集小时自然成立;判据函数角色 = C3/B4 调参约束生成器,AC-40 断言输出与登记约束包一致。 + +#### 推导 + +- **计价口径**:清图必须挖穿残余盲猜区——随安全格被挖走,池内雷占比持续上升,挖穿全部残余安全格的期望踩雷 ≈ |M|×n_s/(n_s+1)(均匀交错模型);逐格按初始比例独立重赌的口径漏计全池挖掘量(模型漏项,actuals 校准无法吸收)。 + +``` +主判式(决策点 = "确定格挖完";策略对 = 推到底清图 vs 停手攒 RAP 刷新): + Δ_clear = [R_res + C_eff] − [X_res + Δr × s_ref] ≥ 0 + + n_s = (1 − ι) × |S| (残余盲猜安全格数——评估点同 F4; + 适用域守卫:n_s < 1(残余集为空)⇒ 主判式不评估、信息端平凡腿接管) + E[踩雷] = |M| × n_s ÷ (n_s + 1) (挖穿残余池期望踩雷——均匀交错模型; + n_s 增大时趋近 |M|:高盲板清图必然踩到几乎全部残余雷) + R_res = n_s × E_dig (残余安全格奖励期望当量) + C_eff = C × (perfect ? P_perfect ÷ 100 : 1) (C = 清图奖励当量 = power 恒等〔clear_blast = 40〕; + perfect 谓词 = 板级零误标 ∧ ≥1 标中——Core Rule 13;×2 = 真实双份交付〔Core Rule 13#2〕) + X_res = E[踩雷] × V_mine (挖穿全池雷险期望当量——V_mine 同 F4) + Δr = ⌈(n_s + E[踩雷]) ÷ AP_r⌉ (轮数差 = 残余区期望总挖掘次数上取整——安全格 + 踩雷格 + 均耗 AP;雾态增量叠算见注) + s_ref = D_ref (结算强度参考 = 当量参考敞口——同一 D_ref,单一换算) + E_dig = 格子池加权(2026-09-14 重算批口径——刀/强治疗已移出格子池入注入池, + 不摊入单格期望;E[战斗获取] ≈ 1 由注入机制另计) + = (20×E[attack_up] + 16×E[defense_up] + 16×E[spark_aoe] + 15×E[heal_minor]) ÷ 67 + = (20×6.0 + 16×3.6 + 16×4 + 15×4) ÷ 67 ≈ 4.5 HP + (治疗按面值 = 上界读法——实现率折减入敏感性腿,同 F4③) + +显式假设登记:停手侧的 RAP 机会收益按 零 计价(RAP 边际效用不入判式—— +图源充裕时 RAP 边际低,零价假设对"推到底"偏利一分;本假设下 verdict 已为负, +若 C3 为 RAP 计价则结论只会更负——模型边界成文,非遗漏)。 + +保守地板式(A3 F1 条款⑤原文形式的可判定化——残余奖励不计入的下界要求): + C_eff ≥ X_res + Δr × s_ref +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 残余盲猜安全格 | n_s | float | [0, \|S\|] | (1−ι)×\|S\|;**域守卫:n_s < 1(残余集为空)⇒ 主判式不评估、信息端平凡腿接管**(零赌博板形不误判) | +| 挖穿期望踩雷 | E[踩雷] | float | [0, \|M\|] | 均匀交错模型 \|M\|×n_s/(n_s+1) | +| 清图奖励当量 | C | int | ≥ 1(A3 条款④) | B4 供给(clear_blast power=40,provisional) | +| 完美乘数 | P_perfect | percent | **{100, 200}** | SettlementTable 配置值;provisional 200(真实双份交付——Core Rule 13#2;换算入账恒 ÷100) | +| 单格奖励期望 | E_dig | float | (0, ∞) | 格子池加权推导(现行 4.5——v3 曲线/现行行集;评审口径带 ≈4.3–5.1,实现率/敞口敏感性浮动);实现率折减敏感性同 F4③ | +| 轮行动点 | AP_r | int | [1, 32] | A3 ActionEconomyTable(默认 8) | +| 雾负担比 | fog_burden_ratio | float | [0, 1] | A1 产物指标(付费格分母口径)——雾态 Δr 修正输入 | + +**四态验算(v3 曲线 + E_dig=4.5 面值口径 + C=clear_blast 40/完美 80;全部 provisional):** + +| 板形 | n_s | E[踩雷] | X_res | Δr(代价) | R_res | Δ_clear(C=40 / 完美 80) | 判定 | +|---|---|---|---|---|---|---|---| +| L1 无雾态(v3:10×10 d0.25,ι=0.88,\|M\|=23,\|S\|=77) | 9.24 | 20.8 | 62.3 | 4(48) | 41.6 | **−28.7 / +11.3** | ✗ / ✓ | +| L2 腿(v3:12×12 d0.25,ι=0.88,\|M\|=34,\|S\|=110——换锚同 F4) | 13.2 | 31.6 | 94.8 | 6(72) | 59.4 | **−67.4 / −27.4** | ✗ / ✗ | +| L1 × 满配语境(unit_count=3,V_mine=9) | 9.24 | 20.8 | 186.8 | 4(48) | 41.6 | **−153.2 / −113.2** | ✗ / ✗ | +| L2 腿 × 满配语境 | 13.2 | 31.6 | 284.5 | 6(72) | 59.4 | **−257.1 / −217.1** | ✗ / ✗ | + +- **实现率折减口径**(E_dig′ = 2.70 provisional——B4 预警②④联动):L1 = −45.3 / −5.3;L2 = −91.2 / −51.2——同向更差(完美翻正腿亦被折减吞没)。 +- **雾态增量(fog_burden_ratio 口径)**:雾格 = 雾元知识可推理安全格(P(安全)=1)→ 属确定格、两策略同挖、不进残余集;雾的条款⑤效应 = 链不可达雾格的全价 AP 抬高轮数——Δr 增量 = \|U\| × dig_ap_cost ÷ AP_r(A1 例板 U=4 → 0.5 轮;v3 曲线复算:(29.99 + 4) ÷ 8 = 4.25 → 上取整 5——较无雾基线 Δr=4 **+1 轮,增量不再被同轮吸收**,登记读数变化);雾池档位加成对冲入 R_res 侧(E_fog 读数见护栏 A——本批读数失效待重推);actuals fog_burden_ratio p50/p90 交付后重跑(对账清单 #6)。 +- **结构性解读**:残余盲猜区越大(n_s ↑),清图的全池雷险越逼近"踩到全部残余雷"——"是否承担盲猜风险是策略点"(game-concept 设计本意)被本判式定量证实;v3 曲线(d0.25/t0.88)使残余集系统性变大、非完美态被支配加深——与 F4 破线同源(曲线加难侧未同步补偿结算侧奖励/防御杠杆);完美高光与清图支配性互 feed 的设计意图保留(L1 完美双份翻正 +11.3),但非完美态仍负 = 被支配。 + +**C3/B4 约束包:** + +| 项 | 约束 | 裁决方 | +|---|---|---| +| ① C 量级上调整向 | 保守地板需求 L1 ≥ 110.3 / L2 ≥ 166.8(v3 曲线重推;现值 C=40 / 完美 80——缺口 **70.3 / 126.8**;原 46.1/91.8 需求系 v1 曲线旧值链产物,退役为历史记录);任何上调与护栏 B 口径联动复核 | B4/C3 | +| ② 或裁"清图非严格支配"为设计态 | 与 A4 定价硬约束②(清图锚随雾负担上浮——MVP 结构性不可闭合)同向;如裁设计态,A3 条款⑤注记的 P1 对冲弱化须同步声明,弃图率雾板分层观测加权监控 | C3(①②二选一) | +| ③ 多敌联动 | 满配语境深度为负与 F4 破线同向——unit_count > 1 行的放行判据 = 条款③⑤在全部登记行组合下同时成立(现行基线已破,判据自动收紧至曲线级重议——与 F4 结论④并案) | C3(双条款并案) | +| **C3 裁决回执(C3 修订批)** | **② 采信(非支配设计态)+ 计价基元换位重推**:clear_default 槽 clear_knife→clear_blast(instant_aoe 当场兑付全体存活敌,C = power = 40)——缺口重推 = 46.1−40 = **6.1**(L1)/ 91.8−40 = **51.8**(L2,v1 曲线口径);C 量级不上调 ✓;P1 对冲弱化同步声明已落 C3 OQ4、弃图率加权监控持续;actuals 交付后按 clear_blast 语义强制重跑(AC-40 同批口径,registry clause5_dominance.c3_ruling) | C3(已裁——①②二选一落定) | +| **B1 重算批重推回执(2026-09-14)** | v3 曲线 × E_dig=4.5 重推:主判式 L1 −28.7/+11.3(完美翻正)、L2 −67.4/−27.4、满配 −153.2/−113.2 与 −257.1/−217.1;保守地板需求 110.3/166.8(缺口 70.3/126.8);**裁决② 维持**(qualitative 结构不变:非完美态负、完美态 L1 翻正、L2 全负——真人零弃图初裁 OQ4 不受读数漂移影响);护栏 A/B 读数挂旧值链(见下表注)——W2 复核随 C3 收口批三选一落档 | B1(重算回执登记) | + +**两条方向性护栏(挖穿残余池口径下复核;读数挂旧值链——W2 复核待重推,见注):** + +| 护栏 | 判据 | 读数 | 突破条件 | +|---|---|---|---| +| A 雾板净当量优势带(B4 pricing_constraints③ 的 B1 判据;与残余池模型无关) | 每链不可达雾格净优势 = (E_fog − E_dig) − s_ref ÷ AP_r | 登记读数 (11.1 − 7.2) − 1.5 = **+2.4 ∈ (0, 3]**(v1 曲线/旧格子池口径);**2026-09-14 重算批方向性复核**:E_dig 修正 4.5 + 雾池构成变更(刀移出格子池——现行雾池加权当量 ≈4.9 面值口径)→ 读数 ≈ **−1.1 < 0,越下界**(雾纯税化风险——链不可达雾格全价 AP 成本超过当量增量);E_fog 现行池构成下的正式重推随 W2 批(C3 收口批三选一落档),本批登记方向性读数、不代裁 | E_fog > 11.7 → C3 雾池联动禁区,禁独立调参(判据式不变——阈值随重推复核) | +| B 防钓鱼——退化板刷清图奖励(输入登记:① \|S\|_min = min_safe_count 是 A1 行级 knob,调参时按当期行集重算、禁写死 50;② \|R₀\|_max = 20 系参考带经验值、无登记供给方 → 登记 A1 产出义务 R₀ 规模分布上界(F4 报告 C 扩列,缺口预检 9);③ DPS 期望严格读数的敌 DPS 参照系 v1 曲线 atk 值(4/4/6)——v3 atk(7/5/9/6/9)+50% 后 DPS 界随之上探,正式重推随 W2 批) | 上界判据 = ⌈(\|S\|_min − \|R₀\|_max) ÷ AP_r⌉ × s_ref ÷ 2 ≈ ⌈(50−20)/8⌉ × 12 ÷ 2 = **24**(D_ref 口径——与敌 DPS 无关,读数不受 v3 换锚影响) | 登记双读数(v1 曲线口径):D_ref 口径上界 24 → C_eff = 16 ≤ 24 ✓ / DPS 严格读数上界 ≈8 → C_eff = 16 越(clear_knife C=8/完美 16 口径);**W2 复核事项(跨评审已登记)**:C3 修订批将 C 提至 clear_blast 40/完美 80 后对两上界越线 1.7–10×——复核被跳过、三选一裁决(上界改判/接受+强化监控/回调 C)随 C3 收口批落档;本批不代裁 | 结构承载 = A1 min_safe_count + 布雷数下限 + A2 R₀≠S 三闸(B1 侧判据为对账复核,非运行期校验) | + +**P_perfect 默认值建议与相容性:** + +| 条 | 内容 | +|---|---| +| 建议 | ×2(SettlementTable.p_perfect = 200,provisional,调参所有权 C3;运行时载体域 {100, 200}) | +| ① | 单一线性换算不破坏——乘子作用于 C 当量换算之后、换算 ÷100 已成文(F1 对照表行内显式列),非新增分源换算率(条款①合规) | +| ② | 与条款⑤同向且是现参数下唯一使 L1 翻正的杠杆(+8.0)——完美高光与清图支配性互 feed;非完美态仍负 → C 量级上调约束不因 P_perfect 消解 | +| ③ | 触发谓词 = 板级 flag 记录(零误标 ∧ ≥1 标中),与踩雷正交(盲挖踩雷不破坏 perfect,标错旗才破坏——以"标旗未中"零值行留痕,Core Rule 5.2) | +| ④ | RAP 刷新路径零清图奖励 → 无 perfect farming 环(双份兑付不改此性质——刷新图无兑付点) | +| ⑤ | 与 F0 weaken 修订同向(标中旗价值 ↑ → perfect 可达性 ↑——高光 #2/#3 互 feed) | +| ⑥ | 上界受护栏 B 约束(登记读数 16 ≤ 24 系 v1 曲线 D_ref 口径;C=40/完美 80 越线裁决随 W2——完美加成不得触碰钓鱼线) | + +### F6 伤害A/B 充能与护甲克制结算(charge_and_armor——W3 规格补全;机制与结算归 B1,四字段数值投值归 C3) + +**结论**:伤害A/B 为挖掘期充能、战斗段主角先手攻击一次性消费的 flat 附加分量——池内加算、**不吃任何 percent 修正**(attack_up 只乘基础分量——B4 F4 同族拍板)、逐分量查 `armor_effectiveness` 甲效表后 floor 相加;充能池单轮生命周期(挖掘期累积 → 本轮战斗段消费 → 轮末归零,跨轮零存量);Output Range D_hero ∈ [0, ∞) 整数、零负值分支。 + +#### 推导 + +``` +累积(B1 facts 缓冲承载——零新事件、零特判): + A 池 = min(本轮 cell_revealed{kind=safe} 事件数 × damage_a_per_cell, damage_a_cap) + (累积时点 = 每轮挖掘期逐格充能——initial/chain/dig 全通道同待遇;cap 封顶 = 连锁轮失控 + 对冲,cap=0 = 不封顶结构位;damage_a_per_cell=0 = 机制关闭合法极端) + B 池 = 本轮 flag_judged{hit=true ∧ 资格未消费} 事件数 × damage_b_per_flag + (累积时点 = 授予段旗判定批——与 weaken 兑付同源判定、每雷实例一次去重〔资格键同 + A3 OQ5 口径〕;无 cap 字段——标中旗吞吐由 AP 预算天然限界) + +消费(战斗段主角先手攻击——F3⓪,单结算一次性清池): + base = floor( atk × (1 + clamp(P_hero_offense, −100, +100)/100) ) (percent 只作用于基础分量) + dmg_a = floor( A 池 × 甲效[dmg_a][target.armor_type] ÷ 100 ) + dmg_b = floor( B 池 × 甲效[dmg_b][target.armor_type] ÷ 100 ) + D_hero = base + dmg_a + dmg_b (逐分量查表 floor 后相加) + 甲效表 = SettlementTable.armor_effectiveness(键集 {base, dmg_a, dmg_b} × {none, a, b} 结构冻结; + 默认 base 恒 ×100 / dmg_a:a=150 b=50 / dmg_b:b=150 a=50——特化 ×150/无甲 ×100/对位 ×50; + 数值 knob 归 C3;armor_type 映射归 EnemyTable) + 轮末:A/B 池清零(与 decay 段同拍——跨轮零存量) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| A 池 / B 池 | — | int | [0, damage_a_cap] / [0, ∞) | 本轮充能池读值(B1 自持,公开可读——P2 还原面) | +| 伤害A 三参数 | damage_a_per_cell / damage_a_cap | int ≥ 0 | 默认 1 / 8(provisional 归 C3) | SettlementTable knob;cap=0 = 不封顶 | +| 伤害B 参数 | damage_b_per_flag | int ≥ 0 | 默认 2(provisional 归 C3) | SettlementTable knob | +| 甲效值 | e | int percent | {50, 100, 150}(现行默认矩阵取值域) | armor_effectiveness 查表;base 行恒 100 | + +- **验算(表读值记号,默认矩阵)**:对甲A 敌(bugbear)10 格挖掘轮——A 池 = min(10×1, 8) = 8 → dmg_a = floor(8×1.5) = 12;2 首标 B 池 = 4 → dmg_b = floor(4×0.5) = 2;base = 6(零 attack_up)→ D_hero = 20。对甲B 敌(ogre_chief):dmg_a = floor(8×0.5) = 4、dmg_b = floor(4×1.5) = 6 → D_hero = 16——甲B 对策 = 伤害B 充能(C3 联动网络 #6)。无甲敌(goblin):8 + 4 → D_hero = 18。 +- **cap 截断腿**:15 格连锁轮 A 池 = min(15, 8) = 8——溢出 7 格零充能、无"溢出未生效"行(充能非奖励、无兑付语义),截断仅入诊断计数。 +- **边界注记**:与乘区关系——A/B 分量在 percent 池乘区之外(敌→主乘区 F2 不作用于 hero→敌方向;hero_offense 池只乘基础分量);与 B4 刀同族——加法增量分量一律不吃 percent;P2 归因——A 池来源 = 开格(逐格可归)、B 池来源 = 首次标中旗(与 weaken 同判定批——"标中旗双收益线"归 W12③ 对账清单);明细行 = hero 伤害行分量二元组 {baseline = 基础分量, attach = dmg_a + dmg_b 合计}(A/B 拆分经充能池公开读值可还原,P2 不丢失);归属——机制与结算归 B1(本节 + F3⓪),四字段数值投值归 C3,甲型映射归 B2 EnemyTable。 + +### 边界参数总表 + +| 参数 | 边界 | 默认 | 出处 | +|---|---|---|---| +| percent 池 clamp | [−100, +100] pp,每池独立收口 | — | F2 | +| 池乘数域 | [0, 2](= 1 + pp/100) | — | F2 | +| m_w(weaken 行值) | [60, 85] | −65 | F0 裁决(瞬时当量口径) | +| 地板交叉层数 | ⌈100/m_w⌉ = 2(值域内恒 2) | — | F0 | +| P_perfect 载体域 | **{100, 200}**,域外加载致命 | 200(×2 双份交付) | Core Rule 13 | +| D_ref(当量参考敞口) | [1, 999] | 12 | SettlementTable | +| HeroTable.atk | [1, 999],0 加载拒绝 | 6 | 2026-09-11 拍板 | +| 伤害 D' | 整数 [0, ∞),零负值分支 | — | F3 | +| D' = 0 | **不调** apply_damage,产零值伤害行 | — | 负值分支禁令 | +| 取整 | floor,单一规则(结构性,非 knob) | — | F2 | +| 溢杀 | 丢弃,不转移、无负 hp 态 | — | F3 | +| 治疗 | hp' = min(hp + power, max_hp) | — | F3 | +| 伤害A 充能 | 单轮池 ≤ damage_a_cap(0 = 不封顶);轮末清零 | 1/格 · cap 8 | F6 / SettlementTable | +| 伤害B 充能 | 首次标中雷每雷一次去重;无 cap(AP 预算天然限界);轮末清零 | 2/雷 | F6 / SettlementTable | +| 甲效矩阵 | 键集 {base, dmg_a, dmg_b} × {none, a, b} 结构冻结;逐分量查表 floor 后相加 | 特化 ×150 / 无甲 ×100 / 对位 ×50 | F6 / SettlementTable | + +### 显式排除声明(归属表) + +| 对象 | 归属 | 依据 | +|---|---|---| +| P_perfect 乘数值(×2 provisional——真实双份交付载体,运行时域 {100,200})与其调参 | **C3** | Core Rule 13(机制本体归 B1,数值归 C3) | +| D_ref、E_dig、裕度目标带等全部本节 provisional 数值 | **C3**(调参)+ B1(对账重跑义务的执行方) | 各式 Variables | +| m_w 修订终值 | **B4/B1 锚 owner 共裁**(值域与默认建议由 F0 给出),调参所有权 C3 | B3 Rule 10 / A3 条款② | +| 敌我表值曲线(atk/hp/cap/unit_count/pattern) | **C3**(B2 行值供给) | B2 Tuning Knobs | +| 奖励行值/权重/tier/C 量级与差异化分支 | **B4/C3** | B4 F6 / A3 条款④ | +| 雷密度/可推理率/雾负担 actuals 分布 | **A1**(F4 报告 C 生产者) | A1 登记口径 | +| 清图总预算"够不够"的判、战斗长度/轮数曲线、遭遇构成 | **A1/A3 契约、C1/C3** | A1 F3 排除声明 | +| RAP 经济(刷新价、转存) | **A3** | A3 Rule 11 | +| 结算明细的呈现(高光行/零值行/聚合计数的可见形态) | **D1/B5** | Core Rule 14 | +| B1 只拥有:统一当量换算规则(F1)、乘区/地板/取整语义(F2)、clamp/溢杀/死亡判定数值边界(F3)、条款③⑤不等式的闭合判据与配对口径(F4/F5)、明细行协议的数值字段 | **B1** | A3 F1 五条款 + B2 义务③④ + B3 义务③⑤ | + +### 对账登记义务清单(本节落定后的回写义务) + +| 编号 | 登记内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | registry `status_effect.anchor_warning` 裁决回执:归一口径 = 每结算瞬时当量(总暴露分支关闭——schema 域禁止 + 重定参照路径不采纳);m_w 修订值域 [60, 85]/默认 65;"7 层触地板"预读更正(地板 = 乘数零点 −100 pp,交叉层数 = ⌈100/m_w⌉ = 2);参照对象 = attack_up 当量(registry 回迁口径) | registry / B3 | 已回写(2026-09-10 回执;重算复核更新腿读数) | +| 2 | registry `status_effect.mvp_content.weaken` + B3 GDD 行表/Tuning Knobs:magnitude −25 → 值域 [60, 85]/默认 −65(接口漂移回写);B3 AC-14 腿 3 非平凡性复查回执(16×65 = 1040 越域 ✓);B3 OQ1/OQ6 关闭回执 | registry / B3 | 已回写(2026-09-10) | +| 3 | registry `clause3_supply.direction_precheck` 闭合回执:统一当量重算值双口径登记(边际配对),pp 代理退役为历史记录;破线 → C3 调参约束登记 | registry / C3 | 已回写(2026-09-10)+ 2026-09-14 重算回执(现行值链破线读数) | +| 4 | registry 新条目 `clause5_dominance`:F5 主判式(挖穿残余池计价)+ 未闭合登记与 C3/B4 约束包(C3 已裁②非支配设计态)+ 两护栏结论 + actuals 重跑义务 | registry / C3、B4 | 已回写(2026-09-10)+ 2026-09-14 重算回执(v3 曲线读数) | +| 5 | registry `reward_table`/`reward_config` 注记:C_eff 口径(P_perfect 入账规则——÷100 成文 + 真实双份交付、载体域 {100,200};当量 = power 恒等);attack_up 死行注记已随基础攻击恒存在批退役(对照表现行 = attack_up 活行) | registry / B4 | 已回写(2026-09-10 回执;死行注记随基础攻击复活退役) | +| 6 | registry `board_gen_table.inferability_vs_p_safe` 消费回执:P(安全) 代理公式(残余盲猜端上界式)+ actuals p10 输入接线路径(A1 F4 报告 C) | registry / A1 | 待回写 | +| 7 | D2 SettlementTable schema(B1 回填 owner)+ registry `settlement_table` 条目(十二字段 = 原八字段 + damage_a_per_cell / damage_a_cap / damage_b_per_flag / armor_effectiveness——护甲克制批) | D2 / registry | 已回写(2026-09-10 回填;2026-09-14 随 W3 字段更新) | +| 8 | systems-index 门禁记录:B1 强制检查点三项(条款①②③⑤)裁决落定登记;A3 OQ1(对照表)、B4 OQ5(T_fog_min 推导式——对照表已落定,改推导式与否由 B4 裁)/OQ6(weaken 联动)状态回写。附注(新缺口):第二腿校准所需的连锁规模分布(c̄)无既定供给方——登记 A1 报告 C 扩列或 A2 telemetry 的归属 OQ | systems-index / B3、B4 | 待回写 | + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Uninit | 冷启动 / 清理完成 | 唯一出口 `begin_battle` | +| BattleSetup | `begin_battle` 受理 | 战斗开始序列;任一步拒绝 = fail-fast 上抛 | +| RoundActive | `notify_round_start` | 不干预挖掘,只缓冲事件流 | +| RoundWaiting | 轮次结束事务达成 | 执行七段结算;窗口段为唯一玩家输入点 | +| BattleOver | 判定段产胜负 | 战斗结束序列 → Uninit | + +**编排不变式**:同一时刻至多一个结算事务在飞(窗口为事务内暂停点非并发入口);段序恒全序执行(战斗段短路是段内规则非跳段);全部跨系统调用集中在 B1——任一下游被 B1 以外语调用 = 架构违约(AC-43 静态扫描域)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| 末图清图即胜利(cleared 与 battle_over 同结算) | 授予段先兑付清图奖励(含完美判定)→ 窗口当轮可选用 → 道具段照常 → 战斗段空转(无行动行)→ 衰减照常执行 → 判胜 → battle_over 清理;未选用件随 clear_inventory 消亡 + 反馈行 | B4 E15 | +| 战斗段 hero hp=0 | 立即短路——剩余敌不作用、不产行动明细行,径入判定段判负;短路后衰减段照常执行(结算完整性优先) | Core Rule 10 | +| 判定时 hero 与最后敌同时 hp=0 | 结构不可达(我方伤敌均先于敌方行动兑付);防御规则 = hero 先判 → 判负(纵深防御) | Core Rule 12 | +| 减伤/削弱合成后 D'=0 | 不调 `apply_damage`(B2 对 n≤0 拒绝——负值分支禁令实现面),改产零值伤害行(分量二元组 + 终值 0)——免伤与零输出必须可见 | 负值分支禁令 | +| 伤害超过目标剩余 hp(溢杀) | 溢出丢弃——无存续、无转移、无负 hp 态;行 value=D'、击杀标记为布尔(不记溢出量) | F3 | +| 敌为 charge 拍而本轮踩雷 | 伏击授予照常执行(授予段先于战斗段);query_attack 返回零作用 → 本结算伏击池全部蒸发(dur=1 轮末清零);战斗段零伤害;明细 = 授予行 + 蓄力行("这轮踩的雷白喂了"可对照) | B2 EC8 | +| 全体存活敌同拍 charge | N 行蓄力行、零 apply_damage、hero 承伤 0——合法状态非错误 | B2 EC | +| 伏击池 L=0 时敌攻击 | D_raw = 纯基线 atk(分量二元组 {atk, 0})——零伏击是 L=0 的自然读值非缺失分支 | B2 F2 | +| 同窗提交多件 | 仅首件受理生效,余件零调用 + 编排违例诊断;满血使用守卫保留为纵深防御(跨结算场景仍可达) | Core Rule 7 / AC-16 | +| 治疗包 power 超过当前缺血 | hp' = min(hp + power, max_hp) 部分回复、不拒绝(满血 = 零收益拒、超量 = 部分收益——两分支语义互斥) | F3 | +| 选定刀施加时已无存活敌 | 照常消耗(Used 不可逆)+ 逐件"已消耗未生效"明细行(零效果判定读 B2 存活集);单件制下不可达、逻辑保留为防御分支;战斗段照常空转推进 | B4 E10 / AC-20 | +| 窗口打开时库存为空 | 跳过恒合法;呈现面 D1 可整体不弹窗;结算段序零停顿继续 | B4 E8 | +| 挖掘期刷新 RAP 不足 | `request_refresh` 返回 false、状态不变 → `refresh_board` 原样上抛拒绝(刷新拒绝不是编排错误,是玩家决策的合法结果) | A3 EC13 | +| 同轮多次刷新成功(cap 内) | 每次成功独立走 `refresh_board` 全序(retire → generate → install → roll_board 恰一次/图)——挖掘期当场连刷合法;每次 generate/roll_board 消费新 scope;第三次提交 → cap 拒绝 | A3 EC15 | +| stopped/ap_exhausted 轮 | 出口分发恒 same_board(本编排无刷新提交面);回发 new_board = B1 违约(绕过 RAP"支出唯一")→ fail-fast 上抛 | A3 Rule 11 | +| 换图生成产物带非法产物标记 | 编排不拒绝产物(玩家永不卡死),上抛玩家侧呈现请求(数据面 = 非法标记;提示/补偿/回退形态归 D1);A2 `install_board` 若拒绝(不变式违例)= 程序级错误 → fail-fast(非玩家域) | A1 EC5 | +| battle_over 清理序列中某调用被拒 | debug 诊断 + 编排纪律修复;幂等守卫(各侧 false + 诊断)兜底,不中断清理序列剩余步骤(防半清理态跨战斗泄漏) | 编排纪律 | +| 战斗中表热重载请求 | `set_reload_gate(true)` 已置位 → D2 门拒绝返回 false | D2 EC8 | +| 衰减段调用时容器已空 | `decay_round()` 返回 0——合法结果、零明细行或聚合零计数 | B3 EC13 | +| 清图槽身份为 buff 且授予时已满层 | 零兑付 + "奖励未生效"行(合法例外);完美板高光行降级不发(完美谓词仍成立,当量入账 = 实际兑付 0);若仅第二份被拒(首份已落地)→ 高光行照发、value = 首份当量 + "奖励未生效"行一条 | B4 E11 / Core Rule 13#2 | +| 完美排雷谓词在 RAP 刷新图上 | 刷新图不产生清图奖励(无兑付点)→ P_perfect 不触发;板级 flag 计数随每次 board install 重置(跨图不累积) | Core Rule 13 | +| 板内既有标中又有误标 | 完美谓词不成立 → 正常兑付(无高光行)+ 本轮首个误标判定产"标旗未中"零值行(highlight=`perfect_broken`;重复上报去重)——零误标是全有或全无谓词、无部分完美档 | Core Rule 5.2 | +| 多敌(unit_count=3)同结算攻击 | 行主序逐敌执行循环、共享同一 L(池共享不消耗);任一敌作用后 hero 死亡 → 短路中止剩余敌(顺位即威胁序的可观测面) | Core Rule 10 | +| 道具段击杀造成死亡即不动手 | 该敌 Dead 后战斗段 `query_alive` 已不含它——不出现行动行(缺席即指针冻结的明细面);抢杀兑付 = 击杀标记进流水(感知数据前置) | Core Rule 9 | +| 零威胁组合(行 atk=0 ∧ cap=0) | 战斗可胜但零风险 = 内容态配置错误(hero atk=0 已被 HeroTable 域下界结构杜绝,战斗恒可定局);护栏 owner = 实现期 ADR/D2 跨字段校验;B1 观测锚 = 战斗轮数异常遥测(进 C3/OQ 清单) | B2 OQ9 并案 | +| 结算开始时 hero hp=0 | 结构不可达(hp=0 只产生于战斗段 → 短路直入判定段);防御断言:窗口打开前置检查 hero 存活,违反 = debug 断言(编排违例显式暴露) | 编排自守 | + +## 依赖 + +**上游**(除 C1/C2 外全部 Approved——B1 是项目首个全部玩法上游均已 Approved 的系统,无 provisional 上游假设): + +| 系统 | 性质 | 接口要点 | 状态 | +|---|---|---|---| +| D2 | hard | SettlementTable schema 承载(B1 回填 owner)+ ConfigDB 读取 + `set_reload_gate()` 唯一调用方;零结算随机流(流封闭不变) | Approved | +| A3 | hard | F1 五条款下游契约(B1 = 条款①②③⑤闭合 owner)+ `refresh_board` 挖掘期刷新编排 + `notify_settlement_done`/`notify_combat_end` 回发 + `last_round_end` 读取 | Approved | +| A2 | hard(订阅) | 三事件流订阅(facts 缓冲;A2 唯一回边的订阅端)+ `install_board`/`retire_board` 调用方 + Waiting 相位观测量 = 结算触发条件;事件到达序不承载语义 | Approved | +| B2 | hard | 单位接口全调用面(spawn/query_*/apply_damage/advance/default_target/ambush_grant_plan/despawn_all)+ B2 义务①–⑥ | Approved | +| B3 | hard | 状态容器五 API 全调用面 + B3 义务①–⑧;weaken 锚红警裁决 owner(F0 检查点) | Approved | +| B4 | hard | 奖励/库存十 API 全调用面 + B4 义务①–⑥(roll_board 恰一次 / 关卡末清库快照计数 / usable_now 组装) | Approved | +| A1 | 间接 hard 接线 | `generate(L, D)` 换图编排调用方 + actuals 分布 p10 对账输入(F4 报告 C)+ attempt 边界非 scope 事件 + EC5 零合法板 B1 半边上抛 | Approved | +| A4 | 对账锚(零运行时接口) | 条款⑤三腿入账(雷险消解/信息双向/AP 腿)+ 两方向性护栏 + 雾格零特判(B1 无变异分支) | Approved | +| C1/C2 | provisional | encounter 上下文 (L, D, hero_hp) 供给(begin_battle 入参);跨战斗 HP 持久性;战斗触发 | 未设计——MVP 冒烟直供(入参封装隔离) | + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| D1 | hard(数据供给,未设计——第 10 位) | 结算明细流水 = 统一数据源契约(Core Rule 14 行协议);承接呈现义务台账:A2×6 / B2×4 / B3×4 / B4×5 / B1 新增 ×3(高光行强调、完美排雷呈现、EC5 零合法板提示)——缺项即 blocking,随 D1 GDD 门禁评审核对 | +| B5 | soft(垂直切片) | 结算步骤流 = 战斗动画回放脚本源;溢出行/origin_cell 玩家可读形态(B3×B5×2 数据源) | +| D4 | soft(Alpha) | 音频挂点 = 明细行类型流(B4 音频挂钩点事件源) | +| D5 | soft(Alpha) | 教学口径数据源(来源标注/RAP 清零/库存清空/满层溢出/蓄力拍贪雷——经 B1 流水兑现) | +| C3 | soft(provisional) | 条款③⑤对账结果 = 调参约束输入(unit_count>1 破线约束 / 保守地板缺口 / 护栏 A/B 突破条件);P_perfect/D_ref 调参所有权 | + +- 双向一致性核对(systems-index 依赖分层表):B1 ← A3,B2,B3,B4,D2(第 3 Feature 层)· D1 ← A2,A3,B1 · B5 ← B1,D1 · D4 ← A2,B1 · D5 ← A2,A3,B1 · A2 事件面 referenced_by 含 B1(订阅边)。 +- C3 消费 B1 对账结论的事实依赖成立——C3 GDD 定稿时须回写索引(对账输出 = 调参约束清单,非运行时接口)。 + +## 呈现与 UI 契约(数据面义务——B1 无自有渲染面,呈现归 D1/B5) + +| 编号 | 数据面义务 | 归属/呈现 | +|---|---|---| +| 1 | 结算明细屏 = MVP 战斗本体:数据源 = Core Rule 14 流水行协议;每行 `value` 数值在暗色底上须高对比(明细屏是 MVP 数字密度最高的屏) | 屏级布局/节奏/滚动形态归 D1/B5 | +| 2 | 行类型区分 = 形状+色相双通道:未生效四类行(奖励未生效/已消耗未生效/使用被拒/溢出未生效)必须携带形状冗余(icon/划线等),防色弱玩家把"没生效"误读为普通行 | D1/B5 | +| 3 | 高光行数据面:perfect_sweep 高光行、标中旗 `highlight` 标记、清图奖励行 H2 强调标记——B1 只锁行字段;未生效类行按聚合计数呈现(数据可聚合字段已入协议) | 强调形态(放大/描边/音画同步)归 D1;`target`/`effect_id` 是多敌归因呈现与 icon/极性映射的直接数据源 | +| 4 | 零值伤害行可见性:免伤轮/零输出轮的零值行不得被"零值不显示"优化吞掉(归因链断点) | D1/B5 | +| 5 | 音频挂钩点(D4,Alpha)= 明细行类型流:道具(入库)/效果/伤害/判定/高光行 + 负反馈聚合(未生效类行数 >0 时单次结算至多一次——防逐行惩罚轰炸) | 挂点事件源归 B1 流水 | +| 6 | 道具使用窗口载荷 = B4 `query_inventory` 投影 + B1 组装的 `usable_now` 预判字段(满血治疗包禁选);选择序收集/跳过/窗口时机指示/触发政策归 D1;窗口选择序列 = 回放输入事件;窗口期输入与棋盘不竞争(单意图帧先例) | D1 | +| 7 | 结算明细屏数据锚 = 终局判定行(胜负画面的数据锚) | D1 | +| 8 | UX Flag:本系统有 UI 贡献(结算明细屏 + 道具使用窗口)——Phase 4 运行 /ux-design 为其创建 UX 规格先于编写 epics,引用 design/ux/[screen].md 而非本 GDD | systems-index 备忘(Phase 5d) | + +## Tuning Knobs + +### SettlementTable schema(单行全局,B1 回填 owner——Rule 4 豁免:单行全局常量/结构表,HeroTable/ActionEconomyTable 先例) + +| 字段 | 类型/域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| equivalence_ref_damage(D_ref) | int [1, 999] | 12 | ✓ | 当量参考敞口(F1);变更触发全对照表 + 条款③⑤对账重跑(联动义务,禁独立调参) | +| p_perfect | percent **{100, 200}** | 200 | ✓ | 完美排雷清图奖励第二份兑付触发(100 = 仅高光无第二份、200 = 真实双份交付);非整数倍数乘数无运行时载体,域外加载致命;上界受护栏 B 约束(双读数 24 / ≈8——裁决归 C3) | +| segment_order | StringList | [grant, item_window, buff_debuff, item, combat, decay, verdict] | ✗ 结构冻结 | 段序骨架的零字面量载体(拍板序);加载期校验恒等于拍板段序,违例致命(防实现乱序静默漂移) | +| source_label_map | Dictionary(source_class → 文案键) | 封闭枚举全覆盖 | ✗ 结构冻结 | source_class → D1 呈现文案映射键(P2 归因呈现侧数据源);键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack};新增键 = 接口漂移 | +| effect_pool_map | Dictionary(effect_id → pool) | attack_up→hero_offense · defense_up→hero_defense · weaken→enemy_output · ambush→flat_attach | ✗ 结构冻结 | 效果语义映射(F2);新 effect_id 上线须随批登记映射,悬空 effect_id = 加载期致命(引用型字段 dangling_ref_policy) | +| pool_clamp_lo / pool_clamp_hi | int(pp) | −100 / +100 | ✗ 结构冻结 | percent 池地板域(F2);乘数域随之 [0, 2](全表共八字段) | +| round_direction | enum {floor} | floor | ✗ 结构冻结 | 乘区取整方向(F2——单一取整规则,结构性非 knob) | +| damage_a_per_cell | int ≥ 0 | 1 | ✓ | 伤害A 开格充能/格(护甲克制批——充能与结算归 B1 战斗段,C3 投值) | +| damage_a_cap | int ≥ 0 | 8 | ✓ | 伤害A 单轮充能封顶(0 = 不封顶——防连锁轮失控) | +| damage_b_per_flag | int ≥ 0 | 2 | ✓ | 伤害B 标雷充能/首次标中雷(每雷实例一次去重) | +| armor_effectiveness | Dictionary {base, dmg_a, dmg_b} × {none, a, b} | 特化 ×150 / 无甲 ×100 / 对位 ×50 | 键集 ✗ 结构冻结 / 数值 ✓ | 伤害类型 × 护甲克制矩阵——主角攻击三分量(基础/伤害A/伤害B)各自查表后 floor 相加;敌种甲型映射归 EnemyTable.armor_type | + +- **加载校验**:effect_id 主键唯一(ConfigDB 既有);effect_pool_map 键 ⊆ StatusEffectTable 活行集(跨表悬空致命——B4→B3 首引激活先例同款);segment_order 违例致命;p_perfect ∈ {100, 200}(载体域,域外加载致命;100 = 完美必须 ≥ 不加倍的机制语义下界)。 + +### Knob 表 + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| equivalence_ref_damage | 12 | 全当量表膨胀 → 条款③左臂虚高、锚裕度失真(重跑强制) | 当量坍缩 → weaken 锚第二腿破线(m_w ≥ 60 下界的前提失效) | +| p_perfect | 200 | 载体域封顶 200(C 量级上调走 B4 clear 池 knob,须与护栏 B 口径联动复核:防钓鱼线 D_ref 读数 24 / DPS 严格读数 ≈8) | =100 → 第二份兑付不触发(完美仅剩高光行、无实质增量——情绪奖励感最弱档,机制空转) | + +- **非 knob(结构冻结,修订走 GDD 流程非调参)**:段序骨架、乘区结构(两池乘算)、floor 取整、池 clamp 域、明细行协议、零随机、结算触发 = Waiting 相位、调用序(query_attack→apply_damage→advance)、衰减恒执行。 +- **Knob 交互**:D_ref × m_w × m_d × duration_d(锚两腿与条款③ V_safe 共用 D_ref——🔴-1③:defense_up.magnitude/duration 已纳入"禁独立调参"联动网络;B4/B1 共裁 m_w、C3 投 m_d 时须同步核对 D_ref 并重跑条款②③⑤);p_perfect × C(clear 池当量)——护栏 B 联动;D_ref × 条款⑤ s_ref(同值引用——改 D_ref 即改结算强度参考,两条款重跑);damage_a/b × armor_effectiveness × EnemyTable.armor_type——C3 联动网络 #6(敌种甲型变更须复算 Boss/普通战 K 区间)。 +- **调参所有权**:全部 knob provisional 归 C3;每次调参须重跑 F1 对照表 + 条款②③⑤预检并同步 registry(接口漂移义务——B2/B4 同款条款);actuals p10 交付后的强制重跑义务见对账清单 #6。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | Core-Battle 全集成冒烟:`godot --headless --script`(脚本 extends SceneTree 以获得 quit(code);ConfigDB/RngService autoload 由脚本显式实例化) | +| 真假声明 | 真 A3(相位机全 API)+ 真 B2/B3/B4(class_name 直引实例化;多敌腿复用 `enemy_multi_unit` 夹具根)+ 真 A2 规则引擎(板走既有登记规格 b_spawn/b_flag/b_batch/b_chain/b_fog/b_mine_walk;挖掘/插拔/停手经 `settle_batch` 真驱动——冒烟扮演 D1 提交意图) | +| A1 桩 | 程序构造产物桩:换图 generate 调用点按登记序列返回 schema 坐标级程序构造板;每次调用经 `open_stream("board_gen")` 取号恰一次(与真 A1 取号契约同构,scope 轨迹断言真实);attempt 重试不重开 scope 的语义归真 A1(deferral);真 A1×A2×B1 端到端集成冒烟与 A1×A2 双包含端到端同槽(生效条件 = A1 实现落地) | +| C1/C2 | provisional:`begin_battle(encounter)` 入参冒烟直供(encounter 坐标复用 B2 行集登记 (L_g,D_g)/(L_a,D_a);hero_hp 按腿直供 max_hp/低值/缺血压值) | +| D1/B5 断言面 | 未设计 → 步骤流捕获列表(主断言面)+ 回发捕获(notify_settlement_done/notify_combat_end)+ set_reload_gate 调用捕获;窗口选择与刷新意图 = 冒烟扮演 D1 经 B1 提交面注入 | +| A2 事件载体 | 事件外发载体(signal vs 回调)归实现期 ADR——依赖真 A2 事件流驱动的 THEN 子集在 ADR 落地前为纸面契约;备选 = A2 半真(事件注入面直喂 facts 缓冲 + A3 真,断言口径不变);本节优先真 A2 全集成,半真仅为 ADR 前过渡载体 | +| 被测面 | `begin_battle(encounter)` · 窗口选择提交面 · 刷新意图提交面(refresh_board 挖掘期刷新编排)· 结算事务(内部触发,经 A2/A3 驱动到达);段序断言 = 步骤流行序(授予类行 < 窗口标记 < 效果行 < 道具行 < 伤害/蓄力行 < 衰减行 < 判定行——全序断言) | +| 只读观测量 | A3 ap/rap/phase/last_round_end · B2 query_alive/query_beat/query_attack/default_target · B3 query_active(六归因字段+快照载荷)· B4 query_identity/query_inventory · RngService 公开 scope 计数器(board_gen/reward_roll 双流——roll_board/generate 恰一次的观测面)· 诊断收集器 | +| 期望值纪律 | 全部 knob 按当前 ConfigDB 表读值推导、不写死;记号:D_ref(equivalence_ref_damage)/ P_pf(p_perfect)/ m_w(weaken.magnitude)/ m_d/D_a/M_a(defense_up)/ m_amb(ambush.magnitude)/ c_r(所在行 cap)/ a_g/h_g(goblin)/ a_e/h_e/m_e/u_c_f(bugbear)/ M_h(HeroTable.max_hp)/ A_h(HeroTable.atk)/ p_i(RewardTable.power)/ AP_r | +| 结构定值断言 | 合法集:segment_order = 拍板序、pool_clamp_lo/hi = −100/+100、round_direction = floor、effect_pool_map 四键、armor_effectiveness 键集 {base, dmg_a, dmg_b} × {none, a, b}、行类型封闭枚举 13 类、公共字段 + 行类型特有字段分层、source_label_map 键集封闭(含 enemy_action/flag_miss/hero_attack) | +| SKIP 与生效前提 | 依赖修订后基线的腿(如 m_w ≤ −51 使 2 层即越域;m_d=−30 使 defense_up 叠满脱离地板边界读法)前提不满足时输出显式 SKIP 防 CI 静默、不假失败;registry 对账 AC(AC-41)在回执落地前失败 = 裁决未兑现的正确信号(不写软口径)——现行回执在案(F0 裁决批 + 重算批),AC-36/41 按现行值链断言 | +| 登记常数 | A2 板登记常数与 B2 行集登记坐标直接复用不重制;B1 新增 σ 登记轨迹(身份依赖腿——如"双治疗包""刀全灭敌""清图槽身份",master=42、经 derive_at 测试通道推导、预推进按 B4 时序口径)+ 登记构造序列(板 × 意图序 × 期望行数);登记后第二人独立推导复核,失效处置 = 先复核登记、无误即实现缺陷;每用例先 `reset_diagnostics()` | +| 夹具策略 | 默认根 b1_default = D2 全表清单 + SettlementTable 默认行(D_ref=12 / P_pf=200 / 拍板段序 / source_label_map 封闭全覆盖 / effect_pool_map 四键 / clamp −100/+100 / floor / 充能三参数 1·8·2 / 甲效矩阵默认值);致命根 ×3 独立进程:b1_segorder_bad(段序置换)/ b1_poolmap_dangling(ghost_effect 键)/ b1_pperfect_oob(p_perfect=99);SettlementTable schema 程序构造承载域校验全谱;板 = A2 既有板规格复用;行为夹具根经 `--data-root=` 注入(仅 debug、不入 data/tables/);各 AC 声明其板/根、用例间无顺序依赖 | +| 致命类收窄 | 恰三行致命 manifest(独立 headless 进程、游戏通道):segorder_violation → ≠0 · poolmap_dangling → ≠0 · pperfect_oob → ≠0;负空间声明(编排自守 fail-fast 断言——new_board 违约守卫、窗口打开前置 hero 存活断言、一事件双转化守卫、单一结算事务在飞——违规路径不可由公开 API 构造)不经冒烟断言、不入 manifest,行为级证据 = 正腿断言 + AC-43 静态扫描;其余致命类别由 D2 AC-2 同类别 loader 级夹具承载 | +| out-of-scope | D1 呈现全域(生效条件 = D1 义务台账 A2×6/B2×4/B3×4/B4×5/B1×3 登记,缺项即 blocking)· B5 消费与可读形态 · D4/D5 音频教学(数据源 = 本节流水行)· C3 全部 provisional 数值(每次调参重跑 AC-35/36/37/39/40/44 对账族——D_ref × m_w × m_d × duration_d 禁独立调参)· A1 actuals p10 重跑 · 条款⑤护栏 playtest 观测面(弃图率雾板分层/速杀退化/零伤害死局遥测——B1 仅供遥测锚)· m_w 修订终值 · flag_miss 惩罚源路径 · C1/C2 真实供给 · 深度高光形态 · 事件外发载体 ADR · attempt 重试细节 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | `b1_default` 经 `--data-root` 注入干净加载 | 冒烟脚本读 SettlementTable | 八字段在位:segment_order ≡ 拍板序 `[grant, item_window, buff_debuff, item, combat, decay, verdict]`(结构定值恒等断言);effect_pool_map 四键逐一 = 定稿映射;clamp −100/+100、round_direction=floor;D_ref 仅域断言 [1,999]、P_pf 域断言 {100,200};source_label_map 键集 ⊇ B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack};同根跑 B4/B3/B2 默认根加载零跨表冲突诊断 | Core Rule 16 / schema 定稿 | +| AC-2 | segment_order 置换根 `b1_segorder_bad`(如 item_window 与 combat 互换) | 独立 headless 进程游戏通道加载(collect_only=false) | 进程非 0 退出且错误清单含 segment_order 类别(防实现乱序静默漂移的进程级落点) | Core Rule 16/4 | +| AC-3 | 映射含 `ghost_effect` 键根 `b1_poolmap_dangling` | 独立进程游戏通道加载 | 非 0 退出、错误含跨表悬空类别(引用型字段 dangling_ref_policy) | Core Rule 16 | +| AC-4 | SettlementTable schema 类直引实例 | `validate()` 作用于 p_perfect ∈ {99, 150, 1000} | 各含域错误类别(载体域 {100,200}——非整数倍数无运行时载体);p_perfect = 100 与 200 零诊断;腿 2:`b1_pperfect_oob` 独立进程加载 → 非 0 退出(manifest `pperfect_oob`) | Core Rule 16/13 | +| AC-5 | 全新 B1(Uninit)+ 真 A3(Idle)+ 真 B2/B3/B4 全新实例 + A1 桩登记首板 b_spawn + master=42 | `begin_battle(encounter)` | 步骤流/调用捕获全序恰一次:set_reload_gate(true) → B3 notify_battle_start → **encounter 骰(unit_count 区间定本战实际数量——遭遇随机化增补)** → B2 spawn(override 数量)→ A2 install_board + B4 roll_board → A3 notify_round_start(phase=Active);**encounter scope = +1**、reward_roll scope = +1、board_gen scope = +1(各恰一 scope);query_alive(enemy) = 本战实际数量个行值快照单位(默认根 unit_count=1 定值行骰退化为 u_c);A2 事件捕获含初始展开批;B3 query_active 双侧空 | 编排专章战斗开始序列 / Core Rule 16① / B4 义务⑥ / C3 修订批 X-6 | +| AC-6 | 构造至 A3 Waiting 且 last_round_end 可用(stopped 轮——经真 A2 `settle_batch([stop])` 到达) | 腿 1:结算事务受理;腿 2:装载层置换 {flag_judged 批, round_end} 两组到达先后分别驱动(捕获-重放,不增设 B1 注入 API) | 腿 1:触发判定只读 A3 相位与 last_round_end(事件捕获序 = A2 管线序,结果正常);腿 2:两遍结算步骤流逐行相同(到达先后不承载语义) | Core Rule 2 / A2 义务① | +| AC-7 | b_flag 满构造结算(挖掘 ≥1 奖励格 + 插 (1,4) hit 旗 + 停手收轮;σ 登记使奖励格含道具类身份) | 结算事务解析 | 步骤流行序全序断言:授予段行 < 窗口标记(open——段界锚)< close/skip < 效果行 < 道具行 < 伤害/蓄力行 < 衰减行 < 判定行;任一段缺行以段界锚与行类型域断言序(空段合法);窗口标记恒存在(零选择 = skip 恒登记);segment_order 表值经 AC-2 恒等拍板序——实现读表驱动非硬编码 | Core Rule 4 / B4 义务① / Core Rule 14 | +| AC-8 | b_batch 部分挖掘后 stopped 收轮、结算完成(判定行 = 继续) | 出口分发 | 回发捕获恰一条 notify_settlement_done(same_board) → A3 notify_round_start(Active、ap=AP_r 硬重置、rap 保留);A2 板状态延续(旗集/已开集不变);board_gen/reward_roll scope 计数器零变化 | 编排专章继续腿 / A3 Rule 5 | +| AC-9 | b_flag 登记构造轮(挖掘 n 格含 m₀ 雷格 + 插 f 旗含 h₀ hit) | 轮末结算;同轮事件批重复投递(装载层故障注入) | 每格受理恰一次:B4 grant 恰 n_safe 次、B2 ambush_grant_plan 恰 1 次、apply_effect(weaken) 恰 h₀ 次、apply_effect(ambush) 恰 granted 次;处理序 ≡ A2 cell_revealed/flag_judged 行主序(B3 义务①落地);重复投递 → 步骤流与容器终态 = 单次投递结果(缓冲去重);B3 不去重、B4 幂等仅兜底(诊断零"重复受理") | Core Rule 3/5 / B3 义务①② / B2 义务② / B4 义务② | +| AC-10 | σ 登记挖掘格身份含 buff 类与道具类各 ≥1(腿 1);b_spawn install 后首轮(初始展开 35 格已发生——channel=initial、reward_eligible=false)(腿 2) | 授予段解析 | 腿 1:buff 格 → apply_effect(effect_id, source={safe_cell, origin_cell, round_index})(六归因字段逐一 = 调用实参);道具格 → stock(item)(库存逐件来源 {cell, round} 可归);即时类当场兑付:instant_aoe = 逐存活敌 apply_damage(u, power) + 单条伤害行(target=all_enemies + count 字段)、instant_heal = clamp 回复 + 治疗行(满血 → "奖励未生效"行零兑付)——零 stock 零窗口;B4 只定"给哪行"、层数语义归 B3 合并(同轮双 buff 格 → 单实例 L=2);腿 2:grant/stock/apply_effect 捕获全零(R₀ 不入奖励库);channel 字段零特判(同板 chain/dig 格正常入库成对照) | Core Rule 5.1 / A2 义务② | +| AC-11 | σ 登记使同轮/邻轮 defense_up 达 M_a 层满 | 第 M_a+1 层授予受理(B3 apply 返回 false) | 产"奖励未生效"行:零折算、携带可聚合计数/批次字段、source 归因完整;B3 容器逐字不变(拒绝原子);连锁批量授予溢出 = 显式接受(b_chain 13 格批量腿:溢出件逐件入行、零转换补偿) | Core Rule 5.1 / B4 义务② / B3 义务⑧邻接 | +| AC-12 | b_flag 插 (1,4) hit 收轮(腿 1);续轮再停手(A2 重报 {(1,4), true})(腿 2);拔 (1,4) 重插再收轮(腿 3);AC-28 换图后新板同坐标旗靶(腿 4);插 (0,3) miss 旗收轮(腿 5) | 结算 | 腿 1:apply_effect(weaken, source={flag_hit, (1,4), round_index}) 恰一次 + 效果行携带 highlight 标记 + 资格消费入集合;腿 2:零 weaken 兑付、零明细行(重复兑付从未发生)+ debug 诊断可选、资格集合不变;腿 3:仍零兑付(资格以雷格实例为键);腿 4:兑付恢复(新实例资格重置);腿 5:"标旗未中"零值行恰 1 条(source=flag_miss、value=0、highlight=perfect_broken、聚合计数)+ 续轮重报零新增行(每雷实例一次去重)+ 换图新板重新产行 | Core Rule 5.2 / A2 义务③ / Core Rule 13 | +| AC-13 | b_spawn 挖尽未开 8 格触发 cleared + σ 登记清图身份(腿 1);σ 登记清图身份 = defense_up 且已满层(腿 2);AC-28 场景 refresh 图(腿 3) | 结算授予段 | 腿 1:grant_clear() 恰一次(同批次重调幂等兜底——诊断零新增)、兑付分流同 AC-10(source=clear_reward)、当轮窗口即可选用;腿 2:零兑付 + "奖励未生效"行 + 完美板高光行捕获零次(首份即零兑付不发高光;完美谓词仍成立,对账入账 = 实际兑付 0);对照腿:仅第二份被拒(首份落地)→ 高光行在案且 value = 首份实际当量 + "奖励未生效"行一条;腿 3:refresh 图 grant_clear 捕获零次(roll 照常、兑付不调) | Core Rule 5.3 / B4 义务③ / B3 OQ4 | +| AC-14 | b_flag 登记构造轮:单轮挖掘雷格 mines_r = 6(登记走位序列;cap = c_r = 4 表读值) | 结算授予段 | ambush_grant_plan(6, c_r) 恰 1 次 → {granted=4, overflow=2}(守恒);前 4 颗(事件规范序)逐颗 apply_effect(ambush, source={mine_hit, origin_cell, round_index})(B3 伏击池 L=4);overflow 2 颗逐颗产"溢出未生效"行(含来源 + 聚合计数);战斗段各敌伤害行 attach 分量 = 4×m_amb(池读值即时性);轮末衰减行 k 含伏击实例(dur=1 蒸发);重复投递判别同 AC-9 | Core Rule 6 / B2 义务②⑥ / B3 义务⑧ | +| AC-15 | 战斗中 hero 满血 + σ 登记库存含治疗包(腿 1);扮演 D1 强制提交满血治疗包(腿 2);提交多件 [刀₂, 刀₁] / 单件 / 零选择(腿 3);无库存轮(腿 4) | 结算到达窗口段 | 腿 1:窗口清单 = query_inventory 投影 + B1 组装 usable_now(治疗包 false——B1 读 B2 hero hp 判定,B4 诊断零"血量查询"新增);伤害道具恒 true(窗口打开时存活敌 ≥1);腿 2:"使用被拒"行 + 包 InStock 不变(use 未调);腿 3:多件 → 仅首件受理(违例诊断、刀₂生效刀₁不消耗);单件 → 生效;skip → 窗口标记 close/skip、段序零停顿、库存不变;腿 4:窗口标记照常 skip(呈现政策归 D1——数据面 = 窗口清单空) | Core Rule 7 / B4 义务④前半 / D1 义务②对侧 | +| AC-16 | begin_battle hero_hp = M_h − 2 + σ 登记库存含治疗包 ×2(power = p_i) | 窗口提交 [包₁, 包₂] | 仅包₁受理生效(治疗行 hp' = min(M_h−2+p_i, M_h)——部分回复钳满)、包₂零调用(InStock 不变)+ 编排违例诊断("窗口至多选用一件");满血守卫保留为纵深防御(跨结算场景仍可达:本结算被拒后下一结算窗口再判) | Core Rule 7/EC / B4 义务④ | +| AC-17 | 同轮两次 weaken 授予(互异 origin_cell)(腿 1);defense_up 行与 weaken 行同轮(腿 2);`enemy_multi_unit` 根同轮 weaken L=1(腿 3) | 增益/减益段解析 | 腿 1:效果行恰 2 条(按 apply_effect 调用流水展开、非按容器单实例),每行 origin_cell 逐一 = 授予值、effect_id=weaken、value = 该次授予贡献值(L×m_w 投影)、pool_value = 池合成值(两行各 −65 贡献、pool_value = −130——合成值不入 value 字段);腿 2:效果行 pool 字段经 effect_pool_map 自 effect_id 派生 = hero_defense / enemy_output;腿 3:全体存活敌伤害行均含减伤合成且 target 逐一可归(side-wide 多敌兼容读法) | Core Rule 8 / B3 义务③⑤⑦ | +| AC-18 | 同结算 weaken L=1 存活 + σ 登记库存含刀(腿 1);`enemy_multi_unit` 根 + σ 登记刀 ×3(每结算至多一件、跨结算序列)(腿 2);hp=2 残敌 + p_i=4 刀(腿 3) | 道具段 | 腿 1:伤害行 value = p_i 恒定、零乘区(分量字段无池修正项——"不吃任何 percent 修正"的行级判别);腿 2:每条伤害行 target = default_target() 返回(顺位最前存活敌)、击杀标记出现于目标 hp ≤ p_i 的件、[0] 死后下一结算顺延 [1]→[2]、击杀标记共 3 个;query_beat 冻结(指针冻结进流水);后续结算战斗段 query_alive 不含它、零行动行;腿 3:死、行 value = p_i、击杀标记布尔、无溢出量字段、零转移(下一件打 default_target 后继) | Core Rule 9 / B2 义务⑤ / B4 义务④ | +| AC-19 | hero 满血 + 库存治疗包(腿 1);AC-20 场景敌已全灭 + 满血 + 提交治疗包(对照腿) | 提交选择 | "使用被拒"行、零 use 调用(不烧包);满血守卫 = 表局部判定(max_hp 对比)、敌全灭构造下仍拦截(守卫不读存活集的不对称可判定形式);clamp 部分回复见 AC-16 | Core Rule 9 / B4 义务④ / EC | +| AC-20 | 单件制 + 窗口结构推论(窗口打开时存活敌 ≥1 ∧ 单件恒打顺位最前存活敌) | 负空间声明(违约路径不可由公开 API 构造) | 窗口提交的刀恒有目标——"已消耗未生效"行在单件制下不可达;零效果判定逻辑保留为防御分支(读 B2 存活集),恢复多件制时本 AC 随批复活;击杀标记断言由 AC-18 腿 2 承载 | Core Rule 9 / B4 义务④ / EC | +| AC-21 | encounter=(L_g,D_g) 单 goblin(腿 1);(L_a,D_a) 连续结算(腿 2);全结算 advance 捕获(腿 3);`enemy_multi_unit` 根(腿 4) | 战斗段解析 | 腿 1:调用捕获序 = query_attack → apply_damage → advance(每存活敌恰一次 advance)+ 衰减行前完成;腿 2:beat 轨迹 = p_e 序、每结算 advance 恰 1;腿 3:advance 捕获实参 ∈ 敌句柄集、零 hero 句柄(B2 对 hero 句柄拒绝为兜底);腿 4:行动/伤害行序 = 生成序且每行 target 字段可归至对应单位 | Core Rule 10 / B2 义务① | +| AC-22 | b_mine_walk 单雷轮(L=1)+ goblin attack(腿 1);登记构造轮 L_amb=2 + weaken L=1 同轮(腿 2);b_flag 双 hit 旗同轮(weaken L=2;非平凡前提 m_w ≤ −51——F0 修订回写后恒满足,不满足时 SKIP 标记)(腿 3);defense_up 叠满同结算 P_pool = 2×m_d(腿 4) | 战斗段攻击拍 | 腿 1:伤害行携带 {baseline=a_g, attach=m_amb, 终值}、source_class=enemy_action、target = 受击单位句柄;L=0 轮 attach=0(自然读值);腿 2:D' = floor(D_raw × (100+P_e)/100 × …) 按表读值推导;腿 3:效果行显合成值 2×m_w 与终值两端、伤害行 value=0、apply_damage 捕获零次、B2 hero hp 不变、诊断零新增;weaken dur=1 轮末蒸发(下轮恢复常态伤害);腿 4:现行 −30 下 P_pool = −60 域内、乘数 0.4 无 clamp 腿——D' = floor(D_raw × 0.4 × …) 按表读值推导(免伤轮现行可达路径 = 腿 3 weaken 饱和;地板恰边界读法 P_pool = −100 由 F2 判据函数输入断言承载——AC-37) | Core Rule 10 / F2 / F3 / B3 义务③ / EC | +| AC-23 | (L_a,D_a) 结算 1 attack 后结算 2 构造挖雷 ×1(L_amb=1)(腿 1);`enemy_multi_unit` 根同步轮转至 charge 拍(腿 2) | 战斗段(charge 拍) | 腿 1:蓄力行(数字兑现面)+ query_attack 读值零作用 + 零伤害行、hero hp 不变;授予段伏击授予行在案(先授予后蒸发对照);轮末衰减清池(下轮 attach=0);腿 2:恰 u_c_f 条蓄力行、零 apply_damage、hero 承伤 0(合法状态非错误) | Core Rule 10/EC / B2 EC8 对侧 | +| AC-24 | `enemy_multi_unit` 根 + begin_battle hero_hp = 4 直供 | 首结算战斗段(敌[0] attack 分量 ≥ 4) | 敌[0] 伤害行后 hero hp=0 → 短路:敌[1]/[2] 零行动行、零 advance 捕获、径入衰减段(照常)→ 判定行 = 战斗失败(hero 先判防御腿——双死结构不可达不构造,判定行次序字段可读);battle_over 序列触发 | Core Rule 10/12 / EC / B2 义务③ | +| AC-25 | `enemy_multi_unit` 根 + 登记轮 L_amb = 2(双雷) | 战斗段逐敌解析 | 敌[0]/敌[1]/敌[2] 伤害行 attach 分量均为 2×m_amb(共享、非均分/消耗——每敌 query_attack 读同一 L);中途无池写捕获 | Core Rule 10 / B2 F2 | +| AC-26 | 任一 AC 场景(腿 1);末图清图胜利结算(AC-13 腿 1 场景 + 敌已全灭构造)(腿 2);全 dur=1 上轮清空后的结算(腿 3) | 结算 | 腿 1:衰减行恰 1 条/结算(k 记入行值)、decay_round 调用捕获恰 1 次;腿 2:判定行 = 胜利之前衰减行在案(先兑付后衰减时序契约;battle_end 全清兜底使两序同终态、次序钉死为段序);腿 3:衰减行 k=0(合法零行/聚合零计数) | Core Rule 11 / B3 义务④⑥ | +| AC-27 | 敌全灭结算(腿 1);失败(腿 2);继续(腿 3) | 判定段 | 腿 1:判定行 = 战斗胜利 → battle_over 序列(AC-31);腿 2/3:见 AC-24/AC-8;判定行公共字段 + 终局行在案(胜负画面数据锚) | Core Rule 12 | +| AC-28 | b_spawn cleared 判定 + A1 桩登记板序列 [板₂](程序构造、坐标级登记)(腿 1);桩登记返回带非法产物标记板(腿 2) | 出口分发 / install | 腿 1:调用捕获全序恰一次:A2 retire_board → B4 retire_mapping → A1 generate(board_gen scope +1——attempt 重试零额外取号)→ A2 install_board → B4 roll_board(reward_roll scope +1——每图恰一次)→ notify_settlement_done(new_board) → notify_round_start;新板资格格 roll 照常、grant_clear 零调用;完美计数随 install 重置;腿 2:install accepted(编排不拒产物)→ B1 上抛玩家侧呈现请求(回发捕获含呈现请求事件 + 数据面 = 非法标记,形态归 D1);A2 install 拒绝(不变式违例)= fail-fast 上抛(程序级非玩家域) | 编排专章换图腿 / B4 义务⑥ / EC | +| AC-29 | Active 且 rap ≥ rap_refresh_cost(腿 1);同轮第二次提交(腿 2);第三次提交(腿 3);rap < cost(腿 4) | 扮演 D1 经 `refresh_board` 提交刷新 | 腿 1:A3 request_refresh → true、rap=0、phase 不变(无结算段、无 round_end)→ 换图全序恰一次(A2 retire → A1 generate → A2 install → B4 roll_board;board_gen/reward_roll scope 各 +1);同轮续挖可证新图;腿 2:第二次完整编排;腿 3:拒绝(resource_face——refresh_cap_per_round=2);腿 4:false、状态不变(非编排错误) | 编排专章 / A3 Rule 11/EC13/EC15 | +| AC-30 | stopped 轮与 ap_exhausted 轮(各构造)(腿 1);cleared 轮(腿 2) | 出口分发 | 腿 1:notify_settlement_done(same_board) 恰一条、new_board 回发零次、retire/generate/roll 捕获全零(本编排无刷新提交面);fail-fast 守卫半 = 自守 debug 断言(内部违约路径不可由公开 API 构造——负空间,契约源 = registry action_economy.rap_rules,存在性由 AC-43 扫描域 + 代码评审承载);腿 2:cleared ⇒ new_board 恰一(AC-28 全序——两腿合成契约全貌) | 编排专章 / A3 rap_rules / 强制检查点③ | +| AC-31 | 判定行 = 胜/负任一(腿 1);末层 victory 或 defeat(腿 2);清理序列注入重复调用(诊断注入构造)(腿 3) | battle_over 序列 / `level_clear()` / 清理中 | 腿 1:捕获全序恰一次:B4 retire_mapping → B2 despawn_all(query 双侧空)→ B3 notify_battle_end(query 双侧空)→ A3 notify_combat_end(phase=Idle、rap=0)→ set_reload_gate(false);clear_inventory 不在 battle_over 序列中(缺席本身为断言腿:battle_over 后 query_inventory 逐字不变);腿 2:clear_inventory 恰一次 + 返回清空前快照计数("剩余道具 ×N 随关卡结束清空"反馈行数据面;折算结构位归 C2);此后 stock/use → false + 诊断;腿 3:debug 诊断 + 序列剩余步骤照常完成(幂等守卫兜底 false);后续 begin_battle 正常受理(防半清理态泄漏尾腿) | 编排专章 battle_over/level_clear 腿 / B4 义务⑤ / EC | +| AC-32 | AC-5 战斗开始序列完成后(gate 捕获 = true 在案)(腿 1);AC-31 battle_over 完成后(腿 2) | 经 ConfigDB `reload()`(debug 构建) | 腿 1:返回 false、ConfigDB 值不变、config_reloaded 不发("战斗中真的被置位"由编排真调用承载);战斗中经测试通道换根直调 load_tables(collect_only=true) 读值不受门约束(通道区分);腿 2:reload() → true 且 config_reloaded 恰一次 | Core Rule 16 / D2 EC8 / 强制检查点① | +| AC-33 | 两套全新全栈实例(同 master=42、同根、同板登记序列、同 encounter);判别腿:仅窗口选择序列不同(同意图) | 同一意图序列 + 窗口选择序列驱动两遍(定长场景:含挖掘/连锁/插旗/停手/窗口选用/刷新换图至少各一) | 两遍步骤流逐行相同(行数/行序/全字段含 value 与归因——target/effect_id/pool_value 均为输入的确定函数,逐行相等断言域含之);判别腿:仅窗口选择序列不同 → 步骤流在窗口标记处分歧、其前逐行相同(窗口选择序列确为回放输入);确定性来源行为面:B1 全程零 RNG 调用(AC-43)+ 表值快照 + attempt 非 scope 事件 | Core Rule 15 / 强制检查点② / D2 AC-15 | +| AC-34 | AC-29 腿 2 连环刷图场景 | 任一结算回放切点取快照(master_seed + 双流 scope 计数器 + 意图序列 + 窗口选择序列) | 无在飞流实例跨界(切点时刻双流计数器 = 已完成 install 数);续玩快照恢复驱动 = 端到端集成冒烟 deferral(生效条件 = D3 实现) | 编排专章回放切点 / Core Rule 15 | +| AC-35 | b_flag 构造零误标 ∧ ≥1 标中(仅插 (1,4) hit 旗)→ 挖尽 cleared(腿 1);同构造 + 插 (0,3) miss 旗(腿 2);全板零旗 cleared(腿 3);AC-28 换图后新板零 flag_judged(腿 4);b_fog 雾内插旗(腿 5) | 清图兑付 / 结算 | 腿 1:高光行在案(value = 实际有效兑付当量)+ 清图奖励身份第二份兑付捕获恰一次(道具类身份 → stock 恰 2 次 / buff 类身份 → apply_effect 恰 2 次——玩家实得两件;grant_clear 本身仍恰 1 次;等价入账 = power × P_pf ÷ 100 记号推导);腿 2:无高光行、正常兑付 + "标旗未中"零值行在案(highlight=perfect_broken)(全有或全无谓词、无部分完美档);腿 3:无高光行(hit=true = 0 不满足谓词);腿 4:完美谓词域 = 本板自 install 起(跨图不累积——构造前板含误标 + 换图后新板零误标有标中 → 新板 cleared 触发高光,重置判别正腿);刷新图本身零 grant_clear 无兑付点;腿 5:雾内插旗计入误标(hit=false)→ 破完美(无变异分支) | Core Rule 13 / EC×2 | +| AC-36 | 默认根 + 登记构造(每活行一 B3 实例:defense_up L=1 / weaken L=1 / ambush L=1×k=1;clear_blast 经 grant_clear 快照) | 结算兑付读值(query_active 快照载荷 / grant_clear power)经统一换算式 E(·)(SettlementTable D_ref 读值) | E_inst 结论与登记对照表一致(F1 重算回执版):E_defense = \|m_d\|/100 × D_ref、E_attack = 0.50 × D_ref、E_weaken = \|m_w\|/100 × D_ref、E_ambush(k=1) = m_amb、E_clear = power ×(perfect ? P_pf ÷ 100 : 1)(真实双份——完美板两件实得、总当量 = 2×power;单一线性换算——各行共用同一 D_ref 读值);锚两腿断言:E_weaken > E_attack ∧ E_weaken ≥ E_dig 推导值(登记常数——重算批回执值);attack_up 活行:hero_offense 池映射在位 ∧ 主角先手攻击伤害行 hero_offense 消费点在案(F3⓪ 基础分量——Core Rule 10 / F3 对账;attack_up 活行口径) | F1 / Core Rule 13 / B3 义务⑤ | +| AC-37 | F2 闭合判据函数(语义映射 → 池加算 → clamp → 乘区 → floor)+ 登记构造 | 以登记构造跑 F2 三例读法(weaken AP 界 8×m_w / 表界 16×m_w / defense_up 叠满 2×m_d——全表读值记号) | 判据结论逐一与 F2 表一致:两越域例 clamp 后 M=0;叠满例 M = 1 + 2×m_d/100 域内(现行 −60 → 0.4,无 clamp 腿);地板恰边界读法(M=0 未越域)以判据函数对 −100 输入断言承载;地板交叉层数 = ⌈100/\|m_w\|⌉ = 2(表读值推导);取整方向 = floor 单一规则(半值例断言);行为级交叉锚 = AC-22 腿 3 | F2 / B3 义务③ | +| AC-38 | F3 判据函数(三分支全序 + 负值禁令 + 死亡边界)+ 登记例(goblin L=2 → weaken → floor 3 记号 / knife_adv p_i vs hp=6 残敌 / D'=0 分支) | 以登记例跑判据 | 结论与 F3 一致:零负值(任意输入组合判据函数输出域 ≥ 0——参数化扫描)、Dead ⇔ hp=0 零负 hp 中间态、hero 先判优先级;行为级交叉锚 = AC-18/20/22/24 | F3 / B2 义务③ | +| AC-39 | F4 闭合判据函数 + 登记常数(C3 v3 行值总表:10×10 d0.25 → \|M\|=23/\|S\|=77、12×12 d0.25 → \|M\|=34/\|S\|=110;ι = 行 target 代 p10 乐观代理;m_d/D_a、m_amb、D_ref 全表读值/登记推导) | 对 L1/L2 两板形与满配语境(unit_count=3)跑配对主式 | 结论与登记一致(双口径断言——重算批回执):L1 裕度 ≈0.48× ✗ / L2 ≈0.47× ✗(锚行口径;期望口径 E_dig=4.5 → ≈0.60×/0.58× 同向破线——双口径登记)/ 满配语境 ≈0.16× 深度破线 ✗ → C3 曲线级约束输出(V_safe 侧杠杆重议——联动网络,F4 结论④);适用域守卫腿:登记构造 n_s < 1(残余集为空)→ 主式不评估、信息端平凡腿接管;信息端平凡腿 P(安全)=1 → V_safe > 0 恒真;registry clause3_supply 重算回执存在性断言;A1 actuals p10 交付后强制重跑 | F4 / B2 义务④ / 强制检查点条款③ | +| AC-40 | F5 闭合判据函数(修后模型:n_s = (1−ι)×\|S\|、E[踩雷] = \|M\|×n_s/(n_s+1)、X_res = E[踩雷]×V_mine、Δr = ⌈(n_s+E[踩雷])÷AP_r⌉、C_eff = C×(perfect ? P_pf ÷ 100 : 1))+ 登记常数(v3 曲线板形同 AC-39 / ι / E_dig 格子池加权推导 / C=clear_blast power / P_pf / AP_r / fog_burden_ratio 登记代表值) | 主判式对 L1 无雾态/L2 两板形 × {unit_count=1, unit_count=3} 四态组合 × {完美/非完美} 分支跑 | 结论与登记约束包一致(重算批回执):L1 −28.7/+11.3、L2 −67.4/−27.4、L1 满配 −153.2/−113.2、L2 满配 −257.1/−217.1——条款⑤未闭合(verdict = 未证明的约束生成器;L1 完美翻正腿在案);实现率折减腿(E_dig×0.6)读数在案(L1 −45.3/−5.3、L2 −91.2/−51.2);保守地板需求输出(L1 ≥ 110.3 / L2 ≥ 166.8);停手侧 RAP 零价假设登记存在性(判据函数输入分解可读);护栏 A/B 行断言 = 登记存在性 + 旧值链待重推注记(护栏 A 方向性复核 −1.1 越下界 / 护栏 B C=40 越线——W2 三选一裁决随 C3 收口批,本 AC 不断言其数值);A4 三腿入账核对(雷险消解/信息双向/AP 腿)逐腿在输入分解中可读;全条为对账断言——非 playtest | F5 / 强制检查点条款⑤ | +| AC-41 | 默认根加载 | 脚本解析 `design/registry/entities.yaml` | ① status_effect.anchor_warning 含 F0 裁决回执注记(瞬时口径 / 值域 [60,85] 默认 65 / 交叉层数 2 / attack_up 参照回迁口径重跑兑现——重算批注记);② weaken 值域修订注记 + clause3_supply.direction_precheck 闭合回执注记(含 2026-09-14 重算回执——现行值链破线读数);③ clause5_dominance 注记存在(含 C3 裁决 + 重算回执);④ settlement_table 条目存在且十二字段名与 SettlementTable schema 逐一相等(pool_clamp_lo/hi 两字段 + 充能三参数与 armor_effectiveness——W3 字段);⑤ knob 活值(D_ref/P_pf/m_w/m_d)不作定值对账 | F0(registry 对账形态)/ Core Rule 16 / 对账清单 | +| AC-42 | 全部 AC 场景步骤流捕获全集(冒烟汇总断言) | 逐行校验 | type ∈ 封闭枚举 13 类(封闭枚举即负空间:新增行类型 = 接口漂移);每行公共字段 type/source_class/origin_cell/round_index/display_name/value/highlight? 全在(origin_cell 板级行可空;highlight 可空枚举 {flag_hit, clear_reward_h2, perfect_broken});行类型特有字段:伤害行/道具行(生效)携 target、效果行携 effect_id + pool_value 且 value = 单次授予贡献值语义;source_class 值域 ⊆ source_label_map 键集 ∪ 判定/道具扩展 ∪ {enemy_action, flag_miss}(新增 = 接口漂移的表侧闸);无来源行 = 断言失败(P2 兜底的机器可判定形式);未生效四类 + 标旗未中行各携带聚合计数/批次字段(非空) | Core Rule 14 | +| AC-43 | B1 文件集(编排器/判据函数/步骤流/冒烟脚本/夹具规格)与全项目非 tests/ 文件集 | 静态扫描 | ① 零 randomize()/RandomNumberGenerator/自建 RNG;② 零 extends Control/Node2D、零 .tscn preload + AC-5 headless 实跑通过(合成判定);③ 结算路径 await 零命中(逐字面 warning 级 + 人工复核);④ 编排唯一性单向扫描:全项目非 tests/ 文件集中 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集;⑤ D2 AC-14 人工三要素字面量扫描(行为级硬证据 = AC-22/36/39/40 全表读值推导 + AC-41 registry 对账);⑥ A2/B3/B4 相互直调零命中 | Core Rule 1 / 编排不变式③ | +| AC-44 | b1_default + 登记构造轮(挖掘 ≥10 安全格〔含连锁——cap 腿 >8〕+ 首次标中旗 2 枚 + 目标甲型三腿:goblin none / bugbear a / ogre_chief b;attack_up 授予 ×1 腿与非授予腿对照) | 结算战斗段主角先手攻击(表读值记号:da/ca/dbf/甲效矩阵) | 三分量读法逐腿与 F6 一致:A 池 = min(开格数×da, ca)、B 池 = 首标数×dbf;base = floor(atk×(1+P_offense/100))、dmg_a/b = floor(池×甲效/100)、D_hero = 三分量相加(表读值推导不写死);甲效三腿读数 = 特化 ×1.5 / 无甲 ×1.0 / 对位 ×0.5(甲B 对策 = dmg_b 腿——C3 联动网络 #6);attack_up 零泄漏腿:P_offense 变更只移动 base、A/B 分量逐字不变;敌方侧 percent 池零作用(hero→敌方向);cap 截断腿:15 格轮 A 池 = ca(截断入诊断计数、零明细行);轮末清零腿:下一结算 A/B 池 = 0;明细行 {baseline, attach} 分量可还原(充能池公开读值) | F6 / W3 / C3 联动网络 #6 | + +### 覆盖矩阵 + +| 类别 | 映射 | +|---|---| +| Core Rules(16/16) | 1→AC-43(单向扫描)+ AC-33(零随机行为级)· 2→AC-6 · 3→AC-9 · 4→AC-7+AC-2 · 5→AC-9/10/11/12(腿 5 = 标旗未中行)/13 · 6→AC-14 · 7→AC-15/16 · 8→AC-17 · 9→AC-18/19/20 · 10→AC-21/22/23/24/25 · 11→AC-26 · 12→AC-24/27 · 13→AC-35(#2 真实双份)+ AC-12(#3+腿 5)+ AC-36(P_pf 入账)· 14→AC-42(13 类+字段分层)· 15→AC-33/34 · 16→AC-1/2/3/4/32/41 | +| Formulas | F0→AC-41(registry 对账形态)· F1→AC-36 · F2→AC-37+AC-22 腿 3 · F3→AC-38+AC-18/20/22/24 · F4→AC-39 · F5→AC-40 · F6→AC-44 | +| 上游义务 23 条 | B3①→AC-9 · ②→AC-9 · ③→AC-37/22 · ④→AC-26 腿 2 · ⑤→AC-36 · ⑥→AC-26 腿 1 · ⑦→AC-17 腿 1 · ⑧→AC-14 ∥ B2①→AC-21 · ②→AC-9/14 · ③→AC-24/38 · ④→AC-39 · ⑤→AC-18 · ⑥→AC-14 ∥ A2①→AC-6 · ②→AC-10 腿 2 · ③→AC-12 腿 2/3 ∥ B4①→AC-7 · ②→AC-9/10/11 · ③→AC-13 · ④→AC-15/16/18/19/20 · ⑤→AC-31 · ⑥→AC-5/28——23/23 全落位 | +| 强制检查点 5 项 | reload gate→AC-32 · 可回放→AC-33 · new_board 违约→AC-30 · roll_board 恰一次→AC-5/28(scope 计数器观测)· 衰减恰一次→AC-26 | +| SettlementTable 校验 | segment_order→AC-2 · 悬空 effect_id→AC-3 · p_perfect 域→AC-4 · schema 定稿→AC-1/41 | +| EC(26 条) | 末图清图→AC-13/26 腿 2/27/31 · hero 短路→AC-24 · 双死 hero 先判→AC-24 注(结构不可达不构造)· D'=0→AC-22 腿 3 · 溢杀→AC-18 腿 3 · charge 蒸发→AC-23 · 全体 charge→AC-23 腿 2 · L=0→AC-22 腿 1 · 同窗多包→AC-16 · 部分回复→AC-16/19 腿 2 · 刀全灭→AC-20 · 空库存→AC-15 腿 4 · RAP 不足→AC-29 腿 3 · 连环刷图→AC-29 腿 2 · 未刷新→AC-30 · 非法产物→AC-28 腿 2 · 清理被拒→AC-31 腿 2 · 战斗中 reload→AC-32 · 衰减空→AC-26 腿 3 · 清图 buff 满层→AC-13 腿 2(高光降级)· 完美刷新图→AC-35 腿 4 · 标中+误标→AC-35 腿 2+AC-12 腿 5 · 多敌共享 L→AC-25 · 死亡即不动手→AC-18 腿 2 · 零伤害死局→负空间+遥测锚 OOS(C3/playtest)· 结算开始 hp=0→负空间 debug 断言——呈现半边全部 OOS 至 D1/B5(生效条件 = D1 义务台账登记) | +| 无洞声明 | Core Rules 16/16、F1–F6、义务 23/23、检查点 5/5、SettlementTable 校验 3/3 全部映射 AC;EC 除 2 条负空间自守(不可由公开 API 构造)与呈现/playtest OOS 外全部 Given-When-Then 或对账断言承载 | + +### 缺口预检 + +| 编号 | 缺口与处置 | 对侧 | 时机 | +|---|---|---|---| +| 1 | 【阻塞级】A2 事件载体 ADR 依赖:经真 A2 事件流驱动 B1 的 THEN 子集为纸面契约直至落地;A2 半真备选(注入面 + A3 真)已登记为过渡载体、断言口径不变 | 实现期 ADR(与 A2 OQ2 同一裁决) | Technical Setup | +| 2 | 【阻塞级】σ 登记轨迹与取号约定:身份依赖腿(双包/刀全灭/清图槽身份/完美板布置)依赖 σ 登记与取号约定(B4 缺口 8 同源);登记后须第二人独立推导复核 | qa / 实现期 ADR | 冒烟框架搭建 + ADR 定名 | +| 3 | m_w 修订回写——已闭合:registry 回执 #1/#2 于 2026-09-10 F0 批落地、2026-09-14 重算批更新腿读数(attack_up 参照回迁口径重跑兑现);B3 AC-14 腿 3 非平凡性复查通过(16×65 = 1040 越域) | B4/B1 共裁 + C3 | 已闭合 | +| 4 | A1 actuals p10 未交付:F4/F5 以行 target 代 p10 乐观代理——交付后强制重跑 AC-39/40 并同步 registry(对账清单 #6);连带 \|M\|/\|S\| 分布与 fog_burden_ratio p50/p90 | A1(交付)→ B1(重跑) | A1 F4 报告 C 交付 | +| 5 | C1/C2 encounter 供给:begin_battle 入参直供为 provisional——C1 定稿后接入真实遭遇上下文与跨战斗 HP 持久性,AC-5/24 的直供构造改为真实供给链 | C1/C2 | C1/C2 GDD | +| 6 | 【阻塞级】D1 呈现义务台账:A2×6/B2×4/B3×4/B4×5/B1×3(高光行强调/完美排雷呈现/EC5 零合法板提示)缺项即 blocking——本节 AC 全部以数据面断言、呈现零覆盖 | D1 | D1 GDD 门禁评审 | +| 7 | SettlementTable registry 条目回填——已闭合:settlement_table 条目 2026-09-10 回填、2026-09-14 重算批随 W3 充能规格更新为十二字段口径;AC-41④ 按现行条目断言 | 本 GDD 定稿批 | 已闭合 | +| 8 | B1 扫描域成员清单登记:AC-43 单向扫描的 B2/B3/B4 API 符号清单与 B1 文件集须登记入 D2 AC-9 项目级 CI 扫描配置(B3/B4 先例) | Technical Setup | B1 GDScript 产物落地即登记 | +| 9 | C3 调参对账重跑流程:D_ref/P_pf/m_w 任一调参 → AC-36/39/40 + AC-2–4 夹具登记值连锁重跑;扩表:A1 行参数(mine_density/min_safe_count/ι target)与 B4 奖励权重/power 调参 → 同批重跑 AC-39/40;A1 新增产出义务 = R₀ 规模分布上界(F4 报告 C 扩列——护栏 B 的登记供给方) | C3(调参)+ qa(重制)+ A1(R₀ 产出) | 每次调参 / 报告 C 扩列交付 | +| 10 | A1 桩 → 真 A1 全链路:换图编排 generate 真实集成(含 attempt 重试不重开 scope 语义、非法产物真分布)登记端到端集成冒烟(与 A1×A2 双包含端到端同槽) | qa/A1 | A1 实现落地 | +| 11 | 零伤害死局遥测锚:EC 死局组合 B1 不设自愈分支——战斗轮数异常遥测的口径登记(进 C3/OQ 清单) | C3/playtest | 垂直切片 playtest | +| 12 | flag_miss 源路径预留:标错惩罚上线时补 `flag_judged{hit=false}` → 敌方伏击 +1 源路径 AC(source_class 新枚举走 B3 修订——A2 预告接口漂移) | B1/A2/A3 联合 | 惩罚实测采纳后修订批 | + +## 开放悬案 + +| 编号 | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | 事件总线载体:signal vs 回调注册 vs 直连调用;B1 触发机制(事件订阅 vs 相位轮询)与 A2 事件捕获断言面同批落地——AC-6/AC-33 相关 THEN 为纸面契约直至落地(缺口预检 1) | 实现期 ADR(与 A2 OQ2 同一裁决) | Technical Setup | +| 2 | m_w 修订终值共裁:F0 已裁值域 [60, 85]/默认 −65 与归一口径;终值确认 + B3 StatusEffectTable 行表/Tuning Knobs/registry 三处回写执行(缺口预检 3);B3 AC-14 腿 3 非平凡性随批复查 | B4/B1 锚 owner 共裁 + C3 调参 | B1 评审后的接口漂移回写批 | +| 3 | A1 actuals p10 交付后条款③⑤强制重跑:ι 以行 target 代 p10 为乐观代理——重跑 AC-39/40 + 同步 registry 重算值(对账清单 #6);连带 fog_burden_ratio p50/p90 | A1 交付 → B1 重跑 | A1 F4 报告 C 交付 | +| 4 | 连锁规模分布供给方悬空:条款②第二腿(连锁放大边际基准)校准需 c̄(每安全格连锁期望放大)——现无既定供给方;候选 = A1 F4 报告 C 扩列 or A2 遥测(对账清单 #8 附注);供给落定前第二腿以 1:1 零链下界运转 | A1/A2 + B1 共裁 | A1 报告 C 扩列或 A2 telemetry 定稿 | +| 5 | EC5 零合法板玩家侧呈现形态:提示/补偿/回退三形态选型——B1 数据面(非法产物标记上抛)已就绪;B1 域内不做补偿逻辑(候选"确定性回退已知好种子"归实现期 ADR——A1 EC5 登记对侧) | D1 裁决 | D1 GDD | +| 6 | C1/C2 encounter 供给接线:begin_battle 入参直供改真实遭遇上下文 + 跨战斗 HP 持久性(B2 hp_persistence 对侧承接);战斗触发(节点移动碰非奖励点)归 C1(缺口预检 5) | C1/C2 | C1/C2 GDD | +| 7 | 深度高光形态:#3 标中旗高光的二期形态(即时 HUD 反馈动画/音画同步)——MVP 已落数据面标记 + 当轮兑付;旗落格高亮/姿态反馈等表现形态随 art-bible 与战斗表现美术测试定 | D1/B5 | 垂直切片 | +| 8 | 标错惩罚 flag_miss 源路径:候选"敌方伏击 +1"上线时——flag_judged{hit=false} 转化路径 + B3 source_class 新枚举(结构冻结项走 GDD 修订)+ 惩罚重复性裁决(hit=false 无去重的不对称)联动关闭(缺口预检 12) | B1/A2/A3 联合 | A2 OQ1 实测采纳后修订批 | +| 9 | hero atk 激活——已闭合(2026-09-11 起"基础攻击恒存在":HeroTable.atk 域 [1,999]、0 加载拒绝、MVP=6;attack_up 复活、消费面 = 主角战斗段先手攻击);对照表回迁 + AC-36 死行断言改写随重算批兑现 | B4/B1 | 已闭合 | +| 10 | B2 OQ1 两 face 归属:query_attack/ambush_grant_plan 归 B2 供给面 vs B1 编排面——裁决归 B1 则 AC-14/22 相关腿迁移至 B1 冒烟(断言口径不变) | 实现期 ADR | Technical Setup | +| 11 | 零伤害死局遥测锚口径:战斗轮数异常遥测的阈值与埋点(EC 死局组合 B1 无自愈分支——护栏 owner = 实现期 ADR/D2 跨字段校验,B2 OQ9 并案)(缺口预检 11) | C3/playtest | 垂直切片 playtest | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| 跨评审 B1 重算批 | 2026-09-14 | /review-all-gdds 🔴-1 后半(registry 同批重登回执):**F0/F1/F4/F5 按现行三输入重推**(duration=1〔两级奖励批〕、m_d=−30〔减伤墙批〕、C3 v3 曲线 d0.25/t0.88 换锚——原 A1 参考行退役)——**条款③两参考板形翻为破线**(锚行 ≈0.48×/0.47×、期望口径 E_dig=4.5 → 0.60×/0.58×;满配 ≈0.16×),旧"通过"回执(≈2.0×/2.6×)退役为历史记录、破线升格 C3 曲线级约束(V_safe 侧杠杆重议——禁独立调参联动网络);E_dig 重推 7.2→4.5(格子池加权,注入池不摊入)· 条款⑤四态重推(L1 −28.7/+11.3 等;保守地板需求 110.3/166.8,C3 裁决②维持)· **F1 对照表按现行行集重登**(两级行值 8/16/40 + 注入池 + clear_blast;attack_up 死行注记清除——与 Core Rule 10 先手攻击对账;锚对照实物改 attack_up 行〔registry 回迁口径重跑兑现〕)· **F6 充能与护甲克制结算规格**(W3——累积时点/单轮 cap/不吃 percent/逐分量甲效查表 floor + AC-44 + F1 对照两行)· W6 挖掘期刷新改**每轮 cap 2**(RoundStart 重置——A3/registry/D1 同口径)· defense_up.magnitude/duration 纳入禁独立调参联动网络 · AC-22/36/37/39/40/41 改写 · 护栏 A/B 读数挂旧值链注记(W2 复核随 C3 收口批)· 悬案 9/缺口预检 3·7 闭合 · 核心规则表增依据列(lint R8 引用列迁移) | +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-6 回执,registry 同批):战斗开始序列增 **encounter 掷骰步**(RngService 封闭流恰一 scope——unit_count 区间骰定本战实际数量,spawn 增 override 参数 n)· Core Rule 1 零随机扩界注记(实例化骰在结算步流之外,可回放性不受影响)· AC-5 增 encounter scope 腿 · SettlementTable +4 字段(damage_a_per_cell / damage_a_cap / damage_b_per_flag / armor_effectiveness——护甲克制批转正,机制与结算归 B1、C3 投值)· 条款⑤ C3 裁决回执行(约束包② 采信 + clear_blast C=40 缺口重推 6.1/51.8)· 敌种键更名 goblin/bugbear(DND 批对齐) | +| v1.1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;散文/枚举/边界情况/AC 转表格;流程转字符画;公式、Variables 与验算数据全保留;语义与 Rev 1 及后续拍板批对齐,零规则变更 | +| D4 修订 | 2026-09-12 | 刷新决策前移挖掘期:新增"挖掘期刷新"出口(refresh_board 编排——request_refresh 扣费 → 换图全序,无结算段、轮次不终结),原 Waiting 期刷新代理调用弃用 | +| 2026-09-11 拍板批 | 2026-09-11 | 主角先手攻击(hero_attack 来源 + F3⓪)、两级奖励裁决(instant_aoe/instant_heal 当场兑付)、道具窗口单件制(>1 件 = 编排违例取首件)、库存关内延续(battle_over 不清库、新增关卡末 level_clear 清库编排) | +| Rev 1 | 2026-09-10 | design-review 8 项 blocking 核销:完美排雷落真实双份交付载体 + 高光行有效兑付语义/零兑付降级;条款⑤计价模型重推为挖穿残余池口径、verdict 翻转 → C3/B4 约束包 + 多敌验算腿 + ι→1 域守卫 + 护栏 B 输入登记与双读数;流水协议 target/effect_id/pool_value 字段分层 + enemy_action/flag_miss 来源枚举;hit=false"标旗未中"零值行(每雷实例一次去重);P_perfect ÷100 换算成文 + 载体域收窄 {100,200};AC-18 腿 2 改写;F4 V_safe 标签更正 + 期望口径敏感性读数;clamp 统一八字段——明细见 reviews/b1-settlement-review-log.md | +| v1 | 2026-09-10 | 一次会话定稿:F0–F5 + 43 条 AC + 23 条上游义务落位;F0 归一口径裁决(每结算瞬时当量,m_w 值域 [60,85]/默认 −65)与高光双落(完美排雷 / 标中旗标记)即定 | diff --git a/design/gdd/legacy-2026-09-14-v1/b2-enemy-system.md b/design/gdd/legacy-2026-09-14-v1/b2-enemy-system.md new file mode 100644 index 00000000..588265d1 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/b2-enemy-system.md @@ -0,0 +1,605 @@ +# B2 敌人系统(Enemy System / 战斗单位系统) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents +> **承载支柱**:P3 主(雷→敌方优势的收件人与同源兑付面)· P2(敌方增益行可归因)· P1(视觉推测型读怪 = 规划素材)· P4 间接(地牢住户) +> **一句话**:B2 定义战斗双方是谁、多强、带什么词条、如何受伤与死亡——雷的代价的收件人。 +> **复核注记**:lean 门禁下 systems-designer(Formulas)/ qa-lead(AC + 缺口预检 7 项)已委托起草;creative-director 与 art-director 未经咨询——生产前人工复核体验锚点情绪定位与 Visual/Audio、art-bible 一致性(art-bible §1–4 须在 D1 GDD 前创建,本节线索清单是其输入之一)。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 战斗域单位层(Layer 2 Core · MVP · 设计顺序第 7 位):定义"战斗双方是谁、各自多强、带什么词条、怎么受伤与死亡";持有 D2 EnemyTable 回填权,承载主角与敌人共用的战斗单位接口。缺本系统则踩雷优势无受益者(P3 断)、胜负判定无主体、难度曲线无施加对象 | +| 拥有 | 单位状态(hp/max_hp/存活/行为环指针)与查询/受击/推进接口 · EnemyTable/HeroTable schema 与双敌种行回填(行键 (level_index, node_depth, enemy_id))· 行为环语义 · 目标选择规则(固定顺位)· 伏击量纲定稿与 advantage_per_round_cap 表值 | +| 不拥有 | 结算编排与伤害消费语义(B1 SettlementTable)· 状态实例容器(B3)· 奖励内容与掉落(B4)· 棋盘规则(A1/A2)· 单位成长曲线与难度修饰(C3)· 遭遇构成(C1)· 呈现(D1/B5/D5)· 单位/词条类结构与事件载体实现(实现期 ADR 域) | +| 上游输入 | D2 EnemyTable/HeroTable 行值(ConfigDB 单点读取、R1 行解析)· hero hp 入参(编排方供给)——A2/A3/A1 事实均经 B1 结算编排转化,零直接接口 | +| 下游输出 | 单位状态查询/受击/推进接口(B1 消费)· 意图视觉映射键 enemy_id 与视觉状态清单(D1/B5 消费)· 伏击量纲 + cap 表值 + k 可算性(B1 条款 3 对账输入)· 行为环教学口径(D5) | +| 硬约束 | 零随机(rng_service 流封闭)· 零 UI 依赖 · 零主动事件订阅 · 悬空引用致命、零软回退 · 行为环敌单位专属 · 单位接口调用权集中归 B1 · 胜利路径 = 基础攻击(恒在)+ B4 奖励侧规划面伤害共同承载——"敌人全灭 → 战斗胜利"可判定 | + +## 体验锚点 + +> 情绪目标一句话:**"对面是个看得懂的对手,不是一个血条"**——威胁可推测、可归因、可拆解;玩家不操作敌人,但每一轮都直接感受它。 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P3 同源的对面对象 | "赌桌对面的那个存在" | 结算明细"敌方获得伏击 +3(第 4 轮 · 雷)"的行可指认——敌人强度是扫雷决策的镜像,威胁有源可溯 | +| P1 可推测的威胁 | "它弓起背了,下一拍是重击" | 读怪推理进入挖掘决策(形态↔行为映射稳定可学,MVP 双环最长 3 拍);不预告精确数值——数字在结算明细兑现,结算前的世界靠观察而非读表 | +| P2/P3 的拆台兑现 | "它强是我喂的,它弱是我拆的" | 标中旗 weaken 与伏击行同框、同一呈现语言,落在同一个它身上 | +| P4 地牢住户 | "越下越险的刻度" | 深层敌人更凶(C3 施加),给下探氛围一个可见刻度 | + +- 反向护栏:威胁绝不能是无源暗账——玩家说不出"它为什么变强 / 这一拳为什么这么重"即归因链失职(P2/P3)。 +- 视觉推测型对偶护栏:线索必须稳定可学(形态↔行为映射每 run 恒定,D1 义务②);任何让线索重掷或不可学的呈现设计默认砍掉(P1)。 +- 血条/攻击力数字可读性适用视觉锚 1(暗色底上高对比)。 +- 参考感受合成:Peglin 固定招式轮替(威胁因可预测而可规划——确定性环是可学习性来源)× 怪物猎人读招(前摇姿态承载威胁信息、无数值面板——视觉推测的同构)→ 本作 = 确定性环 × 视觉推测;反向对照(要避开的感受)= 半随机、不可归因、重掷线索的"猜怪"呈现。 + +## 全局流程 + +B2 编排被动——调用权集中归 B1;下图从 B2 视角画单位级行为环与生命周期出口。 + +### 敌人行为环 + +``` +战斗开始(B1 编排调用,B2 侧语义) + │ ① 解析遭遇行 (level_index, node_depth) —— D2 R1 进度键 floor + enemy_id 精确匹配(悬空致命) + │ ② 行值快照 → 实例化本战实际数量 n 个敌单位(行主序生成序——B1 encounter 骰 override;定值行 n = unit_count)+ 主角(hp 入参) + │ ③ 全部指针指向环首:beat = pattern[0]、steps = 0 + ▼ +每次结算(B1 编排驱动——B2 零主动;推进域 = 每存活敌单位恰一次 advance,主角无行为环不在推进域) + │ ⓪ 主角先手攻击 → apply_damage(顺位最前存活敌, 伤害) [战斗段固定动作,非行为环] + │ ① 兑付读值:query_attack(u, L, m_amb) → {baseline, attach, total} + │ ② 受击上报:apply_damage(hero, 减伤后终值) —— 调用序钉死:倒序会读到下一拍(B1 义务①) + │ ③ 推进:advance(u) —— 存活才推进(先作用后推进) + ▼ +当前拍作用(每存活敌单位) + ├─ attack:本结算对主角造成 atk 点伤害 + ├─ charge:本结算不作用(威胁空拍、规划窗口;不兑付伏击、不读池) + └─ heavy :atk × heavy_multiplier 对主角 + ▼ +拍作用完成 → steps + 1 → beat = pattern[steps mod 环长](环尾回环首;跨轮次、跨扫雷图延续) +轮到前被杀 → 本拍不打、指针冻结(死亡即不动手;MVP 无复活 → 永久冻结) + ▼ +battle_over(B1 调 despawn_all)→ 全部单位 Despawned(状态消亡,不跨战斗) +``` + +### 出口分发 + +| 出口 | 触发 | 动作/去向 | 约束 | +|---|---|---|---| +| 单位死亡 | hp = 0(判定时机归 B1) | 移出顺位、不再作用、不计入"未灭";轮到前死 → 指针冻结、行动后死 → 已推进 | 不可逆(MVP 无复活);Dead 句柄保留只读可查(query_beat 冻结值) | +| 敌全灭 | 存活敌集 = ∅ | "敌人全灭"成立 → B1 胜利判定素材;单体/AOE 无有效目标;敌侧伏击池随轮末衰减自然清空 | 判定逻辑归 B1 | +| 主角死亡 | hero hp = 0 | 战斗失败判定素材(判定时机归 B1) | battle_over → 全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态 | +| battle_over | B1 编排 despawn_all | 全部单位状态消亡、不跨战斗;Despawned 后唯一出口 = 重新 spawn(新战斗) | 重复 despawn_all → false + 诊断;Despawned 为逻辑失效态(失效句柄查询空 + 诊断) | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:主角和敌人共用同一套受伤与死亡规则 +**人话**:这条管的是战斗里所有"单位"的通用身体规则。战斗场上恰有两类单位:主角一个、敌人若干,共用同一套身体语义——每个单位有当前血量与血量上限,挨打就是血量往下减,血量到 0 即死亡。超上限、负数、一刀砍超的部分怎么处理,以及"这一下究竟何时算死",都归战斗结算系统(B1)的结算公式域裁决。 + +B2 只提供受伤上报与状态查询接口、如实记账,"谁在何时对谁造成多少"的流水账本也在 B1。主角血量归零 = 战斗失败的判定素材,敌人全部归零 = 战斗胜利的判定素材——判定本身同样归 B1。 +**举证**:无计算。clamp/负值/溢杀语义与死亡判定时机的成文义务登记为 B1-③(B2 侧 out-of-scope);单位接口、胜负素材与越界拒绝验收见 AC-1/AC-2/AC-6。 + +#### 规则 2:一场战斗 1–3 个同种敌人,敌方效果对全体生效 +**人话**:这条定的是"一场战斗有几个敌人、效果怎么作用"。MVP 每场战斗 1–3 个敌人,全部是同一种敌、属性完全相同——由敌人表同一行实例化,按行序排出先后;数量是敌人表里的调节旋钮。战斗中敌人只减不增(没有召唤/增援/分裂)。 + +敌方吃到的增益/减益是"对全体生效":作用于所有存活敌人,层数进一个共享池——这是与状态系统现接口零改动兼容的 MVP 路径。"只作用于某一个敌人"的单体寻址暂不开放,登记为状态系统的扩展位,等第一个单体效果上线时补列。 +**举证**:无计算。构成/同质性/作用域/单调性细则见下方实现参照表;target_ref 扩展位义务登记为 B3(首个单体效果上线时补列,随批 B1 归集语义/D1 呈现裁决);战斗内不增员见 EC5;验收 AC-1/AC-5/AC-9。 + +### 规则 2 的实现参照——多敌模型 + +| 项 | 内容 | +|---|---| +| 构成 | 1–3 个同敌种单位(unit_count int [1,3] 表字段 knob);战斗开始实例化、行主序生成序 | +| 同质性 | 战斗内敌单位同种同属性——同一 EnemyTable 行实例化 | +| 效果作用域 | side-wide:enemy 侧效果作用全体存活敌单位——层数为 side 共享池(F2 池共享不消耗);B3 现接口零变更兼容 | +| target_ref | 单体寻址登记为 B3 扩展位义务(首个单体效果上线时补列;随批:B1 归集语义/D1 呈现裁决) | +| 单调性 | 战斗内敌单位数 = unit_count 不增(MVP 无 summon/增援/分裂——EC5) | + +#### 规则 3:MVP 就两种敌人,每个关卡坐标只出一种 +**人话**:这条定的是敌人名录。MVP 恰两种敌人:地精(goblin)与熊地精(bugbear),数值全部 provisional(临时占位),MVP 名录定稿走索引门禁。敌人在敌人表里按"关卡序号 × 节点深度"坐标存放:每个坐标的行集里只许出现一种敌人——这是表结构校验,配出两种就加载报错;由此既没有"同一点随机挑一种"的选择规则,也没有引用悬空的风险。 + +行集逐坐标判定,同一关内随深度换敌种合法。MVP 分布只是最平直的合法示例:第 1 关全深度地精、第 2 关全深度熊地精——占位示范、非范本,难度调参勿照抄(防多样性无护栏),遭遇分布调参所有权归难度域(C3)。名录扩容排垂直切片/Alpha;"同深度多敌种怎么选"归遭遇构成(C1)/难度域,目前只是预留结构位。 + +**举证**:无计算。"每行集恰一敌种"(加载期致命)与字段域校验验收见 AC-12/AC-14;敌种 id 集合定值断言 {goblin, bugbear, ogre_chief} 见 AC-14(C3 修订批扩容三敌种);registry 物种注记对账见 AC-16③。 + +#### 规则 4:敌人按排好的剧本轮流出招,走到哪一拍零随机 +**人话**:每种敌人有一条排定的行动剧本(行为环):战斗开始指向剧本第一步,之后每结算一次走一格——轮到谁,谁先把当前这一拍的招打出去、再走格;走到剧本末尾回到开头;跨轮次、跨扫雷图延续,战斗结束随单位一起消亡。轮到它之前被打死:这一拍不出手、指针就地冻结("死亡即不动手")。全程零随机——走到哪一拍由结算次数唯一决定,同种敌人因此永远同拍。行为环是敌人专属:主角没有行为环,对主角调环接口一律拒绝。MVP 只启用攻击/蓄力/重击三种拍,后四类是预留位、配进表会被校验拒绝。 + +**举证**:无计算(推进是确定性函数)。指针/回绕/冻结/同步性质公式见 F1;"每结算恰一次推进"与结算内调用序登记为 B1-①;推进细则与预留值拒绝(fail-fast)见下方实现参照表;验收 AC-3/AC-4/AC-5/AC-15(预留值致命 manifest pattern_reserved)。 + +### 规则 4 的实现参照——行为环推进细则 + +| 项 | 细则 | +|---|---| +| 指针初始化 | 战斗开始指向环首:beat = pattern[0] | +| 推进时机 | 每次结算推进一格——先作用后推进(B3"先兑付后衰减"同款时序哲学);轮到该单位行动时仍存活才行动并推进 | +| 死亡口径 | 轮到前被杀 → 本拍不打、指针冻结("死亡即不动手",F1 承载;MVP 无复活 → 永久冻结、随 battle_over 消亡);行动完成后才死 → 已行动已推进 | +| 回绕与域 | 环尾回环首;指针 ∈ [0, 环长);环长 1(goblin)退化为恒等 | +| 延续 | 跨轮次、跨扫雷图延续(战斗域);battle_over 随单位消亡;除 despawn_all 外无任何重置入口 | +| 确定性 | 零随机——环推进是结算次数的确定函数(rng_service 流封闭,P1) | +| 专属域 | 行为环为敌单位专属:主角无行为环——advance/query_beat 等环接口对主角句柄一律拒绝(false / 空 + 诊断) | +| 实现域 | MVP 恰前三类 {attack, charge, heavy};guard/boost/hinder/summon 为预留语义位(垂直切片/Alpha),EnemyTable.pattern 校验拒绝预留值(fail-fast,A1 solver_rules 冻结先例)——启用走本 GDD 修订(枚举封闭性)非调参 | + +#### 规则 5:每踩一颗雷敌方叠一层伏击——一轮封顶、超出可见不生效 +**人话**:这条定的是踩雷对敌方的反噬。挖开雷格时,敌方全体获得一层"伏击"强化——MVP 伏击清单只有伏击这一条(状态系统表里 mine_hit 雷击来源的唯一效果行);将来加新词条要状态系统加行并连锁更新锚点,不属于 MVP。转化路径:棋盘上报"挖到雷"→ 结算编排把它转成一次伏击施加(作用全体敌人、共享层数池),每颗雷恰转化一次。单轮授予有上限(敌人表行值,默认 4):上限之内逐颗叠加;达到上限后,多踩的雷不生效,但逐颗记一行"溢出未生效"结算明细——惩罚不静默蒸发。 + +| 单轮伏击上限(cap 行值) | 行为 | +|---|---| +| 默认 4 | 前 4 颗雷各叠一层;第 5 颗起每颗记一行"溢出未生效"明细 | +| 调到 8 及以上 | 单轮雷数至多 8 颗、达不到上限——溢出协议自然不可达 | +| 0 | 该行敌种免疫伏击——每颗雷都记"溢出未生效",全部可见、全部无效 | + +**举证**:计算见 F3:granted = min(当轮新开雷数, cap)、overflow = 差值(守恒、零静默蒸发)、L = granted(dur=1 轮末全清)。每雷恰一次转化登记为 B1-②、溢出行协议登记为 B1-⑥;cap 默认 4 定稿与反灾难护栏绑定域(单轮 5–8 雷灾难轮)见 F3;cap 表值同时是 A3 F1 条款 3 挖掘风险不等式的 B2 供给表值(单结算自然边界口径见 F4)。验收 AC-8。 + +#### 规则 6:一层伏击 = 敌人每次攻击多打 3 点固定伤害 +**人话**:这条定的是伏击到底多疼:一层伏击 = 敌人每次攻击类行为(普攻或重击)额外多打 3 点固定伤害,按主角血量点数计。结算读法:某个敌人出手时,附加伤害 = 敌方池当前层数 × 3。两个要点:附加是后置加法、不进重击倍乘(倍率只乘基础伤害);蓄力拍不出手、不附加。伏击只活一轮——每轮结算清零、没有跨轮存量,所以跨轮累计没有表值上界;单次结算有自然上界(见举证)。 + +**举证**:上界算式:单结算单单位附加 ≤ cap × 3 = 12(默认);敌方全体结构上界 = unit_count × min(单轮开雷上界 8, cap) × 3(满配 3×4×3 = 36;默认单敌 = 12);跨结算累计 = 轨迹量、非表值、不设上界(dur=1 每结算清零)。伤害构成与合成序见 F2;等量换算与不等式闭合归 B1(F4);量纲定稿注记按接口漂移回写 registry status_effect.ambush(AC-16④)。 + +#### 规则 7:敌方打主角不用挑,玩家侧永远先打排最前的敌人 +**人话**:这条定的是"打谁"。敌方一侧没有选择可言:所有存活敌人各自按自己行为环的当前拍作用于主角——主角是己方唯一单位,天然是唯一目标。玩家/道具一侧打敌人用固定顺位:单体效果默认打生成序最前的存活敌人,它死了顺延到下一个;范围效果(AOE)打全体存活敌人,同样无需选择。这套规则供结算编排消费;道具自带"指定目标"语义的扩展归道具系统(B4)/结算域。顺位必须对玩家可读——"下一个被打的是谁"要能推测出来,这是呈现层义务。 + +**举证**:无计算(纯规则)。固定顺位供给面、死亡顺延与 u_c=1 退化(AOE ≡ 单体、合法非错误)验收见 AC-10;顺位快照 = 生成序、单体/AOE 分流消费登记为 B1-⑤;顺位可读呈现义务登记为 D1-③(呈现契约表 #3)。 + +#### 规则 8:敌人要出什么招靠看图推测,不直接报数字 +**人话**:这条定的是玩家怎么"读"敌人。界面上不直接显示敌人下一拍的行为与数值——威胁靠看图推测:它弓起背,下一拍就是重击。两条承重规格:"预告时序"(即将执行的一拍在挖掘阶段全程亮出)与"映射每局恒定"(禁止重掷,否则读怪退化成猜怪)——否则"抢在重击前击杀"窗口、"蓄力轮放心贪雷"教学、读怪幻想三者同时落空。精确数字只在结算明细兑现。线索清单与图像规格归呈现层(D1/B5),映射键 = enemy_id;MVP 读怪对象 = 两条环(最长 3 拍),学习成本低。 + +**举证**:无计算(规格非算式)。五条规格全文见下方实现参照表;线索清单/图像规格/映射全局恒定呈现义务登记为 D1-①②(登记 systems-index;映射键 = enemy_id,B3 effect_id→icon 同款契约);"不直接显示数值"的负空间 = 冒烟 API 封闭枚举(无意图数值 API);行为环可学性行为级证据 AC-3/AC-15。 + +### 规则 8 的实现参照——意图可见性五条规格(承重项) + +| # | 规格 | 内容 | +|---|---|---| +| ① | 图像↔行为映射稳定可学 | 同一敌种同一行为恒同一视觉状态(如 bugbear 蓄力拍 = 蓄力姿态) | +| ② | 形态暗示强度 | 敌种形态承载强度档位与环长的直觉编码("大个子多半有重击"式) | +| ③ | 映射每 run 恒定 | 禁止重掷——否则"读怪"退化"猜怪"(违反 P1 裁剪判据);映射键 = enemy_id | +| ④ | 精确数值在结算明细兑现 | P2 归因不变——预告面是图像、数字面是明细 | +| ⑤ | 预告时序锁定 | 视觉状态呈现即将执行的一拍(当前拍预告)——挖掘阶段全程可见;结算时不另设独立意图播片(结算明细是数字兑现面、非意图预告面) | + +#### 规则 9:主角数值在英雄表里——血量上限加恒在的基础攻击 +**人话**:这条定的是主角这边怎么打。主角战斗数值单独放一张全局单行的"英雄表"(本 GDD 是回填 owner,走 D2 接口漂移规则回写),两个字段: + +| 字段 | 域(knob) | MVP 值 | +|---|---|---| +| max_hp 血量上限 | int 1–999 | provisional,出处待查 | +| atk 基础攻击 | int 1–999;0 加载期校验拒绝 | 6(原型锚 HERO_BASE_ATK provisional,归 C3) | + +基础攻击恒存在、不许为 0:零基线乘任何百分比增益恒为零,这个死锁从数据层就进不来、开发期任何内容态都不可能出现。战斗节奏:每次结算的战斗段,主角先手攻击顺位最前的存活敌人——先于敌方行动;攻击是固定动作、不是行为环一拍(主角无环),伤害算式/乘区/明细行协议归结算系统战斗段。攻击增益(attack_up,+50 percent)的唯一消费面就是这次先手攻击;道具伤害是恒定表值、不吃修正。 + +战斗开始时主角血量由编排方入参供给,跨战斗是否保留血量归关卡结构域、本文不定义(MVP 冒烟以血量上限入参);后续解锁成长归 C4、曲线调参归 C3。胜利路径 = 基础攻击 + 奖励侧规划面伤害(囤刀、抢杀、爆发时点的决策素材)共同兑付"敌人全灭 → 战斗胜利",每一分伤害逐行标注来源(hero_attack / 道具逐件实例)。 + +**举证**:无新计算——数值皆表值。schema 域/单行校验验收 AC-13/AC-14;hero hp 入参越界拒绝见 EC16(AC-2);attack_up 消费面口径(B4);跨战斗 HP 持久性归 C1/C2;atk 禁独立调参联动见 Tuning Knobs;HeroTable 新增的接口漂移回写义务 R-4 见依赖节。 + +#### 规则 10:开战按坐标读表生成敌人,数值是生成那一刻的快照 +**人话**:这条定的是敌人从哪来、数值从哪来。战斗开始时,编排方给出遭遇坐标(关卡序号 + 节点深度),敌人系统据此从敌人表精确匹配一行,实例化"本战实际数量 n"个敌单位——n 由编排方开战掷的遭遇骰从表里的数量区间 [unit_count, unit_count_max] 定出(定值行取表值);主角同时以血量入参生成。 + +实例化拿到的是当时表值的快照:之后热重载改表,已生成的单位一概不受影响。难度成长走双通道:表值本身随坐标加深而变 + 难度系统(C3)的整体修饰——修饰施加点已定为"行值快照之前套乘",MVP 难度表为空、零修饰;B2 一律按实例化后的数值运作。 + +**举证**:无计算(数值皆快照表值)。行解析 = D2 R1 进度键 floor + enemy_id 精确匹配、悬空致命(EC13/EC14 → D2 通道);坐标越界前置守卫见 EC14;快照语义同 B3 EC9 先例、热重载与投影拷贝判别验收 AC-11(EC12);难度修饰接口已定——registry `difficulty_table` 条目(算式归 C3)。 + +#### 规则 11:敌人系统自己不主动做事,一切等结算编排来调 +**人话**:这条管的是敌人系统的姿态:B2 不产生随机数、不碰界面、不订阅任何事件——挖雷、插旗等棋盘事实不直接通知 B2,而是经结算编排转化后由它来调用。B2 只摆出两组能力:查状态(血量、当前拍、顺位、存活),受动作(受击、推进、消亡);什么时候调、按什么顺序调,全部由结算编排(B1)说了算——与状态系统"授予集中在编排"是同一条原则。 + +**举证**:无计算。零随机行为级硬证据 = 同序列重放两遍逐位相同(AC-15);零 UI 依赖/零事件订阅/零 B3 符号引用静态扫描见 AC-17(A2 事件面 referenced_by 不含 B2 的代码侧对应);调用权集中与推进恰一次登记为 B1-①。 + +## 枚举 + +### 行为拍类型(封闭枚举,七类) + +| 值 | MVP 状态 | 语义 | +|---|---|---| +| attack | 实现 | 本结算对主角造成 atk 点伤害 | +| charge | 实现 | 本结算不作用——威胁空拍、规划窗口;不兑付伏击(不读池) | +| heavy | 实现 | atk × heavy_multiplier | +| guard | 预留(校验拒绝) | 自防 | +| boost | 预留(校验拒绝) | 自体强化 | +| hinder | 预留(校验拒绝) | 妨害雷图——敌方向棋盘施加变异类妨害(Peglin"干扰弹板"的本作转译) | +| summon | 预留(校验拒绝) | 召唤增援 | + +- pattern 环长 [1, 8];MVP 校验域 = {attack, charge, heavy};预留值校验拒绝(fail-fast);启用预留值走本 GDD 修订(枚举封闭性)非调参。 + +### MVP 双敌种内容行(值全部 provisional) + +| 敌种 | 行为环 | 基线(provisional,出处待查) | +|---|---|---| +| goblin(地精) | [attack](平凡环,环长 1) | hp/atk 暂按原型参考 18/4 占位 | +| bugbear(熊地精) | [attack, charge, heavy](三拍环) | heavy_multiplier = 2 | + +### EnemyTable schema(D2 回写义务——键 (level_index, node_depth, enemy_id)) + +- 进度键 floor(D2 R1)+ 实体键精确匹配;每行集恰一敌种(行集内 enemy_id 唯一 = 表结构校验,违规加载期致命);本表是进度成长表(A1 BoardGenTable 同型,无 Rule 4 豁免);表非空(D2 Rule 6);精英/Boss 编码位预登记 OQ7——难度三维的精英/Boss 修饰在本键结构下无编码位,C1 定稿时强制核对。 + +| 字段 | 类型/范围 | 语义 | +|---|---|---| +| enemy_id | String(行集内主键) | 实体键精确匹配(D2 Rule 4);意图视觉映射键(Rule 8③) | +| display_name | String | 图例/HUD/明细用名(本地化归 D6) | +| unit_count | int [1, 3] | **遭遇区间下界**(knob;同敌种——C3 遭遇随机化:区间 [unit_count, unit_count_max] 经 B1 encounter 骰定本战实际数量) | +| unit_count_max | int [unit_count, 3],可省略 | 遭遇区间上界(缺省 = 定值,旧行零迁移——C3 修订批新增字段) | +| armor_type | enum {none, a, b},缺省 none | 护甲类型(克制矩阵 = SettlementTable.armor_effectiveness——伤害A/B 充能的克制消费面;MVP 映射 goblin=none / bugbear=a / ogre_chief=b) | +| hp | int [1, 999] | 单位最大生命基线(knob;难度修饰前) | +| atk | int [0, 999] | 攻击基线(knob;attack 行为伤害基值;0 合法 = 纯伏击伤敌种) | +| heavy_multiplier | int [1, 16] | heavy 倍率(knob;无 heavy 拍的敌种不消费——非校验项,B3 max_stack 先例) | +| pattern | StringList(环长 [1, 8]) | 行为环(元素 ∈ {attack, charge, heavy} 为 MVP 校验域;guard/boost/hinder/summon 预留、校验拒绝) | +| advantage_per_round_cap | int [0, 16] | 单轮伏击授予上限(knob,默认 4 定稿;F1 条款 3 表值;0 = 该行敌种免疫伏击授予的合法极端) | + +### HeroTable schema(单行全局,D2 新增 schema 行——接口漂移回写义务见依赖节) + +- 主角不随关卡自动成长,成长 = 天赋 C4 / 奖励侧;Rule 4 豁免同 ActionEconomyTable 先例。 + +| 字段 | 类型/范围 | 语义 | +|---|---|---| +| max_hp | int [1, 999] | 主角最大生命(knob,MVP 值 provisional 出处待查) | +| atk | int [1, 999] | 基础攻击(knob,MVP = 6 原型锚 provisional 归 C3;0 = 加载期校验拒绝——零基线死锁自 schema 层杜绝) | + +## 数值结论与推导 + +> 全部公式零随机、同输入恒同输出(可回放前提);本节只含机制公式——平衡归属见文末显式排除声明。术语:拍 = 行为环里的一格行动(bugbear 环 [攻击, 蓄力, 重击] 有三拍);伏击池 = 敌方侧共享的伏击层数(本轮踩几颗雷就几层,轮末清零);溢出 = 单轮踩雷超过上限的部分,转化为明细行可见、不生效。 + +### F1 行为环推进(ring_advance) + +**结论**:beat(u) = pattern[steps_u mod 环长]、行动完成后 steps_u + 1;指针恒 ∈ [0, 环长)、环长 1(goblin)退化为恒等;死亡即不动手——轮到前被杀本拍不打、步数冻结(永久,随 battle_over 消亡);同种单位同指环首且每结算各恰推进一次 ⇒ 存活者永远同拍(同步性质定理——任何死亡时序都不会让幸存者乱拍)。 + +#### 推导 + +``` +beat(u) = pattern[steps_u mod 环长] 行动前读拍;战斗开始 steps_u = 0(指向环首) +steps_u' = steps_u + 1 行动完成后推进(先作用后推进) +轮到前被杀 → steps_u 冻结(死亡即不动手);行动完成后才死 → 已行动已推进 +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 行为环 | pattern | StringList | 环长 [1,8];MVP 元素 ∈ {attack, charge, heavy} | EnemyTable 行值快照(Rule 10) | +| 已行动次数 | steps_u | int | [0, ∞) | 结算次数的确定函数(零随机) | +| 当前拍 | beat | enum | pattern 元素 | 本结算该单位的行动内容 | + +- 同步性质(定理):同种单位同指环首、每结算各恰推进一次(B1 义务①)→ 存活者永远同拍——任何死亡时序都不会让幸存者乱拍。 +- Output Range:指针恒 ∈ [0, 环长);环长 1(goblin)退化为恒等。极端:heavy 拍前被杀 → 重击永不兑现(冻结于该拍)——玩家"抢在重击前击杀"的读怪收益由此产生(P1)。 +- Example:bugbear——结算 1 攻击(p→1)→ 结算 2 蓄力空拍(p→2)→ 结算 3 前被主角击杀 → 重击不兑现、指针冻结于 2;若结算 3 行动后才死 → 重击已兑现、p 回绕 0。 +- 边界注记:归 B2——指针所有权、存活校验、冻结不变式、环首初始化、跨轮跨图延续;归 B1——死亡判定时机、结算内先后次序、每结算恰一次推进(义务①)。 + +### F2 攻击行为裸伤害构成(raw_attack_damage) + +**结论**:attack 拍 D = atk + m_amb × L、heavy 拍 D = atk × heavy_multiplier + m_amb × L、charge 拍 D = 0(不读伏击池);伏击附加是后置加法、不进倍乘(合成序钉死);池共享不消耗(同结算多敌读同一 L);B2 向 B1 供 (基线, 伏击) 分量二元组。 + +#### 推导 + +``` +attack 拍:D = atk + m_amb × L 基线 + 伏击附加(m_amb = StatusEffectTable.ambush.magnitude,provisional 3) +heavy 拍:D = atk × heavy_multiplier + m_amb × L +charge 拍:D = 0 不读伏击池——空拍零兑付 +合成序:伏击附加 = 后置加法,不进倍乘 +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 攻击基线 | atk | int [0,999](快照) | EnemyTable 行值 | 0 合法(纯伏击伤敌种) | +| 重击倍率 | heavy_multiplier | int [1,16](快照) | 仅 heavy 拍消费 | 无 heavy 拍不读(非校验项) | +| 伏击层数 | L | int [0, min(雷数, cap)] | 兑付时读值(B1 经 query_active) | dur=1 → L = 本轮受理授予数,无跨轮存量 | +| 伏击量纲 | m_amb | int flat ≥ 1;provisional 3 | B3 行值 | 量纲 = 主角 HP 点数(本 GDD 定稿);锚验收归 B4/B1 | + +验算表: + +| 验算 | 读数 | 说明 | +|---|---|---| +| bugbear(atk=4, mult=2)heavy 拍 L=4 | 4×2 + 12 = **20** | 合成序判别非平凡:塞进倍乘得 (4+12)×2 = 32 ≠ 20;L=4 = cap=4 默认下游戏态上界(域内判别值);query_attack 的 L 为调用方注入的判别探针、B2 不校验其游戏态域(游戏态域由 F3 预截流承载——探针取值与游戏态域分离,实现期若给 query_attack 加 L 域守卫须回改 AC-7) | +| goblin(atk=4)本轮开雷 3 颗 | D = 4 + 9 = **13** | attack 拍基础例 | +| 满编 3 goblin、L=4 | 各 4+12 = 16、合计 48(伏击分量 36) | 多敌共享读值例 | + +- Output Range(默认表值,cap/atk/m_amb/mult 调参后随之重算):goblin attack ∈ [4, 16];bugbear attack ∈ [4, 16]、heavy ∈ [8, 20]、charge ≡ 0(cap=4 封顶 L=4)。 +- 池共享不消耗:同一结算多个敌人的攻击各自读同一个 L(共享、非均分、非先到先得);层数扣减唯一入口 = B3 轮末衰减(dur=1 轮末全清)。 +- 分量供给建议:B2 向 B1 供 (基线, 伏击) 二元组而非合后标量——结算明细"伏击 +N"的 P2 归因行需要分量边界。 +- 极端:charge 拍零兑付 → 蓄力轮授予的伏击层当轮全部蒸发(合法推论;反向 = 读怪奖励"蓄力拍前放心贪雷"——D5 教学条目);无负值分支(clamp/地板归 B1)。 +- 量级注记:"L≥2 伏击分量过半"仅 attack 拍成立(6/10);heavy 拍因基线 ×mult 过半点后移(6/14 < 半)——支配边界 L* = ⌈atk×mult/3⌉ 与"过半"口径按拍别分别读取。 +- 边界注记:归 B1——L 读值、clamp/负值/溢杀、hero 侧减伤合成次序、死亡判定与"这一下是否兑现"。 + +### F3 伏击授予与溢出(ambush_grant_overflow) + +**结论**:granted_r = min(当轮新开雷数, cap)(cap 判定在授予前——B1 预截流);overflow_r = 当轮雷数 − granted_r(每颗溢出雷一行"溢出未生效"明细、含来源);L_r = granted_r(debuff 只活一结算:当轮兑付一次、轮末全清、无跨轮存量);守恒 granted + overflow = 当轮雷数、零静默蒸发;max_stack = 16 结构不可达(双重不可达——死护栏非死字段)。 + +#### 推导 + +``` +granted_r = min(当轮新开雷数, cap) cap 判定在授予前(B1 预截流) +overflow_r = 当轮新开雷数 − granted_r 每颗溢出雷一行"溢出未生效"明细(含来源) +L_r = granted_r debuff 只活一结算:当轮兑付一次、轮末全清、无跨轮存量 +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 当轮新开雷数 | mines_r | int | [0, 8](结构界 = AP) | round facts 经 B1 收集;复开不可能(A2 棋盘面语义) | +| 单轮伏击授予上限 | cap | int(行值 knob) | [0,16],默认 4 | 0 = 该行敌种免疫伏击(全溢出可见);F1 条款 3 供给值的来源字段 | +| 当轮授予数 | granted_r | int | [0, 4] | B1 编排期逐雷 apply_effect;每雷恰一次转化(义务②) | +| 溢出量 | overflow_r | int | [0, 8] | 明细行数;呈现归 B5/D1(义务⑥) | + +验算表: + +| 场景 | granted | overflow | 说明 | +|---|---|---|---| +| cap=4、开雷 6 | 4 层 | 2 行 | 本结算每个攻击行为附加 +12 | +| cap=4、开雷 5–8(灾难轮) | 4 | 1–4 | cap 反灾难护栏绑定域 | +| cap=0、开雷 m | 0 | m | 免疫极端——全溢出、伏击全无效但全部可见(P2 诚实) | + +- cap 默认 4:反灾难护栏——绑定阈 = 单轮 5–8 雷灾难轮,溢出协议获真实消费域。 +- 结构不可达验证:单轮 L ≤ min(8, cap=4) = 4 < 16,且 dur=1 无跨轮存量 → B3 满层拒绝对 ambush 行双重不可达(不可达性由 dur=1 ∧ AP ≤ 8 承载、与 cap 无关——M=16 是死护栏非死字段)。 +- 重验触发:ambush.duration ≥ 2 ∨ AP 经济改动 ∨ ambush 新增非 mine_hit 授予源(旗源授予不计入 mines_r、cap 预截流漏账 → L 可达 16 触发 B3 满层拒绝、与 F3/B3 双溢出协议冲突——OQ6 标错惩罚采纳时强制重验)。 +- 边界注记:归 B1——预截流编排、授予次序(行主序雷格序为建议序)、每雷恰一次、溢出行协议。 + +### F4 条款 3 供给口径(clause3_supply) + +**结论**:每结算敌方伏击暴露 = k × L × m_amb(k = 本结算存活敌中攻击/重击拍的单位数,确定性可算);每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb(cap > AP 界时 cap 腿松弛);跨结算累计 = 轨迹量、非表值、不设上界(dur=1 每结算清零);B1 对账取"每结算"口径即可闭合、无需战斗长度假设。 + +#### 推导 + +``` +每结算敌方伏击暴露(精确式)= k × L × m_amb k = 本结算存活敌中攻击/重击拍的单位数(确定性可算) +每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb + cap > AP 界时 cap 腿松弛——默认 3×min(8,4)×3 = 36;cap≥8 时真可达上界 72 而非 144 +跨结算累计 = 轨迹量,非表值、不设上界 dur=1 每结算清零、无存量累积;战斗再长不叠加同一层 +``` + +- 上界读数:默认满配 3×min(8,4)×3 = 36、cap ≥ 8 时真可达上界 72 而非 144;默认行值 unit_count=1 下 = 12。 + +- 口径依据(用户定稿原话转写):"不设(每战)上界——踩雷的 debuff 只存在一回合:在一次结算中有效,之后就没有 debuff 了。"——debuff 单结算寿命 = 自然边界。 +- B3 红警闭环:ambush 量纲已由 F2 供给(registry OQ2 关闭条件);条款 3 左臂(安全格 buff 当量)载体 = 主角基础攻击(恒在,attack_up 复活)与 B4 奖励行值;闭合回执见 registry clause3_supply(B1 F4)。 + +方向性预检验算表(provisional——真实对账 = A1 actuals p10 + B1 归一表): + +| 项 | 读数 | 说明 | +|---|---|---| +| 盲猜端单格 P(雷) | ≈ 0.5–0.7 量级 | 有信息挖掘端趋 0(A1 参考:密度 0.10 / target 0.95)——不等式真实战场在残余盲猜集 | +| 右臂(cap=4) | 0.64 × 12 ≈ 7.7 | 条款 3 右臂代理读数 | +| 左臂(条款 2 最低档锚 +50pp) | 0.36 × 50 = 18 | 条款 3 左臂代理读数 | +| 方向稳健裕度 | ≈ 2.3× | registry clause3_supply.direction_precheck 同步回写 | + +- 量级注记(支配边界):atk=4 时 L ≥ 2 伏击分量即过半——常态支配的修正入口 = 敌种 atk 曲线(C3 所有权)或 m_amb 锚(B4/B1 验收),本 GDD 权限外、仅登记方向;cap=4 只治尾部灾难(5–8 雷轮)。 + +### 显式排除声明 + +| 对象 | 归属 | +|---|---| +| 伤害 clamp/地板/溢杀、乘区合成、减伤读法、死亡判定时机 | B1 SettlementTable | +| F1 条款 3 不等式闭合对账(输入 = A1 actuals p10,非 target 参数) | B1 单一 owner | +| m_amb(+3)行值锚验收、weaken 档位 | B4/B1(A3 条款 2/3) | +| 敌种 hp/atk/pattern 曲线数值、难度修饰 | C3 | +| 伤害道具/奖励池(hero 侧伤害来源) | B4 | + +B2 只对"套路怎么转、一下打多少的两块拼图、一轮吃几层、交给 B1 对账的口径"负责。 + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Unspawned | 构造后未实例化 | 无接口副作用;唯一出口 = B1 编排实例化 | +| Alive | 实例化(行值快照 / hp 入参) | 可被指定目标(顺位)、按行为环作用、计入全灭判定;受击 hp 递减 | +| Dead | hp = 0(判定时机归 B1) | 移出顺位、不再作用、不计入"未灭";不可逆(MVP 无复活);句柄保留只读可查(query_beat 冻结值) | +| Despawned | battle_over(B1 编排 despawn_all) | 全部单位状态消亡、不跨战斗;逻辑失效态——失效句柄查询返回空 + 诊断、作用接口拒绝;唯一出口 = 重新 spawn(新战斗) | + +不变式(debug 断言口径):hp ∈ [0, max_hp];敌单位数 = unit_count(战斗内不增——summon 未实现);行为环指针 ∈ [0, 环长)(Alive 期间按结算推进;charge 空过是环位置语义、非独立状态);违规 = debug 断言中止(进程致命保留给 D2 数据通道)。行为级证据 = AC-1(数)、AC-3(指针)、AC-4/AC-6(hp floor)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 敌人在轮到它行动前被击杀 | 本拍不打、指针冻结("死亡即不动手");行动完成后才死 → 已行动已推进(heavy 已兑现) | F1 / Rule 4 | +| EC2 同种单位部分死亡 | 存活者永远同拍(同步性质定理)——幸存者轮转不受任何死亡时序干扰 | F1 | +| EC3 全体敌单位死亡 | 存活敌集 = ∅、"敌人全灭"成立、胜负判定归 B1;单体/AOE 无有效目标;敌侧伏击池随轮末衰减自然清空(无兑付者) | Rule 1 | +| EC4 主角 hp = 0 | 战斗失败判定素材(判定时机归 B1);battle_over → 全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态 | Rule 1 | +| EC5 战斗中敌单位数 ≠ 本战实际数量 n | 结构不可能(MVP 无 summon/增援/分裂;n = B1 encounter 骰定值——区间语义下实例化后仍不增)——出现即 debug 断言中止(进程致命保留给 D2 数据通道) | 实例不变式 | +| EC6 敌种行 cap = 0(免疫伏击的合法极端) | granted ≡ 0、每颗雷各一行"溢出未生效"明细(全部可见、全部无效——P2 诚实);该遭遇条款 3 右臂仅剩基线伤 | Rule 5 / F3 | +| EC7 单轮踩雷 5–8 颗(灾难轮) | granted = 4、溢出 1–4 行;本结算每个攻击行为附加恒 +12 | F3(cap 反灾难护栏绑定域) | +| EC8 全部存活敌同拍蓄力(charge 轮) | 零兑付(k=0);该轮新踩的雷照常授予、轮末照常全部蒸发(合法推论 + 读怪奖励——D5 教学条目已登记) | F2/F3 | +| EC9 敌单位在 heavy 拍前被杀 | 重击永不兑现、指针冻结于 heavy 拍——"抢杀窗口"读怪收益(P1) | F1 | +| EC10 敌种 pattern 环长 = 1(goblin) | mod 1 恒等 → 每拍 attack、无自然空窗(兑付率 ρ=1——该敌种伏击威胁无衰减节拍) | F1 | +| EC11 战斗跨多张扫雷图 | 单位状态(hp/指针)与敌侧伏击池跨图延续(换图/刷新不清空——B3 EC8 同款);雷格兑付资格随图消亡、新图新雷新资格(A3/A2 域,B2 无从感知也无须感知) | Rule 4 | +| EC12 战斗中热重载 EnemyTable/HeroTable | D2 战斗中 reload 拒绝门(D2 EC8);已实例化单位不受任何表重载影响(行值快照) | Rule 10 | +| EC13 行集缺行 / 请求敌种悬空 | D2 R1 实体键精确匹配 → 悬空引用致命(D2 Rule 6 通道);B2 无软回退(错误配置上线比崩溃更伤——P1/P2 信任底线) | D2 R1/Rule 6 | +| EC14 遭遇坐标越界(level_index < 1 / node_depth < 0) | D2 R1 前置守卫致命(D2 EC14)——B2 不自行 clamp | D2 EC14 | +| EC15 unit_count = 1 | 固定顺位顺延与 AOE 分流无消费者(Rule 7 空转)、AOE ≡ 单体——合法退化,非错误 | Rule 7 | +| EC16 主角 hp 入参越界(> max_hp 或 ≤ 0) | 拒绝实例化(false + debug 诊断,承 A3 拒绝口径;进程致命保留给 D2 数据通道)——编排方供给异常显式暴露、不静默吸收 | Rule 9 | +| EC17 同结算内"踩雷授予"与"敌攻击兑付"的先后 | 授予在编排期先于兑付 → 本轮踩的雷当轮即兑付(若该轮有攻击拍);读值语义半句归 B2(无内部缓存、无时间维度——AC-7 腿4)、次序半句归 B1(义务①②编排域) | F2/F3 | + +## 依赖 + +**上游**:D2(hard)——EnemyTable schema + 双敌种行回填(本 GDD 为回填 owner)+ HeroTable 新增(走 D2 接口漂移规则回写其 Core Rules 表清单)+ ConfigDB 单点读取(R1 行解析)。无其他玩法上游;A2/A3/A1 的事实均经 B1 结算编排转化(零直接接口)。 + +**下游与邻接**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | hard(provisional——B1 未设计) | 单位状态查询/受击/推进接口(冒烟 API 面全表)+ B1 侧义务清单 6 项(缺项即 blocking,见义务台账):① 推进每结算恰一次 ② 雷→伏击恰一次 ③ clamp/死亡时机成文 ④ 条款 3 对账(cap 表值 + 量纲 + A1 actuals p10)⑤ 默认目标/AOE 分流 ⑥ 溢出行协议 | +| B3 | 相邻(零运行时依赖——验收锚关系非依赖边,B3 先例同款) | ambush/weaken 的量纲与作用域验收锚归 B2(Rule 6 兑现 registry OQ2);side-wide MVP 兼容(B3 接口零变更);target_ref 扩展位义务(首个单体效果上线时补列,随批:B1 归集/D1 呈现裁决) | +| B4 | soft(provisional) | 伤害道具默认目标消费(Rule 7 固定顺位);伏击清单扩展联动(新增 mine_hit 行时本 GDD 同步修订);B4 道具/增益 = 主角基础攻击之外的规划面伤害(非唯一伤害来源);attack_up 消费面 = 主角战斗段先手攻击(Rule 9 / B1 战斗段) | +| A2/A3 | 间接(零直接接口) | 雷/旗事实经 B1 转化;AP 界(单轮开雷 ≤ 8)= cap 默认值结构来源;weaken 档位锚归 B4/B1(A3 条款 2),B2 无表值 | +| A1 | 间接 | 雷数分布影响伏击吞吐——条款 3 对账消费 A1 actuals p10(B1 义务,非 B2 域) | +| C3 | 下游(soft,provisional) | 难度修饰施加点/算式归 C3 定稿(Rule 10);EnemyTable/HeroTable 曲线数值调参所有权归 C3(A1 参考行先例——MVP 默认值全部 provisional);多敌种同深度选择规则共裁;新敌种投放节奏 = 关卡命脉(MVP 敌信息总量 = 2 环 3 行为、L2 见底——每关须有新敌种或新环段投放;goblin 单拍环 playtest 证伪项 OQ8) | +| C1 | 下游(soft,provisional) | 遭遇构成(战斗触发、(L,D) 供给、unit_count 覆盖语义)归 C1;跨战斗 HP 持久性归 C1/C2 | +| D1 | soft | 视觉推测型呈现义务 4 项(缺项即 blocking,见义务台账);敌单位 HUD(血条/顺位/姿态)呈现;顺位标记资产需求(呈现契约表 #3) | +| B5 | soft | 行为环视觉状态表现(蓄力姿态等——预告时序 Rule 8⑤);结算明细敌方行呈现(数据源 = B1 流水;首个 heavy 兑付明细行分量高亮建议——首遇校准课,ux 注记非硬义务) | +| D5 | soft | 读怪教学(敌种环模式可学、顺位规则)+ "蓄力拍前放心贪雷"教学(charge 轮伏击池蒸发 = 读怪奖励的涌现素材——F2 极端行为)+ 首次蓄力姿态一次性提示候选("它在蓄力,这轮踩雷不挨打"——L1 goblin 无 charge 拍、首课在 L2;MVP 可先上一次性提示、不等垂直切片完整教学)——垂直切片期 | + +- 双向一致性核对(systems-index 依赖分层表):B2 ← D2 ✅(索引仅此一条硬边,其余为事实读取不列边——D2 注记口径);B1 ← …B2 ✅ · C3 ← D2,B2 ✅ · C1 ← C2,C3,B2,B4 ✅。 + +### 义务台账(本 GDD 登记,缺项即 blocking——B3 对 B1 登记先例) + +对外引用口径:B1 清单在 B1/registry 侧引用名为「B2 义务①–⑥」(b1-settlement 上游表同口径);D1 清单引用名为「D1 呈现义务①–④」。 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| B1-① | 单位接口调用集中在结算编排、行为环推进每结算恰一次(double-advance 无守卫——信任编排,B3 义务⑥同款);结算内调用序成文:兑付读值 query_attack → 受击上报 apply_damage → 推进 advance——倒序会读到下一拍(F2 依赖当前拍;比 double-advance 更常见的实现错误),B1 冒烟须回放结算步流 | B1 | 已登记 · 缺项即 blocking | +| B1-② | 雷→伏击事实转化恰一次(每颗雷格实例至多一次;B3 EC6 对侧同款) | B1 | 已登记 · 缺项即 blocking | +| B1-③ | 伤害 clamp/负值/溢杀语义与死亡判定时机成文(Rule 1 B1 域) | B1 | 已登记 · 缺项即 blocking | +| B1-④ | F1 条款 3 闭合对账:消费 B2 advantage_per_round_cap 表值 + 伏击量纲(Rule 6 换算)+ A1 actuals p10(非 target 参数——A1 登记口径) | B1 | 已登记 · 缺项即 blocking | +| B1-⑤ | 默认目标规则消费与单体/AOE 分流(Rule 7;顺位快照 = 生成序) | B1 | 已登记 · 缺项即 blocking | +| B1-⑥ | 伏击 cap 溢出的"溢出未生效"明细行协议(Rule 5 后半;呈现归 B5/D1) | B1 | 已登记 · 缺项即 blocking | +| D1-① | enemy_id→图像线索映射清单成文(形态/姿态↔行为状态;预告时序 = 下一拍预告、挖掘期全程可见〔Rule 8⑤〕;含强度档位与环长直觉编码说明〔Rule 8②〕;形状+色相双通道,art-bible 约束) | D1 | 已登记 · 缺项即 blocking | +| D1-② | 线索映射全局恒定(映射键 = enemy_id——跨 run 恒定、禁止重掷;Rule 8③ 的呈现侧承载) | D1 | 已登记 · 缺项即 blocking | +| D1-③ | 单位顺位与存活可读(固定顺位规则的推测前提——Rule 7;顺位标记资产需求见呈现契约表 #3) | D1 | 已登记 · 缺项即 blocking | +| D1-④ | 敌方 side 池状态呈现口径(伏击层数经 B1 流水——B3 敌侧延伸) | D1 | 已登记 · 缺项即 blocking | + +### registry / 接口漂移回写义务 + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| R-1 | B3 单敌假设回验结果 = 不成立(定多敌)→ 触发 B3 预登记的接口漂移条款("定多敌即触发 target_ref 接口漂移补列"):registry status_effect.single_enemy_assumption 注记更新 + systems-index 门禁记录登记(B3 GDD 正文不改,以其"以 registry 为准"先例承载) | B3 / registry | 待回写(Phase 5b/5d) | +| R-2 | 索引 B2 行描述为窄口径("敌人类型/词条、目标选择规则")→ Phase 5d 更新进度行时补战斗单位系统身份(CD 门禁扩容口径) | systems-index | 待回写(Phase 5d) | +| R-3 | registry status_effect.ambush 量纲定稿注记回写(F2 定稿——B3 registry OQ2 关闭条件) | B3 / registry | 待回写(Phase 5b) | +| R-4 | HeroTable 新增 → D2 Core Rules 表清单 + registry 接口漂移回写 | D2 / registry | 待回写 | + +## 呈现与 UI 契约(数据面义务——B2 无自有渲染面,呈现归 D1/B5) + +| # | 数据面义务 | 归属/呈现 | +|---|---|---| +| 1 | 敌种视觉身份(形状双通道):双敌种剪影必须一眼可辨(视觉锚 2 形状+颜色双通道、色弱可用性);敌种形态承载强度档位直觉编码("大个子多半有重击"式——Rule 8② 的呈现侧) | D1/B5,art-bible 约束 | +| 2 | 行为环视觉状态(承重规格):每个敌种的每类行为(attack/charge/heavy)对应恒定且互异的视觉状态——bugbear 蓄力拍 = 可辨识的蓄力姿态;映射每 run 恒定、禁止重掷(Rule 8③ 护栏;线索清单成文义务 = D1×4①)。MVP 敌种视觉状态清单:goblin 1 态(攻击)、bugbear 3 态(攻击/蓄力/重击) | D1/B5 | +| 3 | 资产需求下限(存活意图态之外三类):受击反馈(多敌同屏归因"谁挨了打"的 P2 前提——两敌种可共用通用受击反馈一组、须显式登记)、死亡表现("重击被掐灭""抢杀窗口"兑付可见性是 P1 兑付点——死亡瞬间须可辨识、不得直接消失)、顺位标记呈现元素(数据面 = B2 顺位查询;资产与呈现归 D1×4③、同等 blocking)——合称资产需求下限,D1/B5 照单生产、不得隐式缺席 | D1/B5 | +| 4 | 单位呈现数据供给:hp/max_hp(血条数字)、顺位(固定顺位目标的可读前提)、存活态、当前拍视觉状态——数据源 = B2 单位状态查询(经 B1 编排流水) | D1 | +| 5 | 敌方增益呈现:伏击池层数的 HUD 呈现口径(D1×4④——数据源 = B1 流水经 B3 投影);结算明细敌方行使用分量二元组("攻击 13 = 基线 4 + 伏击 9"——F2 供给建议)——P2 归因的敌侧兑付面 | D1/B5 | +| 6 | 视觉锚适用与双舞台陷阱:血条/伤害数字暗色底高对比(锚 1);B2 无棋盘渲染面——敌单位出现在棋盘上还是独立战斗场景 = B5 前的"战斗表现美术测试"锁定(game-concept 双舞台资产管线陷阱),B2 只锁数据面与状态清单、不预设舞台;敌人层绘制优先级:锚 3 优先级表(数字>小人>旗>变异纹理>底色)不含敌单位——art-bible 创建前须裁决敌人层插入位置(姿态精灵与数字/旗/小人的遮挡关系),随"战斗表现美术测试"一并锁定,否则"哪些数字可信"的心智模型分裂 | D1/B5 / art-bible | +| 7 | UI 三项义务(均为已登记义务的数据面):① 敌单位 HUD(血条/顺位/姿态——D1×4③)② 结算明细敌方行(分量二元组 + 伏击溢出行——B1 义务⑥协议、B5 呈现)③ 教学口径(读怪 + 蓄力贪雷——D5);MVP 无敌图鉴/敌人列表 UI(敌种名录 2 个、学习成本内),名录扩展时再议(垂直切片) | D1/B5/D5 | +| 8 | 音频:MVP 无需求(攻击/死亡/蓄力音效归 D4 Alpha;临时 SFX 随垂直切片——AD 项先例);蓄力拍的音频前摇是"读招"的潜在第二通道——登记为垂直切片可选项(非 MVP 义务) | D4 | + +## Tuning Knobs + +| Knob(表行值,D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| EnemyTable.unit_count | 1(provisional) | 同步轮转下齐射爆发(全攻轮 side 伤 ×N,非平滑) | =1 时 AOE 分流/顺位顺延零消费者(Rule 7 空转) | +| EnemyTable.hp | 18(provisional,出处待查) | 战斗拉长 → 伏击跨轮累计放大 | 秒杀级 → 惩罚无兑现窗口、伏击机制名存实亡 | +| EnemyTable.atk | 4(provisional,出处待查) | 伏击稀释 → 踩雷无感(P3"风险与收益同源"断) | 支配边界 L*→1,惩罚接管战斗、敌种属性被 C3 曲线稀释 | +| EnemyTable.heavy_multiplier | 2(provisional) | heavy+伏击同拍成秒杀拍(读怪规避价值升、容错归零) | =1 时 heavy ≡ attack、三拍环退化两拍(读怪素材坍缩) | +| EnemyTable.advantage_per_round_cap | 4(本 GDD 定稿) | →8 时溢出协议死码回归、支配区扩大、条款 3 右臂膨胀 | ≤2 时 cap 常态绑定 → 边际惩罚归零("再多踩也不痛")、溢出行噪音、P3 归因梯度塌陷 | +| HeroTable.max_hp | provisional(出处待查) | 容错升、治疗道具贬值 | 灾难轮秒杀感(与 cap 交互) | +| HeroTable.atk | 6(原型锚 provisional 归 C3;域 [1,999]、0 加载拒绝) | 击杀预算贬值 → 道具供给/敌 hp 曲线联动重算(C3,禁止独立调参) | 战斗拖长、基础攻击存在感稀薄(道具侧支配) | + +- 非 knob(结构冻结):行为枚举七类封闭(扩展 = GDD 修订非调参)、死亡即不动手、先作用后推进、伏击池共享读法、合成序后置加法、伏击量纲 = HP 点、固定顺位规则、每行集恰一敌种、零随机——修订走本 GDD 流程。 +- Knob 交互:cap × atk(支配边界 L* = ⌈atk×mult/3⌉ 联动)禁止独立调参——联动评估归 B1 条款 3 闭合对账;unit_count × cap(单结算上界双线性);heavy_multiplier × atk(heavy 拍支配)。pattern 内容(环长/charge 占比 → 兑付率 ρ)属内容行调参,所有权归 C3。 +- 零伤害死局组合护栏(OQ9):hero atk=0 已被 HeroTable schema 域下界杜绝([1,999]、0 加载拒绝)——"全场恒零伤害、战斗永不定局"结构不可能;残余退化 = 行 atk=0 且 cap=0 → 无威胁敌(战斗可胜但零风险——内容调参禁区注记,非死局);跨表量级护栏(atk×mult 上界 vs max_hp 域)owner = 实现期 ADR/D2;混编遭遇(多敌种同战斗)下 side 池 cap 取哪行值未定义——当前结构(每行集恰一敌种)恒单行值,混编引入时随 OQ7 改键一并裁决。 +- 调参所有权:MVP 全部默认值 provisional、曲线数值归 C3(A1 参考行先例);B1 条款 3 对账落定前,cap 与伏击量纲相关联调须重跑方向性预检并同步 registry(接口漂移义务)。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | B2 是单位层纯逻辑类——非 autoload、无场景依附、无自有渲染面。冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code)、ConfigDB autoload 由脚本显式实例化);单位容器与两表 schema 经 class_name 直引实例化(每用例全新实例、无跨用例状态——AC-17 扫描对该实例化路径豁免);每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明) | +| 被测面与调用方 | B1 未实现(设计顺序第 9 位)——冒烟直接驱动 B2 最小 API 面(冒烟脚本扮演编排方,按"实例化 → 查询 → 受击 → 推进 → 消亡"结算编排序列驱动);B1 就绪后改走真实调用方(结算编排)、断言口径不变 | +| 时序口径 | B2 无结算 API——"一次结算"在冒烟中 = 调用方对每个存活敌单位恰一次 advance(含兑付读值与受击注入)的构造窗口(主角无行为环、不在推进域内——Rule 4);"每结算恰一次推进"是 B1 义务①——冒烟以构造序保证前提、不断言编排正确性;"轮到该单位之前被杀" = 同一构造窗口内 advance(u) 之前的 apply_damage(u, …);多步 GIVEN 均给出构造序列 | +| 只读观测量 | 敌单位投影七字段 enemy_id · hp · max_hp · atk · heavy_multiplier · pattern(快照)· advantage_per_round_cap(快照)+ beat(当前拍)+ 存活态;hero 投影 hp · max_hp · atk | +| 相位与投影 | Alive/Dead 由 query_alive 成员差分推断(Dead 句柄经 query_beat 冻结值可读);Despawned = 逻辑失效态(状态位实现——容器为 RefCounted 纯逻辑类、对象不提前释放;失效句柄上查询返回空 + 诊断、作用接口拒绝;瞬态纪律同 A3 RoundEnd);Unspawned 无观测量;投影一律为快照(pattern 等引用字段返回拷贝——原地改写返回值不影响容器,AC-11 腿 3);容器公开可写属性为零(hp 等仅经 apply_damage 变更——AC-17 扫描域);实例不变式(hp ∈ [0, max_hp]、敌单位数 = unit_count、指针 ∈ [0, 环长))为 debug 断言自守——违规路径不可由封闭 API 构造(负空间)、不经冒烟断言(进程致命保留给 D2 数据通道);行为级证据 = AC-1(数)、AC-3(指针)、AC-4/AC-6(hp floor) | +| 期望值纪律 | hp(18)/atk(4)/heavy_multiplier(2)/unit_count/max_hp 均为 provisional knob(Tuning Knobs 节)——各 AC 的 THEN 具体数值按当前 ConfigDB 表值推导、不写死;记号:h_g/a_g(goblin 行)、h_e/a_e/m_e(bugbear 行)、u_c、M_h、c_r(所在行 cap)、p_g/p_e(pattern 表读值)、m_amb = StatusEffectTable.ambush.magnitude 表读值(仅作 AC-7 注入值来源);pattern 环内容属内容行调参(C3 所有权)——轨迹断言一律按 p 表读值推导;L/mines_r/n 等入参为构造选择(显式登记)、期望值由表读值推导 | +| 结构定值断言 | 合法集:敌种 id 集 {goblin, bugbear, ogre_chief}(Rule 3 MVP 收窄——C3 修订批扩容三敌种)、pattern 元素域 {attack, charge, heavy}(枚举封闭)、每行集恰一敌种、HeroTable 单行、HeroTable.atk 域 [1,999](0 加载拒绝;MVP 值 6 provisional 不作定值断言) | +| 致命类收窄与 manifest | B2 冒烟设恰两根致命夹具(均为 B2 表特有校验类别):runner manifest 退出码行 rowset_dup→≠0 · pattern_reserved→≠0(独立 headless 进程、游戏通道,D2 AC-2b 协议同型);其余致命类别(D2 R1 悬空敌种/行集缺行〔EC13〕、遭遇坐标越界〔EC14〕、通用枚举域/域校验)由 D2 AC-2 同类别 loader 级夹具承载(表无关,豁免声明收窄保留——B2 冒烟仅以合法坐标驱动 spawn);HeroTable 单行类别条件性(可选根);schema 侧以程序构造断言域类别(AC-12/13) | + +### 冒烟 API 面(provisional,封闭枚举——实现期 ADR 定名) + +| API | 签名 | 语义与返回口径 | +|---|---|---| +| spawn | `spawn(level_index, node_depth, hero_hp, n) -> bool`(n = 本战实际数量 override——B1 encounter 骰;定值行/省略 n = unit_count 表值) | 解析遭遇行 → 行值快照 → 实例化 n 个敌单位(行主序生成序)+ 主角(hp 入参,MVP 冒烟以 max_hp 入参——Rule 9);Alive 相位重复 spawn → false + 诊断(相位守卫) | +| query_alive | `query_alive(side) -> Array` | side ∈ {hero, enemy};生成序稳定枚举;只读 | +| query_beat | `query_beat(u) -> String` | 当前拍 ∈ pattern 元素;Dead 句柄保留只读可查(冻结不变式的观测量);hero 句柄 → 空 + 诊断(主角无行为环——Rule 4);只读 | +| query_attack | `query_attack(u, L, m_amb) -> {baseline, attach, total}` | F2 分量供给面;L/m_amb 由调用方供值(B1 兑付读值的冒烟等价——B2 对 B3 零运行时依赖的边界由此保持);L 为判别探针入参、B2 不校验其游戏态域(游戏态域由 F3 预截流承载);Dead 句柄 → {0, 0, 0} + 诊断;只读 | +| apply_damage | `apply_damage(u, n) -> bool` | 受击上报;hp 递减、floor 0 → Dead;n ≤ 0 → false + 诊断(无伤害作用非法);Dead 句柄 → false + 诊断(MVP 无复活) | +| advance | `advance(u) -> bool` | 存活才推进("先作用后推进"的推进半);false 分两级:存活校验失败 = 轮到前被杀的预期冻结、无诊断(EC1 常态路径——抢杀窗口是设计卖点,诊断刷屏会淹没信号);相位/句柄失效 = false + 诊断;hero 句柄 → false + 诊断 | +| default_target | `default_target() -> u\|null` | 固定顺位供给面;只读 | +| ambush_grant_plan | `ambush_grant_plan(mines_r, cap) -> {granted, overflow}` | F3 机制算术纯函数——B1 预截流编排的消费面;只读 | +| despawn_all | `despawn_all() -> bool` | battle_over 消亡;重复调用 → false + 诊断(重复调用守卫) | + +- 返回口径:全部布尔/计数返回 = 受理/拒绝(A3 全 API 布尔口径同款)——"拒绝"直接断言返回值,附 debug 诊断、非进程致命(进程致命保留给 D2 数据通道);五个 query 面(query_alive / query_beat / query_attack / default_target / ambush_grant_plan)只读、无副作用。 +- 封闭枚举即负空间验收形态:无 summon/增援 API(EC5 负空间——战斗内不增员)、无复活 API(Dead 不可逆)、无重置/换图 API(EC11 跨图延续的结构前提)、无伏击池写 API(B2 无池——L 恒由调用方注入,EC17 读值语义);新增上述任何入口 = 接口漂移(GDD 修订非调参)。 +- ADR 前置裁决项:query_attack / ambush_grant_plan 两 face 若实现期裁决归 B1(F2"供给建议"与 F3"预截流归 B1"边界注记存在张力),对应 AC 腿迁移至 B1 冒烟、断言口径不变(OQ①)。 + +### 夹具策略 + +| 根 | 构成 | 服务 | +|---|---|---| +| 默认根 | 干净检出:EnemyTable 双敌种行 + HeroTable 单行 | 全部分支行为 AC | +| enemy_multi_unit | D2 全表清单拷贝 + EnemyTable 仅将 (L_a, D_a) 行(bugbear——三拍环)unit_count 置 3(其余字段域内合法;登记值 u_c_f = 3) | 多敌 AC(AC-5 / AC-7 腿4 / AC-9 / AC-10 腿2) | +| enemy_reloaded | D2 全表清单拷贝 + EnemyTable 数值漂移(登记:hp/atk/advantage_per_round_cap 各 +1、pattern 不动——快照差分判定用) | AC-11 | +| enemy_rowset_dup_species(致命) | D2 全表清单拷贝 + EnemyTable 默认行 + 追加一行与 (L_g, D_g) 行同 (level_index, node_depth) 而 enemy_id 相异("goblin_twin",其余字段域内合法)→ 每行集恰一敌种违规 | B2 表结构致命校验的进程级落点——manifest rowset_dup→≠0(B3 status_effect_dup_key 同型) | +| enemy_pattern_reserved(致命) | D2 全表清单拷贝 + EnemyTable (L_g, D_g) 行 pattern 追加预留值 "guard"(其余合法)→ pattern MVP 校验域拒绝预留值 | Rule 4 fail-fast 的 loader 集成缝落点——manifest pattern_reserved→≠0(A1 solver_rules 冻结先例) | +| hero_extra_row(可选根) | HeroTable 置两行(域内合法——单行全局违规) | 单行校验落点若实现期 ADR/D2 裁定在 loader 缝则启用(manifest 行 hero_rows>1→≠0),否则程序构造承载(AC-13)——可选、两处恰一承载 | + +- 行集登记常数协议(A1 黄金向量 / A2 板登记同源):(L_g, D_g) = goblin 行坐标、(L_a, D_a) = bugbear 行坐标,逐坐标手工推导记录入版本库;失效处置 = 先复核登记、登记无误即实现缺陷。 +- 多敌根选 bugbear 行(三拍环)保证判别力:goblin 行环长 1 恒 attack → 混合拍 GIVEN 不可构造、同步断言零判别力、双单位读值不可构造;改 bugbear 行后混合拍经对单单位重复 advance 构造(冒烟构造序可越出"每结算恰一次"契约——B2 无守卫、构造自由度成立)。 +- 显式声明:夹具值非 MVP 内容调参结论(Tuning Knobs 默认 unit_count = 1——多敌语义在默认根上零消费者,多敌根是 Rule 2 的测试承载、仅供冒烟)。 +- 换根加载经 `load_tables(root, collect_only=true)` 直调(非 D2 `reload()` 路径:不过 set_reload_gate、不发 config_reloaded——门语义归 D2 AC-16/B1,B3 AC-15 同款);程序构造(AC-12/AC-13):两 schema 类 class_name 直引实例、不经 load()/preload();行为夹具根经 `--data-root=` 注入(仅 debug、不入 data/tables/——D2 夹具纪律);各 AC 声明其表根、用例间无顺序依赖。 + +### 覆盖声明(out-of-scope——deferral 生效条件 = B1×6 + D1×4 义务清单,均缺项即 blocking) + +| deferral 项 | 生效条件 / 承载 | +|---|---| +| B1×① 行为环推进每结算恰一次与结算内先后次序(double-advance 无守卫——冒烟单调用方次序即构造序,B3 义务⑥同款) | B1 义务① | +| B1×② 雷→伏击事实转化恰一次 + 编排期授予/预截流调用次序(F3 编排半、EC17"先转化后兑付"次序半句) | B1 义务② | +| B1×③ 伤害 clamp/负值/溢杀语义与死亡判定时机(冒烟只断言 hp 递减、floor 0 与 Dead 翻转——"这一下是否兑现"归 B1) | B1 义务③ | +| B1×④ F4 条款 3 闭合对账(cap 表值 + 量纲 + A1 actuals p10——冒烟只供分量面与 k 可算性) | B1 义务④ | +| B1×⑤ 默认目标消费与单体/AOE 分流(冒烟只断言 default_target 供给面) | B1 义务⑤ | +| B1×⑥ 溢出"溢出未生效"明细行协议(呈现归 B5/D1——冒烟只断言 overflow 计数会计) | B1 义务⑥ | +| D1×①② 图像线索清单成文与映射每 run 恒定(Rule 8 视觉面——视觉映射不可 GIVEN-WHEN-THEN;验收形态 = 映射键契约〔enemy_id〕+ B2 侧行为级承载:行为环零随机可学〔AC-3/AC-15〕) | D1 义务①② | +| D1×③ 顺位与存活可读呈现 | D1 义务③ | +| D1×④ 敌方 side 池呈现口径(B3 敌侧延伸) | D1 义务④ | +| EC8 池蒸发腿(dur=1 轮末清池——B3 AC-8/AC-9 既有覆盖,跨引不重复;B2 侧零兑付半 = AC-7 腿3 / AC-9 腿1) | B3 AC 既有 | +| EC13/EC14 端到端致命(D2 R1/EC14 既有通道) | D2 AC-2(manifest 豁免) | +| Rule 10 后半难度修饰施加点(B2 以实例化后数值运作——修饰算式无表值可断) | C3 provisional | +| 跨战斗 HP 持久性 | C1/C2 | +| 曲线数值与遭遇分布定值(AC-14 显式不作分布定值断言) | C3 | +| atk 激活裁决——已退役:基础攻击恒存在,域断言由 AC-13/AC-14 承载 | 无遗留义务 | +| Rule 8"不直接显示数值"负空间(无意图数值 API——冒烟 API 封闭枚举承载) | API 封闭 | +| qa-lead 缺口预检 7 项处置:预检 1/2 入 OQ①②;预检 3/4 随批修正 F4 满配语境与 EC17 切分;预检 5–7 承载于 ADR 裁决项 / HeroTable 可选根 / 坐标登记协议 | 已处置 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | ConfigDB 默认根加载完成、全新 B2 容器(Unspawned) | `spawn(L_g, D_g, M_h)`(hero_hp = max_hp 表读值——MVP 冒烟口径);Alive 相位再 `spawn(同坐标, M_h)` | 首次 true;`query_alive(enemy)` 恰 u_c 个单位(u_c = unit_count 表读值)、全部同 enemy_id、每单位投影 hp = h_g、max_hp = h_g、atk = a_g、pattern = p_g、cap = c_r(行值快照五字段逐一 = 表读值——Rule 10 授予时快照基线);`query_alive(hero)` 恰 1、hp = 入参值;敌投影序 = 行主序生成序(默认根 u_c=1 平凡——多敌序断言由 AC-5 夹具根承载);再 spawn → false + 诊断、容器不变(相位守卫——B3 EC11 输入×相位矩阵同款) | Rule 2/9/10 | +| AC-2 | 默认根、全新实例 | `spawn(L_g, D_g, hero_hp)` 分别取 hero_hp ∈ {M_h+1, 0, −1}(越界三腿:> max_hp / ≤ 0——EC16 口径);随后 `spawn(L_g, D_g, 1)`(域内下界合法值) | 越界三腿均 false + 诊断且 `query_alive` 双侧恒空(拒绝原子性:无半实例化——失败腿不留任何单位);域内下界 → true——越界拒绝精确以域为界、不扩大拒绝域(编排方供给异常显式暴露、不静默吸收) | EC16 / Rule 9 | +| AC-3 | `spawn` 完成(bugbear 坐标 (L_a, D_a)) | 腿 1 读初拍;腿 2 连续 advance;腿 3(goblin 坐标)连续 3 次(构造选择)advance;腿 4 任意长 advance/受击序列 | 腿 1(环首初始化):`query_beat(u)` = p_e[0];腿 2(推进与回绕):均 true 且 beat 轨迹 = p_e[k mod \|p_e\|] 逐拍断言、\|p_e\| 次后回 p_e[0](环尾回环首——指针恒 ∈ [0, 环长) 的行为级证据);腿 3(环长 1 恒等):beat 恒 = p_g[0](mod 1 恒等——每拍 attack、无自然空窗、兑付率 ρ=1 的行为级形式;前提 \|p_g\| = 1 按表读值核对——pattern 内容属 C3 内容行调参,前提不满足时本腿显式 SKIP、不假失败);腿 4(跨轮跨图延续 + 无重置入口,EC11):hp 与 beat 投影 = 驱动序列的确定函数——除 despawn_all 外无任何 API 入口使单位状态重置(结构性裁决;"换图"事件对 B2 不可观测(零接口),端到端"真实换图下单位状态延续"归 B1 集成——编排契约仅 battle_over 调 despawn、非新增义务,B3 AC-10 裁决同款) | Rule 4 / EC10 / EC11 / F1 | +| AC-4 | 默认根;前提按 p_e 表读值核对:\|p_e\| ≥ 3 且 p_e[1] = charge 且 p_e[2] = heavy(默认表值满足;pattern 内容属内容行调参,前提不满足时本 AC 显式 SKIP、不假失败) | 腿 1 构造推进至 charge 拍后 `apply_damage(u, h_e + 5)` 再 `advance(u)`;腿 2 新实例构造至 heavy 拍 `advance(u)` 后 `apply_damage(u, h_e)`;腿 3 对 Dead 句柄 `apply_damage` | 腿 1(heavy 拍前被杀):apply_damage → true、hp 投影 = 0(溢杀 floor:hp ∈ [0, max_hp] 不变式自守——clamp/溢杀语义归 B1×③ 的声明不变);advance → false(预期冻结级——无诊断,API 面两级 false 口径)、`query_beat(u)` 仍 = p_e[1]——冻结于 heavy 前一拍、重击永不兑现("死亡即不动手"+"抢在重击前击杀"读怪收益的行为级证据);腿 2(行动后才死):advance → true、随后 Dead、`query_beat(u)` = p_e[0](已行动已推进——heavy 拍已消费、指针回绕);腿 3(不可逆):false + 诊断(MVP 无复活——States 不可逆) | EC1 / EC9 / F1 | +| AC-5 | 夹具根 enemy_multi_unit(u_c_f = 3 登记值,bugbear 行)`spawn(L_a, D_a, M_h)` 完成 | 腿 1 读三单位初拍;腿 2 定义结算步 = 按生成序对各存活敌单位 advance 恰一次(B1 义务①的冒烟构造等价——hero 无环不在推进域),在某步 advance(u2) 之前 `apply_damage(u2, h_e)` 杀之(h_e = bugbear 行 hp 表读值);腿 3 全程观测规模 | 腿 1(同拍基线):spawn 后三单位 `query_beat` 恒等(同种同指环首、同属性);腿 2(死亡时序不乱拍):u2 本步不打(advance false、指针冻结);步末 `query_beat(u1)` = `query_beat(u3)`、连续多步后幸存者 beat 恒相同(同步性质定理——任何死亡时序都不会让幸存者乱拍);腿 3(单位数单调递减):`query_alive(enemy)` 规模 ∈ {3, 2, 1} 严格递减、无增员(EC5 负空间的行为面——无 summon API) | Rule 2 / EC2 / F1 | +| AC-6 | 默认根 + AC-5 根 | 腿 1(默认根)u_c 个单位逐一 `apply_damage` 致死;腿 2(AC-5 根)`apply_damage(hero, M_h)` 后、`query_alive(enemy)` 仍含存活单位时 `despawn_all()`;随后重复 `despawn_all()`;随后重新 `spawn` | 腿 1(全灭,EC3):`query_alive(enemy)` = 空 Array(存活敌集 = ∅——"敌人全灭"成立,胜负判定归 B1);敌侧伏击池"无兑付者轮末清空"半 = B3 dur=1 decay(跨引 B3 AC,out-of-scope);腿 2(主角死亡与消亡,EC4):hero hp = 0(失败判定素材——判定时机归 B1);despawn_all → true、`query_alive` 双侧空(全部单位 Despawned——不存在"主角死后敌继续攻击"的中间态);Despawned 句柄上 query_beat 返回空 + 诊断、advance/apply_damage → false(域闭);重复 despawn_all → false + 诊断(重复调用守卫——B3 EC11 同款);重新 spawn → true 且为新战斗(Despawned 后唯一出口——下一场重启、不跨战斗) | Rule 1 / EC3 / EC4 | +| AC-7 | 默认根 `spawn(L_a, D_a, M_h)` 完成、m_amb = StatusEffectTable.ambush.magnitude 表读值 | 腿 1 attack 拍 `query_attack(u, L=2, m_amb)`;腿 2 `query_attack(u, L=4, m_amb)` 合成序判别(判别非平凡前提 m_e ≥ 2——默认表值满足;m_e = 1〔合法 knob、heavy ≡ attack〕时负断言两边恒等、本腿显式 SKIP 标记输出——B3 AC-14 腿 3 先例);腿 3 beat 推进至 charge 拍后 `query_attack(u, L=c_r, m_amb)`(游戏态上界注入);腿 4(enemy_multi_unit 根承载——默认根 u_c=1 不可构造)同 L 连续两次 query + 两存活单位各自 query | 腿 1(attack 拍):{baseline = a_e, attach = 2×m_amb, total = a_e + 2×m_amb}(分量二元组——"攻击 13 = 基线 4 + 伏击 9"P2 归因行的数据源);腿 2(合成序判别):baseline = a_e×m_e、attach = 4×m_amb、total = a_e×m_e + 4×m_amb,且判别负断言 total ≠ (a_e + 4×m_amb)×m_e(伏击附加后置加法不进倍乘——默认表值 (4, 2, 3) 下 20 ≠ 32、非平凡性由表读值不等式保证;实现若把伏击分量塞进倍乘即在此暴露;L=4 为游戏态可达值〔cap=4 上界〕——探针不取域外值);腿 3(charge 零兑付——不读池,EC8 的 B2 半):{0, 0, 0}——L>0 下恒零 = "不读伏击池"的可判定形式(蓄力轮授予的伏击层当轮蒸发 = 读怪奖励;池蒸发本身归 B3 decay、out-of-scope 跨引);腿 4(共享不消耗 + 即时读值,EC17):同 L 连续两次 query 结果逐字相同、两存活单位同 L 各自 query 读值一致(共享、非均分、非先到先得);每次 query 前后 query_alive/query_beat 投影零变化(纯函数性——B2 无池写面、无内部缓存、无时间维度;"先转化后兑付"次序与每雷恰一次归 B1×①②、out-of-scope) | F2 / Rule 6 / EC8 / EC17 | +| AC-8 | 默认根、c_r = cap 表读值(全腿纯函数断言——编排期预截流调用与每雷恰一次授予归 B1×②,out-of-scope) | 腿 1 `ambush_grant_plan(mines_r = c_r + 2, c_r)`;腿 2 参数化对 mines_r ∈ {0, 1, c_r, c_r+1};腿 3 mines_r ∈ [5, 8];腿 4 `ambush_grant_plan(m, 0)` | 腿 1(守恒主式):{granted = c_r, overflow = 2}、恒等 granted + overflow = mines_r(零静默蒸发——P2 会计面;"溢出未生效"明细行呈现归 B1×⑥/B5);腿 2(预截流位):各腿 granted = min(mines_r, c_r)、overflow = mines_r − granted 恒成立(cap 判定在授予前);腿 3(灾难轮,EC7):默认 c_r=4 → granted = 4、overflow = mines_r − 4(cap 反灾难护栏绑定域);腿 4(cap=0 免疫,EC6):granted = 0、overflow = m(全溢出——免疫行全部可见、全部无效的会计半;cap=0 为域内合法行值的校验半归 AC-12) | F3 / EC6 / EC7 | +| AC-9 | 夹具根 enemy_multi_unit(u_c_f = 3) | 腿 1 以 advance 序构造混合拍(k 个单位处 attack/heavy 拍、其余 charge)并读暴露;腿 2 记号恒等式断言(AP_r = ActionEconomyTable.ap_per_round 表读值) | 腿 1(k 确定性可算):每结算敌方伏击暴露 = k × L × m_amb,k 经只读观测量导出(query_alive(enemy) ∩ query_beat ∈ {attack, heavy} 计数——确定性可算、无隐藏状态,B2 对 B1 条款 3 的供给面);全体 charge 轮 → k = 0、暴露 = 0(charge 轮零兑付——EC8 汇总);腿 2(结构上界记号):单结算 side 级结构上界 = u_c_f × min(AP_r, c_r) × m_amb(全参表读值/夹具登记值的记号恒等式断言——满配语境 3×min(8,4)×3=36 的记号化、默认行值 unit_count=1 下 = 12、非定值断言;min 项 = cap > AP 界时 cap 腿松弛);跨结算累计 = 轨迹量非表值——B2 投影字段无任何累计观测量(负空间,AC-1 观测量封闭枚举承载);不等式闭合对账(输入 = A1 actuals p10)归 B1×④、out-of-scope | F4 / EC8 | +| AC-10 | 腿 1 默认根(u_c=1)spawn 完成;腿 2 enemy_multi_unit 根;腿 3 承接腿 2 | 腿 1 `default_target()`;腿 2 `default_target()` 对照 `query_alive(enemy)[0]`;腿 3 杀 [0] 后再查、连续杀至空 | 腿 1(u_c=1 合法退化):default_target() = 该唯一存活单位(顺延与 AOE 分流无消费者、AOE ≡ 单体——合法退化非错误,Rule 7 空转);腿 2(行主序最前):default_target() = query_alive(enemy)[0](生成序最前——顺位快照 = 生成序);腿 3(死亡顺延):default_target() = 原 [1](顺延)、连续杀至空 → null(EC3 全灭腿在目标面的汇合);B1 消费侧单体/AOE 分流归 B1×⑤、顺位可读呈现归 D1×③、均 out-of-scope | Rule 7 / EC3 / EC15 | +| AC-11 | 默认根 `spawn(L_a, D_a, M_h)` 完成(快照字段 = 默认表值;坐标登记协议锚点)、存活与 Dead 单位并存 | 经测试通道换根加载 enemy_reloaded(load_tables(root, collect_only=true) 直调——非 D2 reload() 路径,门语义归 D2 AC-16/B1);随后 `despawn_all()` 后以同坐标重新 spawn;腿 3 对返回的 pattern 投影数组原地改写 | 存活与 Dead 单位的快照五字段 + beat + hp 逐字不变、hero 投影不变(授予时快照——实例不受任何表重载影响,B3 EC9 / A4 EC8 同款);新单位快照 = 漂移后表值(换根只影响后续实例化——"快照非活引用"的判别性后半);腿 3(拷贝判别):容器内单位 pattern 投影不变(快照拷贝非活引用——别名引用在换根加载下同样"逐字不变",本腿补足判别力) | Rule 10 / EC12 | +| AC-12 | EnemyTable schema 类(行级 + 表级校验)class_name 直引实例(不经 load()/preload()) | validate() 作用于越域代表值:unit_count ∈ {0, 4}、hp ∈ {0, 1000}、atk ∈ {−1, 1000}、heavy_multiplier ∈ {0, 17}、pattern 环长 ∈ {0, 9}、pattern 元素 = "guard"(预留值拒绝——MVP 校验域 fail-fast)、pattern 元素 = "nonsense"(未知枚举)、cap ∈ {−1, 17};表级构造同 (level_index, node_depth) 两行相异 enemy_id;域内代表值:unit_count ∈ {1, 3}、hp = 1、atk = 0(纯伏击伤敌种合法——F2 Variables)、atk = 999、heavy_multiplier ∈ {1, 16}、pattern ∈ {["attack"], ["attack","charge","heavy"]}(环长两界)、cap = 0(免疫极端合法——EC6)与 16 | 越域各含对应域错误类别(零静默);表级含每行集恰一敌种错误类别;域内零诊断(防过度约束锁死内容——atk=0 与 cap=0 两个合法极端是本断言承重点) | Rule 3/4 / EC6 | +| AC-13 | HeroTable schema 类直引实例 | validate() 作用于 max_hp ∈ {0, 1000}、atk ∈ {−1, 0, 1000};域内代表值 max_hp ∈ {1, 999}、atk ∈ {1, 999};表级构造两行(域内合法) | 越域各含对应域错误类别(atk=0 拒绝 = 基础攻击恒存在的 schema 承重腿——零基线死锁杜绝);域内零诊断;表级两行 → 单行全局约束错误类别(单行全局——Rule 4 豁免同 ActionEconomyTable 先例;若实现期 ADR/D2 裁决单行校验落 loader 缝,则本腿迁移至可选根 hero_extra_row 的 manifest 腿——两处恰一承载) | Rule 9 | +| AC-14 | ConfigDB 默认根加载完成 | 读 EnemyTable;读 HeroTable | EnemyTable 恰含三敌种行、enemy_id 集合 = {goblin, bugbear, ogre_chief}(定值——Rule 3 MVP 收窄,C3 修订批扩容);每 (level_index, node_depth) 行集恰一敌种(默认根数据通过其自身表级校验的行为断言);全部行 pattern 元素 ⊆ {attack, charge, heavy}(MVP 枚举封闭——结构定值);goblin 行 pattern 不含 "heavy"(heavy_multiplier 无 heavy 拍不消费的表数据前提——非校验项、B3 max_stack 先例);unit_count_max 仅在区间行出现(缺省 = 定值——C3 遭遇随机化字段语义);display_name 多值非空(精确值不作定值断言、本地化归 D6);遭遇分布(何 (L,D) 归何敌种)为 C3 域 knob——不作定值断言(provisional 示例非承诺);HeroTable 恰 1 行、atk ∈ [1,999] 域断言(MVP 值 6 = 原型锚 provisional 不作定值断言;0 = 加载拒绝)、max_hp ∈ [1, 999] 域断言(provisional knob 不写死) | Rule 3/9 | +| AC-15 | 全新 B2 实例 ×2(同 ConfigDB 默认根;每遍全新实例、无跨实例状态) | 同一定长序列驱动两遍(spawn → apply_damage(含一次"轮到前击杀"腿——冻结路径入回放)→ 交替 advance 全体存活敌单位 → query 投影(含生成序与逐单位 beat)→ apply_damage → advance → default_target → query 投影) | 两遍的 query_alive 投影序列(含生成序)、逐单位 beat 轨迹、default_target 返回序列逐项相同——零随机的行为级硬证据(rng_service 流封闭;D2 AC-15 的 B2 层前身,与 AC-3 腿 4"状态 = 驱动序列的确定函数"互证) | Rule 4/11 / F1 | +| AC-16 | ConfigDB 默认根加载完成 | 脚本解析 design/registry/entities.yaml 的 enemy 与 hero 条目(逐字段 YAML 键——机器对账前提,action_economy/status_effect 同构) | ① enemy.fields **十键**(enemy_id / display_name / unit_count / **unit_count_max / armor_type** / hp / atk / heavy_multiplier / pattern / advantage_per_round_cap——C3 修订批 +2)与 ConfigDB EnemyTable schema 字段名逐一相等、各键类型/域/枚举描述与 Schema 定稿相等(缺任一键即失败);② hero.fields 两键(max_hp / atk)同上;③ enemy 条目 MVP 内容注记物种 id 集合 = {goblin, bugbear, ogre_chief}、与表行 id 集合相等(非 knob 结构定值——C3 修订批扩容);④ status_effect.ambush 条目含 B2 量纲定稿注记(Rule 6 兑现 B3 registry OQ2 的接口漂移回写落点——注记存在性 + 键断言;量纲活值 m_amb 不作定值对账——knob 纪律,A4 AC-8 同款豁免);⑤ hero 条目"单行全局 Rule 4 豁免"注记与 enemy 条目"每行集恰一敌种校验"注记存在性断言;生效前提 = 本文定稿 Phase 5b/5d 回填完成(不写"若存在"软口径——条件化使一致性检查落空,B3 AC-18 同款) | Rule 6 / registry | +| AC-17 | B2 文件集(单位容器 / 两表 schema / 冒烟脚本 / 夹具规格)纳入 D2 AC-9 模式项目级扫描域 | 静态扫描 | randomize() / RandomNumberGenerator / ConfigDB 外 data 表加载 / extends Control / .tscn preload——零命中(schema 与容器的 class_name 直引实例化豁免——AC-12/13 前提,A4 AC-7 同款);另扫 B2 特有两项:事件订阅 API 零命中(Rule 11 零主动事件订阅——A2 事件面 referenced_by 不含 B2 的代码侧对应)与 B3 API 符号零引用(相邻零运行时依赖——import/call 面为零);D2 AC-14 人工三要素字面量扫描(引用不另设硬 THEN——F2 算式 a×m + 3L 类字面量随实现形态误报率高,D2 已定性 warning 级 + 人工复核;其"零玩法数值字面量"的行为级硬证据 = AC-1/AC-7 全表读值推导 + AC-16 registry 对账);零随机的行为级硬证据 = AC-15 | Rule 11 / D2 AC-9/10/14 | + +### 覆盖矩阵(qa-lead 交付,验收评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules(11/11) | 1→AC-1/AC-6/AC-2(clamp·溢杀·死亡时机→OOS B1×③)· 2→AC-1/AC-5/AC-9(side-wide 兼容与 target_ref 扩展位→B3 接口漂移义务随批 B1/D1、非冒烟域)· 3→AC-12/AC-14/AC-13 · 4→AC-3/AC-4/AC-5/AC-15(预留值拒绝→AC-12+manifest pattern_reserved)· 5→AC-8(每雷恰一次→OOS B1×②;溢出明细行→OOS B1×⑥+B5)· 6→AC-7/AC-16④(量纲锚验收→B4/B1,排除声明)· 7→AC-10(消费分流→OOS B1×⑤;顺位可读→D1×③)· 8→映射键契约(enemy_id)+AC-3/AC-15(行为环可学性承载)+OOS D1×①②、"不显示数值"负空间=API 封闭 · 9→AC-1/AC-2/AC-13/AC-14 · 10→AC-1/AC-11(D2 R1 解析致命→OOS D2 通道;难度修饰→C3 provisional)· 11→AC-15/AC-17(调用权集中→OOS B1×①) | +| Formulas | F1→AC-3/AC-4/AC-5 · F2→AC-7 · F3→AC-8 · F4→AC-9(闭合对账→OOS B1×④) | +| EC(17 条) | EC1→AC-4 腿1/腿2 · EC2→AC-5 腿2 · EC3→AC-6 腿1+AC-10 腿3 · EC4→AC-6 腿2 · EC5→冒烟 API 封闭(负空间——无 summon API 不可 G-W-T,B3 Rule 8 先例)+AC-5 腿3+AC-17 · EC6→AC-8 腿4+AC-12(cap=0 合法腿)· EC7→AC-8 腿3 · EC8→AC-7 腿3+AC-9 腿1(池蒸发腿→B3 AC 既有覆盖,OOS 跨引)· EC9→AC-4 腿1 · EC10→AC-3 腿3 · EC11→AC-3 腿4(跨图结构裁决)+AC-2/AC-6(相位重复守卫,B3 EC11 同款)· EC12→AC-11 · EC13→OOS(D2 R1 通道,D2 AC-2 同类别夹具,manifest 豁免声明)· EC14→OOS(D2 EC14 通道,同前)· EC15→AC-10 腿1 · EC16→AC-2 · EC17→AC-7 腿4(读值即时性/纯函数半)+OOS B1×①②(次序半) | +| 无洞声明 | 17 条 EC 中 EC5/EC13/EC14 三处非 G-W-T 形态,分别以 API 封闭负空间声明与 D2 通道豁免承载(B3 Rule 8 负空间 + 致命收窄豁免先例同款);其余全部映射到 AC 或显式 OOS(deferral 生效条件 = B1×6/D1×4 已登记缺项即 blocking) | + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | query_attack / ambush_grant_plan 两 face 归属——B2 供给面 vs B1 编排消费(F2"供给建议"与 F3"预截流归 B1"边界注记的张力落点);裁决归 B1 则 AC-7/8/9 对应腿迁移至 B1 冒烟(断言口径不变)、回写 AC 前言 | 实现期 ADR 定名时同步裁决 | Technical Setup / 实现期 | +| 2 | 多敌语义默认零消费者复验——默认 unit_count=1 下多敌路径仅测试根可达(死码风险);C1 落地 unit_count>1 真实遭遇后复验 AC-5/9/10 夹具根是否升级为默认根内容;C1 须真实消费 unit_count>1(Boss/精英战最廉价的构成表达),且 Boss 表达不能只靠 unit_count——同种同拍 ×3 = 伤害乘法、非 Boss 幻想 | C1 遭遇分布定稿 | C1 GDD | +| 3 | 条款 3 左臂载体——基础攻击恒存在后"载体悬空、内容态不可通关"事实已闭环;残余 = 左臂当量换算口径(锚参照回迁 attack_up 当量——B4 F4 / registry anchor_warning)随 B1 对照表对账 | B1 | B1 GDD 强制检查点 | +| 4 | 原型参考值溯源——敌 18HP/4ATK、伏击 +3、hero max_hp 均 provisional 且出处待查(D2 Tuning Knobs 既有登记义务);回填定值前补原型 REPORT 溯源,查无出处则按 C3 调参所有权重定 | 本 GDD 回填定稿时 | EnemyTable/HeroTable 定值回填 | +| 5 | HeroTable 单行校验落点——schema validate() vs loader 缝两处恰一承载(AC-13 腿3 / 可选根 hero_extra_row manifest 行) | 实现期 ADR/D2 | Technical Setup | +| 6 | 标错惩罚候选与伏击清单联动——候选"敌方伏击+1"若实测采纳:事件源 = flag_judged{hit=false}(旗落非雷格)、不在 Rule 5 cell_revealed{kind=mine} 转化路径内;B3 需新增行且 source_class 需新枚举值(如 flag_miss)——source_class 枚举属 B3 结构冻结项、须走 B3 GDD 修订(接口漂移)非"加行";B2 侧伏击清单联动修订 + F3 重验触发(非 mine_hit 源)强制重跑;若错标为 mine_hit:P2 归因明细行来源失实(标错被记成踩雷) | A2 OQ1 实测会话 | A2 /design-review 核对在位 | +| 7 | 精英/Boss 遭遇编码位预登记——难度 = f(关卡序号, 节点深度, 精英/Boss 修饰);A1 BoardGenTable 已预留 elite_/boss_ 命名空间与改键退路;本表键冻结 (level_index, node_depth, enemy_id) 且"每行集恰一敌种"入结构冻结——若 C1 定稿分支地图使同深度并存普通与精英节点,精英编码无键位(同深度第二敌种行触发行集校验致命);届时改键(如追加 encounter_class 维度)走 D2 Rule 4 接口漂移修订,schema/AC-12/夹具/registry 连锁回写(A1 OQ6 镜像) | C1/C3 | C1 GDD 定稿强制核对 | +| 8 | goblin 单拍环体验证伪——goblin 环长 1、无前摇无空窗,玩家可能将其感受为"自动平砍的血条"(恰为体验锚点反向对照);MVP 实际读怪内容仅剩 bugbear 一环;预注册证伪项:playtest 采集"goblin 是否被读"信号;证伪则两拍环备用案(如 [attack, charge])由 C3 内容所有权处置、本 GDD 结构不变 | C3/playtest | 垂直切片 playtest | +| 9 | 零伤害死局组合与跨表量级护栏——hero 侧恒零伤害分支已被 HeroTable.atk 域下界([1,999]、0 加载拒绝)结构杜绝;残余 = 行 atk=0 ∧ cap=0 → 无威胁敌(内容调参禁区、非死局);atk×mult 上界与 max_hp 域无跨表量级护栏——遭遇行配置期跨字段校验落点裁决(ActionEconomyTable 跨字段校验先例),详见 Tuning Knobs Knob 交互注记 | 实现期 ADR/D2 | Technical Setup | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;规则/枚举/边界情况/义务清单/覆盖矩阵/AC 转表格;行为环转字符画;公式、Variables 与验算数据全保留;语义与 2026-09-09 定稿及 2026-09-11 基础攻击裁决对齐,零规则变更 | +| 基础攻击裁决批 | 2026-09-11 | HeroTable.atk = 基础攻击恒存在:域 [1,999]、0 加载期校验拒绝(零基线死锁自 schema 层杜绝)、MVP = 6(原型锚 provisional 归 C3);主角每结算战斗段先手攻击;B4 道具/增益转为规划面伤害(非唯一伤害来源);原"atk MVP=0 机制开关、胜利路径依赖 B4 先行"条款与 OQ3 左臂悬空投影退役;OQ9 恒零死局分支随之收窄 | +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-7 回执,registry 同批):**spawn 增 override 参数 n**(B1 encounter 骰定本战实际数量——区间语义 [unit_count, unit_count_max] 实例化后不增,EC5 记号调和)· **Rule 10 DifficultyTable 接口已定**(registry `difficulty_table`——施加点 = 行值快照前套乘,MVP 空表零修饰)· **EnemyTable schema +2 字段**(unit_count_max / armor_type——护甲克制批,克制矩阵归 SettlementTable)· **物种集扩容三敌种 {goblin, bugbear, ogre_chief}**(AC-14/AC-16① 十键/AC-16③/结构定值断言随批;DND 更名批对齐)· 默认根遭遇分布 = C3 行值总表 v3(provisional) | +| Rev 1(full 评审批) | 2026-09-09 | lean 定稿同日 full 评审(六专员 + CD 终裁)8 项 blocking 同日核销后用户拍板收口(A3/A4/A1/B3 同日核销先例、免独立 re-review):① 胜利路径声明 ② AC-7 恒假判别前提 + SKIP ③ 多敌夹具根改 bugbear 行 ④ OQ6 事件源类更正 + F3 重验触发补支 ⑤ 精英/Boss 编码位预登记 OQ7 ⑥ Rule 8 预告时序 ⑦ 资产下限补受击/死亡/顺位 ⑧ 主角推进域排除;同批定稿四项口径:cap 默认 4(旧默认 8 使溢出协议不可达)、死亡即不动手、条款 3 单结算自然边界、C 节三处回写;F2 判别改域内值、F4 结构上界 min 收紧、方向性预检裕度改 2.3×——明细见 design/gdd/reviews/b2-enemy-system-review-log.md | +| v1(lean 定稿) | 2026-09-09 | 一次会话定稿:十一核心规则 + F1–F4 + 17 AC + 义务台账(B1×6 / D1×4);九项结构决策用户拍板(多敌 / HeroTable / 视觉推测型 / Peglin 行为环 / 每行集恰一敌种 / side-wide / 固定顺位 / unit_count 字段 / cap=4);lean 门禁:systems-designer 起草 Formulas、qa-lead 起草/验证 AC | diff --git a/design/gdd/legacy-2026-09-14-v1/b3-status-effect.md b/design/gdd/legacy-2026-09-14-v1/b3-status-effect.md new file mode 100644 index 00000000..25539911 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/b3-status-effect.md @@ -0,0 +1,533 @@ +# B3 增益/状态系统(Status Effect System) + +> **状态**:Approved · lean 格式 +> **版本**:v1.1-lean · 2026-09-13 · 作者:user + agents(systems-designer〔Formulas〕· qa-lead〔AC〕;三结构决策〔单一计数/按行叠层/四行定稿权收编〕为用户拍板) +> **承载支柱**:P2 主(可归因兑现的机制落点)· P3(敌方增益/debuff 的兑付面)· P1(时限透明的规划素材)· P4(间接) +> **Layer**: 1 Foundation · **Milestone**: MVP · **设计顺序**: 第 4 位 +> **一句话**:B3 是战斗域统一状态容器——三向效果同构实例的时限/叠层/衰减机制唯一 owner,兑付接口供 B1 结算逐行消费。 +> **复核注记**:lean 门禁下 systems-designer / qa-lead 已委托起草(Formulas / AC);creative-director 与 art-director 未经咨询——生产前人工复核体验锚点情绪定位与视觉数据面规格同 art-bible 的一致性。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 战斗域统一状态容器:不在当场一次性、而要在若干次结算里持续起作用的战斗效果(安全格增益 / 踩雷敌方增益 / 标中旗削弱 / 清图奖励 buff)作为同构状态实例的唯一存放与生命周期 owner——统一容器让"效果"成为一门单一语言:四源统一当量的对照载体(A3 条款 1)、B1 结算的单一对接面、可回放步骤流的状态侧前提 | +| 拥有 | 机制:时限/叠层/衰减语义、状态实例生命周期(战斗域)· 兑付接口五 API 与 P2 归因字段 · StatusEffectTable 回填 owner · MVP 四行初值定稿权(Rule 10 收编——锚验收权留 B4/B2/B1);机制与内容分离是结构原则——B3 只拥有机制,效果内容的经济归属归各锚 owner | +| 不拥有 | 效果内容经济归属(安全格 buff 与标中旗 debuff 档位锚验收归 B4、敌方增益上限归 B2)· 效果语义到结算公式的映射与兑付作用顺序(B1 SettlementTable)· 呈现(D1/B5)· 教学(D5)· 渲染面与事件外发(零) | +| 上游输入 | D2 StatusEffectTable(hard——数据通道,本 GDD 回填 owner);无其他玩法上游(Foundation 层) | +| 下游输出 | 五 API(B1 结算编排消费)· query_active 投影(D1 HUD 数据源)· 实例六字段 + display_name + 授予时快照(B1 结算明细 / B5 呈现数据源) | +| 硬约束 | 零随机(无 RNG 流消费)· 零 UI 依赖 · 零信号外发(纯被动容器)· 全 API 布尔/计数返回、无 await——可回放步骤流的状态侧前提 | + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P2 可归因的兑现感 | "我攒下的东西正在替我打架" | 每个状态实例携带来源标注(安全格/标中旗/清图/雷),结算明细逐行生效——P2 准话测试"这个结果是我哪步扫雷带来的?"在 B3 字段里就有答案;"规划的延迟兑现"从口号变成可见事实的机制落点 | +| P3 雷的两面 | "雷是赌桌不是陷阱" | 踩雷的敌方增益行与标中旗的削弱行在结算明细里使用同一呈现语言,玩家由此学会雷的两面;旗标对的即时回报部分补偿 chord 缺席的兑现节拍缺失(CD 创意风险 #3 的关联缓解,正式高光设计归 B1/B4) | +| P1 规划素材 | "还剩几次结算、何时到期" | 增益携带明确的剩余结算次数与到期时机,玩家才能把它纳入挖哪格的推理——时限语义的透明度是规划感的前提;满层溢出行可见("满层时别再拿同款") | + +反向护栏:状态效果绝不能是无感的暗账——玩家说不出一行结算"从哪来",来源标注即失职(违反 P2);任何让 buff 时限/剩余次数不可读的设计默认砍掉。参考感受:杀戮尖塔的增益图标(时机与层数一目了然,是决策素材而非装饰)、Peglin 神器在关键时刻叠加生效的"攒出来的强大"。 + +## 全局流程 + +### 效果生命周期(授予 → 存续 → 兑付/衰减 → 出口) + +``` +B1 结算编排(唯一授予入口 apply_effect——A2/B4 与 B3 零直接接口) + │ 事实来源:A2 开格/标中/开雷事件 → B1 收集 round facts → 转化授予(挖掘期不直接写容器) + ▼ +授予(编排期入容器,无 pending 态——首个结算就是它的第一次兑付) + │ merge(effect_id) 三分支:independent 新建实例 / stack L+1(L=M 拒绝) / refresh R←D + │ 实例载荷 = 六归因字段 + display_name + 授予时数值快照 (magnitude, unit) + ▼ +Active(存续——跨轮次、跨扫雷图延续;换图/刷新不清空,旗标记随图消亡不影响实例) + │ 每轮结算:B1 query_active(side) 只读兑付(作用顺序归 B1 SettlementTable) + │ → decay_round() 恰一次:全实例 R−1,R=0 销毁(先兑付后衰减) + ▼ +出口 ─┬ R=0 → Expired:实例销毁(明细流水已由 B1 记录,B3 不留历史) + ├ notify_battle_end → Cleared:全实例销毁、容器回空 → BattleClosed(不跨战斗) + └ 拒绝路径:域闭 / 未知 effect_id / 满层 → false + 容器零写(溢出行归 B1 结算明细) +``` + +### 出口分发 + +| 出口 | 触发 | 动作/去向 | 约束 | +|---|---|---|---| +| 受理入容器 | apply_effect 受理(新实例/叠层/刷新) | 即入 Active——无 pending 态,首个结算是第一次兑付 | 域闭 / 未知 id / 满层 → false 拒绝(见边界情况) | +| 到期移除 | decay_round 后 R=0 | 实例销毁 → Expired | 明细流水已由 B1 记录,B3 不留历史 | +| 战斗清空 | notify_battle_end() | 全实例销毁、容器回空 → BattleClosed | 不跨战斗;除本 API 外无任何清空入口 | +| 满层拒绝 | stack 行 L=M 再授予 | 返回 false、容器逐字不变;"溢出未生效"行归 B1 结算明细(含来源) | 拒绝原子性——无半写、无溢出落层 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:所有持续效果共用一个容器、一套机制 + +**人话**:战斗里不在当场一次性生效、要管上几回结算的效果——主角的增益、敌人的增益、敌人受到的削弱、清图奖励——全都作为同一种"状态实例"存进同一个容器,用同一套计时、叠层、衰减的规矩管理,没有哪类效果走特殊通道。容器自身有四条硬规矩: + +| 规矩 | 含义 | +|---|---| +| 寿命 = 一场战斗 | 战斗开始时容器为空,战斗结束全部清空——不跨到下一场战斗 | +| 跨轮、跨图延续 | 同一场战斗里跨轮次、跨扫雷图都在;换图/刷新不清空(旗标记随旧图消亡归棋盘系统 A2 管,状态实例不随图走) | +| 全程零随机 | 容器不掷任何骰子:授予、合并、衰减、列举顺序全部确定 | +| 敌方侧共享 | MVP 敌人为 1–3 个同种敌单位(敌情系统 B2 定):对敌状态一律对全体存活敌人生效,层数全体共享一份 | + +**举证**:无计算。"不跨战斗"为 game-concept 定稿口径;多敌共享读法按 registry single_enemy_assumption 登记,现接口零变更兼容。 + +#### 规则 2:状态的寿命按"还剩几次结算"倒数 + +**人话**:每个状态实例都自带一个倒数计数——剩余结算次数。它不承诺"活几轮",而是数"经历过几次结算":每轮结算先让状态起作用,随后计数减 1,减到 0 状态即被移除。因此一次性的状态(标中旗削弱、踩雷伏击这类,初始计数 = 1)恰好在授予它的那轮结算里生效一次、轮末消失,MVP 四行内容全部是一次性;将来若有跨轮持续的增益,寿命由表行的初始值表达——给几就活几次结算。停手不挖的轮、弃图刷新的轮也照常算一次结算、计数照减——不存在"不结算就不掉次数"的免费延时。 + +**举证**:计数量纲 = remaining_settlements(剩余结算次数),初始值 = 表行 duration_settlements(一次性 = 1);衰减的精确机制(每轮恰 −1、归 0 即销毁)与"先兑付后衰减"时序契约见「数值结论与推导」F3;时限语义逐要点见下方实现参照表。 + +### 规则 2 的实现参照——单一结算次数量纲(时限语义) + +| 要点 | 内容 | +|---|---| +| 计数量纲 | remaining_settlements(剩余结算次数)——全部状态唯一时限单位 | +| 初始值 | duration_settlements = 授予时初始剩余结算次数(实例持有快照、不受表重载影响——EC9) | +| 衰减 | 每轮结算兑付后统一 −1(先兑付后衰减——B1 兑付完才调 decay_round);归 0 即移除 | +| 一次性 | duration=1 → 授予当轮编排期入容器 → 同轮兑付恰一次 → 轮末移除(MVP 四行全部 D=1——当次结算语义) | + +#### 规则 3:状态只在结算时发放,每笔都记着从哪来 + +**人话**:挖掘期的挖格、插旗、踩雷不会直接改状态容器:棋盘(A2)只上报"发生了什么"的事实,战斗结算编排(B1)在每轮结算时统一把事实兑换成状态、发给容器。这是容器唯一的进货口,棋盘与奖励背包(B4)都没有直通容器的通道。每笔状态随身携带归因信息:哪类来源(安全格奖励/标中旗/踩雷/清图奖励四选一)、从哪个格子来、发生在第几轮——结算明细能逐行回答"这效果是我哪步扫雷带来的"。状态一经发入立即生效(没有"待生效"中间态):同一场结算就是它的第一次兑付。 + +**举证**:无计算。授予动作 = apply_effect(规则 6 五接口之一);四类来源的授予路径与目标侧逐源对照见下方实现参照表;来源取值封闭枚举见「枚举」节 source_class 表。 + +### 规则 3 的实现参照——三向效果映射(授予路径) + +| 事实来源 | source_class | 授予路径 | 目标侧 | MVP 行 | +|---|---|---|---|---| +| 安全格奖励 | safe_cell | A2 开格事实 → B1 转化 apply_effect | hero | attack_up / defense_up | +| 踩雷 | mine_hit | A2 开雷事实 → B1 转化 apply_effect | enemy(side-wide) | ambush | +| 标中旗 | flag_hit | A2 标中判定 → B1 转化 apply_effect;兑付资格(雷格实例键)判定在 A3/A2 域——资格判定不过 B3,B3 无从重复兑付同雷(每次 flag_hit 授予由 B1 保证资格合法) | enemy(side-wide) | weaken | +| 清图奖励 | clear_reward | B4/B1 清图槽含 buff 时授予(B4 期定稿) | 随行 | B4 差异化插槽(OQ4) | + +#### 规则 4:同名状态重复拿到,怎么合并由数据表说了算 + +**人话**:同一种状态还没消失时又拿到一份怎么办?答案不在代码里,而在数据表每行的"叠层策略"字段,共三种:叠加(层数 +1,寿命不动——只有第一次授予定时钟)、刷新(寿命重新计满、层数不变)、并存(各算各的一份、互不影响)。合并在授予那一刻由容器内部完成;多个状态在结算里按什么先后起作用,是结算编排的职责——两件事互不干扰。叠加的作用是纯加法:几层就是几倍的行值(单一线性换算)。MVP 四行内容全部用叠加;刷新与并存的语义已定义、实现已承载,未来内容行按需启用。 + +**举证**:三分支的逐分支语义(含满层/溢出行为与 MVP 消费)见下方实现参照表;合并执行的伪代码见「数值结论与推导」F1;"有效值 = 层数 × 行值"的加算合成见 F2。 + +### 规则 4 的实现参照——按行叠层三分支(stack_policy——授予时合并) + +| stack_policy | 重复授予行为 | 满层/溢出 | MVP 消费 | +|---|---|---|---| +| stack | 层数 +1(R 不动——只有初始授予定钟) | L=M → 拒绝 false + 容器零写,"溢出未生效"行归 B1 | 四行全用(attack_up/defense_up M=2、ambush/weaken M=16) | +| refresh | 剩余次数重置为实例授予时快照 D、层数不变(含 R 已 = D 的空刷新——恒返回 true,无满层/溢出语义、不消费 max_stack) | 无 | 语义已定义、实现承载——MVP 无内容行(零消费者) | +| independent | 并存独立实例(append 入列) | 无满层语义——实例数上界 = 受理授予数(有界确定) | 同上 | + +#### 规则 5:叠满了再给同款不生效,但会明说"这层浪费了" + +**人话**:一种叠加型状态已叠到上限(层数 = 上限值)时,再授予同类不生效:容器拒绝、内容一个字不变。但这份奖励不会被无声吞掉——结算明细会多一行"溢出未生效",连来源一起写明;玩家看得到"这层白拿了",下次就懂得满层时别再拿同款——奖励的浪费是诚实的、可规划的。无论这份重复来自玩家直接踩出的格子、结算转化、还是连锁展开自动开出的奖励格,都走同一个授予入口,满层时同样产生溢出行、同样留痕。 + +**举证**:无计算(判定 = 一次"层数 == 上限"比较)。拒绝原子性(false + 容器逐字不变、无半写无溢出落层)见 F1 推导极端行为段;"溢出未生效"行进结算明细协议由 B1 义务⑧承载、呈现归 B5/D1。 + +#### 规则 6:结算系统只用五个固定接口读写容器 + +**人话**:状态容器是纯被动的仓库,自己不主动做任何事;战斗结算编排(B1)要用它,只有五个固定接口: + +| 接口 | 干什么 | 返回 | +|---|---|---| +| 告知战斗开始 | 打开容器、进入可用状态 | 是/否 | +| 发一笔状态 | 唯一进货口:受理即新建实例/叠层/刷新 | 是/否 | +| 查询生效状态 | 某一方当前生效清单,按发放先后排列且顺序永远稳定 | 只读清单 | +| 轮末衰减 | 全部实例寿命 −1、归 0 即销毁 | 本轮移除数 | +| 告知战斗结束 | 全实例清空、容器回空 | 是/否 | + +**举证**:五接口 = notify_battle_start() / apply_effect(effect_id, source) → bool / query_active(side) → Array / decay_round() → int / notify_battle_end() → bool(签名见「验收标准」测试口径)。 + +- 查询返回 = 六归因字段 + display_name + 授予时数值快照 (magnitude, unit) 的只读投影(qa-lead 载荷口径)。 +- 结算作用顺序(哪个效果先乘)归 B1 SettlementTable,B3 只保证枚举确定性;最后一轮(敌人全灭)是否 decay 归 B1 判定次序域——A3 EC8 同款〔末轮 decay 判定次序域〕。 +- 全部 API 布尔/计数返回、无 await、无表现面——可回放步骤流的状态侧前提。 + +#### 规则 7:容器只管"有多少量、还剩几次",不管这量怎么用 + +**人话**:容器记录每个状态的量(幅度数值 + 量纲标签:百分比还是固定值)和它还剩几次结算,仅此而已。"增伤到底加在哪个算式上、先乘后乘、地板怎么收"这类"效果如何兑现"的问题,一律由结算编排的顺序映射表按状态 id 作答。一句话:容器知道"现在还有 +50、还剩 1 次结算",但不知道这 +50 会变成主角攻击里的什么。 + +**举证**:无计算。语义映射的承载面 = B1 SettlementTable(effect_id 为键);B3 只承载 magnitude/unit 两个通用数值字段。 + +#### 规则 8:已生效的状态玩家动不了——不能驱、不能延、不能转 + +**人话**:状态一旦生效,玩家没有任何手段把它摘掉、往后延、或挪给别人——容器没有提供这类接口。"不能做"和"能做"同样是明确的设计决定:省掉一整类界面与规则复杂度。将来若要加驱散/延时/转移,属于新增接口的修订,不是调参数。 + +**举证**:无计算。负空间规则——验收形态 = API 面封闭枚举 + 接口漂移修订门(见「验收标准」节测试口径 out-of-scope 行)。 + +#### 规则 9:所有数值写在数据表里,代码零写死 + +**人话**:容器本身不认识任何具体数值:哪个状态加多少、活几次、最多叠几层,全部写在状态表里(表结构由本文档定义、数据层 D2 承载),程序运行时统一从配置库 ConfigDB 读表。代码里不出现任何玩法数值字面量——调平衡只改表、不改代码。 + +**举证**:无计算。表 = StatusEffectTable(本 GDD 为回填 owner,schema 逐字段见「Tuning Knobs」节);依据 = D2 Rule 1〔数值字面量禁令〕。 + +#### 规则 10:四个 MVP 状态的初值由本文档定稿,验收权留在对口系统 + +**人话**:MVP 上线的四行内容(增伤、减伤、伏击、削弱)的初始数值由本文档定稿,值域快照见「枚举」节内容行表。但"值合不合适"的验收权留在对口系统(奖励经济 B4、敌情 B2、结算 B1):削弱档位按"必须严过最低档增益"的当量锚验收、伏击按敌方惩罚上限验收、对账由结算闭合。要改初值,走接口漂移规则回写登记库(registry),不是随手调参——初值定稿权归本文档、锚验收权仍归上述三方,这是对"档位归奖励表设计"的显式收编。 + +**举证**:无计算(定稿权条款非公式)。依据 = A3 F1 条款 2〔debuff 严格 > 最低档 buff 当量,参照对象 = attack_up 行当量〕· 条款 3〔B2 惩罚上限〕· A3 OQ5 收编〔registry ownership_note〕;A3 GDD 正文不改,以本条 + registry ownership_note 为准。 + +## 枚举 + +### source_class(封闭枚举——P2 归因字段) + +| 值 | 产出点 | 语义 | +|---|---|---| +| safe_cell | 安全格奖励授予 | 主角增益来源 | +| flag_hit | 标中旗授予 | 怪物削弱来源 | +| mine_hit | 踩雷授予 | 敌方增益来源 | +| clear_reward | 清图奖励授予 | 清图槽 buff 来源(B4 期定稿) | + +新增值 = 接口漂移。B3 不校验 source_class 与行的对应(dumb 账本——EC6 同族);内容行表"来源"列为内容档案(provenance),非 schema 字段、非运行时约束。 + +### target_side / unit / stack_policy(schema 枚举域) + +| 字段 | 值域 | 语义 | +|---|---|---| +| target_side | hero / enemy | 作用侧;enemy 侧 side-wide = 全体存活敌单位(层数 = side 共享池) | +| unit | percent / flat | percent = 百分比修正(百分点线性叠加);flat = 固定值(伏击类——量纲已定稿:每层伏击为每次攻击类行为附加 m_amb 点主角 HP 伤害、后置加法不进倍乘、charge 拍零兑付,B2 GDD 定稿) | +| stack_policy | stack / refresh / independent | 同 id 重复授予的合并策略(见核心规则子表) | + +### StatusEffectTable MVP 内容行(四行值域快照——值全部 provisional,验收锚见 Rule 10) + +| effect_id | 名称 | side | magnitude(现行值) | duration | stack/max | 来源 | provisional 依据 | +|---|---|---|---|---|---|---|---| +| attack_up | 增伤 | hero | +50 percent | 1(当次结算) | stack / 2 | safe_cell | 原型临时值 1.5× 换算(D2 knobs 占位);消费面 = 主角战斗段先手攻击(HeroTable.atk 基础攻击恒存在) | +| defense_up | 减伤 | hero | −30 percent | 1(当次结算) | stack / 2 | safe_cell | 减伤墙批 C3 投值(−50→−30,叠满两层 −60 帽——defense_scan 墙轮 39%/真人 12 轮零承伤定量坐实后回调) | +| ambush | 伏击(敌方增益) | enemy | +3 flat | 1 | stack / 16 | mine_hit | 原型临时值 +3;量纲已定稿(B2 GDD——registry ambush_unit_definition);条款 3 锚验收归 B2/B1 | +| weaken | 削弱(标中旗 debuff) | enemy | −65 percent(值域 [60, 85]) | 1 | stack / 16 | flag_hit | F0 裁决值——回执以 B1 GDD F0 为准(registry anchor_warning);须过条款 2 锚(严格 > 最低档 buff 当量,B1 对照验收) | + +## 数值结论与推导 + +> 章节纪律:结论先行(加粗一行),下接「推导」小标题给公式代码块、Variables 表与验算/示例。systems-designer 代理起草(lean 门禁):只含机制公式,平衡归属见显式排除声明。 + +### F0 weaken 锚裁决回执(m_w 值域与归一口径) + +**结论**:m_w 值域 [60, 85]、默认 −65;归一口径 = 每结算瞬时当量(每 AP 总暴露分支被 percent schema 域禁止,排除);地板交叉层数 = ⌈100/m_w⌉ = 2(值域内恒 2);B3 接口零修订、值变更走 registry 回写——**回执以 B1 GDD F0 为准**(registry status_effect.anchor_warning F0 裁决回执)。 + +#### 推导 + +| 分支 | schema 可行性 | 后果 | 裁决 | +|---|---|---|---| +| 每结算瞬时当量 | 可行(m_w ≥ 51 即过锚——percent 域内) | F2 越域界随移(AP 界 8×m_w / 表界 16×m_w);地板交叉层数 = ⌈100/m_w⌉ = 2;上调的连带成本(地板提前到达、边际层贬值)入裁决 | 采纳 | +| 每 AP 总暴露 | 不可行——需 m_w > 100,被 percent ∈ [−100, 100] schema 域禁止 | 现实解 = B4 重定最低档参照(非上调)——未采纳 | 排除 | + +- 同分母性:瞬时口径下 weaken 与 hero 侧 buff 作用于同一伤害流,"严格大于"在百分点读法与 HP 读法下等价,锚判定无量纲歧义;总暴露口径引入时长权重,把 duration knob 泄漏进锚判定。 +- 机制同构:B3 本就按结算逐次兑付,瞬时口径是容器原生口径;总暴露需跨结算累计账本,与已锁的"单结算自然边界"(dur=1 每结算清零无存量)冲突。 +- 值域依据(@D_ref=12):下界 60 = 条款 2 两腿可行交集(腿一 m_w ≥ 51——65 > 50 严格大于 attack_up 当量〔参照对象,registry 回迁口径〕;腿二随 E_dig 口径修正放宽——m_w ≥ 38,以 B1 GDD F0 重算回执为准);上界 85 = 超出仅在连锁贫瘠板增加裕度、地板交叉层数恒 2;schema 顶 100 保留为绝对上界。 +- 红警关闭:原 −25 值在两个独立线性代理下同向预检不过(条款 2 锚差约 4×,且"单一线性换算"堵死分源改率、"无单档上限"堵死 max_stack 豁免)——F0 修订后锚两腿通过(瞬时当量 7.8 > 6.0 严格大于 ✓ / 7.8 ≥ E_dig 4.5 ✓——E_dig 读数随重算批口径更新);终值共裁与 registry 终值确认联动 B1 OQ2(见开放问题 #1)。 + +### F1 同 id 叠层合并(same_id_merge,授予时执行) + +**结论**:授予时按行 stack_policy 三分支合并——stack 加层不触碰 R(只有初始授予定钟)、refresh 重置 R 取实例授予时快照 D、independent 并存新实例;满层拒绝原子(false + 容器逐字不变)。 + +#### 推导 + +``` +merge(effect_id) —— 前置拒绝(域闭 / 未知 effect_id)沿用 States 节 API 口径,不入分支。 +行 = StatusEffectTable[effect_id],按 行.stack_policy 三分支: + + independent → 恒新建实例 {L=1, R=D},append 入列(同 id 并存多实例) + + stack → 无存活实例 :新建 {L=1, R=D} + 有存活实例 ∧ L < M :L ← L+1(R 不动,见注) + 有存活实例 ∧ L = M :返回 false(满层拒绝;容器零写入,溢出行归 B1 结算明细) + + refresh → 无存活实例 :新建 {L=1, R=D} + 有存活实例 :R ← D(L 不变;含 R 已 = D 的空刷新,恒返回 true—— + refresh 无满层/溢出语义,不消费 max_stack) +``` + +注(spec 决议):stack 授予不触碰 R——duration_settlements 只有初始授予定钟;若 stack 也重置 R,则 stack ≡ refresh + 加层,三分支失去区分性。refresh 的重置目标 D 取实例持有的授予时快照(与 EC9 快照语义一致——实例不受表重载影响)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| effect_id | e | String | 表主键 | StatusEffectTable 行键;未知 = 拒绝 | +| stack_policy | P | enum {stack, refresh, independent} | 表列 | 合并分支选择器(Rule 4) | +| max_stack | M | int [1, 16] | 表列 | 仅 stack 分支消费;refresh/independent 不读(未来 refresh 行配 M>1 时行为 = 忽略) | +| duration_settlements | D | int [1, 99] | 表列 | 新建实例初始 R;refresh 的重置目标即此值 | +| layers | L | int | stack 行 [1, M];其余恒 1 | 实例层数(越界 = debug 断言,States 节不变式) | +| remaining | R | int | 存活态 [1, D] | 剩余结算次数;归 0 移除(F3) | + +- **Output Range:** 正常域——stack/refresh 行同 id 存活实例数 ≤ 1(不变式);independent 行实例数 = 受理授予数(有界确定);返回值 bool。极端行为——满层授予恒 false 且容器逐字不变(拒绝原子性:无半写、无溢出落层);同轮批量授予的先到先得次序由调用方保证(B1 编排须按确定次序调用 apply_effect——B3 只保证插入序枚举稳定,B1 验收项义务①)。 +- **Example:** attack_up(stack,D_a=1、M=2):轮 3 同轮授予批次两个安全格事实 → apply_effect×2 → L=2、R=1;同轮第 3 个授予 → L=2=M → **false**(明细"溢出未生效"行);兑付 +100%(F2)→ 轮末 decay R=0 移除。weaken(stack,D_w=1、M=16):轮 3 三旗命中三雷(雷格实例键,三次授予均合法)→ L=3、R=1;兑付 3×m_w(默认 65 → −195%)→ decay 移除;轮 4 一旗命中新雷 → 旧实例已销毁,**新建** {L=1, R=1}——dur=1 行的层只在单轮授予批次内累积,跨轮必为新实例。dur ≥ 2 行(策略探针 D_f=3):跨轮 R 逐轮 −1,refresh 腿见 AC-5。 + +### F2 有效值合成(effective_value_composition,兑付时只读) + +**结论**:有效值 = L × m 加算(unit 量纲标签原样随值输出、不进乘法);合成值不 clamp、可越出 percent 域——地板/clamp 归 B1 SettlementTable。 + +#### 推导 + +``` +effective_value(实例) = L × m (unit 随行声明,量纲标签原样随值输出、不进乘法) + stack 行:L ∈ [1, M];refresh / independent 行:L ≡ 1(每实例恒一倍) + 加算语义:percent 行的 L×m 是百分点线性叠加(+50 × 2 层 = +100 个百分点); + 乘算读法(1.5^L)在 B3 语义域外。 + 表注:行值出处(provenance,如"原型 1.5×")是数值来历标注,非合成语义。 +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| layers | L | int | 同 F1 | 叠层数 | +| magnitude | m | int | percent ∈ [-100,100] ∧ ≠0;flat ≥ 1(单行字段域,D2 校验) | 行值;合成值不继承此域(见 Output Range) | +| unit | u | enum {percent, flat} | 表列 | 量纲标签;percent = 百分比修正、flat = 固定值;消费语义(乘除侧、地板、单位定义)不在 B3(见排除声明) | + +- **Output Range:** 正常域——percent 行有效值 ∈ [1, 100×M],**可越出 [-100, 100],B3 不 clamp**(合成值地板/clamp 归 B1 SettlementTable——B1 验收项义务③);flat 行 effective ≥ 1。极端行为——weaken 合成下界两档并列:**AP 界 = 8×m_w**(默认 65 → −520%;默认经济下 flag_hit 单轮 ≤ ap_per_round=8 次授予、dur=1 层只在单轮累积 → L=8)与**表界 = 16×m_w**(默认 65 → −1040%;仅非 AP 源如 clear_reward 授予、或 ap_per_round 调参后可达 L=16)——两档均越域,均不在本系统内收口;m_w 值域见 F0。跨 effect_id 聚合、percent 与 flat 混算 = B1 SettlementTable 域(Rule 7)。 +- **Example:** attack_up L=2 → +100 percent(兑换成 2.0× 与否的读法归 B1);defense_up L=2 → −60 percent(B1 换算 0.4×——现行值链下不触地板,"归零"读法须 B1 定义);weaken L=8 → 8×m_w(默认 −520 percent);ambush L=3 → **+9 flat**(3×m_amb——B3 不解释单位)。 + +### F3 结算衰减(settlement_decay,轮末执行) + +**结论**:先兑付后衰减——每实例每轮恰 −1、归 0 即销毁(不留 0 值实例)、返回本轮移除数 k;无参数、无豁免实例。 + +#### 推导 + +``` +decay_round() -> k: + k ← 0 + for 实例 ∈ 容器(插入序枚举,零随机): + R ← R − 1 + if R = 0:销毁实例;k ← k + 1 + return k +``` + +时序契约:**先兑付后衰减**——B1 经 query_active(side) 完成本轮兑付后才调用;duration=1 实例的落点由此完整确定 = 授予当轮编排期入容器 → 同轮兑付恰一次 → 轮末 R=0 移除("一次性"语义的全部机制内容)。停手轮/弃图轮同为结算、照常衰减(Rule 2,无免费延时);最后一轮(敌人全灭)是否 decay 归 B1 判定次序域(Rule 6,A3 EC8 同款——battle_end 全清兜底,两种次序产生同终态)。 + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 容器 | — | 实例多重集 | 存活实例全体 | 枚举序 = 插入序(Rule 1 确定性) | +| remaining | R | int | 存活态 [1, D];decay 后 ∈ [0, D−1] | 单调递减;refresh 的 R←D 发生在授予时(F1),不在本式内——两序分离 | +| 返回值 | k | int | [0, 容器规模] | 本轮移除数 | + +- **Output Range:** 正常域——每实例每轮恰 −1,无参数、无豁免实例;k ≤ 调用前容器规模。极端行为——空容器 → k=0;全 dur=1 轮 → k=容器规模(全清);R 恒不因 decay 变负(归 0 即销毁,不留 0 值实例——与不变式 remaining ∈ [0, D] 相容)。 +- **Example:** 轮 3:编排期授予 attack_up(L=1、R=D_a=1)→ 兑付 +50% → decay_round()=1(移除);轮 4 query_active(hero) 已不含 attack_up。dur ≥ 2 行(策略探针 D_f=3):授予后逐轮兑付、R 逐轮 −1、第 3 轮末移除——R 轨迹与 k 全由表读值推导。 + +### 显式排除声明(R4 式) + +本 GDD **无平衡公式**——F1–F3 只承载机制数学(合并/合成/衰减),不含任何"值该多大"的判断;四行 MVP 值全部 provisional(Rule 10)。平衡与语义归属逐一列明: + +| 对象 | 归属 | 依据 | +|---|---|---| +| 效果档位当量对照表(四源统一当量、单一线性换算) | **B1**(出表方兼闭合对账单一签字人) | A3 F1 条款 1 | +| weaken 档位锚(debuff 当量严格 > 最低档安全格 buff 当量,参照对象 = attack_up 行当量;基准 = 连锁放大边际挖掘收益,provisional 1:1) | **B4 表设计 + B1 对照验收** | A3 F1 条款 2 | +| 挖掘风险不等式(P(安全)×buff 当量下界 > P(雷)×敌方增益当量上界;B2 惩罚上限表值) | **B2 供给上限表值,B1 单一闭合对账** | A3 F1 条款 3 | +| 奖励定价(清图奖励及 B4 三硬约束:雾内档位下界 / 锚随雾成本上浮 / 净当量优势带上界) | **B4** | A4 pricing_constraints;A3 F1 条款 4/5 | +| percent 的消费侧换算与地板(乘除侧读法、合成值越域 clamp);flat 的量纲定义 | percent → **B1 SettlementTable**(Rule 7);flat 量纲 → **B2**(已定稿——registry ambush_unit_definition:每层伏击为每次攻击类行为附加 3 点主角 HP 伤害、后置加法) | Rule 7 + F2 Output Range;Schema 说明列 | +| MVP 四行初值定稿权 | **B3**(Rule 10);B4/B2/B1 持锚验收权,修订走接口漂移回写 registry | Rule 10 | + +B3 只对"L×m 线性合成 + R 计数"负责:合成值多大算强、多弱算废、floor 在哪、+3 打谁——均不在本 GDD 语义域。 + +## 状态 + +容器为**纯逻辑类**(非 autoload、无场景依附),每场战斗实例化一次(B1 编排前提;冒烟经 class_name 直引构造)。容器无运行时大状态机——实例生命周期四态(战斗域): + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| BattleClosed(域闭) | 构造后未开域 / notify_battle_end() 后 | apply_effect 全拒绝(false);唯一出口 notify_battle_start() | +| Active(存续) | apply_effect 受理(新实例/叠层/刷新) | 每轮结算被 B1 兑付消费;decay_round() 后 remaining −1 | +| Expired(移除) | remaining_settlements = 0 | 实例销毁(明细流水已由 B1 记录,B3 不留历史) | +| Cleared(清空) | notify_battle_end() | 全实例销毁、容器回空 → BattleClosed(不跨战斗) | + +不变式(debug 断言口径):remaining ∈ [0, duration_settlements];stack 行 layers ∈ [1, max_stack];stack/refresh 策略下同 id 至多一个实例——违反即 debug 断言中止;API 违规(域闭授予、未知 effect_id、满层授予)= 拒绝(false)+ debug 诊断,不进程致命(承 A3 拒绝口径;进程致命保留给 D2 数据通道)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| EC1 域闭状态授予(BattleClosed 下 apply_effect) | 拒绝(false)、容器不变 + debug 诊断——B1 过早/过晚调用 = 编排错误显式暴露,不静默吞 | States 节 API 口径 | +| EC2 未知 effect_id | 拒绝(false)+ debug 诊断;表行删除/改名后旧引用的暴露点——悬空引用加载校验 = 首引激活条款:B4 RewardTable 首个 effect_id 引用已回填、校验已激活(registry dangling_ref_policy;条件性夹具 = B4 b4_dangling_effect) | A4 counter_item_ids 同款 · D2 首引兑现 | +| EC3 满层授予 | 恒 false 且容器逐字不变(拒绝原子性——无半写、无溢出落层);"溢出未生效"行归 B1 结算明细;同轮多授予谁被拒取决于 B1 授予次序——授予次序确定性是 B1 编排义务 | F1 Output Range · Rule 5 · 义务① | +| EC4 空刷新(refresh 行 R 已 = D 再授予) | 返回 true、无任何变化——合法空操作(refresh 无满层/溢出语义);MVP 无 refresh 内容行 = 该语义零消费者,首个 refresh 行上线时须补冒烟 AC;空刷新静默返 true 与 Rule 5"不静默蒸发"存在原则张力(stack 有溢出行、refresh 无对应物)——MVP 零消费者故休眠,首个 refresh 内容行上线时其授予可见性语义由 B4/B1 随 OQ3 一并裁决 | F1 决议 · systems-index 登记 | +| EC5 同轮 dur=1 行多次授予 | 层在单轮授予批次内累积(L=N)→ 同轮兑付一次(合成 L×m)→ 轮末全移除;跨轮必为新实例——"N 旗 = N debuff 总吞吐"在 B3 侧的呈现 = 单轮层和,对账归 B1 | F1 Example · A3 条款 2 | +| EC6 同一事实被重复授予(同 effect_id + origin_cell + round_index 两次 apply) | B3 不去重——容器是 dumb 账本,无从辨别事实合法性,两次授予 = 两层/两实例;事实→授予的恰一次转化归 B1 编排义务(round_end 双发射源同族问题,A3 先例) | 义务② | +| EC7 合成值越域(L×m 越出 [-100, 100] 或穿越地板) | B3 不 clamp、值 + unit 标签原样输出——收口归 B1 SettlementTable(B1 验收项:percent→乘数换算方向与地板规则须成文——weaken AP 界/表界、defense_up 叠满三例已在 F2 登记) | F2 Output Range · 义务③ | +| EC8 清图换图/弃图刷新 | 容器不清空——状态跨图延续(战斗域非图域);旗标记随图消亡(A2 域)不影响已授予实例;已消费兑付资格的雷格随图消亡(A3 域),新图新雷自然新资格——B3 无从感知也无须感知 | Rule 1 · AC-10 | +| EC9 战斗中热重载 StatusEffectTable | 走 D2 既有门(战斗中 reload 拒绝);已授予实例不受任何表重载影响——实例在授予时从表行拷贝数值快照(m/D/unit),热重载只影响后续授予 | A4 EC8"快照非活引用"同款 | +| EC10 refresh/independent 行配置 max_stack > 1 | 行为 = 忽略(F1 分支不读 M)——内容约定非校验项,validate() 不拒绝(防过度约束锁死未来内容) | F1 · AC-13 | +| EC11 notify_battle_start / notify_battle_end 重复或错序 | Active 下再 start → false + 诊断;BattleClosed 下再 end → false + 诊断(bool 口径统一,承 A3 输入×相位矩阵同款) | A3 同款 | +| EC12 query_active 空 side 或空容器 | 返回空 Array——合法状态非错误;D1 HUD 据此渲染"无状态" | D1 空态义务 | +| EC13 decay_round 在域闭时被调用 | 返回 0——域闭时容器恒空(battle_end 已清),0 是自然结果、无独立分支、不诊断(幂等安全的兜底读法) | F3 极端域 | +| EC14 independent 行被高频授予 | 实例数上界 = 受理授予数(有界、确定,无满层语义)——MVP 无此内容行;未来 DOT 类高频授予的容量语义归扩展(接口漂移),本 GDD 不预写 | F1 Output Range | + +## 依赖 + +**上游**(Foundation 层,无其他玩法上游): + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | hard(数据通道) | StatusEffectTable schema 承载 + ConfigDB 读取;本 GDD 为该表回填 owner(schema + 四行已定稿);新字段走接口漂移规则。systems-index 分层表按其惯例不列纯数据读取边(事实依赖同口径)——两处相容非矛盾 | + +**下游**: + +| 依赖方 | 性质 | 接口要点 | +|---|---|---| +| B1 | hard | 结算编排期调用五 API:notify_battle_start / apply_effect(三源授予——B1 从 A2/A3 事件收集 round facts 后转化)/ query_active(side) 兑付消费 / decay_round() 轮末衰减 / notify_battle_end() 清空;结算明细来源标注数据源 = 实例字段(P2);效果语义映射与结算作用顺序归 B1 SettlementTable(effect_id 键);F1 条款 1 对照表的"实物"供给方;最后一轮 decay 与否归 B1 判定次序域;义务清单 8 项见下表 | +| B4 | soft | 安全格 buff 行值与 weaken 档位的锚验收权(条款 2 + A4 三定价硬约束引用"最低档安全格 buff 当量"——参照对象 = attack_up 行当量);清图奖励若含 buff 经 apply_effect(source=clear_reward,B4 期定);RewardTable 引用 effect_id 的悬空校验已随首引激活(EC2) | +| B2 | soft | ambush 行量纲/上限验收锚(条款 3:B2 惩罚上限表 vs B3 行值,对账归 B1 闭合;量纲已定稿——registry ambush_unit_definition);未来敌人词条引用状态(免疫/抗性)为预留注记,不预写 | +| A3 | 契约相邻(零运行时接口) | A3 不调用 B3;条款 1 四源当量对照以 B3 实例为对照实物;标中旗兑付资格(雷格实例键)判定在 A3/A2 域,B3 只接收 B1 转化的授予——资格判定不过 B3 | +| A2 | 间接(零直接接口) | 开格/标中/开雷事实经事件 → B1 收集 → apply_effect;A2 不感知状态容器 | +| D1 | soft | query_active(hero) 为 HUD 状态栏数据源(display_name/层数/剩余结算次数常显;EC12 空态渲染);层数/到期变化反馈——义务清单 ×4 见下表 | +| B5 | soft | 结算明细/战斗动画的实例字段数据源(经 B1 结算流水;MVP 明细行即呈现,动画归垂直切片)——义务 ×2 见下表 | +| D5 | soft | 教学口径:按结算次数计时("还剩 N 次结算"式读法随行值)、战斗结束全清(防"我的 buff 哪去了"误读——A3 RAP 清零教学同款)、满层溢出浪费提示、"同轮拿满优于跨轮叠"教学项(stack 时序补偿——OQ7 证伪项配套,证伪成立前以中性提示形态) | + +### B1 侧义务清单(登记 B1 GDD 验收项,缺项即 blocking——A4 对 A1/A2 登记先例) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| ① | 同轮多授予按确定次序调用(EC3) | B1 | 已落位 B1 GDD(履行回执) | +| ② | 事实→授予恰一次转化(EC6) | B1 | 已落位 B1 GDD(履行回执) | +| ③ | percent→乘数换算方向与合成值地板成文(EC7/F2——AP/表界数值随 F0 修订更新为 −520%/−1040%) | B1 | 已落位 B1 GDD(履行回执) | +| ④ | 最后一轮 decay 判定(F3) | B1 | 已落位 B1 GDD(履行回执) | +| ⑤ | F1 条款 1 效果档位对照表以 B3 实例为对照实物 | B1 | 已落位 B1 GDD(履行回执) | +| ⑥ | 每结算编排恰一次 decay_round()——decay 基数 = 1 是 dur=1 一次性语义与全部衰减语义的先决条件;Active 相位 double-decay 在 B3 侧无守卫(信任 B1 编排,误编排经本义务验收与可回放步骤流暴露) | B1 | 已落位 B1 GDD(履行回执) | +| ⑦ | 结算明细按授予流水逐条标注来源(F1 合并坍缩归因的兑付面补偿——合并实例只持首授 origin_cell/round_index,per-layer 来源还原的数据源 = B1 授予流水) | B1 | 已落位 B1 GDD(履行回执) | +| ⑧ | "溢出未生效"行进结算明细协议(Rule 5 后半的 B1 侧承载;呈现归 B5/D1) | B1 | 已落位 B1 GDD(履行回执) | + +### 呈现与教学义务清单(已登记 systems-index,缺项即 blocking,随各 GDD 门禁评审核对) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| D1-① | HUD 常显 + EC12 空态规格定稿(隐藏栏 vs 空槽由 D1 裁决但须成文) | D1 | 已登记 | +| D1-② | effect_id→icon 映射键契约(art-bible 形状+色相双通道) | D1 | 已登记 | +| D1-③ | 有利/有害极性呈现映射(数据源 = B1 SettlementTable 效果语义,非 magnitude 符号——负值增益存在) | D1 | 已登记 | +| D1-④ | 战斗结束全清的状态栏去留 | D1 | 已登记 | +| B5-① | 溢出行呈现(数据源 = B1 义务⑧明细协议) | B5 | 已登记 | +| B5-② | origin_cell→玩家可读形态(缩略图/格子高亮映射——坐标值本身不直接呈现) | B5 | 已登记 | +| D5 | "同轮拿满优于跨轮叠"教学项(OQ7 证伪项配套) | D5 | 已登记 | +| B4 | 奖励身份预读(stack 时序规划的推理原料——创意风险 #1 切面,OQ7 配套补偿载体) | B4 | 已登记 | + +### 一致性核对与注记 + +| 项 | 内容 | +|---|---| +| 双向一致性 | B4 ← A2, B3, D2 ✓ · B1 ← A3, B2, B3, B4, D2 ✓ · B3 ∈ Foundation、无上游 ✓(systems-index 依赖分层表) | +| B2 无边说明 | 索引无 B2←B3 边与上表 B2 行不矛盾——敌方状态的承载在 B3,但授予路径经 B1,B2 与 B3 之间是验收锚关系非运行时依赖,故不列边 | +| D2 hard 措辞 | "hard"指 ConfigDB 读表的数据通道事实依赖(registry config_db referenced_by=[all-systems] 同口径);索引分层表不列纯数据读取边——相容非矛盾 | +| Rule 10 回写 | 对 A3 OQ5"档位归 B4 表设计"的收编(初值定稿权归 B3、锚验收权留 B4/B1)已回写 systems-index 门禁记录与 registry ownership_note | + +### 呈现与 UI 契约(数据面义务——B3 无自有渲染面,呈现归 D1/B5) + +| # | 数据面义务 | 归属 | +|---|---|---| +| 1 | HUD 状态栏数据源三要素:图标(effect_id→icon 映射归 D1/art-bible)、层数(L)、剩余结算次数(R)必须可读——时限/剩余次数不可读 = 违反 P1 规划素材与 P2 兑现可见性 | D1 | +| 2 | 结算明细行数据源:display_name + 有效值(L×m)+ unit 标签 + 来源标注(source_class→文案映射归 B1/D1,P2 归因)——"增伤 +100%(第 3 轮 · 安全格)"式逐行读法;origin_cell 玩家可读形态(缩略图/格子高亮)归 B5(坐标值本身不直接呈现);满层溢出提示 = "溢出未生效"行含来源(Rule 5 诚实语义) | B1/D1/B5 | +| 3 | 视觉身份锚适用(game-concept):数值可读性优先于氛围(锚 1)——状态数值在暗色底上须高对比;增益/削弱图标不得只靠色相区分(锚 2 形状+颜色双通道);有利/有害极性不得由 magnitude 符号推导(负值增益存在——数据源 = B1 SettlementTable 效果语义) | D1 | +| 4 | 零信号外发:纯被动容器不发任何事件——呈现刷新点 = B1 结算编排完成后的界面刷新(D1/B5 域);到期/层数变化反馈经 decay 前后查询差分或 B1 流水(D1 裁决);无音频需求(到期/叠加音效归 D4 Alpha 期) | D1/B5/D4 | + +## Tuning Knobs + +### StatusEffectTable schema(D2 承载,B3 回填 owner——Rule 4 豁免:定义表非进度成长表,无 (level_index, node_depth) 维度;未来随关卡成长的效果走接口漂移加列,A4 先例) + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| effect_id | String(主键唯一) | 状态标识;B1 SettlementTable 语义映射的键 | +| display_name | String | 图例/HUD/明细用名(本地化归 D6) | +| target_side | enum {hero, enemy} | 作用侧(enemy 侧 side-wide = 全体存活敌单位) | +| magnitude | int | 域随 unit:percent ∈ [-100, 100] 且 ≠0 / flat ≥ 1(跨字段校验) | +| unit | enum {percent, flat} | percent = 百分比修正;flat = 固定值(伏击类) | +| duration_settlements | int [1, 99] | 授予时初始剩余结算次数(一次性 = 1) | +| stack_policy | enum {stack, refresh, independent} | 同 id 重复授予的合并策略(Rule 4) | +| max_stack | int [1, 16] | 层数上限;仅 stack 消费——refresh/independent 行约定恒 1(内容约定,非校验项) | + +加载校验(表非空——D2 Rule 6,四行即满足):effect_id 唯一(重复键致命——本表唯一致命夹具 dup_key);枚举域校验(target_side/unit/stack_policy 出域致命);duration/max_stack 域校验;跨字段 percent ≠ 0 / flat ≥ 1。 + +**F0 裁决回执注记(m_w)**:weaken.magnitude 现行值域 [60, 85]、默认 −65——**回执以 B1 GDD F0 为准**(registry status_effect.anchor_warning);F2 越域界随移 AP 界 −520% / 表界 −1040%;AC-14 腿 3 非平凡性复查通过(16×65 = 1040 越出 percent 域)。原 −25 值的裁决关系与修订轨迹见版本历史。 + +### Knob 表 + +| Knob(表行值,D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| attack_up.magnitude | +50 | 叠满 +100%,敌方 HP 池相对贬值(战斗时长曲线压向 B1 对账) | 安全格奖励空心化——A3 条款 3 左臂(P(安全)×buff)塌方 | +| defense_up.magnitude | −30 | 叠满 −60%(免伤轮不再由 defense_up 单侧可达——B1 地板规则压力同步解除) | 减伤无感,防御型奖励萎缩(回调下界 = 减伤墙复发——观测锚 = C3 承伤带复验与 B1 条款③重跑) | +| weaken.magnitude | −65(值域 [60, 85]) | 越域界加深(表界 16×m_w)、地板交叉层数 ⌈100/m_w⌉ 恒 2、边际层贬值 | 值域下界 60 = 条款 2 锚两腿可行交集——越界即破锚(修订方向 B4/B1 裁决) | +| ambush.magnitude | +3 flat | 量纲已定稿(每攻击类行为 +3 HP)——踩雷痛感随值放大,条款 3 上限对账归 B1 | 踩雷无痛感——P3"风险与收益同源"失衡 | +| duration_settlements(四行) | 1/1/1/1(当次结算) | 窗口拉长恢复跨轮延续(规划变简单、衰减感消失) | 已在下界 1(一次性语义——再低无定义) | +| max_stack(四行) | 2/2/16/16 | 合成越域幅度增大(B1 地板压力↑) | 叠层机制边缘化(重复奖励变溢出浪费) | + +- **非 knob(结构冻结)**:stack_policy 三分支语义、unit 语义、source_class 枚举、时限量纲 = 剩余结算次数(game-concept 定稿)、先兑付后衰减时序、拒绝原子性、零随机——修订走本 GDD 流程非调参。 +- **Knob 交互**:attack_up 与 weaken 是条款 2/3 对账的**两端**——禁止独立调参,联动评估归 B1 闭合对账;A3 的 ap_per_round 是 weaken 单轮层和的经济上限(F2 AP 界 8×m_w 的来源);效果档位当量对照表(B1)落定前,四行 magnitude 的任何调整都须**重跑条款 2/3 锚预检**并同步 registry(接口漂移义务)。defense_up 叠满 −60% = 当次结算 0.4× 承伤(消费读法归 B1);减伤窗口强度与 attack_up 同入 B1 对账联动评估(B1 地板规则 × 战斗时长曲线 × 条款③ V_safe),禁止独立调参——defense_up.magnitude/duration 已纳入"禁独立调参"联动网络(m_w↔D_ref↔m_d↔duration_d,与 B1 Knob 交互同口径);减伤墙批(−50→−30)即该联动的实证回调(免伤轮现行可达路径 = weaken 饱和,见 F2)。 + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | B3 是纯行为容器——无场景、无表现面、无加载期自有校验类别。冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code)、ConfigDB autoload 由脚本显式实例化);容器经 class_name 直引实例化(每用例全新实例、无跨用例状态——A4 AC-5 直引先例同款;AC-19 扫描对该实例化路径豁免) | +| 被测面 | B1 就绪前冒烟直接驱动 B3 最小 API 面(承 A3"直接驱动最小 API 面"先例);B1 就绪后改走真实调用方(结算编排),断言口径不变。API 面:notify_battle_start() · apply_effect(effect_id, source) → bool · query_active(side) → Array · decay_round() → int · notify_battle_end() → bool | +| 返回口径 | 全部布尔/计数返回 = 受理/拒绝(A3 全 API 布尔口径同款)——"拒绝"直接断言返回值,附 debug 诊断、非进程致命(承 A3 拒绝口径;进程致命保留给 D2 数据通道);decay_round 返回值 = 本轮移除数断言;apply_effect 是唯一授予入口(Rule 3),冒烟直接构造合法 source = {source_class, origin_cell, round_index}(B1 转化产物的冒烟等价——信任方向同 A3 对 chain_size/clears_map 的处理);query_active 为只读、任意相位合法 | +| 只读观测量 | 实例六字段 effect_id · layers · remaining_settlements · source_class · origin_cell · round_index;query_active(side) 元素 = 结算面向载荷投影:六字段 + display_name + 授予时数值快照 (magnitude, unit)(EC9 快照语义 + B5/D1 数据源)——有效值 = layers × 快照 magnitude、unit 标签原样随值输出(F2);枚举序 = 插入序(Rule 6)。相位无字段观测量:BattleClosed/Active 由 API 返回值推断;Expired/Cleared 为销毁态不可观测(瞬态纪律同 A3 RoundEnd) | +| 时序口径 | B3 无 round_start/settlement API——"轮边界"在冒烟中 = 相邻两次 decay_round() 调用之间的授予/查询窗口(B1 编排在 B3 侧的可见面恰为这五个 API);多步 GIVEN 均给出构造序列 | +| 期望值纪律 | magnitude / duration_settlements / max_stack 均为 knob(Tuning Knobs 节)——各 AC 的 THEN 具体数值按当前 ConfigDB 表值推导、不写死(正文以 D_a / M_a / m_w 等表读值记号书写);结构字段(effect_id 集合、target_side、unit、stack_policy)非 knob、定值断言合法;行表"来源"列为内容档案(provenance)非 schema 字段、非运行时约束——B3 不校验 source_class 与行的对应(EC6 dumb 账本同族) | +| 夹具策略 | ① 默认根(干净检出四行 MVP 内容)承载全部分支行为 AC;② 程序构造(schema 类 class_name 直引实例)承载 AC-13 域校验——不经 load()/preload();③ 行为夹具根经 --data-root 注入(仅 debug、不入 data/tables/——D2 夹具纪律),承载 MVP 无行的语义(refresh/independent)。显式声明:夹具测试行非 MVP 内容(Rule 10 四行全 stack;测试行仅供冒烟)。选夹具根而非程序构造注入表的理由:容器经 ConfigDB 单点读表(Rule 2/9)、无表注入 API——--data-root 是 debug/test 构建的既定注入通道;各 AC 声明其表根(默认根/下列三根),用例间无顺序依赖、每用例先 reset_diagnostics()(D2 冒烟协议,全局声明) | +| 致命类收窄 | 恰一根致命夹具 status_effect_dup_key(重复键类别):runner manifest 退出码行 dup→≠0(独立 headless 进程、游戏通道,D2 AC-2b 协议同型)——"游戏内真的会死"在本表上的落点由此腿实证;其余致命类别(枚举域/域校验/跨字段)由 D2 AC-2 同类别 loader 级夹具承载(表无关,豁免声明收窄保留);schema 侧以程序构造断言域类别(AC-13) | +| out-of-scope | deferral 生效条件 = B1 义务清单(依赖节,均已落位):① 同轮多授予先到先得次序(EC3 后半——冒烟单调用方次序即构造序,B1 编排次序 = 义务①)② 事实→授予恰一次转化(EC6 后半——义务②)③ percent→乘数换算方向与合成值地板(EC7 收口半——义务③)④ 最后一轮 decay 判定次序(F3 时序契约的 B1 半——battle_end 全清兜底使两序同终态,冒烟不构造)⑤ 效果档位对照表以 B3 实例为对照实物(义务⑤,对账归 B1)。另有:结算明细"溢出未生效"行与来源标注呈现(冒烟无明细观测量——对侧承载已登记:B1 义务⑧ + B5×2,deferral 生效条件成立);D1 HUD 状态栏与 EC12 空态渲染(D1×4 已登记);效果语义到结算公式映射(Rule 7——B1 SettlementTable 域);Rule 8 负空间(无驱散/延时/转移 API——"无对应 API"不可 GIVEN-WHEN-THEN,验收形态 = 前言 API 面封闭枚举 + 接口漂移修订门);D2 schema 校验致命端到端(双通道协议承载);悬空引用校验(EC2——D2 双通道条件性夹具);EC9 reload 门腿(D2 AC-16 既有覆盖 + B1 是置位/复位唯一调用方——B3 无断言义务);EC8 端到端换图腿(B1 编排合规,AC-10 裁决) | + +夹具规格(登记值即相关 AC 的推导基准): + +| 根 | 内容 | 用途 | +|---|---|---| +| status_effect_policy_rows | refresh_probe(display_name="刷新探针" · hero · percent · magnitude=+10 · duration=3 · stack_policy=refresh · max_stack=4——故意违反"refresh 行恒 1"内容约定)+ independent_probe(display_name="独立探针" · enemy · flat · magnitude=+2 · duration=3 · stack_policy=independent · max_stack=1)——全字段域内合法数据 | AC-5 / AC-6(EC4 / EC10 / EC14 载体) | +| status_effect_policy_rows_reloaded | 仅 fresh_probe 一行(hero · percent · magnitude=+20 · duration=9 · stack · max_stack=1);根规格 = D2 全表清单拷贝 + 仅置换 StatusEffectTable 为本单行(缺任一表文件即触发 D2 EC1 文件缺失致命、GIVEN 不可构造——"仅一行"指该表内容非根内容) | AC-15 重载腿 | +| status_effect_dup_key | D2 全表清单拷贝 + StatusEffectTable 置换为默认四行 + 追加一行与 attack_up 同 effect_id 的重复键行(其余字段域内合法)——唯一致命夹具(A4 variation_dup_key 同型):loader 集成缝(loader 是否对本表调用校验)的进程级落点 | runner manifest dup→≠0 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | 全新容器(BattleClosed、空;表根 = 默认根) | ① 依次 apply_effect(attack_up, 合法 source)、query_active(hero)、query_active(enemy)、decay_round();② notify_battle_start();③ 再次 notify_battle_start() | ① apply 返回 **false**(EC1 域闭拒绝)、双侧 query 均 = **空 Array**(EC12)、decay 返回 **0** 且诊断收集器零新增(EC13——空容器域闭的兜底读法,非独立分支不诊断);② **true** 且 query 仍双侧空(战斗开始时为空——Rule 1);③ **false**、相位与容器不变(EC11 前半——Active 再 start) | +| AC-2 | Active 且已建账(构造:apply_effect(attack_up) ×1 + apply_effect(weaken) ×1,query 断言含两实例) | ① notify_battle_end();② 再次 notify_battle_end();③ decay_round();④ apply_effect(attack_up, …);⑤ notify_battle_start() | ① **true**、query 双侧空(Cleared 全清、不跨战斗——Rule 1);② **false**(EC11 后半——Closed 再 end);③ **0** 且诊断零新增(EC13 域闭腿);④ **false**(EC1 全场战后腿);⑤ **true** 且容器空(唯一出口——下一场战斗重启,States 表) | +| AC-3 | Active | apply_effect("attack_up", {safe_cell, (3,4), round_index=2}) | true 且 query_active(hero) 恰 1 实例,其六字段逐一相等:effect_id="attack_up"、layers=1、remaining=D_a(attack_up.duration_settlements 表读值)、source_class="safe_cell"、origin_cell=(3,4)、round_index=2(P2 归因字段授予即携带——Rule 3,结算明细逐行标注的数据源) | +| AC-4(stack 分支前两腿 + "R 不动" spec 决议) | Active;D_a / M_a 为表读值 | 腿 1:无存活实例时 apply_effect(attack_up, …);腿 2(前提 D_a ≥ 2 ∧ M_a ≥ 2——默认表值 D_a=1 不满足,本腿默认退化跳过 + 显式 SKIP 标记防 CI 静默、不假失败,前提满足时执行;构造 = 腿 1 实例经 decay_round() 一次至 R=D_a−1)再 apply_effect(attack_up, …) | 腿 1:true、新实例 **L=1、R=D_a**;腿 2:true 且 **L=2、R 仍 = D_a−1**——**stack 授予不触碰 R**(F1 注 spec 决议:只有初始授予定钟;实现若重置 R 即三分支失去区分性,本腿即其暴露点) | +| AC-5(refresh 分支 + EC4 + EC10;表根 = status_effect_policy_rows,测试行非 MVP 内容) | Active;D_f=3、m_f=+10(夹具规格登记值) | ① apply_effect(refresh_probe, …);② decay_round() 后 apply_effect(refresh_probe, …);③ R 已 = D_f 时再 apply_effect(refresh_probe, …);④ 再 apply_effect ×2;⑤ 该根经测试通道加载 | ① true、新实例 L=1、R=D_f、载荷 = 1×m_f(refresh 行 L≡1——F2);② 返回 0(R=D_f−1)后 **true、R 重置 = D_f、L 不变**(F1 refresh 正常腿);③ **true 且实例六字段 + 载荷逐字不变**(EC4 空刷新 = 合法空操作——refresh 无满层/溢出语义);④ 均 true(max_stack=4 从不被消费——**EC10 行为 = 忽略**);⑤ 诊断为零(内容约定非校验项——EC10 非校验半句) | +| AC-6(independent 分支 + EC14;同夹具根,测试行非 MVP 内容) | Active;D_i=3(夹具规格登记值) | 同轮授予批次内以互异 origin_cell((1,1)/(2,2)/(3,3)——插入序可观测的前提)apply_effect(independent_probe, …) ×3;再 decay_round() | 全部 true 且 query_active(enemy) 恰 **3 个并存独立实例**(各 L=1、R=D_i、**投影序列 = 授予序** (1,1)→(2,2)→(3,3)——互异 origin_cell 使插入序成为可判定断言,Rule 6 枚举确定性);decay 返回 0(D_i≥2 无移除)且三实例各自 R=D_i−1(F3 逐实例、无豁免);实例数上界 = 受理授予数(**EC14 有界确定**——无满层语义) | +| AC-7(EC6 不去重) | Active 且已有 attack_up 存活实例(L=1,构造同 AC-3) | 以完全相同的 (effect_id, source)(同 origin_cell、同 round_index——同一事实重复转化的模拟)再次 apply_effect | true 且同实例 **L=2**——B3 **不去重**(dumb 账本,无从辨别事实合法性,EC6);out-of-scope:事实→授予恰一次转化归 B1 编排(义务②,deferral 生效条件 = 依赖节清单) | +| AC-8(F3 主式) | Active(构造:apply_effect(weaken) ×2 → 单实例 L=2、R=1;apply_effect(attack_up) ×1 → R=D_a;前提 D_a ≥ 2——第二 WHEN 腿依赖,默认表值 D_a=1 不满足 → 本条退化跳过 + 显式 SKIP 标记(同 AC-4 口径、防 CI 静默),前提满足时执行) | ① decay_round();② 再 decay_round();③ 对空容器 decay_round() | ① 返回 **1**(= 构造中 dur=1 实例数,由表读值推导;weaken 实例移除)、attack_up R=D_a−1 仍在、容器插入序不变;② 返回 1(attack_up 移除——**R 归 0 即销毁,容器无 0 值实例**,与不变式 remaining ∈ [0, D] 相容);③ 0(F3 极端域)。R 轨迹与 k 全部由表值推导 | +| AC-9(EC5 dur=1 单轮叠层 + 一次性全落点) | Active(新轮) | 同轮授予批次内 apply_effect(weaken, …) ×3(N=3 为构造选择,≤M_w 表读值);decay_round();下一轮边界再授予 weaken ×1 | 全部 true、单实例 **L=3、R=1(=D_w 表读值)**、载荷 = 3×m_w(同轮兑付一次的合成输入;N 旗 = N debuff 总吞吐的对账归 B1 条款 2——义务⑤邻接,out-of-scope);decay 返回 1、实例移除;再授予 → **新建**实例 L=1——**跨轮必为新实例**(旧实例已销毁,F1 Example weaken 腿) | +| AC-10(EC8 跨轮/跨图延续——结构性裁决) | Active,attack_up 授予 ×1(R=D_a) | 连续多轮边界仅 decay、无新授予(停手轮/弃图轮的 B3 视角——B3 无图概念、换图/刷新零专用信号);逐步 query | 实例 R 逐轮 −1 **不停摆**(无豁免实例、无参数——**无"不结算就不掉次数"的免费延时**,Rule 2),且每步 query 断言容器非空直至 R 自然归 0——**除 notify_battle_end 外无任何 API 入口使容器清空**(先轮授予的实例按 R 存续至自然到期)。裁决:EC8 的"换图不清空"对 B3 的可测内容 = 本条两句(跨轮延续 + 唯一清空入口);"换图"事件对 B3 不可观测(零接口)——端到端"真实换图下容器仍存续"归 B1 集成(编排契约:仅 battle_over 调 end——B1 API 合规半句,B1 GDD 域,非新增义务) | +| AC-11(EC3 满层拒绝与原子性) | Active;M_a 为 attack_up.max_stack 表读值;构造叠满(M_a 次授予——次数由表值推导、不写死),前 M_a 次均 true、单实例 L=M_a | 第 M_a+1 次 apply_effect(attack_up, …) | **false** 且容器逐字不变——query_active(hero) 恰 1 实例,其六字段 + 载荷 + display_name 与授予前快照**逐项相等**(拒绝原子性:无半写、无溢出落层,F1 Output Range);"溢出未生效"明细行归 B1(out-of-scope,义务①邻接) | +| AC-12(EC2 未知 effect_id) | Active | apply_effect("no_such_effect", 合法 source);表行改名后的旧 id 同理 | **false**、容器不变。政策声明:悬空引用加载校验 = 首引激活条款——B4 RewardTable 首个 effect_id 引用已回填、校验已激活(registry dangling_ref_policy;条件性夹具 = B4 b4_dangling_effect;out-of-scope,D2 双通道) | +| AC-13(加载期域校验全谱——程序构造,class_name 直引,A4 AC-5 同型) | StatusEffectTable schema 类直引实例(不经 load()/preload()) | validate() 分别作用于非法组:percent=0(跨字段 percent≠0)/ percent=101 / percent=−101、flat=0(flat ≥ 1)、duration ∈ {0, 100}、max_stack ∈ {0, 17}、stack_policy/target_side/unit 各一出域枚举值、两行同 effect_id(重复键);再作用于域内代表值 percent ∈ {100, −100, 1, −1}、flat=1、duration ∈ {1, 99}、max_stack ∈ {1, 16} 及 stack_policy=refresh 且 max_stack=4 的行(EC10) | 非法组:各含对应域错误类别;合法组:零诊断(EC10——内容约定非校验项,防过度约束锁死未来内容) | +| AC-14(F2 合成 + EC7 不 clamp) | Active;m 各值为表读值 | 腿 1(percent·stack):apply_effect(attack_up) ×2(前提 M_a ≥ 2——默认满足,M_a=1 时退化为单层恒等断言 + SKIP 标记);腿 2(flat):apply_effect(ambush) ×3;腿 3(越域,EC7):同轮 apply_effect(weaken) ×M_w(M_w 表读值、≤16 同轮批授予合法);腿 4:refresh/independent 行载荷 L≡1 | 腿 1:载荷 = **2×m_a** 且 unit="percent" 标签随值输出(量纲标签不进乘法——F2);腿 2:载荷 = 3×m_amb、unit="flat"(B3 不解释单位——排除声明);腿 3:载荷 = **M_w×m_w 原样输出、无 clamp**("不 clamp"的可判定形式 = 载荷与表值推导值恒相等,任何地板都会使其不等;承载声明:本腿非平凡性依赖默认基线——M_w=16、m_w=−65 → −1040 越出 percent 域;knob 调参使 M_w×m_w 绝对值 ≤ 100 时退化为恒等断言、不假失败,退化时用例输出显式退化标记 SKIP 日志、CI 可见防静默失真;地板/clamp 与换算方向归 B1 SettlementTable——out-of-scope,义务③);腿 4:已由 AC-5①/AC-6 承载(引用不重复) | +| AC-15(EC9 快照 vs 表重载——机制口径) | 表根 = status_effect_policy_rows、Active、已授予 refresh_probe ×1 并 decay 一次(R=D_f−1、载荷 1×m_f) | 冒烟脚本经测试通道换根加载 status_effect_policy_rows_reloaded(load_tables(root, collect_only=true) 直调——非 D2 reload() 路径:不过 set_reload_gate、不发 config_reloaded、无 EC16 语义,门语义归 D2 AC-16/B1〔OQ5〕;reloaded 根规格 = D2 全表清单拷贝 + 仅置换 StatusEffectTable,见前言夹具规格);再 apply_effect(fresh_probe, …)(新 id、无存活实例——refresh 重置目标取实例快照 D 的口径见 F1 注,本腿不涉存活实例重置) | **已授予实例六字段与载荷逐字不变**(授予时拷贝 m/D/unit 数值快照——EC9"快照非活引用",A4 EC8 同款语义;实例为 B3 自持,故 B3 冒烟自测、无需 deferral);fresh_probe → true 且新实例 **R=9、载荷 1×(+20)**(夹具登记值)——**换根只影响后续授予** | +| AC-16(MVP 内容四行) | ConfigDB 默认根加载完成 | 读 StatusEffectTable | **恰含 4 行**、effect_id 集合 = {attack_up, defense_up, ambush, weaken}(定值);target_side:attack_up/defense_up=hero、ambush/weaken=enemy,unit:percent/percent/flat/percent,stack_policy=stack ×4(均非 knob 结构、定值断言合法——Rule 10"四行全 stack");display_name 四值非空(精确值对账归 AC-18);magnitude/duration_settlements/max_stack 仅作类型域断言、期望 = 表读值(knob 纪律——A4 AC-1 同款,防调参打假;行表"来源"列为 provenance 非表字段、不作断言) | +| AC-17(确定性——A3 AC-17 同型;可观测序腿) | 全新容器实例 ×2(同 ConfigDB 默认根;每遍新实例、无跨实例状态) | 同一定长序列驱动两遍:start → defense_up×1 → attack_up×1(同侧双实例、授予序 = 字典序**逆序**——区分"插入序实现"与"字母排序实现"的判别性前提)→ query 投影(hero 恰 2 实例,**投影序列 = [defense_up, attack_up] 定值断言**——插入序的行为级观测点)→ weaken×1 → decay → query 投影 → attack_up×1(并入腿——前提 D_a ≥ 2 成立时并入 L=2;默认表值 D_a=1 时旧实例已销毁,退化为新建 L=1 断言 + SKIP 标记,同 AC-4 口径)→ decay → end → query | 两遍的 query_active 投影序列(**含插入序**)与 decay_round 返回值序列**逐项相同**,且 hero 侧投影序恒 = 授予序(非字典序——零随机 + 插入序枚举的双重行为级证据——Rule 1;D2 AC-15 的 B3 层前身) | +| AC-18(registry 一致——A3 AC-10a 同型,无条件断言;registry 逐字段键) | ConfigDB 默认根加载完成 | 脚本解析 design/registry/entities.yaml 的 status_effect 条目(**逐字段 YAML 键**——机器对账前提,action_economy 同构) | ① fields 八键与 ConfigDB schema 字段名逐一相等、各键的类型/域/枚举描述与 schema 定稿相等(**缺任一键即失败**);② mvp_content 四键 id 集合与表行 id 集合相等,**非 knob 结构字段**(target_side/unit/stack_policy)逐一相等;③ rule4_exemption / dangling_ref_policy 注记存在性断言。knob 活值(magnitude/duration_settlements/max_stack)**不作定值对账**(A4 AC-8 同款——对账口径 = registry 随调参同步〔接口漂移义务〕,本 AC 豁免其值)。生效前提:本条目为定稿回填 + registry 逐字段键重构的产物,AC 生效前提 = 重构回填完成(不写"若存在"软口径——条件化使一致性检查落空,A3/A4 均无条件) | +| AC-19(静态扫描——D2 AC-9/AC-10 模式引用,A3 AC-10b 同型) | B3 文件集(容器/schema/冒烟脚本/夹具规格)纳入 D2 AC-9 模式项目级扫描域 | 静态扫描:randomize() / RandomNumberGenerator / ConfigDB 外 data 表加载 / extends Control / .tscn preload;+ D2 AC-14 人工三要素字面量扫描 | 全部**零命中**(schema 与容器的 class_name 直引实例化豁免——AC-13 前提,A4 AC-7 同款);字面量扫描引用不另设硬 THEN(字面量判定误报率高——D2 已定性 warning 级 + 人工复核);零随机的行为级硬证据 = AC-17 | + +### 覆盖矩阵(qa-lead 交付,验收评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules | Rule 1→AC-1/2/10/17/19 · Rule 2→AC-8/9/10 · Rule 3→AC-3+前言 API 封闭 · Rule 4→AC-4/5/6 · Rule 5→AC-11(明细行→B1 义务⑧+B5×2 已登记)· Rule 6→AC-1/2(decay 次序 OOS)· Rule 7→OOS(载荷只读面 AC-14)· Rule 8→前言 API 封闭+OOS · Rule 9→AC-13/16/19 · Rule 10→AC-16/18 | +| Formulas | F1→AC-4/11(stack)/AC-5(refresh)/AC-6(independent)· F2→AC-14+AC-5①/AC-6 · F3→AC-8/9/10 | +| 边界情况 | EC1→AC-1/2 · EC2→AC-12 · EC3→AC-11 · EC4→AC-5③ · EC5→AC-9 · EC6→AC-7 · EC7→AC-14 腿 3 · EC8→AC-10 · EC9→AC-15 · EC10→AC-5④+AC-13 · EC11→AC-1/2 · EC12→AC-1 · EC13→AC-1/2 · EC14→AC-6——无洞 | + +## 开放问题 + +| # | 问题 | owner | 状态/时机 | +|---|---|---|---| +| 1 | weaken 条款 2 锚裁决——归一口径已由 F0 裁决关闭(每结算瞬时当量;总暴露分支被 schema 域禁止排除),m_w 值域 [60, 85]/默认 −65 已入文。余项:终值共裁确认 + registry 终值回写,裁决时复查 AC-14 腿 3 非平凡性 | B4/B1(deadline:B1 GDD 强制检查点) | 余项随 B1 OQ2 联动 | +| 2 | ambush 量纲——已定稿(B2 GDD:每层伏击为每次攻击类行为附加 m_amb 点主角 HP 伤害、后置加法、charge 拍零兑付——registry ambush_unit_definition)。余项:B1 条款 3 上限检验 | B2 → B1 | 随 B1 对账闭合 | +| 3 | 首个 refresh/independent 内容行的 AC 补全——EC4 登记义务:奖励池引入非 stack 行时补真实内容行冒烟 AC,夹具探针行退役;其授予可见性语义(空刷新 vs Rule 5)随批由 B4/B1 裁决 | B4 | 奖励池引入非 stack 行时 | +| 4 | clear_reward 源授予——清图奖励若含 buff 的授予路径确认(apply_effect source=clear_reward;A3 条款 4 差异化插槽) | B4 | B4 期 | +| 5 | EC9 reload gate 的 B1 侧落点——防"战斗中真的被置位"零落点(qa-lead 缺口 #4) | B1 GDD 门禁评审 | 随 systems-index 门禁记录登记 | +| 6 | 效果当量对照表的预检重跑——条款 1 对照表落定时按预检重跑 weaken/ambush 方向性预检(正式对账载体);C3 首次调参即须按现行参照(attack_up 当量)复算条款 2 预检 | B1 | B1 对照表落定 / C3 首调 | +| 7 | stack-不重置 R 的 playtest 证伪项——F1 注决议(用户拍板、结构冻结)的体验面反例检验:"早拿必亏"(分轮拿满总暴露 150pp vs 同轮拿满 200pp——算例基线 = dur=2 行值;现行四行 D=1 下两读法总暴露相等、不对称休眠,dur ≥ 2 内容行上线时激活)叠加奖励身份不可预读(创意风险 #1)是否伤害"把 buff 纳入挖掘推理"的规划感(P1)。补偿载体先行:B4 奖励身份预读 + D5"同轮拿满"教学;证伪成立则走结构冻结修订流程回炉(合并/重定义 stack 分支)——非调参 | 垂直切片前置 playtest 批次 | playtest | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| 跨评审 B3 接口漂移批 | 2026-09-14 | /review-all-gdds 🔴-1 前半(减伤墙批回写缺口,registry 同批):defense_up.magnitude −50→**−30**(减伤墙批 C3 投值——叠满两层 −60 帽,defense_scan 墙轮 39%/真人 12 轮零承伤定量坐实)——MVP 内容行/Tuning Knobs/F2 Example 同步;免伤轮叙述同步(defense_up 单侧不可达 D'=0,现行可达路径 = weaken 饱和;F2 三例第三例改 defense_up 叠满读法——对侧 B1 F2 同批);Knob 交互:defense_up.magnitude/duration 纳入"禁独立调参"联动网络(m_w↔D_ref↔m_d↔duration_d);核心规则表增依据列(lint R8 引用列迁移) | +| v1.1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;散文/枚举/边界/AC 转表格;流程转字符画;公式本体、Variables 表与验算数据全保留;语义与 registry 现行值对齐(weaken −65 = F0 值域 [60,85] · 四行 duration 1/1/1/1 = 当次结算 · enemy 侧 side-wide 多敌读法),零规则变更 | +| 两级奖励批 | 2026-09-11 | attack_up/defense_up duration 2→1(当次结算语义:授予结算兑付后即衰减);attack_up 复活(HeroTable.atk 域 [1,999]、0 加载拒绝——基础攻击恒存在,消费面 = 主角战斗段先手攻击);条款 2 锚参照对象回迁 attack_up 当量;正文原 D=2 行注记与 Knob 表 2/2/1/1 旧行按本批现行值改写 | +| F0 裁决回执批 | 2026-09-10 | weaken −25 → 值域 [60, 85]/默认 −65(B1 GDD F0:归一口径 = 每结算瞬时当量,总暴露分支被 percent schema 域禁止排除——条款 2 锚红警关闭,锚两腿 7.8 > 6.0 / 7.8 ≥ E_dig 7.2 通过);B1 义务①–⑧履行回执落位;B3 正文回执以 B1 GDD F0 为准(registry status_effect.anchor_warning) | +| 单敌假设回验批 | 2026-09-09 | B2 定多敌模型(1–3 个同敌种敌单位)——Core Rule 1 单敌假设回验不成立,效果 side-wide 语义改"全体存活敌单位"(层数 = side 共享池,B3 现接口零变更兼容);target_ref 单体寻址 = 扩展位义务(首个单体效果上线时 B3 补列,随批 B1 归集语义/D1 呈现裁决)——registry single_enemy_assumption | +| Rev 1 | 2026-09-09 | design-review 7 项 blocking 同日核销:enemy 侧单敌假设入文(后经上批回验改写)· B1 义务 5→8 · D1/B5/D5 呈现义务登记 systems-index · AC-15 机制更正 + reloaded 根全清单规格 · 致命夹具补根 dup_key · AC-18 随 registry 逐字段键重构 · weaken 红警补修复空间不对称 + OQ1 归一口径前置 · stack-不重置 R 登记 OQ7 playtest 证伪项——明细见 reviews/b3-status-effect-review-log.md | +| v1 | 2026-09-08 | 一次会话定稿:三结构决策(单一计数/按行叠层/四行定稿权收编)用户拍板;lean 门禁 systems-designer 起草 Formulas · qa-lead 起草/验证 AC;CD-GDD-ALIGN 按 lean 跳过 | diff --git a/design/gdd/legacy-2026-09-14-v1/b4-reward-item-economy.md b/design/gdd/legacy-2026-09-14-v1/b4-reward-item-economy.md new file mode 100644 index 00000000..df058946 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/b4-reward-item-economy.md @@ -0,0 +1,735 @@ +# B4 奖励与道具经济(Reward & Item Economy) + +> **状态**:In Review → Revised · lean 格式——full design-review(五专员 + creative-director 终审)14 条 blocking 已同日核销,待 delta 复核放行;lean 定稿批的 CD-GDD-ALIGN 缺项已随终审补位 +> **版本**:v1-lean · 2026-09-13 · 作者:user + agents +> **承载支柱**:P2 主(奖励是"扫雷的因"的兑现物)· P3(安全格价值侧)· P1(奖励身份可预读 = 规划素材) +> **一句话**:B4 定义"扫雷决策的兑现物是什么、值多少、怎么随机、怎么入库"——安全格/清图奖励在 install 期预分配身份,经 B1 结算编排按 category 分流兑付,可选类入关卡域库存经道具使用窗口兑付。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 战斗域奖励层——"奖励是什么、值多少、怎么 roll、怎么入库"的唯一 owner;D2 RewardTable 回填权持有方 | +| 拥有 | 奖励类型封闭枚举与两级交付轴 · RewardTable/RewardConfigTable schema(D2 回填 owner)· **供给控制器机制(RewardSupplyController——install 期战斗首件保证 + 指数递减注入,C3 修订批承接登记)** · install 期身份预分配(reward_roll 封闭流)· 分流/入库/库存查询接口 · 关卡域库存容器 · F1–F6 数值供给 · 失衡预警①–⑧ | +| 不拥有 | 何时兑付/结算顺序/明细行协议(B1)· 棋盘资格事实(A2——零直接接口,经 B1 转化)· buff 时限/叠层/衰减(B3)· 棋盘侧功能道具(A5,垂直切片)· 关卡大奖励/代币/天赋(C2/C4)· 伤害与治疗施加、守卫判定(B1 编排)· 呈现(D1/B5/D4) | +| 上游输入 | D2 表值(RewardTable/RewardConfigTable;StatusEffectTable/HeroTable 仅经 ConfigDB 读值)· B1 资格格清单(roll_board 入参)· A2 资格事实经 B1 转化 | +| 下游输出 | 预分配身份映射(B1 grant/grant_clear 兑付数据面)· 库存投影(B1 窗口数据契约)· F6 当量供给(B1 条款①②③⑤对账输入)· 明细行数据字段(B1 流水,B5/D1 呈现消费) | +| 硬约束 | 零随机出 roll_board 一线 · Ready 后映射只读(无重掷 API)· 零主动事件订阅 · 零 UI 依赖 · 零跨系统读 · 机制与内容分离(内容行 MVP 收窄,名录扩展归垂直切片/Alpha) | + +- 缺位后果:P2"规划的延迟兑现"无兑现物(规划退化为空转);B2 已声明当前内容态敌人不可被击杀——胜利路径判定素材悬空;A4 三条定价硬约束(雾内档位下界 / 清图锚上浮 / 净当量带上界)无承载对象,安全格价值体系无从校准。 +- 玩家的每一轮都在预读它:已开格与库存的奖励身份恒定可读,叠加"每个安全格必有货"(F1 结构保证)与"雾格档位更高"(F2)两条全板先验——构成"往雾区推进、囤刀待机、叠层满载后收手"的路线与节奏决策素材。面向特定道具的寻路("绕开两颗雷去拿那把刀")须以未开格身份可读为前提,MVP 未兑现——结构前提已备(身份 install 期已定、预读查询面就绪、A5 探测零成本桥接),呈现深度 = OQ2(垂直切片);创意风险 #1 维持 OPEN(结构前提落地 ≠ 风险闭合)。 + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P4 地牢住户的遗物 | "我在地牢里淘东西,不是在领系统发放的补偿" | 每个安全格必有货(F1 结构保证);雾区/高危区档位下界更高(F2)——"越险货越好"是风险收益同源在拾取侧的直觉;开格瞬间"这格有货"是本系统的高频情绪节拍 | +| P2 拾取的糖、结算的实 | "先是一份捡到的战利品,其次才是一行结算数据" | 拾取不当场生效——入库后轮次结束在结算明细逐行兑付("治疗包 ×1 入库(第 2 轮 · 安全格)");拾取瞬间的糖与延迟兑现的实各管一半,后者是 P2 归因的奖励侧落点 | +| P1 身份可读 | "这格是增伤——看得见才留得住" | 已开格 + 库存奖励身份恒定可读(install 期预分配、不重掷、不明雾化);"囤刀待机、看准重击拍再用"的库存规划 = 拾取感的长线 MVP 形态 | +| P3 越险货越好 | "危险的地方货更好" | 雾格池 tier ≥ T_fog_min 过滤(A4 三条定价硬约束的体验面)——深度轴 MVP 零载体,深度/图类型差异化 = clear_/池变体结构位(垂直切片登记) | + +| 反向护栏 | 处置 | +|---|---| +| 奖励的随机只发生在桌下(roll 决定这格是什么),身份永远在桌上(开格后恒定可读) | 任何"开格后仍不可读""结算时才揭示身份"的呈现设计默认砍掉——违反 P2 归因 + 掐死拾取感本身 | +| 拾取的糖不得改成即时生效 | 绕过 B1 结算统一兑付即违反 P2 归因(game-concept 定稿口径) | + +- 参考感受:Microsoft Minesweeper Adventure 的开格拾取(身份即在场的战利品,不是无名金币)+ 杀戮尖塔奖励节点(有名字、有用途、进构筑的战利品);反向对照 = 无身份的纯数值掉落("你获得了 +5"式系统补偿)与开包式随机惊喜(拿到了也不知道是啥)——本作要避开的两种感受。 + +## 全局流程 + +### 奖励授予(install 期预分配 → 结算编排兑付) + +``` +board install(B1 编排) + │ ① roll_board(资格格清单 = S ∖ R₀) [B4] 恰一次;消费 reward_roll 封闭流恰一 scope 批 + │ ├ 行主序逐格取号 → 格奖励身份(雾格过 F2 档位过滤) + │ ├ 批尾一号 → 清图身份(F3) + │ └ F5 保底校验不过 → 草稿整批弃用、同 scope 续流重 roll(≤ N_draft 次,超出致命) + │ ② 板级双映射(格→reward_id;清图→reward_id)→ Ready(只读) + ▼ +挖掘期(身份恒定可读;A2 资格事实经 B1 转化——B4 零直接接口) + │ + ▼ +轮次结束 → 结算编排(B1 段序骨架) + │ ③ 授予段:逐格 grant(cell) → 预分配身份快照,按 category 分流 + │ ├ buff 类 → B1 调 B3 apply_effect(满层拒绝 → "奖励未生效"行,零折算) + │ ├ instant_aoe → 当场兑付:全体存活敌各 power 点(不进库存不出窗口) + │ ├ instant_heal → 当场兑付:主角 +power(clamp;满血 → "奖励未生效"行零兑付) + │ └ damage_item / heal_pack → stock() 入关卡域库存 + │ ④ cleared 轮:grant_clear() → 预分配清图身份兑付(分流同上;恒非空) + ▼ +⑤ 道具使用窗口(授予段后、增益/减益段前——玩家至多选用一件或跳过) + ▼ +⑥ 道具段 → ⑦ 战斗段 → ⑧ 衰减段 → ⑨ 判定段 + ▼ +battle_over:retire_mapping(板级映射消亡,不跨图;库存不清——跨战斗延续) +``` + +### 库存生命周期(关卡域) + +``` +stock(item)(B1 编排,道具类 grant 后调用) + │ 逐件实例入库存:{item_id, source{cell, round}, 行值快照} → InStock + ▼ +跨轮次 · 跨扫雷图 · 跨战斗延续(数量无上限;稀缺性由 roll 权重承载) + │ + ▼ +道具使用窗口(每轮结算,授予段后)── 玩家至多选一件:use(handle) + │ InStock → Used(不可逆;零游戏态前置守卫) + ▼ +关卡结束(victory/defeat 皆然;"关卡结束"事件归 C1/C2 定义,上层显式编排) + │ clear_inventory() 恰一次(B1 调用;清空前快照计数 → "随关卡结束清空"反馈行) + ▼ +Cleared(库存失效;此后 grant/stock/use → false + 诊断) +``` + +### 出口分发(结算编排各出口的 B4 接口触点) + +| 时点 | 触发 | B4 接口 | 约束 | +|---|---|---|---| +| board install 后、挖掘前 | B1 编排恰一次 | `roll_board(资格格清单)` | 消费 reward_roll 恰一 scope 批(格批 + 清图批尾);Ready 后再调 → false + 诊断;空清单 → debug 断言中止 | +| 结算授予段 | 每格受理事实 | `grant(cell)` | 返回预分配快照;同格重复受理幂等(同快照 + 诊断);清单外 → false + 诊断 | +| cleared 轮授予段 | cleared 结算 | `grant_clear()` | 恒非空兑付;同批重复调用幂等(同身份 + 诊断) | +| 授予段(道具类) | grant 返回道具类后 | `stock(item)` | 仅受理 damage_item/heal_pack;来源真值归 B1、B4 不校验 | +| 道具使用窗口 | 玩家选用 | `use(handle)` | InStock→Used 不可逆;零游戏态前置守卫;Used 句柄 → false + 诊断 | +| battle_over | 战斗结束 | `retire_mapping()` | 板级映射消亡(下一场战斗新板新批);库存不清 | +| 关卡结束 | 上层显式编排 | `clear_inventory()` | 恰一次;重复 → false + 诊断(幂等守卫) | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:奖励只有五种,三种当场生效、两种进背包 + +**人话**:奖励系统发出去的一切东西只从五种类型里出,数据表里出现别的类型直接拒绝加载。三种"即时类"在发奖那一刻当场兑现——不进背包、不用玩家选;两种"可选类"先放进关卡背包,由玩家在结算窗口里自己挑时机用,数量少但更强。想加第六种类型属于改规则,走本文档修订流程,不是调参数。 + +| 类型 | 玩家拿到什么 | 怎么交付 | +|---|---|---| +| buff | 一层临时增益(增伤/减伤) | 当场挂进状态系统,当次结算内生效 | +| instant_aoe | 群体攻击 | 当场对全体存活敌人各打 power 点 | +| instant_heal | 小额即时回血 | 当场给主角回血(不超血量上限) | +| damage_item | 伤害道具(刀) | 进背包,窗口选用、道具段按固定顺位打敌 | +| heal_pack | 强效治疗包 | 进背包,窗口选用回血 | + +**举证**:每条奖励 = RewardTable 一行(九字段见「枚举」节);buff 类必填 B3 effect_id 精确引用——首个引用行回填时激活 B3 悬空校验 + 条件性夹具(dangling_ref_policy 兑现);加载校验即时类 power ≥ 1 同道具类;预留值(resource / aux_item 等)校验拒绝(fail-fast),启用走修订;默认根权重即时类 75% / 可选类 25%(注入批后由注入机制承载——见「枚举」节注记)。 + +#### 规则 2:每张图装好那一刻,每格的奖励就全部定死 + +**人话**:战斗系统把一张新扫雷图装好后、玩家挖第一下之前,结算编排(B1)调用奖励系统办一次"摇号":按固定顺序给这张图上所有够格出奖励的格子(开局已翻开的安全格除外)逐一摇出各自的奖励,同一次还摇出清图奖励,结果写进这张图的映射表。此后这张图活多久,这些身份就定多久——同图跨轮不变;换图或战斗结束,映射随之消亡。随机只发生在这一次摇号里:之后发奖、用道具都不再掷任何骰子,道具威力就是表里写死的数值。 + +**举证**:零随机出 roll_board 一线——唯一随机入口 = roll_board 内消费 reward_roll 封闭流恰一 scope 批;registry `rng_service.scope_event_binding` 语义 = 每次 board install(格奖励批 + 清图奖励批),registry/D2 已同步;选行算子、雾格档位过滤、清图批尾、保底重摇见 F1/F2/F3/F5;执行细节见「规则 2 的实现参照——install 期预分配」。 + +#### 规则 3:发奖时按类型走三条路,各有各的去处 + +**人话**:轮次结束结算时,结算编排(B1)对每个捡到奖励的格子来领一次奖,奖励系统交出早已定好的身份,再按类型分路:增益类由 B1 拿去找状态系统挂上;两种即时类当场兑现——群伤当场结算、回血当场加上,不进背包、不弹选择窗口;道具类放进关卡背包。增益挂满层挂不进去时,这份奖励不予兑付、不给任何折算补偿,只在结算明细里明写一行"奖励未生效"——不静默蒸发。 + +**举证**:分流全表见「规则 1/3 的实现参照——两级奖励分流」;即时类与道具类的效果算式见 F4;受理恰一次归 B1(对齐 B2 义务②/B3 EC6 先例),同格重复 grant = 幂等受理(同一快照 + debug 诊断,E18);满层拒绝协议同 B3 义务⑧;B4 与 B3/A2 零直接接口。 + +#### 规则 4:背包活整个关卡,战斗结束不清空 + +**人话**:道具背包属于整个关卡,不属于某一场战斗:换了地图、打完一场战斗,包里的东西都还在,直到整个关卡打完(无论输赢)才由结算编排一次性清空。每一件都记着从哪个格子的哪一轮捡到,结算能追溯来源。数量没有上限——稀缺感靠掉落权重控制,不靠背包容量。设计意图:可选类道具是"关内资产",囤着一把刀等整个关卡里最好的时机,而不是"这场不花就没了"。 + +**举证**:无计算。库存生命周期流程见「全局流程·库存生命周期」;关卡末清空恰一次 + 幂等守卫见 E7;战斗结束只退役板级映射、不清库存见 E2/E6 与 B1 义务⑤。 + +#### 规则 5:每轮结算,战斗开始前玩家能用一件背包道具 + +**人话**:每轮挖完,结算按固定顺序走:先发放这轮捡到的全部奖励,然后弹出道具使用窗口——玩家至多选一件背包道具用掉,或者跳过;之后才轮到增益减益、道具生效、战斗、衰减、判胜负。窗口一次只能用一件(想一次用多件属于改规则,不是调参数)。顺序本身藏着战术:道具段排在战斗段之前,这轮刚捡的刀(伤害道具的项目行话,全文同义)可以先削血、把敌人打进"死亡即不动手"——"重击拍前用刀"由此成立。 + +| 结算顺序 | 这一步发生什么 | +|---|---| +| ① 授予段 | 逐一发放本轮奖励:清图奖励兑付、即时类当场兑现、道具类入背包 | +| ② 道具使用窗口 | 玩家至多选一件背包道具,或跳过 | +| ③ 增益/减益段 | buff/debuff 结算(核心序:增益减益 > 道具 > 战斗) | +| ④ 道具段 | 窗口选用的那一件生效:伤害道具按固定顺位打敌、治疗包给主角回血;一次性消耗、用过不可逆 | +| ⑤ 战斗段 | 敌我交战 | +| ⑥ 衰减段 | 增益衰减 | +| ⑦ 判定段 | 判定胜负 | + +**举证**:段序骨架与窗口调用序归 B1(B1 义务①——窗口选择序列登记为结算回放输入事件);窗口呈现归 D1;载荷与守卫细则见「规则 5/7 的实现参照——道具使用窗口」;伤害道具固定顺位 = B2 Rule 7 默认目标。无计算。 + +#### 规则 6:清完一张图,必有一份清图奖励 + +**人话**:玩家把一张图的雷全部清完时,结算会给一份清图奖励。这份奖励的身份早在图装上桌时就与格子奖励同批摇好,此时按类型走同样的三条路兑付。它永远不会落空——数据加载时就强制保证清图奖池里至少有一行有实际价值的内容,"清图奖励 > 0"是定价约束钉死的。按地形/图类型换不同奖励池是预留插槽,MVP 只有一个默认槽;同一张图类型不变,清图奖励不重摇。 + +**举证**:roll 算式与恒非空的加载校验见 F3;兑付分流同规则 3(见「规则 1/3 的实现参照——两级奖励分流」);clear_ 行命名空间预留、校验拒绝(A1 elite_/boss_ 同款);A3 条款④ = 定价对账锚(清图奖励 > 0);图类型战斗域恒定、不逐图重掷(install 批次承载)。 + +#### 规则 7:治疗分两档——小额当场回,大包留着挑时机 + +**人话**:治疗有两种形态。即时回血随开格当场给主角加血:量小、常见、不用玩家做任何选择;血满时这份奖励等于白捡,结算明细里明写"奖励未生效"。治疗包则进背包、在窗口里挑时机用:稀有且一次回得多——治疗整体供给变少、决策价值变高,"治疗是稀缺奖励"。血满时用治疗包会被拒绝且不消耗(守卫由结算编排读主角血量判定,奖励系统自己不读别家数据);满血禁选的预判由 B1 组装进窗口数据,供界面禁用/提示。 + +| 治疗形态 | 怎么给 | 特点 | +|---|---|---| +| 即时回血 instant_heal | 开格当场给主角回 power 点(不超上限) | 小额常见、零决策、不进背包;满血 → "奖励未生效"行零兑付 | +| 治疗包 heal_pack | 进背包,窗口选用时回复 | 稀缺且更强;满血使用 → 拒绝、不烧包 | + +**举证**:回血算式 hp' = min(hp + power, max_hp)(clamp)见 F4;heal_pack 默认根权重 6%、power 12(provisional 归 C3);usable_now 可用性预判由 B1 编排组装进窗口数据(义务④)供 D1 禁选/提示;守卫归 B1 编排读 B2 hero hp 判定——B4 零跨系统读。 + +#### 规则 8:开过的格子和背包里的奖励身份永远可查、不变卦 + +**人话**:玩家任何时候都能查到两样东西:已经挖开的格子出的是什么、背包里每件道具是什么。这不是查询时现算的——身份在图装上桌时就已定死,同一张图里永远读到同一个答案。没挖开的格子当前版本不显示里面是什么(虽然结构上已定、也查得到,留给探测类道具桥接与后续呈现决策)。映射就绪后就是只读的:不存在任何"重摇"接口,谁加一个重摇入口谁就是在改接口。 + +**举证**:预读查询面 = query_identity(引用级投影,Ready 后只读);未开格身份 MVP 不呈现、呈现深度 = OQ2(A5 探测道具零成本桥接,OQ 登记);任何重掷入口 = 接口漂移(走 GDD 修订非调参)。 + +#### 规则 9:奖励的内容和参数全部写在两张数据表里 + +**人话**:每条奖励是什么、值多少、多强,全部来自一张奖励内容表——改奖励就是改表行,不用动代码;表有九个字段。另有一张单行全局参数表,装本系统的四个调节旋钮:雾区档位下界、保底刀数、重摇上限、供给闸刀力下界。两张表都由 D2 建表、本 GDD 负责把内容回填进去;奖励内容表是"定义表"而非"进度成长表",主键是 reward_id。 + +**举证**:九字段 schema 见「枚举·RewardTable schema」;表身份声明 = 定义表(非进度成长表),Rule 4 豁免、键 = reward_id(B2/B3 同款声明纪律);回填走 D2 接口漂移规则回写;RewardConfigTable = 单行全局(HeroTable 单行全局先例),承载 T_fog_min / K_dmg / N_draft / P_min 四配置值。 + +#### 规则 10:奖励系统自己不主动做任何事 + +**人话**:奖励系统不订阅任何事件、不画任何界面、除了开局那次摇号不用任何随机数。它像一个仓库加账本:把"每个格子是什么奖励、背包里有什么"的查询口,和"发奖、入库、消耗"的操作口摆在那里;什么时候调、按什么顺序调,全部由结算编排(B1)说了算。棋盘那边发生了什么,奖励系统从不直接看——事实都经 B1 转化后递过来。 + +**举证**:无计算。机器验收 = 静态扫描零随机调用/零 UI 基类/零事件订阅/零跨系统 API 引用(AC-21);调用权集中归 B1 结算编排(B3"授予集中在编排"同款);A2 事件面事实经 B1 转化;reward_roll 流封闭——零随机出 roll_board 一线(rng_service reward_roll 流)。 + +- 实现口径注:奖励容器/道具库存的类结构与事件载体不在本 GDD——实现期 ADR 域。 + +### 规则 2 的实现参照——install 期预分配 + +| 阶段 | 行为 | 守卫 | +|---|---|---| +| 输入 | B1 供资格格清单(S ∖ R₀——initial/mine 通道恒不 eligible,install 期可精确枚举) | 空清单 → debug 断言中止(E1 兜底);清单含重复格 → 去重 + debug 诊断 | +| roll | 行主序逐格取 reward_roll 一号 → 身份;雾格过 F2 档位过滤;批尾一号 → 清图身份(F3) | 流 scope = 每次 board install 恰一批;消费序与恰一次归 B1 | +| 保底 | F5 断言 \|{damage_item 格}\| ≥ K_dmg | 不过 → 草稿整批弃用、同 scope 续流重 roll(草稿弃用连清图一号一并弃用);超 N_draft → 致命 | +| 提交 | 双映射(格→reward_id、清图→reward_id)→ Ready | Ready 后只读(无重掷 API);Ready 相位再调 → false + 诊断(E5) | + +### 规则 1/3 的实现参照——两级奖励分流 + +| category | 交付类 | 兑付方式 | 拒绝语义 | +|---|---|---|---| +| buff | 即时 | B1 调 B3 `apply_effect`(入状态容器,结算段生效;时长 = 当次结算) | 满层 → "奖励未生效"行(可聚合计数),无折算 | +| instant_aoe | 即时 | 授予段当场:全体存活敌各 power 点(逐个 apply_damage、受击即死;恒定表值) | 零存活敌 → "奖励未生效"行零兑付 | +| instant_heal | 即时 | 授予段当场:主角 +power(clamp) | 满血 → "奖励未生效"行零兑付(不进库存无消耗语义) | +| damage_item | 可选 | `stock()` 入关卡域库存 → 窗口选用时兑付(道具段固定顺位打敌) | — | +| heal_pack | 可选 | `stock()` 入关卡域库存 → 窗口选用时兑付 | 满 HP → B1 守卫拒绝(不烧包;usable_now 禁选/提示) | + +### 规则 5/7 的实现参照——道具使用窗口 + +| 面 | 语义 | +|---|---| +| 时机 | 授予段之后、增益/减益段之前——当轮拾取的道具当轮即可用(末图清图奖励同结算窗口可选用) | +| 载荷 | B1 编排组装:`query_inventory` 逐件实例(item_id + 来源 {cell, round} + 行值快照投影 + 状态;枚举序 = stock 受理序)+ B1 侧守卫预判 usable_now(满血治疗包禁选/提示——B4 零跨系统读不变) | +| 选择 | 至多选用一件或跳过;选择序列 = 结算回放输入事件(B3 可回放契约的窗口侧兑现);窗口期选择收集与棋盘输入不竞争(A2 单意图帧先例) | +| 消耗 | 一次性 Used 不可逆;零效果判定(施加时无存活敌)归 B1 读 B2 存活集 → "已消耗未生效"行;B4 `use` 零游戏态前置守卫(存活集/血量对 B4 不可见) | +| 空库存 | 零使用恒合法(跳过恒合法);呈现归 D1(可整体不弹窗——P4 节奏;窗口触发政策:MVP 有库存恒弹,候选"仅可用品触发/常驻库存条非模态"归垂直切片) | + +## 枚举 + +### 奖励 category(封闭枚举,五值) + +| 值 | 交付类 | 字段消费 | 产出点 | 语义 | +|---|---|---|---|---| +| buff | 即时 | effect_id(必填);power 不消费——非校验项 | 授予段 | B3 状态容器行授予(B4 只定"给哪行",层数语义归 B3);时长 = 当次结算(attack_up/defense_up D=1) | +| damage_item | 可选 | power ≥ 1 | 授予段入库 → 道具段 | 可囤可择时的规划面伤害("刀")——基础攻击之外的增量;固定顺位打敌 | +| heal_pack | 可选 | power ≥ 1 | 授予段入库 → 道具段 | 稀缺强治疗(可选治疗唯一形态);满 HP 拒绝 | +| instant_aoe | 即时 | power ≥ 1 | 授予段当场 | 群体攻击:全体存活敌各 power 点 | +| instant_heal | 即时 | power ≥ 1 | 授予段当场 | 主角小额即时回血(clamp) | + +- 预留值:`resource` / `aux_item`(A5 桥接)等 → 校验拒绝;启用走本 GDD 修订(fail-fast,B2 pattern 预留先例)。 + +### RewardTable schema(D2 新表,B4 回填 owner) + +| 字段 | 类型/范围 | 说明 | +|---|---|---| +| reward_id | String(表内主键) | 实体键精确匹配(D2 Rule 4) | +| category | enum {buff, damage_item, heal_pack, instant_aoe, instant_heal} | MVP 校验域(上表五值);预留值拒绝(Rule 1) | +| effect_id | String(可空) | buff 类必填——B3 StatusEffectTable 精确引用;非 buff 类恒空 | +| display_name | String(非空) | 玩家可读名(D1 义务①/窗口呈现数据源;本地化归 D6——B2 EnemyTable/B3 StatusEffectTable 同库惯例) | +| power | int [0, 999] | 伤害道具伤害值 / 治疗包回复值 / 即时类数值(knob);buff 类不消费——非校验项(B2 heavy_multiplier 先例) | +| weight | int [0, 999] | 所在池内 roll 权重(knob);0 = 该池不出此行(合法极端) | +| pool_tag | enum {safe_cell, clear_default}(+ clear_ 预留) | roll 池分组——格奖励池 / 清图默认槽;雾区档位过滤谓词见 F2 | +| tier | int [1, 8] | 强度档位(A4 雾内档位下界约束的载体;knob) | +| use_timing | enum {pre_settlement} | 使用时机(MVP 唯一值;game-concept 预留字段的兑现——扩枚举走修订) | + +### 默认根内容行(值 provisional 归 C3;溯源 = 原型 v3 锚 HERO_MAX_HP=50 + 概念原型 HERO_BASE_ATK=6) + +| reward_id | category | power | weight | tier | pool_tag | 说明 | +|---|---|---|---|---|---|---| +| defense_up_basic | buff | —(不消费) | 16 | 2 | safe_cell | 当次受伤减免(B3 defense_up D=1)——减伤墙批 24→16(attack_up 升池内最高行) | +| attack_up_basic | buff | —(不消费) | 20 | 2 | safe_cell | 当次主角攻击增伤(B3 attack_up D=1——复活后首批入池) | +| spark_aoe | instant_aoe | 4 | 16 | 1 | safe_cell | 群体攻击:全体存活敌各 4 点 | +| heal_minor | instant_heal | 4 | 15 | 1 | safe_cell | 即时小额回血(满血零兑付行) | +| knife_t1 | damage_item | 8 | 60 | 1 | active_inject | 可囤可择时的基础刀(供给控制器注入池) | +| knife_adv | damage_item | 16 | 30 | 2 | active_inject | 雾档升级刀(注入池) | +| heal_pack | heal_pack | 12 | 10 | 2 | active_inject | 稀缺强治疗(注入池——可选治疗唯一形态) | +| clear_blast | instant_aoe | 40 | 1 | 2 | clear_default | 清障炸药:清图槽当场兑付全体存活敌,不入库不占可选类供给(完美板 ×2 = 80——原 clear_knife 入库刀由本行换位替代) | + +- 归一占比(结构变更随 C3 修订批转正):格子池四行即时类合计 67;可选类三行移出格子权重池 → **active_inject 注入池**(供给控制器 install 期注入:战斗首件保证 + 指数递减 0.4→0.2→0.1→…,E[战斗获取] ≈ 1 标定目标)——原"格子权重 75/25 两类占比"由注入机制替代承载;治疗 = heal_minor(格子池)+ heal_pack(注入 10%)。 +- tier_min_buff = 2;T_fog_min = 2 → 雾池 = {defense_up_basic, attack_up_basic, knife_adv, heal_pack}(四行,非单一化 ✓)。 +- AC-19 对账对 = (knife_adv, attack_up_basic)(雾池最低活档行 ≠ tier_min_buff 行——非平凡判别保持)。 + +## 数值结论与推导 + +> 章节纪律:结论先行——先一行加粗结论,下接「推导」给公式代码块、Variables 表与验算/边界表。除 F1/F3/F5 消费 reward_roll 封闭流外全部公式零随机;数值默认全部 provisional 归 C3。术语:资格格 = 开出时 reward_eligible=true 的格(A2 事件面:chain/dig 通道的安全格)。 + +### F1 身份 roll(weighted_pick,install 期执行) + +**结论**:每资格格恰一身份、清图槽恰一身份——weighted_pick 累积区间选行、0 权行取号前剔除、单行池退化合法;Ready 后映射只读。 + +#### 推导 + +``` +资格格集 = S ∖ R₀(安全格减初始展开域——initial/mine 通道恒不 eligible,install 期可精确枚举) +行主序逐格取 reward_roll 一号 r ∈ [0, W):身份 = 累积权重区间 [Σw_{0} ∩(雾格 ? {tier ≥ T_fog_min} : 全体);weight=0 行取号前剔除 +批尾取一号 roll 清图身份(F3;批尾 = 同一批取号中最后消费的一个号——registry terminology 已登记) +→ 双映射提交 Ready +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 取号 | r | int | [0, W) | reward_roll 流单号(F1/F3/F5 消费) | +| 权重 | w | int | [0, 999] | RewardTable.weight;0 = 不入池(取号前剔除) | +| 雾档门限 | T_fog_min | int | [1, 8] | F2;RewardConfigTable 配置值 | +| 资格格集 | S ∖ R₀ | 格集合 | 板级 | B1 供清单(义务⑥) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Example | 三行 w=2/1/1,W=4,r=3 → 第三行(区间 [3,4)) | +| 池空裁定 | 过滤后零行 = 结构性不可达——D2 加载期校验致命(每 pool_tag × 雾/非雾两态各 ≥1 行 weight>0);身份 install 期预分配、授予期零随机零回退,运行时身份缺口无合法语义;单行池退化为恒等选取(合法极端) | +| Output Range | 每资格格恰一身份、清图槽恰一身份;Ready 后映射只读 | +| 边界 | 流 scope = 每次 board install 恰一批(registry 绑定改写义务已兑现,Rule 2);消费序与恰一次归 B1;Ready 后只读(无重掷 API——F5 草稿重 roll 发生在提交前,不变式零触碰) | + +### F2 雾区档位过滤(fog_tier_gate) + +**结论**:雾格池 = {tier ≥ T_fog_min}、非雾格池 = 全 tier(谓词恒真、零分支);T_fog_min = RewardConfigTable 表值(默认 2 provisional);T_fog_min=1 → 雾池=全池(过滤退化合法,A4 约束①仅剩锚侧兜底);=8 → 仅最高档(非空由 F1 校验保证)。 + +#### 推导 + +``` +雾格池 = {tier ≥ T_fog_min};非雾格池 = 全 tier(谓词恒真、零分支) +``` + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| 验收锚性质 | 推导式(= 最低档 buff 当量所在档)依赖 B1 对照表(A3 条款①,未设计)、加载期循环依赖不可行——T_fog_min 取表值;A4 硬约束①(雾内档位下界 ≥ 最低档安全格 buff 当量)登记为验收锚非运行时校验——对照表落定/调参时核对"雾池最低档行当量 ≥ 最低档安全格 buff 当量" | +| AC-19 序数检查的已知失效面 | tier 与当量零耦合(跨类别 tier 序不保证当量序——雾池 tier2 治疗包的当量可能高于 tier3 buff 行),故 AC-19 只作档位序检查非当量保证,升格运行时致命会锁死内容;单行雾池下检查恒平凡真(判别力零)——默认根已布置四行雾池规避 | +| Example | T_fog_min=3 时雾格恒不出 tier≤2 行 | +| 边界 | 链开雾格同雾格待遇(A2 EC4);参数归 C3 | + +### F3 清图奖励 roll(clear_reward_roll) + +**结论**:槽谓词 = clear_(板 biome 匹配 ∧ 池非空)否则 clear_default(MVP:clear_ 行校验拒绝 → 恒退化 clear_default,结构位保留);恒非空的可判定形式 = 恒有正当量行(power=0 道具行被拒——A3 条款④"清图奖励>0"的表侧兑现);空池致命(同 F1 裁定)。 + +#### 推导 + +``` +槽谓词 = clear_(板 biome 匹配 ∧ 池非空)否则 clear_default +MVP:clear_ 行校验拒绝 → 恒退化 clear_default(结构位保留) +恒非空 = 加载期校验:clear_default 池 ≥1 行 weight>0 ∧(buff 行 effect_id 非空 ∨ 道具行 power ≥ 1) +roll = install 批尾一号,选行算子同 F1 +``` + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Example | 命中 heal_pack(power=3) → cleared 轮 grant_clear 入库存 | +| 边界 | roll 在 install 批、兑付在 cleared 结算编排期(零随机出 roll_board 一线);图类型战斗域恒定、不逐图重掷 | + +### F4 道具段与即时类效果式(item_and_instant_effects) + +**结论**:道具伤害恒定表值不吃任何 percent 修正;治疗 clamp hp' = min(hp + power, max_hp);满血拒绝/零兑付拦在消耗前;attack_up 复活——唯一消费面 = 主角战斗段先手攻击(只乘基础攻击、不乘刀)。 + +#### 推导 + +``` +伤害道具(可选类,道具段):D = power(恒定表值——不吃任何 percent 修正) + 目标 = 固定顺位最前存活敌(B2 Rule 7),一次性消耗 +治疗包(可选类,道具段):可用 ⟺ hp < max_hp(满 → 拒绝、不消耗);hp' = min(hp + power, max_hp) +即时群伤 instant_aoe(即时类,授予段当场):对全体存活敌各 D = power(逐个 apply_damage、受击即死); + 零存活敌 → 零兑付"奖励未生效"行(结构性难达——授予段先于道具/战斗段,敌集空仅剩防御分支) +即时回血 instant_heal(即时类,授予段当场):可用 ⟺ hp < max_hp(满 → 零兑付"奖励未生效"行,不进库存无消耗语义); + hp' = min(hp + power, max_hp) +buff 类(即时类,授予段当场):B3 apply_effect;时长 = 当次结算(attack_up/defense_up D=1—— + buff 兑付窗口 = 授予段至本结算衰减段) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 道具威力 | power | int | [1, 999] | RewardTable.power(category ∈ {damage_item, heal_pack} ⇒ power ≥ 1 加载校验);恒定表值非 roll 值 | +| 主角血量 | hp / max_hp | int | [0, 999] | B2 查询(B1 编排读值做守卫——B4 零跨系统读) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| attack_up 口径 | HeroTable.atk 基础攻击恒存在(域 [1,999]、0 加载拒绝——B2 Rule 9)→ attack_up(+50%)复活:唯一消费面 = 主角战斗段先手攻击(伤害算式归 B1 战斗段);"道具不吃修正"拍板不变——attack_up 只乘基础攻击、不乘刀;是否入默认根池随奖励改造批裁决(C3 内容所有权,复活批不擅自入池——OQ1);A3 条款 2 锚参照对象 = attack_up 当量(B3 Interactions 原文即 attack_up;换算归 B1 条款①对照表——B4 只供行值清单;B3 锚预警裁决前置不变) | +| 弱化面排除 | weaken/defense_up 不入本式(敌输出侧/主角承伤侧,B2/B1 域);读值时点 = 道具段、施加归 B1(B4 零跨系统读) | +| Output Range | D = power(恒定);hp' ∈ [hp, max_hp](clamp 式上界——满血分支被拒绝守卫拦在消耗前) | +| Example | power=6 伤害道具 → 顺位最前敌 −6;重击拍前用刀击杀 → 该敌重击永不兑现(B2 死亡即不动手的道具形态);满血(hp=max_hp)用治疗包 → 拒绝、库存不减 | +| 边界 | 取整无涉(全整数);使用守卫(满血)归 B1 编排(义务④);hp=0 在窗口时点结构不可达(败判定在判定段、先于本窗口不存在;上一次结算的判定段保证到本窗口时 hero 存活)——不定义复活语义 | + +### F5 伤害道具保底(damage_item_floor) + +**结论**:每板可选伤害保底 \|{c ∈ 资格格 | 身份(c).category = damage_item}\| ≥ K_dmg——执行 = roll_board 内"草稿-校验-提交",不过则同 scope 续流重 roll、超 N_draft 致命;K_dmg=0 = 断言关闭(**现行默认**——刀保底语义由供给控制器战斗首件保证取代,no_damage_row 后闸托底零活刀行世界);基础攻击在场后本保底为内容健康护栏非胜利路径闸。 + +#### 推导 + +``` +断言 |{c ∈ 资格格 | 身份(c).category = damage_item}| ≥ K_dmg + (RewardConfigTable [0,999],默认 1 provisional) +执行 = roll_board 内"草稿-校验-提交":不过 → 草稿整批弃用、同 scope 续消费流重 roll(全序确定、可回放) +超 N_draft 次(RewardConfigTable,默认 32 provisional)→ 致命(权重配置错误,fail-fast) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 保底下界 | K_dmg | int | [0, 999] | RewardConfigTable 配置;**默认 0**(保底断言关闭——语义由供给控制器战斗首件保证取代,原默认 1 与禁区条款随 C3 修订批解除);仅计 damage_item(保底对象 = 可选类伤害——玩家可控时点的伤害资源;即时类 instant_aoe 不计,其自动兑付无规划面——保底的意义在"每板至少有一把可囤可择时的刀") | +| 重 roll 上限 | N_draft | int | [0, 999] | RewardConfigTable 配置;默认 32 | +| 供给闸刀力下界 | P_min | int | [1, 999] | RewardConfigTable 配置;默认 8 = 默认根最低刀 power | + +**加载期护栏(两条新致命——关闭"保底全绿、游戏不可胜"的威力空洞与零伤害世界后门):** + +| 护栏 | 断言 | manifest 行(根) | +|---|---|---| +| (i) 供给闸 | K_dmg ≥ 1 ⇒ safe_cell 池存在 power ≥ P_min 的活 damage_item 行 | `floor_supply_missing`(根 `b4_floor_supply`) | +| (ii) 零伤害后闸 | K_dmg = 0(断言关闭)⇒ safe_cell ∪ clear_default 仍须含 ≥1 活 damage_item 行(无条件后闸——内容健康护栏、非胜利路径闸:基础攻击保证战斗可胜,本闸防"零伤害道具世界"经纯内容调参静默复活导致战斗拖长、奖励系统空转;与 B2 OQ9 并案登记同一 owner) | `no_damage_row`(根 `b4_nodamage`) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Example | 12 格板首稿 0 件刀 → 弃稿重 roll,次稿 1 件 → 提交 Ready | +| Output Range | K_dmg=0 = 断言关闭(**现行默认**——胜利路径无刀保底,由供给控制器首件保证与基础攻击承载;no_damage_row 后闸托底);\|资格格\| < K_dmg 恒不过 → 重 roll 耗尽致命,暴露配置错误 | +| 概率性致命带注记 | 合法权重下 N_draft 耗尽概率随 K/池构成可达非平凡值(同配置不同种子一个能玩一个崩——种子轮盘);调参期批量仿真(刀供给分布 × A1 板分布的可行率)登记为 C3 调参流程义务(缺口预检),把配置错误暴露在调参台而非玩家局内 | +| 与 Ready 只读不冲突 | 重 roll 全部发生在提交前(Unrolled 态);确定性 = 同 scope 消费轨迹的确定函数 | +| 否决案存档 | 清图槽保底(弃图流覆盖不到——RAP 刷图非必经清图;economy-designer 复核:清图免费换板且多清图槽,吞吐严格支配 RAP 刷新,无刷刀农场漏洞);safe_cell 伤害权重下界(概率缓解非结构保证,留作 C3 伴生调参) | +| 边界 | 断言仅计格奖励身份(清图槽不计);归 B4 自身,B1 仅编排恰一次调用 | + +### F6 当量换算供给(equivalence_supply,A3 条款①② 的 B4 侧输入) + +**结论**:供给 = RewardTable 全活行投影 + tier_min_buff(行集空 = F1 校验对偶致命);量纲建议 = 主角 HP 点;伤害道具当量 = power(恒定表值直接可算);单一线性换算与条款 1/2/3/5 的闭合归 B1 单一 owner,本式只供原材料与量纲建议。 + +#### 推导 + +``` +供给 = RewardTable 全活行投影 + (safe_cell ∪ clear_default 逐行 category/display_name/power/tier/effect_id/weight) +tier_min_buff = min{tier | safe_cell ∧ category=buff ∧ weight>0}(行集空 = F1 校验对偶致命) +量纲建议 = 主角 HP 点(与 m_amb 同量纲);伤害道具当量 = power(恒定表值直接可算) +``` + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| 供给面口径 | 全活行投影(非仅活 buff 行清单)——承载 A4 约束②③与 A3 条款⑤对账,道具/清图行值在内(B1 直读 ConfigDB 本也可绕过,接口名实相符) | +| 衔接注记(B2 OQ3) | 伤害道具作为主角规划面伤害令"安全格拾取 → 伤害当量"可落地(基础攻击为恒定基底、道具为可规划增量——部分缓解非闭合,窗口使用次数是战斗轨迹量归 B1) | +| 默认根 buff 活行 | defense_up_basic / attack_up_basic(attack_up 复活后首批入池——F4 复活口径;是否维持入池随奖励改造批,OQ1) | +| Output Range | tier_min_buff ∈ [1, 8];量纲仅为建议(B3 锚预警"瞬时 vs 总暴露"归一口径裁决前置,B1 可另选) | +| 边界 | 行值 provisional 归 C3;锚验收权 B4/B1(B3 Rule 10 同款) | + +### 方向预检(B2 clause3_supply 先例,登记对账记录) + +| 项 | 读数 | +|---|---| +| 刀供给期望 | ≈ \|资格格\| × 19% × 9.7(加权刀力 = (15×8+4×16)/19 ≈ 9.7)≈ 1.8\|资格格\|/板,另加即时群伤与基础攻击分量 | +| 击杀需求 | 敌 hp 18(单敌档)/ 54(3 敌档) | +| 预警联动 | 速杀退化预警⑥在"基础攻击 6/结算 + 即时类自动兑付"下压力进一步上升——敌 hp 曲线与奖励供给禁止独立调参(C3 联动重算义务) | +| 输入缺口 | \|资格格\| 分布待 A1 actuals p10/p50 → 落地后重跑;max_hp = 50(原型锚)为治疗供给对照基准 | + +## 状态 + +| 容器/条目 | 状态 | 进入 | 出口 | +|---|---|---|---| +| 板级奖励映射 | Unrolled → Ready → Retired | install 后 roll_board 恰一次 → Ready;Ready 期间只读 | retire_board/换图 → Retired(消亡,不跨图) | +| 库存条目 | InStock → Used → Cleared | stock 授予 → InStock;窗口选用消耗 → Used(不可逆) | 关卡结束 → Cleared(clear_inventory——库存跨战斗延续后于关卡末清空;battle_over 只退役映射不清库存) | + +不变式:Ready 后映射零写入;Used 不可逆;库存条目仅存在于关卡域(跨战斗延续)。违规 = debug 断言中止(进程致命保留给 D2 数据通道,B2/B3 同款)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| 资格格集为空(S ∖ R₀ = ∅) | 三层防线——① A1 生产面 R₀ ≠ S 合法性闸(修订义务已登记、未落地);② A2 install 不变式④现行已生效(R₀=S 板装板即拒,B4 在其后序恒见非空集);③ B4 roll_board 致命(fail-fast 兜底,对偶于 F1 池空裁定)。B4 零静默回退 | E1 | +| 战斗结束时仍有未开资格格 | 其预分配身份随映射 Retired 消亡——零授予、零明细行(未拾取 ≠ 溢出:从未发生的事不进结算,非静默蒸发) | E2 | +| 同一结算多格授予同类 buff 超过 max_stack(连锁批量 13 格链 × max_stack=2 ⇒ 大量同文"奖励未生效"明细行) | 按规范序逐格受理,满层起 apply_effect 返回 false → 各产一行"奖励未生效"明细(零兑付、无折算——显式接受;A2 登记义务"连锁批量授予溢出吸收"的 B4 侧裁决落点);道具类无满层概念(库存无上限);玩家侧缓解 = 叠层感知挖掘(HUD 满层后少开安全格/优先挖刀——弱自救;层数长在 B3 容器上不长在格子上,未开格身份不呈现、无格可绕);结构位 = 未开格身份呈现深度(OQ2/A5,垂直切片);明细行数据面:未生效类行携带可聚合的计数/批次字段(同类聚合计数、来源批次——是否合并呈现归 B1/B5 裁决,数据必须可聚合,防事后补字段 = 接口漂移);呈现归 D1 义务③,行协议 owner = B3 义务⑧ | E3 | +| roll_board 后映射 Ready、B1 未调而挖掘已开始 | grant 在 Unrolled 前/Retired 后调用 → false + 诊断(受理/拒绝口径,非进程致命——编排违约显式暴露,B2 同款) | E4 | +| roll_board 被二次调用 | Ready 相位再调 → false + 诊断(相位守卫,B2 AC-1 同款) | E5 | +| 换图(retire + install)发生在战斗中 | 旧映射 Retired(未拾取身份消亡)、新图 install 后 roll 新批(新图新身份 = reward_roll 流新 scope 批,与 board_gen"每次生成新图"同构);库存不受影响(关卡域跨图延续,Rule 4) | E6 | +| 关卡结束(clear_inventory)后仍调 grant/stock/use | Cleared 相位 → false + 诊断;clear_inventory 由 B1 编排于关卡末恰一次(重复调用 false + 诊断,幂等守卫) | E7 | +| 道具使用窗口打开时库存为空 | 零使用恒合法(跳过恒合法);呈现归 D1(可整体不弹窗——P4 节奏,B4 建议登记;窗口触发政策见 D1 义务②——MVP 有库存恒弹,候选"仅可用品触发/常驻库存条"归垂直切片) | E8 | +| 同一件道具被选用两次 | `use` 对已 Used 实例 → false + 诊断(Used 不可逆);单帧选择收集口径归 D1(义务②) | E9 | +| 道具段中伤害道具施加时已无存活敌(前序刀已全灭) | 照常消耗——标记 Used、零效果,逐件进结算明细"已消耗未生效"行(B3 义务⑧"溢出未生效"行协议同款数据面)。现行理由两条:① 守卫所有权不对称(非信息不对称)——满血守卫是 B1 读 hero hp 的表局部判定(F4 已定),而零效果判定须读 B2 存活集,B4 零跨系统读纪律不为它开口子,选定即承诺把战斗轨迹判断留给编排层;② 反抖动——选定即承诺防窗口内反复重选拖垮结算节奏(B2 呈现规格供给 hp/max_hp 血条数字,敌我血量在选择时点均可读;窗口内可用性预判 usable_now〔B1 组装〕承载"误触防护"半句)。段序不截断:选定件全序执行 → 战斗段照常推进(死敌"死亡即不动手"空转)→ 衰减段 → 判定段判胜 → 关卡末 clear_inventory 全清(含未选件——D1 义务⑤清空反馈行);"零效果"事实判定归 B1 编排读 B2 存活集(读值做判定,非拒绝);监控锚 = 预警①(浪费半) | E10 | +| 清图槽身份为 buff 行且授予时已满层 | 零兑付 + "奖励未生效"明细行(Rule 3 同款协议;清图槽允许 buff 行入 clear_default 池);A3 条款④"清图奖励>0"的兑现口径 = 行级恒有正当量(F3 加载期校验:power≥1 ∨ effect_id 非空),运行时满层拒绝为合法例外、P2 诚实由明细行承载;常态化监控锚 = 预警④(清图槽 buff 零兑付单独计数) | E11 | +| 治疗包 power 超过当前缺血 | hp' = min(hp + power, max_hp)——部分回复合法(clamp 归 B1),不拒绝(与满血拒绝相区分:满血 = 零收益守卫、超量 = 部分收益) | E12 | +| RewardTable buff 行的 effect_id 悬空(B3 无此行) | 首个 effect_id 引用行回填后,B3 悬空校验激活 → 加载期致命(D2 通道);此前不校验(dangling_ref_policy 既有口径) | E13 | +| 战斗中热重载 RewardTable | D2 战斗中 reload 拒绝门(D2 EC8,B1 经 set_reload_gate 置位);已授予实例携带行值快照(power/effect_id 授予时冻结——B2 行值快照同款),映射中未授予身份仅是 reward_id 引用、grant 时快照 | E14 | +| cleared 与 battle_over 同结算(末图清图即胜利) | 授予段先兑付清图奖励 → 同结算道具使用窗口即可选用(含当轮清图奖励件)→ 未选用件随库存关卡域延续(末图 = 关卡末图时未选用件至关卡末清空 + 反馈行归 D1 义务⑤);折算/继承结构位归 OQ4(Alpha,C2/D5——折算点 = 关卡末) | E15 | +| 池含 attack_up 行 | 授予走 B3 apply_effect 正常受理、消费于主角战斗段先手攻击;机制层不做校验(内容决策归 C3) | E16 | +| 玩家囤刀跨战斗/跨图累积(数量无上限) | 合法策略且为设计意图(可选类道具是关内资产——"囤刀待机"的规划视野 = 整个关卡,P2 长线规划形态兑现);无运行时上限(伤害恒定表值 ⇒ 无乘法爆炸);失衡监控 = 预警①(浪费半)与⑥(速杀退化),修正入口 = C3 权重调参(items_per_settlement_cap 结构位默认关——Tuning Knobs) | E17 | +| 同格重复 grant / 同一 cleared 结算重复 grant_clear(Ready 相位) | grant 同格重受理 → 返回同一预分配快照 + debug 诊断;grant_clear 重调 → 返回同一预分配身份 + debug 诊断(幂等受理族;受理/兑付恰一次归 B1×②③,重复调用的诊断面留开发期暴露;对比:roll_board 二调 = 拒绝〔结构性相位违规,E5〕,重受理 = 编排瑕疵〔幂等兜底〕) | E18 | +| grant/query_identity 传入清单外坐标(非末次 roll_board 资格清单——R₀ 格/雷格/板外) | query_identity → null + 诊断、grant → false + 诊断(未分配非错误态;A5 探测查询任意格时,清单内未开格恒可查〔AC-2 分支 3〕、清单外 = 未分配) | E19 | +| stock() 入参越域 | 未知 item_id / buff 类条目 → false + 诊断(仅受理道具类条目,category ∈ {damage_item, heal_pack});来源 {cell, round} 由调用方供给、B4 不校验真值(真值归 B1×②) | E20 | +| query_inventory 的枚举序 | = stock 受理序(插入序,B3 Rule 6 先例)——D1 窗口呈现序契约 | E21 | + +## 依赖 + +**上游(hard)**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | hard | RewardTable 新表 schema + 内容行回填(本 GDD owner,接口漂移回写 D2 Core Rules 表清单 + registry)+ RewardConfigTable 单行全局新增(T_fog_min/K_dmg/N_draft/P_min **+ active_p0/active_decay——供给控制器参数,C3 修订批接口漂移增补**)+ pool_tag 枚举增 **active_inject**(注入池);读取经 ConfigDB 单点 | +| A2 | 间接(零直接接口) | 资格事实(reward_eligible/channel)经 B1 转化;A2 只报资格、内容与折算不经 A2(A2 Interactions B4 行对侧确认) | +| A3/A4 | 对账/验收锚(非依赖边) | A3 条款④⑤与 A4 三条定价硬约束 = 对账/验收锚关系;锚验收权归 B4/B1(B3"相邻"口径同款) | + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| B1 | hard(provisional——B1 未设计) | 结算编排期调用:roll_board(install 后恰一次)/ grant / grant_clear / stock / use / query_inventory / clear_inventory;B1 义务清单 6 项(缺项即 blocking,见下表);窗口锚点 = 授予段后、窗口选择序 = 回放输入、每格受理恰一次 | +| B3 | 相邻(零运行时依赖——调用经 B1) | buff 类经 B1 调 apply_effect(source_class=safe_cell/clear_reward 已在 B3 枚举内);锚验收权归 B4/B1(weaken 档位 vs 最低档安全格 buff 当量——A3 条款 2);满层拒绝协议对侧;首个 effect_id 引用回填激活悬空校验 | +| B2 | 相邻(零运行时依赖) | 伤害道具默认目标 = 固定顺位(Rule 5 消费);HeroTable.atk 域 [1,999](基础攻击恒存在——attack_up 复活的供给前提);道具段先于战斗段 → 抢杀窗口的道具形态(段序战术面);伏击清单扩展联动对侧 | +| C3 | 下游 soft(provisional) | 权重/档位/威力/池内容曲线数值调参所有权(默认根行集已登记);T_fog_min/K_dmg/N_draft/P_min **+ active_p0/active_decay(供给控制器)** 参数(RewardConfigTable);调参期批量仿真义务(F5 可行率 + 登记常数重制联动);雾区档位过滤谓词的参数归 C3;失衡预警①–⑧ playtest 归口;**clear_blast 换位 + K_dmg=0 + 注入池结构转正裁决(用户拍板,明细见版本历史)**;attack_up 入池裁决(随奖励改造批) | +| C1 | 下游 soft(provisional) | biome/tag 图类型供给(clear_ 插槽差异化输入——插槽激活的前置) | +| C2 | 下游 soft(provisional) | 关卡大奖励归 C2(B4 无关);关卡结构 = 库存关卡域的容器("关卡结束"事件归 C1/C2 定义,落位前 B1 暴露 level_clear 入口由上层显式调用);关卡末剩余道具折算结构位归 C2(OQ4 残余——转金币/继承候选,Alpha) | +| D1 | soft | 呈现义务 5 项(缺项即 blocking,见下表) | +| B5/D5 | soft | 结算明细道具行(数据源 = B1 流水,含击杀结果字段——H3 击杀标记数据源);道具使用窗口教学(拾取→囤刀→窗口选用的闭环)+ "关卡末库存清空"教学(RAP 清零同款先例) | +| A5 | 相邻(预留桥接位) | aux_item 预留枚举(校验拒绝——启用走修订);未开格奖励身份探测 = A5 探测道具零成本桥接(身份 install 期已定可查,OQ 登记,垂直切片) | + +- 双向一致性核对(systems-index 依赖分层表):索引分层 B4 ← D2(硬上游仅 D2;A2 = 事实经 B1 转化零直接接口、B3 = 验收锚关系非运行时边——B2"其余为事实读取不列边"注记口径同款)。 + +### B1 侧义务清单(缺项即 blocking——B2/B3 对 B1 登记先例) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| B1×① | 结算段序骨架成文:授予段(编排期转化,含 grant_clear 兑付与即时类当场兑付)→ 道具使用窗口(授予段后、增益段前)→ 增益/减益段 → 道具段 → 战斗段 → 衰减段 → 判定段;核心序 buff/debuff > 道具 > 战斗不变;窗口选择序列登记为结算回放输入事件 | B1 GDD | 已登记,待 B1 落地 | +| B1×② | 奖励受理与分流编排:grant → buff 类 apply_effect / 即时类当场兑付(instant_aoe = 全体存活敌各 power、instant_heal = 主角回复 clamp)/ 道具类 stock;每格受理恰一次、stock 与 grant 返回一一对应;满层拒绝/即时类零兑付 = "奖励未生效"明细行、无折算——未生效类行携带可聚合计数/批次字段(合并呈现归 B1/B5 裁决,数据须可聚合) | B1 GDD | 已登记,待 B1 落地 | +| B1×③ | clear_reward 授予路径:清图奖励 roll 身份 install 期已定、cleared 结算编排期兑付——兑付后同结算窗口即可选用;清图槽即时类同分支当场兑付(B3 OQ4 关闭条件) | B1 GDD | 已登记,待 B1 落地 | +| B1×④ | 窗口与道具段编排:窗口时机 = 授予段后、增益段前;窗口数据组装 = B4 query_inventory 投影 + B1 侧守卫预判 usable_now(满血治疗包禁选/提示——呈现归 D1;B4 零跨系统读不变);窗口至多选用一件(单件施加、目标/数值/明细行来源标注);零效果判定读 B2 存活集 + "已消耗未生效"明细行(E10 承接);击杀结果进流水(H3 击杀标记数据源——B5/D1 呈现消费);治疗 clamp | B1 GDD | 已登记,待 B1 落地 | +| B1×⑤ | 生命周期编排:关卡结束调 clear_inventory 恰一次;清空前后快照计数供"随关卡结束清空"反馈行(D1 义务⑤);battle_over 调映射退役(映射 = 板级,随战斗消亡——下一场战斗新板新批) | B1 GDD | 已登记,待 B1 落地 | +| B1×⑥ | roll_board 调用时机:install 后、挖掘前恰一次(B4 侧相位守卫见 E5/AC-1 分支 3;B1 编排纪律承载调用时点——B4 不可观测) | B1 GDD | 已登记,待 B1 落地 | + +### D1 侧义务清单(缺项即 blocking——"清单外登记会丢失"先例) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| D1×① | 已开格 + 库存奖励身份恒定可读:库存常读条 = 挖掘期规划面而非仅轮末窗口(Rule 8);关内延续后常读条跨战斗可见 = 关卡资产感的数据面 | D1 GDD | 已登记,待 D1 落地 | +| D1×② | 道具使用窗口呈现:授予段后弹出;选用/跳过/单件选用、usable_now 可用性投影呈现(满血治疗包禁选或选中提示)、窗口时机指示、空库存可不弹窗(E8)、窗口触发政策注记(MVP 有库存恒弹;候选"仅可用品触发/常驻库存条非模态"归垂直切片) | D1 GDD | 已登记,待 D1 落地 | +| D1×③ | 结算明细四类行呈现:道具行(含入库/生效语境注记)/ "奖励未生效"行(按聚合计数呈现,数据源含可聚合字段)/ "已消耗未生效"行 / "使用被拒"行——数据源 = B1 流水,P2 归因 | D1 GDD | 已登记,待 D1 落地 | +| D1×④ | H2 清图奖励行强调呈现(轮末仪式峰值) | D1 GDD | 已登记,待 D1 落地 | +| D1×⑤ | 关卡末库存清空可见反馈:"剩余道具 ×N 随关卡结束清空"汇总;层间过渡"库存 ×N · HP 带入下一层"可见反馈 = 垂直切片候选 | D1 GDD | 已登记,待 D1 落地 | + +### A4 定价硬约束的 MVP 承载状态(显式宣告) + +| 约束 | 状态 | 说明 | +|---|---|---| +| ① 雾内档位下界 ≥ 最低档安全格 buff 当量 | 锚侧承载 | AC-19 档位序检查(非运行时校验);真当量换算依赖 B1 条款①对照表(未设计) | +| ② 清图奖励锚随雾成本负担上浮 | MVP 结构性不可闭合 | clear_default MVP 单默认槽 = 板无关常量池,清图奖励当量无法随雾负担变化(非 deferral 而是本期不可闭合;B1 强制检查点只能对该常量判 pass/fail,改 power 会同步抬高无雾板、破坏无雾侧定价);激活载体 = clear_ 插槽(垂直切片,待 C1 biome 词表落定) | +| ③ 雾板净当量优势带上界 | 静默绿灯风险 | ②缺位 + 雾格仅 tier 下界加成的 MVP 结构下近乎必然平凡通过;休眠状态与接受风险(重雾板清图欠偿 → 弃图偏斜——A4 警告的 level 2 结构性高发位)在此显式登记;对冲 = 预警⑤雾板分层观测 + 预警⑥速杀退化监测 | + +### 接口漂移登记(回写已兑现——delta 复核逐处对账) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | game-concept 待定问题"主动道具后续版本加入"于 MVP 激活(道具使用窗口)——use_timing 字段从预留转承重 | game-concept(第 144 行注记) | 已同步 | +| 2 | registry `rng_service.scope_event_binding` 的 reward_roll 批次语义改写为"每次 board install(格奖励批 + 清图奖励批)" | registry + D2 正文(Core Rule 5 / Interactions B4 行) | 已同步 | +| 3 | 锚参照迁移传播(attack_up 当量参照回迁) | systems-index A3 OQ5 注记 · registry `status_effect.anchor_warning` · registry `clause3_supply.direction_precheck` | 已同步(三处) | +| 4 | game-concept 创意风险 #1 与可证伪信号的 MVP 口径注记 | game-concept | 已同步 | +| 5 | 窗口锚点修订(授予段后) | systems-index 门禁块 | 已同步 | +| 6 | systems-index 分层行 B4 ← D2 更新 | systems-index | 已同步 | + +## 呈现与 UI 契约 + +> B4 = Core 层数据/逻辑系统,零自有视觉/音频/UI 资产(headless 可跑全奖励流);本节定义数据面供给责任,呈现实现归 D1/B5/D4/D5(义务清单见依赖节)。 + +| 编号 | 数据面义务 | 归属 | +|---|---|---| +| 1 | 高光节点 H1 开格拾取瞬间:身份在场——"这格有货"是 P4 高频情绪节拍;触发数据 = cell_revealed + 身份查询;query_identity 投影含 tier——拾取瞬间可读"好货"档位 | D1 呈现 | +| 2 | 高光节点 H2 清图奖励兑付:轮末仪式峰值——clear_reward 明细行强调呈现(D1 义务④) | D1 | +| 3 | 高光节点 H3 道具抢杀窗口:"重击拍前用刀"——"我算对了"的执行侧兑现(段序战术面,Rule 5);感知数据前置:B1 流水含击杀结果字段(B1 义务④)——无它则"敌灭、重击未落地"不可呈现,兑现点缺席 | B1 供给 / D1 呈现 | +| 4 | 创意风险登记:#2"完美排雷爆发结算"(零误标/全标中加倍)机制本体归 B1 强制检查点(game-concept 原文归属——B4 交付插槽与明细行数据面);#3 标中旗即时节拍补偿同归 B1(A2"清单外登记会丢失"先例——本条目化即义务,D1 义务④⑤为其 blocking 承载) | B1 / D1 | +| 5 | icon 映射键契约:reward_id(全局恒定、跨 run 不重掷——B2 D1×4② 同款口径);category 三类第一层区分(buff/伤害道具/治疗包)+ 行级第二层;形状+色相双通道(art-bible 约束——art-bible 未建前键契约先冻结);名称数据源 = display_name 字段 | D1 / art-bible | +| 6 | 绘制优先级插入点:奖励徽章位于旗子之下、变异纹理之上,不得进入数字区域(game-concept 绘制优先级栈:数字 > 小人 > 旗 > [徽章] > 纹理 > 底色)——纳入格子样张测试验收项 | art-bible | +| 7 | 结算明细四类行协议(数据源 = B1 流水,呈现归 B5/D1 义务③):道具行(含入库/生效语境注记)/ "奖励未生效"行(携带可聚合计数/批次字段——E3)/ "已消耗未生效"行(E10)/ "使用被拒"行(满血守卫拒绝的兜底呈现——usable_now 预判外的残余路径);未选用件"随关卡结束清空"语境注记(D1 义务⑤) | B5/D1 | +| 8 | 音频挂钩点(D4,Alpha):开格拾取音(按 category 分色)/ 道具使用音 / 清图奖励音 / 负反馈聚合音("未生效/被拒"类行数 >0 时单次结算至多一次——防逐行惩罚轰炸或静默惩罚);挂点事件归 B1/D1,B4 零音频依赖 | D4 | +| 9 | 道具使用窗口数据契约:载荷 = B1 编排组装——query_inventory 逐件实例(item_id + 来源 {cell, round} + 行值快照投影 + 状态;枚举序 = stock 受理序〔E21〕)+ usable_now 预判;呈现(逐件列表/选用序收集/跳过/窗口时机指示/usable_now 禁选态/触发政策注记)归 D1;窗口期选择收集与棋盘输入不竞争(A2 单意图帧先例);窗口选择序列 = 结算回放的输入事件 | D1 | +| 10 | UX 规格归 D1 GDD(设计顺序第 10 位)前置 /ux-design——窗口是 D1 域屏,B4 只供数据契约;B4 无 D1 外新增 UI 义务 | D1 | + +## Tuning Knobs + +| Knob(RewardTable 行值 / RewardConfigTable 配置值,均 D2 承载) | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| RewardTable.weight(逐行) | 格子池四行即时类(合计 67)+ 注入池三行可选类(knife 60 / adv 30 / heal_pack 10)+ clear_blast 单行槽——注入池结构与换位随 C3 修订批转正;provisional 归 C3 | 即时类过剩 → 结算明细淹没("奖励未生效"行刷屏)、可选类相对贬值 | 可选类过低 → 窗口长时空转(单件制下规划面稀薄)、重 roll 频发乃至致命 | +| RewardTable.tier | 默认根:knife_t1/spark_aoe/heal_minor t1、attack/defense/heal_pack/knife_adv/clear_knife t2 | 雾区档位上移 → 平原奖励贬值(预警③联动) | T_fog_min 过滤失效 → A4 硬约束①锚失守(对账兜底) | +| RewardTable.power | 默认根:刀 8/16、治疗包 12、即时类 4/4(可选类更强、治疗稀缺化配套) | 道具段秒杀 / 治疗溢出(HP 满拒绝率↑——预警②) | 刀供给名义达标实际不足(击杀需求多轮化) | +| T_fog_min(RewardConfigTable) | 2(provisional) | 雾池收窄 → 雾格奖励稀化、单一化(预警③)、池空致命风险↑ | =1 过滤退化——雾区档位下界仅剩锚兜底 | +| K_dmg(保底刀数) | 0(provisional——保底断言关闭,语义由供给控制器首件保证取代;原 1 与"禁区默认值"条款随 C3 修订批解除) | 重 roll 频发 → 流消费膨胀;> 资格格数恒致命 | =0 = 断言关闭(**现行默认**——no_damage_row 加载后闸托底零活刀行世界) | +| N_draft(重 roll 上限) | 32(provisional) | 坏权重多次过审(掩盖配置错误) | 合法低供给池被误杀致命(概率性致命带——C3 批量仿真义务) | +| 供给控制器 active_p0 / active_decay | 0.4 / 0.5(provisional) | 速杀+囤积回归(获取过量) | 可选类绝育(注入趋零)——E[战斗获取] ≈ 1 标定目标;**机制本体 = RewardSupplyController(install 期战斗首件保证 + 指数递减注入,归本 GDD——C3 修订批承接登记);加难杠杆候选 decay 0.6 随 C3 OQ7 扫描** | +| P_min(K≥1 供给闸刀力下界) | 8(= 默认根最低刀 power) | 闸收紧 → 低威力刀行被加载拒绝(内容收窄) | =1 → 闸空转(power ≥1 恒真) | +| items_per_settlement_cap(结构位) | 关(无上限) | 启用即限制——过高不构成保护 | 过低锁死胜局(击杀需求 ÷ 轮数——economy-designer 警告);启用裁决归 C3 playtest | + +- 非 knob(结构冻结,修订走本 GDD 流程非调参):奖励类型五值封闭枚举(两类交付轴)· install 期预分配 + Ready 只读(无重掷 API)· 道具伤害恒定表值不吃修正(用户拍板)· 满层零兑付无折算 · 库存关卡域(跨战斗延续、关卡末清空)· 窗口单件制(体验反馈后修订走 GDD 流程)· 清图恒非空 · 结算段序骨架(buff/debuff > 道具 > 战斗)· 零随机出 roll_board 一线 · 逐件实例归因。 +- Knob 交互:K_dmg × safe_cell 伤害权重 × T_fog_min(雾池收窄挤压刀供给 → 重 roll 频发——三者禁止独立调参);T_fog_min × tier 分布 × 雾池行数(雾池单一化联动——预警③第二仪表,禁独立调参);power × B2 EnemyTable hp 曲线(击杀需求侧联动,C3 所有权);weight × B3 max_stack(buff 零兑付率联动——预警④)。 +- 调参所有权:内容/曲线全部 provisional 归 C3;A3 条款③⑤与 A4 硬约束①②③的对账闭合归 B1(锚验收权 B4/B1——B3 Rule 10 同款)。 + +## 失衡预警清单(playtest 预注册) + +| 编号 | 指标 | 阈值/读法 | +|---|---|---| +| ① | 刀供给短缺/浪费双指标 | 短缺 = "获得刀伤供给 ÷ 击杀需求" <1.0 即警(供给面信号,按 unit_count {1, 2–3} 两档分设阈值);浪费 = "未用刀伤 ÷ 获得刀伤供给" >0.5 即警(效率面,与短缺分开读——供需贴近下界时高效用刀本就趋零) | +| ② | 治疗双尾 | 溢出 = HP 满拒绝率 >40%(instant_heal 满血零兑付并入本仪表——小额浪费可容忍、阈值放宽;heal_pack 满血拒绝维持 >40%);短缺 = "败北且库存有未用治疗包"占比 >20%(可选治疗稀缺化至 6% 后本仪表为主仪表——强治疗名存实亡的探针) | +| ③ | 雾区档位空窗 + 单一化 | 雾区拾取平均 tier ≤ 平原即警;增雾池单一化监测——雾区拾取 distinct reward_id 数 = 1 持续即警(雾池收窄 → "越险货越同",T_fog_min × tier 分布联动调参) | +| ④ | buff 零兑付双表 | 稳态(单格授予批)零兑付率 >25% 即警;连锁批量授予零兑付率单列(结构性偏高——登记推导:buff 供给 λ = 8 挖/轮 × 44% ≈ 3.5 层/轮 vs 吸收上限 1 层/结算 × max_stack 2,批式授予下零兑付率远高于单格批;阈值 >50% 即警);结构性缓解:buff 时长改当次结算(D=1)——浪费不再跨结算累积、保质期一次结算天然有界,但同结算内批量授予的零兑付仍存在,仪表保留;清图槽 buff 零兑付单独计数维持 | +| ⑤ | 清图支配反转 | 弃图率 >30%(A3 OQ3 联动)——雾板/无雾板分层观测(A4 明文要求补登;聚合指标会被雾板污染或反向掩盖) | +| ⑥ | 速杀退化 | 战斗结束时未开资格格占比 >70% 即警(供给过剩 → 战斗提早结束 → 未挖身份零发放,奖励系统饿死自己的高光与雾内容) | +| ⑦ | 窗口摩擦 | 窗口跳过率、单结算平均明细行数(playtest 预注册,阈值归 C3);结构性缓解:库存只收可选类(25% 权重)+ 窗口单件制——窗口频率与每次决策量双降,本预警预期大幅缓解、保留观测 | +| ⑧ | H2 曝光率 | 含清图奖励兑付的结算数 ÷ 战斗数(OQ4 折算裁决的数据输入) | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | B4 是 Core 层数据/逻辑系统——非 UI、无场景依附;冒烟载体 = `godot --headless --script`(承 D2 冒烟框架;脚本 extends SceneTree 以获得 quit(code),ConfigDB/RngService autoload 由脚本显式实例化);映射容器/库存容器/RewardTable schema 经 class_name 直引实例化(每用例全新实例、无跨用例状态——B3/A4/B2 直引先例;AC-21 扫描对该路径豁免) | +| 被测面 | B1 未设计(设计顺序第 9 位)——冒烟直接驱动 B4 最小 API 面并扮演编排方(B2/B3 先例);B4 冒烟零 B2/B3 实例化(相邻零运行时依赖的代码侧对应——施加面/存活集/血量全部归 B1 OOS,StatusEffectTable/HeroTable 仅经 ConfigDB 读值);资格格清单程序构造(真实来源 = B1 义务⑥——冒烟构造等价,零 A1/A2 接线) | +| 时序口径 | "一次 board install" = roll_board 恰一次的构造窗口;"cleared 轮" = grant_clear 构造调用;"battle_over" = clear_inventory(+retire_mapping)构造调用;结算段序骨架(授予段→道具使用窗口→增益/减益段→道具段→战斗段→衰减段→判定段)与使用窗口编排 = B1 域——冒烟以构造序扮演、不断言编排正确性(B2 时序口径同款);σ 到达机制:D2 全新 RngService 首个 open_stream 恒为 scope 0——到达 scope=σ 须先 `open_stream("reward_roll")` ×σ 丢弃预推进(AC-7 分支 1 按此执行) | +| 纸面契约子集 | 凡依赖 Ψ(σ)/Φ(σ)/σ*/重 roll 轨迹登记值的 THEN,在取号约定实现期 ADR 落地前不视为可执行(受影响:AC-1 分支 1 非雾格值、AC-7 分支 2/3、AC-10 分支 1、AC-12 登记序列);雾格单行退化分支例外(r 无关,现即可执行) | +| 只读观测量 | 映射投影(reward_id/category——引用级)· 库存实例投影(item_id/来源/快照/状态)· 流 scope 计数器(D2 AC-4 公开只读——install 恰一 scope 的观测面);相位无字段观测量:Unrolled/Ready/Retired 由受理返回值推断;Cleared = 逻辑失效态(失效句柄查询返回空 + 诊断、作用接口拒绝——AC-3 域闭;B2 Despawned 逻辑失效口径同款);实例不变式(Ready 后映射零写入 / Used 不可逆 / 库存条目仅存关卡域)= debug 断言自守——违规路径不可由封闭 API 构造(负空间:无重掷 API 即"Ready 后零写入"的结构前提),不经冒烟断言;行为级证据 = AC-1/AC-2/AC-9/AC-14 | +| 期望值推导纪律 | weight/power/tier/T_fog_min/K_dmg/N_draft 全 provisional(Tuning Knobs)——各 AC 的 THEN 具体数值按当前表读值/夹具登记值推导、不写死(记号:w_i/t_i/p_i/T_f/K/N);结构定值断言合法:category 五值封闭、pool_tag MVP 二值封闭、use_timing 单值、清图恒正当量校验存在性;权重比例与数值不作定值断言(provisional 归 C3) | +| 拍板语义承载 | 道具段照常消耗(AC-14 分支 3——不采信任何"拒绝不消耗"旧读法)· 清图槽允许 buff 行(AC-11)· 窗口锚点 = 授予段后(Rule 5——B4 冒烟不断言编排时点,构造序扮演) | +| 登记常数协议 | 期望格身份映射 Ψ(σ)、清图身份 Φ(σ)、重 roll 草稿数轨迹、A4① 对账对——入版本库(B2 行集协议同源);失效处置 = 先复核登记、登记无误即实现缺陷;登记后安排第二人独立推导复核(A2 OQ3 同型义务) | +| 测试种子取号协议 | master=42;σ 取号 = 每 roll_board 调用恰一 scope(install 批含格批 + 清图批尾;retire 后新 install = 新 scope——E6/registry 语义);期望轨迹经 `derive_at("reward_roll", σ)` 纯函数测试通道推导(不推计数器——D2 R2 预览语义的测试侧合法用途,A1 同款先例;黄金向量 m=42 scope0=7422916049646135537 见 D2 R2 向量表,本节复用不重制——推导起点锚) | +| out-of-scope(覆盖声明) | deferral 生效条件 = B1 义务清单 6 项 + D1 义务清单 5 项登记(均缺项即 blocking):B1×① 段序骨架与结算内调用序(含窗口锚点 + 窗口选择序回放输入)· B1×② 受理分流调用(grant→apply_effect/stock;每格恰一次)与满层"奖励未生效"明细行协议 + 聚合计数字段(E3/E11 大量同文行半)· B1×③ clear_reward 授予路径 · B1×④ 窗口与道具段编排——usable_now 组装、满血守卫(拒绝不烧包)+ 逐件动态守卫(同窗顺序多包)、零效果判定(读 B2 存活集)、逐件"已消耗未生效"明细行、击杀结果进流水、治疗 clamp(E12)、伤害施加与"恒定表值不吃修正"纪律 · B1×⑤ 生命周期编排恰一次(关卡末 clear_inventory 时点 + 清空反馈计数——E2"零明细行"= 编排不调即无行)· B1×⑥ roll_board 恰一次编排 ∥ D1×①…⑤ 预读呈现(含库存常读条)/使用窗口(选用·跳过·序收集·usable_now·触发政策)/四类明细行呈现(聚合 + 语境注记)/H2 强调呈现/关卡末清空反馈(不可 G-W-T)∥ 另有:B3 满层拒绝原子(B3 AC 既有——跨引不重复);A4 约束②③(MVP 休眠——依赖节显式宣告)与 A3 条款⑤对账闭合(B1 强制检查点);attack_up 复活联动(OQ1);C3 数值与失衡预警①–⑧(playtest 域);未开格身份呈现深度(OQ2/A5+D1) | + +### API 面(provisional,封闭枚举——实现期 ADR 定名) + +| 接口 | 签名 | 语义与守卫 | +|---|---|---| +| roll_board | `roll_board(eligible: Array[{cell, fog}]) -> bool` | install 期恰一次;消费 reward_roll 封闭流恰一 scope 批(格批 + 清图批尾一号 × F5 草稿数——每稿 = 格批 + 清图批尾一号,草稿弃用连清图一号一并弃用);Ready 相位再调 → false + 诊断(E5);空清单 → debug 断言中止(E1 兜底);清单含重复格 → 去重 + debug 诊断 | +| query_identity | `query_identity(cell) -> Dictionary\|null` | Ready 只读预读面——返回 {reward_id, category, tier} 引用级投影,非行值快照(快照时点 = grant,E10);Retired/Cleared → null + 诊断;清单外坐标 → null + 诊断(E19——A5 探测桥接的结构面) | +| grant | `grant(cell) -> Dictionary\|false` | 受理面——返回 {reward_id, category, effect_id?, power?} 行值快照;Unrolled/Retired/Cleared → false + 诊断(E4/E7);清单外 → false + 诊断(E19);同格重复受理 → 同一快照 + 诊断(幂等,E18) | +| grant_clear | `grant_clear() -> Dictionary\|false` | cleared 轮兑付——身份 = install 批尾预分配;同批次重调 → 同一身份 + 诊断(幂等,E18) | +| stock | `stock(item) -> Dictionary\|false` | 道具类入库——仅受理 category ∈ {damage_item, heal_pack},未知 item_id/buff 类 → false + 诊断(E20);逐件实例 {item_id, source{cell, round}〔调用方供给、B4 不校验真值——真值归 B1×②〕, 快照};Cleared → false + 诊断 | +| use | `use(handle) -> Dictionary\|false` | 窗口选用消耗——true ⇔ InStock→Used + 返回消耗回执(授予时快照投影 + 来源归因);Used 句柄 → false + 诊断(E9);零游戏态前置守卫——存活集/血量对 B4 不可见,E10 照常消耗与 F4 满血守卫归 B1 的结构前提 | +| query_inventory | `query_inventory() -> Array` | 逐件实例枚举含状态 InStock/Used,枚举序 = stock 受理序(E21)——Used 保留只读可查("已消耗未生效"行数据面,B2 Dead 句柄同款);呈现过滤归 D1 | +| clear_inventory | `clear_inventory() -> bool` | 关卡末全清;重复 → false + 诊断(E7 幂等) | +| retire_mapping | `retire_mapping() -> bool` | board retire——B1 义务⑤ | +| equivalence_supply | `equivalence_supply() -> Dictionary` | F6 供给面 {全活行投影, tier_min_buff}——只读 | +| weighted_pick | `weighted_pick(rows, r) -> String\|null` | F1 选行算子纯函数供给面(B2 ambush_grant_plan 先例) | + +- 全部布尔/字典返回 = 受理/拒绝(附 debug 诊断、非进程致命——进程致命保留给 D2 数据通道与 fail-fast 兜底点)。 +- 封闭枚举即负空间验收形态:无重掷 API(Rule 8 Ready 只读)· 无库存上限 API(Rule 4/E17)· 无伤害/治疗/buff 施加 API(施加归 B1——F4/Rule 3)· 无自主清空时点(clear_inventory 调用权归 B1)· 无事件订阅 · 无 RNG 直连(唯一入口 = roll_board 内 open_stream("reward_roll"));新增任何入口 = 接口漂移(GDD 修订非调参)。 + +### 夹具与种子策略 + +| 夹具根 | 登记内容 | +|---|---| +| 默认根 | B4 回填的 RewardTable MVP 行集(枚举节八行:defense_up_basic/attack_up_basic/spark_aoe/heal_minor/knife_t1/knife_adv/heal_pack/clear_knife)+ RewardConfigTable T_fog_min=2 / K_dmg=1 / N_draft=32 / P_min=8——结构断言、F6 供给与方向预检 | +| b4_golden | D2 全表清单拷贝 + 权重/tier 冻结登记值(刀 w=2 t=1 / 治疗包 w=1 t=1 / defense_up w=1 t=3 / clear_default 单刀行 w=1)——黄金载体与默认根内容调参解耦(C3 重调默认权重不触动本根;雾池 = {defense_up t3} 单行退化——雾格分支 r 无关现即可执行) | +| b4_reroll | T_fog_min=1 / K_dmg=1 / N_draft=32 / p_min=4(刀 p=4 须过 F5 加载护栏 (i),随全局 P_min=8 生效);safe_cell = 刀 w=1 t=1 / 治疗包 w=3 t=1 / defense_up w=3 t=2;clear_default 单刀行 w=1 p=4——满足全部加载校验(活 buff 行 ✓ / 雾池非空 ✓ / 清图正当量 ✓);重 roll 可达(首稿零刀 P ≈ (6/7)³ ≈ 0.63)而耗尽经验稀有(≈4×10⁻⁷/σ)——取号约定 ADR 落地后按缺口预检 8 复核可达性拆分、失效即重制 | +| b4_fog_gate | tier 布置使雾池唯一活行 = 高档行 H、非雾池含低档行 L(登记) | +| b4_fog_gate_hot | 同表 + T_fog_min=8 表值烤入——雾态池空加载致命(AC-6 分支 4 致命分支载体——R3 override 不触发加载校验,D2 边界⑤) | +| b4_clear_buff | clear_default 唯一活行 = defense_up | +| b4_reloaded | power/tier 各 +1(effect_id 不动——登记) | +| b4_nofloor | safe_cell 无刀行 + K_dmg=0 | +| b4_attackup_row | safe_cell 追加 attack_up 引用行(attack_up 复活后的正向夹具) | +| 致命根 ×8 | manifest(见下表;含 b4_floor_supply / b4_nodamage) | +| 程序构造 | RewardTable + RewardConfigTable schema 直引(AC-16);资格格清单 b4_list_A(3 格含 1 雾)/ b4_list_B(4 格)/ b4_list_single(1 格)坐标入版本库 | + +- 注入通道:行为夹具根经 `--data-root=` 注入(仅 debug、不入 `data/tables/`——D2 夹具纪律);标量 knob 单变分支(T_fog_min=1、K_dmg=2)经 R3 debug override(域内值、运行时态——D2 AC-7 先例);加载致命分支一律烤入根承载、不经 override;两通道每夹具恰一登记。 + +### 致命类收窄豁免与 runner manifest + +| 类别 | manifest 行 | 触发条件 | 断言 | +|---|---|---|---| +| 加载期 ×8 | pool_empty_safe | safe_cell 全 0 权 | 独立 headless 进程非 0 退出 | +| 加载期 | pool_empty_fog | 全行 tier < T_fog_min(雾态空) | 非 0 退出 | +| 加载期 | clear_empty | clear_default 池空/全 0 权 | 非 0 退出 | +| 加载期 | clear_no_value | clear_default 唯一活行 = power 0 道具行(恒非空的可判定形式——A3 条款④表侧兑现) | 非 0 退出 | +| 加载期 | no_live_buff_row | safe_cell 有活行但零活 buff 行(tier_min_buff 无定义——F1 对偶致命) | 非 0 退出 | +| 加载期 | dangling_effect | buff 行 effect_id = "ghost_buff"(B3 StatusEffectTable 无此行;条件性夹具:默认根已含 effect_id 引用行 ⇒ 悬空校验已激活——dangling_ref_policy 首引兑现点) | 非 0 退出 | +| 加载期 | floor_supply_missing | K_dmg ≥ 1 ∧ safe_cell 池无 power ≥ P_min 活刀行(F5 加载护栏 (i)——根 b4_floor_supply) | 非 0 退出 | +| 加载期 | no_damage_row | K_dmg = 0 ∧ safe_cell ∪ clear_default 全池零活刀行(F5 加载护栏 (ii) 零伤害世界后闸——根 b4_nodamage,B2 OQ9 并案) | 非 0 退出 | +| 运行期 ×2 | floor_exhaust | roll_board 保底耗尽 | 调用后退出码非 0 | +| 运行期 | empty_eligible | roll_board(∅) debug 断言中止(release 构建语义 = push_error 致命路径——D2 EC7"零静默回退"同款,实现期 ADR 登记) | 非 0 退出 | + +- 其余致命类别(通用枚举域/域校验/R1 类)由 D2 AC-2 同类别 loader 夹具承载(表无关,豁免声明收窄——B2 同款);每用例先 `reset_diagnostics()`(D2 冒烟协议,全局声明)。 + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | b4_golden 根加载、全新 B4 容器(Unrolled)、RngService master=42 已播种(scope 计数器 = 0)、清单 = b4_list_A | `roll_board(b4_list_A)`;分支 2:两套全新 RngService+B4(同 master=42、同 scope 起点)分别以规范序/乱序传入同一集合;分支 3:Ready 后再 `roll_board(任意清单)` | true(Unrolled→Ready);query_identity 逐格 = 登记映射 Ψ(0)(含雾格分支——雾格身份 ∈ 雾池行,登记值);reward_roll 流 scope 计数器 = 1(install 恰一 scope——registry 语义的行为面);分支 2:两映射逐格相同(行主序消费序 B4 侧规范化——F1"行主序逐格",排序责任不外推给 B1);分支 3:false + 诊断、映射与流计数器不变(相位守卫——B2 AC-1 同款;double-roll 无守卫的编排半归 B1×⑥) | Rule 2/8 · E5 | +| AC-2 | b4_golden 根、roll_board 完成(Ready) | 腿 1:对每格重复 query_identity ×3;腿 2:grant(c) 后 query_identity(c);腿 3:对未受理格 query_identity;腿 4:query 前后对照;腿 5:retire_mapping() 后 query_identity(c) | 腿 1:逐字相同(结构保证非授予期随机);腿 2:= 授予前值(身份恒定跨授予——同图跨轮恒定的观测半);腿 3:恒返回身份(映射无"已开才可查"谓词——A5 探测道具零成本桥接的结构前提;MVP 不呈现归 D1/OQ2);腿 4:映射/库存投影零变化(纯读);腿 5:null + 诊断(映射消亡的观测形式——E6 旧映射半) | Rule 8 | +| AC-3 | b4_golden 根 | 分支 1:全新容器(Unrolled)上 grant(c)/grant_clear();分支 2:roll_board → retire_mapping 后 grant/grant_clear;分支 3:roll_board → grant → stock → clear_inventory() 后 grant/stock/use、query_inventory、query_identity(c);分支 4:重复 clear_inventory() / 重复 retire_mapping() | 分支 1:false + 诊断(E4 roll 前受理);分支 2:false + 诊断(E4/E2——未拾取身份消亡零兑付);分支 3:均 false + 诊断(E7);query_inventory = 空(InStock 与 Used 均清——关卡域);query_identity(c) → null + 诊断(Cleared 逻辑失效态的观测分支);分支 4:false + 诊断(E7 幂等守卫;同型守卫——B3 EC11 先例,provisional API 完整性分支) | E4/E7 | +| AC-4 | 全新容器、master 已播种(b4_golden;manifest 行 empty_eligible) | `roll_board([])`(S∖R₀=∅ 构造——真实流中 A2 install 不变式④前置,本 AC 直击 B4 兜底半) | debug 断言中止(fail-fast 兜底、零静默回退——F1 池空裁定的运行期对偶);测试通道诊断清单含类别;独立进程非 0 退出 | E1 | +| AC-5 | weighted_pick 纯函数面(行数组 + 取号 r 注入) | 分支 1:三行 w=2/1/1 逐一取 r=0/1/2/3;分支 2:行集 {w=0, w=2} 取 r ∈ {0,1};分支 3:单行 w=5 取 r ∈ [0,5);分支 4:r ≥ W | 分支 1:r=0/1 → 行 1(区间 [0,2))、r=2 → 行 2([2,3))、r=3 → 行 3([3,4)——W=4 边界号);分支 2:W=2、恒唯一非零行(0 权行取号前剔除、W 不含 0 权);分支 3:恒该行(合法极端——F1 裁定);分支 4:debug 断言(纯函数域;游戏通道内 r 恒 < W 由流构造保证) | F1 | +| AC-6 | b4_fog_gate 根(登记:雾池唯一活行 = 高档行 H〔tier ≥ T_fog_min〕、非雾池含低档行 L)+ b4_fog_gate_hot + R3 override | 分支 1:roll_board(清单含雾格);分支 2:对 σ = 0…31 逐一(scope 预推进)roll_board;分支 3:fog 布尔唯一输入构造(链开雾格);分支 4:T_fog_min=1 经 R3 override(运行时态、域内值)/ T_fog_min=8 且表无 tier8 行经烤入根 b4_fog_gate_hot | 分支 1:雾格身份 = H(单行退化判别——r 无关;雾格恒不出 tier < T_fog_min 行);分支 2:① 每稿雾格身份恒 = H(过滤谓词零泄漏——任何泄漏以 ≈w_H/W 概率现形);② 非雾格至少一次命中 tier < T_fog_min 行 L(证明非雾侧零过滤);登记值"非雾格 = L"降为诊断信息不作硬断言(加载规则强制 H 活 ⇒ 非雾池 ⊇ {H, L} 至少两行,单稿"= L"是概率断言,忠实实现也会假失败);分支 3:无 chain/dig 通道分支(A2 EC4 同待遇半——构造性);分支 4:T_fog_min=1 → 雾池 = 全池,雾格身份 ∈ 全体活行集(登记 σ 下 = 登记值——过滤退化合法,A4 约束①仅剩 AC-19 锚侧兜底);T_fog_min=8 → 加载致命(雾态池空——AC-17 pool_empty_fog 同类别;override 通道不触发加载校验) | F2 · A2 EC4 | +| AC-7 | b4_reroll 根(登记权重刀 w=1 / 其余 w=3;K=K_dmg、N=N_draft 表读值)+ b4_nofloor 根 + 登记 σ 参考轨迹表 | 分支 1:对 σ = 0…31 逐一(每 σ 全新服务+容器,先 `open_stream("reward_roll")` ×σ 丢弃预推进至 scope=σ——σ 到达机制)roll_board;分支 2:登记 σ*(参考算术判定首稿 0 刀)roll_board;分支 3:登记 σ**(首稿清图身份 = 刀 ∧ 格刀数 = 0)roll_board;分支 4:b4_nofloor 根(无刀行 ∧ K=0)roll_board | 分支 1:每提交板 \|刀身份格\| ≥ K(每板伤害供给下界保证的行为面);分支 2:提交映射 ≠ 首稿参考映射(草稿弃用、同 scope 续流重 roll);scope 计数器每 install 恒 +1(重 roll 不开新 scope——scope 语义 = install 批非草稿批);分支 3:仍重 roll(断言仅计格奖励身份——参考轨迹判别);分支 4:roll_board true、提交板 0 刀(K_dmg=0 = 关闭——C3 禁区默认值的行为面) | F5 | +| AC-8 | b4_floor_exhaust 根(manifest;safe_cell 池无刀行 ∧ K=1——每稿恒 0 刀) | `roll_board(b4_list_A)`;变体分支:b4_list_single(1 格)∧ K=2(R3 override 域内值) | N 次草稿后致命(权重配置错误 fail-fast);诊断含耗尽类别与草稿计数(N 表读值不写死);独立进程非 0 退出;变体:\|资格格\| < K 恒不过 → 同致命(F5 Output Range) | F5 | +| AC-9 | 两套全新 RngService+B4(同 master=42、同根 b4_reroll) | 同一定长序列驱动两遍(roll_board → query_identity 全格 → grant×2 → stock → use×1 → retire_mapping → roll_board〔新清单,scope+1〕→ grant_clear → clear_inventory → 全 query 面);分支 2:σ 与 σ+1 两板映射对照 | 两遍的映射投影、库存实例投影(含来源 {cell, round})、use 回执、流 scope 计数器轨迹逐项相同——同 σ 同流消费轨迹 ⇒ 同身份映射(含 F5 重 roll 分支)的确定函数性(D2 AC-15 的 B4 层前身);分支 2:σ 与 σ+1 的映射 = 登记相异对(新图新身份——E6 roll 半;库存延续半 = AC-13 分支 2) | Rule 2/10 · E6 | +| AC-10 | b4_golden 根、roll_board 完成 | 腿 1:`grant_clear()`;腿 2:两套同 σ 容器、grant_clear 前置调用序列互异再 grant_clear;腿 3:同批次再 grant_clear() | 腿 1:返回 = 登记清图身份 Φ(σ)(install 批尾一号——登记值含批尾位次假设:实现改批首取号即失配暴露);返回身份满足正当量(道具类 power ≥ 1 表读值 ∨ buff 类 effect_id 非空——条款④"清图奖励>0"的行级兑现面);腿 2:返回恒 Φ(σ)(身份 install 期已定、兑付期零 roll;图类型战斗域恒定不逐图重掷的判别形式——兑付期重掷将随前置序列漂移);腿 3:同一身份 + 诊断(兑付恰一次归 B1×③——E18) | F3 · Rule 6 · A3 条款④ | +| AC-11 | b4_clear_buff 根(clear_default 唯一活行 = defense_up,effect_id 非空——正当量成立)加载通过(加载校验不拒 buff 行) | roll_board → `grant_clear()` | 返回 {category=buff, effect_id=defense_up}(分流数据面);B1 调 B3 apply_effect 与运行时满层拒绝(零兑付 + "奖励未生效"明细行,合法例外口径)归 B1×②/B3,OOS;失衡预警④清图 buff 零兑付单独计数 = playtest 域 OOS | E13 · F3 | +| AC-12 | b4_golden 根、roll_board(b4_list_B〔4 格〕) 完成 | 腿 1:按规范序逐格 grant(连锁批量受理的冒烟构造序——B1 义务①同侧等价);腿 2:grant 返回快照与 grant 时表值对照;腿 3:同格再 grant | 腿 1:逐格返回 = 登记身份序列(受理序 = 规范序);buff 类格返回 {category=buff, effect_id}(B4 只定"给哪行"——层数语义归 B3 合并产生,施加经 B1 调 B3,Rule 3);道具类格(damage_item/heal_pack)返回 {category, power}(治疗包 = 道具类路由、非自动兑付——grant 不触发任何使用语义);即时类格(instant_aoe/instant_heal)同样返回 {category, power} 快照——grant 零路由差别,当场兑付的编排归 B1×②(B4 半 = 返回快照非库存语义);腿 2:返回的 power/effect_id = grant 时表读值(E10 快照时点 = grant——差分判别在 AC-15);腿 3:同一快照 + 诊断(受理恰一次归 B1×②——E18);满层起逐格"奖励未生效"行协议 = OOS B1×②(E3 后半) | Rule 3/7 · E3 前半 | +| AC-13 | b4_golden 根、roll_board + grant(道具格) 完成、冒烟(扮演 B1)stock ×N(N = 登记大数 64) | 腿 1:query_inventory;腿 2:retire_mapping() → roll_board(新清单) → query_inventory;battle_over(映射退役)后 query_inventory;腿 3:clear_inventory() → query → stock | 腿 1:恰 N 件、每件 {item_id, source{cell, round}, 快照投影}(来源归因保 P2——D1 窗口数据契约);N 件全部 InStock、零拒绝(数量无上限——Rule 4/E17,稀缺性由权重承载;无 cap API 负空间);腿 2:query_inventory 逐字不变(含来源——库存关卡域跨图且跨战斗延续,与映射生命周期正交;跨战斗构造 = 共享容器重 roll,切片连续关卡模式同型);映射退役后逐字不变(库存不再随战斗清空——义务⑤ 改写的行为面);腿 3:true、query 空(不跨关卡);此后 stock → false + 诊断(AC-3 分支 3 已载——注记) | Rule 4/5 · E6/E17 | +| AC-14 | b4_golden 根、库存含刀(damage_item)与治疗包各 ≥2 件 | 腿 1:`use(刀)`;腿 2:同句柄再 use;腿 3:use(刀) 于任意游戏态前置(含"道具段无存活敌"等价构造——B4 无敌态输入面,实现若存在存活/血量守卫则本分支失败);对照腿:use(治疗包) 同前置;腿 4:任意序 use 多件 | 腿 1:true + 回执 {category, power=该实例授予时快照值〔E14〕, source{…}};同 reward_id 多件实例回执 power 逐字相同(恒定表值非 roll 值——F4);实例状态 → Used;腿 2:false + 诊断(Used 不可逆);Used 实例 query_inventory 保留只读可查("已消耗未生效"行的数据面——B2 Dead 句柄同款);腿 3:恒 true + 标记 Used、零效果由 B1 判定(选定即承诺:已选刀照常消耗、逐件"已消耗未生效"明细行归 B1 编排读 B2 存活集〔义务④〕+流水,OOS);相位守卫不在本分支豁免域——Unrolled/Cleared/Retired 相位的 use 照旧 false + 诊断(同输入同答案);use(治疗包) 同样无条件受理(满血守卫归 B1 编排读 B2 hero hp——F4/义务④;"拒绝不烧包"半 = B1 不调 use 的编排纪律 + usable_now 预判呈现,OOS);腿 4:每件独立受理、Used 序 = 调用序(窗口选用序的 B4 半——选择收集归 D1×②、段内细则归 B1×①;同窗顺序多包的逐件动态守卫归 B1×④) | Rule 5 · E9/E10 | +| AC-15 | b4_golden 根 roll_board + grant(道具格) + stock 完成(已授予实例)、另设未授予格(映射中 reward_id 引用) | 经测试通道换根加载 b4_reloaded(`load_tables(root, collect_only=true)` 直调——非 D2 `reload()` 路径,门语义归 D2 AC-16/B1,B2 AC-11 同款);再对未授予格 grant;分支 3:对返回快照投影原地改写;分支 4:换根后 use(已授予实例) | 已授予实例快照投影逐字不变(授予时冻结);未授予格 query_identity 的 reward_id 不变(映射仅持引用——热重载拒绝门 D2 EC8 的 B4 侧纵深);对未授予格 grant → 返回漂移后表值(快照时点 = grant——引用非活值的判别后半);分支 3:容器内实例投影不变(快照拷贝非活引用——B2 AC-11 分支 3 同款);分支 4:回执 = 授予时快照值非漂移值(AC-14 腿 1"快照值"措辞的行为级判别) | Rule 9 · E14 | +| AC-16 | RewardTable schema 类(行级 + 跨字段)class_name 直引实例(不经 load()/preload()) | `validate()` 作用于越域代表值:category ∈ {resource, aux_item}(预留值拒绝——B2 pattern 先例)、pool_tag = "clear_fog"(clear_ 命名空间预留拒绝——Rule 6,A1 elite_ 同款)、pool_tag = "nonsense"(未知枚举)、use_timing ∉ {pre_settlement}、weight ∈ {−1, 1000}、tier ∈ {0, 9}、power ∈ {−1, 1000}、跨字段 buff 行 effect_id 空(buff⇒必填)、跨字段非 buff 行 effect_id 非空(非 buff⇒恒空)、跨字段 item 类 power=0(damage_item/heal_pack ⇒ power ≥ 1——F4 加载校验);再作用于域内代表值:weight=0、buff 行 power 任意、tier ∈ {1,8}、item 类 power ∈ {1,999} | 越域各含对应域错误类别(零静默);域内零诊断(防过度约束锁死内容——weight=0 与 buff 行 power 不校验是承重点) | Rule 1/9 | +| AC-17 | 致命根 ×8(独立进程、游戏通道——D2 AC-2b 协议):b4_pool_empty_safe(safe_cell 全 0 权)/ b4_pool_empty_fog(全行 tier < T_fog_min——雾态空)/ b4_clear_empty(clear_default 池空/全 0 权)/ b4_clear_no_value(clear_default 唯一活行 = power 0 道具行——恒非空的可判定形式,A3 条款④表侧兑现)/ b4_no_live_buff_row(safe_cell 有活行但零活 buff 行——tier_min_buff 无定义,F1 对偶致命)/ b4_dangling_effect(buff 行 effect_id = "ghost_buff"——B3 StatusEffectTable 无此行;条件性夹具:默认根已含 effect_id 引用行 ⇒ 悬空校验已激活——dangling_ref_policy 首引兑现点)/ b4_floor_supply(K_dmg ≥ 1 ∧ safe_cell 池无 power ≥ P_min 活刀行——F5 加载护栏 (i))/ b4_nodamage(K_dmg = 0 ∧ 全池零活刀行——F5 加载护栏 (ii) 零伤害世界后闸,B2 OQ9 并案) | 各根经 `--data-root` 注入加载(collect_only=false);对照腿:默认根加载 | 各进程非 0 退出且错误清单含对应类别;默认根干净通过(引用全解析——defense_up 存在) | F1/F3/F5/F6 · E13 | +| AC-18 | ConfigDB 默认根加载;分支 2:b4_attackup_row 根(safe_cell 追加 attack_up 引用行) | 读 RewardTable;分支 2:该根加载 → roll_board → grant(attack_up 格) | category 值域 ⊆ {buff, damage_item, heal_pack}(三值封闭定值);pool_tag 值域 ⊆ {safe_cell, clear_default}(MVP 二值封闭定值);clear_default 池 ≥1 正当量活行(默认根通过自身表级校验的行为断言——AC-17 对偶);权重比例与数值不作定值断言(provisional 归 C3);默认根当前不含 attack_up 行 = 内容态事实、非结构禁止(随奖励改造批重审);分支 2:加载通过、roll_board/grant 正常受理(授予 buff 行 = {category=buff, effect_id=attack_up} 快照——消费于主角战斗段先手攻击,B1 域 OOS;B2 AC-14 的 atk 域断言跨引) | Rule 1 · E16 · F4 | +| AC-19 | 默认根加载(或调参后根——对账随内容态重跑) | `equivalence_supply()`;分支 2:fog 池最低档活行 tier 推导对账 | 返回全活行投影(F6 扩面)与 tier_min_buff = min{活 buff 行 tier}(默认根 = defense_up_basic 行 t=2——登记对账常数;行集空 → 加载致命,AC-17 no_live_buff_row 已载);分支 2(A4① 锚对账——档位序检查):fog 池最低档活行 tier(min{tier : weight>0 ∧ tier ≥ T_fog_min},均表读值推导)≥ tier_min_buff(约束①"雾内奖励档位下界 ≥ 最低档安全格 buff 当量"的 B4 侧可判定形式 = 档位序检查——真当量换算归 B1 条款①对照表〔未设计〕,落定后本分支升级为当量对账〔缺口预检 2〕;当前内容态不等式成立为登记对账记录,C3 调参后须重跑——登记对账对 (knife_adv, attack_up_basic) 漂移检测;锚参照对象 = attack_up 当量,attack_up 入池时 tier_min_buff 重算、对账对随批重制;两行互异 = 非平凡判别——判据名"档位序检查"已入 registry terminology;tier 与当量零耦合的假阳性边界见 F2 注记);A4 约束②③ = MVP 结构性不可闭合/静默绿灯风险——依赖节显式宣告 + B1 检查点——OOS | F6 · A4① | +| AC-20 | ConfigDB 默认根加载完成 | 脚本解析 `design/registry/entities.yaml` | ① rng_service.scope_event_binding 的 reward_roll 语义 = "每次 board install(格奖励批 + 清图奖励批)"(Rule 2 的回写落点——AC-1 scope 观测面的登记侧);② stream_seed_derivation.golden_vectors 含 reward_roll m=42 scope0 向量(derive_at 测试通道的登记前提);③ variation_table.pricing_constraints 注记存在(A4 三约束为 B4 验收项的登记);④ status_effect.dangling_ref_policy 注记含 "B4 RewardTable 首个 effect_id 引用" 条款(AC-17 条件性夹具激活条件的登记侧)+ status_effect.anchor_warning / clause3_supply.direction_precheck 含 attack_up 参照回迁注记(registry 同批回写);⑤ reward_table 条目 fields 九键(reward_id/category/effect_id/display_name/power/weight/pool_tag/tier/use_timing)与 ConfigDB RewardTable schema 字段名逐一相等、MVP 内容注记与默认根行集一致(与 AC-18 对账);⑥ reward_config 条目存在,fields 四键(t_fog_min/k_dmg/n_draft/p_min)与 ConfigDB RewardConfigTable schema 字段名逐一相等;生效前提 = 回填完成(不写"若存在"软口径——条件化使一致性检查落空,B2 AC-16 同款);边界:本 AC 断言 registry 登记面——D2 GDD 正文(Core Rule 5 / Interactions B4 行)与 registry 的对齐 = delta 复核对账项(缺口预检 4——GDD 散文不进自动化断言面) | Rule 9 · 接口漂移登记 | +| AC-21 | B4 文件集(映射容器/库存容器/RewardTable schema/roll 与保底逻辑/冒烟脚本/夹具规格)纳入 D2 AC-9 模式项目级扫描域成员清单(owner Technical Setup——B3 先例;本条分支 1 承载登记义务,清单更新 = Technical Setup 门禁项,缺口预检 7) | 静态扫描 | randomize() / RandomNumberGenerator / 自建 RNG 零命中(零随机出 roll_board 一线——唯一 RNG 入口 = open_stream("reward_roll"));ConfigDB 外 data 表加载零命中(schema 直引实例化豁免——AC-16 前提);extends Control / .tscn preload 零命中(Core 层零 UI 债——headless 实跑 AC-1 合成判定);A2 事件订阅 API 零命中(Rule 10 零主动订阅);B2/B3 API 符号零引用(相邻零运行时依赖——含冒烟脚本;StatusEffectTable/HeroTable 经 ConfigDB 表读合法、API 引用非法);重掷类命名扫描(reroll/resign/rebind 等——Rule 8 负空间,warning 级 + 人工复核,D2 AC-14 纪律同款);零随机的行为级硬证据 = AC-9 | Rule 10 | + +### 覆盖矩阵 + +| 类别 | 映射 | +|---|---| +| Core Rules | 1→AC-16/AC-18 · 2→AC-1/AC-9/AC-10(恰一次编排→OOS B1×⑥)· 3→AC-12(施加与满层零兑付协议→OOS B1×②+B3 跨引)· 4→AC-13 · 5→AC-14(段序/窗口时机/满血守卫/零效果判定→OOS B1×①④;窗口呈现→D1×②)· 6→AC-10/AC-11/AC-16(clear_ 拒绝)/AC-17 · 7→AC-12 分支 1(道具类路由)/AC-14 分支 3(满血守卫→OOS B1×④;clamp→E12 OOS)· 8→AC-2/AC-15(未开格呈现→OQ2/A5+D1)· 9→AC-16/AC-17/AC-18(接口漂移回写→AC-20①⑤)· 10→AC-9/AC-21(调用权集中→OOS B1×①–⑥) | +| Formulas | F1→AC-1/AC-5(+AC-9 轨迹)· F2→AC-6(+AC-19 锚侧)· F3→AC-10/AC-11/AC-17 · F4→AC-14(恒定表值供给面)+AC-18(attack_up 行合法性 = 复活口径)——施加/clamp/守卫/不吃修正纪律→OOS B1×④ · F5→AC-7/AC-8/AC-17(加载护栏 ×2)· F6→AC-19(闭合归 B1——OOS) | +| Edge Cases | E1→AC-4 · E2→AC-3 分支 2+OOS B1×⑤(零明细行半)· E3→AC-12(受理序半)+OOS B1×②(行协议 + 聚合计数半)+B3 AC 跨引(apply false 原子)· E4→AC-3 分支 1/2 · E5→AC-1 分支 3 · E6→AC-9 分支 2(roll 半)+AC-13 分支 2(库存半)· E7→AC-3 分支 3/4 · E8→API 负空间(无强制使用入口——不可 G-W-T,B3 Rule 8 先例)+OOS D1×② · E9→AC-14 分支 2 · E10〔照常消耗〕→AC-14 分支 3(B4 半)+OOS B1×④(零效果判定+"已消耗未生效"行+击杀结果流水)· E11〔清图 buff〕→AC-11(B4 半)+OOS B1×② · E12→OOS B1×④(clamp)+AC-14 分支 1(power 供给)· E13→AC-17(b4_dangling_effect 条件性夹具)· E14→AC-15 · E15→OOS B1×①(授予先于判定的段序半)+AC-10 分支 2/AC-13 分支 3(可构造序弱承载注记)+OOS D1×③⑤(消亡语境注记)· E16→AC-18 分支 2(attack_up 行合法——复活口径)· E17→AC-13 分支 1/2 · E18→AC-12 分支 3/AC-10 分支 3 · E19→API 面(清单外 null/false + 诊断)· E20→API 面(stock 受理域封闭)· E21→API 面(枚举序 = stock 受理序——D1 呈现序契约) | +| 上游承接 | A4①→AC-19 分支 2(夹具对账型,升级缺口预检 2)· A4②③→MVP 休眠显式宣告(依赖节)+ OOS B1 强制检查点 · A3 条款④恒非空→AC-10 分支 1(兑付面)+AC-17 分支 4(表侧)· B3 dangling_ref 首引激活→AC-17 分支 6+AC-20④ · 奖励身份预读→AC-2/AC-15(呈现→OOS D1×①)· attack_up 行合法性→AC-18 分支 2(复活口径)· A3 条款⑤支配性→OOS B1 强制检查点(预警⑤⑧观测) | +| 覆盖口径 | 21 条 EC 中 E8/E15 两处非 G-W-T 形态,分别以 API 负空间与 B1×① 段序 deferral 承载(B2 EC5/EC13/EC14 豁免先例同款);其余全部映射 AC 或显式 OOS(deferral 生效条件 = B1×6/D1×5 义务清单登记);"负空间"学说的边界如实登记:封闭枚举防住"没有的入口",不自动防住"有入口、语义未裁"——E18–E21(重复受理族/清单外坐标/stock 域/枚举序)为评审批补裁的输入域语义;今后新增 API 入口须随附全输入域语义表(实现期 ADR 检查项) | + +### 缺口预检(11 项——每项标 owner 与时机) + +| 编号 | 缺口与处置 | owner | 时机 | +|---|---|---|---| +| 1 | 【阻塞级】B1 编排半整体 deferral:段序骨架(授予>窗口>增益/减益>道具>战斗>衰减>判定——窗口锚点 = 授予段后)、窗口选择序回放输入、满血守卫(拒绝不烧包)+ 同窗顺序多包逐件动态守卫、零效果判定与"已消耗未生效"行、击杀结果进流水、"奖励未生效"行 + 聚合计数、治疗 clamp、clear_inventory/retire/roll_board 恰一次编排——B4 冒烟全部以构造序扮演、编排正确性零断言;E10 的 B1 侧承接(读 B2 存活集做零效果判定)尤须逐字核对 | B1 GDD(义务清单 6 项,缺项即 blocking) | B1 门禁评审 | +| 2 | 【阻塞级】A4① 当量升级与②③状态:AC-19 分支 2 为档位序检查——真当量换算依赖 B1 条款①对照表(未设计);②在 MVP 结构性不可闭合(依赖节显式宣告——非常量池无法随雾负担上浮)、③静默绿灯风险;激活载体 = clear_ 插槽(垂直切片) | B1 | B1 GDD 强制检查点 + 对照表落定/每次调参时核对(F2 验收锚口径)+ clear_ 激活时重启② | +| 3 | T_fog_min 推导式悬置(OQ5):表值默认 2 provisional——B1 对照表落定后改推导式或维持表值;若改动,AC-6 夹具(b4_fog_gate tier 布置与 override 分支)及登记轨迹重制 | B1 | OQ5 关闭 | +| 4 | registry/D2/索引/game-concept 回写(评审批已兑现):reward_roll scope 语义改写(registry + D2 正文两处补同步——AC-20① 生效前提)、锚参照迁移三处传播(systems-index 注记 / registry anchor_warning / clause3_supply.direction_precheck)、game-concept 三处 MVP 口径注记、systems-index 分层行 B4←D2 + 门禁块同步——delta 复核时逐处对账 | 本 GDD 评审批 | 已办,delta 复核确认 | +| 5 | attack_up 复活连锁(OQ1)——已兑现:基础攻击恒存在(HeroTable.atk 域 [1,999]、0 加载拒绝)→ attack_up 复活,消费者 = 主角战斗段先手攻击(B1 GDD 同批修订);AC-18 改承载 attack_up 行合法性、AC-19 锚参照回迁 attack_up 当量、registry anchor_warning/direction_precheck 同批回写;残余 = attack_up 是否入默认根池(随奖励改造批,C3) | C3(残余入池裁决) | 已兑现;残余随奖励改造批 | +| 6 | 登记常数随调参重制流程:b4_golden 冻结权重与默认根解耦,但 T_fog_min/K_dmg/N_draft/P_min/默认根行值调参 → AC-19 对账(对账对 (knife_adv, attack_up_basic) 漂移检测)、AC-6 分支 4、AC-18 结构断言须重跑;C3 调参流程若无"登记常数与对账重制"步骤则相关 AC 假失败 | C3(调参)+ qa(重制) | 每次调参 | +| 7 | B4 扫描域成员清单登记:AC-21 分支 1 的生效载体 = D2 AC-9 项目级 CI 扫描域成员清单更新(B3 先例) | Technical Setup | B4 GDScript 产物落地即登记 | +| 8 | 参考算术工具锚定:期望映射 Ψ(σ)/Φ(σ)/重 roll 轨迹的手工推导需"播种生成器取号序列"参考算术——D2 仅登记 scope 种子向量(未登记逐号序列黄金向量);参考算术以冒烟脚本内置、版本库登记,正确性以 D2 AC-3 种子向量 + AC-5 区间算术手工核验锚定;生成器取号约定(如 `randi % W` vs `randi_range`)为实现期 ADR 事实,定名时同步登记;登记后第二人独立推导复核(A2 OQ3 同型)+ b4_reroll 可达性拆分复核(首稿零刀可达/耗尽稀有——失效即重制夹具) | qa/Technical Setup | 冒烟框架搭建 + 实现期 ADR | +| 9 | D1 呈现义务 deferral:预读呈现(含库存常读条)/使用窗口(含 usable_now 呈现与触发政策)/四类明细行呈现/H2 强调/清空反馈不可 G-W-T——deferral 生效条件 = D1 义务①–⑤登记(缺项即 blocking) | D1 | D1 GDD | +| 10 | C3 调参期批量仿真:F5 概率性致命带(合法权重下 N_draft 耗尽概率随 K/池构成可达非平凡值——种子轮盘)→ 调参流程须含批量仿真(刀供给分布 × A1 板分布的可行率),把配置错误暴露在调参台;与预警①–⑧预注册合并执行候选 | C3 | 每次调参 + playtest | +| 11 | 方向预检输入缺口:刀供给方向预检的 \|资格格\| 分布待 A1 actuals p10/p50 → 落地后重跑(方向预检表登记对账记录);max_hp=50 原型锚出处建议随 B2/D2 回写补登 | C3/B2 | A1 actuals 交付 | + +## 开放问题(OQ) + +| 编号 | 问题 | owner | +|---|---|---| +| OQ1 | attack_up 入池裁决(复活已闭合,残余入池问题):基础攻击恒存在(B2 Rule 9)→ attack_up 复活、消费面 = 主角战斗段先手攻击;attack_up 行是否进入默认根 RewardTable 池随奖励改造批裁决(复活批不擅自入池);A3 条款 2 锚参照对象已回迁 attack_up 当量(F4 口径后果) | C3 | +| OQ2 | 未开格身份呈现深度:MVP 不呈现(Rule 8);A5 探测道具零成本桥接(身份 install 期已定可查);呈现深度(类别/档位/身份)待 A5 定稿 | A5+D1(垂直切片) | +| OQ3 | items_per_settlement_cap 启用裁决:Tuning Knobs 结构位默认关;启用即限制(过低锁死 3 单位 54 击杀需求胜局——economy-designer 警告) | C3 playtest | +| OQ4 | 关卡末剩余道具折算(关内延续裁决后重述):"战斗结束库存清空"的体验负债主体闭合(库存关卡域:跨战斗延续、未选用件不再随战斗消亡;层间延续可见反馈已入切片);残余 = 关卡末剩余道具折算/继承结构位(转金币/继承——Alpha,C2;清空前快照计数已作为折算数据面预留) | C2/D5(Alpha) | +| OQ5 | T_fog_min 推导式:依赖条款①对照表(未设计,加载期循环依赖不可行);落定后改推导式或维持表值(F2,默认 2 provisional) | B1 | +| OQ6 | weaken 锚裁决联动:B3 OQ1 归一口径裁决前置(瞬时 vs 总暴露);若落"总暴露"读法则 B4 重定最低档参照(registry anchor_warning;F6 供给已备行值清单与量纲建议) | B1 | +| OQ7 | 失衡预警预注册归口:预警①–⑧ 阈值与样本量 provisional;与 A3 OQ3、原型 v3 终验 playtest 合并执行候选 | C3/playtest 会话 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-9 回执,registry 同批回写):**clear_blast 清图槽换位**(原 clear_knife 入库刀→instant_aoe p40 当场兑付全体存活敌、不入库不占可选类供给、完美 ×2)· **K_dmg 默认 1→0 + "C3 禁区默认值"条款解除**(刀保底语义由供给控制器战斗首件保证取代,no_damage_row 后闸保留)· **RewardConfigTable +2 参数**(active_p0=0.4 / active_decay=0.5)+ pool_tag 枚举增 active_inject · **供给控制器机制本体承接登记**(RewardSupplyController——install 期注入,E[战斗获取]≈1 标定目标)· 默认根行集重登记(减伤墙批 defense 24→16 + 注入池权重 60/30/10);B1 条款⑤计价基元随换位重推(C = power = 40——clause5_dominance.c3_ruling) | +| v1-lean | 2026-09-13 | lean 格式迁移:legacy 原稿按 B1 lean 样例重排——修订注记出正文收敛至本表;散文/枚举/边界情况/义务清单/失衡预警/覆盖矩阵/缺口预检/AC 全部转表格;流程转字符画;F1–F6 公式、Variables、验算数据与 21 条 AC 全保留;语义与 2026-09-12 对齐状态一致,零规则变更 | +| 摘要同步 | 2026-09-12 | 六处旧口径摘要对齐关卡域权威版(Interactions D1/B5 行、边界情况、依赖、Tuning Knobs、Visual/UI、覆盖声明)——零语义变更 | +| 两级奖励批 | 2026-09-11 | 用户裁决:奖励按交付轴两类(即时 75% / 可选 25%,枚举扩至五值);库存生命周期改关卡域(跨战斗延续、关卡末清空——原战斗域口径退役);治疗两级稀缺化(heal_pack 权重 6%、power 12);道具使用窗口单件制;buff 时长改当次结算(attack_up/defense_up D=1);attack_up 复活(HeroTable.atk 基础攻击恒存在、域 [1,999]、0 加载拒绝)——默认根八行重登记、F5 供给闸 P_min=8、AC-18/AC-19 改写、锚参照回迁 attack_up 当量 | +| full design-review 修订 | 2026-09-09 | 五专员对抗 + creative-director 终审 NEEDS REVISION——14 条 blocking 同日修订核销:道具使用窗口锚点移至授予段后(当轮拾取当轮可用)、B4 配置值落 RewardConfigTable、D1 义务 3→5 扩容、E18–E21 输入域语义补裁、reward_roll scope 语义改写回写(registry/D2 同步)、失衡预警重校准(①拆短缺/浪费双指标、④按吸收上限重推、⑤补雾板分层、新增⑥⑦⑧)、缺口预检 11 项;CD 情绪定位终审与预读/寻路 MVP 呈现面如实降级同批落位;待 delta 复核放行 | +| v1 | 2026-09-09 | lean 门禁一次会话定稿(systems-designer / qa-lead 代理起草):F1–F6 + 21 条 AC + B1/D1 义务清单 + 失衡预警预注册落位;核心拍板——奖励身份 install 期预分配、结算段序(增益/减益 > 道具 > 战斗)、奖励分流与幂等受理、RewardTable/RewardConfigTable schema | diff --git a/design/gdd/legacy-2026-09-14-v1/c2-adventure-structure.md b/design/gdd/legacy-2026-09-14-v1/c2-adventure-structure.md new file mode 100644 index 00000000..b7fbd148 --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/c2-adventure-structure.md @@ -0,0 +1,316 @@ +# C2 冒险结构(Adventure Structure) + +> **状态**:Approved · lean 格式 +> **版本**:v1 · 2026-09-14 · 作者:DK + design-system +> **承载支柱**:P4 地牢下探的探险氛围(主——序列推进 = 不断下探更险的层)· P3(恢复点/囤积决策的冒险级容器)· P1("这局我到第几层"的冒险级对照物) +> **一句话**:C2 管一次冒险的骨架——关卡序列怎么排、通关给什么、死了怎么重来;位于 C1 关内行进之上、B1 战斗结算之下。 +> **复核注记**:v1 solo 出稿 → 经 /design-review(--solo 快速审批)APPROVED(0 blocking / 3 recommended——recommended 随下次修订批执行);专员/总监代理未咨询——生产前人工复核保留。评审日志:`design/gdd/reviews/c2-adventure-structure-review-log.md`。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 冒险/序列层编排:冒险生命周期、关卡序列、跨战斗 HP 账本、关卡大奖励、失败重开——C1 关内行进的上层结构,B1 战斗结算的入参供给方 | +| 拥有 | 冒险状态机与结果语义 · 关卡序列定义(LevelTable 行集内容 owner + 推进规则)· HP 账本(跨战斗持久性的 C2 半边兑现)· 关卡大奖励 · 失败重开语义 · master_seed 接线(冒险 = 种子域单位)· 冒险摘要 | +| 不拥有 | 关内节点行进与节点兑付触发(C1)· 战斗结算与关卡清库调用(B1)· 奖励内容与库存语义(B4/C3)· 数值曲线(C3)· 代币持久化与天赋(C4/D3)· 呈现(D1) | +| 上游输入 | C3 期望表与带锚(逐关通关率带 = 序列难度排布)· C3 L2 深度轴行值(序列行集扩展随批填充)· D2 LevelTable schema / ConfigDB / RngService · B1 出口分发语义(level_clear 上层调用面) | +| 下游输出 | 给 C1 的接口契约四件套 · begin_battle 入参供给(关卡序号 / hero_hp / 上限覆盖值)· 冒险摘要(C4 代币入账与 D3 快照的消费位) | +| 硬约束 | 零随机(零流消费、单一种子注入点)· 序列覆盖(冒险关数 ⊆ 表内容覆盖——越界关卡 = 内容生产缺口)· 死亡即冒险终(Roguelike 语义已锁)· 不碰库存域(关卡末清库由 C1 收束点触发 B1 level_clear) | + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P4 下探节奏 | "又清完一层,再往下" | 序列推进 + 关卡大奖励 = 下探途中的喘息台阶;每关一个可命名的新层(display_name),难度台阶可感知(雾解锁 / 数值跳升 / 通关率带下移) | +| P3 恢复点囤积 | "Boss 前把血囤满" | HP 账本跨战斗延续 + 恢复点节点按比例回血——血是冒险内唯一带得走的资源,恢复点位置就是决策压力点 | +| P1 冒险级掌控 | "这局我到第二层" | 冒险摘要(到达层 / 通关数 / 代币记账)给每次死亡一个可对照的坐标;重开 = 新种子新局面,不是同一关的复读 | +| 锚点时刻 | "这层清完了" | Boss 倒下 → 大奖励兑付 → 下一层开门的节拍;死亡 → 摘要结算 → 一键再来 | + +## 全局流程 + +### 冒险生命周期 + +``` +新冒险(Idle) + │ ① reseed(master_seed)——RngService 种子注入,冒险域单位恰一次 + │ ② HP 账本置满 · 序列位置 = 1 + ▼ +start_level(1) ──► 关内行进(C1 域:节点链逐节点推进) + ▲ │ + │ ├─ battle/boss 节点 ─► C1 组装 begin_battle(L, D, hero_hp, override) → B1 + │ ├─ reward 节点 ─► B4 节点奖励入库(关内域库存) + │ └─ rest 节点 ─► C2 账本恢复(比例 = rest_heal_pct) + │ │ + │ 关卡收束点 = Boss 战胜利 ∨ 英雄死亡 + │ │ C1:调 B1 level_clear(库存关卡域清空 + 快照计数) + │ ▼ + │ level_end{result: cleared | defeated}(C1 → C2) + ├────────────────────────────────────────────┐ + │ cleared 且当前关 < N │ defeated ∨ abandoned + │ ▼ │ ▼ + │ 关卡大奖励(上限提升 · 血回满 · 代币记账) │ 冒险失败结算 + │ ▼ │ + └─ start_level(L+1) │ + │ │ + └─ cleared 且当前关 = N ──► 冒险完成结算 │ + │ │ + ▼ ▼ + 冒险摘要 → Idle(重开 = 全新一轮) +``` + +| 出口 | 触发 | 动作序列 | 约束 | +|---|---|---|---| +| 下一关 | level_end(cleared) 且当前关 < N | 大奖励兑付 → start_level(L+1) | 大奖励在清库之后(C1 收束点已先调 B1 level_clear) | +| 冒险完成 | level_end(cleared) 且当前关 = N | 冒险结算(不再发上限提升——没有下一战可受益) | 摘要标记 run_completed | +| 冒险失败 | level_end(defeated) | 冒险结算 | 库存已由收束点清空;血条归零即终 | +| 冒险放弃 | 玩家发起、非战斗期受理 | 冒险结算(结果 = abandoned) | 已获大奖励不追回;与失败同型 | + +## 核心规则 + +| # | 规则 | 要点 | 依据 | +|---|---|---|---| +| 1 | 冒险从第一关开始,倒下即终、清完末关即成 | 冒险 = 从序列第 1 关起步的完整一轮;关卡通关(Boss 战胜利)→ 自动进下一关;末关通关 = 冒险完成;任意战斗死亡 = 冒险结束。MVP 线性序列——无跳关、无回头路 | game-concept 冒险语义;C3 Boss 通关编排归属(C1/C2 域) | +| 2 | 每关是一条预置节点链,冒险关数不得超出表内容覆盖 | C2 为 LevelTable 行集内容 owner——链 = 战斗/奖励/恢复/Boss 节点依序排列、Boss 恒为末节点;战斗节点深度 = 前序战斗节点计数(Boss = Dmax);冒险关数 = 行集关数,加关 = 内容批(链行 + 敌我数值 + 变异解锁同批落,禁止只加序列不加内容);序列难度排布消费 C3 通关率带(L1→L2 带下移 + 雾解锁台阶) | D2 跨表校验;C3 期望表与 OQ1;A4 unlock_level=2 | +| 3 | 主角一条血走完整个冒险,关卡只换地图不换血条 | 账本收支四口——冒险开始置满 / 每战开始交出 / 战后收回 / 恢复点与大奖励入账;关卡切换零结算(血条跨关延续);血 = 冒险内唯一跨战斗资源(buff 战斗域清、库存关卡域清、RAP 战斗域清) | B2 hp_persistence 归 C1/C2;B1 begin_battle 入参;B4 关内延续同构 | +| 4 | 清完一关,下探前发一次大奖励:上限提升加回满 | 兑付时点 = 关卡收束清库之后、下一关开始之前;构成三件——HP 上限提升(+level_clear_hp_bonus)、血回满、代币记账加一份;不进背包不占库存域;末关通关不兑付 | B4 库存关卡域裁决;C3 区间平移换算率;C4 记账位 | +| 5 | 主角死亡冒险即终,重开是全新冒险不是关卡重试 | 失败 = 冒险终(无关卡重试、无检查点——Roguelike 语义已锁);重开 = 全新一轮(新种子 / 第 1 关 / 满血 / 空手);中途放弃与失败同型结算(已到手的保留);保留面 = 冒险摘要,清零面 = 账本 / 库存 / 种子域 | game-concept"死亡则冒险结束";A3 RAP 战斗域生命周期 | +| 6 | 一次冒险一粒种子 | 种子注入时点 = 冒险开始恰一次;冒险内雷图生成 / 奖励抽取 / 遭遇掷骰全部派生自该种子;冒险内不重置(切片逐关播种为其无冒险层的历史简化,本条转正为冒险域单种子);冒险可复现域 = master_seed + 三流计数器 | registry rng_service 快照契约;D3 消费方 | +| 7 | 关卡收束在关内宣布,冒险层只接结果 | C1 拥有关内一切行进(节点移动 / 兑付触发 / 战斗发起——参数组装:深度取链位、关卡序号与血条取 C2 下放);收束点(Boss 胜或死亡)C1 先调 B1 level_clear、再向 C2 宣布结果;C2 零战斗域直调、零库存域直调 | B1 出口分发"关卡结束 = 上层显式调用";切片 level_runner 先例转正 | + +#### 规则 2 细则:深度标号与覆盖 + +| 项 | 语义 | +|---|---| +| 节点类型 | battle / boss / reward / rest——reward、rest 不占深度、不查战斗表;battle、boss 占深度 | +| 深度标号 | 战斗节点深度 = 该节点之前(含同链)已排 battle/boss 节点数,从 0 起计;Boss 恒为链末节点且深度 = Dmax | +| 链模板(MVP) | battle(D0) → battle(D1) → reward → battle(D2) → battle(D3) → rest → boss(D4)——每关 7 节点 5 战斗深度;L1/L2 同构复用,模板修订随内容批 | +| 覆盖义务 | 冒烟断言:行集内每个 battle/boss 节点 (L, D) 在 enemy / board_gen 两表存在精确行(D2 跨表校验复用 + 序列关数断言)——序列关数 ⊆ 内容覆盖,越界 = 内容生产缺口 | +| 生效前提 | N=2 行集扩展前提 = C3 L2 深度轴行值填充批落地;未填充期冒险以单关行集运行(N=1 合法态——切片现状) | + +#### 规则 4 细则:大奖励构成 + +| 件 | 内容 | 载体 | 数值 | +|---|---|---|---| +| 上限提升 | max_hp_eff += level_clear_hp_bonus(冒险内生效——经 begin_battle 覆盖值传入后续每战) | C2 账本 + AdventureTable | +10 provisional(C3 区间平移换算率锚:+10hp ≈ +1 轮) | +| 血回满 | HP 账本 = 提升后 max_hp_eff | C2 账本 | 结构定式(非 knob——与 C3 满血开局校准口径闭合) | +| 代币记账 | tokens += token_per_level | 冒险摘要 | 10/关 provisional(消费方 C4) | + +#### 规则 7 细则:C1/C2 编排边界(给 C1 的接口契约——C1 GDD 强制输入) + +| 接口 | 方向 | 载荷 | 时点 | +|---|---|---|---| +| start_level(level_index, ctx) | C2 → C1 | ctx = {level_index, hero_hp, hero_max_hp_eff} | 冒险开始 / 每次关卡推进 | +| report_battle_end(hp) | C1 → C2 | 战后英雄 HP 终值(胜负皆报) | 每场战斗结束后 | +| heal_clamped(pct) | C1 → C2 | rest 节点恢复请求(比例取关卡行 rest_heal_pct) | 恢复点节点兑付 | +| level_end{result, hp, node_index} | C1 → C2 | result ∈ {cleared, defeated}(C1 已先调 B1 level_clear) | 关卡收束点 | +| 冒险放弃(abandoned) | D1 → C2 | 无载荷 | 非战斗期受理(MVP 无 UI 入口——语义预留) | +| 战斗发起组装 | C1 编排 | begin_battle(L, D, hero_hp, hero_atk, hero_max_hp):L / hero_hp / hero_max_hp 取 C2 下放,D 取链位,hero_atk 传表值通道(MVP 冒险内不提升攻击) | battle/boss 节点行进至 | + +## 枚举 + +### 冒险结果(封闭枚举) + +| 值 | 触发 | 摘要标记 | +|---|---|---| +| completed | 末关 level_end(cleared) | run_completed = true | +| failed | level_end(defeated) | run_completed = false | +| abandoned | 玩家放弃(非战斗期) | run_completed = false | + +### 关卡收束结果(封闭枚举) + +| 值 | 触发 | 后续 | +|---|---|---| +| cleared | Boss 节点战斗胜利 | 末关判定 → 大奖励 + 推进 / 冒险完成 | +| defeated | 任意战斗英雄死亡 | 冒险失败结算 | + +### 冒险摘要(结算产出,消费方 C4 / D3 / playtest 日志) + +| 字段 | 类型 | 语义 | +|---|---|---| +| run_completed | bool | 冒险是否打穿序列 | +| levels_cleared | int | 已通关数(代币记账乘子) | +| level_reached | int | 止步关卡序号(失败 / 放弃时 = 当前关) | +| tokens_recorded | int | levels_cleared × token_per_level | +| master_seed | int64 | 本冒险种子(复盘 / 快照域标识) | + +## 数值结论与推导 + +> C2 是结构系统——运行时零新增公式,以下为账本结算式与设计期合成式;全部数值 provisional,调参所有权见 Tuning Knobs。 + +### F1 冒险通关率合成(设计期工具式) + +**结论**:P(冒险通关) = Π P(L_i 通关);MVP N=2 合成带 = [6%, 20%](L1 [30,50]% × L2 [20,40]%)——冒险长度即难度总闸。 + +#### 推导 + +``` +P_run = Π_{i=1..N} P(L_i) (逐关独立满血开局口径) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 各关通关率 | P(L_i) | float | (0,1) | C3 期望表逐关目标带(L1 [30,50]% / L2 [20,40]% / L3+ [10,25]% 预留) | +| 冒险关数 | N | int | [1, 行集关数] | LevelTable 行集关数——MVP 2 | + +| 验算 | 读数 | 判定 | +|---|---|---| +| N=2 合成 | [0.30×0.20, 0.50×0.40] = [6%, 20%] | 单次通关率带合理(Roguelike 体感:多数局止步中途) | +| 口径闭合 | C3 带按每关满血开局校准;大奖励血回满使该口径跨关成立 | ✓(若未来大奖励改部分恢复,本式失效须改口径) | + +### F2 HP 账本结算式 + +**结论**:账本三写口全部 clamp 于 [0, max_hp_eff];大奖励回满使每关开局恒满血——血条跨关只降不自动回,恢复只来自恢复点与大奖励两个显式口。 + +#### 推导 + +``` +战后回写: hp := report_battle_end(hp) (victory 腿 hp ∈ [1, max_hp_eff];defeat 腿 = 0 → 冒险终) +恢复点: hp := min(hp + floor(max_hp_eff × rest_heal_pct ÷ 100), max_hp_eff) +大奖励: max_hp_eff := max_hp_eff + level_clear_hp_bonus; hp := max_hp_eff +战斗输入: begin_battle(hero_hp = hp, hero_max_hp = max_hp_eff, hero_atk = 表值通道) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 账本血量 | hp | int | [0, max_hp_eff] | C2 冒险域持有;战斗内由 B2 承载 | +| 生效上限 | max_hp_eff | int | [表值, 表值+加成×已通关数] | HeroTable.max_hp + Σ大奖励提升 | +| 恢复比例 | rest_heal_pct | int | [0,100] | LevelTable 逐关行字段(切片 30 转正 provisional——数值复验归 C3) | +| 上限提升 | level_clear_hp_bonus | int | [0,999] | AdventureTable 单行全局(默认 10) | + +| 验算(示例轨迹,表值上限 50) | 账本读数 | +|---|---| +| 冒险开始 | 50/50 | +| D0–D3 逐战后(示例) | 43 → 31 → 24 → 17 | +| 恢复点(30% × 50 = 15) | min(17+15, 50) = 32 | +| Boss 胜后(假设余 8)→ 大奖励 | max_hp_eff = 60、HP = 60 | +| L2 开局 | 60/60 满血(C3 带口径闭合) | + +### F3 冒险代币记账式 + +**结论**:tokens = levels_cleared × token_per_level——通关与中途死亡同式,差异只在 run_completed 标记;通关加成位预留(MVP 不启用,裁决归 C4)。 + +#### 推导 + +``` +tokens_recorded = levels_cleared × token_per_level (token_per_level 默认 10,消费方 C4) +``` + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| 通关数 | levels_cleared | int | [0, N] | 摘要字段 | +| 每关代币 | token_per_level | int | [0,999] | AdventureTable 单行全局——值归 C4 | + +| 验算 | levels_cleared | tokens | +|---|---|---| +| 死在 L1 | 0 | 0 | +| 清 L1 死在 L2 | 1 | 10 | +| 打穿 N=2 | 2 | 20 + run_completed 标记 | + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Idle | 冷启动 / 冒险结算完成 | 唯一出口 = 开始新冒险(reseed → 置账本 → start_level(1)) | +| RunActive | start_level(L) 受理 | 关内行进委托 C1;C2 只守账本写口与收束监听;放弃请求非战斗期受理 | +| Settling | level_end 或 abandoned 受理 | cleared 非末关 → 大奖励 → start_level(L+1) 回 RunActive;末关 cleared / defeated / abandoned → 摘要产出 → Idle | + +不变式:RunActive 期恰一关卡在飞;master_seed 冒险内恒定;HP 账本 ∈ [0, max_hp_eff];Settling 恰一次(重复宣布拒绝 + 诊断);冒险内零流消费(RngService open_stream 零命中)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| 末关 Boss 战胜利 | 冒险完成结算——不发上限提升(无下一战可受益)、无 start_level(N+1);代币照记 | 规则 1/4 | +| start_level 请求超出行集关数 | 防御 fail-fast(序列配置违约);运行时战斗表 floor 兜底属 D2 既有语义——C2 不产生越界请求,玩家不可达越界关 | 规则 2 / C3 边界情况表 | +| 行集战斗节点无对应 enemy/board_gen 行(内容缺口) | 冒烟断言拦截(覆盖义务);未填充期以单关行集运行(N=1 合法态) | 规则 2 细则 | +| level_end 重复宣布 | 幂等:已 Settling 的冒险拒绝重复兑付 + 诊断,账本与摘要零变化 | 状态不变式 | +| 恢复点满血触发 | 回复 0、合法(账本静默——无明细行协议,与战斗域"奖励未生效"族不同层) | F2 | +| 大奖励时上限已有加成 | 累加(每关一份;N=2 冒险至多一次) | 规则 4 | +| 放弃请求在战斗中 | 拒绝(战斗域单事务在飞;入口呈现仅非战斗期可达——MVP 无 UI 入口) | 规则 5 / 规则 7 细则 | +| 英雄死亡但战斗判定为胜利 | 结构不可达(B1 判定 hero 先判);防御断言:cleared 收束要求账本入账 hp > 0 | 规则 3 / B1 判定段 | +| 重开种子与上局相同 | 合法态(熵源小域);约束 = 种子生成禁走玩法随机通道、冒险内计数器随 reseed 归零——种子值生成源裁决归实现期 ADR | 规则 6 / 开放问题 | +| 冒险内跨关遗留物查询 | 无遗留——buff 战斗域清(B1)、库存关卡域清(C1 收束点)、RAP 战斗域清(A3);冒险层账本与摘要是唯一跨关状态 | 规则 3/7 | +| 关卡行缺失(LevelTable 无该 level_index 行) | fail-fast——序列配置事故非玩家可达态;与战斗表 floor 兜底分属两层 | 规则 2 | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| C3 | hard | 期望表逐关通关率带 = 序列难度排布输入;L2 深度轴行值(enemy / board_gen 的 (2, D1–D4))随批填充 = N=2 生效前提;大奖励 / 恢复点数值联动复验(区间平移换算率锚) | +| D2 | hard | LevelTable schema(切片建表——C2 为行集内容 owner)· AdventureTable 新表走 D2 接口漂移规则(单行全局,C2 回填 owner)· ConfigDB 单点读取 · RngService 种子注入入口(新面——义务登记)· 跨表校验复用 | +| B1 | hard(下游消费面) | begin_battle 入参 (L, D, hero_hp, hero_atk, hero_max_hp) 供给方转正 = C1 组装 / C2 供值;level_clear 调用方 = C1 收束点(C2 零直调);战后 HP 读回 = B1 呈现面英雄投影(载荷自包含) | +| B2 | 间接 | hero hp_persistence 归 C1/C2——本 GDD 兑现 C2 半边(账本);B2 单位接口零变更 | +| B4 | 间接 | 库存关卡域清空触发源 = C1 收束点;节点奖励兑付归 C1;大奖励不进库存域(零接触) | +| A4 | 间接 | 雾 unlock_level=2 = L2 台阶的内容面(序列难度排布消费) | + +**下游**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| C1 | hard | 接口契约四件套 + 收束点语义 + 战斗发起组装权(规则 7 细则表全文)——C1 GDD 强制输入,门禁评审核对项;切片 level_runner 为其实现先例(冒险域职责切出后归 C2 侧新 owner) | +| B1 | 下游(消费 C2 供值) | begin_battle 入参面;B1 依赖表 C1/C2 provisional 行由本 GDD 收编转正 | +| C4 | 远 | 冒险摘要 = 代币入账位;冒险开始参数面 = 天赋注入预留位(MVP 空参数——开始新冒险时注入,结构同 begin_battle 覆盖值通道) | +| D3 | 远 | 快照契约域单位 = 冒险(master_seed + 三流计数器);摘要字段含 master_seed 供复盘 | +| 呈现层(D1) | soft | 冒险摘要结算画面 / 关卡推进指示 / 放弃入口(非战斗期)——数据源 = 本 GDD 摘要与状态投影 | + +**跨 GDD 义务清单(对侧修订批逐项核对,缺项即阻塞级按各自门禁)**: + +| 对侧 | 内容 | 状态 | +|---|---|---| +| C3 | L2 深度轴行值填充(enemy 四行 + board_gen 四行,(2, D1–D4))——OQ1"内容与数值同批"机制的兑现批 | N=2 生效前提 | +| D2 | RngService 种子注入入口登记(reseed——provisional 名,实现载体随 ADR) | 义务登记 | +| D2 | AdventureTable 建表(单行全局两字段——Rule 4 豁免,HeroTable/SettlementTable 先例;C2 回填 owner) | 接口漂移 | +| B1 | begin_battle 入参供给方转正(依赖表 C1/C2 行收编——本 GDD 为 C2 半边定义源) | 转正登记 | +| registry | level_table / adventure_table / hero_override_parameter_passing 三条目登记(本定稿批执行) | 本批 | +| C1 | 接口契约四件套 + 收束点语义全款承接(C1 GDD 门禁评审核对——C2 在 C1 之前设计,本表即其接口基线) | 待 C1 定稿核对 | +| C4 | token_per_level 记账消费 + 天赋注入位消费 | 远(C4 期) | + +## Tuning Knobs + +| 字段 | 类型/域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| LevelTable 行集(N 与链模板) | level.tres 逐关行 | L1 现行 + L2 随批 | ✓(结构) | 加关 = 内容批(链行 + C3 数值 + 变异解锁同批);过长 = 内容缺口,过短 = 冒险时长不足(愿景 20–40 分钟 vs MVP 两关,真人数据后定扩展节奏) | +| LevelTable.rest_heal_pct | int [0,100] 逐关行 | 30 | ✓ | 恢复点比例;数值复验归 C3;100 = 恢复点变全满泉、0 = 死节点 | +| AdventureTable.level_clear_hp_bonus | int [0,999] 单行全局 | 10 | ✓ | 大奖励上限提升;值锚 = C3 平移换算率(+10hp ≈ +1 轮);上调须 C3 重扫 L2 带 | +| AdventureTable.token_per_level | int [0,999] 单行全局 | 10 | ✓ | 代币记账;消费方 C4 | +| 链模板节点构成 | level.tres nodes 数组 | 战×2→奖励→战×2→恢复→Boss | 内容批 | 节点类型序列修订 = 内容批(随 C3 数值同批验带),非调参 | + +- 非 knob(结构冻结):序列线性无跳关、Boss 恒末节点、死亡即冒险终、大奖励三件构成、冒险内零流消费、收束点编排边界。 +- Knob 交互:level_clear_hp_bonus × C3 L2 通关率带(回满 + 上限共同抬开局强度——上调走 C3 重扫);rest_heal_pct × 通关余量带(C3 通关余量 p10 ≥ 3 的对冲杠杆)。 + +## 验收标准 + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | 冒险 Idle | 开始新冒险 | reseed 恰一次注入新 master_seed;HP 账本 = 表值上限满血;start_level(1) 受理;L1 行集加载零诊断 | +| AC-2 | RunActive 当前关 < N | level_end(cleared) | 大奖励恰一次:max_hp_eff += level_clear_hp_bonus、账本 = max_hp_eff、tokens += token_per_level;随后 start_level(L+1) | +| AC-3 | RunActive 当前关 = N | level_end(cleared) | 冒险完成结算:无上限提升、无 start_level(N+1);摘要 run_completed = true、tokens = N × token_per_level | +| AC-4 | RunActive 任意战斗 | 英雄死亡 → level_end(defeated) | 冒险失败结算;摘要 levels_cleared = 已通关数、level_reached = 当前关;账本封闭 | +| AC-5 | 冒险结算后 | 开始新冒险 | 全状态重置(账本 / 序列位置 / 库存域空 / 摘要新生成);reseed 新种子注入 | +| AC-6 | RunActive 全程 | 逐战核对入参 | begin_battle.hero_hp = 账本当前值、hero_max_hp = max_hp_eff;战后账本 = 英雄终值投影(载荷自包含) | +| AC-7 | MVP 全行集 | 冒烟跨表断言 | 每关 battle/boss 节点 (L,D) 在 enemy / board_gen 有精确行;序列关数 = 行集关数 | +| AC-8 | C2 文件集 | 静态扫描 | RngService 流消费(open_stream)零命中;reseed 每冒险恰一次调用 | +| AC-9 | Settling 已受理 | level_end 重复宣布 | 拒绝 + 诊断;账本与摘要零变化 | +| AC-10 | 生效前提未兑现(L2 行集缺行) | 冒烟运行 | 以单关行集载体运行(N=1 合法态);N=2 断言显式 SKIP 非 FAIL | +| AC-11 | RunActive 战斗中提交放弃请求 | 请求受理判定 | 拒绝;非战斗期受理 → abandoned 同失败型结算、已获大奖励不追回 | +| AC-12 | 大奖励后首战 | begin_battle 入参核对 | hero_max_hp = 旧上限 + level_clear_hp_bonus(覆盖值通道);hero_atk 走表值通道(MVP 恒不覆盖) | +| AC-13 | rest 节点兑付 | 满血 / 缺血两腿 | 恢复 = floor(max_hp_eff × rest_heal_pct ÷ 100) clamp 上限;满血腿回复 0 合法 | + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| OQ1 | 冒险长度扩展节奏——MVP N=2(约 15–25 分钟)vs 愿景单局 20–40 分钟;扩关 = 内容批,节奏按真人单关时长数据定 | C2 + C3 | playtest 批次 2 后 | +| OQ2 | 大奖励形态扩展(三选一 / 攻击型 / 通关加成)——AdventureTable 扩列结构位已留,设计随 C4 元进度 | C2 + C4 | C4 GDD | +| OQ3 | master_seed 生成源(熵源通道禁走玩法随机——种子值生成器归属) | D2 / 实现期 ADR | 实现期 | +| OQ4 | 放弃入口呈现与受理细节(仅语义已定:非战斗期受理、同失败型结算) | D1 | D1 / UX 批 | +| OQ5 | L2 display_name 表现层命名(provisional 名"第二层 · 雾漫巢径"——风格对齐 L1"苔藓回廊") | C2 表现层 | L2 内容批 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1 | 2026-09-14 | 初版 solo 出稿:七核心规则(冒险骨架 / 序列覆盖 / HP 账本 / 大奖励 / 失败重开 / 种子接线 / C1-C2 编排边界)+ F1–F3 + 13 AC + 给 C1 接口契约四件套;MVP N=2 + AdventureTable 单行全局 + N=2 生效前提登记;切片 level_runner 关卡域先例切分转正(冒险域归 C2、关内行进归 C1) | +| v1.1 | 2026-09-14 | 评审通过状态同步:状态头 In Design → Approved、复核注记更新(--solo APPROVED + 日志路径登记);零规则变更 | diff --git a/design/gdd/legacy-2026-09-14-v1/c3-difficulty-curve.md b/design/gdd/legacy-2026-09-14-v1/c3-difficulty-curve.md new file mode 100644 index 00000000..5a3f4b3a --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/c3-difficulty-curve.md @@ -0,0 +1,526 @@ +# C3 难度曲线与精英/Boss(Difficulty Curve & Elite/Boss) + +> **状态**:In Design · lean 格式 +> **版本**:v2 · 2026-09-14 · 作者:DK + design-system +> **承载支柱**:P4 地牢下探的探险氛围(主)· P1 推理的掌控感 / P3 风险与收益同源(难度即压力源) +> **一句话**:C3 定义"难度 = f(关卡序号, 节点深度, 精英/Boss 修饰)"中每一项随进度如何变化——敌人强度曲线、雷图压力曲线、奖励供给曲线与精英/Boss 修饰语义的唯一数值所有者。 +> **复核注记**:v1 solo 出稿 → 经 /design-review(lean 单会话)NEEDS REVISION 8 blocking,同日修订核销(本 v2);专员/总监代理未咨询——生产前人工复核保留。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 数值 owner 而非机制 owner:表结构归 D2、生成算法归 A1、结算规则归 B1、敌种机制归 B2——C3 以行值与参数驱动已定稿机制,形成"越下越险"的进度体感(P4);玩家不直接感知本系统(Indirect),它是体感背后的统一坐标系 | +| 拥有 | EnemyTable / BoardGenTable / RewardTable / RewardConfigTable / HeroTable 行值与参数 · DifficultyTable 修饰结构位(MVP 空表)· 难度期望表(按 (L,D) 的期望胜轮带 / 期望承伤占 max_hp 比 / 通关率目标带 / 通关余量带——调参的"正确行为"基准)· **生存链区间模型**(替代关级供给带:每战 S/K 区间三档判定 + 关级通关恒等式 + 输出占比带)· 七步调参流程 · P_perfect 乘数调参权 · m_w 调参通道(终值共裁 B4/B1)· B1 对账约束清单的承接与消解 | +| 不拥有 | 表结构(D2)· 生成算法(A1)· 结算规则与护甲克制矩阵机制(B1——C3 投值)· 敌种机制(B2)· 变异机制框架(A4——C3 只投内容行)· 精英节点完整语义(C1 联动)· 元进度天赋数值(C4——C3 是其世界侧对照物)· 呈现(B2/B5/D1)· encounter 掷骰执行(B1 编排)· 供给控制器机制本体(B4——C3 投参数) | +| 上游输入 | D2 表 schema 与 R1 解析 · B1 条款③⑤对账输出(B1 F4/F5 = 约束生成器)· B2 字段语义与校验域 · B4 联动重算义务与供给控制器机制 · A1 F4 批量红线 · 切片 v3 行集实测数据(含减伤墙批) | +| 下游输出 | 各基表行值(经 ConfigDB 单点读取)· 每战 S/K 区间与三档判定数据面(level_monte_carlo actuals 生产,B4/B1 对账消费)· 难度期望表(C2 难度排布 / C4 天赋标定基准——区间平移换算率:+10hp ≈ +1 轮、atk 翻倍 ≈ K 减半)· unlock_level 投放计划(A4)· "何时变难"数据面(B2/B5/D1 呈现消费) | +| 硬约束 | 运行时零新增公式(C3 侧数值直填基表行;机制本体——encounter 掷骰 / 护甲克制 / 供给控制器——归各 owner GDD,C3 只投行值与参数)· 可推理率 target ∈ [0.85, 1] 绝对锚不可下探 · 条款③⑤与预警⑥约束内调参或显式扩界 · 联动网络六组禁独立调参 · 每次修订强制走七步流程 · 全部值 provisional 起步随 playtest 修正 | + +## 体验锚点 + +> Indirect 体验一句话:**玩家不操作本系统,但每一分钟都在感受它**——第二层的熊地精比第一层的地精更凶、关底 Boss 的压迫性血量、"这一层开始有雾了"的解锁节奏,本系统是这些体感背后的统一坐标系。 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P4 越下越险的压迫节奏 | "这一层开始有雾了" | 每层一个可感知难度台阶:新敌种登场 / 新行为环段 / 新变异解锁 + 基准数值跳升(L 轴)与温和加压(D 轴);台阶可感知但可解释——跃升可归因于可见变化而非纯数字膨胀(后者产生"被坑"感而非"变险"感);可量化形态 = S/K 区间随 (L,D) 的漂移轨迹(绿→黄→红空间分布) | +| P1 攻克的对照物 | "我变强了" | 首关基线明显吃紧但多数玩家能过(通关率目标带);后续关卡陡升使 C4 天赋提升产生可测量的通关期望改善——区间平移换算率(+10hp ≈ +1 轮、atk 翻倍 ≈ K 减半)使"我变强了"从体感变为可推导结论 | +| P3 压迫的顶点 | "Boss 房开门前" | 恢复点/囤刀决策压力顶点——供给稀缺性在此处兑现价值;Boss 血量压迫由 Boss 血量锚承载 | +| 锚点时刻 | "三拍 vs 单拍" | 第二层首遇熊地精——三拍行为环 vs 地精单拍攻击,难度跃升有具体面孔;击杀 Boss——"这层我算下来了"的完成感 | + +- 参考感受合成:Peglin 的地图难度爬升 × 杀戮尖塔的 Act 结构(普通→精英→Boss 三段节奏)→ 本作 = 关间大台阶 × 关内小坡度 × Boss=Dmax 关底压轴。 + +## 全局流程 + +### 难度曲线生成(设计期直填 → 运行时直读) + +``` +设计期(本 GDD 唯一产出物 = 表行值) + │ ① L 轴台阶:每关 ≥1 项新内容投放(新敌种/新环段/新变异)+ base 跳升 k_L ∈ [1.3, 1.6] + │ ② D 轴坡度:同敌种 hp 平缓上移(坡度域)+ 板参数温和加压(target ≥ 0.85 绝对锚) + │ ③ Boss 行:Dmax 直填行(数值大台阶 + 专属行为环 + 本关最大板形) + │ ④ 七步调参流程走查 → 行值写入 enemy / board_gen / reward / reward_config / hero .tres + ▼ +运行时(C3 零公式;机制归各 owner) + │ ⑤ B1.begin_battle 实例化期:unit_count 区间均匀骰(encounter 封闭流)→ 本战实际数量 + │ ⑥ B2.spawn(L, D):R1 floor 解析 → 行值快照 → DifficultyTable 修饰套乘(快照之前;MVP 空表无修饰) + │ ⑦ A1 读 BoardGenTable 行生成 → 挖掘期 → B1 结算(既有乘区:护甲克制 / 状态池 / 充能伤害) + ▼ +战斗/挖掘按既有系统运作(C3 运行时零公式) +``` + +### F7 调参流程(每次行值修订强制走完——七步) + +``` +① 改表值(C3 直辖 knob) + │ ② smoke_all 全绿(含 A1 生成质量闸——d0.28 回退先例的拦截点) + │ ③ level_monte_carlo 扫描(通关率带 + 三档判定 + 通关余量 + 输出占比) + │ ④ B4 登记常数与对账重制(AC-19 对账对漂移检测 / AC-6 分支 4 / AC-18 重跑——B4 义务 #10) + │ ⑤ 对账复算(条款③⑤ / 预警⑥) + │ ⑥ README 调参日志登记(含期望偏离) + ▼ +行值生效(任一步 FAIL → 回滚或登记 OQ——禁止带病前进) +``` + +### 出口分发 + +| 出口 | 触发 | 动作/去向 | 约束 | +|---|---|---|---| +| 调参通过 | 七步全 PASS | 行值生效 → 期望表复算 → 调参日志登记 | 期望偏离必须登记,禁止静默出带 | +| 调参失败 | 任一步 FAIL | 回滚行值或登记 OQ | 禁止带病前进 | +| 红档组合 | 任一战 S_max ≤ K_min | 扫描步拦截,行值禁止入表 | 配置事故——区间模型判定 | +| 期望表偏离 | 调参后越出通关率/余量带 | 回滚或 OQ 登记 | 禁止静默出带(状态表同名行) | +| 投放断档 | 某关无新敌种且无新环段 | 流程投放检查项拦截 | 内容生产缺口——C3 调参无法修补(数值不是内容) | + +## 核心规则 + +| # | 规则 | 要点 | 依据 | +|---|---|---|---| +| 1 | 难度承载结构 = 直填行值主体 + DifficultyTable 修饰结构位 | 全部 (level_index, node_depth) 维度最终数值**直填**各基表行(EnemyTable / BoardGenTable / RewardTable——切片已验证的工作方式:R1 floor 解析直读、零新机制);**DifficultyTable 仅承载"按坐标施加的全局修饰"结构位**(键 (L,D)、乘数域;施加点 = B2.spawn 实例化时套乘于行值快照之前)——MVP 不产生任何修饰行(空表结构位;首个使用场景 = 精英节点;Boss 走 Dmax 直填行、不走修饰表) | D2 接口漂移规则;registry `difficulty_table`;B2 实例化条款"接口未定"预留由本条兑现 | +| 2 | 难度 = f(L, D) 双轴语义 | **L 维(关间)= 大台阶**——每关至少一项新内容投放(新敌种 或 新行为环段 或 新变异解锁)+ 基准数值跳升;**D 维(关内)= 小坡度**——同敌种数值平缓上移(坡度域见关内深度坡度公式)、板参数温和加压(尺寸/密度小步上调,推理率 target 不低于 0.85 绝对锚);**Boss = 各关 Dmax 行**(A1 登记约定兑现)——数值大台阶 + 专属行为环 + 专属板形,见 Boss 遭遇语义条 | B2 投放节奏规则(OQ8);Boss 血量锚公式(F3);A1 第三轴登记约定 | +| 3 | 敌种投放节奏 | 每关至少一个新敌种或既有敌种新环段投放;MVP 投放 = L1 三段(goblin→bugbear→ogre_chief,行值总表);**DND 更名批生效**:grub→goblin、ambusher→bugbear、boss_guardian→ogre_chief——registry/B2/D1/art-bible 键集随本修订批对齐;多敌种同深度选择规则:MVP 每行集恰一敌种(B2 锁定),C1 分支地图定稿时若需同深度多敌种选择,走预登记改键通道——本 GDD 预留不定义 | B2 投放节奏规则(OQ8);B2 多敌种共裁项(OQ7);D1 键集冲突② | +| 4 | 切片行集转正为 MVP 默认曲线 | 垂直切片调参登记的行集有实测依据,转正为 MVP 默认值——**权威枚举 = 「MVP 行值总表」(v3,出处 = 切片 .tres)**,旧登记表(grub 时代值)由本表替代;后续 L2+ 曲线数值随关卡内容扩展填充(结构本条已定,数值行随内容扩展) | MVP 行值总表;L2+ 数值开放问题(OQ1) | +| 5 | Boss 遭遇语义 | ① Boss = 关卡末节点 = Dmax 行(LevelTable 末节点必须 Boss——schema 校验);② Boss 数值台阶 = hp 取本关普通战斗 hp 期望总和的 0.5–1.1 倍 + **atk ≥ 本关普通战最高行 atk**(本次重定义——原"高于均值一档"无档位定义;现值 Boss 9 ≥ bugbear 9 ✓)+ 专属行为环(含 charge 拍——玩家可规划的安全挖掘拍);③ Boss 板 = 本关最大板形 + 密度上探(受可推理率 target 绝对锚约束);④ Boss 战胜利 = 关卡通关(C1/C2 域编排) | Boss 血量锚公式(F3,加难走廊 [1.1, 1.35] 预登记);LevelTable schema 校验;A1 第三轴登记约定 | +| 6 | 精英节点(MVP 不实现,语义预留) | `elite_` / `boss_` 前缀字段命名空间预留保留;精英修饰完整语义(独立节点类型/词缀系统/出现规则)**归 C1 关卡节点地图定稿后的联动设计**——分支地图中"同深度并存精英与普通节点"触发棋盘生成端强制重审(改键走 D2 接口漂移规则);本 GDD 承诺:DifficultyTable 结构位 + 命名空间预留足以承载,不预写语义 | A1 第三轴登记约定(命名空间预留 + 定稿强制重审) | +| 7 | A4 全局变异语义(scope=global 兑现) | `scope=global` 行 = 该变异**施加于整板全部合格格**(区别于 region 的区域布置);**全局变异仅 Boss/精英图可产生**(普通图禁用——加载期校验:global 变异的布置行须伴随 elite/boss 标记;MVP 无标记字段前 = MVP 不产生 global 行,A4 既有 warning 承载);MVP 不定义任何具体全局变异行(岩浆/冰冻无 MVP 容器——A4 锁定),本条只锁定语义框架与禁用面 | A4 scope=global 预留位条款;A4 例外表 warning 行 | +| 8 | 遭遇随机化(区间 unit_count) | EnemyTable 行 `unit_count` 升级为**区间语义** [unit_count, unit_count_max](域 [unit_count, 3],B2 上界锁定不变);掷骰经 RngService `encounter` 封闭流——**接口漂移登记完成**:registry 流集合含 encounter、B1 begin_battle 掷骰增补、B2 spawn override 参数与 unit_count_max 字段增补、D2 schema 两字段增补;细则见下表 | registry `rng_service`(encounter 增流登记);B1/B2/D2 增补条款(X-6~X-8);遭遇随机化细则表 | +| 9 | 调参约束内工作纪律(B1 对账输出承接) | C3 每次行值修订必须走满七步调参流程,且不得触碰已锁约束边界——四条边界见下表(B1 条款③⑤对账输出与预警⑥ = "禁止独立调参"纪律的量化边界,C3 调参必须在其内工作或显式扩界;registry 回执见依赖节对账登记表) | F7 七步流程;B1 条款③⑤对账输出;对账登记表 | + +### MVP 行值总表(权威枚举,出处 = 切片 .tres v3) + +| 关卡 | 深度 | enemy_id | display_name | hp | atk(区间) | unit_count(_max) | pattern | armor_type | +|---|---|---|---|---|---|---|---|---| +| L1 | D0 | goblin | 地精 | 24 | 7 [4,10] | 1 (2) | [attack] | none | +| L1 | D1 | goblin | 地精一伙 | 13 | 5 [2,8] | 2 (3) | [attack] | none | +| L1 | D2 | bugbear | 熊地精 | 40 | 9 [6,12] | 1 | [attack, charge, heavy] | a | +| L1 | D3 | bugbear | 熊地精双雄 | 18 | 6 [3,9] | 2 (3) | [attack, charge, heavy] | a | +| L1 | D4 | ogre_chief | 食人魔酋长 | 150 | 9 [6,12] | 1 | [attack, attack, charge, heavy] | b | +| L2 | D0 | bugbear | 熊地精 | 30 | 7 [4,10] | 1 | [attack, charge, heavy] | a | + +| 表 | 转正行集(登记值) | +|---|---| +| BoardGenTable L1 深度轴 | D0/D1 10×10 · D2–D4 12×12 · d0.25/t0.88 · attempts 24 | +| HeroTable | atk = 6 · max_hp = 50 | +| RewardTable 要点 | 格子池四行即时类(attack_up 20 / defense_up 16 / spark_aoe 16 / heal_minor 15)· 注入池三行可选类(knife_t1 60 / knife_adv 30 / heal_pack 10——供给控制器注入)· clear_blast 40(clear_default 槽,instant_aoe 当场兑付,完美 ×2) | +| RewardConfigTable | t_fog_min=2 · k_dmg=0(保底语义由战斗首件保证取代)· n_draft=32 · p_min=8 · active_p0=0.4 · active_decay=0.5 | +| SettlementTable 增补字段 | damage_a_per_cell=1 · damage_a_cap=8 · damage_b_per_flag=2 · armor_effectiveness 克制矩阵(base/dmg_a/dmg_b × none/a/b——特化 ×150 / 无甲 ×100 / 对位 ×50)· p_perfect=200 · D_ref=12 | + +- 实测依据:期望扫描 N=15–30 × 多种子 + 8 种子密度实验 + 试玩批次 1 + 减伤墙批。 +- v3 数值批(试玩批次 1 裁决):atk 4/4/6/4/7 → 7/5/9/6/9(约 +50%,承伤侧主杠杆);D0 24 保持防秒杀(20hp 会被首轮盲挖粉尘秒杀——12+9);Boss 130→150(用户拍板可上调)。 +- **区间批(现行)**:奖励五行 power 区间化(heal_minor [2,6] / knife_t1 [5,11] / heal_pack [6,18] / knife_adv [10,22] / clear_blast [25,55]——身份批内 roll,中点恒等原值)+ 敌方 atk 区间化(上表 [min,max],遭遇骰同流 spawn 期整战持久,±3 provisional)——宽度均 provisional 归本 GDD 测量迭代;A3 条款①当量锚以中点口径维持;验收口径 = 完美策略档 0 加成通关 ≤40%(OQ7)。 +- **加难走廊(带锚定稿预登记)**:L1 基线通关率目标 [30%, 50%],现扫描 60% 越上界——杠杆候选 = r_boss 上探走廊(Boss 180–207,见 Boss 血量锚公式)+ 群战上探 + 供给 decay 0.5→0.6;**扫描验证待执行(OQ7——本会话无 godot 运行时),落带前 v3 值维持 provisional-pending-scan**。 + +### 遭遇随机化细则(Core Rule 8) + +| 项 | 语义 | +|---|---| +| 区间语义 | unit_count 升级为 [unit_count, unit_count_max];unit_count_max 可省略——缺省 = 定值,旧行零迁移 | +| 域 | [unit_count, 3](B2 unit_count 上界锁定不变) | +| 掷骰时点 | B1.begin_battle 实例化期(扫雷开始前)按区间均匀骰定本战实际数量(B1 GDD 增补条款) | +| 敌种不变 | 同敌种掷骰——不触碰 B2"每行集恰一敌种"锁定 | +| 随机通道 | RngService `encounter` 封闭流(registry 流集合已登记);scope_event_binding = 每次战斗实例化恰一 scope;发生在挖掘决策域之外(P1 合规) | +| 消费方式 | B1 掷骰 → B2.spawn 以 override 参数接收实际数量(B2 零随机纪律保持——收到的即确定值;B2 GDD 增补条款) | +| 教学位豁免 | 新敌种首遇节点固定数量不加骰(D0 goblin ×1、D2 bugbear ×1 固定——行值总表) | +| 可回放性 | 同种子全程可回放(流取号确定性不变) | +| 设计原则 | 数量是比单体质变更优先的深度表达(关内深度坡度公式 Example 延伸);期望总量对齐关级恒等式检算 | + +### 调参约束边界(Core Rule 9) + +| # | 约束边界 | 内容 | +|---|---|---| +| ① | 条款③满配破线 | unit_count>1 的行须满足"降密度或降 m_amb"补偿——registry clause3_supply 已登记;切片群战行 hp/atk 下调为合规先例 | +| ② | 条款⑤清图支配性 | 约束包①需求对 clear_blast(instant_aoe,C = power = 40)重推:缺口 = 46.1−40 = 6.1(L1)/ 91.8−40 = 51.8(L2)——登记为非支配设计态 + OQ4 playtest 观测弃图率(原 clear_knife C=8/16 模型由现行登记替代) | +| ③ | 可推理率绝对锚 | target ∈ [0.85, 1] 不可下探(P1 生成端保证) | +| ④ | 期望表对照 | 调参后期望表必须复算并在 README 调参日志登记偏离 | + +## 枚举 + +### DifficultyTable(新表,MVP 空表——结构位) + +| 项 | 值 | 语义 | +|---|---|---| +| 键 | (level_index, node_depth) | 按坐标施加的全局修饰 | +| 乘数域 | [0.1, 10](registry 预登记) | 套乘施加点 = B2.spawn 实例化时、行值快照之前;越域 → clamp 至域界 + warning(registry 登记的显式例外——组合事故非配置事故,非 fail-fast 场景) | +| MVP 行集 | 空(零修饰行) | 首个使用场景 = 精英节点;Boss 走 Dmax 直填行、不走修饰表;建表走 D2 接口漂移规则 | + +### EnemyTable.unit_count 区间语义(Core Rule 8) + +| 字段 | 域 | 语义 | +|---|---|---| +| unit_count | int [1, 3] | 区间下界;旧行语义 = 固定数量 | +| unit_count_max | int [unit_count, 3],可省略 | 区间上界——缺省 = 定值,旧行零迁移;掷骰 = B1 实例化期区间均匀骰 | + +### RngService 流集合(encounter——已登记) + +| 流 | scope 绑定 | 消费点 | 语义 | +|---|---|---|---| +| encounter | 每次战斗实例化恰一 scope | B1.begin_battle 实例化期(扫雷开始前) | unit_count 区间均匀骰——同敌种数量随机;发生在挖掘决策域之外(P1 合规);同种子全程可回放 | + +### A4 变异 unlock_level 门控序列(投放计划归 C3) + +| 序 | 内容 | 门控 | 说明 | +|---|---|---|---| +| 1 | 普通图(无变异) | 基线 | — | +| 2 | 雾 | unlock_level = 2(A4 锁定) | 雾参数 f≤8d / ≤0.25(P1 度量绝对上限) | +| 3 | 岩浆/冰冻 | 随关卡扩展 | 无 MVP 容器(A4 锁定)——投放计划归 C3 | + +## 数值结论与推导 + +> 定位声明:C3 是数据系统——运行时零新增公式(数值直填基表行,运行时仅经既有 B1 乘区 / B2 接口 / R1 直读运作)。本节全部公式为**设计期工具式**(曲线拟合基准、区间估算、对账复算),执行载体 = 调参期批量工具(level_monte_carlo.gd / reward_expectation.gd,见 F7),不进入游戏运行时。全部数值 provisional 起步,随 playtest 修正。 + +### F1 关内深度坡度(D 轴小坡度) + +**结论**:hp_D = round(hp_0 × (1 + s_hp × D))——每档 +5%–20%,Dmax=4 处 1.2–1.8 × D0、Dmax=5 处 1.25–2.0 × D0(同敌种内);MVP 直填行近似此式、不运行时计算;敌种切换(新敌种登场)不受本式约束——台阶值直接定。 + +#### 推导 + +``` +hp_D = round(hp_0 × (1 + s_hp × D)) +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 深度基准值 | hp_0 | int | [1,999] | 该敌种 D0 行 hp | +| 坡度系数 | s_hp | float | [0.05, 0.20] | 每深度档增幅 | +| 节点深度 | D | int | [0, Dmax] | 节点深度(Boss = Dmax) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Example | goblin 24hp:D1 理论 24×1.1 ≈ 26——切片实际以"群×2"(13hp ×2–3 只)表达深度压力:数量是比单体质变更优先的深度表达(保持单拍环低威胁读数、总量受控) | + +### F2 关间台阶(L 轴大台阶) + +**结论**:base(L+1) ≈ base(L).Dmax × k_L、k_L ∈ [1.3, 1.6]——k_L 作用于**同敌种基准**(敌种延续场景,如 L2 bugbear 延续至 L3);新敌种登场时基准按其环复杂度重定(三拍环单位 hp 低于单拍环同关值——切片先例:bugbear 40/18 < goblin 群总量但威胁更高)。 + +#### 推导 + +``` +base(L+1) ≈ base(L).Dmax × k_L k_L ∈ [1.3, 1.6] +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 关间系数 | k_L | float | [1.3, 1.6] | 下一关 D0 基准 = 本关 Dmax × k_L(数值跳升 + 新敌种/新变异共同构成台阶感) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Output Range | 相邻关数值倍率 1.3–1.6;叠加新敌种环复杂度,实际体感台阶更大 | +| 反例 | L1 Dmax 酋长 150hp → L2 D0 bugbear 基准 ≈ 210 的直推不成立——新敌种登场基准按环复杂度重定,k_L 仅适用于敌种延续场景(L2 锚行 30/7 为内容行非 k_L 直推) | + +### F3 Boss 血量锚 + +**结论**:boss_hp ≈ round(Σ普通战斗hp期望 × r_boss)、r_boss ∈ [0.5, 1.1]——"Boss 是本关最大的单场消耗",量级上探至全关消耗;**加难走廊 [1.1, 1.35] 预登记**(带锚定稿——L1 通关率压带首选杠杆,扫描终裁后落带)。 + +#### 推导 + +``` +boss_hp ≈ round(Σ普通战斗hp期望 × r_boss) r_boss ∈ [0.5, 1.1](走廊 [1.1, 1.35]) +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 本关普通战斗 hp 期望总和 | Σ普通战斗hp期望 | float | [1,999]×N | 本关全部非 Boss 节点的敌方 hp 期望合计(**含 unit_count 区间骰期望**——口径与击杀侧恒等式统一) | +| Boss 占比 | r_boss | float | [0.5, 1.1](走廊 [1.1, 1.35]) | Boss 相对全关消耗的血量锚 | + +**验算与边界:** + +| 验算 | 读数 | 判定 | +|---|---|---| +| L1 v3:Σ = 36(D0 [1,2]×24)+ 32.5(D1 [2,3]×13)+ 40(D2)+ 45(D3 [2,3]×18)= 153.5 → Boss 150/153.5 ≈ **0.98** | 带内 | ✓(口径更正:旧验算 Σ=142 为固定数量口径与旧 Boss 值混算,由本行替代;口径与击杀侧恒等式统一) | +| 走廊验算 | Boss 180/153.5 ≈ 1.17、200/153.5 ≈ 1.30 ∈ [1.1, 1.35] | 走廊内 ✓ | +| 败北分布 | 向 Boss 集中(4/6) | 符合压迫预期 ✓ | + +### F4 生存侧:期望承伤与 S 区间 + +**结论**:E[承伤/场] ≈ Σ_rounds Σ_enemies E[beat_dmg] × (1 − E[减伤] − weaken覆盖);对照带 ≤20%/普通场、≤50%/Boss 场。派生**生存区间** S = [(HP进战 + 治疗下界) ÷ 入伤峰值, (HP进战 + 治疗上界) ÷ 入伤谷值]——区间模型三档判定的生存侧输入。 + +#### 推导 + +``` +E[承伤/场] ≈ Σ_rounds Σ_enemies E[beat_dmg] × (1 − E[减伤] − E[weaken覆盖]) +S = [ (HP进战 + 治疗min) ÷ 入伤峰值 , (HP进战 + 治疗max) ÷ 入伤谷值 ] +``` + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 每拍期望伤害 | E[beat_dmg] | float | [0,999] | 敌 atk × 拍别乘数(heavy=2)+ 伏击 flat(+3×层数,帽 4)——行为环确定性轮转 | +| 减伤期望 | E[减伤] | float | [0,0.6] | defense_up 授予率 × 当次减伤(−30×2 层帽 −60%——减伤墙批现行值) | +| weaken 覆盖 | E[weaken覆盖] | float | [0,0.65] | 标中旗 −65% 输出对战斗轮的覆盖期望——**生存侧最大单一杠杆**(P3 兑现点) | +| 入伤峰值/谷值 | d_max / d_min | float | [0,999] | 峰值 = heavy 齐射尖峰 + 伏击满层、零减伤;谷值 = charge 拍 + 全减伤 | + +**验算与边界:** + +| 验算 | 读数 | 判定 | +|---|---|---| +| D0 goblin ×1(7atk 单拍环)每结算恒 7 × (1−0.22 平均减伤) ≈ 5.5/轮 → 裸 R_survive ≈ 50/5.5 ≈ 9 轮 | — | L1 五战 S 区间手算见区间模型验算表 | +| 减伤墙批注 | defense_scan 实测墙轮 39%、真人 12/12 轮零承伤满血通关 → defense_up −50→−30、石肤权重 24→16 | 承伤带复验责任随批(B3 侧注记已挂) | + +### F5 通关率目标带(期望表核心) + +**结论**:通关率按 (L, 天赋档) 目标带验收——**L1 基线 30–50%**(带锚定稿,替换原 40–60%);区间机制解释:+100% atk → K 区间近似减半 → K_max ≈ S_min 转绿档 → ≥95%。**真人方向更正**:批次 1 实测真人显著强于模拟者(v2 真人 100% vs 模拟 80%)——原"真人预期低于模拟"方向声明由本结论更正;带按模拟口径定义,真人系数批次 2 标定(OQ3)。 + +#### 推导 + +**Variables(目标带):** + +| 目标带 | 基线(无天赋) | 中天赋(+100% atk) | 高天赋(+200%) | +|---|---|---|---| +| L1(首关) | **30–50%** | ≥95% | 100% | +| L2 | 20–40%(provisional,随 OQ1 校准) | 60–80% | ≥90% | +| L3+ | 10–25%(provisional) | 40–60% | 60–80% | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| v3 扫描 | 基线 60%——**越 [30,50] 上界** → 加难杠杆批内即调(带锚定稿杠杆:r_boss 走廊 + 群战上探 + decay 0.6 候选);扫描落带前登记 provisional-pending-scan(OQ7) | +| 完美策略档扫描(OQ7;工具 = smoke/auto_player.gd 完美档求解器 + level_monte_carlo 策略轴) | **区间批后复测(2026-09-14,N=30)**:0 加成通关仍 **100%**、剩余 HP 均 47.4/50、承伤均 3.9(区间前 3.5)、踩雷 0.00/局——+50%~+200% 恒 100%;**承伤带与 F5 带对技能轴上界零分辨力**(calibration_gap 定量读数)。实测结论:**奖励侧 + 敌方侧数值区间已全部落地仍压不动完美档**——其结构性优势(零踩雷直伤 + 全标旗 weaken 顶格 + 快杀)使 47/50 血缓冲吞掉全部值域方差;≤40% 验收线需结构性杠杆(承伤硬地板 / weaken 饱和语义 / 完美度定价)或极端宽度(毁基线与可读性,建议否决)——**待用户裁决(OQ7 待拍板主项)** | +| 败北分布 | 向 Boss 集中(4/6)✓;+200% 天赋 100% ✓ | +| 机制钩子 | 天赋列由区间位移承载(K 减半 → 绿档),非独立拍脑袋带 | + +### F6 生存链区间模型(替代关级供给带) + +**结论**:原关级供给带(比值 ∈ [1.1, 2.0])由本模型替代——其验算 0.54"短缺态"与实测 60% 通关矛盾,方向指引反指(对偏易的游戏报"加供给")。现行 = **生存链区间模型**:每战 S/K 区间三档判定(绿=稳定通过 / 黄=期望通过 / 红=配置事故禁止入表)+ 关级通关恒等式 + 输出占比带。设计期工具式,运行时零公式。 + +#### 推导 + +``` +S = [ (HP进战 + 治疗min) ÷ 入伤峰值 , (HP进战 + 治疗max) ÷ 入伤谷值 ] (生存区间——F4) +K = [ (Σ敌hp − 一次性伤害max) ÷ 输出峰值 , (Σ敌hp − 一次性伤害min) ÷ 输出谷值 ] (击杀区间) + +绿档: S_min > K_max —— 最坏也能过,与运气无关(教学位必须绿档) +黄档: E[S] ≥ E[K] + 1 且非绿 —— 有失败分支,期望占优(margin ≥ 1 轮最低阈值) +红档: S_max ≤ K_min —— 最好也打不过,配置事故(F7 第③步拦截) + +关级恒等式: atk × Σ R_survive(i) + Σ奖励输出 ≈ Σ敌hp期望 × (1 + 缓冲) + (actuals 逐项审计闭合——五项输出 × 三项输入全部入账) +``` + +**Variables:** + +| Variable | 来源 | Description | +|---|---|---| +| 入伤 | 表值 + actuals | 敌 atk × 拍别乘数 + 伏击 flat − 减伤 − weaken(F4) | +| 输出 | 表值 + actuals | 基础攻击 6×(1+attack_up) + 伤害A(开格充能 1/格 帽 8,甲A ×1.5/甲B ×0.5)+ 伤害B(标雷充能 2/雷,甲B ×1.5/甲A ×0.5)+ 刀(8/16,不吃 percent) | +| 一次性伤害 | 表值 + actuals | clear_blast 40×清图数(完美 ×2)、囤刀倾泻 | +| 治疗 | 表值 + actuals | heal_pack 12 + heal_minor 4(注入/格子池期望) | +| 数量骰 | 表值 | unit_count 区间 [unit_count, unit_count_max] | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| L1 五战区间手算(≈ 估算,p10–p90 口径) | D0 goblin×1(教学位固定):S≈[7,12] vs K≈[2,4] → **绿** ✓ · D1 ×[2,3]:S≈[4,10] vs K≈[2,6] → 黄 · D2 bugbear(首遇固定):S≈[4,12] vs K≈[2,6] → 黄 · D3 ×[2,3]:S≈[2,6] vs K≈[2,7] → 黄偏红 · Boss 酋长:S≈[4,15](旗覆盖率主导)vs K≈[7,18](囤刀+甲B 对策)→ 黄红边缘 | +| L1 形态带 | ≥1 绿(教学位)+ 中段黄 + Boss 黄不红——手算形态符合 ✓ | +| 关级恒等式漏项审计 | 原供给 180 的漏项:clear_blast 只算一次(实际 ×清图数 3–4 = 120–160)、伤害A/B 充能、attack_up 增量、RAP 刷新新板追加供给——逐项入账后闭合,"0.54 短缺"消失 | +| 输出占比带 | 奖励侧输出(刀+clear_blast+A/B 充能)÷ 总输出 ∈ **[15%, 40%]**——<15% 奖励摆设、>40% 供给依赖(AC-14) | +| 口径注记 | 区间两口径:公式包络(必过/必死结论)与 actuals p10–p90(日常判定——B1 actuals p10 纪律同构);HP 持久化耦合各战区间,联合分布由 monte_carlo 裁决 | +| C4 平移换算率 | +10hp ≈ +1 轮(÷d̄≈8–10);atk 翻倍 ≈ K 减半——区间位移机制承载通关率天赋列,非独立拍带 | + +### F7 调参流程(每次修订强制走完——七步) + +**结论**:改表值 → smoke_all 全绿 → level_monte_carlo 扫描(通关率带 + 三档判定 + 通关余量 + 占比)→ B4 登记常数与对账重制 → 对账复算(条款③⑤/预警⑥)→ README 调参日志登记——任一步 FAIL → 回滚或登记 OQ,禁止带病前进。 + +#### 推导 + +**Variables:** + +| Variable | Symbol | Type | Range | Description | +|---|---|---|---|---| +| 批量样本量 | N | int | ≥256(正式)/ 15–30(切片临时) | 正式批量工具 = level_monte_carlo 扩展(逐行聚合 + N 参数化 + 区间分位/降级统计)——**扫描执行本体归 A1 F4 工具交付(A1 OQ1),C3 的 level_monte_carlo 扩展为消费方**(所有权澄清:A1 工具产出 actuals → C3 扫描消费,非替代) | + +**验算与边界:** + +| 项 | 内容 | +|---|---| +| Output Range | 任一步 FAIL → 回滚或登记 OQ,禁止带病前进 | +| Example | 切片 d0.28 回退先例:d0.28/t0.87 → best≈0.81 降级告警 → 回退 0.25/0.88——第 2 步(A1 生成质量闸)拦截的成功案例 | +| 第④步依据 | B4 义务 #10:"C3 调参流程若无'登记常数与对账重制'步骤则相关 AC 假失败"(T_fog_min/K_dmg/N_draft/P_min/默认根行值调参 → B4 AC-19 对账对 (knife_adv, attack_up_basic) 漂移检测、AC-6 分支 4、AC-18 重跑) | + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| 未调参行 | 新增 (L,D) 行仅有结构占位 | 经七步流程 → 已调参 | +| 已调参行 | 行值经批量仿真 + 期望表对照 + 对账复算 | 任何修订 → 重走七步流程 | +| SLICE-TUNED 转正 | 切片临时值经本 GDD 背书(行值总表 v3) | → 已调参(切片行集转正条款) | +| provisional-pending-scan | 加难杠杆候选值已登记、扫描步未执行 | 扫描落带 → 已调参;越带 → 回滚候选 | +| 期望表偏离 | 调参后期望值越出目标带 | → 回滚或 OQ 登记(禁止静默出带) | + +不变式:C3 无运行时状态机(数值系统);设计期任一行值恒处于上表五态之一;期望表偏离禁止静默出带。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| unit_count>1 行与高密度板组合 | 条款③满配语境破线(B1 F4 双轴约束)——该组合**禁止入表**;切片先例 = 群战行 hp/atk 下调补偿;对账复算在流程第 5 步强制执行 | Core Rule 9① / B1 F4 | +| 关卡深度轴缺行(如 D3 缺失、D4 存在) | **调参期冒烟断言拦截**(跨表存在性检查——C3 调参工具域载体;D2 R1 fail-fast 语义不动);R1 floor 解析静默兜底至低档(难度降而非崩)+ ConfigDB warning 四元组仅作运行时兜底——缺行根因是调参事故而非运行时事件 | AC-1 / D2 R1 | +| 请求越界关卡(level 999) | R1 floor 解析至表尾行(难度封顶 = 尾行值)+ warning——冒险结构(C2)应在其内容覆盖范围内生成关卡序列;越界 = 内容生产缺口,非玩家可达态 | D2 R1 | +| 玩家天赋拉满(atk+200%)打最低难度行 | 速杀退化(预警⑥——战斗 1–2 轮终局、奖励系统饿死自己的高光);处置 = 预警⑥仪表监控(战斗结束未开资格格占比 >70% 警)+ 接受——低难度行被高天赋速杀是 Roguelike 成长兑现的合法形态,无需修复(区间形态:K_min < 2 = 预警⑥观测位) | 区间模型 / 预警⑥ | +| 密度上探压垮推理率(d0.28 案例) | best inferability ≈0.81 < target 0.87 → 降级告警"接受最优出图";处置 = **回退**(切片先例:0.28/0.87 → 0.25/0.88)——密度与 target 是联动对(A1 求解器能力的天花板内联动调参),禁止为难度牺牲 P1 度量达标;流程第 2 步闸拦截 | F7 / A1 F4 | +| 条款⑤清图支配性反转(弃图率 >30%) | 玩家普遍发现"挖一半就弃图刷 RAP"优于"推到底清图"→ 清图奖励与清图体验被支配;处置 = OQ4 playtest 观测(自动模拟者不弃图,测不到真人策略);届时杠杆 = C 量级上调(B1 约束包①,现值 clear_blast 40)或弃图成本重定价(A3 域)——MVP 接受当前非支配设计态(缺口 6.1/51.8,实测后再裁) | B1 F5 / OQ4 | +| 投放节奏断档(某关无新敌种且无新环段) | OQ8 违例——敌信息总量见底,学习曲线枯竭;处置 = 流程投放检查项(每关至少一项新内容——敌种投放节奏条款);违规 = 内容生产缺口,C3 调参无法修补(数值不是内容) | B2 OQ8 / AC-6 | +| 全部行值调至域上限(hp 999 等) | 数值可行但可玩性崩塌;处置 = 通关率带对照拦截(期望表全面越带 = 配置事故)+ 域校验既有护栏;本场景属调参事故而非玩家可达态 | F5 / D2 域校验 | +| DifficultyTable 修饰行与直填行值叠加后越域(首个精英节点上线时) | B2.spawn 套乘后 hp >999 → **clamp 至域界 + warning**(fail-fast 不适用——修饰乘数是合法配置,越界是组合事故;域界 = 玩法硬顶);MVP 空表无此场景,规则预登记 | DifficultyTable 乘数域 | +| 通关余量越带 | p90 >13(赢得从容)→ 生存侧杠杆优先(治疗稀有度下调 / 敌 atk 上探),击杀侧杠杆次之;p10 <3(1–2 血惨胜成常态)→ 治疗供给上浮或 Boss 前恢复点增益——余量是形态指标,与通关率带联合判读 | AC-13 / 区间模型 | + +## 依赖 + +**上游**: + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | 上游(硬) | 全部基表 schema、R1 解析、ConfigDB 单点读取归 D2;C3 产出 = 行值;DifficultyTable 新表走 D2 接口漂移规则(registry `difficulty_table` 已登记);禁绕过 ConfigDB 自建 load(D2 Rule 2);RngService `encounter` 封闭流(registry 已登记);**EnemyTable schema +2 字段(unit_count_max、armor_type)——接口漂移已回写 D2** | +| B2 | 上游(硬) | EnemyTable/HeroTable 行值由 C3 填(B2 拥有字段语义与校验域);修饰施加点 = B2.spawn(实例化时套乘、行值快照之前);**spawn override 参数 + unit_count_max 字段语义 + 物种集 {goblin, bugbear, ogre_chief}——接口漂移已回写 B2**;投放节奏 OQ8 共担;多敌种同深度选择规则共裁(OQ7——C1 联动) | +| B1 | 上游(soft,事实依赖) | 条款③⑤对账输出 = 调参约束输入(B1 F4/F5 = 约束生成器);P_perfect 乘数调参权归 C3(B1 登记);**begin_battle encounter 掷骰增补 + 护甲克制结算(armor_effectiveness/damage_a/damage_b 字段)——接口漂移已回写 B1**;期望口径双读数纪律(锚行读数不得用于 unit_count=2 放行);对账复算义务随每次修订 | +| B4 | 上游(soft) | RewardTable/RewardConfigTable 参数值归 C3;**供给控制器机制本体归 B4(转正登记——首件保证 + 指数递减注入,active_p0/active_decay 参数归 C3)**;clear_blast 换位 + K_dmg=0 转正(B4 保底语义迁移);敌 hp 曲线与奖励供给禁止独立调参(registry 联动重算义务——区间模型恒等式为联合检算工具);调参期批量仿真义务载体 | +| A1 | 上游(类型依赖) | BoardGenTable 行值归 C3(含 max_gen_attempts 策略);禁反向调用生成(仅经 D2/编排器传值);F4 批量红线(严格零降级)= 调参闸;**F4 全量批量工具 = A1 交付物(A1 OQ1),C3 level_monte_carlo 扩展为消费方**;d0.28 回退先例 = 密度×target 联动调参的实证;黄金向量/solver_rules 冻结不经 C3 | +| A4 | 上游(soft) | scope=global 语义由本 GDD 定稿(Core Rule 7);unlock_level 门控序列投放计划归 C3(随关卡扩展);**AC-11 未来腿 = elite/boss 标记字段落地时 A4 warning 升闸——已登记为 A4 接口漂移义务** | +| A3 | 上游(soft) | ActionEconomyTable 联动观测(ap_per_round 双重身份——节奏兼 RAP 转存规模)——**联动评估归 C3,调参所有权归 A3**(措辞更正:A3 GDD 无调参权授予回执);跨字段校验(动作成本 ≤ ap_per_round)约束内观测 | +| B3 | 上游(soft) | StatusEffectTable 四行强度(attack_up +50 / defense_up −30 / weaken −65 / ambush +3)provisional 归 C3 联动评估(B1 免伤轮合法性注记——强度评估归 C3 对账联动);减伤墙批(−50→−30)为 B3 侧行值批,C3 承伤带复验随批 | + +**下游**: + +| 系统 | 接口要点 | +|---|---| +| C1 | 节点深度语义消费;精英节点形态联动(精英节点预留条款 / A1 定稿强制重审);未设计——Boss=Dmax 约定即为其预留的接口 | +| C2 | 关卡序列的难度排布消费本 GDD 期望表;越界关卡 = 内容生产缺口(边界情况表);未设计 | +| C4 | 下游(远)——元进度天赋数值的"世界侧对照物":通关率目标带(F5)是 C4 天赋强度的标定基准;**区间平移换算率(+10hp ≈ +1 轮、atk 翻倍 ≈ K 减半)为 C4 设计输入**;未设计 | +| 呈现层(B5/D1) | Boss 登场仪式感 / 难度台阶可感知性——视觉义务归 B2/B5,C3 仅提供"何时变难"的数据(见呈现与 UI 契约) | + +**双向一致性核对(registry / systems-index 回执):** + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| X-1 | systems-index 依赖表 C3 ← D2,B2;B1 块增补"C3 消费 B1 对账输出的事实依赖成立" | systems-index | ✅ | +| X-2 | "敌种 hp/atk/pattern 曲线数值、难度修饰 → C3" | B2 | ✅ | +| X-3 | "C3 = 条款③⑤约束输入方" | B1 | ✅ | +| X-4 | "权重/档位/威力曲线 + RewardConfigTable 参数归 C3" | B4 | ✅ | +| X-5 | "每行难度数值与曲线 → C3" | A1 | ✅ | +| X-6 | B1:begin_battle encounter 掷骰增补 + 扫描 scope 腿 + B1"零随机"边界扩界注记(encounter 流在结算步流之外) | B1 | ✅(本次修订批回写) | +| X-7 | B2:spawn override 参数、unit_count_max 字段语义与实例化记号调和、物种集 {goblin, bugbear, ogre_chief} | B2 | ✅(本次修订批回写) | +| X-8 | D2:EnemyTable schema +2 字段(unit_count_max、armor_type);SettlementTable +4 字段(damage_a_per_cell/damage_a_cap/damage_b_per_flag/armor_effectiveness) | D2 | ✅(本次修订批回写) | +| X-9 | B4:供给控制器机制承接 + clear_blast 换位 + K_dmg=0 禁区条款解除(保底语义迁移) | B4 | ✅(本次修订批回写) | +| X-10 | A4:elite/boss 标记字段落地时 warning 升闸(AC-11 未来腿) | A4 | ✅(义务登记) | + +**对账与义务登记(B1 对账输出承接——审计资产):** + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 条款③ | 满配破线两轴:unit_count>1 行须满足"降密度或降 m_amb"补偿(B1 F4 双轴约束)——registry clause3_supply 登记在案 | B1 / registry | 已登记——切片群战行 hp/atk 下调为合规先例 | +| 条款⑤ | 清图支配性:46.1/91.8 需求 vs **clear_blast C=40 现值(缺口 6.1/51.8,对 clear_blast 重推)**——登记为非支配设计态;预留杠杆 = C 量级上调(B1 约束包①)或弃图成本重定价(A3 域) | B1(B1 F5) | 已登记——OQ4 playtest 持续观测弃图率 | +| 预警⑥ | 快杀退化:战斗结束未开资格格占比 >70% 警——高天赋速杀低难度行 = 合法形态,仪表监控不修复;区间形态钩子 = K_min < 2 观测位 | B4 / B1 | 已登记 | +| unit_count 约束 | 区间 [unit_count, unit_count_max] ⊆ [1,3];>1 行联动条款③复算(流程第 5 步强制) | B1 / B2 | 已登记 | +| 期望口径双读数 | 锚行读数(2.0×/2.6× 类)不得作为 unit_count=2 的放行依据 | B1 | 已登记 | + +## 呈现与 UI 契约 + +> C3 无自有视觉/音频/UI 资产(数值系统)——义务转注如下;无新增资产需求、无 D1 新增义务登记。 + +| # | 义务 | 归属 | +|---|---|---| +| 1 | Boss 登场呈现(仪式感锚点——体验锚点"压迫的顶点");设计输入 = Boss=Dmax 行的数据面(本 GDD 提供) | B5 战斗表现 / D1 | +| 2 | 难度台阶可感知性:新敌种 = 新图像线索 = 台阶的可视面孔(意图推测型——图像线索档位编码,"这只比上层的硬"可读) | B2(D1×4 义务) | +| 3 | 变异解锁的"这一层有雾了":图例与首遇提示(已登记义务) | A4 / D1 | +| 4 | 期望表 / 批量仿真输出 = 调参期开发工具(headless——非游戏内 UI) | 开发工具 | +| 5 | 恢复点/奖励点呈现:C1-lite 切片已实现的节点覆盖层;正式形态归 D1/C1 | D1 / C1 | + +## Tuning Knobs + +> C3 是全项目 knob 保有量最大的系统(数值 owner)。全部值 provisional 起步;域与校验归各表 schema(D2);本表只登记所有权、联动与危险方向。 + +| Knob | 表 | 域 | 联动 | 过高 | 过低 | +|---|---|---|---|---|---| +| EnemyTable.hp/atk(逐行) | enemy.tres | [1,999] / [0,999] | **与 B4 供给禁独立调参**(区间模型恒等式联合检算);cap×atk 禁独立(B2) | 打不死、战斗冗长 | 快杀退化预警⑥ | +| EnemyTable.unit_count / unit_count_max(逐行) | enemy.tres | [1,3] / [unit_count,3] | **>1 行须降密度或降 m_amb**(条款③);区间骰经 encounter 流 | 条款③破线 | 压力不足/遭遇零随机 | +| EnemyTable.pattern(逐行) | enemy.tres | MVP 三值组合 | OQ8 投放节奏(每关新环段) | 信息过载 | 学习曲线枯竭 | +| EnemyTable.armor_type(逐行) | enemy.tres | {none, a, b} | **克制矩阵联动**(settlement armor_effectiveness);伤害A/B 充能消费面(B1 域,C3 投值) | 克制失配 | 特化对策失效 | +| HeroTable.atk/max_hp | hero.tres | [1,999] | 全部输出式的分母锚(F4/F6) | 奖励价值稀释 | 挫败感 | +| BoardGenTable 尺寸/密度/attempts(逐行) | board_gen.tres | 域见 schema | **密度×target 联动**(d0.28 回退先例);unit_count>1 行联动 | 推理率不达标降级 | 板退化(低密度先布后删) | +| BoardGenTable.inferability_target(逐行) | board_gen.tres | **[0.85,1] 绝对锚** | 与密度成反耦 | 生成耗时/降级 | P1 空心化 | +| BoardGenTable 雾参数 | board_gen.tres | f≤8d / ≤0.25 | unlock_level=2 锁定 | P1 度量空心(绝对上限) | 雾内容缺位 | +| RewardTable.weight/power/tier(逐行) | reward.tres | 域见 schema | 区间模型恒等式;攻击面稀释;clear_blast 单行槽 | 即时类淹没明细/可选类贬值 | 窗口空转/重 roll 频发 | +| RewardConfigTable.{t_fog_min,k_dmg,n_draft,p_min} | reward_config.tres | 域见 schema | k_dmg=0 = 保底断言关闭(禁区条款已解除——保底语义由首件保证取代) | 保底永不触发 | 种子轮盘致命 | +| 供给控制器 active_p0/active_decay | reward_config.tres | [0,1] / (0,1] | E[战斗获取]≈1 目标(B4 机制本体承接);**与敌 hp 联动** | 速杀+囤积回归 | 可选类绝育 | +| SettlementTable.{damage_a_per_cell,damage_a_cap,damage_b_per_flag,armor_effectiveness} | settlement.tres | 域见 schema | Boss 甲B 对策 = B 充能;A 克制甲A;联动网络 #6 | Boss 裸打过慢 | 充能失效 | +| ActionEconomyTable.{ap_per_round,rap_refresh_cost,refresh_max_per_round} | action_economy.tres | 域见 schema | ap 兼 RAP 转存规模;**调参所有权归 A3,C3 联动评估** | 轮次冗长 | 决策密度坍缩 | +| SettlementTable.P_perfect(乘数) | settlement.tres | {100,200} | 完美排雷×清图支配性互 feed | 钓鱼线(护栏 B) | 高光缺位 | +| DifficultyTable 修饰乘数(MVP 空表) | difficulty.tres(待建) | [0.1,10](registry 预登记) | 精英节点上线时启用 | 越域 clamp+warning | 无修饰 | + +### DifficultyTable schema(difficulty.tres 待建——D2 接口漂移建表) + +| 字段 | 类型/域 | 默认 | knob | +|---|---|---|---| +| level_index + node_depth(复合键) | int × int | — | ✗(结构键) | +| 修饰乘数 | float [0.1, 10](registry 预登记域) | —(MVP 空表无行) | ✓(精英节点上线时启用;越域 clamp + warning——域界 = 玩法硬顶) | +| elite_ / boss_ 前缀预留字段位 | 命名空间预留(D2 schema 断言期望暴露) | 无 | ✗(预留,随精英节点语义定稿) | + +### 联动网络(禁止独立调参清单) + +| # | 联动 | 纪律 | +|---|---|---| +| 1 | 敌 hp ↔ 奖励供给(registry 已锁) | 单边移动必走区间模型恒等式联合检算 | +| 2 | unit_count ↔ 密度/m_amb(条款③) | 群战行上线前的强制复算 | +| 3 | 密度 ↔ target ↔ attempts(A1) | d0.28 回退先例的三元组 | +| 4 | ap_per_round ↔ RAP 体系 ↔ refresh 上限(A3 + 轮内刷新批) | 跨字段校验闸承载 | +| 5 | m_w(weaken −65)↔ 锚对照表(B1 F0) | 终值共裁 B4/B1,调参走 C3 提案通道 | +| 6 | armor_type ↔ 伤害A/B 充能 ↔ 克制矩阵(B1 域) | 敌种甲型变更须复算 Boss/普通战 K 区间 | + +## 验收标准 + +| AC | Given | When | Then | +|---|---|---|---| +| AC-1 | LevelTable 任一 battle/boss 节点 (L,D);ConfigDB 加载全部表 | 调参期冒烟断言逐节点查两表 | board_gen 与 enemy 两表存在该 (L,D) 精确行(跨表护栏⑧转正——载体 = C3 调参期冒烟断言;运行时 floor 兜底属 D2 R1 既有语义) | +| AC-2 | MVP 全部 (L,D) 行 | 逐行扫描 inferability(批量 N=256/行——A1 F4 工具交付后的正式红线) | 全部行 inferability ≥ target ≥ 0.85(P1 绝对锚)且 A1 断言族(退化板/R₀≠S)零违例 | +| AC-3 | EnemyTable 全部 unit_count>1 行 | 条款③复算(密度 × m_amb 组合) | 无破线组合存在(Core Rule 9①) | +| AC-4 | MVP 全部行集(区间模型) | level_monte_carlo 逐战 S/K 区间扫描 | **零红档组合**(S_max ≤ K_min 禁止入表);教学位绿档;L1 形态带 = D0 绿 + 中段黄 + Boss 黄不红(F6 三档判定) | +| AC-5 | 期望表目标带(F5) | level_monte_carlo 全带扫描(基线/+100%/+200%) | L1 基线通关率 ∈ [30, 50](现值 60% 越上界——加难杠杆批内执行,扫描落带前 provisional-pending-scan);越带须 OQ 登记——禁止静默出带 | +| AC-6 | L1 全部行集 | 枚举敌种与环段 | 每关至少一项新投放(新敌种或新环段——OQ8;L1 = goblin→bugbear→ogre_chief 三段 ✓) | +| AC-7 | Boss 行(Dmax) | F3 检算(期望口径 Σ=153.5) | r_boss 记录在案(现值 0.98 带内 ✓;越带须 OQ 登记——加难走廊 [1.1,1.35] 走 OQ2/扫描) | +| AC-8 | 每轮刷新 | 计数达 refresh_max_per_round | 第 N+1 次拒绝(表值驱动——A3 域行为,C3 侧验收 = 联动评估记录存在) | +| AC-9 | 任何 C3 直辖 knob 修订 | 检查其联动网络(Tuning Knobs 六组) | 对应联合检算记录存在于调参日志(禁止独立调参纪律的流程面) | +| AC-10 | DifficultyTable MVP 空表 | B2.spawn 任一 (L,D) | 无修饰行被消费(结构位不产生行为)且未来首行上线时套乘点 = 行值快照前 | +| AC-11 | scope=global 变异行 | MVP 腿:扫描 VariationTable 全行 | MVP 无 global 行存在(表级约束)且 A4 warning 通道在位;未来腿:elite/boss 标记字段落地时加载/生成期拒绝(A4 接口漂移义务——X-10) | +| AC-12 | 每次调参批 | 检查 README 调参日志 | 七步流程的执行记录齐备(流程纪律的可审计面) | +| AC-13 | 通关余量(新指标) | level_monte_carlo 通关轨迹采集 | E[HP_end \| 通关] 的 p90 ≤ 13 且 p10 ≥ 3("只剩一点血"量化带——越带按边界情况表杠杆处置) | +| AC-14 | 关级恒等式闭合 | actuals 逐项审计(流程第③步输出) | 输出侧五项(基础攻击/刀/clear_blast/A+B 充能/attack_up)与输入侧(承伤/治疗/减伤)合计闭合 Σ敌hp期望×(1+缓冲),残差 <5%(provisional 阈值)且奖励输出占比 ∈ [15%, 40%] | + +## 开放问题 + +| # | 悬案 | 进展 | owner | 时机 | +|---|---|---|---|---| +| OQ1 | L2+ 曲线数值——本 GDD 定结构/流程/目标带;具体数值行随关卡内容扩展填充(内容与数值同批——C2 关卡序列定稿后) | 结构已定 | C3 随批 | L2 内容扩展时 | +| OQ2 | Boss 血量占比——v3 记录值更正 0.98(153.5 期望口径,带内 ✓);加难走廊 [1.1, 1.35] 预登记(Boss 180–207 候选);真人实测 Boss 战时长后终裁 | 走廊预登记 | C3 | F7 扫描 + playtest 批次 2 | +| OQ3 | 真人修正系数——批次 1 方向确认:真人显著强于模拟者(v2 真人 100% vs 模拟 80%);F5 方向声明已更正落地;真人系数待 v3 试玩采集定量 | 方向已定 | C3 + playtest | playtest 批次 2 | +| OQ4 | 条款⑤支配性真人观测——批次 1 初步裁决:真人零弃图倾向 → 非支配设计态成立、C 量级不上调 ✓(B1 约束包②采信);**P1 对冲弱化同步声明(B1 ② 条件):本裁决弱化 A3 条款⑤对 P1 失败模式的经济对冲,弃图率观测加权监控随之**;维持登记:单一样本 | 初步裁决 | C3 + B4 | 持续 | +| OQ5 | 精英节点形态——完整语义(独立节点/词缀/出现规则)= C1 分支地图定稿后的联动设计(A1 定稿强制重审随之触发) | 未设计 | C3 + C1 | C1 GDD 定稿 | +| OQ6 | 轮内刷新对难度模型的冲击——批次 1 初步读数:刷新零使用,根因是盲猜墙不足(inferability 0.88);杠杆预登记:密度联动上探(d0.28/t0.85——A1 回退先例风险);保持开放 | 初步读数 | C3 | playtest 批次 2 | +| OQ7 | **加难杠杆扫描执行 + 完美策略档测量**——杠杆候选(r_boss 走廊 180–207 / 群战上探 / decay 0.6)已预登记;执行 = 七步流程第③步 level_monte_carlo 扫描(三档判定 + 通关率带 + 通关余量 + 占比四表对照)。**2026-09-14 完美策略档落地(用户拍板)**:零盲挖/全标旗/卡死刷新技能轴上界测量仪(godot 本机可用已验证)。**区间批已落地并复测(同日拍板②——奖励五行 power 区间 + 敌方 atk 区间 ±3)**:完美档 0 加成仍 100%(承伤 3.5→3.9)——**数值区间杠杆族对 ≤40% 验收线无效已实测坐实;主项待裁决 = 结构性杠杆三选**(承伤硬地板〔不可减免平伤,B1/B2 域〕/ weaken 饱和语义〔全标旗收益递减,B3/B1 域〕/ 极端区间宽度〔建议否决——毁基线与数字可读性锚〕);落带前 v3 值 provisional-pending-scan | 完美档已测;区间批无效性已证;结构杠杆待裁决 | C3 | 用户裁决后 | + +### 试玩批次记录 + +| 批次 | 日期 | 数值基线 | 读数 | 裁决产出 | +|---|---|---|---|---| +| 批次 1(用户本人) | 2026-09-12 | v2 | 全部满血通关(清图为主/优先标雷/不用道具三策略均通)——基线难度不足定量坐实;Boss 血量 OK 或可上调;刷新零使用;零弃图倾向;治疗包零使用(不掉血伴生);伤害道具无特色(暂保留) | v3 数值批(atk 4/4/6/4/7 → 7/5/9/6/9;D0 24 保持防秒杀、Boss 130→150)+ DND 命名批(goblin/bugbear/ogre_chief——用户指令)+ 护甲克制批(armor_type none/a/b + 克制矩阵,数值 provisional)+ F3 带修订 + OQ2/3/4/6 读数;v3 扫描:基线 60%、败北向 Boss 集中 4/6、+200% 100%;下一批 = v3 真人试玩(OQ3 定量 + OQ2 终裁) | +| 减伤墙批(defense_scan 触发) | 2026-09-13 | v3 + 减伤墙批 | defense_scan 实测墙轮 39%、真人局 12/12 轮零承伤满血通关——减伤墙过强定量坐实 | defense_up magnitude −50→−30(叠满两层 = −60 帽)+ 石肤权重 24→16(attack_up 升池内最高行)——B3 侧行值批,C3 承伤带(F4)复验责任随批 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v2 | 2026-09-14 | /design-review 修订批(8 blocking 核销 + 4 项用户拍板):**F6 供给带退役 → 生存链区间模型**(S/K 区间三档判定 + 关级恒等式 + 输出占比带 + C4 平移换算率——用户设计)· **带锚定稿**(通关率 [30,50] / 通关余量 p90≤13 & p10≥3 / L1 形态带)· F3 口径统一 Σ=153.5 / r_boss 0.98(旧验算 142 替代)+ 加难走廊预登记 · F4 升级生存侧(S 区间)· F5 带 [30,50] + 真人方向更正 · **MVP 行值总表 v3 转正**(切片 .tres 权威源,旧 SLICE-TUNED 表替代)· DND 更名生效(goblin/bugbear/ogre_chief)· **四批转正登记**(armor_type / K_dmg=0 / clear_blast 换位 / 供给控制器归 B4——用户拍板)· B1/B2/D2/registry 接口漂移回写(X-6~X-10)· F7 六步→七步(B4 对账重制步)· AC-4/5 重写 + AC-13/14 新增 · 条款⑤重推(C=40,缺口 6.1/51.8)· Boss atk 台阶重定义 · 减伤墙批补录 · OQ7 新增(扫描执行) | +| v1-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;规则/枚举/边界情况/对账登记/AC 转表格;曲线生成与调参流程转字符画;公式、Variables 与验算数据全保留;F3 带按现行 [0.5, 1.1] 成文;语义零变更 | +| 试玩批次 1 / v3 数值批 | 2026-09-12 | 遭遇随机化引入(unit_count 区间语义 + RngService encounter 封闭流——Roguelike 局感引入批,Peglin 对比报告结论 2 的本切片落地项);F3 带修订 [0.35, 0.55] → [0.5, 1.1];v3 数值批(atk 7/5/9/6/9、Boss 130→150、D0 24 保持防秒杀)+ DND 命名批 + 护甲克制批;OQ2/3/4/6 读数登记(条款⑤非支配设计态采信) | +| 初稿 | 2026-09-12 | solo 模式出稿:九核心规则 + F1–F7 + 12 AC + 六 OQ;SLICE-TUNED 转正声明;Boss=Dmax 约定、DifficultyTable 结构位与 scope=global 语义定稿 | diff --git a/design/gdd/legacy-2026-09-14-v1/d1-hud-framework.md b/design/gdd/legacy-2026-09-14-v1/d1-hud-framework.md new file mode 100644 index 00000000..2dc7b5cf --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/d1-hud-framework.md @@ -0,0 +1,771 @@ +# D1 HUD 与界面框架(HUD & UI Framework) + +> **状态**:Designed · lean 格式(评审后修订稿——评审记录见 `design/gdd/reviews/` 与版本历史) +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents +> **承载支柱**:P1 主("我算对了"看得见的感知面)· P2 主(逐行归因的呈现半边)· P4(地牢氛围容器:五区布局与能量档承载) +> **层级**:Layer 4 Presentation · Milestone MVP · 设计顺序第 10 位(MVP 最后一块) +> **上游**:A1–B4/B1 + D2 全部 Approved(C3 为 Designed 未经评审——D1 零玩法消费;battle.md APPROVED · art-bible §1–4 定稿)——项目首个零玩法上游假设的表现层系统 +> **一句话**:D1 把 A1–B4 的全部玩法事实变成玩家看得见、点得动、归因得了的界面——零玩法状态的双层边界呈现框架。 +> **复核注记**:solo 模式下 creative-director(体验锚点情绪定位——与 battle.md Purpose & Player Need 同源,本节为系统级转写)与 art-director(Visual/Audio 与 art-bible 一致性)未经咨询——生产前人工复核。呈现义务承载依据 = `design/ux/battle.md`(APPROVED,§13 台账 27 项经 ux-review 实存验证)+ `design/art/art-bible.md` §1–4。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 呈现层框架:战斗屏(MVP 唯一玩法屏,五区常驻双舞台布局,屏级 spec = `design/ux/battle.md`)+ 一族共享呈现设施——toast 提示体系、模态覆盖层家族(图例 / 明细回看 / 胜负终局)、输入翻译(鼠标意图 → A2 意图对象)、模态层级管理、面向呈现的信号载荷契约、ux 遥测 | +| 拥有 | 系统级呈现契约:五区布局落位与常驻组件呈现形态 · 输入翻译与单意图帧收集 · toast/图例/提示体系 · 模态层级纪律 · 13 项信号载荷 schema · 程序化占位策略 · 键位映射表挂接 · ux 遥测 · F1 呈现几何与 F2 可读性判据执行 | +| 不拥有 | 任何玩法状态与玩法数值(读经核心只读面与封装呈现面、写经组合根——架构七原则"依赖单向组合根",ADR-0001/0003)· 屏级布局/组件/交互/动画时长裁决权威(battle.md)· 美术裁决(art-bible)· 机制语义(各玩法 GDD)· 动画密度(B5)· 音频(D4) | +| 上游输入 | A2 三事件流 + 呈现面(`current_board` / `reveal_record` / `compute_move_path`)· A3 getter(phase/ap/rap/last_round_end)· B1 步骤流(13 类行协议)+ `presentation_snapshot` / `is_battle_live` / `stock_count` / `query_reward_identity` · B2 单位投影 · B3 状态投影(经 B1)· B4 库存/身份载荷(经 B1)· runner 节点链/当前节点/HP 携带 | +| 下游输出 | B5(hard):五区布局与双舞台裁决、B 区演出区结构、明细带呈现位、行协议呈现语义、敌人卡呈现语义、art-bible §4.6 键位映射表——B5 只叠动画密度与演出 · D4:音频挂点事件源 · D5:教学承载位 · D6:本地化契约消费 | +| 硬约束 | 零玩法数值字面量 · 零规则逻辑(不判合法性、不裁语义)· 零核心写调用(唯一发生在组合根)· 零 RNG · 呈现层永不直订 A2/A3(ADR-0001——只订 B1/runner 信号 + 命令式回执)· 禁回读核心生命周期状态(只许成文呈现面与快照) | + +D1 的存在理由(四条): + +- **义务承载**:systems-index 门禁体系向 D1 登记 27 项呈现义务(A2×6 / B2×4 / B3×4 / B4×5 / B1×3 / A4×3 / A3 + A1 EC5——逐项落位表见依赖节,缺项即 blocking),battle.md 已逐项裁决呈现形态,本 GDD 升格为系统级 Core Rules 与 AC。 +- **切片教训兜底**:垂直切片两起呈现层崩溃(窗口载荷缺字段、battle_finished 后读已销毁对象)暴露"已定稿 AC 全覆盖 core、呈现层零测试承载"的系统性盲区——本 GDD 落实三条硬性要求:信号载荷 schema 成文(Rule 17)/ 场景级 headless 测试进 AC / 信号时点状态可见性规则(Rule 18)。 +- **美术解耦**:art-bible §4.6 图标键契约已冻结(键与通道分配定稿、终稿美术归样张批),样张未制作不阻塞 D1——呈现层以程序化占位先行(白话:用代码按冻结色板与形状规则直接画出的临时图形,终稿美术到位后原位替换),验收引用 art-bible §1.4 格子样张标准(Rule 19)。 +- **无 D1 的后果**:A2 的三事件流没有读者、B1 的流水协议没有呈现面、27 项上游义务没有兑付点——"自动战斗沦为无聊过场"(game-concept 已识别风险 #1)的防线就设在 D1 的逐行明细与高光反馈上。 + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 推理掌控感(感知面) | "我看见的每一步都是真的" | 四种格态一瞥可辨(左键双义消歧前提——误挖不可逆)· 悬停预承诺三态光标 · 可挖域常驻呼吸微光 · 每次拒绝可感知——规则层的诚实(A2 数字恒真、三源拒绝)由呈现层原样送达;任何呈现失真(雾格显影 / 拒绝吞掉 / 格态混淆)= P1 的死亡体验 | +| P2 规划延迟兑现(呈现半边) | "这个结果是我哪步扫雷带来的" | 结算明细逐行兑现 + 每行来源标注(B1 流水行协议)· 四类未生效行不静默 · 零值行不被吞——P2 裁剪判据的最后半米由 D1 走完 | +| P4 地牢氛围容器 | "挖掘期一块干净的玻璃,结算期一场丰收" | 挖掘期画面无杂音(能量 2/10,唯一在动 = 悬停呼吸微光——art-bible §2.1)→ 结算期金橙光带逐行横扫(7/10)→ 清图金粒喷泉短促丰收(8/10)→ 失败暮蓝最静一拍(1/10)——§2 能量档与明度恒定硬规则由 D1 的转场与反馈系统承载 | +| 高光节拍 | "完美被加倍看见" | 完美排雷全屏喝彩级反馈(金光带加倍 + 「完美排雷 ×2」横幅)+ 标中旗金框行 + H2 清图奖励金框——P2 爆发兑现节点(创意风险 #2 落点) | + +- 直接承载(Section A 定框):玩家每一次点击、每一行结算、每一面旗都直接与 D1 互动——情绪目标 = **"我看见的每一步都是真的"**:数字是真实雷数(雾格只是不显示,数据层恒真)、拒绝有原因、voided 有提示、结算逐行可归因。 +- 参考感受:Into the Breach 的格内信息分层(预告上格——威胁画在格子上)+ 杀戮尖塔的逐卡结算反馈。 +- **反向护栏**:界面绝不替玩家思考(不预判合法点击以外的东西)、绝不静默改判(voided 不提示 = 读感欺骗)、绝不用装饰伪装逻辑差异(art-bible 原则三)——玩家对 D1 的正确感觉是"一块干净的玻璃":看穿它直接看到棋局与战果,玻璃本身不添乱。 + +## 全局流程 + +### 呈现刷新链路(命令式回执 + 事件流双通道) + +``` +玩家输入(D 区,仅挖掘期可交互;全部操作纯鼠标可完成——Gamepad Partial 非 MVP) + │ 左键已开格 = 移动 ─ 左键未开相邻格 = 挖 ─ 右键 = 插拔旗 ─ 停手按钮 = settle_batch([stop]) + ▼ +单意图帧收集(一帧内 collected 意图整批;stop 点击终结当帧收集;F4 语义序仲裁退为防御性兜底) + ▼ +组合根 ──► A2 settle_batch(intents) [唯一写路径;零直调 A3] + │ + ├─ 回执链(零事件外发动作:旗插拔 / 移动 / AP / RAP / 库存——Rule 2 命令式回执) + │ settle_batch 返回 → 组合根立刻重查只读面(current_board / A3 getter / query_inventory) + │ → 视图 setter 只读注入 → 就地更新(字段级 setter,禁全量重建) + │ + └─ 事件链(A2 cell_revealed / flag_judged → B1 按轮缓冲) + ▼ + B1 结算编排(七段)→ 步骤流(13 类行协议——呈现层唯一订阅面之一) + │ + ├─ 明细带:逐行来源标注「{行为} {值} → {目标}(来源)」→ 84px 实时流(挖掘期收起为 24px 摘要) + ├─ 窗口锚点(授予段后、增益/减益段前):E 区原位激活(usable_now 投影;空库存 skip 不弹窗) + ├─ 高光:flag_hit 金框行 ─ clear_reward_h2 金框 ─ perfect 独立高光行(全屏喝彩级) + ├─ 状态栏刷新(B3 零信号外发):绑 B1 结算编排完成时点(命令式) + ▼ + 判定段 → 判定行(终局呈现数据锚)→ presentation_snapshot()(despawn 前捕获——Rule 18①) + ▼ + 终局呈现(胜利金蜡封 / 失败暮蓝 overlay)→ 状态槽全清 + RAP 清零反馈(battle_over) + ▼ + level_clear(关卡末)→ 库存清空反馈「剩余道具 ×N」→ 下一节点 / 返回(runner 推进,写经组合根) +``` + +### 出口分发 + +| 出口 | 触发 | 呈现动作 | 约束 | +|---|---|---|---| +| 继续挖掘 | settle_batch 回执、轮次未结束 | 命令式重查刷新对应面;明细带保持上轮摘要 1 行 | 每帧至多一批提交(单意图帧) | +| 结算展示 | `round_end`(A2 棋盘路径 / A3 stopped 直发——双发射源同构) | D 区锁输入(去饱和 + 金橙色相偏移、明度不变、数字保持全对比);明细带展开 84px 逐行兑现;阶段指示→"结算中";能量 7/10 | 呈现订阅面 = B1 步骤流;零直订 A2/A3 | +| 清图换图 | cleared 判定 | 金粒喷泉 + 彩纸 3–5s 即收;H2 清图奖励行金框强调;随后换新图 install(200ms 淡入) | 完美排雷高光叠加于清图瞬间之上 | +| 道具窗口 | B1 授予段完成(窗口锚点信号) | E 区原位激活(Rule 13);选用/跳过 → 回结算流(结算阶段续) | 空库存直接 skip 不弹窗、结算零停顿 | +| 战斗终局 | 判定行 verdict=win / lose | 胜利 = 金蜡封 + 暖白金 overlay;失败 = 暮蓝 overlay(明度不变);终局数据全部来自流水行 + `presentation_snapshot()` | 出口 = 下一节点(win)/ 返回(lose)——写经组合根 | +| 关卡结束 | level_clear 编排 | 库存清空反馈「剩余道具 ×N 随关卡结束清空」(N = 清空前快照计数) | 与 battle_over 反馈不合并、时序不倒挂 | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 +> **归属声明**:屏级布局、组件形态、交互细节、动画时长的裁决权威 = `design/ux/battle.md`(APPROVED)——本 GDD 引用其裁决不重复论证,只做三件事:① 升格为系统级 Core Rules 与 AC;② 补齐 spec 之外的系统级契约(信号载荷 schema / 订阅纪律 / 程序化占位 / 命名统一 / 遥测);③ 逐项落位 27 项门禁义务。冲突时以 battle.md + art-bible 为呈现形态权威、以本 GDD 为系统契约权威,机制语义以各玩法 GDD 为准。 + +#### 规则 1:界面只当翻译不当裁判——数值不写死、对错不自己判、核心数据不碰 +**人话**:这条管 D1 自己的代码边界。屏幕上出现的每个玩法数字都不是界面写死的——全部从上游系统的只读接口现读现显;一个动作合不合法、一个结果算什么意思,界面一律不裁——交上游裁定,界面只把裁定结果翻译成画面;挖格、插旗、选道具这类改变游戏状态的操作,界面只递话,真正的写入只发生在组合根一处。界面单元只做两件事:向外发信号、被 setter 注入数据后就地刷新。 + +| 界面不做什么 | 实际从哪来 | +|---|---| +| 不写死玩法数值 | 期望值全部经核心只读面读表值现推 | +| 不判合法性、不裁语义 | 一切判定交上游,呈现只翻译结果 | +| 不直接写核心状态 | 写调用全部经组合根一处下发 | + +**举证**:无计算。视图单元九件套 = BoardView / HUDView / HudPresenter / BattleLogView / ItemWindowOverlay / NodeOverlay / LevelStripView / TunePanel / UiKit(切片九单元形态转正);零玩法数值字面量的读表口径 = D2 Rule 1 对呈现消费面的适用;写路径唯一入口 = 组合根(ADR-0003 Key Interfaces)。 + +#### 规则 2:界面听消息只走中转,不私接玩法系统的线 +**人话**:这条管界面怎么"听到"游戏里发生了什么。界面永远不直接订阅扫雷系统(A2)与行动经济系统(A3)的通知——要听的消息全部由组合根接线转发,或直接来自结算编排(B1)的流水信号与关卡进程(runner)的信号。棋盘、资源、库存这些"不发通知"的数据面靠命令式回执刷新:动作调用一返回,组合根立刻重查一次只读数据来刷新界面,而不是干等通知信号——部分动作本来就不发信号。 + +| 界面听什么 | 从哪听 | +|---|---| +| 结算流水与结算快照 | B1 步骤流信号(经组合根接线) | +| 关卡进程信号 | runner | +| 不发通知的面(棋盘 / AP / RAP / 库存等) | 动作回执后命令式重查只读面 | + +**举证**:无计算。订阅纪律固化自 ADR-0001;battle.md §Events Fired 的"呈现订阅 A2 三事件"表述按本条收窄为"经组合根转发或命令式回执"(措辞差登记冲突清单⑥,battle.md 正文不改动)。 + +#### 规则 3:读数据只走登记过的口子,内部字段一律不碰 +**人话**:这条管界面取数。界面要显示的每个数据,只允许从一张成文的"读数清单"里拿,清单之外的私有字段直读一律禁止。清单按数据来源分家:棋盘事实读扫雷系统,阶段与资源读行动经济的 getter,结算、库存、战斗存活性读结算编排,敌我单位读战斗系统的只读投影,关卡进度读关卡进程;状态效果与战斗背包没有直读口子——一律经结算编排转交。 + +| 数据从哪来 | 允许读的口子 | +|---|---| +| A2 扫雷 | `current_board()`(格态/旗/小人/变异只读投影)、`reveal_record()`、`compute_move_path(cell)`(不可达返回空——预览与执行同源) | +| A3 行动经济 | `phase / ap / rap / last_round_end`(getter-only) | +| B1 结算编排 | `presentation_snapshot() / is_battle_live() / stock_count() / query_reward_identity()` + 步骤流信号 | +| B2 战斗单位 | `hero_unit() / all_enemies()` 投影 | +| B3 / B4 | 零直读——经 B1 呈现面与流水投影(协作对象私有化) | +| runner | 节点链 / 当前节点 / HP 携带(A 区) | + +**举证**:无计算。读接口白名单 = 上表(封装收口批 entry 39/40 转正);登记载体 = registry `d1_presentation_surface`(切片实现、owner GDD 契约登记待批——接口漂移见冲突清单⑧/OQ3)。 + +#### 规则 4:界面的每个动作都走同一条提交通道,总闸只有组合根 +**人话**:这条管界面"递话"。玩家在界面上能发起的每类操作各只有一条提交通道,而且全部先过组合根这道总闸、再由总闸转给对应系统——没有任何绕过总闸的第二写入口。每帧至多提交一批意图(单意图帧,规则 7)。 + +| 玩家动作 | 提交通道 | +|---|---| +| 挖掘 / 插拔旗 / 移动 / 停手 | A2 `settle_batch(intents)`——停手也打包成 stop 走同一接口,不直调 A3 | +| 窗口里选道具 | B1 `submit_window_selection(窗口条目位置序)`——传窗口条目位置,不传库存句柄 | +| 弃图刷新 | 组合根提交刷新意图 → B1 `refresh_board` 编排:A3 扣费 → A2 retire → A1 generate → A2 install → B4 roll_board | +| 节点推进 | runner | + +**举证**:无计算。窗口选择传位置序 = 切片缺陷④的契约修正;停手不经 A3 直调 = 义务 A2×6①;单意图帧 = 规则 7。 + +#### 规则 5:战斗画面分五区常驻同屏,棋盘与敌人不切场景 +**人话**:这条管战斗屏的骨架。1280×800 基座下(canvas_items/expand 拉伸),战斗画面固定分成五块常驻区域:关卡进度条、战斗画面、角色信息、扫雷图、道具栏。战斗画面与扫雷图同屏常驻是硬形态:挖掘期敌人在上方待机可见、结算期原地展开明细,全程不切场景——这是对"双舞台陷阱"的第三形态裁决输入。逐区位置、尺寸与内容见下方实现参照表。 + +**举证**:无计算。分区尺寸与组件落位的屏级权威 = `design/ux/battle.md`;拉伸模式 = ADR-0002;五区布局为结构冻结项(非调参 knob,修订走 GDD 流程)。 + +### 规则 5 的实现参照——五区布局规格 +| 区 | 位置 / 尺寸 | 内容 | 数据源 | +|---|---|---|---| +| A 关卡进度条 | 全宽 28px | 节点链步进、当前高亮、已过暗金(LevelStripView) | runner | +| B 战斗画面 | 全宽 252px | 英雄卡 + 敌人卡 ×1–3 + 轮次·阶段指示 + 结算明细带(24px 摘要 / 84px 实时流) | B2 投影 + B1 步骤流 | +| C 角色信息 | 左下 240×520 | HP / 攻击值含修正显示 / RAP 数据框(直角细线素面)+ 状态效果槽固定 4 槽 + 小人立绘占位 | A3 getter + B3 投影(经 B1) | +| D 扫雷图 | 中下 ~760×520 | AP 点阵条 44px(停手按钮居左、图例与弃图刷新按钮居右——刷新按钮置雷图右上角)+ BoardView 棋盘 + toast 槽(上缘内侧) | A2 呈现面 | +| E 道具栏 | 右下 280×520 | 库存常读列表 + 窗口态原位激活 | B4 库存载荷(经 B1) | + +#### 规则 6:敌人卡把"它是谁、下拍干什么、还剩几滴血"全写在脸上 +**人话**:这条管 B 区的敌人卡(B2 敌人系统的呈现承载)。每张敌人卡固定六件套,玩家不点开任何东西就能读出敌人的关键状态: + +| 卡面元素 | 玩家读出什么 | +|---|---| +| 敌像 | 这是谁——姿态即线索 | +| 血条 | 还剩多少血(hp/max_hp 数字可读) | +| 顺位徽标 | 它第几个出手(固定顺位可读) | +| 意图徽标 | 下一拍它要干什么——把即将执行的一拍的视觉状态摆出来;挖掘期全程可见,结算期不另设播片 | +| 行为环点 | 行动周期多长(环点数 = 环长)、现在走到哪(当前位置高亮) | +| 状态槽 | 它身上挂着什么增益减益 | + +**人话**:三条纪律:伏击层数是全体敌人共享的一个数——各卡显示同一个值;敌人死亡 = 血条直角递减归零 → 卡体灰化 + 碎裂退场,禁止瞬间消失;战斗段被短路掐灭的重击不单独产行动明细行(从行序可推断)。敌卡刷新必须就地更新(字段级 setter),禁止逐行重建——全量重建会把死亡灰化态吞掉。 + +**举证**:无计算。敌像线索键 = art-bible §4.6.4(强度档位与环长的直觉编码——意图线索映射目录见依赖节附表);意图预告时序 = B2 Rule 8⑤;侧池共享单值(数据源 = B1 流水经 B3 投影)与顺位徽标的义务源 = B2×4④ / D1×4③④;禁瞬消 = B2 资产下限;就地更新 = 切片 entry 36 教训。 + +#### 规则 7:鼠标点哪就是哪个意思,一帧内的操作合成一批提交 +**人话**:这条管 D 区输入。界面把鼠标动作翻译成扫雷系统的意图对象: + +| 鼠标操作 | 翻译成 | +|---|---| +| 左键点已开格 | 移动(免费) | +| 左键点未开的相邻格 | 挖 | +| 右键 | 插旗 / 拔旗 | +| 点停手按钮 | 提交 stop——本轮结束 | + +**人话**:单意图帧纪律:一帧内收集到的意图整批提交,点到停手当帧收集立即终结——语义序仲裁只作防御性兜底(生产面造不出会乱序的输入)。悬停预承诺:光标按目标切换三种语义、目标格边框高亮——点下去会发生什么,先看见再点。可挖域常驻指示:小人八向相邻的未开格呼吸微光,是挖掘期唯一在动的元素。悬停不可挖域 = 禁止光标 + 每 run 一次提示「需先走近(点击已开格移动,免费)」。点击远格自动"先移动再挖"的复合意图本期不做——移动免费高频,两击的摩擦符合观测假设。 + +**举证**:无计算。光标三语义与伴随指示见「枚举·光标语义」;no_adjacent 提示见「枚举·一次性提示 hint_id」;单帧整批与每次拒绝可感知的义务源 = A2×6①⑥,悬停预承诺 = A2×6③;复合意图不做 = A2 评估项留痕(P4 ≤2 观测假设);F4 语义序仲裁退为防御性兜底(联合清图让位条款生产面不可达)。 + +#### 规则 8:悬停已开的格,先画给你看小人会怎么走 +**人话**:这条管移动的手感。悬停在任何已开格上,界面立刻把小人到那格的合法路径用蓝色细线点迹画出来——它回答"点这里小人会怎么走",是合法性指示,不是穿行动画。真正执行移动用不超过 150 毫秒的短滑表现。备选方案"穿行动画轨道"明确不采纳:移动免费且高频,动画时间税违反点击摩擦护栏;playtest 若反馈"移动太瞬"再议(留痕,旋钮 = move_slide_ms)。 + +**举证**:无计算。路径数据 = `compute_move_path` 的 BFS 结果(不可达返回空——预览与执行同源);短滑时长 ≤150ms 与再议挂 playtest 观测(battle.md OQ5,knob = move_slide_ms);合法性指示定稿义务源 = A2×6⑤(继承 battle.md 布局决策 D3)。 + +#### 规则 9:四种格子一眼分得清,黑白截图也能逐格分类 +**人话**:这条管棋盘可读性的根。棋盘上四种格态——已开 0 格、已开雾格、未开格、未开雾格——必须一眼可辨,灰度截图下也能逐格分类。这是左键双义的消歧前提:点已开格是移动、点未开格是挖,而误挖不可逆(花 1 AP、可能触雷)——两种格子看起来一样就是事故。已引爆的雷格要"可通行可读":已开的底 + 巢穴纹(危险红族)+ 可作移动目标的视觉线索——走过的雷、能走。四态的视觉构成与语义见「枚举·四种格态」。 + +**举证**:无计算。数字显示判定数据源 = `variant_ids`(digit 恒真透传、雾格隐藏呈现——A2 Core Rule 2;呈现层禁读真实数字自行判定);绘制优先级栈:数字 > 小人 > 旗 > 徽章 > 变异纹理 > 底色,任何元素不遮挡数字、小人与旗不得覆盖文本(art-bible §1.2);义务源 = A2×6②④。 + +#### 规则 10:奖励是什么、包里有什么,随时一眼可读 +**人话**:这条管奖励身份的呈现(B4 奖励系统的呈现承载)。已开的格子里若藏着奖励,格面显示它的家族类型徽章、拾取瞬间档位可读;右侧道具栏是挖掘期的规划面——背包每一件一行: + +| 常读条每行显示 | 说明 | +|---|---| +| 图标 | 家族键定纹样(同族不同档可互辨——刀的两档并排分得清) | +| 名称 | 道具显示名 | +| 属性简述 | 行值快照派生的一句话(伤害 / 回复 / 增益) | +| 状态 | 在包 / 已用(已用的过滤出常读条) | + +**人话**:悬停看完整描述与触发注记,但看不到来源——来源归因是结算明细行的职责。奖励身份键全局恒定、跨 run 禁重掷。其余口径(徽章键集、MVP 呈现范围、栈位、枚举序、Used 过滤)见下方实现参照表。 + +**举证**:无计算。逐项口径与依据 = 「规则 10 的实现参照——奖励徽章与库存常读条」;义务源 = B4×5①。 + +### 规则 10 的实现参照——奖励徽章与库存常读条 + +| 口径 | 内容 | 依据 | +|---|---|---| +| 格面家族徽章 | 五键 attack/defense/heal/blast/weaken——键集冻结;五键槽位在程序化占位映射表预留、未启用键不呈现 | art-bible §4.6.3 | +| 拾取瞬间 | 格面徽章 tier 档位可读 | H1 节拍 | +| 库存徽章 | 八键、reward_id 键——knife_t1/knife_adv 同族互辨是硬需求 | art-bible §4.6.2 | +| MVP 呈现范围 | 格面徽章只在已开格呈现(未开格不预告身份) | B4 Rule 8 现行锁定;art-bible §4.6.3"未开格预读徽章"呈现前提属极性提案实施批——冲突清单⑤ | +| 徽章栈位 | 旗之下、变异纹理之上、不进数字区、不构成格内第二焦点 | art-bible §3.4 | +| 常读条枚举序 | 入包受理序 | B4 E21 | +| tooltip | 完整描述 + 触发注记、不含来源(来源归因由结算明细行承载) | battle.md 口径 | +| Used 过滤 | 已用件过滤出常读条——查询面保留只读、过滤归呈现 | query_inventory 保留只读 | +| 身份恒定 | reward_id / enemy_id / effect_id 三键全局恒定、跨 run 禁重掷 | B4×5① | + +#### 规则 11:身上的增益减益看得见、好坏分得清、没了有交代 +**人话**:这条管 C 区状态槽(B3 状态系统的呈现承载)。角色信息区固定 4 个状态槽,每槽四件套: + +| 槽位元素 | 玩家读出什么 | +|---|---| +| 图标 | 哪种效果(形状纹样定身份,色相不承载身份) | +| 层数角标(L) | 叠了几层 | +| 剩余结算次数点(R) | 还会结算几次 | +| 悬停 tooltip | 来源 + 语义 + 剩余 | + +**人话**:好坏一望即知:有利 = 金族满框、有害 = 红族缺框——判别按效果键的极性表,不看数值正负(存在负值的增益,光看池归属也判不了极性)。开局没有任何状态 → 整排槽隐藏、HP/攻击/RAP 数据框上移补位(空槽占位浪费紧张的纵向空间);图例面板载一句空态说明:「战斗中拾取的增益在此显示,随战斗结束消散」。状态系统不发任何信号——状态栏刷新绑定在结算编排完成的时点(命令式)。 + +**举证**:无计算。逐项口径与依据 = 「规则 11 的实现参照——状态槽呈现口径」;义务源 = B3×4①②③。 + +### 规则 11 的实现参照——状态槽呈现口径 + +| 口径 | 内容 | 依据 | +|---|---|---| +| 图标键 | 效果图标七键——形状纹样身份、色相不承载身份 | art-bible §4.6.1(七键冻结) | +| 极性判别 | 键级极性表:attack_up/defense_up/weaken 有利、ambush 有害——禁以 magnitude 符号判极性(负值增益存在;pool 亦不足以判——enemy_output 池两向并存) | B3×4③ | +| 极性键扩容 | 提案扩展键 enrage/off_guard/sapped 随极性提案实施批 | 极性提案实施批 | +| 语义归属 | 经 SettlementTable `effect_pool_map` 成文(作用面) | B3×4③ | +| 空态裁决 | 槽整体隐藏 + HP/攻击/RAP 上移补位(D1 定稿) | B3×4① | +| 数据源 | B3 投影:六归因字段 + display_name + 授予时数值快照(经 B1 编排面) | B3 已 schema——引用 | +| tooltip 文案 | source_class 文案经 B1 `source_label_map` | B1/B3 协作 | + +#### 规则 12:结算逐行念账——每笔收益标明来源,该高光的当场高光 +**人话**:这条管结算明细带与高光(B1 流水的呈现承载 + B4 行语义)。结算推进时,明细带把 B1 流水逐行实时滚出,每行标明「做了什么、多少、到谁头上、因为什么」——玩家能把每一分收益对回自己哪一步扫雷决策。行按类型各有呈现口径(见下方实现参照表);高光分三档:标中旗金框、清图奖励金框、完美排雷全屏喝彩级——庆祝不打在空兑现上。 + +**举证**:无计算。行协议 = 13 类封闭枚举(B1 Core Rule 14——字段分层见载荷表 #4);高光使用规则 = 事件型用独立高光行、属性型强调用行字段标记(B1);义务源 = B4×5③④ / B1×3①②。 + +### 规则 12 的实现参照——结算明细行呈现口径 + +| 行的类型 | 呈现口径 | 依据 | +|---|---|---| +| 生效行(伤害 / 道具入库) | 实时滚出,逐行来源标注「{行为} {值} → {目标}(来源)」——模板插值,行宽预留 40% 膨胀 + 固定槽高 + clip_contents | 切片缺陷①回归 | +| 效果行 | 两列分示:value = 单次贡献值、pool_value = 池合成值——禁相加(防"−130%×2 行 = −260%"误读) | B1 Core Rule 14 字段 | +| 零值伤害行 | 照常可见、不被吞 | B4×5③ | +| 未生效四类行 + 标旗未中行 | 聚合计数呈现(防逐行惩罚轰炸)+ 形状冗余(图标 / 划线——非色相独载,色弱清单 #9) | B4×5③ | +| 四类未生效行划分 | 道具行(入库 / 生效语境注记)/「奖励未生效」(聚合计数)/「已消耗未生效」/「使用被拒」 | B4×5③ | +| 未选用件 | 语境注记「未选用件随关卡结束清空」(关卡域口径) | B4×5③ | +| 标中旗高光行 | 旗化暴击弹道加重 + 明细行金框(chord 缺席的即时兑现补偿) | B1×3① | +| H2 清图奖励行 | 金框高亮行(轮末仪式峰值) | B1×3① / B4×5④ | +| 完美排雷 | 全屏喝彩级:金光带加倍扫射 + 「完美排雷 ×2」横幅;高光行 value = 实际有效兑付当量;首份即零兑付不发高光 | B1×3② / art-bible §2.2 最高档(创意风险 #2 落点) | + +#### 规则 13:轮末道具窗口在道具栏原地亮起,至多选一件 +**人话**:这条管轮末道具窗口(B4 的呈现承载)。结算推进到窗口锚点(授予段之后、增益减益段之前)时,E 区道具栏原地激活:面板点亮金描边、标题换成「轮末道具窗口 · 至多选用 1 件」,阶段指示同步标出窗口锚点位置;棋盘区去饱和 + 金橙色相偏移并锁输入(明度不变)。满血的治疗包灰置、悬停说明禁选原因(「HP 已满 · 禁选」)。 + +**人话**:选中即承诺——窗口关闭、回结算流,提交的是窗口条目位置;跳过按钮恒在;政策注记常驻「未选用件随关卡结束清空」。背包为空就不弹窗(窗口标记照常登记——回放输入完整性归 B1),结算零停顿。窗口期棋盘输入不竞争——结算期棋盘本就锁输入。窗口激活期间跳过常读条全量重建——全量刷新会冲掉窗口态。 + +**举证**:无计算。E 区原位激活 = battle.md 布局决策 D2;usable_now 投影 = B1 组装载荷;去饱和 + 金橙偏移 = art-bible §2 明度恒定硬规则的合规化实例;窗口触发政策 = MVP 有库存恒弹(候选"仅可用品触发 / 常驻库存条非模态"归垂直切片——B4 D1 义务②注记);窗口态 stomped 教训 = 切片 entry 36;义务源 = B4×5②。 + +#### 规则 14:新机制第一次遇到给提示,忘了随时查图例 +**人话**:这条管 toast 提示、图例面板与教学承载。toast = 棋盘区上缘内侧浮出的非模态提示:自动消退 2.5 秒、不劫持输入、不打断挖掘流。一次性提示共三种(雾首遇 / chord 肌肉记忆 / 不可挖域走近),每个整 run 只弹一次;拒绝、voided、零合法板的提示不属于一次性族——每次都要可感知。 + +图例面板 = 模态覆盖层:格态 / 变异 / 雾语义三组条目,「迷雾 = 无雷盲区」条目恒在、不设解锁门、可随时回看,并载状态槽空态说明(规则 11)。文案定稿权归实现期文案 pass——本规则只锁必含要点与形态。 + +**举证**:无计算。一次性提示封闭枚举(三值 = 雾首遇 / chord / no_adjacent——同时是遥测值域与去重键)的触发、必含要点与频次见「枚举·一次性提示 hint_id」;拒绝与 voided 呈现映射见「枚举·拒绝与 voided 呈现映射」;零合法板见边界情况表;义务源 = A2×6③ / A4×3①②。 + +#### 规则 15:雾格有雾的样子,整片雾开完只报一次"开完了" +**人话**:这条管雾图的呈现(A4 雾变体的呈现承载)。双通道画雾:没开的雾格 = 掩尘完整砖 + 雾纹带——雾身份走形状通道、色相不承载;开过的雾格 = 地板无字 + 底色层淡雾 tint 残留——残留 tint 与"已开 0 格"必须灰度可判定区分:0 格携带"八邻无雷"的推理信息,混淆即污染推理。整片连续雾区全开时,完成信号以视觉合并域为单位一次呈现——相邻两个雾区规则上是独立实例、呈现上合并成一片,禁止逐实例响两次。 + +**举证**:无计算。雾语义条目(无雷盲区)见图例(规则 14);tint 与完成信号随雾重设计批可复核(art-bible 雾格资产占位——内容封禁期);双雾区相邻 = A4 EC11(边界情况表);采纳裁决 = OQ8(battle.md);义务源 = A4×3③。 + +#### 规则 16:一场打完,输了赢了、剩什么没了什么,样样有交代 +**人话**:这条管战斗收尾与资源变化的反馈(B3 / A3 / B4 的呈现承载)。六种反馈各有触发与呈现,逐项见下方实现参照表:胜负终局、状态槽全清、RAP 清零、库存清空、Boss 遭遇变体、弃图刷新确认。两条硬线贯穿:暮蓝 overlay 是全部界面里唯一的暮蓝档、明度不变——失败氛围靠色相与运动表达、不靠变暗;不存在"RAP 满自动刷"——刷新的决策主体恒为玩家。 + +**举证**:无计算。逐项触发与呈现 = 「规则 16 的实现参照——终局与清空反馈」;battle_over 与 level_clear 两反馈不合并、时序不倒挂(边界情况表);"禁止 RAP 满自动刷" = A3 Rule 11;义务源 = B3×4④ / A3 / B4×5⑤。 + +### 规则 16 的实现参照——终局与清空反馈 +| 反馈 | 触发 | 呈现 | +|---|---|---| +| 胜利终局 | 判定行 verdict=win | 金蜡封落于扫雷图区中央 + 全屏暖白金 overlay + 判定行(终局判定行 = 胜负画面数据锚)+「下一节点」+ 明细回看入口 | +| 失败终局 | 判定行 verdict=lose | 明度不变的暮蓝 `#5A6C8C` 氛围 overlay(全作唯一暮蓝档)+ 小人坐砖掸灰 + 插过的旗逐面化光点收回 + 判定行 + 返回 | +| 状态槽全清 | battle_over(B3×4④) | 状态逐槽化光收回 + 微文案「状态随战斗结束消散」(防"我的 buff 哪去了"误读——胜负两终局同款) | +| RAP 清零 | battle_over(A3) | RAP 数字滚动归零 + 微文案(防"攒的钱凭空没了") | +| 库存清空 | level_clear(B4×5⑤——关卡域口径) | 道包逐件化光点飞出 +「剩余道具 ×N 随关卡结束清空」(N = clear_inventory 返回的清空前快照计数——B1 编排义务⑤数据面) | +| Boss 遭遇变体 | encounter=boss | 战斗开始一次性紫赤影子爬过棋盘(§2.6——纯视觉预告)+ 敌人卡体量最大 + 阶段指示附 Boss 名 | +| 弃图刷新确认 | 按钮点击(A3 契约的呈现半边) | 按钮置 AP 条右端,文案实时「RAP−{rap_refresh_cost 表读值} · 本轮剩 N 次」(剩余次数 = `refresh_cap_per_round` − 本轮已用;轮内已用次数读数 = A3 只读观测量缺口,接口漂移登记见 OQ2);入口可用性 = 组合根按只读观测量预判(rap ≥ rap_refresh_cost ∧ 剩余次数 > 0)+ `request_refresh` 返回值兜底(A3 无 `can_request_refresh` API——不引入);确认微交互显式提示代价与旧板旗失效;确认交互与常规确认不同位(位置通道——破坏性确认异位,色弱清单 #7);RAP<8 / cap 超限拒绝可见(边界情况表);换图后 AP 不重置、剩余次数扣减可见 | + +#### 规则 17:界面收到的每份消息,字段清单先写成合同 +**人话**:这条管跨系统消息的契约(切片教训的硬性要求之一)。界面消费的每一份跨系统载荷——事件、快照、回执、遥测行——都必须先把字段清单写成文(共 13 项,见下方实现参照表)。清单双重用途:既是实现契约,也是消费侧断言——信号参数没有运行时类型检查,切片就曾因窗口载荷缺字段直接崩溃,逐字段断言就是防缺字段、防多字段的那道闸。 + +**举证**:无计算。13 项载荷 schema = 「规则 17 的实现参照——呈现信号载荷 schema」;口径对齐 A2 三事件先例;消费侧断言 = ADR-0001"信号参数无运行时类型检查"的缓解;切片教训硬性要求①(entry 17①——缺项即 blocking),AC-24 承载。 + +### 规则 17 的实现参照——呈现信号载荷 schema(13 项) +| # | 载荷 | 来源 | 字段 schema | 状态 | +|---|---|---|---|---| +| 1 | `cell_revealed` | A2 | `{cell, kind ∈ {safe,mine}, digit(仅 safe 携带,恒真透传), variant_ids, channel ∈ {initial,chain,dig}, reward_eligible}` | A2 Core Rule 2 已 schema——引用 | +| 2 | `flag_judged` | A2 | `{cell, hit}`(轮末判定批、行主序) | 同上 | +| 3 | `round_end_board` / `round_end_stopped` | A2 / A3 | `{path_tag ∈ {ap_exhausted,cleared}, continuation_hint ∈ {same_board,new_board}}`(双发射源 payload 同构;信号实名 per ADR-0001) | 同上 + ADR-0001 | +| 4 | 流水行 ×13 类 | B1 | 公共 7 字段 `type / source_class / origin_cell(板级行可空)/ round_index / display_name / value / highlight?({flag_hit, clear_reward_h2, perfect_broken})` + 特有:伤害行/道具行(生效)→ `target`;效果行 → `effect_id` + `pool_value`;未生效四类 + 标旗未中行 → 聚合计数/批次字段;source_class 值域 ⊆ `source_label_map` 键集(B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack}) | B1 Core Rule 14 已 schema——引用+重述 | +| 5 | 结算快照 | B1 | `presentation_snapshot()` 返回(终局呈现数据面——despawn 前全量状态);独立 `settlement_snapshot` 信号系切片实现、B1 GDD 未登记契约——D1 现行消费 = 步骤流判定行 + `presentation_snapshot()`;若需独立信号回 B1 修订批补登记(OQ3) | 切片实现 / 契约待登记 | +| 6 | 意图回执 | A2 | `ActionOutcome{accepted, outcome ∈ {executed, voided, rejected}, reject_reason ∈ {board_face, resource_face, phase, board_invalid}, sub_reason(仅 board_face:not_adjacent / target_revealed / target_concealed / flag_present / flag_absent / no_path / out_of_catalog)}` | A2 已 schema——拒绝呈现数据源 | +| 7 | 资源面拒绝 | A3 | 无载荷(`try_spend` / `request_refresh` 仅 bool + debug 诊断)——呈现文案按动作上下文映射(诚实登记:资源面原因客户端推断,双源可辨由"动作种类 + 拒绝源"组合承载) | A3 AC 前言口径——引用 | +| 8 | 窗口载荷 | B1 组装 | `query_inventory` 逐件 `{item_id, source{cell,round}(不呈现于库存列表与 tooltip), 行值快照投影, 状态 InStock/Used}`(枚举序 = stock 受理序)+ `usable_now`(B1 守卫预判——满血治疗包 false) | B4/B1 已 schema——引用 | +| 9 | 奖励身份投影 | B1 呈现面 | `query_reward_identity(cell) → {reward_id, category, tier} \| null`(引用级投影;未开格可查但 MVP 不呈现) | B4 AC-2 已 schema——引用 | +| 10 | 状态投影 | B3 经 B1 | `query_active(side)` 元素 = 六归因字段 + `display_name` + 授予时快照 `(magnitude, unit)`;有效值 = L×快照 magnitude | B3 已 schema——引用 | +| 11 | 单位投影 | B2 | 敌单位七字段 `enemy_id/hp/max_hp/atk/heavy_multiplier/pattern/cap` + `beat` + 存活态;hero 投影 `hp/max_hp/atk`(经 `hero_unit()/all_enemies()`) | B2 已 schema——引用 | +| 12 | 零事件外发动作刷新面(命令式回执清单) | A2/A3/B3/B4 | 旗插/拔与移动成功(A2 零事件外发)→ `settle_batch` 回执后重查 `current_board()`;AP/RAP(A3 无推送)→ 每次写路径回执后重读 getter;状态效果(B3 零信号)→ B1 结算编排完成时点刷新;库存(B4 查询面)→ 写路径回执后重查 `query_inventory` / `stock_count()` | battle.md 更新触发注记——本行成文为契约 | +| 13 | D1 自有遥测 | D1 | `ux_round_input_ratio{ratio}` · `ux_round_exit{path_tag}` · `ux_refresh_attempt{result ∈ {accepted,rejected_rap,rejected_cap}, round_refresh_index ∈ [1,2]}` · `ux_window_choice{selected_item_id, usable_now, skip}` · `ux_reject{source ∈ {board,resource,phase}, sub_reason}` · `ux_hint_shown{hint_id}`——写 playtest JSONL `ux` 行族(playtest_logger 扩展) | 本 GDD 成文(新) | + +#### 规则 18:对象销毁前先拍快照——界面永远不回读已销毁的东西 +**人话**:这条管信号到达时点的状态可见性(切片教训的硬性要求之一)。核心对象销毁前后,界面不准"回头查"核心内部——只许用三样东西:消息载荷自带的数据、销毁前已取好的快照、封装好的只读投影。落到三条细则: + +| 细则 | 内容 | +|---|---| +| 销毁前捕获 | 终局与死亡的呈现数据(血条归零、卡体灰化、判定行)在核心对象销毁前已进流水行与快照 | +| 载荷自包含 | 每行流水自带来源 / 值 / 目标——界面不按行序反推目标状态机、不回查容器补数据 | +| 生命周期态禁读 | "战斗还能不能刷"只准问 `is_battle_live()` 与阶段投影,禁止读核心对象内部状态位 | + +**人话**:敌卡死亡灰化后仍留场的呈现 = 就地更新保留的最后已知投影(规则 6)——不回读 Dead 句柄以外的任何东西。 + +**举证**:无计算。销毁时序 = B1 判定段先于 battle_over 清理序列(快照含 despawn 前终局——切片缺陷②教训:battle_finished 后读已销毁的 b2.hero 崩溃);自包含字段 = `target` / `effect_id` / `pool_value` / `origin_cell`;生命周期观测口经组合根;切片教训硬性要求③(entry 17③——缺项即 blocking),AC-20 静态扫描承载。 + +#### 规则 19:美术没画好先用代码画的占位图顶着,占位图同样要过硬指标 +**人话**:这条管美术资产的解耦。全部等终稿美术的面(砖面 / 雾纹 / 敌像 / 图标 / 徽章 / 立绘 / 蜡封)都以程序化占位先行:色值直接取冻结色板、形状按形状语言参数域用代码绘制(圆角砖 / 直角数据框 / 纹带形);图标、徽章、线索按键契约挂进键位映射表(数据键 → 占位纹样 ID,一键一槽位)——终稿美术到位时零数据层改动、零运行时换图。 + +**人话**:占位图同样要过硬指标:数字可读性(1–8 全档对格底 ≥4.5:1、0.5× 缩放可辨、纹带不侵数字字形区)必须通过——色板参数域从结构上保证(底足够亮、墨足够深)。样张定稿 = 砖面形态与色板的冻结闸、终稿资产开工的前置;样张批不阻塞 D1 其余全部验收。 + +**举证**:判据 = F2 数字对比恒等式(「数值结论与推导·F2」);色板 = art-bible §4.1 冻结色板(底 L≥0.30 ∧ 饱和 ≤0.40 ∧ 墨 L≤0.027);形状语言 = §3;样张标准 = §1.4 第一验收项;键集扩容走 §4.6 修订;终稿开工 = asset-spec 批。 + +#### 规则 20:切片里的临时名字统一换成正式名 +**人话**:这条管命名(正式批次的一次性统一)。切片阶段用的几个临时名换成本 GDD 正式名——换的只是名字、不改任何行为:刷新接口名、每轮刷新上限字段名、装板参数、终局信号实名各归其位,明细见下方实现参照表。只增不换的特例:`can_request_refresh` 不引入——上游没有这个接口。 + +**举证**:无计算。五项对照 = 「规则 20 的实现参照——切片 provisional 命名统一」;入口可用性 = 组合根按只读观测量预判 + `request_refresh` 返回值兜底(规则 16);统一核销 = session-state entry 35,AC-25 grep 断言承载。 + +### 规则 20 的实现参照——切片 provisional 命名统一 +| 切片 provisional 名 | GDD 正式名 | 载体 | +|---|---|---| +| `request_refresh_active()` | `request_refresh` | A3 API(Active 相位受理——A3 Rule 11 现行口径) | +| `can_request_refresh()` | 不引入(A3 无此 API) | 入口可用性 = 组合根按只读观测量预判 + `request_refresh` 返回值兜底(规则 16) | +| `refresh_max_per_round` | `refresh_cap_per_round` | ActionEconomyTable 结构位(默认 2)——D2/registry 字段回写随实现批(A3 Schema 定稿节已预留) | +| `during_round`(A2 装板参数) | 无独立参数(受理域语义) | A2 `install_board` 受理域增 Active·仅弃图刷新路径(B1 `refresh_board` 编排内)——A2 Core Rule 3 | +| `round_end`(信号名) | 实名 `round_end_board`(A2 面)/ `round_end_stopped`(A3 面) | ADR-0001 信号实名映射 | + +#### 规则 21:弹层排队进出——打开哪层,哪层就真的能用 +**人话**:这条管模态覆盖层的层级纪律(切片缺陷⑤的纪律转正)。全部模态弹层(图例面板 / 明细回看 / 胜利 overlay / 失败 overlay)走同一张登记序栈:统一入栈出栈、后开置顶、关闭逐层恢复;任何一层打开时,"被它遮住的东西要么隐藏、要么锁输入"必须成立——功能存在但被遮住点不到,是呈现层隐形缺陷的高发区。道具窗口不是模态:不入栈,但锁 D 区输入(规则 13)。 + +**人话**:TunePanel 显式初始化为不可见(历史 quirk 转正);它是切片调试入口,正式侧归 C4 天赋树——C4 落位时退役,Playtest 诊断按钮(卡死转储)保留为调试设施。 + +**举证**:无计算。窗口态锁 D 区 = 规则 13;退役时点 = battle.md OQ10 承接;AC-21 承载。 + +#### 规则 22:中文不缺字、字看得清、颜色不独挑大梁、不用键盘也能玩 +**人话**:这条管本地化与可访问性的内建基线:中文显示不缺字、字看得清、颜色不是唯一的信息通道、动效可以减弱、全程只用鼠标也能玩。逐项如下表: + +| 基线 | 要求 | +|---|---| +| 中文字体 | 随包内置 + 设为主题默认字体——运行时默认字体无 CJK 字形,豆腐块 = 验收硬断言 | +| 按钮文案 | 4 汉字上限(「停手」「弃图刷新」「跳过」「选用」);译英 ≤1.6× 不破版;德语长词允许两行或图标+缩写 | +| 明细行宽 | 模板预留 40% 膨胀 | +| 对比度 | 数字对格底 ≥4.5:1(WCAG AA) | +| 色弱安全 | 无信息仅靠色相承载(九对语义对逐项成立)、灰度可辨 | +| 字号下限 | 棋盘数字 ≥18px / 明细正文 ≥14px / tooltip ≥13px(@1280×800) | +| 闪烁 | 无 >3 次/秒 | +| 减动效 | 全部动画时长减半或跳终态开关;结算逐行兑现是信息载体——不可整体跳过、可加速 | +| 输入 | 纯鼠标可完成全部操作 | + +**举证**:无计算。对比判据引用 = F2;本地化契约消费方 = D6(CJK / 按钮宽 / 术语表);正式分层(文本缩放 / 色盲模式 / 按键重映射)待 accessibility-requirements.md 定稿(battle.md OQ2 登记缺口);承载 AC-23 / AC-26。 + +## 枚举 + +### 四种格态(A2×6②——灰度截图逐格可分类) + +| 格态 | 视觉构成 | 语义 | +|---|---|---| +| 已开 0 格 | 裸土地板 | 携带"八邻无雷"推理信息——与已开雾格必须灰度可判定区分(混淆即污染推理) | +| 已开雾格 | 地板无字 + 底色层淡雾 tint 残留 | 雾格只是不显示;tint 为残留视觉锚(OQ8 采纳) | +| 未开格 | 掩尘完整砖 | 左键 = 挖(1 AP) | +| 未开雾格 | 掩尘砖 + 雾纹带 | 左键 = 挖;雾语义 = 无雷盲区(图例恒在条目) | + +### 光标语义(A2×6③⑤——悬停预承诺) + +| 光标 | 悬停目标 | 伴随指示 | +|---|---|---| +| 移动箭头 | 已开格 | `compute_move_path` 路径蓝细线点迹(Rule 8)+ 目标格边框高亮 | +| 挖镐 | 可挖未开格 | 目标格边框高亮(可挖域另有常驻呼吸微光) | +| 旗形 | 旗插拔语义 | 目标格边框高亮 | +| 禁止 | 不可挖域 | + 首 run 一次 no_adjacent toast | + +### 一次性提示 hint_id(封闭枚举——遥测值域与去重键) + +| 值 | 触发 | 必含要点 | 频次 | +|---|---|---|---| +| `fog_first`(雾首遇——A4×3②) | 首个实际含雾格的板进入可操作状态(挖掘阶段开始;生成期一次查询即判定,免疫降级板误触发) | 无雷盲区不赌命 / 雾格不传播连锁(连锁止于雾界属正常)/ 雾内插旗纯损 | 每 run 一次 | +| `chord`(chord 肌肉记忆——A2×6③) | 经典扫雷玩家首日尝试双击快开 | 本作无双击快开——插旗后轮末自动判定标中;点数字格可移动小人 | 每 run 一次 | +| `no_adjacent`(不可挖域——Rule 7) | 不可挖域悬停 | 需先走近(点击已开格移动,免费) | 每 run 一次 | + +- hint_id 封闭枚举 = 上表三值;拒绝 / voided / 零合法板 toast 族不占 hint_id(拒绝须每次可感知——见边界情况表)。 +- 文案定稿权归实现期文案 pass(本 GDD 只锁必含要点与形态约束)。 + +### 拒绝与 voided 呈现映射(A2×6⑥——映射源 = reject_reason + sub_reason) + +| 拒绝源 | 触发 | 呈现动作 | 文案 | +|---|---|---|---| +| board_face·not_adjacent | 挖非相邻格 | 目标格旗朱 `#C63D22` 边框闪 + toast | 「需先走近」 | +| board_face·target_revealed | 目标已开 | 同上 | 「该格已翻开」 | +| board_face·target_concealed | 目标未探明 | 同上 | 「该格未探明」 | +| board_face·flag_present | 目标有旗 | 同上 | 「须先拔旗」 | +| board_face·flag_absent | 目标无旗 | 同上 | 「该格没有旗」 | +| board_face·no_path | 路径不通 | 同上 | 「路径不通」 | +| board_face·out_of_catalog | 调试态 | debug 级 | 不与玩家语义混桶 | +| resource_face(AP 不足 / RAP 不足——无上游载荷,按动作上下文映射) | 资源不足 | AP/RAP 数字脉冲 + toast | 「AP 不足 · 剩 0 点」 | +| phase | 非 Active | toast | 「结算进行中」 | + +- 图标 + 文案冗余(非色相独载)。 + +### ux 遥测行族(载荷表 #13 的字段与值域展开) + +| 行 | 字段 | 值域 | +|---|---|---| +| ux_round_input_ratio | ratio | 每轮输入动作数 ∶ AP 消耗(P4 ≤2 观测假设) | +| ux_round_exit | path_tag | 上游封闭枚举(ap_exhausted / cleared / stopped) | +| ux_refresh_attempt | result; round_refresh_index | result ∈ {accepted, rejected_rap, rejected_cap};index ∈ [1,2] | +| ux_window_choice | selected_item_id; usable_now; skip | skip / usable_now 布尔 | +| ux_reject | source; sub_reason | source ∈ {board, resource, phase};sub_reason 同 ActionOutcome | +| ux_hint_shown | hint_id | hint_id 封闭枚举(上表) | + +## 数值结论与推导 + +> D1 零玩法公式(显式排除声明见本节末)——本节仅承载呈现几何与可读性判据。全部取色禁新增色值,一切经 art-bible §4.1/§4.4 冻结色板。 + +### F1 棋盘格尺寸(cell_size) + +**结论**:cell 为两档定值(battle.md 拍板)——12×12 → 36px、10×10 → 45px;F1 公式为可行性校验器而非闭式推导:floor(min(D_w − pad_h, D_h − ap_bar_h − pad_v) ÷ N) ≥ cell(N) 且 cell(N) ∈ [30, 45] 断言成立;数字字号 = max(18, ceil(cell × 0.5)),像素字体整数倍缩放。 + +#### 公式 + +``` +两档定值:cell(12) = 36px、cell(10) = 45px(battle.md 拍板值,非公式导出) +可行性校验(装板期断言,违例 fail-fast): + floor(min(D_w − pad_h, D_h − ap_bar_h − pad_v) / N) ≥ cell(N) + 且 cell(N) ∈ [30, 45] +N ∈ {10, 12}(A1 BoardGenTable 尺寸档);D 区基准 1280×800 下 D_w=760、D_h=520−44(AP 条) +@1280×800 实算(pad_h=pad_v=16):min(744, 460) = 460 + 460 ÷ 12 → 38 ≥ 36 ✓ 460 ÷ 10 → 46 ≥ 45 ✓ +数字字号 = max(18, ceil(cell × 0.5)),像素字体整数倍缩放(canvas_items stretch 保证清晰) +``` + +**Variables:** + +| 变量 | 符号 | 类型 | 范围 | 说明 | +|---|---|---|---|---| +| D 区宽/高 | D_w / D_h | int px | 布局常量 | 五区布局基准(battle.md Layout) | +| AP 条高 | ap_bar_h | int px | 44 | D 区顶条 | +| 棋盘维数 | N | int | {10, 12} | A1 行值(MVP 两档) | +| 定值档 | cell(N) | int px | {36, 45} | battle.md 拍板两档;非闭式导出(固定边距下无单式可同时得出两档——两定值所需可用高度区间不相交) | + +- **Example**:12×12 → floor(min(760−16, 476−16)/12) = 38 ≥ 36 ✓(定值取 36)→ 数字字号 = 18px;10×10 → floor(460/10) = 46 ≥ 45 ✓(定值取 45)。 +- **为何定值而非闭式**:cell=36 要求可用高度 ∈ [432,443]、cell=45 要求 ∈ [450,459]——两区间不相交,任何固定 pad 的闭式都无法同时得出两档;两档值来源 = battle.md 拍板("格 36–45px 见方"两端),公式只承载界限与可行性断言。 + +### F2 数字对比恒等式(引用 art-bible §4.1——D1 不新增参数) + +**结论**:1–8 全档数字对所在格底 WCAG 对比 ≥4.5:1(§1.4 第一验收项,对程序化占位同样适用)——由色板参数域结构保证:格底相对亮度 L≥0.30 ∧ HSV 饱和 ≤0.40 ∧ 数字墨 L≤0.027 ⇒ 任意底墨组合 ≥4.5:1 恒成立。 + +| 实测锚(底 / 墨) | 对比 | +|---|---| +| #33261B / #C9AF83 | 6.9:1 | +| #4A1E14 / #B39A78 | 5.3:1 | + +- D1 侧判据 = 灰度截图分类 + 对比抽测进 AC;换 biome 色板须重跑 §1.4(换皮只换四槽、永不变信)。 + +### 显式排除声明 + +| 对象 | 归属 | +|---|---| +| 伤害 / 经济 / 概率 / 生成公式 | A1–B4/B1/C3 | +| AP/RAP/价目显示值 | A3 表读值投影 | +| 伤害数字 | B1 流水行 value 投影 | +| D1 拥有的全部 | F1 几何换算、F2 可读性判据的引用执行、Tuning Knobs 节呈现常量 | +| 随机 | 零——呈现层无任何 RNG 消费(S1/S2 扫描域适用) | + +## 状态 + +战斗屏状态机(触发全部来自核心信号/回执——呈现层不自持玩法状态;"阶段"投影枚举 = {挖掘中, 结算中, 道具窗口}): + +| 屏状态 | 进入触发 | 变化 / 出口 | +|---|---|---| +| 挖掘阶段(默认态) | 轮次开始 / 结算完毕回 Active | D 区可交互(AP 条点亮、可挖域呼吸微光);B 区敌人待机(意图徽标全程可见);明细带收起为上轮摘要 1 行;能量 2/10 | +| 结算阶段 | `round_end`(A2 棋盘路径 / A3 stopped 直发) | D 区锁输入(棋盘去饱和 + 金橙色相偏移,明度不变,数字保持全对比);明细带展开 84px 逐行滚动;阶段指示→"结算中";能量 7/10;B1 步骤流驱动逐行兑现 | +| 道具窗口态 | B1 授予段完成(窗口锚点信号) | E 区原位激活(Rule 13);空库存直接 skip 不弹窗;选用/跳过 → 回结算流(结算阶段续) | +| 清图瞬间 | cleared 判定(round_end) | 金粒喷泉 + 彩纸 3–5s 即收(§2.3);H2 清图奖励行金框强调;随后换新图 install | +| 完美排雷高光 | B1 高光行(perfect_sweep) | 全屏喝彩级:金光带加倍扫射 + 「完美排雷 ×2」横幅(叠加于清图瞬间之上) | +| 标中旗高光 | 流水行 highlight=flag_hit | 该旗化暴击弹道加重 + 明细行金框(当轮即兑付) | +| 敌人死亡呈现 | 流水行击杀标记(target 字段) | 该敌卡血条归零 → 灰化 + 碎裂退场(禁瞬消、就地更新) | +| 胜利终局 | 判定行 verdict=win | 金蜡封落于扫雷图区中央 + 暖白金 overlay + 判定行 + 状态槽全清反馈 + RAP 清零反馈;出口 = 下一节点(runner) | +| 失败终局 | 判定行 verdict=lose | 暮蓝氛围 overlay(明度不变)+ 旗化光点收回 + 判定行 + 明细回看 + 状态槽全清反馈 + RAP 清零反馈;出口 = 返回 | +| Boss 遭遇变体 | encounter=boss | 战斗开始一次性紫赤影子爬棋盘 + 敌卡体量最大 + 阶段指示附 Boss 名 | +| 雾图变体 | 板含雾格 | 雾纹带 + 首遇提示(每 run 一次)+ 已开雾格 tint 残留 + 雾区全开合并域完成信号 | +| 空态:无状态效果 | query_active 双侧空 | 状态槽隐藏、数据框上移补位(Rule 11) | +| 空态:零库存 | 关卡初 / 清空后 | E 区「道包空空——安全格里藏着补给」(指引性空态,非报错) | +| 调试变体 | 测试模式 | TunePanel 入口(正式侧归 C4 退役)+ Playtest 诊断按钮 | + +**不变式**:全部状态触发来自核心信号/回执——呈现层不自持玩法状态;模态子栈(Rule 21 统一登记序)= 图例面板 / 明细回看 / 胜利 overlay / 失败 overlay——任一打开时遮挡物隐藏或输入锁定;非模态层 = toast(自动消退)与窗口态(锁 D 区)。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| 每次拒绝(A2×6⑥) | 棋盘面拒绝(sub_reason 七值)→ 目标格旗朱 `#C63D22` 边框闪 + toast(文案映射见枚举节);资源面拒绝(AP 不足 / RAP 不足——无上游载荷,按动作上下文映射)→ AP/RAP 数字脉冲 + toast;相位拒绝 → toast「结算进行中」;图标+文案冗余(非色相独载) | 映射源 = reject_reason + sub_reason——每次拒绝玩家可感知 | +| 轮末意图 voided(至少 stop 被清零须可感知) | toast「本轮已清图,停手未执行 · 剩余 AP 已清零」——防 [dig清, stop] 静默改判清图、剩余 AP 清零不入 RAP 的读感欺骗(F4 联合清图让位在生产面不可构造,本条为诚实兜底) | A2×6⑥ / A2 voided 语义 | +| 零合法板(A1 EC5 → B1×3③——三形态选型定稿) | B1 上抛非法产物标记 → toast 诚实提示(非模态、不打断);补偿与回退不提供(补偿逻辑归 B1 域外、确定性回退已知好种子 = 实现期 ADR 候选〔A1 OQ2 关联〕——MVP 面向玩家的形态 = 仅提示);玩家永不卡死由 A1 验收三分支保证,提示是体验债的诚实兜底(原"玩家不可见遥测域"口径由本条承接闭合) | B1×3③ / A1 EC5 | +| 弃图刷新被拒(RAP 不足) | 按钮 + RAP 读数脉冲 + toast | A3 EC13 | +| 弃图刷新被拒(本轮 cap=2 超限) | toast「本轮刷新次数已用完」 | A3 EC15 | +| 确认微交互缺一步 | 不提交(防误触——破坏性确认异位) | Rule 16 | +| 两个雾区相邻(A4 EC11) | 规则上独立布置实例、呈现合并为一片——雾区全开完成信号以视觉合并域为单元、禁实例级双响(信号只响一次);样张最坏用例含"宽度 1 雾带两侧毗邻数字格" | A4 EC11 / A4 AC-9 | +| 雾首遇触发边界 | 触发谓词 = 首个实际含雾格的板进入可操作状态——免疫 EC2 降级板(无变异)误触发、免疫贴脸雾 t=0 歧义;一次性作用域 = 每 run | A4 UI Requirements 修正口径 | +| 敌人死亡与终局同帧(despawn 时序) | 死亡呈现数据 = 流水行击杀标记 + 目标字段(despawn 前已产出——Rule 18①);battle_over 清理序列后呈现层零核心回读;`is_battle_live()` false 后视图进入终局态只消费快照 | Rule 18① | +| 窗口打开时库存为空 | 不弹窗、直接 skip(结算零停顿);窗口标记照常登记(B1 回放输入完整性) | B4 E8 | +| 满血时窗口含治疗包 | usable_now=false 灰置 + 禁选原因 tooltip;强制提交(不可经 UI 达成——禁选态拦截)→「使用被拒」行兜底 | B1 守卫 | +| battle_over 与 level_clear 反馈叠加(关卡末战) | RAP 清零 + 状态槽全清反馈先行(battle_over),库存清空反馈随后(level_clear 编排)——两反馈不合并(防"N 道具消失"误读为战斗结算内容) | Rule 16 | +| 模态覆盖层叠开(图例上开明细回看) | 统一登记序栈——后开者置顶、关闭逐层恢复;任一层打开时其遮挡物隐藏/输入锁定断言成立 | 缺陷⑤回归 | +| 悬停文案换行 | 全部动态区固定槽高 + clip_contents——换行裁剪不推挤布局(明细行 40% 膨胀 + tooltip 独立浮层) | 缺陷①回归 | +| CJK 字形缺失 | 豆腐块 = AC 硬断言失败(字体随包 + theme 默认;首次导出 checklist 项) | ADR-0003 | +| 敌卡刷新与死亡灰化竞争 | 就地更新(字段级 setter)禁全量重建——死亡灰化态不被后续刷新吞掉;窗口激活期同理跳过常读重建 | entry 36 教训 | +| 减动效开启 | 全部转场/高光/演出时长减半或跳终态;结算逐行兑现降速不跳过(信息载体);无闪烁 >3 次/秒恒成立 | Rule 22 | +| 移动目标 = 当前格 | 幂等零移动——无动画、无 toast | A2 EC14(合法受理的呈现静默) | +| `compute_move_path` 返回空(不可达) | 路径预览不显示 + 禁止光标(预览与执行同源) | A2 AC-14② 对侧 | +| 键集扩容(新 effect_id / reward_id / enemy_id 上线) | 键槽映射表缺键 = 呈现层 fail-fast(debug 断言 + 占位"?"纹样 + 诊断,禁静默空图标)——扩容走 art-bible §4.6 修订(禁运行时换图) | Rule 19 / AC-15 | + +## 依赖 + +| 系统 | 方向 / 性质 | 接口契约 | +|---|---|---| +| A2 | 上游 hard | 读:`current_board()` / `reveal_record()` / `compute_move_path(cell)`(呈现面,entry 39 E2 收口);订阅:零直订(ADR-0001——B1 facts 与组合根转发);写:`settle_batch(intents)` 经组合根(D1×6①);消费其三事件 schema(载荷表 #1–3)与 ActionOutcome(#6);D1×6 六项义务全落位(台账表) | +| A3 | 上游 hard | 读:`phase / ap / rap / last_round_end`(getter-only——entry 39 E5);刷新意图经组合根 → B1 `refresh_board` 编排内 A3 `request_refresh`(D4——挖掘期 Active 受理、每轮上限 `refresh_cap_per_round`=2);接口漂移登记:轮内已用刷新次数读数缺口(OQ2);AP/RAP 常显、RAP 清零反馈、双源拒绝呈现(A3 契约) | +| B1 | 上游 hard(数据供给主通道) | 读:步骤流信号(13 类行协议——载荷表 #4)、`presentation_snapshot()` / `is_battle_live()` / `stock_count()` / `query_reward_identity()`(切片实现、registry `d1_presentation_surface` 登记、B1 GDD 契约登记待批——冲突⑧/OQ3);写:`submit_window_selection(窗口位置序)` / 刷新提交面;level_clear 反馈行数据面(清空前快照计数);B1×3 + B4×5 + B3×4 呈现义务的统一数据源契约 = B1 Core Rule 14 | +| B2 | 上游投影 | `hero_unit() / all_enemies()` 只读投影(entry 39 E3;切片实现、B2 GDD 契约登记待批——冲突⑧);血条/顺位/姿态/存活数据面;despawn 前捕获纪律(Rule 18);B2×4 四项义务落位(敌人卡 Rule 6) | +| B3 | 上游投影(经 B1) | 零直读(B1 协作对象私有化)——状态投影经 B1 编排面(载荷表 #10);EC12 空态、极性映射、全清反馈(Rule 11/16) | +| B4 | 上游投影(经 B1) | 零直读——`query_reward_identity()` / 库存载荷经 B1(载荷表 #8–9);奖励徽章键 §4.6.2、窗口呈现、四类行、H2、关卡末清空反馈(Rule 10/12/13/16) | +| A4 | 上游 soft(呈现规格) | 雾纹理双通道、图例条目、首遇提示三要点、tint + 完成信号(A4×3——Rule 9/14/15);A4 UI Requirements 为本 GDD 引用源 | +| A1 | 间接 | 零直接接口——EC5 零合法板数据面经 B1 上抛(非法产物标记 → toast 告知);biome 标签 = 换皮色板数据锚(程序化占位消费);产物字段保真是呈现诚实的数据前提 | +| D2 | 数据读取 | display_name 等本地化名经核心读表面;D1 自有常量不入 D2 玩法表(Tuning Knobs 载体声明) | +| C1-lite(level_runner) | 上游 hard | A 区节点链 / 当前节点 / HP 携带(关卡域);胜利 overlay「下一节点」→ runner 推进(写经组合根);level_clear 编排反馈;C1 GDD 未开始——本三项为切片代码契约,C1 GDD 定稿时回填(接口漂移回 C1 批) | +| C2 | 预留 | 关卡结算屏(关卡末清空反馈的完整屏形态)归后续 spec `design/ux/level-clear.md`(battle.md OQ9)——MVP 以 overlay 反馈承载 | +| B5 | 下游 hard | D1→B5 交付物:五区布局与双舞台裁决(第三形态)、B 区演出区结构、明细带呈现位、流水行协议呈现语义、敌人卡呈现语义(死亡灰化/禁瞬消)、art-bible §4.6 键位映射表——B5 只叠加动画密度与演出(受击反馈/弹道/退场动画),呈现语义已在本 GDD 定稿;"战斗表现美术测试"以本布局出样 | +| D4 | 下游 soft | 音频挂点事件源 = 明细行类型流 + toast 提示触发(B1/B4 音频挂钩点清单引用);MVP 无音频(Alpha) | +| D5 | 下游 soft | 教学承载位 = toast 体系 + 图例面板(雾三要点 / chord 提示 / 蓄力拍提示候选 / RAP 清零 / 库存清空教学条目)——D5 GDD 定稿口径 | +| A5 | 预留 | 探测类道具显影面(未开格身份呈现深度 OQ2)——接口归 A5 GDD,本 GDD 不预写 | +| D6 | 下游 | 本地化契约消费方(CJK/按钮宽/术语表——Rule 22) | + +- **索引分层注记**:systems-index 依赖分层 "D1 ← A2,A3,B1"——本 GDD 实际投影消费扩至 B2/B3/B4/A4/runner,全部经 B1 编排呈现面 + 封装收口批成文呈现面(entry 39/40),零新增运行时直调边(B2/B3/B4 相互与对 D1 的"零直接接口"纪律保持;呈现面接口清单 = API Boundaries 登记载体)。D1 无游戏状态、无 knob 联动、无 RNG——层 4 Presentation 定位不变。 + +### 呈现义务台账逐项落位表(门禁核对用——27 项,缺项即 blocking) + +| 义务源 | 义务 | 承接方式(本 GDD 落点) | 状态 | +|---|---|---|---| +| A2×6 | ① stop 意图经 `settle_batch` 提交、不直调 A3 | Core Rule 7 + 写路径总表(Rule 4)+ AC-13 | 落位 | +| A2×6 | ② 四种格态一眼可辨 | Core Rule 9 + AC-4 | 落位 | +| A2×6 | ③ 悬停预承诺 + 可挖域常驻指示 + chord 首遇提示 | Core Rule 7 / 14② + AC-5 / AC-14 | 落位 | +| A2×6 | ④ 已引爆雷格"可通行可读" | Core Rule 9 + AC-4 | 落位 | +| A2×6 | ⑤ `compute_move_path` 呈现语义 = 合法性指示(裁决继承 battle.md D3) | Core Rule 8 + AC-6 | 落位 | +| A2×6 | ⑥ 单意图帧收集 + voided 呈现 + 每次拒绝可感知 | Core Rule 7 + 边界情况表 + AC-7 / AC-8 | 落位 | +| B2×4 | ① enemy_id→图像线索映射清单成文(预告时序 = 下一拍、挖掘期全程可见;含强度档位/环长直觉编码;双通道) | Core Rule 6 + 意图线索映射目录(下表)+ AC-10 | 落位 | +| B2×4 | ② 线索映射全局恒定(enemy_id 键,跨 run 禁重掷) | Core Rule 6 / 10 + AC-10 | 落位 | +| B2×4 | ③ 单位顺位与存活可读 | Core Rule 6(顺位徽标)+ AC-10 | 落位 | +| B2×4 | ④ 敌方 side 池呈现口径(共享单值、各卡同值) | Core Rule 6 + AC-10 | 落位 | +| B3×4 | ① HUD 常显 + EC12 空态规格定稿(裁决:槽隐藏) | Core Rule 11 + AC-12 | 落位 | +| B3×4 | ② effect_id→icon 映射键契约 | Core Rule 11(引用 art-bible §4.6.1 七键冻结)+ AC-15 | 落位 | +| B3×4 | ③ 有利/有害极性呈现映射(非 magnitude 符号) | Core Rule 11(键级极性表 + pool 语义归属)+ AC-12 | 落位 | +| B3×4 | ④ 战斗结束全清的状态栏去留 | Core Rule 16 + 状态节胜负终局 + AC-16 | 落位 | +| B4×5 | ① 已开格 + 库存奖励身份恒定可读(常读条 = 挖掘期规划面) | Core Rule 10 + AC-9 | 落位 | +| B4×5 | ② 道具使用窗口呈现(原位激活/单件/usable_now/政策注记/空库存) | Core Rule 13 + AC-13 / AC-17 | 落位 | +| B4×5 | ③ 结算明细四类行呈现(聚合计数 + 语境注记) | Core Rule 12 + AC-11 | 落位 | +| B4×5 | ④ H2 清图奖励行强调呈现 | Core Rule 12 + 状态节清图瞬间 + AC-11 | 落位 | +| B4×5 | ⑤ 关卡末(level_clear)库存清空可见反馈 | Core Rule 16 + 边界情况表 + AC-16 | 落位 | +| B1×3 | ① 高光行强调呈现(标中旗金框 / H2 金框) | Core Rule 12 + AC-11 | 落位 | +| B1×3 | ② 完美排雷呈现形态 | Core Rule 12 + 状态节 + AC-11 | 落位 | +| B1×3 | ③ EC5 零合法板提示/补偿/回退形态(裁决:仅提示) | 边界情况表 + AC-19 | 落位 | +| A4×3 | ① 图例「迷雾 = 无雷盲区」条目(恒在/可回看) | Core Rule 14① + AC-14 | 落位 | +| A4×3 | ② 雾首遇提示(每 run 一次、三要点、非模态) | Core Rule 14② + AC-14 | 落位 | +| A4×3 | ③ 已开雾格 tint 残留 + 雾区全开合并域完成信号(OQ8 采纳) | Core Rule 9 / 15 + AC-4 | 落位 | +| A3 | AP/RAP 常显 + 刷新入口(玩家发起)+ RAP 清零反馈 + 双源拒绝 | Core Rule 16 + 边界情况表 + AC-8 / AC-16 | 落位 | +| A1 | EC5 零合法板诚实呈现(数据面经 B1×3③ 上抛) | 同 B1×3③ | 落位 | + +### 附:意图线索映射目录(B2×4① 成文载体——继承 battle.md 目录 + art-bible §4.6.4 键契约;资产定稿归 B5,本表为 D1 侧成文占位) + +| enemy_id(§4.6.4 冻结键集) | 视觉状态(下一拍预告) | 强度档位编码 | 环长编码 | +|---|---|---|---| +| grub(蛆虫) | 攻击 ×1 态 | 小体量墩形 = 轻击(§3.1 普通怪) | 环点 1(单拍环——无蓄力态 = 每拍都动手) | +| ambusher(伏击兽) | 攻击 / 蓄力 / 重击 ×3 态 | 垂直延伸体量 = 重击(heavy_multiplier 直觉) | 环点 = 拍数;蓄力态在场 = 环里空一拍("可以贪") | + +- 预告时序 = 下一拍预告、挖掘期全程可见、结算期不另设意图播片(B2 Rule 8⑤);精确数值只在结算明细兑现(B2 Rule 8④——图像不报数)。 +- 映射键 = enemy_id,跨 run 恒定、禁重掷(B2 Rule 8③);形状 + 色相双通道;蓄力姿态必须含可辨识蓄力记号(§4.6.4——读怪核心课)。 +- 资产需求下限三类(受击反馈 / 死亡表现 / 顺位标记)在程序化占位中先行(占位纹样挂键);终稿归 B5/样张批。 +- 键集注记:切片 DND 更名(goblin/bugbear/ogre_chief)与 GDD 键集 {grub, ambusher} 差异登记冲突清单②——键位映射表以 GDD 键集为锚,切片更名随 B2/C3 修订批对齐。 + +### 切片教训三条硬性要求落位表(entry 17——门禁评审核对项,缺项即 blocking) + +| # | 硬性要求 | 落位 | +|---|---|---| +| ① | 面向呈现的信号载荷成文 schema | Core Rule 17(13 项载荷表——A2 三事件/B1 行协议引用 + D1 自有遥测成文 + 零事件外发命令式刷新面成文) | +| ② | 场景级 headless 测试进 AC | AC「场景级 headless 测试」硬性块(真实实例化战斗屏场景 + 驱动完整对局 + 断言零脚本错误;载体 = 场景探针,ADR-0004 双载体、gdUnit4 deferred) | +| ③ | 信号时点状态可见性规则 | Core Rule 18(销毁前捕获 / 载荷自包含 / 禁回读核心生命周期态)+ AC 硬性块断言 | + +### 发现冲突清单(列示待裁决——不改已定稿 GDD 正文与提案) + +| # | 冲突 / 待对齐项 | D1 侧处置 | +|---|---|---| +| ① | game-concept 视觉锚原则 1"暗色地牢底色" vs art-bible 明亮底色反转 | 在案(entry 41)——概念侧注记待概念层修订批;D1 全部取色按 art-bible | +| ② | B2 GDD 敌种键 {grub, ambusher} vs 切片 DND 更名 {goblin, bugbear, ogre_chief} | 键位映射表以 GDD 键集为锚(§4.6.4 冻结);切片更名随 B2/C3 修订批对齐 | +| ③ | B4 GDD 默认根 clear_knife vs 切片供给控制器批 clear_blast + active_inject 池 | 徽章键按 reward_id 冻结、池归属不影响键(§4.6.2 注记);clear_blast 键并列预留、启用随 B4 修订批 | +| ④ | 极性提案 §3.5 跨文档回写清单全部待实施批(B3 四行→七行、B4 schema 扩列、A2 OQ1 关闭、D1 义务条目正式登记〔未开格类型图标/极性视觉语言/负向文案族〕) | 本 GDD 已按 §4.6 冻结键预留槽位;义务条目登记随提案实施批入 systems-index D1 台账 | +| ⑤ | art-bible §4.6.3"全部未开格呈现类型徽章"(提案 R3)vs B4 Rule 8"未开格身份 MVP 不呈现" | D1 裁决:MVP 格面徽章只在已开格呈现(B4 Rule 8 为现行锁定规则);未开格预读随极性提案实施批(其机制前提 = cell_type/pool_tag 扩列)随批修订 | +| ⑥ | battle.md §Events Fired"呈现订阅 A2 三事件"表述 vs ADR-0001 订阅纪律(呈现层只订 B1/runner) | 本 GDD 按 ADR-0001 收窄(Core Rule 2);battle.md 复检批对齐措辞(正文不改动) | +| ⑦ | A3 只读观测量(ap/rap/phase/last_round_end)缺轮内已用刷新次数——刷新按钮"本轮剩 N 次"读数无上游供给 | 接口漂移登记义务(OQ2):A3 随批补只读观测量;落地前按钮显示退化为"RAP−{rap_refresh_cost 表读值} · 每轮至多 2 次"静态文案 | +| ⑧ | 切片已实现的 B1/B2 呈现面 API 未在 owner GDD 登记契约:B1 面 `presentation_snapshot` / `is_battle_live` / `stock_count` / `query_reward_identity` / `submit_window_selection` + `settlement_snapshot` 信号、B2 面 `hero_unit` / `all_enemies`(registry `d1_presentation_surface` 已成文——owner=D1,切片事实转正) | OQ3:现行消费以 registry 条目为契约(D1 消费 = 步骤流判定行 + `presentation_snapshot()`,零阻塞);回 B1/B2 修订批补登记 API 面(接口漂移) | + +## Tuning Knobs + +> D1 knob = 呈现节奏与可读性常量(非玩法数值——D2 Rule 1"全部玩法数值入表"纪律不适用;单一来源登记防散落,载体 = presentation 主题资源/常量单点,实现期裁决载体形态;修订走本 GDD 流程非调参)。全部标注尺度。 + +| Knob | 默认 | 尺度 | 调过高 | 调过低 | +|---|---|---|---|---| +| toast_display_ms | 2500 | 每条提示 | 信息滞留、遮挡节奏 | 读不完即消(拒绝原因漏读) | +| move_slide_ms | ≤150 | 每次移动 | 移动拖沓(违反 P4 摩擦护栏) | "太瞬"无方向感(battle.md OQ5 playtest 观测调) | +| board_install_fade_ms | 200 | 每次装板 | 换图迟滞 | 闪切无地牢感 | +| clear_celebration_ms | 3000–5000 | 每次清图 | 拖节奏(§2.3"三五秒即收"上限约束) | 丰收感不足 | +| font_min_board / detail / tooltip | 18 / 14 / 13 px | @1280×800 基座 | 挤占格内空间 | 可读性下限击穿(可访问性基线) | +| hint_once_scope | per_run | 每提示项 | —— | ——(存档级"一生一次"归 D3/C4 后重议) | +| reduce_motion_scale | 0.5(或跳终态) | 每动画 | 减动效失效 | 等效关闭动效 | +| detail_band_height | 24 / 84 px | 摘要 / 实时流 | 挤压 B 区演出区 | 行数不足(逐行归因截断) | + +- **非 knob(结构冻结,修订走 GDD 流程)**:五区布局与分区尺寸(battle.md 拍板)、双舞台常驻、窗口原位激活形态、单意图帧纪律、模态层级纪律、键位映射契约(§4.6)、程序化占位策略、订阅纪律(ADR-0001)。 +- **性能预算(引用非 knob)**:同屏 draw calls < 200、进入战斗屏 ≤500ms 可交互、headless 全套 < 30s(technical-preferences / ADR-0004)。 +- **Knob 交互**:font_min_board × cell 尺寸(F1 字号下限随格尺寸联动——12×12 板 36px 格是 18px 下限的绑定域);toast_display_ms × 一次性提示去重(提示频率观感)。 +- **调参流程**:改先测量后复测(切片 mock 数据先行——code-conventions 数值纪律);playtest 观测项 = 移动手感(OQ5)/ 明细带常驻(OQ6)/ 窗口存在感(OQ7)。 + +## Visual/Audio 要求 + +D1 是本 GDD 体系中唯一的自有渲染面系统——视觉义务从"数据供给规格"反转为"执行 art-bible"。逐条锚定: + +| art-bible 锚点 | D1 执行 | +|---|---| +| §1.2 绘制优先级栈 | 数字 > 小人 > 旗 > 徽章 > 变异纹理 > 底色——BoardView 分层绘制/控件叠序按此栈实现,任何元素不遮挡数字(小人与旗不得覆盖文本;小人所在格边框高亮) | +| §1.4 格子样张(引用依据) | 样张 = 砖面形态定稿闸;D1 引用其验收标准(第一验收项 = 数字可读性硬指标)作为程序化占位与正式资产的共同判据;样张未制作不阻塞 D1(Core Rule 19);样张通过后冻结为制作标准,终稿资产替换走 asset-spec 批 | +| §3.3 UI 形状语法 | 半拟物——面板 = 石板圆角矩形(与地砖同圆角率)、按钮 = 凸圆角"可按的砖"、仪式性确认(胜利蜡封)= 圆形;数据框/准线/进度条一律直角细线无纹理("圆角 = 可触摸的物件,直角 = 须精确的信息") | +| §4.1/§4.4 UI 色板 | 数据素面墨族(#33261B 系)——棋盘与面板读同一种墨;战斗容器中性石色(防暖色渗入分走数字对比预算);语义色规则(红只说危险 / 金只说收益 / 蓝只说己方 / 紫赤只说 Boss / 绿零逻辑语义 / 禁入色相带 300°–345°)逐条适用;明度恒定硬规则(状态区分只经色相/饱和/运动/元素——去饱和 + 金橙偏移即其合规化实例) | +| §4.5 色弱安全清单(逐对执行) | 九对语义对的冗余通道(形/图标/位置/运动)在对应组件落实——四格态靠完整/碎裂形、奖励金 vs 危险红靠剪影+运动、UI 红/金强调对加图标+文案冗余、破坏性确认异位(位置通道)、图标键族一键一纹样色相不承载身份 | +| §4.6 图标键契约(键位挂接) | 效果图标 §4.6.1(七键——sapped 键冻结未入表前禁引用)、奖励徽章 §4.6.2(八键 + clear_blast 并列预留)、格面类型 §4.6.3(五键——MVP 只挂已开格)、敌种线索 §4.6.4(行为拍姿态键)——键位映射表(Core Rule 19)为唯一挂接点,键集扩容走 §4.6 修订、禁运行时换图 | +| §2 能量档承载 | 五区布局的动效与色彩转场按 §2.1–2.6 能量档实现(挖掘 2/10 唯一微光在动 → 结算 7/10 金橙光带 → 清图 8/10 金粒喷泉 → 胜利 6/10 克制 → 失败 1/10 暮蓝最静一拍 → Boss 5/10 紫赤)——"状态间区分度自检"三条(挖掘 vs 结算靠谁在动+饱和;胜负以色相即判;Boss 以紫赤登场标记)为转场验收锚 | +| 音频(D4,Alpha 占位) | MVP 无音频;挂点清单引用 B1/B4 音频挂钩点(明细行类型流 / 负反馈聚合单次 / 开格拾取按 category 分色 / 清图奖励音)+ toast 提示触发(本 GDD 新增挂点候选);临时 SFX 随垂直切片(AD 项先例) | + +> 📌 **Asset Spec**:Visual/Audio 义务已定义。art-bible 已定稿(§1–4),样张批后运行 `/asset-spec system:d1-hud-framework` 产出逐资产视觉描述/尺寸/生成提示词(样张先行——§1.4 为定稿闸)。 + +## UI 要求 + +| 项 | 要点 | +|---|---| +| 权威屏级 spec | `design/ux/battle.md`(APPROVED,B1 UX Flag 的 /ux-design 产物——覆盖子屏:挖掘主界面 / 结算明细 / 道具窗口 / 终局 / 明细回看):本 GDD 为其系统级承接(布局/组件/交互裁决全部继承,见核心规则二—七组);本节不重复 spec 内容,stories 引用 `design/ux/battle.md` 而非本 GDD 直引组件细节 | +| UX Flag 处置 | B1 UX Flag(结算明细屏 + 道具窗口 → Phase 4 前置 /ux-design)已由 battle.md 兑现闭合;D1 自身无新增屏(战斗屏 = 唯一 MVP 玩法屏);相邻域屏 NodeOverlay / 关卡结算 = 后续 spec `design/ux/node-reward.md` / `design/ux/level-clear.md`(battle.md OQ9——本 GDD 只锁出口锚点) | +| pattern 库 | `design/ux/interaction-patterns.md` 已建(核心 8 条:双源拒绝反馈 / 原位窗口激活 / 逐行兑现明细带 / 悬停预承诺光标 / 点阵资源显示 / 模态 overlay 层级纪律 / 破坏性确认异位 / 禁用态即理由全文)——本 GDD 组件全部对号入座;12 目录占位(实现期回写义务) | +| accessibility | `design/accessibility-requirements.md` 未建(gate-check Pre-Production 缺口在案)——本 GDD 内建基线承诺(Rule 22),正式分层(文本缩放/色盲模式/按键重映射)待该文档定稿 | +| 场景探针标配(code-conventions) | 每个可交互 UI 区域配套场景级探针——切片三探针(ui_probe / ui_click_probe / ui_tune_probe)扩展 + 战斗屏场景级探针(AC 硬性块);探针即呈现层契约的活文档 | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | ADR-0004 双载体定稿(gdUnit4 deferred——D2 OQ1 关闭形态):逻辑断言类 = `godot --headless --script` 冒烟扩展;场景级类 = 场景探针(`godot --headless --path . res://smoke/.tscn` 走正常项目引导——autoload 在位、真实 SceneTree;切片三探针为先例) | +| 期望值纪律 | 呈现断言引用表读值(AP=ap_per_round 表读值、RAP 门槛=rap_refresh_cost 表读值等)与 art-bible 冻结参数,不写死玩法数值;呈现常量(toast 时长等)为本 GDD Tuning Knobs 登记值可直断;每用例先 `reset_diagnostics()` | +| 覆盖声明(out-of-scope) | 玩法行为正确性(上游 GDD AC 域——D1 只断言呈现投影与上游契约一致)· 美术终稿质量(样张/asset-spec 批)· 动画密度(B5)· 音频(D4)· gamepad 焦点序(OQ5) | + +### AC 清单(编号不变,Given-When-Then) + +#### 布局与可读 + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 1280×800 与 16:9 全屏(canvas_items/expand) | 进入战斗屏(12×12 板) | 五区无遮挡/无重叠/无内容裁切(棋盘全部格子+数字完整可见)、分区尺寸 = battle.md 布局表值、同屏 draw calls < 200、进入后 ≤500ms 内首格可点 | 五区布局与基座 | +| AC-2 | 12×12 / 10×10 两档板 | 装板渲染 | cell = F1 定值档(36 / 45px @1280×800)且可行性校验通过、数字字号 ≥ max(18, cell/2)、像素字体整数倍缩放无模糊 | F1 格尺寸与字号 | +| AC-3 | 全部格底×数字组合 | 灰度截图 + 对比抽测 | 1–8 全档对比 ≥4.5:1(F2)、0.5× 缩放数字仍可辨、纹带不侵数字字形区;全部状态转场(去饱和+金橙偏移 / 暮蓝 / Boss 紫赤)明度恒定(无变亮/变暗表达) | 对比度与明度恒定(F2) | +| AC-4 | 含雾板 | 逐格渲染后灰度截图(+代码走查) | 四种格态一眼可辨;已引爆雷格呈"可通行可读"(已开底+巢穴纹+可作移动目标);已开雾格 = 地板无字 + 底色层 tint 残留且与已开 0 格灰度可判定区分;数字显示判定消费 variant_ids(呈现层无独立雾判定——代码走查) | A2×6②④ / A4×3③ | +| AC-5 | 小人在场 | 挖掘期任意时刻静止截图 | 8 向相邻未开格常驻呼吸微光(挖掘期唯一在动元素——其余静止截图断言);绘制优先级栈成立(徽章在旗之下纹理之上、不进数字区、无第二焦点) | 可挖域与绘制栈 | + +#### 输入与交互 + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-6 | 悬停已开格 | 悬停 / 点击移动 | `compute_move_path` BFS 路径以小人蓝细线点迹高亮(合法性指示);移动执行 ≤150ms 短滑;不可达格返回空且禁止光标(预览执行同源——A2 AC-14② 对侧);点击远格无自动 move+dig 复合意图(负空间) | A2×6⑤ 定稿 | +| AC-7 | 一帧内多意图(含 stop) | 提交 | 整批经 `settle_batch` 单批到达(stop 终结当帧收集——探针断言单批);光标三语义(移动/挖/旗)随悬停目标正确切换 + 目标格边框高亮 | A2×6⑥③ | +| AC-8 | Active 挖掘期 | ① 点击停手 ② 弃图刷新成功 ③ RAP<8 或 cap 超限提交 | ① 意图经 `settle_batch([stop])` 提交(代码走查 + 探针:零直调 A3 符号)+ 按钮下压 + AP→RAP 飞行数字(RAP 增量 = 剩余 AP 读数投影)② RAP 扣 rap_refresh_cost、AP 不重置、剩余次数扣减可见、旧板旗失效提示可见 ③ 资源面拒绝可见(脉冲 + toast——EC13/EC15) | A2×6① / A3 | +| AC-9 | 已开格 + 库存 | 渲染已开格与库存常读条 | 已开格家族徽章(§4.6.3 键)+ tier 档位拾取瞬间可读;库存常读条逐件 icon+名称+属性简述、枚举序 = stock 受理序、不含来源信息(列表与 tooltip 均无);Used 件不在常读条;同族刀(knife_t1/knife_adv)并排互辨 | B4×5① | + +#### 信息与归因 + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-10 | 双敌种遭遇 | 渲染敌卡 + 击杀驱动 | 意图徽标 = 下一拍预告且挖掘期全程可见、映射按 enemy_id 全局恒定(跨 run 重放同姿态)、含强度档位与环长编码(环点数 = 拍数)、蓄力态含蓄力记号;顺位徽标可读(固定顺位最前者可辨);血条 hp/max_hp 数字可读;多敌伏击层数各卡同值呈现;敌死亡 = 血条归零→灰化+碎裂退场(禁瞬消)、就地更新不丢灰化态(entry 36 回归) | B2×4①②③④ | +| AC-11 | 完整结算 | B1 步骤流驱动明细带 | 逐行明细实时可见且每行带来源标注(source_label_map 文案)、四类未生效行按聚合计数呈现且带形状冗余、零值行可见、效果行 value/pool_value 分示禁相加;标中旗行金框+弹道加重;H2 清图奖励行金框;完美排雷 = 金光带加倍+「完美排雷 ×2」横幅、value = 实际有效兑付当量、首份零兑付时高光降级不发;战斗后可经明细回看 overlay 复看完整流水 | B4×5③④ / B1×3①② | +| AC-12 | 状态在场/清空 | 渲染状态槽 | 槽显示 icon(§4.6.1 键)+层数+剩余结算次数点+tooltip(来源+语义);极性按键级表呈现(金满框/红缺框——非 magnitude 符号:负 magnitude 有利行测试例);空态 = 槽隐藏 + 数据框上移 + 图例载空态说明;battle_over 逐槽化光收回 + 微文案 | B3×4①③④ | +| AC-13 | 窗口触发 | 窗口交互 | E 区原位激活(非中央弹窗)+ 至多选 1 件 + usable_now=false 灰置带禁选原因 + 政策注记「未选用件随关卡结束清空」常驻 + 空库存不弹窗 + 选中提交 = 窗口条目位置(选中件库存态必变 Used——缺陷④回归探针)+ 窗口期不被常读刷新 stomped(entry 36 回归)+ D 区去饱和锁输入(明度不变) | B4×5② | +| AC-14 | 含雾板首遇 + 图例/提示系统在位 | 驱动各 hint_id 触发条件(含同 run 重复触发) | 图例面板「迷雾 = 无雷盲区」条目恒在可随时回看、载状态空态说明;雾首遇提示每 run 一次、非模态不劫持输入、必含三要点;chord 提示每 run 一次含「点数字格 = 移动小人」锚点;no_adjacent 首次提示;hint_id 去重生效(同 run 二次触发零 toast) | A2×6③ / A4×3①② | +| AC-15 | 程序化占位渲染全组件集 | 键位映射表挂接核查 + 未知键注入 | 全部 icon/徽章/线索经键位映射表挂接(数据键→纹样 ID);§4.6.1 七键/§4.6.2 八键/§4.6.3 五键/§4.6.4 姿态键逐一在位;未知键 = fail-fast 断言 + "?"占位 + 诊断(禁静默空图标);sapped 键未入 StatusEffectTable 前零引用 | 键契约执行 | + +#### 状态·资源·终局反馈 + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-16 | battle_over 与 level_clear 信号(关卡末战) | 清空反馈逐项到达 | battle_over:RAP 数字滚动归零+微文案、状态槽逐槽化光+微文案;level_clear:库存逐件化光飞出+「剩余道具 ×N 随关卡结束清空」(N = 清空前快照计数);两反馈不合并、时序不倒挂 | A3 / B3×4④ / B4×5⑤ | +| AC-17 | 判定行 verdict=win/lose(Boss encounter 变体) | 终局呈现 | 胜利 = 金蜡封落于扫雷图区中央+暖白金 overlay+判定行+明细回看入口;失败 = 暮蓝 overlay(明度不变)+旗化光点收回+判定行;判定行内容 = B1 终局判定行投影(数据锚);Boss 战 = 紫赤影子爬棋盘一次性演出 + 阶段指示附 Boss 名 | 终局与变体 | +| AC-18 | 完整对局驱动 | 采集 playtest JSONL | 六类 ux 行按 Core Rule 17#13 schema 写入 playtest JSONL(字段名与值域逐项对表);ux_round_exit.path_tag 值域 = 上游封闭枚举 | 遥测 | + +#### 场景级 headless 测试(切片教训硬性要求②③——缺项即 blocking) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-19 | 真实实例化战斗屏场景(探针场景加载、autoload 在位) | 驱动完整对局(两场遭遇 × 含雾板 × 窗口选用/跳过 × 刷新 × 胜负两终局) | 全程零脚本错误(stderr 归零)+ 五区断言 + 首格可点计时 ≤500ms(切片 ui_probe/click_probe 模式扩展) | 战斗屏场景级探针 | +| AC-20 | AC-19 对局至 despawn 与 battle_over 之后 | 静态扫描 views/ 文件集 + 终局后重放渲染比对 | 呈现层对核心生命周期状态的读数 = 零(静态扫描:views/ 文件集禁 `is_battle_live()` 以外的核心状态位读取、禁核心内部字段访问——只许成文呈现面);终局呈现数据全部来自流水行/快照(断言终局后重放渲染与实时渲染逐像素一致——快照自包含判据) | 信号时点状态可见性——缺陷②回归 | +| AC-21 | 逐一打开全部模态覆盖层(含叠开) | 逐层打开 / 关闭 | 每层打开时遮挡物隐藏/输入锁定断言成立、关闭逐层恢复;TunePanel 默认不可见(显式初始化) | 模态层级——缺陷⑤回归 | +| AC-22 | 悬停 tooltip 最长文案 / 明细行 40% 膨胀宽度构造 | 渲染后截图差分 | 全部动态区固定槽高 + clip_contents:布局零推挤(截图差分断言) | 布局稳定——缺陷①回归 | +| AC-23 | 全部中文文案界面 | 渲染 + 字形覆盖抽测 | 全部中文文案渲染非豆腐块(字形覆盖抽测——ADR-0003 硬性前置) | CJK 字体 | +| AC-24 | AC-19 对局全载荷流 + 六面回执 | 探针逐字段断言 + 代码走查 | 探针对 Core Rule 17 表 #1–#9 载荷逐字段断言(消费侧校验——缺字段/多字段 = 失败,切片"窗口载荷缺 round_index"崩溃回归);#12 命令式刷新面 = 代码走查(旗插拔/移动/AP/RAP/状态/库存六面各一个回执后重查断言) | payload schema 消费断言——硬性要求①兑现 | +| AC-25 | 呈现层文件集 | 静态扫描 / grep | 零 `randomize()`/`randi()`/`randf*`(S1/S2)、零玩法数值字面量(期望值经只读面推导——人工三要素口径 warning 级 + AC-1/2/8 表读值推导合成判定)、核心写调用仅组合根(S7 呈现侧)、切片 provisional 名(`request_refresh_active` / `can_request_refresh` / `refresh_max_per_round` / `during_round`)零残留(grep 断言——entry 35 命名统一核销);每视图单元头注含「实现 GDD:design/gdd/d1-hud-framework.md(系统 ID D1)」溯源行 | 静态扫描与命名 | +| AC-26 | 1280×800 全量界面(含 AC-19 对局) | 截图抽测 + 静态扫描 + 探针 | 字号下限成立(棋盘数字 ≥18px / 明细正文 ≥14px / tooltip ≥13px);无闪烁 >3 次/秒(动画时长/频率参数扫描);减动效开启 = 全部转场/高光/演出减半或跳终态、结算逐行兑现降速不跳过(探针断言);AC-19 对局全程纯鼠标驱动(零键盘依赖) | 可访问性基线(Rule 22) | + +### 覆盖矩阵(门禁评审用) + +| 类别 | 映射 | +|---|---| +| Core Rules | 1–4→AC-19/24/25 · 5→AC-1 · 6→AC-10 · 7→AC-6/7 · 8→AC-6 · 9→AC-4/5 · 10→AC-9 · 11→AC-12/15 · 12→AC-11 · 13→AC-13 · 14→AC-14 · 15→AC-4/11 · 16→AC-8/16/17 · 17→AC-24 · 18→AC-20 · 19→AC-15(键位)/样张引用 · 20→AC-25 · 21→AC-21 · 22→AC-23/26 | +| Formulas | F1→AC-2 · F2→AC-3 | +| Edge Cases | 全表→AC-4/6/8/10/11/13/14/16/17/21/22/23/15(拒绝与 voided→AC-8 及探针负例;零合法板→AC-19 注入腿;双雾区→AC-4/11 腿) | +| 义务台账 | 27 项→呈现义务台账落位表逐行(依赖节) | +| 切片教训 | 3 项→AC-19/20/24 + Core Rules 17/18 | + +## 开放问题 + +| # | 悬案 | owner | 时机 | +|---|---|---|---| +| 1 | _draw 迁移复议(ADR-0003 Decision 3 的 D1 裁决登记):MVP 维持 GridContainer + 逐格控件(裁决依据:呈现层程序化占位、样张未定稿、雾纹理/终稿表现力需求未冻结、三探针与现形态耦合〔改造 = 隐藏成本〕)——样张定稿后按表现力需求(biome 色板/雾纹理/动效复杂度)复议二选一;若迁移,`Viewport.push_input` 端到端探针从加分项变必需项(ADR-0004) | D1 复议批 | 样张定稿后 | +| 2 | A3 轮内刷新次数读数(接口漂移登记):刷新按钮"本轮剩 N 次"需要 A3 暴露轮内已用刷新计数(现只读观测量 ap/rap/phase/last_round_end 未含)——A3 修订批补只读观测量 + AC 前言扩列;落地前按钮退化为静态文案"RAP−{rap_refresh_cost 表读值} · 每轮至多 2 次" | A3 | 正式实现接线批 | +| 3 | B1/B2 呈现面 API 契约登记(冲突⑧扩条):切片已实现的 `presentation_snapshot` / `is_battle_live` / `stock_count` / `query_reward_identity` / `submit_window_selection`(B1 面)+ `settlement_snapshot` 信号 + `hero_unit` / `all_enemies`(B2 面)均未在 B1/B2 GDD 登记——registry `d1_presentation_surface` 为现行契约载体;回 B1/B2 修订批补登记 API 面,独立信号需求随批裁决(接口漂移) | B1 / B2 | B5 GDD 前 | +| 4 | battle.md 订阅表述对齐(冲突清单⑥):ADR-0001 订阅纪律(呈现层只订 B1/runner)与 battle.md §Events Fired"A2 三事件呈现订阅"的措辞差——本 GDD 按 ADR-0001 收窄;battle.md 复检批对齐 | battle.md 复检 | 下次 /ux-review | +| 5 | Gamepad 焦点序(battle.md OQ8 继承):Partial 支持非 MVP——焦点环顺序待 gamepad 批次 | gamepad 批次 | gamepad 批 | +| 6 | 相邻域屏 spec(battle.md OQ9 继承):`design/ux/node-reward.md` / `design/ux/level-clear.md`——本 GDD 只锁出口锚点(节点奖励覆盖层 / 关卡结算屏的完整形态归后续 spec) | 后续 spec | 垂直切片后 | +| 7 | accessibility-requirements.md(battle.md OQ2 继承):正式分层(文本缩放/色盲模式/按键重映射)待该文档定稿——gate-check Pre-Production 缺口在案 | 该文档定稿批 | 文档定稿 | +| 8 | 键集扩容流程锚定:新 effect_id / reward_id / enemy_id / cell_type 上线 → art-bible §4.6 修订 + SettlementTable effect_pool_map 随批登记(接口漂移双登记——本 GDD AC-15 fail-fast 强制暴露漏登记) | art-bible / D2 修订批 | 键集扩容时 | +| 9 | 极性提案实施批的 D1 义务条目(冲突清单④):未开格类型图标 / 极性视觉语言 / 负向文案族按提案 §3.5 正式登记入 systems-index D1 台账(本 GDD 已预留键槽位与裁决⑤) | 极性提案实施批 | 提案实施批 | +| 10 | 明细带挖掘期常驻摘要(battle.md OQ6 继承):常驻(现方案)vs 结算后收起——playtest 观测回看频率后裁决 | playtest 后裁决 | 垂直切片 playtest | +| 11 | D5 教学挂靠(battle.md OQ13 继承):挖到雷=敌方优势 / 旗挡链 / 首次蓄力提示 / 囤刀闭环——toast 体系为落点候选,D5 GDD 批定 | D5 | D5 GDD 批 | +| 12 | TunePanel 退役时点(battle.md OQ10 继承):C4 天赋树落位批退役(Rule 21 已标注);Playtest 诊断按钮保留 | C4 | C4 落位批 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.1 | 2026-09-14 | /design-review(lean)NEEDS REVISION 同日核销:F1 改两档定值 + 可行性校验式(原闭式在固定边距下无法同时得出 36/45——用户裁决方案 A);呈现面 API 接口漂移登记扩条(冲突⑧/OQ3 覆盖 B1×5 函数 + B2×2 投影 + settlement_snapshot 信号);状态头 C3 状态更正(Designed 非 Approved);C1-lite 切片契约注记(C1 GDD 定稿时回填);新增 AC-26 可访问性基线(字号下限/无闪烁/减动效/纯鼠标)并扩覆盖矩阵 Rule 22→AC-23/26;"尸位"用语替换为直白表述;RAP 文案模板改表读值占位 | +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;核心规则/枚举/边界情况/AC 转表格;呈现刷新链路转字符画 + 出口分发表;13 项载荷 schema、27 项呈现义务台账、意图线索目录、切片教训落位表、冲突清单、25 条 AC 全保留;语义与当日定稿版对齐,零规则变更 | +| v1 | 2026-09-13 | /design-system 一次会话定稿:27 项呈现义务落位 + 22 条核心规则 + 13 项载荷 schema 成文 + 25 条 AC + 三条切片硬性要求(solo 模式出稿,未经 /design-review) | diff --git a/design/gdd/legacy-2026-09-14-v1/d2-data-config.md b/design/gdd/legacy-2026-09-14-v1/d2-data-config.md new file mode 100644 index 00000000..dce35e5e --- /dev/null +++ b/design/gdd/legacy-2026-09-14-v1/d2-data-config.md @@ -0,0 +1,629 @@ +# D2 数据配置与数值表(Data Config & Balance Tables) + +> **状态**:Approved · lean 格式 +> **版本**:v1.0-lean · 2026-09-13 · 作者:user + agents +> **承载支柱**:P1、P2(基础设施层,间接支撑全部四支柱) +> **层级**:Layer 1 Foundation · Milestone MVP · 设计顺序第 1 位 +> **评审**:lean 门禁 systems-designer / qa-lead;full 评审 + 同日 re-review 含 game-designer / creative-director,CD-GDD-ALIGN 终裁 Approved——评审日志 `design/gdd/reviews/d2-data-config-review-log.md` +> **一句话**:D2 把全部影响手感与平衡的数字收进可配置、可校验的数值表,经 ConfigDB 单点发放、RngService 统一种子化——战斗可复现、平衡可热调、headless 可测的数据底座。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 数据底座:全部玩法数值的单一事实源(data/tables 的 .tres)+ 统一注册表 autoload ConfigDB + 随机数种子服务 autoload RngService——schema 先行、数值回填 | +| 拥有 | 全部表 schema 类(data/schemas)与数据文件(data/tables)· ConfigDB / RngService · SCHEMA_VERSION · 加载/校验/热重载状态机 · 校验双通道与诊断收集器 · debug override 链 · 难度行解析 resolve | +| 不拥有 | 平衡数值定值与玩法公式(owner 系统 GDD 与 registry)· 玩法随机预置(须走 Rule 5 增流通道 + P1 评审)· 存档(D3)· 任何 UI 表现面 · 精英/Boss 修饰语义(C1/C3) | +| 上游输入 | 无——Foundation 层不依赖任何系统;master_seed 由 C2 冒险开始时设定 | +| 下游输出 | 全部 19 系统经 ConfigDB 类型化读取 · board_gen / reward_roll / encounter 封闭流 · SCHEMA_VERSION + master_seed/计数器快照(D3)· config_reloaded 信号(仅 debug) | +| 硬约束 | 玩法数值零代码字面量 · 零 UI 依赖 · fail-fast 无软回退 · RNG 流集合封闭 · 表路径固定(debug 注入根除外)· release 无 override 源 | + +- 存在理由:没有 D2,参数散落各处硬编码——调一次难度要改五处代码,战斗无法用固定种子复现,headless 自动化测试无从谈起。 +- 策略:schema 先行、数值回填——本 GDD 只定数据结构、注册表与校验模式;具体数值由 owner 系统 GDD 按设计顺序回填(十张已定稿 + 两张占位)。 +- 定位:玩家永远不该注意到的地基——不制造情绪,但失职会直接摧毁 P1/P2 的信任底线(三种消极保障见体验锚点)。 + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P1 / P2(正面对象在别处) | "玩家永远不该注意到的地基" | D2 无专属玩家幻想——P1"我算对了"的掌控感来自 A1/A2 可推理设计,P2 的延迟兑现感来自 B1 可见结算;D2 只提供下列三种消极保障 | +| 手感稳定(消极保障) | "改数字不改代码" | 全部影响手感的数字(AP、旗、折算比例、奖励值)集中一处可调 + 热重载即改即验——"测试数据驱动平衡"的开发方式,任何"这轮玩起来不对劲"都能快速修正且不引入新毛刺 | +| 公平可信(消极保障) | "同决策必同结果" | RNG 统一种子化:相同决策序列必然相同战斗结果——可复现是开发侧信任底线(回放/审计/自动化测试),不等于玩家侧可推理;玩法随机零预置(走增流通道 + P1 评审,Rule 5) | +| 难度可信(消极保障,P4) | "越下越险由结构保证" | 数值表天生带关卡序号/节点深度两维——难度曲线可审计、可回溯,而非散落各处的魔法数字(离线审计 = 曲线导出工具,C3 期交付) | + +## 全局流程 + +### 数据加载与校验链路 + +``` +进程启动(Unloaded) + │ ConfigDB._ready() / load_tables(root, collect_only) + ▼ +Loading ──── 按表清单逐一 load() ── 文件缺失 → Failed + ▼ +Validating ── 单个资源加载成功 → schema validate(),错误入诊断收集器 + ▼ +┌ 校验双通道 ──────────────────────────────────────────┐ +│ 游戏通道 collect_only=false(默认): │ +│ 错误 → push_error 清单 → 致命错误路径 │ +│ editor / CI / headless / release 一律致命 │ +│ 测试通道 collect_only=true(仅 debug/测试构建): │ +│ 收集不升级 → 冒烟脚本逐夹具断言类别 → 一次性退出 │ +└──────────────────────────────────────────────────────┘ + ▼ +Ready ────── 全部通过 → 类型化属性可读 +``` + +### 热重载链路(仅 debug) + +``` +编辑器热重载 / 调试键 → Reloading + │ + ├─ 战斗中(set_reload_gate 已置位)→ 拒绝返回 false,停留 Ready + │ (战斗结束后手动再触发,不自动重放) + ▼ +重走 Loading → Validating + │ + ├─ 校验失败 → 保留旧表停留 Ready + debug 错误清单(唯一软着陆,不杀调试会话) + ▼ +校验通过 → Ready:新值经类型化属性可读 + config_reloaded 恰一次(仅 debug,D1 调试用) +※ RNG 状态线独立:Reloading / Failed 不触碰 RngService——计数器与 master_seed 不变(AC-4) +``` + +### RNG 取数链路 + +``` +进程启动 → 流待命(未播种取数 = 致命) + ▼ +冒险开始 → C2 set_master_seed()(仅全流计数器全零时受理) + ▼ +scope 事件:board_gen = 每次生成新扫雷图(A1 换图时) + reward_roll = 每次 board install 批(格奖励批 + 清图奖励批) + encounter = 每场战斗实例化(B1 begin_battle——unit_count 区间骰) + │ open_stream(stream):计数器 +1 → R2 派生 → 返回已播种实例(同一 scope 事件内取数共用) + ▼ +冒险结束 → 流保留供复盘;快照 = master_seed + 各流 scope 计数器 + (须在无在飞 open_stream 实例时切出——scope 事件边界之外) +``` + +### 出口分发 + +| 出口 | 触发 | 动作 | 约束 | +|---|---|---|---| +| 启动致命中止 | 启动加载任一致命错误 | 输出错误清单并中止构建;headless 独立进程非 0 退出 | 全构建一致,无软回退(Rule 6) | +| 热重载软着陆 | debug 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单 | 唯一例外(EC16) | +| 运行期致命 | 请求越域 / 未播种流取数 | 游戏内致命错误路径;headless quit 非 0 | 不分构建(EC7 / EC14) | +| 热重载成功 | debug 且门未置位且校验通过 | 新值可读 + config_reloaded 恰一次 | 战斗中被门拒绝(EC8) | +| 存档拒读 | SCHEMA_VERSION 高于当前 | 拒绝读取 | 比较归 D3,常量归 D2(EC9) | + +## 核心规则 + +> 每条规则两段:**人话**(作用于哪个系统、什么情况下、谁触发、触发后发生什么;效果与情况用枚举表)→ **举证**(计算方法/公式;纯流程规则明写"无计算")。 + +#### 规则 1:能调的数字只写在表文件里,代码里不写死 +**人话**:管的是整个项目的玩法代码。凡是设计师要调的数字(行动点、插旗成本、奖励值、敌人数值……)只存在于 data/tables/ 下的 .tres 数值表里,一处定义、处处引用;代码文件里禁止出现玩法数值字面量(引擎/工程常量除外)。谁在评审里发现写死的玩法数值,即视为评审阻塞项。 +**举证**:无计算。违规检测 = 启发式扫描 + 人工复核(AC-14,随各 owner 回填定稿与里程碑 gate-check 执行)。 + +#### 规则 2:所有系统读数值,都只找 ConfigDB 这一个入口 +**人话**:管的是全部 19 个系统的取数动作。任何系统要读数值只有一条路:经 autoload ConfigDB 的类型化属性读取(例 `ConfigDB.action_economy.ap_per_round`)。禁止绕过它自己 `load()` 表文件或长期缓存一份副本——副本绕过校验,热重载换新值时它也不知情。唯一例外:debug/测试构建可从命令行注入的根目录加载。 +**举证**:无计算。注入根的机制见本节末「规则 6 的实现参照——校验双通道」夹具注入行。 + +#### 规则 3:先把表结构定下来,数值随后由各系统回填 +**人话**:管的是全部数值表的交付方式。每张表都是"结构 + 数据"两件套:结构 = 一个自定义 Resource 类(data/schemas/),每个字段带类型、范围、默认值和注释;数据 = 数据文件(data/tables/)。本文档先交付全部 schema 与占位表(含键结构),具体数值由 owner 系统 GDD 回填——先有格子、后填数字。 +**举证**:无计算。十张已回填表 schema 目录见「枚举」节,逐表字段行见 Tuning Knobs 节。 + +#### 规则 4:难度数值按"第几关、第几层"的坐标查行 +**人话**:管的是跟着冒险进度成长的数值表(难度、生成参数、敌人配置)。这类表每行都带(关卡序号, 节点深度)两维坐标:系统按坐标取数,进度坐标没有精确命中时就取"不超过请求坐标的最近已有行";敌人等实体键在命中的行里按名字精确匹配、不做近似——那一行里没有这个名字就是悬空引用,直接致命中止。道理:进度坐标可以就近近似,"是哪种敌人"不能近似——身份近似会静默改变遭遇构成、损害 P2 归因。 + +| 表的形状 | 行键怎么定 | +|---|---| +| 进度成长表 | 行键 =(关卡序号 level_index, 节点深度 node_depth) | +| 实体复合键表 | 进度两键之后再复合实体主键列(如 EnemyTable 的 enemy_id;合成规则写入 schema 注释) | +| 单行全局常量表 | 无成长语义的表允许只放一行 | +| 元进度表 | 存档/天赋/代币归 MetaTable,键结构归 C4 定 | + +| 两维坐标之外的数值轴 | 归宿 | +|---|---| +| 战斗内轴(胜场/轮次等) | 由 owner 公式承载或作为表列;归属登记进 schema 注释(C3 定稿时声明) | +| 难度第三维"精英/Boss 修饰" | game-concept 定稿:难度 = f(关卡序号, 节点深度, 精英/Boss 修饰);DifficultyTable 预留 elite_ / boss_ 前缀扩展字段命名空间,语义由 C3 定稿——本 GDD 不定义任何修饰数值、不预写修饰枚举 | +| 两键定不了精英性的节点形态 | 如分支地图同深度并存精英与普通节点——允许改键,按接口漂移修订规则回写(与 MetaTable 同等豁免待遇) | + +**举证**:解析规则(floor 取行、warning 四元组去重、越域前置守卫)见「数值结论与推导 · R1 难度行解析」;悬空引用 = 加载期致命,见规则 6。 + +#### 规则 5:全项目随机数只从 RngService 一处按流取 +**人话**:管的是全项目玩法代码的随机数。只此一家:autoload RngService(D2 范围)持有封闭的三条具名流(下表),流集合封闭、只含具名消费者;要新增随机流(含 B1 若需结算随机)必须改代码走增流通道并通过 P1 支柱评审(随机须有界、可见或可推理)。玩法代码禁止 `randomize()` 或自建 RandomNumberGenerator。master seed 由 C2 在冒险开始时设定——同一冒险内相同决策序列必然相同战斗结果。 + +| 流 | 谁在用 | 取号时机(scope 事件,唯一绑定) | +|---|---|---| +| board_gen | A1 布雷 | 每次生成新扫雷图(A1 换图时) | +| reward_roll | B4 掉落 | 每次 board install 批(格奖励批 + 清图奖励批) | +| encounter | B1 战斗实例化 | 每场战斗实例化恰一次取号(begin_battle 实例化期,扫雷开始前;敌人数量 unit_count 区间骰——挖掘决策域之外,P1 合规) | + +| 取数入口 | 给谁用 | 行为 | +|---|---|---| +| `open_stream(stream) -> RandomNumberGenerator` | 玩法代码(有状态流门面) | 每调用一次 = 一次 scope 取号:该流计数器 +1 → 派生 → 返回已播种实例;一个 scope 事件内所有取数共用该实例 | +| `derive_at(stream, scope)` | 测试/预览(纯函数) | 不推计数器——预览取数不污染正式序列(杜绝预览类取数的平衡 Heisenbug) | + +**举证**:种子派生公式与黄金向量见「数值结论与推导 · R2 流种子派生」;流封闭清单见「枚举 · RNG 流」;依据 = B4 契约源〔AC-1〕· R2 黄金向量。 + +#### 规则 6:数据出错当场中止,绝不带病运行 +**人话**:管的是 D2 的加载与运行期错误处置。数据出错一律致命中止,MVP 有意不做软回退——错误数值上线比崩溃更伤(P1/P2 的信任底线)。错误分两类,处置见下表。唯一例外:debug 热重载校验失败时保留旧表停留 Ready、给出错误清单——不杀运行中的调试会话。 + +| 错误类别 | 什么情况 | 处置 | +|---|---|---| +| 加载期校验错误 | 启动加载/热重载时查出:字段越界、类型错、NaN、表为空或某 level 无行、缺 depth-0 锚行、重复键、悬空引用、权重和 ≤0 | 经校验双通道处理;启动路径在 editor、CI、headless、release 一律致命 | +| 运行期致命 | 运行中越界请求:关卡/深度坐标越域(解析入口前置守卫拦截)、未设种子就取随机数 | 不分构建一律致命:游戏内走致命错误路径,headless 冒烟进程 quit 非 0 | + +**举证**:未播种流取数见 EC7;请求越域见 R1 前置守卫。致命错误类别总表见「枚举 · 致命错误类别」;两类构建如何分流,见下方「规则 6 的实现参照——校验双通道」。 + +#### 规则 7:数据层零界面依赖,无头环境照样跑 +**人话**:管的是 D2 自己的代码与测试。它们一律不 import 任何 Control/场景类——纯数据层不碰表现。这是"Core 层 `--headless` 跑完整战斗"工程债在数据侧的落实:没有界面依赖,CI 与无窗口环境才能跑完整战斗。 +**举证**:无计算。验收 = 静态扫描 + headless 实跑两项合成判定(AC-10)。 + +#### 规则 8:表和存档带版本号,版本太高就拒读 +**人话**:管的是表文件与存档的版本兼容。版本常量 `SCHEMA_VERSION` 归 D2;表文件与存档(D3)携带版本号。存档带来的版本号高于当前支持时,直接拒绝读取——旧程序不硬猜新格式的数据。 +**举证**:无计算。拒读行为见 EC9(比较逻辑归 D3,常量归 D2)。 + +### 规则 6 的实现参照——校验双通道(ConfigDB.load_tables(root, collect_only)) + +| 通道 / 机制 | 要点 | +|---|---| +| 游戏通道(collect_only=false,默认) | 校验错误 → push_error 错误清单 → 走致命错误路径(游戏内中止构建;headless 下独立进程非 0 退出——`--script` 协议,设计顺序 #1 时无主场景可依赖);release 构建的致命表现(引擎默认错误路径的实际行为验证)显式 deferred→首次 release 导出 checklist(与 AC-18 同待遇) | +| 测试通道(collect_only=true,仅 debug/测试构建) | 收集错误不升级,供冒烟脚本逐夹具断言错误类别后一次性退出 | +| 夹具注入 | debug/test 构建允许经命令行 `--data-root=<路径>` 让 ConfigDB 从注入根加载(夹具放 tests/fixtures/);禁止把夹具拷进 data/tables/(污染 AC-1 的干净检出前提) | +| 诊断收集器 | resolve / 校验 / override 的 warning 与错误共用同一类目体系,统一经 ConfigDB 内部诊断收集器投递,同时镜像 push_warning 供开发期控制台(Godot 无进程内拦截 push_warning 的 API——冒烟断言必须走收集器);测试通道暴露只读诊断清单与 `reset_diagnostics()`;跨调用去重状态(R1 四元组注册表)随 `load_tables()` 或 `reset_diagnostics()` 清空(用例间无顺序依赖);游戏通道 warning 仅镜像控制台,不承诺收集 | + +## 枚举 + +### 十张已回填表 schema 目录(Rule 3 交付清单——逐表字段行见 Tuning Knobs 节) + +| 表资源 | 键结构 | Rule 4 豁免 | 跨字段 / 结构校验(加载期致命) | 悬空引用政策 | Owner | +|---|---|---|---|---|---| +| ActionEconomyTable | 单行全局 | 单行全局定义表(先例) | 任一动作成本 ≤ ap_per_round(schema validate() 跨字段类别承载,条件性夹具登记) | — | A3 ✓ | +| VariationTable | 按 variant_id(含 unlock_level 列) | 定义表非进度成长表——进度语义承载于 unlock_level 列(schema 注释) | info_mask=hide_numbers ⇒ cell_constraint=safe_nonzero(夹具 variation_mask_constraint) | counter_item_ids MVP 不校验——A5 首个道具 id 回填时补校验 + 条件性夹具 | A4 ✓ | +| StatusEffectTable | 按 effect_id | 定义表非进度成长表(schema 注释承载) | magnitude:percent ∈ [-100,100] ∧ ≠0 / flat ≥ 1 跨字段校验 | effect_id 校验已激活(B4 RewardTable 首引 defense_up——夹具 b4_dangling_effect) | B3 ✓ | +| BoardGenTable | (level_index, node_depth)——首个进度键表(depth-0 锚行内建;第三轴落点 = Boss:Dmax 行约定 + elite 命名空间预留 + A1 OQ6 重审;改键退路 = Rule 4 接口漂移通道) | 无(进度成长表) | fog_domain_ok(F2)+ min_safe_feasible(F2b)+ cost_feasible(F2c:attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生) | — | A1 ✓ | +| EnemyTable | (level_index, node_depth, enemy_id)——每行集恰一敌种 | 无(进度成长表,A1 同型) | 每行集恰一敌种(行集内 enemy_id 唯一,违规加载期致命) | — | B2 ✓ | +| HeroTable | 单行全局 | 单行全局定义表(ActionEconomyTable 先例) | atk = 0 加载拒绝("0 基线 × 百分比增益恒零"死锁自 schema 层杜绝) | — | B2 ✓ | +| RewardTable | 按 reward_id(多行;pool_tag 分池不参与键) | 定义表非进度成长表(schema 注释承载) | 池空(pool_tag × 雾/非雾两态各 ≥1 活行)· clear_default 恒正当量 · buff⇒effect_id 必填 · safe_cell 活 buff 行集非空 · floor_supply_missing / no_damage_row(跨 RewardConfigTable) | effect_id 精确引用 StatusEffectTable(已激活) | B4 ✓ | +| RewardConfigTable | 单行全局 | 单行全局常量表(HeroTable / ActionEconomyTable 先例) | k_dmg=0 = F5 断言关闭(现行默认——刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸托底) | — | B4 ✓ | +| SettlementTable | 单行全局 | 单行全局常量/结构表(HeroTable / ActionEconomyTable 先例) | segment_order 恒等拍板段序校验违例致命 · effect_pool_map 键 ⊆ StatusEffectTable 活行集 · p_perfect 载体域 {100,200} 域外加载致命——三行致命 manifest:segorder_violation / poolmap_dangling / pperfect_oob | effect_pool_map 悬空加载期致命 | B1 ✓ | +| AuxItemTable | 按 item_id | 定义表非进度成长表(schema 注释承载) | target ∈ 各空间 effect_kind 支持域交集(A5 承载——真视窄域护栏校验,夹具 a5_target_violation) | counter_item_ids(VariationTable → item_id)首引已定稿〔probe_dust〕、悬空校验激活随 D2×② 对侧批 | A5 ✓(原 HelperItemTable 占位更名转正——A5 定稿登记) | + +### 未回填占位表 + +| 表资源 | 占位内容 | 键结构 | Owner | +|---|---|---|---| +| DifficultyTable | 成长参数 + 精英/Boss 修饰预留命名空间(乘数域 [0.1,10] registry 预登记;**越域 clamp+warning = registry 登记的显式例外——组合事故非配置事故,非 fail-fast 场景**) | (level_index, node_depth) | C3(第 12 位) | +| MetaTable | 代币/天赋字段名 | 单行全局(元进度轴豁免,C4 可改键) | C4(第 16 位) | + +### MVP 内容行(事实登记归 registry) + +| 表 | MVP 行集 | 值状态 | +|---|---|---| +| ActionEconomyTable | 单行:ap_per_round=8 · dig/插旗/拔旗=1 · rap_refresh_cost=8 · move/chain=0(冻结) | 定稿值,登记归 registry `action_economy` | +| VariationTable | 恰含 fog 行(unlock_level=2 · hide_numbers · safe_nonzero) | 岩浆/冰冻无 MVP 行(垂直切片建行) | +| StatusEffectTable | 四行 attack_up / defense_up / ambush / weaken(全 stack) | 值 provisional,锚验收归 B4/B2/B1;weaken = −65(值域 [60,85]——B1 F0 裁决,条款 2 锚闭合);defense_up = −30(减伤墙批 C3 投值,叠满两层 −60 帽——B3/registry 同批) | +| BoardGenTable | 参考行 L1 / L2 | provisional(C3 调参所有权) | +| EnemyTable | 三敌种行 goblin / bugbear / ogre_chief(C3 修订批转正——registry `enemy.mvp_content` 为准) | 值 provisional——溯源义务见登记与对账义务表 | +| HeroTable | 单行(max_hp / atk=6 原型锚) | max_hp 出处待查;atk provisional 归 C3 | +| RewardTable | 默认根行集八行:defense_up_basic / attack_up_basic / spark_aoe / heal_minor / knife_t1 / knife_adv / heal_pack / clear_blast | 值 provisional 归 C3;格子池四行即时类 + 注入池三行可选类(active_inject——供给控制器承载,原"即时 75% / 可选 25%"权重占比由注入机制替代)+ 清图槽单行 | +| RewardConfigTable | 单行 t_fog_min=2 · k_dmg=0 · n_draft=32 · p_min=8 · active_p0=0.4 · active_decay=0.5 | provisional 归 C3 | +| SettlementTable | 单行默认(D_ref=12 · p_perfect=200 · 拍板段序 · clamp −100/+100 · floor · **+4 字段:damage_a_per_cell=1 / damage_a_cap=8 / damage_b_per_flag=2 / armor_effectiveness 克制矩阵——护甲克制批,C3 修订批接口漂移**) | provisional 归 C3 | + +### RNG 流(封闭枚举,3 流) + +| 流 | 消费者 | scope 取号事件 | +|---|---|---| +| board_gen | A1 布雷 | 每次生成新扫雷图(A1 换图时) | +| reward_roll | B4 掉落 | 每次 board install 批(格奖励批 + 清图奖励批)——B4 AC-1 为契约源 | +| encounter | B1 战斗实例化 | 每场战斗实例化恰一 scope(begin_battle 实例化期,扫雷开始前——unit_count 区间均匀骰,挖掘决策域之外 P1 合规;C3 遭遇随机化批增流,registry 已登记) | + +新增流 = 增流通道:改代码 + P1 支柱评审(随机须有界、可见或可推理)。 + +### missing_type(R1 warning 分类枚举) + +| 值 | 判定 | +|---|---| +| base_ceil | level 分量向上解析(请求低于表最小键)——恒记 base_ceil(depth 分量缺失与否只写入 warning 内容,不改类型) | +| level_floor / depth_floor | 单维向下解析 | +| both | level_floor 与 depth_floor 同时发生 | + +- depth 分量只有精确命中或向下 floor 两种情形(EC15 锚行保证每个 level 含 depth-0,永不 ceil);AC-5 各用例的 missing_type 均由本规则推导。 + +### 致命错误类别(Rule 6 两类) + +| 类别 | 通道 | 覆盖 | +|---|---|---| +| 加载期校验错误 | 校验双通道(启动路径全构建致命) | 字段越界 / 类型错 / NaN / 表为空 / 某 level 无行 / 缺 depth-0 锚行 / 重复键 / 悬空引用 / 权重和 ≤0 / 段序违例 / 池映射悬空 / p_perfect 越域 | +| 运行期致命 | 不分构建 | 请求越域(R1 前置守卫)· 未播种流取数(EC7) | + +### override key 空间(v1) + +| 项 | 值 | +|---|---| +| key 形态 | schema 字段点路径(与 Rule 2 属性路径同构,例 `action_economy.ap_per_round`) | +| 支持域 | 仅单行全局表字段;进度键表行寻址语法(如 `difficulty.(2,0).hp`)登记为后续扩展——当前 override 对进度表无 key 空间 | + +## 数值结论与推导 + +### R1 难度行解析(difficulty_row_resolution) + +**结论**:resolve 恒返回已有行——进度键 floor 解析、实体键精确匹配;不存在 default 行兜底、不存在 clamp 外推;非精确命中记 warning(四元组去重);请求越域在解析前置守卫致命。Output Range:行资源必有值(前置条件由校验保证);极端输入(超表大关卡号)命中最大已有行。 + +#### 推导 + +``` +row = resolve(table, level_index, node_depth) (ConfigDB 公开解析方法,table 经类型化属性取得) +前置守卫:level_index < 1 ∨ node_depth < 0 → Rule 6 致命(请求越域,不进解析) +L* = max{ lv ∈ LevelKeys(table) | lv ≤ level_index };空集(请求低于表最小键,向上解析)→ L* = min LevelKeys(table) +D* = max{ dp ∈ DepthKeys(L*) | dp ≤ node_depth };空集 → D* = min DepthKeys(L*) +``` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| level_index | int | ≥1 | 当前关卡序号(1 起);越域 → 致命 | +| node_depth | int | ≥0 | 节点深度(0=关卡入口);越域 → 致命 | +| L* / D* | int | 表内键 | 命中行键 | + +- 无兜底 / 无外推的前置:表非空、每个 level 至少 1 行、每个 level 含 depth-0 锚行是 Rule 6 校验前置;超表成长外推是 C3 owner 公式的职责,D2 只解析到最近已有行。 +- warning 定位为开发期异常信号(防刷屏去重是刻意的),不是难度审计工具——离线审计由曲线导出工具承担(headless 脚本导出 resolve(level 1..N × depth 0..M) 矩阵供 diff,登记为 C3 期交付物)。 +- Example:表含行 (1,0)(1,4)(2,0)(2,6)——`resolve(3,3)` → L*=2、D*=0 → 返回 (2,0) 行,warning 一条(missing_type=both);`resolve(1,4)` → 精确命中零 warning;`resolve(99,99)` → 命中 (2,6),missing_type=both(与前者去重键不同,各自一条)。 +- 去重键 = (table_id, missing_type, L*, D*) 四元组,内容含请求 (level_index, node_depth)、解析结果 (L*, D*)、缺失类型;去重注册表随每次 `load_tables()` 或 `reset_diagnostics()` 清空。 + +### R2 流种子派生(stream_seed_derivation) + +**结论**:纯派生函数——同一冒险内(master_seed 固定)同流各 scope 种子互不碰撞;同输入恒同输出;跨 Godot 版本恒定(因此不用运行时 `hash()`——其跨版本稳定性无承诺);输出 ∈ [0, 2^63)。跨 master_seed 的全局单射不做声明(数学上由鸽笼原理排除)。Output Range:[0, 2^63)。 + +#### 公式 + +``` +derive_at(stream, scope) = ( SALT_s XOR wrap64(master_seed + scope × GOLDEN_RATIO) ) AND 0x7FFFFFFFFFFFFFFF +wrap64 = GDScript int(64 位二补数)自然回绕 = MOD 2^64 +``` + +#### 实现级规约(必须逐条遵守,否则黄金向量失配) + +| 规约 | 要点 | +|---|---| +| wrap64 | GDScript int(64 位二补数)的自然回绕 = MOD 2^64;禁止 float 中间量(53 位尾数必丢精度)、禁止对中间量使用 `%`(带符号取余语义易误用)、禁止"主动修复溢出"(中间积本就越界——乘数 GOLDEN_RATIO = 0x9E3779B97F4A7C15 > 2^63,scope=1 即回绕,这是设计依赖的语义,不是缺陷) | +| 常量书写 | 三个常量(盐×2 + 乘数)均 ≥2^63,GDScript 十六进制字面量按二补数解析为负——统一用分段表达式书写(例 `(0x9E3779B9 << 32) \| 0x7F4A7C15`,显式括号、不依赖优先级记忆),禁止直接书写 ≥2^63 的完整字面量;注意 GDScript 的 `^` 是按位 XOR | +| 输出掩码 | 经 AND 掩码保证非负(∈ [0, 2^63))后再赋 `RandomNumberGenerator.seed` | + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| SALT_s | int64 | 编译期常量 | board_gen=0xA0761D6478BD642F · reward_roll=0xE7037ED1A0B428DB · encounter=0x8EBC6AF09C88C6E3(互异奇常数,均 ≠ 乘数) | +| master_seed | int64 | [0, 2^63) | 冒险主种子;`set_master_seed()` 对负输入 abs() 归一化并 warning;恰为 INT64_MIN → 致命(abs 在 int64 对 INT64_MIN 恒等回绕,实测与 0 在全流种子逐一相等——别名机理是输出掩码抹掉 bit 63,掩码本身是规约的一部分);仅许全流计数器全零时调用(冒险中途重播种会静默清零计数器,击穿同冒险无碰撞前提与 D3 快照语义 → debug 断言) | +| scope | int64 | ≥0 | 派生入参;有状态门面的取号值(每流独立计数器:set_master_seed() 时归零,每次 `open_stream` +1,永不重置、不随关卡/图序重置——否则不同关卡的同序号图会得到相同种子);负 scope → debug 断言中止(`derive_at` 是预览工具入口,静默负值会与 scope+2^63 经输出掩码别名) | + +**黄金向量(AC-3 的常量依据,经独立复算验证):** + +| 流 | master_seed | scope | stream_seed | +|---|---|---|---| +| board_gen | 42 | 0 | 2339089373825164293 | +| board_gen | 42 | 1 | 4485977604321908752 | +| board_gen | 42 | 2 | 2024629813010603131 | +| board_gen | 42 | 3 | 8849696722413293638 | +| reward_roll | 42 | 0 | 7422916049646135537 | +| encounter | 42 | 0 | 1061841193803499209 | + +- Example:同流同 scope 重复调用 `derive_at` 恒等(纯函数);同帧对同一流连续 `open_stream` 两次 → 计数器各 +1,得两个互异且顺序确定的种子。 + +### R3 调试覆盖链(debug_override,仅 debug 构建) + +**结论**:`effective = override[key] if _has_override(key) else table_value`——仅 debug 构建加载 override 源;v1 仅支持单行全局表字段;类型/范围不符 → warning + 忽略;release 结构性不存在;永不回写 .tres。 + +#### 边界五条 + +| # | 边界 | +|---|---| +| ① | 覆盖判定与存储内聚于 ConfigDB(Rule 2 单点),gameplay 不可见 override 来源 | +| ② | key = schema 字段点路径(与 Rule 2 属性路径同构,例 `action_economy.ap_per_round`);未知 key → debug warning + 忽略该条;v1 仅支持单行全局表字段——进度键表的行寻址语法(如 `difficulty.(2,0).hp`)登记为后续扩展,当前 override 对进度表无 key 空间 | +| ③ | override 值类型与范围必须与 schema 字段一致,不一致 → debug warning + 忽略该条(复用 schema 校验逻辑,防止 debug 会话注入越界值污染平衡基线) | +| ④ | release 构建不加载、不注册任何 override 源(恒等是结构保证而非"碰巧不生效");debug 谓词实现为可注入形式(`OS.is_debug_build()` 之外引入 feature 标签闸,例 `sim_release`)——AC-8 的模拟以此为基础 | +| ⑤ | override 仅为运行时状态,永不回写 .tres | + +### R4 显式排除 + +**结论**:玩法平衡公式(伤害、掉落权重、成长曲线)不在 D2——归 owner 系统 GDD(B1/B4/C3 等);本 GDD 不含任何平衡数值公式,只负责取数与解析。玩法随机同理:任何系统若需玩法随机(如结算浮动),自带方案走 Rule 5 增流通道并通过 P1 评审,D2 不预置。 + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Unloaded | 进程启动前 | 磁盘 .tres 未加载 | +| Loading | `ConfigDB._ready()` 或 `load_tables()` | 按表清单逐一 `load()`;文件缺失即入 Failed | +| Validating | 单个资源加载成功 | 调 schema 的 `validate()`,错误入收集器 | +| Ready | 全部通过 | 类型化属性可读 | +| Failed(启动路径) | 启动加载任一致命错误 | 全构建致命中止并输出错误清单(Rule 6) | +| Failed(reload 路径) | 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单(不杀运行中的调试会话) | +| Reloading(仅 debug) | 编辑器热重载 / 调试键 | 战斗中拒绝(返回 false,战斗结束后手动再触发);否则重走 Loading → Ready | + +不变式:RNG 与 ConfigDB 状态线相互独立——Reloading / Failed(任一路径)不触碰 RngService,计数器与 master_seed 不变(派生只依赖盐/master_seed/scope,与表数据无关;若按状态机对称性在 reload 时重置 RNG 将击穿 AC-4)。 + +- RNG 生命周期:进程启动 → 流待命(未播种时取数即致命)→ 冒险开始设定 master_seed → 每次具名 scope 事件派生种子 → 冒险结束保留供复盘。 +- 快照 = master_seed + 各流 scope 计数器(仅记录计数器、缺 master_seed 不足以恢复序列——进程重启会从种子序列起点重放;见 D3 接口)。 +- 快照切出须发生在无在飞 `open_stream` 实例时(scope 事件边界之外):门面实例作用域限定于单次 scope 事件、消费者不得跨事件持有——在飞实例的内部取数进度不入快照,违反则续玩/复盘与实况分叉(AC-15 失效),该前提由 D3 消费侧遵守。 + +## 边界情况 + +| # | 条件 | 行为 | 依据 | +|---|---|---|---| +| EC1 | 表文件缺失或 load() 失败 | 致命中止,错误清单含路径;无软回退 | Rule 6 | +| EC2 | 字段越界 / NaN / 类型不符 | `validate()` 收集后致命 | Rule 6 | +| EC3 | 表为空或某 level 行集为空 | 校验阶段致命(R1 前置条件),不会进入运行期 | Rule 6 | +| EC4 | 请求 (level_index, node_depth) 无精确行 | 按 R1 floor 解析 + warning(按四元组去重)——预期行为,不是错误 | R1 | +| EC5 | level_index 超过表最大 level | 命中最大 level 行(floor 规则的自然结果,无独立 clamp 分支) | R1 | +| EC6 | master_seed 为负 | abs() 归一化 + warning,保证 ∈ [0, 2^63);恰为 INT64_MIN → 致命(abs 恒等回绕,与 0 静默别名——机理是输出掩码抹掉 bit 63) | R2 | +| EC7 | 未设 master_seed 就取随机数 | `push_error` 后显式中止(GDScript 的 push_error 本身不终止进程——游戏内走致命错误路径,冒烟脚本 `quit(1)`),取到的值不可用——C2 未接线的显式暴露,禁止 seed=0 软回退 | Rule 6 | +| EC8 | 战斗进行中请求热重载 | 拒绝并返回 false;战斗结束后手动再触发(不自动重放) | 状态表 | +| EC9 | 存档携带的 SCHEMA_VERSION 高于当前 | 拒绝读取(比较逻辑归 D3,常量归 D2) | Rule 8 | +| EC10 | 奖励权重表权重和 ≤ 0 | validate 致命(B4 回填后必须 > 0) | Rule 6 | +| EC11 | debug override 类型或范围与 schema 字段不符 | warning + 忽略该条(仅 debug;release 无 override 源) | R3 | +| EC12 | 同一流名被重复注册或请求未知名流 | debug 断言中止(流名封闭) | Rule 5 | +| EC13 | 两个系统同帧请求同一流 | 允许——`open_stream` 各自取号(计数器各 +1)按调用顺序串行生效;单线程假设成立(Godot 主线程玩法逻辑),不引入锁 | Rule 5 | +| EC14 | 请求越域(level_index < 1 或 node_depth < 0) | Rule 6 致命(R1 前置守卫) | R1 | +| EC15 | 某 level 行集缺 depth-0 锚行 | validate 致命(防"入口天灾":入口请求静默 floor 到深行造成难度突刺);写死开、无运行期开关,C3 如需重议走本 GDD 显式修订流程(非 knob) | Rule 6 | +| EC16 | 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单 | 状态表 reload 路径 | + +## 依赖 + +**上游(hard)**:无——Foundation 层,D2 不依赖任何系统(依赖层 1:无)。 + +| 系统 | 方向 / 性质 | 接口契约 | +|---|---|---| +| 全部其他 19 系统 | 它们 → D2(hard,Rule 1) | ConfigDB 类型化读取;禁止其他取数路径(A2 经 A3 表间接承载参数) | +| A1 | 消费 | BoardGenTable 回填(首个进度键表)+ VariationTable schema(biome/tag 字段)+ `board_gen` 流(换图时取号) | +| A3 | 消费 | ActionEconomyTable schema 回填 | +| A4 | 消费 | VariationTable 回填 | +| B2 | 消费 | EnemyTable 回填(十字段 + 三敌种行 + 每行集恰一敌种校验)+ HeroTable 新增(接口漂移回写) | +| B3 | 消费 | StatusEffectTable 回填 | +| B4 | 消费 | RewardTable 回填(九字段 + 跨字段校验族 + 默认根行集,use_timing 承重 MVP 单值)+ RewardConfigTable 新增(单行全局)+ `reward_roll` 流(scope = 每次 board install 批——格奖励批 + 清图奖励批,B4 Rule 2 接口漂移) | +| B1 | 消费 | SettlementTable 回填(可回放步骤流的数据前提)+ debug 热重载门标志 `set_reload_gate()`(战斗开始置位 / 结束复位,仅 debug 构建)+ `encounter` 流(战斗实例化期取号恰一 scope——unit_count 区间骰,C3 遭遇随机化批增流) | +| C2 / C3 | 消费 | `RngService.set_master_seed()` + DifficultyTable 行解析 + 曲线导出工具交付物 | +| D3 | 消费 | `SCHEMA_VERSION` + 存档内 master_seed + 各流 scope 计数器快照(续玩/复盘;含只读计数器接口,快照须在无在飞实例时切出) | +| A5 / C4 | 消费(垂直切片 / Alpha) | AuxItemTable 回填(A5 定稿登记——原 HelperItemTable 占位更名转正;counter_item_ids 表值回填与悬空校验激活随 D2×② 对侧批)/ MetaTable 回填 | +| D1 / D4 / D5 / D6 | soft | 如需参数表再增 schema(现无占位);display_name 本地化归 D6 | +| D2 外发 | → | 不订阅任何玩法事件;仅 debug 构建发 `config_reloaded`(D1 调试用) | + +- 数据读取类事实依赖不逐条列边(完整清单以本表为准);A2 无直接依赖(只外发事件,参数经 A3 表承载),与 systems-index 的 A2 回边规则一致。 +- 接口漂移修订规则:owner GDD 定稿时可修改占位 schema 类(schema 与表同仓),但必须回写本 GDD 核心规则表清单与 `design/registry/entities.yaml`,并在本文件版本历史登记修订;**回执登记粒度 = 受影响字段/枚举/流——逐表 diff schema 目录(枚举节目录 + 逐表字段行 + MVP 内容行 + 依赖表),禁按"对侧 GDD"粒度整批登记**;占位参考值定稿时必须删除本 GDD Tuning Knobs 清单中的对应条目(值的事实登记归 owner GDD 与 registry)。 +- 双向一致性(与 systems-index 依赖分层表核对一致):A1←A4,D2 · B2←D2 · B4←D2 · B1←A3,B2,B3,B4,D2 · C2←C3,D2 · C4←D3,C2,D2 · C3←D2,B2 · Foundation 含 D2;C2 / B1 / B4 / C4 ← D2 四条边已补入索引依赖分层表;B4←D2 边已按 B4 定稿收窄——A2 事实经 B1 转化零直接接口、B3 为验收锚关系非运行时边。 +- 已知待定:C3 是否消费 B1 输出——见索引门禁增补记录,C3 GDD 定稿,不影响 D2。 + +### 登记与对账义务(条件性夹具 / deferred / 溯源——审计资产) + +| 编号 | 内容 | 对侧 | 状态 | +|---|---|---|---| +| 1 | ActionEconomyTable 跨字段校验(任一动作成本 ≤ ap_per_round)条件性夹具登记 | A3 | 已登记(schema 类别承载,随 A3 冒烟) | +| 2 | VariationTable 跨字段校验 hide_numbers⇒safe_nonzero 夹具 variation_mask_constraint | A4 | 已激活 | +| 3 | StatusEffectTable effect_id 悬空引用校验(条件性夹具——首个含引用字段的 owner 回填时补) | B3 / B4 | 已激活(B4 defense_up 首引,夹具 b4_dangling_effect) | +| 4 | counter_item_ids 悬空引用(D2 首个引用型字段)——MVP 不校验,首个道具 id 回填时补校验 + 条件性夹具 | A5 | 条件性(未激活) | +| 5 | EnemyTable MVP 行值(goblin 18HP / 4ATK 等)溯源补登(原型 REPORT,出处待查) | B2 / C3 | 待补 | +| 6 | HeroTable max_hp MVP 值溯源补登(出处待查) | B2 / C3 | 待补 | +| 7 | 胜场成长机制去留(+25% HP / +1 ATK——反雪球问题) | C3 | 待裁决 | +| 8 | 曲线导出工具(headless 导出 resolve 矩阵供 diff)——R1 审计的离线载体 | C3 | C3 期交付物(排期倒挂注意见开放问题 #5) | +| 9 | Rule 5 对全项目玩法代码的随机扫描 | Technical Setup | 项目级 CI 项 | +| 10 | release 构建致命表现验证(引擎默认错误路径实际行为) | 首次 release 导出 | deferred(AC-18 checklist) | +| 11 | 战斗级可回放集成测试(同 master_seed 同动作序列 → 步骤流逐行相同) | B1 | deferred(AC-15) | +| 12 | 双向一致性四条边补入索引依赖分层表(C2 / B1 / B4 / C4 ← D2) | systems-index | 已完成 | + +## 呈现与 UI 面 + +| 项 | 要点 | +|---|---| +| 玩家 UI | 无——MVP 无面向玩家的 UI;debug override 与夹具注入均经 Godot 编辑器 / 命令行参数设置(`--data-root=`,仅 debug),不做自定义面板 | +| 校验失败呈现 | editor / CI 输出控制台(错误清单格式见 AC-1);release 构建走引擎默认错误路径(实际表现验证 deferred→首次导出 checklist) | +| 开发侧可视化 | Godot 编辑器 Resource inspector(自带)+ 校验错误控制台输出 | +| 音频 | 无需求(纯数据层,无直接表现面) | + +## Tuning Knobs + +### D2 自身 knobs(元参数) + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| floor 解析策略 | floor + warning(无外推) | —— | 引入外推/静默会掩盖 C3 难度断档 | +| rng_stream_names | 封闭 3 流(board_gen / reward_roll / encounter) | 增流需改代码 + P1 评审(有意设卡) | 流复用破坏可复现隔离 | +| debug_override_enabled | false(仅 debug 构建) | 污染测试基线 | —— | +| 表路径清单 | data/tables/ 固定(debug 注入根除外) | —— | 散装路径破坏单点访问 | +| depth-0 锚行校验 | 写死开(非 knob,Rule 6) | —— | ——(重议须走本 GDD 显式修订,EC15) | + +- Knob 交互:floor 策略与 C3 的难度语义共享,改动需 C3 GDD 知情;校验严格度不设 knob(fatal 写死——放行带病数值会使 CI 冒烟失去意义)。 +- 未决占位与溯源义务(provisional——owner 定稿前不视为平衡事实;定稿后删除对应条目,值的事实登记归 owner GDD 与 registry):EnemyTable 行值溯源(义务 #5)· HeroTable max_hp 溯源(义务 #6)· 胜场成长机制去留(义务 #7)——见登记与对账义务表;已定稿占位参考值(标中旗 / 清图奖励 / 状态四行 / 奖励池等)已随各 owner 定稿清退出本清单。 + +### 九张表 schema 逐表字段行(D2 核心资产——字段全集以各 owner GDD Schema 节与 registry 为准) + +#### ActionEconomyTable(单行全局 · A3 回填 ✓) + +| 字段 | knob | 说明 | +|---|---|---| +| ap_per_round | ✓ | 每轮行动点 | +| dig_ap_cost | ✓ | 挖掘成本 | +| flag_place_ap_cost | ✓ | 插旗成本 | +| flag_pull_ap_cost | ✓ | 拔旗成本 | +| rap_refresh_cost | ✓ | RAP 刷新门槛 | +| move_ap_cost | ✗ 冻结 | 移动成本 | +| chain_ap_cost | ✗ 冻结 | 连锁展开成本 | +| 跨字段校验 | — | 任一动作成本 ≤ ap_per_round(违规加载期致命,条件性夹具登记) | + +#### VariationTable(按 variant_id · A4 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| variant_id | String 主键唯一 | — | 行键 | +| display_name | String | — | 本地化归 D6 | +| unlock_level | int [1,999] | ✓ | 进度语义承载列(Rule 4 豁免载体) | +| scope | region / global | — | global 预留 C3 | +| info_mask | none / hide_numbers | — | 雾行 = hide_numbers | +| cell_constraint | any / safe_nonzero | — | 雾行 = safe_nonzero | +| biome_tag | StringList | ✓ | ∅=通配 | +| counter_item_ids | StringList | ✗ | A5 预留;悬空引用政策见目录表 | +| 承重规则 | — | — | 生成期布置约束:雾区仅非 0 安全格(区内无雷无 0 格,A1 生成期强制)+ 零特判原则 | + +#### StatusEffectTable(按 effect_id · B3 回填 ✓) + +| 字段 | 类型 / 域 | 说明 | +|---|---|---| +| effect_id | String 主键唯一 | 行键;引用校验已激活 | +| display_name | String | 本地化归 D6 | +| target_side | hero / enemy | 作用侧 | +| magnitude | percent ∈ [-100,100] ∧ ≠0 / flat ≥ 1 | 跨字段校验 | +| unit | percent / flat | 量纲 | +| duration_settlements | [1,99] | 持续结算次数 | +| stack_policy | stack / refresh / independent | 叠层策略 | +| max_stack | [1,16] | 仅 stack 消费 | + +#### BoardGenTable((level_index, node_depth) 首个进度键表 · A1 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| board_width / board_height | int [8,64] | ✓ | 板尺寸 | +| mine_density | float (0,0.5] | ✓ | 密度浮点(尺寸解耦);布雷数下限 round(d×(W×H−9)) ≥ 1 | +| inferability_target | float [0.85,1] | ✓ | P1 门槛承重参数 + 绝对锚 | +| solver_rules | single / single_subset | ✗ 冻结 single_subset | 推理定义域非调参 | +| min_safe_count | int [1,999] | ✓ | 退化板对冲 | +| max_gen_attempts | int [1,32] | ✓ | 生成-验证循环上限 | +| fog_region_count | int [0,32] | ✓ | 雾区数 | +| fog_target_ratio | float [0,1) | ✓ | ≤ min(8d, 0.25) 绝对上限 | +| board_biome | StringSet 非空 | — | 域校验致命(词表 owner 未定稿前只校验非空) | +| elite_ / boss_ 前缀 | 命名空间预留 | ✗ | 第三轴登记约定——语义归 C1/C3,本 GDD 不定义 | +| 跨字段谓词 | — | — | fog_domain_ok(F2)+ min_safe_feasible(F2b)+ cost_feasible(F2c:attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生)——加载期致命 | + +#### EnemyTable((level_index, node_depth, enemy_id) · B2 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| enemy_id | String(行集内主键) | — | 行集内唯一(每行集恰一敌种) | +| display_name | String | — | 本地化归 D6 | +| unit_count | int [1,3] | ✓ | **遭遇区间下界**(C3 修订批——区间 [unit_count, unit_count_max] 经 B1 encounter 骰定本战实际数量;新敌种首遇节点固定不加骰) | +| unit_count_max | int [unit_count,3],可省略 | ✓ | 遭遇区间上界(缺省 = 定值,旧行零迁移——C3 修订批新增字段) | +| armor_type | enum {none, a, b},缺省 none | ✓ | 护甲类型(克制矩阵 = SettlementTable.armor_effectiveness;MVP 映射 goblin=none / bugbear=a / ogre_chief=b——护甲克制批) | +| hp | int [1,999] | ✓ | 难度修饰前基线 | +| atk | int [0,999] | ✓ | attack 行为伤害基值;0 = 纯伏击伤敌种合法 | +| heavy_multiplier | int [1,16] | ✓ | 无 heavy 拍敌种不消费(非校验项先例) | +| pattern | StringList,环长 [1,8] | — | MVP 校验域 {attack, charge, heavy}——guard/boost/hinder/summon 预留值校验拒绝 | +| advantage_per_round_cap | int [0,16] | ✓ | 默认 4 定稿(反灾难护栏);0 = 免疫伏击合法极端 | + +#### HeroTable(单行全局 · B2 回填 ✓ · 接口漂移新增) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| max_hp | int [1,999] | ✓ | MVP 值 provisional(出处待查) | +| atk | int [1,999] | — | 0 = 加载拒绝(基础攻击恒存在,"0 基线 × 百分比增益恒零"死锁自 schema 层杜绝);MVP = 6 原型锚 provisional 归 C3;消费面 = 主角战斗段先手攻击 | + +#### RewardTable(按 reward_id · B4 回填 ✓) + +| 字段 | 类型 / 域 | knob | 说明 | +|---|---|---|---| +| reward_id | String 主键 | — | 行键(多行;pool_tag 分池不参与键) | +| category | buff / damage_item / heal_pack / instant_aoe / instant_heal | — | 两类交付轴:即时类 {buff, instant_aoe, instant_heal} 当场兑付不进库存 / 可选类 {damage_item, heal_pack} 入关卡域库存;预留值校验拒绝 | +| effect_id | String 可空 | — | buff 类必填(StatusEffectTable 精确引用);非 buff 恒空 | +| display_name | String 非空 | — | D1 呈现数据源,本地化归 D6(B2/B3 同库惯例) | +| power | int [0,999] | ✓ | item 类加载校验 ≥1;buff 类不消费非校验 | +| weight | int [0,999] | ✓ | 0 = 不入池 | +| pool_tag | safe_cell / active_inject / clear_default | — | clear_\ 命名空间预留拒绝;active_inject = 供给控制器注入池(可选类三行——install 期注入,非逐格 roll) | +| tier | int [1,8] | ✓ | A4 雾档约束载体 | +| use_timing | pre_settlement(MVP 单值) | — | 承重——道具使用窗口 MVP 激活,扩枚举走修订 | + +#### RewardConfigTable(单行全局 · B4 回填 ✓ · 接口漂移新增) + +| 字段 | 类型 / 域 | 默认 | 说明 | +|---|---|---|---| +| t_fog_min | int [1,8] | 2 | F2 雾档过滤门限 | +| k_dmg | int [0,999] | 0 | 0 = F5 断言关闭(现行默认——刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸托底) | +| active_p0 | float [0,1] | 0.4 | 供给控制器注入概率初值(首件之后每张新板)——E[战斗获取] ≈ 1 标定目标,归 C3 | +| active_decay | float (0,1] | 0.5 | 注入概率指数递减 0.4→0.2→0.1→…(加难杠杆候选 0.6 随 C3 OQ7 扫描) | +| n_draft | int [1,999] | 32 | F5 草稿重 roll 上限 | +| p_min | int [1,999] | 8 | 默认根最低刀 power——K≥1 供给闸刀力下界(floor_supply_missing) | + +#### SettlementTable(单行全局 · B1 回填 ✓ · 共十二字段) + +| 字段 | 类型 / 域 | 默认 | knob | 说明 | +|---|---|---|---|---| +| equivalence_ref_damage(D_ref) | int [1,999] | 12 | ✓ | 当量参考敞口;变更触发 B1 全对照表 + 条款③⑤对账重跑(禁独立调参) | +| p_perfect | percent {100,200} | 200 | ✓ | 完美排雷清图奖励第二份兑付触发——非整数倍数乘数无运行时载体,域外加载致命 | +| segment_order | StringList | 拍板段序 | ✗ 结构冻结 | 恒等拍板段序校验,违例致命(防实现乱序静默漂移) | +| source_label_map | Dictionary source_class→文案键 | 封闭枚举全覆盖 | ✗ 结构冻结 | P2 归因呈现映射;键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack};新增键 = 接口漂移 | +| effect_pool_map | Dictionary effect_id→{hero_offense, hero_defense, enemy_output, flat_attach} | 四键定稿映射 | ✗ 结构冻结 | 效果语义映射;键 ⊆ StatusEffectTable 活行集,悬空加载期致命;新 effect_id 上线须随批登记 | +| pool_clamp_lo | int pp | −100 | ✗ 结构冻结 | percent 池地板域下界(乘数域随之 [0,2]) | +| pool_clamp_hi | int pp | +100 | ✗ 结构冻结 | percent 池地板域上界 | +| round_direction | enum {floor} | floor | ✗ 结构冻结 | 单一取整规则 | +| damage_a_per_cell | int ≥ 0 | 1 | ✓ | 伤害A 开格充能/格(护甲克制批——充能与结算归 B1 战斗段〔F6〕,C3 投值) | +| damage_a_cap | int ≥ 0 | 8 | ✓ | 伤害A 单轮充能封顶(0 = 不封顶——防连锁轮失控) | +| damage_b_per_flag | int ≥ 0 | 2 | ✓ | 伤害B 标雷充能/首次标中雷(每雷实例一次去重) | +| armor_effectiveness | Dictionary {base, dmg_a, dmg_b} × {none, a, b} | 特化 ×150 / 无甲 ×100 / 对位 ×50 | 键集 ✗ 结构冻结 / 数值 ✓ | 伤害类型 × 护甲克制矩阵——主角攻击三分量(基础/伤害A/伤害B)各自查表后 floor 相加;敌种甲型映射归 EnemyTable.armor_type | + +## 验收标准 + +### 测试口径 + +| 口径 | 要点 | +|---|---| +| 测试载体 | `--headless --script` 冒烟框架(脚本 extends SceneTree 以获得 quit(code);该模式 autoload 须由脚本显式实例化——AC-1 前提声明;设计顺序 #1 时无主场景可依赖) | +| 覆盖 | 8/8 Core Rules;Rule 6 加载期校验错误类别全部有夹具(悬空引用为条件性夹具,已随 B4 首引激活);运行期致命(请求越域 EC14、未播种 EC7)由 AC-5b / AC-17 行为级覆盖;R1 / R2 全部边界 | +| 致命类断言协议 | 致命用例(AC-2b、AC-5b 游戏通道半、AC-6 后半、AC-11、AC-17)各为独立进程调用,冒烟 runner 清单逐条登记"调用 → 期望退出码";单进程内只做测试通道收集断言 | +| warning 断言 | 一律经诊断收集器(见校验双通道),每用例先 `reset_diagnostics()` | +| 人工与延迟 | AC-14 人工 · AC-15 deferred→B1 · AC-18 deferred→首次 release 导出 | + +### AC 清单(Given-When-Then,编号不变) + +| AC | Given | When | Then | 覆盖 | +|---|---|---|---|---| +| AC-1 | 干净检出 | `godot --headless --script tests/smoke_data.gd` | 核心规则 #3 表清单的全部表(以清单为准,不写死数量)加载且 data/tables/ 无清单外 .tres;脚本成功 `quit(0)`,任一失败 `quit(1)` 并输出错误清单(表路径 + 字段 + 错误类别) | Rule 3/6 | +| AC-2 | tests/fixtures/ 夹具集(越界字段 / 文件缺失 / 空表 / 某 level 无行 / 缺 depth-0 锚行 / 重复键 / 权重和 ≤0 各一;悬空引用为条件性夹具——已随 B4 首引激活) | 冒烟脚本经 `--data-root` 注入逐夹具运行 `load_tables(root, collect_only=true)`(测试通道,单进程逐个断言) | 每个夹具的错误清单含对应类别(EC1 / EC2 / EC3 / EC15 / EC10 及重复键类别) | Rule 6 / EC | +| AC-2b | 任一致命夹具经注入根加载 | 冒烟 runner 以独立 headless 进程执行游戏通道 `load_tables(root, collect_only=false)`(`--script` 协议,脚本显式实例化 autoload;不依赖主场景) | 进程非 0 退出且错误清单完整——验证"游戏内真的会死"而非仅"校验器能发现" | Rule 6 游戏通道 | +| AC-3 | R2 黄金向量表(6 常量) | 纯函数 `derive_at` 复算(board_gen master=42 scope 0–3、reward_roll master=42 scope=0、encounter master=42 scope=0);同流同 scope 重复调用 | 逐个等于向量表常量;重复调用恒等(纯函数无副作用) | R2 | +| AC-4 | master_seed=42 | `open_stream("board_gen")` 连续取号 ≥100 次(跨模拟"关卡切换");同期调用任意 `reload()` | 公开只读计数器单调不重置(各关同序号图种子互异);计数器不变(状态线独立) | R2 / Rule 5 / 状态表 | +| AC-5 | 真实 DifficultyTable schema 构造的表含行 (1,0)(1,4)(2,0)(2,6)(每用例先 `reset_diagnostics()`) | 依次 `resolve(3,3)` → `resolve(3,4)` → `resolve(1,4)` → `resolve(99,99)`;单行表 {(2,0)} 上 `resolve(1,5)` | (3,3):返回 (2,0) 行且诊断收集器 warning 恰一条(missing_type=both,含请求/结果/类型);(3,4):warning 不新增(四元组去重键相同);(1,4):精确命中且零 warning;(99,99):命中 (2,6) 且新增恰一条(去重键含 D*,与首条不同);单行表:命中 (2,0) 且 missing_type=base_ceil(由 R1 分类规则推导:level 分量向上解析恒记 base_ceil) | R1 | +| AC-5b | 真实 DifficultyTable schema 构造的合法表 | `resolve(0, 3)`(level_index < 1)与 `resolve(2, -1)`(node_depth < 0);同用例经游戏通道独立进程复跑 | 测试通道诊断清单各含"请求越域"致命类别一条且不进入解析(EC14 / R1 前置守卫);独立进程非 0 退出 | R1 / EC14 | +| AC-6 | master_seed=-7;master_seed=INT64_MIN;`derive_at` 传入负 scope | `set_master_seed()`;`set_master_seed()`;`derive_at` | 归一化为 7 + 诊断收集器 warning 一条,后续种子 ∈ [0, 2^63)(EC6);INT64_MIN 致命(非 warning;独立进程断言退出码非 0);负 scope debug 断言中止(负值经输出掩码与 scope+2^63 别名) | R2 / EC6 | +| AC-7 | debug 构建 override{"action_economy.ap_per_round"=12}(域内值——A3 schema 域 [1,32];越域 override 会被 R3③ 忽略,出域示例会使本 AC 假失败) | 经 ConfigDB 读取;override key 为未知路径、类型或范围与 schema 不符;`reload()` 后 | effective=12;诊断收集器 warning + 忽略该条;.tres 文件内容不变(R3 边界①③⑤) | R3 | +| AC-8 | 非 debug 谓词模拟(feature 标签) | `_has_override(key)` | 恒 false——本条仅证明门存在性;"release 不加载 override 源"的结构保证验证 deferred→首次 release 导出 checklist | R3④ | +| AC-9 | D2 文件集(data/schemas、ConfigDB、RngService、tests) | 静态扫描(模式覆盖 `load(`、`preload(`、`ResourceLoader.load`、`"res://data/" +` 动态拼接;`res://tests/fixtures/` 前缀豁免——不在 `res://data/` 扫描域) | 无 `randomize()` 调用、无 ConfigDB 之外的 data 表加载(Rule 2/5);Rule 5 对全项目玩法代码的扫描登记为项目级 CI 项(义务 #9) | Rule 2/5 | +| AC-10 | D2 文件集 | 静态扫描 + AC-1 headless 实跑 | 无 `extends Control/Node2D`、无 `.tscn` preload,且 AC-1 headless 实跑通过——两项合成判定 Rule 7(扫描是启发式,硬证据是 headless 实跑) | Rule 7 | +| AC-11 | RngService(debug 构建) | 请求未知名流或重复注册流名 | debug 断言中止(EC12;独立进程退出码非 0) | Rule 5 / EC12 | +| AC-12 | 同帧对同一流 | 连续两次 `open_stream` | 两个互异且顺序确定的种子(EC13) | Rule 5 / EC13 | +| AC-13 | 冒烟脚本运行 | 反射断言 | `SCHEMA_VERSION` 常量存在且 ConfigDB 可读;全部 schema 携带版本字段;进度类表(BoardGen/Enemy/Difficulty)暴露 (level_index, node_depth[, enemy_id]) 键字段;DifficultyTable schema 暴露 `elite_` / `boss_` 前缀预留字段("高版本拒读"路径 deferred→D3) | Rule 8 + Rule 4 键契约 + 第三维命名空间 | +| AC-14(人工) | 每次回填定稿 | 验收人复核 | Rule 1 数值字面量禁令成立:启发式扫描(warning 级,误报难免)+ 人工复核三要素——验收人 = owner GDD 回填定稿评审 + 里程碑 gate-check;时机 = 每次回填定稿;证据 = 评审记录 / gate-check 登记 | Rule 1 | +| AC-15(deferred→B1) | 同 master_seed 同动作序列 | B1 集成测试重放 | 结算步骤流逐行相同(D2 阶段无战斗载体) | Rule 5 可回放 | +| AC-16 | debug 构建且 B1 侧经 `set_reload_gate(true)` 置位(模拟战斗中);门未置位;reload 后新表校验失败 | `reload()` | 置位:返回 false、ConfigDB 值不变、不发 `config_reloaded`;未置位:返回 true 且新值经类型化属性可读且 `config_reloaded` 发出恰一次(仅 debug 构建);校验失败:旧表保留、状态停留 Ready(EC16) | 状态表 / EC16 / Rule 6 | +| AC-17 | 未设 master_seed | `open_stream` 或 `derive_at` | push_error + 冒烟通道断言中止路径(quit 非 0)(EC7) | Rule 6 / EC7 | +| AC-18(deferred→首次 release 导出) | release 模板构建 | 致命校验错误发生;查找 override 源 | 表现符合"引擎默认错误路径"预期;override 结构性不存在(R3 边界④、校验双通道 release 侧) | Rule 6 / R3④ | + +## 开放问题 + +| # | 问题 | owner | +|---|---|---| +| 1 | headless 测试载体终稿:MVP 用 `--headless --script` 冒烟(本 GDD 决策);gdUnit4 是否在 Technical Setup 引入并迁移冒烟用例 | Technical Setup 阶段(systems-index 门禁增补记录项) | +| 2 | 编辑器内表文件热重载自动化(当前仅 debug 键触发;低优先) | 未指派 | +| 3 | 数值表显示名的本地化 | D6 | +| 4 | C4 MetaTable 并入 ConfigDB 还是独立 autoload(倾向并入) | C4 GDD | +| 5 | 难度曲线导出工具(R1 审计的离线载体):headless 导出 resolve 矩阵脚本,C3 期交付——排期倒挂:BoardGenTable 可推理率曲线自 A1(第 5 位)起回填,A1 定稿前须有临时抽查载体(入 systems-index 门禁记录) | C3 GDD 登记排期 | + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| 跨评审 D2 漂移批 | 2026-09-14 | /review-all-gdds 🔴-2 五处补漂移修订批(X-8 遗漏项补登,registry 同批):**RNG 流封闭清单 2→3 流**(encounter——消费者 B1,scope = 每场战斗实例化恰一 scope;Core Rule 5 / 取数链路 / 盐值 0x8EBC6AF09C88C6E3 / 黄金向量 +1 常量〔AC-3 五→六〕/ set_master_seed 全流零计数前提)· **pool_tag 枚举增 active_inject** · **RewardConfigTable +2 字段**(active_p0=0.4 / active_decay=0.5)+ k_dmg 默认 1→0 且"C3 禁区默认值"条款解除 · **默认根行集 clear_knife→clear_blast**(注入池占比注记替代 75/25)· **HelperItemTable 更名 AuxItemTable**(A5 定稿转正,schema 目录九→十张)· X-8 补登:SettlementTable 逐表字段行补 4 字段(共十二字段)、EnemyTable MVP 行三敌种登记 · 接口漂移修订规则增"回执按受影响字段/枚举粒度逐表 diff schema 目录"流程纪律;核心规则表增依据列(lint R8 引用列迁移) | +| C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-8 回执,registry 同批):**EnemyTable schema +2 字段**(unit_count_max——遭遇区间上界 / armor_type——护甲类型枚举 {none,a,b},克制矩阵挂 SettlementTable)· **SettlementTable +4 字段**(damage_a_per_cell=1 / damage_a_cap=8 / damage_b_per_flag=2 / armor_effectiveness 克制矩阵——护甲克制批转正,机制与结算归 B1、C3 投值)· DifficultyTable 表清单行补乘数域与 clamp 例外注记(registry 登记的组合事故例外,非 fail-fast 场景)· 敌种键更名 goblin/bugbear/ogre_chief(DND 批对齐) | +| v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;核心规则 / 枚举 / 边界 / AC 转表格;加载校验与热重载链路转字符画;九张表 schema 逐表字段行入 Tuning Knobs;义务与夹具登记收敛为登记与对账义务表。正文残留旧称按 registry 与各 owner 成稿写现行值:SettlementTable 八字段口径(pool_clamp 拆 lo/hi)、p_perfect 载体域 {100,200}、source_label_map 键集含 hero_attack、RewardTable category 五值与默认根八行集——语义零变更 | +| 两级奖励批 + 基础攻击裁决 | 2026-09-11 | RewardTable category 三值扩为五值(即时 / 可选两类交付轴)+ 默认根行集重登记八行(原五行为集退役);RewardConfigTable p_min 默认 4→8;HeroTable atk 改域 [1,999] 且 0 = 加载拒绝(基础攻击恒存在),消费面 = 主角战斗段先手攻击 | +| B1 回填 + 评审批收编 | 2026-09-10 | SettlementTable 定稿回填(registry 原记七字段);同日评审批统一八字段口径——pool_clamp_lo_hi 拆 lo/hi 两字段、p_perfect 域 [100,999] 改载体域 {100,200}、source_label_map 键集补 enemy_action / flag_miss(hero_attack 随 B1 成稿) | +| B4 full 评审 delta | 2026-09-09 | RewardTable 增 display_name(九字段)+ 默认根行集登记 + RewardConfigTable 新增(单行全局四字段,接口漂移——B4 标量 knob 的 Rule 1 载体)+ Core Rule 5 reward_roll 语义正文补同步(每次 board install 批) | +| B4 回填定稿 | 2026-09-09 | RewardTable 八字段 + 跨字段校验族 + use_timing 占位转承重 + 首个 effect_id 引用回填激活 StatusEffectTable 悬空校验 + reward_roll scope 语义改写 + Tuning Knobs 奖励池占位条目清退 | +| B2 回填定稿 | 2026-09-09 | EnemyTable 八字段 + 双敌种行 + 每行集恰一敌种校验;HeroTable 新增(接口漂移回写) | +| A1 full 评审 delta | 2026-09-09 | BoardGenTable inferability_target 收窄 [0.85,1](P1 绝对锚)+ fog_target_ratio 绝对上限 min(8d, 0.25) + 布雷数下限 round(d×(W×H−9)) ≥ 1 + F2c 成本谓词(运行期禁墙钟逃生)+ elite_/boss_ 命名空间预留(第三轴登记约定,Boss=Dmax 行 + A1 OQ6 重审门) | +| A1 回填定稿 | 2026-09-09 | BoardGenTable 九字段 + F2/F2b 跨字段谓词 + solver_rules 冻结 + MVP 参考行(provisional,C3 调参所有权)——首个进度键表 | +| B3 回填定稿 | 2026-09-08 | StatusEffectTable schema 八字段 + 四行 MVP 内容(键 = effect_id,Rule 4 豁免注明;effect_id 悬空校验待首引激活) | +| A4 re-review delta | 2026-09-08 | VariationTable 增跨字段校验 hide_numbers⇒safe_nonzero(加载期致命,夹具 variation_mask_constraint)+ counter_item_ids knob 误标修正(非 knob) | +| A4 回填定稿 | 2026-09-08 | VariationTable 八字段 + fog 行 + Rule 4 豁免注明(registry 新条目 variation_table / variant_pool_gate) | +| A3 三轮评审 delta | 2026-09-07 | ActionEconomyTable 增跨字段 validate 规则(任一动作成本 ≤ ap_per_round,schema 类承载、违规致命、条件性夹具登记) | +| A3 RAP 结构修订 | 2026-09-07 | flags_per_round / flag_unit_cost / stop_multiplier / clear_multiplier 四字段删除(旗无上限、停手 / 清图折算乘数退役)、rap_refresh_cost 新增;AC-7 示例值域内修正(99 出域会使本 AC 假失败) | +| A3 回填定稿 | 2026-09-07 | ActionEconomyTable 字段清单交付 + 成本三字段入表 + 冻结字段(ap_per_round=8 等定稿值登记 registry) | +| 收口 | 2026-09-07 | 两轮评审 + delta 复核 5/5 PASS 后 Approved;CD-GDD-ALIGN 终裁 NEEDS REVISION → 修订落实 → 独立 delta 核对通过 | +| 原型占位值清退 | 2026-09-07 起随各 owner 定稿 | 标中旗 +5 / 面旗(量纲与语义经 A3 OQ5 重锚 → weaken 行)、清图 +10(→ B4 清图奖励插槽)、增伤 1.5× / 减伤 0.5× / 持续 2 次结算(→ StatusEffectTable 四行)、奖励池 5 类(→ RewardTable 五值)——机制与数值事实登记归 owner GDD 与 registry | diff --git a/design/gdd/reviews/a1-board-generation-review-log.md b/design/gdd/reviews/a1-board-generation-review-log.md new file mode 100644 index 00000000..77b39a42 --- /dev/null +++ b/design/gdd/reviews/a1-board-generation-review-log.md @@ -0,0 +1,49 @@ +# A1 扫雷棋盘生成 — Design Review Log + +> 文档:`design/gdd/a1-board-generation.md` · 首次 full 评审:2026-09-09 +> 模式:full(六专员并行对抗 + creative-director 终审)· 用户在席拍板修订路径 + +## Review — 2026-09-09 — Verdict: MAJOR REVISION NEEDED + +- **Scope signal**: L(F1/F2/F2b 三式返工 + 1 个键结构决策 + D2 schema 回写 + 黄金向量全链重制;无新 ADR;下游仅 A2/D2 实质回验) +- **Specialists**: game-designer · systems-designer · qa-lead · level-designer · performance-analyst · creative-director(终审) +- **Blocking items**: 8 | **Recommended**: 14 | **Nice-to-have**: 13 +- **Completeness**: 8/8 节齐备;上游依赖 A4/D2/A3 GDD 均存在且交叉引用核实为真(A4×15 @a4:153、D2 BoardGenTable 回填 @d2:39、A3 EC5 委派 @a3:126);下游未写 GDD 属设计顺序内前向登记 +- **Prior verdict resolved**: First review + +### Summary(creative-director 终审) + +工程纪律罕见的 GDD——五阶段管线、F4/F5 框架、依赖拓扑全部存活——但承重量度(F1/P1 门槛的可判定形式)与承重键结构(第三轴)须在冻结前重定,且验收体系存在构造性不可满足的断言腿。属"承重处返工"而非方向性推翻。五专家切面互补零重叠,无一条 blocking 属过度指控;qa-lead 覆盖矩阵核对成立(5 处标注问题,映射主体为真)。 + +### Blocking 清单(re-review 逐项核销对照;缺一即不得放行) + +| # | 主题 | 来源 | 一行化 | +|---|---|---|---| +| B1 | **P1 度量双洞** | game-designer | F1 雾元知识入分子无占比上限(合法高雾行可零推理过线)+ `inferability_target` 无绝对下限(断言 A 相对化,降 target 永不开火)→ 增第二达标判据(剔除雾格后的可推理率 inferability_core)+ 雾占比绝对上限 + target 绝对锚(schema 域收窄 + F4 绝对断言) | +| B2 | **AC-6(a) 不可满足** | qa-lead | 可达域外扩 1 环内必有芯内数字格的邻雷,"零雷零变异"照文本恒红;所引义务只禁变异不禁雷 → 改写:芯内零雷零变异 + 环内零变异(可含雷) | +| B3 | **雷数取整未钉死** | systems-designer | 取整规则零出现;ceil 下 F2b"sound 保守"为假;同参数 |S| 漂移 36–44;F1 例板"雷 18"不可复算 → 钉死 `round(d×(W×H−9))` 先定数后落位 + 布雷↔可达域顺序(先布后删)+ 指标集增实际雷数/|S| + 例板全链重算 | +| B4 | **F2 ≢ Schema 三条款** | systems-designer + qa-lead | 角点 (count=0, f>0) 两形答案相反(三条款放行 / F2 拒绝),"等价、零新增语义"为假,AC-2 漏测 → Schema 采 F2 规范形(补 `count=0 ⇒ f=0` 腿)+ AC-2 补角点违例腿 | +| B5 | **AC-11(b) 数学反例** | qa-lead | Lm(1)=∅ 时"前缀非降"为假(非法高推理率板 vs 合法未达标板),σ 重搜协议会静默掩藏 → 加前提腿(登记验证 B₁ ∈ Lm) | +| B6 | **A4⑭ 缺半边** | game-designer | A4 原文批量断言两件套(出现率下限**/降级率上限**),部分达成 shortfall 对断言 B 失明且零断言 → 断言 B 收紧为"实际达成区数 = fog_region_count 的板占比 ≥ 0.95" | +| B7 | **第三轴无落点** | level-designer | game-concept 定稿三轴难度(MVP 关卡含关底 Boss),A1 冻结键结构无修饰位(D2 自留 elite_/boss_ 后门而 A1 无)→ **D2 数值回填前**二选一白纸黑字(用户拍板) | +| B8 | **确定性成本闸缺位** | performance-analyst | 64×64/d=0.5/attempts=32 通过全部现行校验、换图相位同步卡死百秒级;墙钟时间逃生与回放硬承诺根本不兼容;F2 裁决不覆盖成本可行性(其有可判定静态形)→ Schema 冻结成本谓词形式(attempts×W×H ≤ C,数值 provisional)+ 文本级禁墙钟逃生 + OQ2 扩写四活路两死路 | + +### Recommended(14,未核销项留待后续修订) + +R1 运行期降级图玩家侧零诚实(熔断回退 + legality-only 追加尝试 + 玩家侧语义登记)[GD] · R2 难度单调性零校验(monotone_in_depth 标记 + F4 断言 E advisory)[LD,终审顶格并入修订包] · R3 d→0 零雷板穿越断言(|M|≥1 不变式)[SD] · R4 σ(L,D) 精确碰撞 + 行间种子重叠(σ := 10⁹+2²⁰·L+2¹²·D)[SD] · R5 F1 注记③"两通道全覆盖"非定理(注记改口)[SD] · R6 AC-10(d) 不免疫调参(条件化)[QA] · R7 AC-12(c) 单调腿空转(前缀一致性腿改写)[QA] · R8 shortfall 术语二义(钉死双字段)[QA] · R9 覆盖矩阵 5 处修正(⑬差分夹具 / 板ⅲ′对照板 / 板ⅰ负面证明 / F3 |U|÷|F| 落点 / ⑪间接承载加注)[QA] · R10 O4 裸奔 deferral(F5 第四独立入口 + Technical Setup 登记)[QA] · R11 AC-2 域违例腿(mine_density=0)+ AC-3 manifest 补全 [QA] · R12 报告 C 增列(付费格分位数 / 预计轮数 / 贴脸雾频率)+ F4"分钟级"条件化 + 分片续跑 [LD+PA] · R13 表达力与体验登记(纯矩形边界 + 雾=雷描边 AD/CD 裁决 + L1 教学身份 + target 递降显式决策 + P1 表述诚实化 + P3 义务冒领改写)[LD+GD] · R14 推理器 worklist 行为契约注记 + ADR 预算基准 + 禁全对全子集 [PA] + +### 分歧裁决(终审) + +雷数取整:SD blocking > GD recommended(F2b 是致命谓词、例板是向量模板)· AC-11 前提:QA blocking > SD nice(spec-AC 阻塞标准字面命中)· 第三轴:LD blocking > GD recommended(MVP 含 Boss 是定稿事实)· A4⑭:GD blocking > 主评审初倾 recommended("缺项即 blocking"门禁规则优先)· 冒烟 30s:PA 反对"风险"定性成立(估算 4.5–9s)· F1:SD"数学自洽"与 GD"度量空心"互补——公式正确地度量了一个错误的量。 + +### Nice-to-have(13) + +出生 3×3 位置钉死 · F2b 保守带量化 · f≤8d 浮点恰界数据作者规则 · `actual_fog_ratio` 定名 · Core Rule 3 三分支措辞补全 · AC-1 锚行存在性登记 + "Core Rules-3"〔D2〕标注 · AC-18 白名单倒置 · AC-11/12 attempts 期望改表读值 · 8×8/d=0.5 经验包络 lint · F4 流式断言姿态 · 冒烟耗时上限断言 · A4 OQ1 最大区域尺寸回执 · AC-5 腿 2 空转口 + 换根协议声明 + +### 终态(2026-09-09 同日) + +- **Blocking 核销**:B1–B8 全部修订落盘(落点见评审输出对照表;GDD header Status 行有逐条索引);cheap-now 6 项顺路核销(σ 打包防碰撞 / F1 注记③ 改口 / F3 |U|÷|F| 删承诺 / monotone_in_depth + 断言 E / EC5 玩家侧语义登记 / 布雷数 N≥1 护栏)。 +- **用语纪律回溯(2026-09-09)**:按 CLAUDE.md 新增用语纪律,本日志与 A1 GDD/registry/systems-index 中的自造词"非雾腿""双闸"已全部替换为直白表述(剔除雾格后的可推理率 / 两项判据)。 +- **用户三决策(拍板记录)**:① 达标判据修法 = 三层组合(F5 增第二判据 inferability_core〔剔除雾格后的可推理率〕+ F2 雾占比上限 0.25 + F4 对照断言);② target 绝对锚 = schema 域收窄 [0.85,1] + F4 断言 A″;③ 第三轴 = 登记约定 + 预留后门(Boss=Dmax 行 + elite_/boss_ 命名空间 + OQ6)。 +- **放行**:用户拍板**免独立 re-review 直接 Approved**(A3/A4 先例);systems-index 已更新,D2/registry 接口漂移回写完成。 +- **留档未核销(9 项 recommended,非阻塞)**:AC-10(d) 条件化(R6)· AC-12(c) 前缀一致性改写(R7)· shortfall 双字段钉死(R8)· ⑬ 连通分量差分夹具(R9 部分)· 板ⅲ′ 去雾对照板 + 板ⅰ single-only 负面证明(R9 部分)· O4 owner/deadline 登记 + F5 第四独立入口(R10)· AC-2 域夹具 + AC-3 manifest 补全(R11)· 报告 C 增列 + 分片续跑(R12)· 表达力/体验登记与 P1/P3 表述修订(R13/R14)——随实现期/调参期处理。 diff --git a/design/gdd/reviews/a2-minesweeper-rules-review-log.md b/design/gdd/reviews/a2-minesweeper-rules-review-log.md new file mode 100644 index 00000000..bcb48edd --- /dev/null +++ b/design/gdd/reviews/a2-minesweeper-rules-review-log.md @@ -0,0 +1,34 @@ +# A2 排雷玩法规则 — Design Review Log + +> 文档:`design/gdd/a2-minesweeper-rules.md` · 首次评审:2026-09-09(full:四专员 + CD 终审,独立会话) + +## Review — 2026-09-09 — Verdict: NEEDS REVISION → 同日修订核销,用户拍板放行 Approved(A3/A4/A1/B3 先例) + +**Scope signal**: L · **Specialists**: game-designer / systems-designer / qa-lead / ux-designer / creative-director(终审) +**Blocking items**: 6 | **Recommended**: 8(随批处理 + 留档) | **Prior verdict resolved**: First review + +**Summary(CD 终审)**:承重结构全部站得住——确定性引擎(AC-19/20/22)、三事件封闭回边 + 双发射源、F1–F4 经逐边界值检验与两轮独立复算零动摇;系统性弱点唯一且四专家收敛于同一处:引擎层诚实,但玩家感知边界层的承诺("拒绝可辨"、旗承诺、左键单义性、P4 氛围)缺乏数据契约与登记台账的承接。六项 blocking 全部为文本级/契约级、同日可核销,规则引擎承重语义零触碰。 + +### 阻塞 → 修复对照(同日核销) + +| # | 阻塞(来源) | 修复落点 | +|---|---|---| +| 1 | 连锁展开碾过旗子——"已开格+滞留旗"未定义状态;OQ1 上线后每轮流血(game-designer GD-1;CD 终审取修法 (a)) | Core Rule 6/F2 传播集 `∖ G`(链不越旗,经典 flood 惯例;旗 = 1 AP 链阻断器)· Rule 8"旗恒在未开格"不变式 · 新增 EC17 · AC-5 分支 2(b_chain 旗挡链,chain_size 13→12)· F2 同构声明加 G=∅ 域注记(双包含不受影响)· D5 教学"旗挡链" · registry 红线 + terminology 同步 | +| 2 | F4"到达序无关性"声明过宽被自身结算语义证伪(ap=1 [move,dig耗]/[dig耗,move] 执行集不同;EC15 自反例)(systems-designer SD-2 + qa-lead QA-2;主评审 ap=2 反例被更正为 ap=1) | 声明收窄((path_tag, hint, AP/RAP) 全批域不变;划分序无关仅异类四组合;同类多成员到达序 = 输入)· 出口映射改按结算时点 clears_map 键控 · "二态重合"句按精确命题改写(C_other 前缀无 C_dig/stop ⇒ 与 A3 Rule 3 不冲突)· AC-10 重写:c/d 改 ap=1 耗尽型救援(修 "rap += 8" 违例)+ 用例 f 联合清图 + 事件序断言 | +| 3 | 三源拒绝撑不起"拒绝可辨"——board_face 折叠 9 种玩家语义(ux-designer UX-1;CD 采 sub_reason 方案,否决 D1 反推 = 第二套实现) | `ActionOutcome` 增 `sub_reason` 封闭枚举 {not_adjacent, target_revealed, target_concealed, flag_present, flag_absent, no_path, out_of_catalog}(Core Rule 5/AC 前言/AC-21/EC9/registry 同步;批前免费窗口,批后 = 接口漂移) | +| 4 | D1 义务登记缺漏(ux-designer UX-2+R3 合并;B3 D1×4 先例) | D1×1 → **D1×6**:① stop 经 settle_batch ② 四格态一眼可辨 ③ 悬停预承诺+可挖域+chord 首遇提示 ④ 雷格可通行可读升台账 ⑤ 路径呈现语义定稿 ⑥ 单意图帧收集+voided 呈现+拒绝可感知——Interactions/Dependencies/UI Requirements/OQ5 升格/systems-index 五处同步 | +| 5 | AC 仪器修复包(systems-designer + qa-lead) | "EC4 腿"编号 **AC-6**(覆盖矩阵/门禁表对齐,计数 23 条)· b_chain/b_fog 夹具补 **spawn=(3,2)**(默认中心读法 B3 含雷违反不变式①——逐格复算)并入 OQ3 · board_invalid 五类封闭枚举 {inv1, inv2a, inv2b, inv3, inv4} · AC-7 拆 ap=1 同帧竞争 · AC-1⑤ 升有序断言 · AC-13 跨轮不重放断言 · AC-14 四对显式配对 · AC-4 单意图批等价护栏 · EC9 目录外用例 · OQ2 范围收窄 · AC-3"以最终产物板计" | +| 6 | 用语纪律违例——CLAUDE.md 点名反例"双闸"×2 在文;"腿"×15;"袋区/合成判定"无定义(全体专家一致) | 机械替换为直白语言 + GDD 头部"术语与约定"白话定义块 + registry `terminology` 条目(豁免通道) | + +### 随批必办(CD 裁定顺路核销) +- Rule 11 注记:雷格通行 = 有价捷径(1 AP + 雷→敌方优势,P3 管线计价)——GD-2 降级 residual(A1 clearable 闸按安全格 8 邻连通计,强制过路费板生成期被拦截,终审裁决降 recommended) +- OQ1 增惩罚重复性裁决项:hit=true 兑付有去重 vs hit=false 惩罚无去重的不对称——实测会话前钉死 +- systems-index 增 B1/B4 义务两条:高光兑现节点条目化(创意风险 #2/#3)· 连锁批量授予溢出吸收设计(GD-3 降级为条目化一行;game-designer R-5) + +### Recommended 留档(未核销不阻塞) +SD-5 联合清图被 stop 剥夺已入 EC6 条款 · UX 点击摩擦指标已规格化(归 D1、输入动作数、挂 A3 OQ3)· 远距复合意图 D1 评估项已登记 · D4/D1 命令式反馈例外已声明 · 其余 AC 补强(AC-10c/d 事件序已补;带旗末格永不 cleared 腿、同轮多雷腿、AC-17 变异残留可检化等 nice 项)与措辞类(雾内旗"赌"措辞、P4 依赖集中度披露)留待实现期顺路处理。 + +### 主评审结构结论(留档) +完整性 8/8;上游 A1/A3/A4/D2 交叉引用逐项核实一致;OQ1 在位(门禁核对项通过);夹具 b_spawn/b_degenerate 独立复算通过;registry 两条件目一致;依赖方向零单向遗漏(B1/B4/D1/D4/D5/A5 未设计以 systems-index 义务清单登记)。 + +**Prior verdict resolved**: First review → Approved(用户拍板,2026-09-09) diff --git a/design/gdd/reviews/a3-action-economy-review-log.md b/design/gdd/reviews/a3-action-economy-review-log.md new file mode 100644 index 00000000..160a8e50 --- /dev/null +++ b/design/gdd/reviews/a3-action-economy-review-log.md @@ -0,0 +1,149 @@ +# A3 行动经济 — 评审日志(Review Log) + +> 文档:`design/gdd/a3-action-economy.md` · 本日志由 /design-review 流程维护 + +## Review — 2026-09-07 — Verdict: NEEDS REVISION(待修订后 re-review) +Scope signal: M +Specialists: game-designer · systems-designer · qa-lead · economy-designer · creative-director(senior 终裁) +Blocking items: 6 | Recommended: 7 | Nice-to-have: 6 +Summary: 工程纪律为前置文档中最佳——8/8 章节齐备、零随机/零字面量、D2 回填值侧(Tuning Knobs 删 4 项 + registry `action_economy` + Status 登记)经独立核实属实、与 game-concept 参数级一致。但六条 blocking 暴露同一族病灶:仲裁、归属、状态语义等"没有公式可写的地方"系统性欠账,且 EC6/EC9 两处出现被自身文本证伪的对冲声称。creative-director 终裁:全部修复为局部文本与清单级工作,无需结构重设计;两个真结构变更(×2 阻尼、旗兑付语义定死)刻意延后并给触发条件。 +Prior verdict resolved: First review + +### 6 条 blocking(修订会话照此清单执行) + +1. **结束路径竞争仲裁缺失 + 状态行枚举顺序固化错误答案**:最后一格恰用最后 1 AP 挖开时 `ap_exhausted`/`cleared` 同时成立,无仲裁;States 表 Active 行把"AP=0 → ap_exhausted"列首位,自上而下实现即"耗尽优先"、静默丢失清图奖励。AC-4 字面未排除最后安全格;EC4 只覆盖 stopped vs cleared;同批次"停手+耗尽 AP 挖格"顺序漂移未定义。修法:总优先级 `cleared > stopped > ap_exhausted`(EC4 并入);AC-4 补前置"该格非最后安全格";新增"AP=1 挖开最后安全格 → (0, cleared) + 清图奖励 + 换图"AC;AC-8 双序断言;`ap_exhausted` 改分段定义 `conversion = 0` + `remaining_ap = 0` 不变式断言(删 ×0 乘数)。 +2. **账本扣减归属三处矛盾 + round_end 发射方未定义**:States L53(A3 扣)vs Interactions L62(A2 扣,且违反 systems-index"A2 只外发事件"与 D2"A2 经 A3 表间接承载参数"两份已定稿架构裁决)vs UI L134(A2 判定)。Rule 3 归零原子性要求归属唯一。修法:A3 独占账本(`try_spend(cost) -> bool`),A2 只判动作合法性并调用;写明 round_end 发射方与 path_tag 搭载契约;同次编辑声明 Waiting 谓词数据源(B1 信号,如 `next_round_mode` / `battle_over`)。 +3. **旗子双态语义空洞,EC9 被自身文本证伪**:只定义口袋旗(每轮重置),"已插旗"全文未定义;与 game-concept"轮次结算统一判定 +5/面"叠加,读法逐轮兑付 → "旗印机"(第 1 轮插 4 面 = 每轮 16 点 + 20 伤害当量零风险无界被动收入,超停手上限);EC9"停手刷资源无复利"对旗腿为假。修法:Core Rules 增"口袋旗(A3,轮次资源)/ 已插旗(A2,棋盘状态,跨轮保留)"双态定义 + 写明清图时已插旗去向;登记 OQ 给 B4/B1 携带硬约束(禁止无界逐轮重复兑付)与默认方案(CD 倾向"标中即消耗旗印",同时修复旗稀缺失真并铺出"全图标中"高光轨道 = 创意风险 #2);EC9 改"AP 腿无复利;旗腿由 OQ 决定"。 +4. **ActionEconomyTable schema 字段清单未交付,"回填已完成"为残缺真**:owner 只回填 4 值 + 复合价目事实;`dig_ap_cost` 自列 knob 在 D2 无落点(违 D2 Rule 1);插旗"1 AP + 1 旗"拆字段 vs 冻结常量未判定。修法:增 schema 定稿小节(逐字段名/型/域/knob 归属,含 place/pull 成本);registry 复合条目同步拆分;AC-10 拆 10a(registry 与 ConfigDB 逐字段相等,自动化)+ 10b(纳入 D2 AC-14 人工三要素,删自动化字面量硬 THEN)。 +5. **AC 测试载体缺失,headless 冒烟声称不成立**:AC-2~9 的 WHEN 引用未设计的 A2/B1 行为,A3 被动账本无注入口径。修法:AC 节增"测试载体与注入口径"前言——最小 API 面 `notify_round_start` / `request_dig(chain_size)` / `request_flag_place` / `request_flag_pull` / `request_stop` / `notify_settlement_done` / `notify_combat_end`,断言落 `last_round_end`;与 blocker 2 同一工件。 +6. **EC6 停手对冲声称失效,×2 契约未向下游索要**:两层对冲(不滚存、敌方时间税)对纯停手全部失灵(纯停手本来不滚存;时间税对称征收而停手路径总税更低)。CD 裁定:×2 支配未被证明(依赖未设计的 B1 兑付率),不做投机性结构改动;但契约必须写。修法:重写 EC6;Dependencies/Formulas 增"下游平衡契约"——B4 显式不等式目标带(B4 单格期望当量 ≥ stop_multiplier;清图奖励 > ap_per_round × (stop−clear)),以 D2 临时锚点(伏击 +3 / 清图 +10)代入首版,登记为 B1 GDD 验收项;契约含"B1 兑付对各 path_tag 同一线性率,否则比例锁定作废须回本 GDD 重议";OQ3 增"纯停轮占比"playtest 指标 + 预注册应急方案(分级乘数/停手门槛,超阈值触发)。 + +### Recommended(CD 要求同一修订遍内顺带完成) + +P1 声称精度(删杀戮尖塔对标句;Rule 2 留 A4 地形成本修饰钩子;移动免费改判归 P4;"压缩决策带宽"改"摊销 + 真压缩器是连锁与停手");F1 变量域绑 knob([0,8]→[0, ap_per_round],registry [0,16] 同步);knob 交互补全(ap×clear 上限、stop:clear 比值、ap_per_round 双重身份);状态机补全(Idle→RoundStart 出口、结算完成门控、Waiting→Idle AC);EC3/EC7"净耗 1 AP"措辞改"插+拔合计 2 AP"(AC-3 THEN 值本身正确不动);缺失 AC(EC3 挪旗、EC6 满 AP 停手=16 边界、EC5 连锁清图;AC-1 拆冷启动/硬重置);旗稀缺三元耦合注记 + 结构不变量 `clear_multiplier < stop_multiplier` + clear knob 低侧敏感性改判(控制权在 B4 清图奖励);价目表"挖开格"行补"仅限 8 向相邻"。 + +### Nice-to-have + +挖已插旗格合法性;旗上限语义注记(A5 安全旗);EC8 声明 out-of-scope(B1 域)+ 考虑"折算先于胜判";零随机确定性 AC;移动 0 成本过账本与否显式声明;game-concept 待定问题"停手/清图转化比例"复选框勾销(CD 要求修订遍完成)。 + +### 专家分歧(已由 CD 裁决) + +×2 修法三路线(结构阻尼 / 只写契约 / 契约条款)→ 契约 blocking + 阻尼降 OQ3 预注册;旗语义定死时机(现在定 / 只登记 OQ)→ 混合:双态定义本轮必须、完整兑付语义归 B4/B1 带约束 OQ;"配额执行退化"表述 → CD 修正为"决策密度不足";主评审"D2 回填已核实执行" → CD 确认部分推翻(值侧真、owner 交付残缺)。 + +### 同日修订落实(2026-09-07,用户两项拍板:旗兑付默认方案 + 契约锚点代入) + +阻塞项 → 修复对照: + +| 阻塞 | 修复 | +|---|---| +| 1. 结束路径竞争仲裁 | Rule 3 增总仲裁 `cleared > stopped > ap_exhausted`(与到达顺序无关、单事务一次判定);States Active 行改"每事务内按优先级仲裁";F1 改分段定义(ap_exhausted 恒 0 + remaining_ap=0 不变式致命断言,删 ×0 乘数);AC-4 补前置;新增 AC-4b(最后 1 AP 挖最后格 → cleared);AC-8 双序断言 | +| 2. 账本归属 + round_end 搭载 | Rule 9 新增:A3 独占账本 `try_spend`(余量检查+扣减+仲裁原子),A2 只判棋盘面合法性并调用;round_end 仍由 A2 外发(systems-index 裁决不变),payload 携带 A3 仲裁结果 `(remaining_ap, path_tag, continuation_hint)`;Waiting 出口消费 B1 回发 `next_round_mode`/`battle_over`;States 表标注"B1 编排权威、本表为 A3 资源侧视图";UI 节非法原因改双源 | +| 3. 旗子双态语义 | Rule 8 新增(口袋旗=A3 轮次资源/已插旗=A2 棋盘状态跨轮保留,清图随图消失);EC9 改"AP 腿无复利,旗腿由 Rule 8/OQ5 封死";OQ5 定稿:**硬约束=兑付一次性、当轮结算、禁止逐轮重复兑付;默认方案(用户拍板)=标中旗奖励锚 B4 最低档安全格同档、当轮一次性兑付,无任何跨轮持续奖励**(+5/面 临时值作废);标记移除/保留归 A2/B4 表现层 | +| 4. schema 字段清单 | 新增"ActionEconomyTable Schema 定稿"节:十字段全集(7 knob + 3 冻结:move/chain/unit),类型域齐备;AC-10 拆 10a(registry 十字段逐项相等,自动化)+ 10b(纳入 D2 AC-14 人工三要素);D2 Core Rules 表行/Status 行、registry 复合条目拆分同步回写 | +| 5. AC 测试载体 | AC 节增前言:最小 API 面(notify_round_start / request_dig(chain_size, clears_map) / request_flag_place / request_flag_pull / request_stop / notify_settlement_done / notify_combat_end),只读观测量 ap/flags/phase/last_round_end;AC-1 拆 a/b、AC-2 加 chain_size 三点与无连锁基线、AC-3 拆 a/b、AC-5/6/7/9 重写、新增 AC-11~14(Waiting→Idle/Idle 拒绝/Idle 出口/结算门控)、AC-15(满停=16 上界)、AC-16(挪旗 2AP)、AC-17(零随机确定性);EC8 声明 out-of-scope | +| 6. EC6 对冲 + F2 契约 | EC6 诚实改写(对冲失效声明);新增 F2 下游平衡契约:B1 单一线性兑付率条款 + 不等式①(B4 单格期望当量 ≥ stop_multiplier)②(清图奖励 > ap×(stop−clear)),首版锚验证(清图 +10 > 8 ✓;①作为预算目标下发 B4,用户拍板代入方案);登记为 B1 GDD 验收项(systems-index 门禁增补记录已登记);OQ3 增预注册指标(纯停轮占比 >50% 信号)与应急方案(分级乘数/停手门槛);OQ6 归一表义务(owner B1) | + +顺带落实(CD 同遍要求):P1 声称修正(删杀戮尖塔对标、Overview 诚实口径、移动归属 P4);Rule 2 留 A4 地形成本修饰钩子 + 补挖/拔相邻口径 + 无 chord 声明;F1 变量域绑 knob([0, ap_per_round]);knob 表补 flag_place/pull 两 knob + 交互乘积三条 + clear 低侧改判 + 结构不变量;EC3/EC7"净耗 1"改"合计 2 AP";EC10(最后 1 AP 清图)/EC11(挖旗格拒绝)/EC12(旗上限)新增;game-concept 转化比例复选框勾销 + 标雷判定行重锚注记;registry 增 round_end_carrier / arbitration_priority 事实。 + +**修订中新增的设计决策(re-review 须覆盖)**:拔旗限相邻(与挖/插对称);挖已插旗格拒绝须先拔(EC11);AC-14 结算未完成时 round_start 拒绝(fail-fast)。 + +## Review — 2026-09-07 — Verdict: NEEDS REVISION(re-review,二轮) +Scope signal: M +Specialists: game-designer · systems-designer · qa-lead · economy-designer · creative-director(senior 终裁) +Blocking items: 7 | Recommended: 8 | Nice-to-have: 5 +Summary: 首轮 6 blocker 中 1/2/4/5 核验为真解决、③纸面解决、⑥未真解决。本轮新暴露 7 条 blocking,核心是 F2 契约的数学错误被三专家独立击穿(②博弈树算错——停手不放弃清图奖励,修正阈值 ≈14-16 使 +10 锚 FAIL;①缺雷风险项)与 OQ5 最低档锚使插旗净 EV ≤ 0;另有 EC12 跨轮封顶被 Rule 8 证伪、AC 可测性四缺口(返回值/批次语义/phase 观测域/EC11 豁免声明)、纯停手轮 round_end 无发射方、D2 AC-7 示例值出域同批矛盾。CD 终裁:契约重写 + 规格文本补丁量(1-2 天、不动结构),够不上 MAJOR,但不能带病放行。 +Prior verdict resolved: Partially(1/2/4/5 resolved;3 paper-only;6 not resolved) + +### 7 条 blocking(下轮修订照此清单执行) + +1. **F2 不等式②博弈树错误**:Rule 4 同图续挖 ⇒ 停手不放弃清图奖励 C;停手→下轮清图双重收益仅付一轮敌方结算,修正阈值 ≈ 2(r+k) ≈ 14-16,+10 锚 FAIL;Tuning Knobs 节"r 点差 + 1 轮敌方结算"已与②自相矛盾。修法:重写为含敌方结算当量 k 的不等式(CD 裁决采纳 systems 框架,k 登记 B1 验收项),+10 锚重锚并如实记录。 +2. **F2 不等式①缺风险项**:安全格奖励 ≠ 挖掘 EV(缺 P(雷)×惩罚项);弱 ≥ 在边界处漂向停手。修法:严格不等式或显式风险条款(B2 owner)+ 混合策略推导写入 F2。 +3. **OQ5 锚经济空心**:最低档锚 = 2 点 = 1 AP 停手等价 ⇒ 插旗最好打平;B4 留余量时旗 farming+停手 = 20/轮破 16 上限;兑付 keyed to 旗实例/格子未定,拔-插重置资格漏洞。修法:锚与最低档解耦(旗奖励 ≥3 点当量)+ 兑付事件以放置实例为键、拔插不重置资格。 +4. **EC12 跨轮封顶被证伪**:R1 插 4 旗停手,R2 口袋重置 4 后拔旧旗 → 5 > 上限。修法:满袋拔旗拒绝或钳制(择一写死)+ 跨轮拔旗 AC。 +5. **AC 可测性四缺口**:request_* 声明 bool 返回;"无记录"改 last_round_end 值断言(防先 emit 后覆盖假通过);AC-8 重写为可测子集 + stop-first 腿显式 logged deferral;声明 phase 观测域(RoundEnd 非静息态,AC-9 半边修);EC11/拔旗相邻/Rule 8 留旗加 out-of-scope 或 AC(日志明列 must-cover)。 +6. **纯停手轮 round_end 无发射方**:停手走输入层直调 A3,round_end 由 A2 外发——无 A2 动作的轮次无发射者;跨调用方批次仲裁机制未命名。修法:Rule 9 择一写死(A3 直发或 A2 代理转发)+ 批次队列 owner。 +7. **同批跨文档不一致**:D2 AC-7 示例 ap_per_round=99 出 A3 schema 域 [1,32](改 12 等域内值);clear_multiplier < stop_multiplier 结构不变量登记为 D2 跨字段 validate() 规则。 + +### Recommended(8) +OQ3 阈值 50%→20-30% + 零动作停手轮先行指标;删"无 chord=经典扫雷语义"措辞、chord 缺席登记 CD 风险 #2 候选;3AP 改错税惩罚对象错位(OQ2 增"挖旗格直接消耗旗+1AP"对照变体);EC5 退化图登记 A1/B1 下游约束;大图 AP 供给比例条款(A1/B1 验收项);F1 上界更名"折算子上限";chain_size 语义定义 + AC-17 重置协议 + settlement_done 后 phase 停留声明 + EC2 拔腿 + GIVEN 构造序列 + THEN 绑表值声明;B4"当量"计量基准(过期折现 vs 面值)。 + +### 专家分歧(CD 已裁决) +停手-下轮清图是否严格支配(economy vs systems)→ 采纳 systems 含 k 框架;chord 补否(game 要求诚实措辞)→ 转 OQ 非 blocking。 + +## 同日二轮修订落实(2026-09-07,用户三项结构性设计决策 + 二轮 7 blocking) + +> 本轮为**结构性修订**而非文本修补:用户拍板三项设计决策,从根上消解二轮评审最重的 blocking。 + +### 用户拍板(2026-09-07) + +1. **三向效果映射**:安全格→主角 buff;标中旗→怪物 debuff;踩雷→怪物 buff。全部扫雷收益以战斗效果直兑,无抽象"资源点"(与 systems-index 既有的 B2 伏击词条门禁天然一致)。 +2. **旗子上限取消**:旗为 A2 棋盘状态单态(原口袋旗双态作废),数量不限,稀缺性由 AP 机会成本 + 8 向相邻承载。 +3. **停手改产 RAP**:剩余 AP 1:1 转存 RAP 池(remain action point),攒满 `rap_refresh_cost`(默认 8)可刷新雷图(无清图奖励);清图剩余 AP 作废(用户追加拍板:**F1 折算公式整体删除**,清图奖励为 B4 差异化插槽、预留按图类型分支)。RAP 生命周期 = 战斗域(用户拍板:每场战斗清零)。 + +### 二轮 7 blocking → 修复对照 + +| 阻塞 | 修复 | +|---|---| +| 1. F2 不等式②博弈树错误(+10 锚 FAIL) | **随结构作废**:停手不再产出战斗资源,"停手 vs 清图"同量纲比较不复存在;F2 整体删除,不修正——移除病灶而非修复 | +| 2. F2 不等式①缺风险项 | 同上作废;风险项以健康条件形态存续于新 F1 条款 3(P(雷)×敌方增益当量,B2 有界 + A1 可推理率共同验收) | +| 3. OQ5 锚使插旗净 EV ≤ 0 | 三向效果定稿:标中旗 = 怪物 debuff;新 F1 条款 2——debuff 档位**严格 >** 最低档安全格 buff 当量;兑付以放置实例为键、拔插不重置资格(Rule 8/OQ5) | +| 4. EC12 跨轮封顶被证伪 | **随旗上限取消而灭失**:口袋旗账本不存在,无越限可言;EC12 改写为 RAP 生命周期边界条目 | +| 5. AC 可测性四缺口 | API 面全面改版:全部 `request_* -> bool`(拒绝直接断言返回值);phase 观测域声明 {Idle, Active, Waiting}(RoundEnd 瞬态,AC-9 改写);AC-8 改"先挖后停"可测子集 + stop-first 腿**显式 logged deferral** 至 A2 集成(门禁登记);"无记录"改 `last_round_end` 值断言(AC-4b);EC11/相邻性/留旗/EC8/刷新奖励全部入覆盖声明(out-of-scope + 承接方);AC-17 补全新实例重置协议;多步 GIVEN 附构造序列;THEN 值绑表值声明 | +| 6. 纯停手轮 round_end 无发射方 | Rule 9 改**双发射源**:A2 外发棋盘路径(原裁决不变)、A3 直发停手路径(payload 同构);systems-index 门禁记录同步 | +| 7. D2 同批跨文档不一致 | D2 delta 回写:AC-7 示例值 99→12(域内,附出域机理注记);~~clear < stop~~ 不变量随折算删除而灭失(无需新增跨字段校验) | + +### 顺带落实(二轮 recommended) + +OQ3 阈值 50%→20–30% + 刷新率/战斗先行指标(原"分级乘数/停手门槛"应急随折算作废删除,新应急:refresh 成本调参/冷却);"无 chord"措辞改"有意偏离经典" + chord 缺席登记 game-concept 创意风险 #3(与 #2 高光缺失合并归 B1/B4);EC5 退化图钓鱼风险登记 A1/B1 下游约束;chain_size 语义定义(附加格数 ≥0);P2 支柱来源清单、game-pillars P3 措辞、game-concept 四处旧规则 + 两条待定问题勾销同步修订;B4 清图奖励差异化插槽(用户新增设计要求,OQ6 + systems-index 门禁)。 + +### 修订中新增/变更的设计决策(re-review 须覆盖) + +- 三向效果映射的经济健康依赖 B1 对照表(F1 条款 1)——新契约比原 F2 简单但同样是 B1 验收项 +- RAP 战斗域生命周期 + 满停一刷基线节拍(`rap_refresh_cost` = `ap_per_round` 的巧合式默认,无结构耦合) +- `request_refresh()` 仅 Waiting 相位受理(编排期资源操作,防绕过仲裁) +- 跨文档已回写:D2(三次回填 delta + AC-7)、registry(action_economy 全条目重写)、game-concept(四处 + 创意风险 #3)、game-pillars(P2/P3)、systems-index(状态行 + 门禁契约改写) + +## Review — 2026-09-07 — Verdict: NEEDS REVISION(re-review,三轮) +Scope signal: M +Specialists: game-designer · systems-designer · qa-lead · economy-designer · creative-director(senior 终裁) +Blocking items: 8 | Recommended: 9 | Nice-to-have: 8 +Summary: 用户三项结构性拍板被终裁确认为对前两轮病灶的正确"移除而非修补"——折算量纲/口袋旗账本/停手现金流三类错误面整体灭失,二轮 7 条中 5 条真解决、结构层经边界值与全序组合推演无退化,无需结构返工。但存在两条移动病灶线:F2 的跨轮激励职责出现无人认领的直系继任("清图 vs 弃图刷新"支配性不等式无人持有,OQ3 预注册对该环结构性失明,EC6"病灶不存在"自证连续第三轮被击穿);跨文档同步族复发(systems-index L69 B1 门禁条款仍载已作废的"停手折算")。叠加 AC 构造算术三处错误、OQ5 兑付键自相矛盾、F1 条款 3① 不可证伪、输入×相位矩阵空洞、schema 跨字段无守卫。CD 终裁:8 条 blocking 全部文本/契约级(1.5–2.5 人天),修毕经 blocking 核销 + F1/AC 两节抽查即可放行,不设第四轮全量评审。 +Prior verdict resolved: Partially(5/7 resolved;#2/#3 部分解决——存续形态恰为本轮 B4/B2) + +### 8 条 blocking(同日修订照此清单执行,CD 终裁合并口径) + +1. **B1 "清图 vs 弃图刷新"支配性不等式无人持有 + OQ3 预注册失明**(game-designer G1 + economy-designer #1/#2 合并,双腿一条):理性环"挖完确定格→不确定即停→攒满刷新"有真实进账零雷险,F1 条款 3② 仅 C>0 守不住;原三指标对该环结构性失明(先挖后停非纯停手、温和变体刷新率低、>80% 即弃使碎片区读数反低)。修法:F1 增条款 5 + OQ3 增弃图率/完成度直方图/停手轮旗动作占比 + 失明声明 + EC6 形态限定改写。 +2. **B2 OQ5 兑付键自相矛盾**(game-designer):"以放置实例为键" ∧ "拔旗重插不重置资格"不可同时为真(重插=新实例=新资格 ⇒ 2 AP/debuff 当轮农场,重开二轮 #3);registry 只登记矛盾一半。修法:资格锚定**雷格实例**(CD 裁定)写死 Rule 8/OQ5 + registry + systems-index + game-concept 四处同步。 +3. **B3 AC 构造算术错误三处**(systems-designer S1/S2 + qa-lead Q1/Q2/Q3):AC-5"3 次挖"得 ap=5 非 3(与 AC-6 同构造不同期望,spec-AC 矛盾);AC-7 前两腿默认表下不可达(EC2 在全 1 价目下不可构造);AC-6b chain_size 差一(k→k−1)+ THEN 散文混值位。 +4. **B4 F1 条款 3① 不可证伪 + 无闭合 owner**(systems-designer S3 + economy-designer #3):无显式不等式、无锚、"A1/B2/B4 共同验收"= 无人签字。修法:显式不等式(P(安全)×buff 下界 > P(雷)×敌方增益上界,锚 A1 可推理率参数 × B2 上限表值)+ B1 单一闭合 owner + provisional 目标带 + B1 强制检查点。 +5. **B5 输入×相位矩阵空洞 + 拒绝机制二义**(systems S4 + qa Q4/Q5/Q6 + game G4):notify_* 返回类型未声明(AC-14 bool/致命两读)、round_start@Active 无守卫(B1 双启动静默 AP←8)、refresh@Active 零覆盖。修法:8 输入×3 相位矩阵表 + 全 API 统一 bool 口径 + AC-18 新增。 +6. **B6 schema 跨字段退化无守卫**(systems S5 判 blocking / game G9 判 recommended,CD 采 blocking):dig=8×ap=1 双双域内 → 挖掘永久不可用软锁;"任意域值均可运作"对跨字段为假。修法:跨字段声明(任一动作成本 ≤ ap_per_round)+ D2 validate() 经接口漂移登记。 +7. **B7 request_refresh 契约三处未钉死**(game G3 升级 + systems S7):决策主体两读(玩家 vs B1 自动)、跨相位连刷语义未定义、refresh⇒new_board 违约无 fail-fast。CD 裁定:玩家发起、B1 代理调用;多次受理合法(时间税定价,冷却入应急方案非规则)。 +8. **B8 跨文档残留批次**(game G8 + systems S8 + economy #7 漂移腿):systems-index L15/L69(**L69 为 B1 门禁条款,危害最高**)"停手折算"旧口径、game-concept"旗子减半"void 候选、A3 引创意风险 #2 实为 #3、OQ6 一名双主、index 缺 D1←A3 边。 + +### 专家分歧(CD 已裁决) + +跨字段校验定级(systems blocking vs game recommended)→ 采 blocking(A3 为 schema owner,二轮 #7 跨字段先例不得双标);"锚缺失"与"指标失明"打包(game 合一条 vs economy 分两条)→ 合并为 B1 双腿;Q4 独立 vs 并族 → 并入 B5 矩阵族;G3 refresh 决策主体 → recommended 升级 blocking(改变 B1 接口面,须在 B1 GDD 前钉死)。Recommended 九条全部接受,G6/E6(RAP 清零呈现)与 Q9(拒绝原因收口)要求同一修订遍内完成。 + +## 同日三轮修订落实(2026-09-07) + +| 阻塞 | 修复 | +|---|---| +| 1. 弃图支配 + 指标失明 | F1 重构为**五条款**(新增条款 5 弃图支配性:C ≥ 弃图节省的期望雷险 + 轮数差×结算强度,B1 闭合,含反向耦合注记与 +10 provisional 验算);EC6 改"形态限定版"诚实声明(①纯停手环自惩成立 ②挖后停环三刹车 + 守门条款指引);OQ3 置换:增弃图率(>30% 观察/>50% 干预)、完成度直方图(<60% 占比 >50%)、停手轮旗动作占比(>50%)、供给 >8 轮占比,附失明声明;应急阈值与 B2 强度联动注记 | +| 2. 兑付键 | Rule 8/OQ5 改**资格以雷格实例为键**(每雷本次战斗至多兑付一次、放置实例仅为当轮结算载体、拔旗重插不恢复已消费资格、换图新雷新实例);registry flag_rules、systems-index 门禁、game-concept 标雷判定行四处同步 | +| 3. AC 算术 | AC-5 构造改 5 挖(rap=3 保正确);AC-7 改 debug override 夹具(flag 成本 2,域内)+ 承载声明(EC2 默认表不可构造);AC-6b 改 GIVEN ap=8/余 4 格、WHEN request_dig(3, true)、THEN 纯值断言;AC-16 GIVEN 改经 API 构造;AC-5b 第二腿补构造序列 | +| 4. 条款 3① | F1 条款 3 显式不等式 + 两端锚(A1 可推理率参数/B2 上限表值)+ B1 单一闭合 owner + provisional 目标带(伏击 +3/敌 18HP/4ATK 代默认表)+ B1 强制检查点 | +| 5. 相位矩阵 | States 节增**输入×相位矩阵表**(8 输入×3 相位);全 8 API 统一 bool(含 notify_*),相位非法 = false + 状态不变 + debug 诊断;AC-14 THEN 改返回 false + 状态不变断言;新增 AC-18(Active 列全覆盖,防双启动);AC-11 增 settlement_done 后 phase=Waiting 中间断言;Idle 行补 ap=0;RoundStart 瞬态声明 | +| 6. 跨字段校验 | Schema 节增跨字段校验条款(任一动作成本 ≤ ap_per_round,validate() 级、违规致命);rap_refresh_cost 行"任意域值均可运作"改"单字段域内";域上界护栏注记;D2 Core Rules 表行 + Status 行登记(schema 类承载,条件性夹具);registry 增 cross_field_validation | +| 7. refresh 契约 | Rule 11 重写:决策主体 = 玩家经 D1 发起、B1 代理调用(B1 接口须含玩家意图路径,禁自动刷);同一 Waiting 多次受理合法(EC15 新增,16 RAP 连刷两图);无结构冷却(OQ3 应急);new_board 回发为契约义务、违约 B1 fail-fast | +| 8. 跨文档残留 | systems-index L15(A3 行删"停手折算")、L69(B1 门禁来源清单改五源)、L57(D1 ← A2,A3,B1)、L90 门禁全量改写(①–⑤ + refresh 契约 + EC4 义务清单 + D1 RAP 反馈)、进度行;game-concept 标雷判定(雷格键)+ 标错惩罚候选(删"旗子数量减半");registry 三处 + 修订记录;A3 创意风险 #2→#3 | + +顺带落实(CD 全部接受的 recommended):条款 2 第二锚(连锁放大边际基准 provisional 1:1 + 总吞吐对账 + 挤占轮成本核算义务);P1"第一个取舍"口径诚实化(构成选择 + 轮末尾巴 + 供给指标);RAP 战斗末清零 D1 呈现 + D5 教学落点(UI/Interactions/Dependencies/OQ4);Q9 收口(拒绝原因读数 defer 至 D1/A2 集成,out-of-scope 声明);try_spend 后 A2 读 last_round_end 组包为接口契约(Rule 9);EC4 deferral 义务清单入 systems-index 门禁;夹具一致性声明(clears_map=false 余量登记);OQ2 双重职责登记;OQ6 biome 战斗域恒定约束;AC-12"无输出"→ last_round_end null 值断言;AC-13 去 RoundStart 瞬态引用;单槽 last_round_end 盲区注记;EC14 不变式断言 debug 载体声明;EC5 初始 3×3 定义落点归 A2;AC-6 增 RAP 跨图保留腿(EC9 cleared 腿);"满停一刷"与纯停手预警同源注记;F1 与门禁编号对齐(①–⑤)。 + +**流程警示(CD 登记)**:"病灶不存在"句式连续三轮被击穿——此后凡此句式必须附形态限定与反例推演义务;跨文档同步族二轮 #7 复发,病根在流程,修订遍须附"本轮已同步文档清单"。 + +**修订中新增/变更的设计决策(下轮核销须覆盖)**:兑付资格锚雷格实例(CD 裁定);refresh 玩家发起/B1 代理 + 多次受理合法 + new_board 契约义务;F1 五条款(新 3/5 条款 + 条款 2 第二锚);跨字段校验规则;全 API bool 口径 + 相位矩阵;OQ3 四项新指标 provisional 阈值。 + +### 放行记录(2026-09-07) + +用户拍板:8 条 blocking 同日核销后**免独立 delta 核销,直接标记 Approved**(未采纳 CD"独立核销 + F1/AC 抽查"建议与主评审"新会话核销"推荐——记录在案)。核销依据 = 上表对照 + 残留扫描("停手折算"/"以放置实例为键"/"三条款"等旧口径全文零残留;AC-1a~AC-18、EC1~EC15 编号完整;schema 七字段 A3/D2/registry 三点一致)。A3 状态置 Approved,下一系统 = A4 地形变异(设计顺序第 3 位)。**下游强制检查点不因免核销而豁免**:B1 GDD 启动时须承接 F1 条款 3/5 闭合对账(强制检查点)与 refresh 玩家意图路径验收;B4 承接雷格实例兑付键与清图奖励插槽。 diff --git a/design/gdd/reviews/a4-terrain-variation-review-log.md b/design/gdd/reviews/a4-terrain-variation-review-log.md new file mode 100644 index 00000000..11fbc803 --- /dev/null +++ b/design/gdd/reviews/a4-terrain-variation-review-log.md @@ -0,0 +1,77 @@ +# A4 地形变异 — 评审日志(Review Log) + +> 文档:`design/gdd/a4-terrain-variation.md` · 本日志由 /design-review 流程维护 +> 成稿会话 lean 门禁注记(systems-designer/qa-lead 各一,2026-09-08)不计为独立评审——本文件首条即首次独立评审。 + +## Review — 2026-09-08 — Verdict: NEEDS REVISION(同日修订完毕,待独立会话 re-review) +Scope signal: M +Specialists: game-designer · systems-designer · qa-lead · economy-designer · level-designer · ux-designer · creative-director(senior 终裁) +Blocking items: 8 | Recommended: 9 | Nice-to-have: 3 +Summary: 核心架构——变异即数据、零特判、生成期承重约束、D2 schema 闭合——经六向对抗仍成立,下游契约质量高于项目均值。但三处承重表述失真(可推理率对账公理自相矛盾、Player Fantasy"三选项"失实、"雾格全部全价"公式半假),叠加 AC 硬伤与 deferral 闭环悬空,未达本项目 spec-AC 一致性阻塞标准。creative-director 终裁:全部修复为文本与契约层工作,不触碰用户拍板的任何承重规则(否决"清图豁免雾格"选项);修订后预期一轮闭合。 +Prior verdict resolved: First review + +### 8 条 blocking(修订会话照此清单执行) + +1. **可推理率对账公理自相矛盾 + Rule 5 单向建模 + 厚度≤2 定理未入文**:Dependencies-B1 行"雾不再作为可推理率减项"与 Rule 5/A1 行/A3 条款 5 注记三方冲突。数学事实(多专员独立验证):雾格⊆非 0 安全格 + 区内无雷 ⇒ 每雾格必有非雾雷邻居 ⇒ 雾区最厚 2 格(3×3 实心雾块不可能)、恒为雷集切比雪夫 1-膨胀边界带、雾面积占比 f≤8d 隐式硬上界;且雾格=已知无雷必邻雷 ⇒ 雾边推理局部反而更易(k 格雾区外环必含 ≥⌈k/8⌉ 雷),"信息剥夺"部分反转为"雾边=推理锚"。 +2. **Player Fantasy"三选项"失实 + B4 定价锚自相矛盾 + P3 声称不过自家裁剪判据**:盲挖零命险、绕开=弃图、买情报 MVP 不存在——权衡感塌缩为"何时交 AP 过路费",正踩自设反向护栏;B4 行却按"无雷险安全收割"最低档定价。CD 裁决:文本级修复(幻想诚实改写 + B4 两条硬约束 + P3 降格为"信息切面"),否决清图豁免雾格(违反零特判拍板)。 +3. **"雾格全部按全价 AP 结算"半假**(level-designer 新发现):邻非雾 0 格的雾格会被免费链开(GDD 自身 EC4 明载)——预算注记/A1 验收/B4 定价口径须改按"链不可达雾格数 × dig_ap_cost"计;量纲同步修正(格×AP×占比 ≠ AP)。 +4. **EC2 触发条件不完整 + 降级无观测**:无解不只"面积不足"——雷膨胀皮肤连通分量碎裂同样无解;level 2 恰是小图低密高发关,静默降级使 P4"解锁新变异"参数侧塌陷且无指标。修法:判据含连通性 + 降级率埋点 + 最低雾出现率参数义务(禁放宽 Rule 3 达成)。 +5. **EC3 预设 A2 未定义行为 + 奖励归属冲突**:"初始自动展开只开 9 格"与出生区多 0 格的 flood-fill 语义冲突;若展开触雾,game-concept"初始展开无奖励"与 EC4"照常入库"无解。修法:Rule 7 扩展的可达域由 A1 生成期确定性计算(A4 原文已有"及其自动展开范围",补确定性来源 + 登记落点);EC3 错误括注作废、邻接合法结论保留。 +6. **AC 四处硬伤 + deferral 闭环悬空**:AC-1 写死 unlock_level=2(违"期望值由表值推导",EC9 调参即假失败);AC-2 双孔(biome_tag≠∅ 时不成立 + u=1 时 u−1=0 越域);夹具前言失实(实为 3 错误夹具 + 1 合法数据变体夹具);A1×11/A2×4 未入 systems-index 门禁记录(对侧无核对义务,"deferral 以对侧登记为生效条件"可永不生效)。 +7. **EC11"可被 counter_item_ids 分别定向"与 schema 矛盾**:该字段为 variant 行级(类型级),两实例共享同一 fog 行清单;照登会使 A1 验收项测出伪语义。 +8. **"雾区无雷"教学零 MVP 验收落点 + 教学口径缺口**:D5 在 Alpha 而雾 MVP level 2 首遇,D1 仅形容词规格、零 enumerated 验收;教学缺"雾内无连锁折扣"(连锁止于雾界玩家视角="连锁莫名断了",触发被骗感护栏);AC-9"任何色板可读"无操作化定义。 + +### Recommended(CD 裁决同遍顺带完成) + +hide_numbers⇒safe_nonzero 跨字段校验(EC4 级联停止是承重规则寄生结论,防未来 cell_constraint=any 行击穿);OQ3 弃图率/完成度指标雾板分层;unlock_level=2 雾占比按关爬坡(教学尖刺);EC10 升格参数域拒绝项+"全图近无数字"描述修正;已开雾格结束拍(底色 tint + 雾区全开完成信号,D1 验收候选);雾区几何 UX 下限(宽度≥2)并入 A1 参数域;AC-3 eligible 未定义 + 夹具行 unlock_level 未指定、AC-5×AC-7 程序构造冲突豁免、空表豁免声明、AC-6 夹具基线漂移、runner manifest 退出码枚举、A1 项 4/5/10 义务措辞改可判定谓词;board_biome 非空无校验 owner(登记 A1 域校验);"跨雾推理"措辞降调 + EC5 标错惩罚推论前置 A2 原型会话。 + +### Nice-to-have + +art-bible 样张补"旗×雾纹理×边界"三重叠加用例;EC11 双雾带合并呈现的玩家可感知性注记归 A5 期;"雾散"动效维持 MVP 不承诺。 + +### 专家分歧(已由 CD 裁决) + +弃图方向因果(economy/systems 边界演算 vs game-designer"必然推高非定理")→ game-designer 胜:两力反向、净效应非定理;登记单向缺陷双方一致,修法一致。连锁开雾格(qa-lead"雾格非 0 永不被展开打开" vs A4 自身 EC4 + level-designer)→ level-designer 胜:qa-lead 误读 Rule 4(不传播 ≠ 不被开);EC3 结论保留、理由删除。幻想处置层级(game-designer 清图豁免选项 vs 文本级修复)→ 文本级修复,豁免否决。 + +### 同日修订落实(2026-09-08,八项阻塞全部核销) + +阻塞项 → 修复对照: + +| 阻塞 | 修复 | +|---|---| +| 1. 对账公理 + Rule 5 单向 + 厚度定理 | Rule 3 增"结构推论"段(厚度≤2 / f≤8d / 元知识可数化,登记 A1 验收);Rule 5 增双向效应段(寂静减项 × 推理锚增项,禁单向补偿);Interactions/Dependencies A1 行改双向净效应建模;Formulas 节条款 5 对账注记改三腿入账(雷险腿消解 + 信息腿双向 + AP 腿),B1 强制检查点输入;registry `fog_ap_note` 同步改写 | +| 2. 幻想 + B4 + P3 | Overview/Player Fantasy 三选项框架改诚实版(雾=清图必经段、绕开=A3 风险退出、买情报标注 A5 期);"里面有价值"由 B4 两条硬约束兜底(雾奖励档位下界 ≥ 最低档安全格 buff 当量、清图奖励锚随雾面积上浮——Interactions B4 行 + OQ3 登记 B4 验收);P3 锚点降格:"变异框架的长期载体,迷雾仅信息切面,雷险由岩浆/冰冻承接" | +| 3. 全价公式 | Formulas 排除声明改两态口径:链可达雾格免费链开、链不可达全价;预算不等式 `链不可达雾格数 × dig_ap_cost ≤ 轮数上限 × ap_per_round`;Rule 4 连锁 bullet 澄清"不传播 ≠ 不被开";registry fog_ap_note 同步 | +| 4. EC2 | 触发条件扩"面积或连通分量尺寸";登记 A1:降级率按 (level_index, node_depth) 埋点 + BoardGenTable 最低雾出现率义务 | +| 5. EC3 / Rule 7 | Rule 7 补"可达域由 A1 生成期确定性计算"+ 初始展开无奖励优先级双保险注记;EC3 括注作废、约束谓词改"出生 3×3 格集及可达域内无变异格,邻接不构成违反" | +| 6. AC 硬伤 + 闭环 | AC-1 改"unlock_level=表读值域内断言";AC-2 双孔修复(tag 条件化 board_biome + 仅 u>1 测 u−1);夹具前言改"3 错误 + 1 数据变体"并补 runner manifest 三行退出码 + 空表豁免声明;A1 清单扩为 15 项并同步登记 systems-index 门禁记录(对侧 GDD 门禁评审逐项核对,缺项即 blocking) | +| 7. EC11 | 改"独立布置身份与独立 board_gen 取号;穿透定向粒度=变异类型(共享 fog 行清单),实例级定向归 A5 期新字段" | +| 8. 教学落点 | UI Requirements ② 登记 D1 MVP 验收两条(图例条目 + 首遇提示触发定义=雾格首次与已开区相邻、一次性、可回看);D5 教学口径补"雾内无连锁折扣/连锁止于雾界属正常/雾格成本两态"+"每雾格必邻区外雷"教学候选;AC-9 操作化(色板无关线稿 + 明度对比数值下限 + 新 biome 强制复测 + 宽度 1 雾带与三重叠加最坏用例) | + +顺带落实:hide_numbers⇒safe_nonzero 跨字段校验(schema 校验节);EC10 升格参数域拒绝 + 呈现描述修正;OQ1 扩容(厚度边界内参数域 + 最低出现率 + 埋点);OQ3 增 B4 两硬约束引用;OQ3 分层指标注记回执 A3(Interactions B1 行);AC-3 eligible 定义 + 夹具行 unlock_level=1;AC-5×AC-7 class_name 豁免;AC-6 来源类别断言;board_biome 非空域校验登记 A1;已开雾格 tint + 完成信号(D1 验收候选);"跨雾推理"措辞降调;lean 版"全价"结论在 Formulas 节头声明部分作废。 + +**修订中无新增设计决策**——全部修复由评审结论与终审裁决直接推导,未触碰用户拍板承重规则(生成期布置约束 + 零特判 + fog unlock_level=2)。 + +## Review — 2026-09-08 — Verdict: NEEDS REVISION(re-review #2,同日修订完毕;**同日用户拍板:按 CD 终裁建议 delta 核销免第三轮 full 评审,直接标 Approved【A3 先例】**) +Scope signal: M +Specialists: game-designer · systems-designer · qa-lead · economy-designer · level-designer · ux-designer · creative-director(senior 终裁) +Blocking items: 6 | Recommended: ~14 | Nice-to-have: ~15 +Summary: 首轮 8 阻塞核销经六方边界值对抗确认方向全部成立、承重规则零触碰(厚度≤2 / f≤8d / ⌈k/8⌉ / 两态 AP / F1 谓词全部存活)。但核销执行为逐字面式、未以失败模式为单位扫描全文:同族部位留转移灶("探路"prong 存活、B4 分母未随成本口径迁移、A2×4 登记在修复执行中丢失、夹具计数二次失实、"调参即假失败"换字段复发),且登记闭环选择性生效(A2×4 / D1×2 / B4×3 / B1 条款 5 输入零门禁登记,deferral 生效条件空转)。CD 终裁:6 项 blocking 全为文本/契约/登记层;B4 经济谓词与统计阈值属越阶段工作、以登记+断言形式条款推给对侧;修复后建议免第三轮 full 评审、按清单 delta 核销放行。 +Prior verdict resolved: 部分——8 项中 3 项半核销、1 项二次失实、1 项修复引入新孔、1 项声明性失实(数学层全部通过) + +### 6 条 blocking(#2 轮修订照此清单执行,同日全部核销) + +| 阻塞 | 修复 | +|---|---| +| 1. systems-index 登记闭环 [qa][ux][gd][econ] | A4 行补全门禁记录:A1×15 全枚举(⑤ 扩链不可达占比生产者义务、⑬ 扩部分达成、⑭ 增批量统计断言形式条款)、A2×4 全枚举、D1×2、B4×3 定价硬约束、B1 条款 5 输入(两态验算 + 优势带 + 防钓鱼 + 生产者);registry referenced_by 补 B1/B4 | +| 2. knob 口径统一 [systems][qa] | counter_item_ids 误标 ✓→✗(D2 表行/registry 同步);AC-1 改"非 knob 六字段定值 + unlock_level/biome_tag 双 knob 表读值断言";前言"唯一 knob"改双 knob;AC-8 对账域显式排除 mvp_content/fog_ap_note + knob 对账口径;registry mvp_content 标 knob 活值 | +| 3. 校验与 AC 可执行性 [systems][qa] | 新增第五夹具 `variation_mask_constraint`(hide_numbers+any,致命)+ AC-4/AC-4b 扩容 + manifest 第四行 ≠0;跨字段校验按接口漂移规则回写 D2 表行与 Status、registry(cross_field_validation 属性);AC-3 第一腿补 level_index=u + 夹具零诊断前置断言;AC-2 钉死默认表根、tag≠∅ 死分支移交 AC-3 夹具腿 | +| 4. 修订残留失实 [gd][econ][systems][qa] | Overview"探路"改连锁路由 + 续付/退出两轴;幻想节"放弃推理的庇护"改 AP 溢价 + 推理原料;P3 节"每格花 AP"改两态;厚度≤2 钉死"≡任意 3×3 窗口非全雾"(对角带反例);夹具前言计数改"3 致命 + 1 warning + 1 合法";预算不等式改增量构成"雾致预算增量"("轮数上限"作废——A3 轮次无界)+ 局限如实登记 + 设计期聚合义务;Upstream 改"零运行时接口消费"并补 dig_ap_cost 引用 | +| 5. B4 约束簇文档腿 [econ][gd] | 硬约束②改"雾成本负担"同分母口径(原"雾面积"分母脱钩作废);增第三条防福利上界(雾板净当量优势带);B1 行增防钓鱼张力(对账 A3 OQ6:逐板差分 × 最低出现率 × 8 RAP = 钓鱼环);①锚 per-AP 平价注记(对齐 A3 条款 2 教义);OQ3 改三约束 | +| 6. MVP 验收契约完整性 [ld][ux] | D1 必含要点枚举补"雾格不传播连锁"(补齐首轮阻塞 8 第二知识源的 MVP 落点)+ 形态硬约束(非模态)+ 图例条目"恒在不设解锁门";首遇提示谓词改"首个实际含雾板进入可操作状态"(免疫 EC2 误触发与 EC3 t=0 歧义)+"一次性"作用域 = 每 run 一次;EC2 补部分达成契约;A1⑤ 定设计期聚合校验 + 违例动作 | + +顺带落实(Recommended 同遍):AC-9 补 art-bible 排期门指针 + v3 紫色临时明度基线 + "宽度 1 雾带两侧毗邻数字格"用例;已开雾格与 0 格可区分性登记 D1 行 + 新增 OQ8(tint/完成信号/信号粒度裁决义务,owner D1+CD);D5"两态成本"教学改玩家侧保守启发式("雾格按全价做预算,连锁白开当惊喜");Rule 7 可达域取保守上界 + A2 门禁回验"展开域 ⊆ 排除域";OQ1 增占比参数双重身份声明(难度 × 情报/探测器强度 + 首遇显著度下限);registry mvp_content 活值标注 + cross_field_validation/pricing_constraints 属性;每用例 reset_diagnostics() 全局声明 + 豁免声明补机制论证。 + +**日志勘误(首轮 Recommended 复核)**:首轮日志 Recommended 行声称"雾区几何 UX 下限(宽度≥2)并入 A1 参数域"与"unlock_level=2 雾占比按关爬坡"已顺带完成——实际首轮修订未落实且反向定调"1~2 格宽雾带为正常形态"(AC-9 最坏用例兜底)。该反转现于此留痕:宽度 1 合法形态保留,位置/显著度调控归 OQ1 BoardGenTable 参数域(A1 期设计决策),非 A4 义务。 + +**修订中无新增设计决策**——两轮修订全部由评审结论与终审裁决直接推导,未触碰用户拍板承重规则(生成期布置约束 + 零特判 + fog unlock_level=2)。 diff --git a/design/gdd/reviews/a5-minesweeper-aux-items-review-log.md b/design/gdd/reviews/a5-minesweeper-aux-items-review-log.md new file mode 100644 index 00000000..ed864e4b --- /dev/null +++ b/design/gdd/reviews/a5-minesweeper-aux-items-review-log.md @@ -0,0 +1,35 @@ +# A5 扫雷辅助道具 — design-review 日志 + +## Review — 2026-09-14 — Verdict: NEEDS REVISION(solo 快速审核,用户指定 `--solo`) +Scope signal: L +Specialists: 无——solo 模式(主审独立完成全部分析;lean lint 首跑 PASS,表格占比 84%) +Blocking items: 3 | Recommended: 5 +Summary: 结构成熟度高——真视窄域护栏(承重规则 + schema 校验 + 专属致命夹具)、零特判延伸、A5/B4 两库存正交、21 项义务闭环(registry 同批登记)与生效前提 deferral 全部落位;上游契约逐条核对无失实(B4 预留枚举/query_identity 预读面/stock 受理域、A4 EC6/OQ2/A5-1、A3 8 API 封闭枚举、D1 呈现面 query_reward_identity、|U|=10 口径溯源 registry direction_precheck 均属实)。3 条 blocking 均为局部修复:① F3 方向自相矛盾(P(雷) 对 ι 严格递增,结论误写为下降);② validate_use 目标谓词欠定义(边缘窗口裁剪/零信息目标无裁定);③ 真视/预读运行时路径零 AC(仅 schema 断言)。recommended:B4×④ 欠登记常数重制、义务编号撞名、aux 行缺 pool_tag、索引行描述失真、应用顺序歧义。 +Prior verdict resolved: First review + +### 修订记录 — 2026-09-14(同会话核销,v1.0-lean → v1.1-lean) + +**用户拍板两项边缘规则:** + +1. **area_3x3 边缘窗口**:照常受理,resolved_target = 窗口 ∩ 板内格(角 4 格 / 边 6 格),道具照常消耗;拒绝案否决(贴边频繁被拒 = 烧包感反噬护栏初衷);真视窄域护栏不破(≤3×3 只缩不扩)。 +2. **零信息目标不可用不消耗**:板级零信息判据(全板无未开雾格 / 无未开资格格)→ 库存投影 usable=false、D1 图标灰、使用入口禁用(B4 usable_now 先例);点击级零信息(area_3x3 窗口内零未开资格格)→ validate_use 拒绝零消耗;组合行判据取 OR(爆破包 ap_grant 恒可用、全图卷 = 有雾格 ∨ 有资格格);真视零雷窗口恒有效(零雷 = 信息)。 + +**核销对照:** + +| blocking/recommended | 修复落点 | +|---|---| +| B1 F3 方向矛盾 | F3 结论/推导/验算表改写(收益 = 盲赌暴露下降、单赌命中率上行属正常)+ registry formulas 同批修正 | +| B2 谓词欠定义 | 新增「使用目标校验谓词」表(Core Rule 8 展开)+ EC1 扩 + EC17/EC18 新增 + target schema 行边缘裁剪注记 | +| B3 运行时 AC 缺失 | AC-15(真视 + 边缘腿 + 零雷腿)/ AC-16(预读 + 全图卷 + 零信息腿 + usable 投影)新增,覆盖矩阵同步 | +| R4 B4×④ 欠重制 | 义务行扩「按 B4 缺口预检 6 流程重制登记常数与受影响 AC(σ 黄金轨迹 Ψ/Φ、AC-18 category 断言)」 | +| R5 编号撞名 | 义务清单源前缀注记(跨 GDD 台账用「A5→对侧×①」形式) | +| R6 pool_tag 缺失 | MVP 行集补 pool_tag=safe_cell 注记 | +| R7 索引行失真 | systems-index A5 行描述改「显影/真视/预读/加 AP 四类辅助消耗品」 | +| R8 应用顺序歧义 | Core Rule 7 / 流程字符画钉死(空间 kind 先、ap_grant 后——列表序非运行时语义) | + +连带同步:query_inventory 投影增 usable 字段(API 面 + 呈现契约 + registry api 行)· D1×② 可用性门呈现扩列(图标灰 + 拾取预览)· 缺口预检 1 扩 A2 读接口白名单(雾区身份 + reward_eligible 资格标记)· systems-index 状态行 In Review → Revised + 门禁增补记录新块 · 正文 Core Rules 表同批口语化重写(并行会话完成,语义零变更,本批已并入新规则口径)。 + +**后续**:re-review 待新会话(`/design-review design/gdd/a5-minesweeper-aux-items.md`);AC-12/AC-13 生效前提(B4×①…⑤ / D2×①② 修订批)不变。 + +## 放行记录 — 2026-09-14 — Verdict: APPROVED +用户拍板:3 blocking + 5 recommended 同日核销后**免独立 re-review 直接放行**(B2/B1/D1 先例)。状态同步:A5 GDD 头部 Approved · systems-index A5 行 Approved · 本日志放行记录(registry a5_aux_items 不承载状态,无改动)。**遗留裁决义务不随放行消失**:B4×①…⑤ / D2×①② / A3×① / B1×①②③ 修订批兑现前,aux 行入池加载致命(B4 Rule 1 预留值拒绝 fail-fast)——义务清单缺项即 blocking 的性质不随放行消失;OQ1–OQ6 维持既有 owner/时机。 diff --git a/design/gdd/reviews/b1-settlement-review-log.md b/design/gdd/reviews/b1-settlement-review-log.md new file mode 100644 index 00000000..eea93453 --- /dev/null +++ b/design/gdd/reviews/b1-settlement-review-log.md @@ -0,0 +1,17 @@ +# B1 战斗结算规则 — Design Review Log + +> 文档:`design/gdd/b1-settlement.md` · 本日志由 /design-review 生成与追加 + +## Review — 2026-09-10 — Verdict: NEEDS REVISION +Scope signal: L(13+ 依赖、6 公式、跨 A1–D2 编排 + 5 下游消费方、2 项实现期 ADR 登记;creative-director 另判修订批本身 M 规模——单批有界、无结构返工) +Specialists: game-designer · systems-designer · economy-designer · qa-lead · ux-designer · creative-director(资深终审) +Blocking items: 8 | Recommended: 21 +Summary: 承重结构经五路对抗评审零翻案(段序骨架、乘区结构、F0 归一口径、生命周期编排、23 条上游义务落位、AC 覆盖矩阵经 qa-lead 抽验属实)。三处契约级缺口构成阻塞主体:完美排雷 ×2 无运行时兑付载体(虚增价值行,须二选一裁决);条款⑤闭合计价模型漏计全池挖掘量(清图须挖穿残余池,期望踩雷 ≈ |M|×n_s/(n_s+1),登记读数系统性乐观、verdict 视为未证明);输出协议在 AC-42 冻结前缺 target/effect_id/敌方 source_class 字段(多敌归因退化 + 接口漂移价格回流)。另有五条纪律性阻塞:条款⑤/AC-40 缺多敌验算腿、hit=false 零痕迹(完美谓词归因断裂)、P_perfect ÷100 换算未成文(spec-AC 同输入不同答案)、AC-18 腿 2 GIVEN-THEN 不可同真。 +专员分歧由 creative-director 裁决四项:F5 修正采完整计价模型方向(读数以重跑为准);×2 定性为"必须写明载体,推荐真实双份交付";误标零痕迹维持阻塞但理由收窄(自建谓词的归因痕迹不等 A2 惩罚);协议扩字段收四项进修订批(qa-lead 会签 AC-42/17/1/33 连锁)。 +Prior verdict resolved: First review + +## Revision — 2026-09-10 — 同日修订批核销 + 用户拍板放行(Approved) +Scope signal: M(修订批本身);系统实现规模维持 L +Blocking items resolved: 8/8 | Recommended outstanding: 19(留后续批次,owner/时机见 GDD 修订前 recommended 清单) +Summary: 8 blocking 全部落盘——完美排雷 ×2 落真实双份交付载体〔用户拍板:道具类 stock ×2 / buff 类 apply_effect ×2,grant_clear 恰一次不变,B3/B4 零接口修订;连带 p_perfect 载体域收窄 {100,200}〕+ 高光行有效兑付语义与零兑付降级规则;F5 计价模型重推为挖穿残余池口径(期望踩雷 = |M|×n_s/(n_s+1)),条款⑤ verdict 翻转为未闭合并登记 C3/B4 约束包(C 量级上调 or 接受非支配设计态,二选一归 C3 裁)+ 多敌腿 + ι→1 域守卫 + 护栏 B 输入登记与双读数;行协议字段分层(target/effect_id/pool_value + enemy_action/flag_miss,行类型 12→13 类);hit=false"标旗未中"零值行(每雷实例一次去重);P_perfect ÷100 成文;AC-18 腿 2 按 qa-lead 改写;F4 V_safe 标签更正 + 双口径读数;clamp 字段统一八字段(registry 同批回写,D2 GDD 行按收编先例以 registry 为准)。**用户拍板接受修订、免独立 re-review 直接标 Approved(A3/B4 先例)**——未核销 recommended 与条款⑤约束包由 C3/B4/D1 各自门禁评审跟进。 +Prior verdict resolved: Yes — 2026-09-10 NEEDS REVISION(8 blocking)同日核销 diff --git a/design/gdd/reviews/b2-enemy-system-review-log.md b/design/gdd/reviews/b2-enemy-system-review-log.md new file mode 100644 index 00000000..26363ada --- /dev/null +++ b/design/gdd/reviews/b2-enemy-system-review-log.md @@ -0,0 +1,62 @@ +# B2 敌人系统(战斗单位系统)— Design Review Log + +> 文档:`design/gdd/b2-enemy-system.md` · 首次评审 2026-09-09(full:game-designer / systems-designer / qa-lead / level-designer / ux-designer / ai-programmer / creative-director 终审) + +## 放行记录 — 2026-09-09 — Verdict: APPROVED +用户拍板:8 blocking 同日核销后**免独立 re-review 直接放行**(A3/A4/A1/B3 先例)——核销质量依据 = 修订全部为声明/前提/登记/规格级文字修复、无结构改动,且承重机制(F1–F4 公式边界实算、F1 同步定理证伪尝试、side-wide 共享池、确定性环 ×死亡即不动手 ×charge 空拍)经六方对抗评审零动摇。creative-director 终裁同步锁定四项核心决策不被后续修订侵蚀:① 视觉推测型 + 线索恒定禁重掷(滑向数值预告的修订一律砍)② 确定性环涌现兑付点(抢杀窗口/蓄力贪雷)③ 雷→伏击守恒 + 溢出可见 + 统一受伤/死亡接口(禁止为可通关仓促激活 hero atk)④ 机制/内容分离 + 信任编排(B1 义务不搬进 B2 守卫)。systems-index B2 状态已同步 Approved。**遗留裁决义务不随放行消失**:OQ1 两 face 归属(实现期 ADR)· OQ3 条款 3 左臂 + 胜利路径(B4 先行,B1 强制检查点)· OQ7 精英/Boss 编码位(C1 定稿强制核对)· OQ8 grub 体验证伪(playtest 预注册)· OQ9 零伤害护栏(实现期 ADR/D2)· B1×6 + D1×4 义务清单(各 GDD 门禁评审核对,缺项即 blocking)。 + +## Review — 2026-09-09 — Verdict: NEEDS REVISION(8 blocking 同日核销) +Scope signal: L +Specialists: game-designer · systems-designer · qa-lead · level-designer · ux-designer · ai-programmer · creative-director(终审) +Blocking items: 8 | Recommended: 11(全部同批核销)| Nice-to-have: 10(廉价项随批核销,其余留档) +Summary: 承重机制零动摇——F1–F4 边界实算全部通过、同步定理证伪失败维持成立、F3 守恒与 B3 max_stack=16 双重不可达复核吻合、AC 覆盖矩阵无洞;落差全在"承重的恰是没写的":胜利路径结构不可达未声明、意图预告无时序条款、资产下限缺非意图态、主角推进域未排除、夹具根与判别断言两处测试完整性缺口、事件源类与精英/Boss 轴两处登记缺位。CD 终裁:全部为文字级修订,一次会话同日核销后直接进入实现;三处专家分歧(L=5 探针口径 / grub 单拍环 / 同步定理证明地位)均由 CD 裁决、无须用户出面。 +Prior verdict resolved: First review(当日修订后 8/8 blocking 核销,见下表) + +## Blocking → 修复对照表(同日核销,2026-09-09) + +| # | Blocking(来源) | 修复落点 | +|---|---|---| +| 1 | 当前内容态结构性不可通关未声明(hero atk=0 全定义域无伤害来源——百分比增益对 0 无效 + B4 未设计;OQ3 登记点错位,按其闭合后游戏照样不可通关)〔gd,CD 排序 #1〕 | Overview 新增"当前内容态无胜利路径"结构事实声明 + "接口就绪 ≠ 胜利路径存在";Interactions/Dependencies B4 行标胜利路径先决;Rule 9 加激活联动禁令(禁止单独激活 atk);OQ3 扩项(左臂闭合 = 其经济侧投影) | +| 2 | AC-7 腿 2 判别负断言在 m_e=1(合法 knob)下恒假——正确实现也打红,违反 spec-AC 一致性阻塞标准〔qa,主评审发现加重〕 | 腿 2 加判别非平凡前提 m_e ≥ 2 + 退化显式 SKIP(B3 AC-14 腿 3 先例);探针改游戏态可达值 L=4(20 ≠ 32) | +| 3 | `enemy_multi_unit` 夹具根改 grub 行(环长 1 恒 attack)→ AC-9 混合拍 GIVEN 不可构造、AC-5 同步断言零判别力、AC-7 腿 4 不可构造——三处死测试〔qa〕 | 夹具根改 **ambusher 行 (L_a, D_a) u_c_f=3**(三拍环);AC-5 改 spawn(L_a,D_a)/h_e;AC-7 腿 4 标注多敌根承载;混合拍经单单位重复 advance 构造(冒烟构造自由度注记) | +| 4 | OQ6 事件源类错误:"B3 加 mine_hit 新行"——标错事实源是 flag_judged{hit=false},且 B3 source_class 枚举属结构冻结;错标令 P2 明细行撒谎〔gd/qa/sys 三方合并〕 | OQ6 重写(新枚举值如 flag_miss 须走 B3 GDD 修订 = 接口漂移,非加行);F3 重验触发死条款(cap 域漂移>16)替换为"ambush 新增非 mine_hit 授予源"(旗源漏账 → L 可达 max_stack、双溢出协议冲突) | +| 5 | 精英/Boss 第三轴缺位:难度三维是概念定稿、A1 已留 elite_/boss_ 命名空间;B2 键冻结 + 每行集恰一敌种入结构冻结 + 致命夹具封死同深度第二敌种编码——C1 分支地图定稿必触发连锁返工〔ld〕 | 新增 OQ7(C1 定稿强制核对;改键如 encounter_class 走 D2 Rule 4 接口漂移,schema/AC-12/夹具/registry 连锁回写预登记,A1 OQ6 镜像)+ Schema 节指针 + registry rowset_rule 注记 | +| 6 | Rule 8 无"何时可见"时序条款——若实现成结算播片,"下一拍是重击"的规划输入不存在,抢杀窗口/蓄力贪雷教学/读怪幻想三者同时落空〔ux〕 | 新增 Rule 8⑤:视觉状态 = 下一拍预告、挖掘期全程可见、结算不另设意图播片;D1×4① 同步 | +| 7 | 资产下限自相矛盾:自称"此清单即资产需求下限"却只含存活意图态——缺受击反馈(多敌归因 P2)、死亡表现(重击被掐灭 P1 兑付点)、顺位标记(D1×4③ 物质前提)〔ux〕 | Visual Req 2 补三类入下限(受击可两敌种共用通用一组但须显式登记;死亡瞬间须可辨识不得直接消失;顺位标记归 D1×4③、同等 blocking) | +| 8 | 主角推进域未定义:AC 前言"每个存活单位恰一次 advance"字面含主角——主角无行为环,照做即环长取模除零、指针不变式对 hero 不可满足〔aip〕 | Rule 4 加"行为环为敌单位专属";AC 前言时序口径改"每存活敌单位";query_beat/advance 对 hero 拒绝(空/false + 诊断);AC-5/AC-15 措辞同步收口 | + +## Recommended 处置(11 条全部同批核销) + +1. F4 裕度 4.7× 口径更正 → **2.3×**(左臂按条款 2 最低档锚 +50pp 实算 = 0.36×50 = 18;原值按 +100 两层叠满取值、与"最低档下界"矛盾)——GDD + registry direction_precheck 同步。 +2. D1×4① 补强度档位/环长直觉编码说明(Rule 8② 孤儿半句的验收落点;并入 ① 不改义务计数)。 +3. 句柄生命周期矛盾("销毁态不可观测" vs AC-6 空+诊断返回)→ 统一为 RefCounted + 状态位逻辑失效口径(防 Node+free() 实现路线)。 +4. AC-3 腿 3 / AC-4 前提写死 C3 内容行 pattern → 改按表读值核对 + 前提不满足显式 SKIP(与期望值推导纪律对齐)。 +5. advance 两级 false:预期冻结(抢杀窗口常态路径)无诊断 / 相位句柄失效有诊断(防诊断刷屏淹没信号)。 +6. B1 义务① 增结算内调用序成文(query_attack→apply_damage→advance——倒序读到下一拍;比 double-advance 更常见,B1 冒烟须回放结算步流)。 +7. F2 Output Range 标"默认表值前提";示例/探针改 L=4 + "探针域与游戏态域分离"声明(实现期若给 query_attack 加 L 域守卫须回改 AC-7——届时升级 blocking)。 +8. grub"被感受为血条" → OQ8 playtest 预注册证伪项;两拍环备用案归 C3 内容所有权(CD 终裁:登记风险、不改结构)。 +9. Rule 3 加"同关内随深度切换敌种合法、分布示例非范本"注记;OQ2 加"C1 须真实消费 unit_count>1、Boss 表达不能只靠 unit_count";C3 行登记"新敌种投放节奏 = 关卡命脉"。 +10. 敌人层绘制优先级注记(art-bible 前裁决插入位置,防"哪些数字可信"心智分裂)· 首次蓄力姿态一次性提示候选(D5——L1 无 charge 拍、首课在 L2)· 首个 heavy 明细分量高亮建议(B5 首遇校准课)。 +11. 零伤害死局组合(行 atk=0 ∧ cap=0 × hero atk=0)→ OQ9 + Tuning Knobs 跨字段护栏 owner 登记(实现期 ADR/D2,ActionEconomyTable 跨字段校验先例)。 + +## Nice-to-have 随批核销(同端口廉价项) + +投影快照 + 公开可写属性为零声明(并入 AC-17 扫描域)· pattern duplicate() 拷贝判别腿(AC-11 腿 3——别名引用在换根下同样"不变",补足判别力)· apply_damage n≤0 拒绝 · query_attack 对 Dead 句柄行为定义 · AC-11 坐标锚补登记 · F4 结构上界 min(AP, cap) 收紧(cap>8 时 144→72)· 公式字面量 3→m_amb 符号统一 · "L≥2 过半"限 attack 拍口径 · 混编遭遇 cap 行值歧义登记(随 OQ7 裁决)· D1×4② 升格"全局恒定"。 + +## 专家分歧裁决(creative-director 终裁,无须用户出面) + +- **F2 示例 L=5 定性**:gd 判非缺陷(API 注入探针)vs qa/主评审判与变量域表冲突 → 采 sys 口径:cap≥5 时 L=5 在公式域内、非缺陷;真缺口是 Output Range 未标默认前提——降 recommended(已核销,见 Recommended 7)。 +- **grub 单拍环**:"反幻想敌种"(gd)vs "合理教学起点"(ld)→ 各对一半:结构不改、OQ8 登记 playtest 证伪项 + C3 两拍环备用案。 +- **F1 同步定理证明地位**:"证伪失败成立"(sys)vs "条件成立、前提违反在 B2 侧不可观测"(aip)→ 兼容:维持条件式表述(前提 = B1 义务①恰一次推进),夹具根修复(blocking 3)后 AC-5 具备行为级判别力。 + +## 未采纳/留待后续 + +- D1×4 扩为 D1×5(强度档位独立条目):并入 ① 承载、落点等价,避免义务计数连锁改写(三处引用同步成本)。 +- query_attack 内嵌 L 游戏态域守卫:保持探针自由度,登记实现期回改触发条件(Recommended 7)。 +- 句柄生命周期语义的形式化验证:留实现期冒烟框架落位。 + +## 修订涉及文件 + +- `design/gdd/b2-enemy-system.md`(GDD 本体:Status、Overview 声明、Rule 3/4/8/9、Schema 注、F2/F3/F4、Interactions 六行、B1×6/D1×4 义务清单、Dependencies 两行、Tuning Knobs、Visual Req 2/5、AC 前言四段、AC-3/4/5/7/9/11、OQ2/3/6 + 新增 OQ7/8/9) +- `design/registry/entities.yaml`(enemy.b1/d1_obligations + rowset_rule · clause3_supply.rule/output_range/account_input/direction_precheck · 修订注释追加) +- `design/gdd/systems-index.md`(B2 状态行 Approved + 门禁增补记录 2026-09-09 B2 full 评审增补块) diff --git a/design/gdd/reviews/b3-status-effect-review-log.md b/design/gdd/reviews/b3-status-effect-review-log.md new file mode 100644 index 00000000..7a011e79 --- /dev/null +++ b/design/gdd/reviews/b3-status-effect-review-log.md @@ -0,0 +1,51 @@ +# B3 增益/状态系统 — Design Review Log + +> 文档:`design/gdd/b3-status-effect.md` · 首次评审 2026-09-09(full:game-designer / systems-designer / qa-lead / ux-designer / creative-director 终审) + +## 放行记录 — 2026-09-09 — Verdict: APPROVED +用户拍板:7 blocking 同日核销后**免独立 re-review 直接放行**(A3 先例)——核销质量依据 = 修订全部为登记/声明/规格级、无结构改动,且承重机制经对抗评审零动摇。systems-index B3 状态已同步 Approved。**遗留裁决义务不随放行消失**:weaken 条款 2 锚(B1 强制检查点,OQ1 归一口径前置)、ambush 量纲(B2)、stack-R playtest 证伪项(OQ7)、D1×4/B5×2/D5/B4 义务清单(各 GDD 门禁评审核对,缺项即 blocking)。 + +## Review — 2026-09-09 — Verdict: NEEDS REVISION(7 blocking 同日核销) +Scope signal: L +Specialists: game-designer · systems-designer · qa-lead · ux-designer · creative-director(终审) +Blocking items: 7 | Recommended: 11(含 1 条降级条件性项) +Summary: 机制内核质量过硬——F1–F3 逐式复算通过、19 条 AC 域内覆盖无洞、先兑付后衰减时序干净;落差在"账本≠叙事"(per-layer 归因坍缩、enemy 侧 side-wide 无载体、呈现义务零登记)。全部 blocking 为登记/声明/规格级修复,无结构改动;creative-director 终裁不推翻用户拍板的结构冻结(stack-不重置 R 降级为条件性 recommended + OQ7 playtest 证伪项)。 +Prior verdict resolved: First review(当日修订后 7/7 blocking 核销,见下表) + +## Blocking → 修复对照表(同日核销,2026-09-09) + +| # | Blocking(来源) | 修复落点 | +|---|---|---| +| 1 | enemy 侧状态无 per-target 载体、单敌假设全文沉默〔gd/sys/ux,CD 排序 #1〕 | **用户拍板:单敌假设入文**——B3 Rule 1 + Schema target_side 行 + Interactions B2 行;registry 增 `single_enemy_assumption`;systems-index 登记 B2 回验义务(定多敌→target_ref 接口漂移) | +| 2 | B1 义务清单漏"每结算恰一次 decay_round"〔sys〕 | B3 Dependencies B1 行义务⑥;registry b1_obligations 5→8;systems-index 同步 | +| 3 | 呈现侧义务零登记,违反自家 deferral 标准〔ux/qa〕 | systems-index 登记 D1×4 / B5×2 / D5 增补 / B4 预读;B3 Interactions/Dependencies 相应行加登记指针;registry 增 `presentation_obligations` | +| 4 | AC-15 换根机制误标 + reloaded 根不可构造〔qa〕 | AC-15 重写(测试通道 load_tables 直调、非 reload() 路径、门语义归 D2 AC-16/B1);夹具规格补"全表清单拷贝+置换本表"前提 | +| 5 | 致命类豁免留 B3 表零进程落点〔qa〕 | 新增 `status_effect_dup_key` 致命夹具根 + runner manifest 行 `dup→≠0`;豁免声明收窄改写 | +| 6 | AC-18 与 registry 散文格式不符、机器对账不可行〔qa〕 | registry `status_effect` 条目 fields/mvp_content 重构为逐字段 YAML 键(action_economy 同构);AC-18 改写(结构字段断言 + knob 活值豁免,A4 AC-8 同款) | +| 7 | weaken 红警缺修复空间不对称 + 裁决循环风险〔sys/gd,CD 定级必改〕 | Formulas 红警增补不对称(瞬时读法 m_w≥51 可行 / 总暴露读法被 schema 域禁止);OQ1 增前置步骤"先钉死归一口径";registry anchor_warning 同步 | + +## Recommended 处置(11 条) + +1. **stack-不重置 R(降级条件性项,CD 终裁)**:不推翻用户拍板的结构冻结——OQ7 登记 playtest 证伪项 + B4 奖励预读/D5"同轮拿满"教学补偿义务登记。 +2. F1 合并坍缩归因 → B1 义务⑦(per-grant 来源标注,授予流水为数据源)。 +3. F2 Example 算术笔误(ambush L=3 应 +9 非 +3)→ 已修正。 +4. 插入序断言不可判定 → AC-6 互异 origin_cell + AC-17 同侧逆字典序双实例定值序断言。 +5. AC 前提调参漏洞 → AC-4/AC-8 补 D_a≥2 ∧ M_a≥2 声明 + SKIP 标记;AC-14 腿3 退化哨兵。 +6. refresh 空刷新 vs Rule 5 原则张力 → EC4 原则注记(休眠,首个内容行上线时 B4/B1 随 OQ3 裁决)。 +7. defense_up 顶点两轮免伤 → Tuning Knobs 联动评估注记(B1 对账,禁止独立调参)。 +8. 连锁展开触发溢出沉默 → Rule 5 增"溢出判定与授予路径无关"半句。 +9. 极性不在数据载荷 → Visual/Audio 注记(数据源 = B1 SettlementTable 语义)+ D1×4 之三登记。 +10. origin_cell 呈现义务无 owner → B5×2 之②登记。 +11. AC-19 扫描域无载体 → systems-index 登记 B3 文件集成员清单义务(Technical Setup 归口)。 + +## 未采纳/留待后续 + +- "六归因字段"命名(含 layers/remaining):投影字段语义分组注记留实现期 ADR 一并处理(nice 级)。 +- B3 诊断通道声明(ConfigDB 收集器 vs push_warning):nice 级,实现期随冒烟框架落位。 +- ambush 先量纲后数值的流程倒置:OQ2 既有 owner(B2)不变。 + +## 修订涉及文件 + +- `design/gdd/b3-status-effect.md`(GDD 本体:Status 行、Rule 1/5、Schema 注、Interactions、Formulas 红警、EC4、Dependencies、Tuning Knobs、Visual/Audio、AC 前言、AC-4/6/8/14/15/17/18、OQ1/7、覆盖矩阵) +- `design/gdd/systems-index.md`(门禁增补记录 2026-09-09 B3 full 评审登记节) +- `design/registry/entities.yaml`(status_effect 条目逐字段键重构 + b1_obligations 5→8 + single_enemy_assumption + presentation_obligations + anchor_warning 不对称 + 修订注释) diff --git a/design/gdd/reviews/b4-reward-item-economy-review-log.md b/design/gdd/reviews/b4-reward-item-economy-review-log.md new file mode 100644 index 00000000..5e0aff48 --- /dev/null +++ b/design/gdd/reviews/b4-reward-item-economy-review-log.md @@ -0,0 +1,83 @@ +# B4 奖励与道具经济(Reward & Item Economy)— Design Review Log + +> 文档:`design/gdd/b4-reward-item-economy.md` · 首次评审 2026-09-09(full:game-designer / systems-designer / economy-designer / qa-lead / ux-designer / creative-director 终审) + +## 放行记录 — 2026-09-09 — Verdict: APPROVED +用户拍板:14 blocking 同日核销后**免独立 re-review 直接放行**(A3/A4/A1/B2/B3 先例)——核销质量依据 = 全部 blocking 为文本级/附表级修复、无结构返工,且承重机制(install 期预分配确定性脊柱、封闭 RNG、F1–F6 边界实算、编排被动性、AC/夹具架构)经五方对抗评审零动摇。creative-director 终裁同步锁定八项核心决策不被后续修订侵蚀:① install 期预分配 + Ready 只读 + 零随机出 roll_board 一线(禁止任何运行时重掷"修"问题)② 结算段序核心序(buff/debuff > 道具 > 战斗)③ 恒定表值不吃修正 + attack_up 死行口径后果链(传播而非重辩)④ 库存战斗域 + 逐件实例归因 ⑤ 满层零兑付无折算(只修量具不动裁定)⑥ B4 零跨系统读/调用权集中 B1(窗口 hp 预判、存活集判定、满血守卫全落 B1 侧,任何给 B4 开 B2 读口的方案一律否决)⑦ B1×6/D1×5 义务清单机制(修订后以 registry 版本为准)⑧ 负空间式封闭枚举 API(新增入口须附全输入域语义表)。**遗留裁决义务不随放行消失**:OQ1 attack_up 复活连锁(B1/C3)· OQ2 未开格呈现深度/寻路兑现(A5+D1,垂直切片——创意风险 #1 维持 OPEN)· OQ3 cap 启用裁决(C3 playtest)· OQ4 折算/继承结构位(C2/D5,Alpha)· OQ5 T_fog_min 推导式(B1)· OQ6 weaken 锚归一口径(B1,registry anchor_warning)· OQ7 预警①–⑧预注册(C3/playtest)· A4 约束② MVP 休眠(clear_\ 激活时重启)· 缺口预检 11 项(B1 强制检查点输入:缺口 1/2 阻塞级)· B1×6 + D1×5 义务清单(B1/D1 门禁评审核对,缺项即 blocking)。 + +## Review — 2026-09-09 — Verdict: NEEDS REVISION(14 blocking 同日修订核销,待 delta 复核放行) +Scope signal: L +Specialists: game-designer · systems-designer · economy-designer · qa-lead · ux-designer · creative-director(终审) +Blocking items: 14 | Recommended: 14(廉价项随批核销,其余留档)| Nice-to-have: 13(大部分随批核销,其余留档) +Summary: 承重机制零动摇——install 期预分配确定性脊柱、封闭 RNG、F1–F6 边界实算、编排被动性、AC/夹具架构经五方对抗评审零动摇,CD 终裁"机械核心是管线迄今最扎实的,结构上没有需要推倒的东西";全部 blocking 为文本级/附表级。落差集中在四簇:①跨文档真相分裂(D2 正文两处旧 reward_roll 语义 / 锚参照迁移三处未传播 / 文内义务清单双副本滞后 E10 拍板 / E3"B1 义务⑧"错引);②幻想 overclaim("预读 = 我要去拿 X 路线规划"四方独立确认在 MVP 结构性不成立——reveal = 同轮授予 + 未开格不呈现);③规约自身缺陷(AC-14 与 AC-3 同输入矛盾、AC-6 分支 2 忠实实现即失败、σ 协议塌缩恒 scope 0、三个标量 knob 无数据载体、双授予语义未裁、"无洞"声明过强、E10 理由与 B2 血条数字矛盾);④经济锚悬空(承重数值零登记、F5 计数保底不保伤害、预警④估计与自身权重上限矛盾、A4 约束②MVP 结构性不可闭合)。 +Prior verdict resolved: First review(当日修订后 14/14 blocking 核销,见下表;放行待 delta 复核) + +## 用户评审拍板(本批) + +- **窗口锚点 = 授予段之后、增益/减益段之前**:当轮拾取的道具当轮即可用(CD 70/30 倾向移动、提请用户,用户拍板移动)——消除"当轮拾取下轮才可用"未陈述规则与末图清图奖励必然作废两个伪影;核心段序(buff/debuff > 道具 > 战斗)拍板不变。 + +## Blocking → 修复对照表(同日核销,2026-09-09) + +| # | Blocking(来源) | 修复落点 | +|---|---|---| +| 1 | D2 正文两处旧 reward_roll 语义未同步(Core Rule 5"每次奖励结算"、Interactions B4 行"每轮结算取号"+ use_timing"占位")——"registry/D2 已同步"声明对 D2 正文失实,按旧正文接线使 B4 全部 σ 轨迹 AC 报废〔sys;CD 实证确认〕 | D2 Core Rule 5 改写 install 批语义(R2 向量不受影响注记)+ Interactions B4 行九字段/承重措辞 + Dependencies 行;AC-20 增边界注记(D2 正文对齐 = delta 复核对账项,缺口预检 4) | +| 2 | 文内义务清单双副本滞后 E10 拍板:Interactions D1×③ 与 D1 清单写"两类行"vs L257/285+registry"三类行";文内 B1×④ 缺零效果判定 + "已消耗未生效"行;E3"归 B1 义务⑧"错引(B4 的 B1 清单只有①–⑥,⑧是 B3 的号)〔qa, ux〕 | 全文以 registry 版本为准对齐;E3 改引 B3 义务⑧ + 行协议 owner 明示;D1 清单扩 5 项(见 Recommended 1) | +| 3 | 锚参照迁移(attack_up→defense_up)未传播:systems-index A3 OQ5 注记 / registry status_effect.anchor_warning / clause3_supply.direction_precheck 仍按 attack_up 口径(registry 内两条 owner-truth 打架);迁移后条款 2 预检未重跑(weaken 25 vs defense_up 50 仍差 2×)〔eco, gd, sys〕 | 三处补迁移注记(历史值与 defense_up 数值巧合全等的事实 + 未重跑事实如实登记);systems-index:98 增迁移注记;预检重跑义务挂 B1 对照表落定(OQ6/B1 裁决前置不变) | +| 4 | "预读 = 我要去拿 X 路线规划(创意风险 #1 正面解)"四处承重位 overclaim——reveal = 同轮授予 + 未开格不呈现,寻路结构性不可能;game-concept 证伪信号会把缺失功能误判为设计失败〔gd, ux;sys/eco 复确认;CD 终裁如实降级〕 | Overview/Player Fantasy P1/P4 锚/E3 自救句/预警④缓解腿改写为 MVP 真话(已开格+库存恒读、每格必有货、雾档先验、叠层感知挖掘);game-concept 风险 #1 与证伪信号加 MVP 口径注记、风险维持 OPEN;agency 定位句加注 | +| 5 | T_fog_min/K_dmg/N_draft 无数据载体——不在任何 D2 表、R3 override 无 key 空间(违反 Rule 1);AC-6 分支 4"override→加载致命"机械不可达(override 仅运行时态,D2 边界⑤)〔sys, qa〕 | **RewardConfigTable** 单行全局新增(t_fog_min/k_dmg/n_draft/p_min——HeroTable 接口漂移先例):registry `reward_config` 条目 + D2 表清单回填 + AC-20⑥ 对账;致命腿改烤入根 `b4_fog_gate_hot`(manifest 扩行);override 仅承载运行时合法分支(T_fog_min=1) | +| 6 | 封闭 API 输入域未裁语义:同格双 grant / 同结算重复 grant_clear / 清单外格 query/grant / stock 入参域 / query_inventory 枚举序——幂等与重复两种实现都能让 21 条 AC 全绿;"无洞"声明过强〔qa, sys;CD 采 qa 重述〕 | E18–E21 补裁(重复受理幂等 + 诊断 / 清单外 null/false + 诊断 / stock 仅受理道具类 / 枚举序 = stock 受理序);B1×② 补"每格受理恰一次";AC-12 分支 3 / AC-10 分支 3 补腿;覆盖矩阵"无洞"撤回重述 + 新增入口须附全输入域语义表纪律 | +| 7 | AC-14 分支 3"任意构造上下文恒 true"与 AC-3 分支 3"Cleared 后 use → false"同输入矛盾〔sys〕 | 分支 3 收窄:零**游戏态**守卫(存活敌/hero hp),相位守卫不在豁免域(AC-3/E4/E7 照旧) | +| 8 | 经济锚全面悬空:刀/治疗包 power、max_hp、默认根行集零登记——"供给无忧/贴近下界"结论与预警阈值无锚(原型锚 DMG=4/治疗+4/HP=50 未继承未弃用)〔eco〕 | 默认根五行登记(knife_t1/knife_adv/heal_t1/defense_up_basic/clear_knife,溯源原型锚,provisional 归 C3)+ 刀供给方向预检(B2 clause3_supply 先例;输入缺口 = A1 actuals,缺口预检 11);max_hp=50 出处建议随 B2/D2 回写 | +| 9 | F5 保底维度洞:计数保底对 power=1 零防御;K=0 ∧ 全池零伤害可静默复活 B2"结构性不可通关"blocking 问题〔eco, sys;CD 裁决 4:计数保底不动、加载校验进 MVP〕 | F5 加载护栏两条新致命:`floor_supply_missing`(K≥1 ⇒ ∃ power ≥ P_min 活刀行,P_min = RewardConfigTable 默认 4)+ `no_damage_row`(K=0 后闸,B2 OQ9 并案);夹具 b4_floor_supply/b4_nodamage;概率性致命带 → C3 调参期批量仿真义务(缺口预检 10);标题改"每板伤害供给下界保证" | +| 10 | 窗口 P2 缺口:满血治疗包拒绝零呈现零数据(E10"误触防护"论证以选择时点可见为前提而契约无 hp);窗口选择序未登记为回放输入;行洪水合并的聚合计数字段缺位〔ux, gd〕 | usable_now 守卫预判由 B1 编排组装进窗口数据(义务④——B4 零跨系统读不变);第四类"使用被拒"行协议;窗口选择序列 = 结算回放输入事件(B1 义务①④);未生效类行携带可聚合计数/批次字段 | +| 11 | 窗口时机伪影:窗口先于授予段 ⇒ 当轮拾取当轮不可用(未陈述)+ 末图清图奖励 100% 作废〔eco 提出、CD 裁决提请用户〕 | **用户拍板:窗口移至授予段后**——Rule 5/E10/E15/D1×②/B1×①④ 五处改写;E15 主体消解(残余 = 消亡注记 D1③⑤ + 折算归 OQ4 Alpha) | +| 12 | σ 协议机制缺口:"每 σ 全新服务"塌缩恒 scope 0(需预推进条款);Ψ/Φ/σ\* 登记常数在取号约定 ADR 前不可推导、缺 A2 OQ2 式"纸面契约子集"声明;b4_reroll 夹具登记不完整〔qa〕 | AC 前言补 σ 预推进机制 + 纸面契约子集声明(雾格单行退化分支例外现即可执行);b4_reroll 全值登记(T_fog_min=1 + 三行权重 + clear 行,满足全部加载校验)+ 可达性拆分复核义务;第二人复核义务(A2 OQ3 同型,缺口预检 8) | +| 13 | AC-6 分支 2 忠实实现即失败:加载规则强制 H 活 ⇒ 非雾池 ⊇ {H,L},"非雾格 = L"是概率断言〔qa〕 | 改 32σ 双重量化(雾格恒 H 零泄漏 + 非雾格至少一次命中低档行);登记值降为诊断信息 | +| 14 | 用语纪律批量违规:"腿"×27(CLAUDE.md 点名反例)、"刀"承重未定义、"换签/批尾/贴地/行洪水/自救面/红警/档位序代理"无登记无定义〔ux;CLAUDE.md 强制〕 | 腿→分支全文替换;档位序代理→档位序检查;贴地/行洪水/自救面/换签/红警机械替换;刀/烧包/批尾首次出现处加白话注;registry terminology 增 B4 六词条 | + +## Recommended 处置(14 条;廉价项随批核销,其余留档) + +1. **D1 义务 3→5 扩容(已核销)**:+④ H2 清图奖励行强调呈现 +⑤ battle_over 库存清空可见反馈(RAP 清零同款先例);H3 感知数据前置 = 击杀结果进 B1 流水(义务④);ux"清单外登记会丢失"先例采纳。 +2. **预警④重校准(已核销,CD 裁决 5)**:按吸收上限重推(λ=2.8 vs 1 层/结算——批式授予结构性偏高);拆"稳态/连锁批量"双表分设阈值;缓解改叠层感知挖掘;**不压 defense_up 权重**(压权重会加重构筑维度塌缩)。 +3. **预警清单扩容 ①–⑧(已核销)**:①拆短缺/浪费 + unit_count 分档;②增治疗短缺;③增雾池单一化监测;⑤雾板分层(A4 明文要求);新增⑥速杀退化/⑦窗口摩擦/⑧H2 曝光率。 +4. **A4 约束②③ MVP 休眠显式宣告(已核销,CD 采 eco 加码)**:②结构性不可闭合(非常量池,B1 也闭合不了——原"归 B1 强制检查点"表述失实)+ ③静默绿灯风险;激活载体 clear_\(垂直切片);registry pricing_constraints 同步。 +5. **AC-19 非平凡化(已核销)**:默认根布置两行雾池,对账对 (knife_adv, defense_up_basic) 互异;tier 与当量零耦合的假阳性理由入 F2 注记(防后人升格致命锁死内容)。 +6. **AC-14 分支 1 快照措辞 + AC-15 分支 4(已核销)**:"表读值"→"授予时快照值";换根后 use 回执 = 快照值判别分支。 +7. **display_name 字段(已核销)**:RewardTable 第九字段(现在补一行 schema,晚补动 D1);AC-20 五键→九键连锁。 +8. **B1 义务⑥措辞 + 逐件动态守卫(已核销)**:"double-roll 无守卫"与 E5 相位守卫字面冲突消解(B4 侧守卫见 E5、B1 承载时点半);同窗顺序多包逐件动态守卫入义务④。 +9. **F5 概率性致命带(已核销为登记义务)**:期望值预检归 C3 调参流程(缺口预检 10)——CD 裁决:不做 roll_board 期 B2 耦合(破坏零相邻依赖)。 +10. **创意风险 #2 归属(已核销,CD 裁定 gd 降级)**:爆发结算机制本体归 B1 强制检查点(game-concept 原文归属);B4 交付插槽与明细行数据面;Visual 节诚实化(H1/H2 = 常规节拍登记、H3 = 战斗侧兑现)。 +11. **徽章绘制优先级插入点(已核销)**:旗子之下、纹理之上意向冻结,纳入格子样张测试验收。 +12. **窗口触发政策 + 摩擦指标(已核销)**:D1 义务②注记(MVP 有库存恒弹,候选归垂直切片)+ 预警⑦。 +13. **Rule 9 定义表声明 + 条款④口径收窄登记(已核销)**:Rule 4 豁免声明成文;A3 条款④"行级正当量"收窄在 Dependencies 显式登记。 +14. **equivalence_supply 全活行投影(已核销)**:A4②③与条款⑤对账的道具/清图行值入供给面。 +- 留档未核销:负反馈聚合音已随批登记(Visual 节);雾池单调化监测已随批入预警③;P4 深度差异化结构位登记(Player Fantasy/垂直切片);battle_over 反馈教学归 D5(垂直切片/Alpha)。 + +## Nice-to-have 随批核销(同端口廉价项) + +hp=0 窗口语义钉死(结构不可达注记)· 资格清单重复格去重 + weighted_pick 空行集 null 域注记 · AC-3 补 Cleared 后 query_identity 观测分支 · AC-4 release 侧语义(push_error 致命路径注记)· "批尾 × 草稿数"歧义钉死(每稿含清图一号、作废连带)· AC-6 分支 4 前半补 THEN · Player Fantasy 示例行改"治疗包 ×1 入库" · AC-12"几层"措辞收敛(层数语义归 B3)· 跨容器句柄/stock 域/枚举序入 E18–E21。留档:query_identity 投影加 tier 已随批(H1"好货"档位可读);"泡"类残留零。 + +## 专家分歧裁决(creative-director 终裁) + +- **窗口时机(唯一用户出面项)**:eco 提议窗口移授予段后 vs 拍板字面"结算解析前"——CD 70/30 倾向移动、提请用户,**用户拍板移动**(段序核心不变)。 +- **E3 自救是否存在**:ux"MVP 无任何自救" vs CD"HUD 叠层感知挖掘是真实的弱自救"——采 CD 口径(改写为叠层感知挖掘,非"绕开满层格")。 +- **完备性口径**:主评审"completeness 8/8" vs qa 重述(8 节在位 + EC 映射完整 + API 输入域/σ 协议有已列明缺口)——采 qa,"无洞"声明撤回重述写入 GDD 覆盖矩阵。 +- **创意风险 #2 严重级**:gd 判 blocking vs CD 降为"B1 交接登记项"(机制本体 game-concept 原文即归 B1)——采 CD,B1 强制检查点登记。 +- **F5 维度修复方案**:eco 伤害量纲保底 vs sys 期望值预检 vs 现行计数保底——CD 组合裁决:计数保底不动(拍板)+ 加载期跨字段护栏进 MVP + 批量仿真归 C3 调参流(拒绝伤害量纲保底——须 install 期耦合 B2 敌表,破坏零相邻依赖与回放纯度)。 +- **预警④校准方向**:eco 压 defense_up 权重 vs CD 重校估计 + 拆指标——采 CD(压权重加重构筑塌缩)。 +- **Rule 7 vs AC-14 层间张力**:sys 判"措辞过宽成真矛盾" vs qa 判"层间切分已消解、残余顺序双包"——兼容采双方:层间切分成立( blocking 7 只收窄措辞),逐件动态守卫入 B1×④。 + +## 未采纳/留待后续 + +- 伤害量纲保底(K_dmg_bev):拒绝理由见分歧裁决 5;窗口使用次数是战斗轨迹量、结构上不可 install 期判定(B2 OQ3 同源)。 +- gd"创意风险 #2 blocking":机制本体归 B1(game-concept 原文),B4 阶段交付结构座位即为完整义务。 +- ux"MVP 无任何自救"绝对化表述:叠层感知挖掘为真实弱自救,已按 CD 口径落文。 +- display_name 之外的第十字段(图标键等):icon 映射已由 reward_id 键契约承载,不再加列。 + +## 修订涉及文件 + +- `design/gdd/b4-reward-item-economy.md`(Status/决策记录/Overview/Player Fantasy/Rule 3/5/7/8/9+RewardConfigTable/Interactions 六行/B1×6+D1×5 义务清单/F2/F4/F5 护栏/F6/默认根内容行/预警①–⑧/E 节头+E3/E8/E10/E15/E17+E18–E21/Dependencies+A4②③宣告/双向一致性五处/Tuning Knobs+P_min/Visual+UI/AC 前言(σ 预推进+纸面契约声明+夹具补全+manifest 十行)/AC-1…AC-21 修订/覆盖矩阵重述/缺口预检 11 项/OQ4 收窄/用语清扫) +- `design/gdd/d2-data-config.md`(Core Rule 5 reward_roll 语义改写 / Core Rules 表清单 RewardTable 九字段 + RewardConfigTable 新行 / Interactions B4 行 / Dependencies B4 行) +- `design/registry/entities.yaml`(新增 `reward_config` 条目 / `reward_table` 九字段+默认根行集+direction_precheck+护栏两条+窗口锚点+幂等语义+义务清单同步 / `status_effect.anchor_warning` 迁移注记 / `clause3_supply.direction_precheck` 迁移注记 / `terminology` 增 B4 六词条 / 修订注释追加) +- `design/gdd/systems-index.md`(B4 状态行 In Review / A3 OQ5 注记迁移注记 / 门禁增补 B4 块 B1×6+D1×5 同步 / 新增"2026-09-09 B4 full 评审增补"块) +- `design/gdd/game-concept.md`(agency 定位句注记 / 创意风险 #1 口径注记 / 可证伪信号 MVP 口径注记) diff --git a/design/gdd/reviews/c2-adventure-structure-review-log.md b/design/gdd/reviews/c2-adventure-structure-review-log.md new file mode 100644 index 00000000..9e0ce41a --- /dev/null +++ b/design/gdd/reviews/c2-adventure-structure-review-log.md @@ -0,0 +1,64 @@ +# C2 冒险结构(c2-adventure-structure.md)评审日志 + +## Review — 2026-09-14 — Verdict: APPROVED + +- **Scope signal**: L(多系统集成:8 依赖 · F1–F3 三式 · AdventureTable 新表 · reseed 接线 · 2 项实现期 ADR 关联〔OQ3 种子生成源 / hero_override ADR-0009 候选〕) +- **Specialists**: 无——`--solo` 单会话快速审批,未生成专家代理;GDD 复核注记的"生产前人工复核"位保留 +- **Blocking items**: 0 | **Recommended**: 3 | **Nice-to-have**: 4 +- **门禁**: `python tools/gdd_lint.py design/gdd/c2-adventure-structure.md` → **PASS**(表格占比 86%;R9 最大节点直接内容合规);R8 人工复核——核心规则表要点列零编号引用(编号引用仅在依据列)✓;R3 人工复核——正文无日期修订注记(版本历史仅文末表)✓ +- **Prior verdict resolved**: First review + +### Summary + +C2 的承重裁决经逐项对源核实全部成立:F1 冒险通关率合成的跨关口径闭合前提在结构上成立(收束点清库 + 血回满 + 上限提升使每关开局状态恒为常量,逐关独立性成立);N=1/N=2 生效前提与 AC-10 SKIP 语义自洽可测;C1/C2 边界给 C1 的分支/精英/线性→分支演化留足空间;大奖励所有权三分清晰;失败重开语义与 game-concept 锁定一致;两处切片简化改写经切片代码核实与事实相符。零 blocking。并行上下文核实:/review-all-gdds(2026-09-14 FAIL)报告全文零 C2 提及,B3/B1/D2 状态与 C2 无涉。 + +### 已锁事实核对(七项全过,零冲突) + +| # | 锁定事实 | 核对结果 | +|---|---|---| +| 1 | C3 带锚 / OQ1 / level 999 / Boss=Dmax | ✓ F1 用带 = C3 F5 定稿带(L1 [30,50] / L2 [20,40],逐字一致);通关余量锚(p90≤13 & p10≥3)在 knob 交互行引用一致;OQ1 兑现批与 C3 OQ1"内容与数值同批——C2 关卡序列定稿后"双向咬合(C2 义务清单 = C3 填充批,(2, D1–D4) 四行 + 既有 (2, D0) 恰拼成链模板五战斗深度);越界 = 内容生产缺口、C2 不产生越界请求 + D2 R1 floor 兜底分层一致;Boss=Dmax 两 GDD 同锁 | +| 2 | B1 begin_battle / level_clear / 关卡结束归属 | ✓ 供给方 = C1 组装 / C2 供值(B1 依赖表 C1/C2 provisional 行 + hero_override_parameter_passing registry 条目,B1 侧转正登记随 B1 修订批);level_clear 上层显式调用、victory/defeat 皆然(B1 出口分发 + AC-31 腿 2 逐字一致);"关卡结束"归 C1/C2 域编排(B1/B4 双侧同口径) | +| 3 | B4 库存关卡域 / 大奖励不进库存域 | ✓ B4 Rule 4 关卡域 + 关卡末 clear_inventory(B1 编排调);C2 大奖励不进背包不占库存域 ↔ B4"关卡大奖励归 C2(B4 无关)" | +| 4 | A4 雾 unlock_level=2 | ✓ A4 硬约束/Core Rule 8/F1/Tuning Knobs 四处锁定;C2 仅作 L2 台阶内容面消费(序列难度排布) | +| 5 | game-concept 死亡即冒险终 / 20–40 分钟 | ✓ Rule 5 与术语表"死亡则冒险结束(Roguelike)"逐字一致;20–40 分钟在 OQ1 + Tuning Knobs 显式登记为愿景 vs MVP N=2 的已注册差距(扩展机制 = 内容批) | +| 6 | registry 四条目 + terminology 三词条 | ✓ rng_service(unseeded_access 致命 / snapshot_contract = master_seed + 各流 scope 计数器——三流与 C2 Rule 6 一致)/ level_table / adventure_table / hero_override_parameter_passing 全在位;HP 账本 / 关卡收束点 / 关卡大奖励三词条已登记且定义与 GDD 正文一致 | +| 7 | 硬门禁 | ✓ lint PASS;R8/R3 人工复核通过(见上) | + +### 重点压力测结论(六项) + +1. **F1 合成 [6,20]% 的口径闭合前提——成立,无漏项**。每关开局状态被结构钉死为常量:收束点清库(B1 level_clear,关卡域)+ buff/RAP 战斗域清(B1/A3)+ 血回满 + 上限提升(C2 账本)⇒ L2 结果分布与 L1 轨迹统计独立,乘法合成合法;逐关带内任意组合可达 ⇒ 区间积 [6%,20%] 即正确合成带。已登记的对冲:max_hp_eff=60(非裸 50)开局强度耦合经 knob 交互行("回满 + 上限共同抬开局强度——上调走 C3 重扫")+"若未来大奖励改部分恢复,本式失效须改口径"自守条款双保险。残余薄弱点见 Recommended-1(填充批起点态口径未成文)。 +2. **N=2 生效前提结构——自洽,AC-10 SKIP 可测**。N=1 合法态(切片现状)下 AC-7 平凡通过、AC-2/3 语义退化由 AC-10 显式 SKIP 承载;"禁止只加序列不加内容"由 AC-7 覆盖断言强制;与 C3"深度轴缺行 = 调参期冒烟断言拦截"双层一致。 +3. **C1/C2 边界——给 C1 留足空间**。契约四件套 + 收束点在 C1 + 组装权在 C1 不预判节点拓扑:rest/reward/battle 节点在分支图中语义不变(heal_clamped 逐节点触发)、精英节点走 C3 Rule 6 预留 + DifficultyTable 套乘(组装面零改动)、线性→分支演化唯一共享结构不变式 = Boss 恒末节点(C3 Rule 5① schema 同锁,非 C2 新增约束)。契约以"C1 GDD 强制输入 + 门禁评审核对项"登记,机制正确。 +4. **大奖励所有权——清晰**。机制/表回填 = C2;level_clear_hp_bonus 值锚 = C3 平移换算率 + 上调须 C3 重扫 L2 带;血回满 = 结构定式非 knob;token_per_level 值归 C4(消费方)默认 10 provisional。过渡期调参权措辞见 Nice-to-have。 +5. **失败重开语义——与锁一致**。死亡即冒险终(无关卡重试/检查点)逐字对齐 game-concept;重开 = 新种子/第 1 关/满血/空手;放弃同失败型、非战斗期受理、MVP 无 UI 入口(OQ4 归 D1)——不与任何锁定语义冲突。 +6. **切片切分转正——与切片事实相符(代码级核实)**。`level_runner.gd start_level(level_index, seed_val)` 内 `rng.new_adventure(seed_val)`(逐关播种)+ `hero_hp = effective_max_hp()`(每关回满)+ 注释自认"关卡起点 = 冒险起点(切片口径;跨关接续归 C2)"——C2 Rule 3/6 的两处改写(冒险单种子 / HP 跨关延续)准确描述了所替代的切片简化;level_runner 冒险域归 C2、关内行进归 C1 的切分与切片代码职责现状吻合。 + +### Dependency Graph + +- ✓ c3-difficulty-curve.md — 存在(Approved v2,带锚定稿) +- ✓ d2-data-config.md — 存在(Needs Revision——并行批欠账,C2 两条 D2 义务〔reseed 入口 / AdventureTable 建表〕已登记随其修订批) +- ✓ b1-settlement.md — 存在(Needs Revision——并行批欠账;C2 仅引用其结构面,begin_battle 转正登记随 B1 修订批) +- ✓ b2-enemy-system.md / b4-reward-item-economy.md / a4-terrain-variation.md / a3-action-economy.md / d1-hud-framework.md — 存在 +- ✗ c1-*.md — 不存在(**正确状态**:C2 在设计顺序上先于 C1,接口契约四件套即其强制输入基线,非 broken reference) +- ✗ c4 / d3 — 不存在(下游"远"位,正确框架化) + +### Required Before Implementation(blocking) + +无。 + +### Recommended Revisions(随 C2 下次修订批 / 对侧批次执行,非阻塞) + +1. **C3 L2 填充批的校准起点态显式化**(C2 → C3 义务行或 F1 处补一句):L2 通关率带校准的模拟起点 = 大奖励后状态(max_hp_eff = 50 + 10、满血、库存/buff/RAP 空),非裸 HeroTable 50/50——防填充批按裸表值校准使 [6,20]% 合成带口径漂移(跨评审教训:回执须按口径粒度登记,B1 V_safe 漂移同型)。 +2. **关卡末剩余道具折算结构位登记**:B4 OQ4 与 B1 AC-31 腿 2 双双指向"折算结构位归 C2(Alpha,转金币/继承候选,清空前快照计数为数据面预留)"——C2 义务面/OQ 零承接。补一行登记(owner C2/D5,Alpha),防"清单外登记会丢失"先例复发。 +3. **OQ1 时长估算与 game-concept 算术对齐**:单关 5–10 分钟 × N=2 = 10–20 分钟,OQ1 写"约 15–25 分钟"——补一句构成假设(含结算/摘要/大奖励节拍开销)或改区间,免读者当矛盾。 + +### Nice-to-Have + +4. token_per_level 补一句过渡期调参权:C4 定稿前 provisional 10 随 C2 表持有(现"值归 C4/消费方 C4"已含归属,仅过渡期主语可更明)。 +5. 速览"不拥有"行"关卡清库调用(B1)"的调用方链首次阅读易混(实链 = C1 收束点调 B1 level_clear 入口 → B1 编排调 B4 clear_inventory)——规则 7 已消歧,速览可括注"C1 发起"。 +6. AC 可选补腿:Settling 期收到放弃请求 → 同"Settling 恰一次"不变式拒绝(现由不变式 + AC-9 族覆盖,无显式 AC)。 +7. F1 可注明 [6,20]% 为 C3 基线(无天赋)档合成(公式本身通用,中/高天赋档代入即得)。 + +### 并行上下文核实 + +`gdd-cross-review-2026-09-14.md`(/review-all-gdds FAIL)全文经检索 **零 C2 提及**:2 blocking(B3/B1 减伤墙回执链、D2 schema 回写欠账)与 16 warnings 均不涉 C2;C2 引用 B1 仅结构面(level_clear / begin_battle)、引用 C3 仅带锚,不涉其数值回写——B3/B1/D2 的 Needs Revision 状态对本次评审结论无影响。C2 自身的对侧义务(B1 转正 / D2 建表 / C3 填充)均已登记、各有明确承载批次。 diff --git a/design/gdd/reviews/c3-difficulty-curve-review-log.md b/design/gdd/reviews/c3-difficulty-curve-review-log.md new file mode 100644 index 00000000..01370297 --- /dev/null +++ b/design/gdd/reviews/c3-difficulty-curve-review-log.md @@ -0,0 +1,46 @@ +# C3 难度曲线与精英/Boss — design-review 日志 + +## Review — 2026-09-14 — Verdict: NEEDS REVISION(8 blocking 同日修订核销)→ **用户拍板免复评放行:Approved** + +Scope signal: L +Specialists: 无(lean 单会话——用户指定不生成专家代理,出稿与评审同会话完成) +Blocking items: 8 | Recommended: 6 +Summary: 骨架承重结构(X-1~X-5 回执、encounter 流与 DifficultyTable registry 登记、A4/B1 授权链、P1 绝对锚)经逐项对上游核实零动摇;阻塞集中在三处——遭遇随机化的 owner 侧登记全面缺位(B1/B2/D2/registry)、F6 供给带被自身数值证伪(0.54 短缺态 vs 实测 60% 通关,方向反指)、现行行值无权威枚举且 v2/v3 两代数值混用。同日修订批按用户四项拍板全部核销:F6 由用户设计的生存链区间模型(S/K 区间三档判定 + 关级恒等式 + 占比带)替代,行值总表从切片 .tres 重建,四批切片既成事实(clear_blast 换位 / K_dmg=0 / 供给控制器 / armor_type)转正登记,接口漂移回写 B1×1/B2×3/D2×2/registry。 +Prior verdict resolved: First review + +### blocking → 修复对照表 + +| # | blocking | 修复落点 | +|---|---|---| +| 1 | 遭遇随机化 owner 侧登记缺位(unit_count_max 字段 / spawn override / B1 掷骰 / boss_guardian 物种集) | B1 战斗开始序列 + AC-5 scope 腿;B2 Rule 10/EC5/spawn 签名/AC-14/AC-16;D2 schema +2 字段;registry enemy 十键 + 三敌种 mvp_content | +| 2 | F6 平衡带被自身数值证伪(0.54 vs AC-4 带 [1.1,2.0]) | F6 供给带退役 → 生存链区间模型(用户设计,带锚定稿);AC-4 重写三档判定;AC-14 新增恒等式闭合;registry supply_balance_band 改写 | +| 3 | 现行行值无权威枚举 + v2/v3 混用(F3 Σ=142 vs F6 153.5;SLICE-TUNED 表 v2 值) | MVP 行值总表 v3 转正(切片 enemy.tres 权威源);F2/F3/F4/F6 验算全部重算;r_boss 更正 0.98 | +| 4 | registry boss_hp_anchor 旧带 [0.35,0.55] | 回写 [0.5,1.1] + 0.98 记录 + 走廊 [1.1,1.35] 预登记 | +| 5 | "clear_blast 40" 无出处(对 B1 C=8/16 口径) | 出处 = 切片 reward.tres(清图槽换位既成事实)——用户拍板采纳转正:B4 换位登记 + B1 条款⑤重推(缺口 6.1/51.8)+ registry clause5_dominance.c3_ruling | +| 6 | 供给控制器(active_p0/decay/首件保证)三处上游零登记 | 用户拍板归 B4 承接:B4 速览/Tuning Knobs/依赖行 + RewardConfigTable +2 参数 + pool_tag 增 active_inject + registry reward_config/settlement 侧登记 | +| 7 | F7 缺 B4"登记常数与对账重制"步 | F7 六步→七步(第④步) | +| 8 | F5 真人方向声明与 OQ3 证据相反 | F5 方向更正 + registry calibration_gap 改写(真人显著强于模拟者——批次 1 反向确认) | + +### 用户拍板(AskUserQuestion 四项,2026-09-14) + +1. 切片奖励侧三改动**全部采纳转正**(clear_blast 换位 / K_dmg 1→0 + 战斗首件保证 / 供给控制器归 B4)。 +2. **armor_type 克制批纳入 MVP**(goblin=none / bugbear=a / ogre_chief=b;克制矩阵入 SettlementTable.armor_effectiveness,数值 provisional 归 C3)。 +3. **三带锚定稿**:L1 基线通关率 [30%, 50%](替换 40–60%)· 通关余量 p90≤13 且 p10≥3("只剩一点血"量化)· L1 结构形态带(教学位绿 + 中段黄 + Boss 黄不红)。 +4. **加难杠杆批内即调**:候选 = r_boss 走廊 180–207 / 群战上探 / 供给 decay 0.6——OQ7 扫描待执行(本会话无 godot),落带前 v3 值 provisional-pending-scan;真人终标定归批次 2。 + +### 同批顺路核销(评审发现非 blocking) + +- DND 更名批对齐:grub→goblin、ambusher→bugbear、boss_guardian→ogre_chief——B1/B2/D2/registry 全文 sweep(D1 冲突② 落定,D1 GDD 侧收尾注记留其下次修订批)。 +- Boss "atk 高于均值一档" 无定义 → 重定义"atk ≥ 本关普通战最高行 atk"(现值 9≥9 ✓)。 +- F1 "Dmax 处 1.25–1.8×" 算术更正(Dmax=4 → 1.2–1.8)。 +- A3 无回执的 knob 声明改写"调参所有权归 A3,C3 联动评估";m_w 改"C3 提案通道"。 +- AC-1 跨表校验载体 = C3 调参期冒烟断言(D2 R1 fail-fast 不动);AC-11 改两腿(MVP 腿 + A4 升闸未来腿,X-10);AC-5 的 hp+100% 维度删除。 +- 减伤墙批(defense_up −50→−30、石肤 24→16——2026-09-13 B3 侧批)补录 C3 试玩批次记录。 + +### 登记在案 follow-up(非本批范围) + +1. **B1/B2/B4/D2 既有 R8 lint 债**——lint 工具本日更新(R8 核心规则表要点列禁编号引用)晚于四 GDD 批准时点;本次触改行零新增违例,存量清扫归各自下次修订批。 +2. B2 多敌夹具(bugbear 行)与 AC 记号(h_e/a_e)随 B2 实现批核验。 +3. OQ7 加难杠杆扫描执行(godot 可用时)。 +4. B4 F5 保底语义迁移后其 AC-6/AC-7 断言措辞随实现批对齐。 +5. registry entities.yaml 为宽松行解析惯例(非严格 YAML——既有 `**: ` 值风格),D2 AC-16 解析器实现时按行解析承载。 diff --git a/design/gdd/reviews/d1-hud-framework-review-log.md b/design/gdd/reviews/d1-hud-framework-review-log.md new file mode 100644 index 00000000..4384e637 --- /dev/null +++ b/design/gdd/reviews/d1-hud-framework-review-log.md @@ -0,0 +1,39 @@ +# D1 HUD 与界面框架——design-review 日志 + +> 目标文档:`design/gdd/d1-hud-framework.md` · 评审载体:/design-review(lean 模式) + +## Review — 2026-09-14 — Verdict: NEEDS REVISION(2 blocking 同日修订核销,用户拍板免复评放行 → Approved) + +Scope signal: L(16 声明依赖 / 13 项载荷 schema / 22 条核心规则 / 25+1 条 AC;无需新 ADR) +Specialists: none(lean 模式——用户指定快速审核单会话,未生成专家代理;主评审独立承担 systems/qa 视角核查) +Blocking items: 2 | Recommended: 3 +Summary: 承重结构经逐项对上游核实零动摇——27 项义务台账与 systems-index 登记吻合、13 项载荷 schema 与 B1 Rule 14 / A2 sub_reason 逐字一致、art-bible / battle.md / ADR-0001~0004 引用全部实存且口径一致。两条 blocking 均为文本级:F1 公式与 36/45 输出数学互斥(改两档定值 + 可行性校验式,用户裁决);B1/B2 呈现面 API 漂移未登记(冲突⑧/OQ3 扩条)。 +Prior verdict resolved: First review + +### Blocking → 修复对照(delta 复核清单) + +| # | blocking | 修复 | 核销 | +|---|---|---|---| +| 1 | **F1 公式与自声称输出/AC-2 矛盾(spec-AC 阻塞标准)**:按示例自带 pad=16/16 实算,min(744,460)=460 → 12×12 floor=38(非 36,示例等式本身不成立)、10×10 floor=46(超自声明 Output Range [30,45] 上界,AC-2 却断言 45);且 cell=36 要求可用高度 ∈[432,443]、cell=45 要求 ∈[450,459]——两区间不相交,**任何固定边距的闭式都无法同时得出两档** | F1 改**两档定值**(battle.md 拍板 36/45)+ **可行性校验式**(`floor(min(D_w−pad_h, D_h−ap_bar_h−pad_v)/N) ≥ cell(N)` 且 ∈[30,45],装板期断言 fail-fast)+ 区间不相交数学依据注记;AC-2 措辞同步(用户裁决方案 A:定值档位+校验式) | ✓ lint PASS | +| 2 | **呈现面 API 接口漂移未闭环**:Rule 3 / 依赖表称 B1 五函数(`presentation_snapshot` / `is_battle_live` / `stock_count` / `query_reward_identity` / `submit_window_selection`)为"B1 成文呈现面"、B2 两投影(`hero_unit` / `all_enemies`)——B1/B2 GDD 正文零命中;唯一成文载体 = registry `d1_presentation_surface`(owner=D1 自身,切片事实转正)。OQ3/冲突⑧原只登记 `settlement_snapshot` 信号,7 个函数漏登记(`is_battle_live` 为 Rule 18③ 唯一生命周期观测口,承重) | 冲突⑧/OQ3 扩条覆盖全部 7 函数 + 1 信号;Rule 3 与依赖表 B1/B2 行措辞改"切片实现、registry 登记、上游 GDD 契约登记待批";回 B1/B2 修订批补登记 API 面(时机 = B5 GDD 前) | ✓ | + +### Recommended(同批顺路核销) + +1. 状态头"上游:全部 Approved(…C3…)"失实——systems-index 记 C3 = Designed 未经评审 → 已更正(并注 D1 零玩法消费 C3)。 +2. C1-lite(level_runner)声明上游 hard 但 C1 GDD 未开始 → 依赖表注记"切片代码契约,C1 GDD 定稿时回填(接口漂移回 C1 批)"。 +3. Rule 22 可访问性子项(字号下限明细 ≥14/tooltip ≥13、无闪烁 >3 次/秒、减动效、纯鼠标)无 AC 覆盖(原仅 Rule 22→AC-23 CJK)→ 新增 **AC-26**,覆盖矩阵 Rule 22→AC-23/26。 + +### Nice-to-have(同批核销) + +- Rule 18③ "尸位"生造比喻 → 直白表述"死亡后仍留场的呈现"(用语纪律)。 +- 「RAP−8」文案模板 → 「RAP−{rap_refresh_cost 表读值}」占位(防实现照抄字面量——OQ2 降级文案同步)。 + +### 核查通过项(供未来 re-review 参考) + +- 27 项义务台账逐项与 systems-index 登记吻合(A2×6 / B2×4 / B3×4 / B4×5 / B1×3 / A4×3 + A3 + A1 EC5)。 +- 载荷 #4 公共七字段 = B1 Core Rule 14 逐字一致;A2 `sub_reason` 七值枚举一致;B3 Rule 6 六归因字段一致;B4 `query_inventory` / `usable_now` / E21 受理序一致。 +- art-bible:色值(#33261B 系 / 旗朱 #C63D22 / 暮蓝 #5A6C8C)、对比锚 6.9:1 / 5.3:1、禁入色相带 300°–345°、§4.6 键集(sapped 冻结未启用 / clear_blast 并列预留)全部一致。 +- battle.md:五区尺寸(28/252/240/~760/280、44px AP 条、84/24px 明细带)、能量档(2/10、7/10)、窗口锚点(授予段后)一致。 +- ADR-0001 订阅纪律(呈现层只订 B1/runner)、ADR-0002 canvas_items/expand 1280×800、ADR-0004 双载体 + gdUnit4 deferred 口径一致。 +- 三条切片教训硬性要求(Rule 17/18 + AC-19/20/24)齐备;冲突清单 ⑧ 项、OQ 12 项均有 owner/时机。 +- 遗留(非本 GDD 缺陷):C1 GDD 未开始(runner 契约由切片代码承载);`design/accessibility-requirements.md` 未建(正式分层待定稿——已在 OQ7 登记)。 diff --git a/design/gdd/reviews/d2-data-config-review-log.md b/design/gdd/reviews/d2-data-config-review-log.md new file mode 100644 index 00000000..15ad0f2a --- /dev/null +++ b/design/gdd/reviews/d2-data-config-review-log.md @@ -0,0 +1,45 @@ +# D2 数据配置与数值表 — 评审日志(Review Log) + +> 文档:`design/gdd/d2-data-config.md` · 本日志由 /design-review 流程维护 + +## Review — 2026-09-07 — Verdict: NEEDS REVISION(阻塞项已同日修订,待 re-review) +Scope signal: M +Specialists: game-designer · systems-designer · qa-lead · creative-director(senior,独立复核关键主张后裁定) +Blocking items: 5(簇) | Recommended: 15+ +Summary: 架构选择成立(schema 先行、单点注册表、封闭 RNG 流、fail-fast、黄金向量锚定,R2 六向量经独立复算全对),但四簇规约缺陷阻其兑现存在承诺:R2 按字面在 GDScript 不可书写且溢出论证错误、scope_counter 递增语义与 AC-3 恒等矛盾、续玩快照缺计数器;master_seed=INT64_MIN 经 abs() 与 0 静默别名(实测确认);fail-fast 的 AC-2 单进程自相矛盾、夹具注入路径未定、游戏内/release 致命通道零覆盖;难度公式第三维"精英/Boss 修饰"在两维行键体系无家 + Rule 4 未限定冒险内、行键合成未定义。另有方向级裁决:combat_var"结算浮动"流删除(与 P1"不靠 roll"定位相悖、无具名消费者,增流通道保留作 B1 未来出口)。 +Prior verdict resolved: First review + +### 同日修订落实(agent 代拟,用户 re-review 前 Status 保持 NEEDS REVISION) + +阻塞簇 → 修复对照: + +| 阻塞簇 | 修复 | +|---|---| +| 1. R2 规约不可实现/不自洽 | R2 改写为实现级规约(wrap64 回绕、禁 float/%、分段常量表达式);拆分纯函数 `derive_at` + 有状态门面 `open_stream`(scope 事件唯一绑定);D3 快照契约补各流计数器;"距溢出界极远"论证删除 | +| 2. INT64_MIN 静默别名 | EC6 补"恰为 INT64_MIN → 致命";abs 归一化保留(AC-6 兼容);registry 登记 | +| 3. fail-fast 验证通路 | 校验双通道规格(collect_only 游戏/测试两通道)+ `--data-root` 注入根;AC-2 改单进程测试通道断言;AC-2b 端到端致命路径;AC-18 release 通道 deferred 登记 | +| 4. Rule 4/表结构语义 | Rule 4 限定"冒险内"+ 键合成规则 + 10 表键结构列 + 精英/Boss 修饰预留 `elite_`/`boss_` 命名空间(语义归 C3);AC-5 改用真实 schema、AC-13 补键契约断言 | +| 5. combat_var 流 | 删除(Rule 5 改 2 流;R2 盐/向量、Interactions、Dependencies、registry 同步);R4 增"玩法随机走增流通道 + P1 评审"条款 | + +必修/建议项同步落实:R1 去重键改四元组 + missing_type 枚举补 both/base_ceil + 删"供 C3 审计"改开发期信号 + 曲线导出工具登记 C3 期交付(OQ5);depth-0 锚行校验(EC15/Rule 6);Edge Cases 全量编号 EC1–EC16;夹具补重复键/缺锚行/未播种(AC-17)/悬空引用条件性登记;reward_roll 黄金向量 7422916049646135537(经独立复算);R1 越域守卫(EC14);override key=字段点路径 + 未知 key 告警;States 增 reload-Failed 分支 + RNG 状态线独立声明;碰撞声明改"同冒险内无碰撞";"全部 20 系统"改"其他 19 系统(A2 间接)"+ C2←D2 边登记待索引显式化;占位参考值补溯源分级(出处定稿/待查/机制待定)+ 定稿删除义务;AC-13 自动化、AC-8 收窄、AC-14 三要素、AC-4 观测规格、AC-16 门标志归 B1、AC-1 去硬编码数量。 + +未落(留待用户/后续): +- validation_strictness 陷阱旋钮去留(nit,未动) +- systems-index 依赖表补 C2←D2 显式边(属索引文件,仅改了状态行) +- 占位值"出处待查"三项(伏击 +3 / 清图 +10 / 敌 18HP/4ATK)需查原型 REPORT 补溯源 + +## Re-Review — 2026-09-07 — Verdict: NEEDS REVISION(5 阻塞同日修订落实,待 delta 复核) +Scope signal: M +Specialists: game-designer(APPROVED)· systems-designer(NEEDS REVISION)· qa-lead(NEEDS REVISION)· creative-director(senior,2:1 终裁:NEEDS REVISION·末轮小修) +Blocking items: 5 | Recommended: 8 | Nice-to-have: 6 +Summary: 前轮 5 簇中簇 1/2/5 完全闭合(R2 五条黄金向量经主评审+systems-designer+qa-lead 三方独立复算逐位吻合;INT64_MIN 别名经数学确认;combat_var 全文无残留),簇 3/4 部分闭合。creative-director 终裁:规约-vs-AC 同输入矛盾与 AC 虚假覆盖声明延续上轮簇 1 的阻塞标准,不因修复体量小豁免,亦不升级 MAJOR(零架构返工)。5 项新阻塞(已全部按终裁清单修订):① R1 missing_type 分类规则缺失且与 AC-5 同输入矛盾;② EC14 零 AC 覆盖 + AC-2b"主场景"不可行 + AC 前言覆盖声明不实;③ warning 通道在 headless 下不可观测(增诊断收集器 + reset_diagnostics + 去重注册表复位语义);④ 实体键复合解析语义空白(进度键 floor、实体键精确匹配、缺失即致命);⑤ Rule 6 三处自相矛盾(加载期/运行期二分、EC15 写死开非 knob)。advisory 同步收编:reward_roll 绑定 provisional flag(P1 论证归 B4,创意风险 #1)、负 scope/重播种守卫、R3 范围校验 + v1 仅单行表 + 可注入谓词、elite_/boss_ C1 逃逸条款与 AC-13 断言、快照无在飞实例前提、索引补 C2/B1/B4/C4←D2 四边(同日落实)、曲线导出工具排期倒挂登记(OQ5)。 +Prior verdict resolved: Partial — 簇 1/2/5 RESOLVED;簇 3/4 PARTIAL(残留即本轮 5 阻塞) +日志勘误:上轮"夹具补未播种"表述不准——未播种为行为级覆盖(AC-17),非 AC-2 夹具;本轮 AC 前言已按加载期/运行期分类如实改写。 +Delta 复核清单(下轮只需核对):① R1 分类规则 + AC-5 推导一致性;② AC-5b/AC-2b/致命协议/前言声明;③ 诊断收集器规格与 AC-5/6/7 改写;④ Rule 4 解析语义;⑤ Rule 6/EC15/Tuning Knobs 一致性。 + +## Delta 复核 — 2026-09-07 — Verdict: APPROVED(D2 收口) +Scope signal: M +Specialists: 独立 delta 复核员(1 名子 agent,按上轮"Delta 复核清单"逐项核对) +Blocking items: 0 | Recommended: 0(遗留 advisory 不阻塞下游,归后续 owner GDD/索引) +Summary: 5 项阻塞修复逐一核对 PASS——① R1 分类规则可独立推导 AC-5 全部期望值(both/base_ceil/去重键均自洽);② AC-5b/AC-2b/致命断言协议统一且前言覆盖声明如实;③ 诊断收集器规格落地,AC-5/6/7 断言改经收集器;④ Rule 4 实体键精确匹配语义落地;⑤ Rule 6 加载期/运行期二分,与 EC15/Tuning Knobs/States 表口径一致。残留扫描 0 命中。D2 → Approved,下一系统:A3 行动经济(设计顺序第 2 位)。 +Prior verdict resolved: Yes(re-review 5 阻塞全部闭合) diff --git a/design/gdd/reviews/d2-data-config.pre-revision-2026-09-07.md b/design/gdd/reviews/d2-data-config.pre-revision-2026-09-07.md new file mode 100644 index 00000000..4e4079ee --- /dev/null +++ b/design/gdd/reviews/d2-data-config.pre-revision-2026-09-07.md @@ -0,0 +1,232 @@ +# D2 数据配置与数值表(Data Config & Balance Tables) + +> **Status**: Designed(待用户统一评审 + 独立设计评审) +> **Author**: user + agents(systems-designer、qa-lead 已按 lean 门禁咨询) +> **Last Updated**: 2026-09-07 +> **Implements Pillar**: P1、P2(基础设施层,间接支撑全部四支柱) +> **Layer**: 1 Foundation · **Milestone**: MVP · **设计顺序**: 第 1 位 +> **Creative Director Review (CD-GDD-ALIGN)**: 跳过——Lean mode(生产前请人工复核) + +## Overview + +D2 是游戏的数据底座:把所有影响玩法手感与平衡的数字——行动点、旗子数、奖励数值、敌人属性、成长曲线、变异参数——从代码中搬出,变成可配置、可校验的数值表与配置资源;同时提供两样基础设施:统一的"数值从哪读"注册表(各系统只向 D2 要数据,不再各自硬编码),以及随机数种子服务(RNG 由 D2 统一发放,是战斗可复现、可回放的前提)。 + +玩家不会直接"使用"D2,但每一局都在感受它:难度能否做到"越下越难",靠它第一天就内置的关卡序号 / 节点深度两个维度;每次平衡调整无需改代码即可生效,让"测试数据驱动平衡"成为本作的开发方式——这是个人开发在资产与人力受限下仍能保证手感稳定的底牌。 + +D2 遵循既定的"**schema 先行、数值回填**"策略:本 GDD 只定数据结构、注册表与校验模式,具体数值留空位,由后续系统 GDD(A3 行动经济、B4 奖励经济、C3 难度曲线等)按设计顺序回填。没有 D2,参数将散落各处硬编码:调一次难度要改五处代码,战斗无法用固定种子复现,headless 自动化测试无从谈起。 + +## Player Fantasy + +D2 没有专属的玩家幻想——它是玩家永远不该注意到的地基。正面的情绪目标由其余系统承载:P1"我算对了"的掌控感来自 A1/A2 的可推理设计,P2 的延迟兑现感来自 B1 的可见结算。D2 对体验的贡献是三种**消极保障**——它不制造情绪,但它的失职会直接摧毁这些情绪: + +- **手感稳定**:所有影响手感的数字(AP、旗子、折算比例、奖励值)集中一处可调。任何"这轮玩起来不对劲"都能不改代码地快速修正、反复验证——玩家感受到的是"每次迭代都更顺",而不是改动引发的新毛刺。 +- **公平可信**:RNG 统一种子化,相同的决策序列必然得到相同的战斗结果。"我明明算对了却输了"永远不应源于不可复现的随机——这是 P1 的信任底线。 +- **难度可信**:数值表天生带关卡序号/节点深度维度,"越下越险"(P4)由结构化数据保证,而非散落各处的魔法数字——难度曲线因此可审计、可回溯。 + +## Detailed Design + +### Core Rules + +1. **单一事实源**:所有设计师可调的玩法数值只存在于 `data/tables/` 下的 `.tres` 资源;代码中禁止出现玩法数值字面量(引擎/工程常量除外)。违反视为评审阻塞项。 +2. **单点访问**:各系统经 autoload **`ConfigDB`** 的类型化属性读取(例:`ConfigDB.action_economy.ap_per_round`);禁止绕过 ConfigDB 自行 `load()` 表文件或长期缓存副本(保证校验单点与热重载一致性)。 +3. **Schema 先行、数值回填**:每个数值表 = 一个自定义 Resource 类(`data/schemas/`,字段带类型/范围/默认值/注释)+ 数据文件(`data/tables/`)。本 GDD 交付全部 schema 与占位表;数值由 owner 系统 GDD 回填: + +| 表资源 | Schema 占位内容 | Owner(回填 GDD,设计顺序位) | +|---|---|---| +| ActionEconomyTable | AP/旗/折算/清图字段名 | A3(第 2 位) | +| VariationTable | 变异类型枚举 + **biome/tag 数据字段**(AD 门禁) | A4(第 3 位) | +| StatusEffectTable | buff 时限/叠层字段名 | B3(第 4 位) | +| BoardGenTable | 布雷数/可推理率参数/尺寸字段名(P1 生成端保障) | A1(第 5 位) | +| EnemyTable | 敌人属性/词条/意图可见性字段名 | B2(第 7 位) | +| RewardTable | 奖励池权重 + **"使用时机"字段占位** | B4(第 8 位) | +| SettlementTable | 结算顺序/来源标注字段名(P2 归因) | B1(第 9 位) | +| DifficultyTable | (level_index, node_depth) 行结构、成长参数 | C3(第 12 位) | +| HelperItemTable | 探测/安全旗参数 | A5(第 11 位) | +| MetaTable | 代币/天赋字段名 | C4(第 16 位) | + +4. **进度维度内建**:凡随进程成长的表,行键必须是 `(level_index, node_depth)`;无成长语义的全局常量表允许单行。解析规则见 Formulas-R1。 +5. **RNG 单点**:autoload **`RngService`**(D2 范围)持有封闭命名流:`board_gen`(A1 布雷)、`reward_roll`(B4 掉落)、`combat_var`(B1 结算浮动)。玩法代码禁止 `randomize()` 或自建 RandomNumberGenerator。master seed 在冒险开始时由 C2 调用设定。 +6. **失败即响(fail fast)**:校验错误(字段越界/类型错/表为空或某 level 无行/重复键/悬空引用/权重和 ≤0/未播种流取数)在 editor、CI、headless、release 一律致命中止。MVP 有意不做软回退——错误数值上线比崩溃更伤(P1/P2 的信任底线)。 +7. **零 UI 依赖**:D2 及其测试不 import 任何 Control/场景类;"Core 层 `--headless` 跑完整战斗"工程债在数据侧的落实。 +8. **版本化**:`SCHEMA_VERSION` 常量归 D2;表文件与存档(D3)携带版本号,高版本拒读。 + +### States and Transitions + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Unloaded | 进程启动前 | 磁盘 `.tres` 未加载 | +| Loading | `ConfigDB._ready()` | 按表清单逐一 `load()`;文件缺失即入 Failed | +| Validating | 单个资源加载成功 | 调 schema 的 `validate()`,错误入收集器 | +| Ready | 全部通过 | 类型化属性可读;RngService 流就绪 | +| Failed | 任一致命错误 | 全构建致命中止并输出错误清单(Rule 6) | +| Reloading(仅 debug) | 编辑器热重载/调试键 | 战斗中拒绝;否则重走 Loading → Ready | + +RNG 生命周期:进程启动 → 流待命(**未播种时取数即致命**)→ 冒险开始设定 master_seed → 每场战斗派生 scope 种子 → 冒险结束保留供复盘。 + +### Interactions with Other Systems + +| 系统 | 方向 | 接口契约 | +|---|---|---| +| 全部 20 系统 | 它们 → D2 | ConfigDB 类型化读取;禁止其他取数路径 | +| A1 | 消费 | VariationTable schema(biome/tag 字段)+ `board_gen` 流 | +| A3 / A4 / B2 / B3 / A5 / D1 | 消费 | 各自表 schema 占位(见 Core Rules 表) | +| B4 | 消费 | RewardTable schema(含"使用时机"占位)+ `reward_roll` 流 | +| B1 | 消费 | SettlementTable schema + `combat_var` 流(可回放步骤流的数据前提) | +| C2 / C3 | 消费 | `RngService.set_master_seed()` + DifficultyTable 行解析 | +| D3 | 消费 | `SCHEMA_VERSION` + 存档内 master_seed 快照(续玩/复盘) | +| D2 外发 | → | 不订阅任何玩法事件;仅 debug 构建发 `config_reloaded`(D1 调试用) | + +双向一致性:systems-index 依赖表中以 D2 为依赖方的系统(A1、B2、C3 及 Foundation 层全员的事实依赖)与本节一致;D2 不依赖任何玩法系统(依赖层 1:无)。 + +## Formulas + +> systems-designer 代理已复核(lean 门禁);其指出的 scope 重置撞种子缺陷与 hash() 稳定性问题已修正并入。 + +### R1 难度行解析(difficulty_row_resolution) + +`row = resolve(table, level_index, node_depth)` + +- `L* = max{ lv ∈ LevelKeys(table) | lv ≤ level_index }`;空集 → `L* = min LevelKeys(table)` +- `D* = max{ dp ∈ DepthKeys(L*) | dp ≤ node_depth }`;空集 → `D* = min DepthKeys(L*)` +- 非精确命中(缺失类型 `level_floor` / `depth_floor`)记 warning:**去重键 = table_id**(每表每次运行至多一条),内容含请求 (level_index, node_depth)、解析结果 (L*, D*)、缺失类型——供 C3 审计难度曲线断档 +- **不存在 default 行兜底、不存在 clamp 外推**:表非空且每个 level 至少 1 行是 Rule 6 校验前置;超表成长外推是 C3 owner 公式的职责,D2 只解析到最近已有行 + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| level_index | int | ≥1 | 当前关卡序号(1 起) | +| node_depth | int | ≥0 | 节点深度(0=关卡入口) | +| L* / D* | int | 表内键 | 命中行键 | + +**Output Range:** 行资源必有值(前置条件由校验保证);极端输入(超表大关卡号)命中最大已有行。 +**Example:** 表含行 (1,0) (1,4) (2,0) (2,6);`resolve(t, 3, 3)` → L*=2(level_floor)、D*=0(depth_floor)→ 返回 (2,0) 行,warning 一条(去重后)。 + +### R2 流种子派生(stream_seed_derivation) + +`stream_seed = ( SALT_s XOR ((master_seed + scope_counter × 0x9E3779B97F4A7C15) MOD 2^64) ) AND 0x7FFFFFFFFFFFFFFF` + +**Variables:** + +| 变量 | 类型 | 范围 | 说明 | +|---|---|---|---| +| SALT_s | int64 | 编译期常量 | board_gen=0xA0761D6478BD642F · reward_roll=0xE7037ED1A0B428DB · combat_var=0x8EBC6AF09C88C6E9(互异奇常数,均 ≠ 乘数 0x9E3779B97F4A7C15) | +| master_seed | int64 | [0, 2^63) | 冒险主种子;`set_master_seed()` 对负输入 abs() 归一化并 warning | +| scope_counter | int64 | ≥0 | **每流独立的全局单调计数器**:set_master_seed() 时归零,此后每次派生 +1,永不重置(不按关卡/图序重置——否则不同关卡的同序号图会得到相同种子) | + +**设计约束与边界:** +- 不用运行时 `hash()`——Godot 文档不承诺其跨版本稳定,而跨版本可复现是 P1 信任底线;流集合封闭(Rule 5),编译期盐即可 +- 取数前置:流未播种(master_seed 未设)即 `push_error` + 中止(Rule 6 同级),禁止 seed=0 之类的静默回退 +- 输出经 AND 掩码保证非负后再赋 `RandomNumberGenerator.seed`;GDScript int 为 64 位,scope 实际规模(<10^6)距溢出界极远,无需额外防护 + +**Output Range:** [0, 2^63),任意两次不同输入(盐/主种子/scope 任一不同)输出完全不同;同输入恒同输出。 +**Example:** master_seed=42 时 board_gen 流前 4 次派生(scope 0–3)= 2339089373825164293 · 4485977604321908752 · 2024629813010603131 · 8849696722413293638;同 scope=3 下 combat_var = 6060164893835244160;master_seed=43 的 board_gen scope=3 = 8849696722413293637。 + +### R3 调试覆盖链(debug_override,仅 debug 构建) + +`effective = override[key] if _has_override(key) else table_value` + +边界四条:① 覆盖判定与存储内聚于 ConfigDB(Rule 2 单点),gameplay 不可见 override 来源;② override 值类型必须与 schema 字段一致,不一致 → debug warning + 忽略该条;③ release 构建不加载、不注册任何 override 源(恒等是结构保证而非"碰巧不生效");④ override 仅为运行时状态,永不回写 `.tres`。 + +### R4 显式排除 + +玩法平衡公式(伤害、掉落权重、成长曲线)**不在 D2**——归 owner 系统 GDD(B1/B4/C3 等)。本 GDD 不含任何平衡数值公式,只负责取数与解析。 + +## Edge Cases + +- **If 表文件缺失或 load() 失败**:致命中止,错误清单含路径;无软回退(Rule 6)。 +- **If 字段越界 / NaN / 类型不符**:`validate()` 收集后致命。 +- **If 表为空或某 level 行集为空**:校验阶段致命(R1 前置条件),不会进入运行期。 +- **If 请求 (level_index, node_depth) 无精确行**:按 R1 floor 解析 + warning(去重键 table_id)——预期行为,不是错误。 +- **If level_index 超过表最大 level**:命中最大 level 行(floor 规则的自然结果,无独立 clamp 分支)。 +- **If master_seed 为负**:`abs()` 归一化 + warning,保证 ∈ [0, 2^63)。 +- **If 未设 master_seed 就取随机数**:`push_error` 后**显式中止**(GDScript 的 push_error 本身不终止进程——游戏内走致命错误路径,冒烟脚本 `quit(1)`),取到的值不可用——这是 C2 未接线的显式暴露,禁止 seed=0 软回退。 +- **If 战斗进行中请求热重载**:拒绝并返回 false,提示"下一场生效"。 +- **If 存档携带的 SCHEMA_VERSION 高于当前**:拒绝读取(比较逻辑归 D3,常量归 D2)。 +- **If 奖励权重表权重和 ≤ 0**:validate 致命(B4 回填后必须 > 0)。 +- **If debug override 类型与 schema 字段不符**:warning + 忽略该条(仅 debug;release 无 override 源)。 +- **If 同一流名被重复注册或请求未知名流**:debug 断言中止(流名封闭)。 +- **If 两个系统同帧请求同一流**:允许——计数器各自 +1 按调用顺序串行生效,单线程假设成立(Godot 主线程玩法逻辑),不引入锁。 + +## Dependencies + +**Upstream(hard)**:无。Foundation 层,D2 不依赖任何系统。 + +**Downstream**: + +| 依赖方 | 性质 | 接口 | +|---|---|---| +| 全部 20 系统 | hard(Rule 1 单一事实源) | ConfigDB 类型化读取 | +| A1 | hard | VariationTable schema(biome/tag 字段)+ `board_gen` 流 | +| A3 | hard | ActionEconomyTable schema 回填 | +| A4 | hard | VariationTable 回填 | +| B2 | hard | EnemyTable 回填 | +| B3 | hard | StatusEffectTable 回填 | +| B4 | hard | RewardTable 回填(含"使用时机"占位)+ `reward_roll` 流 | +| B1 | hard | SettlementTable 回填 + `combat_var` 流 | +| C2 | hard | `set_master_seed()` 调用方(冒险开始) | +| C3 | hard | DifficultyTable 回填 + R1 行解析审计 | +| A5 / C4 / D3 | hard(垂直切片 / Alpha) | HelperItemTable / MetaTable 回填;D3 另消费 SCHEMA_VERSION + master_seed 快照 | +| D1 / D4 / D5 / D6 | soft | 如需参数表再增 schema(现无占位) | + +- A2 无直接依赖(只外发事件;其参数经 A3 表承载),与 systems-index 的 A2 回边规则一致。 +- **接口漂移修订规则**:owner GDD 定稿时可以修改占位 schema 类(schema 与表同仓),但必须回写本 GDD 的 Core Rules 表清单与 `design/registry/entities.yaml`,并在本文件 Status 行登记修订。 +- 双向一致性:与 systems-index 依赖分层表核对一致(A1←A4,D2 · B2←D2 · C3←D2,B2 · Foundation 含 D2)。已知待定:C3 是否消费 B1 输出(见索引门禁增补记录,C3 GDD 定稿,不影响 D2)。 + +## Tuning Knobs + +**D2 自身 knobs(元参数):** + +| Knob | 默认 | 调过高 | 调过低 | +|---|---|---|---| +| validation_strictness | fatal | ——(fatal 即上限) | warn:放行带病数值,CI 冒烟失去意义 | +| floor 解析策略 | floor + warning(无外推) | —— | 引入外推/静默会掩盖 C3 难度断档 | +| rng_stream_names | 封闭 3 流 | 增流需改代码(有意设卡) | 流复用破坏可复现隔离 | +| debug_override_enabled | false(仅 debug 构建) | 污染测试基线 | —— | +| 表路径清单 | `data/tables/` 固定 | —— | 散装路径破坏单点访问 | + +- **Knob 交互**:validation_strictness=warn 将使冒烟测试(AC-1)失效,两者必须联动改;floor 策略与 C3 的难度审计共享语义,改动需 C3 GDD 知情。 +- **占位表中的原型参考值**(provisional——owner GDD 定稿前不视为平衡事实):AP 8/轮 · 旗 4/轮 · 伏击 +3/层 · 标中 +5/面 · 停手折算 ×2 · 清图 +10 与剩余 ×1 · 增伤 1.5× / 减伤 0.5× / 持续 2 次结算 · 敌 18HP / 4ATK · 胜场成长 +25% HP / +1 ATK · 奖励池 5 类(伤害/增伤/减伤/AOE/治疗)。owner:A3 / A4 / B3 / B4 / B1 / C3 按设计顺序回填。 + +## Visual/Audio Requirements + +无直接表现面(纯数据层)。仅两项开发侧"可视化":Godot 编辑器 Resource inspector(自带)与校验错误控制台输出(错误清单格式见 AC-1)。无音频需求。 + +## UI Requirements + +MVP 无面向玩家的 UI。debug override 经 Godot 编辑器 / 命令行参数设置,不做自定义面板;校验失败在 editor/CI 输出控制台,release 构建走引擎默认错误路径。 + +## Acceptance Criteria + +> qa-lead 代理复核(lean 门禁):初稿 8 条仅覆盖 5/8 核心规则、5/13 边界,战斗重放条目在 D2 阶段不可测(无战斗载体)——以下为补齐与降级后的清单。除标注"人工/deferred"外全部可纳入 `--headless --script` 冒烟框架。 + +**加载与校验(Rule 3/6)** +- **AC-1** GIVEN 干净检出,WHEN `godot --headless --script tests/smoke_data.gd`,THEN Core Rules-3 表清单的 10 个表全部加载且 `data/tables/` 无清单外 .tres;脚本成功 `quit(0)`,任一失败 `quit(1)` 并输出错误清单(表路径 + 字段 + 错误类别)。 +- **AC-2** GIVEN `tests/fixtures/` 夹具集(越界字段 / 文件缺失 / 空表 / 某 level 无行 / 权重和 ≤0 各一),WHEN 冒烟脚本逐夹具校验,THEN 每个均致命中止(quit 非 0)且错误清单含对应类别(EC1/EC2/EC3/EC10)。 + +**公式(R1/R2/R3)** +- **AC-3** GIVEN R2 黄金向量,WHEN master_seed=42 派生 board_gen scope 0–3 与 combat_var scope=3、master_seed=43 派生 board_gen scope=3,THEN 6 个结果逐个等于 Formulas-R2 Example 常量;同流同 scope 重复派生恒等。 +- **AC-4** GIVEN master_seed=42,WHEN board_gen 跨模拟"关卡切换"连续派生,THEN scope_counter 单调不重置(各关同序号图种子互异)。 +- **AC-5** GIVEN 表含行 (1,0)(1,4)(2,0)(2,6),WHEN `resolve(3,3)`,THEN 返回 (2,0) 行且 warning 恰一条(含请求/结果/缺失类型);WHEN 同表再 `resolve(3,4)`,THEN 不再发新 warning(去重键 table_id);WHEN `resolve(99,99)`,THEN 命中最大已有行(EC5)。 +- **AC-6** GIVEN master_seed=-7,WHEN `set_master_seed()`,THEN 归一化为 7 + warning,后续种子 ∈ [0, 2^63)(EC6)。 +- **AC-7** GIVEN debug 构建 override{ap=99},WHEN 经 ConfigDB 读取,THEN effective=99;GIVEN override 类型与 schema 不符,THEN warning + 忽略该条;GIVEN `reload()` 后,THEN .tres 文件内容不变(R3 边界①②④)。 +- **AC-8** GIVEN 非 debug 构建(feature 标签模拟),WHEN `_has_override(key)`,THEN 恒 false(R3 边界③;release 模板构建验证为半自动)。 + +**静态与架构(Rule 1/2/5/7/8)** +- **AC-9** GIVEN D2 文件集(data/schemas、ConfigDB、RngService、tests),WHEN 静态扫描,THEN 无 `randomize()` 调用、无 ConfigDB 之外的 `load("res://data/…")`(Rule 2/5)。 +- **AC-10** 同扫描无 `extends Control/Node2D`、无 `.tscn` preload,**且** AC-1 headless 实跑通过——两项合成判定 Rule 7。 +- **AC-11** WHEN 请求未知名流或重复注册流名,THEN debug 断言中止(EC12)。 +- **AC-12** GIVEN 同帧对同一流连续两次取数,THEN 两个互异且顺序确定的种子(EC13)。 +- **AC-13** WHEN 代码检查,THEN `SCHEMA_VERSION` 常量存在且 ConfigDB 可读、10 个 schema 均携带版本字段(Rule 8;"高版本拒读"路径 deferred→D3)。 + +**人工与延迟项** +- **AC-14(人工)** Rule 1 数值字面量禁令:启发式扫描 + GDD 评审人工复核(无法完全自动化)。 +- **AC-15(deferred→B1)** 战斗级可回放:同 master_seed 同动作序列 → 结算步骤流逐行相同(B1 集成测试;D2 阶段无战斗载体)。 +- **AC-16** GIVEN debug 构建且 reload 门标志置位(模拟战斗中),WHEN `reload()`,THEN 返回 false、ConfigDB 值不变、不发 `config_reloaded`;GIVEN 门未置位,WHEN `reload()`,THEN 返回 true 且新值经类型化属性可读。 + +## Open Questions + +1. **headless 测试载体终稿**:MVP 用 `--headless --script` 冒烟(本 GDD 决策);gdUnit4 是否在 Technical Setup 引入并迁移冒烟用例——owner:Technical Setup 阶段(systems-index 门禁增补记录项)。 +2. 编辑器内表文件热重载自动化(当前仅 debug 键触发;低优先)。 +3. 数值表显示名的本地化——D6 归口。 +4. C4 MetaTable 并入 ConfigDB 还是独立 autoload——C4 GDD 定(倾向并入)。 diff --git a/design/gdd/reviews/proposal-reward-polarity-2026-09-13-review-log.md b/design/gdd/reviews/proposal-reward-polarity-2026-09-13-review-log.md new file mode 100644 index 00000000..f41e3440 --- /dev/null +++ b/design/gdd/reviews/proposal-reward-polarity-2026-09-13-review-log.md @@ -0,0 +1,23 @@ +# Review Log — proposal-reward-polarity-2026-09-13 + +## Review — 2026-09-13 — Verdict: NEEDS REVISION +Scope signal: L +Specialists: lean 模式(用户要求跳过多专家;主会话独立分析。触发源、上游 GDD〔B4/B1/B3/A2/A3/C3〕、registry、切片数据表、playtest 报告均已通读) +Blocking items: 5 | Recommended: 7 +Summary: 核心概念成立——复用 B4 install 期预分配脊柱、零新增效果行(直伤形态除外)、徽章栈位已预留、预注册指标、批次拆分合理;承重方向无动摇。全部 blocking 为规格级缺口:①类型↔奖励行映射未定义(可选类/注入格/roll 程序)②A3 F1 条款②③⑤契约重议义务未列入 delta ③R6"惩罚必须落地"与 B3 满层拒绝/B2 cap=4 冲突且越层语义未定("2 层封顶"与数据 max_stack=16 矛盾)④授予段直伤致死的段序裁定缺(评审侧曾误读为"挖死"——用户纠正:扫雷阶段零数值计算,直伤在结算段授予段落地,原则本身无违背)⑤registry/D2/game-concept/game-pillars 回写义务未枚举。Recommended 首项:数据基线声明(提案的"既有结构"实为 2026-09-13 切片数据批,B3/B1 GDD 与 registry 未吸收——B3 四行 vs 数据六行、"标错惩罚未上线" vs 已上线)。 +Prior verdict resolved: First review + +## 同日修订(2026-09-13,用户拍板两项:直伤结算段落地原则确认 + 负向封顶 = 池 clamp 饱和) +Blocking ① → R1 增类型 = 行家族属性(cell_type 列、单段 roll 结构不动、注入格同映射);② → §3 A3 增 F1 三条款重推义务;③ → R6 改池 clamp 饱和 + B2 cap 显式例外 + 满层拒绝结构不可达论证;④ → §3 B1 增授予段致死短路裁定 + 架构对齐注记(全兑付均在结算段);⑤ → 新增 §3.5 跨文档回写清单。Recommended 项随批:基线声明入头部、OQ-5 撤销改既定事实引用、触发源引用修正、雾格标错中性池兜底、OQ-6 探旗反滥用登记、类型先验泄漏显式拍板、指标 5 增补。 + +## OQ 全闭合定稿(2026-09-13,用户拍板 → 提案 v3,已拍板待实施) +- OQ-1:继承泛化(踩雷统一走类型负向路由、伏击为负向表一行)——用户批内自裁。 +- OQ-2:标中雷正向取代固定 weaken 赠予;A3 条款②锚对象改"标中雷类型正向当量下界"随批重议。 +- OQ-3:weaken 负向 = **sapped 乏力(反噬镜像)**——我方输出 −25% 进 hero_offense 池(attack_up 镜像,B3 新数据行、机制零改动);否决"敌方 weaken 抵免"(破 B3 Rule 8 负空间)与"暂缓复用 enrage"两候选。 +- OQ-4:雾格图标"?"中性标——预制裁决(雾回归时适用)。 +- OQ-6:雾格内容态封禁(board_gen fog_region_count=0,A4 结构不动、雾回归走重设计批)+ 反滥用机制不引入,指标 5 观测兜底。 +- 随批:§0 基线行更新(B3 七行口径)、§3 B3 数据行 +1、§3.5 registry/D2/B3 清单同步(七行 + sapped→hero_offense 映射)、批 1 增雾封禁内容态调整、§4 指标 5 改观测兜底口径。用户明示免复审,本日志为终态记录。 + +## 终审清雾(2026-09-13,用户指示:提案文本零雾格内容) +用户复核指示"现在不需要有任何和雾格有关的内容"——提案 v3 终稿清除全部雾格残留五处:§3 D1 雾格块(OQ-4 预制裁决 + 雾格标错兜底)、§3.5 systems-index 义务清单两条雾条目、OQ-4 条目整体删除、OQ-6 改写为无雾版(探旗反滥用机制不引入、指标 5 观测兜底——裁决实质保留)、批 1 雾封禁注记。grep 复核零命中。 +**雾格封禁行动的去向(记录义务)**:极性验证期雾格内容态封禁(board_gen 内容行 fog_region_count=0 / variation 池不出 fog 行——A4 结构与 GDD 不动)不在本提案文本承载,落点 = 切片内容/配置批(A1 board_gen 行值 / A4 变异池,C3 内容决策域);雾回归时走重设计批(届时雾×极性交互——雾内类型先验、雾格图标呈现、雾格标错归因——重新设计,本日志上方 OQ-4/OQ-6 原裁决文本仅作历史参考,不再作为预制条款)。 diff --git a/design/gdd/systems-index.md b/design/gdd/systems-index.md new file mode 100644 index 00000000..97dc25c2 --- /dev/null +++ b/design/gdd/systems-index.md @@ -0,0 +1,380 @@ +# 系统索引(Systems Index) + +> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike) +> 生成:2026-09-06 · 分解来源:`design/gdd/game-concept.md`(概念定稿)+ `docs/prototype-report-minesweeper-battle-concept.md`(原型 v3 验证结论;demo 代码已于 2026-09-12 删除) +> Review mode:lean(TD-SYSTEM-BOUNDARY / PR-SCOPE / CD-SYSTEMS 门禁按 lean 跳过) + +## 文档树导航 + +> 本文件是全部设计文档的树根:先在这里看整棵树和每个节点的一句话概要,决定要进哪个节点。每个节点内部遵循"一页纸"纪律——一节/一条规则/一张公式一页内读完(`tools/gdd_lint.py` R9 强制)。 + +``` +Dungeon Sweeper 设计文档树 +│ +├─ 入口层 +│ └─ gdd.md 独立版 GDD:6 节讲清游戏(概念/循环/系统清单/规模/美术/不做清单) [v1] +│ +├─ 概念层 +│ ├─ game-concept 世界观、目标受众、创意风险登记 [定稿] +│ └─ game-pillars 四支柱:P1 我算对了 · P2 因果归因 · P3 雷的两面 · P4 地牢氛围 [定稿] +│ +├─ 系统层(每系统一份 GDD,文件在 design/gdd/) +│ ├─ D2 数据配置 九张表单一事实源 · RNG 封闭流 · fail-fast 校验 [定稿] +│ ├─ A3 行动经济 AP/RAP 账本与价目 · 轮次状态机 · 五条款经济契约 [定稿] +│ ├─ A4 地形变异 雾与变异布置约束 · 承重数学(厚度/f≤8d) [定稿] +│ ├─ A1 棋盘生成 五阶段确定性生成管线 · 可推理率双判据 [定稿] +│ ├─ A2 排雷玩法 三事件回边 · 批次仲裁 · install 四不变式 [定稿] +│ ├─ B3 增益状态 三向效果统一容器 · 单一结算次数量纲 [定稿] +│ ├─ B2 敌人系统 行为环三拍 · 多敌模型 · 意图线索契约 [定稿] +│ ├─ B4 奖励经济 掉落池与双背包 · 两级奖励(即时/可选) [定稿] +│ ├─ B1 战斗结算 七段编排 · 明细流水协议 · 条款①③⑤闭合 [定稿] +│ ├─ A5 辅助道具 挖掘期信息与行动力道具 · 四效果 · 校验谓词 [定稿待人工复核] +│ ├─ C3 难度曲线与Boss 数值成长与全局变异 · SLICE-TUNED 行集 [切片调参版] +│ ├─ C2 冒险结构 关卡序列 · HP 账本 · 关卡大奖励 · 失败重开 · C1 接口契约 [Approved] +│ └─ D1 HUD 框架 五区布局 · 27 项呈现义务 · 信号载荷 schema [定稿待评审] +│ +├─ 表现参照 +│ ├─ design/ux/battle.md 战斗屏级交互与文案(呈现形态权威) [APPROVED] +│ └─ design/art/art-bible.md 视觉基础与图标键契约(§1–4 定稿) [定稿] +│ +└─ 工程面 + ├─ design/registry/entities.yaml 跨系统事实登记表(数值单一事实源) + ├─ docs/architecture/ 架构文档与 ADR + └─ production/session-state/ 项目当前进度(每里程碑同步) +``` + +**怎么读**:想 30 秒了解整个游戏、或给朋友介绍 → 从入口层 `gdd.md` 开始;想懂一条具体规则 → 进入对应系统 GDD 的「核心规则」(人话先行、举证在后);想查一个数值 → registry 或对应 GDD 的 Tuning Knobs;想看某系统管什么/不管什么 → 该 GDD 的「速览」。节点一页装不下就是文档的错,拆子节点,不许铺开。 + +## 系统枚举(20 个) + +### A. 核心玩法层(扫雷侧) + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| A1 | 扫雷棋盘生成 | 隐含 | 布雷、初始安全区、变异布置、按难度参数生成 | MVP | +| A2 | 排雷玩法规则 | 显式 | 小人控制、挖/旗/移、连锁展开、数字提示 | MVP | +| A3 | 行动经济 | 显式 | AP(每轮重置)、RAP 池、旗子(无上限)、停手/清图/耗尽三出口 | MVP | +| A4 | 地形变异 | 显式 | 迷雾/岩浆/冰冻等变异清单与交互规则 | MVP | +| A5 | 扫雷辅助道具 | 显式 | 显影/真视/预读/加 AP 四类辅助消耗品;获取与使用规则 | 垂直切片 | + +### B. 战斗层 + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| B1 | 战斗结算规则 | 显式 | 伤害公式、结算顺序、胜负判定 | MVP | +| B2 | 敌人系统 | 隐含 | 敌人类型/词条、目标选择规则 | MVP | +| B3 | 增益/状态系统 | 显式 | buff 时限、叠层、衰减时机 | MVP | +| B4 | 奖励与道具经济 | 显式 | 奖励表、道具池、权重数值、拆弹奖励 | MVP | +| B5 | 战斗表现 | 隐含 | 结算画面 → 战斗动画、伤害数字 | 垂直切片 | + +### C. 结构与成长层 + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| C1 | 关卡节点地图 | 显式(形态待定) | 关卡内移动、遭遇点/奖励点分布;形态候选已登记(2026-09-09):四方格步进式前进——格网地图、每步从相邻四格选一格推进,可后置 | 垂直切片 | +| C2 | 冒险结构 | 显式 | 关卡序列、关卡大奖励、失败重开 | 垂直切片 | +| C3 | 难度曲线与精英/Boss | 显式 | 数值成长、精英图/Boss 图全局变异 | 垂直切片 | +| C4 | 元进度(存档/天赋/代币) | 显式 | 存档结构、天赋树、代币经济 | Alpha | + +### D. 表现与支撑层 + +| ID | 系统 | 来源 | 说明 | 里程碑 | +|---|---|---|---|---| +| D1 | HUD 与界面框架 | 隐含 | 棋盘/队伍/日志/结算的 UI 容器(简版) | MVP | +| D2 | 数据配置与数值表 | 隐含 | 硬编码参数迁移为可调配置 | MVP | +| D3 | 存档序列化 | 隐含 | 元进度持久化框架 | Alpha | +| D4 | 音频 | 隐含 | 挖掘/踩雷/结算/胜利反馈 | Alpha | +| D5 | 教学引导 | 隐含 | 小人三动作 + 两层循环教学 | Alpha | +| D6 | 设置/本地化 | 隐含 | 常规打磨项 | 完整愿景 | + +## 依赖分层 + +| 层 | 系统 | 依赖 | +|---|---|---| +| 1 Foundation | A3, A4, B3, D2, D3, D6 | 无 | +| 2 Core | A1 ← A4,D2 · A2 ← A1,A3,A4 · B2 ← D2 · B4 ← D2(2026-09-09 B4 定稿收窄:原"A2,B3,D2"——A2 事实经 B1 转化零直接接口、B3 为验收锚关系非运行时边,见 B4 Dependencies 修订注记) | Foundation | +| 2.5 | **A5 ← A2,A4,B4**(辅助道具:依赖排雷规则与变异定义;奖励获取侧挂 B4) | Core | +| 3 Feature | B1 ← A3,B2,B3,B4,D2 · C2 ← C3,D2 · C3 ← D2,B2 · C4 ← D3,C2,D2 · C1 ← C2,C3,B2,B4 | Core | +| 4 Presentation | D1 ← A2,A3,B1 · B5 ← B1,D1 | Feature | +| 5 Polish | D4 ← A2,B1 · D5 ← A2,A3,B1 | Presentation | + +- **循环依赖:无**。全项目唯一允许的回边:A2 只外发事件(`cell_revealed` / `round_end` / `flag_judged`),B4/B1 订阅后回调——A2 不依赖任何人(TD-SYSTEM-BOUNDARY 定) +- **D2 接口依赖边补全(2026-09-07 re-review)**:B4(reward_roll 流)、B1(set_reload_gate 调用)、C2(master_seed 接线)、C4(MetaTable 消费)四条对 D2 的边已补入上表——此四者为 API/接线级依赖,非纯数据读取;其余系统的数据读取为事实依赖,不逐条列边(完整清单见 `design/gdd/d2-data-config.md` Dependencies 表) +- **C3←A1 方向说明**:C3 仅依赖 A1 的**配置 schema**(类型依赖);难度参数经编排器/D2 传递给 A1,禁止 C3 反向调用棋盘生成 +- **瓶颈系统(设计需一次定稳)**:A2 排雷玩法规则(B4/D1/D5 挂其事件)、B1 战斗结算(B5/D4/D5/C3 消费其输出)、D2 数据配置(全员依赖) +- **叶子系统(可晚设计)**:C1、C4、D4、D5、D6 + +## 门禁修正记录(2026-09-06,CD/TD/PR/AD 四总监 CONCERNS 汇总落实) + +- **B2 扩为"战斗单位系统"**:主角与敌人共用受伤/死亡接口;含**雷触发的敌方优势词条(伏击清单)**与**敌方意图可见性规格**(身份承重项,CD 门禁) +- **B1 GDD 须含"轮次生命周期编排"独立章节**(round_end 事件 → 结算 → 判定 → 重置/换图的 owner);输出契约为**可回放步骤流**(结算逻辑内禁止 await/动画);MVP 验收含结算明细来源标注(伤害道具/安全格增益/标中旗削弱/清图奖励/雷→伏击;~~停手折算~~ 已随 A3 RAP 修订作废——停手改产 RAP、退出战斗结算) +- **A1 GDD 须含**:可推理率生成参数(P1 支柱的生成端保障)+ biome/tag 数据字段(AD 门禁换皮约束) +- **A4 MVP 范围收窄**:变异框架 + 迷雾(岩浆/冰冻等无 MVP 容器,随关卡解锁归垂直切片后) +- **B2 MVP 范围收窄**:1–2 种敌人 + 少量词条(名录扩展归垂直切片/Alpha) +- **D2 GDD 策略**:只定 Resource schema + autoload 注册表 + 校验模式,数值表留空位随各系统 GDD 回填;**数值表结构第一天就带关卡序号/节点深度维度** +- **工程债登记**:Core 层必须可 `--headless` 跑完整战斗(不引用 Control/UI);RNG 种子服务归 D2(Foundation);已引爆雷格可通行、治疗不溢出等细节规则写入对应 GDD +- **AD 项**:B5 GDD 前插入"战斗表现美术测试"(一屏样张锁定角色剪影/动画方案/战斗空间);临时 SFX 从 D4 拆出挂垂直切片里程碑 +- **流程债已清**:game-pillars.md 已建(4 支柱);概念文档已增补 Visual Identity Anchor + 目标受众;原型 REPORT.md 已归档(PROCEED) + +## 门禁增补记录(2026-09-07,Concept → Systems Design 重审 CONCERNS 落实) + +> 完整报告:`production/gate-checks/gate-check-systems-design-2026-09-07.md` · 判定 CONCERNS(无阻塞),stage.txt 已写入 + +- **A2 前置**:标错惩罚(v3 现无惩罚)用原型小补丁实测后关闭;可与原型 v3 终验合并为一次会话(CD/PR 门禁) +- **art-bible 排期**:最晚在 D1 GDD(设计顺序第 10 位)前创建(至少 §1–4),"格子样张测试"(数字可读性定风格,game-concept 早期风险登记第 1 条)并入执行;垂直切片门禁硬性要求 art-bible(AD 门禁) +- **B4 GDD 须预留**道具 schema 的"使用时机"字段(主动战斗道具后续版本加入) +- **D1 GDD 须解决**"小人移动路径预览"的表现归属(来源:game-concept 视觉锚风险登记) +- **D2 GDD"校验模式"章节须定** headless 测试载体(`--script` 冒烟 vs gdUnit/GUT);**B1 GDD"轮次生命周期编排"章节须定**事件总线载体(autoload 单例 vs 直连)(TD 门禁) +- **C3 GDD 需定稿**"C3 是否消费 B1 输出"(依赖表 C3 ← D2,B2 与瓶颈注记口径不一)并回写本索引(TD 门禁,可延后) +- **创意风险登记**(CD 门禁,详情见 game-concept"创意风险登记"节):安全格奖励可预期性(P2 前提)→ B4/A4 落实;"我算对了"高光兑现节点 → B1/B4 设计 +- **垂直切片前置**:按预注册验证信号做一次正式 playtest(与标错惩罚实测合并)(CD 门禁) +- **A3 下游经济契约**(2026-09-07 A3 二轮评审登记,同日 RAP 结构修订改写,三轮评审增补条款⑤并钉死闭合 owner):B1 GDD 须承接并验收——① **效果档位对照表**:安全格 buff / 标中旗 debuff / 清图奖励 / 敌方增益统一当量、单一线性换算(分源改率须回 A3 重议);② 标中旗 debuff 档位锚:**严格 >** 最低档安全格 buff 当量,且以**连锁放大的边际挖掘收益**为对照基准(provisional 1:1)、每战标中旗总吞吐由 B1 对账;③ **挖掘风险不等式**:P(安全)×buff 当量下界 > P(雷)×敌方增益当量上界(锚 A1 可推理率参数 × B2 惩罚上限表值,**B1 单一闭合 owner**,B1 GDD 强制检查点);④ 清图奖励 > 0 且预留**按图类型(biome/tag)差异化分支**的结构位(B4 表设计,A3 只上报 cleared;图类型战斗域恒定、不逐图重掷,A1/B4 共同验收);⑤ **清图 vs 弃图刷新支配性**:清图奖励下界使"推到底清图"期望当量 ≥ "确定格挖完即弃图刷新"期望当量(B1 闭合对账,强制检查点——该环与 A1 可推理率反向耦合,是 P1 失败模式的经济对冲)。**request_refresh 契约**:玩家经 D1 发起、B1 结算编排期代理调用(B1 接口须含玩家意图路径,禁止 RAP 满自动刷);刷新成功后回发 `new_board` 为契约义务,未刷新的 stopped/ap_exhausted 轮回发 `new_board` = B1 违约 fail-fast。**【2026-09-12 D4 修订作废】refresh 前移挖掘期(Active 相位受理、B1 `refresh_board` 编排、无结算段无 round_end、每轮上限 2);new_board 回发义务仅限 cleared 轮,stopped/ap_exhausted 恒 same_board——现行契约见 A3 Rule 11 / B1 AC-29/AC-30 / registry `rap_rules` 与下方 2026-09-12 修订登记块】**round_end 双发射源**已登记:A2 外发棋盘路径、A3 直发停手路径(payload 同构);EC4"先停后挖"批次仲裁 deferral 至 A2 集成测试,**义务清单**:{cleared×stopped、stopped×ap_exhausted} × 两到达序共四组合 + RAP 记账随胜出 path_tag(批次 cleared 胜出 → 剩余 AP 作废不入 RAP)。B4 GDD 侧:标中旗一次性兑付、**资格以雷格实例为键**(A3 OQ5,三轮改定——拔旗重插不恢复已消费资格)+ 清图奖励插槽。D1 GDD 侧(三轮增补):RAP 战斗末清零可见反馈。原"stop_multiplier 不等式①②"随停手折算作废而删除。 + +## 门禁增补记录(2026-09-08,B3 定稿登记) + +> B3 GDD:`design/gdd/b3-status-effect.md`(Designed,待 /design-review)· registry 已登记 `status_effect` 条目 · D2 StatusEffectTable 已回填(接口漂移规则) + +- **B1×5 义务清单(B1 GDD 门禁评审逐项核对落位,缺项即 blocking——A4 对 A1/A2 登记先例)**:① 同轮多授予按确定次序调用 apply_effect(EC3 溢出归因依赖)② 事实→授予恰一次转化(EC6——B3 是 dumb 账本不去重)③ percent→乘数换算方向与合成值地板成文(EC7/F2——weaken AP 界 −200%/表界 −400%、defense_up 顶点 −100% 三例)④ 最后一轮 decay 判定(F3/A3 EC8 同款;battle_end 全清兜底)⑤ F1 条款 1 效果档位对照表以 B3 实例为对照实物 +- **🔴 weaken 锚预警(B1 强制检查点输入,registry status_effect.anchor_warning 已登记)**:weaken(−25%×d1)vs A3 F1 条款 2 锚(debuff 当量严格 > 最低档安全格 buff 当量)两独立线性代理同向预检不过(25 vs 100;≈1 vs ≈4)——B1 对照验收优先裁决;坐实违反则上调 magnitude 或 B4 重定参照,接口漂移回写 registry +- **A3 OQ5 收编注记(用户 2026-09-08 拍板)**:"标中旗 debuff 档位归 B4 表设计"修订为——四行 MVP 初值**定稿权归 B3**(已定稿),**锚验收权仍归 B4/B1**(条款 2 + A4 三定价硬约束引用的"最低档安全格 buff 当量"以 attack_up 行为参照对象);A3 GDD(Approved)不改,以本条 + registry ownership_note 为准。**【2026-09-09 B4 评审迁移注记 → 2026-09-11 回迁】**:参照对象曾迁 defense_up 当量(attack_up 时为死行)——2026-09-11 用户裁决"基础攻击恒存在"(HeroTable.atk 域 [1,999]、0 加载拒绝、MVP=6)后 **attack_up 复活,参照对象回迁 attack_up 当量**(消费面 = 主角战斗段先手攻击——B1 GDD 同批增补);本条"attack_up 行为参照对象"字样恢复现行,迁移期结束(历史见 registry status_effect.anchor_warning 回迁注记;C3 首次调参时按 attack_up 口径复算条款 2 预检) +- **EC8 reload gate B1 侧落点(qa-lead 缺口登记,2026-09-09 B4 定稿时错号修正:原记"EC9"——D2 实际编号为 EC8)**:set_reload_gate 置位/复位的真实调用方唯一落点 = B1——B1 GDD 须承载"战斗中真的被置位"的验收(D2 AC-16 只测门机制);防 gate 半句永久零落点 +- **ambush 量纲**:flat "+3" 的单位归 B2 定稿(候选"每颗已开雷伤害加成";联动 F1 条款 3 上限表值,B2 GDD 验收项) +- **B4 三项**:① 首个 refresh/independent 内容行上线时补真实内容行冒烟 AC + 夹具探针退役(B3 EC4/OQ3)② clear_reward 源授予路径确认(source=clear_reward,条款 4 插槽)③ weaken/attack_up 锚验收(条款 2) +- **StatusEffectTable 回填完成**:schema 8 字段 + Rule 4 豁免(定义表键=effect_id)+ 四行 MVP;悬空引用政策 = B4 首引时补校验(B3 EC2);D2 Core Rules 表行与 Status 行已同步回写 + +## 门禁增补记录(2026-09-09,A1 定稿登记) + +> A1 GDD:`design/gdd/a1-board-generation.md`(Designed,待 /design-review)· registry 已登记 `board_gen_table` 条目 + action_economy.referenced_by 增 A1 · D2 BoardGenTable 已回填(接口漂移规则) + +- **A2 双包含回验(A2 GDD 门禁评审缺项即 blocking)**:① A2 实际展开域 ⊆ A1 排除域(既有)② **A1 求解器初始态 ⊆ A2 实际初始展开域**(F1 注记④度量保真对偶——新增;若 A2 定稿展开更小,A1 回退出生 3×3 最小态口径) +- **B1 强制检查点输入口径(登记 B1 GDD)**:条款 3/5 对账消费 **actuals 分布(F4 报告 C 的 p10)而非 inferability_target 参数**——"拿参数当事实"对账空心化防御;fog_burden_ratio(付费格分母)= 条款 5 精确口径;attempt 边界非 scope 事件(B1 编排注记);inferability vs P(安全) 措辞登记 registry(非同义词——生成端结构性上界代理之占比形式) +- **F4 全量批量工具排期(qa-lead G7)**:调参期红线工具(N=256/行)owner = A1 交付物 / C3 消费——**与 D2 OQ5 曲线导出工具同批登记**(排期倒挂:A1 定稿→首行数值回填→即需抽查;临时载体 = 冒烟缩样 AC-6/10,统计意义有限) +- **参考行 provisional 声明**:BoardGenTable MVP 参考行(L1/L2)仅 schema 自洽性证明——曲线数值归 C3 调参所有权,禁止作为平衡事实引用;参考行 biome 占位值 {"dungeon"} 待词表 owner(B4/C1+AD)定稿替换(A1 OQ4) +- **黄金图回归向量协议**:五类手工核验小板 + 整数对比对 + 逐格信息源推导记录入版本库;换 solver_rules/信息源/metric_version = GDD 修订 + 向量重制(推理域冻结的行为侧兑现);**A1 /design-review 时安排手工推导第二人复核**(OQ5) +- **度量独立入口分解面(qa-lead G1,A1 实现契约条款)**:推理器/F3 度量/合法性谓词三入口暴露为独立纯函数(与内嵌调用同一实现)——实现期 ADR 确认;若不暴露,AC-7/8/9/12 单元腿降级重议 +- **A2 前置偏离登记(2026-09-09 用户拍板)**:门禁原要求"标错惩罚用原型小补丁实测后再写 A2 GDD"(2026-09-07 CD/PR 门禁;候选 = 敌方伏击+1;兼任 A3 OQ2 旗滥用定价职责)——现改为**标错惩罚以 OQ 形式登记进 A2 GDD**(候选方案与实测义务随 OQ 保留,不阻塞设计),偏离理由:实测依赖 playtest 会话、先推进设计主链;A2 /design-review 时须核对该 OQ 在位 +- **A1 full 评审终态(2026-09-09,用户拍板放行)**:8 blocking 同日核销(P1 两项达标判据 + target 域 [0.85,1] 绝对锚 / 雾占比上限 0.25 / F2c 成本闸 + 禁墙钟 / 雷数取整与布雷钉死 / F2≡Schema 合流 / AC-6(a) 芯环改写 / AC-11 前提腿 / 断言 B 收紧达成区占比)+ 第三轴登记约定(Boss=Dmax 行、elite_/boss_ 命名空间预留、OQ6 重审门)。**新登记下游承接项**:① EC5 零合法板玩家侧诚实呈现(提示/补偿/回退)→ B1/D1 域;② EC5 运行期熔断(同 (L,D) 连续降级/非法 → 确定性回退已知好种子)→ 实现期 ADR(OQ2 关联)。D2/registry 接口漂移已同步;recommended 9 项留档 A1 日志 + +## 门禁增补记录(2026-09-09,B3 full 评审登记) + +> B3 GDD full 评审:NEEDS REVISION(7 blocking),同日核销——日志 `design/gdd/reviews/b3-status-effect-review-log.md`。修订摘要:enemy 侧**单敌假设入文**〔用户拍板〕· B1 义务 5→8 · D1/B5/D5 呈现义务清单登记 · AC-15 机制更正 + reloaded 根全清单规格 · 致命夹具补根〔dup_key〕· AC-18 随 registry 逐字段键重构 · weaken 红警补修复空间不对称 + OQ1 归一口径前置 · stack-不重置 R 登记 OQ7 playtest 证伪项。 + +- **B1 义务清单 5→8(B1 GDD 门禁评审逐项核对落位,缺项即 blocking;①–⑤ 为 2026-09-08 原清单)**:⑥ **每结算编排恰一次 decay_round**(decay 基数=1 是 dur=1 一次性语义与全部衰减语义的先决条件;Active 相位 double-decay 在 B3 侧无守卫,信任 B1 编排)⑦ **结算明细按授予流水逐条标注来源**(F1 合并坍缩归因的兑付面补偿——合并实例只持首授 origin_cell/round_index)⑧ **“溢出未生效”行进结算明细协议**(Rule 5 后半承载;呈现归 B5/D1) +- **D1×4**:① HUD 常显 + EC12 空态规格定稿(隐藏栏 vs 空槽由 D1 裁决但须成文)② effect_id→icon 映射键契约(art-bible 形状+色相双通道)③ 有利/有害极性呈现映射(数据源 = B1 SettlementTable 效果语义,非 magnitude 符号——负值增益存在)④ 战斗结束全清的状态栏去留。**B5×2**:① 溢出行呈现(数据源 = B1 义务⑧)② origin_cell→玩家可读形态(缩略图/格子高亮;坐标不直接呈现)。**D5 增补**:“同轮拿满优于跨轮叠”教学(OQ7 配套)。**B4 增补**:奖励身份预读(stack 时序规划的推理原料,创意风险 #1 切面)——均缺项即 blocking,随各 GDD 门禁评审核对 +- **单敌假设(用户 2026-09-09 拍板,B3 Rule 1 入文)**:MVP 战斗 = 单个敌人,target_side 无歧义(enemy 侧 side-wide = 该敌);**B2 定敌人数模型时回验**——定多敌即触发 target_ref 接口漂移补列(B1 归集语义/B2 模型/D1 呈现裁决随批验收) +- **weaken 锚裁决前置(OQ1 增补,B1 强制检查点输入)**:B1 先钉死归一口径(每结算瞬时当量 vs 每 AP 总暴露)——瞬时读法 m_w≥51 可行(F2 越域界随移 AP 界 −408%/表界 −816%,7 层触 B1 地板);总暴露读法 m_w>100 被 percent 域**禁止(该分支不可行)**;口径落定前禁止数值修订,落总暴露则现实解 = B4 重定参照 +- **AC-19 扫描域登记(B3 文件集)**:B3 全部 GDScript 产物(状态容器类 + StatusEffectTable schema 类 + 冒烟脚本,文件名实现期定)纳入 D2 AC-9 模式项目级 CI 扫描域——owner:Technical Setup 归口随 B3 实现建立成员清单(qa-lead 评审缺口:此前三份 GDD 引用同一无载体域) +- **registry status_effect 条目逐字段键重构**(B3 owner 修订,2026-09-09):fields/mvp_content 由散文改逐字段 YAML 键(action_economy 同构)——AC-18 机器对账前提;knob 活值不作定值对账(A4 AC-8 同款);anchor_warning 增修复空间不对称;b1_obligations 同步 5→8 + +## 门禁增补记录(2026-09-09,A2 定稿登记) + +> A2 GDD:`design/gdd/a2-minesweeper-rules.md`(Designed,待 /design-review)· registry 已登记 `a2_event_surface` / `a2_initial_expansion` 两条件目 + +- **四件已登记事项逐项落位核销(用户 2026-09-09 开工简报)**:① **A2×4 清单**(A4 登记)+ **A1 双包含回验两臂**——落位 A2 GDD Dependencies 门禁义务表 + AC(A2×4①→EC4 腿 · ②→AC-12 · ③→EC4 腿/AC-18/20 · ④→AC-18;双包含①②→AC-3;**门禁评审缺项即 blocking**);② **A3 委派**——初始展开定义落点(免费/计入清图/无奖励 = Core Rule 4/AC-1)+ round_end 棋盘路径外发 payload 同构(Core Rule 2/10,AC-7/8/9);③ **标错惩罚以 OQ1 登记在位**(候选 = 敌方伏击+1,实测义务随 OQ 保留;**A2 /design-review 核对在位,缺位即 blocking**);④ **已引爆雷格可通行**(Core Rule 11/AC-15——工程债核销)。A3 EC4 批次四组合 deferral 已在 A2 冒烟兑现(AC-10 语义序仲裁 F4,含类内稳定序腿 e) +- **🔴 A1 修订义务(systems-designer 反例 B,主会话独立复算证实——阻断级,A1 定稿后首条)**:A1 布雷数下限 round(d×(W×H−9)) ≥ 1 拦不住"初始展开即 cleared"退化板——8×8 八雷先布后删幸存 1 颗距离-2 雷 → 最终板全 0 格连通 → R₀=S 开局免费清图;N=1 参数行 100% 退化、F4 断言 A(inferability=1.0)零拦截、min_safe_count 无效(|S| 被动满足)。**义务(A1 GDD 修订走接口漂移)**:F5 legality 增 `R₀ ≠ S`(违例 = 尝试作废重试,clearable 同款)+ F4 断言 A′ 族严格零覆盖 + Rule 4 排除域/可达域基准措辞钉死"以最终产物板计";A2 侧 install 不变式④纵深防御(b_degenerate 板拒绝,AC-1b)——双包含两臂不受影响 +- **B1×3 强制项(B1 GDD 门禁评审逐项核对落位,缺项即 blocking)**:① 结算触发 = Waiting 相位编排点、不依赖事件到达序(stopped 轮 A3 直发 round_end 与 A2 flag_judged 批的到达序不承载语义——事件序契约 flag_judged < round_end 全路径统一)② reward_eligible=false(initial 通道)格不得进入奖励入库(A3 EC5 委派闭环)③ 标中旗兑付去重(资格以雷格实例为键——A2 诚实重复上报的对侧) +- **D1×6 强制项(2026-09-09 A2 full 评审由 D1×1 扩容,缺项即 blocking)**:① stop 意图经 A2 `settle_batch` 提交、**不直调 A3**(单一结算序保证——A3 Rule 9"输入层直调"的调用方细化为 A2 批次入口,A3 GDD 不改、以本条 + registry a2_event_surface.api_surface 为准)② 四种格态一眼可辨(已开 0 格地板/已开雾格无字/未开格/未开雾格——左键移动 vs 挖消歧前提,误挖不可逆)③ 悬停预承诺(光标区分移动/挖/旗)+ 可挖域常驻指示 + chord 肌肉记忆首遇提示(非模态每 run 一次)④ 已引爆雷格"可通行可读"呈现 ⑤ `compute_move_path` 呈现语义定稿(合法性指示 vs 穿行动画——P4 载体)⑥ 单意图帧输入收集 + voided 呈现义务(至少 stop 被作废须可感知)+ 每次拒绝玩家可感知(映射源 = reject_reason + sub_reason);移动路径预览数据源 `compute_move_path` 已备(表现归属归 D1 GDD,2026-09-07 门禁项不变) +- **B1/B4 增补两条(2026-09-09 A2 full 评审登记,B1/B4 GDD 门禁评审核对落位,缺项即 blocking)**:① **高光兑现节点条目化**(创意风险 #2/#3 合并——"我算对了"爆发结算 + chord 缺席的标中旗即时兑现补偿候选;原散文登记升义务清单——B3 先例:清单外登记会丢失)② **连锁批量授予溢出吸收设计**(13 格链 × B3 max_stack=2 ⇒ 大量"溢出未生效"行进结算明细,P2 画面反向伤害连锁幻想——溢出转一次性资源/治疗或显式接受,裁决归 B1/B4) +- **A2 full 评审修订完成(2026-09-09,四专员 + CD 终审 NEEDS REVISION 六 blocking 同日核销)**:GD-1 连锁不越旗(传播集 ∖ G,经典 flood 惯例——旗 = 1 AP 链阻断器)· F4 到达序无关性声明收窄((path_tag, hint, AP/RAP) 全批域不变;划分序无关仅异类四组合;AC-10 增耗尽型救援/联合清图用例)· ActionOutcome 增 sub_reason 封闭枚举(UX-1——批前免费窗口,registry 已同步)· D1×6 义务包 · AC 修复包("EC4 腿"编号化 AC-6、b_chain/b_fog spawn=(3,2)、board_invalid 诊断枚举、AC-7 ap=1 同帧竞争、OQ2 范围收窄)· 用语纪律清扫(双闸/腿/袋区/合成判定 + registry terminology 条目);随批:Rule 11 雷格通行有价捷径注记(GD-2 降级 residual——A1 clearable 闸按安全格 8 邻连通计,强制过路费板生成期被拦截)· OQ1 增惩罚重复性裁决项(hit=true 有去重 / hit=false 惩罚无去重的不对称,实测会话前钉死) +- **A2 侧接口事实(provisional 登记)**:批次语义序仲裁(cleared-dig → stop → 其余,F4 四组合真值表 + RAP 记账随胜者);A2 零表读取零 ConfigDB 引用(D2"A2 无直接依赖"事实证实,AC-22③——若实现期 ADR 裁决需读表,走接口漂移) + +## 门禁增补记录(2026-09-09,B2 定稿登记) + +> B2 GDD:`design/gdd/b2-enemy-system.md`(**Approved**——2026-09-09 full 评审 8 blocking 同日核销收口,见下方增补块)· registry 已登记 enemy / hero / clause3_supply 三条目 + status_effect 两注记(single_enemy_assumption 回验改写 / ambush_unit_definition 量纲定稿)+ terminology 增 B2 六词条 · D2 EnemyTable 行回填 + HeroTable 行新增(接口漂移) + +- **B1×6 义务清单(B2 GDD 门禁评审逐项核对落位,缺项即 blocking——B3 对 B1 登记先例)**:① 单位接口调用集中在结算编排、行为环推进每结算恰一次(double-advance 无守卫)② 雷→伏击事实转化恰一次(每雷格实例至多一次)③ 伤害 clamp/负值/溢杀语义与死亡判定时机成文 ④ F1 条款 3 闭合对账(B2 cap 表值 + 量纲 + **A1 actuals p10 非 target 参数**)⑤ 默认目标规则消费与单体/AOE 分流(固定顺位)⑥ 伏击 cap 溢出"溢出未生效"明细行协议 +- **D1×4 义务清单(视觉推测型呈现,缺项即 blocking;2026-09-09 full 评审增补口径)**:① enemy_id→图像线索映射清单成文(**预告时序 = 下一拍预告、挖掘期全程可见**〔B2 Rule 8⑤〕;**含强度档位/环长直觉编码说明**;形状+色相双通道,art-bible 约束)② 线索映射**全局恒定**(enemy_id 键——跨 run 禁重掷)③ 单位顺位与存活可读(固定顺位推测前提;顺位标记资产入 B2 Visual Req 2 下限)④ 敌方 side 池呈现口径(B3 敌侧延伸) +- **B3 单敌假设回验结果**:不成立——定多敌(用户拍板;B3 预授权接口漂移条款兑现)——registry single_enemy_assumption 已改写(side-wide 兼容 + target_ref 扩展位);B3 GDD 正文不改,以 registry 为准 +- **ambush 量纲定稿(2026-09-08 B3 块"ambush 量纲"项兑现)**:+3 flat = 每次攻击类行为附加 3 点主角 HP(charge 拍零兑付)——registry OQ2 关闭条件;值仍 provisional,条款 3 锚验收归 B4/B1(B2 供给 clause3_supply 口径:单结算自然边界〔用户定稿〕+ cap 表值默认 4) +- **cap=4 定稿注记**:反灾难护栏定位(绑定阈 = 单轮 5–8 雷灾难轮);旧默认 8 使溢出协议死码(min(雷数≤8, 8) 恒等式)已废弃;量级注记——atk=4 时 L≥2 伏击分量过半(常态支配修正入口 = C3 atk 曲线 / B4·B1 m_amb 锚,登记方向非 B2 义务) +- **qa-lead 缺口预检 7 项**:🔴-1 query_attack/ambush_grant_plan face 归属(owner:实现期 ADR,B2 OQ1)· 🔴-2 多敌语义默认零消费者复验(owner:C1,B2 OQ2)· 🔴-3/4 已随批修正(F4 满配语境 / EC17 读写切分)· 5–7 承载于 AC 前言(信任编排 / HeroTable 单行落点 / 坐标登记协议) +- **视觉推测型注记**:意图可见性规格 = 不直接显示行为/数值,敌种图像承载稳定可学线索(用户拍板)——P1 护栏 = 线索映射每 run 恒定(D1×4②);结算明细数字兑现(P2 不变);双舞台资产管线陷阱不受影响(B5 前美术测试照旧,B2 只锁视觉状态清单数据面:grub 1 态 + ambusher 3 态) + +### 2026-09-09 B2 full 评审增补(六专员 + CD 终裁,8 blocking 同日核销 → Approved) + +- **胜利路径声明(CD 排序 #1;原文 2026-09-09,2026-09-11 用户裁决退役)**:原"hero atk=0 → 当前内容态结构性不可通关、B4 先行 = 任何可通关验证的先决"作废——**基础攻击恒存在**(HeroTable.atk 域 [1,999]、0 加载拒绝、MVP = 6 原型锚 provisional;主角每结算战斗段先手攻击顺位最前敌),胜利路径 = 基础攻击 + 奖励侧增量共同承载(B2 Overview 2026-09-11 改写;B1 战斗段同批增补主角先手攻击步 + hero_attack 来源值) +- **B1×6 之① 增补**:结算内调用序成文 **query_attack→apply_damage→advance**(倒序读到下一拍;B1 冒烟须回放结算步流);推进口径 = 每存活**敌**单位(主角无环,环接口对 hero 拒绝) +- **资产下限增补**:受击反馈/死亡表现/顺位标记三类入 B2 Visual Req 2 资产需求下限(顺位标记归 D1×4③、同等 blocking) +- **新增 OQ7/8/9**:精英/Boss 编码位(**C1 定稿强制核对**——键冻结下同深度第二敌种触发行集致命,改键走 D2 Rule 4 接口漂移)· grub 单拍环体验证伪(playtest 预注册,两拍环备用案归 C3)· 零伤害死局组合跨字段护栏(实现期 ADR/D2) +- **OQ6 更正**:标错惩罚事件源 = flag_judged{hit=false}(**非 mine_hit**)——B3 source_class 结构冻结,须 B3 GDD 修订非加行;B2 F3 重验触发补"新增非 mine_hit 授予源"支 +- **F4 供给口径修正**(registry clause3_supply 已同步):结构上界改 **min(AP 上界, cap)** 形(cap 腿松弛收紧)· direction_precheck 裕度 4.7×→**2.3×**(按条款 2 最低档锚 +50pp 实算) + +## 门禁增补记录(2026-09-09,B4 定稿登记) + +> B4 GDD:`design/gdd/b4-reward-item-economy.md`(**Approved**——2026-09-09 full 评审 14 blocking 同日修订核销后用户拍板放行;见下方 full 评审增补块)· registry 已登记 `reward_table` 条目 + 三处接口漂移改写(rng_service reward_roll scope 语义 = 每次 board install 批 / status_effect 悬空校验首引兑现 / clause3_supply 左臂载体 = B4 F6)+ action_economy.referenced_by 增 B4 · D2 六处回写(RewardTable 定稿行 / StatusEffectTable 注记 / Dependencies B4 行 / 双向一致性 B4←D2 / Tuning Knobs 占位删除 / Status 登记) + +- **B1×6 义务清单(B4 GDD Dependencies 全文,B1 GDD 门禁评审逐项核对落位,缺项即 blocking——B2/B3 对 B1 登记先例;2026-09-09 full 评审同步**:①段序含窗口锚点与回放输入 ②增"每格受理恰一次" ④增 usable_now 组装/逐件动态守卫/击杀结果进流水**)**:① **结算段序骨架成文**:授予段→**道具使用窗口(授予段后、增益/减益段前——full 评审用户拍板:当轮拾取当轮可用)**→增益/减益段→道具段→战斗段→衰减段→判定段(用户拍板核心序 buff/debuff > 道具 > 战斗不变;**窗口选择序列登记为结算回放输入事件**;B2 义务①②③的转化与推进落点随段序定位)② 奖励受理与分流编排(grant → buff 类 apply_effect / 道具类 stock;**每格受理恰一次、stock 与 grant 返回一一对应**;满层拒绝 = 零兑付 + "奖励未生效"明细行、无折算,**行携带可聚合计数/批次字段**)③ clear_reward 授予路径(**B3 OQ4 关闭条件**:install 期 roll、cleared 编排期兑付——兑付后同结算窗口即可选用)④ 道具段编排(窗口数据组装 = B4 query_inventory + **B1 侧守卫预判 usable_now**、选定序逐件施加、**满血守卫拒绝不烧包 + 同窗顺序多包逐件动态守卫**、**零效果判定读 B2 存活集 + "已消耗未生效"明细行〔E10 照常消耗拍板的 B1 侧承接——缺口预检 1 逐字核对〕**、**击杀结果进流水〔H3 感知数据前置〕**、治疗 clamp)⑤ 生命周期编排(battle_over 调 clear_inventory〔**清空前后快照计数供清空反馈行**〕;board retire 调映射消亡)⑥ roll_board 调用恰一次(B4 侧相位守卫见 E5/AC-1;B1 编排纪律承载调用时点半) +- **D1×5 义务清单(缺项即 blocking;2026-09-09 full 评审 3→5 扩容)**:① 已开格 + 库存奖励身份恒定可读(reward_id 键全局恒定、icon 形状+色相双通道——art-bible 约束;**库存常读条 = 挖掘期规划面**而非仅轮末窗口)② 道具使用窗口呈现(授予段后弹出;选用/跳过/选择序、窗口时机指示、**usable_now 可用性投影**〔满血治疗包禁选/提示〕、空库存建议不弹窗、窗口触发政策注记)③ 结算明细**四类行**呈现(道具行〔语境注记〕/ "奖励未生效"行〔**按聚合计数呈现**〕/ "已消耗未生效"行 / "使用被拒"行——数据源 B1 流水;未选用件"随战斗结束消亡"语境注记)④ **H2 清图奖励行强调呈现**(轮末仪式峰值)⑤ **battle_over 库存清空可见反馈**("剩余道具 ×N 随战斗结束清空"——RAP 清零可见反馈同款先例) +- **两项 E 节拍板(2026-09-09 用户裁决,其中第一项覆盖并行会话草稿)**:**道具段照常消耗**(选定即承诺——无存活敌的已选刀照常 Used + 零效果 + "已消耗未生效"行;不对称理由 2026-09-09 full 评审更正:原"敌 HP 不可见"与 B2 血条数字可读矛盾作废,现行理由 = 守卫所有权不对称〔满血守卫是表局部判定、零效果判定须读 B2 存活集〕+ 反抖动;误触防护由窗口 usable_now 预判承载)· **清图槽允许 buff 行**(A3 条款④兑现口径 = 行级恒有正当量〔F3 加载期校验〕,运行时满层零兑付为合法例外 + 明细行;预警④单独计数监控) +- **attack_up 复活 + 锚参照回迁(2026-09-11 用户裁决批——原"死行 + 锚参照迁移"条目〔2026-09-09,F4 口径后果〕退役为历史)**:基础攻击恒存在(HeroTable.atk 域 [1,999]、0 加载拒绝)→ attack_up 复活、唯一消费面 = 主角战斗段先手攻击(B1 同批增补);"道具伤害恒定表值不吃 percent 修正"拍板不变(attack_up 只乘基础攻击、不乘刀);A3 条款 2 锚参照对象**回迁 attack_up 当量**(换算归 B1 条款①对照表;B3 红警裁决前置不变);**attack_up 已随同日两级奖励批入默认根池**(attack_up_basic w20 t2——本条"入池随奖励改造批"残余已兑现) +- **两级奖励 + 关内延续批(2026-09-11 用户裁决,同批改写 B4/B1/B3/D2/registry + 切片落地)**:① 奖励按**交付轴两类**——即时类 {buff, instant_aoe, instant_heal}(授予段当场兑付、不进库存、零决策,默认根 75%)/ 可选类 {damage_item, heal_pack}(入库存、窗口选用、更强,25%)——category 五值枚举;② **库存生命周期 = 关卡域**(跨战斗延续、关卡末 clear_inventory——原战斗域拍板作废,"关卡结束"事件归 C1/C2、落位前上层显式调用 B1 level_clear);③ **窗口单件制**(至多选用一件——原任意多件作废);④ **治疗两级**:instant_heal 即时小额 + heal_pack 稀缺强治疗(w 25%→6%、p 4→12);⑤ buff 时长当次化(attack_up/defense_up D=1);⑥ **B1 义务⑤/D1 义务⑤改写**(battle_over 清库 → 关卡末 level_clear + 清空反馈);⑦ 同批登记:预警②④⑦结构性缓解注记、方向预检重算(刀供给 ≈1.8|资格格|/板 + 即时类与基础攻击分量)、F5 保底重锚(仅计 damage_item = 可选伤害保底)、P_min=8、AC-19 对账对更新 (knife_adv, attack_up_basic)。敌 hp / 奖励供给联动重算义务(预警⑥)归 C3 随批执行 +- **缺口预检 2 项阻塞级 deferral 至 B1**(qa-lead,共 11 项全文见 B4 GDD——lean 9 + full 评审 2):① B1 编排半整体 deferral(段序/窗口/守卫/四类明细行协议——冒烟以构造序扮演零断言)② A4① 当量升级与②③状态(AC-19 现为档位序检查——B1 对照表落定后升当量对账;**② MVP 结构性不可闭合〔clear_default 常量池,激活载体 = clear_\ 插槽〕、③静默绿灯风险——Dependencies 显式宣告**) +- **概念层口径交 consistency-check**(两处):主动道具 MVP 激活(game-concept"后续版本加入"→ use_timing 单值兑现)· 库存战斗域 vs Peglin"道具跨循环累积"参照差异 +- **索引错号修正顺带**:B3 块"EC9 reload gate"→"EC8"(D2 实际编号,上文已改) + +### 2026-09-09 B4 full 评审增补(五专员〔game/systems/economy/qa/ux-designer〕+ CD 终裁——NEEDS REVISION,14 blocking 同日修订核销,待 delta 复核放行) + +> 承重机制经对抗评审零动摇:install 期预分配确定性脊柱、封闭 RNG、F1–F6 边界实算、编排被动性、AC/夹具架构——CD 终裁"机械核心是管线迄今最扎实的,结构上没有需要推倒的东西";全部 blocking 为文本级/附表级(B2 先例)。日志 `design/gdd/reviews/b4-reward-item-economy-review-log.md`。 + +- **用户评审拍板:窗口锚点 = 授予段之后、增益/减益段之前(当轮拾取当轮可用)**——消除"当轮拾取下轮才可用"未陈述规则与末图清图奖励必然作废两伪影;核心段序(buff/debuff > 道具 > 战斗)不变。Rule 5/E10/E15/D1×②/B1×①④ 五处随批改写;E15/OQ4 主体消解(残余 = 未选用件消亡注记〔D1 义务③⑤〕+ 折算/继承归 Alpha C2) +- **跨文档真相分裂修复(三处)**:D2 Core Rule 5 与 D2 Interactions B4 行的 reward_roll 旧语义("每次奖励结算/每轮结算取号")补同步为 install 批语义(原定稿批只改了 registry 与 D2 Dependencies 行——B4"registry/D2 已同步"声明对 D2 正文失实,实现者按旧正文接线会使 B4 全部 σ 轨迹 AC 报废);systems-index A3 OQ5 注记、registry status_effect.anchor_warning、clause3_supply.direction_precheck 三处补 defense_up 锚参照迁移注记(迁移后预检未重跑事实同批登记);game-concept 创意风险 #1 / 可证伪信号 / agency 定位句三处 MVP 口径注记 +- **预读声称如实降级(四方独立确认的 overclaim)**:"奖励身份预读 = 我要去拿 X 路线规划/创意风险 #1 正面解"在 MVP 结构性不成立(reveal = 同轮授予、未开格不呈现)——四处承重位(Overview/Player Fantasy/E3/预警④)改写为真话:已开格+库存恒读 + 每格必有货 + 雾档先验 + 叠层感知挖掘;寻路 = OQ2/A5 二期形态;创意风险 #1 维持 OPEN;playtest 证伪信号按 MVP 口径读取(防"缺失功能被误判为设计失败") +- **结构新增**:**RewardConfigTable**(单行全局接口漂移——T_fog_min/K_dmg/N_draft/P_min 的 Rule 1 载体,registry `reward_config` 条目 + D2 表清单回填);**RewardTable 第九字段 display_name**;F5 加载护栏两条致命(floor_supply_missing / no_damage_row——后者关闭"K=0 零伤害世界静默复活不可通关"后门,B2 OQ9 并案);默认根内容行集登记(knife_t1/knife_adv/heal_t1/defense_up_basic/clear_knife + 刀供给方向预检——溯源原型锚);API 输入域语义补裁(E18 重复受理幂等/E19 清单外坐标/E20 stock 受理域/E21 枚举序)+ B1×②"每格恰一次" +- **AC 修复**:AC-14 分支 3 与 AC-3 分支 3 同输入矛盾消解(相位守卫不在豁免域);AC-6 分支 2 改 32σ 双重量化 + 分支 4 致命腿改烤入根(override 不触发加载校验);σ 协议补预推进机制 + 纸面契约子集声明(A2 OQ2 先例)+ b4_reroll 夹具补全登记 + 第二人复核义务;AC-15 增使用期快照分支;AC-19 默认根改非平凡对账对 (knife_adv, defense_up_basic);"无洞"声明撤回重述(覆盖口径 = EC 21/21 映射 + 输入域补裁,负空间学说边界如实登记) +- **E10 理由更正(拍板本体不变)**:原"敌 HP 精确值玩家不可见"与 B2 呈现规格(hp/max_hp 血条数字可读)矛盾——现行理由 = 守卫所有权不对称(零效果判定须读 B2 存活集,B4 零跨系统读不开口子)+ 反抖动;窗口 usable_now 预判承载误触防护 +- **经济侧登记**:失衡预警重校准扩容 ①–⑧(①拆短缺/浪费、④按吸收上限重推并拆稳态/连锁双表、⑤雾板分层〔A4 明文要求〕、新增⑥速杀退化/⑦窗口摩擦/⑧H2 曝光率);A4 硬约束②③ MVP 休眠显式宣告(②结构性不可闭合、③静默绿灯);C3 调参期批量仿真义务(F5 概率性致命带);H2/H3 升 D1 义务④⑤ + 创意风险 #2 爆发结算机制本体显式登记归 B1 强制检查点 +- **用语纪律清扫**:腿→分支(全文)、档位序代理→档位序检查、贴地/行洪水/自救面/换签/红警机械替换或加注,registry terminology 增 B4 六词条(刀/烧刀烧包/批尾/死行/档位序检查/预推进) + +## 门禁增补记录(2026-09-10,B1 定稿登记) + +> B1 GDD:`design/gdd/b1-settlement.md`(Designed,待 /design-review)· registry 新增 `settlement_table` / `clause5_dominance` 两条件目 + 四处接口漂移回写(weaken −25→−65 / anchor_warning F0 回执 / clause3_supply 闭合回执 / board_gen 消费回执)+ 三处 b1_obligations 履行回执(B3×8/B2×6/B4×6)+ terminology 五词条;D2 SettlementTable 已回填(接口漂移) + +- **F0 归一口径裁决(B3 红警关闭,B1 强制检查点兑现)**:条款②锚验收归一口径 = **每结算瞬时当量**(总暴露分支被 percent schema 域禁止 + 与 B2"单结算自然边界"冲突,排除);**m_w −25 → 值域 [60,85]/默认 −65**(终值 = B4/B1 共裁,B1 OQ2);地板 = 乘数零点 −100pp、交叉层数 ⌈100/m_w⌉ = 2(原"7 层触地板"预读更正);defense_up 参照保持 +- **条款①③⑤闭合首版验算完成(B1 单一闭合 owner)**:条款①对照表以 B3/B4 默认根活行为对照实物(D_ref=12 载体 = SettlementTable.equivalence_ref_damage;defense_up = 最低档安全格 buff 锚 6.0 HP/结算);条款③默认通过(L1 ≈2.0× / L2 ≈2.6× 边际配对)——**满配语境 unit_count=3 最坏盲点破线 → C3 调参约束**;条款⑤两态通过 + **保守地板缺口如实登记**(C 单独兜底不足,非阻塞)——**actuals p10 / fog_burden_ratio 交付后全部强制重跑(B1 AC-39/40)** +- **高光兑现节点双落(创意风险 #2/#3 机制本体,用户 2026-09-10 拍板——A2 登记义务条目化兑现)**:#2 **完美排雷**(板级零误标 ∧ ≥1 标中 → 清图奖励当量 ×P_perfect 默认 ×2;与踩雷正交、计数随 install 重置、RAP 刷新图无兑付点、上界受护栏 B 防钓鱼线 C_eff ≤ 24 约束);#3 **标中旗 highlight 标记**(数据面——当轮兑付 + 明细强调组合补偿 chord 缺席);呈现归 D1 +- **D1×3 义务清单(B1 出具,缺项即 blocking,并入 D1 既有台账 A2×6/B2×4/B3×4/B4×5)**:① 高光行强调呈现(perfect_sweep 高光行 / 标中旗 highlight / H2 清图奖励强调——数据源 = B1 Core Rule 14 流水行协议)② 完美排雷呈现形态 ③ EC5 零合法板提示/补偿/回退形态(数据面 = 非法产物标记上抛,A1 EC5 玩家侧体验债的 B1→D1 承接) +- **C3 消费 B1 对账输出的事实依赖成立**(2026-09-07 TD 门禁悬案回执)——对账输出 = 调参约束清单(满配破线 / 保守地板缺口 / 护栏 A/B 突破条件),非运行时接口;C3 GDD 定稿时回写本索引 +- **实现期 ADR 裁决项 ×2(B1 出具)**:事件总线载体(TD 门禁 2026-09-07 项的 B1 侧——与 A2 OQ2 同一裁决;AC-6/33 相关 THEN 为纸面契约直至落地)· B2 OQ1 两 face 归属(query_attack/ambush_grant_plan) +- **UX Flag**:B1 有 UI 贡献(结算明细屏 + 道具使用窗口)——Phase 4 前置 `/ux-design`,stories 引用 `design/ux/[screen].md` 非 GDD +- **B1 冒烟口径(项目首个全部玩法上游 Approved 的系统)**:真 A3/A2/B2/B3/B4 全集成 + A1 程序构造桩(取号契约同构,真 A1 全链路登记端到端 deferral)+ C1/C2 encounter 直供(provisional);可回放 AC-33 = D2 AC-15 deferred→B1 兑现;编排唯一性单向静态扫描(B2/B3/B4 API 符号命中文件 ⊆ B1 文件集) + +## 门禁增补记录(2026-09-12,战斗屏 UX 修订登记) + +> `design/ux/battle.md` /ux-review 完成:NEEDS REVISION 7 blocking 同日修订落稿(复检待跑)——B1 UX Flag 承载面即本 spec。同批用户拍板 **D4 弃图刷新前移挖掘期**,四处 GDD + registry 同步修订(接口漂移走各 GDD 修订流程,本块为索引登记): + +- **A3**:`request_refresh` Active 相位受理(原仅 Waiting)+ **每轮上限 2 次**(`refresh_cap_per_round` 结构位默认 2——D1 提案采纳,原"无结构性上限"作废;RoundStart 重置)+ Waiting 代理退役;**旧板旗随旧板作废**(零兑付、旗 AP 不返还——A2 EC11 全量重置承载);**经济预警**:挖掘期当场刷新无结算发生,原"敌方时间税"自惩半句失效——OQ3 弃图率/刷新率预警阈值联动复核(登记) +- **A2**:install_board 受理域增 **Active·仅弃图刷新路径**(B1 `refresh_board` 编排内、A3 扣费成功为前提;非刷新路径"防轮内换板"不变) +- **B1**:stopped/ap_exhausted 轮**恒 same_board**(原 Waiting 期刷新代理 + AC-29 退役/改写);新增**挖掘期刷新编排 `refresh_board`**(A3 扣费 → A2 retire → A1 generate → A2 install → B4 roll_board 恰一次;无结算段、无 round_end——编排唯一性原则保持);AC-30 改恒 same_board 口径 +- **systems-index 本体更正**:A4→D1 义务 ×2→×3(第③项 = 已开雾格 tint + 雾区全开完成信号——原索引漏记;OQ8 已于 battle.md 裁决采纳) +- **B4**:五处旧"battle_over 库存清空/随战斗结束消亡"摘要同步为关卡末 level_clear 口径(权威 = D1 侧义务清单⑤/L94) +- **D1 呈现义务台账状态**:battle.md 对 A2×6/A3/A4×3/B1×3/B2×4/B3×4/B4×5 的承接经本轮评审逐项核对(实质承接全齐;B1×3 映射错位、`settlement_snapshot`/`can_request_refresh` 自造接口引用已修正);entry 17①②③ 战斗屏落点齐备(场景级 AC 已入) + +## 门禁增补记录(2026-09-13,art-bible §1–4 定稿登记) + +> `design/art/art-bible.md` 已建成——2026-09-10 初稿(§1–4 核心方向)于本日增补批**定稿 §1–4**;2026-09-07 门禁排期项("最晚在 D1 GDD 前创建,至少 §1–4,'格子样张测试'并入执行")就此闭合。AD-ART-BIBLE solo 跳过(review-mode.txt = solo,出稿待人工复核)。 + +- **范围声明**:定稿范围 = §1–4(视觉身份宣言/氛围与情绪/形状语言/色彩系统);§5–9(角色/环境/UI/资产标准/参考方向)未建,**不阻塞 D1 GDD**——D1 消费面 = §1.2 绘制优先级栈 + §1.4 格子样张验收标准 + §3 形状语言 + §4 色彩系统 + §4.6 图标键契约。 +- **本批增补内容**:① 绘制优先级栈增补**徽章位**(数字 > 小人 > 旗 > 徽章 > 纹理 > 底色——极性提案 §0 栈位声明落位);② 雷格隐喻换轴同步(雷 = 被惊动的巢穴,"机关"旧称退役——game-concept 2026-09-12 换轴;兑现档红更名危险红,色值不变);③ **§1.4 格子样张测试验收标准成文**(第一验收项 = 数字可读性硬指标,game-concept 早期风险第 1 条;雾格不在样张范围);④ **§4.6 图标键契约冻结**(效果图标 B3 数据六行 + 提案新增 sapped;奖励徽章 B4 默认根八行 + clear_blast 并列预留;格面类型图标五类——极性提案 R1;敌种线索键 B2 Rule 8——键与通道分配冻结、终稿美术归样张/asset-spec 批);⑤ 雾格资产规格一行占位(内容封禁期暂缓,雾重设计批再启)。 +- **D1 GDD 引用义务**:D1 既有台账(A2×6/B2×4/B3×4/B4×5/B1×3)凡涉 icon/线索/徽章的呈现义务,一律以 art-bible §4.6 键契约为视觉侧依据;键集扩容(新效果/新奖励/新敌种/新类型)走 art-bible §4.6 修订,禁运行时换图。 +- **待随批事项**:极性提案实施批须按其 §3.5 正式登记 D1 义务条目入本索引 D1 台账(未开格类型图标/极性视觉语言/负向文案族);registry terminology 增 图标键/徽章/极性 三词条(本批已登记)。 + +## 门禁增补记录(2026-09-13,D1 定稿登记) + +> `design/gdd/d1-hud-framework.md` /design-system 完成:12 节 + 台账表/冲突清单零占位(solo 模式,出稿待人工复核;/design-review 须新会话)。registry 已登记 `d1_presentation_surface` 条目 + terminology 两词条(命令式回执驱动/程序化占位)。 + +- **义务台账核销(缺项即 blocking 项闭环)**:D1 既有台账(A2×6 / B2×4 / B3×4 / B4×5 / B1×3 / A4×3 / A3 + A1 EC5——battle.md §13 27 项)逐项落位表见 D1 GDD Dependencies(门禁评审核对用);已裁决项直接继承 battle.md(双舞台五区布局 / 路径预览=合法性指示 / 悬停预承诺 / 四格态 / 雾图例与首遇 / 四类明细行 / 窗口单件原位激活 / 高光行强调 / 库存关卡域清空反馈 / B3 空态=槽隐藏 / EC5 零合法板=仅提示)。 +- **切片教训三条硬性要求兑现(entry 17,D1 GDD 门禁评审核对项)**:① 信号载荷 schema 13 项成文表(Core Rule 17——A2 三事件/B1 行协议/B4 窗口载荷引用既有 schema + ux 遥测六行与命令式刷新面新成文)② 场景级 headless 测试进 AC(AC-19 战斗屏探针 + ADR-0004 双载体)③ 信号时点状态可见性规则(Core Rule 18:销毁前捕获/载荷自包含/禁回读核心生命周期态 + AC-20 静态扫描与像素重放断言)。 +- **D1→上游接口漂移登记 ×3**:① A3 只读观测量扩列轮内已用刷新次数(刷新按钮"本轮剩 N 次"读数缺口——OQ2)② B1 `settlement_snapshot` 契约登记(切片已实现、GDD 未登记——OQ3,D1 现行消费 = 步骤流判定行 + presentation_snapshot() 零阻塞)③ 极性提案实施批 D1 义务条目(冲突④)。 +- **D1 侧裁决两条**:① MVP 格面徽章只在已开格呈现(B4 Rule 8"未开格身份不呈现"为现行锁定规则——art-bible §4.6.3 未开格预读的机制前提随极性提案实施批,冲突⑤)② 订阅纪律按 ADR-0001 收窄(呈现层只订 B1/runner;battle.md §Events Fired 措辞差列冲突⑥待复检批对齐)。 +- **下游交付物(B5/D4/D5/A5/D6 输入)**:五区布局与双舞台裁决(第三形态)+ 键位映射表(§4.6 全键族)+ 呈现语义(敌人卡死亡禁瞬消/明细带/高光行)——B5 只叠加动画密度;D4 挂点 = 明细行类型流 + toast 触发;D5 承载位 = toast 体系 + 图例面板;C4 = TunePanel 退役接收方。 + +## 门禁增补记录(2026-09-14,D1 design-review 收口) + +> `design/gdd/d1-hud-framework.md` /design-review(lean——用户指定快速单会话,未生成专家代理):NEEDS REVISION 2 blocking 同日修订核销,用户拍板免复评放行 → **Approved**。日志 `design/gdd/reviews/d1-hud-framework-review-log.md`;lint 门禁 PASS。 + +- **Blocking ①(spec-AC)**:F1 棋盘格公式与自声称输出/AC-2 矛盾——固定边距下 36/45 数学上不可同时成立(两定值所需可用高度区间 [432,443] 与 [450,459] 不相交),示例自算实为 38/46 → 用户裁决改**两档定值(battle.md 拍板 36/45)+ 可行性校验式**(`floor(min/N) ≥ cell` 且 ∈ [30,45] 装板期断言);AC-2 措辞同步。 +- **Blocking ②(接口漂移)**:切片实现的 B1/B2 呈现面 API(`presentation_snapshot` / `is_battle_live` / `stock_count` / `query_reward_identity` / `submit_window_selection` + `hero_unit` / `all_enemies`)未在 owner GDD 登记契约——D1 冲突⑧/OQ3 扩条登记,回 B1/B2 修订批补登记(B5 GDD 前);registry `d1_presentation_surface` 为现行契约载体。 +- **顺路核销**:状态头 C3 状态更正(Designed 非 Approved);C1-lite 切片代码契约注记(C1 GDD 定稿时回填);新增 AC-26 可访问性基线(字号下限/无闪烁 >3 次/秒/减动效/纯鼠标)+ 覆盖矩阵 Rule 22→AC-23/26。 +- **MVP 设计链收口注记**:D1 放行后设计顺序 1–10 位(D2/A3/A4/B3/A1/A2/B2/B4/B1/D1)全部 Approved——MVP 玩法与呈现层 GDD 齐备,下一站 A5(垂直切片里程碑首位)。 +- **命名统一核销(session-state entry 35)**:request_refresh / refresh_cap_per_round / install_board 受理域语义 / round_end_board·round_end_stopped 实名——D1 GDD Rule 20 对照表 + AC-25 grep 断言;切片四 provisional 名退役。 + +## 门禁增补记录(2026-09-14,A5 定稿登记) + +> `design/gdd/a5-minesweeper-aux-items.md` /design-system 完成:lean 格式 + lint PASS(表格占比 84%),solo 模式一次会话定稿——专员/总监代理全部跳过,出稿待人工复核;/design-review 须新会话。registry 已登记 `a5_aux_items` 条目 + terminology 四词条;`variation_table.mvp_content.counter_item_ids` 回填 [probe_dust](A4 预授权"A5 期回填"兑现——A5-1 义务闭环)。 + +- **设计指令兑现(用户 2026-09-14)**:"每个维度都要有对应道具" = 五维度框架 {info, risk, action, plan, counter} + F2 覆盖谓词(五维度各 ≥1 活行的加载后内容审计断言,AC-12 机器可查)+ A4×③ 框架契约(未来变异建行时随批配 ≥1 反制道具);"稀有道具高数值与多维度结合" = 稀有两行 tier 3 超现行池最高档 + 双 effect_kinds 组合(地穴全图卷 = 全雾显影×全图预读;爆破工兵包 = +4 AP×3×3 真视)、稀有读数 ≈0.17 件/板(F1) +- **承重护栏(P1 防塌方)**:真视窄域——替代推理的效果(mine_reveal)目标域 ≤3×3、全图级仅限恢复推理原料类(显影/预读);schema 校验强制(夹具 a5_target_violation) +- **接口漂移预授权 ×3(对侧修订批兑现,缺项即 blocking——A5 GDD 义务清单 21 项)**:① **A3 grant_ap 第 9 API**(`grant_ap(n) -> bool`——Active 相位受理、remaining_ap += n 无上限域、零随机;A3 GDD 修订批登记)② **B4 category 扩值 aux_item**(预留枚举激活——Rule 1 修订;effect_id 双表校验 + use_timing 扩 during_dig + 默认根六行 + 池总权重算)③ **D2 AuxItemTable 新表**(A5 回填 owner——七字段 + 校验族 + 两处表值回填) +- **零特判延伸声明**:A2 代码零 A5 分支——叠加层为 A5 自持派生信息层(显影/真视/预读三集)、零回写 A2/B4 状态、随图消亡;A5 零随机零事件订阅零 UI(B4 Rule 10 同款编排被动性) +- **既有 OQ 收口 ×3**:A4 EC6 显影语义定稿(fog_reveal = 恢复推理原料非排雷、时效 = 本图持续)· A4 OQ2(探测时效/定价/counter_item_ids 首引/悬空校验)· B4 OQ2 A5 侧(呈现深度 = 道具驱动预读、自动寻路不做) +- **下游义务登记**:B1×①②③(use_aux 编排/生命周期/授予路由——缺项即 blocking)+ D1×①…④(库存条/使用交互/叠加层呈现/清空反馈——缺项即 blocking);A2 零义务(零特判延伸——A2 GDD 不改) +- **生效前提**:B4×①…⑤/D2×①② 修订批兑现前,RewardTable 含 aux 行会加载致命(B4 Rule 1 预留值拒绝 fail-fast)——A5 GDD AC-12/13 登记 deferral,对侧义务登记即生效条件 +- **UX Flag**:A5 有 UI 贡献(库存条/使用交互/叠加层呈现)——Phase 4 前置 /ux-design;Asset Spec 候选挂 art-bible 样张批 + +### A5 评审修订登记(2026-09-14,solo 快速审核) + +> `/design-review a5-minesweeper-aux-items.md --solo`:Verdict NEEDS REVISION——3 blocking + 5 recommended 同日核销(v1.1-lean,lint 复跑 PASS);用户拍板两项边缘规则:① **area_3x3 边缘窗口照常受理**(resolved_target = 窗口 ∩ 板内格,角 4 格/边 6 格——拒绝案否决,防烧包感反噬护栏初衷)② **零信息目标不可用不消耗**(板级判据 → 图标灰/入口禁用〔B4 usable_now 先例〕;点击级 → 拒绝零消耗;组合行判据取 OR——爆破包 ap_grant 恒可用;真视零雷恒有效——零雷 = 信息)。承重修复:F3 方向自相矛盾改写(P(雷) 对 ι 递增——收益在盲赌暴露下降非单赌命中率,registry formulas 同批同步)· 使用目标校验谓词表(Core Rule 8 展开)· EC17/EC18 · AC-15/AC-16(真视/预读运行时路径补测);同日用户拍板免独立 re-review 直接放行(Approved——B2/B1/D1 先例);日志 `design/gdd/reviews/a5-minesweeper-aux-items-review-log.md`。 + +## 门禁增补记录(2026-09-14,C3 design-review 修订批) + +> `design/gdd/c3-difficulty-curve.md` /design-review(lean 单会话——用户指定不生成专家代理):NEEDS REVISION 8 blocking 同日修订核销,**用户拍板免复评放行 → Approved**。日志 `design/gdd/reviews/c3-difficulty-curve-review-log.md`;C3 v2 lint PASS。**B4/B1/B2/D2/registry 同批接口漂移回写(X-6~X-10)**。 + +- **八 blocking 核销**:① 遭遇随机化 owner 侧登记(B1 encounter 掷骰步 + AC-5 scope 腿 / B2 spawn override 参数 + unit_count_max + 物种集三敌种 / D2 EnemyTable schema +2 / registry enemy 十键)② F6 关级供给带退役 → **生存链区间模型**(用户设计:每战 S/K 区间三档判定〔绿/黄/红〕+ 关级通关恒等式 + 输出占比带 + C4 区间平移换算率)③ MVP 行值总表 v3 转正(切片 .tres 权威源;r_boss 记录值更正 0.98 = 150/153.5 期望口径)④ registry boss_hp_anchor 回写 [0.5,1.1] + 加难走廊 [1.1,1.35] ⑤ 条款⑤计价基元重推(clear_blast C=40,缺口 6.1/51.8——clause5_dominance.c3_ruling)⑥ 供给控制器/clear_blast 换位/K_dmg=0/armor_type 四批转正 ⑦ F7 六步→七步(增 B4 登记常数与对账重制步)⑧ F5 真人方向更正 + 带锚定稿。 +- **用户拍板四项**:切片奖励侧三改动(clear_blast 换位 / K_dmg 1→0 + 战斗首件保证 / 供给控制器归 B4)全采纳转正 · armor_type 克制批纳入 MVP(goblin=none / bugbear=a / ogre_chief=b,克制矩阵入 SettlementTable)· 三带锚定稿(L1 通关率 [30,50] / 通关余量 p90≤13 & p10≥3 / L1 形态带:教学位绿 + 中段黄 + Boss 黄不红)· 加难杠杆批内即调。 +- **加难杠杆与 OQ7**:杠杆候选 = r_boss 走廊(Boss 180–207)/ 群战上探 / 供给 decay 0.6——**扫描待执行**(本会话无 godot 运行时;F7 第③步 level_monte_carlo 四表对照),落带前 v3 值 provisional-pending-scan;真人终标定仍归 playtest 批次 2(OQ3)。 +- **DND 更名批对齐**:grub→goblin、ambusher→bugbear、boss_guardian→ogre_chief——B1/B2/D2/registry 已 sweep;D1 冲突② 键集对齐随之落定(D1 GDD 侧冲突条目收尾注记留其下次修订批)。 +- **登记在案 follow-up**:① B1/B2/B4/D2 存在**既有 R8 lint 债**(核心规则表要点列编号引用——lint 工具本日更新晚于四 GDD 批准时点,非本批引入;各自下次修订批清扫,本批触改行零新增违例)② B2 多敌夹具与 AC 记号(bugbear 行)随 B2 实现批核验 ③ OQ7 扫描执行 ④ B4 F5 保底语义迁移后其 AC-6/AC-7 断言措辞随实现批对齐。 + +## 门禁增补记录(2026-09-14,C2 定稿登记) + +> `design/gdd/c2-adventure-structure.md` /design-system 完成:lean 格式 + lint PASS(表格占比 86%),solo 模式一次会话定稿——专员/总监代理全部跳过,出稿待人工复核;/design-review 须新会话。registry 新增 `level_table` / `adventure_table` 两条件目 + 补登 `hero_override_parameter_passing`(切片 E4 批实现、登记缺口闭合)+ terminology 三词条(HP 账本 / 关卡收束点 / 关卡大奖励)。 + +- **C2 待决四问收口**:① **关卡序列结构** = MVP N=2 线性(L1 地牢基线 → L2 雾解锁 + 数值台阶),冒险通关 = L2 Boss 击杀;加关 = 内容批(链行 + C3 数值 + 变异解锁同批——序列覆盖义务,越界关卡 = 内容生产缺口);N=2 生效前提 = C3 L2 深度轴行值填充批(enemy/board_gen (2, D1–D4)——OQ1 机制兑现,未落地前 N=1 单关冒险为合法态)② **关卡大奖励** = 三件固定构成(max_hp +10 经 begin_battle 覆盖值通道 · 血回满【与 C3 满血开局校准口径闭合——通关率带跨关合成的前提】· 代币记账 +10/关)——不进库存域、末关通关不兑付 ③ **失败重开** = 死亡即冒险终(Roguelike 已锁)、重开 = 新 master_seed 全新一轮、放弃同失败型结算(非战斗期受理,MVP 无 UI 入口——语义预留)④ **C1/C2 边界** = C2 序列/结构层(冒险状态机/HP 账本/大奖励/重开/种子接线),C1 关内节点行进层(行进/兑付触发/战斗发起);收束点语义 = C1 先调 B1 level_clear 再向 C2 宣布 level_end——接口契约四件套(start_level / report_battle_end / heal_clamped / level_end)为 C1 GDD 强制输入(C2 在 C1 之前设计,边界措辞即 C1 接口基线) +- **切片先例转正切分**:level_runner 关卡域生命周期切两半——冒险域(序列推进/HP 账本/大奖励/重开/种子)归 C2 侧新 owner,关内行进与节点兑付触发归 C1;两处切片简化由 C2 转正改写:① 切片 start_level 逐关播种 → 冒险域单种子(关卡间 RNG 连续)② 切片每关开始 HP 回满 → HP 跨关延续(恢复只来自恢复点节点与大奖励两个显式口) +- **下游义务登记**:C3×①(L2 深度轴行值 enemy/board_gen (2, D1–D4)——N=2 生效前提)· D2×2(RngService 种子注入入口登记 provisional 名 reseed / AdventureTable 建表——接口漂移)· B1×1(begin_battle 入参供给方转正——依赖表 C1/C2 行收编,随 B1 修订批)· C4×2(token 记账消费 / 天赋注入位——远) +- **UX Flag**:C2 有 UI 贡献(冒险摘要结算画面 / 关卡推进指示 / 放弃入口非战斗期)——Phase 4 前置 /ux-design;无新增资产需求 + +## 门禁增补记录(2026-09-14,跨评审回写批:D2 漂移 + B3 接口漂移 + B1 重算) + +> /review-all-gdds(`design/gdd/gdd-cross-review-2026-09-14.md`)§七 批调度第 1–2 批执行完毕;修订纪律 = 正文只写现行规则 / 版本历史各一行 / registry 同批回写 / gdd_lint PASS(B3/D2/B1 三文件,本批以核心规则表增依据列完成 R8 引用列迁移)。进度追踪表三行状态自 Needs Revision 回滚(B3/D2 → Approved,B1 → Designed——历史放行记录见各 review log 与跨评审报告附录)。 + +- **批1·D2 漂移批(🔴-2 五处 + X-8 补登)**:RNG 流 2→3(encounter——盐值/黄金向量第 6 常量/全流零计数前提)· pool_tag 增 active_inject · RewardConfigTable +2 字段(active_p0/active_decay)+ k_dmg 默认 0 禁区条款解除 · 默认根行集 clear_knife→clear_blast · HelperItemTable→AuxItemTable(A5 定稿转正,schema 目录九→十张);X-8 补登 = SettlementTable 逐表字段行补 4 字段(十二字段)+ EnemyTable MVP 三敌种行;接口漂移修订规则增"回执按受影响字段/枚举粒度逐表 diff schema 目录"流程纪律。 +- **批1·B3 接口漂移批(🔴-1 前半)**:defense_up.magnitude −50→**−30**(减伤墙批 C3 投值回写落地——B3/registry 同批)+ 免伤轮叙述同步(defense_up 单侧不可达 D'=0,现行可达路径 = weaken 饱和);registry terminology.免伤轮 词条同批。 +- **批2·B1 重算批(🔴-1 后半)**:F0/F1/F4/F5 按现行三输入(duration=1、m_d=−30、C3 v3 曲线 d0.25/t0.88 换锚)重推——**条款③两参考板形翻为破线**(锚行 ≈0.48×/0.47×;E_dig 重推 7.2→4.5;满配 ≈0.16×),旧"通过"回执退役、破线升格 C3 曲线级约束(**V_safe 侧杠杆重议**——defense_up.magnitude/duration 纳入禁独立调参联动网络 m_w↔D_ref↔m_d↔duration_d);条款⑤四态重推(保守地板需求 110.3/166.8,C3 裁决②维持);F1 对照表按现行行集重登(attack_up 死行注记清除——锚对照实物改 attack_up 行,registry 回迁口径重跑兑现);**W3 充能规格 = B1 F6**(累积时点/单轮 cap/不吃 percent/逐分量甲效查表 floor)+ AC-44;**W6** 挖掘期刷新改每轮 cap 2(RoundStart 重置);AC-22/36/37/39/40/41 改写;registry anchor_warning / clause3_supply / clause5_dominance 三处重登回执。 +- **登记在案 follow-up**:① W2 护栏 B 复核 + 护栏 A 重推(本批已登记旧值链待重推注记与方向性读数——护栏 A ≈−1.1 越下界)→ C3 收口批三选一落档 ② B3 F0 锚对照读数已随批同步(attack_up 参照)③ A3 条款①扩列(W3 另一半)归零散批 ④ W7/W11 等 C3 收口批项不受本批影响。 + +## GDD 设计顺序 + +1. D2 数据配置与数值表 +2. A3 行动经济 +3. A4 地形变异 +4. B3 增益/状态系统 +5. A1 扫雷棋盘生成 +6. A2 排雷玩法规则 +7. B2 敌人系统 +8. B4 奖励与道具经济 +9. B1 战斗结算规则 +10. D1 HUD 与界面框架 +11. A5 扫雷辅助道具 +12. C3 难度曲线与精英/Boss +13. C2 冒险结构 +14. C1 关卡节点地图 +15. B5 战斗表现 +16. C4 元进度 +17. D3 存档序列化 +18. D4 音频 +19. D5 教学引导 +20. D6 设置/本地化 + +## 进度追踪 + +| ID | 系统 | 状态 | GDD 路径 | +|---|---|---|---| +| D2 | 数据配置与数值表 | Approved | `design/gdd/d2-data-config.md` | +| A3 | 行动经济 | Approved(2026-09-07 三轮 re-review 8 条文本级 blocking 同日核销;用户拍板免独立 delta 核销直接放行。F1 五条款契约与 refresh 玩家意图路径为 B1/B4/D1 GDD 的强制输入;日志 `design/gdd/reviews/a3-action-economy-review-log.md`) | `design/gdd/a3-action-economy.md` | +| A4 | 地形变异 | Approved(2026-09-08 两轮独立评审:full 8 阻塞 + 同日 re-review【六专员 + CD 终裁】6 阻塞均同日核销;CD 终裁 delta 核销免第三轮,用户拍板放行【A3 先例】;承重数学(厚度≤2【≡任意 3×3 窗口非全雾】/f≤8d/两态 AP/F1 谓词)经两轮对抗零动摇;日志含两轮阻塞→修复对照表:`design/gdd/reviews/a4-terrain-variation-review-log.md`)。**门禁登记(2026-09-08 re-review 补全——下游 GDD 门禁评审逐项核对落位,缺项即 blocking)**——**A1×15**:① 候选池门控(F1)② cell_constraint 生成期强制 ③ 出生区及生成期可达域永不含雾(可达域 A1 确定性计算)④ 可推理率按雾边双向净效应建模(寂静减项 × 推理锚增项,禁单向补偿)⑤ 清图预算增量构成核算(雾致预算增量 = 链不可达雾格数 × dig_ap_cost,设计期聚合校验、违例 = 调参修正)+ 链不可达雾格占比生成期计算输出(B1 对账输入生产者义务)⑥ 空池静默 ⑦ 降级静默 + 产物合法 + 部分达成按 shortfall 分级上报(EC2)⑧ 多变异选取消费 board_gen 流(回放确定性)⑨ EC3 邻接出生区合法(谓词 = 出生 3×3 及可达域内无变异格)⑩ EC10 雾占比参数域(< 全覆盖 × 可推理率联动,可判定谓词)⑪ EC11 双雾区实例独立(布置身份与 board_gen 取号独立)⑫ 厚度≤2【≡任意 3×3 窗口非全雾】/ f≤8d 参数域 ⑬ EC2 判据含连通分量尺寸 ⑭ 降级率按 (level_index, node_depth) 埋点 + 最低雾出现率义务【批量统计断言形式,阈值与样本量归 A1 GDD 定】+ 禁放宽 Rule 3 达成 ⑮ board_biome 非空域校验(fail-fast)。**A2×4**:EC4 连锁开雾格(无数字 + 级联停止 + 计入清图 + 奖励入库)· EC5 雾内旗恒不中(含标错惩罚上线后一致性推论)· 开格上报携带变体标记(数字显示判定数据源)· EC8 棋盘持有生成期变异快照、抗表重载。**D1×3**(2026-09-12 更正:原记 ×2 系索引漏记第③项——A4 GDD UI Requirements 明写"三个呈现义务"):① 图例"迷雾 = 无雷盲区"条目(恒在、不设解锁门、可回看)② 首遇提示(触发 = 首个实际含雾板进入可操作状态、每 run 一次;必含要点 = 无雷盲区 / 雾格不传播连锁 / 雾内旗纯损;非模态不劫持输入)③ 已开雾格 = 地板无字 + 底色层 tint 残留与雾区全开一次性完成信号(验收候选转正——OQ8 已于 design/ux/battle.md 裁决 2026-09-12:采纳 tint + 视觉合并域信号单元;详见 A4 UI Requirements③)。**B4×3 定价硬约束**:雾内奖励档位下界 ≥ 最低档安全格 buff 当量 · 清图奖励锚随雾成本负担(链不可达同分母)上浮 · 雾板净当量优势带上界(防福利)。**B1 条款 5 输入**:三腿入账两态验算 + 雾板净当量优势带 + 防钓鱼方向(对账 A3 OQ6)+ 链不可达占比(A1⑤ 生产者)。承重规则不变:生成期布置约束(雾区仅非 0 安全格【区内无雷无 0 格】)+ 零特判原则;"雾区无雷"为玩家可见知识;A3 F1 条款 5 对账注记三腿入账(非"消解");registry variation_table / variant_pool_gate / fog_ap_note 已同步改写 | `design/gdd/a4-terrain-variation.md` | +| B3 | 增益/状态系统 | Approved | `design/gdd/b3-status-effect.md` | +| A1 | 扫雷棋盘生成 | Approved(2026-09-09 full 评审 MAJOR REVISION NEEDED——8 blocking 同日修订核销 + cheap-now 6 项顺路核销,用户拍板免独立 re-review 直接放行【A3/A4 先例】。承重修订:**P1 可推理率两项达标判据**(inferability_core = 剔除雾格后的可推理率,防止"雾区无雷"知识单独撑线)+ **target 域 [0.85,1] 绝对锚**(断言 A″)+ **雾占比绝对上限 0.25** + **F2c 成本闸**(attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生)+ 雷数取整/布雷钉死(round 先定数、先布后删)+ F2≡Schema 合流(count=0⇒f=0 腿)+ AC-6(a) 芯/环改写 + AC-11 前提腿 + **断言 B 收紧达成区占比**(A4⑭ 两件套落位)+ **第三轴登记约定**(Boss=Dmax 行 + elite_/boss_ 命名空间预留 + OQ6 C1 定稿强制重审);σ 打包防碰撞修复;D2/registry 接口漂移已同步。recommended 9 项未核销留档(AC-10(d) 条件化、⑬ 差分夹具、板ⅲ′ 对照板、O4 登记等——见日志);日志 `design/gdd/reviews/a1-board-generation-review-log.md`) | `design/gdd/a1-board-generation.md` | +| A2 | 排雷玩法规则 | **Approved**(2026-09-09 full 评审:四专员〔game-designer/systems-designer/qa-lead/ux-designer〕+ CD 终审 NEEDS REVISION 六 blocking 同日修订核销,用户拍板放行【A3/A4/A1/B3 先例】。承重修订:**连锁不越旗**(GD-1——传播集 ∖ G,旗 = 1 AP 链阻断器)· **F4 到达序声明收窄**(划分序无关仅异类四组合)+ AC-10 耗尽型救援/联合清图用例 · **ActionOutcome.sub_reason 封闭枚举**(拒绝可辨数据契约)· **D1×6 义务包**(格态可辨/悬停/雷格可读/路径语义/单意图帧/voided 呈现)· AC 修复包(AC-6 编号/b_chain·b_fog spawn=(3,2)/诊断五类枚举/ap=1 同帧竞争)· 用语纪律清扫 + registry terminology 条目;随批 Rule 11 有价捷径注记 / OQ1 惩罚重复性裁决项 / B1·B4 高光与溢出两条登记。承重结构〔确定性引擎/三事件面/F1–F4/零特判〕经对抗评审零动摇;日志 `design/gdd/reviews/a2-minesweeper-rules-review-log.md`。**下游义务见门禁增补记录 2026-09-09 A2 块:A1 修订义务(🔴 反例 B)、B1×3 + 高光/溢出两条、D1×6,缺项即 blocking**) | `design/gdd/a2-minesweeper-rules.md` | +| B2 | 敌人系统(战斗单位系统——CD 门禁扩容身份) | **Approved**(2026-09-09 一次会话定稿 + 同日 full 评审〔六专员 + CD 终裁〕NEEDS REVISION 8 blocking 同日核销、用户拍板免独立 re-review 直接放行【A3/A4/A1/B3 先例】。lean 定稿:lean 门禁 systems-designer〔Formulas〕/qa-lead〔AC 17 条 + 覆盖矩阵 + 缺口预检 7 项〕已咨询,CD 视角随 full 评审补位。定稿要点:**多敌模型**(1–3 同敌种单位——B3 单敌假设回验不成立,side-wide 零接口变更兼容 + target_ref 扩展位;registry single_enemy_assumption 已改写)· **HeroTable 新增**(接口漂移回写 D2,单行全局,atk=0 机制开关)· **意图视觉推测型**(不预告数值/行为,敌种图像线索稳定可学、全局恒定——CD 承重项定稿 + full 评审补预告时序⑤)· **Peglin 行为类型学转译**(行为环确定性轮转:attack/charge/heavy MVP,guard/boost/hinder/summon 预留校验拒绝;双敌种 grub/ambusher 双环;死亡即不动手 + 同步轮转定理)· **伏击量纲定稿**(每攻击类行为 +3 HP 附加、charge 零兑付——registry OQ2 关闭条件)· **cap 默认 4**(反灾难护栏;旧 8 溢出协议死码废弃)· 条款 3 供给 = 单结算自然边界(用户定稿)· EnemyTable 八字段回填 + 每行集恰一敌种校验。**下游义务 B1×6 + D1×4 缺项即 blocking**(① 增调用序成文;D1×4① 增预告时序/档位、② 全局恒定)。full 评审承重修订:**当前内容态无胜利路径声明**(B4 先行 = 可通关先决)· 资产下限补受击/死亡/顺位 · 多敌夹具根改 ambusher 行(三处死测试修复)· AC-7 恒假判别加 m_e≥2+SKIP · OQ6 事件源更正(flag_judged 非 mine_hit)· OQ7 精英/Boss 编码位预登记(C1 强制核对)· 主角推进域排除 · F4 裕度 2.3× + min(AP,cap) 收紧(registry 同步);**新增 OQ7/8/9**。承重机制〔F1–F4 实算/同步定理/side-wide 池〕经对抗评审零动摇;日志 `design/gdd/reviews/b2-enemy-system-review-log.md`) | `design/gdd/b2-enemy-system.md` | +| B4 | 奖励与道具经济 | **Approved**(2026-09-09 full 评审〔五专员 + CD 终裁〕NEEDS REVISION——14 blocking 同日修订核销,用户拍板免独立 re-review 直接放行【A3/A4/A1/B2/B3 先例】;核销质量依据 = 全部 blocking 为文本级/附表级、承重机制〔预分配确定性脊柱/封闭 RNG/F1–F6 边界实算/编排被动性/AC 架构〕经五方对抗零动摇。承重修订:**窗口锚点移授予段后**〔用户评审拍板:当轮拾取当轮可用〕· **跨文档真相分裂修复**(D2 正文两处旧 reward_roll 语义补同步 / 锚参照迁移三处传播 / game-concept 三处注记)· **预读声称如实降级**(创意风险 #1 维持 OPEN,寻路归 OQ2/A5)· **RewardConfigTable 新增**(Rule 1 载体)+ RewardTable 第九字段 display_name + F5 加载护栏两条致命(no_damage_row 关闭零伤害世界后门,B2 OQ9 并案)· 默认根行集登记 + 刀供给方向预检 · API 输入域补裁(E18–E21)+ AC-14/AC-3 矛盾消解 + σ 预推进/纸面契约声明 · E10 理由更正(拍板不变)· 失衡预警①–⑧ 重校准 · A4②③ MVP 休眠显式宣告 · 用语纪律清扫 + terminology 六词条。lean 定稿要点(2026-09-09):install 期身份预分配 / 库存战斗域 / 恒定表值(attack_up 死行)/ F5 草稿重 roll / 道具段照常消耗 + 清图槽允许 buff 行两拍板。registry `reward_table` + `reward_config` 两条目;**下游义务 B1×6 + D1×5 缺项即 blocking**(见门禁增补记录 B4 块 + full 评审增补块);缺口预检 2 项阻塞级 deferral 至 B1;日志 `design/gdd/reviews/b4-reward-item-economy-review-log.md`) | `design/gdd/b4-reward-item-economy.md` | +| B1 | 战斗结算规则 | Designed | `design/gdd/b1-settlement.md` | +| D1 | HUD 与界面框架 | **Approved**(2026-09-13 /design-system solo 模式一次会话定稿;2026-09-14 /design-review lean 评审〔用户指定单会话,未生成专家代理〕NEEDS REVISION 2 blocking 同日修订核销,用户拍板免复评放行【A3/A4/A1/B3/B2/B4/B1 先例】。承重结构〔27 项义务台账/订阅纪律 ADR-0001/13 项载荷 schema/切片教训三条硬性要求/AC 架构〕经逐项对上游核实零动摇;blocking 均为文本级:① F1 公式与 36/45 输出矛盾(固定边距下数学不可同时成立——spec-AC 阻塞标准)→ 两档定值 + 可行性校验式〔用户裁决〕② 呈现面 API 接口漂移登记扩条(冲突⑧/OQ3:B1×5 函数 + B2×2 投影 + settlement_snapshot 信号——registry `d1_presentation_surface` 为现行契约载体,回 B1/B2 修订批补登记);顺路核销:C3 状态头更正 / C1-lite 切片契约注记 / AC-26 可访问性基线;lint 门禁 PASS;日志 `design/gdd/reviews/d1-hud-framework-review-log.md`。定稿要点:**呈现义务台账 27 项逐项落位**(A2×6/B2×4/B3×4/B4×5/B1×3/A4×3/A3+A1 EC5——GDD Dependencies 落位表,battle.md §13 承接升格系统级验收)· **切片教训三条硬性要求兑现**(entry 17:信号载荷 schema 13 项成文表 Core Rule 17 / 场景级 headless 探针 AC-19 / 信号时点状态可见性规则 Core Rule 18 + AC-20)· 订阅纪律按 ADR-0001 收窄(呈现层只订 B1/runner + 命令式回执驱动五面成文——registry `d1_presentation_surface` 登记)· **命名统一核销**(entry 35:request_refresh / refresh_cap_per_round 正式名,AC-25 grep 断言零残留)· 程序化占位策略(art-bible §1.4 引用、样张不阻塞、占位同受数字可读性硬指标)· _draw 维持 GridContainer 裁决(ADR-0003 Decision 3——复议留样张后,OQ1)· 冲突清单 ⑧ 项(含⑤ D1 裁决:MVP 格面徽章只在已开格呈现——B4 Rule 8 承接,未开格预读随极性提案实施批)· 接口漂移登记 ×3(A3 轮内刷新计数读数缺口 / B1 settlement_snapshot 契约 / 极性提案批 D1 条目) | `design/gdd/d1-hud-framework.md` | +| A5 | 扫雷辅助道具 | **Approved**(2026-09-14 /design-review --solo 快速审核 NEEDS REVISION——3 blocking + 5 recommended 同日核销,用户拍板免独立 re-review 直接放行【B2/B1/D1 先例】。用户拍板两项边缘规则:① area_3x3 边缘窗口照常受理〔resolved_target = 窗口 ∩ 板内格〕② 零信息目标不可用不消耗〔板级图标灰/入口禁用、点击级拒绝、组合行判据取 OR、真视零雷恒有效〕。定稿 = 2026-09-14 /design-system solo 模式一次会话。定稿要点:**五维度框架 {info, risk, action, plan, counter} + F2 覆盖断言**("每维度都有对应道具"机器可查)· **真视窄域护栏**(替代推理 ≤3×3、恢复推理可全图——P1 防塌方)· **MVP 六行**(四常规单维 + 两稀有多维高数值——用户设计指令直接兑现)· **获取挂 B4**(aux_item 预留枚举激活——义务 B4×①…⑤)· **使用经 B1 挖掘期编排**(use_aux 规范序——义务 B1×①②③)· **板级叠加层**(零特判延伸——A2 代码零 A5 分支)· **A3 grant_ap 第 9 API 接口漂移预授权**(义务 A3×①)· 21 项跨 GDD 义务清单 + F1–F3 + 16 AC + lint PASS;A4 EC6/OQ2 与 B4 OQ2 收口。**注意:B4/D2/A3 修订批兑现前 aux 行入池会加载致命(生效前提显式登记)**) | `design/gdd/a5-minesweeper-aux-items.md` | +| C3 | 难度曲线与精英/Boss | **Approved**(2026-09-12 /design-system solo 定稿;2026-09-14 /design-review lean 单会话——NEEDS REVISION 8 blocking 同日修订核销,用户拍板免复评放行【A3/A4/A1/B3/B2/B4/B1/D1 先例】。v2 修订批承重要点:**生存链区间模型**(S/K 区间三档判定〔绿/黄/红〕+ 关级恒等式 + 输出占比带——用户设计,替代关级供给带)· **MVP 行值总表 v3 转正**(切片 .tres 权威源,三敌种 goblin/bugbear/ogre_chief)· **带锚定稿**(通关率 [30,50] / 通关余量 p90≤13 & p10≥3 / L1 形态带)· **四批切片既成事实转正**(clear_blast 换位 / K_dmg=0 / 供给控制器归 B4 / armor_type 克制批)· F7 七步 · B1×1/B2×3/D2×2/registry 接口漂移回写(X-6~X-10)· 条款⑤重推(C=40,缺口 6.1/51.8)· **OQ7 加难杠杆扫描待执行**(r_boss 走廊 180–207 等候选,provisional-pending-scan)· 敌种更名批对齐(D1 冲突② 落定);日志 `design/gdd/reviews/c3-difficulty-curve-review-log.md`) | `design/gdd/c3-difficulty-curve.md` | +| C2 | 冒险结构 | **Approved**(2026-09-14 /design-review --solo 快速审批〔单会话,未生成专家代理〕——Verdict APPROVED:0 blocking / 3 recommended / 4 nice-to-have,recommended 随 C2 下次修订批或对侧批次执行;日志 `design/gdd/reviews/c2-adventure-structure-review-log.md`;lint PASS。七项已锁事实核对零冲突(C3 带锚/OQ1、B1 begin_battle·level_clear、B4 库存关卡域、A4 unlock_level=2、game-concept 死亡即终与时长、registry 四条目+三词条、lint+R8+R3);六项压力测全过——F1 合成 [6,20]% 口径闭合前提结构成立、N=1/N=2 生效前提与 AC-10 SKIP 自洽、C1/C2 边界给分支/精英留足空间、大奖励所有权三分清晰、失败重开与 game-concept 一致、两处切片简化改写经切片代码核实相符(start_level 逐关播种 / 每关回满)。承重裁决要点(2026-09-14 /design-system solo 定稿):**冒险域四件套**——关卡序列 MVP N=2 线性(L1 地牢基线 → L2 雾解锁 + 数值台阶;生效前提 = C3 L2 深度轴行值填充批,未落地前 N=1 单关冒险合法态)· 关卡大奖励三件(上限 +10 经 begin_battle 覆盖值通道 / 血回满【与 C3 满血开局校准口径闭合】/ 代币记账 10——AdventureTable 单行全局,末关不兑付)· 失败重开(死亡即冒险终、重开全新种子、放弃同失败型非战斗期受理)· master_seed 接线(冒险 = 种子域单位,零流消费);**HP 账本** = 跨战斗持久性 C2 半边兑现(registry `hero_override_parameter_passing` 补登);**C1/C2 编排边界** = 收束点在 C1(先调 B1 level_clear 再宣布 level_end)、冒险层只接结果——接口契约四件套(start_level / report_battle_end / heal_clamped / level_end)为 C1 GDD 强制输入;registry 新增 `level_table` / `adventure_table` + terminology 三词条;lint PASS(表格占比 86%) | `design/gdd/c2-adventure-structure.md` | +| C1 | 关卡节点地图 | 未开始 | — | +| B5 | 战斗表现 | 未开始 | — | +| C4 | 元进度 | 未开始 | — | +| D3 | 存档序列化 | 未开始 | — | +| D4 | 音频 | 未开始 | — | +| D5 | 教学引导 | 未开始 | — | +| D6 | 设置/本地化 | 未开始 | — | + +## 备注 + +- 原型结论已作为 GDD 素材:小人控制、AP 8、标雷判定、奖励统一结算、增益跨轮次不跨战斗等定稿见 `design/gdd/game-concept.md` +- 概念文档待定问题(主动战斗道具、标错惩罚、节点地图形态、反雪球)将在对应系统 GDD 中解决;~~停手折算比例~~ 已随 2026-09-07 A3 RAP 结构修订作废(停手 1:1 转存 RAP、清图作废) +- **概念层增补(2026-09-09,用户定稿)**:产品名 **Dungeon Sweeper** + 世界观背景(地牢下探叙事 + 机制隐喻映射)+ 美术方向"格子 = 地牢地砖"已写入 game-concept.md;P4 支柱增命名承载。均为表现层/命名层,不触碰任何已锁机制数值与接口(A1–B3 已定稿 GDD 不受影响);art-bible 任务照旧排期(最晚 D1 GDD 前),"格子样张测试"验收项不变 +- **概念层增补(2026-09-12,用户拍板)**:叙事属名换轴——雷格 = **惩罚格**(类型化属名:首发地牢类型 = 怪物巢穴;陷阱/岩浆/冰冻化身及各自机制分化登记为未来机制侧方向归 A1/A2——"陷阱→直接扣血 / 标中陷阱→怪物受伤害"与现行"开雷→敌方优势"锁定规则的取舍归机制侧,概念层不承诺)+ 插旗玩家可见动词 = **控制**(类型无关;对内文档与机制协议仍为"旗/插旗";"封印/镇/反制"降级为类型风味词)+ 连锁展开 = **坍塌显形**(B5 连锁动画 / D4 音效方向登记)+ 标错惩罚叙事 = **控制失败的失控反噬**(随机读作失控;重复性措辞随 A2 OQ1)+ 宝箱呈现仅绑定可使用道具类奖励(非道具奖励不摆箱;"搜刮/遗落物"为可选氛围框架登记不锁)+ 世界观厚度 = **碎片点缀**(不含前人日志)——已写入 game-concept.md 世界观与命名节(映射表 + 背景句,带版本注记)与视觉锚原则 5 表现措辞;均为表现层/命名层,不触碰任何已锁机制数值与接口(A1–B4/B1/C3 已定稿 GDD 不受影响);依据记录:`design/research/lore-nest-metaphor-2026-09-12.md`(⑤节 6 问同日拍板回写);D1"控制"动词手感、art-bible 旗形态/巢感样张、D5 教学措辞、D6 结算风味词输入只在记录③登记,各归各的时机 +- **概念层增补(2026-09-14,用户拍板)**:术语层级补录**行动层**——轮次挖掘期的原子决策单位:有价行动(挖/插旗/拔旗)1 AP/次、受每轮 AP 预算约束(现行表值 8),移动/停手免费不计 AP;行动/意图/动作三视角钉死(玩家决策 / A2 数据形态〔单意图帧整批〕/ A3 计价类别)——写入 game-concept 术语层级表、对应关系(轮次 ⊃ 行动 ×K)与核心循环树。同批**层名裁决 = 节点**(C1"关卡节点地图"注册名与切片 node_* 接口零改动;节点类型分类学与分支机制归 C1 GDD 展开)+ 遭遇 = 战斗事件同义词(不作层名)+ 三个 reward 拆名(节点奖励 / 安全格奖励〔B4〕/ 节点奖励覆盖层〔OQ9 spec〕)——registry terminology 四词条登记。均为命名层,不触碰任何已锁机制数值与接口 diff --git a/design/registry/entities.yaml b/design/registry/entities.yaml new file mode 100644 index 00000000..f2f63478 --- /dev/null +++ b/design/registry/entities.yaml @@ -0,0 +1,577 @@ +# 实体注册表(Entity Registry) +# 跨系统事实的唯一登记处:各 GDD 定稿的命名实体/公式/常量在此登记, +# 后续 GDD 写作时作为"已锁事实"引用——不得默默改值,冲突必须显式提出。 +# 维护规则:见 design-system 技能 Phase 5b。value 字段只能由 owner GDD 修订。 + +entities: + - name: config_db + type: service + owner: design/gdd/d2-data-config.md + attributes: + kind: autoload + role: 数值表单点访问入口(类型化属性) + policy: 禁止绕过(Rule 2,debug 注入根除外);校验失败全构建致命(Rule 6) + validation_channels: 游戏通道 collect_only=false(致命)/ 测试通道 collect_only=true(收集不升级);夹具注入 --data-root=(仅 debug) + referenced_by: [all-systems] + + - name: rng_service + type: service + owner: design/gdd/d2-data-config.md + attributes: + kind: autoload + streams: [board_gen, reward_roll, encounter] + stream_names: 当前封闭清单(增流需改代码 + P1 支柱评审;combat_var 已于 2026-09-07 full 评审删除——玩法随机不预置基础设施;**encounter 增流 2026-09-12 用户拍板**——遭遇随机化批 C3 GDD Core Rule 8,scope_event_binding = 每次战斗实例化恰一 scope,挖掘决策域之外 P1 合规) + stream_facade: open_stream(stream) 取号(计数器+1→派生→返回已播种实例);derive_at(stream, scope) 纯函数供测试/预览(不推计数器) + scope_event_binding: board_gen=每次生成新扫雷图(A1);reward_roll=每次 board install(格奖励批 + 清图奖励批)(B4——2026-09-09 接口漂移改写,原"每次奖励结算 roll 批次";B4 Rule 2/AC-1 为契约源);encounter=每次战斗实例化恰一 scope(B1 begin_battle 实例化期、扫雷开始前——unit_count 区间均匀骰,挖掘决策域之外 P1 合规) + unseeded_access: 致命中止 + snapshot_contract: 续玩/复盘快照 = master_seed + 各流 scope 计数器(消费方 D3) + referenced_by: [A1, B1, B4, C2, D3] + + - name: stream_seed_derivation + type: formula + owner: design/gdd/d2-data-config.md + attributes: + variables: [SALT_s, master_seed, scope] + output_range: "[0, 2^63)" + golden_vectors: 见 d2-data-config.md Formulas-R2 向量表(6 常量:board_gen m=42 scope0-3 · reward_roll m=42 scope0=7422916049646135537 · encounter m=42 scope0=1061841193803499209——2026-09-14 D2 漂移批 encounter 增流补向量) + salt_board_gen: 0xA0761D6478BD642F + salt_reward_roll: 0xE7037ED1A0B428DB + salt_encounter: 0x8EBC6AF09C88C6E3(2026-09-14 encounter 增流——互异奇常数 ≠ 乘数) + multiplier: 0x9E3779B97F4A7C15 + impl_spec: wrap64=二补数自然回绕;禁 float 中间量、禁对中间量用 %、禁"主动修复溢出";常量用分段表达式书写;掩码收尾 + reproducibility_claim: 同冒险内同流各 scope 无碰撞 + 跨版本恒定(不做跨 master_seed 全局单射声明) + master_seed_int64_min: 致命(abs 恒等回绕→与 0 静默别名) + set_master_seed_precondition: 仅全流计数器全零时受理(3 流——2026-09-14 D2 漂移批口径) + referenced_by: [A1, B1, B4, C2, D3] + + - name: difficulty_row_resolution + type: formula + owner: design/gdd/d2-data-config.md + attributes: + rule: (level_index, node_depth) 双维 floor 解析 + base_ceil 命名(请求低于表首向上解析);无 default 行兜底、无外推;越域请求(level<1/depth<0)致命 + warning_dedup_key: (table_id, missing_type, L*, D*) 四元组;missing_type ∈ {level_floor, depth_floor, both, base_ceil} + warning_purpose: 开发期异常信号(非审计工具);难度审计=离线曲线导出工具(C3 期交付物) + prerequisite: 每 level 至少 1 行且含 depth-0 锚行(Rule 6 校验) + referenced_by: [A1, C3] + + - name: schema_version + type: constant + owner: design/gdd/d2-data-config.md + attributes: + value: 实现期定(MVP=1);全部占位 schema 均携带版本字段 + high_version_policy: 拒读(比较逻辑 owner=D3) + referenced_by: [D3] + + - name: action_economy + type: constant_set + owner: design/gdd/a3-action-economy.md + attributes: + table: ActionEconomyTable(D2 承载,单行全局;A3 为回填 owner;2026-09-07 三次回填(RAP 结构修订)——四字段删除 + rap_refresh_cost 新增,见 A3 Schema 定稿节) + ap_per_round: 8(原型 v2 定稿,12 点已否决;双重身份:节奏 knob 兼停手 RAP 转存规模) + dig_ap_cost: 1(连锁展开免费,计入清图进度) + flag_place_ap_cost: 1(旗无实物资源/无数量上限——2026-09-07 用户修订,稀缺性由 AP 承载;限小人 8 向相邻) + flag_pull_ap_cost: 1(移除棋盘标记、无返还物——OQ2 待原型补丁复核,knob 落点) + rap_refresh_cost: 8(RAP 刷新门槛;默认 = ap_per_round,满停一轮 = 恰好一刷基线节拍) + move_ap_cost: 0(冻结:P4 探索氛围承载,非经济杠杆) + chain_ap_cost: 0(冻结:v2 定稿) + rap_rules: RAP = 停手剩余 AP 1:1 转存(结构,非 knob);战斗域生命周期(battle_over 清零,跨轮跨图保留);来源唯一(停手)、支出唯一(刷新);刷新决策主体 = 玩家发起、挖掘期(Active 相位)当场换图——D1 经组合根/B1 refresh_board 编排调用(2026-09-12 D4 修订:原"结算编排期 Waiting 代理调用"退役);每轮上限 2 次(refresh_cap_per_round 结构位默认 2——D1 提案采纳,RoundStart 重置;原"同一 Waiting 相位多次刷新合法/无结构上限"作废);旧板旗随旧板作废(零兑付、旗 AP 不返还——A2 EC11 承载);挖掘期当场刷新无结算发生(原"敌方时间税"自惩失效——A3 OQ3 阈值联动复核);cleared 轮恒 new_board、stopped/ap_exhausted 轮恒 same_board(B1 回发违约 fail-fast 不变);刷新图不产生清图奖励;无战斗收益——原 ×2/×1 折算与 stop/clear_multiplier 已作废(2026-09-07 用户拍板) + flag_rules: 旗 = A2 棋盘状态单态(原口袋旗双态作废);随图存续,清图/刷新消失;标中旗 = 怪物 debuff(三向效果定稿),兑付一次性、资格以雷格实例为键(每雷本次战斗至多兑付一次;放置实例仅为当轮结算载体;拔旗重插不恢复已消费资格——2026-09-07 三轮评审改定,OQ5/F1 条款 2) + round_end_carrier: 双发射源(2026-09-07 修订)——棋盘路径(ap_exhausted/cleared)由 A2 外发(systems-index 原裁决不变),停手路径(stopped)由 A3 直发;payload 同构 (path_tag, continuation_hint) + arbitration_priority: cleared > stopped > ap_exhausted(同帧/同批次结束路径总仲裁;stop-first 批次腿 deferral 至 A2 集成测试,义务清单见 systems-index 门禁记录) + downstream_contract: A3 F1 五条款(① 效果当量对照单一线性换算 · ② 标中旗锚严格 > 最低档 + 连锁放大边际基准 + 总吞吐对账 · ③ 挖掘风险不等式 P(安全)×buff > P(雷)×敌方增益,B1 单一闭合 owner · ④ 清图奖励 > 0 且 B4 差异化插槽(图类型战斗域恒定) · ⑤ 清图 vs 弃图刷新支配性,B1 闭合强制检查点)——2026-09-07 三轮评审定稿 + cross_field_validation: 任一动作成本(dig/flag_place/flag_pull)≤ ap_per_round——ActionEconomyTable schema validate() 跨字段类别,违规加载期致命(2026-09-07 三轮评审增补;条件性夹具登记 D2 校验双通道) + referenced_by: [A1, A2, A4, B1, B3, B4, D1, D5] + + - name: variation_table + type: schema_data + owner: design/gdd/a4-terrain-variation.md + attributes: + table: VariationTable(D2 承载,键 = variant_id;A4 回填 owner,2026-09-08 定稿回填) + fields: variant_id(String 主键唯一)/ display_name / unlock_level(int [1,999],knob)/ scope(enum region|global,global 预留 C3)/ info_mask(enum none|hide_numbers)/ cell_constraint(enum any|safe_nonzero)/ biome_tag(StringList,∅=通配,knob)/ counter_item_ids(StringList,A5 预留,非 knob——2026-09-08 re-review 修正 ✓ 误标) + mvp_content: 恰含 fog 一行(unlock_level=2【knob 活值,事实以表为准;调参须回写本条】 · info_mask=hide_numbers · cell_constraint=safe_nonzero · biome_tag=∅【knob 活值,同上】 · counter_item_ids=[probe_dust]【A5 定稿回填登记——D2 表值与悬空校验激活随 D2/A5 修订批同批落地;回填前为 ∅】);岩浆/冰冻无 MVP 行(垂直切片随关卡解锁建行,建行批随批配 ≥1 反制道具——a5_aux_items 框架内容契约) + load_bearing_rule: 生成期布置约束——雾区只能布置在非 0 安全格(区内无雷、无 0 格,A1 生成期强制);"雾区无雷"为玩家可见知识(D5 教学义务);结构推论(2026-09-08 full 评审入文,同日 re-review 钉死度量):雾区厚度≤2【≡任意 3×3 窗口非全雾,非直线行程读法】、雾面积占比 f≤8d 必要上界——A1 参数域硬边界(A1 验收清单 15 项之⑫) + zero_special_casing: 连锁/数字计数/清图/旗/价目/结算零特判——既有系统行为不感知变异状态(A2 执行逻辑无变异分支;A4 Rule 3 的推论) + rule4_exemption: 定义表非进度成长表,键 = variant_id,进度语义承载于 unlock_level 列(D2 Rule 4 豁免,schema 注释承载) + cross_field_validation: info_mask=hide_numbers ⇒ cell_constraint=safe_nonzero——VariationTable schema validate() 跨字段类别,违规加载期致命(2026-09-08 re-review 增;防未来 hide_numbers+any 行静默击穿 EC4 连锁停止;A4 夹具 variation_mask_constraint 承载) + dangling_ref_policy: counter_item_ids 为 D2 首个引用型字段——**首引已由 A5 定稿(probe_dust,2026-09-14 A5 GDD)**:悬空校验激活 + 表值回填同批(D2 修订批;条件性夹具 a5_dangling_counter 登记 A5 GDD AC-13——A4 测试口径"首个道具 id 回填时补校验"条款即 A5-1 义务,闭环) + fog_ap_note: (2026-09-08 full 评审改两态;同日 re-review 改增量构成)雾不改 AP 价目但改清图预算构成,雾格 AP 成本两态——链可达雾格(邻非雾 0 格)免费链开、链不可达雾格全价 dig_ap_cost;雾致预算增量 = 链不可达雾格数 × dig_ap_cost,计入清图总预算需求参与 A1 BoardGenTable 设计期核算(原"≤ 轮数上限 × ap_per_round"不等式作废——"轮数上限"无定义;lean 版"全价结算"表述作废);A3 F1 条款 5 对账 = 三腿入账非消解:雷险腿消解(雾区无雷无 50/50)+ 信息腿双向净效应(雾边寂静减项 × 雾边推理锚增项,禁单向补偿)+ AP 腿(链不可达全价抬高清图成本)——B1 条款 5 强制检查点输入,雾板/无雾板两态验算 + 两条方向性护栏(雾板净当量优势带上界·防福利 / 防钓鱼·对账 A3 OQ6) + pricing_constraints: B4 三条定价硬约束(2026-09-08 re-review 定稿三条)——① 雾内奖励档位下界 ≥ 最低档安全格 buff 当量(反淹没下界,per-AP 平价归 B1/B4 对账)② 清图奖励锚随雾成本负担上浮(链不可达同分母,防面积/成本分母脱钩)③ 雾板净当量优势带上界(防福利)——B4 GDD 验收项 + referenced_by: [A1, A2, A5, B1, B4, C3, D1, D5] + + - name: variant_pool_gate + type: formula + owner: design/gdd/a4-terrain-variation.md + attributes: + variables: [unlock_level, level_index, biome_tag, board_biome] + rule: pool = {v ∈ VariationTable | unlock_level ≤ level_index ∧ (biome_tag=∅ ∨ biome_tag ∩ board_biome ≠ ∅)};直接整数比较(无 D2 R1 floor);∅ = 通配语义 + output_range: pool 集合(MVP ≤1);空池合法——A1 静默布置普通图,不报错 + board_biome_type: StringSet(值域与来源归 A1 BoardGenTable biome 字段;非空为 F1 输入前提——域校验登记 A1 验收⑮,fail-fast 闭合) + downstream: A1 布置候选池门控(A1 验收清单 15 项之首——2026-09-08 full 评审由 11 项扩容,清单已登记 systems-index 门禁记录);多变异选取须消费 board_gen 流(回放确定性) + referenced_by: [A1] + + - name: status_effect + type: schema_data + owner: design/gdd/b3-status-effect.md + attributes: + table: StatusEffectTable(D2 承载,键 = effect_id;B3 回填 owner,2026-09-08 定稿回填) + fields: + effect_id: String 主键唯一 + display_name: String + target_side: enum {hero, enemy} + magnitude: int——percent ∈ [-100,100] ∧ ≠0 | flat ≥ 1(跨字段校验) + unit: enum {percent, flat} + duration_settlements: int [1,99] + stack_policy: enum {stack, refresh, independent} + max_stack: int [1,16](仅 stack 消费——refresh/independent 行内容约定恒 1,非校验项) + mvp_content: + attack_up: { display_name: 增伤, target_side: hero, magnitude: "+50 percent", duration_settlements: 1, max_stack: 2, stack_policy: stack, source_class: safe_cell } # 2026-09-11 两级奖励批 D=2→1(当次结算语义) + defense_up: { display_name: 减伤, target_side: hero, magnitude: "-30 percent", duration_settlements: 1, max_stack: 2, stack_policy: stack, source_class: safe_cell } # 2026-09-11 两级奖励批 D=2→1;2026-09-13 减伤墙批 −50→−30(叠满两层 −60 帽——C3 投值,B3/registry 2026-09-14 接口漂移批回写落地) + ambush: { display_name: 伏击(敌方增益), target_side: enemy, magnitude: "+3 flat", duration_settlements: 1, max_stack: 16, stack_policy: stack, source_class: mine_hit } + weaken: { display_name: 削弱(标中旗 debuff), target_side: enemy, magnitude: "-65 percent", duration_settlements: 1, max_stack: 16, stack_policy: stack, source_class: flag_hit } + value_status: 四行值全部 provisional,验收锚——weaken→A3 F1 条款 2(B4/B1)· ambush 量纲→B2(条款 3,**量纲已定稿见 ambush_unit_definition**)· 对照表→B1(条款 1)· **weaken 已按 B1 F0 裁决修订 −25→−65(2026-09-10,值域 [60,85]、终值共裁 B1 OQ2——见 anchor_warning 回执)** + ambush_unit_definition: **量纲已定稿(2026-09-09 B2 GDD Rule 6/F2——registry OQ2 关闭条件兑现)**:+3 flat = 每层伏击为每次攻击类行为(attack/heavy)附加 3 点主角 HP 伤害(后置加法、不进倍乘);charge 拍零兑付(空拍蒸发);值仍 provisional(锚验收归 B4/B1——A3 F1 条款 3;B2 供给 cap 表值与单结算自然边界口径见 clause3_supply 条目) + anchor_warning: weaken 行 vs A3 F1 条款 2 锚两独立线性代理同向预检不过(百分点·结算暴露 25 vs 100;原型战斗量级 ≈1 vs ≈4 当量)——B1 对照验收优先裁决,登记 B1 强制检查点输入。**修复空间不对称(2026-09-09 增补)**:每结算瞬时当量归一 m_w≥51 可行(F2 越域界随移 AP 界 −408%/表界 −816%);每 AP 总暴露归一 m_w>100 被 percent ∈ [-100,100] 域禁止(该分支不可行)——OQ1 前置步骤:B1 先钉死归一口径,口径落定前禁止数值修订;落总暴露则现实解 = B4 重定最低档参照。**参照对象迁移注记(2026-09-09 B4 评审;2026-09-11 回迁)**:最低档安全格 buff 当量的参照对象曾迁 defense_up 当量(attack_up 时为死行——B4 F4 迁移)——**2026-09-11 用户裁决"基础攻击恒存在"(HeroTable.atk 域 [1,999]、0 加载拒绝、MVP=6)后 attack_up 复活,参照对象回迁 attack_up 当量**(消费面 = 主角战斗段先手攻击——B1 GDD 同批;defense_up 迁移退役为历史记录,attack_up 入池时 tier_min_buff 重算);本条与 clause3_supply.direction_precheck 的 25 vs 100 预检算术系 attack_up 口径历史登记——defense_up |−50|×dur2 与 attack_up 数值巧合全等(100pp),今日结论不变;**迁移后预检未按新参照重跑**的义务已于 2026-09-14 B1 重算批兑现(F0/F1 按 attack_up 当量重跑 + F1 锚对照实物改 attack_up 行——本迁移注记关闭)。**F0 裁决回执(2026-09-10 B1 GDD——红警关闭)**:归一口径 = **每结算瞬时当量**(总暴露分支被 percent schema 域禁止 + 与"单结算自然边界"已锁事实冲突,排除);m_w 修订 −25 → **值域 [60,85]/默认 −65**(mvp_content.weaken 已同批改写——锚两腿 @D_ref=12:7.8 > 6.0 严格大于 ✓ / 7.8 ≥ E_dig 4.5 ✓〔E_dig 读数 2026-09-14 B1 重算批按 C3 v3 曲线重推——原 7.2 随口径退役〕);地板 = 乘数零点 −100pp、交叉层数 = ⌈100/m_w⌉ = 2(原"7 层触地板"预读按 −400% 合成带估读,更正);defense_up 参照保持(总暴露分支的"重定参照"现实解未采纳);终值 = B4/B1 共裁(B1 OQ2);B3 GDD 正文不改以本条为准(收编先例) + rule4_exemption: 定义表非进度成长表,键 = effect_id(D2 Rule 4 豁免,schema 注释承载) + single_enemy_assumption: **回验结果 = 不成立——B2 定多敌(用户 2026-09-09 拍板;同日 B2 GDD 兑现 B3 预授权接口漂移条款)**:MVP 战斗 = 1–3 个同敌种敌单位(EnemyTable.unit_count);效果 side-wide 语义改"全体存活敌单位"(层数 = side 共享池,**B3 现接口零变更兼容**);target_ref(单体寻址)= 扩展位义务——首个单体效果上线时 B3 补列(随批:B1 归集语义/D1 呈现裁决);B3 GDD 正文不改,以本条为准(A3 OQ5 收编先例) + mechanics: 战斗域统一状态容器(三向效果同构实例)· 单一时限量纲 remaining_settlements(先兑付后衰减,每轮结算 −1,dur=1=一次性;跨轮跨图延续、battle_over 全清不跨战斗)· 按行叠层(stack 授予不触碰 R;refresh 重置取实例授予时快照 D;合并在授予时、结算作用顺序归 B1——两序分离)· 满层授予拒绝原子(false+容器零写)· 合成 = L×m 加算、percent 越域不 clamp(地板/换算归 B1 SettlementTable)· 授予集中在 B1 结算编排(A2/B4 零直接接口)· 零随机(无 RNG 流消费)· 零信号外发(纯被动容器) + api: notify_battle_start / apply_effect(effect_id, source) -> bool / query_active(side) -> Array(插入序;六归因字段+display_name+授予时快照投影)/ decay_round() -> int / notify_battle_end() -> bool——B1 消费契约,provisional 登记 + b1_obligations: 八项(B1 GDD 门禁评审缺项即 blocking)——① 同轮多授予按确定次序调用 ② 事实→授予恰一次转化(B3 不去重)③ percent→乘数换算方向与合成值地板成文 ④ 最后一轮 decay 判定 ⑤ F1 条款 1 效果档位对照表以 B3 实例为对照实物 ⑥ 每结算编排恰一次 decay_round(2026-09-09 增——decay 基数=1 先决条件)⑦ 结算明细按授予流水逐条标注来源(合并坍缩归因补偿,2026-09-09 增)⑧ “溢出未生效”行进结算明细协议(Rule 5 后半承载,2026-09-09 增)。**履行回执(2026-09-10)**:八项已全数落位 B1 GDD(Core Rules 3/5/8/10/11 + Formulas F0–F2——落位映射 = b1 AC 覆盖矩阵"上游义务 23 条"行;③的 AP/表界数值随 F0 修订更新为 −520%/−1040%) + presentation_obligations: (2026-09-09 登记 systems-index,缺项即 blocking)D1×4——HUD 常显+EC12 空态规格定稿 / effect_id→icon 映射键契约(形状+色相双通道)/ 有利有害极性呈现映射(数据源 = B1 SettlementTable 效果语义,非 magnitude 符号)/ 全清状态栏去留;B5×2——溢出行呈现 / origin_cell→玩家可读形态(缩略图/格子高亮);D5——“同轮拿满优于跨轮叠”教学(OQ7 配套);B4——奖励身份预读(创意风险 #1 切面) + ownership_note: A3 OQ5“标中旗 debuff 档位归 B4 表设计”经用户 2026-09-08 拍板收编——四行初值定稿权归 B3,锚验收权仍归 B4/B1,修订走接口漂移回写本条 + dangling_ref_policy: effect_id 悬空校验**已激活**(2026-09-09 B4 RewardTable 首个 effect_id 引用行〔defense_up〕回填——dangling_ref_policy"首个引用字段 owner 补"条款兑现;条件性夹具 = B4 b4_dangling_effect〔AC-17〕) + referenced_by: [B1, B4, B2, D1, D5, B5] + + - name: board_gen_table + type: schema_data + owner: design/gdd/a1-board-generation.md + attributes: + table: BoardGenTable(D2 承载,键 (level_index, node_depth) + depth-0 锚行——首个进度键表;A1 回填 owner,2026-09-09 定稿回填;第三轴登记约定〔B7 修订〕——Boss 图 = 各 level Dmax 行、elite_/boss_ 字段命名空间预留、C1/C3 定稿强制重审 OQ6,改键退路 = D2 Rule 4 接口漂移通道) + fields: board_width/board_height(int [8,64],knob)/ mine_density(float (0,0.5],knob——雷量参数形态=密度浮点,尺寸解耦;布雷数下限 round(d×(W×H−9)) ≥ 1,R3 修订)/ inferability_target(float **[0.85,1]**,knob——P1 门槛承重参数+绝对锚,B1 修订收窄)/ solver_rules(enum single|single_subset,**冻结 single_subset**——推理定义域非调参)/ min_safe_count(int [1,999],knob——A3 EC5 退化板对冲)/ max_gen_attempts(int [1,32],knob)/ fog_region_count(int [0,32],knob)/ fog_target_ratio(float [0,1),knob——**≤ min(8d, 0.25) 绝对上限,B1 修订**)/ board_biome(StringSet **非空**,域校验致命——A4⑮;词表 owner 未定稿前只校验非空)+ elite_/boss_ 前缀字段命名空间预留(非 knob,B7——语义归 C1/C3) + cross_field_validation: fog_domain_ok(F2 规范形——count=0⇔ratio=0 或 0 停手 > 其余"固定类别顺序结算 + 双发射源: round_end 有两个合法发射者——棋盘路径由 A2 发、停手路径由 A3 直发,payload 同构 + 三源拒绝: 拒绝原因三互异来源——棋盘面 board_face / 资源面 resource_face(AP 不足)/ 相位 phase(非 Active) + 单意图批: settle_batch 只装一个意图 ≡ 直接调 try_dig 等便捷 API,行为逐项相同 + 作废(voided): 轮次结束后未获执行的批次意图——零棋盘效果、零扣费、零事件,区别于非法拒绝 + 连锁不越旗: 连锁展开不经过插旗格(旗格不入链、不作传播源——2026-09-09 评审 GD-1 定稿,经典 flood 惯例) + 行为环: 敌种出招的固定顺序表(如攻击→蓄力→重击),按结算轮转、环尾回环首——零随机、可学习(B2 首次定义) + 拍: 行为环里的一格行动("这一拍是重击")——B2 首次定义 + 同步轮转: 同种敌人永远处于同一拍,任何死亡时序都不会让幸存者乱拍(B2 F1 定理) + 伏击池: 敌方侧共享的伏击层数——本轮踩几颗雷就几层,轮末清零(side-wide 语义,B2 首次定义) + 溢出(伏击): 单轮踩雷超过上限(cap)的部分——转为"溢出未生效"明细行可见、不生效(B2 首次定义) + 死亡即不动手: 轮到某敌人行动时它已死 → 这一下不打、招式轮转停格(2026-09-09 用户拍板,B2 首次定义) + 资格格: 开出时 reward_eligible=true 的格——A2 事件面上 chain/dig 通道的安全格(含链开雾格);install 期可精确枚举为 S∖R₀(B4 首次定义,F1 消费) + 刀: 伤害道具(damage_item)的项目行话,全文同义(B4 Rule 5 首次定义);"烧刀/烧包" = 把无法生效的道具白白消耗掉(选定即承诺的成本,B4 E10/E12 语境) + 批尾: 同一批取号中最后消费的一个号——B4 清图奖励身份 = install 批的批尾取号(B4 F1 首次定义) + 死行: 被当前内容态判定为零消费的表行——加载不致命、不入参考池,复活走 GDD 修订连锁(B4 F4 首次定义;历史首例 attack_up〔hero atk=0 时代〕已于 2026-09-11 随"基础攻击恒存在"用户裁决复活,当前内容态暂无在册死行) + 档位序检查: 雾池最低活档 ≥ 最低活 buff 档的序数比较(原"档位序代理"更名,B4 AC-19)——tier 与当量零耦合,仅序数健全性、非当量保证(B4 F2 注记) + 预推进: 测试中为到达 scope=σ 而先行丢弃式消费 σ 个 scope 的构造——RngService 全新实例首个 open_stream 恒为 scope 0,derive_at 不推计数器(B4 AC 前言首次定义) + 完美排雷(perfect_sweep): 板级零误标(flag_judged hit=false=0)∧ ≥1 标中 → 清图奖励当量 ×P_perfect(默认 ×2)——与踩雷正交(盲挖踩雷不破坏、标错才破坏)、全有或全无谓词无部分档、板级计数随 install 重置(B1 Core Rule 13 首次定义) + 当量: 统一价值单位,1 当量 = 1 点主角 HP 的期望净变化——四源收益单一线性换算的通货(B1 F1 首次定义) + 乘区: 伤害公式里相乘的修正段(本作两段:hero 减伤池 × 敌方削弱池——池内加算、clamp [−100,+100]pp、池间乘算、floor 取整)(B1 F2 首次定义) + 溢杀: 一刀超出目标剩余 HP 的部分——丢弃、不转移、无负 hp 中间态(B1 F3 首次定义) + 免伤轮: 主角该结算承伤为 0 的结算——percent 池地板(−100pp → 乘数 0)的自然结果;现行值链可达路径 = weaken 饱和(敌侧单结算零输出),defense_up 叠满 = 0.4×(−30×2 帽)不再单侧触地板(B1 F2 首次定义;原"defense_up 顶点 −100% 恰在地板边界"例随减伤墙批退役——2026-09-14 B3 接口漂移批同步) + 图标键(icon key): "哪个数据行配哪枚图标"的固定对照——数据表主键(effect_id/reward_id/cell_type/enemy_id)直接作图标键,一键一专属纹样、映射全局恒定禁运行时换图(art-bible §4.6 首次定义,2026-09-13 冻结) + 徽章(badge): 画在格子上的身份附属小图标(类型/奖励身份预读)——绘制优先级栈位在旗之下、变异纹理之上,不构成格内第二焦点(art-bible §4.6 首次定义;栈位声明源 = proposal-reward-polarity §0) + 极性(polarity): 同一类型兑付的正负方向——正 = 收益侧、负 = 代价侧,行为×格型→符号矩阵见极性提案 R2;视觉通道冻结 = 金族(有利)对红族(有害)+ 满框对缺框(art-bible §4.6 首次冻结视觉侧) + 命令式回执驱动: 动作调用返回后组合根立刻重查一次只读面来刷新界面、不等通知信号——旗插拔/移动/AP·RAP 读数/状态/库存五类动作零事件外发或零推送,呈现层刷新由此承载(D1 首次定义;ADR-0001 订阅纪律的配对形态) + 程序化占位: 用代码按冻结色板与形状规则直接画出的临时图形——参数取自 art-bible §4.1/§3 参数域、icon 按 §4.6 键位映射表挂槽,终稿美术到位后原位替换、数据层零改动(D1 首次定义;样张未定稿不阻塞呈现层开工的承载形态,占位同样受 §1.4 数字可读性第一验收项约束) + 叠加层(overlay): A5 自持的板级派生信息集(显影/真视/预读三集)——只改"玩家能看见什么"不改棋盘事实,零回写 A2/B4 状态、随图消亡(A5 首次定义) + 真视窄域护栏: 替代推理的道具效果(雷位真视)目标域 ≤3×3、全图级仅限恢复推理原料类(显影/预读)——信息层级护栏,防"扫雷退化为路径优化"(A5 Core Rule 3 首次定义,schema 校验强制) + 渗透率: aux 道具期望件数占奖励池的份额——E[N_aux/板] = |U|×Σw_aux/W_total(A5 F1 首次定义;供给面天然限流,P1 护栏的量化形态) + 行动: 轮次内挖掘期的原子决策单位——同一事件的三个视角:玩家决策层叫行动、进 A2 的数据形态叫意图(单意图帧整批提交)、A3 计价类别叫动作(action_kind 统一价目);有价行动(挖/插旗/拔旗)1 AP/次、受每轮 AP 预算约束(现行表值 8),移动/停手免费不计 AP(game-concept 术语层级 2026-09-14 收录) + 节点: 关卡节点地图上的一个格——关卡内移动的步进单位;层名定稿(2026-09-14 用户裁决:节点 胜 遭遇——成员中立 + C1 注册名/切片 node_* 接口零改动 + 遭遇既有语义 = 战斗不挪用);节点类型分类学与分支机制归 C1 GDD 展开 + 遭遇: 一次完整战斗事件的同义词——战斗节点触发的那一场仗(既有用法:B1"两场遭遇" / D1"双敌种遭遇""Boss 遭遇变体");不作地图层名(2026-09-14 裁决) + 节点奖励: 奖励点节点给的奖励(C1 域)——与安全格奖励(B4,格内开出)区分;节点奖励覆盖层 = 未来 spec design/ux/node-reward.md(battle.md OQ9)——三个"reward"各归各位(2026-09-14 拆名注记) + HP 账本: 冒险域主角血量的唯一记录——C2 持有,每战开始交出、战斗结束收回、恢复点与大奖励入账;关卡切换血条不清零(C2 首次定义;跨战斗 HP 持久性归 C1/C2 已锁事实的 C2 半边兑现) + 关卡收束点: 一关就此定局的时点——Boss 战胜利(通关)或英雄死亡(失败);C1 在收束点先调 B1 关卡清库、再向 C2 宣布关卡结束与结果(C2 首次定义;切片 level_runner 先例转正) + 关卡大奖励: 清完一关、进入下一关前的一次性兑付——上限提升 + 血回满 + 代币记账三件构成,不进背包不占库存域;末关通关不兑付(C2 首次定义;game-concept"冒险内大奖励"的落地形态) + referenced_by: [A2, B2, B4, B1, D1, A5, art-bible, game-concept, C1, C2] + + - name: enemy + type: schema_data + owner: design/gdd/b2-enemy-system.md + attributes: + table: EnemyTable(D2 承载,键 (level_index, node_depth, enemy_id);B2 回填 owner,2026-09-09 定稿回填——进度键 floor(D2 R1)+ 实体键精确匹配) + fields: + enemy_id: String(行集内主键;意图视觉映射键——视觉推测型 D1×4①) + display_name: String(本地化归 D6) + unit_count: int [1,3](knob——**遭遇区间下界**〔2026-09-14 C3 修订批:unit_count 升级区间语义 [unit_count, unit_count_max],B1.begin_battle 实例化期经 RngService encounter 封闭流均匀骰;新敌种首遇/教学位节点固定不加骰〕;默认 1 provisional,多敌语义默认零消费者复验归 C1〔B2 OQ2〕) + unit_count_max: int [unit_count, 3] 可省略(2026-09-14 C3 修订批新增——遭遇区间上界;缺省 = 定值,旧行零迁移) + atk_min/atk_max: 可选配对 int [0,999](2026-09-14 敌方攻击区间批——上阶/下阶区间随机;缺省 = 定值行向后兼容;跨字段:必成对 · **atk = (min+max)/2 中点锚**;roll 时机 = 遭遇骰同流〔encounter 恰一 scope,数量骰先、atk 骰后〕spawn 期**整战持久**〔每拍抖动否决——数字可读性锚〕,B1.begin_battle roll → B2.spawn enemy_atk_override 收确定值〔B2 零随机纪律保持〕;MVP 六行 ±3 provisional 归 C3;验收 = 完美策略档 0 加成通关 ≤40%〔OQ7〕;接口漂移 = B2.spawn 签名 +1 参数〔正式侧 GDD/registry 修订批随 ADR-0009 同批〕) + armor_type: enum {none, a, b} 缺省 none(2026-09-14 护甲克制批转正登记——伤害类型 × 护甲克制,克制矩阵 = settlement_table.armor_effectiveness〔特化 ×150 / 无甲 ×100 / 对位 ×50〕;MVP 映射 goblin=none / bugbear=a / ogre_chief=b;数值 provisional 归 C3) + hp: int [1,999](knob——难度修饰前基线;MVP 18 provisional 出处待查) + atk: int [0,999](knob——attack 行为伤害基值;MVP 4 provisional 出处待查;0 = 纯伏击伤敌种合法) + heavy_multiplier: int [1,16](knob;无 heavy 拍敌种不消费——非校验项,B3 max_stack 先例;MVP 2 provisional) + pattern: StringList(行为环;元素 ∈ {attack, charge, heavy} MVP 校验域——guard/boost/hinder/summon 预留值校验拒绝〔A1 solver_rules 冻结先例〕;环长 [1,8]) + advantage_per_round_cap: int [0,16](knob;**MVP 默认 4 定稿**〔2026-09-09——反灾难护栏定位,旧默认 8 溢出协议死码已废弃〕;0 = 免疫伏击合法极端;A3 F1 条款 3 供给表值来源字段) + rowset_rule: 每行集恰一敌种(行集内 enemy_id 唯一 = 表结构校验,违规加载期致命)——多敌种同深度选择规则归 C1/C3(provisional);精英/Boss 编码位预登记 B2 OQ7(键冻结下同深度第二敌种行触发行集致命——改键走 D2 Rule 4 接口漂移,C1 定稿时强制核对) + mvp_content: **恰 3 敌种(2026-09-14 C3 修订批转正——DND 更名批用户指令:grub→goblin / ambusher→bugbear / boss_guardian→ogre_chief;旧两敌种登记由本条替代;B2 AC-16③ 物种集断言随批更新为 {goblin, bugbear, ogre_chief})**:goblin(地精,[attack] 单拍环——L1 D0 24hp/7atk ×[1,2] 教学位固定 ×1、L1 D1 群 13/5 ×[2,3];armor=none)· bugbear(熊地精,[attack,charge,heavy] 三拍环——L1 D2 40/9 ×1、L1 D3 群 18/6 ×[2,3]、L2 D0 锚行 30/7;armor=a)· ogre_chief(食人魔酋长,[attack,attack,charge,heavy] 四拍 Boss 环——L1 D4 150/9 ×1;armor=b);heavy_multiplier=2 全员;出处 = 切片 enemy.tres v3(试玩批次 1 裁决 + 遭遇随机化批),曲线数值归 C3 + load_bearing_rules: 多敌模型(1–3 同敌种单位,B3 单敌假设回验不成立——见 status_effect.single_enemy_assumption)· 行为环确定性轮转(零随机;先作用后推进;死亡即不动手——轮到前被杀本拍不打指针冻结;同步轮转定理)· 效果 side-wide(伏击池 = side 共享,B3 零接口变更)· 行值快照抗热重载(B3 EC9 同款)· 零随机零 UI 零事件订阅(A2 事件面不含 B2——事实经 B1 转化) + b1_obligations: 六项(B2 GDD 门禁评审缺项即 blocking)——① 推进每结算恰一次(敌单位专属——主角无环)+ 结算内调用序成文 query_attack→apply_damage→advance ② 雷→伏击恰一次转化 ③ clamp/负值/溢杀与死亡判定时机成文 ④ F1 条款 3 闭合对账(cap 表值 + 量纲 + A1 actuals p10 非 target 参数)⑤ 默认目标消费与单体/AOE 分流 ⑥ 伏击 cap 溢出"溢出未生效"明细行协议。**履行回执(2026-09-10)**:六项已全数落位 B1 GDD(Core Rules 5/6/9/10/12 + Formulas F3/F4——落位映射 = b1 AC 覆盖矩阵) + d1_obligations: 四项(视觉推测型呈现,缺项即 blocking)——① enemy_id→图像线索映射清单成文(预告时序=下一拍预告、挖掘期全程可见;含强度档位/环长直觉编码说明;形状+色相双通道)② 映射全局恒定(enemy_id 键——跨 run 禁重掷)③ 顺位与存活可读 ④ 敌方 side 池呈现口径 + rule4_status: 本表是进度成长表(A1 BoardGenTable 同型,无 Rule 4 豁免) + referenced_by: [B1, B4, C1, C3, D1, B5, D5] + + - name: hero + type: schema_data + owner: design/gdd/b2-enemy-system.md + attributes: + table: HeroTable(D2 新增 schema 行〔接口漂移回写,2026-09-09 B2〕,单行全局;B2 回填 owner) + fields: + max_hp: int [1,999](knob;MVP 值 provisional 出处待查) + atk: int [1,999](**0 = 加载拒绝**——基础攻击恒存在,"0 基线 × 百分比增益恒零"死锁自 schema 层杜绝〔2026-09-11 用户裁决,原"机制开关 MVP=0、激活裁决归 B4/B1"作废〕;MVP = 6 原型锚 provisional 归 C3;消费面 = 主角战斗段先手攻击——B1 GDD 2026-09-11 增补) + rule4_exemption: 单行全局定义表(主角不随关卡自动成长——成长=天赋 C4/奖励侧;ActionEconomyTable 先例) + hp_persistence: 跨战斗 HP 持久性归 C1/C2(provisional)——B2 单位接口以初始 hp 入参,不定义冒险内持久性 + referenced_by: [B1, B4, C3] + + - name: reward_table + type: schema_data + owner: design/gdd/b4-reward-item-economy.md + attributes: + table: RewardTable(D2 承载,键 = reward_id;B4 回填 owner,2026-09-09 定稿回填、同日 full 评审增补 display_name——含 use_timing 字段〔game-concept"使用时机"预留的兑现,MVP 单值 pre_settlement 承重,扩枚举走修订〕) + fields: + reward_id: String(表内主键) + category: enum {buff, damage_item, heal_pack, instant_aoe, instant_heal}——MVP 恰五值(2026-09-11 两级奖励批两类交付轴:即时类 {buff, instant_aoe, instant_heal} 当场兑付不进库存 / 可选类 {damage_item, heal_pack} 入库存走窗口),预留值(resource/aux_item 等)校验拒绝 fail-fast # was: {buff, damage_item, heal_pack} 三值 before 2026-09-12 + effect_id: String 可空——buff 类必填(StatusEffectTable 精确引用,悬空校验已激活);非 buff 类恒空 + display_name: String 非空——玩家可读名(D1 呈现数据源,本地化归 D6;2026-09-09 评审增补,B2/B3 同库惯例) + power: int [0, 999](knob)——item 类加载校验 ≥ 1;buff 类不消费非校验(B2 heavy_multiplier 先例) + power_min/power_max: 可选配对 int [1,999](2026-09-14 伤害量值区间批——上阶/下阶区间随机;缺省 = 定值行向后兼容〔armor_type 先例〕;跨字段:必成对 · buff 行禁止 · **power = (min+max)/2 中点锚**〔A3 条款①期望当量不漂移〕;roll 时机 = 身份预分配批内追加取号〔既有取号序零扰动〕,快照 E14 授予时口径不变、B1/呈现零改动) + weight: int [0, 999](knob)——0 = 该池不出此行(合法极端,取号前剔除) + pool_tag: enum {safe_cell, active_inject, clear_default}(2026-09-14 C3 修订批增补 active_inject——供给控制器注入池〔可选类三行移出格子权重池,由 RewardSupplyController install 期注入:首件保证 + 指数递减〕;clear_ 命名空间预留、校验拒绝——A1 elite_ 同款) + tier: int [1, 8](knob)——A4 雾内档位下界约束载体 + use_timing: enum {pre_settlement}——MVP 唯一值,扩枚举走 GDD 修订 + cross_field_validation: 加载期致命族——池空(每 pool_tag × 雾/非雾两态各 ≥1 行 weight>0)· clear_default 恒正当量(≥1 活行 ∧ power≥1 ∨ effect_id 非空——A3 条款④"清图奖励>0"的行级兑现口径)· item 行 power≥1 · buff 行 effect_id 必填/非 buff 恒空 · safe_cell 活 buff 行集非空(tier_min_buff 定义前提,F1 对偶)· **floor_supply_missing(K_dmg ≥ 1 ∧ safe_cell 池无 power ≥ P_min 活刀行)· no_damage_row(K_dmg = 0 ∧ 全池零活刀行——零伤害世界后闸,B2 OQ9 并案)〔2026-09-09 评审增补,跨 RewardConfigTable 校验〕** + mechanics: 奖励身份 install 期预分配(roll_board 恰一次、行主序、消费 reward_roll 封闭流恰一 scope 批〔格批 + 清图批尾一号 × F5 草稿数——每稿含清图一号、草稿作废连清图一号一并作废〕;Ready 后只读、无重掷 API)· 授予期零随机(身份恒定跨轮;换图映射消亡)· **库存关卡域(2026-09-11 关内延续——跨战斗/跨图延续、关卡末 clear_inventory 清空;原战斗域 battle_over 全清作废)**· **两类交付轴(2026-09-11):即时类 {buff, instant_aoe, instant_heal} 授予段当场兑付不进库存(默认根 75%)/ 可选类 {damage_item, heal_pack} 入库存走窗口(25%、更强)——category 五值枚举**· **窗口单件制(2026-09-11 拍板——至多选用一件)**· **治疗两级:instant_heal 小额即时 + heal_pack 稀缺强治疗(w 25%→6%、p 4→12)**· 道具伤害恒定表值不吃 percent 修正(2026-09-11 后 attack_up 已复活〔消费面 = 主角先手攻击〕、入池随奖励改造批——原死行口径与"锚参照迁 defense_up"注记退役;A3 条款 2 锚参照对象 = attack_up 当量)· 道具段照常消耗(选定即承诺:无存活敌的已选刀照常 Used + 零效果 + "已消耗未生效"明细行——理由 2026-09-09 评审更正为守卫所有权不对称 + 反抖动,原"敌 HP 不可见"与 B2 血条数字可读矛盾作废)· **窗口锚点 = 授予段后、增益/减益段前(2026-09-09 评审用户拍板:当轮拾取当轮可用;核心段序 buff/debuff > 道具 > 战斗不变)**· 清图槽允许 buff 行(运行时满层零兑付 = 合法例外 + "奖励未生效"行,预警④监控)· F5 保底 = 草稿-校验-提交重 roll(K_dmg=0 / N_draft=32 provisional——**2026-09-14 C3 修订批转正:K_dmg=0"禁区默认值"条款解除**,刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸仍托底零活刀行世界)· F2 雾档过滤(T_fog_min 默认 2 provisional;A4 约束①为验收锚非运行时校验——AC-19 档位序检查,B1 对照表落定后升级)· **重复受理幂等(同格重复 grant / 同批次重复 grant_clear → 同一快照/身份 + 诊断;受理/兑付恰一次归 B1×②③)** + api: 封闭枚举 10 入口(provisional,实现期 ADR 定名)——roll_board / query_identity(返回 {reward_id, category, tier} 引用级投影;未开格恒可查——A5 桥接结构前提;清单外坐标 → null + 诊断)/ grant(同格重复受理幂等 + 诊断)/ grant_clear(同批次重调幂等 + 诊断)/ stock(仅受理道具类,未知 item_id/buff 类拒绝;来源 {cell, round} 调用方供给不校验真值)/ use(零游戏态守卫;回执 = 授予时快照投影)/ query_inventory(Used 保留只读;枚举序 = stock 受理序)/ clear_inventory / retire_mapping / equivalence_supply(全活行投影 + tier_min_buff)+ weighted_pick 纯函数面;新增入口 = 接口漂移,**新增入口须随附全输入域语义表(2026-09-09 评审纪律)** + mvp_content: **默认根行集重登记(2026-09-14 C3 修订批——减伤墙批 + 供给控制器批 + clear_blast 换位批转正;值 provisional 归 C3;2026-09-11 两级奖励批旧登记由本条替代)**:格子池四行即时类(attack_up_basic buff→attack_up w20 t2 / defense_up_basic buff→defense_up **w16** t2〔减伤墙批 24→16,attack_up 升池内最高行〕/ spark_aoe instant_aoe p4 w16 t1 / heal_minor instant_heal p4 w15 t1——池合计 67)+ 注入池三行可选类(knife_t1 damage_item p8 **w60 active_inject** t1 / knife_adv damage_item p16 **w30 active_inject** t2 / heal_pack heal_pack p12 **w10 active_inject** t2)+ clear_blast(instant_aoe p40 w1 t2 clear_default——**清图槽换位:原 clear_knife 入库刀由当场兑付全体存活敌的 instant_aoe 替代,不入库不占可选类供给;完美板 ×2 = 80**);tier_min_buff = 2;雾池 = {defense_up_basic, attack_up_basic, knife_adv, heal_pack}(AC-19 对账对 (knife_adv, attack_up_basic) 不变);B1 条款⑤计价基元随换位重推(C = power = 40——clause5_dominance.c3_ruling);**2026-09-14 伤害量值区间批:五行 power 区间化**(heal_minor [2,6] / knife_t1 [5,11] / heal_pack [6,18] / knife_adv [10,22] / clear_blast [25,55]——中点恒等原值,身份批内上阶/下阶随机;验收口径 = 完美策略档 0 加成通关率 ≤40%〔OQ7 完美档基线 100%〕) + direction_precheck: **刀供给方向预检(2026-09-09 评审批登记,B2 clause3_supply 先例)**——击杀需求 18(单敌)/ 54(3 敌,B2 EnemyTable 参考行);刀伤供给期望/板 ≈ |资格格| × 40% × 5 = 2|资格格| → |资格格|=10 保守板:单敌 ≈0.9 板、3 敌 ≈2.7 板("单敌无忧 / 2–3 敌贴近下界"现值有锚);输入缺口 |资格格| 分布待 A1 actuals p10/p50 落地后重跑;治疗对照基准 max_hp=50(原型锚——HeroTable 值出处建议 B2/D2 回写补登) + b1_obligations: 六项(B1 GDD 门禁评审缺项即 blocking,清单全文见 B4 GDD Dependencies;2026-09-09 评审同步)——① 结算段序骨架(授予→**窗口**→增益/减益→道具→战斗→衰减→判定;窗口锚点 = 授予段后;窗口选择序 = 回放输入)② 受理分流 + **每格恰一次** + 满层零兑付"奖励未生效"行(含聚合计数字段)③ clear_reward 授予路径(B3 OQ4 关闭条件)④ 窗口与道具段编排(usable_now 组装/选定序/满血守卫 + 逐件动态守卫/零效果判定"已消耗未生效"行/**击杀结果进流水**/治疗 clamp)⑤ 生命周期编排(battle_over clear_inventory〔清空反馈计数〕/ retire 映射消亡)⑥ roll_board 恰一次。**履行回执(2026-09-10)**:六项已全数落位 B1 GDD(Core Rules 4/5/7/9/编排专章——落位映射 = b1 AC 覆盖矩阵;缺口预检 2 阻塞级 deferral 已随 AC-7/AC-15/16/20 承载) + d1_obligations: 五项(缺项即 blocking;2026-09-09 评审 3→5 扩容)——① 已开格 + 库存身份恒定可读(库存常读条 = 挖掘期规划面)② 道具使用窗口呈现(授予段后弹出/选用/跳过/序/usable_now/触发政策注记)③ 结算明细四类行(道具/"奖励未生效"〔聚合计数〕/"已消耗未生效"/"使用被拒")④ H2 清图奖励行强调呈现 ⑤ battle_over 库存清空可见反馈 + pricing_constraints: A4 三条定价硬约束承接——① 雾内档位下界(AC-19 档位序检查夹具对账型,B1 对照表落定后升当量对账)② 清图锚随雾成本上浮(**MVP 结构性不可闭合——clear_default 常量池无法随雾负担变化,非 deferral;激活载体 = clear_ 插槽,B1 检查点仅对常量判 pass/fail**)③ 雾板净当量优势带上界(**MVP 静默绿灯风险——②缺位 + 仅 tier 下界加成下近乎必然平凡通过**;B1 强制检查点 + 预警⑤雾板分层/⑥速杀退化观测对冲)〔②③状态 2026-09-09 评审显式宣告〕 + high_glow_nodes: H1 开格拾取瞬间 / H2 清图奖励兑付 / H3 道具抢杀窗口(2026-09-09 评审诚实化:H1/H2 = 奖励侧常规节拍登记,H3 = 创意风险 #2 战斗侧执行兑现;**风险 #2 排雷侧"完美排雷爆发结算"机制本体归 B1 强制检查点、#3 标中旗节拍补偿归 B1——B4 只交付插槽与明细行数据面**;呈现义务 D1×④⑤) + referenced_by: [B1, D1, C3, A5, B5, D5] + + - name: reward_config + type: schema_data + owner: design/gdd/b4-reward-item-economy.md + attributes: + table: RewardConfigTable(D2 承载,**单行全局**——2026-09-09 B4 full 评审接口漂移新增:B4 三个标量 knob 的 Rule 1 单一事实源载体,R3 override key 空间随之存在) + fields: + t_fog_min: int [1, 8](默认 2)——F2 雾档过滤门限 + k_dmg: int [0, 999](默认 **0**——2026-09-14 C3 修订批转正〔切片 reward_config.tres 现值〕:原默认 1 与"C3 禁区默认值"条款由供给控制器战斗首件保证取代;no_damage_row 加载后闸仍托底零活刀行世界) # was: 默认 1、K=0 = C3 禁区默认值 before 2026-09-14 + active_p0: float [0,1](默认 0.4 provisional——供给控制器首件之后每张新板注入概率初值;E[战斗获取] ≈ 1 标定目标,归 C3) + active_decay: float (0,1](默认 0.5 provisional——注入概率指数递减 0.4→0.2→0.1→…永不绝育;加难杠杆候选 0.6 随 C3 OQ7 扫描) + n_draft: int [1, 999](默认 32)——F5 草稿重 roll 上限 + p_min: int [1, 999](默认 8 = 默认根最低刀 power——2026-09-11 两级奖励批)——K≥1 供给闸刀力下界(floor_supply_missing) # was: 默认 4 before 2026-09-12 + rule4_exemption: 单行全局常量表(HeroTable/ActionEconomyTable 先例) + referenced_by: [B4, C3, B1] + + - name: settlement_table + type: schema_data + owner: design/gdd/b1-settlement.md + attributes: + table: SettlementTable(D2 承载,单行全局;B1 回填 owner,2026-09-10 定稿回填;Rule 4 豁免——单行全局常量/结构表,HeroTable/ActionEconomyTable 先例) + fields: + equivalence_ref_damage: int [1,999](knob,默认 12 provisional——当量参考敞口 D_ref,F1 通货;变更触发全对照表 + 条款③⑤对账重跑,禁独立调参) + p_perfect: int {100,200} percent(knob,默认 200 provisional——完美排雷清图奖励**第二份兑付触发**;2026-09-10 B1 评审批修订:真实双份交付载体〔Core Rule 13#2〕——100 = 仅高光、200 = 双份交付,非整数倍数乘数无运行时载体、域外加载致命;上界受护栏 B 约束〔双读数:D_ref 口径 24 / DPS 严格读数 ≈8,裁决归 C3〕) + segment_order: StringList(结构冻结——段序骨架 [grant, item_window, buff_debuff, item, combat, decay, verdict];加载期恒等拍板序校验违例致命,防实现乱序静默漂移) + source_label_map: Dictionary source_class→文案键(结构冻结——P2 归因呈现映射;键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss}——2026-09-10 B1 评审批修订;新增键 = 接口漂移) + effect_pool_map: Dictionary effect_id→pool {hero_offense|hero_defense|enemy_output|flat_attach}(结构冻结——F2 效果语义映射,B3 Rule 7 载体;键 ⊆ StatusEffectTable 活行集、悬空加载期致命;新 effect_id 上线须随批登记) + pool_clamp_lo: int pp(结构冻结 −100——percent 池地板域下界,乘数域随之 [0,2];2026-09-10 统一八字段口径) + pool_clamp_hi: int pp(结构冻结 +100——percent 池地板域上界;原"七字段 pool_clamp_lo_hi"措辞作废,D2 GDD 行按收编先例以本条为准) + round_direction: enum {floor}(结构冻结——单一取整规则) + damage_a_per_cell: int ≥0(knob 默认 1 provisional——伤害A 开格充能/格;2026-09-14 护甲克制批转正登记,机制与结算归 B1、C3 投值) + damage_a_cap: int ≥0(knob 默认 8——伤害A 单轮充能封顶,防连锁轮失控;0 = 不封顶) + damage_b_per_flag: int ≥0(knob 默认 2——伤害B 标雷充能/首次标中雷,每雷实例一次去重) + armor_effectiveness: Dictionary 伤害类型×护甲→百分比(键集结构冻结 {base, dmg_a, dmg_b} × {none, a, b};默认 base 全 100 / dmg_a→a=150、b=50 / dmg_b→b=150、a=50——特化 ×150 / 无甲 ×100 / 对位 ×50;数值 provisional 归 C3) + validation: segment_order 违例致命 · effect_pool_map 悬空致命 · p_perfect 域 {100,200}(2026-09-10 载体域——100 = "完美必须 ≥ 不加倍"机制语义下界,200 = 双份交付)——B1 冒烟三行致命 manifest(segorder_violation / poolmap_dangling / pperfect_oob) + mechanics: 结算段序骨架唯一权威载体(授予→道具使用窗口〔授予段后〕→增益/减益→道具→战斗→衰减→判定——用户 2026-09-09 拍板序)· 零随机(rng_service 流封闭不含结算流)· 可回放步骤流(同 master_seed 同意图序列同窗口选择序列 ⇒ 步骤流逐行相同——D2 AC-15 deferred→B1 兑现落点)· 调用序钉死 query_attack→apply_damage→advance · 衰减恒执行含末轮 · 高光双落载体(perfect_sweep P_perfect——2026-09-10 修订为真实双份兑付载体 / 标中旗 highlight 标记)· 流水行协议字段分层(公共 7 字段 + 行类型特有 target/effect_id/pool_value/聚合计数——2026-09-10 评审批修订;行类型封闭枚举 13 类)· **伤害A/B 充能与护甲克制结算规格 = B1 GDD F6(W3 补全,2026-09-14 重算批)**——挖掘期充能/战斗段消费/单轮生命周期、不吃 percent、逐分量 armor_effectiveness 查表 floor 相加 + closure_receipt: A3 F1 条款①②③⑤闭合 owner = B1(2026-09-10 F0–F5 首版验算完成——F0 瞬时当量口径 + m_w 值域裁决见 status_effect.anchor_warning;条款③ clause3_supply 闭合回执;条款⑤ clause5_dominance 条目;actuals p10 交付后强制重跑) + referenced_by: [D1, B5, D4, D5, C3] + + - name: clause3_supply + type: formula + owner: design/gdd/b2-enemy-system.md + attributes: + rule: B2→B1 挖掘风险不等式(A3 F1 条款 3)供给口径——每结算敌方伏击暴露精确式 = k × L × m_amb(k = 本结算存活敌中攻击/重击拍单位数,确定性可算);每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb(cap > AP 界时 cap 腿松弛——2026-09-09 B2 full 评审收紧);跨结算累计 = 轨迹量非表值 + boundary: **单结算自然边界(用户 2026-09-09 定稿)**——"不设(每战)上界:踩雷 debuff 只在一次结算中有效,之后没有 debuff"(dur=1 每结算清零无存量) + variables: [cap, m_amb, unit_count, k_r, L_r] + output_range: 单结算结构上界 ∈ [0, unit_count×min(AP, cap)×m_amb](满配语境 3×min(8,4)×3=36;cap≥8 时真可达上界 72 而非 144;默认行值 unit_count=1 下 12) + account_input: 对账输入 = A1 actuals p10(F4 报告 C)非 target 参数(A1 登记口径);闭合 owner = B1 单一签字人;左臂(安全格 buff 当量)载体 = **B4 F6 当量供给**(2026-09-09 B4 定稿——部分缓解非闭合:窗口使用次数是战斗轨迹量,B2 OQ3 扩项保持;2026-09-11 起基础攻击恒在为左臂提供恒定伤害基底,原"当前内容态结构性不可通关"事实退役——B2 Overview 2026-09-11 改写) + direction_precheck: cap=4 下右臂 ≈7.7;左臂按条款 2 最低档锚(+50pp)计 = 0.36×50 = 18——裕度 ≈2.3×(1:1 百分点代理,provisional;2026-09-09 B2 full 评审更正原 4.7×——该值按 +100 两层叠满取值、与"最低档下界"语言矛盾);量级注记:atk=4 时 attack 拍 L≥2 伏击分量过半(heavy 拍过半点后移、按拍别读取;常态支配修正入口 = C3 atk 曲线 / B4·B1 m_amb 锚,B2 权限外)。**参照迁移注记(2026-09-09 B4 评审;2026-09-11 回迁)**:左臂参照对象曾迁 defense_up 当量(B4 F4)——**2026-09-11 用户裁决"基础攻击恒存在"后 attack_up 复活,参照对象回迁 attack_up 当量**(上式 +50pp 系 attack_up 口径,迁移期结束;复算义务随 B1 对照表落定,见 status_effect.anchor_warning 回迁注记)。**闭合回执(2026-09-10 B1 GDD F4——条款③闭合完成;同日评审批双口径更正)**:统一 HP 当量 + 边际配对重算 → 默认 unit_count=1 下 L1 裕度 ≈2.0× / L2 ≈2.6× **通过**(锚行口径;**跨行期望口径 ≈1.2×/1.5× 同向通过——双口径登记,C3 不得以锚行读数放行 unit_count=2**);**满配语境(unit_count=3)最坏盲点破线 → C3 调参约束登记**(unit_count 轴 + 密度×可推理率轴——unit_count>1 行须降密度或降 m_amb);本条 pp 代理预检(2.3×)退役为历史记录(量纲混用 + 右臂聚合上界与单格决策单位错配)。**重算回执(2026-09-14 B1 重算批——按现行三输入重推:duration=1 × m_d=−30 × C3 v3 曲线 d0.25/t0.88)**:L1 裕度 ≈0.48× / L2 ≈0.47× **破线**(锚行口径;跨行期望口径 E_dig=4.5 → ≈0.60×/0.58× 同向破线——双口径登记);满配语境(unit_count=3)≈0.16× 深度破线;**破线升格为默认曲线级约束——V_safe 侧杠杆(m_d/duration/D_ref)× 板形侧杠杆(密度×ι 联动对)× m_amb 全处"禁独立调参"联动网络(m_w↔D_ref↔m_d↔duration_d),修正方向须 C3 提案 + B1 闭合对账共裁**;上段 ≈2.0×/2.6× 读数系 duration=2 × m_d=−50 × v1 曲线旧值链产物,退役为历史记录;actuals p10 交付后强制重跑(B1 AC-39;ι 判式面保守代理 = 行 target 代,总暴露面无效) + referenced_by: [B1] + + - name: clause5_dominance + type: formula + owner: design/gdd/b1-settlement.md + attributes: + rule: A3 F1 条款⑤清图 vs 弃图支配性闭合(B1 单一闭合 owner,B1 GDD F5;**2026-09-10 评审批模型修订:挖穿残余池计价**——E[踩雷] = |M|×n_s/(n_s+1) 均匀交错模型、Δr = ⌈(n_s+E[踩雷])÷AP_r⌉;旧模型按每格独立赌 P(雷) 漏计全池挖掘量、系统性偏乐观)——主判式 Δ_clear = [R_res + C_eff] − [X_res + Δr × s_ref] ≥ 0;E_dig = 默认根加权 7.2 HP(刀 40%/治疗 25%/defense_up 35%);C_eff = C ×(perfect ? P_perfect÷100 : 1)(×2 = 真实双份交付);s_ref = D_ref;**停手侧 RAP 机会收益按零计价(显式假设登记——零价假设下 verdict 已为负,为 RAP 计价只会更负)**;域守卫 n_s < 1 ⇒ 主判式不评估、信息端平凡腿接管 + first_pass: **条款⑤未闭合(2026-09-10 模型修订重推——旧"两态通过"读数作废)**:L1 −5.3 / L2 −10.1(完美双份 +2.7/−2.1);满配语境 unit_count=3 深度为负(−49.6/−97.5,与 clause3_supply 破线同向联动);实现率折减口径(E_dig×0.6)更差(−18.5/−39.5);保守地板需求 L1 ≥ 46.1 / L2 ≥ 91.8(vs C=8/完美 16——缺口 −38.1/−75.8)。**C3/B4 约束包**:① C 量级上调整向(护栏 B 口径联动复核)② 或裁"清图非严格支配"为设计态(与 A4 定价硬约束②同向;如裁设计态须同步声明 A3 条款⑤ P1 对冲弱化 + B4 预警⑤加权监控)——①②二选一归 C3 裁;③ 多敌放行判据 = 条款③⑤在全部登记行组合下同时成立。判据函数角色 = 调参约束生成器(AC-40 断言输出与约束包一致) + c3_ruling: **约束包②采信(非支配设计态)+ 计价基元换位重推(2026-09-14 C3 修订批)**——clear_default 槽由 clear_knife(入库刀 C=8/完美 16)换位为 clear_blast(instant_aoe 当场兑付全体存活敌,C = power = 40):保守地板需求缺口重推 = 46.1−40 = 6.1(L1)/ 91.8−40 = 51.8(L2);C 量级不上调 ✓;P1 对冲弱化同步声明(② 条件)+ 弃图率加权监控随 OQ4 持续;本条 first_pass 旧读数(C=8/16 口径)留作模型演进记录;**区间批义务(2026-09-14)**:C 语义改区间 [25,55](期望中点 40 不变——期望口径缺口 6.1/51.8 维持);保守地板口径(min=25)缺口重推 = 21.1/66.8——B1 修订批对齐条款⑤措辞(义务登记,未兑现前 B1 文本 C=40 按期望中点读)。**重算回执(2026-09-14 B1 重算批——v3 曲线 d0.25/t0.88 × E_dig=4.5 重推,参考板形换锚 C3 MVP 行值总表)**:主判式 L1 −28.7/+11.3(完美翻正)、L2 −67.4/−27.4、满配 −153.2/−113.2 与 −257.1/−217.1;保守地板需求 L1 ≥ 110.3 / L2 ≥ 166.8(缺口 70.3/126.8,对 clear_blast);上段 46.1/91.8 需求系 v1 曲线旧值链产物退役;**C3 裁决②非支配设计态维持**(qualitative 结构不变——非完美态负、完美态 L1 翻正、L2 全负);护栏 A 方向性复核 ≈−1.1 越下界(E_dig 修正 + 雾池构成变更)/ 护栏 B C=40 越线——W2 复核三选一裁决随 C3 收口批;actuals 交付后按 clear_blast 语义强制重跑(B1 AC-40 同批口径) + guardrails: 护栏 A 雾板净当量优势带(每链不可达雾格 +2.4 ∈ (0, 3]——A4 pricing_constraints③ 判据;与残余池模型无关读数不变;突破条件 E_fog > 11.7 → C3 雾池联动禁区)· 护栏 B 防钓鱼(**2026-09-10 输入登记更正 + 双读数**:|S|_min = A1 行级 knob 禁写死;|R₀|_max 无登记供给方——登记 A1 产出义务〔R₀ 规模分布上界,报告 C 扩列〕;s_ref=D_ref=12 比真实敌方 DPS 期望松 2.5–3×→ D_ref 口径上界 24〔C_eff=16 ✓〕/ DPS 严格读数 ≈8〔C_eff=16 越〕——两读数裁决归 C3,与约束包①联动;结构承载 = A1 min_safe_count + 布雷数下限 + A2 R₀≠S 三闸的对账复核,非运行期校验) + a4_three_legs: 条款⑤三腿入账核对(雷险消解〔雾区无雷〕+ 信息双向净效应 + AP 腿〔链不可达全价 Δr 增量〕)——variation_table.fog_ap_note 消费面 + rerun_obligation: actuals p10 / fog_burden_ratio p50·p90 交付后强制重跑(B1 AC-40);C3 每次调参重跑(D_ref/P_pf/C 联动) + referenced_by: [C3, B4, A1] + +# 占位表中的原型参考值(伏击 +3、清图 +10、敌 18HP/4ATK 等)为 provisional, +# 不登记为事实——待各 owner GDD(A4/B3/B4/B1/C3)定稿后由其登记。 +# (AP 等七字段已于 2026-09-07 由 A3 三次回填登记为 action_economy 条目;旗上限与折算乘数同日作废删除。) +# 修订记录:2026-09-07 full 评审(design-review)——删除 combat_var 流、去重键改四元组、 +# 快照契约补计数器、R2 增实现级规约与 reward_roll 向量(owner=d2-data-config.md 代用户修订); +# 同日 re-review(5 阻塞:missing_type 分类规则/EC14 覆盖/诊断收集器/实体键解析语义/Rule 6 二分) +# + 独立 delta 复核 5/5 PASS → D2 Approved(2026-09-07)。 +# 2026-09-07 A3 RAP 结构修订(owner=a3-action-economy.md 代用户修订):action_economy 全条目重写—— +# 删 flags_per_round / flag_unit_cost / stop_multiplier / clear_multiplier,增 rap_refresh_cost 与 +# rap_rules / flag_rules / downstream_contract 事实;round_end 改双发射源(A2 棋盘路径 + A3 停手路径)。 +# 2026-09-07 A3 三轮评审修订(owner=a3-action-economy.md):flag_rules 兑付资格改雷格实例为键(原"放置实例"与 +# "拔插不重置"并存矛盾)· rap_rules 增玩家发起/多次受理/new_board 契约义务 · downstream_contract 改五条款 +# (增挖掘风险不等式 B1 闭合 + 弃图支配性)· 增 cross_field_validation(任一动作成本 ≤ ap_per_round)。 +# 2026-09-08 A4 定稿回填(owner=a4-terrain-variation.md):新增 variation_table 与 variant_pool_gate 条目; +# action_economy.referenced_by 增 A4(清图预算构成注记只读消费 ap_per_round=8,与注册表值一致零冲突)。 +# 2026-09-08 A4 full 评审修订(owner=a4-terrain-variation.md,NEEDS REVISION 8 阻塞同日核销): +# variation_table.load_bearing_rule 增结构推论(厚度≤2/f≤8d)· fog_ap_note 改写(全价→链不可达两态; +# 条款 5"消解"→三腿入账两态验算)· variant_pool_gate 增 board_biome 域校验归属与 15 项验收门禁登记—— +# 详见 design/gdd/reviews/a4-terrain-variation-review-log.md 阻塞→修复对照表。 +# 2026-09-08 A4 re-review 修订(owner=a4-terrain-variation.md,NEEDS REVISION #2 6 阻塞同日核销): +# variation_table 增 cross_field_validation(hide_numbers⇒safe_nonzero 致命)与 pricing_constraints(B4 三 +# 硬约束)· counter_item_ids 修正为非 knob · mvp_content 的 knob 活值标注 · referenced_by 补 B1/B4 · +# fog_ap_note 预算不等式改增量构成("轮数上限"作废)· 厚度≤2 钉死窗口谓词度量。 +# 2026-09-09 B3 full 评审修订(owner=b3-status-effect.md,NEEDS REVISION 7 blocking 同日核销): +# status_effect 条目 fields/mvp_content 改逐字段 YAML 键(AC-18 机器对账前提,action_economy 同构)· +# b1_obligations 5→8(⑥ decay 恰一次 ⑦ per-grant 来源标注 ⑧ 溢出行)· 增 single_enemy_assumption 与 +# presentation_obligations(D1×4/B5×2/D5/B4)· anchor_warning 增修复空间不对称(总暴露读法被 schema 域禁止)。 +# 2026-09-09 A1 full 评审修订(owner=a1-board-generation.md,MAJOR REVISION NEEDED 8 blocking 同日修订): +# board_gen_table 条目同步 B1/B3/B4/B6/B7/B8/R2/R3/R4/R9——inferability_target 域 [0.85,1](P1 绝对锚)· +# fog_target_ratio ≤ min(8d,0.25) 绝对上限 · 布雷数下限 round(d×(W×H−9))≥1 · F2c 成本谓词(att×W×H≤16384 +# provisional,禁墙钟逃生)· 达标两项判据(inferability_core = 剔除雾格后的可推理率)· 布雷钉死(先定数后落位/先布后删)· +# 断言 B 收紧达成区占比(A4⑭ 两件套)· σ 打包 2^20·L+2^12·D 防碰撞 · 指标集六→八项 · 第三轴登记约定 +# (Boss=Dmax 行 + elite_/boss_ 命名空间 + OQ6 重审门)· 黄金向量 core 整数对(派生不重制)。 +# 2026-09-09 A2 定稿回填(owner=a2-minesweeper-rules.md):新增 a2_event_surface / a2_initial_expansion 两条件目 +# (三事件回边契约 + 初始展开域与双包含兑现回执 + A1 修订义务〔反例 B〕登记);A2 /design-review 须核对标错惩罚 OQ 在位。 +# 2026-09-09 A2 full 评审修订(owner=a2-minesweeper-rules.md,NEEDS REVISION 6 blocking 同日核销): +# a2_event_surface.api_surface 增 ActionOutcome.sub_reason 封闭枚举(UX-1,批前免费窗口)· D1×1→D1×6; +# a2_initial_expansion 增诊断类别封闭枚举 + 链不越旗红线(GD-1——连锁传播集 ∖ G,G=∅ 域与 A1 算子①同构不变); +# 新增 terminology 术语条目(用语纪律登记通道)。详见 design/gdd/reviews/a2-minesweeper-rules-review-log.md。 +# 2026-09-09 B2 定稿回填(owner=b2-enemy-system.md):新增 enemy / hero / clause3_supply 三条目(EnemyTable 八字段 + +# 双敌种行 + 每行集恰一敌种校验;HeroTable 单行全局两字段 + atk=0 机制开关〔该口径 2026-09-11 作废,见下〕;条款 3 单结算自然边界供给口径〔用户定稿〕)· +# status_effect.single_enemy_assumption 回验改写(定多敌,side-wide 兼容 + target_ref 扩展位)· +# status_effect 增 ambush_unit_definition(量纲定稿,OQ2 关闭条件)· terminology 增 B2 六词条。 + +# 2026-09-11 用户裁决批(基础攻击恒存在):hero.atk 域改 [1,999]、0 = 加载拒绝(MVP=6 原型锚 HERO_BASE_ATK, +# 概念原型"每次结算的基础伤害")——"机制开关 MVP=0"与"当前内容态无胜利路径"声明作废;attack_up 复活(消费面 = +# 主角战斗段先手攻击,B1 GDD 同批增补 + source_label_map 增 hero_attack 键);锚参照回迁 attack_up 当量(status_effect. +# anchor_warning / clause3_supply.direction_precheck 两处迁移注记退役为历史);b1_obligations 履行回执 2026-09-10 不变; +# 变更 owner = b2/b4/b1 GDD 2026-09-11 同批修订。 +# +# 2026-09-11 用户裁决批(两级奖励 + 关内延续):RewardTable category 扩为五值两类交付轴(即时 75% / 可选 25%)· +# 默认根重登记为八行(attack_up_basic 复活入池 / spark_aoe / heal_minor 新增;heal_pack 稀缺化 w6 p12)· +# status_effect attack_up/defense_up duration 2→1(当次结算语义)· 库存关卡域(跨战斗延续、关卡末清空—— +# 原战斗域作废)· 窗口单件制(原任意多件作废)· reward_config p_min 4→8。 +# 变更 owner = b4/b1/b3 GDD 2026-09-11 同批修订。 +# 2026-09-09 B2 full 评审修订(owner=b2-enemy-system.md,NEEDS REVISION 8 blocking 同日核销): +# enemy.b1_obligations ① 增调用序成文(query_attack→apply_damage→advance)与敌单位专属口径 · d1_obligations +# ① 增预告时序/强度档位、② 改全局恒定 · rowset_rule 增精英/Boss 编码位预登记(B2 OQ7)· clause3_supply +# 结构上界改 min(AP, cap) 形(cap 腿松弛收紧)· direction_precheck 裕度 4.7×→2.3× 更正(最低档锚口径)· +# account_input 增胜利路径声明注记。详见 design/gdd/reviews/b2-enemy-system-review-log.md。 +# 2026-09-09 B4 定稿回填(owner=b4-reward-item-economy.md):新增 reward_table 条目(schema 八字段 + 跨字段 +# 校验族 + 机制/API 面 + B1×6/D1×3 义务清单 + A4 三约束承接 + 高光节点条目化);接口漂移改写三处—— +# rng_service.scope_event_binding 的 reward_roll 语义(每次奖励结算 → 每次 board install 批)、 +# status_effect.dangling_ref_policy(首引条件兑现)、clause3_supply.account_input(左臂载体 = B4 F6); +# action_economy.referenced_by 增 B4(F6 当量供给消费条款①②)。并行会话冲突经用户裁决:道具段"照常消耗" +# 拍板为最终语义(覆盖并行草稿"拒绝不消耗"版),清图槽允许 buff 行拍板同批落定。 + + - name: d1_presentation_surface + type: convention + owner: design/gdd/d1-hud-framework.md + attributes: + kind: 呈现面契约(读图接口清单 + 订阅纪律 + 命令式刷新面 + 载荷 schema 索引——封装收口批〔session-state entry 39/40〕切片事实的 GDD 转正) + read_map: A2 current_board()/reveal_record()/compute_move_path() · A3 getter-only phase/ap/rap/last_round_end · B1 presentation_snapshot()/is_battle_live()/stock_count()/query_reward_identity() + 步骤流信号 · B2 hero_unit()/all_enemies() · **B3/B4 零直读**(经 B1 呈现面与流水投影)· runner(节点链/当前节点/HP 携带) + write_paths: settle_batch(stop 亦经此,禁直调 A3)/ submit_window_selection(窗口条目位置序,非库存句柄) / 刷新意图 → B1 refresh_board 编排 / runner 推进——**全部唯一发生在组合根** + subscription_discipline: 呈现层永不直接订阅 A2/A3——只订 B1 步骤流/快照与 runner 信号(ADR-0001 固化);接线全部在组合根 + imperative_refresh_faces: 零事件外发动作的刷新 = 命令式回执驱动(白话:动作调用返回后组合根立刻重查一次只读面刷新界面,不等通知信号)——旗插拔/移动成功(A2)/ AP·RAP 读数(A3 无推送)/ 状态效果(B3 零信号,绑 B1 结算完成时点)/ 库存(B4 查询面)五面成文 + payload_schema_index: D1 GDD Core Rule 17 表 13 项——A2 三事件/B1 流水行协议/B4 窗口载荷等均为引用既有 schema;D1 自有成文 = ux 遥测六行(ux_round_input_ratio/ux_round_exit/ux_refresh_attempt/ux_window_choice/ux_reject/ux_hint_shown)+ hint_id 封闭枚举 {fog_first, chord, no_adjacent} + lifetime_visibility_rule: 销毁前捕获(终局/死亡数据 despawn 前已入流水与快照)/ 载荷自包含(行协议自带来源/值/目标,禁按行序反推)/ 禁回读核心生命周期态(唯一观测口 = is_battle_live() 与 phase 投影)——切片缺陷②教训转正 + naming_unification: 正式名 request_refresh(A3)/ refresh_cap_per_round(结构位默认 2)——切片 provisional 名 request_refresh_active/can_request_refresh/refresh_max_per_round/during_round 退役(AC grep 断言零残留) + drift_registrations: ① A3 只读观测量缺轮内已用刷新次数(刷新按钮"本轮剩 N 次"读数缺口——A3 修订批补)② 切片 settlement_snapshot 信号 B1 GDD 未登记契约(D1 现行消费 = 步骤流判定行 + presentation_snapshot(),独立信号须回 B1 补登记)③ 极性提案实施批 D1 义务条目(未开格类型图标/极性视觉语言/负向文案族——随批入 systems-index D1 台账) + referenced_by: [B5, D4, D5, A5, D6] + + - name: a5_aux_items + type: schema_data + owner: design/gdd/a5-minesweeper-aux-items.md + attributes: + table: AuxItemTable(D2 承载【D2×① 修订批登记】,键 = item_id;A5 回填 owner,2026-09-14 定稿登记;定义表非进度成长表——Rule 4 豁免;生效前提 = B4×①…⑤/D2×①② 修订批兑现) + fields: + item_id: String 主键唯一——B4 RewardTable.effect_id(aux 行)与 VariationTable.counter_item_ids 的引用键 + display_name: String 非空(D1 呈现数据源,本地化归 D6——B2/B3/B4 同库惯例) + dimension_tags: StringList ⊆ {info, risk, action, plan, counter} 非空——维度覆盖断言载体(F2:五维度各 ≥1 活行 = "每维度都有对应道具"的机器可查形态) + effect_kinds: StringList ⊆ {fog_reveal, mine_reveal, reward_preview, ap_grant} 非空、无重复(MVP ≤2——稀有多维组合行的 schema 承载) + target: enum {none, fog_region, area_3x3, board_wide}——校验 = target ∈ 各空间 kind 支持域交集(全非空间 ⇒ 恒 none) + magnitude: int [0,999](knob)——仅 ap_grant 行消费(= AP 点数,加载校验 ≥1);空间行不消费非校验项 + use_phase: enum {active_dig}——MVP 单值(扩枚举走 A5 GDD 修订;B4 use_timing 扩值 during_dig 同批对齐——B4×③) + mvp_content: 六行(值 provisional 归 C3)——probe_dust 显影尘〔info+counter / fog_reveal / fog_region / w5 t2〕· sapper_order 工兵令〔action / ap_grant / none / mag 2 / w5 t2〕· loot_compass 探宝罗盘〔plan / reward_preview / area_3x3 / w4 t2〕· nest_pendulum 探巢摆钟〔risk / mine_reveal / area_3x3 / w4 t2〕· catacomb_scroll 地穴全图卷〔info+plan / fog_reveal+reward_preview / board_wide / w1 t3〕· blast_kit 爆破工兵包〔action+risk / ap_grant+mine_reveal / area_3x3 / mag 4 / w1 t3〕——四常规单维(t2)+ 两稀有多维高数值(t3;稀有读数 ≈0.17 件/板,F1) + load_bearing_rules: 真视窄域护栏(承重)——mine_reveal 支持域 ≤area_3x3、board_wide 仅 fog_reveal/reward_preview(恢复推理可全图、替代推理必窄域——P1 防塌方;schema 校验强制,夹具 a5_target_violation)· 效果时效随 kind 派生无独立字段(空间 kind 恒本图持续、ap_grant 恒当轮一次性)· 零随机(零 RNG 流消费——信息全部来自 A2 恒真数字/B4 Ready 映射/A3 账本三既有事实源)· 零特判延伸(A2 代码零 A5 分支)· 无效/零信息目标不可用不消耗(可用性门——2026-09-14 用户拍板:板级判据 → 图标灰/入口禁用〔B4 usable_now 先例〕、点击级 → 拒绝零消耗、组合行判据取 OR——爆破包 ap_grant 恒可用、真视零雷恒有效;与 B4 E10 照常消耗区分——挖掘期非结算管线、守卫 A5 本地可判)· area_3x3 边缘窗口 resolved_target = 窗口 ∩ 板内格(2026-09-14 用户拍板——护栏只缩不扩)· 使用零 AP 成本(不经 try_spend)· 库存关卡域(与 B4 库存正交两容器、生命周期同构)· Used 不可逆 + overlay_semantics: 板级叠加层三集(A5 自持派生信息层、零回写 A2/B4 状态、随图消亡)——digit_reveal 显影集(D1 显示雾格数字,本体 = A2 恒真数字)/ mine_reveal 真视集(真视≠自动兑付——插旗仍花 1 AP 走既有 flag_hit 路径)/ reward_preview 预读集(徽章数据源 = B1 query_reward_identity 既有读面——A5 零 B4 数据复制) + api: 封闭枚举 8 入口(provisional,实现期 ADR 定名)——add_item(item_id, source) / query_inventory(枚举序 = 受理序,Used 保留只读,逐件含 usable 板级零信息可用性预判)/ validate_use(handle, target?) → 效果投影(只读不消耗;目标校验谓词 = 越板/点击级零信息拒绝 + 边缘窗口裁剪 resolved_target = 窗口 ∩ 板内格)/ apply_overlay(kind, target, mag)(B1 逐 kind 调用)/ consume(handle)(Used 不可逆 + 快照回执)/ query_overlays(三集只读投影)/ retire_overlays(B1 编排——与 B4 retire_mapping 同点)/ clear_inventory(关卡末幂等);负空间 = 无自动使用入口/无重掷/无事件订阅/无 RNG 直连/无 A2/B4 写入口 + use_orchestration: B1 use_aux 规范序(唯一调用权)= 相位 gate(Active——A3 phase getter)→ validate_use(目标校验读 A2 只读面——使用目标校验谓词表见 A5 GDD)→ 逐 kind 应用(顺序钉死:空间先、ap_grant 后——effect_kinds 列表序非运行时语义)→ consume——序即原子(validate 拦下全部可失败输入;apply/grant 域内无失败路径 → consume 恒达) + formulas: F1 aux 渗透供给(E[N_aux/板] = |U|×Σw_aux/W_total,默认根 ≈1.7 件/板、稀有 ≈0.17;|U| 分布缺口待 A1 actuals——B4 预检 11 同批重跑)· F2 维度覆盖谓词 coverage(d) · F3 信息价值方向预检(显影 ι↑ → 残余盲猜端 n_s = (1−ι)|S| 收缩 → 盲赌暴露下降 → 期望踩雷↓——P(雷) = |M|/(|M|+n_s) 对 ι 递增,单赌命中率上行属正常,收益在"少赌"非"单赌更安全";与 B1 F4 同式 + n_s<1 适用域守卫;2026-09-14 评审修正原"P(雷)↓"方向误写;定量当量归 B1 条款①对照表扩列——B1×④注记,MVP 不入对照表) + obligations: 21 项跨 GDD 义务清单(A5 GDD Dependencies 全文,对侧修订批逐项核对、缺项即 blocking)——B1×①②③(use_aux 编排/生命周期/授予路由,缺项即 blocking)+ B1×④(对照表扩列注记)· B4×①…⑤(category 扩值 aux_item/effect_id 双表校验+夹具 b4_dangling_aux/use_timing 扩 during_dig/默认根六行+池总权重算/OQ2 收口注记)· A3×①(grant_ap 第 9 API 接口漂移预授权——Active 受理、无上限域、零随机)· A4×①②③(fog.counter_item_ids=[probe_dust] 回填/悬空校验激活+夹具 a5_dangling_counter/未来变异随批配 ≥1 反制)· D1×①…④(库存条/使用交互/叠加层三集呈现/清空反馈)· D2×①②(AuxItemTable 登记/两处表值回填+校验激活)· D5×①(教学候选,非 blocking)· art-bible×①(§4.6 键扩容:item_id 六键+叠加层标记三族) + ux_flag: 有 UI 贡献(库存条/使用交互/叠加层呈现)——Phase 4 前置 /ux-design;asset-spec 候选(art-bible 样张批后) + closures: A4 EC6 显影语义定稿(=fog_reveal,恢复推理原料非排雷;时效 = 本图持续)· A4 OQ2 收口(时效/定价/首引回填/悬空校验)· B4 OQ2 A5 侧收口(呈现深度 = 道具驱动预读;未预读格不呈现、自动寻路不做——终态与否 OQ6)· 创意风险 #1 道具侧兑现(plan 维度预读——结构闭环最后一块,呈现归 D1) + warnings: 预警①…④ playtest 预注册归 C3(道具依赖度/预读覆盖率/AP 转存联动/稀有感知) + referenced_by: [B1, B4, A4, A3, D1, D2, D5, art-bible] + + - name: difficulty_table + type: table + owner: design/gdd/c3-difficulty-curve.md + attributes: + kind: 全局修饰结构位(MVP 空表——零行为) + key: (level_index, node_depth) + fields: 修饰乘数(域预登记 [0.1,10],越域 clamp+warning——非 fail-fast,组合事故非配置事故) + application_point: B2.spawn 实例化时、行值快照前套乘(B2 Rule 10 兑现——接口漂移建表随 C3 实现批) + first_use_scenario: 精英节点(C1 分支地图定稿后联动设计——C3 OQ5);Boss 走 Dmax 直填行不走本表 + referenced_by: [B2, C3] + + - name: difficulty_expectation_table + type: formula_set + owner: design/gdd/c3-difficulty-curve.md + attributes: + kind: 调参目标带(F5)+ 承伤估算(F4)+ 供给平衡式(F6)——设计期工具式,运行时零新增公式(C3 定位声明) + clear_rate_target: L1 基线 **30-50%**(2026-09-14 带锚定稿——旧 40-60% 由本条替代;现扫描 60% 越上界,加难杠杆批内执行 OQ7)/ L2 20-40% / L3+ 10-25%(中高天赋带见 GDD F5 表——区间位移机制承载:atk 翻倍 ≈ K 减半 → 绿档) + supply_balance_band: "**关级供给带 [1.1,2.0] 退役(2026-09-14——其验算 0.54 与实测 60% 通关矛盾)**——由生存链区间模型替代:每战 S/K 区间三档判定(绿 S_min>K_max / 黄 E[S]≥E[K]+1 / 红 S_max≤K_min 禁止入表)+ 关级恒等式(atk×ΣR_survive + Σ奖励输出 ≈ Σ敌hp期望×(1+缓冲),actuals 逐项审计闭合)+ 输出占比带 [15%,40%] + 通关余量带(p90≤13 & p10≥3——'通关只剩一点血'量化)" + tool_carrier: level_monte_carlo.gd / reward_expectation.gd(切片原型→C3 期正式批量工具,A1 F4 排期倒挂解除载体) + calibration_gap: 真人修正系数待 playtest 标定(OQ3——**方向已由试玩批次 1 反向确认:真人显著强于模拟者**〔v2 真人 100% vs 模拟 80%〕;旧"真人预期低于模拟"方向声明由本条替代;带按模拟口径定义,真人系数批次 2 定量) + referenced_by: [B4, B1, C2, C4] + + - name: boss_dmax_convention + type: convention + owner: design/gdd/c3-difficulty-curve.md + attributes: + rule: Boss = 各关最大深度 Dmax 行(A1 第三轴登记约定的 GDD 侧兑现)+ LevelTable 末节点必须 Boss(schema 校验)+ 专属行为环含 charge 拍 + boss_hp_anchor: F3 = Σ普通战斗hp期望 × r_boss ∈ [0.5, 1.1](现行带——旧带 [0.35,0.55] 与"切片 0.81 越上界"读数由本条替代,OQ2 采信用户"可上调"裁决后修订)· L1 v3 记录值 150/153.5 ≈ **0.98** 带内 ✓(期望口径含 unit_count 区间骰 Σ=153.5——2026-09-14 C3 修订批口径统一,旧固定口径 Σ=142 读数替代)· **加难走廊 [1.1, 1.35] 预登记**(Boss 180–207 候选,OQ7 扫描终裁) + elite_reserved: elite_/boss_ 前缀命名空间预留(A1 B7);精英完整语义归 C1 定稿联动(OQ5/OQ6 强制重审) + referenced_by: [A1, B2, C1, C2] + + - name: level_table + type: schema_data + owner: design/gdd/c2-adventure-structure.md + attributes: + table: LevelTable(D2 承载,键 = level_index 行、行内 nodes 节点链数组——切片建表,C2 定稿转正为行集内容 owner) + fields: + level_index: int 行主键(冒险序列序号,从 1 连续递增——线性序列无跳关) + display_name: String(玩家可读层名——L1"第一层 · 苔藓回廊"切片现行;L2 provisional"第二层 · 雾漫巢径",终稿随 L2 内容批) + rest_heal_pct: int [0,100](knob 默认 30——恢复点回复 max_hp_eff 的百分比,随上限加成同步放大防高上限档恢复失效;数值复验归 C3) + nodes: 节点链数组——node_kind ∈ {battle, reward, rest, boss}(切片字段名 type);battle/boss 携带 node_depth;reward/rest 不占深度 + rowset_rule: Boss 恒末节点(schema 校验);战斗节点深度 = 前序战斗节点计数、Boss = Dmax(A1 第三轴约定兑现面);MVP 链模板 = battle D0 → battle D1 → reward → battle D2 → battle D3 → rest → boss D4(7 节点 5 战斗深度,L1/L2 同构复用) + cross_table_validation: battle/boss 节点 (L,D) 在 board_gen/enemy 两表存在精确行(D2 跨表校验⑧复用 + C2 AC-7 冒烟断言——序列关数 ⊆ 内容覆盖,越界关卡 = 内容生产缺口) + mvp_content: L1 全行(切片转正——7 节点链、rest_heal_pct=30、全程无雾);L2 行 = N=2 扩展批(同构模板 + C3 深度轴行值同批填充——C2 GDD 生效前提,未落地前冒险以单关行集运行 N=1 合法态) + referenced_by: [C2, C1, C3, A1, D2] + + - name: adventure_table + type: schema_data + owner: design/gdd/c2-adventure-structure.md + attributes: + table: AdventureTable(D2 承载,单行全局;C2 回填 owner;Rule 4 豁免——单行全局常量表,HeroTable/SettlementTable 先例;建表走 D2 接口漂移规则) + fields: + level_clear_hp_bonus: int [0,999](knob 默认 10——关卡大奖励 HP 上限提升,冒险内生效经 begin_battle 覆盖值通道传入后续每战;值锚 = C3 区间平移换算率 +10hp ≈ +1 轮,上调须 C3 重扫 L2 通关率带) + token_per_level: int [0,999](knob 默认 10——每通关一关的代币记账值;消费方 C4) + mvp_content: 两字段默认值即 MVP 行(10 / 10,provisional) + extension_reserve: 大奖励形态扩列位(三选一 / 攻击型 / 通关加成——C2 OQ2,随 C4 元进度设计) + referenced_by: [C2, C4, D2] + + - name: hero_override_parameter_passing + type: convention + owner: design/gdd/b1-settlement.md + attributes: + kind: 主角战斗入参覆盖值通道(begin_battle 显式参数传递——切片 E4 封装收口批实现,registry 登记缺口由 C2 定稿批补登) + contract: begin_battle 增 hero_hp / hero_atk / hero_max_hp 显式入参 → B2.spawn 增 atk_override / max_hp_override 参数;-1 = 表值兜底(不覆盖);hero 表运行期不可变(D2 单一事实源机制化——表写通道全删) + consumers: HP 账本(C2——跨战斗 HP 持久性 + 大奖励上限提升的传入面)/ 元进度加成(C4 远期预留)/ 切片 TunePanel(调试通道) + adr_status: ADR-0009 候选——正式侧 B1/B2 GDD 修订随 ADR-0009 批;C2 GDD 规则 3/4 为 C2 半边(HP 账本供值)的正式定义源 + referenced_by: [B1, B2, C2, C4] +# 2026-09-09 B4 full 评审修订批(owner=b4-reward-item-economy.md 代用户修订,NEEDS REVISION 14 blocking 同日核销): +# 新增 reward_config 条目(RewardConfigTable 单行全局——T_fog_min/K_dmg/N_draft/P_min 的 Rule 1 载体); +# reward_table 增 display_name(九字段)+ 默认根行集登记 + direction_precheck(刀供给)+ 跨字段护栏两条 +# (floor_supply_missing / no_damage_row——B2 OQ9 并案)+ 窗口锚点改授予段后(用户评审拍板:当轮拾取当轮可用) +# + 重复受理幂等语义 + B1×6/D1×3→D1×5 义务清单同步;锚参照迁移传播三处(本条 anchor_warning 迁移注记 / +# clause3_supply.direction_precheck 迁移注记 / systems-index A3 OQ5 注记——attack_up 口径历史值与 defense_up +# 数值巧合全等、迁移后预检未重跑事实同批登记);terminology 增 B4 六词条(刀/批尾/死行/档位序检查/预推进/烧刀烧包)。 +# 详见 design/gdd/reviews/b4-reward-item-economy-review-log.md。 +# 2026-09-10 B1 定稿回填(owner=b1-settlement.md):新增 settlement_table / clause5_dominance 两条件目 +# (SettlementTable 七字段 + 三行致命 manifest + 段序骨架/可回放/高光双落载体;条款⑤主判式 + 保守地板缺口 + +# 两护栏 + 三腿入账)· 接口漂移回写四处——status_effect.mvp_content.weaken magnitude −25→−65(F0 裁决, +# 值域 [60,85],终值共裁 B1 OQ2)+ anchor_warning F0 裁决回执(红警关闭:瞬时当量口径/地板层数=2/defense_up +# 参照保持)· clause3_supply.direction_precheck 闭合回执(≈2.0×/2.6× 边际配对、满配破线 C3 约束、pp 代理退役) +# · board_gen_table.inferability_vs_p_safe 消费回执(P(安全) 残余盲猜端代理式);status_effect/enemy/reward_table +# 三处 b1_obligations 履行回执(23 条义务全落位 b1 AC 覆盖矩阵);terminology 增 B1 五词条 +# (完美排雷/当量/乘区/溢杀/免伤轮)。 +# 2026-09-10 B1 评审批修订(owner=b1-settlement.md,/design-review NEEDS REVISION 同日核销 8 blocking): +# settlement_table——p_perfect 载体域 {100,200}(真实双份交付)、pool_clamp_lo_hi 拆 lo/hi 两字段(八字段口径统一、 +# D2 GDD 行按收编先例以本条为准)、source_label_map 键集补 enemy_action/flag_miss、mechanics 补行协议字段分层; +# clause5_dominance——模型修订重登(挖穿残余池计价、条款⑤未闭合 + C3/B4 约束包 + 护栏 B 双读数 + RAP 零价假设); +# clause3_supply——闭合回执双口径更正(锚行 ≈2.0×/2.6× / 期望 ≈1.2×/1.5×、破线约束两轴)。 +# 详见 design/gdd/reviews/b1-settlement-review-log.md。 +# 2026-09-13 D1 定稿回填(owner=d1-hud-framework.md):新增 d1_presentation_surface 条目(呈现面契约—— +# 读图接口清单/订阅纪律/命令式刷新面/载荷 schema 索引/生命周期可见性规则/命名统一/接口漂移登记 ×3); +# terminology 增两词条(命令式回执驱动/程序化占位)+ referenced_by 增 D1。solo 模式出稿待人工复核。 +# 2026-09-14 A5 定稿回填(owner=a5-minesweeper-aux-items.md,solo 模式一次会话定稿、出稿待人工复核): +# 新增 a5_aux_items 条目(AuxItemTable 七字段 + MVP 六行【四常规单维 + 两稀有多维,值 provisional 归 C3】+ +# 真视窄域护栏承重规则 + 叠加层三集语义 + 8 API 面 + use_orchestration 规范序 + F1–F3 + 21 项跨 GDD 义务清单 +# + A4 EC6/OQ2 与 B4 OQ2 收口);variation_table.mvp_content.counter_item_ids 回填 [probe_dust](A4 预授权 +# "A5 期回填"兑现——D2 表值与悬空校验激活随 D2/A5 修订批同批落地)+ dangling_ref_policy 首引兑现改写; +# terminology 增四词条(叠加层/真视窄域护栏/渗透率 + referenced_by 增 A5)。 +# 生效前提:B4×①…⑤(category 扩值 aux_item/use_timing 扩 during_dig/默认根六行等)与 D2×①② 修订批—— +# 未兑现前 RewardTable 含 aux 行会加载致命(B4 Rule 1 预留值拒绝)、A5 GDD AC-12/13 登记 deferral。 +# 2026-09-14 跨评审回写批(/review-all-gdds 🔴-1/🔴-2 修复,owner=b3/b1/d2 GDD 代用户修订): +# 批1(D2 漂移批 + B3 接口漂移批)——status_effect.mvp_content.defense_up magnitude −50→−30(减伤墙批 +# 2026-09-13 C3 投值的对侧回写落地);rng_service.scope_event_binding 增 encounter 绑定 + referenced_by 增 B1; +# stream_seed_derivation 增 salt_encounter=0x8EBC6AF09C88C6E3 与第 6 常量黄金向量(encounter m=42 scope0)+ +# set_master_seed 全流零计数前提;terminology 免伤轮词条随减伤墙批退役旧例。 +# 批2(B1 重算批)——status_effect.anchor_warning F0 回执腿读数更新(E_dig 7.2→4.5)+ 参照回迁迁移注记关闭; +# clause3_supply.direction_precheck 增 2026-09-14 重算回执(现行值链破线 ≈0.48×/0.47×、约束升格曲线级—— +# 禁独立调参联动网络 m_w↔D_ref↔m_d↔duration_d);clause5_dominance 增重算回执(v3 曲线四态 + 需求 110.3/166.8 + +# 裁决②维持 + 护栏 W2 待重推注记);settlement_table.mechanics 增 B1 F6 充能与护甲克制结算规格登记(W3)。 diff --git a/design/research/lore-nest-metaphor-2026-09-12.md b/design/research/lore-nest-metaphor-2026-09-12.md new file mode 100644 index 00000000..b2f156dc --- /dev/null +++ b/design/research/lore-nest-metaphor-2026-09-12.md @@ -0,0 +1,111 @@ +# 世界观设定记录:雷 = 怪物巢穴(地牢隐喻增补提案) + +> 日期:2026-09-12 +> 性质:世界观设定记录(叙事 / 隐喻层)——**非机制定案**。本稿不触碰任何已锁机制数值、接口与 GDD 正文(A1–B4、B1、C3 已定稿内容不受影响);涉及机制侧的事项仅在 ③ 末行以"边界提醒"列出。 +> 状态:**已拍板并采纳(2026-09-12),概念层修订已执行**——game-concept 世界观与命名节 + 视觉锚原则 5 已带 2026-09-12 版本注记改写;systems-index 备注·概念层增补已登记;⑤节六问拍板结论已逐条回写(含两处修正案:⑤-1 惩罚格类型化、⑤-2 对外动词"控制")。原状态:草案,待用户反馈。 +> 素材来源:用户提案(雷图 = 地牢 / 雷 = 怪物巢穴 / 插旗 = 封印或火把 / 安全格 = 探索安全区 / 道具 = 宝箱)+ 新拍板机制"标错旗 → 随机敌增伤 / 敌 buff,或我方 debuff(时效约 1 回合)"的包装需求。 +> 关联:`game-concept.md`(世界观与命名,定稿 2026-09-09)· `game-pillars.md`(P1–P4)· `b2-enemy-system.md`(Overview / Player Fantasy)· `systems-index.md`(概念层增补块)· `a2-minesweeper-rules.md`(OQ1 / EC5 / GD-1 / Rule 11)· `a4-terrain-variation.md`(雾区无雷)· `b4-reward-item-economy.md`(install 期预分配)· `b1-settlement.md`(完美排雷 / 标中旗 highlight / flag_miss 行)。 + +## ① 设定评估 + +### 1.1 与现行正典的关系:升级换轴,不是推翻 + +现行定稿映射(game-concept 2026-09-09):雷 = 地牢**陷阱**、旗 = 探险者**标记的已知陷阱**。本提案把两行再具体化一步: + +- 雷:陷阱 → **怪物巢穴** +- 旗:被动标记 → **封印 / 火把(主动干预)** + +关键差别在主体性:陷阱是纯环境危险(风险单向),巢穴是**争夺中的资源**——同一个实体,处置方式决定它是威胁还是战利品。定稿映射里"触发即魔物占得先机"的 P3 注记,在陷阱框架下只是补丁式解释(踩陷阱为什么敌人变强?),在巢穴框架下是结构本身:巢里的东西出来了,先机自然归它。**叙事自洽性的一次实质升级,值得采纳**——但属正典改写,须显式版本注记(见 ③)。 + +### 1.2 对现有游戏的帮助 + +1. **P3"风险与收益同源"获得字面意义的自洽**。支柱原文"雷既是敌人的武器,也是你的赌注"在机制上已成立(踩雷 → 伏击 / 标中 → 敌 debuff),但陷阱隐喻下两个结果挂在同一物体上略生硬——陷阱只该有坏处。巢穴隐喻下两者天然同源:不处理 → 怪物倾巢而出(敌 buff);封印成功 → 巢穴废置、巢主暴露(我方增伤 / 敌削弱)。玩家全部风险行为围绕"巢穴"这一个实体展开,**没有第二套惩罚源**;新拍板的标错旗惩罚也属这个同源家族(源于你对未知格的错误处置),叙事上不违反 P3"不引入第二套随机惩罚"。 + +2. **P1 读怪有了动机包装**。B2 Player Fantasy 的"赌桌对面的那个存在"是抽象定位;巢穴框架给它具象身份——**敌人就是这些巢穴的居民**。你排的不是抽象雷区,是怪物的居住区;读怪(视觉推测型:"它弓起背了")= 观察巢居生物的习性。MVP 双敌种与框架严丝合缝:grub(蛆虫)= 腐巢孵出的爬虫(环长 1、无前摇、蠢而直接),ambusher(伏击兽)= 巢区猎手(潜行-蓄势-扑杀,三拍环)。"我谨慎它就弱、我贪婪它就吃壮"的闭环也更本能:你惊动多少巢,它就吃多少先机。 + +3. **P4 地牢下探氛围获得动词**。现行叙事是静态的(下探陷阱地砖阵);巢穴框架给下探一个目的动词——**肃清**(sweep):每层被巢穴侵染,清图 = 肃清本层巢区,下探 = 追着更深处的巢走。产品名双关(minesweeper → dungeon sweeper)从"清扫地牢的人"强化为"清巢者",P4 的命名承载更实。 + +4. **新伤害 A / 伤害 B 分量的学习成本下降**。两个增伤分量若裸呈现,玩家会问"为什么有两个伤害加成"。叙事给每个分量独立的因、源和心智模型: + - 伤害 A(开格增伤)= **地利**:探明 = 站稳脚跟——视野、据点、无后顾之忧,所以打得准打得狠; + - 伤害 B(标雷增伤)= **暴露**:被封印的巢穴废置,巢主失去庇护,打它更疼。 + P2 归因同步受益:结算明细语言从"开格数 / 标中数"升级为"地利 +X / 封印 +Y",来源可指认。另注意:现行机制里标中旗收益是敌 weaken,拟定中的伤害 B 是我方增伤——**封印框架对两种读法都成立**(失庇护 = 被削弱;暴露 = 好打),lore 对机制侧最终落点保持鲁棒。 + +5. **新拍板"标错旗惩罚"的叙事免费午餐**。旧框架下"标错了"难有惩罚自洽性——标记只是笔记,笔记错了为何要罚?封印框架下自明:**封印是主动的断言 + 仪式,立错了就有反噬**。新机制(随机敌增伤 / buff 或我方 debuff,约 1 回合)直接获得叙事壳,详见 1.3-4。 + +### 1.3 风险与不自洽点 + +1. **连锁展开(一次性免费开一大片安全区)**。逐格挖掘是"探索",连锁瞬间撕开半张图需要节拍解释,否则探索感被"作弊感"稀释。候选包装:(a) **空腔连通**——数字 0 = 四面寂静无巢,地牢厅室本就被掏空连通,你只是让光进去;(b) **坍塌显形**——挖穿薄壳,整片松动地面塌陷露出;(c) **回声感知**——探险者的感知沿寂静区扩散。三选一影响 B5 连锁动画 / D4 音效基调(⑤-4)。顺带:连锁不越旗(A2 GD-1)在 (a)(c) 下读作"封印挡住了蔓延 / 你拒绝在未确认处继续掏",自洽零成本。 + +2. **已引爆雷格可通行、可站立**(A2 Rule 11)。这是巢穴框架的**意外强项**而非弱点:开雷 = 怪物倾巢而出占得先机(伏击 +3/层 的叙事因)→ 巢已空、残址可通行。敌方 buff 与格子可走两个机制副作用由**同一个叙事事件**一次性解释,比"爆掉的陷阱"更顺(弹坑为什么能站?)。建议把这条因果写成标准句式供 D1/D5 引用。残余小问:站上"刚被惊动的巢址"是否留表现层余韵(余温 / 噪声衰减)——纯美术取舍,留 art-bible。 + +3. **雾格的叙事身份**。机制承诺很硬:雾区无雷是**玩家可见知识**(A4)、雾内插旗恒 hit=false 纯损、雾区连 0 格都没有。叙事若按惯性做成"危险的迷雾"会与机制正面对撞(叙事暗示有险、实际绝对安全——违反 P2 诚实)。候选正调:**净化之雾 / 瘴气死区**——确认无巢、但气息读取(数字)失效的盲区;雾内封印恒不中 = "净化区无巢可封"。D1 图例条目(A4)与 D5 教学措辞需跟随这个定调。 + +4. **标错惩罚的"随机"如何不显得玄学**。玩家无法预测反噬流向——处理不好就是"系统随机整我"。解法是给随机一个**世界内的因**:封印仪式失控,溢出的能量 / 惊扰**流向不可控**(或惊动真巢 → 敌方受益;或反噬施术者 → 我方短暂失衡,约 1 回合)。同一个因解释两个分支,"随机"被读成"失控"而非"玄学"。三条护栏建议:(a) 明细行写明来源(B1 已有 flag_miss"标旗未中"行协议位)+ 固定因果措辞("封印失败");(b) D5 教学把教训定为"别乱封"(确定性防御),不引导玩家"赌反噬方向"(那才会伤 P1);(c) A2 OQ1 重复性未决(hit=false 现无去重:每轮累计 vs 一次性)对应两种叙事("反噬未平息 / 流血" vs "当轮反扑"),措辞等机制定案后跟随,不先写死。 + +5. **宝箱密度**。B4 结构是"每格安全格必有货"(install 期预分配奖励身份)。每格摆一个"宝箱"实物的量级会击穿地牢尺度感(一屋 30 个箱子)。建议把"宝箱"从**每格实体**降维成**搜刮框架**:探明区域的每一寸都可能翻出前人遗落物——即时类 = 当场消耗(药草、口粮、现场磨刀),可选类 = 收进行囊(刀、治疗包,库存 = 关卡域即行囊到本层结束)。"宝箱"保留为高档位 / 可选类格的视觉强调形态(⑤-5)。 + +6. **术语漂移与文档一致性**。定稿映射表标注"定稿 2026-09-09",采纳本提案须显式版本注记改写,不能静默覆盖;各文档已固化的"陷阱"字样(game-concept 视觉锚原则 5、A2 教学条目等)需一次口径清扫,或在 lore 中声明"陷阱 = 旧称 / 部分 biome 变体"。机制侧边界提醒见 ③ 末行。 + +## ② 机制↔叙事映射表 + +("用户提案"= 本次口述;空 = 提案未覆盖;补充建议 = 本稿新增,供挑选。) + +| 机制元素 | 叙事包装(用户提案) | 补充建议 | +|---|---|---| +| 雷图 / 扫雷图 | 地牢的一层 | "被巢穴侵染的厅室";产品名双关强化为"清巢者";清图 = 夺回本间厅室 | +| 雷(未开格) | 怪物巢穴 | 建议定为正典;旧"陷阱"降级为部分 biome 变体皮或旧称;数字 = 相邻巢穴的气息数(探险者的感知,非系统 UI) | +| 开雷(cell_revealed{kind=mine}) | 踩中怪物巢穴 → 惩罚 | "惊动巢穴——怪物倾巢而出占得先机":伏击 +3/层 = 惊动程度;同一事件顺带解释巢址变空、可通行(标准句式供 D1/D5 引用) | +| 插旗 · 标中(flag_judged hit=true) | 封印 / 插火把 → 巢穴不可用 → 伤害加成(伤害 B) | "封印"承载"失败有反噬"(服务标错惩罚),"火把"承载探险感;同时覆盖现行 weaken(失庇护 = 削弱)与拟定增伤(暴露 = 好打)两种机制读法;标中旗 highlight(B1)= 封印生效脉冲 | +| 插旗 · 标错(flag_judged hit=false) | (新拍板:随机敌增伤 / buff,或我方 debuff,约 1 回合) | 叙事 = "封印失败的失控反噬,流向不可控"——一个因解释两个随机分支;1 回合时效 = 失衡短暂平息;护栏见 1.3-4 | +| 安全格 | 探索到的安全地区 → 伤害 A | "地利":探明 = 站稳脚跟(视野 / 据点 / 无后顾之忧);结算措辞候选"地利 +X" | +| 连锁展开(免费 flood fill) | (提案未覆盖) | "空腔连通 / 坍塌显形 / 回声感知"三选一(⑤-4);连锁不越旗 = 封印阻断蔓延 | +| 数字提示 | (提案未覆盖) | 贴地听音 / 数巢穴气息——把 UI 读成探险者技能;D5 教学语言来源 | +| 初始安全区(3×3,无奖励) | (提案未覆盖) | 入口营地 / 前人据点——已搜刮过的出生点(对齐 initial 通道无奖励);与"扎营"意象共用词汇 | +| 道具 / 奖励(B4 install 期预分配) | 藏在安全地区的宝箱 | "前人探险者的遗落物 / 搜刮"框架替代每格宝箱(密度见 1.3-5);即时类 = 当场消耗,可选类 = 收进行囊 | +| 清图奖励(H2 强调呈现) | 搜刮完毕 | "夺回犒赏 / 肃清仪式"——轮末仪式峰值的叙事由头;完美排雷(P_perfect 默认 ×2)= 全图封印成功的猎人荣耀 | +| RAP / 停手 | (提案未覆盖;用户点名候选"扎营休整") | 就地扎营蓄力;攒满刷图 = 放弃本间厅室改走支线——"知难而退是冒险者的判断",风险退出的叙事尊严 | +| 雾格(A4) | (提案未覆盖) | 候选"净化之雾":确认无巢但气息读取失效——与"雾区无雷 = 玩家可见知识"严格对齐;雾内旗恒不中 = 净化区无巢可封 | +| 敌人(grub / ambusher) | (提案未覆盖) | 巢居生物:蛆虫 = 腐巢爬虫(环长 1、无前摇),伏击兽 = 巢区猎手(潜行-蓄势-扑杀)——读怪 = 观察巢居习性(B2 视觉推测型的具象落点) | +| Boss(C3 boss_guardian) | (提案未覆盖) | 巢穴之主 / 守巢者;Boss 图全局变异(C3 scope=global)= 母巢领域浸染整层地砖 | +| 伏击池层数(side-wide,默认 cap 4) | (提案未覆盖) | HUD 呈现候选"骚动档位"(蠕动 / 骚动 / 暴动 / 倾巢,默认 cap=4 下四档)——给共享池一个可读皮肤,强化"它强是因为我喂的"(P2);溢出行 = 惊扰过饱和、部分先机未生效 | + +## ③ 文档影响清单 + +(判断基准:现有 GDD 是机制文档;世界观属概念层 / 表现层——值得写回 game-concept 的只有"承重隐喻"本身,其余留给 art-bible 与垂直切片。) + +| 目标文档 | 建议改动要点 | 层级 / 优先级 | 时机 | +|---|---|---|---| +| `game-concept.md` 世界观与命名节 | 若采纳:映射表改写 2 行(雷 → 怪物巢穴;旗 → 封印 / 火把)+ 背景 1–2 句(巢穴侵染、前人遗落物);带显式版本注记(参照 2026-09-09 概念层增补句式"表现层 / 命名层,不触碰任何已锁机制数值与接口") | 概念层 · **高**(P3/P4 承重隐喻 + 全项目教学语言源头,值得写回) | 用户对 ⑤ 拍板后一次性改写,不逐条渗漏 | +| `game-concept.md` 视觉锚原则 5 | "雷 = 触发的陷阱机关"候选表现补一词:"触发的巢穴 / 崩塌的巢址" | 概念层 · 中 | 同上一批 | +| `game-pillars.md` | 建议**不改**——支柱是评审尺度,保持抽象;巢穴意象与 P3/P4 现有措辞("触发即魔物占得先机" / "命名承载")自然兼容 | — · 低 | 不动 | +| `systems-index.md` 备注·概念层增补 | 采纳后追加登记行(2026-09-09 块同款句式) | 流程登记 · 低 | 采纳后顺手 | +| **art-bible**(未来任务) | 输入:旗的三形态(火把 / 封印符 / biome 变体)、未开雷格"隐约巢感"样张、空巢已开格表现、净化之雾表现、巢居生物剪影(衔接 B2 Visual Req 1/2)、遗落物图标;**格子样张测试验收项不变(数字可读性第一)**,巢感只能做在数字可读性余量内 | 表现层 · 中高 | art-bible §1–4 创建前(最晚 D1 GDD 前),与格子样张测试同批 | +| **B5 战斗表现**(垂直切片) | 方向性输入:敌人出场 = 自巢中涌出、战斗空间巢穴装饰、连锁动画三选一(1.3-1);**不预设舞台**(双舞台资产管线陷阱照旧) | 表现层 · 低中 | B5 GDD 前战斗表现美术测试 | +| **D5 教学引导**(Alpha) | 措辞候选:"挖到雷 = 惊动巢穴而非死亡"(现有条目叙事化)、"雾内插旗纯损 = 净化区无巢可封"、"旗挡链 = 封印阻断蔓延"、标错惩罚上线时"封印失败有反噬" | 表现层 · 低 | D5 GDD(设计顺序第 19 位) | +| D6 / 文案 | 结算明细叙事措辞(伏击行"惊扰 +N"、flag_miss 行"封印失败"、地利 / 封印增伤行名)——跟随机制定案,本稿只供词汇表 | 表现层 · 低 | 文案期 / D6 | +| **机制侧边界提醒(非本稿范围)** | 新拍板"标错旗 → 随机敌增伤 / buff 或我方 debuff"若与 A2 OQ1 现行候选(敌方伏击 +1)不同:机制落点须走 A2 OQ1 更新(含重复性裁决:hit=false 现无去重)→ B2 OQ6(事件源 = flag_judged{hit=false})→ B3 source_class 新枚举(**结构冻结项,须 B3 GDD 修订,非加行**)→ B1 兑付编排(flag_miss 行协议已在 B1 评审 blocking ⑤ 留位);"随机"的 rng 流归属归机制侧裁决。本稿 lore 对"一次性 / 每轮累计""敌侧 / 我侧"各落点均兼容 | 机制侧 · 中 | 机制侧推进时(A2 修订会话) | + +### ③ 补充登记(2026-09-12 概念层修订执行后 · /consistency-check 抓获) + +1. **B1 L29 / B3 L22 "雷是赌桌不是陷阱"措辞碰撞**:换轴后"陷阱"为惩罚格类型词表一员(未来类型:直接扣血),句面可误读为"雷格不是陷阱类型";句本意(雷是争夺中的资源、非单向危险)在新正典下反而更成立,风险低。措辞更新归 B1/B3 各自下一批修订(候选替换:"雷是赌桌不是死局"),本会话不动机制 GDD。 +2. **battle.md 玩家可见文案采纳点**:插旗/拔旗/标旗等玩家可见动词在文案 pass / D1 批统一替换为"控制"(battle.md L258 明载文案定稿权归实现期文案 pass;对内交互语言仍为旗/插旗——见 ⑤-2 拍板)。 + +## ④ 补充设定点子(供挑选,非定案) + +1. **前人遗落物(道具来源正典化)**:地牢吞噬过许多探险者——白骨与行囊散落各处。一个设定同时解决三件事:道具从哪来(B4 每格必有货的合理性)、危险的实证(不用台词就传达"这里死过人")、搜刮框架的实体锚。可选延伸:极低频"前人日志"碎片作零成本氛围文本(取决于 ⑤-6 厚度预算)。 +2. **Boss = 巢穴之主 / 母巢**:每层一母巢,Boss 战 = 断根;下探 = 沿母巢链追猎。C3 已有的 `boss_guardian`(守巢者)可直接收编;Boss 图全局变异 = 母巢领域把整层地砖浸染(岩浆 / 冰冻等变异的叙事壳——"巢液浸染")。 +3. **RAP = 扎营休整**:停手 = 就地扎营(篝火意象,Roguelike 认知零成本);RAP 攒满刷图 = 放弃本间厅室、改走支线。把"风险退出"从机制词变成冒险者的尊严判断——知难而退也是探险技术(与 P4 同频)。 +4. **旗随 biome 换皮,动词统一**:地牢 = 火把、圣所 = 封印符、腐巢 = 驱虫香……视觉形态换、机制动词统一为"镇 / 封"。直接服务视觉锚原则 4(调色板交换优先于新增资产);也回应"封印 vs 火把"之争——不必二选一,定一个正调、其余作变体(⑤-2)。 +5. **敌种叙事身份轴(C3 内容扩展用)**:现轴 = 巢居虫群型(grub:多、薄、无脑)↔ 巢穴猎手型(ambusher:蓄势、爆发、读得懂)。未来新敌种(含拟议中的护甲类怪)沿轴投放:甲壳巢卫(厚重硬壳 = 高防 / "火烧封印破甲"的隐喻接口)、噬旗腐虫(专拆封印 = 反旗机制的叙事壳)等——仅登记方向,数值与投放归 C3。 +6. **伏击 = "骚动"计量**:敌方共享伏击池的 HUD 呈现换骚动档位词(蠕动 / 骚动 / 暴动 / 倾巢)。给 side 池一个叙事皮肤,让"它这一拳为什么重"的归因行(基线 + 惊扰,B2 分量二元组)多一层可读性——纯呈现候选、零机制含义。 + +## ⑤ 开放问题(2026-09-12 用户拍板,逐条回写) + +> **回写首注**:六问全部有结论。其中两问未采纳本稿推荐、走了用户修正案——⑤-1 以**惩罚格类型化**取代"单类型正典"之争,⑤-2 以类型无关动词**控制**取代封印/火把之争。②表相应行(雷、旗)与③表首行的执行口径以本节拍板为准;本稿正文中残留的"封印失败"字样一律按⑤-2读作"控制失败"。 + +1. **正典取舍** — **已采纳(修正案):惩罚格类型化**。雷格对外叙事称呼定**惩罚格**(机制名"雷/雷格"不变),下辖类型;首发地牢类型 = **怪物巢穴**(与现行锁定机制严丝合缝:惊动→怪物倾巢而出占先机、标中→巢主失庇护 debuff);"陷阱"降级为类型词表的一员(不再是正典属名,也未废弃)。类型机制分化——陷阱→直接扣血、标中陷阱→怪物受伤害、岩浆/冰冻化身各配效果——**登记为未来机制侧方向**(归 A1/A2 修订裁决;"陷阱→直接扣血"与现行锁定规则"开雷→敌方优势非直接伤害"相抵触,取舍归机制侧,可能连带 P3 表述复查),概念层只写属名结构、不承诺任何分化效果。 +2. **旗的正调** — **已采纳(修正案):控制**。玩家可见动词定"控制"(类型无关——控制巢穴 = 封印镇住、控制陷阱 = 借力反制,未来岩浆/冰冻类型各有着落);对内文档与机制协议仍为"旗/插旗"(B1/B2 现行协议零变化)。本稿"封印/火把"之争的降级落点:封印/镇/反制 = 类型风味词(结算行与教学的可选风味层,不承担机制含义);火把等视觉形态 = biome 换皮候选归 art-bible(只登记)。小尾巴:按钮/tooltip 上"控制"的手感归 D1 GDD。 +3. **标错惩罚叙事** — **已采纳:失控反噬**(本稿推荐)。保留机制随机,随机读作失控:控制失败、反噬流向不可控(流向真巢 → 敌受益;反噬自身 → 我方失衡,约 1 回合)。三护栏随行:(a) 结算固定措辞"控制失败"(B1 flag_miss 行协议位;本稿原文"封印失败"随⑤-2更改);(b) D5 教学定调"没把握别乱控制"(确定性教训),不引导玩家赌反噬方向;(c) 重复性措辞("反噬未平息" vs "当轮反扑")等 A2 OQ1 定案后跟随,本次不预写。 +4. **连锁展开节拍** — **已采纳:坍塌显形**(**未采纳**本稿推荐"空腔连通"——用户取冲击感路线)。连锁 = 挖穿薄壳、整片松动地面塌陷露出;登记为 B5 连锁动画 / D4 音效方向(尘土、震动、塌露)。随行自洽注记:连锁不越旗(A2 GD-1)读作"你控制过的地面不会塌",塌到控制点截停;与视觉锚原则 5 已开格候选"碎裂/裸露地面"天然对齐;本稿 1.3-1 对坍塌的保留意见(地砖身份/反复坍塌合理性)转记为 art-bible/B5 表现侧注意项,不影响拍板。 +5. **道具呈现密度** — **已采纳(前提修正)**:用户指出"每格必有货 = 每格宝箱"是误会——每个安全格都有奖励,但奖励不一定是可使用道具。执行口径:**宝箱呈现仅绑定可使用道具类奖励**(可选类:刀/治疗包等,保留高档位视觉强调),即时类(buff/瞬时收益等非道具奖励)不摆箱;本稿"搜刮/遗落物"框架降级为可选氛围背景,登记不锁(背景句采用"搜刮前人遗落物"氛围措辞,不构成呈现规则承诺)。影响面登记:D1 已开格奖励恒读形态、B4 图标方向(各归各的时机)。 +6. **世界观厚度预算** — **已采纳:碎片点缀**(本稿推荐,不含前人日志)。功能性文本(道具名/结算行/教学措辞)lore 化 + 极少量一次性提示(首遇时刻的功能性 flavor 行);**前人日志(④-1 延伸)不做**,登记为未来内容方向;art-bible/D5/D6 成本边界 = 不新增文本系统。 diff --git a/design/research/peglin-comparison-2026-09-12.md b/design/research/peglin-comparison-2026-09-12.md new file mode 100644 index 00000000..844ee2be --- /dev/null +++ b/design/research/peglin-comparison-2026-09-12.md @@ -0,0 +1,81 @@ +# 对比研究报告:Dungeon Sweeper × Peglin(初见游戏体验) + +> 日期:2026-09-12 · 触发:用户试玩反馈"关卡完全没有 Roguelike 的体现" +> 数据来源:[Peglin Wiki wiki.gg](https://peglin.wiki.gg)(Forest 区域敌人页逐页核实:[Batty](https://peglin.wiki.gg/wiki/Batty)、[Green Slimedrop](https://peglin.wiki.gg/wiki/Green_Slimedrop)、[Slime Boss](https://peglin.wiki.gg/wiki/Slime_Boss)、[Encounters](https://peglin.wiki.gg/wiki/Encounters)、[Peglin 职业页](https://peglin.wiki.gg/wiki/Peglin)、[Cruciball](https://peglin.wiki.gg/wiki/Cruciball));本方数值 = 切片 v3 实测(level_monte_carlo N=15)。 +> ⚠️ Peglin 版本迭代频繁(区域命名已从初版变更),数值以当前 wiki 为准;未精确核实项已标注。 + +## 一、结论 + +**"没有 Roguelike 感"的根因不是缺随机,而是三个结构差异,按影响排序:** + +1. **构筑密度差一个数量级**:Peglin **每场战斗后**都获得遗物选择(普通 1 选 1 / 精英 2 选 1 / Boss 3 选 1)——一关 8–10 场 = 玩家在单关内做出 8–10 次"变强选择",每场都在改变下一场的打法定义了"这是一局新的"。我们每关仅 ~6 件可选道具(且多为伤害数值件、无协同效应)——**决策密度低是"没有局感"的第一根因**,比随机性更重要。 + +2. **遭遇零随机 + 线性无分支**:Peglin 每个战斗节点"spawn a random group"(敌群从区域池随机生成)+ 节点地图分支 + 事件节点(神秘?/商店/篝火);我们同坐标恒同遭遇、线性无选择——每次进入第一关的体验逐格相同。这是你判断"关卡内节点选择要随机"的印证,**方向正确**。 + +3. **资源模型不同导致压力形态不同**:Peglin 有**战后回复**(每场胜利回血,Boss 后大回复)→ 敌人低伤(1–5)+ 高频战斗 → 压力 = 跨场累积磨损;我们无战斗间回复(仅恢复点节点)→ 敌人高伤(7–9)+ 少战斗 → 压力 = 单场爆发伤害。两种模型都成立,**但我们的敌伤害端此前不足**(试玩批次 1:满血全通关),v3 已上调。 + +**是否现在引入随机性**:分两档—— +- **现在就做(本切片内,成本低)**:**遭遇随机化**——同 (L,D) 敌人数在区间内掷骰(如地精 1–2 只、熊地精 1 只或 +1 只补充)。同敌种不触碰 B2"每行集恰一敌种"锁定,只需把 unit_count 从定值升级为区间(C3 GDD 增补一条 Core Rule + B2.spawn 掷骰)。这一条就能让"每次进第一关都不一样"。 +- **后置(结构改动,登记不实现)**:C1 分支节点地图(你已拍板后置)· **多波战斗**(Peglin 每场 2–3 波、总敌量 120–200——这是 B1 编排级改动,波间状态保留规则也要定义,建议 Alpha 期评估)· 遗物类被动道具(B4 构筑扩展——中期最大的"局感"提升项)。 + +## 二、数值对比表(双方均为初见第一区域) + +### 2.1 玩家侧 + +| 维度 | Peglin(Forest) | 我们(L1,v3) | 差异注记 | +|---|---|---|---| +| 基础 HP | **100** | 50 | Peglin 的 2 倍 | +| 战斗间回复 | **每场胜利后回复**(机制存在,数值未精确核实;Boss 后大回复) | 无(仅恢复点节点 +30%) | **结构性差异——不可直接换算数值** | +| 每场可承受失误 | 场伤 10–25 ≈ 10–25% HP | 场伤 20–45 ≈ 40–90% HP | 我们单场权重高 4 倍 | +| 构筑资源 | **每场 1 次遗物选择**(普通 1 选 1/精英 2 选 1/Boss 3 选 1) | 每关 ~6 件可选道具 + 1 奖励点 | 决策密度 ≈ 1:6(每场 vs 每关) | +| 输出成长 | 球等级/新球(卡组构筑) | 伤害道具(恒定表值)+ buff | Peglin 输出成长是组合性的 | + +### 2.2 敌人侧(第一区域) + +| 维度 | Peglin(Forest) | 我们(L1 v3) | 差异注记 | +|---|---|---|---| +| 最弱杂鱼 | Green Slimedrop **60 HP / 1 伤** | 地精 24 HP / 7 伤 | Peglin 敌 HP ≈ 我们 2.5 倍、伤害 ≈ 我们 1/5 | +| 常规敌 | Batty **65 HP / 2 伤**(吸血) | 熊地精 40 HP / 9 伤 | 同上 | +| 每场敌人总量 | **2–3 波 × 1–3 只**(总 HP ≈ 120–200,wiki 未精确登记波次表——用户观察+社区共识) | 单波 1–2 只(总 HP 24–52) | Peglin 单场总 HP ≈ 我们 3–4 倍 | +| 战斗时长感 | 多回合磨血(每球 10–20 伤 × 多波) | 4–7 轮快攻(先手 6 × 轮数) | 时长相似、节奏完全不同 | +| 精英 | Invisible Miniboss **500 HP** / 5–7 伤 | 无独立精英(Dmax=Boss) | 我们结构位已预留 | +| Boss | Slime Boss **900 HP** / 8 melee+3 投射 | 食人魔酋长 150 HP / 9 伤(重击 18) | Peglin Boss HP ≈ 我们 6 倍(有战后回复+遗物成长托底) | +| 敌人上场规则 | 新波进场有移动距离(远程怪从远处开始)→ **首回合不挨打** | 敌在场上立即参与结算(无距离概念) | 你观察到的"一开始不会攻击到角色"——Peglin 用移动距离实现,我们用 charge 拍(熊地精/酋长环内蓄力拍=玩家安全拍)部分同构 | +| 敌群随机性 | **每场从区域池随机 spawn** | 同 (L,D) 恒定 | 结构性差异(见结论 2) | + +### 2.3 首关通关剩余状态期望 + +| 维度 | Peglin(Forest 通关) | 我们(L1 通关,模拟 N=15) | +|---|---|---| +| 剩余 HP 期望 | 高(每场战后回复持续回填;净磨损 = 累积失误) | **HP 24/50 ≈ 48%(基线通关者)** | +| 构筑增量 | 遗物 8–10 个 + 球 3–5 张 | 道具 ~5 件 + buff(当次) | +| 死亡主因 | 连续失误累积磨损 | 单场爆发(Boss 重击 18 + 伏击叠加) | + +## 三、可以参考 / 不能直接参考 + +### 可以参考(按收益排序) + +1. **每场战斗的构筑决策密度**(最大差距):哪怕不做完整遗物系统,让普通战斗胜利也产出一次"二选一"小抉择(如两件道具选一件、或道具 vs 小回复),把"每关 6 次决策"变成"每场 1 次决策"——**直接制造局感**。→ B4 扩展提案,登记。 +2. **遭遇随机化**(本切片可做):同敌种数量区间掷骰(见结论)。 +3. **敌群入场规则的同构物**:Peglin 的"远程怪从远处走来"= 首回合威胁延迟。我们已有 charge 拍同构(熊地精/酋长),可以把"新敌人首结算强制 charge/延迟拍"作为**多波制的前置探索**——若未来做多波,波进场延迟是天然的难度阀门。 +4. **战后回复的概念**(谨慎参考):Peglin 用它支撑"低伤多场"模型;我们的"高伤少场"模型若引入战后回复,必须同步下调敌 atk 否则死亡压力崩塌——**绑定修订,不可单独引入**。这是"关卡内 HP 作为资源"的另一种诠释(我们当前 = HP 是跨场存量的硬资源)。 +5. **Boss 血量量级思路**:Peglin Boss 900 = 普通怪的 15 倍、靠遗物成长跨过;我们 Boss 150 = 普通怪 4–6 倍。若未来引入每场构筑,Boss 倍率可以再抬(当前 150 在无成长密度下已到上限)。 + +### 不能直接参考(属性模型不同) + +1. **全部绝对数值(HP/伤害)**:Peglin 100 HP/60 敌/900 Boss 是"战后回复 + 多波 + 遗物成长"模型下的自洽值;我们是"无回复 + 单波 + 有限道具"——直接抄 = 死亡压力完全错位。**只可比结构比例,不可比绝对值**。 +2. **多波战斗的直接移植**:波次制依赖 Peglin 的"弹珠伤害溢出浪费"与波间调度,我们的结算制(每轮全敌行动)下多波需要重新定义波间资源保留——不是加个循环就完事。 +3. **遗物系统的被动协同**:Peglin 遗物互相组合成 build(弹珠路径/元素/生成器协同);我们的道具是数值件。直接加"每场掉遗物"而没有协同设计 = 决策密度上去了但决策质量是空的——**先设计协同,再提密度**。 +4. **Cruciball 类难度分层**:Peglin 用全局修饰(玩家 -15% HP 等)做难度档——我们已有 (L,D) 双轴 + 未来精英修饰,结构不同源。 + +## 四、行动建议(落回本切片与 GDD) + +| 项 | 时机 | 义务登记 | +|---|---|---| +| 遭遇随机化(unit_count 区间掷骰) | **本切片可做** | C3 GDD 增补 Core Rule(遭遇骰)+ B2.spawn 掷骰接口漂移登记 | +| 普通战"二选一"产出 | 下一批设计(B4 扩展提案) | 登记于 C3 GDD 联动网络(供给↔决策密度) | +| 多波战斗 | Alpha 评估 | B1 波次表提案登记(本报告 2.2 表) | +| 战后回复 | 与敌 atk 下调绑定评估 | 本报告 2.1 结构差异注记 | +| C1 分支地图 | 已拍板后置 | 维持 | + +> 报告使用纪律:Peglin 数值为当前 wiki 快照(版本迭代频繁),引用时注明日期;本报告结论服务于 C3/C1/C2/B4 的设计决策,非数值移植依据。 diff --git a/design/research/proposal-reward-polarity-2026-09-13.md b/design/research/proposal-reward-polarity-2026-09-13.md new file mode 100644 index 00000000..e7ed5bff --- /dev/null +++ b/design/research/proposal-reward-polarity-2026-09-13.md @@ -0,0 +1,87 @@ +# 提案:正反奖励(格面身份通感)——奖励池重构与极性路由 + +> **状态**:草案 v3(2026-09-13 定稿——/design-review〔lean〕5 blocking 同日修订 + OQ-1~6 全部用户拍板闭合;日志 `design/gdd/reviews/proposal-reward-polarity-2026-09-13-review-log.md`)——**已拍板待实施** +> **基线声明(v2 增)**:本文"既有结构"以 **2026-09-13 切片数据批**为准(status_effects.tres 六行〔含 enrage/off_guard〕、defense_up −30、标错惩罚池已上线、可选类供给控制器在位);B3/B1 GDD 正文与 registry 对该批存在未同步债(B3 四行 vs 数据六行、B1"标错惩罚未上线" vs 已上线、registry status_effect 四行)——清偿义务见 §3.5,引用两文档处以其数据实态为准。 +> **触发**:playtest-2026-09-12 报告供给发现(即时类构成单一——可选类退出格子池后概率质量流向即时类、buff 频率 44%→59%,session-state 条目 21;报告 Top3 之供给量重拍板)+ 用户提案(安全格正、雷格反、类型通用、图标预读)。原记"报告 Q2/Q3"系提案会话口头编号、归档报告无此编号——v2 改引实质出处。 +> **目标一句话**:把"挖哪格"从无差别推进升级为读敌拍板的路线规划——格面类型图标预读 + 对错定符号的极性兑付,在护甲克制之上叠加第二层战斗互动策略。 + +## 0. 与既有结构的关系(为什么地基已在) + +| 既有结构 | 本提案对它的处置 | +|---|---| +| B4 Rule 2 身份 install 期预分配(S∖R₀ 格) | 扩域:雷格同批 roll 类型(pool_tag 新值 `mine_cell`) | +| B4 Rule 8 已开格恒读 / OQ2 未开格不呈现(结构已备、呈现待垂直切片) | **激活 OQ2**:未开格呈现类型图标(查询面 `query_identity` 本就对未开格可查——AC-2 分支 3) | +| 标中旗=削弱(正)/ 标错旗=暴走·失衡·伏击(负)/ 踩雷=伏击(负)——B1 规则 6 + settlement.tres 标错惩罚池 | **推广为统一极性**:惩罚不再按行为固定,而是"该格类型的负向版" | +| B3 效果行(数据六行:增伤/减伤/伏击/削弱/暴走/失衡——2026-09-13 批) | 机制零改动——正负向复用既有行 + 一条直伤负向(heal)+ 一条新负向数据行 sapped 乏力(weaken,OQ-3 定型——B3 加行不加机制) | +| 绘制优先级栈(数字 > 小人 > 旗 > **[徽章]** > 纹理 > 底色) | 徽章位已预留——类型图标落此栈位 | + +## 1. 核心规则草案 + +- **R1 类型通用**:每格(安全格+雷格)install 期恰一类型身份 ∈ {attack, defense, heal, blast, weaken}(weaken 为新增正向类型——见 §2)。类型决定**兑付什么**,极性决定**兑给谁/什么符号**。**类型 = 行的家族属性(v2 钉死实现面)**:roll 保持 B4 F1 单段 weighted_pick 结构不动(不引入两段式),RewardTable 新增 `cell_type` 列——家族映射:attack_up / knife_t1 / knife_adv → attack · defense_up → defense · heal_minor / heal_pack → heal · spark_aoe → blast · weaken → weaken;供给控制器注入格同映射取所注入行的家族;雷格池负向行各携类型(见 §2)。§2 权重为**行权重**、类型频次由行权重涌现(C3 调参对象)。图标呈现家族类型;可选类格(刀/治疗包)图标 = 家族类型 + 既有道具徽章语言区分(呈现细则归 D1 义务)。 +- **R2 极性矩阵(行为 × 格型 → 符号)**: + +| 行为 | 安全格 | 雷格 | +|---|---|---| +| 挖开 | **+类型** | **−类型**(与既有伏击的关系见 OQ-1) | +| 插旗且留到轮末判定 | **−类型**(标错) | **+类型**(标中) | +| 插旗后本轮取消 | 零兑付(AP 已耗 = 变相惩罚,A3 承载) | 零兑付(AP 已耗) | + +- **R3 身份预读**:全部未开格呈现**类型图标**(不呈现极性、不呈现雷/安——推雷仍是扫雷本征推理,图标只多给"值不值得绕路"的规划素材)。**类型分布即先验信息(v2 显式拍板)**:雷池/安全池类型权重独立可调 ⇒ 类型图标概率性泄漏雷/安属性——此为**有意的规划素材**(与"值不值得绕路"同向),非缺陷;偏差幅度入 §4 指标监测,P1 交互随 playtest 复核。 +- **R4 负向兑付即即时**:负向全部当场兑付(授予段),不入库、不出窗口——惩罚不设库存语义。 +- **R5 供给联动不变**:可选类(刀/治疗包)不入极性域(不进雷格池、不产负向版);首件保证/F5 保底语义不动。 +- **R6 满层语义(v2 钉死)**:负向不走 B3 满层拒绝路径——授予恒受理;饱和由 B1 既有池 clamp [−100,+100] 承载(enrage/off_guard 沿用表行 max_stack=16,enrage 4 层即 +100% 饱和——原稿"2 层封顶同 max_stack"系对数据误读,v2 改正);饱和后的授予照产诚实行(贡献值边际归零 + pool_value 顶格显示——与正向饱和行同款呈现语义)。**结构不可达论证**:单轮负向授予数 ≤ AP(≤8)< max_stack 16,满层拒绝路径本身不可达,B3 侧零改动成立。**B2 cap 显式例外**:blast 负向(ambush +2 层)受 B2 cap=4 反灾难护栏约束——cap 溢出照既有"溢出未生效"行协议(护栏优先于落地语义;与现行 mine_hit→ambush 转化同池同协议)。 + +## 2. 极性数值草案(provisional 全归 C3) + +| 类型 | 正向(复用行) | 负向(复用行) | +|---|---|---| +| attack 增伤 | attack_up +50%(×1 层拍板待 C3) | enrage 敌方暴走 +25%(max_stack=16 沿用数据、池 clamp 4 层饱和——R6) | +| defense 减伤 | defense_up −30%(2026-09-13 调参批新值) | off_guard 我方失衡 +25% | +| heal 回血 | heal_minor +4(满血零兑付照旧) | **直伤**:主角 −4(B1 直接 apply_damage,不经 B3——新负向即时形态) | +| blast 群伤 | spark_aoe 全体敌 −4 | ambush 敌方伏击 +2 层 | +| weaken 削弱(新增正向) | weaken 敌方输出 −65%(标中旗既有语义入池) | **sapped 乏力(OQ-3 已裁决 2026-09-13——反噬镜像)**:我方输出 −25% 进 hero_offense 池(attack_up 镜像;B3 新数据行:hero/percent −25/stack/max 16/dur 1;值 provisional 归 C3) | + +正向权重草案(**行权重**——类型频次由此涌现,R1):attack_up 20 / defense_up 16 / spark_aoe 16 / heal_minor 15 / weaken 12(石肤〔=defense_up〕让位后 weaken 递补第四频次——"怪要放大抢减伤、怪虚弱抢增伤、怪重叠抢削弱"三策成型;刀/治疗包走供给控制器注入、不入本权重面)。雷格池 = 同类型表(负向半),权重可独立调。 + +## 3. 逐系统 delta 清单(实施拆批依据) + +- **A2**:mine 通道 reward_eligible 修订(身份扩展到雷格——registry `a2_event_surface.cell_revealed` 的 reward_eligible 语义"mine 恒 false"须随批改写);拔旗动作**已在位**(`try_flag_pull`,`flag_pull_ap_cost=1` 已登记 registry/A3——原 OQ-5 系重开既定事实,v2 撤销改引用);EC 相应扩写;**A2 OQ1(标错惩罚)由本提案取代关闭**——候选"敌方伏击+1"被类型化负向路由取代,切片已实装惩罚池(enrage/off_guard/ambush+2),重复性口径("标旗未中"行每格实例一次)随批继承到负向兑付语义,A2 GDD 修订批关闭该 OQ。 +- **B4**:`pool_tag` 新值 `mine_cell`;`cell_type` 列(R1 家族映射);roll_board 批次扩雷格 roll(同一 scope 批内);RewardTable 新增 `polarity` 字段(+/-,**类型枚举五值不动**——负向经 polarity + effect_id 表达,结构修订最小面);负向行内容回填;schema 校验族扩展。 +- **B1**:`_judge_flag` 扩展(标错安全格 → 该格类型负向兑付;标中雷 → 该格类型正向兑付——**OQ-2 已裁决 2026-09-13**:取代现行固定 weaken 赠予,weaken 改走格面身份;A3 条款②锚对象改"标中雷类型正向当量下界",重议随批登记);`_convert_ambush` 改类型化负向(OQ-1 已裁决:统一路由、伏击为负向表一行);明细行极性标注。**授予段致死裁定(v2 增)**:负向直伤于授予段使 hp=0 → 段序短路直入判定段判负(战斗段短路同族推广——死主角不消费窗口/增益/道具/战斗段,防死尸挨打行归因噪音)。**架构对齐注记(v2 增)**:本提案全部兑付(正向/负向/直伤)均发生于结算段——扫雷阶段零数值计算、事实入队轮末统一转化(B1 轮 facts 批处理既有结构);R2/R4 的"当场"均指授予段当场,非挖掘期。 +- **B3**:机制零改动(R6 满层拒绝结构不可达——见 R6 论证);**数据行 +1**:sapped 乏力(OQ-3 定型,schema 七字段照填);直伤负向不经 B3(B1 → B2 apply_damage)。 +- **A3**:拔旗价目已在位(`flag_pull_ap_cost=1`——引用,不新登记)。**F1 契约重议义务(v2 增,阻塞级)**:本提案触发 A3 F1 三条款重推——**条款②**(标中旗锚:OQ-2 采纳后"标中旗 debuff 档位"锚对象消解,替代形态〔类型正向当量下界〕随 OQ-2 拍板一并重议——条款①"分源改率须回 A3 重议"被触发);**条款③**(V_mine 类型化:B1 F4 不等式右臂按类型分化,取最坏型或加权型,重推);**条款⑤**(挖穿计价惩罚项类型化:B1 F5 均匀交错模型的踩雷代价按类型加权,重推)。owner:B1(单一闭合)+ A3(契约方)。 +- **D1**:未开格类型图标(徽章栈位);极性视觉语言(正负色相+形状双通道——art-bible 约束对齐);结算行负向文案族。义务条目随批正式登记 systems-index D1 台账("清单外登记会丢失"先例)。 +- **C3**:正负两套数值全调平;F4 期望带复算(含 Q1 调参批〔= 2026-09-13 减伤墙调参批〕后的新基线);失衡预警扩展(负向兑付率、类型选择分布);探旗通道观测(OQ-6 已裁决:反滥用机制不引入——指标 5 观测兜底,越线再调)。 +- **B5/D5**:负向明细行文案 + "读拍抢格"教学一段。 + +### 3.5 跨文档回写清单(v2 增——评审阻塞项,"清单外登记会丢失"先例) + +- **registry**:`reward_table`(pool_tag 增 `mine_cell` + 新列 cell_type/polarity + 跨字段校验族扩展);`status_effect`(**七行同步——enrage/off_guard 系 2026-09-13 批未登记存量债 + sapped 新行(OQ-3)**,`settlement_table.effect_pool_map`"新 effect_id 上线须随批登记"已被违反的存量债一并清偿);`settlement_table`(effect_pool_map 增 sapped→hero_offense;source_label_map 若增负向文案键随批);`a2_event_surface`(cell_revealed.reward_eligible "mine 恒 false" 改写)。 +- **D2**:RewardTable schema 扩列(cell_type/polarity);StatusEffectTable 七行随 B3 修订批。 +- **B3 GDD 修订批**:四行→七行入文(六行存量债 + sapped)、defense_up −50→−30 同步(2026-09-13 批遗留债)。 +- **B4 GDD 修订批**:pool_tag/cell_type/polarity schema、roll_board 批次扩雷格、负向行内容、AC 族扩展(AC-1/2/12/16/17/20 受 σ 轨迹与枚举域变化影响——黄金向量/登记常数重制义务随批)。 +- **game-concept**:锁定规则注记兑现("开雷→敌方优势非直接伤害"的搁置取舍由本提案 heal 负向直伤作机制侧裁决落地);三向效果映射表述改写("标中雷 = 怪物获得削弱"固定语义 → 类型化);核心循环"雷 = 敌方优势"补极性注记。 +- **game-pillars**:P3 表述注记("标中雷削弱敌人" → "标中雷兑付该雷类型正向"——同源原则强化,支柱语义不变)。 +- **systems-index**:本提案 D1 delta 拆正式义务条目入 D1 台账(未开格类型图标〔激活 B4 OQ2〕/极性视觉语言/负向文案族)+ 门禁增补记录块。 + +## 4. playtest 预注册指标(防"感觉对了但测不出") + +1. **路线选择率**:敌重击前一拍内,安全格挖掘中减伤类的占比(有拍预读 → 预期显著高于基线;自动玩家策略变体可先行仿真)。 +2. **极性归因清晰度**:踩雷负向行后玩家"知道为什么亏"的自述率(对比现行伏击语义)。 +3. **图标误读率**:ux_reject / 结算期"预期正向实际负向"事件率。 +4. **Q1 后承伤带基线**:level_monte_carlo 对 C3 F4(≤20%/普通场、≤50%/Boss 场)复验——本提案实施前先落 Q1 调参批基线(Q1 = 2026-09-13 减伤墙调参批;其前置的零承伤根因探针〔playtest Top1〕未闭合前禁入批 3)。 +5. **类型选择性探测率(OQ-6 观测兜底)**:插旗目标中"负向温和类型(heal)未开格"占比 vs 类型分布基线——显著偏高即探旗策略成形(已裁决不引入反滥用机制,越线再调)。 + +## 5. OQ 裁决记录(2026-09-13 用户全部拍板闭合) + +- **OQ-1(已裁决)**:~~踩雷 −类型 = 取代还是叠加既有伏击?~~——**继承泛化关系,非二选一**:旧"踩雷→伏击"是新路由(踩雷 → 兑付该雷类型负向行)的退化特例(旧系统 = 所有雷同一类型);伏击保留为负向表中的一行(如 blast 负向 = ambush +2 层),其余类型扩展各自负向(暴走/失衡/直伤/乏力)。路由统一、无叠加裁决。 +- **OQ-2(已裁决)**:标中雷正向 = **取代**现行固定 weaken 赠予——weaken 移入类型池后,标中雷给"该雷的类型",削弱成为可规划素材而非固定施予;A3 条款②锚对象改"标中雷类型正向当量下界",重议随批(见 §3 A3)。 +- **OQ-3(已裁决)**:weaken 负向 = **sapped 乏力(反噬镜像)**——我方输出 −25% 进 hero_offense 池(attack_up 镜像;B3 新数据行、机制零改动;值 provisional 归 C3)。原"敌方 weaken 抵免"候选否决(需新免疫语义、破 B3 Rule 8 负空间);原"暂缓复用 enrage"候选否决(用户定型)。 +- **OQ-5(v2 撤销——既定事实引用)**:~~拔旗动作 AP 定价~~——`try_flag_pull` / `flag_pull_ap_cost=1`(无返还物)已登记 registry action_economy 与 A3 schema,非开放问题。 +- **OQ-6(已裁决)**:类型选择性探旗(旗未知格 → 结算 hit/miss 反馈 → 拔旗)**不引入反滥用机制**——接受为既有机制面的策略深度,指标 5 观测兜底、越线再调。 + +## 6. 实施批次 + +1. **批 1(数据面 + 路由)**:A2/B4 schema 与行、B1 极性路由、冒烟扩展——机制先可玩(占位图标)。 +2. **批 2(呈现)**:D1 图标族 + 极性视觉 + 教学。 +3. **批 3(数值与验证)**:C3 调平 + level_monte_carlo/defense_scan 复验 + 试玩批次。 diff --git a/design/ux/battle.md b/design/ux/battle.md new file mode 100644 index 00000000..27dab775 --- /dev/null +++ b/design/ux/battle.md @@ -0,0 +1,427 @@ +# UX Spec: 战斗屏(Battle Screen) + +> **Status**: Complete — /ux-review 修订落稿(2026-09-12,blocking 7 项已修,待复检) +> **Author**: user + ux-designer +> **Last Updated**: 2026-09-12 +> **Platform Target**: PC (Steam)——Keyboard/Mouse 主(全部操作纯鼠标可完成)、Gamepad Partial 非 MVP、Touch None(同 technical-preferences) +> **上游修订依赖**: 弃图刷新挖掘期受理已随 D4 落稿上游(2026-09-12:A3 Active 受理 + 每轮上限 2 次、A2 Active·仅刷新路径装板、B1 `refresh_board` 编排 + 恒 same_board、registry `rap_rules` 同步——见 OQ12) +> **Template**: UX Spec +> **覆盖子屏**: 挖掘阶段主界面 · 轮末自动战斗结算(B1 明细屏) · 道具使用窗口(B4/B1 域屏) · 胜利/失败终局 · 结算明细回看 +> **输入义务**: A2×6 + A3 + A4×3 + B1×3 + B2×4 + B3×4 + B4×5(systems-index 门禁增补记录,缺项即 blocking——承接落点见 §13 义务对照表) + +--- + +## Purpose & Player Need + +战斗屏是 Dungeon Sweeper 的核心玩法屏——游戏的全部 agency(排雷规划 + 轮末资源决策)都发生在这里。玩家在此屏完成完整轮次循环:**用本轮 AP 在扫雷图上挖掘/标旗 → 主动停手或 AP 耗尽 → 观看轮末自动战斗结算逐行兑现 → 下一轮**。 + +一句话目的:**让"我算对了"的推理过程与它的战果兑现同屏可见、互为因果。** + +- 扫雷图区承载 P1(推理掌控感):数字是主角,一切服务信息可读 +- 战斗画面区承载 P2(规划的延迟兑现):扫雷之因逐行兑现为战果,全程可见可归因 +- 角色信息/道具区承载规划面:状态、资源、库存常读,支撑"往雾区推进/囤刀待机"类决策(B4 可证伪信号的 MVP 观测面) + +若此屏缺失或难用:推理与兑现分裂(自动战斗沦为无聊过场——已登记创意风险)、逐行归因不可见(P2 失败)、AP/RAP/库存不可读(规划失去原料)。 + +--- + +## Player Context on Arrival + +- **首次到达**:从关卡首节点(战×2 的第一战)进入。MVP 无引导流,A4 雾首遇提示、chord 肌肉记忆提示等非模态提示承担首次教学(见 §States) +- **到达前刚在做**:关卡节点链上点击战斗节点(LevelRunner 推进);或上一场战斗胜利后点下一节点 +- **情绪状态**:棋手式清明专注(art-bible §2.1,能量 2/10)——挖掘期画面必须无杂音;结算期转为清算爽感(7/10) +- **到达方式**:游戏推进送入(非自愿导航),进入即处于挖掘阶段(棋盘已 install、初始 3×3 已展开、小人出生) + +> ⚠️ Player journey map 未建(`design/player-journey.md` 不存在)。模板位于 ux-design 技能;建议在本 spec 进入实现前补建,以校准各阶段情绪曲线。 + +--- + +## Navigation Position + +``` +存档 → 冒险 → 关卡(节点链,C1-lite) → 【战斗屏】 + ├─ 胜利 → 节点奖励/恢复覆盖层 → 下一节点(下一场战斗屏) + ├─ 胜利(末节点 Boss)→ 关卡结算(level_clear,库存清空反馈)→ 下关 + └─ 失败 → 冒险终结 → 返回存档/营地 +``` + +战斗屏是**上下文相关屏**(仅由战斗节点进入),非顶层常驻。关卡内节点进度常驻屏顶(A 区),玩家始终知道自己身处链中第几环。 + +- 上游屏:关卡节点链(节点地图形态 = 线性,C1 已裁剪为线性第一关) +- 下游屏:节点奖励/恢复覆盖层(NodeOverlay,已有 demo 组件——建议后续单独出 spec `design/ux/node-reward.md`) +- 平行屏:结算明细回看 overlay(本屏内域屏)、图例面板 overlay(本屏内域屏) + +--- + +## Entry & Exit Points + +**进入**: + +| Entry Source | Trigger | Player carries this context | +|---|---|---| +| 关卡战斗节点 | 点击节点(LevelRunner 推进 encounter) | 携带 HP(跨战斗延续)、库存(关卡域延续)、RAP=0(战斗域新开) | +| 上一场战斗胜利 → 下一节点 | 同上 | 同上 | + +**离开**: + +| Exit Destination | Trigger | Notes | +|---|---|---| +| 节点奖励/恢复覆盖层 | 敌方全灭(B1 判定段 verdict=win)且非末节点 | 不可逆:battle_over 已发生,RAP 清零 | +| 关卡结算 | Boss 战胜利 → level_clear | 不可逆:库存清空(须可见反馈,B4 D1×⑤) | +| 冒险终结 | 主角 HP=0(verdict=lose) | 不可逆:Roguelike 死亡重开(art-bible §2.5 温柔惋惜调性) | +| 无中途退出 | — | MVP 不设战斗内放弃/暂停菜单(回合制无时间压力;如需暂停留 Playtest 诊断按钮,调试用) | + +--- + +## Layout Specification + +### Information Hierarchy + +本屏必须传达的全部信息,按注视优先级分五层: + +1. **扫雷图本体**(第一注视点,挖掘期占视觉预算大头):四格态(已开 0 格地板/已开雾格无字/未开格/未开雾格——A2 D1×6② 一眼可辨)、数字、旗、小人、可挖域(8 向相邻常驻指示)、变异纹理(迷雾) +2. **本轮行动资源**:AP 余量(决定还能挖几格)、停手/弃图刷新按钮——紧贴棋盘上缘(动作发生地) +3. **战果兑现面**(结算期为第一注视点):敌人血条变化、逐行结算明细、高光(完美排雷/标中旗) +4. **规划面**(边缘注视,常读):HP、攻击、RAP 池、状态效果(己方/敌方)、库存列表、敌人意图徽标与顺位 +5. **进度语境**:轮次/阶段指示、关卡节点进度、雾图例入口 + +依据:挖掘期玩家视线锚定棋盘(art-bible §2.1"视线始终被数字牵引");结算期视线转向战斗画面区(§2.2"兑现光带逐行横扫")。规划信息必须常读但不得抢焦——这正是用户布局图三分下栏的合理性:**动作区居中,规划区两翼**。 + +### Layout Zones + +采用用户拍板的布局图(2026-09-12),按 1280×800 基座(ADR-0002)落尺寸: + +| 区 | 位置与尺寸 | 内容 | +|---|---|---| +| **A 关卡进度条** | 全宽 × 28px 顶栏 | 节点链步进(⚔战 ◆道具 ⚔战 ⚔战 ♥恢复 👑Boss),当前节点高亮 | +| **B 战斗画面** | 全宽 × 252px | 英雄卡(左)+ 兑现演出区(中)+ 敌人卡 ×1–3(右);底部结算明细带(24px 摘要 / 结算期 84px 实时流);顶部中央轮次·阶段指示 | +| **C 角色信息** | 左下 240px × 520px | HP 条、攻击值(含修正显示)、RAP 池、状态效果槽(固定 4 槽)、小人立绘占位 | +| **D 扫雷图** | 中下 ~760px × 520px | 顶部 AP 条(44px:左——AP 点阵 + 停手按钮;右——图例按钮 + 弃图刷新按钮〔雷图右上角,2026-09-12 拍板〕);棋盘主体(居中,格 36–45px 见方);toast 槽(上缘内侧) | +| **E 道具** | 右下 280px × 520px | 库存常读列表(固定槽高 + clip_contents——缺陷①教训);窗口期原位激活为道具使用窗口 | + +**布局决策 D1(本 spec 最大决策,用户拍板)**:**常驻双舞台分屏**——战斗画面与扫雷图同屏常驻,挖掘期敌人待机可见,结算期不切场景。 +- 备选(已否决):Peglin 式"结算时才切入独立战斗场景"——切换打断推理心流,且资产管线成本翻倍 +- **对 B5 双舞台陷阱的输入**:game-concept 登记的"战斗发生在棋盘上还是独立场景"悬念,由本布局裁决为**第三形态:同屏双舞台**。B5 前的"战斗表现美术测试"(一屏样张)应按此布局出样 +- 敌人不占格(B2 已锁)与本布局自洽:敌人实体只存在于 B 区,棋盘永远只有数字/旗/小人/变异 + +**棋盘格尺寸公式**:`cell = floor(min(D宽−padding, D高−AP条−padding) / N)`,N∈{10,12}(A1 D1–D4 行)。1280×800 下 12×12 → 36px,10×10 → 45px。数字字号 ≥ 格高的 50%,像素字体整数倍缩放(canvas_items stretch 保证清晰,ADR-0002)。 + +### Component Inventory + +| 区 | 组件 | 类型 | 内容 | 交互 | 现有 demo 组件 | +|---|---|---|---|---|---| +| A | LevelStripView | 步进条 | 节点链 + 当前位置 | 只读(点击节点不可跳) | ✅ level_strip_view.gd 迁移 | +| B | 英雄卡 | 卡片 | 小人像、HP 条、攻击值、状态效果槽 ×4 | 只读 | HUDView 拆分 | +| B | 敌人卡 ×1–3 | 卡片 | 敌像、血条、顺位徽标、意图徽标、行为环点、状态槽(含敌方 side 池口径:伏击层数为全体敌人共享单值、各卡同值呈现——数据源 = B1 流水经 B3 投影,B2×4④) | 只读 | 新建 | +| B | 轮次·阶段指示 | 标签 | "第 N 轮 · 挖掘中/结算中/道具窗口" | 只读 | 新建 | +| B | 结算明细带 | 滚动列表 | 结算期逐行流水(B1 13 类行协议);挖掘期收起为上轮摘要 1 行 | 点击展开明细回看 overlay | battle_log_view.gd 改造 | +| B | 结算明细回看 | 模态 overlay | 完整流水逐行(四类行呈现,B4 D1×③) | 关闭 | battle_log_view.gd 改造 | +| C | HP/攻击/RAP 显示 | 直角细线数据框 | 当前值/上限、修正来源角标 | 只读;RAP 清零有可见反馈 | HUDView 拆分 | +| C | 状态效果槽 ×4 | 图标槽 | effect_id→icon(形状+色相双通道)、层数角标、剩余结算次数点 | 悬停 tooltip(来源+语义) | 新建(B3 D1×①) | +| D | AP 条 | 点阵 + 按钮组 | 本轮 AP 点阵、停手按钮(左)、弃图刷新按钮(右端·雷图右上角;RAP−8 · 本轮剩 N 次——上限 2 次已随 A3 2026-09-12 修订生效) | 点击提交意图 | HUDView 拆分 | +| D | BoardView | 网格 | 棋盘全部格态 + 小人 + 旗 + 雾纹带 + 奖励身份 icon(已开格;绘制优先级 数字>小人>旗>奖励徽章>变异纹带>底色——徽章不进数字区,样张验收项) | 左/右键意图提交 | ✅ board_view.gd 迁移 | +| D | 悬停预承诺光标 | 光标态 | 移动/挖/旗三语义光标 + 目标格边框高亮 | 自动 | 新建(A2 D1×6③) | +| D | 可挖域指示 | 常驻微高亮 | 小人 8 向相邻可操作格 | 自动 | 新建(A2 D1×6③) | +| D | 移动路径预览 | 路径高亮 | 悬停已开格显示 BFS 路径(合法性指示) | 自动 | 新建(A2 D1×6⑤,裁决见下) | +| D | toast 槽 | 非模态提示 | 拒绝原因(双源)、voided、满层溢出、雾首遇、chord 提示、零合法板告知 | 自动消退 2.5s | 新建 | +| D | 图例按钮 + 面板 | 按钮+模态 overlay | 格态/变异/雾语义图例("迷雾 = 无雷盲区"条目恒在) | 开/关 | 新建(A4 D1×②) | +| E | 库存列表 | 固定槽高列表 | 逐件:icon+名称+属性简述+状态;枚举序 = stock 受理序(E21) | 悬停 tooltip(完整描述+触发注记);窗口期点选 | item_window_overlay.gd 改造 | +| E | 道具使用窗口态 | 面板原位激活态 | "轮末道具窗口 · 至多选 1 件"+ 时机指示 + usable_now 禁选态 + 跳过按钮 + 触发政策注记 | 点选/跳过 | ✅ item_window_overlay.gd 改造(形态裁决见下) | +| 全屏 | 胜利/失败 overlay | 模态 | 金蜡封/暮蓝收旗 + 判定行 + 明细回看入口 | 继续/下一节点 | node_overlay.gd 扩展 | +| 调试 | TunePanel | 调试面板 | atk%/hp% 滑条 + 批量模拟(正式侧归 C4,切片调试入口保留) | 调试 | ✅ tune_panel.gd 保留 | + +**布局决策 D2 — 道具窗口形态 = 右栏原位激活**:窗口触发时 E 区面板点亮描边、标题切换为窗口态,棋盘侧去饱和 + 金橙色相偏移锁输入(明度不变——art-bible §2 明度恒定合规化,2026-09-12)。 +- 备选(已否决):中央模态弹窗——遮挡结算演出首屏(授予段刚完成的视觉记忆),且库存常读位置与窗口位置分离造成空间记忆断裂 +- 依据:窗口期棋盘输入本就不竞争(结算期棋盘不可交互,A2 单意图帧先例),无模态必要性;B1 窗口锚点 = 授予段后、增益/减益段前——玩家需同时看库存与刚到手的奖励 + +**布局决策 D3 — 移动路径预览 = 合法性指示**(A2 D1×6⑤ 悬案裁决):悬停已开格时高亮 BFS 路径(小人蓝细线点迹),移动执行为 ≤150ms 短滑。 +- 备选(登记不采纳):穿行动画轨道——移动免费高频,动画时间税违反 P4 点击摩擦护栏(每轮输入动作:AP ≤2 的观测假设);正式版若 playtest 反馈"移动太瞬"再议(留痕) + +**布局决策 D4 — 弃图刷新 = 挖掘期当场换图(2026-09-12 用户拍板,同日落稿上游)**:按钮置 AP 条右端(雷图右上角)。点击即当轮就地换新图继续挖——AP 不重置、剩余 AP 保留,RAP−8,本轮上限 2 次。 +- **上游修订(2026-09-12 已落稿,OQ12)**:① A3 `request_refresh` Active 相位受理 + 每轮上限 2 次(Waiting 代理退役)② A2 装板受理域增 Active·仅刷新路径 ③ 编排 owner = **B1 `refresh_board`**(A3 扣费 → A2 retire → A1 generate → A2 install → B4 roll_board 恰一次;无结算段、无 round_end——B1 编排唯一性原则保持)④ 旧板旗随旧板**作废**(零兑付、旗 AP 不返还——A2 EC11 全量重置承载;确认微交互须显式提示)。**经济预警**:当场刷新无结算发生,原「敌方时间税」自惩失效——A3 OQ3 弃图率/刷新率预警阈值联动复核。 +- 备选(已否决):结算期决策提示(Waiting 内联)——结算期不做刷新决策(用户拍板);挖掘期预承诺 stop+reroll 复合——决策即终轮,不符「换图继续挖」语义。 + +**敌人意图线索映射目录(B2×4① 成文占位,2026-09-12)**: + +| enemy_id | 视觉状态(意图线索) | 强度档位编码 | 环长编码 | +|---|---|---|---| +| grub(蛆虫) | 攻击 ×1 态 | 小体量墩形 = 轻击 | 环点 1 | +| ambusher(伏击者) | 攻击 / 蓄力 / 重击 ×3 态 | 垂直延伸体量 = 重击(heavy_multiplier) | 环点 = 拍数 | + +- 预告时序 = **下一拍预告**(当前拍视觉状态),挖掘期全程可见;结算期不另设意图播片(B2 Rule 8⑤) +- 映射键 = enemy_id,跨 run 恒定、禁止重掷(B2 Rule 8③);形状 + 色相双通道(art-bible 约束) +- 资产定稿归 B5;本表为 D1 侧成文占位(B2 Visual Req 2 口径:grub 1 态 / ambusher 3 态) + +### ASCII Wireframe + +``` +1280 × 800 ──────────────────────────────────────────────────────────────────── +┌─[A] 关卡进度条 28px ─────────────────────────────────────────────────────────┐ +│ ⚔──⚔──◆──⚔──⚔──♥──👑 (当前节点高亮,已过节点暗金) │ +├─[B] 战斗画面 252px ──────────────────────────────────────────────────────────┤ +│ ┌────────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ 英雄卡 │ 第 3 轮 · 挖掘中 │ ①蛆虫 │ │ ②蛆虫 │ │ +│ │ [小人像] │ │ HP▓▓▓░ │ │ HP▓▓░░ │ │ +│ │ HP▓▓▓▓░ │ [ 兑现演出区 ] │ 意图:爪击 │ │ 意图:蓄力 │ │ +│ │ 攻 6 (+50%) │ (结算期: 光带→弹道→受击) │ 环 ●○○○ │ │ 环 ●●○○ │ │ +│ │ 状态 ⬆2 ·🛡1 │ │ 状态 ⚡2 │ │ 状态 🛡1 │ │ +│ └────────────┘ └──────────┘ └──────────┘ │ +│ ├─ 结算明细带 ────────────────────────────────────────────────────────────┤ │ +│ │ ▸上轮: 主角攻击9→蛆虫-9 | 攻击+50%(1结算) | 伏击+3×2 … [展开明细▾] │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +├─[C] 角色信息 240px ───┬─[D] 扫雷图 ~760px ────────────┬─[E] 道具 280px ─────┤ +│ 520px │ 520px │ 520px │ +│ ┌──────────────────┐ │ ┌───────────────────────────┐ │ ┌─────────────────┐ │ +│ │ HP 42/50 ▓▓▓░ │ │ │ AP ●●●●●○○○ 5/8 │ │ │ 道包 · 关卡域 │ │ +│ │ 攻击 6 (+50%) │ │ │ [⏹停手] │ │ │ (窗口态标题位) │ │ +│ │ RAP ▓▓▓░ 3/8 │ │ │ [?图例] [↻刷新 8|剩2] │ │ ├─────────────────┤ │ +│ ├──────────────────┤ │ │ ┌─toast────────────┐ │ │ │ 🗡 小刀 │ │ +│ │ 状态效果 │ │ │ │AP不足·剩0点 │ │ │ │ 伤害 · 一次性 │ │ +│ │ ⬆攻击+50% ○1 │ │ │ └──────────────────┘ │ │ ├─────────────────┤ │ +│ │ 🛑减攻−65% ○1 │ │ │ 12 12 1 ░ 2 ▓▓ ▓▓ │ │ │ 🗡 精工短刀 │ │ +│ │ (槽×4 固定) │ │ │ 12 █ 1 1 2 ▓▓ ▓▓ │ │ │ 高伤 · 一次性 │ │ +│ ├──────────────────┤ │ │ 1 2 2 🚩 █ 2 1 │ │ ├─────────────────┤ │ +│ │ [小人立绘占位] │ │ │ ░ 1 ▓▓ 👫 1 1 ░ │ │ │ ❤ 治疗包 ×1 │ │ +│ │ │ │ │ ▓▓雾 1 1 ░ ░ 2 ▓▓ │ │ │ 回复 12 · 可用 │ │ +│ └──────────────────┘ │ │ ( █=可挖域微光 👫=小人) │ │ └─────────────────┘ │ +│ │ └───────────────────────────┘ │ │ +└───────────────────────┴───────────────────────────────┴─────────────────────┘ + +窗口态(E 区原位激活,轮末道具窗口): +│ ┌─────────────────┐ 面板描边点亮(金),D 区去饱和锁输入 +│ │ ⏳ 轮末道具窗口 │ 时机指示:位于授予段后、增益段前 +│ │ 至多选用 1 件 │ +│ ├─────────────────┤ 可用件高亮可点;满血治疗包灰置+禁选原因 +│ │ 🗡 小刀 [选用] │ 选中即承诺(照常消耗拍板) +│ │ 🗡 精工短刀[选用] │ +│ │ ❤ 治疗包(满血) │ 灰置 + tooltip「HP 已满 · 禁选」 +│ ├─────────────────┤ +│ │ [跳过] 注记*:未 │ *「未选用件随关卡结束清空」政策注记常驻 +│ │ 选用件关卡末清空 │ +│ └─────────────────┘ +``` + +--- + +## States & Variants + +| State / Variant | Trigger | What Changes | +|---|---|---| +| **挖掘阶段(默认态)** | 轮次开始 / 结算完毕回 Active 相位 | B 区敌人待机(意图徽标全程可见——B2 Rule 8⑤),D 区棋盘可交互,AP 条点亮;能量 2/10,唯悬停呼吸微光在动(§2.1) | +| **结算阶段** | round_end 触发(AP 尽/停手/清图) | D 区锁输入(棋盘去饱和 + 金橙色相偏移,明度不变,数字保持全对比);B 区播放七段编排演出;明细带展开 84px 逐行滚动;阶段指示→"结算中";能量 7/10 | +| **道具窗口态** | 结算授予段完成后(窗口锚点) | E 区原位激活(决策 D2);空库存不弹窗直接跳过(B4 D1×②);usable_now=false 禁选+原因 | +| **清图瞬间** | 全部安全格开完 | 金粒喷泉 + 彩纸 3–5s 即收(§2.3);清图奖励行强调呈现(H2,B4 D1×④——金框高亮行);随后换新图 install | +| **完美排雷高光** | B1 高光判定(全图标中/零误标类) | 全屏喝彩级反馈(§2.2 最高档):金光带加倍扫射 + 「完美排雷 ×2」横幅——P2 爆发兑现节点(创意风险 #2 落点) | +| **标中旗高光** | flag_judged{hit=true} | 该旗化暴击弹道加重 + 明细行金框——chord 缺席的即时兑现补偿(创意风险 #3 落点) | +| **敌人死亡呈现** | 敌 hp=0(流水行击杀标记) | 该敌人卡血条归零 → 卡体灰化 + 碎裂退场(**禁瞬消**——B2 资产下限「死亡瞬间须可辨识、不得直接消失」,同等 blocking);战斗段短路的重击被掐灭不产行动明细行,由行序可推断(B1);动画密度归 B5,呈现语义在本 spec | +| **胜利终局** | verdict=win | 金蜡封落于**扫雷图区中央**(§2.4 字面锚定),全屏暖白金 overlay;判定行 + 「下一节点」;明细回看入口;状态槽全清可见反馈(B3×4④——逐槽化光收回 + 微文案「状态随战斗结束消散」,防「我的 buff 哪去了」误读) | +| **失败终局** | verdict=lose(hero hp=0,战斗段短路) | 明度不变的暮蓝 #5A6C8C 氛围 overlay(全作唯一暮蓝档);小人坐砖掸灰、插过的旗逐面化光点收回(§2.5);判定行 + 明细回看(学习用途)+「返回」;状态槽全清可见反馈(B3×4④,同胜利) | +| **Boss 遭遇变体** | encounter=boss(遗迹守卫) | 战斗开始一次性演出:紫赤 #8F2F6A 影子爬过棋盘(§2.6);敌人卡体量最大+锐角剪影;阶段指示附 Boss 名 | +| **雾图变体(L2+)** | 板含雾格 | 雾纹带双通道呈现;首次含雾板进入挖掘阶段触发一次性首遇提示(每 run 一次);图例恒可回看;**已开雾格保留底色层淡雾 tint**(与已开 0 格可判定区分——OQ8 裁决采纳 2026-09-12;tint 处视觉锚 3 最低层,不遮挡数字);**雾区全开一次性完成信号**(视觉合并域 = 信号单元,禁实例级双响;EC11:双雾区呈现合并、实例独立) | +| **空态:无状态效果** | B3 EC12 | 状态槽隐藏 or 空槽占位——**裁决:槽位隐藏,HP/攻击/RAP 上移补位**(角色信息区纵向空间紧张,空槽占位浪费);图例面板载空态说明(B3 D1×① 成文项) | +| **空态:零库存** | 关卡初/清空后 | E 区显示「道包空空——安全格里藏着补给」(指引性空态,非报错) | +| **调试变体** | 测试模式 | TunePanel 入口(demo 保留,正式侧 C4 退役);Playtest 诊断按钮(卡死转储) | + +**拒绝/作废子态**(A2 D1×6⑥、A3 双源——每次拒绝玩家可感知): +- 棋盘面拒绝(sub_reason:非相邻/挖旗格等)→ 目标格 旗朱 #C63D22 边框闪 + toast 文案(图标+文案冗余,色弱清单 #7) +- 资源面拒绝(AP 不足/RAP 不足)→ AP/RAP 数字脉冲 + toast「AP 不足 · 剩 0 点」 +- voided(stop 被作废等)→ toast「本轮已清图,停手未执行 · 剩余 AP 已作废」——防静默改判读感欺骗 + +--- + +## Interaction Map + +**输入前提**(technical-preferences):Keyboard/Mouse 主输入,全部操作纯鼠标可完成;Gamepad Partial 非 MVP 门禁(导航序留 Open Questions);Touch None。 + +| 组件 | 动作 | 输入 | 即时反馈 | 结果(经组合根) | +|---|---|---|---|---| +| 未开相邻格 | 挖掘 | 左键 | 格碎裂 + 连锁展开(免费)+ 奖励身份揭显 | 意图入当帧批次 → A2 `settle_batch` | +| 已开格 | 移动 | 左键 | 悬停时路径预览高亮(D3);执行 ≤150ms 短滑 | 意图入批(BFS 移动,免费) | +| 未开格 | 插旗 / 已旗格拔旗 | 右键 | 旗落下/移除(旗朱) | 意图入批(1 AP / 免费) | +| 停手按钮 | 主动结束轮次 | 左键 | 按钮下压 + AP→RAP 飞行数字 | `settle_batch([stop])`(**经 A2 批次,不直调 A3**——A2 D1×6①) | +| 弃图刷新按钮(雷图右上角) | 当轮就地换图继续挖 | 左键 | 确认微交互(RAP−8 代价显式 + 旧板弃置提示);RAP<8 → 资源面拒绝可见(EC13) | `request_refresh`(**Active 相位当场换图继续挖**——AP 不重置、本轮剩 N 次扣减;**上游已修订受理——见布局决策 D4/OQ12**) | +| 图例按钮 | 开图例 | 左键 | 模态 overlay | 恒可开(A4 图例条目恒在) | +| 结算明细带 | 展开回看 | 左键 | 明细 overlay | 只读 | +| 状态/道具 tooltip | 悬停 | hover | 状态 tooltip(来源+语义+剩余);道具 tooltip(完整描述+触发注记——不含来源) | 只读 | +| 道具(窗口态可用件) | 选用 | 左键 | 选中态高亮 → 窗口关闭回结算流 | `submit_window_selection(窗口位置)`——**传窗口条目位置,非库存句柄**(缺陷④契约) | +| 道具(窗口态禁选件) | — | 左键无效 | 禁选原因 toast(满血治疗包) | 无 | +| 跳过按钮 | 跳过窗口 | 左键 | 窗口关闭 | 空选择序 | +| 胜利 overlay | 下一节点 | 左键 | → NodeOverlay / 关卡结算 | LevelRunner 推进 | +| 失败 overlay | 返回 | 左键 | → 存档/营地 | 冒险终结 | + +**单意图帧纪律**(A2 D1×6⑥):一帧内 collected 意图整批提交,stop 点击终结当帧收集;F4 语义序仲裁为防御性兜底。悬停预承诺三态光标:移动(箭头)/挖(镐)/旗(旗形)——左键双义(移动 vs 挖)的消歧完全依赖四格态一瞥可辨 + 光标区分(误挖不可逆的感知保险)。 + +**复合意图(A2 评估项留痕)**:点击远格 = 自动 move+dig 的两击摩擦消解——**MVP 不做**(留痕 2026-09-12:免费移动高频,两击摩擦符合 P4 ≤2 观测假设;A2 L298 评估项)。 + +**可挖域常驻指示**:小人 8 向相邻未开格呼吸微光(§2.1 唯一在动的元素);不可挖域(非相邻)悬停显示禁止光标 + 首次 toast 提示「需先走近(点击已开格移动,免费)」。 + +**chord 肌肉记忆首遇提示**(A2 D1×6③):经典扫雷玩家首日尝试双击快开时,非模态 toast 每 run 一次:「本作无双击快开——插旗后轮末自动判定标中;点数字格可移动小人」(含 A2 登记锚点「点击数字格 = 移动小人」)。文案定稿权归实现期文案 pass。 + +--- + +## Events Fired + +项目无分析系统;本节 = **呈现层订阅的核心信号**(ADR-0001 组合根直连 signal)+ playtest 遥测义务(playtest_logger.gd 已有五类行)。呈现层新遥测(写 playtest JSONL `ux` 行): + +| Player Action | Event Fired | Payload / Data | +|---|---|---| +| 每轮结束 | `ux_round_input_ratio` | 原始输入动作数∶AP 消耗(A3 P4 ≤2 护栏观测,A2 D1 遥测义务) | +| 停手/清图 | `ux_round_exit` | path_tag {stopped, ap_exhausted, cleared}(对齐上游封闭枚举)——弃图率(C3 OQ4)观测 | +| 弃图刷新(挖掘期,非轮出口) | `ux_refresh_attempt` | {result: accepted/rejected_rap/rejected_cap, round_refresh_index: 1..2}——刷新使用(C3 OQ6)与 OQ3 预警阈值观测;每轮 0–2 次(D4 cap=2) | +| 窗口选用/跳过 | `ux_window_choice` | {selected_item_id, usable_now, skip} | +| 拒绝/voided | `ux_reject` | {source: board/resource/phase, sub_reason} | +| 提示触发/关闭 | `ux_hint_shown` | {hint_id: fog_first/chord/no_adjacent} | +| 呈现订阅(不改核心) | A2 三事件 / B1 步骤流(13 类行协议,含终局判定行) / battle_over / level_clear | 载荷 schema 成文义务见 §Data Requirements——entry 17 D1 硬性要求① | + +**持久状态写入**全部经组合根唯一路径(settle_batch / submit_window_selection / request_refresh / runner 推进)——UI 层禁直调核心写(架构七原则"依赖单向组合根")。 + +--- + +## Transitions & Animations + +| 时刻 | 过渡 | 依据 | +|---|---|---| +| 进入战斗屏 | 棋盘 install 淡入(200ms)+ 初始 3×3 展开涟漪 + Boss 战先播紫赤影子爬棋盘(§2.6) | A1 install / §2.6 | +| 挖掘→结算 | 明细带自底展开(150ms)+ 棋盘去饱和 + 金橙色相偏移 + 阶段指示翻转 | §2.2 色彩转金橙主导、饱和拉高、明度不变(状态区分经饱和/色相——art-bible §2 合规化 2026-09-12) | +| 结算逐行兑现 | 光带自棋盘行起点横扫 → 开格化箭雨/旗化暴击弹道飞向敌人卡 → 受击闪白 + 血条直角递减 | §2.2 承载元素;因果全程可见可归因(P2) | +| 道具窗口 | E 区描边金光点亮(非弹窗,无缩放突兀) | 决策 D2 | +| 清图换新图 | 金粒喷泉 3–5s → 旧图碎裂淡出 → 新图 install 淡入 | §2.3 短促丰收不拖节奏 | +| RAP 清零(battle_over) | RAP 数字滚动归零 + 「随战斗结束消散」微文案 | A3 可见反馈义务 | +| 敌人死亡 | 血条直角递减至零 → 卡体灰化 + 碎裂退场(禁瞬消) | B2 资产下限(同等 blocking);动画密度归 B5 | +| 状态槽全清(battle_over) | 状态逐槽化光收回 + 「状态随战斗结束消散」微文案 | B3×4④;RAP 清零同款先例 | +| 库存清空(level_clear) | 道包逐件化光点飞出 + 「剩余道具 ×N 随关卡结束清空」 | B4 D1×⑤ | +| 胜利 | 蜡封自上落下弹跳定格 → 碎砖拼纹章 → overlay 暖金渐显 | §2.4 克制不狂欢 | +| 失败 | 全屏明度不变转暮蓝色相 → 旗化光点 800ms 逐面收回 → overlay | §2.5 最静一拍 | +| 减动效选项 | 上述全部动画提供时长减半/直接跳终态开关(项目未建 accessibility 文档前,本项为 spec 内建承诺) | 动议:结算逐行兑现为信息载体不可整体跳过,可加速 | + +--- + +## Data Requirements + +| Data | Source System | Read / Write | Notes | +|---|---|---|---| +| 棋盘四格态/数字/雾/可挖域 | A2 只读面(query) | Read | 呈现刷新面 = A2 三事件信号(载荷 schema 成文——entry 17①) | +| BFS 移动路径 | A2 `compute_move_path` | Read | D3 合法性指示消费 | +| 拒绝原因 | A2 `sub_reason` 封闭枚举 + A3 资源面 | Read | 三源(board/resource/phase)映射文案——呈现层契约成文;资源面原因无上游载荷(A3 仅 bool + debug 诊断),客户端按动作上下文推断 | +| AP / RAP / 本轮刷新计数 | A3 | Read | 常显;刷新入口可用性 = RAP 读数客户端预判(RAP≥8)+ `request_refresh` 返回值(A3 无 `can_request_refresh` API——仅 bool 返回 + 只读观测量;EC13 拒绝合法可见) | +| 结算流水行(13 类)+ 四类行 | B1 流水协议 | Read | 逐行来源标注(P2);聚合计数行呈现 | +| 判定 verdict | B1 判定段 | Read | 终局画面数据锚 | +| 敌单位 hp/顺位/意图环下一拍/enemy_id/存活 | B2 只读面 | Read | 意图线索映射全局恒定(D1×4②);**despawn 前捕获**(entry 17③——缺陷②教训) | +| 状态效果投影(含空态/极性/溢出) | B3 投影口径 | Read | 极性映射数据源 = B1 SettlementTable 效果语义,非 magnitude 符号(B3 D1×③) | +| 库存(item_id/状态/枚举序/行值投影)+ 奖励身份 | B4 `query_inventory` / 快照 | Read | reward_id 全局恒定;**属性简述由行值快照投影派生**(伤害/回复/增益一句话);载荷含来源 {cell, round} 但**不呈现于库存列表与 tooltip**(2026-09-12 用户拍板——来源归因职责由结算明细行承载,P2 不受影响);窗口载荷 usable_now 由 B1 组装(B4 零跨系统读) | +| 节点链/当前节点/HP 携带 | C1-lite LevelRunner | Read | A 区进度条 | +| 意图提交 | A2 | **Write** | `settle_batch`(stop 亦经此——不直调 A3) | +| 窗口选择 | B1 | **Write** | `submit_window_selection(窗口位置序)` | +| 弃图刷新 | A3 | **Write** | `request_refresh`(挖掘期 Active 当场受理——D4 2026-09-12 上游已落稿;B1 `refresh_board` 编排承载) | +| 节点推进 | LevelRunner | **Write** | 胜利 overlay「下一节点」 | +| 终局画面数据锚 | B1 判定行(流水行协议) | Read | 「终局判定行 = 胜负画面的数据锚」(B1 UI Requirements);despawn 前状态可见性纪律 = entry 17③(销毁前捕获、**载荷数据自包含**、禁呈现层回读核心生命周期状态);若需独立终局快照信号,须回 B1/实现期 ADR 补登记(B1 现无 `settlement_snapshot` 契约——2026-09-12 评审修正) | + +**架构注记**:UI 不拥有任何游戏状态——全部读经核心只读面,全部写经组合根(ADR-0001/0003)。呈现层信号载荷 schema 须在 D1 GDD 成文(entry 17①,缺项即 blocking);场景级 headless 测试进 AC(entry 17②);覆盖层层级断言(缺陷⑤——任何模态覆盖层必须有"打开时遮挡物隐藏"断言)。 + +**更新触发注记(2026-09-12 评审增补——防实现层等待不存在的信号,entry 17 教训同类)**:旗插/拔与移动成功反馈**零事件外发**(A2 发射面封闭)——经意图调用 `ActionOutcome` 返回值命令式驱动;AP/RAP 读数 A3 无推送信号——组合根每次调用返回后重查;状态效果 B3 纯被动容器零信号——状态栏刷新绑 B1 结算编排完成时点;库存 B4 `query_inventory` 查询面——写路径回执后重查。 + +--- + +## Accessibility + +项目尚未建 `design/accessibility-requirements.md`(gate-check 缺口,见 Open Questions)。本 spec 内建基线承诺: + +- **对比**:全部格底/面板底对数字墨 ≥4.5:1(WCAG AA)——art-bible §4.1 硬指标已可测(#33261B/#C9AF83 = 6.9:1 实测);UI 数据一律墨族素面 +- **色弱冗余**:无任何信息仅靠色相承载——四格态靠完整/碎裂形+纹带形;奖励金 vs 危险红靠剪影+运动;UI 红/金强调对加图标+文案冗余;Boss 靠形状独占(art-bible §4.5 清单逐项成立) +- **灰度可辨**:变异格纹带、四格态在灰度截图下可分类(art-bible §3.2 验收标准) +- **非色彩破坏性确认**:弃图刷新(消耗 RAP 8 点)的确认交互与常规确认**不同位**(位置通道,色弱清单 #7) +- **字号下限**:棋盘数字 ≥18px、明细带正文 ≥14px、tooltip ≥13px(1280×800 基座) +- **运动**:无闪烁 >3次/秒;结算光带为方向性扫动非频闪;提供减动效选项(§Transitions) +- **键盘/Gamepad**:MVP 纯鼠标可完成全部操作(technical-preferences 平台注记);**MVP 决策留痕:纯键盘导航/焦点序不做**(2026-09-12——回合制点击驱动,平台仅承诺纯鼠标);gamepad 导航序延后(Open Questions) + +--- + +## Localization Considerations + +- **源语言中文(CJK)**:运行时默认字体无 CJK 字形——**CJK 字体随包为硬性前置**(ADR-0003 已锁),实现 AC 覆盖 +- **按钮文案单行约束**:「停手」「弃图刷新」「跳过」「选用」——按钮宽度按 4 汉字上限设计;译为英文("Hold"/"Reroll"/"Skip")不超 1.6× 不破版;德语长词("Aufgeben+Neu"类复合词)风险 HIGH——弃图刷新按钮允许两行或图标+缩写 +- **明细带行文案**:来源标注格式「{行为} {值} → {目标}(来源)」为模板插值,最长 token = 道具名(4 汉字);行模板预留 40% 膨胀宽度(固定槽高 + clip 兜底) +- **数字格式**:全部整数,无本地化格式需求;AP 点阵为图形载体不依赖文本 +- **术语表锚定**:旗/雷/停手/弃图刷新/轮次/结算 已在 GDD 体系内定名,译文须同步 registry terminology(五词条先例) + +--- + +## Acceptance Criteria + +**布局与可读** + +- [ ] 1280×800 与 16:9 全屏(canvas_items/expand)下五区无遮挡、无重叠、无内容裁切(棋盘 12×12 全部格子 + 数字完整可见) +- [ ] 进入战斗屏 ≤500ms 内可交互(棋盘 install 淡入完成后首格可点);同屏 draw calls < 200(technical-preferences 预算) +- [ ] 四种格态(已开 0 格地板/已开雾格无字/未开格/未开雾格)灰度截图下一眼可辨(A2 D1×6②) +- [ ] 悬停光标区分移动/挖/旗三语义;可挖域(8 向相邻)常驻指示;非相邻格悬停显禁止反馈(A2 D1×6③) +- [ ] 已引爆雷格呈"可通行可读"(A2 D1×6④) +- [ ] 全部数字对所在格底对比 ≥4.5:1;明度恒定规则无违例(art-bible §4.1) + +**交互与反馈** + +- [ ] 挖/移/旗/停手/刷新全部操作纯鼠标可完成;stop 意图经 `settle_batch` 提交(代码走查 + 探针断言) +- [ ] 每次拒绝(棋盘面/资源面/相位)玩家可感知且两源可区分;voided(至少 stop 作废)可感知(A2 D1×6⑥、A3 双源) +- [ ] 道具窗口:usable_now=false 禁选+原因可见;至多选用 1 件;空库存不弹窗;选择传窗口位置非库存句柄(选中件库存态必变 Used——行为探针断言,缺陷④回归) +- [ ] 弃图刷新消耗 RAP 8 点、本轮上限 2 次可见(按钮文案实时「剩 N 次」;上限已随 A3 2026-09-12 修订生效);RAP<8 资源面拒绝可见(EC13);确认交互与常规确认不同位;换图后 AP 不重置、剩余次数扣减可见(**上游已修订受理——2026-09-12 D4 落稿**) +- [ ] battle_over 时 RAP 清零有可见反馈;level_clear 时库存清空有可见反馈(A3/B4 D1×⑤) +- [ ] battle_over 时状态槽全清有可见反馈(逐槽化光 + 微文案)(B3 D1×④) +- [ ] 敌人死亡瞬间可辨识(卡体灰化 + 碎裂退场,禁瞬消);战斗段短路被掐灭的重击由行序可推断(B2 资产下限) + +**信息与归因** + +- [ ] 结算逐行明细实时可见、战斗后可回看完整流水(P2 逐行归因);四类行(道具语境/奖励未生效/已消耗未生效/使用被拒)按 B4 D1×③ 呈现 +- [ ] 敌人意图徽标挖掘期全程可见、映射全局恒定、含顺位与环长直觉编码(B2 D1×4①②③) +- [ ] 状态效果:极性呈现映射成文且非 magnitude 符号;空态渲染成文(裁决:槽隐藏);满层溢出经明细行可见(B3 D1×①③④) +- [ ] 图例面板「迷雾 = 无雷盲区」条目恒在可随时回看;雾首遇提示一次性(每 run)且非模态不劫持输入,必含三要点(无雷盲区不赌命/雾格不传播连锁/雾内插旗纯损)(A4 D1×②) +- [ ] 零合法板(A1 EC5 降级)出现时玩家侧诚实提示(toast,非静默) +- [ ] 已开雾格与已开 0 格灰度截图下可判定区分(底色层 tint 残留——OQ8 裁决采纳);雾区全开完成信号以视觉合并域为单元、无实例级双响(EC11;样张须含宽度 1 雾带两侧毗邻数字格等最坏用例——A4 AC-9) +- [ ] 效果行 value 为单次贡献值,不得与 pool_value(池合成值)相加呈现;零值伤害行可见不被吞;衰减行按聚合计数呈现(B1 呈现细则) +- [ ] 未生效类行(奖励未生效/已消耗未生效/使用被拒/溢出未生效)携带形状冗余(icon/划线),非色相独载 +- [ ] 奖励徽章绘制优先级:旗之下、变异纹带之上、不进数字区;拾取瞬间 tier 档位可读(H1) + +**场景级测试**(entry 17②③,缺项即 blocking) + +- [ ] 场景级 headless 测试:真实实例化战斗屏场景 + 驱动完整对局 + 断言零脚本错误(ui_click_probe 模式扩展) +- [ ] 任何模态覆盖层(图例/明细回看/胜利/失败/窗口态)有「打开时遮挡物隐藏/输入锁定」场景级断言(缺陷⑤对照) +- [ ] 动态区全部固定槽高 + clip_contents——悬停文案换行不推挤布局(缺陷①回归) +- [ ] CJK 字体随包,全部中文文案渲染非豆腐块(ADR-0003) +- [ ] 敌人 despawn 后呈现层不回读核心生命周期状态(快照纪律,缺陷②回归) + +--- + +## Open Questions + +1. **Player journey map 未建**——建议实现前补建(`design/player-journey.md`),校准挖掘→结算→终局情绪曲线与 art-bible §2 能量档的匹配 +2. **`design/accessibility-requirements.md` 未建**(gate-check Pre-Production 缺口)——本 spec 内建 WCAG AA 基线承诺,正式分层(文本缩放/色盲模式/按键重映射)待该文档定稿 +3. **`design/ux/interaction-patterns.md` 未建**——本 spec 新引入待入库 pattern:toast 双源拒绝、原位窗口激活、逐行兑现明细带、悬停预承诺光标、点阵资源显示(/ux-design patterns 批次入库) +4. **战斗表现美术测试**(game-concept 已登记,B5 前强制):本 spec 的常驻双舞台分屏(决策 D1)是测试的布局输入——敌人卡卡片式 vs 场景站桩式(无框立绘+底座)待样张定稿;本 spec 按"卡片式"起草(信息分层最稳),站桩式不改变分区结构 +5. **移动动画形态**(D3):≤150ms 短滑为推荐值,playtest 观测「移动太瞬/太拖」反馈后调 +6. **明细带挖掘期是否常驻上轮摘要**:常驻(现方案)vs 结算后即收起——playtest 观测回看频率 +7. **道具窗口原位激活**(D2):若 playtest 反馈窗口存在感不足(漏选),升级为 E 区放大 + 全屏微暗(仍非中央弹窗) +8. **Gamepad 导航序**:Partial 支持(非 MVP 门禁)——焦点环顺序待 gamepad 批次定 +9. **NodeOverlay / 关卡结算屏**:相邻域屏,建议后续 spec `design/ux/node-reward.md` + `design/ux/level-clear.md`(本 spec 只锁出口锚点) +10. **TunePanel 退役计划**:正式侧归 C4(天赋树),切片调试入口在 D1 GDD 标注退役时点 +11. **提示文案定稿**:雾首遇三要点/chord 提示/空态文案归实现期文案 pass,本 spec 只锁必含要点与形态约束 +12. **弃图刷新上游修订(已落稿 2026-09-12,布局决策 D4)**:A3(Active 相位受理 + 每轮上限 2 次 + Waiting 代理退役 + 旧旗作废裁决 + 时间税失效预警)、A2(Active·仅刷新路径装板受理)、B1(stopped/ap_exhausted 恒 same_board + `refresh_board` 挖掘期刷新编排;AC-29 改写/AC-30 恒 same_board)、registry `rap_rules` 同步、systems-index 修订块登记。遗留观察项:当场刷新的节奏健康由 A3 OQ3 弃图率/刷新率预警承载,playtest 复核 +13. **D5 教学缺口挂靠(D5 GDD 未建,留痕)**:挖到雷 = 敌方优势而非死亡(A2 D5「经典扫雷玩家最大预期违背」)、旗挡链(连锁止于旗属正常)、首次蓄力姿态一次性提示(B2)、窗口拾取→囤刀→选用闭环教学(B4)——本屏 toast 体系为落点候选,随 D5 批次定 + +--- + +## §13 上游义务对照表(门禁核对用) + +| 义务源 | 条目 | 本 spec 承载落点 | +|---|---|---| +| A2 D1×6 | ① stop 经批次 | §Interaction Map 停手行 + AC | +| A2 D1×6 | ② 四格态可辨 | §Layout 格态 + AC | +| A2 D1×6 | ③ 悬停预承诺+可挖域+chord 提示 | §Interaction Map + §States | +| A2 D1×6 | ④ 引爆雷格可通行可读 | §AC | +| A2 D1×6 | ⑤ 路径预览裁决 | 决策 D3 | +| A2 D1×6 | ⑥ 单意图帧+voided+拒绝可感知 | §Interaction Map + §States 拒绝子态 + AC | +| A3 | AP/RAP 常显+刷新入口+清零反馈+双源 | §Layout C/D 区 + §Transitions + AC | +| A4×3 | 雾纹理/图例+首遇/已开雾格+tint | §States 雾图变体 + 图例组件 + AC | +| B1×3 | ①高光行强调呈现(标中旗金框行/清图 H2 金框行)②完美排雷呈现形态 ③EC5 零合法板提示/补偿/回退形态(toast 提示;补偿/回退不提供——三形态选型留痕 2026-09-12) | §States 高光/清图 + §States 拒绝子态 + §AC;注:结算明细屏+道具窗口 = B1 UX Flag 事项(本 spec 即其 /ux-design 产物),非 B1×3 台账 | +| B2×4 | 意图映射/恒定/顺位/敌方池 | §Component Inventory 敌人卡 + **意图线索映射目录(B2×4① 成文占位,资产定稿归 B5)** + AC | +| B3×4 | 常显+空态/来源标注/溢出/去留 | §States 空态 + §AC | +| B4×5 | 身份恒读/窗口/四类行/清图强调/清空反馈 | §Component Inventory E 区 + §States 清图/高光 + AC | +| A1 | EC5 零合法板诚实呈现(数据面经 B1×3③ 上抛) | §States 拒绝子态(toast)+ AC | +| B2 资产下限 | 受击反馈/死亡表现/顺位标记 | §Transitions 受击 + §States 敌人死亡呈现(禁瞬消)+ §Component 顺位徽标;呈现语义在本 spec(同等 blocking),动画密度归 B5 | diff --git a/design/ux/interaction-patterns.md b/design/ux/interaction-patterns.md new file mode 100644 index 00000000..8323c588 --- /dev/null +++ b/design/ux/interaction-patterns.md @@ -0,0 +1,227 @@ +# Interaction Pattern Library(交互 pattern 库) + +> **Status**: Complete(核心 8 成文 + 12 目录占位) +> **Author**: user + ux-designer +> **Last Updated**: 2026-09-12 +> **Template**: Interaction Pattern Library +> **来源**: `design/ux/battle.md`(2026-09-12 /ux-review APPROVED)已拍板内容的成文固化——本库不引入新设计决策 + +--- + +## Overview + +- **用途**:ui-programmer 实现时引用既有 pattern,不重复发明;实现中出现新 pattern 时**必须回写本库**(team-ui Phase 3 义务) +- **输入约束**:PC / Keyboard-Mouse 主(全部操作纯鼠标可完成)、Gamepad Partial 非 MVP、Touch None +- **视觉依据**:art-bible §3.3(半拟物:容器穿戏服、数据素面;直角=逻辑信息、圆角=可触摸物件)、§4(彩为世,墨为信——逻辑信息一律近黑暖墨族) +- **全局语义红线**:无任何信息仅靠色相承载(art-bible §4.5 色弱安全清单逐对成立);明度恒定(§2 硬规则——状态区分只用色相/饱和/运动/元素) + +--- + +## Pattern Catalog + +| # | Pattern | 类别 | 状态 | +|---|---|---|---| +| 1 | 双源拒绝反馈 | Feedback | **成文 ✓** | +| 2 | 原位窗口激活 | Overlay | **成文 ✓** | +| 3 | 逐行兑现明细带 | Data Display | **成文 ✓** | +| 4 | 悬停预承诺光标 | Input | **成文 ✓** | +| 5 | 点阵资源显示 | Data Display | **成文 ✓** | +| 6 | 模态 overlay 层级纪律 | Modal | **成文 ✓** | +| 7 | 破坏性确认异位 | Feedback | **成文 ✓** | +| 8 | 禁用态即理由 | Feedback | **成文 ✓** | +| 9 | 常驻意图徽标 | Data Display | **已实现**(enemy_card_view.gd——规格全文待补) | +| 10 | 一次性非模态教学提示 | Feedback | **已实现**(雾首遇/chord——规格全文待补) | +| 11 | tooltip 规范 | Feedback | **已实现**(状态/道具悬停——规格全文待补) | +| 12 | 单意图帧批次提交 | Input | **已实现**(main.gd 组合根——规格全文待补) | +| 13 | 状态效果槽 | Data Display | **已实现**(status_slot_row.gd——规格全文待补) | +| 14 | 步进进度条 | Navigation | **已实现**(level_strip_view.gd——规格全文待补) | +| 15 | 移动路径预览 | Input | **已实现**(board_view.gd BFS 蓝点——规格全文待补) | +| 16 | 清图/换图演出编排 | Feedback | **已实现·占位级**(board_fx_view.gd 金粒彩纸——规格随 B5 美术批补) | +| 17 | 胜利/失败终局编排 | Feedback | **已实现·占位级**(battle_end_overlay.gd——规格随 B5 补) | +| 18 | Boss 遭遇演出 | Feedback | **已实现·占位级**(紫赤影子——规格随 B5 补) | +| 19 | 库存常读列表 | Data Display | **已实现**(inventory_panel_view.gd——规格全文待补) | +| 20 | 标准控件组(button/progress bar/list) | Control | **已实现**(ui_kit.gd 直角细线/凸圆角——规格全文待补) | + +MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab bar——战斗屏无此需求;TunePanel slider 为调试专用。 + +--- + +## Patterns(核心 8) + +### 1. 双源拒绝反馈(dual-source rejection feedback) + +**Category**: Feedback +**Used In**: 战斗屏(棋盘/资源全部意图动作) + +**Description**: 玩家意图被核心拒绝时,反馈必须同时做到:(a) 每次拒绝玩家可感知(防静默拒绝读感欺骗);(b) 来源可区分——棋盘面(A2 判,携 sub_reason 封闭枚举)/资源面(A3 判,客户端推断)/相位(A2 ActionOutcome)三源各异;(c) 图标+文案双通道,非色相独载。 + +**Specification**: +- 触发:`ActionOutcome{accepted=false, reject_reason, sub_reason}` **返回值驱动**(拒绝无事件外发——A2 发射面封闭,实现层不得等待拒绝信号) +- 反馈映射:board_face → 目标格旗朱 `#C63D22` 边框闪 + toast 具体原因(sub_reason→文案映射表:not_adjacent/target_revealed/target_concealed/flag_present/flag_absent/no_path/out_of_catalog);resource_face → 对应资源数字脉冲(AP/RAP)+「AP 不足 · 剩 0 点」式文案;phase → toast(结算中/非挖掘期) +- 即时性:拒绝当帧呈现;toast 自动消退 2.5s +- **voided 区分**:作废 ≠ 拒绝(零效果零扣费零事件)——须独立文案如「本轮已清图,停手未执行 · 剩余 AP 已作废」,至少 stop 作废可感知 +- Accessibility:旗朱边框闪为形状+运动通道;文案常驻 + +**When to Use**: 任何意图提交被拒绝时(唯一拒绝呈现通道) +**When NOT to Use**: 成功反馈(走动作本身的直接视觉反馈通道);voided 语义场景(作废文案独立) + +**Reference**: battle.md §States 拒绝/作废子态、§Interaction Map;A2 D1×6⑥、A3 双源契约 + +--- + +### 2. 原位窗口激活(in-place window activation) + +**Category**: Overlay +**Used In**: 轮末道具使用窗口 + +**Description**: 窗口态切换不弹中央模态——既有面板**原位**点亮为窗口态(描边金光 + 标题切换),输入竞争区压暗锁输入。库存常读位置 = 窗口位置,空间记忆连续、不遮挡结算演出首屏。 + +**Specification**: +- 触发:B1 结算编排窗口锚点(授予段完成后、增益/减益段前) +- 行为:E 区面板描边金光点亮、标题切换「⏳ 轮末道具窗口 · 至多选 1 件」+ 时机指示常驻;D 区棋盘**去饱和 + 金橙色相偏移**锁输入(明度不变——art-bible §2) +- 退出:选用 1 件 / 跳过按钮 → 关闭回结算流;空库存不弹窗直接跳过 +- 选中即承诺(照常消耗);窗口选择序列 = 结算回放输入事件 +- Accessibility:状态区分以饱和/色相表达,非明度;窗口内禁选项用 pattern 8 + +**When to Use**: 窗口内容与常读面板同域(空间记忆连续优先)、输入竞争区明确无需遮断全屏时 +**When NOT to Use**: 全屏打断级信息(胜利/失败终局、图例回看——用 pattern 6 模态 overlay) + +**Reference**: battle.md 布局决策 D2、§States 道具窗口态 + +--- + +### 3. 逐行兑现明细带(line-by-line settlement feed) + +**Category**: Data Display +**Used In**: 战斗画面区底部(结算期第一注视点) + +**Description**: 轮末结算逐行流水实时呈现 + 可回看——「扫雷之因逐行兑现为战果」(P2 归因)的承载面。挖掘期收起为 1 行上轮摘要;结算期展开 84px 实时流;点击开完整回看 overlay。 + +**Specification**: +- 数据源:B1 步骤流(13 类行封闭协议;公共字段 `type/source_class/origin_cell/round_index/display_name/value/highlight?`);**无来源行 = 断言失败** +- 行模板:「{行为} {值} → {目标}(来源)」——来源标注(第 N 轮 · 安全格式);行文案最长 token = 道具名(4 汉字),模板预留 40% 膨胀宽度 +- 聚合纪律:未生效类行(奖励未生效/已消耗未生效/使用被拒/溢出未生效)按聚合计数呈现(防逐行惩罚轰炸);衰减行聚合计数;效果行 value=单次贡献值、禁与 pool_value(池合成值)相加呈现;零值行可见不吞(免伤轮归因不断点) +- 高亮:highlight 字段 {flag_hit, clear_reward_h2, perfect_broken} → 金框强调;完美排雷 = 独立高光行 + 全屏喝彩级反馈 +- 布局:固定槽高 + clip_contents;正文 ≥14px;数值暗底高对比;未生效类行带 icon/划线形状冗余 +- 击杀标记呈现(H3 数据源 = B1 流水击杀结果字段) + +**When to Use**: 任何「因果可归因」的逐行流水呈现 +**When NOT to Use**: 自造行类型/字段(13 类封闭枚举——增删 = 接口漂移,须走 B1 GDD 修订) + +**Reference**: battle.md §Component Inventory 结算明细带、§AC 信息与归因;b1-settlement.md Core Rule 14 / Visual Req + +--- + +### 4. 悬停预承诺光标(hover pre-commitment cursor) + +**Category**: Input +**Used In**: 扫雷图 BoardView + +**Description**: 左键双义(已开格=移动 / 未开相邻格=挖)的消歧完全依赖四格态一瞥可辨 + 悬停光标区分——**误挖不可逆**(1 AP + 可能触雷)的感知保险。 + +**Specification**: +- 光标三态:移动(箭头)/挖(镐)/旗(旗形)——按悬停格态与键位自动切换 +- 目标格边框高亮常驻 +- 可挖域常驻指示:小人 8 向相邻未开格呼吸微光(挖掘期唯一在动元素——art-bible §2.1) +- 非相邻格:禁止光标 + 首次 toast「需先走近(点击已开格移动,免费)」 +- 提交:悬停仅预承诺,**点击才提交意图**(单意图帧批次——目录 #12) +- chord 首遇提示:怀旧玩家双击快开尝试时一次性 toast(含 A2 锚点「点数字格可移动小人」) + +**When to Use**: 一键多义且后果不对称的操作面 +**When NOT to Use**: 单义控件(直接点击反馈即可,勿加光标层) + +**Reference**: battle.md §Interaction Map;A2 D1×6②③ + +--- + +### 5. 点阵资源显示(dot-matrix resource display) + +**Category**: Data Display +**Used In**: AP 条、RAP 框 + +**Description**: 离散点阵 + 数字双承载的资源显示——点阵为图形载体(本地化零成本、一眼可读余量),数字为精确值。 + +**Specification**: +- 形态:`●●●●●○○○` + 「5/8」;直角细线数据框(信息=直线直角,art-bible §3.3) +- 更新触发:组合根每次调用返回后重查(**A3 无推送信号**——实现层不得等待资源变更事件) +- 拒绝反馈:资源不足时数字脉冲(pattern 1 的 resource_face 腿) +- Accessibility:点阵=形状通道、数字=文本通道;对比 ≥4.5:1 + +**When to Use**: 小整数离散资源(≤16 量级) +**When NOT to Use**: 大数值/连续量(用直角细线进度条——目录 #20) + +**Reference**: battle.md §Layout C/D 区;A3 UI Requirements + +--- + +### 6. 模态 overlay 层级纪律(modal overlay discipline) + +**Category**: Modal +**Used In**: 图例面板、结算明细回看、胜利/失败终局 overlay + +**Description**: 任何模态覆盖层打开时必须**隐藏/锁定其遮挡的交互物**,关闭恢复——防「看不见但可点」的幽灵输入(垂直切片缺陷⑤教训,场景级断言强制)。 + +**Specification**: +- 打开:遮挡物隐藏或禁用 + 输入锁定;单一模态栈(无叠模态) +- 关闭:恢复被遮挡物;关闭按钮/再点入口切换 +- 内容:固定槽高 + clip_contents(缺陷①——悬停文案换行不得推挤布局) +- 验收:**场景级断言「打开时遮挡物隐藏/输入锁定」为每个模态 overlay 的强制 AC**(battle.md 场景级测试节) + +**When to Use**: 全屏打断级信息、只读回看、仪式性终局 +**When NOT to Use**: 与常读面板同域的窗口态(用 pattern 2 原位激活) + +**Reference**: battle.md §AC 场景级测试;缺陷①⑤(production/session-state entry 17) + +--- + +### 7. 破坏性确认异位(destructive-confirm position offset) + +**Category**: Feedback +**Used In**: 弃图刷新(RAP−8 消耗 + 旧板弃置) + +**Description**: 消耗性/不可逆操作的确认交互与常规确认**不同位**——位置通道是色盲安全冗余(art-bible §4.5 清单 #7:UI 红/金强调对需图标+文案冗余,且破坏性确认不得与常规确认同位)。 + +**Specification**: +- 弃图刷新:确认微交互显式呈现全部代价(RAP−8 · **旧板旗随旧板作废**提示);确认交互位置与常规确认不同位 +- 双通道:图标 + 文案冗余 +- 拒绝路径:RAP<8 → pattern 1 resource_face(EC13 合法拒绝可见) + +**When to Use**: 任何消耗资源/不可逆的操作确认 +**When NOT to Use**: 无代价操作(直接执行 + 直接反馈) + +**Reference**: battle.md §Accessibility、§AC 交互与反馈;art-bible §4.5 #7 + +--- + +### 8. 禁用态即理由(disabled-state with reason) + +**Category**: Feedback +**Used In**: 道具窗口 usable_now=false 件(满血治疗包) + +**Description**: 禁用不许静默——灰置必须伴随**可见的禁选原因**(tooltip/toast),防「为什么点不动」的困惑。 + +**Specification**: +- 形态:灰置 + tooltip「HP 已满 · 禁选」;点击无效 + 原因 toast 兜底 +- 语义区分:满血 = 零收益守卫(禁选、不烧包)≠ 缺血不足(超量 clamp 部分回复,**不**禁选) +- usable_now 由 B1 组装进窗口载荷(B4 零跨系统读)——呈现层只消费不推断 + +**When to Use**: 任何可见但不可交互的元素 +**When NOT to Use**: 完全隐藏的不可用功能(不存在的东西无需解释) + +**Reference**: battle.md §States 道具窗口态、§Interaction Map;B4 Rule 7 / AC-14 + +--- + +## Gaps & Patterns Needed + +- **目录 #9–#20 已全部实现**(2026-09-12 战斗屏实现批,冒烟 149/149 + 三探针全绿);规格全文回写 = D1 正式化批义务 +- 实现期新增技术件(不单独立 pattern,规格归并):去饱和+金橙状态切换 = 逐格共享 ShaderMaterial(明度不变,gl_compatibility 安全);敌人死亡残位 = 卡体灰化+碎裂禁瞬消(归入 #17 终局编排规格) +- **MVP 未使用标准控件**:toggle / dropdown / input field / tab bar(无需求不设占位);TunePanel slider 为调试专用不入库 +- **实现偏差规则**:pattern 近似但不完全适配时,实现层不得静默变体——登记偏差交 ux-designer 复核后回写 + +--- + +## Open Questions + +1. 目录占位项的补全时点 = 实现期随批回写(owner:ui-programmer → ux-designer 复核) +2. Gamepad 批次引入时,需为 pattern 4/12/15 增补手柄映射节(焦点序已在 battle.md OQ8 留位) diff --git a/docs/architecture/adr-0001-event-bus-and-orchestration-carrier.md b/docs/architecture/adr-0001-event-bus-and-orchestration-carrier.md new file mode 100644 index 00000000..085ed15a --- /dev/null +++ b/docs/architecture/adr-0001-event-bus-and-orchestration-carrier.md @@ -0,0 +1,129 @@ +# ADR-0001: 事件总线与编排载体(Event Bus & Orchestration Carrier) + +## Status +Accepted(2026-09-12,solo 模式用户授权代决;用户迭代期可随时推翻重议——推翻时须走本 ADR 修订 + 下游 AC 连锁复核) + +## Date +2026-09-12 + +## Engine Compatibility + +| Field | Value | +|-------|-------| +| **Engine** | Godot 4.7.2-stable | +| **Domain** | Core / Scripting(Signal/Callable/生命周期) | +| **Knowledge Risk** | LOW(4.x 基线稳定;4.5–4.7 变更清单零命中,godot-specialist 校验通过) | +| **References Consulted** | `docs/engine-reference/godot/`(VERSION / breaking-changes / deprecated-apis / current-best-practices);godotengine/godot#6360 | +| **Post-Cutoff APIs Used** | None | +| **Verification Required** | None(切片 88/88 冒烟已实证:双源 round_end 汇聚、可回放步骤流) | + +## ADR Dependencies + +| Field | Value | +|-------|-------| +| **Depends On** | None | +| **Enables** | ADR-0003(场景结构引用本 ADR 的接线纪律);A2/B1 全部事件序类 AC 从纸面契约转为可执行 | +| **Blocks** | 正式工程骨架搭建(架构文档 Required ADRs 条件) | +| **Ordering Note** | 与 ADR-0008(切片机制正式化)并行无冲突;B1 结算触发语义以本 ADR 载体承载 | + +## Context + +### Problem Statement +A2(三事件外发)、A3(stopped 直发 round_end,双发射源)、B1(结算触发 = Waiting 相位编排点、不依赖事件到达序)之间的信号载体需要一个落地形态——GDD 层只定行为契约(A2 OQ2 / B1 OQ1 均登记为"实现期 ADR 确认项")。 + +### Constraints +- 单线程回合制(D2 EC13);Core 层零 UI 依赖、headless 可跑(D2 Rule 7) +- 结算可回放是硬承诺(B1 Core Rule 15:禁 await、零随机) +- 事件序契约:`flag_judged` < `round_end` 全路径统一,但到达序不承载语义 +- RefCounted 纯逻辑类无场景表示,无法在 .tscn 编辑器里对接信号 + +### Requirements +- 支持 A2 三事件面 + A3 stopped 直发的双发射源汇聚 +- 支持多订阅者(B1 facts 缓冲;呈现层只订 B1/runner) +- 测试可捕获事件序列(A2/B1 AC 的事件捕获断言面) +- 生命周期确定(无悬挂连接、无循环引用) + +## Decision + +**组合根直连 signal 接线 + B1 Waiting 相位触发;否决 autoload 全局事件总线。** + +1. **信号载体**:A2(RefCounted)声明并发射 GDD 三事件——`cell_revealed` / `flag_judged` / `round_end_board`(GDD 概念名 `round_end` 棋盘路径的实现实名);A3 停手路径发射 `round_end_stopped`(payload 与棋盘路径同构)。回发 `next_round_mode` / `battle_over` 同为 A3/B1 侧 signal。 +2. **接线位置**:全部 signal.connect 发生在**组合根**(主场景 Node 的装配代码)——core↔core 无静态耦合,依赖单向由接线拓扑保证。RefCounted 无 .tscn 编辑器对接能力,组合根是唯一自然落点。 +3. **B1 触发**:订阅 A3 相位变更(→Waiting)触发结算事务;触发判定只读 A3 相位观测量与 `last_round_end`,**不消费事件到达序**(同步发射 + 双发射源的正确防御—— specialist 确认信号为同步发射、多订阅者投递序 = 连接序 FIFO)。 +4. **订阅纪律**:呈现层**永不直接订阅 A2/A3**——只订 B1 步骤流 / runner(切片现状固化,防呈现层绕过编排读核心生命周期状态——切片缺陷②教训)。 + +### Architecture Diagram + +``` +[A2 minesweeper_rules]──cell_revealed/flag_judged/round_end_board──┐ +[A3 action_economy]────round_end_stopped / phase→Waiting──────────┤ + ▼ + [组合根 main 装配接线]→[B1 battle_orchestrator] + │ 步骤流行协议 + ▼ + [D1 视图单元(只订 B1/runner)] +``` + +### Key Interfaces + +- 信号均为 **方法 Callable 连接**(见禁令);信号参数带类型标注仅作文档,无运行时检查——payload 校验由消费侧断言承载(A2 AC 前言口径)。 +- A2 事件捕获测试 API:冒烟经 signal connect 到捕获数组(AC 前言"事件捕获断言面"的落地形态)。 + +## Alternatives Considered + +### Alternative 1: autoload 全局事件总线 +- **Description**: 中心 autoload 单例持有全部具名信号,系统经总线收发。 +- **Pros**: 解耦发射者/订阅者;全局可发现。 +- **Cons**: 全局耦合(任何系统可达任何信号——与"依赖单向"原则冲突);单线程回合制无此需求;事件溯源变难。 +- **Rejection Reason**: 过度设计;切片直连形态已验证且更简单。 + +### Alternative 2: 轮询(B1 每帧查 A3 相位) +- **Description**: B1 在 process 中轮询 A3 相位观测量。 +- **Cons**: 延迟(帧粒度)+ 空转;与"回合制无逐帧玩法逻辑"原则冲突。 +- **Rejection Reason**: 无收益;信号已同步即时。 + +### Alternative 3: 回调注册表(A2 持有 handler 列表) +- **Description**: A2 暴露 register(handler) API,非 signal。 +- **Cons**: 非 GDScript 惯用;多订阅者接线繁琐;丢掉引擎信号工具链(.tscn 对接、调试器可视)。 +- **Rejection Reason**: signal 即本用途的引擎原生方案。 + +## Consequences + +### Positive +- 事件生命周期**确定性优于 Node 方案**:RefCounted 引用计数归零即双向自动断连,无 queue_free 帧末窗口(specialist 核实)——不存在"向半死对象发射" +- 组合根接线使依赖方向可视化、可审计;切片 88/88 已验证 + +### Negative +- 组合根承担全部接线职责——视图单元增多时接线代码线性增长(切片 main.gd 433 行已容纳;演进为装配器函数分组即可) +- 信号同步发射的再入语义需实现者理解(本 ADR 的"不依赖到达序"即防御) + +### Risks +- **lambda 捕获 self 的循环引用**:GDScript lambda 捕获持强引用——短命 RefCounted 以捕获 self 的 lambda 连到长命发射者会延长生命周期乃至成环,自动断连永不触发 → **缓解(固化为本 ADR 静态规则)**:core↔core 信号连接只许方法 Callable;发射者禁止持有对订阅者的反向引用 +- 信号参数无运行时类型检查 → 缓解:消费侧断言(既有 AC 口径)+ 呈现载荷 schema 成文义务(code-conventions) + +## GDD Requirements Addressed + +| GDD System | Requirement | How This ADR Addresses It | +|------------|-------------|--------------------------| +| a2-minesweeper-rules.md | Core Rule 2 三事件回边契约(发射面封闭);OQ2 事件外发载体 | signal 载体 + 实名映射(round_end→round_end_board)+ 组合根接线;捕获 API 承载 AC 断言面 | +| b1-settlement.md | Core Rule 2 结算触发 = Waiting 相位编排点;OQ1 事件总线载体 | 订阅 A3 相位触发、只读 last_round_end、不依赖到达序 | +| a3-action-economy.md | Rule 9 round_end 双发射源(stopped 直发、payload 同构) | round_end_stopped 独立信号,同构 payload 由组包纪律保证 | +| systems-index.md | 唯一回边裁决(A2 只外发、B4/B1 订阅) | 组合根接线拓扑 = 裁决的落地形态 | + +## Performance Implications +- **CPU**: 信号发射 = 同步调用链,回合制下每事务个位数次发射——可忽略 +- **Memory**: 方法 Callable 弱引用目标、不延长生命周期;lambda 禁令消除成环风险 +- **Load Time**: 无 +- **Network**: 不适用 + +## Migration Plan +切片即本决策的实现(`core/minesweeper_rules.gd` 三信号、`action_economy.gd` round_end_stopped、`battle_orchestrator.gd` 双源汇聚)——无迁移,正式化 = 把接线纪律与静态规则写入扫描域(ADR-0004)。 + +## Validation Criteria +- A2/B1 冒烟事件捕获断言面全绿(既有 88 条覆盖) +- 静态扫描:core↔core 连接零 lambda;发射者零反向引用(ADR-0004 扫描域承载) +- 回放确定性:同种子同输入步骤流逐行相同(B1 AC-33 先例,切片已证) + +## Related Decisions +- ADR-0003(场景结构与组合根)、ADR-0004(测试载体与静态扫描域) +- docs/architecture/architecture.md(Data Flow §2 事件路径) diff --git a/docs/architecture/adr-0002-renderer-and-window-config.md b/docs/architecture/adr-0002-renderer-and-window-config.md new file mode 100644 index 00000000..2800d9ba --- /dev/null +++ b/docs/architecture/adr-0002-renderer-and-window-config.md @@ -0,0 +1,119 @@ +# ADR-0002: 渲染器与窗口配置(Renderer & Window Config) + +## Status +Accepted(2026-09-12,solo 模式用户授权代决;迭代期可推翻重议) + +## Date +2026-09-12 + +## Engine Compatibility + +| Field | Value | +|-------|-------| +| **Engine** | Godot 4.7.2-stable | +| **Domain** | Rendering | +| **Knowledge Risk** | MEDIUM(渲染域为 4.5–4.7 变更集中地——本 ADR 相关项已逐条核实,godot-specialist 校验通过) | +| **References Consulted** | `docs/engine-reference/godot/`(四文件);Godot 4.3 release notes(Compatibility 特性补齐);Godot Docs Environment and post-processing | +| **Post-Cutoff APIs Used** | None | +| **Verification Required** | 首次正式导出时核对 gl_compatibility 在目标硬件清单上的实际表现(首次 release 导出 checklist 既有项) | + +## ADR Dependencies + +| Field | Value | +|-------|-------| +| **Depends On** | None | +| **Enables** | ADR-0003(工程骨架的 project.godot 基线);D1 正式化 | +| **Blocks** | 正式工程骨架搭建 | +| **Ordering Note** | B5 战斗表现若需 HDR 2D 届时复议(见 Risks) | + +## Context + +### Problem Statement +technical-preferences 将渲染器选择(Forward+ vs gl_compatibility)登记为实现期 ADR;project.godot 的窗口/stretch 基线需要显式定稿——4.7 新项目默认值与训练数据不同(stretch mode canvas_items / aspect expand / Windows 驱动 D3D12),必须显式落盘防漂移。 + +### Constraints +- 2D 回合制;draw calls < 200 预算;无 3D、无移动端 +- headless 测试链路不初始化渲染器(dummy DisplayServer)——测试与渲染器解耦 +- 引擎知识 MEDIUM 风险:渲染域选型必须基于已核实清单 + +### Requirements +- 满足视觉锚(数字可读性、双通道、绘制优先级)所需的渲染能力 +- PC(Steam)硬件门槛尽量低 +- project.godot 各键显式写入(防未来 config_version 升级漂移) + +## Decision + +**渲染器 = gl_compatibility;窗口基座 1280×800(切片已验证值)、stretch mode = canvas_items、aspect = expand;全部键显式写入 project.godot。** + +```ini +rendering/renderer/rendering_method="gl_compatibility" +rendering/renderer/rendering_method.mobile="gl_compatibility" ; PC 无关,显式对齐防混淆 +display/window/size/viewport_width=1280 +display/window/size/viewport_height=800 +display/window/stretch/mode="canvas_items" +display/window/stretch/aspect="expand" +; display/window/dpi/allow_hidpi —— 4.x 默认 true,无需写入(勿关闭) +``` + +- **否决 Forward+**:2D 重 UI 无收益;4.6 起 Windows 新项目 D3D12 默认只作用于 Vulkan 系(Forward+/Mobile)——选 gl_compatibility(OpenGL 3.3)**完全绕开 D3D12 新驱动线**,这是 MEDIUM 风险域里的降险选择;GPU 门槛最低。 +- **stretch/aspect 与 4.7 新项目默认一致**(canvas_items/expand)——仍显式落盘,防引擎默认未来变化。 +- **基座 1280×800** = 切片验证值(非引擎默认 1152×648,亦非本 ADR 初稿设想的 1920×1080);expand 使 16:9 全屏显示正常适配,D1 正式化时如需调整走本 ADR 修订。 + +### Architecture Diagram +(不适用——配置决策;headless dummy DisplayServer 与本决策完全解耦,冒烟链路不受影响) + +### Key Interfaces +- project.godot 键集如上(显式全落盘);渲染器相关代码面 = CanvasItem/Control 绘制(三渲染器共用管线,重 UI 场景 Compatibility 开销最低——specialist 核实)。 + +## Alternatives Considered + +### Alternative 1: Forward+(Vulkan/D3D12) +- **Description**: 4.7 新项目默认渲染器。 +- **Pros**: HDR 2D、高级后处理全量可用。 +- **Cons**: 2D 回合制无收益;D3D12 为 4.6 起新默认驱动线(成熟度风险敞口);GPU 门槛更高。 +- **Rejection Reason**: 零收益 + 增加风险敞口;B5 若需 HDR 2D 届时复议。 + +### Alternative 2: 沿用引擎新项目默认不显式配置 +- **Cons**: 渲染方法默认 forward_plus;基座默认 1152×648 与切片验证值不符;未来 config_version 升级可能漂移。 +- **Rejection Reason**: 违背"显式落盘防漂移"要求。 + +## Consequences + +### Positive +- 绕开 D3D12 驱动线与 Vulkan 门槛——MEDIUM 风险域降险 +- headless 测试与渲染器解耦(已核实),CI 零 GPU 依赖 +- **B5 复议空间存在**:Compatibility 自 4.3 起已支持 2D 灯光+阴影与 glow/bloom——多数 2D 表现需求无需离开本渲染器 + +### Negative +- 无 HDR 2D(Forward+/Mobile 专属);glow 实现不同且部分属性隐藏——B5 视觉上限受此约束 +- Compatibility 已知渲染问题两条登记(GH-110670 HDR2D+tonemap glow 压暗 / GH-90259 开阴影灯光变亮)——B5 采用相关特性时核对 + +### Risks +- B5 需要超出现有能力的表现 → 缓解:复议通道 = 本 ADR 修订(迁移成本 2D 项目可控) +- OpenGL 3.3 在极旧硬件的覆盖 → 缓解:目标平台 PC Steam 主流硬件;首次导出 checklist 核对 + +## GDD Requirements Addressed + +| GDD System | Requirement | How This ADR Addresses It | +|------------|-------------|--------------------------| +| technical-preferences.md | Renderer 选择留实现期 ADR;draw calls < 200 | gl_compatibility + 自绘/控件混合渲染在此预算内宽裕 | +| game-concept.md | 视觉锚三条硬约束 | canvas_items stretch 保证数字缩放清晰;Compatibility 2D 管线满足绘制优先级栈 | +| d2-data-config.md | Rule 7 headless 工程债 | dummy DisplayServer 与渲染器解耦(已核实) | + +## Performance Implications +- **CPU**: Compatibility 2D 管线开销最低(重 UI 场景——specialist 核实) +- **Memory**: 三渲染器中最低 +- **Load Time**: 无差异 +- **Network**: 不适用 + +## Migration Plan +切片 project.godot 已写 gl_compatibility——正式工程直接继承;补齐基座尺寸与 stretch 键显式落盘。 + +## Validation Criteria +- draw calls 实测 < 200(D1 集成时用编辑器 profiler 核对) +- 16:9 全屏 / 窗口两种形态下数字可读性目检(视觉锚 1) +- headless 冒烟不受渲染器影响(88/88 维持) + +## Related Decisions +- ADR-0003(场景结构——project.godot 同批落盘)、ADR-0010(B5 战斗舞台,未立项) +- docs/engine-reference/godot/current-best-practices.md §1(新项目默认值差异) diff --git a/docs/architecture/adr-0003-scene-structure-and-board-rendering.md b/docs/architecture/adr-0003-scene-structure-and-board-rendering.md new file mode 100644 index 00000000..0afd7577 --- /dev/null +++ b/docs/architecture/adr-0003-scene-structure-and-board-rendering.md @@ -0,0 +1,138 @@ +# ADR-0003: 场景结构与棋盘渲染(Scene Structure & Board Rendering) + +## Status +Accepted(2026-09-12,solo 模式用户授权代决;迭代期可推翻重议) + +## Date +2026-09-12 + +## Engine Compatibility + +| Field | Value | +|-------|-------| +| **Engine** | Godot 4.7.2-stable | +| **Domain** | Core / Scripting + UI | +| **Knowledge Risk** | LOW-MEDIUM(结构决策 LOW;渲染呈现项 MEDIUM——CanvasItem 线条 AA 羽化移除已核实,godot-specialist 校验含 2 项实质修正后通过) | +| **References Consulted** | `docs/engine-reference/godot/`(四文件);godotengine/godot#6360(--script 无 autoload) | +| **Post-Cutoff APIs Used** | None | +| **Verification Required** | CJK 字体随包发行的显示核对(首次正式导出 checklist 增补项);D1 正式化时若执行 _draw 迁移则另核命中测试/无障碍 | + +## ADR Dependencies + +| Field | Value | +|-------|-------| +| **Depends On** | ADR-0001(组合根接线纪律)、ADR-0002(project.godot 基线同批落盘) | +| **Enables** | 正式工程骨架搭建;D1 正式化 | +| **Blocks** | ADR-0004 扫描域(引用本 ADR 的 core 隔离规则) | +| **Ordering Note** | 棋盘渲染形态裁决(本 ADR §Decision 3)与 ADR-0004 探针耦合——见 Decision 3 的迁移工作项 | + +## Context + +### Problem Statement +正式工程骨架需要定稿:autoload 清单、core/presentation 的类形态与目录结构、棋盘渲染的实现路径。切片已验证一套形态,但初稿 ADR 把"自绘 _draw()"当作切片事实——godot-specialist 校验发现切片 BoardView 实为 **GridContainer + 逐格常驻控件**,本 ADR 按校验修正。 + +### Constraints +- Core 层零 UI 依赖、headless 可跑(D2 Rule 7);--script 模式下 core 禁裸引用 autoload 全局标识符(编译期即炸——见 ADR-0004) +- 同屏 draw calls < 200;棋盘 ≤12×12(MVP 量级)+数字/小人/旗/变异多层叠加(视觉锚绘制优先级栈) +- 领域命名规范(docs/code-conventions.md:禁 ID 前缀、头注溯源) + +### Requirements +- 切片验证过的结构不推倒(用户拍板"按常规老路子") +- 呈现层与核心的写边界:核心写调用唯一发生在组合根 +- 中文文案可显示(运行时默认字体无 CJK 字形——specialist 核实) + +## Decision + +**1. autoload 清单 = {ConfigDB, RngService}(定名锁定),仅此两个。** +战斗编排器等核心类为普通类、由组合根按局实例化——非 autoload、非场景节点。 + +**2. 类形态与目录结构:core/ 纯逻辑类 = RefCounted;ConfigDB/RngService = autoload Node 单例(引擎约束:autoload 脚本必须 extends Node);presentation/ = Control 派生视图单元。** + +``` +res:// +├── core/ # RefCounted 纯逻辑(board_generator / minesweeper_rules / +│ # action_economy / battle_orchestrator / unit_container / +│ # status_container / reward_economy / level_runner / 数据类) +│ # —— 零 Control 引用、零 .tscn preload、零 autoload 全局标识符裸引用 +├── data/ +│ ├── schemas/ # D2 schema 类(Resource 派生——D2 体系,非本 ADR 变更项) +│ └── tables/ # .tres 数值表(单一事实源) +├── presentation/ +│ ├── views/ # 九单元:BoardView / HUDView / BattleLogView / ItemWindowOverlay / +│ │ # NodeOverlay / LevelStripView / TunePanel / UiKit +│ ├── hud_presenter.gd +│ └── main.tscn + main.gd # 组合根:模式流转 + 信号接线 + 核心写调用唯一落点 +├── smoke/ # 冒烟脚本 + 探针场景(ADR-0004) +└── docs/architecture/ +``` + +**3. 棋盘渲染形态:MVP 沿用切片已验证形态——GridContainer + 逐格常驻控件(BoardView 持 `_cells`,refresh 内逐格更新);自绘 `_draw()` 迁移登记为 D1 正式化候选工作项(非本 ADR 承诺)。** + +- specialist 校验事实:切片 BoardView = GridContainer + 逐格 Button(`_paint_cell` 逐格更新),三探针与该形态耦合(ui_probe 断言 `_cells` 非空、click_probe 依赖 `click_cell` 入口)。初稿"_draw 是切片形态的正式化"**不成立**,按事实修正。 +- 形态对比留档:逐格控件(现状)= 命中测试/悬停/焦点由引擎承担、探针兼容;代价 = 控件树随格数增长(12×12=144 控件,实测无压力)。`_draw()` = 单 draw call、绘制优先级栈完全可控;代价 = 自建命中测试/悬停 + **三个探针改造**(`_cells` 断言与 `click_cell` 入口失效)+ 无障碍面缺失 + 需补 `Viewport.push_input` 端到端输入探针。 +- 裁决:D1 正式化 GDD 评审时按当时视觉需求(biome 色板、雾纹理、动效复杂度)二选一;在此之前禁盲目迁移(探针改造是隐藏成本)。任一形态下 CanvasItem 线条 AA 羽化移除(4.7)需注意——格边框用填充 `draw_rect` 不受影响。 + +**4. CJK 字体前置(specialist 补登,两种形态都受影响)**:Godot 运行时默认字体不含 CJK 字形——全部中文文案(按钮/标签/结算明细)必须**随包发行 CJK 字体并设为 theme 默认字体**。登记为 D1 正式化硬性前置项 + 首次导出 checklist 项。 + +### Architecture Diagram +见 `docs/architecture/architecture.md` System Layer Map + Module Ownership(本 ADR 是其工程形态落点)。 + +### Key Interfaces +- 组合根 = 唯一核心写调用落点(settle_batch / submit_window_selection / runner 推进)——切片纪律转正 +- 视图单元 = 信号外发 + setter 只读注入(BoardView 只读 A2、动作信号外发不碰核心写) + +## Alternatives Considered + +### Alternative 1: 战斗编排器设为 autoload +- **Cons**: 编排器持有跨局状态却永生——生命周期与战斗域契约冲突(Uninit/BattleOver 状态机要求按局重建);与"autoload = 全局基础设施"边界冲突。 +- **Rejection Reason**: 切片形态(按局实例化)已验证且语义正确。 + +### Alternative 2: 棋盘 = 每格一个 Node2D +- **Pros**: 独立节点动画/模组方便。 +- **Cons**: 每节点处理开销与 Control/Node2D 混管复杂度;无必要(specialist 指出 2D 同材质有批处理,draw call 论据最弱,但节点开销与混管论据成立)。 +- **Rejection Reason**: 逐格控件已覆盖需求且已验证。 + +### Alternative 3: TileMapLayer 承载棋盘 +- **Cons**: 4.5 物理分块默认变更(纯视觉不踩,但物理用途即触);瓦片语义与"变体标记/数字/旗多层叠加"不匹配。 +- **Rejection Reason**: 语义不匹配是硬伤;现形态无此问题。 + +## Consequences + +### Positive +- 结构与切片零迁移成本;core 隔离规则可静态扫描(ADR-0004) +- 棋盘形态留有 D1 期复议空间且复议成本已如实登记 + +### Negative +- 逐格控件形态在 D1 需要高表现力时可能仍需迁移 _draw(成本已登记:探针改造 + 命中测试自建 + push_input 端到端探针) +- CJK 字体为新增资产义务(发行体积 +) + +### Risks +- D1 正式化时遗漏"探针与形态耦合"约束 → 缓解:本 ADR Decision 3 + ADR-0004 探针节双登记 +- core 类被随手加 autoload 裸引用 → 缓解:ADR-0004 静态扫描(parse 期即炸的事实使该风险自暴露) + +## GDD Requirements Addressed + +| GDD System | Requirement | How This ADR Addresses It | +|------------|-------------|--------------------------| +| d2-data-config.md | Rule 7 零 UI 依赖;Rule 2 单点访问 | core 隔离规则 + autoload 清单固化(ConfigDB/RngService 定名) | +| game-concept.md | 视觉锚(绘制优先级栈、双通道) | 棋盘形态两案对比留档,D1 正式化按锚需求裁决 | +| b1-settlement.md | Core Rule 1 编排器按局生命周期 | 编排器非 autoload、组合根按局实例化 | +| docs/code-conventions.md | 领域命名 / 组合根纪律 | 目录结构与 Key Interfaces 转正 | + +## Performance Implications +- **CPU**: 144 常驻控件逐格更新——切片实测无压力;_draw 迁移后更优(单 draw call) +- **Memory**: 可忽略 +- **Load Time**: CJK 字体载入一次性成本 +- **Network**: 不适用 + +## Migration Plan +切片即目标形态(结构零迁移);CJK 字体接入与 _draw 复议归 D1 正式化批次。 + +## Validation Criteria +- 冒烟 88/88 + 三探针在正式工程维持全绿 +- 静态扫描:core/ 零 Control extends / 零 .tscn preload / 零 autoload 全局标识符裸引用(ADR-0004 域) +- 中文文案在导出包内显示正常(导出 checklist) + +## Related Decisions +- ADR-0001(接线纪律)、ADR-0002(渲染器与窗口键)、ADR-0004(扫描域与探针) +- docs/architecture/architecture.md(Module Ownership / Required ADRs——本 ADR 落定后其 §Required ADRs 第 3 项闭合) diff --git a/docs/architecture/adr-0004-test-carrier-and-ci.md b/docs/architecture/adr-0004-test-carrier-and-ci.md new file mode 100644 index 00000000..501d19e0 --- /dev/null +++ b/docs/architecture/adr-0004-test-carrier-and-ci.md @@ -0,0 +1,132 @@ +# ADR-0004: 测试载体与 CI(Test Carrier & CI) + +## Status +Accepted(2026-09-12,solo 模式用户授权代决;迭代期可推翻重议) + +## Date +2026-09-12 + +## Engine Compatibility + +| Field | Value | +|-------|-------| +| **Engine** | Godot 4.7.2-stable | +| **Domain** | Core / Scripting(测试与工具链) | +| **Knowledge Risk** | LOW(--script/探针模式为 4.x 基线;godot-specialist 校验通过,含两条规则升格) | +| **References Consulted** | `docs/engine-reference/godot/`(四文件);godotengine/godot#6360(--script 模式不加载 autoload + 不注册全局标识符——官方立场) | +| **Post-Cutoff APIs Used** | None | +| **Verification Required** | None(切片 88/88 + 三探针已实证;CI 外部超时在正式 CI 接入时核对) | + +## ADR Dependencies + +| Field | Value | +|-------|-------| +| **Depends On** | ADR-0003(core 隔离规则是扫描域的输入) | +| **Enables** | 全部 GDD AC 的可执行化;正式 CI 接入 | +| **Blocks** | `/gate-check` 前置项之一(`/test-setup` 以本 ADR 为基线) | +| **Ordering Note** | gdUnit4 复议登记为 D2 OQ1 的关闭形态(推迟,非否决) | + +## Context + +### Problem Statement +D2 GDD 定下 `--headless --script` 冒烟载体(脚本 extends SceneTree、autoload 显式实例化),D2 OQ1 留下"gdUnit4 是否引入"待决;切片新增了场景级 UI 探针形态;项目级静态扫描域散落在各 GDD AC(AC-9/17/21/22/43 等)需要一处统一登记。 + +### Constraints +- headless 全套 < 30s 预算(technical-preferences) +- 致命类用例独立进程协议("调用 → 期望退出码" runner manifest,D2 AC-2b 起) +- 呈现层测试必须有承载(切片教训:已定稿 AC 全覆盖 core、呈现层零测试承载 → 缺陷 ①②④⑤ 漏网) + +### Requirements +- 双模式边界明确:逻辑冒烟(无 autoload)vs 场景探针(完整项目引导) +- 静态扫描规则统一载体,覆盖 D2/A1–B4 各 AC 登记的扫描项 +- CI 可接入且防挂死 + +## Decision + +**双载体 + 统一静态扫描域;gdUnit4 推迟到正式 CI 期。** + +**1. 逻辑冒烟(`--headless --script`)**:脚本 extends SceneTree 以获得 `quit(code)`;ConfigDB/RngService 由脚本显式实例化(引擎事实:--script 模式**不加载 autoload 且不注册全局标识符**——裸引用 = parse error 编译期即炸,GH #6360 官方确认)。Windows 用 console 版 exe 取 stdout。承载:D2/A1–B4 全部纯逻辑 AC + 致命类独立进程 manifest。 + +**2. 场景探针(headless 跑 .tscn 探针场景)**:以探针场景为运行目标(`godot --headless --path . res://smoke/.tscn`)走正常项目引导——autoload 在位、真实 SceneTree、`await process_frame` 有效。承载:呈现层场景级测试(切片三探针 ui_probe / ui_click_probe / ui_tune_probe 为先例)。 +- **口径修正(specialist 校验)**:click_probe 走 `BoardView.click_cell` 是"真实翻译出口",**绕过 Viewport GUI 管线**(无命中测试/悬停/焦点)。表述定为"合成输入走真实翻译出口";真正的端到端输入测试 = `Input.parse_input_event()` / `Viewport.push_input` 带坐标——若 ADR-0003 的 _draw 迁移执行(Button 命中测试消失),push_input 探针从加分项变为**必需项**(登记入迁移工作项)。 + +**3. 项目级静态扫描域(统一登记,CI 与冒烟前置双跑)**: + +| # | 规则 | 来源/动机 | +|---|---|---| +| S1 | 玩法代码禁 `randomize()`、自建 `RandomNumberGenerator` | D2 Rule 5(既有) | +| S2 | **禁全局随机函数 `randi()` / `randf()` / `randf_range()`**(全局 RNG 绕开 RngService——specialist 补登漏洞) | D2 Rule 5 补全 | +| S3 | core/ 禁 ConfigDB 外 data 表加载(`load(` / `preload(` / `ResourceLoader.load`;schema class_name 直引豁免) | D2 Rule 2(既有) | +| S4 | core/ 禁 `extends Control/Node2D`、禁 `.tscn` preload | D2 Rule 7(既有) | +| S5 | **core/ 禁裸引用 autoload 全局标识符**(`ConfigDB` / `RngService` 直接写 = script 模式 parse error)——构造注入 only | specialist 升格(切片已合规,规则防复发) | +| S6 | core↔core 信号连接只许方法 Callable(禁 lambda 捕获 self);发射者禁持有订阅者反向引用 | ADR-0001(防循环引用/生命周期延长) | +| S7 | B1 编排唯一性:B2/B3/B4 API 符号命中文件 ⊆ B1 文件集;A2/B3/B4 零相互直调 | B1 AC-43(既有) | +| S8 | 结算路径 `await` 零命中(B1 结算/编排文件集);字面量扫描 = warning 级 + 人工三要素复核(D2 AC-14 口径) | B1(既有) | + +**4. CI 防挂死**:任何探针/冒烟调用旁必须配**外部 wall-clock 超时**(命令行 timeout)——引擎自身无死锁探测;探针内 30 帧停滞检测(click_probe 先例)是第一道、外部超时是最后防线。headless 全套 < 30s 预算不变。 + +**5. gdUnit4:推迟到正式 CI 期再议**(D2 OQ1 以"deferred"关闭)。它运行在项目模式(autoload 在位),与 SceneTree 冒烟机制正交,引入不废弃既有用例。 + +### Architecture Diagram + +``` +提交 → [静态扫描 S1–S8(秒级)] → [逻辑冒烟 --script(独立进程 manifest,<30s)] + → [场景探针 ×N(headless + 外部超时)] → 全绿放行 +``` + +### Key Interfaces +- 冒烟 runner manifest:条目 = "调用 → 期望退出码"(独立进程、游戏通道)——D2 AC-2b 协议延续 +- 探针退出码:0 = PASS;非 0 = 失败类别;停滞 quit(2) + +## Alternatives Considered + +### Alternative 1: 立即引入 gdUnit4 并迁移全部冒烟用例 +- **Pros**: 标准 assertions/参数化/报告;编辑器面板。 +- **Cons**: 88 条用例迁移成本即刻发生;切片框架已验证且快(~3s);MVP 期无 CI 基建需求。 +- **Rejection Reason**: 收益在 CI 期才兑现——推迟(非否决),D2 OQ1 关闭形态。 + +### Alternative 2: 仅保留逻辑冒烟、UI 靠人工 +- **Cons**: 切片缺陷 ①②④⑤ 全部漏过既有 AC 的教训直接否定——呈现层无承载 = 呈现层缺陷零防线。 +- **Rejection Reason**: 呈现层契约测试是 code-conventions 硬性要求。 + +## Consequences + +### Positive +- 双模式边界以引擎事实固化(parse 期炸 vs 完整引导),不再靠约定俗成 +- S2/S5/S6 三条 specialist 补登规则堵住静默翻车路径(全局随机绕开、裸引用编译炸、lambda 成环) +- 呈现层探针形态沉淀为项目标配(code-conventions 承载) + +### Negative +- 静态扫描规则 8 条的维护成本(新规则须挂真实反面案例——code-conventions 纪律) +- "真实 UI 路径"表述收窄后,端到端输入覆盖存在已知缺口(_draw 迁移前可接受) + +### Risks +- 未被停滞检测覆盖的死锁挂死 CI → 缓解:外部 wall-clock 超时为硬性要求 +- 扫描规则漏报(启发式)→ 缓解:行为级硬证据并行(可回放断言、headless 实跑合成判定——D2 AC-9/10 模式) + +## GDD Requirements Addressed + +| GDD System | Requirement | How This ADR Addresses It | +|------------|-------------|--------------------------| +| d2-data-config.md | Rule 6 校验双通道;AC-2b 独立进程协议;OQ1 gdUnit4 | 冒烟框架定稿;manifest 延续;OQ1 关闭为 deferred | +| a1–b1 各 GDD | 各自静态扫描 AC(AC-9/17/18/19/21/22/43 模式) | S1–S8 统一登记为项目级域,各 AC 引用本 ADR | +| docs/code-conventions.md | 场景探针标配 / 提交前全量 | 双载体 + CI 链路承载 | +| b1-settlement.md | AC-43 编排唯一性单向扫描 | S7 载体 | + +## Performance Implications +- **CPU**: 全套冒烟 ~3s(切片实测),预算 <30s 富余 +- **Memory**: headless dummy server 极低 +- **Load Time**: 探针场景引导一次性 +- **Network**: 不适用 + +## Migration Plan +切片 smoke/ 目录即载体(零迁移);正式 CI 接入时补外部超时包装与 S1–S8 脚本化;`/test-setup` 以本 ADR 为基线脚手架。 + +## Validation Criteria +- 冒烟 + 探针全绿为提交门(code-conventions"提交前全量") +- 扫描域脚本化后:新增违规案例必须先补扫描规则再修码(缺陷-规范对照表纪律) +- `<30s` 预算在 CI 环境复核 + +## Related Decisions +- ADR-0001(S6 规则来源)、ADR-0003(S4/S5 规则来源、探针形态耦合) +- docs/code-conventions.md(缺陷-规范对照表)、各 GDD AC 前言(规范源) diff --git a/docs/architecture/adr-0005-save-contract-and-schema-versioning.md b/docs/architecture/adr-0005-save-contract-and-schema-versioning.md new file mode 100644 index 00000000..46d1ef3c --- /dev/null +++ b/docs/architecture/adr-0005-save-contract-and-schema-versioning.md @@ -0,0 +1,104 @@ +# ADR-0005: 存档契约与版本化(Save Contract & Schema Versioning) + +## Status +Accepted(2026-09-12,solo 模式用户授权代决;轻量契约 ADR——实现归 D3 Alpha,本 ADR 只锁契约使 Foundation 层 TR 覆盖闭合) + +## Date +2026-09-12 + +## Engine Compatibility + +| Field | Value | +|-------|-------| +| **Engine** | Godot 4.7.2-stable | +| **Domain** | Core(数据/序列化契约) | +| **Knowledge Risk** | LOW(契约层; FileAccess/JSON 基线稳定) | +| **References Consulted** | `docs/engine-reference/godot/`(四文件);D2 GDD Rule 8 / EC9 / RNG 生命周期 | +| **Post-Cutoff APIs Used** | None | +| **Verification Required** | None(D3 实现期按契约验证) | + +## ADR Dependencies + +| Field | Value | +|-------|-------| +| **Depends On** | None | +| **Enables** | D3 存档序列化(Alpha)——契约先行,实现期免重议 | +| **Blocks** | None | +| **Ordering Note** | 清零 Foundation 层 TR 覆盖缺口(requirements-traceability.md 的最后两条 pending) | + +## Context + +### Problem Statement +Technical Setup → Pre-Production 门要求"Foundation 层需求 ADR 覆盖零缺口"。63 条 TR 基线中 Foundation 层尚有两条无 ADR 承载:TR-d2-010(SCHEMA_VERSION 版本化)与 TR-gc-002(RNG 快照存续契约)。两者的契约在 D2 GDD 已定但散落,未 ADR 化——本 ADR 收拢,D3 Alpha 实现时照契约执行。 + +### Constraints +- MVP 无元进度存档(C4/D3 归 Alpha)——本 ADR 不引入任何 MVP 实现 +- RNG 快照语义已被 D2 GDD(RNG 生命周期)与 B1 GDD(回放切点)锁定——本 ADR 只做契约归集,不改语义 + +### Requirements +- 版本不匹配拒绝读取(高版本拒读,D2 EC9) +- 续玩/复盘可从快照精确恢复随机序列 +- 快照切点与在飞流实例的互斥有明确归属 + +## Decision + +**1. 版本化契约**:`SCHEMA_VERSION` 常量归 D2(既定);存档文件携带该版本号;**比较逻辑归 D3**(D2 EC9 既有分工)——存档版本 > 当前 SCHEMA_VERSION → 拒绝读取 + 明确错误信息;低版本存档的迁移策略归 D3 Alpha 设计时定(候选:拒读 / 迁移器——届时另议,非本 ADR 范围)。 + +**2. RNG 快照契约(从 D2/B1 GDD 收拢为 ADR 登记)**: +- 快照内容 = `master_seed` + 各封闭流 scope 计数器 + 意图序列 + 窗口选择序列 +- **切点规则**:快照切出必须发生在 scope 事件边界之外(无在飞 `open_stream` 实例时);运行期 scope 事件边界 = board install(A1 取号 / B4 取号)——B1 编排保证无在飞实例跨界(消费侧遵守义务,B1 GDD 回放切点条款) +- 违反后果:续玩/复盘与实况分叉(D2 AC-15 语义失效)——该前提由 D3 消费侧遵守,B1 侧编排义务已在位 + +**3. 存档内容归属预告(D3 Alpha 实现的输入清单,非本 ADR 决策)**:冒险结构(C2 域)、关卡进度(C1 域)、HP/库存(B1/B4 关卡域)、代币/天赋(C4 MetaTable)、RAP 不持久(战斗域清零)。D3 GDD 立项时按此清单展开。 + +### Architecture Diagram +(不适用——契约 ADR;数据流见 architecture.md §Data Flow 3) + +### Key Interfaces +- D3 → D2:读取 `SCHEMA_VERSION`(比较方向:存档版本 ≤ 当前才可读) +- D3 → RngService:只读计数器接口(D2 已暴露)+ `set_master_seed`(仅 C2 调用——D3 只读快照、恢复时经 C2 重放接线) + +## Alternatives Considered + +### Alternative 1: 不立 ADR,依赖 D2/D3 GDD 散落条款 +- **Cons**: gate 的 Foundation 零缺口检查失败;契约散落三处(D2 EC9 / D2 RNG 生命周期 / B1 回放切点),D3 实现时需跨三文档拼装。 +- **Rejection Reason**: 收拢成本一次会话内,收益是门检查全绿 + D3 立项输入就绪。 + +### Alternative 2: 现在就设计存档格式(JSON vs 二进制 vs Resource) +- **Cons**: C4/D3 未设计,过早——MVP 无存档需求牵引。 +- **Rejection Reason**: 违背"schema 先行、数值回填"的项目节奏;格式决策留给 D3 GDD。 + +## Consequences + +### Positive +- Foundation 层 TR 覆盖零缺口(门检查全绿) +- D3 立项时的硬输入就绪(版本比较方向、快照内容、切点规则、内容归属清单) + +### Negative +- 无(纯收拢,零新约束) + +### Risks +- D3 Alpha 实现时偏离切点规则 → 缓解:本 ADR + D2 AC-15 / B1 AC-34 双侧断言承载 + +## GDD Requirements Addressed + +| GDD System | Requirement | How This ADR Addresses It | +|------------|-------------|--------------------------| +| d2-data-config.md | Rule 8 版本化;EC9 高版本拒读;RNG 快照生命周期 | 契约收拢 + 版本比较归属(D3)成文 | +| b1-settlement.md | 回放切点契约(快照语义 = seed + 计数器 + 两类输入序列) | 切点规则 ADR 化(无在飞实例时切出,scope 边界 = board install) | +| game-concept.md | 存档/冒险/关卡术语层级(C4/D3 Alpha 范围) | 存档内容归属预告清单 | + +## Performance Implications +- **CPU / Memory / Load Time / Network**: 无(MVP 零实现;Alpha 期存档体积预期 < 1MB 量级) + +## Migration Plan +无迁移(MVP 零实现)。D3 GDD 立项(Alpha 期)以本 ADR 为硬输入。 + +## Validation Criteria +- D3 实现后:同快照恢复 → 随机序列逐位一致(D2 AC-15 / B1 AC-34 形态) +- 版本不匹配路径拒绝读取且错误信息含两侧版本号 +- tests/integration/ 收录存档往返测试(D3 落地时) + +## Related Decisions +- ADR-0001(回放输入序列的载体)、ADR-0004(集成测试载体) +- docs/architecture/requirements-traceability.md(TR-d2-010 / TR-gc-002 覆盖闭合) diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md new file mode 100644 index 00000000..3eac316f --- /dev/null +++ b/docs/architecture/architecture.md @@ -0,0 +1,295 @@ +# Dungeon Sweeper — Master Architecture + +## Document Status + +- Version: 1.0 +- Last Updated: 2026-09-12 +- Engine: Godot 4.7.2-stable(GDScript 单语言;2026-09-07 锁定) +- Review Mode: solo(`production/review-mode.txt`,2026-09-12 用户拍板——TD/LP 门禁按 solo 跳过,用户授权架构产出自主推进、迭代期回调) +- GDDs Covered: game-concept · game-pillars · systems-index · d2-data-config · a3-action-economy · a4-terrain-variation · b3-status-effect · a1-board-generation · a2-minesweeper-rules · b2-enemy-system · b4-reward-item-economy · b1-settlement(12 份;A5/C1–C4/B5/D3–D6 未设计,按 systems-index 依赖分层预留) +- ADRs Referenced: 无(0 份现存)——本文档 Phase 5/6 列出的实现期裁决项即为首批 ADR 候选 +- Implementation Evidence: 垂直切片 `prototypes/dungeon-sweeper-vertical-slice/`(2026-09-10 起,冒烟 88/88、三 UI 探针 PASS)——本文档的分层/所有权/数据流均经切片实现验证,非纯纸面设计 +- Technical Director Sign-Off: 2026-09-12 — APPROVED WITH CONDITIONS(条件:Required ADRs 于正式工程骨架搭建前定稿;`.claude/docs/director-gates.md` 缺失已登记技术债) +- Lead Programmer Feasibility: SKIPPED — Solo mode + +--- + +## Engine Knowledge Gap Summary + +- **风险等级:MEDIUM**(2026-09-12 `/setup-engine refresh` 后由 HIGH 降级)。4.5→4.7 官方迁移指南已逐条核实并入 `docs/engine-reference/godot/breaking-changes.md` 与 `deprecated-apis.md`;**结论:无一条破坏 GDD 已锁架构契约**(autoload 体系 / .tres 单一事实源 / Callable 信号 / int64 二补数 / headless SceneTree 协议均不受影响)。 +- 写码纪律不变:不确定的 API 一律查 `docs.godotengine.org/en/4.7/` 核实后再用;4.8(dev 阶段)稳定版发布后跑 `/setup-engine refresh`。 +- 三类必须入编码纪律的引擎事实(详见 `docs/engine-reference/godot/current-best-practices.md`): + 1. **GDScript 4.7 两条语义变化**:打包数组元素赋值不再触发属性 setter;类型化返回的方法覆盖必须显式 `return`。 + 2. **新项目默认值已变**:stretch mode `canvas_items` / aspect `expand`(4.7);Windows 渲染驱动 D3D12(4.6,仅 Forward+ 线)——创建 project.godot 后逐项核对。 + 3. `Resource.duplicate(true)` 深复制不再覆盖外部子资源——D2 表资源复制语义注意。 +- 残余风险敞口:modules/ 分域参考未建(按实现期需要建,非阻塞);UI/渲染域(D1/B5)为 MEDIUM 风险集中地,随 ADR-0002/0003 裁决时对照引擎文档核实。 + +--- + +## System Layer Map + +20 系统按 systems-index 依赖分层表(systems-design 门禁已评审)映射到标准五层。切片实现已验证该分层(core/ 纯逻辑零 UI 依赖 headless 可跑、presentation/ 组合根模式)。 + +``` +┌────────────────────────────────────────────────────────────┐ +│ PRESENTATION LAYER │ +│ D1 HUD 与界面框架 (MVP) · B5 战斗表现 (切片后) │ +│ D4 音频 (Alpha) · D5 教学引导 (Alpha) │ +├────────────────────────────────────────────────────────────┤ +│ FEATURE LAYER │ +│ B1 战斗结算编排 (MVP) · C1 关卡节点地图 (切片已含 lite) │ +│ C2 冒险结构 · C3 难度曲线 · C4 元进度 (Alpha) │ +├────────────────────────────────────────────────────────────┤ +│ CORE LAYER │ +│ A1 扫雷棋盘生成 · A2 排雷玩法规则 · B2 敌人系统 │ +│ B4 奖励与道具经济 · A5 扫雷辅助道具 (垂直切片) │ +├────────────────────────────────────────────────────────────┤ +│ FOUNDATION LAYER │ +│ D2 数据配置与数值表 · A3 行动经济 · A4 地形变异 │ +│ B3 增益/状态系统 · D3 存档序列化 (Alpha) · D6 设置/本地化│ +├────────────────────────────────────────────────────────────┤ +│ PLATFORM LAYER │ +│ Godot 4.7.2 引擎 API 面(GDScript / Resource / │ +│ SceneTree / Control / InputEvent)——无游戏系统 │ +└────────────────────────────────────────────────────────────┘ +``` + +| 系统 | 层 | 里程碑 | headless 约束 | 引擎风险域 | +|---|---|---|---|---| +| D2 数据配置 | Foundation | MVP | ✅ 完整可跑 | LOW(Resource/.tres 基线稳定) | +| A3 行动经济 | Foundation | MVP | ✅ 完整可跑 | LOW(纯逻辑) | +| A4 地形变异 | Foundation | MVP | ✅ 完整可跑 | LOW(纯逻辑) | +| B3 状态系统 | Foundation | MVP | ✅ 完整可跑 | LOW(RefCounted 纯逻辑) | +| A1 棋盘生成 | Core | MVP | ✅ 完整可跑 | LOW(纯逻辑;性能姿态归 ADR-0005) | +| A2 排雷规则 | Core | MVP | ✅ 完整可跑 | LOW(零表读取零 UI) | +| B2 敌人系统 | Core | MVP | ✅ 完整可跑 | LOW(纯逻辑) | +| B4 奖励经济 | Core | MVP | ✅ 完整可跑 | LOW(纯逻辑) | +| B1 战斗结算编排 | Feature | MVP | ✅ 完整可跑(禁 await) | LOW(纯逻辑;事件载体归 ADR-0001) | +| C1 关卡节点地图 | Feature | 垂直切片(lite 已入切片) | ✅(切片 level_runner 先例) | LOW | +| C2 冒险结构 | Feature | 垂直切片 | ✅ | LOW | +| C3 难度曲线 | Feature | 垂直切片 | ✅ | LOW | +| C4 元进度 | Feature | Alpha | ✅ | LOW | +| D1 HUD | Presentation | MVP | ❌(UI 层——场景级探针测试承载) | **MEDIUM**(Control/Input/文本域) | +| B5 战斗表现 | Presentation | 垂直切片 | ❌ | **MEDIUM**(Animation/Tween/渲染) | +| D4 音频 / D5 教学 | Presentation | Alpha | 部分 | LOW-MEDIUM | +| D3 存档序列化 | Foundation | Alpha | ✅ | LOW | +| D6 设置/本地化 | Foundation | 完整愿景 | ✅ | LOW | + +**MVP 关键路径**(切片已验证):D2 → A3/A4/B3 → A1 → A2 → B2/B4 → B1 → D1-lite。 +**依赖分层与唯一回边**:A2 只外发三事件(cell_revealed / flag_judged / round_end),B4/B1 订阅后回调——A2 不依赖任何下游(TD-SYSTEM-BOUNDARY 裁决,全项目唯一允许回边)。 + +--- + +## Module Ownership + +> 命名遵循 `docs/code-conventions.md` + CLAUDE.md 代码规范:领域命名、禁 ID 前缀、头注溯源行(实现 GDD:design/gdd/…)。切片已按此规范完成领域命名批。 + +### Foundation 层 + +| 模块 | Owns(独占) | Exposes | Consumes | 引擎 API | +|---|---|---|---|---| +| **ConfigDB**(autoload,D2) | 全部玩法数值表(`data/tables/*.tres` + `data/schemas/*.gd`);SCHEMA_VERSION;加载/校验/override/诊断状态机 | 类型化属性读取;`load_tables(root, collect_only)`;`resolve()`(R1 floor);debug override;`set_reload_gate()`;诊断收集器 | 无(Foundation 顶点) | Resource/.tres 加载、autoload 生命周期——LOW | +| **RngService**(autoload,D2) | master_seed、各流 scope 计数器(封闭流:board_gen / reward_roll;增流走 P1 评审) | `set_master_seed()`(唯一调用方 C2);`open_stream(stream)` 有状态门面;`derive_at(stream, scope)` 纯函数;只读计数器 | ConfigDB(无) | 无——纯逻辑 | +| **action_economy**(A3) | AP/RAP 账本、last_round_end、轮次相位机 {Idle, Active, Waiting}、结束仲裁 | 8 API 全布尔面:notify_round_start / request_dig / request_flag_place / request_flag_pull / request_stop / request_refresh / notify_settlement_done / notify_combat_end;ap/rap/phase 只读 | ConfigDB(ActionEconomyTable);B1 回发 next_round_mode/battle_over | 无 | +| **variation 定义**(A4) | VariationTable schema 与 MVP fog 行、F1 候选池门控谓词 | `variant_pool_gate(level_index, board_biome)` | ConfigDB(VariationTable) | 无 | +| **status_container**(B3) | 状态实例容器(战斗域生命周期、跨轮跨图延续) | 5 API:notify_battle_start / apply_effect / query_active / decay_round / notify_battle_end | ConfigDB(StatusEffectTable) | 无(RefCounted) | +| **save_system**(D3,Alpha) | 存档格式与版本比较 | (未设计——契约锚:SCHEMA_VERSION + master_seed + 各流 scope 计数器快照,须在无在飞 open_stream 实例时切出) | ConfigDB、RngService 只读计数器 | FileAccess——LOW | +| **settings/localization**(D6,愿景) | 设置项、翻译数据 | (未设计) | ConfigDB | 无 | + +### Core 层 + +| 模块 | Owns | Exposes | Consumes | 引擎 API | +|---|---|---|---|---| +| **board_generator**(A1) | 生成算法(五阶段管线)、BoardGenTable 回填、八项生成期指标、合法性谓词(clearable / R₀≠S / min_safe)、可推理率度量(metric_version) | `generate(L, D, rng) -> Board`;度量独立入口×3(推理器 / F3 度量 / 合法性谓词——独立纯函数、同一实现) | ConfigDB(BoardGenTable/VariationTable);board_gen 流(一次 open_stream = 一张图);dig_ap_cost 只读 | 无 | +| **minesweeper_rules**(A2) | 棋盘状态(已开集 O / 旗集 G / 小人位)、三事件面、批次仲裁、install 四不变式 | `install_board(board)`;`settle_batch(intents)`(唯一提交入口);try_dig / try_flag_place / try_flag_pull / try_move(便捷形);`compute_move_path(cell)` 只读;`retire_board()`;三源拒绝(board_face / resource_face / phase)+ sub_reason 封闭枚举 | A1 产物(Board class_name 数据契约);A3 try_spend / last_round_end / 相位 | 无(零表读取、零 UI——切片实证) | +| **unit_container**(B2) | hero/enemy 单位实例(hp/atk/行为环指针)、行值快照 | 9 API:spawn / query_alive / query_beat / query_attack / apply_damage / advance / default_target / ambush_grant_plan / despawn_all | ConfigDB(EnemyTable/HeroTable) | 无 | +| **reward_economy**(B4) | 板级身份映射(install 期预分配、Ready 只读)、关卡域库存(逐件实例)、**RewardSupplyController**(切片新增私有组合件:注入占比 + 指数递减、行值快照、零自有随机) | 10 API:roll_board / query_identity / grant / grant_clear / stock / use / query_inventory / clear_inventory / retire_mapping / equivalence_supply;另 query_full_snapshot(轮内刷新 facts 隔离)、grant_node_active(C1-lite 接口漂移) | ConfigDB(RewardTable/RewardConfigTable);reward_roll 流(scope = 每次 install 批) | 无 | +| **helper_items**(A5,垂直切片) | HelperItemTable、探测显影语义 | (未设计——桥接位:A2 查询接口 + A4 counter_item_ids) | A2 / A4 / B4 | 无 | + +### Feature 层 + +| 模块 | Owns | Exposes | Consumes | 引擎 API | +|---|---|---|---|---| +| **battle_orchestrator**(B1) | 段序骨架(七段结构冻结)、轮 facts 缓冲、可回放步骤流、明细行协议(13 类行 + 字段分层)、条款③⑤闭合对账、**战斗域全部跨系统调用权** | `begin_battle(encounter)`;窗口/刷新意图提交面(扮演 D1);`level_clear()`;步骤流(输出契约) | B2/B3/B4 全调用面(**唯一调用方**);A3 request_refresh 代理 + 回发;A2 install/retire + 三事件订阅;A1 generate(attempt 非 scope 事件);D2 SettlementTable + set_reload_gate | 无(零随机、禁 await——切片 88 冒烟实证可回放) | +| **level_runner**(C1-lite,切片已建) | 关卡域生命周期(节点链推进、共享 B4 + HP 携带、关卡末 level_clear) | 节点推进接口;LevelTable(`level.tres`——节点 enum {battle,reward,rest,boss}、末节点必须 Boss、ConfigDB 跨表校验⑧) | B1 begin_battle、B4 grant_node_active、ConfigDB LevelTable | 无 | +| **run_structure**(C2) | 冒险/关卡序列、失败重开、**master_seed 唯一接线方** | (未设计) | RngService.set_master_seed、C3/D2 | 无 | +| **difficulty**(C3) | DifficultyTable 回填、全部曲线数值调参所有权、战斗期望表(切片 monte_carlo 为工具原型) | 调参输出(经 D2 R1 解析/编排器传递,禁反向调用生成) | ConfigDB、B1 对账输出(调参约束清单)、A1 报告 C | 无 | +| **meta_progression**(C4,Alpha) | MetaTable、代币/天赋 | (未设计) | D3、C2、ConfigDB | 无 | + +### Presentation 层 + +| 模块 | Owns | Exposes | Consumes | 引擎 API | +|---|---|---|---|---| +| **D1 HUD**(切片 lite 已拆 9 单元:BoardView / HUDView / HudPresenter / BattleLogView / ItemWindowOverlay / NodeOverlay / LevelStripView / TunePanel / UiKit + 组合根 main.gd) | 全部 Control/CanvasLayer、输入→意图翻译(**唯一提交路径 = settle_batch,不直调 A3**)、呈现义务台账(A2×6 / B2×4 / B3×4 / B4×5 / B1×3 / A4×2) | 视图单元接口(信号外发、setter 只读注入) | A2 compute_move_path / ActionOutcome;A3 ap/rap;B1 步骤流行协议(数据源);ConfigDB display_name | **MEDIUM**:Control / CanvasLayer / draw_* / InputEvent / RichTextLabel——对照 breaking-changes.md 4.5–4.7 条目(CanvasItem 线条无抗锯齿羽化、设备 ID 常量、RichTextLabel 图片参数两轮变化) | +| **battle_presentation**(B5,垂直切片后) | 结算动画 / 伤害数字 / 战斗舞台 | (未设计——前置"战斗表现美术测试"锁双舞台陷阱) | B1 步骤流(回放脚本源)、D1 | **MEDIUM**:AnimationPlayer(String→StringName 变更)/ Tween | +| **audio**(D4,Alpha) | 音频挂点 | (挂点 = B1 明细行类型流;负反馈聚合——单结算至多一次) | B1 流水 | AudioStreamPlayer(4.7 area_mask 默认 1→0 注意) | +| **tutorial**(D5,Alpha) | 教学条目 | (口径来源:A2/A3/A4/B3/B1 各教学条目) | 各上游 | 无 | + +**依赖单向性**:上层可消费下层,禁止反向(C3 禁调用 A1 生成、D1 不直调 A3、A2/B3/B4 零相互直调)。全项目唯一回边 = A2 事件订阅面。 + +--- + +## Data Flow + +### 1. 帧更新路径(2D 回合制:无逐帧玩法逻辑) + +``` +Input(鼠标)→ D1 输入翻译 → 意图对象 → A2.settle_batch(唯一提交入口) + → A3.try_spend(原子扣减)→ A2 棋盘效果(挖/连锁/旗/移动)→ 三事件外发 + → [棋盘渲染:D1 BoardView 按事件增量重绘;无事件帧 = 静态 + 动效] + → 轮次结束 → A3 Waiting → B1 结算编排(段序七段)→ 明细步骤流 → D1 结算屏逐行呈现 +``` + +- 回合制下每帧只做:输入采样、呈现刷新、动效推进——**玩法状态只在意图事务内变更**(单意图帧输入收集:stop 点击终结当帧)。 +- 无跨线程玩法数据流(Godot 主线程假设,D2 EC13);A1 生成若引入 WorkerThread 须走 ADR-0005 裁决(join 先于任何快照切出)。 + +### 2. 事件/信号路径(系统间通信,无紧耦合) + +``` +A2 ──cell_revealed / flag_judged / round_end(棋盘路径)──▶ B1(轮 facts 缓冲) +A3 ──round_end(stopped 直发,payload 同构)──────────────▶ B1 +B1 ──notify_settlement_done(next_round_mode) / notify_combat_end──▶ A3 +B1 ──apply_effect / apply_damage / grant / stock / advance...──▶ B3/B2/B4(唯一调用方) +B1 ──install_board / retire_board──▶ A2 B1 ──generate(L,D)──▶ A1 +D1 ◀──呈现数据源:A2 事件流 / B1 步骤流 / A3 ap-rap / ConfigDB display_name +``` + +- **载体裁决待 ADR-0001**:切片事实 = 组合根直连信号接线 + B1 Waiting 相位触发(不依赖事件到达序);推荐沿用(单线程回合制,KISS),autoload 全局事件总线为否决倾向。 +- 事件规范序:A2 发射序 = 触发格 BFS、8 邻行主序;事件序契约 `flag_judged` < `round_end` 全路径统一。 + +### 3. 存档/读档路径(MVP 无存档;契约已锁) + +- MVP 无元进度持久化(C4/D3 归 Alpha)。**已锁契约**:续玩/复盘快照 = master_seed + 各流 scope 计数器 + 意图序列 + 窗口选择序列;快照切出须发生在无在飞 `open_stream` 实例时(scope 事件边界外——B1 编排保证:切点 = board install 边界)。 +- 可回放性已实证:切片冒烟同种子双跑步骤流逐行相同(B1 AC-33 前身)。 +- D3 Alpha 期按此契约设计格式;SCHEMA_VERSION 高版本拒读。 + +### 4. 初始化顺序 + +``` +进程启动 + → autoload 实例化:ConfigDB → RngService(顺序固定;战斗编排器为普通类、由组合根按局实例化——切片形态) + → ConfigDB._ready():load_tables 加载 + 校验(致命通道;失败 = 构建中止 + 错误清单) + → 主场景(D1 组合根)装配视图单元 + 信号接线 + → C2 begin_run:RngService.set_master_seed()(唯一接线方;两流计数器全零前提) + → B1.begin_battle(encounter):set_reload_gate(true) → B3 开域 → B2 spawn → A2 install_board(A1.generate) + B4 roll_board(恰一次)→ A3 notify_round_start + → 挖掘循环 …… → battle_over 清理序列 → set_reload_gate(false) + → 关卡末:level_clear()(clear_inventory 恰一次 + 快照计数) +``` + +--- + +## API Boundaries + +> 完整 API 面(含封闭枚举负空间、布尔受理口径、输入×相位矩阵)以各 GDD AC 前言为**规范源**——本节列边界契约与不变式,实现须与 GDD 逐字一致;冲突时以 GDD 为准并走接口漂移修订。 + +| 边界 | 契约要点 | 调用方必须遵守的不变式 | 模块保证 | +|---|---|---|---| +| D1 → A2 | 意图经 `settle_batch(intents)` 提交;不直调 A3 | 单意图帧收集;stop 经批次委托 | 语义序仲裁 + voided 零副作用 | +| A2 → A3 | 挖/插/拔 = 先棋盘面判定后 `try_spend`;A2 不可自行扣减 | 相位判定两侧一致(同一矩阵) | 原子事务(余量+扣减+仲裁一体) | +| A2 → 下游 | 只外发三事件,payload 字段封闭(digit 恒真透传 / reward_eligible / variant_ids) | 增删事件 = 接口漂移 | 发射面封闭 + 规范序 | +| B1 → B2/B3/B4 | 全调用面集中 B1;调用序钉死(query_attack→apply_damage→advance;先兑付后衰减) | 禁第二调用方(静态扫描域:API 符号命中文件 ⊆ B1 文件集) | 恰一次编排纪律(roll_board/decay/清理全序) | +| B1 ↔ A3 | 编排期代理 `request_refresh`(玩家意图,禁 RAP 满自动刷);回发 next_round_mode/battle_over | stopped/ap_exhausted 未刷新回发 new_board = 违约 fail-fast | 刷新受理契约(同 Waiting 多次合法) | +| 各系统 → D2 | ConfigDB 类型化属性单点读取 | 禁绕过 ConfigDB load 表、禁缓存副本、禁玩法数值字面量、禁 randomize()/自建 RNG | 校验双通道 + fail-fast 致命 | +| A1 → A2 | Board 产物 class_name 数据契约 | install 四不变式校验(violation = 拒绝 + 五类诊断枚举) | 生成期合法性闸(R₀≠S / clearable / min_safe) | +| A1 ↔ B1 | attempt 边界非 scope 事件;actuals p10 为对账输入(非 target 参数) | 编排不把重试当取号 | 八项指标输出(含 fog_burden_ratio) | +| B4 → B3 | buff 类经 B1 调 apply_effect(B4 零跨系统读、零直接接口) | 满层拒绝 = 零兑付 + 明细行(无折算) | 授予时数值快照(抗热重载) | +| B1 → D1 | 步骤流行协议(13 类封闭枚举 + 公共字段 type/source_class/origin_cell/round_index/display_name/value/highlight? + 特有字段 target/effect_id/pool_value) | 无来源行 = 断言失败;呈现侧禁按行序重演 B2 状态机 | 可回放步骤流(同种子逐行相同) | + +**引擎类型核验注记**:接口使用的引擎类型 = Resource / .tres(D2 全表)、RefCounted(容器类)、Signal/Callable(事件载体,ADR-0001)、Control/CanvasLayer/Drawing(D1)、SceneTree headless 协议(测试)——均属 4.x 基线稳定面;4.7 Control 域增量(offset_transform_* 等)已在 breaking-changes.md 登记,D1 采纳前对照核实。 + +--- + +## ADR Audit + +**现存 ADR:0 份**(`docs/architecture/` 本文档为首个产物;此前架构约束由 GDD 门禁评审 + CLAUDE.md + code-conventions 事实承载)。 + +### 已锁决策盘点(GDD 层已定稿,不重复立项 ADR) + +| 决策 | 锁定来源 | +|---|---| +| GDScript 单语言;autoload = ConfigDB + RngService(定名勿改) | technical-preferences + D2 GDD | +| 全部玩法数值 .tres 单一事实源;零字面量;R1 floor 解析;fail-fast 双通道 | D2 Core Rules 1–8 | +| RNG 封闭流 + scope 派生(R2 黄金向量);可回放 = 硬承诺 | D2 R2 + B1 Core Rule 15 | +| A2 纯规则引擎(零随机/零表读取/零 UI)+ 三事件面封闭 | A2 Core Rule 1/2 | +| B1 编排唯一性 + 七段段序骨架 + 明细行协议 | B1 Core Rule 1/4/14 | +| 多敌 side-wide / 行为环确定性 / 固定顺位 / 伏击 cap=4 | B2(2026-09-09 用户拍板) | +| 奖励五值两类交付轴 / install 预分配 / 关卡域库存 / 窗口单件制 | B4(2026-09-11 用户裁决批) | +| weake­n 锚 F0 瞬时当量 / 条款①③⑤闭合对账 owner = B1 | B1 F0–F5 | +| 领域命名禁 ID 前缀 / 组合根模式 / 呈现层契约成文 | CLAUDE.md + docs/code-conventions.md(切片教训批) | + +### GDD 登记的"实现期 ADR 裁决项"汇总(= Required ADRs 的来源) + +A2 OQ2(事件外发载体)· B1 OQ1(事件总线载体,与 A2 OQ2 同一裁决)· A1 OQ2/OQ3(生成性能姿态 + 度量独立入口)· B2 OQ1(query_attack/ambush_grant_plan 两 face 归属)· B2 OQ5(HeroTable 单行校验落点)· B2 OQ9(零伤害死局跨字段护栏落点)· D2 OQ1(gdUnit4 取舍)· technical-preferences(渲染器选择)· 切片新增正式化批(轮内刷新方案 A / RewardSupplyController / LevelTable / grant_node_active / clear_blast——GDD+registry 修订义务在切片 README)。 + +### Traceability Coverage Check + +Technical Requirements Baseline 63 条 → ADR 覆盖 0 条(无 ADR 存在);按覆盖载体分层: + +| 覆盖载体 | TR 数 | 说明 | +|---|---|---| +| ✅ 本架构文档(分层/所有权/数据流/API 边界)+ GDD 契约 | 46 | 数据/RNG/核心行为/编排协议类——已锁且切片实证,无需 ADR | +| 🔶 待 ADR 裁决 | 12 | 引擎/测试/实现形态类:TR-gc-008(渲染器)、TR-a1-011/012(生成姿态)、TR-d2-014/016(测试载体)、TR-a3-004+TR-b1-001/002(事件载体)、TR-b2-005/006(face 归属)、TR-d2-010+TR-gc-002(D3 契约正式化) | +| ⏳ 未设计系统预留 | 5 | A5/C1–C4/D3 关联 TR(随各系统 GDD 立项) | + +**结论:0 条孤儿需求**——46 条由本文档+GDD 承载、12 条待 ADR(见下)、5 条随未设计系统。**2026-09-12 更新:ADR-0001~0004(Foundation 四项)已落定 → 正式工程骨架解冻**;ADR-0005~0008 随对应系统开工前裁决(切片代码可作为参考实现,生产化迁移待 ADR-0008 正式化批)。 + +--- + +## Required ADRs + +### 必须在正式编码开始前(Foundation 裁决)——✅ 已全部落定(2026-09-12,solo 授权代决,godot-specialist 校验通过) + +1. **✅ ADR-0001 `event-bus-and-orchestration-carrier`**(`adr-0001-event-bus-and-orchestration-carrier.md`):组合根直连 signal 接线 + B1 Waiting 相位触发;autoload 全局事件总线否决。specialist 增补两条静态规则(方法 Callable only / 发射者禁反向引用)。 +2. **✅ ADR-0002 `renderer-and-window-config`**(`adr-0002-renderer-and-window-config.md`):gl_compatibility(绕开 D3D12 新驱动线);基座 1280×800 + canvas_items + expand 显式落盘。 +3. **✅ ADR-0003 `scene-structure-and-board-rendering`**(`adr-0003-scene-structure-and-board-rendering.md`):autoload 仅 {ConfigDB, RngService};core RefCounted / presentation 九单元组合根;**棋盘渲染 MVP 沿用切片逐格控件形态**(specialist 纠正:切片并非自绘 _draw——迁移登记为 D1 候选工作项含探针改造);CJK 字体随包为硬性前置。 +4. **✅ ADR-0004 `test-carrier-and-ci`**(`adr-0004-test-carrier-and-ci.md`):双载体 + 静态扫描域 S1–S8(specialist 增补 S2 全局 rand* 禁令 / S5 core 禁裸引用 autoload 标识符)+ CI 外部超时;gdUnit4 deferred(D2 OQ1 关闭形态)。 + +实现级立场已登记 `docs/registry/architecture.yaml`。 + +### 相关系统开工前应有 + +5. `/architecture-decision "board-generation-performance"` → ADR-0006:A1 生成性能与异步化(F2c 常数 C 定稿;切片证据 = 10×10/12×12 同步生成可接受——推荐 MVP 同步+加载幕,WorkerThread 留扩展);度量独立入口确认(切片已实现三入口)。 +6. `/architecture-decision "unit-api-face-ownership"` → ADR-0007:B2/B1 两 face 归属(query_attack / ambush_grant_plan)+ HeroTable 单行校验落点。 +7. `/architecture-decision "zero-damage-guard-locus"` → ADR-0008:零伤害死局跨字段/跨表护栏落点(推荐落 D2 跨表校验——切片 LevelTable 校验⑧先例)。 +8. `/architecture-decision "slice-mechanism-formalization"` → ADR-0009:切片新增机制正式化批(轮内刷新方案 A / RewardSupplyController / LevelTable / grant_node_active / clear_blast)——GDD+registry 接口漂移修订的 ADR 化登记。 + +### 已落定(2026-09-12 追加) + +0. **✅ ADR-0005 `save-contract-and-schema-versioning`**(`adr-0005-save-contract-and-schema-versioning.md`):Foundation 层收口——SCHEMA_VERSION 比较归属 + RNG 快照切点契约 ADR 化(TR-d2-010 / TR-gc-002),Foundation 零缺口闭合。 + +### 可推迟 + +- ADR-0010 A1 EC5 运行期熔断(连续降级确定性回退已知好种子) +- ADR-0011 B5 战斗舞台(棋盘上 vs 独立场景)——"战斗表现美术测试"后裁决 +- ADR-0012 A5 探测显影接口 +- ADR-0013 D3 存档格式(RNG 快照契约已由 ADR-0005 锁定,格式归 Alpha) +- C2/C3/C4 结构性决策随各系统 GDD 立项 + +--- + +## Architecture Principles + +1. **Core 纯逻辑、headless 完整可跑**——Core/Feature 玩法逻辑零 Control/场景引用、零 UI 依赖;`--headless --script` 可跑完整战斗(D2 Rule 7 工程债;切片 88 冒烟实证)。输入是意图对象、输出是事件流/步骤流,D1 与输入设备皆可替换。 +2. **单一事实源与零字面量**——玩法数值只存在于 `data/tables/*.tres`,经 ConfigDB 类型化单点读取;代码零玩法数值字面量;参数禁止跨系统复制(D2 Rule 1/2)。 +3. **封闭 RNG 与可回放**——玩法代码零随机;一切随机经 RngService 具名封闭流(增流走 P1 评审);同种子 + 同输入序列 ⇒ 事件流/步骤流逐位相同(结算禁 await,B1 承重约束)。 +4. **编排唯一性与事件面封闭**——战斗域跨系统调用权集中 B1(静态扫描承载);A2 三事件面封闭即负空间;每格受理、每雷转化、每次推进、每次衰减各恰一次。 +5. **fail-fast + 零特判**——数据错误一律致命中止(错误数值上线比崩溃更伤);API 非法 = 拒绝 + 诊断不静默;变异零运行时分支(行为差异全部由生成端约束保证)。 +6. **依赖单向 + 组合根模式**——层间单向依赖、唯一回边 = A2 事件订阅面;核心写调用唯一发生在组合根;禁上帝对象(切片 1100 行 main.gd 反面案例 → 9 单元拆分)。 +7. **呈现层契约成文**——面向呈现的信号载荷必须有成文 schema;模态覆盖层必须有"打开时遮挡物隐藏"场景级断言;场景级 UI 探针测试为标配(切片缺陷 ①②④⑤ 教训沉淀)。 + +--- + +## Open Questions + +| ID | Summary | Priority | Resolution Path | +|----|---------|----------|-----------------| +| QQ-01 | 渲染器选择(Forward+ vs gl_compatibility)与项目窗口基线 | High | ADR-0002(正式骨架前) | +| QQ-02 | 事件总线载体与 A2 事件捕获测试 API | High | ADR-0001(正式骨架前) | +| QQ-03 | gdUnit4 是否引入并迁移冒烟用例 | Medium | ADR-0004(可推迟到正式 CI 期;D2 OQ1) | +| QQ-04 | 轮内刷新数值连锁(清图支配性条款⑤ / 真人刷图行为获取期望)重算 | Medium | playtest 实测 + ADR-0008 正式化批 | +| QQ-05 | 战斗末可选类"j==0 补发 1 件"兜底是否采纳(切片遗留待拍板) | Medium | playtest + B4 GDD 修订批 | +| QQ-06 | 可选类退出格子池后 buff 频率被动上升(44%→59%)的难度对冲杠杆(敌 HP / 权重 / 幅度) | High | C3 调参 + 切片数据复测 | +| QQ-07 | 标错惩罚(A2 OQ1:候选敌方伏击+1;hit=false 无去重的不对称裁决) | Medium | 原型小补丁实测会话(可与 v3 终验合并) | +| QQ-08 | B5 双舞台陷阱(战斗发生在棋盘上还是独立场景) | Low(B5 前) | 战斗表现美术测试 → ADR-0010 | +| QQ-09 | C3 消费 B1 对账输出的事实依赖回写 systems-index | Low | C3 GDD 定稿时 | +| QQ-10 | `.claude/docs/director-gates.md` 缺失(TD-ARCHITECTURE/LP-FEASIBILITY 判据无载体) | Low | 补建 gate 定义文件或维持 solo/lean 模式约定 | diff --git a/docs/architecture/code-architecture-review-2026-09-13.md b/docs/architecture/code-architecture-review-2026-09-13.md new file mode 100644 index 00000000..faa7e9c4 --- /dev/null +++ b/docs/architecture/code-architecture-review-2026-09-13.md @@ -0,0 +1,132 @@ +# 代码架构审查报告 — 代码组织与封装 + +> 日期:2026-09-13 +> 载体:`prototypes/dungeon-sweeper-vertical-slice/`(9707 行 GDScript:core 12 文件 / data 11 / presentation 28 / smoke 10) +> 基线:`docs/architecture/architecture.md` v1.0 + ADR-0001~0005 + `docs/code-conventions.md` +> 触发:用户在 code-review 之后要求补充「代码组织问题 + 封装问题」专项审查 +> 审查方式:core/presentation 关键文件全读 + S1–S8 静态规则逐条 grep 验证 + 跨层引用面扫描 +> +> **Verdict: CONCERNS** — 分层纪律实测扎实(S1–S7 零违例、仅 2 处已登记例外),但封装收口存在 +> 一条系统性欠账:核心模块内部状态以公有字段暴露,呈现层已形成链式穿透的既成事实。 +> 原型期无害;是 ADR-0009「切片机制正式化批」应收口的主题。 + +--- + +## 1. 正面观察(实测合规项) + +| 规则 | 结论 | 证据 | +|---|---|---| +| S1/S2 零随机 | ✅ | core 内唯一 `RandomNumberGenerator.new` 在 rng_service.gd:76(所有者本体);`stream.randf()`(reward_supply_controller.gd:32)取号经 B4 注入封闭流 | +| S3 零表加载 | ✅ | core 内唯一 `load()` 在 config_db.gd:81(单点本体) | +| S4 core 零 UI | ✅ | core/ 无 Control/Node2D/.tscn 引用(grep 命中均为 `RewardSupplyController` 类名误报与注释) | +| S5 零裸引用 autoload | ✅ | core/ 内 ConfigDB/RngService 命中均为日志前缀字符串 | +| S6 core↔core 方法 Callable | ✅ | level_runner.gd:130-134 全部方法转发;lambda 仅用于 sort_custom(不捕获 self) | +| S7 编排唯一性 | ✅ | B2/B3/B4 调用面 core 内收口于 battle_orchestrator.gd;仅 level_runner.gd:112(grant_node_active,已登记接口漂移)与 :168(in_stock_count 只读)两处例外 | +| 呈现层零核心引用 | ✅ | 28 个视图/装配器文件中仅 main.gd(组合根)与 hud_presenter.gd(授权翻译点)触 core | +| 封装典范 | — | status_container / reward_supply_controller / b4.query_inventory():私有状态 + 投影查询 + 授予时快照;battle_orchestrator.diagnostic_snapshot()(:660)为「成文只读出口」正确先例 | + +--- + +## 2. 封装问题(按根因 → 表征排序) + +### E1(根因)编排器协作对象全公开,呈现层链式穿透已成既成事实 — P1 + +- **位置**:core/battle_orchestrator.gd:20-27(`var a1/a2/a3/b2/b3/b4` 全公有) +- **表征**:`b1.a2.board`、`b1.b2.hero.hp`、`b1.a3.phase` 式层层下钻——main.gd 18 处、hud_presenter.gd 15 处。 +- **影响**:组合根与 presenter 是授权翻译点,但访问形态是「恰好字段公开」而非「B1 提供成文读取面」。 + S7 只能做文件级扫描(防另写调用方),防不了呈现层意外调用写 API,也防不了读取路径随重构漂移。 +- **建议**:B1 补聚合呈现快照/查询面(`hud_state()` 一类,先例 = `diagnostic_snapshot()`); + a2–b4 下划线化后经命名代理暴露只读操作。 + +### E2(表征)视图绕过 A2 查询面直读原始状态字典 — P1 + +- **位置**:core/minesweeper_rules.gd:18-20(`board/revealed/flags` 公开可变); + presentation/views/board_view.gd:278、:303 直接 `_a2.revealed.get(c, {})` +- **矛盾点**:board_view.gd:7 头注自称「只读 A2 查询面」——直读 `.revealed` 恰是绕过查询面。 + B1 自身亦直读(battle_orchestrator.gd:565-569 `a2.board.width/height`)。 +- **建议**:`revealed/flags` 私有化;补 `reveal_record(c) -> Dictionary`、`board_dimensions()` 查询面。 + 调用点仅 B1 + board_view + main 三处,成本很低。 + +### E3(表征)B2 裸字典句柄跨模块传递,API 缺「含死亡残位」查询面 — P2 + +- **位置**:core/unit_container.gd:13-14(`hero/enemies` 公开;句柄 = Dictionary 本体,任何持有者可 + `u.hp = 999` 绕过 apply_damage) +- **现症**:hud_presenter.gd:78-79 为渲染死亡残位直读 `b1.b2.enemies` 原始数组—— + B2 的 `query_alive` 不含死亡单位,API 缺口迫使呈现层越界(B2 头注已登记此痛点)。 +- **建议**:短期补 `query_all_units()` 投影面把 presenter 需求收进 API; + 正式化(ADR-0007 两 face 归属裁决时)再决定是否升级 Unit 类型。 + +### E4 `db.hero` 表值运行时改写——测试覆盖通道用共享可变单例传参 — P2 + +- **位置**:presentation/main.gd:327-329、core/level_runner.gd:60-61,75-76 直接写 `db.hero.atk/max_hp`; + B2.spawn 经读表间接获得加成值 +- **前科**:双写叠加 6→18→54 教训、双基线同步义务(main.gd:187-189 注释)——纪律已成文但机制脆弱。 +- **建议**:切片保留;将「运行时表可变」登记进 ADR-0009 候选;正式化改参数传递(`spawn(atk_override=...)`)。 + +### E5 「只读」字段仅靠纪律,无机制约束 — P2 + +- **位置**:core/action_economy.gd:17-20(`phase/ap/rap/last_round_end` 架构文档定义为只读,实际公开可变); + core/rng_service.gd:19(`master_seed` 可绕过 set_master_seed 的计数器全零断言) +- **现状**:当前调用方全部合规(A2 经 try_spend/request_dig,B1 经 notify_*,呈现层只读)。 +- **建议**:下划线化 + 查询方法(`phase()/ap()/rap()`),趁调用方少时收口,成本极低。 + +### E6 小项 — P3 + +| 项 | 位置 | 说明 | +|---|---|---| +| 返回类型多态 | core/reward_economy.gd(grant/stock/use 失败返 `false`,query_* 返 `null`) | B1 消费处被迫 `if not (snap is Dictionary)`;统一 `null` 更符合 GDScript 惯例 | +| 泄漏原始表行 | core/reward_economy.gd:256-265 `equivalence_supply` 返回 `db.reward.rows` 行引用(可变) | 工具观测面,建议 `duplicate()` | +| BoardData 全公开 | core/board_data.gd:8-18「生成后不可变」仅靠注释;视图持板即可读全部雷位(当前未作弊) | 正式化时 duplicate 冻结或下划线化 + 访问器 | +| 成员声明位置 | core/action_economy.gd:121 `var _next_round_mode` 声明在中部、使用在前 | 集中到顶部 | + +--- + +## 3. 代码组织问题 + +### O1 main.gd 回涨至 667 行,再越 ~400 行审视线(规范 1.1)— P1 + +- **位置**:presentation/main.gd(667 行);ui_assembler.gd:3 自记「回涨 761 行触发审视线——拆出」历史 +- **增长主因**:调试模式(`_start_single/_start_debug_levels/_start_battle/_on_battle_finished` 的 + `_level_mode` 分支约 100+ 行)糊在组合根。 +- **建议**:照 tune_sim_controller.gd 先例把调试模式抽成独立控制器,组合根回到纯接线。 + +### O2 调试模式与 LevelRunner 平行实现,语义漂移风险 — P1 + +- **位置**:「库存跨战斗延续 + HP 携带 + 层级推进」存在两份实现——core/level_runner.gd(正式)与 + main.gd 调试分支(历史入口);`level_clear()` 调用时点已各有细节(main.gd:367,376 vs level_runner.gd:167)。 +- **建议**:调试两层模式直接复用 LevelRunner(其设计目标即此),或抽 `DebugBattleSession`。 + +### O3 B1 体量与纯函数沉淀 — P2 + +- **位置**:core/battle_orchestrator.gd 718 行(审视线超 300+); + 伤害公式族 `_resolve_damage/_charge_damage_component/_armor_effectiveness/_clamped_pool` + (约 80 行)为零状态纯算术。 +- **建议**:按规范 1.2 抽 `DamageMath` 静态宿主类;段序骨架不动、可回放承诺无风险。 +- **顺带**:core/board_generator.gd:50-55 对同一张合法板调用两次 `measure_inferability` + (全库最贵求解器)——合并为一次。 + +### O4 呈现层反向依赖 smoke 工具域 — P2 + +- **位置**:presentation/tune_sim_controller.gd:23 `load("res://smoke/level_autoplay.gd")`—— + UI 控制器依赖测试工具目录。 +- **建议**:「关卡自动模拟循环」上移为共享组件(core 或 lib),smoke 与 tune_sim 共同消费。 +- **顺带**:main.gd:106,188 与 tune_sim_controller.gd:23 用运行时字符串 `load()`, + 与 main.gd:16-19 的 `preload` 常量风格混用——统一 `preload`(编译期路径校验)。 + +### O5 头注溯源缺失(规范 1.4)— P3 + +core/ 12 文件中 4 个缺标准 `# 实现 GDD:...` 行: +`board_data.gd`(有 A1 描述但非标准格式)、`config_db.gd`、`rng_service.gd`、`reward_supply_controller.gd`。 + +--- + +## 4. 建议行动顺序 + +1. **P1 三连(合计约 2 天)**: + E2(A2 状态私有化 + 查询面)→ E1(B1 呈现快照面)→ O1+O2(调试模式抽离/复用 runner)。 + 完成后 board_view / hud_presenter / main 与 core 的接触面全部变为命名 API,封装收口完成。 +2. **P2 项并入 ADR-0009「切片机制正式化批」裁决**:E4 表值改写参数化、E3 单位句柄形态、 + O3 DamageMath 拆分、O4 模拟循环上移。 +3. **P3 顺手清**(头注补齐、返回值统一、声明位置、preload 统一)。 + +> 复审触发:P1 三连落地后重跑本审查核对接触面收口;ADR-0009 落定后正式工程骨架迁移前复核 E3/E4 裁决结果。 diff --git a/docs/architecture/requirements-traceability.md b/docs/architecture/requirements-traceability.md new file mode 100644 index 00000000..2afaf459 --- /dev/null +++ b/docs/architecture/requirements-traceability.md @@ -0,0 +1,60 @@ +# Requirements Traceability Matrix — Dungeon Sweeper + +> 生成:2026-09-12 · 源:`docs/architecture/architecture.md` §Technical Requirements Baseline(63 条)+ ADR-0001~0005 +> 门检查口径:**Foundation 层零缺口** = 每条 Foundation TR 都有明确裁决载体(专项 ADR,或经 design-review 门禁的 Approved GDD 契约 + 本架构文档章节),无未决项。 +> 状态图例:✅ 已覆盖 · 🔶 待 ADR(非 Foundation,随对应系统开工前裁决)· ⏳ 随未设计系统 + +## 覆盖总览 + +| 层 | TR 数 | 专项 ADR 覆盖 | GDD 契约 + 架构文档覆盖 | 待裁决 | +|---|---|---|---|---| +| Foundation | 36 | ADR-0001(a3-004)· ADR-0004(d2-014/016)· ADR-0005(d2-010/gc-002) | 32 条(D2/A3/A4/B3 GDD 均 Approved + 架构文档所有权/API 节) | **0** ✅ | +| Core | 21 | ADR-0001(b1-001/002)· ADR-0002(—) | 19 条 | 2(a1-011/012 → ADR-0006) | +| Feature | 2 | — | b1 对账协议等 | 0(b1-010 文档承载) | +| Presentation / Platform | 4 | ADR-0002(gc-008) | gc-006/007 文档 | 2(gc-004/005 随 D1/B5) | + +## Foundation 层逐条(36 条——零缺口核验) + +| TR | 需求 | 裁决载体 | +|---|---|---| +| d2-001~003 | 单一事实源 / ConfigDB 单点 / schema 先行 10 表 | D2 GDD(Approved)+ architecture.md §Module Ownership | +| d2-004 | 进度键 + R1 floor 解析 | D2 GDD R1 + architecture.md §API Boundaries | +| d2-005/006 | RngService 封闭流 / R2 派生与黄金向量 | D2 GDD R2 + architecture.md 原则 3 | +| d2-007 | 校验双通道 + 夹具注入 | D2 GDD + ADR-0004(载体) | +| d2-008 | 加载期/运行期致命 | D2 GDD Rule 6 | +| d2-009 | D2 零 UI 依赖 | D2 GDD Rule 7 + ADR-0003(core 隔离)+ architecture.md 原则 1 | +| **d2-010** | SCHEMA_VERSION 版本化 | **ADR-0005** | +| d2-011 | debug override 链 | D2 GDD R3 | +| d2-012 | 热重载状态机 + reload gate | D2 GDD + B1 GDD(唯一调用方) | +| d2-013 | 诊断收集器 | D2 GDD 校验双通道节 | +| **d2-014** | 冒烟框架协议 | **ADR-0004** | +| **d2-015** | RNG 快照 = seed + 计数器 | **ADR-0005** | +| **d2-016** | 项目级静态扫描域 | **ADR-0004**(S1–S8) | +| a3-001~003/005~008 | AP/RAP 账本、仲裁、相位机、refresh、表承载、last_round_end | A3 GDD(Approved)+ architecture.md | +| **a3-004** | round_end 双发射源 | **ADR-0001**(round_end_board / round_end_stopped) | +| a4-001/002 | 变异框架 + 雾行跨字段校验 | A4 GDD(Approved) | +| a4-003/004 | 生成期布置执行归 A1 / 零特判 | A4 GDD + A1/A2 GDD | +| b3-001~006 | 统一容器 / 时限量纲 / 五 API / 三分支 / 快照 / 不 clamp | B3 GDD(Approved) | + +## Core / Feature / Presentation 层(27 条) + +| TR | 需求 | 载体 | 状态 | +|---|---|---|---| +| a1-001~010, a2-001~010, b2-001~004/006, b4-001~009, b1-003~009/011 | 各系统行为契约(详见 architecture.md §TR Baseline) | 各 GDD(Approved)+ architecture.md §Module Ownership / §API Boundaries;事件类(a2-002/003、b1-001/002)由 **ADR-0001** 承载 | ✅ | +| **a1-011** | 度量独立入口三函数 | 🔶 待 **ADR-0006**(生成性能与异步化——A1 OQ2/OQ3) | 🔶 | +| **a1-012** | F2c 成本闸 / 生成性能姿态 | 🔶 待 **ADR-0006**(Core 层,A1 开工前裁决;切片证据已背书同步方案) | 🔶 | +| **b2-005**(注:face 归属实为 b2 OQ1 项) | query_attack/ambush_grant_plan 两 face | 🔶 待 **ADR-0007**(单位 API face 归属——B2 OQ1/OQ5) | 🔶 | +| **gc-004** | 双舞台资产管线 | ⏳ B5 前美术测试 → ADR-0011 | ⏳ | +| **gc-005** | 视觉锚三硬约束 | 🔶 D1 GDD(未写——MVP 层最后一块;切片教训四条为其硬输入) | 🔶 | +| gc-006/007 | 性能预算 / 平台输入 | technical-preferences + ADR-0002(窗口基座) | ✅ | +| **gc-008** | 渲染器选择 | **ADR-0002** | ✅ | + +## 待裁决 ADR 队列(全部非 Foundation) + +| ADR | 议题 | 触发时机 | +|---|---|---| +| ADR-0006 | 生成性能与异步化(A1 OQ2/OQ3 + 度量独立入口确认) | A1 生产实现前 | +| ADR-0007 | B2/B1 两 face 归属 + HeroTable 单行校验落点 | B2 生产实现前 | +| ADR-0008 | 零伤害死局跨字段护栏落点 | D2 生产化前 | +| ADR-0009 | 切片机制正式化(轮内刷新 / RewardSupplyController / LevelTable / grant_node_active / clear_blast) | 正式化批 | +| ADR-0010~0013 | A1 熔断 / B5 舞台 / A5 显影 / D3 存档格式(契约已由 ADR-0005 锁定) | 各系统 GDD 立项时 | diff --git a/docs/code-conventions.md b/docs/code-conventions.md new file mode 100644 index 00000000..90a89400 --- /dev/null +++ b/docs/code-conventions.md @@ -0,0 +1,155 @@ +# 代码规范(Dungeon Sweeper) + +> 生效:2026-09-11 · 来源:用户在垂直切片评审中逐一指出的真实陋习与缺陷,全部成文。 +> 每条规则附本项目真实反面案例(标注缺陷编号/日期)——规则不是抽象美德,是踩过的坑。 +> 约束范围:`prototypes/` 与正式 `production` 代码同尺度;与 CLAUDE.md「Conventions」节既有硬约束 +> (数值表单点 / RNG 封闭流 / Core 零 UI / fail-fast / spec-AC 一致性)并行生效,冲突以用户裁决为准。 + +**写任何代码前必须通读本文件。** + +--- + +## 1. 结构纪律——高内聚、低耦合 + +### 1.1 禁止上帝对象 +一个类只承载一个职责。场景主脚本(main)= **组合根**:模式流转、信号接线、核心写调用的唯一出口; +渲染、文案拼装、状态装配一律拆分为独立视图/装配器类。单文件超过 ~400 行即触发审视。 + +> **反面案例(2026-09-11,用户:"一个工程师看了想吐")**:main.gd 随功能追加长到 ~1100 行——棋盘渲染、 +> HUD、结算日志格式化、四种模态覆盖层、调整板、蒙特卡洛模拟器、关卡流程全部糊在一个类里。 +> 已拆分为 9 个单一职责单元(views/ + hud_presenter),main 降到 433 行组合根。 + +### 1.2 一切功能以类封装,禁止裸函数堆 +设计稿与实现同尺度:功能 = 类(状态 + 行为 + 信号),类是封装的最小交付单位。 +工具性纯函数只允许挂在明确的宿主类下(静态装配器/工厂),不允许无主散落。 + +> **反面案例(2026-09-11,用户:"我看你都是函数的方式……没有对象封装吧")**:供给控制器的伪代码 +> 以裸函数呈现被否;正式实现按"B4 私有组合件 RewardSupplyController"封装。 + +### 1.3 依赖单向,核心写调用收口 +依赖方向唯一:`views →(信号)→ 组合根 →(写调用)→ core`。 +视图零核心写调用(纯 setter / 信号外发优先);"读核心状态 → 拼展示文案"集中于装配器(presenter); +核心写接口(settle_batch / submit_window_selection / runner 推进)只允许在组合根出现。 + +### 1.4 命名 = 领域命名,禁止系统 ID 前缀 +文件名与类名禁止 `a1_`/`b1_`/`c1_` 等系统 ID 前缀(`a1_board_generator.gd` → `board_generator.gd`)。 +设计追溯不靠前缀:每个核心实现文件头注必须带一行 +`# 实现 GDD:design/gdd/-.md(系统 ID Xn)`;系统 ID 只作为文档与 registry 的追溯锚。 +豁免:D2 schema 类与 autoload(本就领域命名);文档/夹具登记名(如 b4_golden)属文档层。 + +> **反面案例(2026-09-11,用户:"是没有代码规范吗")**:ID 前缀是跟随 GDD 文档名的事实约定, +> 从未成文;同目录 `config_db`/`rng_service` 无前缀与 A/B/C 系列并存——两套命名即规范缺失的铁证。 + +### 1.5 禁止事实约定 +任何"大家默认这么做"的惯例必须成文进本文件(或 CLAUDE.md)才算存在。 +写代码时发现未成文但必须遵守的约定 → 当场补条文,不许靠惯性延续。 + +--- + +## 2. 契约纪律——跨层/跨类调用的语义 + +### 2.1 信号与事件契约成文 +每个跨类信号:参数语义、发送时点、消费方依赖,必须在发送方头注成文(参照 A2 三事件面先例)。 +状态机的每次迁移路径都必须发出对应事件——**缺一条路径的事件 = 消费方永久等待 = 软锁**。 + +> **反面案例(playtest 缺陷②)**:A2 旗动作花完末点 AP 后相位翻 Waiting 但不发 round_end, +> B1 结算点依赖该事件 → 整局软锁。v1 冒烟没抓到是因为自动玩家不用旗——路径覆盖不全的代价。 + +### 2.2 跨层标识符语义单一且成文 +位置(index)/ 句柄(handle)/ 键(key)是三种不同的东西。跨类传递标识符时, +传递方的语义必须与接收方契约逐字一致,禁止凭"当前恰好相等"传值。 + +> **反面案例(playtest 缺陷④)**:窗口选择契约 = 窗口条目位置,UI 却传库存数组句柄—— +> 未消耗时二者巧合相等;消耗一件后错位 → 选中件不消耗、邻件被误耗。 + +### 2.3 模态覆盖层层级规则 +覆盖层遮挡关系由场景树加入顺序决定。打开任何模态覆盖层时,必须显式隐藏会遮挡它的全屏层; +新增覆盖层必须登记其在 z 序中的位置,并提供"打开可见性"的场景断言。 + +> **反面案例(playtest 缺陷⑤)**:数值调整板已实现,但打开时被不透明的开始覆盖层完全遮住 +> = "点击无反应"。功能存在 ≠ 功能可达。 + +### 2.4 状态字段语义显式 +跨战斗/跨场景携带或恢复的状态字段,其语义来源必须显式(表值 / 明确入参),禁止用入参隐式定义。 + +> **反面案例**:B2 spawn 把 hero 的 max_hp 设为入参 hero_hp——跨战斗携带 HP 时上限被压扁, +> 恢复点/治疗包的满血守卫全部失效。 + +--- + +## 3. 测试纪律——"存在"必须证明"可达且正确" + +### 3.1 场景级探针标配 +每新增一个可交互 UI 区域(视图/覆盖层/面板),必须配套场景级 headless 探针: +真实实例化场景 → 驱动真实输入路径 → 断言可见性与状态。**只有 core 层 AC 通过不算有测试** +(GDScript 编译不查字典字段与时序,呈现层错误只在运行时暴露)。 + +### 3.2 覆盖层/面板必测"打开后可见" +凡模态覆盖层,探针必须断言:打开后自身 visible 且遮挡物已隐藏(缺陷⑤回归断言)。 + +### 3.3 缺陷修复必须沉淀回归断言 +每个缺陷修复必须在探针或冒烟中留下能抓住该缺陷的断言(旗动作耗尽 AP → 必须进入第 2 轮; +选用道具 → 库存态必须变 Used)。没有回归断言的修复视为未完成。 + +### 3.4 提交前全量验证 +交付前必须全绿:`smoke_all.gd` + 全部场景探针(ui_probe / ui_click_probe / ui_tune_probe)。 +行为探针使用随机化玩家行为(旗/停手/刷新/无效点击)——自动玩家不做的动作就是测试盲区。 + +--- + +## 4. 语言纪律——GDScript 4.x 实战坑(本项目踩实) + +1. **多行 lambda 内禁止嵌套 match / 复杂控制流**(解析不可靠,报"Expected expression for match + pattern")——信号处理体一律抽成命名方法 + `bind` 传状态。 +2. **Dictionary 取值返回 Variant,无类型推断**——参与算术或传参前显式声明类型(`var x: float = ...`)。 +3. **RichTextLabel:`append_text()` 不回写 `text` 属性**——读可见文本用 `get_parsed_text()`。 +4. **lambda 捕获循环变量按创建时值**——循环内先落地局部变量再捕获,或用 `bind`。 +5. **重命名脚本必须同步 `.uid` 文件**(资源身份稳定);新增 `class_name` 后必须跑一次编辑器扫描 + 刷新全局类缓存,否则 `--headless --script` 模式解析失败。 +6. **`print` 格式占位符与实参类型必须匹配**(`%d` 配 String = 静默输出字面量)——工具输出错 + 等于没有输出。 + +--- + +## 5. 数值与汇报纪律 + +### 5.1 数值口径必须显式标注尺度 +任何期望值/均值汇报必须写明口径:**每格 / 每板 / 每战 / 每关**。混用口径 = 误导 +("可选类 3.32"不加"每板",会被读成每格期望 >1)。 + +### 5.2 调参先测量,改后复测 +改任何玩法数值前,先用扫描工具产出基线(`level_monte_carlo.gd` / `reward_expectation.gd`); +改完必须复测回填数据。禁止拍脑袋调参。 + +### 5.3 数值设计必须跨尺度推演 +在单一尺度校准的数值,必须推演其在更大尺度的复合结果(每战合理 × 关内囤积 = 关卡灾难)。 + +> **反面案例(2026-09-11)**:可选类 25% 格子配比在单战斗尺度校准(击杀需求 18/54), +> ×"库存关内延续"× 5 张板复合后 = 每关 24.8 件、刀总伤 180 > Boss 130 HP——预警⑥在关卡尺度坐实。 + +### 5.4 用户拍板的实现零发挥 +拍板范围外的细节一律不自行补全——以"待拍板问题"清单列出等裁决。 +(反面案例:清图奖励被自行设计成入库刀,用户纠正为"一次性高额伤害不入库"。) + +--- + +## 6. 流程纪律 + +1. **大改动三段走**:设计稿/伪代码 → 用户审阅 → 实现 → 修改记录(README + session-state)。实现不得先于审阅。 +2. **实现批收尾双登记**:切片 README 修改记录 + `production/session-state/active.md` 条目,缺一不可。 +3. **规范变更必须成文进本文件**——发现未成文约定、或用户裁决产生新规则,当场补条文。 +4. **呈现层缺陷的教训回写 D1 GDD 设计输入**(信号载荷 schema / 场景级测试 / 时点状态可见性)。 + +--- + +## 附:缺陷-规范对照表(本项目真实案例,2026-09-11) + +| 缺陷 | 现象 | 根因 | 对应规则 | +|---|---|---|---| +| ① UI 抖动/"第3轮"消失 | 悬停文案换行推挤侧栏,末行被挤出视口 | 动态内容参与布局回流 | 1.1 拆分 + 视图定长槽(HUDView) | +| ② Boss 战 AP 用完不结算(软锁) | 插旗耗尽末点 AP 后整局冻结 | 旗动作路径缺轮末事件 | 2.1 事件发射完整性 | +| ④ 道具使用后不消失 | 选用件留存、邻件被误耗 | 窗口位置 vs 库存句柄错位 | 2.2 标识符语义单一 | +| ⑤ 调整板点击无反应 | 面板已实现但被开始层遮挡 | 模态打开未隐藏遮挡物 | 2.3 层级规则 + 3.2 | +| 上帝对象 | main.gd ~1100 行 | 职责无边界持续堆积 | 1.1–1.3 | +| ID 前缀命名 | a1_/b1_ 与领域命名并存 | 事实约定未成文 | 1.4–1.5 | +| 可选类每关 24.8 件 | 速杀退化、囤刀秒 Boss | 单尺度校准未做跨尺度推演 | 5.3 | diff --git a/docs/consistency-failures.md b/docs/consistency-failures.md new file mode 100644 index 00000000..9bf82abb --- /dev/null +++ b/docs/consistency-failures.md @@ -0,0 +1,38 @@ +# Consistency Failure Log + + + + +| Date | GDD A | GDD B | Conflict Type | Status | +|------|-------|-------|---------------|--------| +| 2026-09-09 | b4-reward-item-economy | registry (rng_service) | 🔴 错引条目名 + 过期语义 | RESOLVED 同日 | +| 2026-09-09 | b4-reward-item-economy | registry (status_effect.b1_obligations) | 🔴 义务编号错引 | RESOLVED 同日 | +| 2026-09-12 | registry (reward_table/reward_config) | b4-reward-item-economy / d2-data-config | 🔴 09-11 批 fields 行漏网 ×2 | RESOLVED 同日 | + +### 2026-09-09 — /consistency-check — 🔴 CONFLICT +**Domain**: 奖励经济 × 数据配置(RNG 流语义) +**Documents involved**: b4-reward-item-economy.md(Overview/Rule 2/Dependencies 三处)vs registry entities.yaml(rng_service.scope_event_binding) +**What happened**: B4 三处引用 "`board_gen.scope_event_binding`…每次奖励结算"——条目名错引(该属性属 rng_service)且为 Phase 5b 改写前旧语义;定稿批回写 registry 后 GDD 正文未同步。 +**Resolution**: B4 三处改引 `rng_service.scope_event_binding` + 新语义(每次 board install 批)+ 回写义务标已兑现。EXECUTED 同日。 +**Pattern**: 接口漂移回写 registry 后,**发起 GDD 自身正文中的旧写法必须同批清扫**——回写义务条款在兑现后要标记状态,否则留下"义务已尽但正文仍说待办"的自相矛盾;跨条目引用(scope_event_binding 属 rng_service 非 board_gen)写全条目名可避免。 + +### 2026-09-09 — /consistency-check — 🔴 CONFLICT +**Domain**: 奖励经济 × 状态系统(义务清单交叉引用) +**Documents involved**: b4-reward-item-economy.md(Rule 3)vs registry entities.yaml(status_effect.b1_obligations ⑥/⑧) +**What happened**: B4 Rule 3 引"(B1 义务⑥ 同款协议)"指代满层"奖励未生效"明细行协议——但 B3 清单⑥ = decay_round 恰一次、B4 自有清单⑥ = roll_board 恰一次;该协议实际落点是 B3 ⑧ / B4 B1×6 ②。项目内并存多套"B1 义务清单",裸编号"义务⑥"产生歧义引用。 +**Resolution**: 改为"B1 义务②〔本 GDD 清单〕/ B3 义务⑧ 同款协议"。EXECUTED 同日。 +**Pattern**: **义务清单编号引用必须带清单限定词**("B3 义务⑧"而非裸"义务⑥")——同一接收方(B1)已累积 4 套清单(B3×8/B2×6/A2×3+2/B4×6),裸编号在多清单语境下不可解析。 + +### 2026-09-10 — /consistency-check — 🔴 CONFLICT +**Domain**: 战斗结算(B1 内部 AC 交叉引用) +**Documents involved**: b1-settlement.md(AC 节内部——qa-lead 代理交付稿) +**What happened**: 9 处 AC 编号误引——AC-13×2/AC-28 引"AC-34"(应为 AC-35/AC-36)、AC-24/AC-27 引"AC-30"(应为 AC-31)、前言负空间引"AC-29/AC-14"(应为 AC-30 腿1/AC-15 腿1)、AC-12 腿4 引"AC-27"(应为 AC-28)、out-of-scope 调参重跑清单缺 AC-36/37/40。 +**Resolution**: 9 处同日修正(定向 /consistency-check 抓获——写入后即查)。 +**Pattern**: **专家代理交付的长 AC 稿必须做编号引用自洽扫描**(引用的 AC 号 vs 被引 AC 实际内容)——qa-lead 起草过程中 AC 编号重排后交叉引用未随批更新,与"义务清单编号引用必须带清单限定词"同族的编号可解析性问题;建议 /design-system 的 AC 节写入后固定跑一次 `grep "AC-\d+" 交叉核对`。 + +### 2026-09-12 — /consistency-check — 🔴 CONFLICT +**Domain**: 奖励经济 × 数据配置(2026-09-11 两级奖励批 registry 回写完整性) +**Documents involved**: registry entities.yaml(reward_table.fields.category / reward_config.fields.p_min)vs b4-reward-item-economy.md L56 / d2-data-config.md L42–43 +**What happened**: 2026-09-11 两级奖励批回写时 registry 同条目"改了 mechanics/头注、漏了 fields 行"×2——① category fields 行仍三值(同条目 mechanics 行已五值,自相矛盾);② p_min fields 行仍默认 4(同文件头注已记"p_min 4→8"、D2 行已 8)。 +**Resolution**: 已执行(2026-09-12 用户批准):registry 两行修正完成(category 五值 / p_min 默认 8),复查扫描旧值清零。 +**Pattern**: **接口漂移批的高发漏网点 = "散文/注释行改了、YAML fields 行没改"**——registry 条目内 fields 与 mechanics/头注是三处独立文本,批量改写须逐处 grep 旧值;本例与 2026-09-09"错引条目名"教训同族(回写完整性核查),修复后应将"fields 行 vs mechanics 行一致性"纳入 /consistency-check 固定检查项。 diff --git a/docs/consistency-report-2026-09-08-b3.md b/docs/consistency-report-2026-09-08-b3.md new file mode 100644 index 00000000..8d28a2f0 --- /dev/null +++ b/docs/consistency-report-2026-09-08-b3.md @@ -0,0 +1,31 @@ +# Consistency Check Report — 2026-09-08(B3 定稿后定向 · 第二次) + +- **Scope**: full · Registry 条目: 9(config_db / rng_service / stream_seed_derivation / difficulty_row_resolution / schema_version / action_economy / variation_table / variant_pool_gate / **status_effect✨ 新增**) +- **GDDs scanned**: d2-data-config · a3-action-economy · a4-terrain-variation · **b3-status-effect✨** +- **触发**: /design-system B3 定稿同日回填后的漂移检查 + +## Conflicts Found + +无 🔴 冲突。 + +## Stale / Pending Write-backs(2 项——均已同日执行) + +⚠️ **D2 Tuning Knobs 占位未随 B3 定稿删除**(d2-data-config.md Tuning Knobs 节) +- D2 自家规则"占位参考值定稿时必须删除对应条目"(A3 三轮回填先例同款);"增伤 1.5× / 减伤 0.5× / 持续 2 次结算(B3)"已随 B3 四行定稿失效 +- → Resolution: **已执行(2026-09-08 同日)**——三值删除(划线保留痕迹),事实登记归 registry `status_effect`;owner 行 B3 标 ✓ + +⚠️ **B3 Overview 内部口径滞后**(b3-status-effect.md Overview 第二段) +- "标中旗 debuff 档位归 B4、敌方增益归 B2"写于 Rule 10 收编决策之前,与 Rule 10"四行定稿权归 B3"存在读法张力 +- → Resolution: **已执行(2026-09-08 同日)**——Overview 改写为"经济归属归各锚 owner(锚验收权 B4/B2/B1),四行 MVP 初值定稿权经 Rule 10 收编归本 GDD",与 Rule 10 / registry ownership_note / 索引门禁记录四方对齐 + +## Unverifiable / Informational + +ℹ️ **A3 GDD"档位归 B4"表述 vs B3 Rule 10 收编**:已治理分叉——systems-index 门禁增补记录(2026-09-08 B3 块)明文"A3 GDD(Approved)不改,以索引条 + registry ownership_note 为准"。非冲突。 +ℹ️ **A4 三条定价硬约束的"最低档安全格 buff 当量"**:B3 定稿后参照对象 = attack_up 行(+50%×d2);A4 未曾定值,零数值冲突;对照闭合归 B1(条款 1 对照表)。 +ℹ️ **weaken 条款 2 锚红色预警**:已知项(B3 Formulas 预警 + registry status_effect.anchor_warning + 索引 B1 强制检查点输入登记),非本次新发现;B1 对照验收优先裁决。 + +## Clean Entries + +✅ config_db · rng_service(流封闭清单一致,combat_var 无复现)· stream_seed_derivation · difficulty_row_resolution · schema_version · action_economy(AP 8 跨文档引用一致;referenced_by 已增 B3——F2 AP 界只读消费)· variation_table(fog 行 GDD/D2/registry 三方一致)· variant_pool_gate · status_effect(GDD/D2 Core Rules 行/registry 三方同日同步一致) + +**Verdict: PASS**(0 冲突;2 项 drift——同日核销) diff --git a/docs/consistency-report-2026-09-08.md b/docs/consistency-report-2026-09-08.md new file mode 100644 index 00000000..7c43d053 --- /dev/null +++ b/docs/consistency-report-2026-09-08.md @@ -0,0 +1,25 @@ +# Consistency Check Report — 2026-09-08 + +- **Scope**: 定向(A4 定稿回填后)· Registry 条目:8(config_db / rng_service / stream_seed_derivation / difficulty_row_resolution / schema_version / action_economy / variation_table✨ / variant_pool_gate✨) +- **GDDs scanned**: d2-data-config · a3-action-economy · a4-terrain-variation + +## Conflicts Found + +无 🔴 冲突。 + +## Stale / Pending Write-backs + +⚠️ **D2 Core Rules 表 VariationTable 行(占位描述落后于 owner 回填)** +- D2 占位:`变异类型枚举 + biome/tag 数据字段(AD 门禁)| 按 variant_id(含 unlock_level 列)| A4` +- A4 已定稿:8 字段 schema + fog 行(D2 接口漂移规则的**预期演化**,非冲突);按漂移规则,owner 回填须回写 D2 Core Rules 表行 + Status 行——**待执行**(A3 三次回填同款义务)。 +- → Resolution: ~~更新 D2 Core Rules 表行与 Status 行~~ **已执行(2026-09-08 同日核销)**:VariationTable 行已标注"已回填定稿(2026-09-08 A4)+ Rule 4 豁免注明",D2 Status 行已登记 A4 回填。 + +## Unverifiable / Informational + +ℹ️ **A3 F1 条款 5 括注示例被 A4 定稿超越**:A3 原文"可推理率越低(迷雾/终局 50/50 越多)"——A4 承重规则(雾区无雷)下迷雾不再产生 50/50,不再作为可推理率减项。条款本体(B1 闭合对账)不变;A4 Interactions 与 registry `variation_table.fog_ap_note` 已登记 B1 对账注记,随 B1 GDD 消化。Approved 文档 A3 不改。 + +## Clean Entries + +✅ rng_service/board_gen(scope 绑定口径一致)· config_db(单点访问)· action_economy(AP 8 / 旗 1 AP 引用一致)· variation_table / variant_pool_gate(源=A4,自洽)· difficulty_row_resolution / stream_seed_derivation(A4 显式不消费,文档化)· schema_version(间接承载)。 + +**Verdict: PASS**(0 冲突;1 项漂移规则回写——已执行) diff --git a/docs/consistency-report-2026-09-09-a1.md b/docs/consistency-report-2026-09-09-a1.md new file mode 100644 index 00000000..de614cfd --- /dev/null +++ b/docs/consistency-report-2026-09-09-a1.md @@ -0,0 +1,26 @@ +# Consistency Check Report — 2026-09-09(A1 定稿后定向) + +- **Scope**: full · Registry 条目: **10**(config_db / rng_service / stream_seed_derivation / difficulty_row_resolution / schema_version / action_economy / variation_table / variant_pool_gate / status_effect / **board_gen_table✨ 新增**) +- **GDDs scanned**: d2-data-config · a3-action-economy · a4-terrain-variation · b3-status-effect · **a1-board-generation✨** +- **触发**: /design-system A1 定稿同日回填后的漂移检查(本会话第二次收口检查) + +## Conflicts Found + +无 🔴 冲突。 + +## Stale / Pending Write-backs(1 项——已同日执行) + +⚠️ **D2 Dependencies 表 A1 行滞后于回填**(汇总行未含 BoardGenTable 回填字样;权威的 Core Rules 表行已更新) +- → Resolution: **已执行(2026-09-09 同日)**——A1 行改为 "BoardGenTable 回填(首个进度键表)+ VariationTable schema + board_gen 流",与 A3/A4 行口径对齐 + +## Unverifiable / Informational + +ℹ️ **rng_service 快照契约的 A1 具体化**:A1 F5 "attempt 边界 = 生成函数完整返回"是 D2 快照契约(消费方 D3)的 A1 侧操作定义——补充细化非冲突,registry `board_gen_table.mechanics` 已登记。 +ℹ️ **combat_var 零复现**:仅存于 registry 历史注记与 `gdd/reviews/` 归档备份(非活文档)。 +ℹ️ **B3 上轮核销项复核在位**:Overview 收编括注 ✓ · D2 Tuning Knobs 三值删除 ✓ · status_effect 三方一致 ✓。 + +## Clean Entries + +✅ config_db · rng_service(board_gen scope 绑定与 A1"一图一取号"一致)· stream_seed_derivation · difficulty_row_resolution · schema_version · action_economy(referenced_by 含 A1,dig_ap_cost 只读引用一致)· variation_table(f≤8d/⑫ 与 A1 F2 一致)· variant_pool_gate · status_effect · **board_gen_table(GDD/D2/registry 三方同日同步)**——全部 GDD 零占位残留。 + +**Verdict: PASS**(0 冲突;1 项 drift——同日核销) diff --git a/docs/consistency-report-2026-09-09-b4.md b/docs/consistency-report-2026-09-09-b4.md new file mode 100644 index 00000000..658abc4a --- /dev/null +++ b/docs/consistency-report-2026-09-09-b4.md @@ -0,0 +1,60 @@ +# Consistency Check Report(B4 定稿后定向) + +Date: 2026-09-09 +Registry entries checked: 17(services 2 · schema_data 8 · formulas 3 · constant_set 1 · glossary 1 · constant 1 · 其他 1) +GDDs scanned: 8(d2 / a3 / a4 / b3 / a1 / a2 / b2 / b4)+ game-concept(两处登记对齐项专查) +Scope: B4 定稿后定向(沿 B3/A1 定稿后定向检查先例) + +--- + +## Conflicts Found + +### 🔴 C1 — B4 引用错误条目名 + 过期语义(3 处) + +- Registry(source: rng_service):`scope_event_binding.reward_roll = "每次 board install(格奖励批 + 清图奖励批)"`(2026-09-09 B4 定稿批改写) +- B4 GDD 三处(Overview / Rule 2 语义注记 / Dependencies ②)写作 "`board_gen.scope_event_binding` … '每次奖励结算'"——**条目名错引**(board_gen 无该属性)且为回写前旧语义 +- Resolution(EXECUTED 同日):B4 三处改为 `rng_service.scope_event_binding` + 新语义;Rule 2/Dependencies 回写义务标"已兑现"。registry 为权威侧(owner 授权漂移),B4 文本系回写前写法,非设计分歧。 + +### 🔴 C2 — B4 Rule 3 义务编号错引 + +- B3 registry `status_effect.b1_obligations` ⑧ = "溢出未生效"行进结算明细协议;B4 自有 B1×6 之② = 满层零兑付 + "奖励未生效"行 +- B4 Rule 3 原文"(B1 义务⑥ 同款协议)"——两套清单的⑥(decay_round / roll_board 恰一次)均非该协议 +- Resolution(EXECUTED 同日):改为"B1 义务②〔本 GDD 清单〕/ B3 义务⑧ 同款协议"。同文件另两处"义务⑥"(资格格清单供给,AC 节/Dependencies)指向 roll_board 恰一次,**引用正确、未动**。 + +### ⚠️ S1 — game-concept 第 144 行过期(主动道具) + +- game-concept(待定问题):"[ ] 主动道具——后续版本加入;B4 GDD 预留'使用时机'字段" +- B4 定稿(2026-09-09 用户拍板):MVP 激活(轮末结算前道具使用窗口;use_timing 单值 pre_settlement 承重) +- Resolution(EXECUTED 同日):game-concept 改为 [x] 定稿条目(沿第 145-147 行 A3 修订注记同款格式)。 + +### ⚠️ S2 — game-concept 第 3 行参照口径(Peglin 道具跨循环累积) + +- game-concept 参考行:"道具跨循环累积"(Peglin 参照特性,无战斗域界定) +- B4 定稿:库存 = 战斗域(跨轮次跨图累积、battle_over 全清不跨战斗) +- Resolution(EXECUTED 同日):参考行加 B4 定稿口径括注(与 Peglin 跨战斗持有显式区分)。非锁值冲突——参照描述对齐。 + +### ℹ️ T1 — 术语登记补漏 + +- B4 首创术语"资格格"(GDD 内已有白话内联定义)未入 registry terminology——用语纪律(CLAUDE.md 2026-09-09)要求登记 +- Resolution(EXECUTED 同日):terminology 增词条 + referenced_by 增 B4。 + +--- + +## 核实一致的关键声称(抽样) + +✅ B3 source_class 四值枚举 `{safe_cell, flag_hit, mine_hit, clear_reward}`(B3 Rule 3 原文核对)——B4"已在枚举内"声称成立 +✅ A2 `reward_eligible` 口径(initial false / chain·dig∧safe true 含雾格 / mine false)与 B4 F1 资格格集 = S∖R₀ 等价 +✅ enemy hp=18 ×unit_count 3 = 54(B4 Tuning Knobs "3 单位 54 需求")与 registry enemy.hp 一致 +✅ dig_ap_cost=1(B4 缺口预检 2 分母引用)与 registry action_economy 一致 +✅ defense_up max_stack=2(B4 E3/F 预警④ "13 格链 × max_stack=2")与 registry status_effect 一致 +✅ B4 AC-20① 断言的 registry 措辞与实际写入逐字一致 +✅ D2 六处回写与 B4 Rule 9 字段清单/registry reward_table 八键一致 +✅ A3 条款④⑤、A4 三约束的引用措辞与 registry action_economy.downstream_contract / variation_table.pricing_constraints 一致 + +--- + +## Verdict + +**PASS(修正后)**——2 🔴 + 2 ⚠️ + 1 ℹ️ 全部同日修正落盘(drift write-back EXECUTED);无未决冲突。 + +修正涉及文件:`b4-reward-item-economy.md`(4 处)· `game-concept.md`(2 处)· `entities.yaml`(terminology 词条 + referenced_by) diff --git a/docs/consistency-report-2026-09-12.md b/docs/consistency-report-2026-09-12.md new file mode 100644 index 00000000..985eadd8 --- /dev/null +++ b/docs/consistency-report-2026-09-12.md @@ -0,0 +1,78 @@ +# 一致性检查报告(2026-09-12,lore 换轴概念层增补后全量) + +> Scope: full · 触发:世界观换轴(惩罚格类型化/控制/坍塌显形)落地后的例行检查 · 方法:grep-first + 疑点定点深查 +> Registry: 23 登记项(实体 14 / 公式 6 / 常量与公式集、词汇表 27 词条) +> 扫描面:GDD ×10(a1–a4 / b1–b4 / c3 / d2)+ 专查 game-concept / game-pillars / design/ux/battle.md / design/ux/interaction-patterns.md / registry / production/session-state(33+ 条目) + +## 结论 + +**CONFLICTS FOUND**——2 项 🔴(registry 2026-09-11 批行内字段漏网,文本级、修复分钟级)+ 2 项 ⚠️(概念层措辞/命名登记级)+ 4 项 ℹ️(核实为在案事项)。**零数值冲突、零结构冲突、零接口冲突、零阻断**;23 项登记项中 21 项跨文档零冲突。 + +--- + +## 🔴 冲突(须修,均已定位到行) + +### 🔴-1 registry `reward_table.fields.category` 三值残留(2026-09-11 批漏网) +- registry fields 行:`enum {buff, damage_item, heal_pack}——MVP 恰三值` +- 事实源:B4 GDD L56 与 D2 L42 均为 **五值** `{buff, damage_item, heal_pack, instant_aoe, instant_heal}`(两类交付轴);**registry 同条目的 mechanics 行也已写"category 五值枚举"**——同条目内自相矛盾,fields 行漏改。 +- 修复:registry fields 行改五值(owner=B4 口径代修)。 + +### 🔴-2 registry `reward_config.fields.p_min` 默认 4 残留(同批漏网) +- registry fields 行:`默认 4 = 默认根最低刀 power` +- 事实源:D2 L43 = `默认 8`(2026-09-11 批);**registry 本文件头部修订注释自己记了"reward_config p_min 4→8"**——注释改了、字段行没改。 +- 修复:registry 默认 4→8(owner=B4 口径代修)。注:entry 21(可选类供给控制器)批将再度改写刀供给/K_dmg=0/active_p0 等(已登记"B4/registry 改写待文档批"),p_min 最终语义随该批走;4→8 这个中间态修正无争议可先行。 + +--- + +## ⚠️ 漂移(登记级,本检查新抓) + +### ⚠️-1 概念层换轴措辞碰撞:B1 L29 / B3 L22 "雷是赌桌不是陷阱" +- 2026-09-12 lore 换轴后"陷阱"成为惩罚格类型词表一员(未来类型:直接扣血),该句字面可误读为"雷格不是陷阱类型的惩罚格"。句子本意(雷是争夺中的资源、非单向环境危险)在新正典下反而更成立——实际风险低。 +- 处置:不动机制 GDD;登记进 lore 记录③;措辞更新归 B1/B3 各自下一批修订(候选替换:"雷是赌桌不是死局")。 + +### ⚠️-2 lore ② 敌种叙事行与切片 DND 命名错位 +- session-state entry 91(2026-09-12 v3 批):切片 enemy_id 已更名 **goblin / bugbear / ogre_chief**(DND 命名批);registry / B2 / D2 / C3 GDD 仍为 grub / ambusher / boss_guardian(待文档批,与 entry 37 同族)。 +- lore 记录②"敌种叙事身份轴"以 grub(蛆虫=腐巢爬虫)为锚——DND 名下"巢居虫群"意象需换皮(goblin/bugbear 仍可作"巢穴居民",蛆虫意象作废)。 +- 处置:随 enemy 文档对账批一并处理;lore 侧届时按正式 enemy_id 重写叙事行。 + +--- + +## ℹ️ 核实为"已知/在案"(非新发现,列出以防重复登记) + +1. **C3 L236/267 `refresh_max_per_round` vs registry/A3/B1 `refresh_cap_per_round`**——session-state entry 35 已登记:切片 provisional 名 vs GDD 正式名、语义一致,正式 D1 批统一命名。在案。 +2. **B3 正文 weaken −25 ×4 处 + 🔴 预警两处(L195/237)未关**——2026-09-10 检查已裁定 documented-exception(registry F0 回执"正文不改以本条为准"收编先例覆盖),维持原判。建议 B3 下一批补一行 F0 关闭回执注记(两处红警读作已裁决),并顺带更正 B1 L422 回执措辞("B3 GDD 行表"实际未回写,按收编先例口径)。 +3. **registry `reward_table.direction_precheck` 旧预检(40%×5=2|资格格|)**——B4 L219 已重算(19%×9.7≈1.8|资格格|),entry 21 供给控制器批再 supersede 且已登记"B4/registry 改写待文档批"。随批。 +4. **D2 三处文本级残留**:L225"category 三值封闭/AOE 不入 MVP"(五值批前旧注);L44 SettlementTable 行"七字段/pool_clamp_lo_hi/p_perfect [100,999]"(clamp 部分已有收编注记,**p_perfect 域 {100,200} 无收编覆盖——D2 schema 层与 B1 载体域两层校验分歧,建议随批优先**);L42 RewardTable 默认根枚举小滑差(旧五行集并列残留 / 八行枚举漏 clear_knife / attack_up 缺 _basic 后缀)。→ 归入 entry 21/37 文档对账批。 + +--- + +## 已登记待办批(session-state 在案,本检查确认仍在册——对账批输入清单) + +| 来源批 | 待回写面 | +|---|---| +| entry 21 可选类供给控制器 | clear_blast 40 / active_inject 60-30-10 / active_p0-decay / K_dmg=0 → B4 GDD F5 + registry reward_table/reward_config | +| entry 27/35 轮内刷新 | A3/B1/B4 GDD + game-concept 层级措辞 + registry;refresh 字段名统一归正式 D1 批 | +| entry 33 遭遇随机化 | EnemyTable.unit_count_max → registry enemy.fields + D2 EnemyTable 行;**D2 Rule 5 流清单缺 encounter 行**(registry rng_service 已登记) | +| entry 37 伤害×护甲批 | EnemyTable.armor_type / B1 三分量构成+armor_effectiveness / B3 六行状态表 / **A2 OQ1(标错惩罚三选一 + 重复性=每雷实例一次)** / registry enemy·settlement·status_effect / C3 数值接管 | +| entry 91 v3 数值批 | DND 命名(goblin/bugbear/ogre_chief)→ B2/registry/D2 enemy 行 | +| C3 SLICE-TUNED 转正 | C3 L42 自declare 回写责任 → registry enemy/hero + D2 行值(grub 24/伏击兽 36/守卫 130、hero 50 等) | + +--- + +## 概念层专项(2026-09-12 lore 换轴 · 本会话范围核验) + +1. game-concept 世界观与命名节 + 视觉锚原则 5:已带 2026-09-12 版本注记落地 ✓;机制节"雷/旗"用词为机制名、按设计不动 ✓ +2. game-pillars P4:引用式写法(指向 game-concept)自动随流,零改动 ✓ +3. registry terminology:概念层术语不入 registry(2026-09-09 概念层增补先例)✓ +4. B1/B3"不是陷阱"措辞碰撞:⚠️-1 登记 +5. battle.md 玩家可见文案"插旗"→"控制"采纳点:文案 pass / D1 批执行(battle.md L258 明载文案定稿权归实现期文案 pass)——登记 +6. **A2 OQ1 重复性已被切片实现事实收口**(entry 37:每雷格实例一次去重,确定性派生三选一)→ lore ⑤-3(c) 措辞可定"**当轮反扑**",待 A2 GDD 修订批确认后回写 lore +7. game-concept 待定问题"标错惩罚"挂 Open:正确状态(机制落点走 A2 OQ1 修订批)——known-open + +--- + +## 干净项 + +✅ 21/23 登记项跨 GDD 零冲突:config_db · rng_service(流语义/scope 绑定一致)· stream_seed_derivation · difficulty_row_resolution · schema_version · action_economy(AP 8/价目/RAP 规则全一致)· variation_table · variant_pool_gate · status_effect(D=1/+3/cap=4)· board_gen_table([0.85,1]/0.25/16384/min_safe)· a2_event_surface · a2_initial_expansion · terminology · enemy(cap=4/行集规则)· hero(atk=6/拒 0)· reward_table(mechanics/mvp_content 行)· settlement_table(registry↔B1 一致)· clause3_supply · clause5_dominance(C3 承接"非支配设计态"= 约束包②选择权行使 ✓)· difficulty_table · difficulty_expectation_table · boss_dmax_convention(Boss=Dmax+charge 拍 ✓) + +**Verdict: CONFLICTS FOUND**(2 🔴 文本级 · 零阻断 · 修复清单见上) diff --git a/docs/engine-reference/godot/VERSION.md b/docs/engine-reference/godot/VERSION.md new file mode 100644 index 00000000..d1c201b4 --- /dev/null +++ b/docs/engine-reference/godot/VERSION.md @@ -0,0 +1,46 @@ +# Godot — Version Reference + +| Field | Value | +|-------|-------| +| **Engine Version** | 4.7.2-stable(2026-08-18 发布) | +| **Project Pinned** | 2026-09-07 | +| **LLM Knowledge Cutoff** | May 2025(训练数据覆盖 ~4.3/4.4) | +| **Risk Level** | **MEDIUM**(2026-09-12 降级)——4.5→4.7 API 级破坏性变更已逐条核实(源:官方迁移指南),无一条破坏本项目架构契约;残余风险 = 4.8(dev 阶段)未来升级 + 未覆盖的运行期边角行为,代理写码规则不变:不确定的 API 一律查 `docs.godotengine.org/en/4.7/` 或 WebSearch 核实后再用 | +| **Last Docs Verified** | 2026-09-12(4.5/4.6/4.7 迁移指南逐条核实 + 版本检查:4.7.2 仍为最新稳定版,4.8 仅 dev 快照) | + +## Post-Cutoff Version Timeline + +| Version | 发布 | 与本项目相关要点 | +|---|---|---| +| 4.4 | 2025-03(训练覆盖边缘) | — | +| 4.5-stable | 2025-09-15 | stencil 效果、无障碍改进;4.5.2(2026-03-19)为末版维护;**逐条变更已核实(2026-09-12)** | +| 4.6-stable | 2026-02 起(4.6.1 2026-02) | Modern 主题、flexible docks、编辑器 QoL;4.6.3 维护;**逐条变更已核实(2026-09-12)** | +| **4.7-stable** | 2026-06(**4.7.2 = 2026-08-18,本项目锁定**) | 见下;**逐条变更已核实(2026-09-12)** | +| 4.8 | dev 快照 1–4(开发中) | 无稳定版发布日期;稳定版发布后跑 `/setup-engine refresh` | + +## 4.7 与本项目相关的变更(已核实) + +- `Tween.tween_await()`(信号等待)——**不影响** B1"结算逻辑禁 await"约束(那是设计约束,非引擎限制) +- Control `offset_transform_*`(平移/旋转/缩放 Control 而不影响容器布局)——D1 HUD 可用 +- DrawableTexture2D、AreaLight3D、HDR 输出、新 Asset Store——本项目暂不依赖 +- 2D:CollisionShape2D 单向碰撞方向属性、conic gradient、AtlasTexture 进 TextureRect +- **GDScript 两条语义变化**(打包数组元素赋值不触发属性 setter;类型化返回覆盖必须显式 return)——详见 `current-best-practices.md` +- **新项目默认值已变**(stretch mode `canvas_items`、aspect `expand`;4.6 起 Windows 驱动 D3D12)——详见 `current-best-practices.md` +- Android/iOS/XR 增强——本项目 PC 先行,无关 + +## Migration Guides(升级/写码前必读) + +- 4.7: https://docs.godotengine.org/en/4.7/tutorials/migrating/upgrading_to_godot_4.7.html +- 4.6 / 4.5 同路径模式:`.../tutorials/migrating/upgrading_to_godot_4.6.html`、`..._4.5.html` +- 三份指南的逐条中文对照与项目相关性标注见 `breaking-changes.md`(2026-09-12 版) + +## Note + +**MEDIUM 风险处置(2026-09-12 更新)**:4.5→4.7 的 API 级破坏性变更清单已逐条核实并入 `breaking-changes.md`,deprecated → replacement 对照已并入 `deprecated-apis.md`;对本项目的结论 = **无一破坏 GDD 已锁架构契约**。残余风险两项:① 4.8 未来升级(dev 阶段,稳定版后 refresh);② 运行期边角行为(迁移指南未覆盖的引擎内部行为差异)——代理写码规则保持不变:**不确定的 API 一律查 `docs.godotengine.org/en/4.7/` 或 WebSearch 核实后再用**。 + +## Verification History + +| 日期 | 动作 | +|---|---| +| 2026-09-07 | 初次锁定 4.7.2;核实版本时间线与 4.7 特性清单 | +| 2026-09-12 | `/setup-engine refresh`:4.5/4.6/4.7 官方迁移指南逐条核实(breaking-changes.md 逐条清单 + deprecated-apis.md 对照表 + 新建 current-best-practices.md);版本检查确认 4.7.2 仍为最新稳定版(4.8 仅 dev);风险等级 HIGH → MEDIUM | diff --git a/docs/engine-reference/godot/breaking-changes.md b/docs/engine-reference/godot/breaking-changes.md new file mode 100644 index 00000000..8844209f --- /dev/null +++ b/docs/engine-reference/godot/breaking-changes.md @@ -0,0 +1,115 @@ +# Godot Breaking Changes(4.4 → 4.7) + +> **Last verified: 2026-09-12**(官方迁移指南三份逐条核实——4.5/4.6/4.7 API 级清单已补全,原 2026-09-07"待 refresh"状态解除) +> 训练数据覆盖至 ~4.4:4.4 及以前的变更视为"已知基线",4.5+ 为超出截止的增量。 +> 官方迁移指南(升级/写码前必读): +> - 4.5:https://docs.godotengine.org/en/4.7/tutorials/migrating/upgrading_to_godot_4.5.html +> - 4.6:https://docs.godotengine.org/en/4.7/tutorials/migrating/upgrading_to_godot_4.6.html +> - 4.7:https://docs.godotengine.org/en/4.7/tutorials/migrating/upgrading_to_godot_4.7.html +> +> 项目相关性图例:🔵 需要留意(本项目域内)/ ⚪ 本项目不用(3D/XR/网络/移动/GLTF/编辑器插件)。 +> 本项目形态:2D 回合制、GDScript 单语言、Control 重 UI、autoload + .tres、无 3D/物理/网络。 + +## 4.5(2025-09-15) + +### API 级变更 + +| 类/方法 | 变更 | GDScript 兼容 | 项目相关性 | +|---|---|---|---| +| `Resource.duplicate(true)` | 深复制**不再覆盖外部子资源**;旧行为需 `duplicate_deep(DEEP_DUPLICATE_ALL)` | 行为变更 | 🔵 D2 schema/表资源复制语义注意 | +| `Node.set_name` | 参数 `String` → `StringName` | 兼容 | 🔵 | +| `JSONRPC.set_scope` | 更名 `set_method` | ❌ | ⚪ | +| `Node.get_rpc_config` | 更名 `get_node_rpc_config` | ❌ | ⚪(RPC 不用) | +| `RenderingServer.instance_reset_physics_interpolation` | **移除** | ❌ | ⚪(3D 插值) | +| `RenderingServer.instance_set_interpolated` | **移除** | ❌ | ⚪ | +| TextServer/Font/CanvasItem/TextLine/TextParagraph 全部 draw 方法 | 新增可选 `oversampling` 参数 | 兼容 | 🔵 D1 自绘文字 | +| `TextServerExtension` 四个 draw 虚方法 | 同上签名变化 | ❌(全不兼容) | ⚪(不实现 TextServer 扩展) | +| `RichTextLabel.add_image`/`update_image` | `size_in_percent` → `width_in_percent` + `height_in_percent` | 兼容 | 🔵(低——富文本用图时) | +| C# `RenderingDevice.Features.Address` | 更名 `BufferDeviceAddress` | 仅 C# | ⚪(C# 不用) | +| GLTF Accessor/BufferView 十字段 | int32 → int64 / 枚举化 | 仅 C# | ⚪(GLTF 不用) | + +### 行为变更 + +- **TileMapLayer 物理分块默认开启**;`get_coords_for_body_rid()` 返回值变化;需 4.4 精确行为设 `physics_quadrant_size=1` —— 🔵 若棋盘实现走 TileMapLayer(当前设计倾向自绘,未定) +- 3D 模型导入 skeleton 行为修复(新文件新行为;旧文件需改 Import Naming Version)—— ⚪ +- Navigation 区域默认异步线程更新;navmesh 合并次序变化 —— ⚪(A2 用自研 BFS,不用 NavigationServer) +- Jolt:`areas_detect_static_bodies` 设置移除 —— ⚪ +- `ProjectSettings.add_property_info()` 对缺失/非法键(含 `usage`)告警 —— 🔵(若程序化注册设置项) + +## 4.6(2026-02) + +### API 级变更 + +| 类/方法 | 变更 | GDScript 兼容 | 项目相关性 | +|---|---|---|---| +| `FileAccess.get_as_text` | `skip_cr` 参数**移除** | ❌ | 🔵 文本读取路径 | +| `FileAccess.create_temp` | `mode_flags` int → `ModeFlags` 枚举 | 兼容性检查变化 | 🔵 | +| AnimationPlayer `assigned_animation`/`autoplay`/`current_animation` | `String` → `StringName`(`get_queue()`、`current_animation_changed` 信号参数同步) | ❌(类型不兼容) | 🔵 B5 期 | +| SpringBoneSimulator3D 三个枚举 | 移至 `SkeletonModifier3D` | ❌ | ⚪ | +| StreamPeerTCP / TCPServer | `disconnect_from_host`/`get_status`/`poll`、`is_connection_available`/`is_listening`/`stop` 上移至 `StreamPeerSocket`/`SocketServer` 基类 | 兼容(继承) | ⚪(网络不用) | +| `Control.grab_focus`/`has_focus`、`LineEdit.edit`、`SplitContainer.clamp_split_offset`、`FileDialog.add_filter` | 新增可选参数 | 兼容 | 🔵 D1 | +| EditorFileDialog | `add_side_menu` **移除**;17 方法 / 8 属性 / 4 信号并入 `FileDialog` 基类 | 兼容(继承) | ⚪(编辑器) | +| `Performance.add_custom_monitor` | 新增可选 `type` 参数 | 兼容 | 🔵(调试遥测可选) | + +### 行为 / 格式变更 + +- **TSCN 格式**:`load_steps` 不再写入、节点唯一 ID 入文件——双向兼容,但 VCS diff 变大(可 Project > Tools > Upgrade Project Files 预升级)—— 🔵 +- **Glow 默认值大改**(blend mode Soft Light → Screen、intensity 0.8 → 0.3、各 level 重排);Mobile 渲染器 glow 重写 —— 🔵 D1/B5 用 2D Glow 时注意视觉基底已变 +- AStar2D/AStar3D/AStarGrid2D:`from_id` 被禁用/固体时返回空路径 —— ⚪(自研 BFS) +- **新项目默认值**:Windows 渲染驱动 D3D12;3D 物理引擎 Jolt —— 🔵 创建 project.godot 时须知(本项目 2D、无 3D 物理,渲染驱动选择归实现期 ADR) + +## 4.7(2026-06;4.7.2 = 本项目锁定版本) + +### API 级变更 + +| 类/方法 | 变更 | GDScript 兼容 | 项目相关性 | +|---|---|---|---| +| **GDScript 语义 ①** | 打包数组元素赋值不再触发整属性 setter(GH-113228) | 语义变化 | 🔵 全部 .gd 代码 | +| **GDScript 语义 ②** | 覆盖类型化返回方法:返回类型自动继承,必须显式 `return`(GH-115763) | 语义变化 | 🔵 全部 .gd 代码 | +| `Object.is_class()` | 参数 `String` → `StringName` | 兼容 | 🔵 | +| `PhysicsServer2D.body_set_shape_as_one_way_collision` | 新增可选 `direction`(= CollisionShape2D 单向碰撞方向属性的 server 层) | 兼容 | 🔵(已核实特性的 API 面) | +| `PhysicsServer2DExtension._body_set_shape_as_one_way_collision` | 新增**必填** `direction` | ❌ | ⚪(不写 PhysicsServer 扩展) | +| `CanvasItem` 线条绘制 | 不再自动加抗锯齿羽化;粗线需手动绘制(GH-105122) | 行为变更 | 🔵 D1 自绘 | +| Input 设备 ID | 鼠标/键盘设备 ID `0` → `InputEvent.DEVICE_ID_MOUSE` / `DEVICE_ID_KEYBOARD` 常量 | 行为变更 | 🔵 D1 输入 | +| `Control.accessibility_live` | 枚举类型 `DisplayServer.` → `AccessibilityServer.AccessibilityLiveMode` | 仅 C# 不兼容 | 🔵(低) | +| RichTextLabel 图片参数 | `add_image`/`update_image` width/height int→float;`width_in_percent/height_in_percent` → `width_unit/height_unit`;常量 `UPDATE_WIDTH_IN_PERCENT` → `UPDATE_WIDTH_UNIT` | ❌(常量更名) | 🔵(低) | +| `Animation.length` | 元数据 float → double | 仅 C# 不兼容 | 🔵(低,B5 期) | +| AnimationNodeBlendSpace1D/2D | 布尔 `sync` 属性 → `SyncMode` 枚举(`sync_mode`) | 行为变更 | ⚪(3D 动画树) | +| `Font.find_variation` / `TreeItem.select` / `ZIPPacker.start_file` / `Image.save_exr`×2 | 新增可选参数 | 兼容 | ⚪/🔵 | +| `AudioEffectSpectrumAnalyzer.tap_back_pos` | **移除** | ❌ | ⚪(D4 Alpha 期若用频谱分析注意) | +| `EditorSceneFormatImporter` 七常量 | 并入 `ImportFlags` 枚举 | 仅 C# 源不兼容 | ⚪ | +| `EditorVCSInterface._commit` | 新增 `amend` 参数 | ❌ | ⚪ | +| `OptimizedTranslation.generate` | 返回 void → bool | 仅 C# 二进制 | ⚪ | +| `AudioStreamPlayer.area_mask` | 默认 1 → 0 | 行为变更 | ⚪(D4 Alpha 期注意) | + +### 行为 / 默认值变更 + +- `LinearToSRGB` visual shader 不再 clamp 到 [0,1](Mobile/Forward+)—— 🔵(若写 shader) +- **新项目默认值**:stretch mode `disabled` → `canvas_items`、stretch aspect `keep` → `expand` —— 🔵 **创建 project.godot 时须知(训练数据默认值已过时)** +- macOS 最低版本 10.13 → 11 —— ⚪ +- `LookAtModifier3D.relative` 默认 true → false —— ⚪ +- `ResourceImporterDynamicFont.hinting` 默认 1 → 3 —— 🔵(字体导入) +- `rendering/reflections/sky_reflections/roughness_layers` 默认 7 → 8 —— ⚪(3D) + +## 已知基线(≤4.4,训练数据内,低风险) + +- 4.x 系列:Tween 一律经 `create_tween()` 获取(Tween 节点已废);信号连接用 `Callable` 语法;`@onready`/`@export` 注解体系;int 恒 64 位(D2 R2 规约依赖此语义,4.x 内稳定) + +## 本项目影响评估(2026-09-12 复核) + +| 变更 | 影响 | +|---|---| +| 4.7 全部已核实特性 | 均为增量,无破坏 D2/A3 已批准规约的项 | +| **4.5–4.7 逐条 API 变更** | **无一破坏 GDD 已锁架构契约**(autoload 体系 / .tres 单一事实源 / Callable 信号 / int64 二补数 / headless SceneTree 协议均不受影响) | +| 需入编码规范的项 | GDScript 两条新语义(打包数组 setter / 显式 return)→ `current-best-practices.md` | +| 需项目设置留意的项 | 新项目默认值已变(stretch mode / aspect / D3D12)→ `current-best-practices.md` | +| `Resource.duplicate(true)` 语义收窄 | D2 表资源含外部子资源时的复制语义注意 | + +## Pending + +- [x] 4.4→4.5 breaking changes 逐条清单(2026-09-12 官方迁移指南) +- [x] 4.5→4.6 breaking changes 逐条清单(2026-09-12 官方迁移指南) +- [x] 4.6→4.7 breaking changes 逐条清单(2026-09-12 官方迁移指南) +- [x] 各版本 deprecated → replacement 对照表(见 `deprecated-apis.md`,2026-09-12) +- [ ] 4.8 逐条清单(当前仅 dev 快照;稳定版发布后跑 `/setup-engine refresh`) +- [ ] modules/ 分域参考(按实现期需要建,非阻塞) diff --git a/docs/engine-reference/godot/current-best-practices.md b/docs/engine-reference/godot/current-best-practices.md new file mode 100644 index 00000000..d9e72399 --- /dev/null +++ b/docs/engine-reference/godot/current-best-practices.md @@ -0,0 +1,49 @@ +# Godot 当前最佳实践(4.7 视角,post-cutoff) + +> **Last verified: 2026-09-12**(源:官方迁移指南 4.5/4.6/4.7 逐条核实 + 4.7 release 页) +> 用途:训练数据(~4.4)之后的实践差异速查。写码代理在以下事项上**不要**按训练数据默认值行事。 + +## 1. 新建 project.godot 的默认值差异(最易踩坑) + +| 设置 | 训练数据默认(≤4.4) | 4.7 实际新项目默认 | 生效版本 | +|---|---|---|---| +| `display/window/stretch/mode` | `disabled` | `canvas_items` | 4.7(新项目) | +| `display/window/stretch/aspect` | `keep` | `expand` | 4.7(新项目) | +| `rendering/rendering_device/driver.windows` | Vulkan(Forward+) | D3D12 | 4.6(新项目) | +| `physics/3d/physics_engine` | GodotPhysics3D | Jolt | 4.6(新项目;本项目 2D、无 3D 物理,无关) | + +本项目为 2D 棋盘 UI:stretch mode / aspect 的最终选择归渲染器实现期 ADR,但须知道**引擎新项目默认值已不是训练数据里的那个**——新建工程后检查这三项。 + +## 2. GDScript 4.7 两条语义变化(建议入编码规范) + +1. **打包数组元素赋值不再触发属性 setter**(4.7,GH-113228):`my_packed_array[i] = v` 不再调用所在对象的 property setter——依赖 setter 副作用的写法必须改为显式赋回 + 显式调用 setter。D2 schema 类若以 setter 做校验,注意此差异。 +2. **类型化返回的方法覆盖必须显式 `return`**(4.7,GH-115763):覆盖带返回类型的方法时返回类型自动继承,缺少 return 直接报错(旧版隐式返回 null)——虚方法覆盖一律显式写 return。 + +## 3. 资源与文件 + +- `Resource.duplicate(true)`(4.5 起):深复制**只覆盖内部子资源**,不再覆盖外部子资源;需旧行为用 `duplicate_deep(DEEP_DUPLICATE_ALL)`。D2 表资源含外部子资源时注意复制语义。 +- `FileAccess.get_as_text()` 的 `skip_cr` 参数已移除(4.6)——换行处理如需自定义须自行实现。 +- TSCN 格式(4.6):`load_steps` 不再写入、节点唯一 ID 入文件——双向兼容,但 VCS diff 会变大(可 Project > Tools > Upgrade Project Files 预升级,减少一次性大 diff)。 +- `ProjectSettings.add_property_info()`(4.5):对缺失/非法键(含 `usage`)告警;改用 `set_as_basic()` / `set_restart_if_changed()` / `set_as_internal()`。 + +## 4. 2D / UI / 文本 + +- `CanvasItem` 线条绘制**不再自动加抗锯齿羽化**(4.7)——粗线/描边需手动方案(D1 自绘棋盘格边框时注意)。 +- 输入设备 ID:比较输入来源时用 `InputEvent.DEVICE_ID_MOUSE` / `DEVICE_ID_KEYBOARD` 常量,**不要硬编码 0**(4.7)——D1 输入路由。 +- 字体导入 `hinting` 默认 1 → 3(4.7);TextServer/Font/CanvasItem 全部 draw 方法新增可选 `oversampling` 参数(4.5)——自绘文字(数字标签)可显式控制过采样。 +- RichTextLabel 图片参数两轮变化:4.5 `size_in_percent` → `width_in_percent`+`height_in_percent`;4.7 → `width_unit`/`height_unit`(bool → `ImageUnit`),常量 `UPDATE_WIDTH_IN_PERCENT` → `UPDATE_WIDTH_UNIT`。 +- **Glow 默认值大改**(4.6):blend mode Soft Light → Screen、intensity 0.8 → 0.3、各 level 重排——2D 后处理发光的视觉基底已变,调参以 4.7 默认为起点。 +- `Control.grab_focus` / `has_focus` 新增可选参数(4.6,`hide_focus` / `ignore_hidden_focus`)——焦点可视化管理更细。 + +## 5. 4.7 已核实新特性(本项目可用的增量) + +- `Tween.tween_await()`:信号等待语法糖——**注意** B1 结算逻辑禁 await 是设计约束(可回放步骤流),该 API 不改变该约束 +- Control `offset_transform_*`:平移/旋转/缩放 Control 不影响容器布局——D1 HUD 局部动效可用 +- CollisionShape2D 单向碰撞方向属性(+ `PhysicsServer2D.body_set_shape_as_one_way_collision` 的 `direction` 参数)——本项目棋盘非物理驱动,暂不依赖 +- conic gradient、AtlasTexture 进 TextureRect——D1 贴图/着色可选 +- DrawableTexture2D、AreaLight3D、HDR 输出、内置 VirtualJoystick、新 Asset Store——本项目暂不依赖 +- `Font.find_variation` 扩展(palette_index / custom_colors)、`TreeItem.select` 扩展——低相关 + +## 6. 本项目不消费的域(相关变更可忽略) + +3D 渲染与物理(Jolt 行为变化 / SoftBody / WorldBoundary / SpringBone / LookAtModifier)、XR/OpenXR、网络(StreamPeerTCP/TCPServer 方法上移、RPC 更名)、GLTF 导入(int64 字段)、移动端导出(Android 模板结构变化、.NET 9)、编辑器插件 API(ImportFlags / VCS / ExportPlatform)。 diff --git a/docs/engine-reference/godot/deprecated-apis.md b/docs/engine-reference/godot/deprecated-apis.md new file mode 100644 index 00000000..bbf3b25b --- /dev/null +++ b/docs/engine-reference/godot/deprecated-apis.md @@ -0,0 +1,51 @@ +# Godot Deprecated APIs(Don't use X → Use Y) + +> **Last verified: 2026-09-12** +> 4.5–4.7 移除/更名/签名变化对照已补全(源 = 官方迁移指南,2026-09-12 逐条核实)。仍不含 4.8(dev 阶段)。**不要向本表添加未经验证的条目。** + +## 4.7 release 页核实 + +- 4.7 release 页未列出新的 API 弃用;完整清单以其迁移指南为准(已逐条核实,见下节) + +## 4.5–4.7 移除 / 更名 / 签名变化对照(源 = 官方迁移指南) + +| Don't use | Use | Since | GDScript 影响 | 项目相关性 | +|---|---|---|---|---| +| `RenderingServer.instance_reset_physics_interpolation()` | (无替代——API 移除) | 4.5 | ❌ 移除 | ⚪ | +| `RenderingServer.instance_set_interpolated()` | (无替代——API 移除) | 4.5 | ❌ 移除 | ⚪ | +| `JSONRPC.set_scope()` | `set_method()` | 4.5 | ❌ 更名 | ⚪ | +| `Node.get_rpc_config()` | `get_node_rpc_config()` | 4.5 | ❌ 更名 | ⚪ | +| `Node.set_name(name: String)` | `set_name(name: StringName)` | 4.5 | 兼容(自动转换) | 🔵 | +| `TextServerExtension` 四个 draw 虚方法 | 签名新增 `oversampling` | 4.5 | ❌(虚方法覆盖不兼容) | ⚪ | +| RichTextLabel `size_in_percent` 参数 | `width_in_percent` + `height_in_percent` | 4.5 | 兼容(参数拆分) | 🔵(低) | +| `FileAccess.get_as_text(skip_cr)` | `get_as_text()`(**skip_cr 参数移除**) | 4.6 | ❌ 参数移除 | 🔵 | +| `EditorFileDialog.add_side_menu()` | (无替代;EditorFileDialog 17 方法/8 属性/4 信号并入 `FileDialog`) | 4.6 | ❌ 移除 | ⚪ | +| AnimationPlayer `assigned_animation` / `autoplay` / `current_animation`(String) | `StringName`(含 `get_queue()`、`current_animation_changed` 参数) | 4.6 | ❌ 类型不兼容 | 🔵(B5 期) | +| SpringBoneSimulator3D 三个枚举 | `SkeletonModifier3D.BoneDirection` / `.RotationAxis` | 4.6 | ❌ | ⚪ | +| `AudioEffectSpectrumAnalyzer.tap_back_pos` | (无替代——属性移除) | 4.7 | ❌ 移除 | ⚪(D4 期注意) | +| AnimationNodeBlendSpace1D/2D `sync: bool` 属性 | `sync_mode: SyncMode` 枚举 | 4.7 | ❌ 属性类型变化 | ⚪ | +| RichTextLabel `ImageUpdateMask.UPDATE_WIDTH_IN_PERCENT` | `UPDATE_WIDTH_UNIT`;`width/height_in_percent` 参数 → `width/height_unit`(bool → `ImageUnit`) | 4.7 | ❌ 常量/参数更名 | 🔵(低) | +| `Object.is_class("X")`(String 字面量) | `is_class(&"X")`(StringName) | 4.7 | 兼容 | 🔵 | +| `EditorVCSInterface._commit()` | 签名新增 `amend` | 4.7 | ❌(虚方法覆盖不兼容) | ⚪ | +| `PhysicsServer2DExtension._body_set_shape_as_one_way_collision()` | 签名新增必填 `direction` | 4.7 | ❌(虚方法覆盖不兼容) | ⚪ | +| C# `RenderingDevice.Features.Address` | `BufferDeviceAddress` | 4.5 | 仅 C# | ⚪ | +| GLTF Accessor/BufferView 十字段(int) | int64 / 强类型枚举 | 4.5 | 仅 C# | ⚪ | + +## 训练数据内基线(≤4.4,高置信,本项目相关) + +| Don't use | Use | Since | 备注 | +|---|---|---|---| +| `Tween` 节点 / `create_tween()` 以外的补间获取 | `create_tween()` / `tween_property()` | 4.0 | Tween 节点已移除 | +| 旧式 `connect("sig", instance, "method")` | `signal.connect(callable)` | 4.0 | Callable 语法 | +| `onready var` | `@onready var` | 4.0 | 注解体系 | +| `export var` | `@export var` | 4.0 | | +| `RandomNumberGenerator` 自建实例(玩法侧) | `RngService.open_stream(stream)` | 本项目 | D2 Rule 5(项目级禁令,非引擎弃用) | +| `randomize()`(玩法代码) | 禁止——种子归 `RngService.set_master_seed()`(C2 调用) | 本项目 | D2 Rule 5 | +| `yield` | `await` | 4.0 | 4.7 新增 `Tween.tween_await()`;但 B1 结算逻辑禁 await(设计约束) | + +## Pending + +- [x] 4.5 deprecated → replacement 对照(2026-09-12) +- [x] 4.6 deprecated → replacement 对照(2026-09-12) +- [x] 4.7 deprecated → replacement 对照(2026-09-12) +- [ ] 4.8 对照(dev 阶段;稳定版发布后 `/setup-engine refresh`) diff --git a/docs/lean-gdd-sample-b1.md b/docs/lean-gdd-sample-b1.md new file mode 100644 index 00000000..b214f30a --- /dev/null +++ b/docs/lean-gdd-sample-b1.md @@ -0,0 +1,311 @@ +# B1 战斗结算规则(lean 格式样例) + +> **状态**:格式样例——用新格式重排现有 `b1-settlement.md` 的内容域,供评审,非生效文档 +> **版本**:lean-sample · 2026-09-13 · 作者:user + agents +> **一句话**:B1 把每轮扫雷的结果逐行兑现为战斗结果,并判定胜负。 + +## 速览 + +| 关键问题 | 答案 | +|---|---| +| 定位 | 战斗域编排与结算中枢;轮次生命周期唯一 owner | +| 拥有 | 段序、判定时机、伤害消费侧语义、明细流水协议、上游义务账本(B3×8 / B2×6 / A2×3 / B4×6) | +| 不拥有 | 单位状态(B2)· 状态实例(B3)· 奖励内容(B4)· 行动价目(A3)· 棋盘规则(A1/A2)· 呈现(D1/B5) | +| 上游输入 | A2 三事件流 · A3 相位观测量 · 各表值快照 | +| 下游输出 | 结算步骤流(D1/B5/D4 消费)· 条款③⑤对账结论(C3 约束输入) | +| 硬约束 | 零随机 · 零 UI 依赖 · 结算禁 await——同种子同输入 ⇒ 步骤流逐行相同 | + +## 体验锚点 + +| 支柱 | 关键词 | 机制承诺 | +|---|---|---| +| P2 因果归因 | "都是我算出来的" | 每行明细携带来源标注;无来源行 = 断言失败(AC-42) | +| P1 掌控感 | "挖下去值得" | 条款③不等式由本系统闭合签字 | +| P3 雷的两面 | "赌桌不是陷阱" | 敌方伏击行与标中旗削弱行同框、同呈现语言 | +| 高光节拍 | "完美被加倍兑现" | 完美排雷双份兑付 + 标中旗行高光 | + +## 全局流程 + +### 战斗开始 + +``` +begin_battle(encounter) + │ ① set_reload_gate(true) [D2] 调试门置位 + │ ② notify_battle_start() [B3] 容器开域 + │ ③ spawn(L, D, hero_hp) [B2] 行值快照实例化 + │ ④ install_board + roll_board [A2/B4] 恰一次;资格格清单 = S ∖ R₀ + │ ⑤ notify_round_start() [A3] → Active + ▼ +RoundActive(挖掘期,B1 只缓冲事件流) +``` + +### 每轮结算(七段骨架,全序执行、段序结构冻结) + +``` +轮末触发:Waiting 相位编排点(只读 A3 相位观测量,不依赖事件到达序) + │ + ▼ +①授予段 ──── cell_revealed 批 → flag_judged 批 → 清图奖励(仅 cleared 轮) + │ (转化规则见下方两表) + ▼ +②道具使用窗口 ─ 玩家至多选一件或跳过(全流程唯一玩家输入点) + │ 满血禁选治疗包;空库存可整体不弹窗 + ▼ +③增益/减益段 ─ query_active 双侧读值 → SettlementTable 语义映射 → 效果明细行 + │ + ▼ +④道具段 ───── 伤害道具:恒定表值打 default_target;治疗包:clamp 回复 + │ 施加时无存活敌 → 照常消耗 + "已消耗未生效"行 + ▼ +⑤战斗段 ───── 主角先手攻击(D_hero = atk ×(1+attack_up)) + │ → 逐存活敌(行主序):query_attack → apply_damage → advance + │ ※ hero hp=0 → 立即短路,剩余敌不作用,直入 ⑦ + ▼ +⑥衰减段 ───── decay_round() 恰一次(含末轮;先兑付后衰减的时序契约) + │ + ▼ +⑦判定段 ───── hero hp=0 → 失败 │ 存活敌=∅ → 胜利 │ 否则 → 下一轮 +``` + +### 出口分发 + +| 结局 | 触发 | 动作序列 | 约束 | +|---|---|---|---| +| 继续 | 判定=下一轮 | `notify_settlement_done(same_board)` → `notify_round_start()` | 同图续轮 | +| 换图 | cleared 轮 | `retire_board` → `retire_mapping` → A1 `generate` → `install_board` → `roll_board` 恰一次 → `notify_settlement_done(new_board)` → `notify_round_start()` | stopped/ap_exhausted 恒 same_board,违约 fail-fast | +| 挖掘期刷新 | 玩家经 D1 发起 | A3 `request_refresh` 扣费 → 换图全序(无结算段、无 round_end、轮次不终结) | RAP 不足→拒绝上抛;每图 cap 2;刷新图无清图奖励 | +| 战斗结束 | 判定=胜/负 | `retire_mapping` → `despawn_all` → `notify_battle_end` → `notify_combat_end` → `set_reload_gate(false)` | 库存不清(关内延续) | +| 关卡结束 | 上层显式调用 | `clear_inventory()` 恰一次,清空前快照计数返回 | victory/defeat 皆然 | + +## 核心规则 + +| # | 规则 | 要点 | +|---|---|---| +| 1 | 编排唯一性 | B1 是战斗域全部跨系统调用的唯一发起方;B2/B3/B4 零相互直调 | +| 2 | 结算触发 | Waiting 相位编排点;不依赖事件到达序 | +| 3 | facts 缓冲 | 按轮缓冲事件、轮末统一转化;事实→授予恰一次(B4 幂等仅兜底) | +| 4 | 段序骨架 | 七段全序(见上图);修订走 GDD 流程,非调参 | +| 5 | 授予段 | 三批转化,见下两表 | +| 6 | 伏击转化 | `ambush_grant_plan(mines_r, cap)`;前 granted_r 颗逐颗授予;池 L side 共享、dur=1 轮末清零 | +| 7 | 道具窗口 | B4 `query_inventory` 投影 + B1 守卫预判;至多一件;选择序列入回放 | +| 8 | 增益/减益 | 读值→语义映射→池合成(F2);明细按授予流水逐条标注来源 | +| 9 | 道具段 | 伤害恒定表值不吃修正;满血守卫拒治疗;零效果判定读 B2 存活集 | +| 10 | 战斗段 | 主角先手攻击 + 逐敌循环(调用序钉死);死亡即不动手;hero 死亡短路 | +| 11 | 衰减段 | 恰一次、恒执行、含末轮 | +| 12 | 判定段 | hero 先判(防御性优先级);胜负明细行 | +| 13 | 高光节点 | #2 完美排雷=清图奖励双份兑付;#3 标中旗=行字段 highlight(见枚举表) | +| 14 | 明细流水协议 | 13 类行封闭枚举 + 公共字段(见枚举表)——D1/B5/D4 的统一数据源契约 | +| 15 | 可回放性 | 零随机 + 确定性输入 + 表值快照 ⇒ 同种子同输入步骤流逐行相同 | +| 16 | 数值承载 | 全部数值在 D2 SettlementTable;B1 代码零数值字面量;战斗中热重载被门拒绝 | + +### 授予段:三批转化 + +| 批 | 输入 | 转化 | 产出 | +|---|---|---|---| +| cell_revealed | `kind=mine` | 调 `ambush_grant_plan`,事件规范序逐颗 `apply_effect(ambush)` | 伏击池 L;溢出产"溢出未生效"行 | +| cell_revealed | `kind=safe ∧ reward_eligible` | 按 category 分流(见下表) | 每格受理恰一次 | +| flag_judged | `hit=true` 且资格未消费 | `apply_effect(weaken)` + 资格消费 | 标中旗行(highlight=`flag_hit`) | +| flag_judged | `hit=true` 且资格已消费 | 去重跳过(每雷实例一次) | 零新增行 | +| flag_judged | `hit=false` | 零兑付 | "标旗未中"零值行(highlight=`perfect_broken`) | +| cleared 轮 | — | `grant_clear()` 恰一次;完美板(零误标 ∧ ≥1 标中)追加第二份同身份兑付 | 清图奖励行;完美高光行 | + +### 授予段:奖励分流 + +| category | 兑付方式 | 拒绝语义 | +|---|---|---| +| buff | `apply_effect`(入池,结算段生效) | 满层 → "奖励未生效"行,无折算 | +| instant_aoe | 全体存活敌各 `power` 点(逐个 apply_damage) | 当场兑付,不进库存 | +| instant_heal | 主角回复 `power`(clamp) | 满血 → "奖励未生效"行 | +| damage_item / heal_pack | `stock()` 入关卡域库存 | 窗口选用时兑付 | + +## 枚举 + +### 明细行类型(封闭枚举,13 类) + +| 行类型 | 特有字段 | 产出点 | 说明 | +|---|---|---|---| +| 效果行 | `effect_id` `pool_value` | 增益/减益段 | value=该次授予贡献值;pool_value=池合成值(防 −130%×2 行误读为 −260%) | +| 伤害行 | `target` | 道具段 / 战斗段 | 分量二元组 + 减伤终值 + 击杀标记 | +| 治疗行 | — | 道具段 | clamp 后真实回复量 | +| 道具行 | `target` | 授予段 / 道具段 | 入库 / 生效 / 语境注记 | +| 奖励未生效行 | 聚合计数 | 授予段 | 满层拒绝、满血、雾板等合法例外 | +| 已消耗未生效行 | — | 道具段 | 施加时已无存活敌(Used 不可逆) | +| 使用被拒行 | — | 道具段 | 满血治疗包守卫 | +| 溢出未生效行 | — | 授予段 | 伏击/奖励族同名协议 | +| 标旗未中行 | highlight=`perfect_broken` | 授予段 | 零值归因行;每雷实例一次去重 | +| 蓄力行 | — | 战斗段 | charge 拍零作用 | +| 衰减行 | — | 衰减段 | 移除数 k(呈现聚合归 D1) | +| 判定行 | — | 判定段 | 胜负 / 继续 | +| 高光行 | — | 授予段(清图) | 完美排雷;value=实际有效兑付,禁记名义值 | + +**公共字段**(13 类全必有):`type` `source_class` `origin_cell`(板级行可空)`round_index` `display_name` `value` `highlight?` +**断言**:无来源行 = 失败(AC-42)。 + +### source_class(封闭枚举) + +| 值 | 来源 | +|---|---| +| safe_cell / flag_hit / mine_hit / clear_default | B3 既有四源 | +| 道具/判定扩展 | B4 交付语义 | +| enemy_action | 敌方行动行(伤害/蓄力) | +| flag_miss | 标旗未中零值行 | +| hero_attack | 主角先手攻击伤害行 | + +新增值 = 接口漂移,须同步 `source_label_map` 键集。 + +### highlight(可空枚举) + +| 值 | 含义 | +|---|---| +| `flag_hit` | 标中旗削弱兑付行强调 | +| `clear_reward_h2` | 清图奖励行强调 | +| `perfect_broken` | 本板完美谓词已破的归因锚 | + +## 数值结论与推导 + +> 章节纪律:**结论先行**(加粗一行),下接「推导」小标题给公式与验算表。F1 对照表 / F3 伤害合成迁移时同模式展开(Variables 表 + Example 一行)。 + +### F0 当量归一口径(weaken 锚裁决) + +**结论**:采用"每结算瞬时当量"口径;m_w 值域 [60, 85]、默认 −65;地板交叉层数 = 2;B3/B4 零接口修订(值变更走 registry 回写)。 + +#### 推导 + +- **同分母性**:weaken 与 defense_up 作用于同一伤害流,"严格大于"在百分点读法与 HP 读法下等价,锚判定无量纲歧义。 +- **机制同构**:B3 本就按结算逐次兑付,瞬时口径是容器原生口径;"每 AP 总暴露"需跨结算累计账本,与已锁的"单结算自然边界"冲突。 +- **schema 禁止**:总暴露口径需 m_w > 100,越出 percent ∈ [−100, 100] 域,改域 = 三侧换算连锁,成本不成比例。 + +### F2 乘区合成 + +**结论**:池内加算 → 每池独立 clamp [−100, +100] → 池间乘算 → floor 单一取整;免伤轮(D'=0)是合法设计态,产零值伤害行、不调 apply_damage。 + +#### 公式 + +``` +P_pool = Σ(同池实例 L×m) 池内加算 +P_pool ← clamp(P_pool, −100, +100) 每池独立地板 +M_pool = 1 + P_pool ÷ 100 域 [0, 2] +D' = floor(D_raw × M_hero × M_enemy) 池间乘算,单一取整 +``` + +例:D_raw=20、weaken L=2 → P=−130 → clamp −100 → M=0 → D'=0(零值伤害行)。 + +### F4 条款③挖掘风险不等式 + +**结论**:默认行值通过(锚行口径 L1≈2.0× / L2≈2.6×;跨行期望口径 1.2× / 1.5×,二元结论一致);满配 unit_count=3 破线 → 登记 C3 调参约束。actuals p10 交付后强制重跑。 + +#### 公式与验算 + +``` +主式:P(安全) × V_safe > P(雷) × V_mine (决策单位=单格挖掘;评估点=残余盲猜端最坏点) +P(雷) = |M| ÷ (|M| + (1−ι)×|S|) (残余盲猜端上界) +V_safe = D_ref(锚行满寿命恒等式);V_mine = unit_count × m_amb +域守卫:(1−ι)×|S| < 1 ⇒ 主式不评估,信息端平凡腿接管 +``` + +| 板形 | P(雷) | 左臂 | 右臂 | 裕度 | 判定 | +|---|---|---|---|---|---| +| L1(\|M\|=9,\|S\|=91,ι=0.95) | 0.664 | 4.03 | 1.99 | ≈2.0× | ✓ | +| L2(\|M\|=16,\|S\|=128,ι=0.92) | 0.610 | 4.68 | 1.83 | ≈2.6× | ✓ | +| 满配(unit_count=3,V_mine=9) | 0.664 | 4.03 | 5.98 | <1 | ✗ 破线 → C3 约束 | + +注:C3 不得以锚行读数(2.0×/2.6×)作为 unit_count=2 的放行依据。 + +### F5 条款⑤清图支配性 + +**结论**:**未闭合**(挖穿残余池计价,两态为负)→ C3/B4 约束包:C 量级上调,或裁"清图非严格支配"为设计态,二选一归 C3。 + +#### 公式 + +``` +Δ_clear = [R_res + C_eff] − [X_res + Δr × s_ref] (主判式,≥0 为闭合) +E[踩雷] = |M| × n_s ÷ (n_s + 1) (挖穿残余池期望,均匀交错模型) +``` + +### 边界参数总表 + +| 参数 | 边界 | 默认 | 出处 | +|---|---|---|---| +| percent 池 clamp | [−100, +100] pp,每池独立收口 | — | F2 | +| 池乘数域 | [0, 2](= 1 + pp/100) | — | F2 | +| m_w(weaken 行值) | [60, 85] | −65 | F0 裁决(瞬时当量口径) | +| 地板交叉层数 | ⌈100/m_w⌉ = 2(值域内恒 2) | — | F0 | +| P_perfect 载体域 | **{100, 200}**,域外加载致命 | 200(×2 双份交付) | Core Rule 13 | +| D_ref(当量参考敞口) | [1, 999] | 12 | SettlementTable | +| HeroTable.atk | [1, 999],0 加载拒绝 | 6 | 2026-09-11 拍板 | +| 伤害 D' | 整数 [0, ∞),零负值分支 | — | F3 | +| D' = 0 | **不调** apply_damage,产零值伤害行 | — | 负值分支禁令 | +| 取整 | floor,单一规则(结构性,非 knob) | — | F2 | +| 溢杀 | 丢弃,不转移、无负 hp 态 | — | F3 | +| 治疗 | hp' = min(hp + power, max_hp) | — | F3 | + +### SettlementTable schema(单行全局,B1 回填 owner) + +| 字段 | 类型/域 | 默认 | knob | +|---|---|---|---| +| equivalence_ref_damage(D_ref) | int [1,999] | 12 | ✓(变更触发全对账重跑) | +| p_perfect | percent {100, 200} | 200 | ✓ | +| effect_pool_map | effect_id → {hero_offense, hero_defense, enemy_output, flat_attach} | — | ✗ | +| rounding_direction | floor(结构性) | floor | ✗ | +| pool_clamp_lo / pool_clamp_hi | int | −100 / +100 | ✗ | +| source_label_map | 键集 = source_class 全集 | — | 随接口漂移 | + +## 状态 + +| 状态 | 进入条件 | 行为/出口 | +|---|---|---| +| Uninit | 冷启动 / 清理完成 | 唯一出口 `begin_battle` | +| BattleSetup | `begin_battle` 受理 | 战斗开始序列;任一步拒绝 = fail-fast 上抛 | +| RoundActive | `notify_round_start` | 不干预挖掘,只缓冲事件流 | +| RoundWaiting | 轮次结束事务达成 | 执行七段结算;窗口段为唯一玩家输入点 | +| BattleOver | 判定段产胜负 | 战斗结束序列 → Uninit | + +不变式:同时刻至多一个结算事务在飞;段序恒全序(短路是段内规则非跳段);跨系统调用集中在 B1。 + +## 边界情况 + +| 条件 | 行为 | 依据 | +|---|---|---| +| 末图清图即胜利 | 清图奖励先兑付 → 窗口可选用 → 战斗段空转 → 衰减 → 判胜 | B4 E15 | +| 战斗段 hero hp=0 | 立即短路,剩余敌不作用不产行 | 用户拍板 | +| 判定时敌我双 hp=0 | 结构不可达;防御规则 hero 先判 | Core Rule 12 | +| 合成后 D'=0 | 不调 apply_damage,产零值伤害行 | 负值分支禁令 | +| 伤害溢出 | 溢出丢弃;行 value=D'、击杀布尔,不记溢出量 | F3 | +| charge 拍本轮踩雷 | 伏击授予照常;战斗段零伤害,池 dur=1 蒸发 | B2 EC8 | +| 全体存活敌同拍 charge | N 行蓄力行、零伤害——合法状态 | B2 EC | +| 伏击池 L=0 时敌攻击 | D_raw=纯基线 atk,自然读值非缺失分支 | B2 F2 | +| 刀施加时已无存活敌 | 照常消耗 + "已消耗未生效"行 | B4 E10 | +| 窗口打开时库存空 | 跳过合法;D1 可不弹窗;段序零停顿 | B4 E8 | +| 挖掘期刷新 RAP 不足 | `request_refresh` 返回 false,原样上抛拒绝 | A3 EC13 | +| 同轮多次刷新(cap 内) | 每次独立走换图全序,取新 scope | A3 EC15 | +| 清图槽 buff 且满层 | 零兑付 + "奖励未生效"行;完美高光降级不发 | B4 E11 | +| 完美谓词在刷新图上 | 刷新图无兑付点,不触发;flag 计数随 install 重置 | Core Rule 13 | +| 板内既有标中又有误标 | 完美不成立;首个误标产"标旗未中"行(perfect_broken) | 单档完美,两档扩展被否 | +| 多敌同结算攻击 | 行主序逐敌、共享 L;任一作用后 hero 死 → 短路 | Core Rule 10 | +| battle_over 清理中被拒 | 诊断 + 幂等兜底;清理序列必须完成 | 防半清理态泄漏 | +| 战斗中热重载请求 | 门已置位 → D2 拒绝返回 false | D2 EC8 | + +## 依赖 + +**上游**(除 C1/C2 外全部 Approved——B1 是首个上游全就绪的系统): + +| 系统 | 性质 | 接口要点 | +|---|---|---| +| D2 | hard | SettlementTable(B1 回填)+ `set_reload_gate` 唯一调用方 | +| A3 | hard | 五条款下游契约(B1 为①②③⑤闭合 owner)+ 结算回发 + `last_round_end` | +| A2 | hard(订阅) | 三事件流 + `install_board`/`retire_board` + Waiting 观测量 | +| B2 / B3 / B4 | hard | 全调用面 + 各自义务清单(义务明细在 registry,不在本文) | +| A1 | 间接 hard | `generate` 换图 + actuals p10 对账输入 | +| C1/C2 | provisional(未设计) | encounter 上下文由 `begin_battle` 入参封装隔离 | + +**下游**:D1(明细流水=统一数据源契约,呈现义务台账在 registry)· B5(步骤流=动画回放脚本源)· D4/D5(音频/教学事件源)· C3(对账结论=调参约束)。 + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| lean-sample | 2026-09-13 | 格式重排样例:修订注记出正文→版本表;规则/枚举/边界/边界情况转表格;流程转字符图。语义与 b1-settlement.md Rev 1 对齐,未改任何现行规则 | +| Rev 1 | 2026-09-10 | design-review 8 项 blocking 核销(完美排雷双份载体、条款⑤计价模型重推、流水协议分层、hit=false 零值行、P_perfect 换算成文等)——明细见 review log | +| v1 | 2026-09-10 | 一次会话定稿(F0–F5 + 43 条 AC + 23 条上游义务落位) | +| D4 修订 | 2026-09-12 | 刷新决策前移挖掘期:新增"挖掘期刷新"出口行,作废原 Waiting 期代理调用 | +| 2026-09-11 拍板批 | 2026-09-11 | 主角先手攻击(hero_attack 来源)、两级奖励、窗口单件制、库存关内延续 | diff --git a/docs/prototype-report-minesweeper-battle-concept.md b/docs/prototype-report-minesweeper-battle-concept.md new file mode 100644 index 00000000..aae4c745 --- /dev/null +++ b/docs/prototype-report-minesweeper-battle-concept.md @@ -0,0 +1,45 @@ +# 概念原型报告:扫雷 × 自动战斗 + +- **原型位置**:`prototypes/minesweeper-battle-concept/`(Godot 4.x,Engine 路径) +- **日期**:2026-09-06 · 迭代 v1 → v3 +- **可证伪假设**:如果玩家通过扫雷决策为自动战斗获取道具,他们会感受到"每一步排雷都在影响战斗结果"的策略联动感。(验证信号:扫雷阶段主动规划目标;主动想进入下一轮) + +## 假设判定:CONFIRMED(核心) / 部分确认(表现层) + +用户(唯一测试者)在 v2 后反馈:"**目前整体轮次的玩法是没有问题的**",且确认小人边缘探索"更像地牢探险,氛围上 OK"。三层联动(挖雷→攒道具→结算兑现)作为循环骨架被接受并持续投入迭代——没有出现"战斗是过场"的否定信号。表现层(逐行结算文字)被判定"无所谓"(正式版将替换为战斗动画),兑现的**情绪强化**仍有待战斗动画验证。 + +## 迭代记录 + +| 版本 | 内容 | 用户反馈 | +|---|---|---| +| v1 | 全图点击挖雷,AP 12,逐格即时奖励 | AP 太多;想要小人控制;开/未开格难分辨;结算应统一画面而非即时生效 | +| v2 | 小人控制(参考 MS Minesweeper Adventure 模式)、初始 3×3 安全区、AP 8、统一战斗结算画面、色块区分 | 地牢氛围 OK;结算画面无所谓(正式版上战斗动画);**整体轮次玩法没有问题** | +| v3 | 行动经济统一(插旗 1AP+1旗)、标雷判定(标中+5 拆弹奖励/标错无惩罚)、修延续图旗子重置 bug | 待终验 | + +## 最好时刻 + +小人控制上线后循环"活"了:从"选格子"变成"走位规划",挖/旗被锁定在小人相邻——位置本身成为资源(用户认可地牢探险感)。 + +## 最糟时刻 / 已修复问题 + +- v1 的 12 AP 节奏拖沓 → v2 降到 8 并引入走位成本 +- v1 奖励即时生效导致"点一格就像结算一次" → v2 改为统一入库、轮末结算 +- v3 前的 bug:延续图上旗子未随轮次重置(旗子资源凭空变多)→ 已修 + +## 意外发现 + +- 标雷判定把"插旗"从记账动作变成了**赌注式动作**——风险与收益同源原则(P3)的雏形在测试中自然浮现 +- 用户主动要求的"小人控制"与 Microsoft Minesweeper Adventure 模式高度吻合,验证了参考方向的合理性 + +## 遗留验证点(下阶段处理,不阻塞 PROCEED) + +1. 标雷"赌注式动作"的实际使用率与手感(v3 终验) +2. 标错惩罚是否需要(候选方案已登记,A2 GDD 前用小补丁实测) +3. 停手折算比例(×2)的数值合理性(A3 GDD 定稿) +4. 兑现的情绪强化(战斗动画)——垂直切片的 B5 验证 + +## Verdict: **PROCEED** + +核心假设成立:扫雷决策与战斗结果的联动被测试者确认,循环骨架定稿并全部落入系统索引。下一步进入正式系统设计(GDD),用原型结论作为 Tuning Knobs 素材。 + +**PIVOT 记录**:无 PIVOT 链。原型代码不进生产(生产实现按 GDD 从零写,原型仅作参数与规则参考)。 diff --git a/docs/registry/architecture.yaml b/docs/registry/architecture.yaml new file mode 100644 index 00000000..a8e0cc39 --- /dev/null +++ b/docs/registry/architecture.yaml @@ -0,0 +1,64 @@ +# Architecture Registry — Dungeon Sweeper +# 生成:2026-09-12(/architecture-decision solo 批:ADR-0001~0004;用户授权代决) +# 规则:只增不改;立场变更 = 旧条目标 status: superseded_by + 新增条目(永改既有条目) +# 设计层立场(状态归属/接口契约/锚验收)登记在 design/registry/entities.yaml——本文件只登记实现级架构立场 + +interfaces: + - name: a2_event_surface + adr: ADR-0001 + signals: [cell_revealed, flag_judged, round_end_board] + note: GDD 概念名 round_end 的 A2 实现实名 = round_end_board;A3 直发 = round_end_stopped(payload 同构) + - name: b1_settlement_trigger + adr: ADR-0001 + contract: B1 订阅 A3 相位→Waiting 触发结算;只读 last_round_end + 相位观测量;不依赖事件到达序 + - name: presentation_subscription_rule + adr: ADR-0001 + contract: 呈现层永不直接订阅 A2/A3——只订 B1 步骤流 / level_runner(切片缺陷②教训固化) + +api_decisions: + - name: renderer + adr: ADR-0002 + decision: gl_compatibility(Forward+ 否决——D3D12 新驱动线降险;B5 出现 HDR 2D 需求时复议) + - name: window_base + adr: ADR-0002 + decision: 1280×800 基座 + stretch canvas_items + aspect expand(全部键显式落盘,防引擎默认漂移) + - name: autoload_set + adr: ADR-0003 + decision: 仅 {ConfigDB, RngService}(定名锁定);战斗编排器等核心类由组合根按局实例化,非 autoload + - name: board_rendering + adr: ADR-0003 + decision: MVP 沿用切片 GridContainer + 逐格控件形态(探针耦合事实);_draw() 迁移 = D1 正式化候选工作项(含三探针改造 + push_input 端到端探针 + 命中测试自建) + - name: cjk_font + adr: ADR-0003 + decision: CJK 字体随包发行并设 theme 默认字体(运行时默认字体无 CJK 字形)——D1 正式化硬性前置 + 导出 checklist 项 + - name: test_carrier + adr: ADR-0004 + decision: 双载体(--headless --script 逻辑冒烟 + headless 场景探针)+ 静态扫描域 S1–S8 + CI 外部 wall-clock 超时;gdUnit4 deferred(D2 OQ1 关闭形态) + - name: save_contract + adr: ADR-0005 + decision: SCHEMA_VERSION 比较逻辑归 D3(存档版本 > 当前 = 拒读);RNG 快照 = master_seed + 各流 scope 计数器 + 意图/窗口选择序列,切出须在 scope 事件边界外(无在飞 open_stream 实例——B1 编排保证) + +forbidden_patterns: + - pattern: autoload_global_event_bus + adr: ADR-0001 + detail: 否决中心事件总线——组合根直连 signal 接线 + - pattern: lambda_capture_self_cross_lifetime + adr: ADR-0001 + detail: core↔core 信号连接只许方法 Callable;发射者禁持有订阅者反向引用(防循环引用/生命周期延长) + - pattern: bare_autoload_global_identifier_in_core + adr: ADR-0004 + detail: core/ 禁裸引用 ConfigDB/RngService 全局标识符(--script 模式不注册——parse error;构造注入 only) + - pattern: global_rand_functions_in_gameplay + adr: ADR-0004 + detail: randi()/randf()/randf_range() 与 randomize() 同禁(全局 RNG 绕开 RngService——D2 Rule 5 补全) + - pattern: core_extends_control_or_tscn_preload + adr: ADR-0004 + detail: core/ 禁 extends Control/Node2D、禁 .tscn preload(D2 Rule 7) + - pattern: second_caller_of_battle_domain_apis + adr: ADR-0004 + detail: B2/B3/B4 战斗域 API 唯一调用方 = B1;A2/B3/B4 零相互直调(静态扫描 S7——B1 AC-43) + + - name: hero_override_parameter_passing + adr: ADR-0009(候选——切片机制正式化批) + decision: 主角加成值经 encounter 显式参数传入(begin_battle 增 hero_atk/hero_max_hp 字段;B2.spawn 增 atk_override/max_hp_override 参数,-1 = 表值兜底)——db.hero 表值运行期只读(切片原表写通道 _apply_hero_override/runner 写表废除) + note: 2026-09-13 E4 收口;接口漂移 = B2.spawn 签名 +2 参数、B1 encounter +2 字段(正式侧 B1/B2 GDD 修订随 ADR-0009 批);ui_tune_probe ⑦⑧ 为表不可变 + 单次加成的回归断言载体 diff --git a/production/gate-checks/gate-check-systems-design-2026-09-07.md b/production/gate-checks/gate-check-systems-design-2026-09-07.md new file mode 100644 index 00000000..408f3820 --- /dev/null +++ b/production/gate-checks/gate-check-systems-design-2026-09-07.md @@ -0,0 +1,70 @@ +# Gate Check 报告:Concept → Systems Design(重审) + +- **日期**:2026-09-07 +- **执行**:gate-check 技能 · 评审模式 lean(`production/review-mode.txt` 不存在,取默认) +- **背景**:上一轮(2026-09-06)四总监 CONCERNS(流程债 FAIL)。本轮为流程债清偿后的重审。 +- **Verdict: CONCERNS**(无阻塞项,遗留项均为登记级文档修正) + +## 必备物证:3/3 通过 + +- ✅ `design/gdd/game-concept.md` — 有实质内容(受众/术语/循环/难度分层/待定问题) +- ✅ 游戏支柱 — `game-pillars.md`,4 条支柱各带裁剪判据 +- ✅ Visual Identity Anchor — game-concept.md:13,1 句总规则 + 4 条支撑原则 +- ✅ (推荐项)概念原型 REPORT — `prototypes/minesweeper-battle-concept/REPORT.md`,Verdict: PROCEED,CONFIRMED(核心) + +## 质量检查:4/4 通过(1 项注明) + +- ✅ 核心循环已描述且文档间一致 +- ✅ 目标受众已定稿(game-concept.md:6) +- ✅ VIA 含一句规则 + ≥2 支撑原则(实为 4 条) +- ✅ 概念已评审 — 无独立 `/design-review` 报告文件,由两轮四总监门禁评审代行,无 MAJOR REVISION 级判定 + +## 总监评审 + +### Creative Director — CONCERNS +1. 流程债四项验证属实清偿;情绪核心与支柱全文档一致 +2. 验证证据质量:CONFIRMED 依据为开发者自测主观反馈,预注册验证信号未逐条记录 → 垂直切片前补一次按信号记录的正式 playtest(可与标错惩罚实测合并) +3. 未登记创意风险:安全格奖励**可预期性**无文档承载(P2 规划前提)→ 进 GDD 前登记,B4/A4 落实奖励可见性/探测 +4. 未登记创意风险:"我算对了"缺高光兑现节点(如全图标中/零误标加倍结算)→ 进 GDD 前登记,B1/B4 设计 + +### Technical Director — CONCERNS +1. 四项已登记技术风险(双舞台管线、可回放结算、headless 可测、RNG 归 D2)核实;依赖无环,前 10 GDD 满足拓扑序 +2. systems-index:55 依赖笔误:`A5 ← B2,A4` 应为 `A5 ← A2,A4`(奖励侧挂 B4 未入箭头)→ 进 GDD 前修正 +3. C3 口径不一::62 称"C3 消费 B1 输出"但 :56 依赖表为 C3 ← D2,B2 → C3 GDD 内定稿回写,可延后 +4. stage.txt 未写入(门禁关闭动作);两条未登记工程约束(headless 测试载体、事件总线载体)归 D2/B1 GDD 章节 + +### Producer — CONCERNS +1. **流程债四项逐项验证 ✓ 全部清偿**(支柱/受众/VIA/REPORT 均非占位、有定稿日期) +2. 里程碑分层与 GDD 设计顺序可执行;待定问题均有归属 +3. 两条跨系统前置未登记进 systems-index:标错惩罚实测(A2 GDD 前)、格子样张测试/art-bible 排期(game-concept:24 指向不存在的"art-bible 任务")→ A2 前补齐;**前 5 份 GDD(D2/A3/A4/B3/A1)无未清前置,可立即开写** +4. active.md 轻微失真:"v1→v3 验证完成"应为"v2 核心假设 CONFIRMED,v3 待终验" + +### Art Director — CONCERNS(代评) + +> 注:AD 代理派单连续 4 次失败(基础设施错误),本项由主会话按已读文档代行评审;如需独立评审可重跑 `/gate-check systems-design`。 + +1. VIA 足以支撑后续美术工作(色弱双通道、绘制优先级、调色板交换均到位);三项美术风险均已登记 +2. art-bible 未创建且未排期:不阻塞进入 Systems Design,但最晚须在 **D1 GDD(设计顺序第 10 位)前**、至迟垂直切片前存在(含格子样张测试) +3. 格子样张测试归属模糊;战斗表现美术测试(B5 GDD 前一屏样张)已登记清晰 +4. D1 行未登记"移动路径预览"归属(game-concept:25 有) + +## 阻塞项 + +无。 + +## 建议(按优先级) + +1. 补 systems-index 登记:标错惩罚实测(A2 前)、art-bible + 格子样张(D1 GDD 前最晚)、B4"使用时机"字段、D1"移动路径预览" +2. 修 systems-index:55 依赖箭头(A5 ← A2,A4,B4) +3. 修 active.md 失真(v3 待终验)+ README 标题 v2→v3 + game-concept:121 拆分停手(×2)/清图(×1) +4. 登记 CD 两条创意风险(奖励可预期性、"我算对了"高光节点) +5. 垂直切片前:按预注册信号做一次正式 playtest(与标错惩罚补丁实测合并) + +## Chain-of-Verification + +5 问核验,结论不变。工具实证:VIA 节(grep game-concept.md:13)、支柱裁剪判据数 = 4、REPORT PROCEED 行、stage.txt 确认不存在、design/gdd/ 仅 3 文件。无 CONCERN 可升级为阻塞;上轮 FAIL 项经文件证据逐项确认清偿。 + +## 阶段处置 + +- **stage.txt 未写入**(判定 CONCERNS,且技能要求阶段变更须用户明确确认) +- 门禁实质通过;用户确认推进后执行:`production/stage.txt` ← `Systems Design`,并同步 active.md 阶段字段 diff --git a/production/qa/bugs/BUG-0001.md b/production/qa/bugs/BUG-0001.md new file mode 100644 index 00000000..b1d9a868 --- /dev/null +++ b/production/qa/bugs/BUG-0001.md @@ -0,0 +1,85 @@ +# Bug Report + +## Summary +**Title**: 结算战斗段首行下发时 StatusSlotRow 按索引误取 tooltip 节点崩溃,结算协程中断 → 结算阶段永久软锁 +**ID**: BUG-0001 +**Severity**: S1-Critical(核心循环阻断——结算阶段永久假死,只能杀进程) +**Priority**: P1-Immediate +**Status**: Closed +**Reported**: 2026-09-12 +**Reporter**: 用户(设计师本人,垂直切片试玩) + +## Classification +- **Category**: Crash / 软锁(soft lock,无崩溃弹窗的假死) +- **System**: C 区状态效果槽(StatusSlotRow)呈现层 × B1 结算编排(信号链中断面) +- **Frequency**: 条件必现(前置 = 状态槽悬停过一次 + 其后任一结算行刷新时英雄侧效果 ≥1;悬停是新增益出现后的自然操作,实际对局高频可触) +- **Regression**: Yes——`status_slot_row.gd` 与 C 区数据框为 2026-09-12 五区改造批新增;2026-09-11 build 无此视图 + +## Environment +- **Build**: 垂直切片 · battle.md 五区改造批(2026-09-12);卡死会话种子 42,19:22:19 开局 +- **Platform**: PC · Win10 · Godot 4.7.2 (OpenGL Compatibility · RTX 2060 SUPER) +- **Scene/Level**: 完整关卡模式(第一关)· 轮末结算(道具窗口跳过后、战斗段首行) +- **Game State**: 第 1 场战斗结算中;道具窗口以「跳过」关闭;英雄侧存在本结算新授予的状态效果(槽可见) + +## Reproduction Steps +**Preconditions**: 对局中英雄侧至少有 1 个状态效果(如安全格授予的增伤),状态槽在 C 区可见。 + +1. 鼠标悬停任一状态效果槽(tooltip 弹出——此步把 `_tip` 挂入 StatusSlotRow 子节点表) +2. 正常结束本轮(停手或 AP 耗尽),进入轮末结算 +3. 结算段任一 step_row 下发(如道具窗口跳过 → 战斗段「主角攻击」行)触发 `_refresh_presentations` +4. 游戏无声冻结:棋盘保持去饱和、阶段文案停在「结算中」,所有输入通道无效 + +**Expected Result**: 结算七段完整回放,逐行兑现后进入下一轮(或终局 overlay)。 +**Actual Result**: `status_slot_row.gd:118` 脚本错误沿信号链回卷,`_finish_settlement` 在战斗段(⑤)中途被中止——`_settling` 恒为 `true`、A3 恒为 `PHASE_WAITING`,结算永不恢复。 + +## Technical Context +- **Likely affected files**: + - `prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd:57-63`(`set_effects` 用 `get_child(i)` 按位取槽——`_tip` 入树后索引错位) + - `prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd:80-88`(`_sync_slots` 显式保留 `_tip`,与上行索引假设自相矛盾) + - `prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd:114-118`(`_fill_slot` 对 cast 为 null 的 `col` 调 `get_child`) + - 中断面:`core/battle_orchestrator.gd:233-263`(`_finish_settlement`——错误回卷致 `_settling` 悬挂) +- **Related systems**: `main.gd _on_step_row → _refresh_presentations`(每结算行同步刷新);`level_runner.gd:128` 转发 lambda;`hud_presenter.gd:29` +- **Possible root cause**(已由崩溃回溯证实): + tooltip 节点 `_tip` 在 `_init` 不入树,仅 `_on_slot_hover` 首次悬停时 `add_child(_tip)`,与动态重建的槽共用同一子节点表。此后 `set_effects` 内 `get_child(i)` 取到的第 0 个"槽"实为 `_tip`(PanelContainer,cast 通过),`_fill_slot` 中 `_tip.get_child(0)` 是 Label → `as VBoxContainer` = null → 118 行 `col.get_child(0)` 报 + `Cannot call method 'get_child' on a null value`。 + Godot 4 对信号回调内的运行时错误整条 GDScript 调用栈回卷:按钮 lambda → `submit_window_selection` → `_finish_settlement` → `_combat_segment` 全部中止,B1 无"结算恢复"路径,软锁。 + +## Evidence +- **Logs**(`user://logs/`,DungeonSweeperVerticalSlice): + - `godot.log`(19:22 会话)完整回溯:`_fill_slot (status_slot_row.gd:118)` ← `set_effects` ← `set_status_effects` ← `HudPresenter.refresh` ← `main._on_step_row` ← `level_runner lambda` ← `_emit_row` ← `_combat_segment (battle_orchestrator.gd:474)` ← `_finish_settlement (242)` ← `submit_window_selection (230)` ← `_on_window_skipped` ← skip 按钮 lambda + - `playtest_42_20260912T192219.jsonl`:`ux_window_choice{skip:true}` 之后再无任何事件(无 settlement 快照、无 ux_round_exit、无下一轮)——结算协程死亡实锤 +- **Visual**: 棋盘去饱和锁定、阶段指示「第 N 轮 · 结算中」、明细带展开但停在该结算首行;与 2026-09-11 崩溃 #1 的"输入锁已上 + 呈现层崩溃 = 无声冻结"同族 + +## Related Issues +- `production/qa/playtests/playtest-2026-09-11-designer.md` 崩溃 #1/#2——同一失败家族(结算期呈现层错误 + 核心中断/输入锁死),当时的防复发三条硬性要求(载荷 schema 成文 / 场景级 headless 测试 / 状态可见性)第三条仍未覆盖本路径 +- `presentation/views/board_view.gd:476` `_make_cursor_texture` 启动期 `canvas_item is null` 渲染错误(P3,独立缺陷,与本软锁无关) +- `status_slot_row.gd:57-63` 潜在次生缺陷:`set_effects` 循环上界是 `effects.size()` 而 `_sync_slots` 只建 `min(size, 4)` 个槽——英雄侧效果 >4 时(off_guard max_stack 16)同函数必崩 +- 场景探针(ui_probe/ui_click_probe)未覆盖「悬停 → 结算刷新」路径——冒烟再次漏掉呈现层时序态 + +## Notes +- 修复方向(最小面):槽与 tooltip 分离承载——`_tip` 改挂 StatusSlotRow 之外的独立层,或 `set_effects`/`_sync_slots` 改用槽引用数组(不按子节点位序索引)。两处任选其一,后者改动面更小但必须同步修掉 >4 溢出循环上界 +- 按代码规范「缺陷修复必须沉淀回归断言」:需在场景级探针补"悬停槽位后触发一次含效果行的结算,断言无脚本错误且 phase 走到下一轮" +- 本次卡死用户未点「诊断转储」(现存三份 diag 均来自探针 `{"test":true}` 上下文,core 字段为空)——诊断按钮对真实软锁会话未被使用,不影响本次定位(godot.log 回溯已足够) + +## Fix Record(2026-09-12,Status 仍 Open——正式流转待 /bug-report verify) +- **修复**:`status_slot_row.gd` 槽生命周期改由 `_slots: Array[PanelContainer]` 引用数组唯一管理,`set_effects`/`_sync_slots`/`set_cleared_feedback` 全部弃用子节点位序索引;顺带修掉 effects > MAX_SLOTS 时循环越界次生缺陷(超出截断至 4 槽,符合"最多 4 槽可见"规格) +- **回归断言**:`smoke/ui_probe.gd` 新增 BUG-0001 回归段(建槽 → 悬停入 tooltip → 重刷断言槽位/填充 → 超上限截断 → 空态清槽) +- **阴性对照**:回滚修复版跑探针,精确复现野外崩溃(`status_slot_row.gd:118` 同行同错);恢复修复版后探针通过——断言有效 +- **验证**:smoke_all 161 检查 0 失败 · ui_probe PASS · ui_click_probe(seed 7)关卡通关/问题=0/选用失效=0/无卡死 · ui_tune_probe PASS ×2 +- **待办**:人类完整试玩复跑(playtest 报告 Top1 优先级)后走 verify/close + +## Verification Record(2026-09-12) +**判定**:VERIFIED FIXED +- 复现路径核查:`status_slot_row.gd` 中 `set_effects`/`_sync_slots`/`set_cleared_feedback` 已无子节点位序索引(grep 复核,仅 `_slots` 引用数组访问)——原崩溃代码路径不复存在 +- 阴性对照:修复前版本在 ui_probe 回归段精确复现野外崩溃(`status_slot_row.gd:118` 同行同错),证明断言与定位有效 +- 相关测试:smoke_all 161 项检查 0 失败(关闭时复跑)· ui_probe PASS(含 BUG-0001 回归段)· ui_click_probe(seed 7)关卡通关/问题=0/无卡死 · ui_tune_probe PASS +- 人工确认:用户试玩确认"没有问题" + +## Closure Record +**Closed**: 2026-09-12 +**Resolution**: Fixed — StatusSlotRow 槽生命周期改由 `_slots` 引用数组唯一管理,弃用子节点位序索引(tooltip 入树不再错位);顺带修复 effects > MAX_SLOTS 循环越界(截断至 4 槽) +**Fix commit / PR**: 本地工作区直接修复(项目非 git 仓) +**Verified by**: qa-tester +**Closed by**: 用户 +**Regression test**: `prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd`(BUG-0001 回归段) +**Status**: Closed diff --git a/production/qa/bugs/BUG-0002.md b/production/qa/bugs/BUG-0002.md new file mode 100644 index 00000000..02ca69ba --- /dev/null +++ b/production/qa/bugs/BUG-0002.md @@ -0,0 +1,61 @@ +# Bug Report + +## Summary +**Title**: 浅深度战斗零敌人(d0/d1 遭遇 unit_count=0、round 1 即 victory)——遭遇骰/spawn 路径疑似掷出 0 +**ID**: BUG-0002 +**Severity**: S3-Minor(复核降级——原报 Medium;当前构建不可复现,遥测签名另有解释) +**Priority**: P3-Backlog(不变式回归已落地,无遗留行动) +**Status**: Closed +**Reported**: 2026-09-12(playtest-2026-09-12-designer 报告 Bugs 表 #1) +**Reporter**: 用户(设计师本人,试玩遥测);本档由 2026-09-13 会话按报告 Action Routing 建档并复核 + +## Classification +- **Category**: Gameplay(遭遇生成)/ 遥测可观测性 +- **System**: B1 遭遇骰(C3 Core Rule 8 区间骰)× B2.spawn × settlement_snapshot 观测面 +- **Frequency**: 原报两次真人局均现(d0 100%、20:13 另含 d1);当前构建复现 0/100 场 +- **Regression**: No——遭遇骰批(2026-09-12)后无相关代码路径可产出 0 + +## Environment +- **Build**: 原报 = 垂直切片 · 2026-09-12 五区改造批;复核 = 2026-09-13 当前工作区(status_effects.tres defense_up=-30 批) +- **Platform**: PC · Win10 · Godot 4.7.2 · seed 42 固定 +- **Scene/Level**: 第一关 d0/d1 战斗节点 +- **Game State**: 战斗第 1 轮结算即 victory,快照 unit_count=0 + +## Reproduction Steps +**Preconditions**: 原报基于 playtest_42_20260912T201317.jsonl 遥测(快照面)。 + +1. seed 42 开局,进入 d0 战斗 +2. 首轮挖满 8 AP 后结算 +3. 快照显示 verdict=victory、unit_count=0(原报判为「零敌人空场」) + +**Expected Result**: C3 Rule 8 区间 [unit_count, unit_count_max] 下限 1——不应出现 0 敌战斗。 +**Actual Result(复核)**: **遥测误读——实为「首轮全灭」而非「空场」**。快照取在衰减段之后的结算末尾,`unit_count` 只计存活敌:英雄首轮爆发(基础攻击 + 伤害A 开格充能 + 爆裂粉尘 instant_aoe 全体)可在 round 1 全歼浅深度低 HP 敌群,快照同样呈现 unit_count=0 + round-1 victory。 + +## Technical Context +- **Likely affected files**: `core/battle_orchestrator.gd`(begin_battle 遭遇骰 / _emit_settlement_snapshot)、`core/unit_container.gd`(spawn 数量守卫) +- **Related systems**: C3 Core Rule 8(区间骰)、playtest_logger(JSONL 快照消费) +- **Possible root cause(复核结论)**: 非遭遇骰缺陷。当前实现掷骰 = `roll_lo + estream.randi_range(0, roll_hi - roll_lo)`,数学下限 = `unit_count` ≥ 1;B2.spawn 对 count<1 fail-fast 拒绝(双闸)。**seed 42 当前构建全关回放(damage_probe level 模式)复现同款遥测签名**:d0 spawn=1、d1 spawn=3,全部「round-1 胜利 = 首轮全灭(wipe)」——与 20:41 辅助局 d1 快照(3 只地精各剩 1 HP = 主角攻击斩首只 + 3 颗爆裂粉尘 ×4 AOE)逐点吻合。 + +## Evidence +- **探针**:`smoke/damage_probe.gd -- level 42`——d0 spawn=1(预测 1 ✓)首轮全灭 · d1 spawn=3(预测 3 ✓)首轮全灭 · 20 种子 × 5 深度 = 100 场 spawn 全域内、0 场空遭遇 +- **遥测取证**:playtest_42_20260912T204636.jsonl d1 r1——unit_count=2 存活(3 只中 2 只各剩 1 HP)+ hero 承伤 −8,证明同种子 d1 在另一局正常刷出敌群(差异 = 真人挖掘路径不同的奖励抽取,非 RNG 流分歧) +- **回归断言**:smoke_all `_smoke_encounter_roll` 增「全深度 × 12 种子 spawn 全域内(60 场,∈[unit_count, unit_count_max] 且 ≥1)——空遭遇不可达」;settlement_snapshot 新增 `spawn_count` 字段 + `_smoke_playtest_logger` 字段/等值断言——unit_count=0 时可判别「首轮全灭」vs「空场」,同类误读不再依赖人肉推断 +- **修复后验证**:冒烟 148/148 全绿 + +## Related Issues +- playtest-2026-09-12-designer 报告 Bugs 表 #1 / Top 3 Priorities #1(探针前置) +- BUG-0003(Boss 攻击零兑现——同一份遥测的姊妹误读,共用根因探针) +- BUG-0001(呈现层软锁——编号顺延基点,无关联) + +## Notes +- 「单位数量 0」类观察的判别义务已机制化:任何后续 JSONL 快照携带 `spawn_count`(实例化总数,含死亡残位),`unit_count < spawn_count` = 有击杀,`spawn_count=0` 才是空场。 +- 原报的怀疑对象(遭遇骰路径)经代码审读 + 100 场扫描证伪;真实可玩性议题「浅深度战斗可被首轮爆发无伤跳过(d0-d3 全部 round-1 通关)」属难度带问题,归 C3 F4/F5 复扫(BUG-0003 关联条目与本会话 Q1 基线数据),不在本缺陷档处置。 + +## Closure Record +**Closed**: 2026-09-13 +**Resolution**: Rejected as coded——零敌遭遇在当前实现不可达(数学下限 + fail-fast 双闸 + 100 场扫描零越域);原观察为「首轮全灭」被快照盲区误读。沉淀 = settlement_snapshot.spawn_count 观测面 + 全域 spawn 回归断言(无游戏数值变更)。 +**Fix commit / PR**: 不适用(非 git 管理;变更 = core/battle_orchestrator.gd 快照字段 + smoke/smoke_all.gd 回归断言 + smoke/damage_probe.gd 探针) +**Verified by**: 2026-09-13 会话(damage_probe level 模式 + smoke_all 148/148) +**Closed by**: 用户授权任务(playtest-2026-09-12 报告 Top3 执行会话) +**Regression test**: `smoke/smoke_all.gd` `_smoke_encounter_roll`(全域扫描)+ `_smoke_playtest_logger`(spawn_count 字段与等值);`smoke/damage_probe.gd -- level`(可复演证据面) +**Status**: Closed diff --git a/production/qa/bugs/BUG-0003.md b/production/qa/bugs/BUG-0003.md new file mode 100644 index 00000000..a16e9399 --- /dev/null +++ b/production/qa/bugs/BUG-0003.md @@ -0,0 +1,63 @@ +# Bug Report + +## Summary +**Title**: Boss 攻击拍零兑现(ogre_chief pattern 轮转正常但英雄血量纹丝不动——攻击链对该敌未生效) +**ID**: BUG-0003 +**Severity**: S2-Major(复核降级——原报 High;非代码缺陷,零承伤根因为已拆除的减伤墙设计语义) +**Priority**: P2-Next Sprint(无代码遗留;难度带复验归 Q1 调参批数据,见 Notes) +**Status**: Closed +**Reported**: 2026-09-12(playtest-2026-09-12-designer 报告 Bugs 表 #2) +**Reporter**: 用户(设计师本人,试玩遥测);本档由 2026-09-13 会话按报告 Action Routing 建档并复核 + +## Classification +- **Category**: Gameplay(战斗结算)/ 数值设计 +- **System**: B1 战斗段 F2 乘区(hero_defense 池)× B3 defense_up(stack×max_stack)× B2 行为环 +- **Frequency**: 原报两局 Boss 战全部攻击拍零兑现;当前构建(defense_up=-30 批)0 复现 +- **Regression**: No——攻击链代码路径无缺陷;行为由表值组合完全解释 + +## Environment +- **Build**: 原报 = 垂直切片 · 2026-09-12 五区改造批(status_effects.tres defense_up=-50 旧值);复核 = 2026-09-13 当前工作区(defense_up=-30) +- **Platform**: PC · Win10 · Godot 4.7.2 · seed 42 固定 +- **Scene/Level**: 第一关 Boss 节点(ogre_chief,pattern [attack, attack, charge, heavy],atk 9) +- **Game State**: 12 结算轮 hero hp 恒 50/50;快照 buffs[] 恒空 + +## Reproduction Steps +**Preconditions**: 原报基于 playtest_42_20260912T201317.jsonl(主分析局)+ 20:41 辅助局。 + +1. 正常推进至 Boss 节点(此时库存/状态由前序战斗积累) +2. Boss 战 6 轮:beat 轮转 attack→attack→charge→heavy→attack→attack 正常推进(advance 在跑) +3. 英雄承伤恒 0(含 fresh 池轮)——原报判「攻击链对该敌未生效」 + +**Expected Result**: 非 charge 拍应按 atk.total × 池乘区兑现伤害。 +**Actual Result(复核)**: **攻击链健康——零兑现由 defense_up 叠满墙(设计语义)全额解释**。快照 buffs[] 恒空是观测盲区:快照取在衰减段之后,时效 1 的 defense_up 当轮授予当轮衰减,快照永远看不见——「buffs 空」不构成「无墙」证据。旧数据 defense_up magnitude=-50 × max_stack 2 = 池 -100 = pool_clamp_lo 触底 → F2 乘区 ×0,任何 atk 曲线乘在 ×0 上(原报根因分析「v3 主杠杆是乘在 ×0 上的调参」由此坐实)。 + +## Technical Context +- **Likely affected files**: `core/battle_orchestrator.gd`(_combat_segment / _clamped_pool / _resolve_damage)、`core/status_container.gd`(stack 授予)、`core/damage_math.gd`(pool_damage) +- **Related systems**: settlement.tres(effect_pool_map / pool_clamp)、status_effects.tres(defense_up 行)、reward.tres(defense_up 权重) +- **Possible root cause**: 设计语义——「stack 策略 × magnitude × max_stack 触池 clamp 下限 = 100% 免伤」授予段生效、先于战斗段,当轮挖到的减伤当轮全额兑现。非 Boss 特异:普通敌在同一墙轮同样零兑现(20:13 局 12/12 轮零承伤)。 + +## Evidence +- **探针(fresh 池裸验,当前构建)**:`smoke/damage_probe.gd -- boss 42` Run A(零挖掘停手轮,零授予零墙)——Boss 攻击拍 d_tick = atk.total 全额兑现:attack 9/9 ×2、charge 0(设计)、heavy 18/18,6 轮内击倒英雄(defeat)——**H2(Boss/敌攻击链缺陷)否证**;拍名轮转与真人局逐拍一致 +- **探针(墙行为,当前数据重证)**:Run B(自动挖掘)——减伤授予可见(池 -30 → d_tick 21→14;池 -60 → 残余 ×0.40),墙轮承伤恒 > 0:**09-13 调参批(-50→-30)后墙最深 -60 > clamp_lo -100,归零不可达** +- **遥测取证**:20:13 局 Boss 6 轮 raw 应承伤 9/9/0/18/9/9 = 54 点 vs 实测 0;d2/d3 首轮攻击拍同样零兑现(非 Boss 特异)——与「全轮墙」假说唯一相容 +- **回归断言**:smoke_all 新增 `_smoke_attack_chain_fresh_pool`——停手轮(零授予→池恒空)断言非蓄力拍 d_tick == atk.total 全额兑现且 > 0;若未来出现 fresh 池零兑现(真攻击链缺陷)在此被抓 +- **修复后验证**:冒烟 148/148 全绿 + +## Related Issues +- playtest-2026-09-12-designer 报告 Bugs 表 #2 / 根因分析「问题②难度:v3 调参为何无效」/ Top 3 Priorities #1 +- BUG-0002(同份遥测的姊妹观察——同一探针分割) +- 09-13 减伤墙调参批(status_effects.tres -50→-30 + reward.tres 权重 24→16——本缺陷的设计侧处置,先于本档落地) +- Action Routing 原定「/propagate-design-change b3-status-effect.md(候选:clamp 收紧/乘法衰减/重击拍穿防)」——**未执行**:结构冻结域拦截(settlement.tres 池 clamp [-100,+100] 冻结)+ 数值拍板权归 C3,候选改法列入待设计批清单,见 Notes + +## Notes +- **待设计批清单(超出本会话授权,列出不执行)**:① 墙语义候选改法(clamp 收紧至 -60/-70 / stack 乘法衰减 / heavy 拍穿防)归 C3 + /propagate-design-change;② B3 GDD "-50" 文字与 status_effects.tres 批注同步(09-13 批已登记随下次 B3 修订批);③ 当前墙残余 -40%/轮仍显著——是否越 F4 带(普通场 ≤20%、Boss 场 ≤50%)由本会话 Q1 复扫数据(production/session-state/active.md 41 条)供 C3 裁决,本会话未做任何数值拍板。 +- 原报 Action Routing 将本项路由至 /bug-report 与 /propagate-design-change 双通道:bug 通道结论 = 代码无缺陷(本档);设计变更通道 = 候选改法待 C3 拍板(上表)。 + +## Closure Record +**Closed**: 2026-09-13 +**Resolution**: Rejected as coded——攻击链无缺陷(fresh 池全额兑现 + 击倒可达);零承伤根因 = defense_up 叠满墙(设计语义,旧数据 -50×2 = clamp 触底 ×0),09-13 调参批已拆除归零能力。沉淀 = fresh 池攻击链回归断言(无游戏数值变更)。 +**Fix commit / PR**: 不适用(变更 = smoke/smoke_all.gd `_smoke_attack_chain_fresh_pool` + smoke/damage_probe.gd 探针) +**Verified by**: 2026-09-13 会话(damage_probe boss 模式 7/7 PASS + smoke_all 148/148) +**Closed by**: 用户授权任务(playtest-2026-09-12 报告 Top3 执行会话) +**Regression test**: `smoke/smoke_all.gd` `_smoke_attack_chain_fresh_pool`;`smoke/damage_probe.gd -- boss`(Run A fresh 池裸验 / Run B 墙行为实测) +**Status**: Closed diff --git a/production/qa/bugs/BUG-0004.md b/production/qa/bugs/BUG-0004.md new file mode 100644 index 00000000..8a41059b --- /dev/null +++ b/production/qa/bugs/BUG-0004.md @@ -0,0 +1,62 @@ +# Bug Report + +## Summary +**Title**: grant_node_active 直调 stock、不经 _supply.notify_granted()——节点奖励完全绕开供给控制器计数 +**ID**: BUG-0004 +**Severity**: S4-Trivial(原报 Low;纯计数口径漏洞,零战斗数值影响) +**Priority**: P3-Backlog(本会话已修复并沉淀回归) +**Status**: Closed +**Reported**: 2026-09-12(playtest-2026-09-12-designer 报告 Bugs 表 #3) +**Reporter**: 用户(设计师本人,试玩报告);本档由 2026-09-13 会话按报告 Action Routing 建档并修复 + +## Classification +- **Category**: Gameplay(奖励经济计数口径) +- **System**: B4 奖励经济 × RewardSupplyController(可选类供给状态机)× C1 奖励节点 +- **Frequency**: Always(每个 reward 节点必现——结构性) +- **Regression**: Yes——2026-09-11 供给控制器批引入 notify_granted 入账点时,关卡扩展批的 grant_node_active(更早)未同步接入 + +## Environment +- **Build**: 复核 = 2026-09-13 当前工作区(漏洞在位 → 本会话修复) +- **Platform**: PC · Win10 · Godot 4.7.2 +- **Scene/Level**: 第一关 reward 节点(战×2 → 道具 → …) +- **Game State**: 节点奖励 roll 一件可选类直接入库 + +## Reproduction Steps +**Preconditions**: 任意到达 reward 节点的关卡流程。 + +1. `level_runner._enter_next_node` 处理 reward 节点 → `b4.grant_node_active(Vector2i(-1,-1), 0)` +2. grant_node_active 内部 roll 一件 damage_item/heal_pack 后直调 `stock(...)` 入库 +3. `_supply.granted_count()` 不变——供给控制器对这次发放零感知 + +**Expected Result**: 「发放即获取」口径全通道一致——节点奖励与格子授予(`grant()`)同一入账点。 +**Actual Result(修复前)**: 节点奖励绕过计数——供给控制器战斗域计数(首件保证/指数递减的入账基础)与 `supply_granted_count()` 观测面均漏记节点奖励,关卡级供给总量核算失真(原报:「结构性漏洞之一」)。 + +## Technical Context +- **Likely affected files**: `core/reward_economy.gd`(grant_node_active) +- **Related systems**: `core/reward_supply_controller.gd`(notify_granted 唯一入账点)、`core/level_runner.gd`(调用方) +- **Possible root cause**: grant_node_active 早于供给控制器批存在(2026-09-11 关卡扩展批),供给控制器批改造 grant() 入账时未覆盖此调用点——事实约定「发放即入账」未成文导致漏改(code-conventions 1.5 同款)。 + +## Evidence +- **代码审读(实锤)**:`core/reward_economy.gd` grant_node_active 直调 stock,无 notify_granted;对照 grant() 对 damage_item/heal_pack 发放必调 `_supply.notify_granted()` +- **修复**:grant_node_active 在 stock 受理成功(`inst is Dictionary`)后补调 `_supply.notify_granted()`——与 grant() 同一入账语义;失败路径(清库后拒绝)不入账 +- **影响面评估**:供给计数器为战斗域(notify_battle_start 清零),节点奖励发生在战斗间——修复**零战斗数值影响**(下一战斗开战即清零,注入概率路径不变);收益 = 计数口径统一 + `supply_granted_count()`/遥测供给核算完整 +- **回归断言**:smoke_all `_smoke_supply` 增⑦——`grant_node_active` 受理后 `supply_granted_count() == 1`(发放即入账;修复前该计数为 0,断言可抓原缺陷) +- **修复后验证**:冒烟 148/148 全绿 + +## Related Issues +- playtest-2026-09-12-designer 报告 Bugs 表 #3 / Top 3 Priorities #3(供给量重拍板 + 节点奖励纳入控制器) +- 报告根因分析「三个结构性放大器」第 1 条(节点奖励绕开控制器再放大供给量) +- **Top 3 Priorities #3 的另一半(供给量语义重拍板)不在本缺陷档**:「每战斗首件保证 → 关卡域首件保证」候选归 C3 裁决(列待拍板清单,本会话未动) + +## Notes +- 报告 Top 3 #3 原文要求「grant_node_active 补 notify_granted 上报」——本档即该义务的执行与沉淀。 +- 关卡域供给总量的观测:修复后 `supply_granted_count()` + 库存查询可完整重建「每关可选类获取账目」(格子授予 + 节点奖励 + 注入三通道),供 C3 供给量重拍板时直接消费。 + +## Closure Record +**Closed**: 2026-09-13 +**Resolution**: Fixed——grant_node_active 补 `_supply.notify_granted()`(stock 受理成功后入账,与 grant() 同一口径);零战斗数值影响(计数器战斗域清零语义不变)。 +**Fix commit / PR**: 不适用(非 git 管理;变更 = core/reward_economy.gd 1 处 + smoke/smoke_all.gd `_smoke_supply` ⑦ 回归断言) +**Verified by**: 2026-09-13 会话(smoke_all 148/148,含新回归断言) +**Closed by**: 用户授权任务(playtest-2026-09-12 报告 Top3 执行会话) +**Regression test**: `smoke/smoke_all.gd` `_smoke_supply` ⑦「节点奖励计入供给控制器(发放即入账)」 +**Status**: Closed diff --git a/production/qa/playtests/playtest-2026-09-11-designer.md b/production/qa/playtests/playtest-2026-09-11-designer.md new file mode 100644 index 00000000..85f8e87f --- /dev/null +++ b/production/qa/playtests/playtest-2026-09-11-designer.md @@ -0,0 +1,65 @@ +# Playtest Report — Dungeon Sweeper 战斗阶段垂直切片 + +> 判定:**INCOMPLETE**(两次中断的试玩尝试,定性反馈未采集)· Review mode: lean(CD-PLAYTEST skipped — Lean mode) +> 技能:/playtest-report(analyze 模式,原始素材 = 本会话用户反馈 + 崩溃日志 + 仪器数据) + +## Session Info +- **Date**: 2026-09-10 ~ 2026-09-11(两次中断的试玩尝试) +- **Build**: 垂直切片(拍板①–④落地版:atk=6 先手 / 两级奖励 / 库存关内延续 / 窗口单件) +- **Duration**: 未完成一场完整战斗(两次均在循环中途中断) +- **Tester**: 用户(设计师本人,唯一测试者) +- **Platform**: PC · KB+M +- **Session Type**: First time ×2(均为中断重试) + +## Test Focus +战斗阶段完整循环:开战 → 挖掘/旗/停手 → 七段结算逐行兑现 → 胜负;含 2026-09-11 新机制(主角先手、即时奖励、库存延续)。 + +## First Impressions +- **未采集**——两次均未撑到可评估的时长。唯一间接信号:用户问"是否有图像"(程序化色块+文字的观感被注意到,属预期内的占位表现)。 + +## Gameplay Flow +### What worked well +- 核心循环链路本身可走通:挖掘、插旗、结算弹出、道具窗口、关卡过渡(第二场试玩已推进到"胜利→库存/HP 带入下一层"节点才崩溃)。 +- 结算明细逐行输出正常工作(崩溃均发生在呈现层读数,流水内容无恙)。 + +### Pain points +- **崩溃 #1(High)**:第一轮结算弹道具窗口即假死——窗口数据缺 `round_index` 字段,弹窗构建崩溃但输入锁已上,游戏无声冻结。用户被迫中止。 +- **崩溃 #2(High)**:胜利/关卡过渡时再次报错——`battle_finished` 信号发出前 `hero` 已被销毁,UI 读 `hp` 崩溃,结算覆盖层未显示。 + +### Confusion points +- 用户问"是不是设置了断点"——假死无任何提示,玩家无法区分"卡了"与"在等输入"。(呈现层崩溃静默锁输入 = 设计缺陷级体验问题,不只是代码 bug。) + +### Moments of delight +- 未采集。 + +## Bugs Encountered +| # | 描述 | 严重度 | 可复现 | 状态 | +|---|---|---|---|---| +| 1 | 道具窗口数据缺 `round_index`,弹窗构建崩溃→输入锁死 | High | 是(固定流程必现) | ✅ 当日修复+字段契约断言入冒烟 | +| 2 | `battle_finished` 载荷缺 hero 状态(despawn 先于信号),胜利过渡崩溃 | High | 是(打到胜利必现) | ✅ 当日修复(载荷销毁前捕获)+冒烟适配 | + +**共性根因**:呈现层与核心间"信号载荷/时序"契约未成文;GDScript 编译不查这两类错;冒烟从未执行 main.gd。防复发已登记为 D1 GDD 三条硬性要求(载荷 schema 成文 / 场景级 headless 测试 / 状态可见性规则)。 + +## Feature-Specific Feedback +### 全部新机制(先手攻击/两级奖励/库存延续/单件窗口) +- **未采集**——机制是否"可感受、可归因、有趣"没有任何用户输入。 + +## Quantitative Data(注意:来自自动玩家,非人类试玩) +- 蒙特卡洛 60 场(30 种子 × L1/L2,理性政策):胜轮均值 1.80、每轮承伤均值 2.1–2.7(max_hp=50 的 0–16%)、每场恰清图 1 次、败北 0。 +- 与设计意图冲突:**多轮结算仪式感 / RAP 取舍 / 囤刀时机未获得发生空间**(B4 失衡预警⑥定量坐实)。 + +## Overall Assessment +- **Would play again**: 未采集(用户实际行为 = 两次遇崩即报,信任受影响——"写代码没检查依赖吗") +- **Difficulty / Pacing**: 未采集(人类视角);仪器视角 = 过易、过快 +- **本轮试玩判定:INCOMPLETE**——循环完整性与 P2 归因("这个结果是我哪步扫雷带来的")这两个切片核心验证问题均未得到回答 + +## Top 3 Priorities +1. **修复后完整重跑一次试玩**(当前 build 冒烟 43/43)——采集首轮真正的定性反馈:归因清晰度、结算节奏、新机制手感 +2. **难度带拍板**(C3 域):以期望表为靶(期望胜轮带/承伤占比),用 monte_carlo.gd 验算 knob 方案 +3. **headless UI 冒烟**:真实实例化 main 场景跑完整对局——把"呈现层从未被执行"的盲区关死 + +## Action Routing(Phase 3) +- Bug ×2:已修复;如需正式追踪 → /bug-report 建档 +- 平衡项(预警⑥)→ /balance-check + C3 期望表 +- 设计变更:暂无(人类定性反馈为零) +- Polish:占位视觉 → 格子样张测试(art-bible 既有任务) diff --git a/production/qa/playtests/playtest-2026-09-12-designer.md b/production/qa/playtests/playtest-2026-09-12-designer.md new file mode 100644 index 00000000..cabbdca8 --- /dev/null +++ b/production/qa/playtests/playtest-2026-09-12-designer.md @@ -0,0 +1,93 @@ +# Playtest Report — Dungeon Sweeper 战斗阶段垂直切片(第 2 次) + +> 判定:**COMPLETE**(首次完整通关的真人局,定性反馈两条,均有遥测坐实)· Review mode: solo(CD-PLAYTEST skipped — Solo mode) +> 技能:/playtest-report(analyze 模式,原始素材 = 本会话用户口述 + playtest JSONL 遥测 + 代码/数据核对) + +## Session Info +- **Date**: 2026-09-12 20:13(主分析局);20:41 辅助局(调参面板加成后) +- **Build**: 垂直切片 · 2026-09-12 五区改造批 + BUG-0001 修复前;数据 = v3 调参批(enemy.tres 18:05:atk 7/5/9/6/9、Boss 150) +- **Duration**: 完整一关(7 节点:battle×2 → reward → battle×2 → rest → boss,12 个结算轮) +- **Tester**: 用户(设计师本人,唯一测试者) +- **Platform**: PC · Win10 · Godot 4.7.2 · seed 42(固定种子) +- **Session Type**: Returning(第 3 次尝试,首次跑通全程——09-11 两局均被崩溃阻断) + +## Test Focus +两条旧问题的复核:① 道具获取(治疗稀缺性 / 伤害:治疗 9:1 体感);② 难度曲线(到 Boss 房前满血)。 + +## First Impressions +- 未单独采集(用户直接进入两条旧问题反馈)。 + +## Gameplay Flow + +### What worked well +- **核心循环首次完整走通**:挖掘 → 结算七段 → 道具窗口 → 关卡节点(reward/rest)→ Boss → level_end victory,全程无崩溃。09-11 报告的两个 High 崩溃与 BUG-0001 软锁家族未复现(BUG-0001 修复批 20:23 落地,主分析局在其之前——悬停路径未触发,属幸存而非验证)。 +- 窗口 usable_now 投影工作正常:满血下治疗包全程禁选(usable_now:false),玩家实际行为 = 连续 skip,机制按设计执行。 + +### Pain points +- **P1(旧问题②):全关零承伤**——12 个结算快照 hero hp 恒 50/50,Boss 房前满血、Boss 战后仍满血。对照 C3 F4 目标带(普通场承伤 ≤20%、Boss 场 ≤50%):实测 0%,越带。详见「根因分析」。 +- **P2(旧问题①):治疗包体感过剩**——Boss 战结束时库存 3 件治疗道具 + 已用 1 把小刀;用户体感"9:1 完全反过来"。实现核对结论:**9:1 配比实现正确,体感由三个结构性放大器制造**(见根因分析)。 + +### Confusion points +- 未采集。 + +### Moments of delight +- 未采集。 + +## Bugs Encountered +| # | 描述 | 严重度 | 可复现 | 状态 | +|---|---|---|---|---| +| 1 | **浅深度战斗零敌人**:两场真人局中 d0 战斗(20:13 还含 d1)`unit_count=0`、round 1 即 victory——遭遇骰/spawn 路径疑似缺陷(C3 Rule 8 区间 [unit_count, unit_count_max] 最小值 1,不应掷出 0) | Medium | 两次真人局均现(d0 100%) | 待查——建议 /bug-report 建档 | +| 2 | **Boss 攻击拍零兑现**:ogre_chief(pattern [attack, attack, charge, heavy])在两局中共 7 个存活结算轮,快照 beat 轮转正常(advance 在跑)但英雄血量纹丝不动——攻击链对该敌未生效(普通敌在 20:41 局 fresh 池轮正常承伤:d1 r1 −34、d3 r1 −9, ambush 乘区算术精确吻合) | High | 两局均现 | 待查——与下条根因共用一个探针 | +| 3 | (轻微)`grant_node_active` 直调 stock、不经 `_supply.notify_granted()`——节点奖励完全绕开供给控制器计数,关卡级供给总量失控的结构性漏洞之一 | Low | 结构性 | 待登记 | + +## 根因分析(本次报告核心增量) + +### 问题②难度:v3 调参为何无效 +- **防御墙机制**:defense_up = stack 策略 × magnitude −50 × max_stack 2,池 clamp [−100, +100](settlement.tres)→ **同轮叠两层 = 100% 免伤**。它在**授予段生效、先于战斗段**——当轮挖到的减伤当轮即全额兑现。 +- defense_up 权重 24/75 = 安全格池**最高**单行(32%/grant),每轮 7–14 安全格 × ~44% 授予率 ≈ 3–6 次 grant → 单轮叠满两层的概率常在 25–50% 区间;20:13 主分析局 12/12 轮零承伤。 +- **结论**:v3 批选的主杠杆"atk +50%"是**乘在 ×0 上的调参**——墙不拆,任何 atk 曲线都无效。C3 F4 的 E[减伤] 估算(0.44 × 50% ≈ 22%)严重低估了 stack-to-clamp 的实际行为(实测 ≈100%)。 +- **未闭合点**:20:41 辅助局 fresh 池轮普通敌正常承伤(−34/−9 精确吻合 ambush 算术),但 Boss 轮 fresh 池仍零承伤——"防御墙"与"Boss 攻击链缺陷"两嫌疑并存,**一次 instrumented headless 探针即可分割**(逐结算行打印 beat/atk.total/pool/d_tick)。 + +### 问题①道具获取:9:1 没有反,但体感是真的 +- **配比核对(三路径全部正确)**:active_inject 池 knife_t1 60 / knife_adv 30 / heal_pack 10 = 9:1(reward.tres 89–93 行注释明示"治疗资源宝贵");grant_node_active 同池同比;B1 分流正确(即时类当场兑付、道具类入库)。 +- **本局实测**:4 次可选类获取 = 3 治疗包 + 1 小刀(Boss r6 用刀后剩 3 治疗包——与用户口述完全吻合)。按 9:1 单次 10% 治疗率,该组合概率 ≈ 0.4%——但 **seed 42 固定**,这条 RNG 轨迹每次重开都原样重放,"反过来"的体感被逐次确认。 +- **三个结构性放大器**(配比正确也挡不住): + 1. **供给量**:09-11 供给控制器批把可选类从格子权重池改为"每战斗首件保证 + 指数递减"——每关 4 战斗 × 首件保证 + 1 奖励节点 + 递减重注 ≈ 4–5 件/关(P(≥1 治疗包) ≈ 40%),且节点奖励绕开控制器(Bug #3)再放大; + 2. **满血累积**:难度零压力 → heal_pack 永远 usable_now=false → 只进不出堆在库存里,显著度极高; + 3. **固定种子**:seed 42 让坏抽变成"每次都一样的坏抽"。 +- **耦合结论**:问题①与问题②**互为放大器**——20:41 辅助局(有承伤)里奖励节点的治疗包在 70/100 时被立即使用。难度修好后治疗包消耗通道自然打开,体感问题预计大半自行消解。 + +## Feature-Specific Feedback +### 可选类供给控制器(09-11 批) +- 机制按设计运转(首件保证/递减/永不绝育均可在遥测中辨认);但"每战斗"首件保证在 4 战斗关卡结构下的总供给量是否偏离"治疗是稀缺奖励"的原始意图,**归 C3 重新拍板**(候选:首件保证升到关卡域、或 inject 池治疗行单独降权)。 + +### 难度(v3 调参批后复测) +- 承伤 0% vs 目标带 ≤20%/≤50%:**严重越带**;通关率 100% vs F5 基线带 40–60%:越带。与 enemy.tres v3 注释自认的"难度严重不足"一致——**调参后未复测即判定"已处理"是本问题拖延三轮的直接流程原因**。 + +## Quantitative Data(遥测实测,playtest_42_20260912T201317.jsonl) +- 12 结算轮 / 7 节点 / level_end victory;hero hp 全程 50/50;ambush_layers 恒 0;buffs[] 恒空。 +- 敌情:d0/d1 空场;d2 bugbear 40hp(2 轮);d3 bugbear 18hp(2 轮);boss ogre 150hp(6 轮,beat 轮转 attack→attack→charge→heavy→attack,兑付伤害 0)。 +- 输出侧:charge_dmg_a 7–14/轮,boss 逐轮 −21/−16/−53(含 clear_blast 40)/−16/−24/−20——**英雄输出正常,Boss 3–6 轮内可斩**。 +- 可选类供给:4 获取(3 heal_pack + 1 knife_t1)/ 1 消耗 / 3 库存残留。 +- 20:41 辅助局(调参面板 max_hp 100 + atk 加成):普通敌承伤正常(−34/−9),Boss 仍零承伤;治疗包在缺血时正常消耗。证明消耗通道与攻击链在普通敌上健康,Boss 路径存疑。 + +## Overall Assessment +- **Would play again**: 未采集(用户两轮即时反馈 = 高关注信号) +- **Difficulty**: 严重不足(0% 承伤,越带)——**非数值问题,机制性免伤墙 + 疑似 Boss 攻击缺陷** +- **Pacing**: Boss 战 6 轮偏拖(输出侧正常但无压力回报) +- **本轮判定:COMPLETE**——两个旧问题本轮均拿到机理级解释,不再停留在"感觉不对" + +## Top 3 Priorities +1. **一次 instrumented 探针分割零承伤根因**:headless 逐结算行打印 beat / atk.total / hero_defense 池值 / d_tick——区分"defense_up 叠满墙"(设计变更)与"Boss/敌攻击链缺陷"(bug 修复)。**这是两问题的共同前置。** +2. **拆墙后重调难度带**:多种子 level_monte_carlo 对 C3 F4/F5 期望表复算(承伤 ≤20%/≤50%、通关率 40–60%),禁止单种子判定;v3 的 atk 值此时才第一次真正参与计算。 +3. **供给量重拍板 + 节点奖励纳入控制器**:C3 裁决"每战斗首件保证"的总量语义(候选:关卡域首件);grant_node_active 补 notify_granted 上报;评估用种子轮换(seed 42 单轨迹不能作为平衡证据)。 + +## Action Routing(Phase 3) +- **设计变更**(防御墙语义:stack×2→clamp −100 = 免伤;候选改法:clamp 收紧至 −60/−70、乘法衰减、重击拍穿防)→ `/propagate-design-change design/gdd/b3-status-effect.md`(连带 B1 池 clamp 语义 + C3 F4 估算式修正) +- **平衡调整**(墙拆除后:atk 曲线/Boss hp/供给量对 F4/F5 期望表复算)→ `/balance-check` +- **Bug**(浅深度空遭遇 #1;Boss 攻击零兑现 #2;节点奖励绕过供给计数 #3)→ `/bug-report` 正式建档 +- **Polish/观测**:治疗包满血堆积的呈现(现有 usable_now 禁选已正确,难度修复后观测残留量)→ 记入失衡预警②仪表(短缺半) + +## 与既往报告的追踪关系 +- playtest-2026-09-11(INCOMPLETE):Top2"难度带拍板"→ v3 批(09-12 18:05)只调了 atk、**未复测**→ 本轮证实无效 + 根因前置(防御墙)。状态:**未解决,根因收窄**。 +- 治疗稀缺化(09-11 两级奖励批):配比 6%→inject 池 10%(9:1)**实现正确**;本轮体感失效根因 = 供给量 + 满血累积 + 固定种子,非配比。状态:**配比已解决,体感未解决,路径已明确**。 diff --git a/production/review-mode.txt b/production/review-mode.txt new file mode 100644 index 00000000..d5ffd5c4 --- /dev/null +++ b/production/review-mode.txt @@ -0,0 +1 @@ +solo \ No newline at end of file diff --git a/production/session-state/active.md b/production/session-state/active.md new file mode 100644 index 00000000..1cac0839 --- /dev/null +++ b/production/session-state/active.md @@ -0,0 +1,126 @@ +# 会话状态:Dungeon Sweeper(扫雷 × 自动战斗,唯一状态源,每里程碑同步一次) + +- **阶段**:Systems Design(`production/stage.txt` = Systems Design,2026-09-07 写入) +- **系统索引**:`design/gdd/systems-index.md`(20 系统,含门禁修正 + 2026-09-07 增补记录) +- **概念文档**:`design/gdd/game-concept.md`(含目标受众 + Visual Identity Anchor + 创意风险登记) +- **游戏支柱**:`design/gdd/game-pillars.md`(4 条,GDD 评审尺度) +- **原型**:概念原型 demo 已删除(2026-09-12,垂直切片承接);验证报告留档 `docs/prototype-report-minesweeper-battle-concept.md`(v3,verdict PROCEED) +- **引擎**:Godot 4.7.2 + GDScript(2026-09-07 锁定;`.claude/docs/technical-preferences.md` + `docs/engine-reference/godot/`;**MEDIUM RISK**——2026-09-12 `/setup-engine refresh` 逐条核实 4.5–4.7 官方迁移指南,无破坏架构契约项,current-best-practices.md 已建) +- **架构**:`docs/architecture/architecture.md` v1.0(2026-09-12 /create-architecture 产出,TD 自评 APPROVED WITH CONDITIONS;review-mode.txt = solo) +- **最近门禁报告**:`production/gate-checks/gate-check-systems-design-2026-09-07.md` + +## 项目当前位置 + +1. 概念原型验证:v2 核心假设 CONFIRMED,v3 终验待做(与标错惩罚实测合并为一次会话),REPORT 已归档 PROCEED +2. `/map-systems` 完成:20 系统四层分解 + GDD 设计顺序 + 优先级 +3. `/gate-check systems-design` 重审(2026-09-07):CONCERNS 无阻塞 → 登记级意见已清偿进 systems-index"门禁增补记录",stage.txt 已写入 +4. **D2 已收口**:两轮 /design-review(full + 同日 re-review)+ 独立 delta 复核 5/5 PASS → **Approved**(2026-09-07)。A3 定稿回填 ActionEconomyTable(AP 8/旗 4/停手 ×2/清图 ×1),D2 Tuning Knobs 对应条目已按定稿删除义务移除,registry 已登记 `action_economy`,索引补齐 C2/B1/B4/C4←D2 四边 +5. **A3 已收口**:GDD 全节已写入(`design/gdd/a3-action-economy.md`,Status: Approved 2026-09-07 三轮 re-review;评审日志 `design/gdd/reviews/a3-action-economy-review-log.md`),registry 已登记 `action_economy` +6. **A4 已收口**:两轮 /design-review(2026-09-08 full 8 阻塞 + 同日 re-review 6 阻塞,均同日核销)→ **Approved**(用户拍板 delta 核销免第三轮,A3 先例)。承重数学(厚度≤2【≡任意 3×3 窗口非全雾】/f≤8d/两态 AP/F1 谓词)经两轮对抗零动摇;承重规则不变(生成期布置约束 + 零特判 + fog unlock_level=2)。D2/registry/systems-index 三方已同步(跨字段校验 hide_numbers⇒safe_nonzero 致命、B4 三条定价硬约束、A1×15/A2×4/D1×2/B1 输入门禁登记全枚举)。评审日志:`design/gdd/reviews/a4-terrain-variation-review-log.md`(含两轮阻塞→修复对照表) +7. **B3 设计会话进行中(2026-09-08 启动)**:上下文预收集同日完成——① review mode = lean(review-mode.txt 不存在,取默认,与索引头部一致);② 引擎域 = Core,无引擎障碍(纯数据+回合逻辑,4.7 变更清单零交集;Core 零 UI 依赖、结算禁 await 为设计约束);③ **已锁事实不得默改**:己方限时增益跨轮次延续(按剩余结算次数)、不跨战斗【game-concept 2026-09-06】;三向效果映射(安全格→主角 buff/开雷→敌方增益/标中雷→怪物 debuff)【v3 定稿】;标中旗一次性兑付、资格以雷格实例为键、拔旗重插不恢复【A3 OQ5】;debuff 档位锚严格 > 最低档安全格 buff 当量、对照基准=连锁放大边际挖掘收益【A3 F1 条款 2】;安全格 buff/标中旗 debuff/清图奖励/敌方增益统一当量单一线性换算、B1 闭合 owner【A3 F1 条款 1】;安全格奖励有回合限制甚至一次性、扫雷只入库轮末结算、P2 要求结算明细标注来源;④ 数据承载 = D2 StatusEffectTable 占位行("buff 时限/叠层字段名",单行全局,owner=B3 回填);⑤ B3 为 Foundation 瓶颈系统,下游 B4/B1 未设计——接口契约需 provisional 登记(A3 条款 1/3/5 的 provisional 先例现成);⑥ 无 docs/consistency-failures.md;既往一致性检查 0 冲突 +8. **B3 已收口(Designed,2026-09-08)**:GDD 全节已写入(`design/gdd/b3-status-effect.md`,12 节零占位;lean 门禁 systems-designer/qa-lead 已咨询;CD-GDD-ALIGN 按 lean 跳过)。定稿要点:战斗域统一状态容器(三向效果同构实例)· 单一结算次数量纲(先兑付后衰减)· 按行叠层三分支(stack 不触碰 R)· MVP 四行 attack_up/defense_up/ambush/weaken(**值全部 provisional;weaken vs A3 条款 2 锚预检不过——红色预警,B1 对照验收裁决**;用户拍板四行定稿权收编 B3、锚验收权留 B4/B1——A3 OQ5 修订已登记索引)。registry 已登记 `status_effect` + action_economy.referenced_by 增 B3;D2 Core Rules/Status 已回写;索引门禁增补记录已登记 B3 块(**B1×5 义务清单——B1 GDD 门禁评审缺项即 blocking**;EC9 reload gate B1 落点;ambush 量纲→B2)。下一步 = **A1 扫雷棋盘生成(设计顺序第 5 位,MVP)**——注意 A4 已登记 A1×15 验收清单 + B3 登记 ambush/weaken 锚供给义务,A1 GDD 门禁评审须逐项核对 + +## GDD 设计顺序(前 10 = MVP) + +D2 数据配置 → A3 行动经济 → A4 地形变异 → B3 增益状态 → A1 棋盘生成 → A2 排雷玩法 → B2 敌人系统 → B4 奖励经济 → B1 战斗结算 → D1 HUD →(垂直切片:A5 → C3 → C2 → C1 → B5)→(Alpha:D3 → D4 → D5 → C4)→ D6 + +## GDD 写作纪律(制作人门禁意见) + +- 每个 MVP 系统 GDD 一次会话完成:决策定稿 + 可调参数 + 事件接口,不写 20 页完美文档 +- 总监修正意见已按系统登记在 systems-index"门禁修正记录",写对应 GDD 时落实 + + + + + +9. **Task: Designing a1-board-generation GDD**(2026-09-08 启动)· File: `design/gdd/a1-board-generation.md` +10. **A1 已收口(Designed,2026-09-09)**:GDD 全节已写入(12 节零占位,~405 行;lean 门禁 systems-designer/qa-lead 已咨询;CD-GDD-ALIGN 按 lean 跳过)。定稿要点:五阶段确定性生成管线 · inferability = 免赌开据占比(F1 三通道 + 雾元知识信息源 + 次序无关 + metric_version)· clearable 合法性闸与质量度量两闸分离 · 验收三分支(达标即出/接受最优/**零合法接受最不坏**——用户定稿玩家永不卡死)· F4 批量红线**严格零**(用户定稿)· BoardGenTable 九字段回填(首个进度键表,solver_rules 冻结,参考行 provisional 归 C3)。registry 已登记 `board_gen_table` + action_economy.referenced_by 增 A1;D2 两处回写;索引门禁增补记录 A1 块(**A2 双包含回验——A2 GDD 门禁缺项即 blocking**;B1 对账消费 actuals p10 非 target;F4 全量工具与 D2 OQ5 同批排期;黄金向量第二人复核)。**下一步 = A2 排雷玩法规则(设计顺序第 6 位,MVP,瓶颈系统)**——硬输入:A3 事件契约(round_end 双发射源/try_spend/last_round_end)、A4×4 验收清单、A1 双包含回验 + 产物消费、标错惩罚待原型小补丁实测后关闭(CD 门禁:可与原型 v3 终验合并会话) +11. **A2 已收口(Designed,2026-09-09)**:GDD 全节已写入(`design/gdd/a2-minesweeper-rules.md`,12 节零占位,365 行;lean 门禁 systems-designer〔Formulas F1–F4〕/qa-lead〔AC 22 条 + 覆盖矩阵 + 夹具坐标规格〕已咨询;CD-GDD-ALIGN 按 lean 跳过)。定稿要点:纯规则引擎(三事件回边 cell_revealed/flag_judged/round_end 发射面封闭 · 零随机/零表读取/零 UI,headless 解耦)· 初始展开 = R₀ 恰等(双包含两臂构造性成立、A1 回退口径不触发;免费/计入清图/无奖励——A3 EC5 委派落点)· 批次语义序仲裁(cleared-dig→stop→其余;A3 EC4 四组合 deferral 在 A2 冒烟兑现 AC-10)· 旗单态轮末判定诚实重复上报(去重归 B1/B4)· 已引爆雷格可通行(工程债核销 AC-15)· install 四不变式。**🔴 反例 B 阻断缺口(systems-designer 发现、主会话独立复算证实)**:A1 布雷数下限拦不住"初始展开即 cleared"退化板(8×8 幸存距离-2 雷 → R₀=S;N=1 行 100% 退化、断言 A 零拦截)——登记 **A1 修订义务**(F5 legality 增 R₀≠S + F4 断言 + 排除域基准措辞;A1 已 Approved 走接口漂移)+ A2 侧不变式④纵深防御。registry 已登记 `a2_event_surface` / `a2_initial_expansion`;systems-index 已登记 A2 门禁块(**A1 修订义务 + B1×3 + D1×1 强制项**;标错惩罚 OQ1 在位——/design-review 核对项)。**下一步 = B2 敌人系统(设计顺序第 7 位,MVP)**——硬输入:B3 单敌假设回验(定多敌即触发 target_ref 接口漂移补列)、ambush 量纲定稿义务(registry anchor:"+3" 单位候选"每颗已开雷伤害加成",联动 A3 F1 条款 3 上限表值)、EnemyTable 回填(D2 占位:词条/意图可见性字段) +12. **B2 已收口(Designed,2026-09-09)**:GDD 全节已写入(`design/gdd/b2-enemy-system.md`,12 节零占位;lean 门禁 systems-designer〔Formulas F1–F4〕/qa-lead〔AC 17 条 + 覆盖矩阵 + 缺口预检 7 项〕已咨询;CD-GDD-ALIGN 按 lean 跳过)。定稿要点:多敌模型(1–3 同敌种,B3 单敌假设回验不成立→side-wide 零接口变更兼容 + target_ref 扩展位)· HeroTable 新增(接口漂移,atk=0 机制开关)· 意图视觉推测型(CD 承重项:图像线索稳定可学/每 run 恒定/结算明细数字兑现)· Peglin 行为环(attack/charge/heavy MVP,双敌种 grub/ambusher 双环,死亡即不动手 + 同步轮转定理)· 伏击量纲定稿(registry OQ2 关闭:每攻击类行为 +3 HP、charge 零兑付)· cap=4(反灾难护栏;旧 8 溢出协议死码废弃)· 条款 3 单结算自然边界(用户定稿)。registry 已登记 enemy/hero/clause3_supply 三条目 + status_effect 两注记 + terminology 六词条;D2 三处回写(Core Rules 表 EnemyTable/HeroTable 行 + Dependencies 行 + Status);索引 B2 进度行 + 门禁增补记录 B2 块(**B1×6/D1×4 义务清单——B4/B1/D1 GDD 门禁评审缺项即 blocking**)。**下一步 = B4 奖励与道具经济(设计顺序第 8 位,MVP)**——硬输入:A3 F1 条款 2 锚(weaken 档位严格 > 最低档安全格 buff 当量)+ A4 三条定价硬约束、奖励身份预读(B3 义务)、伤害道具默认目标消费(B2 固定顺位)、HeroTable.atk 激活裁决参与、高光兑现节点条目化(B1/B4 共担)、clear_reward 源授予路径确认(B3 OQ4) + + +13. **B4 已收口(Designed,2026-09-09)**:GDD 全节已写入(`design/gdd/b4-reward-item-economy.md`,12 节零占位,354 行;lean 门禁 systems-designer〔Formulas F1–F6〕/economy-designer〔权重参考行 + 失衡预警清单〕/qa-lead〔AC 21 条 + 覆盖矩阵 + 缺口预检 9 项〕已咨询;CD-GDD-ALIGN 按 lean 跳过)。**会话注记**:另一会话并行写入 E/F/G 三节与本会话冲突,经用户裁决本会话接管收尾——E 节按"照常消耗"拍板合并(覆盖并行稿"拒绝不消耗"条目)、F/G 两节保留并行稿。定稿要点:install 期身份预分配(roll_board 恰一次、Ready 只读)· 道具使用窗口 MVP 激活(段序骨架 = 授予→增益/减益→道具→战斗→衰减→判定)· 库存战斗域(battle_over 全清)· 道具伤害恒定表值(attack_up 死行、A3 条款 2 锚参照迁移 defense_up)· F5 保底草稿重 roll · E 节两项拍板(照常消耗 / 清图槽允许 buff 行)。registry 已登记 reward_table + 三处漂移改写;D2 六处回写;索引 B4 进度行 + 门禁增补记录 B4 块(**B1×6/D1×3 义务清单缺项即 blocking**;缺口预检 2 阻塞级 deferral 至 B1)+ 分层行 B4←D2 + B3 块 EC8 错号修正。**下一步 = B1 战斗结算规则(设计顺序第 9 位,MVP 瓶颈系统)**——B4 定稿解锁:胜利路径先决已备(B2 Overview 声明的解药);硬输入:B1 义务清单累计 **B3×8 + B2×6 + A2×3+2 + B4×6**(全缺项即 blocking)、weaken 锚红警裁决(B3 OQ1 归一口径前置)、A3 F1 条款①–⑤闭合 owner、A4 硬约束②③对账、EC8 reload gate 落点、事件总线载体裁决(TD 门禁 2026-09-07 登记) + +14. **B1 已收口(Designed,2026-09-10)**:GDD 全节已写入(`design/gdd/b1-settlement.md`,12 节零占位,628 行——项目最大 GDD;lean 门禁 systems-designer〔Formulas F0–F5 + 对账义务清单〕/qa-lead〔AC 43 条 + 覆盖矩阵 + 缺口预检 12 项〕已咨询;CD-GDD-ALIGN/art-director 按 lean 跳过留人工复核注记)。定稿要点:**编排唯一性**(B2/B3/B4 战斗域 API 唯一调用方 + 可回放步骤流〔结算禁 await——D2 AC-15 兑现落点 AC-33〕+ 零随机)· **结算触发 = Waiting 相位编排点**(不依赖事件到达序——A2 义务①)· **段序骨架**(授予→窗口〔授予段后〕→增益/减益→道具→战斗→衰减→判定——B4 义务①原文)· **23 条上游义务全落位**(B3×8/B2×6/A2×3/B4×6,AC 覆盖矩阵 23/23,引用带清单限定词)· **F0 归一口径裁决**(瞬时当量;weaken −25→值域 [60,85]/默认 −65——B3 红警关闭)· **条款①③⑤闭合首版验算**(条款③默认通过、满配 unit_count=3 破线→C3 约束;条款⑤两态通过+保守地板缺口登记;actuals p10 交付后强制重跑)· **高光双落**(#2 完美排雷 ×P_perfect 默认 ×2 / #3 标中旗 highlight——用户拍板)· **战斗段短路**(hero hp=0 即中止——用户拍板)· SettlementTable 七字段回填(三行致命 manifest)。回写已执行(用户批准):registry 新增 settlement_table/clause5_dominance + 四处接口漂移(weaken 值/anchor_warning F0 回执/clause3_supply 闭合/board_gen 消费回执)+ 三处履行回执 + terminology 五词条;systems-index B1 进度行 + 门禁增补记录 B1 块(**D1×3 义务清单缺项即 blocking** + C3 消费注记 + UX Flag + ADR 裁决项×2);D2 SettlementTable 行回填。**下一步 = D1 HUD 与界面框架(设计顺序第 10 位,MVP)**——硬输入:全项目最大呈现义务台账(A2×6/B2×4/B3×4/B4×5/**B1×3**)+ art-bible §1–4 创建门禁(最晚 D1 GDD 前)+ 事件订阅呈现刷新面 + UX Flag(结算屏/道具窗口前置 /ux-design) + +15. **垂直切片启动(2026-09-10,/vertical-slice Phase 3 检查点)**:概念名 **dungeon-sweeper**,目录 `prototypes/dungeon-sweeper-vertical-slice/`(用户已确认开工)。**验证问题**:玩家从零开始一场战斗,能否无指导 10 分钟内经历完整「开战→挖掘规划→轮末结算逐行兑现→胜/负」循环并逐行归因(P2)——以及能否按 GDD 约束(表值单点/RNG 封闭/结算可回放/Core headless)5 个工作日内以代表性质量建完。**范围内系统**:D2(9 表)· A1(含 single_subset 求解器 + 双判据验收,用户拍板完整验收)· A2 · A3 · A4(迷雾)· B1 七段编排全量 · B2 双敌种 · B3 四行 · B4 install 预分配+窗口 · D1-lite 程序化呈现(art-bible 明亮风)· encounter 直供两场(L1×grub / L2 雾×ambusher)。**裁剪**:A5/C1–C4/B5 动画/D3–D6/A1 黄金向量与 F4 批量工具/gdUnit/标错惩罚/多战连续。**架构基线**:docs/architecture 不存在——CLAUDE.md 约定 + D2 Rule 1/2/5/6/7 为事实控制清单(切片头注登记;正式 /create-architecture 留切片验证后)。**成功判据**:①无指导完整战斗≤10min ②逐行归因 ③headless 回放逐行相同+冒烟<30s ④Core 零 UI/零数值字面量 ⑤第 3 日循环可演示 ⑥硬时限 5 个工作日。当前阶段:**Phase 4 — Implement(Day 1)**。速度日志:Day 1 开工。 + +## 垂直切片速度日志(/vertical-slice,2026-09-10 启动) + +- **Day 1(2026-09-10)**:上下文加载(9 份 GDD + registry + 原型)→ 范围确认(用户拍板全量范围 + A1 完整验收)→ 全部核心代码一次性落成(D2×9 表 + ConfigDB + RngService、A1 求解器与五阶段管线、A2/A3/B1–B4、D1-lite 呈现、冒烟框架)→ headless 冒烟迭代 7 轮修错后 **39/39 全绿(约 3 秒)**:表加载校验、R2 黄金向量 5/5、A1 双判据+确定性、两遭遇全战斗 3 轮终局、结算步骤流回放逐行相同。**修掉的关键 bug**:① B4 换图后 Retired 相位拒绝重 roll ② A2 `_do_dig` 对闭包全量发事件(应只发 O′∖O——重复奖励行根因)+ chain_size 口径 ③ B1 轮末最后一挖事件被相位守卫丢弃 ④ GDScript 不允许 Dictionary==bool。**重大设计发现**:A1 参考行 d0.10/0.12 经先布后删后仅幸存 2-3 雷、R₀/|S|≈0.97、残余挖掘 2-4 格(掘近退化板,推理率门槛不可见)——切片临时调参 d0.25/t0.88(数据 + 工具 `smoke/experiment_density.gd` 留档,README 登记表)。 +- **状态**:循环可演示(Day 1,远早于 Day-3 检查点)。**下一步 = Phase 5 playtest**(用户跑 F5 人工试玩 → 按 vertical-slice 技能问询 → Phase 6 报告)。 + +16. **2026-09-11 拍板四条落地(切片程序调整完成)**:① atk 恒存在([1,999] 拒 0、MVP=6、战斗段主角先手、attack_up 唯一修正——attack_up 死行复活)② 奖励两类(即时 75% 五类法 {buff,instant_aoe,instant_heal} 当场兑付 / 可选 25% 入库走窗口、heal_pack w6 p12)③ 库存关内延续(battle_over 不清、level_clear 清、UI 关卡模式共享 B4+HP 延续)④ 窗口至多一件(核心截断+文案)。实现主体由并行会话完成,本会话核对补缺口:settlement source_label_map 键去重(hero_action→hero_attack)、config_db required_sources 增 hero_attack、窗口文案、冒烟加 4 条拍板断言。**冒烟 43/43 全绿**;dump 验证:主角攻击 9=6×1.5、减伤叠满免伤轮、dur=1 当次衰减、治疗包入库、伏击 cap4 溢出行。**待办**:五项 GDD/registry 回写(README"2026-09-11 拍板"节全列);playtest Phase 5 仍未做(用户试玩曾因窗口字段 bug 中断,已修)。 + + **更正(同日)**:条目 16 所列"待回写 GDD/registry 清单"作废——用户已在文档会话先行完成全部回写(b1/b2/b4 GDD、registry、systems-index、d2、game-concept、b3 的 2026-09-11 批),程序调整即以该批文档为依据。本会话完成的是:程序与文档的逐项对齐核查(五类奖励/75-25 权重/heal_pack w6 p12/库存关卡域/窗口单件含"取首件+诊断"/hero_attack 来源值/锚参照回迁 attack_up/reward tier 行集),README 已改记为"文档对齐核查记录"。冒烟 43/43 维持有效。 + +17. **垂直切片教训 → D1 GDD 设计输入(2026-09-11 登记,D1 GDD 门禁评审核对项)**:切片试玩两起呈现层崩溃(窗口条目缺字段 round_index / battle_finished 发出后读已销毁的 b2.hero)暴露系统性盲区——已定稿 AC 全部覆盖 core 层,D1 呈现层无测试承载,且 GDScript 编译不查字典字段与时序。**D1 GDD 三条硬性要求(缺项即 blocking)**:① 每个面向呈现的信号载荷成文 schema(对齐 A2 三事件先例,字段清单入 GDD)② 场景级 headless 测试进 AC(真实实例化 UI 场景 + 驱动完整对局 + 断言零脚本错误;切片的 headless UI 冒烟为其原型)③ 信号时点状态可见性规则(销毁前捕获、载荷数据自包含、禁止呈现层回读核心生命周期状态)。另登记:gdUnit4(D2 OQ1)若引入,UI 场景测试以此为载体。 + +18. **战斗期望值现状核查 + 实测(2026-09-11,用户问询触发)**:文档侧——**"期望胜轮数 / 期望每轮承伤 / 期望败北轮数"无任何直接设定**;最近的三个既有数字全部过期:① B4 刀供给方向预检(2026-09-09:击杀需求 18/54、供给≈2|资格格|)基于旧权重与"无基础攻击"口径未重算 ② B1 F5 Δr(清图轮数 L1≈2/L2≈4)基于旧密度参考行 ③ B1 E_dig=7.2(旧权重 40/25/35)。C3(难度曲线,此类期望的正当 owner)未开始设计。**实测(smoke/monte_carlo.gd,30 种子 × L1/L2,理性政策)**:胜轮均值 1.80(L1 {1轮×6, 2轮×24} / L2 {1×7, 2×22, 3×1});每轮承伤均值 2.1–2.7、整场承伤中位 0–8(max_hp=50 的 0–16%);每场开安全格 7–8、**每场恰清图 1 次**;败北 0。解析锚:零增益击杀地板 = ⌈18/6⌉ = 3 结算,单层 attack_up → 2 结算。**预警⑥定量坐实:战斗长度与承伤均远低于可玩带,难度杠杆余量巨大**。登记:① C3 GDD 设计输入 = "战斗期望表"(期望胜轮带/期望承伤占 max_hp 比/期望开格数/败北轮期望,按 (L,D) 曲线)为强制章节 ② B4 预检 + B1 E_dig/F5 重算义务随 2026-09-11 批触发 ③ smoke/monte_carlo.gd = C3 调参期战斗侧批量工具原型(对应 A1 F4)。 + +19. **垂直切片 v2——第一关完整关卡(2026-09-11,/vertical-slice 扩展批)**:用户拍板线性第一关(无路径分支):**战×2 → 随机主动道具点 → 战×2 → 血量恢复点 → Boss 房**,同步进入数值设计(元进度模拟)。落地:① **LevelTable**(新表 `level.tres` + schema——线性节点链数据位,节点 enum {battle,reward,rest,boss},末节点必须 Boss;ConfigDB 挂表 + 跨表校验⑧"battle/boss 节点 (L,D) 在 board_gen/enemy 有精确行")② **C1-lite 推进器** `core/c1_level_runner.gd`(关卡域生命周期 owner:共享 B4 + HP 携带 + 节点兑付 + 关卡末 level_clear + 测试面板 hero 覆盖;Core 零 UI 依赖)③ **B4.grant_node_active()**(接口漂移登记——可选类活行 roll 一件入库,reward_roll 恰一 scope)④ **B2 spawn max_hp 修复**(原 max_hp=hero_hp 入参压扁携带 HP——恢复点/治疗包满血守卫失效)⑤ EnemyTable L1 深度轴 D0–D4(蛆虫24×1 / 蛆虫群24×2 / 伏击兽36·atk6 / 伏击兽群26×2 / Boss 遗迹守卫130·atk7·环[A,A,C,H])⑥ BoardGen D1–D4 行(D0/D1 10×10、D2-D4 12×12;**d0.28/t0.87 不达标降级告警 best≈0.81 已回退 0.25/0.88**)⑦ UI:节点进度条 + 奖励/恢复覆盖层 + **测试用角色数值调整板**(atk%/hp% 0–300 滑条 + 批量模拟——与 headless 扫描同口径 `smoke/level_autoplay.gd`)。工具:`smoke/level_monte_carlo.gd`(atk%×hp% 网格扫描:通关率/承伤期望/败北节点分布——C3 关卡侧批量工具原型)+ `level_debug.gd`(单关诊断)。冒烟 55/55(新增关卡 10 断言:满配通关/裸装败北/奖励点入库/恢复点公式/Dmax 解析/关卡回放确定性/hero 基线恢复)。**首版调参实测**:敌 HP 208 时基线通关率 73%——预警⑥关卡尺度坐实,v2 上调至 290 后复测中。**登记**:A4 迷雾 unlock_level=2 纪律遵守(L1 全程无雾);GDScript 多行 lambda 内嵌 match 解析不可靠(信号处理体一律静态方法+bind);冒烟两断言错误教训(level_clear 快照=清库前计数/恢复点公式须用生效 max_hp)。**v2 复测**:基线 55% 通关 / +50% 70% / +100% 100% / +100%hp 100%——"数值提升→通关期望改善"曲线成立(数据见切片 README)。 + +20. **Playtest 三缺陷修复 + 奖励期望汇报(2026-09-11,用户 F5 试玩反馈批)**:**缺陷① UI 抖动**——悬停文案换行推挤侧栏布局、"第 N 轮"库存行被挤出视口;修复 = 侧栏全部动态区固定槽高 + clip_contents(区域独立、超长裁剪不回流)。**缺陷② Boss 战 AP 用完不结算(软锁)**——根因 = **A2 核心 bug(v1 即存在)**:`_do_flag_place/_do_flag_pull` 花完末点 AP 后 A3 翻 Waiting 但不发 round_end,B1 的 Waiting 结算点依赖该事件 → 永不结算;v1 冒烟盲区根源 = 自动玩家不用旗。修复 = 旗动作补发 `_emit_round_end_flags_and_payload()`(与 `_do_dig` 同款);正式侧义务 = A2 GDD F4 事件序契约补"旗动作路径 round_end 发射"(接口漂移)。**新增测试载体**:`smoke/ui_click_probe.tscn` 行为探针(合成鼠标事件走真实 UI 路径,随机混合插旗/拔旗/停手/窗口期刷新/无效点击——4 种子全关通扫);smoke_all 加旗动作耗尽 AP 回归断言。连带:L1 12×12 板 max_gen_attempts 16→24(16 次偶发接受最优 best≈0.85–0.87)。**奖励期望汇报(只汇报未改值)**:每资格格 P(可选类)=25%;实测每关合计 ≈24.8 件(≈18.5 刀)——"道具期望太高"定量坐实。工具 `smoke/reward_expectation.gd`。 + +21. **可选类供给控制器(2026-09-11 用户拍板批——伪代码审阅后落地)**:用户重新定义三尺度指标——**格 <0.01 / 板≈0.78 / 战斗期望≈1(首件保证 + 指数递减)**,可选类退出格子权重池。拍板四条:① 注入占比小刀 60/精工 30/**治疗包 10**(治疗宝贵)② 清图奖励改**一次性高额伤害**(clear_blast instant_aoe 40——不入库不占供给,清图与可选类脱钩;首图清图必得=注入格必被打开的自然推论)③ 注入选格**均匀随机** ④ **指数递减**(active_p0=0.4/active_decay=0.5,RewardConfigTable 新参数)。**实现结构(对象封装,回应用户架构批评)**:新类 `RewardSupplyController`(B4 私有组合件:notify_battle_start/should_inject/notify_granted/granted_count;行值快照抗热重载;零自有随机数)· B4(_supply 组合/notify_battle_start 转发/roll_board 重写删 F5 循环/grant 挂钩"发放即获取")· B1(begin_battle 调 b4.notify_battle_start)· 表(reward.tres 三刀行移 active_inject 池 60/30/10 + clear_blast;k_dmg=0 F5 休眠)· 护栏(clear_no_value 含即时类/no_damage_row 扫全池/inject_pool 新护栏)。**实测**:注入 1.00/板 + 占比 60/30/10 + 零泄漏(N=200/深度);**实际获取 ≈0.68–0.91 件/战**(<分配保证 1.0 的机理 = 注入格均匀随机可能落残余格/速杀未挖区,攻击越高速杀越明显;**"战斗末 j==0 补发 1 件"兜底待用户拍板**);每关合计 ≈4.5–6.5 件(原 24.8)✓。冒烟 74/74(+14 供给断言:首件保证恒真/递减统计/占比/清图新语义/发放计数/战斗重置)。行为探针复扫通过(通关剩余道具 ×1——原 ×9)。**⚠️ 供给侧副作用(扫描发现,待拍板)**:可选类退出格子池释放的 25% 概率质量被动流向即时类——buff 每格频率 44%→59%,基线通关率 55%→80%、承伤 127→80,难度方向反了;"每格必有货"锁定下无法引入空白格,候选杠杆 = ①敌 HP 对冲(C3 主杠杆)②压 attack_up/defense_up 相对权重 ③降 buff 幅度(触 B3 表值)。**正式侧义务**:B4 GDD F5 章节修订 + registry reward_table/reward_config 改写(接口漂移批)。 + +22. **缺陷④ 道具使用后不消失(2026-09-11 用户 playtest 反馈批)**:可用次数语义本身存在且正确(`b4.use()` 一次性标记 Used 不可逆,库存过滤 InStock)——真实根因 = **UI 层契约违例**:B1 窗口选择语义 = `_window_items` 位置(义务④"选定序"),UI 选用按钮传 `item.index`(库存数组句柄);未消耗过时二者巧合相等,消耗过一件后句柄=位置+已耗数错位 → 选中件不消耗、邻件被误耗(Boss 战道具多、多次消耗后暴露)。修复 = UI 与行为探针统一传窗口位置;行为探针加"选用必生效"断言(选中件库存态必须变 Used,失效即退出码非零——种子 7/23 实测生效 1/2、失效 0)。冒烟 74/74 维持。**登记义务**:B1 义务④"选定序"在 GDD 中补一句"选择序列元素 = 窗口条目位置、非库存句柄"(防正式实现复发)。 + +23. **缺陷⑤ 数值调整板被遮挡 + 专属测试补建(2026-09-11 用户质询批)**:用户质询"调整板没实现/没测试"——实现存在(含批量模拟),但**层级 bug 使其不可见**:调整板覆盖层位于不透明开始覆盖层之前(场景树序),打开时未隐藏开始层 → 面板被完全遮住 = "点击无反应"。修复 = `_show_tune_panel` 隐藏开始层 / `_close_tune_panel`(含"应用并关闭")恢复;顺带删除重构引入的重复函数定义(同名 parse error)。**专属测试补建**:`smoke/ui_tune_probe.tscn`(场景级全链路 7 断言:打开可见性〔缺陷⑤回归〕/ 滑条→生效预览联动 / 应用写 hero 表 12/75 / 重置回 6/50 / 批量模拟 N=10 全链路〔通关率+败北节点输出〕/ 关闭回开始界面 / 加成跨模式持久)——PASS。全量回归:冒烟 74/74、ui_probe PASS、行为探针种子 7 通过(选用生效 1/失效 0)。**教训登记(D1 GDD 输入④)**:覆盖层堆叠顺序 = 呈现层隐形缺陷高发区("功能存在但不可达"),任何模态覆盖层必须有"打开时遮挡物隐藏"的场景级断言;此三类缺陷(①②⑤ 呈现时序/层级 + ④ 呈现契约)全部漏过既有 AC——呈现层场景测试从"建议"升级为切片标配(ui_probe/ui_click_probe/ui_tune_probe 三探针)。 + +24. **呈现层模块化重构(2026-09-11 用户批——"上帝对象"拆分)**:用户工程师视角否决 main.gd ~1100 行上帝对象。拆为 **9 个单一职责单元**(presentation/views/ + hud_presenter.gd):BoardView(棋盘渲染+输入意图翻译,只读 A2、动作信号外发不碰核心写)· HUDView(侧栏纯视图,setter + 定长槽 clip)· HudPresenter(静态装配器:核心只读面 → 文案,含 outcome_text 三源拒绝映射)· BattleLogView(13 类行协议格式化全内聚)· ItemWindowOverlay(窗口;"选择=窗口位置"契约的呈现侧收口)· NodeOverlay · LevelStripView · TunePanel(自持加成语义 + 批量模拟,awaitable run_simulation)· UiKit。**组合根纪律**:main.gd 433 行(原 ~1100)= 模式流转 + 信号接线;核心写调用(settle_batch/submit_window_selection/runner 推进)唯一发生在组合根。三探针同步迁移到新结构(探针即呈现层契约的活文档)。**回归**:冒烟 74/74 · ui_probe/tune_probe PASS · 行为探针种子 7/23 全通关(选用生效 1/2、失效 0)。**遗留改进位(登记不阻塞)**:调试两层模式的历史入口仍在 main(C2 落位时退役);HudPresenter 为静态装配器,正式 D1 可演进为信号驱动。 + +25. **Core 层领域命名批(2026-09-11 用户问责 → AskUserQuestion 拍板)**:用户质询 core/ 的 a1_/b1_/c1_ 前缀命名("规范缺失还是能力不足")。**定性**:ID 前缀 = 跟随 GDD 文档名的事实约定(意图 = 设计↔代码追溯),但从未成文、且同目录 board_data/config_db/rng_service 无前缀——两套命名并存即规范缺失铁证。**拍板 = 改领域命名**:8 文件机械更名(a1_board_generator→board_generator、a2_board_rules→minesweeper_rules、a3_action_economy→action_economy、b1_battle_orchestrator→battle_orchestrator、b2_unit_container→unit_container、b3_status_container→status_container、b4_reward_economy→reward_economy、c1_level_runner→level_runner),类名同步去前缀(BoardGenerator/BattleOrchestrator/LevelRunner…),.uid 随行保资源身份;全项目 .gd 引用(15 文件)批量替换;每文件头注新增「实现 GDD:design/gdd/-.md(系统 ID Xn)」溯源行——**系统 ID 退役出代码命名,保留为文档/registry 追溯锚**。**规范成文**:CLAUDE.md 新增「代码命名规范」节(领域命名禁止 ID 前缀 / 头注溯源义务 / D2 schema 与 autoload 豁免 / 文档层夹具名不受约束)。**回归**:残留引用 grep 清零 · 冒烟 74/74 · 三探针全 PASS(含行为探针选用生效 1/失效 0)。 + +26. **代码规范成文 + CLAUDE.md 必读门(2026-09-11 用户拍板)**:用户令将全部已指出的陋习总结成规范文档。落地:**`docs/code-conventions.md`**(新建)——六类纪律(结构:禁上帝对象/类封装/依赖单向/领域命名/禁事实约定;契约:信号事件成文与发射完整性/标识符语义单一/覆盖层层级/状态字段显式;测试:场景探针标配/打开可见性断言/缺陷沉淀回归断言/提交前全量;语言:GDScript 六条实战坑;数值:口径显式/先测后调/跨尺度推演/拍板零发挥;流程:伪代码审阅→实现→双登记)+ **缺陷-规范对照表**(缺陷①②④⑤、上帝对象、ID 命名、可选类 24.8 件——每条规则挂本项目真实反面案例)。**CLAUDE.md**:原命名节替换为「代码规范(写代码前必读)」门 + 摘要,指向 docs/code-conventions.md 为单一事实源。回归:冒烟 74/74、ui_probe PASS(文档批不触代码,例行确认)。 + +27. **轮内刷新方案 A 落地(2026-09-12 用户拍板批)**:用户确认刷新实现(仅 Waiting 受理)与设计预期不符(预期 = 扫雷阶段遇推理死路、有剩余 AP 且 RAP 足够即当场刷新继续挖)。**影响分析先行**(三方案对比:A 轮内不打断轮次 / B 停手+刷新组合宏 / 时间税差异点破——"刷新是否触发敌方结算"是唯一实质差异;16 代码触点 + 8 文档 + 5 数值连锁清单)→ 用户拍板 **方案 A**:Active 相位当场换图继续挖、AP 保留不重置、不触发结算、RAP 够即可、**每轮兜底上限 2 次**(表值 refresh_max_per_round)。**实现**:A3 `request_refresh_active()`(仅扣 RAP)· A2 install/retire 增 `during_round` 显式通道(其余守卫不变)· B1 `can_request_refresh()` + 双相位重写 + 每轮计数 · **facts 身份快照隔离**(实现批新暴露的深坑:轮内换图后旧图已挖未结算格与新图坐标重叠,轮末 grant 错拿新图身份——解法 = 缓冲期 `query_full_snapshot` 冻结身份、授予直通快照绕过现行 mapping/_granted)· B4 `query_full_snapshot()` · HUD 按钮"弃图刷新(RAP -8 · 每轮至多 2 次)"。**验证**:冒烟 88/88(+14:当场换图/相位 AP 保留/RAP 扣减/上限拒绝/跨图 facts 完整兑现〔4 格跨三图〕/回放确定性)· 三探针全 PASS · 期望扫描持平(模拟策略不刷图——真人专用出口,playtest 观测项)。**调试教训**:蛆虫 HP 24(已调)误判 18 未掉血——排查先核对当前表值;GDScript lambda 对局部 int 按值捕获(smoke 计数器改容器)。**正式侧义务**:A3/B1/B4 GDD + game-concept 层级措辞 + registry 修订清单见切片 README"2026-09-12 轮内刷新"节;数值连锁(清图支配性条款⑤/真人刷图行为获取期望)待 playtest 实测。 +28. **Task: Designing c3-difficulty-curve GDD**(2026-09-12 启动,/design-system --review solo)· File: `design/gdd/c3-difficulty-curve.md` · 上下文摘要已呈现(上游 D2/B2/B1 全 Approved;锁定事实:clause3_supply 满配破线约束、B1 条款⑤未闭合约束包、A1 Boss=Dmax 约定 + OQ6、A4 scope=global 语义位、B4 全部 provisional 值归 C3;切片实测现状:敌深度轴 D0-D4/供给 p0-0.4/基线通关 55-80%) + +29. **主架构文档产出(2026-09-12,/create-architecture 全流程,solo 模式)**:`docs/architecture/architecture.md` v1.0 已写入(TD 自评 APPROVED WITH CONDITIONS;LP-FEASIBILITY 按 solo 跳过;`.claude/docs/director-gates.md` 缺失登记 QQ-10)。要点:① 20 系统五层映射(依据 systems-index 依赖分层,切片实现验证)② 模块所有权地图(Owns/Exposes/Consumes/引擎 API,含切片新增件 RewardSupplyController/LevelTable/level_runner)③ 四条数据流(帧更新/事件信号/存档快照/初始化顺序)④ API 边界契约表(GDD AC 前言为规范源)⑤ ADR 审计:现存 0 份;63 条 TR 可追溯性 = 46 条架构文档+GDD 承载 / 12 条待 ADR / 5 条随未设计系统,0 孤儿 ⑥ **Required ADRs 13 项分级**(必须编码前:ADR-0001 事件总线与编排载体〔推荐组合根直连 signal + Waiting 相位触发〕/ ADR-0002 渲染器与窗口〔推荐 2D 用 gl_compatibility〕/ ADR-0003 场景结构与棋盘渲染 / ADR-0004 测试载体与 CI;应有:ADR-0005 生成性能 / 0006 单位 API face 归属 / 0007 零伤害护栏落点 / 0008 切片机制正式化;可推迟:0009–0013)⑦ 架构七原则(Core headless / 单一事实源 / 封闭 RNG 可回放 / 编排唯一性 / fail-fast 零特判 / 依赖单向组合根 / 呈现层契约成文)⑧ QQ-01–10 开放问题。**同批执行**:review-mode.txt 写入 solo(用户拍板"前期不需参与太多");引擎参考 refresh 批(breaking-changes/deprecated-apis/VERSION/current-best-practices 四文件,风险 HIGH→MEDIUM)。**下一步(按 ADR 优先级)**:`/architecture-decision "event-bus-and-orchestration-carrier"` 等 4 个 Foundation ADR → /architecture-review(新会话)→ /gate-check 前置项(/test-setup /ux-design)。注意:与条目 28 的 C3 GDD 设计会话并行中——ADR-0001/0002 落定结果与 C3 无接口冲突(C3 消费 B1 对账输出为文档层事实依赖)。 → **GDD 完成(2026-09-12 同会话)**:12 节全部写入(用户全权委托——"所有选择按推荐",逐节问询按用户指示跳过;solo 模式专员代理跳过已在各节登记)。关键决策:① 承载结构 = 直填行值主体 + DifficultyTable 修饰结构位(MVP 空表,B2 Rule 10 兑现)② 切片 SLICE-TUNED 行集转正 MVP 默认曲线 ③ 难度期望表(通关率目标带 L1 基线 40–60%/供给平衡带 [1.1,2.0]——F5/F6)④ F1–F7 全部为设计期工具式、运行时零新增公式 ⑤ B1 条款③⑤/预警⑥约束承接:条款⑤ MVP 接受非支配设计态(C 量级不上调,OQ4 playtest 观测弃图率)⑥ A4 scope=global 语义定稿(仅 Boss/精英图,MVP 不产生行)⑦ Boss 血量占比 0.81 越带如实登记 OQ2。registry +3 条目(difficulty_table/difficulty_expectation_table/boss_dmax_convention)· systems-index C3 → Designed。**注意:未经 /design-review——solo 出稿待人工复核,独立评审须新会话 `/design-review design/gdd/c3-difficulty-curve.md`**。 +29. **C3 GDD 落地切片 + 试玩采集清单(2026-09-12)**:用户拍板跳过 /design-review(solo 出稿待复核状态维持),C3 设计承诺直接落回切片试玩。落地:**DifficultyTable 结构位**(schema 域校验+越域 warning / 空表 difficulty.tres / ConfigDB 挂载 + resolve_difficulty_modifiers(精确匹配+clamp)/ B2.spawn 套乘钩子(行值快照前,MVP 空表恒 1.0))——AC-10 断言含注入套乘(×1.5/×2.0)与越域 clamp(50→10)验证;切片调参登记转正标记;**试玩采集清单**(OQ3 真人修正系数/OQ2 Boss 血量/OQ6 刷新使用/OQ4 弃图倾向/台阶可感知性/供给体感——README 专节)。冒烟 **96/96**(+8)。下一步 = 用户 F5 试玩采集 → OQ 裁决 → (可选)新会话 /design-review。 +30. **Foundation 四 ADR 落定(2026-09-12,/architecture-decision ×4 批,solo 模式用户授权代决 + godot-specialist 子代理校验)**:ADR-0001(事件总线=组合根直连 signal + B1 Waiting 相位触发;信号实名 round_end_board/round_end_stopped;specialist 增补静态规则:core↔core 连接方法 Callable only / 发射者禁反向引用——RefCounted 析构即双向断连,真坑是 lambda 捕获 self 成环)· ADR-0002(gl_compatibility——绕开 4.6 Windows D3D12 新默认线;基座 1280×800 + canvas_items/expand 显式落盘)· ADR-0003(autoload 仅 {ConfigDB, RngService};**specialist 实质纠正:切片 BoardView 实为 GridContainer+逐格控件而非自绘 _draw()——ADR 按事实修正,_draw 迁移登记为 D1 候选工作项(三探针改造 + push_input 端到端 + 命中测试自建为隐藏成本);CJK 字体随包 = 运行时默认字体无 CJK 字形,硬性前置**)· ADR-0004(双载体定稿;**specialist 升格:S2 全局 rand* 函数禁令(绕开 RngService 漏洞)/ S5 core 禁裸引用 autoload 全局标识符(--script 模式不注册全局标识符——裸引用 parse error,GH #6360);CI 必配外部 wall-clock 超时;click_probe 口径修正 = "真实翻译出口";gdUnit4 = deferred(D2 OQ1 关闭形态)**)。产出:`docs/architecture/adr-0001~0004-*.md` 四文件(Status: Accepted)+ `docs/registry/architecture.yaml`(新建:interfaces ×3 / api_decisions ×6 / forbidden_patterns ×6)+ architecture.md §Required ADRs 四项标 ✅(骨架解冻)。**架构阶段正式编码前置条件全部满足**。下一步队列:/test-setup → /ux-design → (新会话)/architecture-review → /gate-check;需要用户的仅剩:切片 F5 试玩(条目 29b 的采集清单)+ C3 OQ 裁决 + 迭代期反馈。注:本条目与会话内另一条目 29(C3 批)编号并列,编号冲突系并行会话所致,内容不冲突。 + +31. **/test-setup 完成(2026-09-12,solo 模式用户授权代决)**:`tests/` 脚手架 + `.github/workflows/tests.yml` 已创建——**一处对 skill 默认的有意偏离**:skill 模板默认生成 gdUnit4 runner,按 ADR-0004(gdUnit4 deferred、双载体)改为项目自有协议。产出:tests/README.md(双载体文档 + 命名规范 + 证据矩阵 + 冒烟协议要点:--script 无 autoload/无全局标识符、quit(code)、manifest、reset_diagnostics、表读值推导)· tests/unit/(**example_test.gd = gate-check 要求的示例测试**,协议活样板)· tests/integration/ · tests/evidence/ · tests/smoke/critical-paths.md(**15 条项目真实关键路径**——挖掘/连锁/旗挡链/雷格通行/停手 RAP/结算归因/窗口单件/清图完美排雷/轮内刷新方案 A/hero_attack/关卡链/调整板层级回归/CJK 显示,面向表现手感,与切片 96 条自动化断言分工)· tests/run_all.gd(CI 冒烟统一入口,D2 AC-2b manifest 协议,正式工程落地后登记套件清单)· tests/static_scan.sh(**ADR-0004 S1–S8 可执行子集:S1/S2 全局随机禁令含 randi/randf/randf_range + S4 core 引擎依赖为硬失败,S3 警告级,S5–S8 登记为精化 TODO**;core/ 不存在时优雅跳过——已验证 exit 0)· .github/workflows/tests.yml(static-scan + smoke 双 job,**timeout 外部超时为硬性要求**,godot 4.7.2;仓库未初始化 git,首次 push 自动生效)。**gate-check Technical Setup 前置三项全满足**(tests/ 目录 ✓ workflows ✓ ≥1 示例测试 ✓)。剩余:/ux-design → (新会话)/architecture-review → /gate-check;用户侧 = F5 试玩采集。 + +32. **门检查前置工件补齐(2026-09-12,用户拍板"先补工件再跑门")**:用户 invoke /gate-check 后发现阶段漂移(stage.txt=Systems Design 但实际已在 Technical Setup 深处),选择先补工件。同批完成:① **`docs/architecture/requirements-traceability.md`**(新建——gate 要求的专用矩阵:63 条 TR 分层覆盖表,Foundation 36 条零缺口核验逐条列载体;Core 待 ADR-0006/0007,Presentation 待 D1 GDD/ADR-0011)② **ADR-0005 `save-contract-and-schema-versioning`**(新建 Accepted——SCHEMA_VERSION 比较归属 D3 + RNG 快照切点契约收拢,Foundation 层最后两条 pending TR 闭合)③ architecture.md §Required ADRs 重排(0005 已用 → 应有队列重编号为 0006–0009,可推迟 0010–0013)④ registry architecture.yaml 增 save_contract 条目。**当前缺口清单(Technical Setup → Pre-Production 门视角)**:/ux-design 两件套(interaction-patterns + accessibility-requirements)→ /architecture-review(**必须新会话**)→ /create-control-manifest(其前置 = architecture-review 通过 + ADR Accepted)→ gate-check。Systems Design → Technical Setup 门另需:D1 GDD(MVP 最后一块)+ /review-all-gdds(新会话)。用户侧唯一项 = F5 试玩采集。 + +30. **试玩日志器 + 诊断转储(2026-09-12 用户需求批——playtest 数据采集基建)**:① B1 新增 `settlement_snapshot` 信号(每结算末尾全量状态快照:判定段重构为 verdict 先算→快照→分支——快照含 despawn 前终局;板序号 _board_seq 归因)② 新组件 `presentation/playtest_logger.gd`(JSONL 持久化 user://logs/playtest_*.jsonl:session/settlement/battle_end/node/level_end 五类行 = 完整试玩时间线;环形缓冲 200 结算行/100 操作)③ HUD 诊断按钮(卡死点击 → 全量核心状态 + 环形缓冲 → diag_*.json——行为探针 _dump_state 产品化;GDScript 无运行时堆栈 API,状态等待型软锁正对症)。**日志首秀即抓真 bug**:_start_full_level 面板加成双写叠加(apply 写表 18 → runner 以 18 为基线 ×3 → atk 54)——修复 = 关卡模式单一应用路径(runner 写表),日志 atk 54→18 证实;教训 = 同一加成多应用路径须单一事实源 + JSONL 快照是数值回归最低成本探测器。冒烟 103/103(+7)。 + +34. **战斗屏 UX 规格产出(2026-09-12,/ux-design battle,自主模式用户委托"缺失补充")**:`design/ux/battle.md` 已写入(Status: Complete 待 /ux-review;14 节 + 上游义务对照表)。**最大决策 = 常驻双舞台分屏**(用户布局图拍板):战斗画面(252px 顶区)+ 角色信息(240px 左下)+ 扫雷图(中下 ~760px)+ 道具(280px 右下)+ 顶栏关卡进度条(1280×800 基座)——**对 B5"双舞台陷阱"的裁决输入 = 同屏双舞台第三形态**(非独立战斗场景),B5 前战斗表现美术测试按此布局出样。关键裁决:道具窗口 = 右栏原位激活(非中央弹窗);移动路径预览 = 合法性指示(A2 D1×6⑤ 悬案裁决,穿行动画留痕不采纳);结算明细带常驻战斗画面底部(P2 逐行归因,点击展开回看);状态效果空态 = 槽隐藏。**全部 D1 呈现义务逐条落位**(A2×6/A3/A4×3/B1×3〔UX Flag 结算屏+窗口〕/B2×4/B3×4/B4×5 + A1 EC5——spec §13 对照表供门禁核对),AC 含 entry 17 三条场景级硬性要求(信号载荷 schema 成文/场景级 headless 测试/信号时点可见性)+ 缺陷①④⑤回归断言。**登记缺口(gate-check Pre-Production 视角)**:`design/accessibility-requirements.md` 与 `design/ux/interaction-patterns.md` 两件套仍未建(spec 内建 WCAG AA 基线承诺,正式分层待补);相邻域屏 NodeOverlay/关卡结算建议后续 spec(design/ux/node-reward.md)。**下一步 = /ux-review design/ux/battle.md(新会话)→ D1 GDD(设计顺序第 10 位,MVP 最后一块,以本 spec 为呈现义务承载依据)→ /review-all-gdds → /gate-check**。**同日修订(用户反馈拍板)**:道具来源不呈现——库存列表逐件描述由"来源格/轮"改为**属性简述**(行值快照投影派生:伤害/回复/增益一句话;tooltip=完整描述+触发注记,不含来源);来源数据仍在 B4 窗口载荷契约中(不改数据层),归因职责由结算明细行的逐行来源标注承载(P2 不受影响)。 +31. **试玩批次 1 → v3 数值批(2026-09-12,F7 流程首次完整实践)**:用户 6 项反馈(满血全通关/Boss OK 或可上调/刷新零使用/零弃图/DND 命名指令/道具先保留)→ 裁决:OQ3 方向确认(真人>>模拟)、OQ2 初步裁决(F3 带 [0.35,0.55]→[0.5,1.1])、OQ4 初步关闭(非支配成立)、OQ6 读数(盲猜墙不足非刷新太顺滑——密度杠杆留观测)。**v3 数值批**:atk +50%(7/5/9/6/9)、Boss 150/9、DND 命名(goblin/bugbear/ogre_chief——enemy_id 同步更名)、L2 锚行同步。**调试收获**:D0 hp 20 会被首轮盲挖粉尘(12)+先手(9)秒杀——旗回归断言 FAIL 抓获,回 24("有过程有痛感"原则:掉血压力 ≠ 秒杀);冒烟硬编码行值断言随数值批同步是维护成本(后续改数据驱动)。**v3 扫描**:基线 60% 带内上沿、败北 4/6 集中 Boss、天赋曲线成立。冒烟 103/103。C3 GDD 回写(F3/OQ/试玩批次记录)+ README + 本条。**下一批 = v3 真人试玩(OQ3 定量/OQ2 终裁)→ 视数据定 v4。** +32. **Peglin 对比研究报告(2026-09-12,用户问"Roguelike 体现"触发)**:报告 = `design/research/peglin-comparison-2026-09-12.md`(数据源 peglin.wiki.gg 逐页核实:Peglin 基础 HP 100、Forest 敌 60-65HP/1-5伤、Boss 900、精英 500、每场战后回复、每场遗物选择)。**三根因结论**:① 构筑密度差一个量级(Peglin 每场 1 次遗物选择 vs 我们每关 6 件——"局感"第一根因)② 遭遇零随机+线性无分支 ③ 资源模型不同(Peglin 战后回复+低伤多场 vs 我们无回复+高伤少场——**数值不可直接移植,只可比结构比例**)。**行动建议**:本切片可做 = 遭遇随机化(同敌种 unit_count 区间掷骰,不触 B2 行集锁定);后置 = 多波战斗(B1 波次表)/遗物协同(先设计协同再提密度)/战后回复(绑定敌 atk 下调)。用户决策点 = 是否本切片做遭遇随机化。 +33. **遭遇随机化落地(2026-09-12 用户拍板——Peglin 对比报告结论 2 的本切片项)**:报告 `design/research/peglin-comparison-2026-09-12.md`(三根因:构筑密度/零随机/资源模型;数值不可移植只可比结构比例)。**用户拍板先做遭遇随机化**。C3 GDD 增补 Core Rule 8(unit_count 区间 [unit_count, unit_count_max] 均匀骰 + encounter 流 + B2 零随机纪律保持 + 教学位不加骰)。实现:RngService 第三流 encounter(SALT 0x5DEECE66D2545F491,三流计数器重播种断言同步)/ EnemyTable unit_count_max 字段(可省略,域 [unit_count,3])/ enemy.tres 三行区间(D0 [1,2]、D1 [2,3] hp13、D3 [2,3] hp18;D2/Boss 教学位固定)/ B1.begin_battle 掷骰(erow 预解析 → override 传 spawn)/ B2.spawn override 参数。registry rng_service 条目接口漂移登记。**扫描**:基线 47%(v3 定值 60% → 区间期望总量 +8% 叠加;F5 带内 ✓)——区间骰使难度分布更宽(运气进入遭遇层 = Roguelike 体核心)。冒烟 149/149(+46)。**待办**:批次 2 试玩(v3+区间双变量采集 OQ3 定量);多波战斗/遗物协同/战后回复 = Alpha 评估(报告第三节)。 + +35. **战斗屏 UX 批:评审通过 + pattern 库建立(2026-09-12,/team-ui 管线 solo 模式)**:① **上游契约批落地**——battle.md /ux-review(7 blocking)+ 用户四拍板(弃图刷新=挖掘期当场换图·雷图右上角按钮 / 保留每轮上限 2 / OQ8 本 spec 裁决采纳 tint+雾区全开信号 / 压暗改去饱和+金橙偏移)→ A3/A2/B1/systems-index/registry 直接修订(refresh 前移挖掘期:A3 Active 受理+cap 2、A2 Active·仅刷新路径装板、B1 `refresh_board` 编排+stopped/ap_exhausted 恒 same_board、旧旗作废、时间税失效预警 OQ3 联动)。**注意与切片 entry 27 命名对账**:切片代码用 provisional 名(`request_refresh_active()`/`can_request_refresh()`/`refresh_max_per_round`/`during_round`),GDD 用正式名(`request_refresh` Active 受理/`refresh_board`/`refresh_cap_per_round` 结构位)——语义一致,正式 D1 批统一命名 ② **/ux-review 独立复检**(全新上下文代理):1 blocking(systems-index:90 旧契约残留)+ 3 advisory 全部同批落稿 → **VERDICT: APPROVED**(§13 台账 27 项义务承接逐项实存验证)③ **interaction-patterns.md 已建**(用户拍板"先写核心 8"):双源拒绝反馈/原位窗口激活/逐行兑现明细带/悬停预承诺光标/点阵资源显示/模态 overlay 层级纪律/破坏性确认异位/禁用态即理由全文 + 12 目录占位(实现期回写义务)④ Phase 2 视觉 spec 跳过(用户拍板,art-bible §1–4 直接驱动)。**下一步 = Phase 3 单次协调实现**:ui-programmer 按 battle.md 改造 presentation/(五区布局/敌人卡/明细带/状态槽/图例/tooltip/教学提示/胜负终局)+ ux_* 遥测 + 探针扩展;solo 模式 Phase 4 总监评审流跳过,产出以冒烟+三探针兜底 +36. **战斗屏五区改造落地(2026-09-12,battle.md 落地 ui-programmer 批——entry 35 的 Phase 3)**:core/ 与 data/ 零字节改动,presentation/ 重构为五区(A 28px 进度条 / B 252px 战斗画面〔英雄卡+敌人卡×1–3+轮次指示+明细带改造〕/ C 240px 数据框+状态槽×4 / D 区 AP 点阵条+BoardView 改造+toast+演出层 / E 区库存常读+原位窗口态)+ 全屏 States(去饱和 shader 明度不变/清图演出/Boss 影子/终局 overlay 胜金蜡封·败暮蓝/一次性提示去重)+ ux 遥测行族(round_input_ratio/round_exit/refresh_attempt/window_choice/reject/hint_shown——快照驱动,呈现层只订 B1)。模态统一登记序管理(缺陷⑤纪律),旧 hud_view.gd 删除。**批内发现并修复**:TunePanel 默认 visible=true 靠开始层遮蔽的历史 quirk(缺陷⑤家族,显式初始化);敌卡逐行重建踩掉死亡灰化(改就地更新);库存清空反馈被常读刷新覆盖(持有期);窗口态被 presenter 全量刷新 stomped(窗口期跳过常读重建——click_probe 首跑即抓)。Waiting 刷新代理按 OQ12 退役(UI 仅 Active 受理)。探针:ui_probe 增五区断言+L2 雾段;click_probe 增刷新确认链/图例/tooltip/模态遮挡/去饱和/首格计时(133–151ms ≤500ms)。回归:smoke 149/149 + 三探针 PASS(click_probe 六种子)。占位登记:意图映射/演出/字体(D1 正式化)归 B5/D1 批。 +37. **伤害类型 × 护甲克制批落地(2026-09-12 用户拍板——设计会话设定表定稿"没有问题"后落程序)**:**结构**——敌人护甲类型 {无甲,护甲A,护甲B}(EnemyTable.armor_type,映射 goblin=none/bugbear=a/ogre_chief=b 教学梯度候选)× 主角攻击三段构成(①基础 atk×(1+attack_up) 不吃克制 ②伤害A=本轮资格格充能×克制 ③伤害B=首次标中雷充能×克制,与 weaken 同键每雷实例一次去重;三分量各自 floor 相加,hero_attack 行携带 base/dmg_a/dmg_b)+ 克制矩阵 SettlementTable.armor_effectiveness(特化 150/无甲 100/对位 50;刀/AOE/清图恒定表值不吃克制)+ 伤害A 单轮 cap 8 + **标错旗随机惩罚三选一**(enrage 暴走敌+25% / ambush+2 层 / off_guard 失衡我方+25%,dur=1,零新流确定性派生 (x×7+y×13+板×5)%3 同种子可回放,每雷格实例一次去重沿用)。**数值全部 provisional**(damage_a_per_cell=1/cap=8/damage_b_per_flag=2/矩阵/enrage·off_guard 25%/映射——登记于 .tres 注释,C3 按 F6 复算)。**验证**:冒烟 161/161(+12 专项断言:护甲三档传播/矩阵表值/三分量字段/flag_miss+反噬行/充能进攻击/派生回放)· ui_probe/tune_probe/click_probe 全 PASS。**实现坑**:enemy_card_view set_intent 引用越作用域变量 d → 类编译失败敌人卡零构建(ui_probe 抓获)——改成员变量 _armor 传递。**正式侧义务(GDD/registry 修订待文档批)**:B2(armor_type)/B1(三分量构成+反噬编排+矩阵载体)/B3(六行状态表+flag_miss 授予)/A2 OQ1(惩罚候选升级三选一+重复性=每雷实例一次)/registry(enemy/settlement/status_effect 条目+A3 条款②③锚重推导)/C3(provisional 数值接管)。**世界观记录**:design/research/lore-nest-metaphor-2026-09-12.md(雷=巢穴/旗=封印、伤害A=地利/伤害B=暴露、反噬=封印失败——6 个开放问题待新会话拍板后写回 game-concept) + +38. **呈现层 code-review 修复批(2026-09-12,/code-review CHANGES REQUIRED → 用户拍板全项修复)**:全切片 53 脚本评审(含两 specialist 并行审查 views/ 与 smoke/+schemas/)裁决 7 项必改,本批清偿:① **TunePanel 封装破口**(视图直写 hero 表 + 视图 load smoke 驱动 core → 面板模拟 pct² 口径 + 模拟后表值残留污染真实对局)——重构纯视图信号外发,加成落表收口组合根 `_apply_hero_override` 单一路径(每次从基线幂等重算),模拟编排拆 `tune_sim_controller.gd` ② **意图徽标双翻译**(presenter 译中文投影 vs EnemyCardView 英文 key 匹配 → 色相/重击标记永久失效)——presenter 直传 B2 原键,渲染映射唯一定义处归 EnemyCardView ③ **C 区 RAP 死分支**(presenter 从不投影 a3.rap,A3 D1 清零反馈不可达)——持续投影 + 视图改 `_last_rap` 成员存储 ④ **smoke_all 恒真断言**改带标签真实断言 ⑤ **level_runner core↔core 接线 lambda ×4 → 方法 Callable**(ADR-0001 S6)⑥ **B1 `diagnostic_snapshot()` 诊断观测面**(main/click_probe 零私有字段直读)⑦ **main.gd 761→667**:节点构建/模态层级/叙事文案/模拟编排四拆分(ui_assembler + modal_stack + battle_narrator + tune_sim_controller,均无 class_name 走 preload;.uid 已生成)——剩余为 ADR 规定组合根职责,进一步压缩登记 D1 批候审。探针同步:ui_tune_probe 重写(真实按钮路径接缝 + 两条双写叠加回归断言)、click_probe 改诊断 API。**验证**:smoke 141/141 · 三探针全 PASS(click 六种子)。登记待办:自动玩家四份复制收口 / schema 溯源行 / S1–S8 扫描脚本化 + S1 豁免口径 / views 转发信号契约头注。 + + + +39. **封装收口批落地(2026-09-13,architecture-review 改编批 → 用户拍板 P1+P2+P3 全修)**:代码架构审查报告 `docs/architecture/code-architecture-review-2026-09-13.md`(Verdict: CONCERNS——分层纪律 S1–S7 全合规,封装欠账 = 核心状态公有 + 呈现层链式穿透)。本批清偿:**E1** B1 协作对象 a1/b2/b3/b4 私有化 + `presentation_snapshot()/is_battle_live()/stock_count()/query_reward_identity()` 成文呈现面(a2/a3 保留公有 = API Boundaries 登记的 D1 契约面);**E2** A2 board/revealed/flags 私有化 + `current_board()/reveal_record()`(board_view 直读 `.revealed` 的自相矛盾收口);**E3** B2 hero/enemies 私有化 + `hero_unit()/all_enemies()` 投影(presenter 死亡残位需求收进 API);**E5** A3/RngService 只读字段 getter-only 机制化(phase/ap/rap/last_round_end/master_seed);**O1+O2** 调试模式拆 `debug_session.gd`(调参语义单一实现;battle_created 先于 begin_battle = ADR-0001 接线归组合根 + 缺陷①时序——ui_probe 抓获初版 battle_ready 才接手 b1 的雾提示丢失);**O3** `core/damage_math.gd` 纯算术抽取 + board_generator 重复求解合并;**P3** 头注溯源 ×4 / B4 返回值统一 null / equivalence_supply 表行 duplicate / load→preload ×3。**附带根治基线既有重入隐患**:main `_on_board_action` 清图判定在 settle_batch 同步重入(关卡终局置空 b1)后摸 `b1.a2` 的 SCRIPT ERROR——click_probe stderr 归零(优于基线)。**验证**:冒烟 141/141 + 三探针 PASS(click done=true 失效=0 问题=0)。**P2 余项设计稿待用户审**:E4 表值参数化(B2.spawn 契约漂移须 GDD/registry 修订)+ O4 自动玩家四份复制收口(合并 W-3)。 + +40. **P2 收口批落地(2026-09-13,用户拍板两项均做——封装审查 P2 设计稿批准)**:① **E4 表值参数化**:db.hero 表写通道全删(main `_apply_hero_override`/runner `start_level` 写表),加成经 encounter 显式参数(B1 begin_battle +hero_atk/hero_max_hp 字段 → B2.spawn +atk_override/max_hp_override 参数,-1 表值兜底)——双写叠加 6→18→54 与双基线同步整类隐患移除,D2 单一事实源机制化;registry 新增 `hero_override_parameter_passing`(ADR-0009 候选,B2.spawn/B1 encounter 接口漂移登记,正式侧 GDD 修订随 ADR-0009 批);ui_tune_probe ⑦⑧ 断言语义升级(表运行期不可变 + 完整关卡路径单次加成快照断言)。② **O4 自动玩家单一实现**(合并 W-3):`smoke/auto_player.gd`(choose_dig + window_selection,差异参数化 heal_hp_div/round_gate/knife_compare),四消费方(smoke_all 3/3/best · monte_carlo 2/0/sum · dump_battle 3/3/sum · level_autoplay 2/3/best)+ click_probe 引用同步;同种子回放断言全绿 = 等价性证明。**验证**:冒烟 141/141 · 三探针 PASS。**遗留**:正式侧 B1/B2 GDD 修订(随 ADR-0009);main.gd 400 行目标受 ADR-0001 组合根职责约束(D1 正式化批候审)。 + +41. **art-bible §1–4 定稿(2026-09-13,/art-bible retrofit 批,solo 模式)**:`design/art/art-bible.md`(2026-09-10 初稿,§1–4 已有核心方向)本批按极性提案与 2026-09-12 隐喻换轴**定向增补定稿 §1–4**——① 绘制优先级栈插徽章位(数字 > 小人 > 旗 > 徽章 > 纹理 > 底色,§1.2/§3.4 同步);② 雷格隐喻换轴同步(雷 = 被惊动的巢穴,"机关"旧称退役;兑现档红更名危险红、色值不变);③ §1.4 格子样张测试验收标准成文(第一验收项 = 数字可读性硬指标 ≥4.5:1 可测判据 + 回退规则"改氛围色不改数字";雾格排除在外);④ §4.6 图标键契约冻结(效果图标 B3 数据六行 + sapped 提案键 / 奖励徽章 B4 默认根八行 + clear_blast 并列预留 / 格面类型图标五类〔提案 R1,未开格预读徽章墨族单色防色相泄漏〕/ 敌种线索键 B2 Rule 8 契约化〔行为拍姿态/环长直觉/强度档位/预告时序/恒定义务〕——键与通道分配冻结,终稿美术归样张与 asset-spec 批);⑤ 雾格资产规格一行占位(内容封禁期暂缓,雾重设计批再启)。registry terminology 增三词条(图标键/徽章/极性)+ referenced_by 增 art-bible;systems-index 新增 2026-09-13 art-bible 登记块(范围声明:§5–9 未建不阻塞 D1,D1 消费面五节点名)。AD 门禁 solo 跳过,出稿待人工复核。**冲突清单(不改 GDD/提案,列示待裁决)**:① game-concept 视觉锚原则 1"暗色地牢底色" vs art-bible 明亮底色反转(art-bible §1.3 2026-09-10 用户定稿在案;概念侧注记待概念层修订批);② B2 GDD 敌种键 {grub, ambusher} vs 切片 DND 更名 {goblin, bugbear, ogre_chief}(2026-09-12 consistency-check ⚠️ 在案;图标键契约以 GDD 键集为锚、声明键集事实源归 B2/C3);③ B4 GDD 默认根八行(clear_knife)vs 切片供给控制器批(clear_blast + 三可选类移 active_inject——entry 21 正式侧义务待清偿;徽章键按 reward_id 冻结、池归属无关);④ 极性提案 §3.5 跨文档回写清单全部待实施批(B3 四行→七行、B4 schema 扩列、A2 OQ1 关闭等)。**下一步 = D1 design-review(D1 GDD 已于 2026-09-13 solo 出稿并完成 lean 迁移,状态 Designed 未经评审——新会话跑 `/design-review design/gdd/d1-hud-framework.md`,PASS 后登记 Approved 即 MVP 十系统全收口)**。 + +42. **GDD lean 格式迁移完成(2026-09-13,11 份全量,子 agent 串行/双并发)**:全部系统级 GDD 按新模板(`.claude/docs/templates/game-design-document.md`)重排,`tools/gdd_lint.py` 11/11 PASS(表格占比 81–89%);修订注记出正文→版本历史表;原稿备份 `design/gdd/legacy-2026-09-13/`。**随迁按 registry 回执写现行的语义统一**:b3(weaken −65、D=1)、d2(八字段/p_perfect {100,200}/category 五值/ActionEconomyTable 现行值)、b1(EC-9 单件制)。**🔴 遗留重算义务**:B1 F1/F4/F5 派生数值(V_safe=D_ref 恒等式、E_life 12.0、E_dig 7.2、裕度 2.0×/2.6×)仍按 defense_up duration=2 旧基线——registry 已 D=1(2026-09-11 批),锚腿读数须 B1/C3 对账重跑(非格式批可改)。迁移代理另有 6 处保守处理待人工复核(b4 AC-19 对账对、c3 Rule 编号引用等,详见各文件版本历史)。 +41. **零承伤根因分割探针 + playtest-09-12 Bug #1–#3 处置 + Q1 复扫基线(2026-09-13,playtest-2026-09-12 报告 Top3 授权任务)**:① **根因分割探针 `smoke/damage_probe.gd`**(boss/level 两模式——逐结算行打印 beat / atk.total / hero_defense 池 / d_tick)。快照盲区发现:settlement_snapshot 取在衰减段之后,时效 1 的 defense_up 当轮授予当轮衰减——真人局 JSONL buffs[] 恒空 ≠ 池为空(报告悬置机理);探针从结算行 pool_value 重建战斗段时点池值(effect_pool_map 表驱动)。**结论:零承伤根因 = 旧数据 defense_up -50×2 = clamp -100 触底 ×0(设计语义,H1 成立)**——fresh 池裸验 Boss 攻击链全额兑现(attack 9/9×2 · heavy 18/18 · charge 0 设计,6 轮击倒英雄)否证攻击链缺陷(H2);当前数据(-30 批)墙最深 -60 > clamp_lo -100 归零不可达(重证),墙轮残余 ×0.40。② **BUG-0002/0003/0004 建档**(production/qa/bugs/,自 BUG-0001 顺延):报告 #1 空遭遇 = **首轮全灭被快照盲区误读**(seed 42 回放 d0 spawn=1/d1 spawn=3 同签名;遭遇骰下限 ≥1 双闸 + 100 场扫描零越域;settlement_snapshot 增 spawn_count 字段机制化判别)→ Closed(Rejected as coded);报告 #2 Boss 攻击零兑现 = 设计语义非 bug → Closed(Rejected as coded);报告 #3 grant_node_active 绕过 _supply.notify_granted = 代码实锤 → **Fixed**(stock 受理后补入账,战斗域清零语义不变 = 零战斗数值影响,纯口径)。回归断言 +7(spawn_count 字段/等值 · 遭遇全深度×12 种子全域 60 场 · _smoke_attack_chain_fresh_pool 攻击链钉死 · _smoke_supply ⑦ 节点奖励入账)。③ **Q1 复扫基线**(level_monte_carlo 增 F4 两列「普通承伤/场% · Boss承伤%」——round 回卷分段 + 末段 Boss 判定;N=30×10 档、种子 4200–4229):基线通关率 **17%**(F5 带 40–60%)· +100% 档 43%(中天赋 ≥95%)· +200% 档 63%(高天赋 100%);普通场承伤 **35.1–43.1%/场**(带 ≤20%)· Boss 场 **71.3–134.9%**(带 ≤50%)(hp+0 档)——**F4/F5 全档位越带**,拆墙 + v3 atk 叠加后难度由严重不足摆向严重过剩。**本批零数值变更**(敌 hp 与奖励供给禁独立调参——registry 预警⑥联动;杠杆方向列待拍板,基线 = F7 测量输入)。**验证**:冒烟 **148/148**(141+7 新回归)· 三探针 PASS · damage_probe boss 7/7 + level 种子 42/43/44 全 PASS。**待拍板/待设计批**:①难度杠杆重拍板(F4/F5 越带——候选 atk 回撤/防御权重回升/供给联动,归 C3 F7);②墙语义候选改法(clamp 收紧/乘法衰减/重击拍穿防——/propagate-design-change + B3 GDD 文字同步);③供给量语义重拍板(每战斗 vs 关卡域首件保证——C3);④浅深度首轮跳战可玩性(C3)。切片 README 同批登记。 + +42. **D1 已收口(Designed,2026-09-13,/design-system solo 模式一次会话定稿)**:GDD 全节已写入(`design/gdd/d1-hud-framework.md`,12 节 + 台账表/冲突清单零占位,25 条 AC;**专员/总监代理全部跳过,出稿待人工复核;未经 /design-review,独立评审须新会话**)。定稿要点:① **呈现义务台账 27 项逐项落位**(A2×6/B2×4/B3×4/B4×5/B1×3/A4×3/A3+A1 EC5——Dependencies 落位表;battle.md §13 承接升格系统级验收,已裁决项全部直接继承)② **切片教训三条硬性要求兑现**(entry 17:载荷 schema 13 项成文表 Core Rule 17 / 场景级 headless 探针 AC-19 / 信号时点可见性规则 Core Rule 18 + AC-20)③ 订阅纪律按 ADR-0001 收窄——呈现层只订 B1 步骤流/快照与 runner,零事件外发面 = **命令式回执驱动**五面成文(registry `d1_presentation_surface` 登记;battle.md §Events Fired 措辞差列冲突⑥)④ **命名统一核销**(entry 35:request_refresh/refresh_cap_per_round 正式名,AC-25 grep 断言零残留)⑤ 程序化占位策略(art-bible §1.4 引用依据、样张未制作不阻塞、占位同受数字可读性硬指标)⑥ _draw 维持 GridContainer 裁决(ADR-0003 Decision 3 兑现——复议留样张后 OQ1)⑦ D1 裁决:MVP 格面徽章只在已开格呈现(B4 Rule 8 承接——冲突⑤)⑧ 发现冲突清单 ⑧ 项成文(不改已定稿正文与提案,含 game-concept 底色/B2 敌种键/clear_blast/极性提案批/订阅表述/A3 刷新计数读数/B1 settlement_snapshot 契约〔entry 41 并行批仍在切片使用该信号——OQ3 登记更增补登记必要性〕)。registry +1 条目 + terminology 两词条;systems-index D1 进度行 + 门禁增补记录块(接口漂移登记 ×3)。**下一步 =(新会话)`/design-review design/gdd/d1-hud-framework.md` → `/review-all-gdds` → `/gate-check`**(Systems Design → Technical Setup 门两件 = D1 GDD〔本条〕+ /review-all-gdds;**MVP GDD 设计顺序前 10 位全部收口**)。 +42. **四项拍板落地(2026-09-13,用户四问裁决——本批零玩法数值变更)**:①**难度主杠杆 = 先真人试玩再拍**(F5 真人修正系数标定优先于数值调整;模拟基线 17% vs 带 40–60% 的越带方向真人预期只会更确认,杠杆方案保持待拍板:atk 回撤/组合/专项三案备选);②**墙语义维持现状 + 归零护栏**:smoke_all `_smoke_wall_zero_guard` 表级不变式(2×defense_up.magnitude > pool_clamp_lo——BUG-0003 事故类永久免疫,未来调参造出 ×0 墙即红灯强制重审);③**供给量先观测再拍**(BUG-0004 封口后账目完整,随复测批/真人局取数);④**跳战并入难度复测再议**:level_monte_carlo 增「跳战率」列(普通战斗 round-1 即胜占比),基线实测 **8%**(+50% 13% / +100% 24% / +150% 27% / +200% 38%)——**seed 42 为高跳战轨迹**(autoplay d0–d3 全部 round-1 全灭),真人局跳战体验含种子成分,基线下跳战是少数派,④复议数据前提成立。附带:defense_scan 过时期望标签清理(「墙轮承伤应为 0」→「合法 ×0.40 帽」)。**验证**:冒烟 **149/149**(+1 护栏)· 复扫各列与 Q1 基线逐值一致(collect_rows 开启不改变行为——确定性交叉验证)。**下一步(用户侧)**:真人试玩一局(观测清单:通关/败北节点 = F5 修正系数分子;普通场掉血体感;Boss 战轮数;d0–d3 跳战与否;治疗包缺血时刻与库存残留 = 拍板③数据)→ 会话分析 JSONL 出修正系数与①③④复议数据。 + +40. **A5 扫雷辅助道具 GDD 定稿(2026-09-14,/design-system solo 模式一次会话——用户设计指令"每个维度都要有对应道具,稀有道具尝试高数值和多维度结合")**:`design/gdd/a5-minesweeper-aux-items.md`(lean 格式,lint PASS 表格占比 84%)· registry 登记 `a5_aux_items` 条目 + terminology 四词条(叠加层/真视窄域护栏/渗透率)+ `variation_table.counter_item_ids` 回填 [probe_dust](A4 预授权 A5-1 义务闭环)· systems-index 门禁增补块 + A5 状态 Designed。**设计要点**:五维度框架 {info, risk, action, plan, counter} + F2 覆盖谓词("每维度都有对应道具"= 加载后内容审计断言,机器可查)· 承重护栏 = 真视窄域(替代推理 ≤3×3、恢复推理可全图——P1 防塌方,schema 校验强制)· MVP 六行 = 四常规单维(显影尘/工兵令/探宝罗盘/探巢摆钟,t2)+ 两稀有多维高数值(地穴全图卷 = 全雾显影×全图预读、爆破工兵包 = +4 AP×3×3 真视,t3、≈0.17 件/板)· 获取挂 B4(aux_item 预留枚举激活——义务 B4×①…⑤)· 使用经 B1 挖掘期编排(use_aux 规范序 validate→apply/grant→consume 序即原子——义务 B1×①②③)· 板级叠加层三集(零特判延伸——A2 代码零 A5 分支、零回写 A2/B4、随图消亡)· A3 grant_ap 第 9 API 接口漂移预授权(义务 A3×①)· 21 项跨 GDD 义务清单 + F1–F3 + 14 AC;A4 EC6/OQ2 与 B4 OQ2 收口。**生效前提**:B4/D2/A3 修订批兑现前 aux 行入池会加载致命(B4 Rule 1 预留值拒绝)——AC-12/13 deferral 已登记。**待办**:/design-review(新会话)→ 对侧修订批(B4/D2/A3/B1/D1)→ UX Flag(/ux-design Phase 4 前置);设计顺序下一站 = C3 复核批/C2。 + +## Session Extract — /review-all-gdds 2026-09-14 +- **Verdict: FAIL**——2 blocking(① B3/B1:defense_up 减伤墙批 −30 未回写 owner、B1 锚算术三重漂移致条款③⑤闭合回执失效;② D2 漂移批五处:encounter 流/pool_tag active_inject/active_p0·decay/k_dmg 禁区/clear_blast 行集+HelperItemTable 更名)+ ⚠️×16 挂各修订批;B3/B1/D2 已标 Needs Revision。报告:`design/gdd/gdd-cross-review-2026-09-14.md` +- **同日 A5**:/design-review --solo 3 blocking + 5 recommended 核销,用户拍板两项边缘规则(area_3x3 边缘窗口裁剪 / 零信息目标不可用不消耗)后免复审标 **Approved**;日志 `design/gdd/reviews/a5-minesweeper-aux-items-review-log.md` +- **下一步**:新会话按报告 §七 批调度跑回写批(D2/B3 → B1 重算 → B4 规格 → C3 收口 → 零散)→ `/review-all-gdds since-last-review` 增量复核 → PASS 再 `/create-architecture`;⚠️ 评审期间检测到并行会话修改 GDD(C3 回写批半途落地致两代理结论互斥)——批收口前勿多会话并行改设计文档 + +43. **C2 冒险结构 GDD 定稿(2026-09-14,/design-system solo 模式一次会话定稿,与 /review-all-gdds 并行——C2 为新增文件零冲突)**:`design/gdd/c2-adventure-structure.md`(lean 格式,lint PASS 表格占比 86%)· registry 新增 `level_table` / `adventure_table` 两条件目 + 补登 `hero_override_parameter_passing`(切片 E4 批实现但 registry 缺登——缺口闭合,owner=b1,ADR-0009 候选状态注记)+ terminology 三词条(HP 账本/关卡收束点/关卡大奖励)· systems-index 门禁增补块 + C2 状态 Designed。**C2 待决四问收口**:① 关卡序列 = MVP N=2 线性(L1 → L2 雾解锁台阶),冒险通关 = L2 Boss;加关 = 内容批,N=2 生效前提 = C3 L2 深度轴行值填充批(未落地前 N=1 合法态)② 大奖励 = 上限 +10 / 血回满 / 代币 +10 三件固定(不进库存域、末关不兑付;血回满与 C3 满血开局校准口径闭合——F1 冒险通关率合成 [6,20]% 的前提)③ 失败重开 = 死亡即冒险终、重开全新种子、放弃同失败型(MVP 无 UI 入口)④ C1/C2 边界 = 收束点在 C1(先调 B1 level_clear 再宣布 level_end),接口契约四件套(start_level / report_battle_end / heal_clamped / level_end)为 C1 GDD 强制输入。**切片先例切分转正**:level_runner 冒险域归 C2、关内行进归 C1;两处切片简化改写(逐关播种 → 冒险单种子;每关 HP 回满 → 跨关延续)。**下游义务**:C3×1(L2 行值——生效前提)· D2×2(reseed 入口 / AdventureTable 建表)· B1×1(begin_battle 入参供给方转正)· C4×2(远)。**下一步 =(新会话)/design-review design/gdd/c2-adventure-structure.md → C1 关卡节点地图(设计顺序第 14 位,以 C2 接口契约四件套为强制输入)**;solo 出稿待人工复核注记已入 GDD 头部。 + +44. **完美策略档测量批(2026-09-14,用户拍板"增加完美策略档测试验证"——OQ7 技能轴上界仪,零玩法数值变更)**:auto_player 增 single+subset 完美求解器(零盲挖/全标旗/卡死刷新,只读 A2 查询面、去 safe_set oracle)+ level_autoplay `policy` 参数 + level_monte_carlo 策略轴(CLI 第 3 参)与踩雷/刷新观测列(零踩雷断言 quit(1));core 零改动,冒烟 **149/149**。**读数(N=30,种子 4200–4229)**:0 加成完美档通关 **100%**、剩 HP 均 **47.4/50**、踩雷 0.00(弱策略基线 60%);+50%~+200% 恒 100% 满血——F5 带与 S/K 区间对技能轴零分辨力,calibration_gap 获机制解释与定量上界。**待拍板**:完美档目标带口径 + 杠杆(r_boss 走廊/群战/decay 0.6 + 新候选"上限掺运气"系:供给量值随机化/承伤硬地板);详情 = 切片 README「2026-09-14 完美策略档测量批」。 + +45. **伤害量值区间批(2026-09-14,用户拍板②——验收口径改完美策略档 0 加成 ≤40%;两步全落地 + 无效性实测)**:第一步奖励侧五行 power 区间化(heal_minor[2,6]/knife_t1[5,11]/heal_pack[6,18]/knife_adv[10,22]/clear_blast[25,55]——身份批追加取号,E14 快照口径不变,B1/呈现零改动)+ 第二步敌方 atk 区间(六行 ±3,遭遇骰同流 spawn 期整战持久——每拍抖动否决〔数字可读性锚〕,B2 零随机保持,spawn +1 参数接口漂移登记);中点恒等现值 = A3 条款①锚不漂移;schema 双批校验(成对/中点锚/buff 禁止)+ smoke `_smoke_power_interval` 4 断言;冒烟 **154/154**。**实测(N=30)**:完美档 0 加成仍 **100%**(承伤 3.5→3.9——方差真实流动被 47/50 血缓冲全吞);基线档 17% 不变——**数值区间杠杆族对 ≤40% 线无效已坐实**(完美玩家结构性优势 = 零踩雷直伤 + weaken 顶格 + 快杀)。**待用户三选裁决(C3 OQ7 主项)**:①承伤硬地板(B1/B2)②weaken 饱和语义(B3/B1——拆全标旗基石)③极端宽度(建议否决——毁基线与可读性);区间机制保留(普通玩家方差体验有效)。registry reward_table/enemy 条目 + clause5 义务 + C3 行值总表/F5/OQ7 同批登记。 diff --git a/production/stage.txt b/production/stage.txt new file mode 100644 index 00000000..7218f9ad --- /dev/null +++ b/production/stage.txt @@ -0,0 +1 @@ +Systems Design \ No newline at end of file diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/.gdignore b/prototypes/dungeon-sweeper-vertical-slice/.godot/.gdignore new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/.gdignore @@ -0,0 +1 @@ + diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/-editstate-d41d8cd98f00b204e9800998ecf8427e.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/-editstate-d41d8cd98f00b204e9800998ecf8427e.cfg new file mode 100644 index 00000000..49ff551d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/-editstate-d41d8cd98f00b204e9800998ecf8427e.cfg @@ -0,0 +1,201 @@ +[editor_states] + +$selected_nodes=Array[NodePath]([NodePath(".")]) +2D={ +"grid_offset": Vector2(0, 0), +"grid_snap_active": false, +"grid_step": Vector2(8, 8), +"grid_visibility": 1, +"ofs": Vector2(-166, -111), +"primary_grid_step": Vector2i(8, 8), +"show_group_gizmos": true, +"show_guides": true, +"show_helpers": false, +"show_lock_gizmos": true, +"show_origin": true, +"show_position_gizmos": true, +"show_rulers": true, +"show_transformation_gizmos": true, +"show_viewport": true, +"show_zoom_control": true, +"smart_snap_active": false, +"snap_guides": true, +"snap_node_anchors": true, +"snap_node_center": true, +"snap_node_parent": true, +"snap_node_sides": true, +"snap_other_nodes": true, +"snap_pixel": true, +"snap_relative": false, +"snap_rotation": false, +"snap_rotation_offset": 0.0, +"snap_rotation_step": 0.2617994, +"snap_scale": false, +"snap_scale_step": 0.1, +"zoom": 1.0 +} +3D={ +"fov": 70.01, +"gizmos_status": { +"AudioListener3D": 0, +"AudioStreamPlayer3D": 0, +"CPUParticles3D": 0, +"CSGShape3D": 0, +"Camera3D": 0, +"ChainIK3D": 0, +"CollisionObject3D": 0, +"CollisionPolygon3D": 0, +"CollisionShape3D": 0, +"Decal": 0, +"FogVolume": 0, +"GPUParticles3D": 0, +"GPUParticlesCollision3D": 0, +"Joint3D": 0, +"Light3D": 0, +"LightmapGI": 0, +"LightmapProbe": 0, +"Marker3D": 0, +"MeshInstance3DCustomAABB": 0, +"NavigationLink3D": 0, +"NavigationObstacle3D": 0, +"NavigationRegion3D": 0, +"OccluderInstance3D": 0, +"Particles3DEmissionShape": 0, +"Path3D": 0, +"PhysicalBone3D": 0, +"RayCast3D": 0, +"ReflectionProbe": 0, +"ShapeCast3D": 0, +"Skeleton3D": 0, +"SoftBody3D": 0, +"SpringArm3D": 0, +"SpringBoneCollision3D": 0, +"SpringBoneSimulator3D": 0, +"TwoBoneIK3D": 0, +"VehicleWheel3D": 0, +"VisibleOnScreenNotifier3D": 0, +"VoxelGI": 0 +}, +"local_coords": false, +"preserve_children_transform": false, +"preview_sun_env": { +"environ_ao_enabled": false, +"environ_enabled": true, +"environ_energy": 1.0, +"environ_gi_enabled": false, +"environ_glow_enabled": false, +"environ_ground_color": Color(0.2, 0.169, 0.133, 1), +"environ_sky_color": Color(0.385, 0.454, 0.55, 1), +"environ_tonemap_enabled": false, +"sun_color": Color(1, 1, 1, 1), +"sun_enabled": true, +"sun_energy": 1.0, +"sun_rotation": Vector2(-1.0471976, 2.6179938), +"sun_shadow_max_distance": 100.0 +}, +"rotate_snap": 15.0, +"scale_snap": 10.0, +"show_grid": true, +"show_origin": true, +"snap_enabled": false, +"trackball_enabled": false, +"translate_snap": 1.0, +"vertex_snap_origin_mode": false, +"vertex_snap_use_collision": false, +"viewport_mode": 1, +"viewport_splits": { +"first": 0.0, +"main": 0.0, +"second": 0.0 +}, +"viewports": [{ +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": true, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}, { +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": false, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}, { +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": false, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}, { +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": false, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}], +"zfar": 4000.01, +"znear": 0.05 +} +Anim={ +"visible": false +} diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_layout.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_layout.cfg new file mode 100644 index 00000000..c15bc208 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_layout.cfg @@ -0,0 +1,80 @@ +[docks] + +dock_3="Scene,Import" +dock_3_selected_tab_idx=0 +dock_4="FileSystem,History" +dock_4_selected_tab_idx=0 +dock_5="Inspector,Signals,Groups" +dock_5_selected_tab_idx=0 +dock_9="Output,Debugger,Audio,Animation,Shader Editor,Search Results,AnimationTree,MeshLibrary,ResourcePreloader,ShaderFile,SpriteFrames,Theme,Polygon,TileSet,TileMap,Replication,GridMap" +dock_floating={} +dock_closed=[] +dock_split_2=0 +dock_split_3=0 +dock_hsplit_1=0 +dock_hsplit_2=280 +dock_hsplit_3=-220 +dock_hsplit_4=0 +dock_main_split=0 +dock_bottom_split=0 +dock_9_selected_tab_idx=0 + +[docks/FileSystem] + +h_split_offset=240 +v_split_offset=0 +display_mode=0 +file_sort=0 +file_list_display_mode=1 +selected_paths=PackedStringArray("res://data/tables/enemy.tres") +uncollapsed_paths=PackedStringArray("res://", "res://presentation/", "res://presentation/views/", "res://data/", "res://data/tables/", "res://data/schemas/", "res://core/") + +[docks/History] + +include_scene=true +include_global=true + +[docks/Theme] + +split_offset=520 + +[EditorNode] + +open_scenes=PackedStringArray("res://presentation/main.tscn") +current_scene="res://presentation/main.tscn" +bottom_panel_offsets={ +"Audio": -450.0, +"Debugger": -369.0, +"Output": -270.0, +"Search Results": 0.0 +} +selected_default_debugger_tab_idx=1 +selected_main_editor_idx=2 + +[EditorWindow] + +screen=0 +mode="windowed" +position=Vector2i(405, 80) +size=Vector2i(1897, 1048) + +[ScriptEditor] + +open_scripts=["res://core/board_generator.gd", "res://core/reward_economy.gd", "res://core/reward_supply_controller.gd", "res://data/schemas/reward_table.gd", "res://presentation/views/status_slot_row.gd"] +selected_script="res://core/board_generator.gd" +open_help=[] +script_split_offset=200 +list_split_offset=0 +zoom_factor=1.0 + +[GameView] + +floating_window_rect=Rect2i(665, 277, 1292, 849) +floating_window_screen=0 + +[ShaderEditor] + +open_shaders=[] +split_offset=200 +selected_shader="" +text_shader_zoom_factor=1.0 diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_script_doc_cache.res b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_script_doc_cache.res new file mode 100644 index 00000000..44f0af13 Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_script_doc_cache.res differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_script_doc_cache.res-folding-a7de57390d6e786984ca20c70a831128.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_script_doc_cache.res-folding-a7de57390d6e786984ca20c70a831128.cfg new file mode 100644 index 00000000..e860dd49 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/editor_script_doc_cache.res-folding-a7de57390d6e786984ca20c70a831128.cfg @@ -0,0 +1,3 @@ +[folding] + +sections_unfolded=PackedStringArray() diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/filesystem_cache10 b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/filesystem_cache10 new file mode 100644 index 00000000..c2dfefac --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/filesystem_cache10 @@ -0,0 +1,93 @@ +78098b89c88a74ebe8c20117b8754620 +::res://::1789291459 +README.md::TextFile::-1::1789245234::0::1::::<><><>0<>0<><>:: +::res://core/::1789236746 +action_economy.gd::GDScript::539248926336522464::1789233852::0::1::::ActionEconomy<>RefCounted<><>0<>0<><>:: +battle_orchestrator.gd::GDScript::6153665499356077672::1789236632::0::1::::BattleOrchestrator<>RefCounted<><>0<>0<><>:: +board_data.gd::GDScript::7569800284505832826::1789232949::0::1::::BoardData<>RefCounted<><>0<>0<><>:: +board_generator.gd::GDScript::3603363749200663606::1789232934::0::1::::BoardGenerator<>RefCounted<><>0<>0<><>:: +config_db.gd::GDScript::5132721691389947172::1789232958::0::1::::<>Node<><>0<>0<><>:: +damage_math.gd::GDScript::190709078776196050::1789232474::0::1::::DamageMath<>RefCounted<><>0<>0<><>:: +level_runner.gd::GDScript::4382564976711397872::1789236746::0::1::::LevelRunner<>RefCounted<><>0<>0<><>:: +minesweeper_rules.gd::GDScript::7936287674986956201::1789233939::0::1::::MinesweeperRules<>RefCounted<><>0<>0<><>:: +reward_economy.gd::GDScript::2149614533123690078::1789232919::0::1::::RewardEconomy<>RefCounted<><>0<>0<><>:: +reward_supply_controller.gd::GDScript::7501430662356378774::1789232984::0::1::::RewardSupplyController<>RefCounted<><>0<>0<><>:: +rng_service.gd::GDScript::1338675472946526387::1789232968::0::1::::<>Node<><>0<>0<><>:: +status_container.gd::GDScript::7936126502523773580::1789129899::0::1::::StatusContainer<>RefCounted<><>0<>0<><>:: +unit_container.gd::GDScript::7462364046193057341::1789236551::0::1::::UnitContainer<>RefCounted<><>0<>0<><>:: +::res://data/::1789034260 +::res://data/schemas/::1789207599 +action_economy_table.gd::GDScript::8522817588810408628::1789144440::0::1::::ActionEconomyTable<>Resource<><>0<>0<><>:: +board_gen_table.gd::GDScript::290673939920197678::1789034188::0::1::::BoardGenTable<>Resource<><>0<>0<><>:: +difficulty_table.gd::GDScript::107972707759898139::1789148819::0::1::::DifficultyTable<>Resource<><>0<>0<><>:: +enemy_table.gd::GDScript::4876593032319850172::1789207522::0::1::::EnemyTable<>Resource<><>0<>0<><>:: +hero_table.gd::GDScript::9222491661618750738::1789061434::0::1::::HeroTable<>Resource<><>0<>0<><>:: +level_table.gd::GDScript::1724087523887552828::1789113333::0::1::::LevelTable<>Resource<><>0<>0<><>:: +reward_config_table.gd::GDScript::7355091203371621038::1789125672::0::1::::RewardConfigTable<>Resource<><>0<>0<><>:: +reward_table.gd::GDScript::5669541951934428507::1789125710::0::1::::RewardTable<>Resource<><>0<>0<><>:: +settlement_table.gd::GDScript::6131581376971031268::1789207599::0::1::::SettlementTable<>Resource<><>0<>0<><>:: +status_effect_table.gd::GDScript::4437475637910601838::1789034138::0::1::::StatusEffectTable<>Resource<><>0<>0<><>:: +variation_table.gd::GDScript::3386413835420127059::1789034137::0::1::::VariationTable<>Resource<><>0<>0<><>:: +::res://data/tables/::1789280172 +action_economy.tres::Resource/ActionEconomyTable::-1::1789144440::0::1::::<><><>0<>0<><>::res://data/schemas/action_economy_table.gd +board_gen.tres::Resource/BoardGenTable::-1::1789118328::0::1::::<><><>0<>0<><>::res://data/schemas/board_gen_table.gd +difficulty.tres::Resource/DifficultyTable::-1::1789148819::0::1::::<><><>0<>0<><>::res://data/schemas/difficulty_table.gd +enemy.tres::Resource/EnemyTable::-1::1789207507::0::1::::<><><>0<>0<><>::res://data/schemas/enemy_table.gd +hero.tres::Resource/HeroTable::-1::1789064873::0::1::::<><><>0<>0<><>::res://data/schemas/hero_table.gd +level.tres::Resource/LevelTable::-1::1789113345::0::1::::<><><>0<>0<><>::res://data/schemas/level_table.gd +reward.tres::Resource/RewardTable::-1::1789280009::0::1::::<><><>0<>0<><>::res://data/schemas/reward_table.gd +reward_config.tres::Resource/RewardConfigTable::-1::1789125741::0::1::::<><><>0<>0<><>::res://data/schemas/reward_config_table.gd +settlement.tres::Resource/SettlementTable::-1::1789280154::0::1::::<><><>0<>0<><>::res://data/schemas/settlement_table.gd +status_effects.tres::Resource/StatusEffectTable::-1::1789280182::0::1::::<><><>0<>0<><>::res://data/schemas/status_effect_table.gd +variation.tres::Resource/VariationTable::-1::1789034260::0::1::::<><><>0<>0<><>::res://data/schemas/variation_table.gd +::res://presentation/::1789246033 +battle_narrator.gd::GDScript::5226435456607670966::1789229051::0::1::::<>RefCounted<><>0<>0<><>:: +debug_session.gd::GDScript::8434422127986491287::1789243501::0::1::::<>RefCounted<><>0<>0<><>:: +hud_presenter.gd::GDScript::266080447502779628::1789233101::0::1::::HudPresenter<>RefCounted<><>0<>0<><>:: +main.gd::GDScript::7302531409199717733::1789243501::0::1::::<>Control<><>0<>0<><>:: +main.tscn::PackedScene::6193690700815517790::1789034097::0::1::::<><><>0<>0<><>::res://presentation/main.gd +modal_stack.gd::GDScript::3147898925570088454::1789229050::0::1::::<>RefCounted<><>0<>0<><>:: +playtest_logger.gd::GDScript::2237693618347780217::1789243222::0::1::::PlaytestLogger<>RefCounted<><>0<>0<><>:: +tune_sim_controller.gd::GDScript::1034382086574245163::1789243536::0::1::::<>RefCounted<><>0<>0<><>:: +ui_assembler.gd::GDScript::7013135001962184004::1789246033::0::1::::<>RefCounted<><>0<>0<><>:: +::res://presentation/views/::1789233039 +ap_bar_view.gd::GDScript::6706481391074465845::1789203369::0::1::::ApBarView<>PanelContainer<><>0<>0<><>:: +battle_end_overlay.gd::GDScript::3399056977949525852::1789203797::0::1::::BattleEndOverlay<>Control<><>0<>0<><>:: +battle_log_view.gd::GDScript::789450573482474203::1789207803::0::1::::BattleLogView<>PanelContainer<><>0<>0<><>:: +battle_stage_view.gd::GDScript::5667172321656987572::1789205728::0::1::::BattleStageView<>PanelContainer<><>0<>0<><>:: +board_fx_view.gd::GDScript::1601194353712525323::1789202137::0::1::::BoardFxView<>Control<><>0<>0<><>:: +board_view.gd::GDScript::2101785913681906214::1789233039::0::1::::BoardView<>Control<><>0<>0<><>:: +board_zone_view.gd::GDScript::5694869616755791188::1789202681::0::1::::BoardZoneView<>PanelContainer<><>0<>0<><>:: +desaturation.gdshader::Shader::3539526562288928378::1789202028::0::1::::<><><>0<>0<><>:: +enemy_card_view.gd::GDScript::5658106289382993794::1789228090::0::1::::EnemyCardView<>PanelContainer<><>0<>0<><>:: +hero_card_view.gd::GDScript::7848267529495934977::1789202280::0::1::::HeroCardView<>PanelContainer<><>0<>0<><>:: +hero_info_panel_view.gd::GDScript::890950290308946654::1789228091::0::1::::HeroInfoPanelView<>PanelContainer<><>0<>0<><>:: +inventory_panel_view.gd::GDScript::5693348628536103891::1789205728::0::1::::InventoryPanelView<>PanelContainer<><>0<>0<><>:: +legend_overlay.gd::GDScript::8747339015513470369::1789202163::0::1::::LegendOverlay<>Control<><>0<>0<><>:: +level_strip_view.gd::GDScript::887444383034759579::1789203296::0::1::::LevelStripView<>HBoxContainer<><>0<>0<><>:: +node_overlay.gd::GDScript::4207691819312229744::1789128470::0::1::::NodeOverlay<>Control<><>0<>0<><>:: +settlement_review_overlay.gd::GDScript::7219371183860710574::1789202239::0::1::::SettlementReviewOverlay<>Control<><>0<>0<><>:: +status_slot_row.gd::GDScript::3482792166004692461::1789215866::0::1::::StatusSlotRow<>HBoxContainer<><>0<>0<><>:: +toast_stack.gd::GDScript::4052418265818498251::1789202048::0::1::::ToastStack<>Control<><>0<>0<><>:: +tune_panel.gd::GDScript::8773926940492071944::1789228138::0::1::::TunePanel<>Control<><>0<>0<><>:: +ui_kit.gd::GDScript::3178727357685254666::1789128242::0::1::::UiKit<>RefCounted<><>0<>0<><>:: +::res://smoke/::1789280205 +auto_player.gd::GDScript::5451104337629630247::1789237174::0::1::::<>RefCounted<><>0<>0<><>:: +defense_scan.gd::GDScript::8379610624300424582::1789280205::0::1::::<>SceneTree<><>0<>0<><>:: +dump_battle.gd::GDScript::5133704044948130493::1789237266::0::1::::<>SceneTree<><>0<>0<><>:: +experiment_density.gd::GDScript::5260599749047087749::1789129898::0::1::::<>SceneTree<><>0<>0<><>:: +level_autoplay.gd::GDScript::662970221026420953::1789243327::0::1::::<>RefCounted<><>0<>0<><>:: +level_debug.gd::GDScript::2760977088613608031::1789126166::0::1::::<>SceneTree<><>0<>0<><>:: +level_monte_carlo.gd::GDScript::549864169145451125::1789243370::0::1::::<>SceneTree<><>0<>0<><>:: +monte_carlo.gd::GDScript::7869543635707275019::1789237266::0::1::::<>SceneTree<><>0<>0<><>:: +reward_expectation.gd::GDScript::6689296240354685181::1789129898::0::1::::<>SceneTree<><>0<>0<><>:: +smoke_all.gd::GDScript::3155837053821105962::1789237237::0::1::::<>SceneTree<><>0<>0<><>:: +supply_scan.gd::GDScript::8762258276062492019::1789243580::0::1::::<>SceneTree<><>0<>0<><>:: +ui_click_probe.gd::GDScript::8163827295684929025::1789237453::0::1::::<>Node<><>0<>0<><>:: +ui_click_probe.tscn::PackedScene::-1::1789117732::0::1::::<><><>0<>0<><>::res://smoke/ui_click_probe.gd +ui_layout_probe.gd::GDScript::3805933281686222633::1789246033::0::1::::<>Node<><>0<>0<><>:: +ui_layout_probe.tscn::PackedScene::-1::1789245918::0::1::::<><><>0<>0<><>::res://smoke/ui_layout_probe.gd +ui_probe.gd::GDScript::7006810144725363874::1789243535::0::1::::<>Node<><>0<>0<><>:: +ui_probe.tscn::PackedScene::-1::1789116662::0::1::::<><><>0<>0<><>::res://smoke/ui_probe.gd +ui_tune_probe.gd::GDScript::1332260224320123543::1789237453::0::1::::<>Node<><>0<>0<><>:: +ui_tune_probe.tscn::PackedScene::-1::1789127580::0::1::::<><><>0<>0<><>::res://smoke/ui_tune_probe.gd diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/filesystem_update4 b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/filesystem_update4 new file mode 100644 index 00000000..2c513e8c --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/filesystem_update4 @@ -0,0 +1,6 @@ +res://core/battle_orchestrator.gd +res://core/reward_economy.gd +res://smoke/defense_scan.gd +res://smoke/level_monte_carlo.gd +res://smoke/auto_player.gd +res://smoke/level_autoplay.gd diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/main.tscn-editstate-1c7ced1d733f75dbde915a77f14685d1.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/main.tscn-editstate-1c7ced1d733f75dbde915a77f14685d1.cfg new file mode 100644 index 00000000..60547f25 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/main.tscn-editstate-1c7ced1d733f75dbde915a77f14685d1.cfg @@ -0,0 +1,201 @@ +[editor_states] + +$selected_nodes=Array[NodePath]([]) +2D={ +"grid_offset": Vector2(0, 0), +"grid_snap_active": false, +"grid_step": Vector2(8, 8), +"grid_visibility": 1, +"ofs": Vector2(-166, -111), +"primary_grid_step": Vector2i(8, 8), +"show_group_gizmos": true, +"show_guides": true, +"show_helpers": false, +"show_lock_gizmos": true, +"show_origin": true, +"show_position_gizmos": true, +"show_rulers": true, +"show_transformation_gizmos": true, +"show_viewport": true, +"show_zoom_control": true, +"smart_snap_active": false, +"snap_guides": true, +"snap_node_anchors": true, +"snap_node_center": true, +"snap_node_parent": true, +"snap_node_sides": true, +"snap_other_nodes": true, +"snap_pixel": true, +"snap_relative": false, +"snap_rotation": false, +"snap_rotation_offset": 0.0, +"snap_rotation_step": 0.2617994, +"snap_scale": false, +"snap_scale_step": 0.1, +"zoom": 1.0 +} +3D={ +"fov": 70.01, +"gizmos_status": { +"AudioListener3D": 0, +"AudioStreamPlayer3D": 0, +"CPUParticles3D": 0, +"CSGShape3D": 0, +"Camera3D": 0, +"ChainIK3D": 0, +"CollisionObject3D": 0, +"CollisionPolygon3D": 0, +"CollisionShape3D": 0, +"Decal": 0, +"FogVolume": 0, +"GPUParticles3D": 0, +"GPUParticlesCollision3D": 0, +"Joint3D": 0, +"Light3D": 0, +"LightmapGI": 0, +"LightmapProbe": 0, +"Marker3D": 0, +"MeshInstance3DCustomAABB": 0, +"NavigationLink3D": 0, +"NavigationObstacle3D": 0, +"NavigationRegion3D": 0, +"OccluderInstance3D": 0, +"Particles3DEmissionShape": 0, +"Path3D": 0, +"PhysicalBone3D": 0, +"RayCast3D": 0, +"ReflectionProbe": 0, +"ShapeCast3D": 0, +"Skeleton3D": 0, +"SoftBody3D": 0, +"SpringArm3D": 0, +"SpringBoneCollision3D": 0, +"SpringBoneSimulator3D": 0, +"TwoBoneIK3D": 0, +"VehicleWheel3D": 0, +"VisibleOnScreenNotifier3D": 0, +"VoxelGI": 0 +}, +"local_coords": false, +"preserve_children_transform": false, +"preview_sun_env": { +"environ_ao_enabled": false, +"environ_enabled": true, +"environ_energy": 1.0, +"environ_gi_enabled": false, +"environ_glow_enabled": false, +"environ_ground_color": Color(0.2, 0.169, 0.133, 1), +"environ_sky_color": Color(0.385, 0.454, 0.55, 1), +"environ_tonemap_enabled": false, +"sun_color": Color(1, 1, 1, 1), +"sun_enabled": true, +"sun_energy": 1.0, +"sun_rotation": Vector2(-1.0471976, 2.6179938), +"sun_shadow_max_distance": 100.0 +}, +"rotate_snap": 15.0, +"scale_snap": 10.0, +"show_grid": true, +"show_origin": true, +"snap_enabled": false, +"trackball_enabled": false, +"translate_snap": 1.0, +"vertex_snap_origin_mode": false, +"vertex_snap_use_collision": false, +"viewport_mode": 1, +"viewport_splits": { +"first": 0.0, +"main": 0.0, +"second": 0.0 +}, +"viewports": [{ +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": true, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}, { +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": false, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}, { +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": false, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}, { +"auto_orthogonal": false, +"auto_orthogonal_enabled": true, +"cinematic_preview": false, +"display_mode": 22, +"distance": 4.0, +"doppler": false, +"frame_time": false, +"gizmos": true, +"grid": true, +"half_res": false, +"information": false, +"listener": false, +"lock_rotation": false, +"orthogonal": false, +"position": Vector3(0, 0, 0), +"transform_gizmo": true, +"use_environment": false, +"view_type": 0, +"x_rotation": 0.5, +"y_rotation": -0.5 +}], +"zfar": 4000.01, +"znear": 0.05 +} +Anim={ +"visible": false +} diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/main.tscn-folding-1c7ced1d733f75dbde915a77f14685d1.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/main.tscn-folding-1c7ced1d733f75dbde915a77f14685d1.cfg new file mode 100644 index 00000000..697fdc7d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/main.tscn-folding-1c7ced1d733f75dbde915a77f14685d1.cfg @@ -0,0 +1,6 @@ +[folding] + +node_unfolds=[NodePath("."), PackedStringArray("Layout")] +resource_unfolds=[] +nodes_folded=[] +animation_groups_folded=[] diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/project_metadata.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/project_metadata.cfg new file mode 100644 index 00000000..3adeff74 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/project_metadata.cfg @@ -0,0 +1,13 @@ +[game_view] + +select_mode=0 +hide_selection=false + +[editor_metadata] + +executable_path="D:/code/godot/Godot_v4.7.2-stable_win64.exe" + +[recent_files] + +scenes=["res://presentation/main.tscn"] +scripts=["res://core/board_generator.gd", "res://data/schemas/reward_table.gd", "res://core/reward_supply_controller.gd", "res://core/reward_economy.gd", "res://presentation/views/status_slot_row.gd", "res://core/a1_board_generator.gd", "res://presentation/main.gd"] diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/script_editor_cache.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/script_editor_cache.cfg new file mode 100644 index 00000000..02b0b6d5 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/editor/script_editor_cache.cfg @@ -0,0 +1,87 @@ +[res://presentation/main.gd] + +state={ +"bookmarks": PackedInt32Array(), +"breakpoints": PackedInt32Array(), +"column": 0, +"folded_lines": PackedInt32Array(), +"h_scroll_position": 0, +"row": 433, +"scroll_position": 395.8, +"selection": false, +"syntax_highlighter": "GDScript" +} + +[res://presentation/views/status_slot_row.gd] + +state={ +"bookmarks": PackedInt32Array(), +"breakpoints": PackedInt32Array(), +"column": 6, +"folded_lines": PackedInt32Array(), +"h_scroll_position": 0, +"row": 121, +"scroll_position": 112.0, +"selection": false, +"syntax_highlighter": "GDScript" +} + +[res://core/reward_economy.gd] + +state={ +"bookmarks": PackedInt32Array(), +"breakpoints": PackedInt32Array(), +"column": 0, +"folded_lines": PackedInt32Array(), +"h_scroll_position": 0, +"row": 317, +"scroll_position": 289.0, +"selection": false, +"syntax_highlighter": "GDScript" +} + +[res://core/reward_supply_controller.gd] + +state={ +"bookmarks": PackedInt32Array(), +"breakpoints": PackedInt32Array(), +"column": 0, +"folded_lines": PackedInt32Array(), +"h_scroll_position": 0, +"row": 27, +"scroll_position": 5.0, +"selection": false, +"syntax_highlighter": "GDScript" +} + +[res://data/schemas/reward_table.gd] + +state={ +"bookmarks": PackedInt32Array(), +"breakpoints": PackedInt32Array(), +"column": 64, +"folded_lines": PackedInt32Array(), +"h_scroll_position": 0, +"row": 11, +"scroll_position": 11.0, +"selection": true, +"selection_from_column": 51, +"selection_from_line": 11, +"selection_to_column": 64, +"selection_to_line": 11, +"syntax_highlighter": "GDScript" +} + +[res://core/board_generator.gd] + +state={ +"bookmarks": PackedInt32Array(), +"breakpoints": PackedInt32Array(), +"column": 24, +"folded_lines": PackedInt32Array(), +"h_scroll_position": 0, +"row": 59, +"scroll_position": 50.0, +"selection": false, +"syntax_highlighter": "GDScript" +} diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/global_script_class_cache.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/global_script_class_cache.cfg new file mode 100644 index 00000000..af83d522 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/.godot/global_script_class_cache.cfg @@ -0,0 +1,345 @@ +list=[{ +"base": &"RefCounted", +"class": &"ActionEconomy", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/action_economy.gd" +}, { +"base": &"Resource", +"class": &"ActionEconomyTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/action_economy_table.gd" +}, { +"base": &"PanelContainer", +"class": &"ApBarView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/ap_bar_view.gd" +}, { +"base": &"Control", +"class": &"BattleEndOverlay", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/battle_end_overlay.gd" +}, { +"base": &"PanelContainer", +"class": &"BattleLogView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/battle_log_view.gd" +}, { +"base": &"RefCounted", +"class": &"BattleOrchestrator", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/battle_orchestrator.gd" +}, { +"base": &"PanelContainer", +"class": &"BattleStageView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/battle_stage_view.gd" +}, { +"base": &"RefCounted", +"class": &"BoardData", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/board_data.gd" +}, { +"base": &"Control", +"class": &"BoardFxView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/board_fx_view.gd" +}, { +"base": &"Resource", +"class": &"BoardGenTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/board_gen_table.gd" +}, { +"base": &"RefCounted", +"class": &"BoardGenerator", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/board_generator.gd" +}, { +"base": &"Control", +"class": &"BoardView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/board_view.gd" +}, { +"base": &"PanelContainer", +"class": &"BoardZoneView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/board_zone_view.gd" +}, { +"base": &"RefCounted", +"class": &"DamageMath", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/damage_math.gd" +}, { +"base": &"Resource", +"class": &"DifficultyTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/difficulty_table.gd" +}, { +"base": &"PanelContainer", +"class": &"EnemyCardView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/enemy_card_view.gd" +}, { +"base": &"Resource", +"class": &"EnemyTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/enemy_table.gd" +}, { +"base": &"PanelContainer", +"class": &"HeroCardView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/hero_card_view.gd" +}, { +"base": &"PanelContainer", +"class": &"HeroInfoPanelView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/hero_info_panel_view.gd" +}, { +"base": &"Resource", +"class": &"HeroTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/hero_table.gd" +}, { +"base": &"RefCounted", +"class": &"HudPresenter", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/hud_presenter.gd" +}, { +"base": &"PanelContainer", +"class": &"InventoryPanelView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/inventory_panel_view.gd" +}, { +"base": &"Control", +"class": &"LegendOverlay", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/legend_overlay.gd" +}, { +"base": &"RefCounted", +"class": &"LevelRunner", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/level_runner.gd" +}, { +"base": &"HBoxContainer", +"class": &"LevelStripView", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/level_strip_view.gd" +}, { +"base": &"Resource", +"class": &"LevelTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/level_table.gd" +}, { +"base": &"RefCounted", +"class": &"MinesweeperRules", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/minesweeper_rules.gd" +}, { +"base": &"Control", +"class": &"NodeOverlay", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/node_overlay.gd" +}, { +"base": &"RefCounted", +"class": &"PlaytestLogger", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/playtest_logger.gd" +}, { +"base": &"Resource", +"class": &"RewardConfigTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/reward_config_table.gd" +}, { +"base": &"RefCounted", +"class": &"RewardEconomy", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/reward_economy.gd" +}, { +"base": &"RefCounted", +"class": &"RewardSupplyController", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/reward_supply_controller.gd" +}, { +"base": &"Resource", +"class": &"RewardTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/reward_table.gd" +}, { +"base": &"Control", +"class": &"SettlementReviewOverlay", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/settlement_review_overlay.gd" +}, { +"base": &"Resource", +"class": &"SettlementTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/settlement_table.gd" +}, { +"base": &"RefCounted", +"class": &"StatusContainer", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/status_container.gd" +}, { +"base": &"Resource", +"class": &"StatusEffectTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/status_effect_table.gd" +}, { +"base": &"HBoxContainer", +"class": &"StatusSlotRow", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/status_slot_row.gd" +}, { +"base": &"Control", +"class": &"ToastStack", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/toast_stack.gd" +}, { +"base": &"Control", +"class": &"TunePanel", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/tune_panel.gd" +}, { +"base": &"RefCounted", +"class": &"UiKit", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://presentation/views/ui_kit.gd" +}, { +"base": &"RefCounted", +"class": &"UnitContainer", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://core/unit_container.gd" +}, { +"base": &"Resource", +"class": &"VariationTable", +"icon": "", +"is_abstract": false, +"is_tool": false, +"language": &"GDScript", +"path": "res://data/schemas/variation_table.gd" +}] diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/scene_groups_cache.cfg b/prototypes/dungeon-sweeper-vertical-slice/.godot/scene_groups_cache.cfg new file mode 100644 index 00000000..e69de29b diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CanvasShaderGLES3/531da6e218709ccfefe2153daddadc3c56f48fb8c9fbb6c826f00cfbdf8514d8/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CanvasShaderGLES3/531da6e218709ccfefe2153daddadc3c56f48fb8c9fbb6c826f00cfbdf8514d8/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache new file mode 100644 index 00000000..5861a006 Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CanvasShaderGLES3/531da6e218709ccfefe2153daddadc3c56f48fb8c9fbb6c826f00cfbdf8514d8/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CopyShaderGLES3/c06c261150fba5e3a2d4cd35bfb426cb156e8601f40cb501e061fb7a1f4e1f00/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CopyShaderGLES3/c06c261150fba5e3a2d4cd35bfb426cb156e8601f40cb501e061fb7a1f4e1f00/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache new file mode 100644 index 00000000..72c8a0be Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CopyShaderGLES3/c06c261150fba5e3a2d4cd35bfb426cb156e8601f40cb501e061fb7a1f4e1f00/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CubemapFilterShaderGLES3/46acd91d7aee1c03048b89cd8bc8ed0577da45c825fcd6f39ca893e9056e1a2a/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CubemapFilterShaderGLES3/46acd91d7aee1c03048b89cd8bc8ed0577da45c825fcd6f39ca893e9056e1a2a/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache new file mode 100644 index 00000000..07bbb339 Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/CubemapFilterShaderGLES3/46acd91d7aee1c03048b89cd8bc8ed0577da45c825fcd6f39ca893e9056e1a2a/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/FeedShaderGLES3/1930a9041449dfc0f9d36405670f3e7631853a0eb90a82ee1b72d530b02dee06/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/FeedShaderGLES3/1930a9041449dfc0f9d36405670f3e7631853a0eb90a82ee1b72d530b02dee06/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache new file mode 100644 index 00000000..7009977b Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/FeedShaderGLES3/1930a9041449dfc0f9d36405670f3e7631853a0eb90a82ee1b72d530b02dee06/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/PostShaderGLES3/9cd720cf1067463bba913483591d66cdcc33fac81befb265c9e320cb41be0c52/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/PostShaderGLES3/9cd720cf1067463bba913483591d66cdcc33fac81befb265c9e320cb41be0c52/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache new file mode 100644 index 00000000..1e53630c Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/PostShaderGLES3/9cd720cf1067463bba913483591d66cdcc33fac81befb265c9e320cb41be0c52/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache new file mode 100644 index 00000000..6f3f5d6f Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/6bb7c9a2fd1c4a8fa77cb96555b9b4a720209981.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/a38af2d0725c8e99d8492f4c72736f24d6a54cc5.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/a38af2d0725c8e99d8492f4c72736f24d6a54cc5.cache new file mode 100644 index 00000000..91654f05 Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/a38af2d0725c8e99d8492f4c72736f24d6a54cc5.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/cafb1506f2db0c3681435e264aa18e216d1a7a40.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/cafb1506f2db0c3681435e264aa18e216d1a7a40.cache new file mode 100644 index 00000000..1a45f6e6 Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SceneShaderGLES3/89a6b56540c86e46c74b1d39a02fba43e7c1f547506f023af638b4759b98b4b3/cafb1506f2db0c3681435e264aa18e216d1a7a40.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SkyShaderGLES3/9c3051099586c72db4acbffcace4cb5e4dd72d24297bf12e2f71e2f69d987234/2c82eb5fea9b549e20c9f41e308abf9a4fc5d76f.cache b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SkyShaderGLES3/9c3051099586c72db4acbffcace4cb5e4dd72d24297bf12e2f71e2f69d987234/2c82eb5fea9b549e20c9f41e308abf9a4fc5d76f.cache new file mode 100644 index 00000000..15c62e51 Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/shader_cache/SkyShaderGLES3/9c3051099586c72db4acbffcace4cb5e4dd72d24297bf12e2f71e2f69d987234/2c82eb5fea9b549e20c9f41e308abf9a4fc5d76f.cache differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/.godot/uid_cache.bin b/prototypes/dungeon-sweeper-vertical-slice/.godot/uid_cache.bin new file mode 100644 index 00000000..0f693b19 Binary files /dev/null and b/prototypes/dungeon-sweeper-vertical-slice/.godot/uid_cache.bin differ diff --git a/prototypes/dungeon-sweeper-vertical-slice/README.md b/prototypes/dungeon-sweeper-vertical-slice/README.md new file mode 100644 index 00000000..f733e64b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/README.md @@ -0,0 +1,405 @@ +# Dungeon Sweeper · 垂直切片(v2:第一关完整关卡) + +> // VERTICAL SLICE - NOT FOR PRODUCTION +> // Validation Question(v2,2026-09-11):线性第一关(战×2→道具→战×2→恢复→Boss)能否承载 +> // "数值提升→通关期望改善"的 Roguelike 元进度验证——基线属性应该难到打不过、天赋数值提升把 +> // 通关期望拉回可玩带,且这条曲线能用数值调整板 + 批量模拟**测量出来** +> // (通关攻击期望 / 败北血量数学期望);v1 的单战斗循环问题(逐行归因 P2 / Core headless 可跑) +> // 已于 2026-09-10 验证并保持回归(smoke_all 前 46 项)。 +> // Date: 2026-09-10(v1)· 2026-09-11(v2 关卡扩展)· 引擎 Godot 4.7.2 · 本目录仅作设计验证参考,禁止 import 进生产代码 + +## 运行 + +1. 用 Godot 4.7.2 打开本目录(含 `project.godot` 的文件夹) +2. 按 F5 运行(主场景 `presentation/main.tscn`) +3. **主入口:`▶ 第一关 · 苔藓回廊`**——线性节点链:战→战→赏(随机主动道具)→战→战→疗(血量恢复点)→王(Boss 遗迹守卫) +4. `测试用 · 角色数值调整板`:攻击/生命百分比加成(模拟天赋树解锁)+ 批量模拟(输出通关率/承伤期望/败北节点分布) + +headless 冒烟(55 项断言,含关卡线性推进/满配通关/裸装败北/回放确定性,约 30 秒): + +``` +Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/smoke_all.gd +``` + +场景级 UI 探针(项目运行模式实例化 main.tscn 驱动第一关,断言棋盘真实渲染——D1 教训"呈现层零测试承载"的原型兑现): + +``` +Godot_v4.7.2-stable_win64_console.exe --headless --path . res://smoke/ui_probe.tscn +``` + +数值调整板场景探针(缺陷⑤回归:打开可见性/滑条联动/应用写表/重置/批量模拟全链路): + +``` +Godot_v4.7.2-stable_win64_console.exe --headless --path . res://smoke/ui_tune_probe.tscn +``` + +关卡期望扫描(C3 调参期关卡侧批量工具原型——atk%×hp% 网格,每格输出通关率/承伤期望/败北节点): + +``` +Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/level_monte_carlo.gd -- [N] [seed0] +``` + +可选类供给跨种子扫描(playtest-2026-09-12 Top1 复核——每局治疗包/小刀/精工短刀获取数、构成占比对 9:1 配比、"≥3 治疗包"局数分布;单种子坏抽与系统性偏置由此分割): + +``` +Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/supply_scan.gd -- [N] [seed0] +``` + +**日志合规(2026-09-12 用户要求)**:任何模拟运行与真人试玩均落全量 JSONL(user://logs/)——逐操作(op/cell/accepted/round)、逐结算明细行、轮次状态快照、battle_end、level_end。模拟(level_monte_carlo / 调整板批量模拟 / supply_scan)一文件多局,run 行携带种子/档位切分局次。 + +单关诊断:`--script res://smoke/level_debug.gd -- [seed] [atk%] [hp%]`。 +战斗级工具(v1 遗产):`dump_battle.gd`(结算流水)/ `monte_carlo.gd`(战斗期望)/ `experiment_density.gd`(密度调参)。 + +## 范围(v1 范围 + v2 追加,用户 2026-09-10/11 确认) + +- **v1 在范围内**:D2(10 表 + ConfigDB + RngService)· A1(single_subset 求解器 + 双判据验收)· A2 · A3(AP8/RAP)· A4(迷雾,L2)· B1(七段结算 + 13 类行协议 + 高光双落)· B2(grub/ambusher/伏击池 cap4)· B3(四行状态容器)· B4(install 预分配/F5 保底/道具窗口)· D1-lite 程序化呈现 +- **v2 追加**:LevelTable(线性节点链数据表)· C1-lite 关卡推进器 `level_runner.gd`(原 c1_level_runner,2026-09-11 领域命名批更名)(关卡域生命周期:共享 B4 关内延续 + HP 跨战斗携带 + 奖励/恢复节点兑付 + 关卡末清库)· Boss 遭遇行(Dmax=4)· 节点奖励 API(B4 `grant_node_active`)· **测试用角色数值调整板**(atk%/hp% 覆盖 + 批量模拟,模拟天赋树——正式侧归 C4) +- **裁剪不变**:A5 / C1 路径分支选择(用户拍板线性)· C2–C4 / B5 战斗动画 / D3–D6 / A1 黄金向量与 F4 批量工具 / gdUnit / 标错惩罚(A2 OQ1) + +## 2026-09-11 可选类供给控制器(用户拍板批——修改记录) + +**背景**:供给控制器批前实测每关可选类 ≈24.8 件(≈18.5 把刀,刀总伤 ≈180 > Boss 130 HP)——"道具获取期望太高"定量坐实。用户重新定义三尺度指标并拍板实现: + +| 尺度 | 指标 | 实现机制 | +|---|---|---| +| 安全格 | 期望极小(全图摊平 < 0.01/格) | 可选类**退出格子权重池**——格子池只剩即时类四行 | +| 雷图 | ≈0.78/板 | 战斗首件保证 ÷ 每战板数(≈1.28)折算 | +| 战斗 | 期望 ≈1,**首件保证**:获取首件前每张新板必注入 1 件;之后按 **p0×decay^(j-1) 指数递减**(永不绝育) | `RewardSupplyController`(B4 私有组合件,战斗域计数) | + +**拍板四条 → 落点**:① 注入占比 小刀 60% / 精工短刀 30% / **治疗包 10%**(治疗资源宝贵)→ `reward.tres` active_inject 池;② **清图奖励改一次性高额伤害**(clear_knife 入库刀 → clear_blast instant_aoe 40,当场兑付全体存活敌,不入库、不占可选类供给——清图与供给脱钩);③ 注入选格**均匀随机**(雾格不加权);④ **指数递减**(active_p0=0.4 / active_decay=0.5 → 0.4/0.2/0.1/0.05…,RewardConfigTable 新参数,provisional 归 C3 标定)。 + +**实现结构**(对象封装——伪代码审阅后落地): + +- `core/reward_supply_controller.gd`(新):`notify_battle_start()`(计数清零)/ `should_inject(stream)`(j==0 恒真;否则 `randf < p0×decay^(j-1)`,取号经传入封闭流)/ `notify_granted()` / `granted_count()`。行值快照抗热重载;零自有随机数。 +- `B4RewardEconomy`(改造):`_supply` 组合件 · `notify_battle_start()`(B1.begin_battle 转发)· `roll_board` 重写(身份批即时类单遍 → 清图槽 → 注入判定/选格;旧 F5 草稿重 roll 循环删除)· `grant()` 挂钩 `notify_granted()`(**发放即获取**——预分配未挖到不计数;去重幂等天然防双计)· `supply_granted_count()` 观测面。 +- `B1BattleOrchestrator`:begin_battle 门序列增 `b4.notify_battle_start()`(编排唯一性不变)。 +- 表/护栏:RewardTable `POOL_TAGS` + `active_inject`;RewardConfigTable + `active_p0/active_decay`、k_dmg 1→0(F5 休眠);ConfigDB 跨表护栏更新(clear_no_value 含即时类 / no_damage_row 扫描全池活行 / 新增 inject_pool 护栏:非可选类拒绝 + 空池致命)。 + +**实测(N=200 板/深度,工具 reward_expectation.gd)**:注入 1.00 件/板(j=0,多件板 0)· 占比 小刀 59–65% / 精工 25–29% / 治疗包 7–17%(名义 60/30/10)· 格池零可选类泄漏 ✓。**实际获取期望**(level_monte_carlo「可选类/战」列,N=15):基线 0.88 → +200% 攻击 0.68 件/战(低于分配保证 1.0 的机理 = 注入格均匀随机,落在残余格/速杀未挖区时该件不获取;战斗末无兜底补发——**是否加"战斗末 j==0 补发 1 件"待用户拍板**)。每关合计 ≈ 4.5–6.5 件(原 24.8)✓ 拍板目标达成。 + +**⚠️ 供给侧副作用(本轮扫描发现,待拍板)**:可选类退出格子池后,其 25% 概率质量被动流向即时类——攻防 buff 每格频率从 44% 升至 59%,基线通关率 55%→80%(N=15)、整关承伤 127→80:**难度不降反升方向反了**。即时类是"每格必有货"锁定设计下无法清空的(除非引入空白格 = 违反锁定事实)。候选杠杆:① 接受更快的 buff 节奏,敌 HP 曲线上调对冲(C3 主杠杆)② 下调 attack_up/defense_up 在即时池内的相对权重(压 buff 频率)③ buff 幅度下调(触碰 B3 表值)。 + +**正式侧义务**:B4 GDD「install 期 roll / F5」章节修订(F5 → 战斗首件保证语义)+ registry reward_table/reward_config 条目改写 + RewardConfigTable knob 登记(active_p0/active_decay)——接口漂移批随实现落位。 + +## 2026-09-12 轮内刷新(方案 A,用户拍板批——修改记录) + +**背景**:用户发现刷新实现(仅 Waiting 结算窗口受理)与设计预期不符——预期为**扫雷阶段遇到推理死路时,有剩余 AP 且 RAP 足够即可当场刷新继续挖**。影响分析(三方案对比 + 16 代码触点 + 8 文档 + 5 数值连锁)后用户拍板方案 A。 + +**拍板语义**:① Active 相位可刷(RAP ≥ 8 且本轮未达上限)→ 当场 retire+install 换新图,**轮次继续**:AP 保留不重置、相位不动、不触发结算(敌方时间税语义变更——GDD 修订义务);② Waiting 相位刷新保持现状(登记意图,exit_distribution 换图);③ **每轮兜底上限 2 次**(Active/Waiting 合计,`ActionEconomyTable.refresh_max_per_round = 2` 表值);④ RAP 来源不变(停手 1:1 转存)。 + +**实现落点**:A3 `request_refresh_active()`(仅扣 RAP,相位/AP 不动)· A2 `install_board/retire_board` 增 `during_round` 显式参数(Active 通道,其余相位守卫不变——fail-fast 保留)· B4 `query_full_snapshot()` · B1 `can_request_refresh()` 查询面 + `player_request_refresh()` 双相位重写 + `_refresh_used_this_round` 计数 + **facts 身份快照隔离**(缓冲期捕获身份快照、授予走快照——防旧图已挖格在轮末错拿新图同坐标身份;`_grant_cell` 增 snap_override)· 呈现层按钮"弃图刷新(RAP -8 · 每轮至多 2 次)"。 + +**核心实现难点(分析批未暴露)**:轮内换图后旧图已挖未结算格与新图**坐标重叠**——轮末授予 `b4.grant(cell)` 会错拿新图身份。解法 = facts 缓冲期用 `query_full_snapshot` 冻结身份,授予段直通快照(绕过现行 mapping/_granted,批次隔离;库存语义不变)。 + +**验证**:冒烟 88/88(+14 轮内刷新断言:当场换图/相位 AP 保留/RAP 扣减/上限拒绝/跨图 facts 完整兑现〔4 格跨三图快照授予〕/回放确定性)· 三探针全 PASS · 期望扫描与刷新前持平(80%/0.88——模拟策略不主动刷图,真人专用出口)。**调试教训**(规范 4.6 补充案例):蛆虫 HP 已调 24 而"18 未掉血"误判——排查数值问题先核对当前表值;GDScript lambda 对局部 int 按值捕获(计数器必须用容器)。 + +**正式侧义务(GDD/registry 修订清单)**:A3 GDD Rule 11(受理相位 Waiting → Active∪Waiting + 相位机表 + 时间税语义注记)· A2 GDD install 四不变式轮内场景 · B1 GDD 编排专章(轮内换图 + facts 快照协议)· B4 GDD E5/E6(query_full_snapshot/grant 快照直通)· game-concept 轮次流程 + "战斗:扫雷图 = 1:M"措辞(一轮跨多图新形态)· registry `action_economy`(refresh_max_per_round)· 数值连锁重跑(清图支配性条款⑤/获取期望——模拟不覆盖真人刷图行为,需 playtest 实测)。 + +## 2026-09-11 关卡扩展批 · 切片级偏差登记(实现期决策,正式化时须回 GDD/ADR) + +1. **C1 线性推进器 provisional**:节点链来自 LevelTable(结构位 = 未来 C1 的数据承载面);路径分支/奖励点分布定稿权归 C1/C3。节点类型 closed enum {battle, reward, rest, boss};末节点必须 Boss(LevelTable schema 校验)。 +2. **B4 新增 `grant_node_active()`**(接口漂移):关卡奖励点在可选类活行 {damage_item, heal_pack} 中 roll 一件直接入库;消耗 `reward_roll` 恰一 scope(与 board install 批同流异 scope,流集合封闭不变)。正式侧应走 B4 GDD 修订(节点奖励 = clear_reward 插槽族的新来源候选)。 +3. **B2 spawn max_hp 语义修复**(切片 bug):原实现 `max_hp = hero_hp 入参`——跨战斗携带 HP 时 max_hp 被压扁成当前 HP,恢复点/治疗包满血守卫失效。修复为 `max_hp = HeroTable.max_hp`(接口语义澄清,正式侧回 B2 GDD Rule 注记)。 +4. **A4 迷雾纪律遵守**:L1 全程无雾(迷雾 `unlock_level=2` 已锁事实——迷雾自第二层解锁);Boss 板以 12×12 大板区分。 +5. **属性覆盖 = 测试专用**:调整板直接写运行时 HeroTable(atk 域 [1,999] 内 clamp ≥1);关卡结束/回菜单恢复基线。正式天赋树归 C4,禁止生产代码引用本机制。 +6. **UI 复用 b1 引用**:关卡模式下呈现层 `b1 = runner.b1`(与调试模式共用 HUD/棋盘路径);runner 在关卡结束置 `b1 = null`。 +7. **runner 信号时序(playtest 缺陷①修复登记)**:`node_entered` 必须先于 `begin_battle` 发出——后发时首板安装的 `board_changed` 到达呈现层时 `b1` 尚未接手,首战棋盘不渲染。场景级探针 `smoke/ui_probe.tscn` 为该缺陷的回归断言载体。 +8. **A2 旗动作末点 AP 软锁(playtest 缺陷②修复登记——A2 核心 bug,v1 即存在)**:`_do_flag_place/_do_flag_pull` 花完末点 AP 后 A3 相位翻 Waiting 但不发 round_end——B1 的 Waiting 结算点依赖 round_end 到达,事件缺席 = 整局软锁(Boss 图插旗多,末点 AP 花在旗上即触发)。修复 = 旗动作检查相位翻转并补发 `_emit_round_end_flags_and_payload()`(与 `_do_dig` 同款)。v1 冒烟盲区根源:自动玩家不用旗。回归断言入 smoke_all(旗动作耗尽 AP 必须进入第 2 轮)+ 行为探针 `smoke/ui_click_probe.tscn`(合成鼠标事件走真实 UI 路径,随机混合插旗/拔旗/停手/窗口刷新/无效点击,4 种子全关通扫)。**正式侧义务**:A2 GDD F4 事件序契约须补"旗动作路径的 round_end 发射"措辞(接口漂移)。 +9. **呈现层固定分区(playtest 缺陷③修复登记)**:侧栏全部动态区(RAP/敌人/增益/库存/悬停状态)固定槽高 + `clip_contents`——悬停文案切换与库存增长不再推挤整体布局(此前"第 N 轮"库存行被挤出视口 + 全 UI 抖动)。区域独立,超长裁剪不回流。 +10. **L1 12×12 板生成余量**:D2–D4(及 L2)`max_gen_attempts` 16→24(F2c 24×144=3456 ≤ 16384 合法)——16 次尝试下偶发"重试耗尽接受最优"(best≈0.85–0.87 < target 0.88);24 次后仍偶发(best 0.866),密度×推理率的根本解归 C3。 +11. **道具使用后不消失(playtest 缺陷④修复登记——UI 层契约违例)**:B1 窗口选择契约 = `_window_items` **位置**(义务④"选定序"),UI 选用按钮却传 `item.index`(库存数组句柄)——未消耗过时二者巧合相等;一旦消耗过一件,已耗条目仍占库存数组位,后续窗口条目"句柄 = 位置 + 已耗数"错位 → 选中件不消耗、邻件被误耗(可用次数语义本身存在且正确:`use()` 一次性标记 Used 不可逆)。修复 = UI/行为探针一律传窗口位置;探针加"选用必生效"断言(选用件库存态变 Used,失效即退出码非零)。 +12. **数值调整板被遮挡不可见(playtest 缺陷⑤修复登记 + 专属测试补建)**:调整板覆盖层在场景树中位于**不透明的开始覆盖层之前**,`_show_tune_panel` 只置 visible 不隐藏开始层 → 面板被完全遮住 = "点击无反应"。修复 = 打开隐藏开始层 / 关闭(含"应用并关闭")恢复。**专属测试**:`smoke/ui_tune_probe.tscn`(场景级全链路:打开可见性〔缺陷⑤回归〕/ 滑条→生效预览联动 / 应用写 hero 表 12/75 / 重置回基线 6/50 / 批量模拟 N=10 全链路〔与 headless 扫描同口径〕/ 关闭回开始界面 / 加成跨模式持久)。 +13. **呈现层模块化重构(2026-09-11 用户批——"上帝对象"拆分)**:`main.gd` 随功能追加长成 ~1100 行上帝对象(棋盘渲染/HUD/日志/四种覆盖层/调整板/模拟器/关卡流程全糊在一个类)。拆分为 **9 个单一职责单元**(总行数不变,最大单类 433 行):`views/board_view.gd`(棋盘渲染+输入意图翻译,动作经信号外发)· `views/hud_view.gd`(侧栏纯视图,setter+定长槽)· `hud_presenter.gd`(核心状态→文案装配,MVP 静态装配器)· `views/battle_log_view.gd`(13 类行协议格式化)· `views/item_window_overlay.gd`(窗口,选择=位置语义收口)· `views/node_overlay.gd` · `views/level_strip_view.gd` · `views/tune_panel.gd`(调整板自持加成语义+批量模拟)· `views/ui_kit.gd`(共享小部件)。**组合根纪律**:main.gd(433 行)= 模式流转 + 信号接线,核心写调用(settle_batch/submit_window_selection/runner 推进)只在此处发生;视图零核心写、装配器无状态。**回归验证**:冒烟 74/74 + ui_probe/ui_tune_probe PASS + 行为探针 2 种子(通关 + 选用生效 1/2 失效 0)。 + +## 切片调参登记 v2(2026-09-12 起已由 C3 GDD 转正为 MVP 默认曲线——下方登记保留作溯源) + +**EnemyTable L1 深度轴(D0–D4,Boss=Dmax=4——A1 第三轴登记约定)**,设计意图 = 基线难、天赋拉回可玩带: + +| 节点 | 行 | 敌种×数量 | hp | atk | 环 | +|---|---|---|---|---|---| +| 战1 | (1,0) | 蛆虫×1 | 24 | 4 | [attack] | +| 战2 | (1,1) | 蛆虫群×2 | 24 | 4 | [attack] | +| 战3 | (1,2) | 伏击兽×1 | 36 | 6 | [attack,charge,heavy] | +| 战4 | (1,3) | 伏击兽群×2 | 26 | 4 | [attack,charge,heavy] | +| Boss | (1,4) | 遗迹守卫×1 | 130 | 7 | [attack,attack,charge,heavy] | + +首版(敌 HP 总量 208:18/36/26/40/88)实测**基线通关率 73%**——预警⑥速杀退化在关卡尺度依旧成立,与"基线非常难"意图相反;v2 上调至 290(24/48/36/52/130)+ Boss atk 7。**群战对价**:unit_count=2 行单位 hp/atk 下调(B1 条款③满配语境约束;C3 正式调参须按 clause3_supply 复算——registry 已登记 unit_count>1 行须降密度或降 m_amb)。 + +**BoardGenTable L1 深度行**:D0/D1 = 10×10、D2/D3/D4 = 12×12,全部 d0.25/t0.88。**登记**:d0.28/t0.87 首测**不达标**(best≈0.805–0.817,降级告警"接受最优出图")——密度轴上调与求解器能力一起解,留给 C3。 + +**奖励获取期望实测(问题2 汇报批,工具 `smoke/reward_expectation.gd`,N=200 板/深度,走真实 B4 roll 管线含 F5 保底条件化)**: + +| 深度 | 板面 | 资格格均(min/max) | 可选类/板 均 | 其中刀 | 治疗包 | +|---|---|---|---|---|---| +| D0 | 10×10 | 12.4(1/36) | 3.32 | 2.57 | 0.75 | +| D1 | 10×10 | 11.1(2/32) | 3.00 | 2.40 | 0.60 | +| D2 | 12×12 | 22.8(4/63) | 5.70 | 4.42 | 1.27 | +| D3 | 12×12 | 23.8(5/61) | 5.87 | 4.55 | 1.31 | +| D4 | 12×12 | 24.1(6/64) | 5.87 | 4.54 | 1.33 | + +**公式**:每资格格 P(可选类) = 25/100 = 25%(小刀 15% / 精工短刀 4% / 治疗包 6%);每板期望 ≈ 25%×|资格格| + F5 保底条件化加成(k_dmg=1 整批重 roll 至少 1 刀——12×12 板无刀概率 0.81²³<1%,加成可忽略;10×10 板 ≈+0.2);清图奖励 clear_knife 仅真清图兑付(理性策略不整清 ≈0);完美排雷 ×2 条件苛刻不计基线;**节点奖励恒为 1 件可选类**。 + +**每关合计 ≈ 23.8(板)+ 1(节点奖励)≈ 24.8 件可选类**(≈18.5 把刀 + ≈5.3 治疗包)——刀总伤 ≈180,超过 Boss 单体 130 HP。**用户"道具获取期望太高"的定量坐实**:关内不清库(2026-09-11 裁决③)× 每格必有货(B4 锁定)× 12×12 深板资格格翻倍,三者相乘。**候选杠杆(全部 C3 所有权,等拍板再动)**:① 可选类占比 25%→8–12%(改 knife_t1/knife_adv/heal_pack 权重)② 敌 HP 曲线上调对冲 ③ 刀伤下调 ④ 库存上限(触碰 B4 E 节结构,需修订)。 + +**期望扫描数据(N=20/格,种子 4200+20,工具 level_monte_carlo.gd;理性玩家策略近似)**: + +| atk% / hp% | 通关率 | 整关承伤 均/p90 | 通关剩余HP均(胜) | 进BossHP均 | 平均轮数 | 败北节点分布 | +|---|---|---|---|---|---|---| +| +0 / +0(基线) | **55%** | 126.8 / 190 | 33.1 | 44.6 | 18.4 | 蛆虫群×4 · 伏击兽×2 · 伏击兽群×3 | +| +0 / +100 | 100% | 165.8 / 219 | 61.5 | 88.7 | 26.1 | — | +| +50 / +0 | 70% | 109.8 / 155 | 27.1 | 45.5 | 17.1 | 分散(含 Boss×1) | +| +100 / +0 | **100%** | 96.3 / 121 | 33.5 | 47.4 | 17.4 | — | +| +200 / +0 | 95% | 61.1 / 99 | 36.6 | 49.0 | 12.9 | 伏击兽群×1 | + +读法:**基线=明显吃紧(55%,死亡集中在前中期群战)→ 攻击+100% 即满通关带**;HP 翻倍是独立有效的第二杠杆(战斗拖长 → 承伤期望上涨 165 但预算翻倍)。v1 敌数值(HP 208)时基线通关 73%——预警⑥速杀退化在关卡尺度坐实,v2(HP 290)压到 55%。注意:模拟者 = 理性策略近似(免费链式开格、无避雷推理),真人基线通关率应显著低于 55%——**目标带定在哪儿(30%?10%?)待 playtest 拍板,杠杆 = EnemyTable hp/atk 深度曲线(C3)**。 + +## 已知热点(playtest 观察项) + +- **基线难度体感**:基线(无面板加成)应"明显吃紧、可能打不过";如果基线就能轻松通关 → 敌数值仍偏弱(找 C3 杠杆:EnemyTable hp/atk 曲线);如果 +100% 攻击还打不过 → 奖励供给或恢复点数值偏弱。 +- **奖励点随机主动道具的体感**:roll 池 = 小刀 8 / 精工短刀 16 / 治疗包 12(等权);到手的刀是否改变了 Boss 战规划(囤刀 vs 即用)。 +- **恢复点(30% max_hp)时机**:位于战4 与 Boss 之间——是否制造"要不要省治疗包给 Boss"的决策。 +- **Boss 战节拍**:遗迹守卫环 [attack,attack,charge,heavy]——蓄力拍是否被玩家用作"安全挖掘拍";重击 14 点是否构成规划压力。 +- 节点进度条/奖励恢复覆盖层可读性;v1 遗留观测项(结算逐行归因、雾池体感、完美排雷可达性)继续有效。 + +## 2026-09-11 用户拍板(四条,已落地本切片) + +| # | 拍板 | 切片实现落点 | +|---|---|---| +| ① | 基础攻击恒存在:atk 域 [1,999]、0 拒绝加载、MVP=6;战斗段主角先手攻击顺位最前敌,attack_up 唯一吃修正 | `hero.tres` atk=6 · `hero_table.gd` 域校验 · B1 战斗段⓪先手(`hero_attack` 行);attack_up 经 `effect_pool_map → hero_offense` 活行 | +| ② | 奖励两类:即时 75%(当场兑付不进库存)/ 可选 25%(刀/治疗包入库走窗口);heal_pack 稀缺化 w6 p12 | `reward.tres` 行集 w75/25;B1 `_resolve_instant` | +| ③ | 库存关内延续:跨战斗不清,关卡末 level_clear 清空 | B1 `_battle_over` 不清库;v2 起由 C1 runner 在关卡末统一调用 | +| ④ | 道具窗口至多选一件 | B1 `submit_window_selection` 截断 `slice(0, 1)` | + +**文档对齐**:四条拍板的 GDD/registry 回写已由文档会话先行完成(b1/b2/b4 GDD、registry、systems-index、d2、game-concept、b3);本切片程序与文档逐项对齐核查通过(详见 session-state 条目 16)。语义注:本切片为参考实现,与文档冲突以 GDD/registry 为准。 + + +## 2026-09-12 试玩日志器 + 诊断转储(playtest 数据采集基建) + +**新增**:① **`PlaytestLogger`**(呈现层组件)——订阅 B1 新信号 `settlement_snapshot`(每结算末尾全量状态:关卡/深度/轮次/verdict/英雄/敌人/行动经济/板序号统计/buff/库存/本轮 facts)+ battle_finished/node_entered/level_finished,逐行写 **JSONL**:`user://logs/playtest__<时间戳>.jsonl`(每行一个完整状态,直接可喂 pandas);② **诊断转储按钮**(侧栏底部"🩺 诊断转储")——卡死时点击,全量核心状态(B1 相位机/计数器/facts、A2 棋盘、A3、B4、runner)+ 最近 200 结算行/100 操作环形缓冲 → `user://logs/diag_*.json`(行为探针 _dump_state 的产品化)。 + +**顺手修复(日志首秀即抓真 bug)**:_start_full_level 面板加成**双写叠加**——tune_panel.apply() 先写表(6→18),runner.set_hero_override 再以 18 为基线 ×3(→54)。修复 = 关卡模式单一应用路径(runner 写表),日志快照 atk 54→18 证实。**教训(规范 5.x 补充案例)**:同一加成的多应用路径必须单一事实源;JSONL 快照是数值回归的最低成本探测器。 + +**冒烟 103/103**(+7:快照信号/字段齐备/板序号/JSONL 解析/诊断文件)。日志目录:`%APPDATA%/Godot/app_userdata/DungeonSweeperVerticalSlice/logs/`。 + +## 2026-09-12 C3 GDD 落地 + 试玩采集清单 + +**落地**:C3 GDD(`design/gdd/c3-difficulty-curve.md`,solo 出稿未评审)的设计承诺落回切片——① **DifficultyTable 结构位**(schema + 空表 + ConfigDB 挂载 + B2.spawn 套乘钩子:行值快照前施加、越域 clamp 至 [0.1,10];MVP 空表零行为——AC-10 冒烟断言含注入/越域验证);② 切片调参登记(敌深度轴/板参数/供给参数)**转正为 MVP 默认曲线**——切片当前数值 = C3 GDD 背书的设计态,试玩即体验 C3 定稿难度。冒烟 96/96。 + +**试玩数据采集清单(反哺 C3 六个 OQ——试玩时请记录)**: +1. **OQ3 真人修正系数(首要)**:你无面板加成打第一关的通关/败北结果(模拟基线 80% 通关——真人预期显著更低,落差就是修正系数)。 +2. **OQ2 Boss 血量**:Boss 战实际打了多少轮、是否觉得血量压迫合理还是拖沓(模拟值 130HP 占比 0.81 越带)。 +3. **OQ6 轮内刷新**:你实际用了几次"弃图刷新"、是否觉得"挖-刷-挖"太顺滑(免结算=敌人不出手)。 +4. **OQ4 弃图倾向**:有没有"挖一半就想弃图刷 RAP"的冲动(模拟测不到真人策略)。 +5. **难度台阶可感知性**:D2 伏击兽/D4 Boss 相比 D0 蛆虫的变强是否有具体面孔(新敌种=新图像线索)。 +6. **奖励供给体感**:可选类道具(刀/治疗包)获取频率与囤积感受(供给控制器批后每关 ≈4.5–6.5 件)。 + +试玩方式:F5 → 「▶ 第一关 · 苔藓回廊」基线打一局 → 调整板 +100% 攻击再打一局(对照"数值提升→通关期望改善"的元进度体感)。 + + +## 2026-09-12 试玩批次 1 → v3 数值批(F7 流程实践) + +**试玩反馈(v2 数值)**:满血全通关(三策略均通)/ Boss 血量 OK 或可上调 / 刷新零使用 / 零弃图倾向 / 治疗包零使用 / DND 命名指令(地精/熊地精/食人魔酋长)。 + +**v3 数值批**:atk 4/4/6/4/7 → **7/5/9/6/9**(承伤主杠杆);D0 goblin 24 保持(20 会被首轮粉尘秒杀——冒烟旗回归断言抓获)、D1 群 13×2、D2 bugbear 40/9、D3 群 26×2、**Boss ogre_chief 150/9**(用户拍板可上调);L2 锚行同步。**DND 命名**:goblin 地精 → bugbear 熊地精(DND 原型即伏击手)→ ogre_chief 食人魔酋长;enemy_id 同步更名。 + +**v3 扫描对照 F5**:基线 **60%**(带 40–60% 上沿 ✓,v2 80%)· 败北 4/6 集中 Boss(压迫预期兑现)· +50%→87% / +100%→93% / +200%→100%(天赋曲线成立)· 承伤 90(v2 80)。C3 GDD 回写:F3 带 [0.5,1.1](OQ2 裁决)、OQ2/3/4/6 试玩读数、试玩批次记录。 + +**冒烟 103/103**(旗回归断言改用 Boss 行——D0 20HP 级被盲挖粉尘秒杀无法承载场景;DifficultyTable 断言对齐 v3 行值)。**下一批 = v3 真人试玩**(OQ3 定量/OQ2 终裁)。 + +## 2026-09-12 遭遇随机化(Roguelike 局感引入批——Peglin 对比报告落地项) + +**触发**:用户试玩反馈"关卡完全没有 Roguelike 的体现"→ Peglin 对比报告(`design/research/peglin-comparison-2026-09-12.md`)→ 用户拍板先做遭遇随机化(节点分支/多波/遗物协同后置)。 + +**设计**(C3 GDD Core Rule 8):EnemyTable 行 `unit_count` 升级为**区间语义** `[unit_count, unit_count_max]`(新字段可省略=定值,旧行零迁移);B1.begin_battle 实例化期按区间均匀骰,**RngService 新增 `encounter` 封闭流**(registry 接口漂移登记——挖掘决策域之外 P1 合规);**B2 零随机纪律保持**(spawn 收 override 即确定值);同种子全程可回放;新敌种首遇节点固定数量(教学位不加骰)。 + +**L1 区间**:地精 [1,2] · 地精一伙 [2,3](hp 13)· 熊地精固定(教学位)· 熊地精双雄 [2,3](hp 18)· Boss 固定——期望总量 ≈304(+8%),F6 带内。 + +**扫描(N=15)**:基线通关 **47%**(区间期望总量上移 +8% 的叠加效应——F5 带 40-60% 带内 ✓);+50% → 67%;败北分散(Boss×3/战1×4/战3×1)。**注意**:区间骰使难度分布更宽(运气成分进入遭遇层——Roguelike 体的核心),真人体感待批次 2。 + +**冒烟 149/149**(+46:区间双值出现/全域内/同种子可回放/D2 教学位固定)。 + +## 2026-09-12 试玩批次 1 → v3 数值批(F7 流程实践) + +## 2026-09-12 伤害类型 × 护甲克制批(用户拍板——三段伤害 + 标错惩罚落地) + +**触发**:设计会话(本日)定稿"怪物护甲类型(无甲/护甲A/护甲B)× 角色伤害三段构成(基础/伤害A/伤害B)"的克制对策系统(穿甲弹/高爆弹式三角)+ 标错旗随机惩罚。设定表经用户多轮审阅确认"没有问题"后落程序(GDD 正式修订另批进行——本批以表格结论为实现依据)。 + +**拍板结构 → 落点**: + +| 设定 | 实现落点 | +|---|---| +| 敌人护甲类型:无甲 / 护甲A / 护甲B | EnemyTable 新字段 `armor_type`({none,a,b},缺省 none 向后兼容)· 映射:goblin=none / bugbear=a / ogre_chief=b(教学梯度候选,provisional)· B2.spawn 行值快照入单位字典 | +| 伤害三段:①基础 ②伤害A(开格充能)③伤害B(标雷充能) | B1 战斗段主角先手攻击改三分量:基础 = atk×(1+attack_up)(**不吃克制**,attack_up 消费面不变);伤害A = 本轮开出的资格格计数 ×`damage_a_per_cell`(初始展开格不计——reward_eligible=false);伤害B = 首次标中雷 ×`damage_b_per_flag`(与 weaken 同键每雷实例一次去重——忘拔旗不重复充能);三分量各自 floor 后相加,hero_attack 行携带 base/dmg_a/dmg_b 分量(P2 归因) | +| 克制矩阵:特化 ×150 / 无甲 ×100 / 对位甲 ×50 | SettlementTable 新字段 `armor_effectiveness`({base,dmg_a,dmg_b}×{none,a,b} percent,缺项回退 100)——刀/AOE/清图奖励**恒定表值不吃克制**(道具=万能稳定伤害,暂定待拍板) | +| 伤害A 单轮封顶 | `damage_a_cap = 8`(与 ap_per_round 对齐——防连锁轮一次充能十几个格把特化甲怪一拍秒杀;0 = 不封顶) | +| 标错旗 → 随机轻惩罚三选一(时效 1 结算) | `_apply_miss_backlash()`:① `enrage` 暴走(敌 +25% 进 enemy_output 池)② 敌方伏击 +2 层(复用 ambush 行)③ `off_guard` 失衡(我方 +25% 进 hero_defense 池 = 多挨打);新 B3 行 ×2(dur=1 与 weaken/ambush 同款先兑付后衰减);沿用 `_flag_miss_seen` 每雷格实例一次去重 | +| 随机载体 | **零新流确定性派生**:(x×7 + y×13 + 板序号×5) mod 3——同种子可回放、不扰动既有三条封闭流取号序;正式侧候选 = RngService 新增封闭流(待拍板) | +| 呈现 | 敌人卡环点行常驻护甲标签(无甲/护甲A/护甲B——克制对策可读前提)· 结算明细主角攻击行拆"(基础 X + 开格 Y + 标雷 Z)" · 反噬效果行入明细流 · settlement_snapshot 增 armor/charge 字段(JSONL 数值分析面) | + +**数值全部 provisional 待拍板/调参**(登记于 enemy.tres / settlement.tres 注释):damage_a_per_cell=1 · damage_a_cap=8 · damage_b_per_flag=2 · 矩阵 150/100/50 · enrage/off_guard 25% · 伏击 +2 层 · 护甲映射 goblin/bugbear/ogre——C3 调参时按 F6 供给平衡式复算(三段充能是新增伤害供给,裸装败北/基线通关率两条冒烟路径已复验仍成立)。 + +**正式侧义务(GDD/registry 修订清单,待文档批)**:B2 GDD(EnemyTable armor_type 字段 + schema)、B1 GDD(F3 主角攻击三分量构成 + flag_miss 反噬编排 + 克制矩阵载体)、B3 GDD(六行 MVP 状态表 + source_class flag_miss 授予)、A2 OQ1(标错惩罚候选升级为三选一随机池 + 重复性口径=每雷格实例一次)、registry(enemy/settlement_table/status_effect 条目 + A3 条款②锚与条款③不等式重推导——标中旗收益从纯 weaken 变"weaken+伤害B 充能")、C3(数值 provisional 接管)。lore 侧:`design/research/lore-nest-metaphor-2026-09-12.md`(伤害A=地利/伤害B=封印暴露、反噬=封印失败——待新会话拍板后写回 game-concept)。 + +**验证**:冒烟 **161/161**(+12:护甲快照三档传播 / 克制矩阵三行表值 / hero_attack 行三分量字段〔全战斗回放 ×2〕/ 标错旗 flag_miss+反噬效果行 / 标中雷充能进主角攻击 / 反噬派生同种子回放)· ui_probe PASS · ui_tune_probe PASS · ui_click_probe(done=true、选用失效=0、问题=0)。 + +## 2026-09-12 战斗屏五区改造批(battle.md 落地——ui-programmer 批) + +**背景**:`design/ux/battle.md`(/ux-review APPROVED)+ `design/ux/interaction-patterns.md`(核心 8 pattern)落地到切片呈现层。**core/ 与 data/ 零改动**(本批纯 presentation/ + smoke/)。 + +**五区布局**(1280×800 基座):A 区 28px 顶栏关卡进度条(`level_strip_view.gd` 改造:暗金已过/奖励金当前高亮);B 区 252px 战斗画面(`battle_stage_view.gd` 新建 = `hero_card_view.gd` + 轮次·阶段指示 + 兑现演出区 + `enemy_card_view.gd` ×1–3〔顺位徽标/意图徽标常驻/环点拍数/伏击共享池/死亡灰化+碎裂残位禁瞬消〕+ 底部结算明细带);C 区 240px `hero_info_panel_view.gd`(直角细线数据框 + 修正来源角标 + RAP 点阵 + `status_slot_row.gd` ×4〔形状+色相双通道/层数角标/剩余结算点/悬停 tooltip 来源+语义+剩余/空态槽隐藏〕);D 区 `board_zone_view.gd`(`ap_bar_view.gd` 44px〔AP 点阵+停手居左;图例+弃图刷新居右——确认 chips 异位 pattern 7〕+ `board_view.gd` 改造〔悬停三语义自绘光标/目标格边框/BFS 路径蓝点预览/可挖域 8 向呼吸微光/奖励徽章 R₀ 域外/已开雾格 tint 残留/雾区全开一次性信号/去饱和 shader〕+ `toast_stack.gd` 2.5s 消退 + `board_fx_view.gd` 演出层)+ `desaturation.gdshader`(去饱和+金橙偏移、明度不变);E 区 280px `inventory_panel_view.gd`(常读列表固定槽高 + 原位窗口态 pattern 2/8:描边金光/标题切换/时机指示/usable_now 禁选+原因/跳过/政策注记;属性简述投影、来源不呈现)。**旧 `hud_view.gd` 删除**(HUDView 职责由 B/C/D 三区视图承接)。 + +**全屏 States 与模态**:`legend_overlay.gd`(图例,「迷雾 = 无雷盲区」恒在 + 状态槽空态说明)/ `settlement_review_overlay.gd`(明细回看)/ `battle_end_overlay.gd`(胜=金蜡封落于棋盘中央+判定行+回看入口;败=暮蓝 #5A6C8C+旗化光点收回)——统一模态登记序管理(`_open_modal` 打开时显式隐藏上方遮挡层,缺陷⑤纪律)。清图金粒喷泉+彩纸 / 完美排雷金光带+横幅 / 标中旗加重弹道 / Boss 紫赤影子爬棋盘 / 零合法板诚实 toast / 雾首遇·chord·非相邻一次性提示(每 run 去重,`ux_hint_shown` 遥测)。**发现并修复历史 quirk**:TunePanel 默认 `visible=true` 靠被不透明开始层遮蔽的树序巧合——已显式初始化 false(缺陷⑤家族)。 + +**遥测**(playtest_logger 增 ux 行族,呈现层观测不改 core):`ux_round_input_ratio`(原始输入:AP 消耗,快照驱动)/ `ux_round_exit{path_tag}`(快照 round_facts.path——呈现层只订 B1 的 ADR 纪律下唯一合法源)/ `ux_refresh_attempt{result, round_refresh_index}` / `ux_window_choice` / `ux_reject{source, sub_reason}` / `ux_hint_shown{hint_id}`。 + +**探针**:ui_probe 增五区构建断言 + L2 雾图段(雾首遇提示/雾区连通分量);ui_click_probe 增刷新确认链/图例开关/tooltip 触发/模态遮挡断言(图例/回看/窗口态/终局)/窗口期去饱和断言/首格可点计时(进入战斗屏口径 133–151ms ≤500ms AC;场景冷启动 ~700ms 为脚本首载编译域,观测项)——既有全部断言保留。** Waiting 刷新代理已按 OQ12 退役**:UI 刷新按钮仅挖掘期 Active 受理(presenter 门禁),原探针"窗口期点刷新"行为由 Active 确认链测试替代。 + +**回归**:smoke_all 149/149 · ui_probe PASS · ui_tune_probe PASS · ui_click_probe PASS(PROBE_SEED 7/11/23/99/42/77 六种子 done=true、选用失效=0、问题=0)。 + +**占位登记**:敌人意图视觉映射按 enemy_id 程序化占位(资产定稿归 B5);D区演出(喷泉/彩纸/弹道/影子)全部程序化占位;CJK 字体依赖系统回退(Windows 可用,随包字体归 D1 正式化硬性前置——ADR-0003);金蜡封/暮蓝氛围为色块占位级。 + +## 2026-09-12 呈现层 code-review 修复批(/code-review CHANGES REQUIRED → 全项清偿) + +**触发**:全切片 53 脚本 ~9400 行 /code-review(CLAUDE.md 代码规范 + ADR-0001/0003/0004 对照),裁决 CHANGES REQUIRED(7 项必改)。core/ 12 文件与 smoke 断言面整体健康;病灶集中于 tune_panel 封装破口、main.gd 结构回涨、三处呈现契约错位。 + +**BLOCKING 修复**: + +| # | 缺陷 | 修复 | +|---|---|---| +| 1 | **TunePanel 视图写核心表**(`apply()` 直写 `_db.hero.atk/.max_hp`)+ `run_simulation` 内 `load("res://smoke/...")` 驱动 core——面板模拟按 pct² 口径、模拟后表值残留污染真实对局(runner 把放大值当基线再乘) | tune_panel 重构为纯视图(`apply_requested`/`reset_requested`/`simulate_requested` 信号外发 + setter 注入);加成落表收口组合根唯一路径 `_apply_hero_override`(每次从 `_ready` 捕获基线重算,幂等禁叠加);模拟编排拆 `tune_sim_controller.gd`(先复位基线再移交);报告文案归 `HudPresenter.tune_sim_report` | +| 2 | **意图徽标双翻译**:presenter 先把 beat key 译中文投影、EnemyCardView 再按英文 key 匹配 → 色相/重击"▼"通道永久失效;`BEAT_NAMES` 两处重复 | presenter 直传 B2 原键(attack/charge/heavy);名/色/标记渲染映射唯一定义处 = EnemyCardView(set_intent 契约头注成文) | +| 3 | **C 区 RAP 死分支**:`_refresh_economy` 从不投影 `a3.rap`,"RAP 滚动归零"反馈(A3 D1 义务)不可达;视图靠解析自身 label 文本当状态 | presenter 持续投影 `a3.rap`;`hero_info_panel_view` 改 `_last_rap` 成员存储,删除 `_rap_value()` 文本解析 | +| 4 | **smoke_all 恒真断言**(`check(true, "全部计数在域内")`——越域也 PASS) | 改带分布标签的真实域内断言 | + +**结构/封装修复**: + +- **main.gd 761 → 667 行**(评审记录基线 433 行,400 行审视线):节点构建拆 `ui_assembler.gd`(只 new/add_child/布局,零接线零核心写);模态层级状态机拆 `modal_stack.gd`(缺陷⑤纪律载体);战斗流程叙事文案拆 `battle_narrator.gd`(兑现 main 头注"不拼呈现字符串");模拟编排拆 `tune_sim_controller.gd`。剩余行数 = ADR-0001 规定的接线 + 模式流转 + 核心写调用唯一出口(再压需移动 ADR 规定职责,登记为 D1 正式化批候审)。 +- **level_runner core↔core 接线 lambda → 方法 Callable ×4**(ADR-0001 静态规则 S6——lambda 捕获 self 延长 RefCounted 生命周期/成环)。 +- **B1 新增 `diagnostic_snapshot()` 诊断观测面**:组合根诊断转储与 click_probe 停滞诊断全部改走成文 API,零下划线私有字段直读(此前直读 8 个)。 +- **信号处理体去 lambda 化**:main 三处多行/单行 lambda(inter 层继续/单场入口/库存拒绝)改命名方法 + `bind`(规范 4.1)。 +- 顺带:`can_request_refresh()` 断行格式修复(原单行内嵌 tab 不可读);`_build_inter_overlay` 死参 `parent_root` 删除;`_start_battle` 重复 `_start_overlay.visible = false` 去重;调试两层入口改由 `_start_battle` 统一取加成后满血(原传参在加成前取表值)。 + +**探针同步(缺陷回归断言随契约更新——3.3)**:ui_tune_probe 重写——应用/重置走真实按钮信号路径(`press_apply_for_probe` 接缝,ADR-0004"合成输入走真实翻译出口"口径);**新增两条回归断言**:⑦模拟后 hero 表保持基线(无残留)⑧应用加成 + 启动完整关卡后 runner 写表值 = 基线 × 单次加成(12/75;旧缺陷表现 24/112 在此被抓获);ui_click_probe 停滞状态与诊断转储改走 `diagnostic_snapshot()`。 + +**验证**:smoke_all **141/141** · ui_probe PASS · ui_tune_probe PASS(含新回归⑦⑧)· ui_click_probe 六种子(7/11/23/99/42/77)全 PASS(done=true、选用失效=0、问题=0)。新增 4 脚本 `.uid` 经 headless 编辑器扫描生成(语言纪律⑤)。 + +**登记待办(非本批范围)**:自动玩家策略四份复制收口(评审 W-3);schema 溯源行补齐(W-9);S1–S8 静态扫描脚本化后 S1 自建 RNG 豁免口径登记(W-1);views 转发层信号契约头注补齐(W-2)。 + +## 2026-09-13 封装收口批(代码架构审查 E1–E5/O1–O5/P3 修复——ADR-0009 候选项的切片侧预收口) + +**触发**:/architecture-review 改编批——以 architecture.md + ADR-0001~0005 + code-conventions 为基线做代码组织/封装专项审查(报告:`docs/architecture/code-architecture-review-2026-09-13.md`,Verdict: CONCERNS),用户拍板 P1+P2+P3 全修(P2 中涉接口漂移两项另出设计稿待审)。 + +**核心封装修复(P1)**: + +| # | 项 | 修复 | +|---|---|---| +| E1 | **B1 协作对象全公开 → 呈现层链式穿透**(`b1.b2.hero`、`b1.a3.phase` 式层层下钻 main×18 / presenter×15 处) | a1/b2/b3/b4 私有化(a2/a3 保留公有 = 架构文档 API Boundaries 的 D1 契约面);新增 `presentation_snapshot()`(载荷 schema 头注成文)/`is_battle_live()`/`stock_count()`/`query_reward_identity()` 四个成文只读出口——diagnostic_snapshot 同款模式 | +| E2 | **A2 状态字典公开且被视图绕过查询面直读**(board_view:278/303 直读 `.revealed`,头注自称"只读查询面"自相矛盾) | `board/revealed/flags` 私有化;新增 `current_board()`/`reveal_record()` 查询面;B1/组合根/探针全部改走命名面 | +| E3 | **B2 裸字典容器公开**(`hero/enemies` 可被任意写;presenter 为死亡残位直读原始数组) | 私有化 + `hero_unit()`/`all_enemies()`(含死亡残位,句柄语义与 query_alive 同构)投影面;presenter 残位渲染需求收进 API | +| E5 | **"只读"字段仅靠纪律**(phase/ap/rap/last_round_end/master_seed 架构文档定只读,实际公开可变) | getter-only 属性 + 下划线后备字段(外部读法 `a3.phase` 不变,写入仅剩方法通道——GDScript 层面机制化) | + +**组织修复(P1/P2/P3)**: + +- **O1/O2 调试模式抽离**:main.gd 调试分支(单场/连续两层/HP 携带/共享 B4)拆 `presentation/debug_session.gd`——调试语义单一实现点(O2 双实现漂移收口);组合根保留接线(battle_created 先于 begin_battle 发出,ADR-0001 接线唯一归组合根 + 缺陷①时序义务)与终局 UI 决策。ui_probe 抓获初版时序缺陷(battle_ready 才接手 b1 → 雾首遇提示丢失)——即缺陷①同款,battle_created 接手修复。 +- **O3 DamageMath 抽取**:B1 伤害公式族(F2 乘区/充能分量,零状态纯算术)拆 `core/damage_math.gd`(规范 1.2 工具函数挂宿主类);board_generator 同一合法板重复调用两次 measure_inferability 合并为一次(全库最贵求解器)。 +- **P3 清债**:头注溯源补齐 ×4(board_data/config_db/rng_service/reward_supply_controller——规范 1.4);B4 grant/grant_clear/stock/use 失败返回统一 `false`→`null`(E6 消费方 `is Dictionary` 判定天然兼容);equivalence_supply 表行 duplicate 防配置源泄漏;load()→preload/类名直引 ×3(main logger/runner、tune_sim autoplay)。 + +**加固(基线既有噪声根治)**:main.gd `_on_board_action` 的清图演出判定在 settle_batch 同步重入(结算→battle_over→关卡终局置空 b1)后仍摸 `b1.a2` → SCRIPT ERROR(基线 click_probe 每跑必现)——补 `b1 != null` 守卫,探针 stderr 归零。 + +**验证**:冒烟 **141/141**(与基线一致)· ui_probe PASS · ui_tune_probe PASS · ui_click_probe PASS(done=true、选用失效=0、问题=0,**SCRIPT ERROR 0**——优于基线)· 新增脚本 .uid 经 headless 编辑器扫描生成(语言纪律⑤)· presentation 层残留穿透引用 grep 归零(呈现面调用 19 处全部为命名 API)。 + +**登记待办(P2 设计稿待审——见审查报告 §4)**:E4 `db.hero` 表值运行时改写参数化(涉 B2.spawn 契约 = 接口漂移,须 GDD/registry 修订批);O4 自动玩家策略四份复制收口(与既有待办 W-3 合并)。 + +## 2026-09-13 P2 收口批(E4 表值参数化 + O4 自动玩家单一实现——用户拍板两项均做) + +**E4(db.hero 表值运行时改写 → 参数显式传递)**: +- `B2.spawn` 增 `atk_override/max_hp_override` 参数(-1 = 表值兜底;沿用 unit_count_override 先例;EC16 越界守卫改对生效上限判); +- `B1.begin_battle` encounter 增 `hero_atk/hero_max_hp` 字段(缺省表值——冒烟直呼路径零改动),战斗段 `_hero_atk` 快照替代 `db.hero.atk` 读取; +- `LevelRunner`:`start_level` 表写删除、生效值进 encounter、`restore_hero_base` 只清百分比(表值不可变后无基线可恢复)、`_base_*` 字段删除(直接读恒定的表); +- main:`_apply_hero_override/_scaled_override` 表写路径废除 → `_debug_hero_override()` 生效值供给 Callable(面板百分比现算经 encounter 入战斗);`_on_tune_apply/_on_tune_simulate` 去复位步骤;`_db_base_*` 字段删除。 +- **收益**:双写叠加(6→18→54)与双基线同步整类隐患随表写移除;D2「单一事实源」原则机制化(表值运行期不可变)。 +- **登记义务**:registry architecture.yaml 新增 `hero_override_parameter_passing` 条目(ADR-0009 候选;B2.spawn 签名 +2 / B1 encounter +2 = 接口漂移,正式侧 B1/B2 GDD 修订随 ADR-0009 批)。 +- **探针语义升级**:ui_tune_probe ⑦「表值残留」→「hero 表运行期不可变」(reintroduce 表写即 FAIL);⑧ 双写叠加回归改为完整关卡路径 + 呈现快照单次生效断言(12/75;旧缺陷形态 24/112 仍被抓获)。ui_probe 的 `_start_single` 语义注记:调试单场恒基线(入口清零滑条——原行为,非缺陷)。 + +**O4(自动玩家策略四份复制 → 单一实现,合并既有待办 W-3)**: +- 新增 `smoke/auto_player.gd`:`choose_dig`(最近可挖格)+ `window_selection`(治疗优先于刀;差异参数化 heal_hp_div 2/3 · round_gate 0/3 · knife_compare best/sum——四调用点原语义逐一保留:smoke_all 3/3/best · monte_carlo 2/0/sum · dump_battle 3/3/sum · level_autoplay 2/3/best); +- 四消费方删除各自的 `_choose_dig`/窗口策略块改走单一实现;click_probe 的 `level_autoplay._choose_dig` 引用同步改指 AutoPlayer; +- **等价性证明**:smoke_all 同种子回放断言(步骤流逐行相同)重构后保持全绿。 + +**验证**:冒烟 **141/141** · ui_probe PASS · ui_tune_probe PASS(含 E4 新断言)· ui_click_probe PASS(done=true、选用失效=0、问题=0)。 + + +## 2026-09-13 零承伤根因分割探针 + playtest-09-12 Bug #1–#3 处置批(报告 Top3 授权任务) + +**触发**:playtest-2026-09-12-designer 报告 Top 3 Priorities(探针分割 → Bug 建档修复 → 多种子复扫基线);数值拍板与设计变更不在授权范围(待拍板清单见文末)。 + +**① 零承伤根因分割(新探针 `smoke/damage_probe.gd`——boss/level 两模式)**: +- **快照盲区发现**:settlement_snapshot 取在衰减段之后,时效 1 的 defense_up 当轮授予当轮衰减——真人局 JSONL `buffs[]` 恒空 ≠ 池为空(报告悬置的直接原因)。探针改从结算行(effect 行 pool_value)重建战斗段时点池值,池归属经 settlement.effect_pool_map 表驱动(零字面量)。 +- **Run A(fresh 池零挖掘裸验)**:Boss 攻击拍 d_tick = atk.total 全额兑现(attack 9/9 ×2 · charge 0 设计语义 · heavy 18/18),6 轮击倒英雄(defeat)——**「Boss/敌攻击链缺陷」否证**;拍名轮转与真人局逐拍一致(attack→attack→charge→heavy→attack)。 +- **Run B(自动挖掘墙行为)**:池 -30 → d_tick 21→14、池 -60 → 残余 ×0.40,墙轮承伤恒 >0;表值判定墙最深 -60 > pool_clamp_lo -100 = **归零不可达(09-13 调参批在当前数据上重证)**。 +- **结论**:真人局 12/12 轮零承伤 = 旧数据 defense_up -50×2 = 池触 clamp 下限 ×0(设计语义)——任何 atk 曲线乘在 ×0 上(原报「v3 主杠杆乘在 ×0 上」坐实)。报告 Bug #2 = 设计问题非 bug。 + +**② Bug #1–#3 建档与处置(BUG-0002/0003/0004,`production/qa/bugs/`,编号自 BUG-0001 顺延)**: +- **BUG-0002(报告 #1 浅深度空遭遇)——Rejected as coded**:seed 42 当前构建全关回放复现同款遥测签名(d0 spawn=1 / d1 spawn=3 + round-1 victory + unit_count 0)但实为**首轮全灭**(英雄爆发 + 爆裂粉尘 instant_aoe 全歼低 HP 敌群;与 20:41 局 d1 双地精各剩 1 HP 逐点吻合);遭遇骰数学下限 ≥1(B1 roll_lo)+ B2.spawn count<1 fail-fast 双闸,100 场扫描零越域。**快照盲区修复**:settlement_snapshot 增 `spawn_count`(实例化总数含死亡残位)——unit_count=0 时「全灭 vs 空场」可判别,同类误读机制化排除。 +- **BUG-0003(报告 #2 Boss 攻击零兑现)——Rejected as coded**:根因见①;候选改法(clamp 收紧 / 乘法衰减 / 重击拍穿防)列待设计批。 +- **BUG-0004(报告 #3 grant_node_active 绕过 _supply.notify_granted)——Fixed**:stock 受理成功后补入账(与 grant() 同一「发放即获取」口径);计数器战斗域清零语义不变 = **零战斗数值影响**,纯计数口径修复(报告 Top3 #3 的上报义务执行)。 +- **回归断言(代码规范 3.3)**:smoke_all +7——快照 spawn_count 字段与等值断言;遭遇全深度 × 12 种子全域内扫描(60 场 spawn ≥1 且 ∈[unit_count, unit_count_max]);`_smoke_attack_chain_fresh_pool`(fresh 池非蓄力拍 d_tick == atk.total 全额兑现——攻击链健康钉死,未来真缺陷在此被抓);`_smoke_supply` ⑦ 节点奖励发放即入账。 + +**③ 多种子关卡复扫(Q1 调参批基线——level_monte_carlo 增 F4 两列「普通承伤/场% · Boss承伤%」:逐战斗承伤切分 = round 回卷分段 + 末段 Boss 判定(须抵达 Boss 节点))**: +- N=30 × 10 档(种子 4200–4229;F7 切片样本量口径)。**结论:全档位越带**——F5 基线通关率 **17%**(带 40–60%)· +100% 档 43%(中天赋 ≥95%)· +200% 档 63%(高天赋 100%);F4 普通场承伤 **35.1–43.1%/场**(带 ≤20%)、Boss 场 **71.3–134.9%**(带 ≤50%)(hp+0 档;hp+100 档普通 13.9–22.6 带内 / Boss 35.8–76.7 仍多数越带)。拆墙(-50→-30 且权重 24→16)与 v3 atk 曲线叠加后,难度由严重不足摆向**严重过剩**。**本批未动任何数值**(敌 hp 曲线与奖励供给禁独立调参——registry 预警⑥联动义务;杠杆方向列待拍板,本基线 = F7 流程的测量输入)。 + +**验证**:冒烟 **148/148**(141 既有 + 7 新回归)· 三探针 PASS(ui_probe / ui_click_probe 6 种子 done=true、选用失效=0、问题=0 / ui_tune_probe)· damage_probe boss 模式 7/7 + level 模式(种子 42/43/44)4/4 全 PASS。 + +**待拍板/待设计批清单(本会话零数值变更、零 GDD 变更)**: +1. **难度杠杆重拍板**(F4/F5 全档位越带)——候选:enemy atk 曲线回撤部分 v3 增量 / defense_up 权重回升 / heal 供给联动——归 C3 按 F7 流程(本基线为测量输入); +2. **墙语义候选改法**(clamp 收紧至 -60/-70 / stack 乘法衰减 / heavy 拍穿防)→ C3 + /propagate-design-change(连带 B1 池 clamp 语义 + B3 GDD "-50" 文字同步); +3. **供给量重拍板**:「每战斗首件保证」的总量语义(候选:关卡域首件)→ C3(BUG-0004 已封计数漏洞,关卡供给账目可完整观测); +4. **浅深度首轮跳战可玩性**(autoplay/真人 d0-d3 round-1 全灭跳过战斗过程)→ C3 结合 F4 复验数据裁决。 + + +## 2026-09-13 拍板落地批(四项拍板——难度杠杆/墙语义/供给量/跳战) + +**拍板结果**(用户四问裁决):①难度主杠杆 = **先真人试玩再拍**(F5 真人修正系数标定优先于数值调整);②墙语义 = **维持现状 + 归零护栏**;③供给量语义 = **先观测再拍**;④跳战 = **并入难度复测再议**。本批零玩法数值变更。 + +**落地变更**: +- smoke_all 新增 `_smoke_wall_zero_guard`(表级不变式:`2×defense_up.magnitude > pool_clamp_lo`)——BUG-0003 事故类(叠满触池底 = ×0 全额免伤)永久免疫:任何未来 magnitude/clamp 调参重新造出 ×0 墙即冒烟红灯,强制显式重审; +- level_monte_carlo 增「**跳战率**」列(普通战斗 round-1 即胜占比——拍板④观测面,此后每次复测自动携带);`_damage_segments` 升级为逐段 {承伤, r1_win} 结构; +- defense_scan 输出标签修正:「墙轮承伤(应为 0)」→「(合法——-30 批墙帽 ×0.40)」(旧 -50 数据时代的期望文案过时清理)。 + +**跳战率基线(N=30,拍板④"再议"数据)**:基线 **8%** · +50% 13% · +100% 24% · +150% 27% · +200% 38%(普通场 round-1 全灭占比)。**seed 42(试玩固定种子)为高跳战轨迹**(autoplay d0–d3 四场全部 round-1 全灭)——真人局 d0–d3 全跳的体验含显著种子成分;基线 talent 下跳战是少数派,拍板④"复测再议"的数据前提成立。其余各列与 Q1 基线逐值一致(collect_rows=true 开启不改变行为——确定性交叉验证)。 + +**验证**:冒烟 **149/149**(+1 护栏)。 + +**下一步(用户侧)**:开一局真人试玩(任意种子;JSONL 自动落 user://logs/)。观测清单(服务拍板①③复议):通关与否 + 败北节点(F5 修正系数 = 真人通关率 ÷ 模拟 17%)· 普通场掉血体感(对照 ≤20% 带)· Boss 战撑过几轮 · d0–d3 是否发生跳战 · 治疗包是否出现缺血时刻与库存残留(拍板③数据)。事后由会话分析 JSONL 出修正系数。 + +## 2026-09-14 完美策略档测量批(OQ7 技能轴上界仪——用户拍板"增加完美策略档测试验证",零玩法数值变更) + +**背景**:用户(扫雷强玩家)指出"完全正确开图/标雷、零踩雷 ⇒ 存活无压力"——现行扫描只有弱策略基线档(不标旗/邻格盲挖/朴素选道具),技能轴不可测;registry `calibration_gap`(真人显著强于模拟者)此前只有方向无机制。本批 = 测量工具批。 + +**落地变更**: +- `auto_player.gd` 增完美策略求解器(`perfect_action`/`_deduce`):single + subset 推理不动点(A1 F1 `measure_inferability` 同族规则集,**去 safe_set oracle**——约束未知邻格含雷格,比生成期闸保守);**零盲挖**(只挖已证安全格 = 构造保证零踩雷,扫描末断言兜底违例 quit(1))· **已证雷格全标旗**(旗先于挖——轮末 weaken + 伤害B 首次充能,雷上旗不阻连锁)· 卡死且资源可 → `b1.player_request_refresh()` 轮内刷新 · 否则停手(残 AP 1:1 入 RAP)。输入只经 A2 查询面(reveal_record/is_revealed/has_flag),**禁读棋盘雷区真值**——测推理上界,非全知作弊。core 层零改动; +- `window_selection` 增 `heal_no_waste` 选项(缺口 < 治疗量不用;完美档启用)+ 完美档轮次门解除(round_gate=0); +- `level_autoplay.gd` 增 `policy` 参数(默认 "normal"——tune_sim_controller/defense_scan/level_debug 既有调用零改动);完美档驱动 `_autoplay_battle_perfect`(move/dig/flag 走 A2 settle_batch、refresh 走 B1 编排、连续拒绝 ≥3 停手防死循环);st 增 `mine_hits`/`refreshes` 计数(mine_hit 证据行埋点); +- `level_monte_carlo.gd` 增第 3 CLI 参 `[policy]`(normal/perfect/both,缺省 normal 兼容既有文档命令)+ 策略/踩雷/刷新列;脚注 F5 带文案同步 [30,50] 带锚定稿。 + +**完美档基线(N=30 · 种子 4200–4229 · 耗时 597.6s)**:**0 加成通关率 100%**(弱策略基线档 60%)、通关剩余 HP 均 **47.4/50**、整关承伤均 3.5(p90 18.0)、普通场承伤 0.5%(带 ≤20%)、Boss 场 5.3%(带 ≤50%)、踩雷 0.00/局(断言 ✓)、刷新 0.23/局、跳战率 20%。+50%~+200% 全档位恒 100% 满血通关(+200% 承伤均 0.4)——**技能轴上界完全碾压现行曲线:F5 带与 F6 S/K 区间对完美档零分辨力**(区间模型无策略水平轴的结构性证实;calibration_gap 获得机制解释与定量上界)。 + +**验证**:冒烟 **149/149** 全绿(默认行为零变更——弱策略档既有断言全通过)。 + +**待拍板**:① 0 加成完美档通关率的目标带(现行 [30,50] 按弱策略口径定义——是否改为按完美档口径验收?);② 杠杆选择(已预登记:r_boss 走廊 180–207 / 群战上探 / decay 0.6;本批新增候选:"上限掺运气"系——供给量值随机化(power 区间化)/ 承伤硬地板——均待完美档复测验证效果)。 + +## 2026-09-14 伤害量值区间批(用户拍板②——"上限掺运气"两步全落地 + 完美档无效性实测,宽度 provisional) + +**拍板**:验收口径改完美策略档(0 加成通关 ≤40%);杠杆 = 伤害值区间随机(上阶/下阶,范围内 roll);覆盖面 = 奖励侧五行先行、不足扩敌方侧(分步授权已用尽)。roll 时机 = 身份预分配批(E14 快照口径不变);中点恒等现值(A3 条款①当量锚不漂移);宽度 provisional 归测量迭代。 + +**落地变更**: +- **第一步·奖励侧五行**(reward.tres + reward_table.gd schema + reward_economy.gd):heal_minor [2,6] / knife_t1 [5,11] / heal_pack [6,18] / knife_adv [10,22] / clear_blast [25,55]——power_min/power_max 可选配对字段(缺省定值行向后兼容,armor_type 先例);schema 校验 = 成对/buff 禁止/中点锚;roll 在 roll_board 身份批内**追加取号**(既有取号序零扰动)+ grant_node_active 同路径;快照 power = roll 值,B1/呈现零改动,授予期零随机纪律不变; +- **第二步·敌方攻击侧**(enemy.tres 六行 + enemy_table.gd schema + unit_container.spawn + battle_orchestrator):atk [min,max] = 行值 ±3 provisional(d0 7[4,10] / d1 5[2,8] / d2 9[6,12] / d3 6[3,9] / Boss 9[6,12] / L2 7[4,10]);roll = 遭遇骰同流(encounter 恰一 scope,数量骰先、atk 骰后)spawn 期**整战持久**——每拍抖动否决(数字可读性锚),B2 零随机纪律保持(收 override 即确定值);spawn 签名 +1 参数(接口漂移登记,ADR-0009 同批); +- smoke_all 新增 `_smoke_power_interval`(4 断言:五行中点锚/roll 域内含清图槽/定值行零外溢/节点路径域内)。 + +**实测(N=30,种子 4200–4229)**: +- 奖励侧单独:完美档 0 加成 **100%**(承伤 3.5——与区间前逐值一致);基线档 17%(不变); +- 敌方侧叠加:完美档 0 加成仍 **100%**(承伤 3.5→3.9 / p90 18→19——方差真实流动但被血缓冲全吞);剩余 HP 均 47.4 不变; +- **验收结论:≤40% 线用数值区间杠杆族到不了**——完美玩家结构性优势(零踩雷 = 零直伤 + 全标旗 = weaken −65% 顶格 + 快杀 = 敌出手次数最少)不受值域方差影响。三选待裁决:①承伤硬地板(每结算不可减免平伤——S_min 压缩,B1/B2 域);②weaken 饱和/递减语义(拆"全标旗顶格"基石,B3/B1 域);③极端宽度(数值可用但毁基线档与可读性锚——建议否决)。**本批区间机制保留**(对普通玩家方差体验有效,中点锚不伤当量)。 + +**验证**:冒烟 **154/149+5 全绿**(区间批 5 新断言 + 既有全部回归含遭遇骰分布/回放)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd new file mode 100644 index 00000000..c7c02aea --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd @@ -0,0 +1,163 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# A3 行动经济:被动账本(AP 硬重置 / 统一价目 / 三路径结束仲裁 cleared > stopped > ap_exhausted / +# RAP 池战斗域 / 双发射源之 stopped 直发)。相位域 = {Idle, Active, Waiting}。 +# 实现 GDD:design/gdd/a3-action-economy.md(系统 ID A3——registry 追溯锚,命名规范见 CLAUDE.md) +class_name ActionEconomy +extends RefCounted + +signal round_end_stopped(payload: Dictionary) # 双发射源之停手路径(A3 直发;payload 与 A2 棋盘路径同构) + +const PHASE_IDLE := "Idle" +const PHASE_ACTIVE := "Active" +const PHASE_WAITING := "Waiting" + +var db +# 相位与账本只读出口(E5 封装收口 2026-09-13):外部读取经 getter-only 属性(架构文档 +# "ap/rap/phase 只读" 的机制化),写入只经 notify_* / request_* 方法通道。 +var _phase: String = PHASE_IDLE +var phase: String: + get: return _phase +var _ap := 0 +var ap: int: + get: return _ap +var _rap := 0 +var rap: int: + get: return _rap +var _last_round_end: Dictionary = {} # {path_tag, continuation_hint}——A2 组包 round_end 的合法数据源 +var last_round_end: Dictionary: + get: return _last_round_end +var _settlement_consumed := false # AC-14 门:notify_settlement_done 消费后才受理 round_start +var _next_round_mode := "" # 调试观测量(同板续轮 / 换新图) + + +func _init(db_ref) -> void: + db = db_ref + + +func _cost(kind: String) -> int: + var ae = db.action_economy + match kind: + "dig": + return ae.dig_ap_cost + "flag_place": + return ae.flag_place_ap_cost + "flag_pull": + return ae.flag_pull_ap_cost + "move": + return ae.move_ap_cost + "chain": + return ae.chain_ap_cost + assert(false, "未知动作价目: " + kind) + return 0 + + +# 通用扣减(A2 用于旗动作;挖握走 request_dig——cleared 仲裁需要 clears_map 参数) +func try_spend(action_kind: String) -> bool: + if phase != PHASE_ACTIVE: + _reject("phase") + return false + return _spend_and_arbitrate(action_kind, false) + + +# A2 挖掘专用:chain_size = 连锁附加格数(不含首格);clears_map = 本次动作后全图安全格开完 +func request_dig(chain_size: int, clears_map: bool) -> bool: + if phase != PHASE_ACTIVE: + _reject("phase") + return false + return _spend_and_arbitrate("dig", clears_map) + + +func _spend_and_arbitrate(action_kind: String, clears_map: bool) -> bool: + var cost := _cost(action_kind) + if _ap < cost: + _reject("resource_face") + return false + _ap -= cost + if clears_map: + # cleared 胜出(同帧仲裁 cleared > ap_exhausted;剩余 AP 作废不入 RAP——EC7/EC10) + _finish_round("cleared", "new_board") + elif _ap == 0: + # AP 归 0 瞬间轮次结束——不存在 0-AP 操作窗口 + _finish_round("ap_exhausted", "same_board") + return true + + +# 停手:剩余 AP 1:1 转存 RAP;A3 直发 stopped 路径 round_end(双发射源) +func request_stop() -> bool: + if phase != PHASE_ACTIVE: + _reject("phase") + return false + _rap += _ap + _ap = 0 + _finish_round("stopped", "same_board") + round_end_stopped.emit({"path_tag": "stopped", "continuation_hint": "same_board"}) + return true + + +# 玩家发起的刷新(B1 在 Waiting 编排期代理调用;禁止 RAP 满自动刷) +func request_refresh() -> bool: + if phase != PHASE_WAITING: + _reject("phase") + return false + if _rap < db.action_economy.rap_refresh_cost: + return false # EC13 资源不足拒绝——合法相位内资源拒绝,非编排错误 + _rap -= db.action_economy.rap_refresh_cost + return true + + +# 轮内刷新(2026-09-12 方案 A 用户拍板):Active 相位受理——仅扣 RAP,相位与 AP 均不动。 +# 刷新不打断轮次、不触发结算(敌方时间税语义变更已登记 GDD 修订义务);换图编排归 B1 +# (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 D1 发起)纪律不变。 +func request_refresh_active() -> bool: + if phase != PHASE_ACTIVE: + _reject("phase") + return false + if _rap < db.action_economy.rap_refresh_cost: + return false # EC13 资源不足拒绝——合法相位内资源拒绝,非编排错误 + _rap -= db.action_economy.rap_refresh_cost + return true + + +# B1 回发:结算完成(仅记录编排意图;phase 停留 Waiting——AC-14 门不被绕过) +func notify_settlement_done(next_round_mode: String) -> bool: + if phase != PHASE_WAITING: + _reject("phase") + return false + _settlement_consumed = true + _next_round_mode = next_round_mode + return true + + +func notify_round_start() -> bool: + if phase == PHASE_IDLE: + pass # 战斗首轮唯一出口 + elif phase == PHASE_WAITING and _settlement_consumed: + pass + else: + _reject("phase") + return false + _ap = db.action_economy.ap_per_round # 每轮硬重置(RAP 不动——唯一跨轮携带量) + _phase = PHASE_ACTIVE + _settlement_consumed = false + return true + + +func notify_combat_end() -> bool: + if phase != PHASE_WAITING: + _reject("phase") + return false + _phase = PHASE_IDLE + _rap = 0 # RAP 战斗域清零(用户拍板;D1 可见反馈义务) + _ap = 0 + return true + + +func _finish_round(path_tag: String, continuation_hint: String) -> void: + _last_round_end = {"path_tag": path_tag, "continuation_hint": continuation_hint} + _phase = PHASE_WAITING + + +func _reject(source: String) -> void: + push_warning("[A3] 动作被拒(%s 相位守卫/资源)" % source if source == "phase" else "[A3] 资源不足拒绝") diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd.uid new file mode 100644 index 00000000..62e7a029 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd.uid @@ -0,0 +1 @@ +uid://hxhl617bokyi diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd new file mode 100644 index 00000000..7df196b0 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd @@ -0,0 +1,792 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# B1 战斗结算编排中枢:轮次生命周期唯一 owner · 段序骨架(授予→窗口→增益/减益→道具→战斗→衰减→判定)· +# 13 类行协议可回放步骤流(零随机零 await)· 高光双落(完美排雷 ×2 / 标中旗 highlight)· 伏击预截流 · +# Waiting 相位结算触发(不依赖事件到达序)。 +# 切片登记:① 事件总线载体 = 直连信号(实现期 ADR 项,provisional)② 多次刷新 EC15 简化为每 Waiting 至多 +# 一次(UI 禁二次发起;多刷契约义务不触)③ 效果行协议 = 授予段逐 grant 行 + 增益/减益段续存实例组合行。 +# 实现 GDD:design/gdd/b1-settlement.md(系统 ID B1——registry 追溯锚,命名规范见 CLAUDE.md) +class_name BattleOrchestrator +extends RefCounted + +signal step_row(row: Dictionary) +signal window_opened(items: Array) +signal battle_finished(payload: Dictionary) # {result, hero_hp, remaining_items}——hero 在 despawn 前捕获(呈现层销毁后读状态的时序防护) +signal round_started(round_index: int) +signal board_changed() +signal settlement_snapshot(snap: Dictionary) # 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析) + +var db +var rng +# E1 封装收口(2026-09-13):协作对象 a1/b2/b3/b4 私有化——跨层读取一律经 presentation_snapshot()/ +# diagnostic_snapshot() 成文出口(呈现层链式穿透 b1._b2.hero_unit() 的根因收口)。 +# a2/a3 保持公有 = 架构文档 API Boundaries 的 D1 消费面(意图提交 settle_batch + compute_move_path +# 只读;ap/rap/相位读取)——非本文档登记的契约面,勿增新用途。 +var _a1: BoardGenerator +var a2: MinesweeperRules +var a3: ActionEconomy +var _b2: UnitContainer +var _b3: StatusContainer +var _b4: RewardEconomy + +var encounter: Dictionary = {} +var round_index := 0 +var battle_result := "" # "" / "victory" / "defeat" + +var _facts_reveals: Array = [] +var _facts_flags: Array = [] +var _facts_path := "" +var _settling := false +var _awaiting_window := false +var _window_items: Array = [] +var _refresh_used_this_waiting := false +var _refresh_used_this_round := 0 # 每轮刷新计数(Active+Waiting 合计,兜底上限 = ActionEconomyTable.refresh_max_per_round) +var _board_seq := 0 # 板序号(本战斗第几张板——快照归因用) +var _flag_consumed := {} # 兑付资格以雷格实例为键(板级;换图重置) +var _flag_miss_seen := {} # 标旗未中行去重(每雷格实例一次) +var _perfect_hit_true := 0 # 板级完美排雷计数(install 重置) +var _perfect_hit_false := 0 +var _enemy_cap := 0 # EnemyTable 行值快照(伏击 cap) +var _m_amb := 0 # StatusEffectTable.ambush.magnitude 快照 +var _hero_atk := 0 # 主角攻击基值快照(encounter.hero_atk;E4 收口——表值运行期不可变) +var _charge_dmg_a := 0 # 伤害A 充能:本轮开出的资格格计数(轮末结算消费后随 facts 重置) +var _charge_dmg_b := 0 # 伤害B 充能:本轮首次标中雷计数 ×damage_b_per_flag(每雷实例一次) + + +func _init(db_ref, rng_ref, shared_b4 = null) -> void: + db = db_ref + rng = rng_ref + _a1 = BoardGenerator.new() + a3 = ActionEconomy.new(db) + a2 = MinesweeperRules.new(db, a3) + _b2 = UnitContainer.new(db) + _b3 = StatusContainer.new(db) + # 库存关卡域(2026-09-11 关内延续):连续关卡模式注入共享 B4 容器,库存跨战斗延续 + _b4 = shared_b4 if shared_b4 != null else RewardEconomy.new(db, rng) + a2.cell_revealed.connect(_on_cell_revealed) + a2.flag_judged.connect(_on_flag_judged) + a2.round_end_board.connect(_on_round_end) + a3.round_end_stopped.connect(_on_round_end) + + +# ---- 战斗开始序列(编排专章①:门 → B3 → B2 → install+roll → A3)---- + +func begin_battle(enc: Dictionary) -> bool: + if round_index != 0: + push_warning("[B1] begin_battle 重复受理") + return false + encounter = enc + db.set_reload_gate(true) + if not _b3.notify_battle_start(): + _fatal("B3 开域被拒(编排违例)") + return false + _b4.notify_battle_start() # 可选类供给控制器战斗域清零(2026-09-11 供给控制器批——首件保证按战重置) + # 遭遇骰(C3 GDD Core Rule 8——2026-09-12 用户拍板):行有 unit_count_max 时按 [unit_count, max] 均匀骰; + # 2026-09-14 敌方攻击区间批扩展:行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值 + # (每拍抖动否决——数字可读性锚;中点 = 行值)。encounter 封闭流恰一 scope(数量骰先、atk 骰后); + # 挖掘决策域之外——P1 合规;B2 零随机纪律保持(收 override 即确定值)。 + var erow_pre: Dictionary = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth)) + var roll_lo: int = int(erow_pre.get("unit_count", 1)) + var roll_hi: int = int(erow_pre.get("unit_count_max", roll_lo)) + var needs_count: bool = roll_hi > roll_lo + var needs_atk: bool = erow_pre.has("atk_min") + var rolled := roll_lo + var rolled_atk := -1 + if needs_count or needs_atk: + var estream: RandomNumberGenerator = rng.open_stream("encounter") + if needs_count: + rolled = roll_lo + estream.randi_range(0, roll_hi - roll_lo) + if needs_atk: + rolled_atk = int(erow_pre.atk_min) + estream.randi_range(0, int(erow_pre.atk_max) - int(erow_pre.atk_min)) + # E4 收口:加成经 encounter 显式携带(hero_atk/hero_max_hp;缺省 = 表值——冒烟/工具直呼路径) + _hero_atk = int(enc.get("hero_atk", int(db.hero.atk))) + if not _b2.spawn(int(enc.level_index), int(enc.node_depth), int(enc.hero_hp), rolled, + _hero_atk, int(enc.get("hero_max_hp", -1)), rolled_atk): + _fatal("B2 spawn 被拒") + return false + var erow: Dictionary = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth)) + _enemy_cap = int(erow.get("advantage_per_round_cap", 0)) + _m_amb = int(db.status_effect_row("ambush").get("magnitude", 0)) + if not _install_new_board(): + return false + _reset_round_facts() # 首板 install 的初始展开事件不入轮 facts(reward_eligible 恒 false 的双保险) + if not a3.notify_round_start(): + _fatal("A3 首轮 notify_round_start 被拒") + return false + round_index = 1 + round_started.emit(round_index) + return true + + +func _install_new_board(during_round := false) -> bool: + var stream: RandomNumberGenerator = rng.open_stream("board_gen") + var board: BoardData = _a1.generate(int(encounter.level_index), int(encounter.node_depth), stream, db) + if board == null: + return false + var res: Dictionary = a2.install_board(board, during_round) + if not res.accepted: + _fatal("A2 install 拒绝(不变式违例): %s" % str(res)) + return false + # B4 义务⑥:roll_board 恰一次(install 后、挖掘前);资格格 = S ∖ R₀ + var r0 := {} + for c in board.compute_r0(): + r0[c] = true + var eligible: Array = [] + for c in board.safe_cells(): + if r0.has(c): + continue + eligible.append({"cell": c, "fog": board.variant_ids(c).size() > 0}) + if not _b4.roll_board(eligible): + _fatal("B4 roll_board 被拒") + return false + _flag_consumed.clear() # 换图新雷 = 新实例,资格自然重置(A3 Rule 8) + _flag_miss_seen.clear() + _perfect_hit_true = 0 # 板级完美计数随 install 重置 + _perfect_hit_false = 0 + _board_seq += 1 + board_changed.emit() + return true + + +# ---- 事件缓冲(轮 facts 收集;结算触发 = Waiting 相位编排点,不依赖到达序)---- + +func _on_cell_revealed(evt: Dictionary) -> void: + # 轮 facts 缓冲不设相位守卫——轮末最后一挖的揭示事件在 request_dig 翻相之后才到(事件序: + # cell_revealed 批 → flag_judged 批 → round_end),phase==Active 守卫会丢最后一挖的事实; + # initial 通道事件(install 初始展开)reward_eligible=false,进入授予段被资格检查自然跳过。 + if battle_result == "": + if evt.kind == "safe" and bool(evt.get("reward_eligible", false)): + # 身份快照随缓冲捕获(批次隔离——轮内刷新后 mapping 已换批,授予必须用缓冲期身份, + # 防旧图已挖格在轮末错拿新图同坐标身份) + var snap = _b4.query_full_snapshot(evt.cell) + if snap != null: + evt = evt.duplicate() + evt["reward_snapshot"] = snap + _facts_reveals.append(evt) + + +func _on_flag_judged(evt: Dictionary) -> void: + _facts_flags.append(evt) + + +func _on_round_end(_payload: Dictionary) -> void: + _facts_path = String(_payload.get("path_tag", "")) + _maybe_settle() + + +func _maybe_settle() -> void: + if _settling or battle_result != "": + return + if a3.phase != a3.PHASE_WAITING: + return + _settle() + + +# ---- 每轮结算序列(段序骨架——Core Rule 4)---- + +func _settle() -> void: + _settling = true + var ri := round_index + # ① 授予段(含 grant_clear 兑付) + var mine_cells: Array = [] + for evt in _facts_reveals: + if evt.kind == "mine": + mine_cells.append(evt.cell) + elif evt.kind == "safe" and evt.reward_eligible: + _charge_dmg_a += 1 # 伤害A 充能:探索本身计费,与该格奖励是否兑付成功无关(初始展开格 reward_eligible=false 不计) + _grant_cell(evt.cell, ri, "safe_cell", evt.get("reward_snapshot")) + # reward_eligible=false(initial 通道)不入库——B1 义务②(A3 EC5 委派闭环) + for evt in _facts_flags: + _judge_flag(evt, ri) + if not mine_cells.is_empty(): + _convert_ambush(mine_cells, ri) + if _facts_path == "cleared": + _grant_clear_reward(ri) + # ② 道具使用窗口(授予段后、增益/减益段前——当轮拾取当轮可用) + _window_items = _assemble_window() + if not _window_items.is_empty(): + _awaiting_window = true + window_opened.emit(_window_items) + _settling = false + return # 窗口选择序列 = 结算回放输入事件 + _finish_settlement([]) + + +func _assemble_window() -> Array: + # 窗口数据组装 = B4 query_inventory 投影 + B1 侧守卫预判 usable_now(B4 零跨系统读不破) + var out: Array = [] + for inst in _b4.query_inventory(): + if inst.status != "InStock": + continue + var usable := true + if inst.category == "heal_pack": + usable = int(_b2.hero_unit().hp) < int(_b2.hero_unit().max_hp) # 满血治疗包禁选 + out.append({ + "index": inst.index, + "item_id": inst.item_id, + "display_name": inst.display_name, + "category": inst.category, + "power": inst.power, + "source_cell": inst.source_cell, + "round_index": inst.round_index, + "usable_now": usable, + }) + return out + + +func submit_window_selection(selection: Array) -> void: + if not _awaiting_window: + push_warning("[B1] 无窗口期的选择提交(编排违例)") + return + _awaiting_window = false + _settling = true + if selection.size() > 1: + push_warning("[B1] 窗口至多选用一件(2026-09-11 用户拍板——避免结算复杂度)——超出部分忽略") + _finish_settlement(selection.slice(0, 1)) + + +func _finish_settlement(selection: Array) -> void: + var ri := round_index + # ③ 增益/减益段(续存实例组合行;本结算新授予的已在授予段逐 grant 出行——B3 义务⑦) + _emit_composition_effects("hero", ri) + _emit_composition_effects("enemy", ri) + # ④ 道具段(窗口选定序逐件生效;逐件动态守卫) + for idx in selection: + _apply_selected_item(int(idx), ri) + # ⑤ 战斗段(调用序钉死 query_attack → apply_damage → advance;hero hp=0 短路) + _combat_segment(ri) + # ⑥ 衰减段(恰一次恒执行含末轮——先兑付后衰减) + var k: int = _b3.decay_round() + _emit_row("decay", "", Vector2i(-1, -1), ri, "状态衰减", k, {}, "") + # ⑦ 判定段(防御性优先级 hero 先判——双死结构不可达的纵深防御) + var verdict := "continue" + if int(_b2.hero_unit().get("hp", 0)) <= 0: + verdict = "defeat" + elif _b2.query_alive("enemy").is_empty(): + verdict = "victory" + _emit_settlement_snapshot(verdict) # 全量状态快照(despawn 前组装——终局含 result) + if verdict == "defeat": + _emit_row("verdict", "", Vector2i(-1, -1), ri, "战斗失败", 0, {}, "") + _battle_over("defeat") + return + if verdict == "victory": + _emit_row("verdict", "", Vector2i(-1, -1), ri, "战斗胜利", 1, {}, "") + _battle_over("victory") + return + _emit_row("verdict", "", Vector2i(-1, -1), ri, "下一轮次", 0, {}, "") + _exit_distribution() + _settling = false + + +# ---- 授予段 helpers ---- + +# snap_override 非空 = 冻结快照授予(轮内刷新批——缓冲期身份,绕过现行 mapping/_granted) +func _grant_cell(cell: Vector2i, ri: int, source_class: String, snap_override = null) -> void: + var snap = snap_override if snap_override != null else _b4.grant(cell) + if not (snap is Dictionary): + return + if snap.category == "buff": + var ok: bool = _b3.apply_effect(snap.effect_id, {"source_class": source_class, "origin_cell": cell, "round_index": ri}) + if ok: + var row: Dictionary = db.status_effect_row(snap.effect_id) + var pool := _pool_effective(snap.effect_id) + _emit_row("effect", source_class, cell, ri, String(row.display_name), int(row.magnitude), {"effect_id": snap.effect_id, "pool_value": pool}) + else: + _emit_row("reward_ineffective", source_class, cell, ri, "奖励未生效(满层)", 0, {"batch_key": String(snap.effect_id)}) + elif _resolve_instant(snap, source_class, cell, ri): + pass # 即时类:授予段当场兑付(不进库存、零决策——2026-09-11 两类奖励裁决) + else: + _b4.stock(snap, cell, ri) + _emit_row("item", source_class, cell, ri, "%s 入库" % snap.display_name, int(snap.power), {}) + + +# 即时类当场兑付(2026-09-11 两类奖励裁决):instant_aoe = 全体存活敌各 power 点; +# instant_heal = 主角 +power(clamp,满血 → "奖励未生效"行)。返回 false = 非即时类(道具类走库存) +func _resolve_instant(snap: Dictionary, source_class: String, cell: Vector2i, ri: int, suffix: String = "", highlight: String = "") -> bool: + if snap.category == "instant_aoe": + var alive: Array = _b2.query_alive("enemy") + if alive.is_empty(): + _emit_row("reward_ineffective", source_class, cell, ri, "%s 未生效(无存活敌人)%s" % [snap.display_name, suffix], 0, {"batch_key": "instant_aoe"}, highlight) + return true + for u in alive: + _b2.apply_damage(u, int(snap.power)) # 恒定表值,逐个;受击即死——死亡即不动手 + _emit_row("damage", source_class, cell, ri, "%s → 全体敌人 ×%d%s" % [snap.display_name, alive.size(), suffix], int(snap.power), {"target": "all_enemies", "count": alive.size()}, highlight) + return true + if snap.category == "instant_heal": + if int(_b2.hero_unit().hp) >= int(_b2.hero_unit().max_hp): + _emit_row("reward_ineffective", source_class, cell, ri, "%s 未生效(满血)%s" % [snap.display_name, suffix], 0, {"batch_key": "instant_heal"}, highlight) + return true + var healed: int = _b2.heal(_b2.hero_unit(), int(snap.power)) # clamp 归 B1 + _emit_row("heal", source_class, cell, ri, "%s 回复%s" % [snap.display_name, suffix], healed, {"target": "hero"}, highlight) + return true + return false + + +func _judge_flag(evt: Dictionary, ri: int) -> void: + var cell: Vector2i = evt.cell + if evt.hit: + if _flag_consumed.has(cell): + return # 去重跳过(资格以雷格实例为键——A2 诚实重复上报的对侧);零明细行 + _flag_consumed[cell] = true + _perfect_hit_true += 1 + _charge_dmg_b += int(db.settlement.damage_b_per_flag) # 伤害B 充能:与 weaken 消费同键(每雷实例一次——忘拔旗不重复充能) + var ok: bool = _b3.apply_effect("weaken", {"source_class": "flag_hit", "origin_cell": cell, "round_index": ri}) + if ok: + var row: Dictionary = db.status_effect_row("weaken") + _emit_row("effect", "flag_hit", cell, ri, String(row.display_name), int(row.magnitude), {"effect_id": "weaken", "pool_value": _pool_effective("weaken")}, "flag_hit") + else: + _emit_row("reward_ineffective", "flag_hit", cell, ri, "奖励未生效(满层)", 0, {"batch_key": "weaken"}) + else: + _perfect_hit_false += 1 + if _flag_miss_seen.has(cell): + return # 每雷格实例一次去重(防逐轮惩罚轰炸) + _flag_miss_seen[cell] = true + var hl := "" + if _perfect_hit_false == 1: + hl = "perfect_broken" # 本板完美谓词已破的归因锚 + _emit_row("flag_miss", "flag_miss", cell, ri, "标旗未中", 0, {}, hl) + _apply_miss_backlash(cell, ri) + + +# 标错惩罚(2026-09-12 用户拍板:随机给敌方一个增伤/buff,或我方 debuff;时效 1 结算——用户拍板"短点,比如 1 回合")。 +# 随机载体 = 坐标×板序号确定性派生(零新流、同种子可回放;正式侧候选 = RngService 新增封闭流——待拍板, +# 见 design/research/lore-nest-metaphor-2026-09-12.md ③ 边界提醒)。三池项:enrage(敌增伤)/ ambush×2(敌 buff)/ off_guard(我方 debuff)。 +func _apply_miss_backlash(cell: Vector2i, ri: int) -> void: + var kind: int = (absi(cell.x) * 7 + absi(cell.y) * 13 + _board_seq * 5) % 3 + var src: Dictionary = {"source_class": "flag_miss", "origin_cell": cell, "round_index": ri} + match kind: + 0: + if _b3.apply_effect("enrage", src): + var row: Dictionary = db.status_effect_row("enrage") + _emit_row("effect", "flag_miss", cell, ri, "反噬·%s(敌方)" % String(row.display_name), int(row.magnitude), {"effect_id": "enrage", "pool_value": _pool_effective("enrage")}) + else: + _emit_row("reward_ineffective", "flag_miss", cell, ri, "反噬未生效(满层)", 0, {"batch_key": "enrage"}) + 1: + var granted := 0 + for i in range(2): + if _b3.apply_effect("ambush", src): + granted += 1 + if granted > 0: + _emit_row("effect", "flag_miss", cell, ri, "反噬·敌方伏击 +%d 层" % granted, granted, {"effect_id": "ambush", "pool_value": _b3.pool_layers("ambush") * _m_amb}) + else: + _emit_row("reward_ineffective", "flag_miss", cell, ri, "反噬未生效(满层)", 0, {"batch_key": "ambush"}) + 2: + if _b3.apply_effect("off_guard", src): + var row2: Dictionary = db.status_effect_row("off_guard") + _emit_row("effect", "flag_miss", cell, ri, "反噬·%s(我方)" % String(row2.display_name), int(row2.magnitude), {"effect_id": "off_guard", "pool_value": _pool_effective("off_guard")}) + else: + _emit_row("reward_ineffective", "flag_miss", cell, ri, "反噬未生效(满层)", 0, {"batch_key": "off_guard"}) + + +func _convert_ambush(mine_cells: Array, ri: int) -> void: + # B2 义务②⑥:预截流计划面 → 前 granted 颗逐颗转化(事件规范序);每颗雷恰一次 + var plan: Dictionary = _b2.ambush_grant_plan(mine_cells.size(), _enemy_cap) + for i in range(plan.granted): + var cell: Vector2i = mine_cells[i] + var ok: bool = _b3.apply_effect("ambush", {"source_class": "mine_hit", "origin_cell": cell, "round_index": ri}) + if ok: + var layers := _b3.pool_layers("ambush") + _emit_row("effect", "mine_hit", cell, ri, "伏击", _m_amb, {"effect_id": "ambush", "pool_value": layers * _m_amb}) + for i in range(plan.granted, plan.granted + plan.overflow): + var cell: Vector2i = mine_cells[i] + _emit_row("overflow", "mine_hit", cell, ri, "伏击溢出未生效", 0, {"batch_key": "ambush_overflow"}) + + +func _grant_clear_reward(ri: int) -> void: + var snap = _b4.grant_clear() + if not (snap is Dictionary): + return + var perfect: bool = _perfect_hit_false == 0 and _perfect_hit_true >= 1 + # 首份兑付 + var first_ok := _grant_clear_identity(snap, ri) + # 完美排雷:真实双份交付(第二份走既有授予通道;grant_clear 本身仍恰一次) + var second_ok := false + if perfect and first_ok: + if snap.category == "buff": + second_ok = _b3.apply_effect(snap.effect_id, {"source_class": "clear_reward", "origin_cell": Vector2i(-1, -1), "round_index": ri}) + if second_ok: + var row: Dictionary = db.status_effect_row(snap.effect_id) + _emit_row("effect", "clear_reward", Vector2i(-1, -1), ri, "%s(完美第二份)" % String(row.display_name), int(row.magnitude), {"effect_id": snap.effect_id, "pool_value": _pool_effective(snap.effect_id)}) + else: + var s2 = _b4.stock(snap, Vector2i(-1, -1), ri) + second_ok = s2 is Dictionary + if second_ok: + _emit_row("item", "clear_reward", Vector2i(-1, -1), ri, "%s 入库(完美第二份)" % snap.display_name, int(snap.power), {}) + if perfect and first_ok: + # 高光行 value = 实际有效兑付份数(禁记名义值;首份满层零兑付 → 降级不发) + _emit_row("highlight", "clear_reward", Vector2i(-1, -1), ri, "完美排雷!清图奖励 ×2", (2 if second_ok else 1), {}, "perfect_sweep") + + +func _grant_clear_identity(snap: Dictionary, ri: int) -> bool: + if snap.category == "buff": + var ok: bool = _b3.apply_effect(snap.effect_id, {"source_class": "clear_reward", "origin_cell": Vector2i(-1, -1), "round_index": ri}) + if ok: + var row: Dictionary = db.status_effect_row(snap.effect_id) + _emit_row("effect", "clear_reward", Vector2i(-1, -1), ri, String(row.display_name), int(row.magnitude), {"effect_id": snap.effect_id, "pool_value": _pool_effective(snap.effect_id)}, "clear_reward_h2") + return true + _emit_row("reward_ineffective", "clear_reward", Vector2i(-1, -1), ri, "清图奖励未生效(满层)", 0, {"batch_key": snap.effect_id}) + return false + if _resolve_instant(snap, "clear_reward", Vector2i(-1, -1), ri, "(清图奖励)", "clear_reward_h2"): + return true + var s = _b4.stock(snap, Vector2i(-1, -1), ri) + if not (s is Dictionary): + return false + _emit_row("item", "clear_reward", Vector2i(-1, -1), ri, "%s 入库(清图奖励)" % snap.display_name, int(snap.power), {}, "clear_reward_h2") + return true + + +# ---- 窗口 / 道具段 ---- + +func _apply_selected_item(window_index: int, ri: int) -> void: + if window_index < 0 or window_index >= _window_items.size(): + return + var item: Dictionary = _window_items[window_index] + if item.category == "heal_pack": + # 逐件动态守卫:前件推满后本件拦截(不烧包——B1 不调 use) + if int(_b2.hero_unit().hp) >= int(_b2.hero_unit().max_hp): + _emit_row("use_rejected", "item", item.source_cell, ri, "%s 被拒(满血)" % item.display_name, 0, {}) + return + var receipt = _b4.use(item.index) + if not (receipt is Dictionary): + return + var healed: int = _b2.heal(_b2.hero_unit(), receipt.power) # clamp 归 B1 + _emit_row("heal", "item", receipt.source_cell, ri, "%s 回复" % item.display_name, healed, {"target": "hero"}) + else: + # 伤害道具:恒定表值不吃修正;零效果判定读 B2 存活集(照常消耗——E10) + if _b2.query_alive("enemy").is_empty(): + var receipt0 = _b4.use(item.index) + if not (receipt0 is Dictionary): + return + _emit_row("consumed_ineffective", "item", receipt0.source_cell, ri, "已消耗未生效(无存活敌人)", 0, {}) + return + var receipt = _b4.use(item.index) + if not (receipt is Dictionary): + return + var target = _b2.default_target() + var killed_before: bool = not target.alive + _b2.apply_damage(target, receipt.power) + var killed: bool = not killed_before and not target.alive + _emit_row("damage", "item", receipt.source_cell, ri, "%s 命中" % item.display_name, receipt.power, {"target": target.display_name, "killed": killed}) + + +# ---- 战斗段 ---- + +func _combat_segment(ri: int) -> void: + var L := _b3.pool_layers("ambush") # 授予段后的当轮值;池共享不消耗 + # ⓪ 主角先手攻击(基础攻击恒存在——2026-09-11 用户裁决;先于敌方行动)。 + # 三段伤害构成(2026-09-12 用户拍板):基础(atk×(1+attack_up),不吃克制)+ 伤害A(开格充能×克制)+ 伤害B(标雷充能×克制), + # 按目标护甲类型分别乘克制系数后 floor 相加;attack_up 仍只乘基础攻击(刀/AOE 恒定表值不吃修正不变)。 + var hero_target = _b2.default_target() + if hero_target != null: + var armor: String = String(hero_target.get("armor_type", "none")) + var up := clampi(_pool_effective("attack_up"), int(db.settlement.pool_clamp_lo), int(db.settlement.pool_clamp_hi)) + var base := maxi(0, int(floor(float(_hero_atk) * (100.0 + float(up)) / 100.0))) + var comp_a := _charge_damage_component("dmg_a", _charge_dmg_a, armor) + var comp_b := _charge_damage_component("dmg_b", _charge_dmg_b, armor) + var d_hero: int = base + comp_a + comp_b + if d_hero > 0: + _b2.apply_damage(hero_target, d_hero) + _emit_row("damage", "hero_attack", Vector2i(-1, -1), ri, "主角攻击", d_hero, {"target": hero_target.display_name, "killed": not hero_target.alive, "base": base, "dmg_a": comp_a, "dmg_b": comp_b}) + for u in _b2.query_alive("enemy"): + if int(_b2.hero_unit().hp) <= 0: + break # hero hp=0 立即短路(死尸挨打行是归因噪音——用户拍板) + var beat: String = _b2.query_beat(u) + var atk: Dictionary = _b2.query_attack(u, L, _m_amb) # 调用序①兑付读值 + if beat == "charge": + _emit_row("charge", "enemy_action", Vector2i(-1, -1), ri, "%s 蓄力(本轮踩雷白喂)" % u.display_name, 0, {"target": "hero"}) + else: + var d_tick := _resolve_damage(int(atk.total)) + if d_tick <= 0: + _emit_row("damage", "enemy_action", Vector2i(-1, -1), ri, "%s 攻击(被减伤归零)" % u.display_name, 0, {"target": "hero", "baseline": int(atk.baseline), "attach": int(atk.attach)}) + else: + _b2.apply_damage(_b2.hero_unit(), d_tick) # 调用序②受击上报 + _emit_row("damage", "enemy_action", Vector2i(-1, -1), ri, "%s 攻击" % u.display_name, d_tick, {"target": "hero", "baseline": int(atk.baseline), "attach": int(atk.attach)}) + _b2.advance(u) # 调用序③推进——每结算每存活敌恰一次(hero 不在推进域) + + +# F2 乘区:池内加算 → clamp [-100,+100] → 池间乘算 → floor(单一取整规则)——算术归 DamageMath(O3) +func _resolve_damage(d_raw: int) -> int: + var p_hero := _clamped_pool("hero_defense") + var p_enemy := _clamped_pool("enemy_output") + return DamageMath.pool_damage(d_raw, p_hero, p_enemy) + + +# 伤害A/B 充能分量:充能 × 每单位值 × 克制 percent → floor(伤害A 先套单轮充能上限——防连锁轮失控) +func _charge_damage_component(component: String, count: int, armor: String) -> int: + var per: int = int(db.settlement.damage_a_per_cell) if component == "dmg_a" else int(db.settlement.damage_b_per_flag) + var charge := count + if component == "dmg_a" and int(db.settlement.damage_a_cap) > 0: + charge = mini(charge, int(db.settlement.damage_a_cap)) + return DamageMath.charge_component(charge, per, _armor_effectiveness(component, armor)) + + +# 克制矩阵读值(SettlementTable.armor_effectiveness:{分量: {护甲: percent}}——缺项回退 100 中性) +func _armor_effectiveness(component: String, armor: String) -> int: + var tbl: Dictionary = db.settlement.armor_effectiveness + var row: Dictionary = tbl.get(component, {}) + return int(row.get(armor, 100)) + + +func _clamped_pool(pool: String) -> int: + var total := 0 + for eff in _b3.query_active("hero") + _b3.query_active("enemy"): + var row: Dictionary = db.status_effect_row(eff.effect_id) + if row.is_empty(): + continue + if String(db.settlement.effect_pool_map.get(eff.effect_id, "")) == pool and String(row.unit) == "percent": + total += eff.effective_value # 池内加算(L×m 原样——越域不 clamp 由本处收口) + return clampi(total, int(db.settlement.pool_clamp_lo), int(db.settlement.pool_clamp_hi)) + + +func _pool_effective(effect_id: String) -> int: + var side := "hero" + var row: Dictionary = db.status_effect_row(effect_id) + if not row.is_empty(): + side = String(row.target_side) + for eff in _b3.query_active(side): + if eff.effect_id == effect_id: + return eff.effective_value + return 0 + + +func _emit_composition_effects(side: String, ri: int) -> void: + for eff in _b3.query_active(side): + if eff.round_index < ri: + # 续存实例(前轮授予)——每结算兑付一次的可见面(合并实例只持首授归因——B3 义务⑦) + _emit_row("effect", eff.source_class, eff.origin_cell, ri, eff.display_name, eff.snap_magnitude, {"effect_id": eff.effect_id, "pool_value": eff.effective_value}) + + +# ---- 结算快照(playtest 数值分析数据面——每次结算全量状态;运行时零决策读取)---- + +func _emit_settlement_snapshot(verdict: String) -> void: + var enemies: Array = [] + for u in _b2.query_alive("enemy"): + enemies.append({"id": String(u.enemy_id), "hp": int(u.hp), "max_hp": int(u.max_hp), "atk": int(u.atk), "beat": query_beat_of(u), "armor": String(u.get("armor_type", "none"))}) + var buffs: Array = [] + for eff in _b3.query_active("hero"): + buffs.append({"id": eff.effect_id, "side": "hero", "layers": eff.layers, "left": eff.remaining_settlements}) + for eff in _b3.query_active("enemy"): + buffs.append({"id": eff.effect_id, "side": "enemy", "layers": eff.layers, "left": eff.remaining_settlements}) + var mines_hit := 0 + var safe_n := 0 + for evt in _facts_reveals: + if evt.kind == "mine": + mines_hit += 1 + elif evt.kind == "safe": + safe_n += 1 + var board_stats := {"seq": _board_seq} + if a2.current_board() != null: + board_stats["w"] = a2.current_board().width + board_stats["h"] = a2.current_board().height + board_stats["revealed"] = a2.revealed_cells().size() + board_stats["flags"] = a2.flag_count() + settlement_snapshot.emit({ + "kind": "settlement", + "level": int(encounter.get("level_index", 0)), + "depth": int(encounter.get("node_depth", 0)), + "round": round_index, + "verdict": verdict, + "unit_count": enemies.size(), + "spawn_count": _b2.all_enemies().size(), # 本战斗实例化总数(含死亡残位)——unit_count=0 时区分「首轮全灭」vs「空场」(playtest-2026-09-12 报告 Bug#1 误读教训) + "hero": {"hp": int(_b2.hero_unit().get("hp", 0)), "max_hp": int(_b2.hero_unit().get("max_hp", 0)), "atk": int(_b2.hero_unit().get("atk", 0))}, + "economy": {"phase": String(a3.phase), "ap": int(a3.ap), "rap": int(a3.rap), "refresh_used": _refresh_used_this_round}, + "board": board_stats, + "enemies": enemies, + "buffs": buffs, + "ambush_layers": _b3.pool_layers("ambush"), + "inventory_in_stock": _b4.in_stock_count(), + "round_facts": {"path": _facts_path, "safe": safe_n, "mines": mines_hit, "flag_events": _facts_flags.size(), "charge_dmg_a": _charge_dmg_a, "charge_dmg_b": _charge_dmg_b}, + }) + + +func query_beat_of(u: Dictionary) -> String: + return _b2.query_beat(u) + + +# ---- 出口分发与战斗结束序列 ---- + +func _exit_distribution() -> void: + var mode := "same_board" + if _facts_path == "cleared": + mode = "new_board" # cleared 轮恒走 new_board(契约义务) + elif _refresh_used_this_waiting: + mode = "new_board" # stopped/ap_exhausted 轮:仅 Waiting 期玩家刷新意图达成时换图(违约 fail-fast 的对侧) + if mode == "new_board": + _b4.retire_mapping() + a2.retire_board() + if not _install_new_board(): + _fatal("换图编排失败") + return + a3.notify_settlement_done(mode) + a3.notify_round_start() + _reset_round_facts() + _refresh_used_this_waiting = false + _refresh_used_this_round = 0 + round_index += 1 + round_started.emit(round_index) + + +# 呈现层查询面(HUD 刷新按钮可用性) +func can_request_refresh() -> bool: + return battle_result == "" \ + and _refresh_used_this_round < int(db.action_economy.refresh_max_per_round) \ + and (a3.phase == a3.PHASE_ACTIVE or a3.phase == a3.PHASE_WAITING) \ + and (a3.phase != a3.PHASE_WAITING or not _refresh_used_this_waiting) \ + and a3.rap >= int(db.action_economy.rap_refresh_cost) + + +# 玩家经 D1 发起、B1 代理调用(禁止 RAP 满自动刷)。 +# 轮内刷新(2026-09-12 方案 A 用户拍板):Active 相位立即换图继续挖(RAP -8;AP 保留、相位不动、 +# 不触发结算);Waiting 相位保持现状(登记意图,exit_distribution 换图)。每轮上限 = 表值(两相位合计)。 +func player_request_refresh() -> bool: + if not can_request_refresh(): + return false + if a3.phase == a3.PHASE_ACTIVE: + if not a3.request_refresh_active(): + return false + _refresh_used_this_round += 1 + _b4.retire_mapping() # 旧图批次退役(B4 Ready→retired;E6 新批重置前提) + a2.retire_board(true) # 旧板退役(轮内通道) + return _install_new_board(true) # 当场换图——失败即编排违例 fail-fast + if not a3.request_refresh(): + return false + _refresh_used_this_round += 1 + _refresh_used_this_waiting = true + return true + + +func _battle_over(result: String) -> void: + battle_result = result + # 库存关卡域(2026-09-11 关内延续):battle_over 不再清库存——跨战斗延续,关卡末经 level_clear() 清空; + # 映射随战斗结束退役(下一场战斗 = 新板新 scope 批,roll_board 相位守卫的前提) + var hero_hp_left := int(_b2.hero_unit().get("hp", 0)) # despawn 前捕获(信号载荷供呈现层"HP 带入下一层") + _b4.retire_mapping() + _b2.despawn_all() + _b3.notify_battle_end() + a3.notify_combat_end() # → Idle,RAP 清零 + db.set_reload_gate(false) + _reset_round_facts() + battle_finished.emit({"result": result, "hero_hp": hero_hp_left, "remaining_items": _b4.in_stock_count()}) + + +# ---- 呈现状态面(E1 封装收口 2026-09-13):呈现层 / headless 自动玩家的唯一读取出口—— +# 协作对象(_b2/_b3/_b4)私有化后,跨层读取一律经本快照或下列命名面(diagnostic_snapshot 同款模式, +# 禁再链式穿透 b1.b2.hero 一类路径)。载荷 schema 成文(code-conventions 2.1): +# round / battle_result / phase / ap / rap / can_request_refresh / hero{hp,max_hp,atk,alive} / +# enemies[{order,display_name,hp,max_hp,enemy_id,armor_type,beat,ring_size,ring_index,ambush_layers,alive}] / +# ambush_layers / hero_effects[](B3 query_active 投影) / inventory[](B4 query_inventory 投影) / in_stock +func presentation_snapshot() -> Dictionary: + var enemies: Array = [] + var all: Array = _b2.all_enemies() + var ambush: int = _b3.pool_layers("ambush") + for i in all.size(): + var u: Dictionary = all[i] + var alive: bool = bool(u.get("alive", false)) + var ring: Array = u.get("pattern", []) + enemies.append({ + "order": i + 1, + "display_name": String(u.display_name), + "hp": int(u.hp), "max_hp": int(u.max_hp), + "enemy_id": String(u.enemy_id), + "armor_type": String(u.get("armor_type", "none")), + "beat": String(_b2.query_beat(u)) if alive else "", + "ring_size": ring.size(), + "ring_index": int(u.get("steps", 0)) % maxi(ring.size(), 1), + "ambush_layers": ambush, + "alive": alive, + }) + var hero := {} + var h: Dictionary = _b2.hero_unit() + if not h.is_empty(): + hero = {"hp": int(h.hp), "max_hp": int(h.max_hp), "atk": int(h.atk), "alive": bool(h.get("alive", false))} + return { + "round": round_index, + "battle_result": battle_result, + "phase": String(a3.phase), + "ap": int(a3.ap), "rap": int(a3.rap), + "can_request_refresh": can_request_refresh(), + "hero": hero, + "enemies": enemies, + "ambush_layers": ambush, + "hero_effects": _b3.query_active("hero"), + "inventory": _b4.query_inventory(), + "in_stock": _b4.in_stock_count(), + } + + +# 战斗进行中谓词(hero 在场且存活)——组合根高频相位判断的低成本面 +func is_battle_live() -> bool: + return battle_result == "" and not _b2.hero_unit().is_empty() + + +# 库存余量(层间过渡文案 / 终局清库反馈) +func stock_count() -> int: + return _b4.in_stock_count() + + +# 奖励身份解析(组合根注入 BoardView 徽章回调的只读代理——Ready 相位守卫归 B4) +func query_reward_identity(cell: Vector2i): + return _b4.query_identity(cell) + + +# 诊断观测面(组合根诊断转储 / 行为探针消费):私有编排状态的成文只读出口—— +# 消费方禁止再直读本类下划线字段(code-review 2026-09-12 封装收口批) +func diagnostic_snapshot() -> Dictionary: return { + "battle_result": battle_result, + "round": round_index, + "settling": _settling, + "awaiting_window": _awaiting_window, + "refresh_used_round": _refresh_used_this_round, + "refresh_used_waiting": _refresh_used_this_waiting, + "board_seq": _board_seq, + "facts_path": _facts_path, + "facts_reveals": _facts_reveals.size(), + "facts_flags": _facts_flags.size(), + "a3": {"phase": String(a3.phase), "ap": a3.ap, "rap": a3.rap}, + "a2_board_installed": a2.current_board() != null, + "a2_revealed": a2.revealed_cells().size() if a2.current_board() != null else 0, + "a2_flags": a2.flag_count() if a2.current_board() != null else 0, + "b4_phase_stock": [_b4.in_stock_count()], + "enemies_alive": _b2.query_alive("enemy").size(), + "hero_hp": int(_b2.hero_unit().get("hp", -1)) if not _b2.hero_unit().is_empty() else -1, + } + + +# 关卡结束编排(C1/C2 关卡结构落位前由上层显式调用——2026-09-11 关内延续): +# 清空库存并返回快照计数("剩余道具 ×N 随关卡结束清空"反馈行 / 折算结构位的数据面) +func level_clear() -> Dictionary: + var remaining := _b4.in_stock_count() + _b4.clear_inventory() + return {"remaining": remaining} + + +func _reset_round_facts() -> void: + _facts_reveals.clear() + _facts_flags.clear() + _facts_path = "" + _charge_dmg_a = 0 # 三段伤害充能随轮 facts 重置(每轮重新充能——结算段消费当轮值) + _charge_dmg_b = 0 + + +# ---- 行协议(Core Rule 14:公共字段 + 行类型特有字段)---- + +func _emit_row(type: String, source_class: String, origin_cell: Vector2i, ri: int, display: String, value: int, extra: Dictionary, highlight: String = "") -> void: + var row := { + "type": type, + "source_class": source_class, + "origin_cell": [origin_cell.x, origin_cell.y], + "round_index": ri, + "display_name": display, + "value": value, + } + if highlight != "": + row["highlight"] = highlight + for k in extra.keys(): + row[k] = extra[k] + step_row.emit(row) + + +func _fatal(msg: String) -> void: + push_error("[B1/fatal] " + msg) + assert(false, msg) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd.uid new file mode 100644 index 00000000..a9c4faea --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd.uid @@ -0,0 +1 @@ +uid://ctw48bkgsuh7m diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd b/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd new file mode 100644 index 00000000..2e81d71a --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd @@ -0,0 +1,122 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# A1 棋盘产物数据契约(A2 消费;生成后不可变——A2 持快照即持活引用,生成器不再触碰)。 +# 实现 GDD:design/gdd/a1-board-generation.md(系统 ID A1——registry 追溯锚,命名规范见 CLAUDE.md) +class_name BoardData +extends RefCounted + +var width: int = 0 +var height: int = 0 +var spawn_center := Vector2i.ZERO +var mines := {} # Vector2i → true +var digits := {} # Vector2i → int(安全格恒有值;数字恒真 = 8 邻雷数) +var variants := {} # Vector2i → Array[String](生成期快照,抗表重载) +var biome := "" +var level_index: int = 1 +var node_depth: int = 0 +var metrics := {} # 八项生成期指标 +var illegal_product := false + + +func in_bounds(c: Vector2i) -> bool: + return c.x >= 0 and c.y >= 0 and c.x < width and c.y < height + + +func index_of(c: Vector2i) -> int: + return c.y * width + c.x + + +func is_mine(c: Vector2i) -> bool: + return mines.has(c) + + +func is_safe(c: Vector2i) -> bool: + return in_bounds(c) and not mines.has(c) + + +func digit_of(c: Vector2i) -> int: + return digits.get(c, -1) + + +func variant_ids(c: Vector2i) -> Array: + return variants.get(c, []) + + +func has_variant(c: Vector2i, vid: String) -> bool: + return variant_ids(c).has(vid) + + +func neighbors8(c: Vector2i) -> Array: + var out: Array = [] + for dy in range(-1, 2): + for dx in range(-1, 2): + if dx == 0 and dy == 0: + continue + var n := Vector2i(c.x + dx, c.y + dy) + if in_bounds(n): + out.append(n) + return out # 行主序(规范序的邻接枚举基序) + + +func spawn_cells() -> Array: + var out: Array = [] + for dy in range(-1, 2): + for dx in range(-1, 2): + var c := Vector2i(spawn_center.x + dx, spawn_center.y + dy) + if in_bounds(c): + out.append(c) + return out + + +func all_cells() -> Array: + var out: Array = [] + for y in height: + for x in width: + out.append(Vector2i(x, y)) + return out + + +func safe_cells() -> Array: + var out: Array = [] + for c in all_cells(): + if is_safe(c): + out.append(c) + return out + + +func fog_cells() -> Array: + var out: Array = [] + for c in all_cells(): + if not variants.get(c, []).is_empty(): + out.append(c) + return out + + +# R₀ = 出生 3×3 ∪ (出生 3×3 经 0 格的 flood 闭包)——与 A2 F3 逐字同构(A1 F1 求解器初始态)。 +func compute_r0() -> Array: + var b3 := spawn_cells() + var r0 := {} + for c in b3: + r0[c] = true + var queue: Array = b3.duplicate() + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if is_safe(c) and digit_of(c) == 0 and variants.get(c, []).is_empty(): + for n in neighbors8(c): + if is_safe(n) and not r0.has(n): + r0[n] = true + queue.append(n) + return r0.keys() + + +# A1 Rule 4 排除域(以最终产物板计):R₀ ∪ 外扩 1 环——生成期确定性计算。 +func exclusion_domain() -> Dictionary: + var r0 := compute_r0() + var dom := {} + for c in r0: + dom[c] = true + for c in r0: + for n in neighbors8(c): + dom[n] = true + return dom diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd.uid new file mode 100644 index 00000000..aa541cbf --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd.uid @@ -0,0 +1 @@ +uid://df34rfpxbxj03 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd b/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd new file mode 100644 index 00000000..6a6a21bd --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd @@ -0,0 +1,541 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# A1 确定性棋盘生成器:五阶段管线(参数→布雷→变异→验证循环→产出)+ F1 可推理率度量 +# (single_subset 求解器)+ F3 雾预算 + F5 验收算子(达标即出/接受最优/接受最不坏)。 +# 切片裁剪登记:黄金向量回归套件与 F4 批量工具不做(C3 调参期工具域)。 +# 实现 GDD:design/gdd/a1-board-generation.md(系统 ID A1——registry 追溯锚,命名规范见 CLAUDE.md) +class_name BoardGenerator +extends RefCounted + +const METRIC_VERSION := 1 + + +# ==================== 生成入口 ==================== + +# rng_stream = 一次 open_stream("board_gen") 实例(一张图的全部取数;重试共用——attempt 边界非 scope 事件) +func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardData: + var row: Dictionary = db.resolve_board_gen(L, D) + if row.is_empty(): + _fatal("BoardGen 行缺失 (%d,%d)" % [L, D]) + return null + # 生成期表值单次读取快照(防热重载撕裂 attempt 语义) + var params := { + "L": L, "D": D, + "w": int(row.board_width), "h": int(row.board_height), + "density": float(row.mine_density), + "target": float(row.inferability_target), + "solver_rules": String(row.solver_rules), + "min_safe": int(row.min_safe_count), + "max_attempts": int(row.max_gen_attempts), + "fog_count": int(row.fog_region_count), + "fog_ratio": float(row.fog_target_ratio), + "biomes": Array(row.board_biomes), + } + var best_legal: BoardData = null + var best_legal_score := -1.0 + var best_bad: BoardData = null + var best_badness := [999999, 999999, 999999] + var attempts_used := 0 + var accepted: BoardData = null + var accept_mode := "" + for k in range(1, params.max_attempts + 1): + attempts_used = k + var b := _attempt(params, rng_stream, db) + var badness := _badness(b, params) + if best_bad == null or _lex_less(badness, best_badness): + best_bad = b + best_badness = badness + if legality(b, params, db): + var m: Dictionary = measure_inferability(b) # 求解器每合法板恰一次(O3 收口——此前重复调用两次) + var inf: float = m["inferability"] + if best_legal == null or inf > best_legal_score: + best_legal = b + best_legal_score = inf + if inf >= params.target and m["inferability_core"] >= params.target: + accepted = b + accept_mode = "first_pass" + break + if accepted == null: + if best_legal != null: + accepted = best_legal + accept_mode = "accept_best" + push_warning("[A1] 重试耗尽无达标——接受最优出图(强告警埋点,target=%.2f best=%.3f)" % [params.target, best_legal_score]) + else: + accepted = best_bad + accept_mode = "accept_worst" + accepted.illegal_product = true + push_error("[A1] attempts 耗尽零合法——接受最不坏出图 + 非法产物标记(玩家永不卡死)") + _finalize(accepted, params, attempts_used, accept_mode, db) + return accepted + + +# ==================== 五阶段:单次尝试 ==================== + +func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> BoardData: + var b := BoardData.new() + b.width = params.w + b.height = params.h + b.spawn_center = Vector2i(int((params.w - 1) / 2.0), int((params.h - 1) / 2.0)) + b.biome = str(params.biomes[0]) + b.level_index = params.L + b.node_depth = params.D + # ② 布雷:N = round(d×(W×H−9)) 先定数;域 = 全格 − 出生 3×3;均匀布雷后"先布后删" + var spawn := {} + for c in b.spawn_cells(): + spawn[c] = true + var domain: Array = [] + for c in b.all_cells(): + if not spawn.has(c): + domain.append(c) + var n_mines: int = int(round(params.density * float(params.w * params.h - 9))) + # 部分 Fisher-Yates(确定性,消费 board_gen 流) + for i in range(0, mini(n_mines, domain.size())): + var j := rng_stream.randi_range(i, domain.size() - 1) + var tmp = domain[i] + domain[i] = domain[j] + domain[j] = tmp + for i in range(0, mini(n_mines, domain.size())): + b.mines[domain[i]] = true + _compute_digits(b) + # 先布后删:0 格闭包(布后基准)外扩 1 环内的雷移除(只减不增、无迭代) + var ring := {} + for c in b.compute_r0(): + for n in b.neighbors8(c): + if not ring.has(n): + ring[n] = true + var removed := 0 + for c in ring.keys(): + if b.mines.has(c): + b.mines.erase(c) + removed += 1 + _compute_digits(b) + # ③ 变异布置(先雷后雾;排除域以最终产物板计) + var shortfall := _place_variants(b, params, rng_stream, db) + b.metrics = { + "metric_version": METRIC_VERSION, + "ring_removed": removed, + "shortfall": shortfall, + } + return b + + +func _compute_digits(b: BoardData) -> void: + b.digits.clear() + for c in b.all_cells(): + if b.is_safe(c): + var d := 0 + for n in b.neighbors8(c): + if b.is_mine(n): + d += 1 + b.digits[c] = d + + +# 变异布置:候选池门控 + cell_constraint 生成期强制 + 排除域排除 + 厚度≤2;返回 shortfall +func _place_variants(b: BoardData, params: Dictionary, rng_stream: RandomNumberGenerator, db) -> int: + if params.fog_count <= 0 or params.fog_ratio <= 0.0: + return 0 + var pool: Array = db.variant_pool(params.L, params.biomes) + if pool.is_empty(): + return 0 # EC1 空池静默——合法非错误 + var exclusion := b.exclusion_domain() + var total_target := int(round(params.fog_ratio * float(params.w * params.h))) + var placed_total := 0 + var achieved := 0 + var placed := {} + for r in range(params.fog_count): + var region_target: int = total_target / params.fog_count + if r < total_target % params.fog_count: + region_target += 1 + if region_target <= 0: + achieved += 1 # 零目标区视为达成(不消耗布置) + continue + var region := _grow_region(b, region_target, exclusion, placed, rng_stream) + if region.is_empty(): + continue # EC2:无种子 → 该区布置失败(shortfall) + for c in region.keys(): + b.variants[c] = ["fog"] + placed[c] = true + placed_total += 1 + if region.size() >= region_target: + achieved += 1 + return params.fog_count - achieved + + +# 区域生长:随机种子 + 随机边界扩张(连通域);厚度≤2 = 任意 3×3 窗口非全雾 +func _grow_region(b: BoardData, target: int, exclusion: Dictionary, placed: Dictionary, rng_stream: RandomNumberGenerator) -> Dictionary: + var candidates: Array = [] + for c in b.all_cells(): + if _valid_fog_cell(b, c, exclusion, placed): + candidates.append(c) + if candidates.is_empty(): + return {} + var region := {} + var seed_c: Vector2i = candidates[rng_stream.randi_range(0, candidates.size() - 1)] + region[seed_c] = true + while region.size() < target: + var frontier: Array = [] + for c in region.keys(): + for n in b.neighbors8(c): + if not region.has(n) and not placed.has(n) and _valid_fog_cell(b, n, exclusion, placed) and not _breaks_thickness(region, n, b): + frontier.append(n) + if frontier.is_empty(): + break # 尽力布置——部分区域合法(shortfall 上报) + var pick: Vector2i = frontier[rng_stream.randi_range(0, frontier.size() - 1)] + region[pick] = true + return region + + +# cell_constraint=safe_nonzero 生成期强制:非雷、非 0(⟺ 8 邻含雷)、不在排除域 +func _valid_fog_cell(b: BoardData, c: Vector2i, exclusion: Dictionary, placed: Dictionary) -> bool: + if placed.has(c) or exclusion.has(c): + return false + if not b.is_safe(c): + return false + if b.digit_of(c) <= 0: + return false + return true + + +func _breaks_thickness(region: Dictionary, candidate: Vector2i, b: BoardData) -> bool: + # 加入 candidate 后是否出现全雾 3×3 窗口 + for dy in range(-1, 2): + for dx in range(-1, 2): + var origin := Vector2i(candidate.x + dx - 1, candidate.y + dy - 1) + var all_fog := true + for wy in range(3): + for wx in range(3): + var c := Vector2i(origin.x + wx, origin.y + wy) + var is_fog := (region.has(c) and c != candidate) or c == candidate + if not is_fog: + all_fog = false + if all_fog: + return true + return false + + +# ==================== F1 可推理率度量(独立入口——与内嵌调用同一实现) ==================== + +# 返回 {r0, r_inf, inferability, inferability_core} +func measure_inferability(b: BoardData) -> Dictionary: + var safe_set := {} + for c in b.safe_cells(): + safe_set[c] = true + var fog_set := {} + for c in b.fog_cells(): + fog_set[c] = true + var r := {} + for c in b.compute_r0(): + r[c] = true + var known_mines := {} + var changed := true + while changed: + changed = false + # 算子① 连锁:0 格的 8 邻安全格全入 R(雾格可被链开但不传播——雾恒非 0,天然为叶) + var frontier: Array = r.keys() + for x in frontier: + if safe_set.has(x) and not fog_set.has(x) and b.digit_of(x) == 0: + for n in b.neighbors8(x): + if safe_set.has(n) and not r.has(n): + r[n] = true + changed = true + # 信息源(b)雾元知识:与 R 邻接的雾格必非雷 → 入 R + for f in fog_set.keys(): + if not r.has(f): + for n in b.neighbors8(f): + if r.has(n): + r[f] = true + changed = true + break + # 信息源(a)数字约束:single + subset(solver_rules=single_subset 冻结域) + var constraints := _build_constraints(b, r, known_mines, safe_set) + # single 规则 + for con in constraints: + if con.mines == 0: + for c in con.unknown: + if not r.has(c): + r[c] = true + changed = true + elif con.mines == con.unknown.size(): + for c in con.unknown: + if not known_mines.has(c): + known_mines[c] = true + changed = true + # subset 规则:U1 ⊂ U2 ⇒ (d2−d1, U2∖U1) + var derived := [] + for i in constraints.size(): + for j in range(i + 1, constraints.size()): + var a: Dictionary = constraints[i] + var bb: Dictionary = constraints[j] + var sub := _subset_of(a.unknown, bb.unknown) + if sub == 1: + derived.append(_diff_constraint(a, bb)) + elif sub == 2: + derived.append(_diff_constraint(bb, a)) + for con in derived: + if con.mines == 0: + for c in con.unknown: + if safe_set.has(c) and not r.has(c): + r[c] = true + changed = true + elif con.mines == con.unknown.size(): + for c in con.unknown: + if not known_mines.has(c): + known_mines[c] = true + changed = true + var r_inf := r.keys() + var s_count := safe_set.size() + var s_nonfog := 0 + var r_nonfog := 0 + for c in safe_set.keys(): + if not fog_set.has(c): + s_nonfog += 1 + if r.has(c): + r_nonfog += 1 + var inferability: float = float(r_inf.size()) / float(maxi(1, s_count)) + var core: float = float(r_nonfog) / float(maxi(1, s_nonfog)) + return { + "r0": b.compute_r0().size(), + "r_inf_count": r_inf.size(), + "inferability": inferability, + "inferability_core": core, + } + + +func _build_constraints(b: BoardData, r: Dictionary, known_mines: Dictionary, safe_set: Dictionary) -> Array: + var out: Array = [] + for c in r.keys(): + if not safe_set.has(c) or b.variant_ids(c).size() > 0: + continue # 雾格数字不可见(hide_numbers)——零特判数据面:变体格不产约束 + var d := b.digit_of(c) + var unknown := {} + var known_here := 0 + for n in b.neighbors8(c): + if known_mines.has(n): + known_here += 1 + elif not r.has(n) and safe_set.has(n): + unknown[n] = true + var d_eff := d - known_here + if d_eff < 0 or (d_eff > 0 and unknown.is_empty()): + continue # 矛盾态(非法板)——不产约束,验收由 legality 闸承担 + if not unknown.is_empty(): + out.append({"mines": d_eff, "unknown": unknown}) + return out + + +func _subset_of(a: Dictionary, b: Dictionary) -> int: + # 1: a ⊂ b;2: b ⊂ a;0: 互不包含 + var a_in_b := true + var b_in_a := true + for k in a.keys(): + if not b.has(k): + a_in_b = false + for k in b.keys(): + if not a.has(k): + b_in_a = false + if a.size() < b.size() and a_in_b: + return 1 + if b.size() < a.size() and b_in_a: + return 2 + return 0 + + +func _diff_constraint(big: Dictionary, small: Dictionary) -> Dictionary: + var diff := {} + for k in big.unknown.keys(): + if not small.unknown.has(k): + diff[k] = true + return {"mines": big.mines - small.mines, "unknown": diff} + + +# ==================== F3 雾预算(独立入口) ==================== + +func measure_fog_budget(b: BoardData, dig_ap_cost: int) -> Dictionary: + var safe_set := {} + for c in b.safe_cells(): + safe_set[c] = true + # Chain₀ = R₀ 经算子①单独闭包 + var chain0 := {} + for c in b.compute_r0(): + chain0[c] = true + var queue: Array = chain0.keys() + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if safe_set.has(c) and b.digit_of(c) == 0 and b.variant_ids(c).is_empty(): + for n in b.neighbors8(c): + if safe_set.has(n) and not chain0.has(n): + chain0[n] = true + queue.append(n) + var u_count := 0 + for c in b.fog_cells(): + if not chain0.has(c): + u_count += 1 + var pay := 0 + for c in safe_set.keys(): + if not chain0.has(c): + pay += 1 + return { + "chain0_count": chain0.size(), + "u_count": u_count, + "delta_budget_fog": u_count * dig_ap_cost, + "fog_burden_ratio": float(u_count) / float(maxi(1, pay)), + } + + +# ==================== 合法性闸(独立入口;F5 legality + R₀≠S 修订义务) ==================== + +func is_clearable(b: BoardData) -> bool: + var safe_set := {} + for c in b.safe_cells(): + safe_set[c] = true + var seen := {} + var queue: Array = b.spawn_cells().duplicate() + for c in queue: + seen[c] = true + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if not safe_set.has(c): + continue + for n in b.neighbors8(c): + if safe_set.has(n) and not seen.has(n): + seen[n] = true + queue.append(n) + for c in safe_set.keys(): + if not seen.has(c): + return false + return true + + +func legality(b: BoardData, params: Dictionary, db) -> bool: + var reasons := _legality_faults(b, params) + return reasons.is_empty() + + +func _legality_faults(b: BoardData, params: Dictionary) -> Array: + var faults: Array = [] + # ① 出生区无雷 ② 出生区∪R₀ 无变异 ③ 数字恒真 ④ R₀ ≠ S(A2 修订义务) + for c in b.spawn_cells(): + if b.is_mine(c): + faults.append("inv1_mine_in_spawn") + var r0 := {} + for c in b.compute_r0(): + r0[c] = true + if b.variant_ids(c).size() > 0: + faults.append("inv2b_variant_in_r0") + for c in b.spawn_cells(): + if b.variant_ids(c).size() > 0: + faults.append("inv2a_variant_in_spawn") + for c in b.all_cells(): + if b.is_safe(c) and b.digit_of(c) != _neighbor_mine_count(b, c): + faults.append("inv3_digit_invariant") + if r0.size() == b.safe_cells().size(): + faults.append("inv4_r0_equals_s") + # clearable(合法性闸——与质量度量两闸分离) + if not is_clearable(b): + faults.append("not_clearable") + # min_safe 产物侧(容错两种入参形态:generate 的 params 快照 / 原始表行) + var min_safe: int = int(params.get("min_safe", params.get("min_safe_count", 0))) + if b.safe_cells().size() < min_safe: + faults.append("min_safe_short") + # 雾结构:cell_constraint / 厚度 / f≤8d / 排除域 + var fog := b.fog_cells() + var exclusion := b.exclusion_domain() + for c in fog: + if b.is_mine(c) or b.digit_of(c) <= 0: + faults.append("fog_cell_constraint") + if exclusion.has(c): + faults.append("fog_in_exclusion") + if _has_all_fog_window(b, fog): + faults.append("fog_thickness") + if fog.size() > 8 * b.mines.size(): + faults.append("fog_over_8d") + return faults + + +func _neighbor_mine_count(b: BoardData, c: Vector2i) -> int: + var d := 0 + for n in b.neighbors8(c): + if b.is_mine(n): + d += 1 + return d + + +func _has_all_fog_window(b: BoardData, fog: Array) -> bool: + var fog_set := {} + for c in fog: + fog_set[c] = true + for y in b.height: + for x in b.width: + var all_fog := true + for wy in range(3): + for wx in range(3): + var c := Vector2i(x + wx, y + wy) + if not b.in_bounds(c) or not fog_set.has(c): + all_fog = false + if all_fog: + return true + return false + + +# ==================== 产出上报(八项指标) ==================== + +func _finalize(b: BoardData, params: Dictionary, attempts_used: int, accept_mode: String, db) -> void: + var m := measure_inferability(b) + var fog_budget := measure_fog_budget(b, db.action_economy.dig_ap_cost) + var fog := b.fog_cells() + var attempt_facts := b.metrics # 保留尝试期事实(ring_removed / shortfall) + b.metrics = { + "metric_version": METRIC_VERSION, + "inferability": m.inferability, + "inferability_core": m.inferability_core, + "actual_fog_ratio": float(fog.size()) / float(b.width * b.height), + "actual_mines": b.mines.size(), + "actual_safe_count": b.safe_cells().size(), + "fog_burden_ratio": fog_budget.fog_burden_ratio, + "delta_budget_fog": fog_budget.delta_budget_fog, + "attempts_used": attempts_used, + "accept_mode": accept_mode, + "shortfall": attempt_facts.get("shortfall", 0), + "ring_removed": attempt_facts.get("ring_removed", 0), + "r0_count": m["r0"], + } + b.illegal_product = accept_mode == "accept_worst" + + +func _badness(b: BoardData, params: Dictionary) -> Array: + # F5 k*** 确定性劣度字典序:(不可达安全格数, min_safe 缺口, 序号) + var unreachable := 0 + var safe_set := {} + for c in b.safe_cells(): + safe_set[c] = true + var seen := {} + var queue: Array = b.spawn_cells().duplicate() + for c in queue: + seen[c] = true + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if not safe_set.has(c): + continue + for n in b.neighbors8(c): + if safe_set.has(n) and not seen.has(n): + seen[n] = true + queue.append(n) + for c in safe_set.keys(): + if not seen.has(c): + unreachable += 1 + var gap: int = maxi(0, int(params.min_safe) - b.safe_cells().size()) + return [unreachable, gap, 0] + + +func _lex_less(a: Array, b: Array) -> bool: + for i in a.size(): + if a[i] < b[i]: + return true + if a[i] > b[i]: + return false + return false + + +func _fatal(msg: String) -> void: + push_error("[A1/fatal] " + msg) + assert(false, msg) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd.uid new file mode 100644 index 00000000..ac9d8fcc --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd.uid @@ -0,0 +1 @@ +uid://brlq4y6ahd6so diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd b/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd new file mode 100644 index 00000000..ef486665 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd @@ -0,0 +1,341 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 ConfigDB autoload——数值表单点访问入口(Rule 2)+ 校验双通道(Rule 6)+ R1 行解析。 +# 实现 GDD:design/gdd/d2-data-config.md(系统 ID D2——registry 追溯锚,命名规范见 CLAUDE.md) +# 切片注:headless --script 模式下由冒烟脚本显式实例化(D2 AC 前言);玩法代码禁止绕过本单点自建 load()。 +extends Node + +const SCHEMA_VERSION := 1 + +const TABLE_MANIFEST := { + "action_economy": "action_economy.tres", + "variation": "variation.tres", + "status_effects": "status_effects.tres", + "board_gen": "board_gen.tres", + "enemy": "enemy.tres", + "hero": "hero.tres", + "reward": "reward.tres", + "reward_config": "reward_config.tres", + "settlement": "settlement.tres", + "level": "level.tres", + "difficulty": "difficulty.tres", +} + +var action_economy: ActionEconomyTable +var variation: VariationTable +var status_effects: StatusEffectTable +var board_gen: BoardGenTable +var enemy: EnemyTable +var hero: HeroTable +var reward: RewardTable +var reward_config: RewardConfigTable +var settlement: SettlementTable +var level: LevelTable +var difficulty: DifficultyTable + +var is_ready := false +var _diagnostics: Array = [] # {severity, category, message} +var _warn_dedup: Dictionary = {} # R1 四元组去重注册表 +var _reload_gate := false # D2 EC8——B1 唯一调用方 + + +func push_diag(severity: String, category: String, message: String) -> void: + _diagnostics.append({"severity": severity, "category": category, "message": message}) + if severity == "error": + push_error("[ConfigDB/%s] %s" % [category, message]) + elif severity == "warning": + push_warning("[ConfigDB/%s] %s" % [category, message]) + + +func reset_diagnostics() -> void: + _diagnostics.clear() + _warn_dedup.clear() + + +func diagnostics() -> Array: + return _diagnostics.duplicate() + + +func diagnostics_count(severity: String) -> int: + var n := 0 + for d in _diagnostics: + if d.severity == severity: + n += 1 + return n + + +func _ready() -> void: + load_tables("res://data/tables", false) + + +func load_tables(root: String, collect_only: bool) -> bool: + is_ready = false + _diagnostics.clear() + _warn_dedup.clear() + var loaded := {} + for key in TABLE_MANIFEST.keys(): + var path := root.path_join(TABLE_MANIFEST[key]) + if not ResourceLoader.exists(path): + push_diag("error", "missing_file", "表文件缺失: %s" % path) + continue + var res: Resource = load(path) + if res == null: + push_diag("error", "load_failed", "表加载失败: %s" % path) + continue + loaded[key] = res + for e in res.validate(): + push_diag("error", e.category, "%s → %s" % [key, e.message]) + # 跨表校验(悬空引用 / 池护栏——全部加载期致命) + _cross_validate(loaded) + var has_errors := diagnostics_count("error") > 0 + if has_errors and not collect_only: + # 游戏通道:致命(Rule 6)——is_ready 恒 false,调用方(主场景)中止构建 + return false + action_economy = loaded.get("action_economy") + variation = loaded.get("variation") + status_effects = loaded.get("status_effects") + board_gen = loaded.get("board_gen") + enemy = loaded.get("enemy") + hero = loaded.get("hero") + reward = loaded.get("reward") + reward_config = loaded.get("reward_config") + settlement = loaded.get("settlement") + level = loaded.get("level") + difficulty = loaded.get("difficulty") + is_ready = not has_errors + return not has_errors + + +func _cross_validate(loaded: Dictionary) -> void: + var st: StatusEffectTable = loaded.get("status_effects") + var rt: RewardTable = loaded.get("reward") + var rc: RewardConfigTable = loaded.get("reward_config") + var st_tab: SettlementTable = loaded.get("settlement") + if st == null or rt == null or rc == null or st_tab == null: + return # 单表校验已报错,不连锁 + var effect_ids := {} + for row in st.rows: + effect_ids[row.get("effect_id", "")] = true + # ① RewardTable effect_id 悬空(首个引用行已回填 → 激活) + for row in rt.rows: + var eff: String = row.get("effect_id", "") + if eff != "" and not effect_ids.has(eff): + push_diag("error", "dangling_effect", "RewardTable %s 引用不存在的 effect_id: %s" % [row.get("reward_id"), eff]) + # ② 池空护栏:safe_cell × 雾/非雾两态各 ≥1 活行;活 buff 行集非空 + var t_fog: int = rc.t_fog_min + var safe_rows: Array = [] + var clear_rows: Array = [] + for row in rt.rows: + if int(row.get("weight", 0)) <= 0: + continue + if row.get("pool_tag", "") == "safe_cell": + safe_rows.append(row) + elif row.get("pool_tag", "") == "clear_default": + clear_rows.append(row) + var nonfog_pool := safe_rows + var fog_pool: Array = [] + for row in safe_rows: + if int(row.get("tier", 0)) >= t_fog: + fog_pool.append(row) + if nonfog_pool.is_empty(): + push_diag("error", "pool_empty_safe", "safe_cell 非雾池无活行") + if fog_pool.is_empty(): + push_diag("error", "pool_empty_fog", "safe_cell 雾态池无活行(tier ≥ t_fog_min)") + var has_buff := false + for row in safe_rows: + if row.get("category", "") == "buff": + has_buff = true + if safe_rows.size() > 0 and not has_buff: + push_diag("error", "no_live_buff_row", "safe_cell 池零活 buff 行——tier_min_buff 无定义") + # ③ clear_default 恒正当量(A3 条款④——2026-09-11 供给控制器批:清图槽改即时类一次性伤害,正当量含即时类) + var clear_value_ok := false + for row in clear_rows: + if row.get("category", "") == "buff" and String(row.get("effect_id", "")) != "": + clear_value_ok = true + elif row.get("category", "") in ["damage_item", "heal_pack", "instant_aoe", "instant_heal"] and int(row.get("power", 0)) >= 1: + clear_value_ok = true + if not clear_value_ok: + push_diag("error", "clear_no_value", "clear_default 池无正当量活行(清图奖励 > 0 违例)") + # ④ floor_supply_missing(F5 护栏 i)——k_dmg ≥ 1 时才检查(供给控制器批后 k_dmg=0 休眠) + if rc.k_dmg >= 1: + var supply_ok := false + for row in safe_rows: + if row.get("category", "") == "damage_item" and int(row.get("power", 0)) >= rc.p_min: + supply_ok = true + if not supply_ok: + push_diag("error", "floor_supply_missing", "K_dmg ≥ 1 但 safe_cell 池无 power ≥ P_min 活刀行") + # ⑤ no_damage_row(F5 护栏 ii——零伤害世界后闸;2026-09-11 供给控制器批:扫描域扩至全池活行, + # 可选类退出格子池后伤害来源在 active_inject 池) + if rc.k_dmg == 0: + var any_knife := false + for row in rt.rows: + if int(row.get("weight", 0)) > 0 and row.get("category", "") == "damage_item": + any_knife = true + if not any_knife: + push_diag("error", "no_damage_row", "K_dmg = 0 且全池零活刀行——零伤害世界后闸") + # ⑨ active_inject 池护栏(可选类供给控制器批):非可选类拒绝;池须有活行(否则战斗首件保证落空) + var inject_live := 0 + for row in rt.rows: + if row.get("pool_tag", "") == "active_inject": + if int(row.get("weight", 0)) > 0: + inject_live += 1 + if row.get("category", "") not in ["damage_item", "heal_pack"]: + push_diag("error", "inject_pool_foreign", "active_inject 池混入非可选类行: %s" % row.get("reward_id")) + if inject_live == 0: + push_diag("error", "inject_pool_empty", "active_inject 池无活行(战斗首件保证必落空)") + # ⑥ SettlementTable.effect_pool_map 悬空 + for eff in st_tab.effect_pool_map.keys(): + if not effect_ids.has(eff): + push_diag("error", "poolmap_dangling", "SettlementTable.effect_pool_map 悬空 effect_id: %s" % eff) + # ⑦ source_label_map 键集封闭性(键 = B3 source_class ∪ {item, enemy_action, flag_miss}) + var required_sources := ["safe_cell", "flag_hit", "mine_hit", "clear_reward", "item", "hero_attack", "enemy_action", "flag_miss"] + for s in required_sources: + if not st_tab.source_label_map.has(s): + push_diag("error", "segorder_violation", "source_label_map 缺必需键: %s" % s) + # ⑧ LevelTable 节点悬空引用(battle/boss 的 (L,node_depth) 须在 board_gen 与 enemy 两表有精确行—— + # 线性推进器按精确深度行跑,floor 兜底静默降难度属调参事故,加载期直接拦) + var lv: LevelTable = loaded.get("level") + var bg: BoardGenTable = loaded.get("board_gen") + var en: EnemyTable = loaded.get("enemy") + if lv != null and bg != null and en != null: + var bg_keys := {} + for row in bg.rows: + bg_keys["%d_%d" % [int(row.level_index), int(row.node_depth)]] = true + var en_keys := {} + for row in en.rows: + en_keys["%d_%d" % [int(row.level_index), int(row.node_depth)]] = true + for lrow in lv.rows: + var li: int = int(lrow.level_index) + for node in lrow.nodes: + var t: String = String(node.get("type", "")) + if t != "battle" and t != "boss": + continue + var key := "%d_%d" % [li, int(node.get("node_depth", -1))] + if not bg_keys.has(key): + push_diag("error", "level_node_dangling", "level %d %s 节点 (%s) 在 board_gen 无精确行" % [li, t, key]) + if not en_keys.has(key): + push_diag("error", "level_node_dangling", "level %d %s 节点 (%s) 在 enemy 无精确行" % [li, t, key]) + + +# ---- R1 难度行解析(floor + warning 四元组去重;请求越域致命)---- + +func resolve_board_gen(L: int, D: int) -> Dictionary: + return _resolve_progress(board_gen.rows, L, D, "board_gen") + + +func resolve_enemy_rowset(L: int, D: int) -> Dictionary: + return _resolve_progress(enemy.rows, L, D, "enemy") + + +# C3 DifficultyTable 消费面(B2.spawn 调用):(L,D) 精确匹配修饰乘数,越域值 clamp(GDD Edge Case——组合事故非配置事故) +func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary: + if difficulty == null: + return {} + for row in difficulty.rows: + if int(row.get("level_index", 0)) == L and int(row.get("node_depth", -1)) == D: + return { + "hp_mult": clampf(float(row.get("hp_mult", 1.0)), 0.1, 10.0), + "atk_mult": clampf(float(row.get("atk_mult", 1.0)), 0.1, 10.0), + } + return {} + + +func _resolve_progress(rows: Array, L: int, D: int, table_id: String) -> Dictionary: + if L < 1 or D < 0: + push_diag("error", "request_oob", "%s 请求越域 (%d,%d)" % [table_id, L, D]) + return {} + var levels := {} + for row in rows: + levels[int(row.get("level_index", 0))] = true + var level_keys := levels.keys() + level_keys.sort() + var l_star: int + var mt_level := "exact" + if L < level_keys[0]: + l_star = level_keys[0] + mt_level = "ceil" + else: + l_star = level_keys[0] + for lv in level_keys: + if lv <= L: + l_star = lv + if l_star < L: + mt_level = "floor" + var depth_keys := {} + for row in rows: + if int(row.get("level_index", 0)) == l_star: + depth_keys[int(row.get("node_depth", 0))] = true + var d_keys := depth_keys.keys() + d_keys.sort() + var d_star: int + var mt_depth := "exact" + if d_keys.is_empty(): + push_diag("error", "structural", "%s level %d 无行(锚行校验应已拦截)" % [table_id, l_star]) + return {} + if D < d_keys[0]: + d_star = d_keys[0] + mt_depth = "floor" + else: + d_star = d_keys[0] + for dv in d_keys: + if dv <= D: + d_star = dv + if d_star < D: + mt_depth = "floor" + var missing := "" + if mt_level == "ceil": + missing = "base_ceil" + elif mt_level == "floor" and mt_depth == "floor": + missing = "both" + elif mt_level == "floor": + missing = "level_floor" + elif mt_depth == "floor": + missing = "depth_floor" + if missing != "": + var dedup_key := "%s|%s|%d|%d" % [table_id, missing, l_star, d_star] + if not _warn_dedup.has(dedup_key): + _warn_dedup[dedup_key] = true + push_diag("warning", "resolve_" + missing, "%s 请求 (%d,%d) floor 解析 → (%d,%d) [%s]" % [table_id, L, D, l_star, d_star, missing]) + for row in rows: + if int(row.get("level_index", 0)) == l_star and int(row.get("node_depth", 0)) == d_star: + return row + return {} + + +# ---- A4 variant_pool_gate:候选池门控(∅ biome_tag = 通配)---- + +func variant_pool(level_index: int, board_biomes: Array) -> Array: + var pool: Array = [] + for row in variation.rows: + if int(row.get("unlock_level", 999)) > level_index: + continue + var tags: Array = row.get("biome_tags", []) + if tags.is_empty(): + pool.append(row) + continue + for t in tags: + if board_biomes.has(t): + pool.append(row) + break + return pool + + +func status_effect_row(effect_id: String) -> Dictionary: + return status_effects.row_by_id(effect_id) + + +# ---- D2 EC8:debug 热重载门(B1 唯一调用方;切片实现 = 门标志 + 拒绝语义)---- + +func set_reload_gate(gated: bool) -> void: + _reload_gate = gated + + +func reload() -> bool: + if _reload_gate: + push_diag("warning", "reload_gated", "战斗中热重载被拒绝(D2 EC8)") + return false + var ok := load_tables("res://data/tables", false) + if ok: + push_diag("debug", "config_reloaded", "表热重载完成") + return ok diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd.uid new file mode 100644 index 00000000..936b5331 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd.uid @@ -0,0 +1 @@ +uid://cfelf84soyn3y diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd b/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd new file mode 100644 index 00000000..c77e6f7b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd @@ -0,0 +1,19 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# 实现 GDD:design/gdd/b1-settlement.md(系统 ID B1——registry 追溯锚,命名规范见 CLAUDE.md) +# B1 战斗段伤害公式族(零状态纯算术宿主——规范 1.2「工具性纯函数挂明确宿主类」; +# 自 battle_orchestrator 拆出 2026-09-13,封装审查 O3)。池读取与表值读取仍归 B1,本类只做算术。 +class_name DamageMath +extends RefCounted + + +# F2 乘区(池值已由调用方 clamp 后传入):池内加算 → 池间乘算 → floor(单一取整规则) +static func pool_damage(d_raw: int, p_hero: int, p_enemy: int) -> int: + var v: int = int(floor(float(d_raw) * (100.0 + float(p_hero)) * (100.0 + float(p_enemy)) / 10000.0)) + return maxi(0, v) + + +# 伤害A/B 充能分量:充能(调用方已套单轮充能上限)× 每单位值 × 克制 percent → floor +static func charge_component(charge: int, per_unit: int, effectiveness_percent: int) -> int: + var pts := charge * per_unit + return int(floor(float(pts) * float(effectiveness_percent) / 100.0)) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd.uid new file mode 100644 index 00000000..47d407b0 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd.uid @@ -0,0 +1 @@ +uid://cym7k76gt61k diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd b/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd new file mode 100644 index 00000000..89d758b2 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd @@ -0,0 +1,215 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 线性第一关(战×2→道具→战×2→恢复→Boss)能否承载"数值提升→通关期望改善"的元进度验证 +# Date: 2026-09-11 +# C1 关卡节点地图(provisional 线性形态——节点链来自 LevelTable,路径分支/形态定稿权归 C1): +# 关卡域生命周期唯一编排者——共享 B4 容器跨战斗延续(2026-09-11 关内延续裁决)、HP 跨战斗携带、 +# 奖励点/恢复点节点兑付、关卡末 level_clear 清库。Core 层零 UI 依赖(headless 可跑完整关卡——D2 Rule 7)。 +# 测试专用:hero_atk_pct/hero_hp_pct 数值覆盖(调整板模拟天赋树——正式侧归 C4 元进度,切片不落盘)。 +# E4 收口(2026-09-13):覆盖值经 encounter 显式传入战斗(不再写 db.hero 表——表值运行期不可变, +# 双基线同步/双写叠加 6→18→54 整类隐患随表写一并移除)。 +# 对应 GDD:C1 关卡节点地图(未设计——provisional 切片先行,定稿后回写;命名规范见 CLAUDE.md) +class_name LevelRunner +extends RefCounted + +signal node_entered(node: Dictionary, info: Dictionary) # info 按 type:battle/boss={} · reward={snapshot} · rest={healed,hp,max_hp} +signal step_row(row: Dictionary) # 转发当前 B1 结算行 +signal window_opened(items: Array) # 转发当前 B1 道具窗口 +signal round_started(round_index: int) +signal board_changed() +signal level_finished(payload: Dictionary) # {result, hp, node_index, node_type, remaining_items} + +var db +var rng +var level_row: Dictionary = {} +var node_index := -1 +var hero_hp := 0 +# 协作对象私有(E1/E3 封装收口 2026-09-13):b1/b4 读取经 current_battle()/inventory() 成文出口 +var _b1 = null # 当前战斗编排器(关卡结束置 null) +var _b4 = null # 关卡域共享 B4 容器(库存跨战斗延续) + +var hero_atk_pct := 0 # 测试面板覆盖值(切片专用) +var hero_hp_pct := 0 +var _running := false +var level_result := "" # "" / "victory" / "defeat" + + +func _init(db_ref, rng_ref) -> void: + db = db_ref + rng = rng_ref + + +# ---- 测试面板覆盖(模拟天赋树属性提升——仅测试用,正式侧归 C4;经 encounter 传入战斗)---- + +func set_hero_override(atk_pct: int, hp_pct: int) -> void: + hero_atk_pct = atk_pct + hero_hp_pct = hp_pct + + +func effective_atk() -> int: + return maxi(1, int(round(float(db.hero.atk) * (100.0 + float(hero_atk_pct)) / 100.0))) + + +func effective_max_hp() -> int: + return maxi(1, int(round(float(db.hero.max_hp) * (100.0 + float(hero_hp_pct)) / 100.0))) + + +# 覆盖复位(关卡结束归零——表值不可变,仅清百分比;调用方兼容保留) +func restore_hero_base() -> void: + hero_atk_pct = 0 + hero_hp_pct = 0 + + +# ---- 关卡生命周期 ---- + +func start_level(level_index: int, seed_val: int) -> bool: + var row := _find_level_row(level_index) + if row.is_empty(): + push_error("[C1] level_index %d 在 LevelTable 无行" % level_index) + return false + level_row = row + node_index = -1 + level_result = "" + hero_hp = effective_max_hp() + _b4 = RewardEconomy.new(db, rng) + rng.new_adventure(seed_val) # 关卡起点 = 冒险起点(切片口径;跨关接续归 C2) + _running = true + return _enter_next_node() + + +func _find_level_row(level_index: int) -> Dictionary: + for row in db.level.rows: + if int(row.level_index) == level_index: + return row + return {} + + +func nodes() -> Array: + return level_row.get("nodes", []) + + +func current_node() -> Dictionary: + if node_index < 0 or node_index >= nodes().size(): + return {} + return nodes()[node_index] + + +func _enter_next_node() -> bool: + if not _running: + return false + node_index += 1 + var node: Dictionary = current_node() + if node.is_empty(): + push_error("[C1] 节点链越界(末节点 Boss 应已结束关卡)") + return false + match String(node.get("type", "")): + "battle", "boss": + return _start_battle(node) + "reward": + var inst = _b4.grant_node_active(Vector2i(-1, -1), 0) + if not (inst is Dictionary): + push_error("[C1] 奖励点入库被拒") + return false + node_entered.emit(node, {"snapshot": inst.snapshot}) + return true + "rest": + var healed := _apply_rest() + node_entered.emit(node, {"healed": healed, "hp": hero_hp, "max_hp": effective_max_hp()}) + return true + push_error("[C1] 未知节点类型: %s" % str(node)) + return false + + +func _start_battle(node: Dictionary) -> bool: + _b1 = BattleOrchestrator.new(db, rng, _b4) # 共享 B4——库存跨战斗延续(关内延续裁决) + # core↔core 接线只许方法 Callable(ADR-0001 静态规则 S6——lambda 捕获 self 会延长 + # RefCounted 生命周期乃至成环:b1 的信号持捕获 runner 的 lambda,runner 又持 b1) + _b1.step_row.connect(_forward_step_row) + _b1.window_opened.connect(_forward_window_opened) + _b1.round_started.connect(_forward_round_started) + _b1.board_changed.connect(_forward_board_changed) + _b1.battle_finished.connect(_on_battle_finished) + # node_entered 先于 begin_battle:呈现层须先拿到 b1 引用,才能响应 begin 期间发出的 + # board_changed(首板安装)并重建棋盘——后发会导致首战棋盘不渲染(2026-09-11 playtest 缺陷①) + node_entered.emit(node, {}) + if not _b1.begin_battle({"level_index": int(level_row.level_index), "node_depth": int(node.get("node_depth", 0)), + "hero_hp": hero_hp, "hero_atk": effective_atk(), "hero_max_hp": effective_max_hp()}): + push_error("[C1] 战斗启动失败(depth %s)" % str(node.get("node_depth", -1))) + return false + return true + + +func _forward_step_row(row: Dictionary) -> void: + step_row.emit(row) + + +func _forward_window_opened(items: Array) -> void: + window_opened.emit(items) + + +func _forward_round_started(ri: int) -> void: + round_started.emit(ri) + + +func _forward_board_changed() -> void: + board_changed.emit() + + +func _on_battle_finished(payload: Dictionary) -> void: + var node := current_node() + var result: String = String(payload.get("result", "")) + if result == "victory": + hero_hp = maxi(1, int(payload.get("hero_hp", 0))) # despawn 前捕获值(载荷自包含) + if String(node.get("type", "")) == "boss" or result == "defeat": + # Boss 胜利 = 关卡通关;任意战斗败北 = 关卡失败(Roguelike——冒险即终,重开归 C2/C4) + var left: int = _b1.level_clear()["remaining"] # 库存随关卡结束清空(关内延续裁决③) + if _b4.in_stock_count() != 0: + push_error("[C1] level_clear 后库存非空(清库违例——fail-fast)") + return + level_result = "victory" if result == "victory" else "defeat" + var hp_left := hero_hp if result == "victory" else 0 + _running = false + _b1 = null + level_finished.emit({"result": level_result, "hp": hp_left, "node_index": node_index, + "node_type": String(node.get("type", "")), "remaining_items": left}) + return + _enter_next_node() + + +func _apply_rest() -> int: + var pct: int = int(level_row.get("rest_heal_pct", 0)) + var mhp := effective_max_hp() + var heal := int(floor(float(mhp) * float(pct) / 100.0)) + var actual: int = mini(heal, mhp - hero_hp) + hero_hp += actual + return actual + + +# ---- 只读出口(E1/E3 封装收口:呈现层/工具经此读当前战斗与关卡库存,禁直读协作对象)---- + +func current_battle(): + return _b1 + + +func inventory() -> Array: + return _b4.query_inventory() if _b4 != null else [] + + +# ---- 玩家输入转发(呈现层只与 runner 对话)---- + +# 奖励/恢复点确认继续(呈现层在覆盖层上点击"继续"后调用;自动模拟在 node_entered 回调内立即调用) +func continue_after_node() -> bool: + var t := String(current_node().get("type", "")) + if t != "reward" and t != "rest": + return false + return _enter_next_node() + + +func submit_window_selection(selection: Array) -> void: + if _b1 != null: + _b1.submit_window_selection(selection) + + +func player_request_refresh() -> bool: + if _b1 == null: + return false + return _b1.player_request_refresh() diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd.uid new file mode 100644 index 00000000..55cb6adc --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd.uid @@ -0,0 +1 @@ +uid://b3o7g73bnbsya diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd new file mode 100644 index 00000000..848c8f93 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd @@ -0,0 +1,440 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# A2 排雷玩法规则引擎:零随机/零表读取/零 UI——输入 = A1 产物 + 意图序列,输出 = 三事件流。 +# 核心契约:install 四不变式 · 初始展开 = R₀ 恰等 · 连锁不越旗 · 批次语义序仲裁(cleared > stop > 其余)。 +# 实现 GDD:design/gdd/a2-minesweeper-rules.md(系统 ID A2——registry 追溯锚,命名规范见 CLAUDE.md) +class_name MinesweeperRules +extends RefCounted + +signal cell_revealed(evt: Dictionary) +signal flag_judged(evt: Dictionary) +signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(A2 外发;stopped 由 A3 直发) + +const SUB_REASONS := ["not_adjacent", "target_revealed", "target_concealed", "flag_present", "flag_absent", "no_path", "out_of_catalog"] + +var db +var a3 +# 棋盘状态私有(E2 封装收口 2026-09-13):对外只暴露 current_board()/reveal_record() 等查询面, +# 禁止跨模块直读/直写原始字典——先例教训 = board_view 绕过查询面直读 revealed。 +var _board: BoardData = null +var _revealed := {} # Vector2i → {"kind": "safe"/"mine", "digit": int} +var _flags := {} # Vector2i → true(棋盘单态;跨轮保留随图存续) +var _hero := Vector2i.ZERO +var _spawn: Dictionary = {} + + +func _init(db_ref, a3_ref) -> void: + db = db_ref + a3 = a3_ref + + +# ---- 板态 ---- + +func has_board() -> bool: + return _board != null + + +# 板产物只读出口(E2 收口——B1 快照 / 组合根 rebuild / 探针经此取板,禁再直读字段) +func current_board() -> BoardData: + return _board + + +# 已开格记录查询面(cell → {kind, digit};未开 = {})——呈现层渲染消费面(board_view 徽章/四格态) +func reveal_record(c: Vector2i) -> Dictionary: + return _revealed.get(c, {}) + + +func hero_pos() -> Vector2i: + return _hero + + +func is_revealed(c: Vector2i) -> bool: + return _revealed.has(c) + + +func has_flag(c: Vector2i) -> bool: + return _flags.has(c) + + +func revealed_cells() -> Array: + return _revealed.keys() + + +func safe_remaining() -> int: + var n := 0 + for c in _board.safe_cells(): + if not _revealed.has(c): + n += 1 + return n + + +func clear_map_cleared() -> bool: + return safe_remaining() == 0 + + +# ---- install(编排边界相位 = A3 Idle/Waiting;Active 拒绝——防轮内换板)---- + +# during_round = 轮内刷新编排路径(2026-09-12 方案 A,B1 唯一调用方):Active 相位合法—— +# 玩家挖掘中立即换图,棋盘状态(revealed/flags)随 install 重置,初始展开事件照发(initial 通道) +func install_board(b: BoardData, during_round := false) -> Dictionary: + if a3.phase == a3.PHASE_ACTIVE and not during_round: + return _outcome(false, "rejected", "phase", "") + if a3.phase == a3.PHASE_IDLE and during_round: + return _outcome(false, "rejected", "phase", "") + if b == null: + return _outcome(false, "rejected", "board_invalid", "out_of_catalog") + var inv := _install_invariant_faults(b) + if not inv.is_empty(): + push_error("[A2] install 不变式违例: %s" % str(inv)) + return _outcome(false, "rejected", "board_invalid", inv[0]) + _board = b + _revealed.clear() + _flags.clear() + _spawn = {} + for c in b.spawn_cells(): + _spawn[c] = true + # 初始展开:O₀ = R₀ 恰等(免费/计入清图/无奖励/永不触雷触雾);板级一次跨轮不重放 + for c in _initial_expansion_order(b): + _revealed[c] = {"kind": "safe", "digit": b.digit_of(c)} + cell_revealed.emit({ + "cell": c, "kind": "safe", "digit": b.digit_of(c), + "variant_ids": [], "channel": "initial", "reward_eligible": false, + }) + _hero = b.spawn_center + return _outcome(true, "executed", "", "") + + +# during_round = 轮内刷新编排路径(2026-09-12 方案 A):Active 相位合法——换图前退役旧板 +func flag_count() -> int: + return _flags.size() + + +func retire_board(during_round := false) -> Dictionary: + if _board == null: + return _outcome(true, "executed", "", "") # 幂等受理 + if a3.phase == a3.PHASE_ACTIVE and not during_round: + return _outcome(false, "rejected", "phase", "") + _board = null + _revealed.clear() + _flags.clear() + return _outcome(true, "executed", "", "") + + +# 四不变式(诊断类别封闭枚举):inv1 出生区雷 / inv2a 出生区变异 / inv2b R₀ 变异 / inv3 数字恒真 / inv4 R₀=S +func _install_invariant_faults(b: BoardData) -> Array: + var faults: Array = [] + var r0 := {} + for c in b.compute_r0(): + r0[c] = true + for c in b.spawn_cells(): + if b.is_mine(c): + faults.append("inv1_mine_in_spawn") + if b.variant_ids(c).size() > 0: + faults.append("inv2a_variant_in_spawn") + for c in r0.keys(): + if b.variant_ids(c).size() > 0: + faults.append("inv2b_variant_in_r0") + for c in b.all_cells(): + if b.is_safe(c): + var d := 0 + for n in b.neighbors8(c): + if b.is_mine(n): + d += 1 + if b.digit_of(c) != d: + faults.append("inv3_digit_invariant") + if r0.size() == b.safe_cells().size(): + faults.append("inv4_r0_equals_s") + return faults + + +# 初始展开规范序:触发格 BFS、8 邻行主序(≡ A1 F1 R₀ 算子;禁外扩环、禁只开 3×3) +func _initial_expansion_order(b: BoardData) -> Array: + var order: Array = b.spawn_cells().duplicate() + var seen := {} + for c in order: + seen[c] = true + var queue: Array = order.duplicate() + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if b.is_safe(c) and b.digit_of(c) == 0 and b.variant_ids(c).is_empty(): + for n in b.neighbors8(c): + if b.is_safe(n) and not seen.has(n): + seen[n] = true + order.append(n) + queue.append(n) + return order + + +# ---- 批次结算(唯一提交入口;单意图批 = 平凡形 ≡ 便捷 API)---- + +func settle_batch(intents: Array) -> Array: + var outcomes: Array = [] + if intents.is_empty(): + return outcomes + if a3.phase != a3.PHASE_ACTIVE or _board == null: + for intent in intents: + outcomes.append(_outcome(false, "rejected", "phase", "")) + return outcomes + # F4 语义序仲裁:C_dig(真模拟清图)→ C_stop → C_other;类内稳定序 = 到达序 + var indexed: Array = [] + for i in intents.size(): + indexed.append({"i": i, "intent": intents[i], "cls": _classify(intents[i])}) + indexed.sort_custom(func(a, b): + if a.cls != b.cls: + return a.cls < b.cls + return a.i < b.i) + var round_over := false + for item in indexed: + if round_over: + outcomes.append(_outcome(false, "voided", "", "")) + continue + var res := _execute(item.intent) + outcomes.append(res) + if a3.phase != a3.PHASE_ACTIVE: + round_over = true + return outcomes + + +func _classify(intent: Dictionary) -> int: + # 0 = C_dig(执行后清图——真模拟,禁计数器捷径);1 = C_stop;2 = C_other + if intent.get("action", "") == "dig": + var c: Vector2i = intent.get("cell", Vector2i(-9, -9)) + if _board_face_dig(c) == "" and not _board.is_mine(c): + # 模拟 Chain(O, c) 后清图判定 + var chain := _chain_closure(c, {}) + var remaining := 0 + for sc in _board.safe_cells(): + if not _revealed.has(sc) and not chain.has(sc): + remaining += 1 + if remaining == 0: + return 0 + return 2 + if intent.get("action", "") == "stop": + return 1 + return 2 + + +func _execute(intent: Dictionary) -> Dictionary: + match intent.get("action", ""): + "dig": + return _do_dig(intent.get("cell", Vector2i(-9, -9))) + "flag_place": + return _do_flag_place(intent.get("cell", Vector2i(-9, -9))) + "flag_pull": + return _do_flag_pull(intent.get("cell", Vector2i(-9, -9))) + "move": + return _do_move(intent.get("cell", Vector2i(-9, -9))) + "stop": + return _do_stop() + push_warning("[A2] 目录外意图: %s" % str(intent)) + return _outcome(false, "rejected", "board_face", "out_of_catalog") + + +# ---- 挖掘 ---- + +func _do_dig(c: Vector2i) -> Dictionary: + var sub := _board_face_dig(c) + if sub != "": + return _outcome(false, "rejected", "board_face", sub) + # 真模拟连锁与清图(F2/F1——单一实现两名字) + var chain := _chain_closure(c, _flags) + var clears: bool = _cleared_after(chain) + # F2 口径:chain_size = |O′ ∖ (O ∪ {c})|——闭包含已开集 O,只数新开格(不含首格) + var chain_size := 0 + for cc in chain.keys(): + if cc != c and not _revealed.has(cc): + chain_size += 1 + # 资源面/相位 + cleared 仲裁在 A3 事务内原子完成 + if not a3.request_dig(chain_size, clears): + return _outcome(false, "rejected", "resource_face", "") + # 事务内执行:首格 + 连锁新格揭示(BFS 规范序,含链开雾格——零特判);事件 = O′ ∖ O + var reveal_order: Array = _chain_reveal_order(c, _flags) + for rc in reveal_order: + if _revealed.has(rc): + continue # 闭包可穿过已开区——已开格零事件(cell_revealed × |O′ ∖ O|) + var is_mine := _board.is_mine(rc) + var channel := "dig" if rc == c else "chain" + if is_mine: + _revealed[rc] = {"kind": "mine", "digit": -1} + cell_revealed.emit({ + "cell": rc, "kind": "mine", "digit": -1, + "variant_ids": [], "channel": channel, "reward_eligible": false, + }) + else: + _revealed[rc] = {"kind": "safe", "digit": _board.digit_of(rc)} + cell_revealed.emit({ + "cell": rc, "kind": "safe", "digit": _board.digit_of(rc), + "variant_ids": _board.variant_ids(rc), "channel": channel, "reward_eligible": true, + }) + if a3.phase != a3.PHASE_ACTIVE: + _emit_round_end_flags_and_payload() + return _outcome(true, "executed", "", "") + + +func _board_face_dig(c: Vector2i) -> String: + if not _board.in_bounds(c): + return "out_of_catalog" + if not _adjacent_to_hero(c): + return "not_adjacent" + if is_revealed(c): + return "target_revealed" + if has_flag(c): + return "flag_present" + return "" + + +# 连锁闭包:μX.[O∪{c} ⊆ X ∧ ∀x∈X:(x∈S ∧ num(x)=0 ⇒ N₈(x)∩S∖G ⊆ X)]——连锁不越旗 +func _chain_closure(start: Vector2i, flag_set: Dictionary) -> Dictionary: + var chain := {start: true} + var queue: Array = [start] + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if _board.is_safe(c) and not flag_set.has(c) and _board.digit_of(c) == 0 and _board.variant_ids(c).is_empty(): + for n in _board.neighbors8(c): + if _board.is_safe(n) and not flag_set.has(n) and not chain.has(n): + chain[n] = true + queue.append(n) + return chain + + +# 揭示规范序:首格 + 触发格 BFS、8 邻行主序 +func _chain_reveal_order(start: Vector2i, flag_set: Dictionary) -> Array: + var order: Array = [start] + var seen := {start: true} + var queue: Array = [start] + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if _board.is_safe(c) and not flag_set.has(c) and _board.digit_of(c) == 0 and _board.variant_ids(c).is_empty(): + for n in _board.neighbors8(c): + if _board.is_safe(n) and not flag_set.has(n) and not seen.has(n): + seen[n] = true + order.append(n) + queue.append(n) + return order + + +func _cleared_after(chain: Dictionary) -> bool: + for c in _board.safe_cells(): + if not _revealed.has(c) and not chain.has(c): + return false + return true + + +# ---- 旗 ---- + +func _do_flag_place(c: Vector2i) -> Dictionary: + var sub := "" + if not _board.in_bounds(c): + sub = "out_of_catalog" + elif not _adjacent_to_hero(c): + sub = "not_adjacent" + elif is_revealed(c): + sub = "target_revealed" + elif has_flag(c): + sub = "flag_present" + if sub != "": + return _outcome(false, "rejected", "board_face", sub) + if not a3.try_spend("flag_place"): + return _outcome(false, "rejected", "resource_face", "") + _flags[c] = true # 旗动作零格子事件外发(发射面封闭;反馈经调用返回值命令式驱动) + if a3.phase != a3.PHASE_ACTIVE: + # 旗动作花完末点 AP 同样终结轮次(A3 三出口——AP 用完与动作种类无关;缺漏使 round_end + # 永不发出、B1 的 Waiting 结算点永不到达 = 整局软锁。playtest 缺陷②,行为探针复现) + _emit_round_end_flags_and_payload() + return _outcome(true, "executed", "", "") + + +func _do_flag_pull(c: Vector2i) -> Dictionary: + var sub := "" + if not _board.in_bounds(c): + sub = "out_of_catalog" + elif not _adjacent_to_hero(c): + sub = "not_adjacent" + elif not has_flag(c): + sub = "flag_absent" + if sub != "": + return _outcome(false, "rejected", "board_face", sub) + if not a3.try_spend("flag_pull"): + return _outcome(false, "rejected", "resource_face", "") + _flags.erase(c) + if a3.phase != a3.PHASE_ACTIVE: + _emit_round_end_flags_and_payload() # 同 flag_place——末点 AP 轮末发射(playtest 缺陷②) + return _outcome(true, "executed", "", "") + + +# ---- 移动(瞬移;免费不经账本;BFS 限已探明区含已引爆雷格)---- + +func _do_move(c: Vector2i) -> Dictionary: + var sub := "" + if not _board.in_bounds(c): + sub = "out_of_catalog" + elif not is_revealed(c): + sub = "target_concealed" + elif c != _hero and compute_move_path(c).is_empty(): + sub = "no_path" + if sub != "": + return _outcome(false, "rejected", "board_face", sub) + _hero = c # 目标 = 当前格定义为零移动(幂等受理) + return _outcome(true, "executed", "", "") + + +func compute_move_path(target: Vector2i) -> Array: + if _board == null or not is_revealed(target): + return [] + if target == _hero: + return [] + # BFS(8 邻行主序)——路径 = 合法性载体 + D1 预览数据源 + var prev := {_hero: _hero} + var queue: Array = [_hero] + while not queue.is_empty(): + var c: Vector2i = queue.pop_front() + if c == target: + break + for n in _board.neighbors8(c): + if is_revealed(n) and not prev.has(n): + prev[n] = c + queue.append(n) + if not prev.has(target): + return [] + var path: Array = [] + var cur := target + while cur != _hero: + path.push_front(cur) + cur = prev[cur] + return path + + +# ---- 停手(经批次入口委托 A3;先发旗判定批再委托——事件序契约 flag_judged < round_end 全路径统一)---- + +func _do_stop() -> Dictionary: + _emit_flags_batch() # 基线 = 事务开始旗位(停手不改旗位) + if not a3.request_stop(): + return _outcome(false, "rejected", "phase", "") + # A3 直发 stopped 路径 round_end;A2 禁双发(双发射源纪律) + return _outcome(true, "executed", "", "") + + +# ---- 轮末旗判定 + 棋盘路径 round_end 外发 ---- + +func _emit_flags_batch() -> void: + # 判定基线 = 轮次结束事务开始时旗位;行主序枚举(确定性);被拔的不判;诚实重复上报(去重归 B1) + var cells: Array = _flags.keys() + cells.sort_custom(func(a, b): return a.y * 1000 + a.x < b.y * 1000 + b.x) + for c in cells: + flag_judged.emit({"cell": c, "hit": _board.is_mine(c)}) + + +func _emit_round_end_flags_and_payload() -> void: + _emit_flags_batch() + var payload: Dictionary = a3.last_round_end # 逐字段取自 A3(读取权 = 接口契约) + round_end_board.emit({"path_tag": payload.get("path_tag", ""), "continuation_hint": payload.get("continuation_hint", "")}) + + +func _adjacent_to_hero(c: Vector2i) -> bool: + var d := Vector2i(absi(c.x - _hero.x), absi(c.y - _hero.y)) + return maxi(d.x, d.y) == 1 + + +func _outcome(accepted: bool, outcome: String, reason: String, sub: String) -> Dictionary: + return {"accepted": accepted, "outcome": outcome, "reject_reason": reason, "sub_reason": sub} diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd.uid new file mode 100644 index 00000000..96caf518 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd.uid @@ -0,0 +1 @@ +uid://dlcnq7jcxrnwr diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd b/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd new file mode 100644 index 00000000..f1728493 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd @@ -0,0 +1,347 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 可选类供给能否按"格极稀 / 板≈0.78 / 战斗期望≈1(首件保证 + 指数递减)"三尺度被精确控制 +# Date: 2026-09-10 +# B4 奖励与道具经济:install 期身份预分配(roll_board 恰一次、消费 reward_roll 封闭流恰一 scope 批)· +# 授予期零随机 · 库存关卡域(battle_over 全清)· 道具伤害恒定表值 · Ready 后只读。 +# 2026-09-11 可选类供给控制器批(用户拍板):格子池只剩即时类;可选类经 RewardSupplyController 注入 +# (战斗首件保证 + p0×decay^(j-1) 指数递减);旧 F5 格池刀保底(k_dmg)休眠;清图槽改一次性伤害。 +# 2026-09-14 伤害量值区间批(用户拍板②):区间行(power_min/max 可选配对)在身份预分配批内追加取号 +# (④ 步,既有取号序零扰动);快照 E14 口径不变——快照 power = roll 值,B1/呈现零改动;授予期零随机不变。 +# 切片登记:取号约定 = randi_range(0, W-1)(实现期 ADR 事实,切片内恒定)。 +# 实现 GDD:design/gdd/b4-reward-item-economy.md(系统 ID B4——registry 追溯锚,命名规范见 CLAUDE.md) +class_name RewardEconomy +extends RefCounted + +var db +var rng +var _supply: RewardSupplyController # 可选类供给状态机(组合件——战斗域计数) +var _phase := "unrolled" # unrolled → ready → retired;clear_inventory 后 _inventory_cleared +var _inventory_cleared := false +var _eligible: Array = [] # [{cell, fog}] 行主序规范序 +var _mapping := {} # Vector2i → reward_id +var _power_roll := {} # Vector2i → 区间行 roll 值(伤害量值区间批——身份批内追加取号) +var _clear_power := -1 # 清图槽区间行 roll 值(-1 = 定值行) +var _granted := {} # Vector2i → 快照(重复受理幂等返回) +var _clear_identity := "" +var _clear_granted := false +var _clear_snapshot := {} +var _inventory: Array = [] # 逐件实例 {item_id, snapshot, source_cell, round_index, status} + + +func _init(db_ref, rng_ref) -> void: + db = db_ref + rng = rng_ref + _supply = RewardSupplyController.new(db.reward_config) + + +# ---- 战斗开始(B1.begin_battle 编排调用——B4 唯一调用方纪律不变)---- +# 供给控制器战斗域计数清零;库存(关卡域)不受影响——关内延续照旧 +func notify_battle_start() -> void: + _supply.notify_battle_start() + + +# ---- F1/F2/F5:install 期 roll(恰一次;Ready 相位再调拒绝)---- + +func roll_board(eligible: Array) -> bool: + # 相位守卫:Ready 再调 = double-roll 拒绝(E5);Retired(换图后)= 合法新批重置(E6 roll 半) + if _phase == "ready": + push_warning("[B4] roll_board 相位守卫拒绝(E5——double-roll 编排违约显式暴露)") + return false + if eligible.is_empty(): + _fatal("roll_board 空资格清单(E1 兜底 fail-fast——A2 不变式④前置)") + return false + # 行主序规范化 + 去重(排序责任不外推给 B1) + var seen := {} + var list: Array = [] + for e in eligible: + var c: Vector2i = e.cell + if seen.has(c): + push_warning("[B4] 资格清单重复格已去重: %s" % str(c)) + continue + seen[c] = true + list.append({"cell": c, "fog": bool(e.get("fog", false))}) + list.sort_custom(func(a, b): return a.cell.y * 1000 + a.cell.x < b.cell.y * 1000 + b.cell.x) + _eligible = list + _granted.clear() # 新批重置映射受理面(旧图快照消亡) + _power_roll.clear() + _clear_power = -1 + _clear_granted = false + _clear_snapshot = {} + var stream: RandomNumberGenerator = rng.open_stream("reward_roll") # 恰一 scope 批(身份批 + 清图槽 + 注入判定/选格 + 区间行 power roll——追加式取号,既有序零扰动) + var safe_rows: Array = _pool_rows("safe_cell") + var clear_rows: Array = _pool_rows("clear_default") + var inject_rows: Array = _pool_rows("active_inject") + # ① 身份批:每资格格恰一即时类身份(可选类已退出格子池——供给控制器批;雾档过滤不变) + var draft := {} + for e in list: + var pool := safe_rows.filter(func(row): + if e.fog and int(row.tier) < int(db.reward_config.t_fog_min): + return false # F2 雾档过滤 + return true) + draft[e.cell] = weighted_pick(pool, stream.randi_range(0, _pool_weight(pool) - 1)) + # ② 清图槽:一次性高额伤害(当场兑付类——不入库、不占可选类供给;2026-09-11 用户拍板) + var clear_rid := weighted_pick(clear_rows, stream.randi_range(0, _pool_weight(clear_rows) - 1)) + # ③ 可选类供给注入(替代旧 F5 刀保底):首件保证 / p0×decay^(j-1) 指数递减——判定恰一取号 + if _supply.should_inject(stream): + var target = list[stream.randi_range(0, list.size() - 1)] # 均匀随机(拍板③——雾格不加权) + draft[target.cell] = weighted_pick(inject_rows, stream.randi_range(0, _pool_weight(inject_rows) - 1)) + # ④ 伤害量值区间批(2026-09-14):身份定稿后逐格 roll power(区间行恰一取号,定值行零取号); + # 授予期零随机纪律不变——快照 E14 口径,B1/呈现零改动 + for cell in draft.keys(): + var row: Dictionary = _row_by_id(draft[cell]) + if row.has("power_min"): + _power_roll[cell] = stream.randi_range(int(row.power_min), int(row.power_max)) + var clear_row: Dictionary = _row_by_id(clear_rid) + if clear_row.has("power_min"): + _clear_power = stream.randi_range(int(clear_row.power_min), int(clear_row.power_max)) + _mapping = draft + _clear_identity = clear_rid + _phase = "ready" + return true + + +# ---- 关卡节点奖励(2026-09-11 关卡扩展批——线性奖励点,正式归属 B4/C1 修订)---- +# 奖励点 = 用户拍板的关卡节点类型:在本表全部可选类活行(damage_item / heal_pack,即"主动道具") +# 中 roll 一件并直接入库。消耗 reward_roll 恰一 scope(与 board install 批同流异 scope——流集合封闭不变)。 +# cell=(-1,-1)/round_index=0 = 非格子来源标记(关卡节点授予,区别于安全格拾取的呈现面)。 +func grant_node_active(cell: Vector2i, round_index: int) -> Dictionary: + var pool: Array = [] + for row in db.reward.rows: + if int(row.weight) > 0 and (String(row.category) == "damage_item" or String(row.category) == "heal_pack"): + pool.append(row) + if pool.is_empty(): + _fatal("节点奖励池为空(damage_item/heal_pack 零活行)") + return {} + var stream: RandomNumberGenerator = rng.open_stream("reward_roll") + var rid := weighted_pick(pool, stream.randi_range(0, _pool_weight(pool) - 1)) + var row: Dictionary = _row_by_id(rid) + var rolled := -1 + if row.has("power_min"): + rolled = stream.randi_range(int(row.power_min), int(row.power_max)) # 区间行恰一追加取号(伤害量值区间批) + var inst = stock(_snapshot(row, rolled), cell, round_index) + if inst is Dictionary: + _supply.notify_granted() # 节点奖励 = 可选类实际发放——与 grant() 同一入账点(playtest-2026-09-12 报告 Bug#3:绕过供给计数修正) + return inst + + +# ---- 查询面(Ready 只读)---- + +func query_identity(cell: Vector2i): + if _phase != "ready": + _diag("query_identity 非 Ready 相位") + return null + var rid: String = _mapping.get(cell, "") + if rid == "": + _diag("query_identity 清单外坐标(E19——未分配非错误态)") + return null + var row: Dictionary = _row_by_id(rid) + return {"reward_id": rid, "category": String(row.category), "tier": int(row.tier)} + + +# 身份完整快照(轮内刷新批次隔离用——facts 缓冲期捕获,授予期 mapping 可能已换批): +# 返回与 grant 相同结构的快照(display_name/power/effect_id 齐备),供 B1 冻结授予直通 +func query_full_snapshot(cell: Vector2i): + if _phase != "ready": + _diag("query_full_snapshot 非 Ready 相位") + return null + var rid: String = _mapping.get(cell, "") + if rid == "": + return null + return _snapshot(_row_by_id(rid), int(_power_roll.get(cell, -1))) + + +# ---- 受理面(B1 编排期调用;E6 收口 2026-09-13:失败统一返回 null——此前 false/null 混用)---- + +func grant(cell: Vector2i): + if _phase != "ready": + _diag("grant 非 Ready 相位(E4/E7)") + return null + if not _mapping.has(cell): + _diag("grant 清单外坐标(E19)") + return null + if _granted.has(cell): + _diag("grant 同格重复受理(E18 幂等兜底——受理恰一次归 B1)") + return _granted[cell] + var row: Dictionary = _row_by_id(_mapping[cell]) + var snap := _snapshot(row, int(_power_roll.get(cell, -1))) + if String(snap.category) == "damage_item" or String(snap.category) == "heal_pack": + _supply.notify_granted() # 发放即获取——首件保证/递减计数的唯一入账点(发放侧语义,满血拒"使用"不影响) + _granted[cell] = snap + return snap + + +func grant_clear(): + if _phase != "ready": + _diag("grant_clear 非 Ready 相位") + return null + if _clear_granted: + _diag("grant_clear 同批次重调(E18 幂等兜底)") + return _clear_snapshot + var row: Dictionary = _row_by_id(_clear_identity) + _clear_snapshot = _snapshot(row, _clear_power) + _clear_granted = true + return _clear_snapshot + + +func stock(snapshot: Dictionary, source_cell: Vector2i, round_index: int): + if _inventory_cleared: + _diag("stock 战斗已清库(E7)") + return null + var cat: String = String(snapshot.get("category", "")) + if cat != "damage_item" and cat != "heal_pack": + _diag("stock 仅受理道具类(E20): %s" % cat) + return null + var inst := { + "item_id": String(snapshot.get("reward_id", "")), + "snapshot": snapshot, + "source_cell": source_cell, + "round_index": round_index, + "status": "InStock", + } + _inventory.append(inst) # 数量无上限;枚举序 = stock 受理序(E21) + return inst + + +# 窗口选用消耗:零游戏态守卫(照常消耗——E10;满血/零效果守卫归 B1) +func use(index: int): + if _inventory_cleared: + _diag("use 战斗已清库") + return null + if index < 0 or index >= _inventory.size(): + _diag("use 句柄越界") + return null + var inst: Dictionary = _inventory[index] + if inst.status != "InStock": + _diag("use 已消耗句柄(E9 Used 不可逆)") + return null + inst.status = "Used" + return { + "item_id": inst.item_id, + "category": String(inst.snapshot.get("category", "")), + "power": int(inst.snapshot.get("power", 0)), # 授予时快照值(E14——非表读值) + "source_cell": inst.source_cell, + "round_index": inst.round_index, + } + + +func query_inventory() -> Array: + var out: Array = [] + for i in _inventory.size(): + var inst: Dictionary = _inventory[i] + out.append({ + "index": i, + "item_id": inst.item_id, + "category": String(inst.snapshot.get("category", "")), + "display_name": String(inst.snapshot.get("display_name", "")), + "power": int(inst.snapshot.get("power", 0)), + "source_cell": inst.source_cell, + "round_index": inst.round_index, + "status": inst.status, + }) + return out + + +func in_stock_count() -> int: + var n := 0 + for inst in _inventory: + if inst.status == "InStock": + n += 1 + return n + + +# 观测面(冒烟/调参工具——本战斗已实际获取的可选类件数) +func supply_granted_count() -> int: + return _supply.granted_count() + + +func clear_inventory() -> bool: + if _inventory_cleared: + _diag("clear_inventory 重复调用(幂等守卫)") + return false + _inventory.clear() # battle_over 全清不跨战斗 + _inventory_cleared = true + return true + + +func retire_mapping() -> bool: + if _phase != "ready": + _diag("retire_mapping 非 Ready 相位(幂等受理族)") + return false + _phase = "retired" + _mapping.clear() + _power_roll.clear() + _clear_power = -1 + _clear_identity = "" + return true + + +# ---- F6 当量供给(B1 条款①对照实物输入)---- + +func equivalence_supply() -> Dictionary: + var live: Array = [] + var tier_min_buff := 99 + for row in db.reward.rows: + if int(row.weight) <= 0: + continue + live.append(row.duplicate()) # E6:表行值拷贝出口——消费方禁改配置源 + if row.pool_tag == "safe_cell" and row.category == "buff": + tier_min_buff = mini(tier_min_buff, int(row.tier)) + return {"live_rows": live, "tier_min_buff": tier_min_buff} + + +# ---- F1 选行算子纯函数 ---- + +func weighted_pick(rows: Array, r: int) -> String: + var total := _pool_weight(rows) + assert(r >= 0 and r < total, "weighted_pick 取号越域(纯函数域守卫)") + var acc := 0 + for row in rows: + acc += int(row.weight) + if r < acc: + return String(row.reward_id) + _fatal("weighted_pick 未命中(不应可达)") + return "" + + +func _pool_rows(pool_tag: String) -> Array: + var out: Array = [] + for row in db.reward.rows: + if row.pool_tag == pool_tag and int(row.weight) > 0: + out.append(row) + return out + + +func _pool_weight(rows: Array) -> int: + var w := 0 + for row in rows: + w += int(row.weight) + return w + + +func _row_by_id(rid: String) -> Dictionary: + for row in db.reward.rows: + if row.reward_id == rid: + return row + return {} + + +func _snapshot(row: Dictionary, rolled_power: int = -1) -> Dictionary: + var snap := { + "reward_id": String(row.reward_id), + "category": String(row.category), + "display_name": String(row.display_name), + "power": int(row.power) if rolled_power < 0 else rolled_power, # 区间行 = 身份批 roll 值(E14 授予时快照口径) + "tier": int(row.tier), + } + if String(row.get("effect_id", "")) != "": + snap["effect_id"] = String(row.effect_id) + return snap + + +func _diag(msg: String) -> void: + push_warning("[B4] " + msg) + + +func _fatal(msg: String) -> void: + push_error("[B4/fatal] " + msg) + assert(false, msg) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd.uid new file mode 100644 index 00000000..f70f12dd --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd.uid @@ -0,0 +1 @@ +uid://5vj3p6fmval7 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd b/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd new file mode 100644 index 00000000..a99a2e56 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd @@ -0,0 +1,43 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 可选类供给能否按"格极稀 <0.01 / 板≈0.78 / 战斗期望≈1(首件保证 + 指数递减)"三尺度被精确控制 +# Date: 2026-09-11 +# 可选类供给控制器(2026-09-11 用户拍板批——B4 私有组合件): +# 格子权重池不再产出可选类;本控制器在 install 期决定"本板是否注入 1 件可选类"—— +# 战斗内尚未获取首件 → 本板必注入(首件保证);已获取 j 件 → 概率 p0×decay^(j-1)(指数下降、永不绝育)。 +# 行值快照(p0/decay 构造期快照——抗热重载,B2 同款);零自有随机数(判定取号经 B4 传入的 reward_roll +# 封闭流实例——Rule 5);"获取"以实际发放入库为准(预分配未挖到不计数——授予侧经 B4.grant 挂钩上报)。 +# 实现 GDD:design/gdd/b4-reward-item-economy.md(系统 ID B4——registry 追溯锚,命名规范见 CLAUDE.md) +class_name RewardSupplyController +extends RefCounted + +var _granted := 0 # 本战斗内已实际获取的可选类件数(战斗域——notify_battle_start 清零) +var _p0 := 0.4 +var _decay := 0.5 + + +func _init(reward_config) -> void: + _p0 = clampf(float(reward_config.active_p0), 0.0, 1.0) + _decay = clampf(float(reward_config.active_decay), 0.001, 1.0) + + +# B1.begin_battle 编排调用(B4 转发)——战斗域计数重置;跨战斗库存延续不受影响 +func notify_battle_start() -> void: + _granted = 0 + + +# install 期注入判定(B4.roll_board 在身份批 + 清图槽之后调用;取号 = 传入封闭流恰一次) +func should_inject(stream: RandomNumberGenerator) -> bool: + if _granted == 0: + return true # 首件保证 + var p: float = _p0 * pow(_decay, float(_granted - 1)) + return stream.randf() < p + + +# B4.grant 实际发放可选类时上报(发放即获取;满血拒"使用"不影响——那是消耗侧语义) +func notify_granted() -> void: + _granted += 1 + + +# 观测面(冒烟/调参工具) +func granted_count() -> int: + return _granted diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd.uid new file mode 100644 index 00000000..fabba169 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd.uid @@ -0,0 +1 @@ +uid://de40p45nneaw1 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd new file mode 100644 index 00000000..f2b7091d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd @@ -0,0 +1,88 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 RngService autoload——封闭命名流单点(Rule 5)。玩法代码禁止 randomize()/自建 RandomNumberGenerator。 +# 实现 GDD:design/gdd/d2-data-config.md(系统 ID D2——registry 追溯锚,命名规范见 CLAUDE.md) +# R2 派生公式(黄金向量已预验证):derive = (SALT XOR wrap64(master + scope×GOLDEN)) AND 0x7FFF… +# 取号语义:全新实例首个 open_stream = scope 0(B4 预推进术语的登记口径)。 +extends Node + +const STREAM_NAMES := ["board_gen", "reward_roll", "encounter"] # 2026-09-12 增 encounter(遭遇随机化批,用户拍板——registry rng_service 条目同步) + +# 分段表达式书写(D2 R2 实现级规约——禁 ≥2^63 完整字面量) +const GOLDEN_RATIO := (0x9E3779B9 << 32) | 0x7F4A7C15 # 0x9E3779B97F4A7C15 +const SALT_BOARD_GEN := (0xA0761D64 << 32) | 0x78BD642F # 0xA0761D6478BD642F +const SALT_REWARD_ROLL := (0xE7037ED1 << 32) | 0xA0B428DB # 0xE7037ED1A0B428DB +const SALT_ENCOUNTER := (0x5DEECE66D << 32) | 0x2545F491 # 0x5DEECE66D2545F491(遭遇骰——2026-09-12) +const OUT_MASK := 0x7FFFFFFFFFFFFFFF +const INT64_MIN := -9223372036854775808 + +# master_seed 只读出口(E5 封装收口 2026-09-13):写入只经 set_master_seed(计数器全零断言 +# 不可绕过);外部读 master_seed 供 D3 快照契约观测。 +var _master_seed: int = -1 # -1 = 未播种(取数即致命,EC7) +var master_seed: int: + get: return _master_seed +var _counters := {"board_gen": 0, "reward_roll": 0, "encounter": 0} + + +func _salt_for(stream: String) -> int: + match stream: + "board_gen": + return SALT_BOARD_GEN + "reward_roll": + return SALT_REWARD_ROLL + "encounter": + return SALT_ENCOUNTER + push_error("[RngService] 未知名流: %s" % stream) + assert(false, "未知名流(流集合封闭,EC12)") + return 0 + + +# 纯函数(测试/预览入口,不推计数器) +func derive_at(stream: String, scope: int) -> int: + assert(scope >= 0, "负 scope 与 scope+2^63 经输出掩码别名(禁止)") + var t: int = _master_seed + scope * GOLDEN_RATIO # wrap64 自然回绕——禁 float 中间量/禁 %/禁主动修复 + var seed_val: int = _salt_for(stream) ^ t + return seed_val & OUT_MASK + + +func set_master_seed(seed_val: int) -> void: + if seed_val == INT64_MIN: + push_error("[RngService] master_seed = INT64_MIN 致命(abs 恒等回绕,与 0 静默别名)") + assert(false, "INT64_MIN 致命") + return + if seed_val < 0: + seed_val = -seed_val + push_warning("[RngService] master_seed 负输入 abs 归一化: %d" % seed_val) + assert(_counters["board_gen"] == 0 and _counters["reward_roll"] == 0 and _counters["encounter"] == 0, "冒险中途重播种会击穿同冒险无碰撞前提(仅许全部流计数器全零时调用)") + _master_seed = seed_val + _counters["board_gen"] = 0 + _counters["reward_roll"] = 0 + + +# 切片登记:冒险开始重播种入口(正式侧调用权归 C2;切片主菜单 = 冒险起点) +func new_adventure(seed_val: int) -> void: + _counters["board_gen"] = 0 + _counters["reward_roll"] = 0 + _counters["encounter"] = 0 + set_master_seed(seed_val) + + +# 有状态流门面:一次调用 = 一次 scope 取号(计数器读值 → 派生 → +1) +func open_stream(stream: String) -> RandomNumberGenerator: + if not STREAM_NAMES.has(stream): + push_error("[RngService] 未知名流: %s" % stream) + assert(false, "未知名流(EC12)") + if _master_seed < 0: + push_error("[RngService] 未设 master_seed 即取数(EC7——C2 未接线的显式暴露)") + assert(false, "未播种流取数致命(EC7)") + var scope: int = _counters[stream] + _counters[stream] = scope + 1 + var rg := RandomNumberGenerator.new() + rg.seed = derive_at(stream, scope) + return rg + + +# 只读计数器(D3 快照契约的观测面) +func counter(stream: String) -> int: + return _counters[stream] diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd.uid new file mode 100644 index 00000000..503238f1 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd.uid @@ -0,0 +1 @@ +uid://tcp13u2etw2n diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd b/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd new file mode 100644 index 00000000..df3213d4 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd @@ -0,0 +1,137 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# B3 统一状态容器:三向效果同构实例 · 单一时限量纲 remaining_settlements(先兑付后衰减)· +# 按行叠层(stack 授予不触碰 R)· 授予时数值快照抗热重载 · 零随机零信号。 +# 注:MVP 单敌假设经 B2 回验不成立(定多敌)——enemy 侧 side-wide 语义按 registry 改写执行。 +# 实现 GDD:design/gdd/b3-status-effect.md(系统 ID B3——registry 追溯锚,命名规范见 CLAUDE.md) +class_name StatusContainer +extends RefCounted + +var db +var _open := false +var _instances: Array = [] # 插入序;元素 = {effect_id, layers, remaining, source_class, origin_cell, round_index, snap_m, snap_unit, snap_display} + + +func _init(db_ref) -> void: + db = db_ref + + +func notify_battle_start() -> bool: + if _open: + push_warning("[B3] Active 下重复 notify_battle_start(EC11)") + return false + _open = true + _instances.clear() + return true + + +# 唯一授予入口(挖掘期零直写——B1 结算编排期统一调用) +func apply_effect(effect_id: String, source: Dictionary) -> bool: + if not _open: + push_warning("[B3] 域闭授予(EC1)") + return false + var row: Dictionary = db.status_effect_row(effect_id) + if row.is_empty(): + push_warning("[B3] 未知 effect_id(EC2): %s" % effect_id) + return false + var policy: String = row.stack_policy + var dur: int = int(row.duration_settlements) + var max_stack: int = int(row.max_stack) + # 授予时快照(抗表重载——EC9) + var snap_m: int = int(row.magnitude) + var snap_unit: String = String(row.unit) + var snap_display: String = String(row.display_name) + match policy: + "independent": + _instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display)) + return true + "refresh": + for inst in _instances: + if inst.effect_id == effect_id: + inst.remaining = dur # R ← D(取实例快照 D——同表值);L 不变;空刷新恒 true + return true + _instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display)) + return true + "stack": + for inst in _instances: + if inst.effect_id == effect_id: + if inst.layers >= max_stack: + return false # 满层拒绝原子——容器零写入(溢出行归 B1) + inst.layers += 1 # stack 授予不触碰 R(spec 决议) + return true + _instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display)) + return true + push_warning("[B3] 未知 stack_policy") + return false + + +func _new_instance(effect_id: String, layers: int, dur: int, source: Dictionary, m: int, unit: String, display: String) -> Dictionary: + return { + "effect_id": effect_id, + "layers": layers, + "remaining": dur, + "source_class": String(source.get("source_class", "")), + "origin_cell": source.get("origin_cell", Vector2i(-1, -1)), + "round_index": int(source.get("round_index", 0)), + "snap_m": m, + "snap_unit": unit, + "snap_display": display, + } + + +# 只读投影(插入序稳定枚举;六归因字段 + display_name + 授予时快照) +func query_active(side: String) -> Array: + var out: Array = [] + for inst in _instances: + var row: Dictionary = db.status_effect_row(inst.effect_id) + if row.is_empty(): + continue + if String(row.target_side) != side: + continue + out.append({ + "effect_id": inst.effect_id, + "layers": inst.layers, + "remaining_settlements": inst.remaining, + "source_class": inst.source_class, + "origin_cell": inst.origin_cell, + "round_index": inst.round_index, + "display_name": inst.snap_display, + "snap_magnitude": inst.snap_m, + "snap_unit": inst.snap_unit, + "effective_value": inst.layers * inst.snap_m, # L×m 加算(percent 越域不 clamp——收口归 B1) + }) + return out + + +# 池层数读值(B1 战斗段伏击池 L 的消费面) +func pool_layers(effect_id: String) -> int: + for inst in _instances: + if inst.effect_id == effect_id: + return inst.layers + return 0 + + +# 轮末衰减:先兑付后衰减;每实例恰 −1;归 0 移除;返回移除数 k(恰一次执行由 B1 编排信任承载) +func decay_round() -> int: + if not _open: + return 0 # EC13 域闭兜底读法——容器恒空,无独立分支不诊断 + var k := 0 + var survivors: Array = [] + for inst in _instances: + inst.remaining -= 1 + if inst.remaining <= 0: + k += 1 + else: + survivors.append(inst) + _instances = survivors + return k + + +func notify_battle_end() -> bool: + if not _open: + push_warning("[B3] BattleClosed 下重复 notify_battle_end(EC11)") + return false + _open = false + _instances.clear() # 全清不跨战斗 + return true diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd.uid new file mode 100644 index 00000000..01b29a35 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd.uid @@ -0,0 +1 @@ +uid://dlck3xxei0t61 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd b/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd new file mode 100644 index 00000000..0d171b05 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd @@ -0,0 +1,194 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# B2 战斗单位系统:hero/enemy 统一受伤死亡接口 · 行为环确定性轮转(死亡即不动手/先作用后推进)· +# 行值快照抗热重载 · side-wide 伏击池读值由调用方注入(B2 无池写面)· 编排被动零事件。 +# 切片登记接口漂移:heal() 为切片新增(B1 治疗 clamp 需要写入口;B2 API 封闭枚举原本无治疗面—— +# 正式侧应走 B2 GDD 修订,切片以头注登记)。 +# 实现 GDD:design/gdd/b2-enemy-system.md(系统 ID B2——registry 追溯锚,命名规范见 CLAUDE.md) +class_name UnitContainer +extends RefCounted + +var db +# 单位容器私有(E3 封装收口 2026-09-13):hero/enemies 原始数组不再外露——读取经 hero_unit()/ +# all_enemies() 投影面(死亡残位查询包含在内),写入只经 apply_damage/heal/advance/spawn/despawn。 +# 句柄语义不变:投影返回的 Dictionary 引用即单位句柄(同 query_alive 先例)。 +var _hero: Dictionary = {} # {hp, max_hp, atk, is_hero: true} +var _enemies: Array = [] # 行主序生成序;元素 = 单位 Dictionary +var _spawned := false +var _despawned := false + + +func _init(db_ref) -> void: + db = db_ref + + +# E4 收口(2026-09-13):加成值经参数显式传入(atk_override/max_hp_override,-1 = 表值兜底)—— +# 此前调用方写 db.hero 表值再由本类读表 = 共享可变单例传参(双写叠加 6→18→54 教训根源)。 +# 接口漂移登记:spawn 签名 +2 参数(B2 GDD API 封闭枚举——正式侧 GDD/registry 修订批随 ADR-0009)。 +# enemy_atk_override(2026-09-14 敌方攻击区间批):B1 遭遇骰同流的 spawn 期整战持久 roll 结果 +# (-1 = 表值定值路径;≥0 = 行值替换后再乘关卡 atk_mult——修饰语义不变)。B2 零随机纪律保持。 +func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_override: int = -1, + atk_override: int = -1, max_hp_override: int = -1, enemy_atk_override: int = -1) -> bool: + if _spawned: + push_warning("[B2] Alive 相位重复 spawn(相位守卫)") + return false + var row: Dictionary = db.resolve_enemy_rowset(level_index, node_depth) + if row.is_empty(): + push_error("[B2] 遭遇行解析失败/越域 (%d,%d)" % [level_index, node_depth]) + assert(false, "遭遇行悬空(R1 前置守卫)") + return false + var eff_atk: int = atk_override if atk_override >= 1 else int(db.hero.atk) + var eff_max_hp: int = max_hp_override if max_hp_override >= 1 else int(db.hero.max_hp) + if hero_hp > eff_max_hp or hero_hp <= 0: + push_warning("[B2] hero_hp 入参越界(EC16): %d" % hero_hp) + return false + # max_hp = 生效上限(非入参 hero_hp)——2026-09-11 关卡扩展批修复:跨战斗携带 HP 时上限不得压扁 + # (恢复点/治疗包的满血守卫与治疗上限随之失效——切片登记接口语义澄清) + _hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": "探险者", "steps": 0, "pattern": [], "enemy_id": "hero"} + _enemies.clear() # C3 DifficultyTable 修饰(2026-09-12 结构位兑现):施加于行值、行值快照之前——MVP 空表 = 恒 1.0 无行为(AC-10) + var mod: Dictionary = db.resolve_difficulty_modifiers(level_index, node_depth) + var hp_mult: float = float(mod.get("hp_mult", 1.0)) + var atk_mult: float = float(mod.get("atk_mult", 1.0)) + # unit_count_override ≥1 = B1 遭遇骰结果(B2 零随机纪律——收到即确定值;-1 = 行值定值路径) + var count: int = unit_count_override if unit_count_override >= 1 else int(row.unit_count) + if count < 1 or count > 3: + push_warning("[B2] 实例化数量越域: %d" % count) + return false + for i in range(count): + var eff_row_atk: int = enemy_atk_override if enemy_atk_override >= 0 else int(row.atk) + _enemies.append({ + "handle_id": i, + "enemy_id": String(row.enemy_id), + "display_name": String(row.display_name), + "hp": maxi(1, int(round(float(row.hp) * hp_mult))), + "max_hp": maxi(1, int(round(float(row.hp) * hp_mult))), + "atk": maxi(0, int(round(float(eff_row_atk) * atk_mult))), + "heavy_multiplier": int(row.heavy_multiplier), + "pattern": Array(row.pattern), # 行值快照(拷贝) + "advantage_per_round_cap": int(row.advantage_per_round_cap), + "armor_type": String(row.get("armor_type", "none")), # 护甲类型快照(克制矩阵消费面——B1 战斗段) + "steps": 0, + "alive": true, + "is_hero": false, + }) + _spawned = true + _despawned = false + return true + + +func query_alive(side: String) -> Array: + if _despawned: + _diag("Despawned 句柄查询(域闭)") + return [] + if side == "hero": + return [] if (_hero.is_empty() or not _hero.alive) else [_hero] + var out: Array = [] + for u in _enemies: + if u.alive: + out.append(u) + return out + + +# 当前拍(Dead 句柄保留只读可查——冻结不变式观测量;hero 句柄拒绝) +func query_beat(u: Dictionary) -> String: + if u.is_empty() or _despawned: + _diag("query_beat 失效句柄") + return "" + if u.get("is_hero", false): + _diag("hero 无行为环(环接口对主角句柄拒绝)") + return "" + var p: Array = u.pattern + return String(p[u.steps % p.size()]) + + +# F2 分量供给面:{baseline, attach, total};L/m_amb 由调用方注入(判别探针,不校验游戏态域) +func query_attack(u: Dictionary, L: int, m_amb: int) -> Dictionary: + if u.is_empty() or _despawned or not u.alive: + _diag("query_attack 失效/死亡句柄") + return {"baseline": 0, "attach": 0, "total": 0} + var beat := query_beat(u) + if beat == "charge": + return {"baseline": 0, "attach": 0, "total": 0} # charge 拍不读伏击池——空拍零兑付 + var baseline: int = u.atk + if beat == "heavy": + baseline = u.atk * int(u.heavy_multiplier) + return {"baseline": baseline, "attach": L * m_amb, "total": baseline + L * m_amb} + + +# 受击上报:hp 递减 floor 0 → Dead;n ≤ 0 拒绝 +func apply_damage(u: Dictionary, n: int) -> bool: + if n <= 0: + _diag("apply_damage n ≤ 0 非法") + return false + if u.is_empty() or _despawned or not u.alive: + _diag("apply_damage 失效/死亡句柄") + return false + u.hp = maxi(0, u.hp - n) + if u.hp == 0: + u.alive = false # Dead 不可逆(死亡判定时机归 B1) + return true + + +# 治疗(切片登记接口漂移——B1 治疗 clamp 的写入口):返回实际回复量 +func heal(u: Dictionary, n: int) -> int: + if n < 0 or u.is_empty() or _despawned or not u.alive: + _diag("heal 非法/失效句柄") + return 0 + var actual: int = mini(n, int(u.max_hp) - int(u.hp)) + u.hp += actual + return actual + + +# 推进:存活才推进;false 两级——预期冻结(无诊断)/ 失效句柄(诊断);hero 拒绝 +func advance(u: Dictionary) -> bool: + if u.is_empty() or _despawned: + _diag("advance 失效句柄") + return false + if u.get("is_hero", false): + _diag("hero 不在推进域(主角无行为环)") + return false + if not u.alive: + return false # 轮到前被杀——预期冻结(死亡即不动手),无诊断 + u.steps += 1 + return true + + +# 固定顺位:行主序生成序最前存活敌;死亡后顺延;空 → null +func default_target(): + for u in _enemies: + if u.alive: + return u + return null + + +# ---- 只读投影面(E3 封装收口:外部读单位经此,禁直读容器数组)---- + +# 主角句柄(未 spawn/despawn 后 = {})——句柄即 Dictionary 引用(同 query_alive 先例) +func hero_unit() -> Dictionary: + return _hero + + +# 全体敌人句柄(含死亡残位——呈现层残位渲染 / 白盒断言的合法入口;数组结构拷贝、字典句柄同源) +func all_enemies() -> Array: + return _enemies.duplicate() + + +# F3 机制算术纯函数(B1 预截流消费面):cap 判定在授予前;守恒 granted + overflow = mines_r +func ambush_grant_plan(mines_r: int, cap: int) -> Dictionary: + var granted: int = mini(mines_r, cap) + return {"granted": granted, "overflow": mines_r - granted} + + +func despawn_all() -> bool: + if _despawned or not _spawned: + _diag("重复/无效 despawn_all") + return false + _despawned = true + _enemies.clear() + _hero = {} + return true + + +func _diag(msg: String) -> void: + push_warning("[B2] " + msg) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd.uid new file mode 100644 index 00000000..b80282b3 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd.uid @@ -0,0 +1 @@ +uid://dek3bsxksloan diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd new file mode 100644 index 00000000..1f423fcb --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd @@ -0,0 +1,41 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 ActionEconomyTable schema(A3 回填值,单行全局;Rule 4 豁免)。 +# 数值域与跨字段校验依据 design/gdd/a3-action-economy.md "ActionEconomyTable Schema 定稿"。 +class_name ActionEconomyTable +extends Resource + +@export var ap_per_round: int = 8 # [1,32] knob +@export var dig_ap_cost: int = 1 # [1,8] knob(域下限 1 = 零预算系统失效拒绝) +@export var flag_place_ap_cost: int = 1 # [0,8] knob +@export var flag_pull_ap_cost: int = 1 # [0,8] knob +@export var rap_refresh_cost: int = 8 # [1,64] knob +@export var refresh_max_per_round: int = 2 # [1,8] knob(2026-09-12 轮内刷新批——每轮刷新兜底上限,Active/Waiting 合计) +@export var move_ap_cost: int = 0 # 冻结 0(P4 氛围承载) +@export var chain_ap_cost: int = 0 # 冻结 0(v2 定稿) + + +func validate() -> Array: + var errs: Array = [] + if ap_per_round < 1 or ap_per_round > 32: + errs.append({"category": "domain", "message": "ap_per_round 越域 [1,32]: %d" % ap_per_round}) + if dig_ap_cost < 1 or dig_ap_cost > 8: + errs.append({"category": "domain", "message": "dig_ap_cost 越域 [1,8]: %d" % dig_ap_cost}) + if flag_place_ap_cost < 0 or flag_place_ap_cost > 8: + errs.append({"category": "domain", "message": "flag_place_ap_cost 越域 [0,8]: %d" % flag_place_ap_cost}) + if flag_pull_ap_cost < 0 or flag_pull_ap_cost > 8: + errs.append({"category": "domain", "message": "flag_pull_ap_cost 越域 [0,8]: %d" % flag_pull_ap_cost}) + if rap_refresh_cost < 1 or rap_refresh_cost > 64: + errs.append({"category": "domain", "message": "rap_refresh_cost 越域 [1,64]: %d" % rap_refresh_cost}) + if refresh_max_per_round < 1 or refresh_max_per_round > 8: + errs.append({"category": "domain", "message": "refresh_max_per_round 越域 [1,8]: %d" % refresh_max_per_round}) + if move_ap_cost != 0: + errs.append({"category": "frozen", "message": "move_ap_cost 结构冻结为 0: %d" % move_ap_cost}) + if chain_ap_cost != 0: + errs.append({"category": "frozen", "message": "chain_ap_cost 结构冻结为 0: %d" % chain_ap_cost}) + # 跨字段(A3 三轮评审增补):任一动作成本 ≤ ap_per_round,违规加载期致命 + for pair in [["dig_ap_cost", dig_ap_cost], ["flag_place_ap_cost", flag_place_ap_cost], ["flag_pull_ap_cost", flag_pull_ap_cost]]: + if pair[1] > ap_per_round: + errs.append({"category": "cross_field", "message": "%s(%d) > ap_per_round(%d)——挖掘/旗动作永久不可用软锁" % [pair[0], pair[1], ap_per_round]}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd.uid new file mode 100644 index 00000000..ca4f595c --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd.uid @@ -0,0 +1 @@ +uid://dtortb6o6p2gu diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd new file mode 100644 index 00000000..76d22f50 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd @@ -0,0 +1,77 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 BoardGenTable schema(A1 回填值;键 (level_index, node_depth)——首个进度键表)。 +# 依据 design/gdd/a1-board-generation.md "BoardGenTable Schema 定稿" + F2/F2b/F2c 谓词。 +class_name BoardGenTable +extends Resource + +@export var rows: Array = [] # Array[Dictionary] +const SOLVER_RULES := ["single", "single_subset"] +const FOG_RATIO_ABS_CAP := 0.25 # B1 修订——雾占比绝对上限 +const COST_BUDGET := 16384 # F2c provisional 量级护栏(禁墙钟逃生) +const TARGET_ABS_FLOOR := 0.85 # B1 修订——target 域下限(P1 绝对锚) + + +func validate() -> Array: + var errs: Array = [] + if rows.is_empty(): + errs.append({"category": "empty_table", "message": "BoardGenTable 为空"}) + return errs + var levels := {} + var anchor_ok := {} + for row in rows: + var L: int = int(row.get("level_index", 0)) + var D: int = int(row.get("node_depth", -1)) + var key := "%d_%d" % [L, D] + if levels.has(key): + errs.append({"category": "dup_key", "message": "BoardGen 键重复: %s" % key}) + levels[key] = true + if L < 1: + errs.append({"category": "domain", "message": "level_index 须 ≥1"}) + if D < 0: + errs.append({"category": "domain", "message": "node_depth 须 ≥0"}) + var w: int = int(row.get("board_width", 0)) + var h: int = int(row.get("board_height", 0)) + if w < 8 or w > 64 or h < 8 or h > 64: + errs.append({"category": "domain", "message": "(%d,%d) 尺寸越域 [8,64]" % [L, D]}) + var d: float = float(row.get("mine_density", 0.0)) + if d <= 0.0 or d > 0.5: + errs.append({"category": "domain", "message": "(%d,%d) mine_density 越域 (0,0.5]" % [L, D]}) + var t: float = float(row.get("inferability_target", 0.0)) + if t < TARGET_ABS_FLOOR or t > 1.0: + errs.append({"category": "domain", "message": "(%d,%d) inferability_target 越域 [0.85,1](P1 绝对锚)" % [L, D]}) + if not SOLVER_RULES.has(row.get("solver_rules", "")): + errs.append({"category": "enum", "message": "(%d,%d) solver_rules 出域" % [L, D]}) + if row.get("solver_rules", "") != "single_subset": + errs.append({"category": "frozen", "message": "(%d,%d) solver_rules 冻结 single_subset" % [L, D]}) + var ms: int = int(row.get("min_safe_count", 0)) + if ms < 1 or ms > 999: + errs.append({"category": "domain", "message": "(%d,%d) min_safe_count 越域 [1,999]" % [L, D]}) + var att: int = int(row.get("max_gen_attempts", 0)) + if att < 1 or att > 32: + errs.append({"category": "domain", "message": "(%d,%d) max_gen_attempts 越域 [1,32]" % [L, D]}) + var fc: int = int(row.get("fog_region_count", 0)) + var fr: float = float(row.get("fog_target_ratio", 0.0)) + # F2 规范合取形 + 雾占比绝对上限(B1 修订) + var fog_ok := (fc == 0 and fr == 0.0) or (fc >= 1 and fr > 0.0 and fr <= minf(8.0 * d, FOG_RATIO_ABS_CAP) and fr < 1.0) + if not fog_ok: + errs.append({"category": "cross_field", "message": "(%d,%d) fog_domain_ok 违例(count=%d ratio=%.3f)" % [L, D, fc, fr]}) + # 布雷数下限(R3)——零雷板拒绝 + var n_mines := int(round(d * float(w * h - 9))) + if n_mines < 1: + errs.append({"category": "cross_field", "message": "(%d,%d) 布雷数下限违例 round(d×(W×H−9)) ≥ 1" % [L, D]}) + # F2b min_safe 可行性 + if w * h - n_mines < ms: + errs.append({"category": "cross_field", "message": "(%d,%d) min_safe_feasible 违例" % [L, D]}) + # F2c 成本可行性 + if att * w * h > COST_BUDGET: + errs.append({"category": "cross_field", "message": "(%d,%d) cost_feasible 违例 attempts×W×H ≤ %d" % [L, D, COST_BUDGET]}) + var biomes: Array = row.get("board_biomes", []) + if biomes.is_empty(): + errs.append({"category": "cross_field", "message": "(%d,%d) board_biomes 非空域校验违例(A4⑮)" % [L, D]}) + anchor_ok[L] = anchor_ok.get(L, false) or D == 0 + for L in anchor_ok.keys(): + if not anchor_ok[L]: + errs.append({"category": "anchor", "message": "level %d 缺 depth-0 锚行(EC15)" % L}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd.uid new file mode 100644 index 00000000..37b2863f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd.uid @@ -0,0 +1 @@ +uid://ee2mfcg5542u diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd new file mode 100644 index 00000000..d0f43d1f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd @@ -0,0 +1,32 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-12 +# D2 DifficultyTable schema(C3 GDD Core Rule 1 兑现——B2 Rule 10 "接口未定 provisional" 的建表兑现,走 D2 接口漂移规则)。 +# 全局修饰结构位:按 (L,D) 精确匹配施加乘数于 EnemyTable 行值(B2.spawn 实例化时、行值快照前)。 +# MVP 空表(零行为——AC-10);首个使用场景 = 精英节点(C1 定稿联动,C3 OQ5);Boss 走 Dmax 直填行不走本表。 +class_name DifficultyTable +extends Resource + +@export var rows: Array = [] # Array[Dictionary]:{level_index, node_depth, hp_mult, atk_mult} + +const MULT_MIN := 0.1 +const MULT_MAX := 10.0 + + +func validate() -> Array: + var errs: Array = [] + var seen := {} + for row in rows: + var L: int = int(row.get("level_index", 0)) + var D: int = int(row.get("node_depth", -1)) + var key := "%d_%d" % [L, D] + if seen.has(key): + errs.append({"category": "dup_key", "message": "DifficultyTable 键重复: %s" % key}) + seen[key] = true + if L < 1 or D < 0: + errs.append({"category": "domain", "message": "难度键越域 (%d,%d)" % [L, D]}) + # 乘数越域 = warning 级(使用时 clamp——组合事故非配置事故,C3 GDD Edge Case 预登记) + for k in ["hp_mult", "atk_mult"]: + var v: float = float(row.get(k, 1.0)) + if v < MULT_MIN or v > MULT_MAX: + errs.append({"category": "mult_oor_warning", "message": "%s (%s) 越域 [0.1,10] 使用时 clamp: %f" % [key, k, v]}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd.uid new file mode 100644 index 00000000..494b901a --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd.uid @@ -0,0 +1 @@ +uid://bskkax0l0f20 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd new file mode 100644 index 00000000..1fe7bc0e --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd @@ -0,0 +1,108 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 EnemyTable / HeroTable schema(B2 回填值)。EnemyTable 键 (L,D,enemy_id),每行集恰一敌种; +# HeroTable 单行全局(Rule 4 豁免)。依据 design/gdd/b2-enemy-system.md Schema 节。 +class_name EnemyTable +extends Resource + +@export var rows: Array = [] # Array[Dictionary] + +const PATTERN_MVP := ["attack", "charge", "heavy"] +const PATTERN_RESERVED := ["guard", "boost", "hinder", "summon"] +# 护甲类型封闭枚举(伤害类型 × 护甲克制批——克制矩阵本体在 SettlementTable) +const ARMOR_TYPES := ["none", "a", "b"] + + +func validate() -> Array: + var errs: Array = [] + if rows.is_empty(): + errs.append({"category": "empty_table", "message": "EnemyTable 为空"}) + return errs + var seen := {} + var rowset_species := {} + var anchor_ok := {} + for row in rows: + var L: int = int(row.get("level_index", 0)) + var D: int = int(row.get("node_depth", -1)) + var eid: String = row.get("enemy_id", "") + var key := "%d_%d_%s" % [L, D, eid] + if seen.has(key): + errs.append({"category": "dup_key", "message": "EnemyTable 键重复: %s" % key}) + seen[key] = true + if L < 1 or D < 0: + errs.append({"category": "domain", "message": "进度键越域 (%d,%d)" % [L, D]}) + if eid == "": + errs.append({"category": "domain", "message": "enemy_id 空"}) + if String(row.get("display_name", "")) == "": + errs.append({"category": "domain", "message": "%s display_name 空" % eid}) + var uc: int = int(row.get("unit_count", 0)) + if uc < 1 or uc > 3: + errs.append({"category": "domain", "message": "%s unit_count 越域 [1,3]" % eid}) + # unit_count_max(可省略——遭遇随机化批,C3 GDD Core Rule 8):区间 [unit_count, max],缺省 = 定值 + if row.has("unit_count_max"): + var ucm: int = int(row.get("unit_count_max", uc)) + if ucm < uc or ucm > 3: + errs.append({"category": "cross_field", "message": "%s unit_count_max(%d) 越域 [unit_count(%d),3]" % [eid, ucm, uc]}) + var hp: int = int(row.get("hp", 0)) + if hp < 1 or hp > 999: + errs.append({"category": "domain", "message": "%s hp 越域 [1,999]" % eid}) + var atk: int = int(row.get("atk", -1)) + if atk < 0 or atk > 999: + errs.append({"category": "domain", "message": "%s atk 越域 [0,999]" % eid}) + # atk_min/atk_max(可省略配对——2026-09-14 敌方攻击区间批):遭遇骰同流 spawn 期整战持久 roll; + # 中点锚 = A3 条款①同款期望当量纪律(调区间必同步调 atk) + var has_amin: bool = row.has("atk_min") + var has_amax: bool = row.has("atk_max") + if has_amin != has_amax: + errs.append({"category": "cross_field", "message": "%s atk_min/atk_max 必须成对出现" % eid}) + elif has_amin: + var amin: int = int(row.get("atk_min", 0)) + var amax: int = int(row.get("atk_max", 0)) + if amin < 0 or amax > 999 or amin > amax: + errs.append({"category": "domain", "message": "%s atk 区间越域 [0,999] 且 min≤max: %d..%d" % [eid, amin, amax]}) + if atk != int((amin + amax) / 2.0): + errs.append({"category": "cross_field", "message": "%s atk(%d) ≠ 区间中点(%d)——中点恒等期望当量锚" % [eid, atk, (amin + amax) / 2]}) + var hm: int = int(row.get("heavy_multiplier", 0)) + if hm < 1 or hm > 16: + errs.append({"category": "domain", "message": "%s heavy_multiplier 越域 [1,16]" % eid}) + var pattern: Array = row.get("pattern", []) + if pattern.size() < 1 or pattern.size() > 8: + errs.append({"category": "domain", "message": "%s pattern 环长越域 [1,8]" % eid}) + for beat in pattern: + if PATTERN_RESERVED.has(beat): + errs.append({"category": "enum", "message": "%s pattern 预留值拒绝(MVP 校验域): %s" % [eid, beat]}) + elif not PATTERN_MVP.has(beat): + errs.append({"category": "enum", "message": "%s pattern 未知枚举: %s" % [eid, beat]}) + var cap: int = int(row.get("advantage_per_round_cap", -1)) + if cap < 0 or cap > 16: + errs.append({"category": "domain", "message": "%s advantage_per_round_cap 越域 [0,16]" % eid}) + # armor_type(可省略——缺省 none 向后兼容) + var at: String = String(row.get("armor_type", "none")) + if not ARMOR_TYPES.has(at): + errs.append({"category": "enum", "message": "%s armor_type 未知枚举: %s(域 {none, a, b})" % [eid, at]}) + # 每行集恰一敌种(行集内 enemy_id 唯一) + var rk := "%d_%d" % [L, D] + if rowset_species.has(rk): + errs.append({"category": "rowset_rule", "message": "(%d,%d) 行集出现第二敌种(每行集恰一敌种违例)" % [L, D]}) + rowset_species[rk] = eid + anchor_ok[L] = anchor_ok.get(L, false) or D == 0 + for L in anchor_ok.keys(): + if not anchor_ok[L]: + errs.append({"category": "anchor", "message": "EnemyTable level %d 缺 depth-0 锚行" % L}) + return errs + + +# R1:进度键 floor + 行集恰一敌种(实体键无歧义,悬空致命由调用方处理) +func resolve_rowset(L: int, D: int) -> Dictionary: + var best: Dictionary = {} + var best_l := -1 + var best_d := -1 + for row in rows: + var rl: int = int(row.get("level_index", 0)) + var rd: int = int(row.get("node_depth", 0)) + if rl <= L and rd <= D and (rl > best_l or (rl == best_l and rd > best_d)): + best = row + best_l = rl + best_d = rd + return best diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd.uid new file mode 100644 index 00000000..121833b8 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd.uid @@ -0,0 +1 @@ +uid://cbqi1lvj5gjx1 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd new file mode 100644 index 00000000..11826b60 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd @@ -0,0 +1,19 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 HeroTable schema(B2 接口漂移新增;单行全局,Rule 4 豁免)。 +# atk = 基础攻击恒存在(2026-09-11 用户裁决):域 [1,999]、0 = 加载拒绝——零基线死锁 schema 层杜绝。 +class_name HeroTable +extends Resource + +@export var max_hp: int = 50 # [1,999] knob(provisional,溯源 = 原型锚) +@export var atk: int = 6 # [1,999] knob——基础攻击(溯源 = 概念原型 HERO_BASE_ATK,provisional 归 C3);消费面 = B1 战斗段先手攻击 + + +func validate() -> Array: + var errs: Array = [] + if max_hp < 1 or max_hp > 999: + errs.append({"category": "domain", "message": "max_hp 越域 [1,999]: %d" % max_hp}) + if atk < 1 or atk > 999: + errs.append({"category": "domain", "message": "atk 越域 [1,999]: %d" % atk}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd.uid new file mode 100644 index 00000000..b4528f5f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd.uid @@ -0,0 +1 @@ +uid://d4npdck7i4jw1 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd new file mode 100644 index 00000000..7e633c2f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd @@ -0,0 +1,57 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 线性第一关(战×2→道具→战×2→恢复→Boss)能否承载"数值提升→通关期望改善"的元进度验证 +# Date: 2026-09-11 +# D2 LevelTable schema(provisional——C1 关卡节点地图未设计,本表为线性切片形态的结构位: +# nodes 数组即未来节点链的数据承载面;路径分支/奖励点分布的定稿权归 C1/C3)。 +# 节点类型 closed enum:battle(战斗,须带 node_depth)/ reward(随机主动道具点)/ rest(血量恢复点)/ boss(关底 Boss,须带 node_depth)。 +class_name LevelTable +extends Resource + +@export var rows: Array = [] # Array[Dictionary] + +const NODE_TYPES := ["battle", "reward", "rest", "boss"] + + +func validate() -> Array: + var errs: Array = [] + if rows.is_empty(): + errs.append({"category": "empty_table", "message": "LevelTable 为空"}) + return errs + var seen := {} + for row in rows: + var L: int = int(row.get("level_index", 0)) + if seen.has(L): + errs.append({"category": "dup_key", "message": "LevelTable level_index 重复: %d" % L}) + seen[L] = true + if L < 1: + errs.append({"category": "domain", "message": "level_index 须 ≥1"}) + if String(row.get("display_name", "")) == "": + errs.append({"category": "domain", "message": "level %d display_name 空" % L}) + var heal: int = int(row.get("rest_heal_pct", -1)) + if heal < 0 or heal > 100: + errs.append({"category": "domain", "message": "level %d rest_heal_pct 越域 [0,100]" % L}) + var nodes: Array = row.get("nodes", []) + if nodes.is_empty(): + errs.append({"category": "domain", "message": "level %d 节点链为空" % L}) + continue + var boss_count := 0 + var battle_count := 0 + for node in nodes: + var t: String = String(node.get("type", "")) + if not NODE_TYPES.has(t): + errs.append({"category": "enum", "message": "level %d 未知节点类型: %s" % [L, t]}) + continue + if t == "battle" or t == "boss": + if int(node.get("node_depth", -1)) < 0: + errs.append({"category": "domain", "message": "level %d %s 节点缺 node_depth" % [L, t]}) + battle_count += 1 + if t == "boss": + boss_count += 1 + if boss_count != 1: + errs.append({"category": "structural", "message": "level %d Boss 节点数 = %d(须恰一)" % [L, boss_count]}) + if battle_count < 2: + errs.append({"category": "structural", "message": "level %d 战斗节点(含 Boss)= %d(≥2 才构成关卡)" % [L, battle_count]}) + # 线性切片纪律:末节点必须是 Boss(关底收尾——C2 关卡定义) + if String(nodes[nodes.size() - 1].get("type", "")) != "boss": + errs.append({"category": "structural", "message": "level %d 末节点非 Boss(线性关卡以 Boss 收尾)" % L}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd.uid new file mode 100644 index 00000000..cd8d051d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd.uid @@ -0,0 +1 @@ +uid://ytfp75gipdwm diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd new file mode 100644 index 00000000..46baf2c3 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd @@ -0,0 +1,30 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 RewardConfigTable schema(B4 full 评审接口漂移新增;单行全局——B4 标量 knob 的 Rule 1 载体)。 +class_name RewardConfigTable +extends Resource + +@export var t_fog_min: int = 2 # [1,8] F2 雾档过滤门限 +@export var k_dmg: int = 0 # [0,999] 旧 F5 格池刀保底——2026-09-11 可选类供给控制器批置 0(语义由"战斗首件保证"取代) +@export var n_draft: int = 32 # [1,999] 旧 F5 草稿重 roll 上限(随 k_dmg=0 休眠——字段保留) +@export var p_min: int = 4 # [1,999] K≥1 供给闸刀力下界(随 k_dmg=0 休眠——字段保留) +@export var active_p0: float = 0.4 # [0,1] 可选类供给:战斗首件之后,每张新板的注入基础概率(2026-09-11 用户拍板批) +@export var active_decay: float = 0.5 # (0,1] 可选类供给:战斗内每多获取 1 件,注入概率乘一次衰减(指数下降、永不绝育——用户拍板④) + + +func validate() -> Array: + var errs: Array = [] + if t_fog_min < 1 or t_fog_min > 8: + errs.append({"category": "domain", "message": "t_fog_min 越域 [1,8]: %d" % t_fog_min}) + if k_dmg < 0 or k_dmg > 999: + errs.append({"category": "domain", "message": "k_dmg 越域 [0,999]: %d" % k_dmg}) + if n_draft < 1 or n_draft > 999: + errs.append({"category": "domain", "message": "n_draft 越域 [1,999]: %d" % n_draft}) + if p_min < 1 or p_min > 999: + errs.append({"category": "domain", "message": "p_min 越域 [1,999]: %d" % p_min}) + if active_p0 < 0.0 or active_p0 > 1.0: + errs.append({"category": "domain", "message": "active_p0 越域 [0,1]: %f" % active_p0}) + if active_decay <= 0.0 or active_decay > 1.0: + errs.append({"category": "domain", "message": "active_decay 越域 (0,1]: %f" % active_decay}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd.uid new file mode 100644 index 00000000..e78c4da6 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd.uid @@ -0,0 +1 @@ +uid://dc14jo26wutra diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd new file mode 100644 index 00000000..56f248d2 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd @@ -0,0 +1,73 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 RewardTable / RewardConfigTable schema(B4 回填值)。 +# RewardTable 行 = Dictionary,键 reward_id(Rule 4 豁免:定义表);跨字段校验族 + 池护栏。 +class_name RewardTable +extends Resource + +@export var rows: Array = [] # Array[Dictionary] + +const CATEGORIES := ["buff", "damage_item", "heal_pack", "instant_aoe", "instant_heal"] # 2026-09-11 两类奖励裁决五值:即时类 {buff, instant_aoe, instant_heal} / 可选类 {damage_item, heal_pack} +const POOL_TAGS := ["safe_cell", "clear_default", "active_inject"] # active_inject = 可选类注入池(2026-09-11 供给控制器批——可选类退出格子权重池);clear_ 命名空间预留拒绝 +const USE_TIMINGS := ["pre_settlement"] # MVP 单值承重 + + +func validate() -> Array: + var errs: Array = [] + if rows.is_empty(): + errs.append({"category": "empty_table", "message": "RewardTable 为空"}) + return errs + var seen := {} + for row in rows: + var rid: String = row.get("reward_id", "") + if rid == "": + errs.append({"category": "domain", "message": "reward_id 空"}) + continue + if seen.has(rid): + errs.append({"category": "dup_key", "message": "reward_id 重复: %s" % rid}) + seen[rid] = true + var cat: String = row.get("category", "") + if not CATEGORIES.has(cat): + errs.append({"category": "enum", "message": "%s category 出域/预留值拒绝: %s" % [rid, cat]}) + var pool: String = row.get("pool_tag", "") + var pool_reserved: bool = pool.begins_with("clear_") and pool != "clear_default" + if not POOL_TAGS.has(pool) and not pool_reserved: + errs.append({"category": "enum", "message": "%s pool_tag 未知: %s" % [rid, pool]}) + elif pool_reserved: + errs.append({"category": "enum", "message": "%s pool_tag clear_ 命名空间预留拒绝: %s" % [rid, pool]}) + if not USE_TIMINGS.has(row.get("use_timing", "")): + errs.append({"category": "enum", "message": "%s use_timing 出域: %s" % [rid, row.get("use_timing")]}) + if String(row.get("display_name", "")) == "": + errs.append({"category": "domain", "message": "%s display_name 空" % rid}) + var power: int = int(row.get("power", 0)) + var weight: int = int(row.get("weight", -1)) + var tier: int = int(row.get("tier", 0)) + if weight < 0 or weight > 999: + errs.append({"category": "domain", "message": "%s weight 越域 [0,999]" % rid}) + if tier < 1 or tier > 8: + errs.append({"category": "domain", "message": "%s tier 越域 [1,8]" % rid}) + var eff: String = row.get("effect_id", "") + # 跨字段:buff ⇒ effect_id 必填;非 buff ⇒ 恒空;item 类 power ≥ 1 + if cat == "buff" and eff == "": + errs.append({"category": "cross_field", "message": "%s buff 行 effect_id 必填" % rid}) + if cat != "buff" and eff != "": + errs.append({"category": "cross_field", "message": "%s 非 buff 行 effect_id 恒空" % rid}) + if cat != "buff" and power < 1: # 2026-09-11:即时类(aoe/heal)与道具类同 consumes power ≥ 1 + errs.append({"category": "cross_field", "message": "%s item 行 power ≥ 1" % rid}) + # 伤害量值区间批(2026-09-14):可选配对 power_min/power_max——缺省 = 定值行向后兼容(armor_type 先例) + var has_min: bool = row.has("power_min") + var has_max: bool = row.has("power_max") + if has_min != has_max: + errs.append({"category": "cross_field", "message": "%s power_min/power_max 必须成对出现" % rid}) + elif has_min: + var pmin: int = int(row.get("power_min", 0)) + var pmax: int = int(row.get("power_max", 0)) + if pmin < 1 or pmax > 999 or pmin > pmax: + errs.append({"category": "domain", "message": "%s power 域违例 [1,999] 且 min≤max: %d..%d" % [rid, pmin, pmax]}) + if cat == "buff": + errs.append({"category": "cross_field", "message": "%s buff 行禁区间字段(power 恒 0)" % rid}) + if power != int((pmin + pmax) / 2.0): + # 中点锚:power = 区间中点(A3 条款①期望当量锚——调区间必同步调 power) + errs.append({"category": "cross_field", "message": "%s power(%d) ≠ 区间中点(%d)——中点恒等期望当量锚" % [rid, power, (pmin + pmax) / 2]}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd.uid new file mode 100644 index 00000000..0e96ce1d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd.uid @@ -0,0 +1 @@ +uid://cm1mr66q2v8b4 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd new file mode 100644 index 00000000..51876855 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd @@ -0,0 +1,57 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 SettlementTable schema(B1 回填值;单行全局,Rule 4 豁免)——八字段口径。 +# 三行致命 manifest:segorder_violation / poolmap_dangling / pperfect_oob。 +class_name SettlementTable +extends Resource + +@export var equivalence_ref_damage: int = 12 # [1,999] knob——当量参考敞口 D_ref +@export var p_perfect: int = 200 # {100,200} percent——完美排雷第二份兑付触发(载体域) +@export var segment_order: PackedStringArray = [] # 结构冻结——恒等拍板段序校验违例致命 +@export var source_label_map: Dictionary = {} # 结构冻结——source_class→文案键 +@export var effect_pool_map: Dictionary = {} # 结构冻结——effect_id→pool(悬空加载致命) +@export var pool_clamp_lo: int = -100 # 结构冻结 +@export var pool_clamp_hi: int = 100 # 结构冻结 +@export var round_direction: String = "floor" # 结构冻结——单一取整规则 +# ---- 伤害类型 × 护甲克制批(2026-09-12 用户拍板结构;数值 provisional 待调参)---- +# 主角攻击 = 基础(atk×(1+attack_up),不吃克制)+ 开格充能×克制 + 标雷充能×克制(分量各自 floor 后相加) +@export var damage_a_per_cell: int = 1 # 伤害A 充能:本轮每开 1 资格格(reward_eligible 安全格)+N +@export var damage_a_cap: int = 8 # 伤害A 单轮充能上限(0 = 不封顶;8 与 ap_per_round 对齐) +@export var damage_b_per_flag: int = 2 # 伤害B 充能:每颗首次标中雷 +N(沿用每雷实例一次去重——结构上限) +@export var armor_effectiveness: Dictionary = {} # 克制矩阵 percent:{分量 {base|dmg_a|dmg_b}: {护甲 {none|a|b}: percent}} + +const ARMOR_KEYS := ["none", "a", "b"] +const COMPONENT_KEYS := ["base", "dmg_a", "dmg_b"] + +const FROZEN_SEGMENT_ORDER := ["grant", "item_window", "buff_debuff", "item", "combat", "decay", "verdict"] +const P_PERFECT_DOMAIN := [100, 200] + + +func validate() -> Array: + var errs: Array = [] + if equivalence_ref_damage < 1 or equivalence_ref_damage > 999: + errs.append({"category": "domain", "message": "equivalence_ref_damage 越域 [1,999]: %d" % equivalence_ref_damage}) + if not P_PERFECT_DOMAIN.has(p_perfect): + errs.append({"category": "pperfect_oob", "message": "p_perfect 载体域 {100,200}: %d" % p_perfect}) + var seg := Array(segment_order) + if seg != FROZEN_SEGMENT_ORDER: + errs.append({"category": "segorder_violation", "message": "segment_order 违例——恒等拍板段序校验失败: %s" % str(seg)}) + if round_direction != "floor": + errs.append({"category": "frozen", "message": "round_direction 结构冻结 floor: %s" % round_direction}) + if pool_clamp_lo != -100 or pool_clamp_hi != 100: + errs.append({"category": "frozen", "message": "pool_clamp 域结构冻结 [-100,+100]"}) + # 伤害类型 × 护甲克制批新字段域校验 + if damage_a_per_cell < 0 or damage_a_per_cell > 999: + errs.append({"category": "domain", "message": "damage_a_per_cell 越域 [0,999]: %d" % damage_a_per_cell}) + if damage_a_cap < 0 or damage_a_cap > 999: + errs.append({"category": "domain", "message": "damage_a_cap 越域 [0,999](0 = 不封顶): %d" % damage_a_cap}) + if damage_b_per_flag < 0 or damage_b_per_flag > 999: + errs.append({"category": "domain", "message": "damage_b_per_flag 越域 [0,999]: %d" % damage_b_per_flag}) + for comp in COMPONENT_KEYS: + var row: Dictionary = armor_effectiveness.get(comp, {}) + for armor in ARMOR_KEYS: + var pct: int = int(row.get(armor, -1)) + if pct < 0 or pct > 999: + errs.append({"category": "domain", "message": "armor_effectiveness.%s.%s 越域 [0,999]: %d" % [comp, armor, pct]}) + return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd.uid new file mode 100644 index 00000000..688453a0 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd.uid @@ -0,0 +1 @@ +uid://ctmgiqmejqe7e diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd new file mode 100644 index 00000000..09a5451b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd @@ -0,0 +1,58 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 StatusEffectTable schema(B3 回填值;行 = Dictionary,键 effect_id;Rule 4 豁免)。 +# 依据 design/gdd/b3-status-effect.md "StatusEffectTable Schema 定稿"。 +class_name StatusEffectTable +extends Resource + +@export var rows: Array = [] # Array[Dictionary] + +const TARGET_SIDES := ["hero", "enemy"] +const UNITS := ["percent", "flat"] +const STACK_POLICIES := ["stack", "refresh", "independent"] + + +func validate() -> Array: + var errs: Array = [] + if rows.is_empty(): + errs.append({"category": "empty_table", "message": "StatusEffectTable 为空"}) + return errs + var seen := {} + for row in rows: + var eid: String = row.get("effect_id", "") + if eid == "": + errs.append({"category": "domain", "message": "effect_id 缺失/空"}) + continue + if seen.has(eid): + errs.append({"category": "dup_key", "message": "effect_id 重复: %s" % eid}) + seen[eid] = true + if String(row.get("display_name", "")) == "": + errs.append({"category": "domain", "message": "%s display_name 空" % eid}) + if not TARGET_SIDES.has(row.get("target_side", "")): + errs.append({"category": "enum", "message": "%s target_side 出域" % eid}) + var unit: String = row.get("unit", "") + if not UNITS.has(unit): + errs.append({"category": "enum", "message": "%s unit 出域" % eid}) + if not STACK_POLICIES.has(row.get("stack_policy", "")): + errs.append({"category": "enum", "message": "%s stack_policy 出域" % eid}) + var m: int = int(row.get("magnitude", 0)) + # 跨字段:percent ∈ [-100,100] 且 ≠0 / flat ≥ 1 + if unit == "percent" and (m == 0 or m < -100 or m > 100): + errs.append({"category": "cross_field", "message": "%s percent magnitude 域错: %d" % [eid, m]}) + if unit == "flat" and m < 1: + errs.append({"category": "cross_field", "message": "%s flat magnitude 须 ≥1: %d" % [eid, m]}) + var dur: int = int(row.get("duration_settlements", 0)) + if dur < 1 or dur > 99: + errs.append({"category": "domain", "message": "%s duration_settlements 越域 [1,99]: %d" % [eid, dur]}) + var ms: int = int(row.get("max_stack", 0)) + if ms < 1 or ms > 16: + errs.append({"category": "domain", "message": "%s max_stack 越域 [1,16]: %d" % [eid, ms]}) + return errs + + +func row_by_id(effect_id: String) -> Dictionary: + for row in rows: + if row.get("effect_id", "") == effect_id: + return row + return {} diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd.uid new file mode 100644 index 00000000..77c86132 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd.uid @@ -0,0 +1 @@ +uid://b4hsttg27d7o1 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd new file mode 100644 index 00000000..4ed8be5b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd @@ -0,0 +1,51 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# D2 VariationTable schema(A4 回填值;行 = Dictionary,键 variant_id)。 +# 依据 design/gdd/a4-terrain-variation.md "VariationTable Schema 定稿"。 +class_name VariationTable +extends Resource + +@export var rows: Array = [] # Array[Dictionary] + +const SCOPES := ["region", "global"] +const INFO_MASKS := ["none", "hide_numbers"] +const CELL_CONSTRAINTS := ["any", "safe_nonzero"] + + +func validate() -> Array: + var errs: Array = [] + if rows.is_empty(): + errs.append({"category": "empty_table", "message": "VariationTable 为空"}) + return errs + var seen := {} + for row in rows: + var vid: String = row.get("variant_id", "") + if vid == "": + errs.append({"category": "domain", "message": "variant_id 缺失/空"}) + continue + if seen.has(vid): + errs.append({"category": "dup_key", "message": "variant_id 重复: %s" % vid}) + seen[vid] = true + if String(row.get("display_name", "")) == "": + errs.append({"category": "domain", "message": "%s display_name 空" % vid}) + var ul: int = int(row.get("unlock_level", 0)) + if ul < 1 or ul > 999: + errs.append({"category": "domain", "message": "%s unlock_level 越域 [1,999]: %d" % [vid, ul]}) + if not SCOPES.has(row.get("scope", "")): + errs.append({"category": "enum", "message": "%s scope 出域: %s" % [vid, row.get("scope")]}) + if not INFO_MASKS.has(row.get("info_mask", "")): + errs.append({"category": "enum", "message": "%s info_mask 出域: %s" % [vid, row.get("info_mask")]}) + if not CELL_CONSTRAINTS.has(row.get("cell_constraint", "")): + errs.append({"category": "enum", "message": "%s cell_constraint 出域: %s" % [vid, row.get("cell_constraint")]}) + # 跨字段(A4 re-review 增补,加载期致命):hide_numbers ⇒ safe_nonzero + if row.get("info_mask", "") == "hide_numbers" and row.get("cell_constraint", "") != "safe_nonzero": + errs.append({"category": "cross_field", "message": "%s hide_numbers ⇒ cell_constraint 必为 safe_nonzero" % vid}) + return errs + + +func row_by_id(variant_id: String) -> Dictionary: + for row in rows: + if row.get("variant_id", "") == variant_id: + return row + return {} diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd.uid new file mode 100644 index 00000000..212dd58e --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd.uid @@ -0,0 +1 @@ +uid://boin0gyrpqvwd diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres new file mode 100644 index 00000000..faf31620 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="ActionEconomyTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/action_economy_table.gd" id="1"] + +[resource] +script = ExtResource("1") +ap_per_round = 8 +dig_ap_cost = 1 +flag_place_ap_cost = 1 +flag_pull_ap_cost = 1 +rap_refresh_cost = 8 +refresh_max_per_round = 2 +move_ap_cost = 0 +chain_ap_cost = 0 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/board_gen.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/board_gen.tres new file mode 100644 index 00000000..622cda3f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/board_gen.tres @@ -0,0 +1,94 @@ +[gd_resource type="Resource" script_class="BoardGenTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/board_gen_table.gd" id="1"] + +[resource] +script = ExtResource("1") +rows = [{ +"level_index": 1, +"node_depth": 0, +"board_width": 10, +"board_height": 10, +"mine_density": 0.25, +"inferability_target": 0.88, +"solver_rules": "single_subset", +"min_safe_count": 50, +"max_gen_attempts": 16, +"fog_region_count": 0, +"fog_target_ratio": 0.0, +"board_biomes": ["dungeon"] +}, { +"level_index": 1, +"node_depth": 1, +"board_width": 10, +"board_height": 10, +"mine_density": 0.25, +"inferability_target": 0.88, +"solver_rules": "single_subset", +"min_safe_count": 50, +"max_gen_attempts": 16, +"fog_region_count": 0, +"fog_target_ratio": 0.0, +"board_biomes": ["dungeon"] +}, { +"level_index": 1, +"node_depth": 2, +"board_width": 12, +"board_height": 12, +"mine_density": 0.25, +"inferability_target": 0.88, +"solver_rules": "single_subset", +"min_safe_count": 60, +"max_gen_attempts": 24, +"fog_region_count": 0, +"fog_target_ratio": 0.0, +"board_biomes": ["dungeon"] +}, { +"level_index": 1, +"node_depth": 3, +"board_width": 12, +"board_height": 12, +"mine_density": 0.25, +"inferability_target": 0.88, +"solver_rules": "single_subset", +"min_safe_count": 60, +"max_gen_attempts": 24, +"fog_region_count": 0, +"fog_target_ratio": 0.0, +"board_biomes": ["dungeon"] +}, { +"level_index": 1, +"node_depth": 4, +"board_width": 12, +"board_height": 12, +"mine_density": 0.25, +"inferability_target": 0.88, +"solver_rules": "single_subset", +"min_safe_count": 60, +"max_gen_attempts": 24, +"fog_region_count": 0, +"fog_target_ratio": 0.0, +"board_biomes": ["dungeon"] +}, { +"level_index": 2, +"node_depth": 0, +"board_width": 12, +"board_height": 12, +"mine_density": 0.25, +"inferability_target": 0.88, +"solver_rules": "single_subset", +"min_safe_count": 60, +"max_gen_attempts": 24, +"fog_region_count": 1, +"fog_target_ratio": 0.10, +"board_biomes": ["dungeon"] +}] + +; 切片调参登记(SLICE-TUNED provisional,调参所有权仍归 C3): +; A1 GDD 参考行(d0.10/0.12)经"先布后删"环内移除后仅幸存 2-3 雷、R0/|S|约0.97、残余挖掘 2-4 格—— +; 掘近退化板(A2 反例 B 机制的低密度放大)。实验数据(8 种子均值,smoke/experiment_density.gd): +; d0.25 → L1 幸存雷 11/残余 13/inf 0.925/att 2.9 · L2 雷 19/残余 21/inf 0.908/att 7.9(seed42 实测 core 0.889→target 定 0.88 首过),零降级告警。 +; target 收窄至 0.88/0.88(不低于 0.85 绝对锚)为密度上移的对价——B1 条款③⑤对账重跑义务随调参触发。 +; 2026-09-11 关卡扩展批:L1 增 D1–D4 行(Boss=Dmax=4)。L1 全程无雾——A4 迷雾 unlock_level=2(已锁事实), +; Boss 板以 12×12 区分(d0.28/t0.87 实测达不到推理率门槛——best≈0.81 降级告警,回退 0.25/0.88; +; 密度轴上调留给 C3 与求解器能力一起解)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/difficulty.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/difficulty.tres new file mode 100644 index 00000000..b08f7c8d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/difficulty.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="DifficultyTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/difficulty_table.gd" id="1"] + +[resource] +script = ExtResource("1") +rows = [] + +; C3 GDD Core Rule 1:全局修饰结构位——MVP 空表(零行为)。首个修饰行 = 精英节点上线(C1 联动)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/enemy.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/enemy.tres new file mode 100644 index 00000000..9452a5a5 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/enemy.tres @@ -0,0 +1,113 @@ +[gd_resource type="Resource" script_class="EnemyTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/enemy_table.gd" id="1"] + +[resource] +script = ExtResource("1") +rows = [{ +"level_index": 1, +"node_depth": 0, +"enemy_id": "goblin", +"display_name": "地精", +"unit_count": 1, +"unit_count_max": 2, +"hp": 24, +"atk": 7, +"atk_min": 4, +"atk_max": 10, +"heavy_multiplier": 2, +"pattern": ["attack"], +"advantage_per_round_cap": 4, +"armor_type": "none" +}, { +"level_index": 1, +"node_depth": 1, +"enemy_id": "goblin", +"display_name": "地精一伙", +"unit_count": 2, +"unit_count_max": 3, +"hp": 13, +"atk": 5, +"atk_min": 2, +"atk_max": 8, +"heavy_multiplier": 2, +"pattern": ["attack"], +"advantage_per_round_cap": 4, +"armor_type": "none" +}, { +"level_index": 1, +"node_depth": 2, +"enemy_id": "bugbear", +"display_name": "熊地精", +"unit_count": 1, +"hp": 40, +"atk": 9, +"atk_min": 6, +"atk_max": 12, +"heavy_multiplier": 2, +"pattern": ["attack", "charge", "heavy"], +"advantage_per_round_cap": 4, +"armor_type": "a" +}, { +"level_index": 1, +"node_depth": 3, +"enemy_id": "bugbear", +"display_name": "熊地精双雄", +"unit_count": 2, +"unit_count_max": 3, +"hp": 18, +"atk": 6, +"atk_min": 3, +"atk_max": 9, +"heavy_multiplier": 2, +"pattern": ["attack", "charge", "heavy"], +"advantage_per_round_cap": 4, +"armor_type": "a" +}, { +"level_index": 1, +"node_depth": 4, +"enemy_id": "ogre_chief", +"display_name": "食人魔酋长", +"unit_count": 1, +"hp": 150, +"atk": 9, +"atk_min": 6, +"atk_max": 12, +"heavy_multiplier": 2, +"pattern": ["attack", "attack", "charge", "heavy"], +"advantage_per_round_cap": 4, +"armor_type": "b" +}, { +"level_index": 2, +"node_depth": 0, +"enemy_id": "bugbear", +"display_name": "熊地精", +"unit_count": 1, +"hp": 30, +"atk": 7, +"atk_min": 4, +"atk_max": 10, +"heavy_multiplier": 2, +"pattern": ["attack", "charge", "heavy"], +"advantage_per_round_cap": 4, +"armor_type": "a" +}] + +; 切片调参登记 v3(2026-09-12 试玩批次 1 触发——F7 流程;C3 GDD F5 对照): +; 试玩反馈 = 基线满血全通关(模拟 80%/真人 100%)——难度严重不足,主杠杆 = atk 曲线上调(承伤侧): +; 4/4/6/4/7 → 7/5/9/6/9(约 +50%);HP 结构调整(D0 24→20 缩短战斗、Boss 130→150 用户拍板可上调)。 +; D0 注:20 HP 会被首轮盲挖粉尘秒杀(12+9)——回 24(4 轮战斗/承伤 42%,有过程有痛感)。 +; DND 命名(用户指令):goblin 地精(单拍杂鱼)→ bugbear 熊地精(三拍伏击环——DND 原型即伏击手)→ +; ogre_chief 食人魔酋长(经典低阶 Boss)。enemy_id 同步更名(内部键,无外部引用)。 +; L2 锚行同步 v3 语义(bugbear 30/7)。 +; 遭遇随机化批(2026-09-12 用户拍板):D0 [1,2]/D1 [2,3] hp13/D3 [2,3] hp18——数量区间骰(encounter 流,B1 掷骰); +; D2/Boss 固定(新敌种首遇教学位不加骰——C3 GDD Core Rule 8)。期望总量 ≈304(+8%),F6 带内。 +; +; 护甲类型批(2026-09-12 用户拍板:伤害类型 × 护甲类型克制——无甲/护甲A/护甲B): +; armor_type ∈ {none, a, b}(缺省 = none 向后兼容);克制矩阵本体在 settlement.tres.armor_effectiveness。 +; 映射(教学梯度:无甲→甲A→甲B):goblin=none · bugbear=a · ogre_chief=b(L2 bugbear 同 a)——候选方案, +; 数值与映射均 provisional 待用户调参。 + +; 敌方攻击区间批(2026-09-14 用户拍板②第二步——"上限掺运气"敌方侧:atk 上阶/下阶,中点恒等行值; +; roll 时机 = 遭遇骰同流〔encounter〕spawn 期整战持久,每拍抖动否决〔数字可读性锚〕;B2 零随机纪律保持 +; ——收 override 即确定值。宽度 ±3 provisional 归 C3 测量迭代;验收 = 完美策略档 0 加成通关 ≤40%。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/hero.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/hero.tres new file mode 100644 index 00000000..33310ef4 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/hero.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="HeroTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/hero_table.gd" id="1"] + +[resource] +script = ExtResource("1") +max_hp = 50 +atk = 6 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/level.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/level.tres new file mode 100644 index 00000000..72033ec8 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/level.tres @@ -0,0 +1,37 @@ +[gd_resource type="Resource" script_class="LevelTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/level_table.gd" id="1"] + +[resource] +script = ExtResource("1") +rows = [{ +"level_index": 1, +"display_name": "第一层 · 苔藓回廊", +"rest_heal_pct": 30, +"nodes": [{ +"node_depth": 0, +"type": "battle" +}, { +"node_depth": 1, +"type": "battle" +}, { +"type": "reward" +}, { +"node_depth": 2, +"type": "battle" +}, { +"node_depth": 3, +"type": "battle" +}, { +"type": "rest" +}, { +"node_depth": 4, +"type": "boss" +}] +}] + +; 切片登记(provisional——C1/C3 未设计,结构位与数值均为 SLICE-TUNED): +; 节点链 = 用户 2026-09-11 拍板的线性第一关(战×2 → 随机主动道具 → 战×2 → 血量恢复点 → Boss)。 +; node_depth 轴 = 节点深度(难度 = f(关卡序号, 节点深度)——game-concept 锁定口径);Boss = Dmax 行(A1 第三轴登记约定)。 +; rest_heal_pct = 30:恢复点回复 max_hp 的百分比(随生命天赋同步放大,防高生命档恢复失效)。 +; L1 全程无雾:A4 迷雾 unlock_level=2(已锁事实——迷雾自第二层解锁),Boss 板以 12×12 加密板区分。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/reward.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/reward.tres new file mode 100644 index 00000000..a07038d1 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/reward.tres @@ -0,0 +1,112 @@ +[gd_resource type="Resource" script_class="RewardTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/reward_table.gd" id="1"] + +[resource] +script = ExtResource("1") +rows = [{ +"reward_id": "defense_up_basic", +"category": "buff", +"effect_id": "defense_up", +"display_name": "石肤术", +"power": 0, +"weight": 16, +"pool_tag": "safe_cell", +"tier": 2, +"use_timing": "pre_settlement" +}, { +"reward_id": "attack_up_basic", +"category": "buff", +"effect_id": "attack_up", +"display_name": "增伤术", +"power": 0, +"weight": 20, +"pool_tag": "safe_cell", +"tier": 2, +"use_timing": "pre_settlement" +}, { +"reward_id": "spark_aoe", +"category": "instant_aoe", +"effect_id": "", +"display_name": "爆裂粉尘", +"power": 4, +"weight": 16, +"pool_tag": "safe_cell", +"tier": 1, +"use_timing": "pre_settlement" +}, { +"reward_id": "heal_minor", +"category": "instant_heal", +"effect_id": "", +"display_name": "急救敷料", +"power": 4, +"power_min": 2, +"power_max": 6, +"weight": 15, +"pool_tag": "safe_cell", +"tier": 1, +"use_timing": "pre_settlement" +}, { +"reward_id": "knife_t1", +"category": "damage_item", +"effect_id": "", +"display_name": "小刀", +"power": 8, +"power_min": 5, +"power_max": 11, +"weight": 60, +"pool_tag": "active_inject", +"tier": 1, +"use_timing": "pre_settlement" +}, { +"reward_id": "knife_adv", +"category": "damage_item", +"effect_id": "", +"display_name": "精工短刀", +"power": 16, +"power_min": 10, +"power_max": 22, +"weight": 30, +"pool_tag": "active_inject", +"tier": 2, +"use_timing": "pre_settlement" +}, { +"reward_id": "heal_pack", +"category": "heal_pack", +"effect_id": "", +"display_name": "治疗包", +"power": 12, +"power_min": 6, +"power_max": 18, +"weight": 10, +"pool_tag": "active_inject", +"tier": 2, +"use_timing": "pre_settlement" +}, { +"reward_id": "clear_blast", +"category": "instant_aoe", +"effect_id": "", +"display_name": "清障炸药", +"power": 40, +"power_min": 25, +"power_max": 55, +"weight": 1, +"pool_tag": "clear_default", +"tier": 2, +"use_timing": "pre_settlement" +}] + +; 2026-09-11 可选类供给控制器批(用户拍板): +; 可选类三行移出 safe_cell 权重池 → active_inject 注入池(小刀 60 / 精工短刀 30 / 治疗包 10——治疗资源宝贵), +; 由 RewardSupplyController 在 install 期注入(首件保证 + 指数递减),不再走格子权重 roll。 +; 清图槽 clear_knife(入库刀)→ clear_blast(instant_aoe 一次性高额伤害,当场兑付全体存活敌,不入库不占可选类供给)。 +; 格子池现仅即时类四行(attack_up 20 / defense_up 16 / spark_aoe 16 / heal_minor 15,合计 67)。 +; 减伤墙调参批(2026-09-13,playtest-2026-09-12 报告 Q1 触发):defense_up 24 → 16—— +; 石肤自 32% 降至 23.9%,攻击成为池内最高行;与 settlement.tres pool_clamp_lo=-60 同批(免伤墙双杠杆)。 + +; 伤害量值区间批(2026-09-14 用户拍板②——"上限掺运气"第一步,奖励侧五行:上阶/下阶区间内随机; +; 中点恒等旧定值 = A3 条款①期望当量锚不漂移;宽度 provisional 归 C3 测量迭代。验收口径 = 完美策略档 +; 0 加成通关率 ≤40%〔OQ7 完美档基线 100%〕)。roll 时机 = 身份预分配批(reward_roll 恰一 scope 批内 +; 追加取号,既有取号序零扰动——追加式);快照 E14 口径不变,B1/呈现零改动。区间: +; heal_minor [2,6] · knife_t1 [5,11] · heal_pack [6,18] · knife_adv [10,22] · clear_blast [25,55]。 +; buff 两行(attack_up/defense_up)与 spark_aoe 不在本批(即时光效类按拍板面五行为准)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/reward_config.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/reward_config.tres new file mode 100644 index 00000000..e0b0ab51 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/reward_config.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="RewardConfigTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/reward_config_table.gd" id="1"] + +[resource] +script = ExtResource("1") +t_fog_min = 2 +k_dmg = 0 +n_draft = 32 +p_min = 8 +active_p0 = 0.4 +active_decay = 0.5 + +; 2026-09-11 可选类供给控制器批(用户拍板):k_dmg 1→0(旧 F5 格池刀保底休眠,语义由"战斗首件保证"取代); +; active_p0 = 0.4 / active_decay = 0.5(首件之后每张新板注入概率 0.4 → 0.2 → 0.1 → …指数下降、永不绝育; +; provisional 起步值,标定目标 E[战斗获取] ≈ 1,level_monte_carlo 扫描验证后归 C3 定稿)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/settlement.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/settlement.tres new file mode 100644 index 00000000..673fa0d1 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/settlement.tres @@ -0,0 +1,61 @@ +[gd_resource type="Resource" script_class="SettlementTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/settlement_table.gd" id="1"] + +[resource] +script = ExtResource("1") +equivalence_ref_damage = 12 +p_perfect = 200 +segment_order = PackedStringArray("grant", "item_window", "buff_debuff", "item", "combat", "decay", "verdict") +source_label_map = { +"safe_cell": "安全格", +"flag_hit": "标中旗", +"mine_hit": "踩雷", +"clear_reward": "清图奖励", +"item": "道具", +"enemy_action": "敌方行动", +"hero_attack": "主角攻击", +"flag_miss": "标旗未中" +} +effect_pool_map = { +"attack_up": "hero_offense", +"defense_up": "hero_defense", +"weaken": "enemy_output", +"ambush": "flat_attach", +"enrage": "enemy_output", +"off_guard": "hero_defense" +} +pool_clamp_lo = -100 +pool_clamp_hi = 100 +round_direction = "floor" +damage_a_per_cell = 1 +damage_a_cap = 8 +damage_b_per_flag = 2 +armor_effectiveness = { +"base": { +"none": 100, +"a": 100, +"b": 100 +}, +"dmg_a": { +"none": 100, +"a": 150, +"b": 50 +}, +"dmg_b": { +"none": 100, +"a": 50, +"b": 150 +} +} + +; 伤害类型 × 护甲克制批(2026-09-12 用户拍板结构,数值 provisional 待用户/C3 调参): +; 主角攻击 = 基础(atk×(1+attack_up),对全部护甲 100%)+ 伤害A(开格充能×克制)+ 伤害B(标雷充能×克制), +; 三分量各自 floor 后相加;克制语义 = 特化甲 ×150 / 无甲 ×100 / 对位甲 ×50(穿甲弹/高爆弹式对策三角)。 +; damage_a_per_cell=1 · damage_a_cap=8(单轮充能封顶,防连锁轮失控;0 = 不封顶)· damage_b_per_flag=2 +; (每颗首次标中雷充能——沿用每雷实例一次去重,忘拔旗不重复充能)。 +; enrage(敌方暴走 +25% 进 enemy_output 池)/ off_guard(我方失衡 +25% 进 hero_defense 池)= +; 标错惩罚池两行(第三项 = 复用 ambush +2 层);时效均 1 结算(用户拍板"短点,比如 1 回合")。 +; 减伤墙调参批(2026-09-13,playtest-2026-09-12 报告 Q1 触发):拟改 pool_clamp_lo=-60 被 schema +; 冻结域拦截([-100,+100] 结构冻结——B1 F2 乘区结构决策),杠杆改走 status_effects.tres +; defense_up magnitude -50 → -30(叠满两层 = -60 减伤帽,本表 clamp 不动);登记以 status_effects.tres 批注为准。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/status_effects.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/status_effects.tres new file mode 100644 index 00000000..a74674d4 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/status_effects.tres @@ -0,0 +1,66 @@ +[gd_resource type="Resource" script_class="StatusEffectTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/status_effect_table.gd" id="1"] + +[resource] +script = ExtResource("1") +rows = [{ +"effect_id": "attack_up", +"display_name": "增伤", +"target_side": "hero", +"magnitude": 50, +"unit": "percent", +"duration_settlements": 1, +"stack_policy": "stack", +"max_stack": 2 +}, { +"effect_id": "defense_up", +"display_name": "减伤", +"target_side": "hero", +"magnitude": -30, +"unit": "percent", +"duration_settlements": 1, +"stack_policy": "stack", +"max_stack": 2 +}, { +"effect_id": "ambush", +"display_name": "伏击", +"target_side": "enemy", +"magnitude": 3, +"unit": "flat", +"duration_settlements": 1, +"stack_policy": "stack", +"max_stack": 16 +}, { +"effect_id": "weaken", +"display_name": "削弱", +"target_side": "enemy", +"magnitude": -65, +"unit": "percent", +"duration_settlements": 1, +"stack_policy": "stack", +"max_stack": 16 +}, { +"effect_id": "enrage", +"display_name": "暴走", +"target_side": "enemy", +"magnitude": 25, +"unit": "percent", +"duration_settlements": 1, +"stack_policy": "stack", +"max_stack": 16 +}, { +"effect_id": "off_guard", +"display_name": "失衡", +"target_side": "hero", +"magnitude": 25, +"unit": "percent", +"duration_settlements": 1, +"stack_policy": "stack", +"max_stack": 16 +}] + +; 减伤墙调参批(2026-09-13,playtest-2026-09-12 报告 Q1 触发——defense_scan 实测墙轮 39%、 +; 真人局 12/12 轮零承伤满血通关):defense_up magnitude -50 → -30——叠满两层 = -60 减伤帽 +; (原拟 pool_clamp_lo=-60 被 schema 冻结域拦截,杠杆移至本表);石肤权重 24→16 随批(reward.tres)。 +; 承伤带对 C3 F4(≤20%/普通场、≤50%/Boss 场)复验责任随批;B3 GDD "-50" 文字同步随下次 B3 修订批。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/variation.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/variation.tres new file mode 100644 index 00000000..fc5819bd --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/variation.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="VariationTable" load_steps=2 format=3] + +[ext_resource type="Script" path="res://data/schemas/variation_table.gd" id="1"] + +[resource] +script = ExtResource("1") +rows = [{ +"variant_id": "fog", +"display_name": "迷雾", +"unlock_level": 2, +"scope": "region", +"info_mask": "hide_numbers", +"cell_constraint": "safe_nonzero", +"biome_tags": [], +"counter_item_ids": [] +}] diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd new file mode 100644 index 00000000..180cb92c --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd @@ -0,0 +1,74 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-12(code-review 修复批——自 main.gd 拆出,规范 1.1/1.3 文案拼装独立成装配器) +# 战斗流程叙事文案装配器:组合根在模式流转点的玩家可见文案(棋盘日志横幅 / 节点行 / 终局文案 / +# 相位指示)在此统一拼装——main.gd 头注"本类不拼呈现字符串"由本类兑现。静态无状态。 +# 数值口径注记:胜利文案携带的 HP/道具数 = 关卡载荷快照值(每关口径)。 +extends RefCounted + + +# 节点进入横幅(棋盘日志——battle/reboss/reward/rest 共用区段样式) +static func node_banner(runner, node: Dictionary, t: String) -> String: + return "[color=#e0b860]── 节点 %d/%d · %s(深度 %d)──[/color]" % [ + runner.node_index + 1, runner.nodes().size(), + "Boss 房" if t == "boss" else "战斗", int(node.get("node_depth", 0))] + + +static func reward_line(snap: Dictionary) -> String: + return "[color=#d0c090]关卡奖励:%s(+%d)入库[/color]" % [ + String(snap.get("display_name", "")), int(snap.get("power", 0))] + + +static func rest_line(healed: int, hp: int, max_hp: int) -> String: + return "[color=#7cc47c]恢复点:回复 %d HP(→ %d/%d)[/color]" % [healed, hp, max_hp] + + +# 关卡开场行(种子/加成口径注记——面板加成是否生效由调用方传入) +static func level_open_line(level_display: String, seed_val: int, atk: int, max_hp: int, boosted: bool) -> String: + return "[color=#c8a860]第一关 · %s · 种子 %d · 攻击 %d / 生命 %d%s[/color]" % [ + level_display, seed_val, atk, max_hp, "(含面板加成)" if boosted else ""] + + +# 调试开场行 +static func debug_open_line(seed_val: int, level: int, level_mode: bool) -> String: + return "[color=#c8a860]种子 %d · 遭遇 %d 层%s[/color]" % [ + seed_val, level, "(关卡模式 · 库存延续)" if level_mode else ""] + + +# 关卡终局文案(result = victory/defeat;载荷 = runner level_finished 快照) +static func level_end_text(result: String, payload: Dictionary, effective_max_hp: int) -> String: + if result == "victory": + return "关卡通关!\n剩余道具 ×%d 随关卡结束清空 · 剩余 HP %d/%d" % [ + int(payload.get("remaining_items", 0)), int(payload.get("hp", 0)), effective_max_hp] + return "关卡失败……探险者倒在了节点 %d(%s)。" % [ + int(payload.get("node_index", 0)) + 1, String(payload.get("node_type", ""))] + + +# 调试战斗终局文案(level_end = 关内两层模式的关卡末;left = 清库剩余件数) +static func battle_end_text(result: String, level_end: bool, left: int) -> String: + if level_end: + return "关卡通关!\n剩余道具 ×%d 随关卡结束清空" % left + if result == "victory": + return "战斗胜利!敌人全灭。" + return "战斗失败……探险者倒下了。" + + +# 相位指示文本(B 区轮次·阶段行) +static func phase_text(round_index: int, battle_result: String, window_open: bool, waiting: bool, boss_active: bool, boss_name: String) -> String: + var boss_tag := " · Boss:%s" % boss_name if boss_active else "" + if battle_result != "": + return "第 %d 轮 · 战斗结束%s" % [round_index, boss_tag] + if window_open: + return "第 %d 轮 · 道具窗口%s" % [round_index, boss_tag] + if waiting: + return "第 %d 轮 · 结算中%s" % [round_index, boss_tag] + return "第 %d 轮 · 挖掘中%s" % [round_index, boss_tag] + + +# Boss 名解析(runner Boss 节点 → enemy 表行名;缺省遗迹守卫) +static func boss_name(runner, db) -> String: + if runner != null: + var node: Dictionary = runner.current_node() + if String(node.get("type", "")) == "boss": + var row: Dictionary = db.resolve_enemy_rowset(int(runner.level_row.level_index), int(node.get("node_depth", -1))) + return String(row.get("display_name", "")) + return "遗迹守卫" diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd.uid new file mode 100644 index 00000000..14be24ee --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd.uid @@ -0,0 +1 @@ +uid://cgpyvmn146x5w diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd new file mode 100644 index 00000000..77832fda --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd @@ -0,0 +1,102 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-13(封装审查 O1/O2 修复批——自 main.gd 拆出,规范 1.1 审视线 / 单一实现纪律) +# 调试模式编排控制器:单场遭遇(第 1/2 层)与连续两层(库存与 HP 关内延续)的模式状态机与战斗生命周期。 +# 职责边界:层序/携带 HP/共享 B4 状态 + B1 生命周期编排(创建 / begin_battle / 关卡末 level_clear)。 +# 授权先例:与 tune_sim_controller 同款(code-review 2026-09-12 批)——控制器承接模式编排, +# 组合根仍持有全部信号接线(battle_created 先于 begin_battle 发出,ADR-0001 接线唯一归组合根) +# 与终局 UI 决策;玩家意图写调用(settle_batch / 窗口提交 / 刷新)不经本类。 +# E4 收口(2026-09-13):加成生效值经注入 Callable 供给(组合根从面板百分比现算)→ encounter +# 显式传入战斗——零表写,"双写叠加/双基线同步"整类隐患随表写移除。 +# O2 收口:调试两层的"库存与 HP 延续"语义唯一实现点(此前与 main.gd 调试分支双实现漂移)。 +extends RefCounted + +signal battle_created(b1, level: int, carry_hp: int) # begin_battle 前——组合根完成信号接线(ADR-0001) +signal battle_ready(b1, level: int, carry_hp: int) # begin_battle 完成后——组合根 rebuild 棋盘 + 刷新呈现 + +var db +var rng # /root/RngService(组合根注入) +var _hero_override: Callable # 组合根生效值供给(→ {atk, max_hp};E4:经 encounter 传参,零表写) +var _seed_value: Callable # 组合根种子读取(LineEdit) + +var _level_mode := false +var _level_battles: Array = [] +var _level_idx := 0 +var _carry_hp := 0 +var _shared_b4 = null +var b1 = null # 当前战斗编排器(组合根在 battle_created 接线、battle_ready 接手) + + +func _init(db_ref, rng_ref, hero_override: Callable, seed_value: Callable) -> void: + db = db_ref + rng = rng_ref + _hero_override = hero_override + _seed_value = seed_value + + +func is_level_mode() -> bool: + return _level_mode + + +# 单场遭遇原语(2026-09-13 用户裁决:单场用户入口已删——本方法仅 ui_probe 雾板变体段使用; +# 无库存延续、standalone 终局语义保留供探针战斗收尾) +func start_single(level: int) -> bool: + _level_mode = false + _shared_b4 = null + return _start_battle(level, -1) + + +# 连续两层(调试入口——共享 B4 + HP 携带;首层满血开局) +func start_debug_levels() -> bool: + _level_mode = true + _level_battles = [1, 2] + _level_idx = 0 + _carry_hp = 0 + rng.new_adventure(_seed_value.call()) + _shared_b4 = RewardEconomy.new(db, rng) + return _start_battle(_level_battles[0], -1) + + +# 层间"进入下一层"(组合根过渡覆盖层确认后调用) +func next_layer() -> bool: + return _start_battle(_level_battles[_level_idx], _carry_hp) + + +# 终局处置(组合根 battle_finished 处理器调用)→ 决策载荷: +# {outcome: "next_layer"|"level_end"|"standalone_end", layer, carry_hp, items_left, remaining} +func on_battle_finished(payload: Dictionary) -> Dictionary: + var result: String = String(payload.get("result", "")) + if result == "victory" and _level_mode and _level_idx < _level_battles.size() - 1: + _carry_hp = maxi(1, int(payload.get("hero_hp", 0))) # despawn 前捕获值 + _level_idx += 1 + return {"outcome": "next_layer", "layer": _level_idx, "carry_hp": _carry_hp, + "items_left": b1.stock_count()} + var remaining := 0 + var is_level_end := result == "victory" and _level_mode + if is_level_end: + remaining = int(b1.level_clear()["remaining"]) # 库存随关卡结束清空(关内延续裁决③) + elif _level_mode: + b1.level_clear() # 失败同样清库(关卡域生命周期闭合——调试模式与 runner 模式同规) + return {"outcome": "level_end" if is_level_end else "standalone_end", + "layer": _level_idx, "carry_hp": _carry_hp, "items_left": 0, "remaining": remaining} + + +# 会话复位(组合根终局出口"返回开始界面"调用) +func reset() -> void: + _level_mode = false + _shared_b4 = null + b1 = null + + +func _start_battle(level: int, carry_hp: int) -> bool: + if not _level_mode or _level_idx == 0: + rng.new_adventure(_seed_value.call()) + var ov: Dictionary = _hero_override.call() # E4:生效值经 encounter 传入——零表写 + b1 = BattleOrchestrator.new(db, rng, _shared_b4 if _level_mode else null) + battle_created.emit(b1, level, carry_hp) + var hp: int = carry_hp if carry_hp > 0 else int(ov.max_hp) + if not b1.begin_battle({"level_index": level, "node_depth": 0, "hero_hp": hp, + "hero_atk": int(ov.atk), "hero_max_hp": int(ov.max_hp)}): + push_error("[DebugSession] 战斗启动失败(level %d)" % level) + return false + battle_ready.emit(b1, level, carry_hp) + return true diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd.uid new file mode 100644 index 00000000..b804bc1c --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd.uid @@ -0,0 +1 @@ +uid://dsfxx2nhu487l diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd new file mode 100644 index 00000000..01632ecf --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd @@ -0,0 +1,208 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(五区呈现装配器)——读核心只读面(B1 呈现快照 / runner)→ +# 组装文案/投影 → 驱动各视图 setter。 +# 高内聚边界:"核心状态 → 呈现文案"的全部翻译集中于此;视图保持零核心依赖; +# 静态无状态——呈现不持久化任何核心句柄。 +# E1 封装收口(2026-09-13):核心状态读取统一经 B1.presentation_snapshot() 成文快照面—— +# 禁再链式穿透 b1.b2.hero / b1.b2.enemies / b1.b3.query_active 一类路径(B1 协作对象已私有化, +# 敌人含死亡残位的投影由快照 enemies[] 承载,含 alive/beat/ring 字段)。 +# 更新触发注记(battle.md Data Requirements):旗/拔/移动成功反馈零事件外发(A2 发射面封闭)—— +# 经 ActionOutcome 返回值命令式驱动;AP/RAP 无推送信号——组合根每次调用返回后重查; +# B3 纯被动容器零信号——状态栏刷新绑 B1 结算编排完成时点;库存 B4 查询面——写路径回执后重查。 +# Date: 2026-09-12(五区改造批;基础 = 2026-09-11 HUD 装配器;2026-09-13 快照面改造) +class_name HudPresenter +extends RefCounted + + +# 每次结算行/轮次/节点变化/动作返回后调用(组合根全量重查装配) +static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApBarView, + inv: InventoryPanelView, b1, runner, level_active: bool, db, refresh_remaining: int) -> void: + if b1 == null: + _refresh_out_of_battle(stage, info, inv, runner, level_active, db) + return + var s: Dictionary = b1.presentation_snapshot() + var hero: Dictionary = s.hero + if hero.is_empty() or not bool(hero.get("alive", false)): + _refresh_out_of_battle(stage, info, inv, runner, level_active, db) + return + _refresh_hero(stage, info, s) + _refresh_economy(info, ap_bar, s, db, refresh_remaining) + stage.set_enemies(s.enemies) + info.set_status_effects(s.hero_effects) + _fill_inventory(inv, s.inventory) + + +# ---- B 区英雄卡 + C 区数据框 ---- + +static func _refresh_hero(stage: BattleStageView, info: HeroInfoPanelView, s: Dictionary) -> void: + var hero: Dictionary = s.hero + var hp: int = int(hero.hp) + var max_hp: int = int(hero.max_hp) + var atk: int = int(hero.atk) + # 修正 = attack_up 池有效值(hero_offense);来源角标 = 效果 display_name(快照 hero_effects 投影) + var mod_total := 0 + var sources: Array = [] + for eff in s.hero_effects: + if String(eff.effect_id) == "attack_up": + mod_total += int(eff.effective_value) + sources.append("%s×%d" % [String(eff.display_name), int(eff.layers)]) + var mod_text := "" + if mod_total != 0: + mod_text = "(%+d%% %s)" % [mod_total, "·".join(sources)] + stage.hero_card.set_data(hp, max_hp, atk, mod_text, _hero_status_summary(s.hero_effects)) + info.set_hero(hp, max_hp) + info.set_attack(atk, mod_text) + + +static func _hero_status_summary(effects: Array) -> String: + var parts: Array = [] + for eff in effects: + parts.append("%s×%d(剩 %d)" % [String(eff.display_name), int(eff.layers), int(eff.remaining_settlements)]) + return " ".join(parts) if not parts.is_empty() else "状态:无" + + +# ---- 行动经济(AP 点阵 / RAP / 弃图刷新按钮态) ---- + +static func _refresh_economy(info: HeroInfoPanelView, ap_bar: ApBarView, s: Dictionary, db, refresh_remaining: int) -> void: + ap_bar.set_ap(int(s.ap), int(db.action_economy.ap_per_round)) + # RAP 活值投影(C 区点阵)——清零反馈分支(A3 D1 义务)依赖本处持续投喂才会可达 + info.set_rap(int(s.rap), int(db.action_economy.rap_refresh_cost)) + var digging := String(s.phase) == ActionEconomy.PHASE_ACTIVE + ap_bar.set_refresh_state(refresh_remaining, + bool(s.can_request_refresh) and digging) # Waiting 代理已随 OQ12 退役——UI 仅挖掘期受理 + ap_bar.set_stop_enabled(bool(s.hero.get("alive", false)) and digging) + + +# ---- E 区库存常读列表(枚举序 = stock 受理序 E21;来源不呈现——2026-09-12 拍板) ---- + +static func _fill_inventory(inv: InventoryPanelView, inventory: Array) -> void: + # 窗口态占屏(D2 原位窗口激活)——常读刷新不得覆盖窗口条目(窗口关闭后由下一轮刷新恢复常读面) + if inv.is_window_open(): + return + var rows: Array = [] + for inst in inventory: + if inst.status != "InStock": + continue + rows.append(_inventory_row(inst.display_name, String(inst.category), int(inst.power), "可用")) + if rows.is_empty(): + inv.show_empty() + else: + inv.show_inventory(rows) + + +# 行值快照投影派生属性简述(伤害/回复/增益一句话)——来源 {cell, round} 不呈现 +static func _inventory_row(name_: String, category: String, power: int, status: String) -> Dictionary: + var desc := "" + var tooltip := "" + match category: + "damage_item": + desc = "伤害 %d · 一次性" % power + tooltip = "对最前敌造成 %d 点伤害(恒定表值,不吃修正)。触发:轮末道具窗口选用,道具段生效。" % power + "heal_pack": + desc = "回复 %d · 可用" % power + tooltip = "回复 %d 点 HP(超量截断)。触发:轮末道具窗口选用;满血时禁选(不烧包)。" % power + "buff": + desc = "增益 · 结算期生效" + tooltip = "状态效果增益。触发:轮末结算增益/减益段。" + _: + desc = "威力 %d" % power + tooltip = "触发:轮末结算。" + return {"display_name": name_, "category": category, "desc": desc, "tooltip": tooltip, "status": status} + + +# 窗口条目投影(B1 window_opened 载荷 → E 区窗口态行;pos = 窗口条目位置——缺陷④契约) +static func window_rows(items: Array, hero_hp: int, hero_max_hp: int) -> Array: + var rows: Array = [] + for pos in items.size(): + var it: Dictionary = items[pos] + var row := _inventory_row(String(it.display_name), String(it.category), int(it.power), "") + row["pos"] = pos + row["usable_now"] = bool(it.usable_now) + row["reason"] = "HP 已满 · 禁选" if not bool(it.usable_now) else "" + rows.append(row) + return rows + + +# 战斗外(节点间隙/关卡结束)——HP 读 runner 携带值 +static func _refresh_out_of_battle(stage: BattleStageView, info: HeroInfoPanelView, inv: InventoryPanelView, + runner, level_active: bool, db) -> void: + if not level_active: + return + if runner != null: + info.set_hero(runner.hero_hp, runner.effective_max_hp()) + info.set_attack(runner.effective_atk(), "(关卡推进中)") + stage.hero_card.set_data(runner.hero_hp, runner.effective_max_hp(), runner.effective_atk(), "(关卡推进中)", "") + stage.clear_enemies() # 节点间隙清残位敌卡(死亡瞬间的灰化呈现已在结算行驱动完成) + info.set_rap(0, int(db.action_economy.rap_refresh_cost)) # battle_over RAP 清零反馈(A3 义务) + var rows: Array = [] + for inst in runner.inventory(): + if inst.status == "InStock": + rows.append(_inventory_row(inst.display_name, String(inst.category), int(inst.power), "可用")) + if rows.is_empty(): + inv.show_empty() + else: + inv.show_inventory(rows) + + +# 三源拒绝可感知(A2 reject_reason/sub_reason → 玩家文案;pattern 1 双源拒绝反馈) +static func outcome_text(outcome: Dictionary) -> String: + if outcome.accepted: + return "执行完成。" + match String(outcome.get("reject_reason", "")): + "board_face": + var sub := String(outcome.get("sub_reason", "")) + var texts := { + "not_adjacent": "不能执行:目标与小人不相邻", + "target_revealed": "不能执行:目标已开", + "target_concealed": "不能移动:目标未开", + "flag_present": "不能挖:此格有旗——先右键拔旗", + "flag_absent": "不能拔旗:此格无旗", + "no_path": "不能移动:已探明区不连通", + "out_of_catalog": "无效目标", + } + return texts.get(sub, "棋盘面拒绝: " + sub) + "resource_face": + return "AP 不足。" + "phase": + return "结算中,非行动阶段。" + "board_invalid": + return "棋盘数据非法(fail-fast)。" + _: + return "动作被拒。" + + +# ---- 调整板批量模拟报告装配(文案归 presenter——视图只 set_text;code-review 2026-09-12 批) ---- + +static func tune_sim_report(n: int, atk_pct: int, hp_pct: int, wins: int, dmg_all: Array, hp_left_win: Array, hp_boss: Array, rounds_all: Array, death_nodes: Dictionary, base_hp: int) -> String: + var lines: Array = [] + lines.append("[b]模拟 N=%d(atk+%d%% / hp+%d%%)[/b]" % [n, atk_pct, hp_pct]) + lines.append("通关率 [color=#80d080]%.0f%%[/color](%d/%d)" % [100.0 * float(wins) / float(maxi(1, n)), wins, n]) + lines.append("整关承伤:均 %.1f · p90 %.0f(基础 HP %d)" % [_mean(dmg_all), _p90(dmg_all), base_hp]) + if not hp_left_win.is_empty(): + lines.append("通关剩余 HP 均 %.1f · 进 Boss HP 均 %.1f" % [_mean(hp_left_win), _mean(hp_boss)]) + lines.append("平均轮数 %.1f" % _mean(rounds_all)) + if death_nodes.is_empty(): + lines.append("败北节点:—") + else: + var parts: Array = [] + for k in death_nodes.keys(): + parts.append("%s×%d" % [k, death_nodes[k]]) + lines.append("败北节点:%s(0-3=战斗序,4=Boss)" % " ".join(parts)) + return "\n".join(lines) + + +static func _mean(a: Array) -> float: + if a.is_empty(): + return 0.0 + var t := 0.0 + for v in a: + t += float(v) + return t / a.size() + + +static func _p90(a: Array) -> float: + if a.is_empty(): + return 0.0 + var b := a.duplicate() + b.sort() + return float(b[mini(b.size() - 1, int(0.9 * b.size()))]) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd.uid new file mode 100644 index 00000000..45bd35b7 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd.uid @@ -0,0 +1 @@ +uid://d15kn31qh3ae diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd new file mode 100644 index 00000000..6c05913e --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd @@ -0,0 +1,641 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 线性第一关(战×2→道具→战×2→恢复→Boss)能否承载"数值提升→通关期望改善"的元进度验证 +# 实现 GDD:design/ux/battle.md(战斗屏五区 A/B/C/D/E + 全屏 States + 遥测)。 +# Date: 2026-09-12(battle.md 五区改造批;基础 = 2026-09-11 D1-lite 组合根;code-review 修复批重构) +# 2026-09-13(封装审查修复批):核心状态读取收口至 B1/runner 成文只读面(presentation_snapshot / +# current_board / current_battle 等——禁链式穿透 b1.b2.hero 一类路径);调试模式编排抽离 +# debug_session.gd(规范 1.1 审视线 + 调试语义单一实现)。 +# 组合根(composition root):唯一的模式流转与 core↔view 接线处。 +# 职责边界:① 构建五区视图并接线信号(节点构建归 ui_assembler.gd——规范 1.1,本类只接线) +# ② 模式管理(完整关卡 C1 runner / 调试两层——后者编排归 debug_session.gd;单场原语仅探针使用) +# ③ 核心写调用只在此处发生(a2.settle_batch / b1.submit_window_selection / 刷新编排入口 / runner 推进; +# E4 收口:加成值零表写——面板百分比经 runner.set_hero_override / debug_session 生效值 Callable +# 以 encounter 参数入战斗,db.hero 运行期只读) +# ④ 全屏 States 状态机(挖掘/结算/道具窗口/终局)与一次性提示去重。 +# 渲染与文案归 views/,核心状态→文案装配归 hud_presenter.gd 与 battle_narrator.gd,节点构建归 +# ui_assembler.gd,模态层级归 modal_stack.gd,批量模拟编排归 tune_sim_controller.gd,调试模式编排归 +# debug_session.gd——本类不拼呈现字符串。 +# ADR-0001 纪律:呈现层永不直接订阅 A2/A3——只订 B1/runner 转发信号;相位读取仅用于驱动视图状态。 +extends Control + +const UiAssembler := preload("res://presentation/ui_assembler.gd") +const Narrator := preload("res://presentation/battle_narrator.gd") +const ModalStack := preload("res://presentation/modal_stack.gd") +const TuneSimController := preload("res://presentation/tune_sim_controller.gd") +const DebugSessionScript := preload("res://presentation/debug_session.gd") + +var db +var b1 = null # 当前战斗编排器(关卡与调试模式统一读它) +var runner = null # C1 线性关卡推进器(完整关卡模式) +var logger = null # 试玩日志器(JSONL 持久化 + 卡死诊断转储——呈现层组件) +var _level_active := false +var _seed_edit: LineEdit +var _start_overlay: Control +var _end_overlay: BattleEndOverlay +var _inter_overlay: Control +var _inter_label: Label + +# 调试模式编排(单场/连续两层——状态机与战斗生命周期归 debug_session.gd,本类只接线与出 UI) +var _debug = null + +# 视图(views/)——battle.md 五区 +var strip_view: LevelStripView # A 区 +var stage: BattleStageView # B 区(含 log_view 明细带) +var log_view: BattleLogView +var info_panel: HeroInfoPanelView # C 区 +var board_zone: BoardZoneView # D 区(含 ap_bar/board_view/toast/fx) +var board_view: BoardView # 探针兼容别名(= board_zone.board_view) +var ap_bar: ApBarView # 探针兼容别名(= board_zone.ap_bar) +var inventory_panel: InventoryPanelView # E 区(原位窗口态) +# 模态 overlay(z 序 = 登记序;pattern 6 层级纪律) +var legend_overlay: LegendOverlay +var review_overlay: SettlementReviewOverlay +var node_overlay: NodeOverlay +var tune_panel: TunePanel + +var _modals = null # 模态层级状态机(modal_stack.gd——缺陷⑤纪律载体) +var _tune_sim = null # 调整板批量模拟控制器(tune_sim_controller.gd) +var _hints_shown := {} # 一次性非模态提示去重(每 run 一次:fog_first/chord/no_adjacent) +var _refresh_used_round := 0 # 本轮已受理刷新次数(遥测 round_refresh_index——每轮 round_started 归零) +var _boss_active := false # Boss 遭遇变体(阶段指示附 Boss 名) +var _window_payload: Array = [] # 窗口投影缓存(ux_window_choice 遥测读 usable_now/item_id) + + +func _ready() -> void: + db = get_node("/root/ConfigDB") + if not db.is_ready: + var ok: bool = db.load_tables("res://data/tables", false) + if not ok: + _fatal_screen("数值表加载失败——见输出错误清单(fail-fast)") + return + _debug = DebugSessionScript.new(db, get_node("/root/RngService"), + Callable(self, "_debug_hero_override"), Callable(self, "_seed_value")) + _debug.battle_created.connect(_on_debug_battle_created) + _debug.battle_ready.connect(_on_debug_battle_ready) + _build_ui() + _start_overlay.visible = true + + +# ==================== 视图接线(battle.md 五区布局 1280×800;节点构建归 UiAssembler) ==================== + +func _build_ui() -> void: + var root := VBoxContainer.new() + add_child(root) + var five: Dictionary = UiAssembler.build_five_zone(root, db) + strip_view = five.strip_view + stage = five.stage + log_view = five.log_view + info_panel = five.info_panel + board_zone = five.board_zone + board_view = five.board_view # 探针兼容别名(= board_zone.board_view) + ap_bar = five.ap_bar # 探针兼容别名(= board_zone.ap_bar) + inventory_panel = five.inventory_panel + log_view.review_requested.connect(_open_review_from_band) + board_zone.action_requested.connect(_on_board_action) + board_zone.stop_pressed.connect(_on_stop_pressed) + board_zone.refresh_confirmed.connect(_on_refresh_confirmed) + board_zone.legend_toggled.connect(_on_legend_toggled) + board_zone.hover_forbidden.connect(_on_hover_forbidden) + board_zone.chord_attempted.connect(_on_chord_attempted) + board_zone.fog_region_completed.connect(_on_fog_region_completed) + inventory_panel.item_selected.connect(_on_window_item_selected) + inventory_panel.skipped.connect(_on_window_skipped) + inventory_panel.reject_reason.connect(_on_inventory_reject) + + # 奖励徽章数据源:B4 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效) + board_view.set_identity_resolver(_identity_lookup) + + logger = PlaytestLogger.new() + logger.ap_per_round = int(db.action_economy.ap_per_round) + + # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 终局 < 调整板 < 开始) + var overlays: Dictionary = UiAssembler.build_overlays(self, db, int(db.hero.atk), int(db.hero.max_hp)) + legend_overlay = overlays.legend_overlay + review_overlay = overlays.review_overlay + node_overlay = overlays.node_overlay + _end_overlay = overlays.end_overlay + tune_panel = overlays.tune_panel + legend_overlay.closed.connect(_on_modal_closed.bind(legend_overlay)) + review_overlay.closed.connect(_on_modal_closed.bind(review_overlay)) + node_overlay.continue_pressed.connect(_on_node_continue) + _end_overlay.review_pressed.connect(_on_end_review) + _end_overlay.continue_pressed.connect(_on_battle_end_continue) + tune_panel.closed.connect(_close_tune_panel) + tune_panel.apply_requested.connect(_on_tune_apply) + tune_panel.simulate_requested.connect(_on_tune_simulate) + _modals = ModalStack.new() + for m: Control in [legend_overlay, review_overlay, node_overlay, _end_overlay, tune_panel]: + _register_modal(m) + _tune_sim = TuneSimController.new(db, tune_panel, int(db.hero.max_hp)) + + var start: Dictionary = UiAssembler.build_start_overlay(self) + _start_overlay = start.overlay + _seed_edit = start.seed_edit + start.lvbtn.pressed.connect(_start_full_level) + start.tunebtn.pressed.connect(_show_tune_panel) + start.dbgbtn.pressed.connect(_start_debug_levels) + + var inter: Dictionary = UiAssembler.build_inter_overlay(self) + _inter_overlay = inter.overlay + _inter_label = inter.label + inter.next_btn.pressed.connect(_on_inter_next_pressed) + + var diag_btn := UiAssembler.build_diag_button(self) + diag_btn.pressed.connect(_on_diagnose_pressed) + + +func _fatal_screen(msg: String) -> void: + var l := UiKit.label(msg, 20, true) + l.add_theme_color_override("font_color", Color8(200, 60, 48)) + l.set_anchors_preset(Control.PRESET_CENTER) + add_child(l) + + +# ==================== 模态 overlay 层级管理(pattern 6 / 缺陷⑤——状态机归 modal_stack.gd) ==================== + +func _register_modal(m: Control) -> void: + _modals.register(m) + + +func _open_modal(m: Control) -> void: + # 打开时显式隐藏树序在其上方的可见层(遮挡物隐藏),关闭恢复——单一模态栈 + _modals.open(m) + + +func _close_modal(m: Control) -> void: + _modals.close(m) + + +func _on_modal_closed(m: Control) -> void: + _close_modal(m) + + +func _input_locked() -> bool: + # 模态/非战斗期棋盘输入锁(各 overlay 自身可见即锁定) + return legend_overlay.is_open() or review_overlay.is_open() or _end_overlay.is_open() \ + or node_overlay.visible or tune_panel.visible or _start_overlay.visible \ + or _inter_overlay.visible or b1 == null + + +# ==================== 完整关卡模式(C1 线性推进器)==================== + +func _start_full_level() -> void: + _start_overlay.visible = false + # E4:加成经 runner.set_hero_override → encounter 参数入战斗(零表写——无双写叠加隐患) + runner = LevelRunner.new(db, get_node("/root/RngService")) + runner.set_hero_override(tune_panel.atk_pct(), tune_panel.hp_pct()) + runner.step_row.connect(_on_step_row) + runner.window_opened.connect(_on_window_opened) + runner.round_started.connect(_on_round_started) + runner.board_changed.connect(_on_board_changed) + runner.node_entered.connect(_on_runner_node) + runner.level_finished.connect(_on_level_finished) + _level_active = true + _reset_run_scope() + log_view.clear() + var seed_val := _seed_value() + logger.start_session(seed_val) # 各战斗的 attach 在 _on_runner_node 内(b1 逐战创建) + if not runner.start_level(1, seed_val): + _fatal_screen("关卡启动失败——见输出日志") + return + log_view.append_bb(Narrator.level_open_line(String(runner.level_row.get("display_name", "")), + seed_val, int(db.hero.atk), int(db.hero.max_hp), + tune_panel.atk_pct() > 0 or tune_panel.hp_pct() > 0)) + + +func _reset_run_scope() -> void: + # 每 run 一次的提示去重与轮内计数(弃图刷新遥测轮内序号) + _hints_shown = {} + _refresh_used_round = 0 + _boss_active = false + inventory_panel.close_window() + + +func _on_runner_node(node: Dictionary, info: Dictionary) -> void: + var t := String(node.get("type", "")) + if t == "battle" or t == "boss": + b1 = runner.current_battle() # 呈现层统一读 b1(与调试模式共用接线) + logger.attach_battle(b1) + _boss_active = t == "boss" + _show_board_fx_for_node(t) + log_view.append_bb(Narrator.node_banner(runner, node, t)) + elif t == "reward": + var snap: Dictionary = info.get("snapshot", {}) + _open_modal(node_overlay) + node_overlay.show_reward(snap) + log_view.append_bb(Narrator.reward_line(snap)) + elif t == "rest": + var healed: int = int(info.get("healed", 0)) + _open_modal(node_overlay) + node_overlay.show_rest(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0))) + log_view.append_bb(Narrator.rest_line(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0)))) + logger.log_node(node, info, runner.node_index) + strip_view.refresh(runner) + _refresh_presentations() + + +func _on_level_finished(payload: Dictionary) -> void: + _level_active = false + var result: String = String(payload.get("result", "")) + b1 = null + _sync_settlement_visuals() # → 全关 + info_panel.set_status_cleared_feedback() # 状态槽全清可见反馈(B3×4④) + stage.gray_all_enemies() # 死亡兜底:卡体灰化残位(禁瞬消语义的战斗收尾面) + if result == "victory": + # 关卡结算:库存清空可见反馈(B4 D1×⑤)+ 胜利终局 overlay(金蜡封落于棋盘中央) + var remaining: int = int(payload.get("remaining_items", 0)) + inventory_panel.play_cleared_feedback(remaining) + _end_overlay.open_victory(Narrator.level_end_text("victory", payload, runner.effective_max_hp()), + "返回冒险", board_view.get_global_rect()) + else: + _end_overlay.open_defeat(Narrator.level_end_text("defeat", payload, 0), + "返回", board_view.get_global_rect()) + logger.log_level_end(payload) # 试玩时间线:关卡终局 + runner.restore_hero_base() # 关卡结束——覆盖值归零(下次从面板重新应用) + strip_view.refresh(runner) + _open_modal(_end_overlay) + _refresh_presentations() + + +func _on_battle_end_continue() -> void: + # 终局出口:返回开始界面(切片无下一关数据面——NodeOverlay/关卡结算屏归相邻域 spec) + _close_modal(_end_overlay) + _end_overlay.close() + _debug.reset() + if runner != null: + runner.restore_hero_base() + runner = null + b1 = null + tune_panel.reset_sliders() + strip_view.refresh(null) + _start_overlay.visible = true + _refresh_presentations() + + +func _on_end_review() -> void: + review_overlay.open(log_view.parsed_text()) + + +# ==================== 调试模式(连续两层——编排归 debug_session.gd; +# 单场遭遇用户入口已删(2026-09-13 用户裁决)——debug_session.start_single 原语仅探针使用)==================== + +func _start_debug_levels() -> void: + tune_panel.reset_sliders() + _start_overlay.visible = false + log_view.clear() + log_view.append_bb(Narrator.debug_open_line(_seed_value(), 1, true)) + _reset_run_scope() + if not _debug.start_debug_levels(): + _fatal_screen("战斗启动失败——见输出日志") + + +func _on_debug_battle_created(b1_ref, _level: int, _carry: int) -> void: + # begin_battle 前接手 + 接线(ADR-0001:signal.connect 唯一归组合根)。b1 必须先于 begin_battle + # 就位——begin 期间发出的 board_changed 要用新板重建棋盘并触发雾首遇提示(缺陷①同款时序义务) + b1 = b1_ref + _wire_battle(b1_ref) + + +func _on_debug_battle_ready(b1_ref, _level: int, _carry: int) -> void: + b1 = b1_ref + board_view.rebuild(b1.a2.current_board(), b1.a2) + _check_board_hints() + _refresh_presentations() + + +func _on_inter_next_pressed() -> void: + _inter_overlay.visible = false + if not _debug.next_layer(): + _fatal_screen("战斗启动失败——见输出日志") + + +# E4:加成生效值供给(debug_session 注入 Callable——每次从面板百分比现算,经 encounter 传参零表写; +# 原表写路径 _apply_hero_override/_scaled_override 已废除,双写叠加 6→18→54 整类隐患随之移除) +func _debug_hero_override() -> Dictionary: + return {"atk": _scaled(int(db.hero.atk), tune_panel.atk_pct()), + "max_hp": _scaled(int(db.hero.max_hp), tune_panel.hp_pct())} + + +func _scaled(base: int, pct: int) -> int: + return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0))) + + +# 奖励徽章身份解析(组合根守卫:仅挖掘期 Active 相位内查询——B4 Ready 域外零读、零诊断噪音) +func _identity_lookup(c: Vector2i): + if b1 == null or b1.a2.current_board() == null or b1.a3.phase != b1.a3.PHASE_ACTIVE: + return null + return b1.query_reward_identity(c) + + +func _show_board_fx_for_node(node_type: String = "") -> void: + # Boss 遭遇变体:紫赤影子爬棋盘一次性占位演出(§2.6) + if node_type == "boss": + board_zone.fx.play_boss_shadow() + + +func _on_battle_finished(payload: Dictionary) -> void: + # 仅调试模式接线(完整关卡的结束归 C1 runner._on_level_finished) + var result: String = String(payload.get("result", "")) + _sync_settlement_visuals() # → 全关(battle_over 反馈面) + stage.gray_all_enemies() # 死亡兜底:卡体灰化残位(禁瞬消语义的战斗收尾面) + info_panel.set_rap(0, int(db.action_economy.rap_refresh_cost)) # RAP 清零滚动 + 微文案 + info_panel.set_status_cleared_feedback() # 状态槽全清可见反馈(B3×4④) + var decision: Dictionary = _debug.on_battle_finished(payload) + match String(decision.outcome): + "next_layer": + _inter_label.text = "第 %d 层通过!\n库存道具 ×%d · HP %d/%d 带入下一层" % [ + int(decision.layer), int(decision.items_left), int(decision.carry_hp), int(db.hero.max_hp)] + _inter_overlay.visible = true + _refresh_presentations() + return + "level_end": + inventory_panel.play_cleared_feedback(int(decision.remaining)) + _end_overlay.open_victory(Narrator.battle_end_text("victory", true, int(decision.remaining)), + "返回", board_view.get_global_rect()) + "standalone_end": + if result == "victory": + _end_overlay.open_victory(Narrator.battle_end_text("victory", false, 0), + "回到遭遇选择", board_view.get_global_rect()) + else: + _end_overlay.open_defeat(Narrator.battle_end_text("defeat", false, 0), + "返回", board_view.get_global_rect()) + _open_modal(_end_overlay) + _refresh_presentations() + + +# ==================== core↔view 接线(战斗信号统一走这里)==================== + +func _wire_battle(b1_ref) -> void: + b1_ref.step_row.connect(_on_step_row) + b1_ref.window_opened.connect(_on_window_opened) + b1_ref.battle_finished.connect(_on_battle_finished) + b1_ref.round_started.connect(_on_round_started) + b1_ref.board_changed.connect(_on_board_changed) + + +func _on_step_row(row: Dictionary) -> void: + log_view.append_row(row) + stage.on_step_row_fx(row) + # 高光演出(B1×3①② 占位):完美排雷横幅 / 标中旗加重弹道 + if String(row.get("type", "")) == "highlight": + board_zone.fx.play_perfect_sweep() + elif String(row.get("highlight", "")) == "flag_hit": + board_zone.fx.play_flag_hit_projectile(true) + _refresh_presentations() + + +func _on_round_started(ri: int) -> void: + log_view.finish_settlement_round() # 上轮摘要行(挖掘期 1 行——明细带 pattern 3) + log_view.append_bb("[color=#e0b860]── 第 %d 轮 ──[/color]" % ri) + _refresh_used_round = 0 + _refresh_presentations() + + +func _on_board_changed() -> void: + # 关卡模式下 board_changed 可能先于主场景接手 b1 到达(begin_battle 期间发)——先从 runner 同步 + if b1 == null and _level_active and runner != null: + b1 = runner.current_battle() + if b1 == null: + return + board_view.rebuild(b1.a2.current_board(), b1.a2) + _check_board_hints() + _refresh_presentations() + + +# ---- 一次性非模态提示(每 run 一次——pattern 10)与诚实提示 ---- + +func _check_board_hints() -> void: + var board = b1.a2.current_board() + if board == null: + return + # 零合法板(A1 EC5 降级)诚实提示——非静默(toast,非模态) + if board.illegal_product: + board_zone.toast.show_toast("本板为零合法板降级产物——出图器已尽力,数据面诚实告知", "warn") + # 雾首遇(每 run 一次;必含三要点:无雷盲区不赌命 / 雾格不传播连锁 / 雾内插旗纯损——A4 D1×②) + if not board.fog_cells().is_empty() and not _hints_shown.has("fog_first"): + _hints_shown["fog_first"] = true + _show_hint("迷雾=无雷盲区,不赌命开雾;雾格不传播连锁;雾内插旗纯损。", "fog_first") + + +func _on_hover_forbidden(_cell: Vector2i) -> void: + if not _hints_shown.has("no_adjacent"): + _show_hint("需先走近(点击已开格移动,免费)", "no_adjacent") + + +func _on_chord_attempted(_cell: Vector2i) -> void: + if not _hints_shown.has("chord"): + _show_hint("本作无双击快开——插旗后轮末自动判定标中;点数字格可移动小人", "chord") + + +func _on_fog_region_completed(region_size: int) -> void: + # 雾区全开一次性完成信号(视觉合并域 = 连通分量,禁实例级双响——EC11) + board_zone.toast.show_toast("雾区已全开(%d 格)——迷雾域尽收眼底" % region_size, "gold") + + +func _show_hint(text: String, hint_id: String) -> void: + _hints_shown[hint_id] = true + logger.log_ux("ux_hint_shown", {"hint_id": hint_id}) + board_zone.toast.show_toast(text, "info") + + +# ---- 道具窗口(E 区原位激活——布局决策 D2 / pattern 2) ---- + +func _on_window_opened(items: Array) -> void: + _window_payload = items + var hero: Dictionary = b1.presentation_snapshot().hero # E1 收口:经呈现快照面读 HP + var hp: int = int(hero.get("hp", 0)) if not hero.is_empty() else 0 + var max_hp: int = int(hero.get("max_hp", 0)) if not hero.is_empty() else 1 + inventory_panel.open_window(HudPresenter.window_rows(items, hp, max_hp)) + _sync_settlement_visuals() # 阶段指示 → 道具窗口 + + +func _on_window_item_selected(pos: int) -> void: + # 选择语义 = 窗口条目位置(缺陷④契约——非库存句柄) + if pos >= 0 and pos < _window_payload.size(): + var it: Dictionary = _window_payload[pos] + logger.log_ux("ux_window_choice", {"selected_item_id": String(it.get("item_id", "")), + "usable_now": true, "skip": false}) + inventory_panel.close_window() + if b1 != null: + b1.submit_window_selection([pos]) + _refresh_presentations() + + +func _on_window_skipped() -> void: + logger.log_ux("ux_window_choice", {"selected_item_id": "", "usable_now": false, "skip": true}) + inventory_panel.close_window() + if b1 != null: + b1.submit_window_selection([]) + _refresh_presentations() + + +func _on_inventory_reject(text: String) -> void: + board_zone.toast.show_toast(text, "warn") + + +func _on_node_continue() -> void: + _close_modal(node_overlay) + if runner != null: + runner.continue_after_node() + + +# ==================== 玩家意图 → 核心写调用(唯一出口)==================== + +func _on_board_action(cell: Vector2i, kind: String) -> void: + # 玩家动作 → 核心写调用的唯一通道(模态/窗口期/无战斗拒绝在此收口) + if _input_locked() or inventory_panel.is_window_open(): + return + var outcomes: Array = b1.a2.settle_batch([{"action": kind, "cell": cell}]) + var accepted: bool = not outcomes.is_empty() and bool(outcomes[0].accepted) + logger.log_action(kind, cell, accepted) + _handle_outcome_feedback(outcomes[0] if not outcomes.is_empty() else {}) + # settle_batch 内部同步走完 结算→战斗结束→关卡推进(事件同步发射)——关卡终局时 b1 已被 + # runner 置空,清图演出判定须先判 b1(基线既有重入隐患,2026-09-13 加固) + if accepted and kind == "dig" and b1 != null and b1.a2.current_board() != null and b1.a2.clear_map_cleared(): + board_zone.fx.play_clear_burst() + board_view.refresh() + _refresh_presentations() + + +func _on_stop_pressed() -> void: + if _input_locked() or inventory_panel.is_window_open(): + return + # stop 经批次提交(A2 D1×6①——不直调 A3) + var outcomes: Array = b1.a2.settle_batch([{"action": "stop"}]) + logger.log_action("stop", Vector2i(-1, -1), not outcomes.is_empty() and bool(outcomes[0].accepted)) + if not outcomes.is_empty() and String(outcomes[0].get("outcome", "")) == "voided": + # voided ≠ 拒绝——独立文案(pattern 1:防静默改判读感欺骗) + board_zone.toast.show_toast("本轮已清图,停手未执行 · 剩余 AP 已作废", "warn") + logger.log_ux("ux_reject", {"source": "voided", "sub_reason": "stop_voided"}) + board_view.refresh() + _refresh_presentations() + + +func _on_refresh_confirmed() -> void: + # 弃图刷新(挖掘期 Active 当场换图继续挖——D4;RAP−8 · 旧板旗作废,每轮上限 2 次) + if _input_locked() or inventory_panel.is_window_open(): + return + var ok: bool = false + if _level_active and runner != null: + ok = runner.player_request_refresh() + elif b1 != null: + ok = b1.player_request_refresh() + logger.log_action("refresh", Vector2i(-1, -1), ok) + if ok: + _refresh_used_round += 1 + logger.log_ux("ux_refresh_attempt", {"result": "accepted", "round_refresh_index": _refresh_used_round}) + board_zone.toast.show_toast("RAP−8 · 旧板旗作废——新图已就位,AP 保留继续挖", "gold") + else: + # 资源面/上限拒绝(EC13 合法拒绝可见;A3 无载荷——客户端按上下文推断) + var rap: int = int(b1.a3.rap) if b1 != null else 0 + var cost: int = int(db.action_economy.rap_refresh_cost) + var result := "rejected_rap" if rap < cost else "rejected_cap" + logger.log_ux("ux_refresh_attempt", {"result": result, "round_refresh_index": _refresh_used_round + 1}) + if result == "rejected_rap": + board_zone.toast.show_toast("RAP 不足 · 还差 %d 点(弃图刷新需 8)" % (cost - rap), "warn") + info_panel.pulse_rap() + else: + board_zone.toast.show_toast("本轮刷新次数已用完(每轮 2 次)", "warn") + board_view.refresh() + _refresh_presentations() + + +func _handle_outcome_feedback(outcome: Dictionary) -> void: + # 双源拒绝反馈(pattern 1):每次拒绝玩家可感知 + 来源可区分 + 返回值命令式驱动(无拒绝事件) + if outcome.is_empty() or bool(outcome.get("accepted", false)): + return + logger.log_ux("ux_reject", {"source": String(outcome.get("reject_reason", "")), + "sub_reason": String(outcome.get("sub_reason", ""))}) + board_zone.toast.show_toast(HudPresenter.outcome_text(outcome), "warn") + if String(outcome.get("reject_reason", "")) == "resource_face": + ap_bar.pulse_ap() + + +# ==================== 全屏 States 状态机(挖掘/结算/窗口/终局)==================== + +func _refresh_presentations() -> void: + # 每个核心信号/写调用返回后的全量重查装配(更新触发注记——无推送信号,命令式重查) + var refresh_remaining: int = maxi(0, int(db.action_economy.refresh_max_per_round) - _refresh_used_round) + HudPresenter.refresh(stage, info_panel, ap_bar, inventory_panel, b1, runner, _level_active, db, refresh_remaining) + _sync_settlement_visuals() + + +func _sync_settlement_visuals() -> void: + # 结算阶段(round_end → 下一轮):D 区去饱和+金橙偏移锁输入(明度不变)+ 明细带展开 84px + var settling := _is_settling() + board_view.set_desaturated(settling) + board_view.set_dig_glow_enabled(not settling) + stage.set_settle_focus(settling) + log_view.set_mode("settle" if settling else "dig") + stage.set_round_text(_phase_text()) + + +func _is_settling() -> bool: + if b1 == null or not b1.is_battle_live(): + return false + return b1.a3.phase == b1.a3.PHASE_WAITING + + +func _phase_text() -> String: + # 文案装配归 narrator(b1 缺位 = 非战斗态——组合根模式状态守卫) + if b1 == null: + return "第 — 轮 · —" + return Narrator.phase_text(int(b1.round_index), String(b1.battle_result), + inventory_panel.is_window_open(), b1.a3.phase == b1.a3.PHASE_WAITING, + _boss_active, Narrator.boss_name(runner, db)) + + +# ==================== B 区明细带 / 图例(模态入口)==================== + +func _open_review_from_band() -> void: + review_overlay.open(log_view.parsed_text()) + + +func _on_legend_toggled() -> void: + if legend_overlay.is_open(): + _close_modal(legend_overlay) + else: + _open_modal(legend_overlay) + + +# ==================== 诊断转储(卡死时点击——全量核心状态 + 环形缓冲)==================== + +func _on_diagnose_pressed() -> void: + var ctx := {"mode": "level" if _level_active else "debug", "seed": _seed_value()} + if _level_active and runner != null: + ctx["runner"] = {"node_index": runner.node_index, "level_result": String(runner.level_result), + "hero_hp": runner.hero_hp, "b1_is_null": runner.current_battle() == null} + if b1 != null: + ctx["b1"] = b1.diagnostic_snapshot() # 诊断观测面(私有字段零直读——code-review 2026-09-12) + var path: String = logger.dump_diagnosis(ctx) + board_zone.toast.show_toast("已转储诊断: " + path, "info") + + +# ==================== 数值调整板(加成应用/批量模拟编排——组合根承接,视图纯 UI)==================== + +func _show_tune_panel() -> void: + _start_overlay.visible = false + tune_panel.visible = true + + +func _close_tune_panel() -> void: + tune_panel.visible = false + _start_overlay.visible = true + + +func _on_tune_apply(atk_pct: int, hp_pct: int) -> void: + # E4:加成无持久态可"落"——各战斗开局经 _debug_hero_override/runner 现算生效(面板仅关闭回开始层) + _close_tune_panel() + + +func _on_tune_simulate(n: int) -> void: + # 批量模拟编排 = 控制器承接(tune_sim_controller.gd);E4 后无表态需复位——模拟走 runner 覆盖参数 + _tune_sim.run(n) + + +# ==================== 工具 ==================== + +func _seed_value() -> int: + if _seed_edit.text.is_valid_int(): + return int(_seed_edit.text) + return 42 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd.uid new file mode 100644 index 00000000..02bcb0e8 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd.uid @@ -0,0 +1 @@ +uid://dcbnvi7tvlkun diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/main.tscn b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.tscn new file mode 100644 index 00000000..14272fc3 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://slice0main0scene"] + +[ext_resource type="Script" path="res://presentation/main.gd" id="1_main"] + +[node name="Main" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_main") diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/modal_stack.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/modal_stack.gd new file mode 100644 index 00000000..4dbdb370 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/modal_stack.gd @@ -0,0 +1,34 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/interaction-patterns.md(pattern 6 模态 overlay 层级纪律)。 +# Date: 2026-09-12(code-review 修复批——自 main.gd 拆出,规范 1.1 状态装配独立成类) +# 模态层级状态机:登记序 = 树序 = z 遮挡序;打开时显式隐藏树序在其上方的可见层、关闭恢复 +# (缺陷⑤纪律载体:功能存在 ≠ 功能可达——遮挡关系不得靠树序巧合)。 +# 纯逻辑零节点构建——组合根持有本类并喂入 Control;不持久化核心状态。 +extends RefCounted + +var _list: Array = [] # 登记序 = z 遮挡序(低 → 高) +var _occluded := {} # modal → 被其隐藏的上方层(关闭恢复) + + +func register(m: Control) -> void: + _list.append(m) + + +func open(m: Control) -> void: + var hidden: Array = [] + var idx := _list.find(m) + for i in range(idx + 1, _list.size()): + var higher: Control = _list[i] + if is_instance_valid(higher) and higher.visible: + higher.visible = false + hidden.append(higher) + _occluded[m] = hidden + m.visible = true + + +func close(m: Control) -> void: + m.visible = false + for h in _occluded.get(m, []): + if is_instance_valid(h): + h.visible = true + _occluded.erase(m) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/modal_stack.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/modal_stack.gd.uid new file mode 100644 index 00000000..cb03ec17 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/modal_stack.gd.uid @@ -0,0 +1 @@ +uid://bk34o2kt2gfbq diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd new file mode 100644 index 00000000..6662e4b0 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd @@ -0,0 +1,165 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(§Events Fired 遥测义务——ux 行族)。 +# Date: 2026-09-12(五区改造批增 ux 遥测;基础 = 2026-09-12 日志器首版) +# 试玩日志器(呈现层组件——订阅核心信号持久化 JSONL,供数值分析;卡死诊断转储)。 +# 职责边界:核心层只发 settlement_snapshot 信号(B1)/既有信号(battle_finished/node_entered/ +# level_finished/step_row);本组件负责订阅、缓冲与写文件——核心零日志感知(零 UI 依赖不变)。 +# 文件 = user://logs/playtest__<时间戳>.jsonl(每行一次结算/终局/节点的全量状态 JSON)。 +# ux 行族(呈现层观测,不改 core):ux_round_input_ratio(原始输入动作数∶AP 消耗)、 +# ux_round_exit{path_tag}(快照 round_facts.path——呈现层只订 B1 的 ADR 纪律下唯一合法来源)、 +# ux_refresh_attempt / ux_window_choice / ux_reject / ux_hint_shown(组合根显式上报)。 +# 诊断转储 = 全量核心状态 + 最近结算/操作环形缓冲 → user://logs/diag_<时间戳>.json(卡死时点按钮)。 +class_name PlaytestLogger +extends RefCounted + +const RING_SIZE := 200 + +var _file: FileAccess = null +var _path := "" +var _seed := 0 +var _recent_steps: Array = [] # 环形缓冲:最近结算行 +var _recent_actions: Array = [] # 环形缓冲:最近玩家动作 +var _snap_count := 0 +var ap_per_round := 8 # 组合根注入(AP 消耗口径分母——A3 无推送信号) +var _ux_inputs := 0 # 本结算轮原始输入动作数(log_action 计数,快照后归零) + + +# 开启会话(每次 start_level/start_battle 调用——按种子分文件)。返回日志路径。 +func start_session(seed_val: int) -> String: + _seed = seed_val + _recent_steps.clear() + _recent_actions.clear() + _snap_count = 0 + DirAccess.make_dir_recursive_absolute("user://logs") + var stamp: String = Time.get_datetime_string_from_system().replace(":", "").replace("-", "").replace(" ", "_") + _path = "user://logs/playtest_%d_%s.jsonl" % [seed_val, stamp] + if _file != null: + _file.close() + _file = FileAccess.open(_path, FileAccess.WRITE) + _append({"kind": "session", "seed": seed_val, "ts": Time.get_datetime_string_from_system()}) + return _path + + +func close_session() -> void: + if _file != null: + _file.close() + _file = null + + +func log_path() -> String: + return _path + + +func attach_battle(b1) -> void: + b1.step_row.connect(_on_step_row) + b1.settlement_snapshot.connect(_on_settlement_snapshot) + b1.battle_finished.connect(func(p): _append({ + "kind": "battle_end", "result": String(p.get("result", "")), + "hero_hp": int(p.get("hero_hp", 0)), "remaining_items": int(p.get("remaining_items", 0))})) + + +# ---- ux 遥测(battle.md §Events Fired——呈现层观测行族,不改 core) ---- + +func log_ux(ux_kind: String, payload: Dictionary) -> void: + var line := {"kind": "ux", "ux": ux_kind} + for k in payload.keys(): + line[k] = payload[k] + _append(line) + + +func log_action(kind: String, cell: Vector2i, accepted: bool, extra: Dictionary = {}) -> void: + _ux_inputs += 1 # 原始输入动作数(含被拒——P4 ≤2 护栏观测口径 = 原始输入) + # 逐操作落盘(2026-09-12 用户要求:操作记录完整持久化——此前仅入环形缓冲、卡死诊断才可见) + var line := {"kind": "action", "op": kind, "cell": [cell.x, cell.y], "accepted": accepted} + for k in extra.keys(): + line[k] = extra[k] + _append(line) + _ring(_recent_actions, {"kind": kind, "cell": [cell.x, cell.y], "accepted": accepted}.merged(extra)) + + +func _on_settlement_snapshot(snap: Dictionary) -> void: + _append(snap) + # ux_round_exit{path_tag}:对齐上游封闭枚举 {stopped, ap_exhausted, cleared} + var facts: Dictionary = snap.get("round_facts", {}) + log_ux("ux_round_exit", {"path_tag": String(facts.get("path", ""))}) + # ux_round_input_ratio:原始输入动作数∶AP 消耗(本轮 AP 配额 − 快照时剩余 = 消耗) + var economy: Dictionary = snap.get("economy", {}) + var ap_left: int = int(economy.get("ap", 0)) + var consumed: int = clampi(ap_per_round - ap_left, 0, ap_per_round) + log_ux("ux_round_input_ratio", {"inputs": _ux_inputs, "ap_consumed": consumed, + "ratio": float(_ux_inputs) / float(maxi(consumed, 1))}) + _ux_inputs = 0 + + +func log_node(node: Dictionary, info: Dictionary, node_index: int) -> void: + var t := String(node.get("type", "")) + var line := {"kind": "node", "type": t, "index": node_index} + if t == "battle" or t == "boss": + line["depth"] = int(node.get("node_depth", 0)) + elif t == "reward": + var snap: Dictionary = info.get("snapshot", {}) + line["reward_id"] = String(snap.get("reward_id", "")) + line["power"] = int(snap.get("power", 0)) + elif t == "rest": + line["healed"] = int(info.get("healed", 0)) + _append(line) + + +# 批量模拟会话的局次标记(一文件多局——run_start 行携带该局种子/档位,供逐局切分) +func log_run(meta: Dictionary) -> void: + var line := {"kind": "run"} + for k in meta.keys(): + line[k] = meta[k] + _append(line) + + +func log_level_end(payload: Dictionary) -> void: + _append({"kind": "level_end", "result": String(payload.get("result", "")), + "node_index": int(payload.get("node_index", -1)), "hp": int(payload.get("hp", 0)), + "remaining_items": int(payload.get("remaining_items", -1))}) + + +func _on_step_row(row: Dictionary) -> void: + _ring(_recent_steps, {"t": row.type, "src": row.source_class, "v": int(row.value), "r": int(row.round_index)}) + # 结算明细行逐行落盘(2026-09-12 用户要求——七段结算完整回放数据面) + _append({"kind": "step", "type": row.type, "source": row.source_class, + "cell": row.origin_cell, "round": int(row.round_index), + "text": String(row.display_name), "value": int(row.value), + "highlight": String(row.get("highlight", ""))}) + + +# ---- 诊断转储(卡死时点击——全量状态 + 环形缓冲)---- + +func dump_diagnosis(context: Dictionary) -> String: + DirAccess.make_dir_recursive_absolute("user://logs") + var stamp: String = Time.get_datetime_string_from_system().replace(":", "").replace("-", "").replace(" ", "_") + var path := "user://logs/diag_%s.json" % stamp + var dump := { + "kind": "diagnosis", "ts": Time.get_datetime_string_from_system(), "seed": _seed, + "session_log": _path, "snap_count": _snap_count, + "recent_actions": _recent_actions.duplicate(), + "recent_steps": _recent_steps.duplicate(), + "core": context, + } + var f := FileAccess.open(path, FileAccess.WRITE) + if f != null: + f.store_string(JSON.stringify(dump, " ")) + f.close() + print("[playtest_logger] 诊断转储已写入: %s(项目日志目录见输出)" % path) + print(" [diag] %s" % JSON.stringify(context)) + return path + + +func _ring(buf: Array, item: Dictionary) -> void: + buf.append(item) + while buf.size() > RING_SIZE: + buf.pop_front() + + +func _append(dict: Dictionary) -> void: + if dict.get("kind", "") == "settlement": + _snap_count += 1 + if _file == null: + return + _file.store_line(JSON.stringify(dict)) + _file.flush() diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd.uid new file mode 100644 index 00000000..4068d49b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd.uid @@ -0,0 +1 @@ +uid://647qs6ujbugf diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/tune_sim_controller.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/tune_sim_controller.gd new file mode 100644 index 00000000..4206109f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/tune_sim_controller.gd @@ -0,0 +1,54 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-12(code-review 修复批——自 main.gd 拆出,规范 1.1/1.2 模拟编排独立成类) +# 调整板批量模拟控制器:逐次驱动 LevelAutoplay(与 headless 扫描同口径)+ 进度/报告 setter 注入面板。 +# 职责边界:零核心写——表基线复位由组合根在移交前完成(_apply_hero_override(0,0)), +# 加成施加 = LevelAutoplay 内部 runner.set_hero_override 单路径;报告文案装配归 HudPresenter。 +# 组合根只转发 simulate_requested 信号。await 依赖面板所在场景树(进度逐帧刷新)。 +extends RefCounted + +const SEED_BASE := 4200 # 模拟种子基(与历史实现同源——同种子可回放) +const LevelAutoplay := preload("res://smoke/level_autoplay.gd") # 与 headless 扫描同口径的自动策略(P3:preload 编译期路径校验) + +var db +var panel # TunePanel setter 面:atk_pct/hp_pct/set_sim_running/set_sim_progress/set_sim_report +var _base_hp := 0 # 报告中的"基础 HP"口径注记值 + + +func _init(db_ref, panel_ref, base_hp: int) -> void: + db = db_ref + panel = panel_ref + _base_hp = base_hp + + +func run(n: int) -> void: + # 共享日志会话(2026-09-12 用户要求:模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL; + # 一文件多局,run 行携带种子切分局次) + var logger = load("res://presentation/playtest_logger.gd").new() + logger.ap_per_round = int(db.action_economy.ap_per_round) + var log_path: String = logger.start_session(SEED_BASE) + print("[tune_sim] 模拟日志: %s" % log_path) + panel.set_sim_running(true) + panel.set_sim_progress("模拟中:0/%d …" % n) + var wins := 0 + var dmg_all: Array = [] + var hp_left_win: Array = [] + var hp_boss: Array = [] + var rounds_all: Array = [] + var death_nodes := {} + for i in range(n): + var st = LevelAutoplay.run_level(db, SEED_BASE + i, 1, panel.atk_pct(), panel.hp_pct(), false, logger) + dmg_all.append(st.hero_dmg) + rounds_all.append(st.rounds) + if st.result == "victory": + wins += 1 + hp_left_win.append(st.hp_left) + hp_boss.append(st.hp_at_boss) + elif st.result == "defeat": + var key := "%d.%s" % [st.defeat_node_index, st.defeat_node_type] + death_nodes[key] = int(death_nodes.get(key, 0)) + 1 + panel.set_sim_progress("模拟中:%d/%d …" % [i + 1, n]) + await panel.get_tree().process_frame + logger.close_session() + panel.set_sim_report(HudPresenter.tune_sim_report(n, panel.atk_pct(), panel.hp_pct(), + wins, dmg_all, hp_left_win, hp_boss, rounds_all, death_nodes, _base_hp)) + panel.set_sim_running(false) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/tune_sim_controller.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/tune_sim_controller.gd.uid new file mode 100644 index 00000000..b4353d8e --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/tune_sim_controller.gd.uid @@ -0,0 +1 @@ +uid://o0cee33g0yml diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd new file mode 100644 index 00000000..f6de17ef --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd @@ -0,0 +1,164 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(五区装配器——组合根的 UI 构建拆分载体)。 +# Date: 2026-09-12(code-review 修复批:main.gd 回涨 761 行触规范 1.1 审视线——构建拆出) +# 高内聚边界(规范 1.1/1.3):视图构建装配器——只做 new / add_child / 布局, +# 零信号连接、零核心写调用;返回句柄供组合根接线(ADR-0001:接线拓扑唯一归组合根)。 +# 静态无状态。z 序契约:调用方必须按 build 返回序 addChild(登记序 = 模态遮挡序——缺陷⑤纪律)。 +extends RefCounted + + +# 战斗屏五区(A 进度条 / B 战斗画面 / C·D·E 三列)。返回句柄字典: +# {strip_view, stage, log_view, info_panel, board_zone, board_view, ap_bar, inventory_panel} +static func build_five_zone(root: Control, db) -> Dictionary: + # set_anchors_and_offsets_preset(非 set_anchors_preset):后者会把控件重置回最小内容尺寸 + # (实测 927×613——五区缩顶、棋盘 45px 格 ×10 行超出 D 区 333px 高被裁,2026-09-13 用户报告回归) + root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + root.add_theme_constant_override("separation", 0) + + # A 区:关卡进度条(全宽 × 28px 顶栏) + var strip_panel := PanelContainer.new() + strip_panel.custom_minimum_size = Vector2(0, 28) + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(30, 25, 20) + sb.set_corner_radius_all(0) + strip_panel.add_theme_stylebox_override("panel", sb) + root.add_child(strip_panel) + var strip_view := LevelStripView.new() + strip_panel.add_child(strip_view) + + # B 区:战斗画面(全宽 × 252px——常驻双舞台,布局决策 D1) + var stage := BattleStageView.new(db) + root.add_child(stage) + + # C/D/E 三列(520px) + var bottom := HBoxContainer.new() + bottom.size_flags_vertical = Control.SIZE_EXPAND_FILL + bottom.add_theme_constant_override("separation", 4) + root.add_child(bottom) + + var info_panel := HeroInfoPanelView.new() + bottom.add_child(info_panel) + + var board_zone := BoardZoneView.new() + bottom.add_child(board_zone) + + var inventory_panel := InventoryPanelView.new() + bottom.add_child(inventory_panel) + + return { + "strip_view": strip_view, + "stage": stage, + "log_view": stage.log_view, + "info_panel": info_panel, + "board_zone": board_zone, + "board_view": board_zone.board_view, + "ap_bar": board_zone.ap_bar, + "inventory_panel": inventory_panel, + } + + +# 模态覆盖层组(z 序 = 登记序:图例 < 明细回看 < 节点 < 终局 < 调整板)。 +# 返回 {legend_overlay, review_overlay, node_overlay, end_overlay, tune_panel};调用方按本序 addChild。 +static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Dictionary: + var legend_overlay := LegendOverlay.new() + host.add_child(legend_overlay) + + var review_overlay := SettlementReviewOverlay.new() + host.add_child(review_overlay) + + var node_overlay := NodeOverlay.new() + host.add_child(node_overlay) + + var end_overlay := BattleEndOverlay.new() + host.add_child(end_overlay) + + var tune_panel := TunePanel.new(db, base_atk, base_hp) + tune_panel.visible = false # 显式初始化——原版依赖"被不透明开始层遮蔽"的树序巧合(缺陷⑤家族);模态锁输入以 visible 为准 + host.add_child(tune_panel) + + return { + "legend_overlay": legend_overlay, + "review_overlay": review_overlay, + "node_overlay": node_overlay, + "end_overlay": end_overlay, + "tune_panel": tune_panel, + } + + +# 开始覆盖层(返回 {overlay, box, seed_edit, lvbtn, tunebtn, dbgbtn}——按钮接线归组合根; +# 单场遭遇用户入口已删(2026-09-13 用户裁决——单场原语仅探针经 debug_session.start_single 使用)) +static func build_start_overlay(host: Control) -> Dictionary: + var overlay := overlay_bg(host, Color8(28, 22, 18)) + var box := overlay_box(overlay) + var title := UiKit.label("Dungeon Sweeper", 34, true) + title.add_theme_color_override("font_color", Color8(240, 226, 200)) + box.add_child(title) + var sub := UiKit.label("战斗屏五区改造批 —— 扫雷的因,逐行兑现的果", 16, false) + sub.add_theme_color_override("font_color", Color8(200, 188, 166)) + box.add_child(sub) + box.add_child(HSeparator.new()) + + var seed_row := HBoxContainer.new() + box.add_child(seed_row) + seed_row.add_child(UiKit.label("随机种子:", 15, false)) + var seed_edit := LineEdit.new() + seed_edit.text = "42" + seed_edit.custom_minimum_size = Vector2(120, 0) + seed_row.add_child(seed_edit) + + var lvbtn := UiKit.button("▶ 第一关 · 苔藓回廊(战×2 → 道具 → 战×2 → 恢复 → Boss)") + box.add_child(lvbtn) + var tunebtn := UiKit.button("测试用 · 角色数值调整板(模拟天赋树属性提升)") + box.add_child(tunebtn) + var dbgbtn := UiKit.button("调试 · 连续关卡两层(库存与 HP 关内延续)") + box.add_child(dbgbtn) + + var help := UiKit.label("玩法:每轮 8 AP——挖开(1AP)/插旗(1AP)/移动(免费)/连锁(免费)。\n安全格=拾取战利品(轮末结算生效)· 雷=敌方获得伏击 · 标中旗=削弱敌人。\n停手:剩余 AP 转存 RAP;弃图刷新:RAP−8 当场换图继续挖(每轮剩 2 次)。\n敌人全灭胜、主角倒下负。悬停可看三态光标与可挖域微光。", 13, false) + help.add_theme_color_override("font_color", Color8(178, 166, 146)) + box.add_child(help) + + return {"overlay": overlay, "box": box, "seed_edit": seed_edit, + "lvbtn": lvbtn, "tunebtn": tunebtn, "dbgbtn": dbgbtn} + + +# 调试两层模式的层间过渡覆盖层(历史入口专用)。返回 {overlay, label, next_btn}——接线归组合根 +static func build_inter_overlay(host: Control) -> Dictionary: + var overlay := overlay_bg(host, Color(0, 0, 0, 0.6)) + var box := overlay_box(overlay) + var label := UiKit.label("", 24, true) + label.add_theme_color_override("font_color", Color8(240, 226, 200)) + box.add_child(label) + var next_btn := UiKit.button("进入下一层") + box.add_child(next_btn) + overlay.visible = false + return {"overlay": overlay, "label": label, "next_btn": next_btn} + + +# Playtest 诊断按钮(调试变体——battle.md §States 允许;卡死转储) +static func build_diag_button(host: Control) -> Button: + var diag_btn := UiKit.button("诊断转储") + diag_btn.set_anchors_preset(Control.PRESET_BOTTOM_LEFT) + diag_btn.offset_left = 6.0 + diag_btn.offset_top = -30.0 + diag_btn.offset_right = 118.0 + diag_btn.offset_bottom = -6.0 + host.add_child(diag_btn) + return diag_btn + + +static func overlay_bg(host: Control, color: Color) -> ColorRect: + var dim := ColorRect.new() + dim.color = color + dim.set_anchors_preset(Control.PRESET_FULL_RECT) + host.add_child(dim) + return dim + + +static func overlay_box(overlay: Control) -> VBoxContainer: + var center := CenterContainer.new() + center.set_anchors_preset(Control.PRESET_FULL_RECT) + overlay.add_child(center) + var box := VBoxContainer.new() + box.add_theme_constant_override("separation", 12) + center.add_child(box) + return box diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd.uid new file mode 100644 index 00000000..2c16168d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd.uid @@ -0,0 +1 @@ +uid://c66hkrbcacd2s diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd new file mode 100644 index 00000000..b01c0e41 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd @@ -0,0 +1,184 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(D 区 AP 条 44px——点阵+数字〔pattern 5 点阵资源显示〕、 +# 停手按钮居左、图例+弃图刷新按钮居右〔雷图右上角〕;弃图刷新确认微交互与常规确认不同位 +# 〔pattern 7 破坏性确认异位:RAP−8 · 旧板旗作废显式代价〕)。 +# Date: 2026-09-12 +# 高内聚边界:纯视图——意图经信号外发(stop_pressed/refresh_confirmed/legend_toggled), +# 状态由组合根经 setter 注入;刷新可用性预判 = RAP≥8 读数(A3 无推送信号——每次调用返回后重查)。 +class_name ApBarView +extends PanelContainer + +signal stop_pressed +signal refresh_confirmed +signal legend_toggled + +const COL_GOLD := Color8(240, 168, 40) +const COL_DOT_OFF := Color8(120, 106, 88) +const COL_INK := Color8(51, 38, 27) + +var _dots: HBoxContainer +var _ap_label: Label +var _btn_stop: Button +var _btn_legend: Button +var _btn_refresh: Button +var _refresh_sub: Label +var _confirm_row: HBoxContainer +var _confirm_armed := false +var _arm_tween: Tween + + +func _init() -> void: + custom_minimum_size = Vector2(0, 44) + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(226, 210, 182) + sb.set_corner_radius_all(0) + sb.set_border_width_all(1) + sb.border_color = Color8(120, 106, 88) + sb.content_margin_left = 8.0 + sb.content_margin_right = 8.0 + sb.content_margin_top = 4.0 + sb.content_margin_bottom = 4.0 + add_theme_stylebox_override("panel", sb) + + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 8) + add_child(row) + + _dots = HBoxContainer.new() + _dots.add_theme_constant_override("separation", 3) + _dots.size_flags_vertical = Control.SIZE_SHRINK_CENTER + row.add_child(_dots) + _ap_label = UiKit.label("AP 0/0", 16, true) + _ap_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER + row.add_child(_ap_label) + + _btn_stop = UiKit.button("停手") + _btn_stop.custom_minimum_size = Vector2(64, 0) + _btn_stop.size_flags_vertical = Control.SIZE_SHRINK_CENTER + _btn_stop.pressed.connect(func(): stop_pressed.emit()) + row.add_child(_btn_stop) + + var spacer := Control.new() + spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.add_child(spacer) + + # 破坏性确认异位(pattern 7):确认 chips 出现在刷新按钮左方——与常规"按钮自身确认"不同位 + _confirm_row = HBoxContainer.new() + _confirm_row.add_theme_constant_override("separation", 4) + _confirm_row.visible = false + row.add_child(_confirm_row) + var confirm_lab := UiKit.label("RAP−8 · 旧板旗作废", 12, true) + confirm_lab.add_theme_color_override("font_color", COL_INK) + _confirm_row.add_child(confirm_lab) + var confirm_btn := UiKit.button("确认弃图") + confirm_btn.pressed.connect(_disarm_and_fire) + _confirm_row.add_child(confirm_btn) + + _btn_legend = UiKit.button("图例") + _btn_legend.custom_minimum_size = Vector2(56, 0) + _btn_legend.size_flags_vertical = Control.SIZE_SHRINK_CENTER + _btn_legend.pressed.connect(func(): legend_toggled.emit()) + row.add_child(_btn_legend) + + var refresh_box := VBoxContainer.new() + refresh_box.add_theme_constant_override("separation", 0) + refresh_box.size_flags_vertical = Control.SIZE_SHRINK_CENTER + row.add_child(refresh_box) + _btn_refresh = UiKit.button("弃图刷新") + _btn_refresh.pressed.connect(_on_refresh_pressed) + refresh_box.add_child(_btn_refresh) + _refresh_sub = UiKit.label("RAP−8 · 剩 0 次", 10, false) + _refresh_sub.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + refresh_box.add_child(_refresh_sub) + + +# ---- setter 面(组合根每次核心调用返回后重查注入——A3 无推送信号) ---- + +func set_ap(ap: int, ap_max: int) -> void: + while _dots.get_child_count() < ap_max: + _dots.add_child(_make_dot()) + while _dots.get_child_count() > ap_max: + var last: Control = _dots.get_child(_dots.get_child_count() - 1) + _dots.remove_child(last) + last.queue_free() + for i in _dots.get_child_count(): + var d: ColorRect = _dots.get_child(i) + d.color = COL_GOLD if i < ap else COL_DOT_OFF + _ap_label.text = "AP %d/%d" % [ap, ap_max] + + +func set_refresh_state(remaining: int, ready: bool) -> void: + _refresh_sub.text = "RAP−8 · 剩 %d 次" % remaining + _btn_refresh.disabled = not ready + if not ready: + _disarm() + + +func set_stop_enabled(on: bool) -> void: + _btn_stop.disabled = not on + + +func set_legend_enabled(on: bool) -> void: + _btn_legend.disabled = not on + + +# 探针/装配复用:刷新按钮可用性(预判 = RAP 足额 + 上限未到) +func is_refresh_ready() -> bool: + return not _btn_refresh.disabled + + +# 探针接缝(ADR-0004"合成输入走真实翻译出口"口径):刷新按钮点击 → 确认 chips 全链路 +func press_refresh_for_probe() -> void: + _on_refresh_pressed() + + +func confirm_visible() -> bool: + return _confirm_row.visible + + +func press_confirm_for_probe() -> void: + if _confirm_armed: + _disarm_and_fire() + + +func refresh_remaining_text() -> String: + return _refresh_sub.text + + +# ---- 确认微交互(pattern 7 异位:常规确认 = 按钮自身;本确认 = 左侧独立 chips) ---- + +func _on_refresh_pressed() -> void: + if _btn_refresh.disabled: + return + _confirm_row.visible = true + _confirm_armed = true + if _arm_tween != null and _arm_tween.is_valid(): + _arm_tween.kill() + _arm_tween = create_tween() + _arm_tween.tween_interval(4.0) + _arm_tween.tween_callback(_disarm) + + +func _disarm_and_fire() -> void: + _disarm() + refresh_confirmed.emit() + + +func _disarm() -> void: + _confirm_armed = false + _confirm_row.visible = false + + +# 资源面拒绝反馈(pattern 1 resource_face 腿):AP 数字脉冲 +func pulse_ap() -> void: + var tw := create_tween() + tw.tween_property(_ap_label, "modulate", Color(1.0, 0.4, 0.3), 0.1) + tw.tween_property(_ap_label, "modulate", Color(1, 1, 1), 0.25) + + +func _make_dot() -> ColorRect: + var d := ColorRect.new() + d.custom_minimum_size = Vector2(10, 10) + d.color = COL_DOT_OFF + d.mouse_filter = Control.MOUSE_FILTER_IGNORE + return d diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd.uid new file mode 100644 index 00000000..d1160fc9 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd.uid @@ -0,0 +1 @@ +uid://c2sw1eujfvh5f diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd new file mode 100644 index 00000000..e68a0bdd --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd @@ -0,0 +1,125 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(胜利/失败终局 overlay——金蜡封占位落于扫雷图区中央 + 判定行 + +# 明细回看入口 + 继续/返回;失败 = 暮蓝 #5A6C8C 氛围(明度不变)+ 旗化光点收回占位; +# pattern 6 模态 overlay 层级纪律——遮挡物隐藏/输入锁定断言归场景探针)。 +# Date: 2026-09-12(扩展自 node_overlay 的战斗终局职责分离) +class_name BattleEndOverlay +extends Control + +signal review_pressed +signal continue_pressed + +const COL_GOLD := Color8(240, 168, 40) +const COL_DUSK := Color8(90, 108, 140) # 暮蓝 #5A6C8C(全作唯一暮蓝档) +const COL_WARM := Color8(240, 226, 200) + +var _dim: ColorRect +var _seal: Panel +var _verdict: Label +var _review_btn: Button +var _btn: Button +var _board_rect := Rect2() + + +func _init() -> void: + _dim = ColorRect.new() + _dim.color = Color(COL_WARM, 0.0) + _dim.set_anchors_preset(Control.PRESET_FULL_RECT) + add_child(_dim) + var center := CenterContainer.new() + center.set_anchors_preset(Control.PRESET_FULL_RECT) + _dim.add_child(center) + var box := VBoxContainer.new() + box.add_theme_constant_override("separation", 12) + center.add_child(box) + _verdict = UiKit.label("", 26, true) + _verdict.add_theme_color_override("font_color", COL_WARM) + _verdict.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + box.add_child(_verdict) + _review_btn = UiKit.button("明细回看") + _review_btn.pressed.connect(func(): review_pressed.emit()) + box.add_child(_review_btn) + _btn = UiKit.button("继续") + _btn.pressed.connect(func(): continue_pressed.emit()) + box.add_child(_btn) + # 金蜡封占位(胜利):圆形色块落于扫雷图区中央——位置由 set_board_rect 注入 + _seal = Panel.new() + _seal.mouse_filter = Control.MOUSE_FILTER_IGNORE + var sb := StyleBoxFlat.new() + sb.bg_color = COL_GOLD + sb.set_corner_radius_all(200) + sb.set_border_width_all(4) + sb.border_color = Color8(176, 122, 20) + _seal.add_theme_stylebox_override("panel", sb) + _seal.size = Vector2(72, 72) + _seal.visible = false + _seal.top_level = true + add_child(_seal) + visible = false + + +# 终局画面数据锚 = B1 判定行(verdict)——判定行文案由组合根装配传入 +func open_victory(verdict_line: String, continue_label: String, board_rect: Rect2) -> void: + _dim.color = Color(COL_WARM, 0.28) + _verdict.text = verdict_line + _verdict.add_theme_color_override("font_color", Color8(60, 44, 20)) + _btn.text = continue_label + _board_rect = board_rect + visible = true + _play_seal_fall() + _play_flag_recall(0) # 胜利不放旗收回 + + +func open_defeat(verdict_line: String, continue_label: String, board_rect: Rect2) -> void: + # 明度不变的暮蓝色相氛围(全屏唯一暮蓝档——§2.5 最静一拍) + _dim.color = Color(COL_DUSK, 0.45) + _verdict.text = verdict_line + _verdict.add_theme_color_override("font_color", COL_WARM) + _btn.text = continue_label + _board_rect = board_rect + visible = true + _seal.visible = false + _play_flag_recall(8) + + +func close() -> void: + visible = false + _seal.visible = false + + +func is_open() -> bool: + return visible + + +func verdict_text() -> String: + return _verdict.text + + +# 金蜡封自上落下弹跳定格于棋盘中央(§2.4 克制不狂欢——占位级) +func _play_seal_fall() -> void: + var target := _board_rect.get_center() - _seal.size * 0.5 + if _board_rect.size == Vector2.ZERO: + target = get_viewport_rect().size * 0.5 - _seal.size * 0.5 + _seal.position = Vector2(target.x, -100.0) + _seal.visible = true + var tw := _seal.create_tween() + tw.tween_property(_seal, "position:y", target.y - 26.0, 0.42).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + tw.tween_property(_seal, "position:y", target.y, 0.22).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT) + + +# 旗化光点收回(失败——插过的旗逐面化光点收回占位,§2.5) +func _play_flag_recall(count: int) -> void: + for i in count: + var dot := ColorRect.new() + dot.color = COL_GOLD + dot.size = Vector2(6, 6) + var base := _board_rect.get_center() if _board_rect.size != Vector2.ZERO else get_viewport_rect().size * 0.5 + dot.position = base + Vector2((float(i % 5) - 2.0) * _board_rect.size.x * 0.14 if _board_rect.size.x > 0 else 0.0, + (float(i % 3) - 1.0) * _board_rect.size.y * 0.18) # 确定性散布(S2 禁全局随机——占位演出同守) + dot.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(dot) + var tw := dot.create_tween() + tw.tween_interval(0.1 * i) + tw.tween_property(dot, "position:y", dot.position.y - 120.0, 0.8) + tw.parallel().tween_property(dot, "modulate:a", 0.0, 0.8) + tw.tween_callback(dot.queue_free) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd.uid new file mode 100644 index 00000000..1059a077 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd.uid @@ -0,0 +1 @@ +uid://boor8bol5td8w diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd new file mode 100644 index 00000000..beb2761e --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd @@ -0,0 +1,215 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(B 区结算明细带改造——挖掘期收起 1 行上轮摘要 / 结算期 84px +# 实时流 / 点击展开回看 overlay〔pattern 6 模态纪律〕;行模板「{行为} {值} → {目标}(来源)」、 +# 聚合计数行、零值行可见、效果行 value≠pool_value 禁相加、未生效四类行 icon/划线形状冗余、 +# highlight 金框〔flag_hit/clear_reward_h2/perfect_broken〕、击杀标记)。 +# Date: 2026-09-12(基础 = 2026-09-11 13 类行协议格式化版) +# 高内聚边界:B1 步骤流 → 玩家文案的全部格式化规则内聚于此;来源文案映射来自 SettlementTable。 +class_name BattleLogView +extends PanelContainer + +signal review_requested + +const BAND_H_DIG := 24 +const BAND_H_SETTLE := 84 +const AGG_TYPES := ["reward_ineffective", "consumed_ineffective", "use_rejected", "overflow"] +const COL_GOLD := "#f0a828" +const COL_INK := "#33261b" + +var _db +var _log: RichTextLabel +var _summary: Label +var _hint: Label +var _mode := "dig" # dig = 收起摘要 / settle = 实时流(模式归组合根驱动) +var _round_rows: Array = [] # 本结算轮行缓冲(上轮摘要构建源) +var _pending_agg := {} # 聚合计数行 pending(type+batch_key → {row, count}) + + +func _init(db_ref) -> void: + _db = db_ref + custom_minimum_size = Vector2(0, BAND_H_DIG) + size_flags_vertical = Control.SIZE_SHRINK_END + var vb := VBoxContainer.new() + vb.add_theme_constant_override("separation", 2) + add_child(vb) + var head := HBoxContainer.new() + vb.add_child(head) + _summary = UiKit.label("", 14, false) + _summary.custom_minimum_size = Vector2(0, BAND_H_DIG - 4) + _summary.clip_text = true + _summary.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + head.add_child(_summary) + _hint = UiKit.label("结算明细 ▸ 点击展开回看", 12, false) + _hint.size_flags_horizontal = Control.SIZE_SHRINK_END + head.add_child(_hint) + _log = RichTextLabel.new() + _log.bbcode_enabled = true + _log.scroll_following = true + _log.custom_minimum_size = Vector2(0, BAND_H_SETTLE - 26) + _log.clip_contents = true + _log.scroll_active = true + vb.add_child(_log) + _log.visible = false + gui_input.connect(_on_gui_input) + + +func set_mode(mode: String) -> void: + if _mode == mode: + return + _mode = mode + var settle := mode == "settle" + _log.visible = settle + _summary.visible = not settle + custom_minimum_size = Vector2(0, BAND_H_SETTLE if settle else BAND_H_DIG) + + +func mode() -> String: + return _mode + + +func clear() -> void: + _log.clear() + _round_rows.clear() + _pending_agg = {} + _summary.text = "" + + +# 每结算轮结束(下一轮开始/终局)——刷新挖掘期"上轮摘要"行 +func finish_settlement_round() -> void: + _flush_pending_agg() + if _round_rows.is_empty(): + return + var parts: Array = [] + for bb in _round_rows: + parts.append(_strip_bb(String(bb))) + var text := "上轮: " + " | ".join(parts) + if text.length() > 190: + text = text.substr(0, 190) + "…" + _summary.text = text + _round_rows.clear() + + +func append_bb(bb: String) -> void: + _flush_pending_agg() + _log.append_text(bb + "\n") + + +func append_row(row: Dictionary) -> void: + if AGG_TYPES.has(String(row.get("type", ""))): + _accumulate_agg(row) + return + _flush_pending_agg() + var bb := _format_row(row) + _round_rows.append(bb) + _log.append_text(bb + "\n") + + +func parsed_text() -> String: + return _log.get_parsed_text() + + +func _on_gui_input(ev: InputEvent) -> void: + if ev is InputEventMouseButton and ev.pressed and ev.button_index == MOUSE_BUTTON_LEFT: + review_requested.emit() + + +# ---- 聚合计数(未生效类行防逐行惩罚轰炸——pattern 3 聚合纪律) ---- + +func _accumulate_agg(row: Dictionary) -> void: + var key := "%s|%s" % [String(row.get("type", "")), String(row.get("batch_key", ""))] + if _pending_agg.has(key): + _pending_agg[key]["count"] = int(_pending_agg[key]["count"]) + 1 + else: + _flush_pending_agg() + _pending_agg[key] = {"row": row, "count": 1} + + +func _flush_pending_agg() -> void: + if _pending_agg.is_empty(): + return + for key in _pending_agg: + var item: Dictionary = _pending_agg[key] + var row: Dictionary = item["row"] + var count: int = item["count"] + var bb := _format_row(row, count) + _round_rows.append(bb) + _log.append_text(bb + "\n") + _pending_agg = {} + + +# ---- 行格式化(模板「{行为} {值} → {目标}(来源)」) ---- + +func _source_label(src_key: String) -> String: + if src_key == "": + return "" + return String(_db.settlement.source_label_map.get(src_key, src_key)) + + +func _gold_frame(text: String) -> String: + # highlight 金框强调(形状+色彩双通道:■ 前缀 + 金底) + return "[bgcolor=%s][color=#3a2a08]■ %s[/color][/bgcolor]" % [COL_GOLD, text] + + +func _format_row(row: Dictionary, agg_count := -1) -> String: + var src := _source_label(String(row.get("source_class", ""))) + var round_no: int = int(row.get("round_index", 0)) + var prefix := " [color=#9a8a72]R%d%s[/color] " % [round_no, ("·" + src) if src != "" else ""] + var hl := String(row.get("highlight", "")) + var v: int = int(row.get("value", 0)) + var name_: String = String(row.get("display_name", "")) + var target := String(row.get("target", "")) + var tail := " → %s" % target if target != "" else "" + var suffix := "(%s)" % src if src != "" else "" + match String(row.get("type", "")): + "effect": + # 效果行 value = 单次贡献值,pool_value = 池合成值——禁相加呈现(B1 呈现细则) + var pool: int = int(row.get("pool_value", 0)) + var col := "#7cc47c" if v > 0 else "#c47c7c" + var body := "%s 单次%+d(池 %+d)%s" % [name_, v, pool, suffix] + return prefix + (_gold_frame(body) if hl != "" else "[color=%s]%s[/color]" % [col, body]) + "damage": + var extra := "" + if row.get("baseline", null) != null: + extra = "(基线 %d + 伏击 %d)" % [int(row.baseline), int(row.attach)] + elif row.get("dmg_a", null) != null: + # 主角攻击三分量拆行(伤害×护甲克制批——P2 归因:这刀里哪段是开格/标雷挣的) + extra = "(基础 %d + 开格 %d + 标雷 %d)" % [int(row.base), int(row.dmg_a), int(row.dmg_b)] + var kill := " ⚔击杀!" if row.get("killed", false) else "" + var body := "%s -%d%s%s%s%s" % [name_, v, extra, tail, kill, suffix] + var body_col := "#e07050" if row.get("killed", false) else ("#9a8a72" if v == 0 else "#e0a050") + return prefix + "[color=%s]%s[/color]" % [body_col, body] + "heal": + return prefix + "[color=#7cc47c]%s +%d%s%s[/color]" % [name_, v, tail, suffix] + "item": + return prefix + "[color=#d0c090]%s(+%d)%s[/color]" % [name_, v, suffix] + "reward_ineffective", "consumed_ineffective", "use_rejected", "overflow": + # 未生效四类行:⚠ icon + 划线形状冗余(非色相独载)+ 聚合计数 + var n := (" ×%d" % agg_count) if agg_count > 1 else "" + return prefix + "[s][color=#b08060]⚠ %s%s[/color][/s]%s" % [name_, n, suffix] + "flag_miss": + var body := "✗ %s%s" % [name_, "(本板完美排雷已破——归因此旗)" if hl == "perfect_broken" else ""] + return prefix + (_gold_frame(body) if hl == "perfect_broken" else "[color=#9a8a72]%s[/color]" % body) + "charge": + return prefix + "[color=#9a8a72]%s%s[/color]" % [name_, suffix] + "decay": + return prefix + "[color=#8a9ab0]状态衰减 ×%d[/color]" % v + "verdict": + if int(row.value) == 1: + return "[color=#80d080][b]◆ %s[/b][/color]" % name_ + return "[color=#e0b860][b]◆ %s[/b][/color]" % name_ + "highlight": + return prefix + _gold_frame("★ %s" % name_) + return prefix + name_ + suffix + + +func _strip_bb(bb: String) -> String: + var out := "" + var in_tag := false + for ch in bb: + if ch == "[": + in_tag = true + elif ch == "]": + in_tag = false + elif not in_tag: + out += ch + return out.replace(" ", " ").strip_edges() diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd.uid new file mode 100644 index 00000000..396fedb2 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd.uid @@ -0,0 +1 @@ +uid://lioeu027qyr0 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_stage_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_stage_view.gd new file mode 100644 index 00000000..3ccf7d22 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_stage_view.gd @@ -0,0 +1,129 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(B 区战斗画面 252px 全宽——英雄卡(左)+ 兑现演出区(中)+ +# 敌人卡 ×1–3(右);顶部中央轮次·阶段指示「第 N 轮 · 挖掘中/结算中/道具窗口」; +# 底部结算明细带(布局决策 D1 常驻双舞台分屏))。 +# Date: 2026-09-12 +# 高内聚边界:容器+组合视图——敌人卡槽位管理、轮次指示、结算行演出分发改由 setter 面承接; +# 结算流水格式化归 BattleLogView,核心状态→文案装配归 hud_presenter.gd。 +class_name BattleStageView +extends PanelContainer + +var hero_card: HeroCardView +var round_label: Label +var stage_center: Label +var enemy_box: HBoxContainer +var log_view: BattleLogView +var _cards: Array = [] + + +func _init(db_ref) -> void: + custom_minimum_size = Vector2(0, 252) + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(216, 200, 172) + sb.set_corner_radius_all(0) + sb.set_border_width_all(1) + sb.border_color = Color8(120, 106, 88) + sb.content_margin_left = 6.0 + sb.content_margin_right = 6.0 + sb.content_margin_top = 4.0 + sb.content_margin_bottom = 4.0 + add_theme_stylebox_override("panel", sb) + var vb := VBoxContainer.new() + vb.add_theme_constant_override("separation", 4) + add_child(vb) + + round_label = UiKit.label("第 — 轮 · —", 15, true) + round_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + vb.add_child(round_label) + + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 10) + row.size_flags_vertical = Control.SIZE_EXPAND_FILL + vb.add_child(row) + + hero_card = HeroCardView.new() + row.add_child(hero_card) + + var center := CenterContainer.new() + center.size_flags_horizontal = Control.SIZE_EXPAND_FILL + var center_col := VBoxContainer.new() + center_col.alignment = BoxContainer.ALIGNMENT_CENTER + center.add_child(center_col) + stage_center = UiKit.label("兑现演出区\n(结算期:光带 → 弹道 → 受击)", 13, false) + stage_center.add_theme_color_override("font_color", Color8(140, 124, 100)) + stage_center.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + center_col.add_child(stage_center) + row.add_child(center) + + enemy_box = HBoxContainer.new() + enemy_box.add_theme_constant_override("separation", 8) + enemy_box.alignment = BoxContainer.ALIGNMENT_END + row.add_child(enemy_box) + + log_view = BattleLogView.new(db_ref) + vb.add_child(log_view) + + +# 敌卡投影更新:数量不变 = 就地更新(死亡灰化/受击闪烁等卡上动画不被逐行刷新重置——禁瞬消纪律); +# 数量变化 = 新战斗域,重建卡槽(轮内不增删,无回流) +func set_enemies(cards_data: Array) -> void: + if _cards.size() != cards_data.size(): + for c in enemy_box.get_children(): + c.queue_free() + _cards.clear() + for d in cards_data: + var card := EnemyCardView.new() + card.set_data(d) + enemy_box.add_child(card) + _cards.append(card) + return + for i in cards_data.size(): + if is_instance_valid(_cards[i]): + _cards[i].set_data(cards_data[i]) + + +func clear_enemies() -> void: + for c in enemy_box.get_children(): + c.queue_free() + _cards.clear() + + +func set_round_text(text: String) -> void: + round_label.text = text + + +# 结算行演出分发(占位级):伤害行 → 目标卡闪白 / 击杀 → 卡体灰化碎裂 +func on_step_row_fx(row: Dictionary) -> void: + var t := String(row.get("type", "")) + if t != "damage": + return + var target := String(row.get("target", "")) + if target == "hero" or target == "": + return + var card: EnemyCardView = _card_by_name(target) + if card == null: + return + card.flash_hit() + if bool(row.get("killed", false)): + card.mark_dead() + + +func _card_by_name(display_name: String) -> EnemyCardView: + for c in _cards: + if not is_instance_valid(c): + continue + if String(c._name_label.text).contains(display_name): + return c + return null + + +# battle_over:全场敌人卡灰化兜底(短路击杀等未走 killed 行的边角) +func gray_all_enemies() -> void: + for c in _cards: + if is_instance_valid(c) and not c.is_dead(): + c.mark_dead() + + +# 结算期 B 区第一注视点提示(演出区文案切换——能量 7/10 清算调性占位) +func set_settle_focus(on: bool) -> void: + stage_center.text = "兑现演出区\n(结算中——逐行兑现)" if on else "兑现演出区\n(结算期:光带 → 弹道 → 受击)" diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_stage_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_stage_view.gd.uid new file mode 100644 index 00000000..b0f2b057 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_stage_view.gd.uid @@ -0,0 +1 @@ +uid://cm0hragbcft1m diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd new file mode 100644 index 00000000..982577b9 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd @@ -0,0 +1,131 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(D 区演出层——清图金粒喷泉+彩纸 3–5s 占位、Boss 紫赤影子爬棋盘 +# 一次性占位、完美排雷金光带+横幅占位、标中旗加重弹道占位;全部程序化色板原语,真实美术归 B5)。 +# Date: 2026-09-12 +# 高内聚边界:纯演出——组合根按核心信号调用 play_*;本类零核心依赖、不自持游戏状态。 +class_name BoardFxView +extends Control + +const COL_GOLD := Color8(240, 168, 40) +const COL_BOSS := Color8(143, 47, 106) # 紫赤 #8F2F6A +const COL_FLAG := Color8(198, 61, 34) # 旗朱 +const CONFETTI_COLS := [Color8(240, 168, 40), Color8(198, 61, 34), Color8(46, 110, 142), Color8(240, 226, 200)] + +var _banner: Label + + +func _init() -> void: + mouse_filter = Control.MOUSE_FILTER_IGNORE + set_anchors_preset(Control.PRESET_FULL_RECT) + _banner = UiKit.label("", 26, true) + _banner.add_theme_color_override("font_color", COL_GOLD) + _banner.set_anchors_preset(Control.PRESET_CENTER_TOP) + _banner.grow_horizontal = Control.GROW_DIRECTION_BOTH + _banner.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + _banner.visible = false + _banner.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(_banner) + + +# 清图瞬间:金粒喷泉 + 彩纸 3–5s 即收(§2.3 占位) +func play_clear_burst() -> void: + var fountain := CPUParticles2D.new() + fountain.position = size * 0.5 + fountain.amount = 90 + fountain.lifetime = 1.6 + fountain.one_shot = true + fountain.explosiveness = 0.2 + fountain.direction = Vector2(0, -1) + fountain.spread = 55.0 + fountain.initial_velocity_min = 320.0 + fountain.initial_velocity_max = 560.0 + fountain.gravity = Vector2(0, 700) + fountain.scale_amount_min = 2.0 + fountain.scale_amount_max = 4.5 + fountain.color = COL_GOLD + fountain.emitting = true + add_child(fountain) + var confetti := CPUParticles2D.new() + confetti.position = Vector2(size.x * 0.5, -8.0) + confetti.amount = 60 + confetti.lifetime = 3.2 + confetti.one_shot = true + confetti.explosiveness = 0.9 + confetti.direction = Vector2(0, 1) + confetti.spread = 75.0 + confetti.initial_velocity_min = 90.0 + confetti.initial_velocity_max = 200.0 + confetti.gravity = Vector2(0, 140) + confetti.scale_amount_min = 3.0 + confetti.scale_amount_max = 5.0 + confetti.color_ramp = _confetti_ramp() + confetti.emitting = true + add_child(confetti) + var tw := create_tween() + tw.tween_interval(3.5) + tw.tween_callback(func(): + for n in [fountain, confetti]: + if is_instance_valid(n): + n.queue_free()) + + +func _confetti_ramp() -> Gradient: + var g := Gradient.new() + g.set_color(0, CONFETTI_COLS[0]) + g.set_color(1, CONFETTI_COLS[1]) + return g + + +# Boss 遭遇:紫赤影子爬过棋盘一次性占位演出(§2.6) +func play_boss_shadow() -> void: + var shadow := ColorRect.new() + shadow.color = Color(COL_BOSS, 0.55) + shadow.size = Vector2(size.x, 56.0) + shadow.position = Vector2(0, -60.0) + shadow.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(shadow) + var tw := shadow.create_tween() + tw.tween_property(shadow, "position:y", size.y, 1.1).set_trans(Tween.TRANS_SINE) + tw.tween_property(shadow, "modulate:a", 0.0, 0.5) + tw.tween_callback(shadow.queue_free) + + +# 完美排雷:金光带加倍扫射 + 横幅占位(§2.2 最高档) +func play_perfect_sweep() -> void: + for i in 2: + var band := ColorRect.new() + band.color = Color(COL_GOLD, 0.35) + band.size = Vector2(size.x, 14.0 + 10.0 * i) + band.position = Vector2(-size.x, size.y * 0.3 + 40.0 * i) + band.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(band) + var tw := band.create_tween() + tw.tween_property(band, "position:x", size.x + 8.0, 0.5 + 0.15 * i) + tw.tween_callback(band.queue_free) + _show_banner("完美排雷 ×2") + + +# 标中旗:旗化暴击弹道加重占位(自棋盘上缘飞向 B 区敌人卡方向——越界即收) +func play_flag_hit_projectile(heavy: bool) -> void: + var shot := ColorRect.new() + var w := 8.0 if heavy else 4.0 + shot.color = COL_FLAG + shot.size = Vector2(w, w * 2.2) + shot.position = Vector2(size.x * 0.5, size.y * 0.7) + shot.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(shot) + var tw := shot.create_tween() + tw.tween_property(shot, "position", Vector2(size.x * 0.75, -30.0), 0.35) + tw.parallel().tween_property(shot, "modulate:a", 0.0, 0.35) + tw.tween_callback(shot.queue_free) + + +func _show_banner(text: String) -> void: + _banner.text = text + _banner.visible = true + _banner.modulate.a = 0.0 + var tw := create_tween() + tw.tween_property(_banner, "modulate:a", 1.0, 0.2) + tw.tween_interval(1.6) + tw.tween_property(_banner, "modulate:a", 0.0, 0.4) + tw.tween_callback(func(): _banner.visible = false) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd.uid new file mode 100644 index 00000000..b23e7cca --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd.uid @@ -0,0 +1 @@ +uid://w2vo4isaagqt diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd new file mode 100644 index 00000000..53f6d0d9 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd @@ -0,0 +1,477 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(战斗屏 D 区 BoardView——四格态/悬停预承诺光标/可挖域呼吸微光/ +# BFS 路径预览/奖励徽章/雾区全开一次性完成信号/结算期去饱和锁输入)。 +# Date: 2026-09-12(battle.md 五区改造批;基础 = 2026-09-11 D1-lite 版) +# 棋盘视图:格子渲染(art-bible 明亮风、数字高对比、雾形状+色相双通道、 +# 绘制优先级 数字>小人>旗>奖励徽章>变异纹带>底色)+ 玩家输入意图翻译 + 悬停预承诺三态。 +# 高内聚边界:只读 A2 查询面(+B4 身份解析回调)做渲染与意图识别,动作经信号外发——不调用核心写接口 +# (settle_batch 归组合根编排),不持久持有战斗态(a2 引用随 refresh 更新,仅作只读查询)。 +# 缺陷①回归约束:格子控件常驻、逐格重绘在 refresh 内完成,无布局回流(格控件/子标记定长,不增删)。 +# ADR-0003 形态约束:GridContainer + 逐格常驻控件(探针耦合 _cells/click_cell——禁盲目迁移 _draw)。 +class_name BoardView +extends Control + +signal action_requested(cell: Vector2i, kind: String) # kind ∈ {dig, move, flag_place, flag_pull} +signal hover_forbidden(cell: Vector2i) # 非相邻未开格悬停(首遇 toast 归组合根去重) +signal chord_attempted(cell: Vector2i) # 双击快开尝试(chord 肌肉记忆提示锚) +signal fog_region_completed(region_size: int) # 雾区全开一次性信号(视觉合并域=连通分量,EC11) + +const COL_BRICK := Color8(201, 175, 131) # 未开——掩尘完整地砖(色板原语 #C9AF83) +const COL_BRICK_FOG := Color8(150, 136, 196) # 未开雾格——色相 + "雾"字双通道 +const COL_FLOOR := Color8(179, 154, 120) # 已开——裸土 #B39A78(碎裂裸露地面) +const COL_FLOOR_FOG := Color8(216, 208, 234) # 已开雾格——底色层淡雾 tint 残留(OQ8 裁决;无字) +const COL_DIGIT := Color8(51, 38, 27) # 数字——墨族 #33261B(对格底 ≥4.5:1 硬指标) +const COL_HERO := Color8(46, 110, 142) # 小人边框——小人蓝 #2E6E8E +const COL_FLAG := Color8(198, 61, 34) # 旗朱 #C63D22 +const COL_GOLD := Color8(240, 168, 40) # 奖励金 #F0A828 +const COL_PATH := Color8(46, 110, 142) # 路径预览点迹——小人蓝细线 +const COL_DIG_EDGE := Color8(120, 190, 120) # 可挖域呼吸微光基色(绿相,明度贴格底) +const COL_HL_TARGET := Color8(240, 168, 40) # 悬停目标格边框(金) +const REWARD_BADGE := { # 已开格奖励身份 icon(tier 档位另经字号/形状区分) + "instant_aoe": "爆", "instant_heal": "急", "damage_item": "刃", "heal_pack": "医", "buff": "纹", +} +const CURSOR_DIG := Input.CURSOR_CROSS # 挖——镐形自绘纹理载体 +const CURSOR_FLAG := Input.CURSOR_POINTING_HAND # 旗——旗形自绘纹理载体 +const CURSOR_MOVE_SEM := Input.CURSOR_ARROW # 移动——箭头(命名避开 Control.CURSOR_MOVE 原生成员遮蔽) +const CHORD_WINDOW_MS := 350 + +var _grid: GridContainer +var _cells := {} # Vector2i → Button +var _cell_sb := {} # Vector2i → StyleBoxFlat(呼吸微光就地改边框色) +var _marker := {} # Vector2i → {"dot": ColorRect, "badge": Label, "hl": Panel} +var _board: BoardData = null +var _a2 = null # A2 只读查询引用(refresh 更新;null = 无战斗) +var _identity_cb := Callable() # B4 身份解析回调(组合根注入只读面)——Vector2i → Dictionary/null +var _diggable := {} # 可挖域(8 向相邻未开格)——呼吸微光作用集 +var _breath_t := 0.0 +var _dig_glow := false # 挖掘期才允许唯一在动元素(art-bible §2.1) +var _hover := Vector2i(-9, -9) +var _last_click := {"cell": Vector2i(-9, -9), "ms": -100000} +var _fog_done := {} # 本板已完成雾区连通分量(键 = 分量最小格) +var _fog_parts: Array = [] # 本板雾区连通分量(rebuild 期计算——合并呈现域) +var _r0 := {} # 出生展开区 R₀(B4 mapping 资格域 = S∖R₀——徽章查询跳过,防 E19 诊断噪音) +var _desat_mat: ShaderMaterial +var _desat_tween: Tween +var _cell := 46.0 # 当前格边长(set_cell_area 按容器尺寸重算——battle.md 格尺寸公式) + + +func _init() -> void: + size_flags_horizontal = Control.SIZE_EXPAND_FILL + size_flags_vertical = Control.SIZE_EXPAND_FILL + var wrap := CenterContainer.new() + wrap.set_anchors_preset(Control.PRESET_FULL_RECT) + wrap.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(wrap) + _grid = GridContainer.new() + _grid.add_theme_constant_override("h_separation", 2) + _grid.add_theme_constant_override("v_separation", 2) + wrap.add_child(_grid) + _desat_mat = ShaderMaterial.new() + _desat_mat.shader = load("res://presentation/views/desaturation.gdshader") + _desat_mat.set_shader_parameter("strength", 0.0) + + +func board() -> BoardData: + return _board + + +func _ready() -> void: + _apply_custom_cursors() + + +# 结算期/道具窗口态:D 区去饱和 + 金橙偏移锁输入(明度不变——art-bible §2;占位级逐格材质) +func set_desaturated(on: bool) -> void: + if _desat_tween != null and _desat_tween.is_valid(): + _desat_tween.kill() + _desat_tween = create_tween() + var target := 1.0 if on else 0.0 + _desat_tween.tween_method(func(v: float): _desat_mat.set_shader_parameter("strength", v), + float(_desat_mat.get_shader_parameter("strength")), target, 0.15) + for c in _cells: + (_cells[c] as Button).material = _desat_mat if on else null + + +# 组合根注入 B4 身份解析只读回调(query_identity)——奖励徽章数据源 +func set_identity_resolver(cb: Callable) -> void: + _identity_cb = cb + + +# D 区可用面积(AP 条之外)→ 格边长 = floor(min(宽, 高)/N)(battle.md 格尺寸公式,36–45px 档) +func set_cell_area(avail: Vector2) -> void: + if _board == null or _board.width == 0: + return + var n: int = maxi(_board.width, _board.height) + var cell := int(floor(minf(avail.x - 12.0, avail.y - 12.0) / float(n))) + _cell = float(clampi(cell, 26, 46)) + for c in _cells: + (_cells[c] as Button).custom_minimum_size = Vector2(_cell, _cell) + + +# 战斗启动/换板:按板尺寸重建格子控件(a2 引用随之更新) +func rebuild(board: BoardData, a2) -> void: + _board = board + _a2 = a2 + for child in _grid.get_children(): + child.queue_free() + _cells.clear() + _cell_sb.clear() + _marker.clear() + _fog_done.clear() + _fog_parts.clear() + _r0.clear() + _hover = Vector2i(-9, -9) + for c in _board.compute_r0(): + _r0[c] = true + if _board == null: + return + _compute_fog_parts() + _grid.columns = _board.width + for y in _board.height: + for x in _board.width: + var c := Vector2i(x, y) + var btn := Button.new() + btn.custom_minimum_size = Vector2(_cell, _cell) + btn.focus_mode = Control.FOCUS_NONE + btn.gui_input.connect(func(ev: InputEvent): _on_cell_input(c, ev)) + btn.mouse_entered.connect(func(): _on_cell_hover(c)) + btn.mouse_exited.connect(func(): _on_cell_unhover(c)) + _grid.add_child(btn) + _cells[c] = btn + _marker[c] = _build_markers(btn) + set_cell_area(size) + refresh() + + +# 逐格重绘(挖掘/结算后调用) +func refresh() -> void: + if _board == null or _a2 == null: + return + var hero: Vector2i = _a2.hero_pos() + _diggable.clear() + for n in _board.neighbors8(hero): + if not _a2.is_revealed(n): + _diggable[n] = true + for y in _board.height: + for x in _board.width: + _paint_cell(Vector2i(x, y), hero) + _check_fog_completion() + _refresh_hover_after_paint() + + +# 真实输入路径(行为探针复用):合成点击 → 与真实 gui_input 同一翻译出口 +func click_cell(cell: Vector2i, button: int) -> void: + var ev := InputEventMouseButton.new() + ev.button_index = button + ev.pressed = true + _on_cell_input(cell, ev) + + +func _on_cell_input(c: Vector2i, ev: InputEvent) -> void: + if _a2 == null or not (ev is InputEventMouseButton and ev.pressed): + return + if ev.button_index == MOUSE_BUTTON_LEFT: + if _a2.is_revealed(c): + action_requested.emit(c, "move") + else: + action_requested.emit(c, "dig") + _detect_chord(c) + elif ev.button_index == MOUSE_BUTTON_RIGHT: + if _a2.has_flag(c): + action_requested.emit(c, "flag_pull") + else: + action_requested.emit(c, "flag_place") + + +# chord 肌肉记忆首遇锚:同格双左击快开尝试(350ms 窗)——每 run 一次提示由组合根去重 +func _detect_chord(c: Vector2i) -> void: + var now := Time.get_ticks_msec() + if _last_click.cell == c and now - int(_last_click.ms) <= CHORD_WINDOW_MS: + chord_attempted.emit(c) + _last_click.ms = -100000 + return + _last_click = {"cell": c, "ms": now} + + +# ---- 悬停预承诺(pattern 4):三语义光标 + 目标格边框 + BFS 路径预览 ---- + +func _on_cell_hover(c: Vector2i) -> void: + _hover = c + _apply_hover_visuals(c) + + +func _on_cell_unhover(c: Vector2i) -> void: + if _hover == c: + _hover = Vector2i(-9, -9) + _clear_hover_visuals(c) + + +func _refresh_hover_after_paint() -> void: + # 状态随 refresh 变化后光标/边框语义需重算(悬停格四格态可能已变) + if _hover.x >= 0 and _cells.has(_hover): + _apply_hover_visuals(_hover) + + +func _apply_hover_visuals(c: Vector2i) -> void: + if _a2 == null or not _cells.has(c): + return + _clear_hover_visuals(c) + var revealed: bool = _a2.is_revealed(c) + var flag: bool = _a2.has_flag(c) + var adjacent: bool = _diggable.has(c) + var hl: Panel = _marker[c]["hl"] + if revealed: + hl.visible = true + _set_hl_color(hl, COL_HL_TARGET) # 移动目标格边框(金) + _set_cursor_shape(c, CURSOR_MOVE_SEM) + _show_path_preview(c) + elif flag: + hl.visible = true + _set_hl_color(hl, COL_FLAG) # 拔旗目标(旗朱) + _set_cursor_shape(c, CURSOR_FLAG) + elif adjacent: + hl.visible = true + _set_hl_color(hl, COL_HL_TARGET) # 挖掘目标格边框 + _set_cursor_shape(c, CURSOR_DIG) + else: + hl.visible = true + _set_hl_color(hl, Color8(120, 60, 50)) # 不可及域灰禁框(禁止光标+首遇 toast 双通道) + _set_cursor_shape(c, Input.CURSOR_FORBIDDEN) + hover_forbidden.emit(c) + + +func _clear_hover_visuals(c: Vector2i) -> void: + if not _marker.has(c): + return + _marker[c]["hl"].visible = false + _hide_path_preview() + + +func _set_cursor_shape(c: Vector2i, shape: int) -> void: + if _cells.has(c): + (_cells[c] as Button).mouse_default_cursor_shape = shape + + +func _show_path_preview(c: Vector2i) -> void: + # D3 合法性指示:悬停已开格 → BFS 路径蓝细线点迹(compute_move_path 只读面) + if _a2 == null: + return + var path: Array = _a2.compute_move_path(c) + for pc in path: + if _marker.has(pc): + _marker[pc]["dot"].visible = true + + +func _hide_path_preview() -> void: + for c in _marker: + _marker[c]["dot"].visible = false + + +# ---- 奖励徽章(已开格身份 icon——旗之下、纹带之上、不进数字区) ---- + +func _update_badge(c: Vector2i, btn: Button) -> void: + var badge: Label = _marker[c]["badge"] + badge.visible = false + if _a2 == null or not _a2.is_revealed(c) or not _identity_cb.is_valid(): + return + if _r0.has(c): + return # R₀ 初始展开格无奖励身份(B4 资格域 = S∖R₀) + var rec: Dictionary = _a2.reveal_record(c) # E2 收口:已开记录经查询面(禁直读 A2 状态字典) + if rec.get("kind", "") != "safe": + return + var ident = _identity_cb.call(c) + if not (ident is Dictionary): + return + badge.text = String(REWARD_BADGE.get(String(ident.get("category", "")), "◇")) + badge.add_theme_font_size_override("font_size", maxi(10, int(_cell * 0.28))) + badge.visible = true + + +# ---- 四格态渲染(灰度截图可辨:完整覆尘/碎裂裸土/雾纹带/已开雾 tint) ---- + +func _paint_cell(c: Vector2i, hero: Vector2i) -> void: + var btn: Button = _cells.get(c) + if btn == null: + return + var revealed: bool = _a2.is_revealed(c) + var fog: bool = _board.variant_ids(c).size() > 0 + var flag: bool = _a2.has_flag(c) + var is_hero: bool = c == hero + var text := "" + var bg: Color + var digit_col := COL_DIGIT + if revealed: + var rec: Dictionary = _a2.reveal_record(c) # E2 收口:已开记录经查询面 + if rec.get("kind", "") == "mine": + bg = COL_FLOOR + text = "雷" + digit_col = Color8(150, 44, 36) + elif fog: + bg = COL_FLOOR_FOG # 已开雾格 = 淡雾 tint 残留 + 无字(OQ8 裁决——与已开 0 格可判定区分) + else: + bg = COL_FLOOR + var d: int = rec.get("digit", 0) + if d > 0: + text = str(d) + else: + if flag: + bg = COL_BRICK + text = "旗" + digit_col = COL_FLAG + elif fog: + bg = COL_BRICK_FOG + text = "雾" + digit_col = Color8(238, 232, 250) + else: + bg = COL_BRICK + var sb := StyleBoxFlat.new() + sb.bg_color = bg + sb.set_corner_radius_all(3) + sb.set_border_width_all(1) + sb.border_color = bg.darkened(0.15) + if is_hero: + sb.set_border_width_all(3) + sb.border_color = COL_HERO + if text == "": + text = "人" + digit_col = COL_HERO + _cell_sb[c] = sb + btn.add_theme_stylebox_override("normal", sb) + btn.add_theme_stylebox_override("hover", sb) + btn.add_theme_stylebox_override("pressed", sb) + btn.text = text + btn.add_theme_color_override("font_color", digit_col) + btn.add_theme_font_size_override("font_size", maxi(14, int(_cell * 0.5))) # 数字 ≥ 格高 50% + _update_badge(c, btn) + # 悬停光标三态(静态语义按四格态预置——hover 期再精调) + if is_hero: + _set_cursor_shape(c, Input.CURSOR_ARROW) + elif revealed: + _set_cursor_shape(c, CURSOR_MOVE_SEM) + elif flag: + _set_cursor_shape(c, CURSOR_FLAG) + elif _diggable.has(c): + _set_cursor_shape(c, CURSOR_DIG) + else: + _set_cursor_shape(c, Input.CURSOR_FORBIDDEN) + + +# ---- 可挖域 8 向常驻呼吸微光(挖掘期唯一在动元素) ---- + +func set_dig_glow_enabled(on: bool) -> void: + _dig_glow = on + if not on: + _reset_breath() + + +func _process(delta: float) -> void: + if not _dig_glow or _diggable.is_empty(): + return + _breath_t += delta + var k := 0.5 + 0.5 * sin(_breath_t * 3.0) # 3 rad/s≈0.48Hz——无 >3 次/秒频闪(AC 基线) + var col := COL_DIG_EDGE.lerp(Color8(46, 80, 60), 0.55 - 0.35 * k) + for c in _diggable: + if not _cell_sb.has(c): + continue + var sb: StyleBoxFlat = _cell_sb[c] + sb.set_border_width_all(2) + sb.border_color = col + + +func _reset_breath() -> void: + for c in _diggable: + if _cell_sb.has(c): + var sb: StyleBoxFlat = _cell_sb[c] + sb.set_border_width_all(1) + sb.border_color = sb.bg_color.darkened(0.15) + + +# ---- 雾区全开一次性完成信号(视觉合并域 = 连通分量;实例独立、呈现合并——EC11) ---- + +func _compute_fog_parts() -> void: + var seen := {} + for y in _board.height: + for x in _board.width: + var c := Vector2i(x, y) + if seen.has(c) or _board.variant_ids(c).is_empty(): + continue + var comp: Array = [] + var queue: Array = [c] + seen[c] = true + while not queue.is_empty(): + var cur: Vector2i = queue.pop_front() + comp.append(cur) + for n in _board.neighbors8(cur): + if not seen.has(n) and not _board.variant_ids(n).is_empty(): + seen[n] = true + queue.append(n) + comp.sort_custom(func(a, b): return a.y * 1000 + a.x < b.y * 1000 + b.x) + _fog_parts.append(comp) + + +func _check_fog_completion() -> void: + for comp in _fog_parts: + var key: Vector2i = comp[0] + if _fog_done.has(key): + continue + var all_open := true + for c in comp: + if not _a2.is_revealed(c): + all_open = false + break + if all_open: + _fog_done[key] = true + fog_region_completed.emit(comp.size()) + + +# ---- 子标记层(不参与命中测试) ---- + +func _build_markers(btn: Button) -> Dictionary: + var dot := ColorRect.new() + dot.color = COL_PATH + dot.custom_minimum_size = Vector2(6, 6) + dot.set_anchors_preset(Control.PRESET_CENTER) + dot.mouse_filter = Control.MOUSE_FILTER_IGNORE + dot.visible = false + btn.add_child(dot) + var badge := Label.new() + badge.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT) + badge.grow_horizontal = Control.GROW_DIRECTION_BEGIN + badge.grow_vertical = Control.GROW_DIRECTION_BEGIN + badge.add_theme_color_override("font_color", COL_GOLD) + badge.mouse_filter = Control.MOUSE_FILTER_IGNORE + badge.visible = false + btn.add_child(badge) + var hl := Panel.new() + hl.set_anchors_preset(Control.PRESET_FULL_RECT) + hl.mouse_filter = Control.MOUSE_FILTER_IGNORE + var hl_sb := StyleBoxFlat.new() + hl_sb.draw_center = false + hl_sb.set_border_width_all(2) + hl_sb.border_color = COL_HL_TARGET + hl.add_theme_stylebox_override("panel", hl_sb) + hl.visible = false + btn.add_child(hl) + return {"dot": dot, "badge": badge, "hl": hl, "hl_sb": hl_sb} + + +func _set_hl_color(hl: Panel, col: Color) -> void: + var sb: StyleBoxFlat = hl.get_theme_stylebox("panel") + sb.border_color = col + + +# ---- 自绘光标纹理(镐/旗——引擎内置光标无此语义;程序化占位,headless 跳过) ---- + +func _apply_custom_cursors() -> void: + if DisplayServer.get_name() == "headless": + return + Input.set_custom_mouse_cursor(_make_cursor_texture("⛏"), Input.CURSOR_CROSS, Vector2(4, 4)) + Input.set_custom_mouse_cursor(_make_cursor_texture("⚑"), Input.CURSOR_POINTING_HAND, Vector2(2, 2)) + + +func _make_cursor_texture(glyph: String) -> ImageTexture: + var img := Image.create(24, 24, false, Image.FORMAT_RGBA8) + img.fill(Color(0, 0, 0, 0)) + var f := ThemeDB.fallback_font + if f != null: + f.draw_string(img, Vector2(2, 18), glyph, HORIZONTAL_ALIGNMENT_LEFT, 20, 18, COL_DIGIT) + return ImageTexture.create_from_image(img) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd.uid new file mode 100644 index 00000000..d2a27625 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd.uid @@ -0,0 +1 @@ +uid://47edvur3ala1 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_zone_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_zone_view.gd new file mode 100644 index 00000000..d1135cfd --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_zone_view.gd @@ -0,0 +1,49 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(D 区扫雷图 ~760×520 容器——顶部 AP 条 44px〔左:点阵+停手; +# 右:图例+弃图刷新〕、棋盘主体居中、toast 槽上缘内侧、演出层)。 +# Date: 2026-09-12 +# 高内聚边界:容器组合 + 信号转发——动作意图全部信号外发到组合根,零核心写调用。 +class_name BoardZoneView +extends PanelContainer + +var ap_bar: ApBarView +var board_view: BoardView +var toast: ToastStack +var fx: BoardFxView + +signal action_requested(cell: Vector2i, kind: String) +signal stop_pressed +signal refresh_confirmed +signal legend_toggled +signal hover_forbidden(cell: Vector2i) +signal chord_attempted(cell: Vector2i) +signal fog_region_completed(region_size: int) + + +func _init() -> void: + size_flags_horizontal = Control.SIZE_EXPAND_FILL + var vb := VBoxContainer.new() + vb.add_theme_constant_override("separation", 4) + add_child(vb) + ap_bar = ApBarView.new() + vb.add_child(ap_bar) + var board_area := Control.new() + board_area.size_flags_vertical = Control.SIZE_EXPAND_FILL + board_area.clip_contents = true + vb.add_child(board_area) + board_area.resized.connect(func(): board_view.set_cell_area(board_area.size)) + board_view = BoardView.new() + board_view.set_anchors_preset(Control.PRESET_FULL_RECT) + board_area.add_child(board_view) + fx = BoardFxView.new() + board_area.add_child(fx) + toast = ToastStack.new() + board_area.add_child(toast) + # 信号转发(组合根只接线一处) + board_view.action_requested.connect(func(c: Vector2i, kind: String): action_requested.emit(c, kind)) + board_view.hover_forbidden.connect(func(c: Vector2i): hover_forbidden.emit(c)) + board_view.chord_attempted.connect(func(c: Vector2i): chord_attempted.emit(c)) + board_view.fog_region_completed.connect(func(n: int): fog_region_completed.emit(n)) + ap_bar.stop_pressed.connect(func(): stop_pressed.emit()) + ap_bar.refresh_confirmed.connect(func(): refresh_confirmed.emit()) + ap_bar.legend_toggled.connect(func(): legend_toggled.emit()) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_zone_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_zone_view.gd.uid new file mode 100644 index 00000000..4096bdf0 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_zone_view.gd.uid @@ -0,0 +1 @@ +uid://cnevw2ftulpc3 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/desaturation.gdshader b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/desaturation.gdshader new file mode 100644 index 00000000..ff5fda46 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/desaturation.gdshader @@ -0,0 +1,19 @@ +// VERTICAL SLICE - NOT FOR PRODUCTION +// 实现 GDD:design/ux/battle.md(§States 结算阶段/道具窗口态——D 区去饱和 + 金橙色相偏移锁输入, +// 明度不变:状态区分只用饱和/色相——art-bible §2 合规化 2026-09-12)。 +// 逐格 canvas_item 材质(gl_compatibility 兼容;CanvasGroup 回背缓冲方案为 B5 复议项)。 +shader_type canvas_item; + +uniform float strength : hint_range(0.0, 1.0) = 0.0; + +void fragment() { + vec4 col = COLOR; + float lum = dot(col.rgb, vec3(0.299, 0.587, 0.114)); + // 金橙目标色相(#F0A828 族),重标定到原色明度——明度恒定 + vec3 gold = vec3(0.941, 0.659, 0.157); + float gold_lum = dot(gold, vec3(0.299, 0.587, 0.114)); + vec3 shifted = gold * (lum / max(gold_lum, 0.001)); + vec3 desat = vec3(lum); + vec3 mixed = mix(col.rgb, mix(desat, shifted, 0.65), strength); + COLOR = vec4(mixed, col.a); +} diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/desaturation.gdshader.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/desaturation.gdshader.uid new file mode 100644 index 00000000..014b89be --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/desaturation.gdshader.uid @@ -0,0 +1 @@ +uid://bqotqrcgexg6m diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd new file mode 100644 index 00000000..37f354f9 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd @@ -0,0 +1,170 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(B 区敌人卡 ×1–3——敌像占位、血条、顺位徽标、意图徽标〔常驻: +# 挖掘期全程可见,映射 = enemy_id 恒定、环点 = 拍数直觉编码,B2 数据源 = query_beat 当前拍〕、 +# 状态槽〔敌方 side 池口径:伏击层数全体共享单值各卡同值——B1 流水经 B3 投影〕)。 +# Date: 2026-09-12 +# 敌人死亡呈现(B2 资产下限同等 blocking):血条归零 → 卡体灰化 + 碎裂退场,禁瞬消; +# 卡体保留灰化残位——定长卡槽,死亡动画不回流布局。 +# 意图线索映射目录(B2×4① 成文占位——资产定稿归 B5,本处按 enemy_id 程序化占位)。 +class_name EnemyCardView +extends PanelContainer + +const COL_INK := Color8(51, 38, 27) +const COL_BEAT := {"attack": Color8(198, 61, 34), "charge": Color8(90, 108, 140), "heavy": Color8(143, 47, 106)} +const BEAT_NAMES := {"attack": "攻击", "charge": "蓄力", "heavy": "重击"} +# 护甲类型呈现(伤害×护甲克制批——可读性前提:玩家须能看出该用开格还是标雷打它) +const ARMOR_NAMES := {"none": "无甲", "a": "护甲A", "b": "护甲B"} + +var _portrait: Label +var _name_label: Label +var _hp_bar: ProgressBar +var _intent_badge: Label +var _ring: Label +var _status_label: Label +var _armor := "none" # 护甲类型快照(set_data 写入、set_intent 渲染——环点行常驻标签) +var _dead := false + + +func _init() -> void: + custom_minimum_size = Vector2(190, 176) + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(226, 212, 190) + sb.set_corner_radius_all(4) + sb.set_border_width_all(1) + sb.border_color = Color8(150, 60, 48) + sb.content_margin_left = 8.0 + sb.content_margin_right = 8.0 + sb.content_margin_top = 6.0 + sb.content_margin_bottom = 6.0 + add_theme_stylebox_override("panel", sb) + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 8) + add_child(row) + _portrait = UiKit.label("敌", 34, true) + _portrait.add_theme_color_override("font_color", Color8(150, 60, 48)) + _portrait.custom_minimum_size = Vector2(48, 0) + _portrait.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + _portrait.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + row.add_child(_portrait) + var col := VBoxContainer.new() + col.size_flags_horizontal = Control.SIZE_EXPAND_FILL + col.add_theme_constant_override("separation", 2) + row.add_child(col) + _name_label = UiKit.label("敌人", 13, true) + col.add_child(_name_label) + _hp_bar = ProgressBar.new() + _hp_bar.custom_minimum_size = Vector2(0, 10) + _hp_bar.show_percentage = false + col.add_child(_hp_bar) + _intent_badge = UiKit.label("意图 —", 13, true) + _intent_badge.add_theme_color_override("font_color", COL_INK) + col.add_child(_intent_badge) + _ring = UiKit.label("", 12, false) + col.add_child(_ring) + _status_label = _fixed_label(30) + col.add_child(_status_label) + + +# data = B3/B2 投影:{order, display_name, hp, max_hp, enemy_id, beat, ring_size, ring_index, ambush_layers} +func set_data(d: Dictionary) -> void: + _name_label.text = "%s" % String(d.get("display_name", "敌人")) + _hp_bar.max_value = maxi(1, int(d.get("max_hp", 1))) + _hp_bar.value = int(d.get("hp", 0)) + _set_order(int(d.get("order", 1))) + _armor = String(d.get("armor_type", "none")) + set_intent(String(d.get("beat", "")), int(d.get("ring_size", 0)), int(d.get("ring_index", 0))) + var ambush: int = int(d.get("ambush_layers", 0)) + _status_label.text = "伏击池 ×%d(全体共享)" % ambush if ambush > 0 else "" + _portrait.text = _portrait_glyph(String(d.get("enemy_id", ""))) + if _dead and int(d.get("hp", 0)) > 0: + _revive() + + +func _set_order(i: int) -> void: + # 顺位徽标 = 行主序生成序(固定顺位——B2 顺位标记义务) + var base: String = _name_label.text.trim_prefix("① ").trim_prefix("② ").trim_prefix("③ ") + _name_label.text = "%s %s" % [String("①②③"[clampi(i - 1, 0, 2)]), base] + + +# beat = B2 行为环词表原键(attack/charge/heavy,B1 query_beat 直出——presenter 不翻译); +# 名(BEAT_NAMES)/色(COL_BEAT)/重击标记(▼)渲染映射唯一定义处 = 本类(code-review 2026-09-12: +# 双翻译曾使色相与重击通道永久失效)。信号/入参契约:set_data 每结算行刷新、set_intent 每拍刷新。 +func set_intent(beat: String, ring_size: int, ring_index: int) -> void: + if _dead: + return + var label := String(BEAT_NAMES.get(beat, beat)) + # 强度档位编码:重击 = 垂直延伸体量(占位:▼)+ 色相双通道(色弱冗余 = 文字常驻) + var beat_col: Color = COL_BEAT.get(beat, Color8(90, 78, 66)) + _intent_badge.add_theme_color_override("font_color", beat_col) + if label == "": + _intent_badge.text = "意图:—" + else: + _intent_badge.text = "意图:" + label + ("▼" if beat == "heavy" else "") + # 环点 = 拍数直觉编码(● 已到拍 / ○ 未到)+ 护甲类型标签(克制对策的可读前提——恒常驻非悬停) + if ring_size > 0: + var dots := "" + for i in ring_size: + dots += "●" if i == ring_index else "○" + _ring.text = "%s · 环 %s(%d 拍)" % [_armor_tag(_armor), dots, ring_size] + else: + _ring.text = _armor_tag(_armor) + + +func _armor_tag(armor: String) -> String: + return String(ARMOR_NAMES.get(armor, "无甲")) + + +# 死亡呈现:卡体灰化 + 碎裂退场(禁瞬消)——灰化残位 + 裂纹字符 + 击破标记 +func mark_dead() -> void: + if _dead: + return + _dead = true + modulate = Color(0.62, 0.62, 0.62, 0.9) + var tw := create_tween() + tw.tween_property(self, "rotation_degrees", 2.5, 0.12) + tw.tween_property(self, "rotation_degrees", -1.5, 0.1) + tw.tween_property(self, "rotation_degrees", 0.0, 0.08) + _portrait.text = "✕" + _intent_badge.text = "—— 击破 ——" + _intent_badge.add_theme_color_override("font_color", Color8(120, 110, 98)) + _ring.text = "" + _status_label.text = "" + _hp_bar.value = 0 + + +func is_dead() -> bool: + return _dead + + +# 受击闪白(结算演出占位——§2.2 受击反馈) +func flash_hit() -> void: + var tw := create_tween() + tw.tween_property(self, "modulate", Color(1.6, 1.4, 1.2), 0.08) + tw.tween_property(self, "modulate", Color(1, 1, 1) if not _dead else Color(0.62, 0.62, 0.62, 0.9), 0.2) + + +func _revive() -> void: + _dead = false + modulate = Color(1, 1, 1) + rotation_degrees = 0.0 + _portrait.text = "敌" + + +func _portrait_glyph(enemy_id: String) -> String: + # enemy_id 恒定映射(B2 Rule 8③)——程序化占位字形:小体量墩形/垂直延伸/最大体量 + match enemy_id: + "goblin": + return "▂" + "bugbear": + return "▆" + "ogre_chief": + return "█" + return "敌" + + +func _fixed_label(height: float) -> Label: + var l := UiKit.label("", 11, false) + l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + l.custom_minimum_size = Vector2(0, height) + l.clip_contents = true + return l diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd.uid new file mode 100644 index 00000000..f8522432 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd.uid @@ -0,0 +1 @@ +uid://cmu3givbs3323 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd new file mode 100644 index 00000000..aec802e5 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd @@ -0,0 +1,81 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(B 区英雄卡——小人像占位/HP 条/攻击值含修正显示/状态效果槽引用)。 +# Date: 2026-09-12 +# 高内聚边界:纯视图 setter 面——状态装配(含修正文案)归 hud_presenter.gd;定长卡体无布局回流。 +class_name HeroCardView +extends PanelContainer + +const COL_INK := Color8(51, 38, 27) +const COL_HERO := Color8(46, 110, 142) + +var _portrait: Label +var _hp_bar: ProgressBar +var _hp_label: Label +var _atk_label: Label +var _status_label: Label + + +func _init() -> void: + custom_minimum_size = Vector2(216, 176) + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(232, 220, 196) + sb.set_corner_radius_all(4) + sb.set_border_width_all(1) + sb.border_color = Color8(120, 106, 88) + sb.content_margin_left = 8.0 + sb.content_margin_right = 8.0 + sb.content_margin_top = 6.0 + sb.content_margin_bottom = 6.0 + add_theme_stylebox_override("panel", sb) + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 8) + add_child(row) + _portrait = UiKit.label("人", 40, true) + _portrait.add_theme_color_override("font_color", COL_HERO) + _portrait.custom_minimum_size = Vector2(56, 0) + _portrait.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + _portrait.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + row.add_child(_portrait) + var col := VBoxContainer.new() + col.size_flags_horizontal = Control.SIZE_EXPAND_FILL + col.add_theme_constant_override("separation", 3) + row.add_child(col) + col.add_child(UiKit.label("探险者", 14, true)) + _hp_bar = ProgressBar.new() + _hp_bar.custom_minimum_size = Vector2(0, 12) + _hp_bar.show_percentage = false + col.add_child(_hp_bar) + _hp_label = UiKit.label("HP —", 13, false) + col.add_child(_hp_label) + _atk_label = UiKit.label("攻击 —", 13, false) + col.add_child(_atk_label) + _status_label = _fixed_label(46) # 缺陷①约束:固定槽高 + clip——状态文案切换零回流 + col.add_child(_status_label) + + +func set_data(hp: int, max_hp: int, atk: int, atk_mod: String, status_summary: String) -> void: + _hp_bar.max_value = maxi(1, max_hp) + _hp_bar.value = hp + _hp_label.text = "HP %d / %d" % [hp, max_hp] + _atk_label.text = "攻 %d %s" % [atk, atk_mod] + _status_label.text = status_summary + + +func set_idle() -> void: + _hp_bar.value = 0 + _hp_label.text = "HP —" + _atk_label.text = "攻击 —" + _status_label.text = "" + + +# battle_over 反馈(B3×4④):状态逐槽化光收回 + 微文案——英雄卡侧紧凑引用同步 +func set_status_cleared_feedback() -> void: + _status_label.text = "状态随战斗结束消散" + + +func _fixed_label(height: float) -> Label: + var l := UiKit.label("", 12, false) + l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + l.custom_minimum_size = Vector2(0, height) + l.clip_contents = true + return l diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd.uid new file mode 100644 index 00000000..a7da8a89 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd.uid @@ -0,0 +1 @@ +uid://dj2024ofvmee0 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd new file mode 100644 index 00000000..5d61280f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd @@ -0,0 +1,167 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(C 区角色信息 240×520——HP/攻击(+修正)/RAP 直角细线数据框 +# 〔修正来源角标〕、状态效果槽 ×4、小人立绘占位;RAP 清零可见反馈〔A3 义务〕; +# 状态槽空态 = 槽隐藏上移补位〔EC12〕)。 +# Date: 2026-09-12 +# 高内聚边界:纯视图 setter 面——装配(含修正文案)归 hud_presenter.gd;定长槽 + clip_contents +# (缺陷①回归约束)。 +class_name HeroInfoPanelView +extends PanelContainer + +const COL_INK := Color8(51, 38, 27) +const COL_GOLD := Color8(240, 168, 40) +const COL_DOT_OFF := Color8(120, 106, 88) + +var _hp_bar: ProgressBar +var _hp_label: Label +var _atk_label: Label +var _rap_dots: HBoxContainer +var _rap_label: Label +var _rap_feedback: Label +var _status_feedback: Label +var slot_row: StatusSlotRow +var _last_rap := 0 # 上次投影值(清零反馈判据——禁解析自身 label 文本当状态) + + +func _init() -> void: + custom_minimum_size = Vector2(240, 0) + clip_contents = true + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(232, 220, 196) + sb.set_corner_radius_all(0) + sb.set_border_width_all(1) + sb.border_color = Color8(120, 106, 88) + sb.content_margin_left = 8.0 + sb.content_margin_right = 8.0 + sb.content_margin_top = 6.0 + sb.content_margin_bottom = 6.0 + add_theme_stylebox_override("panel", sb) + var col := VBoxContainer.new() + col.add_theme_constant_override("separation", 6) + add_child(col) + + col.add_child(UiKit.label("探险者", 16, true)) + _hp_bar = ProgressBar.new() + _hp_bar.custom_minimum_size = Vector2(0, 12) + _hp_bar.show_percentage = false + col.add_child(_hp_bar) + _hp_label = _data_line() + col.add_child(_hp_label) + _atk_label = _data_line() + col.add_child(_atk_label) + + # RAP 框(pattern 5 点阵资源显示——点阵图形 + 数字精确值双承载) + var rap_head := HBoxContainer.new() + rap_head.add_theme_constant_override("separation", 6) + col.add_child(rap_head) + _rap_dots = HBoxContainer.new() + _rap_dots.add_theme_constant_override("separation", 2) + rap_head.add_child(_rap_dots) + _rap_label = UiKit.label("RAP 0", 13, true) + rap_head.add_child(_rap_label) + _rap_feedback = UiKit.label("", 12, false) + _rap_feedback.add_theme_color_override("font_color", Color8(140, 124, 100)) + col.add_child(_rap_feedback) + + col.add_child(UiKit.label("状态效果", 14, true)) + slot_row = StatusSlotRow.new() + col.add_child(slot_row) + _status_feedback = UiKit.label("", 12, false) + _status_feedback.add_theme_color_override("font_color", Color8(140, 124, 100)) + col.add_child(_status_feedback) + + # 小人立绘占位(程序化色块——真实立绘归 B5) + var portrait := ColorRect.new() + portrait.color = Color8(46, 110, 142, 56) + portrait.custom_minimum_size = Vector2(0, 120) + portrait.size_flags_vertical = Control.SIZE_EXPAND_FILL + col.add_child(portrait) + var portrait_glyph := UiKit.label("小人立绘占位", 13, false) + portrait_glyph.add_theme_color_override("font_color", COL_INK) + portrait_glyph.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + portrait_glyph.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + portrait_glyph.mouse_filter = Control.MOUSE_FILTER_IGNORE + portrait_glyph.set_anchors_preset(Control.PRESET_FULL_RECT) + portrait.add_child(portrait_glyph) + + +func set_hero(hp: int, max_hp: int) -> void: + _hp_bar.max_value = maxi(1, max_hp) + _hp_bar.value = hp + _hp_label.text = "HP %d / %d" % [hp, max_hp] + + +func set_attack(atk: int, mod_text: String) -> void: + # 修正来源角标:mod_text 形如「(+50% 增伤)」——来源 = 效果 display_name(B3 投影) + _atk_label.text = "攻击 %d %s" % [atk, mod_text] + + +func set_rap(rap: int, cap_hint: int) -> void: + var prev := _last_rap + _last_rap = rap + _rap_label.text = "RAP %d(攒 %d 刷图)" % [rap, cap_hint] + _render_rap_dots(rap) + if prev > 0 and rap == 0: + # battle_over RAP 清零可见反馈(A3 D1 义务):数字滚动归零 + 微文案占位 + _rap_feedback.text = "随战斗结束消散" + var tw := create_tween() + tw.tween_property(_rap_label, "modulate", Color(1.4, 1.2, 0.7), 0.12) + tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), 0.3) + elif rap > 0: + _rap_feedback.text = "" + + +func set_status_effects(effects: Array) -> void: + slot_row.set_effects(effects) + _status_feedback.text = "" + + +# battle_over:状态槽全清可见反馈(逐槽化光 + 微文案——B3×4④) +func set_status_cleared_feedback() -> void: + slot_row.set_cleared_feedback() + _status_feedback.text = "状态随战斗结束消散" + + +func set_idle() -> void: + _hp_bar.value = 0 + _hp_label.text = "HP —" + _atk_label.text = "攻击 —" + _last_rap = 0 + _render_rap_dots(0) + _rap_label.text = "RAP —" + slot_row.set_effects([]) + + +func hp_text() -> String: + return _hp_label.text + + +func rap_text() -> String: + return _rap_label.text + + +# 资源面拒绝反馈(pattern 1 resource_face 腿):RAP 数字脉冲 +func pulse_rap() -> void: + var tw := create_tween() + tw.tween_property(_rap_label, "modulate", Color(1.0, 0.4, 0.3), 0.1) + tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), 0.25) + + +func _render_rap_dots(rap: int) -> void: + var shown := mini(rap, 16) + while _rap_dots.get_child_count() < shown: + var d := ColorRect.new() + d.custom_minimum_size = Vector2(8, 8) + d.size_flags_vertical = Control.SIZE_SHRINK_CENTER + d.mouse_filter = Control.MOUSE_FILTER_IGNORE + _rap_dots.add_child(d) + for i in _rap_dots.get_child_count(): + var d: ColorRect = _rap_dots.get_child(i) + d.color = COL_GOLD if i < shown else COL_DOT_OFF + + +func _data_line() -> Label: + var l := UiKit.label("", 14, false) + l.clip_text = true + l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + return l diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd.uid new file mode 100644 index 00000000..110b5096 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_info_panel_view.gd.uid @@ -0,0 +1 @@ +uid://mxt26g8ok6tm diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd new file mode 100644 index 00000000..ab554418 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd @@ -0,0 +1,226 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(E 区道具 280×520——库存常读列表〔逐件 icon+名称+属性简述, +# 枚举序 = stock 受理序、固定槽高 + clip_contents(缺陷①教训)〕;窗口期原位激活为道具使用窗口 +# 〔布局决策 D2 / pattern 2 原位窗口激活〕:描边金光点亮、标题切换+时机指示、usable_now 禁选态 +# +原因〔pattern 8 禁用态即理由〕、跳过按钮、政策注记常驻)。 +# Date: 2026-09-12(改造自 item_window_overlay.gd——中央模态形态废弃,原位激活替代) +# 契约红线:选择语义 = 窗口条目位置(B1 义务④"选定序"),库存句柄不出本类 +# (缺陷④契约约束的呈现侧收口点)。来源 {cell, round} 不呈现于列表与 tooltip(2026-09-12 拍板)。 +class_name InventoryPanelView +extends PanelContainer + +signal item_selected(pos: int) +signal skipped +signal reject_reason(text: String) # 禁选件点击 → pattern 8 原因 toast(组合根接 toast 槽) + +const COL_GOLD := Color8(240, 168, 40) +const ROW_H := 44 + +var _title: Label +var _timing: Label +var _list: VBoxContainer +var _skip_btn: Button +var _policy: Label +var _normal_title := "道包 · 关卡域" +var _window_title := "⏳ 轮末道具窗口 · 至多选 1 件" +var _panel_sb: StyleBoxFlat +var _window_mode := false +var _cleared_hold_until := 0 # 库存清空反馈持有期(ms 时间戳——期间常读刷新不覆盖提示) + + +func _init() -> void: + custom_minimum_size = Vector2(280, 0) + clip_contents = true + _panel_sb = StyleBoxFlat.new() + _panel_sb.bg_color = Color8(232, 220, 196) + _panel_sb.set_corner_radius_all(0) + _panel_sb.set_border_width_all(1) + _panel_sb.border_color = Color8(120, 106, 88) + _panel_sb.content_margin_left = 8.0 + _panel_sb.content_margin_right = 8.0 + _panel_sb.content_margin_top = 6.0 + _panel_sb.content_margin_bottom = 6.0 + add_theme_stylebox_override("panel", _panel_sb) + var col := VBoxContainer.new() + col.add_theme_constant_override("separation", 4) + add_child(col) + _title = UiKit.label(_normal_title, 16, true) + col.add_child(_title) + _timing = UiKit.label("", 12, false) + _timing.add_theme_color_override("font_color", Color8(140, 124, 100)) + col.add_child(_timing) + _list = VBoxContainer.new() + _list.add_theme_constant_override("separation", 2) + _list.size_flags_vertical = Control.SIZE_EXPAND_FILL + col.add_child(_list) + _skip_btn = UiKit.button("跳过") + _skip_btn.visible = false + _skip_btn.pressed.connect(func(): skipped.emit()) + col.add_child(_skip_btn) + _policy = UiKit.label("注:未选用件随关卡结束清空", 12, false) + _policy.add_theme_color_override("font_color", Color8(140, 124, 100)) + _policy.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + col.add_child(_policy) + + +# ---- 常读态(挖掘期规划面——B4 D1 义务①) ---- + +func show_inventory(rows: Array) -> void: + if Time.get_ticks_msec() < _cleared_hold_until: + return # 清空反馈展示中(「剩余道具 ×N 随关卡结束清空」) + _window_mode = false + _set_window_visual(false) + _title.text = _normal_title + _timing.text = "" + _skip_btn.visible = false + _rebuild_rows(rows, false) + + +# 空态(指引性,非报错——B4 EC 空态) +func show_empty() -> void: + if Time.get_ticks_msec() < _cleared_hold_until: + return + show_inventory([]) + _add_empty_hint() + + +# ---- 窗口态(pattern 2 原位激活:面板描边金光点亮、标题切换 + 时机指示常驻) ---- + +# items = B1 组装窗口投影(usable_now 由 B1 组装——呈现层只消费不推断) +func open_window(items: Array) -> void: + _window_mode = true + _set_window_visual(true) + _title.text = _window_title + _timing.text = "时机:授予段后 · 增益/减益段前" + _skip_btn.visible = true + _rebuild_rows(items, true) + + +func close_window() -> void: + _window_mode = false + _set_window_visual(false) + + +func is_window_open() -> bool: + return _window_mode + + +# 探针兼容面:窗口悬置判定 +func is_open() -> bool: + return _window_mode + + +# 库存清空反馈(B4 D1×⑤):逐件化光点飞出 + 「剩余道具 ×N 随关卡结束清空」占位 +func play_cleared_feedback(remaining: int) -> void: + _cleared_hold_until = Time.get_ticks_msec() + 3000 + var rows := _list.get_children() + for r in rows: + var tw := (r as Control).create_tween() + tw.tween_property(r, "modulate:a", 0.0, 0.5) + tw.tween_property(r, "position:y", (r as Control).position.y - 40.0, 0.5) + _tw_after_clear(remaining) + + +func _tw_after_clear(remaining: int) -> void: + var tw := create_tween() + tw.tween_interval(0.6) + tw.tween_callback(func(): + _title.text = _normal_title + _cleared_hold_until = 0 + _rebuild_rows([], false) + _add_empty_hint("剩余道具 ×%d 随关卡结束清空" % remaining)) + + +# ---- 行构建(固定槽高 + clip——缺陷①回归约束) ---- + +func _rebuild_rows(rows: Array, window: bool) -> void: + for child in _list.get_children(): + child.queue_free() + for r in rows: + _list.add_child(_make_row(r, window)) + if rows.is_empty() and not window: + _add_empty_hint() + + +func _add_empty_hint(text := "道包空空——安全格里藏着补给") -> void: + var lab := UiKit.label(text, 13, false) + lab.add_theme_color_override("font_color", Color8(140, 124, 100)) + lab.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + lab.custom_minimum_size = Vector2(0, ROW_H) + lab.clip_contents = true + _list.add_child(lab) + + +func _make_row(r: Dictionary, window: bool) -> PanelContainer: + var row := PanelContainer.new() + row.custom_minimum_size = Vector2(0, ROW_H) + row.clip_contents = true + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(224, 208, 180) + sb.set_corner_radius_all(0) + sb.set_border_width_all(1) + sb.border_color = Color8(150, 134, 112) + sb.content_margin_left = 6.0 + sb.content_margin_right = 6.0 + sb.content_margin_top = 3.0 + sb.content_margin_bottom = 3.0 + row.add_theme_stylebox_override("panel", sb) + var h := HBoxContainer.new() + h.add_theme_constant_override("separation", 6) + row.add_child(h) + var icon := UiKit.label(_icon_glyph(String(r.get("category", ""))), 16, true) + icon.custom_minimum_size = Vector2(20, 0) + h.add_child(icon) + var mid := VBoxContainer.new() + mid.size_flags_horizontal = Control.SIZE_EXPAND_FILL + mid.alignment = BoxContainer.ALIGNMENT_CENTER + h.add_child(mid) + var name_lab := UiKit.label(String(r.get("display_name", "")), 14, true) + mid.add_child(name_lab) + var desc := UiKit.label(String(r.get("desc", "")), 11, false) + desc.add_theme_color_override("font_color", Color8(120, 106, 88)) + desc.clip_text = true + desc.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + mid.add_child(desc) + row.tooltip_text = String(r.get("tooltip", "")) # 完整描述 + 触发注记(不含来源——拍板) + if not window: + var st := UiKit.label(String(r.get("status", "")), 12, false) + st.size_flags_vertical = Control.SIZE_SHRINK_CENTER + h.add_child(st) + return row + # 窗口态:usable_now 禁选 + 原因(pattern 8——灰置 + tooltip「HP 已满 · 禁选」+ 点击原因 toast) + var usable: bool = bool(r.get("usable_now", false)) + var use_btn := UiKit.button("选用") + use_btn.custom_minimum_size = Vector2(56, 0) + use_btn.size_flags_vertical = Control.SIZE_SHRINK_CENTER + use_btn.disabled = not usable + use_btn.pressed.connect(func(): item_selected.emit(int(r.get("pos", -1)))) + h.add_child(use_btn) + if not usable: + name_lab.add_theme_color_override("font_color", Color8(150, 140, 130)) + row.tooltip_text = String(r.get("reason", "暂不可用")) + "\n" + row.tooltip_text + row.gui_input.connect(func(ev: InputEvent): + if ev is InputEventMouseButton and ev.pressed and ev.button_index == MOUSE_BUTTON_LEFT: + reject_reason.emit(String(r.get("reason", "暂不可用")))) + return row + + +func _icon_glyph(category: String) -> String: + match category: + "damage_item": + return "刃" + "heal_pack": + return "医" + "buff": + return "纹" + return "◆" + + +func _set_window_visual(on: bool) -> void: + # 原位激活视觉(pattern 2):描边金光点亮(占位级——静态金描边) + if on: + _panel_sb.set_border_width_all(3) + _panel_sb.border_color = COL_GOLD + else: + _panel_sb.set_border_width_all(1) + _panel_sb.border_color = Color8(120, 106, 88) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd.uid new file mode 100644 index 00000000..6847c48f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd.uid @@ -0,0 +1 @@ +uid://cnd5vc3sonulf diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd new file mode 100644 index 00000000..ecb3c07d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd @@ -0,0 +1,85 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(D 区图例按钮 + 模态 overlay——格态/变异/雾语义图例, +# 「迷雾 = 无雷盲区」条目恒在〔A4 D1×②〕;状态槽空态说明成文载入〔B3 D1×①〕)。 +# Date: 2026-09-12 +# pattern 6 模态 overlay 层级纪律:打开时由组合根隐藏上方遮挡层 + 锁棋盘输入; +# 本类自持开关(toggle),探针断言"打开后自身 visible"——遮挡物断言归场景探针。 +class_name LegendOverlay +extends Control + +signal closed + +const ENTRIES := [ + ["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键插旗(1AP)"], + ["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"], + ["#9691C4", "雾纹带(色相+\"雾\"字)", "未开雾格——数字被迷雾遮蔽,雾=无雷盲区"], + ["#D8D0EA", "淡雾残留(无字)", "已开雾格——与已开 0 格同规则,雾不传播连锁"], + ["#C63D22", "旗", "插旗标记——轮末自动判定标中;雾内插旗纯损"], + ["#2E6E8E", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"], + ["#F0A828", "金徽章", "已开格奖励身份 icon(旗之下、纹带之上,不进数字区)"], + ["#33261B", "状态槽空态", "无状态效果时槽位隐藏,数据框上移补位——非异常"], +] + +var _panel: PanelContainer + + +func _init() -> void: + var dim := ColorRect.new() + dim.color = Color(0.08, 0.06, 0.05, 0.55) + dim.set_anchors_preset(Control.PRESET_FULL_RECT) + dim.gui_input.connect(func(ev: InputEvent): + if ev is InputEventMouseButton and ev.pressed: + close()) + add_child(dim) + var center := CenterContainer.new() + center.set_anchors_preset(Control.PRESET_FULL_RECT) + dim.add_child(center) + _panel = PanelContainer.new() + center.add_child(_panel) + var box := VBoxContainer.new() + box.custom_minimum_size = Vector2(520, 0) + box.add_theme_constant_override("separation", 6) + _panel.add_child(box) + box.add_child(UiKit.label("图例 · 格态与变异", 20, true)) + # 「迷雾 = 无雷盲区」条目恒在(A4 图例条目恒在义务) + box.add_child(UiKit.label("迷雾 = 无雷盲区:雾格不藏雷,不需要赌命开雾。", 14, true)) + box.add_child(HSeparator.new()) + for e in ENTRIES: + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 8) + var chip := ColorRect.new() + chip.custom_minimum_size = Vector2(18, 18) + chip.color = Color(e[0]) + row.add_child(chip) + var lab := UiKit.label("%s —— %s" % [e[1], e[2]], 13, false) + lab.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + lab.custom_minimum_size = Vector2(440, 0) + row.add_child(lab) + box.add_child(row) + box.add_child(HSeparator.new()) + var close_btn := UiKit.button("关闭") + close_btn.pressed.connect(close) + box.add_child(close_btn) + visible = false + + +func is_open() -> bool: + return visible + + +func toggle() -> void: + if visible: + close() + else: + open() + + +func open() -> void: + visible = true + + +func close() -> void: + if not visible: + return + visible = false + closed.emit() diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd.uid new file mode 100644 index 00000000..6a62f886 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd.uid @@ -0,0 +1 @@ +uid://dwvjnpkagfxft diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd new file mode 100644 index 00000000..a70ca993 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd @@ -0,0 +1,64 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(A 区关卡进度条——全宽 × 28px 顶栏,当前节点高亮、已过节点暗金)。 +# Date: 2026-09-12(battle.md 五区改造批——对齐 28px 顶栏与高亮样式;基础 = 2026-09-11 C1-lite 呈现) +# 关卡节点进度条视图(C1-lite 呈现):线性节点链 战→赏→疗→王 的推进状态。 +# 高内聚边界:只读 runner 的节点链/进度做渲染,无任何写面。 +class_name LevelStripView +extends HBoxContainer + +const COL_DONE := Color8(140, 116, 60) # 已过——暗金 +const COL_CURRENT := Color8(240, 168, 40) # 当前——奖励金高亮 +const COL_UPCOMING := Color8(64, 58, 52) +const COL_BOSS_BORDER := Color8(198, 61, 34) # Boss 边框——旗朱 +const NODE_LABELS := {"battle": "战", "reward": "赏", "rest": "疗", "boss": "王"} + + +func _init() -> void: + alignment = BoxContainer.ALIGNMENT_CENTER + add_theme_constant_override("separation", 6) + custom_minimum_size = Vector2(0, 28) # A 区 28px 顶栏(battle.md §Layout) + + +func refresh(runner) -> void: + for child in get_children(): + child.queue_free() + if runner == null: + return + var nodes: Array = runner.nodes() + for i in nodes.size(): + var node: Dictionary = nodes[i] + var type := String(node.get("type", "")) + add_child(_node_chip(type, _node_color(i, runner))) + if i < nodes.size() - 1: + var arrow := UiKit.label("→", 13, false) + arrow.add_theme_color_override("font_color", Color8(120, 110, 98)) + add_child(arrow) + + +func _node_color(i: int, runner) -> Color: + var finished: bool = i < int(runner.node_index) or String(runner.level_result) != "" + var is_current: bool = i == int(runner.node_index) and String(runner.level_result) == "" + if is_current: + return COL_CURRENT + if finished: + return COL_DONE + return COL_UPCOMING + + +func _node_chip(type: String, col: Color) -> PanelContainer: + var p := PanelContainer.new() + var sb := StyleBoxFlat.new() + sb.set_corner_radius_all(4) + sb.content_margin_left = 9.0 + sb.content_margin_right = 9.0 + sb.content_margin_top = 2.0 + sb.content_margin_bottom = 2.0 + sb.bg_color = col + if type == "boss": + sb.set_border_width_all(2) + sb.border_color = COL_BOSS_BORDER + p.add_theme_stylebox_override("panel", sb) + var l := UiKit.label(String(NODE_LABELS.get(type, "?")), 15, true) + l.add_theme_color_override("font_color", Color8(30, 26, 22) if col != COL_UPCOMING else Color8(160, 150, 138)) + p.add_child(l) + return p diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd.uid new file mode 100644 index 00000000..626c051a --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd.uid @@ -0,0 +1 @@ +uid://mv5cxybk4mf0 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/node_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/node_overlay.gd new file mode 100644 index 00000000..75b9e69b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/node_overlay.gd @@ -0,0 +1,51 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11 +# 关卡节点覆盖层(模态):奖励点/恢复点兑付结果的确认呈现。 +# 高内聚边界:节点文案的玩家可读化(谁获得什么/回复多少)内聚于此;"继续"意图经信号外发。 +class_name NodeOverlay +extends Control + +signal continue_pressed + +var _label: Label +var _btn: Button + + +func _init() -> void: + var dim := ColorRect.new() + dim.color = Color(0, 0, 0, 0.6) + dim.set_anchors_preset(Control.PRESET_FULL_RECT) + add_child(dim) + var center := CenterContainer.new() + center.set_anchors_preset(Control.PRESET_FULL_RECT) + dim.add_child(center) + var box := VBoxContainer.new() + box.add_theme_constant_override("separation", 12) + center.add_child(box) + _label = UiKit.label("", 20, true) + _label.add_theme_color_override("font_color", Color8(240, 226, 200)) + _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + box.add_child(_label) + _btn = UiKit.button("继续") + _btn.pressed.connect(func(): continue_pressed.emit()) + box.add_child(_btn) + visible = false + + +func show_reward(snap: Dictionary) -> void: + _label.text = "关卡奖励点\n获得「%s」(威力 %d)\n已收入库存——跨战斗延续 · 关卡末清空" % [ + String(snap.get("display_name", "")), int(snap.get("power", 0))] + _btn.text = "收下,继续" + visible = true + + +func show_rest(healed: int, hp_after: int, max_hp: int) -> void: + _label.text = "血量恢复点\n休息回复 %d HP(%d → %d / %d)" % [ + healed, hp_after - healed, hp_after, max_hp] + _btn.text = "休息完毕,继续" + visible = true + + +# 探针复用:以真实按钮路径触发继续 +func press_continue() -> void: + _btn.pressed.emit() diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/node_overlay.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/node_overlay.gd.uid new file mode 100644 index 00000000..0ed5dd97 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/node_overlay.gd.uid @@ -0,0 +1 @@ +uid://b07icmmegvvto diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_review_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_review_overlay.gd new file mode 100644 index 00000000..3f3e25ca --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_review_overlay.gd @@ -0,0 +1,57 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(B 区结算明细回看——模态 overlay:完整流水逐行、只读、关闭恢复; +# pattern 6 模态 overlay 层级纪律——打开时组合根隐藏上方遮挡层 + 锁棋盘输入,场景级断言承载)。 +# Date: 2026-09-12 +class_name SettlementReviewOverlay +extends Control + +signal closed + +var _log: RichTextLabel + + +func _init() -> void: + var dim := ColorRect.new() + dim.color = Color(0.08, 0.06, 0.05, 0.62) + dim.set_anchors_preset(Control.PRESET_FULL_RECT) + dim.gui_input.connect(func(ev: InputEvent): + if ev is InputEventMouseButton and ev.pressed: + close()) + add_child(dim) + var center := CenterContainer.new() + center.set_anchors_preset(Control.PRESET_FULL_RECT) + dim.add_child(center) + var panel := PanelContainer.new() + center.add_child(panel) + var box := VBoxContainer.new() + box.custom_minimum_size = Vector2(760, 560) + box.add_theme_constant_override("separation", 6) + panel.add_child(box) + box.add_child(UiKit.label("结算明细回看(完整流水 · 逐行归因)", 18, true)) + _log = RichTextLabel.new() + _log.bbcode_enabled = true + _log.size_flags_vertical = Control.SIZE_EXPAND_FILL + _log.clip_contents = true + _log.scroll_active = true + box.add_child(_log) + var close_btn := UiKit.button("关闭") + close_btn.pressed.connect(close) + box.add_child(close_btn) + visible = false + + +func is_open() -> bool: + return visible + + +func open(full_bb_text: String) -> void: + _log.clear() + _log.append_text(full_bb_text) + visible = true + + +func close() -> void: + if not visible: + return + visible = false + closed.emit() diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_review_overlay.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_review_overlay.gd.uid new file mode 100644 index 00000000..43c8afe1 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/settlement_review_overlay.gd.uid @@ -0,0 +1 @@ +uid://da4d8u4kl3x3k diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd new file mode 100644 index 00000000..4288958e --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd @@ -0,0 +1,144 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(C 区状态效果槽 ×4〔B3 D1×①〕——effect_id→icon 形状+色相双通道、 +# 层数角标、剩余结算次数点、悬停 tooltip(来源+语义+剩余);空态 = 槽隐藏(数据框上移补位, +# EC12 空态裁决);极性映射 = B1 SettlementTable 效果语义而非 magnitude 符号〔B3 D1×③〕)。 +# Date: 2026-09-12 +# 高内聚边界:纯视图——效果投影装配归 hud_presenter.gd;B3 纯被动容器零信号——刷新绑 B1 +# 结算编排完成时点(battle.md 更新触发注记)。 +class_name StatusSlotRow +extends HBoxContainer + +const MAX_SLOTS := 4 +# 极性/语义映射(数据源 = SettlementTable.effect_pool_map 效果语义 + StatusEffectTable display_name): +# 益 = ▲/◆/▼(金/蓝族);害 = ⚡(旗朱族)——形状+色相双通道,文字名常驻第三通道 +const EFFECT_ICON := { + "attack_up": ["▲", Color8(176, 122, 20), "益"], + "defense_up": ["◆", Color8(46, 110, 142), "益"], + "weaken": ["▼", Color8(46, 110, 142), "益"], + "ambush": ["⚡", Color8(198, 61, 34), "害"], +} +const EFFECT_SEMANTIC := { + "attack_up": "主角攻击加成(hero_offense 池加算)", + "defense_up": "受到伤害减免(hero_defense 池加算)", + "weaken": "敌方输出削弱(enemy_output 池乘算)", + "ambush": "敌方全体攻击附加(flat_attach——踩雷转化,战斗段共享不消耗)", +} +const SOURCE_NAMES := {"safe_cell": "安全格", "flag_hit": "标中旗", "mine_hit": "踩雷", "clear_reward": "清图奖励"} + +var _tip: PanelContainer +var _tip_label: Label +var _slots: Array[PanelContainer] = [] # 槽引用数组持有——tooltip _tip 悬停后入子节点表,按位序 get_child 会误取(BUG-0001) +var _cleared_feedback := false + + +func _init() -> void: + add_theme_constant_override("separation", 6) + _tip = PanelContainer.new() + _tip.z_index = 10 + _tip.mouse_filter = Control.MOUSE_FILTER_IGNORE + _tip.visible = false + var sb := StyleBoxFlat.new() + sb.bg_color = Color(0.16, 0.12, 0.09, 0.96) + sb.set_corner_radius_all(2) + sb.set_border_width_all(1) + sb.border_color = Color8(240, 168, 40) + sb.content_margin_left = 6.0 + sb.content_margin_right = 6.0 + sb.content_margin_top = 3.0 + sb.content_margin_bottom = 3.0 + _tip.add_theme_stylebox_override("panel", sb) + _tip_label = UiKit.label("", 13, false) + _tip_label.add_theme_color_override("font_color", Color8(240, 226, 200)) + _tip_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + _tip_label.custom_minimum_size = Vector2(210, 0) + _tip.add_child(_tip_label) + + +# effects = B3 query_active 投影数组(含空态);空槽隐藏,非空按插入序填充(最多 4 槽可见——超出截断) +func set_effects(effects: Array) -> void: + _cleared_feedback = false + _sync_slots(effects.size()) + for i in _slots.size(): + _fill_slot(_slots[i], effects[i]) + _slots[i].visible = true + + +# battle_over 反馈(B3×4④):逐槽化光收回 + 微文案(下次 set_effects 恢复常态) +func set_cleared_feedback() -> void: + _cleared_feedback = true + for slot in _slots: + slot.visible = true + slot.modulate = Color(0.7, 0.7, 0.7, 0.5) + var col := slot.get_child(0) as VBoxContainer + var icon := col.get_child(0) as Label + icon.text = "✕ " + icon.text + + +func _sync_slots(n: int) -> void: + # 移除旧槽(槽生命周期由 _slots 数组唯一管理——子节点表含 tooltip 节点,禁按位序索引;BUG-0001) + for slot in _slots: + remove_child(slot) + slot.queue_free() + _slots.clear() + var count := mini(maxi(n, 0), MAX_SLOTS) + for i in count: + var slot := _make_slot() + _slots.append(slot) + add_child(slot) + + +func _make_slot() -> PanelContainer: + var slot := PanelContainer.new() + slot.custom_minimum_size = Vector2(50, 46) + var sb := StyleBoxFlat.new() + sb.bg_color = Color8(226, 210, 182) + sb.set_corner_radius_all(0) + sb.set_border_width_all(1) + sb.border_color = Color8(120, 106, 88) + slot.add_theme_stylebox_override("panel", sb) + slot.mouse_entered.connect(_on_slot_hover.bind(slot)) + slot.mouse_exited.connect(func(): _tip.visible = false) + var col := VBoxContainer.new() + col.alignment = BoxContainer.ALIGNMENT_CENTER + slot.add_child(col) + var icon := UiKit.label("", 16, true) + icon.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + col.add_child(icon) + var meta := UiKit.label("", 11, false) + meta.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + col.add_child(meta) + return slot + + +func _fill_slot(slot: PanelContainer, eff: Dictionary) -> void: + slot.modulate = Color(1, 1, 1) + slot.set_meta("eff", eff) + var col := slot.get_child(0) as VBoxContainer + var icon := col.get_child(0) as Label + var meta := col.get_child(1) as Label + var icon_cfg: Array = EFFECT_ICON.get(String(eff.effect_id), ["◇", Color8(120, 106, 88), "?"]) + icon.text = String(icon_cfg[0]) + icon.add_theme_color_override("font_color", icon_cfg[1]) + # 层数角标 + 剩余结算次数点(● 未耗余量) + var dots := "" + var left: int = int(eff.get("remaining_settlements", 0)) + for i in maxi(left, 1): + dots += "●" if i < left else "○" + meta.text = "×%d %s" % [int(eff.get("layers", 1)), dots] + + +func _on_slot_hover(slot: Control) -> void: + # tooltip:来源 + 语义 + 剩余(≥13px;悬停只读反馈通道——无点击无写) + if not slot.has_meta("eff"): + return + var eff: Dictionary = slot.get_meta("eff") + var icon_cfg: Array = EFFECT_ICON.get(String(eff.effect_id), ["◇", Color8(120, 106, 88), "?"]) + var src: String = String(SOURCE_NAMES.get(String(eff.get("source_class", "")), String(eff.get("source_class", "")))) + var semantic: String = String(EFFECT_SEMANTIC.get(String(eff.effect_id), String(eff.get("display_name", "")))) + _tip_label.text = "%s〔%s〕\n%s\n剩余结算 %d 次\n来源:%s(第 %d 轮)" % [ + String(eff.get("display_name", "")), String(icon_cfg[2]), semantic, + int(eff.get("remaining_settlements", 0)), src, int(eff.get("round_index", 0))] + if not _tip.is_inside_tree(): + add_child(_tip) + _tip.position = Vector2(0, -70) + _tip.visible = true diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd.uid new file mode 100644 index 00000000..dbf799f4 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd.uid @@ -0,0 +1 @@ +uid://bpvdcwk5lf5gr diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/toast_stack.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/toast_stack.gd new file mode 100644 index 00000000..aade2a8d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/toast_stack.gd @@ -0,0 +1,63 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(D 区 toast 槽——非模态提示:拒绝原因双源/voided/雾首遇/chord/ +# 零合法板;自动消退 2.5s;pattern 1/10 呈现通道)。 +# Date: 2026-09-12 +# 高内聚边界:纯呈现——组合根决定何时弹什么文案;本类只管槽位堆叠与消退动画。 +class_name ToastStack +extends Control + +const LIFE_SEC := 2.5 +const MAX_SLOTS := 3 +const COL_INFO := Color8(51, 38, 27) +const COL_WARN := Color8(198, 61, 34) # 旗朱族——拒绝/作废 +const COL_GOLD := Color8(176, 122, 20) # 奖励金族——提示/达成 + +var _slot_box: VBoxContainer + + +func _init() -> void: + mouse_filter = Control.MOUSE_FILTER_IGNORE + set_anchors_preset(Control.PRESET_TOP_WIDE) + custom_minimum_size = Vector2(0, 84) + _slot_box = VBoxContainer.new() + _slot_box.mouse_filter = Control.MOUSE_FILTER_IGNORE + _slot_box.alignment = BoxContainer.ALIGNMENT_BEGIN + _slot_box.add_theme_constant_override("separation", 2) + add_child(_slot_box) + + +func show_toast(text: String, tone: String = "info") -> void: + while _slot_box.get_child_count() >= MAX_SLOTS: + var old: Control = _slot_box.get_child(0) + _slot_box.remove_child(old) + old.queue_free() + var row := PanelContainer.new() + row.mouse_filter = Control.MOUSE_FILTER_IGNORE + row.clip_contents = true # 缺陷①约束:固定槽高裁剪,不回流布局 + row.custom_minimum_size = Vector2(0, 24) + var sb := StyleBoxFlat.new() + sb.bg_color = Color(0.94, 0.91, 0.84, 0.95) + sb.set_corner_radius_all(2) + sb.set_border_width_all(1) + sb.content_margin_left = 8.0 + sb.content_margin_right = 8.0 + sb.content_margin_top = 2.0 + sb.content_margin_bottom = 2.0 + var col := COL_INFO + if tone == "warn": + col = COL_WARN + elif tone == "gold": + col = COL_GOLD + sb.border_color = col + row.add_theme_stylebox_override("panel", sb) + var lab := UiKit.label(("⚠ " if tone == "warn" else "") + text, 14, tone != "info") + lab.add_theme_color_override("font_color", col) + lab.clip_text = true + row.add_child(lab) + _slot_box.add_child(row) + var tw := row.create_tween() + tw.tween_interval(LIFE_SEC) + tw.tween_property(row, "modulate:a", 0.0, 0.35) + tw.tween_callback(func(): + if is_instance_valid(row): + row.queue_free()) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/toast_stack.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/toast_stack.gd.uid new file mode 100644 index 00000000..9cbfd0a3 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/toast_stack.gd.uid @@ -0,0 +1 @@ +uid://bx0a0bydmnwv8 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd new file mode 100644 index 00000000..dacfd7f1 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd @@ -0,0 +1,213 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11(2026-09-12 code-review 修复批重构) +# 测试用角色数值调整板(模拟天赋树属性提升——仅测试,正式侧归 C4 元进度)。 +# 纯视图边界(规范 1.3):本类只承载滑杆 UI 与生效值预览;加成应用 / 重置落表 / 批量模拟编排 +# 全部经信号外发由组合根承接(apply_requested / reset_requested / simulate_requested / closed), +# 模拟进度与报告经 setter 注入——视图零核心写、零 smoke 脚本依赖、不拼报告文案(装配归 presenter)。 +# 探针接缝(ADR-0004"合成输入走真实翻译出口"口径):press_apply_for_probe 等三键 = 按钮真实信号路径。 +class_name TunePanel +extends Control + +signal apply_requested(atk_pct: int, hp_pct: int) # 应用并关闭——加成落表归组合根单一应用路径 +signal reset_requested() # 重置为 0——视图自清滑杆,落表归组合根 +signal simulate_requested(count: int) # 批量模拟编排归组合根(表基线复位 + 单路径施加) +signal closed + +var _db +var _base_atk := 0 +var _base_hp := 0 +var _atk: HSlider +var _hp: HSlider +var _atk_val: Label +var _hp_val: Label +var _eff: Label +var _sim_count: SpinBox +var _sim_btn: Button +var _sim_out: RichTextLabel + + +func _init(db_ref, base_atk: int, base_hp: int) -> void: + _db = db_ref + _base_atk = base_atk + _base_hp = base_hp + _build() + _sync_labels() + + +# ---- 对外语义面(组合根/探针)---- + +func atk_pct() -> int: + return int(_atk.value) + + +func hp_pct() -> int: + return int(_hp.value) + + +func set_sliders(atk: int, hp: int) -> void: + _atk.value = atk + _hp.value = hp + + +func reset_sliders() -> void: + _atk.value = 0 + _hp.value = 0 + _sync_labels() + + +func effect_text() -> String: + return _eff.text + + +func set_sim_count(n: int) -> void: + _sim_count.value = n + + +func is_running() -> bool: + return _sim_btn.disabled + + +func set_sim_running(running: bool) -> void: + _sim_btn.disabled = running + _sim_btn.text = "模拟中…" if running else "运行模拟" + + +func set_sim_progress(text: String) -> void: + _sim_out.text = text + + +func set_sim_report(text: String) -> void: + _sim_out.text = text + + +func output_text() -> String: + return _sim_out.get_parsed_text() + + +# ---- 探针接缝(真实翻译出口——按钮 pressed 信号路径,非直调组合根)---- + +func press_apply_for_probe() -> void: + _apply_btn_pressed() + + +func press_reset_for_probe() -> void: + _reset_btn_pressed() + + +func press_simulate_for_probe() -> void: + _sim_btn.pressed.emit() + + +# ---- 内部(信号 → 外发)---- + +func _apply_btn_pressed() -> void: + apply_requested.emit(atk_pct(), hp_pct()) + + +func _reset_btn_pressed() -> void: + reset_sliders() + reset_requested.emit() + + +func effective_atk() -> int: + return _scaled(_base_atk, atk_pct()) + + +func effective_max_hp() -> int: + return _scaled(_base_hp, hp_pct()) + + +# ---- 内部构建 ---- + +func _build() -> void: + var dim := ColorRect.new() + dim.color = Color8(28, 22, 18) + dim.set_anchors_preset(Control.PRESET_FULL_RECT) + add_child(dim) + var center := CenterContainer.new() + center.set_anchors_preset(Control.PRESET_FULL_RECT) + dim.add_child(center) + var box := VBoxContainer.new() + box.custom_minimum_size = Vector2(640, 0) + box.add_theme_constant_override("separation", 8) + center.add_child(box) + + var title := UiKit.label("测试用 · 角色数值调整板", 26, true) + title.add_theme_color_override("font_color", Color8(240, 226, 200)) + box.add_child(title) + var desc := UiKit.label( + "模拟玩家在循环中解锁天赋获得的属性提升(仅测试工具,不落盘)。\n百分比叠加基础值:攻击 %d / 生命 %d。影响所有模式(手动与模拟)。" % [_base_atk, _base_hp], + 13, false) + desc.add_theme_color_override("font_color", Color8(200, 188, 166)) + box.add_child(desc) + box.add_child(HSeparator.new()) + + var atk_row := _slider_row(box, "攻击加成") + _atk = atk_row.slider + _atk_val = atk_row.value_label + var hp_row := _slider_row(box, "生命加成") + _hp = hp_row.slider + _hp_val = hp_row.value_label + _eff = UiKit.label("", 14, false) + box.add_child(_eff) + + var btn_row := HBoxContainer.new() + box.add_child(btn_row) + var apply_btn := UiKit.button("应用并关闭") + apply_btn.pressed.connect(_apply_btn_pressed) + btn_row.add_child(apply_btn) + var reset_btn := UiKit.button("重置为 0") + reset_btn.pressed.connect(_reset_btn_pressed) + btn_row.add_child(reset_btn) + box.add_child(HSeparator.new()) + + var sim_row := HBoxContainer.new() + box.add_child(sim_row) + sim_row.add_child(UiKit.label("批量模拟第一关", 15, false)) + _sim_count = SpinBox.new() + _sim_count.min_value = 10 + _sim_count.max_value = 200 + _sim_count.step = 10 + _sim_count.value = 30 + _sim_count.custom_minimum_size = Vector2(90, 0) + sim_row.add_child(_sim_count) + sim_row.add_child(UiKit.label("次(理性玩家策略,含面板当前加成)", 13, false)) + _sim_btn = UiKit.button("运行模拟") + _sim_btn.pressed.connect(func(): simulate_requested.emit(int(_sim_count.value))) + sim_row.add_child(_sim_btn) + _sim_out = RichTextLabel.new() + _sim_out.bbcode_enabled = true + _sim_out.custom_minimum_size = Vector2(0, 300) + box.add_child(_sim_out) + + var close_btn := UiKit.button("关闭") + close_btn.pressed.connect(func(): closed.emit()) + box.add_child(close_btn) + + +func _slider_row(box: VBoxContainer, title: String) -> Dictionary: + var row := HBoxContainer.new() + box.add_child(row) + row.add_child(UiKit.label(title, 15, false)) + var s := HSlider.new() + s.min_value = 0 + s.max_value = 300 + s.step = 5 + s.custom_minimum_size = Vector2(320, 24) + row.add_child(s) + var v := UiKit.label("+0%", 15, true) + v.custom_minimum_size = Vector2(70, 0) + row.add_child(v) + s.value_changed.connect(func(_value: float): _sync_labels()) + return {"slider": s, "value_label": v} + + +func _sync_labels() -> void: + _atk_val.text = "+%d%%" % atk_pct() + _hp_val.text = "+%d%%" % hp_pct() + _eff.text = "生效值:攻击 %d → %d · 生命 %d → %d" % [ + _base_atk, effective_atk(), _base_hp, effective_max_hp()] + + +func _scaled(base: int, pct: int) -> int: + return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0))) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd.uid new file mode 100644 index 00000000..8174dd13 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd.uid @@ -0,0 +1 @@ +uid://dxafka2y1f2xg diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd new file mode 100644 index 00000000..4d5cb6fb --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd @@ -0,0 +1,20 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11 +# 呈现层共享小部件工厂(各视图视图复用;避免每个视图类重复六行工厂函数)。 +class_name UiKit +extends RefCounted + + +static func label(text: String, size: int, bold: bool) -> Label: + var l := Label.new() + l.text = text + l.add_theme_font_size_override("font_size", size) + if bold: + l.add_theme_color_override("font_color", Color8(46, 34, 24)) + return l + + +static func button(text: String) -> Button: + var b := Button.new() + b.text = text + return b diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd.uid new file mode 100644 index 00000000..78b528c5 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ui_kit.gd.uid @@ -0,0 +1 @@ +uid://blj2f7qu5eide diff --git a/prototypes/dungeon-sweeper-vertical-slice/project.godot b/prototypes/dungeon-sweeper-vertical-slice/project.godot new file mode 100644 index 00000000..9ab870b3 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/project.godot @@ -0,0 +1,32 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="DungeonSweeperVerticalSlice" +run/main_scene="res://presentation/main.tscn" +config/features=PackedStringArray("4.7") + +[autoload] + +ConfigDB="*res://core/config_db.gd" +RngService="*res://core/rng_service.gd" + +[display] + +window/size/viewport_width=1280 +window/size/viewport_height=800 +window/stretch/mode="canvas_items" +window/stretch/aspect="expand" + +[rendering] + +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd new file mode 100644 index 00000000..075324b5 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd @@ -0,0 +1,206 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-13(O4/W-3 收口——自动玩家策略四份复制单一实现) +# 自动玩家策略集(headless 冒烟/扫描/转储共用的理性玩家近似): +# choose_dig = 挖最近可挖格(先移到邻接已开格);window_selection = 道具窗口确定性选择 +# (治疗包优先于刀;半血阈值/轮次门/刀力口径经参数表达——四调用点原语义逐一保留)。 +# 读取面:B1.presentation_snapshot() / A2 查询面(E1/E2 收口后的成文 API——禁直读核心状态)。 +# 等价性证明:smoke_all 同种子回放断言(步骤流逐行相同)在重构后保持全绿。 +extends RefCounted + + +# 行主序枚举未开格,找与已开集 8 邻接者;小人先移到该邻接已开格(BFS 可达)。无可挖 → {} +static func choose_dig(a2) -> Dictionary: + var revealed: Array = a2.revealed_cells() + var revealed_set := {} + for c in revealed: + revealed_set[c] = true + var board = a2.current_board() + for y in board.height: + for x in board.width: + var c := Vector2i(x, y) + if a2.is_revealed(c) or a2.has_flag(c): + continue + for n in board.neighbors8(c): + if revealed_set.has(n): + var path: Array = a2.compute_move_path(n) + if not path.is_empty() or n == a2.hero_pos(): + return {"dig_cell": c, "hero_cell": n} + return {} + + +# 道具窗口确定性选择(s = b1.presentation_snapshot();半血以下治疗包优先,否则刀): +# opts.heal_hp_div = 治疗包启用分母(hero_hp × div ≤ max_hp——原四实现 2/3 两档) +# opts.round_gate = 刀启用轮次门(round ≥ gate 才用刀;0 = 不设门——monte_carlo 口径) +# opts.knife_compare = "best"(最强单刀 ≥ 敌余 HP)| "sum"(刀力合计——dump/monte 口径) +static func window_selection(s: Dictionary, items: Array, opts: Dictionary = {}) -> Array: + var heal_div: int = int(opts.get("heal_hp_div", 3)) + var round_gate: int = int(opts.get("round_gate", 3)) + var compare: String = String(opts.get("knife_compare", "best")) + var enemy_hp := 0 + for u in s.enemies: + if bool(u.alive): + enemy_hp += int(u.hp) + var best_knife := -1 + var best_power := 0 + var sum_power := 0 + var best_heal := -1 + var best_heal_power := 0 + for i in items.size(): + var it: Dictionary = items[i] + if not bool(it.usable_now): + continue + if it.category == "damage_item": + sum_power += int(it.power) + if int(it.power) > best_power: + best_power = int(it.power) + best_knife = i + elif it.category == "heal_pack": + best_heal = i + best_heal_power = int(it.power) + var knife_power: int = best_power if compare == "best" else sum_power + var selection: Array = [] + # heal_no_waste(完美档):缺口 < 治疗量时不用——满血贴药 = 白费一次性资源(严格劣策略) + var heal_missing_ok: bool = not bool(opts.get("heal_no_waste", false)) \ + or int(s.hero.max_hp) - int(s.hero.hp) >= best_heal_power + if best_heal >= 0 and int(s.hero.hp) * heal_div <= int(s.hero.max_hp) and heal_missing_ok: + selection.append(best_heal) + elif best_knife >= 0 and knife_power >= enemy_hp and (round_gate <= 0 or int(s.round) >= round_gate): + selection.append(best_knife) + return selection + + +# ==================== 完美策略档(2026-09-14 用户拍板:OQ7 技能轴上界测量仪——只作测量,非玩法数值) ==================== +# 策略语义:零盲挖(只挖已证安全格 ⇒ 构造保证零踩雷)· 已证雷格全标旗(旗先于挖——旗 = 纯收益 +# 〔轮末 weaken + 伤害B 首次充能〕且雷上旗不阻连锁)· 卡死且刷新资源可用 → 轮内刷新 +# (经 b1.player_request_refresh 核心编排)· 否则停手(残 AP 1:1 转存 RAP——下轮刷新资源)。 +# 推理规则集 = A1 F1 measure_inferability 同族(single + subset——solver_rules=single_subset 冻结域); +# 口径差异 = unknown 集不含 A1 生成期 safe_set oracle(真实玩家约束的未知邻格含雷格)——比生成期闸 +# 保守,卡死率略高属口径诚实。输入只经 A2 查询面(reveal_record/is_revealed/has_flag/current_board +# 的邻居与可达面),禁读棋盘雷区真值(测推理上界,非全知作弊)。 +# 道具窗口:轮次门解除(round_gate=0)+ 治疗无浪费(heal_no_waste)。 +# 待拍板(未实现):非卡死主动刷新、囤刀倾泻 vs 即用即耗、完美板目标加权。 + +# 单步决策入口(每动作一查——挖掘揭示新数字后重推理):move/dig/flag/refresh/stop 五值。 +static func perfect_action(a2, b1) -> Dictionary: + var ded := _deduce(a2) + for c in ded["mines"]: + if not a2.is_revealed(c) and not a2.has_flag(c): + return _plan_on(a2, c, "flag") + for c in ded["safe"]: + if not a2.is_revealed(c) and not a2.has_flag(c): + return _plan_on(a2, c, "dig") + if b1 != null and bool(b1.can_request_refresh()): + return {"kind": "refresh"} + return {"kind": "stop"} + + +# 目标格需英雄 8 邻——不可达先移到目标的已开 8 邻格(行主序首个可达者;同 choose_dig 寻路口径) +static func _plan_on(a2, c: Vector2i, kind: String) -> Dictionary: + var d := Vector2i(absi(c.x - a2.hero_pos().x), absi(c.y - a2.hero_pos().y)) + if maxi(d.x, d.y) == 1: + return {"kind": kind, "cell": c} + var board = a2.current_board() + for n in board.neighbors8(c): + if a2.is_revealed(n) and (n == a2.hero_pos() or not a2.compute_move_path(n).is_empty()): + return {"kind": "move", "cell": n} + return {"kind": "stop"} # 不可达(理论不可达——已证格必邻接已开区);防御性停手防死循环 + + +# 推理不动点:single(约束余雷=0⇒未知全安全 / 余雷=未知数⇒全雷)+ subset(U_a ⊂ U_b ⇒ 差集约束)。 +# 约束只依赖 revealed/flags/mines 推断集——mines 集增长才需重建约束(safe 标记不改正约约束)。 +static func _deduce(a2) -> Dictionary: + var board = a2.current_board() + var safe := {} + var mines := {} + var changed := true + while changed: + changed = false + var cons := _constraints(a2, board, mines) + for con in cons: + if int(con["mines"]) == 0: + for c in con["unknown"].keys(): + if not safe.has(c): + safe[c] = true + changed = true + elif int(con["mines"]) == (con["unknown"] as Dictionary).size(): + for c in con["unknown"].keys(): + if not mines.has(c): + mines[c] = true + changed = true + var cons2 := _constraints(a2, board, mines) + for i in cons2.size(): + for j in range(i + 1, cons2.size()): + var der := _subset_derived(cons2[i], cons2[j]) + if der.is_empty(): + continue + if int(der["mines"]) == 0: + for c in der["unknown"].keys(): + if not safe.has(c): + safe[c] = true + changed = true + elif int(der["mines"]) == (der["unknown"] as Dictionary).size(): + for c in der["unknown"].keys(): + if not mines.has(c): + mines[c] = true + changed = true + return {"safe": _row_major(safe), "mines": _row_major(mines)} + + +# 约束集 = 已开数字格:mines_left = digit − 已知雷旗(旗位 + 已证雷格),unknown = 未开无旗 8 邻。 +# 已爆雷格(kind=mine)不带数字约束——其贡献已含在邻格 digit 内(数字恒真不变式 inv3)。 +static func _constraints(a2, board, mines: Dictionary) -> Array: + var out: Array = [] + for c in a2.revealed_cells(): + var rec: Dictionary = a2.reveal_record(c) + if String(rec.get("kind", "")) != "safe": + continue + var d := int(rec.get("digit", -1)) + if d < 0: + continue + var unknown := {} + var known := 0 + for n in board.neighbors8(c): + if a2.has_flag(n): + known += 1 + elif not a2.is_revealed(n): + if mines.has(n): + known += 1 + else: + unknown[n] = true + var d_eff := d - known + if unknown.is_empty() or d_eff < 0: + continue # 零未知 = 无信息;负余雷 = 矛盾态(非法板/误旗)——不产约束 + out.append({"mines": d_eff, "unknown": unknown}) + return out + + +# pair 差集约束:U_a ⊂ U_b ⇒ {unknown = U_b∖U_a, mines = mines_b−mines_a}(互不包含/相等集 → 空) +static func _subset_derived(ca: Dictionary, cb: Dictionary) -> Dictionary: + var res := _subset_one_way(ca, cb) + if not res.is_empty(): + return res + return _subset_one_way(cb, ca) + + +static func _subset_one_way(ca: Dictionary, cb: Dictionary) -> Dictionary: + var ua: Dictionary = ca["unknown"] + var ub: Dictionary = cb["unknown"] + if ua.is_empty() or ub.is_empty() or ua.size() >= ub.size(): + return {} + for k in ua.keys(): + if not ub.has(k): + return {} + var diff := {} + for k in ub.keys(): + if not ua.has(k): + diff[k] = true + var dm := int(cb["mines"]) - int(ca["mines"]) + if dm < 0: + return {} # 矛盾态防御(完美档无误旗——理论不可达) + return {"unknown": diff, "mines": dm} + + +static func _row_major(cells: Dictionary) -> Array: + var out: Array = cells.keys() + out.sort_custom(func(a, b): return a.y * 1000 + a.x < b.y * 1000 + b.x) + return out diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd.uid new file mode 100644 index 00000000..9355caba --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd.uid @@ -0,0 +1 @@ +uid://cjws5lgencguh diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd new file mode 100644 index 00000000..4ad3e55c --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd @@ -0,0 +1,458 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-13(playtest-2026-09-12 报告 Top1——零承伤根因分割探针) +# 逐结算行打印 beat / atk.total / hero_defense 池值 / d_tick,分割报告悬置的两个嫌疑: +# H1 defense_up 叠满墙(设计语义——hero_defense 池触 clamp 下限 = 全额免伤) +# H2 Boss/敌攻击链缺陷(bug——fresh 池下攻击拍仍零兑现) +# 快照盲区说明:settlement_snapshot 取在衰减段之后,时效 1 的 defense_up 当轮授予当轮衰减—— +# 真人局 JSONL buffs[] 恒空 ≠ 池为空,本探针改从结算行(授予/续存 effect 行的 pool_value)重建战斗段时点池值。 +# 观测面:全部读 B1 结算行 / 表值 / 呈现快照(零核心穿透);池归属/墙深/池界全读表(D2 Rule 1——零数值字面量)。 +# 模式(-- 后参数):boss [seed](默认 42:Run A 零挖掘 fresh 池 + Run B 自动挖掘墙行为) +# level [seed](全关遭遇回放 + 多种子 spawn 扫描——报告 Bug#1 证据面) +# 运行:Godot_console.exe --headless --path . --script res://smoke/damage_probe.gd -- boss 42 +# Godot_console.exe --headless --path . --script res://smoke/damage_probe.gd -- level 42 +extends SceneTree + +const AutoPlayer := preload("res://smoke/auto_player.gd") + +var _checks := 0 +var _fails := 0 + + +func check(cond: bool, label: String) -> void: + _checks += 1 + if cond: + print(" [PASS] " + label) + else: + _fails += 1 + print(" [FAIL] " + label) + + +func _initialize() -> void: + print("=== 零承伤根因分割探针(playtest-2026-09-12 报告 Top1)===") + var db = load("res://core/config_db.gd").new() + if not db.load_tables("res://data/tables", false): + print("表加载失败") + quit(1) + return + var args := OS.get_cmdline_user_args() + var mode := "boss" + if args.size() >= 1 and (args[0] == "boss" or args[0] == "level"): + mode = args[0] + var seed_val := 42 + if args.size() >= 2 and args[1].is_valid_int(): + seed_val = int(args[1]) + if mode == "boss": + _probe_boss(db, seed_val) + else: + _probe_level(db, seed_val) + print("=== 探针完成:%d 项检查,%d 项失败 ===" % [_checks, _fails]) + quit(1 if _fails > 0 else 0) + + +# ==================== boss 模式:逐结算行打印 + 根因判定 ==================== + +func _probe_boss(db, seed_val: int) -> void: + var math = load("res://core/damage_math.gd") + var def_row: Dictionary = db.status_effect_row("defense_up") + var wall_floor: int = int(def_row.get("magnitude", 0)) * int(def_row.get("max_stack", 1)) + var clamp_lo: int = int(db.settlement.pool_clamp_lo) + var clamp_hi: int = int(db.settlement.pool_clamp_hi) + print("表值面:defense_up magnitude=%d × max_stack=%d = 墙最深池 %d · pool_clamp [%d, %d]" % [ + int(def_row.get("magnitude", 0)), int(def_row.get("max_stack", 1)), wall_floor, clamp_lo, clamp_hi]) + + print("--- Run A:fresh 池(零挖掘停手轮——无授予、无墙、攻击链裸验)---") + var run_a := _run_boss(db, seed_val, false) + _print_rounds(db, math, run_a, true) + var a_ok := _assert_rounds(db, math, run_a, true) + check(a_ok.fresh_and_full, "A1 攻击链健康(fresh 池):非蓄力拍 d_tick = atk.total 全额兑现(×%d 拍)——H2 攻击链缺陷否证" % a_ok.n_attack) + check(a_ok.arith_ok, "A2 池算术完整:全部非蓄力拍 d_tick == DamageMath.pool_damage(×%d 拍)" % a_ok.n_attack) + check(String(run_a.result) == "defeat", "A3 攻击链致死可达:fresh 池 Boss 在 %d 轮内击倒英雄(result=%s——伤害确实落地)" % [run_a.snaps.size(), str(run_a.result)]) + var run_a2 := _run_boss(db, seed_val, false) + check(JSON.stringify(run_a.rows) == JSON.stringify(run_a2.rows), "A4 同种子回放:Run A 两遍结算行逐行相同") + + print("--- Run B:自动挖掘(授予照常发生——当前数据墙行为实测)---") + var run_b := _run_boss(db, seed_val, true) + _print_rounds(db, math, run_b, true) + var b_ok := _assert_rounds(db, math, run_b, false) + check(b_ok.arith_ok, "B1 池算术完整(含墙轮):全部非蓄力拍 d_tick == pool_damage(raw, hero_def, enemy_out)(×%d 拍)" % b_ok.n_attack) + check(b_ok.wall_tick_positive, "B2 墙轮不归零(当前数据重证):hero_def<0 的轮 d_tick 恒 > 0(墙轮 ×%d,最深实测池 %d)" % [b_ok.n_wall, b_ok.deepest]) + var binding_floor: int = maxi(wall_floor, clamp_lo) # 墙深与池界取更浅者 = 实际可达的最深免伤池 + check(wall_floor > clamp_lo, "B3 墙归零不可达(表值判定):墙最深池 %d > pool_clamp_lo %d → 最低残余 ×%.2f——旧数据墙(旧 magnitude×2 = clamp_lo 触底 → ×0)为真人局零承伤根因,09-13 调参批已拆(H1 成立=设计语义,非攻击链 bug)" % [wall_floor, clamp_lo, float(100 + binding_floor) / 100.0]) + + +func _run_boss(db, seed_val: int, dig: bool) -> Dictionary: + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var b1 = load("res://core/battle_orchestrator.gd").new(db, rng) + var cap := {"rows": [], "snaps": [], "result": ""} + b1.step_row.connect(func(row): cap["rows"].append(row)) + b1.settlement_snapshot.connect(func(s): cap["snaps"].append(s)) + b1.battle_finished.connect(func(p): cap["result"] = String(p.get("result", ""))) + b1.window_opened.connect(func(items): + var s: Dictionary = b1.presentation_snapshot() + b1.submit_window_selection(AutoPlayer.window_selection(s, items, {"heal_hp_div": 2, "round_gate": 3, "knife_compare": "best"}))) + if not b1.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(db.hero.max_hp)}): + cap["result"] = "begin_failed" + return cap + var rounds := 0 + while b1.battle_result == "" and rounds < 40: + rounds += 1 + var start_round: int = b1.round_index + if dig: + var a2 = b1.a2 + var guard := 400 + while b1.a3.phase == "Active" and b1.round_index == start_round and guard > 0: + guard -= 1 + var plan := AutoPlayer.choose_dig(a2) + if plan.is_empty(): + break + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if b1.a3.phase == "Active": + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if b1.a3.phase == "Active" and b1.round_index == start_round: + a2.settle_batch([{"action": "stop"}]) + else: + b1.a2.settle_batch([{"action": "stop"}]) + return cap + + +# 逐轮:授予行汇总 → 重建战斗段时点池值(effect_pool_map 表驱动归属)→ 敌行动行逐行打印 +func _print_rounds(db, math, run: Dictionary, verbose: bool) -> void: + var groups := _round_groups(run.rows) + var beats := _played_beats(run, db) + var rounds: Array = groups.keys() + rounds.sort() + for ri in rounds: + var rows: Array = groups[ri] + var an := _analyze_round(db, math, rows, beats) + var grant_txt := "(无)" + if not an.grants.is_empty(): + grant_txt = " · ".join(an.grants) + print(" [R%d] 授予: %s" % [ri, grant_txt]) + for line in an.lines: + print(" " + String(line)) + var hp_txt := "" + if int(ri) - 1 < run.snaps.size(): + var s: Dictionary = run.snaps[int(ri) - 1] + var hero: Dictionary = s.get("hero", {}) + hp_txt = " · hero %d/%d · verdict %s" % [int(hero.get("hp", -1)), int(hero.get("max_hp", -1)), String(s.get("verdict", ""))] + print(" [R%d] 池值@战斗段: hero_def=%d enemy_out=%d%s" % [ri, an.pd, an.pe, hp_txt]) + + +func _assert_rounds(db, math, run: Dictionary, fresh_mode: bool) -> Dictionary: + var groups := _round_groups(run.rows) + var beats := _played_beats(run, db) + var rounds: Array = groups.keys() + rounds.sort() + var out := {"fresh_and_full": true, "arith_ok": true, "wall_tick_positive": true, + "n_attack": 0, "n_wall": 0, "deepest": 0} + for ri in rounds: + var an := _analyze_round(db, math, groups[ri], beats) + for t in an.ticks: + out.n_attack += 1 + if fresh_mode and (an.pd != 0 or an.pe != 0): + out.fresh_and_full = false + if not bool(t.ok): + out.arith_ok = false + if fresh_mode and (int(t.tick) <= 0 or int(t.raw) <= 0): + out.fresh_and_full = false + if an.pd < 0: + out.n_wall += 1 + out.deepest = mini(out.deepest, an.pd) + if int(t.tick) <= 0: + out.wall_tick_positive = false + return out + + +func _round_groups(rows: Array) -> Dictionary: + var groups := {} + for row in rows: + var ri := int(row.get("round_index", 0)) + if not groups.has(ri): + groups[ri] = [] + groups[ri].append(row) + return groups + + +func _analyze_round(db, math, rows: Array, beats: Dictionary) -> Dictionary: + var last_pool := {} + var grants: Array = [] + for row in rows: + if String(row.get("type", "")) == "effect": + var eid := String(row.get("effect_id", "")) + var pv := int(row.get("pool_value", 0)) + last_pool[eid] = pv + grants.append("%s→池%d" % [String(row.get("display_name", "")), pv]) + # 池归属表驱动(settlement.effect_pool_map),percent 池加算后 clamp(B1 F2 同口径) + var pools := {"hero_defense": 0, "enemy_output": 0, "hero_offense": 0, "flat_attach": 0} + for eid in last_pool: + var pool_name: String = String(db.settlement.effect_pool_map.get(eid, "")) + if pools.has(pool_name): + pools[pool_name] += int(last_pool[eid]) + var clamp_lo: int = int(db.settlement.pool_clamp_lo) + var clamp_hi: int = int(db.settlement.pool_clamp_hi) + var pd: int = clampi(int(pools["hero_defense"]), clamp_lo, clamp_hi) + var pe: int = clampi(int(pools["enemy_output"]), clamp_lo, clamp_hi) + var lines: Array = [] + var ticks: Array = [] + for row in rows: + var t := String(row.get("type", "")) + if t == "charge": + lines.append("拍=charge(蓄力拍零兑付——设计语义,不读池)") + elif t == "damage" and String(row.get("source_class", "")) == "enemy_action": + var baseline: int = int(row.get("baseline", 0)) + var attach: int = int(row.get("attach", 0)) + var raw: int = baseline + attach + var tick: int = int(row.get("value", 0)) + var expected: int = math.pool_damage(raw, pd, pe) + var zeroed := "✓" if tick == expected else "✗ 算术不符!" + lines.append("拍=%s 原始=%d+%d=%d hero_def=%d enemy_out=%d d_tick=%d 期望=%d %s" % [ + String(beats.get(int(row.get("round_index", 0)), "?")), baseline, attach, raw, pd, pe, tick, expected, zeroed]) + ticks.append({"raw": raw, "tick": tick, "expected": expected, "ok": tick == expected}) + elif t == "damage" and String(row.get("source_class", "")) == "hero_attack": + lines.append("主角攻击 %d(base %d + 开格 %d + 标雷 %d)→ %s" % [ + int(row.get("value", 0)), int(row.get("base", 0)), int(row.get("dmg_a", 0)), int(row.get("dmg_b", 0)), String(row.get("target", ""))]) + return {"pd": pd, "pe": pe, "grants": grants, "lines": lines, "ticks": ticks} + + +# 拍名重建:round r 的已打拍 = 快照 r-1 的敌人 beat 字段(快照取在 advance 之后 = 下一拍预付); +# R1 = 行 pattern 首元素(steps 从 0 起)。快照 enemies 空时(敌死亡轮)回退 "?"——该轮敌已无行动行。 +func _played_beats(run: Dictionary, db) -> Dictionary: + var beats := {} + var erow: Dictionary = db.resolve_enemy_rowset(1, 4) + var pat: Array = erow.get("pattern", []) + if pat.size() > 0: + beats[1] = String(pat[0]) + for r in range(2, run.snaps.size() + 1): + var s: Dictionary = run.snaps[r - 2] + var en: Array = s.get("enemies", []) + if en.size() > 0: + beats[r] = String(en[0].get("beat", "?")) + return beats + + +# ==================== level 模式:全关遭遇回放(Bug#1 证据面) ==================== + +func _probe_level(db, seed_val: int) -> void: + print("--- 全关回放 seed %d(战×2→道具→战×2→恢复→Boss)---" % seed_val) + var prediction := _encounter_prediction(db, seed_val) + var cap := _run_level(db, seed_val) + var battles: Array = cap.battles + for b in battles: + var depth: int = int(b.get("depth", -1)) + var spawn: int = int(b.get("spawn", -1)) + var predicted: int = int(prediction.get(depth, -1)) + var verdict_txt := _battle_verdict_txt(cap, depth) + var match_txt := "✓" if spawn == predicted else "✗ 与遭遇骰预测不符!" + print(" d%d %s: spawn=%d(遭遇骰预测 %d %s)%s" % [depth, String(b.get("type", "")), spawn, predicted, match_txt, verdict_txt]) + var all_match := true + var all_ge_1 := true + for b in battles: + var spawn2: int = int(b.get("spawn", -1)) + if spawn2 < 1: + all_ge_1 = false + if spawn2 != int(prediction.get(int(b.get("depth", -1)), -2)): + all_match = false + check(all_ge_1, "全部战斗 spawn ≥ 1(C3 Rule 8 区间下限——空遭遇在当前实现不可达)") + check(all_match, "spawn 与独立遭遇骰预测逐战斗一致(encounter 封闭流同种子可回放)") + var expect_battles := _battle_nodes_upto(db, 1, int(cap.get("end_node", -1))) + check(battles.size() == expect_battles, "战斗推进数与节点链一致(实测 %d/预期 %d——败北截断或全链跑通)" % [battles.size(), expect_battles]) + print(" 关卡结果: %s" % str(cap.result)) + print("--- 多种子 spawn 扫描(报告 Bug#1 回归面:20 种子 × 5 战斗节点)---") + var scan := _scan_spawns(db, seed_val, 20) + check(scan.bad == 0, "%d 场战斗 spawn 全域内(∈[unit_count, unit_count_max] 且 ≥1)——越域 %d 场" % [scan.total, scan.bad]) + + +func _encounter_prediction(db, seed_val: int) -> Dictionary: + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var out := {} + var scope := 0 + var lrow := _level_row(db, 1) + for node in lrow.get("nodes", []): + var t := String(node.get("type", "")) + if t != "battle" and t != "boss": + continue + var depth := int(node.get("node_depth", -1)) + var row: Dictionary = db.resolve_enemy_rowset(1, depth) + var lo := int(row.get("unit_count", 1)) + var hi := int(row.get("unit_count_max", lo)) + var rolled := lo + if hi > lo: + var rg := RandomNumberGenerator.new() + rg.seed = rng.derive_at("encounter", scope) + scope += 1 + rolled = lo + rg.randi_range(0, hi - lo) + out[depth] = rolled + return out + + +func _level_row(db, level_index: int) -> Dictionary: + for row in db.level.rows: + if int(row.level_index) == level_index: + return row + return {} + + +func _run_level(db, seed_val: int) -> Dictionary: + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var runner = load("res://core/level_runner.gd").new(db, rng) + var cap := {"battles": [], "rows": [], "result": "", "end_node": -1} + runner.step_row.connect(func(row): cap["rows"].append(row)) + runner.window_opened.connect(_on_level_window.bind(runner)) + runner.node_entered.connect(_on_level_node_entered.bind(runner, cap)) + runner.round_started.connect(_on_level_round_started.bind(runner, cap)) + runner.level_finished.connect(_on_level_finished.bind(cap)) + runner.start_level(1, seed_val) + var guard_battles := 20 + while cap["result"] == "" and guard_battles > 0: + guard_battles -= 1 + var b1 = runner.current_battle() + if b1 == null: + break + _drive_battle(runner, b1, cap) + return cap + + +func _on_level_finished(p: Dictionary, cap: Dictionary) -> void: + cap["result"] = String(p.get("result", "")) + cap["end_node"] = int(p.get("node_index", -1)) + + +func _battle_nodes_upto(db, level_index: int, node_idx: int) -> int: + var n := 0 + var nodes: Array = _level_row(db, level_index).get("nodes", []) + for i in range(mini(node_idx + 1, nodes.size())): + var t := String(nodes[i].get("type", "")) + if t == "battle" or t == "boss": + n += 1 + return n + + +func _on_level_window(items: Array, runner) -> void: + var b1 = runner.current_battle() + if b1 == null: + runner.submit_window_selection([]) + return + var s: Dictionary = b1.presentation_snapshot() + runner.submit_window_selection(AutoPlayer.window_selection(s, items, {"heal_hp_div": 2, "round_gate": 3, "knife_compare": "best"})) + + +func _on_level_node_entered(node: Dictionary, _info: Dictionary, runner, cap: Dictionary) -> void: + var t := String(node.get("type", "")) + if t == "battle" or t == "boss": + cap["battles"].append({"type": t, "depth": int(node.get("node_depth", -1)), "spawn": -1}) + else: + runner.continue_after_node() + + +func _on_level_round_started(ri: int, runner, cap: Dictionary) -> void: + if ri != 1 or cap["battles"].is_empty(): + return + var b1 = runner.current_battle() + if b1 == null: + return + var s: Dictionary = b1.presentation_snapshot() + cap["battles"][cap["battles"].size() - 1]["spawn"] = s.enemies.size() + + +func _drive_battle(runner, b1, cap: Dictionary) -> void: + var round_guard := 60 + while cap["result"] == "" and runner.current_battle() == b1 and b1.battle_result == "" and round_guard > 0: + round_guard -= 1 + var start_round: int = b1.round_index + var a2 = b1.a2 + var action_guard := 400 + while runner.current_battle() == b1 and b1.battle_result == "" and b1.a3.phase == "Active" \ + and b1.round_index == start_round and action_guard > 0: + action_guard -= 1 + var plan := AutoPlayer.choose_dig(a2) + if plan.is_empty(): + break + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if b1.a3.phase != "Active" or b1.round_index != start_round: + break + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if runner.current_battle() == b1 and b1.battle_result == "" and b1.a3.phase == "Active" and b1.round_index == start_round: + a2.settle_batch([{"action": "stop"}]) + + +# 战斗判定摘要:round-1 victory 的战斗必须能从结算行分辨「首轮全灭」vs「空场」—— +# 判据:首轮存在伤害行 = 有敌且被杀(wipe);零战斗行 = 空场(Bug#1 特征) +func _battle_verdict_txt(cap: Dictionary, depth: int) -> String: + # 逐战斗行段切分:round_index 回卷 = 新战斗(defense_scan 同口径) + var battle_rows: Array = [] + var segs: Array = [] + var last_round := 0 + for row in cap.rows: + var r: int = int(row.get("round_index", 0)) + if r < last_round: + segs.append(battle_rows) + battle_rows = [] + last_round = r + battle_rows.append(row) + if not battle_rows.is_empty(): + segs.append(battle_rows) + var target := _battle_index_of_depth(depth, cap) + if target >= 0 and target < segs.size(): + return _summarize_rows(segs[target]) + return "" + + +func _battle_index_of_depth(depth: int, cap: Dictionary) -> int: + for i in cap["battles"].size(): + if int(cap["battles"][i].get("depth", -1)) == depth: + return i + return -1 + + +func _summarize_rows(rows: Array) -> String: + var verdict := "" + var r1_rows: Array = [] + for row in rows: + if String(row.get("type", "")) == "verdict": + verdict = String(row.get("display_name", "")) + if int(row.get("round_index", 0)) == 1: + r1_rows.append(row) + if verdict != "战斗胜利" or r1_rows.is_empty(): + return "· %s(%d 行)" % [verdict, rows.size()] + var combat_rows := 0 + var kills := 0 + for row in r1_rows: + var t := String(row.get("type", "")) + if t == "damage": + combat_rows += 1 + if bool(row.get("killed", false)): + kills += 1 + if combat_rows > 0: + return "· round-1 胜利 = 首轮全灭(wipe):战斗行 ×%d、击杀 ×%d——非空场" % [combat_rows, kills] + return "· round-1 胜利且零战斗行 = 空场特征(Bug#1)!" + + +func _scan_spawns(db, seed0: int, n_seeds: int) -> Dictionary: + var depths: Array = [] + for node in _level_row(db, 1).get("nodes", []): + var t := String(node.get("type", "")) + if t == "battle" or t == "boss": + depths.append(int(node.get("node_depth", -1))) + var total := 0 + var bad := 0 + for i in range(n_seeds): + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed0 + i) + for depth in depths: + total += 1 + var b1 = load("res://core/battle_orchestrator.gd").new(db, rng) + if not b1.begin_battle({"level_index": 1, "node_depth": int(depth), "hero_hp": int(db.hero.max_hp)}): + bad += 1 + continue + var n: int = b1.presentation_snapshot().enemies.size() + var row: Dictionary = db.resolve_enemy_rowset(1, int(depth)) + var lo := int(row.get("unit_count", 1)) + var hi := int(row.get("unit_count_max", lo)) + if n < 1 or n < lo or n > hi: + bad += 1 + return {"total": total, "bad": bad} diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd.uid new file mode 100644 index 00000000..61f8e65f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd.uid @@ -0,0 +1 @@ +uid://cl02pun3o8wcj diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/defense_scan.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/defense_scan.gd new file mode 100644 index 00000000..92c9914b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/defense_scan.gd @@ -0,0 +1,84 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-13(playtest-2026-09-12 Q1 复核——"安全格减伤是否过量导致满血打完 Boss") +# 逐结算轮统计 defense_up 授予层数(effect 行 value=-50)与该轮敌方实伤,分割"免伤墙轮" +# (≥2 层 = 池 clamp -100 = 100% 免伤)与非墙轮的实际承伤。 +# 运行:Godot_console.exe --headless --path . --script res://smoke/defense_scan.gd -- [N] [seed0] +extends SceneTree + + +func _initialize() -> void: + var args := OS.get_cmdline_user_args() + var n := 8 + if args.size() >= 1 and args[0].is_valid_int(): + n = maxi(2, int(args[0])) + var seed0 := 42 + if args.size() >= 2 and args[1].is_valid_int(): + seed0 = int(args[1]) + var db = load("res://core/config_db.gd").new() + if not db.load_tables("res://data/tables", false): + print("表加载失败") + quit(1) + return + var autoplay = load("res://smoke/level_autoplay.gd") + print("=== 减伤叠层扫描:第一关 · N=%d · 种子 %d+%d ===" % [n, seed0, n]) + print("%-6s %-6s %-8s %-10s %-12s %-12s" % ["seed", "轮数", "战斗轮", "墙轮(≥2层)", "墙轮承伤", "非墙轮承伤"]) + var wall_rounds := 0 + var open_rounds := 0 + var wall_dmg := 0 + var open_dmg := 0 + var stack_total := 0 + var combat_rounds := 0 + for i in range(n): + var st = autoplay.run_level(db, seed0 + i, 1, 0, 0, true) + # 逐轮聚合(按战斗分段——round_index 跨战斗重置,直接用轮号会串桶): + # effect 行 value=-50 计减伤层;enemy_action damage 计承伤 + var def_by_round := {} + var dmg_by_round := {} + var battle_seq := 0 + var last_round := 0 + for row in st.rows: + var r: int = int(row.get("round_index", 0)) + if r < last_round: + battle_seq += 1 # 轮号回卷 = 新战斗 + last_round = r + var key := battle_seq * 1000 + r + if String(row.get("type", "")) == "effect" and String(row.get("display_name", "")) == "减伤" \ + and String(row.get("source_class", "")) == "safe_cell": + def_by_round[key] = int(def_by_round.get(key, 0)) + 1 + elif String(row.get("type", "")) == "damage" and String(row.get("source_class", "")) == "enemy_action": + dmg_by_round[key] = int(dmg_by_round.get(key, 0)) + int(row.get("value", 0)) + for k in def_by_round: + combat_rounds += 1 + stack_total += int(def_by_round[k]) + var d: int = int(dmg_by_round.get(k, 0)) + if int(def_by_round[k]) >= 2: + wall_rounds += 1 + wall_dmg += d + else: + open_rounds += 1 + open_dmg += d + print("%-6d %-6d %-8d %-10d %-12s %-12s" % [seed0 + i, st.rounds, def_by_round.size(), + _wall_count(def_by_round), str(dmg_in(dmg_by_round, def_by_round, 2)), str(dmg_in(dmg_by_round, def_by_round, 1))]) + if combat_rounds > 0: + print("---- 战斗轮 %d:免伤墙轮 %d(%.0f%%,均值 %.2f 层/轮)· 墙轮承伤合计 %d(合法——-30 批墙帽 ×0.40)· 非墙轮承伤合计 %d(均 %.1f/轮)" % [ + combat_rounds, wall_rounds, 100.0 * float(wall_rounds) / float(combat_rounds), + float(stack_total) / float(combat_rounds), wall_dmg, open_dmg, float(open_dmg) / float(maxi(1, open_rounds))]) + quit(0) + + +func _wall_count(def_by_round: Dictionary) -> int: + var c := 0 + for r in def_by_round: + if int(def_by_round[r]) >= 2: + c += 1 + return c + + +func dmg_in(dmg_by_round: Dictionary, def_by_round: Dictionary, mode: int) -> int: + # mode=2 墙轮承伤 / mode=1 非墙轮承伤 + var t := 0 + for r in def_by_round: + var walled := int(def_by_round[r]) >= 2 + if (mode == 2 and walled) or (mode == 1 and not walled): + t += int(dmg_by_round.get(r, 0)) + return t diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/defense_scan.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/defense_scan.gd.uid new file mode 100644 index 00000000..212c24e0 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/defense_scan.gd.uid @@ -0,0 +1 @@ +uid://drne7y5gn62xe diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd new file mode 100644 index 00000000..eda07e31 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd @@ -0,0 +1,56 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# 一次性检查脚本:打印一场完整战斗的结算步骤流 + 棋盘摘要(肉眼验收 P2 归因质量)。 +# 运行:Godot_console.exe --headless --path . --script res://smoke/dump_battle.gd +extends SceneTree + +const AutoPlayer := preload("res://smoke/auto_player.gd") # O4 收口:自动玩家策略单一实现 + +func _initialize() -> void: + var db = load("res://core/config_db.gd").new() + db.load_tables("res://data/tables", false) + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(42) # 种子 = 42;第二个命令行参数可覆盖(关卡 种子) + var b1 = load("res://core/battle_orchestrator.gd").new(db, rng) + var stream: Array = [] + b1.step_row.connect(func(row): stream.append(row)) + b1.window_opened.connect(func(items): + var s: Dictionary = b1.presentation_snapshot() # E1 收口:经呈现快照面读英雄/敌况 + var selection: Array = AutoPlayer.window_selection(s, items, + {"heal_hp_div": 3, "round_gate": 3, "knife_compare": "sum"}) # O4:原 dump_battle 口径 + b1.submit_window_selection(selection)) + var level := 2 + if OS.get_cmdline_user_args().size() > 0: + level = int(OS.get_cmdline_user_args()[0]) + if OS.get_cmdline_user_args().size() > 1: + rng.set_master_seed(int(OS.get_cmdline_user_args()[1])) + b1.begin_battle({"level_index": level, "node_depth": 0, "hero_hp": db.hero.max_hp}) + print("=== 棋盘摘要 L%d ===" % level) + var bd = b1.a2.current_board() + print("尺寸 %dx%d · 雷 %d · 安全格 %d · 雾格 %d · R₀ %d" % [bd.width, bd.height, bd.mines.size(), bd.safe_cells().size(), bd.fog_cells().size(), bd.metrics.r0_count]) + print("指标: inferability=%.3f core=%.3f attempts=%s accept=%s" % [bd.metrics.inferability, bd.metrics.inferability_core, bd.metrics.attempts_used, bd.metrics.accept_mode]) + var rounds := 0 + while b1.battle_result == "" and rounds < 40: + rounds += 1 + var start_round: int = b1.round_index + var a2 = b1.a2 + var a3 = b1.a3 + var guard := 400 + while a3.phase == "Active" and b1.round_index == start_round and guard > 0: + guard -= 1 + var plan := AutoPlayer.choose_dig(a2) + if plan.is_empty(): + break + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if a3.phase == "Active": + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if a3.phase == "Active" and b1.round_index == start_round: + a2.settle_batch([{"action": "stop"}]) + print("战斗结果: %s(%d 轮)" % [b1.battle_result, rounds]) + print("=== 结算步骤流(%d 行)===" % stream.size()) + for row in stream: + print(JSON.stringify(row)) + quit(0) + diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd.uid new file mode 100644 index 00000000..707e4504 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd.uid @@ -0,0 +1 @@ +uid://cfe2mdbuakpk4 diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd new file mode 100644 index 00000000..83b87cd1 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd @@ -0,0 +1,76 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-10 +# 一次性实验:不同 mine_density 下的 R₀ 占比 / 幸存雷数 / 残余挖掘量 / 推理率——为切片临时调参提供数据。 +extends SceneTree + +func _initialize() -> void: + var db = load("res://core/config_db.gd").new() + db.load_tables("res://data/tables", true) + var a1 = load("res://core/board_generator.gd").new() + print("=== 密度实验(L1 10×10 target0.95 / L2 12×12 雾 target0.92,各 5 种子)===") + print("d\t|M|/N\tR₀\tR₀/|S|\t残余挖掘\tinf\tcore\tatt") + for d in [0.22, 0.25, 0.28, 0.30, 0.33]: + for cfg in [[10, 10, 0, 0.88, 50], [12, 12, 1, 0.90, 60]]: + var w: int = cfg[0] + var h: int = cfg[1] + var fc: int = cfg[2] + var t: float = cfg[3] + var ms: int = cfg[4] + var agg := {"m": 0, "r0": 0, "rem": 0, "inf": 0.0, "core": 0.0, "att": 0, "n": 0} + for s in range(8): + var row := { + "level_index": 1, "node_depth": 0, "board_width": w, "board_height": h, + "mine_density": d, "inferability_target": t, "solver_rules": "single_subset", + "min_safe_count": ms, "max_gen_attempts": 16, + "fog_region_count": fc, "fog_target_ratio": 0.10 if fc > 0 else 0.0, + "board_biomes": ["dungeon"], + } + _patch_row(row, d) + var params := _params_from(row) + var rg := RandomNumberGenerator.new() + rg.seed = 1000 + s + var b = a1.generate(1, 0, rg, _db(db, row)) + if b == null: + continue + agg.m += b.mines.size() + agg.r0 += int(b.metrics.r0_count) + agg.rem += b.safe_cells().size() - int(b.metrics.r0_count) + agg.inf += float(b.metrics.inferability) + agg.core += float(b.metrics.inferability_core) + agg.att += int(b.metrics.attempts_used) + agg.n += 1 + if agg.n > 0: + var n := float(agg.n) + var n_placed: int = int(round(d * float(w * h - 9))) + print("%.2f %dx%d f%d\t%d/%d\t%d\t%.2f\t%d\t%.3f\t%.3f\t%.1f" % [d, w, h, fc, int(agg.m / n + 0.5), n_placed, int(agg.r0 / n + 0.5), agg.r0 / n / (w * h - int(agg.m / n + 0.5)), int(agg.rem / n + 0.5), agg.inf / n, agg.core / n, agg.att / n]) + quit(0) + +# --- 简易 db 替身:只供给本实验的行 --- +var _row_override: Dictionary = {} + +func _patch_row(row: Dictionary, _d: float) -> void: + _row_override = row + +func _params_from(row: Dictionary) -> Dictionary: + return { + "L": 1, "D": 0, "w": int(row.board_width), "h": int(row.board_height), + "density": float(row.mine_density), "target": float(row.inferability_target), + "solver_rules": "single_subset", "min_safe": int(row.min_safe_count), + "max_attempts": int(row.max_gen_attempts), "fog_count": int(row.fog_region_count), + "fog_ratio": float(row.fog_target_ratio), "biomes": ["dungeon"], + } + +class FakeDB: + var row: Dictionary + func resolve_board_gen(_L: int, _D: int) -> Dictionary: + return row + func variant_pool(_L: int, _b: Array) -> Array: + return [{"variant_id": "fog", "unlock_level": 1, "scope": "region", "info_mask": "hide_numbers", "cell_constraint": "safe_nonzero", "biome_tags": [], "counter_item_ids": []}] + var action_economy := {"dig_ap_cost": 1} + func status_effect_row(_id: String) -> Dictionary: + return {} + +func _db(_real, row: Dictionary): + var f := FakeDB.new() + f.row = row + return f diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd.uid new file mode 100644 index 00000000..87cad61f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd.uid @@ -0,0 +1 @@ +uid://cg7jkrsc03rar diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd new file mode 100644 index 00000000..e4d55802 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd @@ -0,0 +1,200 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11(2026-09-12 增完整遥测接线——用户要求:任何模拟运行与真人试玩同日志合规: +# 操作记录 / 轮次状态 / 战斗结束 / 通关结束 全量 JSONL。logger=null 时自建会话(每局一文件), +# 批量调用方传共享 logger(log_run 局次标记切分)。 +# 关卡自动通关策略(理性玩家近似)——呈现层数值调整板的批量模拟与 headless 期望扫描(level_monte_carlo) +# 共用同一实现,保证"面板模拟读数"与"扫描表读数"同口径。策略与 smoke_all 战斗自动玩家同族: +# 挖最近可挖格 / 无可挖停手 / 半血以下用治疗包 / 击杀有把握且第 3 轮起用最强刀 / 奖励恢复点立即确认。 +# 2026-09-14 增完美策略档(policy="perfect",默认 "normal" 零行为变更——既有调用方签名兼容): +# 零盲挖/全标旗/卡死刷新——上界测量仪,求解器与动作语义见 auto_player.gd 完美策略档节。 +# 运行前提:db 已 load_tables;每个 run_level 全新 RngService(同种子 ⇒ 全程逐步可回放)。 +# 实现注:信号处理体全部为静态方法 + bind 传状态——GDScript 多行 lambda 内嵌 match/控制流的解析不可靠。 +extends RefCounted + +const AutoPlayer := preload("res://smoke/auto_player.gd") # O4 收口:自动玩家策略单一实现 + + +static func run_level(db, seed_val: int, level_index: int, atk_pct: int, hp_pct: int, collect_rows: bool = false, logger = null, policy: String = "normal") -> Dictionary: + var own_session := logger == null + if own_session: + logger = load("res://presentation/playtest_logger.gd").new() + logger.start_session(seed_val) + logger.ap_per_round = int(db.action_economy.ap_per_round) + logger.log_run({"seed": seed_val, "level_index": level_index, "atk_pct": atk_pct, "hp_pct": hp_pct, "policy": policy}) + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var runner = load("res://core/level_runner.gd").new(db, rng) + var st := { + "result": "", "hp_left": 0, "battles": 0, "rounds": 0, "hero_dmg": 0, + "reward_snapshot": {}, "rest_healed": 0, "hp_before_rest": -1, + "defeat_node_index": -1, "defeat_node_type": "", "remaining_items": -1, + "boss_depth": -1, "hp_at_boss": -1, "rows": [], "timeout": false, + "actives_total": 0, "log_path": "", "policy": policy, "refreshes": 0, "mine_hits": 0, + } + runner.step_row.connect(_on_step_row.bind(runner, st, collect_rows)) + runner.window_opened.connect(_on_window_opened.bind(runner, st, logger)) + runner.node_entered.connect(_on_node_entered.bind(runner, st, logger)) + runner.level_finished.connect(_on_level_finished.bind(st, logger)) + runner.set_hero_override(atk_pct, hp_pct) + if not runner.start_level(level_index, seed_val): + st.result = "start_failed" + runner.restore_hero_base() + if own_session: + logger.close_session() + return st + var guard_battles := 20 + while st.result == "" and guard_battles > 0: + guard_battles -= 1 + var this_b1 = runner.current_battle() + if this_b1 == null: + break # level_finished 已发(b1 置 null)而信号尚未置 result 的防御 + _autoplay_battle(runner, this_b1, st, logger) + if st.result == "": + st.result = "timeout" + st.timeout = true + runner.restore_hero_base() + st.log_path = logger.log_path() + if own_session: + logger.close_session() + return st + + +static func _on_step_row(row: Dictionary, _runner, st: Dictionary, collect_rows: bool) -> void: + if row.type == "damage" and row.source_class == "enemy_action": + st.hero_dmg += int(row.value) + if row.source_class == "mine_hit": + st.mine_hits += 1 # 踩雷证据行(effect/overflow——完美档断言恒 0;盲挖惩罚链埋点) + if row.type == "item": + st.actives_total += 1 # 可选类入库行 = 获取计数(供给控制器批度量口径) + if collect_rows: + st.rows.append(row) + + +static func _on_window_opened(items: Array, runner, st: Dictionary, logger) -> void: + var b1 = runner.current_battle() + var s: Dictionary = b1.presentation_snapshot() # E1 收口:经呈现快照面读英雄/敌况 + var opts := {"heal_hp_div": 2, "round_gate": 3, "knife_compare": "best"} # O4:原 autoplay 口径 + if String(st.get("policy", "normal")) == "perfect": + opts = {"heal_hp_div": 2, "round_gate": 0, "knife_compare": "best", "heal_no_waste": true} + var sel: Array = AutoPlayer.window_selection(s, items, opts) + if logger != null: + var chosen: Array = [] + for i in sel: + chosen.append(String(items[i].get("item_id", ""))) + logger.log_ux("ux_window_choice", {"selected_item_id": ",".join(chosen), + "usable_now": not sel.is_empty(), "skip": sel.is_empty()}) + runner.submit_window_selection(sel) + + +static func _on_node_entered(node: Dictionary, info: Dictionary, runner, st: Dictionary, logger) -> void: + var t := String(node.get("type", "")) + if t == "battle" or t == "boss": + st.battles += 1 + if logger != null and runner.current_battle() != null: + logger.attach_battle(runner.current_battle()) + if t == "boss": + st.boss_depth = int(node.get("node_depth", -1)) + st.hp_at_boss = runner.hero_hp + elif t == "reward": + st.reward_snapshot = info.get("snapshot", {}) + runner.continue_after_node() + elif t == "rest": + st.rest_healed = int(info.get("healed", 0)) + st.hp_before_rest = int(info.get("hp", 0)) - st.rest_healed + runner.continue_after_node() + if logger != null: + logger.log_node(node, info, runner.node_index) + + +static func _on_level_finished(p: Dictionary, st: Dictionary, logger) -> void: + st.result = String(p.get("result", "")) + st.hp_left = int(p.get("hp", 0)) + st.defeat_node_index = int(p.get("node_index", -1)) + st.defeat_node_type = String(p.get("node_type", "")) + st.remaining_items = int(p.get("remaining_items", -1)) + if logger != null: + logger.log_level_end(p) + + +static func _autoplay_battle(runner, b1, st: Dictionary, logger) -> void: + if String(st.get("policy", "normal")) == "perfect": + _autoplay_battle_perfect(runner, b1, st, logger) + return + var round_guard := 60 + while st.result == "" and runner.current_battle() == b1 and b1.battle_result == "" and round_guard > 0: + round_guard -= 1 + st.rounds += 1 + var start_round: int = b1.round_index + var action_guard := 400 + while runner.current_battle() == b1 and b1.battle_result == "" and b1.a3.phase == "Active" \ + and b1.round_index == start_round and action_guard > 0: + action_guard -= 1 + var plan := AutoPlayer.choose_dig(b1.a2) + if plan.is_empty(): + break + if plan.hero_cell != b1.a2.hero_pos(): + var out_m: Array = b1.a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if logger != null: + logger.log_action("move", plan.hero_cell, _accepted(out_m), {"round": start_round}) + if b1.a3.phase != "Active" or b1.round_index != start_round: + break + var out_d: Array = b1.a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if logger != null: + logger.log_action("dig", plan.dig_cell, _accepted(out_d), {"round": start_round}) + if runner.current_battle() == b1 and b1.battle_result == "" and b1.a3.phase == "Active" and b1.round_index == start_round: + var out_s: Array = b1.a2.settle_batch([{"action": "stop"}]) + if logger != null: + logger.log_action("stop", Vector2i(-1, -1), _accepted(out_s), {"round": start_round}) + + +# 完美策略档战斗驱动(2026-09-14):单步决策循环——move/dig/flag 经 A2 settle_batch 提交, +# refresh 经 b1.player_request_refresh(B1 核心编排:A3 扣 RAP + 退役 + 当场换新板), +# stop 出内环走统一停手提交。连续拒绝 ≥3 = 编排违约防御(fail-fast 语义,停手防死循环)。 +static func _autoplay_battle_perfect(runner, b1, st: Dictionary, logger) -> void: + var round_guard := 60 + while st.result == "" and runner.current_battle() == b1 and b1.battle_result == "" and round_guard > 0: + round_guard -= 1 + st.rounds += 1 + var start_round: int = b1.round_index + var action_guard := 400 + var rejects := 0 + while runner.current_battle() == b1 and b1.battle_result == "" and b1.a3.phase == "Active" \ + and b1.round_index == start_round and action_guard > 0 and rejects < 3: + action_guard -= 1 + var act := AutoPlayer.perfect_action(b1.a2, b1) + var kind := String(act.get("kind", "stop")) + if kind == "stop": + break + var ok := false + if kind == "move": + ok = _submit_intent(b1, "move", act.get("cell"), logger, start_round) + elif kind == "dig": + ok = _submit_intent(b1, "dig", act.get("cell"), logger, start_round) + elif kind == "flag": + ok = _submit_intent(b1, "flag_place", act.get("cell"), logger, start_round) + elif kind == "refresh": + ok = bool(b1.player_request_refresh()) + if logger != null: + logger.log_action("refresh", Vector2i(-1, -1), ok, {"round": start_round}) + if ok: + if kind == "refresh": + st.refreshes = int(st.refreshes) + 1 + rejects = 0 + else: + rejects += 1 + if runner.current_battle() == b1 and b1.battle_result == "" and b1.a3.phase == "Active" and b1.round_index == start_round: + var out_s: Array = b1.a2.settle_batch([{"action": "stop"}]) + if logger != null: + logger.log_action("stop", Vector2i(-1, -1), _accepted(out_s), {"round": start_round}) + + +static func _submit_intent(b1, action: String, cell, logger, round_index: int) -> bool: + var out: Array = b1.a2.settle_batch([{"action": action, "cell": cell}]) + if logger != null: + logger.log_action(action, cell, _accepted(out), {"round": round_index}) + return _accepted(out) + + +static func _accepted(outcomes: Array) -> bool: + return not outcomes.is_empty() and bool(outcomes[0].accepted) + diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd.uid new file mode 100644 index 00000000..f2ff6bde --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd.uid @@ -0,0 +1 @@ +uid://jpfa1hm7risn diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_debug.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_debug.gd new file mode 100644 index 00000000..53c98428 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_debug.gd @@ -0,0 +1,34 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11 +# 单关卡诊断:跑一遍完整关卡并打印进度与统计(定位挂起/数值问题的临时工具)。 +# 运行:Godot_console.exe --headless --path . --script res://smoke/level_debug.gd -- [seed] [atk%] [hp%] +extends SceneTree + + +func _initialize() -> void: + var args := OS.get_cmdline_user_args() + var seed_val := 4200 + var atk_pct := 0 + var hp_pct := 0 + if args.size() >= 1 and args[0].is_valid_int(): + seed_val = int(args[0]) + if args.size() >= 2 and args[1].is_valid_int(): + atk_pct = int(args[1]) + if args.size() >= 3 and args[2].is_valid_int(): + hp_pct = int(args[2]) + var db = load("res://core/config_db.gd").new() + if not db.load_tables("res://data/tables", false): + print("表加载失败") + for d in db.diagnostics(): + if d.severity == "error": + print(" error: [%s] %s" % [d.category, d.message]) + quit(1) + return + var autoplay = load("res://smoke/level_autoplay.gd") + var t0 := Time.get_ticks_msec() + print("开始:seed=%d atk+%d%% hp+%d%%" % [seed_val, atk_pct, hp_pct]) + var st = autoplay.run_level(db, seed_val, 1, atk_pct, hp_pct) + print("结果: %s · 用时 %.2f 秒" % [st.result, float(Time.get_ticks_msec() - t0) / 1000.0]) + print(" 战斗节点=%d 轮数=%d 整关承伤=%d 剩余HP=%d 进BossHP=%d 可选类获取=%d" % [st.battles, st.rounds, st.hero_dmg, st.hp_left, st.hp_at_boss, st.actives_total]) + print(" 败北节点=%d(%s) 恢复点回复=%d(前HP %d) 奖励=%s" % [st.defeat_node_index, st.defeat_node_type, st.rest_healed, st.hp_before_rest, str(st.reward_snapshot)]) + quit(0) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_debug.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_debug.gd.uid new file mode 100644 index 00000000..3656d79b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_debug.gd.uid @@ -0,0 +1 @@ +uid://bflo5h8xysqsl diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd new file mode 100644 index 00000000..7fc9076f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd @@ -0,0 +1,143 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11 +# 关卡期望扫描(C3 调参期关卡侧批量工具的原型——对应 A1 F4 / 战斗侧 monte_carlo.gd): +# 扫描 atk% × hp% 网格(模拟天赋树数值提升档位),输出每档"通关率 / 整关承伤期望 / 败北节点分布 / +# 通关剩余 HP 期望 / 进 Boss HP 期望 / 平均轮数"——把"多少数值能通关、通关要掉多少血"变成可测量数据。 +# 运行:Godot_console.exe --headless --path . --script res://smoke/level_monte_carlo.gd -- [N] [seed0] +extends SceneTree + + +func _initialize() -> void: + var args := OS.get_cmdline_user_args() + var n := 30 + if args.size() >= 1 and args[0].is_valid_int(): + n = maxi(5, int(args[0])) + var seed0 := 4200 + if args.size() >= 2 and args[1].is_valid_int(): + seed0 = int(args[1]) + var db = load("res://core/config_db.gd").new() + if not db.load_tables("res://data/tables", false): + print("表加载失败") + quit(1) + return + # 共享日志会话(2026-09-12 用户要求:模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL, + # 一文件多局,run 行携带种子/档位切分局次) + var logger = load("res://presentation/playtest_logger.gd").new() + logger.ap_per_round = int(db.action_economy.ap_per_round) + var log_path: String = logger.start_session(seed0) + print("模拟日志: %s" % log_path) + var autoplay = load("res://smoke/level_autoplay.gd") + var t0 := Time.get_ticks_msec() + # 策略轴(2026-09-14 OQ7 完美策略档——技能轴上界测量):normal = 原理性玩家近似; + # perfect = 零盲挖/全标旗/卡死刷新(auto_player.gd 完美策略档节);both = 双档对照。 + var policy_arg := "normal" + if args.size() >= 3 and args[2] in ["normal", "perfect", "both"]: + policy_arg = args[2] + var policies: Array = ["normal"] + if policy_arg == "perfect": + policies = ["perfect"] + elif policy_arg == "both": + policies = ["normal", "perfect"] + var atk_axis: Array = [0, 50, 100, 150, 200] + var hp_axis: Array = [0, 100] + print("=== 关卡期望扫描:第一关(战×2→道具→战×2→恢复→Boss)· N=%d/格 · 种子 %d+%d · 策略=%s ===" % [n, seed0, n, policy_arg]) + print("%-6s %-10s %-8s %-14s %-14s %-14s %-10s %-12s %-12s %-10s %-10s %-8s %-8s" % ["策略", "atk%/hp%", "通关率", "整关承伤均/p90", "通关剩余HP均(胜)", "进BossHP均", "平均轮数", "可选类/战", "普通承伤/场%", "跳战率", "Boss承伤%", "踩雷/局", "刷新/局"]) + var perfect_mine_total := 0 # 完美档构造保证 = 零踩雷(只挖已证安全格)——扫描末断言 + for policy in policies: + var tag := "基线" if policy == "normal" else "完美" + for atk_pct in atk_axis: + for hp_pct in hp_axis: + var wins := 0 + var dmg_all: Array = [] + var hp_left_win: Array = [] + var hp_boss: Array = [] + var rounds_all: Array = [] + var actives_per_battle: Array = [] + var normal_dmg_pct: Array = [] # 每普通战斗承伤占生效 max_hp 比(C3 F4 口径——≤20% 带) + var boss_dmg_pct: Array = [] # Boss 战承伤占生效 max_hp 比(C3 F4 口径——≤50% 带) + var wipes := 0 # 普通战斗 round-1 即胜数(首轮全灭跳战——拍板④观测面) + var normal_battles := 0 + var death_nodes := {} + var mine_per_run: Array = [] + var refresh_per_run: Array = [] + for i in range(n): + var st = autoplay.run_level(db, seed0 + i, 1, atk_pct, hp_pct, true, logger, policy) + dmg_all.append(st.hero_dmg) + rounds_all.append(st.rounds) + mine_per_run.append(int(st.mine_hits)) + refresh_per_run.append(int(st.refreshes)) + actives_per_battle.append(float(st.actives_total) / float(maxi(1, st.battles))) + var eff_max := maxi(1, int(round(float(db.hero.max_hp) * (100.0 + float(hp_pct)) / 100.0))) + # 逐战斗承伤切分(round_index 回卷 = 新战斗;末段 = Boss 战——当且仅当本局抵达 Boss 节点) + var segs := _damage_segments(st.rows) + for k in segs.size(): + var pct := 100.0 * float(segs[k].dmg) / float(eff_max) + if k == segs.size() - 1 and int(st.hp_at_boss) >= 0: + boss_dmg_pct.append(pct) + else: + normal_dmg_pct.append(pct) + normal_battles += 1 + if bool(segs[k].r1_win): + wipes += 1 + if st.result == "victory": + wins += 1 + hp_left_win.append(st.hp_left) + hp_boss.append(st.hp_at_boss) + elif st.result == "defeat": + var key := "%d.%s" % [st.defeat_node_index, st.defeat_node_type] + death_nodes[key] = int(death_nodes.get(key, 0)) + 1 + var death_txt := "—" + if not death_nodes.is_empty(): + var parts: Array = [] + for k in death_nodes.keys(): + parts.append("%s×%d" % [k, death_nodes[k]]) + death_txt = " ".join(parts) + var mine_mean := _mean(mine_per_run) + if policy == "perfect": + perfect_mine_total += int(round(mine_mean * float(n))) + print("%-6s atk%+3d%% hp%+3d%% %5.0f%% %6.1f / %-6.1f %6.1f %6.1f %6.1f %5.2f %6.1f %5.0f%% %6.1f %6.2f %6.2f %s" % [ + tag, atk_pct, hp_pct, 100.0 * float(wins) / float(n), + _mean(dmg_all), _p90(dmg_all), _mean(hp_left_win), _mean(hp_boss), _mean(rounds_all), _mean(actives_per_battle), + _mean(normal_dmg_pct), 100.0 * float(wipes) / float(maxi(1, normal_battles)), _mean(boss_dmg_pct), mine_mean, _mean(refresh_per_run), death_txt]) + print("(F4 带:承伤 ≤20%%/普通场 · ≤50%%/Boss 场,占生效 max_hp · F5 带:L1 基线通关率 [30,50]%%——2026-09-14 带锚定稿 · 跳战率 = 普通战斗 round-1 即胜(首轮全灭)占比 · 承伤含败者至死 · 败北节点 0-3=战斗节点序 4=Boss · 可选类/战=每战斗实际获取件数期望 · 完美档 = 技能轴上界测量(零盲挖/全标旗/卡死刷新——口径见 auto_player.gd)· 耗时 %.1f 秒)" % [float(Time.get_ticks_msec() - t0) / 1000.0]) + if "perfect" in policies and perfect_mine_total > 0: + push_error("[level_monte_carlo] 完美策略档出现踩雷 %d 次——构造保证被破坏(只挖已证安全格),求解器/驱动有缺陷" % perfect_mine_total) + quit(1) + return + logger.close_session() + quit(0) + + +# 逐战斗段切分:round_index 回卷 = 新战斗(defense_scan 同口径) +# 每段 = {dmg: 英雄承伤合计, r1_win: round-1 即胜(首轮全灭跳战签名——verdict 胜利行 value=1)} +func _damage_segments(rows: Array) -> Array: + var segs: Array = [] + var last_round := 0 + for row in rows: + var r: int = int(row.get("round_index", 0)) + if segs.is_empty() or r < last_round: + segs.append({"dmg": 0, "r1_win": false}) + last_round = r + var seg: Dictionary = segs[segs.size() - 1] + if String(row.get("type", "")) == "damage" and String(row.get("source_class", "")) == "enemy_action": + seg.dmg = int(seg.dmg) + int(row.get("value", 0)) + if String(row.get("type", "")) == "verdict" and r == 1 and int(row.get("value", 0)) == 1: + seg.r1_win = true + return segs + + +func _mean(a: Array) -> float: + if a.is_empty(): + return 0.0 + var t := 0.0 + for v in a: + t += float(v) + return t / float(a.size()) + + +func _p90(a: Array) -> float: + if a.is_empty(): + return 0.0 + var b := a.duplicate() + b.sort() + return float(b[mini(b.size() - 1, int(0.9 * b.size()))]) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd.uid new file mode 100644 index 00000000..b5b3319a --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd.uid @@ -0,0 +1 @@ +uid://h3mghae7yvvx diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd new file mode 100644 index 00000000..5230c8e4 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd @@ -0,0 +1,141 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11 +# 战斗期望蒙特卡洛:当前参数下"胜轮数 / 每轮承伤 / 清图次数 / 开格数"的分布实测。 +# 政策 = 理性玩家近似:击杀有把握即用刀、半血以下用库存治疗包、挖最近可挖格。 +# 运行:Godot_console.exe --headless --path . --script res://smoke/monte_carlo.gd +extends SceneTree + +const N := 30 +const SEED0 := 42 + + +func _initialize() -> void: + var db = load("res://core/config_db.gd").new() + db.load_tables("res://data/tables", false) + for L in [1, 2]: + _run_encounter(db, L) + quit(0) + + +func _run_encounter(db, L: int) -> void: + var rounds_v: Array = [] # 胜利战斗的轮数 + var dmg_per_round: Array = [] # 每战斗 每轮承伤 + var dmg_total: Array = [] + var safe_opened: Array = [] + var clears: Array = [] # 每战斗清图(换新板)次数 + var buff_grants: Array = [] # attack_up+defense_up 授予数 + var losses := 0 + var timeouts := 0 + for i in range(N): + var seed_val: int = SEED0 + i + var r := _run_battle(db, seed_val, L) + if r.result == "victory": + rounds_v.append(r.rounds) + dmg_total.append(r.hero_dmg) + dmg_per_round.append(float(r.hero_dmg) / float(r.rounds)) + safe_opened.append(r.safe_opened) + clears.append(r.board_swaps) + buff_grants.append(r.buffs) + elif r.result == "defeat": + losses += 1 + else: + timeouts += 1 + print("=== L%d · %d 种子(败 %d / 超时 %d)===" % [L, N, losses, timeouts]) + print(" 胜利轮数 均值 %.2f 中位 %d p10 %.1f p90 %.1f 分布 %s" % [_mean(rounds_v), _median(rounds_v), _pct(rounds_v, 0.1), _pct(rounds_v, 0.9), _hist(rounds_v)]) + print(" 每轮承伤 均值 %.2f 中位 %.1f p90 %.1f" % [_mean(dmg_per_round), _median_f(dmg_per_round), _pct_f(dmg_per_round, 0.9)]) + print(" 整场承伤 均值 %.1f 中位 %.0f(主角 max_hp=%d)" % [_mean(dmg_total), _median_f(dmg_total), int(db.hero.max_hp)]) + print(" 开安全格 均值 %.1f 中位 %d" % [_mean(safe_opened), _median(safe_opened)]) + print(" 清图(换板) 均值 %.2f 中位 %d" % [_mean(clears), _median(clears)]) + print(" 攻防buff授予 均值 %.2f" % [_mean(buff_grants)]) + + +func _run_battle(db, seed_val: int, L: int) -> Dictionary: + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var b1 = load("res://core/battle_orchestrator.gd").new(db, rng) + var out := {"rounds": 0, "result": "", "hero_dmg": 0, "safe_opened": 0, "board_swaps": 0, "buffs": 0} + b1.battle_finished.connect(func(payload): out.result = payload.get("result", "")) + b1.board_changed.connect(func(): out.board_swaps += 1) + b1.step_row.connect(func(row): + if row.type == "damage" and row.source_class == "enemy_action": + out.hero_dmg += int(row.value) + elif row.type == "effect" and row.source_class == "safe_cell" and (row.effect_id == "attack_up" or row.effect_id == "defense_up"): + out.buffs += 1) + b1.a2.cell_revealed.connect(func(evt): + if evt.kind == "safe" and evt.channel != "initial": + out.safe_opened += 1) + b1.window_opened.connect(func(items): + var s: Dictionary = b1.presentation_snapshot() # E1 收口:经呈现快照面读英雄/敌况 + var selection: Array = AutoPlayer.window_selection(s, items, + {"heal_hp_div": 2, "round_gate": 0, "knife_compare": "sum"}) # O4:原 monte_carlo 口径 + b1.submit_window_selection(selection)) + if not b1.begin_battle({"level_index": L, "node_depth": 0, "hero_hp": int(db.hero.max_hp)}): + return out + var guard_rounds := 40 + while out.result == "" and guard_rounds > 0: + guard_rounds -= 1 + out.rounds += 1 + var start_round: int = b1.round_index + var a2 = b1.a2 + var guard := 400 + while b1.a3.phase == "Active" and b1.round_index == start_round and guard > 0 and out.result == "": + guard -= 1 + var plan := AutoPlayer.choose_dig(a2) + if plan.is_empty(): + break + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if b1.a3.phase != "Active" or b1.round_index != start_round: + break + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if b1.a3.phase == "Active" and b1.round_index == start_round and out.result == "": + a2.settle_batch([{"action": "stop"}]) + if out.result == "": + out.result = "timeout" + return out + + + +func _mean(a: Array) -> float: + if a.is_empty(): + return 0.0 + var t := 0.0 + for v in a: + t += float(v) + return t / a.size() + + +func _median(a: Array) -> int: + return int(_median_f(a)) + + +func _median_f(a: Array) -> float: + if a.is_empty(): + return 0.0 + var b := a.duplicate() + b.sort() + return float(b[b.size() / 2]) + + +func _pct(a: Array, p: float) -> float: + return _pct_f(a, p) + + +func _pct_f(a: Array, p: float) -> float: + if a.is_empty(): + return 0.0 + var b := a.duplicate() + b.sort() + return float(b[mini(b.size() - 1, int(p * b.size()))]) + + +func _hist(a: Array) -> String: + var h := {} + for v in a: + h[v] = int(h.get(v, 0)) + 1 + var keys := h.keys() + keys.sort() + var parts: Array = [] + for k in keys: + parts.append("%d轮×%d" % [k, h[k]]) + return "{" + ", ".join(parts) + "}" diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd.uid new file mode 100644 index 00000000..3dd0284a --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd.uid @@ -0,0 +1 @@ +uid://dkech7og6a6od diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd new file mode 100644 index 00000000..708a8c16 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd @@ -0,0 +1,89 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11 +# 奖励期望测量(供给控制器批口径):逐深度实测 |资格格|(safe ∖ R0)分布、每板注入件数(j=0 → 恒 1)、 +# 注入种类分布(60/30/10 拍板值)、格子池零可选类验证——走真实 B4.roll_board 管线(含注入判定取号序)。 +# 运行:Godot_console.exe --headless --path . --script res://smoke/reward_expectation.gd +extends SceneTree + +const N := 200 + + +func _initialize() -> void: + var db = load("res://core/config_db.gd").new() + if not db.load_tables("res://data/tables", false): + print("表加载失败") + quit(1) + return + var a1 = load("res://core/board_generator.gd").new() + var b4_factory = load("res://core/reward_economy.gd") + var rng_factory = load("res://core/rng_service.gd") + var active_ids := {"knife_t1": true, "knife_adv": true, "heal_pack": true} + print("=== 奖励期望测量(供给控制器批 · N=%d 板/深度 · 每板全新战斗态 j=0)===" % N) + print("depth | 资格格均/min/max | 注入件数/板 均 | 小刀%% | 精工%% | 治疗包%% | 格池可选类泄漏") + for depth in range(5): + var elig: Array = [] + var act: Array = [] + var leak := 0 + var species := {"knife_t1": 0.0, "knife_adv": 0.0, "heal_pack": 0.0} + for i in range(N): + var rng = rng_factory.new() + rng.set_master_seed(90000 + depth * 1000 + i) + var board = a1.generate(1, depth, rng.open_stream("board_gen"), db) + if board == null: + continue + var r0 := {} + for c in board.compute_r0(): + r0[c] = true + var eligible: Array = [] + for c in board.safe_cells(): + if not r0.has(c): + eligible.append(c) + elig.append(eligible.size()) + var b4 = b4_factory.new(db, rng) + var list: Array = [] + for c in eligible: + list.append({"cell": c, "fog": board.variant_ids(c).size() > 0}) + if not b4.roll_board(list): + continue + var n_active := 0 + for c in eligible: + var snap = b4.query_identity(c) + if snap != null and active_ids.has(String(snap.reward_id)): + n_active += 1 + species[String(snap.reward_id)] += 1.0 + act.append(n_active) + if n_active > 1: + leak += 1 + var total: float = species.knife_t1 + species.knife_adv + species.heal_pack + var kt: float = 100.0 * float(species.knife_t1) / maxf(1.0, total) + var ka: float = 100.0 * float(species.knife_adv) / maxf(1.0, total) + var hp: float = 100.0 * float(species.heal_pack) / maxf(1.0, total) + print("D%d %dx%d | %6.1f / %3d / %3d | %5.2f | %4.0f%% | %4.0f%% | %4.0f%% | 多件板 %d" % [ + depth, 10 if depth <= 1 else 12, 10 if depth <= 1 else 12, + _mean(elig), _min(elig), _max(elig), _mean(act), kt, ka, hp, leak]) + print("(口径:每板全新战斗态 j=0 → 首件保证恒注入 1 件;战斗内后续板的 0.78/板 期望 = 1 ÷ 每战板数折算,") + print(" 实际获取期望由 level_monte_carlo 的\"可选类/战\"列度量——获取以实际挖到入库为准,残余格未挖不计数)") + quit(0) + + +func _mean(a: Array) -> float: + if a.is_empty(): + return 0.0 + var t := 0.0 + for v in a: + t += float(v) + return t / a.size() + + +func _min(a: Array) -> int: + var m := 99999 + for v in a: + m = mini(m, int(v)) + return m + + +func _max(a: Array) -> int: + var m := 0 + for v in a: + m = maxi(m, int(v)) + return m diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd.uid new file mode 100644 index 00000000..29b656c7 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd.uid @@ -0,0 +1 @@ +uid://c2kly46dskpqr diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd new file mode 100644 index 00000000..c5d8fb4b --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd @@ -0,0 +1,821 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) +# Date: 2026-09-10 +# headless 冒烟入口(承 D2 冒烟框架:extends SceneTree + autoload 显式实例化)。 +# 运行:Godot_console.exe --headless --path --script res://smoke/smoke_all.gd +extends SceneTree + +const AutoPlayer := preload("res://smoke/auto_player.gd") # O4 收口:自动玩家策略单一实现 + +var _failures := 0 +var _checks := 0 + + +func check(cond: bool, label: String) -> void: + _checks += 1 + if cond: + print(" [PASS] " + label) + else: + _failures += 1 + print(" [FAIL] " + label) + + +func _initialize() -> void: + print("=== Dungeon Sweeper 垂直切片冒烟 ===") + var db = load("res://core/config_db.gd").new() + var load_ok: bool = db.load_tables("res://data/tables", true) + check(load_ok, "全部表加载 + 校验通过(含跨表护栏)") + if not load_ok: + for d in db.diagnostics(): + if d.severity == "error": + print(" error: [%s] %s" % [d.category, d.message]) + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(42) + # R2 黄金向量(D2 AC-3) + check(rng.derive_at("board_gen", 0) == 2339089373825164293, "R2 向量 board_gen m=42 scope=0") + check(rng.derive_at("board_gen", 1) == 4485977604321908752, "R2 向量 board_gen m=42 scope=1") + check(rng.derive_at("board_gen", 2) == 2024629813010603131, "R2 向量 board_gen m=42 scope=2") + check(rng.derive_at("board_gen", 3) == 8849696722413293638, "R2 向量 board_gen m=42 scope=3") + check(rng.derive_at("reward_roll", 0) == 7422916049646135537, "R2 向量 reward_roll m=42 scope=0") + check(rng.derive_at("board_gen", 0) == rng.derive_at("board_gen", 0), "derive_at 纯函数恒等") + # scope 取号语义:首个 open_stream = scope 0;计数器单调 + var s0 = rng.open_stream("board_gen") + var s1 = rng.open_stream("board_gen") + check(rng.counter("board_gen") == 2, "open_stream 计数器单调 +1 ×2") + check(s0.seed == rng.derive_at("board_gen", 0) and s1.seed == rng.derive_at("board_gen", 1), "首个取号 = scope 0(顺序确定)") + + if load_ok: + _run_core_smoke(db, rng) + + print("=== 冒烟完成:%d 项检查,%d 项失败 ===" % [_checks, _failures]) + quit(1 if _failures > 0 else 0) + + +# ---- Core 集成冒烟(表加载成功才执行)---- +var _db +var _rng +var _last_b1 = null + + +func _run_core_smoke(db, rng) -> void: + _db = db + _rng = rng + _smoke_wall_zero_guard() + _smoke_a1() + _smoke_full_battle(1, 0, "L1 · grub(无雾)") + _smoke_full_battle(2, 0, "L2 · ambusher(雾图)") + _smoke_level() + _smoke_flag_ap_exhausted() + _smoke_supply() + _smoke_power_interval() + _smoke_active_refresh() + _smoke_difficulty_table() + _smoke_damage_types() + _smoke_playtest_logger() + _smoke_encounter_roll() + _smoke_attack_chain_fresh_pool() + + +# ---- 可选类供给控制器冒烟(2026-09-11 供给控制器批:首件保证/指数递减/清图新语义/注入占比)---- + +func _smoke_supply() -> void: + print("--- B4 可选类供给控制器 ---") + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260911) + var b4f = load("res://core/reward_economy.gd") + # 合成资格清单:6 格含 1 雾格(fog=true) + var eligible: Array = [] + for i in range(6): + eligible.append({"cell": Vector2i(i, 0), "fog": i == 0}) + # ① 首件保证 + 每板恰 1 注入 + 注入占比(j=0 恒注入;种子固定 → 分布确定,宽容差断言) + var b4 = b4f.new(_db, rng) + var ok_roll: bool = b4.roll_board(eligible) + check(ok_roll, "roll_board 受理(供给控制器批)") + var species := {"knife_t1": 0, "knife_adv": 0, "heal_pack": 0} + var multi := 0 + for trial in range(400): + var b4t = b4f.new(_db, rng) + if not b4t.roll_board(eligible): + continue + var actives := 0 + for e in eligible: + var snap = b4t.query_identity(e.cell) + if snap != null and (snap.category == "damage_item" or snap.category == "heal_pack"): + actives += 1 + species[snap.reward_id] = float(species[snap.reward_id]) + 1.0 + if actives != 1: + multi += 1 + check(multi == 0, "j=0 时每板恰注入 1 件可选类(400 板,多件板=%d)" % multi) + var total: float = species.knife_t1 + species.knife_adv + species.heal_pack + check(total == 400.0, "注入总量 = 板数(%d)" % int(total)) + check(species.knife_t1 / total > 0.52 and species.knife_t1 / total < 0.68, "小刀占比 ≈60%%(实测 %.0f%%)" % [100.0 * species.knife_t1 / total]) + check(species.knife_adv / total > 0.23 and species.knife_adv / total < 0.37, "精工短刀占比 ≈30%%(实测 %.0f%%)" % [100.0 * species.knife_adv / total]) + check(species.heal_pack / total > 0.05 and species.heal_pack / total < 0.15, "治疗包占比 ≈10%%(实测 %.0f%%——稀缺)" % [100.0 * species.heal_pack / total]) + # ② 格子池零可选类:身份批不含任何可选类身份(j=0 板上唯一可选类 = 注入件——已由①覆盖计数 =1) + # ③ 清图槽新语义:一次性高额伤害(instant_aoe,当场兑付类) + check(b4.grant_clear().category == "instant_aoe", "清图奖励 = 一次性伤害(instant_aoe,非入库道具)") + check(int(b4.grant_clear().power) >= 1, "清图奖励正当量(power=%d)" % int(b4.grant_clear().power)) + # ④ 递减判定:j=1 → ≈p0;j=2 → ≈p0×decay(固定种子统计,宽容差) + var ctrl = load("res://core/reward_supply_controller.gd").new(_db.reward_config) + var raw := RandomNumberGenerator.new() + raw.seed = 777 + ctrl.notify_granted() + var hits1 := 0 + for i in range(2000): + if ctrl.should_inject(raw): + hits1 += 1 + check(absf(float(hits1) / 2000.0 - float(_db.reward_config.active_p0)) < 0.03, + "j=1 注入率 ≈ p0=%.2f(实测 %.3f)" % [float(_db.reward_config.active_p0), float(hits1) / 2000.0]) + ctrl.notify_granted() + var hits2 := 0 + for i in range(2000): + if ctrl.should_inject(raw): + hits2 += 1 + var expect2: float = float(_db.reward_config.active_p0) * float(_db.reward_config.active_decay) + check(absf(float(hits2) / 2000.0 - expect2) < 0.03, + "j=2 注入率 ≈ p0×decay=%.2f(实测 %.3f——指数递减)" % [expect2, float(hits2) / 2000.0]) + # ⑤ 首件保证恒真:j=0 时 2000 次判定全注入 + ctrl.notify_battle_start() + var all_true := true + for i in range(2000): + if not ctrl.should_inject(raw): + all_true = false + check(all_true, "j=0 首件保证恒注入(2000 次判定)") + # ⑥ 发放计数 + 战斗重置 + ctrl.notify_granted() + ctrl.notify_granted() + check(ctrl.granted_count() == 2, "发放计数(%d)" % ctrl.granted_count()) + ctrl.notify_battle_start() + check(ctrl.granted_count() == 0, "战斗开始计数清零(每战重新保底)") + # ⑦ 节点奖励入账回归(playtest-2026-09-12 报告 Bug#3):grant_node_active 此前直调 stock 绕过 + # _supply.notify_granted——发放即入账与格子授予(grant)同一口径,绕过通道已封 + var b4n = b4f.new(_db, rng) + var node_inst = b4n.grant_node_active(Vector2i(-1, -1), 0) + check(node_inst is Dictionary, "节点奖励入库受理(%s)" % str(node_inst.get("item_id", "无"))) + check(b4n.supply_granted_count() == 1, "节点奖励计入供给控制器(granted=%d——Bug#3 回归:发放即入账)" % b4n.supply_granted_count()) + + +# ---- A2 旗动作耗尽末点 AP 回归(playtest 缺陷②——v1 冒烟盲区:自动玩家不用旗)---- + +func _smoke_flag_ap_exhausted() -> void: + print("--- A2 旗动作耗尽末点 AP 回归 ---") + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260911) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + var rounds: Array = [] + b1.round_started.connect(func(ri): rounds.append(ri)) + b1.window_opened.connect(func(items): b1.submit_window_selection([])) # 结算窗口立即应答 + check(b1.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "回归战斗启动(Boss 行——D0 20HP 级会被盲挖粉尘秒杀,无法承载旗回归场景)") + var a2 = b1.a2 + # 恰花 7 点 AP 挖掘,留末点给旗 + for i in range(7): + if b1.a3.phase != "Active" or b1.a3.ap == 0: + break + var plan = AutoPlayer.choose_dig(a2) + if plan.is_empty(): + break + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + check(b1.a3.phase == "Active" and b1.a3.ap == 1, "7 次挖掘后恰剩 1 AP(phase=%s ap=%d)" % [str(b1.a3.phase), b1.a3.ap]) + # 末点 AP 花在插旗——轮次必须终结并结算进入第 2 轮 + # 旗目标 = 挖掘策略的下一目标格(构造保证:未开、与小人邻接——先把小人移到其邻接已开格) + var plan2 = AutoPlayer.choose_dig(a2) + check(not plan2.is_empty(), "仍存在可挖目标(旗目标来源)") + var out: Array = [] + if not plan2.is_empty(): + if plan2.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan2.hero_cell}]) + out = a2.settle_batch([{"action": "flag_place", "cell": plan2.dig_cell}]) + check(not out.is_empty() and bool(out[0].accepted), "末点 AP 插旗被受理") + check(rounds.size() >= 2, "旗动作耗尽 AP 触发轮末结算并进入第 2 轮(rounds=%s)" % str(rounds)) + + +# ---- C1 关卡线性推进冒烟(2026-09-11 关卡扩展批)---- + +func _smoke_level() -> void: + print("--- C1 关卡线性推进(战×2→道具→战×2→恢复→Boss)---") + var autoplay = load("res://smoke/level_autoplay.gd") + # ① 满配通关路径(atk+200%/hp+100%——测试面板同源的属性覆盖) + var win = autoplay.run_level(_db, 42, 1, 200, 100) + check(win.result == "victory", "关卡·满配(atk+200/hp+100)通关(实测:%s @节点 %d)" % [win.result, win.defeat_node_index]) + check(win.battles == 5, "战斗节点数 = 5(战×4 + Boss)") + check(win.boss_depth == 4, "Boss 节点以 Dmax=4 解析(A1 第三轴登记约定)") + check(win.reward_snapshot.has("reward_id") and (win.reward_snapshot.category == "damage_item" or win.reward_snapshot.category == "heal_pack"), + "奖励点 rolled 可选类主动道具(%s)" % str(win.reward_snapshot.get("reward_id", "无"))) + check(win.remaining_items >= 0, "关卡结束 level_clear 快照计数返回(清库前 %d 件随关卡结束清空)" % win.remaining_items) + check(win.actives_total >= 1 and win.actives_total <= win.battles + 1, + "可选类获取 ∈ [1, 战斗数+奖励点](实测 %d 件/%d 战——供给控制器批口径)" % [win.actives_total, win.battles]) + check(win.hp_at_boss >= 1 and win.hp_at_boss <= int(_db.hero.max_hp) * 3, "进 Boss HP 携带在域内(%d)" % win.hp_at_boss) + # ② 恢复点:回复 = min(floor(生效max_hp×pct/100), 缺口)——hp+100% 档生效 max = 基线×2 + var rest = autoplay.run_level(_db, 4242, 1, 100, 100) + if rest.result != "start_failed" and rest.hp_before_rest >= 0: + var eff_max := maxi(1, int(round(float(_db.hero.max_hp) * 2.0))) # hp_pct=100(run_level 已恢复基线) + var expect: int = mini(int(floor(float(eff_max) * 0.3)), eff_max - rest.hp_before_rest) + check(rest.rest_healed == expect, "恢复点回复 = floor(生效max_hp×30%%) 与缺口的较小值(%d/%d,生效 max %d)" % [rest.rest_healed, expect, eff_max]) + else: + check(false, "恢复点未到达(seed 4242 hp+100 在恢复点前败北?%s)" % rest.result) + # ③ 裸装败北路径(atk1/hp10——Roguelike 基线应难以通关) + var lose = autoplay.run_level(_db, 42, 1, -80, -80) + check(lose.result == "defeat", "关卡·裸装(atk≈1/hp≈10)败北路径可达(实测:%s)" % lose.result) + check(lose.defeat_node_index >= 0 and lose.defeat_node_index < 7, "败北节点记录在域内(节点 %d)" % lose.defeat_node_index) + # ④ 回放确定性:同种子两遍步骤流逐行相同 + var rep1 = autoplay.run_level(_db, 777, 1, 100, 100, true) + var rep2 = autoplay.run_level(_db, 777, 1, 100, 100, true) + check(JSON.stringify(rep1.rows) == JSON.stringify(rep2.rows), "关卡回放断言:同种子两遍结算步骤流逐行相同") + check(rep1.rows.size() > 0, "关卡步骤流非空(%d 行)" % rep1.rows.size()) + # ⑤ 属性覆盖恢复:模拟后 hero 表回基线(调整板不泄漏到后续会话) + check(int(_db.hero.atk) == 6 and int(_db.hero.max_hp) == 50, "模拟后 hero 表恢复基线(atk=%d hp=%d)" % [int(_db.hero.atk), int(_db.hero.max_hp)]) + + +# ---- 减伤墙归零护栏(2026-09-13 用户拍板:墙语义维持现状 + 护栏——BUG-0003 事故类永久免疫)---- +# 表级不变式:2×defense_up.magnitude > pool_clamp_lo。违反 = 叠满两层触池底 = F2 乘区 ×0 全额免伤 +# (playtest-2026-09-12 真人局 12/12 轮零承伤根因)。任何未来 magnitude/clamp 调参重新造出 ×0 墙在此红灯, +# 强制显式重审(含 clamp 收紧等结构变更——本护栏随之需要有意修订)。 + +func _smoke_wall_zero_guard() -> void: + print("--- B3 减伤墙归零护栏 ---") + var def_row: Dictionary = _db.status_effect_row("defense_up") + var wall_floor: int = int(def_row.get("magnitude", 0)) * int(def_row.get("max_stack", 1)) + var clamp_lo: int = int(_db.settlement.pool_clamp_lo) + check(wall_floor > clamp_lo, "墙最深池 %d > pool_clamp_lo %d → 叠满不可归零(最低残余 ×%.2f)" % [ + wall_floor, clamp_lo, float(100 + wall_floor) / 100.0]) + + +func _smoke_a1() -> void: + print("--- A1 棋盘生成 ---") + var a1 = load("res://core/board_generator.gd").new() + for L in [1, 2]: + var row = _db.resolve_board_gen(L, 0) + check(not row.is_empty(), "BoardGen 行解析 (%d,0)" % L) + var b1 = a1.generate(L, 0, _rng.open_stream("board_gen"), _db) + check(b1 != null, "L%d 生成产物非空" % L) + if b1 == null: + continue + check(a1.is_clearable(b1), "L%d clearable(安全格 8 邻连通)" % L) + check(a1.legality(b1, row, _db), "L%d legality 全过" % L) + check(b1.metrics.get("inferability", 0.0) >= float(row.inferability_target), "L%d inferability %.3f ≥ target %.2f" % [L, b1.metrics.get("inferability", 0.0), float(row.inferability_target)]) + check(b1.metrics.get("inferability_core", 0.0) >= float(row.inferability_target), "L%d inferability_core %.3f ≥ target(双判据)" % [L, b1.metrics.get("inferability_core", 0.0)]) + check(b1.metrics.get("attempts_used", 99) >= 1 and b1.metrics.get("attempts_used", 99) <= int(row.max_gen_attempts), "L%d attempts_used ∈ [1,%d]" % [L, int(row.max_gen_attempts)]) + check(not b1.illegal_product, "L%d 非法产物标记 = false" % L) + # 生成确定性:同种子逐格相同 + var b2 = a1.generate(L, 0, _mk_stream("board_gen", 777), _db) + var b3 = a1.generate(L, 0, _mk_stream("board_gen", 777), _db) + check(_board_dump(b2) == _board_dump(b3), "L%d 同种子两次生成逐格相同" % L) + var b4 = a1.generate(L, 0, _mk_stream("board_gen", 778), _db) + check(_board_dump(b2) != _board_dump(b4), "L%d 异种子产物不同(退化护栏)" % L) + + +func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator: + var rg := RandomNumberGenerator.new() + rg.seed = _rng.derive_at(stream_name, scope) + return rg + + +func _board_dump(b) -> String: + var parts: Array = [str(b.width), str(b.height), str(b.spawn_center)] + for y in b.height: + for x in b.width: + var c := Vector2i(x, y) + parts.append("%d:%d:%d:%s" % [x, y, 1 if b.is_mine(c) else 0, str(b.variants.get(c, []))]) + return "\n".join(parts) + + +# ---- 完整战斗回放(自动玩家,确定性)——回放断言:同种子两遍步骤流逐行相同 ---- + +func _smoke_full_battle(L: int, D: int, label: String) -> void: + print("--- 完整战斗回放:%s ---" % label) + var run1 = _run_battle(L, D) + var run2 = _run_battle(L, D) + check(run1.terminated, "%s 战斗在轮次上限内终局(%d 轮,%s)" % [label, run1.rounds, run1.result]) + if JSON.stringify(run1.stream) != JSON.stringify(run2.stream): + for i in range(maxi(run1.stream.size(), run2.stream.size())): + var a := JSON.stringify(run1.stream[i]) if i < run1.stream.size() else "" + var b := JSON.stringify(run2.stream[i]) if i < run2.stream.size() else "" + if a != b: + print(" 流差异 @%d:\n run1=%s\n run2=%s" % [i, a, b]) + break + check(JSON.stringify(run1.stream) == JSON.stringify(run2.stream), "%s 回放断言:结算步骤流逐行相同" % label) + check(JSON.stringify(run1.events) == JSON.stringify(run2.events), "%s 回放断言:事件流逐位相同" % label) + check(run1.stream.size() > 0, "%s 结算步骤流非空(%d 行)" % [label, run1.stream.size()]) + var has_verdict := false + var hero_attacks := 0 + var hero_breakdown_ok := true + for row in run1.stream: + if row.get("type", "") == "verdict": + has_verdict = true + if row.get("source_class", "") == "hero_attack": + hero_attacks += 1 + # 三段伤害构成行(伤害×护甲克制批):hero_attack 行必须携带 base/dmg_a/dmg_b 分量(P2 归因) + if not (row.has("base") and row.has("dmg_a") and row.has("dmg_b")): + hero_breakdown_ok = false + check(has_verdict, "%s 步骤流含判定行" % label) + check(hero_attacks >= 1, "%s 主角先手攻击行存在(2026-09-11 拍板①)×%d" % [label, hero_attacks]) + check(hero_breakdown_ok, "%s 主角攻击行携带三分量 base/dmg_a/dmg_b(克制批)" % label) + if run1.terminated and run1.result == "victory": + var lc = _last_b1.level_clear() # 拍板③:关卡结束清库路径(战斗域不再清) + check(lc.has("remaining"), "%s level_clear 返回快照计数(剩余 %d)" % [label, int(lc.get("remaining", -1))]) + + +func _run_battle(L: int, D: int) -> Dictionary: + # 每遍全新 RngService(同 master 种子)——回放断言的隔离前提(B4 AC"两套全新 RngService+B4"同款) + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(42) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + var out := {"stream": [], "events": [], "rounds": 0, "result": "", "terminated": false} + b1.step_row.connect(func(row): out.stream.append(row)) + b1.battle_finished.connect(func(payload): out.result = payload.get("result", "")) + b1.window_opened.connect(func(items): + # 窗口数据契约断言:UI 读取的全部字段必须齐备(防呈现层中途崩溃把游戏锁死) + for it in items: + for key in ["index", "item_id", "display_name", "category", "power", "source_cell", "round_index", "usable_now"]: + if not it.has(key): + print(" [FAIL] 窗口条目缺字段 %s" % key) + _failures += 1 + # 第 2 轮结算期发起玩家刷新(exercising request_refresh → new_board 编排链) + if b1.round_index == 2: + b1.player_request_refresh() + # 确定性策略(窗口至多一件——2026-09-11 用户拍板): + # 半血以下优先用治疗包;否则第 3 轮起且最强单刀 ≥ 敌剩余总 HP(击杀有把握)时用该刀;否则跳过 + var s: Dictionary = b1.presentation_snapshot() # E1 收口:经呈现快照面读英雄/敌况(禁直读 b2) + var selection: Array = AutoPlayer.window_selection(s, items) # O4:策略单一实现(div=3/gate=3/best 默认口径) + b1.submit_window_selection(selection)) + b1.a2.cell_revealed.connect(func(evt): out.events.append(evt)) + b1.a2.flag_judged.connect(func(evt): out.events.append(evt)) + b1.a2.round_end_board.connect(func(p): out.events.append({"ev": "round_end_board", "path_tag": p.path_tag})) + b1.a3.round_end_stopped.connect(func(p): out.events.append({"ev": "round_end_stopped", "path_tag": p.path_tag})) + var a2 = b1.a2 + var a3 = b1.a3 + _last_b1 = b1 + if not b1.begin_battle({"level_index": L, "node_depth": D, "hero_hp": _db.hero.max_hp}): + out.stream.append({"type": "verdict", "value": "begin_failed"}) + return out + var max_rounds := 40 + while out.result == "" and out.rounds < max_rounds: + out.rounds += 1 + _auto_play_round(b1, out) + if out.result == "": + out.result = "timeout" + out.terminated = out.result == "victory" or out.result == "defeat" + return out + + +func _auto_play_round(b1, out: Dictionary) -> void: + var a2 = b1.a2 + var a3 = b1.a3 + var start_round: int = b1.round_index + # 确定性自动玩家:挖最近的已探明邻接未开格;无可挖则停手(结算窗口由信号回调提交) + var guard := 400 + while a3.phase == "Active" and b1.round_index == start_round and guard > 0 and out.result == "": + guard -= 1 + var plan := AutoPlayer.choose_dig(a2) + if plan.is_empty(): + break + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if a3.phase != "Active" or b1.round_index != start_round: + break + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if a3.phase == "Active" and b1.round_index == start_round and out.result == "": + a2.settle_batch([{"action": "stop"}]) + + + +# ---- B4 伤害量值区间批冒烟(2026-09-14:身份批 roll 域内 + 中点锚 + 变异实际流动 + 定值行不受扰)---- + +func _smoke_power_interval() -> void: + print("--- B4 伤害量值区间(上阶/下阶随机)---") + # ① 表侧静态锚:五行配对齐全、power = 中点(A3 条款①期望当量锚) + var interval_rows := {"heal_minor": [2, 6], "knife_t1": [5, 11], "heal_pack": [6, 18], "knife_adv": [10, 22], "clear_blast": [25, 55]} + var anchors_ok := true + for row in _db.reward.rows: + var rid := String(row.reward_id) + if interval_rows.has(rid): + var lo: int = int(row.get("power_min", -1)) + var hi: int = int(row.get("power_max", -1)) + if lo != interval_rows[rid][0] or hi != interval_rows[rid][1] or int(row.power) != (lo + hi) / 2: + anchors_ok = false + elif row.has("power_min") or row.has("power_max"): + anchors_ok = false # 区间配对只许五行(buff/spark_aoe 定值) + check(anchors_ok, "五行区间域与中点锚在位(其余行零区间字段)") + # ② 运行侧:24 板 roll——区间行快照 power 全域内 + 至少上下阶都出现过 + 定值行不受扰 + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260914) + var b4f = load("res://core/reward_economy.gd") + var eligible: Array = [] + for i in range(6): + eligible.append({"cell": Vector2i(i, 0), "fog": false}) + var seen := {"heal_minor": {}, "knife_t1": {}, "heal_pack": {}, "knife_adv": {}, "clear_blast": {}} # rid → {roll 值集} + var in_domain := true + var fixed_ok := true + for trial in range(24): + var b4 = b4f.new(_db, rng) + if not b4.roll_board(eligible): + continue + for e in eligible: + var snap: Dictionary = b4.query_full_snapshot(e.cell) + var rid := String(snap.reward_id) + if not seen.has(rid): + seen[rid] = {} + if interval_rows.has(rid): + var v := int(snap.power) + seen[rid][v] = true + if v < interval_rows[rid][0] or v > interval_rows[rid][1]: + in_domain = false + elif int(snap.power) != _row_power(rid): + fixed_ok = false # 定值行快照 power 必须仍是表值 + var cs: Dictionary = b4.grant_clear() + var cv := int(cs.power) + seen["clear_blast"][cv] = true + if cv < 25 or cv > 55: + in_domain = false + check(in_domain, "24 板全区间行快照 power ∈ [power_min, power_max](含清图槽)") + check(fixed_ok, "定值行(buff/spark_aoe)快照 power 恒等表值——区间批零外溢") + var variance_ok: bool = seen.has("knife_t1") and int(seen["knife_t1"].size()) >= 2 \ + and seen.has("clear_blast") and int(seen["clear_blast"].size()) >= 2 \ + and seen.has("heal_minor") and int(seen["heal_minor"].size()) >= 2 + check(variance_ok, "上阶/下阶实际流动(knife_t1/clear_blast/heal_minor 各 ≥2 个不同 roll 值)") + # ③ 节点奖励路径:grant_node_active 同样 roll(域内) + var b4n = b4f.new(_db, rng) + var node_ok := true + for trial in range(8): + var inst = b4n.grant_node_active(Vector2i(-1, -1), 0) + var rid := String(inst.item_id) + if interval_rows.has(rid): + var v := int(inst.snapshot.power) + if v < interval_rows[rid][0] or v > interval_rows[rid][1]: + node_ok = false + check(node_ok, "grant_node_active 区间行 roll 域内(节点奖励路径)") + + +func _row_power(rid: String) -> int: + for row in _db.reward.rows: + if String(row.reward_id) == rid: + return int(row.power) + return -1 + + + + +# ---- A3 轮内刷新冒烟(2026-09-12 方案 A 用户拍板:Active 相位当场换图继续挖)---- + +func _smoke_active_refresh() -> void: + print("--- A3 轮内刷新(方案 A)---") + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260912) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + # 计数器用容器(GDScript lambda 对局部 int 按值捕获——int 计数在 lambda 内加无效) + var ct := {"board_swaps": 0, "safe_events": 0, "safe_rows": 0} + b1.board_changed.connect(func(): ct["board_swaps"] += 1) + b1.a2.cell_revealed.connect(func(evt): + if evt.kind == "safe" and evt.channel != "initial": + ct["safe_events"] += 1) + b1.window_opened.connect(func(items): b1.submit_window_selection([])) + b1.step_row.connect(func(row): + if row.source_class == "safe_cell": + ct["safe_rows"] += 1) # 结算流中 safe_cell 来源行(含兑付/未生效——每格必有行) + check(b1.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "轮内刷新战斗启动(Boss 行 150HP——承载多轮样本)") + var a2 = b1.a2 + # 轮 1–3:零挖掘停手攒 RAP 24(每轮 8 AP 全转存) + for i in range(3): + a2.settle_batch([{"action": "stop"}]) + check(b1.round_index == 4 and b1.a3.rap == 24, "三轮停手攒 RAP=24(round=%d rap=%d)" % [b1.round_index, b1.a3.rap]) + check(b1.a3.phase == "Active", "第 4 轮 Active 相位") + # 挖 1 格 → 刷 → 挖 1 格 → 刷 → 挖 1 格 → 第 3 刷拒(上限) + var plan = AutoPlayer.choose_dig(a2) + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + var ap_before: int = b1.a3.ap + var board_before = a2.current_board() + var swaps_before: int = ct["board_swaps"] + var ok1: bool = b1.player_request_refresh() + check(ok1, "轮内第 1 次刷新受理") + check(b1.a3.phase == "Active" and b1.a3.ap == ap_before, "相位保持 Active、AP 保留不重置(ap=%d)" % b1.a3.ap) + check(b1.a3.rap == 16, "RAP -8(%d)" % b1.a3.rap) + check(int(ct["board_swaps"]) == swaps_before + 1 and a2.current_board() != board_before, "当场换新图(board_changed +1)") + var plan2 = AutoPlayer.choose_dig(a2) + if plan2.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan2.hero_cell}]) + a2.settle_batch([{"action": "dig", "cell": plan2.dig_cell}]) + var ok2: bool = b1.player_request_refresh() + check(ok2, "轮内第 2 次刷新受理(RAP=%d)" % b1.a3.rap) + var plan3 = AutoPlayer.choose_dig(a2) + if plan3.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan3.hero_cell}]) + a2.settle_batch([{"action": "dig", "cell": plan3.dig_cell}]) + var ok3: bool = b1.player_request_refresh() + check(not ok3, "第 3 次刷新被拒(每轮兜底上限 2——RAP %d 足额仍拒)" % b1.a3.rap) + # 跨图 facts 完整兑现:拒绝后挖到 AP 耗尽自动结算(扩大跨图样本——三段挖掘分属三张图) + var dig_guard := 20 + while b1.a3.phase == "Active" and b1.round_index == 4 and dig_guard > 0: + dig_guard -= 1 + var p = AutoPlayer.choose_dig(a2) + if p.is_empty(): + a2.settle_batch([{"action": "stop"}]) + break + if p.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": p.hero_cell}]) + a2.settle_batch([{"action": "dig", "cell": p.dig_cell}]) + check(b1.round_index == 5 and b1.battle_result == "", "轮 4 挖至 AP 耗尽自动结算(round=%d result='%s')" % [b1.round_index, b1.battle_result]) + check(int(ct["safe_rows"]) == int(ct["safe_events"]), "跨图 facts 完整兑现(safe_cell 结算行 %d == safe 事件 %d——快照批次隔离)" % [int(ct["safe_rows"]), int(ct["safe_events"])]) + check(int(ct["safe_events"]) >= 3, "跨图挖掘 safe 事件 ≥3(%d——断言非空转)" % int(ct["safe_events"])) + # 回放确定性:同种子两遍步骤流逐行相同(含两次轮内刷新取号序) + var rep1 = _active_refresh_replay() + var rep2 = _active_refresh_replay() + check(JSON.stringify(rep1) == JSON.stringify(rep2), "轮内刷新回放断言:同种子两遍步骤流逐行相同") + check(rep1.size() > 0, "轮内刷新步骤流非空(%d 行)" % rep1.size()) + + +func _active_refresh_replay() -> Array: + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260912) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + var rows: Array = [] + b1.step_row.connect(func(row): rows.append(row)) + b1.window_opened.connect(func(items): b1.submit_window_selection([])) + if not b1.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}): + return rows + var a2 = b1.a2 + for i in range(3): + a2.settle_batch([{"action": "stop"}]) + for attempt in range(3): + var plan = AutoPlayer.choose_dig(a2) + if not plan.is_empty(): + if plan.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + b1.player_request_refresh() + a2.settle_batch([{"action": "stop"}]) + return rows + + +# ---- C3 DifficultyTable 结构位冒烟(AC-10:空表零行为 + 套乘点 = 行值快照前)---- + +func _smoke_difficulty_table() -> void: + print("--- C3 DifficultyTable 结构位 ---") + check(_db.difficulty != null and _db.difficulty.rows.is_empty(), "DifficultyTable MVP 空表加载(结构位)") + # ① 空表零行为:B2.spawn 敌值 = EnemyTable 行值原样 + var b2 = load("res://core/unit_container.gd").new(_db) + check(b2.spawn(1, 0, 50), "spawn 受理") + var grub = b2.all_enemies()[0] + check(int(grub.hp) == 24 and int(grub.atk) == 7, "空表零修饰(hp=%d atk=%d = v3 行值 24/7)" % [int(grub.hp), int(grub.atk)]) + # ② 修饰行运行时注入(debug 通道——R3 override 同族):套乘生效于 hp/atk + _db.difficulty.rows.append({"level_index": 1, "node_depth": 0, "hp_mult": 1.5, "atk_mult": 2.0}) + var b2b = load("res://core/unit_container.gd").new(_db) + check(b2b.spawn(1, 0, 50), "修饰后 spawn 受理") + var g2 = b2b.all_enemies()[0] + check(int(g2.hp) == 36 and int(g2.max_hp) == 36, "hp 修饰 ×1.5(24→%d,max_hp 同步)" % int(g2.hp)) + check(int(g2.atk) == 14, "atk 修饰 ×2.0(7→%d)" % int(g2.atk)) + # ③ 越域 clamp:乘数 50 → clamp 10 + _db.difficulty.rows.clear() + _db.difficulty.rows.append({"level_index": 1, "node_depth": 0, "hp_mult": 50.0, "atk_mult": 1.0}) + var b2c = load("res://core/unit_container.gd").new(_db) + check(b2c.spawn(1, 0, 50), "越域修饰 spawn 受理") + check(int(b2c.all_enemies()[0].hp) == 240, "越域乘数 clamp 至 10(24→%d)" % int(b2c.all_enemies()[0].hp)) + _db.difficulty.rows.clear() # 还原空表(后续冒烟/关卡断言基线) + + +# ---- 伤害类型 × 护甲克制 + 标错惩罚冒烟(2026-09-12 克制批:护甲快照/克制矩阵/充能/反噬三选一)---- + +func _smoke_damage_types() -> void: + print("--- 伤害类型 × 护甲克制 + 标错惩罚 ---") + # ① 护甲类型快照传播(spawn 行值 → 单位字典) + var b2 = load("res://core/unit_container.gd").new(_db) + check(b2.spawn(1, 0, 50) and String(b2.all_enemies()[0].armor_type) == "none", "goblin 护甲 = none(教学位无甲)") + var b2b = load("res://core/unit_container.gd").new(_db) + check(b2b.spawn(1, 2, 50) and String(b2b.all_enemies()[0].armor_type) == "a", "bugbear 护甲 = a") + var b2c = load("res://core/unit_container.gd").new(_db) + check(b2c.spawn(1, 4, 50) and String(b2c.all_enemies()[0].armor_type) == "b", "ogre_chief 护甲 = b") + # ② 克制矩阵表值在位(三角:特化 150 / 中性 100 / 对位 50) + var am: Dictionary = _db.settlement.armor_effectiveness + check(int(am.dmg_a.a) == 150 and int(am.dmg_a.b) == 50 and int(am.dmg_a.none) == 100, "伤害A 克制行:甲A 150 / 无甲 100 / 甲B 50") + check(int(am.dmg_b.b) == 150 and int(am.dmg_b.a) == 50 and int(am.dmg_b.none) == 100, "伤害B 克制行:甲B 150 / 无甲 100 / 甲A 50") + check(int(am.base.none) == 100 and int(am.base.a) == 100 and int(am.base.b) == 100, "基础伤害全护甲中性 100(不吃克制)") + # ③ 标错惩罚 + 标雷充能(Boss 行 150HP 多轮样本;板真相直读保证 hit/miss 确定性) + var run1 = _run_flag_backlash_round(20260912) + var miss_rows := 0 + var backlash_rows := 0 + var dmg_b_round := -1 + var dmg_b_seen := 0 + for row in run1.rows: + if row.get("type", "") == "flag_miss": + miss_rows += 1 + if row.get("type", "") == "effect" and String(row.get("source_class", "")) == "flag_miss": + backlash_rows += 1 # 反噬效果行(暴走/伏击/失衡三选一) + if row.get("source_class", "") == "hero_attack" and int(row.get("dmg_b", 0)) >= 1: + dmg_b_round = int(row.get("round_index", -1)) + dmg_b_seen = int(row.get("dmg_b", 0)) + check(miss_rows >= 1, "标错旗产生 flag_miss 行(×%d)" % miss_rows) + check(backlash_rows >= 1, "标错旗触发随机反噬效果行(×%d——敌增伤/敌buff/我方debuff 三选一)" % backlash_rows) + check(dmg_b_round >= 1, "标中雷充能进主角攻击(dmg_b=%d @R%d——每雷实例一次)" % [dmg_b_seen, dmg_b_round]) + # ④ 同种子回放(确定性派生反噬——零新流同种子逐行相同) + var run2 = _run_flag_backlash_round(20260912) + check(JSON.stringify(run1.rows) == JSON.stringify(run2.rows), "标错惩罚轮回放断言:同种子步骤流逐行相同(反噬派生确定性)") + + +# 构造轮:标 1 面"安全格旗"(必 miss → 反噬)+ 标 1 面"雷旗"(必 hit → 充能)后停手收轮 +func _run_flag_backlash_round(seed_val: int) -> Dictionary: + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + var rows: Array = [] + b1.step_row.connect(func(row): rows.append(row)) + b1.window_opened.connect(func(items): b1.submit_window_selection([])) + if not b1.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}): + return {"rows": rows} + var a2 = b1.a2 + var safe_t := _find_flag_target(a2, false) + var mine_t := _find_flag_target(a2, true) + if not safe_t.is_empty(): + if safe_t.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": safe_t.hero_cell}]) + a2.settle_batch([{"action": "flag_place", "cell": safe_t.flag_cell}]) + if not mine_t.is_empty(): + if mine_t.hero_cell != a2.hero_pos(): + a2.settle_batch([{"action": "move", "cell": mine_t.hero_cell}]) + a2.settle_batch([{"action": "flag_place", "cell": mine_t.flag_cell}]) + a2.settle_batch([{"action": "stop"}]) + return {"rows": rows} + + +# 板真相直读找旗目标:未开 + 指定雷/安全属性 + 存在可达已开邻格(测试允许读板真相——A2 冒烟同口径) +func _find_flag_target(a2, want_mine: bool) -> Dictionary: + var revealed: Array = a2.revealed_cells() + var revealed_set := {} + for c in revealed: + revealed_set[c] = true + for y in a2.current_board().height: + for x in a2.current_board().width: + var c := Vector2i(x, y) + if a2.is_revealed(c) or a2.has_flag(c): + continue + if a2.current_board().is_mine(c) != want_mine: + continue + for n in a2.current_board().neighbors8(c): + if revealed_set.has(n) and (a2.compute_move_path(n).size() > 0 or n == a2.hero_pos()): + return {"flag_cell": c, "hero_cell": n} + return {} + + +# ---- 试玩日志器冒烟(结算快照 JSONL + 诊断转储)---- +func _smoke_playtest_logger() -> void: + print("--- PlaytestLogger(结算快照 JSONL + 诊断转储)---") + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260912) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + var logger = load("res://presentation/playtest_logger.gd").new() + var path: String = logger.start_session(20260912) + logger.attach_battle(b1) + var snaps: Array = [] + b1.settlement_snapshot.connect(func(snap): snaps.append(snap)) + check(path.contains("playtest_20260912"), "会话文件开启(%s)" % path) + check(b1.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 50}), "快照战斗启动") + var a2 = b1.a2 + for i in range(2): + a2.settle_batch([{"action": "stop"}]) + check(snaps.size() == 2, "每结算一快照(%d 次结算 → %d 快照)" % [2, snaps.size()]) + var s1: Dictionary = snaps[0] + var required := ["kind", "level", "depth", "round", "verdict", "hero", "economy", "board", "enemies", "buffs", "ambush_layers", "inventory_in_stock", "round_facts", "spawn_count"] + var missing := [] + for k in required: + if not s1.has(k): + missing.append(k) + check(missing.is_empty(), "快照字段齐备(缺 %s)" % str(missing)) + check(s1.hero.max_hp == 50 and s1.economy.has("phase") and s1.board.has("seq"), "快照含英雄/经济/板序号(板 seq=%d)" % int(s1.board.seq)) + # spawn_count 回归(playtest-2026-09-12 报告 Bug#1):停手两轮零击杀(主角攻击 6×2 < 24 HP 确定)→ + # unit_count 必须 == spawn_count(全灭才允许 <);快照级区分「首轮全灭」vs「空场」的数据面在位 + check(int(s1.get("spawn_count", -1)) >= 1 and int(s1.get("spawn_count", -1)) == int(s1.get("unit_count", -2)), + "快照 spawn_count 在位且 == unit_count(无击杀轮:%d/%d——全灭/空场可判别)" % [int(s1.get("spawn_count", -1)), int(s1.get("unit_count", -1))]) + logger.log_action("dig", Vector2i(1, 1), true) + var diag: String = logger.dump_diagnosis({"test": true}) + check(diag.contains("diag_"), "诊断转储文件生成(%s)" % diag) + # JSONL 文件内容验证(重新读取) + var f := FileAccess.open(path, FileAccess.READ) + var lines := 0 + var parse_ok := true + while not f.eof_reached(): + var line := f.get_line() + if line.strip_edges() == "": + continue + lines += 1 + var parsed = JSON.parse_string(line) + if parsed == null: + parse_ok = false + check(lines >= 3 and parse_ok, "JSONL 全部行可解析(%d 行 = session + 2 结算)" % lines) + logger.close_session() + + +# ---- 遭遇随机化冒烟(C3 Core Rule 8:区间骰/encounter 流/可回放/空表定值路径)---- + +func _smoke_encounter_roll() -> void: + print("--- 遭遇随机化(unit_count 区间骰)---") + var counts: Array = [] + for i in range(20): + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(30000 + i) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + check_silent(b1.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 50})) + counts.append(b1.presentation_snapshot().enemies.size()) + # 区间生效:20 个种子应出现 1 和 2(D0 [1,2]) + check(counts.has(1) and counts.has(2), "D0 区间 [1,2] 两值均出现(分布 %s)" % str(counts)) + # 域内断言带标签(原恒真 check(true) 已废——断言必须能抓住失效,code-review 2026-09-12) + var all_in_domain := true + for c in counts: + if c < 1 or c > 2: + all_in_domain = false + check(all_in_domain, "全部计数在域内 [1,2](分布 %s)" % str(counts)) + # 同种子可回放:两遍同数量(encounter 流确定性) + var r1 := _encounter_replay(30000) + var r2 := _encounter_replay(30000) + check(r1 == r2, "同种子遭遇骰可回放(%s == %s)" % [str(r1), str(r2)]) + # D1 [2,3]:seed 30005 路径(任意种子域内即可) + var rng3 = load("res://core/rng_service.gd").new() + rng3.set_master_seed(31000) + var b13 = load("res://core/battle_orchestrator.gd").new(_db, rng3) + check(b13.begin_battle({"level_index": 1, "node_depth": 1, "hero_hp": 50}), "D1 战斗启动") + var n1: int = b13.presentation_snapshot().enemies.size() + check(n1 >= 2 and n1 <= 3, "D1 区间 [2,3] 域内(%d 只)" % n1) + # D2 固定(教学位不加骰) + var rng4 = load("res://core/rng_service.gd").new() + rng4.set_master_seed(31000) + var b14 = load("res://core/battle_orchestrator.gd").new(_db, rng4) + b14.begin_battle({"level_index": 1, "node_depth": 2, "hero_hp": 50}) + check(b14.presentation_snapshot().enemies.size() == 1, "D2 熊地精固定 1(首遇教学位)") + # 全深度 × 多种子域内扫描(playtest-2026-09-12 报告 Bug#1 回归——区间下限 1 不可掷 0: + # B1 roll_lo 下限 + B2 spawn count<1 fail-fast 双闸;单深度单种子是盲区,全域扫描才钉死不变式) + var depths: Array = [] + for node in _level_battle_nodes(1): + depths.append(int(node.get("node_depth", -1))) + var scan_bad := 0 + var scan_total := 0 + for i in range(12): + var rngs = load("res://core/rng_service.gd").new() + rngs.set_master_seed(32000 + i) + for depth_v in depths: + scan_total += 1 + var b1s = load("res://core/battle_orchestrator.gd").new(_db, rngs) + if not b1s.begin_battle({"level_index": 1, "node_depth": int(depth_v), "hero_hp": 50}): + scan_bad += 1 + continue + var n: int = b1s.presentation_snapshot().enemies.size() + var erow2: Dictionary = _db.resolve_enemy_rowset(1, int(depth_v)) + var lo2: int = int(erow2.get("unit_count", 1)) + var hi2: int = int(erow2.get("unit_count_max", lo2)) + if n < 1 or n < lo2 or n > hi2: + scan_bad += 1 + check(scan_bad == 0, "全深度 × 12 种子 spawn 全域内(%d 场,∈[unit_count, unit_count_max] 且 ≥1)——空遭遇不可达(Bug#1 回归)" % scan_total) + + +func _level_battle_nodes(level_index: int) -> Array: + var out: Array = [] + for row in _db.level.rows: + if int(row.level_index) == level_index: + for node in row.nodes: + var t := String(node.get("type", "")) + if t == "battle" or t == "boss": + out.append(node) + return out + + +# ---- B1 战斗段 fresh 池攻击链回归(playtest-2026-09-12 报告 Bug#2 沉淀—— +# 「Boss 攻击拍零兑现」实为旧 defense_up 数据 ×2 叠满 = 池触底 ×0(设计语义);本断言钉死: +# 池为空时攻击链必须全额兑现——若未来出现 fresh 池零兑现(真攻击链缺陷)在此被抓)---- + +func _smoke_attack_chain_fresh_pool() -> void: + print("--- B1 fresh 池攻击链回归(Bug#2)---") + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260913) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + var ticks: Array = [] # {raw, tick}——敌行动伤害行重建 + b1.step_row.connect(func(row): + if row.type == "damage" and row.source_class == "enemy_action": + ticks.append({"raw": int(row.baseline) + int(row.attach), "tick": int(row.value)})) + b1.window_opened.connect(func(items): b1.submit_window_selection([])) + check(b1.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "fresh 池回归战斗启动(Boss 行)") + # 停手轮:零挖掘零旗 → 零授予 → 池恒空;Boss pattern 前 4 拍 = attack/attack/charge/heavy + for i in range(4): + if b1.battle_result != "": + break + b1.a2.settle_batch([{"action": "stop"}]) + check(ticks.size() >= 2, "非蓄力攻击拍采样充足(%d 拍——charge 拍零兑付为设计语义不计)" % ticks.size()) + var all_full := true + for t in ticks: + if int(t.tick) != int(t.raw) or int(t.raw) <= 0: + all_full = false + check(all_full, "fresh 池非蓄力拍 d_tick == atk.total 全额兑现(Boss 攻击链健康——Bug#2 回归断言)") + +func _encounter_replay(seed_val: int) -> Array: + var out: Array = [] + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var b1 = load("res://core/battle_orchestrator.gd").new(_db, rng) + b1.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 50}) + out.append(b1.presentation_snapshot().enemies.size()) + return out + +func check_silent(cond: bool) -> void: + _checks += 1 + if not cond: + _failures += 1 + print(" [FAIL] (silent check)") diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd.uid new file mode 100644 index 00000000..9afd813a --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd.uid @@ -0,0 +1 @@ +uid://bk7yf7sf5w3h3 diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/supply_scan.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/supply_scan.gd new file mode 100644 index 00000000..0c878d2f --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/supply_scan.gd @@ -0,0 +1,80 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-13(playtest-2026-09-12 Top1 复核——"seed 42 每局 ≥3 治疗包"是固定种子重放还是系统性偏置?) +# 跨种子可选类供给扫描:每局统计治疗包/小刀/精工短刀的实际获取数(step_row item 行 + 关卡奖励点补计), +# 输出构成占比(对 9:1 设计配比)与"≥3 治疗包"局数分布——单种子坏抽与系统性偏置由此分割。 +# 运行:Godot_console.exe --headless --path . --script res://smoke/supply_scan.gd -- [N] [seed0] +extends SceneTree + + +func _initialize() -> void: + var args := OS.get_cmdline_user_args() + var n := 16 + if args.size() >= 1 and args[0].is_valid_int(): + n = maxi(4, int(args[0])) + var seed0 := 100 + if args.size() >= 2 and args[1].is_valid_int(): + seed0 = int(args[1]) + var db = load("res://core/config_db.gd").new() + if not db.load_tables("res://data/tables", false): + print("表加载失败") + quit(1) + return + var autoplay = load("res://smoke/level_autoplay.gd") + print("=== 可选类供给跨种子扫描:第一关 · N=%d · 种子 %d+%d(基线数值)===" % [n, seed0, n]) + print("%-8s %-8s %-6s %-8s %-16s %-10s" % ["seed", "结果", "轮数", "承伤", "治疗包/小刀/短刀", "进BossHP"]) + var heals: Array = [] + var total_h := 0 + var total_k := 0 + var total_a := 0 + for i in range(n): + var seed_val: int = seed0 + i + var st = autoplay.run_level(db, seed_val, 1, 0, 0, true) + var h := 0 + var k := 0 + var a := 0 + for row in st.rows: + if String(row.get("type", "")) != "item": + continue + var txt := String(row.get("display_name", "")) + if txt.begins_with("治疗包"): + h += 1 + elif txt.begins_with("小刀"): + k += 1 + elif txt.begins_with("精工短刀"): + a += 1 + # 关卡奖励点(grant_node_active)不发 step_row——经 node 快照补计 + var nid := String(st.reward_snapshot.get("reward_id", "")) + if nid == "heal_pack": + h += 1 + elif nid == "knife_t1": + k += 1 + elif nid == "knife_adv": + a += 1 + heals.append(h) + total_h += h + total_k += k + total_a += a + print("%-8d %-8s %-6d %-8d %-16s %-10d" % [seed_val, st.result, st.rounds, st.hero_dmg, + "%d/%d/%d" % [h, k, a], st.hp_at_boss]) + var t := total_h + total_k + total_a + print("---- 合计 %d 件:治疗包 %d(%.0f%%)· 小刀 %d(%.0f%%)· 精工短刀 %d(%.0f%%)—— 伤害:治疗 = %.1f:1(设计 9:1)" % [ + t, total_h, 100.0 * total_h / maxi(1, t), total_k, 100.0 * total_k / maxi(1, t), + total_a, 100.0 * total_a / maxi(1, t), float(total_k + total_a) / maxi(1, total_h)]) + var ge3 := 0 + var zero := 0 + for c in heals: + if c >= 3: + ge3 += 1 + elif c == 0: + zero += 1 + print("每关治疗包:均值 %.2f · ≥3 包 %d/%d 局 · 0 包 %d/%d 局" % [_mean(heals), ge3, n, zero, n]) + quit(0) + + +func _mean(a: Array) -> float: + if a.is_empty(): + return 0.0 + var t := 0.0 + for v in a: + t += float(v) + return t / float(a.size()) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/supply_scan.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/supply_scan.gd.uid new file mode 100644 index 00000000..5d3bba21 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/supply_scan.gd.uid @@ -0,0 +1 @@ +uid://dw3rd4l55w70r diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd new file mode 100644 index 00000000..178b2459 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd @@ -0,0 +1,312 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(§AC 场景级测试 + §Interaction Map——UI 行为随机化探针 v3)。 +# Date: 2026-09-12(五区改造批扩展;基础 = 2026-09-11 问题3 复现器 v2——既有断言全保留) +# 合成鼠标事件走真实呈现层路径(BoardView.click_cell / 按钮信号 → 组合根动作通道), +# 随机混合玩家行为——插旗/拔旗、提前停手、弃图刷新确认链、图例模态、明细回看模态、 +# tooltip 悬停、窗口选用/跳过、无效点击——扫描结算停滞与选用失效(缺陷②④回归载体) +# + 模态遮挡断言(缺陷⑤回归:图例/回看/终局 overlay 打开时上方遮挡层隐藏、输入锁定) +# + 性能 AC:场景实例化到首格可点 ≤500ms 探针计时。 +# 行为序列由 PROBE_SEED 决定(shell 传参);程序玩法数值不受影响(随机只用于探针策略)。 +# 运行:PROBE_SEED=7 Godot_console.exe --headless --path . res://smoke/ui_click_probe.tscn +extends Node + +const AutoPlayer := preload("res://smoke/auto_player.gd") # O4:自动玩家策略单一实现 + +var main +var _last_state := {} +var _stall_frames := 0 +var _done := false +var _rand := RandomNumberGenerator.new() +var _flags_placed: Array = [] +var _ap_spent_this_round := 0 +var _used_ok := 0 +var _used_bad := 0 +var _problems: Array = [] # 新增覆盖的问题收集(退出码合并判定) +var _tested_legend := false +var _tested_review := false +var _tested_refresh_chain := false +var _tested_tooltip := false +var _tested_window_assert := false + + +func _ready() -> void: + var seed_env := OS.get_environment("PROBE_SEED") + _rand.seed = int(seed_env) if seed_env.is_valid_int() else 7 + await get_tree().process_frame + var t0 := Time.get_ticks_msec() + main = load("res://presentation/main.tscn").instantiate() + add_child(main) + await get_tree().process_frame + var t_ui_ready := Time.get_ticks_msec() - t0 + # 满配直达 Boss(基线在节点 2-4 就会败北——探针目标是 Boss 战的结算路径) + main.tune_panel.set_sliders(200, 100) + var t1 := Time.get_ticks_msec() + main._start_full_level() + var t_entry := Time.get_ticks_msec() - t1 + # 性能 AC:进入战斗屏到首格可点 ≤500ms(棋盘 install 完成口径); + # 场景实例化冷启动总时长(含全部脚本首载编译——引擎启动域,非 AC 口径)另列观测 + var t_total := Time.get_ticks_msec() - t0 + var first_cell_ms := t_entry + if first_cell_ms > 500: + _problems.append("进入战斗屏首格可点超时 %dms > 500ms" % first_cell_ms) + if main.board_view._cells.is_empty(): + _problems.append("首格未可点(cells=0)") + print("[click_probe] 首格可点计时 %dms(进入战斗屏口径,≤500ms AC)· 场景冷启动 %dms(含 UI 构建 %dms,观测项)" % [ + first_cell_ms, t_total, t_ui_ready]) + var action_budget := 6000 + while not _done and action_budget > 0: + action_budget -= 1 + await get_tree().process_frame + if main.node_overlay.visible: + main.node_overlay.press_continue() + _flags_placed.clear() + continue + if main.inventory_panel.is_window_open(): + if not _tested_window_assert: + # 去饱和渐变(0.15s Tween)完成后断言——窗口悬置期无自动关闭,等待安全 + for _wait in 20: + await get_tree().process_frame + _assert_window_state_once() + _answer_window() + continue + if main._end_overlay.is_open(): + # 终局 overlay 模态断言(pattern 6:打开时输入锁定) + if not main._input_locked(): + _problems.append("终局 overlay 打开时输入未锁定") + _done = true + print("[click_probe] 关卡结束:%s" % main._end_overlay.verdict_text().replace("\n", " ")) + break + if main.b1 == null or main.runner == null: + continue + var diag: Dictionary = main.b1.diagnostic_snapshot() # 诊断观测面(私有字段零直读) + var st := { + "node": main.runner.node_index, "round": main.b1.round_index, + "phase": main.b1.a3.phase, "ap": main.b1.a3.ap, + "win": main.inventory_panel.is_window_open(), "settling": diag.settling, + "await": diag.awaiting_window, + } + if JSON.stringify(st) == JSON.stringify(_last_state): + _stall_frames += 1 + if _stall_frames == 30: + _report_stall(st) + return + else: + _stall_frames = 0 + _ap_spent_this_round = 0 + _last_state = st + if main.b1.battle_result == "" and main.b1.a3.phase == "Active": + _run_modal_probes_once() + _play_one_action() + print("[click_probe] 循环退出(done=%s budget=%s)选用生效=%s 选用失效=%s 问题=%d" % [ + str(_done), str(action_budget), str(_used_ok), str(_used_bad), _problems.size()]) + for p in _problems: + push_error("[click_probe] FAIL — " + p) + var exit_code := 0 + if not _done: + exit_code = 1 + if _used_bad != 0 or not _problems.is_empty(): + exit_code = 1 + get_tree().quit(exit_code) + + +# ---- 模态/tooltip/刷新链一次性覆盖(真实信号路径驱动) ---- + +func _run_modal_probes_once() -> void: + # ① 图例模态(pattern 6):打开 → 遮挡物隐藏 + 输入锁定;关闭 → 恢复 + if not _tested_legend: + _tested_legend = true + main.ap_bar._btn_legend.pressed.emit() + if not main.legend_overlay.is_open(): + _problems.append("图例打开失败") + if main.review_overlay.is_open() or main._end_overlay.is_open() or main._start_overlay.visible: + _problems.append("图例打开时上方遮挡层未隐藏(缺陷⑤复发)") + if not main._input_locked(): + _problems.append("图例打开时棋盘输入未锁定") + if not main.legend_overlay.ENTRIES[2][2].contains("无雷盲区"): + _problems.append("图例缺『迷雾 = 无雷盲区』恒在条目") + main.legend_overlay.close() + if main.legend_overlay.is_open() or main._input_locked(): + _problems.append("图例关闭后未恢复输入/可见性") + # ② 结算明细回看模态(pattern 6):B 区明细带点击入口 → overlay → 关闭恢复 + if not _tested_review: + _tested_review = true + main._open_review_from_band() + if not main.review_overlay.is_open(): + _problems.append("明细回看打开失败") + if not main._input_locked(): + _problems.append("明细回看打开时输入未锁定") + main.review_overlay.close() + if main.review_overlay.is_open() or main._input_locked(): + _problems.append("明细回看关闭后未恢复") + # ③ tooltip(状态效果槽悬停——来源+语义+剩余):有槽可见时触发 + if not _tested_tooltip: + var row = main.info_panel.slot_row + for child in row.get_children(): + if child is PanelContainer and child.visible and child.has_meta("eff"): + _tested_tooltip = true + child.mouse_entered.emit() + if not row._tip.visible: + _problems.append("状态槽 tooltip 未显示") + elif not row._tip_label.text.contains("来源") or not row._tip_label.text.contains("剩余"): + _problems.append("状态槽 tooltip 缺来源/剩余(%s)" % row._tip_label.text.replace("\n", "|")) + child.mouse_exited.emit() + if row._tip.visible: + _problems.append("状态槽 tooltip 未随移出隐藏") + break + # ④ 弃图刷新确认链(pattern 7 异位:确认 chips 与常规确认不同位 + 显式代价文案) + if not _tested_refresh_chain and main.ap_bar.is_refresh_ready(): + _tested_refresh_chain = true + var rap_before: int = int(main.b1.a3.rap) + var board_before = main.b1.a2.current_board() + main.ap_bar.press_refresh_for_probe() + if not main.ap_bar.confirm_visible(): + _problems.append("弃图刷新确认 chips 未出现(异位确认缺失)") + main.ap_bar.press_confirm_for_probe() + if int(main.b1.a3.rap) != rap_before - 8: + _problems.append("弃图刷新未扣 RAP8(%d→%d)" % [rap_before, int(main.b1.a3.rap)]) + if main.b1.a3.phase != "Active": + _problems.append("轮内刷新后相位丢失(%s)" % main.b1.a3.phase) + if main.b1.a2.current_board() == board_before: + _problems.append("轮内刷新未当场换图") + if main._refresh_used_round != 1: + _problems.append("刷新轮内计数错误(%d)" % main._refresh_used_round) + + +func _assert_window_state_once() -> void: + if _tested_window_assert: + return + _tested_window_assert = true + # 原位窗口态(pattern 2):E 区描边金光点亮 + 标题切换 + D 区去饱和锁输入(明度不变) + if not main.inventory_panel._title.text.contains("轮末道具窗口"): + _problems.append("窗口态标题未切换(%s)" % main.inventory_panel._title.text) + if not main.inventory_panel._skip_btn.visible: + _problems.append("窗口态缺跳过按钮") + var strength: float = float(main.board_view._desat_mat.get_shader_parameter("strength")) + if strength < 0.9: + _problems.append("窗口期棋盘未去饱和(strength=%.2f)" % strength) + + +func _play_one_action() -> void: + var b1 = main.b1 + if b1 == null: + return # 结算竞态防抖:battle_finished 与本轮动作调度同帧到达时 b1 可能已被置空(基线既有噪声——加固) + var a2 = b1.a2 + # 行为①:随机插旗(右键——未开相邻格),约 20% 轮次 + if _ap_spent_this_round == 0 and _rand.randf() < 0.2: + var target := _random_unknown_adjacent(a2) + if target.x >= 0: + main.board_view.click_cell(target, MOUSE_BUTTON_RIGHT) + _flags_placed.append(target) + _ap_spent_this_round += 1 + return + # 行为②:随机拔旗(右键已插旗格),约 10% 轮次 + if _rand.randf() < 0.1 and not _flags_placed.is_empty(): + var fc: Vector2i = _flags_placed.pop_back() + if a2.has_flag(fc) and not a2.is_revealed(fc): + main.board_view.click_cell(fc, MOUSE_BUTTON_RIGHT) + _ap_spent_this_round += 1 + return + # 行为③:随机无效点击(非相邻格/已开格),约 8% 轮次 + if _rand.randf() < 0.08: + main.board_view.click_cell(Vector2i(_rand.randi_range(0, a2.current_board().width - 1), _rand.randi_range(0, a2.current_board().height - 1)), MOUSE_BUTTON_LEFT) + return + # 挖掘主策略(最近可挖格) + var plan = AutoPlayer.choose_dig(a2) # O4 收口:策略单一实现 + if not plan.is_empty(): + if plan.hero_cell != a2.hero_pos(): + main.board_view.click_cell(plan.hero_cell, MOUSE_BUTTON_LEFT) + main.board_view.click_cell(plan.dig_cell, MOUSE_BUTTON_LEFT) + _ap_spent_this_round += 1 + # 行为④:提前停手(已花 ≥3 AP 时 15%)——停手经 AP 条按钮真实信号路径 + if _ap_spent_this_round >= 3 and b1.a3.phase == "Active" and _rand.randf() < 0.15: + _click_stop() + elif b1.a3.phase == "Active": + _click_stop() + + +func _answer_window() -> void: + # 行为⑥:选用首件可用道具 / 跳过(选择语义 = 窗口条目位置——与 UI 契约一致,缺陷④回归载体) + if _rand.randf() < 0.5 and main.b1 != null: + var b1 = main.b1 + var s: Dictionary = b1.presentation_snapshot() # E1 收口:经呈现快照面读库存/英雄 + var pos := -1 + for inst in s.inventory: + if inst.status != "InStock": + continue + pos += 1 + var usable: bool = int(s.hero.hp) < int(s.hero.max_hp) or inst.category == "damage_item" + if usable: + var inv_index: int = int(inst.index) + b1.submit_window_selection([pos]) + main.inventory_panel.close_window() + # 缺陷④回归断言:被选件必须真实消耗(Used),不得误耗邻件/留存 + var after: Array = b1.presentation_snapshot().inventory + if not b1.is_battle_live(): + # 终局结算窗口:选用后战斗立即结束、level_clear 合法清库——Used 断言不适用 + _used_ok += 1 + elif inv_index < after.size() and String(after[inv_index].status) == "Used": + _used_ok += 1 + else: + _used_bad += 1 + print("[dbg-use] FAIL pos=%d inv_index=%d cat=%s statuses=%s" % [ + pos, inv_index, inst.category, + str(after.map(func(x): return [int(x.index), String(x.status)]))]) + return + _skip_window() + return + _skip_window() + + +func _skip_window() -> void: + if main.b1 != null: + main.b1.submit_window_selection([]) + main.inventory_panel.close_window() + + +func _random_unknown_adjacent(a2) -> Vector2i: + var hero: Vector2i = a2.hero_pos() + var cands: Array = [] + for dy in range(-1, 2): + for dx in range(-1, 2): + if dx == 0 and dy == 0: + continue + var c: Vector2i = hero + Vector2i(dx, dy) + if a2.current_board().in_bounds(c) and not a2.is_revealed(c) and not a2.has_flag(c): + cands.append(c) + if cands.is_empty(): + return Vector2i(-1, -1) + return cands[_rand.randi_range(0, cands.size() - 1)] + + +func _click_stop() -> void: + # 停手走真实按钮信号路径(AP 条 → 组合根 settle_batch——A2 D1×6① 回归载体) + main.ap_bar._btn_stop.pressed.emit() + + +func _report_stall(st: Dictionary) -> void: + var why := "" + if st.phase == "Waiting" and not st.win and not st.await: + why = "结算停滞:Waiting 相位无窗口悬置" + elif st.phase == "Waiting" and st.await: + why = "窗口悬置未被消费(面板窗口态=%s)" % str(main.inventory_panel.is_window_open()) + elif st.phase == "Active" and st.ap == 0: + why = "Active 相位 AP=0(应在归零瞬间翻 Waiting)" + elif st.phase == "Idle" and main.b1.battle_result == "": + why = "Idle 相位但战斗未结束(begin 失败?)" + else: + why = "状态无推进(battle_result='%s')" % str(main.b1.battle_result) + print("[click_probe] 🔴 卡死复现(PROBE_SEED=%s):%s" % [OS.get_environment("PROBE_SEED"), why]) + _dump_state() + get_tree().quit(2) + + +func _dump_state() -> void: + var diag: Dictionary = main.b1.diagnostic_snapshot() # 诊断观测面(私有字段零直读) + print(" node_index=%s round=%s phase=%s ap=%s rap=%s" % [ + str(main.runner.node_index), str(main.b1.round_index), + str(main.b1.a3.phase), str(main.b1.a3.ap), str(main.b1.a3.rap)]) + print(" battle_result='%s' settling=%s awaiting_window=%s window_visible=%s window_open=%s" % [ + str(diag.battle_result), str(diag.settling), str(diag.awaiting_window), + str(main.inventory_panel._window_mode), str(main.inventory_panel.is_window_open())]) + print(" board_installed=%s facts_path='%s' refresh_used=%s flags=%s" % [ + str(diag.a2_board_installed), str(diag.facts_path), str(diag.refresh_used_waiting), str(_flags_placed)]) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd.uid new file mode 100644 index 00000000..58d1bd5d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd.uid @@ -0,0 +1 @@ +uid://dokvbpqulbdjl diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.tscn b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.tscn new file mode 100644 index 00000000..f469a278 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://smoke/ui_click_probe.gd" id="1"] + +[node name="UIClickProbe" type="Node"] +script = ExtResource("1") diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd new file mode 100644 index 00000000..4b54253d --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd @@ -0,0 +1,48 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-13(五区布局诊断探针——"内容未满幅/棋盘底裁切"回归定位:打印窗口/视口/五区实际矩形 +# 与最小尺寸、展开旗标,对照 battle.md §Layout 1280×800 基座规格) +# 运行:Godot_console.exe --headless --path . res://smoke/ui_layout_probe.tscn +extends Node + + +func _ready() -> void: + await get_tree().process_frame # autoload 就绪 + var main = load("res://presentation/main.tscn").instantiate() + add_child(main) + await get_tree().process_frame + main._start_full_level() + for i in range(3): + await get_tree().process_frame + var win := get_window() + print("window.size=%s pos=%s mode=%s" % [win.size, win.position, win.mode]) + print("viewport.visible_rect=%s" % get_viewport().get_visible_rect()) + print("main.size=%s" % main.size) + for c in main.get_children(): + if c is Control: + _dump_ctrl(c, 0) + var zones := { + "board_zone": main.board_zone, "board_view": main.board_view, + "info_panel": main.info_panel, "inventory_panel": main.inventory_panel, + "stage": main.stage, + } + for k in zones: + var ctl: Control = zones[k] + if ctl == null: + print("%s = null" % k) + continue + print("%-16s pos=%s size=%s min=%s hflag=%d vflag=%d" % [k, ctl.position, ctl.size, + ctl.custom_minimum_size, ctl.size_flags_horizontal, ctl.size_flags_vertical]) + var root_box: Control = main.get_child(0) + print("five-zone root: anchors=(%s,%s)-(%s,%s) offsets=(%s,%s,%s,%s) size=%s" % [ + root_box.anchor_left, root_box.anchor_top, root_box.anchor_right, root_box.anchor_bottom, + root_box.offset_left, root_box.offset_top, root_box.offset_right, root_box.offset_bottom, root_box.size]) + print("board_view cell_size=%s board=%s" % [main.board_view.get("cell_size"), main.board_view.board()]) + get_tree().quit(0) + + +func _dump_ctrl(c: Control, depth: int) -> void: + var ind := " ".repeat(depth) + print("%s%s pos=%s size=%s min=%s" % [ind, c.name, c.position, c.size, c.custom_minimum_size]) + for ch in c.get_children(): + if ch is Control: + _dump_ctrl(ch, depth + 1) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd.uid new file mode 100644 index 00000000..46dbe351 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd.uid @@ -0,0 +1 @@ +uid://buhvfu36e2dqf diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.tscn b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.tscn new file mode 100644 index 00000000..1b5a4210 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://smoke/ui_layout_probe.gd" id="1"] + +[node name="UILayoutProbe" type="Node"] +script = ExtResource("1") diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd new file mode 100644 index 00000000..ed158eed --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd @@ -0,0 +1,108 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 实现 GDD:design/ux/battle.md(场景级 headless 探针——五区布局实例化断言;entry 17②③)。 +# Date: 2026-09-12(五区改造批扩展;基础 = 2026-09-11 首战棋盘探针——既有断言全保留) +# 项目运行模式(autoload 在位)实例化 main.tscn,直接驱动 _start_full_level(), +# 断言五区真实构建(棋盘/进度条/英雄卡/敌人卡/AP 点阵/明细带/数据框/库存面板)。 +# 运行:Godot_console.exe --headless --path . res://smoke/ui_probe.tscn +extends Node + + +func _ready() -> void: + await get_tree().process_frame # autoload 就绪 + var main = load("res://presentation/main.tscn").instantiate() + add_child(main) + await get_tree().process_frame + main._start_full_level() + await get_tree().process_frame + await get_tree().process_frame + var problems: Array = [] + # ---- 既有断言(回归基线)---- + if main.board_view._cells.is_empty(): + problems.append("棋盘格未生成(cells=0)") + if main.board_view.board() == null: + problems.append("board 未装载") + if main.strip_view.get_child_count() < 2 * main.runner.nodes().size() - 1: + problems.append("节点进度条未构建(%d 子节点)" % main.strip_view.get_child_count()) + if main.info_panel.hp_text().begins_with("HP —") or main.info_panel.hp_text().contains("—"): + problems.append("C 区血量未显示(%s)" % main.info_panel.hp_text()) + if main.b1 == null or main.b1.battle_result != "": + problems.append("首战未处于进行中") + if not main.log_view.parsed_text().contains("节点 1/"): + problems.append("节点头日志缺失") + # ---- 五区布局断言(battle.md §Layout)---- + if not main.stage.round_label.text.contains("第 1 轮"): + problems.append("B 区轮次·阶段指示缺失(%s)" % main.stage.round_label.text) + if main.stage.enemy_box.get_child_count() < 1: + problems.append("B 区敌人卡未构建(%d 张)" % main.stage.enemy_box.get_child_count()) + if main.ap_bar._dots.get_child_count() != int(main.db.action_economy.ap_per_round): + problems.append("D 区 AP 点阵数量错误(%d)" % main.ap_bar._dots.get_child_count()) + if main.ap_bar.refresh_remaining_text() == "": + problems.append("D 区弃图刷新按钮文案缺失") + if main.board_zone.toast == null: + problems.append("D 区 toast 槽缺失") + if main.info_panel.slot_row == null: + problems.append("C 区状态效果槽缺失") + if main.log_view.mode() != "dig" or main.log_view.custom_minimum_size.y != 24.0: + problems.append("B 区明细带挖掘期应收起 24px(mode=%s h=%s)" % [main.log_view.mode(), str(main.log_view.custom_minimum_size.y)]) + if main.inventory_panel._title.text != "道包 · 关卡域": + problems.append("E 区库存常读标题错误(%s)" % main.inventory_panel._title.text) + # ---- 敌人卡投影(顺位/意图徽标常驻——B2×4)---- + var card = main.stage.enemy_box.get_child(0) + if card != null and card._name_label.text == "": + problems.append("敌人卡名称为空") + if card != null and not card._intent_badge.text.begins_with("意图"): + problems.append("敌人卡意图徽标缺失(%s)" % card._intent_badge.text) + # ---- BUG-0001 回归(tooltip 入树后 set_effects 槽位不错位——结算软锁修复沉淀)---- + var slots_row = main.info_panel.slot_row + var probe_eff: Array = [{"effect_id": "attack_up", "display_name": "增伤", "layers": 1, + "remaining_settlements": 1, "source_class": "safe_cell", "round_index": 1, "effective_value": 50}] + slots_row.set_effects(probe_eff) + if slots_row._slots.size() != 1: + problems.append("BUG-0001 回归:set_effects 未建槽(%d)" % slots_row._slots.size()) + slots_row._on_slot_hover(slots_row._slots[0]) # 悬停 → tooltip _tip 入子节点表(野外崩溃前置态) + slots_row.set_effects(probe_eff) # 修复前:_tip 被按位序当作槽 0 → _fill_slot 崩溃 → 结算协程中断软锁 + if not (slots_row._tip in slots_row.get_children()): + problems.append("BUG-0001 回归:悬停后 tooltip 未入树(回归前置失真)") + elif slots_row._slots.size() != 1 or slots_row._slots[0] == slots_row._tip: + problems.append("BUG-0001 回归:tooltip 入树后槽引用错位") + else: + var icon_lab: Label = slots_row._slots[0].get_child(0).get_child(0) + if icon_lab.text != "▲": + problems.append("BUG-0001 回归:悬停后重刷槽未填充(icon=%s)" % icon_lab.text) + slots_row.set_effects([probe_eff[0], probe_eff[0], probe_eff[0], probe_eff[0], probe_eff[0], probe_eff[0]]) + if slots_row._slots.size() != 4: + problems.append("BUG-0001 回归:超上限效果未截断至 4 槽(%d)" % slots_row._slots.size()) + slots_row.set_effects([]) + if not slots_row._slots.is_empty(): + problems.append("BUG-0001 回归:空态未清槽(%d)" % slots_row._slots.size()) + # ---- L2 雾图变体段(A4:雾首遇一次性提示 / 雾格呈现 / 去饱和接口可用性)---- + # 2026-09-13 单场用户入口已删——探针直调 debug_session 原语(battle_created/ready 仍经组合根接线; + # _reset_run_scope 复现原 _start_single 的提示去重复位,保证 fog_first 断言前置态) + main._reset_run_scope() + main._debug.start_single(2) + await get_tree().process_frame + await get_tree().process_frame + if main.board_view.board() == null: + problems.append("L2 单场未启动(board=null)") + elif main.board_view.board().fog_cells().is_empty(): + problems.append("L2 板无雾格(A4 变体缺失)") + elif not bool(main._hints_shown.get("fog_first", false)): + problems.append("雾首遇一次性提示未触发(fog_first)") + else: + if main.board_zone.toast._slot_box.get_child_count() < 1: + problems.append("雾首遇 toast 未呈现") + if main.board_view._fog_parts.is_empty(): + problems.append("雾区连通分量未计算(合并呈现域)") + main.board_view.set_desaturated(true) + main.board_view.set_desaturated(false) + if problems.is_empty(): + print("[ui_probe] PASS — cells=%d board=%dx%d strip=%d hp='%s' enemies=%d ap_dots=%d" % [ + main.board_view._cells.size(), main.board_view.board().width, main.board_view.board().height, + main.strip_view.get_child_count(), main.info_panel.hp_text(), + main.stage.enemy_box.get_child_count(), main.ap_bar._dots.get_child_count()]) + get_tree().quit(0) + else: + for p in problems: + push_error("[ui_probe] FAIL — " + p) + print("[ui_probe] FAIL — " + "; ".join(problems)) + get_tree().quit(1) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd.uid new file mode 100644 index 00000000..8f6a6020 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd.uid @@ -0,0 +1 @@ +uid://c63fe0ea78baw diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.tscn b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.tscn new file mode 100644 index 00000000..c0e54d73 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://smoke/ui_probe.gd" id="1"] + +[node name="UIProbe" type="Node"] +script = ExtResource("1") diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd new file mode 100644 index 00000000..8f4c4cc4 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd @@ -0,0 +1,73 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# Date: 2026-09-11(2026-09-12 code-review 修复批重写——加成应用契约改为组合根单一应用路径) +# 数值调整板场景探针(playtest 缺陷⑤回归载体——面板此前被不透明开始覆盖层遮挡 = "点击无反应"): +# 驱动真实 UI 路径——打开可见性 / 滑条联动 / 应用(apply_requested → 组合根)/ +# 重置 / 批量模拟全链路(N=10)/ E4 回归断言(hero 表运行期不可变 + 单次加成经 encounter 生效)。 +# 运行:Godot_console.exe --headless --path . res://smoke/ui_tune_probe.tscn +extends Node + + +func _ready() -> void: + await get_tree().process_frame + var main = load("res://presentation/main.tscn").instantiate() + add_child(main) + await get_tree().process_frame + var problems: Array = [] + # ① 打开可见性(缺陷⑤回归:打开面板必须隐藏不透明的开始覆盖层) + main._show_tune_panel() + if not main.tune_panel.visible: + problems.append("面板未打开") + if main._start_overlay.visible: + problems.append("开始覆盖层未隐藏——面板被遮挡(缺陷⑤复发)") + # ② 滑条 → 生效预览联动 + main.tune_panel.set_sliders(100, 50) + if "12" not in main.tune_panel.effect_text() or "75" not in main.tune_panel.effect_text(): + problems.append("生效预览未联动(%s)" % main.tune_panel.effect_text()) + # ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照 + main.tune_panel.press_apply_for_probe() + # ④ 重置(重开面板 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线) + main._show_tune_panel() + main.tune_panel.press_reset_for_probe() + # ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位) + main.tune_panel.set_sliders(100, 0) + main.tune_panel.set_sim_count(10) + main.tune_panel.press_simulate_for_probe() + var guard := 1800 # N=10 headless 模拟防挂死上限(ADR-0004:探针内停滞检测第一道) + while main.tune_panel.is_running() and guard > 0: + guard -= 1 + await get_tree().process_frame + if main.tune_panel.is_running(): + problems.append("模拟未在预算帧内完成(疑似挂死)") + var out: String = main.tune_panel.output_text() + if not out.contains("通关率"): + problems.append("模拟输出缺通关率") + if not out.contains("败北节点"): + problems.append("模拟输出缺败北节点") + if "N=10" not in out: + problems.append("模拟输出 N 标注错误(%s)" % out.substr(0, 40)) + # ⑥ 关闭 → 回开始界面 + main._close_tune_panel() + if main.tune_panel.visible or not main._start_overlay.visible: + problems.append("关闭后层级状态错误") + # ⑦ hero 表运行期不可变(E4 收口回归断言:任何代码 reintroduce 表写在此被抓获——原"表值残留"断言语义升级) + if int(main.db.hero.atk) != 6 or int(main.db.hero.max_hp) != 50: + problems.append("hero 表被运行时改写(E4 违例:atk=%s hp=%s,期望恒基线 6/50)" % [str(main.db.hero.atk), str(main.db.hero.max_hp)]) + # ⑧ 单次加成生效回归断言:应用加成 + 启动单场战斗 → 呈现快照 hero 必须是"基线 × 单次加成" + # (6×2=12 / 50×1.5=75)——旧缺陷(残留值被当基线再乘 → 24/112)在 encounter 参数链上同样被抓获 + main.tune_panel.set_sliders(100, 50) + main.tune_panel.press_apply_for_probe() + main._start_full_level() # 完整关卡路径(_start_single 按语义清零滑条 = 恒基线,不承载加成断言) + await get_tree().process_frame + await get_tree().process_frame + var hero: Dictionary = main.b1.presentation_snapshot().hero + if int(hero.get("atk", -1)) != 12 or int(hero.get("max_hp", -1)) != 75: + problems.append("战斗加成单次生效违例(atk=%s max_hp=%s,期望 12/75)" % [ + str(hero.get("atk", "?")), str(hero.get("max_hp", "?"))]) + if problems.is_empty(): + print("[tune_probe] PASS — 打开/联动/应用/重置/模拟/关闭/基线无残留/单次加成 全链路通过(模拟 N=10 输出 %.30s…)" % out) + get_tree().quit(0) + else: + for p in problems: + push_error("[tune_probe] FAIL — " + p) + print("[tune_probe] FAIL — " + "; ".join(problems)) + get_tree().quit(1) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd.uid b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd.uid new file mode 100644 index 00000000..dd968998 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd.uid @@ -0,0 +1 @@ +uid://s8mepyuvkyl8 diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.tscn b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.tscn new file mode 100644 index 00000000..9d96ad75 --- /dev/null +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://smoke/ui_tune_probe.gd" id="1"] + +[node name="UITuneProbe" type="Node"] +script = ExtResource("1") diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..0822321a --- /dev/null +++ b/tests/README.md @@ -0,0 +1,67 @@ +# Test Infrastructure — Dungeon Sweeper + +**Engine**: Godot 4.7.2-stable(GDScript 单语言) +**Test Framework**: 双载体(ADR-0004)——`--headless --script` 逻辑冒烟 + headless 场景探针;gdUnit4 **deferred**(D2 OQ1 关闭形态,正式 CI 期再议) +**CI**: `.github/workflows/tests.yml`(首次 push 到 GitHub 后自动生效;仓库 2026-09-12 尚未初始化 git) +**Setup date**: 2026-09-12 +**参考实现**: `prototypes/dungeon-sweeper-vertical-slice/smoke/`(冒烟 96/96 + 三 UI 探针——正式工程的活样板) + +## Directory Layout + +``` +tests/ + unit/ # 隔离单元测试(公式/状态机/纯逻辑)——extends SceneTree + quit(code) + integration/ # 跨系统测试(Core-Core 集成、回放确定性、存档往返) + smoke/ # critical-paths.md——人工 gate 清单(/smoke-check 读取) + evidence/ # 截图与人工签核记录 + run_all.gd # CI 冒烟统一入口(套件 manifest 协议,D2 AC-2b) + static_scan.sh # ADR-0004 静态扫描域 S1–S8 可执行子集 +``` + +## Running Tests + +```bash +# 逻辑冒烟(双载体之一)——Windows 须用 console 版 exe 取 stdout +godot --headless --script tests/run_all.gd # 统一入口(CI 调用点) +godot --headless --script tests/unit/example_test.gd + +# 场景探针(双载体之二)——以探针场景为运行目标,走完整项目引导(autoload 在位) +godot --headless --path . res://tests/probes/.tscn + +# CI 旁必须配外部 wall-clock 超时(ADR-0004:引擎无死锁探测) +timeout 120s godot --headless --script tests/run_all.gd +``` + +**协议要点**(D2 冒烟协议 + ADR-0004): +- 冒烟脚本 `extends SceneTree` 以获得 `quit(code)`;成功 `quit(0)`、失败 `quit(1)` +- `--script` 模式**不加载 autoload 且不注册全局标识符**(裸引用 `ConfigDB` = parse error)——ConfigDB/RngService 由脚本显式实例化,core 一律构造注入 +- 致命类用例独立进程,runner manifest 登记"调用 → 期望退出码" +- 每用例先 `reset_diagnostics()`(诊断收集器协议) +- 期望值由表读值推导,不写死玩法数值 + +## Test Naming + +- **Files**: `[system]_[feature]_test.gd`(领域命名,禁 ID 前缀——docs/code-conventions.md) +- **Functions**: `test_[scenario]_[expected]` +- **Example**: `reward_roll_test.gd` → `test_fog_cell_filters_below_tier_min()` + +## Story Type → Test Evidence + +| Story Type | Required Evidence | Location | +|---|---|---| +| Logic | Automated unit test — must pass | `tests/unit/[system]/` | +| Integration | Integration test OR playtest doc | `tests/integration/[system]/` | +| Visual/Feel | Screenshot + lead sign-off | `tests/evidence/` | +| UI | Manual walkthrough OR interaction test(场景探针为首选) | `tests/evidence/` | +| Config/Data | Smoke check pass | `production/qa/smoke-*.md` | + +## Static Scan(提交前双跑:本地 + CI) + +`tests/static_scan.sh` 实现 ADR-0004 扫描域 S1–S8 的可执行子集;规则全集与动机见 +`docs/architecture/adr-0004-test-carrier-and-ci.md`。新违规案例必须**先补扫描规则再修码**。 + +## CI + +Tests run automatically on every push to `main` and on every pull request. +A failed test suite blocks merging. **gdUnit4 安装说明(deferred,届时启用)**: +AssetLib → GdUnit4 → Install → Project Settings → Plugins 勾选 → 重启编辑器 → 确认 `res://addons/gdunit4/`。 diff --git a/tests/evidence/README.md b/tests/evidence/README.md new file mode 100644 index 00000000..87741d0c --- /dev/null +++ b/tests/evidence/README.md @@ -0,0 +1,6 @@ +# tests/evidence/ — 人工测试证据 + +截图、录屏、人工签核记录。命名:`[日期]-[里程碑]-[检查项].md`。 + +面向表现/手感的验收(视觉锚三硬约束、格子样张测试、结算屏可读性、呈现义务台账核对)归此处—— +自动化探针(场景级)能覆盖的走 `tests/unit` / 探针场景,不能 G-W-T 的走本目录。 diff --git a/tests/integration/README.md b/tests/integration/README.md new file mode 100644 index 00000000..376628c6 --- /dev/null +++ b/tests/integration/README.md @@ -0,0 +1,5 @@ +# tests/integration/ — 跨系统测试 + +承载:Core-Core 集成(真 A3 + 真 B2/B3/B4 容器实例)、回放确定性(同种子步骤流逐行相同——B1 AC-33 形态)、存档/RNG 快照往返(D3 落地后)、A1×A2 双包含端到端。 + +各 GDD 登记的 deferral 集成用例(以对侧登记为生效条件)随对应系统实现迁入本目录。 diff --git a/tests/run_all.gd b/tests/run_all.gd new file mode 100644 index 00000000..ce552cd0 --- /dev/null +++ b/tests/run_all.gd @@ -0,0 +1,41 @@ +# tests/run_all.gd — CI 冒烟统一入口(ADR-0004 双载体的逻辑冒烟调用点) +# 用法:godot --headless --script tests/run_all.gd (CI 侧包装外部超时:timeout 120s …) +# 协议:套件清单逐条"套件 → 期望退出码"(D2 AC-2b manifest);致命类独立进程。 +# 现状:正式 Godot 工程尚未在根目录创建(代码在 prototypes/dungeon-sweeper-vertical-slice/, +# 其 smoke/smoke_all.gd 96/96 为参考实现)——本入口当前仅验证运行协议自身; +# 正式工程落地后,把套件清单登记进 _suites()。 +extends SceneTree + +func _init() -> void: + print("[run_all] formal smoke entry — protocol check") + if DisplayServer.get_name() != "headless": + push_warning("[run_all] 建议以 --headless 运行(D2 冒烟协议)") + var failures := 0 + for suite in _suites(): + failures += _run_suite(suite) + if failures > 0: + push_error("[run_all] %d 个套件失败" % failures) + quit(1) + return + print("[run_all] all green (%d suites)" % _suites().size()) + quit(0) + +# 返回 Array of Dictionary:{ "path": String, "expect": int } +# 正式工程落地后在此登记;探针场景(.tscn 载体)由 CI 单独步骤调用,不经本入口。 +func _suites() -> Array: + return [ + # {"path": "res://tests/unit/example_test.gd", "expect": 0}, + # {"path": "res://tests/unit/reward_economy/reward_roll_test.gd", "expect": 0}, + # 致命类(独立进程,期望非 0): + # {"path": "res://tests/unit/d2/fixture_missing_table_test.gd", "expect": 1}, + ] + +func _run_suite(suite: Dictionary) -> int: + # SceneTree 脚本内不能直接以 OS.execute 递归调 godot 的成熟形态留待正式工程 + # (需解析 godot 二进制路径);当前协议检查阶段只断言清单形态合法。 + var path: String = suite.get("path", "") + if path.is_empty(): + push_error("[run_all] suite missing path") + return 1 + print("[run_all] registered: %s (expect=%d)" % [path, suite.get("expect", 0)]) + return 0 diff --git a/tests/smoke/critical-paths.md b/tests/smoke/critical-paths.md new file mode 100644 index 00000000..d2ef2fa9 --- /dev/null +++ b/tests/smoke/critical-paths.md @@ -0,0 +1,33 @@ +# Smoke Test: Critical Paths — Dungeon Sweeper + +**Purpose**: QA 交接前 15 分钟内人工跑完的关键路径清单。 +**Run via**: `/smoke-check`(读本文件)。 +**Update**: 新核心系统落地后增补条目。 +**分工注记**:自动化冒烟(切片 96/96)覆盖规则正确性;本清单面向**表现与手感**(自动化探针不可 G-W-T 的部分)。 + +## Core Stability(恒跑) + +1. 启动到开始界面无崩溃;开始界面响应全部输入 +2. 开战:装板后初始展开可见、小人落位出生区、AP 显示 8 + +## Core Mechanic + +3. 挖掘:单格 1 AP、数字亮起与 8 邻雷数一致、连锁免费撕开一片 +4. 插旗/拔旗/挪旗:AP 价目正确、连锁止于旗(旗挡链)属正常 +5. 移动:已开格瞬移、**已引爆雷格可通行可读**(工程债呈现半边) +6. 停手:RAP 1:1 转存、下轮 AP 硬重置同图继续 +7. 轮末结算:明细逐行呈现、来源标注可读(P2——"这一行是我哪步扫雷带来的"可答) +8. 道具使用窗口:授予段后弹出、至多选一件、满血治疗包 usable_now 禁选态正确 +9. 清图:清图奖励兑付 + 完美排雷高光(零误标时双份)+ 换新图;未刷新的停手轮不换图 +10. 轮内刷新(方案 A):Active 当场换图、AP 保留、RAP 扣减、每轮 ≤2 次、跨图事实结算完整 + +## Battle & Level + +11. 战斗段:主角先手攻击行(hero_attack 来源)、敌方行为环读怪(姿态↔下一拍)、死亡即不动手 +12. 胜负判定与 battle_over 清理全序;库存跨战斗延续、关卡末"剩余道具 ×N 清空"反馈 +13. 关卡推进:节点链兑付(奖励点/恢复点)、Boss 房(Dmax 行)、层间 HP 携带 + +## Data & Perf + +14. 数值调整板:**打开可见**(覆盖层层级回归——缺陷⑤)、滑条即时生效、批量模拟可跑 +15. 无明显卡顿(60fps 目标);连续 5 分钟无内存异常增长;窗口/结算屏文案中文显示正常(CJK 字体核对) diff --git a/tests/static_scan.sh b/tests/static_scan.sh new file mode 100644 index 00000000..1fe690c5 --- /dev/null +++ b/tests/static_scan.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# tests/static_scan.sh — ADR-0004 静态扫描域 S1–S8 可执行子集 +# 规则全集与动机:docs/architecture/adr-0004-test-carrier-and-ci.md §Decision 3 +# 正式工程(core/ 存在)时执行;否则优雅跳过(切片阶段代码在 prototypes/,不受本扫描管辖)。 +# 纪律:新违规案例必须先补本脚本规则再修码(docs/code-conventions.md 缺陷-规范对照表)。 +set -u +cd "$(dirname "$0")/.." + +if [ ! -d core ]; then + echo "[static_scan] core/ 不存在(正式工程未创建)——跳过,退出 0" + exit 0 +fi + +fail=0 + +# S1/S2 — 玩法代码禁 randomize()/自建 RandomNumberGenerator/全局随机函数(绕开 RngService 的口子) +hits=$(grep -rnE '\brandomize[[:space:]]*\(|\bRandomNumberGenerator\b|\brandi[[:space:]]*\(|\brandf[[:space:]]*\(|\brandf_range[[:space:]]*\(' \ + core presentation --include='*.gd' 2>/dev/null | wc -l) +if [ "$hits" -gt 0 ]; then echo "[static_scan] S1/S2 FAIL: 全局随机/RNG 自建命中 $hits 处"; grep -rnE '\brandomize[[:space:]]*\(|\bRandomNumberGenerator\b|\brandi[[:space:]]*\(|\brandf[[:space:]]*\(|\brandf_range[[:space:]]*\(' core presentation --include='*.gd' 2>/dev/null; fail=1; else echo "[static_scan] S1/S2 ok"; fi + +# S4 — core/ 禁 extends Control/Node2D、禁 .tscn preload(headless 债) +hits=$(grep -rnE 'extends[[:space:]]+(Control|Node2D)\b|\.tscn' core --include='*.gd' 2>/dev/null | wc -l) +if [ "$hits" -gt 0 ]; then echo "[static_scan] S4 FAIL: core 引擎/UI 依赖命中 $hits 处"; grep -rnE 'extends[[:space:]]+(Control|Node2D)\b|\.tscn' core --include='*.gd' 2>/dev/null; fail=1; else echo "[static_scan] S4 ok"; fi + +# S3 — core/ 禁 ConfigDB 外 data 表加载(schema class_name 直引豁免) +hits=$(grep -rnE '\b(load|preload)[[:space:]]*\(|ResourceLoader\.load' core --include='*.gd' 2>/dev/null \ + | grep -v 'res://tests/' | wc -l) +if [ "$hits" -gt 0 ]; then echo "[static_scan] S3 WARNING(人工复核——class_name 直引误报需逐条排除): $hits 处"; grep -rnE '\b(load|preload)[[:space:]]*\(|ResourceLoader\.load' core --include='*.gd' 2>/dev/null; fi + +# TODO(正式工程落地后精化为扫描配置并升格为硬失败): +# S5 core/ 禁裸引用 ConfigDB/RngService 全局标识符(--script 模式 parse error;grep 近似误报多,需 AST 级或精确白名单) +# S6 core↔core 信号连接只许方法 Callable(禁 lambda 捕获 self);发射者禁反向引用 +# S7 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集(编排唯一性) +# S8 结算路径 await 零命中(B1 结算/编排文件集) + +exit $fail diff --git a/tests/unit/README.md b/tests/unit/README.md new file mode 100644 index 00000000..f251534a --- /dev/null +++ b/tests/unit/README.md @@ -0,0 +1,6 @@ +# tests/unit/ — 隔离单元测试 + +每系统一个子目录(`reward_economy/`、`board_generator/`…)。协议与命名见 `tests/README.md`。 + +**活样板**:`example_test.gd`(本目录)——第一个真实测试请以它为模板。 +**参考实现**:`prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd`(96 条断言的组织方式)。 diff --git a/tests/unit/example_test.gd b/tests/unit/example_test.gd new file mode 100644 index 00000000..df553db7 --- /dev/null +++ b/tests/unit/example_test.gd @@ -0,0 +1,37 @@ +# tests/unit/example_test.gd — 示例测试:项目测试协议的活样板(gate-check 硬性要求 ≥1 示例测试) +# 运行:godot --headless --script tests/unit/example_test.gd +# 协议要点(ADR-0004 + D2 冒烟协议): +# extends SceneTree 以获得 quit(code);断言失败 → push_error + quit(1);全绿 → quit(0) +# --script 模式无 autoload 且无全局标识符(裸引用 ConfigDB = parse error)—— +# 涉及表值的测试须显式实例化 ConfigDB,期望由表读值推导不写死 +# 第一个真实测试:复制本文件为 tests/unit//__test.gd 并替换占位断言 +extends SceneTree + +func _init() -> void: + var failures := 0 + failures += _check("headless SceneTree 协议可用(quit(code) 通道)", _test_headless_protocol()) + failures += _check("命名规范可执行:test_[scenario]_[expected]", _test_naming_convention_followed()) + if failures > 0: + push_error("[example_test] %d 项失败" % failures) + quit(1) + return + print("[example_test] all green") + quit(0) + +func _check(desc: String, ok: bool) -> int: + if not ok: + push_error("[example_test] FAIL: " + desc) + return 1 + print("[example_test] PASS: " + desc) + return 0 + +func _test_headless_protocol() -> bool: + # 占位断言:验证脚本本身在 SceneTree 上下文可执行。 + # 真实测试写法示例(引用不执行): + # var cfg := ConfigDBStub.new() # 显式实例化,非全局标识符 + # return cfg.action_economy.ap_per_round >= 1 + return true + +func _test_naming_convention_followed() -> bool: + # 占位断言:本文件函数名均以 test_ 前缀 + 场景_期望 结构(docs/code-conventions.md) + return true diff --git a/tools/gdd_lint.py b/tools/gdd_lint.py new file mode 100644 index 00000000..0c0b3c04 --- /dev/null +++ b/tools/gdd_lint.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python3 +r"""gdd_lint.py — GDD lean 格式门禁 + +用法: + python tools/gdd_lint.py # 检查 design/gdd/ 下全部系统级 GDD(默认范围) + python tools/gdd_lint.py ... # 检查指定文件 + python tools/gdd_lint.py --json # 机器可读输出(供 gate/CI 使用) + +规则(与 .claude/docs/templates/game-design-document.md 纪律一一对应): + R1 散文段落 ≤ 240 字(代码块/表格/列表/引用不计) + R2 表格字符占比 ≥ 60%(代码块不计入分母) + R3 正文禁止修订注记:~~删除线~~、"作废"、括号内"日期+评审/修订/拍板/裁决/核销"(版本历史节豁免) + R4 必须有「版本历史」节且含表格 + R5 流程/状态类章节必须含代码块图(字符画或 Mermaid) + R6 表格列数一致(含转义 \| 识别) + R7 括号嵌套跨度 ≤ 80 字(长括号 = 审计注记挤进正文的信号) + R8 核心规则表:规则列必须是"谁·何时·做什么"陈述句所在的可读表——说明/要点列 + 禁止出现编号引用(AC-x/F-x/EC-x/E-x/OQ-x/Rule-x/义务),引用只允许在依据类列 + R9 一页纸节点:任一标题节点的直接内容(到下一标题为止)≤ 80 行——装不下就拆子节点 +""" +import argparse +import json +import re +import sys +from pathlib import Path + +MAX_PARA = 240 +# 2026-09-14 放宽 60→30:lean 改革后的新模板(参考 analysis/design 两件套)允许 +# 短段落与编号人话规则——防铺开的职责改由 R1(段 ≤240 字)与 R9(节点 ≤80 行)承担, +# R2 降为兜底下限(防纯散文文档)。 +MIN_TABLE_RATIO = 30 +MAX_PAREN_SPAN = 80 + +DEFAULT_DIR = "design/gdd" +DEFAULT_EXCLUDE = {"game-concept.md", "game-pillars.md", "systems-index.md"} +VERSION_SECTION = "版本历史" +FLOW_SECTION_RE = re.compile(r"(流程|生命周期)") + +DATE_KEYWORD_RE = re.compile( + r"[((][^()()]*\d{4}-\d{2}-\d{2}[^()()]*" + r"(评审|修订|拍板|裁决|核销|门禁|作废|批)[^()()]*[))]" +) + + +def parse(text): + """按行解析:返回代码块行集合、版本历史节行集合、表格块列表、散文段落列表。""" + code_lines, version_lines, tables, paras = set(), set(), [], [] + cur_table, cur_para, cur_section = [], [], "" + in_code = False + for raw in text.splitlines(): + stripped = raw.strip() + if stripped.startswith("```"): + in_code = not in_code + code_lines.add(raw) + if cur_para: + paras.append(" ".join(cur_para)) + cur_para = [] + if cur_table: + tables.append(cur_table) + cur_table = [] + continue + if raw.startswith("## "): + cur_section = stripped.lstrip("#").strip() + if stripped.startswith("|"): + if cur_para: + paras.append(" ".join(cur_para)) + cur_para = [] + cur_table.append(raw) + if cur_section == VERSION_SECTION: + version_lines.add(raw) + continue + if cur_table: + tables.append(cur_table) + cur_table = [] + is_para = ( + stripped + and not in_code + and not stripped.startswith(("#", ">", "- ", "* ")) + and not re.match(r"^\d+[.、))]\s", stripped) + ) + if in_code: + code_lines.add(raw) + elif cur_section == VERSION_SECTION: + version_lines.add(raw) + elif is_para: + cur_para.append(stripped) + elif cur_para: + paras.append(" ".join(cur_para)) + cur_para = [] + if cur_para: + paras.append(" ".join(cur_para)) + if cur_table: + tables.append(cur_table) + return code_lines, version_lines, tables, paras + + +def unescaped_pipes(line): + return len(re.findall(r"(? 0: + depth -= 1 + if depth == 0: + spans.append(text[start : i + 1]) + return spans + + +MAX_NODE_LINES = 80 + + +def lint_file(path): + text = Path(path).read_text(encoding="utf-8") + code_lines, version_lines, tables, paras = parse(text) + issues, infos = [], [] + + # R9 一页纸节点:每个标题的直接内容 ≤ MAX_NODE_LINES 行 + in_code, node_name, node_count = False, None, 0 + for raw in text.splitlines(): + if raw.strip().startswith("```"): + in_code = not in_code + if not in_code and re.match(r"^#{1,6}\s", raw): + if node_name is not None and node_count > MAX_NODE_LINES: + issues.append( + f"R9 节点「{node_name}」直接内容 {node_count} 行(>{MAX_NODE_LINES})——超一页,拆为子节点" + ) + node_name = raw.lstrip("#").strip()[:40] + node_count = 0 + continue + if raw.strip() and node_name is not None: + node_count += 1 + if node_name is not None and node_count > MAX_NODE_LINES: + issues.append(f"R9 节点「{node_name}」直接内容 {node_count} 行(>{MAX_NODE_LINES})——超一页,拆为子节点") + infos.append(f"最大节点直接内容 {max(node_count, 0)} 行") + + # R1 段落长度 + for p in paras: + if len(p) > MAX_PARA: + issues.append(f"R1 散文段 {len(p)} 字(>{MAX_PARA}): {p[:50]}…") + + # R2 表格占比 + code_chars = sum(len(l) for l in code_lines) + table_chars = sum(len(l) for l in text.splitlines() if l.strip().startswith("|")) + total = max(len(text) - code_chars, 1) + ratio = int(100 * table_chars / total) + if ratio < MIN_TABLE_RATIO: + issues.append(f"R2 表格占比 {ratio}%(<{MIN_TABLE_RATIO}%)") + else: + infos.append(f"表格占比 {ratio}%") + + # R3 修订注记(版本历史节豁免;代码块豁免) + body_no_code = "\n".join( + l for l in text.splitlines() if l not in code_lines and l not in version_lines + ) + for m in re.finditer(r"~~.+?~~", body_no_code): + issues.append(f"R3 删除线残留: …{m.group(0)[:40]}…") + if re.search(r"作废", body_no_code): + line_no = next( + i + 1 + for i, l in enumerate(body_no_code.splitlines()) + if "作废" in l + ) + issues.append(f"R3 \"作废\"注记残留(第 {line_no} 行附近)") + for m in DATE_KEYWORD_RE.finditer(body_no_code): + issues.append(f"R3 正文修订注记: {m.group(0)[:50]}") + + # R4 版本历史 + if VERSION_SECTION not in text: + issues.append(f"R4 缺「{VERSION_SECTION}」节") + elif not any(l.strip().startswith("|") for l in version_lines): + issues.append(f"R4 「{VERSION_SECTION}」节无表格") + + # R5 流程章节须含代码块图 + cur_section, prev_section = "", "" + pending_flow, has_code = False, False + for raw in text.splitlines(): + stripped = raw.strip() + if raw.startswith("## "): + if pending_flow and not has_code: + issues.append(f"R5 「{prev_section or cur_section}」节无流程图(须含代码块字符画/Mermaid)") + prev_section, cur_section = cur_section, stripped.lstrip("#").strip() + pending_flow = bool(FLOW_SECTION_RE.search(cur_section)) and cur_section != VERSION_SECTION + has_code = False + elif stripped.startswith("```") and cur_section: + has_code = True + if pending_flow and not has_code: + issues.append(f"R5 「{cur_section}」节无流程图(须含代码块字符画/Mermaid)") + + # R6 表格列数一致 + for t_idx, table in enumerate(tables): + if len(table) < 2: + continue + base = unescaped_pipes(table[0]) + for row in table[1:]: + if unescaped_pipes(row) != base: + issues.append(f"R6 第 {t_idx + 1} 个表格列数不一致: {row.strip()[:50]}") + + # R8 核心规则表:编号引用不得混入说明/要点列(只允许在依据类列) + CITE_RE = re.compile(r"(AC-\d|EC\d|OQ\d|\bF\d|\bE\d+\b|Rule \d|义务)") + ALLOWED_HEADER_RE = re.compile(r"(#|编号|依据|来源|出处|对侧|归属|状态|覆盖|版本|日期|owner|Owner|级别)") + for t_idx, table in enumerate(tables): + if len(table) < 3: + continue + header_cells = [c.strip() for c in re.split(r"(?= len(header_cells): + continue + m = CITE_RE.search(cell) + if m: + issues.append( + f"R8 核心规则第 {t_idx + 1} 表:说明/要点列混入编号引用「{m.group(0)}」——应放依据列: {cell[:40]}…" + ) + break + + # R7 括号跨度 + longest = max((paren_spans(p) for p in paras), key=lambda s: max((len(x) for x in s), default=0), default=[]) + longest_span = max((len(s) for s in longest), default=0) + if longest_span > MAX_PAREN_SPAN: + issues.append(f"R7 最长括号跨度 {longest_span} 字(>{MAX_PAREN_SPAN})——疑似审计注记挤进正文") + infos.append(f"最长括号跨度 {longest_span} 字") + + return {"file": str(path), "issues": issues, "infos": infos, "table_ratio": ratio} + + +def main(): + ap = argparse.ArgumentParser(description="GDD lean 格式门禁") + ap.add_argument("files", nargs="*", help="待检查文件(默认 design/gdd/ 下系统级 GDD)") + ap.add_argument("--json", action="store_true", help="JSON 输出") + args = ap.parse_args() + + if args.files: + targets = [Path(f) for f in args.files] + else: + root = Path(DEFAULT_DIR) + targets = sorted( + p for p in root.glob("*.md") + if p.name not in DEFAULT_EXCLUDE and not re.search(r"review|report", p.name, re.I) + ) + if not targets: + print(f"未找到待检文件({DEFAULT_DIR}/)", file=sys.stderr) + sys.exit(2) + + results = [lint_file(p) for p in targets] + failed = [r for r in results if r["issues"]] + + if args.json: + print(json.dumps(results, ensure_ascii=False, indent=2)) + else: + for r in results: + mark = "❌ FAIL" if r["issues"] else "✅ PASS" + print(f"\n{mark} {r['file']} (表格占比 {r['table_ratio']}%)") + for i in r["infos"]: + print(f" · {i}") + for issue in r["issues"]: + print(f" ! {issue}") + print(f"\n共 {len(results)} 个文件,{len(failed)} 个不通过") + sys.exit(1 if failed else 0) + + +if __name__ == "__main__": + main() diff --git a/tools/hook_gdd_lint.py b/tools/hook_gdd_lint.py new file mode 100644 index 00000000..7f41feda --- /dev/null +++ b/tools/hook_gdd_lint.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""PostToolUse hook: after Write/Edit on a design document, run gdd_lint and +feed failures back to the writing agent. Never crashes; best-effort.""" +import json +import re +import subprocess +import sys +from pathlib import Path + +try: + payload = json.load(sys.stdin) +except Exception: + sys.exit(0) + +path = "" +try: + tool_input = payload.get("tool_input") or {} + path = tool_input.get("file_path") or "" +except Exception: + sys.exit(0) + +if not path or not re.search(r"design[\\/].*\.md$", path.replace("\\", "/")): + sys.exit(0) +if re.search(r"review|report|legacy-", Path(path).name, re.I): + sys.exit(0) +# 与 gdd_lint.DEFAULT_EXCLUDE 同源:索引/概念层文档不在门禁默认范围,钩子同样跳过 +if Path(path).name in {"game-concept.md", "game-pillars.md", "systems-index.md"}: + sys.exit(0) +# 设计推演件(analysis 两件套的另一半):以散文推演为主,不适用 GDD 格式门禁 +norm = path.replace("\\", "/") +if Path(path).name == "analysis.md" or "/analysis/" in norm: + sys.exit(0) +if not Path("tools/gdd_lint.py").exists(): + sys.exit(0) + +try: + result = subprocess.run( + [sys.executable, "tools/gdd_lint.py", path, "--json"], + capture_output=True, text=True, timeout=30, + ) + data = json.loads(result.stdout or "[]") + failures = [r for r in data if r.get("issues")] + if failures: + lines = ["[gdd_lint] 刚写入的文档未过格式门禁,请修复后再结束:"] + for r in failures: + for issue in r.get("issues", []): + lines.append(f" ! {issue}") + print("\n".join(lines), file=sys.stderr) + sys.exit(2) +except Exception: + pass +sys.exit(0)