using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using AIGraph; using AssetShards; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Hook; using GTFO.API; using GTFO.API.Utilities; using GameData; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using ItemSpawnFix.CustomSettings; using ItemSpawnFix.Dependencies; using ItemSpawnFix.JSON; using ItemSpawnFix.JSON.Converters; using ItemSpawnFix.Patches; using ItemSpawnFix.Redistribute; using ItemSpawnFix.Utils; using LevelGeneration; using MTFO.API; using Microsoft.CodeAnalysis; using SNetwork; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ItemSpawnFix")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+019529ce85147220aa027e7ab7f3cd1f21f2e7ee")] [assembly: AssemblyProduct("ItemSpawnFix")] [assembly: AssemblyTitle("ItemSpawnFix")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace ItemSpawnFix { internal static class Configuration { private static readonly ConfigEntry _showDebugMessages; private static readonly ConfigFile configFile; public static bool ShowDebugMessages => _showDebugMessages.Value; static Configuration() { //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, "ItemSpawnFix.cfg"), true); string text = "Debug Settings"; _showDebugMessages = configFile.Bind(text, "Enable Logs", false, "Prints information to the logs when redistributing resources."); } public static void Init() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown LiveEdit.CreateListener(Paths.ConfigPath, "ItemSpawnFix.cfg", false).FileChanged += new LiveEditEventHandler(OnFileChanged); } private static void OnFileChanged(LiveEditEventArgs _) { configFile.Reload(); } } internal static class DinoLogger { private static ManualLogSource logger = Logger.CreateLogSource("ItemSpawnFix"); public static void Log(string format, params object[] args) { Log(string.Format(format, args)); } public static void Log(string str) { if (logger != null) { logger.Log((LogLevel)8, (object)str); } } public static void Warning(string format, params object[] args) { Warning(string.Format(format, args)); } public static void Warning(string str) { if (logger != null) { logger.Log((LogLevel)4, (object)str); } } public static void Error(string format, params object[] args) { Error(string.Format(format, args)); } public static void Error(string str) { if (logger != null) { logger.Log((LogLevel)2, (object)str); } } public static void Debug(string format, params object[] args) { Debug(string.Format(format, args)); } public static void Debug(string str) { if (logger != null) { logger.Log((LogLevel)32, (object)str); } } } [BepInPlugin("Dinorush.ItemSpawnFix", "ItemSpawnFix", "1.3.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] internal sealed class EntryPoint : BasePlugin { public const string MODNAME = "ItemSpawnFix"; public override void Load() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) SettingsManager.Init(); Configuration.Init(); RedistributeUtils.Init(); LG_DistributionPatches.Init(); LG_PopulatePatches.Init(); new Harmony("ItemSpawnFix").PatchAll(); ((BasePlugin)this).Log.LogMessage((object)"Loaded ItemSpawnFix"); } } } namespace ItemSpawnFix.Utils { internal static class DictExtensions { public static TValue GetOrAdd(this IDictionary dict, TKey key) where TValue : new() { if (!dict.TryGetValue(key, out TValue value)) { dict.Add(key, value = new TValue()); } return value; } } internal class DictListEnumerator : IEnumerator, IEnumerator, IDisposable { private readonly IEnumerator> _valueEnumerator; private List? _curList; private int _index; private T? _current; public T Current => _current; object IEnumerator.Current => Current; internal DictListEnumerator(Dictionary> data) { _valueEnumerator = data.Values.GetEnumerator(); if (_valueEnumerator.MoveNext()) { _curList = _valueEnumerator.Current; } _index = -1; } public void Dispose() { } public bool MoveNext() { if (_curList == null || ++_index >= _curList.Count) { if (!_valueEnumerator.MoveNext()) { return false; } _curList = _valueEnumerator.Current; _index = 0; } _current = _curList[_index]; return true; } public void Reset() { _valueEnumerator.Reset(); if (_valueEnumerator.MoveNext()) { _curList = _valueEnumerator.Current; } _index = -1; } } internal static class StringExtensions { public static T ToEnum(this string? value, T defaultValue) where T : struct { if (string.IsNullOrEmpty(value)) { return defaultValue; } if (!Enum.TryParse(value.Replace(" ", null), ignoreCase: true, out var result)) { return defaultValue; } return result; } } } namespace ItemSpawnFix.Redistribute { [HarmonyPatch(typeof(LG_ResourceContainer_Storage))] public static class RedistributeUtils { public const eCommodityType CustomCommodityType = (eCommodityType)5114; private static readonly Dictionary> _nodeTrackers = new Dictionary>(); private static readonly Dictionary> _nodeTrackersEmpty = new Dictionary>(); private static StorageTracker? _currentTracker; public static Random Random { get; private set; } = new Random(); public static ExpeditionFunction DistributeFunction { get; set; } = (ExpeditionFunction)0; public static void Init() { LevelAPI.OnBuildStart += OnBuildStart; } private static void OnBuildStart() { SetSeed(Builder.BuildSeedRandom.Seed); _nodeTrackers.Clear(); _nodeTrackersEmpty.Clear(); } internal static void OnZoneFinished(LG_Zone zone) { DoSetSpawns(zone); foreach (List value in _nodeTrackersEmpty.Values) { foreach (StorageTracker item in value) { SNet.DestroySelfManagedReplicatedObject(((Component)((Component)item.Storage).transform.parent).gameObject); } } _nodeTrackers.Clear(); _nodeTrackersEmpty.Clear(); } internal static void OnContainerStorageSpawned(LG_ResourceContainer_Storage storage) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)DistributeFunction != 10) { return; } AIG_CourseNode spawnNode = storage.m_core.SpawnNode; if (spawnNode != null) { int nodeID = spawnNode.NodeID; _currentTracker = new StorageTracker(storage, nodeID); List list; if (!_nodeTrackers.TryGetValue(nodeID, out List value)) { _nodeTrackers.Add(nodeID, value = new List()); _nodeTrackersEmpty.Add(nodeID, list = new List()); } else { list = _nodeTrackersEmpty[nodeID]; } value.Add(_currentTracker); list.Add(_currentTracker); } } internal static void OnContainerItemSpawned(LG_ResourceContainer_Storage storage, Transform align) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)DistributeFunction != 10 || _currentTracker == null) { return; } if (_currentTracker.Unused) { AIG_CourseNode spawnNode = storage.m_core.SpawnNode; if (spawnNode != null && _nodeTrackersEmpty.TryGetValue(spawnNode.NodeID, out List value)) { value.RemoveAt(value.Count - 1); } } if (!_currentTracker.RemoveAndCheckSpace(align)) { AIG_CourseNode spawnNode2 = storage.m_core.SpawnNode; if (spawnNode2 != null && _nodeTrackers.TryGetValue(spawnNode2.NodeID, out List value2)) { value2.RemoveAt(value2.Count - 1); } } } public static void SetSeed(int seed) { Random = new Random(seed); } public static bool TryRedistributeItems(AIG_CourseNode node, List items, out List remainingItems, bool empty) { remainingItems = new List(); if (items.Count == 0) { return true; } remainingItems.EnsureCapacity(items.Count); Enumerator enumerator = items.GetEnumerator(); while (enumerator.MoveNext()) { ResourceContainerSpawnData current = enumerator.Current; remainingItems.Add(current); } Dictionary> dictionary = (empty ? _nodeTrackersEmpty : _nodeTrackers); if (dictionary.TryGetValue(node.NodeID, out var value) && TryRedistributeToList(remainingItems, value, empty)) { return true; } List list = new List(dictionary.Values.Sum((List list2) => list2.Count)); foreach (List value2 in dictionary.Values) { list.AddRange(value2); } return TryRedistributeToList(remainingItems, list, empty); } private static bool TryRedistributeToList(List items, List trackers, bool empty = false) { if (trackers.Count == 0) { return false; } StorageTracker currentTracker = _currentTracker; _currentTracker = null; Dictionary> dictionary = new Dictionary>(); int[] array = Enumerable.Range(0, trackers.Count).ToArray(); Shuffle(array); for (int i = 0; i < array.Length; i++) { if (items.Count <= 0) { break; } int index = array[i]; StorageTracker storageTracker = trackers[index]; while (storageTracker.Count > 0 && items.Count > 0) { if (!storageTracker.RemoveRandomAndCheckSpace(out StorageSlot slot) && !empty) { dictionary.GetOrAdd(storageTracker.NodeID).Add(storageTracker.ID); } SpawnItem(storageTracker.Storage, slot, items[items.Count - 1]); items.RemoveAt(items.Count - 1); } if (empty) { dictionary.GetOrAdd(storageTracker.NodeID).Add(storageTracker.ID); } } foreach (var (key, idsToRemove) in dictionary) { if (empty) { RemoveFromList(_nodeTrackersEmpty[key], idsToRemove); RemoveFromList(_nodeTrackers[key], idsToRemove, (StorageTracker tracker) => tracker.Count == 0); } else { RemoveFromList(_nodeTrackers[key], idsToRemove); } } _currentTracker = currentTracker; return items.Count == 0; } private static void RemoveFromList(List list, HashSet idsToRemove, Predicate? cond = null) { int num = 0; for (int i = 0; i < list.Count; i++) { StorageTracker storageTracker = list[i]; if (!idsToRemove.Contains(storageTracker.ID) || (cond != null && cond(storageTracker))) { list[num++] = storageTracker; } } list.RemoveRange(num, list.Count - num); } private static void Shuffle(T[] array) { int num = array.Length; while (num > 1) { int num2 = Random.Next(0, num--); int num3 = num2; int num4 = num; T val = array[num]; T val2 = array[num2]; array[num3] = val; array[num4] = val2; } } private static void DoSetSpawns(LG_Zone zone) { //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (!SettingsManager.TryGetSetSpawns(zone, out List list)) { return; } List list2 = null; List list3 = null; List list4 = new List(1); foreach (BaseSpawnData item in list) { if (!item.TryGetContainerData(out ResourceContainerSpawnData data)) { continue; } list4.Add(data); int[] array = item.AreaIndex; if (item.AreaIndex.Length > 1) { array = (int[])array.Clone(); Shuffle(array); } int[] array2 = array; foreach (int num in array2) { if (num >= 0 && num < zone.m_areas.Count) { AIG_CourseNode courseNode = zone.m_areas[num].m_courseNode; if ((!item.PreferEmpty || !_nodeTrackersEmpty.TryGetValue(courseNode.NodeID, out List value) || !TryRedistributeToList(list4, value, empty: true)) && (!_nodeTrackers.TryGetValue(courseNode.NodeID, out value) || !TryRedistributeToList(list4, value)) && Configuration.ShowDebugMessages) { DinoLogger.Log($"No room for set spawn [{data.m_type}, {data.m_ammo}] in {((Object)zone.NavInfo).ToString()}{((Object)courseNode.m_area.m_navInfo).ToString()}, trying next area..."); } } else { DinoLogger.Error($"Set spawn has area index {num}, but zone max index is {zone.m_areas.Count - 1}! Trying next area..."); } } if (item.PreferEmpty && list2 == null) { list2 = new List(_nodeTrackersEmpty.Values.Sum((List list5) => list5.Count)); foreach (List value2 in _nodeTrackersEmpty.Values) { list2.AddRange(value2); } } if (item.PreferEmpty && TryRedistributeToList(list4, list2, empty: true)) { continue; } if (list3 == null) { list3 = new List(_nodeTrackers.Values.Sum((List list5) => list5.Count)); foreach (List value3 in _nodeTrackers.Values) { list3.AddRange(value3); } } if (!TryRedistributeToList(list4, list3)) { list4.Clear(); } } SettingsManager.ClearSetSpawns(zone); } private static void SpawnItem(LG_ResourceContainer_Storage storage, StorageSlot storageSlot, ResourceContainerSpawnData data) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected I4, but got Unknown int num = Random.Next(); eResourceContainerSpawnType type = data.m_type; switch ((int)type) { case 0: case 1: case 2: case 9: if ((Object)(object)storageSlot.ResourcePack != (Object)null) { storage.SpawnResourcePack(data, storageSlot.ResourcePack, num); } break; case 3: if ((Object)(object)storageSlot.CommoditySmall != (Object)null) { storage.SpawnCommodity(data, storageSlot.CommoditySmall, num); } break; case 4: if ((Object)(object)storageSlot.CommodityMedium != (Object)null) { storage.SpawnCommodity(data, storageSlot.CommodityMedium, num); } break; case 5: if ((Object)(object)storageSlot.CommodityLarge != (Object)null) { storage.SpawnCommodity(data, storageSlot.CommodityLarge, num); } break; case 6: if ((Object)(object)storageSlot.Consumable != (Object)null) { storage.SpawnConsumable(data, storageSlot.Consumable, num); } break; case 7: if ((Object)(object)storageSlot.Keycard != (Object)null) { storage.PlaceKeyCard(data, storageSlot.Keycard, num); } break; case 8: if ((Object)(object)storageSlot.Keycard != (Object)null) { storage.PlaceSmallGenericPickup(data, storageSlot.Keycard, num); } break; case 10: if ((Object)(object)storageSlot.Keycard != (Object)null) { storage.SpawnArtifact(data, storageSlot.Keycard, num); } break; } } public static string GetPackListString(List packs) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder("["); for (int i = 0; i < packs.Count - 1; i++) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(4, 2, stringBuilder2); handler.AppendLiteral("("); handler.AppendFormatted(packs[i].m_type); handler.AppendLiteral(":"); handler.AppendFormatted(packs[i].m_ammo); handler.AppendLiteral("),"); stringBuilder3.Append(ref handler); } if (packs.Count > 0) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(3, 2, stringBuilder2); handler.AppendLiteral("("); handler.AppendFormatted(packs[packs.Count - 1].m_type); handler.AppendLiteral(":"); handler.AppendFormatted(packs[packs.Count - 1].m_ammo); handler.AppendLiteral(")"); stringBuilder4.Append(ref handler); } stringBuilder.Append(']'); return stringBuilder.ToString(); } public static string GetPackListString(List packs) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder("["); for (int i = 0; i < packs.Count - 1; i++) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(4, 2, stringBuilder2); handler.AppendLiteral("("); handler.AppendFormatted(packs[i].m_type); handler.AppendLiteral(":"); handler.AppendFormatted(packs[i].m_ammo); handler.AppendLiteral("),"); stringBuilder3.Append(ref handler); } if (packs.Count > 0) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(3, 2, stringBuilder2); handler.AppendLiteral("("); handler.AppendFormatted(packs[packs.Count - 1].m_type); handler.AppendLiteral(":"); handler.AppendFormatted(packs[packs.Count - 1].m_ammo); handler.AppendLiteral(")"); stringBuilder4.Append(ref handler); } stringBuilder.Append(']'); return stringBuilder.ToString(); } } public class StorageTracker { private static uint s_nextID; public readonly int NodeID; public readonly uint ID; public readonly LG_ResourceContainer_Storage Storage; private readonly List<(StorageSlot slot, HashSet alignSet)> _slotAligns; private readonly int _maxCount; public bool Unused => _slotAligns.Count == _maxCount; public int Count => _slotAligns.Count; public StorageTracker(LG_ResourceContainer_Storage storage, int nodeID) { NodeID = nodeID; ID = s_nextID++; Storage = storage; _slotAligns = new List<(StorageSlot, HashSet)>(); foreach (StorageSlot item in (Il2CppArrayBase)(object)storage.m_storageSlots) { HashSet hashSet = new HashSet(); _slotAligns.Add((item, hashSet)); TryAddAlign(item.ResourcePack, hashSet); TryAddAlign(item.CommoditySmall, hashSet); TryAddAlign(item.CommodityMedium, hashSet); TryAddAlign(item.CommodityLarge, hashSet); TryAddAlign(item.Consumable, hashSet); TryAddAlign(item.Keycard, hashSet); } _maxCount = _slotAligns.Count; } private void TryAddAlign(Transform align, HashSet alignSet) { if ((Object)(object)align != (Object)null) { alignSet.Add(((Il2CppObjectBase)align).Pointer); } } public bool RemoveRandomAndCheckSpace(out StorageSlot slot) { int index = RedistributeUtils.Random.Next(0, Count); slot = _slotAligns[index].slot; _slotAligns.RemoveAt(index); return Count > 0; } public bool RemoveAndCheckSpace(Transform align) { (StorageSlot, HashSet) item = _slotAligns.First<(StorageSlot, HashSet)>(((StorageSlot slot, HashSet alignSet) pair) => pair.alignSet.Contains(((Il2CppObjectBase)align).Pointer)); _slotAligns.Remove(item); return Count > 0; } } } namespace ItemSpawnFix.Patches { [HarmonyPatch] internal static class LG_DistributionPatches { private unsafe delegate void d_Enqueue(IntPtr _this, IntPtr item, Il2CppMethodInfo* methodInfo); private static bool _inDistribute; private static INativeDetour? GetDistributionDetour; private static d_Enqueue? orig_Enqueue; [HarmonyPatch(typeof(LG_DistributeResourceContainer), "ShouldBeRemoved")] [HarmonyPostfix] private static void Post_ShouldBeRemoved(ref bool __result) { __result = false; } [HarmonyPatch(typeof(LG_Distribute_ResourcePacksPerZone), "Build")] [HarmonyPatch(typeof(LG_Distribute_PickupItemsPerZone), "Build")] [HarmonyPatch(typeof(LG_Distribute_ProgressionPuzzles), "CreateKeyItemDistribution")] [HarmonyPrefix] private static void Pre_Build() { _inDistribute = true; } [HarmonyPatch(typeof(LG_Distribute_ResourcePacksPerZone), "Build")] [HarmonyPatch(typeof(LG_Distribute_PickupItemsPerZone), "Build")] [HarmonyPatch(typeof(LG_Distribute_ProgressionPuzzles), "CreateKeyItemDistribution")] [HarmonyPostfix] private static void Post_Build() { _inDistribute = false; } public unsafe static void Init() { INativeClassStruct val = UnityVersionHandler.Wrap((Il2CppClass*)(void*)Il2CppClassPointerStore>.NativeClassPtr); for (int i = 0; i < val.MethodCount; i++) { INativeMethodInfoStruct val2 = UnityVersionHandler.Wrap(val.Methods[i]); if (Marshal.PtrToStringAnsi(val2.Name) == "Enqueue") { GetDistributionDetour = INativeDetour.CreateAndApply(val2.MethodPointer, (d_Enqueue)EnqueuePatch, ref orig_Enqueue); break; } } } private unsafe static void EnqueuePatch(IntPtr _this, IntPtr item, Il2CppMethodInfo* methodInfo) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) orig_Enqueue(_this, item, methodInfo); if (_inDistribute) { LG_DistributeItem val = new LG_DistributeItem(item); val.m_assignedNode.FunctionDistributionPerAreaLookup[val.m_function].Add(val); if (Configuration.ShowDebugMessages) { DinoLogger.Log($"New distribution created for {((Object)val.m_assignedNode.m_zone.NavInfo).ToString()} {((Object)val.m_assignedNode.m_area.m_navInfo).ToString()} of type {val.m_function} added to lookup"); } } } } [HarmonyPatch(typeof(LG_PopulateFunctionMarkersInZoneJob))] internal static class LG_PopulatePatches { private unsafe delegate void d_BuildFunc(IntPtr _this, IntPtr builder, IntPtr distItem, out IntPtr deepestSpawner, bool debug, Il2CppMethodInfo* methodInfo); private static readonly List _validNodes = new List(); private static int _currZoneID = -1; private static INativeDetour? TriggerFunctionBuilderDetour; private static d_BuildFunc? orig_TriggerFunctionBuilder; [HarmonyPatch("Build")] [HarmonyPostfix] private static void PostBuild(LG_PopulateFunctionMarkersInZoneJob __instance, bool __result) { if (__result) { RedistributeUtils.OnZoneFinished(__instance.m_zone); } } [HarmonyPatch("BuildBothFunctionAndPropMarkerAndRemoveSurplus")] [HarmonyWrapSafe] [HarmonyPriority(200)] [HarmonyPrefix] private static bool OverrideFallbackLogic(LG_PopulateFunctionMarkersInZoneJob __instance, LG_FunctionMarkerBuilder builder, LG_DistributeItem distItem) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected I4, but got Unknown if (!__instance.m_fallbackMode) { return true; } ExpeditionFunction function = distItem.m_function; switch (function - 8) { default: return true; case 0: case 2: case 4: { LG_MarkerSpawner val = default(LG_MarkerSpawner); __instance.TriggerFunctionBuilder(builder, distItem, ref val, false); return false; } } } [HarmonyPatch("Build")] [HarmonyWrapSafe] [HarmonyPriority(200)] [HarmonyPrefix] private static bool OverrideBuildOrder(LG_PopulateFunctionMarkersInZoneJob __instance, ref bool __result) { if (!SettingsManager.ActiveSettings.RaiseObjectSpawnPriority) { return true; } __result = false; if (!__instance.BuildGenericFunctionItems((eDistributionQueue)1)) { return false; } if (!__instance.BuildGateKeyItems()) { return false; } if (!__instance.BuildGenericFunctionItems((eDistributionQueue)0)) { return false; } if (!__instance.BuildPickupItems()) { return false; } if (!__instance.BuildResourceContainers()) { return false; } __result = true; return false; } [HarmonyPatch("BuildGenericFunctionItems")] [HarmonyWrapSafe] [HarmonyPriority(200)] [HarmonyPrefix] private static bool AllowRedistributeGeneric(LG_PopulateFunctionMarkersInZoneJob __instance, eDistributionQueue queue, ref bool __result) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown if (!SettingsManager.ActiveSettings.AllowRedistributeObjects) { return true; } DistributeItemQueueWrapper val = (((int)queue != 0) ? __instance.m_distributionData.PrioritizedClusterItems : __instance.m_distributionData.GenericFunctionItems); if (val.Count != 0) { LG_DistributeItem val2 = val.m_itemQueue.Dequeue(); __result = false; LG_FunctionMarkerBuilder val3 = new LG_FunctionMarkerBuilder(val2.m_assignedNode, val2.m_function, val2.m_isWardenObjective, val2.m_wardenObjectiveChainIndex, val2.m_assignedGate, val2.m_localTerminalLogFiles, val2.m_terminalStartStateData, val2.m_terminalPlacementData); LG_MarkerSpawner val4 = default(LG_MarkerSpawner); __instance.TriggerFunctionBuilder(val3, val2, ref val4, false); return false; } __result = true; return false; } public unsafe static void Init() { TriggerFunctionBuilderDetour = INativeDetour.CreateAndApply((IntPtr)(nint)Il2CppAPI.GetIl2CppMethod("TriggerFunctionBuilder", typeof(void).Name, false, new string[4] { "LG_FunctionMarkerBuilder", "LG_DistributeItem", typeof(LG_MarkerSpawner).MakeByRefType().FullName, typeof(bool).Name }), (d_BuildFunc)TriggerFunctionBuilderPatch, ref orig_TriggerFunctionBuilder); LevelAPI.OnBuildStart += delegate { _currZoneID = -1; }; } private unsafe static void TriggerFunctionBuilderPatch(IntPtr _this, IntPtr builder, IntPtr distItem, out IntPtr deepestSpawner, bool debug, Il2CppMethodInfo* methodInfo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Invalid comparison between Unknown and I4 //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Invalid comparison between Unknown and I4 //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Invalid comparison between Unknown and I4 //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_04b3: Unknown result type (might be due to invalid IL or missing references) LG_PopulateFunctionMarkersInZoneJob val = new LG_PopulateFunctionMarkersInZoneJob(_this); LG_FunctionMarkerBuilder val2 = new LG_FunctionMarkerBuilder(builder); LG_DistributeItem val3 = new LG_DistributeItem(distItem); ExpeditionFunction val4 = (RedistributeUtils.DistributeFunction = val3.m_function); bool flag = (int)val4 == 10; LG_DistributeResourceContainer val5 = ((!flag) ? ((LG_DistributeResourceContainer)null) : new LG_DistributeResourceContainer(distItem)); bool flag2 = (flag ? (val5.m_packs.Count == 0) : val3.ShouldBeRemoved()); if (val.m_fallbackMode) { if ((int)val4 == 20) { if (!flag2) { orig_TriggerFunctionBuilder(_this, builder, distItem, out deepestSpawner, debug, methodInfo); } else { deepestSpawner = IntPtr.Zero; } RedistributeUtils.DistributeFunction = (ExpeditionFunction)0; return; } RedistributeUtils.SetSeed(Builder.BuildSeedRandom.Range(0, int.MaxValue, "NO_TAG")); AIG_CourseNode assignedNode = val3.m_assignedNode; string value = ((Object)assignedNode.m_area.m_navInfo).ToString(); string value2 = ((Object)assignedNode.m_zone.NavInfo).ToString(); if (!flag2 && Configuration.ShowDebugMessages) { DinoLogger.Log($"Creating {val4} with floor fallback in {value2} {value}"); } if (flag2) { deepestSpawner = IntPtr.Zero; } else if (flag && TryRedistributeItems(val3.m_assignedNode, val5, empty: false)) { deepestSpawner = IntPtr.Zero; } else { orig_TriggerFunctionBuilder(_this, builder, distItem, out deepestSpawner, debug, methodInfo); } RedistributeUtils.DistributeFunction = (ExpeditionFunction)0; return; } if ((int)val4 != 20) { val3.m_allowFunctionFallback = false; } orig_TriggerFunctionBuilder(_this, builder, distItem, out deepestSpawner, debug, methodInfo); if (flag2 || (int)val4 == 20) { RedistributeUtils.DistributeFunction = (ExpeditionFunction)0; return; } if (deepestSpawner == IntPtr.Zero) { RedistributeUtils.SetSeed(Builder.BuildSeedRandom.Range(0, int.MaxValue, "NO_TAG")); AIG_CourseNode node = val3.m_assignedNode; string value3 = ((Object)node.m_area.m_navInfo).ToString(); LG_Zone zone = node.m_zone; string text = ((Object)zone.NavInfo).ToString(); if (Configuration.ShowDebugMessages) { DinoLogger.Log($"No markers remaining for distribute {val4} in {text} {value3} trying to redistribute"); } if (flag && TryRedistributeItems(node, val5, empty: true)) { deepestSpawner = IntPtr.Zero; RedistributeUtils.DistributeFunction = (ExpeditionFunction)0; return; } if (_currZoneID != zone.ID) { _currZoneID = zone.ID; _validNodes.Clear(); Enumerator enumerator = zone.m_areas.GetEnumerator(); while (enumerator.MoveNext()) { LG_Area current = enumerator.Current; _validNodes.Add(current.m_courseNode); } } int num = _validNodes.FindLastIndex((AIG_CourseNode n) => n.NodeID == node.NodeID); if (num >= 0) { _validNodes.RemoveAt(num); } while (_validNodes.Count > 0) { int index = RedistributeUtils.Random.Next(0, _validNodes.Count); val2.m_node = _validNodes[index]; val3.m_assignedNode = _validNodes[index]; orig_TriggerFunctionBuilder(_this, builder, distItem, out deepestSpawner, debug, methodInfo); if (deepestSpawner == IntPtr.Zero) { _validNodes.RemoveAt(index); continue; } if (Configuration.ShowDebugMessages) { DinoLogger.Log($"Redistributed {val4} to {text} {((Object)_validNodes[index].m_area.m_navInfo).ToString()} (orig: {value3})"); } RedistributeUtils.DistributeFunction = (ExpeditionFunction)0; return; } if (Configuration.ShowDebugMessages) { DinoLogger.Log("No markers available in " + text); } if (flag && TryRedistributeItems(node, val5, empty: false)) { deepestSpawner = IntPtr.Zero; RedistributeUtils.DistributeFunction = (ExpeditionFunction)0; return; } val2.m_node = node; val3.m_assignedNode = node; if (Configuration.ShowDebugMessages) { DinoLogger.Log($"Unable to redistribute {val4} in {text}, moving to floor fallback in {value3}"); } AddToFallbackQueue(val, val3); } RedistributeUtils.DistributeFunction = (ExpeditionFunction)0; } private static bool TryRedistributeItems(AIG_CourseNode node, LG_DistributeResourceContainer distRes, bool empty) { string text = ((Object)node.m_zone.NavInfo).ToString(); if (RedistributeUtils.TryRedistributeItems(node, distRes.m_packs, out List remainingItems, empty)) { if (Configuration.ShowDebugMessages) { if (empty) { DinoLogger.Log("Redistributed " + RedistributeUtils.GetPackListString(distRes.m_packs) + " to empty containers in " + text); } else { DinoLogger.Log("Redistributed " + RedistributeUtils.GetPackListString(distRes.m_packs) + " to containers in " + text); } } return true; } if (Configuration.ShowDebugMessages) { if (empty) { DinoLogger.Log("Not enough empty containers in " + text + ", sending remaining items to new container spawns: " + RedistributeUtils.GetPackListString(remainingItems)); } else { DinoLogger.Log("Not enough containers in " + text + ", sending remaining items to floor container spawns: " + RedistributeUtils.GetPackListString(remainingItems)); } } distRes.m_packs.Clear(); foreach (ResourceContainerSpawnData item in remainingItems) { distRes.m_packs.Add(item); } return false; } private static void AddToFallbackQueue(LG_PopulateFunctionMarkersInZoneJob job, LG_DistributeItem distItem) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 LG_ZoneDistribution distributionDataFallback = job.m_zone.DistributionDataFallback; if ((int)distItem.m_function == 10) { distributionDataFallback.ResourceContainerItems.Enqueue(((Il2CppObjectBase)distItem).Cast()); } else if ((int)distItem.m_function == 8 || (int)distItem.m_function == 12) { distributionDataFallback.PickupItems.Enqueue(((Il2CppObjectBase)distItem).Cast()); } else { distributionDataFallback.GenericFunctionItems.Enqueue(distItem); } } } [HarmonyPatch(typeof(LG_ResourceContainer_Storage))] internal static class LG_ResourceContainerPatches { [HarmonyPatch("Setup")] [HarmonyPostfix] private static void Post_Setup(LG_ResourceContainer_Storage __instance) { RedistributeUtils.OnContainerStorageSpawned(__instance); } [HarmonyPatch("SpawnResourcePack")] [HarmonyPatch("SpawnCommodity")] [HarmonyPatch("SpawnConsumable")] [HarmonyPatch("PlaceKeyCard")] [HarmonyPatch("PlaceSmallGenericPickup")] [HarmonyPatch("SpawnArtifact")] [HarmonyPostfix] private static void Post_SpawnItem(LG_ResourceContainer_Storage __instance, Transform align) { RedistributeUtils.OnContainerItemSpawned(__instance, align); } [HarmonyPatch(typeof(LG_ResourceContainer_Storage), "SpawnConsumable")] [HarmonyWrapSafe] [HarmonyPrefix] private static bool Pre_SpawnConsumable(LG_ResourceContainer_Storage __instance, ResourceContainerSpawnData pack, Transform align, int randomSeed) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if ((int)pack.m_comType != 5114) { return true; } LG_PickupItem val = GOUtil.SpawnChildAndGetComp(AssetShardManager.GetLoadedAsset(__instance.m_pickupAssetPath, false), align); val.SetupAsConsumable(randomSeed, pack.m_genericItemId); __instance.SetSpawnNode(((Component)val).gameObject, __instance.m_core.SpawnNode); __instance.DisableInteraction(((Component)val).gameObject); return false; } } } namespace ItemSpawnFix.JSON { public static class ISFJson { private static readonly JsonSerializerOptions _setting; static ISFJson() { _setting = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip, IncludeFields = true, PropertyNameCaseInsensitive = true, WriteIndented = true, IgnoreReadOnlyProperties = true }; _setting.Converters.Add(new JsonStringEnumConverter()); } public static T? Deserialize(string json) { return JsonSerializer.Deserialize(json, _setting); } public static T? Deserialize(ref Utf8JsonReader reader) { return JsonSerializer.Deserialize(ref reader, _setting); } public static object? Deserialize(Type type, string json) { return JsonSerializer.Deserialize(json, type, _setting); } public static string Serialize(T value) { return JsonSerializer.Serialize(value, _setting); } public static void Serialize(Utf8JsonWriter writer, T value) { JsonSerializer.Serialize(writer, value, _setting); } public static void Serialize(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, options); } public static void Serialize(Utf8JsonWriter writer, string name, T value) { writer.WritePropertyName(name); JsonSerializer.Serialize(writer, value, _setting); } public static void Serialize(Utf8JsonWriter writer, string name, T value, JsonSerializerOptions options) { writer.WritePropertyName(name); JsonSerializer.Serialize(writer, value, options); } } } namespace ItemSpawnFix.JSON.Converters { public sealed class LevelTargetConverter : JsonConverter { public override LevelTarget? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { LevelTarget levelTarget = new LevelTarget(); if (ParseTarget(ref reader, levelTarget, options)) { return levelTarget; } throw new JsonException("Expected level target to be a tier, tierindex, or level layout ID"); } private static bool ParseTarget(ref Utf8JsonReader reader, LevelTarget target, JsonSerializerOptions options) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_007b: Invalid comparison between Unknown and I4 //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Invalid comparison between Unknown and I4 if (reader.TokenType == JsonTokenType.Null) { return true; } if (reader.TokenType == JsonTokenType.String) { string text = reader.GetString(); if (text.Length >= 5) { target.Tier = text.Substring(0, 5).ToEnum((eRundownTier)99); } else { target.Tier = ("Tier" + text[0]).ToEnum((eRundownTier)99); } int num = ((text.Length < 5) ? 1 : 5); if ((int)target.Tier != 99 && text.Length > num) { if (int.TryParse(text.Substring(num), out var result)) { target.TierIndex = result - 1; } else { target.Tier = (eRundownTier)99; } } if ((int)target.Tier == 99) { target.LevelLayoutID = JsonSerializer.Deserialize(ref reader, options); } return true; } if (reader.TokenType == JsonTokenType.Number) { target.LevelLayoutID = reader.GetUInt32(); return true; } return false; } public override void Write(Utf8JsonWriter writer, LevelTarget? value, JsonSerializerOptions options) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (value != null) { if (value.LevelLayoutID == 0 && (int)value.Tier == 99) { writer.WriteNullValue(); return; } if (value.LevelLayoutID != 0) { writer.WriteNumberValue(value.LevelLayoutID); return; } string? text = ((object)value.Tier/*cast due to .constrained prefix*/).ToString(); writer.WriteStringValue(text[text.Length - 1] + ((value.TierIndex >= 0) ? (value.TierIndex + 1).ToString() : "")); } } } public sealed class OptionalArrayConverter : JsonConverter { public override bool HandleNull => true; public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) { return Array.Empty(); } if (reader.TokenType != JsonTokenType.StartArray) { return new T[1] { JsonSerializer.Deserialize(ref reader, options) }; } List list = new List(); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndArray) { return list.ToArray(); } list.Add(JsonSerializer.Deserialize(ref reader, options)); } throw new JsonException("Expected EndArray token, ran out of tokens"); } public override void Write(Utf8JsonWriter writer, T[]? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } if (value.Length == 0) { ISFJson.Serialize(writer, default(T), options); return; } if (value.Length == 1) { ISFJson.Serialize(writer, value[0], options); return; } writer.WriteStartArray(); foreach (T value2 in value) { ISFJson.Serialize(writer, value2, options); } writer.WriteEndArray(); } } public sealed class SettingsDataConverter : JsonConverter { public override SettingsData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) { return null; } SettingsData settingsData = new SettingsData(); if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("Expected SettingsData to be either a string or object"); } while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return settingsData; } if (reader.TokenType != JsonTokenType.PropertyName) { throw new JsonException($"Expected PropertyName token (found {reader.TokenType})"); } string text = reader.GetString(); reader.Read(); switch (text.ToLowerInvariant()) { case "levels": case "level": if (reader.TokenType == JsonTokenType.StartArray) { settingsData.Levels = JsonSerializer.Deserialize(ref reader, options); } else if (reader.TokenType != JsonTokenType.Null) { settingsData.Levels = new LevelTarget[1] { JsonSerializer.Deserialize(ref reader, options) }; } break; case "rundownid": case "rundown": settingsData.RundownID = reader.GetUInt32(); break; case "raiseobjectspawnpriority": settingsData.RaiseObjectSpawnPriority = reader.GetBoolean(); break; case "allowredistributeobjects": settingsData.AllowRedistributeObjects = reader.GetBoolean(); break; case "setconsumablespawns": settingsData.SetConsumableSpawns = JsonSerializer.Deserialize(ref reader, options); break; case "setresourcespawns": settingsData.SetResourceSpawns = JsonSerializer.Deserialize(ref reader, options); break; default: throw new JsonException("Unrecognized property name found: " + text); } } throw new JsonException("Expected EndObject token"); } public override void Write(Utf8JsonWriter writer, SettingsData? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } writer.WriteStartObject(); writer.WritePropertyName("Levels"); if (value.Levels.Length == 0) { writer.WriteNullValue(); } else if (value.Levels.Length == 1) { JsonSerializer.Serialize(writer, value.Levels[0], options); } else { JsonSerializer.Serialize(writer, value.Levels, options); } writer.WriteNumber("RundownID", value.RundownID); writer.WriteBoolean("RaiseObjectSpawnPriority", value.RaiseObjectSpawnPriority); writer.WriteBoolean("AllowRedistributeObjects", value.AllowRedistributeObjects); ISFJson.Serialize(writer, "SetConsumableSpawns", value.SetConsumableSpawns, options); ISFJson.Serialize(writer, "SetResourceSpawns", value.SetResourceSpawns, options); writer.WriteEndObject(); } } } namespace ItemSpawnFix.Dependencies { internal static class MTFOWrapper { public const string PLUGIN_GUID = "com.dak.MTFO"; public static readonly bool HasMTFO; public static string GameDataPath { get { if (!HasMTFO) { return ""; } return GameDataPath_Unsafe(); } } public static string CustomPath { get { if (!HasMTFO) { return ""; } return CustomPath_Unsafe(); } } public static bool HasCustomContent { get { if (!HasMTFO) { return false; } return HasCustomContent_Unsafe(); } } static MTFOWrapper() { HasMTFO = ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("com.dak.MTFO"); } [MethodImpl(MethodImplOptions.NoInlining)] private static string GameDataPath_Unsafe() { return MTFOPathAPI.RundownPath; } [MethodImpl(MethodImplOptions.NoInlining)] private static string CustomPath_Unsafe() { return MTFOPathAPI.CustomPath; } [MethodImpl(MethodImplOptions.NoInlining)] private static bool HasCustomContent_Unsafe() { return MTFOPathAPI.HasRundownPath; } } } namespace ItemSpawnFix.CustomSettings { [JsonConverter(typeof(LevelTargetConverter))] public sealed class LevelTarget { public uint LevelLayoutID { get; set; } public eRundownTier Tier { get; set; } = (eRundownTier)99; public int TierIndex { get; set; } = -1; public bool IsMatch(uint layoutID, eRundownTier tier, int tierIndex) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (layoutID == LevelLayoutID) { return true; } if (Tier == tier && (TierIndex == -1 || TierIndex == tierIndex)) { return true; } return false; } } [JsonConverter(typeof(SettingsDataConverter))] public sealed class SettingsData { public static readonly SettingsData Default = new SettingsData(); public static readonly SettingsData[] Template = new SettingsData[3] { new SettingsData { Levels = new LevelTarget[1] { new LevelTarget { LevelLayoutID = 420u } } }, new SettingsData { Levels = new LevelTarget[1] { new LevelTarget { Tier = (eRundownTier)1 } }, SetResourceSpawns = new ResourceSpawnData[1] { new ResourceSpawnData { PackType = (eResourceContainerSpawnType)1, AreaIndex = new int[2] { 0, 1 } } } }, new SettingsData { Levels = new LevelTarget[2] { new LevelTarget { LevelLayoutID = 4115u }, new LevelTarget { Tier = (eRundownTier)2, TierIndex = 0 } }, SetConsumableSpawns = new ConsumableSpawnData[1] { new ConsumableSpawnData() } } }; public uint RundownID; public LevelTarget[] Levels { get; set; } = Array.Empty(); public bool RaiseObjectSpawnPriority { get; set; } public bool AllowRedistributeObjects { get; set; } public ConsumableSpawnData[] SetConsumableSpawns { get; set; } = Array.Empty(); public ResourceSpawnData[] SetResourceSpawns { get; set; } = Array.Empty(); } public sealed class SettingsManager { public static readonly SettingsManager Current; private static (eRundownTier tier, int tierIndex) _currentLevel; private static readonly Dictionary<(eDimensionIndex, LG_LayerType, eLocalZoneIndex), List> _currentSetSpawns; private readonly Dictionary> _fileToData = new Dictionary>(); public static SettingsData ActiveSettings { get; set; } public static bool TryGetSetSpawns(LG_Zone zone, [MaybeNullWhen(false)] out List list) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) return _currentSetSpawns.TryGetValue((zone.DimensionIndex, zone.Layer.m_type, zone.LocalIndex), out list); } public static void ClearSetSpawns(LG_Zone zone) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) _currentSetSpawns.Remove((zone.DimensionIndex, zone.Layer.m_type, zone.LocalIndex)); } private void FileChanged(LiveEditEventArgs e) { DinoLogger.Warning("LiveEdit File Changed: " + e.FileName); LiveEdit.TryReadFileContent(e.FullPath, (Action)delegate(string content) { ReadFileContent(e.FullPath, content); }); } private void FileDeleted(LiveEditEventArgs e) { DinoLogger.Warning("LiveEdit File Removed: " + e.FileName); _fileToData.Remove(e.FullPath); RefreshSettings(); } private void FileCreated(LiveEditEventArgs e) { DinoLogger.Warning("LiveEdit File Created: " + e.FileName); LiveEdit.TryReadFileContent(e.FullPath, (Action)delegate(string content) { ReadFileContent(e.FullPath, content); }); } private void ReadFileContent(string file, string content) { _fileToData.Remove(file); List list = null; try { list = ISFJson.Deserialize>(content); } catch (JsonException ex) { DinoLogger.Error("Error parsing settings json " + file); DinoLogger.Error(ex.Message); } if (list != null) { _fileToData[file] = list; RefreshSettings(); } } private static uint ActiveRundownID() { string activeRundownKey = RundownManager.ActiveRundownKey; uint result = default(uint); if (!RundownManager.RundownProgressionReady || !RundownManager.TryGetIdFromLocalRundownKey(activeRundownKey, ref result)) { return 0u; } return result; } private static void RefreshSettings() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) uint num = ActiveRundownID(); if (num == 0) { ActiveSettings = SettingsData.Default; _currentSetSpawns.Clear(); return; } pActiveExpedition activeExpeditionData = RundownManager.GetActiveExpeditionData(); if ((int)activeExpeditionData.tier == 99) { ActiveSettings = SettingsData.Default; _currentSetSpawns.Clear(); } else { if (_currentLevel.tier == activeExpeditionData.tier && _currentLevel.tierIndex == activeExpeditionData.expeditionIndex) { return; } uint levelLayoutData = RundownManager.ActiveExpedition.LevelLayoutData; IEnumerator enumerator = Current.GetEnumerator(); while (enumerator.MoveNext()) { SettingsData current = enumerator.Current; if (current.RundownID != 0 && current.RundownID != num) { continue; } LevelTarget[] levels = current.Levels; for (int i = 0; i < levels.Length; i++) { if (levels[i].IsMatch(levelLayoutData, activeExpeditionData.tier, activeExpeditionData.expeditionIndex)) { ActiveSettings = current; CacheSetSpawns(); return; } } } } } private static void CacheSetSpawns() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) _currentSetSpawns.Clear(); ConsumableSpawnData[] setConsumableSpawns = ActiveSettings.SetConsumableSpawns; foreach (ConsumableSpawnData consumableSpawnData in setConsumableSpawns) { List list = DictExtensions.GetOrAdd(key: (consumableSpawnData.DimensionIndex, consumableSpawnData.Layer, consumableSpawnData.LocalIndex), dict: _currentSetSpawns); list.EnsureCapacity(list.Count + consumableSpawnData.Count); for (int j = 0; j < consumableSpawnData.Count; j++) { list.Add(consumableSpawnData); } } ResourceSpawnData[] setResourceSpawns = ActiveSettings.SetResourceSpawns; foreach (ResourceSpawnData resourceSpawnData in setResourceSpawns) { List list2 = DictExtensions.GetOrAdd(key: (resourceSpawnData.DimensionIndex, resourceSpawnData.Layer, resourceSpawnData.LocalIndex), dict: _currentSetSpawns); list2.EnsureCapacity(list2.Count + resourceSpawnData.Count); for (int k = 0; k < resourceSpawnData.Count; k++) { list2.Add(resourceSpawnData); } } } private SettingsManager() { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown if (!MTFOWrapper.HasCustomContent) { DinoLogger.Log($"No custom path detected! Has MTFO? {MTFOWrapper.HasMTFO}"); return; } string text = Path.Combine(MTFOWrapper.CustomPath, "ItemSpawnFix"); if (!Directory.Exists(text)) { DinoLogger.Log("No settings directory detected. Creating template."); Directory.CreateDirectory(text); StreamWriter streamWriter = File.CreateText(Path.Combine(text, "Template.json")); streamWriter.WriteLine(ISFJson.Serialize(SettingsData.Template)); streamWriter.Flush(); streamWriter.Close(); } else { DinoLogger.Log("SettingsData directory detected."); } foreach (string item in Directory.EnumerateFiles(text, "*.json", SearchOption.AllDirectories)) { string content = File.ReadAllText(item); ReadFileContent(item, content); } LiveEditListener obj = LiveEdit.CreateListener(text, "*.json", true); obj.FileCreated += new LiveEditEventHandler(FileCreated); obj.FileChanged += new LiveEditEventHandler(FileChanged); obj.FileDeleted += new LiveEditEventHandler(FileDeleted); } static SettingsManager() { ActiveSettings = SettingsData.Default; _currentLevel = (tier: (eRundownTier)99, tierIndex: 0); _currentSetSpawns = new Dictionary<(eDimensionIndex, LG_LayerType, eLocalZoneIndex), List>(); Current = new SettingsManager(); } internal static void Init() { LevelAPI.OnBuildStart += RefreshSettings; } public List GetSettingsData() { List list = new List(_fileToData.Values.Sum((List list2) => list2.Count)); IEnumerator enumerator = GetEnumerator(); while (enumerator.MoveNext()) { list.Add(enumerator.Current); } return list; } public IEnumerator GetEnumerator() { return new DictListEnumerator(_fileToData); } } public abstract class BaseSpawnData { public eDimensionIndex DimensionIndex { get; set; } public LG_LayerType Layer { get; set; } public eLocalZoneIndex LocalIndex { get; set; } [JsonConverter(typeof(OptionalArrayConverter))] public int[] AreaIndex { get; set; } = Array.Empty(); public bool PreferEmpty { get; set; } = true; public int Count { get; set; } = 1; public abstract bool TryGetContainerData([MaybeNullWhen(false)] out ResourceContainerSpawnData data); } public class ConsumableSpawnData : BaseSpawnData { public uint ItemID { get; set; } public override bool TryGetContainerData([MaybeNullWhen(false)] out ResourceContainerSpawnData data) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown if (ItemID == 0) { data = null; return false; } data = new ResourceContainerSpawnData { m_type = (eResourceContainerSpawnType)6, m_comType = (eCommodityType)5114, m_genericItemId = ItemID }; return true; } } public class ResourceSpawnData : BaseSpawnData { public eResourceContainerSpawnType PackType { get; set; } public float Amount { get; set; } public override bool TryGetContainerData([MaybeNullWhen(false)] out ResourceContainerSpawnData data) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0031: Expected I4, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (Amount == 0f) { data = null; return false; } ExpeditionBalanceDataBlock activeExpeditionBalanceData = RundownManager.ActiveExpeditionBalanceData; eResourceContainerSpawnType packType = PackType; float num = (int)packType switch { 0 => activeExpeditionBalanceData.HealthPerZone, 1 => activeExpeditionBalanceData.WeaponAmmoPerZone, 2 => activeExpeditionBalanceData.ToolAmmoPerZone, _ => ((int)packType != 9) ? (-1f) : activeExpeditionBalanceData.DisinfectionPerZone, }; if (num < 0f) { DinoLogger.Error($"Unable to do set resource spawn! Type is invalid ({PackType})!"); data = null; return false; } data = new ResourceContainerSpawnData { m_type = PackType, m_ammo = Amount * num }; return true; } } }