Eye sequence props first pass

This commit is contained in:
Joshua Thome 2025-01-18 10:09:15 -06:00
parent ffe41747fb
commit 63fbc8afeb
15 changed files with 743 additions and 0 deletions

View File

@ -0,0 +1,187 @@
using NewHorizons.Builder.Props.Audio;
using NewHorizons.Components.EyeOfTheUniverse;
using NewHorizons.External;
using NewHorizons.External.Modules;
using NewHorizons.External.Modules.Props.Audio;
using NewHorizons.External.Modules.Props.EyeOfTheUniverse;
using NewHorizons.Handlers;
using NewHorizons.Utility;
using NewHorizons.Utility.OuterWilds;
using NewHorizons.Utility.OWML;
using UnityEngine;
namespace NewHorizons.Builder.Props
{
public static class EyeOfTheUniverseBuilder
{
public static TravelerEyeController MakeEyeTraveler(GameObject planetGO, Sector sector, EyeTravelerInfo info, NewHorizonsBody nhBody)
{
var go = DetailBuilder.Make(planetGO, sector, nhBody.Mod, info);
var travelerController = go.GetAddComponent<TravelerEyeController>();
if (!string.IsNullOrEmpty(info.startPlayingCondition))
{
travelerController._startPlayingCondition = info.startPlayingCondition;
}
else if (string.IsNullOrEmpty(travelerController._startPlayingCondition))
{
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have a Start Playing condition set");
}
if (travelerController._animator == null)
{
travelerController._animator = go.GetComponentInChildren<Animator>();
}
if (info.dialogue != null)
{
var (dialogueTree, remoteTrigger) = DialogueBuilder.Make(planetGO, sector, info.dialogue, nhBody.Mod);
if (travelerController._dialogueTree != null)
{
travelerController._dialogueTree.OnStartConversation -= travelerController.OnStartConversation;
travelerController._dialogueTree.OnEndConversation -= travelerController.OnEndConversation;
}
travelerController._dialogueTree = dialogueTree;
travelerController._dialogueTree.OnStartConversation += travelerController.OnStartConversation;
travelerController._dialogueTree.OnEndConversation += travelerController.OnEndConversation;
}
else if (travelerController._dialogueTree == null)
{
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have any dialogue set");
}
OWAudioSource loopAudioSource = null;
if (!string.IsNullOrEmpty(info.loopAudio))
{
var signalInfo = new SignalInfo()
{
audio = info.loopAudio,
detectionRadius = 0,
identificationRadius = 10f,
frequency = string.IsNullOrEmpty(info.frequency) ? "Traveler" : info.frequency,
parentPath = go.transform.GetPath(),
isRelativeToParent = true,
position = Vector3.up * 0.5f,
};
var signalGO = SignalBuilder.Make(planetGO, sector, signalInfo, nhBody.Mod);
var signal = signalGO.GetComponent<AudioSignal>();
travelerController._signal = signal;
loopAudioSource = signal.GetOWAudioSource();
}
else if (travelerController._signal == null)
{
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have any loop audio set");
}
OWAudioSource finaleAudioSource = null;
if (!string.IsNullOrEmpty(info.finaleAudio))
{
var finaleAudioInfo = new AudioSourceInfo()
{
audio = info.finaleAudio,
track = External.SerializableEnums.NHAudioMixerTrackName.Music,
};
finaleAudioSource = GeneralAudioBuilder.Make(planetGO, sector, finaleAudioInfo, nhBody.Mod);
finaleAudioSource.SetTrack(finaleAudioInfo.track.ConvertToOW());
finaleAudioSource.loop = false;
finaleAudioSource.spatialBlend = 0f;
}
var travelerData = EyeSceneHandler.GetOrCreateEyeTravelerData(info.id);
travelerData.info = info;
travelerData.controller = travelerController;
travelerData.loopAudioSource = loopAudioSource;
travelerData.finaleAudioSource = finaleAudioSource;
return travelerController;
}
public static QuantumInstrument MakeQuantumInstrument(GameObject planetGO, Sector sector, QuantumInstrumentInfo info, NewHorizonsBody nhBody)
{
var go = DetailBuilder.Make(planetGO, sector, nhBody.Mod, info);
go.layer = Layer.Interactible;
if (info.interactRadius > 0f)
{
var collider = go.AddComponent<SphereCollider>();
collider.radius = info.interactRadius;
collider.isTrigger = true;
go.GetAddComponent<OWCollider>();
}
go.GetAddComponent<InteractReceiver>();
var quantumInstrument = go.GetAddComponent<QuantumInstrument>();
quantumInstrument._gatherWithScope = info.gatherWithScope;
var trigger = go.AddComponent<QuantumInstrumentTrigger>();
trigger.gatherCondition = info.gatherCondition;
var travelerData = EyeSceneHandler.GetOrCreateEyeTravelerData(info.id);
travelerData.quantumInstruments.Add(quantumInstrument);
if (travelerData.info != null)
{
if (!string.IsNullOrEmpty(travelerData.info.loopAudio))
{
var signalInfo = new SignalInfo()
{
audio = travelerData.info.loopAudio,
detectionRadius = 0,
identificationRadius = 0,
frequency = string.IsNullOrEmpty(travelerData.info.frequency) ? "Traveler" : travelerData.info.frequency,
parentPath = go.transform.GetPath(),
isRelativeToParent = true,
position = Vector3.zero,
};
var signalGO = SignalBuilder.Make(planetGO, sector, signalInfo, nhBody.Mod);
}
else
{
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have any loop audio set");
}
}
else
{
NHLogger.LogError($"Quantum instrument with ID \"{info.id}\" has no matching eye traveler");
}
return quantumInstrument;
}
public static InstrumentZone MakeInstrumentZone(GameObject planetGO, Sector sector, InstrumentZoneInfo info, NewHorizonsBody nhBody)
{
var go = DetailBuilder.Make(planetGO, sector, nhBody.Mod, info);
var instrumentZone = go.AddComponent<InstrumentZone>();
var travelerData = EyeSceneHandler.GetOrCreateEyeTravelerData(info.id);
travelerData.instrumentZones.Add(instrumentZone);
return instrumentZone;
}
public static void Make(GameObject go, Sector sector, EyeOfTheUniverseModule module, NewHorizonsBody nhBody)
{
if (module.eyeTravelers != null)
{
foreach (var info in module.eyeTravelers)
{
MakeEyeTraveler(go, sector, info, nhBody);
}
}
if (module.instrumentZones != null)
{
foreach (var info in module.instrumentZones)
{
MakeInstrumentZone(go, sector, info, nhBody);
}
}
if (module.quantumInstruments != null)
{
foreach (var info in module.quantumInstruments)
{
MakeQuantumInstrument(go, sector, info, nhBody);
}
}
}
}
}

