Explorar el Código

First stable release

DigitalzombieTLD hace 3 años
padre
commit
a27088f4ab

+ 1 - 0
.gitignore

@@ -348,3 +348,4 @@ MigrationBackup/
 
 # Ionide (cross platform F# VS Code tools) working folder
 .ionide/
+/.editorconfig

+ 37 - 0
AudioMain.cs

@@ -0,0 +1,37 @@
+using MelonLoader;
+using Il2CppInterop.Runtime.Injection;
+
+namespace AudioMgr
+{
+    public class AudioMain : MelonMod
+	{
+        bool initialized = false;           
+
+        public override void OnInitializeMelon() 
+		{
+            ClassInjector.RegisterTypeInIl2Cpp<Shot>();
+            ClassInjector.RegisterTypeInIl2Cpp<Queue>();
+        }
+
+        public override void OnSceneWasLoaded(int buildIndex, string sceneName)
+        {
+            if (sceneName.Contains("Boot"))
+            {
+                AudioMaster.CreateMasterParent();
+            }
+
+            if (sceneName.Contains("Menu"))
+            {
+                initialized = true;
+            }
+        }
+
+        public override void OnFixedUpdate()
+        {
+            if (initialized)
+            {
+                AudioMaster.MoveMasterToPlayer();
+            }
+        }        
+	}
+}

+ 83 - 0
AudioManager.csproj

@@ -0,0 +1,83 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <!--This is an xml comment. Comments have no impact on compiling.-->
+
+    <PropertyGroup>
+        <!--This needs to be changed for the mod to compile.-->
+        <TheLongDarkPath>G:\Games\Steam\steamapps\common\TheLongDark</TheLongDarkPath>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <!--This is the .NET version the mod will be compiled with. Don't change it.-->
+        <TargetFramework>net6.0</TargetFramework>
+
+        <!--This tells the compiler to use the latest C# version.-->
+        <LangVersion>Latest</LangVersion>
+
+        <!--This adds global usings for a few common System namespaces.-->
+        <ImplicitUsings>enable</ImplicitUsings>
+
+        <!--This enables nullable annotation and analysis. It's good coding form.-->
+        <Nullable>enable</Nullable>
+
+        <!--This tells the compiler to use assembly attributes instead of generating its own.-->
+        <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+
+        <!--PDB files are mostly useless for modding since they can't be loaded.-->
+        <DebugType>none</DebugType>
+    </PropertyGroup>
+
+    <!--This tells the compiler where to look for assemblies. Don't change it.-->
+    <PropertyGroup>
+        <MelonLoaderPath>$(TheLongDarkPath)/MelonLoader/net6</MelonLoaderPath>
+        <ManagedPath>$(TheLongDarkPath)/MelonLoader/Managed</ManagedPath>
+        <Il2CppPath>$(TheLongDarkPath)/MelonLoader/Il2CppAssemblies</Il2CppPath>
+        <ModsPath>$(TheLongDarkPath)/Mods</ModsPath>
+        <AssemblySearchPaths>$(AssemblySearchPaths);$(MelonLoaderPath);$(ManagedPath);$(Il2CppPath);$(ModsPath);</AssemblySearchPaths>
+        <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
+        <RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
+        <OutputType>Library</OutputType>
+        <GenerateDocumentationFile>True</GenerateDocumentationFile>
+    </PropertyGroup>
+
+    <!--This tells the compiler to not include referenced assemblies in the output folder.-->
+    <ItemDefinitionGroup>
+        <Reference>
+            <Private>False</Private>
+        </Reference>
+    </ItemDefinitionGroup>
+
+    <!--This is the list of assemblies that the mod references. Most of these are unnecessary for normal mods, but are included here for completeness.-->
+    <ItemGroup>
+        <Reference Include="MelonLoader" />
+        <Reference Include="0Harmony" />
+        <Reference Include="Il2CppInterop.Common" />
+        <Reference Include="Il2CppInterop.Runtime" />
+        <Reference Include="Assembly-CSharp-firstpass" />
+        <Reference Include="Assembly-CSharp" />
+        <Reference Include="Il2CppMono.Security" />
+        <Reference Include="Il2Cppmscorlib" />
+        <Reference Include="Il2CppSystem.Configuration" />
+        <Reference Include="Il2CppSystem.Core" />
+        <Reference Include="Il2CppSystem.Data" />
+        <Reference Include="Il2CppSystem" />
+        <Reference Include="UnityEngine.AnimationModule" />
+        <Reference Include="UnityEngine.AssetBundleModule" />
+        <Reference Include="UnityEngine.AudioModule" />
+        <Reference Include="UnityEngine.CoreModule" />
+        <Reference Include="UnityEngine" />
+        <Reference Include="UnityEngine.DSPGraphModule" />
+        <Reference Include="UnityEngine.InputLegacyModule" />
+        <Reference Include="UnityEngine.InputModule" />
+        <Reference Include="UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule" />
+        <Reference Include="UnityEngine.SharedInternalsModule" />
+        <Reference Include="UnityEngine.StreamingModule" />
+        <Reference Include="UnityEngine.SubsystemsModule" />
+        <Reference Include="UnityEngine.UnityWebRequestAssetBundleModule" />
+        <Reference Include="UnityEngine.UnityWebRequestAudioModule" />
+        <Reference Include="UnityEngine.UnityWebRequestModule" />
+        <Reference Include="UnityEngine.UnityWebRequestTextureModule" />
+        <Reference Include="UnityEngine.UnityWebRequestWWWModule" />
+        <Reference Include="UnityEngine.Il2CppAssetBundleManager" />
+        <Reference Include="UnityEngine.Il2CppImageConversionManager" />
+    </ItemGroup>
+</Project>

+ 30 - 0
AudioManager.sln

