275 lines
9.7 KiB
GDScript
275 lines
9.7 KiB
GDScript
class_name FriendPanel
|
|
extends RefCounted
|
|
|
|
# 好友/排行面板:
|
|
# - 负责全国排行、好友排行、翻页和好友行渲染。
|
|
# - 排行接口返回的数据只在本面板分页状态中保存,不参与全局玩家状态。
|
|
# - 行按钮后续扩展访问好友时,应继续通过 callbacks 发回上层路由。
|
|
const NumberFormatterScript := preload("res://scripts/core/number_formatter.gd")
|
|
const I18nScript := preload("res://scripts/core/i18n.gd")
|
|
|
|
var _ui
|
|
var _texture_provider
|
|
var _friend_api
|
|
var _context
|
|
var _callbacks: Dictionary = {}
|
|
var _page_size := 7
|
|
var _rank_type := 0
|
|
var _show_type := 1
|
|
var _page_index := 1
|
|
var _page_max := 1
|
|
var _panel: Control
|
|
var _list: Control
|
|
var _page_label: Label
|
|
var _title_lv_label: Label
|
|
|
|
|
|
func setup(options: Dictionary) -> void:
|
|
_ui = options.get("ui_builder")
|
|
_texture_provider = options.get("texture_provider")
|
|
_friend_api = options.get("friend_api")
|
|
_context = options.get("context")
|
|
_callbacks = options.get("callbacks", {})
|
|
if _callbacks.is_empty() and _context != null and _context.has_method("legacy_callbacks"):
|
|
_callbacks = _context.legacy_callbacks()
|
|
_page_size = int(options.get("page_size", _page_size))
|
|
|
|
|
|
func create(rank_type: int) -> Control:
|
|
_rank_type = rank_type
|
|
_show_type = 1
|
|
_page_index = 1
|
|
_page_max = 1
|
|
_panel = _ui.create_common_panel_shell("FriendPanel", Vector2(594.0, 862.0), "panel_icon_friend", _texture_provider.atlas_path("friend"), "panel_title_friend", _texture_provider.atlas_path("friend"))
|
|
_ui.add_content_panel_background(_panel, "InnerPage", Vector2(88.0, 206.0), Vector2(464.0, 638.0))
|
|
_ui.create_tab_bar(_panel, "FriendTabs", ["全国排名", "好友排名"], Vector2(88.0, 159.0), _rank_type, Callable(self, "_on_tab_pressed"), 50.0)
|
|
|
|
_ui.add_panel_label(_panel, "TitleRank", "排名", Vector2(120.0, 233.0), Vector2(60.0, 32.0), 26, Color.html("#ffcc00"), 3, Color.html("#9c460b"))
|
|
_ui.add_panel_label(_panel, "TitleName", "游戏昵称", Vector2(201.0, 233.0), Vector2(108.0, 32.0), 26, Color.html("#ffcc00"), 3, Color.html("#9c460b"))
|
|
_title_lv_label = _ui.add_panel_label(_panel, "TitleLv", "等级", Vector2(335.0, 233.0), Vector2(72.0, 32.0), 26, Color.html("#ffcc00"), 3, Color.html("#9c460b"))
|
|
_ui.add_panel_label(_panel, "TitleHome", "家园", Vector2(413.0, 233.0), Vector2(64.0, 32.0), 26, Color.html("#ffcc00"), 3, Color.html("#9c460b"))
|
|
|
|
var scroll := ScrollContainer.new()
|
|
scroll.name = "FriendScroll"
|
|
scroll.position = Vector2(102.0, 278.0)
|
|
scroll.size = Vector2(446.0, 487.0)
|
|
scroll.clip_contents = true
|
|
_ui.configure_vertical_scroll(scroll)
|
|
_panel.add_child(scroll)
|
|
|
|
_list = Control.new()
|
|
_list.name = "FriendList"
|
|
_list.custom_minimum_size = Vector2(450.0, float(_page_size) * 70.0)
|
|
_list.size = Vector2(450.0, float(_page_size) * 70.0)
|
|
scroll.add_child(_list)
|
|
|
|
_ui.add_texture_button(_panel, "FriendPrev", _texture_provider.common("btn_page_prev"), Vector2(364.0, 791.0), Vector2(46.0, 41.0), "", Callable(self, "_on_prev_pressed"), 20)
|
|
_ui.add_texture_button(_panel, "FriendNext", _texture_provider.common("btn_page_next"), Vector2(481.0, 791.0), Vector2(46.0, 41.0), "", Callable(self, "_on_next_pressed"), 20)
|
|
_page_label = _ui.add_panel_label(_panel, "FriendPage", "1/1", Vector2(409.0, 800.0), Vector2(72.0, 28.0), 24, Color.html("#753f24"), 0, Color.TRANSPARENT)
|
|
|
|
_ui.add_texture_button(_panel, "FriendGold", _texture_provider.friend("friend_btn_caifu"), Vector2(94.0, 773.0), Vector2(72.0, 65.0), "", Callable(self, "_on_gold_pressed"), 20)
|
|
_ui.add_texture_button(_panel, "FriendLv", _texture_provider.friend("friend_btn_lv"), Vector2(173.0, 760.0), Vector2(70.0, 80.0), "", Callable(self, "_on_lv_pressed"), 20)
|
|
_ui.add_texture_button(_panel, "FriendPower", _texture_provider.friend("friend_btn_zhanli"), Vector2(253.0, 773.0), Vector2(66.0, 67.0), "", Callable(self, "_on_power_pressed"), 20)
|
|
return _panel
|
|
|
|
|
|
func list() -> Control:
|
|
return _list
|
|
|
|
|
|
func page_label() -> Label:
|
|
return _page_label
|
|
|
|
|
|
func title_lv_label() -> Label:
|
|
return _title_lv_label
|
|
|
|
|
|
func update_page() -> void:
|
|
var list := _list
|
|
if _friend_api == null or not _is_live_node(list):
|
|
return
|
|
_render_empty_rows(list)
|
|
_update_page_label()
|
|
|
|
var token := _token()
|
|
if token.is_empty():
|
|
return
|
|
var result: Dictionary = await _friend_api.fetch_list(token, _rank_type, _show_type, _page_index, _page_size)
|
|
if not _is_current_node(list, _list):
|
|
return
|
|
if not result.get("ok", false):
|
|
_show_toast("排行榜加载失败:%s" % result.get("game_status", result.get("status", 0)))
|
|
return
|
|
var body = result.get("body", {})
|
|
var data = body.get("data", {}) if body is Dictionary else {}
|
|
if not data is Dictionary:
|
|
return
|
|
_page_max = maxi(int(ceil(float(int(data.get("total_count", 0))) / float(_page_size))), 1)
|
|
_update_page_label()
|
|
_clear_children(list)
|
|
var rows = data.get("list", [])
|
|
var row_index := 0
|
|
if rows is Array:
|
|
for raw_row in rows:
|
|
if raw_row is Dictionary:
|
|
var row: Dictionary = raw_row.duplicate(true)
|
|
if not row.has("ranking"):
|
|
row["ranking"] = (_page_index - 1) * _page_size + row_index + 1
|
|
_add_row_to_list(list, _create_row(row), row_index)
|
|
row_index += 1
|
|
for index in range(row_index, _page_size):
|
|
_add_row_to_list(list, _create_row({}), index)
|
|
|
|
|
|
func _on_tab_pressed(index: int) -> void:
|
|
_rank_type = index
|
|
_page_index = 1
|
|
var tab_bar := _panel.get_node_or_null("FriendTabs") if _panel != null else null
|
|
_ui.update_tab_bar(tab_bar as Control, _rank_type)
|
|
update_page()
|
|
|
|
|
|
func _on_prev_pressed() -> void:
|
|
if _page_index <= 1:
|
|
return
|
|
_page_index -= 1
|
|
update_page()
|
|
|
|
|
|
func _on_next_pressed() -> void:
|
|
if _page_index >= _page_max:
|
|
return
|
|
_page_index += 1
|
|
update_page()
|
|
|
|
|
|
func _on_gold_pressed() -> void:
|
|
_show_type = 0
|
|
if _title_lv_label != null:
|
|
_title_lv_label.text = I18nScript.t("财富")
|
|
_page_index = 1
|
|
update_page()
|
|
|
|
|
|
func _on_lv_pressed() -> void:
|
|
_show_type = 1
|
|
if _title_lv_label != null:
|
|
_title_lv_label.text = I18nScript.t("等级")
|
|
_page_index = 1
|
|
update_page()
|
|
|
|
|
|
func _on_power_pressed() -> void:
|
|
_show_toast("战力排行暂未开放")
|
|
|
|
|
|
func _render_empty_rows(parent: Control) -> void:
|
|
if not _clear_children_if_live(parent):
|
|
return
|
|
for index in range(_page_size):
|
|
_add_row_to_list(parent, _create_row({}), index)
|
|
|
|
|
|
func _update_page_label() -> void:
|
|
if _is_live_node(_page_label):
|
|
_page_label.text = "%s/%s" % [_page_index, _page_max]
|
|
|
|
|
|
func _add_row_to_list(parent: Control, row: Control, index: int) -> void:
|
|
if not _is_live_node(parent):
|
|
return
|
|
row.position = Vector2(0.0, float(index) * 70.0)
|
|
parent.add_child(row)
|
|
|
|
|
|
func _create_row(row: Dictionary) -> Control:
|
|
var holder := Control.new()
|
|
holder.name = "FriendRow%s" % int(row.get("user_id", 0))
|
|
holder.custom_minimum_size = Vector2(450.0, 70.0)
|
|
holder.size = Vector2(450.0, 70.0)
|
|
_ui.add_texture(holder, "Bg", _texture_provider.rank("friend_item"), Vector2(0.0, 8.0), Vector2(430.0, 56.0))
|
|
if row.is_empty():
|
|
return holder
|
|
var rank := int(row.get("ranking", row.get("rank", 0)))
|
|
if rank >= 1 and rank <= 3:
|
|
_ui.add_texture(holder, "RankIcon", _texture_provider.rank("rank%s" % rank), Vector2(24.0, 15.0), Vector2(31.0, 40.0), TextureRect.STRETCH_KEEP_ASPECT_CENTERED)
|
|
else:
|
|
_ui.add_panel_label(holder, "Rank", str(rank), Vector2(19.0, 27.0), Vector2(39.0, 24.0), 24, Color.html("#753f24"), 0, Color.TRANSPARENT)
|
|
_ui.add_panel_label(holder, "Name", str(row.get("nickname", row.get("name", ""))).left(8), Vector2(46.0, 27.0), Vector2(193.0, 24.0), 20, Color.html("#753f24"), 0, Color.TRANSPARENT)
|
|
var value := int(row.get("level", row.get("lv", 0))) if _show_type == 1 else int(row.get("gold", 0))
|
|
_ui.add_panel_label(holder, "Value", NumberFormatterScript.compact(value), Vector2(200.0, 27.0), Vector2(113.0, 24.0), 20, Color.html("#753f24"), 0, Color.TRANSPARENT)
|
|
_ui.add_texture_button(holder, "Home", _texture_provider.rank("btn_friend_home"), Vector2(308.0, 6.0), Vector2(63.0, 59.0), "", Callable(self, "_on_home_pressed").bind(row.duplicate(true)), 20)
|
|
return holder
|
|
|
|
|
|
func _on_home_pressed(row: Dictionary) -> void:
|
|
await _visit_friend(row)
|
|
|
|
|
|
func _token() -> String:
|
|
return str(_call("token", [], ""))
|
|
|
|
|
|
func _show_toast(message: String) -> void:
|
|
_call("show_toast", [message])
|
|
|
|
|
|
func _visit_friend(row: Dictionary) -> void:
|
|
var target_id := _target_user_id(row)
|
|
if target_id <= 0:
|
|
_show_toast("好友数据异常")
|
|
return
|
|
var own_id := int(_call("player_id", [], 0))
|
|
if own_id > 0 and own_id == target_id:
|
|
_show_toast("这是你的农场,不用去啦!")
|
|
return
|
|
await _call_async("visit_friend_home", [row])
|
|
|
|
|
|
func _target_user_id(row: Dictionary) -> int:
|
|
for key in ["uid", "user_id", "id"]:
|
|
var value := int(row.get(key, 0))
|
|
if value > 0:
|
|
return value
|
|
return 0
|
|
|
|
|
|
func _call(name: String, args: Array = [], fallback: Variant = null) -> Variant:
|
|
if _context != null and _context.has_method("call_action"):
|
|
return _context.call_action(name, args, fallback)
|
|
var callable: Callable = _callbacks.get(name, Callable())
|
|
if not callable.is_valid():
|
|
return fallback
|
|
return callable.callv(args)
|
|
|
|
|
|
func _call_async(name: String, args: Array = []) -> Variant:
|
|
if _context != null and _context.has_method("call_action_async"):
|
|
return await _context.call_action_async(name, args)
|
|
var callable: Callable = _callbacks.get(name, Callable())
|
|
if not callable.is_valid():
|
|
return null
|
|
return await callable.callv(args)
|
|
|
|
|
|
func _clear_children(parent: Node) -> void:
|
|
for child in parent.get_children():
|
|
parent.remove_child(child)
|
|
child.queue_free()
|
|
|
|
|
|
func _is_live_node(node: Variant) -> bool:
|
|
return node != null and is_instance_valid(node) and node is Node
|
|
|
|
|
|
func _is_current_node(candidate: Variant, current: Variant) -> bool:
|
|
return _is_live_node(candidate) and _is_live_node(current) and candidate == current
|
|
|
|
|
|
func _clear_children_if_live(parent: Variant) -> bool:
|
|
if not _is_live_node(parent):
|
|
return false
|
|
_clear_children(parent as Node)
|
|
return true
|