paddle.gd 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. class_name Paddle
  2. extends AnimatableBody2D
  3. ## Follows the player's finger (or mouse) along the bottom of the screen.
  4. ## Emitted when the player lifts their finger; lets go of caught balls.
  5. signal released
  6. enum Width { NORMAL, HALF, DOUBLE }
  7. const SCALE := { Width.NORMAL: 1.0, Width.HALF: 0.5, Width.DOUBLE: 2.0 }
  8. const LASER_INTERVAL := 0.4 # seconds between shots
  9. const LASER_FLASH := 0.08 # seconds a beam stays visible
  10. var width := Width.NORMAL
  11. ## While above 0, balls that land on the paddle are caught.
  12. var catch_time := 0.0
  13. var _laser_time := 0.0
  14. var _laser_cooldown := 0.0
  15. var _beams: Array[Vector2] = [] # local end points of the beams on screen
  16. var _beam_time := 0.0
  17. var _screen_width := 0.0
  18. var _shape := RectangleShape2D.new()
  19. @onready var _timer: Timer = $NormalTimer
  20. func _ready() -> void:
  21. _screen_width = get_viewport_rect().size.x
  22. $CollisionShape2D.shape = _shape
  23. _timer.timeout.connect(normal_size)
  24. normal_size()
  25. func _unhandled_input(event: InputEvent) -> void:
  26. if event is InputEventScreenTouch:
  27. var touch := event as InputEventScreenTouch
  28. if touch.is_pressed():
  29. _move_to(touch.position.x)
  30. else:
  31. released.emit()
  32. elif event is InputEventScreenDrag:
  33. _move_to((event as InputEventScreenDrag).position.x)
  34. func half_width() -> float:
  35. return Params.PADDLE_WIDTH / 2.0 * SCALE[width]
  36. ## Doubles the paddle for a while, or cancels a halving.
  37. func double_size() -> void:
  38. if width == Width.HALF:
  39. normal_size()
  40. return
  41. _set_width(Width.DOUBLE)
  42. _timer.start(Params.POWER_UP_PADDLE_SECONDS)
  43. ## Halves the paddle for a while, or cancels a doubling.
  44. func half_size() -> void:
  45. if width == Width.DOUBLE:
  46. normal_size()
  47. return
  48. _set_width(Width.HALF)
  49. _timer.start(Params.POWER_UP_PADDLE_SECONDS)
  50. func normal_size() -> void:
  51. _timer.stop()
  52. _set_width(Width.NORMAL)
  53. func set_catch(seconds: float) -> void:
  54. catch_time = maxf(catch_time, seconds)
  55. queue_redraw()
  56. func set_laser(seconds: float) -> void:
  57. _laser_time = maxf(_laser_time, seconds)
  58. queue_redraw()
  59. func _physics_process(delta: float) -> void:
  60. var was_catching := catch_time > 0.0
  61. catch_time = maxf(catch_time - delta, 0.0)
  62. if was_catching and catch_time == 0.0:
  63. released.emit()
  64. queue_redraw()
  65. if _beam_time > 0.0:
  66. _beam_time -= delta
  67. if _beam_time <= 0.0:
  68. _beams.clear()
  69. queue_redraw()
  70. if _laser_time > 0.0:
  71. _laser_time -= delta
  72. _laser_cooldown -= delta
  73. if _laser_cooldown <= 0.0:
  74. _laser_cooldown = LASER_INTERVAL
  75. _fire_lasers()
  76. if _laser_time <= 0.0:
  77. queue_redraw()
  78. ## Two beams straight up from the paddle's ends; each damages the first segment it meets.
  79. func _fire_lasers() -> void:
  80. _beams.clear()
  81. var space := get_world_2d().direct_space_state
  82. for side in [-1.0, 1.0]:
  83. var from := global_position + Vector2(side * (half_width() - 4.0), -Params.PADDLE_HEIGHT / 2.0)
  84. var query := PhysicsRayQueryParameters2D.create(from, Vector2(from.x, 0.0), Params.LAYER_BLOCKS)
  85. var result := space.intersect_ray(query)
  86. var end: Vector2 = result.position if result else Vector2(from.x, 0.0)
  87. if result and result.collider is Block:
  88. (result.collider as Block).damage()
  89. _beams.append(to_local(from))
  90. _beams.append(to_local(end))
  91. _beam_time = LASER_FLASH
  92. queue_redraw()
  93. func _set_width(value: Width) -> void:
  94. width = value
  95. _shape.size = Vector2((Params.PADDLE_WIDTH + Params.PADDLE_HEIGHT) * SCALE[width], Params.PADDLE_HEIGHT)
  96. _move_to(position.x)
  97. queue_redraw()
  98. func _move_to(x: float) -> void:
  99. position.x = clampf(x, half_width(), _screen_width - half_width())
  100. func _draw() -> void:
  101. var half := half_width()
  102. var cap := Params.PADDLE_HEIGHT / 2.0
  103. # A caught-ball paddle turns green.
  104. var fill := Params.COLOR_GREEN if catch_time > 0.0 else Params.COLOR_GREY
  105. draw_line(Vector2(-half, 0), Vector2(half, 0), fill, Params.PADDLE_HEIGHT, true)
  106. draw_circle(Vector2(-half, 0), cap, fill, true, -1.0, true)
  107. draw_circle(Vector2(half, 0), cap, fill, true, -1.0, true)
  108. if _laser_time > 0.0:
  109. for side in [-1.0, 1.0]:
  110. draw_rect(Rect2(side * (half - 4.0) - 3.0, -cap - 6.0, 6.0, 6.0), Params.COLOR_RED)
  111. if not _beams.is_empty():
  112. draw_multiline(_beams, Params.COLOR_RED, 3.0, true)