View File

@ -0,0 +1,97 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace NewHorizons.Components.EyeOfTheUniverse
{
public class EyeMusicController : MonoBehaviour
{
private List<OWAudioSource> _loopSources = new();
private List<OWAudioSource> _finaleSources = new();
private bool _transitionToFinale;
private bool _isPlaying;
public void RegisterLoopSource(OWAudioSource src)
{
src.loop = false;
src.SetLocalVolume(1f);
_loopSources.Add(src);
}
public void RegisterFinaleSource(OWAudioSource src)
{
src.loop = false;
src.SetLocalVolume(1f);
_finaleSources.Add(src);
}
public void StartPlaying()
{
if (_isPlaying) return;
_isPlaying = true;
StartCoroutine(DoLoop());
}
public void TransitionToFinale()
{
_transitionToFinale = true;
}
private IEnumerator DoLoop()
{
// Initial delay to ensure audio system has time to schedule all loops for the same tick
double timeBufferWindow = 0.5;
// Track when the next audio (loop or finale) should play, in audio system time
double nextAudioEventTime = AudioSettings.dspTime + timeBufferWindow;
// Determine timing using the first loop audio clip (should be Riebeck's banjo loop)
var referenceLoopClip = _loopSources.First().clip;
double loopDuration = referenceLoopClip.samples / (double)referenceLoopClip.frequency;
double segmentDuration = loopDuration / 4.0;
while (!_transitionToFinale)
{
// Play loops in sync
var loopStartTime = nextAudioEventTime;
foreach (var loopSrc in _loopSources)
{
loopSrc._audioSource.PlayScheduled(loopStartTime);
}
nextAudioEventTime += loopDuration;
// Handle loop segments (the current musical measure will always finish playing before transitioning to the finale)
for (int i = 0; i < 4; i++)
{
// Interrupting the upcoming segment for the finale
if (_transitionToFinale)
{
// End the loop at the start time of the upcoming segment
var loopStopTime = loopStartTime + segmentDuration * (i + 1);
// Cancel scheduled upcoming loop
foreach (var loopSrc in _loopSources)
{
loopSrc._audioSource.SetScheduledEndTime(loopStopTime);
}
// Schedule finale for as soon as the loop ends
nextAudioEventTime = loopStopTime;
break;
}
// Wait until shortly before the next segment (`nextAudioEventTime` will be ahead of current time by `timeBufferWindow`)
yield return new WaitForSecondsRealtime((float)segmentDuration);
}
}
// Play finale in sync
foreach (var finaleSrc in _finaleSources)
{
finaleSrc._audioSource.PlayScheduled(nextAudioEventTime);
}
}
}
}

