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)