158 lines
6.2 KiB
GDScript
158 lines
6.2 KiB
GDScript
extends Control
|
|
## FragmentCraftPanel — Shows all craftable skins and their fragment requirements.
|
|
## Linked from GachaPanel via the "🧩 Fragment Craft" button.
|
|
|
|
signal closed
|
|
|
|
# ─── Node refs ───────────────────────────────────────────────────────────────
|
|
@onready var back_btn := %BackBtn as Button
|
|
@onready var recipe_list := %RecipeList as VBoxContainer
|
|
@onready var status_label := %StatusLabel as Label
|
|
@onready var common_label := %CommonLabel as Label
|
|
@onready var uncommon_label := %UncommonLabel as Label
|
|
@onready var rare_label := %RareLabel as Label
|
|
|
|
const FRAG_ICONS := {
|
|
"frag_common": "⬜",
|
|
"frag_uncommon": "🟩",
|
|
"frag_rare": "🟦"
|
|
}
|
|
|
|
# ─── Lifecycle ────────────────────────────────────────────────────────────────
|
|
func _ready() -> void:
|
|
back_btn.pressed.connect(_on_close)
|
|
|
|
func show_panel() -> void:
|
|
show()
|
|
_refresh()
|
|
|
|
func _refresh() -> void:
|
|
_update_frag_balance()
|
|
_rebuild_recipe_list()
|
|
|
|
# ─── Fragment balance header ──────────────────────────────────────────────────
|
|
func _update_frag_balance() -> void:
|
|
var frags: Dictionary = UserProfileManager.fragments
|
|
common_label.text = str(frags.get("frag_common", 0))
|
|
uncommon_label.text = str(frags.get("frag_uncommon", 0))
|
|
rare_label.text = str(frags.get("frag_rare", 0))
|
|
|
|
# ─── Recipe cards ─────────────────────────────────────────────────────────────
|
|
func _rebuild_recipe_list() -> void:
|
|
for c in recipe_list.get_children(): c.queue_free()
|
|
await get_tree().process_frame
|
|
|
|
var recipes: Dictionary = GachaManager.get_all_recipes()
|
|
for recipe_id in recipes:
|
|
var recipe: Dictionary = recipes[recipe_id]
|
|
var card := _make_recipe_card(recipe_id, recipe)
|
|
recipe_list.add_child(card)
|
|
|
|
func _make_recipe_card(recipe_id: String, recipe: Dictionary) -> PanelContainer:
|
|
var can_craft: bool = GachaManager.can_craft(recipe_id)
|
|
var frags: Dictionary = UserProfileManager.fragments
|
|
|
|
var panel := PanelContainer.new()
|
|
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
|
|
# Apply Tekton panel style
|
|
var panel_style := StyleBoxFlat.new()
|
|
panel_style.bg_color = Color(0.14117648, 0.16862746, 0.19215687, 1)
|
|
panel_style.corner_radius_top_left = 12
|
|
panel_style.corner_radius_top_right = 12
|
|
panel_style.corner_radius_bottom_right = 12
|
|
panel_style.corner_radius_bottom_left = 12
|
|
panel_style.shadow_color = Color(0, 0, 0, 0.3529412)
|
|
panel_style.shadow_size = 4
|
|
panel_style.shadow_offset = Vector2(-2, 2)
|
|
panel.add_theme_stylebox_override("panel", panel_style)
|
|
|
|
var margin := MarginContainer.new()
|
|
margin.add_theme_constant_override("margin_left", 14)
|
|
margin.add_theme_constant_override("margin_top", 10)
|
|
margin.add_theme_constant_override("margin_right", 14)
|
|
margin.add_theme_constant_override("margin_bottom", 10)
|
|
panel.add_child(margin)
|
|
|
|
var hbox := HBoxContainer.new()
|
|
hbox.add_theme_constant_override("separation", 14)
|
|
margin.add_child(hbox)
|
|
|
|
# Left info
|
|
var vbox := VBoxContainer.new()
|
|
vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
vbox.add_theme_constant_override("separation", 4)
|
|
hbox.add_child(vbox)
|
|
|
|
var name_lbl := Label.new()
|
|
name_lbl.text = recipe.get("name", recipe_id)
|
|
name_lbl.add_theme_font_size_override("font_size", 16)
|
|
name_lbl.add_theme_color_override("font_color", Color(0.9, 0.8, 0.3))
|
|
vbox.add_child(name_lbl)
|
|
|
|
var char_cat_lbl := Label.new()
|
|
char_cat_lbl.text = "%s • %s" % [recipe.get("character", "All"), recipe.get("category", "").capitalize()]
|
|
char_cat_lbl.add_theme_font_size_override("font_size", 11)
|
|
char_cat_lbl.add_theme_color_override("font_color", Color(0.65, 0.65, 0.65))
|
|
vbox.add_child(char_cat_lbl)
|
|
|
|
# Fragment cost row
|
|
var cost_hbox := HBoxContainer.new()
|
|
cost_hbox.add_theme_constant_override("separation", 12)
|
|
vbox.add_child(cost_hbox)
|
|
|
|
var cost: Dictionary = recipe.get("cost", {})
|
|
for fid in ["frag_common", "frag_uncommon", "frag_rare"]:
|
|
if not cost.has(fid): continue
|
|
var needed: int = cost[fid]
|
|
var have: int = frags.get(fid, 0)
|
|
var icon: String = FRAG_ICONS.get(fid, "?")
|
|
var cost_lbl := Label.new()
|
|
cost_lbl.text = "%s %d/%d" % [icon, have, needed]
|
|
cost_lbl.add_theme_font_size_override("font_size", 13)
|
|
cost_lbl.add_theme_color_override("font_color",
|
|
Color(0.4, 1.0, 0.5) if have >= needed else Color(1.0, 0.4, 0.4))
|
|
cost_hbox.add_child(cost_lbl)
|
|
|
|
# Craft button with Tekton dark style
|
|
var craft_btn := Button.new()
|
|
craft_btn.text = "🔨 Craft"
|
|
craft_btn.custom_minimum_size = Vector2(100, 40)
|
|
craft_btn.disabled = not can_craft
|
|
|
|
var btn_style := StyleBoxFlat.new()
|
|
btn_style.bg_color = Color(0.15, 0.15, 0.15, 1)
|
|
btn_style.corner_radius_top_left = 8
|
|
btn_style.corner_radius_top_right = 8
|
|
btn_style.corner_radius_bottom_right = 8
|
|
btn_style.corner_radius_bottom_left = 8
|
|
craft_btn.add_theme_stylebox_override("normal", btn_style)
|
|
|
|
if not can_craft:
|
|
craft_btn.modulate = Color(0.5, 0.5, 0.5, 0.7)
|
|
craft_btn.pressed.connect(_on_craft_pressed.bind(recipe_id, panel))
|
|
hbox.add_child(craft_btn)
|
|
|
|
return panel
|
|
|
|
# ─── Craft action ─────────────────────────────────────────────────────────────
|
|
func _on_craft_pressed(recipe_id: String, _card: PanelContainer) -> void:
|
|
var ok := GachaManager.craft(recipe_id)
|
|
if ok:
|
|
var recipes := GachaManager.get_all_recipes()
|
|
var name: String = recipes.get(recipe_id, {}).get("name", recipe_id)
|
|
_set_status("✅ Crafted: %s!" % name, Color(0.4, 1.0, 0.4))
|
|
_refresh()
|
|
else:
|
|
_set_status("❌ Not enough fragments.", Color(1.0, 0.4, 0.4))
|
|
|
|
func _set_status(msg: String, col: Color = Color.WHITE) -> void:
|
|
status_label.add_theme_color_override("font_color", col)
|
|
status_label.text = msg
|
|
await get_tree().create_timer(3.0).timeout
|
|
if status_label.text == msg: status_label.text = ""
|
|
|
|
func _on_close() -> void:
|
|
hide()
|
|
closed.emit()
|