Compare commits

..
Author SHA1 Message Date
DKChan 6b5a96b6c7 iter45-卡②T2: 断点域拆件 breakpoint_store(R2整族归件v1.1)——六成员(save_manager/resume_bp/pending_next_level/last_level_payload/last_treasure_cleared/loadout_seeded)+BREAKPOINT_FIELDS 27键冻结+函数族九个归件:纯读侧三函数与_bp_parts/snapshot body逐字(宿主输入state/level_index/master_seed/runner/counters逐调传参——件禁反向持宿主),四_resume_*按所有权缝语句分家:件侧(主角账三件restore_fragment/curio池与特性还原/stop与bank片段回填/数值账规范化钳制)迁store五个回填函数,宿主侧(八项重绑定与_reset_curio_state/_attune_refresh_x_total调用)按原语句序吸收进resume_adventure壳,引用束九引用解包为原名成员保body逐字(6+9=15贴顶);宿主留_snapshot_breakpoint一行委托壳(CurioStop的Callable目标零改)与set_save_manager转发件,断点键面零变化,语句序逐步保形;测试:185改址BreakpointStoreScript.BREAKPOINT_FIELDS(判据零变化——第14节申报)〔补件 V2〕;验证:run_all三套件全绿+run_smoke 15项0失败 2026-10-31 00:15:43 +08:00
DKChan 96ec80aa97 iter45-卡②T1: 悬停域拆件 board_hover——11函数族+_hover/_last_click两成员迁出(函数体逐字,三处机械改写:_cell_at→_cb_cell_at.call带类型标注/hover_forbidden与chord_attempted.emit→注入Callable.call/mouse_default_cursor_shape写入→_cb_cursor.call),COL_HL_TARGET/CURSOR_DIG/CURSOR_MOVE_SEM/CHORD_WINDOW_MS随函数迁常量、COL_SEAL双侧服役各自alias UiKit单源,三入站口(_gui_input/_on_cell_input/click_cell)与hover_probe读面留宿主委托,_r0留宿主;构造=引用束字典注入(cells/minesweeper/diggable共享引用+格命中换算/光标写入/两信号发射Callable)防max-parameters新键,minesweeper重绑由rebuild同拍回写件成员〔补件 V1〕;验证:probe_gate --click --full全绿+render_gate golden 8屏OK;键账board_view类变量32→31(只降不升)/公开方法27持平 2026-10-31 00:15:42 +08:00
DKChan fe0aeb8f28 iter45-卡①T3: ui_input_probe 直读改址 u.face.*——四处断言(case①②③⑭ hl_visible/hl_color/dot_visible/mutation_mark,11处)+case④b封印纹理槽 st.tex_seal 两处(设计文档§1.4直读面清单漏计的第五处,L1就地修复)——断言条件与期望值零变化,只改读路径;T0定谳绿凭据下的改址义务落码收口〔状态件 V4〕;验证:probe_gate --click --full 全绿+render_gate golden三张0.00%复跑 2026-10-31 00:15:41 +08:00
DKChan 75780e31fa iter45-卡①T2: 站桩两状态件建件与宿主改造——_StandeeState 真实25成员沿身份命值/资产演出缝拆 standee_vitals(12成员,apply_hp=_set_hp整体搬移,hp_value setter逐字)/standee_motion(11成员,六迁入函数:set_portrait_texture(tex,flip,box)/register_hero_state/apply_hero_state(is_hero参判,三连早退序逐字)/reset_state(RESET绕同态早退两拍封装)/play_intent_motion(charge参判)/state_probe),HeroState枚举与_HS_NAME/_HS_VALUE随三态机归motion(全仓零外部消费实测);宿主10成员(五冻结var+st/mo+三演出探针计数器上收),转调壳三入口外形零变,fx_probe改读宿主计数器,tween路径串st.hp_value/mo._intent_scale/mo._glyph_rot,源码锚三函数(_draw_hp_bar/_draw_armor_name_line/set_pending)留宿主且锚行逐字(st=vitals名沿用故血条/名字行零改写)〔状态件 V3〕;battle_stage_view一行未改实测;孤立验证run_all两套件全绿+run_smoke 2026-10-31 00:15:39 +08:00
DKChan ea03765adc iter45-卡①T1: 单格两绘制域件建件与宿主瘦身——_CellPaintState 27成员沿绘制层域轴拆 cell_base_layer(12成员+九函数+九常量,draw_base入口)/cell_mark_overlay(13成员+六函数段+draw_centered_text单点化,四绘制入口),宿主退六步时序器类变量33→10(edge/cell/glow_group上收+glyph/glyph_color/plate/fog_state/_glow_layer冻结面原位+base/face两实例),board_view写入面49处改址(u.st.X→u.base./u.face./u.edge/u.glow_group,u.st.清零)〔状态件 V1/V2〕;逐字搬运+三处机械改写(st.X→裸成员/draw_X→canvas.draw_X/st.edge→rect.size.x),draw_glyph_layer增plate第5参(设计文档§2.1'plate绘制侧零消费'系413124b0恢复守卫前的过时实查——现行if plate:守卫在位,迁出即随行,保golden 0.00%零漂移);孤立验证:render_gate golden三张0.00%+run_all四套件全绿+run_smoke 15项0失败 2026-10-31 00:15:38 +08:00
103 changed files with 2937 additions and 4519 deletions

No files matched your search

+3 -11
View File
@@ -77,15 +77,7 @@
### 格式优先级
能画图的内容一律画图说明,图是第一优先载体;枚举用列表,二维对照用表格;散文是最后手段。
### 图优先
**判据**:描述含多个实体、实体间有明确关系(先后顺序、包含、依赖、状态转移、布局对比)的内容时,直接画图说明,不写长段文字;单点事实(一句装得下)不画,纯数据对照用表格。
**载体**:默认用 visualize 技能出 HTML/SVG(会话内联渲染,表现力优先),流程/状态/结构/实体关系与空间对比、像素级示意、界面草图同一通道;查看端不支持内联渲染时退回 Mermaid 代码块,仍不行用字符画。
**边界**:硬数值与前提条件留在文字里,图不承载唯一数值依据;图与文禁重复说同一件事,文字只留图说不清的部分。
枚举用列表,二维对照用表格,按内容形态选;散文是最后手段。
## 主动检索提醒
@@ -97,8 +89,8 @@
- **仓库根禁止设会话状态文件**(`active.md` 已废除)——现状看 roadmap「当前位」,待办看待办清单,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节)
- 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测
- 合回后验证 = 本地 CI 全量域闸全绿(唯一强制验证点;唯一入口、唯一闸清单、唯一域划分与选闸算法仍只在 `tools/run_all_gates.sh`,禁在文档另列;触发与追绿语义见「运行与验证」首条与 process-conventions §1)。开发期手动跑批按需选档(`--code` 关联档选测——02 跑「卡片申报 ∪ diff 推导」并集套件、三件保险恒跑、横切面回落全量——算法只在闸脚本内定义),手动跑不构成验收;全量回归由用户自发发起。文档面直改批验收=commit 点 git 钩子自动跑 `--docs`(process-conventions §1)
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由本地 CI 全量验证(红项修复提交追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,由用户发令在主工作区直改+直提(commit 点 git 钩子自动跑文档域闸);大体量改动(含触及代码面的工程改动如纯测试维护)按约定走 worktree 批(非强制);文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:(如需设计讨论)→ 用户发令直改 → 主区直改+commit 点闸(车道细则=process-conventions §1)
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由本地 CI 全量验证(红项修复提交追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,登记在案后由用户发令在主工作区直改+直提(commit 点 git 钩子自动跑文档域闸),完成即删条目;大体量改动(含触及代码面的工程改动如纯测试维护)按约定走 worktree 批(非强制);文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:登记 →(如需设计讨论)→ 用户发令直改 → 主区直改+commit 点闸 → 完成删条目(车道细则=process-conventions §1)
- MC/模拟:无数值改动零跑;档位硬闸见 `sim-data/README.md`「MC 采集档位表」,一档只能由用户发起
- 闸机轮次纪律:新码先孤立验证 → 出红只跑红件最小单元 → 长批先最小参数试车 → 开工扫坑册(process-conventions §6)
- **验收闭合**——每个需求点与每个行为改动都要有从真实入口进入的验收断言,可 grep 定位、在当轮真实跑批中执行并通过,判据绑被测改动特有的可观测提交面;**禁变异类测试与撤销类取证**(2026-10-24/2026-10-28 用户裁——人为制造红属伪造证据),证明做不出=停卡呈报;禁删断言/放宽阈值换绿;合回前独立验收审计未过则禁止合并(细则=test-conventions §8+iter 技能第 5 步)
+3 -8
View File
@@ -130,7 +130,7 @@
### 3.5 营地场景图
- **要什么**:营地场景图 10 张——`scene_camp_bg`(背景层,不透明全幅=天空+远山+整片地面+门洞内一条通向远处的小路)+ `scene_camp_wall`(围墙层,透明底=横贯全宽密排木桩墙+两根门柱,门洞留透明缺口)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_tent`(承载件)。钥石顶栏图标沿用现役 `icon_keystone`。
- **要什么**:营地场景图 9 张——`scene_camp_bg`(背景)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_tent`(承载件)。钥石顶栏图标沿用现役 `icon_keystone`。
- **来源/规格**:**✦ 插画级**(作者自跑远程生图管线);烛光黄 `#F6C667` + 木色 `#8A5A34`(art-bible §6.2);2× 源档出图、最近邻降采样到 1280×800;后处理=抠底/降采样/色板量化/描边统一。**出图登记照 [remote-gen-prompts](remote-gen-prompts.md) 头注轻量口径执行:用途+生成时间必注,其余参数有则记。**
- **排期**:营地交互重做**美术自立批**(不占迭代位;UI 贴图重构批已搁置)。终稿落 `game/assets/art/scene/`,消费常量 `UiKit.TEX_CAMP_*`。
- **现状**:全占位。
@@ -155,18 +155,13 @@
| 资产 | 需求 | 风格/规格要点 | 来源 |
|---|---|---|---|
| 受击反馈 / 死亡表现 / 顺位标记 | 敌人系统登记的资产需求**下限三类** | 死亡=血条归零后**灰化+碎裂退场,禁止瞬间消失**;顺位标记需在敌多时可读 | ⚙/✦ 挂自产精灵——死亡灰化碎裂是语义硬规则,不可整包复用;烟尘碎屑粒子可 ♻ 改色 |
| B 区舞台背景层 `scene_stage_bg_dungeon.png` | 战斗舞台背景·dungeon 变体(art-bible §6.1 资产契约) | **✦ 插画级**;源 = 基准屏宽 × `stage_h`(读布局表 `battle_layout.tres`)、B 区全高、显示 1×;顶部空区+纵深洞厅远景(远/中/近三层;暖石棕、宽岩台/层理、地精巢穴支洞/木栈道/木桩围栏),不画近景地面;按 `board_type` 选图,不走 `wall_ambient` tint | ✦ 插画级(作者自跑远程生图管线) |
| B 区舞台背景层 `scene_stage_bg_underdark.png` | 战斗舞台背景·underdark 变体(art-bible §6.1 资产契约) | **✦ 插画级**;源 = 基准屏宽 × `stage_h`、B 区全高、显示 1×;顶部空区+纵深洞厅远景(远/中/近三层;冷蓝灰、巨柱/石笋/蛛网/螺旋塔剪影/巨菇/石桥),不画近景地面;按 `board_type` 选图,不走 `wall_ambient` tint | ✦ 插画级(作者自跑远程生图管线) |
| B 区舞台背景层 `scene_stage_bg_deep_underdark.png` | 战斗舞台背景·deep_underdark 变体(art-bible §6.1 资产契约) | **✦ 插画级**;源 = 基准屏宽 × `stage_h`、B 区全高、显示 1×;顶部空区+纵深洞厅远景(远/中/近三层;灰黑、石笋城郭/棱角石厅/石桥/矿道口/烟囱塔、冷银/铜灰矿脉),不画近景地面;按 `board_type` 选图,不走 `wall_ambient` tint | ✦ 插画级(作者自跑远程生图管线) |
| B 区连续近景地面层 `scene_stage_ground.png` | 近景泥土地面层(art-bible §6.1 图层归属) | **✦ 插画级**;源 = 基准屏宽 × `ground_h`(布局表新增字段);干净压实泥土为主、全幅只零星几颗小石、散点分布,顶缘不规则泥土边/阴影缝作分隔带;三关共用、跨三关恒定、不吃 `wall_ambient`;独立层压在 B 区底部,小石台在其上 | ✦ 插画级(作者自跑远程生图管线) |
| 站桩石板地台件 `scene_stage_plaza.png` | 近景地台独立件(art-bible §6.1/§8.2) | 源 48×12、九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形;跨三关恒定,不吃 `wall_ambient` tint;连续地面层在其下方,先保留 | ⚙/✦ 自产 |
| B 区舞台背景插画 `scene_stage_bg.png` | 战斗舞台背景(art-bible §6.1 资产契约) | **✦ 插画级**;源 1280×270、B 区全高含明细带底、显示 1×;**只出远景 + 中景两带**(近景带在结算期被 84px 明细带底整条盖住);三关换挡走 §4.3 `wall_ambient` tint 槽,不按板出三张 | ✦ 插画级(作者自跑远程生图管线) |
| 站桩石板地台件 `scene_stage_plaza.png` | 近景地台独立件(art-bible §6.1/§8.2) | 源 48×12、九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形;跨三关恒定,不吃 `wall_ambient` tint | ⚙/✦ 自产 |
| 特殊技动画终稿 | 并入 §2.3 三档意图动画,不另起体系 | 攻/防两型旧口径由三档替代 | ✦/⚙ 自产(同 §2.3) |
| 连锁坍塌显形演出 | 连锁展开触发时的连锁演出 | 与音频方向同批配对登记;「坍塌显形」=连锁展开的玩家可见名 | ♻ 粒子/碎块改色 + ⚙ 坍塌结构自产 |
| 封印特效 | 该动作的呈现名 = **封印**(art-bible §3.2):**施印瞬间特效 + 印记常显形态**两件 | 印记旗面走封印黑 `#000000`(现行封印色,色史归 art-bible §4.1);特效走运动+色相(§2:状态/情绪不走明度,空间光照可走);现行占位 = 封印黑「旗」字形;印记常显形态可与 §3.3 旗面像素化合并出 | ✦/⚙ 自产(精灵+粒子,烟尘光点可 ♻ 改色) |
| 弹道/退场等动画密度 | 战斗表现输入清单,方案未定 | 样张锁动画方案后启动 | ♻ 通用弹道/光点/消散粒子改色为主 |
**现状**:三关舞台背景层与连续地面层已交付(2026-10-31 入包 `game/assets/art/scene/`:dungeon v10c / underdark v10c / deep_underdark v10c,均 1280×252;共用地面 v12b,1280×100,按作者裁保留原亮度 L≈0.132);小石台件已在役。
---
## 5. 明确不在本清单
+12 -17
View File
@@ -1,6 +1,6 @@
# 美术圣经(Art Bible)— Dungeon Sweeper
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**;现行为 v3.30,历次修订明细见 git 提交史
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**;现行为 v3.23,历次修订明细见 git 提交史
>
> **Art Director Sign-Off(AD-ART-BIBLE)**:历次批均未签核——此为 Lean/Solo 模式(非 PHASE-GATE)下的现行治理状态;full 门禁签核时机待用户表态。
> 风格基调:地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身。
@@ -212,7 +212,7 @@
- **重甲冷灰金属材质色 `#868C9A`(ARMOR_PLATE_COLD,token 名暂拟、随实装批登记)**|语义/UI token 板(**分层①**,占 1 枚额度,**余量 2→1**;用户裁):重甲档甲材质色;character-design §5.3 通道 = 甲面覆盖率 × 甲材质色,甲材质色按甲档统一,故石君王岩石板甲**同引本枚、不另计额度**;材质色跨屏恒定,落分层①不落分层②(环境色才随场景走)。过门情况:第二道门按宽松口径(禁止三通道全 <8)与在册色逐一比对,**无三通道全 <8 的对 → 过门**;§4.2 语义检查过门(色相 222°,不占红/金/蓝/紫赤/绿语义段、不在 300°–345° 禁入带;材质色不进信息层与语义物件岗)。对比实测:对深素底 `#282220` 4.65:1、对舞台底 `#3E362C` 3.52:1(图形 ≥3:1 门槛);相对亮度 L≈0.261 落 §2 世界中层 0.22–0.45 带。验收:机检〈本值入板后全板色对中三通道差全部 <8/255 的对数 = 0〉
- **卓尔浅紫灰皮 `#A79CB8`(DRIDER_SKIN,token 名暂拟、随实装批登记)**|语义/UI token 板(**分层①**,占 1 枚额度,**余量 1→0,额度用尽**;用户裁):蛛化卓尔精灵上体皮色(体块主色,L≈0.355 落 §2 世界中层带)。过门情况:第二道门同上行口径,**无三通道全 <8 的对 → 过门**;§4.2 语义检查过门(色相 264°,避开 300°–345° 禁入带,紫赤独占仍归 `#8F2F6A`;不占本节五语义段)。对比实测:对深素底 `#282220` **6.04:1**、对舞台底 `#3E362C` **4.57:1**——**两处均过图形 ≥3:1 门槛**(本轮新算)。验收:机检〈本值入板后全板色对中三通道差全部 <8/255 的对数 = 0〉
- **换色合并登记五处(用户裁,部件色落值)**|按本表「不过门即触发换色合并、禁加色绕行」执行,五处**零新增额度**(各件原拟新增候选一律不取):① 主角铲刃浅铁灰(原拟 `#A8AEB8`)→ 并入 `FOG_MEMORY_INK #C4C4C4`,**部件取值 = `#C4C4C4`**;② 主角提灯铜色(原拟 `#C08A3A`)→ 并入 `GOLD_DEEP #B07A14`,**部件取值 = `#B07A14`**;③ 食人魔酋长暖褐皮(原拟 `#B08050` / `#A97C4E`)→ 并入 `暖石 #B8A582`,**部件取值 = `#B8A582`**;④ 石君王灰皮(原拟 `#9A968C` / `#949089`,按**次级皮**口径——板甲视同体块主色、灰皮属次级皮故不受 L 0.22–0.45 约束)→ 并入 `STONE #786E62`,**部件取值 = `#786E62`**;⑤ 轻甲护甲图标木色 `#8A5A34`(深素底下 2.72:1 不过门;原拟新增木色亮档 `#C49A5E`)→ 并入 `WARM_FAINT #CDB88C`,**部件取值 = `#CDB88C`**(8.08:1)。**一色两岗代价已随裁定接受**:① 的 `#C4C4C4` 语义岗 = 迷雾记忆墨(§6.5)、② 的 `#B07A14` 属金族(§4.2 金只说收益与兑现)——两者均作角色随身件材质色用,不进信息层与语义物件岗;③④⑤ 无语义岗冲突。验收:机检〈五处部件取值点全部改指合并目标色,四枚原拟新增候选值零入板〉
- **换色合并登记三处**|按本表「不过门即触发换色合并、禁加色绕行」执行,三处零新增额度:①`#C4AC84`(地台顶缘受光线,代码 `STAND_STONE_RIM`)→ 并入 `#C9AF83`(掩尘,并与 §8.2 高墙口沿高光 `Color8(196,172,132)` 统一为同一枚);②`#5A4C3A`(地台描边,代码 `STAND_STONE_EDGE`)→ 并入 `#4C4038`;③`#3E342A`(舞台底,代码 `STAGE_BG`)→ 并入 `#3E362C`(状态槽底)。①②的绘制点全部改指合并目标色、源常量删除——程序化回退绘制与已落合并值的贴图保持一致;③随 §6.1 舞台背景插画批一并处置:背景资产接管该绘制面时本常量随绘制点退役或改指,禁单独为改值出批。验收:机检〈①②两枚源常量全仓零引用,绘制点全部改指合并目标色〉
- **换色合并登记三处**|按本表「不过门即触发换色合并、禁加色绕行」执行,三处零新增额度:`#3E342A`(舞台底,代码 `STAGE_BG`)→ 并入 `#3E362C`(状态槽底);`#C4AC84`(地台顶缘受光线,代码 `STAND_STONE_RIM`)→ 并入 `#C9AF83`(掩尘,并与 §8.2 高墙口沿高光 `Color8(196,172,132)` 统一为同一枚);`#5A4C3A`(地台描边,代码 `STAND_STONE_EDGE`)→ 并入 `#4C4038`。验收:机检〈全仓三枚源常量零引用,绘制点全部改指合并目标色〉
**对比硬指标(可测)**:数字可读性由**格底明度分档**承载——棋盘数字亮墨 **L≥0.75** 对**已开格底 L 0.08–0.14** ⇒ 直画对比 **≥4.5:1 恒成立**(WCAG AA,样张逐组合抽测冻结);**色牌后备**:色牌素色恒定 **L≥0.70**、牌上数字深墨 **L≤0.027** ⇒ 任意牌墨组合 ≥4.5:1(实测样例:#33261B/#F5EAD3 = 12.3:1)。未开格底槽(含全部图类型)相对亮度 **L≥0.30**(墙顶面受光档)、HSV 饱和 **≤0.55**、避 Boss 禁入色相带;已开格底走世界底层档(见上)。物件识别不依赖格底(§5.1 轮廓通道),旗面、金色不作识别依赖。验收:机检〈亮墨对已开格底 ≥4.5:1;未开格底 L≥0.30、饱和 ≤0.55;任意牌墨组合 ≥4.5:1〉 ←原二;地形变异 AC-9
@@ -376,21 +376,17 @@
### 6.1 战斗舞台背景(B 区环境)
- **双舞台常驻下的 B 区环境**:战斗画面区(站桩立绘所在)的背景 = 明亮地窟,与 §1 风格基调同源——暖倾向、无压抑;背景整体**低对比自然后退**(§3.4 焦点预算延伸):背景禁止构成完整凸形、禁止抢 B 区立绘与 D 区数字的焦点,识别层级恒为 立绘 > 背景。验收:目检〈背景无完整凸形、立绘最抢眼〉
- **舞台正面目标**:B 区 = **地窟看台**——立绘脚下地台/落影承载(世界中层),背景环境层低饱和后退、明度走世界底层档;角色禁止直接贴平色底。规格与终值归整屏样张闸(§1.4)冻结。**地台面上零文字**——暖白 `#F0E2C8` 对地台板身 `#968264` 实测仅 2.89:1,已越 §7.2 的 4.5:1 硬指标,落法按 §7.2「文字禁直接压纹理底」(垫容器、素牌或罩层),非新条款;站桩文字位改垫素底或落进结算明细带(实装=站桩名字行垫 `HP_BAR_BACK` 深档素底牌、文字基线上移至地台顶上方——2026-10-30 落地)。验收:目检〈立绘脚下有地台/落影,非贴平色底〉+机检〈B 区站桩文字位正下方像素非地台板身色 #968264〉
- **舞台正面目标**:B 区 = **地窟看台**——立绘脚下地台/落影承载(世界中层),背景环境层低饱和后退、明度走世界底层档,允许光源氛围(洞顶光柱/暖光晕候选);角色禁止直接贴平色底。规格与终值归整屏样张闸(§1.4)冻结。**地台面上零文字**——暖白 `#F0E2C8` 对地台板身 `#968264` 实测仅 2.89:1,已越 §7.2 的 4.5:1 硬指标,落法按 §7.2「文字禁直接压纹理底」(垫容器、素牌或罩层),非新条款;站桩文字位改垫素底或落进结算明细带(实装=站桩名字行垫 `HP_BAR_BACK` 深档素底牌、文字基线上移至地台顶上方——2026-10-30 落地)。验收:目检〈立绘脚下有地台/落影,非贴平色底〉+机检〈B 区站桩文字位正下方像素非地台板身色 #968264〉
- **明度分层适用**:背景氛围的情绪表达仍禁止以压暗/渐晕实现(§2 禁令);空间纵深与洞穴感走明度分层允许域——世界底层档、光照氛围、色相饱和与形体承载(§3.2 同款逻辑)。验收:方向性
- **图类型联动**:背景层按 `board_type` 选三张变体(`scene_stage_bg_dungeon.png` / `scene_stage_bg_underdark.png` / `scene_stage_bg_deep_underdark.png`,方向取 §6.3 三关色板候选);**背景不再走 §4.3 `wall_ambient` tint**(该槽保留给棋盘/世界的墙雾氛围,避免对三张背景二次染色)。紫赤仅 Boss 战允许短暂进入背景氛围纹样(§2.6 色相独占的舞台侧延伸,高饱和独占不变),非 Boss 战高饱和紫赤禁入。验收:机检〈背景吸色无非 Boss 高饱和紫赤;背景素材不引用 `wall_ambient` 槽〉
- **场景构成(2026-10-31 用户裁)**:B 区舞台从远到近分三段——**顶部空区**:洞顶以上的昏暗空间,只留深色空区,**不画钟乳石、吊锥、悬挂物或任何可辨形状**;**远景(纵深洞厅)**:不是贴墙的后壁,按远/中/近三层组织——远层暗剪影(远处洞口/通道、层叠岩体、洞底轮廓)、中层主要细节层(岩台、岩柱、凹壁、裂缝)、近层左右暗色岩框;剪影被中层部分遮挡、不贴顶、不孤立,**禁贴墙感、禁单件圆柱贴顶、禁砖石墙/规则砌缝/方整石块**;三关母题(地精=支洞/木栈道/木桩围栏;卓尔=巨柱/石笋/蛛网/螺旋塔剪影/巨菇/石桥;灰矮人=石笋城郭/棱角石厅/石桥/矿道口/烟囱塔)见 `battle-stage-prompts.md` §3.2;**分隔带**:远壁根部与近景地面之间一条不规则碎石带/阴影缝,边界清楚但不笔直,**禁瓷砖缝、踢脚线、规则台阶**;**近景地面**:干净压实泥土为主,全幅只零星几颗小石、散点分布,中央平缓留空给角色站位,**禁成排/成堆碎石、满铺砂砾、泥点噪声、瓷砖、规则铺装、草、木地板**。纵深与三段构图归 §1.4 样张;逐条要素、分层提示词与否定后缀 = `battle-stage-prompts.md`(本文件不重复维护)。验收:目检〈顶部无钟乳石/吊锥;远景有纵深、非贴墙;无孤立圆柱贴顶;近景为泥土碎石;分隔带不规则;中央无大障碍〉
- **图层归属(2026-10-31 用户裁)**:背景层 = 三关变体 `scene_stage_bg_dungeon/_underdark/_deep_underdark.png`(顶部空区+纵深洞厅远景,不画近景地面;按 `board_type` 选一张);近景地面 = **独立连续地面层** `scene_stage_ground.png`(泥土/砂砾/碎石地面,全宽、底边贴 B 区底、顶缘即分隔带;高度读布局表 `ground_h`,字段由地面层实装批新增并回填,本批终稿高 100、代码批落表);现有每角色小石台 `scene_stage_plaza.png` 保留,叠在连续地面之上。连续地面层与小石台**三关共用**、跨关恒定、**不吃 `wall_ambient` tint**(世界中层承载面随关卡换明度会破 §2 层间关系);背景三张变体各自带关卡色族,不再叠 tint。验收:机检〈三张背景文件名带 board_type 后缀;地面层/小石台文件名无 board_type 后缀;背景与地面层均不引用 `wall_ambient` 槽〉
- **资产渠道与契约**:✦ 插画级(演出类场景贴图,清单 §4 联动登记);战斗一屏样张(§1.4 清单 1.2)含背景出样。**背景层 = 三张按关卡变体** `scene_stage_bg_dungeon.png` / `scene_stage_bg_underdark.png` / `scene_stage_bg_deep_underdark.png`(源 = 基准屏宽 × `stage_h`,读布局表 `game/data/tables/battle_layout.tres`;显示 1×;按 `board_type` 选图;**不走 `wall_ambient` tint**);三张共用本节构图规则,仅岩体形态与色族按 §6.3 三关方向不同。**背景只画顶部空区+纵深洞厅远景,不画近景地面**:结算期明细带整条压住 B 区底带(带高 = `log_band_h_settle`,读布局表),近景地面在独立层;画进背景等于画一件永远看不见、且会随背景变体重复的东西。**连续地面层** = `scene_stage_ground.png`(源 = 基准屏宽 × `ground_h`,读布局表;显示 1×;三关共用、跨关恒定、不吃 tint);地面层是极宽薄条,出图与后处理按 `battle-stage-prompts.md` §2.2 的工艺(可平铺纹理条/最宽档裁切,禁横向拉伸)。**光柱不迁入背景**:三张背景均不画光柱/光带/光束/光晕与任何光源实体,明暗只由岩体层理与暗部承载;现行四柱程序层是临时占位,资产到位后直接退役(代码批)。资产已入包(2026-10-31),四柱程序层与平色底由代码批退役/改指;出图工单 = `battle-stage-prompts.md`,交付记录见本节「B 区舞台背景与共用地面的交付绑定」。验收:目检〈三关背景可辨为不同洞窟,非同一图换色;远景有纵深、非贴墙;无孤立圆柱贴顶;背景无地台/地面结构件、无砖石墙、无瓷砖地面、无光柱/光带;地面层为泥土/砂砾/碎石〉
- **B 区舞台背景与共用地面的交付绑定(2026-10-31 入包)**:三张背景与一张地面已按本节契约入包 `game/assets/art/scene/`——`scene_stage_bg_dungeon.png` / `scene_stage_bg_underdark.png` / `scene_stage_bg_deep_underdark.png` 均 1280×252、不透明全幅、单张 ≤32 色、按 `board_type` 选图;`scene_stage_ground.png` 1280×100、顶缘以上透明、单张 ≤32 色、三关共用。地面按作者 2026-10-31 裁保留候选 v12b 原亮度(实测不透明域 L≈0.132,低于 §2 世界中层 0.22–0.45,登记为作者例外;后续调亮归调参/替换,不重开设计);背景实测 L:dungeon 0.0148、underdark 0.0132、deep_underdark 0.0111(后两者低于世界底层下沿带下限 0.014 不超过 0.003,登记读数)。验收:机检〈四件文件名与尺寸逐位相符;地面顶缘以上 alpha=0、不透明域 ≤32 色;背景不透明全幅 ≤32 色〉+目检〈三关可辨、无光柱、地面读作泥土〉。参考指针:`asset-path-map.md` `scene/` 行、`battle-stage-prompts.md` §2/§4。
- **近景地面与地台走独立件**:连续地面层 `scene_stage_ground.png` 与角色脚下小石台 `scene_stage_plaza.png` 均为独立层,三关共用、跨关恒定。小石台源 48×12、九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形(规格见 §8.2);连续地面层的顶缘即分隔带(不规则碎石/阴影缝,不是直线亮边)。独立的两条理由——① 三张背景按关卡变体,地面若画进任一背景就得三张各画一遍、且三关站位面会漂移;② 结算期明细带整条压住底带(带高读 `log_band_h_settle`),地面在独立层才能跨关恒定并保持世界中层承载面不变。小石台先保留,是否退役归实装批/后续裁决。验收:机检〈地台件按九宫格拉伸后顶缘受光线宽度恒为 2px〉+目检〈连续地面为泥土碎石、顶缘不规则〉
- **图类型联动**:背景 tint 走 §4.3 `wall_ambient` 槽,随三关换挡(方向候选见 §6.3);紫赤仅 Boss 战允许短暂进入背景氛围纹样(§2.6 色相独占的舞台侧延伸,高饱和独占不变),非 Boss 战高饱和紫赤禁入。验收:机检〈背景吸色无非 Boss 高饱和紫赤〉
- **资产渠道**:✦ 插画级(演出类场景贴图,清单 §4 联动登记);战斗一屏样张(§1.4 清单 1.2)含背景出样。**资产契约(尺寸候选,样张冻结终稿)**:基准一张 = `scene_stage_bg.png`(源 1280×270,B 区全高含明细带底;三关换挡走 §4.3 `wall_ambient` tint 槽,不按板出三张——换皮成本复用棋盘同款逻辑),显示 1×;呈现约束按本节各条(低对比后退/明度世界底层档/光柱氛围保留)。程序化平色+光柱现行使用 = 占位;插画工单归 PRD,交付后 art-status-checklist 同步 ✅。**出图只出远景 + 中景两带**:结算期明细带底 84px 会盖住 B 区 78%–100% 段(约 59px),近景带在结算期整条不可见——画进背景等于画一件永远看不见的东西。远景/中景的构图占比归整屏样张(§1.4)。验收:目检〈背景插画内零地台结构件〉
- **近景地台走独立件**:`scene_stage_plaza.png`,源 48×12、九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形(规格见 §8.2)。独立的两条理由——① 上条的 84px > 59px 覆盖账;② §4.3 `wall_ambient` tint 作用于整张背景,地台若画进背景会随三关换明度,而 §6.1 舞台正面目标把地台定死为世界中层承载面,随关卡变会破 §2 明度分层的层间关系。独立件跨关恒定。验收:机检〈地台件按九宫格拉伸后顶缘受光线宽度恒为 2px〉
### 6.2 营地场景
- **情绪与色域**:承载 §2.8——全作最暖:烛光黄 `#F6C667` × 木色 `#8A5A34`(§7.2),中等饱和,无棋盘式高对比。
- **场景构成**:一张可点击的营地场景图(1280×800,与工程档同幅面)。远景=天空与远山;中景=**独立成层的木围墙**(密排竖木桩、桩顶削尖、不透缝无横木)与**半开的营地大门**;近景=中央篝火、左侧锻炉铁砧、右侧帐篷与宝箱、帐篷旁的笔记本,另有木桩与挂在围墙上的布告板。**中景带自底部往上约 2/3 处起**;**近景物件可覆盖围墙,唯一例外=中间门洞必须留空、不被任何物件遮挡**。七个可点物件的功能绑定归 `camp.md`,本节只定画法。
- **图层拆分(10 张)**:场景按「远景/中景/近景要素各自成层」拆——`scene_camp_bg` 背景层(z0,不透明全幅)=天空+远山+整片地面+门洞内一条通向远处的小路;`scene_camp_wall` 围墙层(z1,透明底)=横贯全宽密排木桩墙(左右两段)+两根略高的门柱,**门洞处留透明缺口**透出背景层的小路;`scene_camp_board` 布告板(z2)、`scene_camp_gate` 门扇(z3)、`scene_camp_dummy` 木桩/`scene_camp_forge` 锻炉/`scene_camp_tent` 帐篷(z4)、`scene_camp_chest` 宝箱/`scene_camp_notebook` 笔记本(z5)、`scene_camp_campfire` 篝火(z6,最前)=八个物件层,各透明底单件一张。**帐篷**为承载件、不可点。围墙独立成层的好处:桩高可单独锁死、门洞做透明缺口、背景层不混入暖木色。提示词与坐标 = `camp-scene-prompts.md`,消费常量 = `asset-path-map`。
- **交付形态**:**背景层 1 张(不透明全幅)+ 围墙层 1 张 + 八个物件层 = 10 张**,其中 9 张透明底、1 张不透明全幅 + 一份坐标表(贴图矩形/命中矩形/z 序)。**篝火例外**:命中矩形只取下半(火堆),上半(火焰)只作特效展示。围墙层落盘按细条 `(0, 240, 1280, 70)` 裁,不做全幅(全幅约 3.9 MB 显存,细条约 0.35 MB)。
- **场景构成**:一张可点击的营地场景图(1280×800,与工程档同幅面)。远景=天空与远山;中景=木围墙(密排竖木桩、桩顶削尖、不透缝无横木)与**半开的营地大门**(门洞内一条通向远处的小路);近景=中央篝火、左侧锻炉铁砧、右侧帐篷与宝箱、帐篷旁的笔记本,另有木桩与挂在围墙上的布告板。**中景带自底部往上约 2/3 处起**;**近景物件可覆盖围墙(围墙是纯背景板),唯一例外=中间门洞必须留空、不被任何物件遮挡**。七个可点物件的功能绑定归 `camp.md`,本节只定画法。
- **交付形态**:**背景 1 层 + 七个物件各 1 层**(**帐篷**为承载件、不可点),共 8 张透明 PNG + 一份坐标表(贴图矩形/命中矩形/z 序)。**篝火例外**:命中矩形只取下半(火堆),上半(火焰)只作特效展示。
- **色彩与氛围依据**:营地场景的冷暖分工(冷的大面积后退、暖的小面积前进)、空气透视三档与大面积用色规则 = `reports/提案-营地色彩与氛围脚本-2026-10-23.md`。本节不复述色值;新增环境色按 §4.1 场景环境子板条款登记(每屏一套、≤32 色、过 §4.2 语义检查与 RGB 三通道差 ≥8/255 两道门)。
- **交互四态(零新素材,全部程序化)**:悬停=**轮廓亮边 + 浮动**(浮动须整像素吸附);按下=位移 1px;**禁用/未解锁=降明度 + 去饱和**(§2 已缩窄至情绪域,状态表达不受限),**仅锻炉(锻造工坊)与宝箱(秘宝匣)两件会有此态**;已解锁=常态。
- **钥石余额**:常驻画面顶部的一行小 HUD 栏(高 48px,**浮层覆盖,不挤压画面**)。
@@ -400,7 +396,6 @@
### 6.3 三关图类型色板候选(board_type 换挡方向)
- **模板与不变槽**:换皮只换 §4.3 四槽(`tile_closed`/`tile_open`/`wall_ambient`/`variant_tint`),信息与语义层全图类型恒定——本节只给三关**方向性候选**,终值按 §4.3 现行规则重跑 §1.4 格子样张冻结。**终值的取值程序**:先跑「三关色相 × 五组类型微光」互辨抽测(抽测任务登记在待办清单),再按 §4.3 现行规则重跑 §1.4 格子样张冻结——本节不填终值色值,待该抽测与格子样张重跑后落本节修订。验收:机检〈本节三关行无 `tile_closed`/`wall_ambient` 终值色值字面量(终值未冻结前禁填)〉
- **三关背景变体**:§6.1 的三张背景取本节三关方向作为岩体形态、剪影语言与色族差异(dungeon 暖石棕+地精巢穴支洞/木栈道/木桩围栏;underdark 冷蓝灰+卓尔巨柱/石笋/蛛网/螺旋塔剪影/巨菇/石桥;deep_underdark 灰黑+灰矮人石笋城郭/棱角石厅/矿道口/烟囱塔);背景不复用 `wall_ambient` tint(该槽作用于棋盘/世界),背景色值归战斗屏场景环境子板。验收:目检〈三关背景可辨为不同洞窟,非同一图换色〉
- **深度轴叙事**(§2.7 下探过渡):色相层进预告「更深更险」,明度档不动——
- **dungeon 地精洞窟**(第一关):现行掩尘系暖石棕(`#C9AF83` 基)即基准档,`wall_ambient` 暖棕;
- **underdark 幽暗地域**(第二关;方向:**冷调幽暗**):主调**灰白蓝**候选(低饱和蓝灰域,`tile_closed`),少量紫黑倾向点缀——紫系取紫罗兰域(≈256°)候选,禁入带 300°–345° 不变;`wall_ambient` 冷蓝灰;
@@ -452,7 +447,7 @@
### 7.3 布局栅格
- **结构**:双舞台三区(B 战斗画面/C 数据框/D 棋盘;字母保持不重编)为结构冻结项,分区几何的唯一事实源 = `game/data/tables/battle_layout.tres`,分区尺寸与像素级裁决归 battle.md(HUD框架只载 1280×800 基座口径,不复写数值)。验收:机检〈三区结构常驻〉
- **结构**:双舞台三区(B 战斗画面/C 数据框/D 棋盘;字母保持不重编)为结构冻结项,分区尺寸与像素级裁决归 battle.md(HUD框架承载 1280×800 基座锚点值,如扫雷图区 1036×504、结算明细带展开 84px)。验收:机检〈三区结构常驻〉
- **栅格规则(候选)**:面板间距/内边距按 4px 步进档(8/12/16/24 候选),全 UI 恒定;数据区直角细线不占圆角预算。终值归样张。验收:目检〈间距落在步进档〉
### 7.4 字体与字号
@@ -460,7 +455,7 @@
- **字体**:全局一套像素字,单套即终稿——缝合像素字体 Fusion Pixel(OFL,已接入 `game/assets/fonts/ui_font_fusion_pixel_zh_hans.ttf`),拉丁字符与数字沿用其自带拉丁字形,不另设拉丁专件(现状即终稿;旧拉丁候选 Kenney Fonts 已弃;生产工单=清单 §3.6);墨色走主墨系(§7.2);换字体 = 全局决策。验收:机检〈主题字体指向 Fusion Pixel 件〉
- **字号**:像素字体**整数倍缩放,禁非整数缩放**(像素模糊);下限(1280×800 基座):棋盘数字 ≥18px(公式 = `max(18, ceil(格边长 × 0.5))`不变)、**正文/明细 ≥16px、tooltip ≥14px**(1280×800 低于 1080p 基准,同字号实际更小)。验收:机检〈静态文字位点字号 ≥ 档位表值〉
- **字号档位表(六档)**:全站 UI 文字字号只经 `UiKit.FS_*` 单点取值,裸数字字号弃用——`FS_CAPTION 12`/`FS_HINT 14`/`FS_BODY 16`/`FS_SUB 18`/`FS_TITLE 24`/`FS_H1 34`。旧实测档按就近归档收编(10/11/12→CAPTION、13/14→HINT、15/16→BODY、17/18/20→SUB、21/22/24/26→TITLE、32/40→H1;静态位点全归档,数据驱动动态字号位点除外);棋盘数字公式 `maxi(18, ceil(格边长×0.5))` 与站桩血数 11px(条带内读数豁免)不入表。注:六档相邻比 12→14→16→18 三段 <1.25×,系裁决值优先于字重纪律的档差参考线,该参考线对新层级设计仍有效。验收:机检〈全站无裸数字字号,位点走 FS_*〉
- **小字号豁免条款(合并单条不开两口子)**:下款两类**豁免于本节下限**——①**条带内读数**:压在窄条带(血条/进度条等 ≤12px 高带)内居中的数字,受条带高度物理约束,维持 11px 下限,且必须按 §7.2 垫素色牌后计对比(实装=B 区站桩血数 11px 压 HP_BAR_BACK 牌 14.3:1);②**装饰性 overline/caption**:不承载须精确读取的信息、仅作分区标题或氛围小字者,可至 **12px**。**豁免逐处清单(撤除史归 git 提交史,余清单如下)**:条带内读数=B 区站桩血数一处;装饰 caption 档(FS_CAPTION)在册面=棋盘底操作提示条/营地柜台说明文与结构位预留注记/图例 RAP 词条说明/设置警示文两处/关卡地图底栏组标(生命/属性/特性)与分隔系/站桩消散提示/B 区状态槽微计数(×N●○)/bcard 脚注/RAP 反馈行/刷新余量徽标——清单外 11-13px 文字一律按正文/tooltip 下限提档,新增 caption 位须同批增补本清单。**站桩顺位徽标 ①②③ 不增列本清单**:该处原为 13px 字形,既不在①条带内读数在册面、也不在②装饰 caption 在册面,属「清单外 11–13px 文字」,按本条纪律提档至 `FS_HINT` 14 即归六档;为它单独入册等于开第二口子,与「合并单条不开两口子」的立条理由正面相撞。**站桩名字行同理提档**:名字行(敌人名与护甲文字)承载须精确读取的信息,原取 12px 不在①②两在册面、属「清单外 11–13px 文字」,按本条纪律提档至 `FS_HINT` 14 归六档、不增列②在册面,名字行几何(素底牌矩形、文字基线、宽度测量)随档位联动。②装饰 caption 在册面逐处维持不变。验收:机检〈豁免文字位均可对上本清单,清单外无 <14px;站桩顺位徽标与站桩名字行绘制点取 UiKit.FS_HINT,且②在册面不含该两位〉
- **小字号豁免条款(合并单条不开两口子)**:下款两类**豁免于本节下限**——①**条带内读数**:压在窄条带(血条/进度条等 ≤12px 高带)内居中的数字,受条带高度物理约束,维持 11px 下限,且必须按 §7.2 垫素色牌后计对比(实装=B 区站桩血数 11px 压 HP_BAR_BACK 牌 14.3:1);②**装饰性 overline/caption**:不承载须精确读取的信息、仅作分区标题或氛围小字者,可至 **12px**。**豁免逐处清单(撤除史归 git 提交史,余清单如下)**:条带内读数=B 区站桩血数一处;装饰 caption 档(FS_CAPTION)在册面=棋盘底操作提示条/营地柜台说明文与结构位预留注记/图例 RAP 词条说明/设置警示文两处/关卡地图底栏组标(生命/属性/特性)与分隔系/站桩消散提示/B 区状态槽微计数(×N●○)/bcard 脚注/RAP 反馈行/刷新余量徽标——清单外 11-13px 文字一律按正文/tooltip 下限提档,新增 caption 位须同批增补本清单。**站桩顺位徽标 ①②③ 不增列本清单**:该处原为 13px 字形,既不在①条带内读数在册面、也不在②装饰 caption 在册面,属「清单外 11–13px 文字」,按本条纪律提档至 `FS_HINT` 14 即归六档;为它单独入册等于开第二口子,与「合并单条不开两口子」的立条理由正面相撞。②装饰 caption 在册面逐处维持不变。验收:机检〈豁免文字位均可对上本清单,清单外无 <14px;站桩顺位徽标绘制点取 UiKit.FS_HINT,且②在册面不含该位〉
- **字重纪律(字重两档不启用)**:层级手段为两件——**字号档差(相邻层级 ≥1.25×)+ 墨色深浅**;全局单字重,不上第二个字重(**重开钩子已注销**——整屏样张设计轮已重开并复核:屏内各级文字全部能归进本节六档字号,顺位徽标与站桩名字行两处按本节纪律提档即可,屏内实测对比全部达标(暖白对舞台底 9.49:1、GOLD_BRIGHT 6.47:1、血数 14.3:1),**未实证标题层级不足**,维持单字重,不另立条目);数字禁描边/阴影伪加粗不变(字形为唯一数值载体,§4.1 同源纪律)。验收:机检〈主题仅注册单字重〉/目检〈屏内标题与正文层级仅靠字号档差与墨色深浅两件即可分辨,无一处需要第二字重才读得清〉
### 7.5 营地界面
@@ -521,8 +516,8 @@
- **画布基准(暂定值)**:
- **主角人设原图 = 48×72**(1× 基准,暂定;与站姿降采样可读性验证一致);
- **棋盘小人 = 32×32 二头身派生版(暂定,允许更小)**——独立资产,自人设定稿压缩派生(character-design §5.4),不吞数字位、占位 ≤ 0.75× 格边长;板越小占比摩擦越大(13×13 / 14×14 更小,算例见 [battle.md](../gdd/ux/battle.md)「棋盘格尺寸公式」),32px 源档的去留已定终值 = **保留 32×32 源档**(沿「独立小源档」路线;N=12 板下显示占位上限 27px = 0.84 倍,正落本节乙类非整数缩放许可范围,**连豁免都不用再办**),待决标注注销;
- **站桩立绘源档 = 八敌逐键定档**:主角 48 不变;八敌源档高 = goblin 36 / giant_spider 38 / duergar 46 / hook_horror 58 / bugbear 62 / ogre_chief 74 / drider 90 / duergar_king 94,显示一律 **×2 整数档(NEAREST)** = 72–188。定档依据 = 生物体型三约束(钩爪恐魔>灰矮人、灰矮人<熊地精、食人魔>熊地精——体量=物种体型,非强度线性映射,见 character-design §5.2);可读性实测门槛 = 源高 ≥32(24 糊/32 可读/48 清楚/64 细节完整),全阶梯 36–94 达标;派生管线 = pixel-1.png 紧裁 → 等比缩至档高 → dom_block_resize 块内众数降采样(tools/pixelize_candidates.py `STANDEE_TIER_H`,`SIZES` 降格为管线上游中间分辨率表);体量存在感经整屏对比校验(B 区高读 `stage_h`/立绘显示上限 188)。验收:机检〈standee_asset_check 逐键档高校验〉
- **站桩立绘排版**:① 区高 B 区 ≈1/3 屏 / C 区 ≈2/3 屏,**扫雷盘原尺寸不缩、格子不变**(区高事实源 = 布局表 `battle_layout.tres`;压缩由 C 区面板空档吸收);② 站桩内部重排——立绘双脚落石台上缘(弃盒内居中)、**血条+状态/名字行落底排**(立绘→血条→状态 顺序,全场血条对齐一线;血条嵌石板位、台缘线留作地面线、落影保留接地感),状态空时该行退化为名字行;③ 大体档(显示 >150px 级:drider/duergar_king)**允许向上溢出盒顶 N px**(N 由实装批按实测尺寸算定,**与背景/光柱无关**——现行溢出区恰为临时光柱氛围层〔§6.1 方向稿四柱,x 基准 110/900/1055/1210@1280,`battle_stage_view.gd:47-56`〕,正式舞台背景插画(三关变体)到位后该层直接退役(背景不画光柱),**溢出判定不随之变更**;横向禁止压到顶部中央轮次·阶段指示,溢出上限由实装批用真图复算 x 区间定),血条仍落底(落底排的第二个收益=头部探顶不带出血条);意图徽标悬浮头顶上方(探顶档压胸);④ 「血条悬浮头顶+立绘盒内居中」排式已弃——盒顶固定 y 与盒内居中在高盒下拉开血条-立绘空洞。验收:目检〈血条一线对齐、大体档溢出不压轮次指示〉
- **站桩立绘源档 = 八敌逐键定档**:主角 48 不变;八敌源档高 = goblin 36 / giant_spider 38 / duergar 46 / hook_horror 58 / bugbear 62 / ogre_chief 74 / drider 90 / duergar_king 94,显示一律 **×2 整数档(NEAREST)** = 72–188。定档依据 = 生物体型三约束(钩爪恐魔>灰矮人、灰矮人<熊地精、食人魔>熊地精——体量=物种体型,非强度线性映射,见 character-design §5.2);可读性实测门槛 = 源高 ≥32(24 糊/32 可读/48 清楚/64 细节完整),全阶梯 36–94 达标;派生管线 = pixel-1.png 紧裁 → 等比缩至档高 → dom_block_resize 块内众数降采样(tools/pixelize_candidates.py `STANDEE_TIER_H`,`SIZES` 降格为管线上游中间分辨率表);体量存在感经整屏对比校验(B 区 270/立绘显示上限 188)。验收:机检〈standee_asset_check 逐键档高校验〉
- **站桩立绘排版**:① 区高 B 区 270(≈1/3)/C 区 530(≈2/3),**扫雷盘 438×438 原尺寸不缩、格子不变**(区高事实源 battle.md 区表;压缩由 C 区面板空档吸收);② 站桩内部重排——立绘双脚落石台上缘(弃盒内居中)、**血条+状态/名字行落底排**(立绘→血条→状态 顺序,全场血条对齐一线;血条嵌石板位、台缘线留作地面线、落影保留接地感),状态空时该行退化为名字行;③ 大体档(显示 >150px 级:drider/duergar_king)**允许向上溢出盒顶 N px**(N 由实装批按实测尺寸算定,**与背景/光柱无关**——现行溢出区恰为临时光柱氛围层〔§6.1 方向稿四柱,x 基准 110/900/1055/1210@1280,`battle_stage_view.gd:55-63`〕,正式舞台背景插画 `scene_stage_bg.png` 到位后该层由背景绘制吸收,**溢出判定不随之变更**;横向禁止压到顶部中央轮次·阶段指示,溢出上限由实装批用真图复算 x 区间定),血条仍落底(落底排的第二个收益=头部探顶不带出血条);意图徽标悬浮头顶上方(探顶档压胸);④ 「血条悬浮头顶+立绘盒内居中」排式已弃——盒顶固定 y 与盒内居中在高盒下拉开血条-立绘空洞。验收:目检〈血条一线对齐、大体档溢出不压轮次指示〉
- **站桩石板地台件**:`scene_stage_plaza.png`,源 48×12、九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形;跨三关恒定、不吃 §4.3 `wall_ambient` tint(独立理由见 §6.1 近景地台独立件条)。验收:机检〈地台件按九宫格拉伸后顶缘受光线宽度恒为 2px〉
### 8.3 导出格式与命名规范
-293
View File
@@ -1,293 +0,0 @@
# 美术后处理与校验工具清单
> **性质**:本文件是 `tools/` 下美术资产处理与校验脚本的用法清单。回答三件事:这类资产该跑哪件工具、命令怎么敲、判据与坑在哪。
> **边界**:只管工具用法,不立美术规格。色值、画布、体量、命名与验收条款归 [art-bible](art-bible.md)(§4.1 主色板、§8 资产与技术规范)和 [art-assets-checklist](art-assets-checklist.md);命令与源码冲突时以源码现行为准,美术规格冲突时以 art-bible 为准。素材入库与代码接线归 [asset-path-map](asset-path-map.md),不在本文件。
> **分工**:[art-prompts](art-prompts.md) 管角色级提示词与五步出图流程,[camp-scene-prompts](camp-scene-prompts.md) 管营地场景分层提示词与四步流程;两卡只把「后处理」一步指到本文件。提示词怎么写出图、出完图怎么处理,是两件事。
> **命令前提**:以下命令均在仓库根执行。素材不入 git,worktree 缺件时部分脚本回退主检出 `D:/code/g-plan2`,部分直接返回错误,见各节「已知坑与适用边界」。
## 按资产类型选工具
| 资产类型 | 先跑 | 再跑或校验 | 说明 |
|---|---|---|---|
| 角色精灵(八敌、主角) | `tools/pixelize_candidates.py` | `tools/standee_asset_check.py`、`tools/palette_check.py` | 需要收网格时加 `--fixer` |
| 场景不透明全幅(营地背景层、战斗舞台背景) | 无专用工具 | `tools/palette_check.py` 只出读数 | 见「三条已知边界」第 3 条 |
| 场景透明分层(围墙层、八个物件层) | 无专用工具 | `tools/palette_check.py` 只出读数 | 现役批量处理的键表只认角色键 |
| UI 框材(面板、按钮、石框、底噪、图标) | `tools/quantize_ui_skin.py` | `tools/palette_check.py`、`tools/path_check.py` | 主题小件另跑 `tools/gen_theme_icons.py` |
| 地形瓦片 | `tools/recolor_terrain_tiles.py` | `tools/palette_check.py` | 只换色,不重绘 |
| 整屏样张 | `tools/render_sample_sheet_v3.py`、`tools/render_level_map_sheet.py` | `tools/shot_checker.py`;棋盘回归用 `tools/golden_board_crop.py` 加 `tools/golden_compare.py` | 样张脚本生成后自带机检 |
## 三条已知边界
1. **`pixelize_candidates.py` 不能处理任意资产。** `main()` 写死 `SIZES` 角色键表,只遍历这八个键的子目录;目录名不在表里就跳过,没有单文件入口,也没有通用目录模式。
2. **`palette_check.py` 不做色相禁入带检查,也没有全图明度带检查。** 它只统计单张色数,以及对基线判新增色近色;HSV 300°–345° 禁入带与 §4.1 的明度带不在它的检查范围。
3. **现存工具链没有「不透明全幅背景件」的专用入口。** 角色批量处理按角色键,UI 量化按键名,瓦片换色按 `terrain_*_dungeon` 命名,整屏样张是合成器;没有一件接受一张不透明全幅场景图并输出同尺寸成品。`camp-scene-prompts.md` 第 3 步写明背景层不去底,但这一步没有对应的仓库命令。
## tools/pixelize_candidates.py
**一句话定位**:把远程生图的角色候选批量处理成透明底像素精灵;带 `--standee48` 时按逐键档高产出站桩源档。
**归属**:仓库内 `tools/pixelize_candidates.py`。
**输入与输出**:默认批量模式读 `<批次根>/<角色键>/v*.png`,角色键取 `SIZES` 的八个键;输出 `<角色键>/pixel-<n>.png`、四联底测试条 `pixel-<n>-strip.png`,批次根再出一张 `0-像素化预览.png`。站桩模式读 `<批次根>/<角色键>/pixel-1.png` 与 `<批次根>/hero-ref/edit/normal_1x.png`,有 `attack_1x.png`、`hit_1x.png` 时一并产出;输出 `character_enemy_<键>.png`、`character_hero_portrait.png`、`character_hero_attack.png`、`character_hero_hit.png`。
**用法命令**:
```bash
python tools/pixelize_candidates.py <候选批次目录>
python tools/pixelize_candidates.py <候选批次目录> --fixer
python tools/pixelize_candidates.py <候选批次目录> --standee48
python tools/pixelize_candidates.py <候选批次目录> --standee48 --standee-out game/assets/art/character
python tools/pixelize_candidates.py <候选批次目录> --fixer --fixer-exe D:/code/pixel-art-fixer/rust/target/release/pixelfixer.exe
```
**判据或口径**:`SIZES` 是中间图最长边,不是最终体量——goblin/giant_spider/hook_horror 96,bugbear/duergar 128,ogre_chief/drider/duergar_king 192。`STANDEE_TIER_H` 是站桩档高——goblin 36、giant_spider 38、duergar 46、hook_horror 58、bugbear 62、ogre_chief 74、drider 90、duergar_king 94;主角取 `STANDEE_SLOT_H` 48。站桩源宽上限 75,脚贴画布底缘。去底走奶油描边屏障法,屏障法保留面积低于底色门路径的 60% 时自动回落;随后吃描边。像素化顺序:内容裁剪、BOX 预滤到 4 倍、中位切分量化 64 色(`QUANT_COLORS`)、NEAREST 降到目标、alpha 二值(阈值 128)、删小于 10px 的孤立不透明域、边缘色内插 2 遍。四联底测试条的四底色在 `STRIP_BGS`。正常结束退出 0;源码没有显式退出码,未捕获异常按 Python 默认非零。
**已知坑与适用边界**:键表固定,任意资产不能直接跑,见「三条已知边界」第 1 条。候选文件名须以 `v` 开头且不含 `pixel`;命名解析只取 `v` 后一位数字,`v10.png` 会与 `v1.png` 落到同一个 `pixel-1.png`。`--standee48` 分支读完 `pixel-1.png` 后直接返回,`--fixer` 在站桩分支不生效;要先跑一次带 `--fixer` 的批量模式。`--fixer` 调用失败只打印一行并回退原图,批次继续,不会以非零退出码中断。去底依赖奶油描边;描边被压暗开缺口会漏灌,脚本按 60% 面积阈值回落,但仍假定底色均匀浅色。输出是角色精灵,不处理不透明全幅件。
## 外部工具:pixel-art-fixer
**一句话定位**:检测伪像素网格并把图重建为原生分辨率的真像素图;是 `--fixer` 的收网格后端。
**归属**:仓库外 `D:/code/pixel-art-fixer`,Retro Diffusion 开源,MIT。可执行文件 `D:/code/pixel-art-fixer/rust/target/release/pixelfixer.exe`;`pixelize_candidates.py` 的默认路径同此,可用 `--fixer-exe` 覆写。
**输入与输出**:PNG 或 JPEG,最短边不小于 16px、总像素不超过 4MP。`process` 输出原生 1x PNG,并在 stdout 打一行 JSON,含 `cols`、`rows`、`step_x`、`step_y`、`consensus`、`mode` 与耗时。透明按每格多数保留。
**用法命令**:
```bash
D:/code/pixel-art-fixer/rust/target/release/pixelfixer.exe process <输入.png> <输出.png> full
D:/code/pixel-art-fixer/rust/target/release/pixelfixer.exe process <输入.png> <输出.png> fast
D:/code/pixel-art-fixer/rust/target/release/pixelfixer.exe process <输入.png> <输出.png> full legacy
D:/code/pixel-art-fixer/rust/target/release/pixelfixer.exe recon <输入.png> <step_x> <step_y> <cols> <rows> <输出.png>
D:/code/pixel-art-fixer/rust/target/release/pixelfixer.exe full <输入.png>
```
**判据或口径**:`process` 做自动检测加重建,默认重建走 `two_stage_pack`——量化只用于决定结构,颜色取原图对应像素;加 `legacy` 改走旧的 mode-vote 重建。`recon` 是已知网格参数只做重建,默认 raw 不量化,传 `palette` 才启用自动色板,另可传 `dark`。`full` 与 `fast` 只检测不写图,逐图打 JSON;`full` 跑完整仲裁,`fast` 只跑便宜检测器。退出码:成功 0;`process` 输入不合法或写图失败 1;用法错误或未知命令 2。
**已知坑与适用边界**:输出常是几十到几百像素的原生图,显示必须 NEAREST 放大。它是收网格器,不是降噪或调色器;色数与色板仍归 `palette_check.py` 与 `standee_asset_check.py`。`recon` 不检测网格,参数给错就按错的网格重建。`pixelize_candidates.py` 只在 `--fixer` 时调用 `process`,失败打印一行后回退原图。仓库内的可执行文件是本地构建产物;换机器要按 README 的 Rust 用法执行 `cargo build --release`。
## tools/standee_asset_check.py
**一句话定位**:站桩素材硬闸,逐件校验档高、宽度、底锚、色数、alpha 二值与描边亮度。
**归属**:仓库内 `tools/standee_asset_check.py`。
**输入与输出**:默认读 `game/assets/art/character/` 下固定 11 件——八敌 `character_enemy_<键>.png`,主角 `character_hero_portrait.png`、`character_hero_attack.png`、`character_hero_hit.png`。输出 PASS/FAIL 文本,或 `--json` 机器可读结果。
**用法命令**:
```bash
python tools/standee_asset_check.py
python tools/standee_asset_check.py game/assets/art/character
python tools/standee_asset_check.py game/assets/art/character --json
```
**判据或口径**:档高逐键取自 `pixelize_candidates.STANDEE_TIER_H`,主角三态 48。源宽不大于 75,即乘 2 后不超站桩盒宽 150。画布底行至少一个不透明像素,即脚贴底缘。色数不大于 32,只统计 alpha>0 的唯一 RGB。alpha 只允许 0 或 255,出现中间值即判非整数倍缩放痕迹。敌人轮廓像素中暖白描边占比不低于 40%;暖白指亮度不低于 150 且饱和不高于 0.35。主角三态豁免描边断言。全过 0,任一缺失或红灯 1。
**已知坑与适用边界**:主角三态描边实测约 0.4% 暖白,属暗边已知缺口,本闸豁免、不判红;要验主角描边得另找证据。文件名必须与固定清单完全一致,缺一件即失败;目录里多出的 PNG 不检查。它只查站桩 11 件,不覆盖棋盘小人、场景层、UI 或瓦片。色数口径必须排除全透明像素;按所有像素计会假报 33 色。
## tools/palette_check.py
**一句话定位**:扫目录内 PNG,报单张色数与新增色,并按基线色板判新增色近色。
**归属**:仓库内 `tools/palette_check.py`。
**输入与输出**:递归读目录下所有 `.png`;可选 `--baseline` 目录作为基线色板。输出逐张 PASS/WARN/FAIL、小计与累计新增色。
**用法命令**:
```bash
python tools/palette_check.py <待检目录>
python tools/palette_check.py <待检目录> --baseline <基线目录>
```
**判据或口径**:单张色数等于 alpha>0 的唯一 RGB 数,排除全透明。无 `--baseline` 时是只读模式,只出读数、不判红。有基线时,本图颜色减去基线颜色并集得到新增色;新增色与基线任一色的最大单通道差小于 8/255 即判近色红灯。48 色是设计色板 token 上限,不是单张 PNG 的色数;单张 32 色冻结由 `standee_asset_check.py` 执法,本工具的单张色数只报读数。退出码:0 通过,1 有新增色红灯,2 没给目录。
**已知坑与适用边界**:近色只判新增色对基线,不做存量色两两互查。不做色相禁入带检查,也没有全图明度带检查,见「三条已知边界」第 2 条。单张色数超过 32 只标 WARN 或读数,不进退出码;文件头写的「超单张上限判红」与现行源码不一致,按源码为准。文件头提到 `--tokens`,但现行 `main()` 没有这个参数分支,`TOKEN_MAX=48` 目前只是常量。基线目录不存在时不会返回错误:基线色集为空,新增色判据失效。只排除 alpha=0 的像素,半透明像素的 RGB 也计入色数。
## tools/quantize_ui_skin.py
**一句话定位**:把 UI 复用包里的面板、按钮、石框、底噪与图标按 token 明度带量化改色,产出 `game/assets/art/ui/` 下的终稿件。
**归属**:仓库内 `tools/quantize_ui_skin.py`。
**输入与输出**:源包固定为 `artwork/ui-reuse-2026-09-23/` 下的 Kenney 与 OGA CC0 件;当前仓缺位时回退主检出 `D:/code/g-plan2`。输出 14 件到 `game/assets/art/ui/`——5 件框材与底噪、4 枚营地图标、5 枚节点图标。
**用法命令**:
```bash
python tools/quantize_ui_skin.py
```
**判据或口径**:面板、按钮、石框与底噪按五条硬编码 ramp 做明度重映射,保留 alpha 与纹理结构;ramp 常量在脚本顶部 `RAMP_*`。图标走单色重着色:钥石取奖励金,柜台四件取暖白;节点五件取暖白并烘到 20×20。节点图标自检三项——全像素只能是 `NODE_WARM` 或透明、尺寸 20×20、两两 alpha 剪影错位率不低于 0.08;失败 `SystemExit(1)`。复用闸四道(量化、禁入带、灰度可辨、CC0 授权)的来源说明在文件头;脚本实际自检只跑节点图标这三项,框材件没有逐件断言。全过 0,失败 1,源件缺位抛 `FileNotFoundError`。
**已知坑与适用边界**:源包路径、子目录名与文件名全部硬编码;换包或改名直接 `FileNotFoundError`,不是通用量化器。源包在 `artwork/`,整目录不入 git;worktree 或新检出缺件时才会回退主检出,主检出也没有就失败。只服务这一批 UI 复用件,不处理角色、场景或瓦片。输出覆盖同名件;改色值要改脚本顶部 ramp 常量后重跑,再让 Godot 重导入。
## tools/recolor_terrain_tiles.py
**一句话定位**:把 dungeon 地形瓦片按家族与亮度秩序换色,产出 underdark 与 deep_underdark 两套。
**归属**:仓库内 `tools/recolor_terrain_tiles.py`。
**输入与输出**:默认源与输出都是 `game/assets/art/board/`;只匹配 `terrain_*_dungeon.png` 与 `terrain_*_dungeon_<数字>.png`。输出把文件名里的 `_dungeon` 换成 `_underdark` 或 `_deep_underdark`。
**用法命令**:
```bash
python tools/recolor_terrain_tiles.py
python tools/recolor_terrain_tiles.py --palettes underdark
python tools/recolor_terrain_tiles.py --src <源目录> --out <输出目录> --palettes underdark,deep_underdark
```
**判据或口径**:源图每个唯一色先按 RGB 距离归到 dungeon 锚槽的四家族——墙顶、立面、地面、雷坑;再在家族内按亮度秩序线性映射到目标色表同家族槽列。目标色表 `PALETTES` 硬编码在脚本里,槽值来源为归档包 `archive/归档-terrain-walls-2026-09-26.zip` 内的生成器。映射确定性,无随机;脚本打印每枚源图的唯一色数。成功 0;源目录没有匹配瓦片时打印「零输出」并返回 1。
**已知坑与适用边界**:只做换色,不重绘、不增删结构;结构件缺件不能用它补。家族归属靠最近锚色距离,偏离锚槽的颜色可能归错家族。只认 dungeon 命名,其他图类型或其他前缀不处理。默认输出覆盖同目录同名件;源瓦片不入 git,跑前确认盘上源件在。
## tools/gen_theme_icons.py
**一句话定位**:程序化生成 UI 主题小位图——滑轨柄三态、SpinBox 上下箭头、OptionButton 箭头、勾选框四态。
**归属**:仓库内 `tools/gen_theme_icons.py`。
**输入与输出**:无输入参数;输出 9 张 PNG 到 `game/assets/art/ui/gen/`。
**用法命令**:
```bash
python tools/gen_theme_icons.py
```
**判据或口径**:色值取 `game/presentation/views/ui_kit.gd` 的既有常量,脚本头注明溯源;不新增美术素材、不改 art-bible §7。脚本幂等,重复执行覆盖同名件。滚动条柄不走位图,由 `StyleBoxFlat` 承载,本脚本不产出。无显式退出码;正常结束 0,写入异常按 Python 默认非零。
**已知坑与适用边界**:脚本没有验收断言,生成完要目检并让 Godot 重导入。改色值只能改脚本顶部常量再重跑,脚本不读 `ui_kit.gd` 的运行时值。只出主题小件,不处理面板、图标或场景。产出在素材目录,不入 git。
## tools/shot_checker.py
**一句话定位**:整屏截图机检,判屏级骨架、文字对比、背景纹理、明度三档带与文字是否还在框内。
**归属**:仓库内 `tools/shot_checker.py`。
**输入与输出**:截图目录,默认屏清单 `shot_menu.png`、`shot_camp.png`、`shot_level.png`、`shot_battle.png`;同目录 `shot_meta.json` 提供取样框。输出逐项 PASS/FAIL 摘要。
**用法命令**:
```bash
python tools/shot_checker.py <截图目录>
python tools/shot_checker.py <截图目录> shot_menu.png shot_battle.png
```
**判据或口径**:A 屏级骨架——PNG 存在、任一边 1280、灰度 std 不低于 8、唯一色数不低于 64。B 文字对比——战斗 C 区面板与明细带提示的 WCAG 不低于 4.5:1。C 明度三档——战斗屏信息面高于世界中,世界中高于世界底。D 文字禁压纹理——文字框背景中带灰度 std 不高于 18。F 文字在——菜单标题框内与中位灰度差大于 60 的像素占比落在 2% 到 55%。B/C/D 只在清单含 `shot_battle.png` 时跑,F 只在含 `shot_menu.png` 时跑,其余屏只跑 A。全过 0,缺参数或任一失败 1。
**已知坑与适用边界**:截图必须由窗口化 `shot_probe` 产出;headless 没有渲染面,截图是空的。`shot_meta.json` 缺失时 B/C/D 直接判 meta 几何缺失,不会跳过。尺寸断言按「任一边等于 1280」放行;1920×1080 截图两边都不等于 1280,会被判非工程档,与文件头「1920 档 1.5×」说明不一致,按源码为准。焦点预算是目检项,机检不判。
## tools/golden_board_crop.py
**一句话定位**:按 `board_shot_probe` 输出的 `board_meta.json` 从整屏截图裁出棋盘区,供 golden 逐像素比对。
**归属**:仓库内 `tools/golden_board_crop.py`。
**输入与输出**:三个参数依次是 `board_meta.json`、截图目录、输出目录;读截图目录内 `board_<tag>.png`,按 meta 矩形裁剪,写输出目录同名文件。
**用法命令**:
```bash
python tools/golden_board_crop.py <board_meta.json> <截图目录> <输出目录>
```
**判据或口径**:meta 键即 tag,矩形字段为 x/y/w/h;越界按图像边界收拢。meta 缺失、空表、截图缺失、矩形字段非法或退化,均返回 1;全部裁出 0。禁硬编码像素:布局改动只重跑探针刷新 meta。
**已知坑与适用边界**:依赖 `board_shot_probe` 的 meta 与截图命名;手工截图或改名不认。只裁剪不缩放,输出尺寸随 meta 矩形。它不跑比对;比对是 `golden_compare.py`。
## tools/golden_compare.py
**一句话定位**:黄金图与实测截图逐屏灰度对比,报视觉回归。
**归属**:仓库内 `tools/golden_compare.py`。
**输入与输出**:黄金图目录、新截图目录、可选屏清单;默认四屏 `shot_menu.png`、`shot_camp.png`、`shot_level.png`、`shot_battle.png`。输出逐屏 changed 占比与 PASS/FAIL。
**用法命令**:
```bash
python tools/golden_compare.py game/tests/golden <截图目录>
python tools/golden_compare.py game/tests/golden <棋盘裁剪目录> board_L1_dungeon.png board_L2_underdark.png board_L3_deep_underdark.png
```
**判据或口径**:两图尺寸必须一致;逐像素灰度差大于 16 记为 changed。changed 占比超过 2% 判失败;尺寸不符、缺基线或缺截图也判失败。全过 0,有失败 1。
**已知坑与适用边界**:只比灰度,不比色相;纯换色但明度相同可能漏过。基线在 `game/tests/golden/`,资产换代要显式重制并按该目录 README 记录判定,不能拿重制基线掩盖回归。默认四屏不含棋盘与关卡图;比棋盘要传裁剪后的文件名,比关卡图要传 `level_map.png`。截图分辨率必须与基线一致,基线是 1280 档。
## tools/render_sample_sheet_v3.py
**一句话定位**:战斗一屏样张的当前生成器,出 1280×800 与 1920×1080 两版,并在生成过程中跑断言。
**归属**:仓库内 `tools/render_sample_sheet_v3.py`。
**输入与输出**:读 `game/assets/art/board` 的 21 枚瓦片、`game/assets/art/character` 的小人与立绘、`game/assets/ui/icons` 的状态图标;素材缺位回退主检出。输出 `artwork/mockups/sample-batch-v3-final-2026-09-30.png`、`sample_battle_screen_1280x800.png`、`sample_battle_screen_1920x1080.png`;有基线时再出 `sample_battle_screen_before_after.png`;机检报告写到 `reports/`。
**用法命令**:
```bash
python tools/render_sample_sheet_v3.py
python tools/render_sample_sheet_v3.py --save-baseline
python tools/render_sample_sheet_v3.py --gen-assets
```
**判据或口径**:生成即验。F 段管方案甲拼装、旗位、脚锚、0 格字形、微光带限、立面双明度带、悬崖同源;A 段管色板对表、禁入带、周界闭合、明度对比、混排、净空、观感密度;整屏扩展含 S1–S4、P1–P5、V1、V10、A5b。全绿 0,有红 1,报告逐项列 PASS/FAIL。分区几何不设脚本常量:B 区高 / C 区宽 / AP 条高 / 明细带两态高经 `tools/layout_config.py` 读 `game/data/tables/battle_layout.tres`,与游戏侧 `ConfigDb.presentation.battle_layout` 同源。`--gen-assets` 产四件——`scene_stage_plaza.png` 48×12 与 `ui_armor_light`、`ui_armor_ward`、`ui_armor_heavy` 三枚 24×24;尺寸或色数不过直接 `RuntimeError`。`--save-baseline` 只把当前整屏存成基线并返回 0,整屏断言不跑,板面断言即使有红也不改退出码。
**已知坑与适用边界**:固定输出文件名带 2026-09-30,重跑覆盖同名件。字体硬编码 `C:\Windows\Fonts\msyh.ttc`,非 Windows 环境起不来。第一次跑要先 `--save-baseline`,否则 V10 因缺基线判红。素材不入 git;worktree 缺件会抛 `FileNotFoundError`,回退主检出后仍缺也失败。它产的是样张与机检报告,不是游戏内资产;唯一例外是 `--gen-assets` 的四件。
## tools/render_level_map_sheet.py
**一句话定位**:关卡画面样张生成器,出岔路、入口、单链与换层示意,并跑断言。
**归属**:仓库内 `tools/render_level_map_sheet.py`。
**输入与输出**:读 `artwork/ui-reuse-2026-09-23/` 的节点图标与字体、`game/assets/art/ui/backdrop_stone.png`;画布公式、停点标题与 `HIT_PAD` 从 `game/presentation/views/level_map_screen.gd` 解析。输出 `artwork/mockups/level-map-sheet-v1-2026-09-28.png`;机检报告写到 `reports/`。
**用法命令**:
```bash
python tools/render_level_map_sheet.py
```
**判据或口径**:断言 L1 图标互辨、L2 四态与 Boss 环灰度、L3 对比不低于 4.5:1、L4 禁入带零命中、L5 画布自适应、L6 独立屏底、L7 占位区不侵入;另有 SRC 同源、ST 态齐备、S3 换层。全绿 0,有红 1,报告逐项列 PASS/FAIL。节点图标 20px、通道线宽 2/3/4px 等冻结值在脚本顶部;画布公式不自立,解析自 `level_map_screen.gd`。
**已知坑与适用边界**:依赖 `level_map_screen.gd` 存在且常量写法可解析;改常量写法会让脚本抛 `FileNotFoundError` 或解析失败。字体同样硬编码 Windows 字体。固定输出文件名带 2026-09-28,重跑覆盖。样张是验收图,不是游戏资产。
## tools/render_sample_sheet.py
**一句话定位**:战斗一屏样张 v2 生成器,现行 v3 的前一版。
**归属**:仓库内 `tools/render_sample_sheet.py`。
**输入与输出**:读同一批游戏素材,输出 `artwork/mockups/sample-batch-v2-2026-09-28.png`;机检报告写到 `reports/`。
**用法命令**:
```bash
python tools/render_sample_sheet.py
```
**判据或口径**:断言 F1–F7 与 A1–A6;全绿 0,有红 1。产物为 1280×800 单版,没有 v3 的 1920 档、整屏 S/P 段与改前改后并排。
**已知坑与适用边界**:已被 `render_sample_sheet_v3.py` 取代,只用于复现 v2 旧口径,不要拿它出新冻结。固定输出名与报告名带 2026-09-28,重跑覆盖。字体与素材依赖同 v3。
## tools/path_check.py
**一句话定位**:扫描 `asset-path-map.md` 声明的目录与现役文件,以及代码里的 `res://assets/art` 引用,报悬空与退役命名。
**归属**:仓库内 `tools/path_check.py`。
**输入与输出**:可选 `--repo` 仓库根、`--code` 代码根,默认 `.` 与 `game`。输出 L1 目录、L2 文件、L3 代码引用三段 PASS/FAIL。
**用法命令**:
```bash
python tools/path_check.py
python tools/path_check.py --repo . --code game
```
**判据或口径**:L1 查 `asset-path-map` §1 的声明目录,`design/art/candidates` 按需创建豁免。L2 查 `game/assets/art` 的四个语义子目录与现役文件清单,含主角 idle/walk/portrait、八敌、铲三帧、封印印记。L3 grep 代码里的 `res://assets/art` 引用,核对盘上存在性,并检出 `board_floor`、`board_wall`、`pillar_dungeon`、`production/mockups` 等退役命名;路径模板含 `%s` 或 `%d` 的跳过。worktree 素材未同步时,缺失项标 SKIP 不判红;主区完整素材环境才全量判。全过 0,有红灯 1。
**已知坑与适用边界**:依赖 `asset-path-map.md` 的声明清单与代码常量;新增素材目录不更新文档就不会被查。素材不入 git,worktree 空或半同步会大量 SKIP,不能据此判断主区健康。只核对路径存在与退役命名,不核对图像内容、尺寸或色板。
+44 -9
View File
@@ -1,7 +1,7 @@
# 生图提示词卡(角色级资产)v2.12
# 生图提示词卡(角色级资产)v2.11
> **性质**:美术清单生图通道(✦ 用户自跑,现走线上 API)的执行侧文件——每资产一段**自包含完整提示词**:正向 = 通用底座+主体+动作已拼妥、负向 = 通用底座+增补已并妥,整段复制直接贴进工作流即可,**不需要读其他文件、不需要拼装**。人物规格转写自 [character-design.md](character-design.md)(人物设计 v1.0,自 art-bible §5 迁出,圣经 §5 为存根),风格/色彩条款转写自 [art-bible](art-bible.md)(§4.1 主色板 / §4.2 语义色规则);冲突时人物规格以 character-design 为准、色彩以 art-bible 为准。
> **边界**:本文件只管**角色级**资产的生图通道。图标/格面/样张走脚本管线(⚙ 代理出),规格在 art-bible §4.6 与 [art-assets-checklist](art-assets-checklist.md),不需要提示词。**场景级资产(营地场景图的背景层与八个物件层)另立 [camp-scene-prompts](camp-scene-prompts.md);战斗舞台背景层与连续地面层另立 [battle-stage-prompts](battle-stage-prompts.md),本文件不覆盖**;两卡的角色/场景分工以「谁的资产谁出卡」为界,同一段提示词不两处维护。战斗舞台的小石台件与护甲类型图标三枚的**出图工单**登记在本文件 §5;§5 只把各权威节的定值收进逐件工单,不立新值。意图动画三档是动画资产,等一屏样张锁方案后另立规格卡,本文件不覆盖。
> **边界**:本文件只管**角色级**资产的生图通道。图标/格面/样张走脚本管线(⚙ 代理出),规格在 art-bible §4.6 与 [art-assets-checklist](art-assets-checklist.md),不需要提示词。**场景级资产(营地场景图的背景层与八个物件层)另立 [camp-scene-prompts](camp-scene-prompts.md),本文件不覆盖**;两卡的角色/场景分工以「谁的资产谁出卡」为界,同一段提示词不两处维护。战斗舞台的场景级两件与护甲类型图标三枚的**出图工单**登记在本文件 §5(camp-scene-prompts 只管营地场景图,战斗屏这两类无处可落);§5 只把各权威节的定值收进逐件工单,不立新值。意图动画三档是动画资产,等一屏样张锁方案后另立规格卡,本文件不覆盖。
> **状态**:提示词卡成文,出图未开始;按「美术圣经 §5 现行」重生成(正序:B 路线),继承 seed 纪律 / 色相禁入带 / `#F2F2F2` 生成底色;沿革见 git 提交史。
---
@@ -11,7 +11,7 @@
1. **出图**:按下面卡片把正向/负向提示词粘进生图渠道的提示词栏(**渠道 = 远程对话式生图为主,本地 ComfyUI 为备选**——2026-10-06 拍板;对话操作纪律见 [remote-gen-prompts](remote-gen-prompts.md)),每个资产出 **2–3 版候选**(建议固定 3 个 seed 连拍,便于横评)。
2. **入库**:候选原图(带底)放 `artwork/candidates/<资产名>/`,文件名 `v1.png / v2.png / v3.png`。这个目录只存候选,不进引擎。
3. **裁决**:你按 §4 验收速查预筛,挑出每一资产的入围候选拍板。
4. **后处理**:喊我一声,脚本管线代跑——去底转透明、最近邻降采样到目标尺寸、色板量化、四联底测试条(掩尘底/裸土底/暗底/灰度)。后处理工具清单与用法见 [art-pipeline-tools](art-pipeline-tools.md)。
4. **后处理**:喊我一声,脚本管线代跑——去底转透明、最近邻降采样到目标尺寸、色板量化、四联底测试条(掩尘底/裸土底/暗底/灰度)。
5. **转正**:终稿从 `artwork/candidates/` 转入切片资产目录,随表现层升级批接线;清单状态列改「已交付」。
**出图通道(2026-09-23 定稿)**:线上 API,用户自跑——本地 ComfyUI 生图路线已经用户拍板**彻底关闭**(母版工作流/像素 LoRA/Z-Image 模型已清退,不留复活预案,本卡不依赖任何本地管线)。
@@ -286,15 +286,50 @@ Seamless tileable pixel art texture of dry dirt ground, top-down, for a dungeon
## 5. 资产出图工单(四组九件)
> **范围注**:本文件原定位角色级(见文件头边界句),§4bis 已有「地形类同库登记」的先例,本节循同一先例补登记战斗屏的**小石台件(②)**与护甲类型图标三枚(③),两组在营地卡与清单里都无处落工单;战斗舞台背景层(①)与连续地面层的元素/提示词已另立 [battle-stage-prompts](battle-stage-prompts.md),§5.1 只留指路;角色级立绘重产(④)是本文件的本职。四组的规格定值清单 §4 已有登记,本节补的是「逐件照着就能出件」那一层。
> **范围注**:本文件原定位角色级(见文件头边界句),§4bis 已有「地形类同库登记」的先例,本节循同一先例补登记战斗屏两类——战斗舞台的场景级两件(① ②)与护甲类型图标三枚(③),两组在营地卡与清单里都无处落工单;角色级立绘重产(④)是本文件的本职。四组的规格定值清单 §4 已有登记,本节补的是「逐件照着就能出件」那一层。
> **定值纪律(§8 头注 定值单点纪律)**:本节**指路不立值**——色值 = 圣经 §4.1(含场景环境子板)、尺寸与缩放 = §8.2、明度带 = §2、甲档 = character-design §5.3、命名 = §8.3 + asset-path-map。本节新增零定值;凡圣经未定的值,一律进本节 §5.5 缺口,不在本节填空。
> **验收标注纪律**:逐条按三档标 `←机检: <断言>` / `←目检: <判据>` / `←方向性`,照圣经头注 2026-10-06 用户拍板的写法。
> **流程**:出图前后一律走 §0 五步;出图登记义务照 remote-gen-prompts 头注轻量口径(用途+生成时间必注,其余参数有则记),随候选图存 `artwork/candidates/<资产名>/`;登记不作正稿遴选前置。
> **产出状态**:组二地台件与组三护甲三枚已按各节 ⑤「程序化产出即终稿」产出入包(`scene_stage_plaza.png` 48×12、`ui_armor_light/ward/heavy.png` 24×24,路径见各组 ① 字段;登记=战斗一屏样张批,合并提交 3b66d1a6);组一背景插画已迁至 [battle-stage-prompts](battle-stage-prompts.md)(仍待出图),组四立绘重产仍待出图。
> **产出状态**:组二地台件与组三护甲三枚已按各节 ⑤「程序化产出即终稿」产出入包(`scene_stage_plaza.png` 48×12、`ui_armor_light/ward/heavy.png` 24×24,路径见各组 ① 字段;登记=战斗一屏样张批,合并提交 3b66d1a6);组一背景插画与组四立绘重产仍待出图。
### 5.1 组一 · B 区舞台背景插画(1 件)
> **本件已迁出本文件**:逐条要素、分层提示词、否定后缀与验收 = [battle-stage-prompts](battle-stage-prompts.md)(背景层 §2.1/§4);场景构成与图层归属 = [art-bible](art-bible.md) §6.1;尺寸读 `game/data/tables/battle_layout.tres`。本文件不再复写本件提示词,避免两处维护。
| 字段 | 值 |
|---|---|
| ① 路径与命名 | `game/assets/art/scene/scene_stage_bg.png`——域键 `scene`(asset-path-map §3),命名照 §8.3 `<域>_<对象键>_<状态\|变体>[_<序号>].png`。**三关不按板出三张,文件名不带 `_dungeon`/`_underdark`/`_deep_underdark` 后缀**(三关换挡走 §4.3 `wall_ambient` tint 槽,与棋盘同款换皮逻辑) |
| ② 尺寸档与像素网格 | 源 1280×270(B 区全高含明细带底),显示 1×;九宫格 = 否。甲/乙两分类不覆盖环境件(§8.2 甲类 = 文本与语义件、乙类 = 角色与单位精灵),本件在 1280×800 基座下满幅铺 1×、不缩放;1920 档口径见 §8.2「1920×1080 档策略」条,本节不另立值 |
| ③ 色板约束 | 进**战斗屏场景环境子板**(§4.1 分层②:每屏一套 ≤32 色、跨屏不共享、**不占 48 token 额度**——48 额度余量现为 0(2026-10-28 用尽),本件不动它)。**零新增**——主体三色:远景最深 `#282220`(默认;备选 `#3A2E22`,二选一,选后者则图内禁用 `#362B20`,两者三通道差 4/3/2 全部 <8,是 §4.1 欠账清单六对之一);中景洞壁 `#3E362C` 与中景受光壁面 `#453723` 交替,**两色只靠笔触与纹理分出块面,不靠第三个色**;需过渡色时只从在册的 `#362B20`/`#4C4038` 取。单张 PNG 像素域色数 ≤32。第二道门 = 与既有任一色三通道差不得全部 <8/255(§4.1 增量纪律),不过门即换色合并、禁加色绕行 |
| ④ 明度带归属 | **世界底层下沿子带 L 0.014–0.06**(§2 世界底层两子带)——全图落该子带,不得越进上沿子带 L 0.08–0.14(那是已开格底与迷雾记忆态合成底的地盘)。三色实测: `#282220` L≈0.016 / `#3E362C` L≈0.039 / `#453723` L≈0.042 |
| ⑤ 是否程序化件 | 否 → **须出图**(✦ 插画级,清单 §4 已定来源 = 作者自跑远程生图管线)。现行程序化平色 + 光柱 = 占位(◐),按 §7.1 占位完成条款须在终稿闸前换代 |
| ⑥ 验收判据 | 见下方逐条判据 |
| ⑦ 出图操作要点 | 见下方提示词与负面约束 |
**逐条判据**
- 构图只出远景 + 中景两带:远景 0–35%、中景 35–78%,78%–100% 段不画 ←机检: 画布 1280×270 逐位相符;`y ≥ 210`(78%) 段内 `#968264` 像素数 = 0 且该段与中景段无独立地台结构件
- 三关只走 tint 槽 ←机检: 仓内 `scene_stage_bg*` 文件名无 board_type 后缀变体(三关方向指路 §6.3,该节终值未冻结,属实装批,不阻塞本件出图)
- 色板零新增 ←机检: 单张像素域色数 ≤32 且逐色在 §4.1 战斗屏场景环境子板在册
- 明度带 ←机检: 全图取样点相对亮度 L 全部落 [0.014, 0.060]
- 禁入带 ←机检: 全图无 HSV 色相 300°–345° 像素(清单 §0 复用闸②吸色检查同口径)
- 临时光柱层退役 ←机检: 本件入包后 `battle_stage_view.gd` 四柱临时光柱层退役(§8.2 站桩排版③条;该条的立绘溢出判定不随之变更)
- 背景自然后退 ←目检: 立绘与 C 区数字在前,本件不构成完整凸形、无第二焦点(§3.4 焦点预算 / §6.1 低对比后退)
- 气质 ←方向性: 明亮地窟、暖倾向、无压抑(§1 风格基调;§9.2 Darkest Dungeon 式压暗在反面清单)
**出图画布**:2560×540(终稿 2×,保证整数倍降采样;平台不支持该宽高比时取可用的最宽档出图、按上下居中裁到 4.74:1,禁横向拉伸变形)。
**正向提示词(整段可贴)**:
```
Pixel art style. True pixel art game background illustration drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about eight colors, retro 16-bit look, Aseprite style, all ages, bright cheerful fantasy cavern, not dark and not grim, flat side view, orthographic, no perspective distortion, very wide horizontal band about 4.7 to 1 aspect ratio. This is the BACK WALL of a bright underground cavern arena seen straight on, and it is a BACKGROUND ONLY: it must contain no characters, no creatures, no floor platform and no props, because the standing stone platform and the characters are drawn on top of it as separate pieces. TWO BANDS ONLY, and the bottom twenty-two percent of the frame stays completely empty. FAR BAND (the top zero to thirty-five percent of the frame height): the deepest hollow of the cavern, a flat dark far wall receding into shadow, one or two rows of very simple flat stalactite silhouettes hanging from the top edge, the darkest area of the picture, its darkest value is hex color #282220, no light sources, no small detail, everything reduced to a few large flat shapes. MIDDLE BAND (thirty-five to seventy-eight percent of the frame height): the cavern wall, broad flat facets of stone alternating between hex colors #3E362C and #453723, the facets separated only by their brush texture and by a few slightly darker seam lines, with two or three soft vertical light shafts falling from the cave roof across the wall, the shafts being flat low-contrast bands of light and not beams of particles. WHOLE IMAGE: every color is very dark and low in contrast, the entire background recedes behind the characters, there are no bright highlights and no rim lights, no single element forms a complete solid shape that competes for attention, and all wall facets stay large and simple.
```
**负向提示词(整段可贴)**:
```
Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; blur or compression artifacts; a vignette, dark corners, or any mood darkening; any purple or magenta anywhere, any color with a hue between 300 and 345 degrees; any text, letters, numbers, watermark, signature or logo; any frame, border, panel, arrow or UI element; any characters, people, creatures or animals; any floor, ground plane, stone platform, pedestal, stage floor or baseboard in the lower part of the frame, because the bottom twenty-two percent of the frame must stay completely empty; any torch, brazier, lantern, candle or glowing object as an object, only the flat light shafts described above; bright or saturated colors, any area lighter than hex #453723; high contrast edges, rim lights, black outlines; rubble, chains, bones, murals, paintings, furniture or any extra prop.
```
**后处理**:§0 五步第 4 步——本件是**不透明全幅件**,去底与四联底测试条两步不适用,只走最近邻降采样到 1280×270 + 色板量化到本节 ③ 所列色;终稿入包后走 §1.4 整屏样张闸。
### 5.2 组二 · 近景地台件(1 件)
@@ -308,7 +343,7 @@ Seamless tileable pixel art texture of dry dirt ground, top-down, for a dungeon
| ⑥ 验收判据 | 见下方逐条判据 |
| ⑦ 出图操作要点 | 见下方工艺要点(手绘/程序化,无须 AI 生图) |
**独立成件的两条理由(§6.1「近景地面与地台走独立件」条,写进工单备查)**:① 背景层按关卡出三张变体,地台若画进任一张就得三张各画一遍、且三关站位面会漂移;② 结算期明细带整条压住 B 区底带(带高 = `log_band_h_settle`,读布局表),地台在独立层才能跨关恒定并保持 §6.1 定死的世界中层承载面不变。
**独立成件的两条理由(§6.1「近景地台走独立件」条,写进工单备查)**:① 结算期明细带底 84px 会盖住 B 区 78%–100% 段(约 59px),84 > 59,近景带在结算期整条不可见——画进背景等于画一件永远看不见的东西;② §4.3 `wall_ambient` tint 作用于整张背景,地台若画进背景会随三关换明度,而 §6.1 舞台正面目标把地台定死为世界中层承载面,随关卡变会破 §2 明度分层的层间关系。
**逐条判据**
@@ -344,7 +379,7 @@ Seamless tileable pixel art texture of dry dirt ground, top-down, for a dungeon
- ←机检: 源 24×24、显示 1× 整数倍(§8.2 显示档登记例外条:素材一字节禁改)
- ←机检: 文件名逐位等于 `ui_armor_light` / `ui_armor_ward` / `ui_armor_heavy`
- ←机检: 单张像素域色数 ≤32
- ←机检: 三枚对其实际挂载底色的图形对比 ≥3:1(§4.1 图标槽柔和档条的图形参考线硬约束)。**挂载底色 = 站桩名字行的素底,不是舞台底也不是地台面**——站桩名字行是文字,§7.2「一切文字(含菜单/说明/tooltip/按钮/底栏,**无区域例外**)」+「文字禁直接压纹理底——垫容器或罩层」两条覆盖它;§6.1 舞台正面目标条(2026-10-28 v3.22 定值)已就站桩文字位显式落法:「站桩文字位改垫素底或落进结算明细带」,并明写「**非新条款**」,§7.2 同批加「站桩石板地台面同理」句、两处机检断言同文。舞台背景插画(`scene_stage_bg_<board_type>` 三关变体;battle-stage-prompts §2.1)属纹理底,落法门触发。**故「站桩名字行落法 = 垫素底」是既有条款的落法,本节不立新条款**(§5 头注定值纪律:本节指路不立值)。素底明暗档 **2026-10-28 已裁 = 深档 `#282220`**(§7.4 血数垫牌 `HP_BAR_BACK` 在册同款,零新增;浅档 `#F5EAD3` 落选,两档对照与理据见 §5.5 缺口 2 已裁条)
- ←机检: 三枚对其实际挂载底色的图形对比 ≥3:1(§4.1 图标槽柔和档条的图形参考线硬约束)。**挂载底色 = 站桩名字行的素底,不是舞台底也不是地台面**——站桩名字行是文字,§7.2「一切文字(含菜单/说明/tooltip/按钮/底栏,**无区域例外**)」+「文字禁直接压纹理底——垫容器或罩层」两条覆盖它;§6.1 舞台正面目标条(2026-10-28 v3.22 定值)已就站桩文字位显式落法:「站桩文字位改垫素底或落进结算明细带」,并明写「**非新条款**」,§7.2 同批加「站桩石板地台面同理」句、两处机检断言同文。舞台背景插画 `scene_stage_bg.png` 的中景两色只靠笔触与纹理分块面(5.1 ③),属纹理底,落法门触发。**故「站桩名字行落法 = 垫素底」是既有条款的落法,本节不立新条款**(§5 头注定值纪律:本节指路不立值)。素底明暗档 **2026-10-28 已裁 = 深档 `#282220`**(§7.4 血数垫牌 `HP_BAR_BACK` 在册同款,零新增;浅档 `#F5EAD3` 落选,两档对照与理据见 §5.5 缺口 2 已裁条)
- ←机检: B 区站桩名字行与护甲图标正下方像素非地台板身色 `#968264`(§6.1 / §7.2 同文断言)
- ←目检: 灰度下按甲面覆盖率三档可辨——无甲板 → 符纹带 → 大面积甲板(character-design §5.3 灰度通道条)
- ←目检: 三枚互辨,身份只靠形状不靠色相(§4.5 ⑨ 图标键族:色相不承载身份)
@@ -456,7 +491,7 @@ Seamless tileable pixel art texture of dry dirt ground, top-down, for a dungeon
- ←机检: 本值入板后全板色对中三通道差全部 <8/255 的对数 = 0
2. **站桩名字行的落法 + 素底明暗档——已裁(2026-10-28)= 垫素底、素底取深档 `#282220`**,本节转指路(原记为「轻甲木色在深底上不过 3:1」;核出落法是它的前置,故先落这一条)。
- **推导核实 = 成立,且是既有条款的落法、非新条款**:站桩名字行是文字,§7.2「一切文字(含菜单/说明/tooltip/按钮/底栏,**无区域例外**)」+「文字禁直接压纹理底——垫容器或罩层」两条覆盖它;§6.1 舞台正面目标条(2026-10-28 v3.22 定值)已就站桩文字位显式落法——「站桩文字位改垫素底或落进结算明细带」并明写「**非新条款**」,§7.2 同批加「站桩石板地台面同理」句,两处机检断言同文。舞台背景插画(`scene_stage_bg_<board_type>` 三关变体;battle-stage-prompts §2.1)属纹理底 → 落法门触发。**已回填**:站桩名字行落法 = **垫素底**(§5.3 共用验收条 + 5.3.1 / 5.3.3 对比判据)。
- **推导核实 = 成立,且是既有条款的落法、非新条款**:站桩名字行是文字,§7.2「一切文字(含菜单/说明/tooltip/按钮/底栏,**无区域例外**)」+「文字禁直接压纹理底——垫容器或罩层」两条覆盖它;§6.1 舞台正面目标条(2026-10-28 v3.22 定值)已就站桩文字位显式落法——「站桩文字位改垫素底或落进结算明细带」并明写「**非新条款**」,§7.2 同批加「站桩石板地台面同理」句,两处机检断言同文。舞台背景插画 `scene_stage_bg.png` 中景两色只靠笔触与纹理分块面(5.1 ③),属纹理底 → 落法门触发。**已回填**:站桩名字行落法 = **垫素底**(§5.3 共用验收条 + 5.3.1 / 5.3.3 对比判据)。
- **原冲突 = 素底明暗档圣经未定死(2026-10-28 已由作者裁,本节不替裁的冲突至此闭合)**:§4.1 只在册浅档 `#F5EAD3`(数字色牌,圣经唯一素色牌值),§7.4 血数垫牌在用的深档 `HP_BAR_BACK #282220` 亦在册。两档后果**相反**(下表留作裁定依据账):
| 受影响的位 | 浅档 `#F5EAD3` | 深档 `#282220` |
-1
View File
@@ -40,7 +40,6 @@
| 连锁坍塌显形演出 | ⬜ | [art-assets-checklist](art-assets-checklist.md) §4 |
| 封印特效(动作=封印,两件:施印特效+印记常显) | ⬜ | [art-bible](art-bible.md) §3.2 + [art-assets-checklist](art-assets-checklist.md) §4 |
| 弹道 / 退场等动画密度 | ◐ 程序化弹道在役(play_projectile 0.16s+0.19s 到达拍调度;密度终稿归战斗表现) | [art-assets-checklist](art-assets-checklist.md) §4(战斗表现输入清单) |
| B 区舞台背景三关变体+共用连续地面层(场景件) | ✅ 已交付(2026-10-31 入包 `game/assets/art/scene/`:三张背景 1280×252、共用地面 1280×100;地面按作者裁保留候选 v12b 原亮度 L≈0.132) | [art-bible](art-bible.md) §6.1 · [battle-stage-prompts](battle-stage-prompts.md) §2 |
---
+1 -1
View File
@@ -27,7 +27,7 @@
|---|---|---|
| `character/` | 角色棋盘帧 + B 区立绘 | character_hero_idle/walk(棋盘 32×32 朝左基准)、character_hero_portrait(B 区立绘 46×48)+ character_enemy_<键> 八敌 |
| `fx/` | 特效帧 | shovel_swing_f0/f1/f2(挥铲三帧,握把锚 (18,12)) |
| `scene/` | **场景图**(背景层 + 地面层 + 围墙层 + 可点物件层;营地场景图首批) | 营地 10 张:`scene_camp_bg`(背景层,不透明全幅=天空+远山+地面+门洞小路)+ `scene_camp_wall`(围墙层,透明底、门洞留透明缺口)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_tent`(承载件,不可点)+ `scene_stage_bg_dungeon` / `scene_stage_bg_underdark` / `scene_stage_bg_deep_underdark`(B 区战斗舞台背景层三关变体,✦ 插画级,顶部空区+远景天然洞壁,不画近景地面,按 board_type 选图)+ `scene_stage_ground`(B 区连续近景地面层,✦ 插画级,泥土/砂砾/碎石+顶缘分隔带,跨关恒定)+ `scene_stage_plaza`(B 区角色脚下小石台,48×12 九宫格件)——B 区战斗舞台背景与连续地面 2026-10-31 已入包;登记超前:营地场景图批落地前盘上未建 |
| `scene/` | **场景图**(背景层 + 可点物件层;营地场景图首批) | `scene_camp_bg`(背景)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_tent`(承载件,不可点)+ `scene_stage_bg`(B 区战斗舞台背景,✦ 插画级,只出远景+中景两带)+ `scene_stage_plaza`(B 区近景地台,48×12 九宫格件)——登记超前:营地场景图批落地前盘上未建 |
| `board/` | 砖面/封印印记(三关换皮 + 封印印记件) | terrain_floor_<board_type>_<n>.png(三关地面系)、seal_mark.png |
## 4. 坐标/朝向等工程侧约定备查
-182
View File
@@ -1,182 +0,0 @@
# 战斗舞台背景 · 分层生图提示词卡 v1.8
> **性质**:B 区战斗舞台背景(顶部空区+远景天然洞壁+近景连续泥土地面+分隔带)的出图执行侧文件。背景按三关出三张变体,地面三关共用一张;场景级资产,与角色级 [art-prompts](art-prompts.md) 并列,两文件互不覆盖。
> **依据**:场景构成与图层归属 = [art-bible](art-bible.md) §6.1;三关方向 = art-bible §6.3;色板 = art-bible §4.1(战斗屏场景环境子板);尺寸 = `game/data/tables/battle_layout.tres`(背景读 `stage_h`;地面读 `ground_h`,该字段由地面层实装批新增并回填);命名与路径 = [asset-path-map](asset-path-map.md);后处理 = [art-pipeline-tools](art-pipeline-tools.md)。
> **边界**:只管三张背景变体与一张共用地面的提示词与验收;角色、格面、图标、小石台 `scene_stage_plaza.png` 与样张走 art-prompts 与脚本管线,不在本卡。
> **建立**:2026-10-31 · 状态:三关背景变体与共用地面已按本工单出图并入包(选定=dungeon v10c/underdark v10c/deep_underdark v10c/ground v12b,落点与读数见 art-bible §6.1「B 区舞台背景与共用地面的交付绑定」);出图通道由作者自定(参考候选可用内置 ImageGen,水印可留;入包终稿须干净)。
---
## 0. 使用流程(四步)
1. **出图**:按 §2 把提示词贴进生图渠道;每次出图先放 §1 的风格参考图。**先只出 dungeon 背景 1 张**,作者确认风格前缀后,再出 underdark / deep_underdark 两张变体与连续地面层。
2. **入库**:候选原图放 `artwork/candidates/scene_stage_bg/`(三关变体用 `dungeon_*` / `underdark_*` / `deep_underdark_*` 命名)与 `artwork/candidates/scene_stage_ground/`;`_登记.txt` 记用途与生成时间。
3. **后处理**:三张背景不去底(不透明全幅);连续地面层去底转透明(顶缘以上键控)+最近邻降采样+色板量化+底测试条;工具清单与已知边界见 [art-pipeline-tools](art-pipeline-tools.md)。
4. **转正**:终稿转入 `game/assets/art/scene/`;三张背景按 `board_type` 选图、连续地面层接线与布局表 `ground_h` 字段由实装批落码。
---
## 1. 风格参考图(每次出图都要带)
| 参考图 | 作用 |
|---|---|
| `game/assets/art/character/character_hero_idle.png` | 定像素密度、平涂质感、描边与暖色族 |
| `game/assets/art/board/terrain_floor_dungeon_1.png` | 定岩面/地面的笔触与色族;只借质感,不抄瓦片形状与拼块接缝 |
| `game/assets/art/scene/scene_stage_plaza.png` | 定近景层色族与顶缘处理;只借色与像素密度,不借石板铺装 |
**禁止**拿未经验证的自造图当风格参考——风格错了整批全废。元素参考可用 D&D 洞穴/地下城的岩层、裂缝、凹壁、碎石等常见构成(见 §3),但不把外部图当风格锚。
贴参考图时,在提示词末尾接这一句:
```
Style reference: the attached images are this game's own finished pixel art — match only their rendering style, the same pixel density, flat shading, hard edges and warm limited palette. They are style references only: do not copy their subjects, characters, gear, props, tile patterns or any specific rock or ground layout, and do not put any character into the scene.
```
---
## 2. 两层提示词
### 2.1 背景层(三关变体,按 `board_type` 选一张)
**画什么**:顶部空区(深色、无物);**纵深洞厅远景**——远层暗剪影(远处洞口/通道、层叠岩体、洞底轮廓)、中层主要细节层(岩台、岩柱、凹壁、裂缝、建筑剪影)、近层左右暗色岩框;三关母题按 §3.2。
**不画什么**:近景地面、地台、平台;贴墙感、单件圆柱/贴图直接贴顶、悬挂物;砖石墙、规则砌缝、方整石块、瓷砖/铺装;钟乳石、吊锥、洞顶结构;角色、生物、道具、火把/灯/发光物;文字/UI。
**三关变体**:
| 关卡 `board_type` | 资产 | 岩体/构图 | 色族 |
|---|---|---|---|
| dungeon | `scene_stage_bg_dungeon.png` | 深远洞厅:远层支洞/通道口;中层岩台+歪斜木栈道/脚手架+削尖木桩围栏 | 暖石棕(`#282220` / `#3E362C` / `#453723`) |
| underdark | `scene_stage_bg_underdark.png` | 深远洞厅:巨型石柱、细长石笋、蛛网、螺旋塔剪影、巨菇、裂谷石桥 | 冷蓝灰+少量紫罗兰灰(约 256° 域;避 300°–345°) |
| deep_underdark | `scene_stage_bg_deep_underdark.png` | 深远洞厅:石笋城郭、棱角石厅/扶壁、跨谷石桥、矿道口、烟囱塔群 | 灰黑主调+金属质矿脉 |
**出图画布**:三张同规格——终稿 2× = (2× 基准屏宽) × (2× `stage_h`),尺寸读 `battle_layout.tres`。平台不支持该宽高比时取可用最宽档、按中央 5:1 横带裁到目标宽高比(基准屏宽 / `stage_h`),禁横向拉伸。当前内置参考通道最宽 2560×1080,按中央带裁。
**共用底座正向提示词(三张共用)**:
```
Pixel art style. True pixel art game background layer drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about eight colors, retro 16-bit look, Aseprite style, all ages, bright cheerful fantasy cavern, not grim, flat side view, orthographic, no perspective distortion. A very wide horizontal composition, about 5 to 1 aspect ratio; if the canvas is taller than that band, paint the band across the middle and fill the area above and below with flat dark continuation #282220, keeping all detail inside the band. This is the FAR BACKGROUND of an underground cavern arena: the camera stands at the arena edge and looks across a deep cave chamber, so the view must read as a distant cavern interior with real depth, NOT as a flat wall close behind the characters. BACKGROUND LAYER ONLY: no ground, no floor, no platform, no characters, no creatures, no props in the near view, no torches, no lanterns, no light sources of any kind, no light shafts, no beams of light, no glow. TOP BAND (the top fifth of the band): completely empty flat solid darkness #282220, no texture, no ceiling, no stalactites, no hanging cones, no spikes, no objects, and nothing touches the top edge. DEPTH LAYERS, from far to near: FAR LAYER: the farthest depth, simple dark silhouettes of distant rock masses and two or three dark side tunnel openings; no continuous floor line, the far depth fades into darkness; these are the darkest and simplest shapes, their edges partly hidden behind the midground, and they never form isolated cutouts against the top band. MIDGROUND: the main readable layer across the middle of the band — several enormous rock masses and broad ledges at different heights that overlap and partly hide each other, irregularly spaced, not a row of evenly spaced pillars; a few tall natural rock pillars rise from behind the ledges; the central part of the frame opens into a deeper recess with one large dark tunnel mouth far behind it; shallow alcoves and recesses, cracks and fissures; this layer carries most of the detail, low in contrast; the rock reads as huge natural cave masses, not a built wall, with only a few faint, short, broken strata hints that fade out, never continuous wavy stripes. NEAR FRAME: at the left and right edges, two dark rock masses frame the view, slightly darker than the midground; no isolated cylinder, column or prop pasted against the top; no hanging shapes. CENTER: the central half of the band stays calm and low in detail so the characters and UI can read. The overlapping ledges, pillars and far silhouettes must create several depth planes, not one flat backdrop. BOTTOM BAND (the bottom third of the band): simple flat dark rock in #282220 fading into shadow, no floor, no ground plane, no continuous horizontal ground line, no stone platform, no pedestal, no baseboard, no props — this area will be covered by a separate ground layer and UI. WHOLE IMAGE: very dark and low in contrast, no bright highlights, no rim lights, no vignette, no complete solid shape that competes for attention; no dense horizontal wavy lines, no repeated banding, no zebra stripes, no wood-grain pattern.
```
**三关变体段(接在共用底座后)**:
dungeon:
```
Use warm limestone for the far and mid rock: dark warm brown-grey, from the darkest #282220 through #3E362C to the lighter #453723; broad calm rock faces and a few wide low ledges; only a couple of faint, broken strata hints, not bands across the wall. GOBLIN LAIR DETAILS, fully built into the rock, only in the midground and far layer, small and low contrast, partly hidden: one broken crude wooden walkway anchored into a distant ledge with its posts set into the rock, its middle hidden behind a rock outcrop; one short pointed stake fence embedded along the edge of a far ledge, partly behind a rock mass; a dark side tunnel mouth with a rough timber frame built into the rock; no isolated fence segments, no floating walkway pieces, no structures sitting on top of ledges like icons; never in the near frame, never at the top, never lit.
```
underdark:
```
Use cool desaturated blue-grey rock for the far and mid rock: the rock faces are dark cool blue-grey, not warm brown; the cracks are sharper and more vertical; in the far layer add simple dark silhouettes of a few giant natural rock pillars whose tops fade into the dark top band, slender stone spires rising from distant ledges, thin muted grey web strands between rocks, a few curved spiral tower silhouettes, huge mushroom silhouettes on far ledges, and a distant chasm crossed by a thin stone bridge; add only a few thin, muted blue-violet mineral seams around 256 degrees hue, never saturated purple or magenta; no glowing fungi, no faerie fire, no bricks, no regular masonry.
```
deep_underdark:
```
Use very dark neutral grey-black rock for the far and mid rock: the rock faces are grey-black and angular, with broken, step-like ledges; in the far layer add simple dark silhouettes of a city carved into a giant stalagmite, with angular stone halls, buttresses and stepped terraces, a few stone bridges spanning a rift, several mine entrances with dark timber frames, and a row of chimney and vent stacks; a few thin cool silver and copper-grey mineral veins run through the rock as matte metal, not glowing; no forge fire, no glow, no smoke plume, no bricks, no regular masonry.
```
**共用否定后缀(三张共用)**:
```
Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; black or heavy dark outlines; blur or compression artifacts; a vignette, dark corners, or any mood darkening; any saturated purple or magenta anywhere, any color with a hue between 300 and 345 degrees (the underdark's muted 256-degree blue-violet seams are allowed and must stay desaturated); brick patterns, rectangular cut stone blocks, regular masonry joints, mortar lines, tiled surfaces, paved surfaces, a near-view carved or dressed stone wall, a flat wall close behind the characters, a single isolated column or cylinder pasted against the top band, any element touching the top edge, isolated fence segments, floating walkway pieces, structures sitting on top of ledges like icons, a continuous horizontal floor line; dense horizontal wavy lines, repetitive wavy banding, zebra stripes, wood grain, layer-cake stripes, continuous parallel strata across the whole wall; stalactites, stalagmites used as hanging shapes, hanging cones, ceiling spikes, cave curtains; any ground, floor plane, stone platform, pedestal, stage floor, baseboard, path or paving in the lower band; any character, person, creature or animal; any light source of any kind, any torch, brazier, lantern, candle, crystal, glowing object, light shaft, god ray, beam of light, glow, bloom or bright patch; any props in the near view, bones, chains, rubble pile, furniture, murals, paintings, grass, moss, plants, trees, small foreground fungi or glowing fungi (the underdark's far-layer giant mushroom silhouettes are allowed and must stay dark and unlit); bright or saturated colors, any area lighter than the variant's lightest wall color; high contrast edges, rim lights, black outlines; text, letters, numbers, watermark, signature, logo; frame, border, panel, arrow or UI element.
```
**组合方式**:每张背景 = 共用底座 + 对应关卡变体段 + 共用否定后缀,拼起来即整段可贴。三张仅岩体形态与色族不同,构图规则一致。
**后处理**:三张均不去底;最近邻降采样到终稿尺寸,色板量化到 art-bible §4.1 战斗屏场景环境子板(三张共用该子板,新增冷/灰色按 §4.1 两道门入板),单张 ≤32 色;四道机检见 §4。
### 2.2 连续地面层(`scene_stage_ground`,三关共用,顶缘以上透明)
**画什么**:全宽近景地面——**干净压实泥土为主**,整片平整、中低明度;全幅只保留**零星 3–5 颗小石**,散点分布、不成排不成堆;顶缘为不规则泥土边+阴影缝,可带 2–3 颗小石;中央平缓留空;地面跨三关恒定。
**不画什么**:瓷砖、砖缝、规则铺装、石板块、踢脚线/直亮边;草、木地板;远景/洞壁/天空;角色、生物、道具、火把/灯;中央大石或碎石堆挡站位;满铺碎石、密集砂砾、成排/成堆碎石、亮色浅棕大片、泥点噪声、垃圾杂物。
**出图画布**:终稿 = 基准屏宽 × `ground_h`(`battle_layout.tres` 新增字段,地面层实装批回填);本件是极宽薄条,平台无对应宽高比时按横向可平铺纹理条出图(左右无缝,宽度取平台方档,高度取 `ground_h` 的整数倍),终稿裁/平铺到目标宽高比,禁横向拉伸。试片阶段可按构图比例先出,终稿尺寸读表。
**正向提示词**:
```
Pixel art style. True pixel art game layer drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about eight colors, retro 16-bit look, Aseprite style, all ages, bright cheerful fantasy cavern, not grim, flat side view, orthographic, no perspective distortion. A full-width horizontal texture strip of the NEAR GROUND of a bright underground cavern arena, seen from the front, drawn on a plain flat uniform light grey #F2F2F2 background for easy keying: the ground strip occupies the lower part of the canvas and extends down to the bottom edge, and everything above its top edge is clean empty #F2F2F2 with nothing drawn in it. The ground is mostly CLEAN packed bare dirt with large calm flat areas of nearly unbroken color, medium-dark warm brown, not light tan and not bright; across the whole strip there are only THREE OR FOUR small stones in total, scattered singly and far apart, plus two or three small stones along the top edge; no rows of pebbles, no clusters, no gravel patches, no piles; the middle half of the strip is smooth and clear so characters can stand there; the surface is only slightly uneven with two or three thin cracks; no dense speckles, no heavy noise, no scattered pebbles spread everywhere, no muddy patches, no dirt clumps, no debris. The TOP EDGE of the ground is an irregular edge of packed dirt with a thin dark shadow seam, with only two or three small stones, clearly marking the boundary between the ground and the far wall behind it; the top edge must NOT be a straight tile edge, NOT a clean bright rim, NOT a masonry joint, NOT a regular curb, NOT a continuous row of stones or equal-sized pebbles. Colors: packed earth body in warm tan #968264 with large darker areas of #7A6448 and #5E4C38, darker soil patches #453723 and #3E362C, only the few small stones in #C9AF83, dark cracks and shadow seam in #4C4038 and #282220; the overall ground is medium-dark, not light tan and not bright, no large light-colored areas; matte flat colors, no gloss. No regular paving, no tiles, no bricks, no stone slabs, no grass, no plants.
```
**负向提示词**:
```
Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; black or heavy dark outlines; blur or compression artifacts; a vignette, dark corners, or any mood darkening; any purple or magenta anywhere, any color with a hue between 300 and 345 degrees; tiles, tile joints, paving, rectangular stone slabs, bricks, masonry, mortar lines, a clean straight rim, a bright curb or a regular edge along the top of the ground; grass, moss, plants, trees, fungi; any character, person, creature or animal; any prop, tool, bone, chain, furniture, torch, brazier, lantern, crystal or glowing object; any far wall, cave wall, background scenery or sky above the ground, the area above the top edge must stay plain flat uniform light grey #F2F2F2; any large boulder or rubble pile in the middle of the ground, the middle must stay clear for standing; many stones, rows of pebbles, clusters of rubble, gravel patches, stone piles, a continuous row of stones along the top edge, light tan or bright ground, large light-colored areas, dense speckles, heavy noise, scattered pebbles spread everywhere, muddy patches, dirt clumps, debris, garbage, a messy or dirty ground surface; any complete solid shape that competes for attention; bright or saturated colors, anything lighter than #C9AF83 inside the ground itself (the flat #F2F2F2 keying area above the top edge is required and allowed); high contrast edges, rim lights, black outlines; text, letters, numbers, watermark, signature, logo; frame, border, panel, arrow or UI element.
```
**后处理**:去底(`#F2F2F2` 键控,顶缘以上转透明)+ 最近邻降采样到终稿尺寸 + 色板量化;地面主体色落 art-bible §4.1 世界中层色族,单张 ≤32 色;四道机检见 §4。
---
## 3. 纵深结构与三关母题(2026-10-31 作者认可)
### 3.1 纵深结构(三关共用)
| 层次 | 放什么 | 规则 |
|---|---|---|
| 远层 | 远处洞口/通道、层叠岩体、洞底轮廓、远景建筑剪影 | 压到最深,细节最少;边缘被中层遮住,不孤立、不贴顶 |
| 中层 | 岩台、岩柱、凹壁、裂缝、主要建筑剪影 | 细节主要放这里;低对比 |
| 近层 | 左右暗色岩体作画框 | 不挡中央;禁单独圆柱/贴图贴顶 |
| 顶部/底部 | 顶部空区、底部暗部 | 顶部不挂钟乳石/吊锥;底部留给地面层 |
| 中央 | 角色站位区 | 1/2–2/3 宽保持低细节、无大障碍 |
### 3.2 三关母题(推荐项启用,可选项关闭)
| 关卡 | 推荐项(本版启用) | 可选项(本版关闭) |
|---|---|---|
| dungeon 地精 | 远处支洞/通道口、木质栈道/脚手架、削尖木桩/围栏残段 | 骨堆/头骨图腾、蘑菇丛/苔藓、暗河/细水流、丢弃箱桶/破布 |
| underdark 卓尔 | 巨型天然石柱、细长石笋群/尖塔、蛛网/蛛丝、弧形塔楼/螺旋尖塔剪影、巨型蘑菇剪影、裂谷+石桥、紫罗兰灰矿脉 | 雕刻拱门/门廊、蛛形雕刻 |
| deep_underdark 灰矮人 | 挖空石笋的城郭剪影、棱角石厅/扶壁/台阶、跨裂谷石桥、矿道口/木框矿井、烟囱/通风塔群、铜灰/银灰矿脉 | 铁箍/铁门/锁链、暗湖边缘、矮人面雕像 |
### 3.3 通用排除
钟乳石/吊锥/悬挂物、砖石墙/规则砌缝/瓷砖/铺装、贴墙感、单件圆柱贴顶、光柱/光带/光源/发光物、火把/灯、角色/生物、文字/UI、紫/洋红。
**近景地面**:独立连续地面层,画法与验收见 §2.2,不并入背景。
---
## 4. 验收(机检 + 目检)
**三张背景变体 `scene_stage_bg_<board_type>`**
- 每张画布 = (基准屏宽, `stage_h`) 逐位相符(终稿);文件名带 `_dungeon` / `_underdark` / `_deep_underdark` 后缀。
- 顶部空区无钟乳石/吊锥/悬挂物,且无任何元素触碰画面上缘;远景为天然岩体,无砖石/规则砌缝/瓷砖/方整石块。
- 纵深分层:远/中/近三层可辨;远层剪影被中层部分遮挡、不孤立、不贴顶;无贴墙感、无单件圆柱/贴图直接贴顶;无连续地平线/地面;无孤立栅栏/栈道图标。
- 背景不含近景地面、地台、平台;底带只留暗部(结算期被明细带压住)。
- 无光柱/光带/光束/光晕,无光源实体(火把/灯/发光物);明暗只由岩体层理与暗部承载。
- 明度:全图取样点 L ∈ [0.014,0.060](世界底层下沿带,art-bible §2)。
- 无 300°–345° 色相;无角色/生物/道具/文字/UI;无完整凸形焦点(立绘 > 背景)。
- 背景不引用 `wall_ambient` tint;三张按 `board_type` 选图。
- 目检:三关色族与母题——dungeon 暖石棕+支洞/木栈道/木桩围栏;underdark 冷蓝灰+巨柱/石笋/蛛网/螺旋塔/巨菇/石桥;deep_underdark 灰黑+石笋城郭/棱角石厅/石桥/矿道口/烟囱塔;可选项不出现;三张可辨为不同洞窟,不是同一张图换色。
**共用连续地面层 `scene_stage_ground`**
- 画布 = (基准屏宽, `ground_h`)(终稿;`ground_h` 读布局表);文件名无 board_type 后缀。
- 地面为干净压实泥土为主,全幅只零星几颗小石、散点分布;无瓷砖/砖缝/规则铺装/石板块/草。
- 顶缘 = 不规则泥土边/阴影缝,只带 2–3 颗小石;不是直线亮边/踢脚线,也不是一排石子;顶缘以上键控后透明、无残留底色。
- 中央 1/2–2/3 宽平缓、无大石阻挡;无成排/成堆碎石、满铺砂砾、泥点噪声、垃圾杂物。
- 地面主体色 L ∈ [0.22,0.45] 的中低段(世界中层,art-bible §2;裂缝/阴影不按明度带判定;避免浅棕/亮面)。
- 三关共用、不吃 `wall_ambient`;无角色/生物/道具/文字/UI。
- 目检:地面读作泥土/碎石,不是瓷砖或石板铺装。
**参考与入包**:参考候选可带水印;入包终稿须无水印;四件终稿均按 §0 后处理与 `art-pipeline-tools` 的判据执行。
---
## 5. 待裁与待办(不阻塞提示词成文)
1. **三张背景按 `board_type` 选图**——游戏代码改动,走需求卡片+迭代;背景 `wall_ambient` tint 退役,棋盘/世界的该槽保留。
2. **`ground_h` 字段**——布局表新增字段,地面层实装批回填;本卡试片按构图比例,终稿读表。
3. **冷/灰色入板**——underdark / deep_underdark 需要的冷蓝灰、灰黑与矿脉色按 art-bible §4.1 战斗屏场景环境子板两道门入板;三张背景共用该子板 ≤32 色。
4. **地面层出图工艺**——极宽薄条;默认横向可平铺纹理条(左右无缝);若作者要整幅不拼接,另议。
5. **小石台 `scene_stage_plaza.png`**——先保留;是否退役归实装批/后续裁决。
6. **四柱光柱程序层退役**——光柱不迁入背景;背景资产入包后该程序层退役(代码批)。
7. **风格前缀**——首张 dungeon 背景试片后由作者确认,确认后才出另外两张与地面层。
8. **可选要素池**——按 §3.2 可选项本版关闭;要加走本卡修订。
---
## 6. 不在本卡
角色与敌种立绘、棋盘格面/墙面纹理、UI 图标、护甲图标、小石台 `scene_stage_plaza.png`、整屏样张与游戏代码接线;这些各归 art-prompts、脚本管线与实装批。
+26 -50
View File
@@ -1,9 +1,9 @@
# 营地场景 · 分层生图提示词卡 v1.2
# 营地场景 · 分层生图提示词卡 v1.0
> **性质**:营地场景图(1280×800,一张可点击场景图 + 十张分层:背景层 1 + 围墙层 1 + 八个物件层)的出图执行侧文件。**场景级资产,与角色级 [art-prompts](art-prompts.md) 并列,两文件互不覆盖**。
> **性质**:营地场景图(1280×800,一张可点击场景图 + 八个物件层)的出图执行侧文件。**场景级资产,与角色级 [art-prompts](art-prompts.md) 并列,两文件互不覆盖**。
> **依据**:色彩与氛围 = `reports/提案-营地色彩与氛围脚本-2026-10-23.md`(本卡不复述色值);坐标与层序 = `artwork/2026-10-23-营地场景/布局草案-1280x800.html`;物件命名与消费链 = [asset-path-map](asset-path-map.md);画法与交付形态 = [art-bible](art-bible.md) §6.2。冲突时色彩以色彩脚本为准,画法以 art-bible 为准。
> **边界**:只管营地场景图的分层出图。角色、图标、格面走 art-prompts.md 与脚本管线,不需要提示词。
> **建立**:2026-10-25 · 状态:十条提示词成文(背景层 1 + 围墙层 1 + 八个物件层),出图通道由作者自定
> **建立**:2026-10-25 · 状态:九条提示词成文,出图未开始(出图通道由作者自定)
---
@@ -11,10 +11,10 @@
1. **出图**:按下面各条把提示词贴进生图渠道。出图前先放**风格参考图**(见 §1)。
2. **入库**:候选原图放 `artwork/candidates/<资产名>/`,文件名 `v1.png / v2.png / v3.png`。
3. **后处理**:脚本管线代跑——去底转透明、降采样到终稿尺寸(整数倍)、色板量化、四联底测试条。**背景层不去底(全幅不透明)**;围墙层与八个物件层去底转透明。后处理工具清单与用法见 [art-pipeline-tools](art-pipeline-tools.md)。
3. **后处理**:脚本管线代跑——去底转透明、降采样到终稿尺寸(整数倍)、色板量化、四联底测试条。
4. **转正**:终稿转入切片资产目录,按坐标表接线。
**出图顺序(先锁后产,不能跳)**:概念脚本 → **本卡提示词** → 贴风格参考图**先出背景层 `scene_camp_bg` 1 张** → 作者确认风格前缀 → 才扩产围墙层 `scene_camp_wall` 与八个物件层(共 9 张)。
**出图顺序(先锁后产,不能跳)**:概念脚本 → **本卡提示词** → 贴风格参考图出背景层 → 作者确认风格前缀 → 才扩产八件。
---
@@ -37,40 +37,24 @@ Style reference: the attached images are this game's own finished pixel art and
---
## 2. 背景层与围墙层(两张,不可点)
围墙从背景层拆出、独立成层:围墙层可单独锁死桩高、门洞做成透明缺口透出背景层的小路;背景层不混入暖木色、更易守住冷色占比判据(判据见 §4)。两层都不可点;按乙工序门**先只出背景层 1 张**供作者确认风格前缀,确认后才扩产围墙层与八个物件层。
### 2.1 背景层(`scene_camp_bg`,不可点,不透明全幅)
## 2. 背景层(`scene_camp_bg`,不可点,不透明全幅)
**出图画布**:2560×1600(终稿 1280×800 的 2 倍,保证整数倍降采样)
**画什么**:天空、远山、整片地面、门洞位置一条通向远处的小路
**不画什么**:木围墙、门柱、门扇、七个物件、角色、接地阴影——围墙与门柱是 §2.2 的独立层,门洞是它的透明缺口;小路在本层画好、透出来即可
**画什么**:天空、远山、地面、木围墙、门柱与门洞
**不画什么**:七个物件、门扇、角色、接地阴影——那些是上面的独立图层
```
Pixel art style. True pixel art game background layer drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about 24 colors, retro 16-bit look, Aseprite style, all ages, flat side view, orthographic, no perspective distortion. Wide horizontal composition, 16:10 aspect ratio. This is the FAR BACKGROUND LAYER ONLY of a small adventurer camp: sky, distant hills and the whole ground. It must contain no palisade wall, no gate posts, no camp objects, no gate leaves, no characters and no drop shadows, because all of those are drawn as separate transparent layers on top of this one. COLOR SCRIPT — cool recedes, warm advances: the sky, the hills and the ground are all COOL, pale, desaturated and low in contrast; there is no warm accent anywhere in this layer, warm color belongs only to the separate object layers drawn on top. ATMOSPHERIC DEPTH: every plane that is farther away is lighter, closer to the value of the sky, cooler, lower in contrast and simpler in detail, while the nearest plane is the darkest and highest in contrast. SKY (top of frame): the lightest thing in the picture, flat pale cool off-white with a faint blue-grey tint, no yellow or cream tone, no sun anywhere; the very top strip of the frame, about one sixteenth of the frame height, stays completely calm and empty, reserved as clear space with nothing in it; at most one or two extremely simple flat low-contrast clouds. FAR DISTANCE: two or three layers of low rolling hill silhouettes in pale cool blue-grey, each farther layer lighter, cooler and lower in contrast than the one in front, fading towards the value of the sky, no trees, no individual foliage. HILLS TO GROUND: the hills fade gradually and smoothly down into the ground, with NO visible horizon line and NO distant ground band — the transition must not read as a separate horizontal stripe of a different color. GATE PATH: in the middle of the frame, between x=1040 and x=1520, one clearly visible dirt path runs from the ground at two thirds of the frame height measured from the bottom edge away into the distance between the hills, narrowing as it recedes, painted in a muted desaturated grey-brown earth tone, never golden or warm yellow; this path shows through the empty gate opening of the wall layer above. FOREGROUND GROUND: the darkest area of the picture, dry packed bare earth in a neutral desaturated grey-brown, never golden, amber or warm yellow, hard flat color blocks, hard edges, no gradient, no grass, no grass tufts, no weeds and no plants of any kind. The foreground ground is completely empty apart from the dirt path: no objects, no icons, no badges, no markers, no decorations of any kind. Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; black or heavy dark outlines; drop shadows, contact shadows or any shadow band; blur or compression artifacts; a vignette, dark corners, or any mood darkening; night, dusk, moon, stars, sunset or sunrise light; a sun or any light source drawn in the sky; a visible horizon line, a separate horizontal distant ground band or any distant grass strip below the hills; trees, forests or foliage in the distance; grass, grass tufts, weeds or plants in the foreground; a palisade wall, wooden fence, fence posts, gate posts or any wall structure; any warm, golden, amber or yellow cast on the sky, on the hills or on the ground; any camp object, furniture, crate, barrel, fire, tent or tool; any gate leaf, door panel or hinge; any purple or magenta anywhere; any text, letters, numbers, watermark, signature or logo; any frame, border, panel, arrow or UI element; any icon, badge, item tile, emblem, sign, marker or symbol anywhere in the frame; any characters, people, creatures or animals.
Pixel art style. True pixel art game background layer drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about 24 colors, retro 16-bit look, Aseprite style, all ages, flat side view, orthographic, no perspective distortion. Wide horizontal composition, 16:10 aspect ratio. This is the BACKGROUND LAYER ONLY of a small adventurer camp: sky, distant hills, ground and the wooden palisade wall around it. It must contain no camp objects, no gate leaves, no characters and no drop shadows, because all of those are drawn as separate transparent layers on top of this one. COLOR SCRIPT — cool recedes, warm advances: the sky, the hills, the ground and the wall are all COOL, pale, desaturated and low in contrast; there is no warm accent anywhere in this layer, warm color belongs only to the separate object layers drawn on top. ATMOSPHERIC DEPTH: every plane that is farther away is lighter, closer to the value of the sky, cooler, lower in contrast and simpler in detail, while the nearest plane is the darkest and highest in contrast. SKY (top of frame): the lightest thing in the picture, flat pale cool off-white with a faint blue-grey tint, no yellow or cream tone, no sun anywhere; the very top strip of the frame, about one sixteenth of the frame height, stays completely calm and empty because a HUD bar is drawn over it; at most one or two extremely simple flat low-contrast clouds. FAR DISTANCE: two or three layers of low rolling hill silhouettes in pale cool blue-grey, each farther layer lighter, cooler and lower in contrast than the one in front, fading towards the value of the sky, no trees, no individual foliage; the foot of the hills is cut off and hidden behind the wall, so there is no visible horizon line and no distant ground band between the hills and the wall. MIDDLE GROUND: the base line where the logs meet the ground sits at two thirds of the frame height measured from the bottom edge; at that height runs a wooden palisade wall across the whole width of the frame, built from closely spaced vertical logs standing side by side with no gaps between them, every log cut to a sharp point at the top, a solid barrier that completely blocks the view of the outside — no horizontal rails, no see-through gaps, no ranch-style post-and-rail fence; the wall is painted in a muted, greyed wood brown. In the middle of the wall one wide gap holds two thick gate posts slightly taller than the wall, and between them an opening showing a dirt path running away into the distance; the gate opening itself stays empty, because the two gate leaves are a separate layer. FOREGROUND GROUND: the darkest area of the picture, dry packed bare earth in a neutral desaturated grey-brown, never golden, amber or warm yellow, hard flat color blocks, hard edges, no gradient, no grass, no grass tufts, no weeds and no plants of any kind; the ground meets the foot of the wall cleanly with no shadow band and no gradient. Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; black or heavy dark outlines; drop shadows, contact shadows or any shadow band; blur or compression artifacts; a vignette, dark corners, or any mood darkening; night, dusk, moon, stars, sunset or sunrise light; a sun or any light source drawn in the sky; a visible horizon line, a distant ground band or any distant grass strip below the hills; trees, forests or foliage in the distance; grass, grass tufts, weeds or plants in the foreground; a ranch-style post-and-rail fence, a see-through fence, flat-topped posts or horizontal rails; any warm, golden, amber or yellow cast on the sky, on the hills or on the ground; any camp object, furniture, crate, barrel, fire, tent or tool; any gate leaf, door panel or hinge; any purple or magenta anywhere; any text, letters, numbers, watermark, signature or logo; any frame, border, panel, arrow or UI element; any characters, people, creatures or animals.
```
### 2.2 围墙层(`scene_camp_wall`,不可点,透明底)
**出图画布**:2560×1600(与背景层同画布,门洞 x 坐标直接对齐);墙横贯画在出图 y480–620 这条**窄带**内,其余为纯色底 `#F2F2F2`。落盘由 S4 后处理裁成终稿外接矩形 `(0, 240, 1280, 70)` / 出图 `(0, 480, 2560, 140)` 的细条,**不做全幅 1280×800 落盘**(全幅约 3.9 MB 显存,细条约 0.35 MB)。
**画什么**:横贯全宽的密排竖木桩墙(左右两段)+ 两根略高的门柱,门洞处完全透明
**不画什么**:天空、远山、地面、小路、门扇、七个物件、角色;门洞内不画任何东西,透出背景层的小路
```
Pixel art style. True pixel art game layer drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about 24 colors, retro 16-bit look, Aseprite style, all ages, flat side view, orthographic, no perspective distortion. Wide horizontal composition, 16:10 aspect ratio. This is the PALISADE WALL LAYER ONLY of a small adventurer camp, drawn on a plain flat uniform light grey #F2F2F2 background for easy keying. A wooden palisade wall runs straight across the full width of the frame, seen from the front, built from closely spaced vertical logs standing side by side with no gaps between them, every log cut to a sharp point at the top, a solid barrier with no horizontal rails, no see-through gaps, no ranch-style post-and-rail fence. The wall is SHORT and LOW: including the pointed tips the whole band occupies only about one eighteenth of the total frame height, a thin horizontal strip, NOT a tall barrier; the logs stand only about one eighteenth of the total frame height above their base line. In the middle of the wall one wide gap holds two thick gate posts slightly taller than the wall, and the entire opening between them is completely EMPTY — plain flat uniform light grey #F2F2F2 showing through, no logs, no path, no ground, no scenery inside the opening, because the dirt path and the hills belong to the background layer below and the gate leaves are a separate object layer. The wall is painted in a muted greyed wood brown, with a thin warm cream outline around the whole silhouette, flat shading. Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; black or heavy dark outlines; drop shadows or contact shadows; blur or compression artifacts; a vignette or dark corners; any sky, hills, ground, grass, dirt path or scenery of any kind; any gate leaf, door panel or hinge; any camp object, furniture, crate, barrel, fire, tent or tool; any characters, people, creatures or animals; any purple or magenta anywhere; any text, letters, numbers, watermark, signature or logo; any frame, border, panel, arrow or UI element.
```
### 2.3 出图画布上的坐标(终稿 ×2)
### 出图画布上的坐标(终稿 ×2)
| 元素 | 终稿 1280×800 | 出图画布 2560×1600 |
|---|---|---|
| 顶栏 HUD 保留带 | y ≤ 48 | **y ≤ 96**(保持空旷) |
| 背景层 `scene_camp_bg` | 全幅 1280×800 | 全幅 2560×1600 |
| 围墙带(wall 层桩身) | y 267–310(**高 43px**) | y 534–620(**高 86px**) |
| 门柱(wall 层,略高) | y 240–310;x 500–520 / 760–780 | y 480–620;x 1000–1040 / 1520–1560 |
| 门洞(wall 层透明缺口) | x 520–760 | x 1040–1520 |
| 门洞内小路(画在 bg 层) | x 520–760,自 y310 向上收窄至远山 | x 1040–1520,自 y620 向上收窄至远山 |
| 围墙带 | y 267–310 | **y 534–620** |
| 门洞(必须留空) | x 520–760 | **x 1040–1520** |
| 门柱 | x 500–520 / 760–780 | **x 1000–1040 / 1520–1560** |
---
@@ -78,7 +62,7 @@ Pixel art style. True pixel art game layer drawn with chunky square pixels, stri
### 组装方式(不要逐条重复前缀)
每件提示词 = **风格前缀** + **本件正文** + **否定后缀**。前缀与后缀在**本节单点定义**,**八件共用**,避免同一段话八处维护。背景层与围墙层用各自的完整提示词(§2.1/§2.2),不用本节的物件前缀。
每件提示词 = **风格前缀** + **本件正文** + **否定后缀**。前缀与后缀在**本节单点定义**,九张共用,避免同一段话九处维护。
**风格前缀**(八件共用,照抄):
@@ -94,16 +78,16 @@ Do not add any of the following: realistic, photorealistic or 3D rendering; pain
### 逐件正文(只替换这一段)
| 件 | z 序 | 终稿矩形 | 出图画布 | 正文 |
| 件 | 层 | 终稿矩形 | 出图画布 | 正文 |
|---|---|---|---|---|
| 布告板 | 2 | 130×140 | 260×280 | §3.1 |
| 门扇 | 3 | 240×220 | 480×440 | §3.2 |
| 木桩 | 4 | 120×180 | 240×360 | §3.3 |
| 锻炉·铁砧 | 4 | 240×240 | 480×480 | §3.4 |
| 帐篷 | 4(不可点) | 300×170 | 600×340 | §3.5 |
| 宝箱 | 5 | 130×90 | 260×180 | §3.6 |
| 笔记本 | 5 | 100×80 | 200×160 | §3.7 |
| 篝火 | 6 | 160×160 | 320×320 | §3.8 |
| 布告板 | L1 | 130×140 | 260×280 | §3.1 |
| 门扇 | L2 | 240×220 | 480×440 | §3.2 |
| 木桩 | L3 | 120×180 | 240×360 | §3.3 |
| 锻炉·铁砧 | L4 | 240×240 | 480×480 | §3.4 |
| 帐篷 | L5(不可点) | 300×170 | 600×340 | §3.5 |
| 宝箱 | L6 | 130×90 | 260×180 | §3.6 |
| 笔记本 | L7 | 100×80 | 200×160 | §3.7 |
| 篝火 | L8 | 160×160 | 320×320 | §3.8 |
**3.1 布告板(→ 图鉴)**
```
@@ -149,18 +133,10 @@ A campfire made of a ring of grey stones around crossed stacked logs with a warm
## 4. 验收(机检 + 目检)
**背景层 `scene_camp_bg`**
**背景层**
- 黄度 =(R+G)/2−B 分区取样:天空 ≤20、远山 ≤20、地面 ≤35
- 冷色像素占比 ≥55%;本层不应出现任何暖色高饱和像素
- 目检:顶部约 1/16 完全空旷;**无地平线、无远景地面带**(远山自然渐隐过渡到地面);不含围墙、门柱、门扇、物件、角色
- 目检/机检:门洞 x1040–1520 处**有一条清晰可辨、向远处收窄的土路**(非纯色块),透出「出去即冒险」
- 机检/目检:全幅**无任何独立物件/图标/徽章/标记**(前景地面除小路外全空);参考做法=中下方块区独立色数应≈邻区(旧口径 v1 该区仅 179 色)
**围墙层 `scene_camp_wall`**
- 透明底 `#F2F2F2` 可去底、奶油描边闭合
- **桩身高度 = 出图 y534–620(终稿 43px);门柱略高 y480–620。桩身超出围墙带下沿 y620 达 5px 以上即判红**
- 目检:密排竖木桩、桩顶削尖、不透缝、无横木;门洞 x1040–1520 **完全透明**(透出 bg 小路)、无门扇
- 单张色数 ≤32(量化后)、缩放为整数倍无拉伸痕迹
- 目检:顶部约 1/16 完全空旷;无地平线;围墙密排尖桩、桩顶削尖、不透缝;门洞空着没有门扇
**八件**
- 单件居中、四周留边、整件完整不出画
@@ -174,7 +150,7 @@ A campfire made of a ring of grey stones around crossed stacked logs with a warm
## 5. 待裁(不阻塞出图,阻塞坐标表与定稿)
1. **帐篷尺寸**:现 300×170 待重定,重定后按比例重出
2. **门扇高宽**:现 240×220,围墙带只有 43px 高;围墙独立成层后门扇叠在门洞上,两者高宽关系待裁
2. **门扇高宽**:现 240×220,围墙带只有 43px 高,比例待裁
3. **布告板位置**:现 y330 悬空在围墙下方,建议 `(845, 268, 130, 140)` 挂回围墙带
4. **描边颜色追认**:本卡按奶油色(去底技术前提);若改参照物的深灰褐,需换去底方案
5. **营地环境子板色值**:art-bible §4.1 为数值单点,色值待作者拍板,本卡不复述
+14 -14
View File
@@ -85,7 +85,7 @@
| 模块 | 独占职责 | 对外暴露 | 消费依赖 | 引擎 API |
|---|---|---|---|---|
| **ConfigDB**(autoload,D2) | 全部玩法数值表与表现层布局配置表(`data/tables/*.tres` + `data/schemas/*.gd`);SCHEMA_VERSION;加载/校验/override/诊断状态机。**行族访问器归位各 schema 表类(表管自己的行查询),本类只留加载/校验/override/诊断门面;表持有成员按域收进四张域容器 Resource(`config_domain_combat/run/crossrun/presentation.gd`——前三件迭代 41 已建件,第四件表现层布局域 2026-10-30 建件,类型化成员 7/7/5/1);三张 resolver 表(board_gen.resolve_row/enemy.resolve_rowset/difficulty.resolve_modifiers)经 `load_tables` 装配段注入各容器,resolver 文件与键零搬移**——红线不变:本类仍是唯一加载与持有单点,"经 ConfigDB 单点读取"=经其持有的表对象 | 类型化域容器属性(每域一张容器);公开面 8(push_diag/is_fatal/reset_diagnostics/diagnostics/diagnostics_count/load_tables/set_reload_gate/reload)、类变量 10 | 无(基础层顶点) | Resource/.tres 加载、autoload 生命周期——LOW |
| **ConfigDB**(autoload,D2) | 全部玩法数值表(`data/tables/*.tres` + `data/schemas/*.gd`);SCHEMA_VERSION;加载/校验/override/诊断状态机。**行族访问器归位各 schema 表类(表管自己的行查询),本类只留加载/校验/override/诊断门面;表持有成员按域收进三张域容器 Resource(`config_domain_combat/run/crossrun.gd`——已建件(迭代 41),类型化成员 7/7/5);三张 resolver 表(board_gen.resolve_row/enemy.resolve_rowset/difficulty.resolve_modifiers)经 `load_tables` 装配段注入各容器,resolver 文件与键零搬移**——红线不变:本类仍是唯一加载与持有单点,"经 ConfigDB 单点读取"=经其持有的表对象 | 类型化域容器属性(每域一张容器);公开面 8(push_diag/is_fatal/reset_diagnostics/diagnostics/diagnostics_count/load_tables/set_reload_gate/reload)、类变量 9 | 无(基础层顶点) | Resource/.tres 加载、autoload 生命周期——LOW |
| **SlotStore**(`core/slot_store.gd`,D3 存档槽位域件,已建件——迭代 41 存量债批) | 存档三槽位文件域唯一所有者(自 save_manager 收编槽位族):槽位挂载与模板、激活与建删、槽位摘要窥视(peek 族);改名覆盖原语(_rename_overwrite)留 save_manager(IO 原语单点) | 槽位读写原语族+摘要投影(公开 10/类变量 4;SaveManager 与菜单消费方经 `save.slots.` 直连) | save_manager(注入路径与模板配置);FileAccess——LOW |
| **MetaStateStore**(`core/meta_state_store.gd`,D3 存档内元进度数据件,已建件——迭代 41 存量债批) | 存档内元进度数据的读写唯一层(自 save_manager 收编 accessor 族:钥石/道具解锁/树节点与叉侧/锻造路线/同调/真视/计数器/摘要——数据读写原语,不含业务规则);save_manager 与 meta_progression 双 God Class 的共同病根=该数据域摊在两文件,本件即其归一 | 写口原语 13+只读投影 6(tree_state/forge_state/attune_state/career_state 四投影+keystone_balance/item_unlocked)=公开 19(add_keystone 私有 `_add_keystone`),零 _data 副本零缓存;MetaProgression 业务层与营地/锻造 UI 消费方经 `save.meta_state.` 直连(meta 的纯转发读方法已退役) | save_manager(序列化接线);FileAccess——LOW |
| **RngService**(autoload,D2) | master_seed、各流 scope 计数器(封闭流词表现值以 `RngServiceCore.PURPOSE_TAGS` 为准;增流走 P1 评审) | `set_master_seed()`(唯一调用方 C2);`open_stream(stream)` 有状态门面;`derive_at(stream, scope)` 纯函数;只读计数器 | ConfigDB(无) | 无——纯逻辑 |
@@ -110,7 +110,7 @@
| **HeroLedger**(`core/hero_ledger.gd`,冒险域主角实体账,已实装(2026-10-27 迭代 39 拆件批)) | 冒险生命周期主角数值与获得记录唯一所有者:生命与 GP 钱包(gain/spend/clear 单写原语,钱包事件发射点归件)、锻造强化快照、调试百分比、攻击/甲类型轴快照、特性持有集(活引用注入——关内兑付当关生效)、本局钥石增量与逐杀明细、真视使用记录;获得记录三层结构=获得记录列表(三选一件/商店购买/战末奖励各一条)→属性增益明细(带来源指回)→四轴汇总(由明细推导);生效上限与回血封顶公式单点归件 | 构造注入;单写原语族+只读投影;断点自交片段(账本键+获得日记键) | ConfigDB(表值读取);由 AdventureRunner 冒险开始创建、注入 CurioStop/level_runner/ShopSession/战斗 encounter | 无(RefCounted 纯逻辑) |
| **AttuneLoadout**(`core/attune_loadout.gd`,同调配装件,已实装(2026-10-27 迭代 39 拆件批)) | 装备中的秘宝件对象集唯一所有者(每件内含充能单位与重置周期);同调充能的物化行为唯一事实源——种行/补行改写时同步(RunLedger 充能字典轨与差额对账函数退役);旧档 aux_items 同调行迁移归件 | 构造注入;充能查询/消耗/按周期重置原语;断点自交片段(attunement_charges 键,键名与策略不变) | ConfigDB(秘宝行集);TreasureItems(同调行投放面);注入战斗使用路径 | 无 |
| **CurioStop**(`core/curio_stop.gd`,三选一停点件,已实装(2026-10-27 迭代 39 拆件批)) | 三选一停点编排唯一所有者(架构原则 9 本域承载):停点三态(关卡完成三选一在途/已兑付待推进/奖励节点三选一在途)、刷新三源账(刷新件 X/营地池 Y/刷新券存量/付费刷新计数)、兑付方法族(pick/refresh/redeem/skip)、凭证出箱(RAP/时沙待注入);三选一信号面自带;零自动推进停点语义承载 | 对外=开停点/刷新/兑付/放行谓词/交断点片段五组入口+信号面(curio_choice_presented/curio_resolved);由 AdventureRunner 关卡完成受理时调用 | ConfigDB;注入 CurioDeck(抽签/摘牌)与 HeroLedger(落账);随机只经 CurioDeck 的 reward_pick 封闭流 | 无 |
| **breakpoint_store**(`core/breakpoint_store.gd`,冒险域·断点组装与回放件,已建件——迭代 45) | 存档断点组装与回放唯一承载(自 adventure_runner 收编 6 成员+函数族):断点快照拼盘、回放回填、读侧策略分发;`BREAKPOINT_FIELDS` 27 键登记单源随件(键名/策略标签/键序冻结) | 构造=引用束注入(九引用束成员解包为原名成员——保迁移函数体逐字);`resume_adventure` 留宿主(壳+宿主侧回填语句——R2 所有权缝分家);断点键面零变化;实测类变量 15(贴顶禁再增) | adventure_runner(宿主);注入主角账三件与 curio/stop/bank/loadout 引用 | 无(RefCounted 纯逻辑) |
| **breakpoint_store**(`core/breakpoint_store.gd`,冒险域·断点组装与回放件,2026-10-29 零豁免裁设计闭合——实现批建件) | 存档断点组装与回放唯一承载(自 adventure_runner 收编 6 成员+函数族):断点快照拼盘、回放回填、读侧策略分发;`BREAKPOINT_FIELDS` 27 键登记单源随件(键名/策略标签/键序冻结) | 构造=引用束注入;`resume_adventure` 留宿主薄壳;断点键面零变化 | adventure_runner(宿主);注入主角账三件与 curio/stop/bank/loadout 引用 | 无(RefCounted 纯逻辑) |
### 功能层
@@ -121,8 +121,8 @@
| **RewardGrant**(`core/reward_grant.gd`,B1 域·奖励授予件,2026-10-26 编排域拆件设计定稿——实现批建件) | 奖励授予唯一兑付管线(自 battle_orchestrator 收编 grant 族):极性字典路由与封印溢价兑付(_grant_routed)、三种行应用(flat/percent/gp)、伤害单据提交面(_submit 族)、清图奖励与身份授予、秘宝使用编排(use_treasure 四步)、战斗域 GP 缓冲 escrow 与授予段暂存伤害闸(_defer_board_damage/_deferred_board) | use_treasure 编排入口(treasure_interact_controller 经组合根现值回调直连);授予入口(结算段类直调);只读投影(escrow 读与 GP 掉落合计——宿主呈现快照与 _roll_gp_drop 消费) | B4 RewardEconomy(polarity_row/grant);B1 域 DamageController(开单);BattleFxHost 引用(treasure_used 事件发射与计数器消耗);A2 minesweeper(校验回调/fx_reveal/remove_mines_in);A3 action_economy(相位闸/grant_ap/queue_ap_delta);B3 status_container(apply_effect/池读);B2 unit_container(hero_unit/heal);ConfigDB;注入 emit Callable、宿主 fatal Callable(battle_result 状态机唯一写点留宿主)、宿主 attune 读投影(_attune_safe_seen)、encounter 账本引用;TreasureItems 经宿主 setter 转发注入(库存注入时序在宿主构造之后) | 无(零随机零 await——授予期零随机口径,roll 值授予前已定) |
| **damage_controller**(B1 域·伤害单据管线) | 伤害单据唯一兑现管线(单据九栏目声明→分量掷点/克制修正/伤害下限归一→明细行发射;双方同规) | 兑付入口(编排侧开单)+ fx 揭示回调注入位 | B2 单位实例(单据目标传入);B1 注入(EffectContext 引用束与编排回调) | 无 |
| **BoardInstaller**(`core/board_installer.gd`,B1 域·装板域件,2026-10-29 尾波清偿批设计闭合——实现批建件,细则见「战斗编排域拆件定稿」节轴丙) | 棋盘安装与变异布置域唯一承载(自 battle_orchestrator 收编装板族六函数):生成受理、全局作用域变异决议、资源摇号资格域组装(roll_board 恰一次)、region 变异决议与连通片布置;BoardGenerator 实例随件自建自接线(宿主成员退役)——宿主侧装板后状态复位(封印/完美计数清零、板序推进、board_changed 发射、秘宝叠加层退役)留宿主薄壳,时序逐字保持 | `install(during_round) -> bool` 单入口;构造=引用束注入(rng/db/minesweeper/reward_economy/encounter 只读引用+宿主 fatal Callable——防 _init max-parameters 新键) | A1 board_generator(件内持有);A2 install_board;B4 roll_board/board_skew 写;A4 变异决议行集经 ConfigDB;board_gen/variant_place 两封闭流随件、流名与开流时点零漂移 | 无(零 await——装板全同步) |
| **RoundCycle**(`core/round_cycle.gd`,B1 域·轮循环驱动件,已建件——迭代 44) | 轮循环驱动域唯一承载(自宿主收编 7 成员):结算五段段件持有与驱动、重入门闸、开轮摇号、踩雷账 | 三参直构(host/fx_host_ref/reward_grant_ref——五段构造透传不落成员);段驱动入口由宿主接线调用;实测类变量 8/公开方法 3 | B1 宿主;settlement/ 五段段类;A3 相位读 | 无 |
| **BattleFacts**(`core/battle_facts.gd`,B1 域·战斗事实件,已建件——迭代 44) | 战斗事实域唯一承载(自宿主收编 14 成员):受理事实(encounter)、轮 facts 缓冲(reveals/seals/path)、封印判定账、事实溯源、同调快照读面;attune 状态两成员随件(行为函数留宿主) | 零参直构;宿主与段类经注入引用读写;实测类变量 15(贴顶)/公开方法 4(reset_round+三 mark 写口) | B1 宿主;A2/A3 只读 | 无 |
| **RoundCycle**(`core/round_cycle.gd`,B1 域·轮循环驱动件,2026-10-29 设计闭合——实现批建件) | 轮循环驱动域唯一承载(自宿主收编 7 成员):结算五段段件持有与驱动、重入门闸、开轮摇号、踩雷账 | 构造=引用束注入;段驱动入口由宿主接线调用 | B1 宿主;settlement/ 五段段类;A3 相位读 | 无 |
| **BattleFacts**(`core/battle_facts.gd`,B1 域·战斗事实件,2026-10-29 设计闭合——实现批建件) | 战斗事实域唯一承载(自宿主收编 14 成员):受理事实(encounter)、轮 facts 缓冲(reveals/seals/path)、封印判定账、事实溯源、同调快照读面;attune 状态两成员随件(行为函数留宿主) | 构造=引用束注入;宿主与段类经注入引用读写 | B1 宿主;A2/A3 只读 | 无 |
| **settlement/ 五段段类**(B1 域·结算段件) | B1 每轮结算序列的五段逻辑件(授予/增益减益/战斗/衰减/判定——细则见下「战斗域装配现状」节) | 段处理入口(B1 接线调用);可直接 `new()` 单测 | B2/B3/B4 兑付面与 B1 轮 facts 缓冲(各段按职责) | 无 |
| **level_runner**(C1-lite,切片已建) | 关卡域生命周期(节点链推进、共享 B4 + HP 携带、关卡末 level_clear) | 节点推进接口;LevelTable(`level.tres`——节点 enum {battle,reward,rest,boss}、末节点必须 Boss、ConfigDB 跨表校验⑧) | B1(直构 BattleOrchestrator 接线战斗)、ConfigDB LevelTable | 无 |
| **run_structure**(C2) | 冒险/关卡序列、失败重开、**master_seed 唯一接线方** | (契约规范源:`../../gdd/systems/冒险循环/冒险结构.md`) | RngService.set_master_seed、C3/D2 | 无 |
@@ -164,15 +164,15 @@
|---|---|---|---|---|
| **D1 HUD**(呈现面拆分持续换代:组合根 main.gd + 三屏壳(MenuScreen/CampScreen/AdventureScreen)+ views/ 视图单元(现行承载面见「呈现层装配与会话生命周期」节;早期 9 单元清单与 LevelStripView 已随场景换代退役);营地覆盖层=四柜台并排〔树淬炼主界面/锻造工坊·秘宝匣钥匙门/图鉴常开——迭代 21 T6 程序占位重排〕) | 全部 Control/CanvasLayer(例外:BoardView 棋盘本体与 BattleStageView 站桩本体 = Control 壳 + SubViewportContainer + SubViewport + Node2D 世界〔BoardWorld/CellUnit;StageWorld/StandeeBody——B 区 slot_row 伴随件留壳层〕,探针契约面不变——棋盘 Node2D 迁移批 Phase A 2026-09-23 / 战斗舞台 Phase B 2026-09-26)、输入→意图翻译(**唯一提交路径 = settle_batch,不直调 A3**)、呈现义务状态记录(A2×6 / B2×4 / B3×4 / B4×5 / B1×3) | 视图单元接口(信号外发、setter 只读注入) | A2 compute_move_path / ActionOutcome;A3 ap/rap;B1 步骤流行协议(数据源);ConfigDB display_name | **MEDIUM**:Control / CanvasLayer / draw_* / InputEvent / RichTextLabel——对照 breaking-changes.md 4.5–4.7 条目(CanvasItem 线条无抗锯齿羽化、设备 ID 常量、RichTextLabel 图片参数两轮变化) |
| **组合根拆件**(呈现侧写口子件:curio_interact_controller / camp_shop_presenter / treasure_interact_controller) | 代组合根执行核心写调用(冒险节点后续推进/营地购买与刷新/道具使用——依赖与回调由 main 构造注入) | 各拆件的写入口(仅组合根装配面可达;视图零直连) | 构造注入的核心写引用(推进/商店/道具各域——详见各拆件头注) | 无 |
| **ui_registry**(`presentation/ui_registry.gd`,界面注册面,已建件——迭代 44) | 呈现层接口面唯一承载:层序合同(LAYER_CONTRACT)迁驻与合同寻址、三域句柄容器 ui_screens(9 屏壳与覆盖层句柄)/ui_battle(5 战区句柄)/ui_modals(13 模态句柄+ModalFlow);组合根经其一级寻址接线,探针挂载假设迁移为 `main.ui.<域容器>.<句柄>`;七个探针别名/转发成员随批消灭 | 三容器属性+合同寻址口(RefCounted 门面,不挂树、零会话期引用);四件(UiRegistry/UiScreens/UiBattle/UiModals)类变量 3/9/5/14,ModalFlow 随 UiModals 容器构造即建 | 构造注入:三屏壳、覆盖层、战区句柄、ModalFlow(main_ui_build 交付随之改型) | 无(RefCounted) |
| **board_hover**(`views/board_hover.gd`,悬停预承诺呈现域件,已建件——迭代 45) | 悬停反馈域唯一承载(自 board_view 收编 11 函数族+_hover/_last_click 两成员):三语义光标、目标格边框、BFS 路径预览、chord 双击首遇锚;_r0 出生展开区成员留宿主 | 构造=引用束注入实为 7 项(cells/minesweeper/diggable 引用+格命中换算/光标写入/hover_forbidden/chord_attempted 四 Callable——卡片预估 5 项,11 函数逐字迁移的最小必需面);三入站口留宿主委托;hover_probe 读面留宿主 | board_view(宿主);输入事件经宿主入站口 | 无(RefCounted) |
| **cell_base_layer/cell_mark_overlay**(`views/`,单格两绘制域件,已建件——迭代 45) | 单格绘制域拆分(自 board_cell_unit 状态件 `_CellPaintState` 收编,入参与该层绘制命令同件):基座层(底面/描边/立面/投影/接触带/倒角,12 成员)与标识叠加层(标记/字形/徽章/封印/点迹/高亮/退色,13 成员);宿主 _draw 退为六步时序器,类变量 30→10 | 构造注入;绘制命令由宿主 _draw 按层序调用 | board_cell_unit(宿主);gdstyle 键各文件独立计数(12/13 均 ≤15) | 无(RefCounted) |
| **standee_vitals/standee_motion**(`views/`,站桩两状态件,已建件——迭代 45) | 站桩状态域拆分(自 `_StandeeState` 真实 25 成员收编):身份命值(hp/atk/状态值族,12 成员)与资产演出(动作/姿态/位移演出族,11 成员);三个演出探针计数器留宿主,宿主类变量 19→10 | 构造注入;st(沿用)/mo 两实例由宿主持有 | standee_body(宿主);battle_stage_view 写入面逐字保持 | 无(RefCounted) |
| **ui_registry**(`presentation/ui_registry.gd`,界面注册面,2026-10-29 零豁免裁设计闭合——实现批建件) | 呈现层接口面唯一承载:层序合同(LAYER_CONTRACT)迁驻与合同寻址、三域句柄容器 ui_screens(9 屏壳与覆盖层句柄)/ui_battle(5 战区句柄)/ui_modals(13 模态句柄+ModalFlow);组合根经其一级寻址接线,探针挂载假设迁移为 `main.ui.<域容器>.<句柄>`;七个探针别名/转发成员随批消灭 | 三容器属性+合同寻址口(RefCounted 门面,不挂树、零会话期引用) | 构造注入:三屏壳、覆盖层、战区句柄、ModalFlow(main_ui_build 交付随之改型) | 无(RefCounted) |
| **board_hover**(`views/board_hover.gd`,悬停预承诺呈现域件,尾波清偿批 D2 设计闭合——实现批建件) | 悬停反馈域唯一承载(自 board_view 收编 11 函数族+_hover/_last_click 两成员):三语义光标、目标格边框、BFS 路径预览、chord 双击首遇锚;_r0 出生展开区成员留宿主 | 构造=引用束注入(cells/minesweeper/diggable 引用+光标写入与 hover_forbidden 注入回调);三入站口留宿主委托;hover_probe 读面留宿主 | board_view(宿主);输入事件经宿主入站口 | 无(RefCounted) |
| **cell_base_layer/cell_mark_overlay**(`views/`,单格两绘制域件,2026-10-29 设计闭合——实现批建件) | 单格绘制域拆分(自 board_cell_unit 状态件 `_CellPaintState` 收编,入参与该层绘制命令同件):基座层(底面/描边/立面/投影/接触带/倒角,12 成员)与标识叠加层(标记/字形/徽章/封印/点迹/高亮/退色,13 成员);宿主 _draw 退为六步时序器,类变量 30→10 | 构造注入;绘制命令由宿主 _draw 按层序调用 | board_cell_unit(宿主);gdstyle 键各文件独立计数(12/13 均 ≤15) | 无(RefCounted) |
| **standee_vitals/standee_motion**(`views/`,站桩两状态件,2026-10-29 设计闭合——实现批建件) | 站桩状态域拆分(自 `_StandeeState` 真实 25 成员收编):身份命值(hp/atk/状态值族,12 成员)与资产演出(动作/姿态/位移演出族,11 成员);三个演出探针计数器留宿主,宿主类变量 19→10 | 构造注入;st(沿用)/mo 两实例由宿主持有 | standee_body(宿主);battle_stage_view 写入面逐字保持 | 无(RefCounted) |
| **HudPresenter**(`presentation/hud_presenter.gd`,迭代 37 加注) | 战斗屏**数据投影+文案拼装静态族**——核心状态→呈现文案的全部翻译集中于此(B/C/D 区文案与投影),静态无状态 | `refresh(…)` 全量重查装配(组合根每次动作/结算返回后调用,漏调即陈旧呈现)+只读静态投影族 | 核心读取只走 `settlement.presentation_snapshot()` 成文快照面(禁链式穿透结算内部);ConfigDB display_name | 无(RefCounted 静态) |
| **board_resolvers**(`presentation/board_resolvers.gd`,迭代 37 拆出) | 棋盘每格渲染数据的只读解析回调四函数(奖励徽章身份——挖掘期 Active 相位内、地形变异数字遮蔽与角标、调试加成生效值)——有态绑定件,与无态静态投影族 HudPresenter 职责互斥 | 构造注入 db、调试浮窗与结算活态取值,产出绑定 Callable 供组合根接线 | ConfigDB / DebugOverlay / 结算活态取值 Callable | 无(纯投影) |
| **main_ui_build**(`presentation/main_ui_build.gd`,迭代 37 拆出) | `_build_ui` 八步拆分件——组合根 UI 装配分步承接(三屏壳实例化、视图句柄接线、覆盖层装配与模态编排注入按步拆开);main 侧留同名薄委托,装配序与 z 序零改动 | 分步静态函数(成员访问经 root 显式传入) | 层序逻辑不在此件——唯一事实源=`ui_registry.LAYER_CONTRACT`+`ui.apply_layer_contract(root)`(迭代 44 迁驻注册面) | 同 D1(Control 装配面) |
| **main_ui_build**(`presentation/main_ui_build.gd`,迭代 37 拆出) | `_build_ui` 八步拆分件——组合根 UI 装配分步承接(三屏壳实例化、视图句柄接线、覆盖层装配与模态编排注入按步拆开);main 侧留同名薄委托,装配序与 z 序零改动 | 分步静态函数(成员访问经 root 显式传入) | 层序逻辑不在此件——唯一事实源仍是 `main.LAYER_CONTRACT` + `_apply_layer_contract()` | 同 D1(Control 装配面) |
| **main_adventure_flow**(`presentation/main_adventure_flow.gd`,迭代 37 拆出) | main 冒险流程族——菜单开局/续玩后的冒险编排接线、节点事件分发、终局面与选路屏开合;逐字搬运,main 侧留同名薄委托(探针 `main.X` 路径零迁移) | 流程静态函数(root 显式) | 局态全局住 run_scope 节点——本件零成员态、零新增信号连接(连接语句随原段搬运) | 无 |
| **main_overlay_flow**(`presentation/main_overlay_flow.gd`,迭代 37 拆出) | main 浮层流程族——设置/状态面板/生涯页/选档界面/图例/钱包事件的开合与数据投喂;逐字搬运,main 侧留同名薄委托 | 流程静态函数(root 显式) | 全部开合走组合根模态编排口(`root.ui.modals.flow.open/close`)——本件零成员态 | 同 D1 |
| **main_overlay_flow**(`presentation/main_overlay_flow.gd`,迭代 37 拆出) | main 浮层流程族——设置/状态面板/生涯页/选档界面/图例/钱包事件的开合与数据投喂;逐字搬运,main 侧留同名薄委托 | 流程静态函数(root 显式) | 全部开合走组合根模态单口(`root._open_modal/_close_modal`)——本件零成员态 | 同 D1 |
| **main_debug_flow**(`presentation/main_debug_flow.gd`,迭代 37 拆出) | main 调试流程族——调试浮窗开合与上下文同步、批量模拟入口、两层调试对局起手、诊断转储;逐字搬运,main 侧留同名薄委托 | 流程静态函数(root 显式) | 调试会话状态机归 debug_session.gd——本件只接线与出 UI,零成员态 | 同 D1 |
| **TilePalette**(`presentation/views/tile_palette.gd`,迭代 37 拆出) | 瓦片排版/装载:高墙/地面瓦片整批装载与运行时镜像合成、地板明度归一、数字墨族按板自适应判据、格坐标确定性散列与十六正交情形墙瓦片查表 | 查表族(读件持棋盘引用——棋盘视图 rebuild 经 `bind_board` 同步);装载缓存七成员持于件内(同板型零重载、换板重载时序同源) | 零绘制面、零玩法数据写入(绘制调用面留棋盘视图);纹理路径单点在件内 | Resource 纹理加载——LOW |
| **HeroSprite**(`presentation/views/hero_sprite.gd`,迭代 37 拆出) | 英雄精灵四态与落位装配(落位与位移演出、撞墙帧闪、挖掘弹跳、脚底锚与持握手位出口) | 由棋盘视图 `_init` 建为子件(注入世界根与壳层容器引用)——精灵本体经世界根 ensure_hero 建于 SubViewport 世界内,本件只驱动其状态 | BoardWorld 世界根;素材缺位=零精灵(棋盘视图字形「人」回退路径在役) | Node2D / create_tween(Tween 系 Node 方法——MEDIUM 域对照 breaking-changes.md) |
@@ -190,7 +190,7 @@
### 三屏壳与组合根句柄面
- 呈现场景拆为三屏壳:MenuScreen(开始菜单屏)/CampScreen(营地屏)/AdventureScreen(冒险屏——内含关卡地图与战斗两容器)。
- 屏成员寻址自实现批起迁经界面注册面(**已建件——迭代 44**):探针挂载假设 `main.X` 迁移为 `main.ui.<域容器>.<句柄>`(ui_screens/ui_battle/ui_modals 三域容器,约 867 处探针与系统测试引用随批改址);原 S13 豁免登记〔2026-10-27「组合根类变量面即探针契约面」〕注销;main.gd 类变量 49→15,注册面形态见「模块归属·呈现层」表 ui_registry 行;ModalFlow 实例随 UiModals 容器构造建立(装配早期注入点依赖其先在座);取证探针 shot_probe/fx_shot_probe 随批改址;局态一律住 RunScope 节点,禁在屏/视图侧持有局态引用。
- 屏成员寻址自实现批起迁经界面注册面(**2026-10-29 零豁免裁+注册面设计闭合——实现批建件**):探针挂载假设 `main.X` 迁移为 `main.ui.<域容器>.<句柄>`(ui_screens/ui_battle/ui_modals 三域容器,约 867 处探针与系统测试引用随批改址);原 S13 豁免登记〔2026-10-27「组合根类变量面即探针契约面」〕注销;main.gd 类变量 49→15,注册面形态见「模块归属·呈现层」表 ui_registry 行;局态一律住 RunScope 节点,禁在屏/视图侧持有局态引用。
- **组合根拆件**=组合根构造注入、代组合根执行核心写调用的呈现侧子件(现行三件:curio_interact_controller/camp_shop_presenter/treasure_interact_controller——冒险节点后续推进、营地购买与刷新、道具使用)。视图禁直连拆件;核心写调用的可达路径仍是「视图→信号→组合根(含拆件)→核心」,依赖规则与组合根单点装配不变。成员见「模块归属·呈现层」表。
### 视觉层装配合同
@@ -318,11 +318,11 @@
本节为测试域(测试载体、判定口径、静态扫描域)的事实源。新增违规案例仍须先补 static_scan 规则再修码(code-conventions 缺陷-规范对照表纪律)。
**S13 测量面豁免注销(2026-10-29 零豁免裁)**:原循环内 `await` 豁免〔2026-10-27〕注销——tune_sim_controller 改 process_frame 信号常驻连接+步进状态机(每帧一局、帧相位逐点对齐,已建件——迭代 44 卡③);运行态收尾现行形态=run() 尾每批一次执行(卡③ v1.0 申报的「每局置假还原」前提已被迭代 41 测试债批 215da315 先行消化,行为改动申报随 v1.1 停卡重裁剥离)。
**S13 测量面豁免注销(2026-10-29 零豁免裁)**:原循环内 `await` 豁免〔2026-10-27〕注销——tune_sim_controller 改 process_frame 信号常驻连接+步进状态机(每帧一局、帧相位逐点对齐,设计闭合、实现批建件);随批申报行为改动一处:运行态收尾由「每局置假」还原为「整批收尾单次置假」(消除与 wd8/ui_tune_probe 断言面的静态矛盾)。
**S13 豁免第三条注销(2026-10-29 零豁免裁)**:原 battle_orchestrator max-class-variables 豁免〔2026-10-28 迭代 40 卡①〕注销——RoundCycle/BattleFacts 两新件设计闭合(宿主实测 15,已建件——迭代 44 卡② 合回,见「战斗编排域拆件定稿」节轴丁)。
**S13 豁免第三条注销(2026-10-29 零豁免裁)**:原 battle_orchestrator max-class-variables 豁免〔2026-10-28 迭代 40 卡①〕注销——RoundCycle/BattleFacts 两新件设计闭合(宿主 38→15,实现批建件,见「战斗编排域拆件定稿」节轴丁)。
**gdstyle 计数截断缺陷登记(2026-10-29 两设计批独立实证)**:gdstyle v0.4.0 对文件内 inner class 的成员计数存在截断——内嵌类中带行尾注释的属性声明截断其后续成员计数(standee_body 实测 31 声明仅记 19、board_cell_unit 33 仅记 30)。基线登记值属欠估;闸机扫描与基线同源、判定口径自洽不误报,债的真值以实查为准——零豁免各批按真实口径设计(实数低于阈值,不依赖解析缺陷)。**清偿结果(2026-10-31)**:board_cell_unit 30→10、standee_body 19→10 已随迭代 45 真实清偿,基线 refresh 已收账(欠估口径旁证=33/31 声明与实测吻合)。登记待办:向 gdstyle 上游报 issue(仍在册)。
**gdstyle 计数截断缺陷登记(2026-10-29 两设计批独立实证)**:gdstyle v0.4.0 对文件内 inner class 的成员计数存在截断——内嵌类中带行尾注释的属性声明截断其后续成员计数(standee_body 实测 31 声明仅记 19、board_cell_unit 33 仅记 30)。基线登记值属欠估;闸机扫描与基线同源、判定口径自洽不误报,债的真值以实查为准——零豁免各批按真实口径设计(实数低于阈值,不依赖解析缺陷)。登记待办:向 gdstyle 上游报 issue。
---
+5 -5
View File
@@ -18,7 +18,7 @@
写文档像向朋友当面解释你的游戏:
1. **完整句子优先**:一段只说一件事,长句拆短句。
2. **禁止碎片笔记体**:不写压缩碎片,不把 `A→B→C` 箭头链当叙述(流程交给图,见第 3 节),不用只有作者自己懂的缩略语。
2. **禁止碎片笔记体**:不写压缩碎片,不把 `A→B→C` 箭头链当叙述(流程交给字符画,见第 3 节),不用只有作者自己懂的缩略语。
3. **每节人话开头**:每小节第一段先用白话说清"这是什么、为什么需要它";只读各节第一段就能看懂全文故事线。
4. **术语首现即解释**:专业术语或项目概念首次出现用一句话解释;玩家可见专属名词(如"显影尘")同样要说清。
5. **叙述优先于引用**:AC-x、F-x 等编号引用不进正文叙述,只出现在验收标准与附录。
@@ -30,11 +30,11 @@
|---|---|---|
| 规则的来龙去脉、行为逻辑 | 散文段 | 每段 ≤240 字,一段一件事 |
| 枚举、数值、边界、依赖 | 表格 | 表格只放可列举的事实;禁把需要解释的内容塞进单元格让读者自己拼 |
| 流程、状态流转、结构关系 | 文档内嵌 HTML/SVG(默认)· Mermaid(网页发布保底)· 字符画(无渲染环境保底) | 禁用散文逐步描述流程;默认在文档内嵌 HTML/SVG(表现力优先,本地预览即渲染,图体行数计入节点篇幅);发布到会过滤内嵌 HTML 的网页端时改 Mermaid;无渲染查看环境用字符画 |
| 流程、状态流转 | 字符画(代码块) | 禁用散文逐步描述流程 |
| 公式 | 结论先行 | 先一行加粗结论,再放公式代码块、变量表与验算表 |
| 体验锚点 | 关键词表 | 支柱/关键词/机制承诺 三列表,禁长篇情绪描述 |
节点内顺序:先人话,再表格枚举,再流程图(内嵌 HTML/SVG 或字符画),最后公式举证。**规则两段式**:先写"人话阶段"(作用于哪个系统、什么情况下、谁触发、触发后发生什么),再写"举证阶段"(逐效果给出计算方法;纯流程规则明写"无计算")。规则名禁止纯名词短语(反面例:"获取路由")。
节点内顺序:先人话,再表格枚举,再字符流程图,最后公式举证。**规则两段式**:先写"人话阶段"(作用于哪个系统、什么情况下、谁触发、触发后发生什么),再写"举证阶段"(逐效果给出计算方法;纯流程规则明写"无计算")。规则名禁止纯名词短语(反面例:"获取路由")。
## 4. 用语纪律(见名知意 · 所见即所得 · 禁自造黑话)
@@ -136,7 +136,7 @@
### 12.1 待办清单条目格式与状态机
待办清单是流水线的唯一登记处:**现在不做、以后才做的事**登记一条,随流水线段推进改状态,做完整条删除。条目记录某件事走到了哪一段,不是各段的开工前置。一条条目分两层——**标题=登记元数据**(何时登记、从哪来、紧急度,登记后不再变),**正文=四个属性**(随进程演进)。四个属性各管一件事:`实况` 管凭据,`缺口` 管这条的**输入**(还缺哪一次设计),`改动面` 管这条的**产出**(要动哪些面),`状态` 管这条走到流水线哪一段。
待办清单是流水线的唯一登记处:要做的事先登记一条,随流水线段推进改状态,做完整条删除。一条条目分两层——**标题=登记元数据**(何时登记、从哪来、紧急度,登记后不再变),**正文=四个属性**(随进程演进)。四个属性各管一件事:`实况` 管凭据,`缺口` 管这条的**输入**(还缺哪一次设计),`改动面` 管这条的**产出**(要动哪些面),`状态` 管这条走到流水线哪一段。
标题格式(尖括号处按实际填写,方括号内为可选段):
@@ -173,7 +173,7 @@
文档线:待文档设计 → 文档设计中 → 文档设计已闭合
美术线:待美术设计 → 美术设计中 → 美术设计已闭合
下游共用序列(需求改动):待写卡 → 已写卡 → 已编迭代 N → 完成即整条删除
直改序列(工程改动/文档设计改动/美术素材改动):待执行 → 完成即整条删除(经设计讨论的可前置闭合段,如「文档设计已闭合+待执行」)
直改序列(工程改动/文档设计改动/美术素材改动):登记 → 待执行 → 完成即整条删除(经设计讨论的可前置闭合段,如「文档设计已闭合+待执行」)
累积示例:文档设计已闭合+待美术设计+已写卡
```
+2 -2
View File
@@ -16,13 +16,13 @@
## 2. 五段流水线
**需求改动**(游戏代码,含随逻辑写/改的测试)按五段流水线走:**登记 → 文档设计/美术设计(两条设计线)→ 写需求卡片 → 迭代编排 → 实现**;工程/文档设计/美术素材三类改动不走写卡与编排段,走「(如需设计讨论)→ 用户发令直改 → 主区直改+commit 点闸」(车道细则见 §1)。两层口径(2026-10-25 用户立):需求卡片=需求单位的实施文档,任何工作量皆可写、皆可编入迭代;工作量限制只属迭代层——迭代估时总量 <5 个工作日=「冻结待凑批」,不可开工。段间不自动流转,每段转进由用户发令;每段一个技能入口(五者均装于项目 `.zcode/skills/`:doc-design/asset-ingest/req-card/iter-plan/iter),登记不立技能(条目格式由流水线闸强制)。设计工作在会话内进行、用户主导、禁无监管自跑。
**需求改动**(游戏代码,含随逻辑写/改的测试)按五段流水线走:**登记 → 文档设计/美术设计(两条设计线)→ 写需求卡片 → 迭代编排 → 实现**;工程/文档设计/美术素材三类改动不走写卡与编排段,走「登记 →(如需设计讨论)→ 用户发令直改 → 主区直改+commit 点闸 → 完成删条目」(车道细则见 §1)。两层口径(2026-10-25 用户立):需求卡片=需求单位的实施文档,任何工作量皆可写、皆可编入迭代;工作量限制只属迭代层——迭代估时总量 <5 个工作日=「冻结待凑批」,不可开工。段间不自动流转,每段转进由用户发令;每段一个技能入口(五者均装于项目 `.zcode/skills/`:doc-design/asset-ingest/req-card/iter-plan/iter),登记不立技能(条目格式由流水线闸强制)。设计工作在会话内进行、用户主导、禁无监管自跑。
| 段 | 入口技能 | 产出 | 出入口铁律 |
|---|---|---|---|
| 一·登记 | 无(闸管格式) | 待办清单条目 | 条目=标题登记元数据(日期/来源/可选优先级)+正文四属性(实况/缺口/改动面/状态);标题登记后不再变,正文随进程演进。格式与状态机权威=doc-conventions 第 12 节 |
| 二·文档设计/美术设计 | doc-design(文档线,兼任反哺点写入)/asset-ingest(美术录入;样张素材由用户主导产出) | GDD 文档或 art-bible/过审样张素材 | 用户提起或从待办清单取出;会话内进行、用户主导、禁无监管自跑;经用户点头确认心智一致才许写入文档;完成判据=待裁点清零,带未裁点的是讨论稿;设计线闭合凭据记入待办清单实况清单(文档线=文档路径+版本号,美术线=art-bible 条款版本);缺哪条线写在缺口属性,闭合与否写在状态段 |
| 三·写需求卡片 | req-card | 需求卡片 | 设计线闭合后用户发令;新玩法/新系统/新内容必须有设计定稿前置,例外=需求性工程条目(缺陷修复/重构等改游戏行为)凭实况里的证据指认跳过设计线;配套维护类(工程债/测试基建/工具维护,不动游戏行为)=工程改动直改类,不走本段,由用户发令直改;卡片必经 grilling 多轮复核、零待裁点才成立;写完在待办清单状态记「已写卡」,卡片路径记入实况清单(防遗忘) |
| 三·写需求卡片 | req-card | 需求卡片 | 设计线闭合后用户发令;新玩法/新系统/新内容必须有设计定稿前置,例外=需求性工程条目(缺陷修复/重构等改游戏行为)凭实况里的证据指认跳过设计线;配套维护类(工程债/测试基建/工具维护,不动游戏行为)=工程改动直改类,不走本段,登记后「待执行」由用户发令直改;卡片必经 grilling 多轮复核、零待裁点才成立;写完在待办清单状态记「已写卡」,卡片路径记入实况清单(防遗忘) |
| 四·迭代编排 | iter-plan | roadmap 迭代行 | 编排四查:①每卡卡片文件存在 ②每卡设计闭合凭据齐(或需求性工程条目的证据指认)③估时与冻结线——所选卡估时按区间**中值**求和(单位工作日、写精确值不取整):**卡片任何工作量均可编入**(2026-10-25 用户立:工作量限制属迭代层、非卡片层);编入后迭代总量 <5 个工作日=迭代行状态「冻结待凑批」、不可开工,凑批 ≥5 后由编排段改「待开工」(迭代体量偏好,2026-10-21 用户立、2026-10-25 改冻结制);5–10 个工作日为常规区间;超过 10 个工作日须经用户确认(确认前呈报整批/拆批两案等裁,禁自行拆分或塞入),确认后迭代行随带日期特批标记;超过 20 个工作日禁止编(硬上限,用户确认也不放行,必须拆批)④写完 roadmap 行后跑流水线闸确认绿;编入未完成迭代,新开迭代行经用户确认 |
| 五·实现 | iter | 代码与配置,可运行的游戏 | 需求卡片永不单独施工,统一由用户指定迭代号触发(=U0 开工令),按 iter 技能与本文 §3 执行;**本段是代码实现批:禁写 `design/` 与根规则文件,产出物只落 `reports/`(见 §3 第 12 条)**;设计权威文档(GDD/art-bible)批内一律不更新;完成人闸=实机目检清单+实现决定记录写齐即完成批内义务 |
+3 -29
View File
@@ -11,23 +11,21 @@
>
> **兑付频率(先看这条,它是三条渠道的共同前提)**:奇物**不是打一场战斗给一份**。关内的普通战斗节点打完了什么也不给;一关之内奇物**只兑付一次**,触发点是**关底 Boss 被击败**——而按 [冒险结构](冒险结构.md) 的定义,打掉关底 Boss 恰好就等于"通过这一关",这两件事是同一个时刻,不是一条链上的两步。
>
> 状态:**已实现**(2026-10-04 双层 BD 实施批落地);现行为 v1.2。本文件是奇物的唯一定义文档。
> 状态:**已实现**(2026-10-04 双层 BD 实施批落地);现行为 v1.1。本文件是奇物的唯一定义文档。
>
> 数值纪律:本文件只写概念、取值域与指路;每件给多少、效果是什么一律不进正文,事实源是 `data/tables/feats.tres` 与 `data/tables/adventure.tres`。
## 专长(重设计·基础框架 v1)
> 本节是专长分类的现行事实源(2026-10-07 用户终审认同写入文档)。件表为设计声明——数值全 provisional 归调参批;命名全为代号,正式命名归用户。**新框架已实装:`feats.tres` 30 行=基础 27+迁入 2+规则豁免 1,行为与数据行 1:1。**
> 本节是专长分类的现行事实源(2026-10-07 用户终审认同写入文档)。件表为设计声明——数值全 provisional 归调参批;命名全为代号,正式命名归用户。**新框架已随迭代 20 专长实装批落地(2026-10-11):`feats.tres` 29 行=基础 27+迁入 2,行为与数据行 1:1。**
### 设计原则
1. **触发器句式**:每件专长的效果声明为「当 X 时 Y」(on / when / do 三字段,词表=效果引擎封闭集)。件的性格来自它挂在哪个事件上;combo 靠事件共享自然涌现——不设流派标签、不设档位机制。
2. **事件族组织**(设计侧分组工具,不上卡面):开格系 / 封印系 / 踩雷系 / 节奏系 / 战斗系 / 换图系六族。族名禁上玩家可见面(命名铁律)。
2. **事件族组织**(设计侧分组工具,不上卡面):开格系 / 封印系 / 踩雷系 / 节奏系 / 战斗系五族。族名禁上玩家可见面(命名铁律)。
3. **准入校验**:代价与收益须同账可核算——「纯代价件」禁止出生;契约件的代价必须一句话讲得清(契约不可怕,糊涂账才可怕)。
4. **信息红线**:禁止任何效果让数字说谎(信息规则不豁免);揭示类效果只揭真格。
5. **防兑换环**:互相转换的通道必须有损耗或每轮上限;单向通道优先。
6. **基座先行**:流派先确定基座专长——通过改变资源来源、动作成本或频率、动作结算、动作之间的关系,让原本偶尔用于止损的动作成为可以持续使用的主要打法。后续专长再围绕这个新行为提供输出、生存或资源收益;泛用数值增长不能代替基座。
7. **行为改变检验**:基座必须能回答「拿到前后操作有何不同、新行为靠什么资源持续、后续专长为何依附这个新行为」。只是同一操作后的结算数字变大,不算形成新流派。
> **落行注记(迭代 20 反哺写入,2026-10-12 追认⑤)**:「每第 N 次 / 积满触发」的数据行条件写 **N−1**——效果引擎分发时 when 先于 do 读计数器现值(预评估),故「满 N 触发」落行写 N−1(例:地质学家「满 4 种」写 eq 3、封印匠「每第 3 次」写 mod_eq ref 2、双管镐「每第 2 次」写 mod_eq ref 1)。行为仍为每第 N 次准点触发,单元测试钉住;新件落行照此口径。
@@ -97,28 +95,6 @@
|---|---|---|
| 血税 | 本局:踩雷 −2 最大生命;标中 +1 攻击(**均下一战起生效**——战末汇入冒险加算区) | 双向都痛都爽,一句话讲得清;双向同账、本局累积、冒险收束清零。生命上限/攻击系属战界重建属性,即时改会被重建冲掉,故滞后一战兑付(2026-10-12 追认⑨回写;卡面 desc 同步补「下一战起生效」) |
### 换图系(7 件,设计定稿,待实现)
换图系把「弃图刷新」从坏图逃生改成主动构筑行为。两件基座分别解决 RAP 的来源与价格;后续专长让成功刷新产生输出、生存或资源收益。七件不要求一次拿齐,不设强制先拿基座、集件标签或集件奖励。
| 层 | 代号 | 规则 | 行为职责 |
|---|---|---|---|
| 基座 | 掘进储能 | 每次付费挖格后额外获得 RAP;安全格与危险格同口径 | 持续挖图也能准备刷新,不再必须少挖停手 |
| 基座 | 折价换图 | 弃图刷新的 RAP 成本减半 | 同一份储备支持更多刷新 |
| 输出 | 弃图轰击 | 成功刷新后,在本轮战斗段对全体存活敌人造成一次伤害 | 刷新成为直接进攻行为 |
| 输出 | 换图蓄势 | 成功刷新攒层,层数有上限;下一次攻击附加全部层数对应的伤害后清零,战斗结束清空 | 连续换图蓄成一次单点爆发 |
| 生存 | 换图庇护 | 成功刷新后,在本轮授予段获得护盾 | 用刷新预防后续伤害 |
| 生存 | 弃图休整 | 成功刷新后,在本轮授予段回复生命,封顶生命上限 | 放弃当前图换取恢复 |
| 资源 | 弃图回收 | 成功刷新后,按旧图探索进度在本轮授予段回收清图奖励 | 先探索再主动弃图变现 |
共同规则:
- **双基座叠加**:掘进储能与折价换图可以同时持有,产 RAP 与成本减半分别生效;每轮刷新次数上限继续限频,不设互斥规则。
- **成功才触发**:后续五件只认成功换图;刷新被拒绝时不产生效果。同一轮多次成功刷新按发生顺序逐次登记。
- **轮末兑付**:刷新不新增结算段。群伤、护盾、回血与回收按[战斗结算](../战斗循环/战斗结算.md)的既有段序兑付,刷新瞬间不直接改这些战斗结果。
- **回收看旧图**:弃图回收读取被放弃旧图的探索进度,不把旧图或新图判为清图;具体口径归[地图战斗资源](../战斗循环/地图战斗资源.md)。
- **基线不变**:没有这些专长时,RAP 来源、刷新成本、清图奖励与刷新结果保持原规则。数值与稀有度归 `feats.tres`,落表前本节只作设计声明。
### 迁入登记(秘宝域迁出件;实装批落地后并入件表)
| 代号沿用 | 目标族 | on / when / do 要点 | 备注 |
@@ -478,8 +454,6 @@
10. 一个渠道一旦确定要出某个分类,该分类的成员就都在它的池子里——"渠道出哪些分类"不受这条约束,那是渠道轴的定位。
11. 凭证拿到即开,不进持有集,玩家无法囤积——选中或买下的同一时刻就开专长三选一。
12. 凭证带有计步保底:连续 5 次三选一展示未出现则第 6 次必出;计数跨奖励节点与商店共享,刷新后的重新展示计入次数,限购用满后保底不再触发。
13. 被称为流派基座的专长必须能说明拿到前后的操作差异与新行为的持续资源来源;只有同一操作后的泛用数值增长,不得登记为基座。
14. 换图系两件基座同时持有时都生效;后续五件只在成功刷新后触发,无专长基线不变;弃图回收按旧图探索进度结算,同一旧图至多回收一次。
> 实装批落地前,现役代码按旧 27 件专长目录与旧资源成员在同框架下运行;新成员口径(27 件基础件表 / 凭证件表)自实装批起验。
@@ -1,7 +1,7 @@
# 地图战斗资源
> 一句话:格面资源的产出、极性字典与清图奖励封装
> 状态:已定稿(自旧体系迁移);现行为 v1.2
> 状态:已定稿(自旧体系迁移);现行为 v1.1
## 设计目的
@@ -158,29 +158,8 @@
| 可配置可替换 | 内容 = 池行值;允许即时类(当场兑付,不入库) |
| 图类型差异化是预留插槽 | 现行只有默认槽;结构位已留,启用走棋盘生成词表落定 |
持有弃图回收专长时,同一清图奖励对象也可按旧图探索进度部分回收;这不改变正常清图与清图奖励的定义,例外口径见下节。
举证:本节为封装与流程规则,无计算。
### 弃图回收:按旧图探索进度兑付(设计定稿,待实现)
弃图回收让玩家不必挖完最后几个安全格,也能把已经推进的探索换成一部分清图奖励。它只读取被放弃的旧图,不把旧图判为清图,也不把新图判为已清图。
旧图退役前记录三项读数:装板初始已开安全格数、装板初始未开安全格数、退役时已开安全格数。探索进度定义为:
```
探索进度 =(退役时已开安全格数 − 装板初始已开安全格数)÷ 装板初始未开安全格数
```
进度限制在 0~1。玩家付费挖开与连锁展开的安全格都真实推进探索,计入分子;装板时白送的初始格、封锁格与危险格不计入。没有新探索就刷新,进度为 0,不能获得回收奖励。
回收兑付的是旧图在装板期已经摇定的清图奖励对象,按探索进度取对应份额;具体比例、档位、取整与数值归调参。它只发基础奖励的进度份额:
- 不触发完美封印加倍,不产生清图事件,不执行正常清图的整板兑付。
- 同一旧图只在成功退役时回收一次;新图不继承旧图探索进度。
- 刷新被拒绝时旧图不退役,也不回收;成功后的兑付时机归[战斗结算](战斗结算.md)本轮授予段。
- 正常清图仍发完整清图奖励对象;弃图回收不得让同等探索投入的收益无条件高于正常清图。
## 依赖与输出
本系统被两侧夹住:事实与数值从上游来,调用与呈现往下游去。下表就是删除任何一方时的影响面清单。
@@ -260,9 +239,6 @@
- 类型封闭与字典完整:表里出现九型之外的类型、或任一型缺正向/负向行,加载拒绝。
- 未生效可见:满层增益、满血正向治疗、无存活敌的群伤,各自在结算明细记"奖励未生效"行,零折算、零补偿。
- 加载护栏:池空、清图池无正当量、增益引用悬空——任一命中即加载时返回错误并中止;默认内容加载通过。
- 弃图回收进度:付费挖开与连锁展开的安全格计入探索,初始格、封锁格与危险格不计;无新探索时进度为 0,不能获得回收奖励。
- 弃图回收恰一次:成功退役的旧图按当次探索进度回收其预摇清图奖励对象,新图不继承进度,同一旧图不能重复回收;被拒绝的刷新零兑付。
- 回收不是清图:弃图回收不触发完美封印加倍、清图事件或正常清图整板兑付;正常清图仍发完整清图奖励对象。
- 表实一致:数据表字段与奖励表字段定义逐字段一致。
## 待办
@@ -1,7 +1,7 @@
# 战斗结算
> 一句话:轮末结算的编排与逐行兑现
> 状态:已定稿(自旧体系迁移);现行为 v1.3
> 状态:已定稿(自旧体系迁移);现行为 v1.2
## 设计目的
@@ -90,7 +90,7 @@
第一层按行型豁免两类:衰减行(到期移除本就是段级动作,不属于任何一格)、判定行(它是胜负出口的结论,不是棋盘事实)。这两类连来源类别都不必填。
第二层按来源类别豁免,封闭清单六类:主角攻击行(来源是主角单位本身)、敌方行动行(来源是当时动手的那个敌人单位)、清图奖励行(来源是"这张板被清掉"这一整板事实)、专长揭示掷格行(落空行没有真实棋盘格可指)、专长 GP 入账行(来源是钱包面而非棋盘面)、专长刷新效果行(来源是刷新专长与被弃旧图,不指向单一格)——它们都不指向某个棋盘格,来源格校验因此跳过。
第二层按来源类别豁免,封闭清单五类:主角攻击行(来源是主角单位本身)、敌方行动行(来源是当时动手的那个敌人单位)、清图奖励行(来源是"这张板被清掉"这一整板事实)、专长揭示掷格行(落空行没有真实棋盘格可指)、专长 GP 入账行(来源是钱包面而非棋盘面)——它们都不指向某个棋盘格,来源格校验因此跳过。
豁免不等于免检:按行型豁免的行仍要带齐公共字段、守住各自的语义约束(如衰减行的数值必须是本次移除的读数),按来源类别豁免的行仍必须写明来源类别,其余行必须指回一个真实存在的棋盘格。豁免只是免掉"来源格必须有效"这一条校验,不是免掉整份行协议。
@@ -266,24 +266,6 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
挖掘期刷新是玩家在挖掘中途主动换图,经[行动经济](../排雷循环/行动经济.md)扣费:扣费成功后按"旧板退役、生成新板、装板、资源预分配恰一次、变异布置"编排,本轮不打断——没有结算段、没有轮次结束。储备不足被拒是玩家决策的合法结果,不是编排错误。刷新图不发清图奖励,完美封印计数随装板重置。
### 换图系专长:刷新事实与本轮兑付(设计定稿,待实现)
持有换图系后续专长时,成功刷新除执行上述换图编排外,还产生一次 `board_refreshed` 事实。旧图退役前保存它的探索进度与清图奖励对象身份;新板就位后登记事实。刷新被拒绝时不产生事实,任何后续效果都不触发。同一轮多次成功刷新按发生顺序逐次登记,不合并成一次。
刷新只登记事实,不当场插入结算。各效果在既有五段内兑付:
| 专长 | 兑付规则 |
|---|---|
| 弃图轰击 | 每次刷新事实开一张全体敌人伤害单据,在本轮战斗段末经伤害控制器统一结账;目标按结账时的存活状态解析 |
| 换图蓄势 | 每次刷新事实当场累计一层,受表值上限约束;下一次主角攻击附加全部层数对应的伤害,随后清零;战斗结束清空 |
| 换图庇护 | 每次刷新事实在本轮授予段经既有护盾通道授予一次,不新开挖掘期状态入口 |
| 弃图休整 | 每次刷新事实在本轮授予段回复生命,按既有治疗规则封顶生命上限;满血时记未生效行 |
| 弃图回收 | 每次刷新事实在本轮授予段按旧图探索进度回收其清图奖励对象;进度与奖励口径归[地图战斗资源](地图战斗资源.md) |
弃图轰击若击杀最后敌人,仍由本轮判定段按既有顺序判胜,不新增挖掘期即时胜利出口。换图系效果使用扩展行留痕:来源写明专长与被弃旧图,弃图轰击走伤害单据,护盾、回血与回收各保留自己的兑付记录;它们不指向单一棋盘格,使用明细流水的专长刷新效果来源豁免,不伪造来源格。
弃图回收不是清图:不触发完美封印加倍,不发清图事件,不改变「刷新图不发清图奖励」。它兑的是被放弃旧图按探索进度回收的一部分奖励。
战斗开始按固定序列开张:关上配置热重载的闸门、初始化状态池、掷骰定敌方实际数量、生成敌人、装板并预分配资源恰一次、变异布置([地形变异](../排雷循环/地形变异.md)——棋盘设置顺序第 4 步:生成棋盘 → 打开状态初始化 → 战斗资源格设置 → 变异格设置,消费 `variant_place` 封闭流)、发开轮通知并执行开轮摇号(摇定首轮各敌本拍动作)。上一关的大奖励(如血上限提升)经战斗开始入参的覆盖值通道生效,内容归[冒险结构](../冒险循环/冒险结构.md)。
举证:本节为编排流程,无计算。
@@ -378,9 +360,6 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
- 无来源即炸:每行明细都能指回一个来源事实(棋盘格、单位、清图或段级结论);找不到来源即断言失败,行型与来源类别两层豁免清单见明细流水一节,豁免行仍受其它契约约束。
- 行型封闭:流水行型只出现本文登记的基础 9 类与扩展行;行型词表改动必须走本文修订并同步呈现层行格式化,未登记行型禁止落行。
- 伤害单据管线(2026-09-24):全部伤害(主角先手、敌方伤害技、棋盘克制直伤、群伤、反噬、地形变异)经伤害控制器单据管线兑现,兑付配方封闭五种;目标在结账时刻照规则解析;死亡事件由单位自行宣布——怪死其未结单据作废(零行、不进冷却不进保底计数)、主角死兑付终止;段界结账为现行口径、开单即结为回切结构位,两模式各自同种子同输入逐位复现。
- 换图系刷新事实:成功刷新保存旧图探索进度并登记一次事实,被拒绝时不登记;同一轮多次刷新按发生顺序逐次兑付,刷新本身不新增结算段或轮次结束。
- 换图系输出:弃图轰击在本轮战斗段末经伤害控制器结算;换图蓄势受层数上限约束,下一次主角攻击消费全部层数,战斗结束清空;群伤致敌清空时仍由本轮判定段判胜。
- 换图系生存与回收:护盾、回血与弃图回收在本轮授予段兑付并留下可归因扩展行;回复封顶生命上限;回收不触发清图事件或完美封印加倍。
- 条款3 对账(2026-10-05 判式换代批 T3——与行动经济 F1 条款3 重立同步):①聚合健康式 (1−ρ̂)·E_dig(u) − ρ̂·V_mine(u) ≥ 0 与 β 比值带 [1.2, 2.5] 判读逐 u 检算(清图支配性审计为常设重跑项,读数快照登记归 clause3 重立条);②封印溢价兑付正确:封印标中正向兑付 = 同型正向 ×(1+δ)(δ 读 `settlement.tres` flag_hit_premium,100=无溢价直通零扰动),三类形态全覆盖(flat power / percent 幅度 / gp 缓冲)、标空与挖格兑付不乘溢价、危险格与安全格同池结构零触碰。
## 待办
@@ -1,7 +1,7 @@
# 行动经济
> 一句话:每轮行动点怎么花、停手与刷新的代价
> 状态:已定稿(自旧体系迁移);现行为 v1.2
> 状态:已定稿(自旧体系迁移);现行为 v1.1
## 设计目的
@@ -91,19 +91,6 @@
停手与刷新合起来构成这本账的基线节拍:满行动点一轮停手,正好攒够一次刷新。行动点修正队列会扰动该节拍的实际兑现(+1 轮可多挖一格),不变式本身不动;对账归曲线调参批。
### 换图系专长对储备与刷新的改写(设计定稿,待实现)
换图系两件基座分别改写 RAP 的来源与刷新价格。它们只给持有专长的玩家增加规则,不改变无专长时的基线账本。
| 专长 | 规则 | 不变边界 |
|---|---|---|
| 掘进储能 | 每次付费挖格被受理后,额外获得表值 RAP;安全格与危险格同口径 | 连锁展开只随首格的一次付费动作结算,不逐格追加;封印、移动、刷新不产 RAP |
| 折价换图 | 弃图刷新的 RAP 成本为基线的 50% | 只改价格;AP、轮次、每轮刷新上限与换图流程不变 |
两件基座可以同时持有并叠加:付费挖格照常产 RAP,刷新同时按半价扣费。两条规则作用在不同账户入口,不互相覆盖,也不设互斥。刷新仍须储备达到折后门槛才受理;储备不足或超过每轮次数上限时拒绝,状态不变。RAP 仍在战斗结束清零,刷新本身不返 RAP。
无掘进储能时,挖格不新增 RAP;无折价换图时,刷新成本仍读全局基值。后续输出、生存与资源专长如何使用成功刷新这个事实,归[战斗结算](../战斗循环/战斗结算.md)。
### 下轮行动点修正(格面资源八型批 2026-09-20)
格面资源八型批(2026-09-20)新增 action 型格面资源:正向行 ap_up = 下轮行动点 +1,负向行 ap_drain(迟滞)= 下轮行动点 −1。修正不在授予当轮生效:结算期经行动经济的行动点修正队列登记(同轮多笔按净值累加),下一轮开始时(开轮通知)一次性兑现——兑现值 = 基线 ap_per_round + 排队净值,下限 0,兑现后队列清零;排队净值不跨轮保留。明细在结算流水记效果行(effect_id=ap_delta)。
@@ -249,8 +236,6 @@
| move_ap_cost | int | 表值(冻结 0) | 移动免费——冻结,非调参项 |
| chain_ap_cost | int | 表值(冻结 0) | 连锁免费——冻结,非调参项 |
| refresh_cap_per_round | int | 表值(provisional,结构位预留) | 每轮刷新上限,开轮重置计数 |
| (跨表)掘进储能产 RAP | int [1, 64] | 设计定稿,待入 `feats.tres` | 每次受理的付费挖格兑付一次;连锁不逐格追加 |
| (跨表)折价换图成本率 | percent | 50%(结构已裁,待入 `feats.tres`) | 只改持有者的刷新价格;双基座叠加时不覆盖掘进储能 |
| (跨表)跨冒险行动点加成 | int [0,10] / int [0,99],冒险表 | adventure_table bonus_ap_flat / bonus_cap_ap(provisional) | 三选一 AP 件兑现值与全冒险次数上限;加成轴语义见「跨冒险行动点加成轴」节 |
字段名注记:2026-09-18 措辞完成批已全链对齐正式名——表字段与本文行名统一为 `refresh_cap_per_round`,刷新入口命名与表字段口径对齐(命名统一登记兑现,切片临时名零残留)。
@@ -265,8 +250,6 @@
- 仲裁唯一:最后 1 点行动点挖开最后一个安全格时,判清图(剩余清零 + 换图 + 清图奖励),不判耗尽;输出恰一条。
- 停手 1:1:剩余 3 点停手,储备池加 3,下轮同图继续。
- 刷新规则:储备不足时刷新被拒绝且状态不变;同一轮第 3 次刷新被拒绝;刷新不重置行动点、不发清图奖励、旧图上的封印印记失效。
- 掘进储能:持有专长时,每次受理的付费挖格额外产一次 RAP,安全格与危险格同口径,连锁展开不逐格追加;无专长时挖格不新增 RAP。
- 折价换图与双基座:持有折价换图时按基线的 50% 扣费;与掘进储能同时持有则两条规则都生效,刷新仍受储备门槛与每轮次数上限约束。
- 储备清零:战斗结束信号到达后储备池归 0,行动点进入休眠。
- 跨字段校验:任一动作成本配置超过每轮预算时,加载期致命中止。
- 零随机:同一动作序列驱动两个独立行动点实例,轮次结束记录与储备轨迹逐项相同。
@@ -55,13 +55,13 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
三区各管一块:
| 区 | 位置与几何 | 管什么 |
| 区 | 位置与尺寸 | 管什么 |
|---|---|---|
| B 战斗画面 | 全宽,高读布局表 `stage_h` | 主角站桩立绘、敌人站桩立绘 ×1–3(**全站桩形态,2026-09-20 用户拍板:无框立绘+底座、状态小图标挂血条下,英雄卡/敌人卡退役**)、轮次阶段指示、结算明细带 |
| C 角色信息 | 左下,宽读 `info_panel_w`,高 = 基准屏高 − B 区高 | 生命 / 攻击 / 储备数据框、状态效果槽、小人立绘 |
| D 扫雷图 | 中下,宽 = 基准屏宽 − C 区宽 − 列间距(原右下面板删除后横向加宽),高同 C 区 | 行动点条与按钮组、棋盘本体、toast 槽 |
| B 战斗画面 | 全宽 × 270px | 主角站桩立绘、敌人站桩立绘 ×1–3(**全站桩形态,2026-09-20 用户拍板:无框立绘+底座、状态小图标挂血条下,英雄卡/敌人卡退役**)、轮次阶段指示、结算明细带 |
| C 角色信息 | 左下 240×530 | 生命 / 攻击 / 储备数据框、状态效果槽、小人立绘 |
| D 扫雷图 | 中下约 1036×530(原右下面板删除后横向加宽) | 行动点条与按钮组、棋盘本体、toast 槽 |
分区几何的唯一事实源 = `game/data/tables/battle_layout.tres`(经 `ConfigDb.presentation.battle_layout` 读取);分区尺寸与组件落位的像素级裁决归 [battle.md](../../ux/battle.md),两份文档只写结构关系、不复写数值。三区结构与双舞台常驻是结构冻结项,修订走文档修订而非调参。节点链进度不在战斗屏呈现——它由关卡画面(选路覆盖层常驻形态)的节点态承载,见[关卡节点地图](../冒险循环/关卡节点地图.md)。
分区尺寸与组件落位的像素级裁决归 [battle.md](../../ux/battle.md);三区结构与双舞台常驻是结构冻结项,修订走文档修订而非调参。节点链进度不在战斗屏呈现——它由关卡画面(选路覆盖层常驻形态)的节点态承载,见[关卡节点地图](../冒险循环/关卡节点地图.md)。
**结论**:棋盘格边长由 [battle.md](../../ux/battle.md)「棋盘格尺寸公式」按棋盘区实测面积与板型算出;本文只判"塞不塞得进",不复制算式与算例。**分辨率档位(2026-09-30 迭代 9 三裁决案甲修订)**:两档=1280×800 基准档(本表全部定值的口径)+1920×1080 档=**全局等比**(stretch canvas_items+aspect keep——16:10 设计在 16:9 窗口为等比 1.35×+左右黑边各 ~96px);本节与各屏定值一律按 1280 基准档书写,1920 档由引擎等比承载、零重排。
@@ -74,9 +74,9 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
| 变量 | 含义 | 现行值 |
|---|---|---|
| N | 棋盘边长格数 | 读[棋盘生成](../排雷循环/棋盘生成.md)(现行四档;事实源 board_gen.tres) |
| 棋盘区宽 / 高 | 扫雷图区基准尺寸 | 由 D 区尺寸扣行动点条高(读 `ap_bar_h`)得出,本表不复制现算值 |
| 棋盘区宽 / 高 | 扫雷图区基准尺寸 | 由 D 区尺寸扣行动点条 44px 得出,本表不复制现算值 |
装板可行性由上面的不等式保证;格边长的可读下限见 [battle.md](../../ux/battle.md),算例由样张渲染脚本按布局表现算、不入文档。棋盘数字直接显示排雷玩法给的真值——呈现层禁止自己算数字,这是"数据层从不说谎"在呈现侧的镜像。
装板可行性由上面的不等式保证;格边长的现行算例与可读下限见 [battle.md](../../ux/battle.md)。棋盘数字直接显示排雷玩法给的真值——呈现层禁止自己算数字,这是"数据层从不说谎"在呈现侧的镜像。
### 提示与弹层:轻提示随起随消、重弹层排队进出
@@ -244,7 +244,7 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
## 调参表
本系统的参数全部是呈现节奏与可读性常量,不是玩法数值——玩法数值一律归[数据配置](数据配置.md)的表,与本表无关。修订本文常量走文档修订流程。**本表常量无数据表承载,为文档面唯一事实源;入驻 .tres 时本列改指路。** 布局几何(B 区高 / C 区宽 / AP 条高 / 明细带两态高)已入驻 `game/data/tables/battle_layout.tres`,不在本表重复。
本系统的参数全部是呈现节奏与可读性常量,不是玩法数值——玩法数值一律归[数据配置](数据配置.md)的表,与本表无关。修订本文常量走文档修订流程。**本表常量无数据表承载,为文档面唯一事实源;入驻 .tres 时本列改指路。**
| 参数 | 域 | 现行值(文档自持,无表承载) | 说明 |
|---|---|---|---|
@@ -254,6 +254,7 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
| clear_celebration_ms | 每次清图 | 3000–5000 | 上限约束:三五秒即收 |
| font_min_board / font_min_detail / font_min_tooltip | 1280×800 基座 | 18 / 14 / 13 px | 可访问性基线 |
| reduce_motion_scale | 每段动画 | 0.5 或跳终态 | 减动效开关系数 |
| detail_band_height | 结算明细带 | 24 / 84 px | 摘要 / 实时流两态高度 |
结构冻结(非调参项,改动走文档修订):三区布局与分区尺寸、双舞台常驻、单意图帧纪律、弹层层级纪律、图标键映射契约、程序化占位策略、订阅纪律。性能预算为引用值:同屏 draw calls < 200、进入战斗屏 500ms 内可交互。
@@ -261,7 +262,7 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
以下判据逐条可测,全部通过才算闭环达标。
- 三区无遮挡、无重叠、无内容裁切(棋盘全部格子与数字完整可见),分区几何与布局表 `battle_layout.tres` 一致;同屏 draw calls < 200;进入战斗屏 500ms 内首格可点。
- 三区无遮挡、无重叠、无内容裁切(棋盘全部格子与数字完整可见),分区尺寸与 battle.md 布局表一致;同屏 draw calls < 200;进入战斗屏 500ms 内首格可点。
- 任意板型装板后网格实高不超出棋盘区:N × 格边长 + (N−1) × 2px ≤ 棋盘区可用高;数字字号与整数倍缩放判据见 battle.md。
- 已开 / 未开格态在灰度截图下可逐格分类;已引爆危险格"可通行可读"(已开底加巢穴纹、可作移动目标)。
- 类型微光:微光只在英雄周围 8 格内的未开格显示;出圈即灭、入圈即亮;已开格与封锁格恒无微光;微光呈现不随格是雷是安变化(不泄危险格与安全格)。(迭代 30 卡①实装后转正式验收)
@@ -70,7 +70,6 @@
| 关卡表 | 逐关节点图与层名 | [冒险结构](../冒险循环/冒险结构.md) |
| 冒险表 | 关卡大奖励与钥石入账(单行全局) | [冒险结构](../冒险循环/冒险结构.md) |
| 元进度表 | 元进度字段(已建表 2026-09-21——现行五行族:属性锻造 / Boss 钥石 / 工坊阈值 / 换算率 / 同调刷新升级行族,顺带扩列) | [元进度](../跨局循环/元进度.md) |
| 战斗屏布局表(`battle_layout.tres`) | 分区几何与明细带两态高(单行;呈现层布局配置,非玩法数值——供呈现层与样张工具同源读取) | [战斗屏规格](../../ux/battle.md)(结构)/[HUD框架](HUD框架.md)(呈现常量) |
举证:本节为数据纪律约定,无计算。违规检测 = 静态扫描加人工复核,随各表回填定稿与里程碑检查执行。
+15 -11
View File
@@ -115,13 +115,13 @@
### Layout Zones
采用用户拍板的布局图(2026-09-12),按 1280×800 基座落尺寸。**分区几何的唯一事实源 = `game/data/tables/battle_layout.tres`**(经 `ConfigDb.presentation.battle_layout` 读取;字段 `stage_h` / `info_panel_w` / `ap_bar_h` / `log_band_h_dig` / `log_band_h_settle`)。本节只写结构关系与内容职责,不复写数值;任何文档与代码不另立第二份布局值。
采用用户拍板的布局图(2026-09-12),按 1280×800 基座落尺寸:
| 区 | 位置与几何 | 内容 |
| 区 | 位置与尺寸 | 内容 |
|---|---|---|
| **B 战斗画面** | 全宽,高读 `stage_h`(2026-10-06 方案二拍板:演出 ≈1/3 屏) | 主角站桩立绘(左)+ 兑现演出区(中)+ 敌人站桩立绘 ×1–3(右)——**全站桩形态(2026-09-20 用户拍板,敌人卡形态待办裁决完成):主角与敌人均为无框立绘+底座,零卡片**;主角与敌人站桩 = **HP 与状态效果的唯一展示面(2026-09-26 信息归属拍板)**;底部结算明细带(挖掘期收起高读 `log_band_h_dig`,结算期展开高读 `log_band_h_settle`);顶部中央轮次·阶段指示 |
| **C 角色信息** | 左下,宽读 `info_panel_w`,高 = 基准屏高 − B 区高 | 攻击值(含修正显示)、护甲 AC;秘宝槽、奇物列表(现行标题「特性」)——HP 条/RAP 池/状态效果槽/立绘占位/钱包行退役(2026-09-26 信息归属拍板:HP 与状态唯一展示面 = B 区站桩;RAP 迁 D 区 AP 条;GP/钥石迁出战斗屏) |
| **D 扫雷图** | 中下,宽 = 基准屏宽 − C 区宽 − 列间距,高同 C 区(2026-10-06 方案二:底行压缩全部由面板空档吸收,**棋盘原尺寸不缩、格子不变**;原右下面板删除后横向加宽,吸收其宽度) | 顶部 AP 条(高读 `ap_bar_h`:左——AP 点阵 + 停手按钮;中——RAP 点阵+数字〔自 C 区迁入,贴弃图刷新按钮,2026-09-26 拍板〕;右——图例按钮 + 弃图刷新按钮〔雷图右上角,2026-09-12 拍板〕);棋盘主体(居中,格边长见下方「棋盘格尺寸公式」);toast 槽(上缘内侧) |
| **B 战斗画面** | 全宽 × 270px(2026-10-06 方案二拍板:演出 ≈1/3 屏,原 252) | 主角站桩立绘(左)+ 兑现演出区(中)+ 敌人站桩立绘 ×1–3(右)——**全站桩形态(2026-09-20 用户拍板,敌人卡形态待办裁决完成):主角与敌人均为无框立绘+底座,零卡片**;主角与敌人站桩 = **HP 与状态效果的唯一展示面(2026-09-26 信息归属拍板)**;底部结算明细带(24px 摘要 / 结算期 84px 实时流);顶部中央轮次·阶段指示 |
| **C 角色信息** | 左下 240px × 530px(2026-10-06 方案二:548→530) | 攻击值(含修正显示)、护甲 AC;秘宝槽、奇物列表(现行标题「特性」)——HP 条/RAP 池/状态效果槽/立绘占位/钱包行退役(2026-09-26 信息归属拍板:HP 与状态唯一展示面 = B 区站桩;RAP 迁 D 区 AP 条;GP/钥石迁出战斗屏) |
| **D 扫雷图** | 中下 ~1036px × 530px(2026-10-06 方案二:548→530——压缩全部由面板空档吸收,**棋盘 438×438 原尺寸不缩、格子不变**;原右下面板删除后横向加宽,吸收其宽度) | 顶部 AP 条(44px:左——AP 点阵 + 停手按钮;中——RAP 点阵+数字〔自 C 区迁入,贴弃图刷新按钮,2026-09-26 拍板〕;右——图例按钮 + 弃图刷新按钮〔雷图右上角,2026-09-12 拍板〕);棋盘主体(居中,格边长见下方「棋盘格尺寸公式」);toast 槽(上缘内侧) |
> 布局为 B/C/D 三区——关卡进度条不在战斗屏(节点链进度常驻关卡画面,见 Navigation Position;字母不重编沿用 B/C/D)。
@@ -145,7 +145,11 @@
- N 的取值与每关板型的对应关系读[棋盘生成](../systems/排雷循环/棋盘生成.md),现行值的事实源是 `data/tables/board_gen.tres`。
- 数字字号 = max(18, ceil(格边长 × 0.5)),像素字体整数倍缩放(canvas_items stretch 保证清晰)。
算例不入文档:棋盘区可用面积随布局表与板型变化,不是契约值——现算并绘在整屏样张上,本文件不复写。
按 1280×800 基准档、棋盘区可用高 486px(D 区 530 减行动点条 44)推算的算例(随布局与板型变化,不是契约值):
| 板型 N | 10 | 12 | 13 | 14 |
|---|---|---|---|---|
| 格边长 | 43px | 36px | 33px | 30px |
### Component Inventory
@@ -192,13 +196,12 @@
### ASCII Wireframe
> **2026-09-26 信息归属拍板**:C 区 HP/RAP/状态槽/立绘占位/钱包行退役——HP 与状态唯一展示面 = B 区站桩,RAP 迁 D 区 AP 条贴弃图刷新按钮,GP/钥石迁出战斗屏;C 区改攻击数据框+两预留区。下方线框图已随拍板重绘;旧线框留档于 git 历史。
> **2026-09-23 停点前移批**:A 区关卡进度条退役,下方线框图为 B/C/D 三区(纵向空间划归 C/D 底行;关卡内节点进度常驻关卡画面,战斗屏零进度承载);旧四区线框留档于 git 历史。
> **几何标注已撤**:线框只示意结构,各区几何读 `game/data/tables/battle_layout.tres`,图中不标像素值。
> **2026-09-23 停点前移批**:A 区关卡进度条退役,下方线框图为 B/C/D 三区(28px 纵向空间划归 C/D 底行 520 → 548;关卡内节点进度常驻关卡画面,战斗屏零进度承载);旧四区线框留档于 git 历史。
> **2026-09-20 全站桩形态改版**:敌人卡形态裁决完成(卡片式退役、主角与敌人均站桩——无框立绘+底座、状态小图标挂血条下),下方线框图 B 区已随裁决重绘;旧卡片式线框留档于 git 历史。
```
1280 × 800 ────────────────────────────────────────────────────────────────────
┌─[B] 战斗画面(高读布局表) ──────────────────────────────────────────────────┐
┌─[B] 战斗画面 270px ──────────────────────────────────────────────────────────┐
│ /小人\ 第 3 轮 · 挖掘中 伏×2 伏×2 │
│ (落影) [ 兑现演出区 ] /蛆虫\ /蛆虫\ │
│ HP▓▓▓▓░ (结算期: 光带→弹道→受击) ①HP▓▓▓░ ②HP▓▓░░ │
@@ -206,7 +209,8 @@
│ ├─ 结算明细带 ────────────────────────────────────────────────────────────┤ │
│ │ ▸上轮: 主角攻击3→蛆虫-3 | 攻击+50%(1结算) | 警觉+1×2 … [展开明细▾] │ │ # 2026-09-17 数值压缩批:UI 示意文本同步(atk 7→3〔地精格数÷3 + L1 锚行;UX 示意按当前地精行值读〕、伏击 +3 → +1/层,幅度参考 data/tables/)
│ └────────────────────────────────────────────────────────────────────────┘ │
├─[C] 角色信息 ─────────┬─[D] 扫雷图 ───────────────────────────────────────────┤
├─[C] 角色信息 240px ───┬─[D] 扫雷图 ~1036px ──────────────────────────────────┤
│ 548px │ 548px │
│ ┌──────────────────┐ │ ┌─────────────────────────────────────────────────┐ │
│ │ 攻击 6 (+50%) │ │ │ AP ●●●●●○○○ 5/8 RAP ▓▓▓░ 3/8 │ │
│ │ 护甲 AC 12 │ │ │ [⏹停手] [?图例] [↻刷新 8|剩2] │ │
@@ -228,7 +232,7 @@
| State / Variant | Trigger | What Changes |
|---|---|---|
| **挖掘阶段(默认态)** | 轮次开始 / 结算完毕回 Active 相位 | B 区敌人待机(意图徽标全程可见——敌人系统 Rule 8⑤),D 区棋盘可交互,AP 条点亮;能量 2/10,唯悬停呼吸微光在动(§2.1) |
| **结算阶段** | round_end 触发(AP 尽/停手/清图) | D 区锁输入(棋盘去饱和 + 金橙色相偏移,明度不变,数字保持全对比);B 区播放五段编排演出(授予段 → 增益/减益段 → 战斗段 → 衰减段 → 判定段,战斗结算 段序冻结);明细带按结算期档展开(高读 `log_band_h_settle`)逐行滚动;阶段指示→"结算中";能量 7/10 |
| **结算阶段** | round_end 触发(AP 尽/停手/清图) | D 区锁输入(棋盘去饱和 + 金橙色相偏移,明度不变,数字保持全对比);B 区播放五段编排演出(授予段 → 增益/减益段 → 战斗段 → 衰减段 → 判定段,战斗结算 段序冻结);明细带展开 84px 逐行滚动;阶段指示→"结算中";能量 7/10 |
| **清图瞬间** | 全部安全格开完 | 金粒喷泉 + 彩纸 3–5s 即收(§2.3);清图奖励行强调呈现(H2,地图战斗资源×④——金框高亮行);随后换新图 install |
| **完美封印高光** | 战斗结算高光判定(全图标中/零误标类) | 全屏喝彩级反馈(§2.2 最高档):金光带加倍扫射 + 「完美封印 ×2」横幅——P2 爆发兑现节点(创意风险 #2 落点) |
| **封印标中高光** | 封印判定·标中 | 该印记化暴击弹道加重 + 明细行金框——chord 缺席的即时兑现补偿(创意风险 #3 落点) |
+3 -5
View File
@@ -1,6 +1,6 @@
# 营地场景 UX spec(camp)
> 状态:v2.2(现行)
> 状态:v2.1(现行)
> 事实源:本 spec(呈现)+ [元进度](../systems/跨局循环/元进度.md)(机制)
> 美术画法:[art-bible](../../art/art-bible.md) §6.2/§7.5;素材命名与路径:[asset-path-map](../../art/asset-path-map.md)
> 相邻:[level-map.md](level-map.md)(主菜单语境)
@@ -24,11 +24,9 @@
纵深=远景天空与远山/中景木围墙与营地大门/近景篝火与左右物件(作者纵深稿)。中景围墙是横贯全宽的密排竖木桩墙——木桩一根挨一根无缝、桩顶削尖、无横木,只在中段留缺口给大门。
**场景按要素分层(10 张)**:远景天空/远山/整片地面/门洞内的小路合成 `scene_camp_bg` 背景层(z0,不透明全幅);中景木围墙独立成 `scene_camp_wall` 围墙层(z1,透明底,门洞处留透明缺口);其余八个物件层各一张、由上往下叠。门洞是围墙层的透明缺口,透出背景层画的**一条通向远处的小路**,不可填死。
**中景带自画面底部往上约 2/3 处起**;近景物件允许压住围墙(围墙是纯背景板),**唯一例外=中间门洞必须留空、不被任何物件遮挡**。围墙形态、门扇比例与帐篷尺寸的现行口径归 [art-bible](../../art/art-bible.md) §6.2,本 spec 不再复述。
**中景带自画面底部往上约 2/3 处起**;近景物件允许压住围墙(围墙不可点,z 序在近景物件之下),**唯一例外=中间门洞必须留空、不被任何物件遮挡**。围墙形态、门扇比例与帐篷尺寸的现行口径归 [art-bible](../../art/art-bible.md) §6.2,本 spec 不再复述。
坐标草案与分层验收判据见 [camp-scene-prompts](../../art/camp-scene-prompts.md) §2.3/§4(10 张口径现行事实源);2026-10-22 美术方案讨论留痕见 [提案](../../../reports/提案-营地场景图美术生成方案-2026-10-22.md)。
坐标草案与验收判据见 [提案](../../../reports/提案-营地场景图美术生成方案-2026-10-22.md) §2.5/§5。
## 点选反馈(根态)
+1 -1
View File
@@ -81,7 +81,7 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab
**Category**: Data Display
**Used In**: 战斗画面区底部(结算期第一注视点)
**Description**: 轮末结算逐行流水实时呈现 + 可回看——「扫雷之因逐行兑现为战果」(P2 归因)的承载面。挖掘期收起为 1 行上轮摘要;结算期展开为实时流(带高读 `log_band_h_settle`);点击开完整回看 overlay。
**Description**: 轮末结算逐行流水实时呈现 + 可回看——「扫雷之因逐行兑现为战果」(P2 归因)的承载面。挖掘期收起为 1 行上轮摘要;结算期展开 84px 实时流;点击开完整回看 overlay。
**Specification**:
- 数据源:[战斗结算](../systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)步骤流(9 类行封闭协议(现行);公共字段:来源类别 / 来源格 / 轮次 / 显示名 / 数值 / 可空高光标记);**无来源行 = 断言失败**
+12 -29
View File
@@ -93,29 +93,11 @@ func request_dig(chain_size: int, clears_map: bool) -> bool:
_reject("phase")
return false
var cost := _cost("dig")
# dig_paid 发射+施加(付费开掘发起、动作经济闸收对价前——施加单点 _apply_dig_paid_hits):
# dig_paid 发射+施加(付费开掘发起、动作经济闸收对价前:
# counter_spend 命中=尝试加收对价并积计数;资源不足走 fallback/耗尽即止=只付常规基础对价。
var apply_res := _apply_dig_paid_hits(cost)
cost = int(apply_res["cost"])
if _ap < cost:
_reject("resource_face")
return false # 拒绝轮零入池(矿脉储能只在扣费受理后兑付——C2 成功才登记同款纪律)
if int(apply_res["refund"]) > 0:
cost = maxi(0, cost - int(apply_res["refund"])) # 返还=对价减免(净费不低于 0——免费开格不产生负对价)
var accepted := _spend_cost_and_arbitrate(cost, clears_map)
if accepted and int(apply_res["rap_gain"]) > 0:
grant_reserve_ap(int(apply_res["rap_gain"])) # 每次受理的付费挖格产 RAP(一次性入池——每动作恰一挖,连锁不二次)
return accepted
# dig_paid 命中施加单点(迭代 46 卡A T3 自 request_dig 迁出——分支本体逐字保留:
# counter_spend 加收对价并积计数/counter_add 施加〔双管镐 付费挖格计数〕/
# grant_ap 对价减免〔每第 2 次返 1 AP——mod_eq 闸在行 when〕/grant_reserve_ap 矿脉储能累计;
# 命中序=声明序不变。返回 {cost, refund, rap_gain} 交受理面消费)
func _apply_dig_paid_hits(base_cost: int) -> Dictionary:
var cost := base_cost
# counter_add〔双管镐 付费挖格计数〕/ grant_ap〔每第 2 次返 1 AP——对价减免,
# mod_eq 闸在行 when〕同面施加)
var dig_refund := 0
var rap_gain := 0
if _fx != null:
for h in _fx.dispatch(_ctx, "dig_paid", {"ap_cost": cost, "ap_surplus": _ap - cost}):
if String(h.get("kind", "")) == "counter_spend":
@@ -128,9 +110,12 @@ func _apply_dig_paid_hits(base_cost: int) -> Dictionary:
EffectEngine.counter_add(_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
elif String(h.get("kind", "")) == "grant_ap":
dig_refund += int(h.get("value", 0))
elif String(h.get("kind", "")) == "grant_reserve_ap":
rap_gain += int(h.get("value", 0))
return {"cost": cost, "refund": dig_refund, "rap_gain": rap_gain}
if _ap < cost:
_reject("resource_face")
return false
if dig_refund > 0:
cost = maxi(0, cost - dig_refund) # 返还=对价减免(净费不低于 0——免费开格不产生负对价)
return _spend_cost_and_arbitrate(cost, clears_map)
func _spend_cost_and_arbitrate(cost: int, clears_map: bool) -> bool:
@@ -211,15 +196,13 @@ func finish_round_stop(transfer: bool, transfer_pct: int = 100) -> void:
# 轮内刷新:唯一入口 =
# Active 相位——仅扣 RAP,相位与 AP 均不动。刷新不打断轮次、不触发结算;换图编排归 战斗结算
# (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 HUD框架发起)纪律不变。
# cost_override=折后价入参(迭代 46 卡A T3——BattleRefreshFlow 折价读数传入;缺省 -1=表基线逐位不变)
func request_refresh(cost_override: int = -1) -> bool:
func request_refresh() -> bool:
if _phase != Phase.ACTIVE:
_reject("phase")
return false
var cost := int(db.run.action_economy.rap_refresh_cost) if cost_override < 0 else cost_override
if _rap < cost:
if _rap < db.run.action_economy.rap_refresh_cost:
return false # EC13 资源不足拒绝——合法相位内资源拒绝,非编排错误
_rap -= cost
_rap -= db.run.action_economy.rap_refresh_cost
return true
-6
View File
@@ -168,12 +168,6 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
var bp_counters = _bp_store._bp_read("adventure_counters", bp)
if bp_counters is Dictionary:
adventure_counters = (bp_counters as Dictionary).duplicate(true)
return _resume_pending_or_start(bp)
# 续玩收束段(宿主侧——停点直回/已兑付重发/无停点续玩起步;R2 回放段的尾半,语句序与
# resume_adventure 内联时逐句相同):三选一停点态与待推进位经件读写,选路停点快照单跳传递。
func _resume_pending_or_start(bp: Dictionary) -> bool:
var bp_pending: Dictionary[Variant, Variant] = bp.get("bonus_pending", {}) as Dictionary
var bp_result: Dictionary[Variant, Variant] = bp.get("bonus_result", {}) as Dictionary
if not bp_pending.is_empty():
+17 -9
View File
@@ -31,9 +31,8 @@ signal settlement_snapshot(snap: Dictionary) ## 每结算末尾的全量状态
const SOURCE_EXEMPT_TYPES := ["decay", "verdict"]
# 行来源断言豁免集合(来源类别层)——事实来源是单位 / 清图而非棋盘格;
# fx_reveal=专长揭示掷格(落空行无真实格);
# fx_grant_gp=专长·GP 入账(钱包面而非棋盘面,行无格子可指);
# fx_board_refresh=成功刷新事实族(迭代 46 卡A——崩壁冲击群伤行源=换图动作而非棋盘格)
const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward", "fx_reveal", "fx_grant_gp", "fx_board_refresh"]
# fx_grant_gp=专长·GP 入账(钱包面而非棋盘面,行无格子可指)
const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward", "fx_reveal", "fx_grant_gp"]
var _facts: BattleFacts # 战斗事实件(battle_facts.gd——事实族 15 成员单点)
var _rounds: RoundCycle # 轮循环驱动件(settlement/round_cycle.gd——五段持有/段序/重入闸/摇号)
@@ -597,17 +596,26 @@ func _exit_distribution() -> void:
round_started.emit(round_index)
# 呈现层查询面(HUD 刷新按钮可用性)——挖掘期唯一通道(折价成本同源:判据归 BattleRefreshFlow)
# 呈现层查询面(HUD 刷新按钮可用性)——挖掘期唯一通道
func can_refresh() -> bool:
return BattleRefreshFlow.can_refresh(self)
return battle_result == "" \
and _facts.refresh_used < int(db.run.action_economy.refresh_cap_per_round) \
and action_economy.phase == action_economy.PHASE_ACTIVE \
and action_economy.rap >= int(db.run.action_economy.rap_refresh_cost)
# 玩家经 HUD框架发起、战斗结算 代理调用(禁止 RAP 满自动刷)。
# 挖掘期唯一通道:Active 相位立即换图继续挖(RAP 按折后价扣;AP 保留、相位不动、不触发结算);
# 非挖掘期请求一律拒绝——非清图轮恒同图。刷新门/折价扣费/换图/成功事实归 BattleRefreshFlow
# (迭代 46 卡A T3 拆出——编排器入口与唯一通道不变,C10 结构预算只降不升)。
# 挖掘期唯一通道:Active 相位立即换图
# 继续挖(RAP -8;AP 保留、相位不动、不触发结算);非挖掘期请求一律拒绝——非清图轮恒同图。
func player_request_refresh() -> bool:
return BattleRefreshFlow.execute(self)
if not can_refresh():
return false
if not action_economy.request_refresh():
return false
_facts.refresh_used += 1
_reward_economy.retire_mapping() # 旧图批次退役(地图战斗资源 Ready→retired;E6 新批重置前提)
minesweeper.retire_board(true) # 旧板退役(轮内通道)
return _install_and_reset(true) # 当场换图——失败即编排违例 fail-fast
func _battle_over(result: String) -> void:
+5 -15
View File
@@ -1,9 +1,8 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 数值表单点访问入口:全部 .tres 表的加载、校验诊断与门面(垂直切片验证:改表即改玩法,数值零硬编码)。
## 玩法代码禁止绕过本单点自建 load();行查询归各表类(表管自己的行),
## 表持有按域收进四容器(combat 战斗编排域/run 冒险 runner 域/crossrun 跨局元进度域/
## presentation 表现层布局域),消费面经 db.<容器>.<表> 直连。
## 「经 ConfigDB 单点读取」=经其持有的容器与表对象(架构 :88)。
## 表持有按域收进三容器(combat 战斗编排域/run 冒险 runner 域/crossrun 跨局元进度域),
## 消费面经 db.<容器>.<表> 直连。「经 ConfigDB 单点读取」=经其持有的容器与表对象(架构 :88)。
## 冒烟脚本可显式实例化本类(headless --script 亦会加载 autoload——共享 .tres 上的 resolver 归最后装载的实例);
## 加载失败 is_ready=false 加 fatal 封印,调用方须查。
class_name ConfigDb
@@ -29,13 +28,11 @@ const TABLE_MANIFEST := {
"adventure": "adventure.tres", # AdventureTable(大奖励与钥石行值单点)
"feats": "feats.tres", # FeatTable 规则改写型特性(定义表 Rule 4 豁免——TreasureItemTable 先例;空表合法态=主时间线零扰动)
"forge": "forge.tres", # ForgeTable——两根类型轴默认值+六路线行族;攻防所有权切分唯一数据面
"battle_layout": "battle_layout.tres", # BattleLayoutTable——战斗屏分区几何与明细带高度单行
}
var combat: ConfigDomainCombat # 战斗编排域七表(board_gen/variation/status_effects/enemy/skill/hero/settlement)
var run: ConfigDomainRun # 冒险 runner 域七表(level/difficulty/level_gen_params/reward/adventure/action_economy/shop)
var crossrun: ConfigDomainCrossrun # 跨局元进度域五表(meta/meta_tree/treasure_items/feats/forge)
var presentation: ConfigDomainPresentation # 表现层布局域一表(battle_layout)
var is_ready := false
var _diagnostics: Array[Dictionary] = [] # {severity, category, message}
@@ -48,7 +45,6 @@ func _init() -> void:
combat = ConfigDomainCombat.new()
run = ConfigDomainRun.new()
crossrun = ConfigDomainCrossrun.new()
presentation = ConfigDomainPresentation.new()
_resolver = ConfigDbResolver.new(push_diag,
func() -> Variant: return combat.board_gen.rows, func() -> Variant: return combat.enemy.rows, func() -> Variant: return run.difficulty) # 协作类持门面引用(诊断/致命通道回呼)
@@ -121,14 +117,7 @@ func load_tables(root: String, collect_only: bool) -> bool:
if has_errors and not collect_only:
# 游戏通道:致命(Rule 6)——is_ready 恒 false,调用方(主场景)中止构建
return false
# 容器装配(表行按域收进四容器+resolver 注入)归私有协函数——load_tables 只留加载/校验/致命门面
_assign_loaded(loaded)
is_ready = not has_errors
return not has_errors
# 容器装配协函数:20 表成员按域收进四容器(2026-10-27 全清设计定稿,2026-10-31 增表现层布局域)
func _assign_loaded(loaded: Dictionary) -> void:
# 容器装配(19 表成员按域收进三容器——2026-10-27 全清设计定稿)
combat.board_gen = loaded.get("board_gen")
combat.variation = loaded.get("variation")
combat.status_effects = loaded.get("status_effects")
@@ -148,11 +137,12 @@ func _assign_loaded(loaded: Dictionary) -> void:
crossrun.treasure_items = loaded.get("treasure_items")
crossrun.feats = loaded.get("feats")
crossrun.forge = loaded.get("forge")
presentation.battle_layout = loaded.get("battle_layout")
# resolver 协作件注入三表(resolve 委托承载——resolver 文件与键零搬移)
combat.board_gen._resolver = _resolver
combat.enemy._resolver = _resolver
run.difficulty._resolver = _resolver
is_ready = not has_errors
return not has_errors
# 跨表校验(悬空引用 / 池护栏——全部加载期致命)本体:
-6
View File
@@ -1,6 +0,0 @@
## 表现层布局域容器(D2 域容器——config_db 表持有按域收敛):战斗屏布局一表类型化持有。
## 零方法零行为——纯持有结构;「经 ConfigDB 单点读取」=经其持有的容器与表对象(架构 :88 定稿注记)。
class_name ConfigDomainPresentation
extends Resource
var battle_layout: BattleLayoutTable
@@ -1 +0,0 @@
uid://bay1b1s3lrd1e
+10 -28
View File
@@ -460,8 +460,8 @@ func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary
# 敌方伤害技:掷点 → 折扣/暴击 → 克制点数(attack_type vs 主角护甲类型——
# 无类型恒中性 delta 0,写法同 hero_attack 的 _counter_delta 消费位)→ hero_defense×enemy_output
# 池乘区 → 下限 → +rider(后置必中附加)→ 交付 → commit_action(作废单不 commit——死亡即不动手)。
# 掷点算术归 _enemy_roll;damage_taken 统一发射+消费归 _emit_hero_damage_taken(发射在本函数——
# 与减伤落快照的先后序固定;「掷点后、交付前」消费时点不变)。
# 掷点算术归 _enemy_roll;damage_taken 施加面归 _enemy_shield_apply(发射在本函数——
# 与减伤落快照的先后序固定)。
func _resolve_enemy_skill(t: Dictionary) -> void:
var hero: Dictionary[Variant, Variant] = units.hero_unit()
var roll := _enemy_roll(t)
@@ -469,8 +469,9 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
# damage_taken 发射(kind=enemy_skill;铁壁整备减伤订阅面:shield 加法族合计 value×per(charge_layers),
# 稳健拓荒树规则同面(rebuild 合成订阅);减后过下限保底。damage_reduce〔背水 hp_ratio when〕/
# counter_add〔保险丝/石肤〕/ac_bonus〔石肤〕同面施加;hp_ratio fact 由 _fx_fact 补充事实面回落提供)
var dt_res := _emit_hero_damage_taken(total, "enemy_skill", String(t.context.get("skill_id", "")), t.origin_cell, int(t.round_index))
total = int(dt_res["total"])
var dt_hits := _fx_hits("damage_taken", {"kind": "enemy_skill", "amount": total,
"source": String(t.context.get("skill_id", "")), "round": int(t.round_index)})
total = int(_enemy_shield_apply(dt_hits, total)["total"])
units.apply_damage(hero, total)
var payload: Dictionary[Variant, Variant] = {"target": "hero", "skill_id": String(t.context.get("skill_id", "")), "baseline": int(t.context.get("baseline", 0)),
"roll": int(roll["roll"]), "ac": int(roll["threshold"]), "tier": int(roll["discount"]), "attack_type": String(t.attack_type)}
@@ -501,22 +502,9 @@ func _enemy_roll(t: Dictionary) -> Dictionary:
return {"roll": int(roll_read.roll), "threshold": threshold, "crit": crit, "discount": discount, "total": total}
# 全局受伤发射单点(迭代 46 卡A T2——所有主角承伤路径唯一发射口:敌方攻击/地形/危险格/错误封印
# 资源直伤(后三者经 board_flat_hero 单据进入)都经此 dispatch 一次 damage_taken 并消费一次;
# 同一伤害单据重复发射由 context.hero_damage_consumed 标记闸拦(地形路径在开单点标记)。
# payload 必含 kind/amount/source/cell/round/hp_ratio;返回 {total, hits} 交调用侧按减后值开单/交付)。
func _emit_hero_damage_taken(amount: int, damage_kind: String, source: String, cell: Vector2i, ri: int) -> Dictionary:
var hu: Dictionary[Variant, Variant] = units.hero_unit()
var payload: Dictionary[Variant, Variant] = {"kind": damage_kind, "amount": amount, "source": source,
"cell": cell, "round": ri, "hp_ratio": int(round(100.0 * float(int(hu.get("hp", 0))) / float(maxi(1, int(hu.get("max_hp", 1))))))}
var hits := _fx_hits("damage_taken", payload)
return {"total": int(_apply_hero_damage_effects(hits, amount)["total"]), "hits": hits}
# damage_taken 唯一消费族(组合次序冻结:shield 加法族合计→damage_reduce 逐条减量〔背水/石层缓冲〕→
# ac_bonus 转编排器单点〔石肤 AC 累积归 set_hero_ac_override 消费面〕→counter_add 施加〔保险丝/石肤/
# 换图系临时计数夹具〕。返回 {total, shield})。
func _apply_hero_damage_effects(hits: Array, total: int) -> Dictionary:
# damage_taken 施加面(shield 加法族合计;damage_reduce 逐条减量〔背水〕;ac_bonus 转编排器单点
# 〔石肤 AC 累积归 set_hero_ac_override 消费面〕;counter_add 施加)。返回 {total, shield}。
func _enemy_shield_apply(hits: Array, total: int) -> Dictionary:
var shield := 0
for h in hits:
match String(h.get("kind", "")):
@@ -577,15 +565,9 @@ func _resolve_board_blast(t: Dictionary) -> void:
_outcomes[int(t.ticket_id)] = "redeemed"
# 棋盘主角直伤(反噬/治疗包负向/危险格/错误封印资源直伤):无类型必中,下限保底;context 键并入行载荷。
# damage_taken 全局发射点(迭代 46 卡A T2——单据未标 hero_damage_consumed 时在下限前发射并消费一次;
# 地形路径已在授予段开单点发射并标记,此处跳过防重复)
# 棋盘主角直伤(反噬/治疗包负向/地形变异):无类型必中,下限保底;context 键并入行载荷
func _resolve_board_flat_hero(t: Dictionary) -> void:
var dealt: int = int(t.base_value)
if not bool((t.get("context", {}) as Dictionary).get("hero_damage_consumed", false)):
var res_flat := _emit_hero_damage_taken(dealt, "board_flat_hero", String(t.get("source_class", "")), t.origin_cell, int(t.round_index))
dealt = int(res_flat["total"])
dealt = maxi(int(db.combat.settlement.damage_roll.damage_floor), dealt) # 伤害下限全来源(减后过下限)
var dealt: int = maxi(int(db.combat.settlement.damage_roll.damage_floor), int(t.base_value)) # 伤害下限全来源
units.apply_damage(units.hero_unit(), dealt)
var extra: Dictionary[Variant, Variant] = {"target": "hero"}
for k in t.context.keys():
+4 -12
View File
@@ -12,15 +12,11 @@ extends RefCounted
const EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "attack_commit", "clamp_check",
"armor_roll", "kill", "damage_taken", "enemy_hp_threshold", "round_start", "seal_settle",
"round_end", "reward_roll", "treasure_used", "charge_full", "dig_stop", "battle_won", "seal_missed",
"board_installed",
# 换图系双事件(refresh_request=玩家请求刷新〔省力周转 apply_discount 订阅面——折扣只改门槛
# 与扣费不改拒绝判据〕;board_refreshed=成功刷新事实〔换图系七件订阅主挂点——装新板后才发射〕)
"refresh_request", "board_refreshed"]
"board_installed"]
## 扫雷侧事件集(🔴 流派扫雷触发铁律新形态:每流派 ≥1 行订阅本集事件——schema 加载校验。
## 原 MINE_HOOKS 的同义迁移:挖掘/封印/轮末结算面事件+轮内刷新面〔换图系〕)
const MINE_EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "seal_settle", "round_end", "reward_roll",
"refresh_request", "board_refreshed"]
## 原 MINE_HOOKS 的同义迁移:挖掘/封印/轮末结算面事件)
const MINE_EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "seal_settle", "round_end", "reward_roll"]
## 效果类型词汇(解释器 switch 闭集——词表实数以 EFFECT_KINDS 常量为准,含共享行拆型:
## grant_gp/bias_wallet、counter_add/counter_spend、reveal_tiles/remove_mines/refresh_pick、
@@ -44,11 +40,7 @@ const EFFECT_KINDS := ["add_ticket_rider", "modify_ticket", "damage_mult", "open
"mine_count_preview",
# forgive_seal_miss=完美保险(本场首次封印落空不计入完美
# 封印判定——订阅 seal_missed 的豁免效果,消费点在授予段;feats.tres 首例 domain=battle 行)
"forgive_seal_miss",
# 换图系两效果(grant_reserve_ap=矿脉储能·付费挖格产 RAP〔消费点在行动经济 dig_paid
# 受理后一次性入池〕;apply_discount=省力周转·刷新成本折扣〔mode=refresh cost_mult 百分比,
# floor 取整——只改门槛与扣费、不改拒绝判据〕)
"grant_reserve_ap", "apply_discount"]
"forgive_seal_miss"]
## fact 条件词汇(封闭枚举——payload 同名键直读面〔fact 取值序 payload
## 优先——effect_engine._fact_value 既有机制〕。new_resource_type=首开资源类型〔地质学家〕;
-6
View File
@@ -365,12 +365,6 @@ func outcome_of(ticket_id: int) -> String:
return "redeemed" if ticket_id < 0 else damage.outcome(ticket_id)
# 延迟单据入队(迭代 46 卡A T4——成功刷新的崩壁冲击群伤单走案C 同队列:授予段闸外唯一入口,
# 战斗段末 _board_deferred_settle 按开单序补提交统一结账;类变量零增员——复用 defer.queue)
func defer_ticket(t: Dictionary) -> void:
(defer.queue as Array).append(t)
# 主角直伤单据(反噬/治疗包负向——无类型必中,下限保底在控制器管线内)
func _submit_flat_hero(display: String, power: int, att: Dictionary, sync: bool) -> String:
var t := damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, String(att.get("source_class", "")), display,
@@ -1,43 +0,0 @@
class_name BattleRefreshFlow
extends RefCounted
## 换图刷新流程件(迭代 46 卡A T3/T4 自编排器拆出):刷新可用性门、折价扣费、换图执行与成功事实登记。
## 拆分边界(需求卡片 C13):本件只负责可用性、扣费、换图与成功事实登记——七件效果的算法留在
## 效果引擎、授予段与伤害控制器。调用路径=呈现层→编排器单行转发→本件→行动经济/装板壳/授予段。
## 反自动刷新(C11):只经玩家入口发起,禁 RAP 满自动刷——编排器公开面单行转发,无自动调用点。
## 成功序钉死(T4):折价扣 RAP→refresh_used+1→旧资源退役→旧板退役→装新板→授予段成功事实发射;
## 装板失败返回 false 且零效果——成功事实只在新板就位后发射。
## 折价成本读数(refresh_request 命中省力周转 apply_discount——只认 kind==apply_discount 且
## mode==refresh,取声明序第一命中,floor(base×cost_mult/100);无命中=表基线逐位不变。
## can_refresh 与 execute 同源取数〔V3——can 与执行同源判断〕;折扣只改变门槛与扣费,
## 不改变拒绝判据〔C2——每轮上限与相位判据照旧〕)
static func _effective_refresh_cost(oc: BattleOrchestrator) -> int:
var base := int(oc.db.run.action_economy.rap_refresh_cost)
for h in oc.fx_host().fx_emit("refresh_request", {"mode": "refresh", "base": base}):
if String(h.get("kind", "")) == "apply_discount" and String(h.get("mode", "")) == "refresh":
return int(floor(float(base) * float(int(h.get("cost_mult", 100))) / 100.0))
return base
## 可用性门(HUD 刷新按钮同源判据):战斗未终局、每轮上限未满、Active 相位、RAP 抵折后价。
static func can_refresh(oc: BattleOrchestrator) -> bool:
return oc.battle_result == "" \
and oc._facts.refresh_used < int(oc.db.run.action_economy.refresh_cap_per_round) \
and oc.action_economy.phase == oc.action_economy.PHASE_ACTIVE \
and oc.action_economy.rap >= _effective_refresh_cost(oc)
## 执行(玩家请求刷新唯一通道体):扣费受理后继续;扣费拒绝/门拒零效果零事实。
static func execute(oc: BattleOrchestrator) -> bool:
if not can_refresh(oc):
return false
if not oc.action_economy.request_refresh(_effective_refresh_cost(oc)):
return false
oc._facts.refresh_used += 1
oc._reward_economy.retire_mapping() # 旧图批次退役(地图战斗资源 Ready→retired;E6 新批重置前提)
oc.minesweeper.retire_board(true) # 旧板退役(轮内通道)
if not oc._install_and_reset(true): # 当场换图——失败即编排违例 fail-fast(零成功事实)
return false
oc._rounds._grant_seg.apply_board_refreshed(oc.round_index) # 成功事实:新板就位后一次发射
return true
@@ -1 +0,0 @@
uid://d2kjxlt5b2eqv
+24 -50
View File
@@ -6,8 +6,7 @@ extends RefCounted
# 效果施加归 BattleFxHost(段持三引用直调,零转发壳)——
# 本段只搬运时序,零算法改动(🔴 零行为变更契约:同种子完整局输出逐位一致)。
# run 编排骨架+批私有族(_reveal_batch/_terrain_batch/_seal_batch/_round_end_apply,段内施加面
# 再拆 _reveal_safe_apply/_reveal_mine_apply/_seal_hit_effects 等单面函数);地形 damage_taken
# 消费随迭代 46 卡A T2 并入伤害控制器全局消费族(本段零本地减伤函数);
# 再拆 _reveal_safe_apply/_reveal_mine_apply/_terrain_damage_reduce/_seal_hit_effects 等单面函数);
# kind 分派单点化(P1-1 同批):counter_add 五位三行组 / grant_material 三位兑付块
# 收 _apply_counter_hit / _apply_material_hit 两助手(分支本体逐字保留在助手内)。
@@ -33,27 +32,6 @@ func run(ri: int) -> void:
_round_end_apply(ri)
# 成功刷新事实发射+施加(迭代 46 卡A T4——BattleRefreshFlow 装新板后单点调用,每成功刷新恰一次;
# 拒绝刷新零事实〔C2〕。命中消费三面:counter_add→既有单点落账〔蓄力周转/石层缓冲/坑道小憩 计数〕;
# grant_gp→战斗内缓冲 escrow 入账+fx_grant_gp 行〔矿道红利——不绑清图奖励,C7〕;
# open_ticket channel=blast→崩壁冲击群伤单延迟入队〔案C 同队列,战斗段末按存活目标结账〕)
func apply_board_refreshed(ri: int) -> void:
var br_hits := fx_host.fx_emit("board_refreshed", {"round": ri})
for h in br_hits:
if String(h.get("kind", "")) == "counter_add":
_apply_counter_hit(h)
for h in br_hits:
if String(h.get("kind", "")) == "grant_gp":
reward_grant.escrow += int(h.get("value", 0))
oc._emit_row({"type": "effect", "source_class": "fx_grant_gp", "origin_cell": Vector2i(-1, -1)}, ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow})
for h in br_hits:
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "blast":
var bb_t := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "fx_board_refresh", "崩壁冲击",
{"channel": "board", "base_value": int(h.get("value", 0)), "target_rule": DamageController.TARGET_ALL_ALIVE,
"ri": ri, "origin_cell": Vector2i(-1, -1)})
reward_grant.defer_ticket(bb_t)
# 开格/踩雷兑付批(轮 facts_reveals 资源事件序:safe 正向乘区兑付 / mine_hit 减半负向兑付)
func _reveal_batch(ri: int) -> void:
for evt in oc._facts.reveals:
@@ -189,22 +167,37 @@ func _terrain_batch(ri: int) -> void:
if String(vrow.get("effect", "")) != "flat_damage":
continue # 触发词表按 effect 路由:flat_damage=on_open 兑现;hide_number/on_open 外组合 = 数据行新词表未接线(显式忽略面)
# 地形变异伤害单据——无类型必中,下限保底在控制器管线内
# damage_taken 统一发射(迭代 46 卡A T2——地形路径并入全局消费族 _emit_hero_damage_taken:
# 发射在单据开立前、减后值开单,组合消费含 shield〔全局受伤语义〕;
# hero_damage_consumed 标记入单据 context——战斗段末 _resolve_board_flat_hero 跳过重复发射;
# grant_material〔荆棘/地脉〕移到单据提交后(兑付值序一致——收益不因减伤变化))
# damage_taken 发射在单据开立前:damage_reduce〔背水——hp_ratio when
# 面〕先收集再按减后值开单;counter_add〔保险丝/石肤〕+ ac_bonus〔石肤〕照常施加;
# grant_material〔荆棘/地脉〕移到单据提交后(兑付值序一致——收益不因减伤变化)
var dt_amount := int(vrow.get("effect_value", 0))
var dt_res := oc._damage._emit_hero_damage_taken(dt_amount, "terrain", String(vid), evt.cell, ri)
dt_amount = int(dt_res["total"])
var dt_hits := fx_host.fx_emit("damage_taken", {"kind": "terrain", "amount": dt_amount,
"source": String(vid), "cell": evt.cell, "round": ri, "terrain_kind": String(vid),
"hp_ratio": int(round(100.0 * float(int(oc._enemy_system.hero_unit().get("hp", 0))) / float(maxi(1, int(oc._enemy_system.hero_unit().get("max_hp", 1))))))})
dt_amount = _terrain_damage_reduce(dt_hits, dt_amount)
var vt := oc._damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, "地形变异", String(vrow.get("display_name", vid)),
{"channel": "board", "base_value": dt_amount, "target_rule": DamageController.TARGET_HERO,
"ri": ri, "origin_cell": evt.cell, "context": {"variant_id": String(vid), "hero_damage_consumed": true}})
"ri": ri, "origin_cell": evt.cell, "context": {"variant_id": String(vid)}})
reward_grant.submit(vt, false)
for h in (dt_res["hits"] as Array):
for h in dt_hits:
if String(h.get("kind", "")) == "grant_material":
_apply_material_hit(h, evt.cell, ri)
# damage_taken 施加面(damage_reduce 减量收集〔背水——定值〕/ac_bonus〔石肤〕/counter_add〔保险丝/石肤〕;
# 返回减后 amount 交开单消费)
func _terrain_damage_reduce(dt_hits: Array, dt_amount: int) -> int:
for h in dt_hits:
match String(h.get("kind", "")):
"damage_reduce":
dt_amount = maxi(0, dt_amount - int(h.get("value", 0)))
"ac_bonus":
fx_host.apply_ac_bonus(h)
"counter_add":
_apply_counter_hit(h)
return dt_amount
# 封印判定批+战封印军规开单段(封印流:seal_judged 批后统一开单:轮末每面封印命中对最前存活敌
# 结算 board_typed 必中伤害〔复用零新配方;「封印所在巢穴敌」简化为 default_target 最前存活敌——
# 台账披露待追认〕;完美布防=本轮封印批全中(hit>0 且 miss==0)时封印伤 ×mult——全对推理奖励)。
@@ -266,7 +259,6 @@ func _seal_strike_value(fs_fx: Array, fs_hits: int, fs_misses: int) -> Dictionar
func _round_end_apply(ri: int) -> void:
var re_fx := fx_host.fx_emit("round_end", {"round": ri})
_round_end_blast_tickets(re_fx, ri)
_round_end_heal_value_counter(re_fx, ri) # 迭代 46 卡A T5——坑道小憩恢复兑付(先于清零行消费层值,声明序=heal 行在 counter set 行前)
for h in re_fx:
if String(h.get("kind", "")) == "counter_add":
_apply_counter_hit(h)
@@ -286,24 +278,6 @@ func _round_end_apply(ri: int) -> void:
oc._damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界)
# round_end heal value_counter 兑付(迭代 46 卡A T5——坑道小憩:层值=本轮成功刷新累计恢复点,
# units.heal 封顶、满血发未生效行;层清零由同事件 counter_add set 行承载〔声明序在后〕——本面
# 先于 counter_add 遍历消费层值即保序。行源=成功刷新事实族(无棋盘格——fx_board_refresh 豁免类))
func _round_end_heal_value_counter(re_fx: Array, ri: int) -> void:
for h in re_fx:
if String(h.get("kind", "")) != "heal" or String(h.get("value_counter", "")) == "":
continue
var layers := EffectEngine.counter(fx_host.counters_ctx, String(h.get("value_counter", "")))
if layers <= 0:
continue
var hu: Dictionary[Variant, Variant] = oc._enemy_system.hero_unit()
if int(hu.get("hp", 0)) >= int(hu.get("max_hp", 1)):
oc._emit_row({"type": "reward_ineffective", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 未生效(满血)", 0, {"batch_key": String(h.get("value_counter", ""))})
continue
var healed: int = oc._enemy_system.heal(hu, layers)
oc._emit_row({"type": "heal", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 回复", healed, {"target": "hero"})
# round_end blast 通道反打单(per-counter 积层读数×value=反打总量;source_class 用中性名,
# per-counter 反打通道保留泛用面;案C 暂存战斗段末兑付)
func _round_end_blast_tickets(re_fx: Array, ri: int) -> void:
-30
View File
@@ -1,30 +0,0 @@
## 战斗屏布局表 schema:分区几何与明细带高度的单行类型化读取(呈现层布局配置,非玩法数值)。
## 消费面 = BattleStageView / BattleLogView / HeroInfoPanelView / ApBarView(经 ConfigDb.presentation.battle_layout);
## 文档只写结构关系与表名,不复制数值。validate() 违例加载期致命(fail-fast)。
class_name BattleLayoutTable
extends Resource
@export var stage_h: int = 252 # B 区高(全宽战斗画面,含底部明细带)
@export var info_panel_w: int = 240 # C 区宽(左下角色信息列)
@export var ap_bar_h: int = 44 # D 区 AP 条高
@export var log_band_h_dig: int = 24 # 明细带挖掘期收起高(单行上轮摘要)
@export var log_band_h_settle: int = 84 # 明细带结算期展开高(实时结算流)
func validate() -> Array:
var errs: Array[Dictionary] = []
if stage_h < 120 or stage_h > 600:
errs.append({"category": "domain", "message": "stage_h 越域 [120,600]: %d" % stage_h})
if info_panel_w < 120 or info_panel_w > 480:
errs.append({"category": "domain", "message": "info_panel_w 越域 [120,480]: %d" % info_panel_w})
if ap_bar_h < 24 or ap_bar_h > 96:
errs.append({"category": "domain", "message": "ap_bar_h 越域 [24,96]: %d" % ap_bar_h})
if log_band_h_dig < 16 or log_band_h_dig > 80:
errs.append({"category": "domain", "message": "log_band_h_dig 越域 [16,80]: %d" % log_band_h_dig})
if log_band_h_settle < 24 or log_band_h_settle > 160:
errs.append({"category": "domain", "message": "log_band_h_settle 越域 [24,160]: %d" % log_band_h_settle})
if log_band_h_settle < log_band_h_dig:
errs.append({"category": "cross_field", "message": "log_band_h_settle(%d) < log_band_h_dig(%d)——展开态不得矮于收起态" % [log_band_h_settle, log_band_h_dig]})
if log_band_h_settle >= stage_h:
errs.append({"category": "cross_field", "message": "log_band_h_settle(%d) ≥ stage_h(%d)——明细带不得占满 B 区" % [log_band_h_settle, stage_h]})
return errs
@@ -1 +0,0 @@
uid://b3jh66yq4b7rp
-11
View File
@@ -1,11 +0,0 @@
[gd_resource type="Resource" script_class="BattleLayoutTable" load_steps=2 format=3]
[ext_resource type="Script" path="res://data/schemas/battle_layout_table.gd" id="1"]
[resource]
script = ExtResource("1")
stage_h = 252
info_panel_w = 240
ap_bar_h = 44
log_band_h_dig = 24
log_band_h_settle = 84
-210
View File
@@ -869,215 +869,5 @@ rows = [
}
}
]
},
{
"feat_id": "vein_reserve",
"display_name": "矿脉储能",
"rarity": "excellent",
"cap": 1,
"desc": "挖格储能:每次受理的付费挖格 +1 储备点(RAP)",
"triggers": [
{
"on": "dig_paid",
"do": {
"kind": "grant_reserve_ap",
"value": 1
}
}
]
},
{
"feat_id": "easy_swap",
"display_name": "省力周转",
"rarity": "excellent",
"cap": 1,
"desc": "刷新半价:弃图刷新成本为基线 50%",
"triggers": [
{
"on": "refresh_request",
"do": {
"kind": "apply_discount",
"mode": "refresh",
"cost_mult": 50
}
}
]
},
{
"feat_id": "wall_blast",
"display_name": "崩壁冲击",
"rarity": "excellent",
"cap": 1,
"desc": "刷后群伤:成功刷新后全体存活敌各受 2 点",
"triggers": [
{
"on": "board_refreshed",
"do": {
"kind": "open_ticket",
"channel": "blast",
"value": 2
}
}
]
},
{
"feat_id": "mine_profit",
"display_name": "矿道红利",
"rarity": "rare",
"cap": 1,
"desc": "刷后GP:每次成功刷新固定 +1 GP",
"triggers": [
{
"on": "board_refreshed",
"do": {
"kind": "grant_gp",
"value": 1
}
}
]
},
{
"feat_id": "stone_mitigation",
"display_name": "石层缓冲",
"rarity": "common",
"cap": 1,
"desc": "当轮减伤:本轮成功刷新后,每次受伤固定 −2(多刷不叠加)",
"triggers": [
{
"on": "board_refreshed",
"do": {
"kind": "counter_add",
"counter_id": "round_swap_guard",
"value": 1
}
},
{
"on": "damage_taken",
"when": [
{
"fact": "counter",
"id": "round_swap_guard",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "damage_reduce",
"value": 2
}
}
]
},
{
"feat_id": "charge_rotation",
"display_name": "蓄力周转",
"rarity": "excellent",
"cap": 1,
"desc": "刷后蓄力:每次刷新 +1 层(上限 3),下一次攻击消费全部层数",
"triggers": [
{
"on": "board_refreshed",
"when": [
{
"fact": "counter",
"id": "swap_charge_layers",
"op": "lt",
"value": 3
}
],
"do": {
"kind": "counter_add",
"counter_id": "swap_charge_layers",
"value": 1
}
},
{
"on": "attack_commit",
"when": [
{
"fact": "counter",
"id": "swap_charge_layers",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "attack_bonus",
"value_counter": "swap_charge_layers"
}
},
{
"on": "attack_commit",
"when": [
{
"fact": "counter",
"id": "swap_charge_layers",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "counter_add",
"counter_id": "swap_charge_layers",
"set": 0
}
}
]
},
{
"feat_id": "tunnel_rest",
"display_name": "坑道小憩",
"rarity": "common",
"cap": 1,
"desc": "当轮恢复:每次刷新累计 1 点(上限 3),本轮轮末合计回复",
"triggers": [
{
"on": "board_refreshed",
"when": [
{
"fact": "counter",
"id": "round_swap_rest",
"op": "lt",
"value": 3
}
],
"do": {
"kind": "counter_add",
"counter_id": "round_swap_rest",
"value": 1
}
},
{
"on": "round_end",
"when": [
{
"fact": "counter",
"id": "round_swap_rest",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "heal",
"value_counter": "round_swap_rest"
}
},
{
"on": "round_end",
"when": [
{
"fact": "counter",
"id": "round_swap_rest",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "counter_add",
"counter_id": "round_swap_rest",
"set": 0
}
}
]
}
]
+1 -1
View File
@@ -634,7 +634,7 @@ func _refresh_presentations() -> void:
func _sync_settlement_visuals() -> void:
# 结算阶段(round_end → 下一轮):D 区去饱和+金橙偏移锁输入(明度不变)+ 明细带展开(高度读 battle_layout)
# 结算阶段(round_end → 下一轮):D 区去饱和+金橙偏移锁输入(明度不变)+ 明细带展开 84px
var settling := _is_settling()
if settling and not run_scope.was_settling:
ui.battle.settlement_fx.play_round_sweep(ui.battle.board_zone.board_view.get_global_rect()) # 结算轮光带——时序敏感演出触发,例外留在组合根
+1 -1
View File
@@ -1,7 +1,7 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 组合根覆盖层流程承接件:设置/状态面板/生涯页/选档界面/图例/钱包事件的开合与数据投喂。
## 本件是 main.gd 同名函数的逐字搬运(成员访问改 root 前缀)——main 侧留同名薄委托,探针 main.X 路径零改动。
## 全部开合走组合根模态编排口(root.ui.modals.flow.open/close);本件零成员态。
## 全部开合走组合根模态单口(root._open_modal/_close_modal);本件零成员态。
extends RefCounted
static func _open_settings(root: Variant) -> void:
+7 -9
View File
@@ -6,8 +6,8 @@ extends RefCounted
# 战斗屏三区(B 战斗画面 / C·D 两列——节点链
# 进度由关卡画面本体(选路覆盖层整图)承载,纵向空间归 C/D 底行;
# 无顶栏进度条、无道具面板区)。分区几何读 ConfigDb.presentation.battle_layout。
# 进度由关卡画面本体(选路覆盖层整图)承载,28px 纵向空间拨给 C/D 底行 520→548;
# 无顶栏进度条、无道具面板区)。
# + 结算演出覆盖层(A–D 之上、模态之下——跨区弹道/光带载体)。
# 返回句柄字典:{stage, log_view, info_panel, board_zone, board_view, ap_bar, settlement_fx}
# (返回句柄不含 gp_label/keystone_label——GP/钥石不在战斗屏呈现)
@@ -17,22 +17,20 @@ static func build_battle_ui(root: Control, db: ConfigDb) -> Dictionary:
root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
root.add_theme_constant_override("separation", 0)
var layout: BattleLayoutTable = db.presentation.battle_layout
# B 区:战斗画面(全宽 × battle_layout.stage_h——常驻双舞台,布局决策 D1)
var stage := BattleStageView.new(db, layout)
# B 区:战斗画面(全宽 × 252px——常驻双舞台,布局决策 D1)
var stage := BattleStageView.new(db)
root.add_child(stage)
# C/D 两列(高度 = 基座高 − B 区高——底行承载)
# C/D 两列(548px——含顶栏让出的 28px 纵向空间)
var bottom := HBoxContainer.new()
bottom.size_flags_vertical = Control.SIZE_EXPAND_FILL
bottom.add_theme_constant_override("separation", 4)
root.add_child(bottom)
var info_panel := HeroInfoPanelView.new(layout)
var info_panel := HeroInfoPanelView.new()
bottom.add_child(info_panel)
var board_zone := BoardZoneView.new(layout)
var board_zone := BoardZoneView.new()
bottom.add_child(board_zone)
board_zone.ap_bar.set_refresh_cost(int(db.run.action_economy.rap_refresh_cost)) # RAP 读数表值驱动
+2 -2
View File
@@ -31,8 +31,8 @@ var _arm_timer := ArmedTimer.new() # 弃图刷新确认窗(T3/D3 P4 乙—
const REFRESH_CONFIRM_WINDOW_S := 4.0 # 弃图刷新确认窗(原 tween_interval 字面量升常量——语义零变化)
func _init(layout: BattleLayoutTable) -> void:
custom_minimum_size = Vector2(0, layout.ap_bar_h)
func _init() -> void:
custom_minimum_size = Vector2(0, 44)
_arm_timer.expired.connect(_disarm)
var sb := UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, 0, 1, Vector4(8, 4, 8, 4))
add_theme_stylebox_override("panel", sb)
+7 -9
View File
@@ -4,12 +4,12 @@ class_name BattleLogView
extends PanelContainer
const BAND_H_DIG := 24
const BAND_H_SETTLE := 84
const AGG_TYPES := ["reward_ineffective"]
const COL_GOLD := UiKit.HEX_GOLD
var _db
var _band_h_dig := 0
var _band_h_settle := 0
var _log: RichTextLabel
var _summary: Label
var _hint: Label
@@ -18,11 +18,9 @@ var _round_rows: Array[String] = [] # 本结算轮行缓冲(上轮摘要
var _pending_agg := {} # 聚合计数行 pending(type+batch_key → {row, count})
func _init(db_ref, layout: BattleLayoutTable) -> void:
func _init(db_ref) -> void:
_db = db_ref
_band_h_dig = layout.log_band_h_dig
_band_h_settle = layout.log_band_h_settle
custom_minimum_size = Vector2(0, _band_h_dig)
custom_minimum_size = Vector2(0, BAND_H_DIG)
size_flags_vertical = Control.SIZE_SHRINK_END
var vb := VBoxContainer.new()
vb.add_theme_constant_override("separation", 2)
@@ -31,7 +29,7 @@ func _init(db_ref, layout: BattleLayoutTable) -> void:
vb.add_child(head)
# 明细条灰字对深灰底实测 2.63:1 → 暗底双色墨制=暖白族
_summary = UiKit.label("",UiKit.FS_BODY, false, UiKit.WARM)
_summary.custom_minimum_size = Vector2(0, _band_h_dig - 4)
_summary.custom_minimum_size = Vector2(0, BAND_H_DIG - 4)
_summary.clip_text = true
_summary.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
head.add_child(_summary)
@@ -41,7 +39,7 @@ func _init(db_ref, layout: BattleLayoutTable) -> void:
_log = RichTextLabel.new()
_log.bbcode_enabled = true
_log.scroll_following = true
_log.custom_minimum_size = Vector2(0, _band_h_settle - 26)
_log.custom_minimum_size = Vector2(0, BAND_H_SETTLE - 26)
_log.clip_contents = true
_log.scroll_active = true
vb.add_child(_log)
@@ -55,7 +53,7 @@ func set_mode(mode: String) -> void:
var settle := mode == "settle"
_log.visible = settle
_summary.visible = not settle
custom_minimum_size = Vector2(0, _band_h_settle if settle else _band_h_dig)
custom_minimum_size = Vector2(0, BAND_H_SETTLE if settle else BAND_H_DIG)
func mode() -> String:
+5 -5
View File
@@ -24,11 +24,11 @@ var _viewport: SubViewport
var world: Node2D # StageWorld(preload 脚本实例——Node2D 世界根/字体挂点)
func _init(db_ref, layout: BattleLayoutTable) -> void:
custom_minimum_size = Vector2(0, layout.stage_h)
func _init(db_ref) -> void:
custom_minimum_size = Vector2(0, 252)
_build_backdrop()
_build_world_stage()
_build_text_half(db_ref, layout)
_build_text_half(db_ref)
_build_companion_layer(db_ref)
@@ -75,7 +75,7 @@ func _build_world_stage() -> void:
## Control 文本半区域(round_label / stage_center / log_view 原样保留——C1 永久 Control)
func _build_text_half(db_ref, layout: BattleLayoutTable) -> void: # type-hint: ignore(db 鸭子型注入——单测以最小 stub 构造)
func _build_text_half(db_ref) -> void: # type-hint: ignore(db 鸭子型注入——单测以最小 stub 构造)
var vb := VBoxContainer.new()
vb.add_theme_constant_override("separation", 4)
vb.set_anchors_preset(Control.PRESET_FULL_RECT)
@@ -86,7 +86,7 @@ func _build_text_half(db_ref, layout: BattleLayoutTable) -> void: # type-hint:
add_child(vb)
_build_narr_row(vb)
_build_center_row(vb)
log_view = BattleLogView.new(db_ref, layout)
log_view = BattleLogView.new(db_ref)
vb.add_child(log_view)
+4 -4
View File
@@ -18,17 +18,17 @@ signal hover_forbidden(cell: Vector2i)
signal chord_attempted(cell: Vector2i)
func _init(layout: BattleLayoutTable) -> void:
func _init() -> void:
size_flags_horizontal = Control.SIZE_EXPAND_FILL
var vb := VBoxContainer.new()
vb.add_theme_constant_override("separation", 4)
add_child(vb)
_build_views(vb, layout)
_build_views(vb)
## 视图三件域:AP 条+棋盘区(含动效与 toast 叠层)
func _build_views(vb: VBoxContainer, layout: BattleLayoutTable) -> void:
ap_bar = ApBarView.new(layout)
func _build_views(vb: VBoxContainer) -> void:
ap_bar = ApBarView.new()
vb.add_child(ap_bar)
var board_area := Control.new()
board_area.size_flags_vertical = Control.SIZE_EXPAND_FILL
+1 -1
View File
@@ -173,7 +173,7 @@ func _draw_south_facade(canvas: CanvasItem, rect: Rect2) -> void:
var y_far := rect.size.x - far_h
var y_near := y_far - near_h
var y_hl := y_near - FACADE_HIGHLIGHT_PX
canvas.draw_rect(Rect2(0.0, y_hl, rect.size.x, FACADE_HIGHLIGHT_PX), UiKit.BRICK, true) # 口沿高光(合并归掩尘枚 BRICK——art-bible §4.1 换色合并①同值单点)
canvas.draw_rect(Rect2(0.0, y_hl, rect.size.x, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点)
canvas.draw_rect(Rect2(0.0, y_near, rect.size.x, near_h), FACADE_NEAR_COL, true)
canvas.draw_rect(Rect2(0.0, y_far, rect.size.x, far_h), FACADE_FAR_COL, true)
@@ -21,8 +21,8 @@ var _board_variant_section: VBoxContainer # 地形变异常驻角标(空态
var _board_variant_list: VBoxContainer
func _init(layout: BattleLayoutTable) -> void:
custom_minimum_size = Vector2(layout.info_panel_w, 0)
func _init() -> void:
custom_minimum_size = Vector2(240, 0)
clip_contents = true
# 主面板穿石板戏服(纹理本色;数据框仍素面 §7.1;缺位回退 flat)
var sb := UiKit.panel_skinned(Vector4(8, 6, 8, 6))
+13 -16
View File
@@ -59,8 +59,7 @@ func intent_probe() -> Dictionary:
# 名字行探针出口(ui_probe 呈现口径 + 本批 V6/V8 断言面:armor_icon = 绑定纹理路径〔缺位回退字形〕、
# 几何 = 素底牌矩形与文字基线、素底色——V6 判据 = baseline_y < plinth_top_y;
# name_font_size = 名字行字号档实读(迭代 46 卡B——提档 FS_HINT 14 的几何单点出口))
# 几何 = 素底牌矩形与文字基线、素底色——V6 判据 = baseline_y < plinth_top_y)
func name_line_probe() -> Dictionary:
var f := _efont()
var tex := UiKit.load_tex(_armor_tex_path(st._armor))
@@ -70,8 +69,7 @@ func name_line_probe() -> Dictionary:
"armor_tex_bound": tex != null,
"plate": plate, "baseline_y": _name_baseline(plate).y,
"plinth_top_y": _pedestal_rect().position.y,
"plate_color": UiKit.HP_BAR_BACK,
"name_font_size": UiKit.FS_HINT}
"plate_color": UiKit.HP_BAR_BACK}
# ---- setter 面(presenter/组合根零改排) ----
@@ -334,16 +332,15 @@ func _name_plate_rect(f: Font, tex: Texture2D) -> Rect2:
var icon_w := 24.0
if tex == null:
icon_w = f.get_string_size(String(ARMOR_ICONS.get(st._armor, "◇")),
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT).x
var name_w := f.get_string_size(st._display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT).x
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION).x
var name_w := f.get_string_size(st._display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION).x
var total_w := icon_w + 3.0 + name_w
return Rect2((box.x - total_w - 8.0) * 0.5, box.y - 18.0 - 26.0, total_w + 8.0, 26.0)
# 名字行文字基线(牌内几何单点——图标 24px 垂直居中牌内〔中心=牌顶+13〕,14px 字形基线=中心+5
# (0.35h 规则按字号档推导 14×0.35≈4.9→5,T0 实测校验——迭代 46 卡B §7.4 提档))
# 名字行文字基线(牌内几何单点——图标 24px 垂直居中牌内〔中心=牌顶+13〕,12px 字形基线=中心+4)
func _name_baseline(plate: Rect2) -> Vector2:
return plate.position + Vector2(4.0, 18.0)
return plate.position + Vector2(4.0, 17.0)
# 绘制内容矩形(世界局部;ui_struct_probe ⑥standee「不出框+下余量≥8px」断言读数面——
@@ -365,8 +362,8 @@ func _draw() -> void:
## 地面域:椭圆落影(圣经 §6.1 地台落影压脚:方向稿椭圆 (0,0,0,70),压在地台
## 上缘周围;24 段多边形近似椭圆,中心=台面上方 6px、横半径=台宽一半)
## + 石板地台(§6.1 近景地台独立件:TEX_STAGE_PLAZA 源 48×12 九宫格横向拉伸、顶缘受光线 2px
## 不随拉伸变形、跨三关恒定;素材缺位回退程序化三段绘制〔板身 STAND_STONE/描边 PLAZA_EDGE/
## 顶缘线 BRICK——art-bible §4.1 换色合并①②改指目标色,几何同源 _pedestal_rect〕)
## 不随拉伸变形、跨三关恒定;素材缺位回退程序化三段绘制〔STAND_STONE 族方向稿取色,几何同源
## _pedestal_rect——与 panel_skinned 缺位回退同构〕)
func _draw_ground(f: Font) -> void:
var sh_center := Vector2(box.x * 0.5, box.y - 24.0)
var sh_rx := (box.x - 36.0) * 0.5
@@ -382,8 +379,8 @@ func _draw_ground(f: Font) -> void:
_draw_plinth_stretched(plaza, ped)
else:
draw_rect(ped, UiKit.STAND_STONE, true)
draw_rect(ped, UiKit.PLAZA_EDGE, false, 1.0)
draw_rect(Rect2(ped.position.x, ped.position.y, ped.size.x, 2.0), UiKit.BRICK, true)
draw_rect(ped, UiKit.STAND_STONE_EDGE, false, 1.0)
draw_rect(Rect2(ped.position.x, ped.position.y, ped.size.x, 2.0), UiKit.STAND_STONE_RIM, true)
## 地台九宫格横向拉伸域(循环体提函数——左右 4px 端头切片 + 中段拉伸;纵向恒 12px 源档,
@@ -467,13 +464,13 @@ func _draw_armor_name_line(f: Font) -> void:
draw_texture_rect(tex, Rect2(baseline.x, plate.position.y + 1.0, 24.0, 24.0), false)
else:
var armor_txt := String(ARMOR_ICONS.get(st._armor, "◇"))
icon_w = f.get_string_size(armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT).x
icon_w = f.get_string_size(armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION).x
var col: Color = UiKit.WARM_FAINT # 轻甲 = 暖白弱化(v3.5 暗底双色墨制)
match st._armor:
"ward":
col = UiKit.HERO_BLUE_BRIGHT # 魔法护甲 = 小人蓝亮档
"heavy":
col = UiKit.GOLD_BRIGHT # 重甲 = 金族亮档
f.draw_string(get_canvas_item(), baseline, armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, col)
f.draw_string(get_canvas_item(), baseline, armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION, col)
f.draw_string(get_canvas_item(), baseline + Vector2(icon_w + 3.0, 0.0), st._display_name,
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, UiKit.WARM) # v3.5 暗底双色墨制
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION, UiKit.WARM) # v3.5 暗底双色墨制
+2 -1
View File
@@ -185,7 +185,8 @@ const TIP_BG := Color(0.16, 0.12, 0.09, 0.96) # tooltip 深底
const STAGE_BG := Color8(62, 52, 42) # B 区舞台暗底(方向稿 render_battle_screen——battle_stage_view 面板底)
const BEAM_LIGHT := Color8(255, 236, 200) # B 区顶光柱暖光(用时乘 alpha 渐隐——battle_stage_view 光柱层)
const STAND_STONE := Color8(150, 130, 100) # 站桩石板地台·板身(standee_body 方向稿取色)
const PLAZA_EDGE := Color8(76, 64, 56) # 小石台描边(art-bible §4.1 换色合并②合并目标——贴图已落此值,缺位回退同值)
const STAND_STONE_EDGE := Color8(90, 76, 58) # 站桩石板地台·描边
const STAND_STONE_RIM := Color8(196, 172, 132) # 站桩石板地台·顶缘受光线
const CAMP_HEADER_BAND := Color8(110, 69, 38) # 营地柜台头带深棕 #6E4526(4-camp.png 方向稿取色;对齐 demo --wood-deep)
const CAMP_ROW_PLATE := Color8(240, 230, 206) # 营地行板暖米(同上)
const CAMP_BADGE_PANEL := Color8(40, 32, 24) # 营地钥石徽暗底(同上)
+1 -1
View File
@@ -136,7 +136,7 @@ func _ready() -> void:
# ---- ⑥ 主角受击/死亡路由(battle_stage 主角走完整呈现链的断言) ----
var db2 = load("res://core/config_db.gd").new()
if db2.load_tables("res://data/tables", false):
var stage2 = BattleStageView.new(db2, db2.presentation.battle_layout)
var stage2 = BattleStageView.new(db2)
add_child(stage2)
var flash_before: int = int(stage2.hero_standee.fx_probe().get("flash_count", 0))
stage2.on_step_row_fx({"type": "damage", "target": "hero", "killed": false})
+1 -1
View File
@@ -530,7 +530,7 @@ func _career_nojump_contract(main: Node) -> void:
problems.append("[career][nojump] 页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav)
# ⑥standee 专项:B 区站桩内容矩形 ⊆ 舞台矩形(高度读布局表;内缩 8px)且不被裁切
# ⑥standee 专项:B 区站桩内容矩形 ⊆ 252px 舞台矩形(内缩 8px)且不被裁切
# (名行基线离盒底 1px 同型高危写法返修回归闸;
# 内容矩形=StandeeBody.content_rect(),最低绘制元素=底座下缘)
func _standeefit_contract(main: Node) -> void:
+3 -11
View File
@@ -112,12 +112,7 @@ tests/
| 挖/封印合法性与可辨拒绝(拒绝零副作用) | 排雷玩法 | `MinesweeperRules.settle_batch()` | `minesweeper_rules_test.gd:_test_dig_face_rules`;`minesweeper_rules_test.gd:_test_seal_place`;`minesweeper_rules_test.gd:_test_reject_out_of_catalog` |
| 封印不可逆与挖开封印格放行(兑付改判四情况) | 排雷玩法 | `MinesweeperRules.settle_batch()` | `seal_polarity_test.gd:_smoke_seal_irreversible_quadrants`;`minesweeper_rules_test.gd:_test_seal_place` |
| AP 扣费、末点耗尽即收轮与战斗收束清零 | 行动经济 | `MinesweeperRules.settle_batch()`(→`ActionEconomy.try_spend()`/`ActionEconomy.notify_combat_end()`) | `seal_polarity_test.gd:_smoke_seal_ap_exhausted`;`round_flow_skill_pool_test.gd:_smoke_full_battle`;`round_flow_skill_pool_test.gd:_smoke_active_refresh` |
| 停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励) | 行动经济 | `BattleOrchestrator.player_request_refresh()`/`MinesweeperRules.settle_batch()` | `round_flow_skill_pool_test.gd:_smoke_active_refresh`;`seal_polarity_test.gd:_smoke_refresh_provenance_guard`;`refresh_feats_test.gd:test_no_feats_baseline` |
| 换图系刷新链路与效果词表接线 | 行动经济×战斗结算×奇物 | `MinesweeperRules.settle_batch()`/`BattleOrchestrator.player_request_refresh()` | `effect_words_t1_test.gd:_init`;`forge_table_test.gd:_test_effect_kinds_closed`;`refresh_feats_test.gd:test_board_refresh_fact_only_on_success` |
| 换图基座:挖格产 RAP 与刷新折扣 | 行动经济×奇物 | `MinesweeperRules.settle_batch()`/`BattleOrchestrator.player_request_refresh()` | `refresh_feats_test.gd:test_reserve_on_accepted_dig`;`refresh_feats_test.gd:test_refresh_discount_gate_and_charge`;`refresh_feats_test.gd:test_double_base_stack` |
| 换图输出:刷新群伤与蓄力攻击 | 战斗结算×奇物 | `BattleOrchestrator.player_request_refresh()`→轮末 `MinesweeperRules.settle_batch(stop)` | `refresh_feats_test.gd:test_blast_defers_to_combat_segment`;`refresh_feats_test.gd:test_charge_layers_consumed_same_round` |
| 换图生存:全局固定减伤与轮末恢复 | 战斗结算×奇物 | `BattleOrchestrator.player_request_refresh()`→敌方攻击/危险格/错误封印/地形真实受伤入口 | `refresh_feats_test.gd:test_all_hero_damage_paths_emit_once`;`refresh_feats_test.gd:test_stone_mitigation_all_damage`;`refresh_feats_test.gd:test_rest_rotation_cap_and_clear` |
| 换图资源:刷新固定 GP 收益 | 战斗结算×奇物 | `BattleOrchestrator.player_request_refresh()` | `refresh_feats_test.gd:test_mine_profit_gp` |
| 停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励) | 行动经济 | `BattleOrchestrator.player_request_refresh()`/`MinesweeperRules.settle_batch()` | `round_flow_skill_pool_test.gd:_smoke_active_refresh`;`seal_polarity_test.gd:_smoke_refresh_provenance_guard` |
| 下轮行动点修正排队与兑现 | 行动经济 | `ActionEconomy.pending_ap_delta()`(排队经 `ActionEconomy.queue_ap_delta()`,写入方为效果引擎) | `seal_polarity_test.gd:_smoke_negative_dictionary`;`feat_vouchers_test.gd:_init` |
**旅程段六:表现与支撑**
@@ -133,9 +128,6 @@ tests/
| 中文字体随包与字形覆盖 | HUD框架 | `UiKit.load_font()` | `ui_font_asset_test.gd:_init`;`theme_projection_test.gd:_smoke_global_theme` |
| 地形瓦片套与格面数字可读 | HUD框架 | `TilePalette.digit_ink_mode()`/`TilePalette.normalize_floor_luma()` | `terrain_tile_sets_test.gd:_test_set_completeness`;`terrain_tile_sets_test.gd:_test_hue_domains`;`terrain_tile_sets_test.gd:_test_digit_ink_modes` |
| 站桩敌人呈现与结算演出落点 | 战斗表现 | `BattleStageView.enemy_standee_center()`/`BattleEndOverlay.open_victory()` | `standee_fx_sweep_test.gd:_init`;`pacing_queue_test.gd:_init` |
| 站桩呈现定值与素材绑定 | 战斗表现 | `UiKit.load_tex()`/`StandeeBody.name_line_probe()` | `standee_present_values_test.gd:_init` |
| B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) | HUD框架 | `BattleStageView.set_board_type()` | `stage_background_test.gd:_test_background_selection`;`stage_background_test.gd:_test_ground_layer_geometry`;`stage_background_test.gd:_test_beams_retired` |
| 战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) | HUD框架 | `BoardView.set_cell_area()`(D 区可用面积 → 格边长);读面 `BoardView.cell_size` | `layout_probe.gd:_ready`;`board_gen_reward_test.gd:_smoke_layout_cell_formula` |
| 调参测速批量模拟(逐帧步进与统计读数) | 调参测量 | `TuneSimController.run()`(调试浮窗批量模拟入口) | `tune_sim_batch_parity_test.gd:_init`(配对对拍套件,随测速循环批建) |
| 战斗叙述与终局文案(数值带入/胜负互异) | HUD框架 | `BattleNarrator.battle_end_text()`/`BattleNarrator.boss_name()` | `battle_narrator_test.gd:_test_numeric_lines_carry_values`;`battle_narrator_test.gd:_test_end_text_differs_by_result`;`battle_narrator_test.gd:_test_boss_name_uses_boss_rowset` |
| 玩家面文案与危险格格面字形 | HUD框架 | `BoardView.rebuild()`/`StatusSlotRow.set_effects()` | `player_copy_sweep_test.gd:_sweep_board_glyph`;`player_copy_sweep_test.gd:_sweep_legend`;`player_copy_sweep_test.gd:_sweep_hero_panel` |
@@ -167,13 +159,13 @@ tests/
| 战斗循环 | 地图战斗资源.md | 清图奖励双份与零路径;GP 钱包/escrow/商店收口/共享池;封印与极性路由/不可逆 | 已覆盖 | — |
| 战斗循环 | 增益状态.md | 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理;封印与极性路由/不可逆 | 部分覆盖 | 增益叠层/衰减/清除结算链无断言;见待办清单「能力清单覆盖缺口补齐」条 |
| 战斗循环 | 战斗结算.md | 伤害公式与掷点乘区;伤害单据编排与回放;轮内流与结算五段全序 | 已覆盖 | — |
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
| 战斗循环 | 敌人系统.md | 技能池数据面与回放独立;Boss 半血解锁技门控;敌人技能池跨表链接校验 | 已覆盖 | — |
| 排雷循环 | 地形变异.md | 变异布置门控;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
| 排雷循环 | 排雷玩法.md | 装板与开局免费展开(封锁格无资源、计入清图);挖/封印合法性与可辨拒绝(拒绝零副作用);封印不可逆与挖开封印格放行(兑付改判四情况) | 已覆盖 | — |
| 排雷循环 | 棋盘生成.md | 棋盘按参数行出图、数字恒真;同参数同种子逐位复现、异种子不退化;可推理率与清图可达(合法性先于质量分)、重试有界、降级诚实 | 已覆盖 | — |
| 排雷循环 | 行动经济.md | AP 扣费、末点耗尽即收轮与战斗收束清零;停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励);下轮行动点修正排队与兑现 | 已覆盖 | — |
| 表现与支撑 | HUD框架.md | 拒绝反馈分面可辨与未知兜底文案;结算明细带逐行呈现与排播节奏;模态覆盖层编排与输入锁;未开格类型微光(8 邻作用集/五组色相/零玩法写入);UI 全局主题与按钮五态(含对比度锚);中文字体随包与字形覆盖;地形瓦片套与格面数字可读;战斗叙述与终局文案(数值带入/胜负互异);玩家面文案与危险格格面字形;战斗屏三区几何与棋盘格边长(分辨率两档×四档板型);B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) | 已覆盖 | — |
| 表现与支撑 | HUD框架.md | 拒绝反馈分面可辨与未知兜底文案;结算明细带逐行呈现与排播节奏;模态覆盖层编排与输入锁;未开格类型微光(8 邻作用集/五组色相/零玩法写入);UI 全局主题与按钮五态(含对比度锚);中文字体随包与字形覆盖;地形瓦片套与格面数字可读;战斗叙述与终局文案(数值带入/胜负互异);玩家面文案与危险格格面字形 | 已覆盖 | — |
| 表现与支撑 | 存档序列化.md | 存档写盘⇄读回往返保真;三槽位隔离与激活失败回滚;槽位摘要窥视与坏槽三态;启动态无激活不写盘;旧档迁移链与断点续玩零跳号(集成);存档⇄RNG 快照往返保真(集成) | 已覆盖 | — |
| 表现与支撑 | 教学引导.md | — | 未覆盖 | 教学形态与首遇时机待设计;见待办清单「教学形态未定」条 |
| 表现与支撑 | 数据配置.md | 全表装载、就绪态与表清单目录对账;进度坐标查行:精确命中/向上取整/封顶/警告去重;运行期越域致命与进程非 0 退出;加载期违规表拒绝:跨表悬空/计数器前缀/真视窄域;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
+1 -1
View File
@@ -188,7 +188,7 @@ func _suites() -> Array:
{"path": "res://tests/unit/presentation/pacing_queue_test.gd", "expect": 0}, # AC⑤/T9——排空零分叉/零丢行/延迟双态
{"path": "res://tests/unit/presentation/standee_fx_sweep_test.gd", "expect": 0}, # 呈现域缺陷扫掠(d2 蓄力档/d4 庆祝句柄/d5+d8 互斥/d12 站桩按 id 解析)
{"path": "res://tests/unit/presentation/tune_sim_batch_parity_test.gd", "expect": 0}, # 测速循环批双实现对拍(帧索引/运行态/进度串/stats/报告五面——信号步进 vs await 基线)
{"path": "res://tests/unit/presentation/standee_present_values_test.gd", "expect": 0}, # 战斗一屏样张批 V5–V12(血条危险红/名字行素底不压地台与提档 FS_HINT/徽标/护甲 PNG 绑定/素材入包/地台色合并退役)
{"path": "res://tests/unit/presentation/standee_present_values_test.gd", "expect": 0}, # 战斗一屏样张批 V5–V9(血条危险红/名字行素底不压地台/徽标 FS_HINT/护甲 PNG 绑定/素材入包)
{"path": "res://tests/unit/presentation/reward_overlay_test.gd", "expect": 0}, # 奖励节点 UI 直验(盲区补漏)
{"path": "res://tests/unit/presentation/level_map_info_fog_test.gd", "expect": 0}, # 信息迷雾——呈现显隐/Boss 豁免/详情零显示/大图拟合
{"path": "res://tests/unit/presentation/forge_workbench_test.gd", "expect": 0}, # 工作间两区六路线 UI 直验(V13 自动化前置面)
+2 -4
View File
@@ -314,7 +314,7 @@ fi
# get_tree());排除注释行(行首 #/##)与字符串字面量(双引号包裹段内的 await 非代码),
# 口径仿 S1/S2 的 grep 排除手法。
# 自检:清单内文件缺位(路径写错/被改名)即判红——防扫面为空静默绿。
S8_FILES="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd game/core/settlement/battle_refresh_flow.gd"
S8_FILES="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd"
S8_MISS=0
for f in $S8_FILES; do
if [ ! -f "$f" ]; then
@@ -356,9 +356,7 @@ fi
# 自检:扫描面为空即判红(require_scan_face);B1 文件集与三族自家文件缺位即判红
# (文件被改名=豁免名单失效,宁可红不可静默放行)。
if require_scan_face "S7" "$DIR_CORE"; then
# B1 域文件集(迭代 46 卡A 增补 battle_refresh_flow——换图刷新流程件=C13 拆分边界的 B1 域拆件,
# 承载旧资源退役/装板编排调用;architecture.md「模块归属」BattleRefreshFlow 行随反哺点落文)
S7_B1_SET="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd game/core/settlement/battle_refresh_flow.gd"
S7_B1_SET="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd"
S7_MISS=0
for f in $S7_B1_SET game/core/unit_container.gd game/core/status_container.gd game/core/reward_economy.gd; do
if [ ! -f "$f" ]; then
@@ -1,650 +0,0 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 换图系双基座七件专项宿主(迭代 46 卡A——对照表 V1–V12 按轮落地:V5=T2 全局受伤链路、
# V2/V3=T3 两基座、V4/V10=T4 成功事实与 GP、V6–V9=T5 五件后续、V11=T6 数据与池;
# V12 无专长基线回归归 round_flow/seal_polarity 既有断言承载)。
# 临时承伤计数专长经现行 ConfigDB 注入(表行追加+持有集传 id——正常数据配置路径,卡片 §12);
# 真实入口驱动:AutoPlayer 盲挖循环+停手/封印动作;判据绑可观测提交面(damage_taken 命中计数器
# 与 target=hero 明细行)。
# 运行:godot --headless --path game --script res://tests/unit/battle_settlement/refresh_feats_test.gd(expect 0)
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
const ConfigDBScript = preload("res://core/config_db.gd")
const EffectEngineScript = preload("res://core/effect_engine.gd")
const AutoPlayer := preload("res://sim/auto_player.gd")
# 直接伤型资源类型(负向极性兑付=主角直伤单据的三型——reward 表 pierce/magic/heal 负向行)
const DIRECT_TYPES := ["pierce", "magic", "heal"]
var _db
func _init() -> void:
_db = ConfigDBScript.new()
if not _db.load_tables("res://data/tables", true):
print("[refresh_feats_test] FAIL — 数值表加载失败")
quit(1)
return
_install_dt_probe()
test_all_hero_damage_paths_emit_once()
test_no_duplicate_damage_taken_event()
test_reserve_on_accepted_dig()
test_refresh_discount_gate_and_charge()
test_double_base_stack()
test_no_feats_baseline()
test_board_refresh_fact_only_on_success()
test_mine_profit_gp()
test_stone_mitigation_all_damage()
test_rest_rotation_cap_and_clear()
test_charge_layers_consumed_same_round()
test_blast_defers_to_combat_segment()
test_seven_feats_validate_and_pool()
quit(Kit.finish("refresh_feats_test"))
# ---- 夹具族(真实入口驱动——settle_batch/stop/封印动作;直构只用于编排器受理)----
# 临时承伤计数专长(damage_taken → counter_add battle 桶——每张主角承伤单据恰 +1)
func _install_dt_probe() -> void:
if _db.crossrun.feats.row_by_id("probe_dt_counter").is_empty():
(_db.crossrun.feats.rows as Array).append({
"feat_id": "probe_dt_counter", "display_name": "承伤计数探针", "rarity": "common",
"desc": "测试夹具:每次主角承伤计数 +1",
"triggers": [{"on": "damage_taken", "do": {"kind": "counter_add", "counter_id": "dt_probe_hits", "value": 1}}],
})
func _orch(seed_v: int):
var rng = load("res://core/rng_service.gd").new()
rng.set_master_seed(seed_v)
return load("res://core/battle_orchestrator.gd").new(_db, rng)
func _enc(feats: Array, voucher_rap: int = 0, armor: int = 10, hero_hp: int = 200) -> Dictionary:
return {"level_index": 1, "node_depth": 4, "hero_hp": hero_hp, "hero_atk": 8, "hero_max_hp": 200,
"hero_armor_class": armor, "hero_ap_bonus": 0, "attunement": [], "feats": feats, "tree_rules": [],
"battle_index": 1, "voucher_rap": voucher_rap, "voucher_time_sand": 0, "node_type": "boss", "adv_bucket": {}}
func _probe_count(orch) -> int:
return EffectEngineScript.counter(orch.fx_host().counters_ctx, "dt_probe_hits")
# 主角承伤明细行收集(type=damage 且 target=hero——敌方技与棋盘主角直伤两类单据的行面)
func _collect_hero_rows(orch) -> Array:
var rows: Array = []
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "damage" and String(row.get("target", "")) == "hero":
rows.append(row))
return rows
# 按来源类分桶计数(rows 收集器的只读后处理)
func _count_by_source(rows: Array, source: String) -> int:
var n := 0
for r_v in rows:
if String((r_v as Dictionary).get("source_class", "")) == source:
n += 1
return n
# AutoPlayer 盲挖循环直到 cell_revealed 谓词命中(真实挖掘入口;返回 {"cell","evt"} 或空)
func _dig_until(orch, pred: Callable) -> Dictionary:
var ms = orch.minesweeper
var hit := {}
ms.cell_revealed.connect(func(evt):
if hit.is_empty() and bool(pred.call(evt)):
hit["cell"] = evt.cell
hit["evt"] = evt)
var guard := 40
while hit.is_empty() and guard > 0:
guard -= 1
var plan = AutoPlayer.choose_dig(ms)
if plan.is_empty():
break
if plan.hero_cell != ms.hero_pos():
ms.settle_batch([{"action": "move", "cell": plan.hero_cell}])
ms.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
return hit
# 直接伤型快照判定(未揭示格可查——reward_economy 装板期摇定身份,示警烟火同源读面)
func _is_direct_type(orch, c: Vector2i) -> bool:
var snap_v = orch.reward_grant().reward_economy.query_full_snapshot(c)
return snap_v is Dictionary and DIRECT_TYPES.has(String((snap_v as Dictionary).get("cell_type", "")))
# 可封印的直接伤型安全格扫描(未揭示+非雷+直接伤型+邻接可达 revealed 格——封印动作真实落点位)
func _find_sealable_direct(orch) -> Vector2i:
var ms = orch.minesweeper
var b = ms.current_board()
var revealed := {}
for c_v in ms.revealed_cells():
revealed[c_v] = true
for y in b.height:
for x in b.width:
var c := Vector2i(x, y)
if ms.is_revealed(c) or b.is_mine(c) or ms.has_seal(c) or not _is_direct_type(orch, c):
continue
for n_v in b.neighbors8(c):
var n := Vector2i(n_v)
if revealed.has(n) and (ms.compute_move_path(n).size() > 0 or n == ms.hero_pos()):
return c
return Vector2i(-1, -1)
# 盲挖扩板直至出现可封印直接伤型安全格(有界;返回格位或 (-1,-1))
func _seal_direct_with_expansion(orch) -> Vector2i:
var c := _find_sealable_direct(orch)
var guard := 12
while c.x < 0 and guard > 0:
guard -= 1
var plan = AutoPlayer.choose_dig(orch.minesweeper)
if plan.is_empty():
break
if plan.hero_cell != orch.minesweeper.hero_pos():
orch.minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}])
orch.minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
c = _find_sealable_direct(orch)
return c
# ---- V5 全局受伤链路(迭代 46 卡A T2)----
# 四条真实路径(敌方攻击/危险格/错误封印/地形)逐路驱动:每条至少一张主角承伤单据在案,
# 且承伤计数器 == 主角承伤单据行数(每单据恰发射一次的全量判据——重复发射即计数器>行数)。
func test_all_hero_damage_paths_emit_once() -> void:
print("--- V5 全局 damage_taken:四路径逐路恰一次 ---")
# ① 敌方攻击路径(Boss 战零挖掘连停三轮——干净样本:零地形零资源单据;多轮保证伤害技在案)
var orch1 = _orch(46001)
var rows1 := _collect_hero_rows(orch1)
Kit.check("V5① 前置:战斗受理", orch1.begin_battle(_enc(["probe_dt_counter"])))
for _i in 3:
orch1.minesweeper.settle_batch([{"action": "stop"}])
var enemy_rows := _count_by_source(rows1, "enemy_action")
Kit.check("V5① 敌方攻击路径:承伤单据在案且逐单恰一次(行 %d=计数 %d)〔换图系批 V5〕" % [enemy_rows, _probe_count(orch1)],
enemy_rows >= 1 and _probe_count(orch1) == enemy_rows)
# ② 地形路径(AutoPlayer 真挖至伤害型变异格开出——发射在授予段开单点、单据标 hero_damage_consumed)
var orch2 = _orch(46002)
var rows2 := _collect_hero_rows(orch2)
Kit.check("V5② 前置:战斗受理", orch2.begin_battle(_enc(["probe_dt_counter"])))
var terr := _dig_until(orch2, func(evt):
for vid_v in (evt.get("variant_ids", []) as Array):
if String(_db.combat.variation.row_by_id(String(vid_v)).get("effect", "")) == "flat_damage":
return true
return false)
Kit.check("V5② 前置:伤害型变异格已挖开(%s)" % str(terr.get("cell", Vector2i(-1, -1))), not terr.is_empty())
orch2.minesweeper.settle_batch([{"action": "stop"}])
var terrain_rows := _count_by_source(rows2, "地形变异")
Kit.check("V5② 地形路径:地形单据在案且全单据逐张恰一次(行 %d=计数 %d——盲挖途经单据一并计入)〔换图系批 V5〕" % [rows2.size(), _probe_count(orch2)],
terrain_rows >= 1 and _probe_count(orch2) == rows2.size())
# ③ 危险格路径(AutoPlayer 真挖直接伤型危险格——负向直伤单据)
var orch3 = _orch(46003)
var rows3 := _collect_hero_rows(orch3)
Kit.check("V5③ 前置:战斗受理", orch3.begin_battle(_enc(["probe_dt_counter"])))
var mine_hit := _dig_until(orch3, func(evt):
return String(evt.get("kind", "")) == "mine" and _is_direct_type(orch3, evt.cell))
Kit.check("V5③ 前置:直接伤型危险格已挖开(%s)" % str(mine_hit.get("cell", Vector2i(-1, -1))), not mine_hit.is_empty())
orch3.minesweeper.settle_batch([{"action": "stop"}])
var danger_rows := _count_by_source(rows3, "mine_hit")
Kit.check("V5③ 危险格路径:直伤单据在案且全单据逐张恰一次(行 %d=计数 %d)〔换图系批 V5〕" % [rows3.size(), _probe_count(orch3)],
danger_rows >= 1 and _probe_count(orch3) == rows3.size())
# ④ 错误封印路径(扩板→封直接伤型安全格不挖→停手轮末判标错——负向直伤单据)
var orch4 = _orch(46004)
var rows4 := _collect_hero_rows(orch4)
Kit.check("V5④ 前置:战斗受理", orch4.begin_battle(_enc(["probe_dt_counter"])))
var scell := _seal_direct_with_expansion(orch4)
Kit.check("V5④ 前置:可封直接伤型安全格在位(%s)" % str(scell), scell.x >= 0)
if scell.x >= 0:
var plan4 = AutoPlayer.choose_dig(orch4.minesweeper)
# 封印动作真实落点:先就位到目标邻接可达格(封印距离同挖掘距离)
var seal_plan := _plan_seal(orch4, scell)
if not seal_plan.is_empty():
if seal_plan["hero_cell"] != orch4.minesweeper.hero_pos():
orch4.minesweeper.settle_batch([{"action": "move", "cell": seal_plan["hero_cell"]}])
var seal_out: Array = orch4.minesweeper.settle_batch([{"action": "seal_place", "cell": scell}])
Kit.check("V5④ 前置:封印受理", not seal_out.is_empty() and bool((seal_out[0] as Dictionary).get("accepted", false)))
orch4.minesweeper.settle_batch([{"action": "stop"}])
var sealwrong_rows := _count_by_source(rows4, "seal_miss")
Kit.check("V5④ 错误封印路径:直伤单据在案且全单据逐张恰一次(行 %d=计数 %d)〔换图系批 V5〕" % [rows4.size(), _probe_count(orch4)],
sealwrong_rows >= 1 and _probe_count(orch4) == rows4.size())
# 封印落点计划(目标格邻接可达 revealed 位——AutoPlayer._plan_on 同构最小私有形)
func _plan_seal(orch, target: Vector2i) -> Dictionary:
var ms = orch.minesweeper
for n_v in ms.current_board().neighbors8(target):
var n := Vector2i(n_v)
if ms.is_revealed(n) and (ms.compute_move_path(n).size() > 0 or n == ms.hero_pos()):
return {"hero_cell": n}
return {}
# 同一伤害不得出现两次消费(重复发射=计数器>行数):敌方路径与地形路径两样本逐位核对——
# 地形单据带 hero_damage_consumed 标记,战斗段末 _resolve_board_flat_hero 跳过二次发射由此钉住。
func test_no_duplicate_damage_taken_event() -> void:
print("--- V5 全局 damage_taken:无重复消费 ---")
var orch1 = _orch(46011)
var rows1 := _collect_hero_rows(orch1)
Kit.check("ND 前置:战斗受理", orch1.begin_battle(_enc(["probe_dt_counter"])))
for _i in 2:
orch1.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("ND 敌方路径零重复(行 %d=计数 %d——重复消费即计数>行)〔换图系批 V5〕" % [rows1.size(), _probe_count(orch1)],
rows1.size() >= 1 and _probe_count(orch1) == rows1.size())
var orch2 = _orch(46012)
var rows2 := _collect_hero_rows(orch2)
Kit.check("ND 前置:战斗受理", orch2.begin_battle(_enc(["probe_dt_counter"])))
var terr2 := _dig_until(orch2, func(evt):
for vid_v in (evt.get("variant_ids", []) as Array):
if String(_db.combat.variation.row_by_id(String(vid_v)).get("effect", "")) == "flat_damage":
return true
return false)
Kit.check("ND 前置:伤害型变异格已挖开(%s)" % str(terr2.get("cell", Vector2i(-1, -1))), not terr2.is_empty())
orch2.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("ND 地形路径零重复(行 %d=计数 %d——context 标记闸失效即计数翻倍)〔换图系批 V5〕" % [rows2.size(), _probe_count(orch2)],
rows2.size() >= 1 and _probe_count(orch2) == rows2.size())
# ---- V2/V3 两件基座(迭代 46 卡A T3)----
# 表驱动停手攒 RAP(每轮 1:1 转存——无转存率订阅缺省)
func _bank_rap(orch, rounds_n: int) -> void:
for _i in rounds_n:
orch.minesweeper.settle_batch([{"action": "stop"}])
# AutoPlayer 单次付费挖格(真实挖掘入口——移位+dig 一动作)
func _auto_dig(orch) -> void:
var plan = AutoPlayer.choose_dig(orch.minesweeper)
if not plan.is_empty():
if plan.hero_cell != orch.minesweeper.hero_pos():
orch.minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}])
orch.minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
# V2 矿脉储能:受理的付费挖格 RAP 恰 +1、AP 按表扣、板资源不变
func test_reserve_on_accepted_dig() -> void:
print("--- V2 矿脉储能:付费挖格产 RAP ---")
var orch = _orch(46021)
Kit.check("V2 前置:战斗受理", orch.begin_battle(_enc(["vein_reserve"])))
_bank_rap(orch, 1)
var rap_before: int = orch.action_economy.rap
var ap_before: int = orch.action_economy.ap
_auto_dig(orch)
var dig_cost: int = int(_db.run.action_economy.dig_ap_cost)
Kit.check("V2 受理付费挖格 RAP 恰 +1(%d→%d)〔换图系批 V2〕" % [rap_before, orch.action_economy.rap],
orch.action_economy.rap == rap_before + 1)
Kit.check("V2 AP 按表扣(%d→%d,dig 价 %d)〔换图系批 V2〕" % [ap_before, orch.action_economy.ap, dig_cost],
orch.action_economy.ap == ap_before - dig_cost)
Kit.check("V2 受理记账闭合:AP 耗尽轮 RAP 增量恰等于受理挖数(AP 消耗数——每受理恰 +1,未受理零入池)〔换图系批 V2〕",
_drain_round_rap_accounting(orch))
# V2 辅证:挖到 AP 耗尽——RAP 增量 == 受理挖数(每受理恰 +1;移位耗 AP 不入账故按挖计数)
func _drain_round_rap_accounting(orch) -> bool:
var rap0: int = orch.action_economy.rap
var digs := 0
var guard := 24
while orch.action_economy.phase == "Active" and guard > 0:
guard -= 1
var plan = AutoPlayer.choose_dig(orch.minesweeper)
if plan.is_empty():
break
if plan.hero_cell != orch.minesweeper.hero_pos():
orch.minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}])
if orch.action_economy.phase != "Active":
break # 移位耗尽 AP 收轮——本轮不再有受理挖
orch.minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
digs += 1
return digs > 0 and orch.action_economy.rap - rap0 == digs
# V3 省力周转:折价门槛与扣费(can 与执行同源;base 8→扣 4)
func test_refresh_discount_gate_and_charge() -> void:
print("--- V3 省力周转:折价门槛与扣费 ---")
var orch = _orch(46022)
Kit.check("V3 前置:战斗受理", orch.begin_battle(_enc(["easy_swap"])))
_bank_rap(orch, 1)
var base_cost: int = int(_db.run.action_economy.rap_refresh_cost)
var discounted: int = int(floor(float(base_cost) * 50.0 / 100.0))
var rap0: int = orch.action_economy.rap
Kit.check("V3 门同源:can_refresh 与折后价同源(RAP %d ≥ 折后 %d)〔换图系批 V3〕" % [rap0, discounted],
orch.can_refresh())
Kit.check("V3 第 1 次刷新受理", orch.player_request_refresh())
Kit.check("V3 折后单次扣费(%d→%d,−%d)〔换图系批 V3〕" % [rap0, orch.action_economy.rap, discounted],
orch.action_economy.rap == rap0 - discounted)
Kit.check("V3 第 2 次刷新受理(RAP=%d)" % orch.action_economy.rap, orch.player_request_refresh())
Kit.check("V3 两次折后扣费累计(RAP=%d=%d−2×%d)〔换图系批 V3〕" % [orch.action_economy.rap, rap0, discounted],
orch.action_economy.rap == rap0 - 2 * discounted)
Kit.check("V3 门槛拒:RAP 不足折后价零受理(can=false 且执行拒)〔换图系批 V3〕",
orch.action_economy.rap < discounted and not orch.can_refresh() and not orch.player_request_refresh())
# V3 双基座叠加:挖格产 RAP 与刷新折价同场生效
func test_double_base_stack() -> void:
print("--- V3 双基座叠加 ---")
var orch = _orch(46023)
Kit.check("V3 前置:战斗受理", orch.begin_battle(_enc(["vein_reserve", "easy_swap"])))
_bank_rap(orch, 1)
var rap_before: int = orch.action_economy.rap
var base_cost: int = int(_db.run.action_economy.rap_refresh_cost)
_auto_dig(orch)
Kit.check("V3 双基座:挖格产 RAP +1(%d→%d)〔换图系批 V3〕" % [rap_before, orch.action_economy.rap],
orch.action_economy.rap == rap_before + 1)
var rap_after_dig: int = orch.action_economy.rap
Kit.check("V3 双基座:刷新按折后价扣(−%d)〔换图系批 V3〕" % int(base_cost / 2),
orch.player_request_refresh() and orch.action_economy.rap == rap_after_dig - int(base_cost / 2))
# V12(refresh_feats 侧):无专长基线——挖格零产 RAP、刷新按表基线扣、AP 保留、上限照旧
func test_no_feats_baseline() -> void:
print("--- V12 无专长基线(refresh_feats 侧) ---")
var orch = _orch(46024)
Kit.check("V12 前置:战斗受理", orch.begin_battle(_enc([])))
_bank_rap(orch, 3)
var rap_before: int = orch.action_economy.rap
var ap_before: int = orch.action_economy.ap
var base_cost: int = int(_db.run.action_economy.rap_refresh_cost)
_auto_dig(orch)
Kit.check("V12 无基座:挖格零产 RAP(%d 不变)〔换图系批 V12〕" % rap_before, orch.action_economy.rap == rap_before)
Kit.check("V12 刷新受理并按表基线扣(−%d)〔换图系批 V12〕" % base_cost,
orch.player_request_refresh() and orch.action_economy.rap == rap_before - base_cost)
Kit.check("V12 AP 保留、相位 Active(基线刷新不重置)〔换图系批 V12〕",
orch.action_economy.ap == ap_before - int(_db.run.action_economy.dig_ap_cost) and orch.action_economy.phase == "Active")
Kit.check("V12 每轮上限照旧:第 3 次拒(RAP 足额仍拒)〔换图系批 V12〕",
orch.player_request_refresh() and not orch.player_request_refresh())
# ---- V4/V10 成功刷新事实与命中施加(迭代 46 卡A T4)----
# 成功刷新事实探针专长安装(board_refreshed → counter_add——成功事实的可观测计数面)
func _install_br_probe() -> void:
if _db.crossrun.feats.row_by_id("probe_br_counter").is_empty():
(_db.crossrun.feats.rows as Array).append({
"feat_id": "probe_br_counter", "display_name": "刷新事实探针", "rarity": "common",
"desc": "测试夹具:每次成功刷新计数 +1",
"triggers": [{"on": "board_refreshed", "do": {"kind": "counter_add", "counter_id": "br_probe_hits", "value": 1}}],
})
func _br_count(orch) -> int:
return EffectEngineScript.counter(orch.fx_host().counters_ctx, "br_probe_hits")
# V4 成功事实只在新板就位后发射:成功恰一次;非 Active/每轮上限/RAP 不足三拒面全零事实零换图
func test_board_refresh_fact_only_on_success() -> void:
print("--- V4 成功刷新事实:拒绝零事实 ---")
_install_br_probe()
var orch = _orch(46031)
var swaps := {"n": 0}
var waiting_rejected := {"v": false}
orch.board_changed.connect(func(): swaps["n"] += 1)
# 非 Active 探针(round_flow 同款:借衰减行在结算段内同步探测一次——Waiting 相位刷新必拒)
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "decay" and not bool(waiting_rejected["v"]):
waiting_rejected["v"] = not orch.player_request_refresh())
Kit.check("V4 前置:战斗受理", orch.begin_battle(_enc(["probe_br_counter"])))
_bank_rap(orch, 3) # RAP=3×AP;首轮结算衰减行触发 Waiting 探针
var swaps0: int = swaps["n"]
Kit.check("V4 非 Active 拒零事实(结算段内探测拒绝)〔换图系批 V4〕",
bool(waiting_rejected["v"]) and _br_count(orch) == 0 and int(swaps["n"]) == swaps0)
# 成功 ×2:事实计数与换图信号逐次 +1
Kit.check("V4 第 1 次成功(事实 +1 换图 +1)〔换图系批 V4〕",
orch.player_request_refresh() and _br_count(orch) == 1 and int(swaps["n"]) == swaps0 + 1)
Kit.check("V4 第 2 次成功(事实 +1 换图 +1)〔换图系批 V4〕",
orch.player_request_refresh() and _br_count(orch) == 2 and int(swaps["n"]) == swaps0 + 2)
# 每轮上限拒:used=2 满门、RAP 足额——零事实零换图
Kit.check("V4 上限拒零事实(RAP %d 足额仍拒)〔换图系批 V4〕" % orch.action_economy.rap,
not orch.can_refresh() and not orch.player_request_refresh() and _br_count(orch) == 2 and int(swaps["n"]) == swaps0 + 2)
# RAP 不足拒:停手进新轮(used 重置、停手再入 RAP)→ 消费至 RAP 0 → 拒(零事实零换图;
# 纯 RAP 不足拒绝面已由 V3 门槛拒单测钉住,此处并拦形态)
orch.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("V4 新轮受理两次(used 重置、RAP 复入)〔换图系批 V4〕",
orch.action_economy.phase == "Active" and orch.player_request_refresh() and orch.player_request_refresh() and _br_count(orch) == 4)
Kit.check("V4 RAP 不足拒零事实(RAP %d < 价 %d)〔换图系批 V4〕" % [orch.action_economy.rap, int(_db.run.action_economy.rap_refresh_cost)],
orch.action_economy.rap < int(_db.run.action_economy.rap_refresh_cost) and not orch.can_refresh() and not orch.player_request_refresh() and _br_count(orch) == 4 and int(swaps["n"]) == swaps0 + 4)
# V10 矿道红利:每次成功刷新 escrow 恰 +1 且 fx_grant_gp 行在案;拒绝零变化
func test_mine_profit_gp() -> void:
print("--- V10 矿道红利:刷新固定 GP ---")
var orch = _orch(46032)
var gp_rows := {"n": 0}
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "effect" and String(row.get("effect_id", "")) == "fx_grant_gp":
gp_rows["n"] += 1)
Kit.check("V10 前置:战斗受理", orch.begin_battle(_enc(["mine_profit"])))
_bank_rap(orch, 2)
var escrow0: int = orch.reward_grant().escrow
Kit.check("V10 第 1 次成功 escrow +1(%d→%d)〔换图系批 V10〕" % [escrow0, orch.reward_grant().escrow],
orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 1)
Kit.check("V10 fx_grant_gp 行在案(%d 行)〔换图系批 V10〕" % int(gp_rows["n"]), int(gp_rows["n"]) == 1)
Kit.check("V10 第 2 次成功 escrow +1(%d→%d)〔换图系批 V10〕" % [orch.reward_grant().escrow, orch.reward_grant().escrow + 1],
orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 2)
# 拒绝零变化:RAP 不足
Kit.check("V10 拒绝零变化(escrow=%d)〔换图系批 V10〕" % orch.reward_grant().escrow,
not orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 2 and int(gp_rows["n"]) == 2)
# ---- V6/V8/V9/V7 五件后续效果(迭代 46 卡A T5)----
func _counter(orch, counter_id: String) -> int:
return EffectEngineScript.counter(orch.fx_host().counters_ctx, counter_id)
# 伤害明细行泛收集(type=damage 全量——按来源类与值断言)
func _collect_damage_rows(orch) -> Array:
var rows: Array = []
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "damage":
rows.append(row))
return rows
func _damage_rows_by_source(rows: Array, source: String) -> Array:
var out: Array = []
for r_v in rows:
var r := r_v as Dictionary
if String(r.get("source_class", "")) == source:
out.append(r)
return out
# V6 石层缓冲:全局固定减伤(危险格 pierce 8→6 两档钉死:单刷减 2·同轮多刷不叠加·未刷新轮不减;
# 地形路径减后值=max(下限, effect_value−2)——含下限保持;敌方路径照常单次发射)
func test_stone_mitigation_all_damage() -> void:
print("--- V6 石层缓冲:全局固定减伤 ---")
# S1 同轮双刷 → 危险格 pierce 8 减 2=6(叠加即 4——多刷不增钉死)
var orch1 = _orch(46041)
var rows1 := _collect_damage_rows(orch1)
Kit.check("V6 S1 前置:战斗受理", orch1.begin_battle(_enc(["stone_mitigation"])))
_bank_rap(orch1, 3)
Kit.check("V6 S1 同轮双刷受理", orch1.player_request_refresh() and orch1.player_request_refresh())
Kit.check("V6 S1 guard 累计 2(同轮双刷)", _counter(orch1, "round_swap_guard") == 2)
var mine1 := _dig_until(orch1, func(evt):
return String(evt.get("kind", "")) == "mine" and _is_direct_type(orch1, evt.cell))
Kit.check("V6 S1 前置:直接伤型危险格已挖开(%s)" % str(mine1.get("cell", Vector2i(-1, -1))), not mine1.is_empty())
orch1.minesweeper.settle_batch([{"action": "stop"}])
var pierce_rows1 := _damage_rows_by_source(rows1, "mine_hit")
Kit.check("V6 S1 危险格减伤恰 2 且多刷不叠加(行值 %s∈[6]——叠加态应为 4)〔换图系批 V6〕" % str(pierce_rows1.map(func(r): return int(r.value))),
pierce_rows1.size() >= 1 and int((pierce_rows1[0] as Dictionary).get("value", -1)) == 6)
# S2 未刷新轮:guard 随 round_start 清空 → 同型危险格不减(pierce/magic 表幂 8——heal 型幂 6 不同锚勿混)
var mine2 := _dig_until(orch1, func(evt):
var snap_v2 = orch1.reward_grant().reward_economy.query_full_snapshot(evt.cell)
return String(evt.get("kind", "")) == "mine" and snap_v2 is Dictionary \
and ["pierce", "magic"].has(String((snap_v2 as Dictionary).get("cell_type", ""))))
Kit.check("V6 S2 前置:第二直接伤型危险格已挖开(%s)" % str(mine2.get("cell", Vector2i(-1, -1))), not mine2.is_empty())
orch1.minesweeper.settle_batch([{"action": "stop"}])
var pierce_rows2 := _damage_rows_by_source(rows1, "mine_hit")
Kit.check("V6 S2 未刷新轮不减伤(新轮行值 %s 含 8——round 桶轮始清空)〔换图系批 V6〕" % str(pierce_rows2.map(func(r): return int(r.value))),
pierce_rows2.size() >= 2 and int((pierce_rows2[pierce_rows2.size() - 1] as Dictionary).get("value", -1)) == 8)
# S3 地形路径:减后值 = max(下限, effect_value−2)——下限保持与定值减一并钉死
# (种子 46002=V5② 已证含 flat_damage 变异板;刷新换板后变异重布—— digging guard 内可达)
var orch3 = _orch(46002)
var rows3 := _collect_damage_rows(orch3)
Kit.check("V6 S3 前置:战斗受理", orch3.begin_battle(_enc(["stone_mitigation"])))
_bank_rap(orch3, 2)
Kit.check("V6 S3 单刷受理", orch3.player_request_refresh())
var terr3 := _dig_until(orch3, func(evt):
for vid_v in (evt.get("variant_ids", []) as Array):
if String(_db.combat.variation.row_by_id(String(vid_v)).get("effect", "")) == "flat_damage":
return true
return false)
Kit.check("V6 S3 前置:伤害型变异格已挖开(%s)" % str(terr3.get("cell", Vector2i(-1, -1))), not terr3.is_empty())
orch3.minesweeper.settle_batch([{"action": "stop"}])
var terrain_rows := _damage_rows_by_source(rows3, "地形变异")
var floor_v: int = int(_db.combat.settlement.damage_roll.damage_floor)
var ev_ok := true
for tr_v in terrain_rows:
var tr := tr_v as Dictionary
var vid := String(tr.get("variant_id", ""))
var ev: int = int(_db.combat.variation.row_by_id(vid).get("effect_value", 0))
if int(tr.get("value", -1)) != maxi(floor_v, ev - 2):
ev_ok = false
Kit.check("V6 S3 地形减伤=max(下限,effect_value−2) 逐单闭合(%d 单)〔换图系批 V6〕" % terrain_rows.size(),
terrain_rows.size() >= 1 and ev_ok)
# V9 坑道小憩:轮末合计回复(白盒预置承载上限 3——计数器直改属数据面注入;
# 自然累计 2 与满血未生效两形态分战场钉死)
func test_rest_rotation_cap_and_clear() -> void:
print("--- V9 坑道小憩:轮末恢复 ---")
# R1 白盒预置 3 层 + 刷新闸(when<3 拒第 4 层)→ 停手回复 min(3, 缺血)——护甲降档保缺血 ≥3
var orch1 = _orch(46051)
var rows1: Array = []
orch1.step_row.connect(func(row): rows1.append(row))
Kit.check("V9 R1 前置:战斗受理", orch1.begin_battle(_enc(["tunnel_rest"], 0, 10, 100)))
_bank_rap(orch1, 1) # 开局血差 100(hero_hp 100/max 200)——缺血 ≥3 恒成立,回复面确定
var missing: int = 200 - int(orch1._enemy_system.hero_unit().get("hp", 0))
EffectEngineScript.counter_add(orch1.fx_host().counters_ctx, "round_swap_rest", 3)
Kit.check("V9 R1 预置 3 层后刷新被闸(when<3——上限 3)", orch1.player_request_refresh() and _counter(orch1, "round_swap_rest") == 3)
orch1.minesweeper.settle_batch([{"action": "stop"}])
var heal_rows1: Array = []
var idle_rows1 := 0
for r_v in rows1:
var r := r_v as Dictionary
if String(r.get("type", "")) == "heal" and String(r.get("source_class", "")) == "fx_board_refresh":
heal_rows1.append(r)
if String(r.get("type", "")) == "reward_ineffective" and String(r.get("batch_key", "")) == "round_swap_rest":
idle_rows1 += 1
Kit.check("V9 R1 轮末回复=min(3,缺血%d) 且层清 0(heal 行值 %s)〔换图系批 V9〕" % [missing, str(heal_rows1.map(func(r): return int(r.value)))],
missing >= 3 and heal_rows1.size() == 1 and int((heal_rows1[0] as Dictionary).get("value", -1)) == 3 and _counter(orch1, "round_swap_rest") == 0)
# R2 满血未生效:首轮刷新后停手——授予段 heal 时点主角仍满血(combat 段还手在其后)
var orch2 = _orch(46052)
var rows2: Array = []
orch2.step_row.connect(func(row): rows2.append(row))
Kit.check("V9 R2 前置:战斗受理", orch2.begin_battle(_enc(["tunnel_rest"], 8)))
Kit.check("V9 R2 首轮刷新受理(voucher_rap 入池)", orch2.player_request_refresh())
orch2.minesweeper.settle_batch([{"action": "stop"}])
var heal_rows2 := 0
var idle2 := 0
for r2_v in rows2:
var r2 := r2_v as Dictionary
if String(r2.get("type", "")) == "heal" and String(r2.get("source_class", "")) == "fx_board_refresh":
heal_rows2 += 1
if String(r2.get("type", "")) == "reward_ineffective" and String(r2.get("batch_key", "")) == "round_swap_rest":
idle2 += 1
Kit.check("V9 R2 满血未生效行在案且零 heal 行(未生效 %d·heal %d)〔换图系批 V9〕" % [idle2, heal_rows2],
idle2 == 1 and heal_rows2 == 0 and _counter(orch2, "round_swap_rest") == 0)
# R3 自然累计:同轮双刷 → 回复 2(上限 3 的闸面由 R1 白盒钉住,此处证自然路径)
var orch3 = _orch(46053)
var rows3: Array = []
orch3.step_row.connect(func(row): rows3.append(row))
Kit.check("V9 R3 前置:战斗受理", orch3.begin_battle(_enc(["tunnel_rest"])))
_bank_rap(orch3, 2)
Kit.check("V9 R3 同轮双刷受理", orch3.player_request_refresh() and orch3.player_request_refresh())
Kit.check("V9 R3 自然累计 2 层", _counter(orch3, "round_swap_rest") == 2)
orch3.minesweeper.settle_batch([{"action": "stop"}])
var heal3 := 0
for r3_v in rows3:
if String((r3_v as Dictionary).get("type", "")) == "heal" and String((r3_v as Dictionary).get("source_class", "")) == "fx_board_refresh":
heal3 += 1
Kit.check("V9 R3 轮末 heal 行值 2(自然累计路径)〔换图系批 V9〕",
heal3 == 1 and _counter(orch3, "round_swap_rest") == 0)
# V8 蓄力周转:当轮攻击消费全部层数(上限 3 闸=白盒预置;战斗新建自然清空)
func test_charge_layers_consumed_same_round() -> void:
print("--- V8 蓄力周转:攻击消费层 ---")
var orch1 = _orch(46061)
var rows1 := _collect_damage_rows(orch1)
Kit.check("V8 前置:战斗受理", orch1.begin_battle(_enc(["charge_rotation"], 16)))
# 上限 3 闸:白盒预置 3 层后刷新被拒层(when<3)
EffectEngineScript.counter_add(orch1.fx_host().counters_ctx, "swap_charge_layers", 3)
Kit.check("V8 上限 3 闸(预置 3 刷新不加层)", orch1.player_request_refresh() and _counter(orch1, "swap_charge_layers") == 3)
# 当轮攻击消费:停手 → 主角先手攻击 → 层数全消费清零
orch1.minesweeper.settle_batch([{"action": "stop"}])
var attack_rows := 0
for r_v in rows1:
if String((r_v as Dictionary).get("source_class", "")) == "hero_attack":
attack_rows += 1
Kit.check("V8 当轮攻击消费全部层(攻击行在案 %d·层清 0)〔换图系批 V8〕" % attack_rows,
attack_rows >= 1 and _counter(orch1, "swap_charge_layers") == 0)
# 自然累计:双刷 2 层(voucher_rap 16 入池)→ 停手消费归零
var orch2 = _orch(46062)
Kit.check("V8 前置:战斗受理", orch2.begin_battle(_enc(["charge_rotation"], 16)))
Kit.check("V8 战斗新建层 0(引擎重建自然清空)", _counter(orch2, "swap_charge_layers") == 0)
Kit.check("V8 双刷累计 2 层", orch2.player_request_refresh() and orch2.player_request_refresh() and _counter(orch2, "swap_charge_layers") == 2)
orch2.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("V8 攻击后层清 0(消费面闭合)〔换图系批 V8〕", _counter(orch2, "swap_charge_layers") == 0)
# V7 崩壁冲击:延迟群伤单(刷新时点零伤害行——战斗段末按存活目标结账)
func test_blast_defers_to_combat_segment() -> void:
print("--- V7 崩壁冲击:延迟群伤 ---")
var orch = _orch(46071)
var rows := _collect_damage_rows(orch)
Kit.check("V7 前置:战斗受理", orch.begin_battle(_enc(["wall_blast"], 8)))
Kit.check("V7 刷新受理(voucher_rap 入池)", orch.player_request_refresh())
Kit.check("V7 刷新时点零伤害行(授予段不结账——案C 延迟)〔换图系批 V7〕",
_damage_rows_by_source(rows, "fx_board_refresh").is_empty())
orch.minesweeper.settle_batch([{"action": "stop"}])
var blast_rows := _damage_rows_by_source(rows, "fx_board_refresh")
Kit.check("V7 战斗段末群伤单恰一单(全体存活敌 ×2——行值 2 count≥1)〔换图系批 V7〕",
blast_rows.size() == 1 and int((blast_rows[0] as Dictionary).get("value", -1)) == 2
and int((blast_rows[0] as Dictionary).get("count", 0)) >= 1)
# ---- V11 数据与池(迭代 46 卡A T6)----
# 七件 validate/在册/稀有度 cap 对表/跨表计数器引用闭合/desc 禁词
func test_seven_feats_validate_and_pool() -> void:
print("--- V11 七件数据与池 ---")
var seven := {"vein_reserve": "excellent", "easy_swap": "excellent", "wall_blast": "excellent",
"charge_rotation": "excellent", "stone_mitigation": "common", "tunnel_rest": "common", "mine_profit": "rare"}
Kit.check("V11 feats.validate 零违例(七行全过词表/行键白名单/前缀校验)〔换图系批 V11〕",
_db.crossrun.feats.validate().is_empty())
var all_in := true
var desc_bad := 0
for fid in seven.keys():
var row: Dictionary = _db.crossrun.feats.row_by_id(String(fid))
if row.is_empty() or String(row.get("rarity", "")) != String(seven[fid]) or int(row.get("cap", 0)) != 1:
all_in = false
if String(row.get("desc", "")).contains("雷") or String(row.get("desc", "")).contains("旗"):
desc_bad += 1
Kit.check("V11 七行在册且稀有度/cap 逐件对表〔换图系批 V11〕", all_in)
Kit.check("V11 七件 desc 禁词零命中(雷/旗字面)〔换图系批 V11〕", desc_bad == 0)
# 跨表计数器引用闭合(判据域=换图系七件自身:value_counter/when-id 读取全部有 counter_add 写入方;
# 预存行的消费点记账形态〔如完美保险 insurance_used 由授予段落账〕不在此判据域)
var written := {}
for row_v in (_db.crossrun.feats.rows as Array):
for tv in ((row_v as Dictionary).get("triggers", []) as Array):
var do_d: Dictionary = (tv as Dictionary).get("do", {})
if String(do_d.get("kind", "")) == "counter_add":
written[String(do_d.get("counter_id", ""))] = true
var refs_ok := true
for fid2 in seven.keys():
var row2: Dictionary = _db.crossrun.feats.row_by_id(String(fid2))
for tv2 in ((row2.get("triggers", []) as Array)):
var tr2 := tv2 as Dictionary
var do2: Dictionary = tr2.get("do", {})
for reader in [String(do2.get("value_counter", "")), String(do2.get("value_from", ""))]:
if reader != "" and not written.has(reader):
refs_ok = false
for c_v in (tr2.get("when", []) as Array):
var cid := String((c_v as Dictionary).get("id", ""))
if (c_v as Dictionary).get("fact", "") == "counter" and cid != "" and not written.has(cid):
refs_ok = false
Kit.check("V11 七件跨表计数器引用闭合(value_counter/when-id 全部有 counter_add 写入方)〔换图系批 V11〕", refs_ok)
Kit.check("V11 换图系三计数器在写入集(swap_charge_layers/round_swap_guard/round_swap_rest)〔换图系批 V11〕",
written.has("swap_charge_layers") and written.has("round_swap_guard") and written.has("round_swap_rest"))
@@ -1 +0,0 @@
uid://bmsh0idfmpw3j
+5 -5
View File
@@ -461,8 +461,8 @@ func _smoke_adventure() -> void:
func _smoke_feats() -> void:
print("--- 专长重设计实装(feat_table 四级 · 专长带池接线 · 订阅面逐件)---")
# ① 现行表牌库构成:37 件全量入池——102 数值令牌+37 relic 令牌(四级 rarity 直映射)
Kit.check("feat_table 已加载(37 件=基础 27+迁入 2+完美保险 1+迭代 46 换图系七件——换代)", _db.crossrun.feats != null and (_db.crossrun.feats.rows as Array).size() == 37)
# ① 现行表牌库构成:30 件全量入池——102 数值令牌+30 relic 令牌(四级 rarity 直映射)
Kit.check("feat_table 已加载(30 件=基础 27+迁入 2+卡② 完美保险 1——迭代 31 卡② 换代)", _db.crossrun.feats != null and (_db.crossrun.feats.rows as Array).size() == 30)
Kit.check("feat_table.validate 零违例(四级 rarity/cap 域/trigger 词表/行键白名单全过)", _db.crossrun.feats.validate().is_empty())
var a_r0 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(71))
Kit.check("专长带场景:冒险启动(seed 71)", a_r0.start_adventure(71))
@@ -473,7 +473,7 @@ func _smoke_feats() -> void:
feat_tokens += 1
var tk := String((t as Dictionary).get("tier", ""))
tier_counts[tk] = int(tier_counts.get(tk, 0)) + 1
Kit.check("专长带入池:37 件四级直映射(普通 14/优秀 13/稀有 10/传说 0)——牌库 102+37=139〔专长批 V2+迭代 46 换图系七件换代〕", feat_tokens == 37 and int(tier_counts.get("common", 0)) == 14 and int(tier_counts.get("excellent", 0)) == 13 and int(tier_counts.get("rare", 0)) == 10 and a_r0.deck().deck.size() == 139)
Kit.check("专长带入池:30 件四级直映射(普通 12/优秀 9/稀有 9/传说 0)——牌库 102+30=132〔专长批 V2+卡② 完美保险迁入〕", feat_tokens == 30 and int(tier_counts.get("common", 0)) == 12 and int(tier_counts.get("excellent", 0)) == 9 and int(tier_counts.get("rare", 0)) == 9 and a_r0.deck().deck.size() == 132)
# ② 旧 27 件零残留钉面(V2——arcane_ember 系旧件表首行;全仓 grep 零残留归静态核查)
Kit.check("旧 27 件已退役:arcane_ember/magma_body 表内零残留〔专长批 V2〕", _db.crossrun.feats.row_by_id("arcane_ember").is_empty() and _db.crossrun.feats.row_by_id("magma_body").is_empty())
Kit.check("迁出收尾:treasure_items 无 venom_edge/seal_burst 行(同调目录自然收窄)〔专长批 V13〕", _db.crossrun.treasure_items.row_by_id("venom_edge").is_empty() and _db.crossrun.treasure_items.row_by_id("seal_burst").is_empty())
@@ -769,7 +769,7 @@ func _smoke_feats() -> void:
and orch0.fx_host().fx_emit("mine_hit", {"damage": 5}).is_empty()
and orch0.fx_host().fx_emit("battle_won", {"round": 1}).is_empty())
# ⑦c 特性持有呈现段(C 区「特性」节;呈现面在 06 在册)
var panel = load("res://presentation/views/hero_info_panel_view.gd").new(_db.presentation.battle_layout)
var panel = load("res://presentation/views/hero_info_panel_view.gd").new()
panel.set_feat_holdings([])
Kit.check("特性段空态隐藏(视图空态纪律同特性段)", not panel._feat_section.visible)
panel.set_feat_holdings(["疼痛记忆 ×1", "双管镐 ×1"])
@@ -788,7 +788,7 @@ func _adv_drive_to_shop(a, st: Dictionary, autoplay) -> bool:
autoplay._drive_level(a.runner(), st, null, "normal", "min")
if String(st["result"]) != "":
return false
if a._bp_store._pending_next_level:
if a._pending_next_level:
# 三选一停点确认(确定性选件后推进,与 run_adventure 自动策略同口径;TP0 令牌化)
if not a.stop()._pending.is_empty():
var bcands: Array = a.stop()._pending["candidates"]
@@ -14,11 +14,6 @@ func _init() -> void:
# ① 词表在册断言(扩签面:2 事件+8 事实+7 效果类型+1 算子)
Kit.check_quiet(EffectEngineScript.EVENTS.has("dig_stop") and EffectEngineScript.EVENTS.has("battle_won"),
"新事件在册:dig_stop/battle_won〔专长批 V1〕")
# ①-a 换图系两事件两效果在册(迭代 46 扩签——旧出域负例全部保留)
Kit.check_quiet(EffectEngineScript.EVENTS.has("refresh_request") and EffectEngineScript.EVENTS.has("board_refreshed"),
"换图系新事件在册:refresh_request/board_refreshed〔换图系批 V1〕")
Kit.check_quiet(EffectEngineScript.EFFECT_KINDS.has("grant_reserve_ap") and EffectEngineScript.EFFECT_KINDS.has("apply_discount"),
"换图系新效果在册:grant_reserve_ap/apply_discount〔换图系批 V1〕")
Kit.check_quiet(EffectEngineScript.FACTS.has("hp_ratio") and EffectEngineScript.FACTS.has("is_edge_cell")
and EffectEngineScript.FACTS.has("new_resource_type") and EffectEngineScript.FACTS.has("dug_under_seal")
and EffectEngineScript.FACTS.has("ap_exhausted_last") and EffectEngineScript.FACTS.has("ap_left")
+2 -2
View File
@@ -78,7 +78,7 @@ func _init() -> void:
# 账本保留(契约 1):主动件充能与每冒险种行入口零改动(单源化后=差额种行幂等——不吞装配)
a._attunement = ["incense_stick"]
a._loadout.set_attunement(["incense_stick"])
a._bp_store._loadout_seeded = false # 模拟首关 bind 时点(分派走 seed_for_adventure 分支——成员随断点件收编改址)
a._loadout_seeded = false # 模拟首关 bind 时点(分派走 seed_for_adventure 分支)
a._loadout_on_level_inventory() # 种行(首关 bind 时点语义)
var incense_left := 0
for r in a.runner().treasure_inventory.snapshot_inventory():
@@ -289,7 +289,7 @@ func _init() -> void:
# A12 呈现两态文案与 _format_row 一致:两态均零「雷」字面
var LogViewScript := load("res://presentation/views/battle_log_view.gd")
var lv = LogViewScript.new(db, db.presentation.battle_layout)
var lv = LogViewScript.new(db)
var broken_row := {"type": "seal_miss", "source_class": "seal_miss", "origin_cell": [0, 0],
"round_index": 1, "display_name": "封印未中", "value": 0, "highlight": "perfect_broken"}
var forgiven_row := {"type": "seal_miss", "source_class": "seal_miss", "origin_cell": [0, 0],
@@ -1477,7 +1477,7 @@ func _adv_drive_to_shop(a, st: Dictionary, autoplay) -> bool:
autoplay._drive_level(a.runner(), st, null, "normal", "min")
if String(st["result"]) != "":
return false
if a._bp_store._pending_next_level:
if a._pending_next_level:
# 三选一停点确认(确定性选件后推进,与 run_adventure 自动策略同口径;TP0 令牌化)
if not a.stop()._pending.is_empty():
var bcands: Array = a.stop()._pending["candidates"]
@@ -199,7 +199,7 @@ func _test_negative_cases() -> void:
# 现行 31;本套件零扩签,钉现行数(将来任何加词即转红提示复核)。
func _test_effect_kinds_closed() -> void:
Kit.check("效果引擎效果类型闭集 34 种(迭代 46 换图系扩签 grant_reserve_ap/apply_discount 换代)〔锻造工坊批 V11〕",
EffectEngineScript.EFFECT_KINDS.size() == 34)
Kit.check("效果引擎事件词表在册 22 种(迭代 46 换图系扩签 refresh_request/board_refreshed 换代)〔锻造工坊批 V11〕",
EffectEngineScript.EVENTS.size() == 22)
Kit.check("效果引擎效果类型闭集 32 种(锻造工坊批零新增口径随迭代 31 卡② 扩签 forgive_seal_miss 换代)〔锻造工坊批 V11〕",
EffectEngineScript.EFFECT_KINDS.size() == 32)
Kit.check("效果引擎事件词表在册 20 种(本批零扩签参照面)〔锻造工坊批 V11〕",
EffectEngineScript.EVENTS.size() == 20)
@@ -126,7 +126,7 @@ func _init() -> void:
UiKit.motion_scale = 1.0
# ---- 断言④:排播两处取用按行 target_id 解析站桩(d12 排播半——假 stage 记参)----
var rp = RowPacerScript.new()
var fstage := FakeStage.new(null, root.get_node("/root/ConfigDB").presentation.battle_layout)
var fstage := FakeStage.new(null)
var ffx := FakeFx.new()
rp.stage = fstage
rp.board_view = main.ui.battle.board_zone.board_view
@@ -41,7 +41,7 @@ func _init() -> void:
# ① C 区两段头(u1/u2——直建 hero 面板,adventure_feats_test ⑦c 族同构)
func _sweep_hero_panel() -> void:
var panel = load("res://presentation/views/hero_info_panel_view.gd").new(_db.presentation.battle_layout)
var panel = load("res://presentation/views/hero_info_panel_view.gd").new()
var feat_head := _first_label(panel._feat_section)
var curio_head := _first_label(panel._curio_section)
Kit.check("C 区专长段头==「专长」(u1 换代——段头文案钉值)〔wa1〕",
@@ -95,7 +95,7 @@ func _sweep_source_names() -> void:
row_view._hide_tip()
Kit.check("结算表 source_label_map[mine_hit]==「危险格」(u6 换代——ConfigDB 真装载)〔wa7〕",
String(_db.combat.settlement.core.source_label_map.get("mine_hit", "")) == "危险格")
var log_view = load("res://presentation/views/battle_log_view.gd").new(_db, _db.presentation.battle_layout)
var log_view = load("res://presentation/views/battle_log_view.gd").new(_db)
var row_txt := String(log_view._format_row({"type": "effect", "source_class": "mine_hit",
"display_name": "挖掘反噬", "value": -5, "pool_value": -5, "round_index": 1}))
Kit.check("战斗日志 mine_hit 来源行含「(危险格)」(u6 消费半——_format_row 生产路径)〔wa7〕",
@@ -72,7 +72,7 @@ func _init() -> void:
UiKit.motion_scale = 1.0
# ---- ⑤ 站桩按 id 解析(两口共用同名字敌载体)----
var stage = StageScript.new(null, load("res://data/tables/battle_layout.tres"))
var stage = StageScript.new(null)
root.add_child(stage)
var s1 = StandeeBodyScript.new("enemy")
var s2 = StandeeBodyScript.new("enemy")
@@ -1,16 +1,12 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 战斗一屏样张批 V5–V12 断言面(2026-10-29 迭代 43 立五项;2026-10-31 迭代 46 卡B 增 V10–V12)——
# 实装定值的真实入口断言:
# 战斗一屏样张批 V5–V9 断言面(2026-10-29 迭代 43)——五项实装定值的真实入口断言:
# V5 血条填充 = 危险红(UiKit.HP_BAR_FILL 值单点 + 绘制点源码锚:全仓零 #689CE8 与零裸
# Color(0.41,0.61,0.91);条底收 HP_BAR_TRACK 常量);
# V6 站桩名字行垫 HP_BAR_BACK 素底且不压地台(名字行基线 y < 地台顶 y——name_line_probe 几何出口);
# V7 顺位徽标提档 UiKit.FS_HINT(值断言 + 绘制点源码锚,§7.4 清单外 11–13px 提档);
# V8 护甲图标三键(light/ward/heavy)绑对应 24×24 PNG(ResourceLoader 装载结果 + name_line_probe
# 绑定面);
# V9 地台件 scene_stage_plaza.png + 护甲三枚入包可加载且尺寸 48×12 / 24×24(§5.2/§5.3 工单);
# V10 名字行提档 FS_HINT 14(探针 name_font_size 实读 + 两绘制点源码锚 + FS_CAPTION 计数锚==1);
# V11 退役常量不在册 + 掩尘 BRICK 逐位 + 顶缘线/口沿高光两绘制点改指源码锚;
# V12 PLAZA_EDGE 逐位 #4C4038 + 回退描边改指源码锚。
# V9 地台件 scene_stage_plaza.png + 护甲三枚入包可加载且尺寸 48×12 / 24×24(§5.2/§5.3 工单)。
# headless 无渲染面:像素采样以常量单点 + 源码锚 + 探针几何承载(观测面注记,验收对照表 V5 行)。
# 运行:godot --headless --path game --script res://tests/unit/presentation/standee_present_values_test.gd(expect 0)
extends SceneTree
@@ -52,39 +48,14 @@ func _init() -> void:
Rect2(nl.plate).end.y <= float(nl.plinth_top_y) + 0.01)
Kit.check("名字行素底 = HP_BAR_BACK 深档 #282220(挂载底色口径)〔样张批 V6〕",
Color(nl.plate_color).to_html(false) == "282220")
Kit.check("基线读数与实装几何同源(基线 = 地台顶上方 8px 档:150 < 158——14px 档牌顶+18)〔样张批 V6〕",
absf(float(nl.baseline_y) - 150.0) < 0.51 and absf(float(nl.plinth_top_y) - 158.0) < 0.51)
Kit.check("基线读数与实装几何同源(基线 = 地台顶上方 9px 档:149 < 158)〔样张批 V6〕",
absf(float(nl.baseline_y) - 149.0) < 0.51 and absf(float(nl.plinth_top_y) - 158.0) < 0.51)
# ---- V7 顺位徽标提档 FS_HINT ----
Kit.check("FS_HINT = 14(§7.4 六档)〔样张批 V7〕", UiKit.FS_HINT == 14)
Kit.check("顺位徽标绘制点取 UiKit.FS_HINT(源码锚)〔样张批 V7〕",
body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT"))
# ---- V10 名字行提档 FS_HINT 14(迭代 46 卡B——五取值点+基线随档联动) ----
Kit.check("名字行探针字号实读 = FS_HINT(几何单点出口 name_font_size)〔名字行 V10〕",
int(nl.name_font_size) == UiKit.FS_HINT)
Kit.check("名字与护甲两绘制点取 FS_HINT(源码锚——沿 V7 先例)〔名字行 V10〕",
body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, UiKit.WARM")
and body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, col"))
Kit.check("全文件 FS_CAPTION 仅剩消散提示一处(计数锚==1——三处宽度测量点强制换代)〔名字行 V10〕",
body_src.count("UiKit.FS_CAPTION") == 1)
# ---- V11/V12 地台色合并(迭代 46 卡B——常量面载体=Script 常量表,源码锚正证改指目标色) ----
var uikit_script: Script = load("res://presentation/views/ui_kit.gd")
var cmap: Dictionary = uikit_script.get_script_constant_map()
var cell_src := _read_source("res://presentation/views/cell_base_layer.gd")
Kit.check("退役常量不在册(STAND_STONE_RIM/STAND_STONE_EDGE 零键)〔名字行 V11〕",
not cmap.has("STAND_STONE_RIM") and not cmap.has("STAND_STONE_EDGE"))
Kit.check("掩尘 BRICK 值逐位 #C9AF83(合并目标①)〔名字行 V11〕",
UiKit.BRICK.to_html(false) == "c9af83")
Kit.check("顶缘受光线与口沿高光两绘制点改指 BRICK(源码锚)〔名字行 V11〕",
body_src.contains("ped.size.x, 2.0), UiKit.BRICK, true")
and cell_src.contains("FACADE_HIGHLIGHT_PX), UiKit.BRICK, true"))
Kit.check("PLAZA_EDGE 值逐位 #4C4038(合并目标②)〔名字行 V12〕",
UiKit.PLAZA_EDGE.to_html(false) == "4c4038")
Kit.check("回退描边绘制点改指 PLAZA_EDGE(源码锚)〔名字行 V12〕",
body_src.contains("UiKit.PLAZA_EDGE"))
# ---- V8 护甲图标三键绑 24×24 PNG ----
var armor_paths := {"light": UiKit.TEX_ARMOR_LIGHT, "ward": UiKit.TEX_ARMOR_WARD,
"heavy": UiKit.TEX_ARMOR_HEAVY}
@@ -383,7 +383,7 @@ func _smoke_type_glow() -> void:
# 本段把现行行形态逐型喂进真实视图:拆行残留与行字段错位在此转红)----
func _smoke_battle_log_view() -> void:
print("--- 战斗日志视图行格式化(行协议 9 类呈现层同步闸)---")
var view = load("res://presentation/views/battle_log_view.gd").new(_db, _db.presentation.battle_layout)
var view = load("res://presentation/views/battle_log_view.gd").new(_db)
# 敌方行动伤害行(现行形态:baseline 在场、attach 已出协议——修复前此行即 Invalid access)
view.append_row({"type": "damage", "display_name": "地精 撕咬", "value": 2, "target": "主角",
"baseline": 3, "roll": 15, "ac": 12, "tier": 100,
@@ -1 +0,0 @@
uid://ceh2mcde5wrtj
+26
View File
@@ -0,0 +1,26 @@
# 迭代 44 反哺点清单——2026-10-30
要写进权威文档的点逐条列落点(改哪一节、改成什么、依据);用户在会话中裁决后归文档批落文。实现批禁写 `design/` 与根规则文件,本清单是唯一移交通道。
## 架构文档(design/engineering/architecture/architecture.md)
1. **「模块归属·呈现层」ui_registry 行现状化**——现登记「实现批建件」态;实现后改现状陈述:UiRegistry/UiScreens/UiBattle/UiModals 四件已建(全 RefCounted、类变量 3/9/5/14、UiModals.flow 随容器构造即建)。
2. **同表 main_ui_build 行「层序逻辑不在此件——唯一事实源仍是 main.LAYER_CONTRACT + _apply_layer_contract()」句失效**——唯一事实源已迁 `ui_registry.LAYER_CONTRACT`+`ui.apply_layer_contract(root)`;该句改指 ui_registry(依据=设计文档 §3.3 建议句+实现实况)。
3. **「呈现层装配与会话生命周期」节屏成员寻址迁注册面句核对**——v1.25 已预写「探针挂载假设=main.ui.<域>.<句柄>」;实现实况与其一致,但需补两句实况细节:①ModalFlow 实例随 UiModals 容器构造建立(装配早期注入点依赖其先在座);②探针契约面新增两件取证探针(shot_probe/fx_shot_probe)随批改址。
4. **「战斗编排域拆件定稿」节轴丁两行「构造=引用束注入」句与实态口径差**——实态=RoundCycle 三参直构(host/fx_host_ref/reward_grant_ref,五段构造透传不落成员)、BattleFacts 零参直构;句按设计文档 §8 预判「三参直构无需束」现状化。
5. **「模块归属·功能层」RoundCycle/BattleFacts 两行实测值换代**——RoundCycle 类变量 8/公开方法 3;BattleFacts 类变量 15(贴顶)/公开方法 4(reset_round+三 mark);宿主 battle_orchestrator 实测 15 成员/811 行(文件长键 1035 已远超实况——基线 refresh 后自然消失)。
6. **「测试与质量域」S13 豁免第三条注销句现状化**——v1.25 预写「设计闭合、实现批注销」;键账已达成(宿主实测 15),注销句转已实施事实。**同节 tune_sim 测量面豁免注销句现状化随卡③ v1.1 恢复**:裸键已消除(实测命中消失、全文件零循环);注销句保持(注销对象是 await 豁免、不依赖行为改动申报),句内「随批申报行为改动一处(收尾置假还原)」半句已失效(前提被迭代 41 测试债批 215da315 消化、申报已剥离)——现状化时一并修正为收尾现行陈述(「运行态收尾在 run() 尾每批一次执行」)。
## gdstyle 基线与闸面(tools/)
7. **基线 refresh 义务(用户人工,非落文)**——`python tools/gdstyle_gate.py --refresh`:预期消失键=main.gd max-class-variables=49(实测 15)+battle_orchestrator max-class-variables=38(实测 15)+battle_orchestrator max-file-length=1035(实测 811)+轴丙遗留归绿键;合计消失 ≥2 条触发骤降守卫,须 `--force`。与待办清单「零豁免五键清偿批」批末一次人工 refresh 合并执行。
8. **S13 no-self-assign 检查的误报形态观察**(登记不阻塞)——`X.y.z = y` 形态(赋值链尾名与右侧成员同名)被判自赋值;本批以 self 前缀消解(level_runner:520)。若后续批次再现此巧合,可考虑工程批把检查器改为比较完整接收链(登记归「迭代 42 批末移交小件」同类工具小件)。
## 待办清单(根流程清单——状态回退拟稿,落笔归上游段/用户)
9. **「零豁免五键清偿批」条目测速循环批子项状态(净零处理——原回退拟稿作废)**:卡③ 停卡呈报→用户裁重裁→写卡会话重写 v1.1(零待裁点,重裁时订正 S8 十文件与两处行号注记)→本会话恢复施工收口——条目状态直推「文档设计已闭合+已写卡+已编迭代 44」无回退中间态;重裁经过实况记录于实现报告 §5/§5.1,本条不再承载状态回退义务。
10. **「零豁免五键清偿批」条目键账进度换代**——五键中两键已真实清偿(main.gd 49→15、battle_orchestrator 37→15),待基线 refresh 收账;呈现层状态件批(迭代 45 在册)两键不受影响。
## 其他
11. **main.gd 死 preload 存量**(工程小件登记建议)——拆件遗留的零使用 preload 常量 8 条(UiAssembler/TuneSimController/DebugSessionScript/TreasureInteractController/CurioInteractController/CampShopPresenter/BoardResolversScript/三场景 preload 中 main 零使用者);本批仅清理与迁移直接相关的 ModalStack/RowPacer 两条,其余属存量债不扩范围;归工程改动直改批顺路清理。
+118
View File
@@ -0,0 +1,118 @@
# 迭代 44 实现报告(组合根注册面批+战斗编排归约批+测速循环批)——2026-10-30
状态:三卡全部施工收口(卡③ 按停卡呈报建议案重裁为 v1.1 后施工);迭代总收口(批末闸/审计/合并)进行中。
## 0. 各卡状态
| 卡 | 状态 | 提交(worktree iter44,基点 c8f5091d) |
|---|---|---|
| 卡① 组合根注册面批 | **收口**——main.gd 49→15、四新件建件、全消费面改址 | bee0682f(T1 建件)/b89ee850(T2 改形)/784d581b(T3 改址)/1929516a(T4 消灭收口) |
| 卡② 战斗编排归约批 | **收口**——battle_orchestrator 38→15(实测 37 轴丙基数起算)、RoundCycle/BattleFacts 两新件 | 24f3ca51(T1 BattleFacts)/a832965a(T2 RoundCycle)/15c833e0(T3 宿主收束)/3e9d3afa(T4 键账) |
| 卡③ 测速循环批 | **收口(v1.1 重裁后施工)**——v1.0 停卡呈报→用户裁重裁→v1.1 剥离行为改动申报面后 T0–T3 全落 | c82ef23f(T1 步进状态机)/04783fae(T2 对拍套件) |
## 1. 卡① 实际对照表(卡片 §8 十一行 → 实况)
| # | 需求点 | 改动实况 | 验收测试实况 | 结果 |
|---|---|---|---|---|
| 1 | 注册面建件+合同机器迁驻 | ui_registry.gd(LAYER_CONTRACT 逐字迁入+node(key) 19 键 match+apply_layer_contract 唯一落位口)+ui_screens/ui_battle/ui_modals 三容器 | 系统测试 V1c(system_test.gd:94-106 改址 main.ui.LAYER_CONTRACT/main.ui.node,尾缀〔装配生命批 V1〕保留)——树序逐对保序 | 绿 |
| 2 | main.gd 改形回归(49→15) | 28 迁出+7 消灭+1 新增=22(T2 中间态)→15(T4 终态);六转发方法+_flush_row_queue 退役 | run_smoke 15 项 0 失败+run_system_test 69 项 0 失败;S13 静态判据=gdstyle 实测 15(基线键 49 已不命中、成死键) | 绿 |
| 3 | 装配与流程件改址 | main_ui_build 八步赋值目标 root.ui.*(八步顺序/add_child 序/连接语句逐条不变);三流程件同表映射;smoke_boot get() 4 改 2 留 | 冒烟主屏存活面六断言+系统测试 V2 句柄等价五连比(system_test.gd:108-109 改址) | 绿 |
| 4 | 探针面批量改址 | 探针九件+probe_ui_util+shot_probe/fx_shot_probe(决定记录 2)共约 460 行机械改写 | probe_gate --click --full 全段退出码 0(九件 PASS) | 绿 |
| 5 | 系统测试面改址+RunScope 生命周期 | system_test V1a/V1c/V2/V3 矩阵取址改指 | V1–V9 全绿(V3a–V3i 存活性矩阵第五引用面逐面同源) | 绿 |
| 6 | 别名/接缝成员消灭(7 个) | _seed_edit/board_view/ap_bar/_row_queue/_modals/_settlement_stop_pending/_deferred_modal 全删;消费方改址(pacing_queue 4 处+接缝探针三件+main 自身) | pacing_queue 绿+ui_struct_probe 登记序断言(改 flow.stack)+t17_defer_probe 挂起态断言(改 flow._deferred) | 绿 |
| 7 | 六转发模态方法退役 | _open_modal/_close_modal/_on_modal_closed/_input_locked/_open_modal_after_playback/_on_pump_ended 退役;调用方改指 ui.modals.flow 五真方法+pump_ended 注入改 flow 方法 | modal_flow 绿+系统测试 V3g 模态开合段+ui_click_probe 模态开合路径 | 绿 |
| 8 | V1d 源码断言重锚 | source_guard_test.gd 重锚全 presentation 目录扫描(move_child 只许 ui_registry.apply_layer_contract 体内),尾缀〔注册面 V1〕 | source_guard 绿(8 checks) | 绿 |
| 9 | V6 源码断言改串 | theme_projection_test.gd V6 改断言 ui.modals.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed),尾缀〔注册面 V2〕 | theme_projection 绿(164 checks) | 绿 |
| 10 | 视觉零变化主证 | 全批呈现层改址零像素改动 | render_gate golden 零差异(shot_probe 七屏+golden 全过) | 绿 |
| 11 | 改址完整性收口 | 49 名逐名 grep 残留=0(词形态+字符串键形态两轮全量扫描,T3 提交信息留档) | 批末代码域闸归迭代总收口(待跑);grep 自检静态判据已留档 | 静态部分绿 |
静态判据申报执行实况(卡片 §8 域口径):S13 键账对账=gdstyle 实测 main.gd 15、四新件零新键(T1/T4 两轮 static_scan 确认);基线 refresh 留用户人工(见 §4 遗留义务)。
## 2. 卡② 实际对照表(卡片 §11 V0–V6 行 → 实况)
| 行 | 需求点 | 改动实况 | 验收测试实况 | 结果 |
|---|---|---|---|---|
| V0 | 行为零变化(总判据) | 成员迁移与改指;RNG 五流零触碰(behavior_roll 随件迁移取号序逐字);五信号留宿主;两快照签名载荷逐字 | 关联套件全绿零改址应红项(round_flow/settle_segments/skill_unlock/trigger_cond/seal_polarity/feat_vouchers/variant_placement/meta_forge/adventure_feats/treasure_effects/roll_multiplier/breakpoint_resume/level_shop_economy/table_structure_guard) | 绿 |
| V1 | 两新件建件接线 | battle_facts.gd(15 成员贴顶+reset_round 逐字+三 mark 写口)+round_cycle.gd(8 成员+三参直构+五段构造逐字迁入) | settle_segments_test V1 断言(_rounds 在座且 oc 回指+_facts 在座+五段段件齐 _rounds)〔编排归约 V1〕 | 绿 |
| V2 | reset_round 复位边界 | reset_round 只清 reveals/seals/path 三项 | round_flow_skill_pool_test 内嵌断言:终局后 presentation_snapshot()["refresh_used"] == 末轮结算快照 economy.refresh_used〔编排归约 V2〕 | 绿 |
| V3 | 段驱动与摇号迁移 | 三函数迁出;宿主调用点五处改址;trigger_cond 3 处改指 | round_flow 全套+settle_segments V4-3+skill_unlock+trigger_cond(改指后绿)——同种子回放断言零改址钉住 | 绿 |
| V4 | _hero_atk 派生读 | 成员删→私有函数;combat_segment :56 改调 | roll_multiplier_test 内嵌:override 贯通 spawn+缺省回退表值〔编排归约 V4〕;攻击单 base_value 贯通由既有 hero_atk_val=100 全链路夹具承载 | 绿 |
| V5 | treasure_inventory 注入改址 | 宿主成员与 setter 删、五处读改指、level_runner:520 改经 reward_grant() 出口直注 | treasure_effects_test 内嵌引用同一性断言〔编排归约 V5〕+ob8/ob11/ob13 既有 use_treasure 受理链改址后全绿 | 绿 |
| V6 | 封印判定账/装板溯源/受理事实迁移 | 事实族改指(grant_segment 18 处+宿主全量);三 mark 写口升级;attune 读源改 _facts | seal_polarity/feat_vouchers(契约 2 计数纯净 R2)/variant_placement/meta_forge/adventure_feats/table_structure_guard(attune 外部面零改址应绿——最硬的外部面零变化证)全绿 | 绿 |
行为改动申报核对:diff 中行为改动零条(全部非行为改动——成员迁移与改指;完美写口升级=同一时点同一增量改经方法单点)。测试削弱申报:零申报兑现(测试改指只动成员寻址;新增断言 V1/V2/V4/V5 四条+V1d/V6 重锚两条=卡片 §8 申报全集)。
## 3. 实现决定记录(代拍板白名单外事项备案)
卡①:
1. **ModalFlow 实例随 UiModals 容器构造即建**(ui_modals._init)——装配第二步 row_pacer.pump_ended 注入早于第八步 flow 建立,转发壳退役后需 flow 先行在座;查询/钩子 Callable 注入时点不变(_build_modal_flow 内语句序不变),八步顺序零变化。
2. **shot_probe/fx_shot_probe 纳入改址**——两件是 render_gate 取证探针、不在卡片「探针九件」清单(probe_gate 段构成),但同为 main.X 一级地址消费方;T3 改址完整性 grep 自检抓漏后纳入(render_gate 对照表行 10 的承载件,属消费面全量改址义务)。
3. **ui_tune_probe 零连接巡检白名单登记 flow.close**——表 D 申报「closed bind 目标改 flow.close.bind」的机检联动:巡检白名单原钉 main._on_modal_closed(组合根转发壳),改钉 main.ui.modals.flow.close(同一连接的新宿主);判据面「closed 外发消费目标是模态编排口」语义不变,conn_count=11 断言零变化。
4. **layout_probe 三区 get() 字符串键改容器字段键**——卡片表 D 字符串键清单只列 smoke_boot 6 处;layout_probe:60 `main.get(key)`(key="stage"/"info_panel"/"board_zone")为漏网字符串键,改 `main.get("ui").battle.get(key)`(战区域容器字段名与三区合同键同名)。
5. **curio_overlay/confirm_dialog 两词在装配/流程件替换表漏列**——main.gd 表有、main_ui_build 与三流程件表漏,冒烟(Invalid assignment on main.gd)与 ui_click_probe sl7(开窗调用静默失效)抓漏后补改;教训=机械改写必须逐词对照 T0 名单全量扫描(T3 终检已按 49 名逐词复查全域归零)。
卡②:
1. **grant_segment._seal_preview_area 的 oc.treasure_inventory 读为 T3 盘点漏项第五处**(卡片 T3 列宿主四处读)——SCRIPT ERROR 抓漏后改指 reward_grant.treasure_inventory。
2. **treasure_effects_test 五处直构注入 obX.treasure_inventory**(ob8/ob11/ob13 注入与读、ob14 段)——设计文档 §11 盘点「1 处」漏项;同属「成员寻址改指、断言零变化」改经 reward_grant() 出口。
3. **level_runner 注入行加 self 前缀**消 S13 no-self-assign 文本巧合(`X.treasure_inventory = treasure_inventory` 左侧链尾名与右侧成员同名);语义零变化。
4. **_hero_atk 派生读落位**:受理段写点删除、spawn 读点改调、combat_segment 消费改调——「读面即引用、无第二账」先例同款。
5. 宿主内 encounter/attunement 词替换以占位符保护三处字符串键(behavior_roll 前置流名 "encounter"/BoardInstaller bundle 键 "encounter"/enc.get("attunement") 键)——键字面零变化。
## 4. 遗留读数义务与登记项
- **gdstyle 基线 refresh(用户人工,批末总收口后)**:`python tools/gdstyle_gate.py --refresh`——卡① 死键 1 条(main.gd max-class-variables=49)+卡② 预期死键 1 条(battle_orchestrator max-class-variables=38;文件长键 1035 已由轴丙归绿基线在册需一并核对)+轴丙遗留归绿键,合计消失 ≥2 条触发骤降守卫须 `--force`(与待办清单「零豁免五键清偿批」批末一次人工 refresh 合并执行口径一致)。
- **实机目检总清单(登记不阻塞)**:①三屏切换与模态开合实机过一遍(卡① T4 义务——冒烟/探针/golden 已机检承载,真人观感复核登记);②战斗一局全流程观感(卡② 结构迁移零行为变化的一般性目检)。
- **能力清单核对(收尾核对义务)**:本批卡① 零新增能力(改址只换地址)、卡② 零新增能力(新增断言全部内嵌既有函数)——game/tests/README.md 零增删改,批末核对表上代表用例名与实际落地一致(未改名,核对通过)。
- **批末代码域闸**(run_all_gates.sh --code)归迭代总收口——卡③ 裁决后与 reshape 串联执行。
## 5. 卡③ 停卡呈报(缺陷移交——按卡片 §13 L3 条款①预设停机点)
**分歧点**:卡③ §3 断言「`_run_one_level` 尾部两行 :67 `panel.set_sim_running(false)`/:68 `_running = false` 每局执行一次(潜伏红本体)」与现行代码不符——实测两行在 `run()` 尾 :53-54(for 循环外、每批恰一次),`_run_one_level` 尾部 :67-68 实为败局节点记账。
**定谳证据**(三重):
1. git 历史:迭代 41 测试债批提交 215da315(2026-10-29 合回)提交信息明载「两个产品回归随批修(……**tune_sim 运行态收尾归还 run**)」——潜伏红已由该批先修复。
2. T0 实跑面一:`--only=standee_fx_sweep` 全绿(18 checks)——wd8 三断言(`drv.seen == [true, true]`/收尾双运行态假/报告非空,现位 :145-147)现行全 PASS。
3. T0 实跑面二:ui_tune_probe 全链路 PASS——⑤ 守卫环 1800 帧预算内退出、输出四串在(模拟 N=10 完整输出)。
**两种读法**:
- 读法 A(证据支持):卡片成文(2026-10-29 v1.0 定稿)时点潜伏红确实在(u23 卷入形态),同日合回的迭代 41 测试债批把收尾归还 run() 尾——卡片「现状考证」被上游批施工消化变质。215da315 提交信息与代码现状一致。
- 读法 B:卡片考证当时即误读(:67-68 为败局记账非置假)。与 215da315 提交信息矛盾,不采。
**影响面**:
- 已申报行为改动 V3(收尾置假还原)**无对象**——还原动作已由迭代 41 测试债批完成并申报。
- 目标 2「wd8 三断言与 ui_tune_probe ⑤ 零改动**转绿**」变「零改动**保持绿**」。
- 核心义务仍有效:S13 await-in-loop 裸键消除(tools/gdstyle_baseline.txt:15 在册、基线 16 键之一)与双实现对拍套件(帧相位等价执行化证据)——对拍夹具与产品码的申报差异由两处减为一处(收尾还原差异消失,仅余 stats 暴露成员)。
- T0「实跑定谳」义务已完成(上列三重证据),实现段零施工(停摆卡不提交)。
**建议案**(供写卡段/用户裁决):
1. 卡③ 退回写卡段重裁:剥离行为改动申报(对照表 V3 行删、§1 目标 2/3 措辞改「保持绿」)、T0 判读出口改「两面全绿=前提已由迭代 41 测试债批消化,直接按无行为改动形态开工」;保留裸键消除+双实现对拍义务(夹具=现行产品码 await 形态逐字副本——收尾在 run() 尾形态——与产品码唯一申报差异=夹具暴露 stats 供对拍)。
2. 若用户不愿重写卡:裁「卡③ 剥离出迭代 44」(roadmap 行拆批),裸键消除与对拍随下一迭代重编。
**待办清单条目状态回退拟稿**(落笔归上游段/用户,本批禁写根流程清单):「零豁免五键清偿批」条目的测速循环批子项状态由「文档设计已闭合+已写卡+已编迭代 44」回退为「文档设计已闭合+待写卡(现状考证已被迭代 41 测试债批消化——行为改动申报面失效,卡片重裁后重编)」。
## 6. 批末全量域闸与闸红修复(收口实况)
**闸况**:reshape 后第三次全量域闸(--code)全绿——00 冒烟/01 系统测试/03 场景探针/05 渲染闸/10 熵增闸绿;**02 单元与集成、09 静态扫描从基线红改善归绿**(基线记的迭代 41 时代存量红指纹已在册待换代——现真绿);回执 commit 对齐分支尖(b27db010)。
**闸红修复三笔(新红必修,均孤跑复绿)**:
1. S9 R1:tune_sim_controller `_sim_stats` 裸 Dictionary 注解改 `Dictionary[Variant, Variant]`(Variant 参数化现行口径)——本批新码写法违规。
2. ui_input_probe :482/:488 case⑮ 装配直构注入 `settlement.attunement` → `_facts.attunement`——卡② 成员迁移的探针消费漏网(T0 映射谱系只扫 main./root./self. 前缀,直构注入形态不在谱系);SCRIPT ERROR 中断 _ready 致探针挂死超时。
3. run_time_fatal_test :75 BoardInstaller 束键值 `oc.encounter` → `oc._facts.encounter`+meta_progression_forge_test 三处编排器 encounter 直读改指(:340/:749/:758)——同型漏网(负面套件名与触面无字面关联=关联档选测盲区;meta_forge 分片复现、单进程未现的夹具路径差异)。
**实现决定记录补(白名单外备案)**:跨卡触面(卡② 迁移成员被卡① 已改址的探针/未申报套件消费)只有全量闸能抓——关联档选测的盲区实证;全域终扫(encounter/attunement 直读形态)后同类漏网清零。
## 7. 独立验收审计结论与记录级缺漏补记
审计报告:reports/iter44-验收审计-2026-10-30.md——**结论=通过(可合并)**。21 行对照表逐行通过、判据绑定面自跑全绿、测试削弱零发现(37 条被删断言逐条有配对新行)、非行为改动成立、证据链在案。
记录级缺漏三项(不阻塞,随本节补记):
1. 卡③ 对拍串行驱动为白名单外决定——已在 §5.1 卡③ 实现决定记录 1 备案(审计方读的是早期版本);提交 04783fae 信息同载。
2. main_overlay_flow.gd:4 头注仍写已退役的 root._open_modal/_close_modal 转发方法名——词面遗留(卡面头注义务只列 main.gd 与 main_ui_build 两处);闸后代码面禁改(改则回执作废),留反哺点通道随文档批或下一代码批顺路改写。
3. 卡② grant_segment 事实族改指实测 18 处 vs 卡面「约 23」估计偏差——记录偏轻,本节补记(T0 点名清单实数为准)。
例外备案:V1d 扫描面为 presentation 平铺 27 文件口径(与卡面 glob 一致、只扩不缩;递归口径下批前既有 ui_kit.gd:819 即红——非本批引入)。
## 8. 停机状态与收口(进行中)
- 迭代总收口(冻结→并 master→reshape→批末全量域闸→独立验收审计→合并)**未启动**——按停机规则「迭代收口合并须等停摆卡闭合、或用户裁拆批后整体进行」。
- 卡① 卡② 全部收口提交在 worktree iter44 分支(9 笔提交),冻结待收口。
+99
View File
@@ -0,0 +1,99 @@
# 迭代 44 独立验收审计报告——2026-10-30
审计方:未参与实现的独立会话(只读审计+自跑最窄抽查)。对象=worktree D:\code\g-plan2-iter44 分支 iter44 尖 9f1dda40,对照基线 master 尖 ab3b4a9d;材料=三张需求卡片(组合根注册面批/战斗编排归约批/测速循环批 v1.1)+实现报告+闸回执与日志。本报告不修改任何代码与测试,缺漏如实记录不代修。
**结论行:通过(可合并)**——三卡 21 行对照表逐行核到落点、判据绑定面自跑全绿、测试削弱零发现(断言计数 22 文件零减少)、非行为改动判定成立、证据链在案;2 项记录级缺漏+1 处注释级陈旧指址+4 项例外备案,均不动行为、判据与验收闭合(见 §6),不构成合并阻塞。
## 1. 逐行核对——卡①(§8 十一行)
| # | 判定 | 凭据(文件:行/自跑) |
|---|---|---|
| 1 | 通过 | V1c 改址 `main.ui.LAYER_CONTRACT`/`main.ui.node`(system_test.gd:90/:94,尾缀〔装配生命批 V1〕保留);自跑 run_system_test 69 项 0 失败;LAYER_CONTRACT 与基线逐字一致(git show ab3b4a9d 对读,唯一差异=注释指址 `_apply_layer_contract`→`apply_layer_contract`,白名单 3) |
| 2 | 通过 | main.gd `^var` 计数 49(基线)→15(尖);自跑 run_smoke 15 项 0 失败+run_system_test 69 项 0 失败;gdstyle 零基线外新键 |
| 3 | 通过 | main_ui_build.gd `build()` 八步名序与基线逐字同;`_build_screens` 赋值序/add_child 序不变;42 条 connect 一一对应(仅 :97/:206 两处 closed bind 改 `flow.close.bind`=表 D 申报);smoke_boot get() 4 改 2 留(settings_store/run_scope 留);冒烟六断言+V2 五连比(system_test.gd:108-109)自跑绿 |
| 4 | 通过 | 探针面 diff 归一化对账 88 处非平凡行全部落表 A/C/D+实现决定 2/3/4;probe_gate 日志全绿(.gates/probe.log 06:53,全段);ui_tune_probe 审计自跑 PASS |
| 5 | 通过 | system_test 改址全对账(V1a/V1b/V1c/V2/V3 矩阵 `_face_snapshot` mf→`main.ui.modals.flow`、rp→`main.ui.screens.row_pacer`);V3a–V3i 存活性矩阵自跑全绿(69 项含) |
| 6 | 通过 | 七别名消费方改址逐处对上(pacing_queue 4 处、ui_struct_probe `flow.stack`、t17_defer_probe `flow._deferred`、ui_click_probe `settlement_stop_timer.is_armed()`);自跑 `--only=pacing_queue --only=modal_flow` 双绿;改后断言判据零变化(归一化对账) |
| 7 | 通过 | 六转发方法+_flush_row_queue 全库零残留(grep 只余 1 处陈旧头注,见 §6 缺漏 2);modal_flow 24 checks 绿+系统测试 V3g 段绿 |
| 8 | 通过 | source_guard_test.gd:69-92 重锚——尾缀〔注册面 V1〕;自跑输出「V1d 层序落位单口(presentation 27 文件——apply_layer_contract 之外 move_child 调用点=0)」;扫描面较改前(单文件)只扩不缩;口径例外见 §6 例外 1 |
| 9 | 通过 | theme_projection_test.gd:725-726 改断言新串 `ui.modals.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)`,尾缀〔注册面 V2〕;自跑 164 checks 绿 |
| 10 | 通过 | render_gate 日志全绿(.gates/render.log 06:54,shot_probe 七屏+golden);diff 零像素面改动(改址只动地址词) |
| 11 | 通过 | 审计独立 grep:49 名旧一级地址 `main./root./self.` 代码级残留=0(仅 3 处注释级命中);代码域闸日志六件全绿(§5);留档偏薄见 §6 例外 5 |
## 2. 逐行核对——卡②(§11 V0–V6)
| 行 | 判定 | 凭据 |
|---|---|---|
| V0 | 通过 | 关联套件全绿:审计自跑 8 套件(settle_segments/round_flow_skill_pool/roll_multiplier/treasure_effects/pacing_queue/modal_flow/source_guard/theme_projection)+批末闸 unit.log 分片全绿(15 suites shard 4/4);behavior_roll 随件迁移取号序逐字(§4 核对);五信号留宿主(battle_orchestrator.gd:12-16);两快照出口在宿主(:677/:739) |
| V1 | 通过 | settle_segments_test.gd:28-29 两断言〔编排归约 V1〕:`oc._rounds != null and oc._rounds.oc == oc`+`oc._facts != null`(check_quiet 静默通过=套件 18 checks 零失败);五段段件齐在 _rounds 由 :30-34 五条既有接线断言改指 `oc._rounds._X_seg` 承载(尾缀〔迭代27 T2〕保留,纯改址) |
| V2 | 通过 | round_flow_skill_pool_test.gd:112-115〔编排归约 V2〕:终局后 `presentation_snapshot()["refresh_used"]` == 末轮结算快照 `economy.refresh_used`(经 settlement_snapshot 末次发射承接);自跑实跑输出 5 实例(L1·D0×3+L2·D0×2)全 PASS;reset_round 只清三项(battle_facts.gd:32-35) |
| V3 | 通过 | roll_round_actions 与基线 `_roll_round_actions` 逐行对读一致(仅成员改指;`oc.rng.open_stream("behavior_roll")` 流名与开流时点不变,round_cycle.gd:102);settle_if_due 闸序 settling→battle_result→phase 逐字、终局早退不回置;trigger_cond_test 3 处改指(:119/:126/:131);自跑 round_flow 134 checks+settle_segments+trigger_cond 全绿 |
| V4 | 通过 | roll_multiplier_test.gd:45-59 四断言〔编排归约 V4〕:override 100→spawn hero_unit atk==100;缺省→hero 表值(check_quiet 静默通过);攻击单 base_value 贯通由既有 `_hero_attack_row_with`(:343,hero_atk_val=100 全链路停手结算行)承载——两消费点(spawn+base_value)闭合;combat_segment.gd:56 改调 `oc._hero_atk()`,宿主派生读 :69/:282 |
| V5 | 通过 | treasure_effects_test.gd:311-315〔编排归约 V5〕:`ob14.reward_grant().treasure_inventory == adr14.runner().treasure_inventory` 引用同一性;use_treasure 受理链由 ob8/ob11/ob13 既有段承载(:311 ob8 直写改指后绿);level_runner.gd:520 注入改经 `reward_grant()` 出口(`self.` 前缀=实现决定 3) |
| V6 | 通过 | seal_polarity(_perfect 直写→_facts 8 处对读)/feat_vouchers(契约 2 计数纯净段改指后全绿)/variant_placement/meta_forge/adventure_feats/table_structure_guard——批末闸 unit 分片全绿;三 mark 写口(grant_segment.gd:311/:379/:394 与基线直写同点同增量);attune 三外部消费面零改址(main.gd/treasure_interact_controller/table_structure_guard 均不在改址 diff) |
## 3. 逐行核对——卡③ v1.1(§12 V1–V3)
| 行 | 判定 | 凭据 |
|---|---|---|
| V1 | 通过 | tune_sim_batch_parity_test.gd 五面断言〔测速 V4/V5〕自跑绿(n=3 stats 六键逐键重算+报告逐字含四关键串;n=0 六键零计);run 可等待契约由 wd8 `await ctrl.run(2)`(standee_fx_sweep_test.gd:144,零改动)承载;全文件零循环(grep -c "for \|while " = 0)、代码 await 仅 :69 一处 `await _batch_finished` |
| V2 | 通过 | 五面对拍断言〔测速 V1–V3〕自跑绿:帧序列相对偏移逐位+相邻差恒一(每步恰一帧)、运行态序列逐位+收尾双假、进度串逐位;n=3+n=0 两案各 5 断言全 PASS;manifest 增行(run_all.gd:189);拓扑例外见 §6 缺漏 1 |
| V3 | 通过 | wd8 三断言〔wd8〕自跑 PASS(18 checks:`drv.seen == [true, true]`/收尾双运行态假/报告非空——standee_fx_sweep_test.gd 不在本批 diff=零改动);ui_tune_probe 审计自跑 PASS(⑤ 守卫环 1800 帧预算内退出+输出四串);收尾四语句迁入 `_finish_batch()`(:87-92)与基线 run() 尾次序逐句同(close→报告→置假→_running),`_run_one_level` 零触碰(不在 diff) |
| 静态 | 通过 | gdstyle_gate.py 审计自跑 exit 0:命中 7 全基线内、基线外新违规 0;基线 :15 `tune_sim await-in-loop` 裸键不命中=死键(消失键不参与红判定) |
## 4. 测试削弱检查(对照三卡申报全集)
- **断言计数对账**:22 个改动测试/探针文件 `Kit.check|check(|check_quiet` 计数基线→尖逐一比较——零文件减少;仅 5 增(roll_multiplier 36→40=V4 四断言、round_flow 100→101=V2、settle_segments 16→18=V1 两条、treasure_effects 58→59=V5、tune_sim_batch_parity 0→10=新套件)。与申报新增面(卡② V1/V2/V4/V5 四条+卡① V1d/V6 重锚+卡③对拍五断言)完全一致。
- **删除断言逐条对账**:全 diff 被删 check/断言行 37 条逐条找到配对新行——语义不变只换地址(含 smoke_boot 三 get、system_test V2 五连比、pacing 四断言、V1d/V6 两条);无任何断言删除后无配对。
- **阈值/扫描面**:零阈值放宽;扫描面唯一改动=V1d 单文件→27 文件(申报为强化,成立);static_scan.sh S8/S7 两清单增行 round_cycle.gd(卡② T2 申报;battle_facts.gd 未增行=卡片明示「零协作调用不入清单」,成立)。
- **换下层直调**:零发现——探针仍走真实按钮/输入路径(pressed.emit/push_input 等既有形态未动)。
- **wd8 三断言**:文件不在 diff=零改动,自跑保持绿。**ui_tune_probe 巡检白名单**(实现决定 3):`host == main and m == "_on_modal_closed"` → `host == flow_host and m == "close"`(ui_tune_probe.gd:214-216)——属卡①表 D「closed bind 目标改 flow.close.bind」的机检联动,同一连接新宿主登记;conn_count=11 断言与消息逐字不变(:223-224 与基线 :221-222 对读);判据面「closed 外发消费目标=模态编排口」语义保持。**判定:不属削弱**。
- 卡② §15 零申报、卡③ §16 零申报——兑现成立(超出申报的删改零处)。
## 5. 非行为改动判定与证据链
**卡①**:装配序(八步名序、add_child 序、赋值序)逐字不变;42 条连接语句一一对应;LAYER_CONTRACT 19 项逐字迁驻;move_child 单口由 V1d 27 文件扫描绿+grep 复证(全 presentation 仅 ui_registry :84/:98 两处调用,views/ui_kit.gd:819 为批前既有、不在 diff);main._ready 仅插入 `ui = UiRegistry.new()` 一行于 _build_ui 之前;`_build_modal_flow` 注入语句序与基线逐字同(flow 实例改随 UiModals._init 构造=实现决定 1,注入时点不变)。
**卡②**:成员迁移与改指——roll_round_actions/settle_if_due 逐字迁移对读成立;grant_segment 三 mark=同点同增量;encounter/attunement 词替换的三处字符串键保护(实现决定 5)grep 复证(behavior_roll 前置流名 "encounter" 字面在位);treasure_inventory 注入时序(宿主构造后 :520)不变。
**卡③**:收尾四语句迁移=同帧同相位(对拍 V2 运行态序列+V5 报告逐字承载,自跑绿);`_run_one_level` 零触碰。
**证据链**:
1. 卡③ T0 三重证据在实现报告 §5 在案:215da315 提交信息明载「tune_sim 运行态收尾归还 run」(审计直读提交信息复证)+两面实跑读数;审计自跑两面(wd8 套件+ui_tune_probe)复证现行全绿——前提消化链条完整。
2. 卡③ v1.1 重裁凭据:卡 v1.1 状态行+§15 裁决记录(2026-10-30 停卡重裁三点)在案。
3. 卡①卡②实现决定记录(报告 §3 共 5+5 条)对照卡① §10 七条、卡② §17 五条白名单——白名单外事项(shot/fx_shot 纳入、layout_probe get 键、curio/confirm 漏列补改、flow 随容器构造、grant 第五处读、treasure_effects 五处直构、self 前缀、占位符保护)均有备案,无遗漏。
4. 闸凭据:receipt.json 现为 docs 域回执(9f1dda40 提交时 pre-commit 钩子生成,b27db010 时代,全绿);代码域末跑凭据=.gates 六件日志(smoke 15/0、system_test 69/0、static_scan ok、probe_gate 全绿、render_gate 全绿、unit 分片全绿),时间戳 06:49–06:54 恰落 b27db010(06:49:41 提交)与 9f1dda40(06:57:19 提交)之间——绑定终态代码(9f1dda40 只含报告文件)。
5. 红线:diff 只触 `game/`+`reports/` 两目录;零 `randomize()`/自建 RandomNumberGenerator;零触及 `design/`、`game/tests/README.md`、两份根流程清单。
## 6. 缺漏与例外(如实记录,不修)
**缺漏(记录级,不动判据)**:
1. **卡③实现决定记录缺位**:对拍驱动由卡 T2 的「先启动两形态、后分别 await」并行拓扑改为**串行驱动+相对帧偏移归一**(tune_sim_batch_parity_test.gd:145-162)——卡 §14 白名单六条不覆盖,应在实现报告「实现决定记录」节备案;实际只在提交 04783fae 信息中有案(理由=GDScript 协程动态调用强制 await、无并行启动形态+_init 时点 root 未挂树实测)。报告 §3 无卡③条目。实质判据未损(相对偏移+相邻差恒一仍承载「每步恰一帧+相位同点」,审计自跑绿)。
2. **main_overlay_flow.gd:4 头注陈旧指址**:注释仍写「全部开合走组合根模态单口(root._open_modal/_close_modal)」,两转发方法已退役、实际调用全改 `root.ui.modals.flow.open/close`——「指址须真」词面违例,行为无涉,批内漏改。
3. **卡②改指处数偏差记录偏轻**:grant_segment 实改 18 处与卡片「约 23」的 L2 偏差,实况数在报告 §2 V6 行在案但未入「实现决定记录」节(卡 §14 L2 要求记入该节)。
**例外(依据在案,不判红)**:
1. **V1d 扫描面口径**:实现为 DirAccess 平铺(res://presentation 顶层 27 个 .gd),不含 views/、scenes/ 子目录——与卡片字面 glob(`game/presentation/*.gd`)一致、较改前单文件只扩不缩;但「全 presentation 目录零第二落位点」按递归读法不成立:批前既有 `views/ui_kit.gd:819` 有一处 `body.move_child(...)`(视图内行排序、非层序落位;ui_kit.gd 不在本批 diff、归迭代 42 尾波清偿批在册债)。ui_registry.gd:3 头注「全仓唯一 move_child 点」沿袭旧 main.gd 注释口径、与该既有事实并存。若日后递归化扫描面,须先处置该行。
2. **回执被覆盖**:receipt.json 为 docs 域回执(报告提交钩子生成);代码域末跑以六件日志为凭(§5 第 4 条),与报告 §6「第三次全量域闸全绿、02/05/09 从基线红改善归绿」叙述吻合。
3. **`_finished` 成员写而不读仍保留**(tune_sim_controller.gd:26/:64/:88)——卡 §14 白名单 3 明示「零读者确认后可删」,保留属白名单内两可选择,键账只升 1(11/15 在阈内)。
4. **报告 row4 计数口径差**:「约 460 行机械改写」(报告 §1)与 784d581b「422 行」——统计口径差,不影响判据。
## 7. 审计自跑记录(独立复证清单)
| 项 | 结果 |
|---|---|
| `python tools/run_smoke.py` | PASS——15 项 0 失败 |
| `python tools/run_system_test.py` | PASS——69 项 0 失败(V1a/V1c/V2/V3a–V3i 全过) |
| `--only=source_guard --only=theme_projection` | 绿——V1d「presentation 27 文件…调用点=0」〔注册面 V1〕+V6 新串〔注册面 V2〕+theme 164 checks |
| `--only=settle_segments --only=round_flow_skill_pool --only=roll_multiplier --only=treasure_effects` | 4 套件绿——V1 两断言、V2 五实例、V4 四断言、V5 一断言(check_quiet 静默通过=零失败) |
| `--only=pacing_queue --only=modal_flow` | 双绿 |
| `--only=tune_sim_batch_parity --only=standee_fx_sweep` | 双绿——对拍 10 断言〔测速 V1–V5〕+wd8 三断言 |
| `res://probe/ui_tune_probe.tscn`(headless) | PASS——⑤ 守卫环预算内退出+输出四串+全链路通过 |
| `python tools/gdstyle_gate.py` | exit 0——命中 7 全基线内、零新违规;tune_sim 裸键不命中 |
| grep:tune_sim 循环/await | 循环 0;代码 await 仅 run() :69 一处 |
| grep:49 名旧一级地址残留 | 代码级 0(仅注释级 3 处) |
| 成员数 | main.gd 49→15;battle_orchestrator 37→15;容器 3/9/5/14 |
| 断言计数对账 | 22 文件零减少、5 增(+4/+1/+2/+1/+10) |
| diff 归一化对账 | 88 处非平凡行对全部落表 A/C/D+已记录决定 |
## 8. 总结论
**通过(可合并)。** 三卡对照表 21 行全部核到落点且判据绑定面当轮实跑成立;测试削弱零发现;三卡「非行为改动」判定在代码级对读与实跑两面成立;证据链(卡③ T0 三重证据、v1.1 重裁、卡①卡②决定记录、闸日志)在案。缺漏三项(卡③决定记录缺位、main_overlay_flow.gd:4 陈旧头注、卡②计数记录偏轻)与例外四项均属记录/注释级,不触行为零变化、判据绑定与验收闭合;建议随批末收口由实现侧补记(补记动作归收口段,不由本审计代作)。gdstyle 基线 refresh(--force)仍属用户人工遗留义务(反哺点 7 在案)。
-25
View File
@@ -1,25 +0,0 @@
# 迭代 46 GDD 反哺点清单(2026-10-31)
> 接收方:待办清单「反哺点专条」通道——用户会话逐条裁决后由文档批写入 `design/`,写入后本清单条目清掉(吸收即死)。
> 依据:迭代 46 实现报告(同目录)与实测评读数。实现批禁写 `design/`,以下为待裁决落文面。
## 卡A 换图系双基座七件批
1. **[奇物.md]** 基础件表(或专长实装状态句):七件(矿脉储能/省力周转/崩壁冲击/蓄力周转/石层缓冲/坑道小憩/矿道红利)由设计声明改为**已实装**(feats.tres 37 行、稀有度 14/13/10/0、cap 均 1);「双基座叠加、后续效果本轮五段结算内兑付」已在案零改述。依据=实现报告 §二 V2–V11。
2. **[行动经济.md]** 两件基座由「待实现」改为在册:付费挖格 +1 RAP(受理后一次性入池、拒绝轮零入池)、刷新成本折价 50%(floor 取整、can 与执行同源、只改门槛与扣费不改拒绝判据)。依据=V2/V3。
3. **[战斗结算.md]** 全局 `damage_taken` 落码状态补记:敌方攻击/地形/危险格/错误封印资源直伤四路径统一经唯一发射口与唯一消费族;**地形路径消费面自三件扩为五件(+shield)——铁壁自此对地形伤害生效;危险格/错误封印直伤路径自此纳入 damage_taken 订阅面**(用户已裁全局语义,实现报告 §六);成功刷新事实 `board_refreshed` 为七件订阅主挂点(装新板后恰一次发射)。依据=V5/V4。
4. **[地图战斗资源.md]** 清图奖励语义确认句:矿道红利不绑清图奖励(每次成功刷新固定 +1 GP 走战斗内缓冲,不读旧图探索进度、不触发清图事件;刷新图零清图奖励守卫不变)——若现行正文已含此意则零改(本行核对性反哺)。依据=V10/V12。
5. **[architecture.md]** 「模块归属」增补/现状化三行:①**BattleRefreshFlow**(新 B1 域拆件——刷新可用性门/折价扣费/换图执行/成功事实登记,调用路径呈现层→编排器单行转发→流程件→行动经济/装板壳/授予段;static_scan S7_B1_SET 已随批增补该文件,判据口径不变);②**damage_controller** 行现状化(damage_taken 唯一发射口 `_emit_hero_damage_taken`+唯一消费族 `_apply_hero_damage_effects`(原名 _enemy_shield_apply 改名)+board_flat_hero 结账前统一发射);③**action_economy** 行现状化(dig_paid 命中施加抽私有 `_apply_dig_paid_hits` 单点;request_refresh 收折后价入参)+**grant_segment** 行现状化(apply_board_refreshed 成功事实消费+round_end heal value_counter 兑付面)。依据=实现报告 §五/§八.6。
## 卡B 站桩名字行提档与地台色合并批
6. **[art-bible.md §4.1]** 换色合并登记①②句的代码指称换代:①「代码 `STAND_STONE_RIM`」②「代码 `STAND_STONE_EDGE`」两源常量已删除(全仓零引用——唯一残留=退役断言测试串,预期例外);②合并目标 `#4C4038` 补代码名 **`PLAZA_EDGE`**(ui_kit 唯一落点)。依据=卡B 对照表 3/4 行+grep 取证(实现报告 §三)。
7. **[art-bible.md §8.2]** 高墙瓦片体系条口沿高光行:「Color8(196,172,132)」值随换值换代为 **`#C9AF83`(201,175,131)**(并档掩尘枚 BRICK 单点);程序化回退顶缘线与口沿高光两绘制点已改指。依据=对照表 3 行。
## 卡C 棋盘格边长口径收口批(停摆——随裁决落文)
8. **[battle.md §棋盘格尺寸公式]** 算例表(:148-152)与实机偏差实测落点:1280 档实板可用面积 **(1022, 454)**(算例假设 486 可用高)→ 实测 L1 格边 40px、13×13 推定 30px(恰达标)、**14×14 推定 28px(低于可读下限 30)**;1080 档 (1662, 734) → L1=46、13/14 推定=46(钳制顶格)。落文方向随「30px 下限适用域」裁决(读法甲/乙见停机呈报 §三):读法乙需补「下限按视口档分级」条+算例表填实测值或标注「按旧布局口径」。依据=停机呈报 §一/§四。
## 术语表
9. 零新增术语——本批新名(BattleRefreshFlow/_cell_for/PLAZA_EDGE/counter_id 三枚)均属代码标识符与数据行键,不入术语表(登记口径=概念规范名)。
-109
View File
@@ -1,109 +0,0 @@
# 迭代 46 实现报告(2026-10-31)
> 批次:迭代 46 三卡——卡A 换图系双基座七件批 · 卡B 站桩名字行提档与地台色合并批 · 卡C 棋盘格边长口径收口批。
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,自 b5b2c555 检出)。
> **批内状态总览:卡A 收口 · 卡B 收口 · 卡C 停摆(L3 #2 预设停机点,呈报见主区 `reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`)**。合并阻塞等裁决。
## 一、各卡状态
| 卡 | 状态 | 出口证据 |
|---|---|---|
| 卡A 换图系双基座七件批 | **收口**(T0–T8 全落) | `tools/run_all_gates.sh --code` 7 闸全绿(回执 commit 45b53d68 时点);此后 S9/S7 修正与卡B 提交待迭代级复跑 |
| 卡B 站桩名字行提档与地台色合并批 | **收口**(T0–T3 全落) | standee_present_values_test 26 checks 绿;golden 板面三张 0.00% 留痕;gdstyle 零新增键 |
| 卡C 棋盘格边长口径收口批 | **停摆**(T0 完成;T1–T3 改动已验未提交;T4 未走) | 卡C §9 L3 #2:实板可用面积代入 14×14 得 28px < 30px(1280 档)——停卡呈报等裁决 |
## 二、卡A 实际对照表(改动—验收)
| 需求点 | 改动(文件:行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 结果 |
|---|---|---|---|---|---|---|
| V1 词表扩签 | effect_engine.gd EVENTS 20→22/MINE_EVENTS 同步换图刷新面/EFFECT_KINDS 32→34;forge_table/effect_words 计数换代 | `effect_words_t1_test.gd:_init`(新增①-a 两断言);`forge_table_test.gd:_test_effect_kinds_closed` | 单元 | validate 与整表加载 | EVENTS=22、EFFECT_KINDS=34、出域仍拒(旧负例全保留) | 绿(20/41 checks) |
| V2 矿脉储能 | action_economy.gd dig_paid 命中环累计 grant_reserve_ap(扣费受理后一次性入池·拒绝轮零入池);feats.tres vein_reserve 行 | `refresh_feats_test.gd:test_reserve_on_accepted_dig` | 接口 | `settle_batch(dig)`(AutoPlayer 真实挖掘) | RAP 恰+1(8→9)、AP 按表扣(8→7)、耗尽轮增量==受理挖数 | 绿 |
| V3 省力周转 | BattleRefreshFlow `_effective_refresh_cost`(声明序第一命中 floor(base×mult/100),can 与执行同源);request_refresh 收折后价入参;easy_swap 行 | `test_refresh_discount_gate_and_charge`;`test_double_base_stack` | 接口 | `player_request_refresh()` | base 8→扣 4;两次折后扣尽 8→0;门槛拒零受理;双基座同场生效 | 绿 |
| V4 成功事实 | battle_refresh_flow execute 成功序(折价扣RAP→used+1→旧资源退役→旧板退役→装新板→apply_board_refreshed);编排器两公开方法单行转发 | `test_board_refresh_fact_only_on_success` | 集成 | `player_request_refresh()` | 非 Active/每轮上限/RAP 不足三拒面零事实零换图(probe_br_counter 计数+board_changed 双观测);成功恰一次 | 绿 |
| V5 全局受伤 | damage_controller `_emit_hero_damage_taken` 唯一发射口+`_apply_hero_damage_effects` 唯一消费族(改名自 _enemy_shield_apply,组合次序冻结);敌方攻击/棋盘直伤/地形三路径接入;grant_segment 删本地 _terrain_damage_reduce | `test_all_hero_damage_paths_emit_once`;`test_no_duplicate_damage_taken_event` | 集成 | 敌方攻击、dig danger、seal wrong、terrain 四真实入口 | 每路径承伤单据在案且计数器==行数(逐单恰一次);context 标记闸防地形二次发射 | 绿 |
| V6 石层缓冲 | round_swap_guard 计数+damage_taken when≥1 damage_reduce 2(round 桶轮始自清) | `test_stone_mitigation_all_damage` | 集成 | 刷新→危险格(pierce 8)/地形两真实受伤路径 | pierce 8→6(双刷不叠加)、未刷新轮不减(8)、地形 max(下限, effect_value−2) 逐单闭合 | 绿 |
| V7 崩壁冲击 | apply_board_refreshed open_ticket blast→reward_grant.defer_ticket(案C 同队列);SOURCE_EXEMPT_CLASSES+fx_board_refresh;wall_blast 行 | `test_blast_defers_to_combat_segment` | 集成 | 刷新→停手 | 刷新时点零伤害行;战斗段末恰一单 ×2 全体存活敌 | 绿 |
| V8 蓄力周转 | swap_charge_layers 战斗桶+attack_bonus value_counter+set0(既有消费面零新码) | `test_charge_layers_consumed_same_round` | 接口 | 两次刷新→停手(主角先手攻击) | 双刷 2 层→攻击后 0;白盒预置 3 层时刷新被闸(上限 3);战斗新建 0 | 绿 |
| V9 坑道小憩 | round_swap_rest+round_end heal value_counter(grant_segment 新增 `_round_end_heal_value_counter`——先于 set0 行消费层值) | `test_rest_rotation_cap_and_clear` | 集成 | 刷新→停手(轮末授予段) | 回复 min(3,缺血)==3、层清 0、满血发未生效行、自然累计 2 路径 | 绿 |
| V10 矿道红利 | board_refreshed grant_gp→escrow+fx_grant_gp 行(apply_board_refreshed 消费);mine_profit 行 | `test_mine_profit_gp` | 集成 | `player_request_refresh()` | escrow 每成功+1(0→1→2)、fx_grant_gp 行在案、拒绝零变化 | 绿 |
| V11 数据与池 | feats.tres 七行(rarity 14/13/10/0·cap 1);adventure_feats 30→37/牌库 132→139 | `test_seven_feats_validate_and_pool`;adventure_feats `_smoke_feats` | 单元 | ConfigDB 整表与冒险牌库 | rows 37、relic 37、14/13/10、deck 139、validate 空、禁词 0、七件计数器引用闭合 | 绿(85/138 checks) |
| V12 无专长回归 | round_flow/seal 旧基线零改动(刷新体迁移编排器单行转发) | `_smoke_active_refresh`;`_smoke_refresh_provenance_guard`;`test_no_feats_baseline` | 单元+集成 | 现行刷新/清图入口 | RAP8、AP 保留、上限 2、刷新图零清图、挖格零产 RAP、表基线扣费 | 绿(134/70 checks) |
**窄测实跑记录**:refresh_feats 85 · adventure_feats 138 · round_flow 134 · seal_polarity 70 · damage_controller 44 · effect_words 20 · forge_table 41 · config_db 29——全绿。批末 `--code` 闸 00/01/02/03/05/09/10 全绿(回执 45b53d68)。
## 三、卡B 实际对照表(改动—验收)
| # | 需求点 | 改动 | 验收测试 | 结果 |
|---|---|---|---|---|
| 1 | 名字行字号提档 FS_HINT 14 | standee_body.gd 五取值点+name_line_probe 增 name_font_size | 〔名字行 V10〕探针实读==FS_HINT+两绘制点源码锚+FS_CAPTION 计数锚==1 | 绿 |
| 2 | 名字行几何随档联动 | _name_baseline 偏移 17→18+注释换代 | V6 换代读数 150±0.51(8px 档)且 < plinth_top_y | 绿 |
| 3 | 受光线常量退役改指 BRICK | ui_kit 删 STAND_STONE_RIM;standee_body:386+cell_base_layer:176 改指 | 〔名字行 V11〕常量表零键+BRICK==#C9AF83+两绘制点源码锚;grep 反证(仅剩测试断言串——预期例外) | 绿 |
| 4 | 描边常量退役改指新 token | ui_kit 删 STAND_STONE_EDGE+增 PLAZA_EDGE=Color8(76,64,56);standee_body:385 改指 | 〔名字行 V12〕PLAZA_EDGE 逐位+常量表零旧键+描边源码锚 | 绿 |
| 5 | 板面 golden 与换值一致 | (预期重锚——实际无需) | render_gate 板面三张 0.00%(改后跑,基线未动) | 绿(见实现决定 #4) |
| 6 | 批末收口 | 全批 | 迭代级 --code 闸(待卡C 裁决后统一走) | 待迭代出口 |
卡B 改前读数(T0):standee_present_values_test 18 checks 绿 · gdstyle 5 键全基线内 · 渲染闸绿(--code 回执 45b53d68 内 05 闸)· 字体度量 ts(14px)=(14,20)。
## 四、卡C 停摆实况(改动已验未提交)
- T0 完成:行号重锚(board_view set_cell_area 现 :206-224、layout_probe MIN_CELL :15、冒烟 :53-69);取档对账 resolve_row(1,0)/(2,0)/(3,0)/(3,9)=10/12/13/14 全符;改前读数 board_gen_reward_test 62 绿+layout_probe 两档绿+gdstyle board_view max-public-methods 27 持平。
- T1 算式单点:`BoardView._cell_for(n, avail)` 提取(逐位同式)+set_cell_area 改调——等价性由 board_gen_reward_test 既有断言承载(62 checks 绿)+gdstyle 持平。
- T2 探针:MIN_CELL 36→30+注释改指 battle.md+BoardViewScript preload+零尺寸前置+13/14 推定断言——**推定断言触发 L3 #2(14×14=28<30)**。
- T3 冒烟四档:取样表 [{1,0,10},{2,0,10→12},{3,0,13},{3,9,14}]+板型一致断言+算式单点等价断言——74 checks 绿(单元面全过:钳制 [26,46] 面无越界)。
- 三件改动**未提交**(停摆卡不提交),随裁决处置(可复用——行为零变化)。
- 停机呈报:主区 `reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`(分歧读法/建议案/待办回退拟稿)。
## 五、结构预算(S13 前后)
| 触面 | 前测 | 后测 | 判定 |
|---|---|---|---|
| battle_orchestrator.gd | 811 行/类变量 15(卡A 写 38 系笔误) | 803 行(−8 达标)/类变量 15 | 只降不升 ✓ |
| reward_grant.gd | 类变量 15 | 15(defer_ticket 为方法零增员) | ✓ |
| grant_segment.gd | 类变量 3/公开方法 1 | 3/2(apply_board_refreshed);私有新增 1(_round_end_heal_value_counter)≤4 | ✓ |
| damage_controller.gd | 公开方法 9 | 9(私有净增 1:_emit_hero_damage_taken)≤2 | ✓ |
| action_economy.gd | 公开方法 15 | 15(命中环抽私有 _apply_dig_paid_hits——S13 分支超限重构,分支本体逐字迁入) | ✓ |
| battle_refresh_flow.gd(新件) | — | 公开函数 2(can_refresh/execute)/类变量 0 | ≤20/≤15 ✓ |
| gdstyle_gate | 5 键全基线内 | 5 键全基线内零新增零恶化 | ✓ |
## 六、旧 damage_taken 专长覆盖面变化
全局统一后,订阅 `damage_taken` 的既有专长(背水 hp_ratio when/石肤 ac_bonus+counter_add/保险丝 counter_add)与换图系石层缓冲的覆盖面变化:
- **敌方攻击路径**:不变(原即经 _enemy_shield_apply 全组合消费)。
- **地形路径**:消费族从本地三件(damage_reduce/ac_bonus/counter_add)扩为全局五件(+shield)——**铁壁(shield)自此对地形伤害生效**(用户已裁全局语义;卡A §十二削弱申报第 2 条在案)。
- **危险格/错误封印资源直伤路径(board_flat_hero)**:原零事件,现统一发射——上述专长自此对这些路径生效(同上申报面)。
- 无专长基线逐位零漂移(V12 全绿;同一伤害恰一次由 V5 钉死)。
## 七、实机目检总清单(登记不阻塞)
1. 〔卡A〕玩家持基座与收益件后,是否主动规划刷新而非只在坏图刷新(行为观感)。
2. 〔卡B〕站桩名字行 14px 清晰可读且长名不截字(验收底线句)。
3. 〔卡B〕墙格口沿亮线换值后观感无异(#C4AC84→#C9AF83)。
4. 〔卡B〕贴图缺位回退描边与顶缘线观感无异(PLAZA_EDGE/BRICK 程序化回退)。
5. 〔卡C·停摆批登记〕第三关 14×14 板格面数字在基座档的可辨读性(28px 实况——随裁决)。
## 八、实现决定记录(超白名单/偏差备案)
1. **卡A 自洽扫描笔误三处(不改合同)**:「类变量 38」实为 15;gdstyle_baseline.txt:9 引用实无 battle_orchestrator 键(该文件现无 S13 键);稀有度断言实挂 :476 非 :465。T0 按实数重锚记录,合同(只降不升/换代判据)不受影响。
2. **卡A T0 工具名**:卡写 `bash tools/gdstyle.sh game`——该文件不存在,实际工具 `python tools/gdstyle_gate.py`(与卡B/C 一致),按实跑。
3. **MINE_EVENTS 同步口径**:卡A T1「同步 :19 的 MINE_EVENTS」取「两新事件入集」读法——弃图刷新属扫雷侧动作面,流派铁律订阅集随之认列(零机械消费面差异,mine_covered 生产无强制点)。
4. **卡B golden 预期红未现**:卡预期「板面三张红→重锚」,实测三张 0.00%——口沿高光换值 #C4AC84→#C9AF83 每通道差 ≤5,低于逐像素容差(≤16 差/≤2%),golden 判定零变化;重锚步骤无需执行(基线未动、零差留痕达成 V5 判据;禁借重锚夹带红线零触碰)。
5. **S9 修正**:四处显式非参数化容器注解改 `:=` 推断(R1)——零行为改动。
6. **S7 域集增补**:`static_scan.sh` S7_B1_SET 增补 battle_refresh_flow.gd——换图刷新流程件=卡A C13 拆分边界的 B1 域拆件(承载 retire_mapping/装板编排调用,卡A §八明列新结算族件);判据口径不变(B2/B3/B4 对外 API 仍限 B1 域调用)。**architecture.md「模块归属」需补 BattleRefreshFlow 行——已入反哺点清单**。
7. **V11 引用闭合判据域**:全表扫描发现预存行完美保险 when 读 insurance_used 无 counter_add 写入方(消费点落账合法形态——feats.tres 注记在案)——判据域收窄为「换图系七件自身引用闭合」,预存形态不入判据。
8. **apply_board_refreshed 访问形态**:BattleRefreshFlow 经 `oc._rounds._grant_seg` 直调(round_cycle 不在卡A §八改动文件清单内,不加转发壳——沿结算族直调惯例)。
9. **卡A T8「代拍板追认单」不另立**:iter 技能与 process-conventions §2 定「超白名单记实现报告实现决定记录,不另立追认单」——本批全部决定在上列备案,白名单内决定零追认面。
10. **测试断言口径修正(开发期自纠)**:V5②③④ 聚合判据从「两来源行合计」修正为「全 hero 行数」(盲挖途经的其他直伤单据一并入账);V2 辅证改「RAP 增量==受理挖数」(移位耗 AP 不入账);V4 时序修正(停手再入 RAP)——均为测试侧修正,生产码零回退。
11. **V9 上限 3 承载形态**:每轮刷新上限 2(表值)使自然路径层值至多 2——「上限 3」判据由白盒计数器预置+真实刷新入口承载(test-conventions §5.4 计数器直改属数据面注入),自然累计 2 另设战场钉死。
## 九、独立验收审计结论
- **审计方**:未参与实现的子代理(独立会话上下文),2026-10-31 执行。
- **报告**:`reports/iter46-验收审计-2026-10-31.md`(本目录)。
- **结论**:**审计通过(零缺漏)**——卡A V1–V12 与卡B 6 行对照表逐行通过;尾缀全可 grep;真实入口零绕过(V2 真挖/V3·V4·V10 经 player_request_refresh/V5 四路径真实战斗驱动);自跑复核 refresh_feats 85/standee 26/adventure_feats 138/round_flow 134/seal_polarity 70/effect_words 20/forge_table 41 全绿+gdstyle 5 键零新增+render_gate 三张 0.00% 实跑;削弱核对全部落在两卡申报面内(round_flow/seal 零进 diff,禁删面原样在案);无变异/撤销类取证。
- **审计附加发现处置**:①两枚新件 .uid 边车未随批提交——已补提交(.gitignore 要求);②static_scan S8_FILES 与 S7_B1_SET 未同步增补新件——已同步(新件零 await,扫描面实跑绿);③V10 断言消息在求值前格式化的显示瑕疵(判据本体正确,零处置);④V9 私有读前置守卫附注(审计方判例外不阻塞——判据主面走公共 step_row 信号)。
## 十、遗留读数义务
- 卡C 裁决后:探针两档 bv.size/L1 格边/13·14 推定读数已录本报告第四节与停机呈报——重写卡可直接引用,无需重采。
- 无 MC/数值验证义务(本批无数值改动,feats.tres 七行为卡内 provisional 值——归调参批)。
-130
View File
@@ -1,130 +0,0 @@
# 迭代 46 独立验收审计报告
> 审计方:独立验收审计子代理(未参与本批实现——迭代 loop 内启动,符合 test-conventions §8.7 两入口铁规)。
> 日期:2026-10-31。
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,基点 b5b2c555)。Godot:`Godot_v4.7.2-stable_win64_console.exe`。
> 审计范围:合并候选=卡A(换图系双基座七件批)+卡B(站桩名字行提档与地台色合并批)已提交 diff=`git diff b5b2c555..c10bbc11`(10 笔提交=9 实现+1 收尾文档;20 文件,+1231/−96)。卡C(棋盘格边长)停摆不计。
> 审计方式:不采信实现报告「已全绿」自述——逐行核对对照表两端、自跑最窄套件与闸件、对照 diff 逐处核对削弱申报。本审计只写本报告,零源码/测试/数据文件改动。
## 一、审计自跑记录(当轮真实执行)
| 套件/闸 | 结果 | 与报告申报对照 |
|---|---|---|
| refresh_feats_test | **all green (85 checks)**,exit 0 | 与报告§二 申报 85 一致 |
| standee_present_values_test | **all green (26 checks)**,exit 0 | 与报告§三 申报 26 一致 |
| adventure_feats_test | **all green (138 checks)**,exit 0 | 与报告§二 申报 138 一致 |
| round_flow_skill_pool_test | **all green (134 checks)**,exit 0 | 与报告§二 申报 134 一致 |
| seal_polarity_test(附加) | all green (70 checks),exit 0 | 与报告§二 申报 70 一致 |
| effect_words_t1_test(附加,V1) | all green (20 checks),exit 0 | 与报告§二 申报 20 一致 |
| forge_table_test(附加,V1) | all green (41 checks),exit 0 | 与报告§二 申报 41 一致 |
| gdstyle_gate | 5 键全基线内、基线外新违规 0、恶化 0 | 与报告§五「零新增零恶化」一致 |
| static_scan.sh | exit 0——S7 ok(B2/B3/B4 符号收敛 B1 文件集)/S8 ok/S13 ok | S7 新域集生效见§六① |
| render_gate.sh(窗口化实跑) | 板面三张+menu_boot 全 **changed=0.00%**,全绿 | 与报告§八.4 一致,见§三行 5 |
## 二、卡A 对照表逐行核对(V1–V12)
| 需求点 | 测试存在 | 尾缀可 grep | 真实入口无绕过 | 判定 |
|---|---|---|---|---|
| V1 词表扩签 | `effect_words_t1_test.gd:_init` 新增①-a 两断言+`forge_table_test.gd:_test_effect_kinds_closed` 22/34 换代 | 〔换图系批 V1〕×2+〔锻造工坊批 V11〕×2 | ConfigDB 整表加载+词表常量直读 | **通过**(实跑 20/41 checks 绿;diff 核对 effect_words 纯增量 5 行,旧出域负例零删) |
| V2 矿脉储能 | `test_reserve_on_accepted_dig` | 〔换图系批 V2〕×3 | 经 `_auto_dig`→`settle_batch([move]+[dig])` 真实付费挖格;辅证 `_drain_round_rap_accounting` 仍走 settle_batch 逐挖计数 | **通过**(RAP 8→9、AP 按表扣、耗尽轮增量==受理挖数三面全钉) |
| V3 省力周转 | `test_refresh_discount_gate_and_charge`+`test_double_base_stack` | 〔换图系批 V3〕×6 | 经 `orch.player_request_refresh()` 唯一入口;can 与执行同源(`_effective_refresh_cost` 单点,diff 核实编排器两公开方法单行转发) | **通过**(折后 −4×2、门槛拒 can=false 且执行拒、双基座同场生效) |
| V4 成功事实 | `test_board_refresh_fact_only_on_success` | 〔换图系批 V4〕×6 | 经 `player_request_refresh()`;非 Active 拒=decay 行内同步探测(round_flow_skill_pool_test:161 先例同款),上限拒/RAP 不足拒均在 Active 相位真入口 | **通过**(三拒面 probe_br_counter==0+board_changed 计数冻结双观测;成功恰一次×2+新轮×2) |
| V5 全局受伤 | `test_all_hero_damage_paths_emit_once`+`test_no_duplicate_damage_taken_event` | 〔换图系批 V5〕×8 | **四路径全真实战斗驱动、零直调 `_emit`**:①敌方攻击=boss 战零挖掘连停三轮(enemy_action 行在案);②地形=AutoPlayer 盲挖 flat_damage 变异格+stop;③危险格=真挖直接伤型雷格;④错误封印=真 `seal_place`+stop | **通过**(每路径「承伤行数==计数器」恰一次+无重复消费两样本;实跑读数 行2=计2/行9=计9/行2=计2/行2=计2) |
| V6 石层缓冲 | `test_stone_mitigation_all_damage` | 〔换图系批 V6〕×3 | 刷新经 `player_request_refresh`;危险格/地形经真挖+stop 结算 | **通过**(pierce 8→6 双刷不叠=叠加态 4 反证钉死;未刷轮不减=round 桶轮始清空;地形 max(下限, effect_value−2) 逐单闭合) |
| V7 崩壁冲击 | `test_blast_defers_to_combat_segment` | 〔换图系批 V7〕×2 | 刷新+停手(轮末授予段→战斗段末) | **通过**(刷新时点零伤害行=授予段不结账;停手后恰一单 value 2 全体存活敌) |
| V8 蓄力周转 | `test_charge_layers_consumed_same_round` | 〔换图系批 V8〕×2 | 双刷+停手(主角先手攻击)真实消费 | **通过**(白盒预置 3 层上限闸=实现决定 #11 申报面;双刷 2 层攻击后清 0;战斗新建 0) |
| V9 坑道小憩 | `test_rest_rotation_cap_and_clear` | 〔换图系批 V9〕×3 | 刷新经真入口;轮末恢复在授予段(stop 触发) | **通过(附未注例外,见§五)**(回复 min(3,缺血)==3 且层清 0;满血未生效行+零 heal 行;自然累计 2 路径) |
| V10 矿道红利 | `test_mine_profit_gp` | 〔换图系批 V10〕×4 | 经 `player_request_refresh()`;拒绝=RAP 不足真拒 | **通过**(escrow 每次+1、fx_grant_gp 行恰 1、拒绝零变化;消息文案时点瑕疵见§七.3,不涉判据) |
| V11 数据与池 | `test_seven_feats_validate_and_pool`+adventure_feats `_smoke_feats` | 〔换图系批 V11〕×5+〔专长批 V2〕换代句 | ConfigDB 整表+冒险牌库真实启动(seed 71) | **通过**(validate 空/七行稀有度 cap 对表/desc 禁词/七件计数器引用闭合/三计数器在册;rows 37、14/13/10/0、deck 139;feats.tres 七行 trigger 结构逐行对照卡§十.T6 零出入) |
| V12 无专长回归 | `test_no_feats_baseline`+round_flow `_smoke_active_refresh`+seal `_smoke_refresh_provenance_guard` | 〔换图系批 V12〕×4 | 挖格/刷新/停手全真入口 | **通过**(挖格零 RAP、表基线扣 8、AP 保留、第 3 次拒;round_flow/seal 两文件**零进 diff**——RAP8/上限2/AP保留/刷新图零清图/五段序/回放断言原样在案,134/70 checks 实跑绿) |
## 三、卡B 对照表逐行核对(6 行)
| # | 需求点 | 判定 |
|---|---|---|
| 1 | 名字行字号提档 FS_HINT 14 | **通过**——diff 核对五取值点(`:334-336` 两测量+`:467` 测量+`:474` 护甲绘制+`:476` 名字绘制)全改 `UiKit.FS_HINT`;`:114` 消散提示零触碰(FS_CAPTION 计数锚==1 断言在案且实跑绿);`name_line_probe` 增 `name_font_size` 键(探针实读==FS_HINT 绿) |
| 2 | 名字行几何随档联动 | **通过**——`_name_baseline` 偏移 17→18+注释换代(diff 核实);V6 读数 150±0.51 与「8px 档」句换代=卡B§8 申报第 1 条面内;`baseline_y < plinth_top_y` 文字不压地台判据零改动且绿 |
| 3 | 受光线常量退役改指 BRICK | **通过**——ui_kit 真删 `STAND_STONE_RIM` 无别名占位;`standee_body` 顶缘线与 `cell_base_layer:176` 口沿高光两绘制点改指 `BRICK`;V11=常量表零键+BRICK 逐位 c9af83+两处源码锚全绿;grep 全仓退役常量仅剩测试断言串(与报告§三「预期例外」申报一致) |
| 4 | 描边常量退役改指新 token | **通过**——ui_kit 删 `STAND_STONE_EDGE`+原位增 `PLAZA_EDGE := Color8(76, 64, 56)`;`standee_body` 回退描边改指;V12=PLAZA_EDGE 逐位 4c4038+源码锚全绿 |
| 5 | 板面 golden 与换值一致 | **通过**——审计方自跑 `bash tools/render_gate.sh`(窗口化):board_L1/L2/L3 与 menu_boot 全 0.00%;golden 五文件 git 零改动=基线未动、重锚未执行(与实现决定 #4 备案一致——#C4AC84→#C9AF83 灰度差约 2,低于容差 ≤16,0.00% 为必然);level_map.png 不在 render_gate 对比域、git 零改动 |
| 6 | 批末收口 | **通过(遗留义务明示)**——迭代级 `--code` 闸未跑(卡B 行 6 申报「待迭代出口」);批内证据=commit 点 docs 域回执(fde1795d)全绿含 08c 流水线闸+审计方补跑 gdstyle/static_scan/render_gate/五套件全绿。合回后本地 CI 全量域闸为唯一强制验证点(process-conventions §1),本行遗留义务随合回 CI 承接 |
## 四、抽查断言原样输出行(当轮实跑摘录)
```
[refresh_feats_test] all green (85 checks)
[PASS] V5① 敌方攻击路径:承伤单据在案且逐单恰一次(行 2=计数 2)〔换图系批 V5〕
[PASS] V5② 地形路径:地形单据在案且全单据逐张恰一次(行 9=计数 9——盲挖途经单据一并计入)〔换图系批 V5〕
[PASS] V5③ 危险格路径:直伤单据在案且全单据逐张恰一次(行 2=计数 2)〔换图系批 V5〕
[PASS] V5④ 错误封印路径:直伤单据在案且全单据逐张恰一次(行 2=计数 2)〔换图系批 V5〕
[PASS] V2 受理付费挖格 RAP 恰 +1(8→9)〔换图系批 V2〕
[PASS] V3 折后单次扣费(8→4,−4)〔换图系批 V3〕
[PASS] V4 第 1 次成功(事实 +1 换图 +1)〔换图系批 V4〕
[PASS] V6 S1 危险格减伤恰 2 且多刷不叠加(行值 [6]∈[6]——叠加态应为 4)〔换图系批 V6〕
[PASS] V9 R1 轮末回复=min(3,缺血102) 且层清 0(heal 行值 [3])〔换图系批 V9〕
[PASS] V10 第 1 次成功 escrow +1(0→0)〔换图系批 V10〕
[PASS] V12 刷新受理并按表基线扣(−8)〔换图系批 V12〕
[standee_present_values] all green (26 checks)
[PASS] 基线读数与实装几何同源(基线 = 地台顶上方 8px 档:150 < 158——14px 档牌顶+18)〔样张批 V6〕
[PASS] 名字行探针字号实读 = FS_HINT(几何单点出口 name_font_size)〔名字行 V10〕
[PASS] 全文件 FS_CAPTION 仅剩消散提示一处(计数锚==1——三处宽度测量点强制换代)〔名字行 V10〕
[PASS] 退役常量不在册(STAND_STONE_RIM/STAND_STONE_EDGE 零键)〔名字行 V11〕
[PASS] PLAZA_EDGE 值逐位 #4C4038(合并目标②)〔名字行 V12〕
[adventure_feats_test] all green (138 checks)
[PASS] 专长带入池:37 件四级直映射(普通 14/优秀 13/稀有 10/传说 0)——牌库 102+37=139〔专长批 V2+迭代 46 换图系七件换代〕
[round_flow_skill_pool_test] all green (134 checks)
[seal_polarity_test] all green (70 checks)
[effect_words_t1] all green (20 checks)
[forge_table_test] all green (41 checks)
```
```
[render_gate] [golden] board_L1_dungeon.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] [golden] board_L2_underdark.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] [golden] board_L3_deep_underdark.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] [golden] menu_boot.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] 全绿
```
注:V10 消息「escrow +1(0→0)」的两个读数均在 `player_request_refresh()` 求值前格式化——显示瑕疵;断言本体绑刷新后 `escrow == escrow0 + 1`,判据面正确(绿即真值)。
## 五、内部字段直查受控例外核对(test-conventions §80/§8.7 第 4 条)
| 直查行 | 性质 | 判定 |
|---|---|---|
| `orch.fx_host().counters_ctx`(refresh_feats_test 5 处:读计数器×3+白盒预置×2) | `counters_ctx` 是 BattleFxHost **公开成员**(无下划线),读经公共 API `EffectEngine.counter` | 非私有直查;存量套件 adventure_feats_test 同款先例 |
| `EffectEngineScript.counter_add(...)` 白盒预置(V9/V8 上限 3 闸) | 计数器直改=数据面注入 | **已申报**——实现决定 #11(test-conventions §5.4 引用)+对照表 V8/V9 行「白盒预置」在案 |
| `orch1._enemy_system.hero_unit()`(refresh_feats_test:518,V9 缺血读数) | **私有面直查 1 处** | **例外(手续缺注,不阻塞)**——对照表 V9 行与实现决定 #11 均未注该行理由。依据:①判据主面(heal 行/未生效行)经公共 `step_row` 信号在案,私有读只作前置量(`missing >= 3` 守卫),行为并非钉在私有面上;②存量套件 roll_multiplier_test.gd:51 同款先例;③公开替代面存在(`presentation_snapshot()` 含 hero.hp)。建议实现报告 V9 行补注一句(随合回流程,本审计不代改) |
| 生产码 `oc._damage._emit_hero_damage_taken`/`oc._rounds._grant_seg` 直调 | 结算族内部协作 | 实现决定 #8 备案(结算族直调惯例)——非测试面,不在本节判域 |
## 六、附加核对(实现决定复核三项+卡C 脏面)
1. **S7_B1_SET 增补(实现决定 #6)——判据口径未变,通过**:static_scan.sh diff 仅 1 行清单+2 行注释;B2/B3/B4 三族符号交替式(14/5/9 符号)逐字未动;豁免语义(自家文件、注释/字符串排除、s7-b1-exempt)未动;`battle_refresh_flow.gd` 调 `oc._reward_economy.retire_mapping()` 命中 B4 符号,入 B1 域集与 C13 拆分边界自洽。审计实跑 static_scan:S7 ok(50 文件扫描面)。
2. **S9 修正(f498d2ec)——零行为变化,通过**:4 处显式非参数化容器注解(`damage_controller.gd` 3 处 `: Dictionary`/`: Array`、`grant_segment.gd` 1 处)改 `:=` 推断,静态注解改写,无逻辑分支变化。
3. **卡C 停摆脏面核对——申报一致,另见§七.1**:`git status` 修改面恰为申报的三件(`board_view.gd`/`layout_probe.gd`/`board_gen_reward_test.gd`),均未提交、不在审计 diff 内;另有 2 个未跟踪 `.uid` 文件(§七.1)。
4. **golden 申报制核对**:golden 五文件(三张板面+level_map+menu_boot)git 零改动=重锚申报制零触发(实现决定 #4「无需重锚」与实跑 0.00% 互证);不存在借重锚夹带。
## 七、测试削弱核对清单(diff 被改/被删旧断言逐处 → 申报面)
| 位置 | 改动 | 申报依据 | 判定 |
|---|---|---|---|
| adventure_feats_test :464/:476 | 行数 30→37、稀有度 12/9/9→14/13/10、牌库 132→139 | 卡A§十二第 1 条(计数换代申报) | **通过**——零放宽:validate/禁词/退役钉面零删(diff 核对) |
| forge_table_test :202 附近 | EFFECT_KINDS 32→34、EVENTS 20→22 | 卡A§十二第 1 条 | **通过** |
| effect_words_t1_test :17-21 | 纯增量 5 行(两事件两效果在册) | 增量非削弱 | **通过**(旧负例全保留,diff 核对) |
| standee_present_values_test V6 | 读数 149→150+「9px 档→8px 档」句 | 卡B§8 申报第 1 条 | **通过**(文字不压地台判据零改动) |
| standee_present_values_test V10–V12 | 纯增量 8 断言+文件头注释换代 | 卡B§8 申报第 2 条 | **通过**(V5/V7/V8/V9 判据零改动,diff 核对) |
| run_all.gd :191 | 套件注释 V5–V9→V5–V12 | 非行为改动(卡B§6 预告) | **通过** |
| 禁删面(卡A§十二):RAP8 基线/每轮上限 2/AP 保留/刷新图零清图/伤害下限/五段序/同种子回放 | round_flow/seal 两文件零进 diff;伤害下限在 `_resolve_board_flat_hero` 保位(`maxi(下限, dealt)` 减后过下限=卡C4 同款) | 卡A§十二禁删令 | **通过**(134/70 checks 实跑绿承载) |
| 生产面删除项 | `_terrain_damage_reduce` 删/编排器旧刷新体删/`_enemy_shield_apply` 改名/ui_kit 两常量删 | 卡A T2/T3 步骤明列+卡B§1/§3 | **通过**(全部卡面内,无未申报删改) |
| 变异/撤销类取证 | 全 diff 无回退 hunk/注入破坏证明 | 卡A§十二+卡B§3 | **通过**(临时专长经 ConfigDB 表行追加=卡§十二「正常数据配置路径」申报一致) |
## 八、附加发现(不属验收对照面、不阻塞合并——处置建议随合回流程)
1. **批内新件两枚 `.uid` 未随批提交**:`game/core/settlement/battle_refresh_flow.gd.uid` 与 `game/tests/unit/battle_settlement/refresh_feats_test.gd.uid` 未跟踪(mtime 07:49,早于 T3 提交 07:52——非审计跑测所生成)。`.gitignore` 第 6 行明文「Godot 4 的 .uid 文件必须提交,不要忽略」,全仓其余 `.gd` 均有 `.uid` 入库伴件。已核零 UID 引用(grep 两 uid 串零命中),风险低;建议合回前补提交(本审计受只读约束不代提交)。
2. **S8_FILES 未与 S7_B1_SET 同步增补**:await 零命中扫描集仍为 10 文件,新 B1 拆件不在其内。实查新件零 await(grep 证),当日无漏扫;但两清单同源 B1 域集出现分叉,建议 architecture「模块归属」BattleRefreshFlow 行落文(反哺点 #5)时同批补 S8_FILES。
3. **V10 消息文案时点**(§四注):纯文案瑕疵,判据不受影响。
4. **V9 私有读未注理由**(§五):建议补注。
## 九、结论
**审计通过(零缺漏)**——卡A 对照表 V1–V12 与卡B 对照表 6 行逐行通过(例外 1 处=V9 私有读手续缺注,附依据不阻塞);四套件+三附加套件+gdstyle+static_scan+render_gate 当轮自跑全绿;测试削弱全部在两卡申报面内,无未申报删改,无变异/撤销类取证;结构预算申报(803 行/类变量 15/各件增员)经实数核对全部相符。§八附加发现四条均不属验收对照面,随合回流程处置。
@@ -1,21 +0,0 @@
# grilling 复核——站桩名字行提档与地台色合并批需求卡片(2026-10-31)
> 接收方:reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md(v1.0→v1.1)。复核方=独立子代理对抗审查(未参与写卡;Godot 4.7.2 console 版实测断言写法)。本件属中间文档,随批退役。
## Findings 与处置(五条全落 v1.1)
| # | 级别 | 发现 | 处置(落卡位置) |
|---|---|---|---|
| 1 | 阻断 | V11 断言 `UiKit.get_script().get_script_constant_map()` 实测 parse error(类引用直调非静态函数不合法);机制本身可靠(实测枚举 186 键含目标常量) | T2 断言载体改 `load("res://presentation/views/ui_kit.gd").get_script_constant_map()`,依据随句注明(§7.3) |
| 2 | 阻断 | 五取值点中三处宽度测量点零断言——漏改测量点时牌宽按 12px 计、文字溢素底牌,全断言仍绿 | V10 增第三断言=计数锚 `body_src.count("UiKit.FS_CAPTION") == 1`(改后仅剩 :114 消散提示一处),三测量点强制换代(§7.2/§8 行 1) |
| 3 | 阻断 | 绘制点改指只有 grep 反证(旧名消失)无正证(落成错值也绿,golden 重锚还会冻结错值) | V11/V12 补三处绘制点源码锚正证:顶缘线 `ped.size.x, 2.0), UiKit.BRICK, true`、口沿 `FACADE_HIGHLIGHT_PX), UiKit.BRICK, true`、描边 `UiKit.PLAZA_EDGE`(§7.3/§8 行 3/4);grep 降为反证补充 |
| 4 | 建议 | §8 首句「6 行对应完成定义第 1–6 条」对应关系不成立(第 5 条无行) | 首句改「第 1–4、6 条;第 5 条经 T3 核对步承载」(§8 首句) |
| 5 | 建议 | `run_all.gd:191` 套件注释「V5–V9」增 V10–V12 后成死注,但该文件不在结构图 | §6 文件结构图补 run_all.gd 行(注释换代,非行为改动)+T2 增注释换代步(§7.3) |
## 无发现审点
步骤扫描(每步一动作一可核查结果)/名字一致性(PLAZA_EDGE、name_font_size、V10–V12 逐字一致且与盘面无冲突;V10 锚串与顺位徽标行无碰撞)/落位与复用(`Color8(76` 全仓零命中、`#4C4038` 仅探针注释非 token——新常量论证成立)/预算与契约(probe 增字典键非成员,gdstyle 三触面零基线键,S13 零新增预期成立;golden 重锚工具缺省只动板面三张,与禁触清单兼容)/估时(0.5 天与触面相称)。
## 结论
三阻断两建议全部落卡后 **PASS**——卡片 v1.1 成立,可交迭代编排段。
@@ -0,0 +1,85 @@
# grilling 复核报告——零豁免五卡(2026-10-29,主会话委派)
状态行:对抗式复核完成。方法=逐卡过审点 A–J(验收三问/对照表两端/静态判据形态/能力行锚定/预算实数/拆分纪律/编队碰撞/行为改动完备性/裁决一致性/可施工性),判据=req-card 必备结构 15 节+code-conventions §1.1+test-conventions §8;证据=五卡 file:line 抽验约 90 处(命中率高,个别 1 行漂移属 T0 兜住范围)+五份设计文档口径核对+architecture v1.25+能力清单逐行核对+gdstyle_baseline.txt 键账清点+迭代 42 在飞两卡触面比对+pipeline_gate 全绿。只读复核,未改任何卡与既有文件。
## 卡一:gdstyle 尾波补件批(board_hover+breakpoint_store)
**裁决:有条件成立**(四项编入前须补,均为编辑级补丁,不动拆件方案本身)。
逐条发现:
1. 【应修】卡面 §7 编队注记——「与呈现层状态件批同代时**推荐**其先行」,而呈现层状态件批 §3 已把同一约束写死为「已裁……本批先于其 board_hover 半边合回」的硬义务。同一约束两卡一软一硬,编排段按弱措辞排卡会让 hover 拆件落在旧 u.st 写入面上、board_view 改址对账表(单格篇 §5.1)随之失配。改法:board_hover 半边改成硬前置句「呈现层状态件批合回后施工」;breakpoint_store 半边明示不受此限、可独立先行。
2. 【应修】§8 V1 判据绑定面写「格件 **st** 边框/点迹字段」——本卡自认编排在状态件批之后,届时该读面已改 u.face.*(ui_input_probe 四处改址为状态件批 V4 行),判据面描述按施工时点必陈旧。改法:改写为「格件状态件边框/点迹字段(状态件批合回后=u.face.*)」。
3. 【应修】§3 现状事实「公开入口 `resume_adventure`(:94)」行号错——实查 :94 是 start_adventure,resume_adventure 在 adventure_runner.gd:141。改法:改 :141(T0 重锚虽会兜住,现状事实按规约须代码级准确)。
4. 【应修】§13 预算节「预算 2(_hover/_last_click)**+引用束成员**」「预算 6……**+引用束**」未给引用束成员数,复核门无法对账 ≤15。按 §7/T2 构造面推算 board_hover 约 7(2+cells/minesweeper/diggable+2 Callable)、breakpoint_store 约 14(6+db/rng/主角账三件/curio/stop/bank/loadout)——均达标但后者贴顶。改法:把引用束成员逐一列名计数写进预算表。
5. 【可接受申报】V1 零漂移证明全压既有断言面;悬停 11 函数族中 chord 双击首遇锚(board_view.gd:475 _detect_chord)在「棋盘悬停反馈」三语义行与 ui_click_probe 之外无显式断言,属既有覆盖缺口。零行为改动批不为既有缺口补测不违规,实现报告宜记一句该缺口。
6. 核验通过项:A 两行对照表三问可答(真实入口/判据面/能力行齐);C 对照表零静态行;E 键账全对(board_view 32 实数清点吻合、−2=约30;adventure_runner 28 清点吻合、−6=约22;断点域 6 成员与 9 函数族 file:line 全命中;BREAKPOINT_FIELDS :539 起、breakpoint_resume_test.gd:185 命中);F 拆分职责语句在位、_r0 留宿主防凑计数袋论证成立(board_view.gd:238/:296/:563 三消费方实查);G 与迭代 42 的 board_view 触面已声明、编入待其合回;I 与裁决①②⑤一致(D5 留债、禁落豁免登记、_r0 留宿主);J 一步一动作、无 TBD 类句式。
## 卡二:组合根注册面批(main 49→15)
**裁决:有条件成立**(一项 blocker 级必改+两项应修;机检与数字面全过,方案本身成立)。
逐条发现:
1. 【blocker】§1 编队注记「与迭代 42……**无文件交叠(main.gd 与探针不在其触面)**」与盘面矛盾——在飞并排卡 `reports/需求卡片-level_runner商店委托层拆除-2026-10-29.md` 文件清单明确含 game/presentation/main.gd(改址 1 处)、main_adventure_flow.gd(6 处)、main_overlay_flow.gd(2 处),三者全在本卡触面集(main.gd+三流程件)。用户裁决④要求五卡编队碰撞约束与迭代 42 自洽,此句是失实声明,编排段靠它排卡。改法:改为「与迭代 42 商店委托层拆除卡在 main.gd/main_adventure_flow/main_overlay_flow 三文件有改址点交叠——待其合回后开工,T0 映射表生成时并核其改址点」;T0 步骤同步补一句。
2. 【应修】§8 对照表第 2 行与第 11 行内含静态判据(S13 键账对账「main.gd 实测 15」、grep 残留=0)——姊妹卡三/四/五均已把静态键账判据按 test-conventions §8.4 出口移出对照表(在域口径段申报),本卡两行在表内带静态成分,形态不一致。改法:静态部分移入 §8 域口径静态申报段,行内只留行为主证(双宿主摘要/全量闸回执)。
3. 【应修】转发壳清点漏一件:main.gd:516 `main._flush_row_queue()`(纯转发 `_row_pacer.flush()`,消费方 pacing_queue_test.gd:103+main 内部两处注记)不在「七别名+六转发方法」清单——_row_pacer 迁 ui.screens 后该 Middle Man 壳归宿未声明,与本批净消灭转发壳的方向不一致。改法:表 C/表 D 补一行(退役并改址消费方,或写明保留理由)。
4. 【可接受申报】V4(探针批量改址)与 V10(render_gate)的能力行锚取九探针中单点代表行(棋盘悬停反馈/站桩敌人呈现),语义为最小可得锚且未逐行披露该局限。建议 §8 域口径补一句「探针/渲染行能力锚取代表行」。
5. 核验通过项:A 十一行全齐;E 数字全对平——main.gd 49 个 var 实数清点吻合(grep ^var 恰 49)、49=28 迁(表 A 9+5+14)+14 留(表 B)+7 消灭(表 C)+1 新增=15;四新件预算(2/0/0/1 公开方法、3/9/5/14 成员)与 §7.2 接口块逐名一致;LAYER_CONTRACT :39 起、六转发方法 :156/:161/:282/:285/:288/:292、别名七件行号全命中;smoke_boot 六处 get()=4 改 2 留逐字吻合;pacing_queue_test :108/:132/:147/:149、source_guard V1d :69-77、theme_projection V6 :724-725、system_test V1c/V2 :94-109 全命中;D 能力行六名全在 game/tests/README.md 且受影响行核对成立;F §12 复用排除论证(ModalFlow/ModalStack/ui_assembler/run_scope 逐一否决+ConfigDB 三容器先例)充分;唯一面实查成立(game/presentation/ 下四名未建、仅 ui_assembler 在册);I 与裁决①③一致(零豁免、注销句、净消灭);J 估时 3–4 日在冻结线内、步骤全部可核查。pipeline_gate 机检 13 卡全过。
## 卡三:战斗编排归约批(RoundCycle+BattleFacts,38→15)
**裁决:有条件成立**(一项应修算术错;其余全过,是五卡中证据密度最高的一张)。
逐条发现:
1. 【应修】§4 红线句内部算术矛盾:「已迁 **22** 成员声明随迁即删(**21** 迁两件+treasure_inventory 退役+_hero_atk 退役)」——21+2=23≠22。实数=迁两件 22(BattleFacts 15+RoundCycle 7)+退役 2=删声明 24 项;轴丙后宿主 37→13 旧留+2 新=15,账平。architecture v1.25 两行写「收编 7+14=21」与本卡 BattleFacts 15 名单的 1 名口径差,已由 §6 反哺点通道(「批末成员数实测值与在册数字出入」)覆盖,但本句数字须改正以免实现期对账混乱。改法:改「22 迁两件+2 退役」。
2. 【可接受申报】encounter 归 BattleFacts 最弱格标注诚实:设计文档 §6 白纸黑字「它也是本方案最弱的一格」+两条备选砍路(nest_preview_count 解冻/_reward_economy 经授予件中转)均判更差;卡内 L3② 给出实现期证伪的停卡出口、§4 给出 15/15 贴顶与 SealLedger 扩容阀、设计 §8 险点 8 处理了 BoardInstaller bundle 键 encounter 传 _facts.encounter 的同引用零冲突——无隐瞒。
3. 核验通过项:A V0–V6 七行齐(V2 复位边界险点单独配断言、判据绑两快照同键值恒等);E 类变量 38 实数清点吻合(:37-85+:223-227 逐名)、基线两条类级键与「零函数级键」实查一致、保留 15 名单/两新件 8+15 成员账全平;file:line 抽验约 20 处全命中(treasure_inventory setter :48-52、外部注入 level_runner.gd:520、宿主四读 :111/:203/:450/:858、grant_segment :370-371、combat_segment :56、settle_segments_test :27 接线断言原文、static_scan S8 :317/S7 :359 九文件清单、run_all_gates PARITY 清量 battle_orchestrator :169、五段构造/三闸/摇号/复位块/完美谓词行号);C 静态判据(键账收敛、S7/S8 增行)已按 §8.4 出口移出对照表在域口径段申报;D 十三处能力行全部在 README 在场且语义对题(V0 锚「回放确定性」为精确锚);F 两新件职责语句+集体身份在位(「持段件者不能是被持者」「事实账不是授予物」)、已否决切法留档;G 编队前置「迭代 42 合回之前禁开工」覆盖与在飞两卡在 battle_orchestrator/level_runner/static_scan.sh/treasure_effects_test 等的全部触面交叠;I 与裁决①③⑤一致;J 估时 2.5–3.75 对设计 §12 的 2–3 日给了上探理由,PARITY 批内实跑须用户点头不占估时。
## 卡四:呈现层状态件批(单格两域件+站桩两件,30→10/19→10)
**裁决:有条件成立**(一项应修编队漏声明;潜伏红处置路径成立,成员拆分账五卡中最难算、已全对平)。
逐条发现:
1. 【应修】与卡二在 game/probe/ui_input_probe.gd 的**同文件交叠两卡均未声明**:组合根注册面批改该探针的 main.X 地址面(:34-43 等约 867 处散布之一),本批改四处 u.face.* 直读(:234-235/:243-244/:250/:374-379)——行位不相交但同文件,两卡又是同一条目(零豁免五键清偿批四卡)的同代候选,编排段靠编队声明排卡,此漏会制造二次 T0 重锚。改法:两卡编队注记互写一句(本卡补「与组合根注册面批在 ui_input_probe.gd 有触面交叠,先后施工各自 T0 重锚」,卡二同步)。
2. 【可接受申报】潜伏红 T0 定谳路径诚实:四处直读实查确在探针源码、迭代 41 已把 hl_visible/hl_color/dot_visible/mutation_mark 迁入内嵌类 _CellPaintState(board_cell_unit.gd:27 起),宿主 Node2D 无同名属性,「Invalid get index」推演成立;卡内预设 L3③「实跑全绿=证伪设计前提=停卡」的出口——未把推断当已证事实写死,定谳义务落在 T0 实跑。
3. 【可接受申报】V4 能力行锚「棋盘悬停反馈」含 case⑭ 变异角标(非悬停三语义)的最小拉伸;同一探针同一入口,可接受。
4. 核验通过项:A 四行齐(V1/V2 golden 主证、V3 演出链、V4 悬停金框/禁入红框/路径点迹/变异角标四处断言面);E 成员账全对平——内嵌类 27 名实数清点吻合、基座 12(11+_owner)+叠加 13(12+_owner)+宿主上收 edge/cell/glow_group+原位 glyph/glyph_color/plate/fog_state/_glow_layer+base/face=宿主 10;站桩真实 25=vitals 11 名+motion 10 名+原 _owner+3 计数器、宿主 10=5 冻结 var+st+mo+3 计数器;31/33 全文件声明数与 :26-122/:125-143、:23-59/:62-68 分区实查一致;九常量 :12-23 逐字命中;C 静态键账判据已按出口移出对照表(§9 域口径段申报,五卡中形态最规范);D 六处能力行全在场且语义对题;F 四新件职责语句+逐件排除论证(TilePalette/HeroSprite/BoardWorld/battle_stage_view/StatusSlotRow)在位,迷雾留宿主的「唯一不产生跨件回引」论证成立;G 与迭代 42 的 board_view 五段改型耦合声明完整(T0 前置+「先于尾波补件批 board_hover 半边合回」反向义务);gdstyle 截断缺陷红线(禁利用截断口径降数、禁把 _portrait_tex 当削减目标)与裁决一致;golden 三张禁换锚禁重制。
## 卡五:测速循环批(信号步进状态机+双实现对拍)
**裁决:有条件成立**(一项应修能力行悬置;对拍方案与申报形态是五卡中最完整的)。
逐条发现:
1. 【应修】能力行锚语义错位且专属行悬置无主:三行对照表能力列锚「站桩敌人呈现与结算演出落点」,该行语义是站桩呈现与终局演出(README 旅程段六,系统列=战斗表现),与本批测量面(批量模拟逐帧步进/运行态贯穿/报告注入)不同域;卡内自承「测量面专属能力行=写卡段后续处理」——但写本卡的会话**就是**写卡段,2026-10-29「当场入表」规则下该行应随本卡增行(真实入口=debug_overlay press_simulate_for_probe() 按钮 pressed 信号链→run(n);代表用例=standee_fx_sweep_test.gd:_init〔wd8〕+tune_sim_batch_parity_test.gd:_init),增行后本列改锚。悬置写法让义务无承载主体、批末无人对账。
2. 【可接受申报】行为改动申报完备性成立:全件 68 行实读核对——:67-68 确为每局置假(潜伏红本体),静态矛盾推演(drv.seen=[true,false]、探针守卫环同段退出四串全红)与源码逐句吻合;改形其余面(常驻连接/is_connected 守卫/首步同步/n=0 同步收尾/_step_frame else 先断连)核为非行为改动;「_finished 若零读者可删」方向是删不是留,不违禁为机检保留成员条款。
3. 【可接受申报】脚本错误中断路径差异按 §8.4 出口声明缺口(协程死亡静默停摆 vs 确定性错误逐帧重试),T3 文档化义务在位,未伪装成可证判据;两形态测量批遇错都整批作废、同级不同形的论证成立。
4. 核验通过项:A 三行齐且真实入口真实(wd8 假驱动回调内读 is_running()、探针真实按钮信号链);E 键账预算表逐规则落卡、成员 5+6=11(信号计入则 12)账与 T1 名单吻合、_run_one_level 5 参临界持平声明在位、改造后全文件零循环键消除逻辑成立(结构性免疫);file:line 全量命中(run :36-52、循环 await :50、FakePanel/FakeDriver、wd8 三断言与〔wd8〕尾缀、ui_tune_probe SIM_GUARD_FRAMES=1800 :16 与 :63-83 四串断言、debug_overlay is_running :77/set_sim_running :81/_sync_sim_button :107、main_debug_flow :72-77 不 await 形态与 :84 互查守卫、hud_presenter 空守卫与 maxi(1,n));C 静态判据与声明缺口均不入对照表;F 零拆出单元的「不拆理由」在位(步进机是私有运行机制、拆出命不出职责语句=凑计数袋,§8 引 code-conventions ②④);遗留 await 形态副本全仓唯一性+夹具不入四扫描面的声明经实查成立(基线 :15 唯一键、S8 九文件不含本件);G 与其余四卡及迭代 42 无产品码触面交集声明成立(run_all.gd manifest 潜在交叠已交编排段);I 与裁决③④⑤逐条一致(双实现对拍入批、收尾还原作为已申报行为改动、错误路径接受并文档化);J 估时 1–1.5 日合理,T0 定谳证据列为审计第③项必查项。
## 总表
| 卡 | 裁决 | blocker | 应修 | 可接受申报 |
|---|---|---|---|---|
| 卡一 尾波补件批 | 有条件成立 | 0 | 4 | 1 |
| 卡二 组合根注册面批 | 有条件成立 | 1 | 2 | 1 |
| 卡三 战斗编排归约批 | 有条件成立 | 0 | 1 | 1 |
| 卡四 呈现层状态件批 | 有条件成立 | 0 | 1(与卡二共担) | 2 |
| 卡五 测速循环批 | 有条件成立 | 0 | 1 | 2 |
全库 blocker 与应修清单(编排段放行前置):
- **blocker(卡二 §1)**:编队声明失实——「与迭代 42 无文件交叠(main.gd 不在其触面)」被在飞商店委托层拆除卡的文件清单(main.gd/main_adventure_flow/main_overlay_flow)直接证伪,须改写为如实交叠声明+开工前置。
- **应修(卡二+卡四,跨卡)**:ui_input_probe.gd 同文件触面交叠两卡编队注记均未声明,互写一句补齐。
- **应修(卡一 §7)**:board_hover 半边的状态件批先行约束从「推荐」升为硬前置,与卡四侧已裁义务对齐;breakpoint_store 半边明示可独立先行。
- **应修(卡一 §8)**:V1 判据绑定面「格件 st 边框/点迹字段」按施工时点改 u.face.* 口径。
- **应修(卡一 §3)**:resume_adventure 行号 :94 改 :141。
- **应修(卡一 §13)**:预算节引用束成员列名计数(board_hover 约 7、breakpoint_store 约 14)。
- **应修(卡二 §8)**:对照表第 2/11 行静态判据移入域口径申报段,与卡三/四/五形态统一。
- **应修(卡二 表 C/D)**:main._flush_row_queue(:516)转发壳归宿补声明(退役改址或保留理由)。
- **应修(卡三 §4)**:「已迁 22 成员(21 迁两件+2 退役)」算术改正为「22 迁两件+2 退役」。
- **应修(卡五 §6/§12)**:测量面专属能力行随本卡当场增入 game/tests/README.md 并改锚三行能力列,废「写卡段后续处理」悬置表述。
总体判断:五卡可以进编排段,但须先落上表一项 blocker 与九项应修——全部是编辑级补丁与声明修正,无一张需要推翻拆件方案或重做设计。证据面结论:五卡 file:line 抽验约 90 处命中(唯一硬伤=卡一处行号),键账与成员迁移账全部数平(含卡四最难的 27/25 内嵌类双 _owner 拆分),两处静态矛盾推演(卡四潜伏红、卡五每局置假)经源码逐句复核成立,与五项用户裁决无一冲突,机检 pipeline_gate 全绿。最大的真实风险不在卡内而在编队声明层:卡二对迭代 42 的无交叠声明失实、卡二与卡四未声明探针同文件交叠——这两处修正后,编排段才有可信的排卡依据。
@@ -1,208 +0,0 @@
# 审计:全库架构审查(2026-10-29)
> **性质**:设计讨论段过程稿(审计读数)。`game/` 全目录只读架构审查的发现清单与证据,作为写卡段、工程改动批与文档设计批的输入材料;结论被吸收进权威文档与修复批后按「吸收即死」退役。
> **范围**:core 44 件、presentation 71 件、data 24 schema + 19 表、sim 15 件、probe 19 件、tests 69 件、`tools/` 闸件。判据双轨=项目自有规范(`design/engineering/architecture/architecture.md` v1.24 + 五份工程规约)+ Godot 4.7 最佳实践。
> **方式**:分层依赖方向/数据管线/测试覆盖三路并行静态取证+机械 grep 复核。**零测试执行、零引擎运行、零闸执行**(测试两入口铁规),全部结论来自静态读盘;下文每条命中均给 文件:行号,抽查原文已核。
---
## 一、总评
分层骨架是健康的:core 纯逻辑(44 件里 37 件 `RefCounted`、3 件 `Resource`,`extends Node` 仅 4 个 Autoload)、Core 零 UI 八项判据全零命中、core 内 RNG 单点 100% 成立、字符串式信号连接全库 0 处、core 层函数体超 120 行零命中、19 表 ↔ 19 schema 一一对应零孤儿、无循环 preload。
**真正的风险不在分层,在三条写了却没闸守的纪律被实际破坏,外加三处守卫面缺口。**
1. 「玩法数值只存 `data/tables/`」被 18 处硬编码击穿,其中两处是玩家可见的提示文案数字。
2. `game/sim/` 与 `game/core/` 存在四组平行实现,直接威胁「模拟读数 = 游戏真实数值」这一前提。
3. RNG 单点、类型闸、质量闸、结算禁 await 的扫描面都止步于 core/presentation(部分只扫 9 文件显式清单),sim / probe / tests 三层几乎不受约束。
另有封装面(结算段类直取编排器私有成员)与文档漂移(架构文档登记件与盘上实件不一致)两类低危问题。
---
## 二、健康面(已逐条核,零问题)
| 判据 | 结果 | 证据口径 |
|---|---|---|
| Core 零 UI | 零命中 | `res://presentation`、`Control`/`Node2D` 系、`.tscn`、`$` 节点语法、`get_parent()` 在 core 全域零命中;core 内 `get_node` 仅 2 处且均为 Autoload 取依赖 |
| RNG 单点(core 内) | 100% 合规 | 15 处 `open_stream` + 1 处 `pure_stream` 全经 RngService;`RandomNumberGenerator.new()` 在 core 内仅出现在 `rng_service.gd` 自身 |
| 字符串式信号连接 | 全库 0 处 | 正则 `\.connect\(\s*"` 全 `game/` 零命中 |
| core 层 await | 零命中 | 4 处匹配全在文件头注释里自述「零 await」 |
| core 超长函数 | 零命中 | 函数体 >120 行只在 presentation(`board_view._paint_cell` 142 行、`ui_kit.danger_button` 122 行) |
| 表 ↔ schema | 一一对应 | 19 `.tres` ↔ 19 顶层 schema,零孤儿表、零缺 schema;24 schema 全被消费 |
| resolver / cross_validator | 非测试专用 | 二者都在 `ConfigDb._ready → load_tables()` 产品路径真实运行 |
| 循环 preload | 零命中 | 全仓依赖图 DFS 仅 2 个自环,均在注释文本里 |
| 静态闸防空转自检 | 已内置 | S1–S13 各规则自带扫描面文件数为零即判红 |
---
## 三、P0 — 破坏既有纪律,已有实证
### 3.1 数值单一事实源被击穿(架构原则 2:代码零玩法数值字面量)
判据来自 `design/engineering/architecture/architecture.md` 架构原则 2 与 `code-conventions.md` 数值类硬纪律。下表分两类:**A 类=用户可观测的玩法数值写死在代码,直接违规**;**B 类=自标「机制结构常量/产出约束」,是否该入表属待裁,不计违规**。
#### A 类(18 处,直接违规)
| # | 位置 | 写死的值 | 表内真值 / 影响 |
|---|---|---|---|
| 1 | `game/presentation/main.gd:787` | 提示文案「弃图刷新需 8」 | `action_economy.tres` 的 `rap_refresh_cost`。紧邻 783 行是正确读表的,文案另写一份 |
| 2 | `game/presentation/main.gd:790` | 提示文案「每轮 2 次」 | 同表 `refresh_cap_per_round`。表改了玩家看到的数字不改 |
| 3 | `game/core/battle_orchestrator.gd:883` | GP 掉落乘数 `1.5` | 表内无此项;代码注释自认「乘数 provisional 150%」 |
| 4 | `game/core/node_map_generator.gd:380` | 岔口分支概率 `0.35` | `level_gen_params` 表内无此项 |
| 5 | `game/core/node_map_generator.gd:455` | rest/reward 排列起始骰 `0.5` | 表内无此项 |
| 6 | `game/core/node_map_generator.gd:361` | 岔口目标数上限 `24` | 表内无此项 |
| 7 | `game/core/effect_engine.gd:146` | 天赋「守」盾值 `1` | 表内无此项 |
| 8 | `game/core/effect_engine.gd:152` | 天赋「锋」伤害减半 `50` | 表内无此项 |
| 9 | `game/core/effect_engine.gd:151/154/161/164` | 天赋每关限次 `2` | 表内无此项 |
| 10 | `game/core/damage_controller.gd:435` | 直伤分量默认 `50`(百分比) | 表内无此项,`h.get("value", 50)` 兜底 |
| 11 | `game/core/battle_orchestrator.gd:507` | 变体成包大小下界 `2` | 只有上界读表 `variation.pack_max` |
| 12 | `game/core/settlement/combat_segment.gd:64` | 连击分段默认 `cap=2` / `divisor=3` | 表内无此项 |
| 13 | `game/presentation/playtest_logger.gd:16` | `ap_per_round := 8` | 与 `action_economy.tres` 同名值逐字重复 |
| 14 | `game/core/config_db_resolver.gd:118-119` | clamp 域 `0.1` / `10.0` | 与 `difficulty_table.gd` 的 `MULT_MIN`/`MULT_MAX` 重复但另写一份字面量 |
| 15 | `game/core/curio_deck.gd:98` | 令牌 cap 兜底 `99` | `config_db_cross_validator.gd:91` 注释写 cap ∈ [1,99],schema 无定义 |
| 16 | `game/core/shop_session.gd:155` | 货架位 `3` | 表内无此项 |
| 17 | `game/sim/level_autoplay.gd:29-34` | 失误三概率 `0.10` / `0.05` / `0.10` | 直接决定 MC 读数,属模拟口径的一部分 |
| 18 | `game/sim/level_autoplay.gd:410-428` | 选路评分 `30/5/10/80/20` | 同上 |
**最危险的是 #1 #2**:玩家会直接看到错误数字,且没有任何闸能发现「表值与文案数字不一致」。
**同族不一致(顺带查出)**:同调位上限在 `core/attunement.gd:14` 与 `core/meta_progression.gd:476` 写 `range(1, 4)`(上限 3),而 `presentation/views/hero_info_panel_view.gd:61/175` 写 `for i in 4`(4 槽)——两处口径已不一致。
#### B 类(自标结构常量,是否入表待裁,不计违规)
| 位置 | 常量 | 代码自述 |
|---|---|---|
| `game/core/node_map_generator.gd:12` | `MAX_WIDTH := 4` | 注释自标「产出约束·宽度=分叉生长」 |
| `game/core/action_economy.gd:14` | `CHARGE_LAYER_CAP := 3` | 注释自标「机制结构常量,非玩法表值」 |
| `game/core/board_data.gd:9` | `SPAWN_AREA_CELLS := 9` | 被 `data/schemas/board_gen_table.gd:77/106/107` 反向引用参与表校验——core 常量成了 data 侧校验的输入 |
| `game/core/node_map_generator.gd:83-94` | 层深下限 6、出口数域 [1,3]、岔口去路域 2/3 | 与 `level_gen_params.gd` 的域校验平行,两份域定义 |
---
### 3.2 sim 与 core 平行实现(四组)
`game/sim/` 15 件里 10 件是纯驱动器/采集器(调用 core,不重算规则),但有四组平行实现,其中两组直接导致「模拟读数 ≠ 游戏真实数值」。
| 组 | core 侧 | sim 侧 | 后果 |
|---|---|---|---|
| ① 扫雷推理引擎 | `board_generator.gd:175/204/217/234/249/265/286`(`measure_inferability` 族,输入含 `safe_set` oracle) | `auto_player.gd:114/154/181/188`(`_deduce` 族,输入 no-peek,且不做 0 格连锁算子) | 两边可推理率/卡死率不可直接换算。`auto_player.gd:80-83` 注释自认口径差异 |
| ② 伤害掷点期望 | `damage_math.gd` + `damage_controller.gd` 兑付管线 | `damage_probe.gd:196-201` 自建 `base_factor`/`rolled_base`/`floor` 取整顺序,只在末步复用 `pool_damage` | 探针的 ✓/✗ 比对基于自行重建的中间量 |
| ③ 档位排序与驱动循环 | `treasure_item_table.gd:44` 的 `TIER_RANK`;`sim/battle_driver.gd:17` 自称「驱动循环单点」 | `level_autoplay.gd:300-311` 另写一份档位排序;`level_autoplay.gd:521-575` 另写一份驱动循环(守卫阈值还不同:多出 `round_guard=60`、`rejects<3`) | 被声明为单点的东西存在第二份实现 |
| ④ 伪造表喂 core | 数值单一事实源 = `data/tables/*.tres` | `calibrate_boards.gd:14-19` 与 `experiment_density.gd:61-66` 用 `FakeDB` 伪造 `board_gen` 行喂 core 生成器 | 校准类读数绕开 SSOT(实验用途可理解,但无闸约束其扩散) |
---
## 四、P1 — 守卫覆盖缺口
### 4.1 RNG 单点纪律在 core 之外失守,且无闸
core 内干净,绕过点全在 core 之外:
| 层 | 位置 | 形态 | 是否被闸扫到 |
|---|---|---|---|
| presentation | `main.gd:914` | `randi()` 建种子,行内标 `rng-seed-exempt` | 扫到但被豁免标记排除 |
| probe | `ui_click_probe.gd:20` 等 8 处 | `RandomNumberGenerator.new()` | **不扫** |
| sim | `calibrate_boards.gd:54`、`experiment_density.gd:27`、`damage_probe.gd:274`、`level_autoplay.gd:388` | 自建 RNG + 显式定种 | **不扫** |
| tests | 3 处测试夹具 | 自建 RNG | **不扫** |
全部绕过点均显式定种、无 `randomize()`(全库零命中),确定性尚存;问题在于**没有任何闸防它扩散**——S1/S2 扫描面只有 `DIR_CORE` + `DIR_PRES`(`static_scan.sh:20-21, 88-91`)。
### 4.2 静态闸扫描面结构性缺口
`game/tests/static_scan.sh` 的 S1–S13 扫描面实况:
| 闸 | 扫描面 | 缺口 |
|---|---|---|
| S1/S2(RNG 单点) | core + presentation | sim / probe / tests 不扫 |
| S3(core 表加载单点) | core | — |
| S4(core 零引擎 UI 依赖) | core | — |
| S5(core 禁裸引 Autoload) | core | — |
| S6(core 信号禁内联 lambda) | core | — |
| S7(B1 编排唯一性) | core | 不管私有成员直取面(见 5.1) |
| S8(结算路径禁 await) | 9 文件显式清单 | 新增结算文件忘登记=静默不扫;缺位自检只防删改不防增 |
| S9(类型闸) | core + data + presentation | sim / probe / tests 不扫 |
| S13(gdstyle 质量闸) | core + data + presentation | 同上 |
| S11(术语禁用词)/S12(旧件残留) | game/ 全域 | 无缺口 |
净效果:**sim / probe / tests 三层除 S11/S12 外,零类型闸、零质量闸、零 RNG 闸、零 await 闸。**
### 4.3 S13 豁免常态化
三处豁免全落在体量最大、风险最高的文件上:
| 豁免对象 | 豁免内容 | 后果 |
|---|---|---|
| `core/battle_orchestrator.gd` | S13 全库单文件最大户,成员数阈值 15 豁免(实测键账 50→38 仍豁免) | 豁免后 S13 对它恒不约束,成员数再涨无感知 |
| `presentation/main.gd` | 46 个句柄成员 | 同上 |
| `presentation/tune_sim_controller.gd` | await 使用 | 同上 |
### 4.4 四个 core 文件零测试点名
判定口径:文件名与 `class_name` 在 `game/tests/` 全域 grep 均零命中。
| 文件 | 行数 | 职责 | 风险 |
|---|---|---|---|
| `core/attune_loadout.gd` | 117 | 同调配装件:装备中秘宝件对象集唯一所有者 | 有状态 |
| `core/battle_fx_host.gd` | 179 | 效果引擎宿主:fx 事件发射面、事实判定、AC/bonus 累积 | **有状态且在结算路径上** |
| `core/meta_state_store.gd` | 176 | 存档内元进度数据件(13 写口 + 6 只读投影) | **有状态且在存档路径上** |
| `core/slot_store.gd` | 189 | 存档槽位文件域件(挂载/激活/建删/摘要窥视) | 同上 |
`config_domain_combat` / `config_domain_run` / `config_domain_crossrun` 三个纯持有容器(零方法)零覆盖属可接受。
低覆盖(仅 1 个测试文件点名)另计:`hero_stats`、`skill_lottery`、`shop_session`、`curio_deck`。
---
## 五、P2 — 封装与漂移
### 5.1 settlement 五段类直读写编排器私有成员
`core/settlement/` 五段类作为「提取的方法对象」,直接读写主编排器的下划线私有成员 20+ 处:
- `combat_segment.gd:34/37` 写 `oc._damage.inject[...]`;`:52/53/58/77/78/80/89/91` 读 `oc._enemy_system.*`;`:123` 调 `oc._damage.flush()`
- `grant_segment.gd:96/121/151/178/229/289/333` 调 `oc._damage.open_ticket(...)`
- `aura_segment.gd:15/16` 调 `oc._emit_composition_effects(...)`;`decay_segment.gd:15` 调 `oc._status_effects.decay_round()`
GDScript 无编译期访问检查,私有成员改名不会有任何闸报错,只会静默断裂。S7 只管 B2/B3/B4 对外 API 符号收敛面,不管这个。
### 5.2 架构文档与代码漂移
| 文档位置 | 文档写的 | 盘上实况 |
|---|---|---|
| `architecture.md:110` | `core/hero_ledger.gd`「已实装」 | 该文件不存在;已拆为 `hero_stats.gd` / `gain_journal.gd` / `gp_wallet.gd` / `refresh_bank.gd` 四件。`tests/unit/core/hero_ledger_test.gd:171` 显式断言 `not FileAccess.file_exists("res://core/hero_ledger.gd")`,即代码层已把它当回归禁区 |
| `architecture.md:135` | `battle_orchestrator` 1313 行 / 50 成员 | 实际 1034 行(拆件后未回写) |
| `architecture.md` EC8 节 | 数据配置热重载能力(`set_reload_gate` 保护 `reload()`) | `ConfigDb.reload()` 全仓零调用方;且因走 `load()` 命中引擎资源缓存,即便调用也拿不到磁盘新值。门被 `battle_orchestrator.gd:295/861` 真实开合,但门后入口是死的 |
### 5.3 class_name 级双向依赖
`core/config_db.gd` → `data/schemas/board_gen_table.gd`(持有 `BoardGenTable` 实例)→ `core/board_data.gd:9`(取 `SPAWN_AREA_CELLS`)形成跨目录回边。双方均走 `class_name` 全局引用、未走 preload,不产生解析期死锁,但构成架构层面双向耦合。
同族:`core/node_map_generator.gd:10` 与 `data/schemas/level_table.gd` 各持一份 `NODE_TYPES` 词表(内容同、顺序不同)。
### 5.4 Autoload 形态不一致
四个 Autoload 里 `MetaProgression` 是唯一用 `get_node("/root/...")` 取依赖(`meta_progression.gd:26/28`)、唯一跨 Autoload 直调、唯一持 Node 引用的;另三个零交叉引用。注释说明是为绕 `--script` 模式 parse error,但与另外三个的构造注入形态不一致,且隐含 Autoload 加载顺序依赖(ConfigDB 须先 ready)。
---
## 六、待裁点
以下均需用户裁定,Agent 不代裁:
1. **A 类硬编码 18 处怎么修**——按改动四类归属,其中 `core/` 与 `presentation/` 的玩法数值入表属**需求改动**(写卡 + 编排),`sim/` 两处模拟口径常量属**工程改动**。两类不能混一批。
2. **B 类结构常量要不要入表**——`MAX_WIDTH` / `CHARGE_LAYER_CAP` / `SPAWN_AREA_CELLS` / `node_map_generator` 的域字面量四组,代码自标「非玩法表值」,但 `SPAWN_AREA_CELLS` 已被 data 侧校验反向引用,是否算玩法数值需裁。
3. **sim 平行实现的处置口径**——是收敛(sim 调 core 的推理与掷点)还是承认双口径并显式登记(避免读数混用)。四组里①②影响数值验证可信度,③④影响可维护性。
4. **闸面缺口补不补**——S1/S2/S9/S13 是否把 sim 纳入扫描面;S8 是否改成目录域而非 9 文件显式清单。
5. **S13 三处豁免的存废**——豁免已常态化到最大户文件上,是收回还是登记为长期例外。
6. **零测试点名的 4 个 core 文件**——补测试,还是登记豁免在册。
7. **文档漂移三处**——`architecture.md` 的 HeroLedger 登记行、battle_orchestrator 行数、EC8 热重载门表述,是否现在就按 doc-design 落回(纯文档面,主区直改)。
---
## 七、取证口径备注
- 全程只读,未跑测试、未跑引擎、未跑闸(测试两入口铁规)。
- 3.1 表 A 类 18 处中,#1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #13 #14 #15 #17 逐条 `sed` 取原文核过;其余按同一 grep 口径判定。
- 「零测试点名」判定词 = 文件 basename 与 `class_name` 全称,检索面 `game/tests/**/*.gd`;被当作类型间接引用与有针对性断言已分开计数。
- `static_scan.sh` 扫描面结论来自脚本自身变量定义(`DIR_CORE` / `DIR_PRES` / `SCAN_DIRS`)与 S8 的 `S8_FILES` 显式清单,非推测。
@@ -1,126 +0,0 @@
# 工程改动报告——存量断言精简 C 档(2026-10-28)
**批性质**:工程改动(纯测试维护),代码面独立 worktree 批(`assert-slim-b2-20261028`)。验证项目覆盖逐条不变,断言静态站点收敛。
**输入合同**:待办清单「存量断言精简——验证内容不变下的断言收敛」余账②(C 档手写族改表驱动循环)+用户 2026-10-28 会话裁决「六组全批(推荐)」。
**对账基准**:`game/tests/验证集清单.md` 冻结册(基点 `b9260138`,1587 条静态调用点;本批只比对不改写)。
## 1. 判据线
沿用 A+B 批判据线:**同一读数面 + 同一输入集 + 谓词字面等价**(C 档是同一族的逐例手写断言改为表驱动循环,每旧例成为循环表的一行数据,标签串保留在数据里可 grep)。
与 A+B 的区别:A+B 是删并(旧站点消失、判据由承接者承担);C 档是**形态改造**——旧例全部保留为循环的一行,执行条数不变,只有静态 `check(` 调用点下降。失败定位由「独立断言行」改为「违例明细串」(表内每行仍输出自己的标签,附带期望/实际)。
## 2. 删并申报表(33 旧站点 → 7 新站点)
「旧序号」=冻结册序号;「旧站点」=冻结册行号(`file:line` 省略相同路径前缀)。六组逐一列出,处置列写明合并形态。
### C1 关卡生成参数负向族(11 → 1)· `game/tests/unit/data_config/level_gen_params_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1221 | `_test_negative:72` | 合法三行域校验零错误(夹具自证)〔fv2〕 | `:107` | 表行 `cases[0]`(夹具自证行保留在表首) |
| #1222 | `_test_negative:75` | L1 出口=2 被拒(出口 L1 恒 1 已锁)〔fv2〕 | `:107` | 表行 `cases[1]` |
| #1223 | `_test_negative:78` | 配比合计 0.5 被拒(四类合计 1 已锁)〔fv2〕 | `:107` | 表行 `cases[2]` |
| #1224 | `_test_negative:81` | 层深 5<6 被拒(层深下限已锁)〔fv2〕 | `:107` | 表行 `cases[3]` |
| #1225 | `_test_negative:85` | 层深增幅倒挂被拒(递增且增幅变大)〔fv2〕 | `:107` | 表行 `cases[4]` |
| #1226 | `_test_negative:88` | 出口 4 越域 [1,3] 被拒〔fv2〕 | `:107` | 表行 `cases[5]` |
| #1227 | `_test_negative:92` | 岔口去路数 (2,4) 被拒(恒 2/3 已锁)〔fv2〕 | `:107` | 表行 `cases[6]` |
| #1228 | `_test_negative:97` | 字段键集非 ROW_KEYS 闭集被拒〔fv2〕 | `:107` | 表行 `cases[7]` |
| #1229 | `_test_negative:100` | 键重复行被拒〔fv2〕 | `:107` | 表行 `cases[8]` |
| #1230 | `_test_negative:102` | 战斗保底 -1 被拒(≥0)〔fv2〕 | `:107` | 表行 `cases[9]` |
| #1231 | `_test_negative:105` | 空表域校验零错误(合法态)〔fv2〕 | `:107` | 表行 `cases[10]` |
新站点标签模板 `域校验:%s(期望%s,实际%s)〔fv2〕`:`%s` 即旧标签原文(表内逐字保留),`期望/实际` 附违例明细串。
### C2 RNG 黄金向量族(8 → 1)· `game/tests/unit/data_config/rng_stream_boot_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1239 | `_smoke_initialize:37` | 统一流向量 m=42 scope=0(=旧 board_gen scope0 连续性锚) | `:40` | `golden[0]`(循环标签补注 scope0 锚语义) |
| #1240 | `:38` | 统一流向量 m=42 scope=1 | `:40` | `golden[1]` |
| #1241 | `:39` | 统一流向量 m=42 scope=2 | `:40` | `golden[2]` |
| #1242 | `:40` | 统一流向量 m=42 scope=3 | `:40` | `golden[3]` |
| #1243 | `:41` | 统一流向量 m=42 scope=4 | `:40` | `golden[4]` |
| #1244 | `:42` | 统一流向量 m=42 scope=5 | `:40` | `golden[5]` |
| #1245 | `:43` | 统一流向量 m=42 scope=6 | `:40` | `golden[6]` |
| #1246 | `:44` | 统一流向量 m=42 scope=7 | `:40` | `golden[7]` |
黄金向量八值逐字不变;标签由逐行写死改为 `scope=%d` 循环变量,grep 锚 `统一流向量 m=42 scope=` 保留。
### C3a 奖励点头部禁词族(3 → 1)· `game/tests/unit/presentation/player_copy_sweep_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1383 | `_sweep_narrator:59` | 奖励点头部零「同权重」旧话(u3 清零)〔wa4〕 | `:60` | 禁词表行「同权重」 |
| #1384 | `:60` | 奖励点头部零「同上限」旧话(u3 清零)〔wa4〕 | `:60` | 禁词表行「同上限」 |
| #1385 | `:61` | 奖励点头部零「提升池」旧话(u3 清零)〔wa4〕 | `:60` | 禁词表行「提升池」 |
标签模板 `奖励点头部零「%s」旧话(u3 清零)〔wa4〕`,禁词作参数,与旧标签逐字同形。
### C3b 图例封印行禁词族(2 → 1)· 同文件
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1389 | `_sweep_legend:76` | 图例封印行零「插错」(u4 旗族遗留词干清零)〔wa5〕 | `:76` | 禁词表行「插错」 |
| #1390 | `:77` | 图例封印行零「挖雷」(u4 雷字清零)〔wa5〕 | `:76` | 禁词表行「挖雷」 |
标签模板 `图例封印行零「%s」〔wa5〕`;`seal_row` 取值表达式(`size > 2` 守卫)逐字保留在循环体内。
### C4 站桩蓄力置位四入参(4 → 1)· `game/tests/unit/presentation/standee_fx_sweep_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1407 | `_init:95` | 蓄力技能(heavy)置 `_intent_charge=true`〔wd2〕 | `:103` | 入参表行 `["damage","heavy",true,…]` |
| #1408 | `:97` | 非蓄力攻击技(goblin_slash)零置位〔wd2〕 | `:103` | 入参表行 `["damage","goblin_slash",false,…]` |
| #1409 | `:99` | 增益系零置位〔wd2〕 | `:103` | 入参表行 `["buff","goblin_rage",false,…]` |
| #1410 | `:101` | 普攻零表示路径复位 `_intent_charge`〔wd2〕 | `:103` | 入参表行 `["damage","basic",false,…]` |
判定式 `s1._intent_charge == 期望` 与四旧例同谓词;`set_pending` 入参与旧例逐项相同。
### C5 字体字形集三组(3 → 1)· `game/tests/unit/presentation/ui_font_asset_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1583 | `_init:18` | 中文字形在集(攻/雷/旗/封) | `:30` | 字形组「攻雷旗封」 |
| #1584 | `:20` | 中文字形在集(危——已开危险格字形换代前置,迭代36 u12/wa9) | `:30` | 字形组「危」 |
| #1585 | `:21` | ASCII/数字字形在集(A/0/%) | `:30` | 字形组「A0%」 |
判定由逐字 `has_char` 合取改为组内逐字扫描,标签附缺字明细(缺字集为空即绿)。
### C6 锻造工作间长剑/轻甲双行(2 → 1)· `game/tests/unit/presentation/forge_workbench_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1336 | `_run:63` | 长剑行:购买钮「锻造 20」(0 级位几何价)+选中钮「当前」禁用〔锻造工坊批 V13〕 | `:64` | 行表 `["sword","长剑"]` |
| #1337 | `:66` | 轻甲行:购买钮「锻造 20」+选中钮「当前」禁用〔锻造工坊批 V13〕 | `:64` | 行表 `["light","轻甲"]` |
**谓词取并集申报**:旧 #1336 含 `not buy_btn.disabled` 与 `select_btn != null` 两子句、#1337 缺这两子句。合并后统一用 #1336 全谓词(**只增不减**,轻甲行增补 `select_btn != null` 与 `not buy_btn.disabled` 两子句)——已在用户裁决时呈报并随「六组全批」批准。标签统一为 `%s行:…(0 级位几何价)…`,两种行名均保留可 grep。
## 3. 件核
| 项 | 改前 | 改后 | 判据 |
|---|---|---|---|
| 六套件静态站点 | 33 | 7 | 冻结册逐行比对 |
| 全清单静态条数(`--diff`) | 1587 | **1561** | `python tools/extract_checks.py --diff game/tests/验证集清单.md`:消失 33/新增 7,与上表逐条对应 |
| 运行期条数(六套件) | 17/13/22/18/5/12 | 17/13/22/18/5/12 | 合并前后同值;`ui_font 5<6`、`forge 12<13` 的静态差为失败分支站点(装载失败/字体缺失 `Kit.check(…, false)`),绿跑不执行,改前即如此 |
| 套件全量回归 | — | 65 套件全绿 | `unit.log`:`[run_all] all green (65 suites)` |
| 全码域闸 | — | all_green | 回执 commit `fd6cf968`:domain=code、dirty_game=false;01/02/10 绿;09 存量红指纹 `8467b4588a20` 零新增;00/03/04/05/11 影响面选闸 SKIP(diff 全在 `game/tests/` 测量面) |
| 合并 | — | `87d241c1` | 合并后主区 `--editor --quit` 退出 0;`python tools/run_smoke.py` PASS(15 项检查,三硬闸全过) |
运行期条数不变+每个旧标签都在循环数据里逐字保留=**验证项目覆盖逐条不变**的直接证据;静态条数降 26=本批唯一预期变化。
## 4. 余账与去向
| 余账 | 处置 |
|---|---|
| 类别②(内部面断言被入口严格承载) | A+B 批实测 0 条,不再找 |
| C 档候选 | 六组全数执行,C 档本波清零;无剩余候选 |
| D 档四处(theme:215/217 × type_glow:148/150、level_shop_economy 现行图 vs 扩面段) | 2026-10-28 用户裁全保留,不属精简账 |
| 恒真与名实不符断言五条 | 判据缺陷另账 → 待办清单新登记「恒真与名实不符断言五条——判据缺陷修正」(`feat_vouchers:51`/`treasure_effects:263`/`level_shop_economy:593`/`:696`/`system_contracts:66-67`) |
| `Kit.check_quiet` 静态站点 407 个在提取器口径外 | → 待办清单新登记「断言提取器改版——覆盖 Kit.check_quiet 静默通道」 |
## 5. 纪律自检
- 无删断言、无放宽阈值、无缩小扫描面、无下层直调替换:33 条判据全部保留为循环表行,输入集与判定式逐条对应(C6 为谓词并集,已申报)。
- 无变异法、无撤销类取证;绿由保持断言复跑取得(整套件全绿)。
- 冻结册未动一字,`--diff` 差异面与申报表逐条对齐。
@@ -1,331 +0,0 @@
# 提案:弃图刷新系专长流派(双基座七件)——预案 v0.4(2026-10-30)
> **性质**:待办清单条目「封锁-蓄力」combo 设计素材的设计提案与 GDD 修改清单。双基座七件结构已经用户确认,修改清单已经用户审核并落入四份 GDD;本文按用户要求保留,供后续写需求卡片参考。代码与数值表尚未实现。
>
> **需求来源**:用户 2026-10-30 明确设计方向——流派先有基座专长,从资源来源、动作成本或结算规则上改变基础行为;后续专长围绕新行为提供输出、生存与资源收益。只有刷新后的数值增长,不足以形成流派。
>
> **结构裁定**:用户指定设计 **2 件基座+2 件输出+2 件生存+1 件资源**,共 7 件;两件基座允许同时持有并叠加;弃图回收按旧图探索进度发清图奖励,不采用固定发一份。本提案结构待裁点已清零。
>
> **结论行**:「掘进储能」让挖格产 RAP,解决刷新资源从哪里来;「折价换图」让刷新 RAP 成本减半,解决同样资源能刷几次。两件基座叠加后形成完整换图引擎。后续五件让刷新分别产生群体直伤、下次攻击爆发、护盾、回血与按探索进度回收的清图奖励。拿到任一基座后,刷新不再只是坏图逃生;配上后续专长,玩家会为了输出、生存或资源主动规划换图。
---
## 0. 七件总表
> 名称均为代号;数值与稀有度均为 provisional,不在本预案定稿。
| 层 | 代号 | 效果候选 | 它改变的决策 |
|---|---|---|---|
| **基座** | **掘进储能** | 每次付费挖格后额外获得 RAP | 想刷新不再必须少挖停手;持续挖图也能蓄能 |
| **基座** | **折价换图** | 弃图刷新的 RAP 成本减半 | 同样的储备支持更多刷新;部分挖格+部分停手也能维持换图 |
| **输出** | **弃图轰击** | 成功刷新后,对全体存活敌人造成伤害 | 刷新本身成为进攻动作 |
| **输出** | **换图蓄势** | 每次刷新攒层;下一次攻击附加全部层数后清零 | 连续换图可以蓄成一次单点爆发 |
| **生存** | **换图庇护** | 成功刷新后获得护盾 | 刷新可以主动用来抵挡后续伤害 |
| **生存** | **弃图休整** | 成功刷新后回复生命 | 玩家可以放弃当前图,换取一次恢复机会 |
| **资源** | **弃图回收** | 成功刷新后,按旧图探索进度回收清图奖励 | 不必挖完最后几格;可以先投入,再主动弃图变现 |
**结构关系**:基座负责让主动刷新可持续;输出、生存、资源五件负责让主动刷新有明确目的。后续专长不需要设置「必须先拿基座」的硬前置,它们的联动由实际刷新频率自然形成。
---
## 1. 通用设计思想:先改变行为,再给新行为收益
这一节同时作为后续修订 `奇物.md`「专长·设计原则」的建议文本。现在只记录在提案中,尚未写入 GDD。
### 1.1 基座专长的定义
**基座专长=让某个原本偶尔使用、主要用于止损的动作,变成可以持续使用的主要打法的专长。**
它至少改变下列一项:
1. **资源来源**:原来主要靠停手获得 RAP,现在挖格也能获得 RAP。
2. **动作成本或频率**:原来一整轮停手才能刷新一次,现在同样储备可以支持更多刷新。
3. **动作结算**:原来危险格只伤害玩家,现在也能重伤敌人,使主动踩雷成为进攻手段。
4. **动作关系**:原来挖图与弃图争夺资源,现在挖图为弃图蓄能,两个动作组成循环。
成本减半虽然形式上仍是数值变化,但如果它让刷新从偶尔可用变成持续可用,也满足基座要求。**判断标准不是改了多少规则文本,而是玩家的实际操作是否改变。**
### 1.2 基座与后续专长的分工
| 层 | 职责 | 不合格形态 |
|---|---|---|
| **基座专长** | 建立新的资源循环或动作规则,让核心动作能持续使用 | 只在原动作后加少量攻击、护甲或资源,动作使用时机不变 |
| **后续专长** | 让核心动作产生输出、生存或资源收益,扩展新打法 | 不依赖新行为、放进任何构筑都同样有效的泛用数值增长 |
后续专长仍可采用「当 X 时 Y」的触发器句式,也可以各自独立生效;不另设集件标签。它们与基座的联系来自实际玩法:没有基座时,刷新少、收益兑现慢;有基座后,同一效果成为主循环的一部分。
### 1.3 行为改变检验
新流派或新基座专长过准入时,必须回答三个问题:
1. **拿之前怎么玩,拿之后怎么玩?** 如果最优操作与决策时机基本不变,只是结算数字变大,就不是基座。
2. **新行为为什么能持续?** 必须写清资源从哪里来、多久能做一次、什么限制防止无限循环。
3. **后续专长为什么属于这里?** 它应当兑现或强化基座建立的行为,而不是替代基座。
以自爆流为例:没有相关专长时,玩家应躲开危险格;拿到能减少自身雷伤、同时让危险格重伤敌人的基座后,玩家会主动寻找危险格。后续专长再提供踩雷后的回复、护甲或追加伤害。**「本来不该点」变成「现在值得主动点」,才是行为改变。**
---
## 2. 两件基座:分别改资源来源与动作价格
### 2.1 掘进储能
**效果候选**:每次付费挖格后,额外获得一定 RAP。安全格与危险格都按付费挖格计,不另判玩家意图。
**基础循环**:
```
持续挖格并获得格面收益 ──同时积攒 RAP──▶ 主动弃图刷新
▲ │
└──────── 新图继续挖格 ◀───────────────┘
```
**行为改变**:
- 玩家可以一边挖图拿收益,一边准备下一次刷新,不再必须停手。
- 每轮 AP 有限,因此新增 RAP 也有限;它扩大刷新机会,不提供无限刷新。
- 单拿时,坏图更容易被换掉;配上任意后续专长得益后,主动刷新才有完整目的。
### 2.2 折价换图
**效果候选**:弃图刷新的 RAP 成本减半。
**基础循环**:
```
部分挖格/部分停手留 AP → 同一份 RAP 支持更多次刷新 → 更频繁兑现后续专长
```
**行为改变**:
- 玩家不再需要为一次刷新攒下完整一轮的 AP;挖图与留 AP 的比例出现更多选择。
- 与掘进储能不同,它保留停手攒 RAP 的旧来源,但降低主动换图所需的投资。
- 单拿时,刷新从昂贵逃生变成常用工具;配上后续专长后,刷新次数直接转化为输出、生存或资源。
### 2.3 两件基座的组合
两件基座作用在不同位置:一个改 RAP 来源,一个改 RAP 价格。**用户已裁定允许同时持有并叠加**,组合后形成完整基座:
```
挖格产 RAP + 刷新半价 → 每轮更容易达到刷新次数上限 → 后续五件兑现收益
```
叠加后的硬边界仍是每轮刷新次数上限、有限 AP、旧图进度损失与战斗结束 RAP 清零。若实测过强,优先调产能量与半价数值,不增加「两件基座互斥」的特殊规则。
---
## 3. 两件输出:即时群伤与延迟爆发
### 3.1 弃图轰击
**效果候选**:每次弃图刷新成功后,对全体存活敌人造成伤害。
**玩法职责**:提供即时、稳定的群体输出。玩家会在「继续挖旧图」与「现在刷新打一轮伤害」之间做选择。
**与其他件的关系**:
- 掘进储能支持持续挖、持续刷、持续轰击。
- 折价换图让同一份 RAP 兑现更多次轰击。
- 与换图蓄势组合时,一次刷新同时提供当场群伤与下次攻击的蓄力层。
**边界**:只有成功刷新才触发;刷新被拒绝不触发;每轮刷新上限同时限制伤害次数。
### 3.2 换图蓄势
**效果候选**:每次成功刷新获得 1 层蓄势,层数有上限;下一次攻击附加全部层数对应的伤害,然后清零。
**玩法职责**:提供延迟、集中的单点爆发。与弃图轰击的即时群伤不同,它鼓励连续换图后寻找合适的攻击窗口。
**与其他件的关系**:
- 双基座组合更容易连续刷新,为一次重攻蓄满层数。
- 与现有攻击强化专长可以组合,但不另设专属加成。
- 层数只在当前战斗内保留,战斗结束清零,避免跨战囤积。
**边界**:层数必须有上限;一次攻击消费全部层数,不能同一批层数重复附加到多次攻击。
---
## 4. 两件生存:当场护盾与直接回血
### 4.1 换图庇护
**效果候选**:每次成功刷新后获得护盾,按现有护盾规则抵挡伤害与消耗。
**玩法职责**:提供面向未来伤害的保护。玩家可以预判敌方压力,主动弃图换护盾,而不是只在棋盘不好时刷新。
**与弃图休整的区别**:护盾适合抵挡即将发生的伤害;休整用于修复已经失去的生命。一件偏预防,一件偏补救。
### 4.2 弃图休整
**效果候选**:每次成功刷新后回复一定生命。
**玩法职责**:把旧图与 RAP 转成恢复机会。生命偏低时,玩家可以放弃当前图的后续收益,换取继续冒险的空间。
**边界**:
- 回复不能超过生命上限,不转化为额外生命。
- 刷新成本与每轮次数上限不变,不能无代价连续回血。
- 强度对账时须同时计算放弃旧图进度的损失,不能只看单次回血量。
两件生存专长可以同时持有,让刷新兼具保护与恢复;但它们不替代输出件,玩家仍需通过其他攻击手段结束战斗。
---
## 5. 一件资源:把旧图投入换成清图奖励
### 5.1 弃图回收
**已定效果**:成功刷新后,按旧图本次探索进度回收清图奖励;探索越多,回收越完整。具体档位、比例与奖励内容归后续调参。
**玩法职责**:建立与正常清图不同的资源兑现路线:
```
先挖图投入 AP、积累探索进度 → 不再挖最后几格 → 主动刷新 → 按进度回收清图奖励
```
**行为改变**:旧图不再只有「清完」与「全部放弃」两个结果。玩家可以比较继续清图的时间与风险,选择提前弃图变现。
**为什么不固定发一份**:空图刷新也拿完整收益,会让挖图投入失去意义,并把资源流退化成机械地点刷新。按进度回收要求玩家先探索、再决定何时兑现,保留先投入再弃图的循环,也保留接近清图时的取舍。用户已据此裁定采用按进度回收。
**边界**:同一旧图只能在成功放弃时结算一次;新图没有继承旧图的可回收进度;刷新被拒绝时不发奖励。
---
## 6. 七件如何组成不同打法
| 构筑方向 | 组合 | 主要操作 |
|---|---|---|
| **持续轰击** | 掘进储能+弃图轰击 | 边挖边蓄 RAP,够价就刷新,刷新持续削全体敌人 |
| **蓄力爆发** | 折价换图+换图蓄势 | 用较低成本连续换图,把多次刷新汇成一次重攻 |
| **双基座输出** | 两基座+两件输出 | 高频刷新同时提供群伤与攻击层数,形成完整输出流 |
| **换图续命** | 任一基座+换图庇护+弃图休整 | 在生命与敌方压力下主动换图,用护盾与回血延长战斗 |
| **弃图采收** | 掘进储能+弃图回收 | 挖到一定进度后主动放弃,连续从新图获取探索收益与回收奖励 |
| **七件完整形态** | 全部七件 | 挖格蓄能、低价刷新,每次换图同时兑现输出、生存与资源;仍受每轮次数上限约束 |
**最小成立单位**是任一基座+任一符合当前需要的后续专长。七件不要求一次拿齐,也不设集件奖励。
---
## 7. 防退化与共同边界
| 检查 | 约束 |
|---|---|
| **是否无限自供能** | 新 RAP 来自有限 AP 的付费挖格;刷新本身不产 RAP;每轮刷新上限与战斗结束清零不变。 |
| **无专长基线是否改变** | 不改变。两件基座是专长提供的新规则,不直接修改无专长时的全局经济。 |
| **双基座是否失控** | 叠加后仍受每轮次数上限;数值对账以每轮实际可达到的刷新次数为准。 |
| **输出是否重复消费** | 轰击每次成功刷新结算一次;蓄势层数在一次攻击后全部清零。 |
| **生存是否无代价** | 护盾与回血都要求成功刷新;玩家同时付出 RAP、旧图进度与刷新名额。 |
| **正常清图是否被删除** | 不删除。弃图回收不应让同等投入的收益无条件高于正常清图。 |
| **空图是否拿满资源** | 推荐案不允许;回收按旧图探索进度计算。 |
| **信息红线** | 七件均不增加揭示效果,不碰危险格信息规则。 |
若实装后玩家仍然只在坏图时刷新,说明基座或后续收益没有真正成立;不能只继续增加数值来掩饰行为没有改变。
---
## 8. 旧案处置
| 旧内容 | 本案处置 |
|---|---|
| 抬高刷新成本 | 删除;与主动刷新方向相反 |
| 刷新全板开 | 不进入本案;不是形成刷新流的必要条件 |
| 静气:不刷新给减伤 | 从本流派删除;奖励相反行为 |
| 换图回气 | 删除;只返还部分成本,不建立新循环 |
| 换图开眼/明察 | 删除;属于信息附属收益 |
| 换图退守 | 由换图庇护承接生存方向 |
| 换图成势 | 保留其延迟爆发思路,重写为换图蓄势;另增即时群伤弃图轰击 |
| 单基座二选一 | 改为双基座并存;用户已裁定允许同时持有并叠加 |
---
## 9. GDD 修改清单(用户已审核,已落文)
以下清单已经用户审核并落入对应 GDD;现行规则以各 GDD 为准,本节保留供后续写卡核对改动面。采用**刷新记录事实、本轮结算兑付效果**的衔接方案:不新增第六个结算段,不让刷新当场结束一轮,也不改状态只能在轮末授予的现行规则。
### 9.1 `design/gdd/systems/冒险循环/奇物.md`
- 在「专长·设计原则」加入基座专长定义:改变资源来源、动作成本/频率、动作结算或动作关系,使原本止损用的动作成为持续主打法。
- 加入三项行为改变检验:拿前后操作是否不同;新行为如何持续;后续专长为何依附该行为。泛用数值增长不能冒充基座。
- 事件族由五族扩为六族,新增设计侧分组「换图系」;族名仍不上玩家卡面,不设集件奖励或强制先拿基座。
- 登记双基座七件:掘进储能、折价换图、弃图轰击、换图蓄势、换图庇护、弃图休整、弃图回收;写清两件基座允许叠加。
- 写清七件职责:两件输出分别为群体直伤与下次攻击爆发;两件生存分别为护盾与回血;资源件按旧图探索进度回收清图奖励。
- 同步专长总数、族数与相关验收标准;落文已按当轮 `feats.tres` 实数复核为 30 行,换图系七件标为待实现,不计入现役行数。
- 落文版本=v1.2。
**不自动改自爆流**:现行「净血账期望必须为负、防见雷就踩」与用户描述的理想自爆流方向不同,需另一次明确裁决,不随换图系一并改掉。
### 9.2 `design/gdd/systems/排雷循环/行动经济.md`
- 在停手转存之外登记专长提供的第二 RAP 来源:持有掘进储能时,每次付费挖格额外获得 RAP;安全格与危险格同口径,无专长时不变。
- 登记折价换图:持有专长时刷新 RAP 成本减半;只改专长持有者的价格,不改全局表基值。
- 写清双基座叠加:挖格产 RAP 与刷新半价同时生效;结算顺序、拒绝刷新不产效果要有验收口径。
- 保留现有限制:刷新不终结本轮、不重置 AP;每轮次数上限不变;RAP 战斗结束清零;刷新本身不产 RAP。
- 增补对应验收标准。
### 9.3 `design/gdd/systems/战斗循环/战斗结算.md`
- 新增「刷新成功」事实:只有成功换图才产生;旧图退役前保存本次探索进度,供弃图回收使用;刷新被拒绝不产生事实。
- **推荐时序**:刷新只记录事实,不当场插入一轮结算。弃图轰击在本轮战斗段经伤害控制器兑付;换图庇护与弃图休整在本轮授予段兑付;弃图回收随本轮授予段结算。五段顺序不变。
- 换图蓄势在刷新成功时累计层数,下一次主角攻击附加并清零;层数上限与战斗结束清零写入规则。
- 群伤若击杀最后敌人,由本轮既有判定段判胜,避免新增挖掘期即时胜利分支。
- 专长效果写扩展现行:群伤走伤害单据,护盾/回血/回收各留可归因的结算行;来源指向刷新专长与被弃旧图,不伪造格子来源。
- 「刷新图不发清图奖励」保持不动;弃图回收兑的是**旧图**按进度回收的奖励,不是把新图判成已清图。
- 增补对应验收标准。
### 9.4 `design/gdd/systems/战斗循环/地图战斗资源.md`
- 扩展清图奖励对象的使用口径:正常清图仍发完整对象;持有弃图回收时,旧图可按探索进度回收同一奖励对象的一部分。
- 建议把「探索进度」定义为:`(旧图已开安全格数 − 装板初始已开安全格数)÷ 装板初始未开安全格数`,限制在 0~1。连锁展开真实推进探索,计入;初始封锁格与危险格不计入。
- 写清弃图回收只发基础奖励的进度份额:不触发完美封印加倍,不产生清图事件,不把旧图判为清图。
- 同一旧图只在成功退役时回收一次;新图不继承旧图进度;空投入刷新不能拿完整奖励。
- 具体比例、档位、取整与奖励数值归调参;已增补对应验收标准,落文版本=v1.2。
### 9.5 按推荐时序不需要修改的文档
- `增益状态.md`:护盾仍在轮末授予段进入状态池,继续遵守「授予只在结算时发生」与「跨轮跨图延续」,不需要为刷新开即时授予例外。
- `02-系统清单.md`:不新增文档,只改现有系统文档,无需改根索引。
- `术语表.md`:七件名称目前均为代号,正式命名前不登记新术语。
### 9.6 本 GDD 批明确不改
- 不改 `feats.tres`、其他数值表、代码、测试或架构实现现状;这些归后续写卡与实现批。
- 不改自爆流现行条款,不顺带重做其他专长族。
- 不采用「刷新瞬间结算全部效果」方案。若用户要求即时生效,需另增 `增益状态.md` 与战斗胜利时序的修改项,再重新审核。
**两处衔接已经用户审核**:①后续效果统一在本轮结算兑付,而非刷新瞬间兑付;②弃图回收的探索进度按安全格实际推进计算,连锁计入、初始格与危险格不计。
---
## 10. 后续验收必须证明的行为
1. **掘进储能**:从真实挖格入口进入,持有专长时 RAP 按声明增加;无专长时挖格不新增 RAP。
2. **折价换图**:真实刷新入口按修改后的成本扣 RAP;AP、轮次与其他刷新规则不变。
3. **基座组合**:两件同时持有时,挖格产 RAP 与刷新半价都生效;验证成本与产 RAP 顺序,任何组合都不能超过每轮刷新上限。
4. **弃图轰击**:成功换图后全体存活敌人受伤;刷新被拒绝时不触发。
5. **换图蓄势**:多次刷新正确叠层并受上限约束;下一次攻击附加全部层数后清零;战斗结束清空。
6. **换图庇护/弃图休整**:只在成功刷新后给护盾或回血;回复不超过生命上限。
7. **弃图回收**:读取被放弃旧图的探索进度;不同进度按规则得到不同奖励;同一旧图不能重复领取。
8. **基线零扰动**:没有这些专长时,RAP 来源、刷新成本、清图奖励与刷新结果保持现行规则。
9. **真人体验判断**:拿到基座与后续专长后,玩家会主动考虑刷新,而不是仍只在坏图时使用。此项登记真人验证,不阻塞实现验收。
---
## 11. 已定结构:零待裁点
| 决策 | 用户裁定 | 落地口径 |
|---|---|---|
| **D1 双基座关系** | 允许同时持有并叠加 | 掘进储能改 RAP 来源,折价换图改 RAP 价格;每轮刷新上限继续限频,不设互斥规则。 |
| **D2 弃图回收** | 按旧图探索进度回收 | 必须先探索再兑现;不采用每次刷新固定发一份,避免空图刷新拿完整收益、退化成机械点刷新。 |
双基座七件结构由此闭合。具体数值与稀有度不属于结构待裁点,统一归后续调参。
---
## 12. 下一步
1. 用户发令后进入写需求卡片段,以本提案与四份 GDD 为输入,按第 10 节建立真实入口断言与行为验收。
2. 迭代编排后实现;具体数值与稀有度归后续调参。
3. 自爆流现行条款不在本批改动面内;若以后要按同一基座思想重构,另行登记与裁决。
本提案保留为写卡参考;换图系七件尚未实装,未跑任何游戏测试。
@@ -1,7 +1,6 @@
# 提案:营地场景图美术生成方案(渠道 · 分层 · 工作流 · 样张验收判据)
> 性质:设计讨论段过程稿(2026-10-22 美术方案讨论产出)。**只讨论生成方案,不出图、不写需求卡片、不改 `game/`、不编迭代**——美术线是用户自立批(铁律 2),方向稿与「过/不过」的目检结论由作者出。
> **现行口径指针(2026-10-30 作者裁定)**:分层张数与围墙归属已有新裁定——营地场景图 **10 张** = 背景层 `scene_camp_bg`(z0,不透明全幅:天空+远山+整片地面+门洞小路)+ 围墙层 `scene_camp_wall`(z1,透明底:横贯密排木桩墙+两根门柱,门洞留透明缺口)+ 八个物件层(布告板 z2/门扇 z3/木桩·锻炉·帐篷 z4/宝箱·笔记本 z5/篝火 z6)。**围墙已从背景层拆出、独立成层**;门洞小路画在背景层、由围墙门洞透明缺口透出。**现行坐标、提示词、验收与消费名的单一事实源 = `design/art/camp-scene-prompts.md` §2/§4、`art-bible.md` §6.2、`asset-path-map.md` §3、`art-assets-checklist.md` §3.5、`camp.md`**;本稿 §2.2 层表、§2.5 坐标草案、§4 消费常量表、§5 验收、§12 分层元素表所记的「8 张/背景层含围墙/门柱门洞画在背景层」为 2026-10-22 讨论当时口径,其推理留痕保留不改,执行以上列五份现行文档为准。
> 输入事实源:`reports/需求卡片-营地场景图新建批-2026-10-18.md`(卡④ v2.3,已退回美术设计段)、`reports/需求澄清-营地交互重做-2026-10-18.md`(目标体验原话/纵深稿/10 条交互裁决)、`design/art/art-bible.md` §6.2/§7.5/§4.1/§4.2/§8.3、`design/art/asset-path-map.md`、`design/art/art-prompts.md` §0/§4bis、`design/art/remote-gen-prompts.md`。
> 待作者带的输入:①参照物(作者原话「后续找一下」)②渠道一句话裁定 ③每轮方向稿「过/不过」的目检结论。
@@ -1,46 +0,0 @@
# 盘点:验证与清单体系(阶段 0)
> 2026-10-28 · 服务待办清单「验证与清单体系整改——四方向分阶段推进」条
> 性质:过程件(吸收即死)——结论吸收进待办条目与阶段 1 设计文档后删除。
## 1. 结论
1. **验证集清单零机器闸依赖**:`tools/run_all_gates.sh`、hooks、五个项目技能均不引用它;规范性锚点只有 `design/engineering/test-conventions.md` §8.3 一处。退役不需要改闸、改钩子、改技能。
2. **测试名尾缀不是全量追溯键**:47 个有断言的文件中 19 个零尾缀(含三条 35 条以上断言的大套件)。能力清单只能按「能力→代表用例」建,不能要求逐断言尾缀。
3. **对照表与待办清单都有机器闸**:前者的列结构、后者的条目格式均由 `tools/pipeline_gate.py` 强制;这两处瘦身要动闸,属阶段 3/4,不是阶段 1 的顺手活。
4. **能力清单已有现成形态**:`game/tests/README.md` 的「集成旅程清单」5 行,扩面成本低;`repo_structure_audit.py` 不限制 `game/tests/` 下的 .md 文件名。
## 2. 依赖面盘点
| 件 | 消费方 | 机器闸依赖 | 退役/改动成本 |
|---|---|---|---|
| `game/tests/验证集清单.md` | test-conventions §8.3、待办清单 1 条、2 份过程报告、`tools/extract_checks.py` | 无 | 低:改 1 条规约+删文件 |
| `tools/extract_checks.py` | 无自动调用;手工生成/对账 | 无 | 可保留为审计工具,或随清单退役 |
| 改动—验收对照表 | req-card 技能、iter 技能、`pipeline_gate.check_ledger`(强制 5 列) | 有 | 中:规约+两技能+闸三处 |
| 待办清单 | `pipeline_gate`(格式全套)、`doc_ref_audit`、`doc_term_audit`、`repo_structure_audit`、各技能 | 有 | 低:正文压缩不触机检;新增「≤5 行」规则才动闸 |
| 独立验收审计 | iter 技能、req-card 技能、`pipeline_gate`(只查「验收审计」字样) | 有(字样) | 中:改抽样规则要动两技能+规约 |
| 集成旅程清单 | 无机器消费 | 无 | 低:直接扩面 |
## 3. 覆盖数据
- `game/tests/**/*.gd` 共 70 个文件;静态 `check(` 粗口径 1607 处(含 `test_kit` 定义行与 `smoke_boot` 等提取器口径外宿主),清单口径 1587 处。
- 标签尾缀〔…〕756 个(上限口径,一条标签一个)。
- 有断言的文件 47 个:28 个有尾缀,19 个零尾缀。
- 零尾缀大套件:`seal_polarity` 69、`variant_placement` 42、`roll_multiplier` 35、`cross_system_flow` 22、`damage_math` 21、`skill_unlock` 20、`type_glow` 20、`minesweeper_rules` 16、`system_contracts` 15。
- 静默收集通道 `check_quiet` 约 407 站点在提取口径外(既有备案,test-conventions §5 工具局限)。
## 4. 对四阶段的影响
| 阶段 | 盘点结论对它的约束 |
|---|---|
| 1 换尺子(A) | 先扩「集成旅程清单」到核心域;每行=能力|真实入口|代表用例|最后验证批次|缺口。退役验证集清单;`extract_checks.py` 去留在设计讨论裁 |
| 2 按新尺子精简(B) | 删并对象改为能力项;每项能力保留至少一个代表用例;不要求逐断言尾缀 |
| 3 调验收强度(C) | 对照表列与审计抽样一起改,触 `pipeline_gate`、req-card、iter 三处;不要与阶段 1 同批 |
| 4 文档减肥(D) | 待办正文压缩可直接做;「≤5 行」入闸需改 `pipeline_gate` |
## 5. 未决点(阶段 1 设计讨论)
1. 能力清单落点:`game/tests/README.md` 扩面,还是独立文件?
2. 能力项粒度与覆盖面:按哪些域切、预计多少行?
3. `tools/extract_checks.py` 去留:随清单退役,还是保留为审计工具?
4. 验证集清单退役时点:立即冻结,还是阶段 1 设计定稿后一并退役?
@@ -0,0 +1,322 @@
# 设计——单格绘制状态件拆分(board_cell_unit 类变量键真实清偿)
设计讨论段过程稿 v1.0——待主会话汇总裁决。
结论行:推荐「绘制层域两件案」——状态件按 _draw 既有的层域轴拆成两个独立文件的绘制域件(格面基座层件 12 成员/格面标识与叠加层件 13 成员),几何与微光子件面留宿主(10 成员),绘制命令随入参同件迁移,宿主 _draw 退为六步时序器;三文件各自类变量 10/12/13,全部低于阈值 15 且不依赖任何闸机口径漏洞。
---
## 1. 现状与两项关键发现
### 1.1 键账现状
- 涉案键:`game/presentation/views/board_cell_unit.gd|quality/max-class-variables = 30`(tools/gdstyle_baseline.txt,2026-10-29 重锚版)。阈值 15(gdstyle 默认值,tools/gdstyle.toml 不设数值阈值)。
- 源码实查(2026-10-29):全文件 33 个类级 var 声明=宿主 6+inner 状态件 `_CellPaintState` 27。闸实测 30(本会话对单文件实跑 gdstyle v0.4.0 复核)。
- 文件头注在案:状态件职责语句「单格绘制输入集」;探针直读冻结成员(glyph/fog_state/plate/glyph_color)与 `_glow_layer` 住宿主。
- 迭代 41 已把 `_draw` 拆成约 20 个分层私有函数——函数级键已清零,本设计只解决类变量 30 这一个键。
### 1.2 发现一:gdstyle 对 inner class 的计数不是均匀展平,是「首个 setter 截断」
主会话给的事实是「inner class 成员按宿主展平计数」。本会话实测修正:截断点在 inner class 内**第一个带 setter 的属性**。
| 文件 | 声明数(宿主+inner) | 闸实测 | 归因 |
|---|---|---|---|
| board_cell_unit.gd | 6+27=33 | 30 | 宿主 6+inner 第一个 setter(edge,30–35 行)之后的 24 个=30;setter 声明本身及其之前的 3 个(_owner/cell/edge)留在 inner 类一侧不进宿主账 |
| standee_body.gd | 6+25=31 | 19 | 宿主 6+inner 第一个 setter(_intent_scale,37–40 行)之后的 13 个=19 |
| board_view.gd(无 inner class) | 32 | 32 | 平面文件=声明数逐一全计 |
| main.gd(无 inner class) | 49 | 49 | 同上 |
四点全部精确吻合。推论三条:
1. 现行登记值 30 是截断口径的**欠估**——若 gdstyle 升级修掉该解析行为,同文件键跳到 33=恶化红。任何「调整 inner class 内成员顺序/把 setter 挪位置」的降数手法都是在利用解析缺陷,工具一升级即翻车,且属「为迎合机检保留无效形态」,禁。
2. 主会话「任何 inner class 重新分组都降不了数」的结论在截断口径下依然成立(重排不改总量级),且存在更深的坑——重排确实能改数(把首个 setter 挪到类尾=宿主账只剩 6),但那正是第 1 条禁的手法。
3. **唯一稳健出路=拆成独立文件**:平面文件口径=声明数逐一全计(board_view/main 两点实证),预测值即实数,不依赖闸机任何解析行为。
### 1.3 发现二:ui_input_probe 疑似潜伏红(迭代 41 合并遗留,须 T0 先证)
ui_input_probe.gd 有四处**绕过 st 直接读格件成员**的断言:case① `u1.hl_visible`/`u1.hl_color`(234–235 行)、case② `.hl_color`(243–244 行)、case③ `.dot_visible`(250 行)、case⑭ `.mutation_mark`(374–379 行)。这四个成员迭代 41 已随状态件迁入 `_CellPaintState`,而 GDScript 外部实例读不到 inner class 的成员(取索引=运行时脚本错误)。
时间线:探针末次全绿 2026-10-28 05:03(game/tests/.gates/probe_full.log)早于状态件合入 2026-10-29 01:37(三文件同秒 mtime=合并检出);基线注记「迭代 41 两卡特许直接合回未重跑代码域闸」。合入后探针未跑过,潜伏红未经证实也未经证伪。同批 standee(u4)作者对同类问题做了宿主侧出口(fx_sweep_counters 方法返回计数器字典),u3 未处理这四处直读。
处置(本设计的立场):**无论潜伏红真假,本设计的拆分都把探针直读面显式化**——hl_color/hl_visible/dot_visible/mutation_mark 落入格面标识与叠加层件,探针四处断言改址 `u.face.*`(对照表申报的探针改址,断言条件与期望值零变化);四个头注冻结成员(glyph/fog_state/plate/glyph_color)留宿主不动。T0 用一次 ui_input_probe 实跑证实或证伪潜伏红,结果记实现报告。
### 1.4 探针/测试直读面全清单(改址与保留的依据)
| 消费方 | 读什么 | 现路径 | 设计后路径 |
|---|---|---|---|
| ui_input_probe case④b⑪ | glyph | u.glyph | u.glyph(宿主冻结,不变) |
| ui_input_probe case⑮ | fog_state | u.fog_state | u.fog_state(宿主冻结,不变) |
| player_copy_sweep_test wa8 | plate/glyph_color | u.plate/u.glyph_color | 不变(宿主冻结) |
| ui_input_probe case①②③⑭ | hl_visible/hl_color/dot_visible/mutation_mark | u.X(已断——潜伏红) | u.face.X(探针改址四处) |
| board_view.cell_rect_probe:891 | edge | u.st.edge | u.edge(board_view 内部改址) |
| board_shot_probe/type_glow_shot_probe | 格矩形 | 经 cell_rect_probe | 不变(间接) |
其余状态件成员全仓零外部读方(本会话 grep 全量复核)。
---
## 2. 现状成员全列(33 个)
写入方全部在 board_view.gd(唯一写方文件);行号为 2026-10-29 实查,实现批 T0 重核。setter→重绘语义列=该成员 setter 的现行副作用(无 setter=普通赋值不重绘)。
### 2.1 宿主 6 个
| 成员 | 类型 | 重绘语义 | 写入方 | 读取方 | 绘制消费层 |
|---|---|---|---|---|---|
| st | _CellPaintState 聚合 | — | 宿主 _init:147 | 全部绘制函数经 st.X | 载体 |
| glyph | String | setter→queue_redraw | _paint_cell:667 | _draw_glyph_layer:200/205、_draw_fog_memory:344 | 字形层+迷雾记忆态(探针冻结) |
| glyph_color | Color(UiKit.INK) | setter→queue_redraw | _paint_cell:668/674 | _draw_glyph_layer:205 | 字形层(探针冻结) |
| plate | bool | 无 setter | _paint_cell:675 | **绘制侧零消费**(_draw_digit_plate 由 glyph 非空无条件触发,plate 不参与判据);读方仅 player_copy_sweep_test:138 | 无(探针冻结成员,禁删禁接线——接线即行为变化) |
| fog_state | int | 守卫 setter→queue_redraw | _paint_cell:734/736 | _draw_fog_layer:247 | 迷雾层(探针冻结) |
| _glow_layer | Node2D 子件 | — | _sync_glow_layer:407–412 | edge/badge_glyph 两 setter、_sync_glow_layer | 微光子件(头注在案住宿主) |
### 2.2 状态件 27 个
| 成员 | 类型 | 重绘语义 | 写入方(board_view) | 读取方 | 绘制消费层 |
|---|---|---|---|---|---|
| _owner | Node2D 回引 | — | 宿主 _init:147 | 各 setter 内部 | — |
| cell | Vector2i | 无 setter | rebuild:250 | **全仓零读方** | 无(诊断观测面,待裁点 3) |
| edge | float | setter→glow setup+queue_redraw | set_cell_area:204、rebuild:251;读 _paint_cell:704、cell_rect_probe:891 | 全部层 | 全层(rect 尺寸基准) |
| bg_sb | StyleBoxFlat | 无 setter | _paint_cell:666 | _draw_base_layer:174、_draw_contact_or_fallback:187、_draw_bevel:314 | 基座层 |
| contact_bands | Array[int] | 无 setter | _apply_contact_bands:804 | _draw_contact_bands:271 | 接触带 |
| contact_corners | Array[int] | 无 setter | :805 | :279 | 接触带 |
| contact_col | Color | 无 setter | _process 呼吸:780/787、_apply_contact_bands:810/816 | _draw_contact_or_fallback:183、_draw_contact_bands:278、_draw_contact_corner:293–305 | 接触带 |
| outline_sides | Array[int] | 无 setter | _paint_cell:676(assign)/:700(append) | _draw_base_layer:166、_draw_outline_sides:257 | 描边 |
| facade_south | bool | setter→queue_redraw | _paint_cell:677/690;读 :696 | _draw_south_facade:352 | 南立面 |
| proj_band | float | setter→queue_redraw | _paint_cell:678/704 | _draw_north_projection:367 | 地面投影 |
| badge_glyph | String | setter→glow mask+queue_redraw | _paint_cell:719/726 | _draw_glyph_layer:206–210、_sync_glow_layer:413 | 徽章 |
| dot_visible | bool | setter→queue_redraw | 路径预览:549/554 | _draw_overlay_layers:230 | 叠加层(探针直读 case③) |
| hl_visible | bool | setter→queue_redraw | 悬停:512/517/521/525/534 | :233 | 叠加层(探针直读 case①) |
| hl_color | Color | setter→queue_redraw | :513/518/522/526 | :235 | 叠加层(探针直读 case①②) |
| font | Font | 无 setter | set_world_font:185、rebuild:260 | _draw_glyph:392、_draw_badge:418 | 字形/徽章/迷雾记忆 |
| bevel | float | setter→queue_redraw | rebuild:253 | _draw_bevel:312–321 | 倒角 |
| tex_base | Texture2D | 无 setter | _paint_cell:665 | _draw_base_layer:161、_draw_fog_memory:341 | 基座+迷雾记忆 |
| tex_mutation | Texture2D | 无 setter | 无(素材槽位恒 null) | _draw_glyph_layer:197 | 纹带槽 |
| tex_glyph | Texture2D | 无 setter | 无(槽位) | :202 | 数字槽 |
| tex_badge | Texture2D | 无 setter | 无(槽位) | :207 | 徽章槽 |
| tex_seal | Texture2D | 守卫 setter→queue_redraw | _paint_cell:614/618 | :212 | 封印槽 |
| tex_facade | Texture2D | 无 setter | _paint_cell:679/709 | _draw_e_facade:380–386 | E 格墙面 |
| facade_strip | bool | setter→queue_redraw | _paint_cell:680/708 | :380 | E 格墙面 |
| treasure_mark | String | 守卫 setter→queue_redraw | _paint_cell:713 | _draw_mutation_marks:326 | 变异标记 |
| mutation_mark | String | 守卫 setter→queue_redraw | _paint_cell:723/725 | :330 | 变异标记(探针直读 case⑭) |
| glow_group | int | 守卫 setter→_owner._sync_glow_layer() | _paint_cell:727、_sync_glow_to_units:768 | _sync_glow_layer:404/412 | 微光子件 |
| removed_dim | bool | setter→queue_redraw | _paint_cell:737 | _draw_overlay_layers:239 | 退色层 |
---
## 3. 方案形态——三条路的评估
### 3.1 路 A:状态件升格独立文件×N(推荐,取「绘制域件」形态)
按 _draw 已有的层域轴切。迭代 41 拆函数时已经沿这条轴走了(基座/字形/叠加/迷雾/描边/接触带/倒角……),本设计把同一条轴从函数级升到类级:**每个件=一个绘制层的入参+该层的绘制命令**。件有行为(draw 方法族+setter 重绘语义)、有职责语句、单一变化原因=该层的视觉规格(art-bible 对应条款族),满足 code-conventions §1.1 全部五条拆分判据与 §1.2「功能=类」。这是对迭代 41「被动数据袋」否决的正面回答:成员不再是「剩下的装一起」,而是「这一层画什么、怎么画」的完整领域单元。
细化两个子形态:
- **A2 绘制命令随迁(推荐)**:件的 draw 方法持有绘制命令本体,宿主 _draw 退为时序器。真内聚(状态+行为同件);宿主从 423 行缩到约 150 行,退离 §1.1 的 400 行审视线。
- **A1 纯入参件(淘汰)**:件只装数据,绘制函数留宿主。宿主仍有约 20 个绘制函数经 st 间接取值,件本质=把一个被动数据袋拆成两个被动数据袋,迭代 41 的否决理由原样复活;§1.1-① 立不住。
### 3.2 路 B:成员真实削减(淘汰为主出路,保留两处微收割)
要降到 15 需削 18 个成员,而可削的只有:
- `cell`:全仓零读方(写入仅 rebuild 一处)。可删,零行为变化(待裁点 3)。
- `plate`:绘制侧零消费,**但**是探针冻结成员(player_copy_sweep_test wa8 直读)——禁删;给它接上绘制判据反而改变 golden 现状(「人」回退格会少画牌),禁。
- tex_mutation/tex_glyph/tex_badge 三槽位:从未被写入但被 _draw 消费(elif 程序化回退路径),是 art-bible §8.2 在册素材槽位——结构承载,非无效成员,留。
削减上限 1–2 个,差 16 个以上,**单独不可达**。上移 board_view 方向被堵死:board_view 类变量 32 已按尾波卡 D5 裁豁免终局(键永久基线内、禁再恶化),上移任何成员都构成禁再恶化的恶化。
### 3.3 路 A3:整体搬成一个大状态件文件(淘汰)
状态件 24–27 个成员整体独立成文件,新文件自身 24–27>15=基线外新键——任务简报预警的陷阱,实测确认。拆是必须的,问题只是切几刀、沿什么轴。
### 3.4 路 C:重绘回引形态三选(随 A2 定案)
| 形态 | 判定 |
|---|---|
| **_owner 直引(推荐,现状形态)** | Node 持 RefCounted 成员、RefCounted 持 Node 回引——Node 引用不参与引用计数,**无环无泄漏**(现行 inner class 同款,先例在役)。setter 语句逐字保形,零新契约。Godot 官方判据下 RefCounted 值对象持宿主回引是常规形态。 |
| Callable 注入 | 淘汰。edge/badge_glyph/glow_group 三个 setter 的副作用不止 queue_redraw(glow 层 setup/badge mask/子件同步),一个 Callable 表达不了,得注两三个=每件多 1–2 成员+setter 体改写=语义面重写而非迁移。 |
| 信号 | 淘汰。Godot 信号同步派发、时序等价,但守卫式 setter(tex_seal/treasure_mark/mutation_mark/fog_state 的判重)与 glow 副作用都得改写成发射+宿主分发,新增每信号一行的 §2.1 成文义务,换不来任何行为收益。 |
| 弱引用 | 淘汰。解的是不存在的环引用问题,白白加 is_instance_valid 守卫复杂度。 |
---
## 4. 拆分定案(推荐案 A2 两件案)
### 4.1 总账
| 文件 | 类变量(全计) | 构成 |
|---|---|---|
| board_cell_unit.gd(宿主) | **10** | edge/cell/glow_group/glyph/glyph_color/plate/fog_state/_glow_layer/base/face |
| cell_base_layer.gd(新) | **12** | _owner/bg_sb/contact_bands/contact_corners/contact_col/outline_sides/facade_south/proj_band/bevel/tex_base/tex_facade/facade_strip |
| cell_mark_overlay.gd(新) | **13** | _owner/badge_glyph/dot_visible/hl_visible/hl_color/font/tex_mutation/tex_glyph/tex_badge/tex_seal/treasure_mark/mutation_mark/removed_dim |
对账:原 33 声明=新 10+12+13=35;差额 2=每件各持一个 _owner(原仅一个)。edge/cell/glow_group 上收宿主的理由:edge 是格件自身几何且其 setter 驱动宿主自有 _glow_layer(收编后 setter 体少两处跨对象回引);glow_group 驱动微光子件生命周期(加/撤子节点是 Node 职责,RefCounted 件做不了);cell 是格件身份。三者上收后,跨对象回引只剩「件→宿主 queue_redraw/glow mask」这一类,与头注「setter 触发宿主重绘语义经 _owner 回引保持逐字」完全同形。
### 4.2 件 A:cell_base_layer.gd(格面基座层绘制件)
- 落点:`game/presentation/views/cell_base_layer.gd`;extends **RefCounted**(godot-master 判据:瞬态逻辑/数据容器用 RefCounted,Resource 是给可序列化、要进 Inspector 的数据——绘制入参每拍重写、永不落盘,Resource 只带来共享默认值陷阱与序列化包袱);无 class_name(类名缓存面纪律,同宿主与 cell_type_glow_layer 先例),经宿主 preload 引用。
- 职责语句:格面基座层绘制域——底面/描边/高墙立面/地面投影/接触带微光/顶面倒角的全部绘制入参与绘制命令。
- 单一变化原因:art-bible 基座层视觉规格(§1.2 叠层序/§3.2 接触带/§8.5 结构归程序/v3.5 冻结①立面条款族)——瓦片、立面、投影、微光的画法变了改本件,不触字形与迷雾。
- 成员:上表 4.1 行所列 12 个;类型与默认值逐字继承,四个 setter(facade_south/proj_band/bevel/facade_strip)体逐字(_owner.queue_redraw())。
- 绘制命令:现宿主九个私有函数随迁——_draw_base_layer/_draw_outline_sides/_draw_south_facade/_draw_north_projection/_draw_e_facade/_draw_contact_or_fallback/_draw_contact_bands/_draw_contact_corner/_draw_bevel——逐字搬运,三处机械改写:`st.X`→裸成员名、`draw_X(...)`→`canvas.draw_X(...)`、`st.edge`→`rect.size.x`;公开入口一个 `draw_base(canvas: CanvasItem, rect: Rect2)`,其余私有。九函数现役零超标键(基线无其函数级键),搬运不改行数与分支=零新函数级键。
- 常量随迁:FACADE_HIGHLIGHT_PX/FACADE_NEAR_FRAC/FACADE_FAR_FRAC/FACADE_NEAR_COL/FACADE_FAR_COL/PROJECTION_NEAR_COL/PROJECTION_MID_COL/PROJECTION_EDGE_COL/E_FACADE_FRAC 九个 D/E 档常量(本会话 grep 实查全仓零外部引用,宿主头注「绘制侧单点」句随迁改写)。UiKit.E_FACADE_FOOT_COL 是 UiKit 自己的成员,不动。
- 素材槽位 tex_base 例外消费:迷雾记忆态画底纹(宿主 _draw_fog_memory)读 base.tex_base——公开成员读,单向、成文于宿主头注。
### 4.3 件 B:cell_mark_overlay.gd(格面标识与顶层叠加绘制件)
- 落点:`game/presentation/views/cell_mark_overlay.gd`;extends RefCounted;无 class_name。
- 职责语句:格面标识与顶层叠加绘制域——变异标记/数字色牌与字形/奖励徽章/封印/路径点迹/焦点高亮/中立移除退色的全部绘制入参与绘制命令。
- 单一变化原因:art-bible 标识与叠加层视觉规格(冻结④色牌/徽章金字/真视红框/悬停高亮/退色降档条款族)——「格面上标什么记号」变了改本件,不触底面与迷雾。
- 成员:上表 4.1 行所列 13 个;八个守卫/非守卫 setter 体逐字(含 badge_glyph 的 glow mask 回引 `_owner._glow_layer`——头注在案的回引语义原样保留)。
- 绘制命令:现宿主六个私有函数随迁——_draw_mutation_marks/_draw_glyph_layer/_draw_digit_plate/_draw_glyph/_draw_badge+_draw_overlay_layers 的 dot/hl/removed 三段。字形文字与色(glyph/glyph_color)是宿主冻结成员,作**参数**传入(`draw_glyph_layer(canvas, rect, glyph, glyph_color)`,4 参≤5);文字渲染单点化为本件公开方法 `draw_centered_text(canvas, rect, text, color, font_size)`(5 参合规),宿主迷雾记忆态复用它画记忆墨数字——零复制。
- 拆出两个绘制入口的原因:迷雾(fog_state 宿主冻结成员)在原 _draw_overlay_layers 内部夹在 hl 与 removed 之间,宿主时序器在两入口之间插雾层——见 §6。
### 4.4 宿主保留面(10 成员)
- 职责语句:被动绘制壳——绘制时序器+视野迷雾域(fog_state 冻结成员+记忆态换装)+微光子件生命周期(_glow_layer/glow_group/_sync_glow_layer)+格件几何(edge/cell)+探针冻结面(glyph/glyph_color/plate)。
- 单一变化原因:迷雾规格(art-bible §6.5)与格件节点生命周期。迷雾居基座与标识两件之上、又读件 A 的 tex_base 与件 B 的字体渲染——把它留在宿主是三域中唯一不产生跨件回引的落点。
- 头注改写要点(S10 口径 ≤8 行、无日期、无链接):状态件句改为两件句;「探针直读冻结成员」清单照旧(glyph/fog_state/plate/glyph_color+_glow_layer);新增一句「hl_color/hl_visible/dot_visible/mutation_mark 探针读 u.face.*」。
### 4.5 宿主 _init 与建件
```
const _BASE_LAYER := preload("res://presentation/views/cell_base_layer.gd")
const _MARK_OVERLAY := preload("res://presentation/views/cell_mark_overlay.gd")
func _init() -> void:
base = _BASE_LAYER.new(self)
face = _MARK_OVERLAY.new(self)
```
件侧 `func _init(owner: Node2D) -> void: _owner = owner`(S9 R3 对 _init 参数豁免——构造注入面形态自由)。每格三对象(Node2D+两 RefCounted)与现状每格两对象同量级;尾波卡 T1 的 `_build_cell_unit` 构建器里仍只有一处 `_CELL_UNIT.new()`,件在宿主 _init 内自建,不触发 allocation-in-loop 新键(该规则扫循环体内的 .new() 文本,构建器函数体不在循环体内)。
### 4.6 类型闸与头注义务
两个新件全量类型标注(S9 R1/R2/R3 同尺度);文件头 `##` 注释 ≤8 行、零日期、零链接、零变更叙事(S10);每件头注写职责语句与 _owner 回引语义(§2.1 精神——回引不是跨类信号,无信号成文义务)。
---
## 5. setter→重绘语义零变化论证(写入路径逐点对账)
原则:全部 setter 体逐字搬家或原位不动;改址只改 board_view 侧赋值表达式的路径段。queue_redraw 本身合帧(同帧多次申请一次重绘),时序逐字=写入点顺序不变。
### 5.1 board_view 写入面改址对照(行号 2026-10-29,T0 重核)
| 现写点 | 新写点 | 语义 |
|---|---|---|
| u.st.edge(:204/:251 写;:704/:891 读) | u.edge | 宿主 setter 体:原 `_owner._glow_layer`→`_glow_layer`、`_owner.queue_redraw()`→`queue_redraw()`——glow setup+重绘的执行序逐字等价(语句同、对象同、时机同) |
| u.st.glow_group(:727/:768) | u.glow_group | 守卫 setter 调 _sync_glow_layer()(原经 _owner 转发,现直调——同一函数同一时机) |
| u.st.cell(:250) | u.cell(或随待裁点 3 删除) | 无 setter,纯数据 |
| u.st.bg_sb(:666) | u.base.bg_sb | 无 setter |
| u.st.tex_base(:665) | u.base.tex_base | 无 setter |
| u.st.outline_sides(:676 assign/:700 append) | u.base.outline_sides | 无 setter;**assign 保持数组引用语义**——件内成员同为一处 Array[int],引用不变 |
| u.st.facade_south(:677/:690 写;:696 读) | u.base.facade_south | setter→queue_redraw 逐字;双写触发两次申请=现状同形 |
| u.st.proj_band(:678/:704) | u.base.proj_band | 同上 |
| u.st.tex_facade(:679/:709) | u.base.tex_facade | 无 setter |
| u.st.facade_strip(:680/:708) | u.base.facade_strip | setter→queue_redraw 逐字 |
| u.st.contact_bands/corners(:804/:805) | u.base.contact_* | 无 setter(整组引用换新) |
| u.st.contact_col(:780/:787/:810/:816) | u.base.contact_col | 无 setter(_process 逐拍呼吸色) |
| u.st.bevel(:253) | u.base.bevel | setter→queue_redraw 逐字 |
| u.st.badge_glyph(:719/:726) | u.face.badge_glyph | setter:`_owner._glow_layer` mask+queue_redraw 逐字(回引体原样) |
| u.st.dot_visible(:549/:554) | u.face.dot_visible | setter→queue_redraw 逐字 |
| u.st.hl_visible/hl_color(:512–526/:534) | u.face.hl_* | setter→queue_redraw 逐字 |
| u.st.tex_mutation/tex_glyph/tex_badge | u.face.tex_* | 无 setter |
| u.st.tex_seal(:614/:618) | u.face.tex_seal | 守卫 setter 逐字(判重防覆盖语义是 case④b 回归断言的守护对象,禁动) |
| u.st.treasure_mark/mutation_mark(:713/:723/:725) | u.face.*_mark | 守卫 setter 逐字 |
| u.st.removed_dim(:737) | u.face.removed_dim | setter→queue_redraw 逐字 |
| u.st.font(:185/:260) | u.face.font | 无 setter |
| u.glyph/u.glyph_color/u.plate/u.fog_state | 不变 | 宿主冻结面 |
| u.queue_redraw()(:738 终写) | 不变 | paint 尾部全格重绘申请原样 |
### 5.2 宿主内消费改址
- _sync_glow_layer:st.glow_group→glow_group、st.edge→edge、st.badge_glyph→face.badge_glyph(公开成员读);setup/set_badge_mask 调用序逐字。
- _draw_fog_memory:st.tex_base→base.tex_base;字形重画改调 face.draw_centered_text(self, rect, glyph, UiKit.FOG_MEMORY_INK, maxi(18, int(ceil(edge * 0.5))))——同公式同字号(§7.4 冻结式)。
- cell_rect_probe:u.st.edge→u.edge。
---
## 6. 绘制命令序零变化论证(golden 判据本体)
新宿主 _draw=六步时序器,命令序与现行四函数调用树**逐命令等价**:
```
func _draw() -> void:
var rect := Rect2(Vector2.ZERO, Vector2(edge, edge))
base.draw_base(self, rect) # 原青 _draw_base_layer 整树(含描边/立面/投影/接触带/倒角与其补画序)
face.draw_marks(self, rect) # 原青 _draw_mutation_marks
face.draw_glyph_layer(self, rect, glyph, glyph_color) # 原青 _draw_glyph_layer+_draw_digit_plate+_draw_glyph+_draw_badge
face.draw_overlay_under(self, rect) # 原青 _draw_overlay_layers 前两段(点迹+高亮)
_draw_fog_layer(rect) # 宿主(原位不变——雾夹在 hl 与 removed 之间)
face.draw_removed_dim(self, rect) # 原青 _draw_overlay_layers 末段(退色)
```
等价性来源:①层间顺序=原 _draw 的四调用序+_draw_overlay_layers 内部序,一步未动;②件内顺序=九+六函数逐字搬运,`draw_*` 引擎调用全部经 canvas 参数落到同一 CanvasItem(宿主 self),命令流与命令参数逐位相同;③接触带回退路径(contact_col.a>0 时补画投影/E 墙面/接触带;elif bg_sb 路径补画立面/投影/倒角)整体在件 A 内部,补画序逐字;④浮点面:rect.size.x 与 st.edge 同值同源,推导式零改动。结论:绘制命令流逐位一致 ⇒ golden 三张差异 0.00% 可预期。
---
## 7. gdstyle 键账预期(逐文件,平面口径=声明数全计)
| 文件 | 类变量(阈值 15) | 公开方法(阈值 20) | 函数级键预测 |
|---|---|---|---|
| board_cell_unit.gd | 10(余量 5) | 6(5 setter+_draw) | 无——_draw 时序器约 10 行;_draw_fog_layer/_draw_fog_memory/_sync_glow_layer 原体(现役零键) |
| cell_base_layer.gd | 12(余量 3) | 5(draw_base+4 setter) | 无——九函数逐字搬运,行数/分支/局部/返回不变,现役零键 |
| cell_mark_overlay.gd | 13(余量 2) | 13(5 绘制入口+8 setter) | 无——同上;draw_glyph_layer 4 参、draw_centered_text 5 参(max-parameters 5=合规上限内) |
三件全无 inner class ⇒ 不受 §1.2 截断口径影响,预测值即闸实测值。余量最小 2(件 B)——后续 art-bible 加标记类成员的余量,恶化守卫(WORSENED)在位兜底。
---
## 8. 行为零变化风险点
1. **golden 三张(L1 dungeon/L2 underdark/L3 deep_underdark)**:判据=§6 命令流逐位一致;差异非零即停机呈报,禁换锚。最大实操风险=搬运时手误(常量值、rect 推导式)——对照表逐函数「逐字搬运」核对。
2. **探针冻结成员**:glyph/fog_state/plate/glyph_color 宿主保留、名称路径零变化(ui_input case④b⑪⑮+player_copy wa8 断言零改动);hl_*/dot_visible/mutation_mark 探针改址四处(断言条件与期望值零变化,只改路径段——对照表申报行)。
3. **绘制调用序**:见 §6——含「平色回退+微光并发的英雄格补画」这种跨层重复绘制路径,必须原样保形(重复无害是现行为的一部分)。
4. **setter 双写时序**:_paint_cell 内 facade_south(:677/:690)、badge_glyph(:719/:726)、mutation_mark(:723/:725)的多写多触发序不变。
5. **`_glow_layer` 生命周期**:宿主成员不动;glow_group/edge 上收后其 setter 直调宿主自有面——子件 add/free 时机零漂移(type_glow_shot_probe/type_glow_probe 断言面)。
6. **潜伏红变量**:T0 先跑 ui_input_probe——若现状已红(§1.3 判定),红因与本设计无关但同批修复(改址四处);若绿(本会话对 GDScript 取索引语义判断有误),改址四处照做(新路径本就不同),结论记实现报告。
---
## 9. 验收载体建议(全部既有载体,零新增断言面)
| 载体 | 钉什么 |
|---|---|
| render_gate 棋盘 golden 三张 | 绘制命令流逐位一致(本批主判据;对拍差异 0.00%) |
| ui_input_probe(probe_gate 默认段) | case①②③④b⑪⑭⑮——悬停/禁入/点迹/封印槽/字形回退/变异角标/迷雾直读面全链路 |
| probe_gate --click(ui_click_probe) | dig/seal/chord 输入路径(hl 状态写入面的行为侧) |
| probe_gate --full(type_glow_shot_probe/board_shot_probe/fx_probe) | 微光子件与格矩形取证面 |
| run_all --only=player_copy,theme_projection,type_glow | u.plate/u.glyph_color 直读面+字形墨族+微光派生 |
| run_smoke+run_system_test+static_scan(三件保险恒跑) | 启活面+真实玩法路径+S9/S10/S13 键账对账 |
批末跑 `tools/run_all_gates.sh --code`(改动全为代码面);改址探针断言与对应表申报同批落地。
---
## 10. 估时
| 段 | 内容 | 估时 |
|---|---|---|
| T0 | 读数复核(33 声明/30 闸账对账)+ui_input_probe 潜伏红一跑定谳 | 0.25–0.5 日 |
| T1 | 建 cell_base_layer.gd(成员+九函数+九常量随迁) | 0.5–0.75 日 |
| T2 | 建 cell_mark_overlay.gd(成员+六函数随迁+draw_centered_text 单点化) | 0.5–0.75 日 |
| T3 | 宿主瘦身(时序器/雾/glow/edge/glow_group setter 上收)+board_view 写入面约 40 处改址 | 0.5 日 |
| T4 | 探针改址四处+孤立验证(run_all 选档+probe_gate --click) | 0.25–0.5 日 |
| T5 | 批末代码域全量闸+键账对账(10/12/13 实测核对) | 0.25–0.5 日 |
合计 2.25–3.5 工作日。出红只跑红件最小单元(闸机轮次纪律)。
---
## 11. 待裁点清单(呈主会话)
1. **潜伏红处置**:ui_input_probe 四处直读疑似已被迭代 41 打断(§1.3)。方案=本设计批内改址 u.face.* 并 T0 实跑定谳;若用户希望不等本批,可先走最小工程批把四处改 u.st.* 恢复(后续本批再改一次)。取哪个时序,裁。
2. **与尾波清偿卡的编排关系**:尾波卡 T1 把 _paint_cell 拆五段(传值句写明「经 u.st/glyph/digit_col」)。本设计改变状态件路径名——同批编队时 T1 段函数传值句改指 u.base./u.face.(T0 对账自然兜住);先后皆可,同批省一次 golden 全套。取哪个编队,裁。
3. **cell 成员去留**:全仓零读方(写入仅 rebuild:250)。删除=真实削减+§1.1-⑤ 无效成员归零;保留=「诊断观测面」注释语义。本设计倾向删(宿主降为 9),删/留,裁。
4. **gdstyle 截断口径的登记**:§1.2 发现属工具行为事实(基线欠估、工具升级可能翻键),建议经反哺点通道在 gdstyle_gate 头注或审计报告补一句注记(工程改动,防后人按「均匀展平」误判键账)。落不落、落哪里,裁。
---
## 12. 把握度声明
把握最低的一点:**潜伏红判定**——它建立在「GDScript 外部实例读不到 inner class 成员」这一语义判断与时间线推断上,本会话禁跑测试无法实证,T0 一次 ui_input_probe 即可证实或证伪;即便误判(现状可读),设计本身不受影响(改址四处照做,冻结四面照留)。次低:gdstyle 截断口径系两点反推(30=6+24、19=6+13 双双精确吻合),未读工具源码;设计不依赖该口径——三文件平面化后预测=实数。
@@ -0,0 +1,183 @@
# 设计——战斗编排类变量归约(S13 真修复方案)
> 设计讨论段过程稿 v1.0——待主会话汇总裁决(2026-10-29)。输入=用户裁定「不允许任何豁免:battle_orchestrator.gd 的 max-class-variables 必须真实修复到阈值 15 内,architecture S13 豁免登记第三条注销」。读者=主会话(裁决)与后续写卡段(转需求卡片)。
## 结论
宿主 38 个类变量经「BoardInstaller 已定稿退役 1 + 两枚新件收编 19 + 库存注入改址 1 + 攻基线快照转派生读 1」收敛到 **15(=阈值,合规)**。两枚新件:RoundCycle(轮循环驱动件——五段段件持有、段序执行、结算重入门闸、开轮摇号、踩雷计数账)与 BattleFacts(战斗事实件——受理事实、轮 facts 缓冲、封印判定账、装板溯源、条件计数、同调装配快照)。已实装两拆件 BattleFxHost/RewardGrant 与五段段类构造、呈现层、A2 订阅面、封闭流全部零触碰或仅改指。
---
## 1. 目标形态——battle_orchestrator 改后保留什么
宿主保留的是「编排」本身,全部在 architecture「战斗编排域拆件定稿」第 1 款的在册职责面内(attune 一款按本文件第 5 节重裁决为「状态归事实件、行为留宿主」):
1. **状态机与生命周期**:`begin_battle` 受理四段序列(受理/装板/运行态/预告经济)、`_exit_distribution` 轮间转移、`_battle_over` 终局序列、`battle_result` 与 `_fatal` 唯一写点、`round_index` 轮时钟。
2. **A2 三事件订阅与事件处理**:三条 connect 留 `_init`(订阅面逐字);`_on_cell_revealed`(旁路守卫→积点自动兑付触发→身份快照捕获→缓冲追加)、`_on_seal_judged`、`_on_round_end` 三个处理器留宿主——它们是多件协作的编排判断点,不是纯缓冲。
3. **行发射基建**:`_emit_row`/`_assert_row_source`/`_fatal`+SOURCE_EXEMPT 两常量词表——协议面单点,零迁移。
4. **呈现投影**:`presentation_snapshot`/`diagnostic_snapshot`/`_emit_settlement_snapshot` 三出口——签名与载荷逐字不变,内部读数改指两件投影。
5. **刷新面**:`can_refresh`/`player_request_refresh`。
6. **attune 编排读数与触发面**(重裁决后):`attune_has`/`attune_param_sum`/`attune_convert_state`/`_attune_charge_safe_row`/`_reseed_attune_charges`/`_hook_attune_consume`/`_attune_safe_seen_read`/积点自动兑付体——读数出口与外部面零改址(见第 5 节)。
7. **账本读面**:`_journal`/`_attune_loadout`/`_battle_hooks` 三派生读与回调束。
8. **装板薄壳**:`_install_and_reset`(轴丙定稿形态——五处计数清零、板序推进、board_changed 发射、叠加层退役,时序逐字)。
9. **B2/B3/B4 协作者持有**:S7「唯一调用方=B1 域文件集」的承载面。
搬出去的只有两类东西:**轮结算周期的推进机构**(谁持有段件、按什么顺序跑、开轮怎么摇号、重入怎么挡——归 RoundCycle)与**发生过什么与本战是什么的记录**(事实账——归 BattleFacts)。两件的职责语句与单一变化原因见第 3、4 节。
## 2. 逐成员去向对账表(38 项)
| # | 成员 | 去向 | 依据(一句话) |
|---|---|---|---|
| 1 | db | 留宿主 | B1 跨系统调用权的表读取面,宿主级协作者 |
| 2 | rng | 留宿主 | encounter/gp_drop 两封闭流在宿主函数体内,behavior_roll 随摇号件经 oc 取用 |
| 3 | _board_generator | **随 BoardInstaller 退役** | 轴丙已定稿(v1.24):实例随件自建自接线 |
| 4 | minesweeper | 留宿主 | A2 订阅面+HUD 公有消费面(API Boundaries 在册)双锁定 |
| 5 | action_economy | 留宿主 | A3 回发面+HUD 公有消费面双锁定 |
| 6 | _enemy_system | 留宿主 | B2 生命周期(spawn/despawn)与三快照读源 |
| 7 | _status_effects | 留宿主 | B3 开域/闭域与三快照读源 |
| 8 | _reward_economy | 留宿主 | B4 恰一次 roll/retire 编排+set_treasure_unlocked/query_reward_identity 两外部代理 |
| 9 | treasure_inventory | **退役改址** | 唯一外部注入点 level_runner:520 改经 `reward_grant()` 出口直注(RewardGrant 在册成员);宿主与授予段读面改指 grant |
| 10 | _damage | 留宿主 | 单据管线接线(fx_calls/inject)与 AC 基线注入时序锁定在受理段内 |
| 11 | _fx_host | 留宿主 | 组件出口 `fx_host()`——定稿在册取件面 |
| 12 | _reward_grant | 留宿主 | 组件出口 `reward_grant()`+库存持有者+escrow 读源 |
| 13 | encounter | **BattleFacts** | 本战受理事实(打什么、带什么进来)——与过程事实同一记录口径 |
| 14 | round_index | 留宿主 | 轮时钟;system_test 等四套件约 15 处 `settlement.round_index` 公有直读面,零改址 |
| 15 | battle_result | 留宿主 | 状态机终局态;_fatal/_battle_over 唯一写点归属不变 |
| 16 | _facts_reveals | **BattleFacts.reveals** | 轮 facts 缓冲 |
| 17 | _facts_seals | **BattleFacts.seals** | 封印判定批缓冲 |
| 18 | _facts_path | **BattleFacts.path** | 轮末路径事实 |
| 19 | _settling | **RoundCycle._settling** | 结算重入门闸——与段序执行同一单元 |
| 20 | _grant_seg | **RoundCycle** | 五段段件持有归驱动件(定稿「段序编排+接线」的代码承载) |
| 21 | _aura_seg | **RoundCycle** | 同上 |
| 22 | _combat_seg | **RoundCycle** | 同上 |
| 23 | _decay_seg | **RoundCycle** | 同上 |
| 24 | _verdict_seg | **RoundCycle** | 同上 |
| 25 | _refresh_used_this_round | **BattleFacts.refresh_used** | 本轮刷新计数=过程事实(复位点=出口分发,语句随迁) |
| 26 | _board_seq | **BattleFacts.board_seq** | 装板溯源事实 |
| 27 | _board_from_midround_refresh | **BattleFacts.board_from_midround** | 装板 provenance(清图奖励守卫读源) |
| 28 | _seal_consumed | **BattleFacts.seal_consumed** | 封印兑付资格账(install 重置) |
| 29 | attunement | **BattleFacts.attunement** | 本战装配集快照=受理事实(enc.attunement 携带) |
| 30 | _mines_hits | **RoundCycle.mines_hits** | 摇号条件账——转存语句本就在 `_roll_round_actions` 轮末,随摇号件走 |
| 31 | _attune_safe_seen | **BattleFacts.attune_safe_seen** | 积点计数=过程事实 |
| 32 | nest_preview_count | 留宿主 | 冻结外部直读面(main.gd×2+treasure_effects_test×1)零变化 |
| 33 | _feat_first_dig_used | **BattleFacts.feat_first_dig_used** | 条件事实计数(魔矢每战首次) |
| 34 | _seal_miss_seen | **BattleFacts.seal_miss_seen** | 标错去重账 |
| 35 | _perfect_hit_true | **BattleFacts.perfect_hit_true** | 完美封印三账(契约 2 写口归件,见第 4 节) |
| 36 | _perfect_hit_false | **BattleFacts.perfect_hit_false** | 同上 |
| 37 | _perfect_forgiven | **BattleFacts.perfect_forgiven** | 同上 |
| 38 | _hero_atk | **退役→派生读函数** | `_hero_atk()` 派生读(`encounter.get("hero_atk", 表值)`)——`_journal()`「读面即引用、无第二账」同款先例;E4 收口语义不变(encounter 受理后只读,派生值恒等) |
**宿主改后 15 项**:db、rng、minesweeper、action_economy、_enemy_system、_status_effects、_reward_economy、_damage、_fx_host、_reward_grant、round_index、battle_result、nest_preview_count、_rounds、_facts。
## 3. 新件一:RoundCycle(轮循环驱动件)
- **文件落点**:`game/core/settlement/round_cycle.gd`(与五段段件同目录——它是段件的序主人)。
- **extends**:RefCounted(fx_host/reward_grant/段类同款;随编排器每战新建同灭)。
- **职责语句**:每轮结算周期的唯一驱动——结算重入门闸(settling)、五段段序执行(段序骨架冻结于 SettlementTable.segment_order)、开轮摇号(共识点 B:技能选择前置到轮次开始,摇定即本拍结算的选技)、踩雷计数账(转存时点=摇号轮末)。
- **单一变化原因**:轮结算周期的推进结构(段序、重入闸、摇号时序)。
- **类变量(8 ≤ 15)**:`oc`(宿主,段类同款直调形态)、`_grant_seg`、`_aura_seg`、`_combat_seg`、`_decay_seg`、`_verdict_seg`、`_settling`、`mines_hits`({"total","last","round"} 三读数合一账)。
- **公开方法(3 ≤ 20)**:`settle_if_due()`(=原 `_maybe_settle` 三闸+`_settle` 段序体合体)、`roll_round_actions()`(原函数逐字迁入)、`is_settling()`(diagnostic_snapshot 读投影)。
- **行数估算**:约 120 行(头注+成员+四函数)。
- **耦合边界**:
- `_init(host, fx_host, reward_grant)`——3 参不触发 max-parameters,无需引用束;五段构造五行从宿主 `_init` 逐字搬入。
- 段类对宿主的直调形态不变(定稿在册:「段类经宿主引用直调,现状形态」);驱动件经 `oc` 取 facts/协作件/行发射/出口分发,与段类同款。
- **VerdictSegment 零改动**:`_exit_distribution` 留宿主(轮间转移=宿主生命周期域:换图编排、notify、轮始发射),判定段仍直调 `oc._exit_distribution()`——不产生段类对驱动件的新引用,不新增 RefCounted 引用环。
- 随机只经 `oc.rng` 的 behavior_roll 封闭流(流名、开流时点、取号序逐字);零 await。
- **宿主侧变化**:`_on_round_end` 末行改调 `_rounds.settle_if_due()`;`begin_battle` 与 `_exit_distribution` 的 `_roll_round_actions()` 改调 `_rounds.roll_round_actions()`;`_maybe_settle`/`_settle`/`_roll_round_actions` 三函数从宿主删除。
## 4. 新件二:BattleFacts(战斗事实件)
- **文件落点**:`game/core/battle_facts.gd`。
- **extends**:RefCounted。
- **职责语句**:战斗事实的唯一记录面——本战受理事实(encounter:打什么、带什么进来)、同调装配事实(attunement)、轮 facts 缓冲(reveals/seals/path)、封印判定账(去重两集+完美三账)、装板溯源(seq/provenance)、条件事实计数(refresh_used、feat_first_dig_used);自带轮复位与完美账写口。
- **单一变化原因**:战斗事实的记录与查询口径——内容批新增触发条件事实/判定账只落此一处。实证:`_feat_first_dig_used`(魔能共鸣)、`_seal_miss_seen`(危险直觉)、`_mines_hits`(触发条件面)、enc 的 feats/tree_rules/voucher 携带键,全部由内容批按同一节奏累加。
- **类变量(15 ≤ 15,贴顶)**:`encounter`、`reveals`、`seals`、`path`、`refresh_used`、`board_seq`、`board_from_midround`、`seal_consumed`、`seal_miss_seen`、`perfect_hit_true`、`perfect_hit_false`、`perfect_forgiven`、`feat_first_dig_used`、`attunement`、`attune_safe_seen`。
- **公开方法(5 ≤ 20)**:`reset_round()`(原 `_reset_round_facts` 逐字——只清 reveals/seals/path 三项,**不碰 refresh_used**:_battle_over 后快照读到的 refresh_used 必须保持末轮值)、`mark_perfect_hit()`/`mark_perfect_miss()`/`mark_perfect_forgiven()`(契约 2 写口:`_perfect_hit_false` 纯净禁回退改写的执行点从段类纪律升级为账本单点)。其余一律公有成员直读写(段类/快照/宿主经 `oc._facts.X` 改指——fx_host._fx_bonus_delta 跨件直读写同款现状形态)。
- **行数估算**:约 80 行。
- **耦合边界**:**零注入引用、零信号、零协作对象调用**——纯数据+谓词+写口;缓冲写入点在宿主 A2 处理器(订阅面锁宿主),身份快照捕获(query_full_snapshot)留宿主 `_on_cell_revealed`(fact 件不持 B4 引用,防它变成第二编排面)。
- **成员冻结义务**:15/15 贴顶——实现期禁增员,增员即停卡。预留扩容阀:下一张要加事实成员的卡,先把封印判定账五成员二次拆出(SealLedger,−5)再落新账,禁硬挤。
## 5. attune 族重新裁决(architecture「原位保留」句的废止建议)
**建议:部分改裁——状态两成员归 BattleFacts,行为函数留宿主。** 理由四条:
1. **原位裁的前提已消失**:该句是迭代 39/40 两批的防冲突临时裁,原文自认「本波残留债归尾波」;两批均已合回,本波即尾波,且用户已裁「不允许任何豁免」打开重裁通道。
2. **状态是事实,行为是编排**:`attunement`=本战装配了什么(enc.attunement 受理携带的快照,受理后只读)、`attune_safe_seen`=本战安全开格积点——两者都是战斗事实,归账本件名正言顺。而参数合计(装配集行扫描)、积点自动兑付(何时 grant_ap=编排决策)、充能重置触发、呈现进度读数——这些是 B1 编排面的读数与触发,与 `_fx_host_fact` 同位的宿主读数形态,留在宿主不违职责语句。
3. **外部面零改址**:`attune_has`(断点续玩测试×2+表结构守卫×2)、`attune_param_sum`(main.gd 授予段两处+treasure_effects_test)、`attune_convert_state`(treasure_interact_controller 取值回调)全部留宿主公有面——main.gd 与 treasure_interact_controller 本批零改动。若整体拆成 BattleAttune 新件,这三面约 10 处消费点全要改址(组件出口或转发壳),收益只是把 8 个读数函数换个文件——判据③(阈值动机)嫌疑大于内聚收益。
4. **不并进 AttuneLoadout**:那是营地/冒险域的充能与装备集物化单源(迭代 39 已闭合契约);把战斗域读数插进去会重开已闭合边界。BattleFacts 持有的只是快照与计数,充能物化仍全归 AttuneLoadout(`_reseed_attune_charges`/`_hook_attune_consume` 经调用点传 loadout/treasure 委派,宿主侧单点不变)。
## 6. 已否决的方向(为什么不是别的切法)
1. **协作者引用束归袋**(任务提示①的直译形态):db/rng/units/statuses/reward_economy 等 11 个协作者句柄若收进一个「引用束」对象,宿主 150+ 处 `db.`/`oc._enemy_system.` 全部换名——零内聚收益的纯改名,命不出领域名字=凑计数袋(拆分判据②红)。引用束先例只用于新件 `_init` 防参(本批两新件参数均 ≤3,未触发)。
2. **attune 整体拆件(BattleAttune)**:宿主 14 基数+3 句柄=16 不可达;且如第 5 节理由 3,外部三面 10 处改址换不来职责改善。
3. **encounter 留宿主**:宿主 16 超阈。备选砍口只剩两处——nest_preview_count 解冻迁 facts(违背任务给定冻结面)或 _reward_economy 经 RewardGrant 中转(B4 mapping 生命周期与代理面挂到授予件名下=所有权失真)。encounter 归事实件是唯一不破冻结面、不失真的砍口;它也是本方案最弱的一格,见待裁点 1。
4. **四件以上碎片化**:每多一件付一个宿主句柄;封印判定账、装板溯源单独成件都撑不起独立变化原因,且把宿主推向 16+。
5. **_exit_distribution 迁入驱动件**:判定段须改持驱动件引用(RefCounted 环+段类构造变化),换图编排又是宿主生命周期语义——收益不抵风险,否。
## 7. 随迁键收敛义务
**基线实证**:`tools/gdstyle_baseline.txt` 中 battle_orchestrator.gd 现仅两条类级键(max-class-variables=38、max-file-length=1035),**零函数级键**——59 个函数全部在默认阈值内(长度≤50/分支≤8/局部≤10/返回≤6/参数≤5)。函数级指标是函数体内在属性,逐字迁移(仅引用改指)不产生新键。据此:
- **随迁键清单:预计零条**。义务改为「迁移函数逐字纪律」:`roll_round_actions`(45 行/分支≈8 贴线)、`settle_if_due`、`reset_round`、三个 mark 方法——迁移与改指**禁顺手增分支/局部**;确需增条件即停手核对 gdstyle,超线在卡内拆助手函数收敛(先例=迭代 40 卡①的 12 随迁键收敛)。
- 新增小函数全部远在阈值内(最长 `roll_round_actions` 45 行;新件 `_init` 参≤3)。
- 两新件与改址后的宿主、段类落地后跑 `python tools/gdstyle_gate.py` 预检一次再进批末闸。
## 8. 行为零变化风险点与对策
1. **RNG 取号序**:宿主三封闭流——encounter(`_begin_battle_dice`,留宿主)、gp_drop(`_roll_gp_drop`,留宿主)、behavior_roll(随 `roll_round_actions` 迁 RoundCycle——流名与开流时点逐字,只经 `oc.rng`)。fx_reveal(fx_host 内零触碰)、board_gen/variant_place(BoardInstaller 已定稿随件)。对策=同种子完整局对拍(见第 9 节 PARITY)。
2. **信号契约**:五信号(step_row/battle_finished/round_started/board_changed/settlement_snapshot)全留宿主;发射语句零迁移——`round_started.emit` 在 begin_battle/`_exit_distribution` 内原位,仅 round_index 读源不变;A2 三 connect 逐字。发射时序零变化。
3. **段类接线**:五段构造参数与持有形态不变(grant/combat 三引用、aura/decay/verdict 单引用);verdict 段零改动(`_exit_distribution` 留宿主)。段内约 25 处 `oc._X` 改指 `oc._facts.X`/`reward_grant.treasure_inventory`——调用点改指、禁转发壳(定稿同款纪律)。
4. **_fatal 与 battle_result 唯一写点**:留宿主;BoardInstaller(已定稿 fatal Callable)、RoundCycle/段类(经 oc 直调)零改形。
5. **结算时序**:`settle_if_due` 三闸顺序(settling→battle_result→phase)与「终局不回置 _settling、_battle_over 接管」早退语义逐字;判定段内 `_exit_distribution`→回置 `_settling` 的先后不变(出口留宿主保证);`reset_round` 不碰 refresh_used(_battle_over 后快照的 refresh_used 必须保持末轮值——这是本轮盘点抓出的一处险点)。
6. **重入语义**:`_on_cell_revealed` 不设 settling 守卫(现状——flush_pending_reveals 的 fx_reveal 事件依赖 reveal_busy 旁路闸而非 settling),处理器体零改形;install 初始展开事件「先缓冲后清除」路径(begin/exit 两处 reset_round)保持。
7. **快照逐字**:三快照字段名、嵌套结构、值口径逐字——读数改指投影后值恒等(round_index/attune_safe_seen/perfect 三账/hero_atk 派生读均受理后不变量)。
8. **装板薄壳时序**:轴丙定稿五处清零→板序推进→board_changed 发射→叠加层退役的顺序逐字(成员改指 facts 不改序);BoardInstaller 闭合设计的 bundle 键「encounter」传 `_facts.encounter`——同一字典引用,零冲突。
## 9. 验收载体与判据
改动—验收对照表(卡内落六列)建议主行:
| 需求点 | 真实入口 | 载体 | 判据绑定面 |
|---|---|---|---|
| 行为零变化(本批总判据) | 真实局 begin_battle→挖掘→结算→终局 | **PARITY 同种子完整局对拍**(parity_anchor/parity_compare) | step_row/settlement_snapshot 逐位一致;**实跑须用户批准、禁重锚**(battle_orchestrator.gd 触 PARITY_TOUCH_FACES) |
| 段序与重入门 | stop→WAITING→settle_if_due | round_flow_skill_pool_test、settle_segments_test(真实一轮走段序,零改址应绿) | 五段裁定行序、settling 闸、终局早退 |
| 开轮摇号迁移 | begin_battle/出口分发→roll_round_actions | round_flow_skill_pool_test(技能池/保底/冷却)+trigger_cond_test(mines_hits 账——3 处读改指 _rounds) | 同种子摇定结果逐位、转存读数 |
| 封印判定账迁移 | 封印动作→结算 | seal_polarity_test(4 处 perfect 直写改指)、feat_vouchers_test(8 处+_grant_seg 改指)、meta_progression_forge_test(契约 12 守侧) | 完美三账读数、去重闸、契约 2 计数纯净 |
| 装板 shell/溯源 | begin/refresh/exit 装板 | variant_placement_test(零改址应绿)+seal_polarity_test provenance 守卫 | 刷新图不发清图奖励、board_seq |
| attune 面零改址 | 装配起战→读数/兑付 | treasure_effects_test、breakpoint_resume_test、table_structure_guard_test(**应零改址全绿**——本批最硬的「外部面零变化」证) | attune_has/param_sum/convert_state/积点兑付 |
| 受理事实迁移 | begin_battle(enc) | adventure_feats_test、meta_progression_forge_test(encounter 4 处改指) | enc 携带键贯通 |
| 快照逐字 | presentation/diagnostic 出口 | system_test+对拍 | 载荷 schema 与值逐字 |
| 库存注入改址 | level_runner 建战注入 | treasure_effects_test 秘宝使用链 | use_treasure 受理面 |
配套义务:①三件保险恒跑(冒烟/系统测试/静态扫描)+上表关联套件(02 关联档);②静态扫描随批增行——RoundCycle 进 S7 B1 域文件集与 S8 await 文件集(经 oc 调 B2/B3/B4 符号落在该文件),BattleFacts 零协作调用不入两清单;③新文件全类型标注(S9)、头注按 §1.4.1;④本批零数值改动,MC 零跑。
## 10. gdstyle 键账预期
| 文件 | 类变量 | 公开方法 | 长度 | 预期键 |
|---|---|---|---|---|
| battle_orchestrator.gd | **15**(38→15) | 14 | ~830 | **两条基线键全消失**(类变量/文件长双双回落阈值内),零新键 |
| settlement/round_cycle.gd | 8 | 3 | ~120 | 零键 |
| battle_facts.gd | 15(贴顶) | 5 | ~80 | 零键(15=阈值内合法,fx_host「14≤15」同口径) |
| settlement/grant_segment.gd | 3(不变) | — | +~2 行改指 | 零键 |
| 其余段类/fx_host/reward_grant | 不变 | 不变 | 不变 | 零键、零 diff |
基线处理:`--refresh` 人工重生成;全基线 16 条消失 2 条(−12.5%>10% 骤降守卫)→ 须 `--force` 显式确认,属正常还债缩基线。architecture S13 豁免登记第三条随批注销(反哺点,实现批禁写 design/)。
## 11. 改址盘点(触面清单)
- **产品码**:battle_orchestrator.gd(主改——成员改指+三函数迁出+hero_atk 派生化+头注指址改写);settlement/round_cycle.gd、battle_facts.gd(新建);grant_segment.gd(~25 处改指);combat_segment.gd(2 处);level_runner.gd(1 行——库存注入改经 `reward_grant()` 出口);aura/decay/verdict、battle_fx_host、reward_grant、damage_controller、main.gd、treasure_interact_controller、board_installer(**零改动**)。
- **测试**:feat_vouchers_test(~13)、meta_progression_forge_test(~8)、seal_polarity_test(~8)、trigger_cond_test(3)、round_flow_skill_pool_test(1)、adventure_feats_test(1)、treasure_effects_test(1——`ob14.attunement` 改指);settle_segments_test、roll_multiplier_test、system_test、breakpoint_resume_test、table_structure_guard_test(零改址——attune 面与 round_index 面不动的直接受益者)。
- **probes/sim**:零触碰(盘点无命中)。
- **闸件数据**:gdstyle_baseline.txt(--refresh --force)。
## 12. 估时
**2–3 个工作日**:实现(两新件+宿主改址+段类改址+测试改址)1.5–2 日;验收(关联档回归+PARITY 批跑+对照表与审计配合)0.5–1 日。PARITY 实跑须用户在场批准(不占卡估时);基线 refresh 属批内闸步骤。
## 13. 待裁点清单
1. **encounter 归战斗事实件**(本方案最弱一格):受理参数与过程记录同件,变化理由按「战斗事实面扩展」归一(第 4 节实证),但确与事实缓冲不同复位节奏(encounter 随战、facts 随轮)。若否决:宿主 16,只剩两条砍路——nest_preview_count 冻结面解锁(外部读面 3 处改址)或 _reward_economy 经授予件中转(所有权失真),两者都更差,届时按判据④停卡呈报。
2. **treasure_inventory 注入面改址**:唯一外部产品码触面(level_runner 一行改经组件出口直注),拆件定稿「外部赋值路径零改址」句随本批失效,需追认。
3. **attune 重裁决幅度**:推荐「状态归 facts、行为留宿主」(第 5 节);备选「整体拆 BattleAttune」因宿主 16 不可达已被排除——即本条实际是确认推荐案或否决整个方向。
4. **完美账写口升级**:grant_segment 三处直写改经 facts 三方法(契约 2 单点执行)——推荐采纳;若求最小 diff 可退直写(现状纪律同款),卡内裁量。
5. **PARITY 对拍批准**:battle_orchestrator.gd 触 PARITY_TOUCH_FACES,实跑对拍须用户点头、不重锚。
@@ -0,0 +1,210 @@
# 设计——测速循环去 await(S13 `tune_sim_controller` await-in-loop 键真实消除)
状态行:设计讨论段过程稿 v1.0——待主会话汇总裁决(2026-10-29)。
## 1. 背景与目标
用户 2026-10-29 裁定「不允许任何豁免」:S13 静态质量闸(gdstyle)的裸键 `game/presentation/tune_sim_controller.gd|quality/await-in-loop|'await' used inside a loop` 必须真实消除;architecture「测试与质量域」S13 测量面豁免登记(2026-10-27,理由=「循环内 await 是调参测量的 wall-clock 步进承载语义——重构即测量口径漂移」)随之注销(注销落文走反哺点通道,实现批禁直改 architecture)。
本设计回答一件事:**把「循环内 await 逐帧让出」换成信号驱动形态后,测量语义是否逐点等价**——每步恰占一帧、每步执行相位不变、终止与读数不变、输出逐字不变。结论先行:**可行,推荐 process_frame 信号连接+步进状态机形态**;等价性四判据三条可严格证明并可用双实现对拍执行化验证,一条(脚本错误中断路径)无法严格证明、列为已声明缺口。
## 2. 现状全解
### 2.1 测速循环逐句流程
现码 `run(n)`(`game/presentation/tune_sim_controller.gd:36-52`):
| 步 | 语句 | 做什么 |
|---|---|---|
| 1 | `_running = true` | 控制器运行态(组合根 `_start_debug_levels` 互查守卫的判据) |
| 2 | `PlaytestLogger.new()`+`start_session(SEED_BASE)` | 开共享 JSONL 会话(一文件多局,`log_run` 行带种子切分局次) |
| 3 | `panel.set_sim_running(true)` | 浮窗运行态(`DebugOverlay.st._sim_running`——模拟钮置灰+探针守卫环判据) |
| 4 | `set_sim_progress("模拟中:0/n …")` | 进度串 |
| 5 | 建 `stats` 六键束、取 autoplay 驱动 | 测量桶与策略驱动 |
| 6 | 循环体:`_run_one_level(...)` | 单局全量模拟(`level_autoplay.run_level` 静态同步函数——自建 RngService、种子=SEED_BASE+i,逐局确定可回放)→ 四桶记账 |
| 7 | `set_sim_progress("i+1/n")` | 进度串 |
| 8 | `await panel.get_tree().process_frame` | 每局恰让出一帧(命中键的语句) |
| 9 | 循环尽:`close_session()`+`set_sim_report(...)` | JSONL 收口+报告注入(文案归 `HudPresenter.tune_sim_report`,零触碰) |
读数本质:`stats` 六键与报告是 **(种子, atk_pct, hp_pct) 的纯函数**——帧时点不进测量值(`run_level` 不读 wall-clock)。豁免登记所保护的「测量语义」是运行姿态:每局之间恰隔一帧 process_frame 发射(进度可见、窗口不僵死),以及 run 全程的运行态语义。本设计保的就是这个姿态。
### 2.2 读数消费链
- 报告文案:`tune_sim_report(stats)` → 浮窗 RichTextLabel(`set_sim_report`);探针 ⑤ 断言「关卡完成率/关卡完成剩余 HP 均/败北节点/N=10」四串。
- JSONL:`user://logs/playtest_*.jsonl`,由未触碰的 `level_autoplay` 逐行写;`close_session` 时点随收尾帧。
- 运行态:`panel.set_sim_running` → 浮窗模拟钮置灰;`_running` → 组合根两层调试入口守卫。
### 2.3 断言面盘点与一处静态矛盾(潜伏红——本设计的关键发现)
两个在库断言面钉死了 run 生命周期的语义:
1. **单元套件 `standee_fx_sweep_test.gd`〔wd8〕**(`game/tests/unit/presentation/standee_fx_sweep_test.gd:126-134`):假驱动在**每一局** `run_level` 回调内读 `ctrl.is_running()`,断言 `drv.seen == [true, true]`(两局读数都真);收尾断言 `is_running()` 假+`panel.running` 假+报告非空;且用 `await ctrl.run(2)` 驱动——**run 必须保持可等待(协程),await 它须挂起到整批完成**。
2. **探针 `ui_tune_probe.gd` ⑤**(`game/probe/ui_tune_probe.gd:67-83`):按下模拟钮后以 `while debug_overlay.is_running()` 守卫环逐帧等——**语义=浮窗运行态贯穿整批**,环退出后断言输出含报告四串。
静态推演现码:`_run_one_level` 尾部两行(`panel.set_sim_running(false)`/`_running = false`,:67-68)**每局执行一次**——首局完成即把两个运行态置假。后果:
- wd8 首断言实测必得 `[true, false]` → 红;
- 探针 ⑤ 守卫环在按下返回后立即退出(首局在按下同一同步段内已跑完)→ 输出停在进度串 → 四条输出断言全红。
推断:这两行原本在循环**之后**(run 收尾单次执行),是「循环体提函数」(run() 局部变量 11→10 清偿批,2026-10-26 审计在案该键)搬运时被卷入提取体——wd8 写于 d8 批且当时必绿,可反证。两断言面均不在近期批次的关联档选测覆盖内(套件名与文件名无字面关联、probe_gate --full 非恒跑三保险),潜伏红未暴露。**T0 义务:实跑 standee_fx_sweep 单套件+probe_gate --full 复核现状**(本设计零测试执行,此判断待实跑确认)。
推论:本批若逐字保留现行为,wd8 与探针 ⑤ 必红、批不可绿。**本设计按「run 收尾单次置 false」的还原语义设计**(两断言面共同钉住的语义),该还原作为已申报行为改动进卡(待裁点 1)。
## 3. 方案形态评估
判定轴:帧相位(步进体在帧循环中的执行时点是否与 await 恢复点相同)、步进节律(相邻两步之间是否恰隔一次 process_frame 发射)、run 可等待性(wd8 契约)、键风险(S13 新键)。
Godot 4 帧循环 process 段的迭代序(engine-reference 口径):**① `emit process_frame`**(所有连接的 Callable 同步依次调用;`await process_frame` 的恢复点=此刻挂在信号上的临时连接被调用)→ **② 节点 `_process`**(Timer 节点在此段)→ **③ 帧末**(SceneTreeTimer 超时、延迟删除等)。官方文档:process_frame「Emitted immediately before Node._process is called on every node」。await 恢复点与显式 connect 回调**同处①、同一相位**——这是全部等价性论证的支点。
| 形态 | 帧相位 | 步进节律 | run 可等待 | 键风险 | 裁决 |
|---|---|---|---|---|---|
| **process_frame 信号连接+步进状态机** | 与 await 恢复点同相位(同一 emit 内) | 恒等(常驻连接,每发射恰走一步) | 保留(函数级单次 await 完成信号,见 §4) | 零新键 | **采纳** |
| SceneTreeTimer 链 | timeout 在③帧末——相位不同 | `create_timer(0)` 同帧即燃→节律塌缩;加时长→漂移 | 须循环内 await→键不消 | await-in-loop 残留+每步分配 | 否 |
| Timer 节点 | timeout 在②节点 process 段——相位不同 | wait_time 按秒计,一帧节律不精确 | 须 await timeout 链→键不消 | 键残留+须建 Node 入树(RefCounted 控制器破形、生命周期管理新增) | 否 |
| 递归协程代循环 | 同相位(恢复点内即调下一步) | 恒等 | 天然(await 协程链) | 零键 | 备选否决——n 个并发函数状态链、错误中断语义更浑、可读性差 |
| 线程 / WorkerThreadPool | 脱离主循环 | — | — | 测量确定性破坏+面板 setter 跨线程 | 否(一句话排除) |
godot-master 惯用法对齐:异步操作移出循环、改由信号触发的独立方法承载(其 NEVER-list 第 7 条同款);动态连接用毕即断(第 27 条);逻辑件保持 RefCounted 不引入 Node(When-NOT-to-use-Node);连接前 `is_connected` 守卫(迭代 39「恰好连接一次」先例)。
## 4. 推荐案:process_frame 信号步进状态机
### 4.1 代码结构草图(伪代码级)
```gdscript
signal _batch_finished ## 批量模拟整批收尾完成(无参数;发射时点=报告注入与运行态复位之后;
## 消费方=run() 自身收尾放行——外部 `await 控制器.run(n)` 经此放行)
# run 期活成员(批末失效,下次 run 重写——非无效成员)
var _sim_n := 0
var _sim_i := 0
var _sim_stats := {}
var _sim_autoplay = null
var _sim_logger = null
var _finished := false
func run(n: int) -> void:
_running = true
# …logger 建会话/set_sim_running(true)/进度 0/n/stats 六键束/_autoplay()——逐字保持现码…
_sim_n = n; _sim_i = 0; _sim_stats = …; _sim_autoplay = …; _sim_logger = logger
_finished = false
if n > 0:
var tree: SceneTree = panel.get_tree()
if not tree.process_frame.is_connected(_step_frame):
tree.process_frame.connect(_step_frame) # 恰连接一次守卫(迭代 39 先例)
_step_frame() # 首步同步执行——与 await 形态「第 0 局在调用栈内跑完」对齐
await _batch_finished # 函数级单次 await——不在任何循环体内(键消除的落点)
else:
_finish_batch() # n=0:与 await 形态一致——循环零进,同步收尾零帧
# 每帧恰执行一步(process_frame 发射点回调);步尽次帧收尾
func _step_frame() -> void:
if _sim_i < _sim_n:
_run_one_level(_sim_autoplay, _sim_stats, _sim_i, panel, _sim_logger) # 逐字保持
panel.set_sim_progress("模拟中:%d/%d …" % [_sim_i + 1, _sim_n]) # 逐字保持
_sim_i += 1
else:
panel.get_tree().process_frame.disconnect(_step_frame) # 先断连(防再入)
_finish_batch()
_batch_finished.emit() # 放行 run() 的 await 与外部 await run(n) 的调用方
# 收尾单点(运行态复位还原为「run 收尾单次执行」——待裁点 1 的申报改动落点)
func _finish_batch() -> void:
_finished = true
_sim_logger.close_session()
panel.set_sim_running(false)
_running = false
panel.set_sim_report(HudPresenter.tune_sim_report(_sim_n, panel.atk_pct(), panel.hp_pct(), _sim_stats, _base_hp))
```
要点:
- **await 只剩一处、在函数级**(`await _batch_finished`),文件内不再有任何 for/while——await-in-loop 键从文本形态上根除,且该文件此后对键**结构性免疫**(无循环即无循环内 await)。
- **run 保持协程身份**:含 await 即仍是协程——`_on_tune_simulate` 不 await 调用(返回函数态、丢弃,与现行为逐字一致);wd8 的 `await ctrl.run(2)` 挂起到 `_batch_finished` 发射、恢复时整批已完成——**契约零变化、wd8 断言零改动**。
- `_run_one_level` 除删去尾部两行(回归还原,见 §2.3)外逐字保持(含 5 参签名,参数≤5 临界不恶化)。
- 头注按 code-conventions §2.1 增 `_batch_finished` 契约一行(现 4 行→5 行,S10 ≤8 行内)。
- 收尾段内四语句的相对次序(close/两运行态复位/报告)对两个断言面均不可观测(wd8 在整批完成后检查、探针在帧边界检查)——按草图次序落码即可,实现期查 git 历史原序可进一步对齐。
### 4.2 帧相位等价性论证(逐步对齐表)
以 n=3 为例,旧(await)新(状态机)两形态逐帧对齐:
| 时点 | 旧形态 | 新形态 | 等价 |
|---|---|---|---|
| 调用点同步段(按钮信号链内) | setup→第 0 局→进度 1/n→await 挂起 | setup→connect→`_step_frame()` 跑第 0 局→进度 1/n→`await _batch_finished` 挂起 | 同一调用栈、同一同步段、语句同序 |
| 第 1 帧 ①(emit process_frame) | 恢复→第 1 局→进度 2/n→再 await | `_step_frame` 回调→第 1 局→进度 2/n | **同一 emit、同一相位**(帧首、节点 _process 之前) |
| 第 k 帧(1≤k≤n−1) | 第 k 局 | 第 k 局 | 步进体=同一函数 `_run_one_level`+同一进度语句 |
| 第 n 帧 ① | 恢复(循环自然尽)→close→报告→返回 | `_step_frame` else 分支→断连→close→复位→报告→emit→run 返回 | 终止语句同帧同相位,恰多等的一帧来自旧形态末次 await——草图 else 分支精确复刻 |
| n=0 | 循环零进,同步收尾,不挂起 | else 直调 `_finish_batch`,同步收尾,不挂起 | 一致(含 run 返回形态:均非挂起返回) |
连接次序论证(与同信号其他订阅者的相对序):产品码内 process_frame 订阅面仅两处——本件与 `settlement_fx_view.gd:94` 的一次性 await(等文本度量,战斗结算呈现期,与模拟批不并存;T0 实查复核)。探针 ⑤ 守卫环的 await 连接时点恒晚于本件(按下返回后才首轮 await)→ 每帧步进先于探针复查,两种形态一致。旧形态每次恢复后重新挂尾连接、新形态常驻连接——对「连接时点早于 run()」的订阅者,两形态中本件都排其后;差异仅在「run 途中新增订阅者」的假想场景,现库为零(已列风险 3)。
## 5. 等价性证明清单(四判据)
| 判据 | 结论 | 依据 |
|---|---|---|
| ① 步数与帧数一一对应不变 | **严格成立** | §4.2 对齐表:第 i 局恒在(调用点 i=0/第 i 帧发射点 i≥1),收尾恒在第 n 帧发射点,n=0 退化路径一致 |
| ② 每步工作量与执行时点不变(同相位) | **严格成立** | 步进体逐字同一函数;执行点同为 emit process_frame(①段,帧首)——await 恢复点与信号回调在引擎内同处一列连接、同一 emit 内被同步调用 |
| ③ 终止条件与终止时读数不变 | **严格成立** | 终止=i 走满 n(else 分支↔循环自然尽);终止时语句(close/复位/报告)同帧执行 |
| ④ 输出逐字不变 | **成立+执行化验证** | 报告=未触碰的 `tune_sim_report(stats)`;stats 为种子纯函数(帧时点不进值);JSONL 由未触碰的 autoplay 写、close 时点同帧——逐位一致由双实现对拍执行证明(见下) |
执行化验证=**双实现对拍**(test-conventions §5.3「重放:改前改后配对」词汇,非变异类):新增配对套件——`game/tests/` 面置**遗留形态夹具**(现 run() 的 await 形态逐字副本,尾部两行按还原语义置收尾;tests/ 不在 S13/S8 扫描面,夹具内循环 await 合法在册);同一假面板+确定性假驱动分别驱动两形态,断言五行全等:每局调用时记录的**帧索引序列**(直接证明「每步恰一帧、相位同点」——把 §4.2 的论证变成当轮真实执行证据)、驱动读到的运行态序列、进度串序列、stats 六键逐位、报告全文逐字。
**已声明缺口(不能严格证明的点)**:
1. **脚本错误中断路径**:GDScript 无错误陷阱 API。旧形态:步进体内脚本错误→协程死亡→静默停摆(运行态永真、报告永不落)。新形态:错误中断 `_step_frame`→计数未推进→次帧重试同一步(确定性错误→逐帧重试,探针 1800 帧守卫可抓;生产态=持续重试的可见坏,与旧「静默卡死」同级不同形)。此差异不可消除,接受并文档化(待裁点 3);测量批遇错本就整批作废,两形态都不产出可用读数。
2. **微观连接序对未来订阅者**:若未来有人在 run 途中向 process_frame 新增连接,常驻连接与挂尾连接的相对序会分叉——现库零此场景,登记为实现期 T0 实查项+代码评审注记。
## 6. gdstyle 键账预期(逐规则核对)
| 规则 | 预期 | 依据 |
|---|---|---|
| quality/await-in-loop(裸键,本案标的) | **消除** | 全文件零 for/while,await 仅函数级一处 |
| quality/allocation-in-loop | 不命中 | 文件零循环;`PlaytestLogger.new()` 在 run 顶层(现状即如此,非循环内) |
| max-function-length ≤50 | 达标 | run 约 22 行、`_step_frame` 约 12 行、`_run_one_level` 删两行后约 10 行 |
| max-branches ≤8 | 达标 | `_step_frame` 一个 if/else;run 一个 if/else |
| max-local-variables ≤10 | 达标 | run 局部 3(logger/stats/autoplay——余者转成员);`_step_frame` 局部 1 |
| max-returns ≤6 | 达标 | 全 void |
| max-parameters ≤5 | 达标(临界持平) | `_run_one_level` 5 参逐字保持;新函数 0 参 |
| max-class-variables ≤15 | 达标 | 现 5+新增 6 成员=11(`_batch_finished` 信号若计入则 12)——成员皆 run 期活承载,非凑数袋 |
键账流程:键消失后闸判定即绿(消失键不参与红判定);基线死键由批末用户人工 `--refresh` 自然清去(16→15,降幅 <10%,不需 `--force`)。
## 7. 行为零变化风险点
1. **测量读数分布**:stats/JSONL/报告是 (种子, atk, hp) 纯函数、帧时点不进值——结构上零漂移;执行证据=双实现对拍逐位+探针 ⑤ 输出四串断言。
2. **探针断言面**:⑤ 守卫环语义依赖浮窗运行态贯穿整批——本设计还原该语义(§2.3)→ 探针由「静态推演红」转绿;探针其余段(①–④、⑥–⑩)零触碰。
3. **发布态零影响**:浮窗角标 `_debug_entry` 门控(`main.gd:200`,发布态恒隐、浮窗无 UI 开启口——系统测试与冒烟既有断言面在役);tune_sim 全链路发布态不可达,本改动不触任何发布路径。
4. **生命周期**:控制器由 `main._tune_sim` 应用期持有(RefCounted 不因帧回调悬挂);`is_connected` 守卫防信号绕过 disabled 的二次 run 造成双连接双步进;收尾先断连防再入;`_batch_finished` 发射时 run 自身的 await 连接恒在位(不存在零听众发射——n=0 路径不经 emit)。
5. **并发暴露面**:`_on_tune_simulate` 无 is_running 早退(现状即无,本设计不新增——守卫=按钮置灰〔还原后贯穿整批〕+两层调试入口 is_running 互查);与还原前相比,运行态中途置假的再入洞被顺手封死(还原语义的附带收益,申报面见待裁点 1)。
## 8. 验收载体
| # | 载体 | 判什么 | 入闸 |
|---|---|---|---|
| 1 | T0 实跑复核:standee_fx_sweep 单套件+probe_gate --full | 确认 §2.3 潜伏红现状(改动前读数) | 开工首步 |
| 2 | wd8 三断言〔wd8〕(既有,零改动) | 每局运行态真/收尾运行态假+报告注入/run 可等待——**卡内须显式申报该套件**(套件名与涉案文件无字面关联,diff 推导可能漏选) | run_all 关联档 |
| 3 | 双实现对拍配对套件(新增,§5) | 帧索引序列/运行态序列/进度串/stats 逐位/报告逐字——五面对拍 | run_all 关联档 |
| 4 | ui_tune_probe ⑤(既有,零改动) | 真实 UI 全链路 N=10:守卫环完成+报告四串+运行期钮禁用 | probe_gate --full |
| 5 | 批末 `run_all_gates.sh --code` | 所选域全绿;static_scan S13 对账确认 await 键消失、无新键 | 批末 |
## 9. 估时
| 项 | 估时 |
|---|---|
| T0 现状复核(两断言面实跑) | 0.25 工作日 |
| 控制器重构(§4.1 草图落码+头注契约行) | 0.25–0.5 工作日 |
| 双实现对拍(夹具+配对套件) | 0.25–0.5 工作日 |
| 孤立验证+批末代码域闸+键账对账 | 0.25 工作日 |
| **合计** | **1–1.5 工作日** |
## 10. 待裁点清单
1. **潜伏红处置(必裁)**:现码 `_run_one_level` 尾部两行与 wd8/探针 ⑤ 静态矛盾(§2.3)。推荐:随批还原「run 收尾单次置 false」,作为已申报行为改动进卡(T0 实跑确认现状后施工);该还原同时封死运行中途的模拟再入洞。若用户掌握「每局置 false 系有意行为」的背景,请指出——但该选择下 wd8 与探针 ⑤ 必红、批不可绿,需另行裁决断言面处置。
2. **双实现对拍是否入批**:推荐入(把帧相位等价从纸面论证变成当轮执行证据;tests/ 面不入 S13 扫描面、夹具内循环 await 合法)。备选:仅靠 wd8+探针 ⑤(省一测试件,相位等价无执行化证据)。
3. **错误路径差异接受度**:脚本错误下「逐帧重试」对「静默停摆」的差异(§5 缺口 1)不可消除。推荐:接受并文档化。备选:`_on_tune_simulate` 加 is_running 早退守卫兜底——+1 行为面、system_test〔wd7〕前置断言随之需复核,代价大于收益。
## 11. 关联文档同步面(随批义务备查,经反哺点通道落文)
- `design/engineering/architecture/architecture.md`「测试与质量域」:S13 测量面豁免登记首条(2026-10-27 tune_sim await)**注销**;同段其余条目(battle_orchestrator 第三条等)不在本案范围。
- `tools/gdstyle_baseline.txt`:await 裸键随批末人工 `--refresh` 消失(禁实现批中途 refresh)。
- `game/tests/README.md` 能力清单:配对套件入表归写卡段(实现批禁写该文件)。
@@ -0,0 +1,171 @@
# 设计——站桩状态件拆分(S13 max-class-variables 键清偿)
设计讨论段过程稿 v1.0——待主会话汇总裁决。
## 0. 一句话结论
把 `_StandeeState` 沿「身份命值 / 资产演出」职责缝拆成两个独立文件类(12 + 11 成员),三个演出探针计数器移到宿主;宿主类变量 19 → 10,两新文件各自 ≤15,站桩外部契约与呈现时序零变化。
**先纠一个前提**:主会话实测的「_StandeeState 成员 13 个、独立成文件即 13≤15 合规」不成立。实测(gdstyle v0.4.0 二进制逐案验证):该文件 `_StandeeState` 真实成员是 **25 个**;基线键账 19 是 gdstyle 一个解析缺陷造出的截断读数——内嵌类里**带行尾注释的属性声明**(`var _intent_scale := 1.0: # 注释`)会把该内嵌类的成员计数截断,其后的成员全部漏记进宿主。把 `_StandeeState` 原样拆成独立文件,键账是 25 不是 13,照样红。
## 1. 机检事实(gdstyle v0.4.0 计数语义,实测五案)
| 案 | 内容 | gdstyle 读数 | 结论 |
|---|---|---|---|
| 1 | standee_body.gd 原文件 | 外层类 19(1:1),内嵌类零命中 | 19 = 顶层 6 + 漏记内嵌 13 |
| 2 | 内嵌 25 成员抽成独立文件 | 该类 25 | **拆出即 25 > 15,方案①不成立** |
| 3 | 内嵌类全普通 var(无属性) | 内嵌类单独报 25,宿主 6 不报 | 无属性时内嵌类独立计数,正常 |
| 4 | 内嵌类 12 普通 var + 带行尾注释属性 + 12 普通 var + 宿主 6 | 宿主 18(6+12 漏记) | 属性行尾注释 = 截断点 |
| 5 | board_cell_unit.gd(旁证) | 基线 30 = 顶层 6 + 漏记 24 | 同一规则,账目吻合 |
有效规则:**类各自计数;内嵌类遇到「属性声明 + 行尾注释」即成员计到该属性为止,其后成员漏进宿主**。standee_body.gd 的截断点 = `_intent_scale`(37 行),其后 13 个成员(`_glyph_rot` 起到 `_flash_count`)漏进宿主:6 + 13 = 19。board_cell_unit.gd 截断点 = `edge`(30 行),6 + 24 = 30,同款吻合。
含义:不动内嵌形态,把三行属性声明移到内嵌类末尾(或删其行尾注释)就能让账变回「宿主 6 / 内嵌 25」——但内嵌 25 仍超阈。**真正的债是状态件 25 个成员本身**,任何合规解都必须把它拆成 ≥2 个类或砍到 ≤15。
## 2. 现状成员全列(31 个:宿主 6 + 状态件 25)
### 2.1 宿主顶层(6 个——全部有外部冻结契约)
| 成员 | 类型 | 写入方 | 读取方 |
|---|---|---|---|
| `st` | _StandeeState | 成员初始化器;`_init` 灌 _owner/_hero_state/_kind | 宿主全部内部函数经 `st.X` 读写;**外部零直访**(全仓 grep 证实) |
| `box` | Vector2(150,176) | battle_stage_view `_sync_layout`(198/208 行);fx_probe 三处 | `_place_portrait`/`_portrait_rect`/`_pedestal_rect`/`content_rect`/`_draw_*` 五域;battle_stage_view 两个 center 出口读 |
| `font` | Font | battle_stage_view `set_world_font`(232/235 行) | `_efont` |
| `slot_row` | StatusSlotRow | battle_stage_view(140/265 行) | `set_effects`/`set_effects_cleared_feedback`/`mark_dead`/`_revive`;battle_stage_view `_sync_companions`;ui_probe/ui_click_probe |
| `_intent_charge` | bool | `set_pending`(源码锚行 194)、`mark_dead` | `_play_intent_motion`;standee_fx_sweep_test 直读(103 行) |
| `has_world_hp_bar` | true 常量 | 无(声明即终值) | ui_probe 141 行 |
### 2.2 _StandeeState(25 个)
| # | 成员 | 类型 | 写入方 | 读取方 |
|---|---|---|---|---|
| 1 | `_owner` | Node2D | `_init` | 三个属性 setter(queue_redraw) |
| 2 | `_kind` | String | `_init` | `set_hero_state`/`_revive`/`_portrait_glyph`/`_draw`/`_draw_hp_bar`/`_portrait_color` |
| 3 | `_portrait_sprite` | Sprite2D | `set_portrait_texture`/`_place_portrait` | 同函数 + `_draw_portrait`/`set_hero_state`/`_settle_hero_idle`/`portrait_probe` |
| 4 | `_portrait_tex` | Texture2D | `set_portrait_texture` | 仅 `_place_portrait` 判空(与 sprite.texture 冗余) |
| 5 | `_hero_tex` | Dictionary | `register_hero_state` | `set_hero_state`/`_settle_hero_idle`/`hero_state_probe` |
| 6 | `_hero_state` | int | `_init`/`set_hero_state`/`_settle_hero_idle`/`_revive` | `set_hero_state`/`hero_state_probe` |
| 7 | `_state_revert_tw` | Tween | `set_hero_state` | 同函数(重启先杀) |
| 8 | `_order` | int | `_set_order` | `_draw_hp_bar`(顺位徽标) |
| 9 | `_intent_glyph` | String | `set_pending` | `intent_probe`/`_draw_intent_badge` |
| 10 | `_intent_col` | Color | `set_pending` | `_draw_intent_badge` |
| 11 | `_intent_visible` | bool | `set_pending`×3/`mark_dead` | `intent_probe`/`_draw_intent_badge` |
| 12 | `_intent_scale` | float(属性→重绘) | `_play_intent_motion` 的 tween | `_draw_intent_badge` |
| 13 | `_glyph_rot` | float(属性→重绘) | `_play_intent_motion` 的 tween | `_draw_portrait` |
| 14 | `_display_name` | String | `set_data` | `display_name`/`name_line_probe`/`_draw_armor_name_line` |
| 15 | `_enemy_id` | String | `set_data` | `enemy_id`(d12 解析主键) |
| 16 | `_armor` | String | `set_data` | `name_line_probe`/`_draw_armor_name_line` |
| 17 | `_dead` | bool | `mark_dead`/`_revive` | `set_data`/`set_hero`/`is_dead`/`flash_hit`/`_portrait_glyph`/`set_pending`/`mark_dead` |
| 18 | `_hp_max` | float | `set_data`/`set_hero` | `_set_hp`/`_draw_hp_bar` |
| 19 | `_hp_cur` | int | `_set_hp` | `_draw_hp_bar`(数字即时值) |
| 20 | `hp_value` | float(属性→重绘+clamp) | `_set_hp` 的 tween | `_draw_hp_bar`(条宽) |
| 21 | `_hp_tween` | Tween | `_set_hp` | 同函数(重启先杀) |
| 22 | `_hp_flash_tw` | Tween | `flash_hit` | 同函数(重启先杀;回色读 `_dead`) |
| 23 | `_death_shards` | int | `_spawn_death_shards` | `fx_probe` |
| 24 | `_hit_sparks` | int | `_spawn_hit_spark` | `fx_probe` |
| 25 | `_flash_count` | int | `flash_hit` | `fx_probe` |
### 2.3 外部触面(拆分边界 = 全部保持)
写状态的外部方只有 battle_stage_view(box/font/slot_row 三 var + 全部经方法)与 fx_probe(box 三处);读面 = 两个 center 出口、六个 probe 出口、`_intent_charge` 测试直读、theme_projection_test 三条源码锚断言(679–683 行:`GOLD_BRIGHT` 顺位墨、`HERO_BLUE_BRIGHT`/`GOLD_BRIGHT` 护甲墨、`_intent_charge = pid in CHARGE_SKILL_IDS`)——三条锚所在函数(`_draw_hp_bar`/`_draw_armor_name_line`/`set_pending`)**全部留在宿主且锚行逐字不动**。
## 3. 方案形态评估
### 方案 A(主会话原①):_StandeeState 升格独立文件
**淘汰。** 实测拆出即 25 > 15(机检事实案 2),不削成员不合规;而削到 15 需砍 10 个,全是承重成员(见方案 B 的砍不动论证),等于把 A 逼成 B+拆分。
### 方案 B(主会话原②):原地真实削减 4 个(19→15)
**淘汰。** 被计入 19 的是宿主 6(全部外部冻结:box/font/slot_row/has_world_hp_bar 探针契约面、_intent_charge 源码锚成员、st 本体)+ 漏记的 13(`_glyph_rot`/身份三元组/`_armor`/`_dead`/`_hp_max`/`_hp_cur`/`hp_value`/两条 tween 槽/三个探针计数)。逐个过堂:身份三元组合一个字典 = 丢类型的凑计数袋(违反 code-conventions §1.1①⑤);三个探针计数合一个字典同理且键变字符串;`_hp_cur` 与 `hp_value` 一个是即时目标一个是 tween 滞后值,不可互推;两条 tween 槽的重启语义各自独立;`_glyph_rot` 是 tween 目标属性。**诚实削减余额 ≈ 0(顶多 `_portrait_tex` 一个冗余成员)**,−4 凑不出来。
### 方案 C(推荐):沿职责缝拆两个独立文件类 + 三个演出探针计数器移宿主
25 个成员按领域分三堆,各自命得出名字:
**新件一 `game/presentation/views/standee_vitals.gd`「站桩身份与命值态」(12 成员)**
- 成员:`_owner`、`_kind`、`_display_name`、`_enemy_id`、`_armor`、`_order`、`_dead`、`_hp_max`、`_hp_cur`、`hp_value`(属性→重绘+clamp)、`_hp_tween`、`_hp_flash_tw`。
- 职责语句:站桩是谁、活没活、血量几何的唯一账面——身份五元组(种类/名/id/护甲/顺位)+ 存活位 + 血量账与两条血量系 tween 槽。
- 单一变化原因:战斗结算快照投影的字段形态(set_data/set_hero 投影什么它记什么)。
- 迁入行为(防纯数据袋):`apply_hp(hp)`=原 `_set_hp` 整体搬移(clamp、重启 `_hp_tween`、tween 写 `hp_value`);`hp_value` 属性 setter 逐字。`_hp_flash_tw` 归此件:`flash_hit` 的回色分支读 `_dead`,与存活位同域。
- extends **RefCounted**(godot-master 判据:瞬态逻辑数据包、随宿主生灭、零序列化零 Inspector——Resource 的共享/.tres 语义此处零收益)。
**新件二 `game/presentation/views/standee_motion.gd`「站桩资产与演出态」(11 成员)**
- 成员:`_owner`、`_portrait_sprite`、`_portrait_tex`、`_hero_tex`、`_hero_state`、`_state_revert_tw`、`_intent_glyph`、`_intent_col`、`_intent_visible`、`_intent_scale`(属性→重绘)、`_glyph_rot`(属性→重绘)。
- 职责语句:站桩的资产挂点与一次性演出——立绘精灵落位与换装、主角三态注册与 0.35s 短驻回归、意图徽标弹入动画。
- 单一变化原因:美术素材落位规则与演出编排设计(立绘 48px 档、三态帧档、§4.6.4 三档运动签名)。
- 迁入行为:`set_portrait_texture(tex, flip, box)`(原函数整体搬移,box 由宿主传入——box 是宿主冻结 var);`register_hero_state`;`apply_hero_state(state, is_hero)`(原 `set_hero_state`,唯一改形 = `st._kind != "hero"` 早退改参判 `is_hero`——早退序逐字);`reset_state()`(原 `_revive` 内「`_hero_state = RESET` 强制绕同态早退」两拍的 encapsulation);`play_intent_motion(charge)`(原 `_play_intent_motion`,`_intent_charge` 改参传入);`state_probe()`。
**宿主 standee_body.gd 保留(类变量 10)**
- 五个冻结 var 原位 + `st`(vitals 实例,**名字保留**——约 40 处引用与 tween 路径串 `"st.hp_value"` 零改)+ `mo`(motion 实例,命名待裁)+ 三个演出探针计数器 `_death_shards`/`_hit_sparks`/`_flash_count`。
- 计数器归宿主的内聚依据:唯一写入方 = 宿主演出函数(`_spawn_death_shards`/`_spawn_hit_spark`/`flash_hit`),唯一读取方 = 宿主 `fx_probe()` 出口——仪器跟着驱动它的函数走;同时给 vitals 让出余量(15 满额 → 12)。
- 保留函数:`_init`、`set_data`/`set_hero`(跨两域编排)、`set_effects`/`set_effects_cleared_feedback`(slot_row 域)、`set_pending`(分档计算 + 源码锚行,字段写改指 mo)、`mark_dead`/`flash_hit`/`play_hit_impact`/`_spawn_*`/`_revive`(跨域演出编排)、`_draw` 全族与几何函数(绘制是宿主 CanvasItem 职责)、六个 probe/display_name/enemy_id/is_dead 出口(外形零变,体内前缀改指)。
接线形态:**保持 `_owner: Node2D` 直回引,不改 Callable 注入、不改信号**。依据:①现状形态,setter 体逐字(`_owner.queue_redraw()`)即重绘时序零漂移——tween 每帧写属性 → setter 同步触发,正是「弹入动画逐帧重绘」的承载;②Node 非 RefCounted,state→宿主回引无环引用泄漏(与 BattleFxHost 场景不同——那边是两 RefCounted 互持才禁双向);③queue_redraw 是 CanvasItem 公开 API,不属「件回调宿主私有族」禁令射程。
淘汰的变形:**两内类同居主文件**(diff 更小)——依赖成员排序躲解析缺陷(属性须排各类末尾),未来任何人往类中部加一个带注释属性就静默漏账(基线恶化闸要到 19→29 才红,9→15 区间六格漂移无感知);两新件独立文件则完全脱离该缺陷的作用面。**改属性行注释形态躲缺陷**同理淘汰——赌解析器怪癖不如离开怪癖作用面。
## 4. 写入路径对账(外部写入方零改动清单)
| 外部触点 | 现状 | 改后 |
|---|---|---|
| battle_stage_view 140/265 行 `slot_row =` | 宿主 var 直写 | 不变(var 原位) |
| battle_stage_view 198/208 行 `box =` | 宿主 var 直写 | 不变;box 仍是无 setter 普通 var,**不触发重绘是现状语义**(重排后的呈现由后续 set_data/字体注入等 queue_redraw 承载),设计不补挂不拆挂 |
| battle_stage_view 232/235 行 `font =` + `queue_redraw` | var 直写 | 不变 |
| battle_stage_view 132–138/267 行 set_portrait_texture/register_hero_state | 宿主方法 | 同名宿主方法保留(1–2 行转调 mo,box 作参),battle_stage_view 一行不改 |
| battle_stage_view 313/316/388 行 set_hero_state | 宿主方法 | 同名保留,转调 `mo.apply_hero_state(state, st._kind == "hero")` |
| battle_stage_view 333–335 行 play_hit_impact/flash_hit/mark_dead | 宿主方法 | 不变(宿主保留) |
| hud_presenter 77/78/137/162/163 行 set_hero/set_effects | 宿主方法 | 不变 |
| fx_probe/ui_probe/ui_struct_probe/ui_click_probe 全部读数 | 六 probe 出口 + box/has_world_hp_bar/slot_row | 出口外形零变(fx_probe 改读宿主计数器、intent_probe/portrait_probe 前缀改指 mo) |
| standee_fx_sweep_test 103 行 `s1._intent_charge` | 宿主 var 直读 | 不变(成员原位) |
| 内部 tween 路径串 | `"st.hp_value"`(vitals 成员名保留 → 零改);`"st._intent_scale"`/`"st._glyph_rot"` | 改 `"mo._intent_scale"`/`"mo._glyph_rot"`(搬进 mo 的同一函数体内) |
重绘/重排时序零变化论证:三个属性 setter 体逐字(`_owner.queue_redraw()`);tween 目标路径只换持有者前缀、写序不变;box/font/slot_row 写入面与现状同拍同序(battle_stage_view 一行不改 = 接线时序恒等)。
## 5. gdstyle 键账预期(逐文件 ≤15)
| 文件 | 类变量实数 | 基线对账 |
|---|---|---|
| standee_body.gd | **10**(5 冻结 + st + mo + 3 计数器) | 基线登记 19,实数 10 < 19 = 改小不红 |
| standee_vitals.gd | **12** | 无基线键,12 ≤ 15 = 零命中 |
| standee_motion.gd | **11** | 无基线键,11 ≤ 15 = 零命中 |
函数级键:**零函数迁入新键**——六个搬移函数逐字搬运(仅前缀与两处改参),长度/分支/局部/返回/参数全在原值内(最大者 `apply_hero_state` 4 参数 4 分支);宿主新增转调壳 1–2 行零键。基线不随批刷新(改小即绿;收账归后续全清批人工 `--refresh`,须用户批准——与 battle_orchestrator 50→38 先例同口径)。
## 6. 行为零变化风险点
1. **tween 属性路径串**:`"st.hp_value"`/`"mo._intent_scale"`/`"mo._glyph_rot"` 拼错 = 运行时属性找不到、动画静默失效。兜底:fx_probe 段的意图弹入断言、hero_state_probe 三态断言、fx_shot_probe 帧取证都在闸内。
2. **hero 三态机改形两处**(`is_hero` 参数化、`reset_state` 封装):早退序与 RESET 哨兵语义必须逐拍对齐——`set_hero_state` 的三连早退(非主角/同态/未注册)条件序不变;`_revive` 强制回 idle 两拍改两调用、顺序不变。
3. **`_hp_flash_tw` 回色读 `_dead`**:flash_hit 留宿主、跨读 `st._dead`,与现状同一拍。
4. **源码锚三条**(theme_projection 679–683):锚行所在函数留宿主且锚行逐字,改造只动同函数其他行的 `st.` 前缀。
5. **成员初始化时序**:st/mo 于成员初始化器构造、`_init` 灰 `_owner`/`_kind`——与现状同形,构造期零属性写早于 `_owner` 赋值。
6. **counters 迁移**:三个计数器无 setter 无重绘语义,纯读写易位;`fx_probe()` 返回键名逐字。
## 7. 验收载体建议(真实入口)
| 载体 | 断言面 | 绑定 |
|---|---|---|
| static_scan S13(gdstyle_gate 回执) | 「基线外新违规 0、恶化 0」+ standee_body 键账 10 | 被测改动本身的直接判据(键清偿),grep 回执可定位 |
| run_all 关联套件 | standee_fx_sweep_test(_intent_charge 直读、fx_probe().flash_count、enemy_id 解析);theme_projection_test(三条源码锚) | 拆分后宿主成员与源码锚零变 |
| probe_gate --full | fx_probe 段(碎裂 5/火花 6/闪白计数/三态切换/死亡残位 idle);ui_probe(intent_probe 三档、name_line_probe、portrait_probe、has_world_hp_bar、slot_row);ui_struct_probe ⑥standee 出框断言;ui_click_probe slot_row tooltip | 外部契约面逐一可达 |
| render_gate | fx_shot_probe 取证 + 战斗舞台 golden | 站桩呈现像素级零变化 |
| run_smoke / run_system_test | 恒跑保险 | 活性 |
对照表口径:本批为非行为改动(结构重组)逐行申报 + 既有断言面全绿作呈现零变化证明;S13 键账数字即键清偿判据,无需新增断言。
## 8. 估时
写卡段不计。实现批(worktree):宿主改造与函数搬移 ≈2h;两新件与头注(≤8 行、无反向链接)≈0.5h;闸跑(S13 + run_all 关联 + probe_gate --full + render_gate + 冒烟/系统)与取证 ≈1.5h;对照表与实现报告 ≈0.5h——**合计 ≈0.5 工作日**。
## 9. 反哺点(预登记)
1. architecture.md「模块归属·呈现层」表登记两新件(standee_vitals/standee_motion,各一句职责)。
2. 相邻发现(不属本批):board_cell_unit.gd 内嵌类 `_CellPaintState` 27 成员受同一 gdstyle 解析缺陷截断(基线 30 = 6 + 24 漏记),属 gdstyle 存量债同族,是否立卡清偿待裁。
3. `_portrait_tex` 与 `_portrait_sprite.texture` 冗余(单一事实源可归一)——本批不动(零变化优先),留债册。
## 10. 待裁点
1. **方案形态确认**:推荐案 C(两独立文件 + 计数器归宿主)vs 淘汰案 B(原地 −4,凑不出)与两内类变形(赌解析缺陷)。前提修正:原①「拆出即合规」实测不成立。
2. **探针计数器落点**:宿主(推荐——写入方/读取方同在宿主,vitals 让出余量)vs vitals 内(15 满额零余量,快照投影类加一个字段即红)。
3. **motion 实例成员命名**:`mo`(推荐,短、域对)vs 其他;vitals 实例沿用 `st`(引用面与 `"st.hp_value"` 路径串零改)。
4. **基线收账时点**:本批不刷新(改小即绿,先例口径)vs 随批人工 `--refresh`(须用户批准)。
@@ -0,0 +1,236 @@
# 设计——组合根注册面(S13 真修复:main.gd 类变量归 15)
状态行:设计讨论段过程稿 v1.0——待主会话汇总裁决(2026-10-29)
## 0. 背景与判据来源
用户 2026-10-29 裁「不允许任何豁免」:main.gd 的 S13 键(`game/presentation/main.gd|quality/max-class-variables = 49`,阈值 15)必须真实修复,architecture 在册的 S13 豁免登记(「组合根类变量面即探针契约面」)随批注销。主会话已点头的主方向:把屏壳+覆盖层一级句柄收进一个「注册面/接口层门面」件,探针从 `main.X` 改走注册面地址。
本设计的判据来源:
- code-conventions §1.1 拆分边界纪律五条(职责轴拆分、集体身份、过阈值只作发现信号、拆不出即停卡、禁无效成员别名)与 §1.3 依赖单向(组合根=main.gd+其构造注入的组合根拆件)。
- architecture「呈现层装配与会话生命周期」「模块归属·呈现层」「测试与质量域」三节。
- godot-master 的 Node 取舍判据("Use RefCounted for logic objects and data containers. Reserve Node for things that must exist in the scene tree")与信号卫生条款;本仓既有先例:ConfigDB 迭代 41 用「三张域容器」解决同构的成员数超标问题,ModalFlow/各交互控制器均为持引用的 RefCounted。
- 源码实查:main.gd 915 行 49 var(实数复核);探针+system_test 的 `main.X` 成员引用 867 处(机械统计,扣除 main.tscn/get_node/get_viewport 等非成员形态);四个流程承接件 `root.X` 引用约 350 处。
## 1. 注册件形态
### 1.1 推荐形态:一个门面+三个域容器(四文件,全 RefCounted,不挂树)
| 新件 | 文件 | extends | 类变量数 | 职责一句话 |
|---|---|---|---|---|
| **界面注册面** | `game/presentation/ui_registry.gd` | RefCounted | **3** | 呈现层界面元素清单:持有三域容器、LAYER_CONTRACT 合同表、`node(key)` 合同寻址、`apply_layer_contract(root)` 唯一落位口 |
| **屏壳与全屏容器域** | `game/presentation/ui_screens.gd` | RefCounted | **9** | 三屏壳、屏内一级子层、全屏态、浮层角标、排播泵节点的句柄册 |
| **战区视图域** | `game/presentation/ui_battle.gd` | RefCounted | **5** | 战斗屏 A–D 区视图与结算演出句柄册 |
| **模态层域** | `game/presentation/ui_modals.gd` | RefCounted | **14** | 十三个模态覆盖层句柄+ModalFlow 栈编排(登记、开合、锁谓词) |
main.gd 新增一个成员 `ui`(类型 UiRegistry),探针与流程件的新地址形如 `main.ui.modals.level_screen`、`main.ui.battle.board_zone`、`main.ui.screens.menu_screen`。
**为什么是三容器而不是一件**: gdstyle 阈值 15 是按文件的,句柄 27 个+flow/row_pacer 共 29 个进一个文件必然超阈、又要开新基线键=留债,违用户裁决出口。 分组轴不是拍脑袋切的——它就是 LAYER_CONTRACT 的 kind 轴(modal/shell/state/float/node)加上 battle.md 的战区轴,三组身份先于本批存在(详见 §2),是「按既有领域轴收拢」,不是「为凑 15 而切」。
**为什么是 RefCounted 不是 Node**: 注册面是纯句柄容器+合同数据,零 `_process`、零绘制、零树位需求——按 godot-master 判据「Node 只留给必须在场景树里存在的东西」应取 RefCounted。 它持有的节点(屏壳/覆盖层/视图)都是 `_build_ui` 一次装配、随 main 同生死的常驻件,不含任何会话期 `queue_free` 对象(局态在 run_scope,留 main),RefCounted 持 Node 引用无悬挂风险;本仓先例充分:ModalFlow、TreasureInteractController、CampShopPresenter、DebugSession、SettingsStore 全是持引用 RefCounted。 若做成 Node 挂树,反而要回答「它占哪个树位」「进不进 LAYER_CONTRACT」「冒烟 V1d 怎么看它」三个新问题,纯增噪声。
**class_name**: 四件均给 class_name(UiRegistry/UiScreens/UiBattle/UiModals),main 里 `var ui: UiRegistry` 类型化;随批跑一次编辑器扫描刷新全局类缓存+同步 .uid(code-conventions 语言纪律 5)。
### 1.2 逐成员去向对账表(main.gd 现有 49 var,三档:进注册面/留 main/消灭)
**进注册面(28)**——成员名在容器内去下划线(变为公共地址面,与 LAYER_CONTRACT 键名对齐):
| # | 现 main 成员 | 去向 | 依据 |
|---|---|---|---|
| 1 | `_menu_screen` | `ui.screens.menu_screen` | 屏壳(kind=shell) |
| 2 | `_camp_screen` | `ui.screens.camp_screen` | 屏壳 |
| 3 | `_adventure_screen` | `ui.screens.adventure_screen` | 屏壳 |
| 4 | `_start_overlay` | `ui.screens.start_overlay` | 菜单屏内一级子层 |
| 5 | `_battle_root` | `ui.screens.battle_root` | 冒险屏内一级子层 |
| 6 | `_inter_overlay` | `ui.screens.inter_overlay` | 全屏态(kind=state) |
| 7 | `_inter_label` | `ui.screens.inter_label` | 过场层附随标签 |
| 8 | `debug_badge` | `ui.screens.debug_badge` | 全局浮层(kind=float) |
| 9 | `_row_pacer` | `ui.screens.row_pacer` | LAYER_CONTRACT kind=node 注册层(节点本体仍 add_child 到 main,注册面只持引用,树位零变化) |
| 10 | `stage` | `ui.battle.stage` | 战斗屏 B 区 |
| 11 | `log_view` | `ui.battle.log_view` | B 区明细带 |
| 12 | `info_panel` | `ui.battle.info_panel` | C 区 |
| 13 | `board_zone` | `ui.battle.board_zone` | D 区(含 toast/fx/board_view/ap_bar) |
| 14 | `settlement_fx` | `ui.battle.settlement_fx` | 结算演出覆盖层 |
| 15 | `legend_overlay` | `ui.modals.legend_overlay` | 模态层(kind=modal,13 件全集) |
| 16 | `node_overlay` | `ui.modals.node_overlay` | 模态层 |
| 17 | `level_screen` | `ui.modals.level_screen` | 模态层(host=adventure_screen,TAG_SCREEN_STATE) |
| 18 | `curio_overlay` | `ui.modals.curio_overlay` | 模态层 |
| 19 | `_end_overlay` | `ui.modals.end_overlay` | 模态层 |
| 20 | `shop_overlay` | `ui.modals.shop_overlay` | 模态层 |
| 21 | `camp_overlay` | `ui.modals.camp_overlay` | 模态层(shell=camp_screen) |
| 22 | `settings_overlay` | `ui.modals.settings_overlay` | 模态层 |
| 23 | `status_panel` | `ui.modals.status_panel` | 模态层 |
| 24 | `career_overlay` | `ui.modals.career_overlay` | 模态层 |
| 25 | `save_select_overlay` | `ui.modals.save_select_overlay` | 模态层 |
| 26 | `confirm_dialog` | `ui.modals.confirm_dialog` | 模态层 |
| 27 | `debug_overlay` | `ui.modals.debug_overlay` | 模态层(TAG_NON_LOCKING) |
| 28 | `_modal_flow` | `ui.modals.flow` | 模态编排与模态层同域(登记/开合/锁谓词的宿主) |
**留 main(14)+新增(1)=改后 15**:
| # | 成员 | 保留理由 |
|---|---|---|
| 1 | `db` | 核心配置读视图——组合根唯一加载单点读取入口(`_ready` 装配) |
| 2 | `run_scope` | 会话承载节点——架构在册「局态一律住 RunScope,组合根经 run_scope.X 读写」;173 处探针引用零迁移 |
| 3 | `save_manager` | autoload 读视图——architecture 在册「main.save_manager 语义零迁移」 |
| 4 | `meta` | autoload 读视图(同上) |
| 5 | `settings_store` | 系统级设置存储——非界面元素(无视觉面),`_ready` 装配、`_quit_game` 落盘、SettingsOverlay 构造注入 |
| 6 | `logger` | 试玩日志器——组合根埋点面(非界面元素) |
| 7 | `_debug_entry` | 调试会话门控(组合根启动态布尔) |
| 8 | `_confirm_consumer` | 确认弹窗分派状态量(组合根自身状态) |
| 9 | `_confirm_consumer_slot` | 分派附随槽号(与 8 同一关注点成对读写) |
| 10 | `_debug` | DebugSession——调试会话状态机持有(逻辑件非界面件) |
| 11 | `_treasure_ui` | TreasureInteractController——组合根拆件(核心写调用代执行) |
| 12 | `_bonus` | CurioInteractController——组合根拆件 |
| 13 | `_camp_pres` | CampShopPresenter——组合根拆件 |
| 14 | `_tune_sim` | TuneSimController——调参测量控制器(逻辑件) |
| 15 | `ui`(新增) | 界面注册面——本设计的唯一新成员 |
这 14+1 全部是「组合根自身状态+拆件引用+autoload 读视图」,正是 §1.1 拆分纪律下组合根该留的最小内聚面;没有一件是因为「计数不够了才留下」的。
**消灭(7)**——全部是别名或一行转发,属拆分纪律⑤明令禁止保留的无效成员:
| # | 成员 | 本体 | 消灭后地址 |
|---|---|---|---|
| 1 | `board_view` | = board_zone.board_view | `ui.battle.board_zone.board_view`(探针惯用 `var bv = ...` 局部承接,每文件 1–2 处) |
| 2 | `ap_bar` | = board_zone.ap_bar | `ui.battle.board_zone.ap_bar` |
| 3 | `_modals` | = _modal_flow.stack | `ui.modals.flow.stack`(ui_struct 登记序断言) |
| 4 | `_row_queue` | 委托属性 = _row_pacer.pending_rows() | `ui.screens.row_pacer.pending_rows()`(main 自身 3 处+pacing_queue_test 1 处;同一 Array 引用,语义不变) |
| 5 | `_seed_edit` | = debug_overlay.seed_edit | `ui.modals.debug_overlay.seed_edit.text`(仅 main._seed_value 内部消费,探针零引用) |
| 6 | `_settlement_stop_pending` | 委托属性(读=timer.is_armed(),写=arm()/disarm()) | `run_scope.settlement_stop_timer` 直呼(ui_click_probe 2 处读+main_adventure_flow 3 处写) |
| 7 | `_deferred_modal` | 委托属性 = _modal_flow._deferred | `ui.modals.flow._deferred`(t17_defer_probe 1 处) |
对账:14(留)+28(进注册面)+7(消灭)=49 ✓;main 改后 = 14+1(ui)=**15,恰在阈值内**。
### 1.3 main.gd 改后类变量实数
**15**(14 留+ui)。gdstyle 的 max-class-variables 语义是「超过 15 才红」,15=绿。此后 main 每想加成员就是过阈红——这正是拆分纪律③的合规用法:过阈值作发现信号,新句柄该进注册面、新职责该拆新件。
## 2. 职责语句+单一变化原因
| 件 | 职责语句 | 单一变化原因 | 集体身份判据 |
|---|---|---|---|
| ui_registry | 「呈现层界面元素清单的唯一事实源——哪些层、什么序、地址在哪」 | 界面元素集合或层序变化(加屏/加覆盖层/改层序) | 它就是今天散在 main 的 LAYER_CONTRACT+_layer_node+一级句柄三样东西的合体;合同键、登记序、句柄址本是同一份清单的三个投影 |
| ui_screens | 「不进模态栈的树位层册——三屏壳与其一级子层、全屏态、浮层角标、排播泵节点」 | 屏壳/全屏态/浮层集合变化 | 集体身份=LAYER_CONTRACT 里 kind≠modal 的项(shell/state/float/node)+屏内一级子层句柄 |
| ui_battle | 「战斗屏四区(A 状态/B 明细/C 数据/D 棋盘)视图与结算演出句柄册」 | 战斗区视图构成变化(battle.md 三区面) | battle.md 三区+演出,是架构文档反复引用的既有概念,非本批发明 |
| ui_modals | 「模态层域——十三个模态覆盖层句柄+模态栈编排(登记/开合/输入锁谓词)」 | 模态层集合或模态编排语义变化 | 集体身份=「被 ModalStack 登记的层」——遮挡互斥、开合同步、锁谓词是这 13 件共享的行为面 |
**禁凑计数袋自检(拆分判据①–⑤逐条)**:
1. 拆分沿职责轴:三容器的轴=LAYER_CONTRACT kind 轴+战区轴,两者都是本批之前就存在的领域轴;过阈值(49→15)只是**发现信号**,不是切分依据。
2. 集体身份:每件命得出名字、说得清「共同表达什么」(见上表),没有一个容器是「剩下的成员」。
3. 过阈值只作动机=违规:本设计若只把 30 个句柄原样搬进一件,那才是凑计数;按领域轴分三域+把合同机器(今天在 main 里同样占职责面的 LAYER_CONTRACT/_layer_node)一并归位,是职责收拢。
4. 拆不出内聚单元须停卡:本设计拆得出(见上),不停卡。
5. 禁无效成员/别名:本批**净消灭 7 个别名/转发成员**(§1.2 第三表)+退役 main 侧六个纯转发方法(§4.2),方向是减不是留。
依赖方向自检(§1.3):注册面只持视图引用、零核心写调用、零信号连接——它不是新的写调用出口,组合根单点装配不变;视图仍经信号到 main(或 main 拆件)再到 core,依赖单向零触碰。
## 3. 探针契约迁移
### 3.1 新地址形态
**推荐**:`main.ui.<域>.<句柄>`(例:`main.ui.modals.level_screen`、`main.ui.battle.board_zone`、`main.ui.screens.menu_screen`)。三段式地址每段都有领域含义(组合根→注册面→域→元素),与 ConfigDB 的 `db.<域容器>.<成员>` 两段式同构——本仓已验证的容器寻址形态。
**备选一(双容器直挂,`main.modals.X` 两段式)**:少一跳,但 main 要放 3 个容器成员,改后 main=16 过阈,只能再靠合并 `_confirm_consumer_slot` 之类凑减员——凑计数味,淘汰。
**备选二(单件+字典存储+方法访问器 `main.ui.layer("legend_overlay")`)**:丢了类型化字段,且约 30 个访问器方法有撞 max-public-methods 默认阈值的风险(board_view 现值 27 已在基线,默认阈值必然更低)=新基线键=留债,淘汰。
### 3.2 改址策略(约 867 处探针/系统测试引用+约 200 处流程件 root.X 引用+main 自身)
**机械映射表**(实现批以脚本生成+对账,人工复核语义位):映射表条目形如 `旧路径 → 新路径`,来源即 §1.2 对账表。三类引用分别处理:
1. **属性读/写(约 750 处)**:`main.<名>` → `main.ui.<域>.<名′>`(`名′`=去下划线后的名)。前缀链式读写在 GDScript 天然支持(`main.ui.modals.debug_overlay.seed_edit.text = "42"` 与现形态同构);探针里 `var bv = main.board_view` 的局部承接习惯使别名消灭的实际改动收敛到每文件 1–2 处。
2. **方法调用(约 110 处,其中绝大多数零迁移)**:组合根流程方法(`_start_adventure_from_menu` 16、`_open_save_select` 7、`_on_save_select_closed` 9、`_on_camp_closed` 6、`_on_battle_end_continue` 4、`_show_debug_overlay` 4、`_resume_from_menu` 3、`_flush_row_queue` 3、`_seed_value` 3 等)**留 main,零迁移**——它们是模式流转与流程入口,本就是组合根职责。真正改址的方法引用只有四条:`main._input_locked()`(18 处)→ `main.ui.modals.flow.locks_input()`;`main._layer_node(key)`(3 处,system_test)→ `main.ui.node(key)`;`main._open_modal/_close_modal`(探针各 1 处+流程件 root. 共约 23 处)→ `main.ui.modals.flow.open/close`;`root._open_modal_after_playback`(2 处)→ `root.ui.modals.flow.open_after_playback`。
3. **字符串键取值(smoke_boot 的 `main.get("<名>")` 6 处)**:`main.get("run_scope")`/`main.get("settings_store")` 零迁移(留 main);其余改 `main.get("ui").<域>.<名′>`(get 只解析一层,后续走公共字段)。
**流程承接件随批同规改址**:main_ui_build 的赋值目标改 `root.ui.<域>.<名′> = ...`;main_adventure_flow/main_overlay_flow/main_debug_flow 的 `root.<名>` 同表映射。构造注入的 context 束(CurioFlowContext/CampFlowContext 的 overlay/toast/start_overlay 等键值)改址取值,注入时点与值不变。
**源码级断言随批重锚(易漏项,实查全量在案)**:
- `source_guard_test.gd` V1d「层序落位单口」:现扫描 main.gd 源码中 `_apply_layer_contract` 之外零 `move_child(`。合同机器迁入 ui_registry 后,重锚为「扫全 `game/presentation/*.gd`:`move_child(` 只允许出现在 ui_registry.gd 的 `apply_layer_contract` 函数体内」(判据同步强化:从扫单文件变扫全目录,比原判据更严)。
- `theme_projection_test.gd` V6:`main_src.contains("debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)")` 字符串断言 → 改为 ui.modals.debug_overlay 新串。
- 其余 main.gd 源码断言(V8 GP 影子账残留检查)为「禁止出现」型,不受影响;`meta_progression_forge_test` 经 `load("res://presentation/main.gd")` 直呼 static `build_adventure_params`——留 main,零迁移。
**改址完整性自检**(验收辅证):映射表驱动的全仓 grep——`game/` 下(探针/tests/flow 四件/main 自身)旧一级路径 `main.<旧名>`/`root.<旧名>`/`self.<旧名>` 残留必须为 0(按 49 名逐名 grep,报告留档)。
### 3.3 architecture 登记句改写建议
「呈现层装配与会话生命周期 §三屏壳与组合根句柄面」现句(含 S13 豁免登记)整句退役,建议改写为:
> 屏成员与覆盖层句柄经**界面注册面**(main.ui,`ui_registry.gd`+三域容器 ui_screens/ui_battle/ui_modals,全 RefCounted)统一暴露;探针挂载假设=`main.ui.<域>.<句柄>` 路径(2026-10-29 用户裁「不允许任何豁免」——原「组合根 main.gd 一级句柄(main.X 路径零迁移)」挂载假设与 S13 豁免登记同批注销,探针面全量改址,行为零变化由 probe_gate/render_gate/冒烟/系统测试四闸主证)。组合根 main.gd 成员面收口为组合根自身状态+拆件引用+autoload 读视图(类变量 15,阈内)。层序唯一事实源=`ui_registry.LAYER_CONTRACT`+`ui.apply_layer_contract()`;模态登记/开合/锁谓词单口=`ui.modals`(ModalFlow 语义零变化)。
「模块归属·呈现层」表同步增四行(四新件),main_ui_build 行的「层序逻辑不在此件——唯一事实源仍是 main.LAYER_CONTRACT」句改指 ui_registry。
## 4. main.gd 改后形态
### 4.1 剩余成员清单
见 §1.2 第二表(14 留+ui,逐个理由在表内,不重复)。文件头注同步改写:组合根职责语句追加一句「界面句柄与层清单在 ui 注册面(ui_registry.gd)」。
### 4.2 接线流程变化
1. **装配入口**:`_ready` 在 `_build_ui()` 之前 `ui = UiRegistry.new()`(容器随门面构造);`MainUiBuild.build(self)` 八步内部赋值目标全部改 `root.ui.<域>.<名′>`,八步顺序、add_child 顺序、连接语句逐条不变。
2. **MainUiBuild 交付物改型**:build 签名不变(仍 `build(root)`),改的是写入目标——从「给 root 造 30 个一级成员」变「往 root.ui 三容器登记」。装配序与 z 序零改动(合同落位仍最后一步)。
3. **合同机器搬家**:`LAYER_CONTRACT` 常量(内容逐字不动)、`_apply_layer_contract()`(root 作参传入)、`_layer_node(key)`(改为 ui_registry.`node(key)` 的 19 键 match)、`_register_modal`(改入 ui_modals 为 `register_layer(m, tags)`,anchors preset+flow.register 语义逐字)四样整体迁入注册面;main 侧不留转发壳。
4. **模态口收口**:main 的六个纯转发方法退役——`_open_modal`/`_close_modal`/`_on_modal_closed`/`_input_locked`/`_open_modal_after_playback`/`_on_pump_ended`。调用方(三个流程件、context 注入束、row_pacer.pump_ended 注入点、信号 connect 的 bind 目标、探针)一律改指 `ui.modals.flow` 真方法;`_replay_locked_click` **留 main**(读 run_scope.locked_click+调 _on_board_action,是组合根行为,作为回调注入 flow 不变)。
5. **`_seed_value()`**:种子框读取改 `ui.modals.debug_overlay.seed_edit.text`(全工程唯一种子框读取处纪律不变)。
### 4.3 LAYER_CONTRACT 与 _apply_layer_contract
合同表条目、顺序、tags、host/shell 标注**逐字零变化**,只换宿主文件;落位算法逐字搬运(move_child 单口语义不变,冒烟 V1d 判据按 §3.2 重锚后仍覆盖「全 presentation 无第二落位点」)。改层序仍只改合同表。
### 4.4 RunScope 生命周期零变化声明
`_begin_session`/闲时会话/整节点 `queue_free` 机制**一字不动**;run_scope 留 main,注册面与三容器**零会话期引用**(不持 adventure/runner/settlement,控制器仍走现值回调),会话更替对注册面完全透明。存活性矩阵第五引用面的四持句柄件+_row_pacer 逐面同源比对断言(system_test V3a–V3i)逻辑零变化,仅 `_modal_flow`→`main.ui.modals.flow`、`_row_pacer`→`main.ui.screens.row_pacer` 两处取址随批改。
## 5. 行为零变化风险点
1. **装配与接线时序**:容器在 `_ready` 建闲时会话之前创建并装配完毕,八步 build 顺序、`_apply_layer_contract` 末位调用点、信号连接顺序逐条不变。风险形态=漏改一处地址→运行期 null 实例 SCRIPT ERROR→被冒烟闸③与探针 error_scan 抓住(不静默)。
2. **模态开合**:ModalFlow 的八个查询/钩子 Callable(start_visible_get/inter_visible_get/settlement_get/rows_empty_get/flush_rows/on_open_sync/on_close_sync/replay_click)注入值不变,仅 `root._start_overlay.visible` 这类 lambda 体内取址改 `root.ui.screens.start_overlay.visible`;开合编排、挂起兜底、点击重放语义零触碰。
3. **会话更替**:注册面零局态引用(§4.4),queue_free 不触及;row_pacer 节点本体仍 add_child 到 main、树位由合同落位,注册面只持引用——第五引用面断言改址后逐面同源比对仍成立。
4. **别名消灭的断言等价性**:board_view/ap_bar/_modals/_row_queue 四件消灭后,探针断言经多一跳取到**同一对象引用**(属性链与别名同指),断言语义逐条等价;system_test V1 的句柄等价断言(`main.board_view == main._adventure_screen.board_view` 型)改址为 `ui.battle.board_zone.board_view == ui.screens.adventure_screen.board_view` 后仍是有效的装配结构断言。
5. **接缝属性消灭的读/写映射**:`_settlement_stop_pending` 读=armed→`is_armed()`、写=arm/disarm→同名方法直呼,映射一一对应且 run_scope 在探针用时点恒非空(_ready 即建,永非 null 契约);`_deferred_modal`→`flow._deferred` 同字段直读。
6. **演出链**:settlement_fx/row_pacer 的 play/enqueue 调用点改址不改序——结算行排播、轮横幅、锁期点击重放的时序敏感面(T17)零触碰;t17_defer_probe 挂起态断言改读 `flow._deferred`,判据不变。
7. **源码级断言漏改**:V1d/V6 两处实查在案(§3.2),随批重锚;若存在未登记的字符串匹配型断言,run_all 关联档跑批暴露——本设计已全量 grep `presentation/main.gd` 引用面确认只有上列两件。
RNG 消费序零触碰(本批不涉任何随机面);PARITY 触面(battle_orchestrator.gd)不在 diff;呈现层订阅禁令、核心写调用唯一出口纪律零触碰。
## 6. 验收载体建议
| 闸 | 载体与判据 |
|---|---|
| probe_gate | **默认段+`--click`+`--full` 全跑**——探针是本批改址的第一消费方(ui_click_probe 1079 行是最大户),只跑默认段会漏最深的改址面;判据=全段退出码 0 |
| render_gate | golden 零差异(shot_probe 七屏+菜单 golden+棋盘 golden)——视觉零变化的主证 |
| run_smoke | V1 装配句柄在位(get() 键改址后)+V2 启动态六条——boot 侧证明注册面装配完整 |
| run_system_test | V1–V9 全绿:LAYER_CONTRACT 寻址(改 ui.LAYER_CONTRACT/ui.node)、句柄等价、存活性矩阵、模态互斥、两连局+续玩 |
| run_all(关联档) | 申报套件:source_guard_test(V1d 重锚)、theme_projection_test(V6 字符串)、pacing_queue_test(_row_queue 改址)、meta_progression_forge_test(static 入口零变化);main.gd 是否按横切面回落全量套件见 §8 待裁点 |
| static_scan | S9 过(四新文件全类型标注);S13 键账:main.gd 键从基线消失、四新文件零新键(§7) |
| 改址完整性自检 | §3.2 的 49 名逐名 grep 残留=0,报告留档(实现报告对照表挂行) |
验收闭合口径(test-conventions §8):判据绑定面=「探针经新地址取到同一节点并驱动真实输入路径」——probe_gate 全绿+系统测试全绿+golden 零差异三件齐,即「改址不改变行为」的真实入口主证;grep 自检只作完整性辅证,不作行为判据。
## 7. gdstyle 键账预期
| 文件 | 改后类变量 | 键账 |
|---|---|---|
| main.gd | **15**(14+ui) | 基线键 `main.gd|quality/max-class-variables = 49` 消失——还债方向下降,注销合法 |
| ui_registry.gd | 3 | 零新键 |
| ui_screens.gd | 9 | 零新键 |
| ui_battle.gd | 5 | 零新键 |
| ui_modals.gd | 14 | 零新键(阈内,余量 1) |
基线 refresh 人工跑(`--refresh`):净减 1 键(约 15 键的 6.7%),低于 10% 骤降守卫,无需 `--force`。四新文件其余质量键(max-public-methods/max-file-length 等)预期零命中——容器纯字段无长函数,match 键数(19)在 max-branches 默认内,若实现批实测出新键按基线外 NEW 红处理、不得开豁免(用户裁决口径统一适用)。
§3.3 的登记句改写即豁免注销文案;「S13 豁免登记第二/三条」(tune_sim_controller、battle_orchestrator)不在本批范围、原文不动。
## 8. 估时与待裁点
**估时:3–4 个工作日**。构成:映射表脚本生成+四文件建件+main/流程件改址 1–1.5 天;探针/系统测试/冒烟改址+源码级断言重锚 1 天;闸循环(probe_gate --click --full/render golden/smoke/system_test/run_all 关联档/static_scan)修复余量 0.5–1 天;architecture 登记句+模块归属表+卡片改动—验收对照表 0.5 天。低于迭代冻结线(5 工作日),可编批。
**待裁点(呈用户裁决)**:
1. **注册面拓扑**:推荐「一门面+三域容器」(§1.1,main=15);备选「双容器直挂」因 main 会过阈 16、需再凑减员而淘汰。是否照推荐案执行。
2. **合同机器是否随批迁入注册面**:推荐迁(LAYER_CONTRACT+_apply_layer_contract+_layer_node+_register_modal → ui_registry/ui_modals,职责闭合);不迁则注册面只管址不管序,main 留合同——两案都可达 15,迁是内聚更优解但多动 V1d/system_test 约 8 处。
3. **消灭与改名清单**:七个别名/转发成员消灭+六个 main 纯转发模态方法退役+容器内成员去下划线(对齐合同键名)——涉及探针断言面写法(`main._input_locked()`→`ui.modals.flow.locks_input()` 等),请整体确认或逐项裁。
4. **run_all 关联档口径**:main.gd 是全探针宿主,保守口径=按横切面回落全量套件;精确口径=只跑 §6 申报四件。请裁跑批档位。
5. **编批**:估时 3–4 天在冻结线内,编入哪个迭代由迭代编排段定。
@@ -1,193 +0,0 @@
# 评估:Dungeon Sweeper 美术资产清单与性能预算
> 版本 v1 | 2026-10-30 | 目标引擎 Godot 4.7.2-stable(`gl_compatibility` / 纯 2D)| 平台 PC Windows 先行 | 设计分辨率 1280×800 基座 + 1920×1080(全局 1.35× 等比)
> 性质:评估过程件,对应美术师交付物「美术资产清单」+「美术规格与预算」两章合并(一次问三事,不拆两份重复)。**未改任何权威文档与 `game/` 文件**;结论若要进 `design/` 走 doc-design 登记 + 用户发令。
> 事实源:`design/art/art-assets-checklist.md`(需求)· `art-status-checklist.md`(现状)· `art-bible.md` §8(尺寸定值单点)· `asset-path-map.md`(路径)· `game/assets/` 实扫 · `game/project.godot` 与 117 份 `.import`(引擎现状)· `design/gdd/ux/battle.md` 板形表 · `game/data/tables/enemy.tres`。
> 显存口径:`art-engine-handoff/scripts/asset_budget.py` 实算;格式按项目真实导入(`compress/mode=0` Lossless → 运行时解码为 RGBA8 = 4 B/px,关 mipmap)。Draw Call 与峰值显存**未真机实测**,只给内控线与估算口径。
## 0. 结论先行
1. **纹理显存不是瓶颈**:现状 126 张 PNG 解码后合计 **0.82 MB**,占项目内控线 96 MB 的 0.85%。
2. **满配试算 39.95 MB**,占内控线 41.6%,余量 56.05 MB(≥20% 达标);前三项占 96.9%,压显存只需盯营地场景图。(营地由 9 张拆为 10 张:新增 `scene_camp_wall` 围墙层,按细条 `(0,240,1280,70)`=0.34 MB 计,不做全幅;较原口径净增 0.34 MB。)
3. **真瓶颈在 Draw Call 与包体结构**:196 格棋盘 + 碎图各自独立纹理,最重一屏粗估 400–600 次;磁盘 6.87 MB 中字体独占 6.68 MB(97%)。
4. **导入设置现状正确,无需改**:117 张全 Lossless + mipmap 全关 + 全局 Nearest,与像素风四纪律一致;非 2 幂尺寸在当前 PC Lossless 下不产生补齐,脚本 warn 可忽略。
5. **1920×1080 档不新增纹理**:全局等比缩放复用同一份资源,显存与档位无关。
## 1. 目标引擎与导入基线
### 1.1 工程侧已核事实(实测,非文档推断)
| 项 | 值 | 来源 |
|---|---|---|
| 引擎 | 4.7.2-stable,`config/features=PackedStringArray("4.7")` | `project.godot` |
| 渲染方法 | `gl_compatibility`(PC OpenGL Compatibility) | `project.godot` |
| Stretch | `canvas_items`,1280×800 视口;1920 档 aspect keep | §8.1/§8.2 |
| 默认过滤 | `default_texture_filter=0`(Nearest) | §8.2,全局像素风基线 |
| 导入类型 | `CompressedTexture2D`,`vram_texture: false`,117/117 | `.import` 全量 |
| 压缩 | `compress/mode=0`(Lossless),117/117 | `.import` 全量 |
| Mipmap | `mipmaps/generate=false`,117/117 | `.import` 全量 |
| 3D 检测 | `detect_3d/compress_to=1`(2D 项目不触发) | `.import` 全量 |
| 透明边 | `process/fix_alpha_border=true`,117/117 | `.import` 全量 |
| 字体 | `theme/custom_font` = Fusion Pixel 单套 TTF | §3.6 清单已定稿 |
**Lossless 的显存真相**:`compress/mode=0` 只压缩磁盘上的 `.ctex`,运行时仍解码成 RGBA8 上传显存(`vram_texture: false` 佐证),故显存一律按 **4 B/px** 计,不按 PNG 文件大小计。这是本次全部试算的口径。
**非 2 幂 warn 的边界**:实扫 126 张中大量 20×20 / 46×48 / 66×94 等非 2 幂尺寸。在当前 **PC + Lossless** 下不补齐、不影响压缩,脚本提示可忽略;若日后转 VRAM 压缩或上移动端/小游戏,该项即变为硬约束(无法压缩或图集补齐),属「待实测项」。
### 1.2 Godot 4.7 导入设置对照表
按 `engine-import.md` 逐项填,只列目标引擎列与项目现状;3D 相关项因纯 2D 标不适用。
| 项 | 通用要求 | Godot 4.7 项目现状 | 判定 |
|---|---|---|---|
| 纹理类型 | 2D 项目用 Sprite2D / TextureRect | `CompressedTexture2D` | ✅ |
| 压缩 | 像素风 = 关闭压缩或无损 | Lossless(117/117) | ✅ 正确;插画级大图是否转 VRAM 压缩待裁 |
| sRGB | 颜色图勾 sRGB | 走 PNG 色彩空间默认,`hdr_as_srgb=false` | ⚠ 建议显式逐张标注色彩空间(待核,无风险) |
| Mipmap | UI / 精灵 / 特效关闭 | 全关 | ✅ |
| 过滤 | Point(像素风) | 全局 Nearest;显式 LINEAR 豁免仅 vignette / gradient_base / beams 三件,已入册 | ✅ |
| 九宫格 | StyleBoxTexture 设边距 | `scene_stage_plaza` 48×12 横向拉伸、顶缘 2px 不随拉伸变形;UI 面板走主题样式盒 | ✅ 边距需随件填,不靠记忆 |
| 图集 | 同屏资产归同组 | **现无图集**,126 张独立纹理 | ⚠ Draw Call 处置方向,待工程评估 |
| 字体 | 动态字体优先,授权干净 | Fusion Pixel 单套,SIL OFL 1.1 在册 | ✅ |
| 序列帧 | 播放器关插值、行列写进规格 | 帧级素材未产;帧数已冻结(待机 4 / 攻击 6 / 受击 2,§8.4) | ⏸ 待产 |
| 3D 模型 / 法线 / PBR / LOD | 单位轴向、OpenGL 法线 | **不适用**(纯 2D,`mdl` / `tex` 类为空) | — |
## 2. 资产清单(运行时资源,十类逐一过)
### 2.1 十类现状总表
优先级沿用 `art-assets-checklist.md` 的 P0–P3 口径(玩法依赖,非排期);状态取自 `art-status-checklist.md`。前缀为美术师通用分类,项目文件命名以 §8.3 为准(见 2.3)。
| 类 | 前缀 | 项目对应资产 | 在盘件数 | 状态 | 优先级 |
|---|---|---|---|---|---|
| 角色 | `char` | 主角 B 区三态立绘 + 棋盘小人 32×32 四向 | 13(`art/character`) | ✅ 三态已接 | P1 |
| 怪物 | `mon` | 八敌站桩立绘(36–94 逐键定档) | 13(同上,含敌) | ✅ 已服役 | P1 |
| 场景 | `env` | 砖面/地形 `terrain_*` 三关换皮、封印印记、舞台地台 | 64(`art/board`)+1(`art/scene`) | ◐ 砖面程序化观感待升级 | P2 |
| UI | `ui` | 面板/按钮/进度条/节点图容器 | 33(`art/ui`) | ◐ 面板与节点图占位 | P2 |
| 图标 | `icon` | 状态九枚、格面徽章 19、护甲三枚、柜台四枚、节点四类 | 12(`assets/ui/icons`)+19 待产 | ◐ 部分在役 | P2 |
| 特效 | `fx` | 挥铲三帧、打击火花、烟尘碎屑、封印特效 | 3(`art/fx`) | ◐ 程序化在役 | P3 |
| 动画 | `anim` | 意图准备三档、死亡灰化碎裂、弹道密度 | 0(帧级未产) | ◐ 占位符号在役 | P1/P3 |
| 3D 资产 | `mdl` | — | 0 | **不适用** | — |
| 字体 | `font` | Fusion Pixel 单套单字重 | 1 TTF(6.68 MB) | ✅ 已定稿 | P2 |
| 插画 | `img` | 营地场景图 10 张(9 全幅+1 围墙细条)、B 区舞台背景 1280×270 | 0(全占位) | ⬜ 未开始 | P2/P3 |
在盘合计:PNG 126 张(`art/` 114 + `ui/icons/` 12)+ TTF 1 件;磁盘 6.87 MB。**纯 2D,零 3D 资产,无法线/PBR/LOD 需求。**
### 2.2 缺口清单(未 ✅ 项,P0 写清阻塞什么)
| # | 资产 | 类 | 规格(§8.2 定值) | 用途 | 优先级 | 状态 | 阻塞什么 |
|---|---|---|---|---|---|---|---|
| 1 | 战斗一屏样张 | 插画/样张 | 1280×800 双舞台整屏 | 锁剪影/动画/战斗空间/敌人层 | **P0** | ⬜ | 角色终稿、意图动画、演出类全部不得开工(清单 §1.2 硬前置) |
| 2 | 意图准备动画三档 | 动画 | 待机 4 / 攻击 6 / 受击 2 帧,灰度剪影可辨 | 敌方下一拍预告 | P1 | ◐ | 预告从占位符号退场;文字/技能名退出徽标后的唯一承载 |
| 3 | 格面资源徽章 19 枚 | 图标 | 32×32 级,极性=金满框/红缺框 | 九家族正负 + 清图槽 | P2 | ◐ | 无(占位够试玩,观感不达标) |
| 4 | 雷/封印/数字砖面全套 | 场景 | 32×32 瓦片随格边长整格铺满 | 扫雷核心读盘 | P2 | ◐ | 前置格子样张已 ✅,无硬阻塞;观感升级 |
| 5 | 关卡节点图图标组 | 图标 | 四类节点 + 行进器 | 节点图 UI | P2 | ◐ | 正式节点图 UI 设计未成文 |
| 6 | 营地场景图 10 张(9 全幅+1 围墙细条) | 插画 | 2× 源出图 → 降采样 1280×800 | 营地交互重做 | P2 | ◐ | 营地交互重做美术自立批(不占迭代位) |
| 7 | 护甲类型图标三枚 | 图标 | 24×24,显示 1× | 站桩名字行 | P2 | ⬜ | 名字行 ◇/◆ 字形占位退役 |
| 8 | B 区舞台背景 | 插画 | 1280×270,远景+中景两带 | 战斗舞台 | P3 | ⬜ | 临时光柱氛围层由背景吸收 |
| 9 | 受击/死亡/顺位、连锁坍塌、封印特效、弹道密度 | 特效/动画 | 灰化+碎裂退场(禁瞬消) | 战斗打磨 | P3 | ◐/⬜ | 无硬阻塞,样张后排期 |
| 10 | 状态九枚 / 字体 / 主角三态 / 八敌立绘 / 地台 | — | — | — | — | ✅ | 已交付,不列为缺口 |
格子样张 ✅ v3 冻结(2026-09-30 机检 17/17);收益/秘宝图标、封面宣传图、地形变异专属纹理按清单 §5 **明确不在本清单**,不计入预算。
### 2.3 命名口径冲突(呈报)
美术师通用规范为四段式 `<类别>_<对象>_<状态>_v<N>.png`(旧版改名 `_deprecated` 不删);项目 §8.3 已定案为**域键式** `<域>_<对象键>_<状态|变体>[_<序号>].png`,对象键 = 数据表主键(`effect_id`/`reward_id`/`cell_type`/`enemy_id`),文件名键全局恒定禁运行时换图,且素材不入 git、候选与终稿分目录、版本号走归档纪律。
**处置:以项目 §8.3 为准**,通用四段式不覆盖。依据:项目规范已定稿且有代码闸(`standee_asset_check`、图标键契约)依赖该命名;本条仅呈报差异,不擅自改。
### 2.4 产出批次
沿用清单 §6 依赖链,不重排:格子样张(✅)→ 砖面全套;战斗一屏样张(⬜)→ 角色终稿(✅)→ 意图动画 → 演出类;徽章/节点图/效果图标/复用件为独立线可插队;营地场景图归营地美术自立批。**第 1 批基准图未定稿前不排后续批次规格。**
## 3. 性能预算(六项,先预算后规格)
### 3.1 倒推起点:同屏对象数(已核)
| 对象 | 上限 | 来源 |
|---|---|---|
| 棋盘格 | 14×14 = **196 格**(格边长 30px) | `battle.md` 板形表 N=14 |
| B 区单位 | 主角 1 + 敌最多 **3** | `enemy.tres` `unit_count_max` 全表最大 = 3 |
| HUD / C 区 | 数据框、状态槽、按钮组约 60 件 | 现役 UI 结构 |
| 营地 | 背景 1 + 可点物件 7 + 承载件 1 = 9 | 清单 §3.5 |
### 3.2 六项预算表
内控线 = 本项目自定(通用 PC 档为起步值,需实测校准);均留 ≥20% 余量;实测日期栏为「未实测」的项不得当验收结论。
| 维度 | PC 档参考 | 项目内控线 | 现状/试算 | 判定 | 备注 |
|---|---|---|---|---|---|
| 纹理内存 | ≤ 1.5 GB | **96 MB** | 现状 0.82 / 满配 39.95 MB | ✅ | 余量 56.05 MB;可用线(20% 余量)76.8 MB |
| Draw Call | ≤ 800 | **600** | 估 400–600,**未实测** | ⚠ | 留 25% 余量;真机跑最重一屏取峰值 |
| 同屏三角面 | ≤ 100 万 | 不构成约束 | 约 0.14 万(2D quad 2 三角) | ✅ | 远低于档位,仅记录 |
| 同屏粒子 | ≤ 1000 | **450** | 程序化火花/碎屑,未实测 | ⚠ | 建议单次特效峰值 ≤150、同屏 ≤3 |
| 单角色骨骼 | ≤ 150 | **不适用** | 0(无骨骼,序列帧+程序化) | — | 纯 2D |
| 包体(磁盘) | PC 首包不适用 | 记录值 | **6.87 MB**(PNG 0.19 + 字体 6.68) | ✅ | 字体占 97%,压包体先做字体子集化而非压图 |
**内控线 96 MB 的依据**:通用 PC 档 1.5 GB 面向 3D/4K,本项目 2D 单场景全驻留最坏试算 39.95 MB;取 ~2.4× 作内控线,给三关场景、秘宝图标与后续插画留扩展空间,并满足 ≥20% 余量。
### 3.3 纹理内存试算(脚本实算,非估)
| 版本 | 项数 | 合计 | 占内控线 | 余量 | 前三名占比 |
|---|---|---|---|---|---|
| 现状版 | 20 种尺寸 / 126 张 | **0.82 MB** | 0.85% | 95.18 MB | 75.3%(32×32×78 = 0.30 MB、256×256×1 = 0.25 MB、64×64×4 = 0.06 MB) |
| 满配版 | 26 项 / 189 件 | **39.95 MB** | 41.6% | 56.05 MB | 96.9% |
满配增量清单口径(全部 RGBA32、mipmap 关):
| 增量项 | 规格 | 件数 | MB | 占满配 |
|---|---|---|---|---|
| 营地场景图 | 1280×800 ×9 + 围墙细条 1280×70 ×1 | 10 | **35.50** | 88.9% |
| 敌人动画图集 | sheet:64×64 帧、12 帧、6 列、pad 2 → 408×136 | 9 套 | 1.91 | 4.8% |
| B 区舞台背景 | 1280×270 | 1 | 1.32 | 3.3% |
| 徽章/节点/护甲等图标 | 32×32 | 36 | 0.14 | 0.4% |
| 特效帧/粒子贴图 | 64×64 | 6 | 0.09 | 0.2% |
| 主角动画图集 | sheet:48×72 帧、12 帧、6 列、pad 2 → 312×152 | 1 套 | 0.18 | 0.5% |
| 现状存量 | 实扫聚合 | 126 | 0.82 | 2.1% |
按格式聚合:RGBA32 100%(项目全 Lossless)。**现状与满配差 39.13 MB,其中营地 10 张独占 90.7% 的增量。**(围墙层落盘按细条 `(0,240,1280,70)`=0.34 MB,不占 1280×800 全幅大图名额。)
### 3.4 由预算倒推单件规格(≥20% 余量)
1. **大图是唯一约束面**:可用线 76.8 MB 减去非大图 4.45 MB = 72.35 MB;1280×800 @ RGBA32 单价 3.906 MB → **同档大图总量 ≤ 18 张**(18 张 = 70.31 MB,合计 74.76 MB ✅;19 张即 78.67 MB ❌)。营地首批 9 张全幅大图(另加 1 张围墙细条 0.34 MB,不占大图名额)安全,但「三关各 9 张全幅大图 = 27 张」必然触发超线。
2. **触发后的处置优先级**:① 大图换 VRAM 压缩(BC3 = 1 B/px,单价降至 0.977 MB,27 张仅 26.4 MB);② 或降入包分辨率至 960×600。**像素小图不动**——BC 块状伪影会毁像素边缘,且它们只占 0.82 MB。
3. **动画图集省的是 Draw Call,不是显存**:9 套图集 1.91 MB,同帧数逐帧独立 PNG 反而只需 1.69 MB(图集 padding 与补齐多出 0.2 MB)。进图集的收益在合批,别拿它当省显存的理由。
4. **小图/图标无需限规格**:32×32 级 78 张仅 0.30 MB,继续按 §8.2 甲类整数倍纪律产出即可。
5. **1920×1080 档零增量**:全局 1.35× 等比缩放复用同一份资源,不产生第二套纹理,显存与档位无关(§8.2 已裁)。
### 3.5 Draw Call 控制(美术能做的部分)
现状 126 张独立纹理、零图集,是 Draw Call 的结构性来源。最重一屏估算口径(**非实测**):196 格 ×(瓦片纹理 + 程序化覆盖层)≈ 390,B 区 4 单位 × 4 件 ≈ 16,HUD/C 区 ≈ 60 → **400–600 区间**,逼近内控线 600。
| 风险 | 处置方向 | 归属 |
|---|---|---|
| 三关瓦片 64 张交替绘制打断合批 | 同关同族瓦片合并为 `AtlasTexture` | 工程面改动,待裁/工程评估 |
| UI 切图过碎、每图标一材质 | 战斗页 / 营地页 / 节点图分页分图集 | 同上 |
| 特效贴图各自独立 | 特效帧进同一图集,粒子用同材质染色 | 同上 |
| 程序化 `_draw` 覆盖层 | 与纹理绘制混排会断批,排绘制顺序时可收敛 | 同上 |
| 动态字体 | Fusion Pixel 单套已合批,保持 | ✅ 无需动 |
**本报告只列处置方向,不新增需求、不改工程面**;任何一条落地须由用户发令走对应通道。
### 3.6 待实测项(未跑,发起权归用户)
| 项 | 方法 | 输出 | 状态 |
|---|---|---|---|
| 最重一屏峰值 Draw Call | 真机跑 14×14 满敌战斗场景 | 峰值次数 vs 内控 600 | **未实测** |
| 峰值显存 | 真机含营地切场景往返 | vs 96 MB 内控线 | **未实测** |
| 粒子峰值 | 同屏 3 特效叠加 | vs 450 内控线 | **未实测** |
| VRAM 压缩可用性 | GL Compatibility 下试导 BC3 大图 | 画质目检 + 显存实测 | **未实测** |
| 1920 档观感 | 1.35× 非整数缩放目检 | 甲类件宽窄不均是否可感 | 未目检 |
**禁止**用编辑器或模拟器数据当验收结论(实测差 2–3 倍);上表机型与实测日期留空待填。
## 4. 假设与待确认
1. 假设:目标平台 = PC Windows 先行(AGENTS.md),暂不上移动端/小游戏 → 无首包硬限、压缩不走 ASTC/ETC2。
2. 假设:满配 = 营地 10 张(9 全幅+1 围墙细条) + 舞台背景 1 + 9 敌动画 12 帧/套 + 主角 1 套 + 图标 36 + 特效 6;秘宝图标/封面宣传图按清单 §5 排除。
3. 假设:同屏敌人上限 3(`enemy.tres` `unit_count_max` 全表最大=3,已核)、棋盘 N=14(`battle.md` 板形表,已核)。
4. 假设:动画帧尺寸按 64×64 源档均值估(实际立绘逐键 36–94),帧数按 §8.4(4+6+2=12/套);量级不受影响。
5. 待确认:营地 10 张是否同屏常驻(Godot 引用计数不自动卸载,本报告按最坏全驻留计);动画是否走 `SpriteFrames` 图集(影响 Draw Call 估算,不影响显存量级)。
## 5. 不改动声明
未改 `design/art/` 任何权威清单与圣经、未改 `game/` 任何文件;未生成图、未跑测试、未跑 MC(无数值改动)。本报告为过程件,按 `reports/README.md`「吸收即死」:结论若被权威文档吸收即可删,若需落设计文档则走 doc-design 登记 + 用户发令。
@@ -0,0 +1,134 @@
# 需求卡片——gdstyle 尾波补件批
> **版本状态**:v1.1——2026-10-30 用户裁 T2 机制=R2 整族归件(实现段停卡呈报 `reports/iter45-停机呈报-断点域拆件规格缺口-2026-10-30.md` 裁决),第 7 节 T2 条款改述、其余条款沿 v1.0 定稿——零待裁点(2026-10-29 用户裁:恢复尾波卡 v2.0 裁去的 D2/D4 两拆件项;D5 五降值键=留债、零豁免延伸)。
> **接收方**:待办清单条目「gdstyle 尾波补件批——悬停域与断点域拆件(2026-10-29 登记)」。
> **来源**:尾波清偿批设计闭合后的用户再裁(D2/D4 改判拆件)。**唯一事实源**:`design/engineering/architecture/architecture.md` v1.25(「模块归属·呈现层」表 board_hover 行、「核心层」表 breakpoint_store 行、「冒险域拆分」节断点随动句、「呈现层装配」节屏成员寻址句)。
## 1. 目标与完成定义
这批做什么:把尾波卡 v2.0 裁去的两个拆件项恢复执行——board_view 悬停域拆出 board_hover、adventure_runner 断点域拆出 breakpoint_store。三个 gdstyle 键随之降值(board_view 类变量 32→约30、adventure_runner 类变量 28→约22;board_view 公开方法 27 持平——全为契约面)。
什么状态算完:
1. 两新件在库、宿主成员迁出、键账达第 3 节预期(降值口径,不承诺清零)。
2. 行为零变化:同种子下事件流、断点快照、棋盘渲染逐位一致。
3. 批末代码域闸全绿;三键终值按留债口径记入实现报告(禁落 architecture 豁免登记)。
## 2. 设计事实源
architecture.md v1.25 三处在册设计(2026-10-29 零豁免裁设计闭合):board_hover 行(职责语句「悬停预承诺呈现域」、拟收 11 函数族+2 成员、耦合边界)、breakpoint_store 行(职责语句「存档断点组装与回放唯一承载」、拟收 6 成员+函数族、27 键冻结)、「冒险域拆分」节随动句(拼盘与读侧策略机组拆出、应用层落盘口不变)。
## 3. 现状事实(2026-10-29 实查;行号为迭代 42 合回前基线——T0 必须按当轮 master 重测重锚,board_view 的 `_paint_cell` 届时已由迭代 42 拆为五段、行号必漂移)
- board_view.gd:悬停域成员 `_hover`(:70)、`_last_click`(:71);函数族 11 个=`_hover_at`(:442)、`_on_shell_mouse_exited`(:453)、`_detect_chord`(:475)、`_on_cell_hover`(:486)、`_on_cell_unhover`(:491)、`_refresh_hover_after_paint`(:497)、`_apply_hover_visuals`(:503)、`_clear_hover_visuals`(:531)、`_set_cursor_shape`(:538)、`_show_path_preview`(:542)、`_hide_path_preview`(:552)。`_r0`(:72)是出生展开区资格域成员(:238/:296/:563 三消费方),**不随迁**——划入悬停件即凑计数袋。
- adventure_runner.gd:断点域成员 6 个=`_save_manager`(:35)、`_resume_bp`(:36)、`_pending_next_level`(:30)、`_last_level_payload`(:31)、`_last_treasure_cleared`(:32)、`_loadout_seeded`(:37);常量 `BREAKPOINT_FIELDS`(:539-568,27 键);函数族=`_snapshot_breakpoint`(:571)、`_bp_parts`(:583)、`_bp_read`(:636)、`_bp_strategy`(:646)、`_bp_read_int`(:651)、`_resume_ledger`(:182)、`_resume_curio_state`(:193)、`_resume_params`(:218)、`_resume_counters`(:237);公开入口 `resume_adventure`(:141;:94 为 start_adventure)留宿主薄壳。
- 测试触面:`game/tests/unit/core/breakpoint_resume_test.gd:185` 直读 `AdventureRunnerScript.BREAKPOINT_FIELDS`——随件迁改址(判据零变化)。
- 键账:board_view 类变量 32(键留债目标约 30)、公开方法 27(持平——27 个逐类核对全为探针契约面与依赖单向纪律承载面);adventure_runner 类变量 28(留债目标约 22)。
## 4. 设计契约(违反即返工的红线条款)
- 🔴 **行为零变化**:同种子下事件流/断点快照/存档往返/棋盘渲染逐位一致;玩家可见面零变化。
- 🔴 **断点键集冻结**:`BREAKPOINT_FIELDS` 27 键逐键冻结(键名、策略标签、键序零变化);随件迁移只换承载文件,不换键面。
- 🔴 **RNG 零触碰**:本批零新流、零消费序变化(断点回放 restore 同序取号;悬停域无随机)。
- 🔴 **呈现零变化**:悬停反馈三语义(光标/边框/路径预览)行为逐点保持;棋盘 golden 三张对拍差异非零即红。
- 🔴 **拆分边界纪律**(判据权威=code-conventions §1.1,卡内执行条款):①拆出单元须有职责语句与单一变化原因(实现报告逐件一行);②集体身份——命不出名字的打包=凑计数袋,禁;③「过阈值」只作发现信号;④拆不出内聚单元→停卡呈报;⑤禁沿阈值轴拆。
- 🔴 **禁为机检文本保留无效成员**:禁止为迎合机检消息文本保留或新增无效成员/别名;机检键与卡片契约互斥=停卡呈报。
- 🔴 **残留键留债(D5 零豁免裁)**:三键降值后留基线冻结禁恶化;**禁落 architecture 豁免登记**;禁为凑阈值硬拆。
## 5. 范围边界
做什么:第 3 节两拆件项(board_hover、breakpoint_store)。
不做什么:迭代 42 在飞范围零触碰(_paint_cell 拆五段、循环内 new、_enter_node、商店委托壳——并排卡承接);单格/站桩状态件(呈现层状态件批承接);main.gd/battle_orchestrator/tune_sim 五键(零豁免五键各批承接);三残留键的终局清偿(留债,后续窗口再议)。
## 6. 关联文档同步面
| 文档 | 关联点 | 同步义务 |
|---|---|---|
| design/engineering/architecture/architecture.md | 两新件登记行(建件后「实现批建件」改现状);「冒险域拆分」节随动句核对 | 设计权威文档——实现批禁直改;逐条列进 `reports/iter<N>-反哺点-<日期>.md`,用户裁决后归文档批落文 |
| game/tests/README.md | 「棋盘悬停反馈」行代表用例列(本批复用既有探针、零新增断言——预计零改动);断点行入口句核对 | 机械面文档——实现批禁写;如需更新由写卡段后续卡修订版同批处理 |
| design/gdd/systems/ | 无关联文档——结构整改不改游戏规则与玩家可见行为 | 无修订预期;契约句被触碰=停卡呈报走反哺点通道 |
## 7. 任务卡 T0..T3
**T0 读数重锚与对账(0.25–0.5 工作日)**
- [ ] 以当轮 master 重测 board_view/adventure_runner 三键实测值与第 3 节全部 file:line;漂移处就地改卡并记录;对不上即停卡呈报
- [ ] 复核 11+9 函数族现行键面(`tools/gdstyle_baseline.txt` 无其函数级键——逐字搬运零新键;实查确认)
**T1 board_hover 拆件(0.75–1 工作日)**
- [ ] 建 `game/presentation/views/board_hover.gd`(RefCounted;构造=引用束注入:cells 容器引用、minesweeper 只读引用、diggable 集合引用+壳层光标写入与 hover_forbidden 注入 Callable)
- [ ] 悬停函数族 11 个+成员 `_hover`/`_last_click` 迁出(函数体逐字搬运);三入站口(`_gui_input`/`_on_cell_input`/`click_cell`)留宿主委托;`hover_probe()` 留宿主读件投影(返回语义零变化);`_r0` 留宿主
- [ ] 孤立验证:`tools/probe_gate.sh --click`+`--full` 全绿;`tools/render_gate.sh` 棋盘 golden 三张差异 0.00%(非零即停机呈报)
**T2 breakpoint_store 拆件(1–1.5 工作日)**
- [ ] 建 `game/core/breakpoint_store.gd`(RefCounted;构造=引用束注入:db、rng、主角账三件、curio/stop/bank/loadout 引用——防 max-parameters 新键)
- [ ] 断点域 6 成员+`BREAKPOINT_FIELDS`+函数族 9 个归件(v1.1 R2 整族归件,机制=所有权缝语句分家):纯读侧三函数(`_bp_read`/`_bp_strategy`/`_bp_read_int`)逐字;`_bp_parts`/`_snapshot_breakpoint` body 逐字(宿主输入 state/level_index/master_seed/runner/adventure_counters 逐调传参——件不反向持宿主);四 `_resume_*` 按所有权缝语句分家——件侧语句(主角账三件片段回填、curio 池与特性持有集还原、stop/bank 片段回填、六成员改写)迁入 store,宿主侧语句(summary/levels_total/level_index/_shop_mode/_treasure_unlocked/_attunement/_tree_rules/adventure_counters 重绑定与 `_reset_curio_state`/`_attune_refresh_x_total` 宿主调用)按原语句序吸收进 `resume_adventure` 壳,语句序逐步保形、27 键冻结;宿主保留 `_snapshot_breakpoint` 一行委托壳(CurioStop 的 Callable 目标零改)与 `set_save_manager` 转发件;宿主对六成员的其余读写点改址经件
- [ ] `breakpoint_resume_test.gd:185` 改址为 breakpoint_store 常量(判据零变化——第 14 节申报)
- [ ] 孤立验证:`--script res://tests/run_all.gd -- --only=breakpoint_resume,save_rng_roundtrip,cross_system_flow` 全绿+`python tools/run_smoke.py`
**T3 键账对账与收口(0.5 工作日)**
- [ ] 批末代码域闸全绿(`tools/run_all_gates.sh --code`)
- [ ] 三键终值对账(board_view 约30/27、adventure_runner 约22)——留债口径记入实现报告
- [ ] 产出物落位卡:实现报告与反哺点清单写齐方算收口
合计估时约 2.5–3.5 工作日。编队注记:编入待迭代 42 合回;**board_hover 半边硬前置=呈现层状态件批先合回**(该批改型 u.st 写入面后,hover 拆件在新写入面上施工——grilling 复核定级,原「推荐先行」升格);breakpoint_store 半边无此依赖,可独立先行。另:本卡与组合根注册面批共享 ui_input_probe.gd 触面(该批改 main.X 地址面、本批不改探针内 u.face 读面)——同代编排时先后皆可。
## 8. 验证闸——改动—验收对照表
域口径:本批改动全为 `game/` 下非 .md 行为载体——批末跑代码域闸;里程碑步(T 段)出口跑一次全套闸,波内修复只做出红最小单元的最窄确认,新增测试先行孤立验证再并入全套。总判据=每行改动以「既有断言面全绿+golden 零差异」证明零行为漂移;全量闸电池=冒烟+系统测试+run_all 全量+probe_gate --click --full+render_gate+static_scan(含 S13 对账)。
| 行 | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| V1 | 悬停域拆件 | board_hover.gd 新建;board_view.gd 函数族与 :70-71 成员迁出 | 复用 ui_input_probe 悬停三语义断言(case①到达/②禁入/③路径预览)+ui_click_probe+type_glow_shot_probe;新增断言零条 | 探针 | 合成 InputEventMouseMotion/MouseButton 经 `_gui_input` 入站口;`hover_probe()` 读面 | 探针可见性断言面(hover_probe 返回值、光标形状、格件状态面边框/点迹字段——施工时点在呈现层状态件批合回后,按其改型后投影面口径核对,判据语义零变化)+golden 三张零差异 | 既有:棋盘悬停反馈(三语义光标/目标格边框/路径预览点迹) |
| V2 | 断点域拆件 | breakpoint_store.gd 新建;adventure_runner 断点函数族与 6 成员迁出 | 复用 breakpoint_resume_test 全套+save_rng_roundtrip_test+cross_system_flow_test:_feats_breakpoint_roundtrip;新增断言零条;:185 改址(第 14 节申报) | 单元+集成 | AdventureRunner.resume_adventure()/start_adventure() × SaveManagerCore 断点域 | 断点快照往返逐键断言面(27 键冻结——键名/策略/键序零变化) | 既有:断点片段往返与续玩幂等;既有:旧档迁移链与断点续玩零跳号(集成) |
反向核对:diff 中每个行为改动均有行——本批申报行为改动为零(全部非行为改动=结构整改,判据=零漂移证明)。孤儿断言:无(零新增断言)。
## 9. 异常分级
- **L1 自救**:迁移函数编译错、探针调用面改址漏改——就地修复重跑最窄确认。
- **L2 记录继续**:实测值与预期区间偏离但同档位——记入实现报告「实现决定记录」。
- **L3 停机写报告等用户**:①拆分边界纪律④触发(含 `_r0` 归属认定被实现期证伪);②机检预算互斥——新件实数超第 12 节预算或迁移函数现出超标键=停卡退回写卡段;③golden 对拍差异非零;④S13 键恶化或基线外新键;⑤断言面被下游兜底吸收致判据绑不上。
## 10. 代拍板白名单
1. 迁移函数头注指址微调(词表与语义不动)。
2. 新件私有 helper 命名微调(职责语义不变)。
3. 断言失败消息尾缀〔补件 Vn〕分配与措辞。
4. 改址机械改写(preload 顺序、空行排版)。
## 11. 待裁点
零待裁点,开工门=U0。(裁决记录:2026-10-29 用户裁——恢复 D2/D4 拆件;D5 五降值键留债、零豁免延伸;`_r0` 留宿主;断点域六成员全迁。2026-10-30 用户裁——T2 机制=R2 整族归件、四 `_resume_*` 所有权缝语句分家、宿主侧语句吸收进壳;呈报=reports/iter45-停机呈报-断点域拆件规格缺口-2026-10-30.md。)
## 12. 落位与可复用性预检
- **board_hover.gd(新增)**:复用面核查——悬停预承诺逻辑现行全在 board_view(11 函数族全仓无第二实现,无既有件可复用);不能并入 TilePalette(瓦片装载)、HeroSprite(英雄演出)、CellUnit(格件绘制)——悬停预承诺与三者是不同变化原因。落点唯一:`game/presentation/views/` 新件,board_view `_init` 建为子件(HeroSprite 挂载先例)。调用路径:只被 board_view 持有与调用,输入经宿主入站口委托,禁反向持宿主引用(注入回调单向)。
- **breakpoint_store.gd(新增)**:复用面核查——断点拼盘与读侧策略现行在 adventure_runner(`BREAKPOINT_FIELDS` 登记单源唯一);不能并入 save_manager(存档 IO 原语与冒险断点语义是两个变化原因——SlotStore 拆分已立界)、不能并入 MetaStateStore(元进度数据件)。落点唯一:`game/core/` 新件。调用路径:只被 adventure_runner 调用;宿主经件组装快照后写盘,应用层落盘口不变。
- **新增函数**:零——全部为既有函数体逐字搬运。零新增数据件、零新增常量表(`BREAKPOINT_FIELDS` 为迁移非新增)。
## 13. 实现规范预算(gdstyle 默认阈值对账)
| 新件 | 公开方法 ≤20 | 类变量 ≤15 | 迁移函数对账 |
|---|---|---|---|
| board_hover.gd | 预算 6(悬停驱动入口) | 预算 2(`_hover`/`_last_click`)+引用束约 7(cells/minesweeper/diggable 引用+光标写入与 hover_forbidden 注入 Callable)=合计约 9 | 11 函数现行均无超标键(基线无其函数级键);`_apply_hover_visuals` 分支 5 |
| breakpoint_store.gd | 预算 8(快照/回填/读侧入口族) | 预算 6(拟拆成员)+引用束约 14(db/rng/主角账三件/curio/stop/bank/loadout)=合计约 20?否——引用束为单成员字典注入,实计=6+1 束+件内散标 ≤15(T0 按实数对账,贴顶禁再增) | `_bp_parts` 现行 49 行/9 局部(:583-630 实测)均在默认阈值内;`_resume_*` 四函数无超标键 |
存量触面一查:board_view 6 键与 adventure_runner 1 键 T0 读数写明「S13 键较登记值只降不升」预期;`_init` 一律引用束注入防 max-parameters 新键。
## 14. 测试削弱申报
1. `breakpoint_resume_test.gd:185`——`AdventureRunnerScript.BREAKPOINT_FIELDS` 直读随件迁改址为 breakpoint_store 常量;判据零变化,仅常量承载文件换代。
2. 除此之外禁删断言、禁放宽阈值、禁缩小扫描面、禁把真实入口换成下层直调;未申报的削弱由独立验收审计判红。
## 15. 独立验收审计
合并前由未参与实现的会话或子代理执行;报告落 `reports/iter<N>-验收审计-<日期>.md`。审计至少做四件事:逐行核对第 8 节对照表(测试存在、真执行、尾缀可 grep、真实入口未绕过改动点);抽查判据绑定面;检查测试削弱(对照第 14 节申报);核对两行「非行为改动」判定的零漂移证明是否在当轮真实跑批中成立。未过不得合并。
## 16. MC/数值与 RNG 零触碰声明
本批零数值改动、零数据表改动、零 RNG 流词表改动——断点回放 restore 同序取号、悬停域无随机。MC 对拍不跑。
@@ -0,0 +1,172 @@
# 需求卡片——呈现层状态件批
> **版本状态**:v1.0 定稿——零待裁点(2026-10-29 用户裁:单格两文件案、站桩两文件案、探针计数器留宿主、st/mo 命名、潜伏红随批修复)。
> **接收方**:待办清单条目「零豁免五键清偿批——S13 余量五键真修四卡(2026-10-29 登记)」(本卡=四卡中的「呈现层状态件批」一卡,承接 board_cell_unit 类变量 30→10 与 standee_body 类变量 19→10 两键)。
> **来源**:两份设计定稿全文。**唯一事实源**:`reports/设计-单格绘制状态件-2026-10-29.md`+`reports/设计-站桩状态件-2026-10-29.md`;`design/engineering/architecture/architecture.md` v1.25「模块归属·呈现层」表 cell_base_layer/cell_mark_overlay 行(:169)与 standee_vitals/standee_motion 行(:170)。
## 1. 目标与完成定义
这批做什么:把两个呈现层宿主的内部状态件沿职责缝拆成四个独立文件类——单格 `board_cell_unit.gd` 的 `_CellPaintState`(27 成员)按绘制层域轴拆 `cell_base_layer.gd`(12 成员)+`cell_mark_overlay.gd`(13 成员),宿主 `_draw` 退六步时序器、类变量 30→10;站桩 `standee_body.gd` 的 `_StandeeState`(真实 25 成员——基线 19 是 gdstyle 计数截断的欠估读数)按「身份命值/资产演出」缝拆 `standee_vitals.gd`(12 成员)+`standee_motion.gd`(11 成员),三个演出探针计数器留宿主、类变量 19→10。宿主实例命名:单格两件 `base`/`face`;站桩沿用 `st`(指 vitals)+新增 `mo`。附带处置一处潜伏红:ui_input_probe 四处绕过状态件直读格件成员的断言疑似已被迭代 41 状态件化打断,T0 实跑定谳,确红则随批修复。
什么状态算完:
1. 四新件在库、两宿主瘦身到位、六个 gdstyle 键账达预期(board_cell_unit 10/standee_body 10——较登记值 30/19 只降不升;四新件 12/13/12/11 全部阈值内零新键)。
2. 行为零变化:棋盘 golden 三张(L1 dungeon/L2 underdark/L3 deep_underdark)对拍差异 0.00%;探针冻结成员(glyph/fog_state/plate/glyph_color)与站桩演出时序、三条源码锚逐字不动。
3. 潜伏红定谳结论记实现报告;确红则四处改址落地、probe_gate 全绿闭环。
4. 批末代码域闸全绿;实现报告与反哺点清单写齐方算收口。
## 2. 设计事实源
两份设计定稿(2026-10-29 设计闭合,待办清单条目凭据与本节一致):单格篇 §4 拆分定案(两件案 A2、成员总账 10/12/13、写入路径对账表 §5.1、六步时序器 §6、键账预期 §7)、§1.2 gdstyle 截断缺陷与 §1.3 潜伏红发现、§1.4 探针直读面全清单;站桩篇 §3 方案 C(两独立文件+计数器留宿主)、§4 外部写入方零改动清单、§5 键账预期、§6 行为零变化六点。architecture.md v1.25 两行在册(呈现层表 :169-:170——职责语句、成员数、宿主 30→10/19→10)与「gdstyle 计数截断缺陷登记」节。
## 3. 现状事实(2026-10-29 主区实查;行号为迭代 42 合回前盘面——T0 必须在迭代 42 合回后按当轮 master 重锚)
**编队注记**:本卡与迭代 42 有 board_view 触面耦合——迭代 42(在飞,roadmap 迭代 42 行「进行中」)尾波清偿批 T1 把 `_paint_cell` 拆五段(`_paint_cell_base/_ink/_facade/_overlays/_fog`,段间只经 u.st/glyph/digit_col 传值)并抽 `_build_cell_unit`(:246-262),改变 u.st 写入面的函数分布结构(写入语句从单函数分散进段函数),写入路径形态 u.st.X 本身不变。**本卡 T0 必须在迭代 42 合回后执行**;本节行号按当前盘面实查,届时 board_view 全部写入点行号漂移、以 T0 重锚为准。反向编队义务:尾波补件批(board_hover)已裁「u.st 写入面改型后 hover 拆件在新面上施工」——本批先于其 board_hover 半边合回。另:本卡与组合根注册面批共享 ui_input_probe.gd 触面(该批改 main.X 地址面、本批改四处 u.face.* 读面,互不覆盖但同文件)——同代编排时禁并行、先后皆可,编入前置声明互指。
- **键账**:`tools/gdstyle_baseline.txt` :16 `board_cell_unit.gd|quality/max-class-variables = 30`、:23 `standee_body.gd|quality/max-class-variables = 19`。源码实查:board_cell_unit.gd 全文件 33 个类级 var(宿主 6+`_CellPaintState` 27,:26-122 内嵌类/:125-143 宿主);standee_body.gd 全文件 31 个(宿主 6+`_StandeeState` 25,:23-59 内嵌类/:62-68 宿主)。登记值 30/19 均为 gdstyle 对 inner class「首个带行尾注释属性截断」的欠估(architecture v1.25 已登记该工具缺陷;board_view.gd 无内嵌类、32 逐一全计——平面口径预测即实测)。
- **单格写入方**:唯一写方文件 board_view.gd。写入点实查——set_world_font:185(font)、set_cell_area:204(edge)、rebuild:250/251/253/260(cell/edge/bevel/font)、悬停面 :512-534(hl_visible/hl_color 五组写+一清)、路径预览 :549/:554(dot_visible)、`_paint_cell` :601-738(tex_seal:614/618、tex_base:665、bg_sb:666、glyph:667、glyph_color:668/674、plate:675、outline_sides:676/700、facade_south:677/690、proj_band:678/704、tex_facade:679/709、facade_strip:680/708、treasure_mark:713、badge_glyph:719/726、mutation_mark:723/725、glow_group:727、fog_state:734/736、removed_dim:737、终写 queue_redraw:738)、接触带 :780/:787/:804/:805/:810/:816、glow 同步 :768、cell_rect_probe 读 :891(u.st.edge)。
- **站桩外部触面**:唯一写状态的外部方 battle_stage_view——slot_row :140/:265、box :198/:208、font :232/:235、set_portrait_texture/register_hero_state :132-138、set_hero_state :313/:316/:388、演出三方法 :333-336;hud_presenter :77/:78/:137/:162/:163(set_hero/set_effects/set_effects_cleared_feedback);fx_probe 对 box 直写三处。测试直读 standee_fx_sweep_test.gd:103(`s1._intent_charge`);源码锚 theme_projection_test.gd:678-683(GOLD_BRIGHT 顺位墨/HERO_BLUE_BRIGHT 与 GOLD_BRIGHT 护甲墨/`_intent_charge = pid in CHARGE_SKILL_IDS` 三条 contains 断言)。
- **潜伏红(T0 定谳)**:ui_input_probe.gd 四处绕过 st 直读格件成员——:234-235(case① `u1.hl_visible`/`u1.hl_color`)、:243-244(case② `.hl_color`)、:250(case③ `.dot_visible`)、:374-379(case⑭ `.mutation_mark`)。这四个成员迭代 41 已迁入内嵌类 `_CellPaintState`,宿主 Node2D 上不存在同名属性,外部读预期运行时「Invalid get index」错误。时间线:探针末次全绿 2026-10-28 05:03 早于状态件合入 2026-10-29 01:37,合入后未跑过。设计文档同篇已含改址方案(u.face.*)。
- **探针冻结面(零改动义务)**:ui_input_probe.gd:271/:363(u.glyph)、:400-:492(u.fog_state case⑮族);player_copy_sweep_test.gd:138-140(u.plate/u.glyph_color wa8)。plate 绘制侧零消费(禁删禁接线——接线即行为变化)。
- **演出探针计数器**:standee_body.gd:57-59(_death_shards/_hit_sparks/_flash_count);唯一写入方宿主函数 :275/:315/:336,唯一读方 fx_probe() :301。
- **文件体量**:board_cell_unit.gd 423 行(迭代 41 已拆函数级键、本设计只解决类变量键;拆后宿主约 150 行);standee_body.gd 531 行。
## 4. 设计契约(违反即返工的红线条款)
- 🔴 **行为零变化**:绘制调用序与层序零变化——宿主 `_draw` 六步时序器与现行四函数调用树逐命令等价(设计文档单格篇 §6 四点论证:层间序/件内逐字搬运/接触带回退补画序整体保形/浮点推导式零改动);接触带「平色回退+微光并发的英雄格补画」重复绘制路径原样保形;setter 多写多触发序不变(facade_south/badge_glyph/mutation_mark 双写点);站桩六点(tween 路径串、三态机早退序、`_hp_flash_tw` 回色读 `_dead` 同拍、源码锚逐字、成员初始化时序、计数器纯易位)=站桩篇 §6。
- 🔴 **golden 三张零差异**:render_gate 棋盘 golden(L1 dungeon/L2 underdark/L3 deep_underdark)对拍差异 0.00%;非零即停机呈报,禁换锚、禁重制基线。
- 🔴 **冻结面逐字**:探针冻结成员 glyph/fog_state/plate/glyph_color 宿主保留、名称路径零变化;站桩三条源码锚所在函数留宿主且锚行逐字;battle_stage_view 一行不改(写入面零改动清单=站桩篇 §4)。
- 🔴 **拆分边界纪律**(判据权威=code-conventions §1.1,卡内执行条款;实现报告逐件一行职责语句):①拆出单元须有可陈述职责语句与单一变化原因——四件职责语句与宿主保留面语句照设计文档 §4.2/§4.3/§4.4 与站桩篇 §3 逐字承载(基座层绘制域/标识叠加绘制域/身份命值唯一账面/资产挂点与一次性演出;单格宿主=被动绘制壳+迷雾域+微光子件生命周期);②集体身份——命不出名字的打包=凑计数袋,禁;③「过阈值」只作发现信号,拆分动机是职责轴(迭代 41 函数级已沿同一条绘制层域轴,本批升到类级);④拆不出内聚单元→停卡呈报;⑤禁沿阈值轴拆。
- 🔴 **禁为机检文本保留无效成员**:禁止为迎合机检消息文本(含计数)保留或新增无效成员/别名;机检键与卡片契约互斥=实现段停卡呈报,不自造第三条路。
- 🔴 **gdstyle 截断缺陷声明**:本卡不依赖任何解析行为——四新文件与两宿主全部平面口径(无内嵌类),实数即闸读数;基线登记 30/19 属截断欠估,禁任何利用截断口径的降数手法(重排成员顺序/挪 setter 位置/删行尾注释),禁把 `_portrait_tex` 这类承重槽位当削减目标凑数。
- 🔴 **潜伏红处置纪律**:T0 一次 ui_input_probe 实跑定谳——确红=已申报的存量红承接,随批修复(T3 四处改址 u.face.*,断言条件与期望值零变化);禁静默放过、禁在实现报告外私记了结。
## 5. 范围边界
做什么:第 3 节两拆件项(单格两域件+宿主上收+board_view 写入面改址;站桩两状态件+宿主转调壳)、潜伏红定谳与四处改址、键账对账收口。
不做什么:迭代 42 在飞范围零触碰(_paint_cell 拆五段、_build_cell_unit、BoardInstaller、商店委托壳);board_hover/breakpoint_store(尾波补件批承接,且其 board_hover 半边待本批合回后施工);main.gd/battle_orchestrator/tune_sim 其余三键(零豁免五键各卡承接);`_portrait_tex` 冗余归一(站桩篇 §9-3 留债册);基线收账(各批合回后用户发令一次人工 `--refresh --force`,本批不刷新基线)。
## 6. 关联文档同步面
| 文档 | 关联点 | 同步义务 |
|---|---|---|
| design/engineering/architecture/architecture.md | 呈现层表 cell_base_layer/cell_mark_overlay 行与 standee_vitals/standee_motion 行(建件后「实现批建件」改现状登记);「gdstyle 计数截断缺陷登记」节的欠估数字(30/33、19/31——收账归后续人工 refresh 批) | 设计权威文档——实现批禁直改;逐条列进 `reports/iter<N>-反哺点-<日期>.md`,用户裁决后归文档批落文 |
| game/tests/README.md | 「棋盘悬停反馈(三语义光标/目标格边框/路径预览点迹)」行——真实入口(ui_input_probe 合成鼠标事件→BoardView._gui_input();读面 hover_probe())与代表用例(ui_input_probe.gd:_ready)预计零变化,批末核对;「地形瓦片套与格面数字可读」「站桩敌人呈现与结算演出落点」「未开格类型微光」「玩家面文案与危险格格面字形」各行代表用例列核对(本批零新增能力、复用既有载体,预计零改动) | 机械面文档——实现批禁写;批末收尾核对表上名字与实际落地一致(改名则回表) |
| design/gdd/ | 无关联文档——结构整改不改游戏规则与玩家可见行为 | 无修订预期;契约句被触碰=停卡呈报走反哺点通道 |
## 7. 文件结构图(卡前)
| 文件 | 新增/修改 | 职责 | 行为属性 |
|---|---|---|---|
| game/presentation/views/cell_base_layer.gd | 新增 | 单格基座层绘制域件:12 成员+九绘制函数逐字搬运+九个 D/E 档常量随迁+公开入口 draw_base | 非行为改动(V1) |
| game/presentation/views/cell_mark_overlay.gd | 新增 | 单格标识与叠加层绘制域件:13 成员+六函数段随迁+draw_centered_text 单点化+两绘制入口 | 非行为改动(V2) |
| game/presentation/views/standee_vitals.gd | 新增 | 站桩身份与命值态件:12 成员+apply_hp(原 _set_hp 整体搬移)+hp_value setter 逐字 | 非行为改动(V3) |
| game/presentation/views/standee_motion.gd | 新增 | 站桩资产与演出态件:11 成员+六迁入函数(两处改形点见 T2) | 非行为改动(V3) |
| game/presentation/views/board_cell_unit.gd | 修改 | 宿主瘦身:类变量 33→10(edge/cell/glow_group 上收+base/face 两实例)、_draw 退六步时序器、雾/glow/记忆态留宿主、头注改写(≤8 行) | 非行为改动(V1/V2) |
| game/presentation/views/board_view.gd | 修改 | 写入面改址约 40 处(u.st.X→u.base./u.face./u.edge/u.glow_group/u.cell,全清单=设计文档单格篇 §5.1/§5.2 对账表;行号 T0 重锚)+cell_rect_probe 一处 | 非行为改动(V1/V2) |
| game/presentation/views/standee_body.gd | 修改 | 宿主改造:类变量 31→10(五冻结 var+st+mo+三计数器)、同名转调壳、tween 路径串改指、源码锚行逐字不动 | 非行为改动(V3) |
| game/probe/ui_input_probe.gd | 修改 | 四处直读改址 u.face.*(T3,:234-235/:243-244/:250/:374-379) | 非行为改动(V4——判据零变化;T0 确红时兼具存量红修复身份) |
零新增数据件、零新增常量表(九常量为迁移非新增)、零新增测试件(全部复用既有断言面)。行为改动核对:本批申报行为改动为零——全部改动为逐字搬运、换参形与读路径改址;判据=零漂移证明(golden 零差异+既有断言面全绿+源码锚逐字)。
## 8. 任务卡 T0..T4
**T0 重锚与潜伏红定谳(0.25–0.5 工作日)**
- [ ] 前置确认:迭代 42 已合回 master(`_paint_cell` 五段在盘)——未合回即停,T0 不开工(编队注记)
- [ ] 以当轮 master 重测六个键账读数(board_cell_unit 33 声明/30 闸账、standee_body 31 声明/19 闸账——截断口径双读数对照)与第 3 节全部 file:line;board_view 写入点按五段新分布逐点重锚(路径形态核对仍为 u.st.X)、漂移处就地改卡并记录;对不上即停卡呈报
- [ ] ui_input_probe 一次实跑定谳潜伏红:红=确证设计文档单格篇 §1.3;绿=证伪设计前提(L3 停机呈报,见第 10 节)——定谳结果记实现报告
- [ ] 复核迁移函数零超标键:`tools/gdstyle_baseline.txt` 无 board_cell_unit/standee_body 任何函数级键(实查确认)——九+六函数与六搬移函数逐字/换参搬运零新键
**T1 单格两域件建件与迁移(1.5–2.5 工作日)**
- [ ] 建 `game/presentation/views/cell_base_layer.gd`:extends RefCounted、无 class_name、`_init(owner: Node2D)`;12 成员+四 setter 体逐字;九函数(_draw_base_layer/_draw_outline_sides/_draw_south_facade/_draw_north_projection/_draw_e_facade/_draw_contact_or_fallback/_draw_contact_bands/_draw_contact_corner/_draw_bevel)逐字搬运,三处机械改写=`st.X`→裸成员名、`draw_X(...)`→`canvas.draw_X(...)`、`st.edge`→`rect.size.x`;公开入口 `draw_base(canvas: CanvasItem, rect: Rect2)`;九常量随迁(FACADE_HIGHLIGHT_PX/FACADE_NEAR_FRAC/FACADE_FAR_FRAC/FACADE_NEAR_COL/FACADE_FAR_COL/PROJECTION_NEAR_COL/PROJECTION_MID_COL/PROJECTION_EDGE_COL/E_FACADE_FRAC——board_cell_unit.gd:12-23,UiKit.E_FACADE_FOOT_COL 不动)
- [ ] 建 `game/presentation/views/cell_mark_overlay.gd`:同形态;13 成员+八 setter 体逐字(含 badge_glyph 的 `_owner._glow_layer` mask 回引);六函数段随迁(_draw_mutation_marks/_draw_glyph_layer/_draw_digit_plate/_draw_glyph/_draw_badge+_draw_overlay_layers 的 dot/hl/removed 三段);公开入口 `draw_marks`/`draw_glyph_layer(canvas, rect, glyph, glyph_color)`(4 参)/`draw_overlay_under`/`draw_removed_dim`/`draw_centered_text(canvas, rect, text, color, font_size)`(5 参,文字渲染单点化)
- [ ] 宿主瘦身:board_cell_unit.gd——`base`/`face` 两实例于 `_init` 自建(preload 常量 `_BASE_LAYER`/`_MARK_OVERLAY`,设计文档 §4.5);edge/cell/glow_group 上收宿主(setter 体改直调自有面:edge 的 glow setup+queue_redraw、glow_group 守卫调 `_sync_glow_layer()`);glyph/glyph_color/plate/fog_state/_glow_layer 原位;`_draw` 改六步时序器(base.draw_base→face.draw_marks→face.draw_glyph_layer→face.draw_overlay_under→`_draw_fog_layer` 原位→face.draw_removed_dim,设计文档 §6 逐字);`_sync_glow_layer`/`_draw_fog_memory` 改址(§5.2——记忆态墨数字改调 face.draw_centered_text,同公式同字号);头注改写(两件句+冻结清单+「hl_color/hl_visible/dot_visible/mutation_mark 探针读 u.face.*」句,≤8 行)
- [ ] board_view 写入面逐点对账改址:按设计文档单格篇 §5.1 对账表每处核对改后形态(T0 重锚行号为准,约 40 处)——u.base.bg_sb/tex_base/outline_sides(assign 保引用语义)/facade_south/proj_band/tex_facade/facade_strip/contact_bands/contact_corners/contact_col/bevel;u.face.badge_glyph/dot_visible/hl_visible/hl_color/tex_seal/treasure_mark/mutation_mark/removed_dim/font/tex_mutation/tex_glyph/tex_badge;u.edge/u.cell/u.glow_group;u.glyph/u.glyph_color/u.plate/u.fog_state 与终写 u.queue_redraw() 不动;cell_rect_probe `u.st.edge`→`u.edge`
- [ ] 孤立验证:`tools/render_gate.sh` golden 三张差异 0.00%(非零即 L3 停机);`--script res://tests/run_all.gd -- --only=player_copy,theme_projection,type_glow,terrain` 全绿;`python tools/run_smoke.py`。probe_gate 各段本卡内暂不计绿——ui_input_probe 段在 T3 改址前处于 T0 已定谳态(存量红或改址等待),T3 闭环
**T2 站桩两状态件建件与迁移(0.5–1 工作日)**
- [ ] 建 `game/presentation/views/standee_vitals.gd`:RefCounted、无 class_name;12 成员(_owner/_kind/_display_name/_enemy_id/_armor/_order/_dead/_hp_max/_hp_cur/hp_value/_hp_tween/_hp_flash_tw);`apply_hp(hp)`=原 `_set_hp` 整体搬移(clamp、重启 `_hp_tween`、tween 写 hp_value——路径串 `"st.hp_value"` 零改);`hp_value` setter 逐字(clamp+`_owner.queue_redraw()`);`_hp_flash_tw` 归此件(flash_hit 回色分支读 `_dead` 同域)
- [ ] 建 `game/presentation/views/standee_motion.gd`:RefCounted、无 class_name;11 成员(_owner/_portrait_sprite/_portrait_tex/_hero_tex/_hero_state/_state_revert_tw/_intent_glyph/_intent_col/_intent_visible/_intent_scale/_glyph_rot);六迁入函数——`set_portrait_texture(tex, flip, box)`(box 由宿主传入)、`register_hero_state`、`apply_hero_state(state, is_hero)`(原 set_hero_state,唯一改形=`st._kind != "hero"` 早退改参判 `is_hero`,三连早退条件序逐字)、`reset_state()`(原 `_revive` 内「置 RESET 强制绕同态早退」两拍封装)、`play_intent_motion(charge)`(`_intent_charge` 改参传入;tween 路径串改 `"mo._intent_scale"`/`"mo._glyph_rot"`)、`state_probe()`
- [ ] 宿主改造:standee_body.gd——`st` 名保留改指 vitals、新增 `mo`;三计数器留宿主(:57-59 原位);同名转调壳(set_portrait_texture/register_hero_state/set_hero_state 三入口 1–2 行转调,签名外形零变);`_draw` 全族与几何函数留宿主、成员前缀改指(st.→mo. 仅资产演出域成员);六个 probe/display_name/enemy_id/is_dead 出口外形零变体内改指(fx_probe 改读宿主计数器);源码锚所在三函数(_draw_hp_bar/_draw_armor_name_line/set_pending)留宿主且 :678-683 锚行逐字不动;`_intent_charge` 与五冻结 var 原位
- [ ] battle_stage_view 写入面对账(站桩篇 §4 清单逐点):结论=零改动——slot_row/box/font 直写原位、三同名转调入口承接、演出三方法原位;hud_presenter :77/:78/:137/:162/:163 零改动;实机核对一遍
- [ ] 孤立验证:`--script res://tests/run_all.gd -- --only=standee_fx_sweep,theme_projection` 全绿;孤立验证同 T1 口径(probe_gate 推迟 T3 闭环)
**T3 潜伏红处置(0.25–0.5 工作日)**
- [ ] ui_input_probe.gd 四处改址::234-235→`u1.face.hl_visible`/`u1.face.hl_color`、:243-244→`.face.hl_color`、:250→`.face.dot_visible`、:374-379→`.face.mutation_mark`——断言条件与期望值零变化,只改读路径
- [ ] 孤立验证(本卡闭环点):`tools/probe_gate.sh --click --full` 全绿——ui_input_probe 恢复绿;若 T0 定谳为红,本步同时即存量红修复闭环;`tools/render_gate.sh` golden 三张复跑一次仍 0.00%
**T4 键账对账与收口(0.5 工作日)**
- [ ] 批末代码域闸全绿(`tools/run_all_gates.sh --code`——三件保险恒跑+本卡申报套件族+探针+渲染+静态)
- [ ] 六文件键账对账:board_cell_unit 10/cell_base_layer 12/cell_mark_overlay 13/standee_body 10/standee_vitals 12/standee_motion 11(平面口径实测);两存量键 30→10、19→10「较登记值只降不升」达成;基线不随批刷新(收账归用户发令人工 refresh)
- [ ] 产出物落位卡:`reports/iter<N>-实现报告-<日期>.md`(各卡状态+潜伏红定谳结论+实现决定记录)与 `reports/iter<N>-反哺点-<日期>.md`(第 6 节所列每条有落点)写齐方算收口
合计约 3–5 工作日。切分预案:T1 超软盒时拆 T1a(基座件+宿主上收)/T1b(叠加件+board_view 改址)各 0.75–1.25 工作日。回归措辞:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套(本批零新增测试)。
## 9. 验证闸——改动—验收对照表
域口径:本批改动全为 `game/` 下非 .md 行为载体——批末跑代码域闸(重闸按域选档);总判据=每行改动以「既有断言面全绿+golden 零差异」证明零行为漂移。**静态键账判据按 test-conventions §8.4 出口在本段申报观测面局限、不入对照表**:S13 六文件键账(10/12/13/10/12/11)与两存量键只降不升(30→10、19→10)以 static_scan S13 回执为判据(可 grep 定位)——键清偿是结构判据非运行期断言,故不出现在下表。
| 行 | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| V1 | 单格基座层域件 | cell_base_layer.gd 新建(12 成员+九函数+九常量);board_cell_unit.gd 宿主上收 edge/cell/glow_group+六步时序器;board_view.gd 基座域写入点改址(对账表 §5.1 基座行) | render_gate 棋盘 golden 三张(board_L1/L2/L3)+board_shot_probe/type_glow_shot_probe 取证;新增断言零条 | 探针+golden | BoardView.rebuild()→refresh() 真实场景帧(board_shot_probe) | golden 逐像素容差比对(差 ≤16/changed ≤2%)差异 0.00%=绘制命令流逐位;微光子件生命周期取证(glow_group setter 直调时序零漂移) | 既有:地形瓦片套与格面数字可读;既有:未开格类型微光(8 邻作用集/五组色相/零玩法写入) |
| V2 | 单格标识叠加域件+冻结面保留 | cell_mark_overlay.gd 新建(13 成员+六函数段+draw_centered_text);board_cell_unit.gd 四冻结成员原位;board_view.gd 标识域写入点改址+cell_rect_probe 一处 | 复用 player_copy_sweep_test.gd:_sweep_board_glyph+wa8(:138-140 plate/glyph_color 直读零改动);ui_input_probe case④b/⑪/⑮(glyph/fog_state 冻结读面);run_all 02 闸选跑+render_gate golden;新增断言零条 | 单元+探针+golden | BoardView.rebuild()→refresh()(player_copy 夹具真实构建);ui_input_probe 合成鼠标事件 | u.plate/u.glyph_color/u.glyph/u.fog_state 直读断言零改动全绿;golden 三张零差异(雾层原位夹在 hl 与 removed 之间) | 既有:玩家面文案与危险格格面字形;既有:地形瓦片套与格面数字可读 |
| V3 | 站桩两状态件 | standee_vitals.gd/standee_motion.gd 新建;standee_body.gd 宿主 10 成员+转调壳+tween 路径串改指;battle_stage_view 写入面零改动 | 复用 standee_fx_sweep_test 全套(:103 `_intent_charge` 直读、fx_probe().flash_count、enemy_id 解析)+theme_projection_test 三条源码锚(:678-683 逐字)+probe_gate --full 段(fx_probe/ui_probe:碎裂 5/火花 6/闪白计数/三态切换/意图三档/name_line/portrait/has_world_hp_bar/slot_row)+ui_click_probe tooltip;新增断言零条 | 单元+探针 | StandeeBody 公开方法面(set_data/set_hero/set_pending/mark_dead/flash_hit)与 battle_stage_view.on_step_row_fx 演出链;探针场景真实实例化 | fx_probe()/intent_probe()/hero_state_probe() 出口返回逐键;源码锚 contains 断言逐字命中;tween 属性路径串("st.hp_value"/"mo._intent_scale"/"mo._glyph_rot")由意图弹入与三态断言兜底 | 既有:站桩敌人呈现与结算演出落点;既有:UI 全局主题与按钮五态(含对比度锚) |
| V4 | 潜伏红处置与探针改址 | ui_input_probe.gd:234-235/:243-244/:250/:374-379 四处直读改址 u.face.*(判据零变化;T0 确红时兼具存量红修复) | ui_input_probe 全套断言改址后执行且绿(case①②③⑭);probe_gate --click --full 全绿 | 探针 | ui_input_probe 合成鼠标事件→BoardView._gui_input() | 四处断言执行且绿(悬停金框/禁入红框/路径点迹/变异角标,读路径 u.face.*);probe_gate 三硬闸全过=存量红闭环 | 既有:棋盘悬停反馈(三语义光标/目标格边框/路径预览点迹) |
反向核对:diff 中每个改动均有行——本批申报行为改动为零(结构整改,判据=零漂移证明),V1–V4 四行覆盖全部八个文件行。孤儿断言:无(零新增断言)。
## 10. 异常分级
- **L1 自救**:迁移函数编译错、改址漏改、preload 常量名笔误——就地修复重跑最窄确认。
- **L2 记录继续**:实测行号与第 3 节偏离但路径形态一致(T0 重锚记录);探针段实测时长同量级漂移。
- **L3 停机写报告等用户**:①机检预算互斥——新件实数超第 14 节预算或迁移函数现出超标键=上游缺陷停卡退回写卡段;②golden 对拍差异非零(禁换锚禁重制基线);③潜伏红证伪设计前提——T0 实跑 ui_input_probe 现状绿,即设计文档单格篇 §1.3 的 GDScript 取索引语义判断被证伪(改址四处义务不变,等用户裁决后继续);④S13 键恶化或基线外新键;⑤拆分边界纪律④触发(按职责轴拆不出内聚单元);⑥断言面被下游兜底吸收致判据绑不上(test-conventions §8.4 出口呈报)。
## 11. 代拍板白名单
1. 迁移函数与新件头注指址微调(职责语句、冻结清单句与语义不动)。
2. 新件私有 helper 命名微调(职责语义不变;公开入口名按第 8 节逐字)。
3. 改址机械改写(preload 常量顺序、空行排版、注释随迁改写)。
4. 断言失败消息尾缀〔状态件 Vn〕分配与措辞。
5. T0 重锚产生的行号漂移卡面注记更新(不改判据与改后形态)。
## 12. 待裁点
零待裁点,开工门=U0。(裁决记录:2026-10-29 用户裁——单格两文件案〔淘汰整体一件 A3 与成员削减路 B〕;站桩两文件案+三个演出探针计数器留宿主〔淘汰原地 −4 案〕;实例命名 st 沿用/mo;潜伏红随批修复〔弃「先最小工程批改 u.st.* 再二次改址」时序〕;cell 成员保留〔宿主 10 含 cell——弃删除倾向〕;gdstyle 截断口径登记已由 architecture v1.25 落文收口。)
## 13. 落位与可复用性预检
- **cell_base_layer.gd(新增)**:复用面核查——基座层绘制现行全在 board_cell_unit 内嵌类+宿主九函数(全仓唯一实现,无既有件可复用);不能并入 TilePalette(瓦片装载与查表件、架构表明载零绘制面——职责互斥)、不能并入 BoardWorld(世界根/材质挂点)、不能并入 CellUnit 本体的字形迷雾域(变化原因不同)。落点唯一:`game/presentation/views/` 新件,board_cell_unit `_init` 自建持有(每格 Node2D+两 RefCounted,与现状同量级;件内自建不触发 allocation-in-loop 新键——构建器函数体不在循环体内)。交互路径:宿主持有两件引用;件→宿主仅 `_owner.queue_redraw()`/glow mask 回引(与现状 inner class 同形);宿主迷雾记忆态读 `base.tex_base` 公开成员单向(成文于宿主头注)。
- **cell_mark_overlay.gd(新增)**:同上排除;迷雾留宿主是三域中唯一不产生跨件回引的落点(设计文档 §4.4——雾夹在两件入口之间、读件 A 纹理与件 B 字体渲染);`draw_centered_text` 单点化供宿主复用(零复制)。
- **standee_vitals.gd/standee_motion.gd(新增)**:复用面核查——25 成员现行全在 `_StandeeState`(全仓唯一);不能并入 battle_stage_view(壳层视图,站桩本体是世界件)、不能并入 StatusSlotRow(壳层伴随件)。落点唯一:`game/presentation/views/` 新件,standee_body 持有 st/mo;外部仍只经宿主方法与五冻结 var(box/font/slot_row/has_world_hp_bar/_intent_charge 原位),零新增外部直访面;`_owner` 回引同形、禁反向引用。
- **四新件对既有周边件逐一排除**(TilePalette/HeroSprite/CellUnit/BoardWorld,架构文档呈现层表在册件):TilePalette=瓦片装载与查表件、架构表明载零绘制面——四件都不并入;HeroSprite=英雄精灵演出件(落位/位移/帧闪)——与四件变化原因不同;CellUnit 本体(board_cell_unit 宿主)=两格件的持有壳而非合并对象(拆分方向是自宿主出、非并入宿主);BoardWorld=棋盘 Node2D 世界根(材质挂点)——零绘制入参承载。站桩两件另排除 battle_stage_view(壳层视图)与 StatusSlotRow(壳层伴随件)。
- **新增函数清单**:apply_hp/set_portrait_texture(3 参形)/apply_hero_state(is_hero 参形)/reset_state/play_intent_motion(charge 参形)/state_probe/draw_base/draw_marks/draw_glyph_layer/draw_overlay_under/draw_removed_dim/draw_centered_text——全部为既有函数体整体搬移或既有函数换参形与改名入口,无新逻辑。新增前已 grep 同名标识符:`apply_hero_state`/`reset_state`/`state_probe`/`draw_base` 等盘上无第二处。
## 14. 实现规范预算(gdstyle 默认阈值对账)
| 新件 | 公开方法 ≤20 | 类变量 ≤15 | 迁移函数对账 |
|---|---|---|---|
| cell_base_layer.gd | 预算 5(draw_base+4 setter) | 12(设计文档 §4.1 实数) | 九函数逐字搬运——基线无其函数级键(T0 实查)、行数/分支/局部/返回不变=零新键 |
| cell_mark_overlay.gd | 预算 13(5 绘制入口+8 setter) | 13(同上) | 六函数段同上;draw_glyph_layer 4 参、draw_centered_text 5 参(max-parameters 默认 5=合规上限内) |
| standee_vitals.gd | 预算 1(apply_hp)+hp_value setter | 12(设计文档 §5 实数) | apply_hp 整体搬移零键(原 _set_hp 现役无超标键) |
| standee_motion.gd | 预算 6(六迁入函数)+2 属性 setter | 11(同上) | 六搬移函数逐字/换参——最大者 apply_hero_state 4 参数 4 分支全在原值内;宿主转调壳 1–2 行零键 |
存量触面一查:board_cell_unit 键 30→10、standee_body 键 19→10——T0 写明「S13 键较登记值只降不升」预期与达成手段(成员迁出后实数即证);board_view 六键零触碰(改址不动成员数与函数面,32 留债冻结)。宿主 `_init` 一律单参构造注入,防 max-parameters 新键。
## 15. 测试削弱申报
1. ui_input_probe.gd 四处直读改址(T3/V4)——断言条件与期望值零变化,仅读路径 u.X→u.face.X;若 T0 确红,同处兼具存量红修复(恢复断言效力)。
2. 除此之外禁删断言、禁放宽阈值、禁缩小扫描面、禁把真实入口换成下层直调;golden 三张基线禁重制;未申报的削弱由独立验收审计判红。
## 16. 独立验收审计
合并前由未参与实现的会话或子代理执行;报告落 `reports/iter<N>-验收审计-<日期>.md`。审计至少做四件事:逐行核对第 9 节对照表(测试存在、真执行、尾缀可 grep、真实入口未绕过改动点);抽查判据绑定面(golden 零差异在当轮真实跑批成立、源码锚逐字);检查测试削弱(对照第 15 节申报);核对全部行「非行为改动」的零漂移证明。未过不得合并。
## 17. MC/数值与 RNG 零触碰声明
本批零数值改动、零数据表改动、零 RNG 流词表改动;站桩演出与呼吸微光时长全经 UiKit.motion 现值、零漂移。MC 对拍不跑。
@@ -1,453 +0,0 @@
# 需求卡片:战斗区背景替换——B 区舞台插画与连续地面层
> 日期:2026-10-31
> 性质:需求改动实现批卡片;零待裁点,开工门=U0。
> 来源:待办清单「战斗区背景图替换——B 区舞台插画 + 临时光柱退役」条(唯一事实源:[art-bible](../design/art/art-bible.md) §6.1 v3.28 与 [battle-stage-prompts](../design/art/battle-stage-prompts.md) v1.7;候选样张过审 2026-10-31,候选与推荐套见 `artwork/2026-10-30-战斗区背景/候选保存清单-2026-10-31.md`)。
> 估时:T0–T5 ≈ **3.75~4.5 个工作日**;切分预案见第十三节。
## 一、设计事实源
1. [art-bible](../design/art/art-bible.md) §6.1 v3.28:B 区舞台场景构成、图层归属与资产契约——背景层三关变体(按 `board_type` 选图、不画光柱、不走 tint)、独立连续地面层(三关共用、不吃 tint、高度读 `ground_h`)、小石台保留并叠在连续地面之上;`ground_h` 字段由本实装批新增并回填。
2. [art-bible](../design/art/art-bible.md) §4.1 换色合并登记③(现行工作区 v3.29 行):`STAGE_BG`(原舞台底)随 §6.1 舞台背景插画批一并处置——背景资产接管绘制面时源常量退役或改指,禁单独为改值出批。本卡按其口径退役源常量,平色缺图回退改指 `UiKit.SLOT_BACKDROP`(#3E362C,合并目标)。
3. [battle-stage-prompts](../design/art/battle-stage-prompts.md) v1.7:三张背景与一张共用地面的画布尺寸、分层要求、否定后缀与验收;终稿尺寸 = 基准屏宽 × `stage_h`/基准屏宽 × `ground_h`。
4. 候选过审:2026-10-31 用户确认三关背景各 3 张全部可用、共用地面前后两轮修正后 v12 系列可用;遴选与后续替换/编辑由作者另行安排,不阻塞本卡。
本卡只实现上述设计,不改设计。艺术资产的最终遴选、编辑与入包走美术素材直改通道(asset-ingest),不属本卡写面。
## 二、目标与完成定义
本批把 B 区舞台从「程序化平色底+四柱光柱层」临时占位,换成「三关背景变体+共用连续地面层+保留小石台」的正式呈现,并把选图接到真实装板链上。
完成定义:
1. B 区背景按 `board_type` 选图:`dungeon` / `underdark` / `deep_underdark` 三值分别命中 `scene_stage_bg_<board_type>.png`;空串与未知值回落 `dungeon`;目标变体缺图时先回落 `dungeon`,仍缺则保留平色底 `UiKit.SLOT_BACKDROP`(#3E362C,原舞台底合并目标)。
2. B 区底部新增共用连续地面层 `scene_stage_ground.png`,底边贴 B 区底、全宽、高度读布局表 `ground_h`;三关共用,不按 `board_type` 换图。
3. 布局表新增 `ground_h` 字段(本卡回填 100)并带加载期校验;`tools/layout_config.py` 同步读取该字段。
4. 四柱光柱程序层退役:删除光柱绘制层与 `UiKit.BEAM_LIGHT`,任何缺图态也不出现光柱;舞台底源常量 `UiKit.STAGE_BG` 随背景接管退役,平色回退改指 `UiKit.SLOT_BACKDROP`(#3E362C,换色合并③目标);背景与地面均不引用 `wall_ambient` tint(现行代码本就无该路径)。
5. 现有小石台 `scene_stage_plaza.png` 原样保留,叠在连续地面之上;不改地台件与 `standee_body` 的地台绘制。
6. 装板真实链路(`board_changed` 与调试对局就绪)把当前板 `board_type` 同步到舞台;连续一层未知或空串时不崩、不刷错误。
7. 既有战斗屏呈现回归全绿:站桩、排播、明细带、三区几何、文字对比、明度三档带、棋盘/菜单/关卡 golden 零变化;渲染闸全绿。
8. 独立验收审计通过;反哺点清单覆盖第六节全部目标;本批不写 `design/` 与根规则文件。
## 三、现状事实
1. `BattleStageView._init(db_ref, layout)` 以 `layout.stage_h` 设置 B 区最小高度,随后建背景、世界、文本半区、伴随件层:[battle_stage_view.gd:27](../game/presentation/views/battle_stage_view.gd#L27)。
2. 背景现行=`Panel` 平色底 `UiKit.panel_flat(UiKit.STAGE_BG, UiKit.PANEL_BORDER, 0, 1)`,随后调用光柱层:[battle_stage_view.gd:37](../game/presentation/views/battle_stage_view.gd#L37)、[:44](../game/presentation/views/battle_stage_view.gd#L44)。
3. 光柱层为全幅 `Control`,绘制四柱(x 基准 110/900/1055/1210@1280,60 段,α26→0):[battle_stage_view.gd:50](../game/presentation/views/battle_stage_view.gd#L50)、[:145](../game/presentation/views/battle_stage_view.gd#L145)。
4. `UiKit.STAGE_BG = Color8(62,52,42)`、`UiKit.BEAM_LIGHT = Color8(255,236,200)`:[ui_kit.gd:185](../game/presentation/views/ui_kit.gd#L185)、[:186](../game/presentation/views/ui_kit.gd#L186)。
5. `UiKit.TEX_STAGE_PLAZA` 是现有唯一场景件常量;`UiKit.load_tex()` 以 `ResourceLoader.exists` 守卫缺图:[ui_kit.gd:250](../game/presentation/views/ui_kit.gd#L250)、[:290](../game/presentation/views/ui_kit.gd#L290)。
6. `game/assets/art/scene/` 现只有 `scene_stage_plaza.png`;三张背景与地面尚未入包(素材目录不入 git,工作区需同步素材)。
7. 布局表 schema 现五字段:`stage_h` / `info_panel_w` / `ap_bar_h` / `log_band_h_dig` / `log_band_h_settle`;`validate()` 已检查域与两处跨字段:[battle_layout_table.gd:7](../game/data/schemas/battle_layout_table.gd#L7)、[:14](../game/data/schemas/battle_layout_table.gd#L14)。无 `ground_h`。
8. 布局表值:`stage_h=252`、`info_panel_w=240`、`ap_bar_h=44`、`log_band_h_dig=24`、`log_band_h_settle=84`:[battle_layout.tres:7](../game/data/tables/battle_layout.tres#L7)。
9. `ConfigDB` 的 manifest 已登记 `battle_layout`,加载后装配到 `presentation.battle_layout`:[config_db.gd:32](../game/core/config_db.gd#L32)、[:151](../game/core/config_db.gd#L151)。
10. `BoardData.board_type` 为图类型标签(`dungeon` / `underdark` / `deep_underdark`):[board_data.gd:19](../game/core/board_data.gd#L19)。
11. 棋盘视图 `rebuild()` 已按 `board_type` 装载瓦片套,空串回落 `dungeon`:[board_view.gd:257](../game/presentation/views/board_view.gd#L257)。
12. 装板成功即发 `board_changed`:[battle_orchestrator.gd:406](../game/core/battle_orchestrator.gd#L406);主场景处理点重建棋盘视图:[main.gd:361](../game/presentation/main.gd#L361)、[:367](../game/presentation/main.gd#L367)。
13. 调试对局就绪路径直接重建棋盘视图,不经过主场景处理点:[main_debug_flow.gd:101](../game/presentation/main_debug_flow.gd#L101)、[:103](../game/presentation/main_debug_flow.gd#L103)。
14. 舞台当前没有任何接收 `board_type` 的入口,也没有背景/地面纹理常量(全仓 grep 零命中)。
15. 舞台世界为透明 SubViewport;站桩、地台、落影都在世界内绘制:[battle_stage_view.gd:60](../game/presentation/views/battle_stage_view.gd#L60)、[:71](../game/presentation/views/battle_stage_view.gd#L71);小石台绘制在站桩底:[standee_body.gd:367](../game/presentation/views/standee_body.gd#L367),台面矩形 `_pedestal_rect()` 在 [standee_body.gd:312](../game/presentation/views/standee_body.gd#L312)。
16. 舞台由装配器一次性构造(冒险屏 `_ready` 时):[ui_assembler.gd:27](../game/presentation/ui_assembler.gd#L27)、[adventure_screen.gd:28](../game/presentation/scenes/adventure_screen.gd#L28);背景/地面只能在建件后按板同步,不能重建整屏。
17. 布局表读取工具 `FIELDS` 现五字段,无 `ground_h`:[layout_config.py:12](../tools/layout_config.py#L12)。
18. 渲染闸 `shot_checker` 对战斗屏判 C 区文字、明细带提示、明度三档带(信息面 > 世界中 > 世界底):[shot_checker.py:143](../tools/shot_checker.py#L143);golden 仅棋盘三张+菜单+关卡,不覆盖 B 区舞台。
19. `theme_projection_test` 的光柱断言对象是 `UiKit._beams_rect()`(菜单斜光带),不是舞台四柱层:[theme_projection_test.gd:628](../game/tests/unit/presentation/theme_projection_test.gd#L628);本批退役舞台层不影响该断言。
20. 全仓代码无 `wall_ambient` 标识符;仅 `ui_kit.gd:249` 有注释说明小石台不吃该 tint,内容不构成本批改动。
21. 现有地台件入包与尺寸断言:[standee_present_values_test.gd:76](../game/tests/unit/presentation/standee_present_values_test.gd#L76)。
22. 布局探针已从真实选路入口进战斗屏并逐档检查三区几何:[layout_probe.gd:31](../game/probe/layout_probe.gd#L31)、[:55](../game/probe/layout_probe.gd#L55)。
23. `STAGE_BG` 现行四处引用:`ui_kit.gd:185` 常量、`battle_stage_view.gd:39` 绘制点、`theme_projection_test.gd:669`、`ui_contrast_probe.gd:30`;art-bible §4.1 换色合并③(工作区 v3.29 行)要求随本批退役或改指,禁单独改值出批。
## 四、设计契约
- 🔴 **C1 选图口径**:背景只按 `board_type` 三值选图;空串与未知值回落 `dungeon`;不得按关卡序号、节点深度或 `board_variants` 另造第二套选择逻辑。
- 🔴 **C2 背景尺寸**:背景纹理源 = 1280 × `stage_h`,基准档显示 1×;三张文件名必须带 `_dungeon` / `_underdark` / `_deep_underdark` 后缀,禁止同一张图换色或换 tint。
- 🔴 **C3 地面层**:连续地面层源 = 1280 × `ground_h`,三关共用、文件名不带 `board_type` 后缀;底边贴 B 区底、全宽;顶缘即与远景的分隔带;禁横向拉伸(基准档 1×)。
- 🔴 **C4 图层序**:背景 < 连续地面 < 世界站桩/小石台 < 文本半区 < 壳层伴随件。小石台必须在连续地面之上;禁把地面画进背景、禁把地台并进地面件。
- 🔴 **C5 光柱退役**:四柱程序层与 `UiKit.BEAM_LIGHT` 全删;任何缺图态、任何 `board_type` 下都不得再出现光柱/光带/光晕;菜单斜光带 `UiKit._beams_rect()` 不在退役面。
- 🔴 **C6 `ground_h` 单源**:`ground_h` 只存布局表,经 `ConfigDb.presentation.battle_layout` 读取;视图层零 `ground_h` 数值字面量。schema 校验不得放行「地面层被收起态明细带整条盖住」与「地面层超过 B 区一半」。
- 🔴 **C7 缺图回退**:目标背景缺图 → 回 `dungeon` 背景 → 再缺 → 保留平色底 `UiKit.SLOT_BACKDROP`(#3E362C,原舞台底合并目标);地面缺图 → 地面层隐藏、背景仍显示。任何回退都不得崩溃、不得刷脚本错误、不得出现光柱。
- 🔴 **C8 接板真实入口**:`board_changed` 与调试就绪两条现有入口都在重建棋盘后同步舞台 `board_type`;禁另立轮询、定时器或第二套装板通知。
- 🔴 **C9 tint 口径**:背景三张各自带关卡色族、连续地面三关恒定,二者均不引用 `wall_ambient` 槽;不得给舞台加第二层 tint 或乘色。
- 🔴 **C10 呈现回归**:渲染闸全绿——B 区文字对比、明细带纹理幅度、明度三档带(信息面 > 世界中 > 世界底)判据不变;棋盘三张、菜单、关卡 golden 零改动。判据若需改动=停卡呈报,禁调阈值换绿。
- 🔴 **C11 结构预算**:`battle_stage_view.gd` 类变量 ≤15、公开方法 ≤20、函数长 ≤50 行、单函数分支 ≤8;`battle_layout_table.gd` 的 `validate()` 不新增分支(地面校验拆私有辅助);S13 基线外零新增、存量键只降不升。
- 🔴 **C12 写面**:实现批只写 `game/` 与 `reports/`;禁写 `design/`、`roadmap.md`、`待办清单.md`、`体验验证清单.md`、`game/tests/README.md`;文档同步只列反哺点。
- 🔴 **C13 禁凑计数袋**:不得为凑「类变量数」把背景/地面状态塞进无关容器;不得为凑「方法数」把选图与地面几何强行合并成一个无职责函数;拆分单元须有可陈述职责。
- 🔴 **C14 舞台底换色合并**:`UiKit.STAGE_BG` 源常量随本批退役;平色缺图回退改指 `UiKit.SLOT_BACKDROP`(#3E362C,art-bible §4.1 换色合并③目标);`theme_projection_test` 与 `ui_contrast_probe` 的舞台底对比锚同步改读合并目标,阈值、判据与覆盖不变。
## 五、范围边界
做:
1. `UiKit` 增三关背景路径格式常量与共用地贴路径常量。
2. `BattleStageView` 增背景纹理层、连续地面层、`set_board_type()`、缺图回退与测试用取纹理单点。
3. 布局表增 `ground_h=100`、schema 校验与 `tools/layout_config.py` 读取字段。
4. `main.gd` 与 `main_debug_flow.gd` 在既有棋盘重建点同步舞台 `board_type`。
5. 删四柱光柱层、`_draw_light_beams` 与 `UiKit.BEAM_LIGHT`;退役 `UiKit.STAGE_BG` 源常量,平色回退改指 `UiKit.SLOT_BACKDROP`;同步改读两处对比锚。
6. 新增单元套件、扩充布局探针断言、登记 `run_all.gd`。
7. 实现报告、代拍板追认单、反哺点清单与独立验收审计。
不做:
1. 不选图、不修图、不生成新图、不入包素材——本卡开工前置是四件终稿已在 `game/assets/art/scene/`;遴选与编辑归作者/asset-ingest。
2. 不改 `scene_stage_plaza.png`、不改 `standee_body` 的地台绘制与落影。
3. 不改棋盘瓦片、数字、图标、文字排版、明细带内容。
4. 不改样张工具 `render_sample_sheet*.py` 的光柱绘制与注释;不更新任何 golden。
5. 不跑 MC;不做数值调参。
6. 不新增 UI 组件、不新增场景文件、不新增 ConfigDB 容器。
7. 不把「背景/地面」做成可按 `wall_ambient` 换色的通用槽。
## 六、关联文档同步面
| 文档 | 关联点 | 同步义务 |
|---|---|---|
| [art-bible](../design/art/art-bible.md) §6.1 | 「程序化平色+光柱现行使用=占位」「`ground_h` 由实装批新增并回填」「资产到位后四柱直接退役」三句改为实装事实;回填 `ground_h=100` | 实现批禁写;逐条落 `reports/iter<N>-反哺点-<日期>.md`,用户裁决后文档批落文 |
| [art-bible](../design/art/art-bible.md) §4.1 换色合并登记③ | `STAGE_BG` 由「随舞台背景插画批处置」改为已退役、平色回退改指 `SLOT_BACKDROP`(#3E362C) | 同上 |
| [art-assets-checklist](../design/art/art-assets-checklist.md) §4 | 三张背景与地面「已出候选 v10」状态句——更新为候选 v12 过审、终稿入包状态;小石台保留句核对 | 同上 |
| [battle-stage-prompts](../design/art/battle-stage-prompts.md) §4/§5 | `ground_h` 由「待实装批回填」改为已回填 100;档 6「四柱退役(代码批)」改为已退役 | 同上 |
| [battle.md](../design/gdd/ux/battle.md) :118、:155 | 布局字段清单补 `ground_h`;「现行溢出区为临时光柱氛围层,正式背景到位后该层退役」改为已退役、判定不变 | 同上 |
| [HUD框架.md](../design/gdd/systems/表现与支撑/HUD框架.md) :60、:247 | B 区内容补「三关背景+连续地面层」;布局几何清单补地面层高 | 同上 |
| [数据配置.md](../design/gdd/systems/表现与支撑/数据配置.md) :73 | 布局表用途句补「地面层高度」 | 同上 |
| [体验验证清单.md](../体验验证清单.md) | 三关背景可辨、无光柱、地面非瓷砖/非亮面、结算带压底带等目检项如要转正式条目 | 实现批禁写;实现报告登记,转条目走反哺点 |
| [game/tests/README.md](../game/tests/README.md) | 新增「B 区舞台背景三关变体与连续地面层」能力行与覆盖域表 | 写卡段已入表;实现批只核对名实,不再修改 |
| [architecture.md](../design/engineering/architecture/architecture.md) | 无字段级陈述需要改;本批不新增容器/类,仅舞台视图内部图层 | 无同步义务 |
## 七、落位与可复用性预检
### 7.1 新增件与新增成员
| 新增件/成员 | 复用面查询结论 | 唯一面 | 分层与交互路径 |
|---|---|---|---|
| `UiKit.TEX_STAGE_BG_FMT` | 全仓无背景路径常量;`UiKit` 已是场景件/角色件/图标路径的唯一装载单点(`TEX_STAGE_PLAZA` 先例),不新建路径件 | 背景路径格式只在 `ui_kit.gd` 一处 | 舞台视图经 `UiKit.load_tex` 装载;不直连 ResourceLoader |
| `UiKit.TEX_STAGE_GROUND` | 同上;地面是唯一共用场景件 | 地面路径只此一处 | 同上 |
| `BattleStageView.set_board_type()` | 舞台无任何板类型入口;棋盘视图的 `tile_palette.load_board_tiles` 只管瓦片,不复用其选择逻辑(职责不同) | 舞台选图只在这一方法 | 组合根在 `board_changed` 后调用;舞台不反向读结算内部 |
| `BattleStageView.bg_texture` / `ground_layer` | 舞台现无背景纹理层与地面层;不复用 `Panel` 平色底(平色底保留为回退) | 两层各一枚,几何只由布局表与舞台锚点决定 | 都是舞台子 Control,位于世界 SubViewport 之下 |
| `BattleStageView._load_stage_texture()` | 现有 `UiKit.load_tex` 是公共单点;此私有薄封装只服务缺图回退的可测性,不复制资源路径 | 背景与地面装载都经此一处 | 只被本件 `set_board_type`/`_build_ground` 调用 |
| `BattleLayoutTable.ground_h` + `_ground_field_errors()` | 布局表是分区几何唯一事实源;不把地面高塞进 `BattleStageView` 常量 | `ground_h` 只在布局表;校验只在此 schema | ConfigDB 加载期 `validate()` 调用;视图经容器读取 |
| `tools/layout_config.py` FIELDS 补 `ground_h` | 工具读取单点已存在;只补字段,不新建读取器 | 工具侧字段清单只此一处 | 样张工具与游戏侧同源读表 |
| `stage_background_test.gd` | 现有 `standee_present_values_test` 是「战斗一屏样张批 V5–V9」专用套件;`layout_probe` 是场景探针。两者职责都不覆盖「背景选图/地面几何/缺图回退」单元面 | 舞台背景与地面层单元断言唯一落点 | `run_all.gd` 单元入口;舞台直建,不起完整存档 |
| `layout_probe.gd` 追加断言 | 已有真实选路进战斗屏的探针,不新建第二探针 | 真实入口接线断言唯一落点 | `probe_gate.sh` 默认段;读 `main.ui.battle.stage` |
### 7.2 唯一性核查
- 背景路径、地面路径、`ground_h`、`board_type` 归一化规则各只有一处定义:`ui_kit.gd` 两常量、`battle_layout_table.gd` 一字段、`BattleStageView.set_board_type()` 一方法。
- 舞台接板同步只在 `main.gd:_on_board_changed` 与 `main_debug_flow.gd:_on_debug_battle_ready` 两个既有重建点各加一行;不新增第三通知源。
- 小石台绘制保持 `standee_body.gd` 唯一落点;地面层不重画地台,不复制 `_pedestal_rect`。
- 缺图回退只走 `_load_stage_texture` → `UiKit.load_tex` 一条链,不另建资源检查器。
### 7.3 分层与跨层出口
- Core 零改动:`BoardData.board_type` 只读;舞台不导入 core 内部,只接组合根传入的字符串。
- 呈现层内部:布局值经 `ConfigDb.presentation.battle_layout` 单点;纹理装载经 `UiKit.load_tex`;世界内站桩/地台不动。
- 测试面:单元套件直建 `BattleStageView`(与 `pacing_queue_test` 的 `FakeStage` 同型手法);真实入口由 `layout_probe` 从选路按钮进入。
## 八、实现规范预算
| 触面 | 预算 | 结论 |
|---|---|---|
| [battle_stage_view.gd](../game/presentation/views/battle_stage_view.gd) | 类变量 11→14(+`bg_texture`/`ground_layer`/`_bg_board_type`,≤15);公开方法 12→13(+`set_board_type`,≤20);新增私有 `_build_ground`、`_load_stage_texture`;函数长 ≤50、分支 ≤8;文件长 394→目标 ≤470(gdstyle 现无该件基线键) | 通过;T4 后光柱删除会回收约 15 行 |
| [ui_kit.gd](../game/presentation/views/ui_kit.gd) | 加 2 条 const、删 `BEAM_LIGHT` 与 `STAGE_BG`(净 0);不新增变量/方法 | 通过 |
| [battle_layout_table.gd](../game/data/schemas/battle_layout_table.gd) | 类变量 5→6;公开方法仍 1(`validate`);`validate` 现有 7 个 if 不再增分支,地面校验拆 `_ground_field_errors()`(2 个 if、返回 2 次) | 通过;避免 `validate` 9 分支越默认 8 |
| [main.gd](../game/presentation/main.gd) | 既有 `_on_board_changed` 内加局部变量与 2 行守卫同步;不增成员 | 通过;文件无 S13 基线键 |
| [main_debug_flow.gd](../game/presentation/main_debug_flow.gd) | 既有就绪函数内加局部变量与 2 行守卫同步;不增成员 | 通过 |
| [layout_config.py](../tools/layout_config.py) | `FIELDS` +1 字符串 | 通过 |
| [stage_background_test.gd](../game/tests/unit/presentation/stage_background_test.gd) | 新件;公开函数 0(仅 `_init` 与私有测试函数)、类变量 0;预计 ≤220 行 | 通过 |
| 迁移函数 | 本批无函数自存量文件迁入;无 `gdstyle_baseline.txt` 同名函数键 | 通过 |
| 存量触面 | 所有被改 `.gd` 的 S13 键只降不升;超出即停卡 | 通过 |
## 九、文件结构图
| 文件 | 职责 | 改动类型 | 验收行 |
|---|---|---|---|
| [ui_kit.gd](../game/presentation/views/ui_kit.gd) | 背景路径格式常量、共用地贴常量;退役 `BEAM_LIGHT` 与 `STAGE_BG` | 行为改动 | V2/V3/V5 |
| [battle_stage_view.gd](../game/presentation/views/battle_stage_view.gd) | 背景纹理层、连续地面层、`set_board_type()`、缺图回退;平色回退改指 `SLOT_BACKDROP`;退役四柱层 | 行为改动 | V2/V3/V4/V5 |
| [battle_layout_table.gd](../game/data/schemas/battle_layout_table.gd) | 新增 `ground_h` 字段与地面校验辅助 | 行为改动(加载期校验) | V1 |
| [battle_layout.tres](../game/data/tables/battle_layout.tres) | 回填 `ground_h = 100` | 行为改动(数据表) | V1/V4 |
| [layout_config.py](../tools/layout_config.py) | 工具读取字段补 `ground_h` | 非游戏行为(工具维护) | V1 |
| [main.gd](../game/presentation/main.gd) | `_on_board_changed` 重建棋盘后同步舞台板型 | 行为改动 | V6 |
| [main_debug_flow.gd](../game/presentation/main_debug_flow.gd) | 调试对局就绪同步舞台板型 | 行为改动 | V6 |
| [layout_probe.gd](../game/probe/layout_probe.gd) | 真实入口追加背景/地面装载断言 | 非行为改动(探针断言) | V6/V7 |
| [theme_projection_test.gd](../game/tests/unit/presentation/theme_projection_test.gd) | `STAGE_BG` 对比锚改读 `SLOT_BACKDROP`(合并目标,阈值不变) | 非行为改动(测试改读) | V5 |
| [ui_contrast_probe.gd](../game/probe/ui_contrast_probe.gd) | 舞台底对比锚改读 `SLOT_BACKDROP`(合并目标,阈值不变) | 非行为改动(探针改读) | V5/V7 |
| [stage_background_test.gd](../game/tests/unit/presentation/stage_background_test.gd) | 新建单元套件:字段/选图/回退/地面几何/光柱退役 | 非行为改动 | V1–V5/V8 |
| [run_all.gd](../game/tests/run_all.gd) | 登记新套件 | 非行为改动 | V8 |
| [README.md](../game/tests/README.md) | 能力行与覆盖域表 | 非行为改动;**写卡段已入表** | V8 |
| `game/assets/art/scene/scene_stage_bg_*.png` ×3、`scene_stage_ground.png` | 开工前置资产,非本批写面;实现期只读装载 | 输入件 | V2/V3/V4 |
## 十、任务卡
### T0 开工基线与资产前置(0.25d)
取用:
- 待办清单条目状态=美术设计已闭合+已写卡;本卡路径。
- 候选过审记录:`artwork/2026-10-30-战斗区背景/候选保存清单-2026-10-31.md`(工作区文件,不入 git)。
步骤:
- [ ] 读 `game/tests/.gates/ci-status.json`;CI 红未修复即停本卡,禁开新批。
- [ ] 核对四件终稿已在包:`game/assets/art/scene/scene_stage_bg_dungeon.png`、`scene_stage_bg_underdark.png`、`scene_stage_bg_deep_underdark.png`、`scene_stage_ground.png`;用 PIL 读数:三张背景恰为 `(1280, stage_h)`、地面恰为 `(1280, ground_h=100)`。缺位或尺寸不符=L3 停卡,退回 asset-ingest;禁在实现期自行裁图/缩图。
- [ ] 复核第三节 1–22 行的 file:line 与实数;漂移先停卡核对,不靠猜行号施工。
- [ ] 跑 `python tools/gdstyle_gate.py` 记录本批触面当前读数;另记录 `battle_stage_view.gd` 行数/类变量/公开方法数、`battle_layout_table.validate()` 分支数。
- [ ] `grep -rn "STAGE_BG" game` 记录四处现行引用:`ui_kit.gd` 常量、`battle_stage_view.gd` 绘制点、`theme_projection_test.gd:669`、`ui_contrast_probe.gd:30`;T4 收口时四处全部处置。
- [ ] 写开工闸记录:基线 commit、既有红项、资产四件尺寸读数、S13 读数。
产出:资产齐盘、代码事实无漂移、预算基线可对账。
### T1 布局字段 `ground_h` 与读表口径(0.5d)
取用:T0 确认的表结构;`ConfigDb` manifest 已登记 `battle_layout`。
接口块:
```gdscript
@export var ground_h: int = 100
func validate() -> Array
func _ground_field_errors() -> Array
```
步骤:
- [ ] 在 [battle_layout_table.gd:7](../game/data/schemas/battle_layout_table.gd#L7) 的 `stage_h` 后新增 `@export var ground_h: int = 100`,注释写「B 区连续地面层高(源 = 基准屏宽 × 本值)」。
- [ ] 在 [battle_layout_table.gd:14](../game/data/schemas/battle_layout_table.gd#L14) 的 `validate()` 末尾追加 `errs.append_array(_ground_field_errors())`;不把两个新 if 直接塞进 `validate()`(保持分支 ≤8)。
- [ ] 新增私有 `_ground_field_errors() -> Array`:`ground_h < 16 or ground_h > stage_h / 2` 报 `domain`;`ground_h <= log_band_h_dig` 报 `cross_field`(地面层不得被收起态明细带整条盖住)。消息含字段名与实际值。
- [ ] 在 [battle_layout.tres:7](../game/data/tables/battle_layout.tres#L7) 的 `stage_h = 252` 后写 `ground_h = 100`;其余五值不动。
- [ ] 在 [layout_config.py:12](../tools/layout_config.py#L12) 的 `FIELDS` 元组中 `stage_h` 后加 `"ground_h"`。
- [ ] 新建 [stage_background_test.gd](../game/tests/unit/presentation/stage_background_test.gd),先写 `_test_layout_ground_field()`:
1. `load("res://data/tables/battle_layout.tres")`,断言 `ground_h == 100` 且 `validate()` 为空。
2. 用 `BattleLayoutTable.new()` 三态:`ground_h=20`(≤log_band_h_dig=24)、`ground_h=200`(>252/2)、`ground_h=100`;断言前两态各返回一条含 `ground_h` 的错误,第三态无 ground 相关错误。
- [ ] 在 [run_all.gd:191](../game/tests/run_all.gd#L191) 邻近 presentation 区追加:
`{"path": "res://tests/unit/presentation/stage_background_test.gd", "expect": 0}`
- [ ] 验证步骤:
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/unit/presentation/stage_background_test.gd`
`python -c "from tools.layout_config import load; print(load()['ground_h'])"` → 必须输出 `100`。
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/unit/data_config/config_db_test.gd` → 绿。
验收:布局表单源有 `ground_h`,校验拒绝越域与「被收起带整条盖住」,游戏侧与工具侧读同值。
产出:`ground_h` 字段、schema 校验、工具读取与 `_test_layout_ground_field`。
### T2 背景层与 `board_type` 选图(1.0d)
取用:T1 的布局表;`UiKit.load_tex`;`BoardData.board_type`;`board_changed` 信号。
接口块:
```gdscript
const TEX_STAGE_BG_FMT := "res://assets/art/scene/scene_stage_bg_%s.png"
var bg_texture: TextureRect
func set_board_type(board_type: String) -> void
func _load_stage_texture(path: String) -> Texture2D
```
步骤:
- [ ] 在 [ui_kit.gd:250](../game/presentation/views/ui_kit.gd#L250) 邻近 `TEX_STAGE_PLAZA` 新增 `TEX_STAGE_BG_FMT`;注释写「三关背景按 board_type 选图,不走 tint」。
- [ ] 在 [battle_stage_view.gd:14](../game/presentation/views/battle_stage_view.gd#L14) 成员区新增 `var bg_texture: TextureRect` 与 `var _bg_board_type := ""`。
- [ ] 在 [battle_stage_view.gd:37](../game/presentation/views/battle_stage_view.gd#L37) 的 `_build_backdrop()` 中,平色 Panel 之后新增全幅 `TextureRect`:`set_anchors_preset(PRESET_FULL_RECT)`、`expand_mode=EXPAND_IGNORE_SIZE`、`stretch_mode=STRETCH_SCALE`、`mouse_filter=IGNORE`、初始 `visible=false`;随后调用 `set_board_type("dungeon")`。
- [ ] 新增 `set_board_type(board_type: String) -> void`:
- 空串归一为 `"dungeon"`;与 `_bg_board_type` 相同则直接返回。
- 先取 `UiKit.TEX_STAGE_BG_FMT % bt`;为目标缺图且 `bt != "dungeon"` 时再取 `dungeon` 路径;两取都经 `_load_stage_texture`。
- 写入 `bg_texture.texture`;`visible = texture != null`;不得在缺图时抛错或打印产品日志。
- [ ] 新增私有 `_load_stage_texture(path: String) -> Texture2D`,只返回 `UiKit.load_tex(path)`;作为缺图回退的可测单点。
- [ ] 在 [main.gd:367](../game/presentation/main.gd#L367) 前后:把 `current_board()` 存局部变量,`board_view.rebuild(board, ...)` 后加 `if board != null: ui.battle.stage.set_board_type(String(board.board_type))`;不改信号连接与重建顺序。
- [ ] 在 [main_debug_flow.gd:103](../game/presentation/main_debug_flow.gd#L103) 同型:存局部变量、重建后同两行同步;不新增第三个通知源。
- [ ] 在 `stage_background_test.gd` 写 `_test_background_selection()`:
1. 直建 `BattleStageView.new(null, load("res://data/tables/battle_layout.tres"))`;默认应已装载 dungeon 背景,纹理非空且尺寸 1280×`stage_h`。
2. 依次 `set_board_type("underdark")`、`"deep_underdark"`、`"dungeon"`;每次断言 `bg_texture.texture.resource_path` 以对应 `_<board_type>.png` 结尾,且尺寸不变。
3. 断言 `bg_texture.get_index()` 小于 `world.get_parent().get_parent().get_index()`(背景在世界之下)。
- [ ] 写 `_test_background_fallback()`:用 `class MissingUnderdarkStage extends BattleStageView` 覆写 `_load_stage_texture`——`underdark` 路径返回 null、其余走 `UiKit.load_tex`;断言 `set_board_type("underdark")` 后纹理路径为 dungeon。再用全 null 子类断言 `set_board_type("dungeon")` 后 `bg_texture.texture == null` 且 `visible == false`,无异常。
- [ ] 在 [layout_probe.gd:55](../game/probe/layout_probe.gd#L55) 的 `_check_one()` 内追加:`stage.bg_texture` 非空、纹理非空、`resource_path` 以 `_dungeon.png` 结尾(L1 真实入口);记录 `label` 便于两档定位。
- [ ] 窄测:
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/unit/presentation/stage_background_test.gd`
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game res://probe/layout_probe.tscn`
验收:三值各命中对应文件;未知/空串→dungeon;目标缺图→dungeon→平色;真实选路进 L1 后背景为 dungeon。
产出:`TEX_STAGE_BG_FMT`、`bg_texture`、`set_board_type`、两处接板同步、背景选图/回退断言。
### T3 连续地面层与几何(0.75d)
取用:T1 的 `ground_h`;T2 的舞台图层序与 `_load_stage_texture`;T0 已入包的地面件。
接口块:
```gdscript
const TEX_STAGE_GROUND := "res://assets/art/scene/scene_stage_ground.png"
var ground_layer: TextureRect
func _build_ground(layout: BattleLayoutTable) -> void
```
步骤:
- [ ] 在 [ui_kit.gd:250](../game/presentation/views/ui_kit.gd#L250) 新增 `TEX_STAGE_GROUND`;注释写「三关共用、无 board_type 后缀、不吃 tint」。
- [ ] 在 [battle_stage_view.gd:14](../game/presentation/views/battle_stage_view.gd#L14) 成员区新增 `var ground_layer: TextureRect`。
- [ ] 在 [battle_stage_view.gd:27](../game/presentation/views/battle_stage_view.gd#L27) 的 `_init` 中,`_build_backdrop()` 之后、`_build_world_stage()` 之前调用 `_build_ground(layout)`。
- [ ] 新增 `_build_ground(layout)`:全幅 `TextureRect`,`set_anchors_preset(PRESET_BOTTOM_WIDE)` 后四 offset 归零再把 `offset_top = -float(layout.ground_h)`;`expand_mode=EXPAND_IGNORE_SIZE`、`stretch_mode=STRETCH_SCALE`、`mouse_filter=IGNORE`;`texture = _load_stage_texture(UiKit.TEX_STAGE_GROUND)`;`visible = texture != null`。
- [ ] 不修改 `standee_body.gd`;小石台仍在世界内站桩底绘制,天然位于地面层之上。
- [ ] 在 `stage_background_test.gd` 写 `_test_ground_layer_geometry()`:
1. 地面纹理可加载且尺寸 `(1280, layout.ground_h)`。
2. `ground_layer.anchor_left==0`、`anchor_top==1`、`anchor_right==1`、`anchor_bottom==1`;`offset_left==0`、`offset_right==0`、`offset_bottom==0`、`offset_top == -layout.ground_h`。
3. 把 stage 尺寸设为 `Vector2(1280, layout.stage_h)` 并加树一帧后,`ground_layer.size == Vector2(1280, layout.ground_h)`、`ground_layer.position.y == layout.stage_h - layout.ground_h`。
4. `ground_layer.get_index()` 大于 `bg_texture.get_index()`、小于 `world.get_parent().get_parent().get_index()`。
- [ ] 在 [layout_probe.gd:55](../game/probe/layout_probe.gd#L55) 追加:`ground_layer` 可见、`ground_layer.size.y == layout.ground_h`(两档都成立;宽度随档位变化不判)。
- [ ] 窄测:
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/unit/presentation/stage_background_test.gd`
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game res://probe/layout_probe.tscn`
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/unit/presentation/standee_present_values_test.gd`(地台件 V9 不动)。
验收:地面层底边贴 B 区底、高读表、全宽、位于背景与世界之间;小石台仍在其上;缺图时地面隐藏不崩。
产出:`TEX_STAGE_GROUND`、`ground_layer`、`_build_ground`、地面几何断言。
### T4 四柱光柱与舞台底源常量退役(0.5d)
取用:T2/T3 已完成的舞台图层;现有 `UiKit._beams_rect()` 菜单斜光带不在退役面;art-bible §4.1 换色合并③。
步骤:
- [ ] 删除 [battle_stage_view.gd:44](../game/presentation/views/battle_stage_view.gd#L44) 的 `_build_beams_layer()` 调用。
- [ ] 删除 [battle_stage_view.gd:50](../game/presentation/views/battle_stage_view.gd#L50) 的 `_build_beams_layer()` 与 [battle_stage_view.gd:145](../game/presentation/views/battle_stage_view.gd#L145) 的 `_draw_light_beams()`。
- [ ] 删除 [ui_kit.gd:186](../game/presentation/views/ui_kit.gd#L186) 的 `BEAM_LIGHT` 常量;平色回退能力由下一步改指 `SLOT_BACKDROP` 承接。
- [ ] 按 art-bible §4.1 换色合并③:把 [battle_stage_view.gd:39](../game/presentation/views/battle_stage_view.gd#L39) 的 `UiKit.STAGE_BG` 改指 `UiKit.SLOT_BACKDROP`(#3E362C,合并目标);删除 [ui_kit.gd:185](../game/presentation/views/ui_kit.gd#L185) 的 `STAGE_BG` 常量。
- [ ] 把 [theme_projection_test.gd:669](../game/tests/unit/presentation/theme_projection_test.gd#L669) 的 `uikit_s.STAGE_BG` 改读 `uikit_s.SLOT_BACKDROP`;把 [ui_contrast_probe.gd:30](../game/probe/ui_contrast_probe.gd#L30) 的 `UiKit.STAGE_BG` 改读 `UiKit.SLOT_BACKDROP`;两处阈值与断言文案不变,标签可注「合并目标」。
- [ ] 更新 `battle_stage_view.gd` 顶注与 `_build_backdrop()` 注释:去掉「四柱/光柱氛围层」表述,写明「背景三关变体→连续地面→世界站桩;光柱层已退役,不迁入背景;平色回退走合并目标 `SLOT_BACKDROP`」。
- [ ] 在 `stage_background_test.gd` 写 `_test_beams_retired()`:
1. 读 `res://presentation/views/battle_stage_view.gd` 源码,断言不含 `_build_beams_layer`、`_draw_light_beams`、`BEAM_LIGHT`,且含 `UiKit.SLOT_BACKDROP`。
2. `load("res://presentation/views/ui_kit.gd").get_script_constant_map()` 不含 `BEAM_LIGHT` 与 `STAGE_BG`。
3. 断言菜单 `_beams_rect()` 仍在(`haskey("_beams_rect")` 或调用返回 TextureRect),防止误删菜单斜光带。
- [ ] 静态核实全仓代码 `wall_ambient` 仅 `ui_kit.gd:249` 注释一处;不新增该标识符。
- [ ] 窄测:
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/unit/presentation/stage_background_test.gd`
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/unit/presentation/theme_projection_test.gd`
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game res://probe/ui_contrast_probe.tscn`
验收:舞台无光柱层与相关常量;舞台底源常量退场、回退改指合并目标;菜单斜光带与两处对比锚仍绿;缺图态仍无光柱。
产出:光柱层退役、`_test_beams_retired`、注释口径更新。
### T5 回归、渲染闸与产出物(0.75d)
取用:T0–T4 全部产物;`tools/run_all_gates.sh` 为唯一闸入口;独立验收审计未参与实现。
步骤:
- [ ] 运行开发期关联窄测:
`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path game --script res://tests/run_all.gd -- --only=stage_background|standee_present_values|standee_fx_sweep|pacing_queue|theme_projection|config_db`
- [ ] 出红只做红件最小单元复跑;禁在波内追加全量档。
- [ ] 里程碑出口跑一次代码域全套闸:
`bash tools/run_all_gates.sh --code`
- [ ] 回执必须 `all_green=true`、`dirty_game=false`;重点读 `05 渲染闸`:`shot_checker` 的 C 区文字 WCAG ≥4.5、纹理幅度 ≤18、明度三档带 `info>world>bottom` 全过;棋盘三张、菜单、关卡 golden 零变化。任何新增红停卡,禁带红合回。
- [ ] 独立验收审计由未参与实现的会话或子代理执行,产出 `reports/iter<N>-验收审计-<日期>.md`;未过禁合并。
- [ ] 轻闸全绿、合并前写齐三份:
1. `reports/iter<N>-实现报告-<日期>.md`
2. `reports/iter<N>-代拍板追认单-<日期>.md`
3. `reports/iter<N>-反哺点-<日期>.md`
- [ ] 实现报告须含:T0–T5 状态、V1–V8 实跑读数、S13 前后数、资产四件尺寸、渲染闸逐项读数、玩家目检总清单(登记不阻塞:三关背景可辨、无光柱、远景有纵深、地面为泥土非瓷砖且不亮、结算带压住地面底带、站桩踩在连续地面/小石台上)。
- [ ] 反哺点逐条覆盖第六节九个目标;每条写文件、节、原句、拟改句、依据,并明确「实现批未写 design/、未写根流程清单、未写 game/tests/README.md」。
- [ ] 本批不新增 golden、不改 `shot_checker` 阈值、不删既有断言。
完成定义:三份产出物齐全+代码域闸全绿+验收审计通过,才进入合回;合回后按项目现行机制由本地 CI 复验。
## 十一、验证闸:改动—验收对照表
| 需求点 | 改动(文件/行为) | 验收测试(名称+落点) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|
| V1 `ground_h` 字段与校验 | schema 加字段+地面校验辅助;表回填 100;工具读取 | `stage_background_test.gd:_test_layout_ground_field`;`config_db_test.gd:_test_manifest_dir_sync` | 单元 | `BattleLayoutTable.validate()`;`ConfigDB.load_tables()` | 表值=100;默认表 validate 空;`ground_h=20` 与 `=200` 各得一条含 `ground_h` 的错误;工具 load() 得 100 | 新增:B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) |
| V2 背景按 board_type 选图 | `TEX_STAGE_BG_FMT`+`set_board_type`+两处接板同步 | `stage_background_test.gd:_test_background_selection`;`layout_probe.gd:_check_one` | 单元+探针 | `BattleStageView.set_board_type()`;`level_screen.press_candidate(0)`→`board_changed` | 三值纹理路径逐值对应且尺寸 1280×252;L1 真实进入后路径以 `_dungeon.png` 结尾 | 新增:B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) |
| V3 缺图回退 | `_load_stage_texture`+回退链 | `stage_background_test.gd:_test_background_fallback` | 单元 | `BattleStageView.set_board_type("underdark")`(子类缺图) | 目标缺→dungeon 路径;全缺→`texture=null`、`visible=false`、无异常 | 新增:B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) |
| V4 连续地面层几何 | `TEX_STAGE_GROUND`+`_build_ground` | `stage_background_test.gd:_test_ground_layer_geometry`;`layout_probe.gd:_check_one` | 单元+探针 | 舞台构造+真实进战斗屏 | 纹理 1280×100;anchor 0/1/1/1;`offset_top=-100`;1280 档 `size.y=100`;图层序 bg<ground<world;探针两档 `size.y=ground_h` | 新增:B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) |
| V5 四柱光柱与舞台底源常量退役 | 删舞台光柱层、`BEAM_LIGHT`、`STAGE_BG`;回退改指 `SLOT_BACKDROP` | `stage_background_test.gd:_test_beams_retired`;`theme_projection_test.gd:_smoke_contrast_inks`;`ui_contrast_probe.gd:_ready` | 单元+探针 | `BattleStageView` 构造+`UiKit` 常量表;`ui_contrast_probe.tscn` | 舞台源码零 `_build_beams_layer`/`_draw_light_beams`/`BEAM_LIGHT` 且含 `UiKit.SLOT_BACKDROP`;UiKit 常量表无 `BEAM_LIGHT`/`STAGE_BG`;菜单 `_beams_rect` 仍在;两处舞台底对比锚读合并目标且阈值不变 | 新增:B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) |
| V6 接板真实链路 | main 与调试就绪两处同步 board_type | `layout_probe.gd:_check_one`;`stage_background_test.gd:_test_background_selection` 的源码锚 | 探针+单元 | `level_screen.press_candidate(0)`→`board_changed` | L1 舞台背景路径为 `_dungeon.png`;源码锚 `main.gd` 与 `main_debug_flow.gd` 均含 `stage.set_board_type(` | 新增:B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) |
| V7 既有呈现回归 | 不改站桩/明细/棋盘;只换舞台底图 | `standee_present_values_test.gd:_init`;`standee_fx_sweep_test.gd:_init`;`pacing_queue_test.gd:_init`;`layout_probe.gd:_check_one` | 单元+集成+探针 | 现有各套件真实入口 | 地台 48×12、站桩几何、排播、三区几何全绿;渲染闸 shot_checker 三项与明度带全过;棋盘/菜单/关卡 golden 零变化 | 既有:站桩敌人呈现与结算演出落点;既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| V8 能力行名实与套件登记 | `run_all.gd` 登记;README 写卡段入表 | `run_all.gd` manifest 与整套 `stage_background_test.gd` | 单元 | `res://tests/run_all.gd` | 套件在 manifest 且 exit 0;能力行代表用例与函数名逐字一致;`capability_audit` 零红 | 新增:B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) |
落点集中:V1 归 T1;V2/V3/V6 归 T2;V4 归 T3;V5 归 T4;V7/V8 归 T5。全表名实核对归 T5 与独立审计。
## 十二、测试方式与测试削弱申报
自动验证:
- V1–V6 全部从真实入口或舞台直建公共面驱动;缺图路径用子类覆写 `_load_stage_texture` 构造观测,不改包内资产、不注入破坏。
- 真实接板链路只认 `board_changed` 与调试就绪两条既有入口;不建后门 API、不在产品码写测试专用分支。
- 渲染闸为窗口化实跑;`shot_checker` 阈值与 golden 不因本批改动。
真人目检(登记不阻塞,写入实现报告):
- 三关背景可辨为不同洞窟(地精暖石/卓尔冷蓝灰/灰矮人灰黑),远景有纵深、非贴墙、无孤立圆柱贴顶。
- 背景无光柱/光带/光源实体;任何缺图态也不出现光柱。
- 连续地面读作泥土/碎石,不是瓷砖/石板;全幅只零星小石、不成排成堆;中低明度不刺眼。
- 站桩与小石台踩在连续地面上;结算期明细带压住地面底带,不出现半截地台/悬空。
- B 区文字(轮次牌、演出提示、明细带)对背景/地面可读,无字压纹理。
测试削弱申报:
- 零删除、零放宽、零换下层直调。唯一允许的改读:`theme_projection_test.gd:669` 与 `ui_contrast_probe.gd:30` 的舞台底对比锚由 `STAGE_BG` 改读 `SLOT_BACKDROP`(art-bible §4.1 换色合并③,目标色 #3E362C);阈值、断言文案与覆盖不变。
- 舞台四柱层退役不涉任何现存断言(全仓核实:光柱断言只针对菜单 `UiKit._beams_rect`)。
- `layout_probe` 只追加背景/地面断言,不改既有三区几何判据。
- 若实现期发现任何既有断言因换图而红,先停卡呈报并列入本表,禁静默删/改阈值。
## 十三、估时与切分预案
| 卡 | T0 | T1 | T2 | T3 | T4 | T5 | 合计 |
|---|---:|---:|---:|---:|---:|---:|---:|
| 工作日 | 0.25 | 0.5 | 1.0 | 0.75 | 0.5 | 0.75 | **3.75d** |
浮动 ±0.75d(渲染闸首跑与两档探针等待占大头)。
切分预案:若 T2/T3 超软盒,段一=T0–T2(资产前置、布局字段、背景选图,约 1.75d);段二=T3–T5(地面层、光柱退役、闸与产出物,约 2.0d)。两段各自可测;段一合回前必须保证光柱层仍在时背景已正确显示(不半删),段二再退役光柱。禁在段一合回。
## 十四、异常分级
L1(自救一次):
- 编译错误、签名拼写、`run_all.gd` 登记遗漏、测试 helper 顺序。
- 注释措辞与私有局部变量命名。
L2(记录继续;同一红连续三次升 L3):
- 与本批无关的存量红,先按回执归因并留档。
- 行数较 T0 预期漂移但 S13 键不升。
L3(立即停卡写报告):
- CI 状态红未修。
- 四件资产缺位或尺寸不符(停卡退回 asset-ingest;禁实现期裁图代偿)。
- 机检键/计数与卡片契约互斥;**上游缺陷停卡退回写卡段,禁为凑绿保留无效成员或造第三条路**。
- `battle_stage_view` 类变量 >15、公开方法 >20,或 S13 基线外新增键无法在不改契约下消除。
- `ground_h` 校验与既有跨字段校验冲突,或必须改 `log_band_h_*` 才能成立。
- 渲染闸新增红(文字对比、纹理幅度、明度三档带、golden)且不属于可归因的既有存量红。
- 任何验收行无法从真实入口绑定特有可观测提交面。
- 必须改 `design/`、根流程清单或 `game/tests/README.md` 才能过关。
## 十五、代拍板白名单
允许实现期自行拍板:
1. 私有函数在指定文件内的顺序、局部变量名;签名与行为不变。
2. 测试 helper 的顺序与断言消息措辞;不得改判据值、阈值与层级。
3. 注释与日志文案;不得引入「光柱仍在」类误导表述,不得写变更历史。
4. `TextureRect` 的 `expand_mode`/`stretch_mode` 写法细节,只要满足基准档 1×、全幅锚点与尺寸判据。
5. B 区 1px 面板描边的可见性处理(被背景覆盖或另加描边薄层均可);不得为描边缩放/裁切纹理,且不得新增类变量。
6. 既有错误扫描暴露的本文件纯格式问题,在不改语义范围内一次修净。
超出必须呈报:
- 任何资产尺寸、`ground_h` 取值、`board_type` 词表、回退链顺序、图层序、文件职责、T 卡验收落点。
- 任何对 `shot_checker`、golden、既有断言或探针判据的改动。
- 任何新增 ConfigDB 容器、Core 字段、UI 组件或第二套板类型选择逻辑。
## 十六、待裁点与开工门
待裁点:**零**。
卡内 provisional(不是设计待裁):
- `ground_h = 100`——按候选出图 1280×100 工艺回填;作者后续可直接调表值,不重开设计。
- 背景/地面文件名与路径格式——art-bible/asset-path-map 已定,本卡只落常量。
- 缺图回退链顺序——dungeon → 平色,按 C7 执行。
开工门:
1. 四件终稿已入包且尺寸对(T0 实读);缺位退 asset-ingest。
2. 待办清单条目状态=美术设计已闭合+已写卡,实况载本卡路径。
3. 经 iter-plan 编入迭代后由 `/iter` 发令开工;本卡无迭代外执行通道。
4. CI 绿、T0 基线无漂移。
5. 本卡估时 3.75~4.5 个工作日;是否单独成批或与邻卡凑迭代归 iter-plan,本卡不代裁。
@@ -0,0 +1,208 @@
# 需求卡片——战斗编排归约批(battle_orchestrator 类变量 38→15)
> **版本状态**:v1.0 定稿——零待裁点(2026-10-29 用户裁:RoundCycle/BattleFacts 两件形态、encounter 归 BattleFacts、attune 改判、treasure_inventory 改址追认、完美账写口升级可裁量)。
> **接收方**:待办清单条目「战斗域结构深拆批——效果发射端口+宿主瘦身+掷格/受理分件(2026-10-27 登记,来源=批次移交)」——本卡承接其中 battle_orchestrator 瘦身面(architecture 轴丁);该条目的效果发射端口与 reward_economy 分件两子项不在本卡。
> **来源**:`reports/设计-战斗编排类变量归约-2026-10-29.md`(设计讨论段定稿,五待裁点 2026-10-29 用户裁全闭合)。**唯一事实源**:该设计文档全文 + `design/engineering/architecture/architecture.md` v1.25「战斗编排域拆件定稿」节轴丁与「模块归属·功能层」RoundCycle/BattleFacts 两行。
## 1. 目标与完成定义
这批做什么:把战斗结算编排宿主 `game/core/battle_orchestrator.gd` 的 38 个类变量收敛到 15(=gdstyle 阈值,合规)。手段=按变化原因拆两枚新件收编 22 个成员——**RoundCycle**(轮循环驱动件:五段段件持有、段序执行、结算重入门闸、开轮摇号、踩雷计数账)与 **BattleFacts**(战斗事实件:受理事实 encounter、轮 facts 缓冲、封印判定账、装板溯源、条件计数、同调快照;attune 状态两成员随件、行为函数留宿主)——外加两项单点处置:treasure_inventory 注入改址(level_runner 经 RewardGrant 出口直注,原「外部赋值路径零改址」句经用户追认失效)与 _hero_atk 转派生读。
什么状态算完:
1. 宿主类变量实测 15、两新件零 gdstyle 键、battle_orchestrator 全文件零超标键(第 9 节键账)。
2. 游戏行为零变化:同种子完整局步骤流、事件流、两快照载荷逐位一致(第 4 节红线;第 11 节 V0 行)。
3. 批末代码域闸全绿(冒烟+系统测试+单元集成关联档+探针+静态扫描),键账对账完成(第 12 节)。
4. 实现报告与反哺点清单落 `reports/`(T4 产出物落位卡)。
## 2. 设计事实源
- `reports/设计-战斗编排类变量归约-2026-10-29.md`——设计讨论段定稿全文。逐成员去向(38 项五去向)、两新件形态与耦合边界、attune 重裁决、已否决切法、随迁键义务、行为零变化风险点、键账预期、改址盘点,全部以该文档对应节为唯一合同;本卡不复制其表格,T0 逐名对账时引用。
- `design/engineering/architecture/architecture.md` v1.25——「战斗编排域拆件定稿」节轴丁两件(RoundCycle/BattleFacts,跨件单向接线、段类随动、attune 改判、S13 键账收敛义务、验收红线)与「模块归属·功能层」两行、「测试与质量域」S13 豁免第三条注销句——实现照册施工。
- 五待裁点裁决(2026-10-29 用户裁,对应设计文档 §13):①encounter 归战斗事实件=采纳;②treasure_inventory 注入改址=追认(「外部赋值路径零改址」句失效);③attune 重裁决=采纳「状态归 facts、行为留宿主」;④完美账写口升级=采纳(grant_segment 三处直写改经 facts 三方法);⑤对拍闸已随 2026-10-30 工程批退役(用户裁:整局重放对拍不再作验收载体)——原 PARITY 批准待裁点就此注销。
## 3. 现状事实(代码级,2026-10-29 实查;开工 T0 按当轮 master 重测复核)
- `tools/gdstyle_baseline.txt:9-10`——battle_orchestrator.gd 现登记两条类级键:`quality/max-class-variables = 38`、`quality/max-file-length = 1035`;**该文件零函数级键**(59 个函数全部在默认阈值内——设计文档 §7 前提成立,T0 复核)。
- 宿主 38 类变量声明区=`battle_orchestrator.gd:37-85` 与 `:223-227`(逐名清单见设计文档 §2 表)。宿主改后保留 15:db、rng、minesweeper、action_economy、_enemy_system、_status_effects、_reward_economy、_damage、_fx_host、_reward_grant、round_index、battle_result、nest_preview_count、_rounds、_facts。
- **编队前置实态**:迭代 42(gdstyle 尾波清偿批,roadmap 在册)合回后轴丙 BoardInstaller 已建——`_board_generator` 成员随件退役(38→37)、装板域六函数迁出、宿主复位块留薄壳 `_install_and_reset`、文件长键归绿;届时宿主约 890 行/37 成员,本卡叠加其上。**本卡在迭代 42 合回之前禁开工**;T0 按当轮 master 重测对账。
- 段驱动现态:五段构造 `battle_orchestrator.gd:256-260`(授予/战斗两段三引用形,aura/decay/verdict 单引用形);三闸 `_maybe_settle` :592-597;段序体 `_settle` :602-613(终局早退不回置 _settling);开轮摇号 `_roll_round_actions` :677-721(behavior_roll 流 :717,mines_hits 转存 :720-721);`_on_round_end` 末行 :589 调 `_maybe_settle`。
- 事实族写读点:缓冲追加 `_on_cell_revealed` :554-580(身份快照捕获 :576 留宿主);`_on_seal_judged` :583;`_facts_path` 写 `_on_round_end` :588;轮复位 `_reset_round_facts` :976-979(三调用点 :273/:810/:862);封印/完美/标错/板序/溯源清零在装板复位块 :442-446(迭代 42 后归宿主薄壳);`_refresh_used_this_round` 复位点 `_exit_distribution` :811;完美谓词 `perfect_sweep_earned` :1033-1035。
- treasure_inventory:宿主声明+setter 转发块 `battle_orchestrator.gd:48-52`;唯一外部注入点 `level_runner.gd:520`;宿主读 :111/:203/:450/:858;`grant_segment.gd:370-371` 读 `oc.treasure_inventory`。
- _hero_atk:声明 `battle_orchestrator.gd:227`、受理写入 :304、spawn 消费 :305、`combat_segment.gd:56` 消费 `oc._hero_atk`。
- attune 外部三面(零改址标的):`attune_has` 公有面消费=table_structure_guard_test 起战断言(:162/:166)与 breakpoint_resume_test;`attune_param_sum`=main.gd:567 与 treasure_effects_test;`attune_convert_state`=treasure_interact_controller.gd:125。三面全部留宿主公有面。
- 冻结面:`nest_preview_count` 外部直读=main.gd:536-537+treasure_effects_test:153;`round_index` 公有直读=system_test.gd:447-462(st.round_index)等四套件约 15 处。
- **设计文档 §11 漏列一处(T0 实查补入)**:`settle_segments_test.gd:27` 接线断言 `oc._grant_seg != null and oc._grant_seg.oc == oc`——段件迁 RoundCycle 后必红,须改指 `oc._rounds._grant_seg`。
- 对拍闸(原闸 11 与 PARITY_TOUCH_FACES 触面判定)已随 2026-10-30 工程批退役——本批无触面批准事项;T0 复核以此为准。
- `game/tests/static_scan.sh`——S8 await 文件集(:317,9 文件)与 S7 B1 域文件集(:359,9 文件)均未含两新件路径;round_cycle.gd 经 oc 调 B2 符号(`_enemy_system` 族)须随批增行,battle_facts.gd 零协作调用不入清单。
## 4. 设计契约(违反即返工的红线条款)
- 🔴 **行为零变化(本批总判据)**:同种子下事件流/步骤流/两快照载荷/存档往返逐位一致;玩家可见面零变化;`design/gdd/systems/` 零修订预期。
- 🔴 **RNG 取号序零漂移(封闭流随件迁移先例句)**:behavior_roll 随 `roll_round_actions` 迁 RoundCycle——流名、开流时点、取号序逐字(先例句=「fx_reveal 封闭流随件迁移取号序不变」同款);encounter/gp_drop/board_gen/variant_place/fx_reveal 五流留原地零触碰;零 `randomize()`/自建随机。
- 🔴 **信号顺序契约冻结**:五信号(step_row/battle_finished/round_started/board_changed/settlement_snapshot)留宿主;发射语句零迁移、发射时序零变化;A2 三 connect(:251-254)逐字。
- 🔴 **两快照出口签名与载荷逐字不变**:presentation_snapshot/diagnostic_snapshot 字段名、嵌套结构、值口径逐字——内部读数改指两件投影后值恒等(round_index/attune_safe_seen/完美三账/hero_atk 派生读均受理后不变量)。
- 🔴 **明细行协议词表零触碰**:基础 9 类+扩展行词表、SOURCE_EXEMPT_TYPES/SOURCE_EXEMPT_CLASSES 两常量、`_emit_row`/`_assert_row_source` 行发射基建留宿主零迁移。
- 🔴 **行为零变化承载(对拍闸退役后)**:同种子逐位一致由关联套件的既有回放断言与两快照恒等断言承载;对拍闸(原闸 11)已随 2026-10-30 工程批退役,无闸级对拍批准事项。
- 🔴 **拆分边界纪律**(判据权威=code-conventions §1.1,此处立卡内执行条款):①每个拆出单元须有可陈述的职责语句与单一变化原因(实现报告逐件一行承载);②拆出单元须有集体身份——命不出名字、只能说「剩下的成员」的打包=凑计数袋,禁;③「过阈值」只许作为发现信号,作为拆分或收拢的动机即违规;④按纪律拆不出内聚单元 → 停卡呈报走豁免/还债通道,禁硬拆凑绿;⑤拆分沿职责轴不沿阈值轴。两新件职责语句见第 8 节(RoundCycle=轮结算周期的推进结构;BattleFacts=战斗事实的记录与查询口径)。
- 🔴 **禁为机检文本保留无效成员**:22 成员随件迁出+treasure_inventory/_hero_atk 两成员退役=宿主删 24 项(37−22−2+RoundCycle/BattleFacts 两新件接线引用=宿主 15,账平——grilling 复核订正原算术),禁保留别名、别名转发属性或空壳;机检键与卡片契约互斥时,实现段一律停卡呈报,不得自造第三条路。
- 🔴 **随迁键收敛义务**:迁移函数逐字纪律——`roll_round_actions`(45 行/分支≈8 贴线)、`settle_if_due`、`reset_round`、三 mark 方法迁移与改指**禁顺手增分支/局部**;函数迁入新文件若出超标键=卡内同步收敛(拆助手函数,拆分不改语义;先例=迭代 40 卡① 12 随迁键收敛),禁带新键进批末闸。
- 🔴 **段类接线改形守 Middle Man 禁壳**:段类对宿主的直调形态不变(段类经宿主引用直调,现状形态);段内改指一律调用点改指(`oc._X` → `oc._facts.X`/`reward_grant.treasure_inventory`),宿主与新件禁留转发壳。
- 🔴 **_fatal 与 battle_result 状态机唯一写点留宿主**:RoundCycle/段类经 oc 直调 fatal 面零改形;BoardInstaller(迭代 42 建件)fatal Callable 零触碰。
- 🔴 **BattleFacts 成员冻结**:15/15 贴顶——实现期禁增员,增员即停卡;扩容阀=下一张要加事实成员的卡先把封印判定账五成员二次拆出(SealLedger,−5)再落新账,禁硬挤。
- 🔴 **reset_round 复位边界**:只清 reveals/seals/path 三项、**不碰 refresh_used**——_battle_over 后快照读到的 refresh_used 必须保持末轮值(设计文档 §8.5 险点,对照表 V2 行断言钉住)。
## 5. 范围边界
做什么:设计文档 §2 逐成员去向表的全部迁移与改址、两新件建件、treasure_inventory 改址、_hero_atk 派生读、随迁键收敛、S7/S8 静态扫描随动、对照表新增断言四条。
不做什么:
- 轴丙 BoardInstaller 建件——迭代 42 在册卡执行,本卡前置、不重复。
- 五段段类职责与段序语义零改写(只改指);`_exit_distribution` 留宿主(设计文档 §6.5 已否决迁驱动件——判定段直调宿主,零新引用环)。
- attune 行为函数留宿主(attune_has/attune_param_sum/attune_convert_state/积点自动兑付体/充能重置零迁——设计文档 §5 重裁决);AttuneLoadout 零触碰。
- nest_preview_count 冻结面、round_index 公有直读面、A2 三事件订阅面——零变化。
- 数值表、RNG 流词表、信号载荷 schema、明细行词表——零触碰;本批零数值改动,MC 零跑(第 13 节)。
- `game/tests/README.md`、`design/` 全目录、根规则文件——实现批禁写(反哺点通道,第 6 节)。
- 效果发射端口定形与 reward_economy 分件——归接收方条目其余子项,另行设计讨论。
## 6. 关联文档同步面
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|---|---|---|
| design/engineering/architecture/architecture.md | 轴丁两行与功能层 RoundCycle/BattleFacts 行——设计已闭合落文 v1.25,实现照册施工;「测试与质量域」S13 豁免第三条注销句已预写注销(本批键账达成即生效,未达成=停卡非落文);两行「构造=引用束注入」句与本卡三参直构/零参直构实态的口径差;批末成员数/行数/公开方法数实测值与在册数字出入 | 设计权威文档——实现批禁直改;出入逐条列进 `reports/iter<N>-反哺点-<日期>.md`,用户裁决后归文档批落文 |
| game/tests/README.md | 本批零新增能力、零入口与代表用例名变更(测试改指只动成员寻址;新增断言四条全部内嵌既有函数);能力清单零增删改 | 机械面文档——实现批禁写;批末收尾核对表上名字与实际落地一致(改名则回表) |
| design/gdd/systems/(全树) | 无关联文档——结构整改不改游戏规则与玩家可见行为 | 无修订预期;若实现中发现契约句被触碰,停卡呈报走反哺点通道 |
## 7. 卡前文件结构图
| 文件 | 职责 | 改动性质 |
|---|---|---|
| game/core/battle_facts.gd | 战斗事实件(新增,architecture v1.25 功能层在册)——受理事实/轮 facts 缓冲/封印判定账/溯源/条件计数/同调快照,自带轮复位与完美账写口 | 新增(非行为改动——成员迁移;验收=对照表 V1/V2/V6 行) |
| game/core/settlement/round_cycle.gd | 轮循环驱动件(新增,同在册)——五段段件持有与段序执行、结算重入门闸、开轮摇号、踩雷计数账 | 新增(非行为改动;验收=V1/V3 行) |
| game/core/battle_orchestrator.gd | 战斗结算编排中枢——宿主 15 成员定着、行发射基建、状态机、两快照出口、attune 行为读数与触发面 | 修改(非行为改动——成员改指、三函数迁出、派生读、注入改址;验收=V0–V5 行) |
| game/core/settlement/grant_segment.gd | 授予段——事实族改指约 23 处+完美三处直写改经三 mark 方法 | 修改(非行为改动) |
| game/core/settlement/combat_segment.gd | 战斗段——2 处改指(oc._facts_reveals 扫描、oc._hero_atk 派生调) | 修改(非行为改动) |
| game/core/level_runner.gd | 关卡域生命周期——库存注入 1 行改经 `reward_grant()` 出口直注 | 修改(非行为改动——注入路径改址,注入时序与消费面零变化;验收=V5) |
| game/tests/static_scan.sh | 静态扫描域承载——S7 B1 域文件集与 S8 await 文件集增行 round_cycle.gd | 修改(工程随动,architecture 拆件定稿节 S7/S8 随动条在册) |
| game/tests/unit/battle_settlement/seal_polarity_test.gd · unit/core/feat_vouchers_test.gd · unit/battle_settlement/trigger_cond_test.gd · unit/battle_settlement/round_flow_skill_pool_test.gd · unit/core/settle_segments_test.gd · unit/core/treasure_effects_test.gd · unit/core/adventure_feats_test.gd · unit/core/meta_progression_forge_test.gd · unit/battle_settlement/roll_multiplier_test.gd | 成员寻址改指(断言条件与期望值零变化)+新增断言内嵌既有函数(V1/V2/V4/V5) | 修改(改指=非行为改动;新增断言行=对照表对应行) |
aura/decay/verdict 三段、battle_fx_host、reward_grant、damage_controller、main.gd、treasure_interact_controller、board_installer——零改动(设计文档 §11 盘点)。新增件「为什么不能复用」理由与唯一落点核查见第 8 节;对不上即卡片不成立。
## 8. 落位与可复用性预检
- **battle_facts.gd(新增)**:复用面核查——战斗事实记录面现行只摊在 battle_orchestrator 成员区(全仓 grep 同名与同义标识符:`_facts_reveals`/`_seal_consumed`/`_perfect_*` 等无第二实现),无既有件可复用。不能并入 BattleFxHost(效果引擎宿主域——变化原因是效果发射与施加,不是事实记录)、不能并入 RewardGrant(授予管线域——事实账不是授予物)、不能并入五段段类(段类只搬运时序零持有账——缓冲写读跨段跨界,归宿主编排面)、不能并入 AttuneLoadout(营地/冒险域充能物化单源,迭代 39 已闭合契约——重开已闭合边界=设计文档 §5 理由 4 否决)。落点唯一:`game/core/battle_facts.gd`。分层:core 层 RefCounted,零注入引用、零信号、零协作对象调用——纯数据+谓词+写口;身份快照捕获(query_full_snapshot)留宿主 `_on_cell_revealed`,防它变成第二编排面。
- **round_cycle.gd(新增)**:复用面核查——轮结算周期推进机构(段件持有/重入闸/摇号/踩雷账)现行只存在于宿主三函数+五成员,无第二实现。不能并入 BattleFxHost/RewardGrant(变化原因不同,同上)、不能并入任一段类(它是段件的序主人——持段件者不能是被持者)。落点唯一:`game/core/settlement/round_cycle.gd`(与五段段件同目录)。分层:core 层 RefCounted、零 await;随机只经 `oc.rng` 的 behavior_roll 封闭流。
- **引用束注入构成**:引用束先例(BattleFxHost/RewardGrant 的字典束)防的是 `_init` max-parameters 新键——本批两新件参数均未触发:RoundCycle `_init(host, fx_host, reward_grant)` 三参直构(fx_host/reward_grant 仅作段构造透传,不落成员);BattleFacts 零参直构。architecture 两行「构造=引用束注入」句按设计文档 §3/§4 实态执行(三参直构无需束),出入随反哺点申报。
- **依赖方向**:宿主→两新件单向(宿主 `_init` 建件并接线,每战新建同灭);RoundCycle 持 `oc`=五段段类既定同款形态(段类已持宿主引用直调——非新增反向引用),除 `oc` 外禁持宿主其他成员、禁回调宿主私有族;BattleFacts 不持任何对象引用——宿主与段类经注入引用直读写(fx_host._fx_bonus_delta 跨件直读写同款现状形态)。新件之间零互引。
- **新增函数**:宿主 `_hero_atk()` 派生读=`_journal()`「读面即引用、无第二账」同款先例(encounter 受理后只读,派生值恒等);`reset_round`=原 `_reset_round_facts` 逐字;三 mark 方法=新写口(升级采纳案)。零新增数据件、零新增常量表。
## 9. 实现规范预算(新件对照 gdstyle 默认阈值;写码前对账,超预算面=停卡退回写卡段)
| 新件 | 公开方法 ≤20 | 类变量 ≤15 | 迁移函数对账(基线源函数键) |
|---|---|---|---|
| battle_facts.gd | 预算 4(reset_round+mark 三方法;设计文档 §10 表载 5 属含 `_init` 宽口径计数差,两口径均 ≤20 零键风险) | 15(贴顶——实现期禁增员,增员即停卡;扩容阀=SealLedger 二次拆出) | `reset_round`=原 `_reset_round_facts` 逐字(基线无其函数级键);三 mark 新增小函数远在阈值内 |
| round_cycle.gd | 预算 3(settle_if_due/roll_round_actions/is_settling) | 8(oc+五段段件+_settling+mines_hits) | `settle_if_due`=`_maybe_settle`+`_settle` 合体(两源函数基线零键;闸序与段序逐字);`roll_round_actions` 45 行/分支≈8 贴线——迁移禁增分支/局部,确需增条件即停手核对 gdstyle;`is_settling` 新增小函数 |
存量触面一查:battle_orchestrator 类变量 37(轴丙后实测)→15 只降、文件长约 890→约 830 只降(均阈值内,S13 键较登记值只降不升——达成手段=本卡迁移,无豁免申报);grant_segment/combat_segment 改指零新增键(`_judge_seal` 等函数体只换成员寻址);level_runner 改 1 行零新增键。两新件与改址后宿主、段类落地后先跑 `python tools/gdstyle_gate.py` 预检一次再进批末闸(T4 步骤)。`_init` 形态:RoundCycle 三参/BattleFacts 零参(max-parameters 阈值 5 未触发)。新增文件全类型标注(S9——容器参数化走 `Dictionary[Variant, Variant]` 现行口径);头注按 code-conventions §1.4.1(职责语句+封闭面声明,禁变更叙事、禁文档反链)。
## 10. 任务卡 T0..T4(估时为区间;工作量限制只属迭代层)
**T0 重测对账(0.25–0.5 工作日)**(前置:迭代 42 合回——BoardInstaller 在库;产出:逐成员对账结论与改址点名清单,后续各卡取用)
- [ ] 以当轮 master 重测 battle_orchestrator.gd 类变量逐名清单,对账设计文档 §2 表五去向(留宿主/RoundCycle/BattleFacts/退役改址/派生读;轴丙后实态 37 项,`_board_generator` 行应已消失)——对不上即停卡呈报
- [ ] 复核 gdstyle 基线:battle_orchestrator 函数级键数(预期零——漂移即停卡呈报);类变量键现值与文件长键现状(应已随轴丙归绿或基线已 refresh)
- [ ] 复核第 3/7/9 节全部 file:line 引用与设计文档 §11 改址盘点实数(grant_segment 约 23–25 处、测试各套件逐处点名——含 settle_segments_test:27 补入项);漂移处就地改卡并记录
**T1 BattleFacts 建件与事实成员迁移(0.75–1 工作日)**(取用:T0 对账清单;产出:`_facts` 在座、事实族 15 成员归件——T2/T3 引用其成员名)
- [ ] 建 `game/core/battle_facts.gd`:class_name BattleFacts,extends RefCounted;15 类变量初值逐字承原宿主声明(encounter/reveals/seals/path/refresh_used/board_seq/board_from_midround/seal_consumed/seal_miss_seen/perfect_hit_true/perfect_hit_false/perfect_forgiven/feat_first_dig_used/attunement/attune_safe_seen);公开方法 4:`reset_round()`(原 `_reset_round_facts` 逐字——只清 reveals/seals/path 三项,不碰 refresh_used)、`mark_perfect_hit()`、`mark_perfect_miss()`、`mark_perfect_forgiven()`;头注写职责语句+成员封闭面+零注入零信号零协作调用
- [ ] 宿主 `_init` 建 `_facts = BattleFacts.new()`;事实族成员全量改指:宿主声明删 15 项、读写点改 `_facts.X`(encounter 全部读点——_journal/_attune_loadout/_fx_host_fact/受理段/fx_state/两快照/gp_drop,按 T0 点名清单逐处;缓冲追加/复位/装板清零/perfect 谓词同批);`_reset_round_facts` 函数删除,三调用点改调 `_facts.reset_round()`(时序位置逐字)
- [ ] grant_segment 事实族改指(`oc._facts_reveals/_facts_seals/_facts_path/_seal_consumed/_seal_miss_seen/_feat_first_dig_used/_perfect_*` → `oc._facts.X`);完美三处直写改经三 mark 方法(:311/:379/:394——写口升级采纳案;:397 forgiven 读数直读 `_facts.perfect_forgiven` 零变化)
- [ ] 测试改指:seal_polarity_test 8 处(_perfect 直写→_facts.perfect_*)、feat_vouchers_test facts 族、treasure_effects_test:309(ob14.attunement→ob14._facts.attunement)、adventure_feats_test 1 处(encounter 读→_facts.encounter)——断言条件与期望值零变化
- [ ] 新增断言 round_flow_skill_pool_test.gd:_smoke_full_battle 内嵌〔编排归约 V2〕:终局后 `presentation_snapshot()["refresh_used"]` 等于末轮结算 snapshot 的 economy.refresh_used(复位边界险点,规格见对照表)
- [ ] 孤立验证:`godot --headless --script res://tests/run_all.gd -- --only=seal_polarity,feat_vouchers,treasure_effects,adventure_feats,table_structure_guard,round_flow_skill_pool,variant_placement` 全绿+`python tools/run_smoke.py`(出红只跑红件最小单元)
**T2 RoundCycle 建件与段驱动迁移(0.75–1 工作日)**(取用:T1 的 `_facts`;产出:`_rounds` 在座、段驱动三入口——T3 引用其方法名)
- [ ] 建 `game/core/settlement/round_cycle.gd`:class_name RoundCycle,extends RefCounted;类变量 8(oc/mines_hits 公有+_grant_seg/_aura_seg/_combat_seg/_decay_seg/_verdict_seg/_settling 私有);`_init(host, fx_host_ref, reward_grant_ref)` 三参——五段构造五行从宿主 `_init` 逐字搬入(构造参数与持有形态不变);公开方法 3:`settle_if_due()`(原 `_maybe_settle` 三闸+`_settle` 段序体合体——闸序 settling→battle_result→phase 逐字,终局早退不回置 _settling)、`roll_round_actions()`(原函数逐字迁入——behavior_roll 流名/开流时点/取号序逐字,宿主引用经 oc 改指,禁增分支/局部)、`is_settling()`;头注写职责语句与单一变化原因
- [ ] 宿主 `_init`:五段构造五行迁出,改 `_rounds = RoundCycle.new(self, _fx_host, _reward_grant)`(置于两件建齐与 bind 接线之后——构造序禁环不变);五段成员声明删除;`_maybe_settle`/`_settle`/`_roll_round_actions` 三函数删除
- [ ] 宿主调用点改址:`_on_round_end` 末行→`_rounds.settle_if_due()`;begin_battle 与 `_exit_distribution` 的 `_roll_round_actions()`→`_rounds.roll_round_actions()`;diagnostic_snapshot settling 读→`_rounds.is_settling()`;`_begin_battle_fx_state` 踩雷清零→`_rounds.mines_hits = {"total": 0, "last": 0, "round": 0}`(赋值复位——语句时点逐字);`_book_negative_blast` 两写→`_rounds.mines_hits`
- [ ] 测试改指:trigger_cond_test :119/:126/:131(orch._mines_hits→orch._rounds.mines_hits);settle_segments_test :27(→oc._rounds._grant_seg);round_flow_skill_pool_test:1022 与 feat_vouchers_test 5 处(_grant_seg→_rounds._grant_seg)
- [ ] 新增断言 settle_segments_test.gd(接线检查段内嵌)〔编排归约 V1〕:oc._rounds 非空且 `_rounds.oc == oc`、五段段件齐在 _rounds、oc._facts 非空(规格见对照表)
- [ ] `game/tests/static_scan.sh` S8_FILES(:317)与 S7_B1_SET(:359)增行 `game/core/settlement/round_cycle.gd`(battle_facts.gd 零协作调用不入两清单)
- [ ] 孤立验证:`-- --only=round_flow_skill_pool,settle_segments,trigger_cond,seal_polarity,skill_unlock,roll_multiplier,variant_placement` 全绿+冒烟
**T3 宿主改形收束(0.5–0.75 工作日)**(取用:T1/T2 两件与成员名;产出:宿主 15 成员定着)
- [ ] 宿主 `_rounds`/`_facts` 成员声明落位;已迁成员声明复核清零——类变量实测 15(第 3 节名单),禁无效成员
- [ ] treasure_inventory 改址:宿主成员与 setter 转发块(:48-52)删除;四处读改指 `_reward_grant.treasure_inventory`(_hook_attune_consume/_reseed_attune_charges/装板薄壳叠加层退役/_battle_over 叠加层退役);`level_runner.gd:520` 改 `_settlement.reward_grant().treasure_inventory = treasure_inventory`(注入时序在宿主构造之后不变)
- [ ] _hero_atk 派生读:宿主新增私有函数 `_hero_atk() -> int`(返回 `int(_facts.encounter.get("hero_atk", int(db.combat.hero.atk)))`);受理段写入与 spawn override 改调;`combat_segment.gd:56` oc._hero_atk→oc._hero_atk();_hero_atk 成员删除
- [ ] attune 读数出口核验:attune_has/attune_param_sum/attune_convert_state/_attune_charge_safe_row/_attune_safe_seen_read 读源改 `_facts.attunement`/`_facts.attune_safe_seen`——三个外部消费面(main.gd:567、treasure_interact_controller.gd:125、table_structure_guard_test/breakpoint_resume_test 起战断言)零改址零触碰
- [ ] 新增断言 roll_multiplier_test〔编排归约 V4〕与 treasure_effects_test〔编排归约 V5〕(规格见对照表)
- [ ] 孤立验证:`-- --only=roll_multiplier,treasure_effects,breakpoint_resume,level_shop_economy,round_flow_skill_pool` 全绿+`python tools/run_system_test.py`
**T4 键账对账与收口(0.25–0.5 工作日)**(取用:T0–T3 全部产出;产出:本批交付物)
- [ ] `python tools/gdstyle_gate.py` 预检一次——宿主类变量 15、round_cycle 8、battle_facts 15、文件长 ≤1000(预期约 830)、零基线外新键;出超标键→按第 4 节随迁键收敛义务卡内拆助手后复检
- [ ] 里程碑步出口跑全套闸:`bash tools/run_all_gates.sh --code` 全绿(改动全为代码面);闸 11 PARITY 触面按第 4 节条款——实跑对拍须用户批准发起、不得借机重锚,NEED-APPROVAL 时停步呈用户裁决
- [ ] 键账对账:第 9 节预期逐键核;基线 refresh 由用户发令人工 `python tools/gdstyle_gate.py --refresh`(须 --force——全基线 16 键消失 ≥2 条触发骤降守卫;与姊妹卡同批编队时一次 refresh 合并执行)
- [ ] 产出物落位卡:`reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+实现决定记录+逐拆出单元职责语句一行)与 `reports/iter<N>-反哺点-<日期>.md`(第 6 节同步面逐条落点)写齐方算收口
合计估时约 2.5–3.75 工作日(设计文档 §12 载 2–3 日;上探余量=测试改址逐处点名与 PARITY 批跑配合;PARITY 实跑须用户在场批准,不占卡估时)。切分预案:T1/T2 迁移函数出超标键需卡内拆助手时,超软盒预挂改由 T3 并 T4 吸收。
## 11. 验证闸——改动—验收对照表
域口径:本批改动全为 `game/` 下非 .md 行为载体——批末跑代码域闸(`tools/run_all_gates.sh --code`);里程碑步(T 段)出口跑一次全套闸,波内修复只做出红最小单元的最窄确认,新增测试先行孤立验证再并入全套。测试执行两入口铁规不破。三件保险(冒烟/系统测试/静态扫描)恒跑;02 单元与集成按关联档选套件(上表落点套件并集)。能力清单:本批零新增能力、零入口与代表用例名变更——能力行全部为既有名,零增删改(第 6 节)。**静态判据申报(不入对照表)**:键账收敛与静态随动(宿主 38→15、round_cycle 8/battle_facts 15、零基线外新键、static_scan.sh S7/S8 两清单增行)无运行时可观测提交面——判据=`python tools/gdstyle_gate.py` 与 `game/tests/static_scan.sh` 退出码(T4 执行),属静态判据+评审注记申报观测面局限(test-conventions §8.4 出口)。
| 行 | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| V0 | 行为零变化(本批总判据) | 全部结构改动(第 7 节清单) | 关联套件零改址应绿(对拍闸已随 2026-10-30 工程批退役——总判据由 V1–V5 各行能力行并集承载);静态判据申报同上(键账收敛与 S7/S8 两清单增行) | 集成 | 真实局 begin_battle→挖掘→结算→终局 | 关联套件全绿且改址面零失败——出红且无改址归因即停机呈报 | 既有:轮内流与结算五段全序;既有:技能池数据面与回放独立;既有:伤害公式与掷点乘区;既有:秘宝结构冻结与 12 件效果 |
| V1 | 两新件建件接线(T1/T2) | battle_facts.gd/round_cycle.gd 新建;宿主 _init 建件接线;五段构造迁 round_cycle | 新增 settle_segments_test.gd 接线检查段内嵌〔编排归约 V1〕:`oc._rounds` 非空且 `_rounds.oc == oc`;五段段件齐在 `_rounds`;`oc._facts` 非空 | 单元 | BattleOrchestrator 直构(settle_segments_test 现行夹具同款) | 编排器接线面(新件在座与宿主回指——接线断言本体) | 既有:轮内流与结算五段全序 |
| V2 | reset_round 复位边界(险点) | battle_facts.reset_round 只清三缓冲;宿主三调用点改调 | 新增 round_flow_skill_pool_test.gd:_smoke_full_battle 内嵌〔编排归约 V2〕:终局后 `presentation_snapshot()["refresh_used"]` == 末轮结算 snapshot `economy.refresh_used` | 单元 | begin_battle→轮内刷新→停手→结算→终局 | 两快照同键值恒等(复位不碰 refresh_used 的可观测提交面) | 既有:轮内流与结算五段全序;既有:停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励) |
| V3 | 段驱动与摇号迁移(重入门闸/settle_if_due/roll_round_actions/mines_hits 账) | 三函数迁出宿主;宿主调用点改址;trigger_cond_test 3 处改指 | 复用 round_flow_skill_pool_test 全套(_smoke_full_battle/_smoke_settlement_segments/_smoke_skill_pool_replay_independence)、settle_segments_test V4-3 真实一轮、skill_unlock_test、trigger_cond_test(改指后应绿)——零新增断言 | 单元 | begin_battle;stop→WAITING→settle_if_due | 五段裁定行序+settling 闸+终局早退+同种子摇定逐位(behavior_roll 流随件零漂移由既有回放断言钉住)+mines_hits 转存读数 | 既有:轮内流与结算五段全序;既有:技能池数据面与回放独立;既有:Boss 半血解锁技门控 |
| V4 | _hero_atk 派生读 | 成员删→私有函数;combat_segment.gd:56 改调 | 新增 roll_multiplier_test.gd(既有主角攻击链函数内嵌)〔编排归约 V4〕:enc 携 `hero_atk` override 起战→主角攻击单 base_value==override;缺省→base_value==hero 表值 | 单元 | begin_battle(enc)→战斗段主角先手 | 主角攻击单基值(受理后派生值恒等的判据本体) | 既有:伤害公式与掷点乘区 |
| V5 | treasure_inventory 注入改址 | level_runner.gd:520 改经 reward_grant() 出口;宿主四处读改指;宿主 setter 转发块删 | 新增 treasure_effects_test.gd(秘宝使用链内嵌)〔编排归约 V5〕:注入后 `current_battle().reward_grant().treasure_inventory` 与关卡库存同一引用,且 use_treasure 受理走通 | 单元 | LevelRunner 建战注入→use_treasure 四步 | 注入引用同一性+受理面走通(改址后消费面不断链) | 既有:秘宝结构冻结与 12 件效果 |
| V6 | 封印判定账/装板溯源/受理事实迁移(完美写口升级、attune 状态随件) | 事实族改指(grant_segment 约 23 处+测试);三 mark 写口;attune 读源改 _facts | 复用 seal_polarity_test 全套、feat_vouchers_test(契约 2 计数纯净 R2)、variant_placement_test、meta_progression_forge_test、adventure_feats_test、table_structure_guard_test(attune 面零改址应绿——本批最硬的外部面零变化证)——零新增断言 | 单元 | 封印动作→结算;begin/refresh/exit 装板;begin_battle(enc) | 完美三账读数+去重闸+契约 2 计数纯净;刷新图零清图奖励+board_seq;enc 携带键贯通;attune_has/param_sum/convert_state 外部面零变化 | 既有:封印与极性路由/不可逆;既有:清图奖励双份与零路径;既有:变异布置门控;既有:专长(奇物)授予与凭证保底;既有:元进度入账与锻造两轴;既有:装板与开局免费展开(封锁格无资源、计入清图) |
反向核对:diff 中每个行为载体改动均有行——本批申报**行为改动零条**(全部为非行为改动:成员迁移与调用点改指,判断依据=V0 同种子逐位一致证明+既有断言面零改址应绿;完美写口升级=同一时点同一增量改经方法单点,语义逐字)。新增断言四条(V1/V2/V4/V5)全部挂行;孤儿断言零条。测试削弱申报见第 15 节。
## 12. 基线纪律(S13 键账)
1. 本卡键账目标=battle_orchestrator 类变量键归绿(实测 15 后随 refresh 消失);文件长键已由轴丙(迭代 42)归绿,本卡保持文件长 ≤1000(预期约 830)。
2. 禁为过闸 refresh 换绿:基线重生成只由用户发令,批末键账对账后人工 `python tools/gdstyle_gate.py --refresh` 一次执行(禁闸内自动重生成、禁实现批中途 refresh)。
3. 重锚守卫:新基线键数较旧基线少逾 10% 须 `--force` 显式确认——本批与轴丙预期合计消失 ≥2 条(16 键基线),须带 `--force` 由用户执行。
4. 还债=数值下降自然绿:迁移后同键数值下降不判红;出现恶化(同键数值较登记值上升、基线外新键)=停卡呈报,禁自行消化。
5. S13 豁免第三条(battle_orchestrator max-class-variables,architecture v1.25 已预写注销句)随本批达成生效——未达成=停卡呈报,非落文事项。
## 13. MC/数值与 RNG 零触碰声明
本批零数值改动、零数据表改动、零 RNG 流词表改动、零玩法字面量引入——结构整改不改数值与 RNG 消费序。behavior_roll 封闭流随件迁移取号序不变(流名、开流时点、取号序逐字)。数值与 RNG 零触碰则 MC 零跑;对拍闸已随 2026-10-30 工程批退役,无闸级对拍事项。
## 14. 异常分级
- **L1 自救**:改指漏改编译错、测试寻址漏改——就地修复重跑最窄确认。
- **L2 记录继续**:实测行数/改指处数与设计文档「约」值区间偏离、头注指址微调——记入实现报告「实现决定记录」。
- **L3 停机写报告等用户**:①机检预算互斥——两新件实数超第 9 节预算、BattleFacts 需增员、或迁移函数现出超标键=上游缺陷停卡退回写卡段(禁自造第三条路);②encounter 归属被实现期证伪——受理事实与轮 facts 复位节奏冲突实锤、且设计文档 §6.3 两条砍路均破冻结面(宿主 16 不可达)=停卡呈报走判据④通道;③T0 对账不符(成员去向对不上、基线出现函数级键、file:line 漂移过大);④V0 行为零变化被证伪(关联套件出红且无改址归因);⑤断言面被下游兜底吸收致判据绑不上(按 test-conventions §8.4 出口呈报);⑥S13 键恶化或基线外新键。
## 15. 测试削弱申报(允许改动的旧断言及理由)
零申报——本批不删任何断言、不改判据阈值、不缩扫描面、不把真实入口换成下层直调;测试改指只动成员寻址(断言条件与期望值零变化);新增断言四条(V1/V2/V4/V5,全部内嵌既有函数)。未申报的削弱由独立验收审计判红。
## 16. 独立验收审计
合并前由未参与实现的会话或子代理执行;报告落 `reports/iter<N>-验收审计-<日期>.md`。审计至少做四件事(test-conventions §8.7):逐行核对第 11 节对照表(测试存在、真执行、尾缀可 grep、真实入口未绕过改动点);抽查判据绑定面(V2 两快照同键、V4 攻击单基值、V5 引用同一性——当轮真实跑批执行过);检查测试削弱(对照第 15 节零申报);核对全部「非行为改动」判定依据(零漂移证明是否在当轮真实跑批中成立)。未过不得合并。
## 17. 代拍板白名单(允许 agent 自行拍板的微决策)
1. 迁移函数与改指处头注/行注的指址微调(词表与语义不动——「指址须真」纪律)。
2. 调用面改指的机械改写(排版、注释同步、行内换行)。
3. 断言失败消息尾缀编号分配与措辞(〔编排归约 Vn〕系列)。
4. 新件私有成员与小函数命名微调(职责语义不变、公有面契约名逐字不动)。
5. 宿主 _init 内两新件建件语句的次序微调(约束不变:_facts 无依赖先建、_rounds 在两件建齐与 bind 接线之后)。
超清单决策记入实现报告「实现决定记录」节。
## 18. 待裁点
零待裁点——五项待裁点(设计文档 §13)已由用户 2026-10-29 全裁闭合(第 2 节裁决记录),完美账写口升级已按推荐案采纳落卡。**零待裁点,开工门=U0**。编队前置:迭代 42(gdstyle 尾波清偿批,含轴丙 BoardInstaller)合回之后开工;T0 按当轮 master 重测对账。
@@ -1,431 +0,0 @@
# 需求卡片:换图系双基座七件批
> 日期:2026-10-31
> 性质:需求改动实现批卡片;零待裁点,开工门=U0。
> 来源:待办清单「「封锁-蓄力」combo 设计素材」条。唯一事实源:[奇物](../design/gdd/systems/冒险循环/奇物.md) v1.3+[行动经济](../design/gdd/systems/排雷循环/行动经济.md) v1.3+[战斗结算](../design/gdd/systems/战斗循环/战斗结算.md) v1.4+[地图战斗资源](../design/gdd/systems/战斗循环/地图战斗资源.md) v1.3;参考设计过程件:[提案 v0.5](提案-弃图刷新系专长流派-2026-10-30.md)。
> 估时:T0–T8 ≈ **7.75~8.5 个工作日**;切分预案见第十二节。
## 一、设计事实源
本卡实现下列已闭合设计,不重新裁断机制:
| 来源 | 本卡采用的事实 |
|---|---|
| [奇物 v1.3](../design/gdd/systems/冒险循环/奇物.md) | 基座先行原则、七件名称与职责、双基座叠加 |
| [行动经济 v1.3](../design/gdd/systems/排雷循环/行动经济.md) | 付费挖格产 RAP、刷新成本减半、两件基座叠加 |
| [战斗结算 v1.4](../design/gdd/systems/战斗循环/战斗结算.md) | 成功刷新事实、当轮结算兑付、全局 `damage_taken` |
| [地图战斗资源 v1.3](../design/gdd/systems/战斗循环/地图战斗资源.md) | 现行清图奖励语义不变;旧探索进度回收方案已注销 |
| [提案 v0.5](提案-弃图刷新系专长流派-2026-10-30.md) | 七件最终结构与行为验收面;保留作实现参考,不替代 GDD |
## 二、目标与完成定义
交付七件专长:
| 层 | feat_id | 显示名 | 稀有度/cap | provisional 数值 |
|---|---|---|---|---|
| 基座 | `vein_reserve` | 矿脉储能 | excellent/1 | 每次受理的付费挖格 +1 RAP |
| 基座 | `easy_swap` | 省力周转 | excellent/1 | 刷新成本为基线 50% |
| 输出 | `wall_blast` | 崩壁冲击 | excellent/1 | 全体存活敌各受 2 点 |
| 输出 | `charge_rotation` | 蓄力周转 | excellent/1 | 每刷一次 +1 层,上限 3;下一次主角攻击消费全部层 |
| 生存 | `stone_mitigation` | 石层缓冲 | common/1 | 当轮有成功刷新时,每次主角受伤固定 −2 |
| 生存 | `tunnel_rest` | 坑道小憩 | common/1 | 每刷一次累计 1 点恢复,上限 3;本轮授予段合计回血 |
| 资源 | `mine_profit` | 矿道红利 | rare/1 | 每次成功刷新固定 +1 GP |
完成定义:
1. `feats.tres` 30→37 行;稀有度 普通14/优秀13/稀有10/传说0;牌库 132→139。
2. 两件基座可叠加;刷新次数与 RAP 清零限制保持现行。
3. 所有主角伤害路径统一经过 `damage_taken`,每种伤害单据恰发射一次。
4. 刷新效果只在成功刷新后产生;RAP 不足、相位非法、每轮上限均零触发。
5. 崩壁冲击沿用案 C,在战斗段末结账;石层缓冲、坑道小憩、矿道红利均在本轮既有通道兑付。
6. 无七件专长时现行行为逐位不漂移;持有旧 `damage_taken` 专长时按全局受伤语义生效。
7. 新测试套件与计数回归全部通过,旧断言只允许因语义扩展而改,不得放宽阈值。
## 三、现状事实
1. `dig_paid` 在 AP 扣费前发射,消费环已有 counter/grant AP 通道,无 RAP 通道:[action_economy.gd:101](../game/core/action_economy.gd#L101)。
2. `grant_reserve_ap()` 已存在,直接增加 RAP:[action_economy.gd:177](../game/core/action_economy.gd#L177)。
3. `request_refresh()` 固定读全局基线成本:[action_economy.gd:199](../game/core/action_economy.gd#L199)。
4. 刷新成功序=扣 RAP→计数→旧资源退役→旧板退役→装新板:[battle_orchestrator.gd:610](../game/core/battle_orchestrator.gd#L610)。
5. 当前事件词表20种,效果词表32种:[effect_engine.gd:12](../game/core/effect_engine.gd#L12)。
6. `damage_taken` 现只在敌方攻击路径发射:[damage_controller.gd:472](../game/core/damage_controller.gd#L472)。
7. 地形伤害在开单前单独发射并只消费 `damage_reduce/ac_bonus/counter_add`,不消费 shield:[grant_segment.gd:174](../game/core/settlement/grant_segment.gd#L174)。
8. 棋盘主角直伤在控制器终段只做伤害下限后扣血,不发射事件:[damage_controller.gd:569](../game/core/damage_controller.gd#L569)。
9. 危险格与错误封印的负向资源直伤最终进入同一 `board_flat_hero` 单据:[reward_grant.gd:320](../game/core/reward_grant.gd#L320)、[reward_grant.gd:369](../game/core/reward_grant.gd#L369)。
10. 授予段伤害单已有案 C 暂存队列,战斗段末统一补提交:[grant_segment.gd:24](../game/core/settlement/grant_segment.gd#L24)、[combat_segment.gd:114](../game/core/settlement/combat_segment.gd#L114)。
11. `grant_gp` 现消费点写 `reward_grant.escrow` 并发专长 GP 行:[grant_segment.gd:265](../game/core/settlement/grant_segment.gd#L265)。
12. `attack_bonus` 已支持 `value_counter`:[damage_controller.gd:360](../game/core/damage_controller.gd#L360)。
13. GP 袋正常格面行值为3,普通战斗掉落均值3:[reward.tres:184](../game/data/tables/reward.tres#L184)、[shop.tres:7](../game/data/tables/shop.tres#L7)。
14. 现行专长表30行、牌库132;稀有度12/9/9/0:[adventure_feats_test.gd:465](../game/tests/unit/core/adventure_feats_test.gd#L465)。
15. 刷新真实入口回归已覆盖 RAP、AP保留、上限与刷新图零清图:[round_flow_skill_pool_test.gd:145](../game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd#L145)。
16. BattleOrchestrator 当前811行,类变量与文件长度均有 S13 登记值:[battle_orchestrator.gd:600](../game/core/battle_orchestrator.gd#L600)、[gdstyle_baseline.txt:9](../tools/gdstyle_baseline.txt#L9)。
17. `BattleFacts` 成员冻结15/15,不得新增刷新队列:[battle_facts.gd:10](../game/core/battle_facts.gd#L10)。
18. `RewardGrant` 类变量封闭15/15,不得新增 GP/回收队列:[reward_grant.gd:13](../game/core/reward_grant.gd#L13)。
## 四、设计契约
- 🔴 **C1 无专长基线零扰动**:挖格不产 RAP、刷新成本8、伤害事件旧结果、GP与清图路径均保持现行。
- 🔴 **C2 成功刷新才登记**:RAP不足、非Active、每轮第3次均不产生 `board_refreshed`;成本折扣只改变门槛与扣费,不改变拒绝判据。
- 🔴 **C3 全局受伤事件恰一次**:敌方攻击、危险格、错误封印资源格、地形伤害都经统一 `damage_taken`;同一伤害单据不得重复发射。
- 🔴 **C4 减伤固定不堆**:石层缓冲只看当轮是否至少成功刷新一次;同一轮多刷不提高减伤,减后仍走现行全局伤害下限。
- 🔴 **C5 不引入临时生命**:本批不新增HP护盾池、承伤库存或生命上限变化。
- 🔴 **C6 五段时序不变**:崩壁冲击走现有延迟伤害队列;坑道小憩在授予段;GP走现有战斗内缓冲。
- 🔴 **C7 矿道红利不绑清图奖励**:每次成功刷新固定1 GP;不读取旧图探索进度、不触发清图事件。
- 🔴 **C8 数值单源**:RAP产量、50%折扣、群伤、恢复、减伤、GP均从 `feats.tres` 读取;代码零玩法数值字面量。
- 🔴 **C9 词表封闭**:只新增 `refresh_request`、`board_refreshed` 两事件与 `grant_reserve_ap`、`apply_discount` 两效果。
- 🔴 **C10 结构预算**:BattleOrchestrator文件长度与类变量只降不升;新增流程件独立承载刷新生命周期。
- 🔴 **C11 反自动刷新**:仍只允许玩家入口发起,RAP满不自动刷新。
- 🔴 **C12 信息红线**:不揭示格、不改数字、不改棋盘生成分布。
- 🔴 **C13 拆分边界**:`BattleRefreshFlow` 只负责刷新可用性、扣费、换图与成功事实登记;七件效果的算法留在效果引擎、授予段与伤害控制器。
- 🔴 **C14 禁凑计数袋**:不把所有分支堆进一个无名 helper;中央受伤消费件只处理 `damage_taken` 命中组合。
## 五、行为结构
```mermaid
flowchart LR
A[付费挖格受理] -->|矿脉储能| B[RAP增加]
C[玩家请求刷新] -->|省力周转| D[折后成本]
D -->|成功| E[board_refreshed]
E --> F[崩壁冲击:延迟群伤单]
E --> G[蓄力周转:当轮攻击层数]
E --> H[石层缓冲:当轮全局减伤]
E --> I[坑道小憩:当轮累计恢复]
E --> J[矿道红利:1 GP]
K[敌方攻击/危险格/错误封印/地形] --> L[damage_taken 恰一次]
H --> L
```
## 六、范围边界
做:
1. 扩两事件、两效果;统一全局 `damage_taken` 发射与消费。
2. 实现两件基座、三件输出/两件生存/一件资源的数据行与消费点。
3. 拆 `BattleRefreshFlow`,保持编排器入口与唯一通道。
4. 新增七件专项测试,同步词表、专长池与刷新回归。
5. 能力清单入表。
不做:
1. 不做探索进度回收、清图奖励折算或旧图快照。
2. 不做临时生命值或独立护盾池。
3. 不新增 UI、素材、渲染基线、MC档位。
4. 不重构自爆流、不改 `增益状态`、不新增数据表。
5. 不抬高刷新成本,不做全板开,不返 RAP。
## 七、关联文档同步面
| 文档 | 关联点 | 同步义务 |
|---|---|---|
| [奇物](../design/gdd/systems/冒险循环/奇物.md) | 七件由设计声明改为实装;行数、稀有度与验收口径 | 实现批禁写;落 `reports/iter<N>-反哺点-<日期>.md`,用户裁决后文档批执行 |
| [行动经济](../design/gdd/systems/排雷循环/行动经济.md) | 两件基座由待实现改为在册 | 同上 |
| [战斗结算](../design/gdd/systems/战斗循环/战斗结算.md) | 全局受伤事件与刷新事实落码状态 | 同上 |
| [地图战斗资源](../design/gdd/systems/战斗循环/地图战斗资源.md) | 确认清图奖励不受矿道红利改变 | 同上 |
| [架构文档](../design/engineering/architecture/architecture.md) | 新增事件、效果、BattleRefreshFlow与API边界 | 同上 |
| [测试手册](../game/tests/README.md) | 换图系能力行 | 本写卡段已入表;实现批只核对名实一致 |
## 八、文件结构与职责
| 文件 | 职责 | 改动类型 | 验收行 |
|---|---|---|---|
| [effect_engine.gd](../game/core/effect_engine.gd) | 封闭词表扩两事件、两效果 | 行为改动 | V1 |
| [action_economy.gd](../game/core/action_economy.gd) | dig_paid产RAP、折价成本受理 | 行为改动 | V2/V3 |
| [battle_refresh_flow.gd](../game/core/settlement/battle_refresh_flow.gd) | 新建:刷新门、扣费、换图、成功事实 | 行为改动 | V4/V10 |
| [battle_orchestrator.gd](../game/core/battle_orchestrator.gd) | 刷新公开口单行转发;来源豁免 | 行为改动 | V4 |
| [damage_controller.gd](../game/core/damage_controller.gd) | 全局受伤消费、攻击层消费、延迟群伤目标解析 | 行为改动 | V5/V6/V7 |
| [grant_segment.gd](../game/core/settlement/grant_segment.gd) | 刷新命中施加、GP、当轮恢复、地形路径接入统一消费 | 行为改动 | V5/V6/V7/V9 |
| [reward_grant.gd](../game/core/reward_grant.gd) | 群伤单延迟入队 | 行为改动 | V6 |
| [feats.tres](../game/data/tables/feats.tres) | 七行专长与数值 | 行为改动 | V2/V3/V6/V7/V8/V9/V11 |
| [run_all.gd](../game/tests/run_all.gd) | 新测试登记 | 非行为改动 | V12 |
| [refresh_feats_test.gd](../game/tests/unit/battle_settlement/refresh_feats_test.gd) | 新建七件与全局受伤链路专项 | 非行为改动 | V2–V10 |
| [adventure_feats_test.gd](../game/tests/unit/core/adventure_feats_test.gd) | 专长行数、稀有度、牌库 | 非行为改动 | V11 |
| [forge_table_test.gd](../game/tests/unit/data_config/forge_table_test.gd) | 事件/效果闭集计数 | 非行为改动 | V1 |
| [effect_words_t1_test.gd](../game/tests/unit/core/effect_words_t1_test.gd) | 新词在册与出域拒绝 | 非行为改动 | V1 |
| [round_flow_skill_pool_test.gd](../game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | 无专长基线回归 | 非行为改动 | V12 |
| [README.md](../game/tests/README.md) | 能力清单 | 非行为改动 | V11 |
## 九、落位与可复用性预检
### 9.1 新增件
| 新件 | 复用结论 | 唯一落点 | 调用路径 |
|---|---|---|---|
| `BattleRefreshFlow` | 已查 `can_refresh`、`player_request_refresh`、`request_refresh`、商店代理、autoplay;没有同时负责成本查询与成功事实登记的现成件。不能复用 GrantSegment,因为刷新发生在授予段外;不能塞 BattleOrchestrator,因其文件长度贴顶 | `game/core/settlement/battle_refresh_flow.gd` | 呈现层→编排器转发→流程件→行动经济/装板壳/授予段 |
| `refresh_feats_test.gd` | 现有刷新测试只覆盖无专长基线;专长宿主没有经真实 `player_request_refresh` 驱动七件的夹具 | `game/tests/unit/battle_settlement/refresh_feats_test.gd` | `settle_batch`/`player_request_refresh`/轮末结算 |
| `_apply_hero_damage_effects` | 复用现有 `_enemy_shield_apply` 的组合算法,不新造事件消费件 | DamageController私有族 | 所有 `damage_taken` 发射点唯一消费 |
### 9.2 实现规范预算
| 触面 | 实测预算 | 结论 |
|---|---|---|
| `battle_refresh_flow.gd` | 公开函数2/类变量0 | ≤20/≤15,通过 |
| `battle_orchestrator.gd` | 类变量38不变;当前811行,删除旧刷新体并改两行转发,目标下降至少8行 | S13只降不升 |
| `action_economy.gd` | 公共方法15→15,仅扩参与局部状态 | ≤20,通过 |
| `grant_segment.gd` | 类变量3→3;公开方法1→2;新增私有施加函数≤4 | ≤20/≤15,通过 |
| `damage_controller.gd` | 公共方法不新增;现有 `_enemy_shield_apply` 改名为 `_apply_hero_damage_effects`,私有函数净增≤2 | S13逐键实测 |
| `reward_grant.gd` | 类变量15→15;新增一个公开延迟入口 | 禁增员,通过 |
| `feats.tres` | 同一专长表30→37 | 无第二数据源 |
## 十、任务卡
### T0 开工基线与预算实测(0.5d)
步骤:
- [ ] 读 `game/tests/.gates/ci-status.json`;CI红未修即停卡。
- [ ] 复核第三节 file:line;任一事实漂移先停卡核对。
- [ ] 跑开发期局部读数:`bash tools/gdstyle.sh game`。
- [ ] 记录 BattleOrchestrator行数/类变量、RewardGrant类变量、DamageController与GrantSegment方法数。
产出:所有结构预算基线;本卡无事实漂移。
### T1 词表扩签与计数(0.5d)
接口块:
- EVENTS 新增:`refresh_request`、`board_refreshed`
- EFFECT_KINDS 新增:`grant_reserve_ap`、`apply_discount`
步骤:
- [ ] 在 [effect_engine.gd:12](../game/core/effect_engine.gd#L12) 追加两事件,并同步 [effect_engine.gd:19](../game/core/effect_engine.gd#L19) 的 MINE_EVENTS。
- [ ] 在 [effect_engine.gd:24](../game/core/effect_engine.gd#L24) 追加两效果;不新增fact/op/DO_KEYS。
- [ ] 更新 [forge_table_test.gd:202](../game/tests/unit/data_config/forge_table_test.gd#L202):EVENTS20→22、EFFECT_KINDS32→34。
- [ ] 更新 [effect_words_t1_test.gd:14](../game/tests/unit/core/effect_words_t1_test.gd#L14):两事件与两效果在册;旧出域负例全部保留。
- [ ] 孤立验证:`effect_words_t1_test.gd`、`forge_table_test.gd`。
验收:词表封闭性无洞,旧词行为不变。
### T2 全局 `damage_taken` 统一(1.75d)
接口块:
```gdscript
func _emit_hero_damage_taken(amount: int, damage_kind: String, source: String, cell: Vector2i, ri: int) -> Dictionary
func _apply_hero_damage_effects(hits: Array, total: int) -> Dictionary
```
步骤:
- [ ] 把 [damage_controller.gd:507](../game/core/damage_controller.gd#L507) 的敌方受伤消费族改名为 `_apply_hero_damage_effects`,保留 shield、damage_reduce、ac_bonus、counter_add 的组合次序。
- [ ] 新增 `_emit_hero_damage_taken()`:payload必含 `kind/amount/source/cell/round/hp_ratio`,dispatch后调用唯一消费族,返回减后金额与命中束。
- [ ] 敌方攻击改走该方法,保持现有“掷点后、交付前”消费时点。
- [ ] `_resolve_board_flat_hero()` 在伤害下限前调用该方法;若单据 context 已标 `hero_damage_consumed=true` 则跳过,避免重复。
- [ ] 地形伤害改为调用该方法并把 `hero_damage_consumed=true` 写入单据 context,删除本地重复消费:[grant_segment.gd:174](../game/core/settlement/grant_segment.gd#L174)。
- [ ] 危险格与错误封印的负向主角直伤经 `board_flat_hero` 自动进入该方法,不新增第二发射点。
- [ ] 写专项函数:
- `test_all_hero_damage_paths_emit_once()`:注入临时 `counter_add damage_taken` 专长,从敌方攻击、危险格、错误封印、地形四种真实入口进入,每种单据恰好计数一次。
- `test_no_duplicate_damage_taken_event()`:同一伤害不得出现两次消费。
- [ ] 窄测:`refresh_feats|damage_controller|seal_polarity|round_flow` 红件最小复跑。
验收:`damage_taken` 字面语义覆盖所有主角承伤;无专长仍逐位保持原伤害。
### T3 两件基座(1d)
接口块:
```gdscript
func request_refresh(cost_override: int = -1) -> bool
static func _effective_refresh_cost(oc: BattleOrchestrator) -> int
```
步骤:
- [ ] 在 [action_economy.gd:101](../game/core/action_economy.gd#L101) 的 `dig_paid` 命中环累计 `grant_reserve_ap`,只在扣费受理后一次性调用 `grant_reserve_ap()`;连锁不二次调用。
- [ ] `refresh_request` 命中只认 `kind=="apply_discount" && mode=="refresh"`;取声明序第一个命中,按 `floor(base*cost_mult/100)` 计算。
- [ ] 新建 `BattleRefreshFlow`:公开 `can_refresh(oc)`、`execute(oc)`;execute只在ActionEconomy扣费成功后继续。
- [ ] 编排器两个公开方法改为单行转发,删除旧刷新体。
- [ ] 写:
- `test_reserve_on_accepted_dig()`:真实挖格入口,RAP+1,AP按表扣。
- `test_refresh_discount_gate_and_charge()`:真实刷新入口成本4,can_refresh同源判断。
- `test_double_base_stack()`:同时持两件时挖格产RAP、刷新扣4。
- `test_no_feats_baseline()`:旧成本与RAP来源逐值不变。
- [ ] 窄测 `--only=refresh_feats|round_flow`。
验收:两件基座改变行为但不破坏现行经济闸。
### T4 成功刷新事实与命中施加(1d)
接口块:
```gdscript
static func execute(oc: BattleOrchestrator) -> bool
func apply_board_refreshed(ri: int) -> void
func defer_ticket(t: Dictionary) -> void
```
步骤:
- [ ] `execute()` 成功序钉死:折价扣RAP→refresh_used+1→旧资源退役→旧板退役→装新板→GrantSegment `apply_board_refreshed(ri)`。
- [ ] install失败时返回false且零效果;成功事实只在新板就位后发射。
- [ ] `apply_board_refreshed()` dispatch一次 `board_refreshed`,payload `{round:ri}`,命中消费:
- `counter_add` → 既有单点落账。
- `grant_gp` → `reward_grant.escrow += value` 并发 `fx_grant_gp` 行。
- `open_ticket && channel=="blast"` → `DamageController.KIND_BOARD_BLAST`、`TARGET_ALL_ALIVE`、`source_class="fx_board_refresh"`,调用新增 `defer_ticket()`。
- [ ] 在 [battle_orchestrator.gd:35](../game/core/battle_orchestrator.gd#L35) 的来源豁免追加 `"fx_board_refresh"`。
- [ ] 写 `test_board_refresh_fact_only_on_success()`、`test_mine_profit_gp()`。
- [ ] 窄测 `--only=refresh_feats|round_flow`。
验收:拒绝刷新零事实;GP固定入账且每轮上限同时限制次数。
### T5 五件后续效果(1.75d)
接口块:
```gdscript
func _apply_hero_damage_effects(hits: Array, total: int) -> Dictionary
func _round_end_apply(ri: int) -> void
```
步骤:
- [ ] 崩壁冲击已在T4延迟开单;确认授予段不结账、战斗段末按存活目标结账,最后敌死亡仍由判定段胜。
- [ ] 石层缓冲用 `round_swap_guard`:成功刷新时+1;`damage_taken` 条件≥1时 `damage_reduce value=2`,不清层、不随伤害段变化。round桶轮始自然清空。
- [ ] 坑道小憩用 `round_swap_rest`:成功刷新累计至上限3;`round_end` 命中 `heal value_counter=round_swap_rest`,调用 `units.heal()`封顶,满血发未生效行;随后同事件行 `counter_add set=0`。
- [ ] 蓄力周转用战斗桶 `swap_charge_layers`:成功刷新至上限3;`attack_commit` 消费全部层数,随后清零;战斗新建自然清空。
- [ ] 写:
- `test_stone_mitigation_all_damage()`
- `test_rest_rotation_cap_and_clear()`
- `test_charge_layers_consumed_same_round()`
- `test_blast_defers_to_combat_segment()`
- [ ] 窄测 `--only=refresh_feats|damage_controller|seal_polarity`。
验收:五件只在成功刷新链生效;固定减伤不堆、恢复累计、GP定额。
### T6 七件数据行与牌库计数(1d)
接口块:`feats.tres` 行键沿用 `feat_id/display_name/rarity/cap/desc/triggers`,不扩schema。
步骤:
- [ ] 追加七行,trigger结构固定:
| feat_id | triggers |
|---|---|
| `vein_reserve` | `dig_paid → grant_reserve_ap value=1` |
| `easy_swap` | `refresh_request → apply_discount mode=refresh cost_mult=50` |
| `wall_blast` | `board_refreshed → open_ticket channel=blast value=2` |
| `charge_rotation` | `board_refreshed when counter<3 → counter_add swap_charge_layers1`;`attack_commit when≥1 → attack_bonus value_counter=swap_charge_layers`;`attack_commit when≥1 → set0` |
| `stone_mitigation` | `board_refreshed → counter_add round_swap_guard1`;`damage_taken when≥1 → damage_reduce value=2` |
| `tunnel_rest` | `board_refreshed when<3 → counter_add round_swap_rest1`;`round_end when≥1 → heal value_counter=round_swap_rest`;`round_end when≥1 → set0` |
| `mine_profit` | `board_refreshed → grant_gp value=1` |
- [ ] desc分别写:挖格储能、刷新半价、刷后群伤、刷后蓄力、当轮减伤、当轮恢复、刷后GP;禁“雷/旗”字面。
- [ ] 行数30→37;稀有度12/9/9/0→14/13/10/0;relic令牌30→37;牌库132→139。
- [ ] 同步 [adventure_feats_test.gd:465](../game/tests/unit/core/adventure_feats_test.gd#L465)、[:476](../game/tests/unit/core/adventure_feats_test.gd#L476),旧30行断言只按实数换代,不降低 validate/禁词检查。
- [ ] 写 `test_seven_feats_validate_and_pool()`。
验收:七件全部数据驱动,跨表计数器引用闭合。
### T7 回归与能力名实核对(0.75d)
步骤:
- [ ] 核对 [README.md](../game/tests/README.md) 五类换图能力行,文件名与测试函数逐字一致。
- [ ] `round_flow_skill_pool_test.gd:_smoke_active_refresh()` 无专长基线断言不删除、不放宽。
- [ ] `seal_polarity_test.gd:_smoke_refresh_provenance_guard()` 继续证明刷新图零清图奖励。
- [ ] 实跑关联窄测:
`--only=refresh_feats|adventure_feats|round_flow|seal_polarity|damage_controller|effect_words|forge_table|config_db`
- [ ] 出红只按红件最小复跑,波内不跑全量。
验收:新增与旧专长并存;无UI/素材改动,不新增golden。
### T8 批末出口与产出物(1d)
步骤:
- [ ] 里程碑出口跑 `bash tools/run_all_gates.sh --code`。
- [ ] 回执必须 `all_green=true`、`dirty_game=false`。
- [ ] 独立验收审计报告 `reports/iter<N>-验收审计-<日期>.md` 未过禁合并。
- [ ] 轻闸全绿、合并前写齐:
1. `reports/iter<N>-实现报告-<日期>.md`
2. `reports/iter<N>-代拍板追认单-<日期>.md`
3. `reports/iter<N>-反哺点-<日期>.md`
- [ ] 实现报告列七卡状态、V1–V12读数、S13前后数、旧damage_taken专长覆盖面变化、玩家主动刷新目检项。
- [ ] 反哺点逐条覆盖第七节六个目标。
完成定义:三份产出物齐全+代码域闸全绿+验收审计通过。
## 十一、验证闸:改动—验收对照表
| 需求点 | 改动(文件/行为) | 验收测试 | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|
| V1 词表扩签 | effect_engine;forge/effect_words | `effect_words_t1_test.gd:_init`;`forge_table_test.gd:_test_effect_kinds_closed` | 单元 | validate与整表加载 | EVENTS=22、EFFECT_KINDS=34、出域仍拒 | 新增:换图系刷新链路与效果词表接线 |
| V2 矿脉储能 | action_economy dig_paid消费 | `refresh_feats_test.gd:test_reserve_on_accepted_dig` | 接口 | `settle_batch(dig)` | RAP恰+1、AP扣值与板资源不变 | 新增:换图基座:挖格产 RAP 与刷新折扣 |
| V3 省力周转 | BattleRefreshFlow成本门 | `test_refresh_discount_gate_and_charge`;`test_double_base_stack` | 接口 | `player_request_refresh()` | base8→扣4;can与执行同源;两件都生效 | 新增:换图基座:挖格产 RAP 与刷新折扣 |
| V4 成功事实 | BattleRefreshFlow install尾点 | `test_board_refresh_fact_only_on_success` | 集成 | `player_request_refresh()` | RAP不足/第3次上限/非Active均board_changed与效果零变化 | 新增:换图系刷新链路与效果词表接线 |
| V5 全局受伤 | damage_controller统一发射消费 | `test_all_hero_damage_paths_emit_once`;`test_no_duplicate_damage_taken_event` | 集成 | 敌方攻击、dig danger、seal wrong、terrain触发 | 每路径一次事件、无重复扣血或重复counter | 新增:换图生存:全局固定减伤与轮末恢复 |
| V6 石层缓冲 | round_swap_guard+damage_reduce | `test_stone_mitigation_all_damage` | 集成 | 刷新→四种真实受伤路径 | 每种伤害最终金额减2;多刷不增加;下限1保持 | 新增:换图生存:全局固定减伤与轮末恢复 |
| V7 崩壁冲击 | open_ticket延迟队列 | `test_blast_defers_to_combat_segment` | 集成 | 刷新→停手 | 授予段无伤害行、战斗段末全体存活敌各受伤 | 新增:换图输出:刷新群伤与蓄力攻击 |
| V8 蓄力周转 | counter+attack_bonus value_counter | `test_charge_layers_consumed_same_round` | 接口 | 两次刷新→本轮主角攻击 | 攻击加2层、随后0;战斗新建0;上限3 | 新增:换图输出:刷新群伤与蓄力攻击 |
| V9 坑道小憩 | round_end heal消费 | `test_rest_rotation_cap_and_clear` | 集成 | 三次刷新→停手 | 回复3、上限3、层清0、hp≤max;满血未生效 | 新增:换图生存:全局固定减伤与轮末恢复 |
| V10 矿道红利 | board_refreshed grant_gp | `test_mine_profit_gp` | 集成 | `player_request_refresh()` | escrow每次成功刷新+1;拒绝零变化;fx_grant_gp行在案 | 新增:换图资源:刷新固定 GP 收益 |
| V11 数据与池 | feats七行;adventure计数 | `test_seven_feats_validate_and_pool`;adventure_feats | 单元 | ConfigDB整表与冒险牌库 | rows37、relic37、14/13/10、deck139、validate空、禁词0 | 既有:专长(奇物)授予与凭证保底 |
| V12 无专长回归 | round_flow/seal旧基线 | `_smoke_active_refresh`;`_smoke_refresh_provenance_guard` | 单元+集成 | 现行刷新/清图入口 | RAP8、AP保留、上限2、刷新图零清图、五段与回放不降 | 既有:停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励) |
## 十二、测试方式与削弱申报
自动验证:
- V1–V12均从真实公共入口进入;计数器、事件行、escrow、hp与伤害行都是可观测提交面。
- 不造变异或回退hunk证明;临时专长夹具通过现行ConfigDB注入,属正常数据配置路径。
真人目检:
- 登记一项:玩家持基座与收益件后,是否主动规划刷新而非只在坏图刷新。写入实现报告,不阻塞合回。
测试削弱申报:
- 允许把行数、稀有度、事件/效果计数从旧实数改到新实数。
- 允许旧 `damage_taken` 行为测试增加危险格/错误封印路径,以体现用户已裁的全局语义;原敌方攻击与地形断言不得删除或放宽。
- 禁止删除RAP8基线、每轮上限2、AP保留、刷新图零清图、伤害下限、五段序、同种子回放等断言。
## 十三、估时与切分预案
| T0 | T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | 合计 |
|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| 0.5 | 0.5 | 1.75 | 1 | 1 | 1.75 | 1 | 0.75 | 1 | **8d** |
浮动±0.5d。
切分预案:段一=T0–T4+T6(基座、全局受伤事件、刷新事实、数据接线,约4.75d);段二=T5+T7–T8(五件收益兑现与回归出口,约3.5d)。两段各自独立可测、独立提交。
## 十四、异常分级
L1(自救一次):编译错误、签名拼写、manifest登记遗漏。
L2(记录继续;同红三次升L3):既有无关文档域红;局部测试文案漂移。
L3(停卡写报告):
- CI红未修。
- 同一伤害出现两次 `damage_taken`,或某路径完全不出现。
- 石层缓冲使伤害归零、突破下限或改变治疗语义。
- 刷新生效出现清图奖励/探索进度耦合。
- BattleOrchestrator或RewardGrant结构预算被迫上升。
- 机检键与本卡契约互斥;禁为凑绿保留无效成员。
- 任何需求点找不到真实入口与特有可观测提交面。
## 十五、代拍板白名单
允许实现期自拍:
1. 私有函数换行、局部变量名、内部注释位置。
2. 测试断言消息措辞与helper顺序。
3. 错误扫描暴露的纯格式修复。
4. 不改变语义的前提下复用现有 `units.heal()`、counter API与GP行格式。
超清单必停卡呈报:
- 数值、稀有度、cap、counter_id、事件/效果名、来源类别、文件职责、验收入口。
- 任何把清图奖励、探索进度、临时生命重新引入本批的改法。
## 十六、零待裁点
七件名称、收益对象、全局受伤范围、叠加规则、时序、数值与稀有度均由最终确认单锁定。本卡零待裁点,开工门=U0。
## 十七、开工门
1. 由 iter-plan 编入迭代后,`/iter` 才可开工;无迭代外执行通道。
2. 开工前四份GDD凭据在待办清单闭合、CI绿、T0实测无漂移。
3. 本卡预计8个工作日,符合迭代硬线。
@@ -0,0 +1,195 @@
# 需求卡片——测速循环批(tune_sim_controller 循环内 await 真实消除)
> **版本状态**:v1.1 定稿——零待裁点(2026-10-30 停卡重裁:实现段 T0 三重证据定谳 v1.0 的「潜伏红」前提已被迭代 41 测试债批消化〔215da315——运行态收尾归还 run〕,按停卡呈报建议案剥离已申报行为改动 V3、T0 义务改现状复核、夹具申报差异两处减一处;2026-10-29 原四点裁决中②作废,①③④仍有效——裁决记录见第 15 节)。
> **接收方**:待办清单条目「零豁免五键清偿批——S13 余量五键真修四卡(2026-10-29 登记,来源=会话提及)」——本卡为其四卡中的测速循环批卡。
> **来源**:reports/设计-测速循环去await-2026-10-29.md(2026-10-29 设计闭合;§2.3 潜伏红推演与 §10 待裁点 1 的「还原」申报已随停卡重裁作废,§3–§7 形态与等价性论证仍有效)。**唯一事实源**:`reports/设计-测速循环去await-2026-10-29.md` + `design/engineering/architecture/architecture.md` v1.25「测试与质量域」S13 测量面豁免注销句(2026-10-29 零豁免裁:原循环内 `await` 豁免〔2026-10-27〕注销——tune_sim_controller 改 process_frame 信号常驻连接+步进状态机,每帧一局、帧相位逐点对齐;注销对象是 await 豁免、不依赖行为改动申报,句保持;句内「随批申报行为改动一处」半句已失效,随实现批现状化时一并修正——见第 6 节)+ 停卡呈报三重证据(iter44 worktree 分支 `reports/iter44-实现报告-2026-10-30.md` §5——重裁依据)。
## 1. 目标与完成定义
这批做什么:把 S13 静态质量闸(gdstyle)在册裸键 `game/presentation/tune_sim_controller.gd|quality/await-in-loop` 真实消除——`run(n)` 的 for 循环内 `await process_frame` 改为 process_frame 信号常驻连接+步进状态机(每帧回调执行一局、步尽次帧收尾断连),`run()` 只留函数级一次 `await _batch_finished` 保持协程身份;改后全文件零 for/while,该键从文本形态根除、此文件对这个键结构性免疫。随批执行一项已裁事项:新增双实现对拍套件,把帧相位等价论证变成当轮真实执行证据。**本批零行为改动**——收尾置假已在 run() 尾每批一次执行(迭代 41 测试债批 215da315 归还后的现行形态),本批收尾四语句同帧同相位迁入 `_finish_batch()`,语句集与语义零变化(第 3、4 节)。
什么状态算完:
1. `tune_sim_controller.gd` 全文件零循环、`await` 仅 `run()` 函数级一处;`python tools/gdstyle_gate.py` 对账该裸键消失(消失键不参与红判定)、无基线外新键。
2. 测量语义逐点等价(第 4 节四判据)经双实现对拍在当轮真实跑批中证明——五面对拍全等;wd8 三断言与 ui_tune_probe ⑤ 零改动保持绿。
3. diff 中行为改动零处——收尾四语句迁移为非行为改动(同帧同相位、语义零变化,对照表 V3 行),无任何行为漂移。
4. 脚本错误中断路径差异作为声明缺口写入实现报告(禁静默)。
5. 批末代码域闸全绿;实现报告与反哺点清单写齐方算收口。
## 2. 设计事实源
设计依据唯一出处=`reports/设计-测速循环去await-2026-10-29.md`(2026-10-29 设计闭合,零待裁点):§3 形态评估(process_frame 信号步进状态机采纳)、§4 推荐案(伪代码结构)、§5 等价性证明清单(四判据+已声明缺口)、§6 键账预期——以上各节不受前提消化影响、仍是本卡依据;该文档 §2.3 潜伏红推演与 §10 待裁点 1「还原」申报已作废(前提消化证据见停卡呈报)。架构面凭据=`design/engineering/architecture/architecture.md` v1.25「测试与质量域」S13 测量面豁免注销句(2026-10-29 零豁免裁落文)——豁免注销句保持,句内行为改动申报半句随实现批现状化修正(第 6 节同步面)。前提消化与重裁凭据=iter44 worktree 分支 `reports/iter44-实现报告-2026-10-30.md` §5 停卡呈报(git 历史证据 215da315+T0 两面实跑绿)。待办清单条目「零豁免五键清偿批」状态=已写卡+已编迭代 44(本卡 v1.1 重裁段产出)。
## 3. 现状事实(代码级,2026-10-30 实查;开工 T0 按当轮施工基面重锚——行号基线为本日 master)
- `game/presentation/tune_sim_controller.gd`(全件 68 行):`run(n)` 在 :36-54——:37 置运行态真、:40-42 开共享 JSONL 日志会话、:43 `panel.set_sim_running(true)`、:44 进度 0/n、:45 建 stats 六键束、:46 取自动策略驱动、:47-50 for 循环体(`_run_one_level` → 进度串 → :50 `await panel.get_tree().process_frame`——命中键的语句)、:51-54 收尾四语句(`close_session()` :51 → 报告注入 :52 → `panel.set_sim_running(false)` :53 → `_running = false` :54)——**收尾置假在 run() 尾、for 循环外、每批恰一次**。`_run_one_level` 在 :57 注释/:58-68(5 参),尾部 :66-68 为败局节点记账(defeat 分支)——非置假。类成员 5(db/panel/_base_hp/_driver/_running)+常量 2(:7 SEED_BASE=4200、:8 PlaytestLogger preload);头注 4 行。
- 前提消化定谳(v1.0「静态矛盾推演」作废依据):git 历史——迭代 41 测试债批提交 215da315(2026-10-29 合回)提交信息明载「两个产品回归随批修(……tune_sim 运行态收尾归还 run)」,收尾置假两行由该批归还 run() 尾并随该批申报;v1.0 成文(2026-10-29)与该批合回同日,其「现状考证」被上游批施工消化变质(读法 A;读法 B「考证当时即误读」与 215da315 提交信息矛盾,停卡呈报 §5 不采)。T0 实跑三重证据(iter44 worktree,停卡呈报 §5):wd8 三断言(现位 :145-147)全 PASS(套件 18 checks 全绿);ui_tune_probe 全链路 PASS(⑤ 守卫环 1800 帧预算内退出+输出四串在)。**潜伏红不存在**——两面现行全绿,「零改动转绿」改「零改动保持绿」。
- 断言面一〔wd8〕`game/tests/unit/presentation/standee_fx_sweep_test.gd:139-147`:FakePanel(内类 Node,:20-40——setter 面并入树供 get_tree)+FakeDriver(:44-55——`run_level` 回调内读 `ctrl.is_running()` 逐局记 `seen`);:142 直构控制器(字典夹具 db)、:144 `await ctrl.run(2)`(run 必须可等待、挂起至整批完成的契约)、:145-147 三断言——`drv.seen == [true, true]`/收尾 `not ctrl.is_running() and not panel.running`/报告非空。套件在 run_all.gd manifest 在册(res://tests/unit/presentation/standee_fx_sweep_test.gd,expect 0,manifest :188)。
- 断言面二〔探针 ⑤〕`game/probe/ui_tune_probe.gd:63-83`::65-66 设滑条与 N=10、:67 `press_simulate_for_probe()`(真实按钮 pressed 信号链)、:68-71 守卫环(`while main.ui.modals.debug_overlay.is_running()` 逐帧等——SIM_GUARD_FRAMES=1800 在 :16)、:72-73 预算内未完成判红、:74-83 输出四串断言(关卡完成率/关卡完成剩余 HP 均〔口径换代 V7〕/败北节点/N=10)。面板语义面 `debug_overlay.gd`:`set_sim_running` :81-83 → `_sync_sim_button` :107-110(模拟与两层调试两钮禁用+「模拟中…」文案——浮窗运行态贯穿整批的可见面)、`is_running` :77-79。
- 调用面 `game/presentation/main_debug_flow.gd`:`_on_tune_simulate` :72-77——守卫(活跃结算/冒险早退)后 :77 `root._tune_sim.run(n)` **不 await 调用**(返回的协程态丢弃——新形态必须保持此调用形态可用、零改动);`_start_debug_levels` :84 守卫读 `root._tune_sim.is_running()`(两层调试入口互查守卫)。
- 键账:`tools/gdstyle_baseline.txt:15` = 该文件唯一在册键(await-in-loop 裸键,基线 16 键之一)。S13 扫描面=产品码三目录(game/core+data+presentation——probe/sim/tests 不在面,`game/tests/static_scan.sh` S13 头注在案);S8 文件集=**十文件**显式清单(static_scan.sh :317 现行实查——不含本件);S9/S10 扫描面同不含 game/tests/。
- 产品码 process_frame 订阅面(T0 实查项):本件 :50(循环内 await)+`settlement_fx_view.gd:94`(play_damage_number 内一次性 await 等文本度量——战斗结算呈现期,与模拟批不并存)。现库无「run 途中新增订阅者」场景。
- 报告渲染面 `game/presentation/hud_presenter.gd` :197-218 `tune_sim_report`(静态函数,零触碰计划):空数组 `_mean`/`_p90` 双守卫(:221-227/:230-235)、完成率分母 `maxi(1, n)`(:206)——n=0 退化路径渲染安全(本批对拍 n=0 案的前提,实查在案)。
- 能力表现状:`game/tests/README.md` 能力表「调参测速批量模拟(逐帧步进与统计读数)」行已入表(:131——写卡段 v1.0 同批义务,真实入口=批量模拟入口,代表用例=对拍套件);断言宿主 wd8 套件锚既有行「站桩敌人呈现与结算演出落点」(:130)。
## 4. 设计契约(违反即返工的红线条款,标 🔴 触碰=L3 停机呈报)
- 🔴 **测量语义逐点等价**(设计文档 §5 四判据逐条落卡,双实现对拍为当轮执行证据):
① 步数与帧数一一对应不变——每局恰占一帧:第 i 局恒在调用点同步段(i=0)或第 i 帧信号发射点(i≥1),收尾恒在第 n 帧发射点,n=0 退化路径两形态同为同步收尾零帧;
② 每步工作量与执行相位不变——步进体=同一 `_run_one_level`+同一进度语句;执行点同为 emit process_frame 帧首段(await 恢复点与 connect 回调在引擎内同处一列连接、同一 emit 内被同步调用——设计文档 §4.2 逐步对齐表);
③ 终止条件与终止时读数不变——终止=计数走满 n;close_session/运行态复位/报告注入在同帧执行;
④ 输出逐字不变——报告=未触碰的 `HudPresenter.tune_sim_report`;stats 是 (种子, atk_pct, hp_pct) 的纯函数、帧时点不进测量值;JSONL 由未触碰的自动策略逐行写、close 时点同帧。
违反即返工;等价性对拍失败=L3 停机呈报。
- 🔴 **行为改动零处(收尾语义零变化)**:收尾置假已在 run() 尾每批一次执行(:53-54,215da315 归还后的现行形态)——本批 `_run_one_level`(:58-68)零触碰、运行态复位语义零变化;收尾四语句(:51-54)迁入 `_finish_batch()` 为同帧同相位语句迁移(判据③承载——非行为改动,对照表 V3 行)。禁任何行为漂移——`_on_tune_simulate` 不加运行态早退守卫(设计文档待裁点 3 备选否决)、发布态零触碰、探针①–④⑥–⑩ 段零触碰、`HudPresenter`/`PlaytestLogger`/`sim/level_autoplay.gd`/`debug_overlay.gd` 零触碰。
- 🔴 **脚本错误中断路径差异=声明缺口**(设计文档 §5 缺口 1,接受并文档化):GDScript 无错误陷阱 API、差异不可消除——旧形态步进体内脚本错误=协程死亡静默停摆(运行态永真、报告永不落);新形态=计数未推进、次帧重试同一步(确定性错误逐帧重试,探针 1800 帧守卫可抓;生产态=持续重试的可见坏,与旧「静默卡死」同级不同形)。实现报告文档化义务(T3),禁静默、禁造第三条路(错误陷阱/守卫兜底均已否决);测量批遇错本就整批作废,两形态都不产出可用读数。
- 🔴 **S8 结算路径 await 零命中面不触碰**:本件不在 S8 文件集(十文件显式清单实查不含本件);本批零触碰 S8 文件集与一切闸件。
- 🔴 **拆分边界纪律**(判据权威=code-conventions §1.1,卡内执行条款):本批零拆出单元——步进状态机收编本件(职责语句与不拆理由见第 8 节);①拆出单元须有可陈述职责语句与单一变化原因;②命不出名字、只能说「剩下的成员」的打包=凑计数袋,禁;③「过阈值」只作发现信号——本批键消除靠形态根除、不靠拆件凑数;④按纪律拆不出内聚单元→停卡呈报;⑤禁沿阈值轴拆。
- 🔴 **RNG 与种子零触碰**:SEED_BASE=4200 与逐局种子切分(SEED_BASE+i)零变化——stats 是种子纯函数是等价性论证的前提。
- 🔴 **注释纪律**:新增头注/注释零变更叙事(「由 await 改为信号」类句禁——沿革归 git);S11 注释禁词由术语表解析供给,新增注释用白话描述现行形态与信号契约。
## 5. 范围边界
做什么:第 3 节四件事——T0 现状复核(两面实跑应绿)、`tune_sim_controller.gd` 步进状态机改造(收尾语义零变化)、双实现对拍套件、键账对账与收口。
不做什么(明示去向):
- 零豁免其余四键(main.gd 类变量 49、battle_orchestrator 类变量 38、board_cell_unit 30、standee_body 19)——各归组合根注册面批/战斗编排归约批/呈现层状态件批(同条目四卡,待办清单在册)。
- `tools/gdstyle_baseline.txt` 禁实现批中途刷新——批末由用户人工重生成(第 6 节)。
- `_on_tune_simulate` 运行态早退守卫——不收件(+1 行为面、system_test 前置断言随之需复核,代价大于收益,设计文档待裁点 3 否决)。
- `design/` 整个目录、两份根流程清单——实现批禁写;该改的点列反哺点清单。`game/tests/README.md`:测量面专属能力行已随本卡 v1.0 写卡段同批入表(「调参测速批量模拟」行),实现批禁写红线不变。
- 探针其余段与套件其余断言族(standee_fx_sweep 的 wd2/wd4/wd5/wd12 面)零触碰;`architecture.md` 其余豁免条目(battle_orchestrator 第三条注销句现状化归战斗编排归约批反哺点)零触碰。
## 6. 关联文档同步面
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|---|---|---|
| design/engineering/architecture/architecture.md | 「测试与质量域」S13 测量面豁免注销句(v1.25——「设计闭合、实现批建件」批后现状化;句保持——注销对象是 await 豁免、不依赖行为改动申报;句内「随批申报行为改动一处(收尾置假还原)」半句已失效〔前提被迭代 41 消化、申报已剥离〕,现状化时一并修正为收尾现行陈述) | 设计权威文档——实现批禁直改;逐条列进 `reports/iter44-反哺点-2026-10-30.md`(豁免注销句现状化,该清单 #6 已登记暂停点、随本卡 v1.1 恢复),用户裁决后归文档批落文 |
| game/tests/README.md | 能力表——测量面专属能力行「调参测速批量模拟(逐帧步进与统计读数)」已随本卡写卡段同批入表(真实入口=批量模拟入口,代表用例=本卡对拍套件);manifest 计数行随套件增减浮动(自助声明,零义务) | 机械面文档——实现批禁写;入表已完成于写卡段(本卡 v1.0 同批),实现期零改动 |
| tools/gdstyle_baseline.txt | tune_sim await-in-loop 裸键(:15,该文件唯一在册键)随键消失成死键 | 重生成为人工专用——批末由用户执行 `python tools/gdstyle_gate.py --refresh`(键数 16→15,降幅 <10% 不需 --force);禁实现批中途刷新 |
| design/gdd/(全目录) | 无关联文档——测量工具私有机制改造,不改游戏规则与玩家可见行为(tune_sim 全链路发布态不可达,设计文档 §7.3) | 无修订预期;契约句被触碰=停卡呈报走反哺点通道 |
## 7. 卡前文件结构图(本批新建/修改文件全列)
| 文件 | 新增/复用 | 职责 | 行为改动 |
|---|---|---|---|
| game/presentation/tune_sim_controller.gd | 复用(改造) | 批量模拟控制器——process_frame 信号常驻连接+步进状态机;收尾单点 `_finish_batch`(四语句同帧同相位迁入);全文件零循环 | 非行为改动(结构改形——收尾语义零变化,零漂移由对拍证;回归锚面=对照表 V3 行) |
| game/tests/unit/presentation/tune_sim_batch_parity_test.gd | 新增 | 双实现对拍配对套件——现行 await 形态夹具(内类,收尾在 run() 尾的现行形态)+五面对拍断言 | 非行为改动(测试面) |
| game/tests/run_all.gd | 复用(改造) | manifest 增一行(新套件注册,expect 0) | 非行为改动(测试基建) |
结构图与第 8 节互指:新增件一件(对拍套件),其「为什么不能复用/落点唯一」理由见第 8 节;其余两行均为既有文件改造。行为改动列全为零——每行的验收落点见对照表 V1/V2/V3 行。
## 8. 落位与可复用性预检
- **步进状态机不引入新文件(本件内改造)——为什么不拆新件**:单一职责已在件内——`tune_sim_controller` 的职责就是批量模拟控制,步进机制(每帧一局的节律与帧相位承载)是它的私有运行机制,随批生灭(run 期活成员,批末失效下次 run 重写),没有独立于批量模拟的存在意义;拆出「步进机」件命不出独立职责语句=凑计数袋(code-conventions §1.1 判据②④禁)。复用面核查:全仓 grep 无信号步进机同职责实现可复用;其余四形态(SceneTreeTimer 链/Timer 节点/递归协程/线程)已被设计文档 §3 逐一否决(相位不同或键不消)。
- **对拍套件落 `game/tests/unit/presentation/` 的唯一性与命名**:落点唯一——与断言宿主 wd8 同目录同协议(extends SceneTree+Kit 收尾),单元层承载;命名 `tune_sim_batch_parity_test.gd`(领域命名、无 ID 前缀);grep 同名与同义标识符实查无第二处。**遗留 await 形态副本全仓只许此一份**——产品码禁留第二实现(旧形态只活在测试夹具内);tests/ 不在 S13/S8/S9/S10 扫描面(扫描面=产品码三目录与 S8 文件集,实查在案),夹具内循环 await 合法在册。
- **复用面**:夹具的假面板/假驱动复用 wd8 的形态先例(FakePanel/FakeDriver 同形——代码在各套件内自建,不跨套件共享 helper,禁散落);收尾复用 `test_kit.gd` 的 Kit.finish(零断言专用退出码协议)。
- **分层与交互**:控制器保持 RefCounted、零 Node 入树;process_frame 连接经 `panel.get_tree()` 取树(与现状同一出口);对拍套件 `--script` 模式零 autoload 依赖(db 用字典夹具,同 wd8 :142 先例);零新增跨层出口、零数值表读取、零随机。
## 9. 实现规范预算(gdstyle 默认阈值对账:公开方法 ≤20、类变量 ≤15、单函数分支 ≤8、返回 ≤6、参数 ≤5、局部 ≤10、函数 ≤50 行)
本批零新增产品码文件(改造 `tune_sim_controller.gd` 本件)——改造后该文件键账预期(设计文档 §6 逐规则落卡):
| 规则 | 预期 | 依据 |
|---|---|---|
| quality/await-in-loop(裸键,本案标的) | **消除** | 全文件零 for/while;await 仅 `run()` 函数级一处(`await _batch_finished`)——结构性免疫 |
| quality/allocation-in-loop | 不命中 | 文件零循环;`PlaytestLogger.new()` 在 run 顶层(现状即如此) |
| max-function-length ≤50 | 达标 | run 约 22 行、`_step_frame` 约 12 行、`_finish_batch` 约 6 行、`_run_one_level` 现行 11 行零触碰 |
| max-branches ≤8 | 达标 | `_step_frame` 一个 if/else;run 一个 if/else |
| max-local-variables ≤10 | 达标 | run 局部 3(logger/stats/autoplay——余者转成员);`_step_frame` 局部 1 |
| max-returns ≤6 | 达标 | 全 void |
| max-parameters ≤5 | 临界持平 | `_run_one_level` 5 参逐字保持;两个新函数 0 参 |
| max-class-variables ≤15 | 达标 | 现 5+新增 6=11(`_batch_finished` 信号若被工具计入成员则 12——两值均达标;成员皆 run 期活承载,非凑数袋) |
存量触面一查:基线实查该文件唯一在册键=await-in-loop 裸键(`tools/gdstyle_baseline.txt:15`)——S13 键较登记值只降不升,裸键消失即清偿;死键由批末用户人工 `--refresh` 自然清去(16→15,降幅 <10% 不需 --force)。新增测试件不在 S13/S9/S10/S8 扫描面(第 3 节实查在案);仍按 test-conventions §6 全类型标注自律(参数/返回值/容器全带标注)。
## 10. 禁为机检文本保留无效成员(重构类卡必填条款)
禁止为迎合 S13 机检消息文本(含计数)保留或新增无效成员/别名;本批新增 6 个 run 期活成员皆为 `_step_frame`/`_finish_batch` 读写的承载(`_finished` 若实现期确认零读者,可删——见第 14 节白名单 3,成员 11→10 键账只降不升);机检键与卡片契约互斥时,实现段一律停卡呈报,不得自造第三条路。
## 11. 任务卡 T0..T3(估时为区间;工作量限制只属迭代层——合计 1–1.5 工作日)
**T0 现状复核与读数重锚(0.25 工作日)**
- [ ] 实跑 wd8 单套件(开发期最窄确认,不作验收凭据):`godot --headless --path game --script res://tests/run_all.gd -- --only=standee_fx_sweep`——记录 :145-147 三断言实况,**应绿**(绿=前提已被迭代 41 测试债批消化,按无行为改动形态开工)。套件若因在册无关债先红于 wd8 断言,先核该债修复状态、不得借本批顺手修非申报面——读数以「wd8 断言可达性」为准呈报(L2 记录)
- [ ] 实跑 ui_tune_probe 单探针:`…console.exe --headless --path game res://probe/ui_tune_probe.tscn`——记录 ⑤ 守卫环与输出四串实况,**应在**
- [ ] 判读:两面实跑全绿=重裁前提成立开工;任一面红=现状与重裁前提失配(迭代 41 修复被回退或新潜伏红)——L3 停卡呈报(第 13 节)
- [ ] 按当轮施工基面重锚第 3 节全部 file:line 与成员计数;漂移处就地改卡并记录(本卡行号基线=2026-10-30 master;同迭代卡①改址过 main_debug_flow/ui_tune_probe——两件相关行实测未漂移,仍以 T0 重锚为准)
- [ ] 实查产品码 process_frame 订阅面——预期两处(本件 :50+settlement_fx_view.gd:94);出现 run 途中新增订阅者场景=L2 记录+代码评审注记(设计文档 §5 缺口 2)
产出:现状复核读数(两面绿——前提消化链条的当轮证据,独立验收审计要核对);重锚后行号。
**T1 步进状态机改造(0.25–0.5 工作日)**
- [ ] `tune_sim_controller.gd` 头注增 `_batch_finished` 信号契约一行(code-conventions §2.1——每信号一行:发射时点=报告注入与运行态复位之后;消费方=run() 自身收尾放行,外部 `await run(n)` 经此放行;现 4 行→5 行;禁变更叙事句)
- [ ] 增 `signal _batch_finished` 与 6 个 run 期活成员(名字按设计文档 §4.1 逐字:`_sim_n: int`/`_sim_i: int`/`_sim_stats: Dictionary`/`_sim_autoplay`/`_sim_logger`/`_finished: bool`)
- [ ] `run(n)` 改形:setup 段(:37-46 语句逐字保持);n>0 分支——`is_connected` 守卫 connect `panel.get_tree().process_frame`(恰好连接一次,迭代 39 先例)→ `_step_frame()` 首步同步执行(与 await 形态「第 0 局在调用栈内跑完」对齐)→ `await _batch_finished`(函数级单次 await——键消除落点);n=0 分支直调 `_finish_batch()`(循环零进同步收尾,与 await 形态一致不挂起)
- [ ] 新增 `_step_frame() -> void`(0 参):`_sim_i < _sim_n` 分支——`_run_one_level(_sim_autoplay, _sim_stats, _sim_i, panel, _sim_logger)`+`panel.set_sim_progress("模拟中:%d/%d …" % [_sim_i + 1, _sim_n])`(串逐字保持现码 :49)+`_sim_i += 1`;else 分支——先 disconnect(防再入)→ `_finish_batch()` → `_batch_finished.emit()`
- [ ] 新增 `_finish_batch() -> void`(0 参):收尾四语句由 run() 尾 :51-54 迁入(`close_session()`→报告注入→`panel.set_sim_running(false)`→`_running = false`,同帧同相位——判据③;`_finished = true` 置位为新增首句,对两断言面不可观测——白名单 5)
- [ ] `_run_one_level` **零触碰**(:58-68 逐字保持——败局节点记账非置假;本批行为零改动的锚面)
- [ ] 孤立验证:`--only=standee_fx_sweep` 全绿(wd8 三断言零改动保持绿=收尾语义零变化实证)+ui_tune_probe 单探针绿(probe_gate --full 段承载)
- [ ] 自检:新增头注/注释零变更叙事、S11 禁词零命中
取用:T0 重锚行号。产出:`_batch_finished` 信号与 `_step_frame`/`_finish_batch` 签名(T2 对拍新形态即产品码本体);wd8 保持绿读数。
**T2 双实现对拍夹具(0.25–0.5 工作日)**
- [ ] 建 `game/tests/unit/presentation/tune_sim_batch_parity_test.gd`(extends SceneTree,Kit 收尾,全类型标注):内类 FakePanel(Node——wd8 同形 setter 面,另带进度串/报告记录数组)+FakeDriver(RefCounted——固定返回值两档〔victory/defeat,同 wd8 :53-55 取值〕,零随机;逐局记录帧索引与运行态读数,帧索引经宿主注入的计数 Callable 读取)+AwaitLoopRunner(RefCounted——现 `run()` await 形态逐字副本,含循环内 await 与收尾四语句在 run() 尾的现行形态(:47-54 语句逐字),含自身 `is_running()` 镜像;**与产品码新形态的申报差异仅一处**:批末把 stats 六键束存夹具成员供对拍读取——产品码 stats 经报告公开面观测,第 17 节直查受控例外申报)
- [ ] 对拍驱动:两形态各经 `run(n)` 真实入口驱动(新形态=真 TuneSimScript 直构,旧形态=夹具直构;同参假面板/假驱动各自实例);拓扑钉死——测试宿主先连 process_frame 帧计数、后启动两形态(两形态下「计数先于步进体」相对次序同构,序列可比);n=3 常规+n=0 退化两案
- [ ] 五面对拍断言(落点 `_init`,尾缀〔测速 V1〕..〔测速 V5〕):V1 帧索引序列——两形态逐局记录序列逐位相等(每步恰一帧+相位同点的执行证据);V2 运行态序列——驱动内 `is_running()` 读数序列逐位相等+收尾双运行态假相等;V3 进度串序列——面板记录的 set_sim_progress 串序列逐位相等;V4 stats 六键逐位——夹具暴露 stats 与测试内按假驱动固定返回值重算的六键期望(wins/dmg_all/hp_left_win/hp_boss/rounds_all/death_nodes)逐键相等;V5 报告全文逐字——两形态面板记录的 set_sim_report 串逐字相等,n=3 案含「关卡完成率/关卡完成剩余 HP 均/败北节点/N=」渲染面(n=0 案只断逐字相等——空 stats 渲染无剩余 HP 行,`_mean`/`_p90` 空守卫实查在案)
- [ ] `run_all.gd` manifest 增行 `{"path": "res://tests/unit/presentation/tune_sim_batch_parity_test.gd", "expect": 0}`
- [ ] 孤立验证:`--only=tune_sim_batch_parity` 绿+`--only=standee_fx_sweep` 复跑绿
取用:T1 的产品码新形态与 `_batch_finished` 放行语义。产出:五面对拍断言面(对照表 V1/V2 行验收主体)。
**T3 键账对账与收口(0.25 工作日)**
- [ ] S13 对账:`python tools/gdstyle_gate.py` 输出确认 tune_sim await-in-loop 裸键消失(消失键不参与红判定)、无基线外新键
- [ ] 关联档复跑:`--only=standee_fx_sweep,tune_sim_batch_parity`+ui_tune_probe(probe_gate --full 段)全绿
- [ ] 批末代码域闸全绿:`tools/run_all_gates.sh --code`(改动含 game/ 下非 .md 行为载体——`--code` 档)
- [ ] 错误路径声明缺口写入实现报告(第 4 节 🔴 条全文迁入——禁静默)
- [ ] 产出物落位卡:`reports/iter44-实现报告-2026-10-30.md`(各卡状态+实机目检总清单〔本批零目检项〕+遗留读数义务+实现决定记录)与 `reports/iter44-反哺点清单-2026-10-30.md`(第 6 节同步面逐条落点)写齐,方算收口
编队注记:本卡与零豁免其余三卡无产品码触面交集(本卡触面=tune_sim_controller.gd+game/tests/ 两件);run_all.gd manifest 行与并行批可能交叠——编队归编排段。本卡在 iter44 worktree 施工(卡①卡② 已收口冻结在分支),T0 重锚以 worktree 基面为准。
## 12. 验证闸——改动—验收对照表
域口径:本批改动全为 `game/` 下非 .md 行为载体——批末跑代码域闸;里程碑步(T 段)出口跑一次全套闸,波内修复只做出红最小单元的最窄确认,新增测试先行孤立验证再并入全套(测试执行两入口铁规不破)。三件保险(冒烟/系统测试/静态扫描)恒跑;02 单元与集成按关联档选套件——**本卡显式申报套件:standee_fx_sweep_test(wd8——套件名与涉案文件无字面关联,diff 推导可能漏选,特此申报)+tune_sim_batch_parity_test(新增)**。测试方式两分法:本批全部调试接口自动化(假驱动/假面板直构+真实按钮信号链探针),零用户目检项(tune_sim 全链路发布态不可达——登记不阻塞条款零登记项)。**静态判据申报(不入对照表,test-conventions §8.4 出口)**:S13 await-in-loop 裸键消除无独立运行期提交面——判据=`python tools/gdstyle_gate.py` 与 `game/tests/static_scan.sh` 退出码(T3 执行),属静态判据+评审注记申报观测面局限;对拍夹具不在 S13/S8/S9/S10 扫描面的声明同此口径(扫描面=产品码三目录与 S8 文件集,static_scan.sh 实查在案)。**声明缺口(不入对照表——证明做不出出口)**:脚本错误中断路径差异不可消除,见第 4 节 🔴 条,实现报告文档化(T3)。**能力清单**:本批新增能力一行「调参测速批量模拟(逐帧步进与统计读数)」已随本卡 v1.0 写卡段同批入表(README :131)——对照表三行能力列锚该行;manifest 计数行随套件增减浮动。
| 行 | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| V1 | 步进状态机改造(循环内 await 消除的落码面) | tune_sim_controller.gd :36-54 改形(run 函数级单次 `await _batch_finished`,全文件零循环;收尾四语句 :51-54 迁入 `_finish_batch`)+:58 起新增 `_step_frame`/`_finish_batch` | 复用 wd8 驱动形态 `await ctrl.run(2)`(:144——run 协程身份契约,零改动);新增对拍 V4/V5(stats 六键逐位+报告逐字,落点 tune_sim_batch_parity_test.gd:_init,尾缀〔测速 V4/V5〕) | 单元 | TuneSimScript 直构(字典夹具 db+假面板+假驱动,wd8 :142 同款)经 `run(n)` 真实入口;对拍两形态同入口 | run 可等待(await 挂起=整批完成放行)+stats 六键逐位+报告全文逐字 | 新增:调参测速批量模拟(逐帧步进与统计读数) |
| V2 | 双实现对拍(帧相位等价的执行化证据) | tune_sim_batch_parity_test.gd 新建+run_all.gd manifest 增行 | 新增五面对拍断言〔测速 V1–V3〕:帧索引序列/运行态序列/进度串序列逐位相等(落点 tune_sim_batch_parity_test.gd:_init) | 单元 | 两形态各经 `run(n)` 真实入口(n=3 常规+n=0 退化);帧索引经测试宿主 process_frame 计数 | 两形态五面记录序列逐位相等(每步恰一帧+相位同点的当轮执行证据——判据本体) | 新增:调参测速批量模拟(逐帧步进与统计读数) |
| V3 | 运行态语义零变化(收尾迁移的回归锚面——非行为改动行) | 收尾四语句(:51-54)迁入 `_finish_batch` 同帧同相位——非行为改动判断依据:语句集与语义零变化、对两断言面不可观测,等价证据由对拍 V2/V5 承载;`_run_one_level` :58-68 零触碰 | 复用 wd8 三断言〔wd8〕(:145-147——`drv.seen == [true, true]`/收尾双运行态假/报告非空——零改动**保持绿**)+ui_tune_probe ⑤ 守卫环与输出四串(:63-83——零改动保持绿) | 单元+探针 | wd8 假驱动回调读 `is_running()`(`await ctrl.run(2)`);探针 `press_simulate_for_probe()` → 按钮 pressed 信号链 → `run(10)` → 守卫环逐帧复查 | 驱动逐局运行态读数序列 [true, true]+收尾双运行态假+守卫环 1800 帧预算内退出+`output_text()` 四关键串(浮窗运行态贯穿整批的可观测提交面——绿=保持绿,非转绿) | 新增:调参测速批量模拟(逐帧步进与统计读数) |
反向核对:diff 中行为改动共零处;其余为非行为改动——V1 结构改形(零漂移判据=对拍五面全等+wd8/probe 零改动保持绿)、V2 测试面新增、V3 回归锚面行(非行为改动)、manifest 增行(测试基建)。孤儿断言:对拍五断言挂 V1/V2 两行,wd8/probe 既有断言挂 V3 行,无孤儿。
## 13. 异常分级
- **L1 自救**:编译错、断言尾缀措辞、夹具记录数组名笔误——就地修复,重跑最窄单元确认。
- **L2 记录继续**:T0 套件因在册无关债先红于 wd8 断言(记录可达性事实,不借批顺手修);行号重锚漂移(就地改卡记录);产品码 process_frame 订阅面发现第三处(记录+评审注记——非 run 途中新增场景不红)。
- **L3 停机写报告等用户**:①T0 两面任一红=现状与重裁前提失配(迭代 41 修复被回退或新潜伏红),停卡呈报;②等价性对拍失败——五面任一不等=测量语义漂移,设计前提崩塌;③机检预算互斥——改造后实测键超第 9 节预算或基线外新键=上游缺陷停卡退回写卡段(含禁为机检保留无效成员条款冲突);④S13 对账 await 键未消或冒新键;⑤判据绑不上可观测提交面(证明做不出=停卡呈报,禁自造注入破坏或回退取证)。
## 14. 代拍板白名单(逐条列,超清单记实现报告「实现决定记录」)
1. 断言失败消息尾缀〔测速 V1–V5〕的分配与措辞。
2. 头注 `_batch_finished` 契约行措辞(信号语义与发射时点不变)。
3. `_finished` 成员零读者确认后删去(成员 11→10——键账只降不升,行为与断言面零变化)。
4. 夹具假面板/假驱动成员命名微调(记录语义不变;与 wd8 同形自建、不跨套件共享)。
5. 收尾段语句相对次序——现行 run() 尾次序(close→报告→置假→_running)与设计草图次序(close→置假→_running→报告)对两个断言面均不可观测,两种皆可落码(实现期查 git 历史原序可进一步对齐);`_finished = true` 置位为新增首句(成员写,不可观测)。
6. 排版机械改写(空行、preload 顺序、注释换行)。
## 15. 待裁点
零待裁点,开工门=U0。(裁决记录:2026-10-29 用户裁四点——①信号步进状态机形态采纳〔SceneTreeTimer 链/Timer 节点/递归协程/线程四形态淘汰依据见设计文档 §3〕;②收尾置假还原作为已申报行为改动随批实施【已作废——前提被迭代 41 测试债批 215da315 消化,v1.1 剥离】;③双实现对拍入批;④脚本错误中断路径差异接受并文档化。2026-10-30 停卡重裁(用户裁,按停卡呈报建议案逐条落卡):剥离行为改动申报 V3、T0 义务改现状复核、夹具申报差异两处减一处;重裁沿用 2026-10-29 grilling 复核门结论(reports/复核-grilling-零豁免五卡-2026-10-29.md,修正全落),重裁差异面未新增设计裁决。)
## 16. 测试削弱申报
无申报项——wd8 三断言与 ui_tune_probe ⑤ 零改动零削弱(保持绿);对拍五断言为新增非改判;夹具与产品码新形态的申报差异仅一处(夹具暴露 stats 供对拍读取)系新增件自身设计、设计文档 §5 在案,非既有断言削弱。禁删断言、禁放宽阈值、禁缩小扫描面、禁把真实入口换成下层直调;未申报的削弱由独立验收审计判红。
## 17. 独立验收审计
合并前由未参与实现的会话或子代理执行;报告落 `reports/iter44-验收审计-2026-10-30.md`,未过不得合并。审计至少做五件事:①逐行核对第 12 节对照表(测试存在、真的执行、尾缀可 grep、真实入口未绕过改动点);②抽查判据绑定面——帧索引序列是否真从 process_frame 发射点记录、对拍拓扑两形态是否同构可比、V3 行判据是否钉在运行态提交面(保持绿证据,非终态兜底);③核对现状复核证据在案(T0 两面实跑绿读数+停卡呈报三重证据引注〔215da315 提交信息〕——前提消化链条完整,缺 T0 复核读数=审计不过);④核对行为改动零处——diff 中任何行为改动(含收尾四语句语义漂移)=审计不过,收尾迁移的等价性由对拍 V2 运行态序列+V5 报告逐字承载;⑤检查测试削弱(对照第 16 节零申报)与内部字段直查受控例外(本批零直查——新形态 stats 经报告公开面观测)。
## 18. MC/数值与 RNG 零触碰声明
本批零数值改动、零数据表改动、零 RNG 流变化:SEED_BASE=4200 与逐局种子切分零变化(stats 种子纯函数前提);对拍夹具零随机(假驱动固定返回值)。MC 对拍不跑、零跑批义务(无数值改动——`sim-data/README.md` 档位硬闸:无数值改动零跑)。
@@ -1,188 +0,0 @@
# 需求卡片——站桩名字行提档与地台色合并批(名字行字号 12→14 + 受光线/描边两常量退役改指)
状态行:v1.1 定稿——零待裁点(grilling 复核过:reports/复核-grilling-站桩名字行提档与地台色合并批-2026-10-31.md,三阻断两建议全落;两点裁决已于 2026-10-31 闭合落文 art-bible v3.29,见 §11)
接收方:待办清单条目「迭代 43 批后待裁两点——名字行字号档位/换色常量退役」(状态=文档设计已闭合,本卡写完推进「已写卡」)
来源:**设计事实源**=`design/art/art-bible.md` v3.29——§7.4 小字号豁免条款「站桩名字行同理提档」句(名字行提档 `FS_HINT` 14 归六档、不增列②装饰 caption 在册面、几何随档联动)+§4.1 换色合并登记三处①②(`#C4AC84`→并入掩尘 `#C9AF83`;`#5A4C3A`→并入 `#4C4038`)。验收底线(2026-10-31 用户裁)=名字行清晰可读、长名不截字。
## 1. 目标与完成定义
这批做什么:把 art-bible v3.29 两处新裁决落进代码——①站桩名字行(敌人名+护甲字形+全部宽度测量与基线)从 `FS_CAPTION`(12px)提到 `FS_HINT`(14px),名字行几何随档联动;②`STAND_STONE_RIM`/`STAND_STONE_EDGE` 两枚旧色常量删除,两个绘制点改指合并目标色(站桩石板顶缘受光线与棋盘墙格口沿高光共用枚→掩尘 `BRICK`;石板描边→新常量承载 `#4C4038`),使贴图缺位时的程序化回退绘制与已落合并值的贴图一致。顺带了结迭代 43 移交小件①(能力清单补行——写卡段随本卡同批入表)。
完成定义(全部满足才算完):
1. 名字行五处取值点全取 `UiKit.FS_HINT`,基线偏移按 14px 档重算(0.35h 规则→牌顶+18),素底牌几何单点 `_name_plate_rect`/`_name_baseline` 仍是唯一算式落点。
2. `STAND_STONE_RIM`/`STAND_STONE_EDGE` 在 `ui_kit.gd` 删除且全仓零引用;`UiKit` 脚本常量表断言两枚不在册。
3. 两枚绘制点改指:`cell_base_layer.gd` 口沿高光与 `standee_body.gd` 顶缘受光线→`UiKit.BRICK`;`standee_body.gd` 回退描边→新常量 `UiKit.PLAZA_EDGE`(=Color8(76,64,56))。
4. 渲染 golden 三张板面基线与口沿高光换值一致(改后跑 render_gate,红则重锚三张、重锚后 0.00% 留痕;level_map/menu_boot 不触不重锚)。
5. 能力清单补行落表(`game/tests/README.md` 能力行+覆盖域表战斗表现.md 行,本卡成文同批完成)。
6. 代码域闸全绿;产出物三份落 `reports/`,独立验收审计未过不合并。
## 2. 现状事实
全部行号为 **2026-10-31 实查**——编入后 T0 按 T0 时点 master 重测重锚。
1. 名字行字号取值点五处,全在 `game/presentation/views/standee_body.gd`:`:334-335` 护甲字形宽测量(贴图缺位时)、`:336` 名字宽测量(`_name_plate_rect` 内)、`:467` 护甲字形宽测量(绘制侧)、`:474` 护甲字形绘制、`:476` 名字绘制——全取 `UiKit.FS_CAPTION`。
2. 同文件另有 `FS_CAPTION` 合法在役一处:`:114` 站桩消散提示(§7.4 ②在册面「站桩消散提示」)——本批不动,禁误改。
3. 名字行几何单点:`_name_plate_rect`(`:331-338`,牌高 26 固定=护甲图标 24px+上下各 1px;牌宽=行宽+8 水平居中;牌底沿=地台顶)+`_name_baseline`(`:342-343`,返回 `plate.position + Vector2(4.0, 17.0)`;注释 `:341` 「12px 字形基线=中心+4」)。基线偏移遵循本文件既有 0.35h 规则(立绘字形 `:434` 用 `ts.y * 0.35`;12×0.35≈4.2→4,与 `+4` 吻合)→14×0.35≈4.9→**5**,新基线=牌顶+13+5=**牌顶+18**。
4. 名字行探针 `name_line_probe()`(`standee_body.gd:63`)返回 Dictionary:`baseline_y`/`plinth_top_y`/`plate`/`plate_color`/`armor_icon`/`armor_tex_bound`——消费面=`ui_struct_probe` 与 `standee_present_values_test`。
5. 地台程序化回退(`standee_body.gd:380-383`):`TEX_STAGE_PLAZA` 缺位时三段绘制——板身 `UiKit.STAND_STONE`、描边 `UiKit.STAND_STONE_EDGE`(`:382`)、顶缘受光线 2px `UiKit.STAND_STONE_RIM`(`:383`)。贴图在位时不走此路径(九宫格拉伸域 `:388-393`,贴图已落合并值 `#C9AF83`/`#4C4038`)。
6. 口沿高光:`game/presentation/views/cell_base_layer.gd:176` `canvas.draw_rect(..., UiKit.STAND_STONE_RIM, true)`(墙格南立面受光亮线,线厚常量 `FACADE_HIGHLIGHT_PX := 2.0` 于 `:9`;与地台顶缘线共用同枚常量,行内注释「同值单点」)。设计出处=art-bible §8.2 高墙瓦片体系条(南临空墙格=立面口沿高光 2px `Color8(196,172,132)`)。
7. `ui_kit.gd`:`:188` `STAND_STONE_EDGE := Color8(90, 76, 58)`、`:189` `STAND_STONE_RIM := Color8(196, 172, 132)`;合并目标①=`BRICK := Color8(201, 175, 131)`(`:165` 掩尘,在役);合并目标②=`#4C4038`=Color8(76,64,56)——**全仓无既有 token**(2026-10-31 grep `Color8(76` 零命中),需新常量承载。
8. 全仓 `STAND_STONE_RIM`/`STAND_STONE_EDGE` 引用面=定义 1 处+消费 2 文件 3 行(`:382`/`:383`/`cell_base_layer:176`)——tests/probe/sim 零引用(grep 实查),删除无隐性断裂。
9. 既有断言面 `game/tests/unit/presentation/standee_present_values_test.gd`:V6 硬编码几何读数 `baseline_y=149.0`/`plinth_top_y=158.0`(±0.51);V7 源码锚 `contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT")`(contains 语义——名字行改 `FS_HINT` 后仍绿,零破坏);V5 先例=「绘制点取某常量」用源码锚承载。
10. golden 触面:`game/tests/golden/` 板面三张(board_L1_dungeon/L2_underdark/L3_deep_underdark)渲染含墙格口沿高光像素——换值后逐像素比对必红;载体=`tools/render_gate.sh`(闸 05,窗口化)+重锚工具 `tools/golden_baseline_refresh.sh`。level_map/menu_boot 与站桩名字行均不在 golden 像素域。
11. 能力清单:`game/tests/README.md` 无 `standee_present_values_test` 代表用例行(迭代 43 移交小件①在册待补,登记规格=能力「站桩呈现定值与素材绑定」/真实入口 `UiKit.load_tex()`/`StandeeBody.name_line_probe()`/代表用例 `standee_present_values_test.gd:_init`)。本会话即写卡会话,随本卡同批补行(§5)。
12. gdstyle:`standee_body.gd` 无基线登记键(迭代 44 收尾 refresh 已清偿);本批零新增成员/函数,S13 预期零新增键。
## 3. 设计契约(违反即返工,标 🔴 触碰=L3 停机呈报)
- 🔴 **两个 12px 位不得混**:`FS_HINT` 只进名字行五取值点;`:114` 站桩消散提示的 `FS_CAPTION` 是 §7.4 ②在册面合法位,禁顺手改。
- 🔴 **合并目标逐位**:受光线绘制点→`UiKit.BRICK`(`#C9AF83`),描边绘制点→`UiKit.PLAZA_EDGE`(=Color8(76,64,56));禁改 `BRICK` 值、禁引入任何近似替代值。
- 🔴 **`STAGE_BG` 零触碰**:§4.1 ③已裁随舞台背景插画批处置——本批不改 `STAGE_BG` 值、不改 `battle_stage_view`、不动 `theme_projection_test.gd:669` 与 `ui_contrast_probe.gd:30`。
- 🔴 **几何单点**:字号与基线联动只落在 `_name_plate_rect`/`_name_baseline` 两个既有单点内;探针、测试、任何调用方不得复写算式。
- 🔴 **常量退役=真删**:`STAND_STONE_RIM`/`STAND_STONE_EDGE` 删除后禁留别名、转发常量或空注释占位(禁为机检保留无效成员)。
- 🔴 **golden 重锚申报制**:三张板面基线重锚仅限口沿高光换值所致,重锚前后读数留实现报告;禁借重锚夹带其他像素变化。
- 🔴 **测试削弱申报制**:允许改动的旧断言全集见 §8 申报节,未申报的删改由独立验收审计判红。
- 🔴 **禁变异类测试与撤销类取证**:不得靠回退 hunk 或注入破坏证明断言有效。
## 4. 范围边界
做:名字行五取值点提档+基线重算+探针增 `name_font_size` 键;`ui_kit.gd` 删两常量+增 `PLAZA_EDGE`;三处绘制点改指;golden 三张板面重锚;既有 V6 读数换代;能力清单补行(写卡段,本卡成文同批)。
不做(明示去向):
1. **`STAGE_BG` 改值**——随「战斗区背景图替换」批(art-bible §4.1 ③改注在案)。
2. **`:114` 消散提示与②在册面其他位**——合法 caption 档,零触碰。
3. **口沿高光独立语义 token 化**——拆枚是命名扩张,值同源且 art-bible 已裁「统一为同一枚」,禁另立。
4. **battle.md/art-bible 直改**——实现批禁写 `design/`;§4.1 源常量名退役后的指称换代走 §5 反哺点通道。
5. **长名截字的主动钳制逻辑**——现行无钳制,本批不新增行为;若目检发现截字=产品面问题走 L3 停卡呈报。
6. **MC/数值验证**——无数值改动零跑。
## 5. 关联文档同步面
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|---|---|---|
| design/art/art-bible.md(设计权威——实现批禁直改) | §4.1 换色合并登记①②句中「代码 `STAND_STONE_RIM`/`STAND_STONE_EDGE`」指称随退役失指;②合并目标 `#4C4038` 需补代码名 `PLAZA_EDGE`;§8.2 口沿高光行「`Color8(196,172,132)`」值随换值换代 | 反哺点通道:逐条列进 `reports/iter<N>-反哺点-<日期>.md`(改哪一节、改成什么、依据=本卡与实测读数),用户裁决后归文档批落文 |
| design/art/art-bible.md §7.4 | 无(提档句 v3.29 已落文,实现后即为合规态) | 无(本行占位——按规约查不出也要写明) |
| game/tests/README.md(机械面——归写卡段维护) | 能力行补行「站桩呈现定值与素材绑定」(迭代 43 移交小件①)+覆盖域表战斗表现.md 行补名 | 写卡段于本卡成文同批入表(已完成),实现批零增删零改行;批末核对表上名字与实况一致 |
| design/gdd/ux/battle.md | 无——`:155` 名字行陈述(纯名字+护甲类型图标)不涉字号与色值 | 无(本行占位) |
## 6. 卡前文件结构图
| 文件 | 职责 | 新增/复用 | 行为改动? |
|---|---|---|---|
| game/presentation/views/ui_kit.gd | 删 `STAND_STONE_EDGE`/`STAND_STONE_RIM` 两常量;原位新增 `PLAZA_EDGE := Color8(76, 64, 56)`(注释=art-bible §4.1 ②合并目标·小石台描边回退用色) | 修改 | 是(色值换——对照表行 3/4) |
| game/presentation/views/standee_body.gd | 名字行五取值点 `FS_CAPTION`→`FS_HINT`;`_name_baseline` 偏移 17.0→18.0+注释换代;回退描边 `:382`→`PLAZA_EDGE`、顶缘线 `:383`→`BRICK`;`name_line_probe` 增 `name_font_size` 键 | 修改 | 是(行 1/2/3/4) |
| game/presentation/views/cell_base_layer.gd | 口沿高光 `:176`→`UiKit.BRICK`+行内注释换代 | 修改 | 是(行 3) |
| game/tests/unit/presentation/standee_present_values_test.gd | 增 V10–V12 断言(提档/退役/新 token);V6 基线读数 149→150(申报) | 修改 | 是(测试面——行 1/2/3/4) |
| game/tests/README.md | 能力行补行+覆盖域表战斗表现.md 行补名 | 修改 | 否(机械面文档,写卡段同批完成) |
| game/tests/run_all.gd | 套件登记注释「V5–V9」→「V5–V12」换代 | 修改 | 否(注释换代,非行为改动) |
不触碰:`battle_stage_view.gd`、`board_view.gd`、`theme_projection_test.gd`、`ui_contrast_probe.gd`、`data/tables/` 全部、`core/` 全部。
## 7. 任务卡 T0..T3
### 7.1 T0——读数重锚与改前基线(估时 0.1 个工作日)
- [ ] 重锚 §2 全部行号(standee_body/cell_base_layer/ui_kit/test 五文件);复核实查结论第 7 条(`#4C4038` 无既有 token)与第 8 条(引用面仅三行)——不符=L3 停卡退回写卡段。
- [ ] 改前读数(孤立验证):单件跑 `standee_present_values_test`(绿)+`python tools/gdstyle_gate.py`(三触面文件零新增键、既有键持平)+窗口化跑 `tools/render_gate.sh`(板面三张绿=改前基线确认,读数进实现报告 T0 节)。
- [ ] 字体度量校验:`f.get_string_size("名", HORIZONTAL_ALIGNMENT_LEFT, -1, 14)` 实测 Fusion Pixel 14px 高度,核对 0.35h 推导(≈5);若实测推导出不同偏移,按实测定值并记实现报告「实现决定记录」。
- 取用:本卡 §2。产出:重锚行号+三份改前读数+基线偏移定值(T1/T2 对拍基准)。
### 7.2 T1——名字行提档(估时 0.15 个工作日)
- [ ] `standee_body.gd` 五取值点(`:334-335`/`:336`/`:467`/`:474`/`:476`)`UiKit.FS_CAPTION`→`UiKit.FS_HINT`;`:114` 消散提示零触碰。
- [ ] `_name_baseline` 返回 `plate.position + Vector2(4.0, 18.0)`;`:341` 注释改「14px 字形基线=中心+5(0.35h 规则,T0 实测校验)」。
- [ ] `name_line_probe()`(`:63`)返回字典增键 `"name_font_size": UiKit.FS_HINT`(值出自几何单点所用字号常量)。
- [ ] `standee_present_values_test.gd` 增三断言(尾缀〔名字行 V10〕):①probe 面——`int(nl.name_font_size) == UiKit.FS_HINT`(几何单点实读);②源码锚——`body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, UiKit.WARM")` 且 `body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, col")`(名字/护甲两绘制点,沿 V7 先例);③计数锚——`body_src.count("UiKit.FS_CAPTION") == 1`(改后全文件仅剩 `:114` 消散提示一处——三处宽度测量点随此锚强制换代)。
- [ ] V6 基线读数 149.0→150.0、判据句「地台顶上方 9px 档」→「8px 档」(§8 申报第 1 条)。
- [ ] 孤立验证:单件跑 `standee_present_values_test`(V5–V10 全绿)+`python tools/gdstyle_gate.py`(standee_body 键只降不升)。
- 取用:T0 重锚行号与偏移定值。产出:`name_font_size` 探针出口(T3 对照表行 1 判据面)+绿的单件日志。
### 7.3 T2——地台色合并(估时 0.15 个工作日)
- [ ] `ui_kit.gd` 删 `:188`/`:189` 两常量;原位新增 `const PLAZA_EDGE := Color8(76, 64, 56)`,注释「小石台描边(art-bible §4.1 换色合并②合并目标——贴图已落此值,缺位回退同值)」。
- [ ] `standee_body.gd:382` `STAND_STONE_EDGE`→`PLAZA_EDGE`;`:383` `STAND_STONE_RIM`→`BRICK`;`:365` 附近回退注释「STAND_STONE 族」句随改。
- [ ] `cell_base_layer.gd:176` `STAND_STONE_RIM`→`BRICK`,行内注释「同值单点」句改「合并归掩尘枚(art-bible §4.1 ①)」。
- [ ] `standee_present_values_test.gd` 增两断言(尾缀〔名字行 V11〕〔名字行 V12〕;常量面载体=`load("res://presentation/views/ui_kit.gd").get_script_constant_map()`——经 Script 对象取常量表,`UiKit` 类引用直调 `get_script()` 不合法〔grilling 实测 parse error〕):V11=常量表无 `STAND_STONE_RIM`/`STAND_STONE_EDGE` 两键、`BRICK` 值=Color8(201,175,131)、源码锚 `body_src.contains("ped.size.x, 2.0), UiKit.BRICK, true")`(顶缘受光线)与 `cell_src.contains("FACADE_HIGHLIGHT_PX), UiKit.BRICK, true")`(口沿高光——`cell_src`=同法读 `res://presentation/views/cell_base_layer.gd`);V12=`PLAZA_EDGE` 值=Color8(76,64,56)(§4.1 ②合并目标逐位)+源码锚 `body_src.contains("UiKit.PLAZA_EDGE")`(回退描边)。
- [ ] `standee_present_values_test.gd` 文件头注释「V5–V9 断言面」句与 `game/tests/run_all.gd` 套件注释同步换代「V5–V12」(非行为改动)。
- [ ] 批内静态取证:`grep -rn "STAND_STONE_RIM\|STAND_STONE_EDGE" game/ tools/ --include="*.gd" --include="*.py" --include="*.sh"` 零命中,输出原文落实现报告(绘制点改指的观测面局限申报见 §8——色值进 draw 命令无运行期读出面,由「常量不在册断言+grep 零引用+PLAZA_EDGE 值断言」三件合成承载)。
- [ ] golden 改后比对:窗口化跑 `tools/render_gate.sh`——板面三张红(口沿高光换值所致)=预期证据,读数落实现报告。
- [ ] golden 重锚:跑 `tools/golden_baseline_refresh.sh` 重锚板面三张基线;复跑 `tools/render_gate.sh` 三张 0.00% 留痕;`level_map`/`menu_boot` 禁触(§3 重锚申报制)。
- [ ] 孤立验证:单件跑 `standee_present_values_test`(V5–V12 全绿)+`python tools/gdstyle_gate.py`(零新增键)。
- 取用:T1 产出与 T0 读数。产出:退役常量断言面+重锚后 golden 三张+grep 取证原文。
### 7.4 T3——批末收口与产出物落位(估时 0.1 个工作日)
- [ ] 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(本批改动含 `game/` 下非 `.md` 行为载体);里程碑步出口跑一次全套;波内修复只做出红最小单元的最窄确认。
- [ ] 能力行核对:`game/tests/README.md` 能力行「站桩呈现定值与素材绑定」与覆盖域表战斗表现.md 行名字与实况一致(本卡成文时已落,实现批禁改)。
- [ ] **产出物落位卡**(轻闸全绿后、合并前写齐,方算收口):
- `reports/iter<N>-实现报告-<日期>.md`——各卡状态+T0 三份改前读数与改后对拍+grep 取证原文+golden 重锚前后读数+实机目检总清单(登记不阻塞三项:名字行清晰可读且长名不截字/墙格口沿亮线观感无异/贴图缺位回退描边与顶缘线观感无异)+实现决定记录。
- `reports/iter<N>-反哺点-<日期>.md`——§5 同步面 art-bible 两行逐条有落点。
- `reports/iter<N>-验收审计-<日期>.md`——独立验收审计(未参与实现的会话或子代理执行,未过不得合并)。
- 取用:T1–T2 全部产出。产出:代码域闸回执+三份 reports/ 产出物。
## 8. 验证闸(改动—验收对照表)
本批两处行为改动(名字行字号与几何、两枚色值换)+一节常量面退役;无新玩法验收。对照表 6 行对应 §1 完成定义第 1–4、6 条;第 5 条(能力行落表)经 T3 核对步承载(本卡成文时已落表)。能力行=**新增:站桩呈现定值与素材绑定**(本卡成文同批入表,见 §5)。
| # | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| 1 | 名字行字号提档 FS_HINT 14 | `standee_body.gd` 五取值点+`name_line_probe` 增 `name_font_size` | 「名字行绘制与几何取 FS_HINT」`standee_present_values_test.gd:_init`(〔名字行 V10〕) | 单元 | `godot --headless --path game --script res://tests/unit/presentation/standee_present_values_test.gd` | probe 几何单点实读 `name_font_size==UiKit.FS_HINT`+两绘制点源码锚+`FS_CAPTION` 计数锚==1(三处测量点强制换代,沿 V7 先例) | 新增:站桩呈现定值与素材绑定 |
| 2 | 名字行几何随档联动 | `_name_baseline` 偏移 17→18;V6 读数换代 | V6 既有断言更新值(〔样张批 V6〕,申报第 1 条) | 单元 | 同上 | `baseline_y=150±0.51`(牌顶+18)且 `< plinth_top_y`(文字不压地台判据不变) | 新增:站桩呈现定值与素材绑定 |
| 3 | 受光线常量退役改指 BRICK | `ui_kit.gd` 删 `STAND_STONE_RIM`;`standee_body.gd:383`+`cell_base_layer.gd:176` 改指 | 「退役常量不在册+掩尘值逐位」同套件(〔名字行 V11〕) | 单元 | 同上 | 常量表无该键+`BRICK==Color8(201,175,131)`+顶缘线/口沿高光两处源码锚(正证改指目标色);grep 零引用作反证补充(观测面局限申报:色值进 draw 命令无运行期读出面,正证由源码锚承载) | 新增:站桩呈现定值与素材绑定 |
| 4 | 描边常量退役改指新 token | `ui_kit.gd` 删 `STAND_STONE_EDGE`+增 `PLAZA_EDGE`;`standee_body.gd:382` 改指 | 「描边合并目标逐位」同套件(〔名字行 V12〕) | 单元 | 同上 | `PLAZA_EDGE==Color8(76,64,56)`(§4.1 ②目标逐位)+常量表无旧键+描边行源码锚;grep 零引用反证补充 | 新增:站桩呈现定值与素材绑定 |
| 5 | 板面 golden 与口沿高光换值一致 | golden 三张板面基线重锚 | `tools/render_gate.sh` 三张 0.00% | golden | 窗口化 `tools/render_gate.sh` | 重锚后逐像素零差留痕;level_map/menu_boot 零触 | 新增:站桩呈现定值与素材绑定 |
| 6 | 批末收口 | 全批 | 代码域闸 `tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 新增:站桩呈现定值与素材绑定 |
**测试削弱申报**(实现期允许的旧断言改动全集,超出即审计判红):
1. V6 基线读数 `149.0→150.0` 与判据句「9px 档→8px 档」——规格换代(字号 12→14,0.35h 规则推导),非放宽;文字不压地台判据(`baseline_y < plinth_top_y`)零改动。
2. 新增断言=纯增量;V5/V7/V8/V9 判据零改动(V7 源码锚 contains 语义,名字行改档后自动覆盖,零破坏)。
3. golden 三张板面重锚=有意色值换代的基线换代(§3 申报制),重锚前后读数留实现报告。
4. 显式禁止:删任何旧断言、改 `BRICK` 值、把探针/测试的字号读取换成别的入口。
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表两端、抽查判据绑定面(`name_font_size`/常量表/`PLAZA_EDGE` 值)、核对削弱申报与 golden 重锚申报、确认全文无变异/撤销类取证;未过不得合并。
## 9. 异常分级
- **L1 自救**:单件跑红→定位该件最小单元修后重跑;`--only` 选跑零命中→修正选词重跑。
- **L2 记录继续**:同一红连续 3 次同因→在实现报告「实现决定记录」记因后继续最窄确认。
- **L3 停机写报告等用户**:
1. **机检预算互斥=上游缺陷停卡退回写卡段**——`gdstyle_gate` 出 NEW 键或既有键恶化;本批零新增成员/函数,任何键恶化都不许改基线换绿,禁开豁免。
2. **实机可读面本身不达标**——目检发现名字行截字、叠压血条或 14px 观感劣化;产品面问题停卡呈报,禁缩字号、禁加钳制逻辑凑绿(钳制=行为扩张,归新裁决)。
3. **改前实查与 §2 不符**——引用面超第 8 条清单、`#4C4038` 已有 token、或 V6 读数与实装几何不匹配且非字号所致。
4. **golden 重锚夹带**——重锚后 diff 域超出口沿高光像素带(含名字行字号变化渗入板面基线的可能——板面不含站桩,预期零渗入;若渗入=停卡呈报)。
## 10. 代拍板白名单(逐条列,超清单记实现报告「实现决定记录」)
1. 注释与断言消息文案措辞(判据、数值不变)。
2. `name_line_probe` 新键名(`name_font_size`)与其在返回字典中的位置。
3. 尾缀〔名字行 V10〕–〔名字行 V12〕字样编排(含义不变)。
4. T0 字体度量实测偏移与 0.35h 推导不一致时的定值(按实测,记录依据)。
5. `PLAZA_EDGE` 常量在 `ui_kit.gd` 的落位行与注释句。
## 11. 待裁点
**零待裁点,开工门=U0。** 两点裁决已于 2026-10-31 用户裁闭合并落文 art-bible v3.29(§7.4 名字行提档句、§4.1 换色合并登记句换代;舞台底 `STAGE_BG` 对已裁随背景插画批,不在本卡)。编批由迭代编排段定。
## 12. 落位与可复用性预检
**复用面**:本批零新增文件、零新增函数/类。既有复用——字号档位取 `UiKit.FS_*` 既有单点(§7.4 六档);受光线合并目标复用既有 `BRICK` token;断言承载复用 `standee_present_values_test` 既有套件与源码锚先例(V5/V7);golden 重锚复用既有工具 `golden_baseline_refresh.sh`;能力行补行沿迭代 43 移交小件登记规格,不另立能力名。
**唯一面**:新增常量 `PLAZA_EDGE` 唯一落点=`ui_kit.gd`(色板 token 单点——`#4C4038` 全仓无既有 token,2026-10-31 grep 证实;禁在 `standee_body` 内联字面值);`name_font_size` 探针键唯一落点=`name_line_probe`(探针共用面单点);名字行几何唯一算式落点=`_name_plate_rect`/`_name_baseline`(本批不改此结构)。2026-10-31 实查无同名/同义标识符冲突(`grep PLAZA` 仅 `TEX_STAGE_PLAZA` 纹理路径族)。
**分层与交互面**:全部改动在 presentation 层与单元测试层——零跨层新依赖、零 Core 触碰、零 ConfigDB/RNG 触碰(无随机、无表值变化);探针消费方向不变(`ui_struct_probe`/测试→`name_line_probe` 只读)。
## 13. 禁为机检文本保留无效成员
本批含常量退役——`STAND_STONE_RIM`/`STAND_STONE_EDGE` 删除后禁留别名常量、转发成员或注释占位凑「零引用」判据;机检键与卡片契约互斥时实现段停卡呈报,禁自造第三条路(code-conventions §1.1 同款)。
## 14. 实现规范预算
**新件三查**:本批零新增文件——三查不适用(写「无新增件」)。
**存量触面一查**:`standee_body.gd`(S13 扫描面内,无基线登记键)——零新增成员/函数,`name_line_probe` 返回字典增键非成员,预期 S13 零新增、既有键持平;`ui_kit.gd` 常量净 −1(删 2 增 1);`cell_base_layer.gd` 零成员变化(引用换值)。达成手段:全部改动为常量引用替换与既有函数体内取值/偏移改写。
**指标例外申报**:无。
**估时合计**:T0 0.1+T1 0.15+T2 0.15+T3 0.1=**0.5 个工作日**(区间 0.4–0.6;切分预案=若 T0 字体度量或 golden 重锚节外生枝超软盒,T2 拆出独立卡,T1 先行不阻塞)。
@@ -0,0 +1,337 @@
# 需求卡片——组合根注册面批(main.gd 类变量归 15:界面注册面+三域句柄容器)
状态行:v1.0 定稿——零待裁点(2026-10-29 用户裁:注册面拓扑一门面+三域容器、合同机器迁入、别名消灭清单整体确认;实现层细目授权 agent 按设计文档执行)
接收方:待办清单条目「零豁免五键清偿批——S13 余量五键真修四卡(2026-10-29 登记,来源=会话提及)」——本卡为其四卡之首(组合根注册面批)。
来源:reports/设计-组合根注册面-2026-10-29.md(2026-10-29 设计闭合,**唯一事实源**);architecture 权威=design/engineering/architecture/architecture.md v1.25(2026-10-29——「模块归属·呈现层」表 ui_registry 行、「呈现层装配与会话生命周期」节屏成员寻址迁注册面句=原 S13 豁免登记注销、RunScope 节)。
## 1. 目标与完成定义
这批做什么:把 main.gd(组合根)里散着的屏壳、覆盖层一级句柄与视觉层合同表收进一个「界面注册面」——一个不挂树的 RefCounted 门面件(ui_registry.gd)加三个按领域分的句柄容器(ui_screens/ui_battle/ui_modals),探针和系统测试对界面元素的引用从 `main.X` 一级地址改成 `main.ui.<域容器>.<句柄>` 三段式地址。同时消灭 7 个探针兼容别名/转发成员、退役 6 个 main 纯转发模态方法。main.gd 类变量从 49 降到 15,S13 基线键真实还债,architecture 在册的 S13 豁免登记随批注销。
**行为零变化**:装配序、信号接线、层序(树序=登记序=z 序)、RunScope 会话生命周期、RNG 消费序全部零变化;探针断言只换地址、不换判据。
完成定义(全部满足才算完):
1. 五闸主证全绿——probe_gate 默认段+`--click`+`--full`、render_gate golden 零差异、run_smoke、run_system_test、run_all 02 单元与集成(main.gd 触组合根=横切面,按 AGENTS.md 关联档口径回落全部套件)。
2. static_scan 过——四新文件零新基线键(S9 全类型标注、S10 头注);main.gd 类变量实测 15(键账对账批末申报,基线 refresh 由用户发令)。
3. 改址完整性自检——按 49 名逐名 grep,`game/` 下旧一级路径 `main.<旧名>`/`root.<旧名>`/`self.<旧名>` 残留=0,报告留档。
4. 产出物落 `reports/`——实现报告、反哺点清单、验收审计报告(独立审计未过不合并)。
**编队注记与估时**:本卡与「战斗编排归约批」(battle_orchestrator 拆 RoundCycle/BattleFacts 两件——architecture v1.25 轴丁设计闭合、待写卡)和「测速循环批」(tune_sim_controller 步进状态机——architecture v1.25 S13 测量面豁免注销设计闭合、待写卡)同代待编排;三批无同文件交叠(本批触面=main.gd+main_ui_build+三流程件+探针/测试面)。与迭代 42(在飞:gdstyle 尾波清偿批+level_runner 商店委托层拆除)**存在文件交叠**——并排卡「level_runner 商店委托层拆除」触面含 main.gd(1 处)、main_adventure_flow(6 处)、main_overlay_flow(2 处),全在本批触面集内(grilling 复核证伪原「无交叠」声明,订正);**本批开工前置=迭代 42 合回 master**,T0 按合回点重测改址计数。另与「呈现层状态件批」共享 ui_input_probe.gd 触面(本批改 main.X 地址面、该批改四处 u.face.* 读面)——同代编排时禁并行、先后皆可。估时区间 **3–4 个工作日**(设计文档 §8),低于迭代冻结线(5 个工作日),可编批;编入哪个迭代由迭代编排段定。
## 2. 现状事实
全部行号为 **2026-10-29 实查——本批编入在 gdstyle 尾波补件批相关迭代之前/之后均须 T0 按 T0 时点 master 重测重锚**。
1. `game/presentation/main.gd`(915 行)共 **49 个类变量**(逐名清点 main.gd:61–148,含 3 个委托属性);S13 基线键 `game/presentation/main.gd|quality/max-class-variables = 49`(阈值 15)在 tools/gdstyle_baseline.txt 在册,architecture v1.25 在册 S13 豁免登记〔2026-10-27「组合根类变量面即探针契约面」〕已随 v1.25 注销、待本批真实还债。
2. 视觉层合同机器四样全在 main.gd:`LAYER_CONTRACT` 常量(main.gd:39–59,19 项含 host/shell/tags 标注)、`_apply_layer_contract()`(main.gd:254–279,全仓唯一 move_child 落位点)、`_layer_node(key)`(main.gd:227–248,19 键 match)、`_register_modal(m, tags)`(main.gd:218–224,anchors preset+flow.register)。
3. 探针兼容别名/接缝成员 7 个:`board_view`(main.gd:95)、`ap_bar`(main.gd:96)——赋值点 main_ui_build.gd:48–49;`_modals`(main.gd:129,赋值 main_ui_build.gd:245);`_row_queue`(main.gd:111–113,委托属性读 `_row_pacer.pending_rows()`);`_seed_edit`(main.gd:74,赋值 main_ui_build.gd:208);`_settlement_stop_pending`(main.gd:134–143,读=is_armed()/写=arm()/disarm());`_deferred_modal`(main.gd:148–150,读 `_modal_flow._deferred`)。
4. main 纯转发模态方法 6 个:`_open_modal_after_playback`(main.gd:156–158)、`_on_pump_ended`(main.gd:161–163)、`_open_modal`(main.gd:282–284)、`_close_modal`(main.gd:285–287)、`_on_modal_closed`(main.gd:288–290)、`_input_locked`(main.gd:292–295)——各自一行转呼 `_modal_flow` 真方法。另有一个纯转发方法同批退役:`_flush_row_queue`(main.gd:516,一行转呼 `_row_pacer.flush`)——`_row_pacer` 句柄迁 ui.screens 后调用方 `pacing_queue_test.gd:103` 改调 `main.ui.screens.row_pacer.flush()`(表 D 归宿行)。
5. 装配承接件 main_ui_build.gd:`build(root)` 八步(main_ui_build.gd:21–29),赋值目标全走 `root.X` 一级成员(main_ui_build.gd:38–51 等);`pump_ended` 注入点 main_ui_build.gd:62;context 束注入 `open_modal/close_modal`(main_ui_build.gd:144、168);`_apply_layer_contract()` 末位调用 main_ui_build.gd:247。
6. 消费面引用量(设计文档 §0 机械统计,T0 实跑重计):探针+系统测试 `main.X` 成员引用约 **867 处**;四个流程承接件(main_adventure_flow/main_overlay_flow/main_debug_flow/main_ui_build)`root.X` 引用约 200–350 处(设计文档 §0 与 §3.2 两处统计口径不一,以 T0 映射表脚本实跑计数为准)。抽查实证:ui_input_probe.gd:34–43(seed_edit/level_screen)、system_test.gd:94–109(LAYER_CONTRACT、_layer_node、V2 句柄等价五连比)、smoke_boot.gd:68–75(`main.get()` 6 处)、pacing_queue_test.gd:108/132/147/149(`_row_queue` 3 处+`board_view` 1 处)、source_guard_test.gd:69–77(V1d 层序单口断言,现扫 main.gd 单文件)、theme_projection_test.gd:724–725(V6 断言 main.gd 含 `debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)` 串)。
7. 留 main 的 14 成员(对账见表 B)全部是组合根自身状态、组合根拆件引用、autoload 读视图;`run_scope` 引用(探针约 173 处)零迁移。
8. 探针段构成(architecture「测试与质量域」probe_gate 行):默认段 ui_input_probe/ui_struct_probe/layout_probe/ui_contrast_probe/screen_flow_probe,`--click` 加 ui_click_probe,`--full` 加 ui_probe/ui_tune_probe/t17_defer_probe/fx_probe。
9. meta_progression_forge_test 经 `load("res://presentation/main.gd")` 直呼 static `build_adventure_params`——留 main,零迁移(main.gd:366–376)。
10. 本批不涉 RNG/数值/存档触面;PARITY 触面(battle_orchestrator.gd)不在 diff,无 MC 义务。
## 3. 设计契约(违反即返工,标 🔴 触碰=L3 停机呈报)
- 🔴 **行为零变化**:装配序与 add_child 顺序、信号连接语句逐条不变;层序合同表条目、顺序、tags、host/shell 标注**逐字零变化**只换宿主文件;`_begin_session`/闲时会话/整节点 queue_free 机制一字不动;RNG 消费序零触碰;呈现层订阅禁令与核心写调用唯一出口纪律零触碰。
- 🔴 **探针断言语义零变化**:改址只换地址不换判据——断言条件、尾缀(既有〔装配生命批 V1〕〔装配生命批 V3〕等保留)、消息语义不动;禁借改址删断言、放宽阈值、缩小扫描面。
- 🔴 **拆分边界纪律五条**(判据权威=code-conventions §1.1,此处写红线不复制定义):①每个拆出单元须有可陈述的职责语句与单一变化原因(四件各一句,见 §12 表,实现报告逐件一行承载);②拆出单元须有集体身份——命不出名字、只能说「剩下的成员」的打包=凑计数袋,禁;③「过阈值(49→15)」只许作发现信号,作拆分或收拢动机即违规;④按纪律拆不出内聚单元→停卡呈报走豁免通道,禁硬拆凑绿;⑤禁保留/新增无效成员与别名凑数——本批方向是净消灭(7 成员+6 方法),实现期发现任何「为保地址兼容」新增的转发壳即违契约。
- 🔴 **禁为机检文本保留无效成员**:禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名;机检键与本卡契约互斥时(如实测出新基线键 NEW 红)一律停卡呈报,不得自造第三条路(详见 §13 条款节)。
- 🔴 **测试削弱申报制**:允许改动的旧断言全集见 §8「测试削弱申报」,未申报的删改由独立验收审计判红。
## 4. 范围边界
做:ui_registry+三域容器建件;合同机器四样迁驻;main.gd 49→15 改形;main_ui_build 与三流程件改址;探针九件+probe_ui_util+smoke_boot+system_test+pacing_queue_test 改址;V1d/V6 源码断言重锚;七别名与六转发方法消灭。
不做(明示去向):
1. architecture.md 直改——实现批禁写 `design/`,ui_registry 行现状化与 main_ui_build 行层序句改指走反哺点通道(§6)。
2. game/tests/README.md 改动——实现批禁写;写卡段义务已核=零增删零改行(§6)。
3. modal_stack.gd/modal_flow.gd 语义改动——两件零触碰,本批只改它们的消费方地址。
4. RunScope/会话生命周期机制、row_pacer 排播时序、T17 挂起编排——零触碰。
5. S13 豁免登记第二/三条(tune_sim_controller、battle_orchestrator)——原文不动,归「战斗编排归约批」「测速循环批」。
6. board_cell_unit/standee_body/board_view 悬停域等结构债——归迭代 42 尾波清偿批,不收件。
7. 基线 refresh——`python tools/gdstyle_gate.py --refresh` 由用户发令人工跑(净减 1 键,低于 10% 骤降守卫,无需 --force);实现批只做键账对账申报。
8. MC/数值验证——无数值改动零跑。
## 5. 关联文档同步面
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|---|---|---|
| design/engineering/architecture/architecture.md(设计权威——实现批禁直改) | ①「模块归属·呈现层」ui_registry 行现登记「实现批建件」态,实现后应现状化;②同表 main_ui_build 行「层序逻辑不在此件——唯一事实源仍是 `main.LAYER_CONTRACT` + `_apply_layer_contract()`」句在实现后失效(唯一事实源迁 ui_registry.LAYER_CONTRACT+ui.apply_layer_contract());③「呈现层装配与会话生命周期」节屏成员寻址迁注册面句 v1.25 已落、实现后核对措辞与实况一致 | 反哺点通道:把上列 ①②③ 逐条列进 `reports/iter<N>-反哺点-<日期>.md`(改哪一节、改成什么、依据=设计文档 §3.3 建议句),用户在会话中裁决后归文档批落文 |
| game/tests/README.md(机械面——实现批禁写) | 本批零新增能力(改址只换地址,能力集不变);受影响存量行核对:「模态覆盖层编排与输入锁」行真实入口(ModalFlow 两个公开方法)与代表用例(modal_flow_test.gd:_init 等)不变;「结算明细带逐行呈现与排播节奏」行代表用例 pacing_queue_test.gd:_init 不变(`文件:函数` 锚不受成员改址影响) | 写卡段义务已当场核完=**零增行、零删行、零改行**;批末收尾只核对表上名字与实际落地一致(改名则回表) |
| design/gdd/ 无关联文档 | 行为零变化,任何系统文档陈述不受影响;层序唯一事实源属 architecture 不属 GDD | 无(本行占位——按规约查不出也要写明) |
## 6. 卡前文件结构图(本批新建/修改文件全列)
| 文件 | 职责 | 新增/复用 | 行为改动? |
|---|---|---|---|
| game/presentation/ui_registry.gd | 界面注册面门面:三容器属性+LAYER_CONTRACT+node(key) 合同寻址+apply_layer_contract(root) 唯一落位口 | 新增(预检 §12) | 非行为改动(新建件承载既有合同——落点变化,对照表行 1/2) |
| game/presentation/ui_screens.gd | 屏壳与全屏容器域句柄册(9 成员) | 新增(预检 §12) | 非行为改动(对照表行 3) |
| game/presentation/ui_battle.gd | 战区视图域句柄册(5 成员) | 新增(预检 §12) | 非行为改动(对照表行 3/4) |
| game/presentation/ui_modals.gd | 模态层域句柄册+ModalFlow 持有+register_layer(14 成员) | 新增(预检 §12) | 非行为改动(对照表行 3/7) |
| game/presentation/main.gd | 组合根改形:49→15;合同机器与转发方法迁出;内部取址改 ui.*;头注追加注册面句 | 修改 | 非行为改动(纯结构——装配/接线/层序/RNG 零变化,四闸主证;对照表行 2/6/7) |
| game/presentation/main_ui_build.gd | 八步赋值目标改 root.ui.*;context 束与 pump_ended 注入改址;_seed_edit/_modals 赋值行退役 | 修改 | 非行为改动(八步顺序/add_child 序/连接语句逐条不变;对照表行 3) |
| game/presentation/main_adventure_flow.gd/main_overlay_flow.gd/main_debug_flow.gd | root.X 同表映射(模态口改指 root.ui.modals.flow;句柄读改 root.ui.*) | 修改 | 非行为改动(对照表行 3/7) |
| game/probe/ 九探针+probe_ui_util.gd(凡 T0 映射表命中的 main.X 引用件) | 旧地址→main.ui.* 机械改写 | 修改 | 非行为改动(断言语义零变化;对照表行 4/6) |
| game/tests/smoke_boot.gd | get() 键改址(4 改 2 留) | 修改 | 非行为改动(对照表行 3) |
| game/tests/system_test.gd | main.X 改址(V1a/V1c/V2 句柄等价/V3 矩阵取址两处) | 修改 | 非行为改动(判据零变化;对照表行 1/5) |
| game/tests/unit/contracts/source_guard_test.gd | V1d 重锚(单文件扫描→全目录扫描,判据强化) | 修改 | 非行为改动(断言改写已申报;对照表行 8) |
| game/tests/unit/presentation/theme_projection_test.gd | V6 断言串改新连接串 | 修改 | 非行为改动(断言改写已申报;对照表行 9) |
| game/tests/unit/presentation/pacing_queue_test.gd | board_view/_row_queue 改址(3+1 处) | 修改 | 非行为改动(对照表行 6) |
不触碰:modal_flow.gd/modal_stack.gd/row_pacer.gd/ui_assembler.gd/run_scope.gd/meta_progression_forge_test.gd(static 入口零迁移)/全部 core 与 data。
## 7. 任务卡 T0..T4
### 7.1 地址契约(全批唯一映射依据——表 A/B/C/D 与设计文档 §1.2 对账表同源)
**表 A:迁入注册面的 28 个 main 成员**(容器内成员名去下划线=公共地址面):
| 现成员(2026-10-29 行号) | 新地址 | 现成员 | 新地址 |
|---|---|---|---|
| `_menu_screen`(:100) | ui.screens.menu_screen | `legend_overlay`(:116) | ui.modals.legend_overlay |
| `_camp_screen`(:101) | ui.screens.camp_screen | `node_overlay`(:117) | ui.modals.node_overlay |
| `_adventure_screen`(:102) | ui.screens.adventure_screen | `level_screen`(:83) | ui.modals.level_screen |
| `_start_overlay`(:79) | ui.screens.start_overlay | `curio_overlay`(:78) | ui.modals.curio_overlay |
| `_battle_root`(:84) | ui.screens.battle_root | `_end_overlay`(:80) | ui.modals.end_overlay |
| `_inter_overlay`(:81) | ui.screens.inter_overlay | `shop_overlay`(:118) | ui.modals.shop_overlay |
| `_inter_label`(:82) | ui.screens.inter_label | `camp_overlay`(:119) | ui.modals.camp_overlay |
| `debug_badge`(:126) | ui.screens.debug_badge | `settings_overlay`(:121) | ui.modals.settings_overlay |
| `_row_pacer`(:105) | ui.screens.row_pacer | `status_panel`(:122) | ui.modals.status_panel |
| `stage`(:91) | ui.battle.stage | `career_overlay`(:123) | ui.modals.career_overlay |
| `log_view`(:92) | ui.battle.log_view | `save_select_overlay`(:124) | ui.modals.save_select_overlay |
| `info_panel`(:93) | ui.battle.info_panel | `confirm_dialog`(:75) | ui.modals.confirm_dialog |
| `board_zone`(:94) | ui.battle.board_zone | `debug_overlay`(:125) | ui.modals.debug_overlay |
| `settlement_fx`(:97) | ui.battle.settlement_fx | `_modal_flow`(:128) | ui.modals.flow |
**表 B:留 main 的 14+1**:db、run_scope、save_manager、meta、settings_store、logger、_debug_entry、_confirm_consumer、_confirm_consumer_slot、_debug、_treasure_ui、_bonus、_camp_pres、_tune_sim(保留理由逐个=组合根自身状态/拆件引用/autoload 读视图,见设计文档 §1.2 第二表)+新增 `ui: UiRegistry`。改后 15,恰在阈内。
**表 C:消灭的 7 个别名/接缝成员**:
| 成员 | 本体 | 消灭后地址 |
|---|---|---|
| board_view(:95) | =board_zone.board_view | ui.battle.board_zone.board_view |
| ap_bar(:96) | =board_zone.ap_bar | ui.battle.board_zone.ap_bar |
| _modals(:129) | =_modal_flow.stack | ui.modals.flow.stack |
| _row_queue(:111) | 委托属性=_row_pacer.pending_rows() | ui.screens.row_pacer.pending_rows()(同一 Array 引用,语义不变) |
| _seed_edit(:74) | =debug_overlay.seed_edit | ui.modals.debug_overlay.seed_edit.text(main._seed_value 唯一消费点) |
| _settlement_stop_pending(:134) | 委托属性读=is_armed()写=arm()/disarm() | run_scope.settlement_stop_timer 直呼同名面 |
| _deferred_modal(:148) | 委托属性=_modal_flow._deferred | ui.modals.flow._deferred |
**表 D:方法/函数级改址**:
| 旧地址 | 新地址 | 消费方 |
|---|---|---|
| main._layer_node(key) | main.ui.node(key) | system_test V1c(实查 source 点 system_test.gd:97;设计文档记 3 处——T0 重计) |
| main._register_modal(m, tags) | ui_modals.register_layer(m, tags)(逐字迁入:anchors preset+flow.register) | 仅 _apply_layer_contract 内部消费,随迁 |
| main._apply_layer_contract() | main.ui.apply_layer_contract(root)(root 作参传入,main_ui_build.gd:247 调用点改) | 装配尾唯一调用点 |
| main._open_modal(m) | main.ui.modals.flow.open(m) | 探针各 1 处+流程件 root. 共约 23 处(T0 重计);main 内部调用点(_on_confirm_accepted 等)同改 |
| main._close_modal(m)/main._on_modal_closed(m) | main.ui.modals.flow.close(m)(信号 bind 目标 main_ui_build.gd:99/209 改 flow.close.bind) | 同上 |
| main._input_locked() | main.ui.modals.flow.locks_input() | 探针 18 处(T0 重计)+main._on_board_action/_on_stop_pressed/_on_refresh_confirmed 内部 |
| main._open_modal_after_playback(m) | main.ui.modals.flow.open_after_playback(m) | 流程件 root. 2 处+探针(T0 重计) |
| main._on_pump_ended | main.ui.modals.flow.pump_ended | row_pacer.pump_ended 注入点 main_ui_build.gd:62 |
| smoke_boot `main.get("_start_overlay"/"debug_badge"/"legend_overlay"/"status_panel")` | `main.get("ui").<域>.<名′>`(get 只解析一层,后续走公共字段) | smoke_boot.gd:70/73/74/75;`get("run_scope")`/`get("settings_store")` 零迁移 |
| source_guard V1d 串 | 重锚:扫全 `game/presentation/*.gd`,`move_child(` 只允许出现在 ui_registry.gd 的 apply_layer_contract 函数体内(从扫单文件变扫全目录——判据强化) | source_guard_test.gd:69–77 |
| main._flush_row_queue() | main.ui.screens.row_pacer.flush()(转发壳随 `_row_pacer` 迁移退役) | pacing_queue_test.gd:103 |
| theme_projection V6 串 | 改断言新串 `ui.modals.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)`(连接语句本体随 T2 改写 main.gd:209) | theme_projection_test.gd:724–725 |
### 7.2 新件接口块(T1 定义,T2 起逐字取用;类型标注义务=S9 过)
```
# game/presentation/ui_registry.gd —— 界面注册面(RefCounted,不挂树,零信号连接,零会话期引用)
class_name UiRegistry
extends RefCounted
const ModalStack := preload("res://presentation/modal_stack.gd")
const LAYER_CONTRACT: Array[Dictionary] = [ …main.gd:39-59 内容逐字迁入,一项不增不减不改… ]
var screens: UiScreens
var battle: UiBattle
var modals: UiModals
func node(key: String) -> Node # 合同寻址:_layer_node 的 19 键 match 逐字迁入
func apply_layer_contract(root: Control) -> void # 落位算法逐字搬运(move_child 单口;登记经 modals.register_layer)
# game/presentation/ui_screens.gd —— 屏壳与全屏容器域(RefCounted,纯字段无方法)
class_name UiScreens
extends RefCounted
var menu_screen: Control
var camp_screen: Control
var adventure_screen: Control
var start_overlay: Control
var battle_root: Control
var inter_overlay: Control
var inter_label: Label
var debug_badge: Button
var row_pacer: RowPacer # 经 preload 常量取型(res://presentation/row_pacer.gd 非 class_name 全局)
# game/presentation/ui_battle.gd —— 战区视图域(RefCounted,纯字段无方法)
class_name UiBattle
extends RefCounted
var stage: BattleStageView
var log_view: BattleLogView
var info_panel: HeroInfoPanelView
var board_zone: BoardZoneView
var settlement_fx: SettlementFxView
# game/presentation/ui_modals.gd —— 模态层域(RefCounted)
class_name UiModals
extends RefCounted
var flow: ModalFlow
var legend_overlay: LegendOverlay
var node_overlay: NodeOverlay
var level_screen: LevelMapScreen
var curio_overlay: CurioPickOverlay
var end_overlay: BattleEndOverlay
var shop_overlay: ShopOverlay
var camp_overlay: CampOverlay
var settings_overlay: SettingsOverlay
var status_panel: StatusPanel
var career_overlay: CareerOverlay
var save_select_overlay: SaveSelectOverlay
var confirm_dialog: ConfirmDialog
var debug_overlay: DebugOverlay
func register_layer(m: Control, tags: Array = []) -> void # _register_modal 逐字迁入
```
(main.gd 现有类型标注逐字沿用:ui_battle/ui_modals 十八句柄的类型名=main.gd:78–125 在用全局类名;ui_screens 三屏壳标 Control,若 T1 实测场景根基类不符,按代拍板白名单第 6 条取实际基类。)
### 7.3 T0——读数重锚与对账(估时 0.5 个工作日)
- [ ] 按 T0 时点 master 重测重锚本卡全部行号与计数:main.gd 类变量实数(gdstyle 读数)、合同机器四样行位、七别名与六转发方法行位、六转发方法的全部调用点清单(main 内部+流程件+探针+system_test 分文件计数)。
- [ ] 49 成员逐一对账:对照本卡表 A/B/C 逐名核对仍在位、仍归该档(迁移 28/留 14/消灭 7)——任何一名对不上=L3 停卡呈报(设计文档 §1.2 对账表为第二依据)。
- [ ] 映射表脚本生成+对账:探针九件+probe_ui_util+smoke_boot+system_test+四流程件的 `main.X`/`root.X` 全量计数与旧→新映射(机械生成+人工复核语义位——属性读写约 750 处、方法调用四类、字符串键 6 处、源码断言 2 条);设计文档 867/350/200 三处统计口径以本次实跑为准并在实现报告记数。
- [ ] 未登记源码断言排查:grep `presentation/main.gd` 于 game/tests+game/probe 全部源码断言面——只许 V1d/V6 两条命中;新发现=L3 停卡补申报,禁自行改判。
- [ ] 迁移函数对账:查 tools/gdstyle_baseline.txt 中 main.gd 的 `_apply_layer_contract`/`_layer_node`/`_register_modal` 源函数键——branches/returns/函数长有超默认登记值的,迁移即拆函数(拆分不改语义)。
- [ ] 孤立验证(改前基线读数):`python tools/gdstyle_gate.py`(采集 main.gd=49 读数)+`godot --headless --path game --script res://tests/run_all.gd -- --only=source_guard`(绿)。
- 取用:本卡 §7.1 四表。产出:重锚后的映射表与对账表(实现报告附件节,T2/T3/T4 消费)。
### 7.4 T1——注册面与三域容器建件(估时 1 个工作日)
- [ ] 按 §7.2 接口块建 ui_registry.gd:LAYER_CONTRACT 自 main.gd:39–59 逐字迁入(ModalStack 常量引用随迁);`node(key)` 19 键 match 逐字;`apply_layer_contract(root)` 落位算法逐字搬运(move_child 单口语义不变)。
- [ ] 建 ui_screens/ui_battle/ui_modals(成员与类型按 §7.2;ui_modals.register_layer 自 main.gd:218–224 逐字迁入);四件全类型标注、`_init` 不收参(容器由门面构造)。
- [ ] 四件 class_name(UiRegistry/UiScreens/UiBattle/UiModals)+跑一次编辑器扫描刷新全局类缓存+同步 .uid(code-conventions 语言纪律 5——T2 main.gd 类型标注 UiRegistry 在 --script 模式可解析的前置);文件头 `##` 注释介绍模块(禁变更叙事、禁文档反向链接)。
- [ ] 孤立验证:`game/tests/static_scan.sh`(S9 四新件零红+S10 头注过+S13 四新件零新键)+`godot --headless --path game --script res://tests/run_all.gd -- --only=source_guard`(V8 全库扫描仍绿——新件零字符串 Callable)。
- 取用:T0 映射表。产出:四件公开面(UiRegistry.new()/.screens/.battle/.modals/.node(key)/.apply_layer_contract(root)/modals.register_layer(m, tags)——T2 逐字消费)。
### 7.5 T2——main.gd 改形与装配/流程件改址(估时 1–1.5 个工作日)
- [ ] main.gd 新增 `var ui: UiRegistry`;`_ready` 在 `_build_ui()` 之前 `ui = UiRegistry.new()`(容器随门面构造;装配在 _ready 建闲时会话之前完成——时序零变化)。
- [ ] 表 A 28 成员迁出:main 内部全部取址同步改 `ui.*`(含 main.gd:199–200 启动态两行、_seed_value:911、_on_confirm_accepted/_start_adventure/_resume_from_menu/_replay_locked_click 等内部调用点改指 ui.modals.flow.*/ui.screens.row_pacer.pending_rows());合同机器四样自 main 删除(随 T1 迁驻)。
- [ ] main_ui_build.gd:八步赋值目标改 `root.ui.<域>.<名′>`(八步顺序、add_child 顺序、连接语句逐条不变);`root._seed_edit` 赋值行(:208)删除;`pump_ended` 注入改 `root.ui.modals.flow.pump_ended`(:62);`root._modals` 赋值行(:245)保留至 T4;context 束 overlay/toast/start_overlay/board_view 键值改址(:137–147、:163–169,注入时点与值不变);`_apply_layer_contract` 调用改 `root.ui.apply_layer_contract(root)`(:247)。
- [ ] 三流程件 root.X 按表 A/D 同表映射(root._open_modal/_close_modal/_open_modal_after_playback/_input_locked → root.ui.modals.flow.*;`_settlement_stop_pending` 写点改 `run_scope.settlement_stop_timer.arm()/disarm()`——写点计数 T0 对账)。
- [ ] smoke_boot.gd get() 改址(4 改 2 留,§7.1 表 D)。
- [ ] source_guard_test.gd V1d 重锚为全目录扫描(尾缀〔注册面 V1〕);theme_projection_test.gd V6 改断言新串(尾缀〔注册面 V2〕)——两条改写即 §8 申报的测试改动全集。
- [ ] 孤立验证:`python tools/run_smoke.py`(冒烟绿=装配链完整)+`godot --headless --path game --script res://tests/run_all.gd -- --only=source_guard,theme_projection,meta_progression_forge`(三套件绿——V1d/V6 重锚后绿、static 入口零变化)+`python tools/gdstyle_gate.py`(main.gd 类变量读数记录——此轮含 7 别名=22,基线键 49 内不红,读数进实现报告)。系统测试与探针此轮预期红(消费面未改址——T3 收口),不构成本卡验证面。
- 取用:T1 四件公开面。产出:main.ui.* 地址面全量生效的组合根(T3 消费)。
### 7.6 T3——探针/系统测试/单元批量改址(估时 1 个工作日)
- [ ] 探针九件+probe_ui_util 按 T0 映射表机械改写:属性读/写改 `main.ui.<域>.<名′>`;四类方法调用按表 D;`var bv = main.board_view` 型局部承接改 `var bv = main.ui.battle.board_zone.board_view`(每文件 1–2 处)。
- [ ] 接缝三件:ui_struct_probe 的 `main._modals` → `main.ui.modals.flow.stack`(登记序断言);t17_defer_probe 的 `main._deferred_modal` → `main.ui.modals.flow._deferred`(挂起态断言);ui_click_probe 的 `_settlement_stop_pending` 读点 → `main.run_scope.settlement_stop_timer.is_armed()`。
- [ ] system_test.gd 改址:V1c 的 `main.LAYER_CONTRACT`→`main.ui.LAYER_CONTRACT`、`main._layer_node`→`main.ui.node`;V2 句柄等价断言(:107–109)改 ui 地址(等价语义不变:ui.battle.board_zone.board_view == ui.screens.adventure_screen.board_view 型);V1a 候选清单与全件 main.X 逐表映射;V3 矩阵 `_face_snapshot` 的 `main._modal_flow`→`main.ui.modals.flow`、`main._row_pacer`→`main.ui.screens.row_pacer`(其余四持件取址不变)。
- [ ] pacing_queue_test.gd:`main.board_view`→`main.ui.battle.board_zone.board_view`(:132);`main._row_queue`→`main.ui.screens.row_pacer.pending_rows()`(:108/:147/:149,实查 3 处)。
- [ ] 改址完整性自检:映射表驱动 49 名逐名 grep——`game/` 下(探针/tests/流程件/main 自身)旧一级路径 `main.<旧名>`/`root.<旧名>`/`self.<旧名>` 残留=0,报告留档(实现报告对照表挂行)。
- [ ] 孤立验证:`tools/probe_gate.sh --click --full`(全段退出码 0)+`python tools/run_system_test.py`(V1–V9 摘要 0 失败)+`python tools/run_smoke.py`+`godot --headless --path game --script res://tests/run_all.gd -- --only=pacing_queue,source_guard`。
- 取用:T2 地址面。产出:全部消费面新地址(T4 前提——别名/转发方法零消费方)。
### 7.7 T4——别名与转发方法消灭+批末收口(估时 0.5–1 个工作日)
- [ ] 删 main.gd 七别名/接缝成员(表 C——此点已零消费方)与六转发方法(§2 第 4 条清单);删 main_ui_build.gd:245 `root._modals` 赋值行;main.gd 类变量终态 **15**。
- [ ] 文件头注随迁改写:main.gd 头注追加一句「界面句柄与层清单在 ui 注册面(ui_registry.gd)」;main_ui_build.gd 头注「层序唯一事实源」句改指 ui_registry(指址改写、词面语义不变)。
- [ ] 逐卡扫尾孤立验证:`tools/render_gate.sh`(golden 零差异)+`tools/probe_gate.sh --click --full`+`python tools/run_smoke.py`+`python tools/run_system_test.py`+`godot --headless --path game --script res://tests/run_all.gd -- --only=pacing_queue,modal_flow,source_guard,theme_projection,meta_progression_forge`。
- [ ] 批末全量:`tools/run_all_gates.sh --code` 全绿(main.gd 触组合根=横切面——02 单元与集成按 AGENTS.md 横切面口径回落全部套件;冒烟/系统测试/静态扫描三件保险恒跑)。里程碑步出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
- [ ] S13 键账对账申报:main.gd 键 49→15 只降不升、四新件零新键;基线 refresh 由用户发令人工跑(--refresh,净减 1 键低于骤降守卫)。
- [ ] **产出物落位卡**(轻闸全绿后、合并前写齐,方算收口):`reports/iter<N>-实现报告-<日期>.md`——各卡状态+实机目检总清单(登记不阻塞:注册面改址后实机过一遍三屏切换与模态开合)+遗留读数义务(基线 refresh)+实现决定记录+改址计数与 grep 自检留档;`reports/iter<N>-反哺点-<日期>.md`——§5 同步面所列 architecture 三条逐条有落点;`reports/iter<N>-验收审计-<日期>.md`——独立验收审计(未参与实现的会话或子代理执行,未过不得合并)。
- 取用:T3 零消费方清单。产出:终态 main.gd(15 var)+三份 reports/ 产出物。
## 8. 验证闸(改动—验收对照表)
本批全部改动为**非行为改动**(结构与地址重构):每行判断依据=改前改后在同一输入上行为一致,由探针/系统测试/冒烟/渲染 golden 四闸主证(真实入口全绿),静态 grep 只作完整性辅证。调试接口/目检两分法:本批无新玩法能力,验证全走自动化载体;用户目检仅一条登记项(T4 实机过三屏与模态开合),登记不阻塞。**静态判据申报(不入对照表,test-conventions §8.4 出口——grilling 复核订正)**:S13 键账对账(main.gd 实测 15、四新件零新键、基线 refresh 由用户发令)与映射表驱动 grep 残留=0 自检无独立运行期提交面——判据=`python tools/gdstyle_gate.py`/`game/tests/static_scan.sh` 退出码与 T3 grep 自检留档,属静态判据+评审注记申报观测面局限;对照表行 2/11 只承载其运行面回归与全量闸半边。
| # | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| 1 | 注册面建件+合同机器迁驻(层序零变化) | 新建 ui_registry.gd(LAYER_CONTRACT main.gd:39–59 逐字迁入);ui_screens/ui_battle/ui_modals 建件 | 系统测试 V1c「合同树序链逐对保序」(system_test.gd:94–106 改址 main.ui.LAYER_CONTRACT/main.ui.node,尾缀〔装配生命批 V1〕保留) | 系统测试 | `python tools/run_system_test.py` | 运行期树序逐对保序=LAYER_CONTRACT 序(get_index 链——层序零变化的运行期提交面) | 既有:模态覆盖层编排与输入锁 |
| 2 | main.gd 改形回归(49→15 结构改形) | main.gd:61–148 改形(28 迁出+7 消灭+1 新增);六转发方法退役 | 批末 run_smoke+run_system_test 双宿主摘要 0 失败(改形回归主证;S13 键账静态判据见域口径申报) | 冒烟+系统测试 | `python tools/run_smoke.py`;`python tools/run_system_test.py` | 双宿主摘要 0 失败(改形后全链路回归面) | 既有:模态覆盖层编排与输入锁 |
| 3 | 装配与流程件改址(root.ui.*) | main_ui_build.gd 八步赋值目标;三流程件+context 束+pump_ended 注入点;smoke_boot.gd:70/73/74/75 | 冒烟主屏存活面六断言(smoke_boot.gd:64–75 改址后全绿)+系统测试 V2「组合根句柄指向屏壳成员」等价断言(system_test.gd:107–109 改址,五连比同指) | 冒烟+系统测试 | `python tools/run_smoke.py`;`python tools/run_system_test.py` | 启动态装配句柄在座且等价断言两侧同指一对象(多一跳取到同一引用) | 既有:模态覆盖层编排与输入锁 |
| 4 | 探针面批量改址(约 867 处) | 探针九件+probe_ui_util 旧地址→main.ui.*(只换地址不换判据) | probe_gate 全段退出码 0(ui_input_probe 全 case/ui_struct_probe/layout_probe/ui_contrast_probe/screen_flow_probe+--click ui_click_probe+--full ui_probe/ui_tune_probe/t17_defer_probe/fx_probe) | 探针 | `tools/probe_gate.sh --click --full`(单件形态:`godot --headless --path game res://probe/ui_input_probe.tscn` 等) | 探针经新地址取到同一节点并驱动真实输入路径(改址完整性的第一消费方主证) | 既有:棋盘悬停反馈(三语义光标/目标格边框/路径预览点迹) |
| 5 | 系统测试面改址+RunScope 生命周期零变化 | system_test.gd main.X 改址;V3 矩阵取址两处(_modal_flow→main.ui.modals.flow、_row_pacer→main.ui.screens.row_pacer) | 系统测试 V1–V9 全绿(含 V3a–V3i 九阶段×五引用面矩阵,尾缀〔装配生命批 V3〕保留) | 系统测试 | `python tools/run_system_test.py` | 第五引用面逐面同源比对(回调悬挂=红——存活性矩阵判据零变化) | 既有:模态覆盖层编排与输入锁 |
| 6 | 别名/接缝成员消灭(7 个) | main.gd:74/95/96/111/129/134/148 消灭;消费方改址(pacing_queue_test.gd:108/132/147/149、ui_struct_probe、t17_defer_probe、ui_click_probe、main 自身 4 处 _row_queue) | run_all 选跑 pacing_queue 绿(排空零分叉/零丢行/延迟双态/会话清队断言改址后判据不变)+ui_struct_probe 登记序断言+t17_defer_probe 挂起态断言 | 单元+探针 | `godot --headless --path game --script res://tests/run_all.gd -- --only=pacing_queue`;`tools/probe_gate.sh --full` | 属性链与别名取到同一对象引用(断言逐条等价——同一 Array/同一节点的可观测面) | 既有:结算明细带逐行呈现与排播节奏 |
| 7 | 六个 main 纯转发模态方法退役 | main.gd:156/161/282/285/288/292 退役;调用方改指 ui.modals.flow 五个真方法+注入点 main_ui_build.gd:62 | modal_flow 单元套件绿+系统测试 V3g 模态开合段+ui_click_probe 模态开合路径 | 单元+系统测试+探针 | `godot --headless --path game --script res://tests/run_all.gd -- --only=modal_flow`;`python tools/run_system_test.py`;`tools/probe_gate.sh --click` | 经真方法开合编排(遮挡互斥/锁谓词/挂起兜底行为面零变化——开合后可见性与锁态提交面) | 既有:模态覆盖层编排与输入锁 |
| 8 | V1d 源码断言重锚 | source_guard_test.gd:69–77 重锚为全目录扫描(move_child 只许在 ui_registry.gd apply_layer_contract 体内) | 「V1d 层序落位单口」重锚断言,尾缀〔注册面 V1〕 | 单元 | `godot --headless --path game --script res://tests/run_all.gd -- --only=source_guard` | 源码契约扫描面(扫描面>0 前置——全 presentation 目录零第二落位点) | 既有:模态覆盖层编排与输入锁 |
| 9 | V6 源码断言改串 | theme_projection_test.gd:724–725 改断言新连接串 | 「V6 转储入口连接随迁」新串断言,尾缀〔注册面 V2〕 | 单元 | `godot --headless --path game --script res://tests/run_all.gd -- --only=theme_projection` | main.gd 源码含新连接串(同一连接的宿主面改写) | 既有:UI 全局主题与按钮五态(含对比度锚) |
| 10 | 视觉零变化主证 | 全批呈现层地址重构(像素面零改动) | render_gate golden 零差异(shot_probe 七屏+菜单 golden+棋盘 golden) | 探针/golden | `tools/render_gate.sh` | 截图与基线图逐像素容差零差异(视觉零变化的独立主证——出现任何此前不存在的差异即 L3) | 既有:站桩敌人呈现与结算演出落点 |
| 11 | 改址完整性收口 | 全批改址面(49 名逐名) | 批末代码域闸全绿(`tools/run_all_gates.sh --code`,02 横切面回落全量套件);映射表驱动 grep 自检=T3 步骤义务(静态判据,见域口径申报) | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执 | 既有:模态覆盖层编排与输入锁 |
**测试削弱申报**(实现期允许的旧断言改动全集,超出即审计判红):
1. V1d:扫描面从「main.gd 单文件」扩为「全 game/presentation/*.gd」——判据强化,非削弱。
2. V6:断言串从 main.gd 旧连接串改为新连接串——同一连接的宿主面改写,断言不删除。
3. 冒烟/系统测试/探针/pacing 各断言:只改地址字面,判据与既有尾缀不动——零删除、零放宽、零换下层直调。
4. 七别名+六转发方法随批退役=test-conventions §5.5 同生死条款的执行:消费方断言已全部改指新地址,旧地址无断言残留(T3 grep 自检为证)。
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表、抽查判据绑定面、检查测试削弱、核对静态判据行的观测面局限注记;未过不得合并。
## 9. 异常分级
- **L1 自救**:单点漏改址→grep 定位补改后重跑该闸段;`--only` 选跑零命中(用法错 exit 2)→修正选词重跑;探针段超时→孤立复跑该段定位。
- **L2 记录继续**:同一红闸连续 3 次同因→升级记录并在实现报告「实现决定记录」节记因后继续最窄确认。
- **L3 停机写报告等用户**:
1. **行为零变化契约触探**——任一闸出现改前不存在的红且定位为层序/时序/接线行为变化;render golden 出现任何此前不存在的差异。
2. **机检预算互斥=上游缺陷停卡退回写卡段**——四新件或 main 终态实测超 §14 预算、或 static_scan 出基线外 NEW 红键;禁开豁免、禁自造第三条路。
3. T0 对账失败——49 名逐一对账与表 A/B/C 任一名对不上,或发现未登记的 main.gd 源码字符串断言。
4. 拆分边界④触发——按纪律拆不出内聚单元(本设计已拆出,若实现期发现表 A 归档错误的成员强行迁入会破集体身份时适用)。
## 10. 代拍板白名单(逐条列,超清单记实现报告「实现决定记录」)
1. 改址机械改写:映射表内旧→新地址的行内改写、被改行注释内旧地址字面同步更新(判据不动)。
2. 重锚断言的消息文字措辞(V1d/V6 两条——尾缀〔注册面 V1〕〔注册面 V2〕与判据不变)。
3. 迁移函数/成员头注按新宿主改写指址(词面随迁、语义不改)。
4. 探针内局部承接变量名保留或内联(`var bv` 型——判据不变)。
5. grep 自检留档在实现报告内的节名与格式。
6. ui_screens 三屏壳句柄静态类型在 Control 与实测场景根实际基类之间取(判据=S9 过+赋值编译过;成员名与地址面不变)。
7. main 内部调用点在 T2 先改真方法、转发方法留至 T4 删除的批内中间态时序(终态零转发壳)。
## 11. 待裁点
**零待裁点,开工门=U0。** 设计文档 §8 五项待裁点已于 2026-10-29 用户全裁:①注册面拓扑=一门面+三域容器;②合同机器随批迁入注册面;③七别名消灭+六转发方法退役+容器内成员去下划线——清单整体确认;④run_all 关联档口径=main.gd 触组合根按横切面回落全部套件;⑤编批归迭代编排段定(本卡估时 3–4 个工作日在冻结线内)。
## 12. 落位与可复用性预检
**复用面**(逐个排除既有件——为什么不能复用):
- **modal_flow.gd(ModalFlow)**:模态编排纯逻辑——开合编排、输入锁谓词、T17 挂起。它管行为不管地址;注册面管「元素清单、层序、寻址」,是它的宿主容器(flow 挂在 ui_modals 里)。职责语句不同,不能拿它当注册面。
- **modal_stack.gd(ModalStack)**:登记册数据结构(遮挡记账),被 ModalFlow 持有;层序合同表与三域寻址不归它。非候选。
- **ui_assembler.gd**:无态静态装配工厂——装配期造控件用完即弃,不持任何句柄;注册面是随 main 常驻的句柄容器。把「造」和「持」并进一件会让静态工厂变有态,破坏现行职责。
- **run_scope.gd**:局态节点——装的是会话状态不是界面元素清单;173 处探针引用零迁移义务已核。非候选。
- 形态先例(非复用对象):ConfigDB 迭代 41 三张域容器(config_domain_combat/run/crossrun.gd)解决过同构的成员数超标问题——本批三容器即同一形态在呈现层的落法。
**唯一面**:层序合同唯一事实源随迁 ui_registry.LAYER_CONTRACT(main.gd 侧删除,不双写);合同寻址唯一口=ui_registry.node();模态登记唯一口=ui_modals.register_layer()。2026-10-29 实查 game/presentation/ 下无 ui_registry/ui_screens/ui_battle/ui_modals 同名或同义件(architecture v1.25 登记「实现批建件」即此四名,盘上未建)——新增无第二落点。
**分层与交互面**(调用路径显式):
- 读地址路径:views/探针/流程件 → `main.ui.<域容器>.<句柄>` → 容器字段 → 节点。三段式每段有领域含义(组合根→注册面→域→元素),与 ConfigDB 的 `db.<域容器>.<成员>` 两段式同构。
- 注册面禁反向持有:不持 main、不持 run_scope、零信号连接、零核心写调用、零 `_process`——依赖单向(views→信号→组合根→core)零触碰;组合根单点装配不变(信号连接仍在组合根与 main_ui_build)。
- 构造路径:main._ready 建 `ui = UiRegistry.new()`(门面自建三容器),main_ui_build 八步向容器登记;四件全 RefCounted 不挂树(节点本体树位与 add_child 归属零变化——row_pacer 仍 add_child 到 main,注册面只持引用)。
- Core 零 UI 红线零触碰:本批改动全部在 game/presentation/ 与测试面。
## 13. 禁为机检文本保留无效成员(重构类卡必填条款)
禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名:main.gd 终态 15 个类变量必须个个有 §7.1 表 B 的职责理由,禁留空占位或替身成员凑计数;四新件的字段必须个个是表 A 在册句柄或 flow,禁加「以防万一」的空槽。机检键与本卡契约互斥时(如实测出新基线键 NEW 红、或键账与申报不符),实现段一律停卡呈报走 L3,不得自造第三条路。判据权威=code-conventions §1.1⑤。
## 14. 实现规范预算(gdstyle 默认阈值:公开方法 ≤20、类变量 ≤15、单函数分支 ≤8、返回 ≤6)
| 新件 | 公开方法 | 类变量 | 阈值判定 |
|---|---|---|---|
| ui_registry.gd | 2(node/apply_layer_contract) | 3(三容器属性;LAYER_CONTRACT 为常量不占 var) | 双达 |
| ui_screens.gd | 0 | 9 | 双达 |
| ui_battle.gd | 0 | 5 | 双达 |
| ui_modals.gd | 1(register_layer) | 14(13 句柄+flow) | 双达(余量 1——第 14 个模态句柄出现即触发拆分审视,作发现信号用) |
- 迁移函数对账:`_apply_layer_contract`/`_layer_node`/`_register_modal` 自 main.gd 迁入,T0 查 gdstyle_baseline.txt 源函数键——超默认登记值的迁移即拆函数(拆分不改语义);node(key) 的 19 键 match 与合同落位循环预期在默认分支阈内(设计文档 §7 同判断),实测出新键=基线外 NEW 红=L3 停卡(禁开豁免)。
- 存量触面一查:本批触碰的 presentation 存量件(main.gd/main_ui_build.gd/三流程件)预期「S13 键较登记值只降不升」——main.gd 49→15;main_ui_build 与三流程件零成员变化(仅语句内地址改写);达成手段=表 A/C 全量迁移+别名净消灭。
- 指标例外申报:无(本批不需要任何超默认面;main.gd 改后 15 在阈内,S13 豁免登记注销后不留新例外)。
## 15. 源码级断言与探针契约面的随批义务(防漏项清单)
1. V1d(source_guard_test.gd:69–77)与 V6(theme_projection_test.gd:724–725)随批重锚——本卡已全量排查 main.gd 源码断言面仅此两条(T0 复核义务不豁免)。
2. 探针段覆盖义务:probe_gate 必须 `--click`+`--full` 全跑——ui_click_probe 是最大改址户,只跑默认段会漏最深的改址面。
3. 冒烟 V1 族 get() 键、系统测试 V1c/V2/V3 取址、pacing `_row_queue` 三处——都在 §7.1 表 D 与 T3 清单内,T0 映射表生成后逐条打钩对账。
+4 -5
View File
@@ -5,8 +5,8 @@
## 当前位(只回答「现在该做什么」;三行内随批次换代,禁止在此扩写成状态快照)
- **阶段**:M3——Alpha 版本的完整实现(2026-10-09 立规:未明令宣布完成前停留 M3,迭代只排 M3 需求,M4 不排期;根目录 stage.txt 为旧阶段开关仅存档)。
- **迭代位**:迭代 1–45 全部已完成——迭代 45(呈现层状态件+gdstyle 尾波补件两卡)2026-10-31 合回(合并提交 f817ab2d,独立验收审计通过零缺漏,行为零变化),批后收尾闭合(2026-10-31):反哺点七条处置闭合(architecture 六条落文=breakpoint_store/board_hover/单格两件/站桩两件四行现状化+构造句改述与 7 项引用束/15 贴顶实况+gdstyle 截断缺陷节清偿结果;第 7 条工具观察迁待办新条)、目检三项登记体验验证清单 ㊸、产出物退役九件(两报告/反哺点/审计/两停机呈报+两卡+两设计文档)、worktree 两件清理(iter45+assert-slim-b2)。迭代 44 收尾记录见 git 提交 9eedf896。
- **下一步**:迭代 46 待开工(编排提交 da24047c:换图系双基座七件批+站桩名字行提档与地台色合并批+棋盘格边长口径收口批三卡,≈9.125 个工作日)——开工由用户经 iter 发令。待编池:营地交互重做卡⑤(排卡④之后)、卡④(美术设计已闭合=样张过审 2026-10-31+art-bible v3.24 §6.2,待用户发令重立卡)、冒险前资源配比预设(冻结备选)、迭代 30 原卡②③(2026-10-17 已剔出迭代 30 规划、退回待设计,待美术/设计定稿后重新立卡)。
- **迭代位**:迭代 1–43 全部已完成——迭代 43(战斗一屏样张批)2026-10-30 合回(合并提交 3b66d1a6),同日完成批后收尾(反哺点 13 条处置闭合:落文四处 art-bible v3.23/art-prompts v2.10/战斗表现 v1.2/test-conventions 旧句清偿,消解三条,移交待办清单两条目;产出物与两卡需求卡片退役;方向稿目检登记体验验证清单 ㊶)。迭代 42 收尾记录见 git 提交 7ec2af58。
- **下一步**:迭代 44 待开工(组合根注册面+战斗编排归约+测速循环三卡,编队前置=迭代 42 合回已满足)——开工前宜裁 PARITY 对拍漂移查因/重锚(闸 11 触面)与迭代 43 批后待裁三点(待办清单在册)。待编池:营地交互重做卡⑤(排卡④之后)、卡④(已退回美术设计段,美术设计闭合后重立卡)、冒险前资源配比预设(冻结备选)、迭代 30 原卡②③(2026-10-17 已剔出迭代 30 规划、退回待设计,待美术/设计定稿后重新立卡)。批后可裁事项:gdstyle 基线 refresh 收账随零豁免各批合回一次人工 `--refresh --force`(待办清单在册)。
## 里程碑时间线
@@ -77,9 +77,8 @@
| 迭代 41 | 已完成(2026-10-29) | 已退役(git 历史可考) | 2026-10-27 | ≈13.15 个工作日〔用户特批 2026-10-27 超 10 个工作日〕 | 已退役(git 历史可考) |
| 迭代 42 | 已完成(2026-10-29) | 已退役(git 历史可考) | 2026-10-29 | ≈5.375 个工作日 | 已退役(git 历史可考) |
| 迭代 43 | 已完成(2026-10-30) | 已退役(git 历史可考) | 2026-10-29 | ≈6.5 个工作日 | 已退役(git 历史可考) |
| 迭代 44 | 已完成(2026-10-30) | 已退役(git 历史可考) | 2026-10-29 | ≈7.875 个工作日 | 已退役(git 历史可考) |
| 迭代 45 | 已完成(2026-10-31) | 已退役(git 历史可考) | 2026-10-29 | ≈7 个工作日 | 已退役(git 历史可考) |
| 迭代 46 | 待开工 | reports/需求卡片-换图系双基座七件批-2026-10-31.md<br>reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md<br>reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md | 2026-10-31 | ≈9.125 个工作日 | — |
| 迭代 44 | 待开工 | reports/需求卡片-组合根注册面批-2026-10-29.md<br>reports/需求卡片-战斗编排归约批-2026-10-29.md<br>reports/需求卡片-测速循环批-2026-10-29.md | 2026-10-29 | ≈7.875 个工作日 | — |
| 迭代 45 | 待开工 | reports/需求卡片-呈现层状态件批-2026-10-29.md<br>reports/需求卡片-gdstyle尾波补件批-2026-10-29.md | 2026-10-29 | ≈7 个工作日 | — |
- 迭代 38 收口为带红合并〔用户特批 2026-10-26〕:09 静态扫描 S13 gdstyle 恶化 2 条经批准记入基线存量红(git 提交 c4c69701),卡② 停卡退上游,处方已于 2026-10-26 修订定稿 v1.3(git 提交 b3ea7532),随「gdstyle 存量债全量清偿」条目波 7 待编排。
- 迭代 39 收口为带红合并〔用户特批 2026-10-27〕:02 EV 锚 goblin 0.9148∉[1.00,1.20](触发器批有意行为改动,fp f74b9a48ef4a)已随 EV 锚重锚批换带归绿(git 提交 ba5105aa);08d 生涯.md:55 行号漂移已随 2026-10-28 批后收尾反哺落文修复归绿。三卡拆分后终局 gdstyle 恶化 2 条(battle_orchestrator max-file-length、level_map_screen max-class-variables)归迭代 40 卡①还债与迭代 41 存量债序列。
+1 -6
View File
@@ -69,12 +69,7 @@ entry = {"commit": tip,
try:
with open(gates_dir + "/receipt.json", encoding="utf-8") as f:
r = json.load(f)
# 回执只有确属本跑(commit 对齐)才采信——闸在起手自检即中止时不写新回执,
# 盘上旧回执是上一跑的读数,当作本次红项会撒谎(2026-10-31 实跑缺陷修正)
if r.get("commit") == tip:
entry["red_gates"] = [g["name"] for g in r.get("gates", []) if str(g.get("exit")) != "0"]
else:
entry["note"] = "闸未跑完(起手自检或早期中止,回执为上一跑旧件)——红项读数不可用,看 key_lines 与 ci.log"
entry["red_gates"] = [g["name"] for g in r.get("gates", []) if str(g.get("exit")) != "0"]
except Exception:
pass
try:
+14 -3
View File
@@ -5,8 +5,19 @@
# 判定=键不在基线红(NEW)、同键数值较登记值恶化红(WORSENED)、改小/持平不红。
# 重生成=人工专用:python tools/gdstyle_gate.py --refresh(禁清基线换绿;改规则口径/工具升级须随批
# 重生成;新键数较旧基线少逾 10% 须加 --force 显式确认)。
game/core/adventure_runner.gd|quality/max-class-variables = 23
game/core/adventure_runner.gd|quality/max-class-variables = 28
game/core/battle_orchestrator.gd|quality/max-class-variables = 38
game/core/battle_orchestrator.gd|quality/max-file-length = 1035
game/core/level_runner.gd|quality/max-class-variables = 30
game/core/level_runner.gd|quality/max-public-methods = 33
game/presentation/views/board_view.gd|quality/max-class-variables = 31
game/core/level_runner.gd|quality/max-public-methods = 50
game/core/level_runner.gd|quality/max-returns|_enter_node = 7
game/presentation/main.gd|quality/max-class-variables = 49
game/presentation/tune_sim_controller.gd|quality/await-in-loop|'await' used inside a loop
game/presentation/views/board_cell_unit.gd|quality/max-class-variables = 30
game/presentation/views/board_view.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop
game/presentation/views/board_view.gd|quality/max-branches|_paint_cell = 24
game/presentation/views/board_view.gd|quality/max-class-variables = 32
game/presentation/views/board_view.gd|quality/max-function-length|_paint_cell = 139
game/presentation/views/board_view.gd|quality/max-local-variables|_paint_cell = 22
game/presentation/views/board_view.gd|quality/max-public-methods = 27
game/presentation/views/standee_body.gd|quality/max-class-variables = 19
+1 -9
View File
@@ -12,15 +12,7 @@ REPO="$(git rev-parse --show-toplevel)"
[ -f "$(git rev-parse --git-dir)/MERGE_HEAD" ] && exit 0
RC=0
# 收暂存清单(增量档输入):quotepath 关闭保中文名原样,NUL 转行落临时文件传闸机
_LIST="$(mktemp "${TMPDIR:-/tmp}/gates-staged.XXXXXX")" || _LIST=""
if [ -n "$_LIST" ]; then
git -C "$REPO" -c core.quotepath=false diff --cached --name-only -z | tr '\0' '\n' >"$_LIST"
bash "$REPO/tools/run_all_gates.sh" --docs --staged "$_LIST" || RC=$?
rm -f "$_LIST"
else
bash "$REPO/tools/run_all_gates.sh" --docs || RC=$?
fi
bash "$REPO/tools/run_all_gates.sh" --docs || RC=$?
if [ "$RC" -eq 0 ]; then
exit 0
elif [ "$RC" -eq 1 ]; then
-24
View File
@@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
"""战斗屏布局表读取(`game/data/tables/battle_layout.tres` 单点)。
样张渲染工具不另立布局常量:B 区高 / C 区宽 / AP 条高 / 明细带两态高一律读该表,
与游戏侧 `ConfigDb.presentation.battle_layout` 同源。
"""
import os
import re
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TABLE_PATH = os.path.join(ROOT, "game", "data", "tables", "battle_layout.tres")
FIELDS = ("stage_h", "info_panel_w", "ap_bar_h", "log_band_h_dig", "log_band_h_settle")
def load(path=TABLE_PATH):
with open(path, encoding="utf-8") as f:
text = f.read()
vals = {}
for key in FIELDS:
m = re.search(r"^%s\s*=\s*(\d+)\s*$" % key, text, re.M)
if m is None:
raise ValueError("battle_layout.tres 缺字段或非整数值: %s" % key)
vals[key] = int(m.group(1))
return vals
+27 -41
View File
@@ -35,21 +35,9 @@ import re
import hashlib
from collections import deque
from layout_config import load as load_layout
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MAIN_ROOT = "D:/code/g-plan2"
CANVAS_W = 1280
CANVAS_H = 800
_LAYOUT = load_layout()
B_H = _LAYOUT["stage_h"] # B 区高(布局表单点——与游戏侧同源)
C_W = _LAYOUT["info_panel_w"] # C 区宽
D_X = C_W + 4 # D 区左边 = C 区宽 + 列间距
D_W = CANVAS_W - D_X # D 区宽
BAND_DIG = _LAYOUT["log_band_h_dig"] # 明细带收起高
AP_H = _LAYOUT["ap_bar_h"] # D 区 AP 条高
def asset_root(*parts):
"""素材路径两级回退:worktree → 主检出(素材不入 git,fresh worktree 缺位)。"""
@@ -650,11 +638,10 @@ def draw_standee(canvas, ox, oy, kind, name, enemy_id, order=1, armor="light",
def render_battle_screen():
"""B/C/D 三区全内容 1280×800 mockup。"""
scr = Image.new("RGBA", (CANVAS_W, CANVAS_H), (24, 20, 17, 255))
scr = Image.new("RGBA", (1280, 800), (24, 20, 17, 255))
d = ImageDraw.Draw(scr)
c_y = B_H + 4
# ── B 区(高读布局表)全宽 ──
d.rectangle([0, 0, CANVAS_W, B_H], fill=UIKIT["PANEL_STAGE"], outline=UIKIT["PANEL_BORDER"])
# ── B 区 252px 全宽 ──
d.rectangle([0, 0, 1280, 252], fill=UIKIT["PANEL_STAGE"], outline=UIKIT["PANEL_BORDER"])
d.text((640, 14), "第 3 轮 · 挖掘", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22), anchor="mm")
draw_standee(scr, 40, 34, "hero", "主角", "", hp=0.7, statuses=("attack_up", "ac_up"))
d.text((560, 120), "兑现演出区\n(结算期:光带 → 弹道 → 受击)", font=ImageFont.truetype(ZH_FONT, 13),
@@ -663,34 +650,33 @@ def render_battle_screen():
draw_standee(scr, 973, 34, "enemy", "熊地精", "bugbear", order=2, armor="light", hp=0.8, intent="charge",
statuses=("alert",))
draw_standee(scr, 1128, 34, "enemy", "食人魔酋长", "ogre_chief", order=3, armor="light", hp=1.0, intent="buff")
# 明细带(挖掘期收起——高读布局表)
d.rectangle([0, B_H - BAND_DIG, CANVAS_W, B_H], fill=(90, 80, 66))
d.text((10, B_H - BAND_DIG // 2), "── 第 3 轮 ── 地精 受到 4 点伤害(增伤 ×1)· 熊地精 蓄力(重击预告)…", font=F_LBL2,
# 明细带(挖掘期收起 24px)
d.rectangle([0, 228, 1280, 252], fill=(90, 80, 66))
d.text((10, 240), "── 第 3 轮 ── 地精 受到 4 点伤害(增伤 ×1)· 熊地精 蓄力(重击预告)…", font=F_LBL2,
fill=(232, 222, 200), anchor="lm")
# ── C 区(宽读布局表)──
d.rectangle([0, c_y, C_W, CANVAS_H], fill=UIKIT["PANEL_HERO"], outline=UIKIT["PANEL_BORDER"])
d.rectangle([8, c_y + 8, C_W - 8, c_y + 84], outline=(120, 106, 88), width=1) # 攻击数据框(直角细线)
d.text((16, c_y + 20), "攻击 6", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((16, c_y + 42), "(+50% 增伤 ×1)", font=F_LBL2, fill=(140, 90, 30))
d.text((16, c_y + 62), "护甲 AC 12", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((8, c_y + 96), "魔法道具槽(预留)", font=F_LBL2, fill=(120, 110, 98))
# ── C 区 240×548 ──
d.rectangle([0, 256, 240, 800], fill=UIKIT["PANEL_HERO"], outline=UIKIT["PANEL_BORDER"])
d.rectangle([8, 264, 232, 340], outline=(120, 106, 88), width=1) # 攻击数据框(直角细线)
d.text((16, 276), "攻击 6", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((16, 298), "(+50% 增伤 ×1)", font=F_LBL2, fill=(140, 90, 30))
d.text((16, 318), "护甲 AC 12", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((8, 352), "魔法道具槽(预留)", font=F_LBL2, fill=(120, 110, 98))
for i in range(4):
d.rectangle([8 + i * 40, c_y + 112, 8 + i * 40 + 34, c_y + 146], outline=(120, 106, 88), width=1)
d.text((8, c_y + 160), "本关强化列表(预留)", font=F_LBL2, fill=(120, 110, 98))
d.rectangle([8 + i * 40, 368, 8 + i * 40 + 34, 402], outline=(120, 106, 88), width=1)
d.text((8, 416), "本关强化列表(预留)", font=F_LBL2, fill=(120, 110, 98))
# ── D 区 ──
d.rectangle([D_X, c_y, CANVAS_W, CANVAS_H], fill=(34, 29, 24), outline=UIKIT["PANEL_BORDER"])
# 顶行:停手/图例/刷新+AP 点阵+RAP(坐标随 C 区宽平移)
dx = D_X + 8
d.rectangle([dx, c_y + 6, dx + 52, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((dx + 26, c_y + 17), "停手", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([dx + 58, c_y + 6, dx + 102, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((dx + 80, c_y + 17), "?图例", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([dx + 108, c_y + 6, dx + 176, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((dx + 142, c_y + 17), "刷新 8|剩2", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([244, 256, 1280, 800], fill=(34, 29, 24), outline=UIKIT["PANEL_BORDER"])
# 顶行:停手/图例/刷新+AP 点阵+RAP
d.rectangle([252, 262, 304, 284], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((278, 273), "停手", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([310, 262, 354, 284], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((332, 273), "?图例", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([360, 262, 428, 284], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((394, 273), "刷新 8|剩2", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
for i in range(6):
col = (232, 200, 120) if i < 4 else (120, 106, 88)
d.ellipse([dx + 188 + i * 18, c_y + 10, dx + 200 + i * 18, c_y + 22], fill=col)
d.text((dx + 300, c_y + 17), "RAP 6/6", font=F_LBL2, fill=(200, 180, 140), anchor="lm")
d.ellipse([440 + i * 18, 266, 452 + i * 18, 278], fill=col)
d.text((552, 273), "RAP 6/6", font=F_LBL2, fill=(200, 180, 140), anchor="lm")
return scr
@@ -952,8 +938,8 @@ def main():
# 三区 mockup
scr = render_battle_screen()
# D 区主样张板(10×10@45px=450×450)嵌进 mockup D 区(工具行之下居中)
bx = D_X + (D_W - board.size[0]) // 2
by = B_H + 4 + AP_H + (CANVAS_H - B_H - 4 - AP_H - board.size[1]) // 2
bx = 244 + (1036 - board.size[0]) // 2
by = 256 + 34 + (800 - 256 - 34 - board.size[1]) // 2
scr.paste(board.convert("RGB"), (bx, by), board)
canvas.paste(scr, (20, 52))
d.text((20, 866), "三区 mockup(1280×800 实比):B 区站桩(主角立绘 48×48 朝右/敌字形占位 ▂▆█/顺位/意图/名行=护甲图标+纯名字)",
+34 -46
View File
@@ -40,7 +40,7 @@ v3.0 口径(方向稿=目检先行闸,冻结归机检):
迭代 43 扩展(2026-10-29 战斗一屏样张批):整屏样张两版出口(1280×800 基座 + 1920×1080 全局等比
1.35×+左右黑边,§8.2 1920 档策略)+ art-bible §1.4 整屏四项断言(S1–S4)+ 战斗表现五项断言(P1–P5)
+ S3b 图类型色板变体第二套 + S5 三组关键帧条 + S6 描边同屏对照与遮挡对照组 + V1 两版尺寸断言
+ V10 改前/改后基线并排对拍;分区几何读 game/data/tables/battle_layout.tres(与游戏侧同源)。
+ V10 改前/改后基线并排对拍;B 区高按代码现状 252 出样(art-bible §8.2 文 270 差异只登记不修)。
"""
from PIL import Image, ImageDraw, ImageFilter, ImageChops, ImageFont
import os
@@ -50,8 +50,6 @@ import re
import hashlib
from collections import deque
from layout_config import load as load_layout
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MAIN_ROOT = "D:/code/g-plan2"
@@ -847,22 +845,14 @@ def draw_standee(canvas, ox, oy, kind, name, enemy_id, order=1, armor="light",
canvas.alpha_composite(gray, (ox, oy))
B_H = 252 # B 区高=代码现状(battle_stage_view custom_minimum_size 252;§8.2 文 270 差异只登记不修)
CANVAS_W = 1280 # 基座档(art-bible §8.1)
CANVAS_H = 800
_LAYOUT = load_layout()
B_H = _LAYOUT["stage_h"] # B 区高(布局表单点——与游戏侧同源)
C_W = _LAYOUT["info_panel_w"] # C 区宽
D_X = C_W + 4 # D 区左边 = C 区宽 + 列间距
D_W = CANVAS_W - D_X # D 区宽
BAND_DIG = _LAYOUT["log_band_h_dig"] # 明细带收起高
BAND_SETTLE = _LAYOUT["log_band_h_settle"]
AP_H = _LAYOUT["ap_bar_h"] # D 区 AP 条高
def draw_base(canvas_w=CANVAS_W, canvas_h=CANVAS_H, b_h=B_H):
"""样张底板(T1):B/C/D 三区骨架——分区几何全部读布局表 battle_layout.tres;格边长=CELL_D 45
def draw_base(canvas_w=CANVAS_W, canvas_h=800, b_h=B_H):
"""样张底板(T1):B/C/D 三区骨架——分区数值按 battle.md 指路、B 区按代码 252;格边长=CELL_D 45
(D 区 10×10 板实比,§8.1)。返回 (img, zones)——zones 供 S/P 断言取样:
b=(0,0,w,b_h) 含明细带;band=明细带收起高;c=(0,b_h+4,C_W,h);d=(D_X,b_h+4,w,h)。"""
b=(0,0,w,b_h) 含明细带;band=明细带 24px;c=(0,b_h+4,240,h);d=(244,b_h+4,w,h)。"""
img = Image.new("RGBA", (canvas_w, canvas_h), (24, 20, 17, 255))
d = ImageDraw.Draw(img)
c_y = b_h + 4
@@ -875,26 +865,25 @@ def draw_base(canvas_w=CANVAS_W, canvas_h=CANVAS_H, b_h=B_H):
a = int(26 * (1 - i / 60.0))
db.rectangle([bx0 - 46 + i // 3, 0, bx0 + 46 - i // 3, 40 + i * 3], fill=(255, 236, 200, a))
img.alpha_composite(beam, (0, 0))
# 明细带(挖掘期收起——高读布局表)
d.rectangle([0, b_h - BAND_DIG, canvas_w, b_h], fill=(46, 39, 32))
# 明细带(挖掘期收起 24px)
d.rectangle([0, b_h - 24, canvas_w, b_h], fill=(46, 39, 32))
# C 区
d.rectangle([0, c_y, C_W, canvas_h], fill=UIKIT["PANEL_HERO"], outline=UIKIT["PANEL_BORDER"])
d.rectangle([0, c_y, 240, canvas_h], fill=UIKIT["PANEL_HERO"], outline=UIKIT["PANEL_BORDER"])
# D 区
d.rectangle([D_X, c_y, canvas_w, canvas_h], fill=(34, 29, 24), outline=UIKIT["PANEL_BORDER"])
# D 区顶行工具件(停手/图例/刷新+AP 点阵+RAP——坐标随 C 区宽平移)
dx = D_X + 8
d.rectangle([dx, c_y + 6, dx + 52, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((dx + 26, c_y + 17), "停手", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([dx + 58, c_y + 6, dx + 102, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((dx + 80, c_y + 17), "?图例", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([dx + 108, c_y + 6, dx + 176, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((dx + 142, c_y + 17), "刷新 8|剩2", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([244, c_y, canvas_w, canvas_h], fill=(34, 29, 24), outline=UIKIT["PANEL_BORDER"])
# D 区顶行工具件(停手/图例/刷新+AP 点阵+RAP)
d.rectangle([252, c_y + 6, 304, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((278, c_y + 17), "停手", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([310, c_y + 6, 354, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((332, c_y + 17), "?图例", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
d.rectangle([360, c_y + 6, 428, c_y + 28], fill=(90, 80, 66), outline=(120, 106, 88))
d.text((394, c_y + 17), "刷新 8|剩2", font=F_LBL2, fill=(232, 222, 200), anchor="mm")
for i in range(6):
col = (232, 200, 120) if i < 4 else (120, 106, 88)
d.ellipse([dx + 188 + i * 18, c_y + 10, dx + 200 + i * 18, c_y + 22], fill=col)
d.text((dx + 300, c_y + 17), "RAP 6/6", font=F_LBL2, fill=(200, 180, 140), anchor="lm")
zones = {"b": (0, 0, canvas_w, b_h), "band": (0, b_h - BAND_DIG, canvas_w, b_h),
"c": (0, c_y, C_W, canvas_h), "d": (D_X, c_y, canvas_w, canvas_h)}
d.ellipse([440 + i * 18, c_y + 10, 452 + i * 18, c_y + 22], fill=col)
d.text((552, c_y + 17), "RAP 6/6", font=F_LBL2, fill=(200, 180, 140), anchor="lm")
zones = {"b": (0, 0, canvas_w, b_h), "band": (0, b_h - 24, canvas_w, b_h),
"c": (0, c_y, 240, canvas_h), "d": (244, c_y, canvas_w, canvas_h)}
return img, zones
@@ -912,25 +901,24 @@ def render_battle_screen(board_img=None):
statuses=("alert",))
draw_standee(scr, 1128, 34, "enemy", "食人魔酋长", "ogre_chief", order=3, armor="light", hp=1.0, intent="buff")
# 明细带摘要行 + 结算语境 hero 剪影 mini(P2 三语境之一;右端贴装不越缘)
d.text((10, b_h - BAND_DIG // 2), "── 第 3 轮 ── 地精 受到 4 点伤害(增伤 ×1)· 熊地精 蓄力(重击预告)…", font=F_LBL2,
d.text((10, 240), "── 第 3 轮 ── 地精 受到 4 点伤害(增伤 ×1)· 熊地精 蓄力(重击预告)…", font=F_LBL2,
fill=(240, 226, 200), anchor="lm")
mini = HERO.resize((24, int(24 * HERO.size[1] / HERO.size[0])), Image.NEAREST)
scr.alpha_composite(mini, (CANVAS_W - 44, b_h - BAND_DIG))
# ── C 区内容(次级文对齐 §7.2 ≥4.5:1——STONE_DEEP/GOLD_INK 在册墨;y 随 B 区高平移) ──
cy = b_h + 4
d.rectangle([8, cy + 8, C_W - 8, cy + 84], outline=(120, 106, 88), width=1) # 攻击数据框(直角细线)
d.text((16, cy + 20), "攻击 6", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((16, cy + 42), "(+50% 增伤 ×1)", font=F_LBL2, fill=(116, 80, 11))
d.text((16, cy + 62), "护甲 AC 12", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((8, cy + 96), "魔法道具槽(预留)", font=F_LBL2, fill=(85, 77, 65))
scr.alpha_composite(mini, (1236, b_h - 24))
# ── C 区内容(次级文对齐 §7.2 ≥4.5:1——STONE_DEEP/GOLD_INK 在册墨) ──
d.rectangle([8, 264, 232, 340], outline=(120, 106, 88), width=1) # 攻击数据框(直角细线)
d.text((16, 276), "攻击 6", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((16, 298), "(+50% 增伤 ×1)", font=F_LBL2, fill=(116, 80, 11))
d.text((16, 318), "护甲 AC 12", font=ImageFont.truetype(ZH_FONT, 15), fill=(30, 26, 22))
d.text((8, 352), "魔法道具槽(预留)", font=F_LBL2, fill=(85, 77, 65))
for i in range(4):
d.rectangle([8 + i * 40, cy + 112, 8 + i * 40 + 34, cy + 146], outline=(120, 106, 88), width=1)
d.text((8, cy + 160), "本关强化列表(预留)", font=F_LBL2, fill=(85, 77, 65))
d.rectangle([8 + i * 40, 368, 8 + i * 40 + 34, 402], outline=(120, 106, 88), width=1)
d.text((8, 416), "本关强化列表(预留)", font=F_LBL2, fill=(85, 77, 65))
# ── D 区主样张板(10×10@45px=450×450+悬崖 8)嵌进工具行之下居中 ──
board_origin = None
if board_img is not None:
bx = D_X + (D_W - board_img.size[0]) // 2
by = cy + AP_H + (CANVAS_H - cy - AP_H - board_img.size[1]) // 2
bx = 244 + (1036 - board_img.size[0]) // 2
by = b_h + 4 + 34 + (800 - b_h - 4 - 34 - board_img.size[1]) // 2
scr.paste(board_img.convert("RGB"), (bx, by), board_img)
board_origin = (bx, by)
return scr, zones, board_origin
@@ -1656,7 +1644,7 @@ def main():
d.text((20, 14), "战斗一屏样张方向稿 v3-draft(2026-09-29,art-bible v3.0;迭代 43 扩展:整屏两版+S1–S4/P1–P5 断言)——机检 %s(方向稿参考,冻结归目检后机检批)" %
("全绿" if ok else "有 FAIL"), col=(232, 222, 200), font=F_TITLE)
canvas.paste(scr, (20, 52))
d.text((20, 866), "三区 mockup(1280×800 实比,分区几何读布局表):B 区站桩(主角立绘 96 朝右/敌字形占位/顺位 FS_HINT/意图素牌/血条=危险红/名行=素底牌+护甲 PNG)",
d.text((20, 866), "三区 mockup(1280×800 实比,B 区按代码 252):B 区站桩(主角立绘 96 朝右/敌字形占位/顺位 FS_HINT/意图素牌/血条=危险红/名行=素底牌+护甲 PNG)",
col=(225, 215, 195), font=F_LBL)
d.text((20, 886), "C 区攻击数据框+预留槽 · D 区主样张板(方案甲拼装/接触带微光/悬停金框/封印印记/危险格巢穴占位)",
col=(225, 215, 195), font=F_LBL)
@@ -1744,7 +1732,7 @@ def main():
with open(OUT_REPORT, "w", encoding="utf-8") as f:
f.write("# 样张 v3 机检报告(2026-09-30 批 A 断言定值 + 迭代 43 整屏扩展 S1–S4/P1–P5/V1/V10/A7/A5b)\n\n")
f.write("生成:`python tools/render_sample_sheet_v3.py`;v3.5 冻结口径:格底分档/亮墨直画三档/色牌后备/物件级装饰/B 区舞台;A3b 图底分离 Δ≥30;A4 尺寸基线(立绘 46×48/小人源 32×32 硬等)。"
"迭代 43 扩展:B 区高读布局表出样;整屏四项与战斗表现五项断言落 CHECKS;两版样张出口与基线并排对拍。方向稿目检先行,机检冻结归目检通过后。\n\n")
"迭代 43 扩展:B 区按代码 252 出样;整屏四项与战斗表现五项断言落 CHECKS;两版样张出口与基线并排对拍。方向稿目检先行,机检冻结归目检通过后。\n\n")
f.write("## 断言明细\n\n")
f.write("| 断言 | 判据 | 结果 | 明细 |\n|---|---|---|---|\n")
for cid, name, okk, detail in CHECKS:
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff. Show more