一、里程碑语义立规: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 等)未带
120 lines
6.7 KiB
GDScript
120 lines
6.7 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Validation Question: 三旧体系(锻造/钥石/同调)收敛为一棵营地天赋树后,双币消费、row-gating、
|
||
# A/B 免费切换、彩蛋觉醒能否机械承载(P0——迭代 12 批次二 T8,R1 甲融合案)
|
||
# Date: 2026-10-04(双层 BD 实施批 T8;设计权威 双层BD体系-整合定稿-2026-10-03 §1.4)
|
||
# 数据配置 MetaTreeTable schema(元进度 回填 owner 批末落文;行 = Dictionary 键 node_id,Rule 4 豁免
|
||
# 定义表——FeatTable 先例)。
|
||
# 行字段:node_id / display_name / vein ∈ VEINS(spine=主干脊柱 row-gating,钥石买深度〔2026-10-07
|
||
# 用户裁甲——树全钥石单币,原双币铁律骨架披露销账〕;forge=锻造脉
|
||
# 数值专精;keystone=钥石脉内容解锁;attune=同调脉槽位与规则)/ kind ∈ KINDS(stat=四线数值/
|
||
# rule=全局规则/unlock=内容解锁/socket=同调位/fork=A/B 互斥对/easter=彩蛋觉醒)/
|
||
# currency ∈ {keystone, free}(树全钥石单币——2026-10-07 用户裁甲;free=迁移点亮与彩蛋)/
|
||
# price [0,9999] / cap [1,9](等级上限;fork/unlock 恒 1)/
|
||
# row(脊柱行号 1..N——row-gating:行 N 可购条件 = 全树已购等级总和 ≥ SPINE_ROW_GATE×(N-1))/
|
||
# requires Array[node_id](显式前置——父开子脉内链)/ effects Dictionary(stat 四线 flat:
|
||
# atk/max_hp/hit/armor_class;rule 键封闭 RULE_KEYS)/ fork_group/fork_side(a/b——同组互斥、
|
||
# 营地免费切换)。
|
||
# 🔴 层级纪律(需求卡片红线 2):局外树只放全局规则件——effects 禁流派标签(relic 特性永不入树,
|
||
# archetype 键出域=加载致命);规则改写型特性只走局内随机渠道。
|
||
# A/B 只放规则面(定稿 1.3——全树互斥面 ≤10-15% 首期 4 组);两侧「不同强非强弱」MC 验差 <~3pp(T10)。
|
||
# 彩蛋 ≤3(定稿 1.4——点满某脉某排免费觉醒;发现率 <5% 改灰色预告——T10 追认单呈报)。
|
||
class_name MetaTreeTable
|
||
extends Resource
|
||
|
||
@export var rows: Array = [] # Array[Dictionary] 键 node_id
|
||
|
||
const VEINS := ["spine", "forge", "keystone", "attune"]
|
||
const KINDS := ["stat", "rule", "unlock", "socket", "fork", "easter"]
|
||
const CURRENCIES := ["keystone", "free"] # 树全钥石单币(2026-10-07 用户裁甲——原 gp 枚举值退役,字段保留为呈现元数据)
|
||
const STAT_KEYS := ["atk", "max_hp", "hit", "armor_class"]
|
||
## 规则键封闭词表(零裸 flat 同族护栏——effects.rule 键出域=加载致命;首期两键=T8 Fork 面真实接线)
|
||
const RULE_KEYS := ["posture_first_strike", "posture_bulwark"]
|
||
const KEY_WHITELIST := ["node_id", "display_name", "vein", "kind", "currency", "price", "cap",
|
||
"row", "requires", "effects", "fork_group", "fork_side", "note"]
|
||
## 脊柱行门槛(row-gating:行 N 可购需全树已购总等级 ≥ GATE×(N−1)——「投入点数累计解锁新行」)
|
||
const SPINE_ROW_GATE := 4
|
||
|
||
## A/B 组词表(同组恰两侧 a/b——互斥对;营地免费切换承载面)
|
||
const FORK_GROUPS := ["fg_posture", "fg_sense", "fg_economy", "fg_mastery"]
|
||
|
||
|
||
func validate() -> Array:
|
||
var errs: Array = []
|
||
if rows.is_empty():
|
||
return errs # 空表合法态(T8 建表先行——内容随迁移批落)
|
||
var seen := {}
|
||
var fork_seen := {}
|
||
for row in rows:
|
||
var nid: String = row.get("node_id", "")
|
||
if nid == "":
|
||
errs.append({"category": "domain", "message": "node_id 空"})
|
||
continue
|
||
if seen.has(nid):
|
||
errs.append({"category": "dup_key", "message": "node_id 重复: %s" % nid})
|
||
continue
|
||
seen[nid] = true
|
||
if String(row.get("display_name", "")) == "":
|
||
errs.append({"category": "domain", "message": "%s display_name 空" % nid})
|
||
if not VEINS.has(String(row.get("vein", ""))):
|
||
errs.append({"category": "enum", "message": "%s vein 出域" % nid})
|
||
var kind := String(row.get("kind", ""))
|
||
if not KINDS.has(kind):
|
||
errs.append({"category": "enum", "message": "%s kind 出域" % nid})
|
||
if not CURRENCIES.has(String(row.get("currency", ""))):
|
||
errs.append({"category": "enum", "message": "%s currency 出域" % nid})
|
||
for k in row.keys():
|
||
if not KEY_WHITELIST.has(String(k)):
|
||
errs.append({"category": "zero_flat", "message": "%s 行键出白名单: %s" % [nid, String(k)]})
|
||
var cap := int(row.get("cap", 1))
|
||
if cap < 1 or cap > 9:
|
||
errs.append({"category": "domain", "message": "%s cap 越域 [1,9]: %d" % [nid, cap]})
|
||
if kind in ["unlock", "socket", "fork", "easter"] and cap != 1:
|
||
errs.append({"category": "cross_field", "message": "%s %s 行 cap 恒 1" % [nid, kind]})
|
||
var price := int(row.get("price", 0))
|
||
if price < 0 or price > 9999:
|
||
errs.append({"category": "domain", "message": "%s price 越域 [0,9999]: %d" % [nid, price]})
|
||
var fx: Dictionary = row.get("effects", {}) as Dictionary
|
||
for ek in fx.keys():
|
||
if not (STAT_KEYS.has(String(ek)) or RULE_KEYS.has(String(ek))):
|
||
errs.append({"category": "zero_flat", "message": "%s effects 键出域(四线/rule 词表封闭): %s" % [nid, String(ek)]})
|
||
# 🔴 层级纪律:局外树禁规则改写型特性(archetype 键出域=加载致命)
|
||
if fx.has("archetype"):
|
||
errs.append({"category": "load_bearing", "message": "%s effects 含 archetype(🔴 层级纪律——特性禁入局外树)" % nid})
|
||
# A/B 互斥对结构:fork 行必带 fork_group+side;同组恰 a/b 各一
|
||
if kind == "fork":
|
||
var fg := String(row.get("fork_group", ""))
|
||
if not FORK_GROUPS.has(fg):
|
||
errs.append({"category": "enum", "message": "%s fork_group 出域" % nid})
|
||
var side := String(row.get("fork_side", ""))
|
||
if side != "a" and side != "b":
|
||
errs.append({"category": "enum", "message": "%s fork_side 出域: %s" % [nid, side]})
|
||
var fk := fg + "|" + side
|
||
if fork_seen.has(fk):
|
||
errs.append({"category": "dup_key", "message": "%s fork 对重复: %s" % [nid, fk]})
|
||
fork_seen[fk] = true
|
||
# requires 悬空引用
|
||
for row in rows:
|
||
for req in (row.get("requires", []) as Array):
|
||
if not seen.has(String(req)):
|
||
errs.append({"category": "dangling", "message": "%s requires 悬空: %s" % [String(row.get("node_id", "")), String(req)]})
|
||
# 彩蛋 ≤3
|
||
var easter := 0
|
||
for row in rows:
|
||
if String(row.get("kind", "")) == "easter":
|
||
easter += 1
|
||
if easter > 3:
|
||
errs.append({"category": "load_bearing", "message": "彩蛋节点 %d > 3(定稿 §1.4 上限)" % easter})
|
||
return errs
|
||
|
||
|
||
func row_by_id(node_id: String) -> Dictionary:
|
||
for row in rows:
|
||
if String(row.get("node_id", "")) == node_id:
|
||
return row
|
||
return {}
|
||
|
||
|
||
## row-gating 门槛(行 N 可购需全树总等级 ≥ GATE×(N−1);非脊柱节点跟随其 vein 父行——requires 主导)
|
||
func row_gate(row_num: int) -> int:
|
||
return SPINE_ROW_GATE * maxi(0, row_num - 1)
|