feat: complete mobile UI implementation
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
# —— 客户端显示开关,持久化到 user://system_option.cfg [gameopt];渲染节点按需读取这些设置。
|
||||
extends Node
|
||||
|
||||
const MobileTouchButton = preload("res://ui/mobile/mobile_touch_button.gd")
|
||||
# Packet.h EBlockAction
|
||||
const BLOCK_BITS := {
|
||||
"block_exchange_button": 1 << 0,
|
||||
@@ -73,6 +74,8 @@ var _win: Dictionary = {}
|
||||
var _cfg := ConfigFile.new()
|
||||
var _block_mode := 0 # blockMode(本地跟踪 + 服务端回包同步)
|
||||
var _display := {} # cfg [gameopt] 快照
|
||||
var _mobile_mode := false
|
||||
var _mobile_root: Control
|
||||
|
||||
static func config_path() -> String:
|
||||
var override_dir := OS.get_environment("MT_CONFIG_DIR")
|
||||
@@ -98,8 +101,18 @@ func setup(ui_manager: CanvasLayer, m2client: Node, assets := "") -> void:
|
||||
client.block_mode_changed.connect(_on_server_block_mode)
|
||||
|
||||
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
|
||||
|
||||
# 渲染层开局回填用:cfg [gameopt] 里某显示开关的当前值(0/1),缺省同 setup() 的默认。
|
||||
func display_value(key: String) -> int:
|
||||
return int(_display.get(key, 0))
|
||||
@@ -109,11 +122,19 @@ func toggle() -> void:
|
||||
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
|
||||
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("gameoptiondialog.py")
|
||||
@@ -127,6 +148,128 @@ func open() -> void:
|
||||
_wire()
|
||||
_sync()
|
||||
|
||||
func _open_mobile() -> void:
|
||||
if is_open():
|
||||
return
|
||||
_mobile_root = Control.new()
|
||||
_mobile_root.name = "MobileGameOptionWindow"
|
||||
_mobile_root.size = Vector2(700, 360)
|
||||
_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.name = "MobileGameOptionScroll"
|
||||
scroll.position = Vector2(14, 48)
|
||||
scroll.size = Vector2(672, 298)
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
scroll.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_mobile_root.add_child(scroll)
|
||||
var content := VBoxContainer.new()
|
||||
content.custom_minimum_size = Vector2(650, 0)
|
||||
content.add_theme_constant_override("separation", 8)
|
||||
scroll.add_child(content)
|
||||
_add_mobile_section_title(content, "屏蔽设置")
|
||||
_add_mobile_block_buttons(content)
|
||||
_add_mobile_section_title(content, "PK 模式")
|
||||
_add_mobile_pk_radios(content)
|
||||
_add_mobile_section_title(content, "显示设置")
|
||||
_add_mobile_display_radios(content)
|
||||
ui.open(_mobile_root)
|
||||
|
||||
func _add_mobile_section_title(parent: VBoxContainer, text: String) -> void:
|
||||
var title := Label.new()
|
||||
title.text = text
|
||||
title.custom_minimum_size = Vector2(640, 28)
|
||||
title.add_theme_font_size_override("font_size", 14)
|
||||
title.add_theme_color_override("font_color", Color(0.95, 0.82, 0.54))
|
||||
parent.add_child(title)
|
||||
|
||||
func _add_mobile_block_buttons(parent: VBoxContainer) -> void:
|
||||
var grid := GridContainer.new()
|
||||
grid.columns = 3
|
||||
grid.custom_minimum_size = Vector2(640, 94)
|
||||
grid.add_theme_constant_override("h_separation", 6)
|
||||
grid.add_theme_constant_override("v_separation", 6)
|
||||
parent.add_child(grid)
|
||||
for name in BLOCK_BITS:
|
||||
var button := Button.new()
|
||||
button.text = String(LABELS.get(name, name))
|
||||
button.toggle_mode = true
|
||||
button.button_pressed = (_block_mode & int(BLOCK_BITS[name])) != 0
|
||||
button.custom_minimum_size = Vector2(206, 42)
|
||||
var bit := int(BLOCK_BITS[name])
|
||||
button.pressed.connect(func(): _toggle_block(bit))
|
||||
grid.add_child(button)
|
||||
|
||||
func _add_mobile_pk_radios(parent: VBoxContainer) -> void:
|
||||
var row := HBoxContainer.new()
|
||||
row.custom_minimum_size = Vector2(640, 44)
|
||||
row.add_theme_constant_override("separation", 6)
|
||||
var buttons: Array[Button] = []
|
||||
for name in ["pvp_peace", "pvp_revenge", "pvp_guild", "pvp_free"]:
|
||||
var button := Button.new()
|
||||
button.text = String(LABELS.get(name, name))
|
||||
button.toggle_mode = true
|
||||
button.custom_minimum_size = Vector2(145, 42)
|
||||
button.button_pressed = false
|
||||
var picked: String = name
|
||||
button.pressed.connect(func():
|
||||
for other in buttons.size():
|
||||
buttons[other].set_pressed_no_signal(buttons[other] == button)
|
||||
if client and client.has_method("say"):
|
||||
client.say(0, "/pkmode %d" % PK_CMD[picked]))
|
||||
buttons.append(button)
|
||||
row.add_child(button)
|
||||
var current: Variant = _cfg.get_value("gameopt", "pk_mode", -1)
|
||||
if int(current) in PK_CMD.values():
|
||||
for i in PK_CMD.keys().size():
|
||||
if int(PK_CMD[PK_CMD.keys()[i]]) == int(current):
|
||||
buttons[i].button_pressed = true
|
||||
parent.add_child(row)
|
||||
|
||||
func _add_mobile_display_radios(parent: VBoxContainer) -> void:
|
||||
for group in RADIO_GROUPS:
|
||||
var row := HBoxContainer.new()
|
||||
row.custom_minimum_size = Vector2(640, 44)
|
||||
row.add_theme_constant_override("separation", 6)
|
||||
var key: String = String(DISPLAY_RADIOS[group[0]][0])
|
||||
var caption := Label.new()
|
||||
caption.text = String(LABELS.get(key, key))
|
||||
caption.custom_minimum_size = Vector2(126, 40)
|
||||
caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
caption.add_theme_font_size_override("font_size", 12)
|
||||
row.add_child(caption)
|
||||
var buttons: Array[Button] = []
|
||||
var current := int(_display.get(key, 0))
|
||||
for name in group:
|
||||
var button := Button.new()
|
||||
button.text = String(LABELS.get(name, name))
|
||||
button.toggle_mode = true
|
||||
button.custom_minimum_size = Vector2(112, 42)
|
||||
button.button_pressed = int(DISPLAY_RADIOS[name][1]) == current
|
||||
var selected: String = name
|
||||
button.pressed.connect(func():
|
||||
for other in buttons.size():
|
||||
buttons[other].set_pressed_no_signal(buttons[other] == button)
|
||||
var spec: Array = DISPLAY_RADIOS[selected]
|
||||
_display[String(spec[0])] = int(spec[1])
|
||||
_save()
|
||||
display_option_changed.emit(String(spec[0]), int(spec[1])))
|
||||
buttons.append(button)
|
||||
row.add_child(button)
|
||||
parent.add_child(row)
|
||||
|
||||
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
|
||||
|
||||
func _node(nm: String) -> Control:
|
||||
if _win.is_empty():
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user