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()