diff --git a/docs/superpowers/plans/2026-05-08-sprint-16-snap-points.md b/docs/superpowers/plans/2026-05-08-sprint-16-snap-points.md new file mode 100644 index 0000000..dcaa0b8 --- /dev/null +++ b/docs/superpowers/plans/2026-05-08-sprint-16-snap-points.md @@ -0,0 +1,747 @@ +# Sprint 16 — Snap-Point System 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 SnapPoint nodes to all furniture in all 12 rooms so characters can snap into sitting/lying poses when released near a piece of furniture. + +**Architecture:** Each room `.tscn` gets one `ext_resource` entry for `snap_point.gd` and one `[node]` entry per snap position. SnapPoints are direct children of the room node (not children of ColorRect furniture, since ColorRects have no Node2D hierarchy). All SnapPoints auto-register in the `"snap_points"` group via `_ready()`. No new GDScript is written — this sprint is purely scene data. + +**Tech Stack:** Godot 4.6.2, `.tscn` text format, GUT v9.6.0 (TDD), headless runner. + +**GDD Reference:** `docs/game-design.md` — Kapitel 2 (Raum-Übersicht) + Kapitel 5.1 (Snap-Point System). + +**Headless runner:** +``` +"F:/Development/_tools/Godot_v4.6.2-stable_win64/Godot_v4.6.2-stable_win64.exe" --headless -s res://addons/gut/gut_cmdln.gd -gdir=res://test/ -gexit 2>&1 +``` + +**Existing tests must stay green:** 90 tests passing before this sprint starts. + +--- + +## File Map + +| Action | Path | SnapPoints added | +|---|---|---| +| Modify | `scenes/rooms/floor0/Reception.tscn` | 4 × sitting (2 benches × 2) | +| Modify | `scenes/rooms/floor0/GiftShop.tscn` | 1 × sitting (counter) | +| Modify | `scenes/rooms/floor0/Restaurant.tscn` | 6 × sitting (3 tables × 2) | +| Modify | `scenes/rooms/floor0/EmergencyRoom.tscn` | 1 × lying (medical table) | +| Modify | `scenes/rooms/floor1/XRay.tscn` | 1 × lying (exam table) | +| Modify | `scenes/rooms/floor1/Pharmacy.tscn` | 1 × sitting (counter) | +| Modify | `scenes/rooms/floor1/Lab.tscn` | 2 × sitting (lab bench) | +| Modify | `scenes/rooms/floor1/PatientRoom.tscn` | 2 × lying (2 beds) | +| Modify | `scenes/rooms/floor2/Ultrasound.tscn` | 1 × lying (exam table) | +| Modify | `scenes/rooms/floor2/DeliveryRoom.tscn` | 1 × lying (delivery bed) | +| Modify | `scenes/rooms/floor2/Nursery.tscn` | 3 × lying, baby_only (3 cradles) | +| Modify | `scenes/rooms/home/GardenParty.tscn` | 2 × sitting (garden table) | +| Create | `test/unit/test_snap_points_floor0.gd` | Tests for EG (12 snap points) | +| Create | `test/unit/test_snap_points_floor1.gd` | Tests for 1.OG (6 snap points) | +| Create | `test/unit/test_snap_points_floor2.gd` | Tests for 2.OG + Garten (7 snap points) | + +**Total: 25 SnapPoints across 12 rooms.** + +--- + +## How to add a SnapPoint to a .tscn + +### 1. Increment `load_steps` in the header by 1 + +``` +[gd_scene load_steps=3 format=3 ...] ← was 2 +``` + +### 2. Add `ext_resource` for snap_point.gd after the last existing `ext_resource` line + +For rooms with currently 1 ext_resource (InteractiveObject only), use id `"2_snap"`: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="2_snap"] +``` + +For rooms with currently 2 ext_resources, use id `"3_snap"`: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="3_snap"] +``` + +For GardenParty (3 ext_resources), use id `"4_snap"`: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="4_snap"] +``` + +### 3. Append node entries at the end of the file + +Sitting (default pose, no need to set pose or baby_only): +``` +[node name="SnapBenchLeft1" type="Node2D" parent="."] +position = Vector2(150, 555) +script = ExtResource("2_snap") +``` + +Lying (must set pose): +``` +[node name="SnapExamTable" type="Node2D" parent="."] +position = Vector2(640, 480) +script = ExtResource("2_snap") +pose = "lying" +``` + +Lying + baby only: +``` +[node name="SnapCradle1" type="Node2D" parent="."] +position = Vector2(340, 240) +script = ExtResource("3_snap") +pose = "lying" +baby_only = true +``` + +--- + +## Helper: how tests count SnapPoints in a room + +```gdscript +func _count_snaps(room: Node2D) -> int: + var count: int = 0 + for child: Node in room.get_children(): + if child is SnapPoint: + count += 1 + return count + +func _count_snaps_with_pose(room: Node2D, pose: String) -> int: + var count: int = 0 + for child: Node in room.get_children(): + var snap: SnapPoint = child as SnapPoint + if snap != null and snap.pose == pose: + count += 1 + return count + +func _count_snaps_baby_only(room: Node2D) -> int: + var count: int = 0 + for child: Node in room.get_children(): + var snap: SnapPoint = child as SnapPoint + if snap != null and snap.baby_only: + count += 1 + return count +``` + +--- + +## Task 1: Floor 0 — Reception, GiftShop, Restaurant, EmergencyRoom + +**Files:** +- Modify: `scenes/rooms/floor0/Reception.tscn` +- Modify: `scenes/rooms/floor0/GiftShop.tscn` +- Modify: `scenes/rooms/floor0/Restaurant.tscn` +- Modify: `scenes/rooms/floor0/EmergencyRoom.tscn` +- Create: `test/unit/test_snap_points_floor0.gd` + +### Step 1: Write the failing tests + +Create `test/unit/test_snap_points_floor0.gd`: + +```gdscript +## Tests verifying SnapPoints exist in all EG (Erdgeschoss) room scenes. +extends GutTest + + +func _count_snaps(room: Node2D) -> int: + var count: int = 0 + for child: Node in room.get_children(): + if child is SnapPoint: + count += 1 + return count + + +func _count_snaps_with_pose(room: Node2D, pose: String) -> int: + var count: int = 0 + for child: Node in room.get_children(): + var snap: SnapPoint = child as SnapPoint + if snap != null and snap.pose == pose: + count += 1 + return count + + +func test_reception_has_four_snap_points() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/Reception.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 4) + + +func test_reception_all_snaps_are_sitting() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/Reception.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "sitting"), 4) + + +func test_giftshop_has_one_snap_point() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/GiftShop.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 1) + + +func test_giftshop_snap_is_sitting() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/GiftShop.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "sitting"), 1) + + +func test_restaurant_has_six_snap_points() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/Restaurant.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 6) + + +func test_restaurant_all_snaps_are_sitting() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/Restaurant.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "sitting"), 6) + + +func test_emergency_room_has_one_snap_point() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/EmergencyRoom.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 1) + + +func test_emergency_room_snap_is_lying() -> void: + var room: Node2D = preload("res://scenes/rooms/floor0/EmergencyRoom.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "lying"), 1) +``` + +### Step 2: Run tests — verify they FAIL + +``` +"F:/Development/_tools/Godot_v4.6.2-stable_win64/Godot_v4.6.2-stable_win64.exe" --headless -s res://addons/gut/gut_cmdln.gd -gdir=res://test/ -gexit 2>&1 +``` + +Expected: 8 new failures. 90 existing pass. + +### Step 3: Edit `scenes/rooms/floor0/Reception.tscn` + +Current header: `[gd_scene load_steps=2 format=3 uid="uid://cozypaw_reception"]` + +Change to: `[gd_scene load_steps=3 format=3 uid="uid://cozypaw_reception"]` + +After `[ext_resource type="PackedScene" path="res://scenes/objects/InteractiveObject.tscn" id="1_iobj"]`, add: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="2_snap"] +``` + +Append at the end of the file: +``` +[node name="SnapBenchLeft1" type="Node2D" parent="."] +position = Vector2(150, 555) +script = ExtResource("2_snap") + +[node name="SnapBenchLeft2" type="Node2D" parent="."] +position = Vector2(240, 555) +script = ExtResource("2_snap") + +[node name="SnapBenchRight1" type="Node2D" parent="."] +position = Vector2(990, 555) +script = ExtResource("2_snap") + +[node name="SnapBenchRight2" type="Node2D" parent="."] +position = Vector2(1080, 555) +script = ExtResource("2_snap") +``` + +### Step 4: Edit `scenes/rooms/floor0/GiftShop.tscn` + +Change header to `load_steps=3`. + +Add after InteractiveObject ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="2_snap"] +``` + +Append: +``` +[node name="SnapCounter" type="Node2D" parent="."] +position = Vector2(640, 528) +script = ExtResource("2_snap") +``` + +### Step 5: Edit `scenes/rooms/floor0/Restaurant.tscn` + +Change header to `load_steps=3`. + +Add after InteractiveObject ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="2_snap"] +``` + +Append: +``` +[node name="SnapTable1Left" type="Node2D" parent="."] +position = Vector2(160, 510) +script = ExtResource("2_snap") + +[node name="SnapTable1Right" type="Node2D" parent="."] +position = Vector2(280, 510) +script = ExtResource("2_snap") + +[node name="SnapTable2Left" type="Node2D" parent="."] +position = Vector2(580, 510) +script = ExtResource("2_snap") + +[node name="SnapTable2Right" type="Node2D" parent="."] +position = Vector2(700, 510) +script = ExtResource("2_snap") + +[node name="SnapTable3Left" type="Node2D" parent="."] +position = Vector2(1000, 510) +script = ExtResource("2_snap") + +[node name="SnapTable3Right" type="Node2D" parent="."] +position = Vector2(1120, 510) +script = ExtResource("2_snap") +``` + +### Step 6: Edit `scenes/rooms/floor0/EmergencyRoom.tscn` + +Current header: `load_steps=3` (has Ambulance ext_resource). Change to `load_steps=4`. + +Add after the last ext_resource line (Ambulance): +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="3_snap"] +``` + +Append: +``` +[node name="SnapMedicalTable" type="Node2D" parent="."] +position = Vector2(310, 480) +script = ExtResource("3_snap") +pose = "lying" +``` + +### Step 7: Run tests — verify 8 new tests PASS (98 total) + +Run `--headless --import` first if needed, then the test runner. + +Expected: 98/98 passed. + +### Step 8: Commit + +```bash +cd "F:/Development/_gameDev/Cozypaw-Hospital/.worktrees/sprint-16-snap-points" +git add scenes/rooms/floor0/Reception.tscn scenes/rooms/floor0/GiftShop.tscn +git add scenes/rooms/floor0/Restaurant.tscn scenes/rooms/floor0/EmergencyRoom.tscn +git add test/unit/test_snap_points_floor0.gd +git commit -m "feat(snap-points): add SnapPoints to all EG rooms (Reception, GiftShop, Restaurant, EmergencyRoom)" +``` + +--- + +## Task 2: Floor 1 — XRay, Pharmacy, Lab, PatientRoom + +**Files:** +- Modify: `scenes/rooms/floor1/XRay.tscn` +- Modify: `scenes/rooms/floor1/Pharmacy.tscn` +- Modify: `scenes/rooms/floor1/Lab.tscn` +- Modify: `scenes/rooms/floor1/PatientRoom.tscn` +- Create: `test/unit/test_snap_points_floor1.gd` + +### Step 1: Write the failing tests + +Create `test/unit/test_snap_points_floor1.gd`: + +```gdscript +## Tests verifying SnapPoints exist in all 1.OG room scenes. +extends GutTest + + +func _count_snaps(room: Node2D) -> int: + var count: int = 0 + for child: Node in room.get_children(): + if child is SnapPoint: + count += 1 + return count + + +func _count_snaps_with_pose(room: Node2D, pose: String) -> int: + var count: int = 0 + for child: Node in room.get_children(): + var snap: SnapPoint = child as SnapPoint + if snap != null and snap.pose == pose: + count += 1 + return count + + +func test_xray_has_one_snap_point() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/XRay.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 1) + + +func test_xray_snap_is_lying() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/XRay.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "lying"), 1) + + +func test_pharmacy_has_one_snap_point() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/Pharmacy.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 1) + + +func test_pharmacy_snap_is_sitting() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/Pharmacy.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "sitting"), 1) + + +func test_lab_has_two_snap_points() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/Lab.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 2) + + +func test_lab_all_snaps_are_sitting() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/Lab.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "sitting"), 2) + + +func test_patient_room_has_two_snap_points() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/PatientRoom.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 2) + + +func test_patient_room_all_snaps_are_lying() -> void: + var room: Node2D = preload("res://scenes/rooms/floor1/PatientRoom.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "lying"), 2) +``` + +### Step 2: Run tests — verify 8 new tests FAIL + +Expected: 8 failures. 98 existing pass. + +### Step 3: Edit `scenes/rooms/floor1/XRay.tscn` + +Current header: `load_steps=3` (has XRayMachine). Change to `load_steps=4`. + +Add after the last ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="3_snap"] +``` + +Append: +``` +[node name="SnapExamTable" type="Node2D" parent="."] +position = Vector2(640, 480) +script = ExtResource("3_snap") +pose = "lying" +``` + +### Step 4: Edit `scenes/rooms/floor1/Pharmacy.tscn` + +Current header: `load_steps=2`. Change to `load_steps=3`. + +Add after ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="2_snap"] +``` + +Append: +``` +[node name="SnapCounter" type="Node2D" parent="."] +position = Vector2(640, 520) +script = ExtResource("2_snap") +``` + +### Step 5: Edit `scenes/rooms/floor1/Lab.tscn` + +Current header: `load_steps=2`. Change to `load_steps=3`. + +Add after ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="2_snap"] +``` + +Append: +``` +[node name="SnapLabBench1" type="Node2D" parent="."] +position = Vector2(450, 470) +script = ExtResource("2_snap") + +[node name="SnapLabBench2" type="Node2D" parent="."] +position = Vector2(750, 470) +script = ExtResource("2_snap") +``` + +### Step 6: Edit `scenes/rooms/floor1/PatientRoom.tscn` + +Current header: `load_steps=2`. Change to `load_steps=3`. + +Add after ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="2_snap"] +``` + +Append: +``` +[node name="SnapBed1" type="Node2D" parent="."] +position = Vector2(250, 465) +script = ExtResource("2_snap") +pose = "lying" + +[node name="SnapBed2" type="Node2D" parent="."] +position = Vector2(810, 465) +script = ExtResource("2_snap") +pose = "lying" +``` + +### Step 7: Run tests — verify 8 new tests PASS (106 total) + +Run `--headless --import` first if needed, then the test runner. + +Expected: 106/106 passed. + +### Step 8: Commit + +```bash +cd "F:/Development/_gameDev/Cozypaw-Hospital/.worktrees/sprint-16-snap-points" +git add scenes/rooms/floor1/XRay.tscn scenes/rooms/floor1/Pharmacy.tscn +git add scenes/rooms/floor1/Lab.tscn scenes/rooms/floor1/PatientRoom.tscn +git add test/unit/test_snap_points_floor1.gd +git commit -m "feat(snap-points): add SnapPoints to all 1.OG rooms (XRay, Pharmacy, Lab, PatientRoom)" +``` + +--- + +## Task 3: Floor 2 + Garten — Ultrasound, DeliveryRoom, Nursery, GardenParty + +**Files:** +- Modify: `scenes/rooms/floor2/Ultrasound.tscn` +- Modify: `scenes/rooms/floor2/DeliveryRoom.tscn` +- Modify: `scenes/rooms/floor2/Nursery.tscn` +- Modify: `scenes/rooms/home/GardenParty.tscn` +- Create: `test/unit/test_snap_points_floor2.gd` + +### Step 1: Write the failing tests + +Create `test/unit/test_snap_points_floor2.gd`: + +```gdscript +## Tests verifying SnapPoints exist in all 2.OG and Garten room scenes. +extends GutTest + + +func _count_snaps(room: Node2D) -> int: + var count: int = 0 + for child: Node in room.get_children(): + if child is SnapPoint: + count += 1 + return count + + +func _count_snaps_with_pose(room: Node2D, pose: String) -> int: + var count: int = 0 + for child: Node in room.get_children(): + var snap: SnapPoint = child as SnapPoint + if snap != null and snap.pose == pose: + count += 1 + return count + + +func _count_snaps_baby_only(room: Node2D) -> int: + var count: int = 0 + for child: Node in room.get_children(): + var snap: SnapPoint = child as SnapPoint + if snap != null and snap.baby_only: + count += 1 + return count + + +func test_ultrasound_has_one_snap_point() -> void: + var room: Node2D = preload("res://scenes/rooms/floor2/Ultrasound.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 1) + + +func test_ultrasound_snap_is_lying() -> void: + var room: Node2D = preload("res://scenes/rooms/floor2/Ultrasound.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "lying"), 1) + + +func test_delivery_room_has_one_snap_point() -> void: + var room: Node2D = preload("res://scenes/rooms/floor2/DeliveryRoom.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 1) + + +func test_delivery_room_snap_is_lying() -> void: + var room: Node2D = preload("res://scenes/rooms/floor2/DeliveryRoom.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "lying"), 1) + + +func test_nursery_has_three_snap_points() -> void: + var room: Node2D = preload("res://scenes/rooms/floor2/Nursery.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 3) + + +func test_nursery_all_snaps_are_baby_only() -> void: + var room: Node2D = preload("res://scenes/rooms/floor2/Nursery.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_baby_only(room), 3) + + +func test_nursery_all_snaps_are_lying() -> void: + var room: Node2D = preload("res://scenes/rooms/floor2/Nursery.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "lying"), 3) + + +func test_garden_party_has_two_snap_points() -> void: + var room: Node2D = preload("res://scenes/rooms/home/GardenParty.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps(room), 2) + + +func test_garden_party_all_snaps_are_sitting() -> void: + var room: Node2D = preload("res://scenes/rooms/home/GardenParty.tscn").instantiate() as Node2D + add_child_autofree(room) + assert_eq(_count_snaps_with_pose(room, "sitting"), 2) +``` + +### Step 2: Run tests — verify 9 new tests FAIL + +Expected: 9 failures. 106 existing pass. + +### Step 3: Edit `scenes/rooms/floor2/Ultrasound.tscn` + +Current header: `load_steps=3` (has UltrasoundMachine). Change to `load_steps=4`. + +Add after last ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="3_snap"] +``` + +Append: +``` +[node name="SnapExamTable" type="Node2D" parent="."] +position = Vector2(470, 480) +script = ExtResource("3_snap") +pose = "lying" +``` + +### Step 4: Edit `scenes/rooms/floor2/DeliveryRoom.tscn` + +Current header: `load_steps=3` (has DeliveryBed). Change to `load_steps=4`. + +Add after last ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="3_snap"] +``` + +Append: +``` +[node name="SnapDeliveryBed" type="Node2D" parent="."] +position = Vector2(540, 480) +script = ExtResource("3_snap") +pose = "lying" +``` + +### Step 5: Edit `scenes/rooms/floor2/Nursery.tscn` + +Current header: `load_steps=3` (has Cradle). Change to `load_steps=4`. + +Add after last ext_resource: +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="3_snap"] +``` + +Append: +``` +[node name="SnapCradle1" type="Node2D" parent="."] +position = Vector2(340, 240) +script = ExtResource("3_snap") +pose = "lying" +baby_only = true + +[node name="SnapCradle2" type="Node2D" parent="."] +position = Vector2(600, 240) +script = ExtResource("3_snap") +pose = "lying" +baby_only = true + +[node name="SnapCradle3" type="Node2D" parent="."] +position = Vector2(860, 240) +script = ExtResource("3_snap") +pose = "lying" +baby_only = true +``` + +### Step 6: Edit `scenes/rooms/home/GardenParty.tscn` + +Current header: `load_steps=4` (has GiftBox, TeaPot, HomeButton). Change to `load_steps=5`. + +Add after the last ext_resource (HomeButton): +``` +[ext_resource type="Script" path="res://scripts/objects/snap_point.gd" id="4_snap"] +``` + +Append: +``` +[node name="SnapTableLeft" type="Node2D" parent="."] +position = Vector2(530, 455) +script = ExtResource("4_snap") + +[node name="SnapTableRight" type="Node2D" parent="."] +position = Vector2(750, 455) +script = ExtResource("4_snap") +``` + +### Step 7: Run tests — verify 9 new tests PASS (115 total) + +Run `--headless --import` first, then the test runner. + +Expected: 115/115 passed. + +### Step 8: Commit + +```bash +cd "F:/Development/_gameDev/Cozypaw-Hospital/.worktrees/sprint-16-snap-points" +git add scenes/rooms/floor2/Ultrasound.tscn scenes/rooms/floor2/DeliveryRoom.tscn +git add scenes/rooms/floor2/Nursery.tscn scenes/rooms/home/GardenParty.tscn +git add test/unit/test_snap_points_floor2.gd +git commit -m "feat(snap-points): add SnapPoints to all 2.OG and Garten rooms (Ultrasound, DeliveryRoom, Nursery, GardenParty)" +``` + +--- + +## Final Check + +- [ ] **Run full test suite one last time** + +``` +"F:/Development/_tools/Godot_v4.6.2-stable_win64/Godot_v4.6.2-stable_win64.exe" --headless -s res://addons/gut/gut_cmdln.gd -gdir=res://test/ -gexit 2>&1 +``` + +Expected output: +``` +Scripts 10 +Tests 115 +Passing Tests 115 +---- All tests passed! ---- +``` + +- [ ] **Verify git log shows 3 clean commits** + +```bash +git log --oneline -4 +``` + +Expected: +``` +feat(snap-points): add SnapPoints to all 2.OG and Garten rooms... +feat(snap-points): add SnapPoints to all 1.OG rooms... +feat(snap-points): add SnapPoints to all EG rooms... +```