feat(sprint-14): add Balloon pop/respawn state machine
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
## Balloon — tap to pop, auto-respawns after a delay.
|
||||||
|
class_name Balloon extends Node2D
|
||||||
|
|
||||||
|
enum State { IDLE, POPPING, POPPED, RESPAWNING }
|
||||||
|
|
||||||
|
const POP_DURATION: float = 0.15
|
||||||
|
const RESPAWN_DURATION: float = 0.30
|
||||||
|
const RESPAWN_DELAY: float = 5.0
|
||||||
|
const BUTTON_HALF_SIZE: float = 20.0
|
||||||
|
|
||||||
|
var _state: State = State.IDLE
|
||||||
|
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if _state != State.IDLE:
|
||||||
|
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_SIZE or abs(local.y) > BUTTON_HALF_SIZE:
|
||||||
|
return
|
||||||
|
_start_pop()
|
||||||
|
|
||||||
|
|
||||||
|
func _start_pop() -> void:
|
||||||
|
AudioManager.play_sfx("object_tap")
|
||||||
|
_state = State.POPPING
|
||||||
|
var tween: Tween = create_tween()
|
||||||
|
tween.set_ease(Tween.EASE_IN)
|
||||||
|
tween.set_trans(Tween.TRANS_BACK)
|
||||||
|
tween.tween_property(self, "scale", Vector2.ZERO, POP_DURATION)
|
||||||
|
tween.tween_callback(_on_pop_complete)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_pop_complete() -> void:
|
||||||
|
_state = State.POPPED
|
||||||
|
var tween: Tween = create_tween()
|
||||||
|
tween.tween_interval(RESPAWN_DELAY)
|
||||||
|
tween.tween_callback(_start_respawn)
|
||||||
|
|
||||||
|
|
||||||
|
func _start_respawn() -> void:
|
||||||
|
_state = State.RESPAWNING
|
||||||
|
var tween: Tween = create_tween()
|
||||||
|
tween.set_ease(Tween.EASE_OUT)
|
||||||
|
tween.set_trans(Tween.TRANS_BACK)
|
||||||
|
tween.tween_property(self, "scale", Vector2.ONE, RESPAWN_DURATION)
|
||||||
|
tween.tween_callback(_on_respawn_complete)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_respawn_complete() -> void:
|
||||||
|
_state = State.IDLE
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
## Tests for Balloon — state machine transitions.
|
||||||
|
extends GutTest
|
||||||
|
|
||||||
|
var _balloon: Balloon
|
||||||
|
|
||||||
|
|
||||||
|
func before_each() -> void:
|
||||||
|
_balloon = Balloon.new()
|
||||||
|
add_child_autofree(_balloon)
|
||||||
|
|
||||||
|
|
||||||
|
func test_initial_state_is_idle() -> void:
|
||||||
|
assert_eq(_balloon._state, Balloon.State.IDLE)
|
||||||
|
|
||||||
|
|
||||||
|
func test_start_pop_transitions_to_popping() -> void:
|
||||||
|
_balloon._start_pop()
|
||||||
|
assert_eq(_balloon._state, Balloon.State.POPPING)
|
||||||
|
|
||||||
|
|
||||||
|
func test_on_pop_complete_transitions_to_popped() -> void:
|
||||||
|
_balloon._on_pop_complete()
|
||||||
|
assert_eq(_balloon._state, Balloon.State.POPPED)
|
||||||
|
|
||||||
|
|
||||||
|
func test_on_respawn_complete_transitions_to_idle() -> void:
|
||||||
|
_balloon._state = Balloon.State.RESPAWNING
|
||||||
|
_balloon._on_respawn_complete()
|
||||||
|
assert_eq(_balloon._state, Balloon.State.IDLE)
|
||||||
Reference in New Issue
Block a user