feat: added rocket noises and smoke trails and adjusted AoE of rockets and damage falloff on AoE

This commit is contained in:
DottsGit
2026-06-05 13:32:10 -04:00
parent a85e0952c5
commit 6af87eb7bd
28 changed files with 259 additions and 14 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+32
View File
@@ -433,4 +433,36 @@ for i in range(int(sample_rate * mortar_duration)):
mortar_samples.append(thunk + ring) mortar_samples.append(thunk + ring)
write_wav('assets/sounds/mortar_fire.wav', mortar_samples) write_wav('assets/sounds/mortar_fire.wav', mortar_samples)
# 21. Rocket Flight (Deep continuous looping whoosh/rumble)
flight_duration = 1.0 # 1 second loop
flight_samples = []
prev_f = 0.0
for i in range(int(sample_rate * flight_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
alpha = 0.15 # Higher cutoff for more whoosh
filtered = prev_f + alpha * (noise - prev_f)
prev_f = filtered
rumble = math.sin(t * 70.0 * 2 * math.pi) * 1.5
raw = (filtered * 2.5 + rumble)
# Heavy soft clipping to make it sound intensely loud
flight_samples.append(math.tanh(raw * 2.0))
write_wav('assets/sounds/rocket_flight.wav', flight_samples)
# 22. Swarm Flight (Higher pitched continuous hiss)
swarm_flight_samples = []
prev_sf = 0.0
for i in range(int(sample_rate * flight_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
alpha = 0.4 # Higher cutoff for hiss
filtered = prev_sf + alpha * (noise - prev_sf)
prev_sf = filtered
# Aggressive screaming tone
whine = math.sin(t * 800.0 * 2 * math.pi) * 1.5
raw = (filtered * 2.0 + whine)
# Heavy soft clipping
swarm_flight_samples.append(math.tanh(raw * 2.0))
write_wav('assets/sounds/swarm_flight.wav', swarm_flight_samples)
print("Generated all sounds!") print("Generated all sounds!")
+1 -1
View File
@@ -77,7 +77,7 @@ class_name MovementParams
@export var rocket_jump_falloff: float = 1.5 @export var rocket_jump_falloff: float = 1.5
# ── Shotgun Jump ────────────────────────────────────────────────────────────── # ── Shotgun Jump ──────────────────────────────────────────────────────────────
@export var shotgun_jump_impulse: float = 10.0 @export var shotgun_jump_impulse: float = 15.0
# ── Double Jump ─────────────────────────────────────────────────────────────── # ── Double Jump ───────────────────────────────────────────────────────────────
@export var double_jump_velocity: float = 8.0 @export var double_jump_velocity: float = 8.0
+1 -1
View File
@@ -14,7 +14,7 @@ func _physics_process(delta: float) -> void:
fuse_time -= delta fuse_time -= delta
if fuse_time <= 0.0: if fuse_time <= 0.0:
_explode(global_position) _explode(global_position)
queue_free() destroy()
return return
# Apply gravity drop if any # Apply gravity drop if any
+132 -5
View File
@@ -1,16 +1,141 @@
extends Projectile extends Projectile
class_name ExplosiveProjectile class_name ExplosiveProjectile
var explosion_radius: float = 5.0 var explosion_radius: float = 2.5
var explosion_knockback: float = 20.0 var explosion_knockback: float = 20.0
var can_self_damage: bool = false var can_self_damage: bool = false
var falloff_curve: float = 1.0 # 1.0 is linear, 2.0 is quadratic (steep drop) var falloff_curve: float = 1.0 # 1.0 is linear, 2.0 is quadratic (steep drop)
var flight_sound: AudioStreamPlayer3D
var smoke_sys: GPUParticles3D
var jet_sys: GPUParticles3D
static var _smoke_tex: GradientTexture2D
static func get_smoke_texture() -> GradientTexture2D:
if _smoke_tex: return _smoke_tex
_smoke_tex = GradientTexture2D.new()
_smoke_tex.fill = GradientTexture2D.FILL_RADIAL
_smoke_tex.fill_from = Vector2(0.5, 0.5)
_smoke_tex.fill_to = Vector2(1.0, 1.0)
var g = Gradient.new()
g.add_point(0.0, Color(1, 1, 1, 1))
g.add_point(0.5, Color(1, 1, 1, 0.5))
g.add_point(1.0, Color(1, 1, 1, 0))
_smoke_tex.gradient = g
_smoke_tex.width = 64
_smoke_tex.height = 64
return _smoke_tex
func _ready() -> void:
super._ready()
_setup_effects()
func destroy() -> void:
if is_instance_valid(smoke_sys):
smoke_sys.emitting = false
var p = smoke_sys.get_parent()
if p:
var trans = smoke_sys.global_transform
p.remove_child(smoke_sys)
get_tree().current_scene.add_child(smoke_sys)
smoke_sys.global_transform = trans
var timer = smoke_sys.get_tree().create_timer(smoke_sys.lifetime)
timer.timeout.connect(smoke_sys.queue_free)
if is_instance_valid(jet_sys):
jet_sys.emitting = false
var p = jet_sys.get_parent()
if p:
var trans = jet_sys.global_transform
p.remove_child(jet_sys)
get_tree().current_scene.add_child(jet_sys)
jet_sys.global_transform = trans
var timer = jet_sys.get_tree().create_timer(jet_sys.lifetime)
timer.timeout.connect(jet_sys.queue_free)
super.destroy()
func _setup_effects() -> void:
# Audio
flight_sound = AudioStreamPlayer3D.new()
flight_sound.stream = load("res://assets/sounds/rocket_flight.wav")
flight_sound.autoplay = true
if flight_sound.stream is AudioStreamWAV:
flight_sound.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
flight_sound.unit_size = 50.0
flight_sound.max_db = 6.0
flight_sound.volume_db = 6.0
add_child(flight_sound)
flight_sound.play()
# Jet Fire Particles
jet_sys = GPUParticles3D.new()
var jet_mat = ParticleProcessMaterial.new()
jet_mat.direction = Vector3(0, 0, 1) # Backwards (since model looks at -Z)
jet_mat.spread = 10.0
jet_mat.initial_velocity_min = 1.0
jet_mat.initial_velocity_max = 2.0
jet_mat.gravity = Vector3.ZERO
var jet_mesh = BoxMesh.new()
jet_mesh.size = Vector3(0.05, 0.05, 0.05)
var mat = StandardMaterial3D.new()
mat.albedo_color = Color(1.0, 0.6, 0.0)
mat.emission_enabled = true
mat.emission = Color(1.0, 0.5, 0.0)
jet_mesh.material = mat
jet_sys.draw_pass_1 = jet_mesh
jet_sys.process_material = jet_mat
jet_sys.amount = 16
jet_sys.lifetime = 0.2
jet_sys.position = Vector3(0, 0, 0.1)
add_child(jet_sys)
# Smoke Trail
smoke_sys = GPUParticles3D.new()
var smoke_mat = ParticleProcessMaterial.new()
smoke_mat.direction = Vector3(0, 0, 1)
smoke_mat.spread = 5.0
smoke_mat.initial_velocity_min = 0.0
smoke_mat.initial_velocity_max = 0.5
smoke_mat.gravity = Vector3(0, 0.5, 0)
var grad = Gradient.new()
grad.add_point(0.0, Color(0.5, 0.5, 0.5, 0.8))
grad.add_point(1.0, Color(0.8, 0.8, 0.8, 0.0))
var grad_tex = GradientTexture1D.new()
grad_tex.gradient = grad
smoke_mat.color_ramp = grad_tex
var curve = Curve.new()
curve.add_point(Vector2(0.0, 1.0))
curve.add_point(Vector2(1.0, 3.0))
var curve_tex = CurveTexture.new()
curve_tex.curve = curve
smoke_mat.scale_curve = curve_tex
var smoke_mesh = QuadMesh.new()
smoke_mesh.size = Vector2(0.2, 0.2)
var s_mat = StandardMaterial3D.new()
s_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
s_mat.vertex_color_use_as_albedo = true
s_mat.albedo_texture = get_smoke_texture()
s_mat.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
s_mat.billboard_keep_scale = true
smoke_mesh.material = s_mat
smoke_sys.draw_pass_1 = smoke_mesh
smoke_sys.process_material = smoke_mat
smoke_sys.amount = 32
smoke_sys.lifetime = 1.5
smoke_sys.position = Vector3(0, 0, 0.15)
add_child(smoke_sys)
func _on_hit(result: Dictionary) -> void: func _on_hit(result: Dictionary) -> void:
if not result.collider.has_method("take_damage"): if not result.collider.has_method("take_damage"):
ImpactSpawner.spawn(get_tree(), "crater", result.position, result.normal, explosion_radius * 1.5) ImpactSpawner.spawn(get_tree(), "crater", result.position, result.normal, explosion_radius * 1.5)
_explode(result.position) _explode(result.position)
queue_free() destroy()
func _explode(pos: Vector3) -> void: func _explode(pos: Vector3) -> void:
var space_state = get_world_3d().direct_space_state var space_state = get_world_3d().direct_space_state
@@ -49,10 +174,12 @@ func _explode(pos: Vector3) -> void:
# Normalised distance (0 = epicenter, 1 = edge) # Normalised distance (0 = epicenter, 1 = edge)
var ndist = dist / explosion_radius var ndist = dist / explosion_radius
var intensity = clampf(1.0 - pow(ndist, falloff_curve), 0.0, 1.0) var falloff = clampf(pow(ndist, falloff_curve), 0.0, 1.0)
var damage_intensity = lerpf(1.0, 0.33, falloff)
var knockback_intensity = 1.0 - falloff
var final_damage = damage * intensity var final_damage = damage * damage_intensity
var final_knockback = explosion_knockback * intensity var final_knockback = explosion_knockback * knockback_intensity
# Knockback direction: from explosion pos to target # Knockback direction: from explosion pos to target
var dir = (target_pos - pos).normalized() var dir = (target_pos - pos).normalized()
+83
View File
@@ -8,6 +8,88 @@ var initial_aim_dir: Vector3 = Vector3.ZERO
var cruise_speed: float = 60.0 var cruise_speed: float = 60.0
var ignition_timer: float = 0.5 var ignition_timer: float = 0.5
var ignited: bool = false var ignited: bool = false
var effects_started: bool = false
func _setup_effects() -> void:
pass # Wait until ignited
func _start_homing_effects() -> void:
if effects_started: return
effects_started = true
# Audio
flight_sound = AudioStreamPlayer3D.new()
flight_sound.stream = load("res://assets/sounds/swarm_flight.wav")
flight_sound.autoplay = true
if flight_sound.stream is AudioStreamWAV:
flight_sound.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
flight_sound.unit_size = 50.0
flight_sound.max_db = 6.0
flight_sound.volume_db = 6.0
add_child(flight_sound)
flight_sound.play()
# Jet Fire Particles
jet_sys = GPUParticles3D.new()
var jet_mat = ParticleProcessMaterial.new()
jet_mat.direction = Vector3(0, 0, 1)
jet_mat.spread = 10.0
jet_mat.initial_velocity_min = 0.5
jet_mat.initial_velocity_max = 1.0
jet_mat.gravity = Vector3.ZERO
var jet_mesh = BoxMesh.new()
jet_mesh.size = Vector3(0.02, 0.02, 0.02)
var mat = StandardMaterial3D.new()
mat.albedo_color = Color(1.0, 0.6, 0.0)
mat.emission_enabled = true
mat.emission = Color(1.0, 0.5, 0.0)
jet_mesh.material = mat
jet_sys.draw_pass_1 = jet_mesh
jet_sys.process_material = jet_mat
jet_sys.amount = 16
jet_sys.lifetime = 0.15
jet_sys.position = Vector3(0, 0, 0.05)
add_child(jet_sys)
# Smoke Trail
smoke_sys = GPUParticles3D.new()
var smoke_mat = ParticleProcessMaterial.new()
smoke_mat.direction = Vector3(0, 0, 1)
smoke_mat.spread = 5.0
smoke_mat.initial_velocity_min = 0.0
smoke_mat.initial_velocity_max = 0.2
smoke_mat.gravity = Vector3(0, 0.5, 0)
var grad = Gradient.new()
grad.add_point(0.0, Color(0.5, 0.5, 0.5, 0.8))
grad.add_point(1.0, Color(0.8, 0.8, 0.8, 0.0))
var grad_tex = GradientTexture1D.new()
grad_tex.gradient = grad
smoke_mat.color_ramp = grad_tex
var curve = Curve.new()
curve.add_point(Vector2(0.0, 1.0))
curve.add_point(Vector2(1.0, 3.0))
var curve_tex = CurveTexture.new()
curve_tex.curve = curve
smoke_mat.scale_curve = curve_tex
var smoke_mesh = QuadMesh.new()
smoke_mesh.size = Vector2(0.08, 0.08)
var s_mat = StandardMaterial3D.new()
s_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
s_mat.vertex_color_use_as_albedo = true
s_mat.albedo_texture = ExplosiveProjectile.get_smoke_texture()
s_mat.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
s_mat.billboard_keep_scale = true
smoke_mesh.material = s_mat
smoke_sys.draw_pass_1 = smoke_mesh
smoke_sys.process_material = smoke_mat
smoke_sys.amount = 32
smoke_sys.lifetime = 1.0
smoke_sys.position = Vector3(0, 0, 0.05)
add_child(smoke_sys)
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
if not ignited: if not ignited:
@@ -15,6 +97,7 @@ func _physics_process(delta: float) -> void:
if ignition_timer <= 0.0: if ignition_timer <= 0.0:
ignited = true ignited = true
drop_gravity = 0.0 drop_gravity = 0.0
_start_homing_effects()
if ignited and is_instance_valid(weapon_source): if ignited and is_instance_valid(weapon_source):
var target_dir: Vector3 = initial_aim_dir var target_dir: Vector3 = initial_aim_dir
+1 -1
View File
@@ -76,7 +76,7 @@ func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
proj.bounciness = 0.4 # Heavy, loses momentum on bounce proj.bounciness = 0.4 # Heavy, loses momentum on bounce
# Explosive stats # Explosive stats
proj.explosion_radius = 12.0 # Very large AoE proj.explosion_radius = 10.0 # Very large AoE
proj.explosion_knockback = 60.0 # Massive knockback proj.explosion_knockback = 60.0 # Massive knockback
proj.falloff_curve = 2.0 # Steep dropoff: "Should be an instant kill in closest 50%" proj.falloff_curve = 2.0 # Steep dropoff: "Should be an instant kill in closest 50%"
# (0-50% = dist<6. 6/12 = 0.5. 1.0 - 0.5^2 = 0.75 multiplier. 200 * 0.75 = 150 damage -> 1 shot kill) # (0-50% = dist<6. 6/12 = 0.5. 1.0 - 0.5^2 = 0.75 multiplier. 200 * 0.75 = 150 damage -> 1 shot kill)
+5 -2
View File
@@ -21,7 +21,7 @@ func _ready() -> void:
_previous_position = global_position _previous_position = global_position
# Automatically destroy after 5 seconds to prevent leaks # Automatically destroy after 5 seconds to prevent leaks
var t = get_tree().create_timer(5.0) var t = get_tree().create_timer(5.0)
t.timeout.connect(queue_free) t.timeout.connect(destroy)
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
# Apply gravity drop if any # Apply gravity drop if any
@@ -33,7 +33,7 @@ func _physics_process(delta: float) -> void:
distance_traveled += movement.length() distance_traveled += movement.length()
if distance_traveled > max_range: if distance_traveled > max_range:
queue_free() destroy()
return return
_check_collision() _check_collision()
@@ -63,4 +63,7 @@ func _on_hit(result: Dictionary) -> void:
var size = 0.4 if impact_type == "plasma" else 0.15 var size = 0.4 if impact_type == "plasma" else 0.15
ImpactSpawner.spawn(get_tree(), impact_type, result.position, result.normal, size) ImpactSpawner.spawn(get_tree(), impact_type, result.position, result.normal, size)
destroy()
func destroy() -> void:
queue_free() queue_free()
+1 -1
View File
@@ -98,7 +98,7 @@ func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
proj.owner_player = player proj.owner_player = player
# Explosive stats # Explosive stats
proj.explosion_radius = 8.0 # Moderate AoE proj.explosion_radius = 4.0 # Moderate AoE
proj.explosion_knockback = 45.0 # Strong knockback! proj.explosion_knockback = 45.0 # Strong knockback!
proj.can_self_damage = false # As requested proj.can_self_damage = false # As requested
+3 -3
View File
@@ -109,7 +109,7 @@ func _fire() -> void:
var tween = create_tween() var tween = create_tween()
tween.tween_property(muzzle_flash, "light_energy", 0.0, 0.1) tween.tween_property(muzzle_flash, "light_energy", 0.0, 0.1)
var origin = camera.global_position + camera.global_transform.basis * Vector3(0.4, -0.2, -0.6) var _origin = camera.global_position + camera.global_transform.basis * Vector3(0.4, -0.2, -0.6)
var base_dir = -camera.global_transform.basis.z var base_dir = -camera.global_transform.basis.z
_current_aim_dir = base_dir _current_aim_dir = base_dir
@@ -162,7 +162,7 @@ func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
# Logic # Logic
proj.weapon_source = self proj.weapon_source = self
proj.turn_speed = 4.0 # Slower turn radius so they sweep nicely proj.turn_speed = 6.5 # Slower turn radius so they sweep nicely
proj.position = origin # Start at origin to spread outward around camera proj.position = origin # Start at origin to spread outward around camera
proj.logical_source = origin proj.logical_source = origin
@@ -184,7 +184,7 @@ func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
proj.owner_player = player proj.owner_player = player
# Explosive stats # Explosive stats
proj.explosion_radius = 3.0 # Tiny AoE effect proj.explosion_radius = 1.5 # Tiny AoE effect
proj.explosion_knockback = 5.0 proj.explosion_knockback = 5.0
proj.can_self_damage = false proj.can_self_damage = false