3.8 KiB
Sprint 20 — Navigation-Integration Design Spec
Goal
Close the loop between RoomNavigator, GameState, and AudioManager. Three concrete bugs:
AudioManager.DEFAULT_MUSIC_VOLUMEmissing → HUD crashes on music toggleGameState.current_roomnever updated during gameplay → floor music never switches- 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— callsset_current_room("xray"), assertscurrent_room == "xray"test_set_current_room_emits_state_changed— connects tostate_changed, callsset_current_room, verifies signal fired
test/unit/test_room_navigator.gd — new file:
test_room_names_dict_has_eleven_entriestest_get_room_name_floor0_room0_is_receptiontest_get_room_name_floor1_room2_is_labtest_get_room_name_floor2_room1_is_delivery_roomtest_get_room_name_unknown_returns_emptytest_go_to_room_by_name_sets_home_for_garden_party— verifiesis_at_home()after calltest_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— assertsAudioManager.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_roomstring