# select_item_ui_test —— P11 选魔石窗 headless 自检(假 client + 假 proto + 真 uiscript)。 # godot --headless --path project --script select_item_ui_test.gd # 校验 uiselectitem.RefreshSlot 过滤(只 metin 类型 + grade<=2)、SelectItemSlot 发包、 # Close 发 0、quest_dialog [SELECT_ITEM] token 解析。缺 uiscript 资产时优雅跳过。 extends SceneTree const SelectItemUI = preload("res://ui/select_item_ui.gd") const QuestDialog = preload("res://ui/quest_dialog.gd") class FakeClient extends Node: signal inventory_changed(window: int, cell: int) var inv: Array = [] # [{cell,vnum,count}] var selects: Array = [] # script_select_item 参数 func get_inventory() -> Array: return inv func script_select_item(sel: int) -> bool: selects.append(sel); return true class FakeProto extends Node: # vnum 约定:28030/28031/28032 = metin grade 0/1/2;28963 = metin grade 3; # 19 = 非 metin(武器) func item(vnum: int) -> Dictionary: match vnum: 28030: return {"type": 10, "name": "METIN_A0", "locale_name": "魔石A"} 28031: return {"type": 10, "name": "METIN_B1", "locale_name": "魔石B"} 28032: return {"type": 10, "name": "METIN_C2", "locale_name": "魔石C"} 28963: return {"type": 10, "name": "METIN_D3", "locale_name": "魔石D"} 19: return {"type": 1, "name": "SWORD", "locale_name": "剑"} return {} var _fail := 0 func _ck(c: bool, m: String) -> void: if not c: _fail += 1 printerr("FAIL: " + m) func _init() -> void: _run() if _fail == 0: print("PASS: select_item_ui_test (filter + select packet + close-0 + [SELECT_ITEM] token)") quit(0) else: printerr("%d check(s) failed" % _fail) quit(1) func _run() -> void: # --- quest_dialog [SELECT_ITEM] token 解析(不需资产) --- var qd: Node = QuestDialog.new() var p1: Dictionary = qd.parse_script("请选择一颗魔石[ENTER][SELECT_ITEM]") _ck(p1.get("has_select_item", false) == true, "[SELECT_ITEM] -> has_select_item") var p2: Dictionary = qd.parse_script("普通对话[NEXT]") _ck(p2.get("has_select_item", true) == false, "no tag -> has_select_item false") _ck(qd.has_signal("select_item_requested"), "QuestDialog exposes select_item_requested") qd.free() var assets := AssetRoot.path() if not FileAccess.file_exists(assets.path_join("uiscript/uiscript/selectitemwindow.py")): print(" (skip window part: no selectitemwindow.py — assets checkout missing)") return var UiManager = load("res://ui/ui_manager.gd") var ui: CanvasLayer = UiManager.new() get_root().add_child(ui) ui._ready() var fc := FakeClient.new() var fp := FakeProto.new() get_root().add_child(fc) get_root().add_child(fp) # 背包:cell 2 = grade0 metin, cell 5 = 非 metin(排除), cell 7 = grade2 metin, # cell 9 = grade3 metin(排除), cell 11 = grade1 metin fc.inv = [ {"cell": 2, "vnum": 28030, "count": 1}, {"cell": 5, "vnum": 19, "count": 1}, {"cell": 7, "vnum": 28032, "count": 3}, {"cell": 9, "vnum": 28963, "count": 1}, {"cell": 11, "vnum": 28031, "count": 1}, ] var si: Node = SelectItemUI.new() get_root().add_child(si) si.setup(ui, fc, fp, assets) si.open() _ck(si.is_open(), "select-item window opened") if not si.is_open(): return # 过滤后应保留 3 颗(cell 2 / 7 / 11),按背包格号升序填入选择窗格 0/1/2 _ck(si._slot_to_inv.size() == 3, "3 metin stones kept (got %d)" % si._slot_to_inv.size()) _ck(si._slot_to_inv.get(0, -1) == 2, "slot 0 -> inv cell 2 (grade0)") _ck(si._slot_to_inv.get(1, -1) == 7, "slot 1 -> inv cell 7 (grade2)") _ck(si._slot_to_inv.get(2, -1) == 11, "slot 2 -> inv cell 11 (grade1)") _ck(not si._slot_to_inv.values().has(5), "non-metin (cell 5) excluded") _ck(not si._slot_to_inv.values().has(9), "grade>2 metin (cell 9) excluded") # 点选择窗格 1 -> 发 SendSelectItemPacket(7) + 关窗,且不再补发 0 var cell1: Panel = si._cells.get(1, null) _ck(cell1 != null, "slot cell 1 present") var ev := InputEventMouseButton.new() ev.button_index = MOUSE_BUTTON_LEFT ev.pressed = true cell1.gui_input.emit(ev) _ck(fc.selects == [7], "click slot 1 -> script_select_item(7) (got %s)" % str(fc.selects)) _ck(not si.is_open(), "window closed after select") # 重新打开,直接关闭 -> 发 script_select_item(0)(uiselectitem.Close) fc.selects.clear() si.open() _ck(si.is_open(), "reopened") si.close() _ck(fc.selects == [0], "close without pick -> script_select_item(0) (got %s)" % str(fc.selects)) _ck(not si.is_open(), "window closed")