first commit
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
[gd_scene format=3 uid="uid://dnxeupvhs25c5"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://de0b6l5ylu4oy" path="res://addons/NPC/script/blood_particles.gd" id="1_wa0q6"]
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_7mumk"]
|
||||
lifetime_randomness = 0.34
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 180.0
|
||||
initial_velocity_min = 2.0
|
||||
initial_velocity_max = 4.0
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0c2lx"]
|
||||
albedo_color = Color(0.7527466, 0, 0.041491196, 1)
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_yn2k5"]
|
||||
material = SubResource("StandardMaterial3D_0c2lx")
|
||||
size = Vector3(0.055, 0.05, 0)
|
||||
|
||||
[node name="BloodParticles" type="GPUParticles3D" unique_id=181301576]
|
||||
emitting = false
|
||||
amount = 15
|
||||
lifetime = 0.4
|
||||
one_shot = true
|
||||
explosiveness = 1.0
|
||||
process_material = SubResource("ParticleProcessMaterial_7mumk")
|
||||
draw_pass_1 = SubResource("BoxMesh_yn2k5")
|
||||
script = ExtResource("1_wa0q6")
|
||||
@@ -0,0 +1,34 @@
|
||||
[gd_scene format=3 uid="uid://dr6aa08wnojoo"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bgnhdyrrg72kx" path="res://addons/NPC/script/enemy_mob.gd" id="1_uwf7c"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_se2kl"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_diulm"]
|
||||
size = Vector3(2.6951447, 1.3225784, 5.542081)
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_sngx8"]
|
||||
|
||||
[node name="em" type="StaticBody3D" unique_id=2094693982]
|
||||
collision_layer = 64
|
||||
collision_mask = 121
|
||||
script = ExtResource("1_uwf7c")
|
||||
|
||||
[node name="Hitbox" type="CollisionShape3D" parent="." unique_id=1852865682]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.017934263, 0.009854734, 0.019842744)
|
||||
shape = SubResource("CapsuleShape3D_se2kl")
|
||||
debug_color = Color(0.52798665, 0.57221377, 5.7756904e-07, 0.41960785)
|
||||
|
||||
[node name="DetectArea" type="Area3D" parent="." unique_id=798798411]
|
||||
transform = Transform3D(1.15835, 0, 0, 0, 1.5085664, 0, 0, 0, 0.5336906, 0.4316032, 0.013125002, -0.39214337)
|
||||
collision_layer = 64
|
||||
collision_mask = 2
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="DetectArea" unique_id=1961649541]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5433707, 0.09678304, 0.42794847)
|
||||
shape = SubResource("BoxShape3D_diulm")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1417626392]
|
||||
mesh = SubResource("CapsuleMesh_sngx8")
|
||||
|
||||
[connection signal="area_entered" from="DetectArea" to="." method="_on_detect_area_area_entered"]
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,34 @@
|
||||
[gd_scene format=3 uid="uid://vo2yhy5ym5ug"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bgnhdyrrg72kx" path="res://addons/NPC/script/enemy_mob.gd" id="1_g34hb"]
|
||||
[ext_resource type="Script" uid="uid://drhhet1lt3f5a" path="res://addons/hp/health_component.gd" id="2_pr0ef"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_jkuv2"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_g34hb"]
|
||||
size = Vector3(9.06456, 2.5653687, 8.218338)
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_pr0ef"]
|
||||
|
||||
[node name="CharacterBody3D" type="CharacterBody3D" unique_id=1156384226]
|
||||
collision_layer = 64
|
||||
collision_mask = 121
|
||||
script = ExtResource("1_g34hb")
|
||||
|
||||
[node name="Hitbox" type="CollisionShape3D" parent="." unique_id=1374343369]
|
||||
shape = SubResource("CapsuleShape3D_jkuv2")
|
||||
|
||||
[node name="DetectArea" type="Area3D" parent="." unique_id=356424442]
|
||||
collision_layer = 64
|
||||
collision_mask = 121
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="DetectArea" unique_id=2072637957]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.16861972, 0.64931965, -0.60822296)
|
||||
shape = SubResource("BoxShape3D_g34hb")
|
||||
debug_color = Color(0.5763886, 0.84150624, 0.017774109, 0.41960785)
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=297370616]
|
||||
mesh = SubResource("CapsuleMesh_pr0ef")
|
||||
|
||||
[node name="HealthComponent" type="Node3D" parent="." unique_id=1263552151]
|
||||
script = ExtResource("2_pr0ef")
|
||||
@@ -0,0 +1,52 @@
|
||||
[gd_scene format=3 uid="uid://cdhaqyi31qpjw"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://mlweykxjyekq" path="res://addons/custmode/en.glb" id="1_yy4rf"]
|
||||
[ext_resource type="Texture2D" uid="uid://dyx44n2j1qyuj" path="res://addons/custmode/en_0.png" id="2_f1rwy"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_6soc7"]
|
||||
albedo_texture = ExtResource("2_f1rwy")
|
||||
metallic_specular = 0.42
|
||||
|
||||
[sub_resource type="Skin" id="Skin_0dhb3"]
|
||||
bind_count = 8
|
||||
bind/0/name = &""
|
||||
bind/0/bone = -1
|
||||
bind/0/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
bind/1/name = &""
|
||||
bind/1/bone = -1
|
||||
bind/1/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
bind/2/name = &""
|
||||
bind/2/bone = -1
|
||||
bind/2/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
bind/3/name = &""
|
||||
bind/3/bone = -1
|
||||
bind/3/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
bind/4/name = &""
|
||||
bind/4/bone = -1
|
||||
bind/4/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
bind/5/name = &""
|
||||
bind/5/bone = -1
|
||||
bind/5/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
bind/6/name = &""
|
||||
bind/6/bone = -1
|
||||
bind/6/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
bind/7/name = &""
|
||||
bind/7/bone = -1
|
||||
bind/7/pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
|
||||
[node name="en" unique_id=1144982072 instance=ExtResource("1_yy4rf")]
|
||||
|
||||
[node name="temp_mesh_ply" parent="world" parent_id_path=PackedInt32Array(1653574111) index="0" unique_id=1619009496]
|
||||
material_override = SubResource("StandardMaterial3D_6soc7")
|
||||
skin = SubResource("Skin_0dhb3")
|
||||
|
||||
[node name="FABRIK3D" type="FABRIK3D" parent="." index="1" unique_id=326317683]
|
||||
setting_count = 1
|
||||
settings/0/target_node = NodePath("../world/temp_mesh_ply")
|
||||
settings/0/root_bone_name = ""
|
||||
settings/0/root_bone = -1
|
||||
settings/0/end_bone_name = ""
|
||||
settings/0/end_bone = -1
|
||||
settings/0/joint_count = 0
|
||||
|
||||
[node name="Skeleton3D" type="Skeleton3D" parent="FABRIK3D" index="0" unique_id=1597347973]
|
||||
@@ -0,0 +1,34 @@
|
||||
[gd_scene format=3 uid="uid://dw1m2l3al7kxk"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bv488f4v7ly6a" path="res://addons/NPC/script/dialog_npct.gd" id="1_se2kl"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_se2kl"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_diulm"]
|
||||
size = Vector3(1.6360468, 1.3225784, 2.9930296)
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_sngx8"]
|
||||
|
||||
[node name="npc" type="StaticBody3D" unique_id=2094693982]
|
||||
collision_layer = 64
|
||||
collision_mask = 121
|
||||
script = ExtResource("1_se2kl")
|
||||
|
||||
[node name="Hitbox" type="CollisionShape3D" parent="." unique_id=1852865682]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.017934263, 0.009854734, 0.019842744)
|
||||
shape = SubResource("CapsuleShape3D_se2kl")
|
||||
debug_color = Color(0.52798665, 0.57221377, 5.7756904e-07, 0.41960785)
|
||||
|
||||
[node name="DetectArea" type="Area3D" parent="." unique_id=798798411]
|
||||
transform = Transform3D(1.15835, 0, 0, 0, 1.5085664, 0, 0, 0, 0.5336906, 0.4316032, 0.013125002, -0.39214337)
|
||||
collision_layer = 64
|
||||
collision_mask = 2
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="DetectArea" unique_id=1961649541]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.2729469, 0.09678304, 0.4440172)
|
||||
shape = SubResource("BoxShape3D_diulm")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1417626392]
|
||||
mesh = SubResource("CapsuleMesh_sngx8")
|
||||
|
||||
[connection signal="area_entered" from="DetectArea" to="." method="_on_detect_area_area_entered"]
|
||||
@@ -0,0 +1,9 @@
|
||||
extends GPUParticles3D
|
||||
|
||||
func _ready() -> void:
|
||||
# Принудительно запускаем взрыв частиц
|
||||
emitting = true
|
||||
# Ждем время, равное жизни частиц (Lifetime), плюс небольшой запас
|
||||
await get_tree().create_timer(lifetime + 0.1).timeout
|
||||
# Удаляем узел из игры, чтобы не лагало
|
||||
queue_free()
|
||||
@@ -0,0 +1 @@
|
||||
uid://de0b6l5ylu4oy
|
||||
@@ -0,0 +1,65 @@
|
||||
extends StaticBody3D
|
||||
|
||||
@export var dialogue_data: Array[Dictionary] = [
|
||||
{"name": "Сталкер", "text": "Привет! Наконец-то ты подошел."},
|
||||
{"name": "Игрок", "text": "Привет. Что за проблемы со скриптами?"},
|
||||
{"name": "Сталкер", "text": "Да вот, теперь и подсказки работают как надо!"}
|
||||
]
|
||||
|
||||
var is_player_inside: bool = false
|
||||
var ui_node: Node = null
|
||||
var detect_area: Area3D = null
|
||||
|
||||
func _ready() -> void:
|
||||
detect_area = $DetectArea
|
||||
if detect_area:
|
||||
print("[NPC] Зона DetectArea найдена. Мониторинг подсказок включен.")
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if detect_area == null:
|
||||
return
|
||||
|
||||
# Опрашиваем зону на наличие игрока
|
||||
var overlapping_bodies = detect_area.get_overlapping_bodies()
|
||||
var found_player_this_frame = false
|
||||
|
||||
for body in overlapping_bodies:
|
||||
var weapon_manager = body.find_child("WeaponManager", true, false)
|
||||
if weapon_manager == null and body.get_parent():
|
||||
weapon_manager = body.get_parent().find_child("WeaponManager", true, false)
|
||||
|
||||
if weapon_manager != null or "Player" in body.name or "Character" in body.name:
|
||||
found_player_this_frame = true
|
||||
break
|
||||
|
||||
# Игрок ТОЛЬКО ЧТО ЗАШЕЛ в зону NPC
|
||||
if found_player_this_frame and not is_player_inside:
|
||||
is_player_inside = true
|
||||
|
||||
# Ищем узел интерфейса на сцене
|
||||
ui_node = get_tree().current_scene.find_child("DialogueUI", true, false)
|
||||
if ui_node == null:
|
||||
ui_node = get_tree().current_scene.find_child("dialogue_ui", true, false)
|
||||
|
||||
# ВКЛЮЧАЕМ ПОДСКАЗКУ «Нажмите E»
|
||||
if ui_node and ui_node.has_method("show_prompt"):
|
||||
ui_node.show_prompt()
|
||||
|
||||
# Игрок ТОЛЬКО ЧТО ВЫШЕЛ из зоны NPC
|
||||
elif not found_player_this_frame and is_player_inside:
|
||||
is_player_inside = false
|
||||
|
||||
# ПРЯЧЕМ ПОДСКАЗКУ «Нажмите E»
|
||||
if ui_node and ui_node.has_method("hide_prompt"):
|
||||
ui_node.hide_prompt()
|
||||
if ui_node and ui_node.has_method("end_dialogue"):
|
||||
ui_node.end_dialogue()
|
||||
|
||||
# Проверка нажатия кнопки для старта диалога
|
||||
if is_player_inside:
|
||||
if Input.is_key_pressed(KEY_E):
|
||||
if ui_node and ui_node.has_method("start_dialogue"):
|
||||
# Прячем подсказку, так как диалог уже начался
|
||||
if ui_node.has_method("hide_prompt"):
|
||||
ui_node.hide_prompt()
|
||||
ui_node.start_dialogue(dialogue_data)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bv488f4v7ly6a
|
||||
@@ -0,0 +1,224 @@
|
||||
extends CharacterBody3D
|
||||
|
||||
@export var move_speed: float = 4.0 # Скорость преследования игрока
|
||||
@export var damage_to_player: float = 10.0 # Базовый урон за один выстрел
|
||||
@export var attack_cooldown: float = 1.0 # Интервал стрельбы ПОСЛЕ первого выстрела (в секундах)
|
||||
@export var vision_range: float = 25.0 # Радиус чутья моба (в метрах)
|
||||
|
||||
# --- НАСТРОЙКА ЗАДЕРЖКИ ПЕРЕД ВЫСТРЕЛОМ ---
|
||||
@export var lock_on_time: float = 2.5 # Время на прицеливание перед выстрелом (в секундах)
|
||||
var aiming_timer: float = 0.0 # Текущее накопленное время прицеливания
|
||||
|
||||
# --- НАСТРОЙКА УНИКАЛЬНОЙ МУЗЫКИ ДЛЯ МОБА ---
|
||||
@export var custom_battle_music : AudioStream
|
||||
|
||||
# --- 🖼 ВЫБОР КАРТИНКИ ВСПЫШКИ В ИНСПЕКТОРЕ ---
|
||||
@export var muzzle_flash_texture : Texture2D
|
||||
|
||||
# Переменные здоровья (работают напрямую, как у мишени)
|
||||
@export var health: float = 100.0
|
||||
var isDisabled: bool = false
|
||||
|
||||
var player_node: Node = null
|
||||
var is_player_in_range: bool = false
|
||||
var is_currently_seeing_player: bool = false
|
||||
|
||||
# Динамический узел вспышки, который код создаст сам
|
||||
var dynamic_flash_sprite : Sprite3D
|
||||
|
||||
# Таймер перезарядки оружия
|
||||
var attack_timer: float = 0.0
|
||||
|
||||
# Сила гравитации
|
||||
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
|
||||
func _ready() -> void:
|
||||
if not is_in_group("Enemies"):
|
||||
add_to_group("Enemies")
|
||||
print("[ВРАГ] Тактический моб со зрением через укрытия запущен. Здоровье: ", health)
|
||||
|
||||
# АВТО-СОЗДАНИЕ И НАСТРОЙКА КАРТИНКИ ВСПЫШКИ
|
||||
if muzzle_flash_texture != null:
|
||||
dynamic_flash_sprite = Sprite3D.new()
|
||||
dynamic_flash_sprite.texture = muzzle_flash_texture
|
||||
dynamic_flash_sprite.visible = false
|
||||
dynamic_flash_sprite.billboard = BaseMaterial3D.BILLBOARD_ENABLED # Поворот на игрока
|
||||
|
||||
# Пытаемся прикрепить к кончику ствола оружия, если есть контейнер, иначе к центру моба
|
||||
if has_node("WeaponManager/WeaponContainer"):
|
||||
get_node("WeaponManager/WeaponContainer").add_child(dynamic_flash_sprite)
|
||||
# Сдвигаем чуть вперед по оси Z, чтобы картинка не тонула в дуле
|
||||
dynamic_flash_sprite.position = Vector3(0, 0, -0.4)
|
||||
else:
|
||||
add_child(dynamic_flash_sprite)
|
||||
dynamic_flash_sprite.position = Vector3(0, 1.2, -0.5)
|
||||
|
||||
# ИНИЦИАЛИЗАЦИЯ ОРУЖИЯ ДЛЯ АНИМАЦИЙ ПРИ СТАРТЕ
|
||||
if has_node("WeaponManager/AnimationManager") and has_node("WeaponManager/WeaponContainer"):
|
||||
var anim_mgr = get_node("WeaponManager/AnimationManager")
|
||||
var weapon_model = get_node("WeaponManager/WeaponContainer")
|
||||
|
||||
var mock_weapon = {
|
||||
"tiltRotAmount": 0.05,
|
||||
"tiltRotSpeed": 5.0,
|
||||
"bobFreq": 0.005,
|
||||
"bobAmount": 0.05,
|
||||
"bobSpeed": 5.0,
|
||||
"onIdleBobFreqDivider": 2.0,
|
||||
"bobPos": Vector3.ZERO
|
||||
}
|
||||
anim_mgr.getCurrentWeapon(mock_weapon, weapon_model)
|
||||
|
||||
# --- СИСТЕМА ПОПАДАНИЙ КАК У МИШЕНИ ---
|
||||
|
||||
func hitscanHit(damageVal: float, _hitscanDir: Vector3, _hitscanPos: Vector3) -> void:
|
||||
if isDisabled: return
|
||||
health -= damageVal
|
||||
print("[ВРАГ] Хитскан попал в моба! Здоровье моба: ", health)
|
||||
if health <= 0.0: die()
|
||||
|
||||
func projectileHit(damageVal: float, _hitscanDir: Vector3) -> void:
|
||||
if isDisabled: return
|
||||
health -= damageVal
|
||||
print("[ВРАГ] Прожектайл попал в моба! Здоровье моба: ", health)
|
||||
if health <= 0.0: die()
|
||||
|
||||
func die() -> void:
|
||||
isDisabled = true
|
||||
print("[ВРАГ] Моб уничтожен!")
|
||||
if custom_battle_music and is_currently_seeing_player and has_node("/root/MusicManager"):
|
||||
get_node("/root/MusicManager").unregister_enemy_vision(self, custom_battle_music)
|
||||
queue_free()
|
||||
|
||||
# --- ФИЗИКА, ПРИЦЕЛИВАНИЕ И СТРЕЛЬБА ---
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if isDisabled: return
|
||||
|
||||
if not is_on_floor():
|
||||
velocity.y -= gravity * delta
|
||||
else:
|
||||
velocity.y = 0
|
||||
|
||||
if attack_timer > 0:
|
||||
attack_timer -= delta
|
||||
|
||||
if player_node == null:
|
||||
player_node = get_tree().current_scene.find_child("PlayerCharacter", true, false)
|
||||
|
||||
if player_node != null:
|
||||
var distance = global_position.distance_to(player_node.global_position)
|
||||
var is_player_alive = true
|
||||
var p_health = player_node.find_child("HealthComponent", true, false)
|
||||
if p_health and "current_health" in p_health:
|
||||
is_player_alive = p_health.current_health > 0
|
||||
elif "health" in player_node:
|
||||
is_player_alive = player_node.health > 0
|
||||
|
||||
if distance <= vision_range and is_player_alive:
|
||||
is_player_in_range = true
|
||||
else:
|
||||
is_player_in_range = false
|
||||
else:
|
||||
is_player_in_range = false
|
||||
|
||||
if is_player_in_range and player_node != null:
|
||||
var distance = global_position.distance_to(player_node.global_position)
|
||||
var target_pos = player_node.global_position
|
||||
target_pos.y = global_position.y
|
||||
|
||||
if global_position.distance_to(target_pos) > 0.1:
|
||||
look_at(target_pos, Vector3.UP)
|
||||
|
||||
if distance > 2.5:
|
||||
var direction = (player_node.global_position - global_position).normalized()
|
||||
velocity.x = direction.x * move_speed
|
||||
velocity.z = direction.z * move_speed
|
||||
else:
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
|
||||
var sees_now = has_clear_line_of_sight()
|
||||
|
||||
if custom_battle_music:
|
||||
if sees_now != is_currently_seeing_player:
|
||||
is_currently_seeing_player = sees_now
|
||||
if has_node("/root/MusicManager"):
|
||||
if is_currently_seeing_player:
|
||||
get_node("/root/MusicManager").register_enemy_vision(self, custom_battle_music)
|
||||
else:
|
||||
get_node("/root/MusicManager").unregister_enemy_vision(self, custom_battle_music)
|
||||
|
||||
if sees_now:
|
||||
if attack_timer <= 0:
|
||||
aiming_timer += delta
|
||||
if aiming_timer >= lock_on_time:
|
||||
enemy_shoot()
|
||||
aiming_timer = 0.0
|
||||
else:
|
||||
aiming_timer = 0.0
|
||||
else:
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
aiming_timer = 0.0
|
||||
|
||||
if custom_battle_music and is_currently_seeing_player:
|
||||
is_currently_seeing_player = false
|
||||
if has_node("/root/MusicManager"):
|
||||
get_node("/root/MusicManager").unregister_enemy_vision(self, custom_battle_music)
|
||||
|
||||
move_and_slide()
|
||||
|
||||
func has_clear_line_of_sight() -> bool:
|
||||
if player_node == null: return false
|
||||
var start_pos = global_position + Vector3(0, 1.0, 0)
|
||||
var end_pos = player_node.global_position + Vector3(0, 1.0, 0)
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var query = PhysicsRayQueryParameters3D.create(start_pos, end_pos)
|
||||
|
||||
var exclusion_list = [get_rid()]
|
||||
for child in get_children():
|
||||
if child is CollisionObject3D: exclusion_list.append(child.get_rid())
|
||||
query.exclude = exclusion_list
|
||||
|
||||
query.collide_with_bodies = true
|
||||
query.collide_with_areas = false
|
||||
var result = space_state.intersect_ray(query)
|
||||
if result.is_empty(): return true
|
||||
var hit_node = result.collider
|
||||
if hit_node == player_node or hit_node.get_parent() == player_node or hit_node.is_in_group("Player"):
|
||||
return true
|
||||
return false
|
||||
|
||||
func enemy_shoot() -> void:
|
||||
attack_timer = attack_cooldown
|
||||
if player_node == null: return
|
||||
|
||||
# --- 🔊 ВОСПРОИЗВЕДЕНИЕ ЗВУКА ВЫСТРЕЛА ---
|
||||
var shoot_audio = get_node_or_null("%ShootSound")
|
||||
if shoot_audio == null: shoot_audio = find_child("ShootSound", true, false)
|
||||
if shoot_audio:
|
||||
shoot_audio.stop()
|
||||
shoot_audio.play()
|
||||
|
||||
# --- 🔥 ОТОБРАЖЕНИЕ КАРТИНКИ ВСПЫШКИ ВСПЛЫВАЮЩЕЙ НА ДОЛЮ СЕКУНДЫ ---
|
||||
if is_instance_valid(dynamic_flash_sprite):
|
||||
dynamic_flash_sprite.rotation.z = randf_range(0, TAU) # Каждый раз разный угол картинки
|
||||
dynamic_flash_sprite.visible = true
|
||||
get_tree().create_timer(0.06).timeout.connect(func():
|
||||
if is_instance_valid(dynamic_flash_sprite):
|
||||
dynamic_flash_sprite.visible = false
|
||||
)
|
||||
|
||||
# --- ЗАПУСКАЕМ АНИМАЦИЮ ВЫСТРЕЛА ТЕЛА ---
|
||||
if has_node("AnimationPlayer"):
|
||||
var anim = get_node("AnimationPlayer")
|
||||
anim.stop()
|
||||
anim.play("ShootAnimPistol")
|
||||
|
||||
# --- НАНЕСЕНИЕ УРОНА ИГРОКУ ---
|
||||
var player_health = player_node.find_child("HealthComponent", true, false)
|
||||
if player_health and "take_damage" in player_health:
|
||||
var reduced_damage = damage_to_player * 0.8
|
||||
print("[ВРАГ] Бах! Вспышка отрисована. Нанесено урона: ", reduced_damage)
|
||||
player_health.take_damage(reduced_damage)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bgnhdyrrg72kx
|
||||
Reference in New Issue
Block a user