Files
mtgodot-poc/project/ui/system_menu_ui.gd
T
shenleiandClaude Sonnet 5 d7711ba5f0 40250 classic: increment 50 (Phase 1 W3) — §8.3 in-game help window
Implement the in-game Help window as a 1:1 migration of uihelp.py's
HelpWindow (single-page: ENABLE_HELP_MULTIPAGE = 0 -> LoadDialogSinglePage).

- project/ui/help_ui.gd (new): loads the real helpwindow.py uiscript via
  UiScript -> UiBuild; _relabel() resolves the <HELP_*>/<CLOSE> placeholders
  (produced when UiManager has no locale callback) from the project Locale
  table (en) with verbatim English fallbacks, covering the original script's
  two duplicate help_02 rows (right/middle camera). open/close/toggle/is_open;
  close_button -> close; ESC handled by the UiManager window stack.
- project/ui/system_menu_ui.gd: help_button changed from the placeholder toast
  to close() + open_help(); adds const HelpUI, an injectable help_ui member,
  _ensure_help() (lazy build, reuses the menu's ui/client/assets_root),
  open_help(), toggle_help(); setup() gains an optional 6th arg (old 5-arg
  callers unaffected).
- project/game_scene.gd: single elif in _unhandled_input — KEY_H without
  ctrl/meta -> system_menu_ui.toggle_help() (Ctrl+H stays free for
  /user_horse_ride).
- project/help_ui_test.gd (new): headless SceneTree test — helpwindow.py load,
  19 verbatim help rows + taskbar labels bound and non-placeholder,
  close_button text, rows() helper, close/open/toggle, ESC stack top,
  SystemMenuUI.open_help() lazy build.

Tests: help_ui_test, system_menu_ui_test, gamescene_test, ui_test all PASS
(plus system_option_ui_test, char_status_ui_test). No build/ in this worktree
so C++ ctest not run (no C++ change). Real-server and visual (.sub textures)
not verified. Docs: CLIENT-GAP.md W3 row + increment 50 bullet;
CLIENT-GAP-FIX.md §8.3 status/改动/测试/验收 + C.5 batch record.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014yvAPqPivoY7vmBbzgqK4W
2026-09-02 15:34:36 +09:00

141 lines
4.5 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.
# SystemMenuUI (P11) —— ESC 系统菜单(1:1 迁移 `assets/root/uisystem.py` `SystemDialog`
# 的 `__LoadSystemMenu_Default`;布局走真 `assets/uiscript/uiscript/systemdialog.py`)。
#
# var sm := preload("res://ui/system_menu_ui.gd").new()
# add_child(sm)
# sm.setup(ui_manager, m2client, assets_root, system_option_ui, game_option_ui)
# sm.toggle() # ESC(无其它窗打开时)
#
# 按钮绑定逐字对照 uisystem.py
# help_button → interface.OpenHelpWindowhelp_ui.gd,§8.3
# mall_button → net.SendChatPacket("/in_game_mall")
# system_option_button → uiSystemOption.OptionDialogsystem_option_ui
# game_option_button → uiGameOption.OptionDialoggame_option_ui
# change_button → net.ExitGame() = SendChatPacket("/phase_select")
# logout_button → net.LogOutGame() = SendChatPacket("/logout")
# exit_button → net.ExitApplication() = 退出进程
# cancel_button / 标题栏 → 关闭
extends Node
const HelpUI = preload("res://ui/help_ui.gd")
const LABELS := {
"help_button": "帮助", "mall_button": "商城", "system_option_button": "系统设置",
"game_option_button": "游戏设置", "change_button": "选择角色", "logout_button": "登出",
"exit_button": "退出游戏", "cancel_button": "取消",
}
var ui: CanvasLayer
var client: Node
var assets_root := ""
var uiscript_dir := ""
var system_option_ui: Node
var game_option_ui: Node
var help_ui: Node # HelpUI(§8.3)—— 未注入时 _ensure_help() 懒建
var toast: Callable = Callable() # 可选:func(msg: String) 显示提示
var _win: Dictionary = {}
func setup(ui_manager: CanvasLayer, m2client: Node, assets := "",
sys_opt: Node = null, game_opt: Node = null, help_win: Node = null) -> void:
ui = ui_manager
client = m2client
system_option_ui = sys_opt
game_option_ui = game_opt
help_ui = help_win
assets_root = assets
if assets_root == "" and ui and "assets_root" in ui:
assets_root = ui.assets_root
uiscript_dir = assets_root.path_join("uiscript/uiscript")
if not DirAccess.dir_exists_absolute(uiscript_dir):
uiscript_dir = assets_root.path_join("uiscript")
func is_open() -> bool:
return not _win.is_empty() and is_instance_valid(_win.get("root"))
func toggle() -> void:
if is_open(): close()
else: open()
func close() -> void:
if is_open():
ui.close(_win["root"])
_win = {}
func open() -> void:
if is_open():
return
var path := uiscript_dir.path_join("systemdialog.py")
if not FileAccess.file_exists(path):
push_warning("SystemMenuUI: no systemdialog.py at " + path)
return
_win = ui.open_script(path, assets_root, true) # modal
if not is_open():
return
_relabel()
_wire()
func _node(nm: String) -> Control:
if _win.is_empty():
return null
var n = _win.get("nodes", {}).get(nm, null)
return n if n is Control else null
func _relabel() -> void:
for nm in LABELS:
var n := _node(nm)
if n and n.has_method("set_text"):
n.set_text(LABELS[nm])
func _btn(nm: String, cb: Callable) -> void:
var b := _node(nm)
if b is BaseButton:
b.pressed.connect(cb)
# uisystem.py: self.interface.OpenHelpWindow()。未注入 help_ui 时懒建一个,
# 复用本菜单的 ui / client / assets_root。
func _ensure_help() -> Node:
if not is_instance_valid(help_ui):
help_ui = HelpUI.new()
add_child(help_ui)
help_ui.setup(ui, client, assets_root)
return help_ui
func open_help() -> void:
_ensure_help().open()
func toggle_help() -> void:
_ensure_help().toggle()
func _wire() -> void:
_btn("help_button", func():
close()
open_help())
_btn("mall_button", func():
if client and client.has_method("say"):
client.say(0, "/in_game_mall") # net.SendChatPacket("/in_game_mall")
close())
_btn("system_option_button", func():
close()
if system_option_ui and system_option_ui.has_method("open"):
system_option_ui.open())
_btn("game_option_button", func():
close()
if game_option_ui and game_option_ui.has_method("open"):
game_option_ui.open())
_btn("change_button", func():
if client and client.has_method("say"):
client.say(0, "/phase_select") # net.ExitGame()
close())
_btn("logout_button", func():
if client and client.has_method("say"):
client.say(0, "/logout") # net.LogOutGame()
close())
_btn("exit_button", func(): get_tree().quit())
_btn("cancel_button", close)
var board := _node("board")
if board:
for x in board.find_children("*", "BaseButton", true, false):
if x.get_meta("is_titlebar", false):
x.pressed.connect(close)