From 064f9ffef13e2c23a37da48d06eeefc3afbaf63e Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 14:00:31 -0500 Subject: [PATCH 01/21] I can't tell if this works or not --- .../Builder/Props/ProjectionBuilder.cs | 16 +- .../Components/EOTE/NHSlideCollection.cs | 70 +++++++++ .../EOTE/NHSlideCollectionContainer.cs | 145 +++++++++++++++++- 3 files changed, 219 insertions(+), 12 deletions(-) create mode 100644 NewHorizons/Components/EOTE/NHSlideCollection.cs diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 7df34b6c..30baf64a 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -140,6 +140,8 @@ namespace NewHorizons.Builder.Props var toDestroy = slideReelObj.GetComponent(); var slideCollectionContainer = slideReelObj.AddComponent(); + slideCollectionContainer.slidePaths = info.slides.Select(x => x.imagePath).ToArray(); + slideCollectionContainer.mod = mod; slideReel._slideCollectionContainer = slideCollectionContainer; Component.DestroyImmediate(toDestroy); @@ -156,22 +158,16 @@ namespace NewHorizons.Builder.Props // We can fit 16 slides max into an atlas var textures = new Texture2D[slidesCount > 16 ? 16 : slidesCount]; - var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, true); + // Don't load inverted images if the cache exists, in this case we only load when actively displaying stuff + var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, !CacheExists(mod), true); // If the cache doesn't exist it will be created here, slide reels only use the base image loader for cache creation so delete the images after to free memory imageLoader.deleteTexturesWhenDone = !CacheExists(mod); var key = GetUniqueSlideReelID(mod, info.slides); - if (invImageLoader != null && atlasImageLoader != null) + if (CacheExists(mod) && atlasImageLoader != null) { - // Loading directly from cache - invImageLoader.imageLoadedEvent.AddListener( - (Texture2D tex, int index, string originalPath) => - { - slideCollection.slides[index]._image = tex; - } - ); atlasImageLoader.imageLoadedEvent.AddListener( (Texture2D tex, int _, string originalPath) => { @@ -353,7 +349,7 @@ namespace NewHorizons.Builder.Props // Now we replace the slides int slidesCount = info.slides.Length; - var slideCollection = new SlideCollection(slidesCount); + SlideCollection slideCollection = new NHSlideCollection(slidesCount, mod, info.slides.Select(x => x.imagePath).ToArray()); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false); diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs new file mode 100644 index 00000000..a5e357af --- /dev/null +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -0,0 +1,70 @@ +using HarmonyLib; +using NewHorizons.Utility.Files; +using OWML.Common; +using UnityEngine; + +namespace NewHorizons.Components.EOTE; + +[HarmonyPatch] +public class NHSlideCollection : SlideCollection +{ + + public string[] slidePaths; + public IModBehaviour mod; + + public NHSlideCollection(int startArrSize, IModBehaviour mod, string[] slidePaths) : base(startArrSize) + { + this.mod = mod; + this.slidePaths = slidePaths; + } + + /* + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.RequestStreamSlides))] + public static bool SlideCollection_RequestStreamSlides(SlideCollection __instance, int[] slideIndices) + { + if (__instance is NHSlideCollection collection) + { + foreach (var id in slideIndices) + { + collection.slides[id]._image = ImageUtilities.GetTexture(collection.mod, collection.slidePaths[id]); + } + return false; + } + else + { + return true; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.IsStreamedTextureIndexLoaded))] + public static bool SlideCollection_IsStreamedTextureIndexLoaded(SlideCollection __instance, int streamIdx, ref bool __result) + { + if (__instance is NHSlideCollection collection) + { + __result = ImageUtilities.IsTextureLoaded(collection.mod, collection.slidePaths[streamIdx]); + return false; + } + else + { + return true; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.GetStreamingTexture))] + public static bool SlideCollection_GetStreamingTexture(SlideCollection __instance, int id, ref Texture __result) + { + if (__instance is NHSlideCollection collection) + { + __result = ImageUtilities.GetTexture(collection.mod, collection.slidePaths[id]); + return false; + } + else + { + return true; + } + } + */ +} diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index bcf979ec..5ce38910 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -1,4 +1,12 @@ using HarmonyLib; +using NewHorizons.Builder.Props; +using NewHorizons.External.Modules.Props.EchoesOfTheEye; +using NewHorizons.Utility.Files; +using OWML.Common; +using System; +using System.IO; +using System.Linq; +using UnityEngine; namespace NewHorizons.Components.EOTE; @@ -7,12 +15,14 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { public string[] conditionsToSet; public string[] persistentConditionsToSet; + public string[] slidePaths; + public IModBehaviour mod; [HarmonyPrefix] [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.Initialize))] public static bool SlideCollectionContainer_Initialize(SlideCollectionContainer __instance) { - if (__instance is NHSlideCollectionContainer) + if (__instance is NHSlideCollectionContainer container) { if (__instance._initialized) return false; @@ -24,7 +34,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer __instance._changeSlidesAllowed = true; __instance._initialized = true; __instance._slideCollection.isVision = __instance._owningItem == null; - foreach (var factID in __instance._playWithShipLogFacts) + foreach (var factID in __instance._playWithShipLogFacts ?? Array.Empty()) { var fact = Locator.GetShipLogManager().GetFact(factID); fact?.RegisterSlideCollection(__instance._slideCollection); @@ -59,4 +69,135 @@ public class NHSlideCollectionContainer : SlideCollectionContainer } } } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.NextSlideAvailable))] + public static bool SlideCollectionContainer_NextSlideAvailable(SlideCollectionContainer __instance, ref bool __result) + { + if (__instance is NHSlideCollectionContainer container) + { + __result = container.IsSlideLoaded(container.slideIndex + 1); + return false; + } + else + { + return true; + } + } + + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.PrevSlideAvailable))] + public static bool SlideCollectionContainer_PrevSlideAvailable(SlideCollectionContainer __instance, ref bool __result) + { + if (__instance is NHSlideCollectionContainer container) + { + __result = container.IsSlideLoaded(container.slideIndex - 1); + return false; + } + else + { + return true; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.UnloadStreamingTextures))] + public static bool SlideCollectionContainer_UnloadStreamingTextures(SlideCollectionContainer __instance) + { + if (__instance is NHSlideCollectionContainer container) + { + for (int i = 0; i < container.slidePaths.Length; i++) + { + container.UnloadSlide(i); + } + return false; + } + else + { + return true; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.GetStreamingTexture))] + public static bool SlideCollectionContainer_GetStreamingTexture(SlideCollectionContainer __instance, int id, ref Texture __result) + { + if (__instance is NHSlideCollectionContainer container) + { + __result = container.LoadSlide(id); + return false; + } + else + { + return true; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.RequestManualStreamSlides))] + public static bool SlideCollectionContainer_RequestManualStreamSlides(SlideCollectionContainer __instance) + { + if (__instance is NHSlideCollectionContainer container) + { + container.LoadSlide(__instance._currentSlideIndex); + return false; + } + else + { + return true; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.streamingTexturesAvailable), MethodType.Getter)] + public static bool SlideCollectionContainer_streamingTexturesAvailable(SlideCollectionContainer __instance, ref bool __result) + { + if (__instance is NHSlideCollectionContainer container) + { + __result = container.slidePaths != null && container.slidePaths.Any(); + return false; + } + else + { + return true; + } + } + + public Texture LoadSlide(int index) + { + Texture LoadSlideInt(int index) + { + var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + + var texture = ImageUtilities.GetTexture(mod, path); + this.slideCollection.slides[wrappedIndex]._image = texture; + return texture; + } + var texture = LoadSlideInt(index); + LoadSlideInt(index - 1); + LoadSlideInt(index + 1); + + return texture; + } + + public bool IsSlideLoaded(int index) + { + var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + return ImageUtilities.IsTextureLoaded(mod, path); + } + + public void UnloadSlide(int index) + { + var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + + if (ImageUtilities.IsTextureLoaded(mod, path)) + { + ImageUtilities.DeleteTexture(mod, path, ImageUtilities.GetTexture(mod, path)); + slideCollection.slides[wrappedIndex]._image = null; + } + } } From 1191cbaef88fe6abcf9809a47e9cf9992169a8b1 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 14:35:30 -0500 Subject: [PATCH 02/21] Fix that it was always loaded the raw slides --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 30baf64a..f58518c4 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -159,7 +159,7 @@ namespace NewHorizons.Builder.Props var textures = new Texture2D[slidesCount > 16 ? 16 : slidesCount]; // Don't load inverted images if the cache exists, in this case we only load when actively displaying stuff - var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, !CacheExists(mod), true); + var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, !CacheExists(mod), true, false); // If the cache doesn't exist it will be created here, slide reels only use the base image loader for cache creation so delete the images after to free memory imageLoader.deleteTexturesWhenDone = !CacheExists(mod); @@ -352,7 +352,7 @@ namespace NewHorizons.Builder.Props SlideCollection slideCollection = new NHSlideCollection(slidesCount, mod, info.slides.Select(x => x.imagePath).ToArray()); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false); + var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false, false); // Autoprojector only uses the inverted images so the original can be destroyed if they are loaded (when creating the cached inverted images) imageLoader.deleteTexturesWhenDone = true; @@ -412,7 +412,7 @@ namespace NewHorizons.Builder.Props var slideCollection = new SlideCollection(slidesCount); // TODO: uh I think that info.slides[i].playTimeDuration is not being read here... note to self for when I implement support for that: 0.7 is what to default to if playTimeDuration turns out to be 0 slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - var (_, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, false, false); + var (_, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, false, false, true); imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => { var time = DateTime.Now; @@ -465,7 +465,7 @@ namespace NewHorizons.Builder.Props var slideCollection = new SlideCollection(slidesCount); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - var (_, _, imageLoader) = StartAsyncLoader(mod, slides, ref slideCollection, false, false); + var (_, _, imageLoader) = StartAsyncLoader(mod, slides, ref slideCollection, false, false, true); // This variable just lets us track how many of the slides have been loaded. // This way as soon as the last one is loaded (due to async loading, this may be @@ -509,7 +509,7 @@ namespace NewHorizons.Builder.Props } private static (SlideReelAsyncImageLoader inverted, SlideReelAsyncImageLoader atlas, SlideReelAsyncImageLoader slides) - StartAsyncLoader(IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection, bool useInvertedCache, bool useAtlasCache) + StartAsyncLoader(IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection, bool useInvertedCache, bool useAtlasCache, bool loadRawImages) { var invertedImageLoader = new SlideReelAsyncImageLoader(); var atlasImageLoader = new SlideReelAsyncImageLoader(); @@ -549,12 +549,12 @@ namespace NewHorizons.Builder.Props } else { - if (useInvertedCache && cacheExists) + if (cacheExists && useInvertedCache) { // Load the inverted images used when displaying slide reels to a screen invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, InvertedSlideReelCacheFolder, slideInfo.imagePath))); } - else + if (loadRawImages) { imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); } From 407980f2d7bf76cd9a3e2114910a6922272f9811 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 14:36:03 -0500 Subject: [PATCH 03/21] Actually always have to load raw if no cache --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index f58518c4..ab146dda 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -554,7 +554,7 @@ namespace NewHorizons.Builder.Props // Load the inverted images used when displaying slide reels to a screen invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, InvertedSlideReelCacheFolder, slideInfo.imagePath))); } - if (loadRawImages) + if (!cacheExists || loadRawImages) { imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); } From 853e03cc997c6b85dab68da6927159ac27250693 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 14:54:14 -0500 Subject: [PATCH 04/21] Only unload textures once nobody is using them anymore --- .../EOTE/NHSlideCollectionContainer.cs | 36 +++++++++++++++++-- NewHorizons/Utility/Files/ImageUtilities.cs | 3 ++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index 5ce38910..edc954ca 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -2,11 +2,14 @@ using HarmonyLib; using NewHorizons.Builder.Props; using NewHorizons.External.Modules.Props.EchoesOfTheEye; using NewHorizons.Utility.Files; +using NewHorizons.Utility.OWML; using OWML.Common; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; +using UnityEngine.SceneManagement; namespace NewHorizons.Components.EOTE; @@ -18,6 +21,12 @@ public class NHSlideCollectionContainer : SlideCollectionContainer public string[] slidePaths; public IModBehaviour mod; + public static Dictionary> _slidesRequiringPath = new(); + static NHSlideCollectionContainer() + { + SceneManager.sceneUnloaded += (_) => _slidesRequiringPath.Clear(); + } + [HarmonyPrefix] [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.Initialize))] public static bool SlideCollectionContainer_Initialize(SlideCollectionContainer __instance) @@ -171,6 +180,22 @@ public class NHSlideCollectionContainer : SlideCollectionContainer var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + // We are the first slide collection container to try and load this image + var key = ImageUtilities.GetKey(mod, path); + if (!_slidesRequiringPath.ContainsKey(key)) + { + // Something else has loaded this image i.e., AutoProjector or Vision torch. We want to ensure we do not delete it + if (ImageUtilities.IsTextureLoaded(mod, path)) + { + _slidesRequiringPath[key] = new() { null }; + } + else + { + _slidesRequiringPath[key] = new(); + } + _slidesRequiringPath[key].Add(this); + } + var texture = ImageUtilities.GetTexture(mod, path); this.slideCollection.slides[wrappedIndex]._image = texture; return texture; @@ -194,10 +219,17 @@ public class NHSlideCollectionContainer : SlideCollectionContainer var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + // Only unload textures that we were the ones to load in if (ImageUtilities.IsTextureLoaded(mod, path)) { - ImageUtilities.DeleteTexture(mod, path, ImageUtilities.GetTexture(mod, path)); - slideCollection.slides[wrappedIndex]._image = null; + var key = ImageUtilities.GetKey(mod, path); + _slidesRequiringPath[key].Remove(this); + if (!_slidesRequiringPath[key].Any()) + { + NHLogger.LogVerbose($"Slide reel deleting {key} since nobody is using it anymore"); + ImageUtilities.DeleteTexture(mod, path, ImageUtilities.GetTexture(mod, path)); + slideCollection.slides[wrappedIndex]._image = null; + } } } } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index 2e660d17..cc258967 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -15,6 +15,9 @@ namespace NewHorizons.Utility.Files public static bool CheckCachedTexture(string key, out Texture existingTexture) => _textureCache.TryGetValue(key, out existingTexture); public static void TrackCachedTexture(string key, Texture texture) => _textureCache.Add(key, texture); // dont reinsert cuz that causes memory leak! + public static string GetKey(IModBehaviour mod, string filename) + => GetKey(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename)); + public static string GetKey(string path) => path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length + 1).Replace('\\', '/'); From 25fd6cd7617fbbf965542ad194c5026f8b501ec9 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 15:05:02 -0500 Subject: [PATCH 05/21] Make it async --- .../EOTE/NHSlideCollectionContainer.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index edc954ca..8b0fb6d7 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -21,6 +21,8 @@ public class NHSlideCollectionContainer : SlideCollectionContainer public string[] slidePaths; public IModBehaviour mod; + private HashSet _pathsBeingLoaded = new(); + public static Dictionary> _slidesRequiringPath = new(); static NHSlideCollectionContainer() { @@ -196,9 +198,30 @@ public class NHSlideCollectionContainer : SlideCollectionContainer _slidesRequiringPath[key].Add(this); } - var texture = ImageUtilities.GetTexture(mod, path); - this.slideCollection.slides[wrappedIndex]._image = texture; - return texture; + if (ImageUtilities.IsTextureLoaded(mod, path)) + { + var texture = ImageUtilities.GetTexture(mod, path); + this.slideCollection.slides[wrappedIndex]._image = texture; + return texture; + } + else if (!_pathsBeingLoaded.Contains(path)) + { + var loader = new SlideReelAsyncImageLoader(); + loader.PathsToLoad.Add((wrappedIndex, path)); + loader.Start(true, false); + loader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => + { + slideCollection.slides[wrappedIndex]._image = tex; + _pathsBeingLoaded.Remove(path); + }); + _pathsBeingLoaded.Add(path); + return null; + } + else + { + // It is being loaded so we just wait + return null; + } } var texture = LoadSlideInt(index); LoadSlideInt(index - 1); From c483a8331cbd85690fb8d46cb2583bcce1b98044 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Thu, 13 Feb 2025 15:45:57 -0500 Subject: [PATCH 06/21] Set playWithShipLogFacts for auto projectors --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index ab146dda..47f3db09 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -377,6 +377,7 @@ namespace NewHorizons.Builder.Props } slideCollectionContainer.slideCollection = slideCollection; + slideCollectionContainer._playWithShipLogFacts = Array.Empty(); StreamingHandler.SetUpStreaming(projectorObj, sector); From daef166a399e932d768fb16a68fa94d83d604ab5 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 15:48:28 -0500 Subject: [PATCH 07/21] Make slide reels work on ship log projector --- .../Builder/Props/ProjectionBuilder.cs | 4 +- .../Components/EOTE/NHSlideCollection.cs | 136 +++++++++++++++++- .../EOTE/NHSlideCollectionContainer.cs | 113 +-------------- .../ShipLogPatches/ShipLogSlideReelPatches.cs | 21 +++ 4 files changed, 159 insertions(+), 115 deletions(-) create mode 100644 NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index ab146dda..ad43a058 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -140,8 +140,6 @@ namespace NewHorizons.Builder.Props var toDestroy = slideReelObj.GetComponent(); var slideCollectionContainer = slideReelObj.AddComponent(); - slideCollectionContainer.slidePaths = info.slides.Select(x => x.imagePath).ToArray(); - slideCollectionContainer.mod = mod; slideReel._slideCollectionContainer = slideCollectionContainer; Component.DestroyImmediate(toDestroy); @@ -152,7 +150,7 @@ namespace NewHorizons.Builder.Props // Now we replace the slides int slidesCount = info.slides.Length; - var slideCollection = new SlideCollection(slidesCount); + SlideCollection slideCollection = new NHSlideCollection(slidesCount, mod, info.slides.Select(x => x.imagePath).ToArray()); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null // We can fit 16 slides max into an atlas diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs index a5e357af..0eed0393 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollection.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -1,16 +1,31 @@ using HarmonyLib; +using NewHorizons.Builder.Props; +using NewHorizons.Utility; using NewHorizons.Utility.Files; +using NewHorizons.Utility.OWML; using OWML.Common; +using System.Collections.Generic; +using System.IO; +using System.Linq; using UnityEngine; +using UnityEngine.SceneManagement; namespace NewHorizons.Components.EOTE; [HarmonyPatch] public class NHSlideCollection : SlideCollection { - public string[] slidePaths; public IModBehaviour mod; + private HashSet _pathsBeingLoaded = new(); + public static Dictionary> _slidesRequiringPath = new(); + + private ShipLogSlideProjector _shipLogSlideProjector; + + static NHSlideCollection() + { + SceneManager.sceneUnloaded += (_) => _slidesRequiringPath.Clear(); + } public NHSlideCollection(int startArrSize, IModBehaviour mod, string[] slidePaths) : base(startArrSize) { @@ -18,7 +33,6 @@ public class NHSlideCollection : SlideCollection this.slidePaths = slidePaths; } - /* [HarmonyPrefix] [HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.RequestStreamSlides))] public static bool SlideCollection_RequestStreamSlides(SlideCollection __instance, int[] slideIndices) @@ -27,7 +41,25 @@ public class NHSlideCollection : SlideCollection { foreach (var id in slideIndices) { - collection.slides[id]._image = ImageUtilities.GetTexture(collection.mod, collection.slidePaths[id]); + collection.LoadSlide(id); + } + return false; + } + else + { + return true; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.RequestRelease))] + public static bool SlideCollection_RequestRelease(SlideCollection __instance, int[] slideIndices) + { + if (__instance is NHSlideCollection collection) + { + foreach (var id in slideIndices) + { + collection.UnloadSlide(id); } return false; } @@ -43,7 +75,7 @@ public class NHSlideCollection : SlideCollection { if (__instance is NHSlideCollection collection) { - __result = ImageUtilities.IsTextureLoaded(collection.mod, collection.slidePaths[streamIdx]); + __result = collection.IsSlideLoaded(streamIdx); return false; } else @@ -58,7 +90,7 @@ public class NHSlideCollection : SlideCollection { if (__instance is NHSlideCollection collection) { - __result = ImageUtilities.GetTexture(collection.mod, collection.slidePaths[id]); + __result = collection.LoadSlide(id); return false; } else @@ -66,5 +98,97 @@ public class NHSlideCollection : SlideCollection return true; } } - */ + + public Texture LoadSlide(int index) + { + Texture LoadSlideInt(int index) + { + var wrappedIndex = (index + slides.Length) % slides.Length; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + + // We are the first slide collection container to try and load this image + var key = ImageUtilities.GetKey(mod, path); + if (!_slidesRequiringPath.ContainsKey(key)) + { + // Something else has loaded this image i.e., AutoProjector or Vision torch. We want to ensure we do not delete it + if (ImageUtilities.IsTextureLoaded(mod, path)) + { + _slidesRequiringPath[key] = new() { null }; + } + else + { + _slidesRequiringPath[key] = new(); + } + _slidesRequiringPath[key].Add(this); + } + + if (ImageUtilities.IsTextureLoaded(mod, path)) + { + var texture = ImageUtilities.GetTexture(mod, path); + slides[wrappedIndex]._image = texture; + return texture; + } + else if (!_pathsBeingLoaded.Contains(path)) + { + var loader = new SlideReelAsyncImageLoader(); + loader.PathsToLoad.Add((wrappedIndex, path)); + loader.Start(true, false); + loader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => + { + slides[wrappedIndex]._image = tex; + _pathsBeingLoaded.Remove(path); + if (_shipLogSlideProjector == null) + { + _shipLogSlideProjector = GameObject.FindObjectOfType(); + } + if (_shipLogSlideProjector != null) + { + _shipLogSlideProjector._slideDirty = true; + } + else + { + NHLogger.LogVerbose("No ship log slide reel projector exists"); + } + }); + _pathsBeingLoaded.Add(path); + return null; + } + else + { + // It is being loaded so we just wait + return null; + } + } + var texture = LoadSlideInt(index); + LoadSlideInt(index - 1); + LoadSlideInt(index + 1); + + return texture; + } + + public bool IsSlideLoaded(int index) + { + var wrappedIndex = (index + slides.Length) % slides.Length; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + return ImageUtilities.IsTextureLoaded(mod, path); + } + + public void UnloadSlide(int index) + { + var wrappedIndex = (index + slides.Length) % slides.Length; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); + + // Only unload textures that we were the ones to load in + if (ImageUtilities.IsTextureLoaded(mod, path)) + { + var key = ImageUtilities.GetKey(mod, path); + _slidesRequiringPath[key].Remove(this); + if (!_slidesRequiringPath[key].Any()) + { + NHLogger.LogVerbose($"Slide reel deleting {key} since nobody is using it anymore"); + ImageUtilities.DeleteTexture(mod, path, ImageUtilities.GetTexture(mod, path)); + slides[wrappedIndex]._image = null; + } + } + } } diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index 8b0fb6d7..1f9f0361 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -1,15 +1,7 @@ using HarmonyLib; -using NewHorizons.Builder.Props; -using NewHorizons.External.Modules.Props.EchoesOfTheEye; -using NewHorizons.Utility.Files; -using NewHorizons.Utility.OWML; -using OWML.Common; using System; -using System.Collections.Generic; -using System.IO; using System.Linq; using UnityEngine; -using UnityEngine.SceneManagement; namespace NewHorizons.Components.EOTE; @@ -18,16 +10,6 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { public string[] conditionsToSet; public string[] persistentConditionsToSet; - public string[] slidePaths; - public IModBehaviour mod; - - private HashSet _pathsBeingLoaded = new(); - - public static Dictionary> _slidesRequiringPath = new(); - static NHSlideCollectionContainer() - { - SceneManager.sceneUnloaded += (_) => _slidesRequiringPath.Clear(); - } [HarmonyPrefix] [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.Initialize))] @@ -87,7 +69,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container) { - __result = container.IsSlideLoaded(container.slideIndex + 1); + __result = (container.slideCollection as NHSlideCollection).IsSlideLoaded(container.slideIndex + 1); return false; } else @@ -103,7 +85,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container) { - __result = container.IsSlideLoaded(container.slideIndex - 1); + __result = (container.slideCollection as NHSlideCollection).IsSlideLoaded(container.slideIndex - 1); return false; } else @@ -118,9 +100,9 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container) { - for (int i = 0; i < container.slidePaths.Length; i++) + for (int i = 0; i < (container.slideCollection as NHSlideCollection).slidePaths.Length; i++) { - container.UnloadSlide(i); + (container.slideCollection as NHSlideCollection).UnloadSlide(i); } return false; } @@ -136,7 +118,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container) { - __result = container.LoadSlide(id); + __result = (container.slideCollection as NHSlideCollection).LoadSlide(id); return false; } else @@ -151,7 +133,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container) { - container.LoadSlide(__instance._currentSlideIndex); + (container.slideCollection as NHSlideCollection).LoadSlide(__instance._currentSlideIndex); return false; } else @@ -166,7 +148,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container) { - __result = container.slidePaths != null && container.slidePaths.Any(); + __result = (container.slideCollection as NHSlideCollection).slidePaths != null && (container.slideCollection as NHSlideCollection).slidePaths.Any(); return false; } else @@ -174,85 +156,4 @@ public class NHSlideCollectionContainer : SlideCollectionContainer return true; } } - - public Texture LoadSlide(int index) - { - Texture LoadSlideInt(int index) - { - var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); - - // We are the first slide collection container to try and load this image - var key = ImageUtilities.GetKey(mod, path); - if (!_slidesRequiringPath.ContainsKey(key)) - { - // Something else has loaded this image i.e., AutoProjector or Vision torch. We want to ensure we do not delete it - if (ImageUtilities.IsTextureLoaded(mod, path)) - { - _slidesRequiringPath[key] = new() { null }; - } - else - { - _slidesRequiringPath[key] = new(); - } - _slidesRequiringPath[key].Add(this); - } - - if (ImageUtilities.IsTextureLoaded(mod, path)) - { - var texture = ImageUtilities.GetTexture(mod, path); - this.slideCollection.slides[wrappedIndex]._image = texture; - return texture; - } - else if (!_pathsBeingLoaded.Contains(path)) - { - var loader = new SlideReelAsyncImageLoader(); - loader.PathsToLoad.Add((wrappedIndex, path)); - loader.Start(true, false); - loader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => - { - slideCollection.slides[wrappedIndex]._image = tex; - _pathsBeingLoaded.Remove(path); - }); - _pathsBeingLoaded.Add(path); - return null; - } - else - { - // It is being loaded so we just wait - return null; - } - } - var texture = LoadSlideInt(index); - LoadSlideInt(index - 1); - LoadSlideInt(index + 1); - - return texture; - } - - public bool IsSlideLoaded(int index) - { - var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); - return ImageUtilities.IsTextureLoaded(mod, path); - } - - public void UnloadSlide(int index) - { - var wrappedIndex = (index + this.slideCollection.slides.Length) % this.slideCollection.slides.Length; - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]); - - // Only unload textures that we were the ones to load in - if (ImageUtilities.IsTextureLoaded(mod, path)) - { - var key = ImageUtilities.GetKey(mod, path); - _slidesRequiringPath[key].Remove(this); - if (!_slidesRequiringPath[key].Any()) - { - NHLogger.LogVerbose($"Slide reel deleting {key} since nobody is using it anymore"); - ImageUtilities.DeleteTexture(mod, path, ImageUtilities.GetTexture(mod, path)); - slideCollection.slides[wrappedIndex]._image = null; - } - } - } } diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs new file mode 100644 index 00000000..390a4e79 --- /dev/null +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs @@ -0,0 +1,21 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; + +namespace NewHorizons.Patches.ShipLogPatches; + +[HarmonyPatch] +public static class ShipLogSlideReelPatches +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipLogSlideProjector), nameof(ShipLogSlideProjector.CheckStreamingTexturesAvailable))] + public static bool ShipLogSlideProjector_CheckStreamingTexturesAvailable(ShipLogSlideProjector __instance, ref bool __result) + { + if (__instance._collectionIndex >= 0 && __instance._collectionIndex < __instance._slideCollections.Count && + __instance._slideCollections[__instance._collectionIndex] is NHSlideCollection) + { + __result = true; + return false; + } + return true; + } +} From 3e9ff8c6163a4312e48c6a6207b6e0791647c281 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 12:55:50 -0800 Subject: [PATCH 08/21] comment --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 2 +- NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index aefb3b8b..193216d6 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -375,7 +375,7 @@ namespace NewHorizons.Builder.Props } slideCollectionContainer.slideCollection = slideCollection; - slideCollectionContainer._playWithShipLogFacts = Array.Empty(); + slideCollectionContainer._playWithShipLogFacts = Array.Empty(); // else it NREs in container initialize StreamingHandler.SetUpStreaming(projectorObj, sector); diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index 1f9f0361..367689e9 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -27,7 +27,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer __instance._changeSlidesAllowed = true; __instance._initialized = true; __instance._slideCollection.isVision = __instance._owningItem == null; - foreach (var factID in __instance._playWithShipLogFacts ?? Array.Empty()) + foreach (var factID in __instance._playWithShipLogFacts) { var fact = Locator.GetShipLogManager().GetFact(factID); fact?.RegisterSlideCollection(__instance._slideCollection); From 28bf75d09adfb678205fa701ba5dda26eb9baa4d Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 12:58:37 -0800 Subject: [PATCH 09/21] comment --- NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index 367689e9..ff627456 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -31,6 +31,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { var fact = Locator.GetShipLogManager().GetFact(factID); fact?.RegisterSlideCollection(__instance._slideCollection); + // in original it logs. we dont want that here ig } return false; } From a3f2f9fd8cd8fd0ea0e905c52ffb120e4508a805 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 13:08:09 -0800 Subject: [PATCH 10/21] document StartAsyncLoader --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 193216d6..ca612763 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -507,6 +507,16 @@ namespace NewHorizons.Builder.Props return standingTorch; } + /// + /// start loading all the slide stuff we need async. + /// + /// the mod to load slides from + /// slides to load + /// where to assign the slide objects + /// should we load cached inverted images? + /// should we load cached atlas images? + /// should we load the original images? happens anyway if cache doesnt exist since atlas or inverted will need it + /// the 3 loaders (inverted, atlas, original). inverted and atlas will be null if cache doesnt exist, so check those to find out if cache exists private static (SlideReelAsyncImageLoader inverted, SlideReelAsyncImageLoader atlas, SlideReelAsyncImageLoader slides) StartAsyncLoader(IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection, bool useInvertedCache, bool useAtlasCache, bool loadRawImages) { From 691cd8b096482234d478f332abd96fdabf365a7e Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 16:31:04 -0500 Subject: [PATCH 11/21] Fix some checks, unload all when shiplogslideprojector says to, maybe fix vision torches --- .../Builder/Props/ProjectionBuilder.cs | 26 ++++++++++--------- .../EOTE/NHSlideCollectionContainer.cs | 13 +++++----- .../ShipLogPatches/ShipLogSlideReelPatches.cs | 16 ++++++++++++ NewHorizons/Utility/Files/ImageUtilities.cs | 2 +- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index ca612763..1e12854f 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -156,11 +156,11 @@ namespace NewHorizons.Builder.Props // We can fit 16 slides max into an atlas var textures = new Texture2D[slidesCount > 16 ? 16 : slidesCount]; - // Don't load inverted images if the cache exists, in this case we only load when actively displaying stuff - var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, !CacheExists(mod), true, false); + // Slide reels dynamically load the inverted cached images when needed. We only need to load raw images to generate the cache or atlases + var (_, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, false, true, false); // If the cache doesn't exist it will be created here, slide reels only use the base image loader for cache creation so delete the images after to free memory - imageLoader.deleteTexturesWhenDone = !CacheExists(mod); + imageLoader.deleteTexturesWhenDone = true; var key = GetUniqueSlideReelID(mod, info.slides); @@ -397,9 +397,9 @@ namespace NewHorizons.Builder.Props if (_visionTorchDetectorPrefab == null) return null; // spawn a trigger for the vision torch - var g = DetailBuilder.Make(planetGO, sector, mod, _visionTorchDetectorPrefab, new DetailInfo(info) { scale = 2, rename = !string.IsNullOrEmpty(info.rename) ? info.rename : "VisionStaffDetector" }); + var visionTorchTargetGO = DetailBuilder.Make(planetGO, sector, mod, _visionTorchDetectorPrefab, new DetailInfo(info) { scale = 2, rename = !string.IsNullOrEmpty(info.rename) ? info.rename : "VisionStaffDetector" }); - if (g == null) + if (visionTorchTargetGO == null) { NHLogger.LogWarning($"Tried to make a vision torch target but couldn't. Do you have the DLC installed?"); return null; @@ -420,17 +420,18 @@ namespace NewHorizons.Builder.Props }); // attach a component to store all the data for the slides that play when a vision torch scans this target - var target = g.AddComponent(); - var slideCollectionContainer = g.AddComponent(); + var target = visionTorchTargetGO.AddComponent(); + var slideCollectionContainer = visionTorchTargetGO.AddComponent(); + slideCollectionContainer.doAsyncLoading = false; slideCollectionContainer.slideCollection = slideCollection; - target.slideCollection = g.AddComponent(); + target.slideCollection = visionTorchTargetGO.AddComponent(); target.slideCollection._slideCollectionContainer = slideCollectionContainer; LinkShipLogFacts(info, slideCollectionContainer); - g.SetActive(true); + visionTorchTargetGO.SetActive(true); - return g; + return visionTorchTargetGO; } public static GameObject MakeStandingVisionTorch(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod) @@ -489,6 +490,7 @@ namespace NewHorizons.Builder.Props // Set up the containers for the slides var slideCollectionContainer = standingTorch.AddComponent(); + slideCollectionContainer.doAsyncLoading = false; slideCollectionContainer.slideCollection = slideCollection; var mindSlideCollection = standingTorch.AddComponent(); @@ -550,7 +552,7 @@ namespace NewHorizons.Builder.Props // Load the inverted images used when displaying slide reels to a screen invertedImageLoader.PathsToLoad.Add((i, Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png"))); } - else + else if (!cacheExists || loadRawImages) { // Used to then make cached stuff imageLoader.PathsToLoad.Add((i, Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png"))); @@ -587,7 +589,7 @@ namespace NewHorizons.Builder.Props { invertedImageLoader.Start(true, false); } - else + if (loadRawImages) { imageLoader.Start(true, false); } diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index ff627456..5fded2a3 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -10,6 +10,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { public string[] conditionsToSet; public string[] persistentConditionsToSet; + public bool doAsyncLoading = true; [HarmonyPrefix] [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.Initialize))] @@ -68,7 +69,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.NextSlideAvailable))] public static bool SlideCollectionContainer_NextSlideAvailable(SlideCollectionContainer __instance, ref bool __result) { - if (__instance is NHSlideCollectionContainer container) + if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { __result = (container.slideCollection as NHSlideCollection).IsSlideLoaded(container.slideIndex + 1); return false; @@ -84,7 +85,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.PrevSlideAvailable))] public static bool SlideCollectionContainer_PrevSlideAvailable(SlideCollectionContainer __instance, ref bool __result) { - if (__instance is NHSlideCollectionContainer container) + if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { __result = (container.slideCollection as NHSlideCollection).IsSlideLoaded(container.slideIndex - 1); return false; @@ -99,7 +100,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.UnloadStreamingTextures))] public static bool SlideCollectionContainer_UnloadStreamingTextures(SlideCollectionContainer __instance) { - if (__instance is NHSlideCollectionContainer container) + if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { for (int i = 0; i < (container.slideCollection as NHSlideCollection).slidePaths.Length; i++) { @@ -117,7 +118,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.GetStreamingTexture))] public static bool SlideCollectionContainer_GetStreamingTexture(SlideCollectionContainer __instance, int id, ref Texture __result) { - if (__instance is NHSlideCollectionContainer container) + if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { __result = (container.slideCollection as NHSlideCollection).LoadSlide(id); return false; @@ -132,7 +133,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.RequestManualStreamSlides))] public static bool SlideCollectionContainer_RequestManualStreamSlides(SlideCollectionContainer __instance) { - if (__instance is NHSlideCollectionContainer container) + if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { (container.slideCollection as NHSlideCollection).LoadSlide(__instance._currentSlideIndex); return false; @@ -147,7 +148,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.streamingTexturesAvailable), MethodType.Getter)] public static bool SlideCollectionContainer_streamingTexturesAvailable(SlideCollectionContainer __instance, ref bool __result) { - if (__instance is NHSlideCollectionContainer container) + if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { __result = (container.slideCollection as NHSlideCollection).slidePaths != null && (container.slideCollection as NHSlideCollection).slidePaths.Any(); return false; diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs index 390a4e79..1da6531e 100644 --- a/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs @@ -18,4 +18,20 @@ public static class ShipLogSlideReelPatches } return true; } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipLogSlideProjector), nameof(ShipLogSlideProjector.UnloadCurrentStreamingTextures))] + public static bool ShipLogSlideProjector_UnloadCurrentStreamingTextures(ShipLogSlideProjector __instance) + { + if (__instance._collectionIndex >= 0 && __instance._collectionIndex < __instance._slideCollections.Count && + __instance._slideCollections[__instance._collectionIndex] is NHSlideCollection collection) + { + for (int i = 0; i < collection.slides.Length; i++) + { + collection.UnloadSlide(i); + } + return false; + } + return true; + } } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index cc258967..a0f02204 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -47,7 +47,7 @@ namespace NewHorizons.Utility.Files var key = GetKey(path); if (_textureCache.TryGetValue(key, out var existingTexture)) { - NHLogger.LogVerbose($"Already loaded image at path: {path}"); + //NHLogger.LogVerbose($"Already loaded image at path: {path}"); return (Texture2D)existingTexture; } From af983956864ec37933c0605ee1732812686a3d99 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 16:38:03 -0500 Subject: [PATCH 12/21] Also do not doAsyncLoading on auto projector --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 1e12854f..1532fbe1 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -342,6 +342,7 @@ namespace NewHorizons.Builder.Props var toDestroy = autoProjector.GetComponent(); var slideCollectionContainer = autoProjector.gameObject.AddComponent(); + slideCollectionContainer.doAsyncLoading = false; autoProjector._slideCollectionItem = slideCollectionContainer; Component.DestroyImmediate(toDestroy); From e825882fd191b46a38fcac7999aa780ed361f02d Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 13:40:43 -0800 Subject: [PATCH 13/21] use direct cast so exception is cast exception instead of nre --- .../Components/EOTE/NHSlideCollectionContainer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index 5fded2a3..444d22da 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -71,7 +71,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { - __result = (container.slideCollection as NHSlideCollection).IsSlideLoaded(container.slideIndex + 1); + __result = ((NHSlideCollection)container.slideCollection).IsSlideLoaded(container.slideIndex + 1); return false; } else @@ -87,7 +87,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { - __result = (container.slideCollection as NHSlideCollection).IsSlideLoaded(container.slideIndex - 1); + __result = ((NHSlideCollection)container.slideCollection).IsSlideLoaded(container.slideIndex - 1); return false; } else @@ -102,9 +102,9 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { - for (int i = 0; i < (container.slideCollection as NHSlideCollection).slidePaths.Length; i++) + for (int i = 0; i < ((NHSlideCollection)container.slideCollection).slidePaths.Length; i++) { - (container.slideCollection as NHSlideCollection).UnloadSlide(i); + ((NHSlideCollection)container.slideCollection).UnloadSlide(i); } return false; } @@ -120,7 +120,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { - __result = (container.slideCollection as NHSlideCollection).LoadSlide(id); + __result = ((NHSlideCollection)container.slideCollection).LoadSlide(id); return false; } else @@ -135,7 +135,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { - (container.slideCollection as NHSlideCollection).LoadSlide(__instance._currentSlideIndex); + ((NHSlideCollection)container.slideCollection).LoadSlide(__instance._currentSlideIndex); return false; } else @@ -150,7 +150,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading) { - __result = (container.slideCollection as NHSlideCollection).slidePaths != null && (container.slideCollection as NHSlideCollection).slidePaths.Any(); + __result = ((NHSlideCollection)container.slideCollection).slidePaths != null && ((NHSlideCollection)container.slideCollection).slidePaths.Any(); return false; } else From b1e64c1491ba6a706af3c6e4cc557fb1c1ac41b6 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 13:54:03 -0800 Subject: [PATCH 14/21] dont need to check CacheExists --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 1532fbe1..54141a30 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -164,7 +164,7 @@ namespace NewHorizons.Builder.Props var key = GetUniqueSlideReelID(mod, info.slides); - if (CacheExists(mod) && atlasImageLoader != null) + if (atlasImageLoader != null) { atlasImageLoader.imageLoadedEvent.AddListener( (Texture2D tex, int _, string originalPath) => @@ -585,7 +585,6 @@ namespace NewHorizons.Builder.Props { atlasImageLoader.Start(false, false); } - // When using the inverted cache we never need the regular images if (useInvertedCache) { invertedImageLoader.Start(true, false); From a6c20cb23108701c88ea4f44c6fa9a68318d491e Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 14:20:39 -0800 Subject: [PATCH 15/21] document --- NewHorizons/Components/EOTE/NHSlideCollection.cs | 11 ++++++++++- .../Components/EOTE/NHSlideCollectionContainer.cs | 1 + .../Utility/Files/SlideReelAsyncImageLoader.cs | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs index 0eed0393..6ab5ef8b 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollection.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -18,9 +18,12 @@ public class NHSlideCollection : SlideCollection public string[] slidePaths; public IModBehaviour mod; private HashSet _pathsBeingLoaded = new(); + /// + /// map of slide path to collections that have this path loaded. used to only unload slide when nothing else is using it + /// public static Dictionary> _slidesRequiringPath = new(); - private ShipLogSlideProjector _shipLogSlideProjector; + private static ShipLogSlideProjector _shipLogSlideProjector; static NHSlideCollection() { @@ -113,6 +116,7 @@ public class NHSlideCollection : SlideCollection // Something else has loaded this image i.e., AutoProjector or Vision torch. We want to ensure we do not delete it if (ImageUtilities.IsTextureLoaded(mod, path)) { + // null is dummy value to ensure its never empty (so its not deleted) _slidesRequiringPath[key] = new() { null }; } else @@ -124,17 +128,21 @@ public class NHSlideCollection : SlideCollection if (ImageUtilities.IsTextureLoaded(mod, path)) { + // already loaded var texture = ImageUtilities.GetTexture(mod, path); slides[wrappedIndex]._image = texture; return texture; } else if (!_pathsBeingLoaded.Contains(path)) { + // not loaded yet, we need to load it var loader = new SlideReelAsyncImageLoader(); loader.PathsToLoad.Add((wrappedIndex, path)); loader.Start(true, false); loader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => { + // weird: sometimes we set image, sometimes we return from GetStreamingTexture. oh well + // also somehow setting this later works and updates the cookie without having to manually tell it to do that??? idk how slides[wrappedIndex]._image = tex; _pathsBeingLoaded.Remove(path); if (_shipLogSlideProjector == null) @@ -143,6 +151,7 @@ public class NHSlideCollection : SlideCollection } if (_shipLogSlideProjector != null) { + // gotta tell ship log we updated the image _shipLogSlideProjector._slideDirty = true; } else diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs index 444d22da..030139e4 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -10,6 +10,7 @@ public class NHSlideCollectionContainer : SlideCollectionContainer { public string[] conditionsToSet; public string[] persistentConditionsToSet; + // at some point we'll do streaming on all slides. until then just have an off switch public bool doAsyncLoading = true; [HarmonyPrefix] diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs index e81d8298..e088e9d9 100644 --- a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -42,6 +42,11 @@ public class SlideReelAsyncImageLoader private bool _started; private bool _clamp; + /// + /// start loading the images a frame later + /// + /// sets wrapMode + /// load all slides one at a time vs at the same time public void Start(bool clamp, bool sequential) { if (_started) return; From 8c3649bb40dcb56547b09bce1b608e7b897a0576 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 17:21:00 -0500 Subject: [PATCH 16/21] Can set which slides to display, make empty slide reels transparent (#888) --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 10 ++++++++-- .../Components/EOTE/NHSlideCollection.cs | 14 +++++++++++++- .../Props/EchoesOfTheEye/ProjectionInfo.cs | 8 +++++++- NewHorizons/Utility/Files/ImageUtilities.cs | 17 +++++++++++++++-- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 1532fbe1..32c658b8 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -209,8 +209,14 @@ namespace NewHorizons.Builder.Props var slidesBack = slideReelObj.GetComponentInChildren(true).transform.Find("Slides_Back").GetComponent(); var slidesFront = slideReelObj.GetComponentInChildren(true).transform.Find("Slides_Front").GetComponent(); - // Now put together the textures into a 4x4 thing for the materials - var reelTexture = ImageUtilities.MakeReelTexture(mod, textures, key); + // Now put together the textures into a 4x4 thing for the materials #888 + var displayTextures = textures; + if (info.displaySlides != null && info.displaySlides.Length > 0) + { + displayTextures = info.displaySlides.Select(x => textures[x]).ToArray(); + } + + var reelTexture = ImageUtilities.MakeReelTexture(mod, displayTextures, key); slidesBack.material.mainTexture = reelTexture; slidesBack.material.SetTexture(EmissionMap, reelTexture); slidesBack.material.name = reelTexture.name; diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs index 0eed0393..7aeccb30 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollection.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -24,7 +24,19 @@ public class NHSlideCollection : SlideCollection static NHSlideCollection() { - SceneManager.sceneUnloaded += (_) => _slidesRequiringPath.Clear(); + SceneManager.sceneUnloaded += (_) => + { + foreach (var (slide, collections) in _slidesRequiringPath) + { + // If it has null, that means some other permanent thing loaded this texture and it will get cleared elsewhere + // Otherwise it was loaded by an NHSlideCollection and should be deleted + if (collections.Any() && !collections.Contains(null)) + { + ImageUtilities.DeleteTexture(slide); + } + } + _slidesRequiringPath.Clear(); + }; } public NHSlideCollection(int startArrSize, IModBehaviour mod, string[] slidePaths) : base(startArrSize) diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs index c61ef06b..e059c310 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs @@ -85,6 +85,12 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// Exclusive to the slide reel type. Condition/material of the reel. Antique is the Stranger, Pristine is the Dreamworld, Rusted is a burned reel. /// [DefaultValue("antique")] public SlideReelCondition reelCondition = SlideReelCondition.Antique; - } + /// + /// Set which slides appear on the slide reel model. Leave empty to default to the first few slides. + /// Takes a list of indices, i.e., to show the first 5 slides in reverse you would put [4, 3, 2, 1, 0]. + /// Index starts at 0. + /// + public int[] displaySlides; + } } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index a0f02204..09ff831f 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -71,6 +71,19 @@ namespace NewHorizons.Utility.Files } } + /// + /// Not sure why the other method takes in the texture as well + /// + /// + public static void DeleteTexture(string key) + { + if (_textureCache.ContainsKey(key)) + { + UnityEngine.Object.Destroy(_textureCache[key]); + _textureCache.Remove(key); + } + } + public static void DeleteTexture(IModBehaviour mod, string filename, Texture2D texture) { var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); @@ -194,9 +207,9 @@ namespace NewHorizons.Utility.Files { for (int j = 0; j < size; j++) { - var colour = Color.black; + var colour = Color.clear; - if (srcTexture) + if (srcTexture != null) { var srcX = i * srcTexture.width / (float)size; var srcY = j * srcTexture.height / (float)size; From 8b4989fbee47fd852ba9045c064421384c5e7be4 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 14:23:03 -0800 Subject: [PATCH 17/21] document --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index eebe6739..2ebf64fc 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -196,6 +196,7 @@ namespace NewHorizons.Builder.Props { var time = DateTime.Now; + // inverted slides will be loaded for the whole loop but its fine since this is only when generating cache slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath); NHLogger.LogVerbose($"Slide reel make reel invert texture {(DateTime.Now - time).TotalMilliseconds}ms"); // Track the first 16 to put on the slide reel object From d10d79719d17a0a6c0f2b9f7de0b731e52bdd6cc Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 13 Feb 2025 22:23:29 +0000 Subject: [PATCH 18/21] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 298e2300..1b18d953 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2863,6 +2863,14 @@ "description": "Exclusive to the slide reel type. Condition/material of the reel. Antique is the Stranger, Pristine is the Dreamworld, Rusted is a burned reel.", "default": "antique", "$ref": "#/definitions/SlideReelCondition" + }, + "displaySlides": { + "type": "array", + "description": "Set which slides appear on the slide reel model. Leave empty to default to the first few slides.\nTakes a list of indices, i.e., to show the first 5 slides in reverse you would put [4, 3, 2, 1, 0].\nIndex starts at 0.", + "items": { + "type": "integer", + "format": "int32" + } } } }, From 05a159649314ac0d8c7f429ead729a5f8171def5 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 14:32:10 -0800 Subject: [PATCH 19/21] i get it now --- NewHorizons/Components/EOTE/NHSlideCollection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs index 89b920d1..09d3288a 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollection.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -154,7 +154,6 @@ public class NHSlideCollection : SlideCollection loader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => { // weird: sometimes we set image, sometimes we return from GetStreamingTexture. oh well - // also somehow setting this later works and updates the cookie without having to manually tell it to do that??? idk how slides[wrappedIndex]._image = tex; _pathsBeingLoaded.Remove(path); if (_shipLogSlideProjector == null) From c93ed5fc146e36c67224ca09ef8d0600d1e1849b Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 13 Feb 2025 18:37:01 -0500 Subject: [PATCH 20/21] Fix caching the ship log slide projector --- NewHorizons/Components/EOTE/NHSlideCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs index 09d3288a..905187f7 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollection.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -158,7 +158,7 @@ public class NHSlideCollection : SlideCollection _pathsBeingLoaded.Remove(path); if (_shipLogSlideProjector == null) { - _shipLogSlideProjector = GameObject.FindObjectOfType(); + _shipLogSlideProjector = Resources.FindObjectsOfTypeAll().FirstOrDefault(); } if (_shipLogSlideProjector != null) { From d1ceaaaf717ec7842700a4aa7e9b90c5d993f282 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Feb 2025 19:57:12 -0800 Subject: [PATCH 21/21] cache ship cockpit controller --- NewHorizons/Components/EOTE/NHSlideCollection.cs | 1 + NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs index 905187f7..6b13a833 100644 --- a/NewHorizons/Components/EOTE/NHSlideCollection.cs +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -158,6 +158,7 @@ public class NHSlideCollection : SlideCollection _pathsBeingLoaded.Remove(path); if (_shipLogSlideProjector == null) { + // Object.FindObjectOfType doesnt work with inactive _shipLogSlideProjector = Resources.FindObjectsOfTypeAll().FirstOrDefault(); } if (_shipLogSlideProjector != null) diff --git a/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs b/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs index 63986a2a..b0a0168d 100644 --- a/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs +++ b/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs @@ -5,6 +5,8 @@ namespace NewHorizons.Patches.ToolPatches [HarmonyPatch(typeof(ToolModeSwapper))] public static class ToolModeSwapperPatches { + private static ShipCockpitController _shipCockpitController; + // Patches ToolModeSwapper.EquipToolMode(ToolMode mode) to deny swaps if you're holding a vision torch. // This is critical for preventing swapping to the scout launcher (causes memory slides to fail) but it // just doesn't look right when you switch to other stuff (eg the signalscope), so I'm disabling swapping tools entirely @@ -21,7 +23,9 @@ namespace NewHorizons.Patches.ToolPatches mode == ToolMode.Probe || mode == ToolMode.SignalScope || mode == ToolMode.Translator; - var isInShip = UnityEngine.Object.FindObjectOfType()?._playerAtFlightConsole ?? false; + if (_shipCockpitController == null) + _shipCockpitController = UnityEngine.Object.FindObjectOfType(); + var isInShip = _shipCockpitController != null ? _shipCockpitController._playerAtFlightConsole : false; if (!isInShip && isHoldingVisionTorch && swappingToRestrictedTool) return false;