Files
mtgodot-poc/project/ui/help_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

198 lines
7.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.
# HelpUI (P11 / §8.3) —— 游戏内帮助窗(1:1 迁移 `assets/root/uihelp.py` 的
# `HelpWindow`,单页版:`ENABLE_HELP_MULTIPAGE = 0` → `LoadDialogSinglePage()`)。
#
# var hw := preload("res://ui/help_ui.gd").new()
# add_child(hw)
# hw.setup(ui_manager, m2client, assets_root)
# hw.toggle() # H 键(非 Ctrl
# hw.open() # ESC 系统菜单 help_button
#
# 布局走真 `assets/uiscript/uiscript/helpwindow.py`UiScript → UiBuild)。
# 文本逐字对齐 `uiScriptLocale.HELP_*``locale/locale/<lang>/locale_interface.txt`);
# `ui_manager` 没接 locale 回调时,`helpwindow.py` 里的 `uiScriptLocale.X` 会被
# UiScript 解析成 `<X>` 占位,`_relabel()` 再用本工程的 `Locale` 表 + 英文兜底补回。
#
# 入口逐字对照 `game.py:459 __PressHKey()`:非 Ctrl → `interface.OpenHelpWindow()`
# Ctrl+H 是 `net.SendChatPacket("/user_horse_ride")`,不在本窗职责内)。
# 关闭对照 `helpwindow.py` 的 `HelpWindow``close_button` / `OnPressEscapeKey` /
# `OnPressExitKey` / `OnKeyDown(DIK_H)` 都调用 `eventClose`。ESC 由 `UiManager`
# 的窗口栈统一处理(关最顶层);H 键复用 `toggle()`。
extends Node
# helpwindow.py 的 name → uiScriptLocale key(逐字,含原脚本里重名的两个 `help_02`)。
const FLOAT_ROWS := {
"help_01": "HELP_MOVE_KEY",
"help_02": "HELP_CONTROL_CAMERA_BY_MIDDLEBUTTON", # 原脚本第二个 help_02(后者覆盖)
"help_03": "HELP_SHOW_ALL_NAME",
"help_04": "HELP_OPEN_CHAT",
"help_05": "HELP_OPEN_WHISPER",
"help_06": "HELP_ATTACK_KEY",
"help_07": "HELP_OPEN_CHARACTER",
"help_08": "HELP_OPEN_SKILL",
"help_09": "HELP_OPEN_QUEST",
"help_10": "HELP_OPEN_INVENTORY",
"help_11": "HELP_OPEN_LOG",
"help_12": "HELP_OPEN_ZONEMAP",
"help_13": "HELP_OPEN_MINIMAP",
"help_14": "HELP_CHANGE_PK_MODE",
"help_15": "HELP_PICK_ITEM",
"help_16": "HELP_SCREEN_CAPTURE",
"help_17": "HELP_GUILD_WINDOW",
"help_18": "HELP_MESSENGER_WINDOW",
"help_19": "HELP_HELP",
}
# 任务栏贴纸标签(helpwindow.py 的 `taskbar_help_*` 文本)。
const TASKBAR_ROWS := {
"taskbar_help_01": "HELP_FURY",
"taskbar_help_02": "HELP_HP",
"taskbar_help_03": "HELP_SP",
"taskbar_help_04": "HELP_EXP",
"taskbar_help_05": "HELP_MOUSE_LEFT",
"taskbar_help_06": "HELP_MOUSE_RIGHT",
"taskbar_help_07": "HELP_QUICKSLOT",
"taskbar_help_08a": "HELP_SYSTEM_BUTTON",
"taskbar_help_08b": "HELP_CHARACTER_BUTTON1",
"taskbar_help_08c": "HELP_CHARACTER_BUTTON2",
}
# uiScriptLocale 兜底(逐字取自 `locale/locale/en/locale_interface.txt`)——
# 资产缺 locale 表或 UiManager 没接 locale 回调时用这份。
const FALLBACK := {
"HELP_MOVE_KEY": "Panel: W, A, S, D or arrow keys",
"HELP_CONTROL_CAMERA_BY_RIGHTBUTTON": "Camera View: right or middle mouse button",
"HELP_CONTROL_CAMERA_BY_MIDDLEBUTTON": "Camera View: middle or right mouse button",
"HELP_SHOW_ALL_NAME": "Display Names: Alt",
"HELP_OPEN_CHAT": "Open chat window: Enter",
"HELP_OPEN_WHISPER": "Open Whisper Window: Shift + Enter",
"HELP_ATTACK_KEY": "Attack: left mouse button or space bar",
"HELP_OPEN_CHARACTER": "Open character window: C",
"HELP_OPEN_SKILL": "Open Skill Window: V",
"HELP_OPEN_QUEST": "Open Task Window: N",
"HELP_OPEN_INVENTORY": "Open inventory window: I",
"HELP_OPEN_LOG": "Open Chatlog: L",
"HELP_OPEN_ZONEMAP": "Open Large Map: M",
"HELP_OPEN_MINIMAP": "Open mini map: Shift + M",
"HELP_CHANGE_PK_MODE": "Change attack mode: change setting of left mouse button",
"HELP_PICK_ITEM": "Collect Items: ^ or Y or left mouse button",
"HELP_SCREEN_CAPTURE": "Save Screenshot: Print (will be saved in file \"Metin2\\screenshot\")",
"HELP_GUILD_WINDOW": "Open Guild Window: Alt + G",
"HELP_MESSENGER_WINDOW": "Open Friends List: Alt + M",
"HELP_HELP": "Display help: H",
"HELP_FURY": "Ingame Item Shop",
"HELP_HP": "Hit Points (HP)",
"HELP_SP": "Spell Points (SP)",
"HELP_EXP": "Experience",
"HELP_MOUSE_LEFT": "Function of the Left Mouse button",
"HELP_MOUSE_RIGHT": "Function of the Right Mouse button",
"HELP_QUICKSLOT": "Fast Access Fields",
"HELP_SYSTEM_BUTTON": "System buttons",
"HELP_CHARACTER_BUTTON1": "(Character Window, Inventory Window,",
"HELP_CHARACTER_BUTTON2": "List of Friends and Options)",
"CLOSE": "Close",
}
var ui: CanvasLayer
var client: Node
var assets_root := ""
var uiscript_dir := ""
var _win: Dictionary = {}
var _loc # Localeres://locale.gd
func setup(ui_manager: CanvasLayer, m2client: Node = null, assets := "") -> void:
ui = ui_manager
client = m2client
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")
var LocaleScript = load("res://locale.gd")
if LocaleScript:
_loc = LocaleScript.new()
_loc.setup(assets_root, "en")
# --- open / close ---------------------------------------------------
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("helpwindow.py")
if not FileAccess.file_exists(path):
push_warning("HelpUI: no helpwindow.py at " + path)
return
_win = ui.open_script(path, assets_root)
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
# --- content (uihelp.py.LoadDialogSinglePage + helpwindow.py 文本) ---------
# 解析 `<KEY>` 占位 / 应用 name→key 映射:先扫所有 Label(覆盖原脚本重名的两个
# `help_02`),再按映射兜一遍(nodes dict 只留最后一个同名)。
func _relabel() -> void:
var root: Control = _win.get("root")
if root:
for lbl in root.find_children("*", "Label", true, false):
var t := String(lbl.text)
if t.length() >= 2 and t.begins_with("<") and t.ends_with(">"):
lbl.text = _text(t.substr(1, t.length() - 2))
for nm in FLOAT_ROWS:
_set_text(nm, _text(FLOAT_ROWS[nm]))
for nm in TASKBAR_ROWS:
_set_text(nm, _text(TASKBAR_ROWS[nm]))
_set_text("close_button", _text("CLOSE"))
func _set_text(nm: String, s: String) -> void:
var n := _node(nm)
if n == null:
return
if n.has_method("set_text"):
n.set_text(s)
elif "text" in n:
n.text = s
# uiScriptLocale.<key> 的取值:优先本工程 Locale 表,否则英文兜底,否则原样 key。
func _text(key: String) -> String:
if _loc != null and _loc.has(key):
return _loc.t(key)
return FALLBACK.get(key, key)
# 已绑定的帮助行文本(供自检 / 调试)。
func rows() -> Array:
var out: Array = []
for nm in FLOAT_ROWS:
var n := _node(nm)
if n and "text" in n:
out.append(String(n.text))
return out
# --- wiring (uihelp.py.HelpWindow.SetCloseEvent) ------------------------
func _wire() -> void:
var btn := _node("close_button")
if btn is BaseButton:
btn.pressed.connect(close)
# helpwindow.py2 的多页按钮在单页版不存在;此处不绑。