Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ShipStarterKit v1.0.0
ShipStarterKit.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ShipStarterKit")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ShipStarterKit")] [assembly: AssemblyTitle("ShipStarterKit")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] 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; } } [CompilerGenerated] [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 ShipStarterKit { internal static class Es3SaveGate { public const string SuppliedKey = "ShipStarterKit_Supplied"; private static Type? _es3Type; private static bool _es3Resolved; private static MethodInfo? _keyExists; private static MethodInfo? _deleteKey; private static MethodInfo? _saveBool; public static string? CurrentSaveName() { GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(instance.currentSaveFileName)) { return null; } return instance.currentSaveFileName; } public static bool AlreadySupplied(string saveName) { if (TryEs3KeyExists("ShipStarterKit_Supplied", saveName, out var exists)) { return exists; } return File.Exists(FallbackPath(saveName)); } public static void MarkSupplied(string saveName) { if (TryEs3SaveBool("ShipStarterKit_Supplied", value: true, saveName)) { Plugin.V("ES3 marked ShipStarterKit_Supplied=true on '" + saveName + "'."); return; } try { string text = FallbackPath(saveName); Directory.CreateDirectory(Path.GetDirectoryName(text)); File.WriteAllText(text, "1"); Plugin.Log.LogWarning((object)("ES3 unavailable; wrote fallback marker " + text)); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to write supply marker for '{saveName}': {arg}"); } } public static void ClearSupplied(string saveName) { if (TryEs3DeleteKey("ShipStarterKit_Supplied", saveName)) { Plugin.V("ES3 cleared ShipStarterKit_Supplied on '" + saveName + "' (new game reset)."); } try { string path = FallbackPath(saveName); if (File.Exists(path)) { File.Delete(path); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to clear fallback marker for '" + saveName + "': " + ex.Message)); } } private static string FallbackPath(string saveName) { string text = string.Join("_", saveName.Split(Path.GetInvalidFileNameChars())); return Path.Combine(Paths.ConfigPath, "ShipStarterKit", "supplied_" + text + ".flag"); } private static bool EnsureEs3() { if (_es3Resolved) { return _es3Type != null; } _es3Resolved = true; _es3Type = AccessTools.TypeByName("ES3"); if (_es3Type == null) { Plugin.Log.LogWarning((object)"ES3 type not found; using file fallback for first-load gate."); return false; } _keyExists = AccessTools.Method(_es3Type, "KeyExists", new Type[2] { typeof(string), typeof(string) }, (Type[])null); _deleteKey = AccessTools.Method(_es3Type, "DeleteKey", new Type[2] { typeof(string), typeof(string) }, (Type[])null); MethodInfo[] methods = _es3Type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "Save") && methodInfo.IsGenericMethodDefinition) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 3 && parameters[0].ParameterType == typeof(string) && parameters[2].ParameterType == typeof(string)) { _saveBool = methodInfo.MakeGenericMethod(typeof(bool)); break; } } } if (_keyExists == null || _saveBool == null) { Plugin.Log.LogWarning((object)"ES3 methods incomplete; using file fallback for first-load gate."); _es3Type = null; return false; } Plugin.V("ES3 reflection ready for ShipStarterKit save gate."); return true; } private static bool TryEs3KeyExists(string key, string file, out bool exists) { exists = false; if (!EnsureEs3() || _keyExists == null) { return false; } try { exists = (bool)_keyExists.Invoke(null, new object[2] { key, file }); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("ES3.KeyExists failed: " + ex.Message)); return false; } } private static bool TryEs3SaveBool(string key, bool value, string file) { if (!EnsureEs3() || _saveBool == null) { return false; } try { _saveBool.Invoke(null, new object[3] { key, value, file }); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("ES3.Save<bool> failed: " + ex.Message)); return false; } } private static bool TryEs3DeleteKey(string key, string file) { if (!EnsureEs3() || _deleteKey == null) { return false; } try { _deleteKey.Invoke(null, new object[2] { key, file }); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("ES3.DeleteKey failed: " + ex.Message)); return false; } } } internal static class HostModGate { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnMessage; public static Action<ulong> <1>__OnClientConnected; } public const string MessageName = "MrGlim.ShipStarterKit"; private const byte OpHostHello = 0; private const byte OpClientSyncRequest = 1; private static bool _registered; private static bool _clientConnectedHooked; private static bool _requestedSync; private static bool _clientHostEnabled; public static bool HostHasMod { get { EnsureRegistered(); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return true; } if (singleton.IsServer || singleton.IsHost) { return LocalEnabled(); } return _clientHostEnabled; } } public static bool FeaturesActive => HostHasMod; private static bool LocalEnabled() { if (Plugin.Enabled != null) { return Plugin.Enabled.Value; } return false; } public static void Reset() { _registered = false; _clientConnectedHooked = false; _requestedSync = false; _clientHostEnabled = false; } public static void EnsureRegistered() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { _registered = false; _clientConnectedHooked = false; return; } if (!_registered) { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__OnMessage; if (obj == null) { HandleNamedMessageDelegate val = OnMessage; <>O.<0>__OnMessage = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("MrGlim.ShipStarterKit", (HandleNamedMessageDelegate)obj); _registered = true; Plugin.Log.LogInfo((object)"MrGlim.ShipStarterKit net handler registered."); } if (singleton.IsServer && !_clientConnectedHooked) { singleton.OnClientConnectedCallback += OnClientConnected; _clientConnectedHooked = true; } if (!singleton.IsServer && singleton.IsConnectedClient && !_requestedSync) { _requestedSync = true; RequestSync(); } } private static void OnClientConnected(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && clientId != singleton.LocalClientId) { SendHello(clientId); } } private static void RequestSync() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !singleton.IsServer) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); byte b = 1; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("MrGlim.ShipStarterKit", 0uL, val, (NetworkDelivery)2); ((FastBufferWriter)(ref val)).Dispose(); } } private static void SendHello(ulong clientId) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); byte b = 0; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); bool flag = LocalEnabled(); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref flag, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("MrGlim.ShipStarterKit", clientId, val, (NetworkDelivery)2); ((FastBufferWriter)(ref val)).Dispose(); } } private static void OnMessage(ulong sender, FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) //IL_0044: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives)); NetworkManager singleton = NetworkManager.Singleton; switch (b) { case 1: if ((Object)(object)singleton != (Object)null && singleton.IsServer) { SendHello(sender); } break; case 0: { bool clientHostEnabled = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref clientHostEnabled, default(ForPrimitives)); if ((Object)(object)singleton != (Object)null && !singleton.IsServer) { _clientHostEnabled = clientHostEnabled; Plugin.Log.LogInfo((object)("Host hello: enabled=" + clientHostEnabled)); } break; } } } } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect")] internal static class HostModGateDisconnectPatch { public static void Prefix() { HostModGate.Reset(); Plugin.Log.LogInfo((object)"MrGlim.ShipStarterKit gate reset on disconnect."); } } [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class HostModGateStartPatch { public static void Postfix() { HostModGate.EnsureRegistered(); } } internal static class ItemCountConfig { private const string Section = "ItemCounts"; private static readonly (string Key, string[] Matchers, int Default)[] KnownDefaults = new(string, string[], int)[7] { ("BeltBag", new string[2] { "belt bag", "beltbag" }, 10), ("ProFlashlight", new string[3] { "pro-flashlight", "pro flashlight", "proflashlight" }, 10), ("Jetpack", new string[1] { "jetpack" }, 10), ("Lockpicker", new string[2] { "lockpicker", "lockpick" }, 10), ("SprayPaint", new string[2] { "spray paint", "spraypaint" }, 10), ("WeedKiller", new string[2] { "weed killer", "weedkiller" }, 10), ("Shovel", new string[1] { "shovel" }, 10) }; private static readonly Dictionary<string, ConfigEntry<int>> Entries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> BoundDynamicKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static ConfigFile? _config; private static bool _defaultsBound; public static void BindDefaults(ConfigFile config) { _config = config; if (!_defaultsBound) { (string, string[], int)[] knownDefaults = KnownDefaults; for (int i = 0; i < knownDefaults.Length; i++) { var (text, _, num) = knownDefaults[i]; Entries[text] = config.Bind<int>("ItemCounts", text, num, "How many '" + text + "' to spawn on first save load (0 = skip). Matched flexibly against store itemName."); } _defaultsBound = true; } } public static void EnsureBuyableEntries(Item[]? buyable) { if (_config == null || buyable == null) { return; } foreach (Item val in buyable) { if (!((Object)(object)val == (Object)null) && !string.IsNullOrWhiteSpace(val.itemName) && !val.isScrap && !TryGetKnownKey(val.itemName, out string _)) { string text = SanitizeKey(val.itemName); if (!string.IsNullOrEmpty(text) && !Entries.ContainsKey(text) && !BoundDynamicKeys.Contains(text)) { Entries[text] = _config.Bind<int>("ItemCounts", text, 0, "How many '" + val.itemName + "' to spawn on first save load (0 = skip). Auto-discovered from Terminal.buyableItemsList."); BoundDynamicKeys.Add(text); Plugin.V("Bound dynamic config ItemCounts." + text + " for store item '" + val.itemName + "' (default 0)."); } } } } public static int GetCountForItem(Item item) { if ((Object)(object)item == (Object)null || string.IsNullOrWhiteSpace(item.itemName)) { return 0; } if (TryGetKnownKey(item.itemName, out string key) && Entries.TryGetValue(key, out ConfigEntry<int> value)) { return Math.Max(0, value.Value); } string key2 = SanitizeKey(item.itemName); if (Entries.TryGetValue(key2, out ConfigEntry<int> value2)) { return Math.Max(0, value2.Value); } return 0; } public static bool TryGetKnownKey(string itemName, out string key) { string text = itemName.Trim(); (string, string[], int)[] knownDefaults = KnownDefaults; for (int i = 0; i < knownDefaults.Length; i++) { (string, string[], int) tuple = knownDefaults[i]; string item = tuple.Item1; string[] item2 = tuple.Item2; foreach (string value in item2) { if (text.Equals(value, StringComparison.OrdinalIgnoreCase) || text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { key = item; return true; } } } key = ""; return false; } private static string SanitizeKey(string itemName) { string text = Regex.Replace(itemName.Trim(), "[^A-Za-z0-9]+", ""); if (text.Length == 0) { return ""; } if (char.IsDigit(text[0])) { text = "Item" + text; } return text; } } [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class StartOfRoundStartSupplyPatch { public static void Postfix(StartOfRound __instance) { HostModGate.EnsureRegistered(); NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && (singleton.IsServer || singleton.IsHost)) { StarterKitSpawner.TrySupplyOnShipStart(); } } } [HarmonyPatch(typeof(GameNetworkManager), "ResetSavedGameValues")] internal static class ResetSavedGameValuesClearGatePatch { public static void Postfix(GameNetworkManager __instance) { if (!((Object)(object)__instance == (Object)null) && !string.IsNullOrEmpty(__instance.currentSaveFileName)) { Es3SaveGate.ClearSupplied(__instance.currentSaveFileName); StarterKitSpawner.ResetSession(); Plugin.V("Cleared ShipStarterKit supply flag after ResetSavedGameValues on '" + __instance.currentSaveFileName + "'."); } } } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect")] internal static class DisconnectResetSessionPatch { public static void Prefix() { StarterKitSpawner.ResetSession(); } } [BepInPlugin("com.benhough.lethal.ShipStarterKit", "ShipStarterKit", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "com.benhough.lethal.ShipStarterKit"; public const string ModName = "ShipStarterKit"; public const string ModVersion = "1.0.0"; private readonly Harmony _harmony = new Harmony("com.benhough.lethal.ShipStarterKit"); internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<bool> VerboseLogging { get; private set; } internal static ConfigEntry<bool> SpawnOncePerSave { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle. Host with this mod enabled supplies starter store equipment on first load of each save."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Log item matching, pile positions, and save-gate decisions."); SpawnOncePerSave = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SpawnOncePerSave", true, "If true (recommended), spawn only once per save file via an ES3 flag on that save. Reloading the same save or returning from moons will not re-spawn. Set false only for testing."); ItemCountConfig.BindDefaults(((BaseUnityPlugin)this).Config); try { _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"ShipStarterKit v1.0.0 loaded."); } catch (Exception arg) { Log.LogError((object)$"Harmony patch failed: {arg}"); } } internal static void V(string msg) { if (VerboseLogging != null && VerboseLogging.Value) { Log.LogInfo((object)msg); } } } internal static class PluginInfo { public const string PLUGIN_GUID = "com.benhough.lethal.ShipStarterKit"; public const string PLUGIN_NAME = "ShipStarterKit"; public const string PLUGIN_VERSION = "1.0.0"; } internal static class StarterKitSpawner { private static bool _sessionAttempted; public static void ResetSession() { _sessionAttempted = false; } public static void TrySupplyOnShipStart() { //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) if (_sessionAttempted) { return; } _sessionAttempted = true; if (Plugin.Enabled == null || !Plugin.Enabled.Value) { Plugin.V("Skip supply: Enabled=false."); return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || (!singleton.IsServer && !singleton.IsHost)) { Plugin.V("Skip supply: not host/server."); return; } if (!HostModGate.FeaturesActive) { Plugin.V("Skip supply: HostModGate inactive."); return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { Plugin.Log.LogWarning((object)"Skip supply: StartOfRound.Instance null."); return; } string text = Es3SaveGate.CurrentSaveName(); if (string.IsNullOrEmpty(text)) { Plugin.Log.LogWarning((object)"Skip supply: currentSaveFileName unavailable."); return; } bool flag = Plugin.SpawnOncePerSave == null || Plugin.SpawnOncePerSave.Value; if (flag && Es3SaveGate.AlreadySupplied(text)) { Plugin.Log.LogInfo((object)("ShipStarterKit: save '" + text + "' already supplied — skipping.")); return; } Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val == (Object)null || val.buyableItemsList == null || val.buyableItemsList.Length == 0) { Plugin.Log.LogWarning((object)"Skip supply: Terminal.buyableItemsList unavailable."); return; } ItemCountConfig.EnsureBuyableEntries(val.buyableItemsList); List<(Item, int)> list = BuildSpawnPlan(val.buyableItemsList); if (list.Count == 0) { Plugin.Log.LogInfo((object)"ShipStarterKit: all ItemCounts are 0 — nothing to spawn."); if (flag) { Es3SaveGate.MarkSupplied(text); } return; } Vector3[] array = BuildPileCenters(instance, list.Count); int num = 0; for (int i = 0; i < list.Count; i++) { (Item, int) tuple = list[i]; Item item = tuple.Item1; int item2 = tuple.Item2; Vector3 val2 = array[i]; Plugin.Log.LogInfo((object)$"Spawning pile '{item.itemName}' x{item2} at {val2}"); num += SpawnPile(instance, item, item2, val2); } if (flag) { Es3SaveGate.MarkSupplied(text); } Plugin.Log.LogInfo((object)$"ShipStarterKit: spawned {num} store items across {list.Count} piles on '{text}'."); } private static List<(Item item, int count)> BuildSpawnPlan(Item[] buyable) { List<(Item, int)> list = new List<(Item, int)>(); HashSet<Item> hashSet = new HashSet<Item>(); foreach (Item val in buyable) { if ((Object)(object)val == (Object)null || (Object)(object)val.spawnPrefab == (Object)null || val.isScrap || hashSet.Contains(val)) { continue; } int countForItem = ItemCountConfig.GetCountForItem(val); if (countForItem > 0) { if (ItemCountConfig.TryGetKnownKey(val.itemName, out string key)) { Plugin.V($"Matched store item '{val.itemName}' → config '{key}' count={countForItem}"); } else { Plugin.V($"Matched store item '{val.itemName}' → dynamic count={countForItem}"); } list.Add((val, countForItem)); hashSet.Add(val); } } return list; } private static Vector3[] BuildPileCenters(StartOfRound sor, int pileCount) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_0104: Unknown result type (might be due to invalid IL or missing references) Vector3[] array = (Vector3[])(object)new Vector3[pileCount]; Vector3 val = ((sor.playerSpawnPositions != null && sor.playerSpawnPositions.Length != 0 && (Object)(object)sor.playerSpawnPositions[0] != (Object)null) ? sor.playerSpawnPositions[0].position : ((!((Object)(object)sor.elevatorTransform != (Object)null)) ? Vector3.zero : sor.elevatorTransform.position)); val += new Vector3(0f, 0.15f, 1.2f); if (pileCount == 1) { array[0] = val; return array; } int num = Mathf.Clamp(Mathf.CeilToInt(Mathf.Sqrt((float)pileCount)), 2, 4); int num2 = Mathf.CeilToInt((float)pileCount / (float)num); float num3 = 1.15f; float num4 = 1.05f; float num5 = -0.5f * (float)(num - 1) * num3; float num6 = -0.35f * (float)(num2 - 1) * num4; for (int i = 0; i < pileCount; i++) { int num7 = i % num; int num8 = i / num; array[i] = val + new Vector3(num5 + (float)num7 * num3, 0f, num6 + (float)num8 * num4); } return array; } private static int SpawnPile(StartOfRound sor, Item item, int count, Vector3 center) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)sor.elevatorTransform != (Object)null) ? sor.elevatorTransform : sor.propsContainer); int num = 0; Random random = new Random((item.itemName?.GetHashCode() ?? 0) ^ (count * 397) ^ ((int)(center.x * 100f) * 31) ^ (int)(center.z * 100f)); for (int i = 0; i < count; i++) { try { float num2 = (float)(random.NextDouble() * 0.55 - 0.275); float num3 = (float)(random.NextDouble() * 0.55 - 0.275); float num4 = 0.05f + (float)(random.NextDouble() * 0.08); Vector3 val2 = center + new Vector3(num2, num4, num3); if ((Object)(object)sor.shipBounds != (Object)null) { Bounds bounds = sor.shipBounds.bounds; if (!((Bounds)(ref bounds)).Contains(val2)) { val2 = center; val2.y = center.y + 0.1f; Plugin.V($"Pile item '{item.itemName}' #{i} outside shipBounds; snapped to pile center."); } } GameObject val3 = Object.Instantiate<GameObject>(item.spawnPrefab, val2, Quaternion.identity, val); GrabbableObject component = val3.GetComponent<GrabbableObject>(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogWarning((object)("Prefab for '" + item.itemName + "' has no GrabbableObject — destroying.")); Object.Destroy((Object)(object)val3); continue; } component.fallTime = 0f; component.scrapPersistedThroughRounds = true; component.isInElevator = true; component.isInShipRoom = true; if ((Object)(object)component.radarIcon != (Object)null) { Object.Destroy((Object)(object)((Component)component.radarIcon).gameObject); } NetworkObject networkObject = ((NetworkBehaviour)component).NetworkObject; if ((Object)(object)networkObject == (Object)null) { Plugin.Log.LogWarning((object)("Prefab for '" + item.itemName + "' has no NetworkObject — destroying.")); Object.Destroy((Object)(object)val3); } else { networkObject.Spawn(false); num++; } } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed spawning '{item.itemName}' #{i}: {arg}"); } } return num; } } }