| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- class_name Paddle
- extends AnimatableBody2D
- ## Follows the player's finger (or mouse) along the bottom of the screen.
- ## Emitted when the player lifts their finger; lets go of caught balls.
- signal released
- enum Width { NORMAL, HALF, DOUBLE }
- const SCALE := { Width.NORMAL: 1.0, Width.HALF: 0.5, Width.DOUBLE: 2.0 }
- const LASER_INTERVAL := 0.4 # seconds between shots
- const LASER_FLASH := 0.08 # seconds a beam stays visible
- var width := Width.NORMAL
- ## While above 0, balls that land on the paddle are caught.
- var catch_time := 0.0
- var _laser_time := 0.0
- var _laser_cooldown := 0.0
- var _beams: Array[Vector2] = [] # local end points of the beams on screen
- var _beam_time := 0.0
- var _screen_width := 0.0
- var _shape := RectangleShape2D.new()
- @onready var _timer: Timer = $NormalTimer
- func _ready() -> void:
- _screen_width = get_viewport_rect().size.x
- $CollisionShape2D.shape = _shape
- _timer.timeout.connect(normal_size)
- normal_size()
- func _unhandled_input(event: InputEvent) -> void:
- if event is InputEventScreenTouch:
- var touch := event as InputEventScreenTouch
- if touch.is_pressed():
- _move_to(touch.position.x)
- else:
- released.emit()
- elif event is InputEventScreenDrag:
- _move_to((event as InputEventScreenDrag).position.x)
- func half_width() -> float:
- return Params.PADDLE_WIDTH / 2.0 * SCALE[width]
- ## Doubles the paddle for a while, or cancels a halving.
- func double_size() -> void:
- if width == Width.HALF:
- normal_size()
- return
- _set_width(Width.DOUBLE)
- _timer.start(Params.POWER_UP_PADDLE_SECONDS)
- ## Halves the paddle for a while, or cancels a doubling.
- func half_size() -> void:
- if width == Width.DOUBLE:
- normal_size()
- return
- _set_width(Width.HALF)
- _timer.start(Params.POWER_UP_PADDLE_SECONDS)
- func normal_size() -> void:
- _timer.stop()
- _set_width(Width.NORMAL)
- func set_catch(seconds: float) -> void:
- catch_time = maxf(catch_time, seconds)
- queue_redraw()
- func set_laser(seconds: float) -> void:
- _laser_time = maxf(_laser_time, seconds)
- queue_redraw()
- func _physics_process(delta: float) -> void:
- var was_catching := catch_time > 0.0
- catch_time = maxf(catch_time - delta, 0.0)
- if was_catching and catch_time == 0.0:
- released.emit()
- queue_redraw()
- if _beam_time > 0.0:
- _beam_time -= delta
- if _beam_time <= 0.0:
- _beams.clear()
- queue_redraw()
- if _laser_time > 0.0:
- _laser_time -= delta
- _laser_cooldown -= delta
- if _laser_cooldown <= 0.0:
- _laser_cooldown = LASER_INTERVAL
- _fire_lasers()
- if _laser_time <= 0.0:
- queue_redraw()
- ## Two beams straight up from the paddle's ends; each damages the first segment it meets.
- func _fire_lasers() -> void:
- _beams.clear()
- var space := get_world_2d().direct_space_state
- for side in [-1.0, 1.0]:
- var from := global_position + Vector2(side * (half_width() - 4.0), -Params.PADDLE_HEIGHT / 2.0)
- var query := PhysicsRayQueryParameters2D.create(from, Vector2(from.x, 0.0), Params.LAYER_BLOCKS)
- var result := space.intersect_ray(query)
- var end: Vector2 = result.position if result else Vector2(from.x, 0.0)
- if result and result.collider is Block:
- (result.collider as Block).damage()
- _beams.append(to_local(from))
- _beams.append(to_local(end))
- _beam_time = LASER_FLASH
- queue_redraw()
- func _set_width(value: Width) -> void:
- width = value
- _shape.size = Vector2((Params.PADDLE_WIDTH + Params.PADDLE_HEIGHT) * SCALE[width], Params.PADDLE_HEIGHT)
- _move_to(position.x)
- queue_redraw()
- func _move_to(x: float) -> void:
- position.x = clampf(x, half_width(), _screen_width - half_width())
- func _draw() -> void:
- var half := half_width()
- var cap := Params.PADDLE_HEIGHT / 2.0
- # A caught-ball paddle turns green.
- var fill := Params.COLOR_GREEN if catch_time > 0.0 else Params.COLOR_GREY
- draw_line(Vector2(-half, 0), Vector2(half, 0), fill, Params.PADDLE_HEIGHT, true)
- draw_circle(Vector2(-half, 0), cap, fill, true, -1.0, true)
- draw_circle(Vector2(half, 0), cap, fill, true, -1.0, true)
- if _laser_time > 0.0:
- for side in [-1.0, 1.0]:
- draw_rect(Rect2(side * (half - 4.0) - 3.0, -cap - 6.0, 6.0, 6.0), Params.COLOR_RED)
- if not _beams.is_empty():
- draw_multiline(_beams, Params.COLOR_RED, 3.0, true)
|