randomgeon/player/weapons/projectile_base.gd
Dan Baker fac3327c22 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.
2025-05-09 13:42:09 +01:00

136 lines
3.9 KiB
GDScript

class_name ProjectileBase
extends Area2D
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
@export var direction: Vector2 = Vector2.RIGHT
@export var target_position: Vector2
@export var is_friendly: bool = true
# Modifier-related properties
var pierce_count: int = 0
var has_explosive_impact: bool = true
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
# References
var source_weapon: RangedWeaponComponent # 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 = []
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)
return
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()
# Destroy the projectile
destroy()
func _trigger_explosion():
# Create the explosion VFX
# var explosion = preload("res://scenes/explosion_effect.tscn").instantiate()
# explosion.global_position = global_position
# get_tree().root.add_child(explosion)
# Spawn the additional projectiles
if explosion_projectile_count > 0:
_spawn_explosion_projectiles()
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
var random_angle = randf_range(0, 2 * PI)
var new_dir = Vector2.RIGHT.rotated(random_angle)
# Set properties for the new projectile
new_proj.direction = new_dir
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
# 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()
func _on_lifetime_timeout():
destroy()