class_name OnlineGiftPanel extends PanelModule # 在线礼包面板: # - 负责在线时长礼包的状态、倒计时和领取。 # - 倒计时只在面板打开期间刷新;领取成功后重新拉取服务器状态。 # - 奖励统一写入库存 Facade,避免活动面板直接改 player_data。 var _list: VBoxContainer var _started_at := 0 var _timer: Timer var _panel: Control const CONTENT_BG_POSITION := Vector2(76.0, 250.0) const CONTENT_BG_SIZE := Vector2(488.0, 494.0) const SCROLL_POSITION := Vector2(82.0, 258.0) const SCROLL_SIZE := Vector2(474.0, 470.0) const LIST_WIDTH := 468.0 const ROW_SIZE := Vector2(468.0, 118.0) const ROW_SEPARATION := 18 func open() -> void: _close_modal(false) if _started_at <= 0: _started_at = int(Time.get_unix_time_from_system()) var title: Texture2D = _texture_provider.frame(_texture_provider.atlas_path("gift"), "onLineGift_Title1_png") var icon: Texture2D = _texture_provider.frame(_texture_provider.atlas_path("gift"), "onLineGift_Gift_png") _panel = _ui.create_panel_shell("OnlineGiftPanel", Vector2(600.0, 780.0), icon, title) _ui.add_content_panel_background(_panel, "InnerPage", CONTENT_BG_POSITION, CONTENT_BG_SIZE) var scroll := ScrollContainer.new() scroll.name = "OnlineGiftScroll" scroll.position = SCROLL_POSITION scroll.size = SCROLL_SIZE scroll.clip_contents = true _ui.configure_vertical_scroll(scroll) scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER _panel.add_child(scroll) _list = VBoxContainer.new() _list.name = "OnlineGiftList" _list.custom_minimum_size = Vector2(LIST_WIDTH, 0.0) _list.add_theme_constant_override("separation", ROW_SEPARATION) scroll.add_child(_list) _start_timer() render() _open_modal(_panel) func render() -> void: if _list == null or not is_instance_valid(_list): return _clear_children(_list) var claimed_index := int(_player_data().get("online_gift_get_count", 0)) % 7 for gift in _game_config.get_online_gift_config(): var status_data := _status(gift, claimed_index) _add_row(_list, gift, int(status_data.get("status", 2)), int(status_data.get("left", 0))) func _start_timer() -> void: if _timer == null: _timer = Timer.new() _timer.wait_time = 1.0 _timer.timeout.connect(_on_timer) if _owner != null: _owner.add_child(_timer) if _timer != null and _timer.is_stopped(): _timer.start() func _on_timer() -> void: if _panel != null and is_instance_valid(_panel) and _panel.get_parent() != null: render() func _status(gift: Dictionary, claimed_index: int) -> Dictionary: var id := int(gift.get("id", 0)) if claimed_index >= id: return {"status": 0, "left": 0} if claimed_index != id - 1: return {"status": 2, "left": int(gift.get("receive_time", 0))} var elapsed := int(Time.get_unix_time_from_system()) - _started_at var left := maxi(int(gift.get("receive_time", 0)) - elapsed, 0) return {"status": 1 if left <= 0 else 2, "left": left} func _add_row(parent: Control, gift: Dictionary, status: int, left: int) -> void: var holder := Control.new() holder.name = "OnlineGift%s" % int(gift.get("id", 0)) holder.custom_minimum_size = ROW_SIZE holder.size = ROW_SIZE parent.add_child(holder) _ui.add_nine_patch(holder, "Bg", _texture_provider.frame(_texture_provider.atlas_path("common2"), "common_itemrender_png"), Vector2.ZERO, holder.size, Vector4(95.0, 70.0, 350.0, 108.0)) _ui.add_texture(holder, "GiftIcon", _texture_provider.frame(_texture_provider.atlas_path("gift"), "onLineGift_Gift_png"), Vector2(12.0, 16.0), Vector2(72.0, 74.0), TextureRect.STRETCH_KEEP_ASPECT_CENTERED) _ui.add_panel_label(holder, "Title", "在线礼包 %s" % int(gift.get("id", 0)), Vector2(92.0, 12.0), Vector2(158.0, 28.0), 24, Color.html("#fff75f"), 2, Color.html("#7d1c1c"), HORIZONTAL_ALIGNMENT_LEFT) _add_cost_slots(holder, _reward_fields_to_text(gift, 1, 4), Vector2(92.0, 45.0), 4, 60.0, Vector2(56.0, 56.0)) var frame := "onLineGift_lingqu_png" if status == 1 else ("onLineGift_yilingqu_png" if status == 0 else "onLineGift_lingqud_png") var button_text := "领取" if status == 1 else ("已领取" if status == 0 else _format_duration(left)) var button: Control = _ui.add_texture_button(holder, "Claim", _texture_provider.frame(_texture_provider.atlas_path("gift"), frame), Vector2(344.0, 44.0), Vector2(108.0, 46.0), button_text, Callable(self, "_claim").bind(int(gift.get("id", 0))), 18) _set_texture_button_enabled(button, status == 1, _texture_provider.frame(_texture_provider.atlas_path("gift"), "onLineGift_lingqu_png"), _texture_provider.frame(_texture_provider.atlas_path("gift"), frame)) func _claim(gift_id: int) -> void: if _is_busy(): return var player := _player_data() var gift := _find_config_by_id(_game_config.get_online_gift_config(), gift_id) if gift.is_empty(): return var status_data := _status(gift, int(player.get("online_gift_get_count", 0)) % 7) if int(status_data.get("status", 2)) != 1: _show_toast("礼包倒计时未结束") return _set_busy(true) var result: Dictionary = await _activity_api.claim_online_gift(_token()) _set_busy(false) if not result.get("ok", false): _show_toast(_response_status_text(result, "领取在线礼包失败")) return var rewards := _reward_fields_to_text(gift, 1, 4) _add_reward_items(rewards) player["online_gift_get_count"] = int(player.get("online_gift_get_count", 0)) + 1 _started_at = int(Time.get_unix_time_from_system()) _show_toast("获得 %s" % _format_reward_text(rewards)) await _refresh_store_house() render()