From 2cb265c9223d2384bbce36f1662fc061e497d82d Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Mon, 11 May 2026 20:15:44 +0200 Subject: [PATCH] feat(sprint-14): add Cake cut/reset state machine --- scripts/objects/cake.gd | 56 +++++++++++++++++++++++++++++++++++++++++ test/unit/test_cake.gd | 31 +++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 scripts/objects/cake.gd create mode 100644 test/unit/test_cake.gd diff --git a/scripts/objects/cake.gd b/scripts/objects/cake.gd new file mode 100644 index 0000000..c367120 --- /dev/null +++ b/scripts/objects/cake.gd @@ -0,0 +1,56 @@ +## Cake — tap to cut a slice, slice auto-respawns after a delay. +class_name Cake extends Node2D + +enum State { WHOLE, CUTTING, CUT, RESETTING } + +const CUT_DURATION: float = 0.3 +const RESET_DURATION: float = 0.3 +const RESET_DELAY: float = 3.0 +const BUTTON_HALF_WIDTH: float = 40.0 +const BUTTON_HALF_HEIGHT: float = 20.0 + +var _state: State = State.WHOLE + + +func _input(event: InputEvent) -> void: + if _state != State.WHOLE: + return + if not event is InputEventScreenTouch: + return + var touch: InputEventScreenTouch = event as InputEventScreenTouch + if not touch.pressed: + return + var local: Vector2 = to_local(touch.position) + if abs(local.x) > BUTTON_HALF_WIDTH or abs(local.y) > BUTTON_HALF_HEIGHT: + return + _start_cutting() + + +func _start_cutting() -> void: + AudioManager.play_sfx("object_tap") + _state = State.CUTTING + var slice: Node2D = get_node_or_null("Slice") as Node2D + var tween: Tween = create_tween() + if slice != null: + tween.tween_property(slice, "modulate:a", 0.0, CUT_DURATION) + tween.tween_callback(_on_cut_complete) + + +func _on_cut_complete() -> void: + _state = State.CUT + var tween: Tween = create_tween() + tween.tween_interval(RESET_DELAY) + tween.tween_callback(_start_reset) + + +func _start_reset() -> void: + _state = State.RESETTING + var slice: Node2D = get_node_or_null("Slice") as Node2D + var tween: Tween = create_tween() + if slice != null: + tween.tween_property(slice, "modulate:a", 1.0, RESET_DURATION) + tween.tween_callback(_on_reset_complete) + + +func _on_reset_complete() -> void: + _state = State.WHOLE diff --git a/test/unit/test_cake.gd b/test/unit/test_cake.gd new file mode 100644 index 0000000..af51717 --- /dev/null +++ b/test/unit/test_cake.gd @@ -0,0 +1,31 @@ +## Tests for Cake — state machine transitions. +extends GutTest + +const CakeScript = preload("res://scripts/objects/cake.gd") + +var _cake: Node2D + + +func before_each() -> void: + _cake = CakeScript.new() + add_child_autofree(_cake) + + +func test_initial_state_is_whole() -> void: + assert_eq(_cake._state, CakeScript.State.WHOLE) + + +func test_start_cutting_transitions_to_cutting() -> void: + _cake._start_cutting() + assert_eq(_cake._state, CakeScript.State.CUTTING) + + +func test_on_cut_complete_transitions_to_cut() -> void: + _cake._on_cut_complete() + assert_eq(_cake._state, CakeScript.State.CUT) + + +func test_on_reset_complete_transitions_to_whole() -> void: + _cake._state = CakeScript.State.RESETTING + _cake._on_reset_complete() + assert_eq(_cake._state, CakeScript.State.WHOLE)