# 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//locale_interface.txt`); # `ui_manager` 没接 locale 回调时,`helpwindow.py` 里的 `uiScriptLocale.X` 会被 # UiScript 解析成 `` 占位,`_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 const MobileTouchButton = preload("res://ui/mobile/mobile_touch_button.gd") # 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 # Locale(res://locale.gd) var _mobile_mode := false var _mobile_root: Control var _mobile_labels: Array[Label] = [] 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: if _mobile_mode: return _mobile_root != null and is_instance_valid(_mobile_root) return not _win.is_empty() and is_instance_valid(_win.get("root")) func set_mobile_mode(enabled: bool) -> void: if not enabled and _mobile_root != null: var old_mode := _mobile_mode _mobile_mode = true close() _mobile_mode = old_mode _mobile_mode = enabled func toggle() -> void: if is_open(): close() else: open() func close() -> void: if _mobile_mode: if _mobile_root and is_instance_valid(_mobile_root) and ui: ui.close(_mobile_root) _mobile_root = null _mobile_labels.clear() return if is_open(): ui.close(_win["root"]) _win = {} func open() -> void: if _mobile_mode: _open_mobile() return 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 _open_mobile() -> void: if is_open(): return _mobile_root = Control.new() _mobile_root.name = "MobileHelpWindow" _mobile_root.size = Vector2(760, 340) _mobile_root.set_meta("mobile_title", "帮助") _mobile_root.mouse_filter = Control.MOUSE_FILTER_STOP var panel := Panel.new() panel.set_anchors_preset(Control.PRESET_FULL_RECT) panel.add_theme_stylebox_override("panel", _mobile_panel_style()) _mobile_root.add_child(panel) var scroll := ScrollContainer.new() scroll.position = Vector2(14, 48) scroll.size = Vector2(732, 278) scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED scroll.mouse_filter = Control.MOUSE_FILTER_STOP _mobile_root.add_child(scroll) var list := VBoxContainer.new() list.custom_minimum_size = Vector2(710, 0) list.add_theme_constant_override("separation", 4) scroll.add_child(list) _mobile_labels.clear() for key in FLOAT_ROWS.values(): var row := Label.new() row.text = "• " + _text(String(key)) row.custom_minimum_size = Vector2(700, 28) row.vertical_alignment = VERTICAL_ALIGNMENT_CENTER row.add_theme_font_size_override("font_size", 13) row.add_theme_color_override("font_color", Color(0.82, 0.9, 1.0)) list.add_child(row) _mobile_labels.append(row) var task_hint := Label.new() task_hint.text = "\n" + _text("HELP_QUICKSLOT") + " · " + _text("HELP_SYSTEM_BUTTON") task_hint.add_theme_font_size_override("font_size", 11) task_hint.add_theme_color_override("font_color", Color(0.66, 0.76, 0.9)) list.add_child(task_hint) ui.open(_mobile_root) 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 文本) --------- # 解析 `` 占位 / 应用 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. 的取值:优先本工程 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: if _mobile_mode: var mobile_rows: Array = [] for key in FLOAT_ROWS.values(): mobile_rows.append(_text(String(key))) return mobile_rows 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 的多页按钮在单页版不存在;此处不绑。 func _mobile_panel_style() -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = Color(0.04, 0.065, 0.1, 0.98) style.border_color = Color(0.52, 0.68, 0.9, 0.86) style.set_border_width_all(1) style.set_corner_radius_all(8) return style