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 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) # 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) # 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()