3-level atlas navigation as an implant/map/atlas FULLSCREEN app (D-170 z=20): system picker (◄ ► cycle, Enter opens orbital) → orbital diagram (star centre, bodies grouped by orbit_index with moons sub-orbiting parents, stations as markers, click-to-open body or mini station panel) → body entry (data sheet, Enter stub for #835 heightmap viewer, Esc back). Composed from the ImplantPanel component library (D-169). Wires the panel into hud.tscn, adds an A-key toggle in main.gd, propagates insert_state via snapshot_consumers. Extends generate-star-map-data.py to emit per-system orbit_bodies + stations arrays (with currency_zone, atmosphere, population, terrain_reference, etc.) from systems.db; star_map_data.json regenerated deterministically. Per D-191 §6, Phase 3.
211 lines
7.2 KiB
GDScript
211 lines
7.2 KiB
GDScript
class_name SnapshotConsumers
|
|
## Non-dialogue snapshot consumers and event handlers extracted from main.gd (#775).
|
|
##
|
|
## Registered with SnapshotEventRouter; reads from GameState directly.
|
|
## UI node references passed via init(). All methods are zero-argument
|
|
## callables compatible with SnapshotEventRouter.
|
|
|
|
const LISTENING_FOCUS_TICKS: int = 30 # D-071: stationary ticks before ListeningFocus boost
|
|
|
|
var monologue_display: Node = null
|
|
var dialogue_box: Node = null
|
|
var examine_display: Node = null
|
|
var loading_screen: Node = null
|
|
var debug_console: Node = null
|
|
var cursor_renderer: Node = null
|
|
var interaction_list: Node = null
|
|
var interaction_prompt: Node = null
|
|
var minimap: Node = null
|
|
var star_map: Node = null
|
|
var economics_panel: Node = null # #824: economics monitor (D-181)
|
|
var atlas_panel: Node = null # #834: atlas implant panel (D-191)
|
|
|
|
var _screen_flash_fn: Callable # Callable(color: Color, duration: float)
|
|
|
|
var _last_monologue_tick: int = -1
|
|
var _known_recognition_ids: Dictionary = {}
|
|
var _known_triangle_ids: Dictionary = {}
|
|
var _current_zone: String = ""
|
|
|
|
|
|
func init(refs: Dictionary, screen_flash: Callable) -> SnapshotConsumers:
|
|
monologue_display = refs.get("monologue_display")
|
|
dialogue_box = refs.get("dialogue_box")
|
|
examine_display = refs.get("examine_display")
|
|
loading_screen = refs.get("loading_screen")
|
|
debug_console = refs.get("debug_console")
|
|
cursor_renderer = refs.get("cursor_renderer")
|
|
interaction_list = refs.get("interaction_list")
|
|
interaction_prompt = refs.get("interaction_prompt")
|
|
minimap = refs.get("minimap")
|
|
star_map = refs.get("star_map")
|
|
economics_panel = refs.get("economics_panel")
|
|
atlas_panel = refs.get("atlas_panel")
|
|
_screen_flash_fn = screen_flash
|
|
return self
|
|
|
|
|
|
# OQ-07 (#522): Propagate insert state to all z-layer-6 display nodes.
|
|
func propagate_insert_state() -> void:
|
|
var insert_state := GameState.insert_active
|
|
if cursor_renderer:
|
|
cursor_renderer.set_insert_active(insert_state)
|
|
if interaction_list:
|
|
interaction_list.set_insert_active(insert_state)
|
|
if interaction_prompt:
|
|
interaction_prompt.set_insert_active(insert_state)
|
|
if minimap:
|
|
minimap.set_insert_active(insert_state)
|
|
if star_map:
|
|
star_map.set_insert_active(insert_state)
|
|
if economics_panel:
|
|
economics_panel.set_insert_active(insert_state)
|
|
if atlas_panel:
|
|
atlas_panel.set_insert_active(insert_state)
|
|
|
|
|
|
# D-057: Update interaction list from game state.
|
|
# Suppress during dialogue — player is in conversation, verb list is noise.
|
|
func update_interaction_list() -> void:
|
|
if not interaction_list:
|
|
return
|
|
if dialogue_box and dialogue_box.is_dialogue_active():
|
|
if interaction_list.is_showing():
|
|
interaction_list.hide_list()
|
|
else:
|
|
interaction_list.update_from_state()
|
|
|
|
|
|
# D-018 #125: Play close-range sound events — fired once per snapshot tick.
|
|
func play_close_sound_events() -> void:
|
|
for evt in GameState.close_sound_events:
|
|
if not evt is Dictionary or not evt.has("x") or not evt.has("y"):
|
|
continue
|
|
AudioManager.play_sound_event(
|
|
evt.get("event_type", ""), Vector2(float(evt.x), float(evt.y))
|
|
)
|
|
GameState.close_sound_events = []
|
|
|
|
|
|
# D-067: Recognition chime — fires sfx_monologue_chime when a fog entity
|
|
# enters the cognitive delay recognition queue for the first time.
|
|
func play_recognition_chimes() -> void:
|
|
for rec in GameState.pending_recognitions:
|
|
if not rec is Dictionary or not rec.has("entity_id"):
|
|
continue
|
|
var eid: int = rec.entity_id
|
|
if not _known_recognition_ids.has(eid):
|
|
_known_recognition_ids[eid] = true
|
|
AudioManager.play(AudioManager.CHIME_RECOGNITION)
|
|
|
|
|
|
# #590 D-072/D-089: Triangle activation consumer.
|
|
func handle_triangle_crisis_events() -> void:
|
|
var events: Array = GameState.current_snapshot.get("triangle_crisis_events", [])
|
|
for ev in events:
|
|
if not ev is Dictionary or not ev.has("triangle_id"):
|
|
continue
|
|
var tid: int = ev.triangle_id
|
|
if not _known_triangle_ids.has(tid):
|
|
_known_triangle_ids[tid] = true
|
|
AudioManager.play(AudioManager.CHIME_ACTIVATION, AudioManager.BUS_UI_SOUNDS)
|
|
|
|
|
|
# D-073 (#529): Zone ambient crossfade.
|
|
func update_zone() -> void:
|
|
var zone := GameState.current_zone_id
|
|
if zone != _current_zone:
|
|
_current_zone = zone
|
|
AudioManager.set_zone(zone)
|
|
|
|
|
|
# D-071 (#530): ListeningFocus boost — World SFX +2.5dB when stationary 30+ ticks.
|
|
func update_listening_focus() -> void:
|
|
var current_dip := AudioManager.get_active_dip()
|
|
var threshold_met := GameState.stationary_ticks >= LISTENING_FOCUS_TICKS
|
|
if threshold_met and current_dip == "":
|
|
AudioManager.apply_dip("listening_focus")
|
|
elif not threshold_met and current_dip == "listening_focus":
|
|
AudioManager.clear_dip()
|
|
|
|
|
|
# Consume-once per tick: show monologue text, then clear.
|
|
func consume_monologue() -> void:
|
|
if GameState.current_monologue == null or not monologue_display:
|
|
return
|
|
if GameState.current_tick == _last_monologue_tick:
|
|
return
|
|
_last_monologue_tick = GameState.current_tick
|
|
var mono: Dictionary = GameState.current_monologue
|
|
monologue_display.show_monologue(
|
|
mono.get("text", ""),
|
|
mono.get("duration_seconds", 5.0),
|
|
mono.get("priority", 2),
|
|
mono.get("is_urgent", false)
|
|
)
|
|
# #502: Amber flash on room reset
|
|
var mono_id: String = mono.get("id", "")
|
|
if mono_id.begins_with("room_reset"):
|
|
_screen_flash_fn.call(Constants.ENTITY_COLOR_POI, 0.15)
|
|
GameState.current_monologue = null
|
|
|
|
|
|
# #554/#257: Show save/load result notification; hide loading screen on load complete.
|
|
func consume_save_result() -> void:
|
|
if GameState.save_result == null:
|
|
return
|
|
var result: Dictionary = GameState.save_result
|
|
GameState.save_result = null
|
|
if loading_screen:
|
|
loading_screen.hide_loading()
|
|
var msg: String
|
|
if result.get("success", false):
|
|
if result.get("kind", "") == "save":
|
|
msg = UIStrings.get_text("notifications.save_complete")
|
|
else:
|
|
msg = UIStrings.get_text("notifications.load_complete")
|
|
else:
|
|
if result.get("kind", "") == "save":
|
|
msg = UIStrings.get_text("notifications.save_failed")
|
|
else:
|
|
msg = UIStrings.get_text("notifications.load_failed")
|
|
if monologue_display:
|
|
monologue_display.show_notification(msg)
|
|
|
|
|
|
# #581: Forward debug_response from server to the debug console.
|
|
func consume_debug_response() -> void:
|
|
if GameState.debug_response == null or not debug_console:
|
|
return
|
|
debug_console.append_response(GameState.debug_response)
|
|
GameState.debug_response = null
|
|
|
|
|
|
# #824: Forward economy_snapshot from server to the economics panel (D-181).
|
|
func consume_economy_snapshot() -> void:
|
|
if GameState.economy_snapshot == null or not economics_panel:
|
|
return
|
|
if economics_panel.has_method("receive_economy_data"):
|
|
economics_panel.receive_economy_data(GameState.economy_snapshot)
|
|
GameState.economy_snapshot = null
|
|
|
|
|
|
# #174: Consume examine result — show overlay when server sends character-filtered observation.
|
|
func consume_examine_result() -> void:
|
|
if GameState.current_examine_result == null or not examine_display:
|
|
return
|
|
var result: Dictionary = GameState.current_examine_result
|
|
if dialogue_box and dialogue_box.is_dialogue_active():
|
|
if examine_display.has_method("dismiss"):
|
|
examine_display.dismiss()
|
|
else:
|
|
if examine_display.has_method("show_result"):
|
|
examine_display.show_result(result)
|
|
GameState.current_examine_result = null
|
|
|
|
|
|
## Clear session-scoped recognition state (call on teleport / room change).
|
|
func clear_recognition_state() -> void:
|
|
_known_recognition_ids.clear()
|
|
_known_triangle_ids.clear()
|