- 四view新件(自CampOverlay承载面迁移,界面逻辑复用零重设计契约4): forge_view(钥匙门+两区六路线行板)/treasure_view(12件行板+同调装配段+Y升级行+钥匙门)/tree_view(脉分组行板+Fork双态)/codex_view(占位挂起壳,锁文随形态订正『柜台』称谓L2)——各带返回钮发closed(关层归presenter);公共壳CampSubViewBase(实底dim+铭牌+钥石徽+返回钮) - CampRefreshBus新件: 各写口尾→现开界面刷新+场景根HUD一闸(卡⑤HUD同口挂接;非autoload非信号广播——组合根单点推送);presenter八写口尾refresh_camp→bus.refresh(F9:七口+on_camp_buy双尾) - 承载形态(§9-2拍板): 单一内页切换=场景根留底——模态栈open只隐上方登记层(modal_stack.gd:27实测),四子界面登记于场景根之上且实底遮挡,双开互斥靠遮挡天然成立;V2断言按留底派生(T5落) - ui_registry: LAYER_CONTRACT camp_overlay槽→camp_scene(shell camp_screen)+forge/treasure/tree/codex_view四槽;node()改经modals.camp.X二层(卡⑥契约2);ui_camp_modals五枚成员(overlay枚随退役删除——卡⑥契约4) - camp_screen: 持camp_scene取代CampOverlay成员;main_ui_build: 场景装配(布局表ConfigDB注入)+四view实例化+信号接线(意图→presenter/closed bind携视图/object_pressed→组合根路由) - presenter承接面: 五目标改线(ctx.views束——成员预算12≤15)+open_camp_forge/treasure/tree/codex四开层口+on_camp_sub_closed+on_camp_closed关层收闭(营地层与选档层同收+开始层恢复);main_overlay_flow._on_camp_object_pressed五物件路由(gate/campfire归T4)+生涯营地语境关层分支(笔记本改挂——CareerOverlay本体零改);main.gd薄委托零新增成员 - 退役: camp_overlay_view.gd删除(吸收即死);旧形态断言同批摘除(test-conventions §5.5):theme_projection等高源码断言+最近冒险行投影段(卡面削弱申报授权:注销,条带Q4=甲撤下) - 同批迁移: forge_workbench_test承载类camp_overlay_view→forge_view+_forge._X扁平化(断言零改12项绿);system_test三处机械随迁(V2实例化/:108同一性链/:121候选清单——camp_scene句柄更名) - 验证: 冒烟boot PASS(15项0失败——装配全链通)+全流程孤检绿(12项:五句柄/铺层/点选路由四子界面/返回/生涯改挂/营地语境分支/关层收闭——临时件不入库);S9/S10/S13归基线(codex头注日期清偿/_build_shell超局部变量按顶栏底栏拆)
108 lines
6.2 KiB
GDScript
108 lines
6.2 KiB
GDScript
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/forge_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"))
|