randomgeon/player/weapons/projectile_lightning.gd
Dan Baker fac3327c22 Improves projectile handling and modifiers
Refactors projectile spawning to allow for customized spawn locations and stat assignment.

Applies modifiers to projectiles at the time of spawning, enabling dynamic adjustments to projectile behavior.
2025-05-09 13:42:09 +01:00

207 lines
6.2 KiB
GDScript

class_name ProjectileLightning
extends ProjectileBase
@export var line_width: float = 2
@onready var lightning: Array = get_children()
# Add variables to track collision state
var has_collided: bool = false
var collision_point: Vector2 = Vector2.ZERO
var existing_shape: CollisionShape2D = null
func _init() -> void:
super._init()
Log.pr("ProjectileLightning _init")
func _ready():
super._ready()
Log.pr(ignore_target)
Log.pr('Source Weapon: ', source_weapon)
lifetime_timer = Timer.new()
add_child(lifetime_timer)
lifetime_timer.one_shot = true
lifetime_timer.wait_time = lifetime
lifetime_timer.connect("timeout", _on_lifetime_timeout)
lifetime_timer.start()
# Make sure we have a collision shape
ensure_collision_shape()
for child in lightning:
if child.has_method("set_line_width"):
child.set_line_width(line_width)
emit_signal("on_spawned", self)
connect("body_entered", _on_body_entered)
# Add a method to ensure we have a collision shape
func ensure_collision_shape():
# Check if we already have a collision shape
var has_collision_shape = false
existing_shape = null
for child in get_children():
if child is CollisionShape2D:
has_collision_shape = true
existing_shape = child
break
# If no collision shape exists, create one
if not has_collision_shape:
var collision_shape = CollisionShape2D.new()
var capsule_shape = CapsuleShape2D.new()
# Set the shape properties
capsule_shape.radius = line_width / 2
collision_shape.shape = capsule_shape
add_child(collision_shape)
existing_shape = collision_shape
# Make sure the lightning area is set to detect the right objects
set_collision_mask_value(2, true) # Assuming layer 2 is for enemies
set_collision_mask_value(3, true) # Assuming layer 3 is for objects
# Update the collision shape dimensions and position
update_collision_shape(existing_shape)
# Combined method to update collision shape based on current state
func update_collision_shape(collision_shape = null):
if collision_shape == null:
# Find the collision shape if not provided
for child in get_children():
if child is CollisionShape2D:
collision_shape = child
break
if collision_shape:
var capsule = collision_shape.shape as CapsuleShape2D
if capsule:
#collision_shape.global_position = source_weapon.global_position
global_position = source_weapon.global_position
var target = collision_point if has_collided else target_position
var distance = global_position.distance_to(target)
var dir = (target - global_position).normalized()
# Update capsule height to match exact distance
capsule.height = distance
# Fix rotation based on current direction
collision_shape.rotation = dir.angle() + PI / 2
# Fix position: Move the shape so it starts at origin
collision_shape.position = dir * (distance / 2)
func _process(delta):
# Update target positions for lightning bolts
for child in lightning:
if child is LightningBolt:
child.global_position = source_weapon.global_position
child.goal_point = collision_point if has_collided else target_position
# Update collision shape if it exists
update_collision_shape()
# Implement the body_entered signal handler
func _on_body_entered(body):
if ignore_target.has(body):
Log.pr("Ignoring body: ", body.name)
return
# Check if the colliding body is an enemy or object
if body.is_in_group("enemies"):
Log.pr("Hit enemy: ", body.name)
if not has_collided: # Only process the first collision
# Set collision state and point
has_collided = true
# Calculate the collision point
var direction_to_body = (body.global_position - global_position).normalized()
var body_radius = 10.0 # Adjust for your game
collision_point = body.global_position - (direction_to_body * body_radius)
Log.pr("Collision point updated to: ", collision_point)
# Debug output
Log.pr("Lightning hit: " + body.name + " at point: " + str(collision_point))
# IMPORTANT: Immediately update the collision shape to stop at collision point
update_collision_shape()
#_trigger_explosion()
if body.is_in_group("enemies") and is_friendly:
Log.pr("Hit enemy: ", body.name)
# Deal damage to enemy
if body.has_method("take_damage"):
body.take_damage(damage)
# Handle piercing
if pierce_count > 0:
pierce_count -= 1
else:
# Handle explosive impact
if has_explosive_impact:
# Store the target that triggered the explosion
ignore_target.append(body)
#_trigger_explosion()
self.call_deferred("_trigger_explosion")
# Emit signal for modifiers to react to
emit_signal("on_hit", self, body)
func _spawn_explosion_projectiles():
for i in range(explosion_projectile_count):
var max_target_distance = 200 # Maximum distance to look for enemies
# Get all enemies in the scene
var potential_targets = get_tree().get_nodes_in_group("enemies")
var valid_targets = []
# Filter enemies to only include ones within range
for enemy in potential_targets:
var distance = enemy.global_position.distance_to(collision_point)
if distance <= max_target_distance:
valid_targets.append(enemy)
var random_point = Vector2.ZERO
# Check if we have any valid targets
if valid_targets.size() > 0:
# Pick a random enemy from valid targets
var random_enemy = valid_targets[randi() % valid_targets.size()]
random_point = random_enemy.global_position
else:
return
Log.pr("Spawning explosion projectile")
var new_proj = (load(scene_file_path) as PackedScene).instantiate()
Log.pr("New projectile: ", new_proj)
new_proj.global_position = collision_point
Log.pr("New projectile spawn: ", new_proj.global_position)
new_proj.target_position = random_point
new_proj.damage = damage * explosion_projectile_damage_mult
new_proj.speed = explosion_projectile_speed
# Clear explosive properties so we don't get infinite loops
new_proj.has_explosive_impact = true
new_proj.explosion_projectile_count = 1
# Pass the ignore_target to the new projectiles
new_proj.ignore_target = ignore_target
Log.pr("New projectile: ", new_proj)
# Add to scene tree
get_tree().root.call_deferred("add_child", new_proj)
func _on_lifetime_timeout():
super._on_lifetime_timeout()