From cb548776e014182876328b1b0588c1ce5ffd539f Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Tue, 15 Sep 2026 20:46:21 +0800 Subject: [PATCH] =?UTF-8?q?fix(slice):=20=E6=88=98=E6=96=97=E9=98=B6?= =?UTF-8?q?=E6=AE=B5=E4=B8=83=E9=A1=B9=E4=BF=AE=E5=A4=8D=20+=20=E9=A1=BA?= =?UTF-8?q?=E6=89=8B=E4=BF=AE=E4=B8=83=E9=A1=B9=20+=20=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=8F=A3=E5=BE=84=E6=94=B6=E7=BC=96=EF=BC=88=E5=90=AB=E5=85=A8?= =?UTF-8?q?=E4=BB=93=E8=AF=AD=E4=B9=89=E5=91=BD=E5=90=8D=E6=B8=85=E7=90=86?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 代码修复(垂直切片,冒烟 193/193 · 三探针全绿): - 遭遇流盐值修正(旧值超 int64 被截断,与 registry 黄金向量不符)+ 黄金向量断言入冒烟 - 完美排雷第二份按身份类型真实兑付;零兑付不发高光行;失败记"奖励未生效"行 - 轮内刷新图 provenance 守卫:刷新图清图不发清图奖励 - 可推理率求解器禁读雷位真值(no-peek)——L1/L2 实测 0.953/0.951、一次生成即达标 - 清图分类改传真实旗集(空旗集捷径会误判批次优先级) - 达标判定回归单一判据(可推理率 ≥ 及格线) - 等待期(Waiting)刷新通道整体拆除——非清图轮恒同图 顺手修:清图轮剩余 AP 当场清零 + 耗尽轮断言;degraded_product 降级标记;产物雷数 ≤N 断言; 明细流水"无来源即炸"及两层豁免成文;主角零值伤害行;窗口段三锚行(13→15 类行协议); 图标键 fail-fast + 补齐暴走/失衡两键。 文档:可选类注入递减基准改"按实际获取件数"、首件保证改"必注入"口径(+未挖到不补发待办); 教学位掷骰口径自洽;标错反噬三池收编(状态表四行→六行);行协议 13→15 与豁免清单回写 《战斗结算》与 registry,下游引用同步收敛。 另含:全仓旧代号→语义命名清理(注释/标识符,81 文件);CLAUDE.md 登记 Godot 路径与验证命令。 --- CLAUDE.md | 12 +- design/art/art-bible.md | 52 +- design/gdd/00-游戏概念.md | 12 +- design/gdd/systems/冒险循环/关卡节点地图.md | 3 +- design/gdd/systems/冒险循环/难度曲线与Boss.md | 16 +- design/gdd/systems/战斗循环/增益状态.md | 42 +- design/gdd/systems/战斗循环/奖励道具经济.md | 28 +- design/gdd/systems/战斗循环/战斗结算.md | 32 +- design/gdd/systems/战斗循环/战斗表现.md | 8 +- design/gdd/systems/战斗循环/敌人系统.md | 7 +- design/gdd/systems/排雷循环/地形变异.md | 7 +- design/gdd/systems/排雷循环/辅助道具.md | 2 +- design/gdd/systems/表现与支撑/音频.md | 4 +- design/registry/entities.yaml | 635 ++++++++--------- .../research/lore-nest-metaphor-2026-09-12.md | 70 +- .../research/peglin-comparison-2026-09-12.md | 16 +- .../proposal-reward-polarity-2026-09-13.md | 66 +- design/ux/battle.md | 234 +++--- design/ux/interaction-patterns.md | 38 +- docs/architecture/architecture.md | 4 +- docs/consistency-failures.md | 38 - docs/consistency-report-2026-09-08-b3.md | 31 - docs/consistency-report-2026-09-08.md | 25 - docs/consistency-report-2026-09-09-a1.md | 26 - docs/consistency-report-2026-09-09-b4.md | 60 -- docs/consistency-report-2026-09-12.md | 78 -- docs/doc-conventions.md | 3 +- docs/lean-gdd-sample-b1.md | 2 +- docs/playnote.md | 14 + docs/registry/architecture.yaml | 31 +- production/qa/bugs/BUG-0001.md | 85 --- production/qa/bugs/BUG-0002.md | 61 -- production/qa/bugs/BUG-0003.md | 63 -- production/qa/bugs/BUG-0004.md | 62 -- production/session-state/active.md | 125 ++-- .../dungeon-sweeper-vertical-slice/README.md | 6 +- .../core/action_economy.gd | 44 +- .../core/battle_orchestrator.gd | 429 ++++++----- .../core/board_data.gd | 13 +- .../core/board_generator.gd | 33 +- .../core/config_db.gd | 20 +- .../core/damage_math.gd | 6 +- .../core/level_runner.gd | 78 +- .../core/minesweeper_rules.gd | 66 +- .../core/reward_economy.gd | 34 +- .../core/reward_supply_controller.gd | 14 +- .../core/rng_service.gd | 18 +- .../core/status_container.gd | 26 +- .../core/unit_container.gd | 38 +- .../data/schemas/action_economy_table.gd | 4 +- .../data/schemas/board_gen_table.gd | 10 +- .../data/schemas/difficulty_table.gd | 8 +- .../data/schemas/enemy_table.gd | 6 +- .../data/schemas/hero_table.gd | 4 +- .../data/schemas/level_table.gd | 6 +- .../data/schemas/reward_config_table.gd | 2 +- .../data/schemas/reward_table.gd | 4 +- .../data/schemas/settlement_table.gd | 2 +- .../data/schemas/status_effect_table.gd | 2 +- .../data/schemas/variation_table.gd | 4 +- .../presentation/debug_session.gd | 34 +- .../presentation/hud_presenter.gd | 24 +- .../presentation/main.gd | 143 ++-- .../presentation/playtest_logger.gd | 14 +- .../presentation/views/ap_bar_view.gd | 4 +- .../presentation/views/battle_end_overlay.gd | 2 +- .../presentation/views/battle_log_view.gd | 11 +- .../presentation/views/board_fx_view.gd | 2 +- .../presentation/views/board_view.gd | 68 +- .../presentation/views/enemy_card_view.gd | 16 +- .../presentation/views/hero_card_view.gd | 2 +- .../views/hero_info_panel_view.gd | 10 +- .../views/inventory_panel_view.gd | 10 +- .../presentation/views/legend_overlay.gd | 4 +- .../presentation/views/level_strip_view.gd | 4 +- .../presentation/views/status_slot_row.gd | 33 +- .../presentation/views/tune_panel.gd | 2 +- .../smoke/auto_player.gd | 64 +- .../smoke/damage_probe.gd | 96 +-- .../smoke/dump_battle.gd | 40 +- .../smoke/experiment_density.gd | 4 +- .../smoke/level_autoplay.gd | 70 +- .../smoke/level_monte_carlo.gd | 6 +- .../smoke/monte_carlo.gd | 38 +- .../smoke/reward_expectation.gd | 14 +- .../smoke/smoke_all.gd | 671 ++++++++++++------ .../smoke/ui_click_probe.gd | 82 +-- .../smoke/ui_probe.gd | 30 +- .../smoke/ui_tune_probe.gd | 2 +- 89 files changed, 2139 insertions(+), 2230 deletions(-) delete mode 100644 docs/consistency-failures.md delete mode 100644 docs/consistency-report-2026-09-08-b3.md delete mode 100644 docs/consistency-report-2026-09-08.md delete mode 100644 docs/consistency-report-2026-09-09-a1.md delete mode 100644 docs/consistency-report-2026-09-09-b4.md delete mode 100644 docs/consistency-report-2026-09-12.md create mode 100644 docs/playnote.md delete mode 100644 production/qa/bugs/BUG-0001.md delete mode 100644 production/qa/bugs/BUG-0002.md delete mode 100644 production/qa/bugs/BUG-0003.md delete mode 100644 production/qa/bugs/BUG-0004.md diff --git a/CLAUDE.md b/CLAUDE.md index 050c1dda..c15cbdfc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,10 +6,20 @@ ## Technology Stack - **Engine**: Godot 4.7.2 +- **Godot 可执行文件(本机固定路径,2026-09-15 登记)**:`D:\code\godot\Godot_v4.7.2-stable_win64_console.exe`(headless / 命令行一律用 console 版;编辑器版为同目录 `Godot_v4.7.2-stable_win64.exe`) - **Language**: GDScript - **Build System**: SCons (engine), Godot Export Templates - **Asset Pipeline**: Godot Import System + custom resource pipeline +## 运行与验证(垂直切片) + +工作目录 = 切片目录本身(含 `project.godot` 的那一层): + +- 冒烟(全量 headless 断言):`D:\code\godot\Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/smoke_all.gd` +- 场景级探针(呈现层回归):`…console.exe --headless --path . res://smoke/ui_probe.tscn`;另有 `ui_tune_probe.tscn`(数值调整板)与 `ui_click_probe.tscn`(合成鼠标事件行为探针) +- 调参期批量工具:`--script res://smoke/level_monte_carlo.gd`(关卡期望扫描)· `--script res://smoke/reward_expectation.gd`(奖励获取期望) +- 交付前纪律:smoke + 全部场景探针全绿(见下方代码规范·测试条) + ## Engine Version Reference @docs/engine-reference/godot/VERSION.md @@ -38,7 +48,7 @@ **在编写、修改任何代码之前,必须先通读 `docs/code-conventions.md`**——该文档由用户指出的真实陋习与缺陷逐条成文,含结构/契约/测试/语言/数值/流程六类硬性纪律与本项目反面案例对照表。摘要(全文以文档为准): - **结构**:禁止上帝对象;功能以类封装;views →(信号)→ 组合根 →(写调用)→ core 依赖单向;核心写调用只在组合根发生 -- **命名**:领域命名,禁止系统 ID 前缀(a1_/b1_/c1_)进入文件名与类名;核心文件头注必须带「实现 GDD」溯源行;禁止事实约定——未成文的约定当场补条文 +- **命名**:领域命名,禁止系统 ID 前缀(a1_/b1_/c1_)进入文件名与类名(子系统中文规范名 ↔ 英文代号对照表见 `design/gdd/02-系统清单.md`);核心文件头注必须带「实现 GDD」溯源行;禁止事实约定——未成文的约定当场补条文 - **契约**:信号/事件契约成文,状态机每条迁移路径都必须发事件(缺事件 = 软锁);跨层标识符语义单一(位置 ≠ 句柄);模态覆盖层打开必须隐藏遮挡物 - **测试**:每个可交互 UI 区域必须配套场景级探针(只有 core AC 不算有测试);缺陷修复必须沉淀回归断言;提交前 smoke + 全部场景探针全绿 - **数值**:期望值必须标明尺度(每格/每板/每战/每关);调参先测量后复测;单尺度校准必须跨尺度推演;用户拍板外的细节零发挥,列"待拍板"等裁决 diff --git a/design/art/art-bible.md b/design/art/art-bible.md index e7ce111a..fec2195b 100644 --- a/design/art/art-bible.md +++ b/design/art/art-bible.md @@ -61,7 +61,7 @@ 3. **焦点预算成立(§3.4)**:任意一格至多一个完整凸形;数字所在格无第二焦点。 4. **biome 换皮抽测(§4.3)**:变体组中数字墨三档、旗朱、危险红、奖励金、小人蓝恒定不动;变体格底仍过第 1 项指标。 -**判定与回退**:第 1 项不过 → 按原则二设计测试回退——改氛围色,不改数字(降纹理密度/提墨色浓度皆可,禁止缩字号换字形逃避);第 2–4 项不过 → 修形不修数。样张通过 = 砖面形态与色板冻结;新增 biome/变异色板须重跑本测试(§4.3)。执行时机:正式砖面资产开工前;D1 GDD 以本节为引用依据(2026-09-07 门禁:样张测试并入 art-bible 执行)。 +**判定与回退**:第 1 项不过 → 按原则二设计测试回退——改氛围色,不改数字(降纹理密度/提墨色浓度皆可,禁止缩字号换字形逃避);第 2–4 项不过 → 修形不修数。样张通过 = 砖面形态与色板冻结;新增 biome/变异色板须重跑本测试(§4.3)。执行时机:正式砖面资产开工前;《HUD框架》以本节为引用依据(2026-09-07 门禁:样张测试并入 art-bible 执行)。 --- @@ -81,7 +81,7 @@ ### 2.2 轮末自动战斗结算 -- **主情绪**:延迟兑现的清算爽感——扫雷之因逐行兑现为战果;若 B1/B4 落实「完美排雷」类高光兑现(创意风险 #2,待 GDD 定夺),以其为全场喝彩级的最高反馈档 +- **主情绪**:延迟兑现的清算爽感——扫雷之因逐行兑现为战果;若 战斗结算/奖励道具经济 落实「完美排雷」类高光兑现(创意风险 #2,待 GDD 定夺),以其为全场喝彩级的最高反馈档 - **色彩性格**:明度不变,色彩转向金橙主导、饱和拉高;数字色转译为弹道色沿行推进 - **氛围形容词**:酣畅、递进、一气呵成 - **能量**:7/10,随连击爬升 @@ -151,7 +151,7 @@ ### 3.1 角色剪影哲学 -硬规则:**所有角色自带轮廓通道**——亮色描边+落影底盘,识别不依赖所在背景。敌人不占格,剪影不得借格底色成立;同一套剪影因此在棋盘、结算画面与任一战斗舞台通用,不为 B5 未决的舞台问题付重画成本。←已锁事实 +硬规则:**所有角色自带轮廓通道**——亮色描边+落影底盘,识别不依赖所在背景。敌人不占格,剪影不得借格底色成立;同一套剪影因此在棋盘、结算画面与任一战斗舞台通用,不为 战斗表现 未决的舞台问题付重画成本。←已锁事实 五类 archetype 以高宽比/轮廓凹凸数/锐角数一眼区分: @@ -165,7 +165,7 @@ - **地砖**:一切环境体由同一圆角方形衍生:未开=完整凸砖,已开=同形缺角碎裂,雷=碎裂加巢穴纹(被惊动的巢穴——2026-09-12 换轴,旧称"机关纹"退役);只以"完整度"叙事,不为状态新造形。←原三;P4 - **墙体**:圆角砌块堆叠,禁尖刺装饰——尖刺专属危险语义,装饰不得伪装逻辑差异。←原三 -- **变异格形状通道**(双通道之"形",与色无关):底砖圆角外轮廓不变,砖面内缘绘一圈定宽专属几何纹带(纹带形全作不与他类共用),砖心留白予数字与旗;验收标准=**灰度截图下仍可分类**。纹带对应哪些变异、种类清单,待 A4 GDD 定夺。←原一(纹理位次第 4,让位于数字/小人/旗);原三 +- **变异格形状通道**(双通道之"形",与色无关):底砖圆角外轮廓不变,砖面内缘绘一圈定宽专属几何纹带(纹带形全作不与他类共用),砖心留白予数字与旗;验收标准=**灰度截图下仍可分类**。纹带对应哪些变异、种类清单,待《地形变异》定夺。←原一(纹理位次第 4,让位于数字/小人/旗);原三 - **雾格资产规格**:内容封禁期暂缓,雾重设计批再启(占位一行,不展开设计)。 ### 3.3 UI 形状语法 @@ -194,7 +194,7 @@ - **奖励金 `#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 +**对比硬指标(可测)**:一切格底槽(含全部 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 轮廓通道承担。←原二;地形变异 AC-9 ### 4.2 语义色彩规则 @@ -202,18 +202,18 @@ - **金只说收益与兑现**(奖励、结算、胜利)。禁:金不得作危险障碍色、不上格底、不作日常 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,本节只定模板: +**换皮只换四槽,永不变信**。模板按 biome_tag 键索引,棋盘生成产物 `board_biome` 标签直接消费;清单与词表 owner 待 关卡节点地图/地形变异/奖励道具经济,本节只定模板: - `tile_closed` 未开底 / `tile_open` 已开底:L 与饱和同 4.1 硬指标,避禁入色相带 - `wall_ambient` 墙雾氛围 tint:可带主题色相,L≥0.30 -- `variant_tint` 变异纹带染槽:只改色相不改纹形(对应哪些变异待 A4) +- `variant_tint` 变异纹带染槽:只改色相不改纹形(对应哪些变异待地形变异) -**不变槽**:数字墨三档、旗朱、危险红、奖励金、小人蓝、紫赤、绿——**信息与语义层全 biome 恒定**,换挡只动色相饱和、不动明度档(§2 硬规则)。新增色板须重跑 §1.4 格子样张(灰度可辨+对比抽测)。←原三/原二;A4 AC-9 +**不变槽**:数字墨三档、旗朱、危险红、奖励金、小人蓝、紫赤、绿——**信息与语义层全 biome 恒定**,换挡只动色相饱和、不动明度档(§2 硬规则)。新增色板须重跑 §1.4 格子样张(灰度可辨+对比抽测)。←原三/原二;地形变异 AC-9 ### 4.4 UI 色板 @@ -233,22 +233,22 @@ 6. 小人蓝 vs 青墨数字:明度悬殊 + 小人轮廓与底盘,无需另加 7. **UI 红/金强调对:纹带不管面板——需补图标+文案冗余,且破坏性确认不得与常规确认同位(位置通道)** ←原三 8. 失败暮蓝 vs 小人蓝:状态互斥,不承载区分,无需冗余 -9. 图标键族(§4.6):图标身份全靠纹样形状通道(一键一纹样,色相不承载身份),未开格预读徽章为墨族单色;极性金/红对由框形满/缺冗余——色相全失效时图标仍可逐一辨认与定极性。←原三;B3 D1×4② +9. 图标键族(§4.6):图标身份全靠纹样形状通道(一键一纹样,色相不承载身份),未开格预读徽章为墨族单色;极性金/红对由框形满/缺冗余——色相全失效时图标仍可逐一辨认与定极性。←原三;增益状态×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 键恒定同款纪律),增键走本节修订,禁运行时换图。 +> **白话**:图标键 = "哪个数据行配哪枚图标"的固定对照表——效果、奖励、格面类型、敌种各自的数据表主键(effect_id / reward_id / cell_type / enemy_id)直接作图标键,一枚键配一枚专属纹样。**本节冻结的是键与通道的分配,不冻结终稿美术**:纹样造型、具体色值、动画帧归格子样张(§1.4)与 asset-spec 批。先行冻结是为了 HUD框架/战斗表现开工不必等美术终稿;映射一经冻结**全局恒定、跨 run 禁重掷**(敌人系统 Rule 8③ 与奖励道具经济 reward_id 键恒定同款纪律),增键走本节修订,禁运行时换图。 > -> 承接义务:B3 D1×4②(effect_id→icon 映射键契约)· B4 D1×5①(奖励身份恒定可读)· B2 D1×4①②(敌种图像线索清单成文 + 全局恒定)· 极性提案 §3 D1(类型图标/极性视觉语言)。引用方:D1、B5、D5(教学)、B4 库存呈现。 +> 承接义务:增益状态×4②(effect_id→icon 映射键契约)· 奖励道具经济×5①(奖励身份恒定可读)· 敌人系统×4①②(敌种图像线索清单成文 + 全局恒定)· 极性提案 §3(类型图标/极性视觉语言)。引用方:HUD框架、战斗表现、教学引导、奖励道具经济库存呈现。 **通道分配(全键族通用,双通道之"形+色")**: - **身份 = 形状纹样键**:一键一纹样,全作不复用;**色相不承载身份**(七效果不七色——防色相爆炸,亦防色弱失效)。 -- **极性 = 色相槽 + 框形**:有利兑付 = 金族(§4.2"金只说收益"),有害兑付 = 红族("红只说危险")——零新增语义色;框形冗余:满框对缺框(§3.4"收益用满与圆,危险用刺与缺")。极性判定的数据源 = B1 SettlementTable 效果语义(非 magnitude 符号——B3 D1×4③),本契约只锁视觉通道、不锁数据映射。 +- **极性 = 色相槽 + 框形**:有利兑付 = 金族(§4.2"金只说收益"),有害兑付 = 红族("红只说危险")——零新增语义色;框形冗余:满框对缺框(§3.4"收益用满与圆,危险用刺与缺")。极性判定的数据源 = 战斗结算 SettlementTable 效果语义(非 magnitude 符号——增益状态×4③),本契约只锁视觉通道、不锁数据映射。 - **徽章 = 附属小图标**:格面徽章(类型/奖励身份)为小尺寸记号,栈位在旗之下、变异纹理之上(§1.2 优先级栈),不得构成格内第二焦点(§3.4)。 - **未开格预读徽章 = 墨族单色**(§4 总纲"墨为信"):类型徽章不上语义色相,防色相泄漏雷/安属性(极性提案 R3"不呈现极性、不呈现雷/安"的色彩侧延伸);兑付面与库存徽章可用语义族色,终稿归样张批。 -#### 4.6.1 效果图标键(B3 数据六行 = 2026-09-13 切片数据批实态;B3 GDD 四行→六行入文随其修订批) +#### 4.6.1 效果图标键(增益状态数据六行 = 2026-09-13 切片数据批实态;增益状态 GDD 四行→六行入文随其修订批) | 键(effect_id) | display_name | 纹样语义锚(形状必须让人联想到) | 极性 | |---|---|---|---| @@ -260,10 +260,10 @@ | off_guard | 失衡 | 我方失衡承伤增——踉跄/破绽 | 有害 | | sapped | 乏力 | 我方输出被削弱——蔫软(weaken 的镜像) | 有害 | -- **sapped = 极性提案 OQ-3 新增负向行**(提案 §2,已拍板待实施)——**键先冻结,行未入 StatusEffectTable 前禁引用**(B3 新数据行随提案实施批,悬空引用按 fail-fast 同款纪律处理)。 -- 呈现位 = HUD 状态槽(B3 D1×4①);堆叠层数呈现归 D1/B5,不入本契约。 +- **sapped = 极性提案 OQ-3 新增负向行**(提案 §2,已拍板待实施)——**键先冻结,行未入 StatusEffectTable 前禁引用**(增益状态新数据行随提案实施批,悬空引用按 fail-fast 同款纪律处理)。 +- 呈现位 = HUD 状态槽(增益状态×4①);堆叠层数呈现归 HUD框架/战斗表现,不入本契约。 -#### 4.6.2 奖励徽章键(B4 GDD 默认根八行 = 2026-09-11 两级奖励批登记值) +#### 4.6.2 奖励徽章键(奖励道具经济 GDD 默认根八行 = 2026-09-11 两级奖励批登记值) | 键(reward_id) | category | 徽章语义锚 | 备注 | |---|---|---|---| @@ -274,13 +274,13 @@ | 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 键并列预留,启用随批 | +| clear_knife | damage_item(清图槽) | 清图奖励刀 | **切片批已更替为 clear_blast**(一次性群伤 40,instant_aoe——奖励道具经济 GDD 修订待批);clear_blast 键并列预留,启用随批 | - 即时类四行徽章只在已开格/结算明细呈现(零库存);可选类三件(knife_t1/knife_adv/heal_pack)为库存徽章,互辨性是硬需求。 -- 格面预读徽章按**家族类型**(§4.6.3)呈现、不按行(提案 R1"图标呈现家族类型");可选类格图标 = 家族类型 + 道具徽章语言区分(呈现细则归 D1 义务)。 +- 格面预读徽章按**家族类型**(§4.6.3)呈现、不按行(提案 R1"图标呈现家族类型");可选类格图标 = 家族类型 + 道具徽章语言区分(呈现细则归 HUD框架 义务)。 - **池归属(safe_cell / active_inject / clear_default)不影响键**——切片供给控制器批把三件可选类移注入池、清图行更替,徽章键照旧(该漂移已在交付报告冲突清单列示)。 -#### 4.6.3 格面类型图标键(五类——极性提案 R1/§3 D1;只冻结键契约,不设计终稿) +#### 4.6.3 格面类型图标键(五类——极性提案 R1/§3;只冻结键契约,不设计终稿) | 键(cell_type) | 类型 | 纹样语义锚 | |---|---|---| @@ -291,21 +291,21 @@ | weaken | 削弱族 | 钝化/压制(提案新增正向类型) | - **呈现约束(提案 R3,冻结)**:全部未开格呈现类型徽章、不呈现极性、不呈现雷/安——推雷仍是扫雷本征推理;类型分布即先验(提案 R3 拍板),**徽章误读率 = 提案 §4 指标 3**,样张批须做误读抽测。 -- **状态声明**:提案 v3"已拍板待实施"——本契约先行冻结防 D1/B5 设计倒挂;weaken 入池、雷格 roll 等机制随提案实施批(B4 pool_tag/cell_type/polarity 扩列),键集五值封闭、扩键走本节修订。 +- **状态声明**:提案 v3"已拍板待实施"——本契约先行冻结防 HUD框架/战斗表现设计倒挂;weaken 入池、雷格 roll 等机制随提案实施批(奖励道具经济 pool_tag/cell_type/polarity 扩列),键集五值封闭、扩键走本节修订。 - 与 §4.6.1 的关系:同语义域的键(attack↔attack_up 等)可共族设计,键分立、纹样须可区分(格面小记号级对槽内图标级)。 -#### 4.6.4 敌种线索键(B2 Rule 8 契约化;映射键 = enemy_id) +#### 4.6.4 敌种线索键(敌人系统 Rule 8 契约化;映射键 = enemy_id) | 线索通道 | 冻结内容 | |---|---| -| 行为拍姿态 | 三行为各一恒定视觉姿态键:attack / charge / heavy;**charge 姿态必须含可辨识的"蓄力"记号**(读怪核心课:"它在蓄力,这轮踩雷不挨打"——charge 轮伏击池蒸发,B2 F2);同一敌种同一行为恒同一姿态(Rule 8①) | +| 行为拍姿态 | 三行为各一恒定视觉姿态键:attack / charge / heavy;**charge 姿态必须含可辨识的"蓄力"记号**(读怪核心课:"它在蓄力,这轮踩雷不挨打"——charge 轮伏击池蒸发,敌人系统 F2);同一敌种同一行为恒同一姿态(Rule 8①) | | 环长直觉 | 敌种具备哪些姿态本身就是环长线索:有蓄力拍 = 环里空一拍(可以贪),没有蓄力态 = 每拍都动手(grub 单拍环)——MVP 双敌种双环最长 3 拍(Rule 8②) | | 强度档位 | 体量通道:§3.1 archetype 体量/垂直延伸("大个子多半有重击") | -| 预告时序 | 呈现"即将执行的一拍",**挖掘期全程可见**,结算不另设独立意图播片(Rule 8⑤——B2 承重项) | +| 预告时序 | 呈现"即将执行的一拍",**挖掘期全程可见**,结算不另设独立意图播片(Rule 8⑤——敌人系统承重项) | | 恒定义务 | 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 修订批入本表。 +- MVP 键集 = {grub, ambusher}(敌人系统 Rule 3 定稿;视觉状态清单:grub 1 态攻击 / ambusher 3 态攻击·蓄力·重击)。**资产需求下限三类**(敌人系统 Visual Req 2,呈现归 HUD框架/战斗表现):受击反馈(两敌种可共用一组)、死亡表现(不得直接消失)、顺位标记(敌人系统×4③,同等 blocking)。 +- 精英/Boss 编码位 = 敌人系统 OQ7 预登记(关卡节点地图 定稿强制核对);表现侧现成约束:§2.6 紫赤色相独占 + §3.1 Boss 形状独占;键集扩容随敌人系统/难度曲线与Boss 修订批入本表。 - 剪影通用性:敌不占格,剪影不得借格底色成立(§3.1)——棋盘/结算/任一战斗舞台同一套。 --- diff --git a/design/gdd/00-游戏概念.md b/design/gdd/00-游戏概念.md index 0d2a64a3..f8c235a7 100644 --- a/design/gdd/00-游戏概念.md +++ b/design/gdd/00-游戏概念.md @@ -47,12 +47,12 @@ | 专名 | 是什么 | 出处条目 | |---|---|---| -| 显影尘 | 信息类辅助道具 | a5_aux_items | -| 工兵令 | 加行动点的辅助道具 | a5_aux_items | -| 探宝罗盘 | 预读奖励身份的辅助道具 | a5_aux_items | -| 探巢摆钟 | 探知巢穴位置的辅助道具 | a5_aux_items | -| 地穴全图卷 | 全图级稀有辅助道具 | a5_aux_items | -| 爆破工兵包 | 行动点 + 探巢的稀有辅助道具 | a5_aux_items | +| 显影尘 | 信息类辅助道具 | aux_items | +| 工兵令 | 加行动点的辅助道具 | aux_items | +| 探宝罗盘 | 预读奖励身份的辅助道具 | aux_items | +| 探巢摆钟 | 探知巢穴位置的辅助道具 | aux_items | +| 地穴全图卷 | 全图级稀有辅助道具 | aux_items | +| 爆破工兵包 | 行动点 + 探巢的稀有辅助道具 | aux_items | | 地精 / 熊地精 / 食人魔酋长 | 首发三敌种(末者为 Boss) | enemy | | 第一层 · 苔藓回廊 | 首关层名 | level_table | diff --git a/design/gdd/systems/冒险循环/关卡节点地图.md b/design/gdd/systems/冒险循环/关卡节点地图.md index b22c54be..149cb2a0 100644 --- a/design/gdd/systems/冒险循环/关卡节点地图.md +++ b/design/gdd/systems/冒险循环/关卡节点地图.md @@ -146,7 +146,7 @@ | 条目 | 内容 | |---|---| | 精英与 Boss 编码位 | 敌人表按(关卡序号 × 节点深度 × 敌种)存放,现行键结构没有"精英 / Boss"的编码位;同深度要出第二个敌种,会被每坐标恰一敌种的加载校验拦下。本文定稿时须与键结构改键一并裁决,走数据配置的接口漂移通道;棋盘生成侧的第三轴登记约定(Boss 走最大深度行、精英与 Boss 命名空间预留)同批重审 | -| 多敌数量的消费面 | 敌人表的数量区间在开战时掷定、教学位节点固定不掷——这组既有登记的消费面复验归本文定稿时核对 | +| 多敌数量的消费面 | 敌人表的数量区间在开战时掷定(教学位节点同样掷骰)——这组既有登记的消费面复验归本文定稿时核对 | ## 待设计问题清单 @@ -164,3 +164,4 @@ | 版本 | 日期 | 说明 | |---|---|---| | v0.1 | 2026-09-15 | 自归档线索整合为范围草案(旧体系未单独成文) | +| v0.2 | 2026-09-15 | 待拍板"多敌数量的消费面"条目同步教学位掷骰口径(随[难度曲线与Boss](难度曲线与Boss.md)同批修订):删除"教学位节点固定不掷" | diff --git a/design/gdd/systems/冒险循环/难度曲线与Boss.md b/design/gdd/systems/冒险循环/难度曲线与Boss.md index cd755178..e0ba0797 100644 --- a/design/gdd/systems/冒险循环/难度曲线与Boss.md +++ b/design/gdd/systems/冒险循环/难度曲线与Boss.md @@ -128,10 +128,17 @@ boss_hp ≈ round( Σ普通战斗敌方生命期望 × r_boss ) r_boss | 变量 | 现行值 | 说明 | |---|---|---| -| Σ普通战斗敌方生命期望 | 153.5(第一关,含数量区间期望) | 全关非 Boss 节点敌方生命合计 | +| Σ普通战斗敌方生命期望 | 153.5(第一关,按数量区间期望逐行合计) | 全关非 Boss 节点敌方生命合计,逐数见下方验算 | | r_boss 现值 | 150 ÷ 153.5 ≈ 0.98,带内 | 对应食人魔酋长 150 生命(provisional) | | 加难走廊 | [1.1, 1.35](对应 Boss 生命 180–207) | 通关率压带的首选杠杆候选;扫描未执行,处 provisional-pending-scan(见「待拍板」) | +Σ 的 153.5 逐数闭合如下——数量走开战掷骰的区间期望(区间中点),教学位地精同样掷骰、按期望 1.5 只计入: + +``` +Σ = 1.5×24(D0 地精 ×[1,2])+ 2.5×13(D1 地精一伙 ×[2,3])+ 1×40(D2 熊地精 ×1)+ 2.5×18(D3 熊地精双雄 ×[2,3]) + = 36 + 32.5 + 40 + 45 = 153.5 +``` + Boss 战的棋盘用本关最大板形(第一关为 12×12),板参数仍受[棋盘生成](../排雷循环/棋盘生成.md)的可推理率绝对锚约束——加难不许加到不可推理。Boss 战胜利即关卡通关,编排归[冒险结构](冒险结构.md);登场仪式感归[战斗表现](../战斗循环/战斗表现.md)。 ### 护甲克制:本文锁行值,矩阵本体归战斗结算 @@ -178,7 +185,7 @@ Boss 战的棋盘用本关最大板形(第一关为 12×12),板参数仍 ## P0 最小闭环 1. 设计期:按两条轴把行值直填敌人、主角、棋盘参数、奖励各数据表——运行时零公式,游戏只读表。 -2. 冒险推进到战斗节点,[战斗结算](../战斗循环/战斗结算.md)发起战斗:开战时按行值的数量区间与攻击浮动,用封闭随机流掷定本战实际值(教学位与新敌种首遇位固定不掷)。 +2. 冒险推进到战斗节点,[战斗结算](../战斗循环/战斗结算.md)发起战斗:开战时按行值的数量区间与攻击浮动,用封闭随机流掷定本战实际值;教学位与新敌种首遇位同样走这套掷骰——教学位地精的数量也是掷出来的区间([1,2]),只是区间要求它在任何掷骰结果下都满足绿档。 3. 各表按坐标行值实例化敌人与棋盘,整场战斗按既有机制运作——本系统不在场。 4. 战斗产出实际战绩(承伤、输出、治疗、通关余量),供调参期消费。 5. 调参期批量扫描复算:通关率带、三档区间判定、通关余量带、输出占比带、关级恒等式。 @@ -207,14 +214,14 @@ Boss 战的棋盘用本关最大板形(第一关为 12×12),板参数仍 | 坐标 | 敌种(数据键) | 生命 | 攻击(浮动区间) | 数量 | 行为环 | 护甲 | |---|---|---|---|---|---|---| -| 第一关 D0(教学位) | 地精 goblin | 24 | 7 [4,10] | 固定 1 | 攻击 | 无甲 | +| 第一关 D0(教学位) | 地精 goblin | 24 | 7 [4,10] | [1,2] | 攻击 | 无甲 | | 第一关 D1 | 地精 goblin | 13 | 5 [2,8] | [2,3] | 攻击 | 无甲 | | 第一关 D2(首遇固定) | 熊地精 bugbear | 40 | 9 [6,12] | 1 | 攻击/蓄力/重击 | 甲 | | 第一关 D3 | 熊地精 bugbear | 18 | 6 [3,9] | [2,3] | 攻击/蓄力/重击 | 甲 | | 第一关 D4(Boss) | 食人魔酋长 ogre_chief | 150 | 9 [6,12] | 1 | 攻击/攻击/蓄力/重击 | 重甲 | | 第二关 D0(锚行) | 熊地精 bugbear | 30 | 7 [4,10] | 1 | 攻击/蓄力/重击 | 甲 | -重击倍率全员 2(provisional);单轮伏击授予上限 4(已定稿)。攻击浮动开战时掷定一次、整战持久;数量区间同流掷定、教学位与首遇位豁免。 +重击倍率全员 2(provisional);单轮伏击授予上限 4(已定稿)。攻击浮动开战时掷定一次、整战持久;数量区间同流掷定,教学位(地精 ×[1,2])与首遇位都不豁免——没有区间上界的行自然取定值,那不是豁免,是区间宽度为零。 主角表与棋盘参数表(provisional): @@ -271,3 +278,4 @@ Boss 战的棋盘用本关最大板形(第一关为 12×12),板参数仍 | 版本 | 日期 | 改动 | |---|---|---| | v0.1 | 2026-09-15 | 自归档 GDD 压缩改写 | +| v0.2 | 2026-09-15 | 教学位掷骰自洽(用户拍板:代码不动、文档对齐):删除"教学位/首遇位固定不掷"豁免口径,教学位地精数量明确为开战掷骰区间 [1,2];D0 行数量列改 [1,2];Boss 血量锚 Σ=153.5 补逐数验算(区间期望口径下闭合) | diff --git a/design/gdd/systems/战斗循环/增益状态.md b/design/gdd/systems/战斗循环/增益状态.md index d4cab584..ab233b09 100644 --- a/design/gdd/systems/战斗循环/增益状态.md +++ b/design/gdd/systems/战斗循环/增益状态.md @@ -6,7 +6,7 @@ ## 设计目的 -战斗里有一类效果不在当场一次性用掉,而要管上几次结算:挖开安全格拿到的主角增益、标中旗带来的怪物削弱、踩雷喂给怪物的增强,都是这类。本文把它们统称"状态":发给主角的叫增益,发给敌人的负面效果叫减益;惊动巢穴喂给怪物的增强同样是状态——三个方向共用一本账。 +战斗里有一类效果不在当场一次性用掉,而要管上几次结算:挖开安全格拿到的主角增益、标中旗带来的怪物削弱、踩雷喂给怪物的增强、标错旗招来的反噬,都是这类。本文把它们统称"状态":发给主角的有益效果叫增益(标错反噬的失衡是个例外——它是发给主角的减益),发给敌人的负面效果叫减益;惊动巢穴或反噬喂给怪物的增强同样是状态——几个方向共用一本账。 增益状态就是这本账:一个容器收下全部状态,用同一套规矩管理——发入、叠层(同一种效果重复拿到时层数加一)、衰减(每次结算后剩余次数减一,减到零效果消失)、清空(战斗结束全部清除)。没有它,几类效果得各写一套计时逻辑,结算明细也无法逐行指回"这效果是我哪步棋带来的"。容器管机制、不管数值从哪来:某个效果该多大,由各收益系统的表去定,容器只照章入账、按次衰减。 @@ -43,9 +43,9 @@ ## 核心规则 -### 一个容器管三向:三类效果同账本 +### 一个容器管全部方向:增益减益同账本 -主角的增益、敌方受到的削弱、敌方自带的增强——三个方向的效果在容器里是同一种东西,都叫状态实例(容器里的一份效果,带层数与剩余次数),走同一套受理、合并、倒数、清除的语义。踩雷的惩罚与标中旗的收益共用同一本账,玩家能在同一种读法下看到雷的两面:一边喂强敌人,一边拆它的皮。 +主角的增益与减益、敌方的削弱与增强——各个方向的效果在容器里是同一种东西,都叫状态实例(容器里的一份效果,带层数与剩余次数),走同一套受理、合并、倒数、清除的语义。踩雷的惩罚与标中旗的收益共用同一本账,玩家能在同一种读法下看到雷的两面:一边喂强敌人,一边拆它的皮。 容器四条硬规矩: @@ -62,7 +62,7 @@ 状态只在轮末结算时发入容器。挖掘期的挖格、插旗、踩雷都不直接改容器——棋盘只上报"发生了什么"的事实,战斗结算在每轮结算时把事实统一兑换成状态发进来。这个授予入口是唯一的:棋盘和奖励背包都没有直通通道。 -每笔状态随身携带归因记录:来源类别四选一、来自哪个格子、发生在第几轮。结算明细因此能逐行回答"这效果是我哪步棋带来的"。 +每笔状态随身携带归因记录:来源类别五选一、来自哪个格子、发生在第几轮。结算明细因此能逐行回答"这效果是我哪步棋带来的"。 | 来源类别 | 什么时候发生 | 发给谁 | |---|---|---| @@ -70,6 +70,7 @@ | 标中旗 | 轮末旗判定标中(巢穴被控制) | 敌方,削弱 | | 踩雷 | 挖开惩罚格、惊动巢穴 | 敌方,增强 | | 清图奖励 | 清图奖励槽含增益时 | 随行定,路径归[奖励道具经济](奖励道具经济.md)定稿 | +| 标空旗 | 轮末旗判定标空(标了但格不是雷) | 反噬三选一:敌方暴走、敌方伏击或主角失衡;载体裁决归[战斗结算](战斗结算.md)的标错反噬机制 | 容器是只记账、不裁决的仓库:上游叫它施加几次,它就入账几次。同一笔事实被重复转化两次,就入两层——它不去重、不合并事实,"事实到授予恰好一次"由战斗结算保证。同轮多笔授予按调用方给定的次序先后入列,容器保证入账顺序稳定可读。 @@ -83,10 +84,12 @@ | 合并策略 | 重复授予时发生什么 | 说明 | |---|---|---| -| 叠加 | 层数加一;剩余结算次数不动——只有首次授予定钟 | MVP 四行全部用叠加 | +| 叠加 | 层数加一;剩余结算次数不动——只有首次授予定钟 | MVP 六行全部用叠加 | | 刷新 | 剩余结算次数重置为该实例首次授予时的初始值,层数不变 | MVP 无内容行,语义已就位 | | 并存 | 新建一份独立实例,两份各算各的 | 同上 | +并存分支没有满层上限,但容器规模天然有界:实例数的上界就是受理过的授予数——多受理一份就多一份实例,多少份在授予那一刻就完全确定,不会无限膨胀。 + 叠加是纯加法:几层就是几倍的行值。叠到行上限后再授予同款会被拒绝,容器一个字不变;但这份奖励不会被无声吞掉——结算明细多一行"溢出未生效"(满层后白拿的那份,如实留痕),连来源一起写明,玩家看得到"这层白拿了"。浪费诚实、可规划。 无论重复来自玩家直接踩出的格子,还是连锁展开自动开出的奖励格,都走同一个授予入口、同样产生溢出行、同样留痕。 @@ -95,7 +98,7 @@ ### 剩余次数怎么倒数:按结算次数衰减 -每个状态实例自带一个倒数计数——剩余结算次数(本文说的"衰减"就是它逐次递减、归零消失)。它不承诺"活几轮",而是数"经历过几次轮末结算":每轮结算先让状态起作用,随后计数减一,减到零状态即被移除。持续 1 的状态就是"一次性":在授予它的那轮结算里生效一次,轮末消失。MVP 四行内容全部是一次性;将来跨轮持续的增益,寿命由表行初始值表达,给几就活几次结算。 +每个状态实例自带一个倒数计数——剩余结算次数(本文说的"衰减"就是它逐次递减、归零消失)。它不承诺"活几轮",而是数"经历过几次轮末结算":每轮结算先让状态起作用,随后计数减一,减到零状态即被移除。持续 1 的状态就是"一次性":在授予它的那轮结算里生效一次,轮末消失。MVP 六行内容全部是一次性;将来跨轮持续的增益,寿命由表行初始值表达,给几就活几次结算。 ``` 一轮结算:查询生效清单 → 逐行兑付(层数 × 行值)→ 轮末衰减(全部实例剩余次数 −1)→ 归零实例移除 @@ -130,9 +133,9 @@ 容器还有一条快照纪律:实例的幅度、初始次数、量纲在授予那一刻从表行拷贝。战斗中配置表即使被重载,已授予实例不受任何影响,只影响后续授予。 -### 现在有哪些状态:MVP 四行内容 +### 现在有哪些状态:MVP 六行内容 -MVP 阶段状态表恰有四行:两个主角增益(挖安全格拿)、两个敌方效果(踩雷的增强、标中旗的削弱)。四行全部是一次性、全部用叠加合并;数值全部 provisional(临时值,过锚验收后才定稿)。 +MVP 阶段状态表恰有六行:两个主角增益(挖安全格拿)、三个敌方效果(踩雷的伏击增强、标中旗的削弱、标空旗反噬的暴走)、一个主角减益(标空旗反噬的失衡)。六行全部是一次性、全部用叠加合并;数值全部 provisional(临时值,过锚验收后才定稿)。 | 字段 | 取值 | 说明 | |---|---|---| @@ -149,10 +152,12 @@ MVP 阶段状态表恰有四行:两个主角增益(挖安全格拿)、两 |---|---|---|---|---|---| | attack_up 增伤 | 主角攻击提升 | 主角 | +50%(provisional) | 2 | 安全格 | | defense_up 减伤 | 主角承伤降低 | 主角 | −30%(provisional) | 2 | 安全格 | -| ambush 伏击 | 敌方攻击附带额外伤害 | 敌方 | +3 固定值/层(provisional) | 16 | 踩雷 | +| ambush 伏击 | 敌方攻击附带额外伤害 | 敌方 | +3 固定值/层(provisional) | 16 | 踩雷 / 标空旗反噬 | | weaken 削弱 | 敌方攻击被削弱 | 敌方 | −65%(provisional,取值域 [60,85]) | 16 | 标中旗 | +| enrage 暴走 | 敌方攻击提升 | 敌方 | +25%(provisional) | 16 | 标空旗反噬 | +| off_guard 失衡 | 主角承伤加深(+25 个百分点进承伤修正池) | 主角 | +25%(provisional) | 16 | 标空旗反噬 | -削弱行是当量锚在容器侧的数据落点:标中旗的收益折算成统一价值后,必须严格高于最低档主角增益——"标中旗必须比随便挖一格更值",否则没人愿意插旗。这条锚的契约归[行动经济](../排雷循环/行动经济.md),对照验收归[战斗结算](战斗结算.md);伏击行的固定值量纲定义与惩罚上限锚归[敌人系统](敌人系统.md)。 +削弱行是当量锚在容器侧的数据落点:标中旗的收益折算成统一价值后,必须严格高于最低档主角增益——"标中旗必须比随便挖一格更值",否则没人愿意插旗。这条锚的契约归[行动经济](../排雷循环/行动经济.md),对照验收归[战斗结算](战斗结算.md);伏击行的固定值量纲定义与惩罚上限锚归[敌人系统](敌人系统.md)。暴走与失衡两行是[战斗结算](战斗结算.md)标错反噬机制(标空旗的三选一确定性惩罚)在容器侧的数据落点:三池内容与载体裁决归它,本表只承载行值。 ## 依赖与输出 @@ -173,7 +178,7 @@ MVP 阶段状态表恰有四行:两个主角增益(挖安全格拿)、两 ## P0 最小闭环 1. 战斗结算通知战斗开始:容器开账,空容器进入可用状态。 -2. 轮末结算时,战斗结算把这一轮的扫雷事实兑换成授予:挖开安全格给主角上增益、标中旗削弱敌人、踩雷给敌方上增强——授予入口是唯一进货口。 +2. 轮末结算时,战斗结算把这一轮的扫雷事实兑换成授予:挖开安全格给主角上增益、标中旗削弱敌人、踩雷给敌方上增强、标空旗触发反噬——授予入口是唯一进货口。 3. 容器按表行合并策略受理(新建 / 叠一层 / 刷新),实例立即生效并携带归因记录。 4. 战斗结算查询生效清单,逐行兑付进结算公式(有效值 = 层数 × 行值)。 5. 兑付完做轮末衰减:全部实例剩余次数减一,归零移除——先兑付、后衰减。 @@ -186,16 +191,17 @@ MVP 阶段状态表恰有四行:两个主角增益(挖安全格拿)、两 | 优先级 | 内容 | 后置原因 | |---|---|---| -| P1 | 刷新、并存两类合并策略的首批内容行 | 语义已定义、容器已承载;MVP 四行全用叠加,这两类暂无实际内容,首批上线时随批补验收 | +| P1 | 刷新、并存两类合并策略的首批内容行 | 语义已定义、容器已承载;MVP 六行全用叠加,这两类暂无实际内容,首批上线时随批补验收 | | P1 | 清图奖励含增益的授予路径 | 来源类别已预留;内容与档位归[奖励道具经济](奖励道具经济.md)定稿 | | P1 | 单体寻址扩展位 | 现行对敌效果一律全体共享;首个"只作用于其中一个敌人"的效果上线时给实例加目标字段 | | P2 | 驱散 / 延时 / 转移接口 | 状态一旦生效,玩家没有任何手段摘掉、延后或挪给别人——省掉一整类界面复杂度是明确的设计决定;要加属于新增接口的修订,不是调参 | +| P2 | 并存行高频授予(持续伤害类)的容量语义 | 并存实例数上界 = 受理授予数,有界确定、无满层语义;MVP 无并存内容行,首个并存内容行上线时随批复核容量读法 | | P2 | 敌人词条引用状态(免疫 / 抗性) | 预留位;随[敌人系统](敌人系统.md)的内容批裁决 | | P2 | 随关卡成长的效果行 | 状态表是定义表、不是进度成长表;需要时走加表列的修订 | ## 调参表 -状态表为定义表,键 = 状态标识;已锁数值以 [registry 的 status_effect 条目](../../../registry/entities.yaml) 为准,四行数值全部 provisional。 +状态表为定义表,键 = 状态标识;已锁数值以 [registry 的 status_effect 条目](../../../registry/entities.yaml) 为准,六行数值全部 provisional。 | 参数 | 取值域 | 现行值 | 说明 | |---|---|---|---| @@ -203,8 +209,10 @@ MVP 阶段状态表恰有四行:两个主角增益(挖安全格拿)、两 | defense_up 幅度 | 百分比 [−100,100] 且非 0 | −30(provisional) | 减伤;叠满两层 −60 个百分点 | | ambush 幅度 | 固定值 ≥ 1 | +3(provisional) | 伏击;量纲定义见[敌人系统](敌人系统.md) | | weaken 幅度 | 百分比取值域 [60,85],取负生效 | −65(provisional) | 削弱;须过"标中旗锚"对照 | -| 四行初始剩余次数 | 整数 [1,99] | 1 / 1 / 1 / 1(provisional) | 一次性语义;拉长 = 恢复跨轮延续、规划变简单 | -| 四行叠层上限 | 整数 [1,16] | 2 / 2 / 16 / 16(provisional) | 仅叠加策略消费;调低 = 重复奖励更容易变溢出 | +| enrage 幅度 | 百分比 [−100,100] 且非 0 | +25(provisional) | 暴走;标错反噬三池之一,进敌方输出修正池 | +| off_guard 幅度 | 百分比 [−100,100] 且非 0 | +25(provisional) | 失衡;标错反噬三池之一,进主角承伤修正池(承伤 ×1.25) | +| 六行初始剩余次数 | 整数 [1,99] | 1 × 6(provisional) | 一次性语义;拉长 = 恢复跨轮延续、规划变简单 | +| 六行叠层上限 | 整数 [1,16] | 2 / 2 / 16 / 16 / 16 / 16(provisional) | 仅叠加策略消费;调低 = 重复奖励更容易变溢出 | 非调参的冻结项:三类合并策略语义、两种量纲语义、来源类别枚举、"先兑付后衰减"时序、拒绝原子性、零随机——改动走本文修订流程,不走调参。 @@ -213,7 +221,7 @@ MVP 阶段状态表恰有四行:两个主角增益(挖安全格拿)、两 ## 验收标准 - 容器寿命可答:战斗开始时为空;除战斗结束清空外没有任何入口能清空容器;战斗未开始或已结束时的授予一律被拒且容器不变。 -- 三向同构:主角增益与敌方两类效果共用同一套受理、合并、倒数、清除语义,不存在第二套计时逻辑。 +- 多向同构:主角侧增益与减益、敌方侧增强与削弱共用同一套受理、合并、倒数、清除语义,不存在第二套计时逻辑。 - 授予即生效:状态发入当轮结算即兑付第一次,无"待生效"中间态。 - 只记账不去重:同一事实被转化两次授予,入账两层——去重责任在战斗结算。 - 叠层不减钟:叠加只加层数,剩余次数保持首次授予定下的轨迹不变。 @@ -240,3 +248,5 @@ MVP 阶段状态表恰有四行:两个主角增益(挖安全格拿)、两 | 版本 | 日期 | 改动 | |---|---|---| | v0.1 | 2026-09-15 | 自归档 GDD 压缩改写 | +| v0.2 | 2026-09-15 | 状态表四行改六行:补暴走(enrage,敌方 +25%)与失衡(off_guard,主角承伤 +25%)两行,承载[战斗结算](战斗结算.md)标错反噬机制(2026-09-12 批已上线);来源类别四选一改五选一(新增标空旗);连带更新"MVP 四行/四类"表述与调参表 | +| v0.3 | 2026-09-15 | 并存分支补容量语义(实例数上界 = 受理授予数,有界确定、无满层语义);P2 后置登记并存行高频授予(持续伤害类)的容量复核 | diff --git a/design/gdd/systems/战斗循环/奖励道具经济.md b/design/gdd/systems/战斗循环/奖励道具经济.md index 4d9bb607..47cf3dff 100644 --- a/design/gdd/systems/战斗循环/奖励道具经济.md +++ b/design/gdd/systems/战斗循环/奖励道具经济.md @@ -19,7 +19,7 @@ | 奖励类型封闭枚举 | 五种类型(三种即时、两种可选);表里出现清单外的类型,数据加载直接拒绝 | | 装板期身份预分配 | 每张图装上时一次摇定全部格子身份与清图身份;只读、无重摇 | | 奖励表与奖励配置表 | 两张数据表的承载归[数据配置](../表现与支撑/数据配置.md),内容由本系统回填 | -| 可选类供给 | 战斗首件保证加逐图递减注入;另有按板保底断言(现行默认关闭) | +| 可选类供给 | 战斗首件保证加按实际获取件数递减注入;另有按板保底断言(现行默认关闭) | | 关卡域库存 | 伤害道具与强效治疗包的背包:入库、查询、窗口选用消耗、关卡末清空 | | 窗口数据面 | 库存投影与"现在用有没有效果"的可用性预判组装;窗口本身的编排归战斗结算 | @@ -148,7 +148,7 @@ ### 清图奖励必发,首件保底,加载兜底 -把一张图的雷全部清完时,结算发一份清图奖励:身份早在装板摇号时与格子奖励同批摇好。"清图奖励大于零"是数据加载时强制保证的——奖池里至少要有一行有实际价值的内容,不存在空奖。可选类道具的供给由机制保证而非格子概率:每场战斗开始时第一件必出,之后逐图递减注入。 +把一张图的雷全部清完时,结算发一份清图奖励:身份早在装板摇号时与格子奖励同批摇好。"清图奖励大于零"是数据加载时强制保证的——奖池里至少要有一行有实际价值的内容,不存在空奖。可选类道具的供给由机制保证而非格子概率:首场战斗内玩家拿到第一件之前每张新图必注入一件,之后注入概率随本战斗实际获取件数递减。 | 清图奖励规则 | 含义 | |---|---| @@ -156,7 +156,21 @@ | 可含即时增益 | 清图槽允许放增益行;兑付时若已满层,零兑付并记"奖励未生效"行(合法例外) | | 图类型差异化是预留插槽 | 现行只有默认槽;按图类型换奖池的结构位已留,待[棋盘生成](../排雷循环/棋盘生成.md)的图类型词表落定后启用 | -可选类供给:基础刀、进阶刀、强效治疗包三行不进格子权重池,由供给机制注入——每场战斗开始时第一件保底必出;此后每张新图按一个逐图减半的注入概率补充(初值 0.4、每图乘 0.5,概率永不为零)。标定目标是平均每场战斗约获得一件,全部 provisional,投值归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)。 +可选类供给:基础刀、进阶刀、强效治疗包三行不进格子权重池,由供给机制注入——注入判定是装板摇号批的一部分:在格子身份与清图槽摇定之后追加一次判定,命中则从本图资格格里均匀随机选一个,**覆盖**它刚摇出的即时类身份、换成注入池摇出的一件可选类(被覆盖的即时身份就此作废,不补摇)。标定目标是平均每场战斗约获得一件,全部 provisional,投值归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)。 + +注入概率随获取件数递减,不随图数递减:每场战斗开始时"已获取件数"清零;本战斗尚未获取过可选类时,每张新图必注入一件(首件保证);已实际获取 j 件时,每张新图的注入概率 = 0.4 × 0.5^(j−1)(j = 本战斗已实际获取件数),概率永不为零。"发放即获取":件数只在可选类实际发放(授予段或节点奖励入账)时加一;注入进图却没被挖到的格子不计数——所以没拿到新件,下一张图的概率不衰减,仍按当前件数的那一档掷。这条语义同时是诚实的另一面:首件保证承诺的是"必注入",不是"必到手"——注入格没被挖开,战斗结束也不补发(名义值与实际期望的落差登记于待拍板)。 + +**结论**:注入判定在装板摇号批内恰一次;注入概率 p(j) = p₀ × d^(j−1),j = 本战斗已实际发放的可选类件数。 + +``` +j = 0(未获取)→ p = 1(必注入) +j ≥ 1 → p = p₀ × d^(j−1)(现行 p₀ = 0.4、d = 0.5:0.4、0.2、0.1、…,永不为零) +``` + +| 变量 | 含义 | 来源 | +|---|---|---| +| j | 本战斗内已实际发放(入账)的可选类件数;注入未挖到不计数,战斗开始清零 | 供给机制的战斗域计数 | +| p₀ / d | 注入概率初值 / 衰减因子 | 奖励配置表 active_p0 / active_decay(provisional) | 另有一道按板保底断言:一张图的资格格里带伤害道具的数量低于下界时,整批草稿弃用、沿同一条随机流重摇;重摇超过上限(默认 32 次)即致命报错——那说明权重配置坏了。现行下界默认为 0,即断言关闭:刀保底的语义已由首件保证取代,断言结构保留备用。 @@ -223,8 +237,8 @@ | 参数 | 域 | 现行值 | 说明 | |---|---|---|---| -| active_p0 | float [0,1] | 0.4(provisional) | 首件之后每张新图的注入概率初值;标定目标 = 平均每场战斗约得一件 | -| active_decay | float (0,1] | 0.5(provisional) | 注入概率逐图减半(0.4、0.2、0.1、…),永不为零;加难杠杆候选 0.6 | +| active_p0 | float [0,1] | 0.4(provisional) | 注入概率公式的初值因子:本战斗已获取 j 件时,每张新图注入概率 = active_p0 × active_decay^(j−1);标定目标 = 平均每场战斗约得一件 | +| active_decay | float (0,1] | 0.5(provisional) | 注入概率按本战斗实际获取件数减半(已获 1 件后 0.4、2 件后 0.2、3 件后 0.1、…),永不为零;未获取新件不衰减;加难杠杆候选 0.6 | | k_dmg | int [0,999] | 0(provisional) | 按板伤害保底下界;0 = 断言关闭(现行口径,刀保底语义由首件保证取代) | | n_draft | int [1,999] | 32(provisional) | 保底重摇上限,超过即致命报错 | | p_min | int [1,999] | 8 | 保底开启时格子池伤害行的威力下界(即默认最低刀威力) | @@ -253,7 +267,7 @@ - 每格必有货:装板摇号后,每个资格格恰有一个奖励身份、清图槽恰有一个身份;同图跨轮查询结果逐字相同。 - 恰一次与确定性:摇号对每张新图恰发生一次,重复调用被拒绝且映射不变;同一条随机流轨迹驱动两套实例,映射、库存与使用回执逐位相同。 - 保底重摇与致命:按板保底开启而首稿不达标时,整批弃用、沿同流重摇,达标才提交;重摇超过上限时进程报错退出。 -- 首件保证:每场战斗开始后玩家必获得第一件可选类道具;此后注入概率按表值递减但永不为零。 +- 首件保证:首场战斗内,玩家获取首件可选类道具之前每张新图必注入 1 件到随机资格格(注入即覆盖该格既有即时类身份);注入格未被挖到则不获得、不补发;此后注入概率随本战斗实际获取件数按表值递减但永不为零,未获取新件时概率不衰减。 - 未生效可见:满层增益、满血即时治疗、无存活敌的群体伤害,各自在结算明细记"奖励未生效"行,零折算、零补偿。 - 满血守卫:满血时强效治疗包在窗口禁选;缺血时使用回血不超上限,且道具被消耗。 - 窗口纪律:一次窗口至多选用一件;跳过恒合法;已选件在无存活敌时照常消耗并记"已消耗未生效"行;同一件不能选用第二次。 @@ -271,6 +285,7 @@ |---|---|---| | 奖励预读与推理的边界 | 预读允许到什么深度未定:[不做清单](../../05-不做清单.md)裁定未开格不显示奖励身份、允许恢复推理原料类道具;现役预读道具探宝罗盘(预读 3×3 窗口内未开格的奖励身份,归[辅助道具](../排雷循环/辅助道具.md))为窄域,更深的呈现(未开格身份常显等)与推理保护之间的界线待拍板 | [辅助道具](../排雷循环/辅助道具.md)定稿批与[HUD 框架](../表现与支撑/HUD框架.md) | | 雾格相关定价约束三条失义 | 旧体系三条定价约束——雾内档位下界、清图奖励锚随雾成本上浮、雾板净当量优势带上界——随雾格于 2026-09-14 从设计中移除(见[不做清单](../../05-不做清单.md))整体失义;奖励配置表的档位过滤门限 t_fog_min(登记默认 2)过滤对象随之消失。清图锚是否需按图类型或难度上浮、优势带上界是否重建,均未定 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md) 调参批 | +| 注入格未挖到不补发,实际获取期望低于名义首件保证 | 首件保证承诺的是"必注入"而非"必到手":注入格因清图或速杀未被挖到时不获得、战斗末不补发,实际获取期望低于名义值(切片实测约 0.88 件/战 vs 名义 1.0);拟通过修改退出战斗的触发方式优化(清图/战斗结束的时机与注入格的兜底交互),2026-09-15 拍板暂不做,登记待办 | [战斗结算](战斗结算.md)与[难度曲线与Boss](../冒险循环/难度曲线与Boss.md) | | 速杀退化预警 | 战斗结束时未开资格格占比过高即警:供给过剩使战斗提早结束,奖励身份零发放,奖励系统饿死自己的高光时刻;阈值与联动调参未定 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md) | | 窗口摩擦预警 | 窗口跳过率、单次结算平均明细行数未设阈值;结构性缓解已内置(库存只收可选类、窗口单件制),保留观测 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md) | | 连锁批量授予的溢出吸收 | 一条大连锁一次给出大量奖励资格,超出增益叠层上限时如何处理(折算、顺延或显式接受)未定;与[排雷玩法](../排雷循环/排雷玩法.md)待拍板同源 | [战斗结算](战斗结算.md)与[增益状态](增益状态.md)条目化 | @@ -282,3 +297,4 @@ | 版本 | 日期 | 改动 | |---|---|---| | v0.1 | 2026-09-15 | 自归档 GDD 压缩改写 | +| v0.2 | 2026-09-15 | 可选类注入递减基准对齐代码:逐图减半改为按实际获取件数递减(首件保证 = 未获取则每张新图必注入;p = p₀×d^(j−1));补明注入覆盖即时身份与"发放即获取、未挖到不计数"语义;验收标准首件保证改"必注入"口径;待拍板新增注入格未挖到不补发条目 | diff --git a/design/gdd/systems/战斗循环/战斗结算.md b/design/gdd/systems/战斗循环/战斗结算.md index 4bfab720..2fd52cfa 100644 --- a/design/gdd/systems/战斗循环/战斗结算.md +++ b/design/gdd/systems/战斗循环/战斗结算.md @@ -72,7 +72,7 @@ ### 明细流水:每一行都能指回你刚才的哪一步棋 -结算过程中每一件事都写一行明细,每行标注来源——哪个安全格、哪面旗、哪颗雷、清图奖励。这份流水是结算明细屏(玩家逐行查看决策兑现结果的界面)的数据源,也是回放对账与数据导出共读的唯一契约。行型封闭为 13 类,不允许私自增加。 +结算过程中每一件事都写一行明细,每行标注来源——哪个安全格、哪面旗、哪颗雷、清图奖励。这份流水是结算明细屏(玩家逐行查看决策兑现结果的界面)的数据源,也是回放对账与数据导出共读的唯一契约。行型封闭为 15 类,不允许私自增加;新增或下线行型属于行协议变更,必须走本文修订,并同步呈现层的行格式化。 | 行型 | 什么时候写 | |---|---| @@ -84,13 +84,25 @@ | 奖励未生效行 | 满层拒收、满血治疗等合法例外一行 | | 已消耗未生效行 | 道具消耗了却没产生效果(防御分支) | | 使用被拒行 | 满血选用治疗包被窗口拒绝 | -| 标旗未中行 | 旗插空的零值归因行,每颗雷只记一次 | +| 标旗未中行 | 旗插空的零值归因行,每颗雷只记一次;标错反噬的兑付另记效果行 | | 蓄力行 | 敌人本拍蓄力不动手(攒下一击)时记一行 | | 衰减行 | 到期临时效果移除一行 | | 判定行 | 胜 / 负 / 继续各一行;终局判定行同时是胜负画面的数据锚 | | 高光行 | 完美排雷成立时一行,只记实际有效兑付 | +| 窗口打开锚行(window_open) | 窗口段有候选件、真的弹出时一行;数值 = 本轮候选件数(库存中的在架件数),是"窗口段在案"的打开态锚点 | +| 窗口跳过锚行(window_skip) | 窗口段以跳过收场时一行。两种触发:库存为空、窗口根本不弹,直接跳过;或是窗口弹了而玩家提交零选择主动跳过。两者数值都是 0 | -每行还携带公共字段:来源类别、来源格、轮次、显示名、数值,以及可空的高光标记(用于强调标中旗、清图奖励这类值得高亮的结果)。没生效也要留痕:四类"未生效 / 被拒"行如实记录合法例外,呈现时聚合计数,不逐行刷屏。任何一行找不到来源即断言失败——宁可报错,也不让"合并到看不出来源"的呈现混过数据层。 +窗口段的两类锚行只服务一件事:让"这一轮窗口段发生过什么"恒有一行可查,不参与聚合统计(呈现层对它们的格式化与"未生效类"计数无关)。**窗口段恒在案**由此落成——有候选就落窗口打开锚行,没候选就落跳过锚行(空库存触发),玩家零选择再落一条跳过锚行(未选用触发)。 + +每行还携带公共字段:来源类别、来源格、轮次、显示名、数值,以及可空的高光标记(用于强调标中旗、清图奖励这类值得高亮的结果)。没生效也要留痕:四类"未生效 / 被拒"行如实记录合法例外,呈现时聚合计数,不逐行刷屏。 + +任何一行找不到来源即断言失败——宁可报错,也不让"合并到看不出来源"的呈现混过数据层。但有些行天然没有棋盘来源,断言为此留了两层豁免,豁免集合是封闭清单,改动同样走本文修订。 + +第一层按行型豁免四类:衰减行(到期移除本就是段级动作,不属于任何一格)、判定行(它是胜负出口的结论,不是棋盘事实)、窗口打开锚行与窗口跳过锚行(记录的是"窗口段发生了什么",不是哪一格)。这四类连来源类别都不必填。 + +第二层按来源类别豁免四类:主角攻击行的来源是主角单位本身,敌方行动行的来源是当时动手的那个敌人单位,清图奖励行的来源是"这张板被清掉"这一整板事实,道具行的来源是库存里的那件道具实例——它们都不指向某个棋盘格,来源格校验因此跳过。 + +豁免不等于免检:按行型豁免的行仍要带齐公共字段、守住各自的语义约束(如窗口打开锚行的数值必须是本轮的候选件数),按来源类别豁免的行仍必须写明来源类别,其余行必须指回一个真实存在的棋盘格。豁免只是免掉"来源格必须有效"这一条校验,不是免掉整份行协议。 举证:本节为行协议约定,无计算。 @@ -120,7 +132,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × | 批 | 兑付 | |---|---| | 挖格批 | 踩到的雷逐颗转化为伏击;带奖励资格的安全格按类别分流(见下表) | -| 旗判定批 | 标中则敌方削弱当轮生效,每颗雷的资格只兑一次;标空则零兑付,只记"标旗未中"归因行 | +| 旗判定批 | 标中则敌方削弱当轮生效,每颗雷的资格只兑一次;标空则记"标旗未中"归因行,并触发标错反噬(见下) | | 清图批 | 清图奖励恰好发一次;完美排雷追加第二份同身份兑付 | | 奖励类别 | 兑付方式 | 拒绝语义 | @@ -134,6 +146,8 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × 标中资格认雷不认旗:兑付资格以雷格实例为键——拔旗重插不恢复、同一颗雷跨轮不重复兑付;换图后新雷是新实例,资格自然重置。同一面旗一直插在雷上,每轮都会重新上报标中,去重是本系统的职责。 +标错反噬是标空旗(标了但格不是雷)的已定案惩罚:在旗判定批里,每颗被标空的雷格实例触发一次三选一确定性反噬——暴走(enrage,敌方增伤 +25%)、敌方伏击 +2 层、失衡(off_guard,主角承伤 +25%),三者都是一次性状态(1 结算时效),行值归[增益状态](增益状态.md)的状态表。选哪一项不由掷骰决定,而由载体确定性派生:以标空格的坐标与本场战斗的板序号做哈希((|x|×7 + |y|×13 + 板序号×5) mod 3,0/1/2 依次对应三池),同一坐标同一板永远出同一项。刻意不消费随机数流的理由只有一个:结算全程可回放——同种子同操作序列必须逐位复现同一次反噬,不能为一次惩罚往回放轨迹里塞进新的取号。反噬与"标旗未中"归因行同键去重,每雷格实例只触发一次;叠层已满授予被拒时记"奖励未生效"行,不静默吞掉。 + 完美排雷全有或全无:一张板零误标且至少标中一面旗,清图奖励加倍(现行倍数为翻倍,provisional)。完美计数从装板起算、换图重置;高光行只记实际有效兑付(首份加被接受的第二份),首份即零兑付则高光行不发。标中旗的兑付行自带高光标记,与完美排雷的独立高光行构成两个强制高光检查点。 举证:兑付数值全部读配置表,本节无新公式;完美乘数的入账换算见收益对照表一节。 @@ -223,7 +237,6 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × | 优先级 | 内容 | 后置原因 | |---|---|---| -| P1 | 标错(控制失败)惩罚落地 | 惩罚方案未定案(见「待拍板」);当前"标旗未中"行只是归因痕迹,无惩罚 | | P1 | 深度高光形态(标中旗即时动画、音画同步) | 数据面标记与当轮兑付已落,表现形态归[战斗表现](战斗表现.md)与美术 | | P1 | 冒险侧真实供给接线 | 遭遇上下文与跨战斗生命供给在[冒险结构](../冒险循环/冒险结构.md)定稿前以入参直供 | | P2 | 回放切点续玩 | 依赖[存档序列化](../表现与支撑/存档序列化.md)的快照能力实现 | @@ -247,7 +260,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × ## 验收标准 -- 七段全序:任一轮结算的明细行按七段顺序排列;窗口段恒在案,零选择记跳过。 +- 七段全序:任一轮结算的明细行按七段顺序排列;窗口段恒在案——有候选时落窗口打开锚行(数值等于本轮候选件数),无候选时落跳过锚行,玩家零选择也落跳过行。 - 触发与到达序无关:同一轮末的两条结束消息按任意先后驱动,结算步骤流逐行相同。 - 每事实恰一次:每格受理、每雷转化、每旗判定恰好一次;重复投递不改变结果。 - 伏击守恒:踩雷超出上限时兑付数等于上限,超出部分逐颗记"溢出未生效"行;多敌共享同一伏击池值。 @@ -258,7 +271,8 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × - 出口分发:先判主角倒下(负)再判敌清空(胜);停手与耗尽轮恒同图,清图轮才允许换图。 - 关卡收束:关卡结束清空两个背包恰一次并返回清空前计数;战斗结束不清库存。 - 可回放:同种子同输入两遍步骤流逐行相同;结算路径零随机、零界面等待。 -- 无来源即炸:每行明细都能指回一个来源事实;找不到来源即断言失败。 +- 无来源即炸:每行明细都能指回一个来源事实(棋盘格、单位、清图、库存件或段级结论);找不到来源即断言失败,行型与来源类别两层豁免清单见明细流水一节,豁免行仍受其它契约约束。 +- 行型封闭:流水只出现本文登记的 15 类行型;新增或下线行型必须走本文修订并同步呈现层行格式化,未登记行型不得落行。 ## 待拍板 @@ -269,7 +283,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × | 挖掘风险不等式对账破线 | 归档记录现行值链(防御增益时长 1、行值 −30、现行难度曲线)下两参考板形首版验算破线,多敌满配深度破线;破线已升格为曲线级约束,相关参数处禁独立调参联动网络;对账须在难度曲线调参批重跑 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md)提案,本系统闭合共裁 | | 清图支配性保守地板缺口 | "推到底清图不吃亏"的对账未闭合:非完美板形的清图期望收益低于"挖完确定格就弃图刷新";已裁"清图非严格支配"为设计态,保守地板当量缺口仍在案;对账须在难度曲线调参批重跑 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md),奖励量级联动[奖励道具经济](奖励道具经济.md) | | 对账输入的雾相关分量失义 | 原对账输入含雾成本负担比、雾板分层等分量,随雾格移除(见[不做清单](../../05-不做清单.md))失义;重跑对账时需以现行输入重建判据 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md) | -| 标错(控制失败)惩罚 | 惩罚方案未定案;落地前标空只判"不中",无额外后果 | 数值归[敌人系统](敌人系统.md),兑付归本系统 | +| 标错反噬的正式侧载体 | 标空旗的三选一确定性反噬已定案并上线(2026-09-12 批,机制见授予段一节):三池内容、一次性时效、确定性派生载体均为现行规则;留下的开口是载体实现——现行为坐标×板序号哈希派生、不消费随机数流,正式侧候选 = 改为随机服务新增一条封闭随机流(回放纪律不变,迁移与否未定) | 本系统,随机流结构联动[数据配置](../表现与支撑/数据配置.md) | | 连锁批量奖励的溢出处理 | 一条大连锁给出的奖励超出叠层上限时如何处理未定(现行按显式接受、零补偿执行) | [奖励道具经济](奖励道具经济.md)与本系统条目化 | ## 版本历史 @@ -277,3 +291,5 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × | 版本 | 日期 | 改动 | |---|---|---| | v0.1 | 2026-09-15 | 自归档 GDD 压缩改写 | +| v0.2 | 2026-09-15 | 标错反噬机制收编(2026-09-12 批已上线):旗判定批标空分支补"触发三选一确定性反噬",授予段新增反噬规则正文(三池内容、一次性时效、坐标×板序号确定性派生载体、不走随机流的可回放理由);P1 后置删除"标错惩罚落地"条目;待拍板"标错惩罚未定案"改写为已定案描述并保留正式侧载体候选注记 | +| v0.3 | 2026-09-15 | 明细流水行型清单 13 → 15:补窗口打开锚行 window_open / 窗口跳过锚行 window_skip 的语义、字段与落行时机,服务"窗口段恒在案、零选择记跳过、空库存记跳过";"无来源即炸"的两层豁免集合(行型四类 / 来源类别四类)成文并说明豁免不等于免检;验收标准窗口段措辞与实现对齐,新增行型封闭与行型新增纪律条目 | diff --git a/design/gdd/systems/战斗循环/战斗表现.md b/design/gdd/systems/战斗循环/战斗表现.md index f38b151e..e4429672 100644 --- a/design/gdd/systems/战斗循环/战斗表现.md +++ b/design/gdd/systems/战斗循环/战斗表现.md @@ -22,7 +22,7 @@ |---|---| | 动画与演出的设计 | 在既有呈现语义之上,设计受击、弹道、退场、连锁等动画的密度与节奏(类别见输入清单,方案未定) | | 演出资产终稿 | 敌人受击反馈、死亡表现、顺位标记三类资产的终稿(数据面已由敌人系统锁定,程序化占位先行) | -| 一屏样张的组织 | 正式设计前的硬前置:用一张样张锁定角色剪影、动画方案、战斗空间 | +| 一屏样张的组织 | 正式设计前的硬前置:用一张样张锁定角色剪影、动画方案、战斗空间,并顺带裁决敌人层绘制优先级的插入位置 | 不负责: @@ -73,6 +73,8 @@ 正式设计本文之前,必须先做一次"战斗表现美术测试":用一屏样张(一张按候选方案画成的试验图)锁定角色剪影、动画方案、战斗空间三件事。这是旧体系评审登记的排期硬前置,不是建议;battle.md 也登记了该测试须按双舞台布局出样。样张未过,不启动任何终稿资产与动画设计。 +附着在同一次样张上的还有一项已登记的裁决(登记出处 = 敌人系统的呈现契约):敌人层绘制优先级的插入位置——白话就是敌人的姿态精灵画在绘制顺序的哪一层,会不会、以及如何与棋盘上的数字、旗、小人发生遮挡。它随美术测试一并裁决,不在本文提前定。 + 两项相邻裁定已有结论,设计时直接继承:敌人卡在 battle.md 里先按卡片式起草,卡片式对站桩式留给样张裁决;art-bible 的图标键契约已冻结,动画设计不必等图标终稿。 ### 演出服从信息与可读性 @@ -85,7 +87,7 @@ |---|---|---| | 上游 | [战斗结算](战斗结算.md) | 逐行流水与明细协议——演出时间轴的数据源 | | 上游 | [HUD 框架](../表现与支撑/HUD框架.md) | 五区布局、战斗画面区(五区中的 B 区)演出区结构、敌人卡与明细带呈现语义——本文在其上叠加密度 | -| 上游 | [敌人系统](敌人系统.md) | 资产需求下限三类与敌种姿态数据面——数据已锁,本文出终稿 | +| 上游 | [敌人系统](敌人系统.md) | 资产需求下限三类与敌种姿态数据面——数据已锁,本文出终稿;另登记一项随样张一并裁决的事项:敌人层绘制优先级的插入位置 | | 上游 | [art-bible](../../../art/art-bible.md)(现行权威) | 剪影、色彩、图标键契约、各阶段氛围——表现侧约束 | | 上游 | [battle.md](../../../ux/battle.md)(现行权威) | 屏级呈现形态与呈现语义 | | 协作 | [音频](../表现与支撑/音频.md) | 坍塌显形动画与音效方向系同批配对登记,将来对位设计(挂点未定) | @@ -124,7 +126,7 @@ MVP 不要求本文的闭环:不做清单把战斗动画演出列为 MVP 打 | 条目 | 内容 | |---|---| | 敌人卡形态 | 卡片式还是站桩式(无框立绘加底座)——battle.md 按卡片式起草并登记给样张裁决;本文的动画方案随之而定 | -| 样张验收标准未成文 | 美术测试只登记了三个锁定物(角色剪影 / 动画方案 / 战斗空间),尚无一份成文的验收标准可对照执行(可参照 art-bible 格子样张的成文验收做法),正式组织测试前须补 | +| 样张验收标准未成文 | 美术测试已登记的锁定物共四项(角色剪影 / 动画方案 / 战斗空间 / 敌人层绘制优先级插入位置),尚无一份成文的验收标准可对照执行(可参照 art-bible 格子样张的成文验收做法),正式组织测试前须补 | ## 待设计问题清单 diff --git a/design/gdd/systems/战斗循环/敌人系统.md b/design/gdd/systems/战斗循环/敌人系统.md index b2f7d195..251565b9 100644 --- a/design/gdd/systems/战斗循环/敌人系统.md +++ b/design/gdd/systems/战斗循环/敌人系统.md @@ -80,7 +80,7 @@ | 敌种 | 行为环 | 护甲类型 | 现行定位 | |---|---|---|---| -| 地精 | 攻击(单拍环) | 无 | 首发杂兵;教学位节点固定 1 只 | +| 地精 | 攻击(单拍环) | 无 | 首发杂兵;教学位节点 ×[1,2],开战掷定 | | 熊地精 | 攻击 / 蓄力 / 重击(三拍环) | 甲 | 首发主力;重击倍率 2 | | 食人魔酋长 | 攻击 / 攻击 / 蓄力 / 重击(四拍 Boss 环) | 重甲 | 首发关底 Boss;重击倍率 2 | @@ -198,7 +198,7 @@ | 参数 | 取值域 | 现行值 | 说明 | |---|---|---|---| -| unit_count | [1, 3] | 1(provisional) | 遭遇数量区间下界;教学位节点固定不掷 | +| unit_count | [1, 3] | 1(provisional) | 遭遇数量区间下界;有上界的行开战时在区间内掷定,教学位节点同样掷骰 | | unit_count_max | [unit_count, 3],可省略 | 缺省 = 定值 | 数量区间上界;开战时在区间内掷定本战实际数量 | | atk_min / atk_max | [0, 999] 可选配对 | ±3 浮动(provisional) | 攻击基线区间:开战掷定一次、整战持久;缺省 = 定值行 | | armor_type | {无, 甲, 重甲},缺省无 | 地精无 / 熊地精甲 / 酋长重甲 | 克制结算归[战斗结算](战斗结算.md) | @@ -215,7 +215,7 @@ | max_hp | [1, 999] | provisional(出处待查) | 主角生命上限 | | atk | [1, 999] | 6(原型锚 provisional) | 基础攻击;配 0 加载期拒绝——基础攻击恒在 | -三敌种现行行值(全部 provisional,出处 registry):地精教学位 24 生命 / 7 攻击 ×[1,2] 固定 1 只、群行 13/5 ×[2,3];熊地精 40/9 ×1、群行 18/6 ×[2,3]、深层锚行 30/7;食人魔酋长 150/9 ×1。 +三敌种现行行值(全部 provisional,出处 registry):地精教学位 24 生命 / 7 攻击 ×[1,2](开战掷定)、群行 13/5 ×[2,3];熊地精 40/9 ×1、群行 18/6 ×[2,3]、深层锚行 30/7;食人魔酋长 150/9 ×1。 结构冻结(非调参项,修订走本文流程):行为拍枚举封闭、死亡即不动手、先作用后推进、伏击层池共享读法、附加后置加法、伏击按主角血量点计量纲、固定顺位、每坐标恰一敌种、零随机。联动纪律:单轮上限 × 攻击基线(支配边界联动)、数量 × 上限(单结算上界双线性)、倍率 × 攻击(重击拍支配)禁止独立调参,联调须重跑方向性预检并同步 registry(见待拍板)。 @@ -250,3 +250,4 @@ | 版本 | 日期 | 改动 | |---|---|---| | v0.1 | 2026-09-15 | 自归档 GDD 压缩改写 | +| v0.2 | 2026-09-15 | 教学位掷骰口径自洽(随[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)同批修订):unit_count 字段说明、地精敌种定位与现行行值句中"教学位固定 1 只/固定不掷"改为 ×[1,2] 开战掷定 | diff --git a/design/gdd/systems/排雷循环/地形变异.md b/design/gdd/systems/排雷循环/地形变异.md index 0ab40d0c..91c524db 100644 --- a/design/gdd/systems/排雷循环/地形变异.md +++ b/design/gdd/systems/排雷循环/地形变异.md @@ -60,7 +60,7 @@ | 岩浆 | 仅登记名字,机制未定(provisional;出处为游戏概念文档的存疑项登记) | | 冰冻 | 仅登记名字,机制未定(provisional;出处同上) | -清单结构在归档期曾有完整字段定义(标识、解锁关号、作用域、信息规则、布置约束、地形主题、反制道具清单),其中"信息规则"与"布置约束"两个字段的取值语义绑定具体变异类型,字段集是否沿用随首批类型定稿一并拍板(历史登记见调参表指向的 registry 条目)。 +清单结构在归档期曾有完整字段定义,共八个:标识、玩家可见名、解锁关号、作用域、信息规则、布置约束、地形主题、反制道具清单。其中"信息规则"与"布置约束"两个字段的取值语义绑定具体变异类型,字段集是否沿用随首批类型定稿一并拍板(历史登记见调参表指向的 registry 条目)。 ### 零特判原则(承重约束) @@ -72,6 +72,8 @@ 变异在棋盘生成阶段布置:生成新雷图时决定本图是否携带变异、带在哪些格子上,玩家在挖掘中遭遇。本系统只声明"变异在生成期布置"这个接口位置;布置约束(变异允许落在什么格子上)与布置参数(摆几个、多大、摆哪)未定稿,将与首批变异类型的机制一起拍板,归属棋盘生成的管线预留段。定稿前,棋盘生成不为变异做任何额外工作。 +布置之前有一步候选池门控,它是框架级的现行机制:一种变异要进入本关的布置候选,须同时满足两个条件——它的解锁关号不大于当前关卡序号(直接整数比较,不做舍入换算),且它声明的地形主题与本地形主题有交集(主题声明为空集等于通配,任何地形都算匹配)。候选池为空是合法状态:生成器静默布置一张无变异的普通图,不报错、不警告。门控谓词与取值域登记于 [registry 的 variant_pool_gate 条目](../../../registry/entities.yaml)。 + ### 反制道具配套义务 未来每定稿一类变异,必须随批配套至少一件反制道具(针对该变异、给玩家提供应对手段的道具),不让任何变异成为只亏不赚的纯税。这条配套义务的归属方是本系统;"反制"作为道具的一个维度,由辅助道具的五维度框架承接,反制道具的具体效果、时效与定价都归辅助道具定稿。 @@ -106,6 +108,7 @@ P0 阶段本系统不产出任何可玩内容:变异类型清单为空,没 | P1 | 首批变异类型(岩浆/冰冻)机制定稿并建行 | 名字已登记、机制分化未定;定稿批随批配至少一件反制道具 | | P1 | 全局作用域变异(精英/Boss 图整图生效的变异形态) | 作用域语义未定稿,归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md) | | P2 | "地形成本修饰"价目钩子启用 | 由行动经济持有;启用须随首批变异定稿,与行动经济重议按格定价的接口 | +| P2 | 变异解锁节奏复核 | "每关至少引入一个新元素"的推进节奏,随[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)定稿复核 | ## 调参表 @@ -127,7 +130,7 @@ P0 阶段本系统不产出任何可玩内容:变异类型清单为空,没 | 条目 | 失义原因 | 建议归属 | |---|---|---| | 雾区布置约束族(雾区只允许布置在非 0 安全格——区内无雷、无 0 格;出生区及其初始展开可达域永不含变异等生成期硬约束) | 约束谓词绑定雾格的格类构造;岩浆/冰冻的布置约束未定,不能默认沿用 | [棋盘生成](棋盘生成.md) | -| 承重数学(雾区厚度不大于 2——等价于任意 3×3 窗口非全雾;雾面积占比不大于 8 倍雷密度的必要上界;占比与可推理率下限的联动参数) | 全部由"雾格 = 非 0 安全格"的结构推论导出,前提随雾移除失效 | [棋盘生成](棋盘生成.md) | +| 承重数学(雾区厚度不大于 2——等价于任意 3×3 窗口非全雾;雾面积占比不大于 8 倍雷密度的必要上界;k 格雾区的外环必含至少 ⌈k/8⌉ 颗雷——⌈ ⌉ 为向上取整,"雾边 = 推理锚"的定量形式;占比与可推理率下限的联动参数) | 全部由"雾格 = 非 0 安全格"的结构推论导出,前提随雾移除失效 | [棋盘生成](棋盘生成.md) | | 雾占比上限 0.25(生成参数域的占比校验,结构冻结值) | 校验对象是雾面积占比,对象已不存在 | [棋盘生成](棋盘生成.md) | | 可推理率"剔除雾格"双向净效应建模(雾边线索减少的减项与雾边推理锚的增项两边都算,禁单向补偿) | 建模对象是雾格对可推理率的扰动;未来变异若扰动信息量,须重新建模 | [棋盘生成](棋盘生成.md) | | 清图预算的雾增量核算(链不可达雾格数 × 挖格单价,计入清图总预算的设计期核算) | 增量分子是雾格的挖掘成本,雾移除后分子失义 | [棋盘生成](棋盘生成.md),联动[战斗结算](../战斗循环/战斗结算.md) | diff --git a/design/gdd/systems/排雷循环/辅助道具.md b/design/gdd/systems/排雷循环/辅助道具.md index 075a6fb7..30f4db5f 100644 --- a/design/gdd/systems/排雷循环/辅助道具.md +++ b/design/gdd/systems/排雷循环/辅助道具.md @@ -142,7 +142,7 @@ ## 调参表 -道具定义表与掉落权重的全部数值均为 provisional,归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)调参;现行登记值以 [registry 的 `a5_aux_items` 条目](../../../registry/entities.yaml) 为准,表 schema 归[数据配置](../表现与支撑/数据配置.md)承载、本系统回填。 +道具定义表与掉落权重的全部数值均为 provisional,归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)调参;现行登记值以 [registry 的 `aux_items` 条目](../../../registry/entities.yaml) 为准,表 schema 归[数据配置](../表现与支撑/数据配置.md)承载、本系统回填。 | 道具 | 稀有度 | 维度 | 效果 | 目标形态 | 数值 | 池权重/档位 | |---|---|---|---|---|---|---| diff --git a/design/gdd/systems/表现与支撑/音频.md b/design/gdd/systems/表现与支撑/音频.md index 6c1db9cc..0c39dc75 100644 --- a/design/gdd/systems/表现与支撑/音频.md +++ b/design/gdd/systems/表现与支撑/音频.md @@ -68,7 +68,7 @@ | 挂点输入 | 来源登记 | 说明 | |---|---|---| -| 明细行类型流 | 挂点清单;数据面是[战斗结算](../战斗循环/战斗结算.md)的明细流水协议 | 结算中每件事逐行留档,行型封闭为 13 类——每种行型天然是一个音效挂点候选(伤害、治疗、道具、判定、高光等) | +| 明细行类型流 | 挂点清单;数据面是[战斗结算](../战斗循环/战斗结算.md)的明细流水协议 | 结算中每件事逐行留档,行型封闭为 15 类——每种行型天然是一个音效挂点候选(伤害、治疗、道具、判定、高光等) | | toast 提示触发 | 挂点清单(toast 提示条本体归 [HUD 框架](HUD框架.md)) | toast(棋盘区上缘浮出、自动消退的非模态提示)弹出时的拒绝、提示类短音候选 | | 负反馈聚合单次 | 挂点清单 | 未生效、被拒类负反馈聚合为一次响,不逐行连响——呈现侧已按聚合计数防轰炸,音频侧对齐 | | 开格拾取按奖励类别区分 | 挂点清单 | 呈现侧按奖励类别分色,音频侧对应按类别区分拾取音的听感 | @@ -88,7 +88,7 @@ | 方向 | 系统 / 文档 | 接口要点 | |---|---|---| | 上游 | [排雷玩法](../排雷循环/排雷玩法.md) | 棋盘事件——挖掘与踩雷两个反馈位的触发源 | -| 上游 | [战斗结算](../战斗循环/战斗结算.md) | 明细流水(13 类行型)——结算、胜利反馈与全部明细挂点的事件源 | +| 上游 | [战斗结算](../战斗循环/战斗结算.md) | 明细流水(15 类行型)——结算、胜利反馈与全部明细挂点的事件源 | | 上游 | [HUD 框架](HUD框架.md) | toast 提示触发;挂点先留的登记处(MVP 不出声) | | 协作 | [战斗表现](../战斗循环/战斗表现.md) | 坍塌显形连锁动画与音效同批配对登记,将来对位设计(挂点未定) | | 输出 | 无 | 叶子系统:纯反馈层,无对外接口 | diff --git a/design/registry/entities.yaml b/design/registry/entities.yaml index 21fb702c..c1cf4e84 100644 --- a/design/registry/entities.yaml +++ b/design/registry/entities.yaml @@ -2,11 +2,12 @@ # 跨系统事实的唯一登记处:各 GDD 定稿的命名实体/公式/常量在此登记, # 后续 GDD 写作时作为"已锁事实"引用——不得默默改值,冲突必须显式提出。 # 维护规则:见 design-system 技能 Phase 5b。value 字段只能由 owner GDD 修订。 +# 命名注记(2026-09-15):本表历史上以 A1–D6 系统代号命名文档与义务,已全部清偿为中文规范名;英文代号对照表见 design/gdd/02-系统清单.md。owner 已改指新骨架文档路径(部分为待成文占位,随各文档定稿修订——doc-conventions §8 既定清偿路径);a2_/a5_/d1_/b1_/b4_ 前缀键名与夹具名同批改领域命名(minesweeper_/aux_/hud_/settlement_/reward_)。 entities: - name: config_db type: service - owner: design/archive/gdd-2026-09-14/d2-data-config.md + owner: design/gdd/systems/表现与支撑/数据配置.md attributes: kind: autoload role: 数值表单点访问入口(类型化属性) @@ -16,24 +17,24 @@ entities: - name: rng_service type: service - owner: design/archive/gdd-2026-09-14/d2-data-config.md + owner: design/gdd/systems/表现与支撑/数据配置.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_names: 当前封闭清单(增流需改代码 + P1 支柱评审;combat_var 已于 2026-09-07 full 评审删除——玩法随机不预置基础设施;**encounter 增流 2026-09-12 用户拍板**——遭遇随机化批 难度曲线与Boss 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 合规) + scope_event_binding: board_gen=每次生成新扫雷图(棋盘生成);reward_roll=每次 board install(格奖励批 + 清图奖励批)(奖励道具经济——2026-09-09 接口漂移改写,原"每次奖励结算 roll 批次";奖励道具经济 Rule 2/AC-1 为契约源);encounter=每次战斗实例化恰一 scope(战斗结算 begin_battle 实例化期、扫雷开始前——unit_count 区间均匀骰,挖掘决策域之外 P1 合规) unseeded_access: 致命中止 - snapshot_contract: 续玩/复盘快照 = master_seed + 各流 scope 计数器(消费方 D3) - referenced_by: [A1, B1, B4, C2, D3] + snapshot_contract: 续玩/复盘快照 = master_seed + 各流 scope 计数器(消费方 存档序列化) + referenced_by: [棋盘生成, 战斗结算, 奖励道具经济, 冒险结构, 存档序列化] - name: stream_seed_derivation type: formula - owner: design/archive/gdd-2026-09-14/d2-data-config.md + owner: design/gdd/systems/表现与支撑/数据配置.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 增流补向量) + golden_vectors: 见《数据配置》Formulas-R2 向量表(6 常量:board_gen m=42 scope0-3 · reward_roll m=42 scope0=7422916049646135537 · encounter m=42 scope0=1061841193803499209——2026-09-14 数据配置 漂移批 encounter 增流补向量) salt_board_gen: 0xA0761D6478BD642F salt_reward_roll: 0xE7037ED1A0B428DB salt_encounter: 0x8EBC6AF09C88C6E3(2026-09-14 encounter 增流——互异奇常数 ≠ 乘数) @@ -41,32 +42,32 @@ entities: 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] + set_master_seed_precondition: 仅全流计数器全零时受理(3 流——2026-09-14 数据配置 漂移批口径) + referenced_by: [棋盘生成, 战斗结算, 奖励道具经济, 冒险结构, 存档序列化] - name: difficulty_row_resolution type: formula - owner: design/archive/gdd-2026-09-14/d2-data-config.md + owner: design/gdd/systems/表现与支撑/数据配置.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 期交付物) + warning_purpose: 开发期异常信号(非审计工具);难度审计=离线曲线导出工具(难度曲线与Boss 期交付物) prerequisite: 每 level 至少 1 行且含 depth-0 锚行(Rule 6 校验) - referenced_by: [A1, C3] + referenced_by: [棋盘生成, 难度曲线与Boss] - name: schema_version type: constant - owner: design/archive/gdd-2026-09-14/d2-data-config.md + owner: design/gdd/systems/表现与支撑/数据配置.md attributes: value: 实现期定(MVP=1);全部占位 schema 均携带版本字段 - high_version_policy: 拒读(比较逻辑 owner=D3) - referenced_by: [D3] + high_version_policy: 拒读(比较逻辑 owner=存档序列化) + referenced_by: [存档序列化] - name: action_economy type: constant_set - owner: design/archive/gdd-2026-09-14/a3-action-economy.md + owner: design/gdd/systems/排雷循环/行动经济.md attributes: - table: ActionEconomyTable(D2 承载,单行全局;A3 为回填 owner;2026-09-07 三次回填(RAP 结构修订)——四字段删除 + rap_refresh_cost 新增,见 A3 Schema 定稿节) + table: ActionEconomyTable(数据配置 承载,单行全局;行动经济 为回填 owner;2026-09-07 三次回填(RAP 结构修订)——四字段删除 + rap_refresh_cost 新增,见 行动经济 Schema 定稿节) ap_per_round: 8(原型 v2 定稿,12 点已否决;双重身份:节奏 knob 兼停手 RAP 转存规模) dig_ap_cost: 1(连锁展开免费,计入清图进度) flag_place_ap_cost: 1(旗无实物资源/无数量上限——2026-09-07 用户修订,稀缺性由 AP 承载;限小人 8 向相邻) @@ -74,46 +75,46 @@ entities: 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] + rap_rules: RAP = 停手剩余 AP 1:1 转存(结构,非 knob);战斗域生命周期(battle_over 清零,跨轮跨图保留);来源唯一(停手)、支出唯一(刷新);刷新决策主体 = 玩家发起、挖掘期(Active 相位)当场换图——HUD框架 经组合根/战斗结算 refresh_board 编排调用(2026-09-12 音频 修订:原"结算编排期 Waiting 代理调用"退役);每轮上限 2 次(refresh_cap_per_round 结构位默认 2——HUD框架 提案采纳,RoundStart 重置;原"同一 Waiting 相位多次刷新合法/无结构上限"作废);旧板旗随旧板作废(零兑付、旗 AP 不返还——排雷玩法 EC11 承载);挖掘期当场刷新无结算发生(原"敌方时间税"自惩失效——行动经济 OQ3 阈值联动复核);cleared 轮恒 new_board、stopped/ap_exhausted 轮恒 same_board(战斗结算 回发违约 fail-fast 不变);刷新图不产生清图奖励;无战斗收益——原 ×2/×1 折算与 stop/clear_multiplier 已作废(2026-09-07 用户拍板) + flag_rules: 旗 = 排雷玩法 棋盘状态单态(原口袋旗双态作废);随图存续,清图/刷新消失;标中旗 = 怪物 debuff(三向效果定稿),兑付一次性、资格以雷格实例为键(每雷本次战斗至多兑付一次;放置实例仅为当轮结算载体;拔旗重插不恢复已消费资格——2026-09-07 三轮评审改定,OQ5/F1 条款 2) + round_end_carrier: 双发射源(2026-09-07 修订)——棋盘路径(ap_exhausted/cleared)由 排雷玩法 外发(systems-index 原裁决不变),停手路径(stopped)由 行动经济 直发;payload 同构 (path_tag, continuation_hint) + arbitration_priority: cleared > stopped > ap_exhausted(同帧/同批次结束路径总仲裁;stop-first 批次腿 deferral 至 排雷玩法 集成测试,义务清单见 systems-index 门禁记录) + downstream_contract: 行动经济 F1 五条款(① 效果当量对照单一线性换算 · ② 标中旗锚严格 > 最低档 + 连锁放大边际基准 + 总吞吐对账 · ③ 挖掘风险不等式 P(安全)×buff > P(雷)×敌方增益,战斗结算 单一闭合 owner · ④ 清图奖励 > 0 且 奖励道具经济 差异化插槽(图类型战斗域恒定) · ⑤ 清图 vs 弃图刷新支配性,战斗结算 闭合强制检查点)——2026-09-07 三轮评审定稿 + cross_field_validation: 任一动作成本(dig/flag_place/flag_pull)≤ ap_per_round——ActionEconomyTable schema validate() 跨字段类别,违规加载期致命(2026-09-07 三轮评审增补;条件性夹具登记 数据配置 校验双通道) + referenced_by: [棋盘生成, 排雷玩法, 地形变异, 战斗结算, 增益状态, 奖励道具经济, HUD框架, 教学引导] - name: variation_table type: schema_data - owner: design/archive/gdd-2026-09-14/a4-terrain-variation.md + owner: design/gdd/systems/排雷循环/地形变异.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] + table: VariationTable(数据配置 承载,键 = variant_id;地形变异 回填 owner,2026-09-08 定稿回填) + fields: variant_id(String 主键唯一)/ display_name / unlock_level(int [1,999],knob)/ scope(enum region|global,global 预留 难度曲线与Boss)/ info_mask(enum none|hide_numbers)/ cell_constraint(enum any|safe_nonzero)/ biome_tag(StringList,∅=通配,knob)/ counter_item_ids(StringList,辅助道具 预留,非 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]【辅助道具 定稿回填登记——数据配置 表值与悬空校验激活随 数据配置/辅助道具 修订批同批落地;回填前为 ∅】);岩浆/冰冻无 MVP 行(垂直切片随关卡解锁建行,建行批随批配 ≥1 反制道具——aux_items 框架内容契约) + load_bearing_rule: 生成期布置约束——雾区只能布置在非 0 安全格(区内无雷、无 0 格,棋盘生成 生成期强制);"雾区无雷"为玩家可见知识(教学引导 教学义务);结构推论(2026-09-08 full 评审入文,同日 re-review 钉死度量):雾区厚度≤2【≡任意 3×3 窗口非全雾,非直线行程读法】、雾面积占比 f≤8d 必要上界——棋盘生成 参数域硬边界(棋盘生成 验收清单 15 项之⑫) + zero_special_casing: 连锁/数字计数/清图/旗/价目/结算零特判——既有系统行为不感知变异状态(排雷玩法 执行逻辑无变异分支;地形变异 Rule 3 的推论) + rule4_exemption: 定义表非进度成长表,键 = variant_id,进度语义承载于 unlock_level 列(数据配置 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 连锁停止;地形变异 夹具 variation_mask_constraint 承载) + dangling_ref_policy: counter_item_ids 为 数据配置 首个引用型字段——**首引已由 辅助道具 定稿(probe_dust,2026-09-14 辅助道具 GDD)**:悬空校验激活 + 表值回填同批(数据配置 修订批;条件性夹具 aux_dangling_counter 登记 辅助道具 GDD AC-13——地形变异 测试口径"首个道具 id 回填时补校验"条款即 辅助道具-1 义务,闭环) + fog_ap_note: (2026-09-08 full 评审改两态;同日 re-review 改增量构成)雾不改 AP 价目但改清图预算构成,雾格 AP 成本两态——链可达雾格(邻非雾 0 格)免费链开、链不可达雾格全价 dig_ap_cost;雾致预算增量 = 链不可达雾格数 × dig_ap_cost,计入清图总预算需求参与 棋盘生成 BoardGenTable 设计期核算(原"≤ 轮数上限 × ap_per_round"不等式作废——"轮数上限"无定义;lean 版"全价结算"表述作废);行动经济 F1 条款 5 对账 = 三腿入账非消解:雷险腿消解(雾区无雷无 50/50)+ 信息腿双向净效应(雾边寂静减项 × 雾边推理锚增项,禁单向补偿)+ AP 腿(链不可达全价抬高清图成本)——战斗结算 条款 5 强制检查点输入,雾板/无雾板两态验算 + 两条方向性护栏(雾板净当量优势带上界·防福利 / 防钓鱼·对账 行动经济 OQ6) + pricing_constraints: 奖励道具经济 三条定价硬约束(2026-09-08 re-review 定稿三条)——① 雾内奖励档位下界 ≥ 最低档安全格 buff 当量(反淹没下界,per-AP 平价归 战斗结算/奖励道具经济 对账)② 清图奖励锚随雾成本负担上浮(链不可达同分母,防面积/成本分母脱钩)③ 雾板净当量优势带上界(防福利)——奖励道具经济 GDD 验收项 + referenced_by: [棋盘生成, 排雷玩法, 辅助道具, 战斗结算, 奖励道具经济, 难度曲线与Boss, HUD框架, 教学引导] - name: variant_pool_gate type: formula - owner: design/archive/gdd-2026-09-14/a4-terrain-variation.md + owner: design/gdd/systems/排雷循环/地形变异.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] + rule: pool = {v ∈ VariationTable | unlock_level ≤ level_index ∧ (biome_tag=∅ ∨ biome_tag ∩ board_biome ≠ ∅)};直接整数比较(无 数据配置 R1 floor);∅ = 通配语义 + output_range: pool 集合(MVP ≤1);空池合法——棋盘生成 静默布置普通图,不报错 + board_biome_type: StringSet(值域与来源归 棋盘生成 BoardGenTable biome 字段;非空为 F1 输入前提——域校验登记 棋盘生成 验收⑮,fail-fast 闭合) + downstream: 棋盘生成 布置候选池门控(棋盘生成 验收清单 15 项之首——2026-09-08 full 评审由 11 项扩容,清单已登记 systems-index 门禁记录);多变异选取须消费 board_gen 流(回放确定性) + referenced_by: [棋盘生成] - name: status_effect type: schema_data - owner: design/archive/gdd-2026-09-14/b3-status-effect.md + owner: design/gdd/systems/战斗循环/增益状态.md attributes: - table: StatusEffectTable(D2 承载,键 = effect_id;B3 回填 owner,2026-09-08 定稿回填) + table: StatusEffectTable(数据配置 承载,键 = effect_id;增益状态 回填 owner,2026-09-08 定稿回填) fields: effect_id: String 主键唯一 display_name: String @@ -125,453 +126,455 @@ entities: 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 接口漂移批回写落地) + 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 帽——难度曲线与Boss 投值,增益状态/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] + enrage: { display_name: 暴走(敌方增伤), target_side: enemy, magnitude: "+25 percent", duration_settlements: 1, max_stack: 16, stack_policy: stack, source_class: flag_miss } # 2026-09-12 标错反噬批上线——三池之一(进 enemy_output 池) + off_guard: { display_name: 失衡(我方承伤加深), target_side: hero, magnitude: "+25 percent", duration_settlements: 1, max_stack: 16, stack_policy: stack, source_class: flag_miss } # 2026-09-12 标错反噬批上线——三池之一(进 hero_defense 池,承伤 ×1.25) + value_status: 六行值全部 provisional(2026-09-15 四行→六行——标错反噬批 enrage/off_guard 两行收编;伏击复用为反噬第三池项),验收锚——weaken→行动经济 F1 条款 2(奖励道具经济/战斗结算)· ambush 量纲→敌人系统(条款 3,**量纲已定稿见 ambush_unit_definition**)· 对照表→战斗结算(条款 1)· **weaken 已按 战斗结算 F0 裁决修订 −25→−65(2026-09-10,值域 [60,85]、终值共裁 战斗结算 OQ2——见 anchor_warning 回执)** + ambush_unit_definition: **量纲已定稿(2026-09-09 敌人系统 GDD Rule 6/F2——registry OQ2 关闭条件兑现)**:+3 flat = 每层伏击为每次攻击类行为(attack/heavy)附加 3 点主角 HP 伤害(后置加法、不进倍乘);charge 拍零兑付(空拍蒸发);值仍 provisional(锚验收归 奖励道具经济/战斗结算——行动经济 F1 条款 3;敌人系统 供给 cap 表值与单结算自然边界口径见 clause3_supply 条目) + anchor_warning: weaken 行 vs 行动经济 F1 条款 2 锚两独立线性代理同向预检不过(百分点·结算暴露 25 vs 100;原型战斗量级 ≈1 vs ≈4 当量)——战斗结算 对照验收优先裁决,登记 战斗结算 强制检查点输入。**修复空间不对称(2026-09-09 增补)**:每结算瞬时当量归一 m_w≥51 可行(F2 越域界随移 AP 界 −408%/表界 −816%);每 AP 总暴露归一 m_w>100 被 percent ∈ [-100,100] 域禁止(该分支不可行)——OQ1 前置步骤:战斗结算 先钉死归一口径,口径落定前禁止数值修订;落总暴露则现实解 = 奖励道具经济 重定最低档参照。**参照对象迁移注记(2026-09-09 奖励道具经济 评审;2026-09-11 回迁)**:最低档安全格 buff 当量的参照对象曾迁 defense_up 当量(attack_up 时为死行——奖励道具经济 F4 迁移)——**2026-09-11 用户裁决"基础攻击恒存在"(HeroTable.atk 域 [1,999]、0 加载拒绝、MVP=6)后 attack_up 复活,参照对象回迁 attack_up 当量**(消费面 = 主角战斗段先手攻击——战斗结算 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 战斗结算 重算批兑现(F0/F1 按 attack_up 当量重跑 + F1 锚对照实物改 attack_up 行——本迁移注记关闭)。**F0 裁决回执(2026-09-10 战斗结算 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 战斗结算 重算批按 难度曲线与Boss v3 曲线重推——原 7.2 随口径退役〕);地板 = 乘数零点 −100pp、交叉层数 = ⌈100/m_w⌉ = 2(原"7 层触地板"预读按 −400% 合成带估读,更正);defense_up 参照保持(总暴露分支的"重定参照"现实解未采纳);终值 = 奖励道具经济/战斗结算 共裁(战斗结算 OQ2);增益状态 GDD 正文不改以本条为准(收编先例) + rule4_exemption: 定义表非进度成长表,键 = effect_id(数据配置 Rule 4 豁免,schema 注释承载) + single_enemy_assumption: **回验结果 = 不成立——敌人系统 定多敌(用户 2026-09-09 拍板;同日 敌人系统 GDD 兑现 增益状态 预授权接口漂移条款)**:MVP 战斗 = 1–3 个同敌种敌单位(EnemyTable.unit_count);效果 side-wide 语义改"全体存活敌单位"(层数 = side 共享池,**增益状态 现接口零变更兼容**);target_ref(单体寻址)= 扩展位义务——首个单体效果上线时 增益状态 补列(随批:战斗结算 归集语义/HUD框架 呈现裁决);增益状态 GDD 正文不改,以本条为准(行动经济 OQ5 收编先例) + mechanics: 战斗域统一状态容器(三向效果同构实例)· 单一时限量纲 remaining_settlements(先兑付后衰减,每轮结算 −1,dur=1=一次性;跨轮跨图延续、battle_over 全清不跨战斗)· 按行叠层(stack 授予不触碰 R;refresh 重置取实例授予时快照 D;合并在授予时、结算作用顺序归 战斗结算——两序分离)· 满层授予拒绝原子(false+容器零写)· 合成 = L×m 加算、percent 越域不 clamp(地板/换算归 战斗结算 SettlementTable)· 授予集中在 战斗结算 结算编排(排雷玩法/奖励道具经济 零直接接口)· 零随机(无 RNG 流消费)· 零信号外发(纯被动容器) + api: notify_battle_start / apply_effect(effect_id, source) -> bool / query_active(side) -> Array(插入序;六归因字段+display_name+授予时快照投影)/ decay_round() -> int / notify_battle_end() -> bool——战斗结算 消费契约,provisional 登记 + settlement_obligations: 八项(战斗结算 GDD 门禁评审缺项即 blocking)——① 同轮多授予按确定次序调用 ② 事实→授予恰一次转化(增益状态 不去重)③ percent→乘数换算方向与合成值地板成文 ④ 最后一轮 decay 判定 ⑤ F1 条款 1 效果档位对照表以 增益状态 实例为对照实物 ⑥ 每结算编排恰一次 decay_round(2026-09-09 增——decay 基数=1 先决条件)⑦ 结算明细按授予流水逐条标注来源(合并坍缩归因补偿,2026-09-09 增)⑧ “溢出未生效”行进结算明细协议(Rule 5 后半承载,2026-09-09 增)。**履行回执(2026-09-10)**:八项已全数落位 战斗结算 GDD(Core Rules 3/5/8/10/11 + Formulas F0–F2——落位映射 = 战斗结算 AC 覆盖矩阵"上游义务 23 条"行;③的 AP/表界数值随 F0 修订更新为 −520%/−1040%) + presentation_obligations: (2026-09-09 登记 systems-index,缺项即 blocking)HUD框架×4——HUD 常显+EC12 空态规格定稿 / effect_id→icon 映射键契约(形状+色相双通道)/ 有利有害极性呈现映射(数据源 = 战斗结算 SettlementTable 效果语义,非 magnitude 符号)/ 全清状态栏去留;战斗表现×2——溢出行呈现 / origin_cell→玩家可读形态(缩略图/格子高亮);教学引导——“同轮拿满优于跨轮叠”教学(OQ7 配套);奖励道具经济——奖励身份预读(创意风险 #1 切面) + ownership_note: 行动经济 OQ5“标中旗 debuff 档位归 奖励道具经济 表设计”经用户 2026-09-08 拍板收编——初值定稿权归 增益状态(2026-09-15 起为六行:标错反噬批两行随批收编),锚验收权仍归 奖励道具经济/战斗结算,修订走接口漂移回写本条 + dangling_ref_policy: effect_id 悬空校验**已激活**(2026-09-09 奖励道具经济 RewardTable 首个 effect_id 引用行〔defense_up〕回填——dangling_ref_policy"首个引用字段 owner 补"条款兑现;条件性夹具 = 奖励道具经济 reward_dangling_effect〔AC-17〕) + referenced_by: [战斗结算, 奖励道具经济, 敌人系统, HUD框架, 教学引导, 战斗表现] - name: board_gen_table type: schema_data - owner: design/archive/gdd-2026-09-14/a1-board-generation.md + owner: design/gdd/systems/排雷循环/棋盘生成.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 同构 + 双发射源: round_end 有两个合法发射者——棋盘路径由 排雷玩法 发、停手路径由 行动经济 直发,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 接口漂移批同步) + 行为环: 敌种出招的固定顺序表(如攻击→蓄力→重击),按结算轮转、环尾回环首——零随机、可学习(敌人系统 首次定义) + 拍: 行为环里的一格行动("这一拍是重击")——敌人系统 首次定义 + 同步轮转: 同种敌人永远处于同一拍,任何死亡时序都不会让幸存者乱拍(敌人系统 F1 定理) + 伏击池: 敌方侧共享的伏击层数——本轮踩几颗雷就几层,轮末清零(side-wide 语义,敌人系统 首次定义) + 溢出(伏击): 单轮踩雷超过上限(cap)的部分——转为"溢出未生效"明细行可见、不生效(敌人系统 首次定义) + 死亡即不动手: 轮到某敌人行动时它已死 → 这一下不打、招式轮转停格(2026-09-09 用户拍板,敌人系统 首次定义) + 资格格: 开出时 reward_eligible=true 的格——排雷玩法 事件面上 chain/dig 通道的安全格(含链开雾格);install 期可精确枚举为 S∖R₀(奖励道具经济 首次定义,F1 消费) + 刀: 伤害道具(damage_item)的项目行话,全文同义(奖励道具经济 Rule 5 首次定义);"烧刀/烧包" = 把无法生效的道具白白消耗掉(选定即承诺的成本,奖励道具经济 E10/E12 语境) + 批尾: 同一批取号中最后消费的一个号——奖励道具经济 清图奖励身份 = install 批的批尾取号(奖励道具经济 F1 首次定义) + 死行: 被当前内容态判定为零消费的表行——加载不致命、不入参考池,复活走 GDD 修订连锁(奖励道具经济 F4 首次定义;历史首例 attack_up〔hero atk=0 时代〕已于 2026-09-11 随"基础攻击恒存在"用户裁决复活,当前内容态暂无在册死行) + 档位序检查: 雾池最低活档 ≥ 最低活 buff 档的序数比较(原"档位序代理"更名,奖励道具经济 AC-19)——tier 与当量零耦合,仅序数健全性、非当量保证(奖励道具经济 F2 注记) + 预推进: 测试中为到达 scope=σ 而先行丢弃式消费 σ 个 scope 的构造——RngService 全新实例首个 open_stream 恒为 scope 0,derive_at 不推计数器(奖励道具经济 AC 前言首次定义) + 完美排雷(perfect_sweep): 板级零误标(flag_judged hit=false=0)∧ ≥1 标中 → 清图奖励当量 ×P_perfect(默认 ×2)——与踩雷正交(盲挖踩雷不破坏、标错才破坏)、全有或全无谓词无部分档、板级计数随 install 重置(战斗结算 Core Rule 13 首次定义) + 当量: 统一价值单位,1 当量 = 1 点主角 HP 的期望净变化——四源收益单一线性换算的通货(战斗结算 F1 首次定义) + 乘区: 伤害公式里相乘的修正段(本作两段:hero 减伤池 × 敌方削弱池——池内加算、clamp [−100,+100]pp、池间乘算、floor 取整)(战斗结算 F2 首次定义) + 溢杀: 一刀超出目标剩余 HP 的部分——丢弃、不转移、无负 hp 中间态(战斗结算 F3 首次定义) + 免伤轮: 主角该结算承伤为 0 的结算——percent 池地板(−100pp → 乘数 0)的自然结果;现行值链可达路径 = weaken 饱和(敌侧单结算零输出),defense_up 叠满 = 0.4×(−30×2 帽)不再单侧触地板(战斗结算 F2 首次定义;原"defense_up 顶点 −100% 恰在地板边界"例随减伤墙批退役——2026-09-14 增益状态 接口漂移批同步) 图标键(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] + 命令式回执驱动: 动作调用返回后组合根立刻重查一次只读面来刷新界面、不等通知信号——旗插拔/移动/AP·RAP 读数/状态/库存五类动作零事件外发或零推送,呈现层刷新由此承载(HUD框架 首次定义;ADR-0001 订阅纪律的配对形态) + 程序化占位: 用代码按冻结色板与形状规则直接画出的临时图形——参数取自 art-bible §4.1/§3 参数域、icon 按 §4.6 键位映射表挂槽,终稿美术到位后原位替换、数据层零改动(HUD框架 首次定义;样张未定稿不阻塞呈现层开工的承载形态,占位同样受 §1.4 数字可读性第一验收项约束) + 叠加层(overlay): 辅助道具 自持的板级派生信息集(显影/真视/预读三集)——只改"玩家能看见什么"不改棋盘事实,零回写 排雷玩法/奖励道具经济 状态、随图消亡(辅助道具 首次定义) + 真视窄域护栏: 替代推理的道具效果(雷位真视)目标域 ≤3×3、全图级仅限恢复推理原料类(显影/预读)——信息层级护栏,防"扫雷退化为路径优化"(辅助道具 Core Rule 3 首次定义,schema 校验强制) + 渗透率: aux 道具期望件数占奖励池的份额——E[N_aux/板] = |U|×Σw_aux/W_total(辅助道具 F1 首次定义;供给面天然限流,P1 护栏的量化形态) + 行动: 轮次内挖掘期的原子决策单位——同一事件的三个视角:玩家决策层叫行动、进 排雷玩法 的数据形态叫意图(单意图帧整批提交)、行动经济 计价类别叫动作(action_kind 统一价目);有价行动(挖/插旗/拔旗)1 AP/次、受每轮 AP 预算约束(现行表值 8),移动/停手免费不计 AP(game-concept 术语层级 2026-09-14 收录) + 节点: 关卡节点地图上的一个格——关卡内移动的步进单位;层名定稿(2026-09-14 用户裁决:节点 胜 遭遇——成员中立 + 关卡节点地图 注册名/切片 node_* 接口零改动 + 遭遇既有语义 = 战斗不挪用);节点类型分类学与分支机制归 关卡节点地图 GDD 展开 + 遭遇: 一次完整战斗事件的同义词——战斗节点触发的那一场仗(既有用法:战斗结算"两场遭遇" / HUD框架"双敌种遭遇""Boss 遭遇变体");不作地图层名(2026-09-14 裁决) + 节点奖励: 奖励点节点给的奖励(关卡节点地图 域)——与安全格奖励(奖励道具经济,格内开出)区分;节点奖励覆盖层 = 未来 spec design/ux/node-reward.md(battle.md OQ9)——三个"reward"各归各位(2026-09-14 拆名注记) + HP 账本: 冒险域主角血量的唯一记录——冒险结构 持有,每战开始交出、战斗结束收回、恢复点与大奖励入账;关卡切换血条不清零(冒险结构 首次定义;跨战斗 HP 持久性归 关卡节点地图/冒险结构 已锁事实的 冒险结构 半边兑现) + 关卡收束点: 一关就此定局的时点——Boss 战胜利(通关)或英雄死亡(失败);关卡节点地图 在收束点先调 战斗结算 关卡清库、再向 冒险结构 宣布关卡结束与结果(冒险结构 首次定义;切片 level_runner 先例转正) + 关卡大奖励: 清完一关、进入下一关前的一次性兑付——上限提升 + 血回满 + 代币记账三件构成,不进背包不占库存域;末关通关不兑付(冒险结构 首次定义;game-concept"冒险内大奖励"的落地形态) + referenced_by: [排雷玩法, 敌人系统, 奖励道具经济, 战斗结算, HUD框架, 辅助道具, art-bible, game-concept, 关卡节点地图, 冒险结构] - name: enemy type: schema_data - owner: design/archive/gdd-2026-09-14/b2-enemy-system.md + owner: design/gdd/systems/战斗循环/敌人系统.md attributes: - table: EnemyTable(D2 承载,键 (level_index, node_depth, enemy_id);B2 回填 owner,2026-09-09 定稿回填——进度键 floor(D2 R1)+ 实体键精确匹配) + table: EnemyTable(数据配置 承载,键 (level_index, node_depth, enemy_id);敌人系统 回填 owner,2026-09-09 定稿回填——进度键 floor(数据配置 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) + enemy_id: String(行集内主键;意图视觉映射键——视觉推测型 HUD框架×4①) + display_name: String(本地化归 设置与本地化) + unit_count: int [1,3](knob——**遭遇区间下界**〔2026-09-14 难度曲线与Boss 修订批:unit_count 升级区间语义 [unit_count, unit_count_max],战斗结算.begin_battle 实例化期经 RngService encounter 封闭流均匀骰;新敌种首遇/教学位节点固定不加骰〕;默认 1 provisional,多敌语义默认零消费者复验归 关卡节点地图〔敌人系统 OQ2〕) + unit_count_max: int [unit_count, 3] 可省略(2026-09-14 难度曲线与Boss 修订批新增——遭遇区间上界;缺省 = 定值,旧行零迁移) + atk_min/atk_max: 可选配对 int [0,999](2026-09-14 敌方攻击区间批——上阶/下阶区间随机;缺省 = 定值行向后兼容;跨字段:必成对 · **atk = (min+max)/2 中点锚**;roll 时机 = 遭遇骰同流〔encounter 恰一 scope,数量骰先、atk 骰后〕spawn 期**整战持久**〔每拍抖动否决——数字可读性锚〕,战斗结算.begin_battle roll → 敌人系统.spawn enemy_atk_override 收确定值〔敌人系统 零随机纪律保持〕;MVP 六行 ±3 provisional 归 难度曲线与Boss;验收 = 完美策略档 0 加成通关 ≤40%〔OQ7〕;接口漂移 = 敌人系统.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 归 难度曲线与Boss) 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] + heavy_multiplier: int [1,16](knob;无 heavy 拍敌种不消费——非校验项,增益状态 max_stack 先例;MVP 2 provisional) + pattern: StringList(行为环;元素 ∈ {attack, charge, heavy} MVP 校验域——guard/boost/hinder/summon 预留值校验拒绝〔棋盘生成 solver_rules 冻结先例〕;环长 [1,8]) + advantage_per_round_cap: int [0,16](knob;**MVP 默认 4 定稿**〔2026-09-09——反灾难护栏定位,旧默认 8 溢出协议死码已废弃〕;0 = 免疫伏击合法极端;行动经济 F1 条款 3 供给表值来源字段) + rowset_rule: 每行集恰一敌种(行集内 enemy_id 唯一 = 表结构校验,违规加载期致命)——多敌种同深度选择规则归 关卡节点地图/难度曲线与Boss(provisional);精英/Boss 编码位预登记 敌人系统 OQ7(键冻结下同深度第二敌种行触发行集致命——改键走 数据配置 Rule 4 接口漂移,关卡节点地图 定稿时强制核对) + mvp_content: **恰 3 敌种(2026-09-14 难度曲线与Boss 修订批转正——DND 更名批用户指令:grub→goblin / ambusher→bugbear / boss_guardian→ogre_chief;旧两敌种登记由本条替代;敌人系统 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 裁决 + 遭遇随机化批),曲线数值归 难度曲线与Boss + load_bearing_rules: 多敌模型(1–3 同敌种单位,增益状态 单敌假设回验不成立——见 status_effect.single_enemy_assumption)· 行为环确定性轮转(零随机;先作用后推进;死亡即不动手——轮到前被杀本拍不打指针冻结;同步轮转定理)· 效果 side-wide(伏击池 = side 共享,增益状态 零接口变更)· 行值快照抗热重载(增益状态 EC9 同款)· 零随机零 UI 零事件订阅(排雷玩法 事件面不含 敌人系统——事实经 战斗结算 转化) + settlement_obligations: 六项(敌人系统 GDD 门禁评审缺项即 blocking)——① 推进每结算恰一次(敌单位专属——主角无环)+ 结算内调用序成文 query_attack→apply_damage→advance ② 雷→伏击恰一次转化 ③ clamp/负值/溢杀与死亡判定时机成文 ④ F1 条款 3 闭合对账(cap 表值 + 量纲 + 棋盘生成 actuals p10 非 target 参数)⑤ 默认目标消费与单体/AOE 分流 ⑥ 伏击 cap 溢出"溢出未生效"明细行协议。**履行回执(2026-09-10)**:六项已全数落位 战斗结算 GDD(Core Rules 5/6/9/10/12 + Formulas F3/F4——落位映射 = 战斗结算 AC 覆盖矩阵) + hud_obligations: 四项(视觉推测型呈现,缺项即 blocking)——① enemy_id→图像线索映射清单成文(预告时序=下一拍预告、挖掘期全程可见;含强度档位/环长直觉编码说明;形状+色相双通道)② 映射全局恒定(enemy_id 键——跨 run 禁重掷)③ 顺位与存活可读 ④ 敌方 side 池呈现口径 + rule4_status: 本表是进度成长表(棋盘生成 BoardGenTable 同型,无 Rule 4 豁免) + referenced_by: [战斗结算, 奖励道具经济, 关卡节点地图, 难度曲线与Boss, HUD框架, 战斗表现, 教学引导] - name: hero type: schema_data - owner: design/archive/gdd-2026-09-14/b2-enemy-system.md + owner: design/gdd/systems/战斗循环/敌人系统.md attributes: - table: HeroTable(D2 新增 schema 行〔接口漂移回写,2026-09-09 B2〕,单行全局;B2 回填 owner) + table: HeroTable(数据配置 新增 schema 行〔接口漂移回写,2026-09-09 敌人系统〕,单行全局;敌人系统 回填 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] + atk: int [1,999](**0 = 加载拒绝**——基础攻击恒存在,"0 基线 × 百分比增益恒零"死锁自 schema 层杜绝〔2026-09-11 用户裁决,原"机制开关 MVP=0、激活裁决归 奖励道具经济/战斗结算"作废〕;MVP = 6 原型锚 provisional 归 难度曲线与Boss;消费面 = 主角战斗段先手攻击——战斗结算 GDD 2026-09-11 增补) + rule4_exemption: 单行全局定义表(主角不随关卡自动成长——成长=天赋 元进度/奖励侧;ActionEconomyTable 先例) + hp_persistence: 跨战斗 HP 持久性归 关卡节点地图/冒险结构(provisional)——敌人系统 单位接口以初始 hp 入参,不定义冒险内持久性 + referenced_by: [战斗结算, 奖励道具经济, 难度曲线与Boss] - name: reward_table type: schema_data - owner: design/archive/gdd-2026-09-14/b4-reward-item-economy.md + owner: design/gdd/systems/战斗循环/奖励道具经济.md attributes: - table: RewardTable(D2 承载,键 = reward_id;B4 回填 owner,2026-09-09 定稿回填、同日 full 评审增补 display_name——含 use_timing 字段〔game-concept"使用时机"预留的兑现,MVP 单值 pre_settlement 承重,扩枚举走修订〕) + table: RewardTable(数据配置 承载,键 = reward_id;奖励道具经济 回填 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/呈现零改动) + display_name: String 非空——玩家可读名(HUD框架 呈现数据源,本地化归 设置与本地化;2026-09-09 评审增补,敌人系统/增益状态 同库惯例) + power: int [0, 999](knob)——item 类加载校验 ≥ 1;buff 类不消费非校验(敌人系统 heavy_multiplier 先例) + power_min/power_max: 可选配对 int [1,999](2026-09-14 伤害量值区间批——上阶/下阶区间随机;缺省 = 定值行向后兼容〔armor_type 先例〕;跨字段:必成对 · buff 行禁止 · **power = (min+max)/2 中点锚**〔行动经济 条款①期望当量不漂移〕;roll 时机 = 身份预分配批内追加取号〔既有取号序零扰动〕,快照 E14 授予时口径不变、战斗结算/呈现零改动) 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 雾内档位下界约束载体 + pool_tag: enum {safe_cell, active_inject, clear_default}(2026-09-14 难度曲线与Boss 修订批增补 active_inject——供给控制器注入池〔可选类三行移出格子权重池,由 RewardSupplyController install 期注入:首件保证 + 指数递减〕;clear_ 命名空间预留、校验拒绝——棋盘生成 elite_ 同款) + tier: int [1, 8](knob)——地形变异 雾内档位下界约束载体 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] + cross_field_validation: 加载期致命族——池空(每 pool_tag × 雾/非雾两态各 ≥1 行 weight>0)· clear_default 恒正当量(≥1 活行 ∧ power≥1 ∨ effect_id 非空——行动经济 条款④"清图奖励>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 ∧ 全池零活刀行——零伤害世界后闸,敌人系统 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"注记退役;行动经济 条款 2 锚参照对象 = attack_up 当量)· 道具段照常消耗(选定即承诺:无存活敌的已选刀照常 Used + 零效果 + "已消耗未生效"明细行——理由 2026-09-09 评审更正为守卫所有权不对称 + 反抖动,原"敌 HP 不可见"与 敌人系统 血条数字可读矛盾作废)· **窗口锚点 = 授予段后、增益/减益段前(2026-09-09 评审用户拍板:当轮拾取当轮可用;核心段序 buff/debuff > 道具 > 战斗不变)**· 清图槽允许 buff 行(运行时满层零兑付 = 合法例外 + "奖励未生效"行,预警④监控)· F5 保底 = 草稿-校验-提交重 roll(K_dmg=0 / N_draft=32 provisional——**2026-09-14 难度曲线与Boss 修订批转正:K_dmg=0"禁区默认值"条款解除**,刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸仍托底零活刀行世界)· F2 雾档过滤(T_fog_min 默认 2 provisional;地形变异 约束①为验收锚非运行时校验——AC-19 档位序检查,战斗结算 对照表落定后升级)· **重复受理幂等(同格重复 grant / 同批次重复 grant_clear → 同一快照/身份 + 诊断;受理/兑付恰一次归 战斗结算×②③)** + api: 封闭枚举 10 入口(provisional,实现期 ADR 定名)——roll_board / query_identity(返回 {reward_id, category, tier} 引用级投影;未开格恒可查——辅助道具 桥接结构前提;清单外坐标 → 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 难度曲线与Boss 修订批——减伤墙批 + 供给控制器批 + clear_blast 换位批转正;值 provisional 归 难度曲线与Boss;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) 不变);战斗结算 条款⑤计价基元随换位重推(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 评审批登记,敌人系统 clause3_supply 先例)**——击杀需求 18(单敌)/ 54(3 敌,敌人系统 EnemyTable 参考行);刀伤供给期望/板 ≈ |资格格| × 40% × 5 = 2|资格格| → |资格格|=10 保守板:单敌 ≈0.9 板、3 敌 ≈2.7 板("单敌无忧 / 2–3 敌贴近下界"现值有锚);输入缺口 |资格格| 分布待 棋盘生成 actuals p10/p50 落地后重跑;治疗对照基准 max_hp=50(原型锚——HeroTable 值出处建议 敌人系统/数据配置 回写补登) + settlement_obligations: 六项(战斗结算 GDD 门禁评审缺项即 blocking,清单全文见 奖励道具经济 GDD Dependencies;2026-09-09 评审同步)——① 结算段序骨架(授予→**窗口**→增益/减益→道具→战斗→衰减→判定;窗口锚点 = 授予段后;窗口选择序 = 回放输入)② 受理分流 + **每格恰一次** + 满层零兑付"奖励未生效"行(含聚合计数字段)③ clear_reward 授予路径(增益状态 OQ4 关闭条件)④ 窗口与道具段编排(usable_now 组装/选定序/满血守卫 + 逐件动态守卫/零效果判定"已消耗未生效"行/**击杀结果进流水**/治疗 clamp)⑤ 生命周期编排(battle_over clear_inventory〔清空反馈计数〕/ retire 映射消亡)⑥ roll_board 恰一次。**履行回执(2026-09-10)**:六项已全数落位 战斗结算 GDD(Core Rules 4/5/7/9/编排专章——落位映射 = 战斗结算 AC 覆盖矩阵;缺口预检 2 阻塞级 deferral 已随 AC-7/AC-15/16/20 承载) + hud_obligations: 五项(缺项即 blocking;2026-09-09 评审 3→5 扩容)——① 已开格 + 库存身份恒定可读(库存常读条 = 挖掘期规划面)② 道具使用窗口呈现(授予段后弹出/选用/跳过/序/usable_now/触发政策注记)③ 结算明细四类行(道具/"奖励未生效"〔聚合计数〕/"已消耗未生效"/"使用被拒")④ H2 清图奖励行强调呈现 ⑤ battle_over 库存清空可见反馈 + pricing_constraints: 地形变异 三条定价硬约束承接——① 雾内档位下界(AC-19 档位序检查夹具对账型,战斗结算 对照表落定后升当量对账)② 清图锚随雾成本上浮(**MVP 结构性不可闭合——clear_default 常量池无法随雾负担变化,非 deferral;激活载体 = clear_ 插槽,战斗结算 检查点仅对常量判 pass/fail**)③ 雾板净当量优势带上界(**MVP 静默绿灯风险——②缺位 + 仅 tier 下界加成下近乎必然平凡通过**;战斗结算 强制检查点 + 预警⑤雾板分层/⑥速杀退化观测对冲)〔②③状态 2026-09-09 评审显式宣告〕 + high_glow_nodes: H1 开格拾取瞬间 / H2 清图奖励兑付 / H3 道具抢杀窗口(2026-09-09 评审诚实化:H1/H2 = 奖励侧常规节拍登记,H3 = 创意风险 #2 战斗侧执行兑现;**风险 #2 排雷侧"完美排雷爆发结算"机制本体归 战斗结算 强制检查点、#3 标中旗节拍补偿归 战斗结算——奖励道具经济 只交付插槽与明细行数据面**;呈现义务 HUD框架×④⑤) + referenced_by: [战斗结算, HUD框架, 难度曲线与Boss, 辅助道具, 战斗表现, 教学引导] - name: reward_config type: schema_data - owner: design/archive/gdd-2026-09-14/b4-reward-item-economy.md + owner: design/gdd/systems/战斗循环/奖励道具经济.md attributes: - table: RewardConfigTable(D2 承载,**单行全局**——2026-09-09 B4 full 评审接口漂移新增:B4 三个标量 knob 的 Rule 1 单一事实源载体,R3 override key 空间随之存在) + table: RewardConfigTable(数据配置 承载,**单行全局**——2026-09-09 奖励道具经济 full 评审接口漂移新增:奖励道具经济 三个标量 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 扫描) + k_dmg: int [0, 999](默认 **0**——2026-09-14 难度曲线与Boss 修订批转正〔切片 reward_config.tres 现值〕:原默认 1 与"难度曲线与Boss 禁区默认值"条款由供给控制器战斗首件保证取代;no_damage_row 加载后闸仍托底零活刀行世界) # was: 默认 1、K=0 = 难度曲线与Boss 禁区默认值 before 2026-09-14 + active_p0: float [0,1](默认 0.4 provisional——供给控制器首件之后每张新板注入概率初值;E[战斗获取] ≈ 1 标定目标,归 难度曲线与Boss) + active_decay: float (0,1](默认 0.5 provisional——注入概率指数递减 0.4→0.2→0.1→…永不绝育;加难杠杆候选 0.6 随 难度曲线与Boss 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] + referenced_by: [奖励道具经济, 难度曲线与Boss, 战斗结算] - name: settlement_table type: schema_data - owner: design/archive/gdd-2026-09-14/b1-settlement.md + owner: design/gdd/systems/战斗循环/战斗结算.md attributes: - table: SettlementTable(D2 承载,单行全局;B1 回填 owner,2026-09-10 定稿回填;Rule 4 豁免——单行全局常量/结构表,HeroTable/ActionEconomyTable 先例) + table: SettlementTable(数据配置 承载,单行全局;战斗结算 回填 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〕) + p_perfect: int {100,200} percent(knob,默认 200 provisional——完美排雷清图奖励**第二份兑付触发**;2026-09-10 战斗结算 评审批修订:真实双份交付载体〔Core Rule 13#2〕——100 = 仅高光、200 = 双份交付,非整数倍数乘数无运行时载体、域外加载致命;上界受护栏 B 约束〔双读数:D_ref 口径 24 / DPS 严格读数 ≈8,裁决归 难度曲线与Boss〕) 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 上线须随批登记) + source_label_map: Dictionary source_class→文案键(结构冻结——P2 归因呈现映射;键集 = 增益状态 source_class ∪ {道具/判定扩展, enemy_action, flag_miss}——2026-09-10 战斗结算 评审批修订;新增键 = 接口漂移) + effect_pool_map: Dictionary effect_id→pool {hero_offense|hero_defense|enemy_output|flat_attach}(结构冻结——F2 效果语义映射,增益状态 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 行按收编先例以本条为准) + pool_clamp_hi: int pp(结构冻结 +100——percent 池地板域上界;原"七字段 pool_clamp_lo_hi"措辞作废,数据配置 GDD 行按收编先例以本条为准) round_direction: enum {floor}(结构冻结——单一取整规则) - damage_a_per_cell: int ≥0(knob 默认 1 provisional——伤害A 开格充能/格;2026-09-14 护甲克制批转正登记,机制与结算归 B1、C3 投值) + damage_a_per_cell: int ≥0(knob 默认 1 provisional——伤害A 开格充能/格;2026-09-14 护甲克制批转正登记,机制与结算归 战斗结算、难度曲线与Boss 投值) 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] + 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 归 难度曲线与Boss) + validation: segment_order 违例致命 · effect_pool_map 悬空致命 · p_perfect 域 {100,200}(2026-09-10 载体域——100 = "完美必须 ≥ 不加倍"机制语义下界,200 = 双份交付)——战斗结算 冒烟三行致命 manifest(segorder_violation / poolmap_dangling / pperfect_oob) + mechanics: 结算段序骨架唯一权威载体(授予→道具使用窗口〔授予段后〕→增益/减益→道具→战斗→衰减→判定——用户 2026-09-09 拍板序)· 零随机(rng_service 流封闭不含结算流)· 可回放步骤流(同 master_seed 同意图序列同窗口选择序列 ⇒ 步骤流逐行相同——数据配置 AC-15 deferred→战斗结算 兑现落点)· 调用序钉死 query_attack→apply_damage→advance · 衰减恒执行含末轮 · 高光双落载体(perfect_sweep P_perfect——2026-09-10 修订为真实双份兑付载体 / 标中旗 highlight 标记)· 流水行协议字段分层(公共 7 字段 + 行类型特有 target/effect_id/pool_value/聚合计数——2026-09-10 评审批修订;行类型封闭枚举 15 类——2026-09-15 窗口锚行批由 13 类扩为 15 类,新增窗口打开锚行 window_open〔value = 本轮候选件数,「窗口段恒在案」打开态锚点〕与窗口跳过锚行 window_skip〔空库存不弹窗 / 玩家零选择两触发,value = 0〕,两类均不参与聚合统计;行来源断言两层豁免集合同批成文 = 行型豁免 {decay, verdict, window_open, window_skip} × 来源类别豁免 {hero_attack, enemy_action, clear_reward, item},豁免不等于免检——豁免行仍受公共字段与各自语义约束,清单改动须同步 战斗结算 明细流水一节与 battle_log_view 格式化)· **伤害A/B 充能与护甲克制结算规格 = 战斗结算 GDD F6(W3 补全,2026-09-14 重算批)**——挖掘期充能/战斗段消费/单轮生命周期、不吃 percent、逐分量 armor_effectiveness 查表 floor 相加 + closure_receipt: 行动经济 F1 条款①②③⑤闭合 owner = 战斗结算(2026-09-10 F0–F5 首版验算完成——F0 瞬时当量口径 + m_w 值域裁决见 status_effect.anchor_warning;条款③ clause3_supply 闭合回执;条款⑤ clause5_dominance 条目;actuals p10 交付后强制重跑) + referenced_by: [HUD框架, 战斗表现, 音频, 教学引导, 难度曲线与Boss] - name: clause3_supply type: formula - owner: design/archive/gdd-2026-09-14/b2-enemy-system.md + owner: design/gdd/systems/战斗循环/敌人系统.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 评审收紧);跨结算累计 = 轨迹量非表值 + rule: 敌人系统→战斗结算 挖掘风险不等式(行动经济 F1 条款 3)供给口径——每结算敌方伏击暴露精确式 = k × L × m_amb(k = 本结算存活敌中攻击/重击拍单位数,确定性可算);每结算结构上界 = unit_count × min(AP 上界, cap) × m_amb(cap > AP 界时 cap 腿松弛——2026-09-09 敌人系统 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] + account_input: 对账输入 = 棋盘生成 actuals p10(F4 报告 C)非 target 参数(棋盘生成 登记口径);闭合 owner = 战斗结算 单一签字人;左臂(安全格 buff 当量)载体 = **奖励道具经济 F6 当量供给**(2026-09-09 奖励道具经济 定稿——部分缓解非闭合:窗口使用次数是战斗轨迹量,敌人系统 OQ3 扩项保持;2026-09-11 起基础攻击恒在为左臂提供恒定伤害基底,原"当前内容态结构性不可通关"事实退役——敌人系统 Overview 2026-09-11 改写) + direction_precheck: cap=4 下右臂 ≈7.7;左臂按条款 2 最低档锚(+50pp)计 = 0.36×50 = 18——裕度 ≈2.3×(1:1 百分点代理,provisional;2026-09-09 敌人系统 full 评审更正原 4.7×——该值按 +100 两层叠满取值、与"最低档下界"语言矛盾);量级注记:atk=4 时 attack 拍 L≥2 伏击分量过半(heavy 拍过半点后移、按拍别读取;常态支配修正入口 = 难度曲线与Boss atk 曲线 / 奖励道具经济·战斗结算 m_amb 锚,敌人系统 权限外)。**参照迁移注记(2026-09-09 奖励道具经济 评审;2026-09-11 回迁)**:左臂参照对象曾迁 defense_up 当量(奖励道具经济 F4)——**2026-09-11 用户裁决"基础攻击恒存在"后 attack_up 复活,参照对象回迁 attack_up 当量**(上式 +50pp 系 attack_up 口径,迁移期结束;复算义务随 战斗结算 对照表落定,见 status_effect.anchor_warning 回迁注记)。**闭合回执(2026-09-10 战斗结算 GDD F4——条款③闭合完成;同日评审批双口径更正)**:统一 HP 当量 + 边际配对重算 → 默认 unit_count=1 下 L1 裕度 ≈2.0× / L2 ≈2.6× **通过**(锚行口径;**跨行期望口径 ≈1.2×/1.5× 同向通过——双口径登记,难度曲线与Boss 不得以锚行读数放行 unit_count=2**);**满配语境(unit_count=3)最坏盲点破线 → 难度曲线与Boss 调参约束登记**(unit_count 轴 + 密度×可推理率轴——unit_count>1 行须降密度或降 m_amb);本条 pp 代理预检(2.3×)退役为历史记录(量纲混用 + 右臂聚合上界与单格决策单位错配)。**重算回执(2026-09-14 战斗结算 重算批——按现行三输入重推:duration=1 × m_d=−30 × 难度曲线与Boss 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),修正方向须 难度曲线与Boss 提案 + 战斗结算 闭合对账共裁**;上段 ≈2.0×/2.6× 读数系 duration=2 × m_d=−50 × v1 曲线旧值链产物,退役为历史记录;actuals p10 交付后强制重跑(战斗结算 AC-39;ι 判式面保守代理 = 行 target 代,总暴露面无效) + referenced_by: [战斗结算] - name: clause5_dominance type: formula - owner: design/archive/gdd-2026-09-14/b1-settlement.md + owner: design/gdd/systems/战斗循环/战斗结算.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 三闸的对账复核,非运行期校验) + rule: 行动经济 F1 条款⑤清图 vs 弃图支配性闭合(战斗结算 单一闭合 owner,战斗结算 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)。**难度曲线与Boss/奖励道具经济 约束包**:① C 量级上调整向(护栏 B 口径联动复核)② 或裁"清图非严格支配"为设计态(与 地形变异 定价硬约束②同向;如裁设计态须同步声明 行动经济 条款⑤ P1 对冲弱化 + 奖励道具经济 预警⑤加权监控)——①②二选一归 难度曲线与Boss 裁;③ 多敌放行判据 = 条款③⑤在全部登记行组合下同时成立。判据函数角色 = 调参约束生成器(AC-40 断言输出与约束包一致) + c3_ruling: **约束包②采信(非支配设计态)+ 计价基元换位重推(2026-09-14 难度曲线与Boss 修订批)**——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——战斗结算 修订批对齐条款⑤措辞(义务登记,未兑现前 战斗结算 文本 C=40 按期望中点读)。**重算回执(2026-09-14 战斗结算 重算批——v3 曲线 d0.25/t0.88 × E_dig=4.5 重推,参考板形换锚 难度曲线与Boss 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 曲线旧值链产物退役;**难度曲线与Boss 裁决②非支配设计态维持**(qualitative 结构不变——非完美态负、完美态 L1 翻正、L2 全负);护栏 A 方向性复核 ≈−1.1 越下界(E_dig 修正 + 雾池构成变更)/ 护栏 B C=40 越线——W2 复核三选一裁决随 难度曲线与Boss 收口批;actuals 交付后按 clear_blast 语义强制重跑(战斗结算 AC-40 同批口径) + guardrails: 护栏 A 雾板净当量优势带(每链不可达雾格 +2.4 ∈ (0, 3]——地形变异 pricing_constraints③ 判据;与残余池模型无关读数不变;突破条件 E_fog > 11.7 → 难度曲线与Boss 雾池联动禁区)· 护栏 B 防钓鱼(**2026-09-10 输入登记更正 + 双读数**:|S|_min = 棋盘生成 行级 knob 禁写死;|R₀|_max 无登记供给方——登记 棋盘生成 产出义务〔R₀ 规模分布上界,报告 C 扩列〕;s_ref=D_ref=12 比真实敌方 DPS 期望松 2.5–3×→ D_ref 口径上界 24〔C_eff=16 ✓〕/ DPS 严格读数 ≈8〔C_eff=16 越〕——两读数裁决归 难度曲线与Boss,与约束包①联动;结构承载 = 棋盘生成 min_safe_count + 布雷数下限 + 排雷玩法 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] + rerun_obligation: actuals p10 / fog_burden_ratio p50·p90 交付后强制重跑(战斗结算 AC-40);难度曲线与Boss 每次调参重跑(D_ref/P_pf/C 联动) + referenced_by: [难度曲线与Boss, 奖励道具经济, 棋盘生成] # 占位表中的原型参考值(伏击 +3、清图 +10、敌 18HP/4ATK 等)为 provisional, -# 不登记为事实——待各 owner GDD(A4/B3/B4/B1/C3)定稿后由其登记。 -# (AP 等七字段已于 2026-09-07 由 A3 三次回填登记为 action_economy 条目;旗上限与折算乘数同日作废删除。) +# 不登记为事实——待各 owner GDD(地形变异/增益状态/奖励道具经济/战斗结算/难度曲线与Boss)定稿后由其登记。 +# (AP 等七字段已于 2026-09-07 由 行动经济 三次回填登记为 action_economy 条目;旗上限与折算乘数同日作废删除。) # 修订记录:2026-09-07 full 评审(design-review)——删除 combat_var 流、去重键改四元组、 -# 快照契约补计数器、R2 增实现级规约与 reward_roll 向量(owner=d2-data-config.md 代用户修订); +# 快照契约补计数器、R2 增实现级规约与 reward_roll 向量(owner=数据配置 代用户修订); # 同日 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 全条目重写—— +# + 独立 delta 复核 5/5 PASS → 数据配置 Approved(2026-09-07)。 +# 2026-09-07 行动经济 RAP 结构修订(owner=行动经济 代用户修订):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 / flag_rules / downstream_contract 事实;round_end 改双发射源(排雷玩法 棋盘路径 + 行动经济 停手路径)。 +# 2026-09-07 行动经济 三轮评审修订(owner=行动经济):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 阻塞同日核销): +# (增挖掘风险不等式 战斗结算 闭合 + 弃图支配性)· 增 cross_field_validation(任一动作成本 ≤ ap_per_round)。 +# 2026-09-08 地形变异 定稿回填(owner=地形变异):新增 variation_table 与 variant_pool_gate 条目; +# action_economy.referenced_by 增 地形变异(清图预算构成注记只读消费 ap_per_round=8,与注册表值一致零冲突)。 +# 2026-09-08 地形变异 full 评审修订(owner=地形变异,NEEDS REVISION 8 阻塞同日核销): # variation_table.load_bearing_rule 增结构推论(厚度≤2/f≤8d)· fog_ap_note 改写(全价→链不可达两态; # 条款 5"消解"→三腿入账两态验算)· variant_pool_gate 增 board_biome 域校验归属与 15 项验收门禁登记—— # 详见 design/archive/gdd-2026-09-14/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 · +# 2026-09-08 地形变异 re-review 修订(owner=地形变异,NEEDS REVISION #2 6 阻塞同日核销): +# variation_table 增 cross_field_validation(hide_numbers⇒safe_nonzero 致命)与 pricing_constraints(奖励道具经济 三 +# 硬约束)· counter_item_ids 修正为非 knob · mvp_content 的 knob 活值标注 · referenced_by 补 战斗结算/奖励道具经济 · # fog_ap_note 预算不等式改增量构成("轮数上限"作废)· 厚度≤2 钉死窗口谓词度量。 -# 2026-09-09 B3 full 评审修订(owner=b3-status-effect.md,NEEDS REVISION 7 blocking 同日核销): +# 2026-09-09 增益状态 full 评审修订(owner=增益状态,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 绝对锚)· +# settlement_obligations 5→8(⑥ decay 恰一次 ⑦ per-grant 来源标注 ⑧ 溢出行)· 增 single_enemy_assumption 与 +# presentation_obligations(HUD框架×4/战斗表现×2/教学引导/奖励道具经济)· anchor_warning 增修复空间不对称(总暴露读法被 schema 域禁止)。 +# 2026-09-09 棋盘生成 full 评审修订(owner=棋盘生成,MAJOR REVISION NEEDED 8 blocking 同日修订): +# board_gen_table 条目同步 战斗结算/增益状态/奖励道具经济/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 防碰撞 · 指标集六→八项 · 第三轴登记约定 +# 断言 B 收紧达成区占比(地形变异⑭ 两件套)· σ 打包 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 算子①同构不变); +# 2026-09-09 排雷玩法 定稿回填(owner=排雷玩法):新增 minesweeper_event_surface / minesweeper_initial_expansion 两条件目 +# (三事件回边契约 + 初始展开域与双包含兑现回执 + 棋盘生成 修订义务〔反例 B〕登记);排雷玩法 /design-review 须核对标错惩罚 OQ 在位。 +# 2026-09-09 排雷玩法 full 评审修订(owner=排雷玩法,NEEDS REVISION 6 blocking 同日核销): +# minesweeper_event_surface.api_surface 增 ActionOutcome.sub_reason 封闭枚举(UX-1,批前免费窗口)· HUD框架×1→HUD框架×6; +# minesweeper_initial_expansion 增诊断类别封闭枚举 + 链不越旗红线(GD-1——连锁传播集 ∖ G,G=∅ 域与 棋盘生成 算子①同构不变); # 新增 terminology 术语条目(用语纪律登记通道)。详见 design/archive/gdd-2026-09-14/reviews/a2-minesweeper-rules-review-log.md。 -# 2026-09-09 B2 定稿回填(owner=b2-enemy-system.md):新增 enemy / hero / clause3_supply 三条目(EnemyTable 八字段 + +# 2026-09-09 敌人系统 定稿回填(owner=敌人系统):新增 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 六词条。 +# status_effect 增 ambush_unit_definition(量纲定稿,OQ2 关闭条件)· terminology 增 敌人系统 六词条。 # 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 同批修订。 +# 主角战斗段先手攻击,战斗结算 GDD 同批增补 + source_label_map 增 hero_attack 键);锚参照回迁 attack_up 当量(status_effect. +# anchor_warning / clause3_supply.direction_precheck 两处迁移注记退役为历史);settlement_obligations 履行回执 2026-09-10 不变; +# 变更 owner = 敌人系统/奖励道具经济/战斗结算 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 +# 变更 owner = 奖励道具经济/战斗结算/增益状态 GDD 2026-09-11 同批修订。 +# 2026-09-09 敌人系统 full 评审修订(owner=敌人系统,NEEDS REVISION 8 blocking 同日核销): +# enemy.settlement_obligations ① 增调用序成文(query_attack→apply_damage→advance)与敌单位专属口径 · hud_obligations +# ① 增预告时序/强度档位、② 改全局恒定 · rowset_rule 增精英/Boss 编码位预登记(敌人系统 OQ7)· clause3_supply # 结构上界改 min(AP, cap) 形(cap 腿松弛收紧)· direction_precheck 裕度 4.7×→2.3× 更正(最低档锚口径)· # account_input 增胜利路径声明注记。详见 design/archive/gdd-2026-09-14/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 三约束承接 + 高光节点条目化);接口漂移改写三处—— +# 2026-09-09 奖励道具经济 定稿回填(owner=奖励道具经济):新增 reward_table 条目(schema 八字段 + 跨字段 +# 校验族 + 机制/API 面 + 战斗结算×6/HUD框架×3 义务清单 + 地形变异 三约束承接 + 高光节点条目化);接口漂移改写三处—— # 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 当量供给消费条款①②)。并行会话冲突经用户裁决:道具段"照常消耗" +# status_effect.dangling_ref_policy(首引条件兑现)、clause3_supply.account_input(左臂载体 = 奖励道具经济 F6); +# action_economy.referenced_by 增 奖励道具经济(F6 当量供给消费条款①②)。并行会话冲突经用户裁决:道具段"照常消耗" # 拍板为最终语义(覆盖并行草稿"拒绝不消耗"版),清图槽允许 buff 行拍板同批落定。 - - name: d1_presentation_surface + - name: hud_presentation_surface type: convention - owner: design/archive/gdd-2026-09-14/d1-hud-framework.md + owner: design/gdd/systems/表现与支撑/HUD框架.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} + read_map: 排雷玩法 current_board()/reveal_record()/compute_move_path() · 行动经济 getter-only phase/ap/rap/last_round_end · 战斗结算 presentation_snapshot()/is_battle_live()/stock_count()/query_reward_identity() + 步骤流信号 · 敌人系统 hero_unit()/all_enemies() · **增益状态/奖励道具经济 零直读**(经 战斗结算 呈现面与流水投影)· runner(节点链/当前节点/HP 携带) + write_paths: settle_batch(stop 亦经此,禁直调 行动经济)/ submit_window_selection(窗口条目位置序,非库存句柄) / 刷新意图 → 战斗结算 refresh_board 编排 / runner 推进——**全部唯一发生在组合根** + subscription_discipline: 呈现层永不直接订阅 排雷玩法/行动经济——只订 战斗结算 步骤流/快照与 runner 信号(ADR-0001 固化);接线全部在组合根 + imperative_refresh_faces: 零事件外发动作的刷新 = 命令式回执驱动(白话:动作调用返回后组合根立刻重查一次只读面刷新界面,不等通知信号)——旗插拔/移动成功(排雷玩法)/ AP·RAP 读数(行动经济 无推送)/ 状态效果(增益状态 零信号,绑 战斗结算 结算完成时点)/ 库存(奖励道具经济 查询面)五面成文 + payload_schema_index: HUD框架 GDD Core Rule 17 表 13 项——排雷玩法 三事件/战斗结算 流水行协议/奖励道具经济 窗口载荷等均为引用既有 schema;HUD框架 自有成文 = 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] + naming_unification: 正式名 request_refresh(行动经济)/ refresh_cap_per_round(结构位默认 2)——切片 provisional 名 request_refresh_active/can_request_refresh/refresh_max_per_round/during_round 退役(AC grep 断言零残留) + drift_registrations: ① 行动经济 只读观测量缺轮内已用刷新次数(刷新按钮"本轮剩 N 次"读数缺口——行动经济 修订批补)② 切片 settlement_snapshot 信号 战斗结算 GDD 未登记契约(HUD框架 现行消费 = 步骤流判定行 + presentation_snapshot(),独立信号须回 战斗结算 补登记)③ 极性提案实施批 HUD框架 义务条目(未开格类型图标/极性视觉语言/负向文案族——随批入 systems-index HUD框架 台账) + referenced_by: [战斗表现, 音频, 教学引导, 辅助道具, 设置与本地化] - - name: a5_aux_items + - name: aux_items type: schema_data - owner: design/archive/gdd-2026-09-14/a5-minesweeper-aux-items.md + owner: design/gdd/systems/排雷循环/辅助道具.md attributes: - table: AuxItemTable(D2 承载【D2×① 修订批登记】,键 = item_id;A5 回填 owner,2026-09-14 定稿登记;定义表非进度成长表——Rule 4 豁免;生效前提 = B4×①…⑤/D2×①② 修订批兑现) + table: AuxItemTable(数据配置 承载【数据配置×① 修订批登记】,键 = item_id;辅助道具 回填 owner,2026-09-14 定稿登记;定义表非进度成长表——Rule 4 豁免;生效前提 = 奖励道具经济×①…⑤/数据配置×①② 修订批兑现) fields: - item_id: String 主键唯一——B4 RewardTable.effect_id(aux 行)与 VariationTable.counter_item_ids 的引用键 - display_name: String 非空(D1 呈现数据源,本地化归 D6——B2/B3/B4 同库惯例) + item_id: String 主键唯一——奖励道具经济 RewardTable.effect_id(aux 行)与 VariationTable.counter_item_ids 的引用键 + display_name: String 非空(HUD框架 呈现数据源,本地化归 设置与本地化——敌人系统/增益状态/奖励道具经济 同库惯例) 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 六键+叠加层标记三族) + use_phase: enum {active_dig}——MVP 单值(扩枚举走 辅助道具 GDD 修订;奖励道具经济 use_timing 扩值 during_dig 同批对齐——奖励道具经济×③) + mvp_content: 六行(值 provisional 归 难度曲线与Boss)——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 校验强制,夹具 aux_target_violation)· 效果时效随 kind 派生无独立字段(空间 kind 恒本图持续、ap_grant 恒当轮一次性)· 零随机(零 RNG 流消费——信息全部来自 排雷玩法 恒真数字/奖励道具经济 Ready 映射/行动经济 账本三既有事实源)· 零特判延伸(排雷玩法 代码零 辅助道具 分支)· 无效/零信息目标不可用不消耗(可用性门——2026-09-14 用户拍板:板级判据 → 图标灰/入口禁用〔奖励道具经济 usable_now 先例〕、点击级 → 拒绝零消耗、组合行判据取 OR——爆破包 ap_grant 恒可用、真视零雷恒有效;与 奖励道具经济 E10 照常消耗区分——挖掘期非结算管线、守卫 辅助道具 本地可判)· area_3x3 边缘窗口 resolved_target = 窗口 ∩ 板内格(2026-09-14 用户拍板——护栏只缩不扩)· 使用零 AP 成本(不经 try_spend)· 库存关卡域(与 奖励道具经济 库存正交两容器、生命周期同构)· Used 不可逆 + overlay_semantics: 板级叠加层三集(辅助道具 自持派生信息层、零回写 排雷玩法/奖励道具经济 状态、随图消亡)——digit_reveal 显影集(HUD框架 显示雾格数字,本体 = 排雷玩法 恒真数字)/ mine_reveal 真视集(真视≠自动兑付——插旗仍花 1 AP 走既有 flag_hit 路径)/ reward_preview 预读集(徽章数据源 = 战斗结算 query_reward_identity 既有读面——辅助道具 零 奖励道具经济 数据复制) + api: 封闭枚举 8 入口(provisional,实现期 ADR 定名)——add_item(item_id, source) / query_inventory(枚举序 = 受理序,Used 保留只读,逐件含 usable 板级零信息可用性预判)/ validate_use(handle, target?) → 效果投影(只读不消耗;目标校验谓词 = 越板/点击级零信息拒绝 + 边缘窗口裁剪 resolved_target = 窗口 ∩ 板内格)/ apply_overlay(kind, target, mag)(战斗结算 逐 kind 调用)/ consume(handle)(Used 不可逆 + 快照回执)/ query_overlays(三集只读投影)/ retire_overlays(战斗结算 编排——与 奖励道具经济 retire_mapping 同点)/ clear_inventory(关卡末幂等);负空间 = 无自动使用入口/无重掷/无事件订阅/无 RNG 直连/无 排雷玩法/奖励道具经济 写入口 + use_orchestration: 战斗结算 use_aux 规范序(唯一调用权)= 相位 gate(Active——行动经济 phase getter)→ validate_use(目标校验读 排雷玩法 只读面——使用目标校验谓词表见 辅助道具 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| 分布缺口待 棋盘生成 actuals——奖励道具经济 预检 11 同批重跑)· F2 维度覆盖谓词 coverage(d) · F3 信息价值方向预检(显影 ι↑ → 残余盲猜端 n_s = (1−ι)|S| 收缩 → 盲赌暴露下降 → 期望踩雷↓——P(雷) = |M|/(|M|+n_s) 对 ι 递增,单赌命中率上行属正常,收益在"少赌"非"单赌更安全";与 战斗结算 F4 同式 + n_s<1 适用域守卫;2026-09-14 评审修正原"P(雷)↓"方向误写;定量当量归 战斗结算 条款①对照表扩列——战斗结算×④注记,MVP 不入对照表) + obligations: 21 项跨 GDD 义务清单(辅助道具 GDD Dependencies 全文,对侧修订批逐项核对、缺项即 blocking)——战斗结算×①②③(use_aux 编排/生命周期/授予路由,缺项即 blocking)+ 战斗结算×④(对照表扩列注记)· 奖励道具经济×①…⑤(category 扩值 aux_item/effect_id 双表校验+夹具 reward_dangling_aux/use_timing 扩 during_dig/默认根六行+池总权重算/OQ2 收口注记)· 行动经济×①(grant_ap 第 9 API 接口漂移预授权——Active 受理、无上限域、零随机)· 地形变异×①②③(fog.counter_item_ids=[probe_dust] 回填/悬空校验激活+夹具 aux_dangling_counter/未来变异随批配 ≥1 反制)· HUD框架×①…④(库存条/使用交互/叠加层三集呈现/清空反馈)· 数据配置×①②(AuxItemTable 登记/两处表值回填+校验激活)· 教学引导×①(教学候选,非 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] + closures: 地形变异 EC6 显影语义定稿(=fog_reveal,恢复推理原料非排雷;时效 = 本图持续)· 地形变异 OQ2 收口(时效/定价/首引回填/悬空校验)· 奖励道具经济 OQ2 辅助道具 侧收口(呈现深度 = 道具驱动预读;未预读格不呈现、自动寻路不做——终态与否 OQ6)· 创意风险 #1 道具侧兑现(plan 维度预读——结构闭环最后一块,呈现归 HUD框架) + warnings: 预警①…④ playtest 预注册归 难度曲线与Boss(道具依赖度/预读覆盖率/AP 转存联动/稀有感知) + referenced_by: [战斗结算, 奖励道具经济, 地形变异, 行动经济, HUD框架, 数据配置, 教学引导, art-bible] - name: difficulty_table type: table - owner: design/archive/gdd-2026-09-14/c3-difficulty-curve.md + owner: design/gdd/systems/冒险循环/难度曲线与Boss.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] + application_point: 敌人系统.spawn 实例化时、行值快照前套乘(敌人系统 Rule 10 兑现——接口漂移建表随 难度曲线与Boss 实现批) + first_use_scenario: 精英节点(关卡节点地图 分支地图定稿后联动设计——难度曲线与Boss OQ5);Boss 走 Dmax 直填行不走本表 + referenced_by: [敌人系统, 难度曲线与Boss] - name: difficulty_expectation_table type: formula_set - owner: design/archive/gdd-2026-09-14/c3-difficulty-curve.md + owner: design/gdd/systems/冒险循环/难度曲线与Boss.md attributes: - kind: 调参目标带(F5)+ 承伤估算(F4)+ 供给平衡式(F6)——设计期工具式,运行时零新增公式(C3 定位声明) + kind: 调参目标带(F5)+ 承伤估算(F4)+ 供给平衡式(F6)——设计期工具式,运行时零新增公式(难度曲线与Boss 定位声明) 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 排期倒挂解除载体) + tool_carrier: level_monte_carlo.gd / reward_expectation.gd(切片原型→难度曲线与Boss 期正式批量工具,棋盘生成 F4 排期倒挂解除载体) calibration_gap: 真人修正系数待 playtest 标定(OQ3——**方向已由试玩批次 1 反向确认:真人显著强于模拟者**〔v2 真人 100% vs 模拟 80%〕;旧"真人预期低于模拟"方向声明由本条替代;带按模拟口径定义,真人系数批次 2 定量) - referenced_by: [B4, B1, C2, C4] + referenced_by: [奖励道具经济, 战斗结算, 冒险结构, 元进度] - name: boss_dmax_convention type: convention - owner: design/archive/gdd-2026-09-14/c3-difficulty-curve.md + owner: design/gdd/systems/冒险循环/难度曲线与Boss.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] + rule: Boss = 各关最大深度 Dmax 行(棋盘生成 第三轴登记约定的 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 难度曲线与Boss 修订批口径统一,旧固定口径 Σ=142 读数替代)· **加难走廊 [1.1, 1.35] 预登记**(Boss 180–207 候选,OQ7 扫描终裁) + elite_reserved: elite_/boss_ 前缀命名空间预留(棋盘生成 B7);精英完整语义归 关卡节点地图 定稿联动(OQ5/OQ6 强制重审) + referenced_by: [棋盘生成, 敌人系统, 关卡节点地图, 冒险结构] - name: level_table type: schema_data - owner: design/archive/gdd-2026-09-14/c2-adventure-structure.md + owner: design/gdd/systems/冒险循环/冒险结构.md attributes: - table: LevelTable(D2 承载,键 = level_index 行、行内 nodes 节点链数组——切片建表,C2 定稿转正为行集内容 owner) + table: LevelTable(数据配置 承载,键 = level_index 行、行内 nodes 节点链数组——切片建表,冒险结构 定稿转正为行集内容 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) + rest_heal_pct: int [0,100](knob 默认 30——恢复点回复 max_hp_eff 的百分比,随上限加成同步放大防高上限档恢复失效;数值复验归 难度曲线与Boss) 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] + rowset_rule: Boss 恒末节点(schema 校验);战斗节点深度 = 前序战斗节点计数、Boss = Dmax(棋盘生成 第三轴约定兑现面);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 两表存在精确行(数据配置 跨表校验⑧复用 + 冒险结构 AC-7 冒烟断言——序列关数 ⊆ 内容覆盖,越界关卡 = 内容生产缺口) + mvp_content: L1 全行(切片转正——7 节点链、rest_heal_pct=30、全程无雾);L2 行 = N=2 扩展批(同构模板 + 难度曲线与Boss 深度轴行值同批填充——冒险结构 GDD 生效前提,未落地前冒险以单关行集运行 N=1 合法态) + referenced_by: [冒险结构, 关卡节点地图, 难度曲线与Boss, 棋盘生成, 数据配置] - name: adventure_table type: schema_data - owner: design/archive/gdd-2026-09-14/c2-adventure-structure.md + owner: design/gdd/systems/冒险循环/冒险结构.md attributes: - table: AdventureTable(D2 承载,单行全局;C2 回填 owner;Rule 4 豁免——单行全局常量表,HeroTable/SettlementTable 先例;建表走 D2 接口漂移规则) + table: AdventureTable(数据配置 承载,单行全局;冒险结构 回填 owner;Rule 4 豁免——单行全局常量表,HeroTable/SettlementTable 先例;建表走 数据配置 接口漂移规则) 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) + level_clear_hp_bonus: int [0,999](knob 默认 10——关卡大奖励 HP 上限提升,冒险内生效经 begin_battle 覆盖值通道传入后续每战;值锚 = 难度曲线与Boss 区间平移换算率 +10hp ≈ +1 轮,上调须 难度曲线与Boss 重扫 L2 通关率带) + token_per_level: int [0,999](knob 默认 10——每通关一关的代币记账值;消费方 元进度) mvp_content: 两字段默认值即 MVP 行(10 / 10,provisional) - extension_reserve: 大奖励形态扩列位(三选一 / 攻击型 / 通关加成——C2 OQ2,随 C4 元进度设计) - referenced_by: [C2, C4, D2] + extension_reserve: 大奖励形态扩列位(三选一 / 攻击型 / 通关加成——冒险结构 OQ2,随元进度设计) + referenced_by: [冒险结构, 元进度, 数据配置] - name: hero_override_parameter_passing type: convention - owner: design/archive/gdd-2026-09-14/b1-settlement.md + owner: design/gdd/systems/战斗循环/战斗结算.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 同日核销): + kind: 主角战斗入参覆盖值通道(begin_battle 显式参数传递——切片 E4 封装收口批实现,registry 登记缺口由 冒险结构 定稿批补登) + contract: begin_battle 增 hero_hp / hero_atk / hero_max_hp 显式入参 → 敌人系统.spawn 增 atk_override / max_hp_override 参数;-1 = 表值兜底(不覆盖);hero 表运行期不可变(数据配置 单一事实源机制化——表写通道全删) + consumers: HP 账本(冒险结构——跨战斗 HP 持久性 + 大奖励上限提升的传入面)/ 元进度加成(元进度 远期预留)/ 切片 TunePanel(调试通道) + adr_status: ADR-0009 候选——正式侧 战斗结算/敌人系统 GDD 修订随 ADR-0009 批;冒险结构 GDD 规则 3/4 为 冒险结构 半边(HP 账本供值)的正式定义源 + referenced_by: [战斗结算, 敌人系统, 冒险结构, 元进度] +# 2026-09-09 奖励道具经济 full 评审修订批(owner=奖励道具经济 代用户修订,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 六词条(刀/批尾/死行/档位序检查/预推进/烧刀烧包)。 +# (floor_supply_missing / no_damage_row——敌人系统 OQ9 并案)+ 窗口锚点改授予段后(用户评审拍板:当轮拾取当轮可用) +# + 重复受理幂等语义 + 战斗结算×6/HUD框架×3→HUD框架×5 义务清单同步;锚参照迁移传播三处(本条 anchor_warning 迁移注记 / +# clause3_supply.direction_precheck 迁移注记 / systems-index 行动经济 OQ5 注记——attack_up 口径历史值与 defense_up +# 数值巧合全等、迁移后预检未重跑事实同批登记);terminology 增 奖励道具经济 六词条(刀/批尾/死行/档位序检查/预推进/烧刀烧包)。 # 详见 design/archive/gdd-2026-09-14/reviews/b4-reward-item-economy-review-log.md。 -# 2026-09-10 B1 定稿回填(owner=b1-settlement.md):新增 settlement_table / clause5_dominance 两条件目 +# 2026-09-10 战斗结算 定稿回填(owner=战斗结算):新增 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 代理退役) +# 值域 [60,85],终值共裁 战斗结算 OQ2)+ anchor_warning F0 裁决回执(红警关闭:瞬时当量口径/地板层数=2/defense_up +# 参照保持)· clause3_supply.direction_precheck 闭合回执(≈2.0×/2.6× 边际配对、满配破线 难度曲线与Boss 约束、pp 代理退役) # · board_gen_table.inferability_vs_p_safe 消费回执(P(安全) 残余盲猜端代理式);status_effect/enemy/reward_table -# 三处 b1_obligations 履行回执(23 条义务全落位 b1 AC 覆盖矩阵);terminology 增 B1 五词条 +# 三处 settlement_obligations 履行回执(23 条义务全落位 战斗结算 AC 覆盖矩阵);terminology 增 战斗结算 五词条 # (完美排雷/当量/乘区/溢杀/免伤轮)。 -# 2026-09-10 B1 评审批修订(owner=b1-settlement.md,/design-review NEEDS REVISION 同日核销 8 blocking): +# 2026-09-10 战斗结算 评审批修订(owner=战斗结算,/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 零价假设); +# 数据配置 GDD 行按收编先例以本条为准)、source_label_map 键集补 enemy_action/flag_miss、mechanics 补行协议字段分层; +# clause5_dominance——模型修订重登(挖穿残余池计价、条款⑤未闭合 + 难度曲线与Boss/奖励道具经济 约束包 + 护栏 B 双读数 + RAP 零价假设); # clause3_supply——闭合回执双口径更正(锚行 ≈2.0×/2.6× / 期望 ≈1.2×/1.5×、破线约束两轴)。 # 详见 design/archive/gdd-2026-09-14/reviews/b1-settlement-review-log.md。 -# 2026-09-13 D1 定稿回填(owner=d1-hud-framework.md):新增 d1_presentation_surface 条目(呈现面契约—— +# 2026-09-13 HUD框架 定稿回填(owner=HUD框架):新增 hud_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】+ +# terminology 增两词条(命令式回执驱动/程序化占位)+ referenced_by 增 HUD框架。solo 模式出稿待人工复核。 +# 2026-09-14 辅助道具 定稿回填(owner=辅助道具,solo 模式一次会话定稿、出稿待人工复核): +# 新增 aux_items 条目(AuxItemTable 七字段 + MVP 六行【四常规单维 + 两稀有多维,值 provisional 归 难度曲线与Boss】+ # 真视窄域护栏承重规则 + 叠加层三集语义 + 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; +# + 地形变异 EC6/OQ2 与 奖励道具经济 OQ2 收口);variation_table.mvp_content.counter_item_ids 回填 [probe_dust](地形变异 预授权 +# "辅助道具 期回填"兑现——数据配置 表值与悬空校验激活随 数据配置/辅助道具 修订批同批落地)+ dangling_ref_policy 首引兑现改写; +# terminology 增四词条(叠加层/真视窄域护栏/渗透率 + referenced_by 增 辅助道具)。 +# 生效前提:奖励道具经济×①…⑤(category 扩值 aux_item/use_timing 扩 during_dig/默认根六行等)与 数据配置×①② 修订批—— +# 未兑现前 RewardTable 含 aux 行会加载致命(奖励道具经济 Rule 1 预留值拒绝)、辅助道具 GDD AC-12/13 登记 deferral。 +# 2026-09-14 跨评审回写批(/review-all-gdds 🔴-1/🔴-2 修复,owner=增益状态/战斗结算/数据配置 GDD 代用户修订): +# 批1(数据配置 漂移批 + 增益状态 接口漂移批)——status_effect.mvp_content.defense_up magnitude −50→−30(减伤墙批 +# 2026-09-13 难度曲线与Boss 投值的对侧回写落地);rng_service.scope_event_binding 增 encounter 绑定 + referenced_by 增 战斗结算; # 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)+ 参照回迁迁移注记关闭; +# 批2(战斗结算 重算批)——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)。 +# 裁决②维持 + 护栏 W2 待重推注记);settlement_table.mechanics 增 战斗结算 F6 充能与护甲克制结算规格登记(W3)。 diff --git a/design/research/lore-nest-metaphor-2026-09-12.md b/design/research/lore-nest-metaphor-2026-09-12.md index b2f156dc..b59ff6af 100644 --- a/design/research/lore-nest-metaphor-2026-09-12.md +++ b/design/research/lore-nest-metaphor-2026-09-12.md @@ -1,10 +1,10 @@ # 世界观设定记录:雷 = 怪物巢穴(地牢隐喻增补提案) > 日期:2026-09-12 -> 性质:世界观设定记录(叙事 / 隐喻层)——**非机制定案**。本稿不触碰任何已锁机制数值、接口与 GDD 正文(A1–B4、B1、C3 已定稿内容不受影响);涉及机制侧的事项仅在 ③ 末行以"边界提醒"列出。 -> 状态:**已拍板并采纳(2026-09-12),概念层修订已执行**——game-concept 世界观与命名节 + 视觉锚原则 5 已带 2026-09-12 版本注记改写;systems-index 备注·概念层增补已登记;⑤节六问拍板结论已逐条回写(含两处修正案:⑤-1 惩罚格类型化、⑤-2 对外动词"控制")。原状态:草案,待用户反馈。 +> 性质:世界观设定记录(叙事 / 隐喻层)——**非机制定案**。本稿不触碰任何已锁机制数值、接口与 GDD 正文(排雷循环与战斗循环各系统、难度曲线与Boss 已定稿内容不受影响);涉及机制侧的事项仅在 ③ 末行以"边界提醒"列出。 +> 状态:**已拍板并采纳(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 行)。 +> 关联:`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 行)。以上旧版文件均已迁入 `design/archive/gdd-2026-09-14/`(只读归档)。 ## ① 设定评估 @@ -21,7 +21,7 @@ 1. **P3"风险与收益同源"获得字面意义的自洽**。支柱原文"雷既是敌人的武器,也是你的赌注"在机制上已成立(踩雷 → 伏击 / 标中 → 敌 debuff),但陷阱隐喻下两个结果挂在同一物体上略生硬——陷阱只该有坏处。巢穴隐喻下两者天然同源:不处理 → 怪物倾巢而出(敌 buff);封印成功 → 巢穴废置、巢主暴露(我方增伤 / 敌削弱)。玩家全部风险行为围绕"巢穴"这一个实体展开,**没有第二套惩罚源**;新拍板的标错旗惩罚也属这个同源家族(源于你对未知格的错误处置),叙事上不违反 P3"不引入第二套随机惩罚"。 -2. **P1 读怪有了动机包装**。B2 Player Fantasy 的"赌桌对面的那个存在"是抽象定位;巢穴框架给它具象身份——**敌人就是这些巢穴的居民**。你排的不是抽象雷区,是怪物的居住区;读怪(视觉推测型:"它弓起背了")= 观察巢居生物的习性。MVP 双敌种与框架严丝合缝:grub(蛆虫)= 腐巢孵出的爬虫(环长 1、无前摇、蠢而直接),ambusher(伏击兽)= 巢区猎手(潜行-蓄势-扑杀,三拍环)。"我谨慎它就弱、我贪婪它就吃壮"的闭环也更本能:你惊动多少巢,它就吃多少先机。 +2. **P1 读怪有了动机包装**。敌人系统 Player Fantasy 的"赌桌对面的那个存在"是抽象定位;巢穴框架给它具象身份——**敌人就是这些巢穴的居民**。你排的不是抽象雷区,是怪物的居住区;读怪(视觉推测型:"它弓起背了")= 观察巢居生物的习性。MVP 双敌种与框架严丝合缝:grub(蛆虫)= 腐巢孵出的爬虫(环长 1、无前摇、蠢而直接),ambusher(伏击兽)= 巢区猎手(潜行-蓄势-扑杀,三拍环)。"我谨慎它就弱、我贪婪它就吃壮"的闭环也更本能:你惊动多少巢,它就吃多少先机。 3. **P4 地牢下探氛围获得动词**。现行叙事是静态的(下探陷阱地砖阵);巢穴框架给下探一个目的动词——**肃清**(sweep):每层被巢穴侵染,清图 = 肃清本层巢区,下探 = 追着更深处的巢走。产品名双关(minesweeper → dungeon sweeper)从"清扫地牢的人"强化为"清巢者",P4 的命名承载更实。 @@ -34,17 +34,17 @@ ### 1.3 风险与不自洽点 -1. **连锁展开(一次性免费开一大片安全区)**。逐格挖掘是"探索",连锁瞬间撕开半张图需要节拍解释,否则探索感被"作弊感"稀释。候选包装:(a) **空腔连通**——数字 0 = 四面寂静无巢,地牢厅室本就被掏空连通,你只是让光进去;(b) **坍塌显形**——挖穿薄壳,整片松动地面塌陷露出;(c) **回声感知**——探险者的感知沿寂静区扩散。三选一影响 B5 连锁动画 / D4 音效基调(⑤-4)。顺带:连锁不越旗(A2 GD-1)在 (a)(c) 下读作"封印挡住了蔓延 / 你拒绝在未确认处继续掏",自洽零成本。 +1. **连锁展开(一次性免费开一大片安全区)**。逐格挖掘是"探索",连锁瞬间撕开半张图需要节拍解释,否则探索感被"作弊感"稀释。候选包装:(a) **空腔连通**——数字 0 = 四面寂静无巢,地牢厅室本就被掏空连通,你只是让光进去;(b) **坍塌显形**——挖穿薄壳,整片松动地面塌陷露出;(c) **回声感知**——探险者的感知沿寂静区扩散。三选一影响 战斗表现 连锁动画 / 音频 音效基调(⑤-4)。顺带:连锁不越旗(排雷玩法 GD-1)在 (a)(c) 下读作"封印挡住了蔓延 / 你拒绝在未确认处继续掏",自洽零成本。 -2. **已引爆雷格可通行、可站立**(A2 Rule 11)。这是巢穴框架的**意外强项**而非弱点:开雷 = 怪物倾巢而出占得先机(伏击 +3/层 的叙事因)→ 巢已空、残址可通行。敌方 buff 与格子可走两个机制副作用由**同一个叙事事件**一次性解释,比"爆掉的陷阱"更顺(弹坑为什么能站?)。建议把这条因果写成标准句式供 D1/D5 引用。残余小问:站上"刚被惊动的巢址"是否留表现层余韵(余温 / 噪声衰减)——纯美术取舍,留 art-bible。 +2. **已引爆雷格可通行、可站立**(排雷玩法 Rule 11)。这是巢穴框架的**意外强项**而非弱点:开雷 = 怪物倾巢而出占得先机(伏击 +3/层 的叙事因)→ 巢已空、残址可通行。敌方 buff 与格子可走两个机制副作用由**同一个叙事事件**一次性解释,比"爆掉的陷阱"更顺(弹坑为什么能站?)。建议把这条因果写成标准句式供 HUD框架/教学引导 引用。残余小问:站上"刚被惊动的巢址"是否留表现层余韵(余温 / 噪声衰减)——纯美术取舍,留 art-bible。 -3. **雾格的叙事身份**。机制承诺很硬:雾区无雷是**玩家可见知识**(A4)、雾内插旗恒 hit=false 纯损、雾区连 0 格都没有。叙事若按惯性做成"危险的迷雾"会与机制正面对撞(叙事暗示有险、实际绝对安全——违反 P2 诚实)。候选正调:**净化之雾 / 瘴气死区**——确认无巢、但气息读取(数字)失效的盲区;雾内封印恒不中 = "净化区无巢可封"。D1 图例条目(A4)与 D5 教学措辞需跟随这个定调。 +3. **雾格的叙事身份**。机制承诺很硬:雾区无雷是**玩家可见知识**(地形变异)、雾内插旗恒 hit=false 纯损、雾区连 0 格都没有。叙事若按惯性做成"危险的迷雾"会与机制正面对撞(叙事暗示有险、实际绝对安全——违反 P2 诚实)。候选正调:**净化之雾 / 瘴气死区**——确认无巢、但气息读取(数字)失效的盲区;雾内封印恒不中 = "净化区无巢可封"。HUD框架 图例条目(地形变异)与教学引导 教学措辞需跟随这个定调。 -4. **标错惩罚的"随机"如何不显得玄学**。玩家无法预测反噬流向——处理不好就是"系统随机整我"。解法是给随机一个**世界内的因**:封印仪式失控,溢出的能量 / 惊扰**流向不可控**(或惊动真巢 → 敌方受益;或反噬施术者 → 我方短暂失衡,约 1 回合)。同一个因解释两个分支,"随机"被读成"失控"而非"玄学"。三条护栏建议:(a) 明细行写明来源(B1 已有 flag_miss"标旗未中"行协议位)+ 固定因果措辞("封印失败");(b) D5 教学把教训定为"别乱封"(确定性防御),不引导玩家"赌反噬方向"(那才会伤 P1);(c) A2 OQ1 重复性未决(hit=false 现无去重:每轮累计 vs 一次性)对应两种叙事("反噬未平息 / 流血" vs "当轮反扑"),措辞等机制定案后跟随,不先写死。 +4. **标错惩罚的"随机"如何不显得玄学**。玩家无法预测反噬流向——处理不好就是"系统随机整我"。解法是给随机一个**世界内的因**:封印仪式失控,溢出的能量 / 惊扰**流向不可控**(或惊动真巢 → 敌方受益;或反噬施术者 → 我方短暂失衡,约 1 回合)。同一个因解释两个分支,"随机"被读成"失控"而非"玄学"。三条护栏建议:(a) 明细行写明来源(战斗结算 已有 flag_miss"标旗未中"行协议位)+ 固定因果措辞("封印失败");(b) 教学引导 教学把教训定为"别乱封"(确定性防御),不引导玩家"赌反噬方向"(那才会伤 P1);(c) 排雷玩法 OQ1 重复性未决(hit=false 现无去重:每轮累计 vs 一次性)对应两种叙事("反噬未平息 / 流血" vs "当轮反扑"),措辞等机制定案后跟随,不先写死。 -5. **宝箱密度**。B4 结构是"每格安全格必有货"(install 期预分配奖励身份)。每格摆一个"宝箱"实物的量级会击穿地牢尺度感(一屋 30 个箱子)。建议把"宝箱"从**每格实体**降维成**搜刮框架**:探明区域的每一寸都可能翻出前人遗落物——即时类 = 当场消耗(药草、口粮、现场磨刀),可选类 = 收进行囊(刀、治疗包,库存 = 关卡域即行囊到本层结束)。"宝箱"保留为高档位 / 可选类格的视觉强调形态(⑤-5)。 +5. **宝箱密度**。奖励道具经济 结构是"每格安全格必有货"(install 期预分配奖励身份)。每格摆一个"宝箱"实物的量级会击穿地牢尺度感(一屋 30 个箱子)。建议把"宝箱"从**每格实体**降维成**搜刮框架**:探明区域的每一寸都可能翻出前人遗落物——即时类 = 当场消耗(药草、口粮、现场磨刀),可选类 = 收进行囊(刀、治疗包,库存 = 关卡域即行囊到本层结束)。"宝箱"保留为高档位 / 可选类格的视觉强调形态(⑤-5)。 -6. **术语漂移与文档一致性**。定稿映射表标注"定稿 2026-09-09",采纳本提案须显式版本注记改写,不能静默覆盖;各文档已固化的"陷阱"字样(game-concept 视觉锚原则 5、A2 教学条目等)需一次口径清扫,或在 lore 中声明"陷阱 = 旧称 / 部分 biome 变体"。机制侧边界提醒见 ③ 末行。 +6. **术语漂移与文档一致性**。定稿映射表标注"定稿 2026-09-09",采纳本提案须显式版本注记改写,不能静默覆盖;各文档已固化的"陷阱"字样(game-concept 视觉锚原则 5、排雷玩法 教学条目等)需一次口径清扫,或在 lore 中声明"陷阱 = 旧称 / 部分 biome 变体"。机制侧边界提醒见 ③ 末行。 ## ② 机制↔叙事映射表 @@ -54,19 +54,19 @@ |---|---|---| | 雷图 / 扫雷图 | 地牢的一层 | "被巢穴侵染的厅室";产品名双关强化为"清巢者";清图 = 夺回本间厅室 | | 雷(未开格) | 怪物巢穴 | 建议定为正典;旧"陷阱"降级为部分 biome 变体皮或旧称;数字 = 相邻巢穴的气息数(探险者的感知,非系统 UI) | -| 开雷(cell_revealed{kind=mine}) | 踩中怪物巢穴 → 惩罚 | "惊动巢穴——怪物倾巢而出占得先机":伏击 +3/层 = 惊动程度;同一事件顺带解释巢址变空、可通行(标准句式供 D1/D5 引用) | -| 插旗 · 标中(flag_judged hit=true) | 封印 / 插火把 → 巢穴不可用 → 伤害加成(伤害 B) | "封印"承载"失败有反噬"(服务标错惩罚),"火把"承载探险感;同时覆盖现行 weaken(失庇护 = 削弱)与拟定增伤(暴露 = 好打)两种机制读法;标中旗 highlight(B1)= 封印生效脉冲 | +| 开雷(cell_revealed{kind=mine}) | 踩中怪物巢穴 → 惩罚 | "惊动巢穴——怪物倾巢而出占得先机":伏击 +3/层 = 惊动程度;同一事件顺带解释巢址变空、可通行(标准句式供 HUD框架/教学引导 引用) | +| 插旗 · 标中(flag_judged hit=true) | 封印 / 插火把 → 巢穴不可用 → 伤害加成(伤害 B) | "封印"承载"失败有反噬"(服务标错惩罚),"火把"承载探险感;同时覆盖现行 weaken(失庇护 = 削弱)与拟定增伤(暴露 = 好打)两种机制读法;标中旗 highlight(战斗结算)= 封印生效脉冲 | | 插旗 · 标错(flag_judged hit=false) | (新拍板:随机敌增伤 / buff,或我方 debuff,约 1 回合) | 叙事 = "封印失败的失控反噬,流向不可控"——一个因解释两个随机分支;1 回合时效 = 失衡短暂平息;护栏见 1.3-4 | | 安全格 | 探索到的安全地区 → 伤害 A | "地利":探明 = 站稳脚跟(视野 / 据点 / 无后顾之忧);结算措辞候选"地利 +X" | | 连锁展开(免费 flood fill) | (提案未覆盖) | "空腔连通 / 坍塌显形 / 回声感知"三选一(⑤-4);连锁不越旗 = 封印阻断蔓延 | -| 数字提示 | (提案未覆盖) | 贴地听音 / 数巢穴气息——把 UI 读成探险者技能;D5 教学语言来源 | +| 数字提示 | (提案未覆盖) | 贴地听音 / 数巢穴气息——把 UI 读成探险者技能;教学引导 教学语言来源 | | 初始安全区(3×3,无奖励) | (提案未覆盖) | 入口营地 / 前人据点——已搜刮过的出生点(对齐 initial 通道无奖励);与"扎营"意象共用词汇 | -| 道具 / 奖励(B4 install 期预分配) | 藏在安全地区的宝箱 | "前人探险者的遗落物 / 搜刮"框架替代每格宝箱(密度见 1.3-5);即时类 = 当场消耗,可选类 = 收进行囊 | +| 道具 / 奖励(奖励道具经济 install 期预分配) | 藏在安全地区的宝箱 | "前人探险者的遗落物 / 搜刮"框架替代每格宝箱(密度见 1.3-5);即时类 = 当场消耗,可选类 = 收进行囊 | | 清图奖励(H2 强调呈现) | 搜刮完毕 | "夺回犒赏 / 肃清仪式"——轮末仪式峰值的叙事由头;完美排雷(P_perfect 默认 ×2)= 全图封印成功的猎人荣耀 | | RAP / 停手 | (提案未覆盖;用户点名候选"扎营休整") | 就地扎营蓄力;攒满刷图 = 放弃本间厅室改走支线——"知难而退是冒险者的判断",风险退出的叙事尊严 | -| 雾格(A4) | (提案未覆盖) | 候选"净化之雾":确认无巢但气息读取失效——与"雾区无雷 = 玩家可见知识"严格对齐;雾内旗恒不中 = 净化区无巢可封 | -| 敌人(grub / ambusher) | (提案未覆盖) | 巢居生物:蛆虫 = 腐巢爬虫(环长 1、无前摇),伏击兽 = 巢区猎手(潜行-蓄势-扑杀)——读怪 = 观察巢居习性(B2 视觉推测型的具象落点) | -| Boss(C3 boss_guardian) | (提案未覆盖) | 巢穴之主 / 守巢者;Boss 图全局变异(C3 scope=global)= 母巢领域浸染整层地砖 | +| 雾格(地形变异) | (提案未覆盖) | 候选"净化之雾":确认无巢但气息读取失效——与"雾区无雷 = 玩家可见知识"严格对齐;雾内旗恒不中 = 净化区无巢可封 | +| 敌人(grub / ambusher) | (提案未覆盖) | 巢居生物:蛆虫 = 腐巢爬虫(环长 1、无前摇),伏击兽 = 巢区猎手(潜行-蓄势-扑杀)——读怪 = 观察巢居习性(敌人系统 视觉推测型的具象落点) | +| Boss(难度曲线与Boss boss_guardian) | (提案未覆盖) | 巢穴之主 / 守巢者;Boss 图全局变异(难度曲线与Boss scope=global)= 母巢领域浸染整层地砖 | | 伏击池层数(side-wide,默认 cap 4) | (提案未覆盖) | HUD 呈现候选"骚动档位"(蠕动 / 骚动 / 暴动 / 倾巢,默认 cap=4 下四档)——给共享池一个可读皮肤,强化"它强是因为我喂的"(P2);溢出行 = 惊扰过饱和、部分先机未生效 | ## ③ 文档影响清单 @@ -78,34 +78,34 @@ | `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 修订会话) | +| 旧版 `systems-index.md` 备注·概念层增补 | 采纳后追加登记行(2026-09-09 块同款句式) | 流程登记 · 低 | 采纳后顺手 | +| **art-bible**(未来任务) | 输入:旗的三形态(火把 / 封印符 / biome 变体)、未开雷格"隐约巢感"样张、空巢已开格表现、净化之雾表现、巢居生物剪影(衔接 敌人系统 Visual Req 1/2)、遗落物图标;**格子样张测试验收项不变(数字可读性第一)**,巢感只能做在数字可读性余量内 | 表现层 · 中高 | art-bible §1–4 创建前(最晚 HUD框架 GDD 前),与格子样张测试同批 | +| **战斗表现**(垂直切片) | 方向性输入:敌人出场 = 自巢中涌出、战斗空间巢穴装饰、连锁动画三选一(1.3-1);**不预设舞台**(双舞台资产管线陷阱照旧) | 表现层 · 低中 | 战斗表现美术测试(其 GDD 定稿前) | +| **教学引导**(Alpha) | 措辞候选:"挖到雷 = 惊动巢穴而非死亡"(现有条目叙事化)、"雾内插旗纯损 = 净化区无巢可封"、"旗挡链 = 封印阻断蔓延"、标错惩罚上线时"封印失败有反噬" | 表现层 · 低 | 教学引导 GDD(设计顺序第 19 位) | +| 设置与本地化 / 文案 | 结算明细叙事措辞(伏击行"惊扰 +N"、flag_miss 行"封印失败"、地利 / 封印增伤行名)——跟随机制定案,本稿只供词汇表 | 表现层 · 低 | 文案期 / 设置与本地化 | +| **机制侧边界提醒(非本稿范围)** | 新拍板"标错旗 → 随机敌增伤 / buff 或我方 debuff"若与排雷玩法 OQ1 现行候选(敌方伏击 +1)不同:机制落点须走 排雷玩法 OQ1 更新(含重复性裁决:hit=false 现无去重)→ 敌人系统 OQ6(事件源 = flag_judged{hit=false})→ 增益状态 source_class 新枚举(**结构冻结项,须 增益状态 GDD 修订,非加行**)→ 战斗结算 兑付编排(flag_miss 行协议已在战斗结算评审 blocking ⑤ 留位);"随机"的 rng 流归属归机制侧裁决。本稿 lore 对"一次性 / 每轮累计""敌侧 / 我侧"各落点均兼容 | 机制侧 · 中 | 机制侧推进时(排雷玩法 修订会话) | ### ③ 补充登记(2026-09-12 概念层修订执行后 · /consistency-check 抓获) -1. **B1 L29 / B3 L22 "雷是赌桌不是陷阱"措辞碰撞**:换轴后"陷阱"为惩罚格类型词表一员(未来类型:直接扣血),句面可误读为"雷格不是陷阱类型";句本意(雷是争夺中的资源、非单向危险)在新正典下反而更成立,风险低。措辞更新归 B1/B3 各自下一批修订(候选替换:"雷是赌桌不是死局"),本会话不动机制 GDD。 -2. **battle.md 玩家可见文案采纳点**:插旗/拔旗/标旗等玩家可见动词在文案 pass / D1 批统一替换为"控制"(battle.md L258 明载文案定稿权归实现期文案 pass;对内交互语言仍为旗/插旗——见 ⑤-2 拍板)。 +1. **战斗结算 L29 / 增益状态 L22 "雷是赌桌不是陷阱"措辞碰撞**:换轴后"陷阱"为惩罚格类型词表一员(未来类型:直接扣血),句面可误读为"雷格不是陷阱类型";句本意(雷是争夺中的资源、非单向危险)在新正典下反而更成立,风险低。措辞更新归战斗结算/增益状态各自下一批修订(候选替换:"雷是赌桌不是死局"),本会话不动机制 GDD。 +2. **battle.md 玩家可见文案采纳点**:插旗/拔旗/标旗等玩家可见动词在文案 pass / HUD框架 批统一替换为"控制"(battle.md L258 明载文案定稿权归实现期文案 pass;对内交互语言仍为旗/插旗——见 ⑤-2 拍板)。 ## ④ 补充设定点子(供挑选,非定案) -1. **前人遗落物(道具来源正典化)**:地牢吞噬过许多探险者——白骨与行囊散落各处。一个设定同时解决三件事:道具从哪来(B4 每格必有货的合理性)、危险的实证(不用台词就传达"这里死过人")、搜刮框架的实体锚。可选延伸:极低频"前人日志"碎片作零成本氛围文本(取决于 ⑤-6 厚度预算)。 -2. **Boss = 巢穴之主 / 母巢**:每层一母巢,Boss 战 = 断根;下探 = 沿母巢链追猎。C3 已有的 `boss_guardian`(守巢者)可直接收编;Boss 图全局变异 = 母巢领域把整层地砖浸染(岩浆 / 冰冻等变异的叙事壳——"巢液浸染")。 +1. **前人遗落物(道具来源正典化)**:地牢吞噬过许多探险者——白骨与行囊散落各处。一个设定同时解决三件事:道具从哪来(奖励道具经济 每格必有货的合理性)、危险的实证(不用台词就传达"这里死过人")、搜刮框架的实体锚。可选延伸:极低频"前人日志"碎片作零成本氛围文本(取决于 ⑤-6 厚度预算)。 +2. **Boss = 巢穴之主 / 母巢**:每层一母巢,Boss 战 = 断根;下探 = 沿母巢链追猎。难度曲线与Boss 已有的 `boss_guardian`(守巢者)可直接收编;Boss 图全局变异 = 母巢领域把整层地砖浸染(岩浆 / 冰冻等变异的叙事壳——"巢液浸染")。 3. **RAP = 扎营休整**:停手 = 就地扎营(篝火意象,Roguelike 认知零成本);RAP 攒满刷图 = 放弃本间厅室、改走支线。把"风险退出"从机制词变成冒险者的尊严判断——知难而退也是探险技术(与 P4 同频)。 4. **旗随 biome 换皮,动词统一**:地牢 = 火把、圣所 = 封印符、腐巢 = 驱虫香……视觉形态换、机制动词统一为"镇 / 封"。直接服务视觉锚原则 4(调色板交换优先于新增资产);也回应"封印 vs 火把"之争——不必二选一,定一个正调、其余作变体(⑤-2)。 -5. **敌种叙事身份轴(C3 内容扩展用)**:现轴 = 巢居虫群型(grub:多、薄、无脑)↔ 巢穴猎手型(ambusher:蓄势、爆发、读得懂)。未来新敌种(含拟议中的护甲类怪)沿轴投放:甲壳巢卫(厚重硬壳 = 高防 / "火烧封印破甲"的隐喻接口)、噬旗腐虫(专拆封印 = 反旗机制的叙事壳)等——仅登记方向,数值与投放归 C3。 -6. **伏击 = "骚动"计量**:敌方共享伏击池的 HUD 呈现换骚动档位词(蠕动 / 骚动 / 暴动 / 倾巢)。给 side 池一个叙事皮肤,让"它这一拳为什么重"的归因行(基线 + 惊扰,B2 分量二元组)多一层可读性——纯呈现候选、零机制含义。 +5. **敌种叙事身份轴(难度曲线与Boss 内容扩展用)**:现轴 = 巢居虫群型(grub:多、薄、无脑)↔ 巢穴猎手型(ambusher:蓄势、爆发、读得懂)。未来新敌种(含拟议中的护甲类怪)沿轴投放:甲壳巢卫(厚重硬壳 = 高防 / "火烧封印破甲"的隐喻接口)、噬旗腐虫(专拆封印 = 反旗机制的叙事壳)等——仅登记方向,数值与投放归难度曲线与Boss。 +6. **伏击 = "骚动"计量**:敌方共享伏击池的 HUD 呈现换骚动档位词(蠕动 / 骚动 / 暴动 / 倾巢)。给 side 池一个叙事皮肤,让"它这一拳为什么重"的归因行(基线 + 惊扰,敌人系统 分量二元组)多一层可读性——纯呈现候选、零机制含义。 ## ⑤ 开放问题(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 成本边界 = 不新增文本系统。 +1. **正典取舍** — **已采纳(修正案):惩罚格类型化**。雷格对外叙事称呼定**惩罚格**(机制名"雷/雷格"不变),下辖类型;首发地牢类型 = **怪物巢穴**(与现行锁定机制严丝合缝:惊动→怪物倾巢而出占先机、标中→巢主失庇护 debuff);"陷阱"降级为类型词表的一员(不再是正典属名,也未废弃)。类型机制分化——陷阱→直接扣血、标中陷阱→怪物受伤害、岩浆/冰冻化身各配效果——**登记为未来机制侧方向**(归棋盘生成/排雷玩法 修订裁决;"陷阱→直接扣血"与现行锁定规则"开雷→敌方优势非直接伤害"相抵触,取舍归机制侧,可能连带 P3 表述复查),概念层只写属名结构、不承诺任何分化效果。 +2. **旗的正调** — **已采纳(修正案):控制**。玩家可见动词定"控制"(类型无关——控制巢穴 = 封印镇住、控制陷阱 = 借力反制,未来岩浆/冰冻类型各有着落);对内文档与机制协议仍为"旗/插旗"(战斗结算/敌人系统 现行协议零变化)。本稿"封印/火把"之争的降级落点:封印/镇/反制 = 类型风味词(结算行与教学的可选风味层,不承担机制含义);火把等视觉形态 = biome 换皮候选归 art-bible(只登记)。小尾巴:按钮/tooltip 上"控制"的手感归 HUD框架 GDD。 +3. **标错惩罚叙事** — **已采纳:失控反噬**(本稿推荐)。保留机制随机,随机读作失控:控制失败、反噬流向不可控(流向真巢 → 敌受益;反噬自身 → 我方失衡,约 1 回合)。三护栏随行:(a) 结算固定措辞"控制失败"(战斗结算 flag_miss 行协议位;本稿原文"封印失败"随⑤-2更改);(b) 教学引导 教学定调"没把握别乱控制"(确定性教训),不引导玩家赌反噬方向;(c) 重复性措辞("反噬未平息" vs "当轮反扑")等 排雷玩法 OQ1 定案后跟随,本次不预写。 +4. **连锁展开节拍** — **已采纳:坍塌显形**(**未采纳**本稿推荐"空腔连通"——用户取冲击感路线)。连锁 = 挖穿薄壳、整片松动地面塌陷露出;登记为 战斗表现 连锁动画 / 音频 音效方向(尘土、震动、塌露)。随行自洽注记:连锁不越旗(排雷玩法 GD-1)读作"你控制过的地面不会塌",塌到控制点截停;与视觉锚原则 5 已开格候选"碎裂/裸露地面"天然对齐;本稿 1.3-1 对坍塌的保留意见(地砖身份/反复坍塌合理性)转记为 art-bible/战斗表现 表现侧注意项,不影响拍板。 +5. **道具呈现密度** — **已采纳(前提修正)**:用户指出"每格必有货 = 每格宝箱"是误会——每个安全格都有奖励,但奖励不一定是可使用道具。执行口径:**宝箱呈现仅绑定可使用道具类奖励**(可选类:刀/治疗包等,保留高档位视觉强调),即时类(buff/瞬时收益等非道具奖励)不摆箱;本稿"搜刮/遗落物"框架降级为可选氛围背景,登记不锁(背景句采用"搜刮前人遗落物"氛围措辞,不构成呈现规则承诺)。影响面登记:HUD框架 已开格奖励恒读形态、奖励道具经济 图标方向(各归各的时机)。 +6. **世界观厚度预算** — **已采纳:碎片点缀**(本稿推荐,不含前人日志)。功能性文本(道具名/结算行/教学措辞)lore 化 + 极少量一次性提示(首遇时刻的功能性 flavor 行);**前人日志(④-1 延伸)不做**,登记为未来内容方向;art-bible/教学引导/设置与本地化 成本边界 = 不新增文本系统。 diff --git a/design/research/peglin-comparison-2026-09-12.md b/design/research/peglin-comparison-2026-09-12.md index 844ee2be..c3e18561 100644 --- a/design/research/peglin-comparison-2026-09-12.md +++ b/design/research/peglin-comparison-2026-09-12.md @@ -15,8 +15,8 @@ 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 构筑扩展——中期最大的"局感"提升项)。 +- **现在就做(本切片内,成本低)**:**遭遇随机化**——同「关卡×难度档」(L,D)敌人数在区间内掷骰(如地精 1–2 只、熊地精 1 只或 +1 只补充)。同敌种不触碰敌人系统"每行集恰一敌种"锁定,只需把 unit_count 从定值升级为区间(难度曲线与Boss GDD 增补一条 Core Rule + 敌人系统.spawn 掷骰)。这一条就能让"每次进第一关都不一样"。 +- **后置(结构改动,登记不实现)**:关卡节点地图分支形态(你已拍板后置)· **多波战斗**(Peglin 每场 2–3 波、总敌量 120–200——这是战斗结算编排级改动,波间状态保留规则也要定义,建议 Alpha 期评估)· 遗物类被动道具(奖励道具经济构筑扩展——中期最大的"局感"提升项)。 ## 二、数值对比表(双方均为初见第一区域) @@ -55,7 +55,7 @@ ### 可以参考(按收益排序) -1. **每场战斗的构筑决策密度**(最大差距):哪怕不做完整遗物系统,让普通战斗胜利也产出一次"二选一"小抉择(如两件道具选一件、或道具 vs 小回复),把"每关 6 次决策"变成"每场 1 次决策"——**直接制造局感**。→ B4 扩展提案,登记。 +1. **每场战斗的构筑决策密度**(最大差距):哪怕不做完整遗物系统,让普通战斗胜利也产出一次"二选一"小抉择(如两件道具选一件、或道具 vs 小回复),把"每关 6 次决策"变成"每场 1 次决策"——**直接制造局感**。→ 奖励道具经济扩展提案,登记。 2. **遭遇随机化**(本切片可做):同敌种数量区间掷骰(见结论)。 3. **敌群入场规则的同构物**:Peglin 的"远程怪从远处走来"= 首回合威胁延迟。我们已有 charge 拍同构(熊地精/酋长),可以把"新敌人首结算强制 charge/延迟拍"作为**多波制的前置探索**——若未来做多波,波进场延迟是天然的难度阀门。 4. **战后回复的概念**(谨慎参考):Peglin 用它支撑"低伤多场"模型;我们的"高伤少场"模型若引入战后回复,必须同步下调敌 atk 否则死亡压力崩塌——**绑定修订,不可单独引入**。这是"关卡内 HP 作为资源"的另一种诠释(我们当前 = HP 是跨场存量的硬资源)。 @@ -72,10 +72,10 @@ | 项 | 时机 | 义务登记 | |---|---|---| -| 遭遇随机化(unit_count 区间掷骰) | **本切片可做** | C3 GDD 增补 Core Rule(遭遇骰)+ B2.spawn 掷骰接口漂移登记 | -| 普通战"二选一"产出 | 下一批设计(B4 扩展提案) | 登记于 C3 GDD 联动网络(供给↔决策密度) | -| 多波战斗 | Alpha 评估 | B1 波次表提案登记(本报告 2.2 表) | +| 遭遇随机化(unit_count 区间掷骰) | **本切片可做** | 难度曲线与Boss GDD 增补 Core Rule(遭遇骰)+ 敌人系统.spawn 掷骰接口漂移登记 | +| 普通战"二选一"产出 | 下一批设计(奖励道具经济扩展提案) | 登记于难度曲线与Boss GDD 联动网络(供给↔决策密度) | +| 多波战斗 | Alpha 评估 | 战斗结算波次表提案登记(本报告 2.2 表) | | 战后回复 | 与敌 atk 下调绑定评估 | 本报告 2.1 结构差异注记 | -| C1 分支地图 | 已拍板后置 | 维持 | +| 关卡节点地图分支形态 | 已拍板后置 | 维持 | -> 报告使用纪律:Peglin 数值为当前 wiki 快照(版本迭代频繁),引用时注明日期;本报告结论服务于 C3/C1/C2/B4 的设计决策,非数值移植依据。 +> 报告使用纪律:Peglin 数值为当前 wiki 快照(版本迭代频繁),引用时注明日期;本报告结论服务于难度曲线与Boss/关卡节点地图/冒险结构/奖励道具经济的设计决策,非数值移植依据。 diff --git a/design/research/proposal-reward-polarity-2026-09-13.md b/design/research/proposal-reward-polarity-2026-09-13.md index e7ed5bff..7d312f48 100644 --- a/design/research/proposal-reward-polarity-2026-09-13.md +++ b/design/research/proposal-reward-polarity-2026-09-13.md @@ -1,7 +1,7 @@ # 提案:正反奖励(格面身份通感)——奖励池重构与极性路由 -> **状态**:草案 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,引用两文档处以其数据实态为准。 +> **状态**:草案 v3(2026-09-13 定稿——/design-review〔lean〕5 blocking 同日修订 + OQ-1~6 全部用户拍板闭合;日志 `design/archive/gdd-2026-09-14/reviews/proposal-reward-polarity-2026-09-13-review-log.md`)——**已拍板待实施** +> **基线声明(v2 增)**:本文"既有结构"以 **2026-09-13 切片数据批**为准(status_effects.tres 六行〔含 enrage/off_guard〕、defense_up −30、标错惩罚池已上线、可选类供给控制器在位);增益状态/战斗结算 GDD 正文与 registry 对该批存在未同步债(增益状态四行 vs 数据六行、战斗结算"标错惩罚未上线" vs 已上线、registry status_effect 四行)——清偿义务见 §3.5,引用两文档处以其数据实态为准。 > **触发**:playtest-2026-09-12 报告供给发现(即时类构成单一——可选类退出格子池后概率质量流向即时类、buff 频率 44%→59%,session-state 条目 21;报告 Top3 之供给量重拍板)+ 用户提案(安全格正、雷格反、类型通用、图标预读)。原记"报告 Q2/Q3"系提案会话口头编号、归档报告无此编号——v2 改引实质出处。 > **目标一句话**:把"挖哪格"从无差别推进升级为读敌拍板的路线规划——格面类型图标预读 + 对错定符号的极性兑付,在护甲克制之上叠加第二层战斗互动策略。 @@ -9,79 +9,79 @@ | 既有结构 | 本提案对它的处置 | |---|---| -| 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 加行不加机制) | +| 奖励道具经济 Rule 2 身份 install 期预分配(S∖R₀ 格) | 扩域:雷格同批 roll 类型(pool_tag 新值 `mine_cell`) | +| 奖励道具经济 Rule 8 已开格恒读 / OQ2 未开格不呈现(结构已备、呈现待垂直切片) | **激活 OQ2**:未开格呈现类型图标(查询面 `query_identity` 本就对未开格可查——AC-2 分支 3) | +| 标中旗=削弱(正)/ 标错旗=暴走·失衡·伏击(负)/ 踩雷=伏击(负)——战斗结算规则 6 + settlement.tres 标错惩罚池 | **推广为统一极性**:惩罚不再按行为固定,而是"该格类型的负向版" | +| 增益状态效果行(数据六行:增伤/减伤/伏击/削弱/暴走/失衡——2026-09-13 批) | 机制零改动——正负向复用既有行 + 一条直伤负向(heal)+ 一条新负向数据行 sapped 乏力(weaken,OQ-3 定型——增益状态加行不加机制) | | 绘制优先级栈(数字 > 小人 > 旗 > **[徽章]** > 纹理 > 底色) | 徽章位已预留——类型图标落此栈位 | ## 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 义务)。 +- **R1 类型通用**:每格(安全格+雷格)install 期恰一类型身份 ∈ {attack, defense, heal, blast, weaken}(weaken 为新增正向类型——见 §2)。类型决定**兑付什么**,极性决定**兑给谁/什么符号**。**类型 = 行的家族属性(v2 钉死实现面)**:roll 保持奖励道具经济 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 权重为**行权重**、类型频次由行权重涌现(难度曲线与Boss 调参对象)。图标呈现家族类型;可选类格(刀/治疗包)图标 = 家族类型 + 既有道具徽章语言区分(呈现细则归 HUD框架 义务)。 - **R2 极性矩阵(行为 × 格型 → 符号)**: | 行为 | 安全格 | 雷格 | |---|---|---| | 挖开 | **+类型** | **−类型**(与既有伏击的关系见 OQ-1) | | 插旗且留到轮末判定 | **−类型**(标错) | **+类型**(标中) | -| 插旗后本轮取消 | 零兑付(AP 已耗 = 变相惩罚,A3 承载) | 零兑付(AP 已耗) | +| 插旗后本轮取消 | 零兑付(AP 已耗 = 变相惩罚,行动经济承载) | 零兑付(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 转化同池同协议)。 +- **R6 满层语义(v2 钉死)**:负向不走增益状态满层拒绝路径——授予恒受理;饱和由战斗结算既有池 clamp [−100,+100] 承载(enrage/off_guard 沿用表行 max_stack=16,enrage 4 层即 +100% 饱和——原稿"2 层封顶同 max_stack"系对数据误读,v2 改正);饱和后的授予照产诚实行(贡献值边际归零 + pool_value 顶格显示——与正向饱和行同款呈现语义)。**结构不可达论证**:单轮负向授予数 ≤ AP(≤8)< max_stack 16,满层拒绝路径本身不可达,增益状态侧零改动成立。**敌人系统 cap 显式例外**:blast 负向(ambush +2 层)受敌人系统 cap=4 反灾难护栏约束——cap 溢出照既有"溢出未生效"行协议(护栏优先于落地语义;与现行 mine_hit→ambush 转化同池同协议)。 -## 2. 极性数值草案(provisional 全归 C3) +## 2. 极性数值草案(provisional 全归难度曲线与Boss) | 类型 | 正向(复用行) | 负向(复用行) | |---|---|---| -| attack 增伤 | attack_up +50%(×1 层拍板待 C3) | enrage 敌方暴走 +25%(max_stack=16 沿用数据、池 clamp 4 层饱和——R6) | +| attack 增伤 | attack_up +50%(×1 层拍板待难度曲线与Boss) | 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——新负向即时形态) | +| heal 回血 | heal_minor +4(满血零兑付照旧) | **直伤**:主角 −4(战斗结算直接 apply_damage,不经增益状态——新负向即时形态) | | 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) | +| weaken 削弱(新增正向) | weaken 敌方输出 −65%(标中旗既有语义入池) | **sapped 乏力(OQ-3 已裁决 2026-09-13——反噬镜像)**:我方输出 −25% 进 hero_offense 池(attack_up 镜像;增益状态新数据行:hero/percent −25/stack/max 16/dur 1;值 provisional 归难度曲线与Boss) | 正向权重草案(**行权重**——类型频次由此涌现,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**:负向明细行文案 + "读拍抢格"教学一段。 +- **排雷玩法**:mine 通道 reward_eligible 修订(身份扩展到雷格——registry `minesweeper_event_surface.cell_revealed` 的 reward_eligible 语义"mine 恒 false"须随批改写);拔旗动作**已在位**(`try_flag_pull`,`flag_pull_ap_cost=1` 已登记 registry 行动经济条目——原 OQ-5 系重开既定事实,v2 撤销改引用);EC 相应扩写;**排雷玩法 OQ1(标错惩罚)由本提案取代关闭**——候选"敌方伏击+1"被类型化负向路由取代,切片已实装惩罚池(enrage/off_guard/ambush+2),重复性口径("标旗未中"行每格实例一次)随批继承到负向兑付语义,排雷玩法 GDD 修订批关闭该 OQ。 +- **奖励道具经济**:`pool_tag` 新值 `mine_cell`;`cell_type` 列(R1 家族映射);roll_board 批次扩雷格 roll(同一 scope 批内);RewardTable 新增 `polarity` 字段(+/-,**类型枚举五值不动**——负向经 polarity + effect_id 表达,结构修订最小面);负向行内容回填;schema 校验族扩展。 +- **战斗结算**:`_judge_flag` 扩展(标错安全格 → 该格类型负向兑付;标中雷 → 该格类型正向兑付——**OQ-2 已裁决 2026-09-13**:取代现行固定 weaken 赠予,weaken 改走格面身份;行动经济条款②锚对象改"标中雷类型正向当量下界",重议随批登记);`_convert_ambush` 改类型化负向(OQ-1 已裁决:统一路由、伏击为负向表一行);明细行极性标注。**授予段致死裁定(v2 增)**:负向直伤于授予段使 hp=0 → 段序短路直入判定段判负(战斗段短路同族推广——死主角不消费窗口/增益/道具/战斗段,防死尸挨打行归因噪音)。**架构对齐注记(v2 增)**:本提案全部兑付(正向/负向/直伤)均发生于结算段——扫雷阶段零数值计算、事实入队轮末统一转化(战斗结算轮 facts 批处理既有结构);R2/R4 的"当场"均指授予段当场,非挖掘期。 +- **增益状态**:机制零改动(R6 满层拒绝结构不可达——见 R6 论证);**数据行 +1**:sapped 乏力(OQ-3 定型,schema 七字段照填);直伤负向不经增益状态(战斗结算 → 敌人系统 apply_damage)。 +- **行动经济**:拔旗价目已在位(`flag_pull_ap_cost=1`——引用,不新登记)。**F1 契约重议义务(v2 增,阻塞级)**:本提案触发行动经济 F1 三条款重推——**条款②**(标中旗锚:OQ-2 采纳后"标中旗 debuff 档位"锚对象消解,替代形态〔类型正向当量下界〕随 OQ-2 拍板一并重议——条款①"分源改率须回 行动经济 重议"被触发);**条款③**(V_mine 类型化:战斗结算 F4 不等式右臂按类型分化,取最坏型或加权型,重推);**条款⑤**(挖穿计价惩罚项类型化:战斗结算 F5 均匀交错模型的踩雷代价按类型加权,重推)。owner:战斗结算(单一闭合)+ 行动经济(契约方)。 +- **HUD框架**:未开格类型图标(徽章栈位);极性视觉语言(正负色相+形状双通道——art-bible 约束对齐);结算行负向文案族。义务条目随批正式登记旧版 systems-index 的 HUD框架台账("清单外登记会丢失"先例)。 +- **难度曲线与Boss**:正负两套数值全调平;F4 期望带复算(含 Q1 调参批〔= 2026-09-13 减伤墙调参批〕后的新基线);失衡预警扩展(负向兑付率、类型选择分布);探旗通道观测(OQ-6 已裁决:反滥用机制不引入——指标 5 观测兜底,越线再调)。 +- **战斗表现/教学引导**:负向明细行文案 + "读拍抢格"教学一段。 ### 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 受 σ 轨迹与枚举域变化影响——黄金向量/登记常数重制义务随批)。 +- **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 若增负向文案键随批);`minesweeper_event_surface`(cell_revealed.reward_eligible "mine 恒 false" 改写)。 +- **数据配置**:RewardTable schema 扩列(cell_type/polarity);StatusEffectTable 七行随增益状态修订批。 +- **增益状态 GDD 修订批**:四行→七行入文(六行存量债 + sapped)、defense_up −50→−30 同步(2026-09-13 批遗留债)。 +- **奖励道具经济 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〕/极性视觉语言/负向文案族)+ 门禁增补记录块。 +- **systems-index**:本提案 HUD框架 delta 拆正式义务条目入旧版 systems-index 的 HUD框架台账(未开格类型图标〔激活奖励道具经济 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)。 +4. **Q1 后承伤带基线**:level_monte_carlo 对难度曲线与Boss 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-2(已裁决)**:标中雷正向 = **取代**现行固定 weaken 赠予——weaken 移入类型池后,标中雷给"该雷的类型",削弱成为可规划素材而非固定施予;行动经济条款②锚对象改"标中雷类型正向当量下界",重议随批(见 §3 行动经济)。 +- **OQ-3(已裁决)**:weaken 负向 = **sapped 乏力(反噬镜像)**——我方输出 −25% 进 hero_offense 池(attack_up 镜像;增益状态 新数据行、机制零改动;值 provisional 归难度曲线与Boss)。原"敌方 weaken 抵免"候选否决(需新免疫语义、破增益状态 Rule 8 负空间);原"暂缓复用 enrage"候选否决(用户定型)。 +- **OQ-5(v2 撤销——既定事实引用)**:~~拔旗动作 AP 定价~~——`try_flag_pull` / `flag_pull_ap_cost=1`(无返还物)已登记 registry action_economy 与行动经济 schema,非开放问题。 - **OQ-6(已裁决)**:类型选择性探旗(旗未知格 → 结算 hit/miss 反馈 → 拔旗)**不引入反滥用机制**——接受为既有机制面的策略深度,指标 5 观测兜底、越线再调。 ## 6. 实施批次 -1. **批 1(数据面 + 路由)**:A2/B4 schema 与行、B1 极性路由、冒烟扩展——机制先可玩(占位图标)。 -2. **批 2(呈现)**:D1 图标族 + 极性视觉 + 教学。 -3. **批 3(数值与验证)**:C3 调平 + level_monte_carlo/defense_scan 复验 + 试玩批次。 +1. **批 1(数据面 + 路由)**:排雷玩法/奖励道具经济 schema 与行、战斗结算极性路由、冒烟扩展——机制先可玩(占位图标)。 +2. **批 2(呈现)**:HUD框架图标族 + 极性视觉 + 教学。 +3. **批 3(数值与验证)**:难度曲线与Boss 调平 + level_monte_carlo/defense_scan 复验 + 试玩批次。 diff --git a/design/ux/battle.md b/design/ux/battle.md index 27dab775..bbde7daf 100644 --- a/design/ux/battle.md +++ b/design/ux/battle.md @@ -4,10 +4,10 @@ > **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) +> **上游修订依赖**: 弃图刷新挖掘期受理已随布局决策 4 落稿上游(2026-09-12:行动经济 Active 受理 + 每轮上限 2 次、排雷玩法 Active·仅刷新路径装板、战斗结算 `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 义务对照表) +> **覆盖子屏**: 挖掘阶段主界面 · 轮末自动战斗结算(战斗结算明细屏) · 道具使用窗口(奖励道具经济/战斗结算域屏) · 胜利/失败终局 · 结算明细回看 +> **输入义务**: 排雷玩法×6 + 行动经济 + 地形变异×3 + 战斗结算×3 + 敌人系统×4 + 增益状态×4 + 奖励道具经济×5(旧版 systems-index 门禁增补记录,缺项即 blocking——承接落点见 §13 义务对照表) --- @@ -19,7 +19,7 @@ - 扫雷图区承载 P1(推理掌控感):数字是主角,一切服务信息可读 - 战斗画面区承载 P2(规划的延迟兑现):扫雷之因逐行兑现为战果,全程可见可归因 -- 角色信息/道具区承载规划面:状态、资源、库存常读,支撑"往雾区推进/囤刀待机"类决策(B4 可证伪信号的 MVP 观测面) +- 角色信息/道具区承载规划面:状态、资源、库存常读,支撑"往雾区推进/囤刀待机"类决策(奖励道具经济可证伪信号的 MVP 观测面) 若此屏缺失或难用:推理与兑现分裂(自动战斗沦为无聊过场——已登记创意风险)、逐行归因不可见(P2 失败)、AP/RAP/库存不可读(规划失去原料)。 @@ -27,7 +27,7 @@ ## Player Context on Arrival -- **首次到达**:从关卡首节点(战×2 的第一战)进入。MVP 无引导流,A4 雾首遇提示、chord 肌肉记忆提示等非模态提示承担首次教学(见 §States) +- **首次到达**:从关卡首节点(战×2 的第一战)进入。MVP 无引导流,地形变异雾首遇提示、chord 肌肉记忆提示等非模态提示承担首次教学(见 §States) - **到达前刚在做**:关卡节点链上点击战斗节点(LevelRunner 推进);或上一场战斗胜利后点下一节点 - **情绪状态**:棋手式清明专注(art-bible §2.1,能量 2/10)——挖掘期画面必须无杂音;结算期转为清算爽感(7/10) - **到达方式**:游戏推进送入(非自愿导航),进入即处于挖掘阶段(棋盘已 install、初始 3×3 已展开、小人出生) @@ -39,7 +39,7 @@ ## Navigation Position ``` -存档 → 冒险 → 关卡(节点链,C1-lite) → 【战斗屏】 +存档 → 冒险 → 关卡(节点链,关卡节点地图 lite) → 【战斗屏】 ├─ 胜利 → 节点奖励/恢复覆盖层 → 下一节点(下一场战斗屏) ├─ 胜利(末节点 Boss)→ 关卡结算(level_clear,库存清空反馈)→ 下关 └─ 失败 → 冒险终结 → 返回存档/营地 @@ -47,7 +47,7 @@ 战斗屏是**上下文相关屏**(仅由战斗节点进入),非顶层常驻。关卡内节点进度常驻屏顶(A 区),玩家始终知道自己身处链中第几环。 -- 上游屏:关卡节点链(节点地图形态 = 线性,C1 已裁剪为线性第一关) +- 上游屏:关卡节点链(节点地图形态 = 线性,关卡节点地图已裁剪为线性第一关) - 下游屏:节点奖励/恢复覆盖层(NodeOverlay,已有 demo 组件——建议后续单独出 spec `design/ux/node-reward.md`) - 平行屏:结算明细回看 overlay(本屏内域屏)、图例面板 overlay(本屏内域屏) @@ -66,8 +66,8 @@ | Exit Destination | Trigger | Notes | |---|---|---| -| 节点奖励/恢复覆盖层 | 敌方全灭(B1 判定段 verdict=win)且非末节点 | 不可逆:battle_over 已发生,RAP 清零 | -| 关卡结算 | Boss 战胜利 → level_clear | 不可逆:库存清空(须可见反馈,B4 D1×⑤) | +| 节点奖励/恢复覆盖层 | 敌方全灭(战斗结算判定段 verdict=win)且非末节点 | 不可逆:battle_over 已发生,RAP 清零 | +| 关卡结算 | Boss 战胜利 → level_clear | 不可逆:库存清空(须可见反馈,奖励道具经济×⑤) | | 冒险终结 | 主角 HP=0(verdict=lose) | 不可逆:Roguelike 死亡重开(art-bible §2.5 温柔惋惜调性) | | 无中途退出 | — | MVP 不设战斗内放弃/暂停菜单(回合制无时间压力;如需暂停留 Playtest 诊断按钮,调试用) | @@ -79,7 +79,7 @@ 本屏必须传达的全部信息,按注视优先级分五层: -1. **扫雷图本体**(第一注视点,挖掘期占视觉预算大头):四格态(已开 0 格地板/已开雾格无字/未开格/未开雾格——A2 D1×6② 一眼可辨)、数字、旗、小人、可挖域(8 向相邻常驻指示)、变异纹理(迷雾) +1. **扫雷图本体**(第一注视点,挖掘期占视觉预算大头):四格态(已开 0 格地板/已开雾格无字/未开格/未开雾格——排雷玩法×6② 一眼可辨)、数字、旗、小人、可挖域(8 向相邻常驻指示)、变异纹理(迷雾) 2. **本轮行动资源**:AP 余量(决定还能挖几格)、停手/弃图刷新按钮——紧贴棋盘上缘(动作发生地) 3. **战果兑现面**(结算期为第一注视点):敌人血条变化、逐行结算明细、高光(完美排雷/标中旗) 4. **规划面**(边缘注视,常读):HP、攻击、RAP 池、状态效果(己方/敌方)、库存列表、敌人意图徽标与顺位 @@ -99,12 +99,12 @@ | **D 扫雷图** | 中下 ~760px × 520px | 顶部 AP 条(44px:左——AP 点阵 + 停手按钮;右——图例按钮 + 弃图刷新按钮〔雷图右上角,2026-09-12 拍板〕);棋盘主体(居中,格 36–45px 见方);toast 槽(上缘内侧) | | **E 道具** | 右下 280px × 520px | 库存常读列表(固定槽高 + clip_contents——缺陷①教训);窗口期原位激活为道具使用窗口 | -**布局决策 D1(本 spec 最大决策,用户拍板)**:**常驻双舞台分屏**——战斗画面与扫雷图同屏常驻,挖掘期敌人待机可见,结算期不切场景。 +**布局决策 1(本 spec 最大决策,用户拍板)**:**常驻双舞台分屏**——战斗画面与扫雷图同屏常驻,挖掘期敌人待机可见,结算期不切场景。 - 备选(已否决):Peglin 式"结算时才切入独立战斗场景"——切换打断推理心流,且资产管线成本翻倍 -- **对 B5 双舞台陷阱的输入**:game-concept 登记的"战斗发生在棋盘上还是独立场景"悬念,由本布局裁决为**第三形态:同屏双舞台**。B5 前的"战斗表现美术测试"(一屏样张)应按此布局出样 -- 敌人不占格(B2 已锁)与本布局自洽:敌人实体只存在于 B 区,棋盘永远只有数字/旗/小人/变异 +- **对战斗表现双舞台陷阱的输入**:game-concept 登记的"战斗发生在棋盘上还是独立场景"悬念,由本布局裁决为**第三形态:同屏双舞台**。战斗表现定稿前的"战斗表现美术测试"(一屏样张)应按此布局出样 +- 敌人不占格(敌人系统已锁)与本布局自洽:敌人实体只存在于 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)。 +**棋盘格尺寸公式**:`cell = floor(min(D宽−padding, D高−AP条−padding) / N)`,N∈{10,12}(棋盘生成 D1–D4 行)。1280×800 下 12×12 → 36px,10×10 → 45px。数字字号 ≥ 格高的 50%,像素字体整数倍缩放(canvas_items stretch 保证清晰,ADR-0002)。 ### Component Inventory @@ -112,45 +112,45 @@ |---|---|---|---|---|---| | A | LevelStripView | 步进条 | 节点链 + 当前位置 | 只读(点击节点不可跳) | ✅ level_strip_view.gd 迁移 | | B | 英雄卡 | 卡片 | 小人像、HP 条、攻击值、状态效果槽 ×4 | 只读 | HUDView 拆分 | -| B | 敌人卡 ×1–3 | 卡片 | 敌像、血条、顺位徽标、意图徽标、行为环点、状态槽(含敌方 side 池口径:伏击层数为全体敌人共享单值、各卡同值呈现——数据源 = B1 流水经 B3 投影,B2×4④) | 只读 | 新建 | +| B | 敌人卡 ×1–3 | 卡片 | 敌像、血条、顺位徽标、意图徽标、行为环点、状态槽(含敌方 side 池口径:伏击层数为全体敌人共享单值、各卡同值呈现——数据源 = 战斗结算流水经增益状态投影,敌人系统×4④) | 只读 | 新建 | | B | 轮次·阶段指示 | 标签 | "第 N 轮 · 挖掘中/结算中/道具窗口" | 只读 | 新建 | -| B | 结算明细带 | 滚动列表 | 结算期逐行流水(B1 13 类行协议);挖掘期收起为上轮摘要 1 行 | 点击展开明细回看 overlay | battle_log_view.gd 改造 | -| B | 结算明细回看 | 模态 overlay | 完整流水逐行(四类行呈现,B4 D1×③) | 关闭 | battle_log_view.gd 改造 | +| B | 结算明细带 | 滚动列表 | 结算期逐行流水([战斗结算](../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋) 15 类行协议);挖掘期收起为上轮摘要 1 行 | 点击展开明细回看 overlay | battle_log_view.gd 改造 | +| B | 结算明细回看 | 模态 overlay | 完整流水逐行(四类行呈现,奖励道具经济×③) | 关闭 | 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 拆分 | +| C | 状态效果槽 ×4 | 图标槽 | effect_id→icon(形状+色相双通道)、层数角标、剩余结算次数点 | 悬停 tooltip(来源+语义) | 新建(增益状态×①) | +| D | AP 条 | 点阵 + 按钮组 | 本轮 AP 点阵、停手按钮(左)、弃图刷新按钮(右端·雷图右上角;RAP−8 · 本轮剩 N 次——上限 2 次已随行动经济 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 | 悬停预承诺光标 | 光标态 | 移动/挖/旗三语义光标 + 目标格边框高亮 | 自动 | 新建(排雷玩法×6③) | +| D | 可挖域指示 | 常驻微高亮 | 小人 8 向相邻可操作格 | 自动 | 新建(排雷玩法×6③) | +| D | 移动路径预览 | 路径高亮 | 悬停已开格显示 BFS 路径(合法性指示) | 自动 | 新建(排雷玩法×6⑤,裁决见下) | | D | toast 槽 | 非模态提示 | 拒绝原因(双源)、voided、满层溢出、雾首遇、chord 提示、零合法板告知 | 自动消退 2.5s | 新建 | -| D | 图例按钮 + 面板 | 按钮+模态 overlay | 格态/变异/雾语义图例("迷雾 = 无雷盲区"条目恒在) | 开/关 | 新建(A4 D1×②) | -| E | 库存列表 | 固定槽高列表 | 逐件:icon+名称+属性简述+状态;枚举序 = stock 受理序(E21) | 悬停 tooltip(完整描述+触发注记);窗口期点选 | item_window_overlay.gd 改造 | +| D | 图例按钮 + 面板 | 按钮+模态 overlay | 格态/变异/雾语义图例("迷雾 = 无雷盲区"条目恒在) | 开/关 | 新建(地形变异×②) | +| 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 保留 | +| 调试 | TunePanel | 调试面板 | atk%/hp% 滑条 + 批量模拟(正式侧归元进度,切片调试入口保留) | 调试 | ✅ tune_panel.gd 保留 | -**布局决策 D2 — 道具窗口形态 = 右栏原位激活**:窗口触发时 E 区面板点亮描边、标题切换为窗口态,棋盘侧去饱和 + 金橙色相偏移锁输入(明度不变——art-bible §2 明度恒定合规化,2026-09-12)。 +**布局决策 2 — 道具窗口形态 = 右栏原位激活**:窗口触发时 E 区面板点亮描边、标题切换为窗口态,棋盘侧去饱和 + 金橙色相偏移锁输入(明度不变——art-bible §2 明度恒定合规化,2026-09-12)。 - 备选(已否决):中央模态弹窗——遮挡结算演出首屏(授予段刚完成的视觉记忆),且库存常读位置与窗口位置分离造成空间记忆断裂 -- 依据:窗口期棋盘输入本就不竞争(结算期棋盘不可交互,A2 单意图帧先例),无模态必要性;B1 窗口锚点 = 授予段后、增益/减益段前——玩家需同时看库存与刚到手的奖励 +- 依据:窗口期棋盘输入本就不竞争(结算期棋盘不可交互,排雷玩法单意图帧先例),无模态必要性;战斗结算窗口锚点 = 授予段后、增益/减益段前——玩家需同时看库存与刚到手的奖励 -**布局决策 D3 — 移动路径预览 = 合法性指示**(A2 D1×6⑤ 悬案裁决):悬停已开格时高亮 BFS 路径(小人蓝细线点迹),移动执行为 ≤150ms 短滑。 +**布局决策 3 — 移动路径预览 = 合法性指示**(排雷玩法×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 弃图率/刷新率预警阈值联动复核。 +**布局决策 4 — 弃图刷新 = 挖掘期当场换图(2026-09-12 用户拍板,同日落稿上游)**:按钮置 AP 条右端(雷图右上角)。点击即当轮就地换新图继续挖——AP 不重置、剩余 AP 保留,RAP−8,本轮上限 2 次。 +- **上游修订(2026-09-12 已落稿,OQ12)**:① 行动经济 `request_refresh` Active 相位受理 + 每轮上限 2 次(Waiting 代理退役)② 排雷玩法装板受理域增 Active·仅刷新路径 ③ 编排 owner = **战斗结算 `refresh_board`**(行动经济扣费 → 排雷玩法 retire → 棋盘生成 generate → 排雷玩法 install → 奖励道具经济 roll_board 恰一次;无结算段、无 round_end——战斗结算编排唯一性原则保持)④ 旧板旗随旧板**作废**(零兑付、旗 AP 不返还——排雷玩法 EC11 全量重置承载;确认微交互须显式提示)。**经济预警**:当场刷新无结算发生,原「敌方时间税」自惩失效——行动经济 OQ3 弃图率/刷新率预警阈值联动复核。 - 备选(已否决):结算期决策提示(Waiting 内联)——结算期不做刷新决策(用户拍板);挖掘期预承诺 stop+reroll 复合——决策即终轮,不符「换图继续挖」语义。 -**敌人意图线索映射目录(B2×4① 成文占位,2026-09-12)**: +**敌人意图线索映射目录(敌人系统×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 态) +- 预告时序 = **下一拍预告**(当前拍视觉状态),挖掘期全程可见;结算期不另设意图播片(敌人系统 Rule 8⑤) +- 映射键 = enemy_id,跨 run 恒定、禁止重掷(敌人系统 Rule 8③);形状 + 色相双通道(art-bible 约束) +- 资产定稿归战斗表现;本表为 HUD框架侧成文占位(敌人系统 Visual Req 2 口径:grub 1 态 / ambusher 3 态) ### ASCII Wireframe @@ -207,22 +207,22 @@ | State / Variant | Trigger | What Changes | |---|---|---| -| **挖掘阶段(默认态)** | 轮次开始 / 结算完毕回 Active 相位 | B 区敌人待机(意图徽标全程可见——B2 Rule 8⑤),D 区棋盘可交互,AP 条点亮;能量 2/10,唯悬停呼吸微光在动(§2.1) | +| **挖掘阶段(默认态)** | 轮次开始 / 结算完毕回 Active 相位 | B 区敌人待机(意图徽标全程可见——敌人系统 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 落点) | +| **道具窗口态** | 结算授予段完成后(窗口锚点) | E 区原位激活(布局决策 2);空库存不弹窗直接跳过(奖励道具经济×②);usable_now=false 禁选+原因 | +| **清图瞬间** | 全部安全格开完 | 金粒喷泉 + 彩纸 3–5s 即收(§2.3);清图奖励行强调呈现(H2,奖励道具经济×④——金框高亮行);随后换新图 install | +| **完美排雷高光** | 战斗结算高光判定(全图标中/零误标类) | 全屏喝彩级反馈(§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④,同胜利) | +| **敌人死亡呈现** | 敌 hp=0(流水行击杀标记) | 该敌人卡血条归零 → 卡体灰化 + 碎裂退场(**禁瞬消**——敌人系统资产下限「死亡瞬间须可辨识、不得直接消失」,同等 blocking);战斗段短路的重击被掐灭不产行动明细行,由行序可推断(战斗结算);动画密度归战斗表现,呈现语义在本 spec | +| **胜利终局** | verdict=win | 金蜡封落于**扫雷图区中央**(§2.4 字面锚定),全屏暖白金 overlay;判定行 + 「下一节点」;明细回看入口;状态槽全清可见反馈(增益状态×4④——逐槽化光收回 + 微文案「状态随战斗结束消散」,防「我的 buff 哪去了」误读) | +| **失败终局** | verdict=lose(hero hp=0,战斗段短路) | 明度不变的暮蓝 #5A6C8C 氛围 overlay(全作唯一暮蓝档);小人坐砖掸灰、插过的旗逐面化光点收回(§2.5);判定行 + 明细回看(学习用途)+「返回」;状态槽全清可见反馈(增益状态×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×① 成文项) | +| **雾图变体(第二层起)** | 板含雾格 | 雾纹带双通道呈现;首次含雾板进入挖掘阶段触发一次性首遇提示(每 run 一次);图例恒可回看;**已开雾格保留底色层淡雾 tint**(与已开 0 格可判定区分——OQ8 裁决采纳 2026-09-12;tint 处视觉锚 3 最低层,不遮挡数字);**雾区全开一次性完成信号**(视觉合并域 = 信号单元,禁实例级双响;排雷玩法 EC11:双雾区呈现合并、实例独立) | +| **空态:无状态效果** | 增益状态 EC12 | 状态槽隐藏 or 空槽占位——**裁决:槽位隐藏,HP/攻击/RAP 上移补位**(角色信息区纵向空间紧张,空槽占位浪费);图例面板载空态说明(增益状态×① 成文项) | | **空态:零库存** | 关卡初/清空后 | E 区显示「道包空空——安全格里藏着补给」(指引性空态,非报错) | -| **调试变体** | 测试模式 | TunePanel 入口(demo 保留,正式侧 C4 退役);Playtest 诊断按钮(卡死转储) | +| **调试变体** | 测试模式 | TunePanel 入口(demo 保留,正式侧元进度退役);Playtest 诊断按钮(卡死转储) | -**拒绝/作废子态**(A2 D1×6⑥、A3 双源——每次拒绝玩家可感知): +**拒绝/作废子态**(排雷玩法×6⑥、行动经济双源——每次拒绝玩家可感知): - 棋盘面拒绝(sub_reason:非相邻/挖旗格等)→ 目标格 旗朱 #C63D22 边框闪 + toast 文案(图标+文案冗余,色弱清单 #7) - 资源面拒绝(AP 不足/RAP 不足)→ AP/RAP 数字脉冲 + toast「AP 不足 · 剩 0 点」 - voided(stop 被作废等)→ toast「本轮已清图,停手未执行 · 剩余 AP 已作废」——防静默改判读感欺骗 @@ -235,12 +235,12 @@ | 组件 | 动作 | 输入 | 即时反馈 | 结果(经组合根) | |---|---|---|---|---| -| 未开相邻格 | 挖掘 | 左键 | 格碎裂 + 连锁展开(免费)+ 奖励身份揭显 | 意图入当帧批次 → A2 `settle_batch` | -| 已开格 | 移动 | 左键 | 悬停时路径预览高亮(D3);执行 ≤150ms 短滑 | 意图入批(BFS 移动,免费) | +| 未开相邻格 | 挖掘 | 左键 | 格碎裂 + 连锁展开(免费)+ 奖励身份揭显 | 意图入当帧批次 → 排雷玩法 `settle_batch` | +| 已开格 | 移动 | 左键 | 悬停时路径预览高亮(布局决策 3);执行 ≤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 图例条目恒在) | +| 停手按钮 | 主动结束轮次 | 左键 | 按钮下压 + AP→RAP 飞行数字 | `settle_batch([stop])`(**经排雷玩法批次,不直调行动经济**——单意图帧批次契约) | +| 弃图刷新按钮(雷图右上角) | 当轮就地换图继续挖 | 左键 | 确认微交互(RAP−8 代价显式 + 旧板弃置提示);RAP<8 → 资源面拒绝可见(行动经济 EC13) | `request_refresh`(**Active 相位当场换图继续挖**——AP 不重置、本轮剩 N 次扣减;**上游已修订受理——见布局决策 4/OQ12**) | +| 图例按钮 | 开图例 | 左键 | 模态 overlay | 恒可开(地形变异 图例条目恒在) | | 结算明细带 | 展开回看 | 左键 | 明细 overlay | 只读 | | 状态/道具 tooltip | 悬停 | hover | 状态 tooltip(来源+语义+剩余);道具 tooltip(完整描述+触发注记——不含来源) | 只读 | | 道具(窗口态可用件) | 选用 | 左键 | 选中态高亮 → 窗口关闭回结算流 | `submit_window_selection(窗口位置)`——**传窗口条目位置,非库存句柄**(缺陷④契约) | @@ -249,13 +249,13 @@ | 胜利 overlay | 下一节点 | 左键 | → NodeOverlay / 关卡结算 | LevelRunner 推进 | | 失败 overlay | 返回 | 左键 | → 存档/营地 | 冒险终结 | -**单意图帧纪律**(A2 D1×6⑥):一帧内 collected 意图整批提交,stop 点击终结当帧收集;F4 语义序仲裁为防御性兜底。悬停预承诺三态光标:移动(箭头)/挖(镐)/旗(旗形)——左键双义(移动 vs 挖)的消歧完全依赖四格态一瞥可辨 + 光标区分(误挖不可逆的感知保险)。 +**单意图帧纪律**(排雷玩法×6⑥):一帧内 collected 意图整批提交,stop 点击终结当帧收集;F4 语义序仲裁为防御性兜底。悬停预承诺三态光标:移动(箭头)/挖(镐)/旗(旗形)——左键双义(移动 vs 挖)的消歧完全依赖四格态一瞥可辨 + 光标区分(误挖不可逆的感知保险)。 -**复合意图(A2 评估项留痕)**:点击远格 = 自动 move+dig 的两击摩擦消解——**MVP 不做**(留痕 2026-09-12:免费移动高频,两击摩擦符合 P4 ≤2 观测假设;A2 L298 评估项)。 +**复合意图(排雷玩法评估项留痕)**:点击远格 = 自动 move+dig 的两击摩擦消解——**MVP 不做**(留痕 2026-09-12:免费移动高频,两击摩擦符合 P4 ≤2 观测假设;排雷玩法 L298 评估项)。 **可挖域常驻指示**:小人 8 向相邻未开格呼吸微光(§2.1 唯一在动的元素);不可挖域(非相邻)悬停显示禁止光标 + 首次 toast 提示「需先走近(点击已开格移动,免费)」。 -**chord 肌肉记忆首遇提示**(A2 D1×6③):经典扫雷玩家首日尝试双击快开时,非模态 toast 每 run 一次:「本作无双击快开——插旗后轮末自动判定标中;点数字格可移动小人」(含 A2 登记锚点「点击数字格 = 移动小人」)。文案定稿权归实现期文案 pass。 +**chord 肌肉记忆首遇提示**(排雷玩法×6③):经典扫雷玩家首日尝试双击快开时,非模态 toast 每 run 一次:「本作无双击快开——插旗后轮末自动判定标中;点数字格可移动小人」(含排雷玩法登记锚点「点击数字格 = 移动小人」)。文案定稿权归实现期文案 pass。 --- @@ -265,13 +265,13 @@ | 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_round_input_ratio` | 原始输入动作数∶AP 消耗(行动经济 P4 ≤2 护栏观测,排雷玩法遥测义务) | +| 停手/清图 | `ux_round_exit` | path_tag {stopped, ap_exhausted, cleared}(对齐上游封闭枚举)——弃图率(难度曲线与Boss OQ4)观测 | +| 弃图刷新(挖掘期,非轮出口) | `ux_refresh_attempt` | {result: accepted/rejected_rap/rejected_cap, round_refresh_index: 1..2}——刷新使用(难度曲线与Boss OQ6)与行动经济 OQ3 预警阈值观测;每轮 0–2 次(布局决策 4 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 硬性要求① | +| 呈现订阅(不改核心) | 排雷玩法三事件 / [战斗结算](../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)步骤流(15 类行协议,含终局判定行) / battle_over / level_clear | 载荷 schema 成文义务见 §Data Requirements——旧版台账 entry 17①(HUD框架硬性要求) | **持久状态写入**全部经组合根唯一路径(settle_batch / submit_window_selection / request_refresh / runner 推进)——UI 层禁直调核心写(架构七原则"依赖单向组合根")。 @@ -281,15 +281,15 @@ | 时刻 | 过渡 | 依据 | |---|---|---| -| 进入战斗屏 | 棋盘 install 淡入(200ms)+ 初始 3×3 展开涟漪 + Boss 战先播紫赤影子爬棋盘(§2.6) | A1 install / §2.6 | +| 进入战斗屏 | 棋盘 install 淡入(200ms)+ 初始 3×3 展开涟漪 + Boss 战先播紫赤影子爬棋盘(§2.6) | 棋盘生成 install / §2.6 | | 挖掘→结算 | 明细带自底展开(150ms)+ 棋盘去饱和 + 金橙色相偏移 + 阶段指示翻转 | §2.2 色彩转金橙主导、饱和拉高、明度不变(状态区分经饱和/色相——art-bible §2 合规化 2026-09-12) | | 结算逐行兑现 | 光带自棋盘行起点横扫 → 开格化箭雨/旗化暴击弹道飞向敌人卡 → 受击闪白 + 血条直角递减 | §2.2 承载元素;因果全程可见可归因(P2) | -| 道具窗口 | E 区描边金光点亮(非弹窗,无缩放突兀) | 决策 D2 | +| 道具窗口 | E 区描边金光点亮(非弹窗,无缩放突兀) | 布局决策 2 | | 清图换新图 | 金粒喷泉 3–5s → 旧图碎裂淡出 → 新图 install 淡入 | §2.3 短促丰收不拖节奏 | -| RAP 清零(battle_over) | RAP 数字滚动归零 + 「随战斗结束消散」微文案 | A3 可见反馈义务 | -| 敌人死亡 | 血条直角递减至零 → 卡体灰化 + 碎裂退场(禁瞬消) | B2 资产下限(同等 blocking);动画密度归 B5 | -| 状态槽全清(battle_over) | 状态逐槽化光收回 + 「状态随战斗结束消散」微文案 | B3×4④;RAP 清零同款先例 | -| 库存清空(level_clear) | 道包逐件化光点飞出 + 「剩余道具 ×N 随关卡结束清空」 | B4 D1×⑤ | +| RAP 清零(battle_over) | RAP 数字滚动归零 + 「随战斗结束消散」微文案 | 行动经济可见反馈义务 | +| 敌人死亡 | 血条直角递减至零 → 卡体灰化 + 碎裂退场(禁瞬消) | 敌人系统资产下限(同等 blocking);动画密度归战斗表现 | +| 状态槽全清(battle_over) | 状态逐槽化光收回 + 「状态随战斗结束消散」微文案 | 增益状态×4④;RAP 清零同款先例 | +| 库存清空(level_clear) | 道包逐件化光点飞出 + 「剩余道具 ×N 随关卡结束清空」 | 奖励道具经济×⑤ | | 胜利 | 蜡封自上落下弹跳定格 → 碎砖拼纹章 → overlay 暖金渐显 | §2.4 克制不狂欢 | | 失败 | 全屏明度不变转暮蓝色相 → 旗化光点 800ms 逐面收回 → overlay | §2.5 最静一拍 | | 减动效选项 | 上述全部动画提供时长减半/直接跳终态开关(项目未建 accessibility 文档前,本项为 spec 内建承诺) | 动议:结算逐行兑现为信息载体不可整体跳过,可加速 | @@ -300,25 +300,25 @@ | 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` 编排承载) | +| 棋盘四格态/数字/雾/可挖域 | 排雷玩法 只读面(query) | Read | 呈现刷新面 = 排雷玩法 三事件信号(载荷 schema 成文——旧版台账 entry 17①) | +| BFS 移动路径 | 排雷玩法 `compute_move_path` | Read | 布局决策 3 合法性指示消费 | +| 拒绝原因 | 排雷玩法 `sub_reason` 封闭枚举 + 行动经济 资源面 | Read | 三源(board/resource/phase)映射文案——呈现层契约成文;资源面原因无上游载荷(行动经济 仅 bool + debug 诊断),客户端按动作上下文推断 | +| AP / RAP / 本轮刷新计数 | 行动经济 | Read | 常显;刷新入口可用性 = RAP 读数客户端预判(RAP≥8)+ `request_refresh` 返回值(行动经济 无 `can_request_refresh` API——仅 bool 返回 + 只读观测量;行动经济 EC13 拒绝合法可见) | +| 结算流水行(15 类)+ 四类行 | [战斗结算](../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋) 流水协议 | Read | 逐行来源标注(P2);聚合计数行呈现 | +| 判定 verdict | 战斗结算 判定段 | Read | 终局画面数据锚 | +| 敌单位 hp/顺位/意图环下一拍/enemy_id/存活 | 敌人系统 只读面 | Read | 意图线索映射全局恒定(敌人系统×4②);**despawn 前捕获**(旧版台账 entry 17③——缺陷②教训) | +| 状态效果投影(含空态/极性/溢出) | 增益状态 投影口径 | Read | 极性映射数据源 = 战斗结算 SettlementTable 效果语义,非 magnitude 符号(增益状态×③) | +| 库存(item_id/状态/枚举序/行值投影)+ 奖励身份 | 奖励道具经济 `query_inventory` / 快照 | Read | reward_id 全局恒定;**属性简述由行值快照投影派生**(伤害/回复/增益一句话);载荷含来源 {cell, round} 但**不呈现于库存列表与 tooltip**(2026-09-12 用户拍板——来源归因职责由结算明细行承载,P2 不受影响);窗口载荷 usable_now 由战斗结算组装(奖励道具经济 零跨系统读) | +| 节点链/当前节点/HP 携带 | 关卡节点地图 lite · LevelRunner | Read | A 区进度条 | +| 意图提交 | 排雷玩法 | **Write** | `settle_batch`(stop 亦经此——不直调行动经济) | +| 窗口选择 | 战斗结算 | **Write** | `submit_window_selection(窗口位置序)` | +| 弃图刷新 | 行动经济 | **Write** | `request_refresh`(挖掘期 Active 当场受理——布局决策 4 2026-09-12 上游已落稿;战斗结算 `refresh_board` 编排承载) | | 节点推进 | LevelRunner | **Write** | 胜利 overlay「下一节点」 | -| 终局画面数据锚 | B1 判定行(流水行协议) | Read | 「终局判定行 = 胜负画面的数据锚」(B1 UI Requirements);despawn 前状态可见性纪律 = entry 17③(销毁前捕获、**载荷数据自包含**、禁呈现层回读核心生命周期状态);若需独立终局快照信号,须回 B1/实现期 ADR 补登记(B1 现无 `settlement_snapshot` 契约——2026-09-12 评审修正) | +| 终局画面数据锚 | 战斗结算 判定行(流水行协议) | Read | 「终局判定行 = 胜负画面的数据锚」(战斗结算 UI Requirements);despawn 前状态可见性纪律 = 旧版台账 entry 17③(销毁前捕获、**载荷数据自包含**、禁呈现层回读核心生命周期状态);若需独立终局快照信号,须回战斗结算/实现期 ADR 补登记(战斗结算 现无 `settlement_snapshot` 契约——2026-09-12 评审修正) | -**架构注记**:UI 不拥有任何游戏状态——全部读经核心只读面,全部写经组合根(ADR-0001/0003)。呈现层信号载荷 schema 须在 D1 GDD 成文(entry 17①,缺项即 blocking);场景级 headless 测试进 AC(entry 17②);覆盖层层级断言(缺陷⑤——任何模态覆盖层必须有"打开时遮挡物隐藏"断言)。 +**架构注记**:UI 不拥有任何游戏状态——全部读经核心只读面,全部写经组合根(ADR-0001/0003)。呈现层信号载荷 schema 须在《HUD框架》成文(旧版台账 entry 17①,缺项即 blocking);场景级 headless 测试进 AC(entry 17②);覆盖层层级断言(缺陷⑤——任何模态覆盖层必须有"打开时遮挡物隐藏"断言)。 -**更新触发注记(2026-09-12 评审增补——防实现层等待不存在的信号,entry 17 教训同类)**:旗插/拔与移动成功反馈**零事件外发**(A2 发射面封闭)——经意图调用 `ActionOutcome` 返回值命令式驱动;AP/RAP 读数 A3 无推送信号——组合根每次调用返回后重查;状态效果 B3 纯被动容器零信号——状态栏刷新绑 B1 结算编排完成时点;库存 B4 `query_inventory` 查询面——写路径回执后重查。 +**更新触发注记(2026-09-12 评审增补——防实现层等待不存在的信号,旧版台账 entry 17 教训同类)**:旗插/拔与移动成功反馈**零事件外发**(排雷玩法发射面封闭)——经意图调用 `ActionOutcome` 返回值命令式驱动;AP/RAP 读数:行动经济无推送信号——组合根每次调用返回后重查;状态效果:增益状态纯被动容器零信号——状态栏刷新绑战斗结算的编排完成时点;库存:奖励道具经济 `query_inventory` 查询面——写路径回执后重查。 --- @@ -352,34 +352,34 @@ - [ ] 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④) +- [ ] 四种格态(已开 0 格地板/已开雾格无字/未开格/未开雾格)灰度截图下一眼可辨(排雷玩法×6②) +- [ ] 悬停光标区分移动/挖/旗三语义;可挖域(8 向相邻)常驻指示;非相邻格悬停显禁止反馈(排雷玩法×6③) +- [ ] 已引爆雷格呈"可通行可读"(排雷玩法×6④) - [ ] 全部数字对所在格底对比 ≥4.5:1;明度恒定规则无违例(art-bible §4.1) **交互与反馈** - [ ] 挖/移/旗/停手/刷新全部操作纯鼠标可完成;stop 意图经 `settle_batch` 提交(代码走查 + 探针断言) -- [ ] 每次拒绝(棋盘面/资源面/相位)玩家可感知且两源可区分;voided(至少 stop 作废)可感知(A2 D1×6⑥、A3 双源) +- [ ] 每次拒绝(棋盘面/资源面/相位)玩家可感知且两源可区分;voided(至少 stop 作废)可感知(排雷玩法×6⑥、行动经济双源) - [ ] 道具窗口: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 资产下限) +- [ ] 弃图刷新消耗 RAP 8 点、本轮上限 2 次可见(按钮文案实时「剩 N 次」;上限已随行动经济 2026-09-12 修订生效);RAP<8 资源面拒绝可见(行动经济 EC13);确认交互与常规确认不同位;换图后 AP 不重置、剩余次数扣减可见(**上游已修订受理——2026-09-12 布局决策 4 落稿**) +- [ ] battle_over 时 RAP 清零有可见反馈;level_clear 时库存清空有可见反馈(行动经济/奖励道具经济×⑤) +- [ ] battle_over 时状态槽全清有可见反馈(逐槽化光 + 微文案)(增益状态×④) +- [ ] 敌人死亡瞬间可辨识(卡体灰化 + 碎裂退场,禁瞬消);战斗段短路被掐灭的重击由行序可推断(敌人系统资产下限) **信息与归因** -- [ ] 结算逐行明细实时可见、战斗后可回看完整流水(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 呈现细则) +- [ ] 结算逐行明细实时可见、战斗后可回看完整流水(P2 逐行归因);四类行(道具语境/奖励未生效/已消耗未生效/使用被拒)按 奖励道具经济×③ 呈现 +- [ ] 敌人意图徽标挖掘期全程可见、映射全局恒定、含顺位与环长直觉编码(敌人系统×4①②③) +- [ ] 状态效果:极性呈现映射成文且非 magnitude 符号;空态渲染成文(裁决:槽隐藏);满层溢出经明细行可见(增益状态×①③④) +- [ ] 图例面板「迷雾 = 无雷盲区」条目恒在可随时回看;雾首遇提示一次性(每 run)且非模态不劫持输入,必含三要点(无雷盲区不赌命/雾格不传播连锁/雾内插旗纯损)(地形变异×②) +- [ ] 零合法板(棋盘生成 EC5 降级)出现时玩家侧诚实提示(toast,非静默) +- [ ] 已开雾格与已开 0 格灰度截图下可判定区分(底色层 tint 残留——OQ8 裁决采纳);雾区全开完成信号以视觉合并域为单元、无实例级双响(排雷玩法 EC11;样张须含宽度 1 雾带两侧毗邻数字格等最坏用例——地形变异 AC-9) +- [ ] 效果行 value 为单次贡献值,不得与 pool_value(池合成值)相加呈现;零值伤害行可见不被吞;衰减行按聚合计数呈现(战斗结算 呈现细则) - [ ] 未生效类行(奖励未生效/已消耗未生效/使用被拒/溢出未生效)携带形状冗余(icon/划线),非色相独载 -- [ ] 奖励徽章绘制优先级:旗之下、变异纹带之上、不进数字区;拾取瞬间 tier 档位可读(H1) +- [ ] 奖励徽章绘制优先级:旗之下、变异纹带之上、不进数字区;拾取瞬间 tier 档位可读(奖励道具经济 H1) -**场景级测试**(entry 17②③,缺项即 blocking) +**场景级测试**(旧版台账 entry 17②③,缺项即 blocking) - [ ] 场景级 headless 测试:真实实例化战斗屏场景 + 驱动完整对局 + 断言零脚本错误(ui_click_probe 模式扩展) - [ ] 任何模态覆盖层(图例/明细回看/胜利/失败/窗口态)有「打开时遮挡物隐藏/输入锁定」场景级断言(缺陷⑤对照) @@ -394,34 +394,36 @@ 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 观测「移动太瞬/太拖」反馈后调 +4. **战斗表现美术测试**(game-concept 已登记,战斗表现批次前强制):本 spec 的常驻双舞台分屏(布局决策 1)是测试的布局输入——敌人卡卡片式 vs 场景站桩式(无框立绘+底座)待样张定稿;本 spec 按"卡片式"起草(信息分层最稳),站桩式不改变分区结构 +5. **移动动画形态**(布局决策 3):≤150ms 短滑为推荐值,playtest 观测「移动太瞬/太拖」反馈后调 6. **明细带挖掘期是否常驻上轮摘要**:常驻(现方案)vs 结算后即收起——playtest 观测回看频率 -7. **道具窗口原位激活**(D2):若 playtest 反馈窗口存在感不足(漏选),升级为 E 区放大 + 全屏微暗(仍非中央弹窗) +7. **道具窗口原位激活**(布局决策 2):若 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 标注退役时点 +10. **TunePanel 退役计划**:正式侧归元进度(天赋树),切片调试入口在《HUD框架》标注退役时点 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 批次定 +12. **弃图刷新上游修订(已落稿 2026-09-12,布局决策 4)**:行动经济(Active 相位受理 + 每轮上限 2 次 + Waiting 代理退役 + 旧旗作废裁决 + 时间税失效预警)、排雷玩法(Active·仅刷新路径装板受理)、战斗结算(stopped/ap_exhausted 恒 same_board + `refresh_board` 挖掘期刷新编排;AC-29 改写/AC-30 恒 same_board)、registry `rap_rules` 同步、旧版 systems-index 修订块登记。遗留观察项:当场刷新的节奏健康由行动经济 OQ3 弃图率/刷新率预警承载,playtest 复核 +13. **教学引导缺口挂靠(成文时教学引导 GDD 尚未建,留痕)**:挖到雷 = 敌方优势而非死亡(排雷玩法登记「经典扫雷玩家最大预期违背」)、旗挡链(连锁止于旗属正常)、首次蓄力姿态一次性提示(敌人系统)、窗口拾取→囤刀→选用闭环教学(奖励道具经济)——本屏 toast 体系为落点候选,随教学引导批次定 --- ## §13 上游义务对照表(门禁核对用) +> 「系统名×条目号」记法(如「排雷玩法×6⑥」)= 旧版 systems-index 门禁台账中该系统的义务条目编号,条目内容均已在本表逐条承载。 + | 义务源 | 条目 | 本 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 | +| 排雷玩法×6 | ① stop 经批次 | §Interaction Map 停手行 + AC | +| 排雷玩法×6 | ② 四格态可辨 | §Layout 格态 + AC | +| 排雷玩法×6 | ③ 悬停预承诺+可挖域+chord 提示 | §Interaction Map + §States | +| 排雷玩法×6 | ④ 引爆雷格可通行可读 | §AC | +| 排雷玩法×6 | ⑤ 路径预览裁决 | 布局决策 3 | +| 排雷玩法×6 | ⑥ 单意图帧+voided+拒绝可感知 | §Interaction Map + §States 拒绝子态 + AC | +| 行动经济 | AP/RAP 常显+刷新入口+清零反馈+双源 | §Layout C/D 区 + §Transitions + AC | +| 地形变异×3 | 雾纹理/图例+首遇/已开雾格+tint | §States 雾图变体 + 图例组件 + AC | +| 战斗结算×3 | ①高光行强调呈现(标中旗金框行/清图 H2 金框行)②完美排雷呈现形态 ③棋盘生成 EC5 零合法板提示/补偿/回退形态(toast 提示;补偿/回退不提供——三形态选型留痕 2026-09-12) | §States 高光/清图 + §States 拒绝子态 + §AC;注:结算明细屏+道具窗口 = 战斗结算 UX Flag 事项(本 spec 即其 /ux-design 产物),非 战斗结算×3 台账 | +| 敌人系统×4 | 意图映射/恒定/顺位/敌方池 | §Component Inventory 敌人卡 + **意图线索映射目录(敌人系统×4① 成文占位,资产定稿归战斗表现)** + AC | +| 增益状态×4 | 常显+空态/来源标注/溢出/去留 | §States 空态 + §AC | +| 奖励道具经济×5 | 身份恒读/窗口/四类行/清图强调/清空反馈 | §Component Inventory E 区 + §States 清图/高光 + AC | +| 棋盘生成 | EC5 零合法板诚实呈现(数据面经 战斗结算×3③ 上抛) | §States 拒绝子态(toast)+ AC | +| 敌人系统资产下限 | 受击反馈/死亡表现/顺位标记 | §Transitions 受击 + §States 敌人死亡呈现(禁瞬消)+ §Component 顺位徽标;呈现语义在本 spec(同等 blocking),动画密度归战斗表现 | diff --git a/design/ux/interaction-patterns.md b/design/ux/interaction-patterns.md index 8323c588..97198c8d 100644 --- a/design/ux/interaction-patterns.md +++ b/design/ux/interaction-patterns.md @@ -36,9 +36,9 @@ | 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 补) | +| 16 | 清图/换图演出编排 | Feedback | **已实现·占位级**(board_fx_view.gd 金粒彩纸——规格随战斗表现 美术批补) | +| 17 | 胜利/失败终局编排 | Feedback | **已实现·占位级**(battle_end_overlay.gd——规格随战斗表现补) | +| 18 | Boss 遭遇演出 | Feedback | **已实现·占位级**(紫赤影子——规格随战斗表现补) | | 19 | 库存常读列表 | Data Display | **已实现**(inventory_panel_view.gd——规格全文待补) | | 20 | 标准控件组(button/progress bar/list) | Control | **已实现**(ui_kit.gd 直角细线/凸圆角——规格全文待补) | @@ -53,10 +53,10 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab **Category**: Feedback **Used In**: 战斗屏(棋盘/资源全部意图动作) -**Description**: 玩家意图被核心拒绝时,反馈必须同时做到:(a) 每次拒绝玩家可感知(防静默拒绝读感欺骗);(b) 来源可区分——棋盘面(A2 判,携 sub_reason 封闭枚举)/资源面(A3 判,客户端推断)/相位(A2 ActionOutcome)三源各异;(c) 图标+文案双通道,非色相独载。 +**Description**: 玩家意图被核心拒绝时,反馈必须同时做到:(a) 每次拒绝玩家可感知(防静默拒绝读感欺骗);(b) 来源可区分——棋盘面(排雷玩法判,携 sub_reason 封闭枚举)/资源面(行动经济判,客户端推断)/相位(排雷玩法 ActionOutcome)三源各异;(c) 图标+文案双通道,非色相独载。 **Specification**: -- 触发:`ActionOutcome{accepted=false, reject_reason, sub_reason}` **返回值驱动**(拒绝无事件外发——A2 发射面封闭,实现层不得等待拒绝信号) +- 触发:`ActionOutcome{accepted=false, reject_reason, sub_reason}` **返回值驱动**(拒绝无事件外发——排雷玩法发射面封闭,实现层不得等待拒绝信号) - 反馈映射: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 作废可感知 @@ -65,7 +65,7 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab **When to Use**: 任何意图提交被拒绝时(唯一拒绝呈现通道) **When NOT to Use**: 成功反馈(走动作本身的直接视觉反馈通道);voided 语义场景(作废文案独立) -**Reference**: battle.md §States 拒绝/作废子态、§Interaction Map;A2 D1×6⑥、A3 双源契约 +**Reference**: battle.md §States 拒绝/作废子态、§Interaction Map;排雷玩法×6⑥、行动经济双源契约 --- @@ -77,7 +77,7 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab **Description**: 窗口态切换不弹中央模态——既有面板**原位**点亮为窗口态(描边金光 + 标题切换),输入竞争区压暗锁输入。库存常读位置 = 窗口位置,空间记忆连续、不遮挡结算演出首屏。 **Specification**: -- 触发:B1 结算编排窗口锚点(授予段完成后、增益/减益段前) +- 触发:战斗结算编排窗口锚点(授予段完成后、增益/减益段前) - 行为:E 区面板描边金光点亮、标题切换「⏳ 轮末道具窗口 · 至多选 1 件」+ 时机指示常驻;D 区棋盘**去饱和 + 金橙色相偏移**锁输入(明度不变——art-bible §2) - 退出:选用 1 件 / 跳过按钮 → 关闭回结算流;空库存不弹窗直接跳过 - 选中即承诺(照常消耗);窗口选择序列 = 结算回放输入事件 @@ -86,7 +86,7 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab **When to Use**: 窗口内容与常读面板同域(空间记忆连续优先)、输入竞争区明确无需遮断全屏时 **When NOT to Use**: 全屏打断级信息(胜利/失败终局、图例回看——用 pattern 6 模态 overlay) -**Reference**: battle.md 布局决策 D2、§States 道具窗口态 +**Reference**: battle.md 布局决策 2、§States 道具窗口态 --- @@ -98,17 +98,17 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab **Description**: 轮末结算逐行流水实时呈现 + 可回看——「扫雷之因逐行兑现为战果」(P2 归因)的承载面。挖掘期收起为 1 行上轮摘要;结算期展开 84px 实时流;点击开完整回看 overlay。 **Specification**: -- 数据源:B1 步骤流(13 类行封闭协议;公共字段 `type/source_class/origin_cell/round_index/display_name/value/highlight?`);**无来源行 = 断言失败** +- 数据源:[战斗结算](../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)步骤流(15 类行封闭协议;公共字段 `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 流水击杀结果字段) +- 击杀标记呈现(H3 数据源 = 战斗结算流水击杀结果字段) **When to Use**: 任何「因果可归因」的逐行流水呈现 -**When NOT to Use**: 自造行类型/字段(13 类封闭枚举——增删 = 接口漂移,须走 B1 GDD 修订) +**When NOT to Use**: 自造行类型/字段(15 类封闭枚举——增删 = 接口漂移,须走[《战斗结算》](../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)修订) -**Reference**: battle.md §Component Inventory 结算明细带、§AC 信息与归因;b1-settlement.md Core Rule 14 / Visual Req +**Reference**: battle.md §Component Inventory 结算明细带、§AC 信息与归因;归档《战斗结算》(b1-settlement.md)Core Rule 14 / Visual Req --- @@ -125,12 +125,12 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab - 可挖域常驻指示:小人 8 向相邻未开格呼吸微光(挖掘期唯一在动元素——art-bible §2.1) - 非相邻格:禁止光标 + 首次 toast「需先走近(点击已开格移动,免费)」 - 提交:悬停仅预承诺,**点击才提交意图**(单意图帧批次——目录 #12) -- chord 首遇提示:怀旧玩家双击快开尝试时一次性 toast(含 A2 锚点「点数字格可移动小人」) +- chord 首遇提示:怀旧玩家双击快开尝试时一次性 toast(含排雷玩法登记锚点「点数字格可移动小人」) **When to Use**: 一键多义且后果不对称的操作面 **When NOT to Use**: 单义控件(直接点击反馈即可,勿加光标层) -**Reference**: battle.md §Interaction Map;A2 D1×6②③ +**Reference**: battle.md §Interaction Map;排雷玩法×6②③ --- @@ -143,14 +143,14 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab **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 +**Reference**: battle.md §Layout C/D 区;行动经济 UI Requirements --- @@ -203,18 +203,18 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab **Specification**: - 形态:灰置 + tooltip「HP 已满 · 禁选」;点击无效 + 原因 toast 兜底 - 语义区分:满血 = 零收益守卫(禁选、不烧包)≠ 缺血不足(超量 clamp 部分回复,**不**禁选) -- usable_now 由 B1 组装进窗口载荷(B4 零跨系统读)——呈现层只消费不推断 +- usable_now 由战斗结算组装进窗口载荷(奖励道具经济零跨系统读)——呈现层只消费不推断 **When to Use**: 任何可见但不可交互的元素 **When NOT to Use**: 完全隐藏的不可用功能(不存在的东西无需解释) -**Reference**: battle.md §States 道具窗口态、§Interaction Map;B4 Rule 7 / AC-14 +**Reference**: battle.md §States 道具窗口态、§Interaction Map;奖励道具经济 Rule 7 / AC-14 --- ## Gaps & Patterns Needed -- **目录 #9–#20 已全部实现**(2026-09-12 战斗屏实现批,冒烟 149/149 + 三探针全绿);规格全文回写 = D1 正式化批义务 +- **目录 #9–#20 已全部实现**(2026-09-12 战斗屏实现批,冒烟 149/149 + 三探针全绿);规格全文回写 = HUD框架正式化批义务 - 实现期新增技术件(不单独立 pattern,规格归并):去饱和+金橙状态切换 = 逐格共享 ShaderMaterial(明度不变,gl_compatibility 安全);敌人死亡残位 = 卡体灰化+碎裂禁瞬消(归入 #17 终局编排规格) - **MVP 未使用标准控件**:toggle / dropdown / input field / tab bar(无需求不设占位);TunePanel slider 为调试专用不入库 - **实现偏差规则**:pattern 近似但不完全适配时,实现层不得静默变体——登记偏差交 ux-designer 复核后回写 diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index 3eac316f..73db40cd 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -110,7 +110,7 @@ | 模块 | 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 冒烟实证可回放) | +| **battle_orchestrator**(B1) | 段序骨架(七段结构冻结)、轮 facts 缓冲、可回放步骤流、[明细行协议](../../design/gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)(15 类行 + 字段分层)、条款③⑤闭合对账、**战斗域全部跨系统调用权** | `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 | 无 | @@ -193,7 +193,7 @@ D1 ◀──呈现数据源:A2 事件流 / B1 步骤流 / A3 ap-rap / ConfigDB | 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 状态机 | 可回放步骤流(同种子逐行相同) | +| B1 → D1 | [步骤流行协议](../../design/gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)(15 类封闭枚举 + 公共字段 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 采纳前对照核实。 diff --git a/docs/consistency-failures.md b/docs/consistency-failures.md deleted file mode 100644 index 9bf82abb..00000000 --- a/docs/consistency-failures.md +++ /dev/null @@ -1,38 +0,0 @@ -# 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 deleted file mode 100644 index 8d28a2f0..00000000 --- a/docs/consistency-report-2026-09-08-b3.md +++ /dev/null @@ -1,31 +0,0 @@ -# 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 deleted file mode 100644 index 7c43d053..00000000 --- a/docs/consistency-report-2026-09-08.md +++ /dev/null @@ -1,25 +0,0 @@ -# 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 deleted file mode 100644 index de614cfd..00000000 --- a/docs/consistency-report-2026-09-09-a1.md +++ /dev/null @@ -1,26 +0,0 @@ -# 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 deleted file mode 100644 index 658abc4a..00000000 --- a/docs/consistency-report-2026-09-09-b4.md +++ /dev/null @@ -1,60 +0,0 @@ -# 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 deleted file mode 100644 index 985eadd8..00000000 --- a/docs/consistency-report-2026-09-12.md +++ /dev/null @@ -1,78 +0,0 @@ -# 一致性检查报告(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/doc-conventions.md b/docs/doc-conventions.md index eddfbe68..bad4f2db 100644 --- a/docs/doc-conventions.md +++ b/docs/doc-conventions.md @@ -96,7 +96,7 @@ | 待拍板(可选) | 冲突、缺口、设计变更失义条目 | | 版本历史 | 表格 | -**命名**:中文、见名知意(如"游戏概念""棋盘生成");禁止 a1_/b1_ 编号前缀;不留纯空目录。 +**命名**:中文、见名知意(如"游戏概念""棋盘生成");禁止 a1_/b1_ 编号前缀;不留纯空目录。正文中指代子系统一律用中文规范名;英文代号(供代码标识符与工具链)对照表见 `design/gdd/02-系统清单.md`,旧代号(a1–d6)仅限解读归档时使用,不得写进现行文档。 ## 7. 引用纪律(跨文档高内聚低耦合) @@ -134,3 +134,4 @@ |---|---|---| | v1 | 2026-09-14 | 首版成文:整合用语纪律、文档形态纪律、两件套与树形文档体系、新骨架过渡口径,新增可读性第一纪律与系统文档骨架 | | v2 | 2026-09-14 | 新增第 7 节引用纪律(跨文档高内聚低耦合:单点定义 / 规范名唯一 / 引用不转述 / 反向边登记 / 删除概念 SOP),原 7/8 节顺延为 8/9 | +| v3 | 2026-09-15 | 命名纪律补充:现行文档代号清偿完毕,子系统统一以中文规范名指代;英文代号对照表落盘 `design/gdd/02-系统清单.md`,旧代号仅限解读归档 | diff --git a/docs/lean-gdd-sample-b1.md b/docs/lean-gdd-sample-b1.md index b214f30a..3e385eeb 100644 --- a/docs/lean-gdd-sample-b1.md +++ b/docs/lean-gdd-sample-b1.md @@ -1,6 +1,6 @@ # B1 战斗结算规则(lean 格式样例) -> **状态**:格式样例——用新格式重排现有 `b1-settlement.md` 的内容域,供评审,非生效文档 +> **状态**:格式样例——用新格式重排现有 `b1-settlement.md` 的内容域,供评审,非生效文档(行协议计数等现行口径以 `design/gdd/systems/战斗循环/战斗结算.md` 为准) > **版本**:lean-sample · 2026-09-13 · 作者:user + agents > **一句话**:B1 把每轮扫雷的结果逐行兑现为战斗结果,并判定胜负。 diff --git a/docs/playnote.md b/docs/playnote.md new file mode 100644 index 00000000..3ff37802 --- /dev/null +++ b/docs/playnote.md @@ -0,0 +1,14 @@ +# 试玩问题记录 + +## 垂直切片 v1 + +### 核心循环 + +1. 减伤太多; + - 不要固定值,换成百分比,需要有百分比上限;或者固定上限 +2. 增伤也太多了; + - 考虑替换安全格的奖励占比,增加一种冒险内的累计资源(比如:金币,只能用于冒险内购买道具;) +3. 玩家攻击和怪物攻击不对等 + - 考虑引入命中(AC like),但是造成伤害的方式要区分开; +4. 每次开图就是开了片面积; + - 是否有生成图计算如此? \ No newline at end of file diff --git a/docs/registry/architecture.yaml b/docs/registry/architecture.yaml index a8e0cc39..f9fce3b1 100644 --- a/docs/registry/architecture.yaml +++ b/docs/registry/architecture.yaml @@ -1,24 +1,25 @@ # Architecture Registry — Dungeon Sweeper # 生成:2026-09-12(/architecture-decision solo 批:ADR-0001~0004;用户授权代决) # 规则:只增不改;立场变更 = 旧条目标 status: superseded_by + 新增条目(永改既有条目) +# 命名注记(2026-09-15):A1–D6 系统代号已清偿为中文规范名(对照表见 design/gdd/02-系统清单.md);interfaces 两条目键名同批改领域命名(a2_event_surface→minesweeper_event_surface、b1_settlement_trigger→settlement_trigger)。 # 设计层立场(状态归属/接口契约/锚验收)登记在 design/registry/entities.yaml——本文件只登记实现级架构立场 interfaces: - - name: a2_event_surface + - name: minesweeper_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 + note: GDD 概念名 round_end 的 排雷玩法 实现实名 = round_end_board;行动经济 直发 = round_end_stopped(payload 同构) + - name: settlement_trigger adr: ADR-0001 - contract: B1 订阅 A3 相位→Waiting 触发结算;只读 last_round_end + 相位观测量;不依赖事件到达序 + contract: 战斗结算 订阅 行动经济 相位→Waiting 触发结算;只读 last_round_end + 相位观测量;不依赖事件到达序 - name: presentation_subscription_rule adr: ADR-0001 - contract: 呈现层永不直接订阅 A2/A3——只订 B1 步骤流 / level_runner(切片缺陷②教训固化) + contract: 呈现层永不直接订阅 排雷玩法/行动经济——只订 战斗结算 步骤流 / level_runner(切片缺陷②教训固化) api_decisions: - name: renderer adr: ADR-0002 - decision: gl_compatibility(Forward+ 否决——D3D12 新驱动线降险;B5 出现 HDR 2D 需求时复议) + decision: gl_compatibility(Forward+ 否决——D3D12 新驱动线降险;战斗表现 出现 HDR 2D 需求时复议) - name: window_base adr: ADR-0002 decision: 1280×800 基座 + stretch canvas_items + aspect expand(全部键显式落盘,防引擎默认漂移) @@ -27,16 +28,16 @@ api_decisions: decision: 仅 {ConfigDB, RngService}(定名锁定);战斗编排器等核心类由组合根按局实例化,非 autoload - name: board_rendering adr: ADR-0003 - decision: MVP 沿用切片 GridContainer + 逐格控件形态(探针耦合事实);_draw() 迁移 = D1 正式化候选工作项(含三探针改造 + push_input 端到端探针 + 命中测试自建) + decision: MVP 沿用切片 GridContainer + 逐格控件形态(探针耦合事实);_draw() 迁移 = HUD框架 正式化候选工作项(含三探针改造 + push_input 端到端探针 + 命中测试自建) - name: cjk_font adr: ADR-0003 - decision: CJK 字体随包发行并设 theme 默认字体(运行时默认字体无 CJK 字形)——D1 正式化硬性前置 + 导出 checklist 项 + decision: CJK 字体随包发行并设 theme 默认字体(运行时默认字体无 CJK 字形)——HUD框架 正式化硬性前置 + 导出 checklist 项 - name: test_carrier adr: ADR-0004 - decision: 双载体(--headless --script 逻辑冒烟 + headless 场景探针)+ 静态扫描域 S1–S8 + CI 外部 wall-clock 超时;gdUnit4 deferred(D2 OQ1 关闭形态) + decision: 双载体(--headless --script 逻辑冒烟 + headless 场景探针)+ 静态扫描域 S1–S8 + CI 外部 wall-clock 超时;gdUnit4 deferred(数据配置 OQ1 关闭形态) - name: save_contract adr: ADR-0005 - decision: SCHEMA_VERSION 比较逻辑归 D3(存档版本 > 当前 = 拒读);RNG 快照 = master_seed + 各流 scope 计数器 + 意图/窗口选择序列,切出须在 scope 事件边界外(无在飞 open_stream 实例——B1 编排保证) + decision: SCHEMA_VERSION 比较逻辑归 存档序列化(存档版本 > 当前 = 拒读);RNG 快照 = master_seed + 各流 scope 计数器 + 意图/窗口选择序列,切出须在 scope 事件边界外(无在飞 open_stream 实例——战斗结算 编排保证) forbidden_patterns: - pattern: autoload_global_event_bus @@ -50,15 +51,15 @@ forbidden_patterns: 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 补全) + detail: randi()/randf()/randf_range() 与 randomize() 同禁(全局 RNG 绕开 RngService——数据配置 Rule 5 补全) - pattern: core_extends_control_or_tscn_preload adr: ADR-0004 - detail: core/ 禁 extends Control/Node2D、禁 .tscn preload(D2 Rule 7) + detail: core/ 禁 extends Control/Node2D、禁 .tscn preload(数据配置 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) + detail: 敌人系统/增益状态/奖励道具经济 战斗域 API 唯一调用方 = 战斗结算;排雷玩法/增益状态/奖励道具经济 零相互直调(静态扫描 S7——战斗结算 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 ⑦⑧ 为表不可变 + 单次加成的回归断言载体 + decision: 主角加成值经 encounter 显式参数传入(begin_battle 增 hero_atk/hero_max_hp 字段;敌人系统.spawn 增 atk_override/max_hp_override 参数,-1 = 表值兜底)——db.hero 表值运行期只读(切片原表写通道 _apply_hero_override/runner 写表废除) + note: 2026-09-13 E4 收口;接口漂移 = 敌人系统.spawn 签名 +2 参数、战斗结算 encounter +2 字段(正式侧 战斗结算/敌人系统 GDD 修订随 ADR-0009 批);ui_tune_probe ⑦⑧ 为表不可变 + 单次加成的回归断言载体 diff --git a/production/qa/bugs/BUG-0001.md b/production/qa/bugs/BUG-0001.md deleted file mode 100644 index b1d9a868..00000000 --- a/production/qa/bugs/BUG-0001.md +++ /dev/null @@ -1,85 +0,0 @@ -# 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 deleted file mode 100644 index 02ca69ba..00000000 --- a/production/qa/bugs/BUG-0002.md +++ /dev/null @@ -1,61 +0,0 @@ -# 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 deleted file mode 100644 index a16e9399..00000000 --- a/production/qa/bugs/BUG-0003.md +++ /dev/null @@ -1,63 +0,0 @@ -# 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 deleted file mode 100644 index 8a41059b..00000000 --- a/production/qa/bugs/BUG-0004.md +++ /dev/null @@ -1,62 +0,0 @@ -# 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/session-state/active.md b/production/session-state/active.md index fe9f94ef..1c1dda89 100644 --- a/production/session-state/active.md +++ b/production/session-state/active.md @@ -13,119 +13,120 @@ 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 门禁评审须逐项核对 +4. **数据配置 已收口**:两轮 /design-review(full + 同日 re-review)+ 独立 delta 复核 5/5 PASS → **Approved**(2026-09-07)。行动经济 定稿回填 ActionEconomyTable(AP 8/旗 4/停手 ×2/清图 ×1),数据配置 Tuning Knobs 对应条目已按定稿删除义务移除,registry 已登记 `action_economy`,索引补齐 冒险结构/战斗结算/奖励道具经济/元进度←数据配置 四边 +5. **行动经济 已收口**:GDD 全节已写入(`design/archive/gdd-2026-09-14/a3-action-economy.md`,Status: Approved 2026-09-07 三轮 re-review;评审日志 `design/archive/gdd-2026-09-14/reviews/a3-action-economy-review-log.md`),registry 已登记 `action_economy` +6. **地形变异 已收口**:两轮 /design-review(2026-09-08 full 8 阻塞 + 同日 re-review 6 阻塞,均同日核销)→ **Approved**(用户拍板 delta 核销免第三轮,行动经济 先例)。承重数学(厚度≤2【≡任意 3×3 窗口非全雾】/f≤8d/两态 AP/F1 谓词)经两轮对抗零动摇;承重规则不变(生成期布置约束 + 零特判 + fog unlock_level=2)。数据配置/registry/systems-index 三方已同步(跨字段校验 hide_numbers⇒safe_nonzero 致命、奖励道具经济 三条定价硬约束、棋盘生成×15/排雷玩法×4/HUD框架×2/战斗结算 输入门禁登记全枚举)。评审日志:`design/archive/gdd-2026-09-14/reviews/a4-terrain-variation-review-log.md`(含两轮阻塞→修复对照表) +7. **增益状态 设计会话进行中(2026-09-08 启动)**:上下文预收集同日完成——① review mode = lean(review-mode.txt 不存在,取默认,与索引头部一致);② 引擎域 = Core,无引擎障碍(纯数据+回合逻辑,4.7 变更清单零交集;Core 零 UI 依赖、结算禁 await 为设计约束);③ **已锁事实不得默改**:己方限时增益跨轮次延续(按剩余结算次数)、不跨战斗【game-concept 2026-09-06】;三向效果映射(安全格→主角 buff/开雷→敌方增益/标中雷→怪物 debuff)【v3 定稿】;标中旗一次性兑付、资格以雷格实例为键、拔旗重插不恢复【行动经济 OQ5】;debuff 档位锚严格 > 最低档安全格 buff 当量、对照基准=连锁放大边际挖掘收益【行动经济 F1 条款 2】;安全格 buff/标中旗 debuff/清图奖励/敌方增益统一当量单一线性换算、战斗结算 闭合 owner【行动经济 F1 条款 1】;安全格奖励有回合限制甚至一次性、扫雷只入库轮末结算、P2 要求结算明细标注来源;④ 数据承载 = 数据配置 StatusEffectTable 占位行("buff 时限/叠层字段名",单行全局,owner=增益状态 回填);⑤ 增益状态 为 Foundation 瓶颈系统,下游 奖励道具经济/战斗结算 未设计——接口契约需 provisional 登记(行动经济 条款 1/3/5 的 provisional 先例现成);⑥ 无 docs/consistency-failures.md;既往一致性检查 0 冲突 +8. **增益状态 已收口(Designed,2026-09-08)**:GDD 全节已写入(`design/archive/gdd-2026-09-14/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 行动经济 条款 2 锚预检不过——红色预警,战斗结算 对照验收裁决**;用户拍板四行定稿权收编 增益状态、锚验收权留 奖励道具经济/战斗结算——行动经济 OQ5 修订已登记索引)。registry 已登记 `status_effect` + action_economy.referenced_by 增 增益状态;数据配置 Core Rules/Status 已回写;索引门禁增补记录已登记 增益状态 块(**战斗结算×5 义务清单——战斗结算 GDD 门禁评审缺项即 blocking**;EC9 reload gate 战斗结算 落点;ambush 量纲→敌人系统)。下一步 = **棋盘生成(设计顺序第 5 位,MVP)**——注意 地形变异 已登记 棋盘生成×15 验收清单 + 增益状态 登记 ambush/weaken 锚供给义务,棋盘生成 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 +数据配置 → 行动经济 → 地形变异 → 增益状态 → 棋盘生成 → 排雷玩法 → 敌人系统 → 奖励道具经济 → 战斗结算 → HUD框架 →(垂直切片:辅助道具 → 难度曲线与Boss → 冒险结构 → 关卡节点地图 → 战斗表现)→(Alpha:存档序列化 → 音频 → 教学引导 → 元进度)→ 设置与本地化 ## 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 登记) + + + + + +9. **Task: Designing 棋盘生成 GDD**(2026-09-08 启动)· File: `design/archive/gdd-2026-09-14/a1-board-generation.md` +10. **棋盘生成 已收口(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 归 难度曲线与Boss)。registry 已登记 `board_gen_table` + action_economy.referenced_by 增 棋盘生成;数据配置 两处回写;索引门禁增补记录 棋盘生成 块(**排雷玩法 双包含回验——排雷玩法 GDD 门禁缺项即 blocking**;战斗结算 对账消费 actuals p10 非 target;F4 全量工具与 数据配置 OQ5 同批排期;黄金向量第二人复核)。**下一步 = 排雷玩法规则(设计顺序第 6 位,MVP,瓶颈系统)**——硬输入:行动经济 事件契约(round_end 双发射源/try_spend/last_round_end)、地形变异×4 验收清单、棋盘生成 双包含回验 + 产物消费、标错惩罚待原型小补丁实测后关闭(CD 门禁:可与原型 v3 终验合并会话) +11. **排雷玩法 已收口(Designed,2026-09-09)**:GDD 全节已写入(`design/archive/gdd-2026-09-14/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₀ 恰等(双包含两臂构造性成立、棋盘生成 回退口径不触发;免费/计入清图/无奖励——行动经济 EC5 委派落点)· 批次语义序仲裁(cleared-dig→stop→其余;行动经济 EC4 四组合 deferral 在 排雷玩法 冒烟兑现 AC-10)· 旗单态轮末判定诚实重复上报(去重归 战斗结算/奖励道具经济)· 已引爆雷格可通行(工程债核销 AC-15)· install 四不变式。**🔴 反例 B 阻断缺口(systems-designer 发现、主会话独立复算证实)**:棋盘生成 布雷数下限拦不住"初始展开即 cleared"退化板(8×8 幸存距离-2 雷 → R₀=S;N=1 行 100% 退化、断言 A 零拦截)——登记 **棋盘生成 修订义务**(F5 legality 增 R₀≠S + F4 断言 + 排除域基准措辞;棋盘生成 已 Approved 走接口漂移)+ 排雷玩法 侧不变式④纵深防御。registry 已登记 `minesweeper_event_surface` / `minesweeper_initial_expansion`;systems-index 已登记 排雷玩法 门禁块(**棋盘生成 修订义务 + 战斗结算×3 + HUD框架×1 强制项**;标错惩罚 OQ1 在位——/design-review 核对项)。**下一步 = 敌人系统(设计顺序第 7 位,MVP)**——硬输入:增益状态 单敌假设回验(定多敌即触发 target_ref 接口漂移补列)、ambush 量纲定稿义务(registry anchor:"+3" 单位候选"每颗已开雷伤害加成",联动 行动经济 F1 条款 3 上限表值)、EnemyTable 回填(数据配置 占位:词条/意图可见性字段) +12. **敌人系统 已收口(Designed,2026-09-09)**:GDD 全节已写入(`design/archive/gdd-2026-09-14/b2-enemy-system.md`,12 节零占位;lean 门禁 systems-designer〔Formulas F1–F4〕/qa-lead〔AC 17 条 + 覆盖矩阵 + 缺口预检 7 项〕已咨询;CD-GDD-ALIGN 按 lean 跳过)。定稿要点:多敌模型(1–3 同敌种,增益状态 单敌假设回验不成立→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 六词条;数据配置 三处回写(Core Rules 表 EnemyTable/HeroTable 行 + Dependencies 行 + Status);索引 敌人系统 进度行 + 门禁增补记录 敌人系统 块(**战斗结算×6/HUD框架×4 义务清单——奖励道具经济/战斗结算/HUD框架 GDD 门禁评审缺项即 blocking**)。**下一步 = 奖励与道具经济(设计顺序第 8 位,MVP)**——硬输入:行动经济 F1 条款 2 锚(weaken 档位严格 > 最低档安全格 buff 当量)+ 地形变异 三条定价硬约束、奖励身份预读(增益状态 义务)、伤害道具默认目标消费(敌人系统 固定顺位)、HeroTable.atk 激活裁决参与、高光兑现节点条目化(战斗结算/奖励道具经济 共担)、clear_reward 源授予路径确认(增益状态 OQ4) + + +13. **奖励道具经济 已收口(Designed,2026-09-09)**:GDD 全节已写入(`design/archive/gdd-2026-09-14/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 死行、行动经济 条款 2 锚参照迁移 defense_up)· F5 保底草稿重 roll · E 节两项拍板(照常消耗 / 清图槽允许 buff 行)。registry 已登记 reward_table + 三处漂移改写;数据配置 六处回写;索引 奖励道具经济 进度行 + 门禁增补记录 奖励道具经济 块(**战斗结算×6/HUD框架×3 义务清单缺项即 blocking**;缺口预检 2 阻塞级 deferral 至 战斗结算)+ 分层行 奖励道具经济←数据配置 + 增益状态 块 EC8 错号修正。**下一步 = 战斗结算规则(设计顺序第 9 位,MVP 瓶颈系统)**——奖励道具经济 定稿解锁:胜利路径先决已备(敌人系统 Overview 声明的解药);硬输入:战斗结算 义务清单累计 **增益状态×8 + 敌人系统×6 + 排雷玩法×3+2 + 奖励道具经济×6**(全缺项即 blocking)、weaken 锚红警裁决(增益状态 OQ1 归一口径前置)、行动经济 F1 条款①–⑤闭合 owner、地形变异 硬约束②③对账、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) +14. **战斗结算 已收口(Designed,2026-09-10)**:GDD 全节已写入(`design/archive/gdd-2026-09-14/b1-settlement.md`,12 节零占位,628 行——项目最大 GDD;lean 门禁 systems-designer〔Formulas F0–F5 + 对账义务清单〕/qa-lead〔AC 43 条 + 覆盖矩阵 + 缺口预检 12 项〕已咨询;CD-GDD-ALIGN/art-director 按 lean 跳过留人工复核注记)。定稿要点:**编排唯一性**(敌人系统/增益状态/奖励道具经济 战斗域 API 唯一调用方 + 可回放步骤流〔结算禁 await——数据配置 AC-15 兑现落点 AC-33〕+ 零随机)· **结算触发 = Waiting 相位编排点**(不依赖事件到达序——排雷玩法 义务①)· **段序骨架**(授予→窗口〔授予段后〕→增益/减益→道具→战斗→衰减→判定——奖励道具经济 义务①原文)· **23 条上游义务全落位**(增益状态×8/敌人系统×6/排雷玩法×3/奖励道具经济×6,AC 覆盖矩阵 23/23,引用带清单限定词)· **F0 归一口径裁决**(瞬时当量;weaken −25→值域 [60,85]/默认 −65——增益状态 红警关闭)· **条款①③⑤闭合首版验算**(条款③默认通过、满配 unit_count=3 破线→难度曲线与Boss 约束;条款⑤两态通过+保守地板缺口登记;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 战斗结算 进度行 + 门禁增补记录 战斗结算 块(**HUD框架×3 义务清单缺项即 blocking** + 难度曲线与Boss 消费注记 + UX Flag + ADR 裁决项×2);数据配置 SettlementTable 行回填。**下一步 = HUD框架(设计顺序第 10 位,MVP)**——硬输入:全项目最大呈现义务台账(排雷玩法×6/敌人系统×4/增益状态×4/奖励道具经济×5/**战斗结算×3**)+ art-bible §1–4 创建门禁(最晚 HUD框架 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 开工。 +15. **垂直切片启动(2026-09-10,/vertical-slice Phase 3 检查点)**:概念名 **dungeon-sweeper**,目录 `prototypes/dungeon-sweeper-vertical-slice/`(用户已确认开工)。**验证问题**:玩家从零开始一场战斗,能否无指导 10 分钟内经历完整「开战→挖掘规划→轮末结算逐行兑现→胜/负」循环并逐行归因(P2)——以及能否按 GDD 约束(表值单点/RNG 封闭/结算可回放/Core headless)5 个工作日内以代表性质量建完。**范围内系统**:数据配置(9 表)· 棋盘生成(含 single_subset 求解器 + 双判据验收,用户拍板完整验收)· 排雷玩法 · 行动经济 · 地形变异(迷雾)· 战斗结算 七段编排全量 · 敌人系统 双敌种 · 增益状态 四行 · 奖励道具经济 install 预分配+窗口 · HUD框架-lite 程序化呈现(art-bible 明亮风)· encounter 直供两场(L1×grub / L2 雾×ambusher)。**裁剪**:辅助道具/关卡节点地图–元进度/战斗表现 动画/存档序列化–设置与本地化/棋盘生成 黄金向量与 F4 批量工具/gdUnit/标错惩罚/多战连续。**架构基线**:docs/architecture 不存在——CLAUDE.md 约定 + 数据配置 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(2026-09-10)**:上下文加载(9 份 GDD + registry + 原型)→ 范围确认(用户拍板全量范围 + 棋盘生成 完整验收)→ 全部核心代码一次性落成(数据配置×9 表 + ConfigDB + RngService、棋盘生成 求解器与五阶段管线、排雷玩法/行动经济/战斗结算–奖励道具经济、HUD框架-lite 呈现、冒烟框架)→ headless 冒烟迭代 7 轮修错后 **39/39 全绿(约 3 秒)**:表加载校验、R2 黄金向量 5/5、棋盘生成 双判据+确定性、两遭遇全战斗 3 轮终局、结算步骤流回放逐行相同。**修掉的关键 bug**:① 奖励道具经济 换图后 Retired 相位拒绝重 roll ② 排雷玩法 `_do_dig` 对闭包全量发事件(应只发 O′∖O——重复奖励行根因)+ chain_size 口径 ③ 战斗结算 轮末最后一挖事件被相位守卫丢弃 ④ GDScript 不允许 Dictionary==bool。**重大设计发现**:棋盘生成 参考行 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. **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 关卡模式共享 奖励道具经济+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 维持有效。 + **更正(同日)**:条目 16 所列"待回写 GDD/registry 清单"作废——用户已在文档会话先行完成全部回写(战斗结算/敌人系统/奖励道具经济 GDD、registry、systems-index、数据配置、game-concept、增益状态 的 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 场景测试以此为载体。 +17. **垂直切片教训 → HUD框架 GDD 设计输入(2026-09-11 登记,HUD框架 GDD 门禁评审核对项)**:切片试玩两起呈现层崩溃(窗口条目缺字段 round_index / battle_finished 发出后读已销毁的 b2.hero)暴露系统性盲区——已定稿 AC 全部覆盖 core 层,HUD框架 呈现层无测试承载,且 GDScript 编译不查字典字段与时序。**HUD框架 GDD 三条硬性要求(缺项即 blocking)**:① 每个面向呈现的信号载荷成文 schema(对齐 排雷玩法 三事件先例,字段清单入 GDD)② 场景级 headless 测试进 AC(真实实例化 UI 场景 + 驱动完整对局 + 断言零脚本错误;切片的 headless UI 冒烟为其原型)③ 信号时点状态可见性规则(销毁前捕获、载荷数据自包含、禁止呈现层回读核心生命周期状态)。另登记:gdUnit4(数据配置 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)。 +18. **战斗期望值现状核查 + 实测(2026-09-11,用户问询触发)**:文档侧——**"期望胜轮数 / 期望每轮承伤 / 期望败北轮数"无任何直接设定**;最近的三个既有数字全部过期:① 奖励道具经济 刀供给方向预检(2026-09-09:击杀需求 18/54、供给≈2|资格格|)基于旧权重与"无基础攻击"口径未重算 ② 战斗结算 F5 Δr(清图轮数 L1≈2/L2≈4)基于旧密度参考行 ③ 战斗结算 E_dig=7.2(旧权重 40/25/35)。难度曲线与Boss(此类期望的正当 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 结算。**预警⑥定量坐实:战斗长度与承伤均远低于可玩带,难度杠杆余量巨大**。登记:① 难度曲线与Boss GDD 设计输入 = "战斗期望表"(期望胜轮带/期望承伤占 max_hp 比/期望开格数/败北轮期望,按 (L,D) 曲线)为强制章节 ② 奖励道具经济 预检 + 战斗结算 E_dig/F5 重算义务随 2026-09-11 批触发 ③ smoke/monte_carlo.gd = 难度曲线与Boss 调参期战斗侧批量工具原型(对应 棋盘生成 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)。 +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 有精确行")② **关卡节点地图-lite 推进器** `core/level_runner.gd`(关卡域生命周期 owner:共享 奖励道具经济 + HP 携带 + 节点兑付 + 关卡末 level_clear + 测试面板 hero 覆盖;Core 零 UI 依赖)③ **奖励道具经济.grant_node_active()**(接口漂移登记——可选类活行 roll 一件入库,reward_roll 恰一 scope)④ **敌人系统 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% 网格扫描:通关率/承伤期望/败北节点分布——难度曲线与Boss 关卡侧批量工具原型)+ `level_debug.gd`(单关诊断)。冒烟 55/55(新增关卡 10 断言:满配通关/裸装败北/奖励点入库/恢复点公式/Dmax 解析/关卡回放确定性/hero 基线恢复)。**首版调参实测**:敌 HP 208 时基线通关率 73%——预警⑥关卡尺度坐实,v2 上调至 290 后复测中。**登记**:地形变异 迷雾 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`。 +20. **Playtest 三缺陷修复 + 奖励期望汇报(2026-09-11,用户 F5 试玩反馈批)**:**缺陷① UI 抖动**——悬停文案换行推挤侧栏布局、"第 N 轮"库存行被挤出视口;修复 = 侧栏全部动态区固定槽高 + clip_contents(区域独立、超长裁剪不回流)。**缺陷② Boss 战 AP 用完不结算(软锁)**——根因 = **排雷玩法 核心 bug(v1 即存在)**:`_do_flag_place/_do_flag_pull` 花完末点 AP 后 行动经济 翻 Waiting 但不发 round_end,战斗结算 的 Waiting 结算点依赖该事件 → 永不结算;v1 冒烟盲区根源 = 自动玩家不用旗。修复 = 旗动作补发 `_emit_round_end_flags_and_payload()`(与 `_do_dig` 同款);正式侧义务 = 排雷玩法 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 改写(接口漂移批)。 +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`(奖励道具经济 私有组合件:notify_battle_start/should_inject/notify_granted/granted_count;行值快照抗热重载;零自有随机数)· 奖励道具经济(_supply 组合/notify_battle_start 转发/roll_board 重写删 F5 循环/grant 挂钩"发放即获取")· 战斗结算(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 对冲(难度曲线与Boss 主杠杆)②压 attack_up/defense_up 相对权重 ③降 buff 幅度(触 增益状态 表值)。**正式侧义务**:奖励道具经济 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 中补一句"选择序列元素 = 窗口条目位置、非库存句柄"(防正式实现复发)。 +22. **缺陷④ 道具使用后不消失(2026-09-11 用户 playtest 反馈批)**:可用次数语义本身存在且正确(`b4.use()` 一次性标记 Used 不可逆,库存过滤 InStock)——真实根因 = **UI 层契约违例**:战斗结算 窗口选择语义 = `_window_items` 位置(义务④"选定序"),UI 选用按钮传 `item.index`(库存数组句柄);未消耗过时二者巧合相等,消耗过一件后句柄=位置+已耗数错位 → 选中件不消耗、邻件被误耗(Boss 战道具多、多次消耗后暴露)。修复 = UI 与行为探针统一传窗口位置;行为探针加"选用必生效"断言(选中件库存态必须变 Used,失效即退出码非零——种子 7/23 实测生效 1/2、失效 0)。冒烟 74/74 维持。**登记义务**:战斗结算 义务④"选定序"在 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 三探针)。 +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)。**教训登记(HUD框架 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 可演进为信号驱动。 +24. **呈现层模块化重构(2026-09-11 用户批——"上帝对象"拆分)**:用户工程师视角否决 main.gd ~1100 行上帝对象。拆为 **9 个单一职责单元**(presentation/views/ + hud_presenter.gd):BoardView(棋盘渲染+输入意图翻译,只读 排雷玩法、动作信号外发不碰核心写)· 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(冒险结构 落位时退役);HudPresenter 为静态装配器,正式 HUD框架 可演进为信号驱动。 -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)。 +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 前缀 / 头注溯源义务 / 数据配置 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%) +27. **轮内刷新方案 A 落地(2026-09-12 用户拍板批)**:用户确认刷新实现(仅 Waiting 受理)与设计预期不符(预期 = 扫雷阶段遇推理死路、有剩余 AP 且 RAP 足够即当场刷新继续挖)。**影响分析先行**(三方案对比:A 轮内不打断轮次 / B 停手+刷新组合宏 / 时间税差异点破——"刷新是否触发敌方结算"是唯一实质差异;16 代码触点 + 8 文档 + 5 数值连锁清单)→ 用户拍板 **方案 A**:Active 相位当场换图继续挖、AP 保留不重置、不触发结算、RAP 够即可、**每轮兜底上限 2 次**(表值 refresh_max_per_round)。**实现**:行动经济 `request_refresh_active()`(仅扣 RAP)· 排雷玩法 install/retire 增 `during_round` 显式通道(其余守卫不变)· 战斗结算 `can_request_refresh()` + 双相位重写 + 每轮计数 · **facts 身份快照隔离**(实现批新暴露的深坑:轮内换图后旧图已挖未结算格与新图坐标重叠,轮末 grant 错拿新图身份——解法 = 缓冲期 `query_full_snapshot` 冻结身份、授予直通快照绕过现行 mapping/_granted)· 奖励道具经济 `query_full_snapshot()` · HUD 按钮"弃图刷新(RAP -8 · 每轮至多 2 次)"。**验证**:冒烟 88/88(+14:当场换图/相位 AP 保留/RAP 扣减/上限拒绝/跨图 facts 完整兑现〔4 格跨三图〕/回放确定性)· 三探针全 PASS · 期望扫描持平(模拟策略不刷图——真人专用出口,playtest 观测项)。**调试教训**:蛆虫 HP 24(已调)误判 18 未掉血——排查先核对当前表值;GDScript lambda 对局部 int 按值捕获(smoke 计数器改容器)。**正式侧义务**:行动经济/战斗结算/奖励道具经济 GDD + game-concept 层级措辞 + registry 修订清单见切片 README"2026-09-12 轮内刷新"节;数值连锁(清图支配性条款⑤/真人刷图行为获取期望)待 playtest 实测。 +28. **Task: Designing 难度曲线与Boss GDD**(2026-09-12 启动,/design-system --review solo)· File: `design/archive/gdd-2026-09-14/c3-difficulty-curve.md` · 上下文摘要已呈现(上游 数据配置/敌人系统/战斗结算 全 Approved;锁定事实:clause3_supply 满配破线约束、战斗结算 条款⑤未闭合约束包、棋盘生成 Boss=Dmax 约定 + OQ6、地形变异 scope=global 语义位、奖励道具经济 全部 provisional 值归 难度曲线与Boss;切片实测现状:敌深度轴 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 批)编号并列,编号冲突系并行会话所致,内容不冲突。 +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 的 难度曲线与Boss GDD 设计会话并行中——ADR-0001/0002 落定结果与 难度曲线与Boss 无接口冲突(难度曲线与Boss 消费 战斗结算 对账输出为文档层事实依赖)。 → **GDD 完成(2026-09-12 同会话)**:12 节全部写入(用户全权委托——"所有选择按推荐",逐节问询按用户指示跳过;solo 模式专员代理跳过已在各节登记)。关键决策:① 承载结构 = 直填行值主体 + DifficultyTable 修饰结构位(MVP 空表,敌人系统 Rule 10 兑现)② 切片 SLICE-TUNED 行集转正 MVP 默认曲线 ③ 难度期望表(通关率目标带 L1 基线 40–60%/供给平衡带 [1.1,2.0]——F5/F6)④ F1–F7 全部为设计期工具式、运行时零新增公式 ⑤ 战斗结算 条款③⑤/预警⑥约束承接:条款⑤ MVP 接受非支配设计态(C 量级不上调,OQ4 playtest 观测弃图率)⑥ 地形变异 scope=global 语义定稿(仅 Boss/精英图,MVP 不产生行)⑦ Boss 血量占比 0.81 越带如实登记 OQ2。registry +3 条目(difficulty_table/difficulty_expectation_table/boss_dmax_convention)· systems-index 难度曲线与Boss → Designed。**注意:未经 /design-review——solo 出稿待人工复核,独立评审须新会话 `/design-review design/archive/gdd-2026-09-14/c3-difficulty-curve.md`**。 +29. **难度曲线与Boss GDD 落地切片 + 试玩采集清单(2026-09-12)**:用户拍板跳过 /design-review(solo 出稿待复核状态维持),难度曲线与Boss 设计承诺直接落回切片试玩。落地:**DifficultyTable 结构位**(schema 域校验+越域 warning / 空表 difficulty.tres / ConfigDB 挂载 + resolve_difficulty_modifiers(精确匹配+clamp)/ 敌人系统.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 + 战斗结算 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 迁移登记为 HUD框架 候选工作项(三探针改造 + 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(数据配置 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 的采集清单)+ 难度曲线与Boss OQ 裁决 + 迭代期反馈。注:本条目与会话内另一条目 29(难度曲线与Boss 批)编号并列,编号冲突系并行会话所致,内容不冲突。 -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 试玩采集。 +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 冒烟统一入口,数据配置 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 试玩采集。 +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 待 HUD框架 GDD/ADR-0011)② **ADR-0005 `save-contract-and-schema-versioning`**(新建 Accepted——SCHEMA_VERSION 比较归属 存档序列化 + 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 门另需:HUD框架 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)。 +30. **试玩日志器 + 诊断转储(2026-09-12 用户需求批——playtest 数据采集基建)**:① 战斗结算 新增 `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 评估(报告第三节)。 +34. **战斗屏 UX 规格产出(2026-09-12,/ux-design battle,自主模式用户委托"缺失补充")**:`design/ux/battle.md` 已写入(Status: Complete 待 /ux-review;14 节 + 上游义务对照表)。**最大决策 = 常驻双舞台分屏**(用户布局图拍板):战斗画面(252px 顶区)+ 角色信息(240px 左下)+ 扫雷图(中下 ~760px)+ 道具(280px 右下)+ 顶栏关卡进度条(1280×800 基座)——**对 战斗表现"双舞台陷阱"的裁决输入 = 同屏双舞台第三形态**(非独立战斗场景),战斗表现 前战斗表现美术测试按此布局出样。关键裁决:道具窗口 = 右栏原位激活(非中央弹窗);移动路径预览 = 合法性指示(排雷玩法×6⑤ 悬案裁决,穿行动画留痕不采纳);结算明细带常驻战斗画面底部(P2 逐行归因,点击展开回看);状态效果空态 = 槽隐藏。**全部 HUD框架 呈现义务逐条落位**(排雷玩法×6/行动经济/地形变异×3/战斗结算×3〔UX Flag 结算屏+窗口〕/敌人系统×4/增益状态×4/奖励道具经济×5 + 棋盘生成 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(新会话)→ HUD框架 GDD(设计顺序第 10 位,MVP 最后一块,以本 spec 为呈现义务承载依据)→ /review-all-gdds → /gate-check**。**同日修订(用户反馈拍板)**:道具来源不呈现——库存列表逐件描述由"来源格/轮"改为**属性简述**(行值快照投影派生:伤害/回复/增益一句话;tooltip=完整描述+触发注记,不含来源);来源数据仍在 奖励道具经济 窗口载荷契约中(不改数据层),归因职责由结算明细行的逐行来源标注承载(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。难度曲线与Boss 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 区间掷骰,不触 敌人系统 行集锁定);后置 = 多波战斗(战斗结算 波次表)/遗物协同(先设计协同再提密度)/战后回复(绑定敌 atk 下调)。用户决策点 = 是否本切片做遭遇随机化。 +33. **遭遇随机化落地(2026-09-12 用户拍板——Peglin 对比报告结论 2 的本切片项)**:报告 `design/research/peglin-comparison-2026-09-12.md`(三根因:构筑密度/零随机/资源模型;数值不可移植只可比结构比例)。**用户拍板先做遭遇随机化**。难度曲线与Boss GDD 增补 Core Rule 8(unit_count 区间 [unit_count, unit_count_max] 均匀骰 + encounter 流 + 敌人系统 零随机纪律保持 + 教学位不加骰)。实现: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 教学位固定)/ 战斗结算.begin_battle 掷骰(erow 预解析 → override 传 spawn)/ 敌人系统.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) +35. **战斗屏 UX 批:评审通过 + pattern 库建立(2026-09-12,/team-ui 管线 solo 模式)**:① **上游契约批落地**——battle.md /ux-review(7 blocking)+ 用户四拍板(弃图刷新=挖掘期当场换图·雷图右上角按钮 / 保留每轮上限 2 / OQ8 本 spec 裁决采纳 tint+雾区全开信号 / 压暗改去饱和+金橙偏移)→ 行动经济/排雷玩法/战斗结算/systems-index/registry 直接修订(refresh 前移挖掘期:行动经济 Active 受理+cap 2、排雷玩法 Active·仅刷新路径装板、战斗结算 `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` 结构位)——语义一致,正式 HUD框架 批统一命名 ② **/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——快照驱动,呈现层只订 战斗结算)。模态统一登记序管理(缺陷⑤纪律),旧 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 六种子)。占位登记:意图映射/演出/字体(HUD框架 正式化)归 战斗表现/HUD框架 批。 +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 注释,难度曲线与Boss 按 F6 复算)。**验证**:冒烟 161/161(+12 专项断言:护甲三档传播/矩阵表值/三分量字段/flag_miss+反噬行/充能进攻击/派生回放)· ui_probe/tune_probe/click_probe 全 PASS。**实现坑**:enemy_card_view set_intent 引用越作用域变量 d → 类编译失败敌人卡零构建(ui_probe 抓获)——改成员变量 _armor 传递。**正式侧义务(GDD/registry 修订待文档批)**:敌人系统(armor_type)/战斗结算(三分量构成+反噬编排+矩阵载体)/增益状态(六行状态表+flag_miss 授予)/排雷玩法 OQ1(惩罚候选升级三选一+重复性=每雷实例一次)/registry(enemy/settlement/status_effect 条目+行动经济 条款②③锚重推导)/难度曲线与Boss(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 转发信号契约头注。 +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 直传 敌人系统 原键,渲染映射唯一定义处归 EnemyCardView ③ **C 区 RAP 死分支**(presenter 从不投影 a3.rap,行动经济对 HUD框架 的清零反馈不可达)——持续投影 + 视图改 `_last_rap` 成员存储 ④ **smoke_all 恒真断言**改带标签真实断言 ⑤ **level_runner core↔core 接线 lambda ×4 → 方法 Callable**(ADR-0001 S6)⑥ **战斗结算 `diagnostic_snapshot()` 诊断观测面**(main/click_probe 零私有字段直读)⑦ **main.gd 761→667**:节点构建/模态层级/叙事文案/模拟编排四拆分(ui_assembler + modal_stack + battle_narrator + tune_sim_controller,均无 class_name 走 preload;.uid 已生成)——剩余为 ADR 规定组合根职责,进一步压缩登记 HUD框架 批候审。探针同步: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)。 +39. **封装收口批落地(2026-09-13,architecture-review 改编批 → 用户拍板 P1+P2+P3 全修)**:代码架构审查报告 `docs/architecture/code-architecture-review-2026-09-13.md`(Verdict: CONCERNS——分层纪律 S1–S7 全合规,封装欠账 = 核心状态公有 + 呈现层链式穿透)。本批清偿:**E1** 战斗结算 协作对象 a1/b2/b3/b4 私有化 + `presentation_snapshot()/is_battle_live()/stock_count()/query_reward_identity()` 成文呈现面(a2/a3 保留公有 = API Boundaries 登记的 HUD框架 契约面);**E2** 排雷玩法 board/revealed/flags 私有化 + `current_board()/reveal_record()`(board_view 直读 `.revealed` 的自相矛盾收口);**E3** 敌人系统 hero/enemies 私有化 + `hero_unit()/all_enemies()` 投影(presenter 死亡残位需求收进 API);**E5** 行动经济/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 / 奖励道具经济 返回值统一 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 表值参数化(敌人系统.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 正式化批候审)。 +40. **P2 收口批落地(2026-09-13,用户拍板两项均做——封装审查 P2 设计稿批准)**:① **E4 表值参数化**:db.hero 表写通道全删(main `_apply_hero_override`/runner `start_level` 写表),加成经 encounter 显式参数(战斗结算 begin_battle +hero_atk/hero_max_hp 字段 → 敌人系统.spawn +atk_override/max_hp_override 参数,-1 表值兜底)——双写叠加 6→18→54 与双基线同步整类隐患移除,数据配置 单一事实源机制化;registry 新增 `hero_override_parameter_passing`(ADR-0009 候选,敌人系统.spawn/战斗结算 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。**遗留**:正式侧 战斗结算/敌人系统 GDD 修订(随 ADR-0009);main.gd 400 行目标受 ADR-0001 组合根职责约束(HUD框架 正式化批候审)。 -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 十系统全收口)**。 +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 图标键契约冻结(效果图标 增益状态 数据六行 + sapped 提案键 / 奖励徽章 奖励道具经济 默认根八行 + clear_blast 并列预留 / 格面类型图标五类〔提案 R1,未开格预读徽章墨族单色防色相泄漏〕/ 敌种线索键 敌人系统 Rule 8 契约化〔行为拍姿态/环长直觉/强度档位/预告时序/恒定义务〕——键与通道分配冻结,终稿美术归样张与 asset-spec 批);⑤ 雾格资产规格一行占位(内容封禁期暂缓,雾重设计批再启)。registry terminology 增三词条(图标键/徽章/极性)+ referenced_by 增 art-bible;systems-index 新增 2026-09-13 art-bible 登记块(范围声明:§5–9 未建不阻塞 HUD框架,HUD框架 消费面五节点名)。AD 门禁 solo 跳过,出稿待人工复核。**冲突清单(不改 GDD/提案,列示待裁决)**:① game-concept 视觉锚原则 1"暗色地牢底色" vs art-bible 明亮底色反转(art-bible §1.3 2026-09-10 用户定稿在案;概念侧注记待概念层修订批);② 敌人系统 GDD 敌种键 {grub, ambusher} vs 切片 DND 更名 {goblin, bugbear, ogre_chief}(2026-09-12 consistency-check ⚠️ 在案;图标键契约以 GDD 键集为锚、声明键集事实源归 敌人系统/难度曲线与Boss);③ 奖励道具经济 GDD 默认根八行(clear_knife)vs 切片供给控制器批(clear_blast + 三可选类移 active_inject——entry 21 正式侧义务待清偿;徽章键按 reward_id 冻结、池归属无关);④ 极性提案 §3.5 跨文档回写清单全部待实施批(增益状态 四行→七行、奖励道具经济 schema 扩列、排雷玩法 OQ1 关闭等)。**下一步 = HUD框架 design-review(HUD框架 GDD 已于 2026-09-13 solo 出稿并完成 lean 迁移,状态 Designed 未经评审——新会话跑 `/design-review design/archive/gdd-2026-09-14/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. **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 回执写现行的语义统一**:增益状态(weaken −65、D=1)、数据配置(八字段/p_perfect {100,200}/category 五值/ActionEconomyTable 现行值)、战斗结算(EC-9 单件制)。**🔴 遗留重算义务**:战斗结算 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 批),锚腿读数须 战斗结算/难度曲线与Boss 对账重跑(非格式批可改)。迁移代理另有 6 处保守处理待人工复核(奖励道具经济 AC-19 对账对、难度曲线与Boss 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 回撤/防御权重回升/供给联动,归 难度曲线与Boss F7);②墙语义候选改法(clamp 收紧/乘法衰减/重击拍穿防——/propagate-design-change + 增益状态 GDD 文字同步);③供给量语义重拍板(每战斗 vs 关卡域首件保证——难度曲线与Boss);④浅深度首轮跳战可玩性(难度曲线与Boss)。切片 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. **HUD框架 已收口(Designed,2026-09-13,/design-system solo 模式一次会话定稿)**:GDD 全节已写入(`design/archive/gdd-2026-09-14/d1-hud-framework.md`,12 节 + 台账表/冲突清单零占位,25 条 AC;**专员/总监代理全部跳过,出稿待人工复核;未经 /design-review,独立评审须新会话**)。定稿要点:① **呈现义务台账 27 项逐项落位**(排雷玩法×6/敌人系统×4/增益状态×4/奖励道具经济×5/战斗结算×3/地形变异×3/行动经济+棋盘生成 EC5——Dependencies 落位表;battle.md §13 承接升格系统级验收,已裁决项全部直接继承)② **切片教训三条硬性要求兑现**(entry 17:载荷 schema 13 项成文表 Core Rule 17 / 场景级 headless 探针 AC-19 / 信号时点可见性规则 Core Rule 18 + AC-20)③ 订阅纪律按 ADR-0001 收窄——呈现层只订 战斗结算 步骤流/快照与 runner,零事件外发面 = **命令式回执驱动**五面成文(registry `hud_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)⑦ HUD框架 裁决:MVP 格面徽章只在已开格呈现(奖励道具经济 Rule 8 承接——冲突⑤)⑧ 发现冲突清单 ⑧ 项成文(不改已定稿正文与提案,含 game-concept 底色/敌人系统 敌种键/clear_blast/极性提案批/订阅表述/行动经济 刷新计数读数/战斗结算 settlement_snapshot 契约〔entry 41 并行批仍在切片使用该信号——OQ3 登记更增补登记必要性〕)。registry +1 条目 + terminology 两词条;systems-index HUD框架 进度行 + 门禁增补记录块(接口漂移登记 ×3)。**下一步 =(新会话)`/design-review design/archive/gdd-2026-09-14/d1-hud-framework.md` → `/review-all-gdds` → `/gate-check`**(Systems Design → Technical Setup 门两件 = HUD框架 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。 +40. **辅助道具 GDD 定稿(2026-09-14,/design-system solo 模式一次会话——用户设计指令"每个维度都要有对应道具,稀有道具尝试高数值和多维度结合")**:`design/archive/gdd-2026-09-14/a5-minesweeper-aux-items.md`(lean 格式,lint PASS 表格占比 84%)· registry 登记 `aux_items` 条目 + terminology 四词条(叠加层/真视窄域护栏/渗透率)+ `variation_table.counter_item_ids` 回填 [probe_dust](地形变异 预授权 辅助道具-1 义务闭环)· systems-index 门禁增补块 + 辅助道具 状态 Designed。**设计要点**:五维度框架 {info, risk, action, plan, counter} + F2 覆盖谓词("每维度都有对应道具"= 加载后内容审计断言,机器可查)· 承重护栏 = 真视窄域(替代推理 ≤3×3、恢复推理可全图——P1 防塌方,schema 校验强制)· MVP 六行 = 四常规单维(显影尘/工兵令/探宝罗盘/探巢摆钟,t2)+ 两稀有多维高数值(地穴全图卷 = 全雾显影×全图预读、爆破工兵包 = +4 AP×3×3 真视,t3、≈0.17 件/板)· 获取挂 奖励道具经济(aux_item 预留枚举激活——义务 奖励道具经济×①…⑤)· 使用经 战斗结算 挖掘期编排(use_aux 规范序 validate→apply/grant→consume 序即原子——义务 战斗结算×①②③)· 板级叠加层三集(零特判延伸——排雷玩法 代码零 辅助道具 分支、零回写 排雷玩法/奖励道具经济、随图消亡)· 行动经济 grant_ap 第 9 API 接口漂移预授权(义务 行动经济×①)· 21 项跨 GDD 义务清单 + F1–F3 + 14 AC;地形变异 EC6/OQ2 与 奖励道具经济 OQ2 收口。**生效前提**:奖励道具经济/数据配置/行动经济 修订批兑现前 aux 行入池会加载致命(奖励道具经济 Rule 1 预留值拒绝)——AC-12/13 deferral 已登记。**待办**:/design-review(新会话)→ 对侧修订批(奖励道具经济/数据配置/行动经济/战斗结算/HUD框架)→ UX Flag(/ux-design Phase 4 前置);设计顺序下一站 = 难度曲线与Boss 复核批/冒险结构。 ## 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 回写批半途落地致两代理结论互斥)——批收口前勿多会话并行改设计文档 +- **Verdict: FAIL**——2 blocking(① 增益状态/战斗结算:defense_up 减伤墙批 −30 未回写 owner、战斗结算 锚算术三重漂移致条款③⑤闭合回执失效;② 数据配置 漂移批五处:encounter 流/pool_tag active_inject/active_p0·decay/k_dmg 禁区/clear_blast 行集+HelperItemTable 更名)+ ⚠️×16 挂各修订批;增益状态/战斗结算/数据配置 已标 Needs Revision。报告:`design/archive/gdd-2026-09-14/gdd-cross-review-2026-09-14.md` +- **同日 辅助道具**:/design-review --solo 3 blocking + 5 recommended 核销,用户拍板两项边缘规则(area_3x3 边缘窗口裁剪 / 零信息目标不可用不消耗)后免复审标 **Approved**;日志 `design/archive/gdd-2026-09-14/reviews/a5-minesweeper-aux-items-review-log.md` +- **下一步**:新会话按报告 §七 批调度跑回写批(数据配置/增益状态 → 战斗结算 重算 → 奖励道具经济 规格 → 难度曲线与Boss 收口 → 零散)→ `/review-all-gdds since-last-review` 增量复核 → PASS 再 `/create-architecture`;⚠️ 评审期间检测到并行会话修改 GDD(难度曲线与Boss 回写批半途落地致两代理结论互斥)——批收口前勿多会话并行改设计文档 -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 头部。 +43. **冒险结构 GDD 定稿(2026-09-14,/design-system solo 模式一次会话定稿,与 /review-all-gdds 并行——冒险结构 为新增文件零冲突)**:`design/archive/gdd-2026-09-14/c2-adventure-structure.md`(lean 格式,lint PASS 表格占比 86%)· registry 新增 `level_table` / `adventure_table` 两条件目 + 补登 `hero_override_parameter_passing`(切片 E4 批实现但 registry 缺登——缺口闭合,owner=战斗结算,ADR-0009 候选状态注记)+ terminology 三词条(HP 账本/关卡收束点/关卡大奖励)· systems-index 门禁增补块 + 冒险结构 状态 Designed。**冒险结构 待决四问收口**:① 关卡序列 = MVP N=2 线性(L1 → L2 雾解锁台阶),冒险通关 = L2 Boss;加关 = 内容批,N=2 生效前提 = 难度曲线与Boss L2 深度轴行值填充批(未落地前 N=1 合法态)② 大奖励 = 上限 +10 / 血回满 / 代币 +10 三件固定(不进库存域、末关不兑付;血回满与 难度曲线与Boss 满血开局校准口径闭合——F1 冒险通关率合成 [6,20]% 的前提)③ 失败重开 = 死亡即冒险终、重开全新种子、放弃同失败型(MVP 无 UI 入口)④ 关卡节点地图/冒险结构 边界 = 收束点在 关卡节点地图(先调 战斗结算 level_clear 再宣布 level_end),接口契约四件套(start_level / report_battle_end / heal_clamped / level_end)为 关卡节点地图 GDD 强制输入。**切片先例切分转正**:level_runner 冒险域归 冒险结构、关内行进归 关卡节点地图;两处切片简化改写(逐关播种 → 冒险单种子;每关 HP 回满 → 跨关延续)。**下游义务**:难度曲线与Boss×1(L2 行值——生效前提)· 数据配置×2(reseed 入口 / AdventureTable 建表)· 战斗结算×1(begin_battle 入参供给方转正)· 元进度×2(远)。**下一步 =(新会话)/design-review design/archive/gdd-2026-09-14/c2-adventure-structure.md → 关卡节点地图(设计顺序第 14 位,以 冒险结构 接口契约四件套为强制输入)**;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 完美策略档测量批」。 +44. **完美策略档测量批(2026-09-14,用户拍板"增加完美策略档测试验证"——OQ7 技能轴上界仪,零玩法数值变更)**:auto_player 增 single+subset 完美求解器(零盲挖/全标旗/卡死刷新,只读 排雷玩法 查询面、去 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 同批登记。 +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 快照口径不变,战斗结算/呈现零改动)+ 第二步敌方 atk 区间(六行 ±3,遭遇骰同流 spawn 期整战持久——每拍抖动否决〔数字可读性锚〕,敌人系统 零随机保持,spawn +1 参数接口漂移登记);中点恒等现值 = 行动经济 条款①锚不漂移;schema 双批校验(成对/中点锚/buff 禁止)+ smoke `_smoke_power_interval` 4 断言;冒烟 **154/154**。**实测(N=30)**:完美档 0 加成仍 **100%**(承伤 3.5→3.9——方差真实流动被 47/50 血缓冲全吞);基线档 17% 不变——**数值区间杠杆族对 ≤40% 线无效已坐实**(完美玩家结构性优势 = 零踩雷直伤 + weaken 顶格 + 快杀)。**待用户三选裁决(难度曲线与Boss OQ7 主项)**:①承伤硬地板(战斗结算/敌人系统)②weaken 饱和语义(增益状态/战斗结算——拆全标旗基石)③极端宽度(建议否决——毁基线与可读性);区间机制保留(普通玩家方差体验有效)。registry reward_table/enemy 条目 + clause5 义务 + 难度曲线与Boss 行值总表/F5/OQ7 同批登记。 46. **GDD 体系重构(2026-09-14,用户拍板批)**:旧 GDD 体系整体封存为 `design/archive/gdd-2026-09-14/`(只读参考、不作事实引用,两个 legacy 子目录不保留);`design/gdd/` 原位重建循环式新骨架——6 份顶层文档 + `systems/` 四循环(排雷/战斗/冒险/表现与支撑)20 份子系统占位文档,根索引 = `02-系统清单.md`(文档增删必须同步)。雾格已从设计中移除,地形变异未来范围只覆盖非雾变异(待定)。 47. **新骨架 00–05 成文 + 全仓旧路径清偿(2026-09-14,用户逐份审阅批)**:00 游戏概念 / 01 核心循环 / 03 内容规模 / 04 美术音频方向 / 05 不做清单 成文(子 agent 起草、用户审阅定稿,02 索引已随骨架建立);元进度外层循环补入 01——元进度资源定名**钥石**(关底 Boss 看守的区域核心,击杀取走、局外解锁永久提升,用户拍板;registry 登记随元进度设计批补)。**旧路径清偿**:CLAUDE.md 头部导航与 Project Layout 改指新骨架 + 新增「新骨架过渡口径」条款(lean/lint/两件套纪律仅约束归档,新骨架纪律 = 可读性优先 + 索引同步 + 不建 analysis);registry owner ×33 与切片代码头注 ×17 改指归档路径;gdd_lint PostToolUse 钩子停用(工具留 `tools/` 备查)。**已知待办**:registry/数据表雾残留清理批(variation.tres fog 行 / board_gen L2 锚行雾参数 / 显影尘·地穴全图卷 fog_reveal / L2 层名"雾漫巢径");registry status_effect.mvp_content 缺暴走/失衡两行登记。 48. **文档写作规范成文 + 引用纪律补入(2026-09-14)**:`docs/doc-conventions.md` v1 成文(另一会话,整合全部写作纪律为写文档前必读的单一权威),本会话补第 7 节「引用纪律」(单点定义/规范名唯一/引用不转述/反向边登记/删除概念 SOP,雾格事故为对照反例);CLAUDE.md 挂必读指针与摘要。同批:`systems/排雷循环/行动经济.md`(自归档迁移定稿)与 `systems/表现与支撑/教学引导.md`(范围草案)成文(另一会话)。 +49. **命名清偿批(2026-09-15,用户拍板批)**:旧系统代号(a1–d6)全仓清偿为中文规范名——design/gdd(02-系统清单新增「中文规范名↔英文代号↔旧代号」对照表)/ux/art/research 全绿复扫;registry owner 改指新骨架文档 + 前缀键名改领域命名(minesweeper_/aux_/hud_/settlement_/reward_)+ 头部注记;本文件历史条目同步清偿。QA 四份 Closed BUG 记录删除(用户拍板);切片代码变量领域命名批(子 agent 执行,smoke/探针回归见批报);归档维持只读、后续随批删除。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/README.md b/prototypes/dungeon-sweeper-vertical-slice/README.md index f733e64b..bb6ae66d 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/README.md +++ b/prototypes/dungeon-sweeper-vertical-slice/README.md @@ -54,7 +54,7 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/s - **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) +- **裁剪不变**:A5 / C1 路径分支选择(用户拍板线性)· C2–C4 / B5 战斗动画 / D3–D6 / A1 黄金向量与 F4 批量工具 / gdUnit · 注:标错惩罚(原 A2 OQ1)已于 2026-09-12 批上线(标空旗三选一确定性反噬:暴走 / 伏击 +2 层 / 失衡,坐标×板序号派生、不走随机流),本批(2026-09-15)已收编进现行文档,不再属裁剪项 ## 2026-09-11 可选类供给控制器(用户拍板批——修改记录) @@ -95,6 +95,10 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/s **正式侧义务(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-15 补记(用户拍板)**:**Waiting(等待结算)相位刷新通道整体移除**——呈现层入口此前已按 OQ12 退役(2026-09-12 战斗屏批),本批拆除核心层残留:账本侧 Waiting 受理入口 `request_refresh()` 删除、编排侧 `can_request_refresh` 不再放行 Waiting、`player_request_refresh` 等待期登记意图分支删除、出口分发删除 `_refresh_used_this_waiting → new_board` 换图分支(非清图轮恒同图)、字段与诊断快照键同步移除。文档侧无需修订——[行动经济](../../design/gdd/systems/排雷循环/行动经济.md)只写挖掘期刷新、[战斗结算](../../design/gdd/systems/战斗循环/战斗结算.md)明文"非清图轮恒同图",拆完代码即双双收敛。冒烟断言改为"等待期刷新请求被拒且状态不变"。 + +**同批其它改动(口径读法)**:明细行协议 **13 类 → 15 类**(新增窗口打开锚行 `window_open`、窗口跳过锚行 `window_skip`,服务"窗口段恒在案"验收;单点定义见[战斗结算](../../design/gdd/systems/战斗循环/战斗结算.md));状态表 **四行 → 六行**(暴走 / 失衡两行随标错反噬批收编);达标判定回归**单一判据**(可推理率 ≥ 及格线);求解器禁读雷位真值。本文正文的 v1/v2 范围登记与各历史批条目保留当时陈述,计数以现行文档与代码为准。 + ## 2026-09-11 关卡扩展批 · 切片级偏差登记(实现期决策,正式化时须回 GDD/ADR) 1. **C1 线性推进器 provisional**:节点链来自 LevelTable(结构位 = 未来 C1 的数据承载面);路径分支/奖励点分布定稿权归 C1/C3。节点类型 closed enum {battle, reward, rest, boss};末节点必须 Boss(LevelTable schema 校验)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd index ac7020b9..38cf588f 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd @@ -1,13 +1,13 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# A3 行动经济:被动账本(AP 硬重置 / 统一价目 / 三路径结束仲裁 cleared > stopped > ap_exhausted / +# 行动经济:被动账本(AP 硬重置 / 统一价目 / 三路径结束仲裁 cleared > stopped > ap_exhausted / # RAP 池战斗域 / 双发射源之 stopped 直发)。相位域 = {Idle, Active, Waiting}。 -# 实现 GDD:design/archive/gdd-2026-09-14/a3-action-economy.md(系统 ID A3——registry 追溯锚,命名规范见 CLAUDE.md) +# 实现 GDD:design/archive/gdd-2026-09-14/a3-action-economy.md(系统 ID A3·行动经济——registry 追溯锚,命名规范见 CLAUDE.md) class_name ActionEconomy extends RefCounted -signal round_end_stopped(payload: Dictionary) # 双发射源之停手路径(A3 直发;payload 与 A2 棋盘路径同构) +signal round_end_stopped(payload: Dictionary) # 双发射源之停手路径(行动经济 直发;payload 与 排雷玩法 棋盘路径同构) const PHASE_IDLE := "Idle" const PHASE_ACTIVE := "Active" @@ -25,7 +25,7 @@ var ap: int: 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 = {} # {path_tag, continuation_hint}——排雷玩法 组包 round_end 的合法数据源 var last_round_end: Dictionary: get: return _last_round_end var _settlement_consumed := false # AC-14 门:notify_settlement_done 消费后才受理 round_start @@ -53,7 +53,7 @@ func _cost(kind: String) -> int: return 0 -# 通用扣减(A2 用于旗动作;挖握走 request_dig——cleared 仲裁需要 clears_map 参数) +# 通用扣减(排雷玩法 用于旗动作;挖握走 request_dig——cleared 仲裁需要 clears_map 参数) func try_spend(action_kind: String) -> bool: if phase != PHASE_ACTIVE: _reject("phase") @@ -61,7 +61,7 @@ func try_spend(action_kind: String) -> bool: return _spend_and_arbitrate(action_kind, false) -# A2 挖掘专用:chain_size = 连锁附加格数(不含首格);clears_map = 本次动作后全图安全格开完 +# 排雷玩法 挖掘专用:chain_size = 连锁附加格数(不含首格);clears_map = 本次动作后全图安全格开完 func request_dig(chain_size: int, clears_map: bool) -> bool: if phase != PHASE_ACTIVE: _reject("phase") @@ -76,7 +76,8 @@ func _spend_and_arbitrate(action_kind: String, clears_map: bool) -> bool: return false _ap -= cost if clears_map: - # cleared 胜出(同帧仲裁 cleared > ap_exhausted;剩余 AP 作废不入 RAP——EC7/EC10) + # cleared 胜出(同帧仲裁 cleared > ap_exhausted);剩余 AP 当场清零作废、不进 RAP(EC7/EC10) + _ap = 0 _finish_round("cleared", "new_board") elif _ap == 0: # AP 归 0 瞬间轮次结束——不存在 0-AP 操作窗口 @@ -84,7 +85,7 @@ func _spend_and_arbitrate(action_kind: String, clears_map: bool) -> bool: return true -# 停手:剩余 AP 1:1 转存 RAP;A3 直发 stopped 路径 round_end(双发射源) +# 停手:剩余 AP 1:1 转存 RAP;行动经济 直发 stopped 路径 round_end(双发射源) func request_stop() -> bool: if phase != PHASE_ACTIVE: _reject("phase") @@ -96,20 +97,9 @@ func request_stop() -> bool: 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 发起)纪律不变。 +# 轮内刷新(2026-09-12 方案 A 用户拍板;等待期通道 2026-09-15 用户拍板整体移除):唯一入口 = +# Active 相位——仅扣 RAP,相位与 AP 均不动。刷新不打断轮次、不触发结算;换图编排归 战斗结算 +# (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 HUD框架发起)纪律不变。 func request_refresh_active() -> bool: if phase != PHASE_ACTIVE: _reject("phase") @@ -120,7 +110,7 @@ func request_refresh_active() -> bool: return true -# B1 回发:结算完成(仅记录编排意图;phase 停留 Waiting——AC-14 门不被绕过) +# 战斗结算 回发:结算完成(仅记录编排意图;phase 停留 Waiting——AC-14 门不被绕过) func notify_settlement_done(next_round_mode: String) -> bool: if phase != PHASE_WAITING: _reject("phase") @@ -149,15 +139,19 @@ func notify_combat_end() -> bool: _reject("phase") return false _phase = PHASE_IDLE - _rap = 0 # RAP 战斗域清零(用户拍板;D1 可见反馈义务) + _rap = 0 # RAP 战斗域清零(用户拍板;HUD框架可见反馈义务) _ap = 0 return true func _finish_round(path_tag: String, continuation_hint: String) -> void: + # 实现层断言兜底(行动经济 轮次结束仲裁表):耗尽轮剩余恒为 0;清图轮剩余已当场清零(不进储备池)。 + # 两断言守在唯一仲裁出口,未来若在扣费与仲裁之间挪动 AP 写入在此红灯。 + assert(path_tag != "ap_exhausted" or _ap == 0, "耗尽轮剩余行动点恒为 0(实现层断言兜底)") + assert(path_tag != "cleared" or _ap == 0, "清图轮剩余行动点当场清零(不进储备池)") _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] 资源不足拒绝") + push_warning("[行动经济] 动作被拒(%s 相位守卫/资源)" % source if source == "phase" else "[行动经济] 资源不足拒绝") diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd index 5343df5c..e4d7da38 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd @@ -1,12 +1,12 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# B1 战斗结算编排中枢:轮次生命周期唯一 owner · 段序骨架(授予→窗口→增益/减益→道具→战斗→衰减→判定)· -# 13 类行协议可回放步骤流(零随机零 await)· 高光双落(完美排雷 ×2 / 标中旗 highlight)· 伏击预截流 · +# 战斗结算编排中枢:轮次生命周期唯一 owner · 段序骨架(授予→窗口→增益/减益→道具→战斗→衰减→判定)· +# 15 类行协议可回放步骤流(零随机零 await;2026-09-15 窗口锚行批 13→15)· 高光双落(完美排雷 ×2 / 标中旗 highlight)· 伏击预截流 · # Waiting 相位结算触发(不依赖事件到达序)。 -# 切片登记:① 事件总线载体 = 直连信号(实现期 ADR 项,provisional)② 多次刷新 EC15 简化为每 Waiting 至多 -# 一次(UI 禁二次发起;多刷契约义务不触)③ 效果行协议 = 授予段逐 grant 行 + 增益/减益段续存实例组合行。 -# 实现 GDD:design/archive/gdd-2026-09-14/b1-settlement.md(系统 ID B1——registry 追溯锚,命名规范见 CLAUDE.md) +# 切片登记:① 事件总线载体 = 直连信号(实现期 ADR 项,provisional)② Waiting 相位刷新通道 2026-09-15 用户拍板整体 +# 拆除(仅 Active 挖掘期受理;旧"每 Waiting 至多一次"简化登记随之作废)③ 效果行协议 = 授予段逐 grant 行 + 增益/减益段续存实例组合行。 +# 实现 GDD:design/archive/gdd-2026-09-14/b1-settlement.md(系统 ID B1·战斗结算——registry 追溯锚,命名规范见 CLAUDE.md) class_name BattleOrchestrator extends RefCounted @@ -17,18 +17,29 @@ signal round_started(round_index: int) signal board_changed() signal settlement_snapshot(snap: Dictionary) # 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析) +# 明细流水行型协议(战斗结算 Core Rule 14;15 类——2026-09-15 窗口锚行批由 13 类扩为 15 类, +# 清单改动须同步 battle_log_view 的 _format_row 与本文档头注): +# 效果行 effect · 伤害行 damage · 治疗行 heal · 道具行 item · 溢出未生效行 overflow · +# 奖励未生效行 reward_ineffective · 已消耗未生效行 consumed_ineffective · 使用被拒行 use_rejected · +# 标旗未中行 flag_miss · 蓄力行 charge · 衰减行 decay · 判定行 verdict · 高光行 highlight · +# 窗口打开锚行 window_open · 窗口跳过锚行 window_skip(后两类 = 2026-09-15 补的窗口段锚行) +# 行来源断言豁免集合(行型层)——判定段 / 衰减段 / 窗口锚行本就无棋盘来源,勿一刀切误炸 +const SOURCE_EXEMPT_TYPES := ["decay", "verdict", "window_open", "window_skip"] +# 行来源断言豁免集合(来源类别层)——事实来源是单位 / 清图 / 库存件而非棋盘格 +const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward", "item"] + 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 +# E1 封装收口(2026-09-13):协作对象 board_generator/enemy_system/status_effects/reward_economy 私有化——跨层读取一律经 presentation_snapshot()/ +# diagnostic_snapshot() 成文出口(呈现层链式穿透 settlement._enemy_system.hero_unit() 的根因收口)。 +# minesweeper/action_economy 保持公有 = 架构文档 API Boundaries 的 HUD框架消费面(意图提交 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 _board_generator: BoardGenerator +var minesweeper: MinesweeperRules +var action_economy: ActionEconomy +var _enemy_system: UnitContainer +var _status_effects: StatusContainer +var _reward_economy: RewardEconomy var encounter: Dictionary = {} var round_index := 0 @@ -40,9 +51,9 @@ 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 _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_max_per_round) var _board_seq := 0 # 板序号(本战斗第几张板——快照归因用) +var _board_from_midround_refresh := false # 装板 provenance:轮内刷新换来的图 = true(清图奖励守卫——刷新图不发清图奖励) var _flag_consumed := {} # 兑付资格以雷格实例为键(板级;换图重置) var _flag_miss_seen := {} # 标旗未中行去重(每雷格实例一次) var _perfect_hit_true := 0 # 板级完美排雷计数(install 重置) @@ -54,38 +65,38 @@ var _charge_dmg_a := 0 # 伤害A 充能:本轮开出的资 var _charge_dmg_b := 0 # 伤害B 充能:本轮首次标中雷计数 ×damage_b_per_flag(每雷实例一次) -func _init(db_ref, rng_ref, shared_b4 = null) -> void: +func _init(db_ref, rng_ref, shared_reward_economy = 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) + _board_generator = BoardGenerator.new() + action_economy = ActionEconomy.new(db) + minesweeper = MinesweeperRules.new(db, action_economy) + _enemy_system = UnitContainer.new(db) + _status_effects = StatusContainer.new(db) + # 库存关卡域(2026-09-11 关内延续):连续关卡模式注入共享 奖励道具经济 容器,库存跨战斗延续 + _reward_economy = shared_reward_economy if shared_reward_economy != null else RewardEconomy.new(db, rng) + minesweeper.cell_revealed.connect(_on_cell_revealed) + minesweeper.flag_judged.connect(_on_flag_judged) + minesweeper.round_end_board.connect(_on_round_end) + action_economy.round_end_stopped.connect(_on_round_end) -# ---- 战斗开始序列(编排专章①:门 → B3 → B2 → install+roll → A3)---- +# ---- 战斗开始序列(编排专章①:门 → 增益状态 → 敌人系统 → install+roll → 行动经济)---- func begin_battle(enc: Dictionary) -> bool: if round_index != 0: - push_warning("[B1] begin_battle 重复受理") + push_warning("[战斗结算] begin_battle 重复受理") return false encounter = enc db.set_reload_gate(true) - if not _b3.notify_battle_start(): - _fatal("B3 开域被拒(编排违例)") + if not _status_effects.notify_battle_start(): + _fatal("增益状态 开域被拒(编排违例)") return false - _b4.notify_battle_start() # 可选类供给控制器战斗域清零(2026-09-11 供给控制器批——首件保证按战重置) - # 遭遇骰(C3 GDD Core Rule 8——2026-09-12 用户拍板):行有 unit_count_max 时按 [unit_count, max] 均匀骰; + _reward_economy.notify_battle_start() # 可选类供给控制器战斗域清零(2026-09-11 供给控制器批——首件保证按战重置) + # 遭遇骰(难度曲线与Boss 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 即确定值)。 + # 挖掘决策域之外——P1 合规;敌人系统 零随机纪律保持(收 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)) @@ -101,9 +112,9 @@ func begin_battle(enc: Dictionary) -> bool: 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, + if not _enemy_system.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 被拒") + _fatal("敌人系统 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)) @@ -111,8 +122,8 @@ func begin_battle(enc: Dictionary) -> bool: 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 被拒") + if not action_economy.notify_round_start(): + _fatal("行动经济 首轮 notify_round_start 被拒") return false round_index = 1 round_started.emit(round_index) @@ -121,14 +132,14 @@ func begin_battle(enc: Dictionary) -> bool: 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) + var board: BoardData = _board_generator.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) + var res: Dictionary = minesweeper.install_board(board, during_round) if not res.accepted: - _fatal("A2 install 拒绝(不变式违例): %s" % str(res)) + _fatal("排雷玩法 install 拒绝(不变式违例): %s" % str(res)) return false - # B4 义务⑥:roll_board 恰一次(install 后、挖掘前);资格格 = S ∖ R₀ + # 奖励道具经济 义务⑥:roll_board 恰一次(install 后、挖掘前);资格格 = S ∖ R₀ var r0 := {} for c in board.compute_r0(): r0[c] = true @@ -137,13 +148,14 @@ func _install_new_board(during_round := false) -> bool: 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 被拒") + if not _reward_economy.roll_board(eligible): + _fatal("奖励道具经济 roll_board 被拒") return false - _flag_consumed.clear() # 换图新雷 = 新实例,资格自然重置(A3 Rule 8) + _flag_consumed.clear() # 换图新雷 = 新实例,资格自然重置(行动经济 Rule 8) _flag_miss_seen.clear() _perfect_hit_true = 0 # 板级完美计数随 install 重置 _perfect_hit_false = 0 + _board_from_midround_refresh = during_round # 轮内刷新装板 = 刷新图(清图奖励不发);常规换图/开战装板 = false _board_seq += 1 board_changed.emit() return true @@ -159,7 +171,7 @@ func _on_cell_revealed(evt: Dictionary) -> void: if evt.kind == "safe" and bool(evt.get("reward_eligible", false)): # 身份快照随缓冲捕获(批次隔离——轮内刷新后 mapping 已换批,授予必须用缓冲期身份, # 防旧图已挖格在轮末错拿新图同坐标身份) - var snap = _b4.query_full_snapshot(evt.cell) + var snap = _reward_economy.query_full_snapshot(evt.cell) if snap != null: evt = evt.duplicate() evt["reward_snapshot"] = snap @@ -178,7 +190,7 @@ func _on_round_end(_payload: Dictionary) -> void: func _maybe_settle() -> void: if _settling or battle_result != "": return - if a3.phase != a3.PHASE_WAITING: + if action_economy.phase != action_economy.PHASE_WAITING: return _settle() @@ -196,7 +208,7 @@ func _settle() -> void: 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 委派闭环) + # reward_eligible=false(initial 通道)不入库——战斗结算 义务②(行动经济 EC5 委派闭环) for evt in _facts_flags: _judge_flag(evt, ri) if not mine_cells.is_empty(): @@ -206,22 +218,26 @@ func _settle() -> void: # ② 道具使用窗口(授予段后、增益/减益段前——当轮拾取当轮可用) _window_items = _assemble_window() if not _window_items.is_empty(): + # 窗口打开锚行(战斗结算 验收:"窗口段恒在案")——零棋盘来源,行型豁免 + _emit_row("window_open", "", Vector2i(-1, -1), ri, "道具窗口(%d 件可选)" % _window_items.size(), _window_items.size(), {}) _awaiting_window = true window_opened.emit(_window_items) _settling = false return # 窗口选择序列 = 结算回放输入事件 + # 空库存不弹窗,但窗口段仍要在案("空库存记跳过")——锚行不可省 + _emit_row("window_skip", "", Vector2i(-1, -1), ri, "道具窗口跳过(空库存)", 0, {}) _finish_settlement([]) func _assemble_window() -> Array: - # 窗口数据组装 = B4 query_inventory 投影 + B1 侧守卫预判 usable_now(B4 零跨系统读不破) + # 窗口数据组装 = 奖励道具经济 query_inventory 投影 + 战斗结算 侧守卫预判 usable_now(奖励道具经济 零跨系统读不破) var out: Array = [] - for inst in _b4.query_inventory(): + for inst in _reward_economy.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) # 满血治疗包禁选 + usable = int(_enemy_system.hero_unit().hp) < int(_enemy_system.hero_unit().max_hp) # 满血治疗包禁选 out.append({ "index": inst.index, "item_id": inst.item_id, @@ -237,18 +253,22 @@ func _assemble_window() -> Array: func submit_window_selection(selection: Array) -> void: if not _awaiting_window: - push_warning("[B1] 无窗口期的选择提交(编排违例)") + push_warning("[战斗结算] 无窗口期的选择提交(编排违例)") return _awaiting_window = false _settling = true if selection.size() > 1: - push_warning("[B1] 窗口至多选用一件(2026-09-11 用户拍板——避免结算复杂度)——超出部分忽略") - _finish_settlement(selection.slice(0, 1)) + push_warning("[战斗结算] 窗口至多选用一件(2026-09-11 用户拍板——避免结算复杂度)——超出部分忽略") + var picked: Array = selection.slice(0, 1) + if picked.is_empty(): + # 零选择跳过锚行(战斗结算 验收:"零选择记跳过")——窗口段在案的第二半 + _emit_row("window_skip", "", Vector2i(-1, -1), round_index, "道具窗口跳过(未选用)", 0, {}) + _finish_settlement(picked) func _finish_settlement(selection: Array) -> void: var ri := round_index - # ③ 增益/减益段(续存实例组合行;本结算新授予的已在授予段逐 grant 出行——B3 义务⑦) + # ③ 增益/减益段(续存实例组合行;本结算新授予的已在授予段逐 grant 出行——增益状态 义务⑦) _emit_composition_effects("hero", ri) _emit_composition_effects("enemy", ri) # ④ 道具段(窗口选定序逐件生效;逐件动态守卫) @@ -257,13 +277,13 @@ func _finish_settlement(selection: Array) -> void: # ⑤ 战斗段(调用序钉死 query_attack → apply_damage → advance;hero hp=0 短路) _combat_segment(ri) # ⑥ 衰减段(恰一次恒执行含末轮——先兑付后衰减) - var k: int = _b3.decay_round() + var k: int = _status_effects.decay_round() _emit_row("decay", "", Vector2i(-1, -1), ri, "状态衰减", k, {}, "") # ⑦ 判定段(防御性优先级 hero 先判——双死结构不可达的纵深防御) var verdict := "continue" - if int(_b2.hero_unit().get("hp", 0)) <= 0: + if int(_enemy_system.hero_unit().get("hp", 0)) <= 0: verdict = "defeat" - elif _b2.query_alive("enemy").is_empty(): + elif _enemy_system.query_alive("enemy").is_empty(): verdict = "victory" _emit_settlement_snapshot(verdict) # 全量状态快照(despawn 前组装——终局含 result) if verdict == "defeat": @@ -283,55 +303,57 @@ func _finish_settlement(selection: Array) -> void: # 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) + var snap = snap_override if snap_override != null else _reward_economy.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}) + var ok: bool = _status_effects.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 两类奖励裁决) + elif _resolve_instant(snap, source_class, cell, ri) != "not_instant": + pass # 即时类:授予段当场兑付(兑付行/零兑付未生效行均由 _resolve_instant 自记——2026-09-11 两类奖励裁决) else: - _b4.stock(snap, cell, ri) + _reward_economy.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: +# instant_heal = 主角 +power(clamp,满血 → "奖励未生效"行)。 +# 返回 "redeemed" = 实际有效兑付 / "zero" = 零兑付(奖励未生效行已记)/ "not_instant" = 非即时类(道具类走库存)。 +# 零兑付必须可区分:完美排雷高光行只记实际有效兑付,首份即零兑付则高光行不发(战斗结算 授予段规则)。 +func _resolve_instant(snap: Dictionary, source_class: String, cell: Vector2i, ri: int, suffix: String = "", highlight: String = "") -> String: if snap.category == "instant_aoe": - var alive: Array = _b2.query_alive("enemy") + var alive: Array = _enemy_system.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 + return "zero" for u in alive: - _b2.apply_damage(u, int(snap.power)) # 恒定表值,逐个;受击即死——死亡即不动手 + _enemy_system.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 + return "redeemed" if snap.category == "instant_heal": - if int(_b2.hero_unit().hp) >= int(_b2.hero_unit().max_hp): + if int(_enemy_system.hero_unit().hp) >= int(_enemy_system.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 + return "zero" + var healed: int = _enemy_system.heal(_enemy_system.hero_unit(), int(snap.power)) # clamp 归 战斗结算 _emit_row("heal", source_class, cell, ri, "%s 回复%s" % [snap.display_name, suffix], healed, {"target": "hero"}, highlight) - return true - return false + return "redeemed" + return "not_instant" func _judge_flag(evt: Dictionary, ri: int) -> void: var cell: Vector2i = evt.cell if evt.hit: if _flag_consumed.has(cell): - return # 去重跳过(资格以雷格实例为键——A2 诚实重复上报的对侧);零明细行 + return # 去重跳过(资格以雷格实例为键——排雷玩法 诚实重复上报的对侧);零明细行 _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}) + var ok: bool = _status_effects.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") @@ -357,7 +379,7 @@ func _apply_miss_backlash(cell: Vector2i, ri: int) -> void: var src: Dictionary = {"source_class": "flag_miss", "origin_cell": cell, "round_index": ri} match kind: 0: - if _b3.apply_effect("enrage", src): + if _status_effects.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: @@ -365,14 +387,14 @@ func _apply_miss_backlash(cell: Vector2i, ri: int) -> void: 1: var granted := 0 for i in range(2): - if _b3.apply_effect("ambush", src): + if _status_effects.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}) + _emit_row("effect", "flag_miss", cell, ri, "反噬·敌方伏击 +%d 层" % granted, granted, {"effect_id": "ambush", "pool_value": _status_effects.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): + if _status_effects.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: @@ -380,13 +402,13 @@ func _apply_miss_backlash(cell: Vector2i, ri: int) -> void: func _convert_ambush(mine_cells: Array, ri: int) -> void: - # B2 义务②⑥:预截流计划面 → 前 granted 颗逐颗转化(事件规范序);每颗雷恰一次 - var plan: Dictionary = _b2.ambush_grant_plan(mine_cells.size(), _enemy_cap) + # 敌人系统 义务②⑥:预截流计划面 → 前 granted 颗逐颗转化(事件规范序);每颗雷恰一次 + var plan: Dictionary = _enemy_system.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}) + var ok: bool = _status_effects.apply_effect("ambush", {"source_class": "mine_hit", "origin_cell": cell, "round_index": ri}) if ok: - var layers := _b3.pool_layers("ambush") + var layers := _status_effects.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] @@ -394,25 +416,37 @@ func _convert_ambush(mine_cells: Array, ri: int) -> void: func _grant_clear_reward(ri: int) -> void: - var snap = _b4.grant_clear() + if _board_from_midround_refresh: + return # 轮内刷新换来的新图不发清图奖励(行动经济/战斗结算 明文——刷新图不是清图挣来的) + var snap = _reward_economy.grant_clear() if not (snap is Dictionary): return var perfect: bool = _perfect_hit_false == 0 and _perfect_hit_true >= 1 - # 首份兑付 + # 首份兑付(first_ok = 实际有效兑付——零兑付/被拒均不算) var first_ok := _grant_clear_identity(snap, ri) - # 完美排雷:真实双份交付(第二份走既有授予通道;grant_clear 本身仍恰一次) + # 完美排雷:真实双份交付(第二份按身份类型走真实兑付——即时类重走 _resolve_instant; + # 增益类重授容器;道具类入库。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}) + second_ok = _status_effects.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: + _emit_row("reward_ineffective", "clear_reward", Vector2i(-1, -1), ri, "完美第二份未生效(满层)", 0, {"batch_key": String(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), {}) + var res2 := _resolve_instant(snap, "clear_reward", Vector2i(-1, -1), ri, "(完美第二份)") + if res2 == "redeemed": + second_ok = true + elif res2 == "not_instant": + var s2 = _reward_economy.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), {}) + else: + _emit_row("reward_ineffective", "clear_reward", Vector2i(-1, -1), ri, "完美第二份未生效(入库被拒)", 0, {"batch_key": String(snap.reward_id)}) + # res2 == "zero":零兑付的奖励未生效行已由 _resolve_instant 自记 if perfect and first_ok: # 高光行 value = 实际有效兑付份数(禁记名义值;首份满层零兑付 → 降级不发) _emit_row("highlight", "clear_reward", Vector2i(-1, -1), ri, "完美排雷!清图奖励 ×2", (2 if second_ok else 1), {}, "perfect_sweep") @@ -420,17 +454,19 @@ func _grant_clear_reward(ri: int) -> void: 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}) + var ok: bool = _status_effects.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) + var res := _resolve_instant(snap, "clear_reward", Vector2i(-1, -1), ri, "(清图奖励)", "clear_reward_h2") + if res != "not_instant": + return res == "redeemed" # 即时类零兑付(无存活敌/满血)= 未生效——首份即零兑付则高光行不发 + var s = _reward_economy.stock(snap, Vector2i(-1, -1), ri) if not (s is Dictionary): + _emit_row("reward_ineffective", "clear_reward", Vector2i(-1, -1), ri, "清图奖励未生效(入库被拒)", 0, {"batch_key": String(snap.reward_id)}) return false _emit_row("item", "clear_reward", Vector2i(-1, -1), ri, "%s 入库(清图奖励)" % snap.display_name, int(snap.power), {}, "clear_reward_h2") return true @@ -443,29 +479,29 @@ func _apply_selected_item(window_index: int, ri: int) -> void: 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): + # 逐件动态守卫:前件推满后本件拦截(不烧包——战斗结算 不调 use) + if int(_enemy_system.hero_unit().hp) >= int(_enemy_system.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) + var receipt = _reward_economy.use(item.index) if not (receipt is Dictionary): return - var healed: int = _b2.heal(_b2.hero_unit(), receipt.power) # clamp 归 B1 + var healed: int = _enemy_system.heal(_enemy_system.hero_unit(), receipt.power) # clamp 归 战斗结算 _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) + # 伤害道具:恒定表值不吃修正;零效果判定读 敌人系统 存活集(照常消耗——E10) + if _enemy_system.query_alive("enemy").is_empty(): + var receipt0 = _reward_economy.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) + var receipt = _reward_economy.use(item.index) if not (receipt is Dictionary): return - var target = _b2.default_target() + var target = _enemy_system.default_target() var killed_before: bool = not target.alive - _b2.apply_damage(target, receipt.power) + _enemy_system.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}) @@ -473,11 +509,11 @@ func _apply_selected_item(window_index: int, ri: int) -> void: # ---- 战斗段 ---- func _combat_segment(ri: int) -> void: - var L := _b3.pool_layers("ambush") # 授予段后的当轮值;池共享不消耗 + var L := _status_effects.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() + var hero_target = _enemy_system.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)) @@ -486,13 +522,17 @@ func _combat_segment(ri: int) -> void: 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) + _enemy_system.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: + else: + # 零值伤害行(战斗结算 边界表:"合成后为 0 时不调扣血接口,改写零值伤害行——免伤与零输出必须可见"; + # 敌方侧同款口径已就绪——照抄。三分量字段齐备保 P2 归因不断链) + _emit_row("damage", "hero_attack", Vector2i(-1, -1), ri, "主角攻击(被减伤归零)", 0, {"target": hero_target.display_name, "base": base, "dmg_a": comp_a, "dmg_b": comp_b}) + for u in _enemy_system.query_alive("enemy"): + if int(_enemy_system.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) # 调用序①兑付读值 + var beat: String = _enemy_system.query_beat(u) + var atk: Dictionary = _enemy_system.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: @@ -500,9 +540,9 @@ func _combat_segment(ri: int) -> void: 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) # 调用序②受击上报 + _enemy_system.apply_damage(_enemy_system.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 不在推进域) + _enemy_system.advance(u) # 调用序③推进——每结算每存活敌恰一次(hero 不在推进域) # F2 乘区:池内加算 → clamp [-100,+100] → 池间乘算 → floor(单一取整规则)——算术归 DamageMath(O3) @@ -530,7 +570,7 @@ func _armor_effectiveness(component: String, armor: String) -> int: func _clamped_pool(pool: String) -> int: var total := 0 - for eff in _b3.query_active("hero") + _b3.query_active("enemy"): + for eff in _status_effects.query_active("hero") + _status_effects.query_active("enemy"): var row: Dictionary = db.status_effect_row(eff.effect_id) if row.is_empty(): continue @@ -544,16 +584,16 @@ func _pool_effective(effect_id: String) -> int: 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): + for eff in _status_effects.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): + for eff in _status_effects.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}) @@ -561,12 +601,12 @@ func _emit_composition_effects(side: String, ri: int) -> void: func _emit_settlement_snapshot(verdict: String) -> void: var enemies: Array = [] - for u in _b2.query_alive("enemy"): + for u in _enemy_system.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"): + for eff in _status_effects.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"): + for eff in _status_effects.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 @@ -576,11 +616,11 @@ func _emit_settlement_snapshot(verdict: String) -> void: 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() + if minesweeper.current_board() != null: + board_stats["w"] = minesweeper.current_board().width + board_stats["h"] = minesweeper.current_board().height + board_stats["revealed"] = minesweeper.revealed_cells().size() + board_stats["flags"] = minesweeper.flag_count() settlement_snapshot.emit({ "kind": "settlement", "level": int(encounter.get("level_index", 0)), @@ -588,20 +628,20 @@ func _emit_settlement_snapshot(verdict: String) -> void: "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}, + "spawn_count": _enemy_system.all_enemies().size(), # 本战斗实例化总数(含死亡残位)——unit_count=0 时区分「首轮全灭」vs「空场」(playtest-2026-09-12 报告 Bug#1 误读教训) + "hero": {"hp": int(_enemy_system.hero_unit().get("hp", 0)), "max_hp": int(_enemy_system.hero_unit().get("max_hp", 0)), "atk": int(_enemy_system.hero_unit().get("atk", 0))}, + "economy": {"phase": String(action_economy.phase), "ap": int(action_economy.ap), "rap": int(action_economy.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(), + "ambush_layers": _status_effects.pool_layers("ambush"), + "inventory_in_stock": _reward_economy.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) + return _enemy_system.query_beat(u) # ---- 出口分发与战斗结束序列 ---- @@ -609,77 +649,67 @@ func query_beat_of(u: Dictionary) -> String: 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 的对侧) + mode = "new_board" # cleared 轮恒走 new_board(契约义务);非清图轮恒同图(等待期刷新通道已移除) if mode == "new_board": - _b4.retire_mapping() - a2.retire_board() + _reward_economy.retire_mapping() + minesweeper.retire_board() if not _install_new_board(): _fatal("换图编排失败") return - a3.notify_settlement_done(mode) - a3.notify_round_start() + action_economy.notify_settlement_done(mode) + action_economy.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 刷新按钮可用性) +# 呈现层查询面(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) + and action_economy.phase == action_economy.PHASE_ACTIVE \ + and action_economy.rap >= int(db.action_economy.rap_refresh_cost) -# 玩家经 D1 发起、B1 代理调用(禁止 RAP 满自动刷)。 -# 轮内刷新(2026-09-12 方案 A 用户拍板):Active 相位立即换图继续挖(RAP -8;AP 保留、相位不动、 -# 不触发结算);Waiting 相位保持现状(登记意图,exit_distribution 换图)。每轮上限 = 表值(两相位合计)。 +# 玩家经 HUD框架发起、战斗结算 代理调用(禁止 RAP 满自动刷)。 +# 挖掘期唯一通道(2026-09-12 方案 A 拍板 + 2026-09-15 等待期通道整体移除):Active 相位立即换图 +# 继续挖(RAP -8;AP 保留、相位不动、不触发结算);非挖掘期请求一律拒绝——非清图轮恒同图。 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(): + if not action_economy.request_refresh_active(): return false _refresh_used_this_round += 1 - _refresh_used_this_waiting = true - return true + _reward_economy.retire_mapping() # 旧图批次退役(奖励道具经济 Ready→retired;E6 新批重置前提) + minesweeper.retire_board(true) # 旧板退役(轮内通道) + return _install_new_board(true) # 当场换图——失败即编排违例 fail-fast 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 清零 + var hero_hp_left := int(_enemy_system.hero_unit().get("hp", 0)) # despawn 前捕获(信号载荷供呈现层"HP 带入下一层") + _reward_economy.retire_mapping() + _enemy_system.despawn_all() + _status_effects.notify_battle_end() + action_economy.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()}) + battle_finished.emit({"result": result, "hero_hp": hero_hp_left, "remaining_items": _reward_economy.in_stock_count()}) # ---- 呈现状态面(E1 封装收口 2026-09-13):呈现层 / headless 自动玩家的唯一读取出口—— -# 协作对象(_b2/_b3/_b4)私有化后,跨层读取一律经本快照或下列命名面(diagnostic_snapshot 同款模式, -# 禁再链式穿透 b1.b2.hero 一类路径)。载荷 schema 成文(code-conventions 2.1): +# 协作对象(_enemy_system/_status_effects/_reward_economy)私有化后,跨层读取一律经本快照或下列命名面(diagnostic_snapshot 同款模式, +# 禁再链式穿透 settlement.enemy_system.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 +# ambush_layers / hero_effects[](增益状态 query_active 投影) / inventory[](奖励道具经济 query_inventory 投影) / in_stock func presentation_snapshot() -> Dictionary: var enemies: Array = [] - var all: Array = _b2.all_enemies() - var ambush: int = _b3.pool_layers("ambush") + var all: Array = _enemy_system.all_enemies() + var ambush: int = _status_effects.pool_layers("ambush") for i in all.size(): var u: Dictionary = all[i] var alive: bool = bool(u.get("alive", false)) @@ -690,44 +720,44 @@ func presentation_snapshot() -> Dictionary: "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 "", + "beat": String(_enemy_system.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() + var h: Dictionary = _enemy_system.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), + "phase": String(action_economy.phase), + "ap": int(action_economy.ap), "rap": int(action_economy.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_effects": _status_effects.query_active("hero"), + "inventory": _reward_economy.query_inventory(), + "in_stock": _reward_economy.in_stock_count(), } # 战斗进行中谓词(hero 在场且存活)——组合根高频相位判断的低成本面 func is_battle_live() -> bool: - return battle_result == "" and not _b2.hero_unit().is_empty() + return battle_result == "" and not _enemy_system.hero_unit().is_empty() # 库存余量(层间过渡文案 / 终局清库反馈) func stock_count() -> int: - return _b4.in_stock_count() + return _reward_economy.in_stock_count() -# 奖励身份解析(组合根注入 BoardView 徽章回调的只读代理——Ready 相位守卫归 B4) +# 奖励身份解析(组合根注入 BoardView 徽章回调的只读代理——Ready 相位守卫归 奖励道具经济) func query_reward_identity(cell: Vector2i): - return _b4.query_identity(cell) + return _reward_economy.query_identity(cell) # 诊断观测面(组合根诊断转储 / 行为探针消费):私有编排状态的成文只读出口—— @@ -738,26 +768,25 @@ func diagnostic_snapshot() -> Dictionary: return { "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, + "action_economy": {"phase": String(action_economy.phase), "ap": action_economy.ap, "rap": action_economy.rap}, + "minesweeper_board_installed": minesweeper.current_board() != null, + "minesweeper_revealed": minesweeper.revealed_cells().size() if minesweeper.current_board() != null else 0, + "minesweeper_flags": minesweeper.flag_count() if minesweeper.current_board() != null else 0, + "reward_economy_phase_stock": [_reward_economy.in_stock_count()], + "enemies_alive": _enemy_system.query_alive("enemy").size(), + "hero_hp": int(_enemy_system.hero_unit().get("hp", -1)) if not _enemy_system.hero_unit().is_empty() else -1, } -# 关卡结束编排(C1/C2 关卡结构落位前由上层显式调用——2026-09-11 关内延续): +# 关卡结束编排(关卡节点地图/冒险结构 关卡结构落位前由上层显式调用——2026-09-11 关内延续): # 清空库存并返回快照计数("剩余道具 ×N 随关卡结束清空"反馈行 / 折算结构位的数据面) func level_clear() -> Dictionary: - var remaining := _b4.in_stock_count() - _b4.clear_inventory() + var remaining := _reward_economy.in_stock_count() + _reward_economy.clear_inventory() return {"remaining": remaining} @@ -772,6 +801,7 @@ func _reset_round_facts() -> void: # ---- 行协议(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: + _assert_row_source(type, source_class, origin_cell) var row := { "type": type, "source_class": source_class, @@ -787,6 +817,19 @@ func _emit_row(type: String, source_class: String, origin_cell: Vector2i, ri: in step_row.emit(row) +# 行来源断言(战斗结算 明细流水:"任何一行找不到来源即断言失败——宁可报错"): +# 来源字段 = source_class(公共字段)+ origin_cell(棋盘来源行的真实格)。 +# 豁免按两层成文(见 SOURCE_EXEMPT_TYPES / SOURCE_EXEMPT_CLASSES 常量注释),其余行必须带真实棋盘格。 +func _assert_row_source(type: String, source_class: String, origin_cell: Vector2i) -> void: + if SOURCE_EXEMPT_TYPES.has(type): + return + assert(source_class != "", "[战斗结算] 明细行缺来源类别(type=%s)——任何一行找不到来源即断言失败" % type) + if SOURCE_EXEMPT_CLASSES.has(source_class): + return + assert(origin_cell.x >= 0 and origin_cell.y >= 0, + "[战斗结算] 明细行来源格越界(type=%s source=%s cell=%s)——棋盘来源行必须指回真实格" % [type, source_class, str(origin_cell)]) + + func _fatal(msg: String) -> void: - push_error("[B1/fatal] " + msg) + push_error("[战斗结算/fatal] " + msg) assert(false, msg) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd b/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd index 24aa9c3d..0e8d5d9d 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/board_data.gd @@ -1,8 +1,8 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# A1 棋盘产物数据契约(A2 消费;生成后不可变——A2 持快照即持活引用,生成器不再触碰)。 -# 实现 GDD:design/archive/gdd-2026-09-14/a1-board-generation.md(系统 ID A1——registry 追溯锚,命名规范见 CLAUDE.md) +# 棋盘产物数据契约(排雷玩法消费;生成后不可变——排雷玩法持快照即持活引用,生成器不再触碰)。 +# 实现 GDD:design/archive/gdd-2026-09-14/a1-board-generation.md(系统 ID A1·棋盘生成——registry 追溯锚,命名规范见 CLAUDE.md) class_name BoardData extends RefCounted @@ -15,8 +15,9 @@ var variants := {} # Vector2i → Array[String](生成期快照,抗表 var biome := "" var level_index: int = 1 var node_depth: int = 0 -var metrics := {} # 八项生成期指标 -var illegal_product := false +var metrics := {} # 生成期指标集(键清单单点定义在 board_generator._finalize) +var illegal_product := false # 非法产物标记(接受最不坏出口——零合法板诚实降级) +var degraded_product := false # 降级产物标记(接受最优出口——有合法板但无一达标;与 illegal_product 对称) func in_bounds(c: Vector2i) -> bool: @@ -93,7 +94,7 @@ func fog_cells() -> Array: return out -# R₀ = 出生 3×3 ∪ (出生 3×3 经 0 格的 flood 闭包)——与 A2 F3 逐字同构(A1 F1 求解器初始态)。 +# R₀ = 出生 3×3 ∪ (出生 3×3 经 0 格的 flood 闭包)——与 排雷玩法 F3 逐字同构(棋盘生成 F1 求解器初始态)。 func compute_r0() -> Array: var b3 := spawn_cells() var r0 := {} @@ -110,7 +111,7 @@ func compute_r0() -> Array: return r0.keys() -# A1 Rule 4 排除域(以最终产物板计):R₀ ∪ 外扩 1 环——生成期确定性计算。 +# 棋盘生成 Rule 4 排除域(以最终产物板计):R₀ ∪ 外扩 1 环——生成期确定性计算。 func exclusion_domain() -> Dictionary: var r0 := compute_r0() var dom := {} diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd b/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd index aa2172b0..b87e5010 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/board_generator.gd @@ -1,10 +1,10 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# A1 确定性棋盘生成器:五阶段管线(参数→布雷→变异→验证循环→产出)+ F1 可推理率度量 +# 确定性棋盘生成器:五阶段管线(参数→布雷→变异→验证循环→产出)+ F1 可推理率度量 # (single_subset 求解器)+ F3 雾预算 + F5 验收算子(达标即出/接受最优/接受最不坏)。 -# 切片裁剪登记:黄金向量回归套件与 F4 批量工具不做(C3 调参期工具域)。 -# 实现 GDD:design/archive/gdd-2026-09-14/a1-board-generation.md(系统 ID A1——registry 追溯锚,命名规范见 CLAUDE.md) +# 切片裁剪登记:黄金向量回归套件与 F4 批量工具不做(难度曲线与Boss 调参期工具域)。 +# 实现 GDD:design/archive/gdd-2026-09-14/a1-board-generation.md(系统 ID A1·棋盘生成——registry 追溯锚,命名规范见 CLAUDE.md) class_name BoardGenerator extends RefCounted @@ -52,7 +52,7 @@ func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardDat 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: + if inf >= params.target: # 单一判据(棋盘生成:现行规则已回归"可推理率 ≥ 及格线") accepted = b accept_mode = "first_pass" break @@ -60,12 +60,13 @@ func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardDat if best_legal != null: accepted = best_legal accept_mode = "accept_best" - push_warning("[A1] 重试耗尽无达标——接受最优出图(强告警埋点,target=%.2f best=%.3f)" % [params.target, best_legal_score]) + accepted.degraded_product = true # 降级标记(独立于 illegal_product——三分出口表要求标记随产物上抛) + push_warning("[棋盘生成] 重试耗尽无达标——接受最优出图(强告警埋点,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 耗尽零合法——接受最不坏出图 + 非法产物标记(玩家永不卡死)") + push_error("[棋盘生成] attempts 耗尽零合法——接受最不坏出图 + 非法产物标记(玩家永不卡死)") _finalize(accepted, params, attempts_used, accept_mode, db) return accepted @@ -275,7 +276,7 @@ func measure_inferability(b: BoardData) -> Dictionary: for con in derived: if con.mines == 0: for c in con.unknown: - if safe_set.has(c) and not r.has(c): + if not r.has(c): # 同 no-peek 口径:推导结论不再用 safe_set 复核(约束本身可靠) r[c] = true changed = true elif con.mines == con.unknown.size(): @@ -313,8 +314,8 @@ func _build_constraints(b: BoardData, r: Dictionary, known_mines: Dictionary, sa 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 + elif not r.has(n): + unknown[n] = true # 未揭示格一律计入未知(含雷格)——求解器禁读 safe_set 真值(棋盘生成:雷位不许偷看) var d_eff := d - known_here if d_eff < 0 or (d_eff > 0 and unknown.is_empty()): continue # 矛盾态(非法板)——不产约束,验收由 legality 闸承担 @@ -413,7 +414,7 @@ func legality(b: BoardData, params: Dictionary, db) -> bool: func _legality_faults(b: BoardData, params: Dictionary) -> Array: var faults: Array = [] - # ① 出生区无雷 ② 出生区∪R₀ 无变异 ③ 数字恒真 ④ R₀ ≠ S(A2 修订义务) + # ① 出生区无雷 ② 出生区∪R₀ 无变异 ③ 数字恒真 ④ R₀ ≠ S(排雷玩法 修订义务) for c in b.spawn_cells(): if b.is_mine(c): faults.append("inv1_mine_in_spawn") @@ -477,12 +478,16 @@ func _has_all_fog_window(b: BoardData, fog: Array) -> bool: 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() + # 产物断言(棋盘生成 出生区安全与反退化布雷):实际雷数 ≤ 目标雷数 N——先布后删只减不增,失败即致命 + var n_target: int = int(round(float(params.density) * float(params.w * params.h - 9))) + if b.mines.size() > n_target: + _fatal("产物断言违例:实际雷数 %d > 目标雷数 N=%d(先布后删只减不增)" % [b.mines.size(), n_target]) var attempt_facts := b.metrics # 保留尝试期事实(ring_removed / shortfall) b.metrics = { "metric_version": METRIC_VERSION, @@ -490,16 +495,20 @@ func _finalize(b: BoardData, params: Dictionary, attempts_used: int, accept_mode "inferability_core": m.inferability_core, "actual_fog_ratio": float(fog.size()) / float(b.width * b.height), "actual_mines": b.mines.size(), + "target_mines": n_target, "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, + "degraded_product": accept_mode == "accept_best", + "illegal_product": accept_mode == "accept_worst", "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" + b.degraded_product = accept_mode == "accept_best" func _badness(b: BoardData, params: Dictionary) -> Array: @@ -537,5 +546,5 @@ func _lex_less(a: Array, b: Array) -> bool: func _fatal(msg: String) -> void: - push_error("[A1/fatal] " + msg) + push_error("[棋盘生成/fatal] " + msg) assert(false, msg) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd b/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd index 311a73b3..ba0b0cc8 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/config_db.gd @@ -1,9 +1,9 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 ConfigDB autoload——数值表单点访问入口(Rule 2)+ 校验双通道(Rule 6)+ R1 行解析。 -# 实现 GDD:design/archive/gdd-2026-09-14/d2-data-config.md(系统 ID D2——registry 追溯锚,命名规范见 CLAUDE.md) -# 切片注:headless --script 模式下由冒烟脚本显式实例化(D2 AC 前言);玩法代码禁止绕过本单点自建 load()。 +# ConfigDB autoload(数据配置)——数值表单点访问入口(Rule 2)+ 校验双通道(Rule 6)+ R1 行解析。 +# 实现 GDD:design/archive/gdd-2026-09-14/d2-data-config.md(系统 ID D2·数据配置——registry 追溯锚,命名规范见 CLAUDE.md) +# 切片注:headless --script 模式下由冒烟脚本显式实例化(数据配置 AC 前言);玩法代码禁止绕过本单点自建 load()。 extends Node const SCHEMA_VERSION := 1 @@ -37,7 +37,7 @@ 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 唯一调用方 +var _reload_gate := false # 数据配置 EC8——战斗结算 唯一调用方 func push_diag(severity: String, category: String, message: String) -> void: @@ -148,7 +148,7 @@ func _cross_validate(loaded: Dictionary) -> void: 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 供给控制器批:清图槽改即时类一次性伤害,正当量含即时类) + # ③ clear_default 恒正当量(行动经济 条款④——2026-09-11 供给控制器批:清图槽改即时类一次性伤害,正当量含即时类) var clear_value_ok := false for row in clear_rows: if row.get("category", "") == "buff" and String(row.get("effect_id", "")) != "": @@ -188,7 +188,7 @@ func _cross_validate(loaded: Dictionary) -> void: 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}) + # ⑦ source_label_map 键集封闭性(键 = 增益状态 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): @@ -228,7 +228,7 @@ 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——组合事故非配置事故) +# 难度曲线与Boss DifficultyTable 消费面(敌人系统.spawn 调用):(L,D) 精确匹配修饰乘数,越域值 clamp(GDD Edge Case——组合事故非配置事故) func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary: if difficulty == null: return {} @@ -303,7 +303,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String) -> Diction return {} -# ---- A4 variant_pool_gate:候选池门控(∅ biome_tag = 通配)---- +# ---- 地形变异 variant_pool_gate:候选池门控(∅ biome_tag = 通配)---- func variant_pool(level_index: int, board_biomes: Array) -> Array: var pool: Array = [] @@ -325,7 +325,7 @@ func status_effect_row(effect_id: String) -> Dictionary: return status_effects.row_by_id(effect_id) -# ---- D2 EC8:debug 热重载门(B1 唯一调用方;切片实现 = 门标志 + 拒绝语义)---- +# ---- 数据配置 EC8:debug 热重载门(战斗结算 唯一调用方;切片实现 = 门标志 + 拒绝语义)---- func set_reload_gate(gated: bool) -> void: _reload_gate = gated @@ -333,7 +333,7 @@ func set_reload_gate(gated: bool) -> void: func reload() -> bool: if _reload_gate: - push_diag("warning", "reload_gated", "战斗中热重载被拒绝(D2 EC8)") + push_diag("warning", "reload_gated", "战斗中热重载被拒绝(数据配置 EC8)") return false var ok := load_tables("res://data/tables", false) if ok: diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd b/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd index fef9381d..a83f4bd0 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/damage_math.gd @@ -1,8 +1,8 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) -# 实现 GDD:design/archive/gdd-2026-09-14/b1-settlement.md(系统 ID B1——registry 追溯锚,命名规范见 CLAUDE.md) -# B1 战斗段伤害公式族(零状态纯算术宿主——规范 1.2「工具性纯函数挂明确宿主类」; -# 自 battle_orchestrator 拆出 2026-09-13,封装审查 O3)。池读取与表值读取仍归 B1,本类只做算术。 +# 实现 GDD:design/archive/gdd-2026-09-14/b1-settlement.md(系统 ID B1·战斗结算——registry 追溯锚,命名规范见 CLAUDE.md) +# 战斗结算·战斗段伤害公式族(零状态纯算术宿主——规范 1.2「工具性纯函数挂明确宿主类」; +# 自 battle_orchestrator 拆出 2026-09-13,封装审查 O3)。池读取与表值读取仍归 战斗结算,本类只做算术。 class_name DamageMath extends RefCounted diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd b/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd index 89d758b2..ef2912ef 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/level_runner.gd @@ -1,19 +1,19 @@ # 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 元进度,切片不落盘)。 +# 关卡节点地图(provisional 线性形态——节点链来自 LevelTable,路径分支/形态定稿权归 关卡节点地图): +# 关卡域生命周期唯一编排者——共享 奖励道具经济 容器跨战斗延续(2026-09-11 关内延续裁决)、HP 跨战斗携带、 +# 奖励点/恢复点节点兑付、关卡末 level_clear 清库。Core 层零 UI 依赖(headless 可跑完整关卡——数据配置 Rule 7)。 +# 测试专用:hero_atk_pct/hero_hp_pct 数值覆盖(调整板模拟天赋树——正式侧归元进度,切片不落盘)。 # E4 收口(2026-09-13):覆盖值经 encounter 显式传入战斗(不再写 db.hero 表——表值运行期不可变, # 双基线同步/双写叠加 6→18→54 整类隐患随表写一并移除)。 -# 对应 GDD:C1 关卡节点地图(未设计——provisional 切片先行,定稿后回写;命名规范见 CLAUDE.md) +# 对应 GDD:关卡节点地图(未设计——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 step_row(row: Dictionary) # 转发当前 战斗结算 结算行 +signal window_opened(items: Array) # 转发当前 战斗结算 道具窗口 signal round_started(round_index: int) signal board_changed() signal level_finished(payload: Dictionary) # {result, hp, node_index, node_type, remaining_items} @@ -23,9 +23,9 @@ 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 容器(库存跨战斗延续) +# 协作对象私有(E1/E3 封装收口 2026-09-13):settlement/reward_economy 读取经 current_battle()/inventory() 成文出口 +var _settlement = null # 当前战斗编排器(关卡结束置 null) +var _reward_economy = null # 关卡域共享 奖励道具经济 容器(库存跨战斗延续) var hero_atk_pct := 0 # 测试面板覆盖值(切片专用) var hero_hp_pct := 0 @@ -38,7 +38,7 @@ func _init(db_ref, rng_ref) -> void: rng = rng_ref -# ---- 测试面板覆盖(模拟天赋树属性提升——仅测试用,正式侧归 C4;经 encounter 传入战斗)---- +# ---- 测试面板覆盖(模拟天赋树属性提升——仅测试用,正式侧归 元进度;经 encounter 传入战斗)---- func set_hero_override(atk_pct: int, hp_pct: int) -> void: hero_atk_pct = atk_pct @@ -64,14 +64,14 @@ func restore_hero_base() -> void: 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) + push_error("[关卡节点地图] 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) + _reward_economy = RewardEconomy.new(db, rng) + rng.new_adventure(seed_val) # 关卡起点 = 冒险起点(切片口径;跨关接续归 冒险结构) _running = true return _enter_next_node() @@ -99,15 +99,15 @@ func _enter_next_node() -> bool: node_index += 1 var node: Dictionary = current_node() if node.is_empty(): - push_error("[C1] 节点链越界(末节点 Boss 应已结束关卡)") + push_error("[关卡节点地图] 节点链越界(末节点 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) + var inst = _reward_economy.grant_node_active(Vector2i(-1, -1), 0) if not (inst is Dictionary): - push_error("[C1] 奖励点入库被拒") + push_error("[关卡节点地图] 奖励点入库被拒") return false node_entered.emit(node, {"snapshot": inst.snapshot}) return true @@ -115,25 +115,25 @@ func _enter_next_node() -> bool: 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)) + push_error("[关卡节点地图] 未知节点类型: %s" % str(node)) return false func _start_battle(node: Dictionary) -> bool: - _b1 = BattleOrchestrator.new(db, rng, _b4) # 共享 B4——库存跨战斗延续(关内延续裁决) + _settlement = BattleOrchestrator.new(db, rng, _reward_economy) # 共享 奖励道具经济——库存跨战斗延续(关内延续裁决) # 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 期间发出的 + # RefCounted 生命周期乃至成环:settlement 的信号持捕获 runner 的 lambda,runner 又持 settlement) + _settlement.step_row.connect(_forward_step_row) + _settlement.window_opened.connect(_forward_window_opened) + _settlement.round_started.connect(_forward_round_started) + _settlement.board_changed.connect(_forward_board_changed) + _settlement.battle_finished.connect(_on_battle_finished) + # node_entered 先于 begin_battle:呈现层须先拿到 settlement 引用,才能响应 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)), + if not _settlement.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))) + push_error("[关卡节点地图] 战斗启动失败(depth %s)" % str(node.get("node_depth", -1))) return false return true @@ -160,15 +160,15 @@ func _on_battle_finished(payload: Dictionary) -> void: 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)") + # Boss 胜利 = 关卡通关;任意战斗败北 = 关卡失败(Roguelike——冒险即终,重开归 冒险结构/元进度) + var left: int = _settlement.level_clear()["remaining"] # 库存随关卡结束清空(关内延续裁决③) + if _reward_economy.in_stock_count() != 0: + push_error("[关卡节点地图] 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 + _settlement = null level_finished.emit({"result": level_result, "hp": hp_left, "node_index": node_index, "node_type": String(node.get("type", "")), "remaining_items": left}) return @@ -187,11 +187,11 @@ func _apply_rest() -> int: # ---- 只读出口(E1/E3 封装收口:呈现层/工具经此读当前战斗与关卡库存,禁直读协作对象)---- func current_battle(): - return _b1 + return _settlement func inventory() -> Array: - return _b4.query_inventory() if _b4 != null else [] + return _reward_economy.query_inventory() if _reward_economy != null else [] # ---- 玩家输入转发(呈现层只与 runner 对话)---- @@ -205,11 +205,11 @@ func continue_after_node() -> bool: func submit_window_selection(selection: Array) -> void: - if _b1 != null: - _b1.submit_window_selection(selection) + if _settlement != null: + _settlement.submit_window_selection(selection) func player_request_refresh() -> bool: - if _b1 == null: + if _settlement == null: return false - return _b1.player_request_refresh() + return _settlement.player_request_refresh() diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd index 48989e79..6acfdf79 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd @@ -1,20 +1,20 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# A2 排雷玩法规则引擎:零随机/零表读取/零 UI——输入 = A1 产物 + 意图序列,输出 = 三事件流。 +# 排雷玩法规则引擎:零随机/零表读取/零 UI——输入 = 棋盘生成 产物 + 意图序列,输出 = 三事件流。 # 核心契约:install 四不变式 · 初始展开 = R₀ 恰等 · 连锁不越旗 · 批次语义序仲裁(cleared > stop > 其余)。 -# 实现 GDD:design/archive/gdd-2026-09-14/a2-minesweeper-rules.md(系统 ID A2——registry 追溯锚,命名规范见 CLAUDE.md) +# 实现 GDD:design/archive/gdd-2026-09-14/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 直发) +signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发) const SUB_REASONS := ["not_adjacent", "target_revealed", "target_concealed", "flag_present", "flag_absent", "no_path", "out_of_catalog"] var db -var a3 +var action_economy # 棋盘状态私有(E2 封装收口 2026-09-13):对外只暴露 current_board()/reveal_record() 等查询面, # 禁止跨模块直读/直写原始字典——先例教训 = board_view 绕过查询面直读 revealed。 var _board: BoardData = null @@ -24,9 +24,9 @@ var _hero := Vector2i.ZERO var _spawn: Dictionary = {} -func _init(db_ref, a3_ref) -> void: +func _init(db_ref, action_economy_ref) -> void: db = db_ref - a3 = a3_ref + action_economy = action_economy_ref # ---- 板态 ---- @@ -35,7 +35,7 @@ func has_board() -> bool: return _board != null -# 板产物只读出口(E2 收口——B1 快照 / 组合根 rebuild / 探针经此取板,禁再直读字段) +# 板产物只读出口(E2 收口——战斗结算 快照 / 组合根 rebuild / 探针经此取板,禁再直读字段) func current_board() -> BoardData: return _board @@ -73,20 +73,20 @@ func clear_map_cleared() -> bool: return safe_remaining() == 0 -# ---- install(编排边界相位 = A3 Idle/Waiting;Active 拒绝——防轮内换板)---- +# ---- install(编排边界相位 = 行动经济 Idle/Waiting;Active 拒绝——防轮内换板)---- -# during_round = 轮内刷新编排路径(2026-09-12 方案 A,B1 唯一调用方):Active 相位合法—— +# during_round = 轮内刷新编排路径(2026-09-12 方案 A,战斗结算 唯一调用方):Active 相位合法—— # 玩家挖掘中立即换图,棋盘状态(revealed/flags)随 install 重置,初始展开事件照发(initial 通道) func install_board(b: BoardData, during_round := false) -> Dictionary: - if a3.phase == a3.PHASE_ACTIVE and not during_round: + if action_economy.phase == action_economy.PHASE_ACTIVE and not during_round: return _outcome(false, "rejected", "phase", "") - if a3.phase == a3.PHASE_IDLE and during_round: + if action_economy.phase == action_economy.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)) + push_error("[排雷玩法] install 不变式违例: %s" % str(inv)) return _outcome(false, "rejected", "board_invalid", inv[0]) _board = b _revealed.clear() @@ -113,7 +113,7 @@ func flag_count() -> int: func retire_board(during_round := false) -> Dictionary: if _board == null: return _outcome(true, "executed", "", "") # 幂等受理 - if a3.phase == a3.PHASE_ACTIVE and not during_round: + if action_economy.phase == action_economy.PHASE_ACTIVE and not during_round: return _outcome(false, "rejected", "phase", "") _board = null _revealed.clear() @@ -148,7 +148,7 @@ func _install_invariant_faults(b: BoardData) -> Array: return faults -# 初始展开规范序:触发格 BFS、8 邻行主序(≡ A1 F1 R₀ 算子;禁外扩环、禁只开 3×3) +# 初始展开规范序:触发格 BFS、8 邻行主序(≡ 棋盘生成 F1 R₀ 算子;禁外扩环、禁只开 3×3) func _initial_expansion_order(b: BoardData) -> Array: var order: Array = b.spawn_cells().duplicate() var seen := {} @@ -172,7 +172,7 @@ func settle_batch(intents: Array) -> Array: var outcomes: Array = [] if intents.is_empty(): return outcomes - if a3.phase != a3.PHASE_ACTIVE or _board == null: + if action_economy.phase != action_economy.PHASE_ACTIVE or _board == null: for intent in intents: outcomes.append(_outcome(false, "rejected", "phase", "")) return outcomes @@ -191,7 +191,7 @@ func settle_batch(intents: Array) -> Array: continue var res := _execute(item.intent) outcomes.append(res) - if a3.phase != a3.PHASE_ACTIVE: + if action_economy.phase != action_economy.PHASE_ACTIVE: round_over = true return outcomes @@ -202,7 +202,7 @@ func _classify(intent: Dictionary) -> int: 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 chain := _chain_closure(c, _flags) # 与真实执行同口径——空旗集捷径会在旗挡连锁时误判 C_dig(排雷玩法:真模拟) var remaining := 0 for sc in _board.safe_cells(): if not _revealed.has(sc) and not chain.has(sc): @@ -227,7 +227,7 @@ func _execute(intent: Dictionary) -> Dictionary: return _do_move(intent.get("cell", Vector2i(-9, -9))) "stop": return _do_stop() - push_warning("[A2] 目录外意图: %s" % str(intent)) + push_warning("[排雷玩法] 目录外意图: %s" % str(intent)) return _outcome(false, "rejected", "board_face", "out_of_catalog") @@ -245,8 +245,8 @@ func _do_dig(c: Vector2i) -> Dictionary: 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): + # 资源面/相位 + cleared 仲裁在 行动经济 事务内原子完成 + if not action_economy.request_dig(chain_size, clears): return _outcome(false, "rejected", "resource_face", "") # 事务内执行:首格 + 连锁新格揭示(BFS 规范序,含链开雾格——零特判);事件 = O′ ∖ O var reveal_order: Array = _chain_reveal_order(c, _flags) @@ -267,7 +267,7 @@ func _do_dig(c: Vector2i) -> Dictionary: "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: + if action_economy.phase != action_economy.PHASE_ACTIVE: _emit_round_end_flags_and_payload() return _outcome(true, "executed", "", "") @@ -335,12 +335,12 @@ func _do_flag_place(c: Vector2i) -> Dictionary: sub = "flag_present" if sub != "": return _outcome(false, "rejected", "board_face", sub) - if not a3.try_spend("flag_place"): + if not action_economy.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 缺陷②,行为探针复现) + if action_economy.phase != action_economy.PHASE_ACTIVE: + # 旗动作花完末点 AP 同样终结轮次(行动经济 三出口——AP 用完与动作种类无关;缺漏使 round_end + # 永不发出、战斗结算 的 Waiting 结算点永不到达 = 整局软锁。playtest 缺陷②,行为探针复现) _emit_round_end_flags_and_payload() return _outcome(true, "executed", "", "") @@ -355,10 +355,10 @@ func _do_flag_pull(c: Vector2i) -> Dictionary: sub = "flag_absent" if sub != "": return _outcome(false, "rejected", "board_face", sub) - if not a3.try_spend("flag_pull"): + if not action_economy.try_spend("flag_pull"): return _outcome(false, "rejected", "resource_face", "") _flags.erase(c) - if a3.phase != a3.PHASE_ACTIVE: + if action_economy.phase != action_economy.PHASE_ACTIVE: _emit_round_end_flags_and_payload() # 同 flag_place——末点 AP 轮末发射(playtest 缺陷②) return _outcome(true, "executed", "", "") @@ -384,7 +384,7 @@ func compute_move_path(target: Vector2i) -> Array: return [] if target == _hero: return [] - # BFS(8 邻行主序)——路径 = 合法性载体 + D1 预览数据源 + # BFS(8 邻行主序)——路径 = 合法性载体 + HUD框架预览数据源 var prev := {_hero: _hero} var queue: Array = [_hero] while not queue.is_empty(): @@ -405,20 +405,20 @@ func compute_move_path(target: Vector2i) -> Array: return path -# ---- 停手(经批次入口委托 A3;先发旗判定批再委托——事件序契约 flag_judged < round_end 全路径统一)---- +# ---- 停手(经批次入口委托 行动经济;先发旗判定批再委托——事件序契约 flag_judged < round_end 全路径统一)---- func _do_stop() -> Dictionary: _emit_flags_batch() # 基线 = 事务开始旗位(停手不改旗位) - if not a3.request_stop(): + if not action_economy.request_stop(): return _outcome(false, "rejected", "phase", "") - # A3 直发 stopped 路径 round_end;A2 禁双发(双发射源纪律) + # 行动经济 直发 stopped 路径 round_end;排雷玩法 禁双发(双发射源纪律) 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: @@ -427,7 +427,7 @@ func _emit_flags_batch() -> void: func _emit_round_end_flags_and_payload() -> void: _emit_flags_batch() - var payload: Dictionary = a3.last_round_end # 逐字段取自 A3(读取权 = 接口契约) + var payload: Dictionary = action_economy.last_round_end # 逐字段取自 行动经济(读取权 = 接口契约) round_end_board.emit({"path_tag": payload.get("path_tag", ""), "continuation_hint": payload.get("continuation_hint", "")}) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd b/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd index 0fb30317..9dc4f69f 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/reward_economy.gd @@ -1,14 +1,14 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 可选类供给能否按"格极稀 / 板≈0.78 / 战斗期望≈1(首件保证 + 指数递减)"三尺度被精确控制 # Date: 2026-09-10 -# B4 奖励与道具经济:install 期身份预分配(roll_board 恰一次、消费 reward_roll 封闭流恰一 scope 批)· +# 奖励道具经济: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/呈现零改动;授予期零随机不变。 +# (④ 步,既有取号序零扰动);快照 E14 口径不变——快照 power = roll 值,战斗结算/呈现零改动;授予期零随机不变。 # 切片登记:取号约定 = randi_range(0, W-1)(实现期 ADR 事实,切片内恒定)。 -# 实现 GDD:design/archive/gdd-2026-09-14/b4-reward-item-economy.md(系统 ID B4——registry 追溯锚,命名规范见 CLAUDE.md) +# 实现 GDD:design/archive/gdd-2026-09-14/b4-reward-item-economy.md(系统 ID B4·奖励道具经济——registry 追溯锚,命名规范见 CLAUDE.md) class_name RewardEconomy extends RefCounted @@ -34,7 +34,7 @@ func _init(db_ref, rng_ref) -> void: _supply = RewardSupplyController.new(db.reward_config) -# ---- 战斗开始(B1.begin_battle 编排调用——B4 唯一调用方纪律不变)---- +# ---- 战斗开始(战斗结算.begin_battle 编排调用——奖励道具经济 唯一调用方纪律不变)---- # 供给控制器战斗域计数清零;库存(关卡域)不受影响——关内延续照旧 func notify_battle_start() -> void: _supply.notify_battle_start() @@ -45,18 +45,18 @@ func notify_battle_start() -> void: func roll_board(eligible: Array) -> bool: # 相位守卫:Ready 再调 = double-roll 拒绝(E5);Retired(换图后)= 合法新批重置(E6 roll 半) if _phase == "ready": - push_warning("[B4] roll_board 相位守卫拒绝(E5——double-roll 编排违约显式暴露)") + push_warning("[奖励道具经济] roll_board 相位守卫拒绝(E5——double-roll 编排违约显式暴露)") return false if eligible.is_empty(): - _fatal("roll_board 空资格清单(E1 兜底 fail-fast——A2 不变式④前置)") + _fatal("roll_board 空资格清单(E1 兜底 fail-fast——排雷玩法 不变式④前置)") 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)) + push_warning("[奖励道具经济] 资格清单重复格已去重: %s" % str(c)) continue seen[c] = true list.append({"cell": c, "fog": bool(e.get("fog", false))}) @@ -86,7 +86,7 @@ func roll_board(eligible: Array) -> bool: 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/呈现零改动 + # 授予期零随机纪律不变——快照 E14 口径,战斗结算/呈现零改动 for cell in draft.keys(): var row: Dictionary = _row_by_id(draft[cell]) if row.has("power_min"): @@ -100,7 +100,7 @@ func roll_board(eligible: Array) -> bool: return true -# ---- 关卡节点奖励(2026-09-11 关卡扩展批——线性奖励点,正式归属 B4/C1 修订)---- +# ---- 关卡节点奖励(2026-09-11 关卡扩展批——线性奖励点,正式归属 奖励道具经济/关卡节点地图 修订)---- # 奖励点 = 用户拍板的关卡节点类型:在本表全部可选类活行(damage_item / heal_pack,即"主动道具") # 中 roll 一件并直接入库。消耗 reward_roll 恰一 scope(与 board install 批同流异 scope——流集合封闭不变)。 # cell=(-1,-1)/round_index=0 = 非格子来源标记(关卡节点授予,区别于安全格拾取的呈现面)。 @@ -139,7 +139,7 @@ func query_identity(cell: Vector2i): # 身份完整快照(轮内刷新批次隔离用——facts 缓冲期捕获,授予期 mapping 可能已换批): -# 返回与 grant 相同结构的快照(display_name/power/effect_id 齐备),供 B1 冻结授予直通 +# 返回与 grant 相同结构的快照(display_name/power/effect_id 齐备),供 战斗结算 冻结授予直通 func query_full_snapshot(cell: Vector2i): if _phase != "ready": _diag("query_full_snapshot 非 Ready 相位") @@ -150,7 +150,7 @@ func query_full_snapshot(cell: Vector2i): return _snapshot(_row_by_id(rid), int(_power_roll.get(cell, -1))) -# ---- 受理面(B1 编排期调用;E6 收口 2026-09-13:失败统一返回 null——此前 false/null 混用)---- +# ---- 受理面(战斗结算 编排期调用;E6 收口 2026-09-13:失败统一返回 null——此前 false/null 混用)---- func grant(cell: Vector2i): if _phase != "ready": @@ -160,7 +160,7 @@ func grant(cell: Vector2i): _diag("grant 清单外坐标(E19)") return null if _granted.has(cell): - _diag("grant 同格重复受理(E18 幂等兜底——受理恰一次归 B1)") + _diag("grant 同格重复受理(E18 幂等兜底——受理恰一次归 战斗结算)") return _granted[cell] var row: Dictionary = _row_by_id(_mapping[cell]) var snap := _snapshot(row, int(_power_roll.get(cell, -1))) @@ -202,7 +202,7 @@ func stock(snapshot: Dictionary, source_cell: Vector2i, round_index: int): return inst -# 窗口选用消耗:零游戏态守卫(照常消耗——E10;满血/零效果守卫归 B1) +# 窗口选用消耗:零游戏态守卫(照常消耗——E10;满血/零效果守卫归 战斗结算) func use(index: int): if _inventory_cleared: _diag("use 战斗已清库") @@ -275,7 +275,7 @@ func retire_mapping() -> bool: return true -# ---- F6 当量供给(B1 条款①对照实物输入)---- +# ---- F6 当量供给(战斗结算 条款①对照实物输入)---- func equivalence_supply() -> Dictionary: var live: Array = [] @@ -339,9 +339,9 @@ func _snapshot(row: Dictionary, rolled_power: int = -1) -> Dictionary: func _diag(msg: String) -> void: - push_warning("[B4] " + msg) + push_warning("[奖励道具经济] " + msg) func _fatal(msg: String) -> void: - push_error("[B4/fatal] " + msg) + push_error("[奖励道具经济/fatal] " + msg) assert(false, msg) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd b/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd index 0d41ebec..de830173 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/reward_supply_controller.gd @@ -1,12 +1,12 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 可选类供给能否按"格极稀 <0.01 / 板≈0.78 / 战斗期望≈1(首件保证 + 指数递减)"三尺度被精确控制 # Date: 2026-09-11 -# 可选类供给控制器(2026-09-11 用户拍板批——B4 私有组合件): +# 可选类供给控制器(2026-09-11 用户拍板批——奖励道具经济 私有组合件): # 格子权重池不再产出可选类;本控制器在 install 期决定"本板是否注入 1 件可选类"—— # 战斗内尚未获取首件 → 本板必注入(首件保证);已获取 j 件 → 概率 p0×decay^(j-1)(指数下降、永不绝育)。 -# 行值快照(p0/decay 构造期快照——抗热重载,B2 同款);零自有随机数(判定取号经 B4 传入的 reward_roll -# 封闭流实例——Rule 5);"获取"以实际发放入库为准(预分配未挖到不计数——授予侧经 B4.grant 挂钩上报)。 -# 实现 GDD:design/archive/gdd-2026-09-14/b4-reward-item-economy.md(系统 ID B4——registry 追溯锚,命名规范见 CLAUDE.md) +# 行值快照(p0/decay 构造期快照——抗热重载,敌人系统 同款);零自有随机数(判定取号经 奖励道具经济 传入的 reward_roll +# 封闭流实例——Rule 5);"获取"以实际发放入库为准(预分配未挖到不计数——授予侧经 奖励道具经济.grant 挂钩上报)。 +# 实现 GDD:design/archive/gdd-2026-09-14/b4-reward-item-economy.md(系统 ID B4·奖励道具经济——registry 追溯锚,命名规范见 CLAUDE.md) class_name RewardSupplyController extends RefCounted @@ -20,12 +20,12 @@ func _init(reward_config) -> void: _decay = clampf(float(reward_config.active_decay), 0.001, 1.0) -# B1.begin_battle 编排调用(B4 转发)——战斗域计数重置;跨战斗库存延续不受影响 +# 战斗结算.begin_battle 编排调用(奖励道具经济 转发)——战斗域计数重置;跨战斗库存延续不受影响 func notify_battle_start() -> void: _granted = 0 -# install 期注入判定(B4.roll_board 在身份批 + 清图槽之后调用;取号 = 传入封闭流恰一次) +# install 期注入判定(奖励道具经济.roll_board 在身份批 + 清图槽之后调用;取号 = 传入封闭流恰一次) func should_inject(stream: RandomNumberGenerator) -> bool: if _granted == 0: return true # 首件保证 @@ -33,7 +33,7 @@ func should_inject(stream: RandomNumberGenerator) -> bool: return stream.randf() < p -# B4.grant 实际发放可选类时上报(发放即获取;满血拒"使用"不影响——那是消耗侧语义) +# 奖励道具经济.grant 实际发放可选类时上报(发放即获取;满血拒"使用"不影响——那是消耗侧语义) func notify_granted() -> void: _granted += 1 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd index e07be0de..3f290277 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd @@ -1,24 +1,24 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 RngService autoload——封闭命名流单点(Rule 5)。玩法代码禁止 randomize()/自建 RandomNumberGenerator。 -# 实现 GDD:design/archive/gdd-2026-09-14/d2-data-config.md(系统 ID D2——registry 追溯锚,命名规范见 CLAUDE.md) +# RngService autoload(数据配置)——封闭命名流单点(Rule 5)。玩法代码禁止 randomize()/自建 RandomNumberGenerator。 +# 实现 GDD:design/archive/gdd-2026-09-14/d2-data-config.md(系统 ID D2·数据配置——registry 追溯锚,命名规范见 CLAUDE.md) # R2 派生公式(黄金向量已预验证):derive = (SALT XOR wrap64(master + scope×GOLDEN)) AND 0x7FFF… -# 取号语义:全新实例首个 open_stream = scope 0(B4 预推进术语的登记口径)。 +# 取号语义:全新实例首个 open_stream = scope 0(奖励道具经济 预推进术语的登记口径)。 extends Node const STREAM_NAMES := ["board_gen", "reward_roll", "encounter"] # 2026-09-12 增 encounter(遭遇随机化批,用户拍板——registry rng_service 条目同步) -# 分段表达式书写(D2 R2 实现级规约——禁 ≥2^63 完整字面量) +# 分段表达式书写(数据配置 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 SALT_ENCOUNTER := (0x8EBC6AF0 << 32) | 0x9C88C6E3 # 0x8EBC6AF09C88C6E3(遭遇骰——registry stream_seed_derivation 权威值;旧值 0x5DEECE66D<<32 超 int64 被截断,黄金向量漂移) const OUT_MASK := 0x7FFFFFFFFFFFFFFF const INT64_MIN := -9223372036854775808 # master_seed 只读出口(E5 封装收口 2026-09-13):写入只经 set_master_seed(计数器全零断言 -# 不可绕过);外部读 master_seed 供 D3 快照契约观测。 +# 不可绕过);外部读 master_seed 供 存档序列化 快照契约观测。 var _master_seed: int = -1 # -1 = 未播种(取数即致命,EC7) var master_seed: int: get: return _master_seed @@ -60,7 +60,7 @@ func set_master_seed(seed_val: int) -> void: _counters["reward_roll"] = 0 -# 切片登记:冒险开始重播种入口(正式侧调用权归 C2;切片主菜单 = 冒险起点) +# 切片登记:冒险开始重播种入口(正式侧调用权归 冒险结构;切片主菜单 = 冒险起点) func new_adventure(seed_val: int) -> void: _counters["board_gen"] = 0 _counters["reward_roll"] = 0 @@ -74,7 +74,7 @@ func open_stream(stream: String) -> RandomNumberGenerator: push_error("[RngService] 未知名流: %s" % stream) assert(false, "未知名流(EC12)") if _master_seed < 0: - push_error("[RngService] 未设 master_seed 即取数(EC7——C2 未接线的显式暴露)") + push_error("[RngService] 未设 master_seed 即取数(EC7——冒险结构 未接线的显式暴露)") assert(false, "未播种流取数致命(EC7)") var scope: int = _counters[stream] _counters[stream] = scope + 1 @@ -83,6 +83,6 @@ func open_stream(stream: String) -> RandomNumberGenerator: return rg -# 只读计数器(D3 快照契约的观测面) +# 只读计数器(存档序列化 快照契约的观测面) func counter(stream: String) -> int: return _counters[stream] diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd b/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd index 3865b0b4..2e52f45c 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/status_container.gd @@ -1,10 +1,10 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# B3 统一状态容器:三向效果同构实例 · 单一时限量纲 remaining_settlements(先兑付后衰减)· +# 增益状态·统一状态容器:三向效果同构实例 · 单一时限量纲 remaining_settlements(先兑付后衰减)· # 按行叠层(stack 授予不触碰 R)· 授予时数值快照抗热重载 · 零随机零信号。 -# 注:MVP 单敌假设经 B2 回验不成立(定多敌)——enemy 侧 side-wide 语义按 registry 改写执行。 -# 实现 GDD:design/archive/gdd-2026-09-14/b3-status-effect.md(系统 ID B3——registry 追溯锚,命名规范见 CLAUDE.md) +# 注:MVP 单敌假设经 敌人系统 回验不成立(定多敌)——enemy 侧 side-wide 语义按 registry 改写执行。 +# 实现 GDD:design/archive/gdd-2026-09-14/b3-status-effect.md(系统 ID B3·增益状态——registry 追溯锚,命名规范见 CLAUDE.md) class_name StatusContainer extends RefCounted @@ -19,21 +19,21 @@ func _init(db_ref) -> void: func notify_battle_start() -> bool: if _open: - push_warning("[B3] Active 下重复 notify_battle_start(EC11)") + push_warning("[增益状态] 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)") + push_warning("[增益状态] 域闭授予(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) + push_warning("[增益状态] 未知 effect_id(EC2): %s" % effect_id) return false var policy: String = row.stack_policy var dur: int = int(row.duration_settlements) @@ -57,12 +57,12 @@ func apply_effect(effect_id: String, source: Dictionary) -> bool: for inst in _instances: if inst.effect_id == effect_id: if inst.layers >= max_stack: - return false # 满层拒绝原子——容器零写入(溢出行归 B1) + return false # 满层拒绝原子——容器零写入(溢出行归 战斗结算) 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") + push_warning("[增益状态] 未知 stack_policy") return false @@ -99,12 +99,12 @@ func query_active(side: String) -> Array: "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) + "effective_value": inst.layers * inst.snap_m, # L×m 加算(percent 越域不 clamp——收口归 战斗结算) }) return out -# 池层数读值(B1 战斗段伏击池 L 的消费面) +# 池层数读值(战斗结算·战斗段伏击池 L 的消费面) func pool_layers(effect_id: String) -> int: for inst in _instances: if inst.effect_id == effect_id: @@ -112,7 +112,7 @@ func pool_layers(effect_id: String) -> int: return 0 -# 轮末衰减:先兑付后衰减;每实例恰 −1;归 0 移除;返回移除数 k(恰一次执行由 B1 编排信任承载) +# 轮末衰减:先兑付后衰减;每实例恰 −1;归 0 移除;返回移除数 k(恰一次执行由 战斗结算 编排信任承载) func decay_round() -> int: if not _open: return 0 # EC13 域闭兜底读法——容器恒空,无独立分支不诊断 @@ -130,7 +130,7 @@ func decay_round() -> int: func notify_battle_end() -> bool: if not _open: - push_warning("[B3] BattleClosed 下重复 notify_battle_end(EC11)") + push_warning("[增益状态] BattleClosed 下重复 notify_battle_end(EC11)") return false _open = false _instances.clear() # 全清不跨战斗 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd b/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd index 14dd1eb7..529e53a3 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/unit_container.gd @@ -1,11 +1,11 @@ # 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/archive/gdd-2026-09-14/b2-enemy-system.md(系统 ID B2——registry 追溯锚,命名规范见 CLAUDE.md) +# 敌人系统·战斗单位容器:hero/enemy 统一受伤死亡接口 · 行为环确定性轮转(死亡即不动手/先作用后推进)· +# 行值快照抗热重载 · side-wide 伏击池读值由调用方注入(敌人系统 无池写面)· 编排被动零事件。 +# 切片登记接口漂移:heal() 为切片新增(战斗结算 治疗 clamp 需要写入口;敌人系统 API 封闭枚举原本无治疗面—— +# 正式侧应走 敌人系统 GDD 修订,切片以头注登记)。 +# 实现 GDD:design/archive/gdd-2026-09-14/b2-enemy-system.md(系统 ID B2·敌人系统——registry 追溯锚,命名规范见 CLAUDE.md) class_name UnitContainer extends RefCounted @@ -25,35 +25,35 @@ func _init(db_ref) -> void: # 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 零随机纪律保持。 +# 接口漂移登记:spawn 签名 +2 参数(敌人系统 GDD API 封闭枚举——正式侧 GDD/registry 修订批随 ADR-0009)。 +# enemy_atk_override(2026-09-14 敌方攻击区间批):战斗结算 遭遇骰同流的 spawn 期整战持久 roll 结果 +# (-1 = 表值定值路径;≥0 = 行值替换后再乘关卡 atk_mult——修饰语义不变)。敌人系统 零随机纪律保持。 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(相位守卫)") + push_warning("[敌人系统] 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]) + push_error("[敌人系统] 遭遇行解析失败/越域 (%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) + push_warning("[敌人系统] 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) + _enemies.clear() # 难度曲线与Boss 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 = 行值定值路径) + # unit_count_override ≥1 = 战斗结算 遭遇骰结果(敌人系统 零随机纪律——收到即确定值;-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) + push_warning("[敌人系统] 实例化数量越域: %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) @@ -67,7 +67,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_override: "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 战斗段) + "armor_type": String(row.get("armor_type", "none")), # 护甲类型快照(克制矩阵消费面——战斗结算·战斗段) "steps": 0, "alive": true, "is_hero": false, @@ -126,11 +126,11 @@ func apply_damage(u: Dictionary, n: int) -> bool: return false u.hp = maxi(0, u.hp - n) if u.hp == 0: - u.alive = false # Dead 不可逆(死亡判定时机归 B1) + u.alive = false # Dead 不可逆(死亡判定时机归 战斗结算) return true -# 治疗(切片登记接口漂移——B1 治疗 clamp 的写入口):返回实际回复量 +# 治疗(切片登记接口漂移——战斗结算 治疗 clamp 的写入口):返回实际回复量 func heal(u: Dictionary, n: int) -> int: if n < 0 or u.is_empty() or _despawned or not u.alive: _diag("heal 非法/失效句柄") @@ -174,7 +174,7 @@ func all_enemies() -> Array: return _enemies.duplicate() -# F3 机制算术纯函数(B1 预截流消费面):cap 判定在授予前;守恒 granted + overflow = mines_r +# F3 机制算术纯函数(战斗结算 预截流消费面):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} @@ -191,4 +191,4 @@ func despawn_all() -> bool: func _diag(msg: String) -> void: - push_warning("[B2] " + msg) + push_warning("[敌人系统] " + msg) 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 index c2923202..c11e1539 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 ActionEconomyTable schema(A3 回填值,单行全局;Rule 4 豁免)。 +# 数据配置 ActionEconomyTable schema(行动经济 回填值,单行全局;Rule 4 豁免)。 # 数值域与跨字段校验依据 design/archive/gdd-2026-09-14/a3-action-economy.md "ActionEconomyTable Schema 定稿"。 class_name ActionEconomyTable extends Resource @@ -34,7 +34,7 @@ func validate() -> Array: 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,违规加载期致命 + # 跨字段(行动经济 三轮评审增补):任一动作成本 ≤ 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]}) 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 index 8de93d94..12484743 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/board_gen_table.gd @@ -1,16 +1,16 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 BoardGenTable schema(A1 回填值;键 (level_index, node_depth)——首个进度键表)。 +# 数据配置 BoardGenTable schema(棋盘生成 回填值;键 (level_index, node_depth)——首个进度键表)。 # 依据 design/archive/gdd-2026-09-14/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 FOG_RATIO_ABS_CAP := 0.25 # 战斗结算 修订——雾占比绝对上限 const COST_BUDGET := 16384 # F2c provisional 量级护栏(禁墙钟逃生) -const TARGET_ABS_FLOOR := 0.85 # B1 修订——target 域下限(P1 绝对锚) +const TARGET_ABS_FLOOR := 0.85 # 战斗结算 修订——target 域下限(P1 绝对锚) func validate() -> Array: @@ -53,7 +53,7 @@ func validate() -> Array: 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 修订) + # F2 规范合取形 + 雾占比绝对上限(战斗结算 修订) 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]}) @@ -69,7 +69,7 @@ func validate() -> Array: 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]}) + errs.append({"category": "cross_field", "message": "(%d,%d) board_biomes 非空域校验违例(地形变异⑮)" % [L, D]}) anchor_ok[L] = anchor_ok.get(L, false) or D == 0 for L in anchor_ok.keys(): if not anchor_ok[L]: diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd index d0f43d1f..8e26cb25 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/difficulty_table.gd @@ -1,8 +1,8 @@ # 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 直填行不走本表。 +# 数据配置 DifficultyTable schema(难度曲线与Boss GDD Core Rule 1 兑现——敌人系统 Rule 10 "接口未定 provisional" 的建表兑现,走 数据配置 接口漂移规则)。 +# 全局修饰结构位:按 (L,D) 精确匹配施加乘数于 EnemyTable 行值(敌人系统.spawn 实例化时、行值快照前)。 +# MVP 空表(零行为——AC-10);首个使用场景 = 精英节点(关卡节点地图 定稿联动,难度曲线与Boss OQ5);Boss 走 Dmax 直填行不走本表。 class_name DifficultyTable extends Resource @@ -24,7 +24,7 @@ func validate() -> Array: seen[key] = true if L < 1 or D < 0: errs.append({"category": "domain", "message": "难度键越域 (%d,%d)" % [L, D]}) - # 乘数越域 = warning 级(使用时 clamp——组合事故非配置事故,C3 GDD Edge Case 预登记) + # 乘数越域 = warning 级(使用时 clamp——组合事故非配置事故,难度曲线与Boss 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: diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd index 94b788e0..042143c5 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/enemy_table.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 EnemyTable / HeroTable schema(B2 回填值)。EnemyTable 键 (L,D,enemy_id),每行集恰一敌种; +# 数据配置 EnemyTable / HeroTable schema(敌人系统 回填值)。EnemyTable 键 (L,D,enemy_id),每行集恰一敌种; # HeroTable 单行全局(Rule 4 豁免)。依据 design/archive/gdd-2026-09-14/b2-enemy-system.md Schema 节。 class_name EnemyTable extends Resource @@ -39,7 +39,7 @@ func validate() -> Array: 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],缺省 = 定值 + # unit_count_max(可省略——遭遇随机化批,难度曲线与Boss 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: @@ -51,7 +51,7 @@ func validate() -> Array: 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) + # 中点锚 = 行动经济 条款①同款期望当量纪律(调区间必同步调 atk) var has_amin: bool = row.has("atk_min") var has_amax: bool = row.has("atk_max") if has_amin != has_amax: diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd index 11826b60..141ec95b 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/hero_table.gd @@ -1,13 +1,13 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 HeroTable schema(B2 接口漂移新增;单行全局,Rule 4 豁免)。 +# 数据配置 HeroTable schema(敌人系统 接口漂移新增;单行全局,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 战斗段先手攻击 +@export var atk: int = 6 # [1,999] knob——基础攻击(溯源 = 概念原型 HERO_BASE_ATK,provisional 归 难度曲线与Boss);消费面 = 战斗结算·战斗段先手攻击 func validate() -> Array: diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd index 7e633c2f..802f6330 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/level_table.gd @@ -1,8 +1,8 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 线性第一关(战×2→道具→战×2→恢复→Boss)能否承载"数值提升→通关期望改善"的元进度验证 # Date: 2026-09-11 -# D2 LevelTable schema(provisional——C1 关卡节点地图未设计,本表为线性切片形态的结构位: -# nodes 数组即未来节点链的数据承载面;路径分支/奖励点分布的定稿权归 C1/C3)。 +# 数据配置 LevelTable schema(provisional——关卡节点地图未设计,本表为线性切片形态的结构位: +# nodes 数组即未来节点链的数据承载面;路径分支/奖励点分布的定稿权归 关卡节点地图/难度曲线与Boss)。 # 节点类型 closed enum:battle(战斗,须带 node_depth)/ reward(随机主动道具点)/ rest(血量恢复点)/ boss(关底 Boss,须带 node_depth)。 class_name LevelTable extends Resource @@ -51,7 +51,7 @@ func validate() -> Array: 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 关卡定义) + # 线性切片纪律:末节点必须是 Boss(关底收尾——冒险结构 关卡定义) 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/reward_config_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd index 46baf2c3..c2853235 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_config_table.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 RewardConfigTable schema(B4 full 评审接口漂移新增;单行全局——B4 标量 knob 的 Rule 1 载体)。 +# 数据配置 RewardConfigTable schema(奖励道具经济 full 评审接口漂移新增;单行全局——奖励道具经济 标量 knob 的 Rule 1 载体)。 class_name RewardConfigTable extends Resource diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd index 56f248d2..2b9ccd9c 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/reward_table.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 RewardTable / RewardConfigTable schema(B4 回填值)。 +# 数据配置 RewardTable / RewardConfigTable schema(奖励道具经济 回填值)。 # RewardTable 行 = Dictionary,键 reward_id(Rule 4 豁免:定义表);跨字段校验族 + 池护栏。 class_name RewardTable extends Resource @@ -68,6 +68,6 @@ func validate() -> Array: if cat == "buff": errs.append({"category": "cross_field", "message": "%s buff 行禁区间字段(power 恒 0)" % rid}) if power != int((pmin + pmax) / 2.0): - # 中点锚:power = 区间中点(A3 条款①期望当量锚——调区间必同步调 power) + # 中点锚:power = 区间中点(行动经济 条款①期望当量锚——调区间必同步调 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/settlement_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd index 51876855..b9b20a78 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/settlement_table.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 SettlementTable schema(B1 回填值;单行全局,Rule 4 豁免)——八字段口径。 +# 数据配置 SettlementTable schema(战斗结算 回填值;单行全局,Rule 4 豁免)——八字段口径。 # 三行致命 manifest:segorder_violation / poolmap_dangling / pperfect_oob。 class_name SettlementTable extends Resource 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 index aa1f9c7e..721a2d93 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/status_effect_table.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 StatusEffectTable schema(B3 回填值;行 = Dictionary,键 effect_id;Rule 4 豁免)。 +# 数据配置 StatusEffectTable schema(增益状态 回填值;行 = Dictionary,键 effect_id;Rule 4 豁免)。 # 依据 design/archive/gdd-2026-09-14/b3-status-effect.md "StatusEffectTable Schema 定稿"。 class_name StatusEffectTable extends Resource diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd index 0600ce70..af512c15 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/variation_table.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# D2 VariationTable schema(A4 回填值;行 = Dictionary,键 variant_id)。 +# 数据配置 VariationTable schema(地形变异 回填值;行 = Dictionary,键 variant_id)。 # 依据 design/archive/gdd-2026-09-14/a4-terrain-variation.md "VariationTable Schema 定稿"。 class_name VariationTable extends Resource @@ -38,7 +38,7 @@ func validate() -> Array: 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 + # 跨字段(地形变异 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 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd index 77832fda..5d7ce125 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/debug_session.gd @@ -1,7 +1,7 @@ # 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)。 +# 职责边界:层序/携带 HP/共享 奖励道具经济 状态 + 战斗结算 生命周期编排(创建 / begin_battle / 关卡末 level_clear)。 # 授权先例:与 tune_sim_controller 同款(code-review 2026-09-12 批)——控制器承接模式编排, # 组合根仍持有全部信号接线(battle_created 先于 begin_battle 发出,ADR-0001 接线唯一归组合根) # 与终局 UI 决策;玩家意图写调用(settle_batch / 窗口提交 / 刷新)不经本类。 @@ -10,8 +10,8 @@ # 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 棋盘 + 刷新呈现 +signal battle_created(settlement, level: int, carry_hp: int) # begin_battle 前——组合根完成信号接线(ADR-0001) +signal battle_ready(settlement, level: int, carry_hp: int) # begin_battle 完成后——组合根 rebuild 棋盘 + 刷新呈现 var db var rng # /root/RngService(组合根注入) @@ -22,8 +22,8 @@ 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 接手) +var _shared_reward_economy = null +var settlement = null # 当前战斗编排器(组合根在 battle_created 接线、battle_ready 接手) func _init(db_ref, rng_ref, hero_override: Callable, seed_value: Callable) -> void: @@ -41,18 +41,18 @@ func is_level_mode() -> bool: # 无库存延续、standalone 终局语义保留供探针战斗收尾) func start_single(level: int) -> bool: _level_mode = false - _shared_b4 = null + _shared_reward_economy = null return _start_battle(level, -1) -# 连续两层(调试入口——共享 B4 + HP 携带;首层满血开局) +# 连续两层(调试入口——共享 奖励道具经济 + 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) + _shared_reward_economy = RewardEconomy.new(db, rng) return _start_battle(_level_battles[0], -1) @@ -69,13 +69,13 @@ func on_battle_finished(payload: Dictionary) -> Dictionary: _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()} + "items_left": settlement.stock_count()} var remaining := 0 var is_level_end := result == "victory" and _level_mode if is_level_end: - remaining = int(b1.level_clear()["remaining"]) # 库存随关卡结束清空(关内延续裁决③) + remaining = int(settlement.level_clear()["remaining"]) # 库存随关卡结束清空(关内延续裁决③) elif _level_mode: - b1.level_clear() # 失败同样清库(关卡域生命周期闭合——调试模式与 runner 模式同规) + settlement.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} @@ -83,20 +83,20 @@ func on_battle_finished(payload: Dictionary) -> Dictionary: # 会话复位(组合根终局出口"返回开始界面"调用) func reset() -> void: _level_mode = false - _shared_b4 = null - b1 = null + _shared_reward_economy = null + settlement = 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) + settlement = BattleOrchestrator.new(db, rng, _shared_reward_economy if _level_mode else null) + battle_created.emit(settlement, 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, + if not settlement.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) + battle_ready.emit(settlement, level, carry_hp) return true diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd index 01632ecf..ce376b49 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd @@ -1,14 +1,14 @@ # VERTICAL SLICE - NOT FOR PRODUCTION -# 实现 GDD:design/ux/battle.md(五区呈现装配器)——读核心只读面(B1 呈现快照 / runner)→ +# 实现 GDD:design/ux/battle.md(五区呈现装配器)——读核心只读面(战斗结算 呈现快照 / runner)→ # 组装文案/投影 → 驱动各视图 setter。 # 高内聚边界:"核心状态 → 呈现文案"的全部翻译集中于此;视图保持零核心依赖; # 静态无状态——呈现不持久化任何核心句柄。 -# E1 封装收口(2026-09-13):核心状态读取统一经 B1.presentation_snapshot() 成文快照面—— -# 禁再链式穿透 b1.b2.hero / b1.b2.enemies / b1.b3.query_active 一类路径(B1 协作对象已私有化, +# E1 封装收口(2026-09-13):核心状态读取统一经 战斗结算.presentation_snapshot() 成文快照面—— +# 禁再链式穿透 settlement.enemy_system.hero / settlement.enemy_system.enemies / settlement.status_effects.query_active 一类路径(战斗结算 协作对象已私有化, # 敌人含死亡残位的投影由快照 enemies[] 承载,含 alive/beat/ring 字段)。 -# 更新触发注记(battle.md Data Requirements):旗/拔/移动成功反馈零事件外发(A2 发射面封闭)—— +# 更新触发注记(battle.md Data Requirements):旗/拔/移动成功反馈零事件外发(排雷玩法 发射面封闭)—— # 经 ActionOutcome 返回值命令式驱动;AP/RAP 无推送信号——组合根每次调用返回后重查; -# B3 纯被动容器零信号——状态栏刷新绑 B1 结算编排完成时点;库存 B4 查询面——写路径回执后重查。 +# 增益状态 纯被动容器零信号——状态栏刷新绑 战斗结算 结算编排完成时点;库存 奖励道具经济 查询面——写路径回执后重查。 # Date: 2026-09-12(五区改造批;基础 = 2026-09-11 HUD 装配器;2026-09-13 快照面改造) class_name HudPresenter extends RefCounted @@ -16,11 +16,11 @@ 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: + inv: InventoryPanelView, settlement, runner, level_active: bool, db, refresh_remaining: int) -> void: + if settlement == null: _refresh_out_of_battle(stage, info, inv, runner, level_active, db) return - var s: Dictionary = b1.presentation_snapshot() + var s: Dictionary = settlement.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) @@ -65,7 +65,7 @@ static func _hero_status_summary(effects: Array) -> String: 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 义务)依赖本处持续投喂才会可达 + # RAP 活值投影(C 区点阵)——清零反馈分支(行动经济 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, @@ -110,7 +110,7 @@ static func _inventory_row(name_: String, category: String, power: int, status: return {"display_name": name_, "category": category, "desc": desc, "tooltip": tooltip, "status": status} -# 窗口条目投影(B1 window_opened 载荷 → E 区窗口态行;pos = 窗口条目位置——缺陷④契约) +# 窗口条目投影(战斗结算 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(): @@ -133,7 +133,7 @@ static func _refresh_out_of_battle(stage: BattleStageView, info: HeroInfoPanelVi 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 义务) + info.set_rap(0, int(db.action_economy.rap_refresh_cost)) # battle_over RAP 清零反馈(行动经济 义务) var rows: Array = [] for inst in runner.inventory(): if inst.status == "InStock": @@ -144,7 +144,7 @@ static func _refresh_out_of_battle(stage: BattleStageView, info: HeroInfoPanelVi inv.show_inventory(rows) -# 三源拒绝可感知(A2 reject_reason/sub_reason → 玩家文案;pattern 1 双源拒绝反馈) +# 三源拒绝可感知(排雷玩法 reject_reason/sub_reason → 玩家文案;pattern 1 双源拒绝反馈) static func outcome_text(outcome: Dictionary) -> String: if outcome.accepted: return "执行完成。" diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd index 6c05913e..e0a3fdca 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd @@ -1,21 +1,21 @@ # 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 一类路径);调试模式编排抽离 +# Date: 2026-09-12(battle.md 五区改造批;基础 = 2026-09-11 HUD框架-lite 组合根;code-review 修复批重构) +# 2026-09-13(封装审查修复批):核心状态读取收口至 战斗结算/runner 成文只读面(presentation_snapshot / +# current_board / current_battle 等——禁链式穿透 settlement.enemy_system.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 推进; +# ② 模式管理(完整关卡 关卡节点地图 runner / 调试两层——后者编排归 debug_session.gd;单场原语仅探针使用) +# ③ 核心写调用只在此处发生(minesweeper.settle_batch / settlement.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 转发信号;相位读取仅用于驱动视图状态。 +# ADR-0001 纪律:呈现层永不直接订阅 排雷玩法/行动经济——只订 战斗结算/runner 转发信号;相位读取仅用于驱动视图状态。 extends Control const UiAssembler := preload("res://presentation/ui_assembler.gd") @@ -25,8 +25,8 @@ 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 settlement = null # 当前战斗编排器(关卡与调试模式统一读它) +var runner = null # 关卡节点地图 线性关卡推进器(完整关卡模式) var logger = null # 试玩日志器(JSONL 持久化 + 卡死诊断转储——呈现层组件) var _level_active := false var _seed_edit: LineEdit @@ -102,7 +102,7 @@ func _build_ui() -> void: inventory_panel.skipped.connect(_on_window_skipped) inventory_panel.reject_reason.connect(_on_inventory_reject) - # 奖励徽章数据源:B4 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效) + # 奖励徽章数据源:奖励道具经济 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效) board_view.set_identity_resolver(_identity_lookup) logger = PlaytestLogger.new() @@ -174,10 +174,10 @@ 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 + or _inter_overlay.visible or settlement == null -# ==================== 完整关卡模式(C1 线性推进器)==================== +# ==================== 完整关卡模式(关卡节点地图 线性推进器)==================== func _start_full_level() -> void: _start_overlay.visible = false @@ -194,7 +194,7 @@ func _start_full_level() -> void: _reset_run_scope() log_view.clear() var seed_val := _seed_value() - logger.start_session(seed_val) # 各战斗的 attach 在 _on_runner_node 内(b1 逐战创建) + logger.start_session(seed_val) # 各战斗的 attach 在 _on_runner_node 内(settlement 逐战创建) if not runner.start_level(1, seed_val): _fatal_screen("关卡启动失败——见输出日志") return @@ -214,8 +214,8 @@ func _reset_run_scope() -> void: 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) + settlement = runner.current_battle() # 呈现层统一读 settlement(与调试模式共用接线) + logger.attach_battle(settlement) _boss_active = t == "boss" _show_board_fx_for_node(t) log_view.append_bb(Narrator.node_banner(runner, node, t)) @@ -237,12 +237,12 @@ func _on_runner_node(node: Dictionary, info: Dictionary) -> void: func _on_level_finished(payload: Dictionary) -> void: _level_active = false var result: String = String(payload.get("result", "")) - b1 = null + settlement = null _sync_settlement_visuals() # → 全关 - info_panel.set_status_cleared_feedback() # 状态槽全清可见反馈(B3×4④) + info_panel.set_status_cleared_feedback() # 状态槽全清可见反馈(增益状态×4④) stage.gray_all_enemies() # 死亡兜底:卡体灰化残位(禁瞬消语义的战斗收尾面) if result == "victory": - # 关卡结算:库存清空可见反馈(B4 D1×⑤)+ 胜利终局 overlay(金蜡封落于棋盘中央) + # 关卡结算:库存清空可见反馈(奖励道具经济 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()), @@ -265,7 +265,7 @@ func _on_battle_end_continue() -> void: if runner != null: runner.restore_hero_base() runner = null - b1 = null + settlement = null tune_panel.reset_sliders() strip_view.refresh(null) _start_overlay.visible = true @@ -289,16 +289,16 @@ func _start_debug_levels() -> void: _fatal_screen("战斗启动失败——见输出日志") -func _on_debug_battle_created(b1_ref, _level: int, _carry: int) -> void: - # begin_battle 前接手 + 接线(ADR-0001:signal.connect 唯一归组合根)。b1 必须先于 begin_battle +func _on_debug_battle_created(settlement_ref, _level: int, _carry: int) -> void: + # begin_battle 前接手 + 接线(ADR-0001:signal.connect 唯一归组合根)。settlement 必须先于 begin_battle # 就位——begin 期间发出的 board_changed 要用新板重建棋盘并触发雾首遇提示(缺陷①同款时序义务) - b1 = b1_ref - _wire_battle(b1_ref) + settlement = settlement_ref + _wire_battle(settlement_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) +func _on_debug_battle_ready(settlement_ref, _level: int, _carry: int) -> void: + settlement = settlement_ref + board_view.rebuild(settlement.minesweeper.current_board(), settlement.minesweeper) _check_board_hints() _refresh_presentations() @@ -320,11 +320,11 @@ func _scaled(base: int, pct: int) -> int: return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0))) -# 奖励徽章身份解析(组合根守卫:仅挖掘期 Active 相位内查询——B4 Ready 域外零读、零诊断噪音) +# 奖励徽章身份解析(组合根守卫:仅挖掘期 Active 相位内查询——奖励道具经济 Ready 域外零读、零诊断噪音) func _identity_lookup(c: Vector2i): - if b1 == null or b1.a2.current_board() == null or b1.a3.phase != b1.a3.PHASE_ACTIVE: + if settlement == null or settlement.minesweeper.current_board() == null or settlement.action_economy.phase != settlement.action_economy.PHASE_ACTIVE: return null - return b1.query_reward_identity(c) + return settlement.query_reward_identity(c) func _show_board_fx_for_node(node_type: String = "") -> void: @@ -334,12 +334,12 @@ func _show_board_fx_for_node(node_type: String = "") -> void: func _on_battle_finished(payload: Dictionary) -> void: - # 仅调试模式接线(完整关卡的结束归 C1 runner._on_level_finished) + # 仅调试模式接线(完整关卡的结束归 关卡节点地图 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④) + info_panel.set_status_cleared_feedback() # 状态槽全清可见反馈(增益状态×4④) var decision: Dictionary = _debug.on_battle_finished(payload) match String(decision.outcome): "next_layer": @@ -365,18 +365,18 @@ func _on_battle_finished(payload: Dictionary) -> void: # ==================== 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 _wire_battle(settlement_ref) -> void: + settlement_ref.step_row.connect(_on_step_row) + settlement_ref.window_opened.connect(_on_window_opened) + settlement_ref.battle_finished.connect(_on_battle_finished) + settlement_ref.round_started.connect(_on_round_started) + settlement_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①② 占位):完美排雷横幅 / 标中旗加重弹道 + # 高光演出(战斗结算×3①② 占位):完美排雷横幅 / 标中旗加重弹道 if String(row.get("type", "")) == "highlight": board_zone.fx.play_perfect_sweep() elif String(row.get("highlight", "")) == "flag_hit": @@ -392,12 +392,12 @@ func _on_round_started(ri: int) -> void: 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: + # 关卡模式下 board_changed 可能先于主场景接手 settlement 到达(begin_battle 期间发)——先从 runner 同步 + if settlement == null and _level_active and runner != null: + settlement = runner.current_battle() + if settlement == null: return - board_view.rebuild(b1.a2.current_board(), b1.a2) + board_view.rebuild(settlement.minesweeper.current_board(), settlement.minesweeper) _check_board_hints() _refresh_presentations() @@ -405,13 +405,16 @@ func _on_board_changed() -> void: # ---- 一次性非模态提示(每 run 一次——pattern 10)与诚实提示 ---- func _check_board_hints() -> void: - var board = b1.a2.current_board() + var board = settlement.minesweeper.current_board() if board == null: return - # 零合法板(A1 EC5 降级)诚实提示——非静默(toast,非模态) + # 零合法板(棋盘生成 EC5 降级)诚实提示——非静默(toast,非模态) if board.illegal_product: board_zone.toast.show_toast("本板为零合法板降级产物——出图器已尽力,数据面诚实告知", "warn") - # 雾首遇(每 run 一次;必含三要点:无雷盲区不赌命 / 雾格不传播连锁 / 雾内插旗纯损——A4 D1×②) + # 接受最优降级(有合法板但无一达标)——降级标记独立于非法产物标记,同走诚实告知 + elif board.degraded_product: + board_zone.toast.show_toast("本板为接受最优降级产物(未达可推理率及格线)——数据面诚实告知", "warn") + # 雾首遇(每 run 一次;必含三要点:无雷盲区不赌命 / 雾格不传播连锁 / 雾内插旗纯损——地形变异 D1×②) if not board.fog_cells().is_empty() and not _hints_shown.has("fog_first"): _hints_shown["fog_first"] = true _show_hint("迷雾=无雷盲区,不赌命开雾;雾格不传播连锁;雾内插旗纯损。", "fog_first") @@ -442,7 +445,7 @@ func _show_hint(text: String, hint_id: String) -> void: func _on_window_opened(items: Array) -> void: _window_payload = items - var hero: Dictionary = b1.presentation_snapshot().hero # E1 收口:经呈现快照面读 HP + var hero: Dictionary = settlement.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)) @@ -456,16 +459,16 @@ func _on_window_item_selected(pos: int) -> void: 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]) + if settlement != null: + settlement.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([]) + if settlement != null: + settlement.submit_window_selection([]) _refresh_presentations() @@ -485,13 +488,13 @@ 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 outcomes: Array = settlement.minesweeper.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(): + # settle_batch 内部同步走完 结算→战斗结束→关卡推进(事件同步发射)——关卡终局时 settlement 已被 + # runner 置空,清图演出判定须先判 settlement(基线既有重入隐患,2026-09-13 加固) + if accepted and kind == "dig" and settlement != null and settlement.minesweeper.current_board() != null and settlement.minesweeper.clear_map_cleared(): board_zone.fx.play_clear_burst() board_view.refresh() _refresh_presentations() @@ -500,8 +503,8 @@ func _on_board_action(cell: Vector2i, kind: String) -> void: 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"}]) + # stop 经批次提交(排雷玩法 D1×6①——不直调 行动经济) + var outcomes: Array = settlement.minesweeper.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:防静默改判读感欺骗) @@ -518,16 +521,16 @@ func _on_refresh_confirmed() -> void: var ok: bool = false if _level_active and runner != null: ok = runner.player_request_refresh() - elif b1 != null: - ok = b1.player_request_refresh() + elif settlement != null: + ok = settlement.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 + # 资源面/上限拒绝(EC13 合法拒绝可见;行动经济 无载荷——客户端按上下文推断) + var rap: int = int(settlement.action_economy.rap) if settlement != 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}) @@ -556,7 +559,7 @@ func _handle_outcome_feedback(outcome: Dictionary) -> void: 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) + HudPresenter.refresh(stage, info_panel, ap_bar, inventory_panel, settlement, runner, _level_active, db, refresh_remaining) _sync_settlement_visuals() @@ -571,17 +574,17 @@ func _sync_settlement_visuals() -> void: func _is_settling() -> bool: - if b1 == null or not b1.is_battle_live(): + if settlement == null or not settlement.is_battle_live(): return false - return b1.a3.phase == b1.a3.PHASE_WAITING + return settlement.action_economy.phase == settlement.action_economy.PHASE_WAITING func _phase_text() -> String: - # 文案装配归 narrator(b1 缺位 = 非战斗态——组合根模式状态守卫) - if b1 == null: + # 文案装配归 narrator(settlement 缺位 = 非战斗态——组合根模式状态守卫) + if settlement == 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, + return Narrator.phase_text(int(settlement.round_index), String(settlement.battle_result), + inventory_panel.is_window_open(), settlement.action_economy.phase == settlement.action_economy.PHASE_WAITING, _boss_active, Narrator.boss_name(runner, db)) @@ -604,9 +607,9 @@ 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) + "hero_hp": runner.hero_hp, "settlement_is_null": runner.current_battle() == null} + if settlement != null: + ctx["settlement"] = settlement.diagnostic_snapshot() # 诊断观测面(私有字段零直读——code-review 2026-09-12) var path: String = logger.dump_diagnosis(ctx) board_zone.toast.show_toast("已转储诊断: " + path, "info") diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd index 6662e4b0..ea93e103 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd @@ -2,11 +2,11 @@ # 实现 GDD:design/ux/battle.md(§Events Fired 遥测义务——ux 行族)。 # Date: 2026-09-12(五区改造批增 ux 遥测;基础 = 2026-09-12 日志器首版) # 试玩日志器(呈现层组件——订阅核心信号持久化 JSONL,供数值分析;卡死诊断转储)。 -# 职责边界:核心层只发 settlement_snapshot 信号(B1)/既有信号(battle_finished/node_entered/ +# 职责边界:核心层只发 settlement_snapshot 信号(战斗结算)/既有信号(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_round_exit{path_tag}(快照 round_facts.path——呈现层只订 战斗结算 的 ADR 纪律下唯一合法来源)、 # ux_refresh_attempt / ux_window_choice / ux_reject / ux_hint_shown(组合根显式上报)。 # 诊断转储 = 全量核心状态 + 最近结算/操作环形缓冲 → user://logs/diag_<时间戳>.json(卡死时点按钮)。 class_name PlaytestLogger @@ -20,7 +20,7 @@ var _seed := 0 var _recent_steps: Array = [] # 环形缓冲:最近结算行 var _recent_actions: Array = [] # 环形缓冲:最近玩家动作 var _snap_count := 0 -var ap_per_round := 8 # 组合根注入(AP 消耗口径分母——A3 无推送信号) +var ap_per_round := 8 # 组合根注入(AP 消耗口径分母——行动经济 无推送信号) var _ux_inputs := 0 # 本结算轮原始输入动作数(log_action 计数,快照后归零) @@ -50,10 +50,10 @@ 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({ +func attach_battle(settlement) -> void: + settlement.step_row.connect(_on_step_row) + settlement.settlement_snapshot.connect(_on_settlement_snapshot) + settlement.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))})) 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 index b01c0e41..133b865a 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/ap_bar_view.gd @@ -4,7 +4,7 @@ # 〔pattern 7 破坏性确认异位:RAP−8 · 旧板旗作废显式代价〕)。 # Date: 2026-09-12 # 高内聚边界:纯视图——意图经信号外发(stop_pressed/refresh_confirmed/legend_toggled), -# 状态由组合根经 setter 注入;刷新可用性预判 = RAP≥8 读数(A3 无推送信号——每次调用返回后重查)。 +# 状态由组合根经 setter 注入;刷新可用性预判 = RAP≥8 读数(行动经济 无推送信号——每次调用返回后重查)。 class_name ApBarView extends PanelContainer @@ -92,7 +92,7 @@ func _init() -> void: refresh_box.add_child(_refresh_sub) -# ---- setter 面(组合根每次核心调用返回后重查注入——A3 无推送信号) ---- +# ---- setter 面(组合根每次核心调用返回后重查注入——行动经济 无推送信号) ---- func set_ap(ap: int, ap_max: int) -> void: while _dots.get_child_count() < ap_max: 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 index e68a0bdd..aae0d2e6 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_end_overlay.gd @@ -58,7 +58,7 @@ func _init() -> void: visible = false -# 终局画面数据锚 = B1 判定行(verdict)——判定行文案由组合根装配传入 +# 终局画面数据锚 = 战斗结算 判定行(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 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 index beb2761e..dec49f62 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd @@ -3,8 +3,9 @@ # 实时流 / 点击展开回看 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。 +# Date: 2026-09-12(基础 = 2026-09-11 13 类行协议格式化版;2026-09-15 窗口锚行批 13→15 类: +# 新增 window_open / window_skip 两类锚行格式化——战斗结算 行协议清单同步) +# 高内聚边界:战斗结算 步骤流 → 玩家文案的全部格式化规则内聚于此;来源文案映射来自 SettlementTable。 class_name BattleLogView extends PanelContainer @@ -162,7 +163,7 @@ func _format_row(row: Dictionary, agg_count := -1) -> String: var suffix := "(%s)" % src if src != "" else "" match String(row.get("type", "")): "effect": - # 效果行 value = 单次贡献值,pool_value = 池合成值——禁相加呈现(B1 呈现细则) + # 效果行 value = 单次贡献值,pool_value = 池合成值——禁相加呈现(战斗结算 呈现细则) 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] @@ -199,6 +200,10 @@ func _format_row(row: Dictionary, agg_count := -1) -> String: return "[color=#e0b860][b]◆ %s[/b][/color]" % name_ "highlight": return prefix + _gold_frame("★ %s" % name_) + "window_open": + return prefix + "[color=#c8b070]▤ %s[/color]" % name_ + "window_skip": + return prefix + "[color=#9a8a72]▤ %s[/color]" % name_ return prefix + name_ + suffix 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 index 982577b9..578ecb43 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_fx_view.gd @@ -1,6 +1,6 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(D 区演出层——清图金粒喷泉+彩纸 3–5s 占位、Boss 紫赤影子爬棋盘 -# 一次性占位、完美排雷金光带+横幅占位、标中旗加重弹道占位;全部程序化色板原语,真实美术归 B5)。 +# 一次性占位、完美排雷金光带+横幅占位、标中旗加重弹道占位;全部程序化色板原语,真实美术归 战斗表现)。 # Date: 2026-09-12 # 高内聚边界:纯演出——组合根按核心信号调用 play_*;本类零核心依赖、不自持游戏状态。 class_name BoardFxView diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd index 53f6d0d9..e01201ae 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd @@ -1,11 +1,11 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(战斗屏 D 区 BoardView——四格态/悬停预承诺光标/可挖域呼吸微光/ # BFS 路径预览/奖励徽章/雾区全开一次性完成信号/结算期去饱和锁输入)。 -# Date: 2026-09-12(battle.md 五区改造批;基础 = 2026-09-11 D1-lite 版) +# Date: 2026-09-12(battle.md 五区改造批;基础 = 2026-09-11 HUD框架-lite 版) # 棋盘视图:格子渲染(art-bible 明亮风、数字高对比、雾形状+色相双通道、 # 绘制优先级 数字>小人>旗>奖励徽章>变异纹带>底色)+ 玩家输入意图翻译 + 悬停预承诺三态。 -# 高内聚边界:只读 A2 查询面(+B4 身份解析回调)做渲染与意图识别,动作经信号外发——不调用核心写接口 -# (settle_batch 归组合根编排),不持久持有战斗态(a2 引用随 refresh 更新,仅作只读查询)。 +# 高内聚边界:只读 排雷玩法 查询面(+奖励道具经济 身份解析回调)做渲染与意图识别,动作经信号外发——不调用核心写接口 +# (settle_batch 归组合根编排),不持久持有战斗态(minesweeper 引用随 refresh 更新,仅作只读查询)。 # 缺陷①回归约束:格子控件常驻、逐格重绘在 refresh 内完成,无布局回流(格控件/子标记定长,不增删)。 # ADR-0003 形态约束:GridContainer + 逐格常驻控件(探针耦合 _cells/click_cell——禁盲目迁移 _draw)。 class_name BoardView @@ -30,6 +30,7 @@ const COL_HL_TARGET := Color8(240, 168, 40) # 悬停目标格边框(金) const REWARD_BADGE := { # 已开格奖励身份 icon(tier 档位另经字号/形状区分) "instant_aoe": "爆", "instant_heal": "急", "damage_item": "刃", "heal_pack": "医", "buff": "纹", } +const BADGE_PLACEHOLDER := "?" # 未知图标键占位纹样(HUD框架:禁静默空图标) const CURSOR_DIG := Input.CURSOR_CROSS # 挖——镐形自绘纹理载体 const CURSOR_FLAG := Input.CURSOR_POINTING_HAND # 旗——旗形自绘纹理载体 const CURSOR_MOVE_SEM := Input.CURSOR_ARROW # 移动——箭头(命名避开 Control.CURSOR_MOVE 原生成员遮蔽) @@ -40,8 +41,8 @@ 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 _minesweeper = null # 排雷玩法 只读查询引用(refresh 更新;null = 无战斗) +var _identity_cb := Callable() # 奖励道具经济 身份解析回调(组合根注入只读面)——Vector2i → Dictionary/null var _diggable := {} # 可挖域(8 向相邻未开格)——呼吸微光作用集 var _breath_t := 0.0 var _dig_glow := false # 挖掘期才允许唯一在动元素(art-bible §2.1) @@ -49,7 +50,7 @@ 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 _r0 := {} # 出生展开区 R₀(奖励道具经济 mapping 资格域 = S∖R₀——徽章查询跳过,防 E19 诊断噪音) var _desat_mat: ShaderMaterial var _desat_tween: Tween var _cell := 46.0 # 当前格边长(set_cell_area 按容器尺寸重算——battle.md 格尺寸公式) @@ -91,7 +92,7 @@ func set_desaturated(on: bool) -> void: (_cells[c] as Button).material = _desat_mat if on else null -# 组合根注入 B4 身份解析只读回调(query_identity)——奖励徽章数据源 +# 组合根注入 奖励道具经济 身份解析只读回调(query_identity)——奖励徽章数据源 func set_identity_resolver(cb: Callable) -> void: _identity_cb = cb @@ -107,10 +108,10 @@ func set_cell_area(avail: Vector2) -> void: (_cells[c] as Button).custom_minimum_size = Vector2(_cell, _cell) -# 战斗启动/换板:按板尺寸重建格子控件(a2 引用随之更新) -func rebuild(board: BoardData, a2) -> void: +# 战斗启动/换板:按板尺寸重建格子控件(minesweeper 引用随之更新) +func rebuild(board: BoardData, minesweeper) -> void: _board = board - _a2 = a2 + _minesweeper = minesweeper for child in _grid.get_children(): child.queue_free() _cells.clear() @@ -144,12 +145,12 @@ func rebuild(board: BoardData, a2) -> void: # 逐格重绘(挖掘/结算后调用) func refresh() -> void: - if _board == null or _a2 == null: + if _board == null or _minesweeper == null: return - var hero: Vector2i = _a2.hero_pos() + var hero: Vector2i = _minesweeper.hero_pos() _diggable.clear() for n in _board.neighbors8(hero): - if not _a2.is_revealed(n): + if not _minesweeper.is_revealed(n): _diggable[n] = true for y in _board.height: for x in _board.width: @@ -167,16 +168,16 @@ func click_cell(cell: Vector2i, button: int) -> void: func _on_cell_input(c: Vector2i, ev: InputEvent) -> void: - if _a2 == null or not (ev is InputEventMouseButton and ev.pressed): + if _minesweeper == null or not (ev is InputEventMouseButton and ev.pressed): return if ev.button_index == MOUSE_BUTTON_LEFT: - if _a2.is_revealed(c): + if _minesweeper.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): + if _minesweeper.has_flag(c): action_requested.emit(c, "flag_pull") else: action_requested.emit(c, "flag_place") @@ -212,11 +213,11 @@ func _refresh_hover_after_paint() -> void: func _apply_hover_visuals(c: Vector2i) -> void: - if _a2 == null or not _cells.has(c): + if _minesweeper == 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 revealed: bool = _minesweeper.is_revealed(c) + var flag: bool = _minesweeper.has_flag(c) var adjacent: bool = _diggable.has(c) var hl: Panel = _marker[c]["hl"] if revealed: @@ -253,9 +254,9 @@ func _set_cursor_shape(c: Vector2i, shape: int) -> void: func _show_path_preview(c: Vector2i) -> void: # D3 合法性指示:悬停已开格 → BFS 路径蓝细线点迹(compute_move_path 只读面) - if _a2 == null: + if _minesweeper == null: return - var path: Array = _a2.compute_move_path(c) + var path: Array = _minesweeper.compute_move_path(c) for pc in path: if _marker.has(pc): _marker[pc]["dot"].visible = true @@ -271,36 +272,45 @@ func _hide_path_preview() -> void: 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(): + if _minesweeper == null or not _minesweeper.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 状态字典) + return # R₀ 初始展开格无奖励身份(奖励道具经济 资格域 = S∖R₀) + var rec: Dictionary = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面(禁直读 排雷玩法 状态字典) 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.text = _badge_glyph(String(ident.get("category", ""))) badge.add_theme_font_size_override("font_size", maxi(10, int(_cell * 0.28))) badge.visible = true +# 奖励图标键解析(HUD框架:查不到的键立即断言 + ? 占位纹样 + 诊断;键集 = 奖励道具经济 category 五值枚举) +func _badge_glyph(category: String) -> String: + if REWARD_BADGE.has(category): + return REWARD_BADGE[category] + push_error("[棋盘] 未知奖励图标键(映射表外 category——立即断言): %s" % category) + assert(false, "未知奖励图标键: " + category) + return BADGE_PLACEHOLDER + + # ---- 四格态渲染(灰度截图可辨:完整覆尘/碎裂裸土/雾纹带/已开雾 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 revealed: bool = _minesweeper.is_revealed(c) var fog: bool = _board.variant_ids(c).size() > 0 - var flag: bool = _a2.has_flag(c) + var flag: bool = _minesweeper.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 收口:已开记录经查询面 + var rec: Dictionary = _minesweeper.reveal_record(c) # E2 收口:已开记录经查询面 if rec.get("kind", "") == "mine": bg = COL_FLOOR text = "雷" @@ -415,7 +425,7 @@ func _check_fog_completion() -> void: continue var all_open := true for c in comp: - if not _a2.is_revealed(c): + if not _minesweeper.is_revealed(c): all_open = false break if all_open: 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 index 37f354f9..d95dcb37 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/enemy_card_view.gd @@ -1,11 +1,11 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(B 区敌人卡 ×1–3——敌像占位、血条、顺位徽标、意图徽标〔常驻: -# 挖掘期全程可见,映射 = enemy_id 恒定、环点 = 拍数直觉编码,B2 数据源 = query_beat 当前拍〕、 -# 状态槽〔敌方 side 池口径:伏击层数全体共享单值各卡同值——B1 流水经 B3 投影〕)。 +# 挖掘期全程可见,映射 = enemy_id 恒定、环点 = 拍数直觉编码,敌人系统 数据源 = query_beat 当前拍〕、 +# 状态槽〔敌方 side 池口径:伏击层数全体共享单值各卡同值——战斗结算 流水经 增益状态 投影〕)。 # Date: 2026-09-12 -# 敌人死亡呈现(B2 资产下限同等 blocking):血条归零 → 卡体灰化 + 碎裂退场,禁瞬消; +# 敌人死亡呈现(敌人系统 资产下限同等 blocking):血条归零 → 卡体灰化 + 碎裂退场,禁瞬消; # 卡体保留灰化残位——定长卡槽,死亡动画不回流布局。 -# 意图线索映射目录(B2×4① 成文占位——资产定稿归 B5,本处按 enemy_id 程序化占位)。 +# 意图线索映射目录(敌人系统×4① 成文占位——资产定稿归 战斗表现,本处按 enemy_id 程序化占位)。 class_name EnemyCardView extends PanelContainer @@ -65,7 +65,7 @@ func _init() -> void: col.add_child(_status_label) -# data = B3/B2 投影:{order, display_name, hp, max_hp, enemy_id, beat, ring_size, ring_index, ambush_layers} +# data = 增益状态/敌人系统 投影:{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))) @@ -81,12 +81,12 @@ func set_data(d: Dictionary) -> void: 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 = 敌人系统 行为环词表原键(attack/charge/heavy,战斗结算 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: @@ -151,7 +151,7 @@ func _revive() -> void: func _portrait_glyph(enemy_id: String) -> String: - # enemy_id 恒定映射(B2 Rule 8③)——程序化占位字形:小体量墩形/垂直延伸/最大体量 + # enemy_id 恒定映射(敌人系统 Rule 8③)——程序化占位字形:小体量墩形/垂直延伸/最大体量 match enemy_id: "goblin": return "▂" 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 index aec802e5..1907b1f2 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/hero_card_view.gd @@ -68,7 +68,7 @@ func set_idle() -> void: _status_label.text = "" -# battle_over 反馈(B3×4④):状态逐槽化光收回 + 微文案——英雄卡侧紧凑引用同步 +# battle_over 反馈(增益状态×4④):状态逐槽化光收回 + 微文案——英雄卡侧紧凑引用同步 func set_status_cleared_feedback() -> void: _status_label.text = "状态随战斗结束消散" 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 index 5d61280f..5df41818 100644 --- 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 @@ -1,6 +1,6 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(C 区角色信息 240×520——HP/攻击(+修正)/RAP 直角细线数据框 -# 〔修正来源角标〕、状态效果槽 ×4、小人立绘占位;RAP 清零可见反馈〔A3 义务〕; +# 〔修正来源角标〕、状态效果槽 ×4、小人立绘占位;RAP 清零可见反馈〔行动经济 义务〕; # 状态槽空态 = 槽隐藏上移补位〔EC12〕)。 # Date: 2026-09-12 # 高内聚边界:纯视图 setter 面——装配(含修正文案)归 hud_presenter.gd;定长槽 + clip_contents @@ -70,7 +70,7 @@ func _init() -> void: _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) @@ -92,7 +92,7 @@ func set_hero(hp: int, max_hp: int) -> void: func set_attack(atk: int, mod_text: String) -> void: - # 修正来源角标:mod_text 形如「(+50% 增伤)」——来源 = 效果 display_name(B3 投影) + # 修正来源角标:mod_text 形如「(+50% 增伤)」——来源 = 效果 display_name(增益状态 投影) _atk_label.text = "攻击 %d %s" % [atk, mod_text] @@ -102,7 +102,7 @@ func set_rap(rap: int, cap_hint: int) -> void: _rap_label.text = "RAP %d(攒 %d 刷图)" % [rap, cap_hint] _render_rap_dots(rap) if prev > 0 and rap == 0: - # battle_over RAP 清零可见反馈(A3 D1 义务):数字滚动归零 + 微文案占位 + # battle_over RAP 清零可见反馈(行动经济 D1 义务):数字滚动归零 + 微文案占位 _rap_feedback.text = "随战斗结束消散" var tw := create_tween() tw.tween_property(_rap_label, "modulate", Color(1.4, 1.2, 0.7), 0.12) @@ -116,7 +116,7 @@ func set_status_effects(effects: Array) -> void: _status_feedback.text = "" -# battle_over:状态槽全清可见反馈(逐槽化光 + 微文案——B3×4④) +# battle_over:状态槽全清可见反馈(逐槽化光 + 微文案——增益状态×4④) func set_status_cleared_feedback() -> void: slot_row.set_cleared_feedback() _status_feedback.text = "状态随战斗结束消散" 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 index ab554418..dd5d80c0 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/inventory_panel_view.gd @@ -4,7 +4,7 @@ # 〔布局决策 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 @@ -63,7 +63,7 @@ func _init() -> void: col.add_child(_policy) -# ---- 常读态(挖掘期规划面——B4 D1 义务①) ---- +# ---- 常读态(挖掘期规划面——奖励道具经济 D1 义务①) ---- func show_inventory(rows: Array) -> void: if Time.get_ticks_msec() < _cleared_hold_until: @@ -76,7 +76,7 @@ func show_inventory(rows: Array) -> void: _rebuild_rows(rows, false) -# 空态(指引性,非报错——B4 EC 空态) +# 空态(指引性,非报错——奖励道具经济 EC 空态) func show_empty() -> void: if Time.get_ticks_msec() < _cleared_hold_until: return @@ -86,7 +86,7 @@ func show_empty() -> void: # ---- 窗口态(pattern 2 原位激活:面板描边金光点亮、标题切换 + 时机指示常驻) ---- -# items = B1 组装窗口投影(usable_now 由 B1 组装——呈现层只消费不推断) +# items = 战斗结算 组装窗口投影(usable_now 由 战斗结算 组装——呈现层只消费不推断) func open_window(items: Array) -> void: _window_mode = true _set_window_visual(true) @@ -110,7 +110,7 @@ func is_open() -> bool: return _window_mode -# 库存清空反馈(B4 D1×⑤):逐件化光点飞出 + 「剩余道具 ×N 随关卡结束清空」占位 +# 库存清空反馈(奖励道具经济 D1×⑤):逐件化光点飞出 + 「剩余道具 ×N 随关卡结束清空」占位 func play_cleared_feedback(remaining: int) -> void: _cleared_hold_until = Time.get_ticks_msec() + 3000 var rows := _list.get_children() diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd index ecb3c07d..de26757f 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd @@ -1,6 +1,6 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(D 区图例按钮 + 模态 overlay——格态/变异/雾语义图例, -# 「迷雾 = 无雷盲区」条目恒在〔A4 D1×②〕;状态槽空态说明成文载入〔B3 D1×①〕)。 +# 「迷雾 = 无雷盲区」条目恒在〔地形变异 D1×②〕;状态槽空态说明成文载入〔增益状态 D1×①〕)。 # Date: 2026-09-12 # pattern 6 模态 overlay 层级纪律:打开时由组合根隐藏上方遮挡层 + 锁棋盘输入; # 本类自持开关(toggle),探针断言"打开后自身 visible"——遮挡物断言归场景探针。 @@ -41,7 +41,7 @@ func _init() -> void: 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: 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 index a70ca993..b7a0eef9 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/level_strip_view.gd @@ -1,7 +1,7 @@ # 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 呈现):线性节点链 战→赏→疗→王 的推进状态。 +# Date: 2026-09-12(battle.md 五区改造批——对齐 28px 顶栏与高亮样式;基础 = 2026-09-11 关卡节点地图-lite 呈现) +# 关卡节点进度条视图(关卡节点地图-lite 呈现):线性节点链 战→赏→疗→王 的推进状态。 # 高内聚边界:只读 runner 的节点链/进度做渲染,无任何写面。 class_name LevelStripView extends HBoxContainer 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 index 4288958e..e6973cb9 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/status_slot_row.gd @@ -1,29 +1,37 @@ # VERTICAL SLICE - NOT FOR PRODUCTION -# 实现 GDD:design/ux/battle.md(C 区状态效果槽 ×4〔B3 D1×①〕——effect_id→icon 形状+色相双通道、 +# 实现 GDD:design/ux/battle.md(C 区状态效果槽 ×4〔增益状态 D1×①〕——effect_id→icon 形状+色相双通道、 # 层数角标、剩余结算次数点、悬停 tooltip(来源+语义+剩余);空态 = 槽隐藏(数据框上移补位, -# EC12 空态裁决);极性映射 = B1 SettlementTable 效果语义而非 magnitude 符号〔B3 D1×③〕)。 +# EC12 空态裁决);极性映射 = 战斗结算 SettlementTable 效果语义而非 magnitude 符号〔增益状态 D1×③〕)。 # Date: 2026-09-12 -# 高内聚边界:纯视图——效果投影装配归 hud_presenter.gd;B3 纯被动容器零信号——刷新绑 B1 +# 高内聚边界:纯视图——效果投影装配归 hud_presenter.gd;增益状态 纯被动容器零信号——刷新绑 战斗结算 # 结算编排完成时点(battle.md 更新触发注记)。 class_name StatusSlotRow extends HBoxContainer const MAX_SLOTS := 4 # 极性/语义映射(数据源 = SettlementTable.effect_pool_map 效果语义 + StatusEffectTable display_name): -# 益 = ▲/◆/▼(金/蓝族);害 = ⚡(旗朱族)——形状+色相双通道,文字名常驻第三通道 +# 益 = ▲/◆/▼(金/蓝族);害 = ⚡/⚔/✕(旗朱族)——形状+色相双通道,文字名常驻第三通道。 +# 状态表六行全映射(attack_up/defense_up/weaken/ambush/enrage/off_guard)——HUD框架 图标键契约 +# "一键一枚专属纹样,查不到的键立即断言 + ? 占位 + 诊断"(2026-09-15 补暴走/失衡两键)。 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), "害"], + "enrage": ["⚔", Color8(198, 61, 34), "害"], + "off_guard": ["✕", Color8(198, 61, 34), "害"], } const EFFECT_SEMANTIC := { "attack_up": "主角攻击加成(hero_offense 池加算)", "defense_up": "受到伤害减免(hero_defense 池加算)", "weaken": "敌方输出削弱(enemy_output 池乘算)", "ambush": "敌方全体攻击附加(flat_attach——踩雷转化,战斗段共享不消耗)", + "enrage": "敌方输出加成(enemy_output 池加算——标错反噬)", + "off_guard": "主角承伤加深(hero_defense 池加算——标错反噬)", } const SOURCE_NAMES := {"safe_cell": "安全格", "flag_hit": "标中旗", "mine_hit": "踩雷", "clear_reward": "清图奖励"} +# 未知图标键的占位纹样(HUD框架:占位也要可见,禁静默空图标) +const ICON_PLACEHOLDER := ["?", Color8(120, 106, 88), "?"] var _tip: PanelContainer var _tip_label: Label @@ -54,7 +62,7 @@ func _init() -> void: _tip.add_child(_tip_label) -# effects = B3 query_active 投影数组(含空态);空槽隐藏,非空按插入序填充(最多 4 槽可见——超出截断) +# effects = 增益状态 query_active 投影数组(含空态);空槽隐藏,非空按插入序填充(最多 4 槽可见——超出截断) func set_effects(effects: Array) -> void: _cleared_feedback = false _sync_slots(effects.size()) @@ -63,7 +71,7 @@ func set_effects(effects: Array) -> void: _slots[i].visible = true -# battle_over 反馈(B3×4④):逐槽化光收回 + 微文案(下次 set_effects 恢复常态) +# battle_over 反馈(增益状态×4④):逐槽化光收回 + 微文案(下次 set_effects 恢复常态) func set_cleared_feedback() -> void: _cleared_feedback = true for slot in _slots: @@ -116,7 +124,7 @@ func _fill_slot(slot: PanelContainer, eff: Dictionary) -> void: 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), "?"]) + var icon_cfg: Array = _icon_cfg(String(eff.effect_id)) icon.text = String(icon_cfg[0]) icon.add_theme_color_override("font_color", icon_cfg[1]) # 层数角标 + 剩余结算次数点(● 未耗余量) @@ -132,7 +140,7 @@ func _on_slot_hover(slot: Control) -> void: 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 icon_cfg: Array = _icon_cfg(String(eff.effect_id)) 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 轮)" % [ @@ -142,3 +150,12 @@ func _on_slot_hover(slot: Control) -> void: add_child(_tip) _tip.position = Vector2(0, -70) _tip.visible = true + + +# 图标键解析(HUD框架:查不到的键按错误处理——立即断言 + ? 占位纹样 + 诊断,禁止静默显示空图标) +func _icon_cfg(effect_id: String) -> Array: + if EFFECT_ICON.has(effect_id): + return EFFECT_ICON[effect_id] + push_error("[状态槽] 未知图标键(映射表外键——立即断言): %s" % effect_id) + assert(false, "未知图标键: " + effect_id) + return ICON_PLACEHOLDER diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd index dacfd7f1..e5c753fa 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/tune_panel.gd @@ -1,6 +1,6 @@ # 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)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd index 075324b5..73c81777 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/auto_player.gd @@ -3,32 +3,32 @@ # 自动玩家策略集(headless 冒烟/扫描/转储共用的理性玩家近似): # choose_dig = 挖最近可挖格(先移到邻接已开格);window_selection = 道具窗口确定性选择 # (治疗包优先于刀;半血阈值/轮次门/刀力口径经参数表达——四调用点原语义逐一保留)。 -# 读取面:B1.presentation_snapshot() / A2 查询面(E1/E2 收口后的成文 API——禁直读核心状态)。 +# 读取面:战斗结算.presentation_snapshot() / 排雷玩法 查询面(E1/E2 收口后的成文 API——禁直读核心状态)。 # 等价性证明:smoke_all 同种子回放断言(步骤流逐行相同)在重构后保持全绿。 extends RefCounted # 行主序枚举未开格,找与已开集 8 邻接者;小人先移到该邻接已开格(BFS 可达)。无可挖 → {} -static func choose_dig(a2) -> Dictionary: - var revealed: Array = a2.revealed_cells() +static func choose_dig(minesweeper) -> Dictionary: + var revealed: Array = minesweeper.revealed_cells() var revealed_set := {} for c in revealed: revealed_set[c] = true - var board = a2.current_board() + var board = minesweeper.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): + if minesweeper.is_revealed(c) or minesweeper.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(): + var path: Array = minesweeper.compute_move_path(n) + if not path.is_empty() or n == minesweeper.hero_pos(): return {"dig_cell": c, "hero_cell": n} return {} -# 道具窗口确定性选择(s = b1.presentation_snapshot();半血以下治疗包优先,否则刀): +# 道具窗口确定性选择(s = settlement.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 口径) @@ -72,50 +72,50 @@ static func window_selection(s: Dictionary, items: Array, opts: Dictionary = {}) # ==================== 完美策略档(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 +# (经 settlement.player_request_refresh 核心编排)· 否则停手(残 AP 1:1 转存 RAP——下轮刷新资源)。 +# 推理规则集 = 棋盘生成 F1 measure_inferability 同族(single + subset——solver_rules=single_subset 冻结域); +# 口径差异 = unknown 集不含 棋盘生成 生成期 safe_set oracle(真实玩家约束的未知邻格含雷格)——比生成期闸 +# 保守,卡死率略高属口径诚实。输入只经 排雷玩法 查询面(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) +static func perfect_action(minesweeper, settlement) -> Dictionary: + var ded := _deduce(minesweeper) for c in ded["mines"]: - if not a2.is_revealed(c) and not a2.has_flag(c): - return _plan_on(a2, c, "flag") + if not minesweeper.is_revealed(c) and not minesweeper.has_flag(c): + return _plan_on(minesweeper, 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()): + if not minesweeper.is_revealed(c) and not minesweeper.has_flag(c): + return _plan_on(minesweeper, c, "dig") + if settlement != null and bool(settlement.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)) +static func _plan_on(minesweeper, c: Vector2i, kind: String) -> Dictionary: + var d := Vector2i(absi(c.x - minesweeper.hero_pos().x), absi(c.y - minesweeper.hero_pos().y)) if maxi(d.x, d.y) == 1: return {"kind": kind, "cell": c} - var board = a2.current_board() + var board = minesweeper.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()): + if minesweeper.is_revealed(n) and (n == minesweeper.hero_pos() or not minesweeper.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() +static func _deduce(minesweeper) -> Dictionary: + var board = minesweeper.current_board() var safe := {} var mines := {} var changed := true while changed: changed = false - var cons := _constraints(a2, board, mines) + var cons := _constraints(minesweeper, board, mines) for con in cons: if int(con["mines"]) == 0: for c in con["unknown"].keys(): @@ -127,7 +127,7 @@ static func _deduce(a2) -> Dictionary: if not mines.has(c): mines[c] = true changed = true - var cons2 := _constraints(a2, board, mines) + var cons2 := _constraints(minesweeper, board, mines) for i in cons2.size(): for j in range(i + 1, cons2.size()): var der := _subset_derived(cons2[i], cons2[j]) @@ -148,10 +148,10 @@ static func _deduce(a2) -> Dictionary: # 约束集 = 已开数字格:mines_left = digit − 已知雷旗(旗位 + 已证雷格),unknown = 未开无旗 8 邻。 # 已爆雷格(kind=mine)不带数字约束——其贡献已含在邻格 digit 内(数字恒真不变式 inv3)。 -static func _constraints(a2, board, mines: Dictionary) -> Array: +static func _constraints(minesweeper, board, mines: Dictionary) -> Array: var out: Array = [] - for c in a2.revealed_cells(): - var rec: Dictionary = a2.reveal_record(c) + for c in minesweeper.revealed_cells(): + var rec: Dictionary = minesweeper.reveal_record(c) if String(rec.get("kind", "")) != "safe": continue var d := int(rec.get("digit", -1)) @@ -160,9 +160,9 @@ static func _constraints(a2, board, mines: Dictionary) -> Array: var unknown := {} var known := 0 for n in board.neighbors8(c): - if a2.has_flag(n): + if minesweeper.has_flag(n): known += 1 - elif not a2.is_revealed(n): + elif not minesweeper.is_revealed(n): if mines.has(n): known += 1 else: diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd index 4ad3e55c..6794e97e 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/damage_probe.gd @@ -5,7 +5,7 @@ # H2 Boss/敌攻击链缺陷(bug——fresh 池下攻击拍仍零兑现) # 快照盲区说明:settlement_snapshot 取在衰减段之后,时效 1 的 defense_up 当轮授予当轮衰减—— # 真人局 JSONL buffs[] 恒空 ≠ 池为空,本探针改从结算行(授予/续存 effect 行的 pool_value)重建战斗段时点池值。 -# 观测面:全部读 B1 结算行 / 表值 / 呈现快照(零核心穿透);池归属/墙深/池界全读表(D2 Rule 1——零数值字面量)。 +# 观测面:全部读战斗结算步骤行 / 表值 / 呈现快照(零核心穿透);池归属/墙深/池界全读表(数据配置 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 @@ -83,37 +83,37 @@ func _probe_boss(db, seed_val: int) -> void: 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 settlement = 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)}): + settlement.step_row.connect(func(row): cap["rows"].append(row)) + settlement.settlement_snapshot.connect(func(s): cap["snaps"].append(s)) + settlement.battle_finished.connect(func(p): cap["result"] = String(p.get("result", ""))) + settlement.window_opened.connect(func(items): + var s: Dictionary = settlement.presentation_snapshot() + settlement.submit_window_selection(AutoPlayer.window_selection(s, items, {"heal_hp_div": 2, "round_gate": 3, "knife_compare": "best"}))) + if not settlement.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: + while settlement.battle_result == "" and rounds < 40: rounds += 1 - var start_round: int = b1.round_index + var start_round: int = settlement.round_index if dig: - var a2 = b1.a2 + var minesweeper = settlement.minesweeper var guard := 400 - while b1.a3.phase == "Active" and b1.round_index == start_round and guard > 0: + while settlement.action_economy.phase == "Active" and settlement.round_index == start_round and guard > 0: guard -= 1 - var plan := AutoPlayer.choose_dig(a2) + var plan := AutoPlayer.choose_dig(minesweeper) 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"}]) + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if settlement.action_economy.phase == "Active": + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if settlement.action_economy.phase == "Active" and settlement.round_index == start_round: + minesweeper.settle_batch([{"action": "stop"}]) else: - b1.a2.settle_batch([{"action": "stop"}]) + settlement.minesweeper.settle_batch([{"action": "stop"}]) return cap @@ -184,7 +184,7 @@ func _analyze_round(db, math, rows: Array, beats: Dictionary) -> Dictionary: 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 同口径) + # 池归属表驱动(settlement.effect_pool_map),percent 池加算后 clamp(战斗结算 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, "")) @@ -254,7 +254,7 @@ func _probe_level(db, seed_val: int) -> void: 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_ge_1, "全部战斗 spawn ≥ 1(难度曲线与Boss 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]) @@ -309,10 +309,10 @@ func _run_level(db, seed_val: int) -> Dictionary: var guard_battles := 20 while cap["result"] == "" and guard_battles > 0: guard_battles -= 1 - var b1 = runner.current_battle() - if b1 == null: + var settlement = runner.current_battle() + if settlement == null: break - _drive_battle(runner, b1, cap) + _drive_battle(runner, settlement, cap) return cap @@ -332,11 +332,11 @@ func _battle_nodes_upto(db, level_index: int, node_idx: int) -> int: func _on_level_window(items: Array, runner) -> void: - var b1 = runner.current_battle() - if b1 == null: + var settlement = runner.current_battle() + if settlement == null: runner.submit_window_selection([]) return - var s: Dictionary = b1.presentation_snapshot() + var s: Dictionary = settlement.presentation_snapshot() runner.submit_window_selection(AutoPlayer.window_selection(s, items, {"heal_hp_div": 2, "round_gate": 3, "knife_compare": "best"})) @@ -351,33 +351,33 @@ func _on_level_node_entered(node: Dictionary, _info: Dictionary, runner, cap: Di 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: + var settlement = runner.current_battle() + if settlement == null: return - var s: Dictionary = b1.presentation_snapshot() + var s: Dictionary = settlement.presentation_snapshot() cap["battles"][cap["battles"].size() - 1]["spawn"] = s.enemies.size() -func _drive_battle(runner, b1, cap: Dictionary) -> void: +func _drive_battle(runner, settlement, cap: Dictionary) -> void: var round_guard := 60 - while cap["result"] == "" and runner.current_battle() == b1 and b1.battle_result == "" and round_guard > 0: + while cap["result"] == "" and runner.current_battle() == settlement and settlement.battle_result == "" and round_guard > 0: round_guard -= 1 - var start_round: int = b1.round_index - var a2 = b1.a2 + var start_round: int = settlement.round_index + var minesweeper = settlement.minesweeper 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: + while runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" \ + and settlement.round_index == start_round and action_guard > 0: action_guard -= 1 - var plan := AutoPlayer.choose_dig(a2) + var plan := AutoPlayer.choose_dig(minesweeper) 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: + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if settlement.action_economy.phase != "Active" or settlement.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"}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" and settlement.round_index == start_round: + minesweeper.settle_batch([{"action": "stop"}]) # 战斗判定摘要:round-1 victory 的战斗必须能从结算行分辨「首轮全灭」vs「空场」—— @@ -445,11 +445,11 @@ func _scan_spawns(db, seed0: int, n_seeds: int) -> Dictionary: 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)}): + var settlement = load("res://core/battle_orchestrator.gd").new(db, rng) + if not settlement.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 n: int = settlement.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)) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd index eda07e31..2990369c 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/dump_battle.gd @@ -12,43 +12,43 @@ func _initialize() -> void: 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 settlement = 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 收口:经呈现快照面读英雄/敌况 + settlement.step_row.connect(func(row): stream.append(row)) + settlement.window_opened.connect(func(items): + var s: Dictionary = settlement.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)) + settlement.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}) + settlement.begin_battle({"level_index": level, "node_depth": 0, "hero_hp": db.hero.max_hp}) print("=== 棋盘摘要 L%d ===" % level) - var bd = b1.a2.current_board() + var bd = settlement.minesweeper.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: + while settlement.battle_result == "" and rounds < 40: rounds += 1 - var start_round: int = b1.round_index - var a2 = b1.a2 - var a3 = b1.a3 + var start_round: int = settlement.round_index + var minesweeper = settlement.minesweeper + var action_economy = settlement.action_economy var guard := 400 - while a3.phase == "Active" and b1.round_index == start_round and guard > 0: + while action_economy.phase == "Active" and settlement.round_index == start_round and guard > 0: guard -= 1 - var plan := AutoPlayer.choose_dig(a2) + var plan := AutoPlayer.choose_dig(minesweeper) 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]) + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if action_economy.phase == "Active": + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if action_economy.phase == "Active" and settlement.round_index == start_round: + minesweeper.settle_batch([{"action": "stop"}]) + print("战斗结果: %s(%d 轮)" % [settlement.battle_result, rounds]) print("=== 结算步骤流(%d 行)===" % stream.size()) for row in stream: print(JSON.stringify(row)) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd index 83b87cd1..4bad3ab5 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/experiment_density.gd @@ -6,7 +6,7 @@ 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() + var board_generator = 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]: @@ -29,7 +29,7 @@ func _initialize() -> void: var params := _params_from(row) var rg := RandomNumberGenerator.new() rg.seed = 1000 + s - var b = a1.generate(1, 0, rg, _db(db, row)) + var b = board_generator.generate(1, 0, rg, _db(db, row)) if b == null: continue agg.m += b.mines.size() diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd index e4d55802..7fd5d23e 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_autoplay.gd @@ -45,10 +45,10 @@ static func run_level(db, seed_val: int, level_index: int, atk_pct: int, hp_pct: 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) + var this_settlement = runner.current_battle() + if this_settlement == null: + break # level_finished 已发(settlement 置 null)而信号尚未置 result 的防御 + _autoplay_battle(runner, this_settlement, st, logger) if st.result == "": st.result = "timeout" st.timeout = true @@ -71,8 +71,8 @@ static func _on_step_row(row: Dictionary, _runner, st: Dictionary, collect_rows: 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 settlement = runner.current_battle() + var s: Dictionary = settlement.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} @@ -116,64 +116,64 @@ static func _on_level_finished(p: Dictionary, st: Dictionary, logger) -> void: logger.log_level_end(p) -static func _autoplay_battle(runner, b1, st: Dictionary, logger) -> void: +static func _autoplay_battle(runner, settlement, st: Dictionary, logger) -> void: if String(st.get("policy", "normal")) == "perfect": - _autoplay_battle_perfect(runner, b1, st, logger) + _autoplay_battle_perfect(runner, settlement, st, logger) return var round_guard := 60 - while st.result == "" and runner.current_battle() == b1 and b1.battle_result == "" and round_guard > 0: + while st.result == "" and runner.current_battle() == settlement and settlement.battle_result == "" and round_guard > 0: round_guard -= 1 st.rounds += 1 - var start_round: int = b1.round_index + var start_round: int = settlement.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: + while runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" \ + and settlement.round_index == start_round and action_guard > 0: action_guard -= 1 - var plan := AutoPlayer.choose_dig(b1.a2) + var plan := AutoPlayer.choose_dig(settlement.minesweeper) 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 plan.hero_cell != settlement.minesweeper.hero_pos(): + var out_m: Array = settlement.minesweeper.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: + if settlement.action_economy.phase != "Active" or settlement.round_index != start_round: break - var out_d: Array = b1.a2.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + var out_d: Array = settlement.minesweeper.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 runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" and settlement.round_index == start_round: + var out_s: Array = settlement.minesweeper.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 + 退役 + 当场换新板), +# 完美策略档战斗驱动(2026-09-14):单步决策循环——move/dig/flag 经 排雷玩法 settle_batch 提交, +# refresh 经 settlement.player_request_refresh(战斗结算 核心编排:行动经济 扣 RAP + 退役 + 当场换新板), # stop 出内环走统一停手提交。连续拒绝 ≥3 = 编排违约防御(fail-fast 语义,停手防死循环)。 -static func _autoplay_battle_perfect(runner, b1, st: Dictionary, logger) -> void: +static func _autoplay_battle_perfect(runner, settlement, st: Dictionary, logger) -> void: var round_guard := 60 - while st.result == "" and runner.current_battle() == b1 and b1.battle_result == "" and round_guard > 0: + while st.result == "" and runner.current_battle() == settlement and settlement.battle_result == "" and round_guard > 0: round_guard -= 1 st.rounds += 1 - var start_round: int = b1.round_index + var start_round: int = settlement.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: + while runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" \ + and settlement.round_index == start_round and action_guard > 0 and rejects < 3: action_guard -= 1 - var act := AutoPlayer.perfect_action(b1.a2, b1) + var act := AutoPlayer.perfect_action(settlement.minesweeper, settlement) 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) + ok = _submit_intent(settlement, "move", act.get("cell"), logger, start_round) elif kind == "dig": - ok = _submit_intent(b1, "dig", act.get("cell"), logger, start_round) + ok = _submit_intent(settlement, "dig", act.get("cell"), logger, start_round) elif kind == "flag": - ok = _submit_intent(b1, "flag_place", act.get("cell"), logger, start_round) + ok = _submit_intent(settlement, "flag_place", act.get("cell"), logger, start_round) elif kind == "refresh": - ok = bool(b1.player_request_refresh()) + ok = bool(settlement.player_request_refresh()) if logger != null: logger.log_action("refresh", Vector2i(-1, -1), ok, {"round": start_round}) if ok: @@ -182,14 +182,14 @@ static func _autoplay_battle_perfect(runner, b1, st: Dictionary, logger) -> void 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 runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" and settlement.round_index == start_round: + var out_s: Array = settlement.minesweeper.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}]) +static func _submit_intent(settlement, action: String, cell, logger, round_index: int) -> bool: + var out: Array = settlement.minesweeper.settle_batch([{"action": action, "cell": cell}]) if logger != null: logger.log_action(action, cell, _accepted(out), {"round": round_index}) return _accepted(out) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd index 7fc9076f..cc5b3dc2 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/level_monte_carlo.gd @@ -1,6 +1,6 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Date: 2026-09-11 -# 关卡期望扫描(C3 调参期关卡侧批量工具的原型——对应 A1 F4 / 战斗侧 monte_carlo.gd): +# 关卡期望扫描(难度曲线与Boss 调参期关卡侧批量工具的原型——对应 棋盘生成 F4 / 战斗侧 monte_carlo.gd): # 扫描 atk% × hp% 网格(模拟天赋树数值提升档位),输出每档"通关率 / 整关承伤期望 / 败北节点分布 / # 通关剩余 HP 期望 / 进 Boss HP 期望 / 平均轮数"——把"多少数值能通关、通关要掉多少血"变成可测量数据。 # 运行:Godot_console.exe --headless --path . --script res://smoke/level_monte_carlo.gd -- [N] [seed0] @@ -53,8 +53,8 @@ func _initialize() -> void: 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 normal_dmg_pct: Array = [] # 每普通战斗承伤占生效 max_hp 比(难度曲线与Boss F4 口径——≤20% 带) + var boss_dmg_pct: Array = [] # Boss 战承伤占生效 max_hp 比(难度曲线与Boss F4 口径——≤50% 带) var wipes := 0 # 普通战斗 round-1 即胜数(首轮全灭跳战——拍板④观测面) var normal_battles := 0 var death_nodes := {} diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd index 5230c8e4..d5416086 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/monte_carlo.gd @@ -52,44 +52,44 @@ func _run_encounter(db, L: int) -> void: 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 settlement = 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): + settlement.battle_finished.connect(func(payload): out.result = payload.get("result", "")) + settlement.board_changed.connect(func(): out.board_swaps += 1) + settlement.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): + settlement.minesweeper.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 收口:经呈现快照面读英雄/敌况 + settlement.window_opened.connect(func(items): + var s: Dictionary = settlement.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)}): + settlement.submit_window_selection(selection)) + if not settlement.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 start_round: int = settlement.round_index + var minesweeper = settlement.minesweeper var guard := 400 - while b1.a3.phase == "Active" and b1.round_index == start_round and guard > 0 and out.result == "": + while settlement.action_economy.phase == "Active" and settlement.round_index == start_round and guard > 0 and out.result == "": guard -= 1 - var plan := AutoPlayer.choose_dig(a2) + var plan := AutoPlayer.choose_dig(minesweeper) 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: + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if settlement.action_economy.phase != "Active" or settlement.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"}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if settlement.action_economy.phase == "Active" and settlement.round_index == start_round and out.result == "": + minesweeper.settle_batch([{"action": "stop"}]) if out.result == "": out.result = "timeout" return out diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd index 708a8c16..541e5bb4 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/reward_expectation.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Date: 2026-09-11 # 奖励期望测量(供给控制器批口径):逐深度实测 |资格格|(safe ∖ R0)分布、每板注入件数(j=0 → 恒 1)、 -# 注入种类分布(60/30/10 拍板值)、格子池零可选类验证——走真实 B4.roll_board 管线(含注入判定取号序)。 +# 注入种类分布(60/30/10 拍板值)、格子池零可选类验证——走真实 奖励道具经济.roll_board 管线(含注入判定取号序)。 # 运行:Godot_console.exe --headless --path . --script res://smoke/reward_expectation.gd extends SceneTree @@ -14,8 +14,8 @@ func _initialize() -> void: print("表加载失败") quit(1) return - var a1 = load("res://core/board_generator.gd").new() - var b4_factory = load("res://core/reward_economy.gd") + var board_generator = load("res://core/board_generator.gd").new() + var reward_economy_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) @@ -28,7 +28,7 @@ func _initialize() -> void: 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) + var board = board_generator.generate(1, depth, rng.open_stream("board_gen"), db) if board == null: continue var r0 := {} @@ -39,15 +39,15 @@ func _initialize() -> void: if not r0.has(c): eligible.append(c) elig.append(eligible.size()) - var b4 = b4_factory.new(db, rng) + var reward_economy = reward_economy_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): + if not reward_economy.roll_board(list): continue var n_active := 0 for c in eligible: - var snap = b4.query_identity(c) + var snap = reward_economy.query_identity(c) if snap != null and active_ids.has(String(snap.reward_id)): n_active += 1 species[String(snap.reward_id)] += 1.0 diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd index c5d8fb4b..74d0bf2d 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 -# headless 冒烟入口(承 D2 冒烟框架:extends SceneTree + autoload 显式实例化)。 +# headless 冒烟入口(承数据配置冒烟框架:extends SceneTree + autoload 显式实例化)。 # 运行:Godot_console.exe --headless --path --script res://smoke/smoke_all.gd extends SceneTree @@ -31,12 +31,13 @@ func _initialize() -> void: 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) + # R2 黄金向量(数据配置 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("encounter", 0) == 1061841193803499209, "R2 向量 encounter m=42 scope=0(registry salt_encounter=0x8EBC6AF09C88C6E3 权威值)") 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") @@ -54,14 +55,14 @@ func _initialize() -> void: # ---- Core 集成冒烟(表加载成功才执行)---- var _db var _rng -var _last_b1 = null +var _last_settlement = null func _run_core_smoke(db, rng) -> void: _db = db _rng = rng _smoke_wall_zero_guard() - _smoke_a1() + _smoke_board_generator() _smoke_full_battle(1, 0, "L1 · grub(无雾)") _smoke_full_battle(2, 0, "L2 · ambusher(雾图)") _smoke_level() @@ -74,32 +75,35 @@ func _run_core_smoke(db, rng) -> void: _smoke_playtest_logger() _smoke_encounter_roll() _smoke_attack_chain_fresh_pool() + _smoke_clear_reward_chain() + _smoke_refresh_provenance_guard() + _smoke_window_anchor_rows() # ---- 可选类供给控制器冒烟(2026-09-11 供给控制器批:首件保证/指数递减/清图新语义/注入占比)---- func _smoke_supply() -> void: - print("--- B4 可选类供给控制器 ---") + print("--- 奖励道具经济·可选类供给控制器 ---") var rng = load("res://core/rng_service.gd").new() rng.set_master_seed(20260911) - var b4f = load("res://core/reward_economy.gd") + var reward_economy_factory = 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) + var reward_economy = reward_economy_factory.new(_db, rng) + var ok_roll: bool = reward_economy.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): + var reward_economy_trial = reward_economy_factory.new(_db, rng) + if not reward_economy_trial.roll_board(eligible): continue var actives := 0 for e in eligible: - var snap = b4t.query_identity(e.cell) + var snap = reward_economy_trial.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 @@ -113,8 +117,8 @@ func _smoke_supply() -> void: 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)) + check(reward_economy.grant_clear().category == "instant_aoe", "清图奖励 = 一次性伤害(instant_aoe,非入库道具)") + check(int(reward_economy.grant_clear().power) >= 1, "清图奖励正当量(power=%d)" % int(reward_economy.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() @@ -149,58 +153,58 @@ func _smoke_supply() -> void: 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) + var reward_economy_node = reward_economy_factory.new(_db, rng) + var node_inst = reward_economy_node.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()) + check(reward_economy_node.supply_granted_count() == 1, "节点奖励计入供给控制器(granted=%d——Bug#3 回归:发放即入账)" % reward_economy_node.supply_granted_count()) -# ---- A2 旗动作耗尽末点 AP 回归(playtest 缺陷②——v1 冒烟盲区:自动玩家不用旗)---- +# ---- 排雷玩法 旗动作耗尽末点 AP 回归(playtest 缺陷②——v1 冒烟盲区:自动玩家不用旗)---- func _smoke_flag_ap_exhausted() -> void: - print("--- A2 旗动作耗尽末点 AP 回归 ---") + print("--- 排雷玩法·旗动作耗尽末点 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 settlement = 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 + settlement.round_started.connect(func(ri): rounds.append(ri)) + settlement.window_opened.connect(func(items): settlement.submit_window_selection([])) # 结算窗口立即应答 + check(settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "回归战斗启动(Boss 行——D0 20HP 级会被盲挖粉尘秒杀,无法承载旗回归场景)") + var minesweeper = settlement.minesweeper # 恰花 7 点 AP 挖掘,留末点给旗 for i in range(7): - if b1.a3.phase != "Active" or b1.a3.ap == 0: + if settlement.action_economy.phase != "Active" or settlement.action_economy.ap == 0: break - var plan = AutoPlayer.choose_dig(a2) + var plan = AutoPlayer.choose_dig(minesweeper) 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]) + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + check(settlement.action_economy.phase == "Active" and settlement.action_economy.ap == 1, "7 次挖掘后恰剩 1 AP(phase=%s ap=%d)" % [str(settlement.action_economy.phase), settlement.action_economy.ap]) # 末点 AP 花在插旗——轮次必须终结并结算进入第 2 轮 # 旗目标 = 挖掘策略的下一目标格(构造保证:未开、与小人邻接——先把小人移到其邻接已开格) - var plan2 = AutoPlayer.choose_dig(a2) + var plan2 = AutoPlayer.choose_dig(minesweeper) 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}]) + if plan2.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan2.hero_cell}]) + out = minesweeper.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 关卡扩展批)---- +# ---- 关卡节点地图·线性推进冒烟(2026-09-11 关卡扩展批)---- func _smoke_level() -> void: - print("--- C1 关卡线性推进(战×2→道具→战×2→恢复→Boss)---") + print("--- 关卡节点地图·线性推进(战×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.boss_depth == 4, "Boss 节点以 Dmax=4 解析(棋盘生成 第三轴登记约定)") 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) @@ -234,7 +238,7 @@ func _smoke_level() -> void: # 强制显式重审(含 clamp 收紧等结构变更——本护栏随之需要有意修订)。 func _smoke_wall_zero_guard() -> void: - print("--- B3 减伤墙归零护栏 ---") + print("--- 增益状态·减伤墙归零护栏 ---") 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) @@ -242,27 +246,39 @@ func _smoke_wall_zero_guard() -> void: 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() +func _smoke_board_generator() -> void: + print("--- 棋盘生成 ---") + var board_generator = 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) + var b1 = board_generator.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(board_generator.is_clearable(b1), "L%d clearable(安全格 8 邻连通)" % L) + check(board_generator.legality(b1, row, _db), "L%d legality 全过" % L) + var inf := float(b1.metrics.get("inferability", 0.0)) + var mode := String(b1.metrics.get("accept_mode", "")) + print(" L%d 实测:inferability=%.3f / inferability_core=%.3f / attempts_used=%d / accept_mode=%s / target=%.2f" % [ + L, inf, float(b1.metrics.get("inferability_core", 0.0)), int(b1.metrics.get("attempts_used", -1)), mode, float(row.inferability_target)]) + # 出图验收(棋盘生成 登记口径):first_pass 必须 ≥ target;accept_best(重试耗尽接受最优)允许低于 target + # 但必须打印实测值(正常出口);accept_worst 由下方 illegal_product 断言承担。 + check(inf >= float(row.inferability_target) or mode == "accept_best", + "L%d inferability %.3f %s target %.2f(accept_mode=%s)" % [L, inf, "≥" if inf >= float(row.inferability_target) else "<", float(row.inferability_target), mode]) + check(b1.metrics.has("inferability_core"), "L%d 指标集输出 inferability_core(单一判据下仅作观测,不参与门槛)" % L) + check(b1.metrics.has("degraded_product") and b1.metrics.has("target_mines"), "L%d 指标集输出降级标记与目标雷数(三分出口表)" % L) + check(b1.degraded_product == (mode == "accept_best"), "L%d 降级产物标记 = (accept_mode==accept_best)(%s→%s)" % [L, mode, str(b1.degraded_product)]) + check(b1.illegal_product == (mode == "accept_worst"), "L%d 非法产物标记 = (accept_mode==accept_worst)(%s→%s)" % [L, mode, str(b1.illegal_product)]) + check(not b1.degraded_product or not b1.illegal_product, "L%d 降级/非法产物标记互斥(两出口不可同真)" % L) + check(int(b1.metrics.actual_mines) <= int(b1.metrics.target_mines), "L%d 产物断言:实际雷数 %d ≤ 目标雷数 N=%d(先布后删只减不增)" % [L, int(b1.metrics.actual_mines), int(b1.metrics.target_mines)]) 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) + var b2 = board_generator.generate(L, 0, _mk_stream("board_gen", 777), _db) + var b3 = board_generator.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) + var b4 = board_generator.generate(L, 0, _mk_stream("board_gen", 778), _db) check(_board_dump(b2) != _board_dump(b4), "L%d 异种子产物不同(退化护栏)" % L) @@ -313,79 +329,86 @@ func _smoke_full_battle(L: int, D: int, label: String) -> void: 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() # 拍板③:关卡结束清库路径(战斗域不再清) + var lc = _last_settlement.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"同款) + # 每遍全新 RngService(同 master 种子)——回放断言的隔离前提(奖励道具经济 AC"两套全新 RngService+奖励道具经济"同款) 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 settlement = 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): + var waiting_probe := {"tested": false, "rejected": false} # 等待期刷新反向断言触达标记(通道 2026-09-15 整体移除) + settlement.step_row.connect(func(row): out.stream.append(row)) + settlement.battle_finished.connect(func(payload): out.result = payload.get("result", "")) + settlement.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-15 移除:结算窗口期(Waiting)发起刷新必须被拒且状态不变 + if settlement.round_index == 2: + waiting_probe.tested = true + waiting_probe.rejected = not settlement.player_request_refresh() # 确定性策略(窗口至多一件——2026-09-11 用户拍板): # 半血以下优先用治疗包;否则第 3 轮起且最强单刀 ≥ 敌剩余总 HP(击杀有把握)时用该刀;否则跳过 - var s: Dictionary = b1.presentation_snapshot() # E1 收口:经呈现快照面读英雄/敌况(禁直读 b2) + var s: Dictionary = settlement.presentation_snapshot() # E1 收口:经呈现快照面读英雄/敌况(禁直读 enemy_system) 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}): + settlement.submit_window_selection(selection)) + settlement.minesweeper.cell_revealed.connect(func(evt): out.events.append(evt)) + settlement.minesweeper.flag_judged.connect(func(evt): out.events.append(evt)) + settlement.minesweeper.round_end_board.connect(func(p): out.events.append({"ev": "round_end_board", "path_tag": p.path_tag})) + settlement.action_economy.round_end_stopped.connect(func(p): out.events.append({"ev": "round_end_stopped", "path_tag": p.path_tag})) + var minesweeper = settlement.minesweeper + var action_economy = settlement.action_economy + _last_settlement = settlement + if not settlement.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) + _auto_play_round(settlement, out) if out.result == "": out.result = "timeout" out.terminated = out.result == "victory" or out.result == "defeat" + # 等待期刷新反向断言(等待期换图通道 2026-09-15 整体移除):Waiting 相位请求必须被拒 + if waiting_probe.tested: + check(waiting_probe.rejected, "L%d·D%d 结算等待期刷新被拒(等待期换图通道已移除)" % [L, D]) + else: + print(" (跳过)L%d·D%d 等待期刷新反向断言未触达(该场战斗无第 2 轮结算窗口)" % [L, D]) 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 +func _auto_play_round(settlement, out: Dictionary) -> void: + var minesweeper = settlement.minesweeper + var action_economy = settlement.action_economy + var start_round: int = settlement.round_index # 确定性自动玩家:挖最近的已探明邻接未开格;无可挖则停手(结算窗口由信号回调提交) var guard := 400 - while a3.phase == "Active" and b1.round_index == start_round and guard > 0 and out.result == "": + while action_economy.phase == "Active" and settlement.round_index == start_round and guard > 0 and out.result == "": guard -= 1 - var plan := AutoPlayer.choose_dig(a2) + var plan := AutoPlayer.choose_dig(minesweeper) 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: + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + if action_economy.phase != "Active" or settlement.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"}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + if action_economy.phase == "Active" and settlement.round_index == start_round and out.result == "": + minesweeper.settle_batch([{"action": "stop"}]) -# ---- B4 伤害量值区间批冒烟(2026-09-14:身份批 roll 域内 + 中点锚 + 变异实际流动 + 定值行不受扰)---- +# ---- 奖励道具经济·伤害量值区间批冒烟(2026-09-14:身份批 roll 域内 + 中点锚 + 变异实际流动 + 定值行不受扰)---- func _smoke_power_interval() -> void: - print("--- B4 伤害量值区间(上阶/下阶随机)---") - # ① 表侧静态锚:五行配对齐全、power = 中点(A3 条款①期望当量锚) + print("--- 奖励道具经济·伤害量值区间(上阶/下阶随机)---") + # ① 表侧静态锚:五行配对齐全、power = 中点(行动经济 条款①期望当量锚) 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: @@ -401,7 +424,7 @@ func _smoke_power_interval() -> void: # ② 运行侧: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 reward_economy_factory = load("res://core/reward_economy.gd") var eligible: Array = [] for i in range(6): eligible.append({"cell": Vector2i(i, 0), "fog": false}) @@ -409,11 +432,11 @@ func _smoke_power_interval() -> void: 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): + var reward_economy = reward_economy_factory.new(_db, rng) + if not reward_economy.roll_board(eligible): continue for e in eligible: - var snap: Dictionary = b4.query_full_snapshot(e.cell) + var snap: Dictionary = reward_economy.query_full_snapshot(e.cell) var rid := String(snap.reward_id) if not seen.has(rid): seen[rid] = {} @@ -424,7 +447,7 @@ func _smoke_power_interval() -> void: in_domain = false elif int(snap.power) != _row_power(rid): fixed_ok = false # 定值行快照 power 必须仍是表值 - var cs: Dictionary = b4.grant_clear() + var cs: Dictionary = reward_economy.grant_clear() var cv := int(cs.power) seen["clear_blast"][cv] = true if cv < 25 or cv > 55: @@ -436,10 +459,10 @@ func _smoke_power_interval() -> void: 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 reward_economy_node = reward_economy_factory.new(_db, rng) var node_ok := true for trial in range(8): - var inst = b4n.grant_node_active(Vector2i(-1, -1), 0) + var inst = reward_economy_node.grant_node_active(Vector2i(-1, -1), 0) var rid := String(inst.item_id) if interval_rows.has(rid): var v := int(inst.snapshot.power) @@ -457,67 +480,67 @@ func _row_power(rid: String) -> int: -# ---- A3 轮内刷新冒烟(2026-09-12 方案 A 用户拍板:Active 相位当场换图继续挖)---- +# ---- 行动经济·轮内刷新冒烟(2026-09-12 方案 A 用户拍板:Active 相位当场换图继续挖)---- func _smoke_active_refresh() -> void: - print("--- A3 轮内刷新(方案 A)---") + print("--- 行动经济·轮内刷新(方案 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) + var settlement = 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): + settlement.board_changed.connect(func(): ct["board_swaps"] += 1) + settlement.minesweeper.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): + settlement.window_opened.connect(func(items): settlement.submit_window_selection([])) + settlement.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 + check(settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "轮内刷新战斗启动(Boss 行 150HP——承载多轮样本)") + var minesweeper = settlement.minesweeper # 轮 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 相位") + minesweeper.settle_batch([{"action": "stop"}]) + check(settlement.round_index == 4 and settlement.action_economy.rap == 24, "三轮停手攒 RAP=24(round=%d rap=%d)" % [settlement.round_index, settlement.action_economy.rap]) + check(settlement.action_economy.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 plan = AutoPlayer.choose_dig(minesweeper) + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + var ap_before: int = settlement.action_economy.ap + var board_before = minesweeper.current_board() var swaps_before: int = ct["board_swaps"] - var ok1: bool = b1.player_request_refresh() + var ok1: bool = settlement.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) + check(settlement.action_economy.phase == "Active" and settlement.action_economy.ap == ap_before, "相位保持 Active、AP 保留不重置(ap=%d)" % settlement.action_economy.ap) + check(settlement.action_economy.rap == 16, "RAP -8(%d)" % settlement.action_economy.rap) + check(int(ct["board_swaps"]) == swaps_before + 1 and minesweeper.current_board() != board_before, "当场换新图(board_changed +1)") + var plan2 = AutoPlayer.choose_dig(minesweeper) + if plan2.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan2.hero_cell}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan2.dig_cell}]) + var ok2: bool = settlement.player_request_refresh() + check(ok2, "轮内第 2 次刷新受理(RAP=%d)" % settlement.action_economy.rap) + var plan3 = AutoPlayer.choose_dig(minesweeper) + if plan3.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan3.hero_cell}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan3.dig_cell}]) + var ok3: bool = settlement.player_request_refresh() + check(not ok3, "第 3 次刷新被拒(每轮兜底上限 2——RAP %d 足额仍拒)" % settlement.action_economy.rap) # 跨图 facts 完整兑现:拒绝后挖到 AP 耗尽自动结算(扩大跨图样本——三段挖掘分属三张图) var dig_guard := 20 - while b1.a3.phase == "Active" and b1.round_index == 4 and dig_guard > 0: + while settlement.action_economy.phase == "Active" and settlement.round_index == 4 and dig_guard > 0: dig_guard -= 1 - var p = AutoPlayer.choose_dig(a2) + var p = AutoPlayer.choose_dig(minesweeper) if p.is_empty(): - a2.settle_batch([{"action": "stop"}]) + minesweeper.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]) + if p.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": p.hero_cell}]) + minesweeper.settle_batch([{"action": "dig", "cell": p.dig_cell}]) + check(settlement.round_index == 5 and settlement.battle_result == "", "轮 4 挖至 AP 耗尽自动结算(round=%d result='%s')" % [settlement.round_index, settlement.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"])) # 回放确定性:同种子两遍步骤流逐行相同(含两次轮内刷新取号序) @@ -530,49 +553,49 @@ func _smoke_active_refresh() -> void: 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 settlement = 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)}): + settlement.step_row.connect(func(row): rows.append(row)) + settlement.window_opened.connect(func(items): settlement.submit_window_selection([])) + if not settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}): return rows - var a2 = b1.a2 + var minesweeper = settlement.minesweeper for i in range(3): - a2.settle_batch([{"action": "stop"}]) + minesweeper.settle_batch([{"action": "stop"}]) for attempt in range(3): - var plan = AutoPlayer.choose_dig(a2) + var plan = AutoPlayer.choose_dig(minesweeper) 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"}]) + if plan.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}]) + minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) + settlement.player_request_refresh() + minesweeper.settle_batch([{"action": "stop"}]) return rows -# ---- C3 DifficultyTable 结构位冒烟(AC-10:空表零行为 + 套乘点 = 行值快照前)---- +# ---- 难度曲线与Boss DifficultyTable 结构位冒烟(AC-10:空表零行为 + 套乘点 = 行值快照前)---- func _smoke_difficulty_table() -> void: - print("--- C3 DifficultyTable 结构位 ---") + print("--- 难度曲线与Boss·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] + # ① 空表零行为:敌人系统.spawn 敌值 = EnemyTable 行值原样 + var enemy_system = load("res://core/unit_container.gd").new(_db) + check(enemy_system.spawn(1, 0, 50), "spawn 受理") + var grub = enemy_system.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] + var enemy_system_b = load("res://core/unit_container.gd").new(_db) + check(enemy_system_b.spawn(1, 0, 50), "修饰后 spawn 受理") + var g2 = enemy_system_b.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)) + var enemy_system_c = load("res://core/unit_container.gd").new(_db) + check(enemy_system_c.spawn(1, 0, 50), "越域修饰 spawn 受理") + check(int(enemy_system_c.all_enemies()[0].hp) == 240, "越域乘数 clamp 至 10(24→%d)" % int(enemy_system_c.all_enemies()[0].hp)) _db.difficulty.rows.clear() # 还原空表(后续冒烟/关卡断言基线) @@ -581,12 +604,12 @@ func _smoke_difficulty_table() -> void: 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") + var enemy_system = load("res://core/unit_container.gd").new(_db) + check(enemy_system.spawn(1, 0, 50) and String(enemy_system.all_enemies()[0].armor_type) == "none", "goblin 护甲 = none(教学位无甲)") + var enemy_system_b = load("res://core/unit_container.gd").new(_db) + check(enemy_system_b.spawn(1, 2, 50) and String(enemy_system_b.all_enemies()[0].armor_type) == "a", "bugbear 护甲 = a") + var enemy_system_c = load("res://core/unit_container.gd").new(_db) + check(enemy_system_c.spawn(1, 4, 50) and String(enemy_system_c.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") @@ -618,42 +641,42 @@ func _smoke_damage_types() -> void: 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 settlement = 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)}): + settlement.step_row.connect(func(row): rows.append(row)) + settlement.window_opened.connect(func(items): settlement.submit_window_selection([])) + if not settlement.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) + var minesweeper = settlement.minesweeper + var safe_t := _find_flag_target(minesweeper, false) + var mine_t := _find_flag_target(minesweeper, 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 safe_t.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": safe_t.hero_cell}]) + minesweeper.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"}]) + if mine_t.hero_cell != minesweeper.hero_pos(): + minesweeper.settle_batch([{"action": "move", "cell": mine_t.hero_cell}]) + minesweeper.settle_batch([{"action": "flag_place", "cell": mine_t.flag_cell}]) + minesweeper.settle_batch([{"action": "stop"}]) return {"rows": rows} -# 板真相直读找旗目标:未开 + 指定雷/安全属性 + 存在可达已开邻格(测试允许读板真相——A2 冒烟同口径) -func _find_flag_target(a2, want_mine: bool) -> Dictionary: - var revealed: Array = a2.revealed_cells() +# 板真相直读找旗目标:未开 + 指定雷/安全属性 + 存在可达已开邻格(测试允许读板真相——排雷玩法 冒烟同口径) +func _find_flag_target(minesweeper, want_mine: bool) -> Dictionary: + var revealed: Array = minesweeper.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: + for y in minesweeper.current_board().height: + for x in minesweeper.current_board().width: var c := Vector2i(x, y) - if a2.is_revealed(c) or a2.has_flag(c): + if minesweeper.is_revealed(c) or minesweeper.has_flag(c): continue - if a2.current_board().is_mine(c) != want_mine: + if minesweeper.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()): + for n in minesweeper.current_board().neighbors8(c): + if revealed_set.has(n) and (minesweeper.compute_move_path(n).size() > 0 or n == minesweeper.hero_pos()): return {"flag_cell": c, "hero_cell": n} return {} @@ -663,17 +686,17 @@ 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 settlement = 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) + logger.attach_battle(settlement) var snaps: Array = [] - b1.settlement_snapshot.connect(func(snap): snaps.append(snap)) + settlement.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 + check(settlement.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 50}), "快照战斗启动") + var minesweeper = settlement.minesweeper for i in range(2): - a2.settle_batch([{"action": "stop"}]) + minesweeper.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"] @@ -706,7 +729,7 @@ func _smoke_playtest_logger() -> void: logger.close_session() -# ---- 遭遇随机化冒烟(C3 Core Rule 8:区间骰/encounter 流/可回放/空表定值路径)---- +# ---- 遭遇随机化冒烟(难度曲线与Boss Core Rule 8:区间骰/encounter 流/可回放/空表定值路径)---- func _smoke_encounter_roll() -> void: print("--- 遭遇随机化(unit_count 区间骰)---") @@ -714,9 +737,9 @@ func _smoke_encounter_roll() -> void: 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()) + var settlement = load("res://core/battle_orchestrator.gd").new(_db, rng) + check_silent(settlement.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 50})) + counts.append(settlement.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) @@ -732,18 +755,18 @@ func _smoke_encounter_roll() -> void: # 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() + var settlement_d1 = load("res://core/battle_orchestrator.gd").new(_db, rng3) + check(settlement_d1.begin_battle({"level_index": 1, "node_depth": 1, "hero_hp": 50}), "D1 战斗启动") + var n1: int = settlement_d1.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(首遇教学位)") + var settlement_d2 = load("res://core/battle_orchestrator.gd").new(_db, rng4) + settlement_d2.begin_battle({"level_index": 1, "node_depth": 2, "hero_hp": 50}) + check(settlement_d2.presentation_snapshot().enemies.size() == 1, "D2 熊地精固定 1(首遇教学位)") # 全深度 × 多种子域内扫描(playtest-2026-09-12 报告 Bug#1 回归——区间下限 1 不可掷 0: - # B1 roll_lo 下限 + B2 spawn count<1 fail-fast 双闸;单深度单种子是盲区,全域扫描才钉死不变式) + # 战斗结算 roll_lo 下限 + 敌人系统 spawn count<1 fail-fast 双闸;单深度单种子是盲区,全域扫描才钉死不变式) var depths: Array = [] for node in _level_battle_nodes(1): depths.append(int(node.get("node_depth", -1))) @@ -754,11 +777,11 @@ func _smoke_encounter_roll() -> void: 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}): + var settlement_scan = load("res://core/battle_orchestrator.gd").new(_db, rngs) + if not settlement_scan.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 n: int = settlement_scan.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)) @@ -778,26 +801,26 @@ func _level_battle_nodes(level_index: int) -> Array: return out -# ---- B1 战斗段 fresh 池攻击链回归(playtest-2026-09-12 报告 Bug#2 沉淀—— +# ---- 战斗段 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)---") + print("--- 战斗结算·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 settlement = load("res://core/battle_orchestrator.gd").new(_db, rng) var ticks: Array = [] # {raw, tick}——敌行动伤害行重建 - b1.step_row.connect(func(row): + settlement.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 行)") + settlement.window_opened.connect(func(items): settlement.submit_window_selection([])) + check(settlement.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 != "": + if settlement.battle_result != "": break - b1.a2.settle_batch([{"action": "stop"}]) + settlement.minesweeper.settle_batch([{"action": "stop"}]) check(ticks.size() >= 2, "非蓄力攻击拍采样充足(%d 拍——charge 拍零兑付为设计语义不计)" % ticks.size()) var all_full := true for t in ticks: @@ -809,11 +832,229 @@ 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()) + var settlement = load("res://core/battle_orchestrator.gd").new(_db, rng) + settlement.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 50}) + out.append(settlement.presentation_snapshot().enemies.size()) return out + +# ==================== 清图奖励双份兑付链 / 轮内刷新 provenance 守卫 / 窗口锚行(2026-09-15 补覆盖批) ==================== +# 清图 = 全安全格开完。清图测试用"真雷旗(零误标→完美排雷)+ AutoPlayer 逐轮清图"真实流驱动; +# DifficultyTable 注入 hp_mult=10 把 Boss 血量抬到 1500,保证清图批次发生在战斗结束之前(测试台账,跑完即还原)。 + +const CLEAR_SMOKE_DEPTH := 4 +const CLEAR_SMOKE_HERO_HP := 100000 +var _regular_clear_reward_rows := 0 # 常规(非刷新图)清图的 clear_reward 行数——刷新守卫对照基线 + + +func _smoke_clear_reward_chain() -> void: + print("--- 清图奖励·双份兑付链(完美排雷 / 零兑付 / 第二份被拒 / 高光记实)---") + # ① 真实流完美排雷:标中一面真雷旗 + 零误标 + 清全图 → grant_clear 身份(instant_aoe)双份真实兑付 + var rows: Array = [] + var run := _run_full_clear(20260915, false, rows) + check(bool(run.cleared), "① 完美排雷清图达成(%d 轮)" % int(run.rounds)) + var dmg := [] + for r in rows: + if String(r.get("type", "")) == "damage" and String(r.get("source_class", "")) == "clear_reward": + dmg.append(r) + var hl := _rows_of_type(rows, "highlight") + _regular_clear_reward_rows = dmg.size() + check(dmg.size() == 2, "① 清图奖励双份真实兑付(clear_reward 伤害行 ×%d——instant_aoe 身份两份各打一发)" % dmg.size()) + if dmg.size() == 2: + check(String(dmg[0].display_name).contains("清图奖励") and String(dmg[1].display_name).contains("完美第二份"), + "① 第二份按身份类型走真实兑付(首份『清图奖励』/ 第二份『完美第二份』)") + check(hl.size() == 1 and int(hl[0].value) == 2, "④ 高光行 value = 实际有效份数 2(不记名义值;行数 %d)" % hl.size()) + if hl.size() == 1: + check(String(hl[0].get("highlight", "")) == "perfect_sweep", "高光标记 = perfect_sweep") + # ②/③ 弱形式:正常战斗流不可达的零兑付分支(见函数头注) + _smoke_clear_reward_zero_paths() + + +# 弱形式登记:正常战斗流下"授段时刻无存活敌"不可达——敌全灭当轮判定段即判胜并终止战斗,而清图批在判定段之前, +# 恒有存活敌;故本组直调编排私有面构造状态,只断言"路径存在 + 行为符合契约"(构造手法见各行注释)。 +func _smoke_clear_reward_zero_paths() -> void: + # ②-a 首份零兑付判定(无存活敌)→ first_ok=false + 未生效行 + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260915) + var s = load("res://core/battle_orchestrator.gd").new(_db, rng) + var rows: Array = [] + s.step_row.connect(func(row): rows.append(row)) + s.window_opened.connect(func(items): s.submit_window_selection([])) + check(s.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80}), + "零兑付构造战斗启动") + for u in s._enemy_system.all_enemies(): + s._enemy_system.apply_damage(u, int(u.hp)) # 测试通道:构造"无存活敌"(弱形式——全灭而非 despawn) + var snap = s._reward_economy.grant_clear() + check(snap != null and String(snap.category) == "instant_aoe", + "清图槽身份 = instant_aoe(clear_default 唯一活行——第二份同身份兑付)") + rows.clear() + var first_ok: bool = s._grant_clear_identity(snap, 1) + check(not first_ok, "② 即时类首份零兑付(无存活敌)→ first_ok=false") + check(_has_source_row(rows, "reward_ineffective", "clear_reward", "instant_aoe"), + "② 零兑付产出 reward_ineffective 行(batch_key=instant_aoe)") + # ②-b 高光守卫:perfect 谓词成立但首份零兑付 → 高光行不发(不记名义值) + var rng_b = load("res://core/rng_service.gd").new() + rng_b.set_master_seed(20260915) + var s_b = load("res://core/battle_orchestrator.gd").new(_db, rng_b) + var rows_b: Array = [] + s_b.step_row.connect(func(row): rows_b.append(row)) + s_b.window_opened.connect(func(items): s_b.submit_window_selection([])) + check(s_b.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80}), + "高光守卫构造战斗启动") + for u in s_b._enemy_system.all_enemies(): + s_b._enemy_system.apply_damage(u, int(u.hp)) + s_b._perfect_hit_true = 1 # 测试通道:构造完美谓词成立(真值在正常流由旗判定累加) + s_b._perfect_hit_false = 0 + rows_b.clear() + s_b._grant_clear_reward(1) + check(_has_source_row(rows_b, "reward_ineffective", "clear_reward", "instant_aoe"), + "② _grant_clear_reward 守卫路径重走首份 → 未生效行仍在案") + check(_rows_of_type(rows_b, "highlight").is_empty(), "② 首份零兑付 → 高光行不发(不记名义值 2)") + # ③ 第二份被拒:唯一存活敌 HP ≤ 首份 aoe 量 → 首份击杀 → 第二份无存活敌(zero + 未生效行) + var rng2 = load("res://core/rng_service.gd").new() + rng2.set_master_seed(20260915) + var s2 = load("res://core/battle_orchestrator.gd").new(_db, rng2) + var rows2: Array = [] + s2.step_row.connect(func(row): rows2.append(row)) + s2.window_opened.connect(func(items): s2.submit_window_selection([])) + check(s2.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80}), + "第二份被拒构造战斗启动") + var boss = s2._enemy_system.all_enemies()[0] + boss.hp = 1 # 测试通道:压低至首份 aoe 必杀(弱形式——正常流 Boss 血量不可控) + s2._perfect_hit_true = 1 + s2._perfect_hit_false = 0 + rows2.clear() + s2._grant_clear_reward(1) + check(_has_source_row(rows2, "damage", "clear_reward"), "③ 首份 aoe 真实兑付(击杀唯一存活敌)") + check(_has_source_row(rows2, "reward_ineffective", "clear_reward", "instant_aoe"), + "③ 第二份被拒产出 reward_ineffective 行(零兑付)") + var hl2 := _rows_of_type(rows2, "highlight") + check(hl2.size() == 1 and int(hl2[0].value) == 1, "④ 高光行 value = 实际有效份数 1(不记名义值 2)") + + +# 轮内刷新 provenance 守卫:刷新换来的图同轮清图不发清图奖励(_board_from_midround_refresh 守卫) +func _smoke_refresh_provenance_guard() -> void: + print("--- 轮内刷新 provenance 守卫(刷新图清图不发清图奖励)---") + var rows: Array = [] + var run := _run_full_clear(20260916, true, rows) + check(bool(run.refreshed), "轮内刷新受理(三轮停手攒 RAP 后 Active 相位发起)") + check(bool(run.cleared), "刷新图清图达成(%d 轮)" % int(run.rounds)) + var cr := 0 + var hl := 0 + for r in rows: + if String(r.get("source_class", "")) == "clear_reward": + cr += 1 + if String(r.get("type", "")) == "highlight": + hl += 1 + check(cr == 0, "刷新图清图 → 无 clear_reward 行(守卫在位——实测 %d 行)" % cr) + check(hl == 0, "刷新图清图 → 无高光行(守卫在位——实测 %d 行)" % hl) + check(_regular_clear_reward_rows > 0, "对照基线:常规换图清图有 clear_reward 行(×%d——守卫非全局静默)" % _regular_clear_reward_rows) + + +# 窗口段锚行(战斗结算 验收:"窗口段恒在案,零选择记跳过""空库存记跳过") +func _smoke_window_anchor_rows() -> void: + print("--- 道具窗口段锚行(打开 / 零选择跳过 / 空库存跳过)---") + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(20260917) + var s = load("res://core/battle_orchestrator.gd").new(_db, rng) + var rows: Array = [] + var probe := {"opens": 0, "open_ok": 0, "skips": 0, "empty_skips": 0} + s.step_row.connect(func(row): + rows.append(row) + if String(row.get("type", "")) == "window_skip": + probe["skips"] = int(probe["skips"]) + 1 + if int(row.value) == 0 and String(row.display_name).contains("空库存"): + probe["empty_skips"] = int(probe["empty_skips"]) + 1) + s.window_opened.connect(func(items): + probe["opens"] = int(probe["opens"]) + 1 + if not rows.is_empty(): + var last: Dictionary = rows[rows.size() - 1] + if String(last.get("type", "")) == "window_open" and int(last.value) == items.size(): + probe["open_ok"] = int(probe["open_ok"]) + 1 + s.submit_window_selection([])) + check(s.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": CLEAR_SMOKE_HERO_HP, "hero_max_hp": CLEAR_SMOKE_HERO_HP}), + "窗口锚行战斗启动") + # 轮 1 零挖掘停手 → 库存空 → 记跳过锚行(空库存不弹窗) + s.minesweeper.settle_batch([{"action": "stop"}]) + check(int(probe["opens"]) == 0 and int(probe["empty_skips"]) == 1, + "空库存记跳过(window_skip ×%d,窗口未打开 ×%d)" % [int(probe["empty_skips"]), int(probe["opens"])]) + # 后续轮:挖到首件可选类入库 → 窗口打开锚行 + 零选择跳过锚行 + var guard := 10 + var out := {"result": "", "rounds": 0, "terminated": false, "stream": []} + while int(s.presentation_snapshot().in_stock) == 0 and guard > 0 and s.battle_result == "": + guard -= 1 + _auto_play_round(s, out) + check(int(probe["opens"]) >= 1, "窗口打开锚行触达(window_opened ×%d)" % int(probe["opens"])) + check(int(probe["open_ok"]) == int(probe["opens"]), + "每个 window_opened 前恰有 window_open 锚行且值 = 可选件数(%d/%d)" % [int(probe["open_ok"]), int(probe["opens"])]) + check(int(probe["skips"]) >= int(probe["opens"]), + "零选择记跳过锚行(window_skip %d ≥ window_open %d)" % [int(probe["skips"]), int(probe["opens"])]) + + +# 清图驱动(真实流):标一面真雷旗(完美前置)→ 逐轮 AutoPlayer 挖到轮末;board_seq 递增 = 清图换图。 +# from_refresh=true 时先三轮停手攒 RAP、第 4 轮轮内刷新换图,再清刷新图。 +func _run_full_clear(seed_val: int, from_refresh: bool, rows: Array) -> Dictionary: + _db.difficulty.rows.append({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hp_mult": 10.0, "atk_mult": 1.0}) + var rng = load("res://core/rng_service.gd").new() + rng.set_master_seed(seed_val) + var settlement = load("res://core/battle_orchestrator.gd").new(_db, rng) + settlement.step_row.connect(func(row): rows.append(row)) + settlement.window_opened.connect(func(items): settlement.submit_window_selection([])) + var res := {"cleared": false, "refreshed": false, "rounds": 0} + if not settlement.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, + "hero_hp": CLEAR_SMOKE_HERO_HP, "hero_max_hp": CLEAR_SMOKE_HERO_HP}): + _db.difficulty.rows.clear() + return res + if from_refresh: + for i in range(3): + settlement.minesweeper.settle_batch([{"action": "stop"}]) + res["refreshed"] = settlement.player_request_refresh() + var drive := _drive_clear(settlement) + res["cleared"] = drive.cleared + res["rounds"] = drive.rounds + _db.difficulty.rows.clear() # 测试台账还原(后续冒烟基线) + return res + + +func _drive_clear(settlement) -> Dictionary: + var flag_done := false + var rounds := 0 + while rounds < 60: + rounds += 1 + var ms = settlement.minesweeper + if not flag_done: + var t := _find_flag_target(ms, true) + if not t.is_empty(): + if t.hero_cell != ms.hero_pos(): + ms.settle_batch([{"action": "move", "cell": t.hero_cell}]) + ms.settle_batch([{"action": "flag_place", "cell": t.flag_cell}]) + flag_done = true # 一面真雷旗 = 完美排雷前半(零误标),后半靠 AutoPlayer 清图 + var seq_before: int = int(settlement.diagnostic_snapshot().board_seq) + var out := {"result": "", "rounds": 0, "terminated": false, "stream": []} + _auto_play_round(settlement, out) + if int(settlement.diagnostic_snapshot().board_seq) > seq_before: + return {"cleared": true, "rounds": rounds} # 清图换图 = board_seq 递增 + if settlement.battle_result != "": + return {"cleared": false, "rounds": rounds} + return {"cleared": false, "rounds": rounds} + + +func _rows_of_type(rows: Array, t: String) -> Array: + var out: Array = [] + for r in rows: + if String(r.get("type", "")) == t: + out.append(r) + return out + + +func _has_source_row(rows: Array, t: String, source: String, batch_key: String = "") -> bool: + for r in rows: + if String(r.get("type", "")) != t or String(r.get("source_class", "")) != source: + continue + if batch_key == "" or String(r.get("batch_key", "")) == batch_key: + return true + return false + func check_silent(cond: bool) -> void: _checks += 1 if not cond: diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd index 178b2459..ed0feb8a 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd @@ -76,12 +76,12 @@ func _ready() -> void: _done = true print("[click_probe] 关卡结束:%s" % main._end_overlay.verdict_text().replace("\n", " ")) break - if main.b1 == null or main.runner == null: + if main.settlement == null or main.runner == null: continue - var diag: Dictionary = main.b1.diagnostic_snapshot() # 诊断观测面(私有字段零直读) + var diag: Dictionary = main.settlement.diagnostic_snapshot() # 诊断观测面(私有字段零直读) var st := { - "node": main.runner.node_index, "round": main.b1.round_index, - "phase": main.b1.a3.phase, "ap": main.b1.a3.ap, + "node": main.runner.node_index, "round": main.settlement.round_index, + "phase": main.settlement.action_economy.phase, "ap": main.settlement.action_economy.ap, "win": main.inventory_panel.is_window_open(), "settling": diag.settling, "await": diag.awaiting_window, } @@ -94,7 +94,7 @@ func _ready() -> void: _stall_frames = 0 _ap_spent_this_round = 0 _last_state = st - if main.b1.battle_result == "" and main.b1.a3.phase == "Active": + if main.settlement.battle_result == "" and main.settlement.action_economy.phase == "Active": _run_modal_probes_once() _play_one_action() print("[click_probe] 循环退出(done=%s budget=%s)选用生效=%s 选用失效=%s 问题=%d" % [ @@ -156,17 +156,17 @@ func _run_modal_probes_once() -> void: # ④ 弃图刷新确认链(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() + var rap_before: int = int(main.settlement.action_economy.rap) + var board_before = main.settlement.minesweeper.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: + if int(main.settlement.action_economy.rap) != rap_before - 8: + _problems.append("弃图刷新未扣 RAP8(%d→%d)" % [rap_before, int(main.settlement.action_economy.rap)]) + if main.settlement.action_economy.phase != "Active": + _problems.append("轮内刷新后相位丢失(%s)" % main.settlement.action_economy.phase) + if main.settlement.minesweeper.current_board() == board_before: _problems.append("轮内刷新未当场换图") if main._refresh_used_round != 1: _problems.append("刷新轮内计数错误(%d)" % main._refresh_used_round) @@ -187,13 +187,13 @@ func _assert_window_state_once() -> void: func _play_one_action() -> void: - var b1 = main.b1 - if b1 == null: - return # 结算竞态防抖:battle_finished 与本轮动作调度同帧到达时 b1 可能已被置空(基线既有噪声——加固) - var a2 = b1.a2 + var settlement = main.settlement + if settlement == null: + return # 结算竞态防抖:battle_finished 与本轮动作调度同帧到达时 settlement 可能已被置空(基线既有噪声——加固) + var minesweeper = settlement.minesweeper # 行为①:随机插旗(右键——未开相邻格),约 20% 轮次 if _ap_spent_this_round == 0 and _rand.randf() < 0.2: - var target := _random_unknown_adjacent(a2) + var target := _random_unknown_adjacent(minesweeper) if target.x >= 0: main.board_view.click_cell(target, MOUSE_BUTTON_RIGHT) _flags_placed.append(target) @@ -202,33 +202,33 @@ func _play_one_action() -> void: # 行为②:随机拔旗(右键已插旗格),约 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): + if minesweeper.has_flag(fc) and not minesweeper.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) + main.board_view.click_cell(Vector2i(_rand.randi_range(0, minesweeper.current_board().width - 1), _rand.randi_range(0, minesweeper.current_board().height - 1)), MOUSE_BUTTON_LEFT) return # 挖掘主策略(最近可挖格) - var plan = AutoPlayer.choose_dig(a2) # O4 收口:策略单一实现 + var plan = AutoPlayer.choose_dig(minesweeper) # O4 收口:策略单一实现 if not plan.is_empty(): - if plan.hero_cell != a2.hero_pos(): + if plan.hero_cell != minesweeper.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: + if _ap_spent_this_round >= 3 and settlement.action_economy.phase == "Active" and _rand.randf() < 0.15: _click_stop() - elif b1.a3.phase == "Active": + elif settlement.action_economy.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 收口:经呈现快照面读库存/英雄 + if _rand.randf() < 0.5 and main.settlement != null: + var settlement = main.settlement + var s: Dictionary = settlement.presentation_snapshot() # E1 收口:经呈现快照面读库存/英雄 var pos := -1 for inst in s.inventory: if inst.status != "InStock": @@ -237,11 +237,11 @@ func _answer_window() -> void: 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]) + settlement.submit_window_selection([pos]) main.inventory_panel.close_window() # 缺陷④回归断言:被选件必须真实消耗(Used),不得误耗邻件/留存 - var after: Array = b1.presentation_snapshot().inventory - if not b1.is_battle_live(): + var after: Array = settlement.presentation_snapshot().inventory + if not settlement.is_battle_live(): # 终局结算窗口:选用后战斗立即结束、level_clear 合法清库——Used 断言不适用 _used_ok += 1 elif inv_index < after.size() and String(after[inv_index].status) == "Used": @@ -258,20 +258,20 @@ func _answer_window() -> void: func _skip_window() -> void: - if main.b1 != null: - main.b1.submit_window_selection([]) + if main.settlement != null: + main.settlement.submit_window_selection([]) main.inventory_panel.close_window() -func _random_unknown_adjacent(a2) -> Vector2i: - var hero: Vector2i = a2.hero_pos() +func _random_unknown_adjacent(minesweeper) -> Vector2i: + var hero: Vector2i = minesweeper.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): + if minesweeper.current_board().in_bounds(c) and not minesweeper.is_revealed(c) and not minesweeper.has_flag(c): cands.append(c) if cands.is_empty(): return Vector2i(-1, -1) @@ -279,7 +279,7 @@ func _random_unknown_adjacent(a2) -> Vector2i: func _click_stop() -> void: - # 停手走真实按钮信号路径(AP 条 → 组合根 settle_batch——A2 D1×6① 回归载体) + # 停手走真实按钮信号路径(AP 条 → 组合根 settle_batch——排雷玩法 D1×6① 回归载体) main.ap_bar._btn_stop.pressed.emit() @@ -291,22 +291,22 @@ func _report_stall(st: Dictionary) -> void: 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 == "": + elif st.phase == "Idle" and main.settlement.battle_result == "": why = "Idle 相位但战斗未结束(begin 失败?)" else: - why = "状态无推进(battle_result='%s')" % str(main.b1.battle_result) + why = "状态无推进(battle_result='%s')" % str(main.settlement.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() # 诊断观测面(私有字段零直读) + var diag: Dictionary = main.settlement.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)]) + str(main.runner.node_index), str(main.settlement.round_index), + str(main.settlement.action_economy.phase), str(main.settlement.action_economy.ap), str(main.settlement.action_economy.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)]) + str(diag.minesweeper_board_installed), str(diag.facts_path), str(diag.refresh_used_round), str(_flags_placed)]) diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd index ed158eed..d487dd52 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd @@ -25,7 +25,7 @@ func _ready() -> void: 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 != "": + if main.settlement == null or main.settlement.battle_result != "": problems.append("首战未处于进行中") if not main.log_view.parsed_text().contains("节点 1/"): problems.append("节点头日志缺失") @@ -46,7 +46,7 @@ func _ready() -> void: 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)---- + # ---- 敌人卡投影(顺位/意图徽标常驻——敌人系统×4)---- var card = main.stage.enemy_box.get_child(0) if card != null and card._name_label.text == "": problems.append("敌人卡名称为空") @@ -75,7 +75,29 @@ func _ready() -> void: slots_row.set_effects([]) if not slots_row._slots.is_empty(): problems.append("BUG-0001 回归:空态未清槽(%d)" % slots_row._slots.size()) - # ---- L2 雾图变体段(A4:雾首遇一次性提示 / 雾格呈现 / 去饱和接口可用性)---- + # ---- 图标键回归(HUD框架 图标键契约:状态表六行全映射、奖励 category 五值全映射; + # 查不到的键 fail-fast + ? 占位——此处钉死映射不残缺,未知键断言由 _icon_cfg/_badge_glyph 承担)---- + if not StatusSlotRow.EFFECT_ICON.has("enrage") or not StatusSlotRow.EFFECT_ICON.has("off_guard"): + problems.append("状态图标表缺暴走/失衡两键(states 表六行须全映射)") + for cat in ["instant_aoe", "instant_heal", "damage_item", "heal_pack", "buff"]: + if not BoardView.REWARD_BADGE.has(cat): + problems.append("奖励图标表缺键(%s)" % cat) + var probe_kb: Array = [ + {"effect_id": "enrage", "display_name": "暴走", "layers": 1, "remaining_settlements": 1, "source_class": "flag_miss", "round_index": 1, "effective_value": 25}, + {"effect_id": "off_guard", "display_name": "失衡", "layers": 1, "remaining_settlements": 1, "source_class": "flag_miss", "round_index": 1, "effective_value": 25}, + ] + slots_row.set_effects(probe_kb) + if slots_row._slots.size() != 2: + problems.append("图标键回归:enrage/off_guard 未建槽(%d)" % slots_row._slots.size()) + else: + var ic_enrage: Label = slots_row._slots[0].get_child(0).get_child(0) + var ic_off: Label = slots_row._slots[1].get_child(0).get_child(0) + if ic_enrage.text == "?" or ic_enrage.text == "": + problems.append("暴走图标键未映射(icon=%s)" % ic_enrage.text) + if ic_off.text == "?" or ic_off.text == "": + problems.append("失衡图标键未映射(icon=%s)" % ic_off.text) + slots_row.set_effects([]) + # ---- L2 雾图变体段(地形变异:雾首遇一次性提示 / 雾格呈现 / 去饱和接口可用性)---- # 2026-09-13 单场用户入口已删——探针直调 debug_session 原语(battle_created/ready 仍经组合根接线; # _reset_run_scope 复现原 _start_single 的提示去重复位,保证 fog_first 断言前置态) main._reset_run_scope() @@ -85,7 +107,7 @@ func _ready() -> void: if main.board_view.board() == null: problems.append("L2 单场未启动(board=null)") elif main.board_view.board().fog_cells().is_empty(): - problems.append("L2 板无雾格(A4 变体缺失)") + problems.append("L2 板无雾格(地形变异 变体缺失)") elif not bool(main._hints_shown.get("fog_first", false)): problems.append("雾首遇一次性提示未触发(fog_first)") else: diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd index 8f4c4cc4..49a208ac 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd @@ -59,7 +59,7 @@ func _ready() -> void: main._start_full_level() # 完整关卡路径(_start_single 按语义清零滑条 = 恒基线,不承载加成断言) await get_tree().process_frame await get_tree().process_frame - var hero: Dictionary = main.b1.presentation_snapshot().hero + var hero: Dictionary = main.settlement.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", "?"))])