Decompiled source of MergeScrap v1.1.1

MergeScrap.dll

Decompiled an hour ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using GameNetcodeStuff;
using HarmonyLib;
using InteractiveTerminalAPI.UI;
using InteractiveTerminalAPI.UI.Application;
using InteractiveTerminalAPI.UI.Cursor;
using InteractiveTerminalAPI.UI.Screen;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[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("MergeScrap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1")]
[assembly: AssemblyProduct("MergeScrap")]
[assembly: AssemblyTitle("MergeScrap")]
[assembly: AssemblyVersion("1.1.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MergeScrap
{
	internal static class TokMerge
	{
		internal static bool LooksMerge(string[] parts)
		{
			if (parts.Length == 0)
			{
				return false;
			}
			List<string> list = new List<string>(parts.Length);
			for (int i = 0; i < parts.Length; i++)
			{
				list.Add(parts[i]);
			}
			while (list.Count > 0)
			{
				if (!int.TryParse(list[list.Count - 1], out var _))
				{
					break;
				}
				list.RemoveAt(list.Count - 1);
			}
			if (list.Count > 0)
			{
				return VerbOk(list);
			}
			return false;
		}

		internal static bool VerbOk(List<string> words)
		{
			if (words.Count == 1)
			{
				string text = words[0];
				if (text.Equals("merge", StringComparison.Ordinal))
				{
					return true;
				}
				if (text.Equals("merge-scrap", StringComparison.Ordinal))
				{
					return true;
				}
				if (text.Equals("scrap-merge", StringComparison.Ordinal))
				{
					return true;
				}
			}
			if (words.Count == 2 && words[0].Equals("merge", StringComparison.Ordinal) && words[1].Equals("scrap", StringComparison.Ordinal))
			{
				return true;
			}
			return false;
		}
	}
	internal static class DoneGroups
	{
		private static readonly HashSet<string> completed = new HashSet<string>(StringComparer.Ordinal);

		internal static void Wipe()
		{
			completed.Clear();
		}

		internal static void MarkGroupMerged(string groupKey)
		{
			if (!string.IsNullOrEmpty(groupKey))
			{
				completed.Add(groupKey);
			}
		}

		internal static bool IsGroupMerged(string groupKey)
		{
			if (string.IsNullOrEmpty(groupKey))
			{
				return false;
			}
			return completed.Contains(groupKey);
		}
	}
	internal static class EnterOnce
	{
		private static int lastMergeConfirmFrame = -1;

		internal static bool AlreadyHandledThisFrame()
		{
			return Time.frameCount == lastMergeConfirmFrame;
		}

		internal static void MarkHandledThisFrame()
		{
			lastMergeConfirmFrame = Time.frameCount;
		}
	}
	internal static class EnterOk
	{
		internal static bool WantsConfirm(object terminal)
		{
			if (TerminalReflection.GetTextAdded(terminal) <= 0)
			{
				return true;
			}
			if (MergeMenu.UserHasNavigatedSinceOpen)
			{
				if (!TerminalReflection.TryGetInputChunk(terminal, out string chunk))
				{
					return true;
				}
				string text = NormCmd.Strip(chunk).Trim();
				if (TokMerge.LooksMerge(text.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries)))
				{
					return true;
				}
				if (text.Length == 0)
				{
					return true;
				}
				return false;
			}
			if (!TerminalReflection.TryGetInputChunk(terminal, out string chunk2))
			{
				return false;
			}
			TokMerge.LooksMerge(NormCmd.Strip(chunk2).Trim().Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries));
			return false;
		}
	}
	internal static class ShutHudChat
	{
		private static bool snap;

		private static float savedTargetAlpha;

		private static float savedCgAlpha;

		private static bool savedBlocksRaycasts;

		private static bool savedInteractable;

		internal static void GoDark()
		{
			HUDManager instance = HUDManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)instance.Chat?.canvasGroup != (Object)null)
			{
				if (!snap)
				{
					savedTargetAlpha = instance.Chat.targetAlpha;
					savedCgAlpha = instance.Chat.canvasGroup.alpha;
					savedBlocksRaycasts = instance.Chat.canvasGroup.blocksRaycasts;
					savedInteractable = instance.Chat.canvasGroup.interactable;
					snap = true;
				}
				if (instance.Chat.fadeCoroutine != null)
				{
					((MonoBehaviour)instance).StopCoroutine(instance.Chat.fadeCoroutine);
					instance.Chat.fadeCoroutine = null;
				}
				instance.Chat.targetAlpha = 0f;
				instance.Chat.canvasGroup.alpha = 0f;
				instance.Chat.canvasGroup.blocksRaycasts = false;
				instance.Chat.canvasGroup.interactable = false;
			}
			if (!((Object)(object)instance.chatTextField == (Object)null))
			{
				((Selectable)instance.chatTextField).interactable = false;
				instance.chatTextField.readOnly = true;
				instance.chatTextField.text = string.Empty;
				instance.chatTextField.DeactivateInputField(false);
				if ((Object)(object)instance.chatText != (Object)null)
				{
					((Behaviour)instance.chatText).enabled = false;
				}
				if ((Object)(object)instance.typingIndicator != (Object)null)
				{
					((Behaviour)instance.typingIndicator).enabled = false;
				}
				if ((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null)
				{
					GameNetworkManager.Instance.localPlayerController.isTypingChat = false;
				}
			}
		}

		internal static void UnDim()
		{
			HUDManager instance = HUDManager.Instance;
			if ((Object)(object)instance?.Chat?.canvasGroup != (Object)null && snap)
			{
				instance.Chat.targetAlpha = savedTargetAlpha;
				instance.Chat.canvasGroup.alpha = savedCgAlpha;
				instance.Chat.canvasGroup.blocksRaycasts = savedBlocksRaycasts;
				instance.Chat.canvasGroup.interactable = savedInteractable;
			}
			snap = false;
			if (!((Object)(object)instance?.chatTextField == (Object)null))
			{
				instance.chatTextField.readOnly = false;
				((Selectable)instance.chatTextField).interactable = true;
				if ((Object)(object)instance.chatText != (Object)null)
				{
					((Behaviour)instance.chatText).enabled = true;
				}
			}
		}
	}
	internal static class LateChatNuke
	{
		internal static void Tick()
		{
			if (!MergeMenu.IsActive || MergeMenu.BoundTerminal == null)
			{
				return;
			}
			HUDManager instance = HUDManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)instance.Chat?.canvasGroup != (Object)null)
			{
				instance.Chat.targetAlpha = 0f;
				instance.Chat.canvasGroup.alpha = 0f;
				instance.Chat.canvasGroup.blocksRaycasts = false;
				instance.Chat.canvasGroup.interactable = false;
			}
			if ((Object)(object)instance.chatText != (Object)null)
			{
				((Behaviour)instance.chatText).enabled = false;
			}
			if ((Object)(object)instance.typingIndicator != (Object)null)
			{
				((Behaviour)instance.typingIndicator).enabled = false;
			}
			if ((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null)
			{
				GameNetworkManager.Instance.localPlayerController.isTypingChat = false;
			}
			TMP_InputField chatTextField = instance.chatTextField;
			if ((Object)(object)chatTextField == (Object)null)
			{
				return;
			}
			if (chatTextField.text.Length > 0)
			{
				chatTextField.text = string.Empty;
				chatTextField.stringPosition = 0;
			}
			EventSystem current = EventSystem.current;
			if ((Object)(object)current == (Object)null)
			{
				return;
			}
			GameObject currentSelectedGameObject = current.currentSelectedGameObject;
			Transform transform = ((Component)chatTextField).transform;
			if ((Object)(object)currentSelectedGameObject != (Object)null && ((Object)(object)currentSelectedGameObject.transform == (Object)(object)transform || currentSelectedGameObject.transform.IsChildOf(transform)))
			{
				chatTextField.DeactivateInputField(false);
				current.SetSelectedGameObject((GameObject)null);
				if (MergeMenu.BoundTerminal != null)
				{
					TerminalReflection.LockTermField(MergeMenu.BoundTerminal, on: true);
				}
			}
		}
	}
	internal static class MergeMenu
	{
		private static TerminalNode? pendingNode;

		private static object? pendingTerm;

		private static List<ScrapPile>? rows;

		internal static bool IsActive { get; private set; }

		internal static object? BoundTerminal { get; private set; }

		internal static int SelectedIndex { get; private set; }

		internal static bool OpenedViaItapi { get; private set; }

		internal static bool UserHasNavigatedSinceOpen { get; private set; }

		internal static List<ScrapPile>? GetRowsSnapshot()
		{
			return rows;
		}

		internal static void Open(object term, List<ScrapPile> rowList)
		{
			Arm(term, rowList, itapi: false);
		}

		internal static void OpenForItapi(object term, List<ScrapPile> rowList)
		{
			Arm(term, rowList, itapi: true);
		}

		private static void Arm(object term, List<ScrapPile> rowList, bool itapi)
		{
			SelectedIndex = 0;
			UserHasNavigatedSinceOpen = false;
			if (rowList == null || rowList.Count == 0)
			{
				IsActive = false;
				BoundTerminal = null;
				rows = null;
				OpenedViaItapi = false;
				return;
			}
			OpenedViaItapi = itapi;
			BoundTerminal = term;
			rows = rowList;
			IsActive = true;
			if (!itapi)
			{
				RefreshDisplay(term);
				TerminalReflection.LockTermField(term, on: true);
			}
			ShutHudChat.GoDark();
		}

		internal static void SetSelectedIndex(int i)
		{
			if (rows != null && rows.Count > 0)
			{
				SelectedIndex = Mathf.Clamp(i, 0, rows.Count - 1);
			}
		}

		internal static void SetPendingFinalNodeAfterItapi(object term, TerminalNode node)
		{
			pendingTerm = term;
			pendingNode = node;
		}

		internal static void ApplyPendingNodeAfterItapiDestroyed()
		{
			if (pendingTerm != null && (Object)(object)pendingNode != (Object)null)
			{
				TerminalReflection.LoadNewNode(pendingTerm, pendingNode);
			}
			pendingTerm = null;
			pendingNode = null;
		}

		internal static void Close()
		{
			if (IsActive || BoundTerminal != null || OpenedViaItapi)
			{
				ShutHudChat.UnDim();
				if (BoundTerminal != null)
				{
					TerminalReflection.LockTermField(BoundTerminal, on: false);
				}
				IsActive = false;
				BoundTerminal = null;
				rows = null;
				SelectedIndex = 0;
				UserHasNavigatedSinceOpen = false;
				OpenedViaItapi = false;
			}
		}

		private static List<ScrapPile> RebuildFiltered()
		{
			List<ScrapPile> list = GrabPiles.ListForMenu();
			List<ScrapPile> list2 = new List<ScrapPile>();
			for (int i = 0; i < list.Count; i++)
			{
				ScrapPile scrapPile = list[i];
				if (!DoneGroups.IsGroupMerged(scrapPile.GroupKey))
				{
					list2.Add(scrapPile);
				}
			}
			return list2;
		}

		internal static void RefreshDisplay(object term)
		{
			if (IsActive && term == BoundTerminal && rows != null)
			{
				TerminalReflection.SetScreenBody(term, "\n\n\n" + GrabPiles.FormatMenu(rows, SelectedIndex));
			}
		}

		internal static bool TryConfirmSelection(out TerminalNode node)
		{
			node = null;
			if (!IsActive || BoundTerminal == null || rows == null || rows.Count == 0)
			{
				return false;
			}
			SelectedIndex = Mathf.Clamp(SelectedIndex, 0, rows.Count - 1);
			ScrapPile scrapPile = rows[SelectedIndex];
			string displayName = scrapPile.DisplayName;
			int totalValue = scrapPile.TotalValue;
			if (!SquashGroup.TryMergeRow(scrapPile, out string error, out int destroyed))
			{
				rows = RebuildFiltered();
				SelectedIndex = Mathf.Clamp(SelectedIndex, 0, Mathf.Max(0, rows.Count - 1));
				node = ScreenNode.Make(error + "\n\n" + GrabPiles.FormatMenu(rows, SelectedIndex));
				return true;
			}
			DoneGroups.MarkGroupMerged(scrapPile.GroupKey);
			Plugin.PlayMergeSound();
			rows = RebuildFiltered();
			SelectedIndex = Mathf.Clamp(SelectedIndex, 0, Mathf.Max(0, rows.Count - 1));
			string text = displayName + " merged " + destroyed + " stack $" + totalValue + " item in ship middle\n\n";
			if (rows.Count == 0)
			{
				Close();
				node = ScreenNode.Make(text + "nothing left\n\n");
				return true;
			}
			UserHasNavigatedSinceOpen = true;
			node = ScreenNode.Make(text + GrabPiles.FormatMenu(rows, SelectedIndex));
			return true;
		}

		internal static void MoveSelection(int delta, object term)
		{
			if (rows != null && rows.Count != 0)
			{
				int count = rows.Count;
				SelectedIndex = ((Mathf.Clamp(SelectedIndex, 0, count - 1) + delta) % count + count) % count;
				UserHasNavigatedSinceOpen = true;
				RefreshDisplay(term);
			}
		}
	}
	internal enum PileSpot
	{
		Ship,
		Vehicle,
		Facility
	}
	internal sealed class ScrapPile
	{
		public string GroupKey { get; set; } = "";


		public string DisplayName { get; set; } = "";


		public List<GrabbableObject> PhysicalItems { get; } = new List<GrabbableObject>();


		public int InventoryCount { get; set; }

		public int InventoryValueSum { get; set; }

		public int PhysicalCount => PhysicalItems.Count;

		public int TotalPieces => PhysicalCount + InventoryCount;

		public int PhysicalValueSum
		{
			get
			{
				int num = 0;
				for (int i = 0; i < PhysicalItems.Count; i++)
				{
					GrabbableObject val = PhysicalItems[i];
					if ((Object)(object)val != (Object)null)
					{
						num += val.scrapValue;
					}
				}
				return num;
			}
		}

		public int TotalValue => PhysicalValueSum + InventoryValueSum;

		public int CountAt(PileSpot place)
		{
			int num = 0;
			for (int i = 0; i < PhysicalItems.Count; i++)
			{
				GrabbableObject val = PhysicalItems[i];
				if ((Object)(object)val != (Object)null && GrabPiles.PlaceOf(val) == place)
				{
					num++;
				}
			}
			return num;
		}

		public string FormatLocationCounts()
		{
			List<string> list = new List<string>();
			int num = CountAt(PileSpot.Ship);
			int num2 = CountAt(PileSpot.Facility);
			int num3 = CountAt(PileSpot.Vehicle);
			if (num > 0)
			{
				list.Add($"Ship:{num}");
			}
			if (num2 > 0)
			{
				list.Add($"Floor:{num2}");
			}
			if (num3 > 0)
			{
				list.Add($"Cruiser:{num3}");
			}
			if (InventoryCount > 0)
			{
				list.Add($"Inv:{InventoryCount}");
			}
			if (list.Count <= 0)
			{
				return "";
			}
			return " [" + string.Join(" ", list) + "]";
		}
	}
	internal static class GrabPiles
	{
		internal static string PrefabKeyOf(GrabbableObject g)
		{
			return ((Object)g).name.Replace("(Clone)", "", StringComparison.Ordinal);
		}

		internal static PileSpot PlaceOf(GrabbableObject g)
		{
			StartOfRound instance = StartOfRound.Instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				VehicleController attachedVehicle = instance.attachedVehicle;
				obj = ((attachedVehicle != null) ? ((Component)attachedVehicle).transform : null);
			}
			Transform val = (Transform)obj;
			if ((Object)(object)val != (Object)null)
			{
				Transform val2 = ((Component)g).transform;
				while ((Object)(object)val2 != (Object)null)
				{
					if ((Object)(object)val2 == (Object)(object)val)
					{
						return PileSpot.Vehicle;
					}
					val2 = val2.parent;
				}
			}
			if (!g.isInShipRoom)
			{
				return PileSpot.Facility;
			}
			return PileSpot.Ship;
		}

		internal static List<GrabbableObject> CollectPhysicalScrap()
		{
			List<GrabbableObject> list = new List<GrabbableObject>();
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0);
			foreach (GrabbableObject val in array)
			{
				if (Plugin.ScrapListable(val))
				{
					list.Add(val);
				}
			}
			return list;
		}

		internal static List<ScrapPile> BuildRows()
		{
			Dictionary<string, ScrapPile> dictionary = new Dictionary<string, ScrapPile>(StringComparer.Ordinal);
			foreach (GrabbableObject item in CollectPhysicalScrap())
			{
				string text = PrefabKeyOf(item);
				if (!string.IsNullOrEmpty(text))
				{
					if (!dictionary.TryGetValue(text, out var value))
					{
						ScrapPile obj = new ScrapPile
						{
							GroupKey = text,
							DisplayName = (item.itemProperties?.itemName ?? text)
						};
						value = obj;
						dictionary[text] = obj;
					}
					value.PhysicalItems.Add(item);
				}
			}
			foreach (var (text2, displayName, num) in ShipInventoryMerge.EnumerateInventoryScrapRows())
			{
				if (!string.IsNullOrEmpty(text2))
				{
					if (!dictionary.TryGetValue(text2, out var value2))
					{
						ScrapPile obj2 = new ScrapPile
						{
							GroupKey = text2,
							DisplayName = displayName
						};
						value2 = obj2;
						dictionary[text2] = obj2;
					}
					value2.InventoryCount++;
					value2.InventoryValueSum += num;
				}
			}
			List<ScrapPile> list = new List<ScrapPile>(dictionary.Values);
			list.Sort(ComparePilesForDisplay);
			return list;
		}

		private static int ComparePilesForDisplay(ScrapPile a, ScrapPile b)
		{
			int num = b.TotalPieces.CompareTo(a.TotalPieces);
			if (num == 0)
			{
				return b.TotalValue.CompareTo(a.TotalValue);
			}
			return num;
		}

		internal static List<ScrapPile> ListForMenu()
		{
			List<ScrapPile> list = BuildRows();
			List<ScrapPile> list2 = new List<ScrapPile>();
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].TotalPieces >= 2)
				{
					list2.Add(list[i]);
				}
			}
			return list2;
		}

		internal static string FormatMenu(IReadOnlyList<ScrapPile> rows, int sel = -1)
		{
			if (rows.Count == 0)
			{
				return "empty\n\n";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("arrows, enter");
			stringBuilder.AppendLine();
			for (int i = 0; i < rows.Count; i++)
			{
				ScrapPile scrapPile = rows[i];
				stringBuilder.AppendLine(string.Format("{0}{1}. {2} ×{3} → ${4}{5}", (sel == i) ? "* " : "  ", i + 1, scrapPile.DisplayName, scrapPile.TotalPieces, scrapPile.TotalValue, scrapPile.FormatLocationCounts()));
			}
			return stringBuilder.ToString();
		}
	}
	internal static class SquashGroup
	{
		private const BindingFlags PublicInstance = BindingFlags.Instance | BindingFlags.Public;

		private const float ShipJitter = 0.15f;

		private static int GrabbableLocationSortRank(GrabbableObject g)
		{
			return GrabPiles.PlaceOf(g) switch
			{
				PileSpot.Ship => 2, 
				PileSpot.Facility => 1, 
				_ => 0, 
			};
		}

		internal static bool TryMergeRow(ScrapPile row, out string error, out int destroyed)
		{
			error = "";
			destroyed = 0;
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)val == (Object)null)
			{
				error = "no player";
				return false;
			}
			if (!((NetworkBehaviour)val).IsHost)
			{
				error = "host only";
				return false;
			}
			List<GrabbableObject> list = new List<GrabbableObject>();
			for (int i = 0; i < row.PhysicalItems.Count; i++)
			{
				GrabbableObject val2 = row.PhysicalItems[i];
				if ((Object)(object)val2 != (Object)null && !val2.isHeld)
				{
					list.Add(val2);
				}
			}
			List<object> list2 = ShipInventoryMerge.CollectInventoryEntriesMatchingGroup(row.GroupKey);
			if (list.Count + list2.Count < 2)
			{
				error = "need 2+";
				return false;
			}
			int num = ShipInventoryMerge.SumInventoryScrapValues(list2);
			object obj = ((list2.Count > 0) ? list2[0] : null);
			List<GrabbableObject> list3;
			if (list.Count >= 2)
			{
				list3 = new List<GrabbableObject>(list);
				list3.Sort(delegate(GrabbableObject a, GrabbableObject b)
				{
					int value = GrabbableLocationSortRank(a);
					int num4 = GrabbableLocationSortRank(b).CompareTo(value);
					return (num4 == 0) ? a.scrapValue.CompareTo(b.scrapValue) : num4;
				});
			}
			else
			{
				list3 = list;
			}
			GrabbableObject val3 = ((list3.Count > 0) ? list3[0] : null);
			int num2 = 0;
			for (int j = 0; j < list3.Count; j++)
			{
				num2 += list3[j].scrapValue;
			}
			int num3 = (((Object)(object)val3 != (Object)null && list3.Count >= 2) ? (num2 + num) : ((!((Object)(object)val3 != (Object)null)) ? num : (val3.scrapValue + num)));
			if (list2.Count > 0 && !ShipInventoryMerge.TryRemoveInventoryEntries(list2, out error))
			{
				return false;
			}
			destroyed = list.Count + list2.Count - 1;
			if ((Object)(object)val3 != (Object)null)
			{
				for (int k = 1; k < list3.Count; k++)
				{
					Object.Destroy((Object)(object)((Component)list3[k]).gameObject);
				}
				val3.SetScrapValue(num3);
				PlaceAtShipCenter(val3);
				return true;
			}
			if (obj == null || !TrySpawnMergedFromInv(obj, num3, out error))
			{
				if (list2.Count > 0)
				{
					ShipInventoryMerge.TryAddInventoryEntries(list2, out string _, verifyCount: false);
				}
				return false;
			}
			return true;
		}

		private static void PlaceAtShipCenter(GrabbableObject g)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				GameObject obj = GameObject.Find("/Environment/HangarShip");
				Transform val = ((obj != null) ? obj.transform : null) ?? instance.elevatorTransform;
				Transform[] insideShipPositions = instance.insideShipPositions;
				Vector3 val2 = ((insideShipPositions != null && insideShipPositions.Length > 10 && (Object)(object)insideShipPositions[10] != (Object)null) ? (insideShipPositions[10].position + new Vector3(0f, 0f, 1.5f)) : (((Object)(object)instance.elevatorTransform != (Object)null) ? instance.elevatorTransform.position : ((Component)g).transform.position));
				val2.x += Random.Range(-0.15f, 0.15f);
				val2.z += Random.Range(-0.15f, 0.15f);
				g.parentObject = null;
				((Component)g).transform.SetParent(val, true);
				((Component)g).transform.position = val2;
				g.isInShipRoom = (g.isInElevator = true);
				g.EnablePhysics(true);
				g.EnableItemMeshes(true);
				g.fallTime = 0f;
				g.hasHitGround = true;
				g.floorYRot = -1;
				((Component)g).transform.rotation = Quaternion.Euler(g.itemProperties.restingRotation);
				g.targetFloorPosition = val.InverseTransformPoint(val2);
				((Component)g).transform.localPosition = g.targetFloorPosition;
				g.startFallingPosition = g.targetFloorPosition;
			}
		}

		private static object? GetPropertyOrField(Type declaringType, object instance, string memberName)
		{
			PropertyInfo property = declaringType.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public);
			if (property != null)
			{
				return property.GetValue(instance);
			}
			return declaringType.GetField(memberName, BindingFlags.Instance | BindingFlags.Public)?.GetValue(instance);
		}

		private static bool TrySpawnMergedFromInv(object data, int scrap, out string err)
		{
			err = "";
			if (!ShipInventoryMerge.EnsureItemDataApiReady())
			{
				err = "no ShipInv API";
				return false;
			}
			object obj = ShipInventoryMerge.ResolveGameItemFromItemDataBoxed(data);
			if (obj == null)
			{
				err = "bad inv row";
				return false;
			}
			Type type = obj.GetType();
			object? propertyOrField = GetPropertyOrField(type, obj, "spawnPrefab");
			GameObject val = (GameObject)((propertyOrField is GameObject) ? propertyOrField : null);
			if (val == null)
			{
				err = "no prefab";
				return false;
			}
			Type type2 = data.GetType();
			object propertyOrField2 = GetPropertyOrField(type, obj, "saveItemVariable");
			bool flag = default(bool);
			int num;
			if (propertyOrField2 is bool)
			{
				flag = (bool)propertyOrField2;
				num = 1;
			}
			else
			{
				num = 0;
			}
			bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			propertyOrField2 = GetPropertyOrField(type2, data, "PERSISTED_THROUGH_ROUNDS");
			bool flag3 = default(bool);
			int num2;
			if (propertyOrField2 is bool)
			{
				flag3 = (bool)propertyOrField2;
				num2 = 1;
			}
			else
			{
				num2 = 0;
			}
			bool scrapPersistedThroughRounds = (byte)((uint)num2 & (flag3 ? 1u : 0u)) != 0;
			int num4 = ((GetPropertyOrField(type2, data, "SAVE_DATA") is int num3) ? num3 : 0);
			GameObject val2 = Object.Instantiate<GameObject>(val);
			NetworkObject componentInChildren = default(NetworkObject);
			if (!val2.TryGetComponent<NetworkObject>(ref componentInChildren))
			{
				componentInChildren = val2.GetComponentInChildren<NetworkObject>(true);
			}
			GrabbableObject componentInChildren2 = default(GrabbableObject);
			if (!val2.TryGetComponent<GrabbableObject>(ref componentInChildren2))
			{
				componentInChildren2 = val2.GetComponentInChildren<GrabbableObject>(true);
			}
			if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				err = "spawn busted";
				return false;
			}
			try
			{
				componentInChildren.Spawn(true);
			}
			catch (Exception ex)
			{
				Object.Destroy((Object)(object)val2);
				err = ex.InnerException?.Message ?? ex.Message;
				return false;
			}
			componentInChildren2.scrapPersistedThroughRounds = scrapPersistedThroughRounds;
			if (flag2)
			{
				componentInChildren2.LoadItemSaveData(num4);
			}
			componentInChildren2.SetScrapValue(scrap);
			PlaceAtShipCenter(componentInChildren2);
			return true;
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_TerminalParse
	{
		private static readonly Lazy<MethodInfo> Target = new Lazy<MethodInfo>(() => AccessTools.Method(TerminalType.Value, "ParsePlayerSentence", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no ParsePlayerSentence"));

		private static MethodBase TargetMethod()
		{
			return Target.Value;
		}

		[HarmonyPriority(800)]
		private static bool Prefix(object __instance, ref TerminalNode __result)
		{
			if (!TerminalReflection.TryGetInputChunk(__instance, out string chunk))
			{
				return true;
			}
			chunk = NormCmd.Strip(chunk);
			string[] array = chunk.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
			if (MergeMenu.IsActive && array.Length != 0 && !TokMerge.LooksMerge(array))
			{
				MergeMenu.Close();
			}
			if (!TokMerge.LooksMerge(array))
			{
				return true;
			}
			List<string> list = new List<string>(array.Length);
			for (int i = 0; i < array.Length; i++)
			{
				list.Add(array[i]);
			}
			while (list.Count > 0)
			{
				if (!int.TryParse(list[list.Count - 1], out var _))
				{
					break;
				}
				list.RemoveAt(list.Count - 1);
			}
			if (!TokMerge.VerbOk(list))
			{
				return true;
			}
			DoneGroups.Wipe();
			List<ScrapPile> list2 = GrabPiles.ListForMenu();
			bool flag = InteractiveTerminalManager.ContainsApplication(TerminalReflection.GetRawInputSuffixForItapi(__instance).Trim());
			if (list2.Count == 0)
			{
				__result = ScreenNode.Make("\n\n\n" + GrabPiles.FormatMenu(list2));
				return false;
			}
			if (flag)
			{
				MergeMenu.OpenForItapi(__instance, list2);
				return true;
			}
			MergeMenu.Open(__instance, list2);
			string text = GrabPiles.FormatMenu(list2, MergeMenu.IsActive ? MergeMenu.SelectedIndex : (-1));
			__result = ScreenNode.Make("\n\n\n" + text);
			return false;
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_TerminalSubmit
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(TerminalType.Value, "OnSubmit", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no OnSubmit");
		}

		[HarmonyPriority(800)]
		private static bool Prefix(object __instance)
		{
			if (!TerminalReflection.GetTerminalInUse(__instance))
			{
				return true;
			}
			if (!MergeMenu.IsActive || MergeMenu.BoundTerminal != __instance)
			{
				return true;
			}
			if (MergeMenu.OpenedViaItapi)
			{
				return true;
			}
			if (!EnterOk.WantsConfirm(__instance))
			{
				return true;
			}
			if (EnterOnce.AlreadyHandledThisFrame())
			{
				return false;
			}
			if (!MergeMenu.TryConfirmSelection(out TerminalNode node) || (Object)(object)node == (Object)null)
			{
				return true;
			}
			TerminalReflection.LoadNewNode(__instance, node);
			EnterOnce.MarkHandledThisFrame();
			if (!MergeMenu.IsActive)
			{
				TerminalReflection.ActivateInput(__instance);
			}
			return false;
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_TerminalQuit
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(TerminalType.Value, "QuitTerminal", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no QuitTerminal");
		}

		private static void Postfix()
		{
			MergeMenu.Close();
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_AfterLoadNode
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(TerminalType.Value, "LoadNewNode", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no LoadNewNode");
		}

		private static void Postfix(object __instance)
		{
			if (MergeMenu.IsActive && MergeMenu.BoundTerminal != null && MergeMenu.BoundTerminal == __instance && !MergeMenu.OpenedViaItapi)
			{
				if ((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null)
				{
					GameNetworkManager.Instance.localPlayerController.isTypingChat = false;
				}
				ShutHudChat.GoDark();
				MergeMenu.RefreshDisplay(__instance);
				TerminalReflection.LockTermField(__instance, on: true);
			}
		}
	}
	[HarmonyPatch(typeof(TerminalApplication), "UpdateText")]
	internal static class MergeHooks_ItapiUpdateText
	{
		private static readonly FieldInfo? CurrentScreenField = AccessTools.Field(typeof(TerminalApplication), "currentScreen");

		private static bool Prefix(TerminalApplication __instance)
		{
			if (CurrentScreenField == null)
			{
				return true;
			}
			return CurrentScreenField.GetValue(__instance) != null;
		}
	}
	[HarmonyPatch(typeof(InteractiveTerminalManager), "OnDestroy")]
	internal static class MergeHooks_ItapiDestroy
	{
		private static void Postfix()
		{
			MergeMenu.ApplyPendingNodeAfterItapiDestroyed();
			MergeMenu.Close();
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_TerminalUpdate
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(TerminalType.Value, "Update", (Type[])null, (Type[])null) ?? throw new InvalidOperationException("no Terminal.Update");
		}

		[HarmonyPostfix]
		[HarmonyPriority(int.MaxValue)]
		private static void Postfix(object __instance)
		{
			if (!MergeMenu.IsActive || MergeMenu.BoundTerminal == null || MergeMenu.BoundTerminal != __instance || MergeMenu.OpenedViaItapi)
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			Keyboard current = Keyboard.current;
			if (current != null)
			{
				flag = ((ButtonControl)current.upArrowKey).wasPressedThisFrame;
				flag2 = ((ButtonControl)current.downArrowKey).wasPressedThisFrame;
			}
			if (!flag && !flag2)
			{
				flag = Input.GetKeyDown((KeyCode)273);
				flag2 = Input.GetKeyDown((KeyCode)274);
			}
			if (flag)
			{
				MergeMenu.MoveSelection(-1, __instance);
			}
			else if (flag2)
			{
				MergeMenu.MoveSelection(1, __instance);
			}
			bool flag3 = current != null && (((ButtonControl)current.enterKey).wasPressedThisFrame || ((ButtonControl)current.numpadEnterKey).wasPressedThisFrame);
			if (!flag3)
			{
				flag3 = Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271);
			}
			if (!flag3)
			{
				return;
			}
			Terminal val = (Terminal)((__instance is Terminal) ? __instance : null);
			if (val == null || EnterOnce.AlreadyHandledThisFrame())
			{
				return;
			}
			TerminalNode node;
			if (!EnterOk.WantsConfirm(__instance))
			{
				val.OnSubmit();
			}
			else if (MergeMenu.TryConfirmSelection(out node) && (Object)(object)node != (Object)null)
			{
				TerminalReflection.LoadNewNode(__instance, node);
				EnterOnce.MarkHandledThisFrame();
				if (!MergeMenu.IsActive)
				{
					TerminalReflection.ActivateInput(__instance);
				}
			}
			else
			{
				val.OnSubmit();
			}
		}
	}
	[HarmonyPatch(typeof(TMP_InputField), "ProcessEvent")]
	[HarmonyPriority(800)]
	internal static class MergeHooks_ChatField
	{
		private static bool Prefix(TMP_InputField __instance, Event e)
		{
			if (!MergeMenu.IsActive)
			{
				return true;
			}
			TMP_InputField val = HUDManager.Instance?.chatTextField;
			if (val == null || __instance != val)
			{
				return true;
			}
			e.Use();
			return false;
		}
	}
	[HarmonyPatch(typeof(TMP_InputField), "ProcessEvent")]
	[HarmonyPriority(800)]
	internal static class MergeHooks_TerminalArrows
	{
		private static bool Prefix(TMP_InputField __instance, Event e)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_002b: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			if (!MergeMenu.IsActive || MergeMenu.BoundTerminal == null)
			{
				return true;
			}
			if (TerminalReflection.GetScreenTextObject(MergeMenu.BoundTerminal) != __instance)
			{
				return true;
			}
			if ((int)e.type != 4)
			{
				return true;
			}
			KeyCode keyCode = e.keyCode;
			if (keyCode - 273 > 1)
			{
				return true;
			}
			e.Use();
			return false;
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_HudEnableChat
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.DeclaredMethod(typeof(HUDManager), "EnableChat_performed", new Type[1] { typeof(CallbackContext) }, (Type[])null) ?? throw new InvalidOperationException("no EnableChat_performed");
		}

		private static bool Prefix()
		{
			return !MergeMenu.IsActive;
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_HudSubmitChat
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.DeclaredMethod(typeof(HUDManager), "SubmitChat_performed", new Type[1] { typeof(CallbackContext) }, (Type[])null) ?? throw new InvalidOperationException("no SubmitChat_performed");
		}

		private static bool Prefix()
		{
			return !MergeMenu.IsActive;
		}
	}
	[HarmonyPatch(typeof(HUDManager), "PingHUDElement")]
	internal static class MergeHooks_HudPing
	{
		private static bool Prefix(HUDElement element)
		{
			if (MergeMenu.IsActive && !((Object)(object)HUDManager.Instance == (Object)null))
			{
				return HUDManager.Instance.Chat != element;
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class MergeHooks_ChuteRpc
	{
		private static bool Prepare()
		{
			return (object)TargetMethod() != null;
		}

		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("ShipInventoryUpdated.Scripts.ChuteRetrieve");
			if ((object)type == null)
			{
				return null;
			}
			MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "RetrieveItemsServerRpc"))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType.IsArray)
					{
						return methodInfo;
					}
				}
			}
			return null;
		}

		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !ShipInventoryMergeSuppression.IsSuppressed;
		}
	}
	[BepInPlugin("mine.MergeScrap", "MergeScrap", "1.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "mine.MergeScrap";

		public const string PluginName = "MergeScrap";

		public const string PluginVersion = "1.1.1";

		public const int MergeSoundClipIndex = 0;

		private Harmony harmony;

		public static Plugin Instance { get; private set; }

		public static ConfigEntry<bool> SoundOnMerge { get; private set; }

		private void Awake()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			Instance = this;
			SoundOnMerge = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SoundOnMerge", true, "merge sound");
			InteractiveTerminalManager.RegisterApplication<ItapiPicker>(new string[4] { "merge", "merge scrap", "merge-scrap", "scrap-merge" }, false);
			harmony = new Harmony("mine9289.mergescrap");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"mine9289 / MergeScrap 1.1.1");
		}

		private void LateUpdate()
		{
			LateChatNuke.Tick();
		}

		private void OnDestroy()
		{
			MergeMenu.Close();
		}

		internal static void PlayMergeSound()
		{
			if (!SoundOnMerge.Value)
			{
				return;
			}
			Terminal val = Object.FindObjectOfType<Terminal>();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				val.PlayTerminalAudioServerRpc(0);
			}
			catch
			{
			}
		}

		public static bool ScrapListable(GrabbableObject g)
		{
			if ((Object)(object)g == (Object)null)
			{
				return false;
			}
			if ((Object)(object)g.itemProperties == (Object)null)
			{
				return false;
			}
			if (!g.itemProperties.isScrap)
			{
				return false;
			}
			if (g.isHeld)
			{
				return false;
			}
			if (!g.grabbable)
			{
				return false;
			}
			return true;
		}
	}
	public sealed class ItapiPicker : InteractiveTerminalApplication<CursorElement>
	{
		private string banner = "Pick an item";

		public override void Initialization()
		{
			Rebuild(MergeMenu.GetRowsSnapshot() ?? new List<ScrapPile>());
		}

		protected override Action PreviousScreen()
		{
			return delegate
			{
				PopItapi();
			};
		}

		private static void PopItapi()
		{
			InteractiveTerminalManager instance = InteractiveTerminalManager.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)instance).gameObject);
			}
		}

		private void Rebuild(List<ScrapPile> rows)
		{
			if (rows.Count == 0)
			{
				CursorElement val = CursorElement.Create("close", "", (Action)delegate
				{
					PopItapi();
				}, (Func<CursorElement, bool>)null, true);
				CursorMenu<CursorElement> val2 = (CursorMenu<CursorElement>)(object)(((BaseInteractiveApplication<CursorElement>)(object)this).currentCursorMenu = (BaseCursorMenu<CursorElement>)(object)CursorMenu<CursorElement>.Create(0, '>', (CursorElement[])(object)new CursorElement[1] { val }, (Func<CursorElement, CursorElement, int>[])null));
				((TerminalApplication)this).currentScreen = (IScreen)(object)BoxedScreen.Create("merge", (ITextElement[])(object)new ITextElement[3]
				{
					(ITextElement)TextElement.Create("empty"),
					(ITextElement)TextElement.Create(" "),
					(ITextElement)val2
				});
				return;
			}
			CursorElement[] array = (CursorElement[])(object)new CursorElement[rows.Count];
			for (int i = 0; i < rows.Count; i++)
			{
				ScrapPile scrapPile = rows[i];
				string gk = scrapPile.GroupKey;
				array[i] = CursorElement.Create($"{scrapPile.DisplayName}{scrapPile.FormatLocationCounts()} ×{scrapPile.TotalPieces} → ${scrapPile.TotalValue}", "", (Action)delegate
				{
					OnPick(gk);
				}, (Func<CursorElement, bool>)null, true);
			}
			CursorMenu<CursorElement> val3 = (CursorMenu<CursorElement>)(object)(((BaseInteractiveApplication<CursorElement>)(object)this).currentCursorMenu = (BaseCursorMenu<CursorElement>)(object)CursorMenu<CursorElement>.Create(0, '>', array, (Func<CursorElement, CursorElement, int>[])null));
			((TerminalApplication)this).currentScreen = (IScreen)(object)BoxedScreen.Create("merge", (ITextElement[])(object)new ITextElement[5]
			{
				(ITextElement)TextElement.Create(banner),
				(ITextElement)TextElement.Create(" "),
				(ITextElement)TextElement.Create("enter = merge / esc = cancel"),
				(ITextElement)TextElement.Create(" "),
				(ITextElement)val3
			});
		}

		private static string Truncate(string? t)
		{
			if (string.IsNullOrEmpty(t))
			{
				return "—";
			}
			string[] array = t.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
			string text = ((array.Length != 0) ? array[0] : t);
			if (text.Length <= 220)
			{
				return text;
			}
			return text.Substring(0, 217) + "...";
		}

		private void OnPick(string groupKey)
		{
			string groupKey2 = groupKey;
			object boundTerminal = MergeMenu.BoundTerminal;
			int num = MergeMenu.GetRowsSnapshot()?.FindIndex((ScrapPile r) => r.GroupKey == groupKey2) ?? (-1);
			if (num < 0)
			{
				return;
			}
			MergeMenu.SetSelectedIndex(num);
			if (!MergeMenu.TryConfirmSelection(out TerminalNode node) || (Object)(object)node == (Object)null)
			{
				return;
			}
			if (!MergeMenu.IsActive)
			{
				if (boundTerminal != null)
				{
					MergeMenu.SetPendingFinalNodeAfterItapi(boundTerminal, node);
				}
				PopItapi();
			}
			else
			{
				banner = Truncate(node.displayText);
				Rebuild(MergeMenu.GetRowsSnapshot() ?? new List<ScrapPile>());
			}
		}
	}
	internal static class ShipInventoryMergeSuppression
	{
		private static int depth;

		internal static bool IsSuppressed => depth > 0;

		internal static void Enter()
		{
			depth++;
		}

		internal static void Exit()
		{
			if (depth > 0)
			{
				depth--;
			}
		}
	}
	internal static class ShipInventoryMerge
	{
		[CompilerGenerated]
		private sealed class <EnumerateInventoryScrapRows>d__23 : IEnumerable<(string groupKey, string displayName, int scrapValue)>, IEnumerable, IEnumerator<(string groupKey, string displayName, int scrapValue)>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private (string groupKey, string displayName, int scrapValue) <>2__current;

			private int <>l__initialThreadId;

			private IEnumerator <>7__wrap1;

			(string, string, int) IEnumerator<(string, string, int)>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

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

			[DebuggerHidden]
			public <EnumerateInventoryScrapRows>d__23(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						if (!EnsureLoaded())
						{
							return false;
						}
						IEnumerable enumerable = InvItems();
						if (enumerable == null)
						{
							return false;
						}
						<>7__wrap1 = enumerable.GetEnumerator();
						<>1__state = -3;
						break;
					}
					case 1:
						<>1__state = -3;
						break;
					}
					while (<>7__wrap1.MoveNext())
					{
						object current = <>7__wrap1.Current;
						if (current != null && TryInspect(current, out string groupKey, out string displayName, out int scrapValue))
						{
							<>2__current = (groupKey, displayName, scrapValue);
							<>1__state = 1;
							return true;
						}
					}
					<>m__Finally1();
					<>7__wrap1 = null;
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>7__wrap1 is IDisposable disposable)
				{
					disposable.Dispose();
				}
			}

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

			[DebuggerHidden]
			IEnumerator<(string groupKey, string displayName, int scrapValue)> IEnumerable<(string, string, int)>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <EnumerateInventoryScrapRows>d__23(0);
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<(string, string, int)>)this).GetEnumerator();
			}
		}

		private const BindingFlags PublicInstance = BindingFlags.Instance | BindingFlags.Public;

		private const BindingFlags PublicStatic = BindingFlags.Static | BindingFlags.Public;

		private static bool ok;

		private static Assembly? asm;

		private static Type? itemDataType;

		private static Type? inventoryType;

		private static MethodInfo? getItem;

		private static MethodInfo? remove;

		private static MethodInfo? add;

		private static MethodInfo? addRpc;

		private static FieldInfo? invInstanceField;

		private static Func<object, int>? scrap;

		internal static bool EnsureItemDataApiReady()
		{
			return EnsureLoaded();
		}

		internal static object? ResolveGameItemFromItemDataBoxed(object boxed)
		{
			if (!EnsureLoaded() || getItem == null)
			{
				return null;
			}
			try
			{
				return getItem.Invoke(boxed, null);
			}
			catch
			{
				return null;
			}
		}

		private static object? GetPropertyOrField(Type declaringType, object instance, string memberName)
		{
			PropertyInfo property = declaringType.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public);
			if (property != null)
			{
				return property.GetValue(instance);
			}
			return declaringType.GetField(memberName, BindingFlags.Instance | BindingFlags.Public)?.GetValue(instance);
		}

		private static bool IsScrap(Type declaringType, object instance)
		{
			object propertyOrField = GetPropertyOrField(declaringType, instance, "isScrap");
			bool flag = default(bool);
			int num;
			if (propertyOrField is bool)
			{
				flag = (bool)propertyOrField;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static IEnumerable? InvItems()
		{
			if (inventoryType?.GetProperty("Items", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is IEnumerable result)
			{
				return result;
			}
			return null;
		}

		internal static int GetInventoryItemCount()
		{
			if (!EnsureLoaded() || inventoryType == null)
			{
				return -1;
			}
			object obj = inventoryType.GetProperty("Count", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
			if (obj is int)
			{
				return (int)obj;
			}
			return -1;
		}

		private static object? InvBehaviour()
		{
			if (inventoryType == null)
			{
				return null;
			}
			if ((object)invInstanceField == null)
			{
				invInstanceField = inventoryType.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic);
			}
			object obj = invInstanceField?.GetValue(null);
			if (obj == null)
			{
				Object[] array = Object.FindObjectsByType(inventoryType, (FindObjectsSortMode)0);
				if (array == null || array.Length <= 0)
				{
					return null;
				}
				obj = array[0];
			}
			return obj;
		}

		internal static List<object> CollectInventoryEntriesMatchingGroup(string key)
		{
			List<object> list = new List<object>();
			if (string.IsNullOrEmpty(key) || !EnsureLoaded())
			{
				return list;
			}
			IEnumerable enumerable = InvItems();
			if (enumerable == null)
			{
				return list;
			}
			foreach (object item in enumerable)
			{
				if (item != null && TryInspect(item, out string groupKey, out string _, out int _) && string.Equals(groupKey, key, StringComparison.Ordinal))
				{
					list.Add(item);
				}
			}
			return list;
		}

		internal static int SumInventoryScrapValues(IReadOnlyList<object> list)
		{
			if (scrap == null || list.Count == 0)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i] != null)
				{
					num += scrap(list[i]);
				}
			}
			return num;
		}

		internal static bool TryRemoveInventoryEntries(IReadOnlyList<object> entries, out string error)
		{
			error = "";
			if (entries == null || entries.Count == 0)
			{
				return true;
			}
			if (!EnsureLoaded() || itemDataType == null || remove == null)
			{
				error = "no ShipInv Remove";
				return false;
			}
			ShipInventoryMergeSuppression.Enter();
			try
			{
				Array array = Array.CreateInstance(itemDataType, entries.Count);
				for (int i = 0; i < entries.Count; i++)
				{
					array.SetValue(entries[i], i);
				}
				remove.Invoke(null, new object[1] { array });
				return true;
			}
			catch (Exception ex)
			{
				error = ex.InnerException?.Message ?? ex.Message;
				return false;
			}
			finally
			{
				ShipInventoryMergeSuppression.Exit();
			}
		}

		internal static bool TryAddInventoryEntries(IReadOnlyList<object> entries, out string error, bool verifyCount = true)
		{
			error = "";
			if (entries == null || entries.Count == 0)
			{
				return true;
			}
			if (!EnsureLoaded() || itemDataType == null || add == null)
			{
				error = "no ShipInv Add";
				return false;
			}
			int num = (verifyCount ? GetInventoryItemCount() : (-1));
			try
			{
				Array array = Array.CreateInstance(itemDataType, entries.Count);
				for (int i = 0; i < entries.Count; i++)
				{
					array.SetValue(entries[i], i);
				}
				add.Invoke(null, new object[1] { array });
				if (verifyCount && num >= 0)
				{
					int num2 = num + entries.Count;
					int inventoryItemCount = GetInventoryItemCount();
					if (inventoryItemCount != num2)
					{
						object obj = InvBehaviour();
						if (obj != null && addRpc != null)
						{
							RpcDefaults(addRpc, obj, array);
							inventoryItemCount = GetInventoryItemCount();
						}
					}
					if (inventoryItemCount != num2)
					{
						error = $"inv count {num}->{inventoryItemCount}";
						return false;
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				error = ex.InnerException?.Message ?? ex.Message;
				return false;
			}
		}

		[IteratorStateMachine(typeof(<EnumerateInventoryScrapRows>d__23))]
		internal static IEnumerable<(string groupKey, string displayName, int scrapValue)> EnumerateInventoryScrapRows()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <EnumerateInventoryScrapRows>d__23(-2);
		}

		private static bool TryInspect(object boxed, out string groupKey, out string displayName, out int scrapValue)
		{
			groupKey = (displayName = "");
			scrapValue = 0;
			if (!EnsureLoaded() || itemDataType == null || scrap == null || getItem == null)
			{
				return false;
			}
			if (!itemDataType.IsInstanceOfType(boxed))
			{
				return false;
			}
			object obj;
			try
			{
				obj = getItem.Invoke(boxed, null);
			}
			catch
			{
				return false;
			}
			if (obj == null)
			{
				return false;
			}
			Type type = obj.GetType();
			if (!IsScrap(type, obj))
			{
				return false;
			}
			int num = scrap(boxed);
			displayName = (GetPropertyOrField(type, obj, "itemName") as string) ?? "?";
			object? propertyOrField = GetPropertyOrField(type, obj, "spawnPrefab");
			GameObject val = (GameObject)((propertyOrField is GameObject) ? propertyOrField : null);
			groupKey = ((val != null) ? ((Object)val).name.Replace("(Clone)", "", StringComparison.Ordinal) : displayName);
			if (string.IsNullOrEmpty(groupKey))
			{
				groupKey = displayName;
			}
			scrapValue = num;
			return true;
		}

		private static bool EnsureLoaded()
		{
			if (ok)
			{
				return true;
			}
			try
			{
				if (asm == null)
				{
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					foreach (Assembly assembly in assemblies)
					{
						if (assembly.GetName().Name == "ShipInventoryUpdated")
						{
							asm = assembly;
							break;
						}
					}
				}
				if (asm == null)
				{
					return false;
				}
				if ((object)itemDataType == null)
				{
					itemDataType = asm.GetType("ShipInventoryUpdated.Objects.ItemData");
				}
				if ((object)inventoryType == null)
				{
					inventoryType = asm.GetType("ShipInventoryUpdated.Scripts.Inventory");
				}
				if (itemDataType == null || (getItem = itemDataType.GetMethod("GetItem", Type.EmptyTypes)) == null)
				{
					return false;
				}
				if (inventoryType != null)
				{
					Type type = itemDataType.MakeArrayType();
					if ((object)remove == null)
					{
						remove = inventoryType.GetMethod("Remove", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { type }, null);
					}
					if ((object)add == null)
					{
						add = inventoryType.GetMethod("Add", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { type }, null);
					}
					if ((object)addRpc == null)
					{
						addRpc = FindRpc(inventoryType, "AddServerRpc", type);
					}
				}
				if (scrap == null)
				{
					Type type2 = itemDataType;
					PropertyInfo prop = type2.GetProperty("SCRAP_VALUE", BindingFlags.Instance | BindingFlags.Public);
					FieldInfo fld = ((prop == null) ? type2.GetField("SCRAP_VALUE", BindingFlags.Instance | BindingFlags.Public) : null);
					if (prop != null)
					{
						scrap = (object b) => Convert.ToInt32(prop.GetValue(b) ?? ((object)0));
					}
					else
					{
						if (!(fld != null))
						{
							return false;
						}
						scrap = (object b) => Convert.ToInt32(fld.GetValue(b) ?? ((object)0));
					}
				}
				ok = true;
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static MethodInfo? FindRpc(Type t, string name, Type arg0)
		{
			MethodInfo[] methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != name))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length != 0 && parameters[0].ParameterType == arg0)
					{
						return methodInfo;
					}
				}
			}
			return null;
		}

		private static void RpcDefaults(MethodInfo m, object target, object first)
		{
			ParameterInfo[] parameters = m.GetParameters();
			object[] array = new object[parameters.Length];
			array[0] = first;
			for (int i = 1; i < parameters.Length; i++)
			{
				array[i] = (parameters[i].HasDefaultValue ? parameters[i].DefaultValue : Activator.CreateInstance(parameters[i].ParameterType));
			}
			m.Invoke(target, array);
		}
	}
	internal static class TerminalType
	{
		private static readonly Lazy<Type> Lazy = new Lazy<Type>(delegate
		{
			Type? type = typeof(StartOfRound).Assembly.GetType("Terminal");
			if (type == null)
			{
				throw new InvalidOperationException("no Terminal type");
			}
			return type;
		});

		internal static Type Value => Lazy.Value;
	}
	internal static class NormCmd
	{
		internal static string Strip(string s)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char c in s)
			{
				if (!char.IsPunctuation(c))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString().ToLowerInvariant();
		}
	}
	internal static class ScreenNode
	{
		internal static TerminalNode Make(string text, int maxChars = 80)
		{
			TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>();
			obj.displayText = text;
			obj.clearPreviousText = true;
			obj.maxCharactersToType = maxChars;
			return obj;
		}
	}
	internal static class TerminalReflection
	{
		private static readonly FieldInfo? ScreenTextField;

		private static readonly PropertyInfo? ScreenTextTextProperty;

		private static readonly FieldInfo? TextAddedField;

		private static readonly FieldInfo? CurrentTextField;

		private static readonly FieldInfo? ModifyingTextField;

		private static int inputLockDepth;

		private static bool lockCaretSaved;

		private static Color lockCaret;

		private static Type? terminalNodeType;

		private static Type TerminalNodeType
		{
			get
			{
				object obj = terminalNodeType;
				if (obj == null)
				{
					obj = typeof(StartOfRound).Assembly.GetType("TerminalNode") ?? throw new InvalidOperationException("no TerminalNode");
					terminalNodeType = (Type?)obj;
				}
				return (Type)obj;
			}
		}

		static TerminalReflection()
		{
			Type value = TerminalType.Value;
			ScreenTextField = value.GetField("screenText", BindingFlags.Instance | BindingFlags.Public);
			TextAddedField = value.GetField("textAdded", BindingFlags.Instance | BindingFlags.Public);
			CurrentTextField = value.GetField("currentText", BindingFlags.Instance | BindingFlags.Public);
			ModifyingTextField = value.GetField("modifyingText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			Type type = ScreenTextField?.FieldType;
			if (type != null)
			{
				ScreenTextTextProperty = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
			}
		}

		private static object? ScreenOf(object term)
		{
			return ScreenTextField?.GetValue(term);
		}

		internal static object? GetScreenTextObject(object term)
		{
			return ScreenTextField?.GetValue(term);
		}

		internal static void SetScreenBody(object term, string body)
		{
			ModifyingTextField?.SetValue(term, true);
			object obj = ScreenOf(term);
			if (obj != null && !(ScreenTextTextProperty == null))
			{
				ScreenTextTextProperty.SetValue(obj, body);
				CurrentTextField?.SetValue(term, body);
				TextAddedField?.SetValue(term, 0);
			}
		}

		internal static void LockTermField(object term, bool on)
		{
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			object obj = ScreenOf(term);
			if (obj == null)
			{
				return;
			}
			Type type = obj.GetType();
			PropertyInfo property = type.GetProperty("readOnly", BindingFlags.Instance | BindingFlags.Public);
			PropertyInfo property2 = type.GetProperty("interactable", BindingFlags.Instance | BindingFlags.Public);
			PropertyInfo property3 = type.GetProperty("caretColor", BindingFlags.Instance | BindingFlags.Public);
			if (on)
			{
				if (inputLockDepth == 0)
				{
					if (property3?.GetValue(obj) is Color val)
					{
						lockCaret = val;
						lockCaretSaved = true;
					}
					else
					{
						lockCaretSaved = false;
					}
				}
				inputLockDepth++;
				type.GetMethod("ReleaseSelection", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null);
				type.GetMethod("DeactivateInputField", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null);
				property?.SetValue(obj, true);
				property2?.SetValue(obj, false);
				property3?.SetValue(obj, (object)new Color(0f, 0f, 0f, 0f));
				return;
			}
			if (inputLockDepth > 0)
			{
				inputLockDepth--;
			}
			if (inputLockDepth == 0)
			{
				if (lockCaretSaved)
				{
					property3?.SetValue(obj, lockCaret);
				}
				lockCaretSaved = false;
				property?.SetValue(obj, false);
				property2?.SetValue(obj, true);
			}
		}

		internal static void LoadNewNode(object term, object node)
		{
			MethodInfo methodInfo = null;
			MethodInfo[] methods = TerminalType.Value.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (!(methodInfo2.Name != "LoadNewNode") && methodInfo2.GetParameters().Length == 1 && methodInfo2.GetParameters()[0].ParameterType.IsAssignableFrom(TerminalNodeType))
				{
					methodInfo = methodInfo2;
					break;
				}
			}
			methodInfo?.Invoke(term, new object[1] { node });
		}

		internal static bool GetTerminalInUse(object term)
		{
			Type value = TerminalType.Value;
			object obj = value.GetField("terminalInUse", BindingFlags.Instance | BindingFlags.Public)?.GetValue(term);
			if (obj is bool)
			{
				return (bool)obj;
			}
			obj = value.GetProperty("terminalInUse", BindingFlags.Instance | BindingFlags.Public)?.GetValue(term);
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static void ActivateInput(object term)
		{
			object obj = ScreenOf(term);
			if (obj != null)
			{
				Type type = obj.GetType();
				type.GetMethod("ActivateInputField", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null);
				type.GetMethod("Select", BindingFlags.Instance | BindingFlags.Public)?.Invoke(obj, null);
			}
		}

		internal static int GetTextAdded(object term)
		{
			object obj = TextAddedField?.GetValue(term);
			if (obj is int)
			{
				return (int)obj;
			}
			obj = TerminalType.Value.GetProperty("textAdded", BindingFlags.Instance | BindingFlags.Public)?.GetValue(term);
			if (obj is int)
			{
				return (int)obj;
			}
			return 0;
		}

		internal static string GetRawInputSuffixForItapi(object term)
		{
			object obj = ScreenOf(term);
			if (obj == null || !(ScreenTextTextProperty?.GetValue(obj) is string text))
			{
				return "";
			}
			int textAdded = GetTextAdded(term);
			if (textAdded > 0 && textAdded <= text.Length)
			{
				return text.Substring(text.Length - textAdded);
			}
			return "";
		}

		internal static bool TryGetInputChunk(object term, out string chunk)
		{
			chunk = "";
			object obj = ScreenOf(term);
			if (obj == null || !(ScreenTextTextProperty?.GetValue(obj) is string text))
			{
				return false;
			}
			int textAdded = GetTextAdded(term);
			if (textAdded <= 0 || textAdded > text.Length)
			{
				return false;
			}
			chunk = text.Substring(text.Length - textAdded);
			return true;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MergeScrap";

		public const string PLUGIN_NAME = "MergeScrap";

		public const string PLUGIN_VERSION = "1.1.1";
	}
}