using Godot; using MaxEffort.Code.Core; using MaxEffort.Code.Data; namespace MaxEffort.Code.Systems; [GlobalClass] public partial class SoundManager : Node { [Export] private SoundBank _bank; [Export] private int _poolSize = 8; private AudioStreamPlayer[] _sfxPool; private AudioStreamPlayer _strainPlayer; private AudioStreamPlayer _heartbeatPlayer; private AudioStreamPlayer _musicPlayer; private bool _isLifting = false; private float _currentLiftProgress = 0f; private int _masterBus; private int _musicBus; private int _sfxBus; public override void _Ready() { _masterBus = AudioServer.GetBusIndex("Master"); _musicBus = AudioServer.GetBusIndex("Music"); _sfxBus = AudioServer.GetBusIndex("Sfx"); InitializePool(); EventBus.OnLiftEffortApplied += HandleLiftEffort; EventBus.OnFocusRelease += HandleFocusRelease; EventBus.OnFocusChanged += OnFocusChanged; EventBus.OnLiftCompleted += HandleLiftComplete; EventBus.OnLiftProgress += OnLiftProgress; EventBus.OnHazardSpawned += OnOnHazardSpawned; EventBus.OnHazardResolved += OnHazardResolved; EventBus.OnCameraTrauma += OnCameraTrauma; PlayMusic(_bank.GameMusic); } public override void _ExitTree() { EventBus.OnLiftEffortApplied -= HandleLiftEffort; EventBus.OnFocusRelease -= HandleFocusRelease; EventBus.OnLiftCompleted -= HandleLiftComplete; EventBus.OnLiftProgress -= OnLiftProgress; EventBus.OnHazardSpawned -= OnOnHazardSpawned; EventBus.OnHazardResolved -= OnHazardResolved; EventBus.OnCameraTrauma -= OnCameraTrauma; } public void PlayMusic(AudioStream clip) { if (clip == null || _musicPlayer.Stream == clip) return; _musicPlayer.Stream = clip; _musicPlayer.Play(); } public void ToggleMasterMute(bool isMuted) { AudioServer.SetBusMute(_masterBus, isMuted); } public void ToggleMusicMute(bool isMuted) { AudioServer.SetBusMute(_musicBus, isMuted); } private void InitializePool() { _sfxPool = new AudioStreamPlayer[_poolSize]; for (var i = 0; i < _poolSize; i++) { var p = new AudioStreamPlayer(); p.Bus = "Sfx"; AddChild(p); _sfxPool[i] = p; } _strainPlayer = new AudioStreamPlayer { Bus = "Sfx" }; AddChild(_strainPlayer); _heartbeatPlayer = new AudioStreamPlayer { Bus = "Sfx" }; AddChild(_heartbeatPlayer); _musicPlayer = new AudioStreamPlayer { Bus = "Music" }; AddChild(_musicPlayer); if (_bank != null && _bank.HeartbeatLoop != null) { _heartbeatPlayer.Stream = _bank.HeartbeatLoop; _heartbeatPlayer.VolumeDb = -80f; _heartbeatPlayer.Play(); } } private void PlaySfx(AudioStream clip, float pitch = 1.0f) { if (clip == null) return; AudioStreamPlayer bestCandidate = null; var longestPlaybackPosition = -1f; foreach (var player in _sfxPool) { if (!player.Playing) { bestCandidate = player; break; } if (player.GetPlaybackPosition() > longestPlaybackPosition) { longestPlaybackPosition = player.GetPlaybackPosition(); bestCandidate = player; } } if (bestCandidate != null) { bestCandidate.Stream = clip; bestCandidate.PitchScale = pitch; bestCandidate.Play(); } } private void HandleLiftEffort(float _) { if (!_isLifting) { _isLifting = true; if (_bank.StrainLoop != null) { _strainPlayer.Stream = _bank.StrainLoop; _strainPlayer.Play(); } } _strainPlayer.PitchScale = 1.0f + (_currentLiftProgress * 0.3f); } private void HandleFocusRelease() { if (_isLifting) { _isLifting = false; _strainPlayer.Stop(); PlaySfx(_bank.EffortExhale); } } private void HandleLiftComplete(bool success) { _strainPlayer.Stop(); PlaySfx(success ? _bank.WinStinger : _bank.FailStinger); } private void OnCameraTrauma(float amount) { if (amount > 0.5f) PlaySfx(_bank.CameraTrauma); } private void OnHazardResolved(HazardType _) { PlaySfx(_bank.HazardClear); } private void OnOnHazardSpawned(HazardType _) { PlaySfx(_bank.HazardSpawn); } private void OnLiftProgress(float progress) { _currentLiftProgress = progress; } private void OnFocusChanged(float focus) { if (focus < 0.1f) { _heartbeatPlayer.VolumeDb = -80f; } else { var t = (focus - 0.1f) / 0.9f; _heartbeatPlayer.VolumeDb = Mathf.Lerp(-30f, 5f, t); _heartbeatPlayer.PitchScale = Mathf.Lerp(1.0f, 1.4f, t); } } }