Files
mtgodot-poc/project/system_menu_ui_test.gd
T

192 lines
8.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# system_menu_ui_test —— ESC 系统菜单 + 游戏设置窗 headless 自检(真 uiscript + 假 client)。
# godot --headless --path project --script system_menu_ui_test.gd
# 校验 systemdialog.py / gameoptiondialog.py 装载、/setblockmode + /pkmode 真命令、
# /in_game_mall + /phase_select + /logout、显示开关持久化。缺 uiscript 时优雅跳过。
extends SceneTree
const GameOptionUI = preload("res://ui/game_option_ui.gd")
const SystemMenuUI = preload("res://ui/system_menu_ui.gd")
class FakeClient extends Node:
var says: Array = []
func say(type: int, text: String) -> bool:
says.append([type, text]); return true
func last_say() -> String:
return String(says[-1][1]) if not says.is_empty() else ""
class StubWin extends Node:
var opened := 0
func open() -> void: opened += 1
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: system_menu_ui_test (systemdialog + gameoptiondialog + /setblockmode /pkmode /phase_select)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _cfg_path() -> String:
return ProjectSettings.globalize_path(GameOptionUI.config_path())
func _has_say(fc, text: String) -> bool:
for s in fc.says:
if String(s[1]) == text:
return true
return false
func _run() -> void:
var assets := AssetRoot.path()
if not FileAccess.file_exists(assets.path_join("uiscript/uiscript/systemdialog.py")) \
or not FileAccess.file_exists(assets.path_join("uiscript/uiscript/gameoptiondialog.py")):
print(" (skip: no systemdialog.py / gameoptiondialog.py — assets checkout missing)")
return
if FileAccess.file_exists(_cfg_path()):
DirAccess.remove_absolute(_cfg_path())
var UiManager = load("res://ui/ui_manager.gd")
var ui: CanvasLayer = UiManager.new()
get_root().add_child(ui)
ui._ready()
var fc := FakeClient.new()
get_root().add_child(fc)
# ---------------- GameOptionUI ----------------
var go: Node = GameOptionUI.new()
get_root().add_child(go)
go.setup(ui, fc, assets)
go.open()
_ck(go.is_open(), "game_option window opened")
if not go.is_open():
return
# 屏蔽 toggle -> /setblockmode <mask ^ bit>EBlockAction 位)
(go._node("block_exchange_button") as BaseButton).pressed.emit()
_ck(fc.last_say() == "/setblockmode 1", "block exchange -> /setblockmode 1 (bit 1<<0)")
(go._node("block_party_button") as BaseButton).pressed.emit()
_ck(fc.last_say() == "/setblockmode 3", "block party -> /setblockmode 3 (1|2)")
(go._node("block_exchange_button") as BaseButton).pressed.emit()
_ck(fc.last_say() == "/setblockmode 2", "block exchange again -> /setblockmode 2 (bit cleared)")
_ck(go._block_mode == 2, "_block_mode tracked = 2")
# PK radio -> /pkmode {0,1,4,2}
(go._node("pvp_revenge") as BaseButton).pressed.emit()
_ck(fc.last_say() == "/pkmode 1", "pvp_revenge -> /pkmode 1")
(go._node("pvp_guild") as BaseButton).pressed.emit()
_ck(fc.last_say() == "/pkmode 4", "pvp_guild -> /pkmode 4")
_ck(not (go._node("pvp_revenge") as BaseButton).button_pressed, "pvp radio exclusive")
# 显示开关 -> 持久化 user://system_option.cfg [gameopt]
(go._node("always_show_name_off_button") as BaseButton).pressed.emit()
_ck(int(go._display.get("always_show_name", -1)) == 0, "always_show_name -> 0")
var cfg := ConfigFile.new()
cfg.load(_cfg_path())
_ck(int(cfg.get_value("gameopt", "always_show_name", -1)) == 0, "cfg gameopt.always_show_name persisted")
_ck(int(cfg.get_value("gameopt", "block_mode", -1)) == 2, "cfg gameopt.block_mode persisted")
# §8.4 名字色 radioname_color_normal / name_color_empire-> _display + cfg + 信号
var opt_evt := []
go.display_option_changed.connect(func(k, v): opt_evt.append([k, v]))
(go._node("name_color_empire") as BaseButton).pressed.emit()
_ck(int(go._display.get("name_color", -1)) == 1, "name_color_empire -> _display.name_color = 1")
_ck(opt_evt.size() >= 1 and opt_evt[-1] == ["name_color", 1], "name_color_empire -> display_option_changed('name_color',1)")
# §8.4 看他国目标框 radiotarget_board_view / target_board_no_viewconstinfo 默认 1 = 看)
_ck(int(go._display.get("target_board", -1)) == 1, "target_board 默认 1VIEW_OTHER_EMPIRE_PLAYER_TARGET_BOARD")
(go._node("target_board_no_view") as BaseButton).pressed.emit()
_ck(int(go._display.get("target_board", -1)) == 0, "target_board_no_view -> _display.target_board = 0")
_ck(opt_evt[-1] == ["target_board", 0], "target_board_no_view -> display_option_changed('target_board',0)")
var cfg2 := ConfigFile.new()
cfg2.load(_cfg_path())
_ck(int(cfg2.get_value("gameopt", "name_color", -1)) == 1, "cfg gameopt.name_color persisted")
_ck(int(cfg2.get_value("gameopt", "target_board", -1)) == 0, "cfg gameopt.target_board persisted")
go.close()
# 二次实例读 cfgblock_mode 回填
var go2: Node = GameOptionUI.new()
get_root().add_child(go2)
go2.setup(ui, fc, assets)
_ck(go2._block_mode == 2, "reload: block_mode from cfg")
go2.open()
_ck((go2._node("block_party_button") as BaseButton).button_pressed, "reload: block_party button reflects bit 1<<1")
_ck(not (go2._node("block_exchange_button") as BaseButton).button_pressed, "reload: block_exchange button off")
go2.close()
ui.set_mobile_mode(true)
go2.set_mobile_mode(true)
go2.open()
_ck(go2.is_open() and go2._mobile_root.size == Vector2(700, 360),
"mobile game-option window uses landscape base size")
_ck(go2._mobile_root.get_node_or_null("MobileGameOptionScroll") is ScrollContainer,
"mobile game-option settings are scrollable")
go2._toggle_block(1 << 0)
_ck(fc.last_say() == "/setblockmode 3", "mobile block toggle keeps server command")
go2.close()
# ---------------- SystemMenuUI ----------------
fc.says.clear()
var sys_opt := StubWin.new()
var game_opt := StubWin.new()
get_root().add_child(sys_opt)
get_root().add_child(game_opt)
var sm: Node = SystemMenuUI.new()
get_root().add_child(sm)
sm.setup(ui, fc, assets, sys_opt, game_opt)
sm.open()
_ck(sm.is_open(), "system menu opened")
(sm._node("mall_button") as BaseButton).pressed.emit()
_ck(_has_say(fc, "/in_game_mall"), "mall_button -> /in_game_mall")
_ck(not sm.is_open(), "mall_button closes menu")
sm.open()
(sm._node("change_button") as BaseButton).pressed.emit()
_ck(_has_say(fc, "/phase_select"), "change_button -> /phase_select (net.ExitGame)")
sm.open()
(sm._node("logout_button") as BaseButton).pressed.emit()
_ck(_has_say(fc, "/logout"), "logout_button -> /logout (net.LogOutGame)")
sm.open()
(sm._node("system_option_button") as BaseButton).pressed.emit()
_ck(sys_opt.opened == 1 and not sm.is_open(), "system_option_button -> opens system_option_ui + closes menu")
sm.open()
(sm._node("game_option_button") as BaseButton).pressed.emit()
_ck(game_opt.opened == 1 and not sm.is_open(), "game_option_button -> opens game_option_ui + closes menu")
sm.open()
(sm._node("cancel_button") as BaseButton).pressed.emit()
_ck(not sm.is_open(), "cancel_button closes menu")
# 移动端使用独立横屏菜单,仍复用同一组设置、帮助和服务器命令入口。
ui.set_mobile_mode(true)
sm.set_mobile_mode(true)
sm.open()
_ck(sm.is_open() and sm._mobile_root.size == Vector2(700, 330),
"mobile system menu uses landscape base size")
for action in ["帮助", "商城", "系统设置", "游戏设置", "选择角色", "登出", "退出游戏", "取消"]:
_ck(sm._mobile_buttons.has(action), "mobile system menu action: " + action)
(sm._mobile_buttons["商城"] as Button).pressed.emit()
_ck(_has_say(fc, "/in_game_mall"), "mobile mall action -> /in_game_mall")
_ck(not sm.is_open(), "mobile action closes menu")
sm.open()
(sm._mobile_buttons["系统设置"] as Button).pressed.emit()
_ck(sys_opt.opened == 2 and not sm.is_open(),
"mobile system option opens feature and closes menu")
sm.open()
(sm._mobile_buttons["游戏设置"] as Button).pressed.emit()
_ck(game_opt.opened == 2 and not sm.is_open(),
"mobile game option opens feature and closes menu")
sm.open()
(sm._mobile_buttons["取消"] as Button).pressed.emit()
_ck(not sm.is_open(), "mobile cancel closes menu")
if FileAccess.file_exists(_cfg_path()):
DirAccess.remove_absolute(_cfg_path())