diff --git a/game/tests/unit/battle_settlement/refresh_feats_test.gd b/game/tests/unit/battle_settlement/refresh_feats_test.gd index e601da85..2c8850c5 100644 --- a/game/tests/unit/battle_settlement/refresh_feats_test.gd +++ b/game/tests/unit/battle_settlement/refresh_feats_test.gd @@ -38,6 +38,7 @@ func _init() -> void: test_rest_rotation_cap_and_clear() test_charge_layers_consumed_same_round() test_blast_defers_to_combat_segment() + test_seven_feats_validate_and_pool() quit(Kit.finish("refresh_feats_test")) @@ -602,3 +603,48 @@ func test_blast_defers_to_combat_segment() -> void: Kit.check("V7 战斗段末群伤单恰一单(全体存活敌 ×2——行值 2 count≥1)〔换图系批 V7〕", blast_rows.size() == 1 and int((blast_rows[0] as Dictionary).get("value", -1)) == 2 and int((blast_rows[0] as Dictionary).get("count", 0)) >= 1) + + +# ---- V11 数据与池(迭代 46 卡A T6)---- + +# 七件 validate/在册/稀有度 cap 对表/跨表计数器引用闭合/desc 禁词 +func test_seven_feats_validate_and_pool() -> void: + print("--- V11 七件数据与池 ---") + var seven := {"vein_reserve": "excellent", "easy_swap": "excellent", "wall_blast": "excellent", + "charge_rotation": "excellent", "stone_mitigation": "common", "tunnel_rest": "common", "mine_profit": "rare"} + Kit.check("V11 feats.validate 零违例(七行全过词表/行键白名单/前缀校验)〔换图系批 V11〕", + _db.crossrun.feats.validate().is_empty()) + var all_in := true + var desc_bad := 0 + for fid in seven.keys(): + var row: Dictionary = _db.crossrun.feats.row_by_id(String(fid)) + if row.is_empty() or String(row.get("rarity", "")) != String(seven[fid]) or int(row.get("cap", 0)) != 1: + all_in = false + if String(row.get("desc", "")).contains("雷") or String(row.get("desc", "")).contains("旗"): + desc_bad += 1 + Kit.check("V11 七行在册且稀有度/cap 逐件对表〔换图系批 V11〕", all_in) + Kit.check("V11 七件 desc 禁词零命中(雷/旗字面)〔换图系批 V11〕", desc_bad == 0) + # 跨表计数器引用闭合(判据域=换图系七件自身:value_counter/when-id 读取全部有 counter_add 写入方; + # 预存行的消费点记账形态〔如完美保险 insurance_used 由授予段落账〕不在此判据域) + var written := {} + for row_v in (_db.crossrun.feats.rows as Array): + for tv in ((row_v as Dictionary).get("triggers", []) as Array): + var do_d: Dictionary = (tv as Dictionary).get("do", {}) + if String(do_d.get("kind", "")) == "counter_add": + written[String(do_d.get("counter_id", ""))] = true + var refs_ok := true + for fid2 in seven.keys(): + var row2: Dictionary = _db.crossrun.feats.row_by_id(String(fid2)) + for tv2 in ((row2.get("triggers", []) as Array)): + var tr2 := tv2 as Dictionary + var do2: Dictionary = tr2.get("do", {}) + for reader in [String(do2.get("value_counter", "")), String(do2.get("value_from", ""))]: + if reader != "" and not written.has(reader): + refs_ok = false + for c_v in (tr2.get("when", []) as Array): + var cid := String((c_v as Dictionary).get("id", "")) + if (c_v as Dictionary).get("fact", "") == "counter" and cid != "" and not written.has(cid): + refs_ok = false + Kit.check("V11 七件跨表计数器引用闭合(value_counter/when-id 全部有 counter_add 写入方)〔换图系批 V11〕", refs_ok) + Kit.check("V11 换图系三计数器在写入集(swap_charge_layers/round_swap_guard/round_swap_rest)〔换图系批 V11〕", + written.has("swap_charge_layers") and written.has("round_swap_guard") and written.has("round_swap_rest")) diff --git a/game/tests/unit/core/adventure_feats_test.gd b/game/tests/unit/core/adventure_feats_test.gd index ddbb73b3..e362e441 100644 --- a/game/tests/unit/core/adventure_feats_test.gd +++ b/game/tests/unit/core/adventure_feats_test.gd @@ -461,8 +461,8 @@ func _smoke_adventure() -> void: func _smoke_feats() -> void: print("--- 专长重设计实装(feat_table 四级 · 专长带池接线 · 订阅面逐件)---") - # ① 现行表牌库构成:30 件全量入池——102 数值令牌+30 relic 令牌(四级 rarity 直映射) - Kit.check("feat_table 已加载(30 件=基础 27+迁入 2+卡② 完美保险 1——迭代 31 卡② 换代)", _db.crossrun.feats != null and (_db.crossrun.feats.rows as Array).size() == 30) + # ① 现行表牌库构成:37 件全量入池——102 数值令牌+37 relic 令牌(四级 rarity 直映射) + Kit.check("feat_table 已加载(37 件=基础 27+迁入 2+完美保险 1+迭代 46 换图系七件——换代)", _db.crossrun.feats != null and (_db.crossrun.feats.rows as Array).size() == 37) Kit.check("feat_table.validate 零违例(四级 rarity/cap 域/trigger 词表/行键白名单全过)", _db.crossrun.feats.validate().is_empty()) var a_r0 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(71)) Kit.check("专长带场景:冒险启动(seed 71)", a_r0.start_adventure(71)) @@ -473,7 +473,7 @@ func _smoke_feats() -> void: feat_tokens += 1 var tk := String((t as Dictionary).get("tier", "")) tier_counts[tk] = int(tier_counts.get(tk, 0)) + 1 - Kit.check("专长带入池:30 件四级直映射(普通 12/优秀 9/稀有 9/传说 0)——牌库 102+30=132〔专长批 V2+卡② 完美保险迁入〕", feat_tokens == 30 and int(tier_counts.get("common", 0)) == 12 and int(tier_counts.get("excellent", 0)) == 9 and int(tier_counts.get("rare", 0)) == 9 and a_r0.deck().deck.size() == 132) + Kit.check("专长带入池:37 件四级直映射(普通 14/优秀 13/稀有 10/传说 0)——牌库 102+37=139〔专长批 V2+迭代 46 换图系七件换代〕", feat_tokens == 37 and int(tier_counts.get("common", 0)) == 14 and int(tier_counts.get("excellent", 0)) == 13 and int(tier_counts.get("rare", 0)) == 10 and a_r0.deck().deck.size() == 139) # ② 旧 27 件零残留钉面(V2——arcane_ember 系旧件表首行;全仓 grep 零残留归静态核查) Kit.check("旧 27 件已退役:arcane_ember/magma_body 表内零残留〔专长批 V2〕", _db.crossrun.feats.row_by_id("arcane_ember").is_empty() and _db.crossrun.feats.row_by_id("magma_body").is_empty()) Kit.check("迁出收尾:treasure_items 无 venom_edge/seal_burst 行(同调目录自然收窄)〔专长批 V13〕", _db.crossrun.treasure_items.row_by_id("venom_edge").is_empty() and _db.crossrun.treasure_items.row_by_id("seal_burst").is_empty())