2025-10-28 13:19:33 +01:00
using AppleHills.Core.Settings ;
using Bootstrap ;
using Core ;
using PuzzleS ;
2025-10-17 14:38:42 +02:00
using UnityEngine ;
2025-10-28 13:19:33 +01:00
using UnityEngine.Audio ;
using AppleHills.Core ;
using AppleHills.Core.Interfaces ;
2025-10-29 17:01:02 +01:00
using System.Collections.Generic ;
using AudioSourceEvents ;
using System ;
2025-10-17 14:38:42 +02:00
2025-10-28 13:19:33 +01:00
public class AudioManager : MonoBehaviour , IPausable
2025-10-17 14:38:42 +02:00
{
2025-10-28 13:19:33 +01:00
/// <summary>
/// Play all audio, just music or no audio at all when the game is paused.
/// </summary>
public enum PauseBehavior
2025-10-29 17:01:02 +01:00
{ PlayAllAudio , MusicOnly , NoAudio }
2025-10-28 13:19:33 +01:00
public PauseBehavior currentPauseBehavior ;
2025-10-29 17:01:02 +01:00
public AudioMixer audioMixer ;
2025-10-28 13:19:33 +01:00
private AudioListener _audioListener ;
2025-10-29 17:01:02 +01:00
public AppleAudioSource currentlyPlayingVO ;
2025-10-28 13:19:33 +01:00
private static AudioManager _instance ;
private GameObject _player ;
2025-10-29 17:01:02 +01:00
public List < AppleAudioSource > criticalVOSources ;
public List < AppleAudioSource > VOSources ;
public List < AppleAudioSource > musicSources ;
public List < AppleAudioSource > ambienceSources ;
public List < AppleAudioSource > SFXSources ;
private IAudioEventSource _eventSource ;
2025-10-30 14:17:47 +01:00
private bool wasInterrupted ;
2025-10-29 17:01:02 +01:00
2025-10-28 13:19:33 +01:00
/// <summary>
/// Singleton instance of the AudioManager.
/// </summary>
public static AudioManager Instance = > _instance ;
void Awake ( )
{
_instance = this ;
// Register for post-boot initialization
BootCompletionService . RegisterInitAction ( InitializePostBoot ) ;
GameManager . Instance . RegisterPausableComponent ( this ) ;
}
private void InitializePostBoot ( )
{
}
2025-10-17 14:38:42 +02:00
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start ( )
{
2025-10-28 13:19:33 +01:00
_player = QuickAccess . Instance . PlayerGameObject ;
2025-10-29 17:01:02 +01:00
_audioListener = QuickAccess . Instance . MainCamera . GetComponent < AudioListener > ( ) ;
foreach ( AppleAudioSource _audioSource in criticalVOSources )
{
Debug . Log ( "Found source: " + _audioSource . name ) ;
}
2025-10-28 13:19:33 +01:00
}
public void SetAudioPauseBehavior ( PauseBehavior newPauseBehavior )
{
switch ( newPauseBehavior )
{
case PauseBehavior . PlayAllAudio :
2025-10-29 17:01:02 +01:00
audioMixer . updateMode = AudioMixerUpdateMode . UnscaledTime ;
2025-10-28 13:19:33 +01:00
AudioListener . pause = false ;
break ;
case PauseBehavior . MusicOnly :
2025-10-29 17:01:02 +01:00
audioMixer . updateMode = AudioMixerUpdateMode . UnscaledTime ; break ;
2025-10-28 13:19:33 +01:00
//TODO: Pause all audio mixers except music mixer
case PauseBehavior . NoAudio :
2025-10-29 17:01:02 +01:00
audioMixer . updateMode = AudioMixerUpdateMode . Normal ;
2025-10-28 13:19:33 +01:00
AudioListener . pause = true ;
break ;
}
}
2025-10-29 17:01:02 +01:00
public LevelAudioObject GetCurrentLevelAudioObject ( )
{
Debug . Log ( "Audio objects: " + FindObjectsByType < LevelAudioObject > ( FindObjectsInactive . Include , FindObjectsSortMode . None ) . Length ) ;
if ( FindObjectsByType < LevelAudioObject > ( FindObjectsInactive . Include , FindObjectsSortMode . None ) . Length > 1 )
{
Debug . LogWarning ( "Warning! More than one LevelAudioObject in the level! Using the first one found" ) ;
return FindObjectsByType < LevelAudioObject > ( FindObjectsInactive . Include , FindObjectsSortMode . None ) [ 0 ] ;
}
if ( FindObjectsByType < LevelAudioObject > ( FindObjectsInactive . Include , FindObjectsSortMode . None ) . Length = = 0 )
{
Debug . LogWarning ( "Error! No LevelAudioObject found, AudioManager might not function properly!" ) ;
return null ;
}
else
return FindFirstObjectByType < LevelAudioObject > ( ) ;
}
2025-10-28 13:19:33 +01:00
public void Pause ( )
{
SetAudioPauseBehavior ( PauseBehavior . NoAudio ) ;
2025-10-17 14:38:42 +02:00
}
2025-10-28 13:19:33 +01:00
public void DoResume ( )
2025-10-20 13:57:38 +02:00
{
2025-10-28 13:19:33 +01:00
SetAudioPauseBehavior ( PauseBehavior . PlayAllAudio ) ;
2025-10-17 14:38:42 +02:00
}
2025-10-29 17:01:02 +01:00
public void RegisterNewAudioSource ( AppleAudioSource newAudioSource )
{
switch ( newAudioSource . audioSourceType )
{
case AppleAudioSource . AudioSourceType . CriticalVO :
criticalVOSources . Add ( newAudioSource ) ;
break ;
case AppleAudioSource . AudioSourceType . VO :
VOSources . Add ( newAudioSource ) ;
break ;
case AppleAudioSource . AudioSourceType . SFX :
SFXSources . Add ( newAudioSource ) ;
break ;
case AppleAudioSource . AudioSourceType . Ambience :
ambienceSources . Add ( newAudioSource ) ;
break ;
case AppleAudioSource . AudioSourceType . Music :
musicSources . Add ( newAudioSource ) ;
break ;
}
}
/// <summary>
/// Request playing a VO line. Returns true if whatever is playing is not critical, or weight of requested VO line is lower.
/// </summary>
public bool RequestPlayVO ( AppleAudioSource requestedAudioSource )
{
2025-10-30 14:17:47 +01:00
//Debug.Log($"[AUDIOMANAGER] CurrentVO source prio: {currentlyPlayingVO.sourcePriority}, clip prio: {currentlyPlayingVO.clipPriority} requested VO prio: {requestedAudioSource.sourcePriority}, clip prio: {clipPriority}");
// If nothing is playing, let the requested audio source play
2025-10-29 17:01:02 +01:00
if ( currentlyPlayingVO = = null )
{
2025-10-30 14:17:47 +01:00
SetupNewAudioSource ( requestedAudioSource ) ;
Debug . Log ( $"[AUDIOMANAGER] Playing {currentlyPlayingVO.name} as nothing is currently playing." ) ;
2025-10-29 17:01:02 +01:00
return true ;
}
2025-10-30 14:17:47 +01:00
// If the requested audio source is the same, interrupt and trigger it again
if ( currentlyPlayingVO = = requestedAudioSource )
2025-10-29 17:01:02 +01:00
{
2025-10-30 14:17:47 +01:00
InterruptAudioSource ( requestedAudioSource ) ;
SetupNewAudioSource ( requestedAudioSource ) ;
Debug . Log ( $"[AUDIOMANAGER] {currentlyPlayingVO.name} is the same as {requestedAudioSource.name}. Triggering it again." ) ;
2025-10-29 17:01:02 +01:00
return true ;
2025-10-30 14:17:47 +01:00
2025-10-29 17:01:02 +01:00
}
2025-10-30 14:17:47 +01:00
// if the currently playing audio source is not critical, interrupt it and play the requested audio source
if ( currentlyPlayingVO . audioSourceType ! = AppleAudioSource . AudioSourceType . CriticalVO )
{
InterruptAudioSource ( requestedAudioSource ) ;
SetupNewAudioSource ( requestedAudioSource ) ;
Debug . Log ( $"[AUDIOMANAGER] {currentlyPlayingVO.name} is not critical. Playing {requestedAudioSource.name} instead because it is critical." ) ;
return true ;
}
// If the requested audio source has the same priority as currently playing source, check the priority of the requested clip
if ( currentlyPlayingVO . audioSourceType = = AppleAudioSource . AudioSourceType . CriticalVO & & currentlyPlayingVO . sourcePriority = = requestedAudioSource . sourcePriority )
{
if ( currentlyPlayingVO . clipPriority > requestedAudioSource . clipPriority )
{
InterruptAudioSource ( requestedAudioSource ) ;
SetupNewAudioSource ( requestedAudioSource ) ;
Debug . Log ( $"[AUDIOMANAGER] Interrupted {currentlyPlayingVO.name} because it has same priority as {requestedAudioSource.name} but the requested clip has higher priority" ) ;
return true ;
}
else
{
return false ;
}
}
// If the requested audio source has higher priority than the currently playing source, interrupt the current source and let the requested one play
if ( currentlyPlayingVO . audioSourceType = = AppleAudioSource . AudioSourceType . CriticalVO & & currentlyPlayingVO . sourcePriority > requestedAudioSource . sourcePriority )
2025-10-29 17:01:02 +01:00
{
currentlyPlayingVO . InterruptAudio ( requestedAudioSource . name ) ;
2025-10-30 14:17:47 +01:00
Debug . Log ( $"[AUDIOMANAGER] Interrupted {currentlyPlayingVO.name} because {requestedAudioSource.name} has higher priority" ) ;
InterruptAudioSource ( requestedAudioSource ) ;
SetupNewAudioSource ( requestedAudioSource ) ;
2025-10-29 17:01:02 +01:00
return true ;
}
2025-10-30 14:17:47 +01:00
// If the requested audio source didn't clear any of the above cases, tell it to get rekt.
2025-10-29 17:01:02 +01:00
else
2025-10-30 14:17:47 +01:00
{
Debug . Log ( $"[AUDIOMANAGER] {currentlyPlayingVO.name} is still playing. {requestedAudioSource.name} has lower priority" ) ;
2025-10-29 17:01:02 +01:00
return false ;
}
}
private void OnApplicationQuit ( )
{
// TODO: Release the handles safely ReleaseAllHandles();
}
2025-10-30 14:17:47 +01:00
private void SetupNewAudioSource ( AppleAudioSource audioSource )
2025-10-29 17:01:02 +01:00
{
2025-10-30 14:17:47 +01:00
if ( audioSource . audioSource . resource = = null )
2025-10-29 17:01:02 +01:00
{
2025-10-30 14:17:47 +01:00
Debug . Log ( $"[AUDIOMANAGER] Faled to setup {audioSource.name}. Invalid resource" ) ;
2025-10-29 17:01:02 +01:00
}
else
{
2025-10-30 14:17:47 +01:00
currentlyPlayingVO = audioSource ;
_eventSource = audioSource . audioSource . RequestEventHandlers ( ) ;
2025-10-29 17:01:02 +01:00
_eventSource . AudioStopped + = OnAudioStopped ;
_eventSource . AudioStarted + = OnAudioStarted ;
}
}
private void OnAudioStopped ( object sender , EventArgs e )
{
2025-10-30 14:17:47 +01:00
if ( wasInterrupted )
{
ResetAudioSource ( ) ;
}
else
{
currentlyPlayingVO = null ;
ResetAudioSource ( ) ;
}
2025-10-29 17:01:02 +01:00
}
private void OnAudioStarted ( object sender , EventArgs e )
{
}
2025-10-30 14:17:47 +01:00
private void ResetAudioSource ( )
{
_eventSource . AudioStopped - = OnAudioStopped ;
_eventSource . AudioStarted - = OnAudioStarted ;
wasInterrupted = false ;
}
private void InterruptAudioSource ( AppleAudioSource newAudioSource )
{
wasInterrupted = true ;
currentlyPlayingVO . InterruptAudio ( newAudioSource . name ) ;
ResetAudioSource ( ) ;
currentlyPlayingVO = newAudioSource ;
}
2025-10-17 14:38:42 +02:00
}