Files
gplan-2/game/data/schemas/meta_table.gd
T

90 lines
6.0 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VERTICAL SLICE - NOT FOR PRODUCTION
# Validation Question: 玩家能否在营地用钥石换永久强化、且强化在下一局开局生效——存档变厚成为玩家可感的现实
#(元进度 GDD 验收标准,本批 = 其实现批)
# Date: 2026-09-21(元进度实现批——设计权威 design/gdd/systems/冒险循环/元进度.md 定稿 v1.2)
# 数据配置 MetaTable schema(元进度行族)。
# 行族三轴(数据契约对齐 ConfigDB.meta_* 访问器——消费面一律走访问器,禁直读行集):
# boss_keystone = Boss 钥石两档行(level_index 连续从 1;first_kill ≥ repeat_kill——首杀全量、
# 重复杀固定小额恒定不递减,两档数值归元进度设计批配读数)。
# conversion = 区间平移换算率行(attr 与三属性轴词表一致恰三;per_level_shift_pct ∈ [0,100]——
# registry 键 meta_progression_conversion_table;护甲行强条目:验收读数必须按解锁后参数重测)。
# attune_refresh_upgrade = Y 刷新升级行族(迭代 11 第①批 T4——P1 甲:每冒险开局给 Y 次、冒险周期池、
# 用完即无;level 连续从 1,等级 L → refresh_charges=Y 表值,钥石直购 keystone_cost;数值全 provisional)。
# 〔forge_products 属性锻造行族已退役——迭代 21 T5 攻防所有权切分:生命唯一入口=树脊柱、攻/AC
# 唯一入口=锻造工坊路线;行族、访问器与购买面随批删除,历史投值记载归 git 考古。〕
# 〔workshop_items 道具工坊行族已退役——迭代 22 T2 秘宝实装批:解锁换代混合制(钥石购买价
# unlock_keystone_cost + 条件谓词 unlock_requires 全挂 treasure_items.tres 行内,单一事实源),
# 旧「总通关次数达阈值」渠道行族、WORKSHOP_ITEM_IDS/WORKSHOP_CHANNELS 词表与校验段随批删除。〕
# 行值全部 provisional 归 元进度设计批配读数(用户拍板骨架先行,先例 = GP+商店房批)。
class_name MetaTable
extends Resource
@export var boss_keystone: Array[Dictionary] = [] # Array[Dictionary] 键 level_index
@export var conversion: Array[Dictionary] = [] # Array[Dictionary] 键 attr
@export var attune_refresh_upgrade: Array[Dictionary] = [] # Array[Dictionary] 键 level——Y 刷新升级行族(迭代 11 第①批 T4)
const FORGE_ATTRS := ["max_hp", "atk", "armor_class"] # 三属性轴词表(conversion 行键域——原 forge 词表名沿用作轴名)
func validate() -> Array:
var errs: Array[Dictionary] = []
# 行族空护栏(三族皆为 ConfigDB 消费面——缺族即致命)
for family in ["boss_keystone", "conversion", "attune_refresh_upgrade"]:
if get(family).is_empty():
errs.append({"category": "empty_table", "message": "MetaTable 行族为空: %s" % family})
# boss_keystone:level_index 唯一 + 连续从 1 + 两档域 + first ≥ repeat(首杀全量、重复小额恒定)
var seen_li := {}
for row in boss_keystone:
var li: int = int(row.get("level_index", 0))
if seen_li.has(li):
errs.append({"category": "dup_key", "message": "boss_keystone level_index 重复: %d" % li})
seen_li[li] = true
var first: int = int(row.get("first_kill", -1))
var rep: int = int(row.get("repeat_kill", -1))
if first < 1 or first > 999 or rep < 0 or rep > 999:
errs.append({"category": "domain", "message": "boss_keystone L%d 两档金额越域 first [1,999] / repeat [0,999]: %d/%d" % [li, first, rep]})
if first < rep:
errs.append({"category": "cross_field", "message": "boss_keystone L%d 违反 first_kill(%d) ≥ repeat_kill(%d)——首杀全量、重复杀小额" % [li, first, rep]})
for i in boss_keystone.size():
var li_at: int = int(boss_keystone[i].get("level_index", 0))
if li_at != i + 1:
errs.append({"category": "structural", "message": "boss_keystone level_index 不连续(须从 1 连续递增): 位 %d = %d" % [i + 1, li_at]})
break
# workshop_items 行族已退役(迭代 22 T2——解锁混合制换代,条件谓词挂 treasure_items 行内;
# 校验段随批删除——秘宝解锁条件校验单点=TreasureItemTable.validate)
# conversion:attr 词表与 forge 一致 + 唯一 + 覆盖恰三 + 平移量域 [0,100]
var seen_conv := {}
for row in conversion:
var cattr: String = row.get("attr", "")
if not FORGE_ATTRS.has(cattr):
errs.append({"category": "enum", "message": "conversion attr 出域 {max_hp, atk, armor_class}: %s" % cattr})
if seen_conv.has(cattr):
errs.append({"category": "dup_key", "message": "conversion attr 重复: %s" % cattr})
seen_conv[cattr] = true
var shift: int = int(row.get("per_level_shift_pct", -1))
if shift < 0 or shift > 100:
errs.append({"category": "domain", "message": "conversion %s per_level_shift_pct 越域 [0,100]: %d" % [cattr, shift]})
if not conversion.is_empty():
for conv_key in FORGE_ATTRS:
if not seen_conv.has(conv_key):
errs.append({"category": "coverage", "message": "conversion 缺属性行: %s(三行与 forge 词表一致)" % conv_key})
# attune_refresh_upgrade:level 唯一 + 连续从 1 + charges/cost 域(迭代 11 第①批 T4)
var seen_al := {}
for row in attune_refresh_upgrade:
var al: int = int(row.get("level", -1))
if seen_al.has(al):
errs.append({"category": "dup_key", "message": "attune_refresh_upgrade level 重复: %d" % al})
seen_al[al] = true
var ach: int = int(row.get("refresh_charges", -1))
if ach < 1 or ach > 9:
errs.append({"category": "domain", "message": "attune_refresh_upgrade L%d refresh_charges 越域 [1,9]: %d" % [al, ach]})
var acost: int = int(row.get("keystone_cost", -1))
if acost < 1 or acost > 999:
errs.append({"category": "domain", "message": "attune_refresh_upgrade L%d keystone_cost 越域 [1,999]: %d" % [al, acost]})
for i in attune_refresh_upgrade.size():
var al_at: int = int(attune_refresh_upgrade[i].get("level", 0))
if al_at != i + 1:
errs.append({"category": "structural", "message": "attune_refresh_upgrade level 不连续(须从 1 连续递增): 位 %d = %d" % [i + 1, al_at]})
break
return errs