Files

94 lines
3.3 KiB
GDScript

extends Area2D
# Set by player on spawn
var direction: Vector2 = Vector2.RIGHT
var damage: float = 3.5
var speed: float = 520.0
var max_range: float = 400.0
# Behavior flags (set from player stats)
var ricochet: bool = false
var explosive: bool = false
var explosion_radius: float = 64.0
var explosion_damage: float = 2.5
# Visual override (grey for overclock zero-damage shots)
var color: Color = Color(1.0, 0.85, 0.2) # default yellow
var _distance_traveled: float = 0.0
var _bounces_left: int = 1
var _bounce_cooldown: float = 0.0 # grace period after bounce to avoid re-trigger
func _ready() -> void:
rotation = direction.angle()
body_entered.connect(_on_body_entered)
func _physics_process(delta: float) -> void:
_bounce_cooldown = maxf(_bounce_cooldown - delta, 0.0)
if ricochet and _bounces_left > 0 and _bounce_cooldown == 0.0:
_check_wall_bounce(delta)
var move := direction * speed * delta
position += move
_distance_traveled += move.length()
if _distance_traveled >= max_range:
queue_free()
# ---------------------------------------------------------------------------
# Ricochet — raycast lookahead to get wall normal before physically touching
# ---------------------------------------------------------------------------
func _check_wall_bounce(delta: float) -> void:
var space := get_world_2d().direct_space_state
var reach := speed * delta + 6.0
var params := PhysicsRayQueryParameters2D.create(
global_position,
global_position + direction * reach,
1 # collision_mask: walls on layer 1
)
var result := space.intersect_ray(params)
if result and result["collider"] is StaticBody2D:
direction = direction.bounce(result["normal"])
rotation = direction.angle()
_bounces_left -= 1
_bounce_cooldown = 0.1
# ---------------------------------------------------------------------------
# Placeholder visual — colored circle (grey for zero-damage overclock shots)
# ---------------------------------------------------------------------------
func _draw() -> void:
draw_circle(Vector2.ZERO, 5.0, color)
# ---------------------------------------------------------------------------
# Collision — differentiate walls from enemies
# ---------------------------------------------------------------------------
func _on_body_entered(body: Node) -> void:
if body is StaticBody2D:
# Wall hit — ignore during bounce cooldown (already reflected by raycast)
if _bounce_cooldown > 0.0:
return
if not ricochet or _bounces_left <= 0:
queue_free()
return
if body.has_method("take_damage"):
body.take_damage(damage)
if explosive:
_explode()
queue_free()
# ---------------------------------------------------------------------------
# Explosive — AoE damage to all enemies in radius
# ---------------------------------------------------------------------------
func _explode() -> void:
AudioManager.play("explosion")
var space := get_world_2d().direct_space_state
var query := PhysicsShapeQueryParameters2D.new()
var circle := CircleShape2D.new()
circle.radius = explosion_radius
query.shape = circle
query.transform = Transform2D(0.0, global_position)
query.collision_mask = 1
for r in space.intersect_shape(query):
var c = r["collider"]
if c.has_method("take_damage"):
c.take_damage(explosion_damage)