卡①T4c(拆件批wr5/wr9/V12): settlement 拆两子 Resource 段——新建 settlement_core_table.gd(7列:source_label_map/effect_pool_map/pool_clamp_lo/pool_clamp_hi/round_direction/p_perfect/seal_hit_premium)+damage_roll_table.gd(10列:equivalence_ref_damage/armor_effectiveness/roll_faces/attack_bonus/roll_tier_bounds/roll_tier_discounts/crit_roll/crit_multiplier/damage_floor/armor_roll_stream)+.uid边车;主表三列形(segment_order留主表+core+damage_roll,类变量18→3,const四枚随主表),validate拆五私有(_validate_scalars/_validate_roll_scalars(9分支超阈续拆)/_validate_frozen/_validate_tier_table/_validate_armor_matrix)错误载荷逐字同构+段缺失fail-fast;settlement.tres单文件嵌套重建(两sub_resource,18列值与尾注批注史逐字—脚本比对18/18无diff+tail_equal);消费方改址97处(.settlement.<列>→.settlement.core./damage_roll. 链式95处+cross_validator局部2处,逐规则计数断言全中;segment_order直读1处零改址);测试改址9文件含G15清单(sim/probe/config_db交叉校验)。验收:gdstyle基线外0 NEW+16套件924 PASS+零SCRIPT ERROR〔拆件批 V12〕

