diff --git a/assets/projectiles/components/bolt.gd b/assets/projectiles/components/bolt.gd index 66a3e4f..bedfd61 100644 --- a/assets/projectiles/components/bolt.gd +++ b/assets/projectiles/components/bolt.gd @@ -1,14 +1,14 @@ extends Node2D +class_name LightningBolt + var goal_point: Vector2 = Vector2(100, 100) var min_segment_size: float = 2 var max_segment_size: float = 10 var points: Array = [] var emitting = true var final_goal: Vector2 - @export var angle_var: float = 15 - @onready var line: Line2D = $Line2D func _ready(): @@ -18,34 +18,37 @@ func _ready(): $Timer.start(randf_range(0.1, 0.5)) func _on_timer_timeout(): - Log.pr("Timer timeout") if (points.size() > 0): points.pop_front() line.points = points - + #Small variation for more organic look: $Timer.start(0.002 + randf_range(-0.001, 0.001)) elif (emitting): update_points() line.points = points $Timer.start(0.1 + randf_range(-0.02, 0.1)) - + func update_points(): final_goal = goal_point - global_position var curr_line_len = 0 points = [Vector2()] var start_point = Vector2() - min_segment_size = max(Vector2().distance_to(final_goal) / 40, 1) - max_segment_size = min(Vector2().distance_to(final_goal) / 20, 10) - while (curr_line_len < Vector2().distance_to(final_goal)): + + # Adjust segment size based on the distance to goal + var distance_to_goal = Vector2().distance_to(final_goal) + min_segment_size = max(distance_to_goal / 40, 1) + max_segment_size = min(distance_to_goal / 20, 10) + + while (curr_line_len < distance_to_goal): var move_vector = start_point.direction_to(final_goal) * randf_range(min_segment_size, max_segment_size) var new_point = start_point + move_vector var new_point_rotated = start_point + move_vector.rotated(deg_to_rad(randf_range(-angle_var, angle_var))) points.append(new_point_rotated) start_point = new_point curr_line_len = start_point.length() - + points.append(final_goal) func set_line_width(amount): - line.width = amount + line.width = amount \ No newline at end of file diff --git a/assets/projectiles/projectile_lightning.tscn b/assets/projectiles/projectile_lightning.tscn index 3882142..e94f6f2 100644 --- a/assets/projectiles/projectile_lightning.tscn +++ b/assets/projectiles/projectile_lightning.tscn @@ -4,22 +4,23 @@ [ext_resource type="PackedScene" uid="uid://cafaf3en63bp6" path="res://assets/projectiles/components/bolt.tscn" id="2_gc60m"] [node name="ProjectileLightning" type="Area2D"] +collision_layer = 16 +collision_mask = 12 script = ExtResource("1_2ex40") [node name="Bolt" parent="." instance=ExtResource("2_gc60m")] modulate = Color(0.161899, 0.42984, 0.777414, 1) -rotation = 0.872665 [node name="Bolt2" parent="." instance=ExtResource("2_gc60m")] modulate = Color(0.443066, 0.672326, 1, 1) -rotation = 0.872665 [node name="Bolt3" parent="." instance=ExtResource("2_gc60m")] modulate = Color(0.205858, 0.510414, 0.999999, 1) -rotation = 0.349066 [node name="Bolt4" parent="." instance=ExtResource("2_gc60m")] +modulate = Color(1, 1, 1, 0.596078) [node name="Bolt5" parent="." instance=ExtResource("2_gc60m")] +modulate = Color(1, 1, 1, 0.603922) [node name="Bolt6" parent="." instance=ExtResource("2_gc60m")] diff --git a/map/Map.tscn b/map/Map.tscn index e98271a..671f18d 100644 --- a/map/Map.tscn +++ b/map/Map.tscn @@ -22,3 +22,15 @@ position = Vector2(20, 20) [node name="TestEnemy" parent="." instance=ExtResource("4_raxr4")] position = Vector2(87, 72) + +[node name="TestEnemy2" parent="." instance=ExtResource("4_raxr4")] +position = Vector2(171, 38) + +[node name="TestEnemy3" parent="." instance=ExtResource("4_raxr4")] +position = Vector2(132, 150) + +[node name="TestEnemy4" parent="." instance=ExtResource("4_raxr4")] +position = Vector2(199, 107) + +[node name="TestEnemy5" parent="." instance=ExtResource("4_raxr4")] +position = Vector2(272, 64) diff --git a/player/scripts/player_combat.gd b/player/scripts/player_combat.gd index 65b9fc9..06b283f 100644 --- a/player/scripts/player_combat.gd +++ b/player/scripts/player_combat.gd @@ -5,20 +5,12 @@ var player: CharacterBody2D var animated_sprite: AnimatedSprite2D func process(_delta): - # Get mouse position in global coordinates - var mouse_position = player.get_global_mouse_position() - - # Get player position (assuming this script is attached to the player) - var player_position = player.global_position - - # Calculate direction vector from player to mouse - var direction = mouse_position - player_position - - # You can normalize this vector if you want a unit vector (length of 1) - # This is useful if you only care about direction, not distance - var normalized_direction = direction.normalized() - if Input.is_action_pressed("fire"): + var mouse_position = player.get_global_mouse_position() + var player_position = player.global_position + var direction = mouse_position - player_position + var normalized_direction = direction.normalized() + player.weapon.fire(normalized_direction, mouse_position) # Update animation #update_animation() diff --git a/player/weapons/projectile_base.gd b/player/weapons/projectile_base.gd index 887a8ec..6475c36 100644 --- a/player/weapons/projectile_base.gd +++ b/player/weapons/projectile_base.gd @@ -15,7 +15,7 @@ signal on_destroyed(projectile) # Modifier-related properties var pierce_count: int = 0 var has_explosive_impact: bool = true -var explosion_projectile_count: int = 2 +var explosion_projectile_count: int = 3 var explosion_projectile_damage_mult: float = 0.5 var explosion_projectile_speed: float = 300.0 var explosion_spread_angle: float = 360.0 # Full circle by default @@ -24,13 +24,13 @@ var explosion_spread_angle: float = 360.0 # Full circle by default var source_weapon: RangedWeapon # Reference to the weapon that fired this var lifetime_timer: Timer # Add a variable to track the entity that triggered the explosion -var ignore_target = null +var ignore_target = [] func _on_body_entered(body): - # Check if this is a body we should ignore - if body == ignore_target: + if ignore_target.has(body): + Log.pr("Ignoring body: ", body.name) return - + if body.is_in_group("enemies") and is_friendly: Log.pr("Hit enemy: ", body.name) # Deal damage to enemy @@ -47,7 +47,7 @@ func _on_body_entered(body): # Handle explosive impact if has_explosive_impact: # Store the target that triggered the explosion - ignore_target = body + ignore_target.append(body) _trigger_explosion() # Destroy the projectile @@ -65,7 +65,9 @@ func _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 = duplicate() + Log.pr("New projectile: ", new_proj) new_proj.global_position = global_position # Calculate new direction based on spread @@ -87,6 +89,10 @@ func _spawn_explosion_projectiles(): # Add to scene tree get_tree().root.call_deferred("add_child", new_proj) +func set_projectile_scale(new_scale: Vector2): + # Set the scale of the projectile + self.scale = new_scale + func destroy(): emit_signal("on_destroyed", self) queue_free() diff --git a/player/weapons/projectile_lightning.gd b/player/weapons/projectile_lightning.gd index 002f5ae..346a5b9 100644 --- a/player/weapons/projectile_lightning.gd +++ b/player/weapons/projectile_lightning.gd @@ -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() diff --git a/player/weapons/ranged_weapon.gd b/player/weapons/ranged_weapon.gd index 0eb5a13..3504213 100644 --- a/player/weapons/ranged_weapon.gd +++ b/player/weapons/ranged_weapon.gd @@ -10,7 +10,7 @@ var base_stats = { "fire_rate": 2.0, "projectile_speed": 500.0, "projectile_size": 1.0, - "projectile_lifetime": 5.0, + "projectile_lifetime": 1.0, "projectile_quantity": 1, "projectile_spread": 33, "max_pierce": 0 @@ -84,7 +84,7 @@ func _spawn_projectile(spawn_position: Vector2, spawn_direction: Vector2, target # Set base size var size = stats.get_stat("projectile_size") - projectile.scale = Vector2(size, size) + projectile.set_projectile_scale(Vector2(size, size)) # Allow modifiers to directly modify the projectile for modifier in stats.modifiers: