Files
Cozypaw-Hospital/docs/superpowers/specs/2026-05-09-sprint-18-room-chests.md
T
2026-05-09 00:43:38 +02:00

85 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sprint 18 — Room Chests & Item Spawning Design Spec
## Goal
Implement a `RoomChest` system: tappable storage nodes in every room that spawn `HoldableItem`/`OutfitItem` instances with a fly-out tween. Items persist in the world until manually dragged back to their chest. All 12 existing rooms get populated with placeholder items (string IDs, no textures yet).
## Architecture
### Data Layer
**`ChestItemData`** — `Resource` subclass, one entry per item slot in a chest.
| Field | Type | Purpose |
|---|---|---|
| `item_id` | `String` | Canonical item identifier, e.g. `"stethoscope"` |
| `item_type` | `String` | `"holdable"` or `"outfit"` |
| `outfit_layer` | `int` | 13, only relevant when `item_type == "outfit"` |
| `spawn_offset` | `Vector2` | Relative target position for fly-out tween |
**`RoomChest`** — `Node2D` subclass.
| Field | Type | Purpose |
|---|---|---|
| `chest_id` | `String` | Unique identifier, e.g. `"pharmacy_medicine"` |
| `items` | `Array[ChestItemData]` | Configured items for this chest |
| `_spawned_items` | `Array` | Currently live item nodes in the world |
### Spawn Behaviour
1. Player taps `RoomChest`.
2. If `_spawned_items` is not empty → no-op (already spawned).
3. Otherwise: for each `ChestItemData`, instantiate a `HoldableItem` or `OutfitItem` node. Set `item.home_chest = self`. Reparent to the room scene (not the chest). Tween `global_position` from `chest.global_position` to `chest.global_position + item_data.spawn_offset`.
### Return Behaviour
`HoldableItem._on_drag_released` checks (in priority order):
1. **Chest return** — if `home_chest != null` and `global_position.distance_to(home_chest.global_position) < CHEST_RETURN_RADIUS (80.0)`: tween back to chest → `home_chest.receive_item(self)``queue_free()`.
2. **Outfit apply** — existing OutfitItem logic (80 px body proximity).
3. **Hand slot attach** — existing HoldableItem logic (60 px hand slot proximity).
`RoomChest.receive_item(item: HoldableItem)` removes the item from `_spawned_items`.
### GameState v3
Extend `GameState` with `_chest_states: Dictionary` mapping `chest_id → Array[String]` of currently spawned `item_id`s. Persist in save data as `"version": 3`. Item positions within the room are **not** persisted (deliberate simplification). On load: if a chest has entries in `_chest_states`, re-spawn those items via the normal fly-out tween from the chest — they land at their configured `spawn_offset`, not their last dragged position.
## Room Population
All 12 existing room `.tscn` files get `RoomChest` nodes. No textures in this sprint — `item_id` strings only.
| Room | chest_id | item_ids |
|---|---|---|
| Reception | `reception_desk` | `clipboard`, `pen`, `bandage` |
| GiftShop | `giftshop_shelf` | `gift_box`, `ribbon`, `balloon` |
| Restaurant | `restaurant_counter` | `teacup`, `plate`, `spoon` |
| Emergency | `emergency_cabinet` | `bandage_roll`, `syringe`, `ice_pack` |
| XRay | `xray_cabinet` | `xray_sheet`, `lead_apron`*, `marker` |
| Pharmacy | `pharmacy_medicine` | `pill_bottle`, `syrup` |
| Pharmacy | `pharmacy_tools` | `mortar`, `spatula` |
| Lab | `lab_bench` | `test_tube`, `pipette`, `microscope_slide` |
| PatientRooms | `patient_cabinet` | `thermometer`, `stethoscope`*, `pillow` |
| Ultrasound | `ultrasound_cart` | `gel_tube`, `probe`, `towel` |
| DeliveryRoom | `delivery_cabinet` | `swaddle`*, `scissors`, `cord_clamp` |
| Nursery | `nursery_shelf` | `bottle`, `rattle`, `blanket`* |
| GardenParty | `garden_table` | `teapot`, `cake` |
| GardenParty | `garden_storage` | `confetti`, `party_hat` |
`*` = `item_type: "outfit"` (layer: `lead_apron`→1, `stethoscope`→2, `swaddle`→1, `blanket`→1)
## Testing
- `test/unit/test_room_chest.gd` — unit tests for `RoomChest` and `ChestItemData`
- `test/unit/test_game_state.gd` — appended tests for GameState v3 chest state
- `test/unit/test_holdable_item.gd` — appended tests for chest-return priority
Tests follow GUT v9.6.0, `extends GutTest`, `add_child_autofree`.
## Out of Scope
- Item textures / sprites (Sprint 18 uses null textures throughout)
- Chest open/close sprite animation (visual polish, later sprint)
- Per-item sounds (AudioManager sprint)
- Chest state per save-slot (single save file, existing SaveManager)