docs: add Sprint 20 navigation integration spec and plan
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# 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`:
|
||||
|
||||
```gdscript
|
||||
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:
|
||||
|
||||
```gdscript
|
||||
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:
|
||||
|
||||
```gdscript
|
||||
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:
|
||||
|
||||
```gdscript
|
||||
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
|
||||
Reference in New Issue
Block a user