mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Eye sequence fixes
This commit is contained in:
parent
63fbc8afeb
commit
e0f79ae3ea
@ -18,6 +18,11 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
var go = DetailBuilder.Make(planetGO, sector, nhBody.Mod, info);
|
||||
|
||||
if (string.IsNullOrEmpty(info.name))
|
||||
{
|
||||
info.name = go.name;
|
||||
}
|
||||
|
||||
var travelerController = go.GetAddComponent<TravelerEyeController>();
|
||||
if (!string.IsNullOrEmpty(info.startPlayingCondition))
|
||||
{
|
||||
@ -34,6 +39,8 @@ namespace NewHorizons.Builder.Props
|
||||
if (info.dialogue != null)
|
||||
{
|
||||
var (dialogueTree, remoteTrigger) = DialogueBuilder.Make(planetGO, sector, info.dialogue, nhBody.Mod);
|
||||
dialogueTree.transform.SetParent(travelerController.transform, false);
|
||||
dialogueTree.transform.localPosition = Vector3.zero;
|
||||
if (travelerController._dialogueTree != null)
|
||||
{
|
||||
travelerController._dialogueTree.OnStartConversation -= travelerController.OnStartConversation;
|
||||
@ -54,17 +61,20 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
var signalInfo = new SignalInfo()
|
||||
{
|
||||
name = info.name,
|
||||
audio = info.loopAudio,
|
||||
detectionRadius = 0,
|
||||
detectionRadius = 10f,
|
||||
identificationRadius = 10f,
|
||||
onlyAudibleToScope = false,
|
||||
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);
|
||||
signalGO.transform.SetParent(travelerController.transform, false);
|
||||
signalGO.transform.localPosition = Vector3.zero;
|
||||
|
||||
var signal = signalGO.GetComponent<AudioSignal>();
|
||||
travelerController._signal = signal;
|
||||
signal.SetSignalActivation(false);
|
||||
loopAudioSource = signal.GetOWAudioSource();
|
||||
}
|
||||
else if (travelerController._signal == null)
|
||||
@ -80,11 +90,14 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
audio = info.finaleAudio,
|
||||
track = External.SerializableEnums.NHAudioMixerTrackName.Music,
|
||||
volume = 1f,
|
||||
};
|
||||
finaleAudioSource = GeneralAudioBuilder.Make(planetGO, sector, finaleAudioInfo, nhBody.Mod);
|
||||
finaleAudioSource.SetTrack(finaleAudioInfo.track.ConvertToOW());
|
||||
finaleAudioSource.loop = false;
|
||||
finaleAudioSource.spatialBlend = 0f;
|
||||
finaleAudioSource.playOnAwake = false;
|
||||
finaleAudioSource.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
var travelerData = EyeSceneHandler.GetOrCreateEyeTravelerData(info.id);
|
||||
@ -111,6 +124,7 @@ namespace NewHorizons.Builder.Props
|
||||
go.GetAddComponent<InteractReceiver>();
|
||||
var quantumInstrument = go.GetAddComponent<QuantumInstrument>();
|
||||
quantumInstrument._gatherWithScope = info.gatherWithScope;
|
||||
ArrayHelpers.Append(ref quantumInstrument._deactivateObjects, go);
|
||||
|
||||
var trigger = go.AddComponent<QuantumInstrumentTrigger>();
|
||||
trigger.gatherCondition = info.gatherCondition;
|
||||
@ -124,15 +138,15 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
var signalInfo = new SignalInfo()
|
||||
{
|
||||
name = travelerData.info.name,
|
||||
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);
|
||||
signalGO.transform.SetParent(quantumInstrument.transform, false);
|
||||
signalGO.transform.localPosition = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -16,6 +17,7 @@ namespace NewHorizons.Components.EyeOfTheUniverse
|
||||
{
|
||||
src.loop = false;
|
||||
src.SetLocalVolume(1f);
|
||||
src.Stop();
|
||||
_loopSources.Add(src);
|
||||
}
|
||||
|
||||
@ -23,6 +25,7 @@ namespace NewHorizons.Components.EyeOfTheUniverse
|
||||
{
|
||||
src.loop = false;
|
||||
src.SetLocalVolume(1f);
|
||||
src.Stop();
|
||||
_finaleSources.Add(src);
|
||||
}
|
||||
|
||||
@ -36,61 +39,85 @@ namespace NewHorizons.Components.EyeOfTheUniverse
|
||||
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;
|
||||
var cosmicInflationController = FindObjectOfType<CosmicInflationController>();
|
||||
|
||||
// Track when the next audio (loop or finale) should play, in audio system time
|
||||
double nextAudioEventTime = AudioSettings.dspTime + timeBufferWindow;
|
||||
// Schedule finale for as soon as the current segment loop ends
|
||||
double finaleAudioTime = _segmentEndAudioTime;
|
||||
float finaleGameTime = _segmentEndGameTime;
|
||||
|
||||
// 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;
|
||||
// Cancel loop audio
|
||||
foreach (var loopSrc in _loopSources)
|
||||
{
|
||||
loopSrc._audioSource.PlayScheduled(loopStartTime);
|
||||
loopSrc._audioSource.SetScheduledEndTime(finaleAudioTime);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Set quantum sphere inflation timer
|
||||
var finaleDuration = cosmicInflationController._travelerFinaleSource.clip.length;
|
||||
cosmicInflationController._startFormationTime = Time.time;
|
||||
cosmicInflationController._finishFormationTime = finaleGameTime + finaleDuration - 4f;
|
||||
|
||||
// Play finale in sync
|
||||
foreach (var finaleSrc in _finaleSources)
|
||||
{
|
||||
finaleSrc._audioSource.PlayScheduled(nextAudioEventTime);
|
||||
finaleSrc._audioSource.PlayScheduled(finaleAudioTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Delay between game logic and audio to ensure audio system has time to schedule all loops for the same tick
|
||||
const double TIME_BUFFER_WINDOW = 0.5;
|
||||
|
||||
private double _segmentEndAudioTime;
|
||||
private float _segmentEndGameTime;
|
||||
|
||||
private IEnumerator DoLoop()
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Vanilla audio divides the loop into 4 segments, but that actually causes weird key shifting during the crossfade
|
||||
int segmentCount = 2;
|
||||
double segmentDuration = loopDuration / segmentCount;
|
||||
|
||||
// Track when the next loop will play, in both audio system time and game time
|
||||
double nextLoopAudioTime = AudioSettings.dspTime + TIME_BUFFER_WINDOW;
|
||||
float nextLoopGameTime = Time.time + (float)TIME_BUFFER_WINDOW;
|
||||
|
||||
while (!_transitionToFinale)
|
||||
{
|
||||
// Play loops in sync
|
||||
double loopStartAudioTime = nextLoopAudioTime;
|
||||
float loopStartGameTime = nextLoopGameTime;
|
||||
|
||||
foreach (var loopSrc in _loopSources)
|
||||
{
|
||||
if (!loopSrc.gameObject.activeInHierarchy) continue;
|
||||
if (loopSrc.loop) continue;
|
||||
// We only need to schedule once and then Unity will loop it for us
|
||||
loopSrc._audioSource.PlayScheduled(loopStartAudioTime);
|
||||
loopSrc.loop = true;
|
||||
}
|
||||
|
||||
// Schedule next loop
|
||||
nextLoopAudioTime += loopDuration;
|
||||
nextLoopGameTime += (float)loopDuration;
|
||||
|
||||
// Track loop segment timing (the current musical verse should always finish playing before the finale)
|
||||
for (int i = 0; i < segmentCount; i++)
|
||||
{
|
||||
_segmentEndAudioTime = loopStartAudioTime + segmentDuration * (i + 1);
|
||||
_segmentEndGameTime = loopStartGameTime + (float)(segmentDuration * (i + 1));
|
||||
|
||||
// Wait until the next segment
|
||||
while (Time.time < _segmentEndGameTime && !_transitionToFinale)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Interrupt the remaining segments for the finale
|
||||
if (_transitionToFinale) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,11 @@ namespace NewHorizons.External.Modules.Props.EyeOfTheUniverse
|
||||
/// </summary>
|
||||
public string id;
|
||||
|
||||
/// <summary>
|
||||
/// The name to display for this traveler's signals. Defaults to the name of the detail.
|
||||
/// </summary>
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
/// The dialogue condition that will trigger the traveler to start playing their instrument. Must be unique for each traveler.
|
||||
/// </summary>
|
||||
|
||||
@ -19,6 +19,36 @@ public static class EyeDetailCacher
|
||||
foreach (var body in Main.BodyDict["EyeOfTheUniverse"])
|
||||
{
|
||||
NHLogger.LogVerbose($"{nameof(EyeDetailCacher)}: {body.Config.name}");
|
||||
if (body.Config?.EyeOfTheUniverse?.eyeTravelers != null)
|
||||
{
|
||||
foreach (var detail in body.Config.EyeOfTheUniverse.eyeTravelers)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(detail.assetBundle)) continue;
|
||||
|
||||
AddPathToCache(detail.path);
|
||||
}
|
||||
}
|
||||
|
||||
if (body.Config?.EyeOfTheUniverse?.instrumentZones != null)
|
||||
{
|
||||
foreach (var detail in body.Config.EyeOfTheUniverse.instrumentZones)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(detail.assetBundle)) continue;
|
||||
|
||||
AddPathToCache(detail.path);
|
||||
}
|
||||
}
|
||||
|
||||
if (body.Config?.EyeOfTheUniverse?.quantumInstruments != null)
|
||||
{
|
||||
foreach (var detail in body.Config.EyeOfTheUniverse.quantumInstruments)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(detail.assetBundle)) continue;
|
||||
|
||||
AddPathToCache(detail.path);
|
||||
}
|
||||
}
|
||||
|
||||
if (body.Config?.Props?.details != null)
|
||||
{
|
||||
foreach (var detail in body.Config.Props.details)
|
||||
|
||||
@ -180,6 +180,8 @@ namespace NewHorizons.Handlers
|
||||
|
||||
public static void SetUpEyeCampfireSequence()
|
||||
{
|
||||
if (!GetCustomEyeTravelers().Any()) return;
|
||||
|
||||
_eyeMusicController = new GameObject("EyeMusicController").AddComponent<EyeMusicController>();
|
||||
|
||||
var quantumCampsiteController = Object.FindObjectOfType<QuantumCampsiteController>();
|
||||
@ -196,6 +198,8 @@ namespace NewHorizons.Handlers
|
||||
{
|
||||
if (eyeTraveler.controller != null)
|
||||
{
|
||||
eyeTraveler.controller.gameObject.SetActive(false);
|
||||
|
||||
ArrayHelpers.Append(ref quantumCampsiteController._travelerControllers, eyeTraveler.controller);
|
||||
eyeTraveler.controller.OnStartPlaying += quantumCampsiteController.OnTravelerStartPlaying;
|
||||
|
||||
@ -211,6 +215,7 @@ namespace NewHorizons.Handlers
|
||||
|
||||
if (eyeTraveler.loopAudioSource != null)
|
||||
{
|
||||
eyeTraveler.loopAudioSource.GetComponent<AudioSignal>().SetSignalActivation(false);
|
||||
_eyeMusicController.RegisterLoopSource(eyeTraveler.loopAudioSource);
|
||||
}
|
||||
if (eyeTraveler.finaleAudioSource != null)
|
||||
@ -227,6 +232,7 @@ namespace NewHorizons.Handlers
|
||||
if (ancestorInstrumentZone == null)
|
||||
{
|
||||
// Quantum instrument is not a child of an instrument zone, so treat it like its own zone
|
||||
quantumInstrument.gameObject.SetActive(false);
|
||||
ArrayHelpers.Append(ref quantumCampsiteController._instrumentZones, quantumInstrument.gameObject);
|
||||
}
|
||||
}
|
||||
@ -237,11 +243,13 @@ namespace NewHorizons.Handlers
|
||||
ArrayHelpers.Append(ref quantumCampsiteController._instrumentZones, instrumentZone.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTravelerPositions();
|
||||
}
|
||||
|
||||
public static void UpdateTravelerPositions()
|
||||
{
|
||||
//if (!GetCustomEyeTravelers().Any()) return;
|
||||
if (!GetCustomEyeTravelers().Any()) return;
|
||||
|
||||
var quantumCampsiteController = Object.FindObjectOfType<QuantumCampsiteController>();
|
||||
|
||||
|
||||
@ -438,7 +438,6 @@ namespace NewHorizons
|
||||
_playerAwake = true;
|
||||
EyeSceneHandler.Init();
|
||||
EyeSceneHandler.OnSceneLoad();
|
||||
EyeSceneHandler.SetUpEyeCampfireSequence();
|
||||
}
|
||||
|
||||
if (isSolarSystem || isEyeOfTheUniverse)
|
||||
@ -537,6 +536,8 @@ namespace NewHorizons
|
||||
IsWarpingFromVessel = false;
|
||||
DidWarpFromVessel = false;
|
||||
DidWarpFromShip = false;
|
||||
|
||||
EyeSceneHandler.SetUpEyeCampfireSequence();
|
||||
}
|
||||
|
||||
//Stop starfield from disappearing when there is no lights
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using HarmonyLib;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System.Linq;
|
||||
|
||||
namespace NewHorizons.Patches.EyeScenePatches
|
||||
{
|
||||
@ -11,7 +12,7 @@ namespace NewHorizons.Patches.EyeScenePatches
|
||||
[HarmonyPatch(nameof(CosmicInflationController.UpdateFormation))]
|
||||
public static void CosmicInflationController_UpdateFormation(CosmicInflationController __instance)
|
||||
{
|
||||
if (__instance._waitForCrossfade)
|
||||
if (__instance._waitForCrossfade && EyeSceneHandler.GetCustomEyeTravelers().Any())
|
||||
{
|
||||
NHLogger.Log($"Hijacking finale cross-fade, NH will handle it");
|
||||
__instance._waitForCrossfade = false;
|
||||
|
||||
@ -30,10 +30,13 @@ namespace NewHorizons.Patches.EyeScenePatches
|
||||
[HarmonyPatch(nameof(QuantumCampsiteController.AreAllTravelersGathered))]
|
||||
public static bool QuantumCampsiteController_AreAllTravelersGathered(QuantumCampsiteController __instance, ref bool __result)
|
||||
{
|
||||
if (!EyeSceneHandler.GetCustomEyeTravelers().Any())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool gatheredAllHearthianTravelers = __instance._travelerControllers.Take(4).All(t => t.gameObject.activeInHierarchy);
|
||||
if (!gatheredAllHearthianTravelers)
|
||||
{
|
||||
NHLogger.LogVerbose("");
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
@ -73,7 +76,7 @@ namespace NewHorizons.Patches.EyeScenePatches
|
||||
[HarmonyPatch(nameof(QuantumCampsiteController.OnTravelerStartPlaying))]
|
||||
public static void OnTravelerStartPlaying(QuantumCampsiteController __instance)
|
||||
{
|
||||
if (!__instance._hasJamSessionStarted)
|
||||
if (!__instance._hasJamSessionStarted && EyeSceneHandler.GetCustomEyeTravelers().Any())
|
||||
{
|
||||
NHLogger.Log($"NH is handling Eye sequence music");
|
||||
// Jam session is starting, start our custom music handler
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
using HarmonyLib;
|
||||
using NewHorizons.Handlers;
|
||||
using System.Linq;
|
||||
|
||||
namespace NewHorizons.Patches.EyeScenePatches
|
||||
{
|
||||
@ -9,6 +11,10 @@ namespace NewHorizons.Patches.EyeScenePatches
|
||||
[HarmonyPatch(nameof(TravelerEyeController.OnStartCosmicJamSession))]
|
||||
public static bool TravelerEyeController_OnStartCosmicJamSession(TravelerEyeController __instance)
|
||||
{
|
||||
if (!EyeSceneHandler.GetCustomEyeTravelers().Any())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Not starting the loop audio here; EyeMusicController will handle that
|
||||
__instance._signal.GetOWAudioSource().SetLocalVolume(0f);
|
||||
return false;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user