# D2 数据配置与数值表(Data Config & Balance Tables) > **状态**:Approved · **版本**:v2 · 2026-09-14 > **承载支柱**:P1、P2(基础设施层,间接支撑全部四支柱) · **层级**:Layer 1 Foundation · Milestone MVP · 设计顺序第 1 位 > **一句话**:D2 把全部影响手感与平衡的数字收进可配置、可校验的数值表,经 ConfigDB 单点发放、RngService 统一种子化——战斗可复现、平衡可热调、headless 可测的数据底座。 > **推演件**:`design/gdd/analysis/d2-data-config.md`(本文件只收结论,理由在那里) ## 设计目的 D2 是全部玩法数值的单一事实源:data/tables 的 .tres + autoload ConfigDB + autoload RngService——schema 先行、数值回填。没有 D2,参数散落硬编码:调一次难度改五处代码、战斗无法用固定种子复现、headless 自动化测试无从谈起。它是玩家永远不该注意到的地基:不制造情绪,但失职会直接摧毁 P1/P2 的信任底线。 1. 设计师"改数字不改代码"——全部影响手感的数字(AP、旗、折算比例、奖励值)集中一处可调,热重载即改即验,"这轮玩起来不对劲"能快速修正且不引入新毛刺。 2. 开发与测试"同决策必同结果"——RNG 统一种子化,相同决策序列必然相同战斗结果,回放 / 审计 / 自动化测试可复现。 3. 难度"越下越险由结构保证"——数值表天生带(关卡序号, 节点深度)两维,难度曲线可审计、可回溯,而非散落各处的魔法数字。 4. CI 与无窗口环境照样跑完整战斗——数据层零界面依赖。 ## 系统范围 负责: - 全部表 schema 类(data/schemas)与数据文件(data/tables) - autoload ConfigDB / RngService · SCHEMA_VERSION 常量 - 加载 / 校验 / 热重载状态机 · 校验双通道与诊断收集器 - debug override 链 · 难度行解析 resolve - 十张已定稿表 + 两张占位表的结构与键(数值由 owner 系统按设计顺序回填) 不负责: - 平衡数值定值与玩法公式——伤害、掉落权重、成长曲线(owner 系统 GDD 与 registry:B1 / B4 / C3 等) - 玩法随机预置——任何系统若需玩法随机(如结算浮动),自带方案走增流通道并通过 P1 评审(规则 5) - 存档(D3)· 任何 UI 表现面 - 精英 / Boss 修饰语义(C1 / C3——DifficultyTable 只预留 elite_ / boss_ 命名空间)· MetaTable 键结构(C4) 硬约束:玩法数值零代码字面量 · 零 UI 依赖 · fail-fast 无软回退 · RNG 流集合封闭 · 表路径固定(debug 注入根除外)· release 无 override 源。 ## 核心循环 ### 数据加载与校验链路 ``` 进程启动(Unloaded) │ ConfigDB._ready() / load_tables(root, collect_only) ▼ Loading ──── 按表清单逐一 load() ── 文件缺失 → Failed ▼ Validating ── 单个资源加载成功 → schema validate(),错误入诊断收集器 ▼ ┌ 校验双通道 ──────────────────────────────────────────┐ │ 游戏通道 collect_only=false(默认): │ │ 错误 → push_error 清单 → 致命错误路径 │ │ editor / CI / headless / release 一律致命 │ │ 测试通道 collect_only=true(仅 debug/测试构建): │ │ 收集不升级 → 冒烟脚本逐夹具断言类别 → 一次性退出 │ └──────────────────────────────────────────────────────┘ ▼ Ready ────── 全部通过 → 类型化属性可读 ``` ### 热重载链路(仅 debug) ``` 编辑器热重载 / 调试键 → Reloading │ ├─ 战斗中(set_reload_gate 已置位)→ 拒绝返回 false,停留 Ready │ (战斗结束后手动再触发,不自动重放) ▼ 重走 Loading → Validating │ ├─ 校验失败 → 保留旧表停留 Ready + debug 错误清单(唯一软着陆,不杀调试会话) ▼ 校验通过 → Ready:新值经类型化属性可读 + config_reloaded 恰一次(仅 debug,D1 调试用) ※ RNG 状态线独立:Reloading / Failed 不触碰 RngService——计数器与 master_seed 不变(AC-4) ``` ### RNG 取数链路 ``` 进程启动 → 流待命(未播种取数 = 致命) ▼ 冒险开始 → C2 set_master_seed()(仅全流计数器全零时受理) ▼ scope 事件:board_gen = 每次生成新扫雷图(A1 换图时) reward_roll = 每次 board install 批(格奖励批 + 清图奖励批) encounter = 每场战斗实例化(B1 begin_battle——unit_count 区间骰) │ open_stream(stream):计数器 +1 → R2 派生 → 返回已播种实例(同一 scope 事件内取数共用) ▼ 冒险结束 → 流保留供复盘;快照 = master_seed + 各流 scope 计数器 (须在无在飞 open_stream 实例时切出——scope 事件边界之外) ``` ### 出口分发 | 出口 | 触发 | 动作 | 约束 | |---|---|---|---| | 启动致命中止 | 启动加载任一致命错误 | 输出错误清单并中止构建;headless 独立进程非 0 退出 | 全构建一致,无软回退 | | 热重载软着陆 | debug 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单 | 唯一例外 | | 运行期致命 | 请求越域 / 未播种流取数 | 游戏内致命错误路径;headless quit 非 0 | 不分构建 | | 热重载成功 | debug 且门未置位且校验通过 | 新值可读 + config_reloaded 恰一次 | 战斗中被门拒绝 | | 存档拒读 | SCHEMA_VERSION 高于当前 | 拒绝读取 | 比较归 D3,常量归 D2 | ## 表结构与回填(schema 先行) 规则: 1. 规则 3(结构先行、数值回填):全部数值表按"结构 + 数据"两件套交付——结构 = data/schemas/ 下一个自定义 Resource 类(每字段带类型、范围、默认值和注释),数据 = data/tables/ 下数据文件;D2 先交付全部 schema 与占位表(含键结构),具体数值由 owner 系统 GDD 回填,先有格子、后填数字。 举证:无计算。十张已回填表 schema 目录见本节,逐表字段行见调参表节。 2. 规则 4(进度坐标查行):跟着冒险进度成长的数值表(难度、生成参数、敌人配置)每行带(关卡序号, 节点深度)两维坐标——系统按坐标取数,无精确命中时取不超过请求坐标的最近已有行;敌人等实体键在命中行内按名字精确匹配,那一行没有这个名字就是悬空引用、直接致命中止,不做身份近似。 举证:解析规则(floor 取行、warning 四元组去重、越域前置守卫)见「难度行解析」节;悬空引用 = 加载期致命(规则 6)。 | 表的形状 | 行键怎么定 | |---|---| | 进度成长表 | 行键 =(关卡序号 level_index, 节点深度 node_depth) | | 实体复合键表 | 进度两键之后再复合实体主键列(如 EnemyTable 的 enemy_id;合成规则写入 schema 注释) | | 单行全局常量表 | 无成长语义的表允许只放一行 | | 元进度表 | 存档/天赋/代币归 MetaTable,键结构归 C4 定 | | 两维坐标之外的数值轴 | 归宿 | |---|---| | 战斗内轴(胜场/轮次等) | 由 owner 公式承载或作为表列;归属登记进 schema 注释(C3 定稿时声明) | | 难度第三维"精英/Boss 修饰" | game-concept 定稿:难度 = f(关卡序号, 节点深度, 精英/Boss 修饰);DifficultyTable 预留 elite_ / boss_ 前缀扩展字段命名空间,语义由 C3 定稿——本 GDD 不定义任何修饰数值、不预写修饰枚举 | | 两键定不了精英性的节点形态 | 如分支地图同深度并存精英与普通节点——允许改键,按接口漂移修订规则回写(与 MetaTable 同等豁免待遇) | 设计意图:进度坐标可以就近近似,"是哪种敌人"不能近似——身份近似会静默改变遭遇构成、损害 P2 归因。 ### 十张已回填表 schema 目录(逐表字段行见调参表节) | 表资源 | 键结构 | Rule 4 豁免 | 跨字段 / 结构校验(加载期致命) | 悬空引用政策 | Owner | |---|---|---|---|---|---| | ActionEconomyTable | 单行全局 | 单行全局定义表(先例) | 任一动作成本 ≤ ap_per_round(schema validate() 跨字段类别承载,条件性夹具登记) | — | A3 ✓ | | VariationTable | 按 variant_id(含 unlock_level 列) | 定义表非进度成长表——进度语义承载于 unlock_level 列(schema 注释) | info_mask=hide_numbers ⇒ cell_constraint=safe_nonzero(夹具 variation_mask_constraint) | counter_item_ids MVP 不校验——A5 首个道具 id 回填时补校验 + 条件性夹具 | A4 ✓ | | StatusEffectTable | 按 effect_id | 定义表非进度成长表(schema 注释承载) | magnitude:percent ∈ [-100,100] ∧ ≠0 / flat ≥ 1 跨字段校验 | effect_id 校验已激活(B4 RewardTable 首引 defense_up——夹具 b4_dangling_effect) | B3 ✓ | | BoardGenTable | (level_index, node_depth)——首个进度键表(depth-0 锚行内建;第三轴落点 = Boss:Dmax 行约定 + elite 命名空间预留 + A1 OQ6 重审;改键退路 = 规则 4 接口漂移通道) | 无(进度成长表) | fog_domain_ok(F2)+ min_safe_feasible(F2b)+ cost_feasible(F2c:attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生) | — | A1 ✓ | | EnemyTable | (level_index, node_depth, enemy_id)——每行集恰一敌种 | 无(进度成长表,A1 同型) | 每行集恰一敌种(行集内 enemy_id 唯一,违规加载期致命) | — | B2 ✓ | | HeroTable | 单行全局 | 单行全局定义表(ActionEconomyTable 先例) | atk = 0 加载拒绝("0 基线 × 百分比增益恒零"死锁自 schema 层杜绝) | — | B2 ✓ | | RewardTable | 按 reward_id(多行;pool_tag 分池不参与键) | 定义表非进度成长表(schema 注释承载) | 池空(pool_tag × 雾/非雾两态各 ≥1 活行)· clear_default 恒正当量 · buff⇒effect_id 必填 · safe_cell 活 buff 行集非空 · floor_supply_missing / no_damage_row(跨 RewardConfigTable) | effect_id 精确引用 StatusEffectTable(已激活) | B4 ✓ | | RewardConfigTable | 单行全局 | 单行全局常量表(HeroTable / ActionEconomyTable 先例) | k_dmg=0 = F5 断言关闭(现行默认——刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸托底) | — | B4 ✓ | | SettlementTable | 单行全局 | 单行全局常量/结构表(HeroTable / ActionEconomyTable 先例) | segment_order 恒等拍板段序校验违例致命 · effect_pool_map 键 ⊆ StatusEffectTable 活行集 · p_perfect 载体域 {100,200} 域外加载致命——三行致命 manifest:segorder_violation / poolmap_dangling / pperfect_oob | effect_pool_map 悬空加载期致命 | B1 ✓ | | AuxItemTable | 按 item_id | 定义表非进度成长表(schema 注释承载) | target ∈ 各空间 effect_kind 支持域交集(A5 承载——真视窄域护栏校验,夹具 a5_target_violation) | counter_item_ids(VariationTable → item_id)首引已定稿〔probe_dust〕、悬空校验激活随 D2×② 对侧批 | A5 ✓(原 HelperItemTable 占位更名转正——A5 定稿登记) | ### 未回填占位表 | 表资源 | 占位内容 | 键结构 | Owner | |---|---|---|---| | DifficultyTable | 成长参数 + 精英/Boss 修饰预留命名空间(乘数域 [0.1,10] registry 预登记;越域 clamp+warning = registry 登记的显式例外——组合事故非配置事故,非 fail-fast 场景) | (level_index, node_depth) | C3(第 12 位) | | MetaTable | 代币/天赋字段名 | 单行全局(元进度轴豁免,C4 可改键) | C4(第 16 位) | ### MVP 内容行(事实登记归 registry) | 表 | MVP 行集 | 值状态 | |---|---|---| | ActionEconomyTable | 单行:ap_per_round=8 · dig/插旗/拔旗=1 · rap_refresh_cost=8 · move/chain=0(冻结) | 定稿值,登记归 registry `action_economy` | | VariationTable | 恰含 fog 行(unlock_level=2 · hide_numbers · safe_nonzero) | 岩浆/冰冻无 MVP 行(垂直切片建行) | | StatusEffectTable | 四行 attack_up / defense_up / ambush / weaken(全 stack) | 值 provisional,锚验收归 B4/B2/B1;weaken = −65(值域 [60,85]——B1 F0 裁决,条款 2 锚闭合);defense_up = −30(减伤墙批 C3 投值,叠满两层 −60 帽——B3/registry 同批) | | BoardGenTable | 参考行 L1 / L2 | provisional(C3 调参所有权) | | EnemyTable | 三敌种行 goblin / bugbear / ogre_chief(C3 修订批转正——registry `enemy.mvp_content` 为准) | 值 provisional——溯源义务见登记与对账义务表 | | HeroTable | 单行(max_hp / atk=6 原型锚) | max_hp 出处待查;atk provisional 归 C3 | | RewardTable | 默认根行集八行:defense_up_basic / attack_up_basic / spark_aoe / heal_minor / knife_t1 / knife_adv / heal_pack / clear_blast | 值 provisional 归 C3;格子池四行即时类 + 注入池三行可选类(active_inject——供给控制器承载,原"即时 75% / 可选 25%"权重占比由注入机制替代)+ 清图槽单行 | | RewardConfigTable | 单行 t_fog_min=2 · k_dmg=0 · n_draft=32 · p_min=8 · active_p0=0.4 · active_decay=0.5 | provisional 归 C3 | | SettlementTable | 单行默认(D_ref=12 · p_perfect=200 · 拍板段序 · clamp −100/+100 · floor · +4 字段:damage_a_per_cell=1 / damage_a_cap=8 / damage_b_per_flag=2 / armor_effectiveness 克制矩阵——护甲克制批,C3 修订批接口漂移) | provisional 归 C3 | ## 加载·校验·热重载 规则: 1. 规则 6(出错当场中止):D2 在加载与运行期发现数据出错一律致命中止,不做软回退——错误数值上线比崩溃更伤(P1/P2 信任底线);错误分两类处置(下表)。唯一例外:debug 热重载校验失败时保留旧表停留 Ready、给出错误清单,不杀运行中的调试会话。 举证:未播种流取数见边界表 EC7;请求越域见难度行解析前置守卫。两类构建分流见本节「校验双通道」。 2. 规则 7(零界面依赖):D2 自己的代码与测试一律不 import 任何 Control/场景类——纯数据层不碰表现;没有界面依赖,CI 与无窗口环境才能跑完整战斗。 举证:无计算。验收 = 静态扫描 + headless 实跑两项合成判定(AC-10)。 3. 规则 8(版本拒读):版本常量 `SCHEMA_VERSION` 归 D2;表文件与存档(D3)携带版本号,存档带来的版本号高于当前支持时直接拒绝读取——旧程序不硬猜新格式的数据。 举证:无计算。拒读行为见边界表 EC9(比较逻辑归 D3,常量归 D2)。 | 错误类别 | 什么情况 | 处置 | |---|---|---| | 加载期校验错误 | 启动加载/热重载时查出:字段越界、类型错、NaN、表为空或某 level 无行、缺 depth-0 锚行、重复键、悬空引用、权重和 ≤0、段序违例、池映射悬空、p_perfect 越域 | 经校验双通道处理;启动路径在 editor、CI、headless、release 一律致命 | | 运行期致命 | 运行中越界请求:关卡/深度坐标越域(解析入口前置守卫拦截)、未设种子就取随机数 | 不分构建一律致命:游戏内走致命错误路径,headless 冒烟进程 quit 非 0 | 设计意图:MVP 有意不做软回退——带病数值上线比崩溃更伤信任底线。 ### 校验双通道(规则 6 实现参照:ConfigDB.load_tables(root, collect_only)) | 通道 / 机制 | 要点 | |---|---| | 游戏通道(collect_only=false,默认) | 校验错误 → push_error 错误清单 → 走致命错误路径(游戏内中止构建;headless 下独立进程非 0 退出——`--script` 协议,设计顺序 #1 时无主场景可依赖);release 构建的致命表现(引擎默认错误路径的实际行为验证)显式 deferred→首次 release 导出 checklist(与 AC-18 同待遇) | | 测试通道(collect_only=true,仅 debug/测试构建) | 收集错误不升级,供冒烟脚本逐夹具断言错误类别后一次性退出 | | 夹具注入 | debug/test 构建允许经命令行 `--data-root=<路径>` 让 ConfigDB 从注入根加载(夹具放 tests/fixtures/);禁止把夹具拷进 data/tables/(污染 AC-1 的干净检出前提) | | 诊断收集器 | resolve / 校验 / override 的 warning 与错误共用同一类目体系,统一经 ConfigDB 内部诊断收集器投递,同时镜像 push_warning 供开发期控制台(Godot 无进程内拦截 push_warning 的 API——冒烟断言必须走收集器);测试通道暴露只读诊断清单与 `reset_diagnostics()`;跨调用去重状态(R1 四元组注册表)随 `load_tables()` 或 `reset_diagnostics()` 清空(用例间无顺序依赖);游戏通道 warning 仅镜像控制台,不承诺收集 | ### 状态机 | 状态 | 进入条件 | 行为/出口 | |---|---|---| | Unloaded | 进程启动前 | 磁盘 .tres 未加载 | | Loading | `ConfigDB._ready()` 或 `load_tables()` | 按表清单逐一 `load()`;文件缺失即入 Failed | | Validating | 单个资源加载成功 | 调 schema 的 `validate()`,错误入收集器 | | Ready | 全部通过 | 类型化属性可读 | | Failed(启动路径) | 启动加载任一致命错误 | 全构建致命中止并输出错误清单 | | Failed(reload 路径) | 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单(不杀运行中的调试会话) | | Reloading(仅 debug) | 编辑器热重载 / 调试键 | 战斗中拒绝(返回 false,战斗结束后手动再触发);否则重走 Loading → Ready | 不变式:RNG 与 ConfigDB 状态线相互独立——Reloading / Failed(任一路径)不触碰 RngService,计数器与 master_seed 不变(派生只依赖盐/master_seed/scope,与表数据无关;若按状态机对称性在 reload 时重置 RNG 将击穿 AC-4)。 ### 边界情况 | # | 条件 | 行为 | 依据 | |---|---|---|---| | EC1 | 表文件缺失或 load() 失败 | 致命中止,错误清单含路径;无软回退 | 规则 6 | | EC2 | 字段越界 / NaN / 类型不符 | `validate()` 收集后致命 | 规则 6 | | EC3 | 表为空或某 level 行集为空 | 校验阶段致命(R1 前置条件),不会进入运行期 | 规则 6 | | EC4 | 请求 (level_index, node_depth) 无精确行 | 按 R1 floor 解析 + warning(按四元组去重)——预期行为,不是错误 | R1 | | EC5 | level_index 超过表最大 level | 命中最大 level 行(floor 规则的自然结果,无独立 clamp 分支) | R1 | | EC6 | master_seed 为负 | abs() 归一化 + warning,保证 ∈ [0, 2^63);恰为 INT64_MIN → 致命(abs 恒等回绕,与 0 静默别名——机理是输出掩码抹掉 bit 63) | R2 | | EC7 | 未设 master_seed 就取随机数 | `push_error` 后显式中止(GDScript 的 push_error 本身不终止进程——游戏内走致命错误路径,冒烟脚本 `quit(1)`),取到的值不可用——C2 未接线的显式暴露,禁止 seed=0 软回退 | 规则 6 | | EC8 | 战斗进行中请求热重载 | 拒绝并返回 false;战斗结束后手动再触发(不自动重放) | 状态机表 | | EC9 | 存档携带的 SCHEMA_VERSION 高于当前 | 拒绝读取(比较逻辑归 D3,常量归 D2) | 规则 8 | | EC10 | 奖励权重表权重和 ≤ 0 | validate 致命(B4 回填后必须 > 0) | 规则 6 | | EC11 | debug override 类型或范围与 schema 字段不符 | warning + 忽略该条(仅 debug;release 无 override 源) | R3 | | EC12 | 同一流名被重复注册或请求未知名流 | debug 断言中止(流名封闭) | 规则 5 | | EC13 | 两个系统同帧请求同一流 | 允许——`open_stream` 各自取号(计数器各 +1)按调用顺序串行生效;单线程假设成立(Godot 主线程玩法逻辑),不引入锁 | 规则 5 | | EC14 | 请求越域(level_index < 1 或 node_depth < 0) | 规则 6 致命(R1 前置守卫) | R1 | | EC15 | 某 level 行集缺 depth-0 锚行 | validate 致命(防"入口天灾":入口请求静默 floor 到深行造成难度突刺);写死开、无运行期开关,C3 如需重议走本 GDD 显式修订流程(非 knob) | 规则 6 | | EC16 | 热重载校验失败 | 保留旧表停留 Ready + debug 错误清单 | 状态机 reload 路径 | ## 统一取数与调试覆盖 规则: 1. 规则 1(数字只写在表里):凡是设计师要调的数字(行动点、插旗成本、奖励值、敌人数值……)只存在于 data/tables/ 下的 .tres 数值表里,一处定义、处处引用;代码文件里禁止出现玩法数值字面量(引擎/工程常量除外),评审发现写死的玩法数值即评审阻塞项。 举证:无计算。违规检测 = 启发式扫描 + 人工复核(AC-14,随各 owner 回填定稿与里程碑 gate-check 执行)。 2. 规则 2(取数只找 ConfigDB):全部 19 个系统读数值只有一条路——经 autoload ConfigDB 的类型化属性读取(例 `ConfigDB.action_economy.ap_per_round`);禁止绕过它自己 `load()` 表文件或长期缓存副本(副本绕过校验,热重载换新值时它也不知情)。唯一例外:debug/测试构建可从命令行注入的根目录加载。 举证:无计算。注入根机制见「校验双通道」夹具注入行。 设计意图:单点发放 + 单点读取,热调与校验才有意义。 ### 调试覆盖链(R3 debug_override,仅 debug 构建) **结论**:`effective = override[key] if _has_override(key) else table_value`——仅 debug 构建加载 override 源;v1 仅支持单行全局表字段;类型/范围不符 → warning + 忽略;release 结构性不存在;永不回写 .tres。 | # | 边界 | |---|---| | ① | 覆盖判定与存储内聚于 ConfigDB(规则 2 单点),gameplay 不可见 override 来源 | | ② | key = schema 字段点路径(与规则 2 属性路径同构,例 `action_economy.ap_per_round`);未知 key → debug warning + 忽略该条;v1 仅支持单行全局表字段——进度键表的行寻址语法(如 `difficulty.(2,0).hp`)登记为后续扩展,当前 override 对进度表无 key 空间 | | ③ | override 值类型与范围必须与 schema 字段一致,不一致 → debug warning + 忽略该条(复用 schema 校验逻辑,防止 debug 会话注入越界值污染平衡基线) | | ④ | release 构建不加载、不注册任何 override 源(恒等是结构保证而非"碰巧不生效");debug 谓词实现为可注入形式(`OS.is_debug_build()` 之外引入 feature 标签闸,例 `sim_release`)——AC-8 的模拟以此为基础 | | ⑤ | override 仅为运行时状态,永不回写 .tres | ## 随机数服务(RngService) 规则: 1. 规则 5(随机只从 RngService 按流取):全项目玩法代码的随机数只此一家——autoload RngService 持有封闭的三条具名流(下表),流集合封闭、只含具名消费者;要新增随机流(含 B1 若需结算随机)必须改代码走增流通道并通过 P1 支柱评审(随机须有界、可见或可推理);玩法代码禁止 `randomize()` 或自建 RandomNumberGenerator。master seed 由 C2 在冒险开始时设定——同一冒险内相同决策序列必然相同战斗结果。 举证:种子派生公式与黄金向量见本节「R2 流种子派生」;依据 = B4 契约源〔AC-1〕· R2 黄金向量。 ### 封闭流与取数入口 | 流 | 消费者 | scope 取号事件 | |---|---|---| | board_gen | A1 布雷 | 每次生成新扫雷图(A1 换图时) | | reward_roll | B4 掉落 | 每次 board install 批(格奖励批 + 清图奖励批)——B4 AC-1 为契约源 | | encounter | B1 战斗实例化 | 每场战斗实例化恰一 scope(begin_battle 实例化期,扫雷开始前——unit_count 区间均匀骰,挖掘决策域之外 P1 合规;C3 遭遇随机化批增流,registry 已登记) | 新增流 = 增流通道:改代码 + P1 支柱评审(随机须有界、可见或可推理)。 | 取数入口 | 给谁用 | 行为 | |---|---|---| | `open_stream(stream) -> RandomNumberGenerator` | 玩法代码(有状态流门面) | 每调用一次 = 一次 scope 取号:该流计数器 +1 → 派生 → 返回已播种实例;一个 scope 事件内所有取数共用该实例 | | `derive_at(stream, scope)` | 测试/预览(纯函数) | 不推计数器——预览取数不污染正式序列(杜绝预览类取数的平衡 Heisenbug) | ### R2 流种子派生(stream_seed_derivation) **结论**:纯派生函数——同一冒险内(master_seed 固定)同流各 scope 种子互不碰撞;同输入恒同输出;跨 Godot 版本恒定(不用运行时 `hash()`——其跨版本稳定性无承诺);输出 ∈ [0, 2^63)。跨 master_seed 的全局单射不做声明(数学上由鸽笼原理排除)。Output Range:[0, 2^63)。 ``` derive_at(stream, scope) = ( SALT_s XOR wrap64(master_seed + scope × GOLDEN_RATIO) ) AND 0x7FFFFFFFFFFFFFFF wrap64 = GDScript int(64 位二补数)自然回绕 = MOD 2^64 ``` #### 实现级规约(必须逐条遵守,否则黄金向量失配) | 规约 | 要点 | |---|---| | wrap64 | GDScript int(64 位二补数)的自然回绕 = MOD 2^64;禁止 float 中间量(53 位尾数必丢精度)、禁止对中间量使用 `%`(带符号取余语义易误用)、禁止"主动修复溢出"(中间积本就越界——乘数 GOLDEN_RATIO = 0x9E3779B97F4A7C15 > 2^63,scope=1 即回绕,这是设计依赖的语义,不是缺陷) | | 常量书写 | 三个常量(盐×2 + 乘数)均 ≥2^63,GDScript 十六进制字面量按二补数解析为负——统一用分段表达式书写(例 `(0x9E3779B9 << 32) \| 0x7F4A7C15`,显式括号、不依赖优先级记忆),禁止直接书写 ≥2^63 的完整字面量;注意 GDScript 的 `^` 是按位 XOR | | 输出掩码 | 经 AND 掩码保证非负(∈ [0, 2^63))后再赋 `RandomNumberGenerator.seed` | **Variables:** | 变量 | 类型 | 范围 | 说明 | |---|---|---|---| | SALT_s | int64 | 编译期常量 | board_gen=0xA0761D6478BD642F · reward_roll=0xE7037ED1A0B428DB · encounter=0x8EBC6AF09C88C6E3(互异奇常数,均 ≠ 乘数) | | master_seed | int64 | [0, 2^63) | 冒险主种子;`set_master_seed()` 对负输入 abs() 归一化并 warning;恰为 INT64_MIN → 致命(abs 在 int64 对 INT64_MIN 恒等回绕,实测与 0 在全流种子逐一相等——别名机理是输出掩码抹掉 bit 63,掩码本身是规约的一部分);仅许全流计数器全零时调用(冒险中途重播种会静默清零计数器,击穿同冒险无碰撞前提与 D3 快照语义 → debug 断言) | | scope | int64 | ≥0 | 派生入参;有状态门面的取号值(每流独立计数器:set_master_seed() 时归零,每次 `open_stream` +1,永不重置、不随关卡/图序重置——否则不同关卡的同序号图会得到相同种子);负 scope → debug 断言中止(`derive_at` 是预览工具入口,静默负值会与 scope+2^63 经输出掩码别名) | **黄金向量(AC-3 的常量依据,经独立复算验证):** | 流 | master_seed | scope | stream_seed | |---|---|---|---| | board_gen | 42 | 0 | 2339089373825164293 | | board_gen | 42 | 1 | 4485977604321908752 | | board_gen | 42 | 2 | 2024629813010603131 | | board_gen | 42 | 3 | 8849696722413293638 | | reward_roll | 42 | 0 | 7422916049646135537 | | encounter | 42 | 0 | 1061841193803499209 | - Example:同流同 scope 重复调用 `derive_at` 恒等(纯函数);同帧对同一流连续 `open_stream` 两次 → 计数器各 +1,得两个互异且顺序确定的种子。 ### RNG 生命周期与快照 - RNG 生命周期:进程启动 → 流待命(未播种时取数即致命)→ 冒险开始设定 master_seed → 每次具名 scope 事件派生种子 → 冒险结束保留供复盘。 - 快照 = master_seed + 各流 scope 计数器(仅记录计数器、缺 master_seed 不足以恢复序列——进程重启会从种子序列起点重放;见 D3 接口)。 - 快照切出须发生在无在飞 `open_stream` 实例时(scope 事件边界之外):门面实例作用域限定于单次 scope 事件、消费者不得跨事件持有——在飞实例的内部取数进度不入快照,违反则续玩/复盘与实况分叉(AC-15 失效),该前提由 D3 消费侧遵守。 ## 难度行解析(R1 difficulty_row_resolution) **结论**:resolve 恒返回已有行——进度键 floor 解析、实体键精确匹配;不存在 default 行兜底、不存在 clamp 外推;非精确命中记 warning(四元组去重);请求越域在解析前置守卫致命。Output Range:行资源必有值(前置条件由校验保证);极端输入(超表大关卡号)命中最大已有行。 ``` row = resolve(table, level_index, node_depth) (ConfigDB 公开解析方法,table 经类型化属性取得) 前置守卫:level_index < 1 ∨ node_depth < 0 → 规则 6 致命(请求越域,不进解析) L* = max{ lv ∈ LevelKeys(table) | lv ≤ level_index };空集(请求低于表最小键,向上解析)→ L* = min LevelKeys(table) D* = max{ dp ∈ DepthKeys(L*) | dp ≤ node_depth };空集 → D* = min DepthKeys(L*) ``` **Variables:** | 变量 | 类型 | 范围 | 说明 | |---|---|---|---| | level_index | int | ≥1 | 当前关卡序号(1 起);越域 → 致命 | | node_depth | int | ≥0 | 节点深度(0=关卡入口);越域 → 致命 | | L* / D* | int | 表内键 | 命中行键 | - 无兜底 / 无外推的前置:表非空、每个 level 至少 1 行、每个 level 含 depth-0 锚行是规则 6 校验前置;超表成长外推是 C3 owner 公式的职责,D2 只解析到最近已有行。 - warning 定位为开发期异常信号(防刷屏去重是刻意的),不是难度审计工具——离线审计由曲线导出工具承担(headless 脚本导出 resolve(level 1..N × depth 0..M) 矩阵供 diff,登记为 C3 期交付物)。 - Example:表含行 (1,0)(1,4)(2,0)(2,6)——`resolve(3,3)` → L*=2、D*=0 → 返回 (2,0) 行,warning 一条(missing_type=both);`resolve(1,4)` → 精确命中零 warning;`resolve(99,99)` → 命中 (2,6),missing_type=both(与前者去重键不同,各自一条)。 - 去重键 = (table_id, missing_type, L*, D*) 四元组,内容含请求 (level_index, node_depth)、解析结果 (L*, D*)、缺失类型;去重注册表随每次 `load_tables()` 或 `reset_diagnostics()` 清空。 ### missing_type(R1 warning 分类枚举) | 值 | 判定 | |---|---| | base_ceil | level 分量向上解析(请求低于表最小键)——恒记 base_ceil(depth 分量缺失与否只写入 warning 内容,不改类型) | | level_floor / depth_floor | 单维向下解析 | | both | level_floor 与 depth_floor 同时发生 | - depth 分量只有精确命中或向下 floor 两种情形(EC15 锚行保证每个 level 含 depth-0,永不 ceil);AC-5 各用例的 missing_type 均由本规则推导。 设计意图:解析永不发明数据——floor 到已有行并留 warning,外推归 C3 owner 公式。 ## 依赖与输出 **上游(hard)**:无——Foundation 层,D2 不依赖任何系统;master_seed 由 C2 冒险开始时设定。 | 方向 | 系统 | 性质 | 接口要点 | |---|---|---|---| | 下游 | 全部其他 19 系统 | hard(规则 1) | ConfigDB 类型化读取;禁止其他取数路径(A2 经 A3 表间接承载参数) | | 下游 | A1 | hard | BoardGenTable 回填(首个进度键表)+ VariationTable schema(biome/tag 字段)+ `board_gen` 流(换图时取号) | | 下游 | A3 | hard | ActionEconomyTable schema 回填 | | 下游 | A4 | hard | VariationTable 回填 | | 下游 | B2 | hard | EnemyTable 回填(十字段 + 三敌种行 + 每行集恰一敌种校验)+ HeroTable 新增(接口漂移回写) | | 下游 | B3 | hard | StatusEffectTable 回填 | | 下游 | B4 | hard | RewardTable 回填(九字段 + 跨字段校验族 + 默认根行集,use_timing 承重 MVP 单值)+ RewardConfigTable 新增(单行全局)+ `reward_roll` 流(scope = 每次 board install 批——格奖励批 + 清图奖励批,B4 规则 2 接口漂移) | | 下游 | B1 | hard | SettlementTable 回填(可回放步骤流的数据前提)+ debug 热重载门标志 `set_reload_gate()`(战斗开始置位 / 结束复位,仅 debug 构建)+ `encounter` 流(战斗实例化期取号恰一 scope——unit_count 区间骰,C3 遭遇随机化批增流) | | 下游 | C2 / C3 | hard | `RngService.set_master_seed()` + DifficultyTable 行解析 + 曲线导出工具交付物 | | 下游 | D3 | hard | `SCHEMA_VERSION` + 存档内 master_seed + 各流 scope 计数器快照(续玩/复盘;含只读计数器接口,快照须在无在飞实例时切出) | | 下游 | A5 / C4 | hard(垂直切片 / Alpha) | AuxItemTable 回填(A5 定稿登记——原 HelperItemTable 占位更名转正;counter_item_ids 表值回填与悬空校验激活随 D2×② 对侧批)/ MetaTable 回填 | | 下游 | D1 / D4 / D5 / D6 | soft | 如需参数表再增 schema(现无占位);display_name 本地化归 D6 | | 外发 | —— | —— | 不订阅任何玩法事件;仅 debug 构建发 `config_reloaded`(D1 调试用) | - 数据读取类事实依赖不逐条列边(完整清单以本表为准);A2 无直接依赖(只外发事件,参数经 A3 表承载),与 systems-index 的 A2 回边规则一致。 - 接口漂移修订规则:owner GDD 定稿时可修改占位 schema 类(schema 与表同仓),但必须回写本 GDD 表清单与 `design/registry/entities.yaml`,并在本文件版本历史登记修订;回执登记粒度 = 受影响字段/枚举/流——逐表 diff schema 目录(schema 目录 + 逐表字段行 + MVP 内容行 + 依赖表),禁按"对侧 GDD"粒度整批登记;占位参考值定稿时必须删除本 GDD 调参表清单中的对应条目(值的事实登记归 owner GDD 与 registry)。 - 双向一致性已与 systems-index 依赖分层表核对一致(核对明细见 analysis 附录);已知待定:C3 是否消费 B1 输出——见索引门禁增补记录,归 C3 GDD 定稿,不影响 D2。 ### 登记与对账义务(在案项——已闭环项见 analysis 附录) | 编号 | 内容 | 对侧 | 状态 | |---|---|---|---| | 4 | counter_item_ids 悬空引用(D2 首个引用型字段)——MVP 不校验,首个道具 id 回填时补校验 + 条件性夹具 | A5 | 条件性(未激活) | | 5 | EnemyTable MVP 行值(goblin 18HP / 4ATK 等)溯源补登(原型 REPORT,出处待查) | B2 / C3 | 待补 | | 6 | HeroTable max_hp MVP 值溯源补登(出处待查) | B2 / C3 | 待补 | | 7 | 胜场成长机制去留(+25% HP / +1 ATK——反雪球问题) | C3 | 待裁决 | | 8 | 曲线导出工具(headless 导出 resolve 矩阵供 diff)——R1 审计的离线载体 | C3 | C3 期交付物(排期倒挂注意见 analysis 开放问题 #5) | | 9 | 规则 5 对全项目玩法代码的随机扫描 | Technical Setup | 项目级 CI 项 | | 10 | release 构建致命表现验证(引擎默认错误路径实际行为) | 首次 release 导出 | deferred(AC-18 checklist) | | 11 | 战斗级可回放集成测试(同 master_seed 同动作序列 → 步骤流逐行相同) | B1 | deferred(AC-15) | ## P0 最小闭环 1. D2 交付全部 schema 类与占位表(含键结构)——先有格子。 2. owner 系统 GDD 按设计顺序回填数值(十张已定稿 + 两张占位)。 3. 进程启动:`ConfigDB.load_tables()` 逐一加载 + schema `validate()`,经校验双通道。 4. 全部通过 → Ready:19 系统经类型化属性读数,无第二取数路径。 5. C2 冒险开始 `set_master_seed()`;三条封闭流按各自 scope 事件取号。 6. debug 构建热重载即改即验(战斗中被门拒绝,结束后手动再触发)。 7. `godot --headless --script` 冒烟 `quit(0)`——加载、校验、取数、派生全链路绿。 ## P1 / P2 后置 | 优先级 | 内容 | 后置原因 | |---|---|---| | P1 | 战斗级可回放集成测试(同 master_seed 同动作序列 → 步骤流逐行相同,AC-15) | D2 阶段无战斗载体,deferred→B1 | | P1 | 难度曲线导出工具(headless 导出 resolve 矩阵供 diff——R1 审计的离线载体) | C3 期交付物;排期倒挂见 analysis 开放问题 #5 | | P2 | release 构建致命表现 + override 结构不存在验证(AC-18) | deferred→首次 release 导出 checklist | | P2 | 进度键表 override 行寻址语法(如 `difficulty.(2,0).hp`) | v1 仅支持单行全局表字段,登记为后续扩展 | | P2 | 编辑器内表文件热重载自动化 | 低优先、未指派(analysis 开放问题 #2) | | P2 | gdUnit4 引入与冒烟用例迁移 | Technical Setup 阶段裁决(analysis 开放问题 #1) | ## 呈现与 UI 面 | 项 | 要点 | |---|---| | 玩家 UI | 无——MVP 无面向玩家的 UI;debug override 与夹具注入均经 Godot 编辑器 / 命令行参数设置(`--data-root=`,仅 debug),不做自定义面板 | | 校验失败呈现 | editor / CI 输出控制台(错误清单格式见 AC-1);release 构建走引擎默认错误路径(实际表现验证 deferred→首次导出 checklist) | | 开发侧可视化 | Godot 编辑器 Resource inspector(自带)+ 校验错误控制台输出 | | 音频 | 无需求(纯数据层,无直接表现面) | ## 调参表 ### D2 自身 knobs(元参数) | Knob | 默认 | 调过高 | 调过低 | |---|---|---|---| | floor 解析策略 | floor + warning(无外推) | —— | 引入外推/静默会掩盖 C3 难度断档 | | rng_stream_names | 封闭 3 流(board_gen / reward_roll / encounter) | 增流需改代码 + P1 评审(有意设卡) | 流复用破坏可复现隔离 | | debug_override_enabled | false(仅 debug 构建) | 污染测试基线 | —— | | 表路径清单 | data/tables/ 固定(debug 注入根除外) | —— | 散装路径破坏单点访问 | | depth-0 锚行校验 | 写死开(非 knob,规则 6) | —— | ——(重议须走本 GDD 显式修订,EC15) | - Knob 交互:floor 策略与 C3 的难度语义共享,改动需 C3 GDD 知情;校验严格度不设 knob(fatal 写死——放行带病数值会使 CI 冒烟失去意义)。 - 未决占位与溯源义务(provisional——owner 定稿前不视为平衡事实;定稿后删除对应条目,值的事实登记归 owner GDD 与 registry):EnemyTable 行值溯源(义务 #5)· HeroTable max_hp 溯源(义务 #6)· 胜场成长机制去留(义务 #7)——见登记与对账义务表;已定稿占位参考值(标中旗 / 清图奖励 / 状态四行 / 奖励池等)已随各 owner 定稿清退出本清单。 ### 表 schema 逐表字段行(D2 核心资产——字段全集以各 owner GDD Schema 节与 registry 为准) #### ActionEconomyTable(单行全局 · A3 回填 ✓) | 字段 | knob | 说明 | |---|---|---| | ap_per_round | ✓ | 每轮行动点 | | dig_ap_cost | ✓ | 挖掘成本 | | flag_place_ap_cost | ✓ | 插旗成本 | | flag_pull_ap_cost | ✓ | 拔旗成本 | | rap_refresh_cost | ✓ | RAP 刷新门槛 | | move_ap_cost | ✗ 冻结 | 移动成本 | | chain_ap_cost | ✗ 冻结 | 连锁展开成本 | | 跨字段校验 | — | 任一动作成本 ≤ ap_per_round(违规加载期致命,条件性夹具登记) | #### VariationTable(按 variant_id · A4 回填 ✓) | 字段 | 类型 / 域 | knob | 说明 | |---|---|---|---| | variant_id | String 主键唯一 | — | 行键 | | display_name | String | — | 本地化归 D6 | | unlock_level | int [1,999] | ✓ | 进度语义承载列(规则 4 豁免载体) | | scope | region / global | — | global 预留 C3 | | info_mask | none / hide_numbers | — | 雾行 = hide_numbers | | cell_constraint | any / safe_nonzero | — | 雾行 = safe_nonzero | | biome_tag | StringList | ✓ | ∅=通配 | | counter_item_ids | StringList | ✗ | A5 预留;悬空引用政策见目录表 | | 承重规则 | — | — | 生成期布置约束:雾区仅非 0 安全格(区内无雷无 0 格,A1 生成期强制)+ 零特判原则 | #### StatusEffectTable(按 effect_id · B3 回填 ✓) | 字段 | 类型 / 域 | 说明 | |---|---|---| | effect_id | String 主键唯一 | 行键;引用校验已激活 | | display_name | String | 本地化归 D6 | | target_side | hero / enemy | 作用侧 | | magnitude | percent ∈ [-100,100] ∧ ≠0 / flat ≥ 1 | 跨字段校验 | | unit | percent / flat | 量纲 | | duration_settlements | [1,99] | 持续结算次数 | | stack_policy | stack / refresh / independent | 叠层策略 | | max_stack | [1,16] | 仅 stack 消费 | #### BoardGenTable((level_index, node_depth) 首个进度键表 · A1 回填 ✓) | 字段 | 类型 / 域 | knob | 说明 | |---|---|---|---| | board_width / board_height | int [8,64] | ✓ | 板尺寸 | | mine_density | float (0,0.5] | ✓ | 密度浮点(尺寸解耦);布雷数下限 round(d×(W×H−9)) ≥ 1 | | inferability_target | float [0.85,1] | ✓ | P1 门槛承重参数 + 绝对锚 | | solver_rules | single / single_subset | ✗ 冻结 single_subset | 推理定义域非调参 | | min_safe_count | int [1,999] | ✓ | 退化板对冲 | | max_gen_attempts | int [1,32] | ✓ | 生成-验证循环上限 | | fog_region_count | int [0,32] | ✓ | 雾区数 | | fog_target_ratio | float [0,1) | ✓ | ≤ min(8d, 0.25) 绝对上限 | | board_biome | StringSet 非空 | — | 域校验致命(词表 owner 未定稿前只校验非空) | | elite_ / boss_ 前缀 | 命名空间预留 | ✗ | 第三轴登记约定——语义归 C1/C3,本 GDD 不定义 | | 跨字段谓词 | — | — | fog_domain_ok(F2)+ min_safe_feasible(F2b)+ cost_feasible(F2c:attempts×W×H ≤ 16384 provisional,运行期禁墙钟逃生)——加载期致命 | #### EnemyTable((level_index, node_depth, enemy_id) · B2 回填 ✓) | 字段 | 类型 / 域 | knob | 说明 | |---|---|---|---| | enemy_id | String(行集内主键) | — | 行集内唯一(每行集恰一敌种) | | display_name | String | — | 本地化归 D6 | | unit_count | int [1,3] | ✓ | 遭遇区间下界(C3 修订批——区间 [unit_count, unit_count_max] 经 B1 encounter 骰定本战实际数量;新敌种首遇节点固定不加骰) | | unit_count_max | int [unit_count,3],可省略 | ✓ | 遭遇区间上界(缺省 = 定值,旧行零迁移——C3 修订批新增字段) | | armor_type | enum {none, a, b},缺省 none | ✓ | 护甲类型(克制矩阵 = SettlementTable.armor_effectiveness;MVP 映射 goblin=none / bugbear=a / ogre_chief=b——护甲克制批) | | hp | int [1,999] | ✓ | 难度修饰前基线 | | atk | int [0,999] | ✓ | attack 行为伤害基值;0 = 纯伏击伤敌种合法 | | heavy_multiplier | int [1,16] | ✓ | 无 heavy 拍敌种不消费(非校验项先例) | | pattern | StringList,环长 [1,8] | — | MVP 校验域 {attack, charge, heavy}——guard/boost/hinder/summon 预留值校验拒绝 | | advantage_per_round_cap | int [0,16] | ✓ | 默认 4 定稿(反灾难护栏);0 = 免疫伏击合法极端 | #### HeroTable(单行全局 · B2 回填 ✓ · 接口漂移新增) | 字段 | 类型 / 域 | knob | 说明 | |---|---|---|---| | max_hp | int [1,999] | ✓ | MVP 值 provisional(出处待查) | | atk | int [1,999] | — | 0 = 加载拒绝(基础攻击恒存在,"0 基线 × 百分比增益恒零"死锁自 schema 层杜绝);MVP = 6 原型锚 provisional 归 C3;消费面 = 主角战斗段先手攻击 | #### RewardTable(按 reward_id · B4 回填 ✓) | 字段 | 类型 / 域 | knob | 说明 | |---|---|---|---| | reward_id | String 主键 | — | 行键(多行;pool_tag 分池不参与键) | | category | buff / damage_item / heal_pack / instant_aoe / instant_heal | — | 两类交付轴:即时类 {buff, instant_aoe, instant_heal} 当场兑付不进库存 / 可选类 {damage_item, heal_pack} 入关卡域库存;预留值校验拒绝 | | effect_id | String 可空 | — | buff 类必填(StatusEffectTable 精确引用);非 buff 恒空 | | display_name | String 非空 | — | D1 呈现数据源,本地化归 D6(B2/B3 同库惯例) | | power | int [0,999] | ✓ | item 类加载校验 ≥1;buff 类不消费非校验 | | weight | int [0,999] | ✓ | 0 = 不入池 | | pool_tag | safe_cell / active_inject / clear_default | — | clear_\ 命名空间预留拒绝;active_inject = 供给控制器注入池(可选类三行——install 期注入,非逐格 roll) | | tier | int [1,8] | ✓ | A4 雾档约束载体 | | use_timing | pre_settlement(MVP 单值) | — | 承重——道具使用窗口 MVP 激活,扩枚举走修订 | #### RewardConfigTable(单行全局 · B4 回填 ✓ · 接口漂移新增) | 字段 | 类型 / 域 | 默认 | 说明 | |---|---|---|---| | t_fog_min | int [1,8] | 2 | F2 雾档过滤门限 | | k_dmg | int [0,999] | 0 | 0 = F5 断言关闭(现行默认——刀保底语义由供给控制器战斗首件保证取代;no_damage_row 后闸托底) | | active_p0 | float [0,1] | 0.4 | 供给控制器注入概率初值(首件之后每张新板)——E[战斗获取] ≈ 1 标定目标,归 C3 | | active_decay | float (0,1] | 0.5 | 注入概率指数递减 0.4→0.2→0.1→…(加难杠杆候选 0.6 随 C3 OQ7 扫描) | | n_draft | int [1,999] | 32 | F5 草稿重 roll 上限 | | p_min | int [1,999] | 8 | 默认根最低刀 power——K≥1 供给闸刀力下界(floor_supply_missing) | #### SettlementTable(单行全局 · B1 回填 ✓ · 共十二字段) | 字段 | 类型 / 域 | 默认 | knob | 说明 | |---|---|---|---|---| | equivalence_ref_damage(D_ref) | int [1,999] | 12 | ✓ | 当量参考敞口;变更触发 B1 全对照表 + 条款③⑤对账重跑(禁独立调参) | | p_perfect | percent {100,200} | 200 | ✓ | 完美排雷清图奖励第二份兑付触发——非整数倍数乘数无运行时载体,域外加载致命 | | segment_order | StringList | 拍板段序 | ✗ 结构冻结 | 恒等拍板段序校验,违例致命(防实现乱序静默漂移) | | source_label_map | Dictionary source_class→文案键 | 封闭枚举全覆盖 | ✗ 结构冻结 | P2 归因呈现映射;键集 = B3 source_class ∪ {道具/判定扩展, enemy_action, flag_miss, hero_attack};新增键 = 接口漂移 | | effect_pool_map | Dictionary effect_id→{hero_offense, hero_defense, enemy_output, flat_attach} | 四键定稿映射 | ✗ 结构冻结 | 效果语义映射;键 ⊆ StatusEffectTable 活行集,悬空加载期致命;新 effect_id 上线须随批登记 | | pool_clamp_lo | int pp | −100 | ✗ 结构冻结 | percent 池地板域下界(乘数域随之 [0,2]) | | pool_clamp_hi | int pp | +100 | ✗ 结构冻结 | percent 池地板域上界 | | round_direction | enum {floor} | floor | ✗ 结构冻结 | 单一取整规则 | | damage_a_per_cell | int ≥ 0 | 1 | ✓ | 伤害A 开格充能/格(护甲克制批——充能与结算归 B1 战斗段〔F6〕,C3 投值) | | damage_a_cap | int ≥ 0 | 8 | ✓ | 伤害A 单轮充能封顶(0 = 不封顶——防连锁轮失控) | | damage_b_per_flag | int ≥ 0 | 2 | ✓ | 伤害B 标雷充能/首次标中雷(每雷实例一次去重) | | armor_effectiveness | Dictionary {base, dmg_a, dmg_b} × {none, a, b} | 特化 ×150 / 无甲 ×100 / 对位 ×50 | 键集 ✗ 结构冻结 / 数值 ✓ | 伤害类型 × 护甲克制矩阵——主角攻击三分量(基础/伤害A/伤害B)各自查表后 floor 相加;敌种甲型映射归 EnemyTable.armor_type | ## 验收标准 ### 测试口径 | 口径 | 要点 | |---|---| | 测试载体 | `--headless --script` 冒烟框架(脚本 extends SceneTree 以获得 quit(code);该模式 autoload 须由脚本显式实例化——AC-1 前提声明;设计顺序 #1 时无主场景可依赖) | | 覆盖 | 8/8 Core Rules;规则 6 加载期校验错误类别全部有夹具(悬空引用为条件性夹具,已随 B4 首引激活);运行期致命(请求越域 EC14、未播种 EC7)由 AC-5b / AC-17 行为级覆盖;R1 / R2 全部边界 | | 致命类断言协议 | 致命用例(AC-2b、AC-5b 游戏通道半、AC-6 后半、AC-11、AC-17)各为独立进程调用,冒烟 runner 清单逐条登记"调用 → 期望退出码";单进程内只做测试通道收集断言 | | warning 断言 | 一律经诊断收集器(见校验双通道),每用例先 `reset_diagnostics()` | | 人工与延迟 | AC-14 人工 · AC-15 deferred→B1 · AC-18 deferred→首次 release 导出 | ### AC 清单(Given-When-Then,编号不变) | AC | Given | When | Then | 覆盖 | |---|---|---|---|---| | AC-1 | 干净检出 | `godot --headless --script tests/smoke_data.gd` | 核心规则 #3 表清单的全部表(以清单为准,不写死数量)加载且 data/tables/ 无清单外 .tres;脚本成功 `quit(0)`,任一失败 `quit(1)` 并输出错误清单(表路径 + 字段 + 错误类别) | 规则 3/6 | | AC-2 | tests/fixtures/ 夹具集(越界字段 / 文件缺失 / 空表 / 某 level 无行 / 缺 depth-0 锚行 / 重复键 / 权重和 ≤0 各一;悬空引用为条件性夹具——已随 B4 首引激活) | 冒烟脚本经 `--data-root` 注入逐夹具运行 `load_tables(root, collect_only=true)`(测试通道,单进程逐个断言) | 每个夹具的错误清单含对应类别(EC1 / EC2 / EC3 / EC15 / EC10 及重复键类别) | 规则 6 / EC | | AC-2b | 任一致命夹具经注入根加载 | 冒烟 runner 以独立 headless 进程执行游戏通道 `load_tables(root, collect_only=false)`(`--script` 协议,脚本显式实例化 autoload;不依赖主场景) | 进程非 0 退出且错误清单完整——验证"游戏内真的会死"而非仅"校验器能发现" | 规则 6 游戏通道 | | AC-3 | R2 黄金向量表(6 常量) | 纯函数 `derive_at` 复算(board_gen master=42 scope 0–3、reward_roll master=42 scope=0、encounter master=42 scope=0);同流同 scope 重复调用 | 逐个等于向量表常量;重复调用恒等(纯函数无副作用) | R2 | | AC-4 | master_seed=42 | `open_stream("board_gen")` 连续取号 ≥100 次(跨模拟"关卡切换");同期调用任意 `reload()` | 公开只读计数器单调不重置(各关同序号图种子互异);计数器不变(状态线独立) | R2 / 规则 5 / 状态机表 | | AC-5 | 真实 DifficultyTable schema 构造的表含行 (1,0)(1,4)(2,0)(2,6)(每用例先 `reset_diagnostics()`) | 依次 `resolve(3,3)` → `resolve(3,4)` → `resolve(1,4)` → `resolve(99,99)`;单行表 {(2,0)} 上 `resolve(1,5)` | (3,3):返回 (2,0) 行且诊断收集器 warning 恰一条(missing_type=both,含请求/结果/类型);(3,4):warning 不新增(四元组去重键相同);(1,4):精确命中且零 warning;(99,99):命中 (2,6) 且新增恰一条(去重键含 D*,与首条不同);单行表:命中 (2,0) 且 missing_type=base_ceil(由 R1 分类规则推导:level 分量向上解析恒记 base_ceil) | R1 | | AC-5b | 真实 DifficultyTable schema 构造的合法表 | `resolve(0, 3)`(level_index < 1)与 `resolve(2, -1)`(node_depth < 0);同用例经游戏通道独立进程复跑 | 测试通道诊断清单各含"请求越域"致命类别一条且不进入解析(EC14 / R1 前置守卫);独立进程非 0 退出 | R1 / EC14 | | AC-6 | master_seed=-7;master_seed=INT64_MIN;`derive_at` 传入负 scope | `set_master_seed()`;`set_master_seed()`;`derive_at` | 归一化为 7 + 诊断收集器 warning 一条,后续种子 ∈ [0, 2^63)(EC6);INT64_MIN 致命(非 warning;独立进程断言退出码非 0);负 scope debug 断言中止(负值经输出掩码与 scope+2^63 别名) | R2 / EC6 | | AC-7 | debug 构建 override{"action_economy.ap_per_round"=12}(域内值——A3 schema 域 [1,32];越域 override 会被 R3③ 忽略,出域示例会使本 AC 假失败) | 经 ConfigDB 读取;override key 为未知路径、类型或范围与 schema 不符;`reload()` 后 | effective=12;诊断收集器 warning + 忽略该条;.tres 文件内容不变(R3 边界①③⑤) | R3 | | AC-8 | 非 debug 谓词模拟(feature 标签) | `_has_override(key)` | 恒 false——本条仅证明门存在性;"release 不加载 override 源"的结构保证验证 deferred→首次 release 导出 checklist | R3④ | | AC-9 | D2 文件集(data/schemas、ConfigDB、RngService、tests) | 静态扫描(模式覆盖 `load(`、`preload(`、`ResourceLoader.load`、`"res://data/" +` 动态拼接;`res://tests/fixtures/` 前缀豁免——不在 `res://data/` 扫描域) | 无 `randomize()` 调用、无 ConfigDB 之外的 data 表加载(规则 2/5);规则 5 对全项目玩法代码的扫描登记为项目级 CI 项(义务 #9) | 规则 2/5 | | AC-10 | D2 文件集 | 静态扫描 + AC-1 headless 实跑 | 无 `extends Control/Node2D`、无 `.tscn` preload,且 AC-1 headless 实跑通过——两项合成判定规则 7(扫描是启发式,硬证据是 headless 实跑) | 规则 7 | | AC-11 | RngService(debug 构建) | 请求未知名流或重复注册流名 | debug 断言中止(EC12;独立进程退出码非 0) | 规则 5 / EC12 | | AC-12 | 同帧对同一流 | 连续两次 `open_stream` | 两个互异且顺序确定的种子(EC13) | 规则 5 / EC13 | | AC-13 | 冒烟脚本运行 | 反射断言 | `SCHEMA_VERSION` 常量存在且 ConfigDB 可读;全部 schema 携带版本字段;进度类表(BoardGen/Enemy/Difficulty)暴露 (level_index, node_depth[, enemy_id]) 键字段;DifficultyTable schema 暴露 `elite_` / `boss_` 前缀预留字段("高版本拒读"路径 deferred→D3) | 规则 8 + 规则 4 键契约 + 第三维命名空间 | | AC-14(人工) | 每次回填定稿 | 验收人复核 | 规则 1 数值字面量禁令成立:启发式扫描(warning 级,误报难免)+ 人工复核三要素——验收人 = owner GDD 回填定稿评审 + 里程碑 gate-check;时机 = 每次回填定稿;证据 = 评审记录 / gate-check 登记 | 规则 1 | | AC-15(deferred→B1) | 同 master_seed 同动作序列 | B1 集成测试重放 | 结算步骤流逐行相同(D2 阶段无战斗载体) | 规则 5 可回放 | | AC-16 | debug 构建且 B1 侧经 `set_reload_gate(true)` 置位(模拟战斗中);门未置位;reload 后新表校验失败 | `reload()` | 置位:返回 false、ConfigDB 值不变、不发 `config_reloaded`;未置位:返回 true 且新值经类型化属性可读且 `config_reloaded` 发出恰一次(仅 debug 构建);校验失败:旧表保留、状态停留 Ready(EC16) | 状态机表 / EC16 / 规则 6 | | AC-17 | 未设 master_seed | `open_stream` 或 `derive_at` | push_error + 冒烟通道断言中止路径(quit 非 0)(EC7) | 规则 6 / EC7 | | AC-18(deferred→首次 release 导出) | release 模板构建 | 致命校验错误发生;查找 override 源 | 表现符合"引擎默认错误路径"预期;override 结构性不存在(R3 边界④、校验双通道 release 侧) | 规则 6 / R3④ | ## 版本历史 | 版本 | 日期 | 改动 | |---|---|---| | v2 | 2026-09-14 | 两件套格式迁移:按模板 v2 骨架重排——头部收敛为状态/版本/一句话/推演件指针;设计推演(公式推导论证、数值锚定论证、候选取舍、体验锚点论证、已裁决争议)移至 `design/gdd/analysis/d2-data-config.md`;评审元数据与已闭环对账义务(#1/#2/#3/#12)入 analysis 附录;规则、数值、枚举、EC、AC、OQ 编号与内容零变更 | | 跨评审 D2 漂移批 | 2026-09-14 | /review-all-gdds 🔴-2 五处补漂移修订批(X-8 遗漏项补登,registry 同批):**RNG 流封闭清单 2→3 流**(encounter——消费者 B1,scope = 每场战斗实例化恰一 scope;Core Rule 5 / 取数链路 / 盐值 0x8EBC6AF09C88C6E3 / 黄金向量 +1 常量〔AC-3 五→六〕/ set_master_seed 全流零计数前提)· **pool_tag 枚举增 active_inject** · **RewardConfigTable +2 字段**(active_p0=0.4 / active_decay=0.5)+ k_dmg 默认 1→0 且"C3 禁区默认值"条款解除 · **默认根行集 clear_knife→clear_blast**(注入池占比注记替代 75/25)· **HelperItemTable 更名 AuxItemTable**(A5 定稿转正,schema 目录九→十张)· X-8 补登:SettlementTable 逐表字段行补 4 字段(共十二字段)、EnemyTable MVP 行三敌种登记 · 接口漂移修订规则增"回执按受影响字段/枚举粒度逐表 diff schema 目录"流程纪律;核心规则表增依据列(lint R8 引用列迁移) | | C3 修订批回写 | 2026-09-14 | /design-review C3 修订批(X-8 回执,registry 同批):**EnemyTable schema +2 字段**(unit_count_max——遭遇区间上界 / armor_type——护甲类型枚举 {none,a,b},克制矩阵挂 SettlementTable)· **SettlementTable +4 字段**(damage_a_per_cell=1 / damage_a_cap=8 / damage_b_per_flag=2 / armor_effectiveness 克制矩阵——护甲克制批转正,机制与结算归 B1、C3 投值)· DifficultyTable 表清单行补乘数域与 clamp 例外注记(registry 登记的组合事故例外,非 fail-fast 场景)· 敌种键更名 goblin/bugbear/ogre_chief(DND 批对齐) | | v1.0-lean | 2026-09-13 | 格式迁移:legacy 原稿按 lean 样例重排——修订注记出正文收敛至本表;核心规则 / 枚举 / 边界 / AC 转表格;加载校验与热重载链路转字符画;九张表 schema 逐表字段行入 Tuning Knobs;义务与夹具登记收敛为登记与对账义务表。正文残留旧称按 registry 与各 owner 成稿写现行值:SettlementTable 八字段口径(pool_clamp 拆 lo/hi)、p_perfect 载体域 {100,200}、source_label_map 键集含 hero_attack、RewardTable category 五值与默认根八行集——语义零变更 | | 两级奖励批 + 基础攻击裁决 | 2026-09-11 | RewardTable category 三值扩为五值(即时 / 可选两类交付轴)+ 默认根行集重登记八行(原五行为集退役);RewardConfigTable p_min 默认 4→8;HeroTable atk 改域 [1,999] 且 0 = 加载拒绝(基础攻击恒存在),消费面 = 主角战斗段先手攻击 | | B1 回填 + 评审批收编 | 2026-09-10 | SettlementTable 定稿回填(registry 原记七字段);同日评审批统一八字段口径——pool_clamp_lo_hi 拆 lo/hi 两字段、p_perfect 域 [100,999] 改载体域 {100,200}、source_label_map 键集补 enemy_action / flag_miss(hero_attack 随 B1 成稿) | | B4 full 评审 delta | 2026-09-09 | RewardTable 增 display_name(九字段)+ 默认根行集登记 + RewardConfigTable 新增(单行全局四字段,接口漂移——B4 标量 knob 的 Rule 1 载体)+ Core Rule 5 reward_roll 语义正文补同步(每次 board install 批) | | B4 回填定稿 | 2026-09-09 | RewardTable 八字段 + 跨字段校验族 + use_timing 占位转承重 + 首个 effect_id 引用回填激活 StatusEffectTable 悬空校验 + reward_roll scope 语义改写 + Tuning Knobs 奖励池占位条目清退 | | B2 回填定稿 | 2026-09-09 | EnemyTable 八字段 + 双敌种行 + 每行集恰一敌种校验;HeroTable 新增(接口漂移回写) | | A1 full 评审 delta | 2026-09-09 | BoardGenTable inferability_target 收窄 [0.85,1](P1 绝对锚)+ fog_target_ratio 绝对上限 min(8d, 0.25) + 布雷数下限 round(d×(W×H−9)) ≥ 1 + F2c 成本谓词(运行期禁墙钟逃生)+ elite_/boss_ 命名空间预留(第三轴登记约定,Boss=Dmax 行 + A1 OQ6 重审门) | | A1 回填定稿 | 2026-09-09 | BoardGenTable 九字段 + F2/F2b 跨字段谓词 + solver_rules 冻结 + MVP 参考行(provisional,C3 调参所有权)——首个进度键表 | | B3 回填定稿 | 2026-09-08 | StatusEffectTable schema 八字段 + 四行 MVP 内容(键 = effect_id,Rule 4 豁免注明;effect_id 悬空校验待首引激活) | | A4 re-review delta | 2026-09-08 | VariationTable 增跨字段校验 hide_numbers⇒safe_nonzero(加载期致命,夹具 variation_mask_constraint)+ counter_item_ids knob 误标修正(非 knob) | | A4 回填定稿 | 2026-09-08 | VariationTable 八字段 + fog 行 + Rule 4 豁免注明(registry 新条目 variation_table / variant_pool_gate) | | A3 三轮评审 delta | 2026-09-07 | ActionEconomyTable 增跨字段 validate 规则(任一动作成本 ≤ ap_per_round,schema 类承载、违规致命、条件性夹具登记) | | A3 RAP 结构修订 | 2026-09-07 | flags_per_round / flag_unit_cost / stop_multiplier / clear_multiplier 四字段删除(旗无上限、停手 / 清图折算乘数退役)、rap_refresh_cost 新增;AC-7 示例值域内修正(99 出域会使本 AC 假失败) | | A3 回填定稿 | 2026-09-07 | ActionEconomyTable 字段清单交付 + 成本三字段入表 + 冻结字段(ap_per_round=8 等定稿值登记 registry) | | 收口 | 2026-09-07 | 两轮评审 + delta 复核 5/5 PASS 后 Approved;CD-GDD-ALIGN 终裁 NEEDS REVISION → 修订落实 → 独立 delta 核对通过 | | 原型占位值清退 | 2026-09-07 起随各 owner 定稿 | 标中旗 +5 / 面旗(量纲与语义经 A3 OQ5 重锚 → weaken 行)、清图 +10(→ B4 清图奖励插槽)、增伤 1.5× / 减伤 0.5× / 持续 2 次结算(→ StatusEffectTable 四行)、奖励池 5 类(→ RewardTable 五值)——机制与数值事实登记归 owner GDD 与 registry |