Decompiled source of CustomItemSpawner v1.2.0

CustomItemSpawner/CustomItemSpawner.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CustomItemSpawner")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CustomItemSpawner")]
[assembly: AssemblyTitle("CustomItemSpawner")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CustomItemSpawner
{
	internal struct ItemDirectoryEntry
	{
		internal int Index;

		internal string Name;
	}
	internal static class CreateItemDirectory
	{
		internal static ItemDirectoryEntry[] Create()
		{
			GameObject[] array = (((Object)(object)PrefabsDirectory.instance == (Object)null) ? null : PrefabsDirectory.instance.directory);
			if (array == null)
			{
				return new ItemDirectoryEntry[0];
			}
			ItemDirectoryEntry[] array2 = new ItemDirectoryEntry[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				GameObject val = array[i];
				array2[i] = new ItemDirectoryEntry
				{
					Index = i,
					Name = (((Object)(object)val == (Object)null) ? null : ((Object)val).name)
				};
			}
			return array2;
		}
	}
	[BepInPlugin("com.Exocet.customitemspawner", "Custom Item Spawner", "1.2.0")]
	public sealed class CustomItemSpawnerPlugin : BaseUnityPlugin
	{
		public const string PLUGIN_GUID = "com.Exocet.customitemspawner";

		public const string PLUGIN_NAME = "Custom Item Spawner";

		public const string PLUGIN_VERSION = "1.2.0";

		internal static ConfigEntry<string> CrateDirectoryItem;

		internal static ConfigEntry<string> ItemKeywordSearch;

		internal static ConfigEntry<bool> ClearItemKeywordSearch;

		internal static ConfigEntry<KeyCode> SpawnKey;

		internal static ConfigEntry<bool> CustomBallastObject;

		internal static ConfigEntry<int> CustomObjectMass;

		internal static ManualLogSource Log;

		internal static ConfigFile PluginConfig;

		internal static ItemDirectoryEntry[] ItemDirectory;

		internal static Dictionary<string, int> ItemIndices;

		private static bool itemDirectoryCreated;

		private static bool configurationManagerRefreshRequested;

		private void Awake()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig = ((BaseUnityPlugin)this).Config;
			CrateDirectoryItem = ((BaseUnityPlugin)this).Config.Bind<string>("General", "CrateDirectoryItem", "(Loading item directory...)", new ConfigDescription("Prefab name from PrefabsDirectory.directory used as the custom object.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[1] { "(Loading item directory...)" }), Array.Empty<object>()));
			ItemKeywordSearch = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Item Keyword Search", string.Empty, "Filter the game item directory by a case-insensitive keyword.");
			ClearItemKeywordSearch = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Clear Item Keyword Search", false, "Set to true to clear the keyword search and show the full game item list. This button resets itself after use.");
			CustomBallastObject = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CustomBallastObject", false, "Apply the custom mass and name to spawned objects.");
			CustomObjectMass = ((BaseUnityPlugin)this).Config.Bind<int>("General", "CustomObjectMass", 10000, new ConfigDescription("User defined mass value for this spawned object.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100000), Array.Empty<object>()));
			SpawnKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "SpawnKey", (KeyCode)287, "Key used to spawn the custom object.");
			ItemKeywordSearch.SettingChanged += delegate
			{
				UpdateGameItemDropdown();
			};
			ClearItemKeywordSearch.SettingChanged += delegate
			{
				ClearItemKeywordSearchIfRequested();
			};
			new Harmony("com.Exocet.customitemspawner").PatchAll();
			((MonoBehaviour)this).StartCoroutine(InitializeItemDirectoryWhenReady());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom object spawner loaded.");
		}

		private void Update()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (configurationManagerRefreshRequested)
			{
				RefreshConfigurationManagerSettings();
			}
			if (Input.GetKeyDown(SpawnKey.Value))
			{
				CustomItemSpawner.Spawn();
			}
		}

		internal static void CreateItemDirectoryOnce()
		{
			if (!itemDirectoryCreated)
			{
				RebuildItemDirectory();
			}
		}

		private static void RebuildItemDirectory()
		{
			if ((Object)(object)PrefabsDirectory.instance == (Object)null || PrefabsDirectory.instance.directory == null || PrefabsDirectory.instance.directory.Length == 0)
			{
				return;
			}
			ItemDirectoryEntry[] array = CreateItemDirectory.Create();
			List<string> list = new List<string>();
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			for (int i = 0; i < array.Length; i++)
			{
				string name = array[i].Name;
				if (name != null && !dictionary.ContainsKey(name))
				{
					list.Add(name);
					dictionary.Add(name, array[i].Index);
				}
			}
			if (list.Count != 0)
			{
				ItemDirectory = array;
				ItemIndices = dictionary;
				string text = ((array.Length > 23) ? array[23].Name : null);
				if (text == null || !ItemIndices.ContainsKey(text))
				{
					text = list[0];
				}
				itemDirectoryCreated = true;
				UpdateGameItemDropdown(text);
				Log.LogDebug((object)$"Rebuilt item directory with {ItemDirectory.Length} entries.");
			}
		}

		private static void UpdateGameItemDropdown(string preferredSelection = null)
		{
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Expected O, but got Unknown
			if (!itemDirectoryCreated)
			{
				return;
			}
			List<string> list = new List<string>();
			string text = ((ItemKeywordSearch.Value == null) ? string.Empty : ItemKeywordSearch.Value.Trim());
			for (int i = 0; i < ItemDirectory.Length; i++)
			{
				string name = ItemDirectory[i].Name;
				if (name != null && ItemIndices[name] == ItemDirectory[i].Index && (text.Length == 0 || name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0))
				{
					list.Add(name);
				}
			}
			if (list.Count == 0)
			{
				list.Add("(No matching game items)");
			}
			string text2 = ((CrateDirectoryItem == null) ? null : CrateDirectoryItem.Value);
			string text3 = (list.Contains(text2) ? text2 : (list.Contains(preferredSelection) ? preferredSelection : list[0]));
			PluginConfig.Remove(new ConfigDefinition("General", "CrateDirectoryItem"));
			PluginConfig.Remove(new ConfigDefinition("General", "Game Item Directory"));
			CrateDirectoryItem = PluginConfig.Bind<string>("General", "Game Item Directory", text3, new ConfigDescription("Game object to spawn.", (AcceptableValueBase)(object)new AcceptableValueList<string>(list.ToArray()), Array.Empty<object>()));
			if (CrateDirectoryItem.Value != text3)
			{
				CrateDirectoryItem.Value = text3;
			}
			configurationManagerRefreshRequested = true;
		}

		private static void RefreshConfigurationManagerSettings()
		{
			configurationManagerRefreshRequested = false;
			if (Chainloader.PluginInfos.TryGetValue("com.bepis.bepinex.configurationmanager", out var value) && !((Object)(object)value.Instance == (Object)null))
			{
				MethodInfo method = ((object)value.Instance).GetType().GetMethod("BuildSettingList", BindingFlags.Instance | BindingFlags.Public);
				if (method == null)
				{
					Log.LogWarning((object)"Configuration Manager was found but does not expose BuildSettingList; reopen its window to refresh the game item dropdown.");
					return;
				}
				method.Invoke(value.Instance, null);
				Log.LogDebug((object)"Refreshed Configuration Manager settings after updating the game item dropdown.");
			}
		}

		private static void ClearItemKeywordSearchIfRequested()
		{
			if (ClearItemKeywordSearch.Value)
			{
				ItemKeywordSearch.Value = string.Empty;
				ClearItemKeywordSearch.Value = false;
			}
		}

		private static IEnumerator InitializeItemDirectoryWhenReady()
		{
			while (!itemDirectoryCreated)
			{
				CreateItemDirectoryOnce();
				yield return null;
			}
			if (ClearItemKeywordSearch.Value)
			{
				ClearItemKeywordSearchIfRequested();
			}
		}
	}
	internal static class CustomItemSpawner
	{
		private const string ModDataPrefix = "CustomItemSpawner.item.";

		private const string MarkerSuffix = ".customObject";

		private const string MassSuffix = ".mass";

		private const string NameSuffix = ".name";

		internal static void Spawn()
		{
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			PrefabsDirectory val = (((Object)(object)SaveLoadManager.instance == (Object)null) ? null : ((Component)SaveLoadManager.instance).GetComponent<PrefabsDirectory>());
			string text = ((CustomItemSpawnerPlugin.CrateDirectoryItem == null) ? null : CustomItemSpawnerPlugin.CrateDirectoryItem.Value);
			int value;
			int num = ((CustomItemSpawnerPlugin.ItemIndices == null || text == null || !CustomItemSpawnerPlugin.ItemIndices.TryGetValue(text, out value)) ? (-1) : value);
			if ((Object)(object)val == (Object)null || val.directory == null || num < 0 || num >= val.directory.Length)
			{
				CustomItemSpawnerPlugin.Log.LogError((object)("Cannot spawn custom object: prefab '" + text + "' is unavailable."));
				return;
			}
			GameObject val2 = val.directory[num];
			if ((Object)(object)val2 == (Object)null)
			{
				CustomItemSpawnerPlugin.Log.LogError((object)$"Cannot spawn custom object: PrefabsDirectory index {num} is empty.");
				return;
			}
			if ((Object)(object)Refs.ovrCameraRig == (Object)null)
			{
				CustomItemSpawnerPlugin.Log.LogError((object)"Cannot spawn custom object: the camera rig is unavailable.");
				return;
			}
			GameObject val3 = Object.Instantiate<GameObject>(val2, Refs.ovrCameraRig.position + Refs.ovrCameraRig.forward, Refs.ovrCameraRig.rotation);
			ShipItem component = val3.GetComponent<ShipItem>();
			SaveablePrefab component2 = val3.GetComponent<SaveablePrefab>();
			Good component3 = val3.GetComponent<Good>();
			if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
			{
				Object.Destroy((Object)(object)val3);
				CustomItemSpawnerPlugin.Log.LogError((object)"Cannot spawn custom object: the selected prefab is missing required components.");
				return;
			}
			component.sold = true;
			component2.RegisterToSave();
			if (CustomItemSpawnerPlugin.CustomBallastObject.Value)
			{
				int value2 = CustomItemSpawnerPlugin.CustomObjectMass.Value;
				component.mass = value2;
				component.name = $"{value2} Mass Custom Object";
				SaveCustomObjectProperties(component2.instanceId, component);
			}
			if ((Object)(object)component3 != (Object)null)
			{
				component3.RegisterAsMissionless();
			}
			RestoreLoadedCustomObject(component);
			CustomItemSpawnerPlugin.Log.LogInfo((object)$"Spawned custom object '{((Object)val2).name}' from PrefabsDirectory index {num}.");
		}

		internal static void RestoreLoadedCustomObject(ShipItem item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return;
			}
			SaveablePrefab component = ((Component)item).GetComponent<SaveablePrefab>();
			if (!((Object)(object)component == (Object)null) && IsCustomObject(component.instanceId))
			{
				string modDataKey = GetModDataKey(component.instanceId);
				if (int.TryParse(GetModDataValue(modDataKey + ".mass", ((int)item.mass).ToString(CultureInfo.InvariantCulture)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					item.mass = result;
				}
				item.name = GetModDataValue(modDataKey + ".name", item.name);
			}
		}

		internal static void RestoreLoadedCustomObjects()
		{
			if ((Object)(object)SaveLoadManager.instance == (Object)null)
			{
				return;
			}
			List<SaveablePrefab> currentPrefabs = SaveLoadManager.instance.GetCurrentPrefabs();
			if (currentPrefabs == null)
			{
				return;
			}
			foreach (SaveablePrefab item in currentPrefabs)
			{
				if (!((Object)(object)item == (Object)null))
				{
					RestoreLoadedCustomObject(((Component)item).GetComponent<ShipItem>());
				}
			}
		}

		internal static IEnumerator RestoreLoadedCustomObjectsAfterLoad()
		{
			for (int frame = 0; frame < 2; frame++)
			{
				yield return null;
				RestoreLoadedCustomObjects();
			}
		}

		private static void SaveCustomObjectProperties(int instanceId, ShipItem item)
		{
			if (GameState.modData == null)
			{
				CustomItemSpawnerPlugin.Log.LogWarning((object)"Custom object spawned, but save data is unavailable; custom properties cannot persist.");
				return;
			}
			string modDataKey = GetModDataKey(instanceId);
			GameState.modData[modDataKey + ".customObject"] = "1";
			GameState.modData[modDataKey + ".mass"] = item.mass.ToString(CultureInfo.InvariantCulture);
			GameState.modData[modDataKey + ".name"] = item.name;
		}

		private static bool IsCustomObject(int instanceId)
		{
			return GameState.modData != null && GameState.modData.ContainsKey(GetModDataKey(instanceId) + ".customObject");
		}

		private static string GetModDataKey(int instanceId)
		{
			return "CustomItemSpawner.item." + instanceId;
		}

		private static string GetModDataValue(string key, string fallback)
		{
			string value;
			return GameState.modData.TryGetValue(key, out value) ? value : fallback;
		}
	}
	[HarmonyPatch(typeof(SaveLoadManager), "LoadModData")]
	internal static class CustomObjectLoadPatch
	{
		private static void Postfix()
		{
			CustomItemSpawnerPlugin.CreateItemDirectoryOnce();
			CustomItemSpawner.RestoreLoadedCustomObjects();
			if ((Object)(object)SaveLoadManager.instance != (Object)null)
			{
				((MonoBehaviour)SaveLoadManager.instance).StartCoroutine(CustomItemSpawner.RestoreLoadedCustomObjectsAfterLoad());
			}
			CustomItemSpawnerPlugin.Log.LogDebug((object)"Save data loaded; item directory initialized and custom object properties restored.");
		}
	}
	[HarmonyPatch(typeof(ShipItem), "OnLoad")]
	internal static class CustomObjectShipItemLoadPatch
	{
		private static void Postfix(ShipItem __instance)
		{
			CustomItemSpawner.RestoreLoadedCustomObject(__instance);
		}
	}
	[HarmonyPatch(typeof(ShipItem), "ProcessSaveable")]
	internal static class CustomObjectProcessSaveablePatch
	{
		private static void Postfix(ShipItem __instance)
		{
			CustomItemSpawner.RestoreLoadedCustomObject(__instance);
		}
	}
	[HarmonyPatch(typeof(ItemRigidbody), "UpdateMass")]
	internal static class CustomObjectMassUpdatePatch
	{
		private static void Prefix(ItemRigidbody __instance)
		{
			if ((Object)(object)__instance != (Object)null)
			{
				CustomItemSpawner.RestoreLoadedCustomObject(__instance.GetShipItem());
			}
		}
	}
}