View File

@ -0,0 +1,9 @@
using UnityEngine;
namespace NewHorizons.Components.EyeOfTheUniverse
{
public class InstrumentZone : MonoBehaviour
{
}
}

View File

@ -0,0 +1,30 @@
using UnityEngine;
namespace NewHorizons.Components.EyeOfTheUniverse
{
public class QuantumInstrumentTrigger : MonoBehaviour
{
public string gatherCondition;
private QuantumInstrument _quantumInstrument;
private void Awake()
{
_quantumInstrument = GetComponent<QuantumInstrument>();
_quantumInstrument.OnFinishGather += OnFinishGather;
}
private void OnDestroy()
{
_quantumInstrument.OnFinishGather -= OnFinishGather;
}
private void OnFinishGather()
{
if (!string.IsNullOrEmpty(gatherCondition))
{
DialogueConditionManager.SharedInstance.SetConditionState(gatherCondition, true);
}
}
}
}

View File

@ -102,6 +102,11 @@ namespace NewHorizons.External.Configs
/// </summary>
public DreamModule Dream;
/// <summary>
/// Add features exclusive to the Eye of the Universe scene
/// </summary>
public EyeOfTheUniverseModule EyeOfTheUniverse;
/// <summary>
/// Make this body into a focal point (barycenter)
/// </summary>

View File

@ -0,0 +1,24 @@
using NewHorizons.External.Modules.Props.EyeOfTheUniverse;
using Newtonsoft.Json;
namespace NewHorizons.External.Modules
{
[JsonObject]
public class EyeOfTheUniverseModule
{
/// <summary>
/// Add custom travelers to the campfire sequence
/// </summary>
public EyeTravelerInfo[] eyeTravelers;
/// <summary>
/// Add instrument zones which contain puzzles to gather a quantum instrument. You can parent other props to these with `parentPath`
/// </summary>
public InstrumentZoneInfo[] instrumentZones;
/// <summary>
/// Add quantum instruments which cause their associated eye traveler to appear and instrument zones to disappear
/// </summary>
public QuantumInstrumentInfo[] quantumInstruments;
}
}

View File

@ -0,0 +1,45 @@
using NewHorizons.External.Modules.Props.Dialogue;
using Newtonsoft.Json;
namespace NewHorizons.External.Modules.Props.EyeOfTheUniverse
{
[JsonObject]
public class EyeTravelerInfo : DetailInfo
{
/// <summary>
/// A unique ID to associate this traveler with their corresponding quantum instruments and instrument zones. Must be unique for each traveler.
/// </summary>
public string id;
/// <summary>
/// The dialogue condition that will trigger the traveler to start playing their instrument. Must be unique for each traveler.
/// </summary>
public string startPlayingCondition;
/// <summary>
/// If specified, this dialogue condition must be set for the traveler to participate in the campfire song. Otherwise, the song will be able to start without them.
/// </summary>
public string participatingCondition;
/// <summary>
/// The audio to use for the traveler while playing around the campfire (and also for their paired quantum instrument). It should be 16 measures at 92 BPM (approximately 42 seconds long). Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
/// </summary>
public string loopAudio;
/// <summary>
/// The audio to use for the traveler during the finale of the campfire song. It should be 8 measures of the main loop at 92 BPM followed by 2 measures of fade-out (approximately 26 seconds long in total). Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
/// </summary>
public string finaleAudio;
/// <summary>
/// The frequency ID of the signal emitted by the traveler. The built-in game values are `Default`, `Traveler`, `Quantum`, `EscapePod`,
/// `Statue`, `WarpCore`, `HideAndSeek`, and `Radio`. Defaults to `Traveler`. You can also put a custom value.
/// </summary>
public string frequency;
/// <summary>
/// The dialogue to use for this traveler. Omit this or set it to null if your traveler already has valid dialogue.
/// </summary>
public DialogueInfo dialogue;
}
}

