Files
gplan-2/game/data/schemas/settlement_table.gd

96 lines
5.6 KiB
GDScript
Raw Permalink 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
## 战斗结算表 schema:主表(段序骨架+两段子 Resource 嵌套)——结算全局旋钮(段序/克制矩阵/掷点档位)
## 单行全局类型化读取(垂直切片验证:完整战斗逐行归因)。主表只持段序骨架(3 列形):
## core=SettlementCoreTable(结算结构与封印溢价 7 列)/damage_roll=DamageRollTable(伤害掷点与护甲 10 列)。
## 结构冻结位(段序/取整/pool clamp/效果池映射)违例加载期致命(fail-fast)。
class_name SettlementTable
extends Resource
@export var segment_order: PackedStringArray = [] # 结构冻结——段序恒等校验违例致命(段序骨架留主表)
@export var core: SettlementCoreTable # 「结算结构与封印溢价」段(game/data/schemas/settlement_core_table.gd)
@export var damage_roll: DamageRollTable # 「伤害掷点与护甲」段(game/data/schemas/damage_roll_table.gd)
const ARMOR_KEYS := ["light", "ward", "heavy"]
const ATTACK_TYPE_KEYS := ["normal", "pierce", "magic"]
const FROZEN_SEGMENT_ORDER := ["grant", "buff_debuff", "combat", "decay", "verdict"]
const P_PERFECT_DOMAIN := [100, 200]
func validate() -> Array:
var errs: Array[Dictionary] = []
if core == null or damage_roll == null:
errs.append({"category": "missing_segment", "message": "core/damage_roll 段缺失——settlement 表须两段嵌套(core=%s, damage_roll=%s)" % [core != null, damage_roll != null]})
return errs
_validate_scalars(errs)
_validate_roll_scalars(errs)
_validate_frozen(errs)
_validate_tier_table(errs)
_validate_armor_matrix(errs)
return errs
# 标量域(当量参考敞口/完美封印载体/封印溢价/掷点流标签)
func _validate_scalars(errs: Array[Dictionary]) -> void:
if damage_roll.equivalence_ref_damage < 1 or damage_roll.equivalence_ref_damage > 999:
errs.append({"category": "domain", "message": "equivalence_ref_damage 越域 [1,999]: %d" % damage_roll.equivalence_ref_damage})
if not P_PERFECT_DOMAIN.has(core.p_perfect):
errs.append({"category": "pperfect_oob", "message": "p_perfect 载体域 {100,200}: %d" % core.p_perfect})
if core.seal_hit_premium < 100 or core.seal_hit_premium > 999:
errs.append({"category": "domain", "message": "seal_hit_premium 越域 [100,999](100=无溢价): %d" % core.seal_hit_premium})
if String(damage_roll.armor_roll_stream) == "":
errs.append({"category": "domain", "message": "armor_roll_stream 空"})
# 掷点标量域(骰面/暴击触发与倍率/伤害下限/攻击加成)
func _validate_roll_scalars(errs: Array[Dictionary]) -> void:
var dr := damage_roll
if dr.roll_faces < 2 or dr.roll_faces > 1000:
errs.append({"category": "domain", "message": "roll_faces 越域 [2,1000]: %d" % dr.roll_faces})
if dr.crit_roll < 1 or dr.crit_roll > dr.roll_faces:
errs.append({"category": "domain", "message": "crit_roll 越域 [1,roll_faces(%d)]: %d" % [dr.roll_faces, dr.crit_roll]})
if dr.crit_multiplier < 100 or dr.crit_multiplier > 999:
errs.append({"category": "domain", "message": "crit_multiplier 越域 [100,999]: %d" % dr.crit_multiplier})
if dr.damage_floor < 1 or dr.damage_floor > 999:
errs.append({"category": "domain", "message": "damage_floor 越域 [1,999]: %d" % dr.damage_floor})
if dr.attack_bonus < 0 or dr.attack_bonus > 999:
errs.append({"category": "domain", "message": "attack_bonus 越域 [0,999]: %d" % dr.attack_bonus})
# 结构冻结(段序恒等〔主表〕/取整规则与池钳位〔core 段〕)
func _validate_frozen(errs: Array[Dictionary]) -> void:
var seg := Array(segment_order)
if seg != FROZEN_SEGMENT_ORDER:
errs.append({"category": "segorder_violation", "message": "segment_order 违例——恒等拍板段序校验失败: %s" % str(seg)})
if core.round_direction != "floor":
errs.append({"category": "frozen", "message": "round_direction 结构冻结 floor: %s" % core.round_direction})
if core.pool_clamp_lo != -100 or core.pool_clamp_hi != 100:
errs.append({"category": "frozen", "message": "pool_clamp 域结构冻结 [-100,+100]"})
# 档位表形状:bounds 升序且 ∈ [1, faces-1];discounts 降序且 ∈ [1,99](无零档——非归零);
# 档数 = 边界数 + 1(缺口 [1,b0] → discounts[0],(b_i,b_{i+1}] → discounts[i+1],> b 末值 → 末位档)
func _validate_tier_table(errs: Array[Dictionary]) -> void:
var bounds := damage_roll.roll_tier_bounds
var discounts := damage_roll.roll_tier_discounts
if bounds.is_empty() or discounts.size() != bounds.size() + 1:
errs.append({"category": "cross_field", "message": "roll_tier_bounds/discounts 形状违例(档数须 = 边界数 + 1): %d 边界 vs %d 档" % [bounds.size(), discounts.size()]})
return
for i in bounds.size():
var b: int = int(bounds[i])
if b < 1 or b >= damage_roll.roll_faces or (i > 0 and b <= int(bounds[i - 1])):
errs.append({"category": "cross_field", "message": "roll_tier_bounds[%d] 越域/非升序: %d" % [i, b]})
var d: int = int(discounts[i])
if d < 1 or d > 99 or (i > 0 and d >= int(discounts[i - 1])):
errs.append({"category": "cross_field", "message": "roll_tier_discounts[%d] 越域/非降序(档位不设零档): %d" % [i, d]})
# 克制矩阵 3×3 满格域 [-3,+3](缺项回退 -99 即越域报错——键集满格结构冻结)
func _validate_armor_matrix(errs: Array[Dictionary]) -> void:
for atk_type in ATTACK_TYPE_KEYS:
var row: Dictionary[Variant, Variant] = damage_roll.armor_effectiveness.get(atk_type, {})
for armor in ARMOR_KEYS:
var pct: int = int(row.get(armor, -99))
if pct < -3 or pct > 3:
errs.append({"category": "domain", "message": "armor_effectiveness.%s.%s 越域 [-3,+3]: %d" % [atk_type, armor, pct]})