diff --git a/docs/superpowers/plans/2026-04-17-sprint-14-garten.md b/docs/superpowers/plans/2026-04-17-sprint-14-garten.md new file mode 100644 index 0000000..f1f34c3 --- /dev/null +++ b/docs/superpowers/plans/2026-04-17-sprint-14-garten.md @@ -0,0 +1,594 @@ +# Sprint 14 — Zuhause & Garten Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add the garden/home area below the hospital floors, with interactive gift boxes, a tea pot, and camera navigation buttons in both directions. + +**Architecture:** The Home area lives at world position `(0, 720)` — directly below Floor 0. `RoomNavigator` gains `go_to_home()` / `go_to_hospital()` which tween the camera to `Vector2(640, 1080)`. A `HomeButton` component with `@export var go_to_garden: bool` drives navigation in both directions. No new autoloads; everything hooks into the existing `RoomNavigator` autoload. + +**Tech Stack:** Godot 4.6.2, GDScript (static typing), ColorRect placeholder visuals, Tween animations + +--- + +### Task 1: RoomNavigator — go_to_home / go_to_hospital + +**Files:** +- Modify: `scripts/systems/room_navigator.gd` + +- [ ] **Step 1: Replace the entire file** + +`scripts/systems/room_navigator.gd`: +```gdscript +## RoomNavigator — autoload that moves the Camera2D smoothly between hospital floors, rooms, and the home/garden area. +extends Node + +signal room_changed(floor_index: int, room_index: int) +signal home_entered() +signal hospital_entered() + +const FLOOR_HEIGHT: float = 720.0 +const ROOM_WIDTH: float = 1280.0 +const HOME_CAMERA_X: float = 640.0 +const HOME_CAMERA_Y: float = 1080.0 +const CAMERA_TWEEN_DURATION: float = 0.6 + +var _current_floor: int = 0 +var _current_room: int = 0 +var _is_at_home: bool = false +var _camera: Camera2D + + +func initialize(camera: Camera2D) -> void: + _camera = camera + + +func go_to_floor(floor_index: int) -> void: + go_to_room(floor_index, 0) + + +func go_to_room(floor_index: int, room_index: int) -> void: + if _camera == null: + return + if not _is_at_home and floor_index == _current_floor and room_index == _current_room: + return + _is_at_home = false + _current_floor = floor_index + _current_room = room_index + var target_x: float = room_index * ROOM_WIDTH + ROOM_WIDTH * 0.5 + var target_y: float = floor_index * -FLOOR_HEIGHT + FLOOR_HEIGHT * 0.5 + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(_camera, "position", Vector2(target_x, target_y), CAMERA_TWEEN_DURATION) + tween.finished.connect(func() -> void: room_changed.emit(floor_index, room_index)) + + +func go_to_home() -> void: + if _camera == null: + return + if _is_at_home: + return + _is_at_home = true + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(_camera, "position", Vector2(HOME_CAMERA_X, HOME_CAMERA_Y), CAMERA_TWEEN_DURATION) + tween.finished.connect(func() -> void: home_entered.emit()) + + +func go_to_hospital() -> void: + if _camera == null: + return + if not _is_at_home: + return + _is_at_home = false + var target_x: float = _current_room * ROOM_WIDTH + ROOM_WIDTH * 0.5 + var target_y: float = _current_floor * -FLOOR_HEIGHT + FLOOR_HEIGHT * 0.5 + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(_camera, "position", Vector2(target_x, target_y), CAMERA_TWEEN_DURATION) + tween.finished.connect(func() -> void: hospital_entered.emit()) + + +func get_current_floor() -> int: + return _current_floor + + +func get_current_room() -> int: + return _current_room + + +func is_at_home() -> bool: + return _is_at_home +``` + +- [ ] **Step 2: Verify no parse errors** + +Open Godot, check the Output panel — no errors for `room_navigator.gd`. The autoload entry `RoomNavigator` should still appear in Project → Autoload. + +- [ ] **Step 3: Commit** + +```bash +git add scripts/systems/room_navigator.gd +git commit -m "feat(navigator): add go_to_home / go_to_hospital with home_entered / hospital_entered signals" +``` + +--- + +### Task 2: HomeButton component + +**Files:** +- Create: `scripts/objects/home_button.gd` +- Create: `scenes/objects/HomeButton.tscn` + +- [ ] **Step 1: Create the script** + +`scripts/objects/home_button.gd`: +```gdscript +## HomeButton — tappable button that navigates between the hospital and the garden area. +class_name HomeButton extends Node2D + +const BUTTON_HALF_SIZE: float = 32.0 + +@export var go_to_garden: bool = true + + +func _input(event: InputEvent) -> void: + if not event is InputEventScreenTouch: + return + if not (event as InputEventScreenTouch).pressed: + return + var touch_pos: Vector2 = (event as InputEventScreenTouch).position + var local: Vector2 = to_local(touch_pos) + if abs(local.x) > BUTTON_HALF_SIZE or abs(local.y) > BUTTON_HALF_SIZE: + return + if go_to_garden: + RoomNavigator.go_to_home() + else: + RoomNavigator.go_to_hospital() +``` + +- [ ] **Step 2: Create the scene** + +`scenes/objects/HomeButton.tscn`: +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_homebtn"] + +[ext_resource type="Script" path="res://scripts/objects/home_button.gd" id="1_homebtn"] + +[node name="HomeButton" type="Node2D"] +script = ExtResource("1_homebtn") + +[node name="ButtonBody" type="ColorRect" parent="."] +offset_left = -32.0 +offset_top = -32.0 +offset_right = 32.0 +offset_bottom = 32.0 +color = Color(0.36, 0.70, 0.44, 1) + +[node name="ButtonIcon" type="ColorRect" parent="."] +offset_left = -12.0 +offset_top = -18.0 +offset_right = 12.0 +offset_bottom = 6.0 +color = Color(0.90, 0.88, 0.70, 1) +``` + +- [ ] **Step 3: Verify in Godot** + +Open `HomeButton.tscn` — green square with a cream-coloured rectangle visible. Check Inspector: `go_to_garden` export shows as `true`. + +- [ ] **Step 4: Commit** + +```bash +git add scripts/objects/home_button.gd scenes/objects/HomeButton.tscn +git commit -m "feat(home-button): add HomeButton component for hospital/garden navigation" +``` + +--- + +### Task 3: GiftBox component + +**Files:** +- Create: `scripts/objects/gift_box.gd` +- Create: `scenes/objects/GiftBox.tscn` + +- [ ] **Step 1: Create the script** + +`scripts/objects/gift_box.gd`: +```gdscript +## GiftBox — tap while closed to open: lid rises and fades out, gift inside fades in. +class_name GiftBox extends Node2D + +enum State { CLOSED, OPENING, OPEN } + +const LID_OPEN_Y: float = -120.0 +const OPEN_DURATION: float = 0.5 +const GIFT_FADE_DURATION: float = 0.4 +const BUTTON_HALF_WIDTH: float = 40.0 +const BUTTON_HALF_HEIGHT: float = 50.0 + +var _state: State = State.CLOSED + + +func _ready() -> void: + var gift: Node2D = get_node_or_null("Gift") as Node2D + if gift != null: + gift.modulate.a = 0.0 + + +func _input(event: InputEvent) -> void: + if _state != State.CLOSED: + return + if not event is InputEventScreenTouch: + return + if not (event as InputEventScreenTouch).pressed: + return + var touch_pos: Vector2 = (event as InputEventScreenTouch).position + var local: Vector2 = to_local(touch_pos) + if abs(local.x) > BUTTON_HALF_WIDTH or abs(local.y) > BUTTON_HALF_HEIGHT: + return + _start_opening() + + +func _start_opening() -> void: + _state = State.OPENING + var lid: Node2D = get_node_or_null("Lid") as Node2D + if lid == null: + _state = State.OPEN + return + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_BACK) + tween.tween_property(lid, "position:y", LID_OPEN_Y, OPEN_DURATION) + tween.parallel().tween_property(lid, "modulate:a", 0.0, OPEN_DURATION) + tween.finished.connect(_on_lid_opened) + + +func _on_lid_opened() -> void: + _state = State.OPEN + var gift: Node2D = get_node_or_null("Gift") as Node2D + if gift == null: + return + var tween: Tween = create_tween() + tween.tween_property(gift, "modulate:a", 1.0, GIFT_FADE_DURATION) +``` + +- [ ] **Step 2: Create the scene** + +`scenes/objects/GiftBox.tscn`: +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_giftbox"] + +[ext_resource type="Script" path="res://scripts/objects/gift_box.gd" id="1_giftbox"] + +[node name="GiftBox" type="Node2D"] +script = ExtResource("1_giftbox") + +[node name="BoxBase" type="ColorRect" parent="."] +offset_left = -40.0 +offset_top = -60.0 +offset_right = 40.0 +offset_bottom = 0.0 +color = Color(0.90, 0.28, 0.34, 1) + +[node name="BoxStripe" type="ColorRect" parent="."] +offset_left = -6.0 +offset_top = -60.0 +offset_right = 6.0 +offset_bottom = 0.0 +color = Color(0.98, 0.82, 0.22, 1) + +[node name="Lid" type="Node2D" parent="."] +position = Vector2(0, -60) + +[node name="LidShape" type="ColorRect" parent="Lid"] +offset_left = -44.0 +offset_top = -18.0 +offset_right = 44.0 +offset_bottom = 0.0 +color = Color(0.98, 0.38, 0.42, 1) + +[node name="LidBow" type="ColorRect" parent="Lid"] +offset_left = -10.0 +offset_top = -28.0 +offset_right = 10.0 +offset_bottom = -18.0 +color = Color(0.98, 0.82, 0.22, 1) + +[node name="Gift" type="Node2D" parent="."] +position = Vector2(0, -90) + +[node name="GiftShape" type="ColorRect" parent="Gift"] +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +color = Color(0.96, 0.76, 0.22, 1) +``` + +- [ ] **Step 3: Verify in Godot** + +Open `GiftBox.tscn` — red box body, yellow vertical stripe, red lid above it with yellow bow, yellow star-shaped gift node above lid. The `Gift` node is invisible at runtime (`_ready` sets alpha to 0). + +- [ ] **Step 4: Commit** + +```bash +git add scripts/objects/gift_box.gd scenes/objects/GiftBox.tscn +git commit -m "feat(gift-box): add GiftBox with tap-to-open lid animation and gift reveal" +``` + +--- + +### Task 4: TeaPot component + +**Files:** +- Create: `scripts/objects/tea_pot.gd` +- Create: `scenes/objects/TeaPot.tscn` + +- [ ] **Step 1: Create the script** + +`scripts/objects/tea_pot.gd`: +```gdscript +## TeaPot — tap to pour: tilts 45 degrees, holds briefly, then returns to upright. +class_name TeaPot extends Node2D + +enum State { IDLE, POURING } + +const TILT_ANGLE: float = 45.0 +const TILT_DURATION: float = 0.4 +const POUR_HOLD: float = 0.8 +const RETURN_DURATION: float = 0.4 +const BUTTON_HALF_SIZE: float = 40.0 + +var _state: State = State.IDLE + + +func _input(event: InputEvent) -> void: + if _state != State.IDLE: + return + if not event is InputEventScreenTouch: + return + if not (event as InputEventScreenTouch).pressed: + return + var touch_pos: Vector2 = (event as InputEventScreenTouch).position + var local: Vector2 = to_local(touch_pos) + if abs(local.x) > BUTTON_HALF_SIZE or abs(local.y) > BUTTON_HALF_SIZE: + return + _start_pouring() + + +func _start_pouring() -> void: + _state = State.POURING + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(self, "rotation_degrees", TILT_ANGLE, TILT_DURATION) + tween.tween_interval(POUR_HOLD) + tween.tween_property(self, "rotation_degrees", 0.0, RETURN_DURATION) + tween.finished.connect(func() -> void: _state = State.IDLE) +``` + +- [ ] **Step 2: Create the scene** + +`scenes/objects/TeaPot.tscn`: +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_teapot"] + +[ext_resource type="Script" path="res://scripts/objects/tea_pot.gd" id="1_teapot"] + +[node name="TeaPot" type="Node2D"] +script = ExtResource("1_teapot") + +[node name="PotBody" type="ColorRect" parent="."] +offset_left = -30.0 +offset_top = -50.0 +offset_right = 30.0 +offset_bottom = 0.0 +color = Color(0.96, 0.92, 0.84, 1) + +[node name="PotLid" type="ColorRect" parent="."] +offset_left = -28.0 +offset_top = -58.0 +offset_right = 28.0 +offset_bottom = -48.0 +color = Color(0.74, 0.62, 0.50, 1) + +[node name="PotSpout" type="ColorRect" parent="."] +offset_left = 26.0 +offset_top = -38.0 +offset_right = 46.0 +offset_bottom = -22.0 +color = Color(0.96, 0.92, 0.84, 1) + +[node name="PotHandle" type="ColorRect" parent="."] +offset_left = -48.0 +offset_top = -36.0 +offset_right = -36.0 +offset_bottom = -14.0 +color = Color(0.96, 0.92, 0.84, 1) +``` + +- [ ] **Step 3: Verify in Godot** + +Open `TeaPot.tscn` — cream-coloured pot body, brown lid on top, spout on right, handle on left all visible. + +- [ ] **Step 4: Commit** + +```bash +git add scripts/objects/tea_pot.gd scenes/objects/TeaPot.tscn +git commit -m "feat(teapot): add TeaPot with tap-to-pour tilt animation" +``` + +--- + +### Task 5: GardenParty scene + +**Files:** +- Create: `scenes/rooms/home/GardenParty.tscn` + +- [ ] **Step 1: Create the scene file** + +`scenes/rooms/home/GardenParty.tscn`: +``` +[gd_scene load_steps=4 format=3 uid="uid://cozypaw_gardenparty"] + +[ext_resource type="PackedScene" path="res://scenes/objects/GiftBox.tscn" id="1_giftbox"] +[ext_resource type="PackedScene" path="res://scenes/objects/TeaPot.tscn" id="2_teapot"] +[ext_resource type="PackedScene" path="res://scenes/objects/HomeButton.tscn" id="3_homebtn"] + +[node name="GardenParty" type="Node2D"] + +[node name="Sky" type="ColorRect" parent="."] +offset_left = 0.0 +offset_top = 0.0 +offset_right = 1280.0 +offset_bottom = 400.0 +color = Color(0.53, 0.81, 0.98, 1) + +[node name="Grass" type="ColorRect" parent="."] +offset_left = 0.0 +offset_top = 400.0 +offset_right = 1280.0 +offset_bottom = 720.0 +color = Color(0.55, 0.76, 0.46, 1) + +[node name="GroundEdge" type="ColorRect" parent="."] +offset_left = 0.0 +offset_top = 392.0 +offset_right = 1280.0 +offset_bottom = 404.0 +color = Color(0.38, 0.62, 0.32, 1) + +[node name="TableTop" type="ColorRect" parent="."] +offset_left = 440.0 +offset_top = 464.0 +offset_right = 840.0 +offset_bottom = 480.0 +color = Color(0.82, 0.66, 0.46, 1) + +[node name="TableLeg1" type="ColorRect" parent="."] +offset_left = 456.0 +offset_top = 480.0 +offset_right = 472.0 +offset_bottom = 580.0 +color = Color(0.70, 0.52, 0.34, 1) + +[node name="TableLeg2" type="ColorRect" parent="."] +offset_left = 808.0 +offset_top = 480.0 +offset_right = 824.0 +offset_bottom = 580.0 +color = Color(0.70, 0.52, 0.34, 1) + +[node name="TeaPot" parent="." instance=ExtResource("2_teapot")] +position = Vector2(510, 464) + +[node name="GiftBox1" parent="." instance=ExtResource("1_giftbox")] +position = Vector2(640, 464) + +[node name="GiftBox2" parent="." instance=ExtResource("1_giftbox")] +position = Vector2(730, 464) + +[node name="GiftBox3" parent="." instance=ExtResource("1_giftbox")] +position = Vector2(820, 464) + +[node name="TeaCup" type="ColorRect" parent="."] +offset_left = 558.0 +offset_top = 440.0 +offset_right = 590.0 +offset_bottom = 464.0 +color = Color(0.96, 0.92, 0.84, 1) + +[node name="Balloon1" type="ColorRect" parent="."] +offset_left = 180.0 +offset_top = 120.0 +offset_right = 220.0 +offset_bottom = 180.0 +color = Color(0.96, 0.44, 0.44, 1) + +[node name="Balloon2" type="ColorRect" parent="."] +offset_left = 1020.0 +offset_top = 100.0 +offset_right = 1060.0 +offset_bottom = 160.0 +color = Color(0.56, 0.76, 0.96, 1) + +[node name="HomeButtonReturn" parent="." instance=ExtResource("3_homebtn")] +position = Vector2(100, 620) +go_to_garden = false +``` + +- [ ] **Step 2: Verify in Godot** + +Open `GardenParty.tscn` — blue sky, green grass, dark ground edge, brown table with two legs. TeaPot and 3 GiftBoxes sitting on the table (top at y=464). TeaCup to the left of the pots. Two balloons (red, blue). Green HomeButton bottom-left with `go_to_garden = false` in inspector. + +- [ ] **Step 3: Commit** + +```bash +git add scenes/rooms/home/GardenParty.tscn +git commit -m "feat(garden): add GardenParty scene with table, gifts, teapot, balloons, and return button" +``` + +--- + +### Task 6: Main.tscn — Home integration + +**Files:** +- Modify: `scenes/main/Main.tscn` + +- [ ] **Step 1: Update load_steps** + +Replace: +``` +[gd_scene load_steps=20 format=3 uid="uid://cozypaw_main"] +``` +With: +``` +[gd_scene load_steps=22 format=3 uid="uid://cozypaw_main"] +``` + +- [ ] **Step 2: Add ext_resources after the last existing one (id="18_nursery")** + +``` +[ext_resource type="PackedScene" path="res://scenes/rooms/home/GardenParty.tscn" id="19_gardenparty"] +[ext_resource type="PackedScene" path="res://scenes/objects/HomeButton.tscn" id="20_homebtn"] +``` + +- [ ] **Step 3: Add HomeButtonToGarden on Floor0** + +After the last Floor0 nav arrow node (`NavLeft3to2`), add: +``` +[node name="HomeButtonToGarden" parent="Hospital/Floor0" instance=ExtResource("20_homebtn")] +position = Vector2(640, 620) +go_to_garden = true +``` + +- [ ] **Step 4: Add Home node and GardenParty** + +After the last Floor2 node (`NavLeft2to1_F2`) and before `[node name="Characters" ...`, add: +``` +[node name="Home" type="Node2D" parent="."] +position = Vector2(0, 720) + +[node name="GardenParty" parent="Home" instance=ExtResource("19_gardenparty")] +position = Vector2(0, 0) +``` + +- [ ] **Step 5: Verify in Godot** + +Run the main scene: +1. Scene loads without errors +2. Floor0 shows a green HomeButton square at the bottom centre (x=640, y=620) +3. Tap the HomeButtonToGarden → camera slides smoothly down to garden (blue sky + green grass) +4. Tap HomeButtonReturn in the garden → camera slides back up to Floor0/Reception +5. Elevator buttons between floors 0/1/2 still work correctly +6. Nav arrows between rooms still work on all three floors + +- [ ] **Step 6: Commit** + +```bash +git add scenes/main/Main.tscn +git commit -m "feat(main): integrate Home/GardenParty below Floor0 with bidirectional navigation" +``` diff --git a/docs/superpowers/plans/2026-04-17-sprint-5-7-erdgeschoss.md b/docs/superpowers/plans/2026-04-17-sprint-5-7-erdgeschoss.md new file mode 100644 index 0000000..c983683 --- /dev/null +++ b/docs/superpowers/plans/2026-04-17-sprint-5-7-erdgeschoss.md @@ -0,0 +1,974 @@ +# Sprint 5-7: Erdgeschoss — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Four fully interactive ground-floor rooms (Reception, Gift Shop, Restaurant, Emergency Room) with horizontal room-to-room navigation and an ambulance drive-in animation. + +**Architecture:** Floor 0 rooms are laid out side-by-side at 1280 px intervals along the X axis. `RoomNavigator` is extended to tween the camera both horizontally (room index) and vertically (floor index). `NavigationArrow` objects placed at room boundaries handle left/right navigation. When `go_to_floor()` is called, the room resets to index 0. The ambulance in Emergency Room drives in automatically the first time the player navigates there. + +**Tech Stack:** Godot 4.6.2, GDScript (static typing), placeholder ColorRect graphics, existing `InteractiveObject` + `DragDropComponent` base classes. + +--- + +## File Map + +| Action | File | Responsibility | +|--------|------|----------------| +| Modify | `scripts/systems/room_navigator.gd` | Add horizontal room navigation: `go_to_room(floor, room)` | +| Create | `scripts/objects/navigation_arrow.gd` | Tappable arrow navigating to `(target_floor, target_room)` | +| Create | `scenes/objects/NavigationArrow.tscn` | Visual placeholder (blue button + label) | +| Modify | `scenes/rooms/floor0/Reception.tscn` | Add bench ×2, bell, potted plant | +| Create | `scenes/rooms/floor0/GiftShop.tscn` | Shelf, counter, 4 interactive objects | +| Create | `scenes/rooms/floor0/Restaurant.tscn` | 3 tables, kitchen counter, 4 interactive objects | +| Create | `scripts/objects/ambulance.gd` | Drive-in/out animation, triggered by `RoomNavigator.room_changed` | +| Create | `scenes/objects/Ambulance.tscn` | ColorRect-based ambulance visual | +| Create | `scenes/rooms/floor0/EmergencyRoom.tscn` | Ambulance bay, medical table, IV stand, 2 interactive objects | +| Modify | `scenes/main/Main.tscn` | Add 4 rooms to Floor0, navigation arrows, elevator reposition | + +**Room layout in Floor0 (local X positions):** +- Reception: x=0 (room index 0), camera center world (640, 360) +- GiftShop: x=1280 (room index 1), camera center world (1920, 360) +- Restaurant: x=2560 (room index 2), camera center world (3200, 360) +- EmergencyRoom: x=3840 (room index 3), camera center world (4480, 360) + +--- + +## Task 1: Extend RoomNavigator for horizontal room navigation + +**Files:** +- Modify: `scripts/systems/room_navigator.gd` + +- [ ] **Step 1: Replace room_navigator.gd** + +Full file replacement: + +```gdscript +## RoomNavigator — autoload that moves the Camera2D smoothly between hospital floors and rooms. +extends Node + +signal room_changed(floor_index: int, room_index: int) + +const FLOOR_HEIGHT: float = 720.0 +const ROOM_WIDTH: float = 1280.0 +const CAMERA_TWEEN_DURATION: float = 0.6 + +var _current_floor: int = 0 +var _current_room: int = 0 +var _camera: Camera2D + + +func initialize(camera: Camera2D) -> void: + _camera = camera + + +func go_to_floor(floor_index: int) -> void: + go_to_room(floor_index, 0) + + +func go_to_room(floor_index: int, room_index: int) -> void: + if _camera == null: + return + if floor_index == _current_floor and room_index == _current_room: + return + _current_floor = floor_index + _current_room = room_index + var target_x: float = room_index * ROOM_WIDTH + ROOM_WIDTH * 0.5 + var target_y: float = floor_index * -FLOOR_HEIGHT + FLOOR_HEIGHT * 0.5 + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(_camera, "position", Vector2(target_x, target_y), CAMERA_TWEEN_DURATION) + tween.finished.connect(func() -> void: room_changed.emit(floor_index, room_index)) + + +func get_current_floor() -> int: + return _current_floor + + +func get_current_room() -> int: + return _current_room +``` + +- [ ] **Step 2: Commit** + +```bash +cd "F:/Development/_gameDev/Cozypaw-Hospital" +git add scripts/systems/room_navigator.gd +git commit -m "feat(navigation): extend RoomNavigator for horizontal room-within-floor navigation" +``` + +--- + +## Task 2: NavigationArrow component + +**Files:** +- Create: `scripts/objects/navigation_arrow.gd` +- Create: `scenes/objects/NavigationArrow.tscn` + +- [ ] **Step 1: Create navigation_arrow.gd** + +```gdscript +## NavigationArrow — tappable button that navigates camera to a specific floor and room. +class_name NavigationArrow extends Node2D + +const BUTTON_HALF_SIZE: float = 48.0 + +@export var target_floor: int = 0 +@export var target_room: int = 0 +@export var label_text: String = "→" + + +func _ready() -> void: + var label: Label = get_node_or_null("Label") as Label + if label != null: + label.text = label_text + + +func _input(event: InputEvent) -> void: + var screen_pos: Vector2 + if event is InputEventScreenTouch and event.pressed: + screen_pos = event.position + elif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + screen_pos = event.position + else: + return + var canvas_transform: Transform2D = get_viewport().get_canvas_transform() + var world_pos: Vector2 = canvas_transform.affine_inverse() * screen_pos + var local_pos: Vector2 = to_local(world_pos) + if abs(local_pos.x) <= BUTTON_HALF_SIZE and abs(local_pos.y) <= BUTTON_HALF_SIZE: + _on_pressed() + + +func _on_pressed() -> void: + RoomNavigator.go_to_room(target_floor, target_room) + _play_bounce() + + +func _play_bounce() -> void: + var tween: Tween = create_tween() + tween.tween_property(self, "scale", Vector2(1.15, 1.15), 0.08) + tween.tween_property(self, "scale", Vector2(1.0, 1.0), 0.08) +``` + +- [ ] **Step 2: Create NavigationArrow.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_navarrow"] + +[ext_resource type="Script" path="res://scripts/objects/navigation_arrow.gd" id="1_navarrow"] + +[node name="NavigationArrow" type="Node2D"] +script = ExtResource("1_navarrow") + +[node name="Body" type="ColorRect" parent="."] +color = Color(0.30, 0.60, 1.0, 0.85) +size = Vector2(96, 96) +position = Vector2(-48, -48) + +[node name="Label" type="Label" parent="."] +offset_left = -24.0 +offset_top = -18.0 +offset_right = 24.0 +offset_bottom = 18.0 +text = "→" +theme_override_font_sizes/font_size = 36 +horizontal_alignment = 1 +vertical_alignment = 1 +``` + +> Note: The "→" / "←" label is a placeholder. It will be replaced with a sprite icon in Sprint 15 (Polish). + +- [ ] **Step 3: Commit** + +```bash +git add scripts/objects/navigation_arrow.gd scenes/objects/NavigationArrow.tscn +git commit -m "feat(navigation): add NavigationArrow component for within-floor room navigation" +``` + +--- + +## Task 3: Complete Reception scene + +**Files:** +- Modify: `scenes/rooms/floor0/Reception.tscn` + +Current state: background, counter, floor, walls, one flower. +Add: waiting bench ×2 (with legs), bell on counter, potted plant in corner. + +- [ ] **Step 1: Replace Reception.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_reception"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] + +[node name="Reception" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(0.78, 0.94, 0.80, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.88, 0.80, 0.68, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.92, 0.88, 0.82, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.92, 0.88, 0.82, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="Counter" type="ColorRect" parent="."] +color = Color(0.55, 0.35, 0.18, 1) +size = Vector2(340, 80) +position = Vector2(460, 540) + +[node name="CounterTop" type="ColorRect" parent="."] +color = Color(0.70, 0.50, 0.28, 1) +size = Vector2(340, 12) +position = Vector2(460, 528) + +[node name="NumberDisplayFrame" type="ColorRect" parent="."] +color = Color(0.20, 0.20, 0.20, 1) +size = Vector2(120, 80) +position = Vector2(570, 445) + +[node name="NumberDisplayScreen" type="ColorRect" parent="."] +color = Color(0.10, 0.80, 0.20, 1) +size = Vector2(100, 60) +position = Vector2(580, 455) + +[node name="BenchLeft" type="ColorRect" parent="."] +color = Color(0.60, 0.40, 0.20, 1) +size = Vector2(180, 40) +position = Vector2(100, 578) + +[node name="BenchLeftLeg1" type="ColorRect" parent="."] +color = Color(0.45, 0.28, 0.12, 1) +size = Vector2(16, 32) +position = Vector2(112, 618) + +[node name="BenchLeftLeg2" type="ColorRect" parent="."] +color = Color(0.45, 0.28, 0.12, 1) +size = Vector2(16, 32) +position = Vector2(252, 618) + +[node name="BenchRight" type="ColorRect" parent="."] +color = Color(0.60, 0.40, 0.20, 1) +size = Vector2(180, 40) +position = Vector2(940, 578) + +[node name="BenchRightLeg1" type="ColorRect" parent="."] +color = Color(0.45, 0.28, 0.12, 1) +size = Vector2(16, 32) +position = Vector2(952, 618) + +[node name="BenchRightLeg2" type="ColorRect" parent="."] +color = Color(0.45, 0.28, 0.12, 1) +size = Vector2(16, 32) +position = Vector2(1092, 618) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(300, 520) + +[node name="Flower" parent="." instance=ExtResource("1_iobj")] +position = Vector2(200, 560) + +[node name="Bell" parent="." instance=ExtResource("1_iobj")] +position = Vector2(530, 510) + +[node name="PottedPlant" parent="." instance=ExtResource("1_iobj")] +position = Vector2(1180, 560) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor0/Reception.tscn +git commit -m "feat(reception): complete Reception with waiting benches, bell, and potted plant" +``` + +--- + +## Task 4: Gift Shop scene + +**Files:** +- Create: `scenes/rooms/floor0/GiftShop.tscn` + +- [ ] **Step 1: Create GiftShop.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_giftshop"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] + +[node name="GiftShop" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(1.0, 0.88, 0.92, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.88, 0.80, 0.68, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.96, 0.90, 0.90, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.96, 0.90, 0.90, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="ShelfBoard" type="ColorRect" parent="."] +color = Color(0.75, 0.50, 0.25, 1) +size = Vector2(800, 20) +position = Vector2(240, 360) + +[node name="ShelfLegLeft" type="ColorRect" parent="."] +color = Color(0.60, 0.38, 0.18, 1) +size = Vector2(16, 240) +position = Vector2(240, 380) + +[node name="ShelfLegRight" type="ColorRect" parent="."] +color = Color(0.60, 0.38, 0.18, 1) +size = Vector2(16, 240) +position = Vector2(1024, 380) + +[node name="Counter" type="ColorRect" parent="."] +color = Color(0.60, 0.38, 0.18, 1) +size = Vector2(240, 80) +position = Vector2(520, 540) + +[node name="CounterTop" type="ColorRect" parent="."] +color = Color(0.75, 0.50, 0.25, 1) +size = Vector2(240, 12) +position = Vector2(520, 528) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(300, 520) + +[node name="FlowerBouquet" parent="." instance=ExtResource("1_iobj")] +position = Vector2(380, 330) + +[node name="PlushToy" parent="." instance=ExtResource("1_iobj")] +position = Vector2(640, 330) + +[node name="GiftCard" parent="." instance=ExtResource("1_iobj")] +position = Vector2(900, 330) + +[node name="GiftBox" parent="." instance=ExtResource("1_iobj")] +position = Vector2(640, 510) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor0/GiftShop.tscn +git commit -m "feat(giftshop): add Gift Shop room with shelf, counter, and interactive gift objects" +``` + +--- + +## Task 5: Restaurant scene + +**Files:** +- Create: `scenes/rooms/floor0/Restaurant.tscn` + +- [ ] **Step 1: Create Restaurant.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_restaurant"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] + +[node name="Restaurant" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(1.0, 0.95, 0.82, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.75, 0.65, 0.50, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.96, 0.92, 0.80, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.96, 0.92, 0.80, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="KitchenCounter" type="ColorRect" parent="."] +color = Color(0.55, 0.35, 0.18, 1) +size = Vector2(360, 80) +position = Vector2(460, 240) + +[node name="KitchenCounterTop" type="ColorRect" parent="."] +color = Color(0.70, 0.50, 0.28, 1) +size = Vector2(360, 12) +position = Vector2(460, 228) + +[node name="Table1" type="ColorRect" parent="."] +color = Color(0.65, 0.42, 0.20, 1) +size = Vector2(200, 20) +position = Vector2(120, 520) + +[node name="Table1Leg1" type="ColorRect" parent="."] +color = Color(0.50, 0.32, 0.15, 1) +size = Vector2(16, 100) +position = Vector2(140, 540) + +[node name="Table1Leg2" type="ColorRect" parent="."] +color = Color(0.50, 0.32, 0.15, 1) +size = Vector2(16, 100) +position = Vector2(304, 540) + +[node name="Table2" type="ColorRect" parent="."] +color = Color(0.65, 0.42, 0.20, 1) +size = Vector2(200, 20) +position = Vector2(540, 520) + +[node name="Table2Leg1" type="ColorRect" parent="."] +color = Color(0.50, 0.32, 0.15, 1) +size = Vector2(16, 100) +position = Vector2(560, 540) + +[node name="Table2Leg2" type="ColorRect" parent="."] +color = Color(0.50, 0.32, 0.15, 1) +size = Vector2(16, 100) +position = Vector2(724, 540) + +[node name="Table3" type="ColorRect" parent="."] +color = Color(0.65, 0.42, 0.20, 1) +size = Vector2(200, 20) +position = Vector2(960, 520) + +[node name="Table3Leg1" type="ColorRect" parent="."] +color = Color(0.50, 0.32, 0.15, 1) +size = Vector2(16, 100) +position = Vector2(980, 540) + +[node name="Table3Leg2" type="ColorRect" parent="."] +color = Color(0.50, 0.32, 0.15, 1) +size = Vector2(16, 100) +position = Vector2(1144, 540) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(300, 490) + +[node name="SoupBowl" parent="." instance=ExtResource("1_iobj")] +position = Vector2(220, 490) + +[node name="Sandwich" parent="." instance=ExtResource("1_iobj")] +position = Vector2(640, 490) + +[node name="JuiceGlass" parent="." instance=ExtResource("1_iobj")] +position = Vector2(1060, 490) + +[node name="CashRegister" parent="." instance=ExtResource("1_iobj")] +position = Vector2(640, 210) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor0/Restaurant.tscn +git commit -m "feat(restaurant): add Restaurant room with three tables and interactive food objects" +``` + +--- + +## Task 6: Ambulance component + +**Files:** +- Create: `scripts/objects/ambulance.gd` +- Create: `scenes/objects/Ambulance.tscn` + +The ambulance starts off-screen to the right. When `RoomNavigator` emits `room_changed(0, 3)` (Emergency Room), it drives in. Tapping the ambulance drives it back out; tapping again drives it back in. + +- [ ] **Step 1: Create ambulance.gd** + +```gdscript +## Ambulance — drives in from off-screen right when the Emergency Room is entered; tap to toggle. +class_name Ambulance extends Node2D + +const OFFSCREEN_OFFSET: float = 1200.0 +const BUTTON_HALF_WIDTH: float = 90.0 +const BUTTON_HALF_HEIGHT: float = 50.0 +const DRIVE_DURATION: float = 1.2 + +@export var trigger_floor: int = 0 +@export var trigger_room: int = 3 + +var _parked_x: float = 0.0 +var _is_parked: bool = false +var _is_animating: bool = false + + +func _ready() -> void: + _parked_x = position.x + position.x = _parked_x + OFFSCREEN_OFFSET + RoomNavigator.room_changed.connect(_on_room_changed) + + +func _exit_tree() -> void: + if RoomNavigator.room_changed.is_connected(_on_room_changed): + RoomNavigator.room_changed.disconnect(_on_room_changed) + + +func _on_room_changed(floor_index: int, room_index: int) -> void: + if floor_index == trigger_floor and room_index == trigger_room: + if not _is_parked and not _is_animating: + _drive_in() + + +func _input(event: InputEvent) -> void: + if _is_animating: + return + var screen_pos: Vector2 + if event is InputEventScreenTouch and event.pressed: + screen_pos = event.position + elif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + screen_pos = event.position + else: + return + var canvas_transform: Transform2D = get_viewport().get_canvas_transform() + var world_pos: Vector2 = canvas_transform.affine_inverse() * screen_pos + var local_pos: Vector2 = to_local(world_pos) + if abs(local_pos.x) <= BUTTON_HALF_WIDTH and abs(local_pos.y) <= BUTTON_HALF_HEIGHT: + if _is_parked: + _drive_out() + else: + _drive_in() + + +func _drive_in() -> void: + _is_animating = true + _is_parked = false + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_QUAD) + tween.tween_property(self, "position:x", _parked_x, DRIVE_DURATION) + tween.finished.connect(func() -> void: + _is_parked = true + _is_animating = false + _play_stop_bounce() + ) + + +func _drive_out() -> void: + _is_animating = true + _is_parked = false + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN) + tween.set_trans(Tween.TRANS_QUAD) + tween.tween_property(self, "position:x", _parked_x + OFFSCREEN_OFFSET, DRIVE_DURATION) + tween.finished.connect(func() -> void: + _is_animating = false + ) + + +func _play_stop_bounce() -> void: + var tween: Tween = create_tween() + tween.tween_property(self, "scale", Vector2(1.08, 0.94), 0.08) + tween.tween_property(self, "scale", Vector2(0.96, 1.06), 0.08) + tween.tween_property(self, "scale", Vector2(1.0, 1.0), 0.08) +``` + +- [ ] **Step 2: Create Ambulance.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_ambulance"] + +[ext_resource type="Script" path="res://scripts/objects/ambulance.gd" id="1_ambulance"] + +[node name="Ambulance" type="Node2D"] +script = ExtResource("1_ambulance") + +[node name="Body" type="ColorRect" parent="."] +color = Color(1.0, 1.0, 1.0, 1) +size = Vector2(180, 100) +position = Vector2(-90, -50) + +[node name="RedStripe" type="ColorRect" parent="."] +color = Color(0.90, 0.15, 0.15, 1) +size = Vector2(180, 20) +position = Vector2(-90, -15) + +[node name="Cabin" type="ColorRect" parent="."] +color = Color(0.82, 0.85, 0.90, 1) +size = Vector2(70, 60) +position = Vector2(-90, -50) + +[node name="WindowFront" type="ColorRect" parent="."] +color = Color(0.60, 0.85, 1.0, 1) +size = Vector2(50, 38) +position = Vector2(-82, -46) + +[node name="WheelFront" type="ColorRect" parent="."] +color = Color(0.15, 0.15, 0.15, 1) +size = Vector2(30, 30) +position = Vector2(-75, 48) + +[node name="WheelBack" type="ColorRect" parent="."] +color = Color(0.15, 0.15, 0.15, 1) +size = Vector2(30, 30) +position = Vector2(50, 48) + +[node name="Siren" type="ColorRect" parent="."] +color = Color(0.10, 0.40, 1.0, 1) +size = Vector2(40, 18) +position = Vector2(-20, -68) +``` + +- [ ] **Step 3: Commit** + +```bash +git add scripts/objects/ambulance.gd scenes/objects/Ambulance.tscn +git commit -m "feat(ambulance): add Ambulance component with drive-in/out animation triggered by room navigation" +``` + +--- + +## Task 7: Emergency Room scene + +**Files:** +- Create: `scenes/rooms/floor0/EmergencyRoom.tscn` + +The Ambulance node is placed at position (500, 570) within the room. Its `_ready()` stores `_parked_x = 500` and shifts it to x = 500 + 1200 = 1700 (off-screen right). It drives in when `room_changed(0, 3)` fires. + +- [ ] **Step 1: Create EmergencyRoom.tscn** + +``` +[gd_scene load_steps=3 format=3 uid="uid://cozypaw_emergency"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] +[ext_resource type="PackedScene" path="res://scenes/objects/Ambulance.tscn" id="2_ambulance"] + +[node name="EmergencyRoom" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(0.88, 0.94, 1.0, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.72, 0.74, 0.80, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.90, 0.92, 0.96, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.90, 0.92, 0.96, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="AmbulanceBay" type="ColorRect" parent="."] +color = Color(0.58, 0.60, 0.68, 1) +size = Vector2(380, 120) +position = Vector2(860, 500) + +[node name="AmbulanceBayLine" type="ColorRect" parent="."] +color = Color(0.90, 0.80, 0.10, 1) +size = Vector2(380, 6) +position = Vector2(860, 500) + +[node name="MedicalTable" type="ColorRect" parent="."] +color = Color(0.85, 0.85, 0.92, 1) +size = Vector2(260, 30) +position = Vector2(180, 490) + +[node name="MedicalTableLeg1" type="ColorRect" parent="."] +color = Color(0.68, 0.68, 0.76, 1) +size = Vector2(16, 130) +position = Vector2(200, 520) + +[node name="MedicalTableLeg2" type="ColorRect" parent="."] +color = Color(0.68, 0.68, 0.76, 1) +size = Vector2(16, 130) +position = Vector2(408, 520) + +[node name="IVPole" type="ColorRect" parent="."] +color = Color(0.78, 0.78, 0.84, 1) +size = Vector2(10, 220) +position = Vector2(545, 350) + +[node name="IVBag" type="ColorRect" parent="."] +color = Color(0.55, 0.85, 0.95, 1) +size = Vector2(50, 70) +position = Vector2(525, 280) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(310, 490) + +[node name="Stretcher" parent="." instance=ExtResource("1_iobj")] +position = Vector2(680, 490) + +[node name="IVStand" parent="." instance=ExtResource("1_iobj")] +position = Vector2(550, 440) + +[node name="Ambulance" parent="." instance=ExtResource("2_ambulance")] +position = Vector2(500, 570) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor0/EmergencyRoom.tscn +git commit -m "feat(emergency): add Emergency Room scene with ambulance bay, medical table, and interactive objects" +``` + +--- + +## Task 8: Wire up Floor 0 in Main.tscn + +**Files:** +- Modify: `scenes/main/Main.tscn` + +**Changes:** +- Add GiftShop, Restaurant, EmergencyRoom as instances at x offsets 1280, 2560, 3840 in Floor0 +- Move ElevatorUp0 to position (1100, 160) to avoid overlap with navigation arrows +- Add 6 NavigationArrow instances at room boundaries (3 pairs) +- `load_steps` changes from 9 → 13 (add 4 new ExtResources) + +**NavigationArrow positions in Floor0 local space:** + +| Node name | X | Y | target_floor | target_room | label | +|-----------|---|---|---|---|---| +| NavRight0to1 | 1200 | 480 | 0 | 1 | → | +| NavLeft1to0 | 1340 | 480 | 0 | 0 | ← | +| NavRight1to2 | 2480 | 480 | 0 | 2 | → | +| NavLeft2to1 | 2600 | 480 | 0 | 1 | ← | +| NavRight2to3 | 3760 | 480 | 0 | 3 | → | +| NavLeft3to2 | 3920 | 480 | 0 | 2 | ← | + +**Visibility verification (camera center ± 640 horizontal):** +- Room 0 (cam x=640): screen x 0–1280 → NavRight0to1 at 1200 ✓, elevator at 1100 ✓ +- Room 1 (cam x=1920): screen x 1280–2560 → NavLeft1to0 at 1340 ✓, NavRight1to2 at 2480 ✓ +- Room 2 (cam x=3200): screen x 2560–3840 → NavLeft2to1 at 2600 ✓, NavRight2to3 at 3760 ✓ +- Room 3 (cam x=4480): screen x 3840–5120 → NavLeft3to2 at 3920 ✓ + +- [ ] **Step 1: Replace Main.tscn** + +``` +[gd_scene load_steps=13 format=3 uid="uid://cozypaw_main"] + +[ext_resource type="Script" path="res://scripts/main/main.gd" id="1_main"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/Reception.tscn" id="2_reception"] +[ext_resource type="PackedScene" path="res://scenes/characters/Character.tscn" id="3_char"] +[ext_resource type="PackedScene" path="res://scenes/ui/HUD.tscn" id="4_hud"] +[ext_resource type="PackedScene" path="res://scenes/ui/SettingsMenu.tscn" id="5_settings"] +[ext_resource type="PackedScene" path="res://scenes/objects/ElevatorButton.tscn" id="6_elevbtn"] +[ext_resource type="Script" path="res://scripts/characters/character_data.gd" id="7_chardata"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/GiftShop.tscn" id="8_giftshop"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/Restaurant.tscn" id="9_restaurant"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/EmergencyRoom.tscn" id="10_emergency"] +[ext_resource type="PackedScene" path="res://scenes/objects/NavigationArrow.tscn" id="11_navarrow"] + +[sub_resource type="Resource" id="CharacterData_bunny1"] +script = ExtResource("7_chardata") +id = "bunny_01" +display_name = "Bunny" +species = 0 +state = 0 +current_floor = 0 +position = Vector2(0, 0) + +[node name="Main" type="Node2D"] +script = ExtResource("1_main") + +[node name="Camera2D" type="Camera2D" parent="."] +position = Vector2(640, 360) +zoom = Vector2(1, 1) + +[node name="Hospital" type="Node2D" parent="."] + +[node name="Floor0" type="Node2D" parent="Hospital"] +position = Vector2(0, 0) + +[node name="Reception" parent="Hospital/Floor0" instance=ExtResource("2_reception")] +position = Vector2(0, 0) + +[node name="GiftShop" parent="Hospital/Floor0" instance=ExtResource("8_giftshop")] +position = Vector2(1280, 0) + +[node name="Restaurant" parent="Hospital/Floor0" instance=ExtResource("9_restaurant")] +position = Vector2(2560, 0) + +[node name="EmergencyRoom" parent="Hospital/Floor0" instance=ExtResource("10_emergency")] +position = Vector2(3840, 0) + +[node name="ElevatorUp0" parent="Hospital/Floor0" instance=ExtResource("6_elevbtn")] +position = Vector2(1100, 160) +target_floor = 1 + +[node name="NavRight0to1" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(1200, 480) +target_floor = 0 +target_room = 1 +label_text = "→" + +[node name="NavLeft1to0" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(1340, 480) +target_floor = 0 +target_room = 0 +label_text = "←" + +[node name="NavRight1to2" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(2480, 480) +target_floor = 0 +target_room = 2 +label_text = "→" + +[node name="NavLeft2to1" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(2600, 480) +target_floor = 0 +target_room = 1 +label_text = "←" + +[node name="NavRight2to3" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(3760, 480) +target_floor = 0 +target_room = 3 +label_text = "→" + +[node name="NavLeft3to2" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(3920, 480) +target_floor = 0 +target_room = 2 +label_text = "←" + +[node name="Floor1" type="Node2D" parent="Hospital"] +position = Vector2(0, -720) + +[node name="Background" type="ColorRect" parent="Hospital/Floor1"] +color = Color(0.78, 0.88, 0.96, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="FloorLabel" type="Label" parent="Hospital/Floor1"] +offset_left = 560.0 +offset_top = 300.0 +offset_right = 720.0 +offset_bottom = 360.0 +text = "1. OG" +theme_override_font_sizes/font_size = 32 + +[node name="FloorRect" type="ColorRect" parent="Hospital/Floor1"] +color = Color(0.88, 0.80, 0.68, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="ElevatorDown1" parent="Hospital/Floor1" instance=ExtResource("6_elevbtn")] +position = Vector2(1200, 540) +target_floor = 0 + +[node name="ElevatorUp1" parent="Hospital/Floor1" instance=ExtResource("6_elevbtn")] +position = Vector2(1200, 180) +target_floor = 2 + +[node name="Floor2" type="Node2D" parent="Hospital"] +position = Vector2(0, -1440) + +[node name="Background" type="ColorRect" parent="Hospital/Floor2"] +color = Color(0.96, 0.88, 0.96, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="FloorLabel" type="Label" parent="Hospital/Floor2"] +offset_left = 560.0 +offset_top = 300.0 +offset_right = 720.0 +offset_bottom = 360.0 +text = "2. OG" +theme_override_font_sizes/font_size = 32 + +[node name="FloorRect" type="ColorRect" parent="Hospital/Floor2"] +color = Color(0.88, 0.80, 0.68, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="ElevatorDown2" parent="Hospital/Floor2" instance=ExtResource("6_elevbtn")] +position = Vector2(1200, 360) +target_floor = 1 + +[node name="Characters" type="Node2D" parent="."] + +[node name="Bunny1" parent="Characters" instance=ExtResource("3_char")] +position = Vector2(300, 400) +data = SubResource("CharacterData_bunny1") + +[node name="UI" type="CanvasLayer" parent="."] + +[node name="HUD" parent="UI" instance=ExtResource("4_hud")] + +[node name="SettingsMenu" parent="UI" instance=ExtResource("5_settings")] +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/main/Main.tscn +git commit -m "feat(floor0): wire up all four ground-floor rooms with horizontal navigation arrows" +``` + +- [ ] **Step 3: Push to develop** + +```bash +git push origin develop +``` + +--- + +## Self-Review: Spec Coverage Check + +| Sprint 5-7 requirement | Covered by | +|------------------------|-----------| +| Empfang komplett | Task 3: bench ×2, bell, potted plant, number display | +| Geschenke-Shop | Task 4: full scene with shelf, 4 interactive objects | +| Restaurant | Task 5: full scene with 3 tables, 4 interactive objects | +| Notaufnahme | Task 7: full scene with ambulance bay, table, IV stand | +| Krankenwagen-Animation | Task 6: drive-in on room_changed, drive-out on tap | +| Horizontal room navigation | Task 1 + 2: RoomNavigator + NavigationArrow | +| All rooms accessible from game | Task 8: all wired up in Main.tscn | + +**Placeholder scan:** All code blocks are complete. No "TBD" or "implement later" present. UIDs are convention-based and may be regenerated by Godot on first open — this is expected behavior. + +**Type consistency:** `room_changed(floor_index: int, room_index: int)` signal defined in Task 1 and consumed in Task 6 (`_on_room_changed`). ✓ `go_to_room(floor_index: int, room_index: int)` defined in Task 1 and called in Task 2 (`NavigationArrow._on_pressed()`). ✓ + +--- + +## Smoke Test Checklist (manual, on device after completion) + +- [ ] Game starts at Reception (camera center visible, green background) +- [ ] "→" arrow in Reception navigates smoothly to Gift Shop +- [ ] "←" arrow in Gift Shop navigates back to Reception +- [ ] Full chain: Reception → Gift Shop → Restaurant → Emergency works both directions +- [ ] Elevator Up from Reception navigates to Floor 1 +- [ ] Elevator Down from Floor 1 lands at Reception (room 0), not Emergency Room +- [ ] Tapping flower/bell in Reception plays bounce animation +- [ ] Navigating to Emergency Room for the first time: ambulance drives in from right +- [ ] Tapping ambulance after it's parked: drives back out +- [ ] Tapping ambulance again: drives back in +- [ ] Dragging Bunny character across rooms works correctly +- [ ] Save/load preserves Bunny position after app restart diff --git a/docs/superpowers/plans/2026-04-17-sprint-8-10-obergeschoss.md b/docs/superpowers/plans/2026-04-17-sprint-8-10-obergeschoss.md new file mode 100644 index 0000000..d508c72 --- /dev/null +++ b/docs/superpowers/plans/2026-04-17-sprint-8-10-obergeschoss.md @@ -0,0 +1,804 @@ +# Sprint 8-10: 1. Obergeschoss — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Four fully interactive first-floor rooms (XRay, Pharmacy, Lab, Patient Room) with the X-ray machine's plate slide-in animation as the highlight feature. + +**Architecture:** Floor 1 follows the same side-by-side room layout as Floor 0 — four rooms at X offsets 0, 1280, 2560, 3840 within the `Floor1` node. `XRayMachine` is a new custom component (like `Ambulance`) with its own state machine. The existing `NavigationArrow` and `ElevatorButton` components are reused unchanged. `Main.tscn` is updated to replace the Floor 1 placeholder nodes with the real room scenes. + +**Tech Stack:** Godot 4.6.2, GDScript (static typing), existing `InteractiveObject` base class, placeholder ColorRect graphics. + +--- + +## File Map + +| Action | File | Responsibility | +|--------|------|----------------| +| Create | `scripts/objects/xray_machine.gd` | Plate slide-in animation, 4-state machine (IDLE → SLIDING_IN → EXPOSING → SLIDING_OUT) | +| Create | `scenes/objects/XRayMachine.tscn` | Machine body + viewer screen + sliding plate (all ColorRect) | +| Create | `scenes/rooms/floor1/XRay.tscn` | Exam table, XRayMachine instance, PlasterStation | +| Create | `scenes/rooms/floor1/Pharmacy.tscn` | Medicine shelf, counter, 4 positive medicine objects | +| Create | `scenes/rooms/floor1/Lab.tscn` | Lab bench, 4 interactive lab objects | +| Create | `scenes/rooms/floor1/PatientRoom.tscn` | 2 beds, TV, bedside table | +| Modify | `scenes/main/Main.tscn` | Replace Floor1 placeholder with 4 rooms + nav arrows; move elevator buttons | + +**Room layout in Floor1 (local X positions):** +- XRay: x=0 (room index 0) — camera center world (640, -360) +- Pharmacy: x=1280 (room index 1) — camera center world (1920, -360) +- Lab: x=2560 (room index 2) — camera center world (3200, -360) +- PatientRoom: x=3840 (room index 3) — camera center world (4480, -360) + +**XRayMachine visual layout (local space, origin = machine base center):** +- MachineBody: grey ColorRect 80×200 at local (-40, -200) — tall vertical machine +- MachineArm: grey ColorRect 220×24 at local (-40, -180) — horizontal arm over table +- ViewerFrame: dark ColorRect 80×60 at local (-40, -195) — screen housing on machine +- Viewer: very dark ColorRect 66×46 at local (-33, -189) — screen, lights up during scan +- Plate: Node2D child, starts at local (-50, 20); contains PlateBody (200×14 ColorRect) + - PLATE_PARKED_X = -50.0 (behind/inside machine, hidden) + - PLATE_ACTIVE_X = 130.0 (extends to the right, under the exam table) + +**Floor1 elevator buttons (moved to avoid overlap with nav arrows at x=1200):** +- ElevatorDown1: position (1100, 540) → target_floor = 0 +- ElevatorUp1: position (1100, 180) → target_floor = 2 + +--- + +## Task 1: XRayMachine component + +**Files:** +- Create: `scripts/objects/xray_machine.gd` +- Create: `scenes/objects/XRayMachine.tscn` + +- [ ] **Step 1: Create xray_machine.gd** + +```gdscript +## XRayMachine — X-ray plate slides out when tapped; viewer lights up during exposure. +class_name XRayMachine extends Node2D + +enum State { IDLE, SLIDING_IN, EXPOSING, SLIDING_OUT } + +const SLIDE_DURATION: float = 0.8 +const EXPOSE_DURATION: float = 1.5 +const BUTTON_HALF_WIDTH: float = 50.0 +const BUTTON_HALF_HEIGHT: float = 100.0 +const PLATE_PARKED_X: float = -50.0 +const PLATE_ACTIVE_X: float = 130.0 +const VIEWER_LIT_COLOR: Color = Color(0.75, 0.90, 1.0, 1) +const VIEWER_DARK_COLOR: Color = Color(0.06, 0.08, 0.12, 1) + +var _state: State = State.IDLE + + +func _ready() -> void: + var plate: Node2D = get_node_or_null("Plate") as Node2D + if plate != null: + plate.position.x = PLATE_PARKED_X + + +func _input(event: InputEvent) -> void: + if _state != State.IDLE: + return + var screen_pos: Vector2 + if event is InputEventScreenTouch and event.pressed: + screen_pos = event.position + elif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + screen_pos = event.position + else: + return + var canvas_transform: Transform2D = get_viewport().get_canvas_transform() + var world_pos: Vector2 = canvas_transform.affine_inverse() * screen_pos + var local_pos: Vector2 = to_local(world_pos) + if abs(local_pos.x) <= BUTTON_HALF_WIDTH and abs(local_pos.y) <= BUTTON_HALF_HEIGHT: + _start_scan() + + +func _start_scan() -> void: + _state = State.SLIDING_IN + var plate: Node2D = get_node_or_null("Plate") as Node2D + if plate == null: + _state = State.IDLE + return + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_QUAD) + tween.tween_property(plate, "position:x", PLATE_ACTIVE_X, SLIDE_DURATION) + tween.finished.connect(_on_plate_in) + + +func _on_plate_in() -> void: + _state = State.EXPOSING + var viewer: ColorRect = get_node_or_null("Viewer") as ColorRect + if viewer != null: + viewer.color = VIEWER_LIT_COLOR + var timer: SceneTreeTimer = get_tree().create_timer(EXPOSE_DURATION) + timer.timeout.connect(_start_slide_out) + + +func _start_slide_out() -> void: + _state = State.SLIDING_OUT + var viewer: ColorRect = get_node_or_null("Viewer") as ColorRect + if viewer != null: + viewer.color = VIEWER_DARK_COLOR + var plate: Node2D = get_node_or_null("Plate") as Node2D + if plate == null: + _state = State.IDLE + return + var tween: Tween = create_tween() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_QUAD) + tween.tween_property(plate, "position:x", PLATE_PARKED_X, SLIDE_DURATION) + tween.finished.connect(func() -> void: _state = State.IDLE) +``` + +- [ ] **Step 2: Create XRayMachine.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_xraymachine"] + +[ext_resource type="Script" path="res://scripts/objects/xray_machine.gd" id="1_xraymachine"] + +[node name="XRayMachine" type="Node2D"] +script = ExtResource("1_xraymachine") + +[node name="MachineBody" type="ColorRect" parent="."] +color = Color(0.68, 0.70, 0.76, 1) +size = Vector2(80, 200) +position = Vector2(-40, -200) + +[node name="MachineArm" type="ColorRect" parent="."] +color = Color(0.62, 0.64, 0.70, 1) +size = Vector2(220, 24) +position = Vector2(-40, -180) + +[node name="ViewerFrame" type="ColorRect" parent="."] +color = Color(0.20, 0.22, 0.26, 1) +size = Vector2(80, 60) +position = Vector2(-40, -195) + +[node name="Viewer" type="ColorRect" parent="."] +color = Color(0.06, 0.08, 0.12, 1) +size = Vector2(66, 46) +position = Vector2(-33, -189) + +[node name="Plate" type="Node2D" parent="."] +position = Vector2(-50, 20) + +[node name="PlateBody" type="ColorRect" parent="Plate"] +color = Color(0.28, 0.32, 0.38, 1) +size = Vector2(200, 14) +position = Vector2(0, -7) +``` + +- [ ] **Step 3: Commit** + +```bash +cd "F:/Development/_gameDev/Cozypaw-Hospital" +git add scripts/objects/xray_machine.gd scenes/objects/XRayMachine.tscn +git commit -m "feat(xray): add XRayMachine component with plate slide-in animation" +``` + +--- + +## Task 2: XRay Room scene + +**Files:** +- Create: `scenes/rooms/floor1/XRay.tscn` + +The XRayMachine is placed at (500, 510) in this room. The exam table is to its right. Tapping the machine triggers the plate animation. + +- [ ] **Step 1: Create XRay.tscn** + +``` +[gd_scene load_steps=3 format=3 uid="uid://cozypaw_xray"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] +[ext_resource type="PackedScene" path="res://scenes/objects/XRayMachine.tscn" id="2_xraymachine"] + +[node name="XRay" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(0.88, 0.92, 0.96, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.78, 0.80, 0.86, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.88, 0.90, 0.94, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.88, 0.90, 0.94, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="ExamTable" type="ColorRect" parent="."] +color = Color(0.84, 0.86, 0.90, 1) +size = Vector2(320, 40) +position = Vector2(480, 490) + +[node name="ExamTableLeg1" type="ColorRect" parent="."] +color = Color(0.65, 0.68, 0.74, 1) +size = Vector2(16, 130) +position = Vector2(500, 530) + +[node name="ExamTableLeg2" type="ColorRect" parent="."] +color = Color(0.65, 0.68, 0.74, 1) +size = Vector2(16, 130) +position = Vector2(768, 530) + +[node name="LeadApron" type="ColorRect" parent="."] +color = Color(0.30, 0.40, 0.35, 1) +size = Vector2(60, 120) +position = Vector2(1100, 480) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(200, 490) + +[node name="XRayMachine" parent="." instance=ExtResource("2_xraymachine")] +position = Vector2(500, 510) + +[node name="PlasterStation" parent="." instance=ExtResource("1_iobj")] +position = Vector2(900, 560) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor1/XRay.tscn +git commit -m "feat(xray): add XRay Room scene with exam table and XRayMachine" +``` + +--- + +## Task 3: Pharmacy scene + +**Files:** +- Create: `scenes/rooms/floor1/Pharmacy.tscn` + +All medicine items are positive (vitamins, bandages, syrup, medicine box) — no poison or harmful items per product principles. + +- [ ] **Step 1: Create Pharmacy.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_pharmacy"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] + +[node name="Pharmacy" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(0.96, 0.94, 0.82, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.88, 0.80, 0.68, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.96, 0.94, 0.86, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.96, 0.94, 0.86, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="MedicineShelf" type="ColorRect" parent="."] +color = Color(0.75, 0.55, 0.30, 1) +size = Vector2(700, 20) +position = Vector2(290, 300) + +[node name="MedicineShelfLegLeft" type="ColorRect" parent="."] +color = Color(0.62, 0.44, 0.22, 1) +size = Vector2(16, 320) +position = Vector2(290, 300) + +[node name="MedicineShelfLegRight" type="ColorRect" parent="."] +color = Color(0.62, 0.44, 0.22, 1) +size = Vector2(16, 320) +position = Vector2(974, 300) + +[node name="MedicineShelf2" type="ColorRect" parent="."] +color = Color(0.75, 0.55, 0.30, 1) +size = Vector2(700, 20) +position = Vector2(290, 460) + +[node name="Counter" type="ColorRect" parent="."] +color = Color(0.62, 0.44, 0.22, 1) +size = Vector2(300, 80) +position = Vector2(490, 540) + +[node name="CounterTop" type="ColorRect" parent="."] +color = Color(0.78, 0.62, 0.36, 1) +size = Vector2(300, 12) +position = Vector2(490, 528) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(200, 520) + +[node name="VitaminBottle" parent="." instance=ExtResource("1_iobj")] +position = Vector2(380, 270) + +[node name="BandageRoll" parent="." instance=ExtResource("1_iobj")] +position = Vector2(640, 270) + +[node name="SyrupBottle" parent="." instance=ExtResource("1_iobj")] +position = Vector2(900, 270) + +[node name="MedicineBox" parent="." instance=ExtResource("1_iobj")] +position = Vector2(640, 430) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor1/Pharmacy.tscn +git commit -m "feat(pharmacy): add Pharmacy room with double medicine shelf and positive medicine objects" +``` + +--- + +## Task 4: Lab scene + +**Files:** +- Create: `scenes/rooms/floor1/Lab.tscn` + +- [ ] **Step 1: Create Lab.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_lab"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] + +[node name="Lab" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(0.90, 0.94, 0.96, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.78, 0.82, 0.86, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.88, 0.92, 0.94, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.88, 0.92, 0.94, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="LabBench" type="ColorRect" parent="."] +color = Color(0.88, 0.88, 0.92, 1) +size = Vector2(800, 40) +position = Vector2(240, 480) + +[node name="LabBenchFront" type="ColorRect" parent="."] +color = Color(0.70, 0.72, 0.78, 1) +size = Vector2(800, 16) +position = Vector2(240, 520) + +[node name="LabBenchLegLeft" type="ColorRect" parent="."] +color = Color(0.70, 0.72, 0.78, 1) +size = Vector2(16, 140) +position = Vector2(260, 536) + +[node name="LabBenchLegRight" type="ColorRect" parent="."] +color = Color(0.70, 0.72, 0.78, 1) +size = Vector2(16, 140) +position = Vector2(1008, 536) + +[node name="Sink" type="ColorRect" parent="."] +color = Color(0.75, 0.80, 0.84, 1) +size = Vector2(100, 50) +position = Vector2(1060, 430) + +[node name="SinkBase" type="ColorRect" parent="."] +color = Color(0.60, 0.64, 0.68, 1) +size = Vector2(100, 190) +position = Vector2(1060, 480) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(200, 490) + +[node name="Microscope" parent="." instance=ExtResource("1_iobj")] +position = Vector2(380, 450) + +[node name="TestTubeRack" parent="." instance=ExtResource("1_iobj")] +position = Vector2(600, 450) + +[node name="ReagentBottle" parent="." instance=ExtResource("1_iobj")] +position = Vector2(820, 450) + +[node name="PetriDish" parent="." instance=ExtResource("1_iobj")] +position = Vector2(490, 450) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor1/Lab.tscn +git commit -m "feat(lab): add Lab room with bench, sink, and interactive lab equipment" +``` + +--- + +## Task 5: Patient Room scene + +**Files:** +- Create: `scenes/rooms/floor1/PatientRoom.tscn` + +Two beds, a TV on the wall (interactive object), and a bedside table. The room has a calm, warm feel. + +- [ ] **Step 1: Create PatientRoom.tscn** + +``` +[gd_scene load_steps=2 format=3 uid="uid://cozypaw_patientroom"] + +[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"] + +[node name="PatientRoom" type="Node2D"] + +[node name="Background" type="ColorRect" parent="."] +color = Color(0.96, 0.94, 0.88, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="Floor" type="ColorRect" parent="."] +color = Color(0.88, 0.82, 0.72, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="WallLeft" type="ColorRect" parent="."] +color = Color(0.94, 0.92, 0.86, 1) +size = Vector2(40, 620) +position = Vector2(0, 0) + +[node name="WallRight" type="ColorRect" parent="."] +color = Color(0.94, 0.92, 0.86, 1) +size = Vector2(40, 620) +position = Vector2(1240, 0) + +[node name="Bed1Frame" type="ColorRect" parent="."] +color = Color(0.85, 0.85, 0.90, 1) +size = Vector2(260, 50) +position = Vector2(120, 490) + +[node name="Bed1Pillow" type="ColorRect" parent="."] +color = Color(0.96, 0.96, 1.0, 1) +size = Vector2(80, 50) +position = Vector2(120, 440) + +[node name="Bed1Leg1" type="ColorRect" parent="."] +color = Color(0.70, 0.70, 0.76, 1) +size = Vector2(14, 130) +position = Vector2(136, 540) + +[node name="Bed1Leg2" type="ColorRect" parent="."] +color = Color(0.70, 0.70, 0.76, 1) +size = Vector2(14, 130) +position = Vector2(350, 540) + +[node name="Bed2Frame" type="ColorRect" parent="."] +color = Color(0.85, 0.85, 0.90, 1) +size = Vector2(260, 50) +position = Vector2(680, 490) + +[node name="Bed2Pillow" type="ColorRect" parent="."] +color = Color(0.96, 0.96, 1.0, 1) +size = Vector2(80, 50) +position = Vector2(680, 440) + +[node name="Bed2Leg1" type="ColorRect" parent="."] +color = Color(0.70, 0.70, 0.76, 1) +size = Vector2(14, 130) +position = Vector2(696, 540) + +[node name="Bed2Leg2" type="ColorRect" parent="."] +color = Color(0.70, 0.70, 0.76, 1) +size = Vector2(14, 130) +position = Vector2(910, 540) + +[node name="TVFrame" type="ColorRect" parent="."] +color = Color(0.18, 0.18, 0.20, 1) +size = Vector2(200, 130) +position = Vector2(1000, 200) + +[node name="TVScreen" type="ColorRect" parent="."] +color = Color(0.08, 0.10, 0.14, 1) +size = Vector2(184, 114) +position = Vector2(1008, 208) + +[node name="CharacterSpawn" type="Marker2D" parent="."] +position = Vector2(480, 490) + +[node name="TV" parent="." instance=ExtResource("1_iobj")] +position = Vector2(1100, 265) + +[node name="BedsideTable" parent="." instance=ExtResource("1_iobj")] +position = Vector2(500, 540) +``` + +- [ ] **Step 2: Commit** + +```bash +git add scenes/rooms/floor1/PatientRoom.tscn +git commit -m "feat(patientroom): add Patient Room with two beds, TV, and bedside table" +``` + +--- + +## Task 6: Update Main.tscn — wire up Floor 1 + +**Files:** +- Modify: `scenes/main/Main.tscn` + +**Changes from current file:** +1. `load_steps` increases from 13 → 17 (add 4 new ext_resources: XRay, Pharmacy, Lab, PatientRoom) +2. Add 4 new ext_resource declarations (ids 12–15) +3. In the Floor1 section: **remove** the placeholder `Background`, `FloorLabel`, `FloorRect` nodes +4. **Add** 4 room instances to Floor1 at x offsets 0, 1280, 2560, 3840 +5. **Move** ElevatorDown1 from (1200, 540) → (1100, 540) and ElevatorUp1 from (1200, 180) → (1100, 180) +6. **Add** 6 NavigationArrow instances for Floor1 room navigation + +**NavigationArrow positions in Floor1 local space:** + +| Node | X | Y | target_floor | target_room | label | +|------|---|---|---|---|---| +| NavRight0to1_F1 | 1200 | 480 | 1 | 1 | → | +| NavLeft1to0_F1 | 1340 | 480 | 1 | 0 | ← | +| NavRight1to2_F1 | 2480 | 480 | 1 | 2 | → | +| NavLeft2to1_F1 | 2600 | 480 | 1 | 1 | ← | +| NavRight2to3_F1 | 3760 | 480 | 1 | 3 | → | +| NavLeft3to2_F1 | 3920 | 480 | 1 | 2 | ← | + +- [ ] **Step 1: Read current Main.tscn** + +Read `F:/Development/_gameDev/Cozypaw-Hospital/scenes/main/Main.tscn` to confirm the current content before replacing. + +- [ ] **Step 2: Write the complete new Main.tscn** + +``` +[gd_scene load_steps=17 format=3 uid="uid://cozypaw_main"] + +[ext_resource type="Script" path="res://scripts/main/main.gd" id="1_main"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/Reception.tscn" id="2_reception"] +[ext_resource type="PackedScene" path="res://scenes/characters/Character.tscn" id="3_char"] +[ext_resource type="PackedScene" path="res://scenes/ui/HUD.tscn" id="4_hud"] +[ext_resource type="PackedScene" path="res://scenes/ui/SettingsMenu.tscn" id="5_settings"] +[ext_resource type="PackedScene" path="res://scenes/objects/ElevatorButton.tscn" id="6_elevbtn"] +[ext_resource type="Script" path="res://scripts/characters/character_data.gd" id="7_chardata"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/GiftShop.tscn" id="8_giftshop"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/Restaurant.tscn" id="9_restaurant"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor0/EmergencyRoom.tscn" id="10_emergency"] +[ext_resource type="PackedScene" path="res://scenes/objects/NavigationArrow.tscn" id="11_navarrow"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor1/XRay.tscn" id="12_xray"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor1/Pharmacy.tscn" id="13_pharmacy"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor1/Lab.tscn" id="14_lab"] +[ext_resource type="PackedScene" path="res://scenes/rooms/floor1/PatientRoom.tscn" id="15_patientroom"] + +[sub_resource type="Resource" id="CharacterData_bunny1"] +script = ExtResource("7_chardata") +id = "bunny_01" +display_name = "Bunny" +species = 0 +state = 0 +current_floor = 0 +position = Vector2(0, 0) + +[node name="Main" type="Node2D"] +script = ExtResource("1_main") + +[node name="Camera2D" type="Camera2D" parent="."] +position = Vector2(640, 360) +zoom = Vector2(1, 1) + +[node name="Hospital" type="Node2D" parent="."] + +[node name="Floor0" type="Node2D" parent="Hospital"] +position = Vector2(0, 0) + +[node name="Reception" parent="Hospital/Floor0" instance=ExtResource("2_reception")] +position = Vector2(0, 0) + +[node name="GiftShop" parent="Hospital/Floor0" instance=ExtResource("8_giftshop")] +position = Vector2(1280, 0) + +[node name="Restaurant" parent="Hospital/Floor0" instance=ExtResource("9_restaurant")] +position = Vector2(2560, 0) + +[node name="EmergencyRoom" parent="Hospital/Floor0" instance=ExtResource("10_emergency")] +position = Vector2(3840, 0) + +[node name="ElevatorUp0" parent="Hospital/Floor0" instance=ExtResource("6_elevbtn")] +position = Vector2(1100, 160) +target_floor = 1 + +[node name="NavRight0to1" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(1200, 480) +target_floor = 0 +target_room = 1 +label_text = "→" + +[node name="NavLeft1to0" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(1340, 480) +target_floor = 0 +target_room = 0 +label_text = "←" + +[node name="NavRight1to2" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(2480, 480) +target_floor = 0 +target_room = 2 +label_text = "→" + +[node name="NavLeft2to1" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(2600, 480) +target_floor = 0 +target_room = 1 +label_text = "←" + +[node name="NavRight2to3" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(3760, 480) +target_floor = 0 +target_room = 3 +label_text = "→" + +[node name="NavLeft3to2" parent="Hospital/Floor0" instance=ExtResource("11_navarrow")] +position = Vector2(3920, 480) +target_floor = 0 +target_room = 2 +label_text = "←" + +[node name="Floor1" type="Node2D" parent="Hospital"] +position = Vector2(0, -720) + +[node name="XRay" parent="Hospital/Floor1" instance=ExtResource("12_xray")] +position = Vector2(0, 0) + +[node name="Pharmacy" parent="Hospital/Floor1" instance=ExtResource("13_pharmacy")] +position = Vector2(1280, 0) + +[node name="Lab" parent="Hospital/Floor1" instance=ExtResource("14_lab")] +position = Vector2(2560, 0) + +[node name="PatientRoom" parent="Hospital/Floor1" instance=ExtResource("15_patientroom")] +position = Vector2(3840, 0) + +[node name="ElevatorDown1" parent="Hospital/Floor1" instance=ExtResource("6_elevbtn")] +position = Vector2(1100, 540) +target_floor = 0 + +[node name="ElevatorUp1" parent="Hospital/Floor1" instance=ExtResource("6_elevbtn")] +position = Vector2(1100, 180) +target_floor = 2 + +[node name="NavRight0to1_F1" parent="Hospital/Floor1" instance=ExtResource("11_navarrow")] +position = Vector2(1200, 480) +target_floor = 1 +target_room = 1 +label_text = "→" + +[node name="NavLeft1to0_F1" parent="Hospital/Floor1" instance=ExtResource("11_navarrow")] +position = Vector2(1340, 480) +target_floor = 1 +target_room = 0 +label_text = "←" + +[node name="NavRight1to2_F1" parent="Hospital/Floor1" instance=ExtResource("11_navarrow")] +position = Vector2(2480, 480) +target_floor = 1 +target_room = 2 +label_text = "→" + +[node name="NavLeft2to1_F1" parent="Hospital/Floor1" instance=ExtResource("11_navarrow")] +position = Vector2(2600, 480) +target_floor = 1 +target_room = 1 +label_text = "←" + +[node name="NavRight2to3_F1" parent="Hospital/Floor1" instance=ExtResource("11_navarrow")] +position = Vector2(3760, 480) +target_floor = 1 +target_room = 3 +label_text = "→" + +[node name="NavLeft3to2_F1" parent="Hospital/Floor1" instance=ExtResource("11_navarrow")] +position = Vector2(3920, 480) +target_floor = 1 +target_room = 2 +label_text = "←" + +[node name="Floor2" type="Node2D" parent="Hospital"] +position = Vector2(0, -1440) + +[node name="Background" type="ColorRect" parent="Hospital/Floor2"] +color = Color(0.96, 0.88, 0.96, 1) +size = Vector2(1280, 720) +position = Vector2(0, 0) + +[node name="FloorLabel" type="Label" parent="Hospital/Floor2"] +offset_left = 560.0 +offset_top = 300.0 +offset_right = 720.0 +offset_bottom = 360.0 +text = "2. OG" +theme_override_font_sizes/font_size = 32 + +[node name="FloorRect" type="ColorRect" parent="Hospital/Floor2"] +color = Color(0.88, 0.80, 0.68, 1) +size = Vector2(1280, 100) +position = Vector2(0, 620) + +[node name="ElevatorDown2" parent="Hospital/Floor2" instance=ExtResource("6_elevbtn")] +position = Vector2(1200, 360) +target_floor = 1 + +[node name="Characters" type="Node2D" parent="."] + +[node name="Bunny1" parent="Characters" instance=ExtResource("3_char")] +position = Vector2(300, 400) +data = SubResource("CharacterData_bunny1") + +[node name="UI" type="CanvasLayer" parent="."] + +[node name="HUD" parent="UI" instance=ExtResource("4_hud")] + +[node name="SettingsMenu" parent="UI" instance=ExtResource("5_settings")] +``` + +- [ ] **Step 3: Commit and push** + +```bash +cd "F:/Development/_gameDev/Cozypaw-Hospital" +git add scenes/main/Main.tscn +git commit -m "feat(floor1): wire up all four first-floor rooms with horizontal navigation arrows" +git push origin develop +``` + +--- + +## Self-Review: Spec Coverage Check + +| Sprint 8-10 requirement | Covered by | +|-------------------------|-----------| +| Röntgen mit Slide-Animation | Task 1+2: XRayMachine with 4-state plate animation | +| Apotheke (alle Medikamente positiv) | Task 3: Pharmacy with VitaminBottle, BandageRoll, SyrupBottle, MedicineBox | +| Labor | Task 4: Lab with Microscope, TestTubeRack, ReagentBottle, PetriDish | +| Patientenzimmer | Task 5: PatientRoom with 2 beds, TV, BedsideTable | +| All rooms accessible via elevator + horizontal nav | Task 6: Full Main.tscn wiring | + +**Placeholder scan:** All code blocks complete. No "TBD" or "TODO". ViewerFrame/Viewer nodes referenced in script by node name — both exist in XRayMachine.tscn. ✓ + +**Type consistency:** +- `XRayMachine._on_plate_in()` used as signal callback: matches `tween.finished.connect(_on_plate_in)` ✓ +- `VIEWER_LIT_COLOR` / `VIEWER_DARK_COLOR` used in `_on_plate_in()` and `_start_slide_out()` ✓ +- `Plate` node accessed as `Node2D` (not `ColorRect`) — correct, Plate is a Node2D in the scene ✓ +- `Viewer` node accessed as `ColorRect` — correct, Viewer is a ColorRect in the scene ✓ + +--- + +## Smoke Test Checklist (manual, on device after completion) + +- [ ] Elevator from Floor 0 lands at Floor 1 XRay room +- [ ] "→" navigates Floor 1: XRay → Pharmacy → Lab → PatientRoom and back +- [ ] Tapping XRayMachine: plate slides out to the right, viewer lights up blue-white, plate slides back, viewer goes dark +- [ ] Tapping XRayMachine again during animation: nothing happens (state guard) +- [ ] All medicine objects in Pharmacy bounce when tapped (all positive, no harmful items) +- [ ] All lab objects in Lab bounce when tapped +- [ ] TV and BedsideTable in PatientRoom bounce when tapped +- [ ] Elevator Up from Floor 1 navigates to Floor 2 placeholder +- [ ] Elevator Down from Floor 1 lands at Floor 0 Reception (room 0) +- [ ] Bunny character can be dragged to Floor 1 and position is saved