View File

@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace NewHorizons.External.Modules.Props.EyeOfTheUniverse
{
[JsonObject]
public class InstrumentZoneInfo : DetailInfo
{
/// <summary>
/// The unique ID of the Eye Traveler associated with this instrument zone.
/// </summary>
public string id;
}
}

View File

@ -0,0 +1,34 @@
using Newtonsoft.Json;
using System.ComponentModel;
namespace NewHorizons.External.Modules.Props.EyeOfTheUniverse
{
[JsonObject]
public class QuantumInstrumentInfo : DetailInfo
{
/// <summary>
/// The unique ID of the Eye Traveler associated with this quantum instrument.
/// </summary>
public string id;
/// <summary>
/// A dialogue condition to set when gathering this quantum instrument. Use it in conjunction with `activationCondition` or `deactivationCondition` on other details.
/// </summary>
public string gatherCondition;
/// <summary>
/// Allows gathering this quantum instrument using the zoomed-in signalscope, like Chert's bongos.
/// </summary>
public bool gatherWithScope;
/// <summary>
/// The radius of the added sphere collider that will be used for interaction.
/// </summary>
[DefaultValue(0.5f)] public float interactRadius = 0.5f;
/// <summary>
/// The furthest distance where the player can interact with this quantum instrument.
/// </summary>
[DefaultValue(2f)] public float interactRange = 2f;
}
}

View File

