Armazon/scripts/explosion.gd
2024-01-28 13:44:54 -05:00

123 lines
3.8 KiB
GDScript

@tool
extends Node3D
class_name Explosion
@export_range(0.1,5,0.01,"or_greater") var falloff_exponent: float = 1.2
@export_range(10,100000,10,"or_greater") var impulse: int = 15000
@export_range(0.1,100,0.1,"or_greater") var character_velocity: float = 13
@export_range(0.1,10,0.1,"or_greater") var radius: float = 5:
set(newVal):
if is_instance_valid(blast_visual):
blast_visual.radius = newVal
radius = newVal
get:
return radius
@export_flags_3d_physics var collision_mask: int = 15
@onready var shape_rid: RID
@onready var shape_params: PhysicsShapeQueryParameters3D
@onready var ray_params: PhysicsRayQueryParameters3D
@onready var blast_visual: CSGSphere3D = null
@export var rumble_amount: float = 10
func _ready() -> void:
if Engine.is_editor_hint():
set_physics_process(false)
blast_visual = CSGSphere3D.new()
blast_visual.material = load("res://assets/materials/mat_blast_debug.tres")
blast_visual.radius = radius
blast_visual.radial_segments = 32
blast_visual.rings = 16
add_child(blast_visual)
if not Engine.is_editor_hint():
set_physics_process(true)
# Initialize sphere shape
shape_rid = PhysicsServer3D.sphere_shape_create()
PhysicsServer3D.shape_set_data(shape_rid, radius)
# Initialize shape query params
shape_params = PhysicsShapeQueryParameters3D.new()
shape_params.shape_rid = shape_rid
shape_params.collision_mask = collision_mask
# Initialize ray params
ray_params = PhysicsRayQueryParameters3D.new()
ray_params.collision_mask = collision_mask
ray_params.collide_with_areas = false
ray_params.collide_with_bodies = true
ray_params.hit_from_inside = true
ray_params.hit_back_faces = true
for child in get_children():
if child is GPUParticles3D:
child.one_shot = true
child.emitting = true
var t = $light.create_tween()
t.tween_property($light,'light_energy',0,0.6)
CameraRumble.rumble += rumble_amount
func _exit_tree() -> void:
if not Engine.is_editor_hint():
PhysicsServer3D.free_rid(shape_rid)
func _physics_process(_delta: float) -> void:
set_physics_process(false)
shape_params.transform = global_transform
ray_params.from = shape_params.transform.origin
var space = get_world_3d().direct_space_state
var results = space.intersect_shape(shape_params, 32)
var hit: Array[RigidBody3D] = []
var excludes: Array[RID] = []
for res in results:
var c = res.get("collider", null)
if is_instance_valid(c) and c is RigidBody3D:
hit.append(c)
excludes.append(res.get("rid"))
var ray_excludes = excludes.duplicate()
for i in range(0,hit.size()):
var p: RigidBody3D = hit[i]
var rid: RID = excludes[i]
#print("At index ", i, " looking at hit ", p.to_string(), " with RID ", rid.get_id())
var pos: Vector3 = p.global_position
var dir: Vector3 = (pos - ray_params.from).normalized()
ray_params.to = pos #ray_params.from + (dir * radius)
# Remove index from exclude-list
ray_excludes.erase(rid)
ray_params.exclude = ray_excludes
#print("\t", ray_excludes.has(rid))
var rayRes = space.intersect_ray(ray_params)
ray_excludes.append(rid) # Re-add index to exclude list
#print("\t", rayRes)
if rayRes.has("rid") and rayRes.get("rid").get_id() == rid.get_id(): # If we hit the same object
#call_deferred("place_dot", p, rayRes.get("position"))
var hitPos: Vector3 = rayRes.get("position")
var factor: float = falloff(ray_params.from.distance_to(rayRes.get("position")))
var bodyState = PhysicsServer3D.body_get_direct_state(rid)
if is_instance_valid(bodyState):
bodyState.apply_impulse(dir * impulse, hitPos - pos)
func falloff(dist: float) -> float:
return max((1.0 - pow(dist / radius, falloff_exponent)), 0)
func place_dot(parent: Node3D, location: Vector3) -> void:
var dot = CSGSphere3D.new()
dot.radius = 0.1
parent.add_child(dot)
dot.global_position = location