полнценая система прогресаа и конца уровне с началом и концом

This commit is contained in:
2026-07-23 01:41:44 +07:00
parent 6c1667911f
commit 0d03c06854
17 changed files with 422 additions and 26 deletions
+27
View File
@@ -0,0 +1,27 @@
[gd_scene format=3 uid="uid://cxckcssgallbx"]
[ext_resource type="Script" uid="uid://dux8o5uwin1sy" path="res://addons/Misc/Scripts/level_trigger.gd" id="1_1g6t3"]
[ext_resource type="ArrayMesh" uid="uid://dflmd4037fdqa" path="res://addons/custmode/untitled.obj" id="1_tam0r"]
[sub_resource type="SphereShape3D" id="SphereShape3D_pmktt"]
[sub_resource type="SphereShape3D" id="SphereShape3D_a2m1y"]
radius = 6.0
[node name="LevelTrigger" type="StaticBody3D" unique_id=549172302]
script = ExtResource("1_1g6t3")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1715081297]
transform = Transform3D(6.355, 0, 0, 0, 6.355, 0, 0, 0, 6.355, 0, 0, 0)
shape = SubResource("SphereShape3D_pmktt")
[node name="MeshInstance3D" type="MeshInstance3D" parent="CollisionShape3D" unique_id=1423996988]
transform = Transform3D(0.06, 0, 0, 0, 0.06, 0, 0, 0, 0.06, 0, -0.47206923, 0)
mesh = ExtResource("1_tam0r")
[node name="DetectArea" type="Area3D" parent="." unique_id=1314234424]
collision_layer = 0
collision_mask = 15
[node name="CollisionShape3D" type="CollisionShape3D" parent="DetectArea" unique_id=1390638580]
shape = SubResource("SphereShape3D_a2m1y")
+57
View File
@@ -0,0 +1,57 @@
[gd_scene format=3 uid="uid://c1v246tyt6bng"]
[ext_resource type="Script" uid="uid://bojftnghiqd2g" path="res://addons/Misc/Scripts/results_screen.gd" id="1_esv6s"]
[node name="ResultsScreen" type="CanvasLayer" unique_id=2101110637]
layer = 1000000000
script = ExtResource("1_esv6s")
[node name="TitleLabel" type="Label" parent="." unique_id=864255331]
anchors_preset = -1
anchor_left = 0.586
anchor_top = 0.45200002
anchor_right = 0.586
anchor_bottom = 0.45200002
offset_left = -84.07202
offset_top = -113.89603
offset_right = 82.92798
offset_bottom = -90.89603
text = "УРОВЕНЬ ЗАВЕРШЕН"
[node name="KillsLabel" type="Label" parent="." unique_id=1676098116]
anchors_preset = -1
anchor_left = 0.56700003
anchor_top = 0.43300003
anchor_right = 0.56700003
anchor_bottom = 0.43300003
offset_left = -69.18402
offset_top = 62.415985
offset_right = 68.81598
offset_bottom = 85.415985
text = "Убито врагов: 0\"."
[node name="TimeLabel" type="Label" parent="." unique_id=466879962]
anchors_preset = -1
anchor_left = 0.60200006
anchor_top = 0.46500003
anchor_right = 0.60200006
anchor_bottom = 0.46500003
offset_left = -108.50409
offset_top = 84.67999
offset_right = 108.49591
offset_bottom = 107.67999
text = "Время прохождения: 00:00"
[node name="ContinueButton" type="Button" parent="." unique_id=1156988049]
anchors_preset = -1
anchor_left = 0.586
anchor_top = 0.5
anchor_right = 0.586
anchor_bottom = 0.5
offset_left = -92.07202
offset_top = 122.0
offset_right = 57.92798
offset_bottom = 180.0
text = "Далее"
[connection signal="pressed" from="ContinueButton" to="." method="_on_continue_button_pressed"]
+65
View File
@@ -0,0 +1,65 @@
extends Node
# Хранилище данных, которое живет между уровнями
var saved_health: float = 100.0
var saved_weapons: Array = []
var saved_weapon_index: int = 0
var saved_ammo_dict: Dictionary = {}
var is_transfer_ready: bool = false # Флаг, что у нас есть сохраненные данные
# --- ФУНКЦИЯ 1: СОХРАНЕНИЕ ДАННЫХ ПЕРЕД СМЕНОЙ СЦЕНЫ ---
func save_player_data(player: Node) -> void:
if player == null: return
# 1. Запоминаем Здоровье
var health_comp = player.find_child("HealthComponent", true, false)
if health_comp:
saved_health = health_comp.current_health
# 2. Запоминаем Оружие и Патроны
# Найди этот блок внутри save_player_data в GameTransfer.gd:
var weapon_manager = player.find_child("WeaponManager", true, false)
if weapon_manager:
saved_weapons = weapon_manager.weaponStack.duplicate()
saved_weapon_index = weapon_manager.weaponIndex
# ИСПРАВЛЕНО: Более глубокий поиск менеджера патронов, если автор спрятал его в детях
var ammo_manager = weapon_manager.find_child("AmmoManager", true, false)
if ammo_manager == null and "ammoManager" in weapon_manager:
ammo_manager = weapon_manager.ammoManager
if ammo_manager and "ammoDict" in ammo_manager:
saved_ammo_dict = ammo_manager.ammoDict.duplicate()
is_transfer_ready = true
print("[ПЕРЕНОС] Данные игрока успешно сохранены в кэш!")
# --- ФУНКЦИЯ 2: ВОССТАНОВЛЕНИЕ ДАННЫХ НА НОВОМ УРОВНЕ ---
func load_player_data(player: Node) -> void:
if player == null or not is_transfer_ready: return
# 1. Возвращаем Здоровье
var health_comp = player.find_child("HealthComponent", true, false)
if health_comp:
health_comp.current_health = saved_health
# Обновляем цифры на новом HUD
var health_ui = player.find_child("PlayerHealthUI", true, false)
if health_ui and health_ui.has_method("_process"):
health_ui.notification(NOTIFICATION_PROCESS)
# 2. Возвращаем Оружие и Патроны
var weapon_manager = player.find_child("WeaponManager", true, false)
if weapon_manager:
weapon_manager.weaponStack = saved_weapons.duplicate()
weapon_manager.weaponIndex = saved_weapon_index
if "ammoManager" in weapon_manager and weapon_manager.ammoManager:
weapon_manager.ammoManager.ammoDict = saved_ammo_dict.duplicate()
# Принудительно заставляем ассет достать правильное оружие из памяти
if weapon_manager.has_method("changeWeapon"):
weapon_manager.changeWeapon(saved_weapon_index)
print("[ПЕРЕНОС] Снаряжение и ХП успешно возвращены игроку на новом уровне!")
+1
View File
@@ -0,0 +1 @@
uid://dbnkrmxycgjrl
+30
View File
@@ -0,0 +1,30 @@
extends Node
var current_kills: int = 0
var level_time: float = 0.0
var is_timer_running: bool = false
func _ready() -> void:
start_level_tracking()
func _process(delta: float) -> void:
if is_timer_running:
level_time += delta
func start_level_tracking() -> void:
current_kills = 0
level_time = 0.0
is_timer_running = true
func stop_level_tracking() -> void:
is_timer_running = false
func register_kill() -> void:
current_kills += 1
print("[СТАТИСТИКА] Моб уничтожен! Всего убийств: ", current_kills)
# Функция для красивого форматирования времени (например, 01:23)
func get_formatted_time() -> String:
var minutes = int(level_time) / 60
var seconds = int(level_time) % 60
return "%02d:%02d" % [minutes, seconds]
+1
View File
@@ -0,0 +1 @@
uid://jbvxvnmbrhe8
+55
View File
@@ -0,0 +1,55 @@
extends StaticBody3D
@export_file("*.tscn") var next_scene_path: String = ""
var is_triggered: bool = false
var detect_area: Area3D = null
@export var results_screen_scene: PackedScene
func _ready() -> void:
detect_area = $DetectArea
func _physics_process(delta: float) -> void:
if is_triggered or detect_area == null:
return
var overlapping_stuff: Array = []
overlapping_stuff.append_array(detect_area.get_overlapping_bodies())
overlapping_stuff.append_array(detect_area.get_overlapping_areas())
for node in overlapping_stuff:
if node == self or node == detect_area or node is StaticBody3D or "CSG" in node.name:
continue
var is_real_player = node.name == "PlayerCharacter"
if not is_real_player and node.get_parent():
is_real_player = node.get_parent().name == "PlayerCharacter"
if is_real_player:
is_triggered = true
print("[УРОВЕНЬ] МГНОВЕННЫЙ ФИНИШ! Полностью блокирую камеру и открываю курсор...")
# Отключаем сам триггер
set_physics_process(false)
# Находим корневой узел игрока
var main_player = node if node.name == "PlayerCharacter" else node.get_parent()
# --- МГНОВЕННАЯ ЗАМОРОЗКА ИНПУТОВ КАМЕРЫ И ОРУЖИЯ ---
# Переводим игрока в режим PROCESS_MODE_DISABLED.
# Это мгновенно отключает у него функции _process, _physics_process и ВСЕ _input функции мыши!
# Камера намертво каменеет в ту же секунду, без необходимости кликать кнопками.
if main_player:
main_player.process_mode = Node.PROCESS_MODE_DISABLED
# ----------------------------------------------------
# Аппаратно освобождаем мышь для клика по кнопке "Далее"
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
# Спавним экран результатов в корень игры
if results_screen_scene:
var results_instance = results_screen_scene.instantiate()
get_tree().get_root().add_child(results_instance)
if results_instance.has_method("show_results"):
results_instance.show_results(next_scene_path)
return
+1
View File
@@ -0,0 +1 @@
uid://dux8o5uwin1sy
+79
View File
@@ -0,0 +1,79 @@
extends CanvasLayer
var target_scene_path: String = ""
func _ready() -> void:
# Ставим максимальный слой отрисовки, чтобы HUD игры не перекрывал меню
layer = 120
if has_node("Panel"):
$Panel.visible = true
print("[РЕЗУЛЬТАТЫ] Экран итогов успешно создан прямо на карте!")
func show_results(next_scene: String) -> void:
target_scene_path = next_scene
print("[РЕЗУЛЬТАТЫ] Данные получены. Путь к следующей сцене: ", target_scene_path)
# Останавливаем таймер уровня
if LevelStats:
LevelStats.stop_level_tracking()
# БРОНЕБОЙНОЕ ОБНОВЛЕНИЕ ТЕКСТА: Ищем узлы прямо в момент вызова функции
var kills_label = find_child("KillsLabel", true, false)
if kills_label == null: kills_label = find_child("kills_label", true, false)
if kills_label:
kills_label.text = "Убито врагов: " + str(LevelStats.current_kills)
print("[РЕЗУЛЬТАТЫ] Текст убийств обновлен: ", kills_label.text)
var time_label = find_child("TimeLabel", true, false)
if time_label == null: time_label = find_child("time_label", true, false)
if time_label:
time_label.text = "Время прохождения: " + LevelStats.get_formatted_time()
print("[РЕЗУЛЬТАТЫ] Текст времени обновлен: ", time_label.text)
# ПРИНУДИТЕЛЬНО РАЗРЕШАЕМ ДВИГАТЬ КУРСОР МЫШИ
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func _on_continue_button_pressed() -> void:
print("[РЕЗУЛЬТАТЫ] Кнопка 'Далее' зафиксирована! Запускаю плавное затемнение...")
if target_scene_path == "":
print("[РЕЗУЛЬТАТЫ] ОШИБКА: Путь к следующей сцене пустой!")
return
# --- НАШЕ КРИТИЧЕСКОЕ ДОБАВЛЕНИЕ: СОХРАНЯЕМ ИГРОКА ПЕРЕД УХОДОМ В ЗАТЕМНЕНИЕ ---
var player = get_tree().current_scene.find_child("PlayerCharacter", true, false)
if player and GameTransfer:
GameTransfer.save_player_data(player)
# --------------------------------------------------------------------------------
# Твой старый код затемнения и смены сцены...
var fade_rect = find_child("FadeRect", true, false)
if fade_rect and "visible" in fade_rect:
fade_rect.visible = true
fade_rect.modulate.a = 0.0
var tween = create_tween()
tween.tween_property(fade_rect, "modulate:a", 1.0, 1.0)
await tween.finished
_set_ui_elements_visible(false)
if LevelStats: LevelStats.start_level_tracking()
queue_free()
get_tree().change_scene_to_file(target_scene_path)
# Полностью замени функцию в самом конце results_screen.gd на этот код:
func _set_ui_elements_visible(is_visible: bool) -> void:
# Находим и переключаем надпись убийств
var k_label = find_child("KillsLabel", true, false)
if k_label == null: k_label = find_child("kills_label", true, false)
if k_label: k_label.visible = is_visible
# Находим и переключаем надпись времени
var t_label = find_child("TimeLabel", true, false)
if t_label == null: t_label = find_child("time_label", true, false)
if t_label: t_label.visible = is_visible
# Находим и переключаем кнопку продолжения
var c_button = find_child("ContinueButton", true, false)
if c_button == null: c_button = find_child("continue_button", true, false)
if c_button: c_button.visible = is_visible
@@ -0,0 +1 @@
uid://bojftnghiqd2g