@@ -0,0 +1,30 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33205.214
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AudioManager", "AudioManager.csproj", "{595ABC4B-CAD8-4DFA-8A72-0DAE056EF193}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8E34E0B9-E6EF-4208-AB74-199400BC300E}"
+	ProjectSection(SolutionItems) = preProject
+		.editorconfig = .editorconfig
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{595ABC4B-CAD8-4DFA-8A72-0DAE056EF193}.Debug|Any CPU.ActiveCfg = Release|Any CPU
+		{595ABC4B-CAD8-4DFA-8A72-0DAE056EF193}.Debug|Any CPU.Build.0 = Release|Any CPU
+		{595ABC4B-CAD8-4DFA-8A72-0DAE056EF193}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{595ABC4B-CAD8-4DFA-8A72-0DAE056EF193}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {AC532BEA-FEFD-4E3E-B2B7-4DDB6CE3932F}
+	EndGlobalSection
+EndGlobal

+ 307 - 0
Components/Queue.cs

@@ -0,0 +1,307 @@
+using Il2CppInterop.Runtime.Attributes;
+using MelonLoader;
+using System.Collections;
+using UnityEngine;
+
+namespace AudioMgr
+{
+    public class Queue : MonoBehaviour
+    {
+        public Queue(IntPtr intPtr) : base(intPtr)
+        {
+        }
+
+        private Dictionary<bool, AudioSource> _audioSources;
+        private bool _toggleAudioSource = true;
+
+        private Setting _activeSetting;
+        private AudioMaster.SourceType _sourceType;
+
+        private ClipManager _assignedClipManager;
+        private float _timeGap = 2f;
+        private float _lowerRandomGap = 0;
+        private float _upperRandomGap = 2;
+        private bool _randomGap = false;
+
+        private int _currentClipIndex = 0;
+
+        private object _timerLoop;
+
+        public enum Loop { None, Single, All, Randomize };
+        private Loop _loop = Loop.All;
+
+        public enum PlayState { Stopped, Playing, Paused };
+        private PlayState _playState = PlayState.Stopped;
+
+
+        [HideFromIl2Cpp]
+        public void Setup(ClipManager assignedClipManager, float timeGap, Loop loopType, AudioMaster.SourceType sourceType)
+        {
+            _assignedClipManager = assignedClipManager;
+            _timeGap = timeGap;
+            _loop = loopType;
+            _audioSources = new Dictionary<bool, AudioSource>();
+            _audioSources.Add(true, gameObject.AddComponent<AudioSource>());
+            _audioSources.Add(false, gameObject.AddComponent<AudioSource>());
+
+            _audioSources[true].playOnAwake = false;
+            _audioSources[false].playOnAwake = false;
+
+            _audioSources[true].volume = VolumeMaster.GetVolume(sourceType);
+            _audioSources[false].volume = VolumeMaster.GetVolume(sourceType);
+
+            VolumeMaster.onVolumeChange += ResetVolume;
+
+            ApplySettings(SettingMaster.Defaults(sourceType));
+        }
+
+        [HideFromIl2Cpp]
+        public int GetNextClip()
+        {
+            if (_loop == Loop.Single) // Keep current clip
+            {
+                return _currentClipIndex;
+            }
+            else if (_loop == Loop.Randomize)
+            {
+                int randomIndex = _currentClipIndex;
+
+                while (randomIndex == _currentClipIndex)
+                {
+                    randomIndex = UnityEngine.Random.Range(0, _assignedClipManager.clipCount - 1);
+                }
+
+                return randomIndex;
+            }
+            else if (_currentClipIndex < _assignedClipManager.clipCount - 1) // Not at the end yet, increase by 1
+            {
+                return _currentClipIndex + 1;
+            }
+            else if (_currentClipIndex >= _assignedClipManager.clipCount - 1)
+            {
+                if (_loop == Loop.All)
+                {
+                    return 0;
+                }
+                else
+                {
+                    return -1;
+                }
+            }
+
+            return -1;
+        }
+
+        [HideFromIl2Cpp]
+        public void SetRandomTimeGap(float lowestTimeGap, float highestTimeGap)
+        {
+            _randomGap = true;
+            _lowerRandomGap = lowestTimeGap;
+            _upperRandomGap = highestTimeGap;  
+        }
+
+        public void SetFixedTimeGap(float timeGap)
+        {
+            _randomGap = false;
+            _timeGap = timeGap;
+        }
+
+        [HideFromIl2Cpp]
+        public void Play()
+        {
+            if (_playState == PlayState.Stopped && _assignedClipManager.clipCount > 0)
+            {
+                _playState = PlayState.Playing;
+                _timerLoop = MelonCoroutines.Start(TimerLoop());
+            }
+            else if (_playState == PlayState.Paused)
+            {
+                AudioListener.pause = false;
+                _playState = PlayState.Playing;
+            }
+        }
+
+        [HideFromIl2Cpp]
+        public void Stop()
+        {
+            if (_timerLoop != null)
+            {
+                MelonCoroutines.Stop(_timerLoop);
+                _playState = PlayState.Stopped;
+                _audioSources[true].Stop();
+                _audioSources[false].Stop();
+            }
+        }
+
+
+        [HideFromIl2Cpp]
+        private IEnumerator TimerLoop()
+        {
+            double _startTime;
+            double _timeToNext = 0;
+            int _nextClip = 0;
+                      
+            _startTime = AudioSettings.dspTime + 0.5;
+
+            if (_randomGap)
+            {
+                _timeGap = UnityEngine.Random.Range(_lowerRandomGap, _upperRandomGap);
+            }
+
+            _audioSources[_toggleAudioSource].clip = _assignedClipManager.GetClipAtIndex(_currentClipIndex).audioClip;
+            _audioSources[_toggleAudioSource].PlayScheduled(_startTime + _timeGap);
+
+            _timeToNext = _startTime;
+
+            while (true)
+            {
+                if (_playState == PlayState.Playing)
+                {                    
+                    // Assign new clip                
+                    _nextClip = GetNextClip();
+
+                    if (_nextClip < 0)
+                    {
+                        yield break;
+                    }
+
+                    if(_randomGap)
+                    {
+                        _timeGap = UnityEngine.Random.Range(_lowerRandomGap, _upperRandomGap);
+                    }
+
+                    _toggleAudioSource = !_toggleAudioSource;
+
+                    _timeToNext = _timeToNext + _assignedClipManager.GetClipAtIndex(_currentClipIndex).clipLength + _timeGap;
+
+                    _audioSources[_toggleAudioSource].clip = _assignedClipManager.GetClipAtIndex(_nextClip).audioClip;
+                    _audioSources[_toggleAudioSource].PlayScheduled(_timeToNext);
+
+                    while (AudioSettings.dspTime < _timeToNext + 0.05)
+                    {
+                        yield return null;
+                    }                
+                    _currentClipIndex = _nextClip;
+                }
+                yield return null;
+            }            
+        }
+
+        [HideFromIl2Cpp]
+        public void Settings(ClipManager assignedClipManager, float timeGap, Loop loopType)
+        {
+            _assignedClipManager = assignedClipManager;
+            _timeGap = timeGap;
+            _loop = loopType;
+            _currentClipIndex = 0;
+        }
+       
+
+        [HideFromIl2Cpp]
+        private void OnEnable()
+        {
+            VolumeMaster.onVolumeChange += ResetVolume;         
+        }
+
+        [HideFromIl2Cpp]
+        private void OnDisable()
+        {
+            VolumeMaster.onVolumeChange -= ResetVolume;
+        }
+
+        [HideFromIl2Cpp]
+        private void OnDestroy()
+        {
+            VolumeMaster.onVolumeChange -= ResetVolume;
+        }
+
+        [HideFromIl2Cpp]
+        private void ResetVolume()
+        {
+            if(_sourceType != AudioMaster.SourceType.Custom)
+            {
+                _audioSources[_toggleAudioSource].volume = VolumeMaster.GetVolume(_sourceType);
+                _audioSources[!_toggleAudioSource].volume = VolumeMaster.GetVolume(_sourceType);
+            }            
+        }
+
+        [HideFromIl2Cpp]
+        public void ApplySettings(Setting newSetting)
+        {
+            ApplySettingsToSingle(newSetting, true);
+            ApplySettingsToSingle(newSetting, false);
+        }
+
+        [HideFromIl2Cpp]
+        private void ApplySettingsToSingle(Setting newSetting, bool audioSource)
+        {
+            _activeSetting = newSetting;
+
+            _sourceType = _activeSetting.sourceType;
+            _audioSources[audioSource].spread = _activeSetting.spread;
+            _audioSources[audioSource].panStereo = _activeSetting.panStereo;
+            _audioSources[audioSource].dopplerLevel = _activeSetting.dopplerLevel;
+            _audioSources[audioSource].maxDistance = _activeSetting.maxDistance;
+            _audioSources[audioSource].minDistance = _activeSetting.minDistance;
+            _audioSources[audioSource].pitch = _activeSetting.pitch;
+            _audioSources[audioSource].spatialBlend = _activeSetting.spatialBlend;
+            _audioSources[audioSource].spatialize = _activeSetting.spatialize;
+            //_audioSources[audioSource].rolloffFactor = _activeSetting.rolloffFactor;
+            _audioSources[audioSource].rolloffMode = _activeSetting.rolloffMode;
+            //_audioSources[audioSource].SetCustomCurve(AudioSourceCurveType.CustomRolloff, _activeSetting.rollOffCurve);
+
+            ResetVolume();
+        }
+
+        [HideFromIl2Cpp]
+        public Loop loop
+        {
+            get
+            {
+                return _loop;
+            }
+            set
+            {
+                _loop = value;
+            }
+        }
+
+        [HideFromIl2Cpp]
+        public float timeGap
+        {
+            get
+            {
+                return _timeGap;
+            }
+            set
+            {
+                _timeGap = value;
+            }
+        }
+
+        [HideFromIl2Cpp]
+        public PlayState playState
+        {
+            get
+            {
+                return _playState;
+            }
+        }
+
+        [HideFromIl2Cpp]
+        public AudioMaster.SourceType sourceType
+        {
+            get
+            {
+                return _sourceType;
+            }
+            set
+            {
+                _sourceType = value;
+                ApplySettings(SettingMaster.Defaults(_sourceType));
+            }
+        }
+        
+       
+    }
+}

+ 155 - 0
Components/Shot.cs

@@ -0,0 +1,155 @@
+using Il2Cpp;
+using Il2CppInterop.Runtime.Attributes;
+using MelonLoader;
+using System.Collections;
+using UnityEngine;
+
+namespace AudioMgr
+{
+    public class Shot : MonoBehaviour
+    {
+        public Shot(IntPtr intPtr) : base(intPtr)
+        {
+        }
+
+        private AudioSource _audioSource;
+        private Setting _activeSetting;
+
+        private bool _isEnabled = false;
+       
+        private AudioMaster.SourceType _sourceType;
+
+        [HideFromIl2Cpp]
+        public void Setup(AudioMaster.SourceType sourceType)
+        {
+            _audioSource = gameObject.AddComponent<AudioSource>();
+            _audioSource.playOnAwake = false;
+            _audioSource.volume = VolumeMaster.GetVolume(sourceType);
+            VolumeMaster.onVolumeChange += ResetVolume;
+            ApplySettings(SettingMaster.Defaults(sourceType));
+        }
+
+        [HideFromIl2Cpp]
+        private void OnEnable()
+        {
+            VolumeMaster.onVolumeChange += ResetVolume;
+        }
+
+        [HideFromIl2Cpp]
+        private void OnDisable()
+        {
+            Stop();
+            VolumeMaster.onVolumeChange -= ResetVolume;
+        }
+
+        [HideFromIl2Cpp]
+        private void OnDestroy()
+        {
+            VolumeMaster.onVolumeChange -= ResetVolume;
+        }
+
+        [HideFromIl2Cpp]
+        public void SetVolume(float newVolume)
+        {
+            if (_sourceType == AudioMaster.SourceType.Custom)
+            {
+                _audioSource.volume = newVolume;
+            }
+        }
+
+        [HideFromIl2Cpp]
+        public void AssignClip(Clip audioClip)
+        {
+            Stop();
+            _audioSource.clip = audioClip.audioClip;            
+        }
+
+        [HideFromIl2Cpp]
+        public void Disable()
+        {                     
+            OnDisable();
+        }
+
+        [HideFromIl2Cpp]
+        public void Enable()
+        {
+            OnEnable();
+        }
+
+        [HideFromIl2Cpp]
+        public void Pause()
+        {
+            _audioSource.Pause();
+        }
+
+        [HideFromIl2Cpp]
+        public void Resume()
+        {
+            _audioSource.UnPause();           
+        }
+
+        [HideFromIl2Cpp]
+        public void Play()
+        {
+            _audioSource.Play();
+        }
+
+        [HideFromIl2Cpp]
+        public void Stop()
+        {
+            _audioSource.Stop();
+        }
+
+        [HideFromIl2Cpp]
+        public void ResetVolume()
+        {
+            if(_sourceType != AudioMaster.SourceType.Custom)
+            {
+                _audioSource.volume = VolumeMaster.GetVolume(_sourceType);
+            }            
+        }
+
+        [HideFromIl2Cpp]
+        public void Play(Clip audioClip)
+        {           
+            MelonCoroutines.Start(PlayOneshotRoutine(audioClip));
+        }
+
+        [HideFromIl2Cpp]
+        private IEnumerator PlayOneshotRoutine(Clip audioClip)
+        {            
+            _audioSource.PlayOneShot(audioClip.audioClip);
+            yield return null;
+        }
+
+        [HideFromIl2Cpp]
+        public void ApplySettings(Setting newSetting)
+        {
+            _activeSetting = newSetting;
+
+            _sourceType = _activeSetting.sourceType;
+            _audioSource.spread = _activeSetting.spread;
+            _audioSource.panStereo = _activeSetting.panStereo;
+            _audioSource.dopplerLevel = _activeSetting.dopplerLevel;
+            _audioSource.maxDistance = _activeSetting.maxDistance;
+            _audioSource.minDistance = _activeSetting.minDistance;
+            _audioSource.pitch = _activeSetting.pitch;
+            _audioSource.spatialBlend = _activeSetting.spatialBlend;
+            _audioSource.spatialize = _activeSetting.spatialize;
+            //_audioSource.rolloffFactor = _activeSetting.rolloffFactor;
+            _audioSource.rolloffMode = _activeSetting.rolloffMode;
+            _audioSource.priority = _activeSetting.priority;
+
+            ResetVolume();
+        }
+
+        [HideFromIl2Cpp]
+        public AudioMaster.SourceType sourceType
+        {
+            get
+            {
+                return _sourceType;
+            }
+        }
+    }
+}

+ 31 - 0
Harmony/MusicPatches.cs

@@ -0,0 +1,31 @@
+using Il2Cpp;
+using UnityEngine;
+
+namespace AudioMgr
+{
+    [HarmonyLib.HarmonyPatch(typeof(GameAudioManager), "PlayMusic", new Type[] {typeof(string), typeof(GameObject)})]
+    public class PlayMusicStringPatch
+    {
+        public static bool Prefix(ref GameAudioManager __instance, string soundID, GameObject go)
+        {
+            if (AudioMaster.vanillaMusicDisabled)
+            {
+                return false;                
+            }
+            return true;
+        }
+    }
+
+    [HarmonyLib.HarmonyPatch(typeof(GameAudioManager), "PlayMusic", new Type[] {typeof(uint), typeof(GameObject)})]
+    public class PlayMusicUIntPatch
+    {
+        public static bool Prefix(ref GameAudioManager __instance, uint soundID, GameObject go)
+        {
+            if (AudioMaster.vanillaMusicDisabled)
+            {
+                return false;
+            }
+            return true;
+        }
+    }
+}

+ 22 - 0
Harmony/VolumePatches.cs

@@ -0,0 +1,22 @@
+using Il2Cpp;
+using UnityEngine;
+
+namespace AudioMgr
+{    
+    [HarmonyLib.HarmonyPatch(typeof(GameAudioManager), "SetRTPCValue")]
+    public class VolumeChanged
+    {
+        public static void Prefix(ref GameAudioManager __instance, ref uint rtpcID, ref float rtpcValue, ref GameObject go)
+        {
+            if (VolumeIDs.GetRtpcIDMaster() == rtpcID)
+            {
+                VolumeMaster.SetMasterVolume(rtpcValue/100);
+                
+            }            
+            else if (VolumeIDs.GetRtpcIDList().ContainsKey(rtpcID)) // Set sfx/voice/ambient/bgm
+            {
+                VolumeMaster.SetVolume(VolumeIDs.GetRtpcIDList()[rtpcID], rtpcValue/100);
+            }           
+        }
+    }    
+}

+ 32 - 0
LookupTables/VolumeIDs.cs

@@ -0,0 +1,32 @@
+namespace AudioMgr
+{
+    public static class VolumeIDs
+    {
+        private static Dictionary<uint, AudioMaster.SourceType> _volumeRtpcID = new Dictionary<uint, AudioMaster.SourceType>() 
+        {
+            { 2346531308U, AudioMaster.SourceType.BGM}, // uint MUSICVOLUME = 2346531308U;
+            { 3325181466U, AudioMaster.SourceType.Voice}, // uint VOVOLUME = 3325181466U;
+            { 3546521921U, AudioMaster.SourceType.Ambience}, // uint AMBIENTVOLUME = 3546521921U;
+            { 988953028U, AudioMaster.SourceType.SFX}, // uint SFXVOLUME = 988953028U;
+        };
+
+        private static uint _masterVolume = 2918011349U;  // uint MASTERVOLUME = 2918011349U;
+        private static uint _globalVolume = 4071000082U;  // uint GLOBALVOLUME = 4071000082U;
+
+        public static uint GetRtpcIDMaster()
+        {
+            return _globalVolume;
+        }
+
+        public static uint GetRtpcGlobal()
+        {
+            return _globalVolume;
+        }
+
+        public static Dictionary<uint, AudioMaster.SourceType> GetRtpcIDList()
+        {
+            return _volumeRtpcID;
+        }
+
+    }
+}

+ 193 - 0
Manager/ClipManager.cs

@@ -0,0 +1,193 @@
+using Il2Cpp;
+using MelonLoader;
+using System.Collections;
+using UnityEngine;
+using UnityEngine.Networking;
+
+namespace AudioMgr
+{
+    public class ClipManager
+    {
+        private Dictionary<string, Clip> _loadedClips = new Dictionary<string, Clip>();
+        public enum LoadType { Compressed, Decompressed, Stream };
+        private string downloaderPath = @"file:///" + Application.dataPath + @"/../Mods";
+
+        public Clip GetClip(string clipName)
+        {
+            if (_loadedClips.ContainsKey(clipName))
+            {
+                return _loadedClips[clipName];
+            }
+            else
+            {
+                MelonLogger.Msg("Warning: Trying to retrieve non-existent audioclip " + clipName);
+                return null;
+            }
+        }
+
+        public Clip GetClipAtIndex(int index)
+        {
+            if (_loadedClips.ElementAt(index).Key != null)
+            {
+                return _loadedClips.ElementAt(index).Value;
+            }
+            else
+            {
+                MelonLogger.Msg("Warning: Trying to retrieve non-existent audioclip at index " + index);
+                return null;
+            }
+        }
+
+        public void UnloadClip(string clipName)
+        {
+            if (_loadedClips.ContainsKey(clipName))
+            {
+                _loadedClips[clipName].Unload();
+                _loadedClips.Remove(clipName);
+            }
+        }
+
+        public void UnloadAllClips()
+        {
+            foreach (KeyValuePair<string, Clip> singleClip in _loadedClips)
+            {
+                singleClip.Value.Unload();
+                _loadedClips.Remove(singleClip.Key);
+            }
+        }
+
+        public void LoadClipFromBundle(string newClipName, string clipInBundle, AssetBundle assetBundle)
+        {
+            if (_loadedClips.ContainsKey(newClipName))
+            {
+                return;
+            }
+
+            MelonCoroutines.Start(LoadClipFromBundleRoutine(newClipName, clipInBundle, assetBundle));
+        }
+
+        public void LoadAllClipsFromBundle(AssetBundle assetBundle)
+        {
+            string[] clipNamesInBundle = assetBundle.AllAssetNames();
+
+            foreach(string singleName in clipNamesInBundle)
+            {
+                if(singleName.Contains(".ogg") || singleName.Contains(".wav") || singleName.Contains(".mp3"))
+                {
+                    string[] clipNameSplit;
+                    string tmpClipName;
+
+                    clipNameSplit = singleName.Split('/');
+                    tmpClipName = clipNameSplit[clipNameSplit.Length-1];
+
+                    if (!_loadedClips.ContainsKey(tmpClipName))
+                    {
+                        LoadClipFromBundle(FileNameCutter(tmpClipName), singleName, assetBundle);
+                    }                    
+                }
+            }
+        }
+
+        private IEnumerator LoadClipFromBundleRoutine(string newClipName, string clipInBundle, AssetBundle assetBundle)
+        {
+            _loadedClips.Add(newClipName, new Clip(assetBundle.LoadAsset<AudioClip>(clipInBundle), newClipName));
+            yield break;
+        }
+
+        public void LoadClipsFromDir(string directory, LoadType loadType)
+        {            
+            string[] allTheFilesInDir = Directory.GetFiles(Application.dataPath + "/../Mods/" + directory, "*", SearchOption.TopDirectoryOnly);
+
+            foreach(string singleFile in allTheFilesInDir)
+            {
+                LoadClipFromFile(FileNameCutter(ClipStringCutter(singleFile)), directory + "/" + ClipStringCutter(singleFile), loadType);
+            }
+        }
+
+        public void LoadClipFromFile(string newClipName, string fileName, LoadType loadType)
+        {
+            if (_loadedClips.ContainsKey(newClipName))
+            {
+                return;
+            }
+
+            MelonCoroutines.Start(LoadClipFromFileRoutine(newClipName, fileName, loadType));
+        }
+
+        private IEnumerator LoadClipFromFileRoutine(string clipName, string fileName, LoadType loadType)
+        {
+            bool compressed = true;
+            bool stream = false;
+
+            if (loadType == LoadType.Compressed)
+            {
+                compressed = true;
+                stream = false;
+            }
+
+            if (loadType == LoadType.Decompressed)
+            {
+                compressed = false;
+                stream = false;
+            }
+
+            if (loadType == LoadType.Stream)
+            {
+                compressed = false;
+                stream = true;
+            }
+
+            _loadedClips.Add(clipName, null);
+
+            UnityWebRequest www;
+            www = UnityWebRequest.Get(downloaderPath + @"/" + fileName);
+            www.SendWebRequest();
+
+            while (!www.isDone) yield return null;
+
+            if (!www.isNetworkError && !www.isHttpError)
+            {
+                _loadedClips[clipName] = new Clip(WebRequestWWW.InternalCreateAudioClipUsingDH(www.downloadHandler, www.url, stream, compressed, AudioType.UNKNOWN), clipName);
+                
+
+            }
+            else
+            {
+                _loadedClips.Remove(clipName);
+                MelonLogger.Msg("Error while loading audioclip. Skipping " + www.error);
+            }
+        }
+
+        private string ClipStringCutter(string stringToCut)
+        {
+            string[] clipNameSplit;
+            string tmpClipName;
+
+            clipNameSplit = stringToCut.Split('\\');
+      
+            tmpClipName = clipNameSplit[clipNameSplit.Length-1];
+
+            return (tmpClipName);
+        }
+
+        private string FileNameCutter(string stringToCut)
+        {
+            string[] clipNameSplit;
+            string tmpClipName;
+
+            clipNameSplit = stringToCut.Split('.');
+
+            tmpClipName = clipNameSplit[0];
+
+            return tmpClipName;
+        }
+
+        public int clipCount
+        {
+            get
+            {
+                return _loadedClips.Count;
+            }
+        }
+    }
+}

+ 192 - 0
Master/AudioMaster.cs

@@ -0,0 +1,192 @@
+using Il2Cpp;
+using MelonLoader;
+using System.Collections;
+using UnityEngine;
+
+namespace AudioMgr
+{
+    public static class AudioMaster
+    {
+        private static AudioListener _playerListener;
+
+        private static bool _playerGotBGMQueue = false;
+        private static Queue _playerBGMQueue;   
+        private static GameObject _masterParent;
+        private static Panel_OptionsMenu _vanillaSoundOptionsMenu;
+        private static bool _isVanillaMusicDisabled = false;
+
+
+        public enum SourceType { SFX, BGM, Voice, Ambience, Custom };
+
+        public static void MoveMasterToPlayer()
+        {           
+            if (GameManager.GetMainCamera() != null)
+            {
+                _masterParent.transform.position = GameManager.GetVpFPSCamera().gameObject.transform.position;
+            }
+        }
+
+        public static void CreateMasterParent() 
+        {
+            if (_masterParent == null)
+            {
+                SettingMaster.Setup();
+                _masterParent = new GameObject("AudioStalker");
+                UnityEngine.GameObject.DontDestroyOnLoad(_masterParent);
+
+                _playerListener = _masterParent.AddComponent<AudioListener>();
+            }
+        }
+
+        private static void SearchAndDestroyListeners()
+        {            
+            AudioListener[] foundListeners = UnityEngine.Object.FindObjectsOfType<AudioListener>();
+
+            foreach (AudioListener singleKill in foundListeners)
+            {
+                UnityEngine.Object.Destroy(singleKill);
+            }
+        }
+
+
+
+        /// <summary>
+        /// Creates a new ClipManager instance for loading audioclips from files or bundles.
+        /// </summary>
+        /// <returns>New "ClipManager" class instance. Manage yourself</returns>
+        public static ClipManager NewClipManager()
+        {
+            ClipManager newManager = new ClipManager();
+
+            return newManager;
+        }
+
+        /// <summary>
+        /// Creates a new AudioSource on targetobject. Used for single short audioclips.
+        /// </summary>
+        /// <returns>New "Shot" class instance. Manage yourself</returns>
+        /// <param name="targetObject">Parentobject for new audiosource</param>
+        /// <param name="sourceType">Enum AudioMaster.SourceType.* - Affects volume and 3d audio settings</param>
+        /// 
+        public static Shot CreateShot(GameObject targetObject, SourceType sourceType)
+        {
+            Shot newAudioSource = targetObject.AddComponent<Shot>();
+            newAudioSource.Setup(sourceType);
+
+            return newAudioSource;
+        }
+
+        /// <summary>
+        /// Creates a new AudioSource on the player. Used for single short audioclips that emit from the player (eg. voice)
+        /// </summary>
+        /// <returns>New "Shot" class instance. Manage yourself</returns>
+        /// <param name="sourceType">Enum AudioMaster.SourceType.* - Affects volume and 3d audio settings</param>
+        /// 
+        public static Shot CreatePlayerShot(SourceType sourceType)
+        {
+            Shot newAudioSource = _masterParent.AddComponent<Shot>();
+            newAudioSource.Setup(sourceType);
+
+            return newAudioSource;
+        }
+
+        /// <summary>
+        /// Creates a new AudioSource on targetobject. Used for playing a list of audioclips.
+        /// </summary>
+        /// <returns>New "Queue" class instance. Manage yourself</returns>
+        /// <param name="targetObject">Parentobject for new audiosource</param>
+        /// <param name="clipManager">ClipManager that acts as the playlist</param>
+        /// <param name="timeGap">Pause between clips. Use 0f for gapless playback</param>
+        /// <param name="loopType">Enum Queue.Loop.* - Loop single clip / Loop complete list / Randomize play order (never stop)</param>
+        /// <param name="sourceType">Enum AudioMaster.SourceType.* - Affects volume and 3d audio settings</param>
+        /// 
+        public static Queue CreateQueue(GameObject targetObject, ClipManager clipManager, float timeGap, Queue.Loop loopType, SourceType sourceType)
+        {
+            Queue newAudioSource = targetObject.AddComponent<Queue>();
+            newAudioSource.Setup(clipManager, timeGap, loopType, sourceType);
+
+            return newAudioSource;
+        }
+
+        /// <summary>
+        /// Creates a new AudioSource on the player. Used for playing a list of audioclips.
+        /// </summary>
+        /// <returns>New "Queue" class instance. Manage yourself</returns>
+        /// <param name="clipManager">ClipManager that acts as the playlist</param>
+        /// <param name="timeGap">Pause between clips. Use 0f for gapless playback</param>
+        /// <param name="loopType">Enum Queue.Loop.* - Loop single clip / Loop complete list / Randomize play order (never stop)</param>
+        /// <param name="sourceType">Enum AudioMaster.SourceType.* - Affects volume and 3d audio settings</param>
+        /// 
+        public static Queue CreatePlayerQueue(ClipManager clipManager, float timeGap, Queue.Loop loopType, SourceType sourceType)
+        {
+            if (_playerGotBGMQueue && sourceType == SourceType.BGM)
+            {
+                MelonLogger.Msg("Player already got a queue for BGM attached. Returning null");
+
+                return null;
+            }
+
+            if (sourceType == SourceType.BGM)
+            {
+                _playerBGMQueue = _masterParent.AddComponent<Queue>();
+                _playerBGMQueue.Setup(clipManager, timeGap, loopType, sourceType);
+                _playerGotBGMQueue = true;
+
+                return _playerBGMQueue;
+            }
+
+            Queue newAudioSource = _masterParent.AddComponent<Queue>();
+            newAudioSource.Setup(clipManager, timeGap, loopType, sourceType);
+
+            return newAudioSource;
+        }
+
+
+        /// <summary>
+        /// Removes any Queue on the player with the source type BGM.
+        /// </summary>
+        /// 
+        public static void RemovePlayerBGMQueue()
+        {
+            if (_playerGotBGMQueue)
+            {
+                MelonLogger.Msg("Hostile takeover of BGM source on playerobject!");
+               
+                _playerGotBGMQueue = false;
+                _playerBGMQueue = null;
+            }
+        }
+
+        public static Panel_OptionsMenu vanillaAudioOptionsMenu
+        {
+            get
+            {
+                return _vanillaSoundOptionsMenu;
+            }
+            set
+            {
+                _vanillaSoundOptionsMenu = value;
+            }
+        }
+
+        public static AudioListener audioListener
+        {
+            get
+            {
+                return _playerListener;
+            }  
+        }
+
+        public static bool vanillaMusicDisabled
+        {
+            get
+            {
+                return _isVanillaMusicDisabled;
+            }
+            set
+            {
+                _isVanillaMusicDisabled = value;
+            }
+        }
+    }
+}

+ 110 - 0
Master/SettingMaster.cs

@@ -0,0 +1,110 @@
+using Il2Cpp;
+using UnityEngine;
+
+namespace AudioMgr
+{
+    public static class SettingMaster
+    {
+        private static Dictionary<AudioMaster.SourceType, Setting> _defaultSetting = new Dictionary<AudioMaster.SourceType, Setting>();
+
+        public static Setting NewSetting(AudioMaster.SourceType sourceType, float spread, float panStereo, float dopplerLevel, float maxDistance, float minDistance, float pitch, float spatialBlend, float rolloffFactor, bool spatialize, AudioRolloffMode audioRolloffMode, AnimationCurve rollOffCurve, int priority)
+        {            
+            Setting newSetting = new Setting(sourceType);
+            newSetting.sourceType = sourceType;
+            newSetting.spread = spread;
+            newSetting.panStereo = panStereo;
+            newSetting.dopplerLevel = dopplerLevel;
+            newSetting.maxDistance = maxDistance;
+            newSetting.minDistance = minDistance;
+            newSetting.pitch = pitch;
+            newSetting.spatialBlend = spatialBlend;
+            newSetting.rolloffFactor = rolloffFactor;
+            newSetting.spatialize = spatialize;
+            newSetting.rolloffMode = audioRolloffMode;
+            //newSetting.rollOffCurve = rollOffCurve;
+            newSetting.priority = priority;
+
+            return newSetting;
+        }
+
+        public static void Setup()
+        {
+            //AnimationCurve stdRollOffCurve = null;
+
+            _defaultSetting.Add(AudioMaster.SourceType.SFX, new Setting(AudioMaster.SourceType.SFX));
+            _defaultSetting.Add(AudioMaster.SourceType.Ambience, new Setting(AudioMaster.SourceType.Ambience));
+            _defaultSetting.Add(AudioMaster.SourceType.Voice, new Setting(AudioMaster.SourceType.Voice));
+            _defaultSetting.Add(AudioMaster.SourceType.BGM, new Setting(AudioMaster.SourceType.BGM));
+            _defaultSetting.Add(AudioMaster.SourceType.Custom, new Setting(AudioMaster.SourceType.Custom));
+
+            _defaultSetting[AudioMaster.SourceType.SFX].spread = 0.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].panStereo = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].dopplerLevel = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].maxDistance = 800.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].minDistance = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].pitch = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].spatialBlend = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].rolloffFactor = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.SFX].spatialize = true;
+            _defaultSetting[AudioMaster.SourceType.SFX].rolloffMode = AudioRolloffMode.Linear;
+            //_defaultSetting[AudioMaster.SourceType.SFX].rollOffCurve = stdRollOffCurve;
+            _defaultSetting[AudioMaster.SourceType.SFX].priority = 128;
+
+            _defaultSetting[AudioMaster.SourceType.Ambience].spread = 0.0f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].panStereo = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].dopplerLevel = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].maxDistance = 500.0f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].minDistance = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].pitch = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].spatialBlend = 0.2f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].rolloffFactor = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Ambience].spatialize = true;
+            _defaultSetting[AudioMaster.SourceType.Ambience].rolloffMode = AudioRolloffMode.Linear;
+            //_defaultSetting[AudioMaster.SourceType.Ambience].rollOffCurve = stdRollOffCurve;
+            _defaultSetting[AudioMaster.SourceType.Ambience].priority = 64;
+
+            _defaultSetting[AudioMaster.SourceType.Voice].spread = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].panStereo = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].dopplerLevel = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].maxDistance = 50.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].minDistance = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].pitch = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].spatialBlend = 0.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].rolloffFactor = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Voice].spatialize = true;
+            _defaultSetting[AudioMaster.SourceType.Voice].rolloffMode = AudioRolloffMode.Linear;
+            //_defaultSetting[AudioMaster.SourceType.Voice].rollOffCurve = stdRollOffCurve;
+            _defaultSetting[AudioMaster.SourceType.Voice].priority = 50;
+
+            _defaultSetting[AudioMaster.SourceType.BGM].spread = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].panStereo = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].dopplerLevel = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].maxDistance = 10.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].minDistance = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].pitch = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].spatialBlend = 0.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].rolloffFactor = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.BGM].spatialize = true;
+            _defaultSetting[AudioMaster.SourceType.BGM].rolloffMode = AudioRolloffMode.Linear;
+            //_defaultSetting[AudioMaster.SourceType.BGM].rollOffCurve = stdRollOffCurve;
+            _defaultSetting[AudioMaster.SourceType.BGM].priority = 64;
+
+            _defaultSetting[AudioMaster.SourceType.Custom].spread = 0.0f;
+            _defaultSetting[AudioMaster.SourceType.Custom].panStereo = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Custom].dopplerLevel = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Custom].maxDistance = 500.0f;
+            _defaultSetting[AudioMaster.SourceType.Custom].minDistance = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Custom].pitch = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Custom].spatialBlend = 0.8f;
+            _defaultSetting[AudioMaster.SourceType.Custom].rolloffFactor = 1.0f;
+            _defaultSetting[AudioMaster.SourceType.Custom].spatialize = true;
+            _defaultSetting[AudioMaster.SourceType.Custom].rolloffMode = AudioRolloffMode.Linear;
+            //_defaultSetting[AudioMaster.SourceType.Custom].rollOffCurve = stdRollOffCurve;
+            _defaultSetting[AudioMaster.SourceType.Custom].priority = 80;
+        }
+        public static Setting Defaults(AudioMaster.SourceType sourceType)
+        {
+            return _defaultSetting[sourceType];
+        }
+    }
+}

+ 52 - 0
Master/VolumeMaster.cs

@@ -0,0 +1,52 @@
+using UnityEngine;
+
+namespace AudioMgr
+{
+    public static class VolumeMaster
+    {
+        public delegate void OnVolumeChange();
+        public static event OnVolumeChange onVolumeChange;
+
+        private static float _masterVolume = 1f;
+
+        private static Dictionary<AudioMaster.SourceType, float> _globalVolumes = new Dictionary<AudioMaster.SourceType, float>
+        {
+            { AudioMaster.SourceType.SFX, 1f },
+            { AudioMaster.SourceType.BGM, 1f },
+            { AudioMaster.SourceType.Voice, 1f },
+            { AudioMaster.SourceType.Ambience, 1f },
+            { AudioMaster.SourceType.Custom, 1f }
+        };
+
+        private static void VolumeChanged()
+        {
+            if (onVolumeChange != null)
+            {
+                onVolumeChange();
+            }
+        }
+
+        public static float GetVolume(AudioMaster.SourceType type)
+        {
+            return ApplyMasterOffset(_globalVolumes[type]); 
+        }
+
+        private static float ApplyMasterOffset(float volume)
+        {   
+
+            return volume * _masterVolume;
+        }
+
+        public static void SetMasterVolume(float volume)
+        {
+            _masterVolume = volume;
+            VolumeChanged();
+        }
+
+        public static void SetVolume(AudioMaster.SourceType type, float volume)
+        {
+            _globalVolumes[type] = volume;
+            VolumeChanged();
+        }       
+    }
+}

+ 47 - 0
Minions/Clip.cs

@@ -0,0 +1,47 @@
+using UnityEngine;
+
+namespace AudioMgr
+{
+    public class Clip
+    {
+        private AudioClip _audioClip;
+
+        private string _clipName;
+        private double _clipLength;
+        private int _clipFreq;
+        private int _clipSamples;
+
+        public Clip(AudioClip clip, string clipName)
+        {            
+            _audioClip = clip;
+            _audioClip.hideFlags = HideFlags.DontUnloadUnusedAsset;
+
+            _clipName = clipName;
+
+            _clipFreq = _audioClip.frequency;
+            _clipSamples = _audioClip.samples;
+            _clipLength = (double)_clipSamples / _clipFreq;
+        }
+
+        public void Unload()
+        {
+            _audioClip.UnloadAudioData();
+        }
+
+        public AudioClip audioClip
+        {
+            get
+            {
+                return _audioClip;
+            }
+        }
+
+        public double clipLength
+        {
+            get
+            {
+                return _clipLength;
+            }
+        }
+    }
+}

+ 27 - 0
Minions/Setting.cs

@@ -0,0 +1,27 @@
+using UnityEngine;
+
+namespace AudioMgr
+{
+    public class Setting
+    {
+        public AudioMaster.SourceType sourceType;
+
+        public float spread;
+        public float panStereo;
+        public float dopplerLevel;
+        public float maxDistance;
+        public float minDistance;
+        public float pitch;
+        public float spatialBlend;
+        public bool spatialize;
+        public float rolloffFactor;
+        public AudioRolloffMode rolloffMode;
+        //public AnimationCurve rollOffCurve;
+        public int priority;
+
+        public Setting(AudioMaster.SourceType sourcetype)
+        {
+            sourceType = sourcetype;           
+        }
+    }
+}

+ 9 - 0
Properties/AssemblyInfo.cs

@@ -0,0 +1,9 @@
+using MelonLoader;
+using System.Reflection;
+
+[assembly: AssemblyTitle("AudioManager")]
+[assembly: AssemblyCopyright("Digitalzombie")]
+[assembly: AssemblyVersion("0.8.0")]
+[assembly: AssemblyFileVersion("0.8.0")]
+[assembly: MelonInfo(typeof(AudioMgr.AudioMain), "AudioManager", "0.8.0", "Digitalzombie", null)]
+[assembly: MelonGame("Hinterland", "TheLongDark")]