61 KiB
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 的信任底线。
- 设计师"改数字不改代码"——全部影响手感的数字(AP、旗、折算比例、奖励值)集中一处可调,热重载即改即验,"这轮玩起来不对劲"能快速修正且不引入新毛刺。
- 开发与测试"同决策必同结果"——RNG 统一种子化,相同决策序列必然相同战斗结果,回放 / 审计 / 自动化测试可复现。
- 难度"越下越险由结构保证"——数值表天生带(关卡序号, 节点深度)两维,难度曲线可审计、可回溯,而非散落各处的魔法数字。
- 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 先行)
规则:
- 规则 3(结构先行、数值回填):全部数值表按"结构 + 数据"两件套交付——结构 = data/schemas/ 下一个自定义 Resource 类(每字段带类型、范围、默认值和注释),数据 = data/tables/ 下数据文件;D2 先交付全部 schema 与占位表(含键结构),具体数值由 owner 系统 GDD 回填,先有格子、后填数字。 举证:无计算。十张已回填表 schema 目录见本节,逐表字段行见调参表节。
- 规则 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 |
加载·校验·热重载
规则:
- 规则 6(出错当场中止):D2 在加载与运行期发现数据出错一律致命中止,不做软回退——错误数值上线比崩溃更伤(P1/P2 信任底线);错误分两类处置(下表)。唯一例外:debug 热重载校验失败时保留旧表停留 Ready、给出错误清单,不杀运行中的调试会话。 举证:未播种流取数见边界表 EC7;请求越域见难度行解析前置守卫。两类构建分流见本节「校验双通道」。
- 规则 7(零界面依赖):D2 自己的代码与测试一律不 import 任何 Control/场景类——纯数据层不碰表现;没有界面依赖,CI 与无窗口环境才能跑完整战斗。 举证:无计算。验收 = 静态扫描 + headless 实跑两项合成判定(AC-10)。
- 规则 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(数字只写在表里):凡是设计师要调的数字(行动点、插旗成本、奖励值、敌人数值……)只存在于 data/tables/ 下的 .tres 数值表里,一处定义、处处引用;代码文件里禁止出现玩法数值字面量(引擎/工程常量除外),评审发现写死的玩法数值即评审阻塞项。 举证:无计算。违规检测 = 启发式扫描 + 人工复核(AC-14,随各 owner 回填定稿与里程碑 gate-check 执行)。
- 规则 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)
规则:
- 规则 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 最小闭环
- D2 交付全部 schema 类与占位表(含键结构)——先有格子。
- owner 系统 GDD 按设计顺序回填数值(十张已定稿 + 两张占位)。
- 进程启动:
ConfigDB.load_tables()逐一加载 + schemavalidate(),经校验双通道。 - 全部通过 → Ready:19 系统经类型化属性读数,无第二取数路径。
- C2 冒险开始
set_master_seed();三条封闭流按各自 scope 事件取号。 - debug 构建热重载即改即验(战斗中被门拒绝,结束后手动再触发)。
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_<biome> 命名空间预留拒绝;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 |