extends SceneTree # 锻造工坊工作间 UI 直验套件(V13 人闸的自动化前置面:两区结构/钮态/意图信号/锁定态纪律) # 覆盖:锁定态零行板零购买路径(占位纪律延续)· 两区各三路线 · 初始持有行购买钮(几何价 20)· # 待解锁行解锁钮(200)+无购买钮 · 选中钮当前态禁用 · 余额不足禁用态 · 满级无购买钮 · # 三意图信号外发(UI 只发意图——契约 9,写口归 MetaProgression 由冒烟侧覆盖)。 # 运行:godot --headless --path game --script res://tests/unit/presentation/forge_workbench_test.gd(expect 0) const Kit := preload("res://tests/unit/test_kit.gd") const _TEST_SAVE := "user://test_forge_workbench.json" func _initialize() -> void: call_deferred("_run") func _row_by_id(ov) -> Dictionary: var out := {} for r in ov._forge_rows: out[String(r.route_id)] = r return out func _run() -> void: var db = load("res://core/config_db.gd").new() var load_ok: bool = db.load_tables("res://data/tables", true) if not load_ok: Kit.check("ConfigDb 加载(工作间测试夹具前提)", false) quit(Kit.finish("forge_workbench_test")) return var sv = load("res://core/save_manager.gd").new(_TEST_SAVE) sv.load_from_disk() var mp = load("res://core/meta_progression.gd").new(db, sv) var ov = load("res://presentation/views/camp_overlay_view.gd").new() root.add_child(ov) var buys := [] var unlocks := [] var selects := [] ov.forge_buy_requested.connect(func(rid): buys.append(rid)) ov.forge_unlock_requested.connect(func(rid): unlocks.append(rid)) ov.forge_select_requested.connect(func(rid): selects.append(rid)) # ① 锁定态:树钥匙未点亮 → 工作间不可见、两区零行板(无任何可交互购买路径) ov.open_camp({"balance": 999, "forge": mp.forge_shop_status(), "tree": []}) Kit.check("锁定态:工作间不可见(forge_workshop 钥匙未点亮)〔锻造工坊批 V13〕", not ov._forge_workbench.visible and ov._forge_lock_l.visible) Kit.check("锁定态:两区零行板(占位纪律——零购买路径)〔锻造工坊批 V13〕", ov._forge_zone_weapon.get_child_count() == 0 and ov._forge_zone_armor.get_child_count() == 0) # ② 解锁态:钥匙点亮 → 两区各三路线 ov.open_camp({"balance": 500, "forge": mp.forge_shop_status(), "tree": [{"node_id": "forge_workshop", "level": 1}]}) Kit.check("解锁态:工作间可见(钥匙点亮展开)〔锻造工坊批 V13〕", ov._forge_workbench.visible) Kit.check("两区各三路线(武器锻造区 3 + 护甲锻造区 3)〔锻造工坊批 V13〕", ov._forge_zone_weapon.get_child_count() == 3 and ov._forge_zone_armor.get_child_count() == 3) var rows: Dictionary = _row_by_id(ov) Kit.check("六路线行册恰全(sword/spear/staff/light/heavy/ward)〔锻造工坊批 V13〕", rows.has("sword") and rows.has("spear") and rows.has("staff") and rows.has("light") and rows.has("heavy") and rows.has("ward")) # ③ 初始持有行(长剑/轻甲):购买钮现形带几何价、选中钮=当前禁用 # 两行同取谓词并集(轻甲行补 select_btn 非空 + buy_btn 可用两子句——原件少写,只增不减) for pair in [["sword", "长剑"], ["light", "轻甲"]]: var r: Dictionary = rows.get(pair[0], {}) Kit.check("%s行:购买钮「锻造 20」(0 级位几何价)+选中钮「当前」禁用〔锻造工坊批 V13〕" % pair[1], r.get("buy_btn") != null and String(r.buy_btn.text) == "锻造 20" and not r.buy_btn.disabled and r.get("select_btn") != null and String(r.select_btn.text) == "当前" and r.select_btn.disabled) # ④ 待解锁行(长枪/法杖/重甲/魔法护甲):解锁钮 200、无购买钮、无选中钮、状态含双通道文案 var rsp = rows.get("spear", {}) var rwa = rows.get("ward", {}) Kit.check("长枪行:解锁钮「解锁 200」+无购买钮+无选中钮〔锻造工坊批 V13〕", rsp.get("unlock_btn") != null and String(rsp.unlock_btn.text) == "解锁 200" and not rsp.unlock_btn.disabled and rsp.get("buy_btn") == null and rsp.get("select_btn") == null) Kit.check("魔法护甲行:解锁钮「解锁 200」+状态含并联双通道文案(练满轻甲锻造 或 200 钥石直购)〔锻造工坊批 V13〕", rwa.get("unlock_btn") != null and String(rwa.status_l.text).contains("练满轻甲锻造") and String(rwa.status_l.text).contains("200")) # ⑤ 余额不足禁用态(常态防线) ov.refresh_camp({"balance": 0, "forge": mp.forge_shop_status(), "tree": [{"node_id": "forge_workshop", "level": 1}]}) rows = _row_by_id(ov) Kit.check("余额 0:长剑购买钮禁用+长枪解锁钮禁用(余额联动常态防线)〔锻造工坊批 V13〕", rows.sword.buy_btn.disabled and rows.spear.unlock_btn.disabled) # ⑥ 满级行:无购买钮、状态=已满级(直置存档域构造——两分法调试口径) sv.meta_state.set_forge_route_level("sword", 5) ov.refresh_camp({"balance": 500, "forge": mp.forge_shop_status(), "tree": [{"node_id": "forge_workshop", "level": 1}]}) rows = _row_by_id(ov) Kit.check("长剑满级:无购买钮+状态「已满级」+长枪派生解锁出解锁钮消失(练满通道)〔锻造工坊批 V13〕", rows.sword.get("buy_btn") == null and String(rows.sword.status_l.text) == "已满级" and rows.spear.get("unlock_btn") == null and rows.spear.get("buy_btn") != null) # ⑦ 三意图信号外发(UI 只发意图——按真实按钮路径;选中钮直发信号面) buys.clear(); unlocks.clear(); selects.clear() rows.spear.buy_btn.pressed.emit() # 长枪已派生解锁——真实按钮路径 rows.heavy.unlock_btn.pressed.emit() # 重甲待解锁——真实按钮路径 ov.forge_select_requested.emit("spear") # 选中意图(当前行选中钮禁用态——信号面直发) Kit.check("三意图信号外发(buy=spear/unlock=heavy/select=spear——UI 只发意图)〔锻造工坊批 V13〕", buys == ["spear"] and unlocks == ["heavy"] and selects == ["spear"]) # 尾清(测试文件绝不残留) ov.queue_free() for suffix in ["", ".tmp", ".corrupt.bak"]: if FileAccess.file_exists(_TEST_SAVE + suffix): DirAccess.remove_absolute(_TEST_SAVE + suffix) quit(Kit.finish("forge_workbench_test"))