feat: 2.3.2

This commit is contained in:
2026-05-19 17:30:29 +08:00
parent 7ca11c6534
commit 8430d1054e
39 changed files with 6581 additions and 738 deletions
+106 -22
View File
@@ -8,6 +8,9 @@ signal closed
@onready var item_grid: GridContainer = %ItemGrid
@onready var back_btn: Button = %BackBtn
@onready var status_label: Label = %StatusLabel
@onready var banner1: Button = %Banner1
@onready var banner2: Button = %Banner2
@onready var banner3: Button = %Banner3
# Tabs
@onready var tab_head: Button = %TabHead
@@ -17,6 +20,9 @@ signal closed
@onready var tab_gold: Button = %TabGold
@onready var tab_star: Button = %TabStar
# Maps category -> tab button (populated in _ready)
var _tab_map: Dictionary = {}
# 3D Preview
@onready var character_root: Node3D = %CharacterRoot
@onready var anim_player: AnimationPlayer = %AnimationPlayer
@@ -72,6 +78,14 @@ const STAR_PACKS: Array = [
# _ready
# -----------------------------------------------------------------------
func _ready() -> void:
_tab_map = {
"head": tab_head,
"costume": tab_costume,
"glove": tab_glove,
"accessory": tab_acc,
"gold_packs": tab_gold,
"star_packs": tab_star,
}
back_btn.pressed.connect(_on_close)
tab_head.pressed.connect(_on_tab_selected.bind("head"))
tab_costume.pressed.connect(_on_tab_selected.bind("costume"))
@@ -85,6 +99,7 @@ func _ready() -> void:
if UserProfileManager.profile_updated.connect(_refresh_wallet) != OK:
pass
_set_active_tab(current_category)
_setup_3d_preview()
if UserProfileManager.shop_catalog.is_empty():
@@ -175,12 +190,78 @@ func _fetch_and_build() -> void:
func _build_shop() -> void:
_refresh_wallet()
_populate_banners()
_populate_current_tab()
func _on_tab_selected(category: String) -> void:
current_category = category
_set_active_tab(category)
_populate_current_tab()
func _set_active_tab(active_category: String) -> void:
var style_active := StyleBoxFlat.new()
style_active.bg_color = Color(1, 1, 1, 1)
style_active.content_margin_left = 16.0
style_active.content_margin_top = 10.0
style_active.content_margin_right = 16.0
style_active.content_margin_bottom = 10.0
style_active.corner_radius_top_left = 6
style_active.corner_radius_top_right = 6
style_active.corner_radius_bottom_right = 6
style_active.corner_radius_bottom_left = 6
var style_inactive := StyleBoxFlat.new()
style_inactive.bg_color = Color(0.15, 0.18, 0.22, 1)
style_inactive.content_margin_left = 16.0
style_inactive.content_margin_top = 10.0
style_inactive.content_margin_right = 16.0
style_inactive.content_margin_bottom = 10.0
style_inactive.corner_radius_top_left = 6
style_inactive.corner_radius_top_right = 6
style_inactive.corner_radius_bottom_right = 6
style_inactive.corner_radius_bottom_left = 6
var style_hover := StyleBoxFlat.new()
style_hover.bg_color = Color(0.22, 0.26, 0.30, 1)
style_hover.content_margin_left = 16.0
style_hover.content_margin_top = 10.0
style_hover.content_margin_right = 16.0
style_hover.content_margin_bottom = 10.0
style_hover.corner_radius_top_left = 6
style_hover.corner_radius_top_right = 6
style_hover.corner_radius_bottom_right = 6
style_hover.corner_radius_bottom_left = 6
for cat in _tab_map:
var btn: Button = _tab_map[cat]
if cat == active_category:
btn.add_theme_stylebox_override("normal", style_active)
btn.add_theme_stylebox_override("hover", style_active)
btn.add_theme_stylebox_override("pressed", style_active)
btn.add_theme_color_override("font_color", Color(0.08, 0.09, 0.12))
btn.add_theme_color_override("font_hover_color", Color(0.08, 0.09, 0.12))
else:
btn.add_theme_stylebox_override("normal", style_inactive)
btn.add_theme_stylebox_override("hover", style_hover)
btn.add_theme_stylebox_override("pressed", style_inactive)
btn.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
btn.add_theme_color_override("font_hover_color", Color(1, 1, 1))
# -----------------------------------------------------------------------
# Banner population — promotional / featured items
# -----------------------------------------------------------------------
func _populate_banners() -> void:
var banners: Array[Button] = [banner1, banner2, banner3]
var promos: Array = UserProfileManager.shop_catalog.get("banners", [])
for i in banners.size():
var btn: Button = banners[i]
if i < promos.size():
btn.text = promos[i].get("label", "")
btn.tooltip_text = promos[i].get("id", "")
btn.show()
else:
btn.hide()
# -----------------------------------------------------------------------
# Grid population — builds cards dynamically from localized templates
# -----------------------------------------------------------------------
@@ -214,8 +295,8 @@ func _make_gold_card(pack: Dictionary) -> Control:
var card: Control = template_gold_card.duplicate()
card.visible = true
var amount_lbl: Label = card.find_child("AmountLabel", true, false) as Label
if amount_lbl: amount_lbl.text = "%d" % pack.amount
var amount_lbl: RichTextLabel = card.find_child("AmountLabel", true, false) as RichTextLabel
if amount_lbl: amount_lbl.text = "[right][img=24x24]res://assets/graphics/gui/lobby/gold.png[/img] %d[/right]" % pack.amount
var bonus_lbl: Label = card.find_child("BonusLabel", true, false) as Label
if bonus_lbl:
@@ -237,11 +318,11 @@ func _make_star_card(pack: Dictionary) -> Control:
var card: Control = template_star_card.duplicate()
card.visible = true
var amount_lbl: Label = card.find_child("AmountLabel", true, false) as Label
if amount_lbl: amount_lbl.text = "%d" % pack.amount
var amount_lbl: RichTextLabel = card.find_child("AmountLabel", true, false) as RichTextLabel
if amount_lbl: amount_lbl.text = "[right][img=24x24]res://assets/graphics/gui/lobby/star.png[/img] %d[/right]" % pack.amount
var cost_lbl: Label = card.find_child("CostLabel", true, false) as Label
if cost_lbl: cost_lbl.text = "Cost: ⭐ %d Gold" % pack.gold_cost
var cost_lbl: RichTextLabel = card.find_child("CostLabel", true, false) as RichTextLabel
if cost_lbl: cost_lbl.text = "[center]Cost: [img=20x20]res://assets/graphics/gui/lobby/gold.png[/img] %d[/center]" % pack.gold_cost
var buy_btn: Button = card.find_child("BuyBtn", true, false) as Button
if buy_btn: buy_btn.pressed.connect(_on_buy_star_pressed.bind(pack))
@@ -270,13 +351,16 @@ func _make_cosmetic_card(item: Dictionary) -> Control:
}.get(rarity, Color(0.50, 0.50, 0.50))
rarity_lbl.add_theme_color_override("font_color", rarity_col)
var price_lbl: Label = card.find_child("PriceLabel", true, false) as Label
var price_lbl: RichTextLabel = card.find_child("PriceLabel", true, false) as RichTextLabel
if price_lbl:
var g: int = int(item.get("gold", 0))
var s: int = int(item.get("star", 0))
if g > 0 and s > 0: price_lbl.text = "%d%d" % [g, s]
elif g > 0: price_lbl.text = "%d" % g
else: price_lbl.text = "%d" % s
if g > 0 and s > 0:
price_lbl.text = "[center][img=20x20]res://assets/graphics/gui/lobby/gold.png[/img] %d [img=20x20]res://assets/graphics/gui/lobby/star.png[/img] %d[/center]" % [g, s]
elif g > 0:
price_lbl.text = "[center][img=20x20]res://assets/graphics/gui/lobby/gold.png[/img] %d[/center]" % g
else:
price_lbl.text = "[center][img=20x20]res://assets/graphics/gui/lobby/star.png[/img] %d[/center]" % s
var try_btn: Button = card.find_child("TryBtn", true, false) as Button
if try_btn: try_btn.pressed.connect(_on_try_pressed.bind(item))
@@ -300,8 +384,8 @@ func _make_cosmetic_card(item: Dictionary) -> Control:
func _refresh_wallet() -> void:
var g: int = UserProfileManager.wallet.get("gold", 0)
var s: int = UserProfileManager.wallet.get("star", 0)
gold_label.text = "%d" % g
star_label.text = "%d" % s
gold_label.text = str(g)
star_label.text = str(s)
status_label.text = ""
# -----------------------------------------------------------------------
@@ -351,23 +435,23 @@ func _on_buy_cosmetic_pressed(item: Dictionary) -> void:
if UserProfileManager.inventory.has(item.id):
status_label.text = "Already owned: " + item.get("name", item.id)
return
var price_gold: int = item.get("gold", 0)
var price_star: int = item.get("star", 0)
if UserProfileManager.wallet.get("gold", 0) < price_gold \
or UserProfileManager.wallet.get("star", 0) < price_star:
status_label.text = "Not enough currency."
return
status_label.text = "Purchasing..."
var success: bool = await UserProfileManager.purchase_item(
item.id, price_gold, price_star, current_category)
status_label.text = ("Purchased: " + item.get("name", item.id)) if success else "Purchase failed."
if success:
var err: String = await UserProfileManager.purchase_item(item.id)
if err == "":
status_label.text = "Purchased: " + item.get("name", item.id)
_refresh_wallet()
# Refresh preview to show newly purchased skin's materials
if _preview_revert.is_valid():
_preview_revert.call()
_preview_revert = Callable()
SkinManager.apply_loadout(character_root, UserProfileManager.loadout)
else:
if "NotEnoughFunds" in err or "funds" in err.to_lower() or "wallet" in err.to_lower():
status_label.text = "Not enough currency."
else:
status_label.text = "Purchase failed."
func _on_close() -> void:
# Clean up any open preview when closing the shop