test(迁移): 批 B 波 1 G4 套1 敌方攻击类型轴四条迁入 roll_multiplier_test.gd——新宿主落位+manifest 登记+剪切挂载绿+按条变异 4/4 验红(表列越轨/快照清空连带载荷矩阵/载荷置空/矩阵查表短路各验红);smoke_all.gd 同批删函数节头与调用行;闸01 剩余面 1015 绿

This commit is contained in:
DKChan committed 2026-10-23 02:52:55 +08:00
1 parent 5f190bc354
commit fdb77ffeea
4 files changed
+101 -62

No files matched your search

-62
View File
@@ -153,7 +153,6 @@ var _rng
func _run_core_smoke(db, rng) -> void:
_db = db
_rng = rng
_smoke_enemy_attack_type()
_smoke_armor_roll()
_smoke_pool_wiring()
_smoke_type_glow()
@@ -1399,67 +1398,6 @@ func _smoke_level() -> void:
"模拟后 hero 表恢复基线(atk=%d hp=%d ac=%d——对配置快照)" % [int(_db.hero.atk), int(_db.hero.max_hp), int(_db.hero.armor_class)])
# ---- 敌方攻击类型轴(表列/单位快照/单据载荷/矩阵命中证词)----
# 克制矩阵命中证词 = 同种子三甲型配对实测(不是查表存在性证词):同种子下 armor_roll 流消费逐位一致、
# 阈值(hero armor_class)不变,三甲型唯一差异 = 克制点数 cdelta(normal_vs_light 0 / heavy −1 / ward +1)
# → heavy 伤害 == light −1、ward == light +1(要求基线伤害 ≥ 2——地板 1 会吞 −1 位移)。
# 承伤位移红线(卡片 §8 L3):全局位移只允许 ±1 量级——非此量级 = 矩阵消费面有未盘点的第二落点(停机)。
func _smoke_enemy_attack_type() -> void:
print("--- 敌方攻击类型轴(迭代 24 N3)---")
# ① 表列:20 行全填 normal(N3 全填不做差异化——Q9)
var at_ok := true
var at_detail := ""
for row in _db.enemy.rows:
var at := String(row.get("attack_type", ""))
if at != "normal":
at_ok = false
at_detail += " %s:%s" % [String(row.get("enemy_id", "?")), at]
check(at_ok, "敌方 %d 行 attack_type 全填 normal(迭代 24 N3 轴开列,越轨行%s)" % [
_db.enemy.rows.size(), at_detail if at_detail != "" else "无"])
# ②③④ 矩阵命中证词扫描:(3,0) 灰矮人 atk 区间 [2,4] 基线足够((1,2) 熊地精 atk 1 会塌地板);
# 同 seed 三甲型各开一场、各取首个敌方伤害行,对拍 ±1。
var matrix_found := false
var snap_ok := true
var payload_at := ""
var t_light := -1
var t_heavy := -1
var t_ward := -1
for seed_offset in range(40):
var totals := {}
for armor in ["light", "heavy", "ward"]:
var rng = load("res://core/rng_service.gd").new()
rng.set_master_seed(20261013 + seed_offset)
var settlement = load("res://core/battle_orchestrator.gd").new(_db, rng)
var rows: Array = []
settlement.step_row.connect(func(row): rows.append(row))
if not settlement.begin_battle({"level_index": 3, "node_depth": 0, "hero_hp": int(_db.hero.max_hp), "armor_type": armor}):
totals[armor] = -1
continue
if armor == "light":
for u in settlement._enemy_system.all_enemies():
if String(u.get("attack_type", "")) == "":
snap_ok = false
settlement.minesweeper.settle_batch([{"action": "stop"}])
for row in rows:
if String(row.get("type", "")) == "damage" and String(row.get("source_class", "")) == "enemy_action":
totals[armor] = int(row.get("value", 0))
if armor == "light":
payload_at = String(row.get("attack_type", "")) # 载荷摊平在行顶层(_emit_row 摊平 extra)
break
if not totals.has(armor):
totals[armor] = -1
if int(totals.get("light", -1)) >= 2 and int(totals.get("heavy", -1)) >= 1 and int(totals.get("ward", -1)) >= 1:
t_light = int(totals["light"])
t_heavy = int(totals["heavy"])
t_ward = int(totals["ward"])
matrix_found = (t_heavy == t_light - 1 and t_ward == t_light + 1)
break
check(snap_ok, "敌方单位快照 attack_type 非空(spawn 注入面——迭代 24 N3)")
check(matrix_found and t_light >= 2,
"克制矩阵命中证词:同种子三甲型实测 heavy %d == light %d − 1 · ward %d == light %d + 1(迭代 24 N3 全局平移 −1/0/+1)" % [
t_heavy, t_light, t_ward, t_light])
check(payload_at == "normal", "敌方伤害技单据载荷 attack_type 非空(= %s——第 9 参传值面,迭代 24 N3)" % payload_at)
# ---- 护甲等级掷点·行为面(折扣档分布 / nat20 暴击 / 伤害下限 / 棋盘来源必中)----
+1
View File
@@ -59,6 +59,7 @@ func _suites() -> Array:
{"path": "res://tests/unit/presentation/forge_workbench_test.gd", "expect": 0}, # 工作间两区六路线 UI 直验(V13 自动化前置面)
{"path": "res://tests/unit/battle_settlement/damage_math_test.gd", "expect": 0},
{"path": "res://tests/unit/battle_settlement/damage_controller_test.gd", "expect": 0},
{"path": "res://tests/unit/battle_settlement/roll_multiplier_test.gd", "expect": 0}, # 掷点与乘区宿主(敌攻击类型轴/护甲掷点行为面/乘区接线/伤害类型克制与标错/fresh 池攻击链——批 B 自 smoke_all 迁入)
{"path": "res://tests/unit/battle_settlement/skill_unlock_test.gd", "expect": 0}, # Boss 半血解锁技·容器机制(门谓词/闩不可逆/双面过滤/保底不逼锁定/零扰动)
{"path": "res://tests/unit/contracts/system_contracts_test.gd", "expect": 0},
{"path": "res://tests/unit/presentation/ui_font_asset_test.gd", "expect": 0},
@@ -0,0 +1,93 @@
# tests/unit/battle_settlement/roll_multiplier_test.gd — 掷点与乘区单元测试宿主
# 断言面 = 五套(批 B 断言迁移自 smoke_all.gd,G4 施工组;五套按源编排序分五笔原子提交迁入):
# 敌方攻击类型轴(表列全 normal / spawn 快照注入 / 单据载荷第 9 参 / 克制矩阵命中证词 ±1 位移)
# 护甲等级掷点·行为面(查档纯函数分布 / EV 锚 / nat20 暴击 / 削弱续时 / AC11 逐拍判定 / 伤害下限 / 棋盘来源必中)
# 主角乘区接线 + 硬化折半方向(×1.5 / ×0.75 / ×0.5——池乘区通道)
# 攻击类型 × 护甲克制 + 标错惩罚(护甲快照 / 克制矩阵表值 / 标错负向与标中正向兑付 / 同种子回放)
# 战斗结算 fresh 池攻击链回归(Bug#2——池空时按掷点档位足额兑现)
# 协议照单元侧惯例:extends SceneTree+Kit.check 主通道;--script 模式无 autoload,
# ConfigDB 显式实例化构造注入(各套自建 RngService——源套掷点隔离形态,宿主不持统一流);
# 表加载失败不中止(沿被迁断言面的源语义——诊断在此展开,表值变异的红由具体断言落,承 G2/G3 同款)。
# 运行:godot --headless --path game --script res://tests/unit/battle_settlement/roll_multiplier_test.gd
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
const ConfigDBScript := preload("res://core/config_db.gd")
var _db
func _init() -> void:
_db = ConfigDBScript.new()
# 表加载失败不中止(承 G2/G3 同款:表损坏的红落在具体断言上——
# 诊断在此展开,后续断言按坏表实况逐条验红;collect_only=true 下成员照常赋值可评)
var load_ok: bool = _db.load_tables("res://data/tables", true)
if not load_ok:
push_error("[roll_multiplier_test] 表加载返回失败——诊断如下(红由具体断言落)")
for d in _db.diagnostics():
if d.severity == "error" or d.severity == "fatal":
push_error(" [%s] %s: %s" % [d.severity, d.category, d.message])
# 套执行序沿源编排序
_smoke_enemy_attack_type()
quit(Kit.finish("roll_multiplier_test"))
# ---- 敌方攻击类型轴(表列/单位快照/单据载荷/矩阵命中证词)----
# 克制矩阵命中证词 = 同种子三甲型配对实测(不是查表存在性证词):同种子下 armor_roll 流消费逐位一致、
# 阈值(hero armor_class)不变,三甲型唯一差异 = 克制点数 cdelta(normal_vs_light 0 / heavy −1 / ward +1)
# → heavy 伤害 == light −1、ward == light +1(要求基线伤害 ≥ 2——地板 1 会吞 −1 位移)。
# 承伤位移红线(卡片 §8 L3):全局位移只允许 ±1 量级——非此量级 = 矩阵消费面有未盘点的第二落点(停机)。
func _smoke_enemy_attack_type() -> void:
print("--- 敌方攻击类型轴(迭代 24 N3)---")
# ① 表列:20 行全填 normal(N3 全填不做差异化——Q9)
var at_ok := true
var at_detail := ""
for row in _db.enemy.rows:
var at := String(row.get("attack_type", ""))
if at != "normal":
at_ok = false
at_detail += " %s:%s" % [String(row.get("enemy_id", "?")), at]
Kit.check("敌方 %d 行 attack_type 全填 normal(迭代 24 N3 轴开列,越轨行%s)" % [
_db.enemy.rows.size(), at_detail if at_detail != "" else "无"], at_ok)
# ②③④ 矩阵命中证词扫描:(3,0) 灰矮人 atk 区间 [2,4] 基线足够((1,2) 熊地精 atk 1 会塌地板);
# 同 seed 三甲型各开一场、各取首个敌方伤害行,对拍 ±1。
var matrix_found := false
var snap_ok := true
var payload_at := ""
var t_light := -1
var t_heavy := -1
var t_ward := -1
for seed_offset in range(40):
var totals := {}
for armor in ["light", "heavy", "ward"]:
var rng = load("res://core/rng_service.gd").new()
rng.set_master_seed(20261013 + seed_offset)
var settlement = load("res://core/battle_orchestrator.gd").new(_db, rng)
var rows: Array = []
settlement.step_row.connect(func(row): rows.append(row))
if not settlement.begin_battle({"level_index": 3, "node_depth": 0, "hero_hp": int(_db.hero.max_hp), "armor_type": armor}):
totals[armor] = -1
continue
if armor == "light":
for u in settlement._enemy_system.all_enemies():
if String(u.get("attack_type", "")) == "":
snap_ok = false
settlement.minesweeper.settle_batch([{"action": "stop"}])
for row in rows:
if String(row.get("type", "")) == "damage" and String(row.get("source_class", "")) == "enemy_action":
totals[armor] = int(row.get("value", 0))
if armor == "light":
payload_at = String(row.get("attack_type", "")) # 载荷摊平在行顶层(_emit_row 摊平 extra)
break
if not totals.has(armor):
totals[armor] = -1
if int(totals.get("light", -1)) >= 2 and int(totals.get("heavy", -1)) >= 1 and int(totals.get("ward", -1)) >= 1:
t_light = int(totals["light"])
t_heavy = int(totals["heavy"])
t_ward = int(totals["ward"])
matrix_found = (t_heavy == t_light - 1 and t_ward == t_light + 1)
break
Kit.check("敌方单位快照 attack_type 非空(spawn 注入面——迭代 24 N3)", snap_ok)
Kit.check("克制矩阵命中证词:同种子三甲型实测 heavy %d == light %d − 1 · ward %d == light %d + 1(迭代 24 N3 全局平移 −1/0/+1)" % [
t_heavy, t_light, t_ward, t_light], matrix_found and t_light >= 2)
Kit.check("敌方伤害技单据载荷 attack_type 非空(= %s——第 9 参传值面,迭代 24 N3)" % payload_at, payload_at == "normal")
@@ -276,6 +276,13 @@
施工发现(供后续组参考,接 G2 序号):⑦ 删套函数必须同批删 `_run_core_smoke` 调度体里的调用行——本组首轮删净四函数后闸 01 即 Parse Error「Function not found in base self」(调用行残留致红,三闸之二挂),补删四调用行后全过;G1/G2 记录有载此步(「同批删六调用行」),G3 首轮遗漏,后续组删套时以「函数+调用行」双检为鉴。
**G4 单元·掷点与乘区 → game/tests/unit/battle_settlement/roll_multiplier_test.gd:五套 36 条逐套原子提交(沿 G3 起组内按套提交)。**
挂载终态=宿主 `_init` 自建 `_db`(ConfigDB 构造注入,`load_tables(..., true)` 失败不中止承 G2/G3 同款);各套自建 RngService(源套掷点隔离形态——逐战斗全新取号,宿主不持统一流成员)。helper 归属:`_first_hero_attack_rows/_weaken_refresh_probe/_ac11_tier_samples`(armor_roll 套内独占)、`_hero_attack_row_with`(pool_wiring 套内独占)、`_run_seal_backlash_round`(damage_types 套内独占)随迁且 smoke_all.gd 同批删原件;`_find_seal_target`(G5 未迁四套仍消费)与 `_run_battle/_auto_play_round`(G6 属主)双份过渡——宿主持一致副本、smoke_all.gd 原件保留(发现④),`_run_battle` 副本去 G6 专属「收束相位回 Idle」断言(该条随 G6 迁其宿主,副本保持零断言纯驱动形态)。`DamageMath` 全局 class_name 裸用(G3 同先例)。manifest 登记随套1 commit。
逐套变异记账(每条独立一轮:改坏→跑新宿主→红→`git checkout` 还原→`git status` 清洁确认):
- `_smoke_enemy_attack_type`(4 条):4/4 验红。变异点——enemy.tres:26 goblin attack_type normal→pierce(红唯一 1/4,越轨行 goblin:pierce);unit_container.gd:110 快照注入硬编码空串(红 3/4——快照为载荷/矩阵共同源,条3 条4 连带属预期形态);damage_controller.gd:471 敌方技载荷 attack_type 置空串(红唯一 1/4);damage_controller.gd:577 `_counter_delta` 查表短路恒中性(红唯一 1/4)。
## 十、支援线 9 项待裁闭合(2026-10-22 主会话裁)
1. `extract_checks.py` **常驻保留**——承担清单维护义务(test-conventions §9.3 锚)的再提取/diff 职能;其 DEFAULT_SRC 悬空问题列入波 2:默认源改为三宿主(game/tests/unit、integration、system_test.gd)遍历,或显式路径参数,波 3 出口对账用显式模式。