Files
tekton/scripts/ui/fragment_craft_panel.gd
2026-05-19 17:30:29 +08:00

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