71 lines
2.1 KiB
GDScript
71 lines
2.1 KiB
GDScript
extends Node3D
|
|
class_name Projectile
|
|
|
|
var velocity: Vector3 = Vector3.ZERO
|
|
var damage: float = 10.0
|
|
var max_range: float = 200.0
|
|
var distance_traveled: float = 0.0
|
|
var owner_player: CharacterBody3D
|
|
var falloff_start: float = 10.0
|
|
var min_damage: float = 5.0
|
|
var drop_gravity: float = 0.0 # For arc projectiles (nailgun might have slight drop, plasma none)
|
|
var impact_type: String = "bullet"
|
|
|
|
var logical_source = null
|
|
var _previous_position: Vector3
|
|
|
|
func _ready() -> void:
|
|
if logical_source != null:
|
|
_previous_position = logical_source
|
|
else:
|
|
_previous_position = global_position
|
|
# Automatically destroy after 5 seconds to prevent leaks
|
|
var t = get_tree().create_timer(5.0)
|
|
t.timeout.connect(destroy)
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
# Apply gravity drop if any
|
|
velocity.y -= drop_gravity * delta
|
|
|
|
var movement = velocity * delta
|
|
global_position += movement
|
|
|
|
distance_traveled += movement.length()
|
|
|
|
if distance_traveled > max_range:
|
|
destroy()
|
|
return
|
|
|
|
_check_collision()
|
|
_previous_position = global_position
|
|
|
|
func _check_collision() -> void:
|
|
var space_state = get_world_3d().direct_space_state
|
|
var query = PhysicsRayQueryParameters3D.create(_previous_position, global_position)
|
|
if owner_player:
|
|
query.exclude = [owner_player.get_rid()]
|
|
|
|
var result = space_state.intersect_ray(query)
|
|
if result:
|
|
_on_hit(result)
|
|
|
|
func _on_hit(result: Dictionary) -> void:
|
|
var hit_dist = distance_traveled - (_previous_position.distance_to(result.position))
|
|
|
|
var final_damage = damage
|
|
if hit_dist > falloff_start and falloff_start > 0.0:
|
|
var falloff_factor = clampf((hit_dist - falloff_start) / (max_range - falloff_start), 0.0, 1.0)
|
|
final_damage = lerpf(damage, min_damage, falloff_factor)
|
|
|
|
if result.collider.has_method("take_damage"):
|
|
if owner_player and owner_player.multiplayer.is_server():
|
|
result.collider.take_damage(final_damage, result.position, owner_player)
|
|
elif result.collider is StaticBody3D or result.collider is CSGShape3D:
|
|
var size = 0.4 if impact_type == "plasma" else 0.15
|
|
ImpactSpawner.spawn(get_tree(), impact_type, result.position, result.normal, size)
|
|
|
|
destroy()
|
|
|
|
func destroy() -> void:
|
|
queue_free()
|