Files
Cozypaw-Hospital/docs/superpowers/specs/2026-05-11-sprint-14-garden-party.md
T
Steven Wroblewski 8f0569766c docs(sprint-14): add garden party spec
GiftBox auto-reset, Balloon pop/respawn, Cake cut/reset, chair snap points.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 15:26:14 +02:00

5.5 KiB
Raw Blame History

Sprint 14 — Garden Party Spec

Goal

Make the GardenParty scene a fully playable sandbox room with three interactive objects (GiftBox auto-reset, Balloon pop/respawn, Cake cut/reset) and character snap-to-chair at the party table.


Scope

In scope:

  • gift_box.gd: add RESETTING state (auto-reset 3 s after opening)
  • balloon.gd: new script — tap to pop, auto-respawn after 5 s
  • cake.gd: new script — tap to cut slice, auto-reset after 3 s
  • GardenParty.tscn: replace GiftBox3 with Cake, add Cake node structure, add chair ColorRects under snap points, attach balloon.gd to existing Balloon nodes
  • Tests: extend test_gift_box.gd, add test_balloon.gd, add test_cake.gd

Out of scope:

  • New SFX keys (reuse object_tap for all three objects)
  • Navigation changes (HomeButtonToGarden and HomeButtonReturn already wired)
  • Additional characters beyond Bunny1 already in Main.tscn

Object Mechanics

GiftBox — RESETTING state

New state added to the existing State enum:

CLOSED → OPENING → RESETTING → CLOSED
  • _on_lid_opened(): state → RESETTING, gift fades in (existing tween), inline 3 s tween interval runs in parallel → _start_close_lid()
  • _start_close_lid(): lid tweens back (fade-in alpha 0→1, slide y back to 0) → _on_reset_complete()
  • _on_reset_complete(): state → CLOSED
  • OPEN state removed — RESETTING covers "gift visible + waiting"
  • New internal methods: _start_close_lid(), _on_reset_complete()
  • Constant: RESET_DELAY: float = 3.0

Balloon

New file: scripts/objects/balloon.gd
New scene attachment: balloon.gd attached to existing Balloon1 / Balloon2 nodes in GardenParty.tscn.

IDLE → POPPING → POPPED → RESPAWNING → IDLE

State transitions:

  • Touch input on balloon while IDLE_start_pop()
  • _start_pop(): AudioManager.play_sfx("object_tap"), state → POPPING, scale tween to Vector2.ZERO (0.15 s, EASE_IN, TRANS_BACK) → _on_pop_complete()
  • _on_pop_complete(): state → POPPED, starts 5 s tween interval → _start_respawn()
  • _start_respawn(): state → RESPAWNING, scale tween to Vector2.ONE (0.3 s, EASE_OUT, TRANS_BACK) → _on_respawn_complete()
  • _on_respawn_complete(): state → IDLE

Input: _input(event) — same touch-rect pattern as GiftBox, half-size 20 px (balloon is small).

Constants: POP_DURATION, RESPAWN_DURATION, RESPAWN_DELAY.

Cake

New file: scripts/objects/cake.gd
New node structure in GardenParty.tscn (replaces GiftBox3 at x=820, y=464):

Cake (Node2D, cake.gd)
├── Base (ColorRect, ~80×40 px, warm brown)
└── Slice (ColorRect, ~30×40 px, slightly offset + rotated, lighter brown)
WHOLE → CUTTING → CUT → RESETTING → WHOLE

State transitions:

  • Touch input on Cake while WHOLE_start_cutting()
  • _start_cutting(): AudioManager.play_sfx("object_tap"), state → CUTTING, tween Slice.modulate.a 1→0 (0.3 s, EASE_OUT) → _on_cut_complete()
  • _on_cut_complete(): state → CUT, 3 s tween interval → _start_reset()
  • _start_reset(): state → RESETTING, tween Slice.modulate.a 0→1 (0.3 s, EASE_IN) → _on_reset_complete()
  • _on_reset_complete(): state → WHOLE

Input: touch rect BUTTON_HALF_WIDTH = 40, BUTTON_HALF_HEIGHT = 20.

Constants: CUT_DURATION, RESET_DELAY = 3.0, RESET_DURATION.


Scene Changes (GardenParty.tscn)

Change Detail
Remove GiftBox3 Was at position Vector2(820, 464)
Add Cake Node2D at Vector2(820, 464), cake.gd, child nodes Base + Slice
Attach balloon.gd To existing Balloon1 and Balloon2 ColorRect nodes
Add ChairLeft ColorRect ~60×20 px, brown, under SnapTableLeft (x=530, y=480)
Add ChairRight ColorRect ~60×20 px, brown, under SnapTableRight (x=750, y=480)

Navigation and snap-point logic unchanged.


Tests

test_gift_box.gd (extend existing)

Test Assertion
test_state_becomes_resetting_after_lid_opened Call _on_lid_opened() → state == State.RESETTING
test_state_becomes_closed_after_reset_complete Call _on_reset_complete() → state == State.CLOSED

test_balloon.gd (new, extends GutTest)

Test Assertion
test_initial_state_is_idle state == State.IDLE
test_start_pop_sets_popping Call _start_pop()state == State.POPPING
test_on_pop_complete_sets_popped Call _on_pop_complete()state == State.POPPED
test_on_respawn_complete_sets_idle Call _on_respawn_complete()state == State.IDLE

test_cake.gd (new, extends GutTest)

Test Assertion
test_initial_state_is_whole state == State.WHOLE
test_start_cutting_sets_cutting Call _start_cutting()state == State.CUTTING
test_on_cut_complete_sets_cut Call _on_cut_complete()state == State.CUT
test_on_reset_complete_sets_whole Call _on_reset_complete()state == State.WHOLE

Architecture Notes

  • All three objects follow the same state-machine pattern as existing objects (cradle.gd, gift_box.gd, etc.) — no new abstractions.
  • Tween-based animations are not unit-tested (visual only). State transitions triggered by tween.finished callbacks are tested by calling the callbacks directly.
  • AudioServer.get_driver_name() == "Dummy" guard is already in AudioManager.play_sfx() — no additional guard needed in new scripts.
  • balloon.gd scales the Node2D itself (no child sprite node needed at placeholder stage).