Decompiled source of Item Replacer v1.0.0

BepInEx/plugins/ItemReplacer.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ItemReplacer;
using LethalTraitor;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ItemDropLogger")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ItemDropLogger")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d9fcff8e-712a-485e-ba9a-acb70d779939")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[HarmonyPatch(typeof(RoundManager), "LoadNewLevel")]
internal static class Patch_LoadNewLevel
{
	private static void Postfix(RoundManager __instance)
	{
		ItemReplacerController.Begin(__instance);
	}
}
internal class ItemReplacerController : MonoBehaviour
{
	[CompilerGenerated]
	private sealed class <>c__DisplayClass2_0
	{
		public RoundManager rm;

		internal bool <WaitAndReplace>b__0()
		{
			return rm.bakedNavMesh;
		}
	}

	[CompilerGenerated]
	private sealed class <WaitAndReplace>d__2 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public RoundManager rm;

		private <>c__DisplayClass2_0 <>8__1;

		private float <hardTimeout>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <WaitAndReplace>d__2(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>8__1 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>8__1 = new <>c__DisplayClass2_0();
				<>8__1.rm = rm;
				<hardTimeout>5__2 = 60f;
				goto IL_0074;
			case 1:
				<>1__state = -1;
				goto IL_0074;
			case 2:
				{
					<>1__state = -1;
					if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer)
					{
						return false;
					}
					try
					{
						Plugin.Log.LogInfo((object)"Replacing preplaced items after level load.");
						Plugin.ReplacePreplacedItems();
					}
					catch (Exception arg)
					{
						Plugin.Log.LogError((object)$"Item replacement aborted: {arg}");
					}
					return false;
				}
				IL_0074:
				if (<hardTimeout>5__2 > 0f && (!Object.op_Implicit((Object)(object)<>8__1.rm) || (Object)(object)StartOfRound.Instance == (Object)null))
				{
					<hardTimeout>5__2 -= Time.unscaledDeltaTime;
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				if (!Object.op_Implicit((Object)(object)<>8__1.rm) || (Object)(object)StartOfRound.Instance == (Object)null)
				{
					return false;
				}
				<>2__current = (object)new WaitUntil((Func<bool>)(() => <>8__1.rm.bakedNavMesh));
				<>1__state = 2;
				return true;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	private static ItemReplacerController _instance;

	internal static void Begin(RoundManager rm)
	{
		//IL_0012: 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)
		//IL_001d: Expected O, but got Unknown
		if ((Object)(object)_instance == (Object)null)
		{
			GameObject val = new GameObject("ItemReplacerController");
			Object.DontDestroyOnLoad((Object)val);
			_instance = val.AddComponent<ItemReplacerController>();
		}
		((MonoBehaviour)_instance).StopAllCoroutines();
		((MonoBehaviour)_instance).StartCoroutine(_instance.WaitAndReplace(rm));
	}

	[IteratorStateMachine(typeof(<WaitAndReplace>d__2))]
	private IEnumerator WaitAndReplace(RoundManager rm)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <WaitAndReplace>d__2(0)
		{
			rm = rm
		};
	}
}
namespace ItemReplacer
{
	[HarmonyPatch(typeof(GrabbableObject))]
	public static class GrabbableObject_DiscardItem_Patch
	{
		[HarmonyPostfix]
		[HarmonyPatch("DiscardItem")]
		public static void Postfix_LogObjectNameOnDrop(GrabbableObject __instance)
		{
			if (ConfigSettings.LogDroppedObjectName)
			{
				Plugin.Log.LogInfo((object)("Player dropped item! Object name = " + ((__instance != null && ((Object)__instance).name != null) ? ("\"" + ((Object)__instance).name + "\"") : "<null>")));
			}
		}
	}
	[BepInPlugin("Azx.ItemReplacer", "Item Replacer", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Azx.ItemReplacer";

		public const string PluginName = "Item Replacer";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static Harmony Harmony;

		private static bool sRoutineActive = false;

		private const string ClaimedSuffix = "(Clone)";

		private static readonly Dictionary<string, Item> ResolvedItems = new Dictionary<string, Item>();

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Harmony = new Harmony("Azx.ItemReplacer");
			ConfigSettings.Init((BaseUnityPlugin)(object)this);
			Log.LogInfo((object)"Item Replacer 1.0.0 loaded.");
			Harmony.PatchAll();
		}

		internal static void ReplacePreplacedItems()
		{
			//IL_050b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0510: Unknown result type (might be due to invalid IL or missing references)
			//IL_0513: Unknown result type (might be due to invalid IL or missing references)
			//IL_0518: Unknown result type (might be due to invalid IL or missing references)
			//IL_054b: Unknown result type (might be due to invalid IL or missing references)
			//IL_054d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0562: Unknown result type (might be due to invalid IL or missing references)
			//IL_0567: Unknown result type (might be due to invalid IL or missing references)
			//IL_056b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0570: Unknown result type (might be due to invalid IL or missing references)
			//IL_057b: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_036a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_0396: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			ResolvedItems.Clear();
			foreach (string key in ConfigSettings.ReplacementRules.Keys)
			{
				if (Utility.IsNullOrWhiteSpace(key) || Utility.IsNullOrWhiteSpace(ConfigSettings.ReplacementRules[key]))
				{
					continue;
				}
				string replacementName = ConfigSettings.ReplacementRules[key].Trim();
				if (replacementName.EndsWith("(Clone)"))
				{
					replacementName = replacementName.Substring(0, replacementName.Length - 7).Trim();
				}
				Item val = StartOfRound.Instance?.allItemsList?.itemsList?.FirstOrDefault((Func<Item, bool>)((Item item) => (Object)(object)item != (Object)null && string.Equals(((Object)item).name, replacementName, StringComparison.OrdinalIgnoreCase)));
				if ((Object)(object)val?.spawnPrefab == (Object)null)
				{
					val = StartOfRound.Instance?.allItemsList?.itemsList?.FirstOrDefault((Func<Item, bool>)delegate(Item item)
					{
						object obj2;
						if (item == null)
						{
							obj2 = null;
						}
						else
						{
							GameObject spawnPrefab = item.spawnPrefab;
							obj2 = ((spawnPrefab != null) ? spawnPrefab.GetComponentInChildren<GrabbableObject>(true) : null);
						}
						GrabbableObject val12 = (GrabbableObject)obj2;
						if ((Object)(object)val12 == (Object)null)
						{
							return false;
						}
						string name2 = ((Object)val12).name;
						string name3 = ((object)val12).GetType().Name;
						return string.Equals(name2, replacementName, StringComparison.OrdinalIgnoreCase) || string.Equals(name3, replacementName, StringComparison.OrdinalIgnoreCase);
					});
				}
				if ((Object)(object)val?.spawnPrefab == (Object)null)
				{
					val = StartOfRound.Instance?.allItemsList?.itemsList?.FirstOrDefault((Func<Item, bool>)((Item item) => (Object)(object)item != (Object)null && string.Equals(item.itemName, replacementName, StringComparison.OrdinalIgnoreCase)));
				}
				if ((Object)(object)val?.spawnPrefab == (Object)null)
				{
					Log.LogError((object)("Could not find the '" + ConfigSettings.ReplacementRules[key] + "' item definition and its spawn prefab. Aborting the " + key + " replacement."));
				}
				else
				{
					ResolvedItems[key] = val;
				}
			}
			List<(Item, Vector3, Quaternion, Transform, bool)> list = new List<(Item, Vector3, Quaternion, Transform, bool)>();
			RaycastHit val4 = default(RaycastHit);
			foreach (string key2 in ResolvedItems.Keys)
			{
				List<GrabbableObject> list2 = new List<GrabbableObject>();
				GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsInactive)0, (FindObjectsSortMode)0);
				foreach (GrabbableObject val2 in array)
				{
					if ((Object)(object)val2?.itemProperties != (Object)null && string.Equals(((Object)val2).name, key2))
					{
						list2.Add(val2);
					}
				}
				if (list2.Count == 0)
				{
					Log.LogInfo((object)("No '" + key2 + "' objects found in this scene."));
					continue;
				}
				foreach (GrabbableObject item3 in list2)
				{
					bool? obj;
					if (item3 == null)
					{
						obj = null;
					}
					else
					{
						string name = ((Object)item3).name;
						obj = ((name != null) ? new bool?(Utility.IsNullOrWhiteSpace(name)) : null);
					}
					bool? flag = obj;
					if (flag.GetValueOrDefault(true) || !ResolvedItems.Keys.Contains(((Object)item3).name))
					{
						continue;
					}
					((Object)item3).name = ((Object)item3).name + "(Clone)";
					Transform transform = ((Component)item3).transform;
					bool flag2 = IsOnShip(transform);
					Vector3 position = transform.position;
					Quaternion item2;
					if (flag2)
					{
						item2 = transform.rotation;
					}
					else
					{
						Vector3 val3 = Vector3.up;
						if (Physics.Raycast(position + Vector3.up * 0.25f, Vector3.down, ref val4, 2f, -1, (QueryTriggerInteraction)1))
						{
							val3 = ((RaycastHit)(ref val4)).normal;
						}
						Vector3 val5 = Vector3.ProjectOnPlane(transform.forward, val3);
						if (((Vector3)(ref val5)).sqrMagnitude < 0.0001f)
						{
							val5 = Vector3.ProjectOnPlane(transform.right, val3);
						}
						((Vector3)(ref val5)).Normalize();
						item2 = Quaternion.LookRotation(val5, val3);
					}
					list.Add((ResolvedItems[key2], position, item2, transform.parent, flag2));
				}
				foreach (GrabbableObject item4 in list2)
				{
					if ((Object)(object)item4 != (Object)null && (Object)(object)((Component)item4).gameObject != (Object)null)
					{
						try
						{
							Object.Destroy((Object)(object)((Component)item4).gameObject);
						}
						catch (Exception arg)
						{
							Log.LogWarning((object)$"Failed to destroy {((Object)((Component)item4).gameObject).name}: {arg}");
						}
					}
				}
			}
			if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer)
			{
				Log.LogInfo((object)string.Format("Claimed and destroyed {0} object{1}. Waiting for server to spawn replacements.", list.Count, (list.Count == 1) ? "" : "s"));
				return;
			}
			int num = 0;
			foreach (var (val6, val7, val8, val9, flag3) in list)
			{
				try
				{
					Transform val10 = (flag3 ? val9 : StartOfRound.Instance?.propsContainer);
					GameObject val11 = Object.Instantiate<GameObject>(val6.spawnPrefab, val7, val8, val10);
					Scene scene = ((Component)StartOfRound.Instance).gameObject.scene;
					if (val11.scene != scene)
					{
						SceneManager.MoveGameObjectToScene(val11, scene);
					}
					GrabbableObject component = val11.GetComponent<GrabbableObject>();
					if ((Object)(object)component != (Object)null)
					{
						component.fallTime = 0f;
						if (flag3)
						{
							component.isInShipRoom = true;
						}
						Rigidbody component2 = val11.GetComponent<Rigidbody>();
						if ((Object)(object)component2 != (Object)null)
						{
							component2.velocity = Vector3.zero;
							component2.angularVelocity = Vector3.zero;
						}
					}
					NetworkObject component3 = val11.GetComponent<NetworkObject>();
					if ((Object)(object)component3 != (Object)null)
					{
						component3.Spawn(false);
					}
					else
					{
						Log.LogWarning((object)("Spawned replacement " + ((Object)val6).name + " has no NetworkObject, it will not replicate!"));
					}
					num++;
				}
				catch (Exception arg2)
				{
					Log.LogWarning((object)$"Failed to spawn replacement {((Object)val6).name}: {arg2}");
				}
			}
			Log.LogInfo((object)string.Format("Replaced {0} object{1} with proper networked prefabs.", num, (num == 1) ? "" : "s"));
		}

		private static bool IsOnShip(Transform t)
		{
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				if (((Object)val).name.IndexOf("HangarShip", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}
	}
}
namespace LethalTraitor
{
	internal static class ConfigSettings
	{
		private static readonly List<(ConfigEntry<string> OldEntry, ConfigEntry<string> NewEntry)> _ruleEntries = new List<(ConfigEntry<string>, ConfigEntry<string>)>();

