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

View file

@ -19,4 +19,9 @@ gradient = SubResource("Gradient_rrby1")
[node name="Timer" type="Timer" parent="."] [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"] [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 can_fire: bool = true
var cooldown: Timer var cooldown: Timer
var projectile_spawn_location: Vector2
func _init() -> void: func _init() -> void:
cooldown = Timer.new() cooldown = Timer.new()
add_child(cooldown) add_child(cooldown)
@ -33,7 +35,7 @@ func fire(direction: Vector2, target_position: Vector2) -> void:
if !can_fire: if !can_fire:
return return
spawn_projectile(global_position, direction, target_position) spawn_projectile(direction, target_position)
can_fire = false can_fire = false
cooldown.start(1 / stats.get_stat("ranged.attack_rate")) cooldown.start(1 / stats.get_stat("ranged.attack_rate"))
@ -42,21 +44,33 @@ func set_projectile_scene() -> void:
projectile_scene = basic_projectile projectile_scene = basic_projectile
modifier_manager.check_and_call("set_projectile_scene", [self]) 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") Log.pr("Spawning projectile")
modifier_manager.check_and_call("spawn_projectile", [self]) modifier_manager.check_and_call("spawn_projectile", [self])
## TODO: Handle multiple shots per fire ## TODO: Handle multiple shots per fire
var projectile = projectile_scene.instantiate() var projectile = projectile_scene.instantiate()
projectile.global_position = spawn_position projectile.global_position = get_projectile_spawn_location()
projectile.target_position = target_position projectile.target_position = target_position
projectile.speed = 200 # stats.get_stat("projectile_speed") projectile.set_stats(stats.duplicate())
projectile.damage = 10 # stats.get_stat("damage")
projectile.lifetime = 2 # stats.get_stat("projectile_lifetime")
projectile.source_weapon = self projectile.source_weapon = self
var size = stats.get_stat("ranged.projectile_size") var size = stats.get_stat("ranged.projectile_size")
projectile.set_projectile_scale(Vector2(size, 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(): if get_tree() and get_tree().get_root():
get_tree().get_root().add_child(projectile) get_tree().get_root().add_child(projectile)

View file

@ -18,7 +18,7 @@ tile_set = ExtResource("2_3nv2f")
tile_set = ExtResource("2_3nv2f") tile_set = ExtResource("2_3nv2f")
[node name="Player" parent="." instance=ExtResource("5_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")] [node name="TestEnemy" parent="." instance=ExtResource("4_raxr4")]
position = Vector2(87, 72) position = Vector2(87, 72)

View file

@ -5,6 +5,9 @@ signal on_hit(projectile, target)
signal on_spawned(projectile) signal on_spawned(projectile)
signal on_destroyed(projectile) signal on_destroyed(projectile)
var stats = StatsComponent
var modifier_manager: ModifierManager
@export var speed: float = 500.0 @export var speed: float = 500.0
@export var damage: float = 10.0 @export var damage: float = 10.0
@export var lifetime: float = 2 @export var lifetime: float = 2
@ -26,6 +29,38 @@ var lifetime_timer: Timer
# Add a variable to track the entity that triggered the explosion # Add a variable to track the entity that triggered the explosion
var ignore_target = [] 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): func _on_body_entered(body):
if ignore_target.has(body): if ignore_target.has(body):
Log.pr("Ignoring body: ", body.name) Log.pr("Ignoring body: ", body.name)

View file

@ -7,13 +7,16 @@ extends ProjectileBase
# Add variables to track collision state # Add variables to track collision state
var has_collided: bool = false var has_collided: bool = false
var collision_point: Vector2 = Vector2.ZERO var collision_point: Vector2 = Vector2.ZERO
var existing_shape: CollisionShape2D = null
func _init() -> void: func _init() -> void:
#super._init() super._init()
Log.pr("ProjectileLightning _init") Log.pr("ProjectileLightning _init")
func _ready(): func _ready():
super._ready()
Log.pr(ignore_target) Log.pr(ignore_target)
Log.pr('Source Weapon: ', source_weapon)
lifetime_timer = Timer.new() lifetime_timer = Timer.new()
add_child(lifetime_timer) add_child(lifetime_timer)
@ -36,7 +39,7 @@ func _ready():
func ensure_collision_shape(): func ensure_collision_shape():
# Check if we already have a collision shape # Check if we already have a collision shape
var has_collision_shape = false var has_collision_shape = false
var existing_shape = null existing_shape = null
for child in get_children(): for child in get_children():
if child is CollisionShape2D: if child is CollisionShape2D:
@ -75,6 +78,9 @@ func update_collision_shape(collision_shape = null):
if collision_shape: if collision_shape:
var capsule = collision_shape.shape as CapsuleShape2D var capsule = collision_shape.shape as CapsuleShape2D
if capsule: 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 target = collision_point if has_collided else target_position
var distance = global_position.distance_to(target) var distance = global_position.distance_to(target)
var dir = (target - global_position).normalized() var dir = (target - global_position).normalized()
@ -93,6 +99,7 @@ func _process(delta):
# Update target positions for lightning bolts # Update target positions for lightning bolts
for child in lightning: for child in lightning:
if child is LightningBolt: if child is LightningBolt:
child.global_position = source_weapon.global_position
child.goal_point = collision_point if has_collided else target_position child.goal_point = collision_point if has_collided else target_position
# Update collision shape if it exists # Update collision shape if it exists