feat: complete mobile UI implementation
This commit is contained in:
+88
-8
@@ -17,6 +17,8 @@ const PlayerCtl = preload("res://player_controller.gd")
|
||||
const NetWorld = preload("res://net_world.gd")
|
||||
const NetPlay = preload("res://net_play.gd")
|
||||
const Hud = preload("res://hud.gd")
|
||||
const UiProfile = preload("res://ui/ui_profile.gd")
|
||||
const MobileUiRoot = preload("res://ui/mobile/mobile_ui_root.gd")
|
||||
const Audio = preload("res://audio.gd")
|
||||
const BgmDirector = preload("res://bgm_director.gd")
|
||||
const UiManager = preload("res://ui/ui_manager.gd")
|
||||
@@ -134,11 +136,13 @@ var _guild_invite_dialog: ConfirmationDialog
|
||||
var _gift_dialog: AcceptDialog
|
||||
var _quick_page_mode := false
|
||||
var _show_names := false
|
||||
var _ui_mobile := false
|
||||
|
||||
func setup(m2client: Node, assets_root: String,
|
||||
initial_map_path: String = "OutdoorA1/metin2_map_a1") -> void:
|
||||
client = m2client
|
||||
_assets = assets_root
|
||||
_ui_mobile = UiProfile.resolve() == UiProfile.Mode.MOBILE
|
||||
# GC_MAIN_CHARACTER only carries the server-space coordinates, not the map
|
||||
# folder. The old fixed A1 fallback puts characters from A2/A3 outside the
|
||||
# rendered terrain, which looks like an empty map. Resolve the map from the
|
||||
@@ -187,12 +191,17 @@ func setup(m2client: Node, assets_root: String,
|
||||
or int(pickable.get_meta("vid", -1)) == vid:
|
||||
pc.pickables.erase(pickable))
|
||||
|
||||
hud = Hud.new()
|
||||
if _map_loaded():
|
||||
hud.setup(world, player) # 含小地图 / 快捷栏 / 背包
|
||||
else:
|
||||
if _ui_mobile:
|
||||
hud = MobileUiRoot.new()
|
||||
add_child(hud)
|
||||
hud.call("_build_status") # 无地图兜底:只搭状态栏(血 / 蓝 / 经验 / 等级)
|
||||
hud.setup(world, player, client)
|
||||
else:
|
||||
hud = Hud.new()
|
||||
add_child(hud)
|
||||
if _map_loaded():
|
||||
hud.setup(world, player) # 含小地图 / 快捷栏 / 背包
|
||||
else:
|
||||
hud.call("_build_status") # 无地图兜底:只搭状态栏(血 / 蓝 / 经验 / 等级)
|
||||
|
||||
net_play = NetPlay.new()
|
||||
add_child(net_play)
|
||||
@@ -541,6 +550,9 @@ func setup(m2client: Node, assets_root: String,
|
||||
if client.has_signal("channel_changed") and hud and hud.has_method("set_channel"):
|
||||
client.channel_changed.connect(func(c): hud.set_channel(c))
|
||||
|
||||
if _ui_mobile and hud and hud.has_method("bind_controls"):
|
||||
_setup_mobile_ui()
|
||||
|
||||
client.entity_main_set.connect(_on_main_set)
|
||||
if client.get_main_vid() != 0: # 重连 / 已在局内
|
||||
# net_play._on_main_set 的信号早在它连上前就发过了 —— 手动补上关键那步:
|
||||
@@ -558,8 +570,50 @@ func setup(m2client: Node, assets_root: String,
|
||||
net_world.catch_up()
|
||||
|
||||
set_process_unhandled_input(true)
|
||||
|
||||
func _setup_mobile_ui() -> void:
|
||||
# MobileUiRoot owns the presentation and touch layer; every feature node in
|
||||
# this dictionary remains the existing PC/business controller.
|
||||
hud.bind_controls({
|
||||
"client": client,
|
||||
"ui": ui,
|
||||
"lifecycle": _lifecycle_node(),
|
||||
"player_controller": pc,
|
||||
"net_play": net_play,
|
||||
"quickbar": quickbar,
|
||||
"ground_items": ground_items,
|
||||
"inventory": inventory,
|
||||
"atlas_ui": atlas_ui,
|
||||
"quest_log": quest_log,
|
||||
"party_ui": party_ui,
|
||||
"char_status_ui": char_status_ui,
|
||||
"skills": skills,
|
||||
"chat": chat,
|
||||
"friend_ui": friend_ui,
|
||||
"guild_ui": guild_ui,
|
||||
"love_ui": love_ui,
|
||||
"refine_ui": refine_ui,
|
||||
"cube_ui": cube_ui,
|
||||
"mall_ui": mall_ui,
|
||||
"shop_ui": shop_ui,
|
||||
"exchange_ui": exchange_ui,
|
||||
"safebox_ui": safebox_ui,
|
||||
"private_shop_ui": private_shop_ui,
|
||||
"system_menu_ui": system_menu_ui,
|
||||
"system_option_ui": system_option_ui,
|
||||
"game_option_ui": game_option_ui,
|
||||
"minimap": minimap,
|
||||
})
|
||||
set_process(true)
|
||||
|
||||
func _lifecycle_node() -> Node:
|
||||
# AppFlow owns the single lifecycle observer across login/select/game. The
|
||||
# game scene only borrows it for Android back and focus recovery.
|
||||
var owner := get_parent()
|
||||
if owner:
|
||||
return owner.get_node_or_null("AppLifecycle")
|
||||
return null
|
||||
|
||||
func _process(_dt: float) -> void:
|
||||
# MilesLib::CSoundManager::SetPosition/SetDirection + Update. The audio
|
||||
# node keeps its listener at the origin and rebases active 3D instances.
|
||||
@@ -591,14 +645,17 @@ func _on_guild_invite(guild_id: int, guild_name: String) -> void:
|
||||
_guild_invite_dialog.dialog_text = "加入公会“%s”?" % guild_name
|
||||
_guild_invite_dialog.ok_button_text = "加入"
|
||||
_guild_invite_dialog.cancel_button_text = "拒绝"
|
||||
_guild_invite_dialog.set_meta("mobile_title", "公会邀请")
|
||||
ui.add_child(_guild_invite_dialog)
|
||||
if ui.has_method("track_mobile_modal"):
|
||||
ui.track_mobile_modal(_guild_invite_dialog)
|
||||
_guild_invite_dialog.confirmed.connect(func():
|
||||
client.guild_answer_invite(guild_id, true)
|
||||
_guild_invite_dialog.queue_free())
|
||||
_guild_invite_dialog.canceled.connect(func():
|
||||
client.guild_answer_invite(guild_id, false)
|
||||
_guild_invite_dialog.queue_free())
|
||||
_guild_invite_dialog.popup_centered()
|
||||
_popup_guild_invite()
|
||||
|
||||
func _on_gift_available() -> void:
|
||||
# 40250's Gift_Show has no corresponding claim packet in the shipped
|
||||
@@ -607,17 +664,40 @@ func _on_gift_available() -> void:
|
||||
if ui == null:
|
||||
return
|
||||
if _gift_dialog and is_instance_valid(_gift_dialog):
|
||||
_gift_dialog.popup_centered()
|
||||
_popup_gift()
|
||||
return
|
||||
_gift_dialog = AcceptDialog.new()
|
||||
_gift_dialog.title = "礼物"
|
||||
_gift_dialog.dialog_text = "有新的礼物可查看。"
|
||||
_gift_dialog.ok_button_text = "知道了"
|
||||
_gift_dialog.set_meta("mobile_title", "礼物")
|
||||
ui.add_child(_gift_dialog)
|
||||
if ui.has_method("track_mobile_modal"):
|
||||
ui.track_mobile_modal(_gift_dialog)
|
||||
_gift_dialog.confirmed.connect(func():
|
||||
if is_instance_valid(_gift_dialog):
|
||||
_gift_dialog.queue_free())
|
||||
_gift_dialog.popup_centered()
|
||||
_popup_gift()
|
||||
|
||||
func _popup_guild_invite() -> void:
|
||||
if not is_instance_valid(_guild_invite_dialog):
|
||||
return
|
||||
if _ui_mobile:
|
||||
_guild_invite_dialog.min_size = Vector2i(500, 230)
|
||||
_guild_invite_dialog.size = Vector2i(500, 230)
|
||||
_guild_invite_dialog.popup_centered(Vector2i(500, 230))
|
||||
else:
|
||||
_guild_invite_dialog.popup_centered()
|
||||
|
||||
func _popup_gift() -> void:
|
||||
if not is_instance_valid(_gift_dialog):
|
||||
return
|
||||
if _ui_mobile:
|
||||
_gift_dialog.min_size = Vector2i(460, 210)
|
||||
_gift_dialog.size = Vector2i(460, 210)
|
||||
_gift_dialog.popup_centered(Vector2i(460, 210))
|
||||
else:
|
||||
_gift_dialog.popup_centered()
|
||||
|
||||
# .msa MotionEventData 分派(对齐 GameLib/RaceMotionData EMotionEventType):
|
||||
# 1 EFFECT / 10 EFFECT_TO_TARGET → 挂特效;2 SCREEN_WAVING → 震屏;
|
||||
|
||||
Reference in New Issue
Block a user