hy-farm/godot/FarmGodot/scripts/ui/panels/player_info_panel.gd
2026-05-29 19:54:56 +08:00

435 lines
19 KiB
GDScript

class_name PlayerInfoPanel
extends PanelModule
const NumberFormatterScript := preload("res://scripts/core/number_formatter.gd")
const UiMotionScript := preload("res://scripts/ui/common/ui_motion.gd")
const SKIN_SMALL_TYPE := 990
const DEFAULT_SKIN_ITEM_ID := 104004
const DEFAULT_SKIN_GRAPHIC_ID := 5004
var _farm_api
var _panel: Control
var _setting_popup: Control
var _skin_popup: Control
var _music_open := true
var _effect_open := true
func setup(options: Dictionary) -> void:
super.setup(options)
_farm_api = options.get("farm_api")
var master_bus := AudioServer.get_bus_index("Master")
_music_open = master_bus < 0 or not AudioServer.is_bus_mute(master_bus)
func create() -> Control:
var role_atlas: String = _texture_provider.atlas_path("roleinfo")
_panel = _ui.create_panel_shell(
"PlayerInfoPanel",
Vector2(600.0, 835.0),
_texture_provider.common("panel_icon_warehouse"),
_texture_provider.frame(role_atlas, "panel_title_role_info_png")
)
_ui.add_content_panel_background(_panel, "RoleInfoPage", Vector2(76.5, 151.0), Vector2(487.0, 306.0))
_render_role_info()
_render_achievement_list()
return _panel
func _render_role_info() -> void:
var role_atlas: String = _texture_provider.atlas_path("roleinfo")
var create_role_atlas: String = _texture_provider.atlas_path("createrole")
var player: Dictionary = _player_data()
_ui.add_texture(_panel, "RoleBg", _texture_provider.frame(create_role_atlas, "role_item_bg_png"), Vector2(84.0, 167.0), Vector2(160.0, 164.0))
_ui.add_texture(_panel, "HeadBg", _texture_provider.common("item_bg"), Vector2(105.0, 190.0), Vector2(123.0, 122.0))
var head_clip := Control.new()
head_clip.name = "HeadClip"
head_clip.position = Vector2(101.0, 188.0)
head_clip.size = Vector2(130.0, 125.0)
head_clip.clip_contents = true
head_clip.mouse_filter = Control.MOUSE_FILTER_IGNORE
_panel.add_child(head_clip)
_ui.add_texture(head_clip, "Head", _avatar_texture(player), Vector2.ZERO, head_clip.size, TextureRect.STRETCH_KEEP_ASPECT_COVERED)
_add_role_frame("NameTitle", "role_item_name_png", Vector2(256.72, 180.0))
_add_role_frame("IdTitle", "role_item_id_png", Vector2(256.72, 219.0))
_add_role_frame("LvTitle", "role_item_lv_png", Vector2(255.72, 253.0))
_add_role_frame("PowerTitle", "role_item__png", Vector2(256.0, 289.0))
_add_role_frame("GoldIcon", "role_item_smallgold_png", Vector2(255.72, 331.0))
_add_role_frame("SteelIcon", "role_item_smallsteel_png", Vector2(258.72, 377.0))
_add_role_frame("StoneIcon", "role_item_smallstone_png", Vector2(384.72, 379.0))
_ui.add_panel_label(_panel, "RoleName", str(player.get("nickname", player.get("username", ""))).left(7), Vector2(389.72, 181.0), Vector2(150.0, 25.0), 22, Color.html("#fffbf2"), 2, Color.html("#724f2e"), HORIZONTAL_ALIGNMENT_LEFT)
_ui.add_panel_label(_panel, "RoleId", _player_id_text(player), Vector2(389.72, 219.0), Vector2(133.0, 26.0), 24, Color.html("#fffbf2"), 2, Color.html("#724f2e"), HORIZONTAL_ALIGNMENT_LEFT)
_ui.add_panel_label(_panel, "RoleLv", "LV.%s" % int(player.get("level", 1)), Vector2(389.72, 253.0), Vector2(145.0, 26.0), 24, Color.html("#fffbf2"), 2, Color.html("#724f2e"), HORIZONTAL_ALIGNMENT_LEFT)
_ui.add_panel_label(_panel, "RolePower", NumberFormatterScript.compact(int(player.get("level", 1)) * 100), Vector2(390.0, 289.0), Vector2(168.0, 26.0), 24, Color.html("#fffbf2"), 2, Color.html("#724f2e"), HORIZONTAL_ALIGNMENT_LEFT)
_ui.add_panel_label(_panel, "RoleGold", NumberFormatterScript.compact(int(player.get("gold", 0))), Vector2(296.72, 339.0), Vector2(100.0, 22.0), 20, Color.html("#fffbf2"), 2, Color.html("#724f2e"), HORIZONTAL_ALIGNMENT_LEFT)
_ui.add_panel_label(_panel, "RoleSteel", NumberFormatterScript.compact(int(player.get("steel", 0))), Vector2(299.72, 385.0), Vector2(100.0, 22.0), 20, Color.html("#fffbf2"), 2, Color.html("#724f2e"), HORIZONTAL_ALIGNMENT_LEFT)
_ui.add_panel_label(_panel, "RoleStone", NumberFormatterScript.compact(int(player.get("stone", 0))), Vector2(425.72, 385.0), Vector2(100.0, 22.0), 20, Color.html("#fffbf2"), 2, Color.html("#724f2e"), HORIZONTAL_ALIGNMENT_LEFT)
var setting_button := _add_purple_button(_panel, "SettingButton", Vector2(98.0, 351.0), Vector2(142.0, 52.0), "系统设置", Callable(self, "_open_setting_panel"))
_style_purple_button(setting_button)
func _render_achievement_list() -> void:
var role_atlas: String = _texture_provider.atlas_path("roleinfo")
_ui.add_content_panel_background(_panel, "AchievementPage", Vector2(76.5, 457.26), Vector2(487.0, 357.45))
var scroll := ScrollContainer.new()
scroll.name = "AchievementScroll"
scroll.position = Vector2(84.5, 475.37)
scroll.size = Vector2(479.0, 326.66)
scroll.clip_contents = true
_ui.configure_vertical_scroll(scroll)
_panel.add_child(scroll)
var margin := MarginContainer.new()
margin.name = "AchievementMargin"
margin.custom_minimum_size = Vector2(479.0, 0.0)
margin.add_theme_constant_override("margin_left", 12)
margin.add_theme_constant_override("margin_top", 84)
margin.add_theme_constant_override("margin_right", 0)
margin.add_theme_constant_override("margin_bottom", 12)
scroll.add_child(margin)
var grid := GridContainer.new()
grid.name = "AchievementList"
grid.columns = 5
grid.custom_minimum_size = Vector2(456.0, 0.0)
grid.add_theme_constant_override("h_separation", 2)
grid.add_theme_constant_override("v_separation", 8)
margin.add_child(grid)
for index in range(22):
grid.add_child(_create_achievement_item(index))
var title: TextureRect = _ui.add_texture(_panel, "AchievementTitle", _texture_provider.frame(role_atlas, "role_chengjiuTitle_png"), Vector2(143.0, 418.0), Vector2(354.0, 62.0))
title.z_index = 3
func _create_achievement_item(index: int) -> Control:
var holder := Control.new()
holder.name = "Achievement%s" % index
holder.custom_minimum_size = Vector2(90.0, 94.0)
holder.size = Vector2(90.0, 94.0)
holder.mouse_filter = Control.MOUSE_FILTER_IGNORE
_ui.add_texture(holder, "Bg", _texture_provider.common("item_bg"), Vector2.ZERO, Vector2(90.0, 90.0))
return holder
func _open_setting_panel() -> void:
_close_setting_panel()
_close_skin_panel()
_setting_popup = Control.new()
_setting_popup.name = "SettingPanel"
_setting_popup.size = _panel_screen_size
_setting_popup.z_index = 40
_setting_popup.mouse_filter = Control.MOUSE_FILTER_STOP
_panel.add_child(_setting_popup)
var mask := Button.new()
mask.name = "SettingMask"
mask.set_anchors_preset(Control.PRESET_FULL_RECT)
mask.text = ""
mask.focus_mode = Control.FOCUS_NONE
UiMotionScript.apply_empty_button_style(mask)
_setting_popup.add_child(mask)
_ui.add_texture(_setting_popup, "Bg", _texture_provider.standalone("res://assets/egret/assets/game/friend/friend_alert.png"), Vector2(90.0, 228.7), Vector2(460.0, 500.0))
_ui.add_texture(_setting_popup, "Title", _texture_provider.standalone("res://assets/egret/assets/common/panel_set_title.png"), Vector2(196.5, 192.2), Vector2(247.0, 73.0))
_add_setting_label("MusicText", "音乐:", Vector2(129.0, 345.0), Vector2(65.0, 28.0))
_add_setting_label("EffectText", "音效:", Vector2(317.0, 344.0), Vector2(65.0, 28.0))
_add_setting_label("AccountText", "帐号:", Vector2(132.0, 452.21), Vector2(65.0, 28.0))
_add_setting_label("SkinText", "装扮:", Vector2(317.0, 452.21), Vector2(65.0, 28.0))
_add_setting_label("LanguageText", "语言:", Vector2(132.0, 542.0), Vector2(92.0, 28.0))
var open_music := _add_purple_button(_setting_popup, "OpenMusicBtn", Vector2(188.0, 333.0), Vector2(121.0, 49.0), "开启", Callable(self, "_set_music_open").bind(true))
var close_music := _add_purple_button(_setting_popup, "CloseMusicBtn", Vector2(188.0, 333.0), Vector2(121.0, 49.0), "关闭", Callable(self, "_set_music_open").bind(false))
var open_effect := _add_purple_button(_setting_popup, "OpenEffectBtn", Vector2(376.0, 332.0), Vector2(121.0, 49.0), "开启", Callable(self, "_set_effect_open").bind(true))
var close_effect := _add_purple_button(_setting_popup, "CloseEffectBtn", Vector2(376.0, 332.0), Vector2(121.0, 49.0), "关闭", Callable(self, "_set_effect_open").bind(false))
_add_purple_button(_setting_popup, "ChangeAccountBtn", Vector2(191.0, 438.71), Vector2(121.0, 49.0), "切换帐号", Callable(self, "_reload_game"))
_add_purple_button(_setting_popup, "SkinButton", Vector2(376.0, 438.71), Vector2(121.0, 49.0), "装扮设置", Callable(self, "_open_skin_panel"))
_add_purple_button(_setting_popup, "LanguageEnglishBtn", Vector2(276.0, 528.0), Vector2(170.0, 49.0), I18nScript.language_label(I18nScript.current_locale()), Callable(self, "_set_language").bind(I18nScript.LOCALE_EN))
_ui.add_texture_button(_setting_popup, "ClosePanelBtn", _texture_provider.common("c_btn1"), Vector2(220.0, 683.7), Vector2(200.0, 70.0), "关闭", Callable(self, "_close_setting_panel"), 36)
for button in [open_music, close_music, open_effect, close_effect]:
_style_purple_button(button)
for name in ["ChangeAccountBtn", "SkinButton", "LanguageEnglishBtn"]:
_style_purple_button(_setting_popup.get_node_or_null(name) as Control)
_sync_setting_buttons()
_play_overlay_open(_setting_popup)
func _close_setting_panel() -> void:
if _setting_popup != null and is_instance_valid(_setting_popup):
_setting_popup.queue_free()
_setting_popup = null
func _sync_setting_buttons() -> void:
if _setting_popup == null:
return
var open_music := _setting_popup.get_node_or_null("OpenMusicBtn") as Control
var close_music := _setting_popup.get_node_or_null("CloseMusicBtn") as Control
var open_effect := _setting_popup.get_node_or_null("OpenEffectBtn") as Control
var close_effect := _setting_popup.get_node_or_null("CloseEffectBtn") as Control
if open_music != null:
open_music.visible = not _music_open
if close_music != null:
close_music.visible = _music_open
if open_effect != null:
open_effect.visible = not _effect_open
if close_effect != null:
close_effect.visible = _effect_open
func _set_music_open(value: bool) -> void:
_music_open = value
var master_bus := AudioServer.get_bus_index("Master")
if master_bus >= 0:
AudioServer.set_bus_mute(master_bus, not _music_open)
_sync_setting_buttons()
func _set_effect_open(value: bool) -> void:
_effect_open = value
_sync_setting_buttons()
func _set_language(locale: String) -> void:
I18nScript.set_locale(locale)
_close_setting_panel()
_open_setting_panel()
_show_toast("语言已切换")
func _reload_game() -> void:
if OS.has_feature("web"):
JavaScriptBridge.eval("window.location.href = window.location.href")
return
if _owner != null and _owner.get_tree() != null:
_owner.get_tree().reload_current_scene()
func _open_skin_panel() -> void:
_close_setting_panel()
_close_skin_panel()
await _refresh_store_house()
_skin_popup = _create_local_panel_shell(
"SkinPanel",
Vector2(640.0, 734.0),
_texture_provider.frame(_texture_provider.atlas_path("common_json"), "panel_icon_skin_png"),
_texture_provider.frame(_texture_provider.atlas_path("common_json"), "panel_title_skin_png"),
Callable(self, "_close_skin_panel")
)
_skin_popup.z_index = 45
_panel.add_child(_skin_popup)
var scroll := ScrollContainer.new()
scroll.name = "SkinScroll"
scroll.position = Vector2(41.0, 191.0)
scroll.size = Vector2(558.0, 590.0)
scroll.clip_contents = true
_ui.configure_vertical_scroll(scroll)
_skin_popup.add_child(scroll)
var list := VBoxContainer.new()
list.name = "SkinList"
list.custom_minimum_size = Vector2(558.0, 0.0)
list.add_theme_constant_override("separation", 8)
scroll.add_child(list)
var skins := _skin_items()
skins.sort_custom(func(left: Dictionary, right: Dictionary) -> bool:
return int((left.get("config", {}) as Dictionary).get("id", left.get("item_id", 0))) < int((right.get("config", {}) as Dictionary).get("id", right.get("item_id", 0)))
)
for item in skins:
list.add_child(_create_skin_item(item))
_play_overlay_open(_skin_popup)
func _close_skin_panel() -> void:
if _skin_popup != null and is_instance_valid(_skin_popup):
_skin_popup.queue_free()
_skin_popup = null
func _create_skin_item(item: Dictionary) -> Control:
var config: Dictionary = item.get("config", {})
var holder := Control.new()
holder.name = "SkinItem%s" % int(config.get("id", item.get("item_id", 0)))
holder.custom_minimum_size = Vector2(558.0, 380.0)
holder.size = Vector2(558.0, 380.0)
holder.mouse_filter = Control.MOUSE_FILTER_STOP
_ui.add_texture(holder, "Bg", _texture_provider.standalone("res://assets/egret/assets/game/landup/building_housebg.png"), Vector2(30.0, 36.0), Vector2(489.0, 336.0))
var icon_id := int(config.get("icon", item.get("icon_id", item.get("item_id", DEFAULT_SKIN_GRAPHIC_ID))))
_ui.add_texture(holder, "Icon", _skin_icon_texture(icon_id), Vector2(44.0, 56.0), Vector2(460.0, 284.0), TextureRect.STRETCH_KEEP_ASPECT_CENTERED)
var current := _is_current_skin(config)
var curr: TextureRect = _ui.add_texture(holder, "CurrentTitle", _texture_provider.frame(_texture_provider.atlas_path("common_json"), "skin_title_curr_png"), Vector2(151.0, 5.0), Vector2(267.0, 83.0))
curr.visible = current
var hit := Button.new()
hit.name = "Hit"
hit.position = Vector2.ZERO
hit.size = holder.size
hit.text = ""
hit.focus_mode = Control.FOCUS_NONE
UiMotionScript.apply_empty_button_style(hit)
hit.pressed.connect(Callable(self, "_change_skin").bind(int(config.get("id", item.get("item_id", 0)))))
hit.button_down.connect(func() -> void: UiMotionScript.play_press_scale(_owner, holder))
hit.button_up.connect(func() -> void: UiMotionScript.play_release_scale(_owner, holder))
holder.add_child(hit)
return holder
func _change_skin(item_id: int) -> void:
if item_id <= 0 or _is_busy():
return
var item_config: Dictionary = _game_config.get_item_config(item_id)
if _is_current_skin(_game_config.normalize_item_config(item_config)):
return
var token := _token()
if token.is_empty():
_show_toast("登录信息失效")
return
_set_busy(true)
var result: Dictionary = {"ok": false, "game_status": "missing-api"}
if _farm_api != null:
result = await _farm_api.change_bg(token, item_id)
_set_busy(false)
if not result.get("ok", false):
_show_toast(_response_status_text(result, "装扮切换失败"))
return
var player := _player_data()
player["bg_image_id"] = item_id
_call("render_scene_skin")
_refresh_skin_current_tags()
func _refresh_skin_current_tags() -> void:
if _skin_popup == null:
return
var list := _skin_popup.get_node_or_null("SkinScroll/SkinList") as VBoxContainer
if list == null:
return
for child in list.get_children():
if not child is Control:
continue
var tag := child.get_node_or_null("CurrentTitle") as TextureRect
if tag == null:
continue
var item_id := int(str(child.name).trim_prefix("SkinItem"))
tag.visible = _is_current_skin(_game_config.normalize_item_config(_game_config.get_item_config(item_id)))
func _skin_items() -> Array[Dictionary]:
var result: Array[Dictionary] = []
for raw_item in _call("warehouse_items", [], []):
if not raw_item is Dictionary:
continue
var item: Dictionary = raw_item
var config: Dictionary = item.get("config", {})
if int(config.get("smallType", 0)) == SKIN_SMALL_TYPE:
result.append(item.duplicate(true))
return result
func _is_current_skin(config: Dictionary) -> bool:
if config.is_empty():
return false
var player := _player_data()
var current_item_id := int(player.get("bg_image_id", DEFAULT_SKIN_ITEM_ID))
var current_config: Dictionary = _game_config.get_item_config(current_item_id)
var current_graphic := int(current_config.get("graphical_id", current_item_id if current_item_id > 0 else DEFAULT_SKIN_GRAPHIC_ID))
return current_graphic == int(config.get("icon", config.get("graphical_id", config.get("id", 0))))
func _create_local_panel_shell(panel_name: String, panel_size: Vector2, icon: Texture2D, title: Texture2D, close_callback: Callable) -> Control:
var root := Control.new()
root.name = panel_name
root.size = _panel_screen_size
root.mouse_filter = Control.MOUSE_FILTER_STOP
var mask := Button.new()
mask.name = "ModalMask"
mask.set_anchors_preset(Control.PRESET_FULL_RECT)
mask.text = ""
mask.focus_mode = Control.FOCUS_NONE
UiMotionScript.apply_empty_button_style(mask)
root.add_child(mask)
var bg_position := Vector2((_panel_screen_size.x - panel_size.x) * 0.5, (_panel_screen_size.y - panel_size.y) * 0.5)
_ui.add_panel_shell_background(root, "PanelBg", bg_position, panel_size)
_ui.add_texture(root, "PanelTitle", title, Vector2(331.0 - title.get_width() * 0.5, bg_position.y - 18.0), title.get_size())
_ui.add_texture(root, "PanelIcon", icon, Vector2(142.5 - icon.get_width() * 0.5, bg_position.y + 66.0 - icon.get_height()), icon.get_size())
_ui.add_texture_button(root, "PanelClose", _texture_provider.common("c_btn1"), Vector2(220.0, bg_position.y + panel_size.y - 63.0), Vector2(200.0, 70.0), "离开", close_callback, 36)
return root
func _add_role_frame(name: String, frame_name: String, position: Vector2) -> void:
var texture: Texture2D = _texture_provider.frame(_texture_provider.atlas_path("roleinfo"), frame_name)
_ui.add_texture(_panel, name, texture, position, texture.get_size())
func _add_setting_label(name: String, text: String, position: Vector2, size: Vector2) -> void:
_ui.add_panel_label(_setting_popup, name, text, position, size, 22, Color.html("#fffef7"), 2, Color.html("#685036"), HORIZONTAL_ALIGNMENT_LEFT)
func _add_purple_button(parent: Control, name: String, position: Vector2, size: Vector2, text: String, callback: Callable) -> Control:
var holder: Control = _ui.add_texture_button(parent, name, _texture_provider.sign("sign_btn_purple"), position, size, text, callback, 26)
_style_purple_button(holder)
return holder
func _style_purple_button(button: Control) -> void:
if button == null:
return
var label := button.get_node_or_null("Label") as Label
if label != null:
_ui.style_label(label, 26, Color.WHITE, 2, Color.html("#643591"))
func _play_overlay_open(popup: Control) -> void:
if popup == null or _owner == null:
return
popup.pivot_offset = _panel_screen_size * 0.5
popup.scale = Vector2(0.9, 0.9)
popup.modulate = Color(1.0, 1.0, 1.0, 0.0)
var tween := _owner.create_tween().set_parallel(true)
tween.tween_property(popup, "scale", Vector2.ONE, 0.18).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
tween.tween_property(popup, "modulate:a", 1.0, 0.12)
func _avatar_texture(player: Dictionary) -> Texture2D:
var avatar_id := str(player.get("avatar", player.get("head", "1")))
var path := "res://assets/egret/head/head%s.png" % avatar_id
if not ResourceLoader.exists(path):
path = "res://assets/egret/head/head1.png"
return load(path)
func _skin_icon_texture(icon_id: int) -> Texture2D:
var path := "res://assets/egret/assets/icon/icon/%s.png" % icon_id
if not ResourceLoader.exists(path):
path = "res://assets/egret/assets/icon/skin/%s.jpg" % icon_id
if not ResourceLoader.exists(path):
path = "res://assets/egret/assets/icon/icon/%s.png" % DEFAULT_SKIN_GRAPHIC_ID
return load(path)
func _player_id_text(player: Dictionary) -> String:
if player.has("id"):
return _clean_numeric_text(player.get("id", ""))
if player.has("user_id"):
return _clean_numeric_text(player.get("user_id", ""))
return str(player.get("username", ""))
func _clean_numeric_text(value: Variant) -> String:
if value is int:
return str(value)
if value is float:
var number := float(value)
if is_equal_approx(number, roundf(number)):
return str(int(roundf(number)))
return str(value)