@ -1,14 +1,57 @@
using NewHorizons.Builder.General;
using NewHorizons.Components.EyeOfTheUniverse;
using NewHorizons.Components.Stars;
using NewHorizons.External.Modules.Props.EyeOfTheUniverse;
using NewHorizons.External.SerializableData;
using NewHorizons.Utility;
using NewHorizons.Utility.OWML;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace NewHorizons.Handlers
{
public static class EyeSceneHandler
{
private static Dictionary<string, EyeTravelerData> _eyeTravelers = new();
private static EyeMusicController _eyeMusicController;
public static void Init()
{
_eyeTravelers.Clear();
_eyeMusicController = null;
}
public static EyeMusicController GetMusicController()
{
return _eyeMusicController;
}
public static EyeTravelerData GetOrCreateEyeTravelerData(string id)
{
if (_eyeTravelers.TryGetValue(id, out EyeTravelerData traveler))
{
return traveler;
}
traveler = new EyeTravelerData()
{
id = id,
info = null,
controller = null,
loopAudioSource = null,
finaleAudioSource = null,
instrumentZones = new(),
quantumInstruments = new(),
};
_eyeTravelers[traveler.id] = traveler;
return traveler;
}
public static List<EyeTravelerData> GetCustomEyeTravelers()
{
return _eyeTravelers.Values.ToList();
}
public static void OnSceneLoad()
{
// Create astro objects for eye and vessel because they didn't have them somehow.
@ -134,5 +177,129 @@ namespace NewHorizons.Handlers
SunLightEffectsController.AddStar(starController);
SunLightEffectsController.AddStarLight(sunLight);
}
public static void SetUpEyeCampfireSequence()
{
_eyeMusicController = new GameObject("EyeMusicController").AddComponent<EyeMusicController>();
var quantumCampsiteController = Object.FindObjectOfType<QuantumCampsiteController>();
var cosmicInflationController = Object.FindObjectOfType<CosmicInflationController>();
_eyeMusicController.RegisterFinaleSource(cosmicInflationController._travelerFinaleSource);
foreach (var controller in quantumCampsiteController._travelerControllers)
{
_eyeMusicController.RegisterLoopSource(controller._signal.GetOWAudioSource());
}
foreach (var eyeTraveler in _eyeTravelers.Values)
{
if (eyeTraveler.controller != null)
{
ArrayHelpers.Append(ref quantumCampsiteController._travelerControllers, eyeTraveler.controller);
eyeTraveler.controller.OnStartPlaying += quantumCampsiteController.OnTravelerStartPlaying;
ArrayHelpers.Append(ref cosmicInflationController._travelers, eyeTraveler.controller);
eyeTraveler.controller.OnStartPlaying += cosmicInflationController.OnTravelerStartPlaying;
ArrayHelpers.Append(ref cosmicInflationController._inflationObjects, eyeTraveler.controller.transform);
}
else
{
NHLogger.LogError($"Missing Eye Traveler for ID \"{eyeTraveler.id}\"");
}
if (eyeTraveler.loopAudioSource != null)
{
_eyeMusicController.RegisterLoopSource(eyeTraveler.loopAudioSource);
}
if (eyeTraveler.finaleAudioSource != null)
{
_eyeMusicController.RegisterFinaleSource(eyeTraveler.finaleAudioSource);
}
foreach (var quantumInstrument in eyeTraveler.quantumInstruments)
{
ArrayHelpers.Append(ref quantumInstrument._activateObjects, eyeTraveler.controller.gameObject);
ArrayHelpers.Append(ref quantumInstrument._deactivateObjects, eyeTraveler.instrumentZones.Select(z => z.gameObject));
var ancestorInstrumentZone = quantumInstrument.GetComponentInParent<InstrumentZone>();
if (ancestorInstrumentZone == null)
{
// Quantum instrument is not a child of an instrument zone, so treat it like its own zone
ArrayHelpers.Append(ref quantumCampsiteController._instrumentZones, quantumInstrument.gameObject);
}
}
foreach (var instrumentZone in eyeTraveler.instrumentZones)
{
instrumentZone.gameObject.SetActive(false);
ArrayHelpers.Append(ref quantumCampsiteController._instrumentZones, instrumentZone.gameObject);
}
}
}
public static void UpdateTravelerPositions()
{
//if (!GetCustomEyeTravelers().Any()) return;
var quantumCampsiteController = Object.FindObjectOfType<QuantumCampsiteController>();
var travelers = new List<Transform>()
{
quantumCampsiteController._travelerControllers[0].transform, // Riebeck
quantumCampsiteController._travelerControllers[2].transform, // Chert
quantumCampsiteController._travelerControllers[6].transform, // Esker
quantumCampsiteController._travelerControllers[1].transform, // Felspar
quantumCampsiteController._travelerControllers[3].transform, // Gabbro
};
if (quantumCampsiteController._hasMetSolanum)
{
travelers.Add(quantumCampsiteController._travelerControllers[4].transform); // Solanum
}
if (quantumCampsiteController._hasMetPrisoner)
{
travelers.Add(quantumCampsiteController._travelerControllers[5].transform); // Prisoner
}
// Custom travelers (starting at index 7)
for (int i = 7; i < quantumCampsiteController._travelerControllers.Length; i++)
{
travelers.Add(quantumCampsiteController._travelerControllers[i].transform);
}
var radius = 2f + 0.2f * travelers.Count;
var angle = Mathf.PI * 2f / travelers.Count;
var index = 0;
foreach (var traveler in travelers)
{
// Esker isn't at height 0 so we have to do all this
var initialY = traveler.transform.position.y;
var newPos = quantumCampsiteController.transform.TransformPoint(new Vector3(
Mathf.Cos(angle * index) * radius,
0f,
-Mathf.Sin(angle * index) * radius
));
newPos.y = initialY;
traveler.transform.position = newPos;
var lookTarget = quantumCampsiteController.transform.position;
lookTarget.y = newPos.y;
traveler.transform.LookAt(lookTarget, traveler.transform.up);
index++;
}
}
public class EyeTravelerData
{
public string id;
public EyeTravelerInfo info;
public TravelerEyeController controller;
public OWAudioSource loopAudioSource;
public OWAudioSource finaleAudioSource;
public List<QuantumInstrument> quantumInstruments = new();
public List<InstrumentZone> instrumentZones = new();
}
}
}

View File

@ -691,6 +691,11 @@ namespace NewHorizons.Handlers
atmosphere = AtmosphereBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize).GetComponentInChildren<LODGroup>();
}
if (body.Config.EyeOfTheUniverse != null)
{
EyeOfTheUniverseBuilder.Make(go, sector, body.Config.EyeOfTheUniverse, body);
}
if (body.Config.ParticleFields != null)
{
EffectsBuilder.Make(go, sector, body.Config);

View File

@ -436,7 +436,9 @@ namespace NewHorizons
if (isEyeOfTheUniverse)
{
_playerAwake = true;
EyeSceneHandler.Init();
EyeSceneHandler.OnSceneLoad();
EyeSceneHandler.SetUpEyeCampfireSequence();
}
if (isSolarSystem || isEyeOfTheUniverse)

View File

@ -0,0 +1,23 @@
using HarmonyLib;
using NewHorizons.Handlers;
using NewHorizons.Utility.OWML;
namespace NewHorizons.Patches.EyeScenePatches
{
[HarmonyPatch(typeof(CosmicInflationController))]
public static class CosmicInflationControllerPatches
{
[HarmonyPostfix]
[HarmonyPatch(nameof(CosmicInflationController.UpdateFormation))]
public static void CosmicInflationController_UpdateFormation(CosmicInflationController __instance)
{
if (__instance._waitForCrossfade)
{
NHLogger.Log($"Hijacking finale cross-fade, NH will handle it");
__instance._waitForCrossfade = false;
__instance._waitForMusicEnd = true;
EyeSceneHandler.GetMusicController().TransitionToFinale();
}
}
}
}

View File

@ -0,0 +1,85 @@
using HarmonyLib;
using NewHorizons.Handlers;
using NewHorizons.Utility.OWML;
using System.Linq;
namespace NewHorizons.Patches.EyeScenePatches
{
[HarmonyPatch(typeof(QuantumCampsiteController))]
public static class QuantumCampsiteControllerPatches
{
[HarmonyPostfix]
[HarmonyPatch(nameof(QuantumCampsiteController.Start))]
public static void QuantumCampsiteController_Start()
{
EyeSceneHandler.UpdateTravelerPositions();
}
[HarmonyPostfix]
[HarmonyPatch(nameof(QuantumCampsiteController.ActivateRemainingInstrumentZones))]
public static void QuantumCampsiteController_ActivateRemainingInstrumentZones(QuantumCampsiteController __instance)
{
// We modify this array when registering a custom instrument zone but the vanilla method only activates the first 6
for (int i = 6; i < __instance._instrumentZones.Length; i++)
{
__instance._instrumentZones[i].SetActive(true);
}
}
[HarmonyPrefix]
[HarmonyPatch(nameof(QuantumCampsiteController.AreAllTravelersGathered))]
public static bool QuantumCampsiteController_AreAllTravelersGathered(QuantumCampsiteController __instance, ref bool __result)
{
bool gatheredAllHearthianTravelers = __instance._travelerControllers.Take(4).All(t => t.gameObject.activeInHierarchy);
if (!gatheredAllHearthianTravelers)
{
NHLogger.LogVerbose("");
__result = false;
return false;
}
bool needsSolanum = __instance._hasMetSolanum;
bool gatheredSolanum = __instance._travelerControllers[QuantumCampsiteController.SOLANUM_INDEX].gameObject.activeInHierarchy;
if (needsSolanum && !gatheredSolanum)
{
__result = false;
return false;
}
bool needsPrisoner = __instance._hasMetPrisoner && !__instance._hasErasedPrisoner;
bool gatheredPrisoner = __instance._travelerControllers[QuantumCampsiteController.PRISONER_INDEX].gameObject.activeInHierarchy;
if (needsPrisoner && !gatheredPrisoner)
{
__result = false;
return false;
}
foreach (var traveler in EyeSceneHandler.GetCustomEyeTravelers())
{
bool needsTraveler = true;
if (!string.IsNullOrEmpty(traveler.info.participatingCondition))
{
needsTraveler = DialogueConditionManager.SharedInstance.GetConditionState(traveler.info.participatingCondition);
}
bool gatheredTraveler = traveler.controller.gameObject.activeInHierarchy;
if (needsTraveler && !gatheredTraveler)
{
__result = false;
return false;
}
}
__result = true;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(QuantumCampsiteController.OnTravelerStartPlaying))]
public static void OnTravelerStartPlaying(QuantumCampsiteController __instance)
{
if (!__instance._hasJamSessionStarted)
{
NHLogger.Log($"NH is handling Eye sequence music");
// Jam session is starting, start our custom music handler
EyeSceneHandler.GetMusicController().StartPlaying();
}
// Letting the original method run in case mods have patched TravelerEyeController.OnStartCosmicJamSession()
}
}
}

View File

@ -0,0 +1,17 @@
using HarmonyLib;
namespace NewHorizons.Patches.EyeScenePatches
{
[HarmonyPatch(typeof(TravelerEyeController))]
public static class TravelerEyeControllerPatches
{
[HarmonyPrefix]
[HarmonyPatch(nameof(TravelerEyeController.OnStartCosmicJamSession))]
public static bool TravelerEyeController_OnStartCosmicJamSession(TravelerEyeController __instance)
{
// Not starting the loop audio here; EyeMusicController will handle that
__instance._signal.GetOWAudioSource().SetLocalVolume(0f);
return false;
}
}
}