iter50-卡2:卡B(断言补齐): 四域13新断言+2处既有扩展——①增益结算链V1-V5 ②设置存储V6-V8 ⑤Boss门控V9/V10 ⑥锻造V11-V16; 新件settings_store_test与boss_gate_test; manifest+2; 73套件全绿
This commit is contained in:
1 parent
e658492703
commit
cd133932f9
9 files changed
+329
-1
No files matched your search
@@ -37,6 +37,10 @@ func _init() -> void:
|
||||
_smoke_forge_axes()
|
||||
_smoke_tree()
|
||||
_smoke_tree_rules()
|
||||
_test_forge_buy_reject()
|
||||
_test_blessing_no_forge_leak()
|
||||
_test_default_axes_pre_key()
|
||||
_test_armor_switch_dual_axes()
|
||||
quit(Kit.finish("meta_progression_forge_test"))
|
||||
|
||||
|
||||
@@ -50,7 +54,8 @@ const _META_SMOKE_PATHS := ["user://smoke_meta_buy.json", "user://smoke_meta_dry
|
||||
"user://smoke_meta_tree.json", "user://smoke_meta_career.json",
|
||||
"user://smoke_meta_career_v5.json", "user://smoke_meta_treasure_unlock.json", "user://smoke_meta_treasure_blank.json",
|
||||
"user://smoke_meta_forge.json", "user://smoke_meta_forge2.json", "user://smoke_meta_forge3.json",
|
||||
"user://smoke_meta_forge4.json", "user://smoke_meta_forge5.json"]
|
||||
"user://smoke_meta_forge4.json", "user://smoke_meta_forge5.json",
|
||||
"user://smoke_meta_reject.json", "user://smoke_meta_blessing.json", "user://smoke_meta_axesw.json"]
|
||||
|
||||
|
||||
func _meta_smoke_cleanup() -> void:
|
||||
@@ -418,6 +423,12 @@ func _smoke_forge_axes() -> void:
|
||||
Kit.check("长剑二级购:扣 30、等级→2;轻甲独立零扰动(六路线各练各的)〔锻造工坊批 V2〕",
|
||||
mp2.forge_buy_level("sword") and sv2.meta_state.forge_state().levels.get("sword", 0) == 2
|
||||
and sv2.meta_state.keystone_balance() == 50 and sv2.meta_state.forge_state().levels.get("light", 0) == 0)
|
||||
# V12:长剑购买链走至 3 级(45 档扣款——余额 50−45=5,几何价第三档);链后续夹具段余额基准
|
||||
# 由 add_keystone 补回(注入口径——直写存档域构造状态,下游 V3 段 50−20 语义不变)
|
||||
Kit.check("长剑三级购:扣 45、等级→3(余额 50−45=5)〔断言补齐批 V12〕",
|
||||
mp2.forge_buy_level("sword") and sv2.meta_state.forge_state().levels.get("sword", 0) == 3
|
||||
and sv2.meta_state.keystone_balance() == 5)
|
||||
sv2.meta_state._add_keystone(45)
|
||||
# V2③:余额不足拒且零副作用(等级/选中/解锁三面零变化)
|
||||
var sv2d = _meta_new_save(_META_SMOKE_PATHS[14])
|
||||
var mp2d = load("res://core/meta_progression.gd").new(_db, sv2d)
|
||||
@@ -760,3 +771,72 @@ func _smoke_tree_rules() -> void:
|
||||
Kit.check("聚:战斗掉落乘算 ×1.5(provisional——同种子配对 %d→%d,入账前=结算框显示实得)",
|
||||
gp_hoard == int(round(float(gp_base) * 1.5)) and gp_hoard >= gp_base)
|
||||
_meta_smoke_cleanup()
|
||||
|
||||
|
||||
# ---- 锻造契约补齐(断言补齐批——⑥锻造工坊):拒购零副作用/祝福件零泄漏/默认值解耦/甲型双轴 ----
|
||||
|
||||
func _test_forge_buy_reject() -> void:
|
||||
var sv = _meta_new_save("user://smoke_meta_reject.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
sv.meta_state._add_keystone(10) # 低于首购价 20——余额不足面
|
||||
var fs0: Dictionary = sv.meta_state.forge_state().duplicate(true)
|
||||
var bal0: int = sv.meta_state.keystone_balance()
|
||||
Kit.check("余额不足拒购:sword false、等级与余额零变化〔断言补齐批 V13〕",
|
||||
not mp.forge_buy_level("sword") and sv.meta_state.forge_state() == fs0 and sv.meta_state.keystone_balance() == bal0)
|
||||
sv.meta_state._add_keystone(1000) # 充值后未解锁面不变(spear 直购未走/练满未达)
|
||||
Kit.check("未解锁拒购:spear false、等级与余额零变化〔断言补齐批 V13〕",
|
||||
not mp.forge_buy_level("spear") and sv.meta_state.forge_state() == fs0
|
||||
and sv.meta_state.keystone_balance() == bal0 + 1000)
|
||||
|
||||
|
||||
func _test_blessing_no_forge_leak() -> void:
|
||||
var sv = _meta_new_save("user://smoke_meta_blessing.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
var fs0: Dictionary = sv.meta_state.forge_state().duplicate(true)
|
||||
# 真实入口=商店购买口(LevelRunner 装配的 shop_session.buy_product——祝福件走 GP 钱包+局内加算区)
|
||||
var runner = load("res://core/level_runner.gd").new(_db, _fresh_rng(80))
|
||||
var lvl_ok: bool = runner.start_level(1, 80)
|
||||
Kit.check("V14 前置:关卡启动(L1 行集)", lvl_ok)
|
||||
if not lvl_ok:
|
||||
return
|
||||
runner.gp = 100
|
||||
Kit.check("V14 前置:攻击祝福受理", runner.shop.buy_product("atk_up"))
|
||||
Kit.check("V14 前置:护甲祝福受理", runner.shop.buy_product("armor_up"))
|
||||
Kit.check("祝福件零泄漏:购买后 forge_state 投影逐位不变(levels/选中/解锁三面)〔断言补齐批 V14〕",
|
||||
sv.meta_state.forge_state() == fs0)
|
||||
Kit.check("祝福件住局内加算区(atk +1 在加算日记——通道分离实证)", runner.journal().bonus_total("atk") == 1)
|
||||
|
||||
|
||||
func _test_default_axes_pre_key() -> void:
|
||||
var sv = _meta_new_save("user://smoke_meta_axesw.json")
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
var ax: Dictionary = mp.hero_type_axes()
|
||||
Kit.check("树钥匙未点亮:hero_type_axes 返回锻造配置默认 normal/light(默认值常驻解耦)〔断言补齐批 V15〕",
|
||||
String(ax["attack"]) == "normal" and String(ax["armor"]) == "light")
|
||||
|
||||
|
||||
func _test_armor_switch_dual_axes() -> void:
|
||||
var sv = _meta_new_save("user://smoke_meta_axesw.json")
|
||||
sv.meta_state._add_keystone(1000)
|
||||
var mp = load("res://core/meta_progression.gd").new(_db, sv)
|
||||
Kit.check("V16 前置:重甲直购解锁+首购+选中(200+20 钥石)",
|
||||
mp.forge_buy_unlock("heavy") and mp.forge_buy_level("heavy") and mp.forge_select("armor", "heavy"))
|
||||
var adr = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(81))
|
||||
var p: Dictionary = load("res://presentation/main.gd").build_adventure_params(sv, mp)
|
||||
var adv_ok: bool = adr.start_adventure(81, p)
|
||||
Kit.check("V16 前置:参数面冒险启动", adv_ok)
|
||||
if not adv_ok:
|
||||
return
|
||||
Kit.check("甲型双轴:heavy 选中 → AC 轴 12 基础+路线 Lv1=13(light 同级 11 的 +2 档差)〔断言补齐批 V16〕",
|
||||
int(adr.runner().effective_ac()) == 13 and String(mp.hero_type_axes()["armor"]) == "heavy")
|
||||
Kit.check("甲型双轴:挨普通攻击修正 −1(克制矩阵 normal→heavy)〔断言补齐批 V16〕",
|
||||
int(_db.combat.settlement.damage_roll.armor_effectiveness["normal"]["heavy"]) == -1)
|
||||
Kit.check("V16 前置:切回 light(初始持有已解锁可切)", mp.forge_select("armor", "light"))
|
||||
var adr2 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(82))
|
||||
var p2b: Dictionary = load("res://presentation/main.gd").build_adventure_params(sv, mp)
|
||||
var adv2_ok: bool = adr2.start_adventure(82, p2b)
|
||||
Kit.check("V16 前置:切回后新参数面冒险启动(快照冻结契约——新 runner 读新选中)", adv2_ok)
|
||||
if not adv2_ok:
|
||||
return
|
||||
Kit.check("甲型双轴:切回 light 复原(AC 10+0)且 heavy 等级保留(Lv1 不丢)〔断言补齐批 V16〕",
|
||||
int(adr2.runner().effective_ac()) == 10 and sv.meta_state.forge_state().levels.get("heavy", 0) == 1)
|
||||
@@ -29,6 +29,7 @@ func _init() -> void:
|
||||
_test_shop_and_attribution()
|
||||
_test_battle_floor()
|
||||
_test_exit_structure()
|
||||
_test_boss_exit_per_region()
|
||||
_test_rng_single_point()
|
||||
_test_negative_fixtures()
|
||||
quit(Kit.finish("node_map_generator_test"))
|
||||
@@ -303,6 +304,41 @@ func _terminals_reachable(g: Dictionary, id: int) -> int:
|
||||
return c
|
||||
|
||||
|
||||
# ---- V9 Boss 出场门控(难度曲线与Boss——每区域恰一 Boss 终点)----
|
||||
|
||||
func _test_boss_exit_per_region() -> void:
|
||||
# 真实生成多区域图:全部终点 type=="boss"、boss 终点数==exit_count、深度=L−r(r∈[0,exit_count)
|
||||
# ——每区域恰一 boss、深度互异即分属不同区域)
|
||||
# 节点 depth 字段=入口起最长路径深(生成器校验轴);区域序号 r 的可观测形态=最深 boss(r=0)
|
||||
# 与该 boss 的深度差(同层 boss 深度 = d0 − r——区域段 L−r 的 1 步差)
|
||||
var all_ok := true
|
||||
for L in [1, 2, 3]:
|
||||
for s in [42, 43, 44]:
|
||||
var g := _graph(L, s)
|
||||
var by_id := {}
|
||||
for n in g["nodes"]:
|
||||
by_id[int(n["id"])] = n
|
||||
var exits := int(_params(L)["exit_count"])
|
||||
var d0 := 0
|
||||
for tid in g["terminals"]:
|
||||
d0 = maxi(d0, int(by_id[int(tid)]["depth"]))
|
||||
var per_region := {}
|
||||
for tid in g["terminals"]:
|
||||
var tn: Dictionary = by_id[int(tid)]
|
||||
if String(tn["type"]) != "boss":
|
||||
all_ok = false
|
||||
var r := d0 - int(tn["depth"])
|
||||
if r < 0 or r >= exits:
|
||||
all_ok = false
|
||||
per_region[r] = int(per_region.get(r, 0)) + 1
|
||||
if per_region.size() != exits:
|
||||
all_ok = false
|
||||
for r in per_region:
|
||||
if int(per_region[r]) != 1:
|
||||
all_ok = false
|
||||
Kit.check("每区域恰一 Boss 终点(type=boss × exit_count 个、深度=d0−r 互异分域——区域段 L−r 的可观测形态)〔断言补齐批 V9〕", all_ok)
|
||||
|
||||
|
||||
# ---- V10 RNG 单点(纯流面 counter 不变)----
|
||||
|
||||
func _test_rng_single_point() -> void:
|
||||
|
||||
Reference in new issue
Block a user