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

4.2 KiB
Raw Blame History

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

ChestItemDataResource 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

RoomChestNode2D 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_ids. 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)