Files
SurvivalOfTheSnippest/level/ui/scripts/outfitting_screen.gd
T

249 lines
8.2 KiB
GDScript
Raw Normal View History

extends Control
class_name OutfittingScreen
## Gladiator outfitting screen shown on the main menu.
## Left: the armory shop (buy and equip gear with banked gold).
## Right: the character sheet in preview mode, live-updating as you change loadout.
## Reads/writes GameState (banked_gold, owned_items, selected_loadout).
const WEAPONS_DIR := "res://level/resources/"
# Catalog of WeaponData found on disk, sorted by tier then cost
var _catalog: Array[WeaponData] = []
var _catalog_paths: Dictionary = {} # WeaponData -> resource path
var _gold_label: Label = null
var _items_container: VBoxContainer = null
var _sheet: Control = null # CharacterSheet in preview mode
var _nick_input: LineEdit = null # menu's nickname field (for preview name)
func _ready():
_load_catalog()
_build_ui()
GameState.gold_changed.connect(func(_g): _refresh())
GameState.loadout_changed.connect(func(_l): _refresh())
_refresh()
## The menu's nickname LineEdit, so the preview shows the entered name
func set_nick_input(nick_input: LineEdit):
_nick_input = nick_input
if _nick_input:
_nick_input.text_changed.connect(func(_t): _update_preview())
# --- Catalog --------------------------------------------------------------
func _load_catalog():
_catalog.clear()
_catalog_paths.clear()
var dir = DirAccess.open(WEAPONS_DIR)
if not dir:
push_error("[Outfitting] Cannot open " + WEAPONS_DIR)
return
for file in dir.get_files():
# Exported builds list resources as .tres.remap
var file_name = file.trim_suffix(".remap")
if not file_name.ends_with(".tres"):
continue
var path = WEAPONS_DIR + file_name
var res = load(path)
if res is WeaponData:
_catalog.append(res)
_catalog_paths[res] = path
_catalog.sort_custom(func(a, b):
if a.tier != b.tier:
return a.tier < b.tier
return a.cost < b.cost
)
print("[Outfitting] Loaded ", _catalog.size(), " shop items")
# --- UI construction --------------------------------------------------------
func _build_ui():
var hbox = HBoxContainer.new()
hbox.set_anchors_preset(Control.PRESET_FULL_RECT)
hbox.add_theme_constant_override("separation", 16)
add_child(hbox)
# ----- Shop panel (left) -----
var shop_panel = PanelContainer.new()
shop_panel.custom_minimum_size = Vector2(420, 0)
shop_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
shop_panel.size_flags_stretch_ratio = 0.85
hbox.add_child(shop_panel)
var shop_margin = MarginContainer.new()
shop_margin.add_theme_constant_override("margin_left", 16)
shop_margin.add_theme_constant_override("margin_right", 16)
shop_margin.add_theme_constant_override("margin_top", 12)
shop_margin.add_theme_constant_override("margin_bottom", 12)
shop_panel.add_child(shop_margin)
var shop_vbox = VBoxContainer.new()
shop_vbox.add_theme_constant_override("separation", 8)
shop_margin.add_child(shop_vbox)
var shop_title = Label.new()
shop_title.text = "THE ARMORY"
shop_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
shop_title.add_theme_font_size_override("font_size", 26)
shop_title.add_theme_color_override("font_color", Color(1.0, 0.8, 0.0))
shop_vbox.add_child(shop_title)
_gold_label = Label.new()
_gold_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_gold_label.add_theme_font_size_override("font_size", 20)
_gold_label.add_theme_color_override("font_color", Color(1.0, 0.85, 0.2))
shop_vbox.add_child(_gold_label)
var hint = Label.new()
hint.text = "Buy gear, equip it, then enter the arena.\nDie and the arena takes 90% of everything you're worth."
hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
hint.add_theme_font_size_override("font_size", 13)
hint.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
shop_vbox.add_child(hint)
shop_vbox.add_child(HSeparator.new())
var scroll = ScrollContainer.new()
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
shop_vbox.add_child(scroll)
_items_container = VBoxContainer.new()
_items_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_items_container.add_theme_constant_override("separation", 6)
scroll.add_child(_items_container)
# ----- Character sheet preview (right) -----
var sheet_scene = load("res://level/ui/scenes/character_sheet.tscn")
if sheet_scene:
_sheet = sheet_scene.instantiate()
_sheet.preview_mode = true # Must be set before _ready runs
_sheet.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_sheet.size_flags_stretch_ratio = 1.0
_sheet.custom_minimum_size = Vector2(480, 0)
hbox.add_child(_sheet)
else:
push_error("[Outfitting] Failed to load character_sheet.tscn")
# --- Refresh ---------------------------------------------------------------
func _refresh():
if _gold_label:
_gold_label.text = "Gold: " + str(GameState.banked_gold)
_rebuild_items()
_update_preview()
func _rebuild_items():
if not _items_container:
return
for child in _items_container.get_children():
child.queue_free()
for weapon in _catalog:
_items_container.add_child(_make_item_row(weapon))
func _make_item_row(weapon: WeaponData) -> Control:
var path: String = _catalog_paths[weapon]
var owned := GameState.owns_item(path)
var selected := GameState.is_loadout_item_selected(path)
var row = PanelContainer.new()
var margin = MarginContainer.new()
margin.add_theme_constant_override("margin_left", 8)
margin.add_theme_constant_override("margin_right", 8)
margin.add_theme_constant_override("margin_top", 6)
margin.add_theme_constant_override("margin_bottom", 6)
row.add_child(margin)
var hbox = HBoxContainer.new()
hbox.add_theme_constant_override("separation", 10)
margin.add_child(hbox)
# Name + stat line
var info = VBoxContainer.new()
info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hbox.add_child(info)
var name_label = Label.new()
name_label.text = weapon.weapon_name + (" [" + _hand_label(weapon) + "]")
name_label.add_theme_font_size_override("font_size", 16)
name_label.add_theme_color_override("font_color",
Color(0.4, 1.0, 0.4) if selected else (Color(0.85, 0.85, 1.0) if owned else Color.WHITE))
info.add_child(name_label)
var stats_label = Label.new()
var stat_bits: Array[String] = ["DMG " + str(weapon.damage), "CD " + str(weapon.attack_cooldown) + "s"]
if weapon.can_block:
stat_bits.append("Block " + str(int(weapon.block_reduction * 100)) + "%")
stats_label.text = " | ".join(stat_bits)
stats_label.add_theme_font_size_override("font_size", 12)
stats_label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6))
info.add_child(stats_label)
# Action button
var button = Button.new()
button.custom_minimum_size = Vector2(120, 36)
if not owned:
button.text = str(weapon.cost) + "g Buy"
button.disabled = not GameState.can_afford(weapon.cost)
button.pressed.connect(_on_buy_pressed.bind(path, weapon.cost))
elif selected:
button.text = "Equipped"
button.pressed.connect(_on_unequip_pressed.bind(weapon))
else:
button.text = "Equip"
button.pressed.connect(_on_equip_pressed.bind(path))
hbox.add_child(button)
return row
func _hand_label(weapon: WeaponData) -> String:
match weapon.hand_type:
WeaponData.Hand.OFF_HAND: return "Off-Hand"
WeaponData.Hand.TWO_HAND: return "Two-Hand"
_: return "Main Hand"
# --- Actions ----------------------------------------------------------------
func _on_buy_pressed(path: String, cost: int):
if GameState.buy_item(path, cost):
# Auto-equip fresh purchases - you bought it to use it
GameState.equip_loadout_item(path)
# _refresh happens via gold_changed/loadout_changed signals
func _on_equip_pressed(path: String):
GameState.equip_loadout_item(path)
func _on_unequip_pressed(weapon: WeaponData):
var slot = "offhand" if weapon.hand_type == WeaponData.Hand.OFF_HAND else "main"
GameState.unequip_loadout_slot(slot)
# --- Preview ----------------------------------------------------------------
func _update_preview():
if not _sheet:
return
var loadout = GameState.get_selected_loadout()
var main_data = null
var offhand_data = null
if loadout.get("main", "") != "":
main_data = load(loadout["main"])
if loadout.get("offhand", "") != "":
offhand_data = load(loadout["offhand"])
var nick = "Gladiator"
if _nick_input and _nick_input.text.strip_edges() != "":
nick = _nick_input.text.strip_edges()
_sheet.set_preview({
"nick": nick,
"main": main_data,
"offhand": offhand_data,
})