# popup_windows_parity_test.gd —— 校验弹窗(人物属性面板、仓库、背包等) # 严格按照 40250 客户端的 UI 规范与关闭行为。 # godot --headless --path project --script popup_windows_parity_test.gd extends SceneTree const UiManager = preload("res://ui/ui_manager.gd") const CharStatusUI = preload("res://ui/char_status_ui.gd") const SafeboxUI = preload("res://ui/safebox_ui.gd") const InventoryUI = preload("res://ui/inventory_ui.gd") const AssetRoot = preload("res://asset_root.gd") class FakeClient extends Node: signal points_changed(points: Dictionary) signal entity_main_set(vid: int) signal safebox_opened(size: int, gold: int) signal safebox_closed() signal safebox_item_set(cell: int, vnum: int, count: int, flags: int, anti_flags: int, sockets: Array, attrs: Array) signal inventory_changed(window: int, cell: int) var pts: Array = [] var _main_vid := 7 func _init() -> void: pts.resize(255) pts.fill(0) pts[1] = 42 pts[5] = 850 pts[6] = 900 pts[7] = 120 pts[8] = 150 func get_main_vid() -> int: return _main_vid func get_points() -> Dictionary: return { "points": pts, "hp": pts[5], "max_hp": pts[6], "sp": pts[7], "max_sp": pts[8], "level": pts[1], "exp": 0, "next_exp": 1000, "gold": 10000, } func get_entity(vid: int) -> Dictionary: return {"name": "TestHero", "guild": 1, "race": 0} func get_guild_name(gid: int) -> String: return "HeroGuild" if gid == 1 else "" func get_inventory() -> Array: return [] func get_equipment() -> Array: return [] func get_belt_inventory() -> Array: return [] func get_safebox_items() -> Array: return [] func get_safebox_gold() -> int: return 5000 func get_safebox_size() -> int: return 45 func safebox_close() -> void: pass func say(type: int, text: String) -> bool: return true 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: popup_windows_parity_test (CharacterWindow, SafeboxWindow, InventoryWindow layout + 40250 close behaviors)") quit(0) else: printerr("%d check(s) failed" % _fail) quit(1) func _run() -> void: var assets := AssetRoot.path() if not FileAccess.file_exists(assets.path_join("uiscript/uiscript/characterwindow.py")): print(" (skip: missing uiscript assets)") return var ui: CanvasLayer = UiManager.new() get_root().add_child(ui) ui._ready() var client := FakeClient.new() get_root().add_child(client) # ========================================================================= # 1. CharacterWindow # ========================================================================= var char_ui := CharStatusUI.new() get_root().add_child(char_ui) char_ui.setup(ui, client, assets) char_ui.open("STATUS") _ck(char_ui.is_open(), "CharacterWindow is_open() after open('STATUS')") var char_root: Control = char_ui._win.get("root") _ck(char_root != null, "CharacterWindow root node exists") if char_root: _ck(char_root.position.x >= 0.0, "CharacterWindow position.x (%f) >= 0" % char_root.position.x) _ck(char_root.position.y >= 0.0, "CharacterWindow position.y (%f) >= 0" % char_root.position.y) # Face slot / Guild slot auto-deduction of dimensions var face_slot: Control = char_ui._node("Face_Slot") if face_slot: _ck(face_slot.size.x > 0 and face_slot.size.y > 0, "Face_Slot has non-zero size: %s" % str(face_slot.size)) var guild_slot: Control = char_ui._node("Guild_Name_Slot") if guild_slot: _ck(guild_slot.size.x > 0 and guild_slot.size.y > 0, "Guild_Name_Slot has non-zero size: %s" % str(guild_slot.size)) # Tab button type check: must be Button with toggle_mode, NOT CheckBox var tab1: Control = char_ui._node("Tab_Button_01") if tab1: _ck(tab1 is Button and not (tab1 is CheckBox), "Tab_Button_01 is Button (not CheckBox)") _ck((tab1 as Button).toggle_mode, "Tab_Button_01 has toggle_mode = true") # Close button inside TitleBar var close_btn: BaseButton = char_root.find_child("CloseButton", true, false) _ck(close_btn != null, "CharacterWindow has CloseButton in titlebar") if close_btn: close_btn.pressed.emit() _ck(not char_ui.is_open(), "CharacterWindow closes when CloseButton is pressed") # ========================================================================= # 2. SafeboxWindow # ========================================================================= var safe_ui := SafeboxUI.new() get_root().add_child(safe_ui) safe_ui.setup(client, ui, null, assets) safe_ui.open() _ck(safe_ui.is_open(), "SafeboxWindow is_open() after open()") var safe_root: Control = safe_ui._root _ck(safe_root != null, "SafeboxWindow _root node exists") if safe_root: _ck(safe_root.position.x >= 0.0, "SafeboxWindow position.x (%f) >= 0 (not negative off-screen)" % safe_root.position.x) _ck(safe_root.position.y >= 0.0, "SafeboxWindow position.y (%f) >= 0 (not negative off-screen)" % safe_root.position.y) # Page buttons check: page radio buttons are Buttons with toggle_mode, not CheckBoxes var page1: Control = safe_root.find_child("PAGE1_BUTTON", true, false) if page1: _ck(page1 is Button and not (page1 is CheckBox), "PAGE1_BUTTON is Button (not CheckBox)") # CloseButton on TitleBar var safe_close_btn: BaseButton = safe_root.find_child("CloseButton", true, false) _ck(safe_close_btn != null, "SafeboxWindow has CloseButton in titlebar") if safe_close_btn: safe_close_btn.pressed.emit() _ck(not safe_ui.is_open(), "SafeboxWindow closes when CloseButton is pressed") # Re-open and test ExitButton ("关闭" button at bottom) safe_ui.open() _ck(safe_ui.is_open(), "SafeboxWindow re-opened") if safe_ui.is_open(): var exit_btn: BaseButton = safe_ui._root.find_child("ExitButton", true, false) _ck(exit_btn != null, "SafeboxWindow has ExitButton at bottom") if exit_btn: exit_btn.pressed.emit() _ck(not safe_ui.is_open(), "SafeboxWindow closes when ExitButton is pressed") # ========================================================================= # 3. InventoryWindow # ========================================================================= var inv_ui := InventoryUI.new() get_root().add_child(inv_ui) inv_ui.setup(ui, client, null, assets) inv_ui.open() _ck(inv_ui.is_open(), "InventoryWindow is_open() after open()") var inv_root: Control = inv_ui._win.get("root") _ck(inv_root != null, "InventoryWindow root node exists") if inv_root: _ck(inv_root.position.x >= 0.0, "InventoryWindow position.x (%f) >= 0" % inv_root.position.x) _ck(inv_root.position.y >= 0.0, "InventoryWindow position.y (%f) >= 0" % inv_root.position.y) var inv_close_btn: BaseButton = inv_root.find_child("CloseButton", true, false) _ck(inv_close_btn != null, "InventoryWindow has CloseButton in TitleBar") if inv_close_btn: inv_close_btn.pressed.emit() _ck(not inv_ui.is_open(), "InventoryWindow closes when CloseButton is pressed")