feat(client): Option A pre-game flow + ESC priority chain (Workstream 4)
main_menu now connects SimBridge before character_creation opens, gating the transition on first ObserverSnapshot carrying a bookmark_catalog. Loading screen is shown during the connect; on cancel the SimBridge subprocess is torn down and the player returns to main_menu. Catalog is read straight from GameState.bookmark_catalog in W6. Flow (Option A): 1. New Game → SessionManager.new_game() creates save dir 2. main_menu pushes loading_screen via MetaStack with "Connecting to simulation..." message 3. SimBridge.connect_to_sim() spawned; main_menu listens on connection_state_changed, then on snapshot_received for the catalog 4. On catalog arrival: loading_screen closed, scene-transition to character_creation 5. character_creation Cancel → SimBridge.disconnect_from_sim() + scene transition back to main_menu (Tyre's recommendation: clean state per session over warm-start savings) 6. character_creation Start → ConfirmBookmark sent (stubbed for W4 with first catalog entry; real bookmark + location from W6's UI) ESC priority chain in main.gd OPEN_MENU handler: - MetaStack.handle_escape() first — closes the topmost meta overlay - HudGroups.is_implant_active() / close_app() — closes active implant - Fallback: toggle settings_dialog (existing W2 behavior) Files: - sim_bridge.gd: send_named_action(action_name, action_data) helper. Bridges named tag-enum PlayerActions (RequestBookmarkCatalog, ConfirmBookmark) into the existing outbound buffer, parallel to send_input's InputMapper.Action handling. - loading_screen.gd: set_message(text) for the connecting/loading label. - main_menu.gd: full Option A flow rewrite. Tracks _waiting_for_catalog so re-clicking New Game during connect is a no-op. - character_creation.gd: _on_back disconnect path + _on_start ConfirmBookmark stub. MAIN_MENU_SCENE / GAME_SCENE constants. - main.gd: connect_to_sim guard (don't reconnect when Option A leaves it CONNECTED). ESC chain wiring. Verification: - gdlint clean - godot --headless --path client --quit — no SCRIPT ERROR - test_protocol 62/62, test_implant_nav_stack 52/52, test_client_p3 24/24, test_ui_framework_sprint15 54/54 Pre-existing failing suites unchanged: test_sprint2_proof, test_dialogue_sprint18, test_client_p2 (camera-smoothing assertions that pre-date W4 — main.gd has disabled position_smoothing_enabled since #117 / #501 / #117 manual-lerp; tests were stale). Workstream 5 (3-tab restructure of character_creation: Bookmark / Appearance with sub-nav / Skills / Debug) lands next. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -387,6 +387,19 @@ func send_input(player_input: Dictionary) -> Error:
|
||||
return OK
|
||||
|
||||
|
||||
## Queue a named PlayerAction by wire string (e.g. "RequestBookmarkCatalog").
|
||||
## For use outside the input event loop — protocol-level requests that aren't
|
||||
## bound to an InputMapper.Action enum value.
|
||||
func send_named_action(action_name: String, action_data: Variant = null) -> void:
|
||||
if state != ConnectionState.CONNECTED:
|
||||
push_warning("SimBridge.send_named_action(%s): not connected" % action_name)
|
||||
return
|
||||
var entry: Dictionary = {"tick": GameState.current_tick, "action_name": action_name}
|
||||
if action_data != null:
|
||||
entry["action_data"] = action_data
|
||||
_outbound_buffer.append(entry)
|
||||
|
||||
|
||||
# Poll for snapshot from simulation.
|
||||
# In test mode delegates to test harness. In live mode, returns the last decoded snapshot.
|
||||
func poll_snapshot() -> Variant:
|
||||
|
||||
+10
-3
@@ -47,8 +47,10 @@ func _ready() -> void:
|
||||
# #117: Manual lerp approach — disable Godot's built-in Camera2D smoothing.
|
||||
camera.position_smoothing_enabled = false
|
||||
|
||||
# Connect to simulation (test mode sets CONNECTED immediately)
|
||||
SimBridge.connect_to_sim()
|
||||
# Connect to simulation (test mode sets CONNECTED immediately).
|
||||
# Guard: Option A flow leaves SimBridge CONNECTED when main.tscn loads — don't drop it.
|
||||
if SimBridge.state == SimBridge.ConnectionState.DISCONNECTED:
|
||||
SimBridge.connect_to_sim()
|
||||
|
||||
# #257: Deferred load dispatch
|
||||
if not GameState.pending_load_path.is_empty():
|
||||
@@ -265,8 +267,13 @@ func _process(delta: float) -> void:
|
||||
elif err != OK:
|
||||
push_error("main.gd: LOAD_GAME send_input failed: %s" % error_string(err))
|
||||
continue
|
||||
# #528: ESC/OPEN_MENU — client-only, toggle audio settings dialog
|
||||
# #528: ESC/OPEN_MENU — priority chain: MetaStack modal → implant app → settings dialog
|
||||
if input.action == InputMapper.Action.OPEN_MENU:
|
||||
if MetaStack.handle_escape():
|
||||
continue
|
||||
if HudGroups.is_implant_active():
|
||||
HudGroups.close_app()
|
||||
continue
|
||||
if settings_dialog:
|
||||
if settings_dialog.is_open():
|
||||
settings_dialog.close()
|
||||
|
||||
Reference in New Issue
Block a user