一、里程碑语义立规:M3=Alpha 完整实现/M4=Beta 完整实现,未明令关闭 M3 前只排 M3 需求(roadmap M-4 节头立规注+M-3 节头口径指针+迭代 21+ 行注+06 倍率差条注);迭代 20/21/22 规划冻结(roadmap 迭代 20 行注——先解决眼前问题,重启=用户直接发令) 二、文档正确性审查批三波:全 GDD 29 份过审——状态记录按代码证据订正/里程碑删前缀(M-x+迭代N→迭代N,裸 M-4→Beta 阶段(未排期))/退役词退役件当现役清零/在役旧实现节加隔离横幅;重件=秘宝.md v1.7 老正文深层清算(横幅重写消「过渡形态已退役」误读/沿革节换代/六件旧件登记摘除/验收与 charges 矛盾订正)+奇物 v3.8+冒险结构 v0.18+关卡节点地图 v1.7+图生成 v0.7+难度曲线与Boss v0.26+战斗结算 v0.27+敌人系统 v0.16+地图战斗资源 v0.21+增益状态 v0.13+战斗表现 v0.6+元进度 v1.9+生涯 v0.3+00 v1.3+02 v1.16+03 v1.1+ux 八份(battle 23 处/camp/shop/node-reward/level-clear/interaction-patterns/settlement-summary/level-map) 三、去毒批:奇物 v3.7(魔法道具→秘宝/档名三档→四级/门票与刷新「尚未入表」假话订正/验收第 3 条改令牌口径/资源成员指路凭证件表)+秘宝 v1.6(速览旧目录退役标注/退役件示例换现役) 四、道具退役素材库新建(design/research/道具退役素材库.md,案乙已追认):秘宝退役 9 件含去向+专长退役 27 件名单+待核 4 行;魔法提灯(fog_lantern)裁退役(06 条目已裁+秘宝.md 注记换代+素材库迁表一;拆线归实装批) 五、B2/B3/B4 裁决落文:元进度.md 道具工坊行删旧写新(v1.10——旧六行登记作废、指路秘宝新目录+解锁门槛谓词分级);奇物.md P1「两项合成高阶项」注销(v3.9——用户定性脏信息);§3 刷新句按最新口径换代(刷新券替代、不进持有集) 六、天赋树独立成文(B1 裁决):新建 design/gdd/systems/跨局循环/天赋树.md v0.1(33 节点机制全按 meta_tree.tres 实态+迭代 12 落籍裁决+被删底稿 git 取证——脊柱行门槛/钥石单币/Fork A/B 面/存档 v5 域/注入双通道)+元进度.md 定名节换代(v1.11)+02-系统清单跨局循环区加行(v1.17) 七、A 类卫生包:版本历史表理排四份(秘宝/奇物含 v2.7 断裂行修复/敌人系统/图生成,只调行序)+缺号注记三处(增益状态 v0.6 未考/冒险结构 v0.15/关卡节点地图 v1.6=MC-1 批)+node-reward v2.2 双号改 a/b+07 补建版本历史表+06 删「设置与本地化」空节+「文档/结构闸存量欠账」块挪文末「文档与结构」节+「L1 商店编排」两悬空指针改指 GP 换算失衡条+难度曲线两处过期指向核销注记(v0.27)+B5「现役 UI/文案两处自相冲突」登记 06 待办(battle_narrator.gd:160 旧话/hero_info_panel 两段同标「特性」——随后续迭代修) 八、命名统一(用户令「任务书」全库改「需求卡片」):167 文件约 800 处(design/ 全部文档+roadmap+CLAUDE.md+active.md+entities.yaml+game/ 注释与数据批注);两文件改名(需求卡片-M4数值批/需求卡片-冒险前资源配比预设批);残留=自造短语全量清单(黑话证据文档故意跳过) 九、CLAUDE.md:批次工作流标准清单收口段新增第 12 条「文档同步闸」(需求卡片必列关联文档同步面、合并前同步完毕方算收口,原 12/13 顺延);呈报六问铁规适用范围补立(前序批随批落籍) 十、active.md 快照节+工作日志 414-421(并行会话 416 黑话清扫条目按共享工作日志惯例一并入库);06 中并行会话「active.md 定性与裁剪」条目未提交,按零吞并纪律留在工作区未入库 零测试(文档批两入口铁规);主区前序脏件(.gitignore/.zcode/config.json/playnote/project.godot/.zcodeignore/美术审计报告 html 等)未带
84 lines
4.9 KiB
GDScript
84 lines
4.9 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2)
|
||
# Date: 2026-09-10
|
||
# 数据配置 BoardGenTable schema(棋盘生成 回填值;键 (level_index, node_depth)——首个进度键表)。
|
||
# 依据 design/gdd/systems/排雷循环/棋盘生成.md "BoardGenTable Schema 定稿" + F2/F2b/F2c 谓词。
|
||
class_name BoardGenTable
|
||
extends Resource
|
||
|
||
@export var rows: Array[Dictionary] = [] # Array[Dictionary]
|
||
const SOLVER_RULES := ["single", "single_subset"]
|
||
const COST_BUDGET := 16384 # F2c provisional 量级护栏(禁墙钟逃生)
|
||
const TARGET_ABS_FLOOR := 0.85 # 战斗结算 修订——target 域下限(P1 绝对锚)
|
||
const BOARD_TYPE_VOCABULARY := ["dungeon", "underdark", "deep_underdark"] # 图类型词表 v1.1(2026-09-20 L3 内容批 +deep_underdark 灰矮人矿道;玩家侧显示名「地图类型」)
|
||
|
||
|
||
func validate() -> Array:
|
||
var errs: Array[Dictionary] = []
|
||
if rows.is_empty():
|
||
errs.append({"category": "empty_table", "message": "BoardGenTable 为空"})
|
||
return errs
|
||
var levels := {}
|
||
var anchor_ok := {}
|
||
for row in rows:
|
||
var L: int = int(row.get("level_index", 0))
|
||
var D: int = int(row.get("node_depth", -1))
|
||
var key := "%d_%d" % [L, D]
|
||
if levels.has(key):
|
||
errs.append({"category": "dup_key", "message": "BoardGen 键重复: %s" % key})
|
||
levels[key] = true
|
||
if L < 1:
|
||
errs.append({"category": "domain", "message": "level_index 须 ≥1"})
|
||
if D < 0:
|
||
errs.append({"category": "domain", "message": "node_depth 须 ≥0"})
|
||
var w: int = int(row.get("board_width", 0))
|
||
var h: int = int(row.get("board_height", 0))
|
||
if w < 8 or w > 64 or h < 8 or h > 64:
|
||
errs.append({"category": "domain", "message": "(%d,%d) 尺寸越域 [8,64]" % [L, D]})
|
||
var d: float = float(row.get("mine_density", 0.0))
|
||
if d <= 0.0 or d > 0.5:
|
||
errs.append({"category": "domain", "message": "(%d,%d) mine_density 越域 (0,0.5]" % [L, D]})
|
||
var t: float = float(row.get("inferability_target", 0.0))
|
||
if t < TARGET_ABS_FLOOR or t > 1.0:
|
||
errs.append({"category": "domain", "message": "(%d,%d) inferability_target 越域 [0.85,1](P1 绝对锚)" % [L, D]})
|
||
if not SOLVER_RULES.has(row.get("solver_rules", "")):
|
||
errs.append({"category": "enum", "message": "(%d,%d) solver_rules 出域" % [L, D]})
|
||
if row.get("solver_rules", "") != "single_subset":
|
||
errs.append({"category": "frozen", "message": "(%d,%d) solver_rules 冻结 single_subset" % [L, D]})
|
||
var ms: int = int(row.get("min_safe_count", 0))
|
||
if ms < 1 or ms > 999:
|
||
errs.append({"category": "domain", "message": "(%d,%d) min_safe_count 越域 [1,999]" % [L, D]})
|
||
var att: int = int(row.get("max_gen_attempts", 0))
|
||
if att < 1 or att > 32:
|
||
errs.append({"category": "domain", "message": "(%d,%d) max_gen_attempts 越域 [1,32]" % [L, D]})
|
||
# 布雷数下限(R3)——零雷板拒绝(−9 = 出生区格数,常量单点 BoardData.SPAWN_AREA_CELLS——2026-10-02 修复批)
|
||
var n_mines := int(round(d * float(w * h - BoardData.SPAWN_AREA_CELLS)))
|
||
if n_mines < 1:
|
||
errs.append({"category": "cross_field", "message": "(%d,%d) 布雷数下限违例 round(d×(W×H−9)) ≥ 1" % [L, D]})
|
||
# F2b min_safe 可行性
|
||
if w * h - n_mines < ms:
|
||
errs.append({"category": "cross_field", "message": "(%d,%d) min_safe_feasible 违例" % [L, D]})
|
||
# F2c 成本可行性
|
||
if att * w * h > COST_BUDGET:
|
||
errs.append({"category": "cross_field", "message": "(%d,%d) cost_feasible 违例 attempts×W×H ≤ %d" % [L, D, COST_BUDGET]})
|
||
# 图类型词表 v1.0(2026-09-19 用户拍板定稿:dungeon=地牢基线 / underdark=幽暗地域;扩词随内容批走修订)
|
||
var bt: String = str(row.get("board_type", ""))
|
||
if not BOARD_TYPE_VOCABULARY.has(bt):
|
||
errs.append({"category": "cross_field", "message": "(%d,%d) board_type 越词表: %s" % [L, D, bt]})
|
||
# 初始展开收窄批两参数(迭代14① T1——加载期校验违例致命;缺省 shave=1 现行投值 / cap=0 闸关防御口径)
|
||
var sr: int = int(row.get("shave_ring_radius", 1))
|
||
if sr < 0 or sr > 2:
|
||
errs.append({"category": "domain", "message": "(%d,%d) shave_ring_radius 越域 [0,2]: %d" % [L, D, sr]})
|
||
var r0_cap: int = int(row.get("r0_max_cap", 0))
|
||
if r0_cap > 0:
|
||
# 护栏(需求卡片 §二):cap ≥ 出生格数 9(BoardData.SPAWN_AREA_CELLS)、cap ≤ 板格数一半
|
||
if r0_cap < BoardData.SPAWN_AREA_CELLS:
|
||
errs.append({"category": "cross_field", "message": "(%d,%d) r0_max_cap 低于出生保障面 %d: %d" % [L, D, BoardData.SPAWN_AREA_CELLS, r0_cap]})
|
||
if r0_cap > (w * h) / 2:
|
||
errs.append({"category": "cross_field", "message": "(%d,%d) r0_max_cap 超板格数一半 %d: %d" % [L, D, (w * h) / 2, r0_cap]})
|
||
anchor_ok[L] = anchor_ok.get(L, false) or D == 0
|
||
for L in anchor_ok.keys():
|
||
if not anchor_ok[L]:
|
||
errs.append({"category": "anchor", "message": "level %d 缺 depth-0 锚行(EC15)" % L})
|
||
return errs
|