Files
gplan-2/design/engineering/architecture/architecture.md
T

291 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 跑批与真实局同一套规则,中断续玩恢复到同一停点。