This commit is contained in:
DKChan committed 2026-10-27 07:41:09 +08:00
1 parent bc7a171e3d
commit e2a2fbf3ee
23 files changed
+221 -160

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 战斗结算表「伤害掷点与护甲」段 schema:当量参考敞口、攻击类型×护甲克制矩阵、护甲等级掷点
## 与伤害下限共 10 列纯数据载体(由 SettlementTable.damage_roll 持有;零函数零随机零 await)。
## 域校验由主表 validate 分面执行(fail-fast)。
class_name DamageRollTable
extends Resource
@export var equivalence_ref_damage: int = 12 # [1,999] knob——当量参考敞口 D_ref
# ---- 攻击类型 × 护甲克制 + 充能奖励定义(数值 provisional)----
# 主角攻击 = 普通(atk×(1+attack_up),不吃克制)+ 穿刺 + 魔法(后两者由充能供给),三分量各自 floor 后相加。
# 【两条抽象分离,勿焊死】攻击类型 = 伤害的属性轴,只决定查 armor_effectiveness 哪一行,定义里不含任何棋盘来源;
# 充能 = 按攻击类型分桶的附加伤害储备,"谁往桶里放东西"属**奖励的定义**(开格/标雷只是奖励触发源,奖励内容可改)。
# 无 damage_a_per_cell / damage_a_cap / damage_b_per_seal 与 charge_grants 字段(攻击模型=单分量)。
@export var armor_effectiveness: Dictionary[String, Variant] = {} # 克制修正矩阵(加法点数 delta):{攻击类型 {normal|pierce|magic}: {护甲 {light|ward|heavy}: 点数 [-3,+3]}}
# ---- 护甲等级掷点(数字即规范——registry settlement_table.ac_roll/ac_discount_tiers 定稿)----
# 判定式:掷 1d20,掷点 ≥ 防守方 AC + 攻击加成 → 全额;低掷按缺口(AC−掷点)查档打折,无 miss、无零收益档;
# nat 最大面(crit_roll)= 暴击:该次基础攻击分量 ×2 且恒全额;任何伤害落地全局下限 damage_floor(≥1)——
# 全来源(攻击双方/棋盘直伤/地形变异/反噬,取值域 [1,+∞) 无上限)。
# 掷点只吃基础攻击分量(主角普通分量 / 敌方攻击拍与重击拍基线),穿刺/魔法充能与棋盘来源伤害不掷不折。
# 微调旋钮 = 档位边界(roll_tier_bounds)与档位值(roll_tier_discounts),不动 AC 值(armor_class 归单位表)。
@export var roll_faces: int = 20 # [2,1000] knob——掷点骰面(d20)
@export var attack_bonus: int = 0 # [0,999] 结构位——攻击者掷点加成,切片恒 0(元进度/武器附魔未来投值)
@export var roll_tier_bounds: Array[int] = [] # 缺口档边界升序:缺口 ≥ bounds[i] 落入第 i 档([3,7] = 缺口1-3/4-7/8+ 三档)
@export var roll_tier_discounts: Array[int] = [] # 档位值 percent 降序(与 bounds 同长;[75,50,25] = ×0.75/×0.5/×0.25)
@export var crit_roll: int = 20 # [1,roll_faces] knob——nat 最大面 = 暴击触发掷点(恒全额)
@export var crit_multiplier: int = 200 # [100,999] percent knob——暴击倍率(200 = ×2,只乘该次基础攻击分量)
@export var damage_floor: int = 1 # [1,999] knob——任何伤害落地全局下限·全来源(无"被减伤归零 v=0"语义)
@export var armor_roll_stream: String = "armor_roll" # 掷点用途标签(rng_service PURPOSE_TAGS 成员——不参与派生,仅作事件日志用途标记)
@@ -0,0 +1 @@
uid://ddd6kga4tcivx
@@ -0,0 +1,14 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 战斗结算表「结算结构与封印溢价」段 schema:结构冻结位(段序外的取值域/池钳位/取整/效果池映射)
## 与封印溢价旋钮共 7 列纯数据载体(由 SettlementTable.core 持有;零函数零随机零 await)。
## 结构冻结位(pool clamp/效果池映射/取整)违例加载期致命(fail-fast)。
class_name SettlementCoreTable
extends Resource
@export var source_label_map: Dictionary[String, Variant] = {} # 结构冻结——source_class→文案键
@export var effect_pool_map: Dictionary[String, Variant] = {} # 结构冻结——effect_id→pool(悬空加载致命)
@export var pool_clamp_lo: int = -100 # 结构冻结
@export var pool_clamp_hi: int = 100 # 结构冻结
@export var round_direction: String = "floor" # 结构冻结——单一取整规则
@export var p_perfect: int = 200 # {100,200} percent——完美封印第二份兑付触发(载体域)
@export var seal_hit_premium: int = 100 # [100,999] percent knob——封印命中兑付溢价 ×(1+(n−100)/100):125=×1.25(条款3 条件二封印溢价形态 a,provisional);100=无溢价直通零扰动。只乘封印命中正向兑付(flat power/percent 幅度/gp 缓冲三形态),标空/挖格/清图不乘;雷安同池冻结不动
@@ -0,0 +1 @@
uid://swk6u2opk6ix
+71 -65
View File
@@ -1,38 +1,14 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 战斗结算表 schema:结算全局旋钮(段序/克制矩阵/掷点档位)单行全局类型化读取(垂直切片验证:完整战斗逐行归因)。
## 战斗结算表 schema:主表(段序骨架+两段子 Resource 嵌套)——结算全局旋钮(段序/克制矩阵/掷点档位)
## 单行全局类型化读取(垂直切片验证:完整战斗逐行归因)。主表只持段序骨架(3 列形):
## core=SettlementCoreTable(结算结构与封印溢价 7 列)/damage_roll=DamageRollTable(伤害掷点与护甲 10 列)。
## 结构冻结位(段序/取整/pool clamp/效果池映射)违例加载期致命(fail-fast)。
class_name SettlementTable
extends Resource
@export var equivalence_ref_damage: int = 12 # [1,999] knob——当量参考敞口 D_ref
@export var p_perfect: int = 200 # {100,200} percent——完美封印第二份兑付触发(载体域)
@export var seal_hit_premium: int = 100 # [100,999] percent knob——封印命中兑付溢价 ×(1+(n−100)/100):125=×1.25(条款3 条件二封印溢价形态 a,provisional);100=无溢价直通零扰动。只乘封印命中正向兑付(flat power/percent 幅度/gp 缓冲三形态),标空/挖格/清图不乘;雷安同池冻结不动
@export var segment_order: PackedStringArray = [] # 结构冻结——段序恒等校验违例致命
@export var source_label_map: Dictionary[String, Variant] = {} # 结构冻结——source_class→文案键
@export var effect_pool_map: Dictionary[String, Variant] = {} # 结构冻结——effect_id→pool(悬空加载致命)
@export var pool_clamp_lo: int = -100 # 结构冻结
@export var pool_clamp_hi: int = 100 # 结构冻结
@export var round_direction: String = "floor" # 结构冻结——单一取整规则
# ---- 攻击类型 × 护甲克制 + 充能奖励定义(数值 provisional)----
# 主角攻击 = 普通(atk×(1+attack_up),不吃克制)+ 穿刺 + 魔法(后两者由充能供给),三分量各自 floor 后相加。
# 【两条抽象分离,勿焊死】攻击类型 = 伤害的属性轴,只决定查 armor_effectiveness 哪一行,定义里不含任何棋盘来源;
# 充能 = 按攻击类型分桶的附加伤害储备,"谁往桶里放东西"属**奖励的定义**(开格/标雷只是奖励触发源,奖励内容可改)。
# 无 damage_a_per_cell / damage_a_cap / damage_b_per_seal 与 charge_grants 字段(攻击模型=单分量)。
@export var armor_effectiveness: Dictionary[String, Variant] = {} # 克制修正矩阵(加法点数 delta):{攻击类型 {normal|pierce|magic}: {护甲 {light|ward|heavy}: 点数 [-3,+3]}}
# ---- 护甲等级掷点(数字即规范——registry settlement_table.ac_roll/ac_discount_tiers 定稿)----
# 判定式:掷 1d20,掷点 ≥ 防守方 AC + 攻击加成 → 全额;低掷按缺口(AC−掷点)查档打折,无 miss、无零收益档;
# nat 最大面(crit_roll)= 暴击:该次基础攻击分量 ×2 且恒全额;任何伤害落地全局下限 damage_floor(≥1)——
# 全来源(攻击双方/棋盘直伤/地形变异/反噬,取值域 [1,+∞) 无上限)。
# 掷点只吃基础攻击分量(主角普通分量 / 敌方攻击拍与重击拍基线),穿刺/魔法充能与棋盘来源伤害不掷不折。
# 微调旋钮 = 档位边界(roll_tier_bounds)与档位值(roll_tier_discounts),不动 AC 值(armor_class 归单位表)。
@export var roll_faces: int = 20 # [2,1000] knob——掷点骰面(d20)
@export var attack_bonus: int = 0 # [0,999] 结构位——攻击者掷点加成,切片恒 0(元进度/武器附魔未来投值)
@export var roll_tier_bounds: Array[int] = [] # 缺口档边界升序:缺口 ≥ bounds[i] 落入第 i 档([3,7] = 缺口1-3/4-7/8+ 三档)
@export var roll_tier_discounts: Array[int] = [] # 档位值 percent 降序(与 bounds 同长;[75,50,25] = ×0.75/×0.5/×0.25)
@export var crit_roll: int = 20 # [1,roll_faces] knob——nat 最大面 = 暴击触发掷点(恒全额)
@export var crit_multiplier: int = 200 # [100,999] percent knob——暴击倍率(200 = ×2,只乘该次基础攻击分量)
@export var damage_floor: int = 1 # [1,999] knob——任何伤害落地全局下限·全来源(无"被减伤归零 v=0"语义)
@export var armor_roll_stream: String = "armor_roll" # 掷点用途标签(rng_service PURPOSE_TAGS 成员——不参与派生,仅作事件日志用途标记)
@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"]
@@ -43,47 +19,77 @@ const P_PERFECT_DOMAIN := [100, 200]
func validate() -> Array:
var errs: Array[Dictionary] = []
if equivalence_ref_damage < 1 or equivalence_ref_damage > 999:
errs.append({"category": "domain", "message": "equivalence_ref_damage 越域 [1,999]: %d" % equivalence_ref_damage})
if not P_PERFECT_DOMAIN.has(p_perfect):
errs.append({"category": "pperfect_oob", "message": "p_perfect 载体域 {100,200}: %d" % p_perfect})
if seal_hit_premium < 100 or seal_hit_premium > 999:
errs.append({"category": "domain", "message": "seal_hit_premium 越域 [100,999](100=无溢价): %d" % seal_hit_premium})
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 round_direction != "floor":
errs.append({"category": "frozen", "message": "round_direction 结构冻结 floor: %s" % round_direction})
if pool_clamp_lo != -100 or pool_clamp_hi != 100:
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]"})
if roll_faces < 2 or roll_faces > 1000:
errs.append({"category": "domain", "message": "roll_faces 越域 [2,1000]: %d" % roll_faces})
if crit_roll < 1 or crit_roll > roll_faces:
errs.append({"category": "domain", "message": "crit_roll 越域 [1,roll_faces(%d)]: %d" % [roll_faces, crit_roll]})
if crit_multiplier < 100 or crit_multiplier > 999:
errs.append({"category": "domain", "message": "crit_multiplier 越域 [100,999]: %d" % crit_multiplier})
if damage_floor < 1 or damage_floor > 999:
errs.append({"category": "domain", "message": "damage_floor 越域 [1,999]: %d" % damage_floor})
if attack_bonus < 0 or attack_bonus > 999:
errs.append({"category": "domain", "message": "attack_bonus 越域 [0,999]: %d" % attack_bonus})
if String(armor_roll_stream) == "":
errs.append({"category": "domain", "message": "armor_roll_stream 空"})
# 档位表形状:bounds 升序且 ∈ [1, faces-1];discounts 降序且 ∈ [1,99](无零档——非归零);
# 档数 = 边界数 + 1(缺口 [1,b0] → discounts[0],(b_i,b_{i+1}] → discounts[i+1],> b 末值 → 末位档)
if roll_tier_bounds.is_empty() or roll_tier_discounts.size() != roll_tier_bounds.size() + 1:
errs.append({"category": "cross_field", "message": "roll_tier_bounds/discounts 形状违例(档数须 = 边界数 + 1): %d 边界 vs %d 档" % [roll_tier_bounds.size(), roll_tier_discounts.size()]})
else:
for i in roll_tier_bounds.size():
var b: int = int(roll_tier_bounds[i])
if b < 1 or b >= roll_faces or (i > 0 and b <= int(roll_tier_bounds[i - 1])):
errs.append({"category": "cross_field", "message": "roll_tier_bounds[%d] 越域/非升序: %d" % [i, b]})
var d: int = int(roll_tier_discounts[i])
if d < 1 or d > 99 or (i > 0 and d >= int(roll_tier_discounts[i - 1])):
errs.append({"category": "cross_field", "message": "roll_tier_discounts[%d] 越域/非降序(档位不设零档): %d" % [i, d]})
# 档位表形状: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] = armor_effectiveness.get(atk_type, {})
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]})
return errs
+17 -7
View File
@@ -1,13 +1,11 @@
[gd_resource type="Resource" script_class="SettlementTable" load_steps=2 format=3]
[gd_resource type="Resource" script_class="SettlementTable" load_steps=6 format=3]
[ext_resource type="Script" path="res://data/schemas/settlement_table.gd" id="1"]
[ext_resource type="Script" path="res://data/schemas/settlement_core_table.gd" id="2"]
[ext_resource type="Script" path="res://data/schemas/damage_roll_table.gd" id="3"]
[resource]
script = ExtResource("1")
equivalence_ref_damage = 4
p_perfect = 200
seal_hit_premium = 125
segment_order = PackedStringArray("grant", "buff_debuff", "combat", "decay", "verdict")
[sub_resource type="Resource" id="SettlementCoreTable_core"]
script = ExtResource("2")
source_label_map = {
"safe_cell": "安全格",
"seal_hit": "封印命中",
@@ -28,6 +26,12 @@ effect_pool_map = {
pool_clamp_lo = -100
pool_clamp_hi = 100
round_direction = "floor"
p_perfect = 200
seal_hit_premium = 125
[sub_resource type="Resource" id="DamageRollTable_roll"]
script = ExtResource("3")
equivalence_ref_damage = 4
armor_effectiveness = {
"normal": {
"light": 0,
@@ -54,6 +58,12 @@ crit_multiplier = 200
damage_floor = 1
armor_roll_stream = "armor_roll"
[resource]
script = ExtResource("1")
segment_order = PackedStringArray("grant", "buff_debuff", "combat", "decay", "verdict")
core = SubResource("SettlementCoreTable_core")
damage_roll = SubResource("DamageRollTable_roll")
; 攻击类型 × 护甲克制批(2026-09-12 用户拍板结构;2026-09-15 命名统一批 + 抽象分离批,数值 provisional 待用户/C3 调参):
; 主角攻击 = 单一基础分量(2026-09-19 充能退役批·用户拍板——原三分量模型退役,克制策略归冒险前配置:
; 类型 = hero 表 attack_type,营地换型 = 改字段;局内无挖掘攒弹药的第二供给)。