# Dungeon Sweeper — 总体架构文档 ## 文档状态 - 版本:2.0(收窄为约束面形态——分层/模块归属/数据流/API 边界;实现现状归代码与代码头注,实现历史归 git commit log,闸清单与判定口径归 `game/tests/README.md`,见 doc-conventions「架构事实唯一源」行) - 引擎:Godot 4.7.2-stable(GDScript 单语言;2026-09-07 锁定) - 评审模式:solo(`review-mode.txt`——门禁按 solo 跳过) - 覆盖 GDD:现行全集(根索引=`design/gdd/02-系统清单.md`,文档树构成以根索引为准) - 事实源地位:本文是架构约束的唯一权威(分层/模块归属/数据流/API 边界);写权限=用户专属(agent 与技能只读) - 实现实证:`game/`(完整游戏开发版本;本文分层与所有权经实现验证)。测试层级=冒烟/单元/系统三级+UI 探针与渲染 golden,识别三元组权威=`design/engineering/test-conventions.md`「测试层三元组」节 --- ## 引擎知识缺口摘要 - 引擎事实现行事实源=`design/engineering/engine-reference/godot/`(VERSION / breaking-changes / deprecated-apis / current-best-practices 四件)——迁移核实结论、语义变化与编码注意一律查那里,本文不重复。 - 渲染器与窗口基座=gl_compatibility + 1280×800;UI/渲染域为引擎风险集中地,采纳引擎增量前对照 engine-reference 核实。 - 写码纪律不变:不确定的 API 一律查 `docs.godotengine.org/en/4.7/` 核实后再用。 --- ## 系统分层图 20 系统按 `design/gdd/02-系统清单.md` 依赖分层表映射到标准五层。实现已验证该分层(core/ 纯逻辑零 UI 依赖 headless 可跑、presentation/ 组合根模式)。 ``` ┌────────────────────────────────────────────────────────────┐ │ 呈现层 │ │ D1 HUD 与界面框架 (MVP) · B5 战斗表现 │ │ D4 音频 (Beta) · D5 教学引导 (Beta) │ ├────────────────────────────────────────────────────────────┤ │ 功能层 │ │ B1 战斗结算编排 (MVP) · C1 关卡节点地图 │ │ C2 冒险结构 · C3 难度曲线 · C4 元进度 (Alpha) │ ├────────────────────────────────────────────────────────────┤ │ 核心层 │ │ A1 扫雷棋盘生成 · A2 排雷玩法规则 · B2 敌人系统 │ │ B4 地图战斗资源 · A5 扫雷秘宝 │ ├────────────────────────────────────────────────────────────┤ │ 基础层 │ │ D2 数据配置与数值表 · A3 行动经济 · A4 地形变异 │ │ B3 增益/状态系统 · D3 存档序列化 (Alpha) · D6 设置/本地化│ ├────────────────────────────────────────────────────────────┤ │ 平台层 │ │ Godot 4.7.2 引擎 API 面(GDScript / Resource / │ │ SceneTree / Control / InputEvent)——无游戏系统 │ └────────────────────────────────────────────────────────────┘ ``` > 层名对照:基础层=Foundation、核心层=Core、功能层=Feature、呈现层=Presentation、平台层=Platform——本文一律用中文名;跨文档与代码目录仍见英文名(`game/core/`、`game/presentation/` 等目录名不改)。 | 系统 | 层 | 里程碑 | headless 约束 | 引擎风险域 | |---|---|---|---|---| | D2 数据配置 | 基础层 | MVP | ✅ 完整可跑 | LOW(Resource/.tres 基线稳定) | | A3 行动经济 | 基础层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) | | A4 地形变异 | 基础层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) | | B3 状态系统 | 基础层 | MVP | ✅ 完整可跑 | LOW(RefCounted 纯逻辑) | | A1 棋盘生成 | 核心层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) | | A2 排雷规则 | 核心层 | MVP | ✅ 完整可跑 | LOW(零表读取零 UI) | | B2 敌人系统 | 核心层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) | | B4 地图战斗资源 | 核心层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) | | B1 战斗结算编排 | 功能层 | MVP | ✅ 完整可跑(禁 await) | LOW(纯逻辑) | | C1 关卡节点地图 | 功能层 | MVP | ✅ | LOW | | C2 冒险结构 | 功能层 | MVP | ✅ | LOW | | C3 难度曲线 | 功能层 | MVP | ✅ | LOW | | C4 元进度 | 功能层 | Alpha | ✅ | LOW | | D1 HUD | 呈现层 | MVP | ❌(UI 层——场景级探针测试承载) | **MEDIUM**(Control/Input/文本域) | | B5 战斗表现 | 呈现层 | 切片后立项(未实装,前置战斗表现美术测试) | ❌ | **MEDIUM**(Animation/Tween/渲染) | | D4 音频 / D5 教学 | 呈现层 | Beta(与游戏内容无关者一律排 Beta) | 部分 | LOW-MEDIUM | | D3 存档序列化 | 基础层 | Alpha | ✅ | LOW | | D6 设置/本地化 | 基础层 | 完整愿景 | ✅ | LOW | **依赖分层与唯一回边**:A2 只外发三事件(cell_revealed / flag_judged / round_end),B4/B1 订阅后回调——A2 不依赖任何下游(TD-SYSTEM-BOUNDARY 裁决,全项目唯一允许回边)。 --- ## 模块归属 > 命名遵循 `design/engineering/code-conventions.md`:领域命名、禁 ID 前缀。本表只写约束:模块的职责语句、对外封闭面概述与消费依赖——各件的完整公开方法面、成员与接线明细归代码文件头注(`##` 文档注释)与代码自身承载;文件路径是导航索引。 ### 基础层 | 模块 | 文件 | 独占职责 | 对外封闭面 | 消费依赖 | |---|---|---|---|---| | **ConfigDB**(autoload,D2) | `game/core/config_db.gd` | 全部玩法数值表唯一加载与持有单点(`.tres` 经校验后入容器);行族访问器归各 schema 表类(表管自己的行查询),本类只留加载/校验/override/诊断门面,表持有按域收进域容器 Resource | 类型化域容器属性+load_tables / reload / set_reload_gate / diagnostics 族 | 无(基础层顶点) | | **RngService**(autoload,D2) | `game/core/rng_service.gd` | master_seed 与各封闭流 scope 计数器单点(封闭流词表现值以 `RngServiceCore.PURPOSE_TAGS` 为准,增流走 P1 评审) | set_master_seed(唯一调用方 C2)/ open_stream 有状态门面 / derive_at 纯函数 / 只读计数器 | 无 | | **save_system**(SaveManager,autoload,D3) | `game/core/save_manager.gd` | 存档格式与版本比较、序列化接线单点;槽位文件域归 SlotStore、元进度数据归 MetaStateStore | 写盘/读回/槽位管理/断点快照公开面(契约规范源:`../../gdd/systems/表现与支撑/存档序列化.md`——契约锚:SAVE_FORMAT_VERSION + master_seed + 各流 scope 计数器快照) | ConfigDB、RngService 只读计数器 | | **SlotStore**(D3) | `game/core/slot_store.gd` | 存档三槽位文件域唯一所有者:槽位挂载与模板、激活与建删、摘要窥视 | 槽位读写原语族+摘要投影 | save_manager(注入路径与模板配置) | | **MetaStateStore**(D3) | `game/core/meta_state_store.gd` | 存档内元进度数据的读写唯一层(数据读写原语,不含业务规则) | 写口原语族+只读投影(消费方经 `save.meta_state.` 直连) | save_manager(序列化接线) | | **MetaProgression**(autoload,C4) | `game/core/meta_progression.gd` | 元进度账本服务:钥石收束入账、营地消费定价与解锁判定的纯被动服务,零信号零外发;业务层(数据读写全经 MetaStateStore) | 写口族(天赋树/锻造/同调)+解锁判定与读投影;四个 Autoload 中唯一有跨 Autoload 依赖(ConfigDB/SaveManager) | ConfigDB、SaveManager | | **action_economy**(A3) | `game/core/action_economy.gd` | AP/RAP 账本、轮次相位机 {Idle, Active, Waiting}、结束仲裁 | 意图与通知布尔面(request_dig / request_stop / request_refresh / notify_* 族)+预授权通道;ap/rap/phase 只读 | ConfigDB、EffectPort、B1 回发 | | **variation**(A4) | `game/core/variation*.gd` | VariationTable schema 与 F1 候选池门控谓词 | variant_pool(门控三维)/ global_variant_row 访问器 | ConfigDB | | **status_container**(B3) | `game/core/status_container.gd` | 状态实例容器(战斗域生命周期、跨轮跨图延续) | 五 API:notify_battle_start / apply_effect / query_active / decay_round / notify_battle_end | ConfigDB | | **effect_engine** | `game/core/effect_engine.gd` | 零运行时可变状态的全同步解释器(架构原则 8):事件由宿主在明确代码点位显式 dispatch 进件,命中序=装载声明序,不是事件总线/事件循环 | rebuild / dispatch / validate_trigger_row +静态计数口;事件/事实/效果类型/算子四词表封闭+树规则合成订阅六键 | ConfigDB 行集、EffectContext 引用束(四桶由宿主持有) | | **settings/localization**(D6,愿景) | — | 设置项、翻译数据 | (契约规范源:`../../gdd/systems/表现与支撑/设置与本地化.md`) | ConfigDB | ### 核心层 | 模块 | 文件 | 独占职责 | 对外封闭面 | 消费依赖 | |---|---|---|---|---| | **board_generator**(A1) | `game/core/board_generator.gd` | 生成算法(五阶段管线)、合法性谓词(clearable / R₀≠S / min_safe)、可推理率度量;生成姿态=MVP 同步生成+加载幕(WorkerThread 留扩展) | `generate(L, D, rng) -> Board`;度量独立入口 | ConfigDB;board_gen 封闭流(一次 open_stream=一张图) | | **minesweeper_rules**(A2) | `game/core/minesweeper_rules.gd` | 棋盘状态(已开集/封印集/小人位)、批次仲裁、install 四不变式 | `install_board` / `settle_batch`(唯一提交入口)/ 查询只读面 / preview_reveal 只读预览口 / retire_board;三事件封闭(cell_revealed / flag_judged / round_end,payload 字段封闭) | A1 Board 契约(class_name 数据契约);A3 try_spend | | **unit_container**(B2) | `game/core/unit_container.gd` | hero/enemy 单位实例(hp/atk/实例池) | spawn / query_alive / 动作与伤害面 | ConfigDB | | **reward_economy**(B4) | `game/core/reward_economy.gd` | 格面资源身份映射(install 期预分配、换图映射消亡)、极性路由字典面、受理账面(装配期摇号归掷格件 RewardRoller) | 封闭 API:roll_board / query_identity / grant / grant_clear / retire_mapping / polarity_row / phase | ConfigDB;BattleFxHost 偏运合计查询(注入 Callable) | | **helper_items**(A5) | `game/core/helper_items*.gd` | HelperItemTable、探测显影语义 | (契约规范源:`../../gdd/systems/跨局循环/秘宝.md`——桥接位:A2 查询接口+A4 counter_item_ids) | A2 / A4 / B4 | | **CurioDeck**(A5) | `game/core/curio_deck.gd` | 三选一/商店共享令牌池、抽签、cap 库存与专长凭证判定唯一所有者(停点编排与兑付推进留 CurioStop) | draw_candidates 抽签 / consume 族池消耗 / cap_ok / 凭证判定 / stock 只读投影 | ConfigDB;注入 RngServiceCore(reward_pick 封闭流) | | **ShopSession** | `game/core/shop_session.gd` | 关卡商店双模式(legacy 固定三件/pool 共享令牌池)的货架、定价、购买与刷新唯一宿主;账本/钱包经宿主 LevelRunner 注入共享引用 | 货架与定价只读面+写入口;调用方经宿主公开成员 `shop` 直连 | ConfigDB;注入 RngServiceCore;pool 模式经注入 Callable 转 CurioDeck | | **HeroStats** | `game/core/hero_stats.gd` | 冒险生命周期主角数值域唯一所有者:生命账与特性持有集、生效公式单点 | 构造注入;单写原语族+只读投影 | ConfigDB;注入 GainJournal(只读);AdventureRunner 创建 | | **GainJournal** | `game/core/gain_journal.gd` | 统一冒险加算区与获得记录唯一所有者(三层结构:记录→明细→四轴汇总) | gain_bonus 单写原语;记录与汇总只读投影 | AdventureRunner 创建,注入各消费方 | | **GpWallet** | `game/core/gp_wallet.gd` | 冒险局内货币唯一所有者(不入存档不入摘要) | gain / spend / clear 单写原语 | AdventureRunner 创建,宿主与商店注入读写 | | **RefreshBank** | `game/core/refresh_bank.gd` | 三选一刷新三源账唯一所有者+GP 付费刷报价公式(零停点态依赖,编排归 CurioStop) | 支出与余量原语+报价公式;断点自交片段(refresh_stock / attune_refresh_y) | ConfigDB;注入 CurioStop 支出路径 | | **AttuneLoadout** | `game/core/attune_loadout.gd` | 装备中的秘宝件对象集与同调充能唯一所有者(种行/补行改写时同步) | 构造注入;充能查询/消耗/按周期重置原语 | ConfigDB;TreasureItems 投放面;注入使用路径 | | **CurioStop** | `game/core/curio_stop.gd` | 三选一停点编排唯一所有者(架构原则 9 本域承载):停点三态、刷新编排、兑付方法族、零自动推进语义 | 开停点/刷新/兑付/放行谓词/交断点片段五组入口+信号面(curio_choice_presented / curio_resolved) | ConfigDB;注入 CurioDeck、HeroStats、GainJournal、RefreshBank | | **breakpoint_store** | `game/core/breakpoint_store.gd` | 存档断点组装与回放唯一承载:断点快照拼盘、回放回填、读侧策略分发 | `BREAKPOINT_FIELDS` 键登记单源(键名/策略标签/键序冻结) | 注入引用束 | ### 功能层 | 模块 | 文件 | 独占职责 | 对外封闭面 | 消费依赖 | |---|---|---|---|---| | **battle_orchestrator**(B1,域文件集=编排器+`settlement/` 五段段类+其拆件:BattleFxHost / RewardGrant / DamageController / BattleRefreshFlow / BoardInstaller / RoundCycle / BattleFacts / EffectPort / RewardRoller) | `game/core/battle_orchestrator.gd` 等 | 战斗域编排唯一宿主:战斗状态机与生命周期、A2 三事件订阅、结算五段驱动(授予/增益减益/战斗/衰减/判定,段序冻结于 SettlementTable.segment_order)、行发射基建、呈现投影;**战斗域全部跨系统调用权集中本域**(静态扫描域 S7 执法,文件集清单单点=`game/tests/static_scan.sh`) | `begin_battle(encounter)`;窗口/刷新意图提交面;`level_clear()`;步骤流(输出契约);[明细行协议](../../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋) | B2/B3/B4 全调用面;A2 订阅;A1 generate;A3 代理+回发;D2 SettlementTable | | **level_runner**(C1) | `game/core/level_runner.gd` | 关卡域生命周期:节点链推进、共享 B4、HP 携带、关卡末 level_clear;依赖构造注入一次给齐 | 节点推进接口;LevelTable(ConfigDB 跨表校验) | B1(直构接线战斗)、ConfigDB | | **run_structure**(C2) | — | 冒险/关卡序列、失败重开、**master_seed 唯一接线方** | (契约规范源:`../../gdd/systems/冒险循环/冒险结构.md`) | RngService.set_master_seed、C3/D2 | | **difficulty**(C3) | — | DifficultyTable 回填、全部曲线数值调参所有权、战斗期望表 | 调参输出(经 D2 解析传递,禁反向调用生成) | ConfigDB、B1 对账输出、A1 报告 | | **meta_progression**(C4,业务层) | `game/core/meta_progression.gd` | MetaTable、钥石账本、天赋树域、同调位槽位判定(业务规则与写口编排;数据原语归 MetaStateStore) | tree_* / forge_* / attune_* / settle_adventure / hero_override 族 | MetaStateStore、ConfigDB | ### 呈现层 | 模块 | 文件 | 独占职责 | 对外封闭面 | 消费依赖 | |---|---|---|---|---| | **D1 HUD** | `game/presentation/main.gd`(组合根)+三屏壳+`views/` 视图单元族+`ui_registry.gd` | 全部 Control/CanvasLayer;输入→意图翻译(**唯一提交路径=settle_batch,不直调 A3**);组合根=信号接线与核心写调用唯一出口;views/ 各域呈现拆件的职责与接口归各件头注 | 组合根公开服务面与注册面句柄寻址合同(`main.ui.<域容器>.<句柄>`)归 main.gd / ui_registry.gd 头注承载 | A2 compute_move_path;A3 只读;B1 步骤流;ConfigDB display_name;**MEDIUM**:Control/CanvasLayer/draw_*/InputEvent/RichTextLabel | | **组合根拆件** | `game/presentation/*_controller.gd` / `camp_shop_presenter.gd` | 代组合根执行核心写调用(冒险节点推进/营地购买与刷新/道具使用) | 各拆件写入口(仅组合根装配面可达;视图零直连) | 构造注入的核心写引用 | | **HudPresenter** | `game/presentation/hud_presenter.gd` | 战斗屏数据投影+文案拼装静态族(核心状态→呈现文案的全部翻译集中于此) | refresh 全量重查装配+只读静态投影族 | 只走 `settlement.presentation_snapshot()` 成文快照面(禁链式穿透) | | **board_resolvers** | `game/presentation/board_resolvers.gd` | 棋盘每格渲染数据只读解析回调四函数(与无态静态投影族职责互斥) | 构造注入产出绑定 Callable 供组合根接线 | ConfigDB / 结算活态取值 Callable | | **battle_presentation**(B5,垂直切片后) | — | 结算动画 / 伤害数字 / 战斗舞台 | (契约规范源:`../../gdd/systems/战斗循环/战斗表现.md`——前置「战斗表现美术测试」锁双舞台陷阱) | B1 步骤流、D1;**MEDIUM**:AnimationPlayer/Tween | | **audio**(D4,Alpha) | — | 音频挂点 | 挂点=B1 明细行类型流(负反馈聚合——单结算至多一次) | B1 流水 | | **tutorial**(D5,Alpha) | — | 教学条目 | 口径来源=A2/A3/B3/B1 各教学条目 | 各上游 | **依赖单向性**:上层可消费下层,禁止反向(C3 禁调用 A1 生成、D1 不直调 A3、A2/B3/B4 零相互直调)。全项目唯一回边=A2 事件订阅面。 --- ## 呈现层装配与会话生命周期 ### 三屏壳与组合根 - 呈现场景拆为三屏壳:MenuScreen(开始菜单屏)/CampScreen(营地屏)/AdventureScreen(冒险屏——内含关卡地图与战斗两容器)。 - 屏成员寻址经界面注册面(ui_registry):探针与消费方走 `main.ui.<域容器>.<句柄>` 合同寻址;句柄清单与合同细节归 ui_registry 头注承载。 - 局态一律住 RunScope 节点,禁在屏/视图侧持有局态引用。 ### 视觉层装配合同 - **树序=登记序=z 序**,同源派生;层序唯一事实源=`main.gd` 的 `LAYER_CONTRACT` 常量表(kind 五类:shell/modal/state/node/float)。 - 全仓唯一落位口=`_apply_layer_contract()`(装配尾统一排序+登记);散落 `move_child` 由冒烟源码断言判红;**改层序只改合同表**。 - 具体层清单不复制进本文(双写失同步禁令)——查现行层项直接读 `main.gd`。 ### 会话生命周期(RunScope) - `run_scope.gd`=纯状态承载 Node:零信号连接、零视图构建、零核心写调用(连接只在组合根,组合根经 `run_scope.X` 读写)。 - 会话更替机制:`_begin_session()` 新建会话节点挂树、旧会话节点**整节点 `queue_free()`**——无独立收束函数,跨局字段清理靠节点释放机制。 - **存活性矩阵**(九阶段 × 五引用面;执行判据=系统测试 V3a–V3i): | 阶段 | run_scope | adventure | runner | settlement | |---|---|---|---|---| | ① 闲时 | 在树(闲时会话) | null | null | null | | ② 开局停点 | 在树 | alive | alive | null | | ③ 战斗中 | 在树 | alive | alive | alive | | ④ 结算后(胜利停点)/⑤ 选路页 | 在树 | alive | alive | both | | ⑥ 终局面 | 在树 | alive | alive | null | | ⑦ 回菜单收束 | 新闲时会话即刻在树(旧会话已释放——无真空窗) | null | null | null | | ⑧ 各模态开合后 | 闲时会话零扰动 | null | null | null | | ⑨ 两连局+续玩断点 | 换全新会话在树 | alive | both | both | - 三态记号:**alive**=应有实例、**null**=必须缺位(实测非空判红)、**both**=按分支可空可活。对象有值但已失效一律判「悬挂」红。 ### 模态编排(ModalFlow/ModalStack) - `modal_flow.gd`=模态编排纯逻辑类:开合编排(遮挡记账单口+演播后排窗挂起)/棋盘输入锁谓词/角标置灰谓词三职责;可直接 `new()` 单测。 - 互斥排除语义随层登记(`register(node, tags)`);其余层零标签。 --- ## 测试与质量域 > 定位:本节只写测试域的架构事实。测试工具手册与各闸判定口径的事实源=`game/tests/README.md`;闸清单与改动域划分的唯一出处=`tools/run_all_gates.sh`(脚本即清单);测试怎么写、怎么算合格=`design/engineering/test-conventions.md`;静态扫描域 S 系规则的规则全集与动机=`game/tests/README.md`「Static Scan」节。全量验证=合回后远端 CI;开发期义务=改完即测(process-conventions §4)。 ### 测试域依赖约束 测试域禁止反向依赖呈现层具体实现。五闸及其调用的探针场景、审计脚本只允许依赖两类边界: 1. **autoload 边界**——ConfigDB / RngService 的公开面(--script 模式不加载 autoload 且不注册全局标识符,测试文件自带显式实例化夹具); 2. **场景契约**——探针场景的信号外发面与视图单元接口(信号外发、setter 只读注入),禁直引呈现层实现内部。 测试域是所有层的消费方,进入方式限于上述两类边界;除此之外的依赖方向一律与「模块归属」节依赖单向性同规。 --- ## 数据流 ### 1. 帧更新路径(2D 回合制:无逐帧玩法逻辑) ``` Input(鼠标)→ D1 输入翻译 → 意图对象 → A2.settle_batch(唯一提交入口) → A3.try_spend(原子扣减)→ A2 棋盘效果(挖/连锁/封印/移动)→ 三事件外发 → [棋盘渲染:D1 BoardView 按事件增量重绘;无事件帧 = 静态 + 动效] → 轮次结束 → A3 Waiting → B1 结算编排(段序七段)→ 明细步骤流 → D1 结算屏逐行呈现 ``` - 回合制下每帧只做:输入采样、呈现刷新、动效推进——**玩法状态只在意图事务内变更**(单意图帧输入收集:stop 点击终结当帧)。 - 无跨线程玩法数据流(Godot 主线程假设);A1 生成若引入 WorkerThread,join 必须先于任何快照切出(既定约束)。 ### 2. 事件/信号路径(系统间通信,无紧耦合) ``` A2 ──cell_revealed / flag_judged / round_end(棋盘路径)──▶ B1(轮 facts 缓冲) A3 ──round_end(stopped 直发,payload 同构)──────────────▶ B1 B1 ──notify_settlement_done(next_round_mode) / notify_combat_end──▶ A3 B1 ──apply_effect / apply_damage / grant / 伤害单据 submit…──▶ B3/B2/B4(唯一调用方) B1 ──install_board / retire_board──▶ A2 B1 ──generate(L,D)──▶ A1 D1 ◀──呈现数据源:A2 事件流 / B1 步骤流 / A3 ap-rap / ConfigDB display_name ``` - **事件载体(已定)**:组合根直连信号接线 + B1 Waiting 相位触发(不依赖事件到达序);autoload 全局事件总线已否决(2026-09-12,沿革见 git 历史)——单线程回合制沿用组合根直连(KISS)。选型理由=可回放(架构原则 3):同步直连让调用顺序显式可见,「同种子逐位相同」才可断言;事件驱动形态服务的是并发/IO 场景对外部不确定时序的反应性,与本作「顺序确定」的需求相反。 - 事件规范序:A2 发射序=触发格 BFS、8 邻行主序;事件序契约 `flag_judged` < `round_end` 全路径统一。 - **呈现层订阅禁令(切片缺陷教训固化)**:呈现层永不直接订阅排雷玩法/行动经济的事件流,只订战斗结算步骤流与 `level_runner`——核心事件形态变化不得直接冲击界面。 ### 3. 存档/读档路径 - 存档系统已实装:格式、三槽位、断点域与迁移链的现行契约以 `design/gdd/systems/表现与支撑/存档序列化.md`(owner)为准。 - RNG 快照契约(约束,现行成立):断点续玩回填=master_seed + 各流 scope 计数器;快照切出须发生在无正在执行中的 open_stream 实例时(scope 事件边界外——切点=board install 边界);SAVE_FORMAT_VERSION 高版本拒读。 ### 4. 打包与字体(CJK 随包发行) CJK 字体随包发行并设为 theme 默认字体——运行时默认字体无中文字形。此项是 HUD 框架正式化的硬性前置,同时进导出 checklist 核对项。 ### 5. 初始化顺序 ``` 进程启动 → autoload 实例化:ConfigDB → RngService → SaveManager → MetaProgression(顺序固定;核心类为普通类、由组合根按局实例化) → ConfigDB._ready():load_tables 加载 + 校验(致命通道;失败 = 构建中止 + 错误清单) → 主场景(D1 组合根)装配视图单元 + 信号接线 → C2 begin_run:RngService.set_master_seed()(唯一接线方;两流计数器全零前提) → B1.begin_battle(encounter):set_reload_gate(true) → B3 开域 → B2 spawn → A2 install_board(A1.generate) + B4 roll_board(恰一次)→ A3 notify_round_start → 挖掘循环 …… → battle_over 清理序列 → set_reload_gate(false) → 关卡末:通关结算(钥石/GP/专长 level_clear 渠道——现行语义归 冒险结构 GDD) ``` **启动后置条件**(执行判据事实源在 `game/tests/system_test.gd`): 1. ConfigDB 就绪且表已载(`is_ready` + hero 表在座)。 2. 系统设置已落引擎态——Master 音量 mute/volume_db=settings_store 现值;减动效开关=`UiKit.motion_scale`。 3. 启动态菜单可见(boot 后自树顶向下第一个可见内容层=开始层)。 4. 续玩钮可见态恒等于断点在档态(双向)。 5. 闲时会话已在树(`run_scope` 永非 null 契约的启动侧证明)。 6. 调试浮窗 boot 不自动开(菜单优先)+调试入口全局浮层角标 boot 可见。 --- ## API 边界 > 完整 API 面(含封闭枚举负空间、布尔受理口径、输入×相位矩阵)以各 GDD AC 前言为**规范源**——本节列边界契约与不变式,实现须与 GDD 逐字一致;冲突时以 GDD 为准并走接口漂移修订。 | 边界 | 契约要点 | 调用方必须遵守的不变式 | 模块保证 | |---|---|---|---| | D1 → A2 | 意图经 `settle_batch(intents)` 提交;不直调 A3 | 单意图帧收集;stop 经批次委托 | 语义序仲裁 + voided 零副作用 | | A2 → A3 | 挖/插/拔=先棋盘面判定后 `try_spend`;A2 不可自行扣减 | 相位判定两侧一致(同一矩阵) | 原子事务(余量+扣减+仲裁一体) | | A2 → 下游 | 只外发三事件,payload 字段封闭(digit 恒真透传 / reward_eligible / variant_ids) | 增删事件=接口漂移 | 发射面封闭 + 规范序 | | B1 → B2/B3/B4 | 全调用面集中 B1;调用序钉死(按摇定技能行兑现→apply_damage→commit_action;先兑付后衰减;段内伤害序=主角先手→逐敌还手→地图账殿后) | 禁第二调用方(静态扫描域 S7:API 符号命中文件 ⊆ B1 文件集) | 恰一次编排纪律(roll_board/decay/清理全序) | | B1 ↔ A3 | 编排期代理 `request_refresh`(玩家意图,禁 RAP 满自动刷);回发 next_round_mode/battle_over | stopped/ap_exhausted 未刷新回发 new_board=违约 fail-fast | 刷新受理契约(同 Waiting 多次合法) | | 各系统 → D2 | ConfigDB 类型化属性单点读取 | 禁绕过 ConfigDB load 表、禁缓存副本、禁玩法数值字面量、禁 randomize()/自建 RNG | 校验双通道 + fail-fast 致命 | | A1 → A2 | Board 产物 class_name 数据契约 | install 四不变式校验(violation=拒绝 + 五类诊断枚举) | 生成期合法性闸(R₀≠S / clearable / min_safe) | | A1 ↔ B1 | attempt 边界非 scope 事件;actuals p10 为对账输入(非 target 参数) | 编排不把重试当取号 | 生成期指标集输出(实测可推理率 / 实际雷数与安全格数 / 重试次数 / 降级与非法产物标记) | | B4 → B3 | buff 类经 B1 调 apply_effect(B4 零跨系统读、零直接接口) | 满层拒绝=零兑付 + 明细行(无折算) | 授予时数值快照(抗热重载) | | B1 → D1 | [步骤流行协议](../../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)(基础行型+扩展行两层封闭 + 公共字段 type/source_class/origin_cell/round_index/display_name/value/highlight? + 特有字段 target/effect_id/pool_value) | 无来源行=断言失败;呈现侧禁按行序重演 B2 状态机 | 可回放步骤流(同种子逐行相同) | **引擎类型核验注记**:接口使用的引擎类型=Resource / .tres(D2 全表)、RefCounted(容器类)、Signal/Callable(事件载体)、Control/CanvasLayer/Drawing(D1)、SceneTree headless 协议(测试)——均属 4.x 基线稳定面;4.7 Control 域增量已在 breaking-changes.md 登记,D1 采纳前对照核实。 --- ## 架构原则 1. **核心层纯逻辑、headless 完整可跑**——核心层/功能层玩法逻辑零 Control/场景引用、零 UI 依赖;`--headless --script` 可跑完整战斗。输入是意图对象、输出是事件流/步骤流,D1 与输入设备皆可替换。 2. **单一事实源与零字面量**——玩法数值只存在于 `data/tables/*.tres`,经 ConfigDB 类型化单点读取;代码零玩法数值字面量;参数禁止跨系统复制(D2 Rule 1/2)。 3. **封闭 RNG 与可回放**——玩法代码零随机;一切随机经 RngService 具名封闭流(增流走 P1 评审);同种子 + 同输入序列 ⇒ 事件流/步骤流逐位相同(结算禁 await,B1 承重约束)。 4. **编排唯一性与事件面封闭**——战斗域跨系统调用权集中 B1(静态扫描承载);A2 三事件面封闭即负空间;每格受理、每雷转化、每次推进、每次衰减各恰一次。 5. **fail-fast + 零特判**——数据错误一律致命中止(错误数值上线比崩溃更伤);API 非法=拒绝 + 诊断不静默;变异零运行时分支(行为差异全部由生成端约束保证)。 6. **依赖单向 + 组合根模式**——层间单向依赖、唯一回边=A2 事件订阅面;核心写调用唯一发生在组合根;禁上帝对象。 7. **呈现层契约成文**——面向呈现的信号载荷必须有成文 schema;模态覆盖层必须有"打开时遮挡物隐藏"场景级断言;场景级 UI 探针测试为标配(切片缺陷教训沉淀)。 8. **系统无状态**——效果引擎这类系统零运行时可变状态:数据由宿主对象持有并随宿主生灭,状态按参数传入系统,系统只做判定。系统实例不持计数器——计数器住桶(round/battle/level/adventure 四桶,EffectContext 引用束),桶住宿主。 9. **停点权威在核心层**——玩家决策停点(选路、三选一、商店、恢复这类)的推进守卫住核心层状态机:停点未兑付,推进口一律拒绝(零自动推进契约);呈现层模态只做开合与输入锁——阻塞是两层结构,核心守卫为权威、模态为体验;停点态是核心状态、随断点序列化,headless 跑批与真实局同一套规则,中断续玩恢复到同一停点。