		private static ConfigEntry<bool> _logDroppedObjectName;

		internal static bool LogDroppedObjectName = false;

		internal static Dictionary<string, string> ReplacementRules { get; private set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);


		internal static void Init(BaseUnityPlugin plugin)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			ConfigFile config = plugin.Config;
			_logDroppedObjectName = config.Bind<bool>("Logging", "LogDroppedObjectNames", false, new ConfigDescription("Enable this to see the object name of any item by dropping it. The object name is then written in the game logs. This can be used to easily determine the exact object name of an item.", (AcceptableValueBase)null, Array.Empty<object>()));
			ConfigEntry<int> val = config.Bind<int>("Rule Generation", "NumberOfReplacementRules", 5, new ConfigDescription("Set the number of rules to generate. The 5 default rules fix persistence issues with the preplaced items on Aquatis, and do not affect other items (because these object names do not match normal items).", (AcceptableValueBase)null, Array.Empty<object>()));
			_ruleEntries.Clear();
			for (int i = 0; i < val.Value; i++)
			{
				string text;
				string text2;
				switch (i)
				{
				case 0:
					text = "Boombox";
					text2 = "Boombox";
					break;
				case 1:
					text = "ShovelItem";
					text2 = "Shovel";
					break;
				case 2:
					text = "SprayPaintItem";
					text2 = "Spray paint";
					break;
				case 3:
					text = "MetalSheet";
					text2 = "Metal sheet";
					break;
				case 4:
					text = "BigBolt";
					text2 = "Big bolt";
					break;
				default:
					text = "";
					text2 = "";
					break;
				}
				ConfigEntry<string> val2 = config.Bind<string>("Replacement Rule #" + (i + 1), "OldObjectName" + (i + 1), text, "The old object name. This must be an EXACT match to an existing item's object name. For example, a normal boombox has the object name \"Boombox(Clone)\" so entering \"Boombox\" here will not affect normal boomboxes. If an item isn't being detected then you'll need to check its object name to figure out what to enter here.");
				ConfigEntry<string> val3 = config.Bind<string>("Replacement Rule #" + (i + 1), "NewObjectName" + (i + 1), text2, "The new object name. This accepts object name, item name, or itemName.");
				_ruleEntries.Add((val2, val3));
				val2.SettingChanged += OnAnyRuleSettingChanged;
				val3.SettingChanged += OnAnyRuleSettingChanged;
			}
			_logDroppedObjectName.SettingChanged += OnLogDroppedItemSettingChanged;
			RebuildRules();
			UpdateItemLogging();
		}

		private static void OnAnyRuleSettingChanged(object sender, EventArgs e)
		{
			RebuildRules();
		}

		private static void OnLogDroppedItemSettingChanged(object sender, EventArgs e)
		{
			UpdateItemLogging();
		}

		private static void RebuildRules()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			foreach (var ruleEntry in _ruleEntries)
			{
				ConfigEntry<string> item = ruleEntry.OldEntry;
				ConfigEntry<string> item2 = ruleEntry.NewEntry;
				string text = item?.Value ?? string.Empty;
				string value = item2?.Value ?? string.Empty;
				if (!string.IsNullOrWhiteSpace(text))
				{
					dictionary[text] = value;
				}
			}
			ReplacementRules = dictionary;
		}

		private static void UpdateItemLogging()
		{
			LogDroppedObjectName = _logDroppedObjectName?.Value ?? false;
		}
	}
}