This commit is contained in:
Redsandy
2026-01-18 20:56:24 +03:00
commit 7980e0add8
68 changed files with 1605294 additions and 0 deletions

182
scripts/projectile.gd Normal file
View File

@@ -0,0 +1,182 @@
extends Area3D
class_name Projectile
# Базовый класс для снарядов
@export var speed: float = 120.0
@export var damage: float = 20.0
@export var lifetime: float = 5.0
@export var is_explosive: bool = false
@export var explosion_radius: float = 3.0
var direction: Vector3 = Vector3.ZERO
var velocity: Vector3 = Vector3.ZERO
var owner_unit: Node3D = null
var lifetime_timer: float = 0.0
var has_hit: bool = false
var previous_position: Vector3 = Vector3.ZERO
signal hit(target: Node3D, position: Vector3)
signal exploded(position: Vector3)
func _ready():
lifetime_timer = lifetime
previous_position = global_position
# Настраиваем Area3D для обнаружения целей
body_entered.connect(_on_body_entered)
# Отключаем мониторинг по умолчанию для параболических
monitoring = true
monitorable = false
func _physics_process(delta):
if has_hit:
return
lifetime_timer -= delta
if lifetime_timer <= 0:
if is_explosive:
explode(global_position)
else:
queue_free()
return
# Сохраняем предыдущую позицию для raycast
previous_position = global_position
# Движение снаряда
global_position += velocity * delta
# Проверка столкновений через raycast
check_collisions_raycast()
func check_collisions_raycast():
# Используем raycast для проверки столкновений
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(previous_position, global_position)
query.exclude = [self, owner_unit] if owner_unit else [self]
query.collision_mask = 1 # Только слой земли и объектов
var result = space_state.intersect_ray(query)
if result:
var collider = result.collider
var hit_position = result.position
# Если это враг, наносим урон
if collider.has_method("take_damage"):
collider.take_damage(damage)
hit.emit(collider, hit_position)
on_hit(hit_position)
func _on_body_entered(body: Node3D):
# Дополнительная проверка через Area3D
if has_hit:
return
if body == owner_unit:
return
if body is Projectile:
return
if body.has_method("take_damage"):
body.take_damage(damage)
hit.emit(body, global_position)
on_hit(global_position)
func on_hit(position: Vector3):
if has_hit:
return
has_hit = true
velocity = Vector3.ZERO
# Останавливаем частицы
var fire_particles = get_node_or_null("FireParticles")
var trail_particles = get_node_or_null("TrailParticles")
if fire_particles:
fire_particles.emitting = false
if trail_particles:
trail_particles.emitting = false
if is_explosive:
explode(position)
else:
# Простое попадание
hit.emit(null, position)
# Воспроизводим анимацию попадания
play_hit_animation()
# Удаляем через небольшую задержку
await get_tree().create_timer(0.5).timeout
queue_free()
func explode(position: Vector3):
# Находим все цели в радиусе взрыва
var space_state = get_world_3d().direct_space_state
var query = PhysicsShapeQueryParameters3D.new()
var sphere_shape = SphereShape3D.new()
sphere_shape.radius = explosion_radius
query.shape = sphere_shape
query.transform.origin = position
query.collision_mask = 1
var results = space_state.intersect_shape(query)
# Наносим урон всем в радиусе
for result in results:
var collider = result.collider
if collider != owner_unit and collider.has_method("take_damage"):
var distance = position.distance_to(collider.global_position)
var damage_multiplier = 1.0 - (distance / explosion_radius)
collider.take_damage(damage * damage_multiplier)
exploded.emit(position)
play_explosion_animation()
# Ждем, пока частицы взрыва отыграют (0.5 секунды)
await get_tree().create_timer(0.5).timeout
queue_free()
func play_hit_animation():
# Создаем простой визуальный эффект попадания
var mesh = get_node_or_null("MeshInstance3D")
if mesh:
# Увеличиваем размер и скрываем
var tween = create_tween()
tween.tween_property(mesh, "scale", Vector3(2.0, 2.0, 2.0), 0.2)
tween.tween_callback(func(): mesh.visible = false)
func play_explosion_animation():
# Скрываем основной меш
var mesh = get_node_or_null("MeshInstance3D")
if mesh:
mesh.visible = false
# Останавливаем частицы полета
var fire_particles = get_node_or_null("FireParticles")
var trail_particles = get_node_or_null("TrailParticles")
if fire_particles:
fire_particles.emitting = false
if trail_particles:
trail_particles.emitting = false
# Воспроизводим частицы взрыва
var explosion_particles = get_node_or_null("ExplosionParticles")
var explosion_core = get_node_or_null("ExplosionCore")
var explosion_smoke = get_node_or_null("ExplosionSmoke")
# Масштабируем узлы частиц в зависимости от радиуса взрыва
# Это создаст визуальный эффект взрыва нужного размера
var scale_factor = explosion_radius / 3.0 # 3.0 - базовый радиус взрыва
if explosion_particles:
explosion_particles.scale = Vector3.ONE * scale_factor
explosion_particles.restart()
explosion_particles.emitting = true
if explosion_core:
explosion_core.scale = Vector3.ONE * scale_factor * 0.6
explosion_core.restart()
explosion_core.emitting = true
if explosion_smoke:
explosion_smoke.scale = Vector3.ONE * scale_factor * 1.2
explosion_smoke.restart()
explosion_smoke.emitting = true