Files
Cozypaw-Hospital/docs/superpowers/specs/2026-05-10-sprint-20-navigation-integration.md
T
2026-05-10 20:50:13 +02:00

3.8 KiB

Sprint 20 — Navigation-Integration Design Spec

Goal

Close the loop between RoomNavigator, GameState, and AudioManager. Three concrete bugs:

  1. AudioManager.DEFAULT_MUSIC_VOLUME missing → HUD crashes on music toggle
  2. GameState.current_room never updated during gameplay → floor music never switches
  3. Camera not restored to saved room on game load

Changes

GameState

Add set_current_room(room: String) -> void:

func set_current_room(room: String) -> void:
    current_room = room
    state_changed.emit()

GameState already persists current_room in get_save_data() / apply_save_data(). No version bump needed — structure unchanged.

RoomNavigator

Add room name lookup and call GameState.set_current_room() on every navigation:

const _ROOM_NAMES: Dictionary = {
    Vector2i(0, 0): "reception",
    Vector2i(0, 1): "giftshop",
    Vector2i(0, 2): "restaurant",
    Vector2i(0, 3): "emergency",
    Vector2i(1, 0): "xray",
    Vector2i(1, 1): "pharmacy",
    Vector2i(1, 2): "lab",
    Vector2i(1, 3): "patient_rooms",
    Vector2i(2, 0): "ultrasound",
    Vector2i(2, 1): "delivery_room",
    Vector2i(2, 2): "nursery",
}
const HOME_ROOM_NAME: String = "garden_party"

go_to_room(floor_index, room_index) → after setting internal state, call GameState.set_current_room(_ROOM_NAMES.get(Vector2i(floor_index, room_index), "")).

go_to_home()GameState.set_current_room(HOME_ROOM_NAME).

go_to_hospital()GameState.set_current_room(_ROOM_NAMES.get(Vector2i(_current_floor, _current_room), "")).

Add public get_room_name(floor_index: int, room_index: int) -> String — returns the room name or "".

Add go_to_room_by_name(room_name: String) -> void — reverse lookup: if room_name == HOME_ROOM_NAME, call go_to_home(); otherwise search _ROOM_NAMES for matching value and call go_to_room().

AudioManager

Add missing constant:

const DEFAULT_MUSIC_VOLUME: float = 0.6

No other changes — the existing _on_game_state_changed() observer already handles room-based music switching correctly once GameState.set_current_room() is wired up.

main.gd

After SaveManager.load_game(), restore camera to the saved room:

RoomNavigator.go_to_room_by_name(GameState.current_room)

(Called without tween wait — camera teleports to saved position on startup, no animation.)

To suppress the cross-fade animation on startup (no music cross-fade before the initial room is set), go_to_room_by_name passes a flag or AudioManager's _current_floor is -1 so the first play_floor_music call is treated as initial — this already works correctly since _current_floor starts at -1.

Testing

test/unit/test_game_state.gd — append:

  • test_set_current_room_updates_value — calls set_current_room("xray"), asserts current_room == "xray"
  • test_set_current_room_emits_state_changed — connects to state_changed, calls set_current_room, verifies signal fired

test/unit/test_room_navigator.gd — new file:

  • test_room_names_dict_has_eleven_entries
  • test_get_room_name_floor0_room0_is_reception
  • test_get_room_name_floor1_room2_is_lab
  • test_get_room_name_floor2_room1_is_delivery_room
  • test_get_room_name_unknown_returns_empty
  • test_go_to_room_by_name_sets_home_for_garden_party — verifies is_at_home() after call
  • test_go_to_room_by_name_unknown_is_noop — no crash, no state change

test/unit/test_audio_manager.gd — append:

  • test_default_music_volume_constant_is_0_6 — asserts AudioManager.DEFAULT_MUSIC_VOLUME == 0.6

Out of Scope

  • Camera pan animation on game restore (instant teleport is sufficient)
  • Floor 2 home button wiring (navigation_arrow for garden already exists in Main.tscn)
  • Room-within-floor persistence beyond the existing current_room string