Improves lightning projectile behavior
Refactors the lightning projectile to handle collisions more effectively. It now stops upon hitting an enemy or object, triggers an explosion, and spawns child projectiles from the collision point. The lightning bolt's collision shape is dynamically updated to match the remaining distance to the target or the collision point. Also adds more test enemies to the map for testing purposes.
This commit is contained in:
parent
d0c2a7b3c8
commit
1a959fbc0c
7 changed files with 232 additions and 41 deletions
|
|
@ -1,30 +1,207 @@
|
|||
class_name ProjectileLightning
|
||||
extends ProjectileBase
|
||||
|
||||
@export var line_width: float = 1
|
||||
@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
|
||||
|
||||
func _init() -> void:
|
||||
#super._init()
|
||||
Log.pr("ProjectileLightning _init")
|
||||
|
||||
func _ready():
|
||||
Log.pr(ignore_target)
|
||||
|
||||
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)
|
||||
|
||||
func _process(_delta):
|
||||
for child in lightning:
|
||||
child.goal_point = target_position
|
||||
# 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
|
||||
var 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)
|
||||
|
||||
func _physics_process(delta):
|
||||
position += direction * speed * delta
|
||||
# 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:
|
||||
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.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") or body.is_in_group("objects"):
|
||||
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)
|
||||
|
||||
# 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()
|
||||
|
||||
#super._on_body_entered(body)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
# Emit signal for modifiers to react to
|
||||
emit_signal("on_hit", self, body)
|
||||
|
||||
# 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()
|
||||
|
||||
func _spawn_explosion_projectiles():
|
||||
for i in range(explosion_projectile_count):
|
||||
# Create a new projectile
|
||||
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
|
||||
|
||||
var min_distance = 50
|
||||
var max_distance = 125
|
||||
|
||||
# Generate a random angle
|
||||
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:
|
||||
# Fallback if no enemies in range - use the original random point logic
|
||||
var random_angle = randf_range(0, TAU)
|
||||
var random_distance = randf_range(50, max_target_distance)
|
||||
random_point = collision_point + Vector2(
|
||||
cos(random_angle) * random_distance,
|
||||
sin(random_angle) * random_distance
|
||||
)
|
||||
|
||||
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():
|
||||
Log.pr("ProjectileLightning _on_lifetime_timeout")
|
||||
super._on_lifetime_timeout()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue