Global music module (#901)

## Major features

- Added `GlobalMusic` module to system config (change end times, bramble
dimension, and final end times music) (Resolves #894)

## Bug fixes

- Fixed global bramble music not playing because all dimensions have an
ambient audio volume
This commit is contained in:
xen-42 2024-06-17 19:14:58 -04:00 committed by GitHub
commit 4b9a1297b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 311 additions and 16 deletions

View File

@ -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);
}
}
}

View File

@ -82,11 +82,14 @@ namespace NewHorizons.External.Configs
[Obsolete("travelAudioFilePath is deprecated, please use travelAudio instead")]
public string travelAudioFilePath;
/// <summary>
/// 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.
/// </summary>
[Obsolete("travelAudio is deprecated, please use travelAudio instead")]
public string travelAudio;
/// <summary>
/// Replace music that plays globally
/// </summary>
public GlobalMusicModule GlobalMusic;
/// <summary>
/// Configure warping to this system with the vessel
/// </summary>
@ -192,6 +195,45 @@ namespace NewHorizons.External.Configs
public string backPath;
}
[JsonObject]
public class GlobalMusicModule
{
/// <summary>
/// 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.
/// </summary>
public string travelAudio;
/// <summary>
/// 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.
/// </summary>
public string endTimesAudio;
/// <summary>
/// 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.
/// </summary>
public string endTimesDreamAudio;
/// <summary>
/// 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.
/// </summary>
public string brambleDimensionAudio;
/// <summary>
/// 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.
/// </summary>
public string finalEndTimesIntroAudio;
/// <summary>
/// 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.
/// </summary>
public string finalEndTimesLoopAudio;
/// <summary>
/// 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.
/// </summary>
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)

View File

@ -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)

View File

@ -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<DreamWorldEndTimes>().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<DreamWorldEndTimes>().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));
}
}
}
}

View File

@ -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);
}

View File

@ -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)
@ -35,4 +39,91 @@ public class GlobalMusicControllerPatches
return false;
}
/// <summary>
/// Replaces any <c>85f</c> with <see cref="NewHorizonsExtensions.GetSecondsBeforeSupernovaPlayTime(GlobalMusicController)"/>
/// </summary>
[HarmonyTranspiler]
[HarmonyPatch(nameof(GlobalMusicController.UpdateEndTimesMusic))]
public static IEnumerable<CodeInstruction> GlobalMusicController_UpdateEndTimesMusic(IEnumerable<CodeInstruction> 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;
}
}

View File

@ -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,

View File

@ -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;
}
}
}

View File

@ -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)
{

View File

@ -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<CodeInstruction> LogInstructions(this IEnumerable<CodeInstruction> instructions, string prefix)
{
var message = prefix;
foreach (var instruction in instructions)
message += $"\n{instruction}";
Debug.LogError(message);
return instructions;
}
}
}