127 lines
4.0 KiB
GDScript
127 lines
4.0 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
|
|
@export var nuclear: bool = false
|
|
|
|
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
|
|
if nuclear:
|
|
CameraRumble.emit_signal("nuclear_blast")
|
|
|
|
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: PhysicsDirectBodyState3D = PhysicsServer3D.body_get_direct_state(rid)
|
|
if is_instance_valid(bodyState):
|
|
bodyState.sleeping = false # Force objects to wake up
|
|
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
|