# 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 豁免 # 定义表——RelicTable 先例)。 # 行字段:node_id / display_name / vein ∈ VEINS(spine=主干脊柱 row-gating,GP 买深度;forge=锻造脉 # 数值专精;keystone=钥石脉内容解锁;attune=同调脉槽位与规则)/ kind ∈ KINDS(stat=四线数值/ # rule=全局规则/unlock=内容解锁/socket=同调位/fork=A/B 互斥对/easter=彩蛋觉醒)/ # currency ∈ {gp, keystone, free}(双币铁律:GP 喂脊柱数值、钥石喂 Unlock/Socket/Fork——哈迪斯镜内 # 双币同构;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 := ["gp", "keystone", "free"] 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)