Files
Station-0/scripts/ui/armory_ui.gd
T
2026-03-12 15:57:15 -04:00

195 lines
6.3 KiB
GDScript

extends CanvasLayer
# ---------------------------------------------------------------------------
# Layout constants (screen space: 1920 x 1080)
# ---------------------------------------------------------------------------
const SCREEN_W := 1920.0
const SCREEN_H := 1080.0
const PANEL_W := 620.0
const PANEL_H := 500.0
const LINE_H := 36.0
const LIST_PAD_X := 24.0
const LIST_PAD_Y := 60.0
const BG_COLOR := Color(0.06, 0.07, 0.12, 0.94)
const CURSOR_COLOR := Color(0.9, 0.85, 0.3, 1)
const NORMAL_COLOR := Color(0.75, 0.75, 0.85, 1)
const EQUIPPED_COLOR := Color(0.4, 0.9, 0.6, 1)
# ---------------------------------------------------------------------------
# State
# ---------------------------------------------------------------------------
var _items: Array = [] # Array[BodyPartData] — sorted by slot
var _cursor: int = 0
var _item_labels: Array = [] # Array[Label]
# ---------------------------------------------------------------------------
# UI nodes (built once in _ready)
# ---------------------------------------------------------------------------
var _bg: Polygon2D
var _title_lbl: Label
var _hint_lbl: Label
var _list_root: Node2D # parent for item labels (positioned in screen space)
# ---------------------------------------------------------------------------
# Boot
# ---------------------------------------------------------------------------
func _ready() -> void:
_build_static_ui()
hide()
func _build_static_ui() -> void:
var cx := SCREEN_W / 2.0
var cy := SCREEN_H / 2.0
var hw := PANEL_W / 2.0
var hh := PANEL_H / 2.0
# Background panel
_bg = Polygon2D.new()
_bg.polygon = PackedVector2Array([
Vector2(-hw, -hh), Vector2(hw, -hh),
Vector2( hw, hh), Vector2(-hw, hh),
])
_bg.color = BG_COLOR
_bg.position = Vector2(cx, cy)
add_child(_bg)
# Title
_title_lbl = Label.new()
_title_lbl.text = "— ARMORY —"
_title_lbl.position = Vector2(cx - 60, cy - hh + 14)
add_child(_title_lbl)
# Hint bar
_hint_lbl = Label.new()
_hint_lbl.text = "[↑↓] Navigate [Enter] Equip [Esc] Close"
_hint_lbl.position = Vector2(cx - hw + LIST_PAD_X, cy + hh - 30)
add_child(_hint_lbl)
# Container node for item labels
_list_root = Node2D.new()
_list_root.position = Vector2(cx - hw + LIST_PAD_X, cy - hh + LIST_PAD_Y)
add_child(_list_root)
# ---------------------------------------------------------------------------
# Input
# ---------------------------------------------------------------------------
func _input(event: InputEvent) -> void:
if not visible:
return
if event.is_action_pressed("ui_up"):
_cursor = wrapi(_cursor - 1, 0, maxi(1, _items.size()))
AudioManager.play("ui_select")
_refresh_labels()
get_viewport().set_input_as_handled()
elif event.is_action_pressed("ui_down"):
_cursor = wrapi(_cursor + 1, 0, maxi(1, _items.size()))
AudioManager.play("ui_select")
_refresh_labels()
get_viewport().set_input_as_handled()
elif event.is_action_pressed("ui_accept"):
AudioManager.play("ui_click")
_equip_selected()
get_viewport().set_input_as_handled()
elif event.is_action_pressed("ui_cancel"):
hide()
get_viewport().set_input_as_handled()
# ---------------------------------------------------------------------------
# Open — call from hub.gd
# ---------------------------------------------------------------------------
func open() -> void:
_rebuild_items()
_cursor = 0
_refresh_labels()
show()
# ---------------------------------------------------------------------------
# Default body parts (always available)
# ---------------------------------------------------------------------------
const DEFAULT_PARTS: Array[String] = [
"res://data/body_parts/head_default.tres",
"res://data/body_parts/torso_default.tres",
"res://data/body_parts/left_arm_default.tres",
"res://data/body_parts/right_arm_default.tres",
"res://data/body_parts/legs_default.tres",
]
# ---------------------------------------------------------------------------
# Rebuild item list from UpgradeManager
# ---------------------------------------------------------------------------
func _rebuild_items() -> void:
_items.clear()
# Add default parts first (always available), except default_head
for path: String in DEFAULT_PARTS:
# Skip default head - not shown as equippable
if "head_default" in path:
continue
var part := load(path) as BodyPartData
if part:
_items.append(part)
# Add acquired parts
for path: String in UpgradeManager.acquired_part_paths:
if path == "":
continue
var part := load(path) as BodyPartData
if part:
_items.append(part)
# Sort by slot order so the list groups naturally
var slot_order := UpgradeManager.SLOTS
_items.sort_custom(func(a, b): return slot_order.find(a.slot) < slot_order.find(b.slot))
# Destroy old labels
for lbl: Label in _item_labels:
lbl.queue_free()
_item_labels.clear()
# Create one label per item
for i in _items.size():
var lbl := Label.new()
lbl.position = Vector2(0, i * LINE_H)
_list_root.add_child(lbl)
_item_labels.append(lbl)
func _refresh_labels() -> void:
for i in _item_labels.size():
var part: BodyPartData = _items[i]
var equipped_path: String = UpgradeManager.equipped_parts.get(part.slot, "")
# Check if this part is equipped (empty string means default is equipped)
var is_default_part := part.resource_path in DEFAULT_PARTS
var is_equipped := equipped_path == part.resource_path or (equipped_path == "" and is_default_part)
var is_cursor := i == _cursor
var slot_tag := "[%s]" % part.slot.to_upper()
var equip_tag := "" if is_equipped else ""
var lbl: Label = _item_labels[i]
lbl.text = ("%s %s%s" % [slot_tag, part.display_name, equip_tag]).lstrip("")
if is_cursor:
lbl.add_theme_color_override("font_color", CURSOR_COLOR)
elif is_equipped:
lbl.add_theme_color_override("font_color", EQUIPPED_COLOR)
else:
lbl.add_theme_color_override("font_color", NORMAL_COLOR)
# ---------------------------------------------------------------------------
# Equip the part under the cursor
# ---------------------------------------------------------------------------
func _equip_selected() -> void:
if _items.is_empty():
return
var part: BodyPartData = _items[_cursor]
UpgradeManager.equip_part(part)
SaveManager.save()
_refresh_labels()