feat(client): character select, triangle activation consumer, news ticker
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>
This commit is contained in:
@@ -10,6 +10,11 @@ extends Node
|
||||
# Matches sfx_monologue_chime.ogg from D-038 — "neural lattice firing" feel.
|
||||
const CHIME_RECOGNITION := "sfx_monologue_chime"
|
||||
|
||||
# --- D-067: Triangle activation chime (#590, D-072/D-089) ---
|
||||
# Fires once per session when the triangle's tell_state shifts to RoutineDeviation.
|
||||
# Sharper variant (D-067: "contradiction/anomaly") — sfx_monologue_chime_urgent.ogg.
|
||||
const CHIME_ACTIVATION := "sfx_monologue_chime_urgent"
|
||||
|
||||
# --- Bus names (D-068) ---
|
||||
const BUS_MUSIC := "Music"
|
||||
const BUS_AMBIENT := "Ambient"
|
||||
|
||||
@@ -92,6 +92,11 @@ var debug_response: Variant = null
|
||||
# Format: user://saves/<game-id>/<filename>.sav or "" if no pending load.
|
||||
var pending_load_path: String = ""
|
||||
|
||||
# #588: Character archetype chosen at character select screen.
|
||||
# "detective" or "smuggler". Set before game scene loads; sent in StartupMessage.
|
||||
# Default: "detective" — fallback for legacy saves without character.txt.
|
||||
var character_archetype: String = "detective"
|
||||
|
||||
# v7 fields (#431, D-059/D-060)
|
||||
var pending_recognitions: Array = [] # [{entity_id, x, y, z, remaining_ticks, total_delay_ticks}]
|
||||
|
||||
|
||||
@@ -47,11 +47,12 @@ func new_game() -> String:
|
||||
|
||||
|
||||
## Resume an existing game session by setting the active game-id.
|
||||
## Restores world_seed from the save directory for D-010 deterministic replay.
|
||||
## Restores world_seed and character_archetype from the save directory.
|
||||
func resume_game(game_id: String) -> void:
|
||||
GameState.current_game_id = game_id
|
||||
var save_path := SAVES_DIR + game_id + "/"
|
||||
GameState.world_seed = _read_seed_file(save_path)
|
||||
GameState.character_archetype = _read_archetype_file(save_path)
|
||||
|
||||
|
||||
## List all game directories under user://saves/ sorted by last-modified (most recent first).
|
||||
@@ -146,6 +147,24 @@ func _read_seed_file(save_path: String) -> int:
|
||||
return file.get_64() & 0x7FFFFFFFFFFFFFFF
|
||||
|
||||
|
||||
## Write character_archetype to save directory. Called after new_game() creates the dir.
|
||||
func save_character_archetype(game_id: String, archetype: String) -> void:
|
||||
var save_path := SAVES_DIR + game_id + "/"
|
||||
var file := FileAccess.open(save_path + "character.txt", FileAccess.WRITE)
|
||||
if file == null:
|
||||
push_error("SessionManager: failed to write character.txt: %s" % error_string(FileAccess.get_open_error()))
|
||||
return
|
||||
file.store_string(archetype)
|
||||
|
||||
|
||||
## Read character_archetype from save directory. Returns "detective" if missing (legacy saves).
|
||||
func _read_archetype_file(save_path: String) -> String:
|
||||
var file := FileAccess.open(save_path + "character.txt", FileAccess.READ)
|
||||
if file == null:
|
||||
return "detective"
|
||||
return file.get_as_text().strip_edges()
|
||||
|
||||
|
||||
func _find_newest_save(dir_path: String) -> String:
|
||||
var dir := DirAccess.open(dir_path)
|
||||
if dir == null:
|
||||
|
||||
@@ -233,7 +233,7 @@ func _process(delta: float) -> void:
|
||||
|
||||
# Send startup message with world_seed (#175, D-010/D-029).
|
||||
# Server blocks waiting for this before entering the tick loop.
|
||||
var startup_bytes := Protocol.encode_startup_message(GameState.world_seed)
|
||||
var startup_bytes := Protocol.encode_startup_message(GameState.world_seed, GameState.character_archetype)
|
||||
if startup_bytes.size() > 0:
|
||||
var send_err := _bridge.send_message(startup_bytes)
|
||||
if send_err != OK:
|
||||
|
||||
Reference in New Issue
Block a user