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 frag_balance := %FragBalance 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 var parts: Array = [] for fid in ["frag_common", "frag_uncommon", "frag_rare"]: var icon: String = FRAG_ICONS.get(fid, "?") var count: int = frags.get(fid, 0) parts.append("%s ×%d" % [icon, count]) frag_balance.text = " ".join(parts) # ─── 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 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 var craft_btn := Button.new() craft_btn.text = "🔨 Craft" craft_btn.custom_minimum_size = Vector2(100, 40) craft_btn.disabled = not can_craft 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()