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.
This commit is contained in:
Dan Baker 2025-05-09 13:42:09 +01:00
parent 70839387ca
commit fac3327c22
6 changed files with 74 additions and 11 deletions

View file

@ -10,6 +10,7 @@ var emitting = true
var final_goal: Vector2
@export var angle_var: float = 15
@onready var line: Line2D = $Line2D
#@onready var particles: CPUParticles2D = $Particles
func _ready():
line.width = 2
@ -48,6 +49,7 @@ func update_points():
curr_line_len = start_point.length()
points.append(final_goal)
#particles.position = final_goal
func set_line_width(amount):
line.width = amount

View file

@ -19,4 +19,9 @@ gradient = SubResource("Gradient_rrby1")
[node name="Timer" type="Timer" parent="."]
[node name="Particles" type="CPUParticles2D" parent="."]
visible = false
emission_shape = 1
emission_sphere_radius = 6.9
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]

View file

@ -11,6 +11,8 @@ class_name RangedWeaponComponent
var can_fire: bool = true
var cooldown: Timer
var projectile_spawn_location: Vector2
func _init() -> void:
cooldown = Timer.new()
add_child(cooldown)
@ -33,7 +35,7 @@ func fire(direction: Vector2, target_position: Vector2) -> void:
if !can_fire:
return
spawn_projectile(global_position, direction, target_position)
spawn_projectile(direction, target_position)
can_fire = false
cooldown.start(1 / stats.get_stat("ranged.attack_rate"))
@ -42,21 +44,33 @@ func set_projectile_scene() -> void:
projectile_scene = basic_projectile
modifier_manager.check_and_call("set_projectile_scene", [self])
func spawn_projectile(spawn_position: Vector2, spawn_direction: Vector2, target_position: Vector2) -> void:
## Get the projectile spawn location
## This defaults to the global position of the player
## but can be overridden by modifiers
func get_projectile_spawn_location() -> Vector2:
projectile_spawn_location = global_position
modifier_manager.check_and_call("get_projectile_spawn_location", [self])
return projectile_spawn_location
func spawn_projectile(spawn_direction: Vector2, target_position: Vector2) -> void:
Log.pr("Spawning projectile")
modifier_manager.check_and_call("spawn_projectile", [self])
## TODO: Handle multiple shots per fire
var projectile = projectile_scene.instantiate()
projectile.global_position = spawn_position
projectile.global_position = get_projectile_spawn_location()
projectile.target_position = target_position
projectile.speed = 200 # stats.get_stat("projectile_speed")
projectile.damage = 10 # stats.get_stat("damage")
projectile.lifetime = 2 # stats.get_stat("projectile_lifetime")
projectile.set_stats(stats.duplicate())
projectile.source_weapon = self
var size = stats.get_stat("ranged.projectile_size")
projectile.set_projectile_scale(Vector2(size, size))
for modifier in modifier_manager.modifiers:
Log.pr("Adding modifier to projectile: ", modifier)
if projectile.has_method("add_modifier"):
projectile.add_modifier(modifier)
if get_tree() and get_tree().get_root():
get_tree().get_root().add_child(projectile)

View file

@ -18,7 +18,7 @@ tile_set = ExtResource("2_3nv2f")
tile_set = ExtResource("2_3nv2f")
[node name="Player" parent="." instance=ExtResource("5_3nv2f")]
position = Vector2(20, 20)
position = Vector2(6, 0)
[node name="TestEnemy" parent="." instance=ExtResource("4_raxr4")]
position = Vector2(87, 72)

View file

@ -5,6 +5,9 @@ signal on_hit(projectile, target)
signal on_spawned(projectile)
signal on_destroyed(projectile)
var stats = StatsComponent
var modifier_manager: ModifierManager
@export var speed: float = 500.0
@export var damage: float = 10.0
@export var lifetime: float = 2
@ -26,6 +29,38 @@ var lifetime_timer: Timer
# Add a variable to track the entity that triggered the explosion
var ignore_target = []
func _init() -> void:
Log.pr('Setting up ModifierManager for projectile...')
modifier_manager = ModifierManager.new()
func _ready() -> void:
pass
func set_stats(setting_stats: StatsComponent) -> void:
Log.pr('Snapshotting stats for projectile:', setting_stats)
self.stats = setting_stats
modifier_manager.set_stats(stats)
# Set up the lifetime timer
lifetime_timer = Timer.new()
add_child(lifetime_timer)
lifetime_timer.one_shot = true
lifetime_timer.wait_time = lifetime
#lifetime_timer.connect("timeout", self, "_on_lifetime_timeout")
lifetime_timer.start()
# Ensure we have a collision shape
#ensure_collision_shape()
emit_signal("on_spawned", self)
#connect("body_entered", self, "_on_body_entered")
func add_modifier(modifier: Modifier) -> void:
modifier_manager.add_modifier(modifier)
func remove_modifier(modifier_id: String) -> void:
modifier_manager.remove_modifier(modifier_id)
func _on_body_entered(body):
if ignore_target.has(body):
Log.pr("Ignoring body: ", body.name)
@ -98,4 +133,4 @@ func destroy():
queue_free()
func _on_lifetime_timeout():
destroy()
destroy()

View file

@ -7,13 +7,16 @@ extends ProjectileBase
# 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()
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)
@ -36,7 +39,7 @@ func _ready():
func ensure_collision_shape():
# Check if we already have a collision shape
var has_collision_shape = false
var existing_shape = null
existing_shape = null
for child in get_children():
if child is CollisionShape2D:
@ -75,6 +78,9 @@ func update_collision_shape(collision_shape = null):
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()
@ -87,12 +93,13 @@ func update_collision_shape(collision_shape = null):
# 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