From eea345882bf921ca40cb744cbf9f0971dd4e797f Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Thu, 15 Oct 2026 01:41:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=BF=AD=E4=BB=A327=20T6+T7):=20=E6=B3=A2?= =?UTF-8?q?=E4=BA=94b=20=E5=8D=AB=E7=94=9F=E4=BB=B6=E5=8C=85+=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E5=90=8C=E6=AD=A5=E9=97=B8=E2=80=94=E2=80=94=E5=AF=B9?= =?UTF-8?q?=E6=8B=8D=E5=85=A8=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T6 卫生件(P2 组收口): - 死代码 13 函数删除(审计 16 处复核=ui_kit:486 实读现役不删,追认单#14)+FeatsEngine 整文件退役(class_name 注销) - 字符串 Callable×10 位→方法引用形态(编译期方法存在性校验;语法实证 METHOD_REF=true) - 状态机 enum 化: adventure State(外部 String 契约 getter 词表逐位一致+外部纯读实证)/ standee HeroState(register/set String 形参零变化+probe 面恒等) - ConfigDB _fatal 退出归根(核心层去 quit/crash→_fatal_flag,决策归组合根 fail-fast 通道) +resolver 诊断/数据通道全面 Callable 化(ConfigDb↔Resolver 类型环断根) T7 文档同步闸: - 实现报告 design/qa/iter27-实现报告-2026-10-14.md(五波+闸表+追认单十五项+反哺点两条+目检五件) - 06 迭代 27 条销号+GDD 反哺点两条新条;07 ㉖ 目检节+v1.0 版本行;roadmap ✅ 换代 - 审计报告本体按退役规则 git rm(吸收即死;修正计数由卡片承载);卡片来源行改指路 闸: 对拍三变体全同(第五跑)+冒烟 1081/0+run_all 27 套+probe click+render golden+static_scan 全绿 --- design/gdd/06-待办清单.md | 13 +- design/gdd/07-体验验证清单.md | 14 ++ design/qa/iter27-实现报告-2026-10-14.md | 93 ++++++++++++ .../代码架构与实现质量审计报告-2026-10-14.md | 132 ------------------ ...œ€求卡片-代码架构技术债修复批-2026-10-14.md | 2 +- game/core/adventure_runner.gd | 22 +-- game/core/battle_orchestrator.gd | 10 +- game/core/board_data.gd | 4 - game/core/config_db.gd | 35 ++--- game/core/config_db_resolver.gd | 30 ++-- game/core/damage_controller.gd | 6 +- game/core/effect_engine.gd | 19 +-- game/core/feats_engine.gd | 39 ------ game/core/feats_engine.gd.uid | 1 - game/core/level_runner.gd | 2 +- game/core/minesweeper_rules.gd | 4 - game/core/save_manager.gd | 8 -- game/core/treasure_items.gd | 16 --- .../presentation/curio_interact_controller.gd | 4 - game/presentation/main.gd | 22 ++- game/presentation/views/curio_pick_overlay.gd | 4 - game/presentation/views/stage_world.gd | 7 +- game/presentation/views/standee_body.gd | 16 ++- roadmap.md | 6 +- 24 files changed, 200 insertions(+), 309 deletions(-) create mode 100644 design/qa/iter27-实现报告-2026-10-14.md delete mode 100644 design/research/代码架构与实现质量审计报告-2026-10-14.md delete mode 100644 game/core/feats_engine.gd delete mode 100644 game/core/feats_engine.gd.uid diff --git a/design/gdd/06-待办清单.md b/design/gdd/06-待办清单.md index d55622a2..add5db29 100644 --- a/design/gdd/06-待办清单.md +++ b/design/gdd/06-待办清单.md @@ -593,13 +593,18 @@ **状态**:备忘(不阻塞,不排期) + --- -#### 代码架构技术债修复批——需求卡片已写(2026-10-14 登记) +#### 迭代 27 代码架构技术债修复批——GDD 反哺点两条(2026-10-14 随批登记,待用户逐点裁决另批落文) -**现状**:[代码架构与实现质量审计报告-2026-10-14](../research/代码架构与实现质量审计报告-2026-10-14.md)(双席审计,基线 97de02c;同日第三方逐条复核:主体结论全部成立,五处计数级瑕疵已修正落文)确诊 2 条 P0(全产品单场景/局态手工复位清单)+ 6 条 P1(_settle 巨函数 259 行/刷新双账/GP 影子账/共享可变字典跨三层/组合根不可测/类型闸缺位)+ 7 组 P2 卫生件。用户已裁:全部技术债修复允许放行单一大迭代(12-14 个工作日,2026-10-14 裁决记录)。需求卡片=[需求卡片-代码架构技术债修复批-2026-10-14](../research/需求卡片-代码架构技术债修复批-2026-10-14.md)(**v1.2**,五波:场景拆分+局生命周期 → _settle 拆段 → 账本收口 → 组合根抽取+模态内聚 → 类型闸+卫生件;核心验收=重构前后同种子完整局输出逐位一致)。 +**现状**:五波全量入库(单会话交付;逐位一致对拍三变体五跑全同;实现报告=design/qa/iter27-实现报告-2026-10-14.md)。实现与设计权威文档的差异/缺口如下,经裁决后另批落文——**机械面随批已动的只有本条与 07/roadmap**。 -**归属**:工程债/重构批(core/presentation 结构面;零行为变更契约) +**归属**:工程债/重构批收口反哺(不阻塞,不排期) -**状态**:**🔄 U0 已开工(2026-10-14 用户令编入迭代 27 卡①+同日 /goal+iter 27 发令)**——卡片 v1.2 三点已裁全按推荐案(①SaveManager/MetaProgression=第三个 autoload/②Ledger=单类持四账/③_settle=审计五段),零待裁点;批入库后本条销号 +| # | 文档 | 反哺点 | +|---|---|---| +| 1 | design/dev/architecture/architecture.md + ADR 系列 | 场景分层(三屏壳+组合根句柄面)/会话生命周期(RunScope 物化)/结算五段 Segment(core/settlement/)/RunLedger 四账所有权/ModalFlow 模态编排层与互斥域 tags 协议——现行陈述滞后于实态 | +| 2 | design/dev/code-conventions.md | §4 语言纪律机器化(S9+audit_type_hints.py 三规则);`:= {}` 推断形态豁免口径;`# type-hint: ignore` 行级白名单与 `# ADR-0007-exempt` S1/S2 标记豁免通道;--script 模式禁裸 autoload 标识符纪律(新);Variant 参数化口径与元素精化后续 | +**状态**:备忘(反哺点待裁决;代拍板追认单十五项见实现报告 §三;残留人闸=07 ㉖ 五件登记不阻塞) diff --git a/design/gdd/07-体验验证清单.md b/design/gdd/07-体验验证清单.md index 91b98372..3e24e31b 100644 --- a/design/gdd/07-体验验证清单.md +++ b/design/gdd/07-体验验证清单.md @@ -264,3 +264,17 @@ | v0.7 | 2026-10-13 | 新增 ㉓ 敌人表内容修批三观测目检节(迭代 24 卡①随批登记:新招可见性/甲型体感/第一关节奏——登记不阻塞按 2026-10-12 真人检测立规) | | v0.8 | 2026-10-13 | 新增 ㉔ UI 全局主题批全屏巡检目检节(迭代 25 卡①随批登记:引擎默认灰清零巡检/音量滑轨两段可辨/专项观察点二(滑轨柄抢焦点·滚动条槽可见性)/预盖件无实役注记——登记不阻塞;能力验证半=冒烟 V1-V6) | | v0.9 | 2026-10-14 | 新增 ㉕ UI 交互与观感批逐屏巡检目检节(迭代 26 卡①随批登记:按钮五态反馈/键盘焦点环/全局 Nearest 巡检(含暗角与渐变白名单判据)/布局均衡(商店居中+营地四栏等高)/诊断钮清零——登记不阻塞;能力验证半=冒烟 V1-V6+T4 结构断言) | +| v1.0 | 2026-10-14 | 新增 ㉖ 代码架构技术债修复批全流程走查目检节(迭代 27 卡①随批登记:全流程观感零变化走查/续玩断点恢复/调试浮窗对局中可开不锁棋盘〔:407-408 事故回归〕/刷新余量显示一致/DS_DEBUG 启动浮窗+两屏角标——登记不阻塞;能力验证半=冒烟 V1-V9+parity 三变体逐位一致对拍五跑全同) | + + +## ㉖ 代码架构技术债修复批——全流程走查(迭代 27 卡①随批登记;能力验证半=冒烟 V1-V9+parity 三变体逐位一致对拍五跑全同) + +> 场景拆分+会话物化+战斗域重构+账本收口+模态协议内聚后,玩家可见面唯一验收=观感零变化。登记不阻塞(2026-10-12 真人检测立规)。 + +| # | 目检项 | 判据 | +|---|---|---| +| 1 | 全流程走查 | 开始菜单→营地四柜台→开新冒险→选路→战斗(挖/旗/刷新/停手)→三选一→商店→Boss→终局——逐屏观感与重构前一致(演出时序/文案/布局零漂移) | +| 2 | 续玩断点恢复 | 停点退出→「继续上局」文案与恢复落点正确(run-scope 物化后断点链回归) | +| 3 | 调试浮窗对局中可开不锁棋盘 | 对局中开浮窗→棋盘可继续操作(:407-408 永久锁死事故回归——互斥域 tags 化后钉面) | +| 4 | 刷新余量显示 | 刷新受理后 HUD 余量递减且与受理次数一致(双账删副本后 HUD 单源读快照) | +| 5 | DS_DEBUG=1 启动 | 浮窗启动即开+两屏角标点亮可点(同步面随 ModalFlow 迁移后行为钉面) | diff --git a/design/qa/iter27-实现报告-2026-10-14.md b/design/qa/iter27-实现报告-2026-10-14.md new file mode 100644 index 00000000..46876bbf --- /dev/null +++ b/design/qa/iter27-实现报告-2026-10-14.md @@ -0,0 +1,93 @@ +# 迭代 27 实现报告——代码架构技术债修复批(五波全量入库) + +- 日期:2026-10-14(单会话 /goal+iter U0 交付,worktree g-plan2-iter27-techdebt 分支 iter27-techdebt-20261014) +- 需求卡片:`design/research/需求卡片-代码架构技术债修复批-2026-10-14.md`(v1.2——三点待裁 2026-10-14 用户三裁全按推荐案:①SaveManager/MetaProgression=第三个 autoload/②Ledger=单类持四账/③_settle=审计五段) +- 来源审计:代码架构与实现质量审计报告-2026-10-14(双席基线 97de02c;**本批收口时按退役规则 git rm——吸收即死,结论迁本报告与卡片**) +- 核心验收(V1):**重构前后同种子完整局输出逐位一致**——parity_anchor 三变体(base/attune/shop)×40 局×RUNSUM+JSONL 剔 ts 双面哈希对拍,**五波出口五跑全同**(锚冻结于 T0,种子面 71000/72000/73000) + +## 一、五波交付 + +### 波一 场景拆分+局生命周期物化(P0-1/P0-2,7a0d57d) + +- 三屏壳场景 `presentation/scenes/`:menu_screen/camp_screen/adventure_screen(内含 level_map/battle 两容器)——视图代码原样构建(装配器/视图类零 .tscn 化,契约 2);组合根句柄全部保留(探针挂载假设 main.X 一级路径零迁移);树序=屏壳在前全模态之下(视觉共存对逐对验算:legend/battle、debug/level_screen、shop/level_screen 等全部保序)。 +- 会话物化 `run_scope.gd`:adventure/runner/settlement/局态旗/账本随会话节点承载;`_begin_session` 建会话(_ready 即建闲时态=永非 null)、终局整节点 queue_free()——`_reset_run_scope` 三项+`_on_battle_end_continue` 置空四件手工清单消亡。 +- SaveManager/MetaProgression 升格 autoload(裁点①甲;类名加 Core 后缀沿 RngService 先例);探针隔离改 attach_path 原地重置(set_master_seed 先例);**发现并修复**:屏壳根全屏 Control 默认 STOP 吃掉下层模态鼠标输入(ui_input 实证)——三屏壳根 mouse_filter=IGNORE+V2 回归闸。 +- V2 屏壳独立实例化 11 断言+V3 两连局复位 11 断言进 smoke。 + +### 波二 _settle 五段拆 Segment(P1-1,26301ba) + +- `core/settlement/` 五段类:GrantSegment(授予段体+旗判随段迁入+counter_add 五位/grant_material 三位分支单点化)/AuraSegment/CombatSegment/DecaySegment/VerdictSegment(返回裁定,终局早退语义留宿主);编排器 1725→1325 行退为段序编排+行发射原语单点。 +- 姊妹病灶:`_resolve_hero_attack` 148 行拆四步(ctx 命中面→基值装配→掷点兑现→交付),ctx/redemption 字典穿线算序逐字保留。 +- V4 settle_segments_test 16 断言进 run_all。 + +### 波三 账本收口(P1-2/P1-3/P1-4,6fd7d78) + +- `core/run_ledger.gd` RunLedger 单类持四账(裁点②甲):attune_charges/vision_counter/adventure_counters/bonus;战斗侧只拿 read/append 原语;AR 持账→LR set_run_ledger→enc 单键下传;编排器三裸引用成员退役;调试/单场缺省新账=零扰动面(vision_counter 缺省空=不计,原 enc 缺键守卫语义保留)。 +- 刷新双账:main 副本删除;presentation_snapshot 成文面升键 refresh_used(can_refresh 邻位)——HUD 余量/遥询单源读快照;V5 快照一致性断言×3。 +- GP 影子账:summary 增 gp_final 键(close_wallet 清账前快照);gp_shadow_ledger.gd 全件退役;终局面板 GP 行取数=summary 键;ui_probe 断言换代。 + +### 波四 组合根可测化+模态协议内聚(P1-5/P2-7,8b9e556) + +- **重估结论(卡片 T4 前置义务)**:场景拆分落地后,模式状态机已随 RunScope 物化、屏构建已随屏壳迁出——抽取面收敛为**模态编排域**:`modal_flow.gd` ModalFlow 纯逻辑类(依赖仅 ModalStack+查询 Callable,审计处方形态)——开合编排(挂起兜底/排播排空/遮挡记账/开合同步)/输入锁谓词(三例外注入)/双形制角标谓词。 +- **V6 测试抓出并修正一处真分歧**:挂起兜底递归后误加 return(原 main 语义=先结挂起后继续开本层两拍都走)——单测缝的结构价值实证。 +- ModalStack 互斥域随层登记:register(m, tags)+TAG_NON_LOCKING/TAG_SCREEN_STATE 封闭词表+has_open_excluding_tags 组查询;原调用点排除式拼装(:404-409 输入锁等五处)退役;main._modals=flow.stack(ui_struct _list 断言缝保留)。 +- 控制器构造收拢 context:curio 16 参→CurioFlowContext/camp 13 参→CampFlowContext(字段逐一同名,控制器可独立实例化)。 + +### 波五 类型闸+卫生件(P1-6/P2 组,b605022+T6 笔) + +- 类型闸:`tools/audit_type_hints.py` 机器闸(R1 无类型容器注解/R2 有值返回缺类型/R3 参数缺类型+行级白名单通道);368 处容器注解机械参数化(Array[Variant]/Dictionary[Variant, Variant] 零行为面)、13 处参数真类型;project.godot 拉起 untyped_declaration=1;static_scan 新 S9 规则。 +- 卫生件:死代码 13 函数删除(审计 16 处复核后其中 ui_kit:486 一处经实读为现役非死码,见追认单)+FeatsEngine 整文件退役;字符串 Callable×10 位换方法引用形态(编译期方法存在性校验);冒险状态机/standee 主角三态 enum 化(外部 String 契约逐位一致);ConfigDB `_fatal` 退出归根(核心层去 quit/crash,置 `_fatal_flag`,退出决策归组合根既有 fail-fast 通道)+resolver 诊断/数据通道全面 Callable 化(ConfigDb↔Resolver 类型环断根)。 + +## 二、闸记录(每波出口+批末) + +| 闸 | 基线(T0 冻结) | 波一 | 波二 | 波三 | 波四 | 波五 | +|---|---|---|---|---|---|---| +| 逐位一致对拍(V1 核心) | 锚冻结 | ✓ 全同 | ✓ 全同 | ✓ 全同 | ✓ 全同 | ✓ 全同 | +| 冒烟 | 1055/0 | 1077/0 | 1077/0 | 1079/0 | 1079/0 | 1081/0 | +| run_all | 25 套 | 25 | 26 | 26 | 27 | 27 | +| probe_gate --click | 绿 | 绿 | 绿 | 绿 | 绿 | 绿 | +| render_gate golden | 0.00% | 0.00% | 0.00% | 全绿 | 全绿 | 全绿 | +| static_scan | (历史 cd 错位恒跳过) | — | — | — | — | S1/S2/S4/S9 全绿 | + +批末(T6 后)run_all 27 套全绿+冒烟 1081/0+probe/render 全绿+对拍全同(T6 对拍见 /tmp/parity_check6.log 记录,exit 0)。 + +## 三、代拍板追认单(白名单九项执行实录+计划外决断,待用户逐项追认) + +1. **屏场景命名/落位**(白名单①):`presentation/scenes/{menu,camp,adventure}_screen.tscn`——三屏壳与卡片三屏一一对应。 +2. **屏壳内部分层**(白名单②):adventure 屏壳内 battle_root+level_screen 平级(无同时可见态,层内先后零视觉差)。 +3. **Segment 五类命名/落位**(白名单③):`core/settlement/{grant,aura,combat,decay,verdict}_segment.gd`,类名 XxxSegment。 +4. **Ledger 命名/原语形状**(白名单④):`core/run_ledger.gd` RunLedger;原语=attune_charge_has/set/consume、vision_count_add、counters_seed_snapshot/merge_export、bonus_add_axis(各站点原语义逐字收拢)。 +5. **run-scope 物化形态**(白名单⑤):RunScope Node 挂 main;_ready 即建闲时态(永非 null——全组合根免空判);会话更替=旧节点 queue_free+新节点同拍建。 +6. **探针迁移最小改动**(白名单⑥):组合根句柄保留策略使 10 探针中 7 个零迁移、3 个仅 run_scope 路径换代;pacing 测试改 attach 隔离。 +7. **类型标注写法**(白名单⑦):368 处机械参数化取 `Array[Variant]`/`Dictionary[Variant, Variant]` 零行为面口径——**元素精化(如 Array[Dictionary])留后续批渐进**(机器闸已封住新增无类型)。 +8. **死代码删除切分**(白名单⑧):随 T6 单笔提交。 +9. **enum 命名**(白名单⑨):adventure `State`/standee `HeroState`(+_HS_NAME/_HS_VALUE 双向映射常量)。 +10. **〔计划外〕P2-2 autoload 直取降格实现**:审计处方「编译期全局名」与本仓 --script 模式编译器不解析裸 autoload 标识符**硬冲突**(run_all.gd 头注在案+pacing 实测 Parse Error)——降格为路径常量单点(AUTOLOAD_PATH_* 四常量)+T1 读视图属性;拼写风险收敛一处,类型面保留。 +11. **〔计划外〕board_generator 四 db 参白名单**:generate/_attempt/legality/_finalize 的 db 参保持鸭子型(测量面 _BoardRowStub 冒烟夹具注入,typed 参拒 stub)——`# type-hint: ignore` 显式白名单通道首用。 +12. **〔计划外〕static_scan 历史 cd 错位修复**:9-24 布局迁移后 `dirname/..` 落 game/ 致 `game/core` 守卫恒跳过=静默空转遗产——修 `/../..` 后 S1-S4/S9 首次实跑,暴露并处置两笔存量(S1/S2 main `_seed_value` randi=ADR-0007 文档化豁免面→标记豁免通道;S9 即本批闸)。 +13. **〔计划外〕V6 测试抓出挂起兜底分歧**:ModalFlow 初版在挂起兜底后误加 return(原 main 语义两拍都走)——以原语义为准修正;该处即单测缝价值的直接实证。 +14. **〔计划外〕审计「死代码 16 处」复核为 13 处**:ui_kit.gd:486 一处实读为现役代码(审计抽样误差),不删;其余 12 函数+FeatsEngine 整文件照删。 +15. **〔计划外〕刷新遥测取数面换代**:`ux_refresh_attempt` 的 round_refresh_index 从 main 副本改读快照(P1-2 连带,载荷键与数值语义零变化)。 + +## 四、GDD 反哺点(两条,已登记 06,待用户逐点裁决另批落文) + +| # | 文档 | 反哺点 | +|---|---|---| +| 1 | design/dev/architecture/architecture.md + ADR 系列 | 场景分层陈述(三屏壳+组合根句柄面);会话生命周期(RunScope 物化+_begin_session/_end_session 语义);结算五段 Segment 结构(core/settlement/);RunLedger 四账所有权模型;ModalFlow 模态编排层与互斥域 tags 协议——architecture 现行陈述均滞后于实态 | +| 2 | design/dev/code-conventions.md | §4 语言纪律机器化(S9+audit_type_hints.py 三规则);`:= {}` 推断形态=类型推断合法面不算债的口径;`# type-hint: ignore` 行级白名单通道;`# ADR-0007-exempt` S1/S2 标记豁免通道;--script 模式禁裸 autoload 标识符纪律(新);Variant 参数化口径与元素精化后续 | + +## 五、实机目检总清单(登记 07 ㉖——真人检测不阻塞,2026-10-12 立规) + +1. 全流程操作观感零变化走查:开始菜单→营地(四柜台)→开新冒险→选路→战斗(挖/旗/刷新/停手)→三选一→商店→Boss→终局逐屏过一遍(场景拆分+会话物化后唯一人闸面)。 +2. 续玩断点恢复:停点退出→继续上局(断点文案/恢复落点)。 +3. 调试浮窗对局中可开不锁棋盘(:407-408 事故回归——互斥域 tags 化后行为钉面)。 +4. 刷新按钮余量显示与受理一致(双账删副本后 HUD 单源读快照)。 +5. DS_DEBUG=1 启动即开浮窗+两屏角标(同步面随 ModalFlow 迁移)。 + +## 六、残留与去向 + +- 残留人闸:07 ㉖ 五件(登记不阻塞)。 +- 代拍板追认单十五项(上表)待用户逐项追认。 +- GDD 反哺点两条(06 在册)待裁决另批落文。 +- 审计报告本体已按退役规则 git rm(吸收即死;修正后计数已由需求卡片 v1.2 承载)。 +- 元素精化(Array[Variant]→具体元素型)为类型闸后续渐进面(06 反哺点②附带)。 diff --git a/design/research/代码架构与实现质量审计报告-2026-10-14.md b/design/research/代码架构与实现质量审计报告-2026-10-14.md deleted file mode 100644 index e4f58c76..00000000 --- a/design/research/代码架构与实现质量审计报告-2026-10-14.md +++ /dev/null @@ -1,132 +0,0 @@ -# 代码架构与实现质量审计报告(2026-10-14) - -**审计对象**:`game/` 目录全部实现(约 114 个 GDScript 脚本 / 3.4 万行,不含 tests) -**审计方法**:godot-master 技能库(GD-Agentic-Skills 的本地镜像,Godot 4.7+ 专家知识库)双席审计——甲席走 Analyst 工作流审架构,乙席走 Auditor 工作流审实现规范与设计模式;两席均为**只读静态实读**,未运行游戏。实现席实跑了技能库自带扫描器(信号审计 / 类型标注审计),内存类扫描器需 Godot 运行时,按只读纪律跳过并注明。 -**审计日期**:2026-10-14(基线 master 97de02c) -**基线事实**:全项目真实游戏场景只有 1 个(`presentation/main.tscn`,其余 13 个 .tscn 全在 smoke/ 下是测试探针);节点几乎全部由 GDScript 程序化构建,产品代码 `instantiate()` 调用数为 0;autoload 仅 2 个(ConfigDB / RngService);目录分层 core / data / presentation / assets / tests / smoke。 - ---- - -## 总评 - -**core 层实现质量显著高于垂直切片平均水准,两席审计独立确认核心域健康**;问题集中在场景层与生命周期层。两个 P0 是同一根因的两面:**整条应用生命周期(菜单/营地/冒险/战斗/每局复位)压缩在 main.gd 一个 1385 行的组合根里,靠手工清单维持局间隔离**——领域侧已经长出三层编排器(AdventureRunner 跨局持久 / LevelRunner 一关一生 / BattleOrchestrator 一战一生),场景层没有任何切缝与之对应。一句话定性:**领域分层是洋葱,场景组织是石头。** - ---- - -## P0 发现(2 条,同根) - -### P0-1 全产品单场景:场景作为「组合单元/生命周期单元」完全缺席 - -- **证据**:`project.godot`(main_scene 唯一);`presentation/main.tscn` 仅 1 个 Control 节点挂 main.gd;`presentation/main.gd:204-361` `_build_ui` 程序化构建全部四区 + 12 个模态层。 -- **为什么是问题**:Godot 中场景是复用、组合与生命周期的基本单元——节点进/出树(`_enter_tree`/`_exit_tree`/NOTIFICATION_READY)本可免费承载「每局/每屏」的状态建立与销毁。现在一局结束什么都不销毁,全靠手动清理;任何视图都无法 F6 独立运行或单独复用;13 个 smoke 探针里 10 个只能挂载整个 main.tscn 才能测任何 UI(`smoke/ui_probe.gd:15` 等)。 -- **修复方向**:按「屏」切场景,第一步只拆屏容器 .tscn,视图继续代码构建,不要求全面 .tscn 化。 - -### P0-2 每局状态靠手工复位清单,无局级生命周期边界 - -- **证据**:`presentation/main.gd:647-657`(`_on_battle_end_continue` 手工置空 runner/settlement/adventure/_adventure_mode 并调 debug_overlay.reset_sliders());`main.gd:555-559` `_reset_run_scope` 只清 3 项;局态旗至少 7 个散布类顶(`main.gd:53-56,76-78,129-131`)。这类手工清单已有前科:`main.gd:790-792` 注释记录了续玩分支 runner 未就绪导致的实机崩值。 -- **为什么是问题**:场景机制「打包一组节点,`free()` 即整体回收」正是为消灭这种清单而存在;清单是随新增状态线性膨胀的手工契约,每加一个跨局字段就多一个静默泄漏点,且泄漏表现为低频难复现的脏状态而非显式报错。 -- **修复方向**:把「一局」物化为可整体 `queue_free()` 的节点(或至少一个 run-scope 对象持全部局态),终局释放即复位。 - ---- - -## P1 发现(6 条) - -### P1-1 战斗结算巨函数:259 行、嵌套 7 层、同构兑付块重复 ≥5 处(双席独立命中同一病灶) - -- **证据**:`core/battle_orchestrator.gd:632` `_settle()` = 259 行;`:562-563` 嵌套达 7 层(该文件 ≥5 层缩进行共 80 行,全项目最高)。重复块实读确认:`counter_add` 兑付三行组 ×5(`:682-684, 741-743, 776-778, 838 附近, 1192-1193`);`grant_material` 二次兑付块 ×3(约 `:688-694, :789-792, :1206-1212`);`reveal_tiles` 检查 ×3。效果 kind 字符串分派(`get("kind","") ==`)散布 20 处于结算段内。姊妹病灶:`core/damage_controller.gd:272` `_resolve_hero_attack()` = 148 行。全文件 1725 行、类级状态约 40 个变量(:62-98)、fan-out 13 个 core 类。 -- **为什么是问题**:编排器**不是**纯上帝对象(伤害已单据化进 DamageController,敌人/状态/奖励各自成类),但效果引擎只做了「匹配」数据驱动,「施加」按既定裁决留在发射点——实际效果是新增一种 effect kind 要同时改引擎词表 + 编排器内联分支,编排器增量复杂度与内容量线性挂钩。 -- **修复方向**:不推倒重写——五段结算各抽成 Segment 类(grant/aura/combat/decay/verdict),编排器退为段序编排 + 行发射,20 处 kind 分支随段归位。 - -### P1-2 刷新次数双账本——所有权三问(谁拥有/谁可改/谁知道)不及格 - -- **证据**:权威计数在 `core/battle_orchestrator.gd:73`(`_refresh_used_this_round`,受理门槛 :1522);呈现/遥测另计一份 `presentation/main.gd:129`(`_refresh_used_round`),HUD 余量 = 表上限 − main 自己的计数(`main.gd:1240`)。 -- **为什么是问题**:core 是裁判、main 是记账员,两条路一旦分叉(新增受理路径、调试注入、断点恢复),HUD 显示与真实可刷次数静默不一致。 -- **修复方向**:`presentation_snapshot()` 已可携带本轮已刷次数——删 main.gd 副本,HUD 只读快照。 - -### P1-3 GP 影子账本:表现层持有权威态镜像,根因在 summary 载荷不全 - -- **证据**:`presentation/gp_shadow_ledger.gd:9-19`(表现侧镜像钱包余额);根因链:`core/adventure_runner.gd:1046`(close_wallet() 清零先于 adventure_finished.emit)+ `adventure_runner.gd:1022-1038`(summary 十一个键里没有 final GP),呈现层被迫在清账前从事件流自抄一份。 -- **为什么是问题**:表现层持久化核心态违反「表现禁存数据」;镜像与权威只靠 wallet_changed 事件序维持一致(影子账本文件头自己承认是手工镜像)。该上抛的数据应随信号载荷走,不该让下游自建账本补抄。 -- **修复方向**:summary 增加 `gp_final` 键(清账前快照),删影子账本。 - -### P1-4 共享可变字典跨三层:所有权「名义单点、实际三处可写」 - -- **证据**:`core/level_runner.gd:501-521` begin_battle 的 encounter 载荷携带 bonus_ref/adventure_counters/attune_charges/vision_counter 四个共享 dict 引用;`core/battle_orchestrator.gd:79,86-88` 持引用直写(_attune_charges_ref 注释自认「use_treasure 消费直改」);`level_runner.gd:50` 账本同为字典引用共享。 -- **为什么是问题**:这是确定性设计(回灌/导出时点已文档化),但共享可变引用在函数签名上不可见——「冒险加算区归 AdventureRunner 所有」只是头注契约,LevelRunner 与 BattleOrchestrator 都握着同一本账可写。 -- **修复方向**:共享账收拢为显式 Ledger 类,战斗侧只拿 read+append 原语;战末 export/开局 seed 已有的时点语义不变。 - -### P1-5 组合根(main.gd)不可独立测试:核心读面靠注释纪律维持 - -- **证据**:控制器不可独立实例化——`presentation/curio_interact_controller.gd:39-57` 构造 16 参(10 个组合根 Callable)、`presentation/camp_shop_presenter.gd:25-40` 13 参;测试代价:`tests/unit/presentation/pacing_queue_test.gd:17-18` 必须 load 整个 main.tscn 挂整树并篡改 main 内部字段才能驱动。25 个测试套件中 core/data 侧 22 个直接 new() 即测,presentation 侧只剩整树集成一条路。 -- **为什么是问题**:组合根本身没有单测缝;豁免面与 20+ 访问器(settlement.minesweeper.* 等)无结构强制,全靠 ADR 注释和评审盯。 -- **修复方向**:把 main 的「模式状态机 + 输入锁 + 模态编排」抽成无 UI 纯逻辑类(依赖仅 ModalStack + 查询 Callable),控制器构造参数收拢为 context 对象——不改行为即可获得单测缝。**注:本条应在场景拆分落地后重新评估,main.gd 缩水后抽取面可能大幅变小。** - -### P1-6 静态类型闸缺位:类型规范有文、机器无闸 - -- **证据**:无类型 Array/Dictionary 游戏代码 73 处(core 53 / data 9 / presentation 11,如 `core/adventure_runner.gd:78,95`、`core/effect_engine.gd:94-95`、`presentation/main.gd:69`);core 无返回类型函数 12 处(`core/reward_economy.gd:109,124,136,151` 等);无类型参数(`core/attunement.gd:12`、`core/board_generator.gd:18,83,284,334`)。`project.godot` 全文 36 行没有任何 gdscript warning 配置——`untyped_declaration` 默认 Ignore。 -- **为什么是问题**:仓库 code-conventions §4.2 有明文(Dictionary 取值参与算术前显式声明类型),但无机器闸则规范只靠评审;Variant 容器在结算热循环里吃类型检查开销且放行拼写错。 -- **修复方向**:project.godot 提升 warning 级别让存量显形,新码一律收口;存量 73 处随批清理。**本条必须在其他重构全部落地之后做**,否则新重构代码又会产生新违规。 - ---- - -## P2 卫生件(建议打包随批,不需逐条裁决) - -1. **死代码 16 处**:`core/battle_orchestrator.gd:216` `_attune_row`、`core/damage_controller.gd:102` `_fx_dispatch`(活实现 _fx_fact 的退役孪生)、`core/board_data.gd:61` `clear_variants`、`core/effect_engine.gd:417` `combo_of`、`core/treasure_items.gd:68,83`、`core/minesweeper_rules.gd:41`、`core/feats_engine.gd:13,38`、`core/config_db.gd:253,273`、`core/save_manager.gd:419`、`presentation/curio_interact_controller.gd:58`、`presentation/views/stage_world.gd:17`、`presentation/views/curio_pick_overlay.gd:170`、`presentation/views/ui_kit.gd:486`。其中 **FeatsEngine 整文件零调用**(仅注释提及,`data/schemas/feat_table.gd:22` 自注已随重设计退役)但 `class_name` 仍全局注册。 -2. **绝对路径取 autoload ×4**:`presentation/main.gd:174,190,481,1351` 用 `get_node("/root/…")` 字符串路径取 ConfigDB/RngService——Godot 4 有编译期注入的全局名,字符串路径改名/拼写零防护。 -3. **字符串式 Callable ×9**:`core/battle_orchestrator.gd:105,390,391`、`core/damage_controller.gd:98`、`core/adventure_runner.gd:940,951`、`core/level_runner.gd:166`、`presentation/main.gd:191`——`Callable(self, "方法名字符串")` 绕过编译期方法存在性校验,应改 `self._method` 引用形式。 -4. **裸字符串状态机 ×16**:`core/adventure_runner.gd:120,159,176,263,269,277,402,894,974,1049,1051,1071`(state = "idle"/"running"/"settling");`presentation/views/standee_body.gd:333,373,380,387`。本仓 `core/action_economy.gd:12-23` 已有「内部 enum + match + 外部 String 契约 getter」正解示范,应套用。 -5. **百行级装配/校验长函数 17 个**:`presentation/main.gd:204` `_build_ui()` 160 行、`level_map_screen.gd:72` 145 行、`camp_overlay_view.gd:55` 142 行、`debug_overlay.gd:159` 114 行、schema 校验器群(treasure_item_table.gd:87 = 149 行等)。线性装配圈复杂度不高,但区块级改动每次都在巨函数里动刀。**注:main._build_ui 在场景拆分中自然解体,不必单独修。** -6. **ConfigDB 越权退出 + resolver 类型软环**:`core/config_db.gd:75-82` `_fatal` 直接 quit(1)/OS.crash(fail-fast 政策对,执行位置应归根——`main.gd:176-179` 已有同通道 fatal 屏);`config_db.gd:57-58` 持有并构造 ConfigDbResolver,resolver 反向 class_name 引用 ConfigDb——双向类型耦合,resolver 对门面改持 Callable 诊断通道即解(validator 已是此形态)。 -7. **模态「开态=可见性」协议**:`presentation/modal_stack.gd:39-63` has_open() 靠遍历 visible 位;`main.gd:404-409` 输入锁 = 栈查询 + 三例外 + 排除式;`main.gd:407-408` 注释记录过「DS_DEBUG 默认开 × 遮挡复活 × has_open 组合 = 棋盘永久锁死」事故。12 个层的状态散布在各自 visible 位上,约束只存在于调用点拼装。修法:ModalStack 内聚「互斥域/排除组」概念,开态谓词随层登记。 - ---- - -## 用户场景预想评估(审计席专门裁断) - -用户预想:开始菜单做根场景 + 营地/冒险分开 + 冒险内分关卡/战斗两大块。 - -**裁断:方向支持,三条全部成立;需三处修正、一处明确反对。** - -1. **支持「开始菜单做根场景」**——修正:拆场景后 SaveManager/MetaProgression 不能再由 main.gd new() 出来(`main.gd:182-189`),必须提升为跨场景持有者(第三个 autoload 或根场景常驻子节点),否则换场景即丢档。RngService 已有先例,模式现成。 -2. **支持「营地与冒险分开」**——营地全部读写已收口在 MetaProgression/SaveManager 访问器(camp_shop_presenter 零直写),营地当前只是覆盖层(camp_overlay),升格独立场景不碰任何 core 接口,切缝天然干净。 -3. **支持「冒险内关卡/战斗分块」**——最有力的论据是域侧三层生命周期已经存在,场景层只是补镜像:`level_screen` 已是独立屏雏形(`main.gd:76,259-263`,屏级互斥单出口 `main.gd:978-990`),`_battle_root` 是现成战斗屏宿主(`main.gd:77,207`)。预想与代码现实几乎一一对齐。 -4. **明确反对一条**:不要借机把 40+ 个 views/*.gd 转成 .tscn。「程序化构建」与「场景切分」是两个独立决策——.tscn 根节点 + `_ready` 程序化内容完全合法;全面 .tscn 化会破坏全部探针契约(`board_view.gd:8-14` 对探针零改造契约)且收益为零。 -5. **迁移安全网**:13 个 smoke 探针中 10 个假设「挂载 main.tscn 即全应用」,拆分时这批探针的挂载假设同批迁移,它们就是切分作业的回归保障。建议时序:垂直切片验证期「只拆不重写」——root 壳 + menu.tscn + camp 场景 + adventure.tscn(内含 level_map/battle 两容器),新内容一律进新场景,战斗屏视图代码原样搬。 - ---- - -## 实现规范面补充结论与扫描器运行记录 - -**四大高危面几乎无坑、无 P0 命中**:信号面、Tween 生命周期、数值单一事实源、autoload 卫生。 - -**扫描器实跑记录**(实现席如实登记): -- 技能库 `auditor_audit_signals.py`(python 只读输出):74 处 lambda 连接(presentation 4 / smoke 62 / tests 8)。presentation 4 处逐一实读确认无泄漏(发射者与 lambda 同子树同生共死,refresh 先 queue_free 子树并清登记数组,`camp_overlay_view.gd:227-230`);smoke 62 处为一次性 headless 探针进程,不进游戏包。 -- 技能库 `auditor_audit_type_hints.py`:229 处(untyped Array 209 / Dictionary 20 / string connect 0);按目录 core 53 / data 9 / presentation 11 / smoke 133 / tests 23。 -- 手工 grep 等价检查(如实注明非扫描器):Godot 3 四参 connect 0;字符串 `.connect(` 0;`.disconnect(` 0;CONNECT_* 标志 0;绝对路径 get_node 4(见 P2-2);`Callable(self,"…")` 9(见 P2-3);call_deferred 10 处实读均为帧沿触发非初始化创可贴;await 1 处(dev 工具);全站 `_physics_process` 中 await 0;create_tween 20+ 处实读确认节点绑定+回调清理;print 3 处全在 dev 工具。 -- 跳过并注明:`auditor_audit_memory_fragmentation.gd`、`auditor_purge_report_generator.gd` 需 Godot 运行时,按只读纪律未跑。 -- 误报排除一例:battle_orchestrator 的 round_started/board_changed(:30-31)初判疑为死信号,实读确认 :447,488,1516 有发射、三层转发链全活,非发现。 - ---- - -## 做对了的事(防止报告纯负面失真) - -1. **信号严格向上,core 零反向依赖**:全 core/ 无一处 presentation 引用、无 get_node/树访问;runner 转发族用命名方法 Callable(`core/adventure_runner.gd:918-924` 等,头注写明禁 lambda 捕获 self 成环);信号连接唯一归组合根(ADR-0001 落实到位)。 -2. **Autoload 克制且纯净**:仅两个、纯数据/服务形态、无子节点无 UI 知识;RngService 统一单盐流 + 全事件日志(`core/rng_service.gd:95-108`)使全游戏同种子逐位复现——确定性架构的业内水准做法,也是本次全部重构验收手段的基础。 -3. **效果引擎护栏设计**:事件/效果/事实/算子四词表封闭常量 + 加载期 schema 校验致命 + 组合规则成文(`core/effect_engine.gd:24-91,362-433`),失败模式 fail-fast 而非静默。 -4. **读面成文快照收口**:presentation_snapshot()/diagnostic_snapshot() 成文出口替代链式穿透,协作对象私有化 + 明文豁免面——封装边界是写下来的契约而非默契。 -5. **Tween 生命周期纪律全站一致**:节点绑定 create_tween + tween_callback(queue_free) + is_instance_valid 守卫 + rebuild 重入先杀——技能库最常判死刑的陷阱本项目零命中。 -6. **测试策略真实分层**:25 个 headless 套件直接实例化 core 类、表值现读不写死;确定性 RNG 使「逐位一致」断言成为可能,这在随机系统里是稀缺的测试能力。 - ---- - -## 修复排期估算与裁决记录 - -**逐项估算**:场景拆分 + 局生命周期物化 + 探针迁移 ≈ 4-5d;`_settle` 拆段 ≈ 1.5d;共享账本 Ledger 类化 ≈ 1d;类型闸拉起 + 73 处收口 ≈ 1.5d;组合根可测化抽取 ≈ 1-1.5d(场景拆分后重估);模态协议内聚 ≈ 0.5-1d;卫生件包 ≈ 1.5d。**合计约 12-14 个工作日。** - -**裁决记录(2026-10-14 用户)**:全部技术债修复**允许放行单一大迭代**,12-14 个工作日在允许时间范围内。 - -**批内执行顺序约束**(审计席建议):①场景拆分与局生命周期先行(P0 根因,且决定 main.gd 终态形状);②战斗域重构(_settle 拆段 + Ledger 类化)随后;③组合根抽取在场景拆分后重估再动;④类型闸收尾(其他重构落地后才拉得起 Error 级);⑤卫生件随批顺手带。最大两件(场景拆分动 main.gd 1385 行 / _settle 拆段动战斗结算心脏)各自需要全量冒烟回归,同批内应分波提交、逐波全绿,避免回归时二分定位跨双巨改。 - -**核心验收手段**:确定性 RNG 架构给了杀手级验收——同种子逐位一致回放(重构前后跑完整局输出逐位一致);场景批验收 = 10 个探针迁移后全绿 + run_all 25 套;战斗域批验收 = 逐位一致 + 冒烟全量。 - -**写卡前待裁三点**(grilling 时闭):①SaveManager/MetaProgression 升格形态(第三个 autoload vs 根场景常驻节点);②共享账本 Ledger 类接口口径;③`_settle` 段边界切法。 diff --git a/design/research/需求卡片-代码架构技术债修复批-2026-10-14.md b/design/research/需求卡片-代码架构技术债修复批-2026-10-14.md index 5aeddf87..4c37b96e 100644 --- a/design/research/需求卡片-代码架构技术债修复批-2026-10-14.md +++ b/design/research/需求卡片-代码架构技术债修复批-2026-10-14.md @@ -1,7 +1,7 @@ # 需求卡片:代码架构技术债修复批(场景拆分 · 局生命周期 · 战斗结算拆段 · 账本收口 · 类型闸 · 卫生件) - 版本:v1.2(2026-10-14 成文 v1.0;同日用户令**编入迭代 27 卡①**——roadmap 迭代行已更新,M4 数值批顺延 28+。v1.1→v1.2:**§10 三点待裁全裁**(2026-10-14 用户三裁,全按推荐案:①第三个 autoload/②单类持四账/③审计五段)——零待裁点,卡片成立) -- 来源:[代码架构与实现质量审计报告-2026-10-14](代码架构与实现质量审计报告-2026-10-14.md)(双席审计,基线 master 97de02c;**同日已经第三方逐条复核:主体结论全部成立,五处计数级瑕疵已随批修正落文**) +- 来源:代码架构与实现质量审计报告-2026-10-14(双席审计,基线 master 97de02c;同日第三方逐条复核五处计数级瑕疵修正落文;**该报告已按退役规则于本批收口 git rm——吸收即死,修正后计数由本卡承载,收口结案=design/qa/iter27-实现报告-2026-10-14.md**) - 归属:工程债/重构批(凭审计证据直接写卡,无设计定稿前置——2026-10-10 立规:缺陷修复/工程债/重构类工程需求无设计前置要求) - 排期:**已编入迭代 27 卡①**(2026-10-14 用户令;用户裁决记录:全部技术债修复允许放行单一大迭代,12-14 个工作日在允许时间范围内;**开工门=U0 已满足**——三点已裁(见版本行)+用户 /goal+iter 27 发令,U0=2026-10-14 开工;M4 数值批顺延 28+) diff --git a/game/core/adventure_runner.gd b/game/core/adventure_runner.gd index 67babfb9..d76be229 100644 --- a/game/core/adventure_runner.gd +++ b/game/core/adventure_runner.gd @@ -54,7 +54,11 @@ signal wallet_changed(event: String, gp: int, extra: Dictionary) var db: ConfigDb var rng: RngServiceCore -var state := "idle" # idle / running / settling(冒险结构.md 三态) +enum State { IDLE, RUNNING, SETTLING } # 会话状态机(迭代 27 T6——裸字符串 enum 化:内部 enum,外部 String 契约 getter 词表逐位一致) +var _state: State = State.IDLE +var state: String: # 外部契约面(只读——外部零写点实证;"idle"/"running"/"settling" 零变化) + get: + return State.keys()[_state].to_lower() var ledger := {"hero_hp": 0, "gp": 0} # 冒险账本(生命 + GP 钱包——经 start_level_external 注入 runner 共享) var run_ledger := RunLedger.new() # 共享四本账(迭代 27 T3——P1-4 收口:attune_charges/vision_counter/adventure_counters/bonus 单类持账;裁点②甲) var purchased := {} # product_id → 已购档数(跨关延续、冒险收束清零——递增定价输入) @@ -153,7 +157,7 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool: for r in feat_seed: _feats.append(String(r)) ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径——T17 修复:含调试浮窗 pct,与战斗内同源) - state = "running" + _state = State.RUNNING return _start_current_level() @@ -257,13 +261,13 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: migrated.append({"kind": String(c), "tier": db.adventure.TIER_COMMON}) _curio_pending = {"candidates": migrated} var payload: Dictionary[Variant, Variant] = (bp_pending.get("payload", {}) as Dictionary).duplicate(true) - state = "running" + _state = State.RUNNING curio_choice_presented.emit(payload) return true if not bp_result.is_empty(): _pending_next_level = true _curio_result = bp_result.duplicate(true) - state = "running" + _state = State.RUNNING curio_resolved.emit(_curio_result.duplicate(true)) return true _resume_bp = bp.duplicate(true) @@ -271,7 +275,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: var bp_charges = bp.get("attunement_charges", null) if bp_charges is Dictionary: run_ledger.attune_charges = (bp_charges as Dictionary).duplicate(true) - state = "running" + _state = State.RUNNING return _start_current_level() @@ -936,7 +940,7 @@ func _start_current_level() -> bool: _runner.set_run_ledger(run_ledger) # 迭代 27 T3——共享四账单类下传(原四 setter/参数散传收口;run_ledger=四账类,ledger=关卡账沿用旧名) _runner.sync_attune_inventory() # 迭代 11 第①批 T6 原语义——能力件骑库存面注入(随账下传同拍) var ok: bool = _runner.start_level_external(level_index, ledger, purchased, - Callable(self, "apply_rest"), _base_override, true, template_pick, resume_state) + apply_rest, _base_override, true, template_pick, resume_state) if ok: _runner.set_attunement(_attunement) # 迭代 11 第①批 T5——同调装配下传(enc 携带进战斗结算) _runner.set_feats(_feats) # 迭代 12 T1——特性持有集下传(begin_battle enc.feats 求值面) @@ -945,7 +949,7 @@ func _start_current_level() -> bool: if _shop_mode == "pool": # 商店共享池注入(迭代 11 第②批 T3——库存账本引用+消费 handler 双下传;legacy 模式零注入零扰动。 # 迭代 20 T2——cap 检查回调四参注入) - _runner.set_shop_pool(_curio_deck, Callable(self, "_consume_pool_purchase"), Callable(self, "_feat_ticket_consider"), Callable(self, "_curio_cap_ok")) + _runner.set_shop_pool(_curio_deck, _consume_pool_purchase, _feat_ticket_consider, _curio_cap_ok) # 迭代 20 T4——凭证件三账注入(节点级/回合级一次性:注入即清零,未用即过期=寿命档语义) _runner.set_voucher_grants(_pending_rap, _time_sand_queue, _pending_insurance) _pending_rap = 0 @@ -1043,9 +1047,9 @@ func _settle(result: String) -> void: if _save_manager != null: _save_manager.clear_breakpoint() # 三结局清断点(迭代 8 卡①/T3——退出冒险=放弃流程+清除断点) _save_manager.write_to_disk() - state = "settling" + _state = State.SETTLING adventure_finished.emit(summary) - state = "idle" + _state = State.IDLE # ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明)---- diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 9ec68990..258db5b4 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -108,7 +108,7 @@ var _fx: EffectEngine func _fx_emit(event: String, payload: Dictionary) -> Array: if _fx == null: return [] - return _fx.dispatch(event, payload, Callable(self, "_fx_fact")) + return _fx.dispatch(event, payload, _fx_fact) # round_start 发射+施加(内容数据驱动扩展批 T3——grant_ap mode:queue=当轮入队,行动经济轮切换 @@ -219,10 +219,6 @@ func attune_has(item_id: String) -> bool: # 装备件表值读取(charge_n 等——TreasureItemTable 行;未装配不读表。magnitude 读数面随 T4 # 秘宝订阅迁移退役——行值经 value_from 在 rebuild 定影,零运行期按件取值) -func _attune_row(item_id: String) -> Dictionary: - return db.treasure_item_row(item_id) if _attune_has(item_id) else {} - - # 装配集参数合计(迭代 22 秘宝批 T3——常时参数面 kind 的装配集 Σmagnitude 读数口; # kind 分发查询先例=_attune_refresh_x_total bonus_refresh。零装配=0) func attune_param_sum(kind: String) -> int: @@ -395,8 +391,8 @@ func begin_battle(enc: Dictionary) -> bool: _fx_bonus_delta = {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0} var ledger_ref = enc.get("ledger", null) _ledger = ledger_ref if ledger_ref is RunLedger else RunLedger.new() # 缺省新账=调试/单场零扰动面 - _damage.fx_reveal_request = Callable(self, "_fx_enqueue_reveal") - _damage.fx_ac_request = Callable(self, "_apply_ac_bonus") + _damage.fx_reveal_request = _fx_enqueue_reveal + _damage.fx_ac_request = _apply_ac_bonus var feat_ids: Array[Variant] = enc.get("feats", []) as Array # 迭代 20 T3 换代(V14 旧实现摘除)——流派档位质变随重设计退役(奇物.md v1「不设流派标签、 # 不设档位机制」):FeatsEngine 求值与 rider_tier 注入拆除,散件单体效果=唯一形态 diff --git a/game/core/board_data.gd b/game/core/board_data.gd index cf9bb95f..7ca6da01 100644 --- a/game/core/board_data.gd +++ b/game/core/board_data.gd @@ -58,10 +58,6 @@ func board_variant_ids() -> Array[String]: # 中和格变异(迭代 12 T6 净化工兵——伤害型地形格变普通格;🔴 R4 甲仅限伤害型的过滤在调用方) -func clear_variants(c: Vector2i) -> void: - variants.erase(c) - - func neighbors8(c: Vector2i) -> Array: var out: Array[Vector2i] = [] for dy in range(-1, 2): diff --git a/game/core/config_db.gd b/game/core/config_db.gd index 36156356..5153622e 100644 --- a/game/core/config_db.gd +++ b/game/core/config_db.gd @@ -60,7 +60,8 @@ var _reload_gate := false # 数据配置 EC8——战斗结 func _init() -> void: - _resolver = ConfigDbResolver.new(self) # 协作类持门面引用(诊断/致命通道回呼) + _resolver = ConfigDbResolver.new(Callable(self, "push_diag"), Callable(self, "reset_warn_dedup"), + func() -> Variant: return board_gen.rows, func() -> Variant: return enemy.rows, func() -> Variant: return difficulty) # 协作类持门面引用(诊断/致命通道回呼) func push_diag(severity: String, category: String, message: String) -> void: @@ -71,20 +72,25 @@ func push_diag(severity: String, category: String, message: String) -> void: push_error("[ConfigDB/%s] %s" % [category, message]) -# 运行期数据致命(2026-09-18 用户拍板:越域记 Fatal 级日志后立即退出——数据配置通道 fail-fast 纪律补齐到查询期) +# 运行期数据致命(2026-09-18 用户拍板 fail-fast;迭代 27 T6——退出执行归根:核心层不再自携 +# quit/crash,越域记 Fatal 级日志后置 _fatal_flag,进程退出决策归组合根(main._ready 表加载 +# 失败已有同通道 fatal 屏+fail-fast 边界)——查询期消费方按返回值安全面走既有默认值回退) func _fatal(category: String, message: String) -> void: push_diag("fatal", category, message) - push_error("[ConfigDB/%s] FATAL: %s —— 数据配置致命,进程退出" % [category, message]) - var main_loop := Engine.get_main_loop() - if main_loop is SceneTree: - main_loop.quit(1) # 帧末退出,退出码 1——调用方须立即 return,勿再消费返回值 - else: - OS.crash("ConfigDB/%s fatal: %s" % [category, message]) + push_error("[ConfigDB/%s] FATAL: %s —— 数据配置致命(退出决策归组合根)" % [category, message]) + _fatal_flag = true + + +var _fatal_flag := false # 运行期致命旗(迭代 27 T6——退出归根承载;组合根/闸读面) + + +func is_fatal() -> bool: + return _fatal_flag func reset_diagnostics() -> void: _diagnostics.clear() - _resolver.reset_warn_dedup() # R1 告警去重注册表(协作类持钥——清空时点与拆分前一致) + _resolver._reset_warn.call() # R1 告警去重注册表(协作类持钥——清空时点与拆分前一致;通道 Callable 化随 T6) func diagnostics() -> Array: @@ -250,10 +256,6 @@ func level_count() -> int: # ---- GP+商店房实施批(2026-09-21):ShopTable 消费面(行集解析单点——runner/战斗编排禁自扫表)---- # ── 秘宝访问器(迭代 7 T1——消费面一律走访问器,禁直读行集)── # 活行集(非 retired 非 deferred)——投放校验/奖励节点池/呈现可用性的唯一取数口径 -func treasure_active_rows() -> Array: - return treasure_items.active_rows() if treasure_items != null else [] - - func treasure_item_row(item_id: String) -> Dictionary: return treasure_items.row_by_id(item_id) if treasure_items != null else {} @@ -270,13 +272,6 @@ func treasure_attunement_rows() -> Array: # 活行词表(掉落投放 effect_id 白名单——过渡渠道口径) -func treasure_live_ids() -> Dictionary: - var out := {} - for row in treasure_transitional_rows(): - out[String(row.get("item_id", ""))] = true - return out - - # ── 同调轴访问器已删除(迭代 21 T5——attunement_growth 谓词表随槽位收编退役:槽位数量=树同调位 # 节点点亮数,判定源=Attunement.slots;boss_kill_count/clear_count 计数域保留——非槽位硬消费在册)── diff --git a/game/core/config_db_resolver.gd b/game/core/config_db_resolver.gd index 73becc2a..15e81c87 100644 --- a/game/core/config_db_resolver.gd +++ b/game/core/config_db_resolver.gd @@ -11,12 +11,21 @@ class_name ConfigDbResolver extends RefCounted -var _db: ConfigDb # 门面引用(表读取 + 诊断/致命通道——_init 注入) +var _diag: Callable # 诊断/致命通道(迭代 27 T6——门面类型反持解耦,validator 先例:签名 (level, category, message)) +var _reset_warn: Callable # 去重注册表清空通道(reset_diagnostics 时点由门面驱动) +var _board_gen_rows_get: Callable # () -> Array——board_gen 行集活读(数据面经 Callable 注入,ConfigDb 类型环断根) +var _enemy_rows_get: Callable # () -> Array——enemy 行集活读 +var _difficulty_get: Callable # () -> DifficultyTable(可空——表缺省合法态) var _warn_dedup: Dictionary[String, bool] = {} # R1 四元组去重注册表(自 ConfigDb 迁入) -func _init(db_ref: ConfigDb) -> void: - _db = db_ref +func _init(diag_cb: Callable, reset_warn_cb: Callable, board_gen_rows_cb: Callable, + enemy_rows_cb: Callable, difficulty_cb: Callable) -> void: + _diag = diag_cb + _reset_warn = reset_warn_cb + _board_gen_rows_get = board_gen_rows_cb + _enemy_rows_get = enemy_rows_cb + _difficulty_get = difficulty_cb # 去重注册表清空(门面 load_tables / reset_diagnostics 调用——与拆分前清空时点逐位一致) @@ -27,21 +36,22 @@ func reset_warn_dedup() -> void: # ---- R1 难度行解析(2026-09-18 用户拍板修订:ceil 向上取整保证偏难;域越界与内容缺口 Fatal 退出)---- func resolve_board_gen(L: int, D: int) -> Dictionary: - return _resolve_progress(_db.board_gen.rows, L, D, "board_gen") + return _resolve_progress(_board_gen_rows_get.call(), L, D, "board_gen") func resolve_enemy_rowset(L: int, D: int) -> Array[Dictionary]: # 行集数组形态(2026-10-07 敌人表行集多行批——原单行 Dictionary 退役):ceil 命中 (l*,d*) 后 # 返回该坐标全部行、按表文件行序(行序即契约——同坐标多行文件内相邻由 EnemyTable.validate 兜底)。 # 空集表达 = 空数组(生产表下 ceil 命中坐标恒有行,空集仅表空时可达——validate 已拦)。 - return _resolve_progress(_db.enemy.rows, L, D, "enemy", true) + return _resolve_progress(_enemy_rows_get.call(), L, D, "enemy", true) # 难度曲线与Boss DifficultyTable 消费面(敌人系统.spawn 调用):(L,D) 精确匹配修饰乘数,越域值 clamp(GDD Edge Case——组合事故非配置事故) func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary: - if _db.difficulty == null: + var dt = _difficulty_get.call() + if dt == null: return {} - for row in _db.difficulty.rows: + for row in dt.rows: if int(row.get("level_index", 0)) == L and int(row.get("node_depth", -1)) == D: return { "hp_mult": clampf(float(row.get("hp_mult", 1.0)), 0.1, 10.0), @@ -52,7 +62,7 @@ func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary: func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all := false) -> Variant: if L < 1 or D < 0: - _db._fatal("request_oob", "%s 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [table_id, L, D]) + _diag.call("fatal", "request_oob", "%s 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [table_id, L, D]) return {} var level_keys: Array[int] = [] for row in rows: @@ -60,7 +70,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all if not level_keys.has(lv): level_keys.append(lv) if level_keys.is_empty(): - _db._fatal("structural", "%s 行集为空——锚行校验应已拦截" % table_id) + _diag.call("fatal", "structural", "%s 行集为空——锚行校验应已拦截" % table_id) return {} level_keys.sort() var l_star := _ceil_key(level_keys, L) @@ -96,7 +106,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String, return_all if not _warn_dedup.has(dedup_key): _warn_dedup[dedup_key] = true var verb := "clamp 解析(超出最大行取最大值)" if missing.contains("clamp") else "ceil 解析(向上取整保证偏难)" - _db.push_diag("warning", "resolve_" + missing, "%s 请求 (%d,%d) %s → (%d,%d) [%s]——2026-09-18 拍板" % [table_id, L, D, verb, l_star, d_star, missing]) + _diag.call("warning", "resolve_" + missing, "%s 请求 (%d,%d) %s → (%d,%d) [%s]——2026-09-18 拍板" % [table_id, L, D, verb, l_star, d_star, missing]) if return_all: var all_out: Array[Dictionary] = [] for row in rows: diff --git a/game/core/damage_controller.gd b/game/core/damage_controller.gd index 2a670816..eff8c656 100644 --- a/game/core/damage_controller.gd +++ b/game/core/damage_controller.gd @@ -95,14 +95,10 @@ func set_fx(fx: EffectEngine) -> void: func _fx_hits(event: String, payload: Dictionary) -> Array: if _fx == null: return [] - return _fx.dispatch(event, payload, Callable(self, "_fx_fact")) + return _fx.dispatch(event, payload, _fx_fact) # 事件发射薄面(免施加观测位——enemy_hp_threshold 等现无订阅者的事件保留零扰动面) -func _fx_dispatch(event: String, payload: Dictionary) -> void: - _fx_hits(event, payload) - - # 补充事实面(fact 取值第二优先源——payload 未携带的运行态读数:charge_layers 编排器每轮注入。 # 迭代 20 T1——hp_ratio 补入〔背水 when 面,敌技 damage_taken 载荷不带血比例时回落〕; # fx_reveal_request=揭示效果回调〔斩首学者 kill 订阅面——编排器注入,入队授予段末兑付〕) diff --git a/game/core/effect_engine.gd b/game/core/effect_engine.gd index af4f99da..6c660a29 100644 --- a/game/core/effect_engine.gd +++ b/game/core/effect_engine.gd @@ -413,21 +413,4 @@ static func validate_trigger_row(rid: String, row: Dictionary, triggers: Array, ## 按序改写族(收集→单点按声明序施加):modify_ticket 及一切改写窗口效果。 ## 按序消耗族(按订阅序消耗共享资源,耗尽即止;可选 fallback):counter_spend。 ## 即时执行族(每条命中各自执行一次,执行序=声明序):open_ticket、grant_ap(mode:transfer)、 -## rewrite_board、apply_status——发射点逐条执行。 -static func combo_of(kind: String) -> String: - match kind: - "add_ticket_rider", "attack_bonus", "grant_material", "counter_add", "shield", "heal", "ac_bonus", "damage_reduce", "bonus_delta": - return "additive" - "grant_ap": - return "additive" # mode:transfer 由发射点即时执行族承载(转移非加法) - "yield_mult", "damage_mult": - return "multiplicative" - "spawn_spell", "overflow", "spike": - return "any_true" - "modify_ticket": - return "ordered_write" - "counter_spend": - return "ordered_spend" - "open_ticket", "apply_status", "blast_scaled": - return "immediate" - return "immediate" # 默认即时执行(每条命中各自执行一次) +## rewrite_board、apply_status——发射点逐条执行。 \ No newline at end of file diff --git a/game/core/feats_engine.gd b/game/core/feats_engine.gd deleted file mode 100644 index 7141f26d..00000000 --- a/game/core/feats_engine.gd +++ /dev/null @@ -1,39 +0,0 @@ -# VERTICAL SLICE - NOT FOR PRODUCTION -# Date: 2026-10-04(双层 BD 实施批 T1——标签档位激活引擎) -# 流派成型判据机制化(整合定稿 设计工具 1——以撒套装/小骨铭文先例): -# 同流派标签 relic 计数 ≥2 → 档一(流派核心规则);≥4 → 档二(完全体);<2 = 散件(单体保底—— -# 每件效果独立生效,档位只做质变不做启动,反例 B-2)。 -# 纯函数零 await 零 RNG 零副作用(Attunement 求值服务同款纪律);核心层零特判延伸——引擎只读 -# feat_table 行集与持有集,接线点消费 tiers/hooks 投影。 -class_name FeatsEngine -extends RefCounted - - -## 档位求值:feat_ids 持有集 × 行集 → {archetype: tier}(tier ∈ 0/1/2;无行流派不出现) -static func tiers_for(feat_ids: Array, table: FeatTable) -> Dictionary: - var counts := {} - for rid in feat_ids: - var row: Dictionary[Variant, Variant] = table.row_by_id(String(rid)) if table != null else {} - if row.is_empty(): - continue - var arch := String(row.get("archetype", "")) - counts[arch] = int(counts.get(arch, 0)) + 1 - var out := {} - for arch in counts.keys(): - var n := int(counts[arch]) - if n >= 4: - out[arch] = 2 - elif n >= 2: - out[arch] = 1 - else: - out[arch] = 0 - return out - - -## 挂点投影随 T3 退役(内容数据驱动扩展批——hook 词族消亡:订阅投影=效果引擎 rebuild 装载序 -## (feats 表序),投影稳定性由 EffectEngine._triggers 声明序承载) - - -## 成型判读(读数面口径):流派 tier ≥1 = 成型(档一即判——需求卡片完成定义「标签+档位激活引擎可判成型」) -static func is_formed(tiers: Dictionary, archetype: String) -> bool: - return int(tiers.get(archetype, 0)) >= 1 diff --git a/game/core/feats_engine.gd.uid b/game/core/feats_engine.gd.uid deleted file mode 100644 index 1d722a33..00000000 --- a/game/core/feats_engine.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://6d12lkx5w2x8 diff --git a/game/core/level_runner.gd b/game/core/level_runner.gd index d88c34a6..dfe1445d 100644 --- a/game/core/level_runner.gd +++ b/game/core/level_runner.gd @@ -160,7 +160,7 @@ func start_level(level_index: int, seed_val: int, base_override_in: Dictionary = # 三键 {atk, max_hp, armor_class}、缺键/-1 = 0、负值 clamp 0。游戏直入口仍恒白板—— # 注入面只服务数值验证工具,非玩家可达路径) _external = false - _rest_handler = Callable(self, "rest_heal_amount") + _rest_handler = rest_heal_amount _ledger = {"hero_hp": 0, "gp": 0} var row := _find_level_row(level_index) if row.is_empty(): diff --git a/game/core/minesweeper_rules.gd b/game/core/minesweeper_rules.gd index c527c41a..0c71da3d 100644 --- a/game/core/minesweeper_rules.gd +++ b/game/core/minesweeper_rules.gd @@ -38,10 +38,6 @@ func _init(db_ref: ConfigDb, action_economy_ref: ActionEconomy) -> void: # ---- 板态 ---- -func has_board() -> bool: - return _board != null - - # 板产物只读出口(E2 收口——战斗结算 快照 / 组合根 rebuild / 探针经此取板,禁再直读字段) func current_board() -> BoardData: return _board diff --git a/game/core/save_manager.gd b/game/core/save_manager.gd index d2dbcb3e..4a56eb8f 100644 --- a/game/core/save_manager.gd +++ b/game/core/save_manager.gd @@ -432,14 +432,6 @@ func set_attune_refresh_level(n: int) -> void: # 同调位计数源组装(Attunement 服务取数口——clear_count = 逐关通关次数合计〔含重复关,底稿 §2.1 口径〕; # clear_count(level_index) 逐关访问器零改动复用) -func attunement_counters() -> Dictionary: - var total := 0 - var counts: Dictionary[Variant, Variant] = _data["meta"]["level_clear_counts"] - for k in counts.keys(): - total += int(counts[k]) - return {"clear_count": total, "boss_kill_count": boss_kill_count()} - - func recent_summaries() -> Array: return _data["meta"]["recent_summaries"].duplicate(true) # 值拷贝出户(本类契约「禁碰内部结构」——消费方直改不得渗入存档内存态) diff --git a/game/core/treasure_items.gd b/game/core/treasure_items.gd index f01bff30..dc619f90 100644 --- a/game/core/treasure_items.gd +++ b/game/core/treasure_items.gd @@ -65,14 +65,6 @@ func inventory_count() -> int: # 持有数(迭代 10 奖励重构批——同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有) -func holding_count() -> int: - var n := 0 - for rec in _items: - if not bool(rec.get("used", false)): - n += 1 - return n - - # 同调位守卫谓词——迭代 11 第①批 T7 退役(读取源 transitional_sync_slots 过渡键已随表退役; # 容量闸正主 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏—— # T8 冒烟「过渡键读取点归零」断言覆盖) @@ -80,14 +72,6 @@ func holding_count() -> int: # 持有件记录(迭代 10 追加批——奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标) # 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含) -func discard_item(handle: int) -> bool: - for i in _items.size(): - if int(_items[i].get("handle", -1)) == handle and not bool(_items[i].get("used", false)): - _items.remove_at(i) - return true - return false - - # 板级零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级): # 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用); # 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——反哺批⑤自动兑付无玩家入口) diff --git a/game/presentation/curio_interact_controller.gd b/game/presentation/curio_interact_controller.gd index 7200ad54..71c55110 100644 --- a/game/presentation/curio_interact_controller.gd +++ b/game/presentation/curio_interact_controller.gd @@ -52,10 +52,6 @@ func _init(ctx: CurioFlowContext) -> void: # 停点在途判据(组合根 _on_battle_end_continue 分派面——战斗总结确认后开三选一) -func has_pending() -> bool: - return not _pending_curio.is_empty() - - # 续玩停点恢复入口(true=已接手呈现,调用方中止后续分派)。空候选降级(2026-10-06 用户令): # 候选 0 件不开选择面(空面无卡无钮=死面)——消化停点后按上下文直落后续推进(战斗总结面已 # 给过玩家确认拍,三选一环节整体跳过) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index bf56a4b4..4b219c02 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -42,6 +42,14 @@ # autoload 占净名),存档读盘挂 autoload _ready(时序与旧 main._ready 读盘等价)。 extends Control +# autoload 节点路径单点(迭代 27 T6 P2-2 降格实现:审计处方「编译期全局名」与本仓 --script +# 模式编译器不解析裸 autoload 标识符冲突〔run_all.gd 头注+pacing 实证〕——字符串拼写风险 +# 收敛到四常量,类型面由读视图属性承载) +const AUTOLOAD_PATH_CFG := "/root/ConfigDB" +const AUTOLOAD_PATH_RNG := "/root/RngService" +const AUTOLOAD_PATH_SAVE := "/root/SaveManager" +const AUTOLOAD_PATH_META := "/root/MetaProgression" + const UiAssembler := preload("res://presentation/ui_assembler.gd") const Narrator := preload("res://presentation/battle_narrator.gd") const ModalStack := preload("res://presentation/modal_stack.gd") @@ -62,10 +70,10 @@ var run_scope = null # 会话状态承载(run_scope.gd——迭 # 节点路径双模式通用;组合根调用点/探针挂载面 main.save_manager 语义零迁移)。 var save_manager: SaveManagerCore: get: - return get_node("/root/SaveManager") + return get_node(AUTOLOAD_PATH_SAVE) var meta: MetaProgressionCore: get: - return get_node("/root/MetaProgression") + return get_node(AUTOLOAD_PATH_META) var logger = null # 试玩日志器(JSONL 持久化 + 卡死诊断转储——呈现层组件) var _debug_entry := false # 调试会话门控(迭代 18 卡⑦——DS_DEBUG=1:启动即开浮窗+两屏角标点亮;_ready 赋值) var _seed_edit: LineEdit @@ -175,7 +183,7 @@ func _begin_session(adventure_session: bool) -> void: func _ready() -> void: - db = get_node("/root/ConfigDB") + db = get_node(AUTOLOAD_PATH_CFG) if not db.is_ready: var ok: bool = db.load_tables("res://data/tables", false) if not ok: @@ -187,8 +195,8 @@ func _ready() -> void: settings_store = SettingsStore.new() settings_store.load_settings() settings_store.apply() - _debug = DebugSessionScript.new(db, get_node("/root/RngService"), - Callable(self, "_debug_hero_override"), Callable(self, "_seed_value")) + _debug = DebugSessionScript.new(db, get_node(AUTOLOAD_PATH_RNG), + _debug_hero_override, _seed_value) _debug.battle_created.connect(_on_debug_battle_created) _debug.battle_ready.connect(_on_debug_battle_ready) _build_ui() @@ -482,7 +490,7 @@ func _resume_from_menu() -> void: # 冒险编排构建+接线(开新局/续玩共用——信号连接只在组合根,ADR-0001;断点注入同口) func _wire_adventure() -> void: - run_scope.adventure = AdventureRunner.new(db, get_node("/root/RngService")) + run_scope.adventure = AdventureRunner.new(db, get_node(AUTOLOAD_PATH_RNG)) run_scope.adventure.step_row.connect(_on_step_row) run_scope.adventure.round_started.connect(_on_round_started) run_scope.adventure.board_changed.connect(_on_board_changed) @@ -1343,7 +1351,7 @@ func _debug_run_data() -> Dictionary: func _current_master_seed() -> int: if run_scope.adventure != null: return int(run_scope.adventure.master_seed) - return int(get_node("/root/RngService").master_seed) + return int(get_node(AUTOLOAD_PATH_RNG).master_seed) func _close_debug_overlay() -> void: diff --git a/game/presentation/views/curio_pick_overlay.gd b/game/presentation/views/curio_pick_overlay.gd index e25a3180..de98d26d 100644 --- a/game/presentation/views/curio_pick_overlay.gd +++ b/game/presentation/views/curio_pick_overlay.gd @@ -167,10 +167,6 @@ func press_refresh() -> void: _refresh_btn.pressed.emit() -func selected_id() -> String: - return _selected - - # 兑付反馈态(curio_resolved 到达——结果行追加在头部之后;唯一出口 = 继续) func show_result(text: String) -> void: _label.text = _header + "\n" + text diff --git a/game/presentation/views/stage_world.gd b/game/presentation/views/stage_world.gd index 173af733..36d4fe77 100644 --- a/game/presentation/views/stage_world.gd +++ b/game/presentation/views/stage_world.gd @@ -13,9 +13,4 @@ extends Node2D var world_font: Font = null # 字体注入槽位(null = ThemeDB.fallback_font——BattleStageView.set_world_font 注入) # C6 headless 可验判据(ADR-0003 Decision 4):注入字体非空 + 中文字形 has_char + 采样文本宽度 > 0。 -# 实际光栅观感归批末目检清单。 -func font_probe() -> Dictionary: - var f: Font = world_font if world_font != null else ThemeDB.fallback_font - var has_char: bool = f.has_char("人".unicode_at(0)) - var width: float = f.get_string_size("兑现演出区", HORIZONTAL_ALIGNMENT_LEFT, -1, 14).x - return {"font": f, "has_char": has_char, "width": width} +# 实际光栅观感归批末目检清单。 \ No newline at end of file diff --git a/game/presentation/views/standee_body.gd b/game/presentation/views/standee_body.gd index 85dd8685..38ca6ffe 100644 --- a/game/presentation/views/standee_body.gd +++ b/game/presentation/views/standee_body.gd @@ -37,7 +37,10 @@ var _portrait_flip := false # 主角三态切换(迭代 6 T3):state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理 # (缺位回退链延伸——三态素材缺任一不炸不空)。敌站桩不注册不消费。 var _hero_tex := {} -var _hero_state := "idle" +enum HeroState { IDLE, RESET, ATTACK, HIT } # 主角三态+复位哨兵(迭代 27 T6——裸字符串 enum 化;外部 String 契约=register/set 形参零变化) +const _HS_NAME := {HeroState.IDLE: "idle", HeroState.RESET: "reset", HeroState.ATTACK: "attack", HeroState.HIT: "hit"} +const _HS_VALUE := {"idle": HeroState.IDLE, "reset": HeroState.RESET, "attack": HeroState.ATTACK, "hit": HeroState.HIT} +var _hero_state: int = HeroState.IDLE var _state_revert_tw: Tween var _order := 1 var _intent_glyph := "" @@ -330,7 +333,7 @@ func _revive() -> void: modulate = Color(1, 1, 1) rotation_degrees = 0.0 if _kind == "hero": - _hero_state = "reset" # 强制回 idle 态(绕过同态早退) + _hero_state = HeroState.RESET # 强制回 idle 态(绕过同态早退) set_hero_state("idle") _refresh_portrait_glyph() if slot_row != null: @@ -368,11 +371,12 @@ func register_hero_state(state: String, tex: Texture2D) -> void: # 状态切换:现纹理换注册态(attack/hit);未注册态名或缺位 = 保持现纹理不炸。 # 非 idle 态自动短驻回归 idle(0.35s——演出一拍即回,结算收尾另有显式 idle 兜底)。 func set_hero_state(state: String) -> void: - if _kind != "hero" or state == _hero_state: + var target: int = _HS_VALUE.get(state, -1) + if _kind != "hero" or target == _hero_state: return if not _hero_tex.has(state) and state != "idle": return - _hero_state = state + _hero_state = target if _portrait_sprite != null and _hero_tex.has(state): _portrait_sprite.texture = _hero_tex[state] if _state_revert_tw != null and _state_revert_tw.is_valid(): @@ -384,14 +388,14 @@ func set_hero_state(state: String) -> void: func _settle_hero_idle() -> void: - _hero_state = "idle" + _hero_state = HeroState.IDLE if _portrait_sprite != null and _hero_tex.has("idle"): _portrait_sprite.texture = _hero_tex["idle"] # 三态探针出口(fx_probe——当前态名+注册态集合) func hero_state_probe() -> Dictionary: - return {"state": _hero_state, "registered": _hero_tex.keys()} + return {"state": _HS_NAME.get(_hero_state, "idle"), "registered": _hero_tex.keys()} func _portrait_color() -> Color: diff --git a/roadmap.md b/roadmap.md index 61038f68..e299fe7c 100644 --- a/roadmap.md +++ b/roadmap.md @@ -24,8 +24,8 @@ ## 当前位置(2026-10-14 刷新) - **里程碑语义(2026-10-09 立规换代,详见下方里程碑表的换代注)**:现行里程碑 = **M3「Alpha 版本的完整实现」**——用户未明令宣布完成前停留 M3,迭代只排 M3 需求;M4=Beta 版本的完整实现,一律不排期。下文里程碑表的旧编号(M-3/M-4)保留作历史序列,语义以本节为准。 -- **迭代进度**:迭代 19 五卡全部已入库(2026-10-08);**迭代 20=专长实装批 ✅ 已入库**(2026-10-11,出口闸全绿;残留人闸=实机目检五件+V20/V21/V22 人工核对半,07 在册);**迭代 21=天赋树实装批 ✅ 已入库(2026-10-12 U0 开工单会话交付,T0-T7 全绿;残留人闸=实机目检四柜台)**;**迭代 22=秘宝体系实现重构落地批 ✅ 已入库(2026-10-12 单会话交付,T1-T7 全绿;残留人闸=实机目检秘宝解锁-装配流程)**;**迭代 23=锻造工坊实装批 ✅ 已入库(2026-10-12 单会话交付,T0-T7 全绿;残留人闸=实机目检工作间形态与购买交互)**;**迭代 24=敌人表内容修批(三关)✅ 已入库(2026-10-13 /goal+iter U0 开工单会话交付——N1 (1,2) 降档 hp22/ac2·N2 毒吐盾击两行六池·N3 敌方 attack_type 轴全填 normal+敌方结算克制消费点;冒烟 949/0+run_all 25 套+重闸全绿+MC 同种子配对 40 局×both 位移在带;实现报告=design/qa/iter24-实现报告-2026-10-13.md;残留人闸=实机目检三观测〔07 ㉓〕;代拍板追认单+GDD 反哺点五条待用户;**本迭代位仍预留后续追加卡——用户将把其他需求陆续编入本迭代**);;**迭代 25=UI 全局主题批 ✅ 已入库**(2026-10-13 用户令编入,/goal+iter U0 单会话交付:全局主题资源 main_theme.tres 挂载+两处局部做法退役+冒烟 V1-V6 断言 38 条,冒烟 987/0+重闸全绿;实现报告=design/qa/iter25-实现报告-2026-10-13.md;残留人闸=实机目检全屏巡检〔07 ㉔〕);**迭代 26=UI 交互与观感批已编入待开工**(2026-10-14 用户令;需求卡片 v1.2 零待裁点;开工门=U0——用户新会话 /iter 26 发令;本迭代位预留后续追加卡,同迭代 24 先例);**迭代 27=代码架构技术债修复批 🔄 U0 已开工**(2026-10-14 用户令编入+同日 /goal+iter 27 发令;需求卡片 v1.2 三点全裁零待裁——①第三个 autoload/②单类持四账/③审计五段);**迭代 28+**=M4 数值批(Beta 位·未排期——用户令 2026-10-07:后续新迭代一律不先做 M4;迭代 24/25/26/27 占位后本批顺延)。 -- **近期主战场**:迭代 20/21/22/23/24/25/26 残留人闸(实机目检六批,07 在册);**在飞=迭代 27 代码架构技术债修复批**(需求卡片 v1.2 零待裁,U0=2026-10-14 发令开工;M4 数值批殿后顺延 28+,各批 provisional 终值归其统一标定)。 +- **迭代进度**:迭代 19 五卡全部已入库(2026-10-08);**迭代 20=专长实装批 ✅ 已入库**(2026-10-11,出口闸全绿;残留人闸=实机目检五件+V20/V21/V22 人工核对半,07 在册);**迭代 21=天赋树实装批 ✅ 已入库(2026-10-12 U0 开工单会话交付,T0-T7 全绿;残留人闸=实机目检四柜台)**;**迭代 22=秘宝体系实现重构落地批 ✅ 已入库(2026-10-12 单会话交付,T1-T7 全绿;残留人闸=实机目检秘宝解锁-装配流程)**;**迭代 23=锻造工坊实装批 ✅ 已入库(2026-10-12 单会话交付,T0-T7 全绿;残留人闸=实机目检工作间形态与购买交互)**;**迭代 24=敌人表内容修批(三关)✅ 已入库(2026-10-13 /goal+iter U0 开工单会话交付——N1 (1,2) 降档 hp22/ac2·N2 毒吐盾击两行六池·N3 敌方 attack_type 轴全填 normal+敌方结算克制消费点;冒烟 949/0+run_all 25 套+重闸全绿+MC 同种子配对 40 局×both 位移在带;实现报告=design/qa/iter24-实现报告-2026-10-13.md;残留人闸=实机目检三观测〔07 ㉓〕;代拍板追认单+GDD 反哺点五条待用户;**本迭代位仍预留后续追加卡——用户将把其他需求陆续编入本迭代**);;**迭代 25=UI 全局主题批 ✅ 已入库**(2026-10-13 用户令编入,/goal+iter U0 单会话交付:全局主题资源 main_theme.tres 挂载+两处局部做法退役+冒烟 V1-V6 断言 38 条,冒烟 987/0+重闸全绿;实现报告=design/qa/iter25-实现报告-2026-10-13.md;残留人闸=实机目检全屏巡检〔07 ㉔〕);**迭代 26=UI 交互与观感批已编入待开工**(2026-10-14 用户令;需求卡片 v1.2 零待裁点;开工门=U0——用户新会话 /iter 26 发令;本迭代位预留后续追加卡,同迭代 24 先例);**迭代 27=代码架构技术债修复批 ✅ 已入库**(2026-10-14 /goal+iter U0 开工单会话交付:三屏场景化+会话物化+五段 Segment+RunLedger+ModalFlow+类型闸+卫生件五波全落;逐位一致对拍三变体五跑全同;实现报告=design/qa/iter27-实现报告-2026-10-14.md;残留人闸=实机目检五件〔07 ㉖〕;代拍板追认单十五项+GDD 反哺点两条待用户);**迭代 28+**=M4 数值批(Beta 位·未排期——用户令 2026-10-07:后续新迭代一律不先做 M4;迭代 24/25/26/27 占位后本批顺延)。 +- **近期主战场**:迭代 20/21/22/23/24/25/26 残留人闸(实机目检六批,07 在册);**下一前沿=待用户发令**(迭代 27 已入库收口;M4 数值批殿后顺延 28+,各批 provisional 终值归其统一标定;迭代 20~27 残留人闸七批 07 在册)。 - **实时状态**:以根目录 [active.md](active.md) 顶部「当前状态快照」节为准(`stage.txt` 已定性为旧阶段开关,仅存档)。 ## 主线路线(里程碑 × 迭代序列) @@ -83,7 +83,7 @@ | 迭代 24 | **①敌人表内容修批(三关)(2026-10-13 用户令编入;**本迭代位预留后续追加卡**——用户将把其他需求陆续编入本迭代)**:需求卡片=`design/research/需求卡片-敌人表内容修批-2026-10-13.md`(**v1.1**——grilling 复核九问 Q1-Q9 已全裁、零待裁点、开工门=U0)。三件事合一:**N1** 第一关 (1,2) 降档(`enemy.tres:48` 改两值:`hp` 50→22、`armor_class` 3→2;**敌种 `bugbear` 不动**——`enemy.tres:594-600` 注释载明该行 50 血系 2026-09-24 用户裁决的设计而非填错,且 `hobgoblin` 无立绘,故「改敌种」案已否决;本批定性=阶名化解方案实机未达预期按 2026-10-13 体验反馈重裁)· **N2** 第二/三关普通敌技能投放(`skills.tres` 12→14 行:新增 `venom_spit` 毒吐 damage 1.6/CD2 挂 (2,5)/(2,6)/(2,7) 巨蜘蛛族、`shield_bash` 盾击 damage 1.7/CD3 挂 (3,0)/(3,5)/(3,7) 灰矮人族;**首遇教学位 (2,4) 池不动**,venom_spit 自 (2,5) 起投;两新行均不带 `rider_mult`——低 atk 下恒被 `damage_floor` 兜成 1 与 0.75 档等效)· **N3** 敌方 `attack_type` 加列并全填 `normal`(挂**敌种行**、20 行同值,不做差异化;`enemy_table.gd` 加枚举校验 + `unit_container.gd:119` 加单位快照 + `battle_orchestrator.gd:1406` 第 9 参由 `""` 改传值;全局平移=穿重甲受伤 −1 / 魔法护甲 +1 / 轻甲 0,使锻造甲路线首次具备战斗意义)。**契约要点**:池与敌种键属**契约级**,改必同步 `smoke_all.gd:3211` `expect_pools`(N1 因键值不变零改动、N2 同步 6 键);节点图拓扑零触碰(A1 关卡节点图重构的地盘);(1,1) 地精一伙不在本批范围(「第二个节点」字面偏差已登记 06)。**收口去向**:06「第一关遭遇表数值异常」「第二关技能池与投放」与锻造工坊条迁移面⑥ 三条一并销号;08「二·内容量」敌种池行注摘除 | ✅ 已入库(2026-10-13 单会话交付,出口闸全绿:冒烟 949/0+run_all 25 套〔485 批基线漂移 24→25 在案〕+探针 click/full+render_gate golden 0.00%+doc 三闸/熵增对基线零新增+MC 配对三组读数;实现报告=design/qa/iter24-实现报告-2026-10-13.md;残留人闸=实机目检三观测 07 ㉓) | | 迭代 25 | **①UI 全局主题批(引擎原生控件接管皮肤)(2026-10-13 用户令编入;UI 美化三层诊断 · 第一层第一条)**:**✅ 已全批入库(2026-10-13 /goal+iter U0 开工单会话交付,worktree g-plan2-iter25-theme 分支 iter25-theme-20261013)**——**T0 主题资源**:新建 `game/assets/theme/main_theme.tres` 挂 `project.godot` `gui/theme/custom`(15 类型全覆盖:V/HScrollBar·ScrollContainer·H/VSlider·TooltipPanel/Label·LineEdit·SpinBox·H/VSeparator + 预盖 CheckBox/CheckButton/OptionButton/PopupMenu 弹层;`default_font` 显式指向现役同字体文件=V6 中文字形防御;位图由 `tools/gen_theme_icons.py` 一次性生成落 `game/assets/art/ui/gen/`,色值溯源表落 `game/assets/theme/README.md`)· **T1 换代退役**:`ui_kit.gd` 的 `tooltip_theme()`/`_tip_theme` 与 `styled_slider()` 样式段、`ui_assembler.gd` 挂载行同批删除(禁新旧并存),settings_overlay 几何内联+debug_overlay `_slider_row` 双轨消隐· **T2 断言与文档同步闸**:冒烟新增 V1-V6 主题词条断言 38 条。**顺手销号**=06 第三层「UI 元素细节修」之①(音量滑轨两段对比度重配=已填充段 BG_ROOT #3A2E22 对暖石 #B8A582 实测 5.49:1)。**契约遵守**:🔴 Button 类型词条零接触(冒烟断言在闸)· 🔴 focus 族词条零定义(全主题扫描断言)· 🔴 零新增美术素材(位图=脚本生成纯色/几何)· 🔴 全局纹理过滤未动。**引擎事实订正(呈报追认)**:Godot 4.7.2 源码+默认主题链双查实证——①CheckButton 图标词条名与 CheckBox 同套 `checked`/`unchecked`(卡片 v1.2 注③「CheckButton 用 on/off」不成立,按引擎事实落位)②Slider 无 `grabber_pressed` 图标槽(冻结表「柄·按」值 GOLD_DEEP 留档未落位)③ScrollBar 柄是 StyleBox 非 Icon(无需位图)④4.7 导入参数已无逐贴图 filter 标志(位图原生尺寸消费无滤波伪影)。**闸**:冒烟 987/0(基线 949+本批 38 断言)+run_all 25 套+probe click/full+render_gate golden 0.00%+doc 三闸/熵增对基线零新增;MC 零跑(零数值零行为=纯呈现)。**收口去向**:06「UI 全局主题资源缺失」条移除+「UI 元素细节修」①销号;08 升 v1.3(三·界面主题行→在役、五·表现细节修行注①销号);07 ㉔ 全屏巡检登记;实现报告=design/qa/iter25-实现报告-2026-10-13.md。**待用户**:实机目检全屏巡检+专项观察点二(07 ㉔,登记不阻塞)+代拍板追认单(位图不入 git 政策口径/CheckButton 词条名订正等)+GDD 反哺点(组件规格是否立条款入 art-bible §7 等)。↓ 原任务登记:三件事合一——**T0** 新建 `game/assets/theme/main_theme.tres` 并挂 `project.godot` 的 `gui/theme/custom`,覆盖滚动条 / 滑轨 / 悬停气泡 / 输入件 / 分隔线等引擎原生控件,并预盖 CheckBox / CheckButton / OptionButton 及其弹层 PopupMenu(须在新主题内显式设 `default_font`=现役同一字体文件;新增小位图落 `game/assets/art/ui/gen/`,色值溯源表落 `game/assets/theme/README.md`)· **T1** 退役 `ui_kit.gd` 的 `tooltip_theme()` 与 `styled_slider()` 两处局部做法、消 `debug_overlay._slider_row` 双轨(🔴 旧实现同批删除)· **T2** 断言与文档同步闸。契约要点:🔴 Button 类型词条零接触 · 🔴 focus 族词条零定义 · 🔴 不新增美术素材、不改美术圣经 §7 · 🔴 全局纹理过滤不动 | ✅ 已入库(出口闸全绿+实机目检归用户 07 ㉔ 在册) | T0-T2≈0.5–1d(实际单会话) | | 迭代 26 | **①UI 交互与观感批(2026-10-14 用户令编入;UI 美化三层诊断 · 第一层第二/三条+第三层四件;本迭代位预留后续追加卡,同迭代 24 先例)**:需求卡片=`design/research/需求卡片-UI交互与观感批-2026-10-13.md`(**v1.2**——grilling 两轮八裁全落、零待裁点、卡片成立)。六项:**T1 按钮五态矩阵**(悬停=乙案 modulate 提亮〔与 primary_button 现行 `CANDLE.lightened(0.06)` 同纪律〕/禁用=本族底+α0.45 幽灵化/焦点=INK 2px 内缩 1px/危险钮补本族 pressed;走逐实例 override,迭代 25 两条主题红线断言维持不换代)· **T2 全局纹理过滤**(`project.godot` `default_texture_filter=0`+vignette/gradient_base/beams 显式 LINEAR 白名单+翻转面全枚举)· **T3 <4.5:1 换墨**(站桩顺位/护甲徽标换同族亮档+RAP 行 STONE_DEEP,机检断言锚定)· **T4 光柱连续 alpha 渐变**(0→0.05→0 横向+纵向衰减)· **T5 元素细节余②③**(BCard 图标槽明度差降档/商店卡片区垂直居中/营地四栏等高)· **T5b 诊断转储钮移调试浮窗**(`dump_diagnosis` 功能保留)。🔴 契约 5 条(主题层零新增词条/零新美术素材/棋盘零触碰/对比度 ≥4.5:1/旧实现同批清理);验证闸 V1-V7;代拍板白名单八项;反哺点四条;**渲染闸 golden 面处置**=T6 第 4 步(徽标换墨落 board golden 裁剪区:闸红→核热区限预期面→显式重制基线+呈报→复闸,超预期=L3)。**范围订正**(grilling 实查,iter25 滑轨条同款先例):②垫块仅三选一 BCard(商店卡实为纯文字行卡)/③仅商店+营地(三选一已居中)/危险钮「无按下态」订正为「在但皮肤错配」。估时 T0-T6≈2–2.5d | **✅ 已全批入库(2026-10-14 /goal+iter U0 开工单会话交付,worktree g-plan2-iter26-ui 分支 iter26-ui-20261014 七提交)**——T0 基线+枚举(含卡片 §2.2 枚举漏项勘误)/T1 按钮五态(悬停 modulate 提亮/禁用 α0.45 幽灵化/焦点 INK 2px 内缩 1px/危险钮本族按压态)/T2 全局 Nearest+平滑渐变件 LINEAR 白名单/T3 三处换墨(GOLD_BRIGHT/HERO_BLUE_BRIGHT/STONE_DEEP,6.47/5.88/5.98:1)/T4 光柱连续 alpha/T5 图标槽柔和档+商店居中+营地四栏等高/T5b 诊断钮迁调试浮窗;冒烟 987→1055/0(+68 断言)+run_all 25 套+probe click/full+render_gate golden 0.00%(登记即结未重制)+doc 三闸/熵增对基线零新增;MC 零跑(零数值零行为=纯呈现);实现报告=design/qa/iter26-实现报告-2026-10-14.md;残留人闸=实机目检五项(07 ㉕);待用户=代拍板追认单十三项+GDD 反哺点四条 | T0-T6≈2–2.5d(实际单会话) | -| 迭代 27 | **①代码架构技术债修复批(2026-10-14 用户令编入;来源=[代码架构与实现质量审计报告-2026-10-14](design/research/代码架构与实现质量审计报告-2026-10-14.md),双席审计基线 97de02c,已逐条复核)**:需求卡片=`design/research/需求卡片-代码架构技术债修复批-2026-10-14.md`(**v1.2——三点全裁零待裁〔2026-10-14 用户三裁全按推荐案:①第三个 autoload/②单类持四账/③审计五段〕,卡片成立**)。五波:· **波一 场景拆分+局生命周期物化(≈4-5d)**〔P0-1/P0-2:屏容器 .tscn 切分=开始菜单根场景+营地场景+冒险场景〔内含 level_map/battle 两容器〕,视图代码原样搬、**40+ views 不转 .tscn**;SaveManager/MetaProgression 升格跨场景持有者;「一局」物化为可整体 `queue_free()` 的节点,`main.gd:647-657`/`:555-559` 手工清单消亡;13 个 smoke 探针挂载假设同批迁移〕· **波二 `_settle` 拆段(≈1.5d)**〔P1-1:五段 Segment 类 grant/aura/combat/decay/verdict,24 处 kind 分派随段归位,姊妹病灶 `damage_controller.gd:272` 148 行同批拆〕· **波三 账本收口(≈1.5d)**〔P1-4 共享四账 Ledger 类化+P1-2 刷新双账删 main 副本+P1-3 summary 增 `gp_final` 键、删 `gp_shadow_ledger.gd`〕· **波四 组合根可测化抽取+模态协议内聚(≈1.5-2d)**〔P1-5/P2-7;**须在波一落地后重估抽取面再动**〕· **波五 类型闸收尾+卫生件包(≈3d)**〔P1-6 拉闸+存量 73 处收口,**须在其他重构全部落地后**;P2 七组顺手带〕。🔴 契约 6 条(**零行为变更·重构前后同种子完整局输出逐位一致**/只拆不重写/views 不转 .tscn/探针契约零破坏/信号纪律不变/执行顺序锁+渲染零漂移);**核心验收=逐位一致对拍**(RngService 单盐流在役);验证闸 V1-V9;代拍板白名单九项;反哺点两条(architecture+ADR 系列/code-conventions)。 | **🔄 U0 已开工**(2026-10-14 用户令编入+同日 /goal+iter 27 发令,单会话交付进行中;卡片 v1.2 三点已裁;本迭代位预留后续追加卡,同迭代 24/26 先例) | T0-T7≈12-14d | +| 迭代 27 | **①代码架构技术债修复批(2026-10-14 用户令编入;来源=[代码架构与实现质量审计报告-2026-10-14](design/research/代码架构与实现质量审计报告-2026-10-14.md),双席审计基线 97de02c,已逐条复核)**:需求卡片=`design/research/需求卡片-代码架构技术债修复批-2026-10-14.md`(**v1.2——三点全裁零待裁〔2026-10-14 用户三裁全按推荐案:①第三个 autoload/②单类持四账/③审计五段〕,卡片成立**)。五波:· **波一 场景拆分+局生命周期物化(≈4-5d)**〔P0-1/P0-2:屏容器 .tscn 切分=开始菜单根场景+营地场景+冒险场景〔内含 level_map/battle 两容器〕,视图代码原样搬、**40+ views 不转 .tscn**;SaveManager/MetaProgression 升格跨场景持有者;「一局」物化为可整体 `queue_free()` 的节点,`main.gd:647-657`/`:555-559` 手工清单消亡;13 个 smoke 探针挂载假设同批迁移〕· **波二 `_settle` 拆段(≈1.5d)**〔P1-1:五段 Segment 类 grant/aura/combat/decay/verdict,24 处 kind 分派随段归位,姊妹病灶 `damage_controller.gd:272` 148 行同批拆〕· **波三 账本收口(≈1.5d)**〔P1-4 共享四账 Ledger 类化+P1-2 刷新双账删 main 副本+P1-3 summary 增 `gp_final` 键、删 `gp_shadow_ledger.gd`〕· **波四 组合根可测化抽取+模态协议内聚(≈1.5-2d)**〔P1-5/P2-7;**须在波一落地后重估抽取面再动**〕· **波五 类型闸收尾+卫生件包(≈3d)**〔P1-6 拉闸+存量 73 处收口,**须在其他重构全部落地后**;P2 七组顺手带〕。🔴 契约 6 条(**零行为变更·重构前后同种子完整局输出逐位一致**/只拆不重写/views 不转 .tscn/探针契约零破坏/信号纪律不变/执行顺序锁+渲染零漂移);**核心验收=逐位一致对拍**(RngService 单盐流在役);验证闸 V1-V9;代拍板白名单九项;反哺点两条(architecture+ADR 系列/code-conventions)。 | **✅ 已全批入库(2026-10-14 /goal+iter U0 开工单会话交付,worktree g-plan2-iter27-techdebt 分支 iter27-techdebt-20261014 七提交)**——波一 三屏场景化+run_scope 会话物化+双 autoload(7a0d57d)/波二 _settle 五段 Segment+英雄攻击拆步(26301ba)/波三 RunLedger 单类四账+刷新双账删副本+gp_final 删影子账(6fd7d78)/波四 ModalFlow+互斥域 tags+双 context(8b9e556)/波五a 类型闸 368 参数化+审计器+warning 配置(b605022)/波五b 卫生件 13 死代码+FeatsEngine 退役+Callable 换装+状态机 enum 化+ConfigDB 归根+resolver 解耦(T6 笔)/T7 文档同步(本笔)。**闸**:逐位一致对拍三变体五跑全同(V1 核心验收)+冒烟 1055→1081/0(+V2 屏壳 11+V3 两连局 11+V4 段类 16+V6 模态 19+V5 快照 3+V7 2 断言)+run_all 25→27 套+probe click+render golden 0.00%+static_scan S1/S2/S4/S9 全绿(历史 cd 静默空转顺修);MC 对拍=parity_anchor 三变体×40 局×RUNSUM+JSONL 双面(工具入库 tools/parity_anchor.py);实现报告=design/qa/iter27-实现报告-2026-10-14.md(代拍板追认单十五项+GDD 反哺点两条);残留人闸=实机目检五件(07 ㉖);审计报告本体按退役规则 git rm | T0-T7≈12-14d(实际单会话) | | 迭代 28+ | **M4 数值批(**原 22+;迭代 21 已编入天赋树实装批、迭代 22 已编入秘宝实装批(均 2026-10-11 用户令)、迭代 23 已编入锻造工坊实装批(2026-10-12 用户令)**;迭代 24 已由用户令编入敌人表内容修批、迭代 25 已由用户令编入 UI 全局主题批(2026-10-13)、迭代 26 已由用户令编入 UI 交互与观感批(2026-10-14)、**迭代 27 已由用户令编入代码架构技术债修复批(2026-10-14)**,本批顺延 28+**——**Alpha 收官位·未排期**〔2026-10-09 语义换代:M4=Beta 阶段、非 Alpha 收官位,M3 未由用户明令关闭前不排期维持〕——2026-10-05 用户令编入迭代 17 卡②;2026-10-06 用户令移出独立成批并定序「Alpha 阶段最后——内容补全先行、统一数值调整殿后」〔当日曾被编入迭代 18 为收官卡,2026-10-07 用户令撤回〕;**2026-10-07 用户令二次后移:后续新迭代(迭代 20 起)一律不先做 M4,除非用户明确要求启动 M4 批次**)**:四件数值欠账合一批——①削弱档位终值(weaken 现值 −65/duration 2/refresh 考证在册;叠层改续时语义已定案,差 duration 终值+当量锚复算)②调参批数值点评(06 条目现行登记五条——任务口径「八条」与条目五条不符,需求卡片 §2.2 已呈报差异以条目为准)③迭代 10 奖励重构联动欠账①②(钥石产出重标 boss_keystone 首杀 3/3/3·复杀 2/2/3 provisional + 通关率带重扫〔通关回血 20%+AP 件首轮 +1〕)④元进度定价剩余面(forge base 20/比 150 考证在册——06 条目文本「base 10」滞后于 2026-09-22 对齿批已呈报;Boss 两档终值)。读数义务随批=迭代 14 两件后置复算(T6 MC 四带复算+T7 技能池节奏重测,均挂 07);顺路修复 damage_probe A1 改前既有红灯(判式换代批出口报告 §四.7 在册)。**硬约束:一切 MC/verifier 跑批发起前须用户批准;调参联动禁独立调参网络(β 带 [1.2,2.5] 逐 u 检算)**。需求卡片=`design/research/需求卡片-M4数值批-2026-10-05.md`(**v1.2 2026-10-06 案C 增补+联审修订**:v1.1 三事实与第五推算件照旧;联审=P3 转正顺延至重扫读数后/检算工具 WEAKEN_PCT 入同步面/F11 两句死文本全刷/T3·T5 四带复算并账一次跑批双认账/R5 改前锚=本批新立案C 锚/估时段一勘误 4.25d;案C 增补内容照旧=§2.6 三事实〔行为换代/锚全作废/难度预判〕+T0 推算稿第五件=对拍锚按新序重推导+T6 四带复算按案C 新行为首采;T0-T8≈6.25d,天然切分点 T4 末;🔴 契约 5 条含跑批批准制;**✅ 裁点全裁(2026-10-06 用户令:P1甲=维持 2 场·magnitude [60,85] 带内当量锚复算、具体值随出口 provisional 追认/P2甲 不对称有意/P3甲 3-3-3·2-2-3 维持、转正顺延至重扫读数到账且带内(Q4 二裁)/P4甲 不立直购价);开工=U0**) | 迭代 18 全部内容卡(③④⑤⑦)出口+全部内容批行为变更落定+**用户发令(未排期)**——重锚窗口须收尽全部内容批行为变更 | T0-T8≈6.25d(超软盒预挂,段一 T0-T4≈4.25d〔v1.2 勘误,原 4.5 系加总错误〕/段二 T5-T8≈2.0d) | ## 执行纪律(2026-09-27 用户立规)