feat: implement weapon system, loadout manager, and specialized projectile behaviors with associated assets
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
extends Projectile
|
||||
class_name ExplosiveProjectile
|
||||
|
||||
var explosion_radius: float = 5.0
|
||||
var explosion_knockback: float = 20.0
|
||||
var can_self_damage: bool = false
|
||||
var falloff_curve: float = 1.0 # 1.0 is linear, 2.0 is quadratic (steep drop)
|
||||
|
||||
func _on_hit(result: Dictionary) -> void:
|
||||
_explode(result.position)
|
||||
queue_free()
|
||||
|
||||
func _explode(pos: Vector3) -> void:
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
|
||||
# We use a sphere shape query to find all physics bodies in radius
|
||||
var sphere = SphereShape3D.new()
|
||||
sphere.radius = explosion_radius
|
||||
|
||||
var params = PhysicsShapeQueryParameters3D.new()
|
||||
params.shape = sphere
|
||||
params.transform = Transform3D(Basis(), pos)
|
||||
# Find bodies and areas
|
||||
params.collide_with_bodies = true
|
||||
|
||||
var results = space_state.intersect_shape(params)
|
||||
|
||||
# We want to only damage each object once (intersect_shape can return multiple shapes of the same object)
|
||||
var processed_rids = {}
|
||||
|
||||
for res in results:
|
||||
var col = res.collider
|
||||
if not col or processed_rids.has(res.rid):
|
||||
continue
|
||||
processed_rids[res.rid] = true
|
||||
|
||||
# Calculate distance
|
||||
var target_pos = col.global_position
|
||||
# If it's a character, maybe aim at their center (offset y by 1.0)
|
||||
if col is CharacterBody3D or col.has_method("take_damage"):
|
||||
if col.has_node("CollisionShape3D"):
|
||||
target_pos = col.get_node("CollisionShape3D").global_position
|
||||
|
||||
var dist = pos.distance_to(target_pos)
|
||||
if dist > explosion_radius:
|
||||
continue
|
||||
|
||||
# Normalised distance (0 = epicenter, 1 = edge)
|
||||
var ndist = dist / explosion_radius
|
||||
var intensity = clampf(1.0 - pow(ndist, falloff_curve), 0.0, 1.0)
|
||||
|
||||
var final_damage = damage * intensity
|
||||
var final_knockback = explosion_knockback * intensity
|
||||
|
||||
# Knockback direction: from explosion pos to target
|
||||
var dir = (target_pos - pos).normalized()
|
||||
# Add a tiny bit of upward bias to knockback so it lifts them
|
||||
dir = (dir + Vector3(0, 0.5, 0)).normalized()
|
||||
|
||||
# Apply knockback if it has apply_impulse
|
||||
if col.has_method("apply_impulse"):
|
||||
col.apply_impulse(dir * final_knockback)
|
||||
|
||||
# Apply damage
|
||||
if col.has_method("take_damage"):
|
||||
if col == owner_player and not can_self_damage:
|
||||
# Skip self damage, but we still applied the knockback!
|
||||
pass
|
||||
else:
|
||||
col.take_damage(final_damage, target_pos, owner_player)
|
||||
Reference in New Issue
Block a user