fix(client): address PR #70 review — event leak, quit flush, notification color

- input_mapper.gd: call set_input_as_handled() before early return on
  empty game_id so F5/F6 events don't propagate to other handlers
- session_manager.gd: defer scene change by one frame after buffering
  quit-save so SimBridge._process() flushes the outbound buffer
- monologue_display.gd: tag queued notifications with is_notification
  flag so drain path routes to _show_notification_line (correct color)
  instead of _show_line (lattice-profile fallback color)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 15:43:53 +01:00
co-authored by Claude Opus 4.6
parent e1ea07e746
commit 3725a3df5e
3 changed files with 24 additions and 3 deletions
+1
View File
@@ -128,6 +128,7 @@ func _unhandled_input(event: InputEvent) -> void:
if action == Action.SAVE_GAME or action == Action.LOAD_GAME:
var game_id := GameState.current_game_id
if game_id.is_empty():
get_viewport().set_input_as_handled()
return # No active session — ignore save/load
entry["action_data"] = {"path": "user://saves/" + game_id + "/quicksave.sav"}
input_queue.append(entry)
+10 -1
View File
@@ -85,6 +85,8 @@ func quit_to_menu() -> void:
func _do_quit_to_menu() -> void:
_cleanup_quit_dialog()
# #554: Trigger quicksave before navigating to menu.
# send_input() buffers the command — defer scene change by one frame so
# SimBridge._process() flushes the outbound buffer before teardown.
if not GameState.current_game_id.is_empty():
var path := "user://saves/" + GameState.current_game_id + "/quicksave.sav"
SimBridge.send_input({
@@ -92,7 +94,14 @@ func _do_quit_to_menu() -> void:
"timestamp_msec": Time.get_ticks_msec(),
"action_data": {"path": path},
})
GameState.current_game_id = ""
GameState.current_game_id = ""
_navigate_to_menu.call_deferred()
else:
GameState.current_game_id = ""
get_tree().change_scene_to_file(MENU_SCENE)
func _navigate_to_menu() -> void:
get_tree().change_scene_to_file(MENU_SCENE)
+13 -2
View File
@@ -64,7 +64,10 @@ func _process(delta: float) -> void:
var now := float(Time.get_ticks_msec())
if now >= _next_fade_in_msec:
var next: Dictionary = _queue.pop_front()
_show_line(next.text, next.duration, next.priority, next.is_urgent, next.lattice_profile)
if next.get("is_notification", false):
_show_notification_line(next.text)
else:
_show_line(next.text, next.duration, next.priority, next.is_urgent, next.lattice_profile)
# Display a monologue line.
@@ -82,7 +85,15 @@ func show_notification(text: String) -> void:
if _visible.size() < MAX_VISIBLE and now >= _next_fade_in_msec:
_show_notification_line(text)
else:
_enqueue(text, _NOTIFICATION_DURATION, 1, false, "")
var entry := {text = text, duration = _NOTIFICATION_DURATION, priority = 1, is_urgent = false, lattice_profile = "", is_notification = true}
if _queue.size() < MAX_QUEUE:
_queue.append(entry)
_queue.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return a.priority > b.priority)
else:
var lowest := _lowest_priority_idx()
if 1 >= _queue[lowest].priority:
_queue[lowest] = entry
_queue.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return a.priority > b.priority)
func show_monologue(text: String, duration: float, priority: int = 2, is_urgent: bool = false) -> void: