diff --git a/NewHorizons/Components/EOTE/DreamWorldEndTimes.cs b/NewHorizons/Components/EOTE/DreamWorldEndTimes.cs new file mode 100644 index 00000000..839d286f --- /dev/null +++ b/NewHorizons/Components/EOTE/DreamWorldEndTimes.cs @@ -0,0 +1,32 @@ +using NewHorizons.Utility.Files; +using OWML.Common; +using UnityEngine; + +namespace NewHorizons.Components.EOTE +{ + public class DreamWorldEndTimes : MonoBehaviour + { + private AudioType _endTimesAudio = AudioType.EndOfTime; + private AudioType _endTimesDreamAudio = AudioType.EndOfTime_Dream; + + public void SetEndTimesAudio(AudioType audio) + { + _endTimesAudio = audio; + } + + public void AssignEndTimes(OWAudioSource endTimesSource) => Assign(endTimesSource, _endTimesAudio); + + public void SetEndTimesDreamAudio(AudioType audio) + { + _endTimesDreamAudio = audio; + } + + public void AssignEndTimesDream(OWAudioSource endTimesSource) => Assign(endTimesSource, _endTimesDreamAudio); + + public static void Assign(OWAudioSource endTimesSource, AudioType audio) + { + endTimesSource.Stop(); + endTimesSource.AssignAudioLibraryClip(audio); + } + } +} \ No newline at end of file diff --git a/NewHorizons/External/Configs/StarSystemConfig.cs b/NewHorizons/External/Configs/StarSystemConfig.cs index 28ecc4dd..a5fcb088 100644 --- a/NewHorizons/External/Configs/StarSystemConfig.cs +++ b/NewHorizons/External/Configs/StarSystemConfig.cs @@ -82,11 +82,14 @@ namespace NewHorizons.External.Configs [Obsolete("travelAudioFilePath is deprecated, please use travelAudio instead")] public string travelAudioFilePath; - /// - /// The audio that will play when travelling in space. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. - /// + [Obsolete("travelAudio is deprecated, please use travelAudio instead")] public string travelAudio; + /// + /// Replace music that plays globally + /// + public GlobalMusicModule GlobalMusic; + /// /// Configure warping to this system with the vessel /// @@ -192,6 +195,45 @@ namespace NewHorizons.External.Configs public string backPath; } + [JsonObject] + public class GlobalMusicModule + { + /// + /// The audio that will play when travelling in space. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string travelAudio; + + /// + /// The audio that will play right before the loop ends. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string endTimesAudio; + + /// + /// The audio that will play right before the loop ends while inside the dreamworld. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string endTimesDreamAudio; + + /// + /// The audio that will play when travelling through a bramble dimension. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string brambleDimensionAudio; + + /// + /// The audio that will play when you leave the ash twin project after taking out the advanced warp core. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string finalEndTimesIntroAudio; + + /// + /// The audio that will loop after the final end times intro. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string finalEndTimesLoopAudio; + + /// + /// The audio that will loop after the final end times intro while inside a bramble dimension. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string finalEndTimesBrambleDimensionAudio; + } + [JsonObject] public class VesselModule { @@ -283,7 +325,6 @@ namespace NewHorizons.External.Configs // If current one is null take the other factRequiredForWarp = string.IsNullOrEmpty(factRequiredForWarp) ? otherConfig.factRequiredForWarp : factRequiredForWarp; Skybox = Skybox == null ? otherConfig.Skybox : Skybox; - travelAudio = string.IsNullOrEmpty(travelAudio) ? otherConfig.travelAudio : travelAudio; // False by default so if one is true go true mapRestricted = mapRestricted || otherConfig.mapRestricted; @@ -302,6 +343,21 @@ namespace NewHorizons.External.Configs Vessel ??= otherConfig.Vessel; } + if (GlobalMusic != null && otherConfig.GlobalMusic != null) + { + GlobalMusic.travelAudio = string.IsNullOrEmpty(GlobalMusic.travelAudio) ? otherConfig.GlobalMusic.travelAudio : GlobalMusic.travelAudio; + GlobalMusic.endTimesAudio = string.IsNullOrEmpty(GlobalMusic.endTimesAudio) ? otherConfig.GlobalMusic.endTimesAudio : GlobalMusic.endTimesAudio; + GlobalMusic.endTimesDreamAudio = string.IsNullOrEmpty(GlobalMusic.endTimesDreamAudio) ? otherConfig.GlobalMusic.endTimesDreamAudio : GlobalMusic.endTimesDreamAudio; + GlobalMusic.brambleDimensionAudio = string.IsNullOrEmpty(GlobalMusic.brambleDimensionAudio) ? otherConfig.GlobalMusic.brambleDimensionAudio : GlobalMusic.brambleDimensionAudio; + GlobalMusic.finalEndTimesIntroAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesIntroAudio) ? otherConfig.GlobalMusic.finalEndTimesIntroAudio : GlobalMusic.finalEndTimesIntroAudio; + GlobalMusic.finalEndTimesLoopAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesLoopAudio) ? otherConfig.GlobalMusic.finalEndTimesLoopAudio : GlobalMusic.finalEndTimesLoopAudio; + GlobalMusic.finalEndTimesBrambleDimensionAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesBrambleDimensionAudio) ? otherConfig.GlobalMusic.finalEndTimesBrambleDimensionAudio : GlobalMusic.finalEndTimesBrambleDimensionAudio; + } + else + { + GlobalMusic ??= otherConfig.GlobalMusic; + } + entryPositions = Concatenate(entryPositions, otherConfig.entryPositions); curiosities = Concatenate(curiosities, otherConfig.curiosities); initialReveal = Concatenate(initialReveal, otherConfig.initialReveal); @@ -319,6 +375,11 @@ namespace NewHorizons.External.Configs #pragma warning disable 612, 618 if (!string.IsNullOrEmpty(travelAudioClip)) travelAudio = travelAudioClip; if (!string.IsNullOrEmpty(travelAudioFilePath)) travelAudio = travelAudioFilePath; + if (!string.IsNullOrEmpty(travelAudio)) + { + if (GlobalMusic == null) GlobalMusic = new GlobalMusicModule(); + if (string.IsNullOrEmpty(GlobalMusic.travelAudio)) GlobalMusic.travelAudio = travelAudio; + } if (coords != null || vesselPosition != null || vesselRotation != null || warpExitPosition != null || warpExitRotation != null) { if (Vessel == null) diff --git a/NewHorizons/Handlers/StarChartHandler.cs b/NewHorizons/Handlers/StarChartHandler.cs index b1ef9eea..3b874ee9 100644 --- a/NewHorizons/Handlers/StarChartHandler.cs +++ b/NewHorizons/Handlers/StarChartHandler.cs @@ -121,7 +121,7 @@ namespace NewHorizons.Handlers public static void OnRevealFact(string factID) { - if (_factIDToStarSystem.TryGetValue(factID, out var systemUnlocked)) + if (_factIDToStarSystem != null && _factIDToStarSystem.TryGetValue(factID, out var systemUnlocked)) { NHLogger.Log($"Just learned [{factID}] and unlocked [{systemUnlocked}]"); if (!Main.HasWarpDrive) diff --git a/NewHorizons/Handlers/SystemCreationHandler.cs b/NewHorizons/Handlers/SystemCreationHandler.cs index 5936434a..db2951a3 100644 --- a/NewHorizons/Handlers/SystemCreationHandler.cs +++ b/NewHorizons/Handlers/SystemCreationHandler.cs @@ -8,6 +8,8 @@ using NewHorizons.Utility.OuterWilds; using UnityEngine; using Object = UnityEngine.Object; using NewHorizons.OtherMods; +using NewHorizons.Components.EOTE; +using Epic.OnlineServices.Presence; namespace NewHorizons.Handlers { @@ -46,9 +48,52 @@ namespace NewHorizons.Handlers TimeLoopUtilities.SetLoopDuration(system.Config.loopDuration); } - if (!string.IsNullOrEmpty(system.Config.travelAudio)) + if (system.Config.GlobalMusic != null) { - Delay.FireOnNextUpdate(() => AudioUtilities.SetAudioClip(Locator.GetGlobalMusicController()._travelSource, system.Config.travelAudio, system.Mod)); + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.travelAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.travelAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._travelSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.endTimesAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.endTimesAudio, system.Mod); + Delay.FireOnNextUpdate(() => { + Locator.GetGlobalMusicController().gameObject.GetAddComponent().SetEndTimesAudio(audioType); + Locator.GetGlobalMusicController()._endTimesSource.AssignAudioLibraryClip(audioType); + }); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.endTimesDreamAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.endTimesDreamAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController().gameObject.GetAddComponent().SetEndTimesDreamAudio(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.brambleDimensionAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.brambleDimensionAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._darkBrambleSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.finalEndTimesIntroAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.finalEndTimesIntroAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._finalEndTimesIntroSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.finalEndTimesLoopAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.finalEndTimesLoopAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._finalEndTimesLoopSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.finalEndTimesBrambleDimensionAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.finalEndTimesBrambleDimensionAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._finalEndTimesDarkBrambleSource.AssignAudioLibraryClip(audioType)); + } } } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 7aac5449..3b38bd35 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -690,7 +690,7 @@ namespace NewHorizons if (SystemDict.ContainsKey(starSystemName)) { - if (string.IsNullOrEmpty(SystemDict[starSystemName].Config.travelAudio) && SystemDict[starSystemName].Config.Skybox == null) + if (SystemDict[starSystemName].Config.GlobalMusic == null && SystemDict[starSystemName].Config.Skybox == null) SystemDict[starSystemName].Mod = mod; SystemDict[starSystemName].Config.Merge(starSystemConfig); } diff --git a/NewHorizons/Patches/GlobalMusicControllerPatches.cs b/NewHorizons/Patches/GlobalMusicControllerPatches.cs index 776cb8e8..9697beb7 100644 --- a/NewHorizons/Patches/GlobalMusicControllerPatches.cs +++ b/NewHorizons/Patches/GlobalMusicControllerPatches.cs @@ -1,10 +1,14 @@ -using HarmonyLib; +using HarmonyLib; +using NewHorizons.Components.EOTE; +using NewHorizons.Utility; +using System.Collections.Generic; +using System.Reflection.Emit; using UnityEngine; namespace NewHorizons.Patches; [HarmonyPatch(typeof(GlobalMusicController))] -public class GlobalMusicControllerPatches +public static class GlobalMusicControllerPatches { private static AudioDetector _audioDetector; @@ -21,7 +25,7 @@ public class GlobalMusicControllerPatches PlayerState.AtFlightConsole() && !PlayerState.IsHullBreached() && !__instance._playingFinalEndTimes && - _audioDetector._activeVolumes.Count == 0; // change - don't play if in another audio volume + _audioDetector._activeVolumes.Count <= 1; // change - don't play if in another audio volume other than ambient var playing = __instance._darkBrambleSource.isPlaying && !__instance._darkBrambleSource.IsFadingOut(); if (shouldBePlaying && !playing) @@ -33,6 +37,93 @@ public class GlobalMusicControllerPatches __instance._darkBrambleSource.FadeOut(5f); } + return false; + } + + /// + /// Replaces any 85f with + /// + [HarmonyTranspiler] + [HarmonyPatch(nameof(GlobalMusicController.UpdateEndTimesMusic))] + public static IEnumerable GlobalMusicController_UpdateEndTimesMusic(IEnumerable instructions, ILGenerator generator) + { + return new CodeMatcher(instructions, generator).MatchForward(true, + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Ldc_R4, 85f), + new CodeMatch(OpCodes.Ble_Un) + ).Advance(-1).RemoveInstruction().Insert( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NewHorizonsExtensions), nameof(NewHorizonsExtensions.GetSecondsBeforeSupernovaPlayTime))) + ).MatchForward(true, + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Ldc_R4, 85f), + new CodeMatch(OpCodes.Bge_Un) + ).Advance(-1).RemoveInstruction().Insert( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NewHorizonsExtensions), nameof(NewHorizonsExtensions.GetSecondsBeforeSupernovaPlayTime))) + ).MatchForward(false, + new CodeMatch(OpCodes.Ldc_R4, 85f), + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Sub) + ).RemoveInstruction().Insert( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NewHorizonsExtensions), nameof(NewHorizonsExtensions.GetSecondsBeforeSupernovaPlayTime))) + ).InstructionEnumeration(); + } + + // Custom end times for dreamworld + + [HarmonyPrefix] + [HarmonyPatch(nameof(GlobalMusicController.OnEnterDreamWorld))] + public static bool GlobalMusicController_OnEnterDreamWorld(GlobalMusicController __instance) + { + if (__instance._playingFinalEndTimes) + { + __instance._finalEndTimesIntroSource.Stop(); + __instance._finalEndTimesLoopSource.Stop(); + __instance._finalEndTimesDarkBrambleSource.FadeIn(1f); + } + else + { + if (__instance.TryGetComponent(out DreamWorldEndTimes dreamWorldEndTimes)) + { + dreamWorldEndTimes.AssignEndTimesDream(__instance._endTimesSource); + } + else + { + __instance._endTimesSource.Stop(); + __instance._endTimesSource.AssignAudioLibraryClip(AudioType.EndOfTime_Dream); + } + __instance._playingEndTimes = false; + } + + return false; + } + + + [HarmonyPrefix] + [HarmonyPatch(nameof(GlobalMusicController.OnExitDreamWorld))] + public static bool GlobalMusicController_OnExitDreamWorld(GlobalMusicController __instance) + { + if (__instance._playingFinalEndTimes) + { + __instance._finalEndTimesLoopSource.FadeIn(1f); + __instance._finalEndTimesDarkBrambleSource.Stop(); + } + else + { + if (__instance.TryGetComponent(out DreamWorldEndTimes dreamWorldEndTimes)) + { + dreamWorldEndTimes.AssignEndTimes(__instance._endTimesSource); + } + else + { + __instance._endTimesSource.Stop(); + __instance._endTimesSource.AssignAudioLibraryClip(AudioType.EndOfTime); + } + __instance._playingEndTimes = false; + } + return false; } } diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index 8c479fe8..814773c5 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -59,9 +59,9 @@ "type": "boolean", "description": "Set to `true` if you want the player to stay in this star system if they die in it." }, - "travelAudio": { - "type": "string", - "description": "The audio that will play when travelling in space. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + "GlobalMusic": { + "description": "Replace music that plays globally", + "$ref": "#/definitions/GlobalMusicModule" }, "Vessel": { "description": "Configure warping to this system with the vessel", @@ -143,6 +143,40 @@ } } }, + "GlobalMusicModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "travelAudio": { + "type": "string", + "description": "The audio that will play when travelling in space. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "endTimesAudio": { + "type": "string", + "description": "The audio that will play right before the loop ends. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "endTimesDreamAudio": { + "type": "string", + "description": "The audio that will play right before the loop ends while inside the dreamworld. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "brambleDimensionAudio": { + "type": "string", + "description": "The audio that will play when travelling through a bramble dimension. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "finalEndTimesIntroAudio": { + "type": "string", + "description": "The audio that will play when you leave the ash twin project after taking out the advanced warp core. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "finalEndTimesLoopAudio": { + "type": "string", + "description": "The audio that will loop after the final end times intro. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "finalEndTimesBrambleDimensionAudio": { + "type": "string", + "description": "The audio that will loop after the final end times intro while inside a bramble dimension. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + } + } + }, "VesselModule": { "type": "object", "additionalProperties": false, diff --git a/NewHorizons/Utility/Files/AudioUtilities.cs b/NewHorizons/Utility/Files/AudioUtilities.cs index ca01b44a..fe09d46e 100644 --- a/NewHorizons/Utility/Files/AudioUtilities.cs +++ b/NewHorizons/Utility/Files/AudioUtilities.cs @@ -121,7 +121,9 @@ namespace NewHorizons.Utility.Files } else { - return dh.audioClip; + var audioClip = dh.audioClip; + audioClip.name = Path.GetFileNameWithoutExtension(path); + return audioClip; } } } @@ -140,7 +142,9 @@ namespace NewHorizons.Utility.Files } else { - return DownloadHandlerAudioClip.GetContent(www); + var audioClip = DownloadHandlerAudioClip.GetContent(www); + audioClip.name = Path.GetFileNameWithoutExtension(path); + return audioClip; } } } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index 3c01bd16..218f64ed 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -25,8 +25,12 @@ namespace NewHorizons.Utility.Files return _textureCache.ContainsKey(key); } + #region obsolete // needed for backwards compat :P + // idk what mod used it + [Obsolete] public static Texture2D GetTexture(IModBehaviour mod, string filename, bool useMipmaps, bool wrap) => GetTexture(mod, filename, useMipmaps, wrap, false); + #endregion // bug: cache only considers file path, not wrap/mips/linear. oh well public static Texture2D GetTexture(IModBehaviour mod, string filename, bool useMipmaps = true, bool wrap = false, bool linear = false) { diff --git a/NewHorizons/Utility/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs index 66f5babf..9f4da941 100644 --- a/NewHorizons/Utility/NewHorizonExtensions.cs +++ b/NewHorizons/Utility/NewHorizonExtensions.cs @@ -1,3 +1,4 @@ +using HarmonyLib; using NewHorizons.External.Configs; using NewHorizons.External.Modules.VariableSize; using NewHorizons.External.SerializableData; @@ -435,5 +436,28 @@ namespace NewHorizons.Utility playerCameraEffectController._owCamera.postProcessingSettings.bloom.threshold = 0f; playerCameraEffectController._owCamera.postProcessingSettings.eyeMaskEnabled = true; } + + public static float GetSecondsBeforeSupernovaPlayTime(this GlobalMusicController globalMusicController) + { + var clip = globalMusicController._endTimesSource.audioLibraryClip; + if (clip == AudioType.EndOfTime || clip == AudioType.EndOfTime_Dream) + return GlobalMusicController.secondsBeforeSupernovaPlayTime; + return globalMusicController._endTimesSource.clip.length; + } + + public static CodeMatcher LogInstructions(this CodeMatcher matcher, string prefix) + { + matcher.InstructionEnumeration().LogInstructions(prefix); + return matcher; + } + + public static IEnumerable LogInstructions(this IEnumerable instructions, string prefix) + { + var message = prefix; + foreach (var instruction in instructions) + message += $"\n{instruction}"; + Debug.LogError(message); + return instructions; + } } }