96 lines
5.6 KiB
GDScript
96 lines
5.6 KiB
GDScript
## 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]})
|