Sprint 24 Signal — three client tickets delivering the player-facing storyteller feedback loop: - #588: Character archetype select screen between New Game and session start. Two-card UI (Smuggler/Detective), keyboard+mouse, ESC cancels. GameState.character_archetype persisted and sent in StartupMessage. PROTOCOL_VERSION bumped to 19. - #590: Triangle crisis event consumer. Decodes triangle_crisis_events from snapshot, fires sfx_monologue_chime_urgent once per triangle per session via AudioManager.CHIME_ACTIVATION. - #592: News ticker HUD element. Scrolling marquee on UILayer, visible only when current_ticker is present in snapshot (Last Shift zone). Zero-arg update_from_state reads from GameState.current_snapshot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
90 lines
3.5 KiB
GDScript
90 lines
3.5 KiB
GDScript
extends Control
|
|
## #588: Character archetype select panel — shown after "New Game", before loading main.tscn.
|
|
## Two cards (Smuggler / Detective). Keyboard (left/right/enter/esc) and mouse.
|
|
## Emits archetype_confirmed(archetype: String) or archetype_cancelled on ESC.
|
|
##
|
|
## ESC cancels without creating a save directory — new_game() fires AFTER confirmation.
|
|
|
|
signal archetype_confirmed(archetype: String)
|
|
signal archetype_cancelled
|
|
|
|
const CARD_BG_NORMAL := Color(0.07, 0.07, 0.10, 1.0)
|
|
const CARD_BG_SELECTED := Color(0.10, 0.12, 0.18, 1.0)
|
|
const CARD_BORDER_NORMAL := Color(0.18, 0.22, 0.28, 1.0)
|
|
const CARD_BORDER_SELECTED := Color(0.906, 0.773, 0.278, 1.0) # INSERT_COLOR_HOVER
|
|
|
|
# Archetypes in display order — index 0=smuggler (left card), 1=detective (right card)
|
|
const ARCHETYPES := ["smuggler", "detective"]
|
|
|
|
@onready var _smuggler_wrapper: Control = $Cards/CardSmugglerWrapper
|
|
@onready var _detective_wrapper: Control = $Cards/CardDetectiveWrapper
|
|
@onready var _confirm_btn: Button = $ConfirmBtn
|
|
@onready var _title_label: Label = $TitleLabel
|
|
|
|
var _selected_index: int = 0 # 0=smuggler, 1=detective
|
|
|
|
|
|
func _ready() -> void:
|
|
_title_label.text = UIStrings.get_text("character_select.title")
|
|
_confirm_btn.text = UIStrings.get_text("character_select.confirm")
|
|
|
|
# Smuggler card labels
|
|
$Cards/CardSmugglerWrapper/CardInner/VBox/NameLabel.text = UIStrings.get_text("character_select.smuggler_card_name")
|
|
$Cards/CardSmugglerWrapper/CardInner/VBox/RoleLabel.text = UIStrings.get_text("character_select.smuggler_card_role")
|
|
$Cards/CardSmugglerWrapper/CardInner/VBox/ToneLabel.text = UIStrings.get_text("character_select.smuggler_card_tone")
|
|
|
|
# Detective card labels
|
|
$Cards/CardDetectiveWrapper/CardInner/VBox/NameLabel.text = UIStrings.get_text("character_select.detective_card_name")
|
|
$Cards/CardDetectiveWrapper/CardInner/VBox/RoleLabel.text = UIStrings.get_text("character_select.detective_card_role")
|
|
$Cards/CardDetectiveWrapper/CardInner/VBox/ToneLabel.text = UIStrings.get_text("character_select.detective_card_tone")
|
|
|
|
_confirm_btn.pressed.connect(_on_confirm)
|
|
_smuggler_wrapper.gui_input.connect(_on_card_input.bind(0))
|
|
_detective_wrapper.gui_input.connect(_on_card_input.bind(1))
|
|
|
|
_update_card_visuals()
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if not visible:
|
|
return
|
|
if event is InputEventKey and event.pressed and not event.is_echo():
|
|
match event.keycode:
|
|
KEY_LEFT:
|
|
_selected_index = 0
|
|
_update_card_visuals()
|
|
get_viewport().set_input_as_handled()
|
|
KEY_RIGHT:
|
|
_selected_index = 1
|
|
_update_card_visuals()
|
|
get_viewport().set_input_as_handled()
|
|
KEY_ENTER, KEY_KP_ENTER:
|
|
_on_confirm()
|
|
get_viewport().set_input_as_handled()
|
|
KEY_ESCAPE:
|
|
archetype_cancelled.emit()
|
|
get_viewport().set_input_as_handled()
|
|
|
|
|
|
func _on_card_input(event: InputEvent, card_index: int) -> void:
|
|
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
|
_selected_index = card_index
|
|
_update_card_visuals()
|
|
|
|
|
|
func _on_confirm() -> void:
|
|
archetype_confirmed.emit(ARCHETYPES[_selected_index])
|
|
|
|
|
|
func _update_card_visuals() -> void:
|
|
_set_card_selected(_smuggler_wrapper, _selected_index == 0)
|
|
_set_card_selected(_detective_wrapper, _selected_index == 1)
|
|
_confirm_btn.grab_focus()
|
|
|
|
|
|
func _set_card_selected(wrapper: Control, selected: bool) -> void:
|
|
var border: ColorRect = wrapper.get_node("CardBorder")
|
|
var inner: ColorRect = wrapper.get_node("CardInner")
|
|
border.color = CARD_BORDER_SELECTED if selected else CARD_BORDER_NORMAL
|
|
inner.color = CARD_BG_SELECTED if selected else CARD_BG_NORMAL
|