Decompiled source of OtherLoaderPatched v2.0.3
patchers\Sirdoggy.OldOtherLoaderDisabler.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Mono.Cecil; using Valve.Newtonsoft.Json.Linq; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("Sirdoggy")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Sirdoggy.OldOtherLoaderDisabler")] [assembly: AssemblyTitle("OldOtherLoaderDisabler")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Sirdoggy.OldOtherLoaderDisabler { public static class OldOtherLoaderDisablerPatcher { private const string OtherLoaderDllFullName = "OtherLoader.dll"; private const string OtherLoaderDllFullNameDisabled = "OtherLoader.dll.old"; private const string ManifestFullName = "manifest.json"; private static readonly ManualLogSource Log = Logger.CreateLogSource("OldOtherLoaderDisabler"); public static IEnumerable<string> TargetDLLs => Enumerable.Empty<string>(); internal static void Initialize() { try { Log.LogMessage((object)"Attempting to disable original OtherLoader dlls."); foreach (string pluginDirectory in GetPluginDirectories()) { if (!IsOldOtherLoaderDirectoryWithEnabledManifest(pluginDirectory)) { continue; } string text = Path.Combine(pluginDirectory, "OtherLoader.dll"); string text2 = Path.Combine(pluginDirectory, "OtherLoader.dll.old"); if (!File.Exists(text)) { Log.LogMessage((object)("Did not resolve 'OtherLoader.dll' in " + pluginDirectory + ", OtherLoader is likely already disabled.")); continue; } if (File.Exists(text2)) { File.Delete(text2); } File.Move(text, text2); Log.LogMessage((object)("Successfully disabled original OtherLoader in " + pluginDirectory)); } } catch (Exception arg) { Log.LogError((object)$"Exception when disabling original OtherLoader dlls: {arg}"); } } public static void RestoreOtherLoaderDll() { try { Log.LogMessage((object)"Attempting to restore original OtherLoader dlls"); foreach (string pluginDirectory in GetPluginDirectories()) { if (!IsOldOtherLoaderDirectoryWithEnabledManifest(pluginDirectory)) { continue; } string text = Path.Combine(pluginDirectory, "OtherLoader.dll"); string text2 = Path.Combine(pluginDirectory, "OtherLoader.dll.old"); if (File.Exists(text2)) { if (File.Exists(text)) { Log.LogWarning((object)("Duplicate dlls exist in " + pluginDirectory + ", this shouldn't happen. Deleting dll.old and exiting...")); File.Delete(text2); } else { File.Move(text2, text); Log.LogMessage((object)("Successfully restored original OtherLoader dll in " + pluginDirectory)); } } } } catch (Exception arg) { Log.LogError((object)$"Exception when restoring original OtherLoader dlls: {arg}"); } } private static List<string> GetPluginDirectories() { List<string> list = Directory.GetDirectories(Paths.PluginPath, "*", SearchOption.TopDirectoryOnly).ToList(); if (!list.Any()) { throw new Exception("No directories in plugins folder found."); } return list; } private static bool IsOldOtherLoaderDirectoryWithEnabledManifest(string directory) { string path = Path.Combine(directory, "manifest.json"); if (!File.Exists(path)) { return false; } try { JObject obj = JObject.Parse(File.ReadAllText(path)); string text = ((object)obj["name"])?.ToString(); string text2 = ((object)obj["website_url"])?.ToString(); return text == "OtherLoader" && text2 == "https://github.com/devyndamonster/OtherLoader"; } catch (Exception ex) { Log.LogError((object)("Exception parsing manifest in " + directory + ": " + ex.Message)); return false; } } public static void Patch(AssemblyDefinition _) { } } }
OtherLoader.dll
Decompiled 5 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FistVR; using HarmonyLib; using Microsoft.CodeAnalysis; using OtherLoader.AssetLoading; using OtherLoader.AssetLoading.AssetLoaders; using OtherLoader.AssetLoading.AssetLoaders.Implementations; using OtherLoader.Common; using OtherLoader.Common.FVR; using OtherLoader.Common.Unity; using OtherLoader.ItemSpawner; using OtherLoader.ItemSpawner.CustomCategories; using OtherLoader.ItemSpawner.Patches; using OtherLoader.ItemSpawner.PortableItemSpawner; using OtherLoader.ItemSpawner.SecondaryObjectsLinker; using OtherLoader.ItemSpawner.VanillaCategories; using OtherLoader.ItemUnlocker; using OtherLoader.Logging; using OtherLoader.QuickbeltPanel; using OtherLoader.Unlockathon; using OtherLoader.Unlockathon.Data; using OtherLoader.Unlockathon.Patches; using OtherLoader.Unlockathon.Tags; using Sirdoggy.OldOtherLoaderDisabler; using Sodalite; using Sodalite.Api; using Sodalite.ModPanel; using Stratum; using Stratum.Extensions; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using Valve.Newtonsoft.Json; using Valve.VR; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("OtherLoader")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("OtherLoader")] [assembly: AssemblyTitle("OtherLoader")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class ParamCollectionAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace OtherLoader { [CreateAssetMenu(menuName = "MeatKit/Otherloader/SpawnerEntry", fileName = "New Spawner Entry")] public class ItemSpawnerEntry : ScriptableObject { [Header("Item IDs")] [Tooltip("ItemID for the main object that will spawn")] public string MainObjectID; [Tooltip("ItemIDs for items that will spawn alongside the main object")] public List<string> SpawnWithIDs = new List<string>(); [Tooltip("ItemIDs for items that appear in the secondary items section")] public List<string> SecondaryObjectIDs = new List<string>(); [Header("[OPTIONAL] Populate ItemIDs using FVRObjects directly")] public FVRObject? MainObjectObj; public List<FVRObject> SpawnWithObjs = new List<FVRObject>(); public List<FVRObject> SecondaryObjs = new List<FVRObject>(); [Header("Entry Path Properties")] [Tooltip("The path for the entry")] public string EntryPath; public PageMode Page; public ESubCategory SubCategory; [Header("Display Properties")] [Tooltip("The icon that will appear in the spawner for this entry")] public Sprite EntryIcon; [Tooltip("The name of the entry")] public string DisplayName; [Tooltip("Decides wether the entry will be visible in the spawner.\n Set to false if you only want the entry visible as a secondary")] public bool IsDisplayedInMainEntry; [Tooltip("A list modding tags to allow for sorting by mod groups in itemspawner")] public List<string> ModTags = new List<string>(); [Tooltip("A list of tutorial block IDs that will appear when this entry is selected")] public List<string> TutorialBlockIDs = new List<string>(); [Header("Misc Properties")] public bool UsesLargeSpawnPad; public bool UsesHugeSpawnPad; public bool IsReward; internal bool IsUncategorized; public static ItemSpawnerEntry CreateEmpty(string path) { ItemSpawnerEntry itemSpawnerEntry = ScriptableObject.CreateInstance<ItemSpawnerEntry>(); itemSpawnerEntry.EntryPath = path; return itemSpawnerEntry; } public void PopulateIDsFromObj() { if ((Object)(object)MainObjectObj != (Object)null) { MainObjectID = MainObjectObj.ItemID; OtherLogger.Log("Assigned MainObjectID '" + MainObjectID + "' from MainObjectObj.ItemID", LogTag.Assets); } foreach (FVRObject spawnWithObj in SpawnWithObjs) { if (!SpawnWithIDs.Contains(spawnWithObj.ItemID)) { SpawnWithIDs.Add(spawnWithObj.ItemID); } } foreach (FVRObject secondaryObj in SecondaryObjs) { if (!SecondaryObjectIDs.Contains(secondaryObj.ItemID)) { SecondaryObjectIDs.Add(secondaryObj.ItemID); } } } public bool IsCategoryEntry() { return string.IsNullOrEmpty(MainObjectID); } internal ESubCategory? GetSpawnerSubcategory() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) string[] array = (from o in EntryPath.Split(new char[1] { '/' }) where Enum.IsDefined(typeof(ESubCategory), o) select o).ToArray(); if (array.Length == 0) { return null; } return ((IEnumerable<string>)array).Select((Func<string, ESubCategory>)((string o) => (ESubCategory)Enum.Parse(typeof(ESubCategory), o))).FirstOrDefault(); } internal EItemCategory? GetSpawnerCategory() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) string[] array = (from o in EntryPath.Split(new char[1] { '/' }) where Enum.IsDefined(typeof(EItemCategory), o) select o).ToArray(); if (array.Length == 0) { return null; } return ((IEnumerable<string>)array).Select((Func<string, EItemCategory>)((string o) => (EItemCategory)Enum.Parse(typeof(EItemCategory), o))).FirstOrDefault(); } } public delegate void StatusUpdate(float progress); internal enum LoadOrder { LoadFirst, LoadLast, LoadUnordered } public static class LoaderStatus { private enum BundleStatus { Waiting, CanLoad, Loaded, Unloaded } private class BundleLoadStatus { public readonly string BundleId; public BundleStatus Status; public LoadOrder LoadOrder; public BundleLoadStatus(string bundleId, LoadOrder loadOrder) { BundleId = bundleId; LoadOrder = loadOrder; Status = BundleStatus.Waiting; } } private class ModContainer { private readonly List<BundleLoadStatus> _loadFirst = new List<BundleLoadStatus>(); private readonly List<BundleLoadStatus> _loadUnordered = new List<BundleLoadStatus>(); private readonly List<BundleLoadStatus> _loadLast = new List<BundleLoadStatus>(); private readonly Dictionary<string, BundleLoadStatus> _bundleLoadStatusDic = new Dictionary<string, BundleLoadStatus>(); public void AddToLoadOrder(string bundleID, LoadOrder loadOrder) { BundleLoadStatus bundleLoadStatus = new BundleLoadStatus(bundleID, loadOrder); if (_bundleLoadStatusDic.ContainsKey(bundleID)) { BundleLoadStatus bundleLoadStatus2 = _bundleLoadStatusDic[bundleID]; _bundleLoadStatusDic.Remove(bundleID); _loadFirst.Remove(bundleLoadStatus2); _loadUnordered.Remove(bundleLoadStatus2); _loadLast.Remove(bundleLoadStatus2); bundleLoadStatus.Status = BundleStatus.Unloaded; bundleLoadStatus.LoadOrder = bundleLoadStatus2.LoadOrder; if (bundleLoadStatus2.Status != BundleStatus.Loaded && bundleLoadStatus2.Status != BundleStatus.Unloaded) { OtherLogger.LogError("Tracking a late bundle, but the data bundle isn't already loaded! " + $"Data bundle status: {bundleLoadStatus2.Status}"); } } else { if (loadOrder == LoadOrder.LoadFirst) { if (_loadFirst.Count == 0 || _loadFirst.All((BundleLoadStatus o) => o.Status == BundleStatus.Loaded || o.Status == BundleStatus.Unloaded)) { bundleLoadStatus.Status = BundleStatus.CanLoad; } if (_loadUnordered.Count != 0 || _loadLast.Count != 0) { OtherLogger.LogError("Mod is set to load first, but it looks like unordered or load last mods are already loading! BundleID (" + bundleID + ")"); _loadUnordered.ForEach(delegate(BundleLoadStatus o) { OtherLogger.LogError("Load Unordered BundleID (" + o.BundleId + ")"); }); _loadLast.ForEach(delegate(BundleLoadStatus o) { OtherLogger.LogError("Load Last BundleID (" + o.BundleId + ")"); }); } } if (loadOrder == LoadOrder.LoadUnordered) { if (_loadFirst.Count == 0 || _loadFirst.All((BundleLoadStatus o) => o.Status == BundleStatus.Loaded || o.Status == BundleStatus.Unloaded)) { bundleLoadStatus.Status = BundleStatus.CanLoad; } if (_loadLast.Count != 0) { OtherLogger.LogError("Mod is set to load unordered, but it looks like load last mods are already loading! BundleID (" + bundleID + ")"); _loadLast.ForEach(delegate(BundleLoadStatus o) { OtherLogger.LogError("Load Last BundleID (" + o.BundleId + ")"); }); } } if (loadOrder == LoadOrder.LoadLast && ((_loadFirst.Count == 0 && _loadUnordered.Count == 0 && _loadLast.Count == 0) || (_loadFirst.All((BundleLoadStatus o) => o.Status == BundleStatus.Loaded || o.Status == BundleStatus.Unloaded) && _loadUnordered.All((BundleLoadStatus o) => o.Status == BundleStatus.Loaded || o.Status == BundleStatus.Unloaded) && _loadLast.All((BundleLoadStatus o) => o.Status == BundleStatus.Loaded || o.Status == BundleStatus.Unloaded)))) { bundleLoadStatus.Status = BundleStatus.CanLoad; } } _bundleLoadStatusDic.Add(bundleID, bundleLoadStatus); switch (loadOrder) { case LoadOrder.LoadFirst: _loadFirst.Add(bundleLoadStatus); break; case LoadOrder.LoadLast: _loadLast.Add(bundleLoadStatus); break; case LoadOrder.LoadUnordered: _loadUnordered.Add(bundleLoadStatus); break; } } public BundleStatus? TryGetBundleLoadStatus(string bundleID) { return _bundleLoadStatusDic.GetValueOrNull(bundleID)?.Status; } public void MarkBundleAsLoaded(string bundleID, bool permanentlyLoaded) { BundleLoadStatus bundleLoadStatus = _bundleLoadStatusDic[bundleID]; if (permanentlyLoaded) { bundleLoadStatus.Status = BundleStatus.Loaded; } else { bundleLoadStatus.Status = BundleStatus.Unloaded; } if (bundleLoadStatus.LoadOrder == LoadOrder.LoadFirst) { BundleLoadStatus bundleLoadStatus2 = _loadFirst.FirstOrDefault((BundleLoadStatus o) => o.Status == BundleStatus.Waiting); if (bundleLoadStatus2 != null) { bundleLoadStatus2.Status = BundleStatus.CanLoad; } else if (_loadUnordered.Count > 0) { _loadUnordered.ForEach(delegate(BundleLoadStatus o) { o.Status = BundleStatus.CanLoad; }); } else if (_loadLast.Count > 0) { _loadLast[0].Status = BundleStatus.CanLoad; } } else if (bundleLoadStatus.LoadOrder == LoadOrder.LoadUnordered) { if (_loadUnordered.All((BundleLoadStatus o) => o.Status == BundleStatus.Loaded || o.Status == BundleStatus.Unloaded) && _loadLast.Count != 0) { _loadLast[0].Status = BundleStatus.CanLoad; } } else if (bundleLoadStatus.LoadOrder == LoadOrder.LoadLast) { BundleLoadStatus bundleLoadStatus3 = _loadLast.FirstOrDefault((BundleLoadStatus o) => o.Status == BundleStatus.Waiting); if (bundleLoadStatus3 != null) { bundleLoadStatus3.Status = BundleStatus.CanLoad; } } } public List<BundleLoadStatus> GetBundleDependencies(string bundleID) { List<BundleLoadStatus> list = new List<BundleLoadStatus>(); BundleLoadStatus bundleLoadStatus = _bundleLoadStatusDic[bundleID]; if (bundleLoadStatus.LoadOrder == LoadOrder.LoadFirst) { foreach (BundleLoadStatus item in _loadFirst) { if (item.BundleId == bundleID) { break; } if (!AnvilManager.m_bundles.m_lookup.ContainsKey(item.BundleId)) { list.Add(item); } } } else if (bundleLoadStatus.LoadOrder == LoadOrder.LoadUnordered) { foreach (BundleLoadStatus item2 in _loadFirst) { if (!AnvilManager.m_bundles.m_lookup.ContainsKey(item2.BundleId)) { list.Add(item2); } } } else if (bundleLoadStatus.LoadOrder == LoadOrder.LoadLast) { foreach (BundleLoadStatus item3 in _loadFirst) { if (!AnvilManager.m_bundles.m_lookup.ContainsKey(item3.BundleId)) { list.Add(item3); } } foreach (BundleLoadStatus item4 in _loadUnordered) { if (!AnvilManager.m_bundles.m_lookup.ContainsKey(item4.BundleId)) { list.Add(item4); } } foreach (BundleLoadStatus item5 in _loadLast) { if (item5.BundleId == bundleID) { break; } if (!AnvilManager.m_bundles.m_lookup.ContainsKey(item5.BundleId)) { list.Add(item5); } } } return list; } } internal static float LastLoadEventTime; private static float? _lastStatus; private static StatusUpdate? _progressUpdatedReplayingListeners; private static readonly List<string> ActiveLoaders = new List<string>(); private static readonly Dictionary<string, ModContainer> ModContainers = new Dictionary<string, ModContainer>(); private static readonly Dictionary<string, float> LoaderProgressDic = new Dictionary<string, float>(); private static float _loadersProgressSum; private static float _loadStartTime; private const float LoadingCompleteMargin = 2f; public static event StatusUpdate? ProgressUpdated; public static event StatusUpdate? ProgressUpdatedReplaying { add { _progressUpdatedReplayingListeners = (StatusUpdate)Delegate.Combine(_progressUpdatedReplayingListeners, value); float? lastStatus = _lastStatus; if (lastStatus.HasValue) { value?.Invoke(_lastStatus.Value); } } remove { _progressUpdatedReplayingListeners = (StatusUpdate)Delegate.Remove(_progressUpdatedReplayingListeners, value); } } public static float GetLoaderProgress() { if (LoaderProgressDic.Count == 0) { return 0f; } float num = _loadersProgressSum / (float)LoaderProgressDic.Count; if (num > 0.99999f) { num = 1f; } if (!(Time.realtimeSinceStartup - LastLoadEventTime < 2f)) { return num; } return Mathf.Min(num, 0.99f); } internal static IEnumerator LoadTimeCoroutine() { LastLoadEventTime = Time.realtimeSinceStartup; WaitForSeconds delay = new WaitForSeconds(0.1f); float loaderProgress; do { yield return delay; loaderProgress = GetLoaderProgress(); DoStatusUpdate(loaderProgress); } while (loaderProgress < 1f); OtherLogger.Log($"All items loaded, took {Time.realtimeSinceStartup - _loadStartTime:0.000} seconds!"); } private static void DoStatusUpdate(float status) { _lastStatus = status; _progressUpdatedReplayingListeners?.Invoke(status); LoaderStatus.ProgressUpdated?.Invoke(status); } internal static void AddActiveLoader(string bundleID) { if (!ActiveLoaders.Contains(bundleID)) { ActiveLoaders.Add(bundleID); } } internal static void RemoveActiveLoader(BundleInfo bundleInfo, bool permanentlyLoaded) { string iD = bundleInfo.ID; if (ActiveLoaders.Contains(iD)) { ActiveLoaders.Remove(iD); string fullDirectoryName = bundleInfo.FullDirectoryName; ModContainers[fullDirectoryName].MarkBundleAsLoaded(iD, permanentlyLoaded); LastLoadEventTime = Time.realtimeSinceStartup; } } internal static void TrackLoader(BundleInfo bundleInfo, LoadOrder loadOrder) { LastLoadEventTime = Time.realtimeSinceStartup; string iD = bundleInfo.ID; string fullDirectoryName = bundleInfo.FullDirectoryName; if (LoaderProgressDic.Count == 0) { _loadStartTime = Time.realtimeSinceStartup; OtherLogger.Log("Starting LoadTimeCoroutine", LogTag.BundleLoading); OtherLoader.CoroutineStarter.Invoke(LoadTimeCoroutine()); } if (!LoaderProgressDic.ContainsKey(iD)) { LoaderProgressDic.Add(iD, 0f); } if (!ModContainers.ContainsKey(fullDirectoryName)) { OtherLogger.Log("Adding new load order entry for mod", LogTag.BundleLoading); ModContainers.Add(fullDirectoryName, new ModContainer()); } ModContainers[fullDirectoryName].AddToLoadOrder(iD, loadOrder); OtherLogger.Log($"Tracking modded bundle: {iD}, LoadOrder: {loadOrder}", LogTag.BundleLoading); } internal static bool IsBundleAlreadyTracked(BundleInfo bundleInfo) { string fullDirectoryName = bundleInfo.FullDirectoryName; return (ModContainers.GetValueOrNull(fullDirectoryName)?.TryGetBundleLoadStatus(bundleInfo.ID)).HasValue; } internal static bool CanOrderedModLoad(BundleInfo bundleInfo) { string fullDirectoryName = bundleInfo.FullDirectoryName; BundleStatus? bundleStatus = (ModContainers.GetValueOrNull(fullDirectoryName) ?? throw new Exception("Mod was not found in load order! BundleID: " + bundleInfo.ID)).TryGetBundleLoadStatus(bundleInfo.ID); if (!bundleStatus.HasValue) { throw new Exception("BundleID was not found in mod load order container: " + bundleInfo.ID); } if (bundleStatus.GetValueOrDefault() == BundleStatus.Loaded || bundleStatus.GetValueOrDefault() == BundleStatus.Unloaded) { throw new Exception("BundleID is already loaded/unloaded: " + bundleInfo.ID); } bool flag = bundleStatus.GetValueOrDefault() == BundleStatus.CanLoad; return (PluginConfig.MaxActiveLoaders <= 0 || ActiveLoaders.Count < PluginConfig.MaxActiveLoaders) && flag; } internal static void PrintWaitingBundles(BundleInfo bundleInfo) { string fullDirectoryName = bundleInfo.FullDirectoryName; List<BundleLoadStatus> list = (from o in ModContainers[fullDirectoryName].GetBundleDependencies(bundleInfo.ID) where o.Status != BundleStatus.Loaded && o.Status != BundleStatus.Unloaded select o).ToList(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Bundle '" + bundleInfo.Name + "' has been waiting a long time to load! " + $"Active loaders: {ActiveLoaders.Count}. " + $"Waiting for {list.Count} bundles."); foreach (BundleLoadStatus item in list) { stringBuilder.AppendLine($"- Waiting for BundleID: {item.BundleId}, Status: {item.Status}"); } OtherLogger.Log(stringBuilder.ToString()); } internal static void UpdateProgress(BundleInfo bundleInfo, float progress) { string iD = bundleInfo.ID; float? structOrNull = LoaderProgressDic.GetStructOrNull(iD); if (structOrNull.HasValue) { _loadersProgressSum += progress - structOrNull.Value; LoaderProgressDic[iD] = progress; } } internal static List<string> GetDependenciesBundleIDs(string bundleID) { string directoryFromID = BundleInfo.GetDirectoryFromID(bundleID); return (from o in ModContainers[directoryFromID].GetBundleDependencies(bundleID) select o.BundleId).ToList(); } } [BepInPlugin("h3vr.otherloader", "OtherLoaderPatched", "2.0.3")] [BepInDependency("stratum", "1.1.0")] [BepInDependency("nrgill28.Sodalite", "1.4.1")] public class OtherLoader : StratumPlugin { private class DirectLoadMod { public readonly string Path; public readonly string LoadFirst; public readonly string LoadAny; public readonly string LoadLast; public DirectLoadMod(string path, string loadFirst, string loadAny, string loadLast) { Path = path; LoadFirst = loadFirst; LoadAny = loadAny; LoadLast = loadLast; base..ctor(); } } private const string OtherLoaderVersion = "2.0.3"; public static readonly Dictionary<string, ItemSpawnerEntry> SpawnerEntriesByID = new Dictionary<string, ItemSpawnerEntry>(); internal static readonly Dictionary<string, string> ManagedBundles = new Dictionary<string, string>(); internal static CoroutineStarter CoroutineStarter = null; internal static ExceptionCatcher ExceptionCatcher = null; private static readonly List<DirectLoadMod> DirectLoadMods = new List<DirectLoadMod>(); private void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Expected O, but got Unknown //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Expected O, but got Unknown CoroutineStarter = new CoroutineStarter(((MonoBehaviour)this).StartCoroutine); ExceptionCatcher = new ExceptionCatcher(((BaseUnityPlugin)this).Logger); OtherLogger.Initialize(((BaseUnityPlugin)this).Logger); PluginConfig.Initialize(((BaseUnityPlugin)this).Info); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(LogLevelColorPatcher), (string)null); }); if (PluginConfig.ItemUnlocker != 0) { ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(RewardUnlocksPatchers), (string)null); }); } ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(AnvilRuntimeLoadingPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(QuickbeltPanelPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(ItemPickupPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(VaultSpawnUnlockathonPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(RandomGunSpawnUnlockathonPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(ItemSpawnerV2UnlockathonPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(PortableSpawnerPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(ItemSpawnerV2InitialPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(ItemSpawnerV2SimpleModePatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(ItemSpawnerV2SpawningPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(ItemSpawnerV2DisplayItemsPatcher), (string)null); }); ExceptionCatcher.Run(delegate { Harmony.CreateAndPatchAll(typeof(ItemSpawnerV2TagsFromHeldPatcher), (string)null); }); ExceptionCatcher.Run(UnlockathonManager.Initialize); if (PluginConfig.AddUnloadButton) { WristMenuAPI.Buttons.Add(new WristMenuButton("Unload Bundles", (ButtonClickEvent)delegate { UnloadAllModdedBundles(); })); } } private IEnumerator Start() { GM.SetRunningModded(); OldOtherLoaderDisablerPatcher.RestoreOtherLoaderDll(); ((MonoBehaviour)this).StartCoroutine(SpawnablePanelManager.Initialize()); yield break; } private void OnApplicationQuit() { UnlockathonManager.SaveAll(); } public override void OnSetup(IStageContext<Empty> ctx) { ctx.Loaders.Add("icon", (FileSystemInfo _) => default(Empty)); ctx.Loaders.Add("assembly", MainLoader.LoadAssembly); } public override IEnumerator OnRuntime(IStageContext<IEnumerator> ctx) { ctx.Loaders.Add("item_data", MainLoader.BundlePrepareLate); ctx.Loaders.Add("item_first_late", (FileSystemInfo handle) => MainLoader.BundleRegisterLate(handle, LoadOrder.LoadFirst)); ctx.Loaders.Add("item_unordered_late", (FileSystemInfo handle) => MainLoader.BundleRegisterLate(handle, LoadOrder.LoadUnordered)); ctx.Loaders.Add("item_last_late", (FileSystemInfo handle) => MainLoader.BundleRegisterLate(handle, LoadOrder.LoadLast)); ctx.Loaders.Add("item", (FileSystemInfo handle) => MainLoader.BundleLoadImmediate(handle, LoadOrder.LoadFirst)); ctx.Loaders.Add("item_unordered", (FileSystemInfo handle) => MainLoader.BundleLoadImmediate(handle, LoadOrder.LoadUnordered)); ctx.Loaders.Add("item_last", (FileSystemInfo handle) => MainLoader.BundleLoadImmediate(handle, LoadOrder.LoadLast)); MainLoader.LoadLegacyBundles(); foreach (DirectLoadMod directLoadMod in DirectLoadMods) { MainLoader.BundleLoadDirectFromDirectory(directLoadMod.Path, directLoadMod.LoadFirst.Split(new char[1] { ',' }), directLoadMod.LoadAny.Split(new char[1] { ',' }), directLoadMod.LoadLast.Split(new char[1] { ',' })); } DirectLoadMods.Clear(); LoaderStatus.ProgressUpdated += OnLoaderProgress; yield break; } private static void OnLoaderProgress(float progress) { if (!(progress < 1f)) { LoaderStatus.ProgressUpdated -= OnLoaderProgress; ItemSpawnerIDLinker.LinkQueuedEntries(); ItemManagerDebugger.TryPrintEntries(); SpawnerTilesDatabase.TryCreateTestCategories(); } } public static void RegisterDirectLoad(string path, string guid, string dependancies, string loadFirst, string loadAny, string loadLast) { DirectLoadMods.Add(new DirectLoadMod(path, loadFirst, loadAny, loadLast)); } private void UnloadAllModdedBundles() { foreach (string item in ManagedBundles.Keys.Where((string bundleID) => AnvilManager.m_bundles.m_lookup.ContainsKey(bundleID))) { OtherLogger.Log("Unloading bundle '" + item + "'"); AnvilCallback<AssetBundle> val = (AnvilCallback<AssetBundle>)(object)AnvilManager.m_bundles.m_lookup[item]; AnvilManager.m_bundles.m_loading.Remove((AnvilCallbackBase)(object)val); AnvilManager.m_bundles.m_lookup.Remove(item); val.Result.Unload(false); } } } internal static class PluginAssets { private const string PluginFolderName = "Sirdoggy-OtherLoaderPatched"; private static readonly string AssetsRelativePath = Path.Combine("Sirdoggy-OtherLoaderPatched", "assets"); private static readonly string AssetsFullPath = Path.Combine(Paths.PluginPath, AssetsRelativePath); public static readonly Sprite? UnknownFolder = SpriteMaker.CreateSpriteFromImage(Path.Combine(AssetsFullPath, "UnknownFolder.png")); public static readonly Sprite? Shapes = SpriteMaker.CreateSpriteFromImage(Path.Combine(AssetsFullPath, "Shapes.png")); public static readonly Sprite? Lock = SpriteMaker.CreateSpriteFromImage(Path.Combine(AssetsFullPath, "Lock.png")); public static readonly Sprite? SettingsIcon = SpriteMaker.CreateSpriteFromImage(Path.Combine(AssetsFullPath, "SettingsIcon.png")); public static readonly Texture2D? SpawnablePanelTexture = SpriteMaker.CreateTexture2DFromImage(Path.Combine(AssetsFullPath, "PanelTexture.png")); public const string CustomCategoriesHeaderLabel = "Modded Categories"; public static AssetBundleCreateRequest? OLPAssets => AssetBundle.LoadFromFileAsync(Path.Combine(AssetsFullPath, "otherloaderpatched_assets")); public static string CustomCategoriesButtonLabel(int? count) { return $"MODDED CATEGORIES ({count.GetValueOrDefault()})"; } public static string ModsLoadingProgressLabel(int percentage) { return $"Loading mods | {percentage}%"; } public static string UnlockathonUnlockedItems(int count) { return $"Unlocked items: {count}"; } } internal static class PluginConfig { public static bool OptimizeMemory; public static int MaxActiveLoaders; public static ItemUnlockerMode ItemUnlocker; public static ConfigEntry<bool> PreventSelectingModTagsFromHeldObject; public static ConfigEntry<bool> ShowSpawnItemButtonInPortableSpawner; public static ConfigEntry<ItemSpawnerSortGrouping> ItemSpawnerSortGrouping; public static ConfigEntry<ItemSpawnerSortOrder> ItemSpawnerSortOrder; public static ConfigEntry<bool> ColoredLogs; public static ConfigEntry<LogTag> LogTags; public static bool AddUnloadButton; public static bool EnableLogTraces; public static bool ShowDebugItemSpawnerEntries; public const bool DevMode = false; private static ConfigFile _configFile; private const string ConfigFileName = "OtherLoaderPatched.cfg"; public static void Initialize(PluginInfo pluginInfo) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown _configFile = new ConfigFile(Path.Combine(Paths.ConfigPath, "OtherLoaderPatched.cfg"), true); BindFields(); UniversalModPanel.RegisterPluginSettings(pluginInfo, _configFile); ShowSpawnItemButtonInPortableSpawner.SettingChanged += RefreshItemSpawnerUI; ItemSpawnerSortGrouping.SettingChanged += RefreshItemSpawnerUI; ItemSpawnerSortOrder.SettingChanged += RefreshItemSpawnerUI; } private static void RefreshItemSpawnerUI(object sender, EventArgs eventArgs) { SceneUtils.RunOnAllInstancesOfType<ItemSpawnerV2>((Action<ItemSpawnerV2>)delegate(ItemSpawnerV2 spawner) { spawner.RedrawStandardCanvasesSafe(); }, OtherLoader.ExceptionCatcher); } private static void BindFields() { OptimizeMemory = _configFile.Bind<bool>("General", "OptimizeMemory", false, "If true, assets that aren't set-up for on-demand loading will be automatically optimized for it. This reduces RAM usage for older mods, but has been reported to cause instability on Linux/Proton. Requires game restart.").Value; MaxActiveLoaders = _configFile.Bind<int>("General", "MaxActiveLoaders", 6, "Sets the number of mods that can be loading at once when first launching the game. Values less than 1 will result in all mods being loaded at the same time.").Value; ItemUnlocker = _configFile.Bind<ItemUnlockerMode>("General", "ItemUnlocker", ItemUnlockerMode.Disabled, "Cheat. Unlocks all reward/hidden items, including ones that can only spawn in T&H. Using this won't affect your H3VR save file. Requires game restart.").Value; PreventSelectingModTagsFromHeldObject = _configFile.Bind<bool>("General", "PreventSelectingModTagsFromHeldObject", true, "ModTags will be skipped when using the 'Select Tags From Held Object' button."); ShowSpawnItemButtonInPortableSpawner = _configFile.Bind<bool>("General", "ShowSpawnItemButtonInPortableSpawner", true, "Makes the spawn item button visible in the portable item spawner, letting you spawn objects without having to use the stylus."); ItemSpawnerSortGrouping = _configFile.Bind<ItemSpawnerSortGrouping>("General", "ItemSpawnerSortGrouping", global::OtherLoader.ItemSpawner.VanillaCategories.ItemSpawnerSortGrouping.ShowModdedItemsFirst, "Changes how items are grouped and displayed in the item spawner."); ItemSpawnerSortOrder = _configFile.Bind<ItemSpawnerSortOrder>("General", "ItemSpawnerSortOrder", global::OtherLoader.ItemSpawner.VanillaCategories.ItemSpawnerSortOrder.AlphabeticalByDisplayName, "'Vanilla' orders items alphabetically by their internal ItemIDs. 'AlphabeticalByDisplayName' orders items by their actual visible names. This does not affect how categories are ordered, only items."); ColoredLogs = _configFile.Bind<bool>("Logging", "ColoredLogs", true, "When true, logs will change colors depending on their LogTag."); LogTags = _configFile.Bind<LogTag>("Logging", "LogTags", LogTag.General, "Controls which types of logs should be enabled. Errors and warnings will always be logged, regardless of this setting."); AddUnloadButton = _configFile.Bind<bool>("Debug", "AddUnloadButton", false, "When true, you'll have a wrist menu button that can unload all modded asset bundles for testing purposes. Requires game restart.").Value; EnableLogTraces = _configFile.Bind<bool>("Debug", "EnableLogTraces", false, "Affects performance, only enable when debugging. When true, method call traces will be included in logs. Requires game restart.").Value; ShowDebugItemSpawnerEntries = _configFile.Bind<bool>("Debug", "ShowDebugItemSpawnerEntries", false, "When true, debug spawner entries will be added to the item spawner's custom categories tab. Requires game restart.").Value; } } public class SpawnablePanel : MonoBehaviour { private enum ConfirmState { None, DeleteProfile, ImportData } public GameObject LYT_UnlockathonOn; public GameObject LYT_UnlockathonOff; public OptionsPanel_ButtonSet BTNSet_EnableUnlockathon; public OptionsPanel_ButtonSet BTNSet_ProfileButtons; public List<GameObject> ProfileButtonsList; public GameObject BTN_CreateNewProfile; public GameObject LYT_UnlockathonProfileDetails; public Text TXT_UnlockathonUnlockedItems; public OptionsPanel_ButtonSet BTNSet_UnlockFromVault; public OptionsPanel_ButtonSet BTNSet_UnlockSecondaryItems; public OptionsPanel_ButtonSet BTNSet_KeepModded; public OptionsPanel_ButtonSet BTNSet_KeepGuns; public OptionsPanel_ButtonSet BTNSet_KeepAmmo; public OptionsPanel_ButtonSet BTNSet_KeepGrenadesExplosives; public OptionsPanel_ButtonSet BTNSet_KeepAttachments; public OptionsPanel_ButtonSet BTNSet_KeepMelee; public OptionsPanel_ButtonSet BTNSet_KeepToolsToys; public GameObject LYT_UnlockathonProfileSidePanel; public Text TXT_DeleteProfile; public Text TXT_ImportData; private const string TextPadding = " "; private ConfirmState _confirmState; public void OnSelectUnlockathonPage() { Boop(); RedrawUnlockathonCanvas(); } public void OnClose() { Boop(); SpawnablePanelManager.HidePanel(); } public void OnSetUnlockathonEnabled(bool isEnabled) { Boop(); UnlockathonManager.SetUnlockathonEnabled(isEnabled); } public void OnClickProfileButton(int index) { Boop(); UnlockathonManager.SetSelectedProfile(index); } public void OnSetUnlockItemsSpawnedFromVault(bool isEnabled) { Boop(); UnlockathonManager.SetUnlockItemsSpawnedFromVault(isEnabled); } public void OnSetUnlockSecondaryItems(bool isEnabled) { Boop(); UnlockathonManager.SetUnlockSecondaryItems(isEnabled); } public void OnSetKeepModded(bool isEnabled) { Boop(); UnlockathonManager.SetKeepModded(isEnabled); } public void OnSetKeepGuns(bool isEnabled) { Boop(); UnlockathonManager.SetKeepGuns(isEnabled); } public void OnSetKeepAmmo(bool isEnabled) { Boop(); UnlockathonManager.SetKeepAmmo(isEnabled); } public void OnSetKeepGrenadesExplosives(bool isEnabled) { Boop(); UnlockathonManager.SetKeepGrenadesExplosives(isEnabled); } public void OnSetKeepAttachments(bool isEnabled) { Boop(); UnlockathonManager.SetKeepAttachments(isEnabled); } public void OnSetKeepMelee(bool isEnabled) { Boop(); UnlockathonManager.SetKeepMelee(isEnabled); } public void OnSetKeepToolsToys(bool isEnabled) { Boop(); UnlockathonManager.SetKeepToolsToys(isEnabled); } public void OnCreateNewProfile() { Boop(); UnlockathonManager.CreateNewProfile(); } public void OnCurrentProfileDelete() { Boop(); if (_confirmState == ConfirmState.DeleteProfile) { UnlockathonManager.DeleteCurrentProfile(); return; } _confirmState = ConfirmState.DeleteProfile; RedrawConfirmableButtons(); } public void OnCurrentProfileRename() { Boop(); UnlockathonManager.RenameCurrentProfile(); } public void OnCurrentProfileImportData() { Boop(); if (_confirmState == ConfirmState.ImportData) { UnlockathonManager.ImportDataFromOldOtherLoaderToCurrentProfile(); return; } _confirmState = ConfirmState.ImportData; RedrawConfirmableButtons(); } private IEnumerator Start() { List<GameObject> profileButtonsList = ProfileButtonsList; if (profileButtonsList == null || profileButtonsList.Count != 9) { OtherLogger.LogError("ProfileButtonsList is not configured correctly in MeatKit!"); yield break; } UnlockathonManager.OnUnlockathonStateChanged += RedrawUnlockathonCanvas; RedrawUnlockathonCanvas(); yield return null; RedrawUnlockathonCanvas(); } private void OnDestroy() { UnlockathonManager.OnUnlockathonStateChanged -= RedrawUnlockathonCanvas; } private void Boop() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) SM.PlayGlobalUISound((GlobalUISound)0, ((Component)this).transform.position); } private void RedrawUnlockathonCanvas() { if (!UnlockathonManager.UnlockathonEnabled) { BTNSet_EnableUnlockathon.SetSelectedButton(1); LYT_UnlockathonOn.SetActive(false); LYT_UnlockathonOff.SetActive(true); } else { BTNSet_EnableUnlockathon.SetSelectedButton(0); LYT_UnlockathonOn.SetActive(true); LYT_UnlockathonOff.SetActive(false); RedrawUnlockathonProfiles(); } } private void RedrawUnlockathonProfiles() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) RedrawUnlockathonProfileDetails(); BTNSet_ProfileButtons.SetSelectedButton(UnlockathonManager.SelectedProfileIndex.GetValueOrDefault(9)); BTN_CreateNewProfile.SetActive(false); for (int i = 0; i < 9; i++) { GameObject val = ProfileButtonsList[i]; if (i < UnlockathonManager.Profiles.Count) { Profile profile = UnlockathonManager.Profiles[i]; val.GetComponent<Text>().text = " " + profile.DisplayName; val.SetActive(true); } else if (i == UnlockathonManager.Profiles.Count) { BTN_CreateNewProfile.transform.position = val.transform.position; BTN_CreateNewProfile.SetActive(true); val.SetActive(false); } else { val.SetActive(false); } } } private void RedrawUnlockathonProfileDetails() { if (UnlockathonManager.ActiveProfile == null) { LYT_UnlockathonProfileDetails.SetActive(false); LYT_UnlockathonProfileSidePanel.SetActive(false); return; } _confirmState = ConfirmState.None; RedrawConfirmableButtons(); LYT_UnlockathonProfileDetails.SetActive(true); LYT_UnlockathonProfileSidePanel.SetActive(true); int count = UnlockathonManager.ActiveProfile.GetArrayOfUnlockedSpawnerIDs().Length; TXT_UnlockathonUnlockedItems.text = PluginAssets.UnlockathonUnlockedItems(count); BTNSet_UnlockFromVault.SetSelectedButton((!UnlockathonManager.ActiveProfile.UnlockItemsSpawnedFromVault) ? 1 : 0); BTNSet_UnlockSecondaryItems.SetSelectedButton((!UnlockathonManager.ActiveProfile.UnlockSecondaryItems) ? 1 : 0); BTNSet_KeepModded.SetSelectedButton((!UnlockathonManager.ActiveProfile.KeepModded) ? 1 : 0); BTNSet_KeepGuns.SetSelectedButton((!UnlockathonManager.ActiveProfile.KeepGuns) ? 1 : 0); BTNSet_KeepAmmo.SetSelectedButton((!UnlockathonManager.ActiveProfile.KeepAmmo) ? 1 : 0); BTNSet_KeepGrenadesExplosives.SetSelectedButton((!UnlockathonManager.ActiveProfile.KeepGrenadesExplosives) ? 1 : 0); BTNSet_KeepAttachments.SetSelectedButton((!UnlockathonManager.ActiveProfile.KeepAttachments) ? 1 : 0); BTNSet_KeepMelee.SetSelectedButton((!UnlockathonManager.ActiveProfile.KeepMelee) ? 1 : 0); BTNSet_KeepToolsToys.SetSelectedButton((!UnlockathonManager.ActiveProfile.KeepToolsToys) ? 1 : 0); } private void RedrawConfirmableButtons() { switch (_confirmState) { case ConfirmState.None: TXT_DeleteProfile.text = "Delete Profile"; TXT_ImportData.text = "Import data from\nold OtherLoader"; break; case ConfirmState.DeleteProfile: TXT_DeleteProfile.text = "[Click to delete]"; TXT_ImportData.text = "Import data from\nold OtherLoader"; break; case ConfirmState.ImportData: TXT_DeleteProfile.text = "Delete Profile"; TXT_ImportData.text = "[Click to import]"; break; } } } internal static class SpawnablePanelManager { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ButtonClickEvent <>9__2_0; internal void <Initialize>b__2_0(object _, ButtonClickEventArgs args) { SpawnPanel(args.Hand.OtherHand, null, null); } } private static GameObject? _customCanvasGameO; private static LockablePanel? _lockablePanel; public static IEnumerator Initialize() { AssetBundleCreateRequest bundleRequest = PluginAssets.OLPAssets; yield return bundleRequest; if ((Object)(object)((bundleRequest != null) ? bundleRequest.assetBundle : null) == (Object)null) { OtherLogger.LogError("OLPAssets AssetBundle is null"); yield break; } AssetBundleRequest dataRequest = bundleRequest.assetBundle.LoadAssetAsync<GameObject>("PanelCanvasPrefab"); yield return dataRequest; Object obj = ((dataRequest != null) ? dataRequest.asset : null); GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if (val == null) { OtherLogger.LogError("Could not find panel GameObject in OLPAssets"); yield break; } _customCanvasGameO = val; _lockablePanel = new LockablePanel(); _lockablePanel.Configure += ConfigurePanel; _lockablePanel.TextureOverride = PluginAssets.SpawnablePanelTexture; ICollection<WristMenuButton> buttons = WristMenuAPI.Buttons; object obj2 = <>c.<>9__2_0; if (obj2 == null) { ButtonClickEvent val2 = delegate(object _, ButtonClickEventArgs args) { SpawnPanel(args.Hand.OtherHand, null, null); }; <>c.<>9__2_0 = val2; obj2 = (object)val2; } buttons.Add(new WristMenuButton("OtherLoader Panel", (ButtonClickEvent)obj2)); } public static void SpawnPanel(FVRViveHand? hand, Vector3? position, Vector3? rotation) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (_lockablePanel == null) { OtherLogger.LogError("LockablePanel is null"); return; } GameObject orCreatePanel = _lockablePanel.GetOrCreatePanel(); orCreatePanel.SetActive(true); FVRPhysicalObject component = orCreatePanel.GetComponent<FVRPhysicalObject>(); if ((Object)(object)component == (Object)null) { OtherLogger.LogError("FVRPhysicalObject is null"); return; } if ((Object)(object)hand != (Object)null) { hand.RetrieveObject(component); return; } if (position.HasValue) { ((Component)component).transform.position = position.Value; } if (rotation.HasValue) { Quaternion val = default(Quaternion); ((Quaternion)(ref val)).eulerAngles = rotation.Value; Quaternion rotation2 = val; ((Component)component).transform.rotation = rotation2; } } public static void HidePanel() { LockablePanel? lockablePanel = _lockablePanel; if (lockablePanel != null) { lockablePanel.GetOrCreatePanel().SetActive(false); } } private static void ConfigurePanel(GameObject panel) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) Transform val = panel.transform.Find("OptionsCanvas_0_Main/Canvas"); Object.Instantiate<GameObject>(_customCanvasGameO, val.position, val.rotation, val.parent); Object.Destroy((Object)(object)((Component)val).gameObject); } } } namespace OtherLoader.Unlockathon { internal class Profile { public bool UnlockItemsSpawnedFromVault; public bool UnlockSecondaryItems = true; public bool KeepModded; public bool KeepGuns; public bool KeepAmmo; public bool KeepGrenadesExplosives; public bool KeepAttachments; public bool KeepMelee; public bool KeepToolsToys = true; public string? FileName { get; set; } public string DisplayName { get; set; } public HashSet<string> UnlockedItemSpawnerIDs { get; set; } = new HashSet<string>(); public HashSet<string> UnlockedItemSpawnerIDsSecondaries { get; set; } = new HashSet<string>(); public HashSet<string> UnlockedObjectIDs { get; set; } = new HashSet<string>(); public HashSet<string> UnlockedObjectIDsSecondaries { get; set; } = new HashSet<string>(); public Profile(string displayName) { DisplayName = displayName; } public string[] GetArrayOfUnlockedSpawnerIDs() { if (!UnlockSecondaryItems) { return UnlockedItemSpawnerIDs.ToArray(); } return UnlockedItemSpawnerIDs.Concat(UnlockedItemSpawnerIDsSecondaries).ToArray(); } public bool IsSpawnerIDUnlocked(string id) { FVRObject val = IM.GetSpawnerID(id)?.MainObject; if ((Object)(object)val != (Object)null) { if (val.IsModContent && KeepModded) { return true; } if (val.IsFirearm() && KeepGuns) { return true; } if (val.IsAmmo() && KeepAmmo) { return true; } if (val.IsAttachment() && KeepAttachments) { return true; } if (val.IsExplosive() && KeepGrenadesExplosives) { return true; } if (val.IsMelee() && KeepMelee) { return true; } if (val.IsMisc() && KeepToolsToys) { return true; } } if (!UnlockSecondaryItems) { return UnlockedItemSpawnerIDs.Contains(id); } if (!UnlockedItemSpawnerIDs.Contains(id)) { return UnlockedItemSpawnerIDsSecondaries.Contains(id); } return true; } public bool ShouldSkipUnlockingID(string id) { if (!UnlockedItemSpawnerIDs.Contains(id)) { return UnlockedObjectIDs.Contains(id); } return true; } } internal static class UnlockathonInventoryManager { private struct UnlockRequest { public readonly string ID; public readonly bool IsSecondary; public UnlockRequest(string id, bool isSecondary) { ID = id; IsSecondary = isSecondary; } } public static ItemSpawnerID? GetUnlockableItemSpawnerID(string? id) { return GetUnlockableItemSpawnerIDPrivate(id, firstPass: true); } private static ItemSpawnerID? GetUnlockableItemSpawnerIDPrivate(string? id, bool firstPass) { if (id == null || !IM.HasSpawnedID(id)) { return null; } ItemSpawnerID spawnerID = IM.GetSpawnerID(id); if ((Object)(object)spawnerID == (Object)null) { return null; } if (!UnlockathonManager.UnlockathonEnabled) { return spawnerID; } ItemSpawnerID val = Object.Instantiate<ItemSpawnerID>(spawnerID); if (!IsItemSpawnerIDUnlocked(id)) { val.Sprite = PluginAssets.Lock; } if (firstPass) { ItemSpawnerID[] secondaries = spawnerID.Secondaries; if (secondaries != null && secondaries.Length > 0) { List<ItemSpawnerID> list = new List<ItemSpawnerID>(); secondaries = spawnerID.Secondaries; for (int i = 0; i < secondaries.Length; i++) { ItemSpawnerID unlockableItemSpawnerIDPrivate = GetUnlockableItemSpawnerIDPrivate(secondaries[i]?.ItemID, firstPass: false); if ((Object)(object)unlockableItemSpawnerIDPrivate != (Object)null) { list.Add(unlockableItemSpawnerIDPrivate); } } val.Secondaries = list.ToArray(); } } return val; } public static bool IsItemSpawnerIDUnlocked(string id) { return UnlockathonManager.ActiveProfile?.IsSpawnerIDUnlocked(id) ?? true; } public static void UnlockItem(FVRPhysicalObject? physicalObject) { if ((Object)(object)physicalObject == (Object)null) { return; } Profile activeProfile = UnlockathonManager.ActiveProfile; if (activeProfile != null) { if ((Object)(object)physicalObject.IDSpawnedFrom != (Object)null) { OtherLogger.Log("Skipping unlock for object '" + ((Object)physicalObject).name + "' spawned from item spawner.", LogTag.Unlockathon); return; } if ((Object)(object)((Component)physicalObject).GetComponent<SpawnLockDuplicateTag>() != (Object)null) { OtherLogger.Log("Skipping unlock for object '" + ((Object)physicalObject).name + "' because it's been duplicated by spawn lock.", LogTag.Unlockathon); return; } if ((Object)(object)((Component)physicalObject).GetComponent<SpawnedByRandomGunButtonTag>() != (Object)null) { OtherLogger.Log("Skipping unlock for object '" + ((Object)physicalObject).name + "' spawned by random gun spawner.", LogTag.Unlockathon); return; } if (!activeProfile.UnlockItemsSpawnedFromVault && (Object)(object)((Component)physicalObject).GetComponent<SpawnedFromVaultTag>() != (Object)null) { OtherLogger.Log("Skipping unlock for object '" + ((Object)physicalObject).name + "' spawned from vault.", LogTag.Unlockathon); return; } List<string> ids = new List<string> { physicalObject.ObjectWrapper?.ItemID, physicalObject.IDSpawnedFrom?.ItemID }; OtherLoader.CoroutineStarter.Invoke(UnlockItemsByID(ids)); } } public static IEnumerator UnlockItemsByID(IEnumerable<string?> ids) { Profile profile = UnlockathonManager.ActiveProfile; if (profile == null) { yield break; } HashSet<string> processedIDs = new HashSet<string>(); Queue<UnlockRequest> pendingRequests = new Queue<UnlockRequest>(); foreach (string id in ids) { if (!id.IsNullOrEmpty() && !profile.ShouldSkipUnlockingID(id)) { pendingRequests.Enqueue(new UnlockRequest(id, isSecondary: false)); } } if (pendingRequests.Count == 0) { yield break; } int processedCount = 0; while (pendingRequests.Count > 0) { UnlockRequest unlockRequest = pendingRequests.Dequeue(); string iD = unlockRequest.ID; bool isSecondary = unlockRequest.IsSecondary; if (iD.IsNullOrEmpty() || !processedIDs.Add(iD)) { continue; } OtherLogger.Log($"Attempting to unlock item by ID: {iD}, isSecondary: {isSecondary}", LogTag.Unlockathon); FVRObject valueOrNull = IM.OD.GetValueOrNull(iD); if ((Object)(object)valueOrNull != (Object)null) { if (isSecondary) { if (!profile.UnlockedObjectIDs.Contains(iD)) { profile.UnlockedObjectIDsSecondaries.Add(iD); } } else { profile.UnlockedObjectIDs.Add(iD); profile.UnlockedObjectIDsSecondaries.Remove(iD); } if (!valueOrNull.SpawnedFromId.IsNullOrEmpty()) { pendingRequests.Enqueue(new UnlockRequest(valueOrNull.SpawnedFromId, isSecondary)); } } if (IM.HasSpawnedID(iD)) { if (isSecondary) { if (!profile.UnlockedItemSpawnerIDs.Contains(iD)) { profile.UnlockedItemSpawnerIDsSecondaries.Add(iD); } } else { profile.UnlockedItemSpawnerIDs.Add(iD); profile.UnlockedItemSpawnerIDsSecondaries.Remove(iD); } ItemSpawnerID spawnerID = IM.GetSpawnerID(iD); if ((Object)(object)spawnerID != (Object)null) { if ((Object)(object)spawnerID.MainObject != (Object)null && !spawnerID.MainObject.ItemID.IsNullOrEmpty()) { pendingRequests.Enqueue(new UnlockRequest(spawnerID.MainObject.ItemID, isSecondary)); } if ((Object)(object)spawnerID.SecondObject != (Object)null && !spawnerID.SecondObject.ItemID.IsNullOrEmpty()) { pendingRequests.Enqueue(new UnlockRequest(spawnerID.SecondObject.ItemID, isSecondary)); } if (spawnerID.Secondaries != null) { ItemSpawnerID[] secondaries = spawnerID.Secondaries; foreach (ItemSpawnerID val in secondaries) { if ((Object)(object)val != (Object)null && !val.ItemID.IsNullOrEmpty()) { pendingRequests.Enqueue(new UnlockRequest(val.ItemID, isSecondary: true)); } } } if (spawnerID.Secondaries_ByStringID != null) { foreach (string item in spawnerID.Secondaries_ByStringID) { if (!item.IsNullOrEmpty()) { pendingRequests.Enqueue(new UnlockRequest(item, isSecondary: true)); } } } } } ItemSpawnerEntry valueOrNull2 = OtherLoader.SpawnerEntriesByID.GetValueOrNull(iD); if ((Object)(object)valueOrNull2 != (Object)null && valueOrNull2.SpawnWithIDs != null) { foreach (string spawnWithID in valueOrNull2.SpawnWithIDs) { if (!spawnWithID.IsNullOrEmpty()) { pendingRequests.Enqueue(new UnlockRequest(spawnWithID, isSecondary)); } } } processedCount++; if (processedCount % 50 == 0) { yield return null; } } UnlockathonManager.SaveActiveProfile(); } } internal static class UnlockathonManager { public const int MaxSupportedProfilesCount = 9; private static readonly string SavedProfilesDirectoryPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "My Games/H3VR/OtherLoaderPatched"); private static readonly string SavedPreferencesFilePath = Path.Combine(Paths.ConfigPath, "Sirdoggy.OLPSavedPreferences"); private const string ProfilePrefix = "profile_"; private const string JsonPostfix = ".json"; private const int MaxCharactersInProfileName = 20; public static List<Profile> Profiles { get; } = new List<Profile>(); public static bool UnlockathonEnabled { get; private set; } public static int? SelectedProfileIndex { get; private set; } private static bool IsKeyboardDisplayed { get; set; } public static Profile? ActiveProfile { get { if (!UnlockathonEnabled) { return null; } if (SelectedProfileIndex.HasValue) { return Profiles[SelectedProfileIndex.Value]; } return null; } } public static event Action? OnUnlockathonStateChanged; public static void Initialize() { OtherLogger.Log("Loading Unlockathon data..."); LoadProfiles(); LoadPreferences(); StartListeningToKeyboardEvents(); NotifyStateChanged(); OtherLogger.Log("Loading Unlockathon data complete!"); } public static void SetUnlockathonEnabled(bool enabled) { UnlockathonEnabled = enabled; SavePreferences(); NotifyStateChanged(); } public static void SetSelectedProfile(int index) { SelectedProfileIndex = Mathf.Clamp(index, 0, Profiles.Count - 1); SavePreferences(); NotifyStateChanged(); } public static void SetUnlockItemsSpawnedFromVault(bool enabled) { if (ActiveProfile != null) { ActiveProfile.UnlockItemsSpawnedFromVault = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetUnlockSecondaryItems(bool enabled) { if (ActiveProfile != null) { ActiveProfile.UnlockSecondaryItems = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetKeepModded(bool enabled) { if (ActiveProfile != null) { ActiveProfile.KeepModded = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetKeepGuns(bool enabled) { if (ActiveProfile != null) { ActiveProfile.KeepGuns = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetKeepAmmo(bool enabled) { if (ActiveProfile != null) { ActiveProfile.KeepAmmo = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetKeepGrenadesExplosives(bool enabled) { if (ActiveProfile != null) { ActiveProfile.KeepGrenadesExplosives = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetKeepAttachments(bool enabled) { if (ActiveProfile != null) { ActiveProfile.KeepAttachments = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetKeepMelee(bool enabled) { if (ActiveProfile != null) { ActiveProfile.KeepMelee = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void SetKeepToolsToys(bool enabled) { if (ActiveProfile != null) { ActiveProfile.KeepToolsToys = enabled; SaveProfile(ActiveProfile); NotifyStateChanged(); } } public static void CreateNewProfile() { if (Profiles.Count >= 9) { OtherLogger.LogWarning("Can't create a new profile, max limit reached."); return; } Profile profile = new Profile($"Profile {Profiles.Count + 1}"); Profiles.Add(profile); SelectedProfileIndex = Profiles.Count - 1; SaveProfile(profile); SortProfiles(); SavePreferences(); NotifyStateChanged(); } public static void RenameCurrentProfile() { SteamVR instance = SteamVR.instance; CVROverlay val = ((instance != null) ? instance.overlay : null); if (val == null) { OtherLogger.LogError("SteamVR overlay is null"); } else { OtherLoader.CoroutineStarter.Invoke(ShowRenameProfileOverlayCoroutine(val)); } } public static void DeleteCurrentProfile() { if (!SelectedProfileIndex.HasValue || ActiveProfile == null) { return; } if (ActiveProfile.FileName != null) { string filePath = Path.Combine(SavedProfilesDirectoryPath, ActiveProfile.FileName); if (File.Exists(filePath)) { OtherLoader.ExceptionCatcher.Run(delegate { File.Delete(filePath); }); } } Profiles.RemoveAt(SelectedProfileIndex.Value); SelectedProfileIndex = null; SavePreferences(); NotifyStateChanged(); } public static void ImportDataFromOldOtherLoaderToCurrentProfile() { string text = Path.Combine(Application.dataPath.Replace("h3vr_Data", "OtherLoader"), "UnlockedItems.json"); if (!File.Exists(text)) { OtherLogger.LogWarning("Could not find old OtherLoader save data: " + text); return; } try { OldOtherLoaderDataDto oldOtherLoaderDataDto = JsonConvert.DeserializeObject<OldOtherLoaderDataDto>(File.ReadAllText(text)); if (oldOtherLoaderDataDto?.UnlockedItemIDs == null || oldOtherLoaderDataDto.UnlockedItemIDs.Length == 0) { OtherLogger.LogWarning("Old OtherLoader save data is empty."); } else { OtherLoader.CoroutineStarter.Invoke(ImportDataRoutine(oldOtherLoaderDataDto.UnlockedItemIDs)); } } catch (Exception ex) { OtherLogger.LogError("Failed to import old OtherLoader data: " + ex.Message); } } private static IEnumerator ImportDataRoutine(string[] ids) { yield return UnlockathonInventoryManager.UnlockItemsByID(ids); OtherLogger.Log($"Successfully imported {ids.Length} items from old OtherLoader."); } public static void SaveAll() { foreach (Profile profile in Profiles) { SaveProfile(profile); } SavePreferences(); } public static void SaveActiveProfile() { if (ActiveProfile != null) { SaveProfile(ActiveProfile); NotifyStateChanged(); } } private static void NotifyStateChanged() { SceneUtils.RunOnAllInstancesOfType<ItemSpawnerV2>((Action<ItemSpawnerV2>)delegate(ItemSpawnerV2 spawner) { spawner.RedrawStandardCanvasesSafe(); }, OtherLoader.ExceptionCatcher); UnlockathonManager.OnUnlockathonStateChanged?.Invoke(); } private static IEnumerator ShowRenameProfileOverlayCoroutine(CVROverlay overlay) { if (ActiveProfile != null) { IsKeyboardDisplayed = true; yield return (object)new WaitForSecondsRealtime(0.3f); overlay.ShowKeyboard(0, 0, "Rename Profile", 20u, ActiveProfile.DisplayName, false, 0uL); } } private static void StartListeningToKeyboardEvents() { Event<VREvent_t> val = SteamVR_Events.System((EVREventType)1202); Event<VREvent_t> val2 = SteamVR_Events.System((EVREventType)1200); if (val == null || val2 == null) { OtherLogger.LogError("VREvent_KeyboardDone/Closed is null"); return; } val2.Listen((UnityAction<VREvent_t>)HandleKeyboardClosed); val.Listen((UnityAction<VREvent_t>)HandleKeyboardDone); } private static void HandleKeyboardClosed(VREvent_t _) { IsKeyboardDisplayed = false; } private static void HandleKeyboardDone(VREvent_t _) { if (!IsKeyboardDisplayed) { return; } IsKeyboardDisplayed = false; if (ActiveProfile == null) { return; } StringBuilder stringBuilder = new StringBuilder(160); SteamVR instance = SteamVR.instance; if (instance != null) { CVROverlay overlay = instance.overlay; if (overlay != null) { overlay.GetKeyboardText(stringBuilder, 160u); } } string text = stringBuilder.ToString(); if (string.IsNullOrEmpty(text)) { OtherLogger.Log("Can not rename Unlockathon profile to empty string, aborting..."); return; } string displayName = ActiveProfile.DisplayName; ActiveProfile.DisplayName = text.Take(20).JoinToString(""); OtherLogger.Log("Renamed Unlockathon profile from '" + displayName + "' to '" + text + "'"); if (ActiveProfile.FileName != null) { string filePath = Path.Combine(SavedProfilesDirectoryPath, ActiveProfile.FileName); if (File.Exists(filePath)) { OtherLoader.ExceptionCatcher.Run(delegate { File.Delete(filePath); ActiveProfile.FileName = null; }); } else { ActiveProfile.FileName = null; } } SaveProfile(ActiveProfile); SortProfiles(); SavePreferences(); NotifyStateChanged(); } private static void SortProfiles() { Profile activeProfile = ActiveProfile; Profiles.Sort((Profile a, Profile b) => string.Compare(a.FileName, b.FileName, StringComparison.Ordinal)); if (activeProfile != null) { SelectedProfileIndex = Profiles.IndexOf(activeProfile); } } private static void SaveProfile(Profile profile) { string text; string path; if (profile.FileName != null) { text = profile.FileName; path = Path.Combine(SavedProfilesDirectoryPath, text); } else { text = "profile_" + ReplaceDisallowedFileNameChars(profile.DisplayName) + ".json"; path = Path.Combine(SavedProfilesDirectoryPath, text); int num = 1; while (File.Exists(path)) { text = "profile_" + ReplaceDisallowedFileNameChars(profile.DisplayName) + $"({num})" + ".json"; path = Path.Combine(SavedProfilesDirectoryPath, text); num++; } profile.FileName = text; } try { string contents = JsonConvert.SerializeObject((object)new ProfileDto { DisplayName = profile.DisplayName, UnlockedItemSpawnerIDs = profile.UnlockedItemSpawnerIDs.ToArray(), UnlockedItemSpawnerIDsSecondaries = profile.UnlockedItemSpawnerIDsSecondaries.ToArray(), UnlockedObjectIDs = profile.UnlockedObjectIDs.ToArray(), UnlockedObjectIDsSecondaries = profile.UnlockedObjectIDsSecondaries.ToArray(), UnlockItemsSpawnedFromVault = profile.UnlockItemsSpawnedFromVault, UnlockSecondaryItems = profile.UnlockSecondaryItems, KeepModded = profile.KeepModded, KeepGuns = profile.KeepGuns, KeepAmmo = profile.KeepAmmo, KeepGrenadesExplosives = profile.KeepGrenadesExplosives, KeepAttachments = profile.KeepAttachments, KeepMelee = profile.KeepMelee, KeepToolsToys = profile.KeepToolsToys }, (Formatting)1); File.WriteAllText(path, contents); } catch (Exception ex) { OtherLogger.LogError("Failed to save profile in file '" + text + "': " + ex.Message); } } private static string ReplaceDisallowedFileNameChars(string fileName) { char[] disallowedChars = Path.GetInvalidFileNameChars(); return new string(fileName.Select((char character) => (!disallowedChars.Contains(character)) ? character : '_').ToArray()); } private static void SavePreferences() { try { string contents = JsonConvert.SerializeObject((object)new PreferencesDto { UnlockathonEnabled = UnlockathonEnabled, UnlockathonSelectedProfileFileName = ((!SelectedProfileIndex.HasValue) ? null : Profiles[SelectedProfileIndex.Value]?.FileName) }, (Formatting)1); File.WriteAllText(SavedPreferencesFilePath, contents); } catch (Exception ex) { OtherLogger.LogError("Failed to save preferences: " + ex.Message); } } private static void LoadProfiles() { if (Profiles.Count > 0) { OtherLogger.Log("Profiles have already been loaded."); return; } if (!Directory.Exists(SavedProfilesDirectoryPath)) { OtherLogger.Log("Creating new directory for Unlockathon profiles."); Directory.CreateDirectory(SavedProfilesDirectoryPath); return; } List<string> list = (from name in Directory.GetFiles(SavedProfilesDirectoryPath, "profile_*.json", SearchOption.TopDirectoryOnly) orderby name select name).Take(9).ToList(); if (list.Count == 0) { OtherLogger.Log("No saved profiles, creating a default profile."); Profile profile = new Profile("Profile 1"); Profiles.Add(profile); SelectedProfileIndex = 0; SaveProfile(profile); SavePreferences(); return; } foreach (string item2 in list) { try { ProfileDto profileDto = JsonConvert.DeserializeObject<ProfileDto>(File.ReadAllText(item2)); if (profileDto == null) { OtherLogger.LogWarning("Profile data in file `" + item2 + "` is null, deleting it..."); File.Delete(item2); continue; } Profile profile2 = new Profile(profileDto.DisplayName ?? "Restored Profile") { FileName = Path.GetFileName(item2) }; if (profileDto.UnlockedItemSpawnerIDs != null) { Profile profile3 = profile2; HashSet<string> hashSet = new HashSet<string>(); string[] unlockedItemSpawnerIDs = profileDto.UnlockedItemSpawnerIDs; foreach (string item in unlockedItemSpawnerIDs) { hashSet.Add(item); } profile3.UnlockedItemSpawnerIDs = hashSet; } if (profileDto.UnlockedItemSpawnerIDsSecondaries != null) { Profile profile3 = profile2; HashSet<string> hashSet = new HashSet<string>(); string[] unlockedItemSpawnerIDs = profileDto.UnlockedItemSpawnerIDsSecondaries; foreach (string item in unlockedItemSpawnerIDs) { hashSet.Add(item); } profile3.UnlockedItemSpawnerIDsSecondaries = hashSet; } if (profileDto.UnlockedObjectIDs != null) { Profile profile3 = profile2; HashSet<string> hashSet = new HashSet<string>(); string[] unlockedItemSpawnerIDs = profileDto.UnlockedObjectIDs; foreach (string item in unlockedItemSpawnerIDs) { hashSet.Add(item); } profile3.UnlockedObjectIDs = hashSet; } if (profileDto.UnlockedObjectIDsSecondaries != null) { Profile profile3 = profile2; HashSet<string> hashSet = new HashSet<string>(); string[] unlockedItemSpawnerIDs = profileDto.UnlockedObjectIDsSecondaries; foreach (string item in unlockedItemSpawnerIDs) { hashSet.Add(item); } profile3.UnlockedObjectIDsSecondaries = hashSet; } if (profileDto.UnlockItemsSpawnedFromVault.HasValue) { profile2.UnlockItemsSpawnedFromVault = profileDto.UnlockItemsSpawnedFromVault.Value; } if (profileDto.UnlockSecondaryItems.HasValue) { profile2.UnlockSecondaryItems = profileDto.UnlockSecondaryItems.Value; } if (profileDto.KeepModded.HasValue) { profile2.KeepModded = profileDto.KeepModded.Value; } if (profileDto.KeepGuns.HasValue) { profile2.KeepGuns = profileDto.KeepGuns.Value; } if (profileDto.KeepAmmo.HasValue) { profile2.KeepAmmo = profileDto.KeepAmmo.Value; } if (profileDto.KeepGrenadesExplosives.HasValue) { profile2.KeepGrenadesExplosives = profileDto.KeepGrenadesExplosives.Value; } if (profileDto.KeepAttachments.HasValue) { profile2.KeepAttachments = profileDto.KeepAttachments.Value; } if (profileDto.KeepMelee.HasValue) { profile2.KeepMelee = profileDto.KeepMelee.Value; } if (profileDto.KeepToolsToys.HasValue) { profile2.KeepToolsToys = profileDto.KeepToolsToys.Value; } Profiles.Add(profile2); } catch (Exception ex) { OtherLogger.LogError("Failed to load profile data from file '" + item2 + "': " + ex.Message); } } if (Profiles.Count != list.Count) { OtherLogger.LogWarning($"Loaded {Profiles.Count}/{list.Count} saved profiles."); } else { OtherLogger.Log($"Successfully loaded {Profiles.Count} saved profiles."); } } private static void LoadPreferences() { if (!File.Exists(SavedPreferencesFilePath)) { OtherLogger.Log("No saved preferences file."); return; } try { string text = File.ReadAllText(SavedPreferencesFilePath); PreferencesDto preferencesDto = JsonConvert.DeserializeObject<PreferencesDto>(text); if (preferencesDto == null) { OtherLogger.LogWarning("Preferences data is null."); return; } if (preferencesDto.UnlockathonEnabled.HasValue) { UnlockathonEnabled = preferencesDto.UnlockathonEnabled.Value; } if (preferencesDto.UnlockathonSelectedProfileFileName != null) { int num = Profiles.FindIndex((Profile profile) => profile.FileName == preferencesDto.UnlockathonSelectedProfileFileName); if (num != -1) { SelectedProfileIndex = num; } } else { int? unlockathonSelectedProfileIndex = preferencesDto.UnlockathonSelectedProfileIndex; if (unlockathonSelectedProfileIndex.HasValue) { SelectedProfileIndex = Mathf.Clamp(unlockathonSelectedProfileIndex.Value, 0, Profiles.Count - 1); } } } catch (Exception ex) { OtherLogger.LogError("Failed to load preferences: " + ex.Message); return; } OtherLogger.Log("Successfully loaded saved preferences."); } } } namespace OtherLoader.Unlockathon.Tags { internal class SpawnedByRandomGunButtonTag : MonoBehaviour { } internal class SpawnedFromVaultTag : MonoBehaviour { } internal class SpawnLockDuplicateTag : MonoBehaviour { } } namespace OtherLoader.Unlockathon.Patches { [HarmonyWrapSafe] [HarmonyPatch(typeof(FVRPhysicalObject))] internal class ItemPickupPatcher { private static string? _nextItemDuplicateID; [HarmonyPrefix] [HarmonyPatch("BeginInteraction")] private static void BeginInteraction_Prefix(FVRPhysicalObject __instance) { if (_nextItemDuplicateID != null && _nextItemDuplicateID == __instance?.ObjectWrapper?.ItemID) { OtherLogger.Log("Adding SpawnLockDuplicateTag to object: " + ((Object)__instance).name, LogTag.Unlockathon); if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<SpawnLockDuplicateTag>())) { ((Object)((Component)__instance).gameObject.AddComponent<SpawnLockDuplicateTag>()).hideFlags = (HideFlags)61; } } else { _nextItemDuplicateID = null; UnlockathonInventoryManager.UnlockItem(__instance); } } [HarmonyPrefix] [HarmonyPatch("DuplicateFromSpawnLock")] private static void DuplicateFromSpawnLock_Prefix(FVRPhysicalObject __instance) { _nextItemDuplicateID = __instance?.ObjectWrapper?.ItemID; } } [HarmonyWrapSafe] [HarmonyPatch(typeof(ItemSpawnerV2))] internal static class ItemSpawnerV2UnlockathonPatcher { [HarmonyTranspiler] [HarmonyPatch("RedrawSimpleCanvas")] private static IEnumerable<CodeInstruction> RedrawSimpleCanvas_Transpiler_LockItems(IEnumerable<CodeInstruction> instructions) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) List<CodeInstruction> list = instructions.ToList(); try { return new CodeMatcher((IEnumerable<CodeInstruction>)list, (ILGenerator)null).SearchForward((Func<CodeInstruction, bool>)((CodeInstruction code) => code.opcode == OpCodes.Call && code.operand == AccessTools.Method(typeof(IM), "GetSpawnerID", (Type[])null, (Type[])null))).SetAndAdvance(OpCodes.Call, (object)AccessTools.Method(typeof(UnlockathonInventoryManager), "GetUnlockableItemSpawnerID", (Type[])null, (Type[])null)).InstructionEnumeration(); } catch (Exception arg) { OtherLogger.LogError(string.Format("Exception in {0} transpiler: {1}", "RedrawSimpleCanvas_Transpiler_LockItems", arg)); return list; } } [HarmonyTranspiler] [HarmonyPatch("RedrawListCanvas")] private static IEnumerable<CodeInstruction> RedrawListCanvas_Transpiler_LockItems(IEnumerable<CodeInstruction> instructions) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) List<CodeInstruction> list = instructions.ToList(); try { return new CodeMatcher((IEnumerable<CodeInstruction>)list, (ILGenerator)null).SearchForward((Func<CodeInstruction, bool>)((CodeInstruction code) => code.opcode == OpCodes.Call && code.operand == AccessTools.Method(typeof(IM), "GetSpawnerID", (Type[])null, (Type[])null))).SetAndAdvance(OpCodes.Call, (object)AccessTools.Method(typeof(UnlockathonInventoryManager), "GetUnlockableItemSpawnerID", (Type[])null, (Type[])null)).SearchForward((Func<CodeInstruction, bool>)((CodeInstruction code) => code.opcode == OpCodes.Call && code.operand == AccessTools.Method(typeof(IM), "GetSpawnerID", (Type[])null, (Type[])null))) .SetAndAdvance(OpCodes.Call, (object)AccessTools.Method(typeof(UnlockathonInventoryManager), "GetUnlockableItemSpawnerID", (Type[])null, (Type[])null)) .InstructionEnumeration(); } catch (Exception arg) { OtherLogger.LogError(string.Format("Exception in {0} transpiler: {1}", "RedrawListCanvas_Transpiler_LockItems", arg)); return list; } } [HarmonyTranspiler] [HarmonyPatch("RedrawDetailsCanvas")] private static IEnumerable<CodeInstruction> RedrawDetailsCanvas_Transpiler_LockItems(IEnumerable<CodeInstruction> instructions) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) List<CodeInstruction> list = instructions.ToList(); try { return new CodeMatcher((IEnumerable<CodeInstruction>)list, (ILGenerator)null).SearchForward((Func<CodeInstruction, bool>)((CodeInstruction code) => code.opcode == OpCodes.Call && code.operand == AccessTools.Method(typeof(IM), "GetSpawnerID", (Type[])null, (Type[])null))).SetAndAdvance(OpCodes.Call, (object)AccessTools.Method(typeof(UnlockathonInventoryManager), "GetUnlockableItemSpawnerID", (Type[])null, (Type[])null)).SearchForward((Func<CodeInstruction, bool>)((CodeInstruction code) => code.opcode == OpCodes.Call && code.operand == AccessTools.Method(typeof(IM), "GetSpawnerID", (Type[])null, (Type[])null))) .SetAndAdvance(OpCodes.Call, (object)AccessTools.Method(typeof(UnlockathonInventoryManager), "GetUnlockableItemSpawnerID", (Type[])null, (Type[])null)) .InstructionEnumeration(); } catch (Exception arg) { OtherLogger.LogError(string.Format("Exception in {0} transpiler: {1}", "RedrawDetailsCanvas_Transpiler_LockItems", arg)); return list; } } [HarmonyPriority(100)] [HarmonyPostfix] [HarmonyPatch("RedrawDetailsCanvas")] private static void RedrawDetailsCanvas_Postfix_ShowLockedDetails(ItemSpawnerV2 __instance) { if (string.IsNullOrEmpty(__instance.m_selectedID)) { return; } bool flag = UnlockathonInventoryManager.IsItemSpawnerIDUnlocked(__instance.m_selectedID); OtherLogger.Log($"ItemSpawnerID: {__instance.m_selectedID}, Unlocked: {flag}", LogTag.Unlockathon); if (flag) { return; } GameObject bTN_SpawnSelectedObject = __instance.BTN_SpawnSelectedObject; if (bTN_SpawnSelectedObject != null) { GameObject gameObject = bTN_SpawnSelectedObject.gameObject; if (gameObject != null) { gameObject.SetActive(false); } } } } [HarmonyWrapSafe] internal static class RandomGunSpawnUnlockathonPatcher { private static float _lastClickTimestamp = -100f; private const float MarginSeconds = 5f; [HarmonyPrefix] [HarmonyPatch(typeof(ItemSpawnerV2), "BTN_TryToSpawnRandomGun")] private static void BTN_TryToSpawnRandomGun_Prefix() { _lastClickTimestamp = Time.time; } [HarmonyPrefix] [HarmonyPatch(typeof(FVRInteractiveObject), "Start")] private static void FVRPhysicalObject_Start_Prefix(FVRInteractiveObject __instance) { if (__instance is FVRPhysicalObject && !(_lastClickTimestamp + 5f < Time.time)) { OtherLogger.Log("Adding SpawnedByRandomGunButtonTag to object: " + ((Object)__instance).name, LogTag.Unlockathon); if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<SpawnedByRandomGunButtonTag>())) { ((Object)((Component)__instance).gameObject.AddComponent<SpawnedByRandomGunButtonTag>()).hideFlags = (HideFlags)61; } } } } [HarmonyWrapSafe] [HarmonyPatch(typeof(VaultSystem))] internal static class VaultSpawnUnlockathonPatcher { [HarmonyPrefix] [HarmonyPatch("SpawnVaultFile")] private static void SpawnVaultFile_Prefix_AddTagToObjects(ref ReturnObjectListDelegate del) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown ReturnObjectListDelegate originalDel = del; del = (ReturnObjectListDelegate)delegate(List<FVRPhysicalObject> rootObjs) { OtherLoader.ExceptionCatcher.Run(delegate { if (rootObjs != null) { foreach (FVRPhysicalObject rootObj in rootObjs) { if (!((Object)(object)rootObj == (Object)null)) { List<FVRPhysicalObject> list = new List<FVRPhysicalObject>(); list.Add(rootObj); list.AddRange(rootObj.GetAllChildPhysicalObjectsRecursively()); foreach (FVRPhysicalObject item in list) { OtherLogger.Log("Adding SpawnedFromVaultTag to object: " + ((Object)item).name, LogTag.Unlockathon); if (!Object.op_Implicit((Object)(object)((Component)item).GetComponent<SpawnedFromVaultTag>())) { ((Object)((Component)item).gameObject.AddComponent<SpawnedFromVaultTag>()).hideFlags = (HideFlags)61; } } } } } }); ReturnObjectListDelegate obj = originalDel; if (obj != null) { obj.Invoke(rootObjs); } }; } } } namespace OtherLoader.Unlockathon.Data { public class OldOtherLoaderDataDto { public string[]? UnlockedItemIDs { get; set; } } internal class PreferencesDto { public bool? UnlockathonEnabled { get; set; } public string? UnlockathonSelectedProfileFileName { get; set; } public int? UnlockathonSelectedProfileIndex { get; [Obsolete] set; } } internal class ProfileDto { public string? DisplayName { get; set; } public bool? UnlockItemsSpawnedFromVault { get; set; } public bool? UnlockSecondaryItems { get; set; } public bool? KeepModded { get; set; } public bool? KeepGuns { get; set; } public bool? KeepAmmo { get; set; } public bool? KeepGrenadesExplosives { get; set; } public bool? KeepAttachments { get; set; } public bool? KeepMelee { get; set; } public bool? KeepToolsToys { get; set; } public string[]? UnlockedItemSpawnerIDs { get; set; } public string[]? UnlockedItemSpawnerIDsSecondaries { get; set; } public string[]? UnlockedObjectIDs { get; set; } public string[]? UnlockedObjectIDsSecondaries { get; set; } } } namespace OtherLoader.QuickbeltPanel { internal class QBslotPageController : MonoBehaviour { public const int QbsPerPage = 14; public OptionsPanel_ButtonSet QBslotButtonSet; public GameObject ButtonNextPage; public GameObject ButtonPreviousPage; public int currentPage; public void Start() { SetVisibility(); } public void SetButtons() { ButtonNextPage.SetActive(true); ButtonPreviousPage.SetActive(true); if (currentPage <= 0) { ButtonPreviousPage.SetActive(false); } int num = Mathf.CeilToInt((float)(QBslotButtonSet.ButtonsInSet.Length / 14)); if (currentPage >= num) { ButtonNextPage.SetActive(false); } } public void SetVisibility() { SetButtons(); FVRPointableButton[] buttonsInSet = QBslotButtonSet.ButtonsInSet; for (int i = 0; i < buttonsInSet.Length; i++) { ((Component)buttonsInSet[i]).gameObject.SetActive(false); } int num = currentPage * 14; int num2 = num + 14; for (int j = num; j < num2 && j < QBslotButtonSet.ButtonsInSet.Length; j++) { ((Component)QBslotButtonSet.ButtonsInSet[j]).gameObject.SetActive(true); } } public void GotoPreviousPage() { currentPage--; SetVisibility(); } public void GotoNextPage() { currentPage++; SetVisibility(); } } [HarmonyWrapSafe] internal static class QuickbeltPanelPatcher { [HarmonyPrefix] [HarmonyPatch(typeof(OptionsScreen_Quickbelt), "Awake")] private static bool Awake_Prefix_AddScreens(OptionsScreen_Quickbelt __instance) { //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown OptionsScreen_Quickbelt __instance2 = __instance; QBslotPageController pageController = ((Component)__instance2).gameObject.AddComponent<QBslotPageController>(); pageController.QBslotButtonSet = __instance2.OBS_SlotStyle; GameObject gameObject = ((Component)__instance2.OBS_Handedness.ButtonsInSet[0]).gameObject; FVRPointableButton[] buttonsInSet = __instance2.OBS_SlotStyle.ButtonsInSet; for (int i = 0; i < buttonsInSet.Length; i++) { Object.Destroy((Object)(object)((Component)buttonsInSet[i]).gameObject); } __instance2.OBS_SlotStyle.ButtonsInSet = (FVRPointableButton[])(object)new FVRPointableButton[ManagerSingleton<GM>.Instance.QuickbeltConfigurations.Length]; for (int j = 0; j < __instance2.OBS_SlotStyle.ButtonsInSet.Length; j++) { int num = j % 14; int column = num % 4; int row = (int)Mathf.Floor((float)num / 4f); OtherLogger.Log("Adding QB " + ((Object)ManagerSingleton<GM>.Instance.QuickbeltConfigurations[j]).name, LogTag.BundleLoading); GameObject val = Object.Instantiate<GameObject>(gameObject, ((Component)__instance2.OBS_SlotStyle).transform, true); FVRPointableButton component = val.GetComponent<FVRPointableButton>(); __instance2.OBS_SlotStyle.ButtonsInSet[j] = component; string text = ((Object)ManagerSingleton<GM>.Instance.QuickbeltConfigurations[j]).name.Split(new char[1] { '_' }).Last(); Button uiButton = SetQBSlotOptionsPanelButton(val, row, column, text); ((UnityEvent)uiButton.onClick).AddListener((UnityAction)delegate { __instance2.SetSlotStyle(((Component)uiButton).transform.GetSiblingIndex()); }); ((UnityEvent)uiButton.onClick).AddListener((UnityAction)delegate { __instance2.OBS_SlotStyle.SetSelectedButton(((Component)uiButton).transform.GetSiblingIndex()); }); } Button val2 = SetQBSlotOptionsPanelButton(Object.Instantiate<GameObject>(gameObject, ((Component)__instance2.OBS_SlotStyle).transform, true), 3, 2, "Previous Page"); ((UnityEvent)val2.onClick).AddListener((UnityAction)delegate { pageController.GotoPreviousPage(); }); pageController.ButtonPreviousPage = ((Component)val2).gameObject; val2 = SetQBSlotOptionsPanelButton(Object.Instantiate<GameObject>(gameObject, ((Component)__instance2.OBS_SlotStyle).transform, true), 3, 3, "Next Page"); ((UnityEvent)val2.onClick).AddListener((UnityAction)delegate { pageController.GotoNextPage(); }); pageController.ButtonNextPage = ((Component)val2).gameObject; return true; } public static Button SetQBSlotOptionsPanelButton(GameObject button, int row, int column, string text) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) float num = -100 + 125 * column; float num2 = -40 + -45 * row; button.transform.localPosition = new Vector3(num, num2, button.transform.localPosition.z); ((Component)button.gameObject.transform.GetChild(0)).GetComponent<Text>().text = text; Button component = button.GetComponent<Button>(); ((UnityEventBase)component.onClick).RemoveAllListeners(); return component; } [HarmonyPrefix] [HarmonyPatch(typeof(FVRPlayerBody), "ConfigureQuickbelt")] private static bool ConfigureQuickbelt_Prefix_PreventIndexOutOfRange(ref int index) { index = Mathf.Clamp(index, 0, ManagerSingleton<GM>.Instance.QuickbeltConfigurations.Length - 1); return true; } } } namespace OtherLoader.Logging { [Flags] internal enum LogTag { None = 0, General = 1, Assets = 2, BundleLoading = 4, ItemSpawnerPatches = 8, ItemSpawnerTagging = 0x10, ItemUnlocker = 0x20, Unlockathon = 0x40, All = 0x1F } internal static class OtherLogger { private static ManualLogSource _logSource; public static void Initialize(ManualLogSource log) { _logSource = log; } public static void LogInfo(string log) { _logSource.LogInfo((object)log); } public static void LogWarning(string log) { _logSource.LogWarning((object)log); } public static void LogError(string log) { _logSource.LogError((object)log); } public static void LogFatal(string log) { _logSource.LogFatal((object)log); } public static void Log(string log, LogTag tag = LogTag.General) { if ((tag == LogTag.None || tag == LogTag.All) ? true : false) { LogError("Invalid log tag"); } else if (CanBeLogged(tag)) { ConsoleColor? color = null; if (PluginConfig.ColoredLogs.Value) { color = tag switch { LogTag.Assets => ConsoleColor.Blue, LogTag.BundleLoading => ConsoleColor.DarkCyan, LogTag.ItemSpawnerPatches => ConsoleColor.DarkMagenta, LogTag.ItemSpawnerTagging => ConsoleColor.DarkGreen, LogTag.ItemUnlocker => ConsoleColor.DarkBlue, LogTag.Unlockathon => ConsoleColor.DarkYellow, _ => null, }; } if (color.HasValue) { LogLevelColorPatcher.QueueColor(color); } _logSource.LogMessage((object)log); if ((object)null != null) { ((Harmony)null).UnpatchSelf(); } } } private static bool CanBeLogged(LogTag logTag) { return (PluginConfig.LogTags.Value & logTag) != 0; } } internal class TL { private readonly List<string> _traceElements; private LogTag _logTag; public TL(string first, LogTag logTag) { _logTag = logTag; if (PluginConfig.EnableLogTraces) { _traceElements = new List<string>(1) { first }; } } private TL(TL tl) { _traceElements = new List<string>(tl._traceElements); _logTag = tl._logTag; } public TL New(string next, LogTag? newTag = null) { if (!PluginConfig.EnableLogTraces) { return this; } TL tL = new TL(this); if (!tL._traceElements.Contains(next)) { tL._traceElements.Add(next); } if (newTag.HasValue) { tL._logTag = newTag.Value; } return tL; } public void Log(string message, LogTag? tag = null) { if (!PluginConfig.EnableLogTraces) { OtherLogger.Log(message, tag ?? _logTag); } else { OtherLogger.Log(ConstructFullMessage(message), tag ?? _logTag); } } public void LogInfo(string message) { if (!PluginConfig.EnableLogTraces) { OtherLogger.LogInfo(message); } else { OtherLogger.LogInfo(ConstructFullMessage(message)); } } public void LogError(string message) { if (!PluginConfig.EnableLogTraces) { OtherLogger.LogError(message); } else { OtherLogger.LogError(ConstructFullMessage(message)); } } public void LogWarning(string message) { if (!PluginConfig.EnableLogTraces) { OtherLogger.LogWarning(message); } else { OtherLogger.LogWarning(ConstructFullMessage(message)); } } private string ConstructFullMessage(string message) { StringBuilder stringBuilder = new StringBuilder(); foreach (string traceElement in _traceElements) { stringBuilder.Append("[" + traceElement + "]"); } stringBuilder.Append(' ').Append(message); return stringBuilder.ToString(); } } } namespace OtherLoader.ItemUnlocker { public enum ItemUnlockerMode { Disabled, UnlockOnlyFirearmsAndAmmo, UnlockOnlyModdedItems, UnlockAllItems } [HarmonyWrapSafe] internal static class RewardUnlocksPatchers { private static int _activeMethods; [HarmonyPostfix] [HarmonyPatch(typeof(RewardUnlocks), "IsRewardUnlocked", new Type[] { typeof(ItemSpawnerID) })] private static void IsRewardUnlocked_Postfix(ItemSpawnerID ID, ref bool __result) { if (_activeMethods == 0) { return; } if (!__result) { if (PluginConfig.ItemUnlocker == ItemUnlockerMode.UnlockOnlyModdedItems) { if (string.IsNullOrEmpty(ID.FromMod)) { OtherLogger.Log("Reward item '" + ID.DisplayName + "' is not modded, skipping...", LogTag.ItemUnlocker); return; } } else if (PluginConfig.ItemUnlocker == ItemUnlockerMode.UnlockOnlyFirearmsAndAmmo && !ID.IsFirearm() && !ID.IsAmmo()) { OtherLogger.Log("Reward item '" + ID.DisplayName + "' is not firearm or ammo, skipping...", LogTag.ItemUnlocker); return; } OtherLogger.Log("Game checked if '" + ID.DisplayName + "' is unlocked, returning true!", LogTag.ItemUnlocker); if (!ID.ModTags.Contains("GrantedByItemUnlocker")) { ID.ModTags = ID.ModTags.Concat(new <>z__ReadOnlySingleElementList<string>("GrantedByItemUnlocker")).ToList(); } } __result = true; } private static void AddActiveMethod() { Interlocked.Increment(ref _activeMethods); } private static void RemoveActiveMethod() { if (Interlocked.Decrement(ref _activeMethods) < 0) { Interlocked.Exchange(ref _activeMethods, 0); } } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch(typeof(ItemSpawnerIDRegisterer), "Register")] private static void Register_Prefix() { AddActiveMethod(); } [HarmonyFinalizer] [HarmonyPatch(typeof(ItemSpawnerIDRegisterer), "Register")] private static Exception Register_Finalizer(Exception __exception) { RemoveActiveMethod(); return __exception; } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch(typeof(IM), "GenerateItemDBs")] private static void GenerateItemDBs_Prefix() { AddActiveMethod(); } [HarmonyFinalizer] [HarmonyPatch(typeof(IM), "GenerateItemDBs")] private static Exception GenerateItemDBs_Finalizer(Exception __exception) { RemoveActiveMethod(); return __exception; } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch(typeof(ItemSpawnerV2), "RedrawDetailsCanvas")] private static void RedrawDetailsCanvas_Prefix() { AddActiveMethod(); } [HarmonyFinalizer] [HarmonyPatch(typeof(ItemSpawnerV2), "RedrawDetailsCanvas")] private static Exception RedrawDetailsCanvas_Finalizer(Exception __exception) { RemoveActiveMethod(); return __exception; } } } namespace OtherLoader.ItemSpawner { internal static class ItemSpawnerIDRegisterer { public static void Register(TL oldTL, ItemSpawnerID itemSpawnerID, bool registerInTilesDatabase, bool unifyItemIDs, bool blockDuplicateIDs, string pluginName) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) TL tL = oldTL.New("Register", LogTag.Assets); EnsureMainObjectIsNotNull(tL, itemSpawnerID); itemSpawnerID.FromMod = pluginName; if (unifyItemIDs && itemSpawnerID.MainObject?.ItemID != null) { itemSpawnerID.ItemID = itemSpawnerID.MainObject.ItemID; itemSpawnerID.MainObject.SpawnedFromId = itemSpawnerID.MainObject.ItemID; tL.Log("Unified spawner IDs"); } if (itemSpawnerID.ItemID == null) { tL.LogError("ItemSpawnerID.ItemID is null, can't register ItemSpawnerID!"); return; } tL.Log("Registering new ItemSpawnerID\n- DisplayName '" + itemSpawnerID.DisplayName + "'\n- ItemID '" + itemSpawnerID.ItemID + "'\n- MainObject.ItemID '" + (itemSpawnerID.MainObject?.ItemID ?? "null") + "'" + $"\n- Category '{itemSpawnerID.Category}'" + $"\n- SubCategory '{itemSpawnerID.SubCategory}'" + $"\n- IsDisplayedInMainEntry '{itemSpawnerID.IsDisplayedInMainEntry}'" + $"\n- IsReward '{itemSpawnerID.IsReward}'" + $"\n- SecondObject '{itemSpawnerID.SecondObject}'" + "\n- Secondaries: " + (itemSpawnerID.Secondaries?.JoinToString() ?? "null") + "\n- SecondariesByStringID: " + (itemSpawnerID.Secondaries_ByStringID?.JoinToString() ?? "null")); if (IM.HasSpawnedID(itemSpawnerID.ItemID)) { if (blockDuplicateIDs) { tL.LogWarning("Warning! Duplicates are specifically disabled for this mod! Skipping ItemSpawnerID with ID '" + itemSpawnerID.ItemID + "' because a spawner entry with the same ID is already registered!"); return; } tL.LogWarning("Warning! Registering ItemSpawnerID with ID '" + itemSpawnerID.ItemID + "' even though a spawner entry with the same ID is already registered!"); if (itemSpawnerID.IsDisplayedInMainEntry) { ManagerSingleton<IM>.Instance.SpawnerIDDic[itemSpawnerID.ItemID] = itemSpawnerID; } } else { ManagerSingleton<IM>.Instance.SpawnerIDDic[itemSpawnerID.ItemID] = itemSpawnerID; } if (!GM.Rewards.RewardUnlocks.IsRewardUnlocked(itemSpawnerID)) { tL.LogInfo("Item spawner entry '" + itemSpawnerID.DisplayName + "' (ID: " + itemSpawnerID.ItemID + ") is a reward unlock that's not unlocked, it will not appear in the spawner."); return; } ItemSpawnerIDTagger.RegisterSpawnerIDIntoTagSystem(itemSpawnerID); IM.CD.GetValueOrNull(itemSpawnerID.Category)?.Add(itemSpawnerID); IM.SCD.GetValueOrNull(itemSpawnerID.SubCategory)?.Add(itemSpawnerID); RegisteredItemSpawnerIDsDatabase.RegisteredModdedIDs.Add(itemSpawnerID.ItemID); if (!itemSpawnerID.IsDisplayedInMainEntry || !registerInTilesDatabase) { return; } if (Enum.IsDefined(typeof(EItemCategory), itemSpawnerID.Category)) { if (!Enum.IsDefined(typeof(ESubCategory), itemSpawnerID.SubCategory)) { ItemSpawnerEntry itemSpawnerEntry = ItemSpawnerEntry.CreateEmpty($"{itemSpawnerID.SubCategory}/" + itemSpawnerID.ItemID); itemSpawnerEntry.DisplayName = itemSpawnerID.DisplayName; itemSpawnerEntry.MainObjectID = itemSpawnerID.ItemID; itemSpawnerEntry.EntryIcon = itemSpawnerID.Sprite; SpawnerTilesDatabase.TryRegister(itemSpawnerEntry); } } else { ItemSpawnerEntry itemSpawnerEntry2 = ItemSpawnerEntry.CreateEmpty($"{itemSpawnerID.Category}/" + $"{itemSpawnerID.SubCategory}/" + itemSpawnerID.ItemID); itemSpawnerEntry2.DisplayName = itemSpawnerID.DisplayName; itemSpawnerEntry2.MainObjectID = itemSpawnerID.ItemID; itemSpawnerEntry2.EntryIcon = itemSpawnerID.Sprite; SpawnerTilesDatabase.TryRegister(itemSpawnerEntry2); } } private static void EnsureMainObjectIsNotNull(TL oldTL, ItemSpawnerID spawnerId) { TL tL = oldTL.New("EnsureMainObjectIsNotNull"); if (!((Object)(object)spawnerId.MainObject != (Object)null)) { spawnerId.MainObject = spawnerId.Secondaries.Select((ItemSpawnerID obj) => obj.MainObject).FirstOrDefault((Func<FVRObject, bool>)((FVRObject obj) => (Object)(object)obj != (Object)null)); if ((Object)(object)spawnerId.MainObject == (Object)null) { throw new NullReferenceException("ItemSpawnerID.MainObject is null, and there are no secondary objects to use as the main object"); } spawnerId.ItemID = spawnerId.MainObject.ItemID; tL.Log("Assigned ItemID '" + spawnerId.ItemID + "' from secondary object"); } } } internal static class ItemSpawnerIDTagger { public static void RegisterSpawnerIDIntoTagSystem(ItemSpawnerID spawnerID) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Invalid comparison between Unknown and I4 //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) TL tL = new TL("RegisterSpawnerIDIntoTagSystem", LogTag.ItemSpawnerTagging); tL.Log("Attempting to tag '" + spawnerID.ItemID + "'"); PageMode spawnerPageForSpawnerId = GetSpawnerPageForSpawnerId(spawnerID); tL.Log($"Assigning item to spawner page '{spawnerPageForSpawnerId}'"); RegisterModTags(spawnerID, spawnerPageForSpawnerId); RegisterCategoryTags(spawnerID, spawnerPageForSpawnerId); if ((int)spawnerPageForSpawnerId == 1) { RegisterFirearmIntoMetaTagSystem(spawnerID, spawnerPageForSpawnerId); } else if ((int)spawnerPageForSpawnerId == 3) { RegisterAttachmentIntoMetaTagSystem(spawnerID, spawnerPageForSpawnerId); } else if ((int)spawnerPageForSpawnerId == 2) { RegisterAmmoIntoMetaTagSystem(spawnerID, spawnerPageForSpawnerId); } } private static PageMode GetSpawnerPageForSpawnerId(ItemSpawnerID spawnerId) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (ShouldItemBeTaggedAsToolsToys(spawnerId)) { return (PageMode)5; } if (ShouldItemBeTaggedAsMelee(spawnerId)) { return (PageMode)4; } if (ShouldItemBeTaggedAsAmmo(spawnerId)) { return (PageMode)2; } if ((int)ItemSubCategoryMapper.InferCategoryFrom(spawnerId.SubCategory).GetValueOrDefault() == 4) { return (PageMode)3; } if (spawnerId.IsFirearm()) { return (PageMode)1; } if ((int)spawnerId.MainObject.Category == 5) { return (PageMode)3; } return (PageMode)1; } private static void RegisterCategoryTags(ItemSpawnerID spawnerId, PageMode page) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) IM.AddMetaTag(((object)(EItemCategory)(ref spawnerId.Category)).ToString(), (TagType)2, spawnerId.ItemID, page); if ((int)spawnerId.SubCategory != 0) { IM.AddMetaTag(((object)(ESubCategory)(ref spawnerId.SubCategory)).ToString(), (TagType)3, spawnerId.ItemID, page); } } private static void RegisterModTags(ItemSpawnerID spawnerId, PageMode page) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) foreach (string modTag in spawnerId.ModTags) { IM.AddMetaTag(modTag, (TagType)50, spawnerId.ItemID, page); } IM.AddMetaTag("Ot