卡①T2(核心数据面结构收敛批): 校验族小表组+跨表校验器+解析器八文件拆分——board_gen 行/约束/收窄三域/level_gen_params 行头(skip)/形态/计数三域+层深尾段/meta_table 三行族域/skill_table 行头(skip)/公共/四类型域+表不变量/variation 行域+效果互锁+全局互锁/status_effect 行头+量纲域/cross_validator 七编号块域函数化(_cv_reward_dangling/_cv_reward_treasure/_cv_poolmap/_cv_source_labels/_cv_skill_pool/_cv_shelf_pool/_cv_counter_closure+decls/reads分层)+validate_skill_pool_links计数束化/resolver resolve_enemy_rowset三纯行扫描+_resolve_progress三段(坐标对束化);错误清单与告警序逐字,23键全清,data面13套件全绿
This commit is contained in:
1 parent
c56b816fee
commit
9646ce545d
8 files changed
+545
-321
No files matched your search
@@ -19,63 +19,92 @@ func validate() -> Array:
|
||||
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)
|
||||
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(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]})
|
||||
# 初始展开收窄两参数(加载期校验违例致命;缺省 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
|
||||
errs.append_array(_validate_row(row, levels, anchor_ok))
|
||||
errs.append_array(_validate_row_constraints(row))
|
||||
errs.append_array(_validate_shave_params(row))
|
||||
for L in anchor_ok.keys():
|
||||
if not anchor_ok[L]:
|
||||
errs.append({"category": "anchor", "message": "level %d 缺 depth-0 锚行(EC15)" % L})
|
||||
return errs
|
||||
|
||||
|
||||
## 行域:键唯一与进度/尺寸/密度/推理目标/solver 规则冻结(levels/anchor_ok 引用累计)
|
||||
func _validate_row(row: Dictionary, levels: Dictionary, anchor_ok: Dictionary) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
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]})
|
||||
anchor_ok[L] = anchor_ok.get(L, false) or D == 0
|
||||
return errs
|
||||
|
||||
|
||||
## 约束域:min_safe/生成尝试预算/布雷数下限与可行性/成本护栏/图类型词表
|
||||
func _validate_row_constraints(row: Dictionary) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var L: int = int(row.get("level_index", 0))
|
||||
var D: int = int(row.get("node_depth", -1))
|
||||
var w: int = int(row.get("board_width", 0))
|
||||
var h: int = int(row.get("board_height", 0))
|
||||
var d: float = float(row.get("mine_density", 0.0))
|
||||
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)
|
||||
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(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]})
|
||||
return errs
|
||||
|
||||
|
||||
## 初始展开收窄域(加载期校验违例致命;缺省 shave=1 现行投值 / cap=0 闸关防御口径)
|
||||
func _validate_shave_params(row: Dictionary) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var L: int = int(row.get("level_index", 0))
|
||||
var D: int = int(row.get("node_depth", -1))
|
||||
var w: int = int(row.get("board_width", 0))
|
||||
var h: int = int(row.get("board_height", 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]})
|
||||
return errs
|
||||
@@ -17,50 +17,13 @@ func validate() -> Array:
|
||||
var seen := {}
|
||||
var layers_by_level := {}
|
||||
for row in rows:
|
||||
var L: int = int(row.get("level_index", 0))
|
||||
var key := "level %d" % L
|
||||
if seen.has(L):
|
||||
errs.append({"category": "dup_key", "message": "LevelGenParams 键重复: %d" % L})
|
||||
seen[L] = true
|
||||
if L < 1:
|
||||
errs.append({"category": "domain", "message": "%s 关卡序号越域(≥1)" % key})
|
||||
var ks := (row as Dictionary).keys()
|
||||
ks.sort()
|
||||
var expect := ROW_KEYS.duplicate()
|
||||
expect.sort()
|
||||
if ks != expect:
|
||||
errs.append({"category": "structural", "message": "%s 字段键集非 ROW_KEYS 闭集" % key})
|
||||
var head := _validate_row_head(row, seen)
|
||||
errs.append_array(head["errs"])
|
||||
if not head["ok"]:
|
||||
continue
|
||||
var layers: int = int(row.get("layers", 0))
|
||||
if layers < 6:
|
||||
errs.append({"category": "domain", "message": "%s 层深 %d 低于下限 6(层深 L1≥6 已锁)" % [key, layers]})
|
||||
layers_by_level[L] = layers
|
||||
var bd: float = float(row.get("branch_density", -1.0))
|
||||
if bd < 0.0 or bd > 1.0:
|
||||
errs.append({"category": "domain", "message": "%s 分支密度 %f 越域 [0,1]" % [key, bd]})
|
||||
var ratio_sum := 0.0
|
||||
for rk in RATIO_KEYS:
|
||||
var v: float = float(row.get(rk, -1.0))
|
||||
if v < 0.0 or v > 1.0:
|
||||
errs.append({"category": "domain", "message": "%s %s=%f 越域 [0,1]" % [key, rk, v]})
|
||||
ratio_sum += v
|
||||
if not is_equal_approx(ratio_sum, 1.0):
|
||||
errs.append({"category": "domain", "message": "%s 配比四类合计 %f ≠ 1(已锁)" % [key, ratio_sum]})
|
||||
var entry_count: int = int(row.get("entry_count", 0))
|
||||
if entry_count < 1:
|
||||
errs.append({"category": "domain", "message": "%s 入口个数 %d 越域(≥1)" % [key, entry_count]})
|
||||
var exit_count: int = int(row.get("exit_count", 0))
|
||||
if exit_count < 1 or exit_count > 3:
|
||||
errs.append({"category": "domain", "message": "%s 出口个数 %d 越域 [1,3](已锁范围)" % [key, exit_count]})
|
||||
if L == 1 and exit_count != 1:
|
||||
errs.append({"category": "domain", "message": "%s 出口个数 %d ≠ 1(L1 出口恒 1 已锁)" % [key, exit_count]})
|
||||
var bf: int = int(row.get("battle_floor", -1))
|
||||
if bf < 0:
|
||||
errs.append({"category": "domain", "message": "%s 战斗保底 %d 越域(≥0)" % [key, bf]})
|
||||
var fmin: int = int(row.get("fork_ways_min", 0))
|
||||
var fmax: int = int(row.get("fork_ways_max", 0))
|
||||
if fmin != 2 or fmax != 3:
|
||||
errs.append({"category": "domain", "message": "%s 岔口去路数 (%d,%d) ≠ (2,3)(已锁)" % [key, fmin, fmax]})
|
||||
errs.append_array(_validate_row_shape(row, head["L"], head["key"], layers_by_level))
|
||||
errs.append_array(_validate_row_counts(row, head["L"], head["key"]))
|
||||
# 层深递进尾段(L1<L2<L3 且增幅变大——层深 L1≥6 递增增幅变大已锁)
|
||||
if layers_by_level.has(1) and layers_by_level.has(2) and layers_by_level.has(3):
|
||||
var d12: int = int(layers_by_level[2]) - int(layers_by_level[1])
|
||||
var d23: int = int(layers_by_level[3]) - int(layers_by_level[2])
|
||||
@@ -72,6 +35,68 @@ func validate() -> Array:
|
||||
return errs
|
||||
|
||||
|
||||
## 行头域:level_index 查重/关卡序号越域/字段键集闭集(ok=跳过行剩余判据)
|
||||
func _validate_row_head(row: Dictionary, seen: Dictionary) -> Dictionary:
|
||||
var errs: Array[Dictionary] = []
|
||||
var L: int = int(row.get("level_index", 0))
|
||||
var key := "level %d" % L
|
||||
if seen.has(L):
|
||||
errs.append({"category": "dup_key", "message": "LevelGenParams 键重复: %d" % L})
|
||||
seen[L] = true
|
||||
if L < 1:
|
||||
errs.append({"category": "domain", "message": "%s 关卡序号越域(≥1)" % key})
|
||||
var ks := (row as Dictionary).keys()
|
||||
ks.sort()
|
||||
var expect := ROW_KEYS.duplicate()
|
||||
expect.sort()
|
||||
if ks != expect:
|
||||
errs.append({"category": "structural", "message": "%s 字段键集非 ROW_KEYS 闭集" % key})
|
||||
return {"errs": errs, "L": L, "key": key, "ok": false}
|
||||
return {"errs": errs, "L": L, "key": key, "ok": true}
|
||||
|
||||
|
||||
## 行形态域:层深下限(layers_by_level 引用登记——层深递进尾段取数)/分支密度/配比四类合计 1
|
||||
func _validate_row_shape(row: Dictionary, L: int, key: String, layers_by_level: Dictionary) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var layers: int = int(row.get("layers", 0))
|
||||
if layers < 6:
|
||||
errs.append({"category": "domain", "message": "%s 层深 %d 低于下限 6(层深 L1≥6 已锁)" % [key, layers]})
|
||||
layers_by_level[L] = layers
|
||||
var bd: float = float(row.get("branch_density", -1.0))
|
||||
if bd < 0.0 or bd > 1.0:
|
||||
errs.append({"category": "domain", "message": "%s 分支密度 %f 越域 [0,1]" % [key, bd]})
|
||||
var ratio_sum := 0.0
|
||||
for rk in RATIO_KEYS:
|
||||
var v: float = float(row.get(rk, -1.0))
|
||||
if v < 0.0 or v > 1.0:
|
||||
errs.append({"category": "domain", "message": "%s %s=%f 越域 [0,1]" % [key, rk, v]})
|
||||
ratio_sum += v
|
||||
if not is_equal_approx(ratio_sum, 1.0):
|
||||
errs.append({"category": "domain", "message": "%s 配比四类合计 %f ≠ 1(已锁)" % [key, ratio_sum]})
|
||||
return errs
|
||||
|
||||
|
||||
## 行计数域:入口/出口(L1 恒 1)/战斗保底/岔口去路数锁值
|
||||
func _validate_row_counts(row: Dictionary, L: int, key: String) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var entry_count: int = int(row.get("entry_count", 0))
|
||||
if entry_count < 1:
|
||||
errs.append({"category": "domain", "message": "%s 入口个数 %d 越域(≥1)" % [key, entry_count]})
|
||||
var exit_count: int = int(row.get("exit_count", 0))
|
||||
if exit_count < 1 or exit_count > 3:
|
||||
errs.append({"category": "domain", "message": "%s 出口个数 %d 越域 [1,3](已锁范围)" % [key, exit_count]})
|
||||
if L == 1 and exit_count != 1:
|
||||
errs.append({"category": "domain", "message": "%s 出口个数 %d ≠ 1(L1 出口恒 1 已锁)" % [key, exit_count]})
|
||||
var bf: int = int(row.get("battle_floor", -1))
|
||||
if bf < 0:
|
||||
errs.append({"category": "domain", "message": "%s 战斗保底 %d 越域(≥0)" % [key, bf]})
|
||||
var fmin: int = int(row.get("fork_ways_min", 0))
|
||||
var fmax: int = int(row.get("fork_ways_max", 0))
|
||||
if fmin != 2 or fmax != 3:
|
||||
errs.append({"category": "domain", "message": "%s 岔口去路数 (%d,%d) ≠ (2,3)(已锁)" % [key, fmin, fmax]})
|
||||
return errs
|
||||
|
||||
|
||||
# 参数档单点读取(键 = level_index,缺行返回空字典——沿 level_row() 体例)
|
||||
func row_by_level_index(level_index: int) -> Dictionary:
|
||||
for row in rows:
|
||||
|
||||
@@ -18,7 +18,15 @@ func validate() -> Array:
|
||||
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(首杀全量、重复小额恒定)
|
||||
errs.append_array(_validate_boss_keystone())
|
||||
errs.append_array(_validate_conversion())
|
||||
errs.append_array(_validate_attune_refresh())
|
||||
return errs
|
||||
|
||||
|
||||
## boss_keystone 行族域:level_index 唯一 + 连续从 1 + 两档域 + first ≥ repeat(首杀全量、重复小额恒定)
|
||||
func _validate_boss_keystone() -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var seen_li := {}
|
||||
for row in boss_keystone:
|
||||
var li: int = int(row.get("level_index", 0))
|
||||
@@ -36,9 +44,13 @@ func validate() -> Array:
|
||||
if li_at != i + 1:
|
||||
errs.append({"category": "structural", "message": "boss_keystone level_index 不连续(须从 1 连续递增): 位 %d = %d" % [i + 1, li_at]})
|
||||
break
|
||||
# 无 workshop_items 行族(解锁混合制——条件谓词挂 treasure_items 行内;
|
||||
# 本表不校验秘宝解锁,单点=TreasureItemTable.validate)
|
||||
# conversion:attr 词表与 forge 一致 + 唯一 + 覆盖恰三 + 平移量域 [0,100]
|
||||
return errs
|
||||
|
||||
|
||||
## conversion 行族域(属性换算):attr 词表与 forge 一致 + 唯一 + 覆盖恰三 + 平移量域 [0,100]
|
||||
## (无 workshop_items 行族——解锁混合制:条件谓词挂 treasure_items 行内;本表不校验秘宝解锁,单点=TreasureItemTable.validate)
|
||||
func _validate_conversion() -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var seen_conv := {}
|
||||
for row in conversion:
|
||||
var cattr: String = row.get("attr", "")
|
||||
@@ -54,7 +66,12 @@ func validate() -> Array:
|
||||
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 域
|
||||
return errs
|
||||
|
||||
|
||||
## attune_refresh_upgrade 行族域(Y 刷新升级):level 唯一 + 连续从 1 + charges/cost 域
|
||||
func _validate_attune_refresh() -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var seen_al := {}
|
||||
for row in attune_refresh_upgrade:
|
||||
var al: int = int(row.get("level", -1))
|
||||
|
||||
@@ -31,73 +31,108 @@ func validate() -> Array:
|
||||
var seen := {}
|
||||
var has_basic := false
|
||||
for row in rows:
|
||||
var sid: String = row.get("skill_id", "")
|
||||
if sid == "":
|
||||
errs.append({"category": "domain", "message": "skill_id 缺失/空"})
|
||||
var head := _validate_row_head(row, seen)
|
||||
errs.append_array(head["errs"])
|
||||
if not head["ok"]:
|
||||
continue
|
||||
if seen.has(sid):
|
||||
errs.append({"category": "dup_key", "message": "skill_id 重复: %s" % sid})
|
||||
seen[sid] = true
|
||||
if String(row.get("display_name", "")) == "":
|
||||
errs.append({"category": "domain", "message": "%s display_name 空" % sid})
|
||||
var type: String = row.get("type", "")
|
||||
if not SKILL_TYPES.has(type):
|
||||
errs.append({"category": "enum", "message": "%s type 未知枚举: %s(域 %s)" % [sid, type, str(SKILL_TYPES)]})
|
||||
continue
|
||||
_validate_trigger(sid, row, errs) # trigger(触发条件,可省略——四键白名单/词表出域/类型分派/普攻禁配;加载期报错止动)
|
||||
if type == "basic" and row.has("trigger"):
|
||||
errs.append({"category": "cross_field", "message": "%s 普攻行禁配 trigger(普攻恒可摇的结构前提)" % sid})
|
||||
# 冷却域(全类型):施放后连续 N 拍不可摇中
|
||||
var cd: int = int(row.get("cooldown_beats", -1))
|
||||
if cd < 0 or cd > 99:
|
||||
errs.append({"category": "domain", "message": "%s cooldown_beats 越域 [0,99]: %d" % [sid, cd]})
|
||||
# 普通攻击两条域校验锁死特殊性:倍率恒 1、冷却恒 0——想配的就是伤害技(proposal §2.2)
|
||||
var sid: String = head["sid"]
|
||||
var type: String = head["type"]
|
||||
_validate_common_fields(sid, row, type, errs)
|
||||
if type == "basic":
|
||||
has_basic = true
|
||||
_validate_basic_row(sid, row, cd, errs)
|
||||
# 伤害技:存倍率不存固定值——伤害 = 施放者攻击基线 × 技能倍率(三乘法链第一乘);
|
||||
# 可选 rider_mult = 必中附加分量(后置加法、不吃骰不出暴击——proposal-status-vocabulary §2)
|
||||
_validate_basic_row(sid, row, int(row.get("cooldown_beats", -1)), errs)
|
||||
if type == "damage":
|
||||
if not row.has("damage_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 伤害技缺 damage_mult(存倍率不存固定值)" % sid})
|
||||
else:
|
||||
var m: float = float(row.damage_mult)
|
||||
if m < MULT_DOMAIN_MIN or m > MULT_DOMAIN_MAX:
|
||||
errs.append({"category": "domain", "message": "%s damage_mult 越域 [%.1f, %.1f]: %s" % [sid, MULT_DOMAIN_MIN, MULT_DOMAIN_MAX, str(m)]})
|
||||
if row.has("buff_ref") or row.has("buff_duration"):
|
||||
errs.append({"category": "cross_field", "message": "%s 伤害技不带增益字段" % sid})
|
||||
if row.has("rider_mult"):
|
||||
var rm: float = float(row.rider_mult)
|
||||
if rm <= 0.0 or rm > RIDER_MULT_MAX:
|
||||
errs.append({"category": "domain", "message": "%s rider_mult 越域 (0, %.1f]: %s" % [sid, RIDER_MULT_MAX, str(rm)]})
|
||||
# 增益技:0 伤害动作(无倍率),效果引用 + 持续回合;悬空引用由 ConfigDB 跨表校验兜底
|
||||
_validate_damage_row(sid, row, errs)
|
||||
if type == "buff":
|
||||
if row.has("damage_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 增益技为 0 伤害动作,不带 damage_mult" % sid})
|
||||
if row.has("rider_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 增益技不带必中附加分量(rider_mult 仅伤害技)" % sid})
|
||||
if String(row.get("buff_ref", "")) == "":
|
||||
errs.append({"category": "cross_field", "message": "%s 增益技缺 buff_ref(效果引用)" % sid})
|
||||
var bd: int = int(row.get("buff_duration", 0))
|
||||
if bd < 1 or bd > 99:
|
||||
errs.append({"category": "domain", "message": "%s buff_duration 越域 [1,99]: %d" % [sid, bd]})
|
||||
# 妨害技:0 伤害施放动作,对主角施加负面状态——
|
||||
# 字段复用 buff_ref/buff_duration(落点由状态行 target_side 路由,ConfigDB side 守卫兜底);池纪律妨害 ≤1 在跨表通道
|
||||
_validate_buff_row(sid, row, errs)
|
||||
if type == "debuff":
|
||||
if row.has("damage_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 妨害技为 0 伤害动作,不带 damage_mult" % sid})
|
||||
if row.has("rider_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 妨害技不带必中附加分量(rider_mult 仅伤害技)" % sid})
|
||||
if String(row.get("buff_ref", "")) == "":
|
||||
errs.append({"category": "cross_field", "message": "%s 妨害技缺 buff_ref(效果引用)" % sid})
|
||||
var dbd: int = int(row.get("buff_duration", 0))
|
||||
if dbd < 1 or dbd > 99:
|
||||
errs.append({"category": "domain", "message": "%s buff_duration 越域 [1,99]: %d" % [sid, dbd]})
|
||||
_validate_debuff_row(sid, row, errs)
|
||||
_validate_table_invariants(has_basic, errs)
|
||||
return errs
|
||||
|
||||
|
||||
## 行头域:skill_id 非空与查重/display_name/type 枚举(ok=跳过行剩余判据)
|
||||
func _validate_row_head(row: Dictionary, seen: Dictionary) -> Dictionary:
|
||||
var errs: Array[Dictionary] = []
|
||||
var sid: String = row.get("skill_id", "")
|
||||
if sid == "":
|
||||
errs.append({"category": "domain", "message": "skill_id 缺失/空"})
|
||||
return {"errs": errs, "sid": sid, "type": "", "ok": false}
|
||||
if seen.has(sid):
|
||||
errs.append({"category": "dup_key", "message": "skill_id 重复: %s" % sid})
|
||||
seen[sid] = true
|
||||
return {"errs": errs, "sid": sid, "type": "", "ok": false}
|
||||
seen[sid] = true
|
||||
if String(row.get("display_name", "")) == "":
|
||||
errs.append({"category": "domain", "message": "%s display_name 空" % sid})
|
||||
var type: String = row.get("type", "")
|
||||
if not SKILL_TYPES.has(type):
|
||||
errs.append({"category": "enum", "message": "%s type 未知枚举: %s(域 %s)" % [sid, type, str(SKILL_TYPES)]})
|
||||
return {"errs": errs, "sid": sid, "type": type, "ok": false}
|
||||
return {"errs": errs, "sid": sid, "type": type, "ok": true}
|
||||
|
||||
|
||||
## 公共域(全类型):trigger 触发条件校验/普攻行禁配 trigger/冷却域
|
||||
func _validate_common_fields(sid: String, row: Dictionary, type: String, errs: Array[Dictionary]) -> void:
|
||||
_validate_trigger(sid, row, errs) # trigger(触发条件,可省略——四键白名单/词表出域/类型分派/普攻禁配;加载期报错止动)
|
||||
if type == "basic" and row.has("trigger"):
|
||||
errs.append({"category": "cross_field", "message": "%s 普攻行禁配 trigger(普攻恒可摇的结构前提)" % sid})
|
||||
# 冷却域(全类型):施放后连续 N 拍不可摇中
|
||||
var cd: int = int(row.get("cooldown_beats", -1))
|
||||
if cd < 0 or cd > 99:
|
||||
errs.append({"category": "domain", "message": "%s cooldown_beats 越域 [0,99]: %d" % [sid, cd]})
|
||||
|
||||
|
||||
## 伤害技域:存倍率不存固定值——伤害 = 施放者攻击基线 × 技能倍率(三乘法链第一乘);
|
||||
## 可选 rider_mult = 必中附加分量(后置加法、不吃骰不出暴击——proposal-status-vocabulary §2)
|
||||
func _validate_damage_row(sid: String, row: Dictionary, errs: Array[Dictionary]) -> void:
|
||||
if not row.has("damage_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 伤害技缺 damage_mult(存倍率不存固定值)" % sid})
|
||||
else:
|
||||
var m: float = float(row.damage_mult)
|
||||
if m < MULT_DOMAIN_MIN or m > MULT_DOMAIN_MAX:
|
||||
errs.append({"category": "domain", "message": "%s damage_mult 越域 [%.1f, %.1f]: %s" % [sid, MULT_DOMAIN_MIN, MULT_DOMAIN_MAX, str(m)]})
|
||||
if row.has("buff_ref") or row.has("buff_duration"):
|
||||
errs.append({"category": "cross_field", "message": "%s 伤害技不带增益字段" % sid})
|
||||
if row.has("rider_mult"):
|
||||
var rm: float = float(row.rider_mult)
|
||||
if rm <= 0.0 or rm > RIDER_MULT_MAX:
|
||||
errs.append({"category": "domain", "message": "%s rider_mult 越域 (0, %.1f]: %s" % [sid, RIDER_MULT_MAX, str(rm)]})
|
||||
|
||||
|
||||
## 增益技域:0 伤害动作(无倍率),效果引用 + 持续回合;悬空引用由 ConfigDB 跨表校验兜底
|
||||
func _validate_buff_row(sid: String, row: Dictionary, errs: Array[Dictionary]) -> void:
|
||||
if row.has("damage_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 增益技为 0 伤害动作,不带 damage_mult" % sid})
|
||||
if row.has("rider_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 增益技不带必中附加分量(rider_mult 仅伤害技)" % sid})
|
||||
if String(row.get("buff_ref", "")) == "":
|
||||
errs.append({"category": "cross_field", "message": "%s 增益技缺 buff_ref(效果引用)" % sid})
|
||||
var bd: int = int(row.get("buff_duration", 0))
|
||||
if bd < 1 or bd > 99:
|
||||
errs.append({"category": "domain", "message": "%s buff_duration 越域 [1,99]: %d" % [sid, bd]})
|
||||
|
||||
|
||||
## 妨害技域:0 伤害施放动作,对主角施加负面状态——
|
||||
## 字段复用 buff_ref/buff_duration(落点由状态行 target_side 路由,ConfigDB side 守卫兜底);池纪律妨害 ≤1 在跨表通道
|
||||
func _validate_debuff_row(sid: String, row: Dictionary, errs: Array[Dictionary]) -> void:
|
||||
if row.has("damage_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 妨害技为 0 伤害动作,不带 damage_mult" % sid})
|
||||
if row.has("rider_mult"):
|
||||
errs.append({"category": "cross_field", "message": "%s 妨害技不带必中附加分量(rider_mult 仅伤害技)" % sid})
|
||||
if String(row.get("buff_ref", "")) == "":
|
||||
errs.append({"category": "cross_field", "message": "%s 妨害技缺 buff_ref(效果引用)" % sid})
|
||||
var dbd: int = int(row.get("buff_duration", 0))
|
||||
if dbd < 1 or dbd > 99:
|
||||
errs.append({"category": "domain", "message": "%s buff_duration 越域 [1,99]: %d" % [sid, dbd]})
|
||||
|
||||
|
||||
## 表不变量域:普攻行存在性(池构成纪律的结构前提)与保底旋钮值域
|
||||
func _validate_table_invariants(has_basic: bool, errs: Array[Dictionary]) -> void:
|
||||
if not has_basic:
|
||||
errs.append({"category": "structural", "message": "SkillTable 无普攻行(basic)——池构成纪律的结构前提"})
|
||||
if pity_threshold < 1 or pity_threshold > 99:
|
||||
errs.append({"category": "domain", "message": "pity_threshold 越域 [1,99]: %d" % pity_threshold})
|
||||
return errs
|
||||
|
||||
|
||||
# trigger 域校验(敌人系统 v1.2 触发条件:四键白名单/词表出域/类型分派——枚举事实算子限域 eq/neq
|
||||
|
||||
@@ -18,30 +18,12 @@ func validate() -> Array:
|
||||
return errs
|
||||
var seen := {}
|
||||
for row in rows:
|
||||
var eid: String = row.get("effect_id", "")
|
||||
if eid == "":
|
||||
errs.append({"category": "domain", "message": "effect_id 缺失/空"})
|
||||
var head := _validate_row_head(row, seen)
|
||||
errs.append_array(head["errs"])
|
||||
if not head["ok"]:
|
||||
continue
|
||||
if seen.has(eid):
|
||||
errs.append({"category": "dup_key", "message": "effect_id 重复: %s" % eid})
|
||||
seen[eid] = true
|
||||
if String(row.get("display_name", "")) == "":
|
||||
errs.append({"category": "domain", "message": "%s display_name 空" % eid})
|
||||
if not TARGET_SIDES.has(row.get("target_side", "")):
|
||||
errs.append({"category": "enum", "message": "%s target_side 出域" % eid})
|
||||
var unit: String = row.get("unit", "")
|
||||
if not UNITS.has(unit):
|
||||
errs.append({"category": "enum", "message": "%s unit 出域" % eid})
|
||||
if not STACK_POLICIES.has(row.get("stack_policy", "")):
|
||||
errs.append({"category": "enum", "message": "%s stack_policy 出域" % eid})
|
||||
var m: int = int(row.get("magnitude", 0))
|
||||
# 跨字段:percent ∈ [-100,100] 且 ≠0 / flat ≥ 1 / ac ∈ [-20,20] 且 ≠0(AC 阈值轴——幅度域独立,防呆上限 20)
|
||||
if unit == "percent" and (m == 0 or m < -100 or m > 100):
|
||||
errs.append({"category": "cross_field", "message": "%s percent magnitude 域错: %d" % [eid, m]})
|
||||
if unit == "flat" and m < 1:
|
||||
errs.append({"category": "cross_field", "message": "%s flat magnitude 须 ≥1: %d" % [eid, m]})
|
||||
if unit == "ac" and (m == 0 or m < -20 or m > 20):
|
||||
errs.append({"category": "cross_field", "message": "%s ac magnitude 域错([-20,20] 且 ≠0): %d" % [eid, m]})
|
||||
var eid: String = head["eid"]
|
||||
errs.append_array(_validate_magnitude_domain(row, eid, head["unit"]))
|
||||
var dur: int = int(row.get("duration_settlements", 0))
|
||||
if dur < 1 or dur > 99:
|
||||
errs.append({"category": "domain", "message": "%s duration_settlements 越域 [1,99]: %d" % [eid, dur]})
|
||||
@@ -51,6 +33,44 @@ func validate() -> Array:
|
||||
return errs
|
||||
|
||||
|
||||
## 行头域:effect_id 非空与查重/display_name/target_side/unit/stack_policy 词表
|
||||
func _validate_row_head(row: Dictionary, seen: Dictionary) -> Dictionary:
|
||||
var errs: Array[Dictionary] = []
|
||||
var eid: String = row.get("effect_id", "")
|
||||
if eid == "":
|
||||
errs.append({"category": "domain", "message": "effect_id 缺失/空"})
|
||||
return {"errs": errs, "eid": eid, "unit": "", "ok": false}
|
||||
if seen.has(eid):
|
||||
errs.append({"category": "dup_key", "message": "effect_id 重复: %s" % eid})
|
||||
seen[eid] = true
|
||||
return {"errs": errs, "eid": eid, "unit": "", "ok": false}
|
||||
seen[eid] = true
|
||||
if String(row.get("display_name", "")) == "":
|
||||
errs.append({"category": "domain", "message": "%s display_name 空" % eid})
|
||||
if not TARGET_SIDES.has(row.get("target_side", "")):
|
||||
errs.append({"category": "enum", "message": "%s target_side 出域" % eid})
|
||||
var unit: String = row.get("unit", "")
|
||||
if not UNITS.has(unit):
|
||||
errs.append({"category": "enum", "message": "%s unit 出域" % eid})
|
||||
if not STACK_POLICIES.has(row.get("stack_policy", "")):
|
||||
errs.append({"category": "enum", "message": "%s stack_policy 出域" % eid})
|
||||
return {"errs": errs, "eid": eid, "unit": unit, "ok": true}
|
||||
|
||||
|
||||
## 幅度量纲域(三段量纲判据——percent/flat/ac 各自值域;
|
||||
## ac = AC 阈值轴效果,非池百分比非定点值——幅度域独立,防呆上限 20)
|
||||
func _validate_magnitude_domain(row: Dictionary, eid: String, unit: String) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
var m: int = int(row.get("magnitude", 0))
|
||||
if unit == "percent" and (m == 0 or m < -100 or m > 100):
|
||||
errs.append({"category": "cross_field", "message": "%s percent magnitude 域错: %d" % [eid, m]})
|
||||
if unit == "flat" and m < 1:
|
||||
errs.append({"category": "cross_field", "message": "%s flat magnitude 须 ≥1: %d" % [eid, m]})
|
||||
if unit == "ac" and (m == 0 or m < -20 or m > 20):
|
||||
errs.append({"category": "cross_field", "message": "%s ac magnitude 域错([-20,20] 且 ≠0): %d" % [eid, m]})
|
||||
return errs
|
||||
|
||||
|
||||
func row_by_id(effect_id: String) -> Dictionary:
|
||||
for row in rows:
|
||||
if row.get("effect_id", "") == effect_id:
|
||||
|
||||
@@ -26,57 +26,82 @@ func validate() -> Array:
|
||||
var errs: Array[Dictionary] = []
|
||||
var seen := {}
|
||||
for row in rows:
|
||||
var vid: String = row.get("variant_id", "")
|
||||
if vid == "":
|
||||
errs.append({"category": "domain", "message": "variant_id 缺失/空"})
|
||||
var head := _validate_row(row, seen)
|
||||
errs.append_array(head["errs"])
|
||||
if not head["ok"]:
|
||||
continue
|
||||
if seen.has(vid):
|
||||
errs.append({"category": "dup_key", "message": "variant_id 重复: %s" % vid})
|
||||
var vid: String = head["vid"]
|
||||
errs.append_array(_validate_effect_interlocks(row, vid))
|
||||
errs.append_array(_validate_global_interlocks(row, vid))
|
||||
return errs
|
||||
|
||||
|
||||
## 行域:variant_id 非空与查重/display_name/unlock_level/四词表(scope/effect/trigger/placement)
|
||||
func _validate_row(row: Dictionary, seen: Dictionary) -> Dictionary:
|
||||
var errs: Array[Dictionary] = []
|
||||
var vid: String = row.get("variant_id", "")
|
||||
if vid == "":
|
||||
errs.append({"category": "domain", "message": "variant_id 缺失/空"})
|
||||
return {"errs": errs, "vid": vid, "ok": false}
|
||||
if seen.has(vid):
|
||||
errs.append({"category": "dup_key", "message": "variant_id 重复: %s" % vid})
|
||||
seen[vid] = true
|
||||
if String(row.get("display_name", "")) == "":
|
||||
errs.append({"category": "domain", "message": "%s display_name 空" % vid})
|
||||
var ul: int = int(row.get("unlock_level", 0))
|
||||
if ul < 1 or ul > 999:
|
||||
errs.append({"category": "domain", "message": "%s unlock_level 越域 [1,999]: %d" % [vid, ul]})
|
||||
if not SCOPES.has(row.get("scope", "")):
|
||||
errs.append({"category": "enum", "message": "%s scope 出域: %s" % [vid, row.get("scope")]})
|
||||
if not EFFECTS.has(row.get("effect", "")):
|
||||
errs.append({"category": "enum", "message": "%s effect 出域: %s" % [vid, row.get("effect")]})
|
||||
if not TRIGGERS.has(row.get("trigger", "")):
|
||||
errs.append({"category": "enum", "message": "%s trigger 出域: %s" % [vid, row.get("trigger")]})
|
||||
if not PLACEMENTS.has(row.get("placement", "")):
|
||||
errs.append({"category": "enum", "message": "%s placement 出域: %s" % [vid, row.get("placement")]})
|
||||
# 跨字段互锁(加载期致命——枚举语义一致性立闸):
|
||||
# 伤害型 = 挖开即伤 ⇒ 触发 on_open + 只落未开格(closed_only)+ 伤害量为加法核小整数
|
||||
if row.get("effect", "") == "flat_damage":
|
||||
if row.get("trigger", "") != "on_open":
|
||||
errs.append({"category": "cross_field", "message": "%s flat_damage ⇒ trigger 必为 on_open" % vid})
|
||||
if row.get("placement", "") != "closed_only":
|
||||
errs.append({"category": "cross_field", "message": "%s flat_damage ⇒ placement 必为 closed_only(只出现在未打开的格子)" % vid})
|
||||
var ev: int = int(row.get("effect_value", 0))
|
||||
if ev < 1 or ev > 9:
|
||||
errs.append({"category": "domain", "message": "%s effect_value 越域 [1,9](加法核小整数): %d" % [vid, ev]})
|
||||
# 信息型 = 被打开且显示数字时起作用 ⇒ 触发 on_number_shown
|
||||
if row.get("effect", "") == "hide_number" and row.get("trigger", "") != "on_number_shown":
|
||||
errs.append({"category": "cross_field", "message": "%s hide_number ⇒ trigger 必为 on_number_shown" % vid})
|
||||
# 信息遮蔽型只允许落安全格(遮蔽对象是数字;危险格无数字可遮)——placement=any 时由读取方
|
||||
# 呈现面按 kind=safe 自然短路,此处不设布置约束
|
||||
# 全局作用域型互锁(resource_face ⟺ scope=global 三条 + 新值域;
|
||||
# 判定键=effect/scope 词值,既有六 region 行(flat_damage/hide_number)不入本组——零误伤)
|
||||
if row.get("effect", "") == "resource_face":
|
||||
if row.get("scope", "") != "global":
|
||||
errs.append({"category": "cross_field", "message": "%s resource_face ⇒ scope 必为 global(整图作用域)" % vid})
|
||||
# resource_face 行 effect_value = 生存族权重下调百分比(0=中性合法态——off 锚用)
|
||||
var gev: int = int(row.get("effect_value", 0))
|
||||
if gev < 0 or gev > 50:
|
||||
errs.append({"category": "domain", "message": "%s effect_value 越域 [0,50](资源面偏斜百分比): %d" % [vid, gev]})
|
||||
if row.get("scope", "") == "global":
|
||||
if row.get("effect", "") != "resource_face":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ effect 必为 resource_face" % vid})
|
||||
if row.get("placement", "") != "any":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ placement 必为 any(整图无格子布置域)" % vid})
|
||||
if row.get("trigger", "") != "always":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ trigger 必为 always(整图恒在,无触发时机)" % vid})
|
||||
return {"errs": errs, "vid": vid, "ok": false}
|
||||
seen[vid] = true
|
||||
if String(row.get("display_name", "")) == "":
|
||||
errs.append({"category": "domain", "message": "%s display_name 空" % vid})
|
||||
var ul: int = int(row.get("unlock_level", 0))
|
||||
if ul < 1 or ul > 999:
|
||||
errs.append({"category": "domain", "message": "%s unlock_level 越域 [1,999]: %d" % [vid, ul]})
|
||||
if not SCOPES.has(row.get("scope", "")):
|
||||
errs.append({"category": "enum", "message": "%s scope 出域: %s" % [vid, row.get("scope")]})
|
||||
if not EFFECTS.has(row.get("effect", "")):
|
||||
errs.append({"category": "enum", "message": "%s effect 出域: %s" % [vid, row.get("effect")]})
|
||||
if not TRIGGERS.has(row.get("trigger", "")):
|
||||
errs.append({"category": "enum", "message": "%s trigger 出域: %s" % [vid, row.get("trigger")]})
|
||||
if not PLACEMENTS.has(row.get("placement", "")):
|
||||
errs.append({"category": "enum", "message": "%s placement 出域: %s" % [vid, row.get("placement")]})
|
||||
return {"errs": errs, "vid": vid, "ok": true}
|
||||
|
||||
|
||||
## 效果互锁域(加载期致命——枚举语义一致性立闸):
|
||||
## 伤害型 = 挖开即伤 ⇒ 触发 on_open + 只落未开格(closed_only)+ 伤害量为加法核小整数;
|
||||
## 信息型 = 被打开且显示数字时起作用 ⇒ 触发 on_number_shown
|
||||
## (信息遮蔽型只允许落安全格——遮蔽对象是数字,危险格无数字可遮;placement=any 时由读取方
|
||||
## 呈现面按 kind=safe 自然短路,此处不设布置约束)
|
||||
func _validate_effect_interlocks(row: Dictionary, vid: String) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
if row.get("effect", "") == "flat_damage":
|
||||
if row.get("trigger", "") != "on_open":
|
||||
errs.append({"category": "cross_field", "message": "%s flat_damage ⇒ trigger 必为 on_open" % vid})
|
||||
if row.get("placement", "") != "closed_only":
|
||||
errs.append({"category": "cross_field", "message": "%s flat_damage ⇒ placement 必为 closed_only(只出现在未打开的格子)" % vid})
|
||||
var ev: int = int(row.get("effect_value", 0))
|
||||
if ev < 1 or ev > 9:
|
||||
errs.append({"category": "domain", "message": "%s effect_value 越域 [1,9](加法核小整数): %d" % [vid, ev]})
|
||||
if row.get("effect", "") == "hide_number" and row.get("trigger", "") != "on_number_shown":
|
||||
errs.append({"category": "cross_field", "message": "%s hide_number ⇒ trigger 必为 on_number_shown" % vid})
|
||||
return errs
|
||||
|
||||
|
||||
## 全局作用域互锁域(resource_face ⟺ scope=global;判定键=effect/scope 词值,
|
||||
## 既有六 region 行(flat_damage/hide_number)不入本组——零误伤);
|
||||
## resource_face 行 effect_value = 生存族权重下调百分比(0=中性合法态——off 锚用)
|
||||
func _validate_global_interlocks(row: Dictionary, vid: String) -> Array[Dictionary]:
|
||||
var errs: Array[Dictionary] = []
|
||||
if row.get("effect", "") == "resource_face":
|
||||
if row.get("scope", "") != "global":
|
||||
errs.append({"category": "cross_field", "message": "%s resource_face ⇒ scope 必为 global(整图作用域)" % vid})
|
||||
var gev: int = int(row.get("effect_value", 0))
|
||||
if gev < 0 or gev > 50:
|
||||
errs.append({"category": "domain", "message": "%s effect_value 越域 [0,50](资源面偏斜百分比): %d" % [vid, gev]})
|
||||
if row.get("scope", "") == "global":
|
||||
if row.get("effect", "") != "resource_face":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ effect 必为 resource_face" % vid})
|
||||
if row.get("placement", "") != "any":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ placement 必为 any(整图无格子布置域)" % vid})
|
||||
if row.get("trigger", "") != "always":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ trigger 必为 always(整图恒在,无触发时机)" % vid})
|
||||
return errs
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user