class_name TitleScreen extends Control ## The entry-point screen (mock Title Screen). Editor-first (ADR 0001): the whole ## composition lives in TitleScreen.tscn; this script drives selection, input, and ## dispatches the menu. ยง2: presentation/state โ€” no AI here (the DM enters at the ## shell). The atmosphere (bg shader, glow, embers) is layered on in a later task. signal menu_activated(id: StringName) const SHELL := "res://scenes/shell/MainWindowShell.tscn" var _rows: Array = [] var _sel: int = 0 @onready var _menu: VBoxContainer = $Content/Menu @onready var _footer_left: Label = $FooterLeft @onready var _background: ColorRect = $Background @onready var _rule: ColorRect = $Content/Rule func _ready() -> void: # Root Control run via F6 is not sized by full-rect anchors โ€” size explicitly. _fit_to_viewport() get_viewport().size_changed.connect(_fit_to_viewport) _background.color = Palette.STAGE_1 _rule.color = Palette.GOLD _rows = [] for row in _menu.get_children(): if row.has_meta("action"): _rows.append(row) (row.get_node("Marker") as Label).add_theme_color_override("font_color", Palette.GOLD) _wire_row_mouse() _footer_left.text = Version.new().footer() menu_activated.connect(_on_menu_activated) _update_selection() func _fit_to_viewport() -> void: size = get_viewport_rect().size func _wire_row_mouse() -> void: for i in range(_rows.size()): var idx := i # fresh per-iteration binding for the closures var row: Control = _rows[idx] row.mouse_entered.connect(func(): _sel = idx _update_selection()) row.gui_input.connect(func(e: InputEvent): if e is InputEventMouseButton and e.pressed and e.button_index == MOUSE_BUTTON_LEFT: _activate(idx)) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("ui_down"): _move(1) elif event.is_action_pressed("ui_up"): _move(-1) elif event.is_action_pressed("ui_accept"): _activate(_sel) func _move(delta: int) -> void: if _rows.is_empty(): return _sel = wrapi(_sel + delta, 0, _rows.size()) _update_selection() func _activate(i: int) -> void: if i < 0 or i >= _rows.size(): return menu_activated.emit(_rows[i].get_meta("action")) func _on_menu_activated(id: StringName) -> void: match id: &"new_game": get_tree().change_scene_to_file(SHELL) &"quit": get_tree().quit() _: pass # inert placeholder โ€” the screen lands in a later milestone func _update_selection() -> void: for i in range(_rows.size()): var row: Control = _rows[i] var selected := i == _sel (row.get_node("Marker") as Label).modulate.a = 1.0 if selected else 0.0 var label := row.get_node("Body/Label") as Label label.add_theme_color_override("font_color", Palette.CREAM_BRIGHT if selected else Palette.MUTED_MONO)