extends Node var player_a: AudioStreamPlayer var player_b: AudioStreamPlayer var current_player: AudioStreamPlayer var max_volume: float = -5.0 var min_volume: float = -80.0 var active_enemies: Array = [] var current_track: AudioStream = null # Словарь для сохранения позиции (секунды) каждого трека индивидуально var saved_positions: Dictionary = {} func _ready() -> void: # Инициализируем аудио-плееры player_a = AudioStreamPlayer.new() player_b = AudioStreamPlayer.new() add_child(player_a) add_child(player_b) # Принудительно подключаем к главному аудио-каналу Godot (Bus 0) if AudioServer.get_bus_count() > 0: player_a.bus = AudioServer.get_bus_name(0) player_b.bus = AudioServer.get_bus_name(0) player_a.volume_db = min_volume player_b.volume_db = min_volume current_player = player_a # Метод, вызываемый мобом, когда он ЗАМЕЧАЕТ игрока напрямую func register_enemy_vision(enemy: Node, track: AudioStream) -> void: if track == null: return if not active_enemies.has(enemy): active_enemies.append(enemy) # Включаем трек последнего заметившего нас врага switch_to_track(track) # Метод, вызываемый мобом, когда он ТЕРЯЕТ игрока или УМИРАЕТ func unregister_enemy_vision(enemy: Node, track: AudioStream) -> void: if active_enemies.has(enemy): active_enemies.erase(enemy) if active_enemies.size() == 0: fade_out_current() else: # Если остались другие зоркие враги, включаем трек последнего активного моба var remaining_enemy = active_enemies[-1] if remaining_enemy and "custom_battle_music" in remaining_enemy and remaining_enemy.custom_battle_music: switch_to_track(remaining_enemy.custom_battle_music) # Логика бесшовного переключения (Crossfade) func switch_to_track(new_track: AudioStream) -> void: if current_track == new_track and current_player.playing: return # Запоминаем текущую секунду старого трека перед тем, как его приглушить if current_track != null and current_player.playing: saved_positions[current_track] = current_player.get_playback_position() current_track = new_track # Выбираем противоположный (свободный) плеер для кроссфейда var next_player = player_b if current_player == player_a else player_a next_player.stream = new_track # Принудительно включаем встроенное зацикливание трека в Godot 4 через код if "loop" in next_player.stream: next_player.stream.loop = true elif next_player.stream is AudioStreamOggVorbis: next_player.stream.loop = true # Выясняем, с какой секунды продолжить трек var start_pos = saved_positions.get(new_track, 0.0) next_player.play(start_pos) # Запускаем плавное выравнивание громкости плееров за 1 секунду var tween = create_tween().set_parallel(true) tween.tween_property(current_player, "volume_db", min_volume, 1.0) tween.tween_property(next_player, "volume_db", max_volume, 1.0) var old_player = current_player current_player = next_player # Полностью останавливаем старый плеер только после завершения затухания tween.chain().tween_callback(func(): old_player.stop()) print("[МУЗЫКА] Боевой режим! Трек запущен/продолжен с секунды: ", start_pos) # Логика плавного выключения при выходе из боя func fade_out_current() -> void: current_track = null var tween = create_tween() tween.tween_property(current_player, "volume_db", min_volume, 2.0) tween.tween_callback(func(): if active_enemies.size() == 0 and current_player.playing: if current_player.stream: saved_positions[current_player.stream] = current_player.get_playback_position() current_player.stop() print("[МУЗЫКА] Зона безопасна. Боевой трек поставлен на паузу.") )