feat: adding the skin_shader_generator, and gacha base barebone
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
extends Control
|
||||
## GachaPanel — Two-banner gacha interface.
|
||||
## Banners: Star (✦) and Gold (▤)
|
||||
## Pull results shown in animated card reveal.
|
||||
|
||||
signal closed
|
||||
|
||||
# ─── Node refs ───────────────────────────────────────────────────────────────
|
||||
@onready var back_btn := %BackBtn as Button
|
||||
@onready var star_tab_btn := %StarTabBtn as Button
|
||||
@onready var gold_tab_btn := %GoldTabBtn as Button
|
||||
@onready var banner_label := %BannerLabel as Label
|
||||
@onready var balance_label := %BalanceLabel as Label
|
||||
@onready var pity_label := %PityLabel as Label
|
||||
@onready var pull_1_btn := %Pull1Btn as Button
|
||||
@onready var pull_10_btn := %Pull10Btn as Button
|
||||
@onready var cost_1_label := %Cost1Label as Label
|
||||
@onready var cost_10_label := %Cost10Label as Label
|
||||
@onready var rates_label := %RatesLabel as Label
|
||||
@onready var result_panel := %ResultPanel as PanelContainer
|
||||
@onready var result_grid := %ResultGrid as GridContainer
|
||||
@onready var close_result_btn := %CloseResultBtn as Button
|
||||
@onready var craft_btn := %CraftBtn as Button
|
||||
@onready var status_label := %StatusLabel as Label
|
||||
|
||||
# ─── State ───────────────────────────────────────────────────────────────────
|
||||
var _current_banner: String = "star"
|
||||
var _pulling: bool = false
|
||||
|
||||
const RARITY_COLORS := {
|
||||
"common": Color(0.80, 0.80, 0.80),
|
||||
"uncommon": Color(0.30, 0.85, 0.35),
|
||||
"rare": Color(0.20, 0.55, 1.00),
|
||||
"real_prize": Color(1.00, 0.75, 0.10)
|
||||
}
|
||||
const RARITY_LABELS := {
|
||||
"common": "Common",
|
||||
"uncommon": "Uncommon",
|
||||
"rare": "Rare",
|
||||
"real_prize": "✨ REAL PRIZE ✨"
|
||||
}
|
||||
|
||||
# ─── Lifecycle ────────────────────────────────────────────────────────────────
|
||||
func _ready() -> void:
|
||||
back_btn.pressed.connect(_on_close)
|
||||
star_tab_btn.pressed.connect(func(): _switch_banner("star"))
|
||||
gold_tab_btn.pressed.connect(func(): _switch_banner("gold"))
|
||||
pull_1_btn.pressed.connect(func(): _do_pull(1))
|
||||
pull_10_btn.pressed.connect(func(): _do_pull(10))
|
||||
close_result_btn.pressed.connect(func(): result_panel.hide())
|
||||
craft_btn.pressed.connect(_on_open_craft)
|
||||
result_panel.hide()
|
||||
_switch_banner("star")
|
||||
|
||||
func show_panel() -> void:
|
||||
show()
|
||||
_refresh_ui()
|
||||
|
||||
# ─── Banner switching ─────────────────────────────────────────────────────────
|
||||
func _switch_banner(id: String) -> void:
|
||||
_current_banner = id
|
||||
star_tab_btn.modulate = Color(1.3, 1.1, 0.3) if id == "star" else Color.WHITE
|
||||
gold_tab_btn.modulate = Color(1.3, 1.1, 0.3) if id == "gold" else Color.WHITE
|
||||
_refresh_ui()
|
||||
|
||||
func _refresh_ui() -> void:
|
||||
var gd: Dictionary = GachaManager.data
|
||||
var banner: Dictionary = gd.get("banners", {}).get(_current_banner, {})
|
||||
if banner.is_empty(): return
|
||||
|
||||
var currency: String = banner.get("currency", "star")
|
||||
var icon: String = "✦" if currency == "star" else "▤"
|
||||
var bal: int = GachaManager.get_balance(_current_banner)
|
||||
var pity: int = GachaManager.get_pity(_current_banner)
|
||||
var pity_at: int = banner.get("pity_at", 90)
|
||||
var c1: int = banner.get("pull_1_cost", 0)
|
||||
var c10: int = banner.get("pull_10_cost", 0)
|
||||
var rates: Dictionary = banner.get("rates", {})
|
||||
|
||||
banner_label.text = banner.get("name", "Banner")
|
||||
balance_label.text = "%s %d" % [icon, bal]
|
||||
pity_label.text = "Pity: %d / %d" % [pity, pity_at]
|
||||
cost_1_label.text = "%s %d" % [icon, c1]
|
||||
cost_10_label.text = "%s %d" % [icon, c10]
|
||||
pull_1_btn.disabled = (bal < c1) or _pulling
|
||||
pull_10_btn.disabled = (bal < c10) or _pulling
|
||||
|
||||
var real_pool: Array = gd.get("pools", {}).get("real_prize", [])
|
||||
var real_names: Array = []
|
||||
for rid in real_pool:
|
||||
var rd = gd.get("real_prize_catalog", {}).get(rid, {})
|
||||
real_names.append(rd.get("name", rid))
|
||||
|
||||
rates_label.text = (
|
||||
"Common %.0f%% Uncommon %.0f%% Rare %.0f%% ✨Real Prize %.1f%%\n" +
|
||||
"Guaranteed Real Prize every %d pulls (current pity: %d)\n\n" +
|
||||
"✨ Exclusive Real Prizes:\n" + "\n".join(real_names.map(func(n): return " • " + n))
|
||||
) % [
|
||||
rates.get("common", 0) * 100,
|
||||
rates.get("uncommon", 0) * 100,
|
||||
rates.get("rare", 0) * 100,
|
||||
rates.get("real_prize", 0) * 100,
|
||||
pity_at, pity
|
||||
]
|
||||
|
||||
# ─── Pull ─────────────────────────────────────────────────────────────────────
|
||||
func _do_pull(count: int) -> void:
|
||||
if _pulling: return
|
||||
_pulling = true
|
||||
status_label.text = "Rolling..."
|
||||
var results: Array = await _run_pull(count)
|
||||
_pulling = false
|
||||
if results.is_empty():
|
||||
status_label.text = "❌ Not enough currency!"
|
||||
_refresh_ui()
|
||||
return
|
||||
status_label.text = ""
|
||||
_refresh_ui()
|
||||
_show_results(results)
|
||||
|
||||
func _run_pull(count: int) -> Array:
|
||||
# Yield one frame so UI updates first
|
||||
await get_tree().process_frame
|
||||
return GachaManager.pull(_current_banner, count)
|
||||
|
||||
# ─── Result display ───────────────────────────────────────────────────────────
|
||||
func _show_results(results: Array) -> void:
|
||||
# Clear old cards
|
||||
for c in result_grid.get_children(): c.queue_free()
|
||||
await get_tree().process_frame
|
||||
|
||||
result_panel.show()
|
||||
for res in results:
|
||||
var card := _make_result_card(res)
|
||||
result_grid.add_child(card)
|
||||
# Staggered reveal
|
||||
await get_tree().create_timer(0.08).timeout
|
||||
|
||||
func _make_result_card(res: Dictionary) -> PanelContainer:
|
||||
var rarity: String = res.get("rarity", "common")
|
||||
var col: Color = RARITY_COLORS.get(rarity, Color.WHITE)
|
||||
var label_txt: String = RARITY_LABELS.get(rarity, rarity.capitalize())
|
||||
|
||||
var panel := PanelContainer.new()
|
||||
panel.custom_minimum_size = Vector2(110, 130)
|
||||
|
||||
var margin := MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 8)
|
||||
margin.add_theme_constant_override("margin_top", 8)
|
||||
margin.add_theme_constant_override("margin_right", 8)
|
||||
margin.add_theme_constant_override("margin_bottom", 8)
|
||||
panel.add_child(margin)
|
||||
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
margin.add_child(vbox)
|
||||
|
||||
# Rarity icon
|
||||
var icon_lbl := Label.new()
|
||||
icon_lbl.text = _rarity_icon(rarity)
|
||||
icon_lbl.add_theme_font_size_override("font_size", 36)
|
||||
icon_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
icon_lbl.add_theme_color_override("font_color", col)
|
||||
vbox.add_child(icon_lbl)
|
||||
|
||||
# Item name
|
||||
var name_lbl := Label.new()
|
||||
name_lbl.text = res.get("name", "?")
|
||||
name_lbl.add_theme_font_size_override("font_size", 11)
|
||||
name_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
name_lbl.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
name_lbl.add_theme_color_override("font_color", col)
|
||||
vbox.add_child(name_lbl)
|
||||
|
||||
# Rarity label
|
||||
var rar_lbl := Label.new()
|
||||
rar_lbl.text = label_txt
|
||||
rar_lbl.add_theme_font_size_override("font_size", 9)
|
||||
rar_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
rar_lbl.add_theme_color_override("font_color", col.lerp(Color.WHITE, 0.3))
|
||||
vbox.add_child(rar_lbl)
|
||||
|
||||
# Flash animation
|
||||
panel.modulate.a = 0.0
|
||||
var tween := create_tween()
|
||||
tween.tween_property(panel, "modulate:a", 1.0, 0.25)
|
||||
if rarity == "real_prize":
|
||||
tween.tween_property(panel, "modulate", Color(1.4, 1.3, 0.5, 1.0), 0.2)
|
||||
tween.tween_property(panel, "modulate", Color.WHITE, 0.3)
|
||||
|
||||
return panel
|
||||
|
||||
func _rarity_icon(rarity: String) -> String:
|
||||
match rarity:
|
||||
"common": return "⬜"
|
||||
"uncommon": return "🟩"
|
||||
"rare": return "🟦"
|
||||
"real_prize": return "✨"
|
||||
return "❓"
|
||||
|
||||
# ─── Craft link ───────────────────────────────────────────────────────────────
|
||||
func _on_open_craft() -> void:
|
||||
hide()
|
||||
# Find or load the fragment craft panel in the scene tree
|
||||
var main = get_tree().current_scene
|
||||
var fcp = main.get_node_or_null("FragmentCraftPanel")
|
||||
if fcp:
|
||||
fcp.show_panel()
|
||||
|
||||
func _on_close() -> void:
|
||||
hide()
|
||||
closed.emit()
|
||||
Reference in New Issue
Block a user