273 lines
9.7 KiB
GDScript
273 lines
9.7 KiB
GDScript
# 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
|
||
|
||
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 文本) ---------
|
||
|
||
# 解析 `<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:
|
||
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
|