Decompiled source of Scrap Magic v1.0.1

kylethescientist.scrapmagic.dll

Decompiled 5 hours 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.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("ClientNetworkTransform")]
[assembly: IgnoresAccessChecksTo("DissonanceVoip")]
[assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Jobs")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")]
[assembly: IgnoresAccessChecksTo("Unity.Services.QoS")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Relay")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: AssemblyCompany("kylethescientist.scrapmagic")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("A client-sided mod for organizing and selling items ")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ScrapMagic")]
[assembly: AssemblyTitle("kylethescientist.scrapmagic")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ScrapMagic
{
	public abstract class Argument
	{
		public string name;
	}
	public class Argument<T> : Argument
	{
		public T value;

		public Argument(string name, T value)
		{
			base.name = name;
			this.value = value;
		}

		public static implicit operator T(Argument<T> arg)
		{
			return arg.value;
		}
	}
	public struct ChatArgs
	{
		public Argument[] arguments;

		public string help;

		public int Length => arguments.Length;

		public bool Empty => arguments.Length == 0;

		public bool this[string name] => Get<bool>(name);

		public string this[int name] => Get<string>(name);

		public T Get<T>(string name)
		{
			Argument[] array = arguments;
			foreach (Argument argument in array)
			{
				if (argument.name == name)
				{
					return ((Argument<T>)argument).value;
				}
			}
			return default(T);
		}

		public T Get<T>(int name)
		{
			Argument[] array = arguments;
			foreach (Argument argument in array)
			{
				if (argument.name == name.ToString())
				{
					return ((Argument<T>)argument).value;
				}
			}
			return default(T);
		}
	}
	public class ChatCommand
	{
		public static List<ChatCommand> Commands = new List<ChatCommand>();

		public string keyword;

		public Action<ChatArgs> action;

		public string help;

		public ChatCommand(string keyword, string help, Action<ChatArgs> action)
		{
			this.keyword = keyword;
			this.help = help;
			this.action = action;
		}

		public static ChatCommand New(string keyword, string help, Action<ChatArgs> action)
		{
			ChatCommand chatCommand = new ChatCommand(keyword, help, action);
			Commands.Add(chatCommand);
			return chatCommand;
		}

		public ChatArgs GetArgs(string raw)
		{
			List<Argument> list = new List<Argument>();
			string[] array = raw.Split(' ');
			keyword = array[0];
			for (int i = 1; i < array.Length; i++)
			{
				if (array[i].StartsWith("-"))
				{
					string name = array[i].Substring(1);
					list.Add(new Argument<bool>(name, value: true));
				}
				else
				{
					list.Add(new Argument<string>((i - 1).ToString(), array[i]));
				}
			}
			ChatArgs result = default(ChatArgs);
			result.arguments = list.ToArray();
			result.help = help;
			return result;
		}

		public override string ToString()
		{
			return keyword;
		}
	}
	public static class Extensions
	{
		private static Dictionary<string, Vector3> itemOffsets = new Dictionary<string, Vector3>
		{
			{
				"weed_killer",
				Vector3.forward * 0.5f
			},
			{
				"stun_grenade",
				Vector3.forward * 0.05f
			},
			{
				"walkie_talkie",
				Vector3.forward * 0.025f
			},
			{
				"spray_paint",
				Vector3.forward * 0.05f
			},
			{
				"belt_bag",
				Vector3.forward * 0.05f + Vector3.right * 0.01f
			},
			{
				"zap_gun",
				Vector3.right * 0.25f
			},
			{
				"jetpack",
				Vector3.forward * 0.35f + Vector3.up * -0.6f
			}
		};

		public static string Name(this GrabbableObject item)
		{
			return item.itemProperties.Name();
		}

		public static string Name(this Item item)
		{
			return item.itemName.ToLower().Replace(" ", "_").Replace("-", "_");
		}

		public static float AvgValue(this GrabbableObject item)
		{
			return (item.itemProperties.maxValue + item.itemProperties.minValue) / 2;
		}

		public static Vector3 PlacementOffset(this GrabbableObject item)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			return itemOffsets.GetValueOrDefault(item.Name());
		}

		public static string AddToList(this string s, string t)
		{
			t = t.Trim().ToLower().Replace(" ", "_")
				.Replace("-", "_");
			List<string> list = (from s in s.Split(',')
				select s.Trim()).ToList();
			list.Add(t);
			return string.Join(", ", list);
		}

		public static string RemoveFromList(this string s, string t)
		{
			t = t.Trim().ToLower().Replace(" ", "_")
				.Replace("-", "_");
			List<string> list = (from s in s.Split(',')
				select s.Trim()).ToList();
			list.Remove(t);
			return string.Join(", ", list);
		}

		public static bool ContainsItem(this string s, string t)
		{
			t = t.Trim().ToLower().Replace(" ", "_")
				.Replace("-", "_");
			List<string> list = (from s in s.Split(',')
				select s.Trim()).ToList();
			return list.Contains(t);
		}

		public static GrabbableObject[] ItemsOnCounter(this DepositItemsDesk desk)
		{
			return ((Component)desk).GetComponentsInChildren<GrabbableObject>();
		}
	}
	public class Log
	{
		private static ManualLogSource _log;

		public static void Init(ManualLogSource log)
		{
			_log = log;
		}

		private static string JoinObjects(params object[] objects)
		{
			string text = "";
			foreach (object obj in objects)
			{
				text = text + obj.ToString() + " ";
			}
			return text;
		}

		public static void NotifyPlayer(string header, string body = "", bool isWarning = false)
		{
			HUDManager.Instance.DisplayTip(header, body, isWarning, false, "LC_Tip1");
			Debug(header);
			Debug(body);
		}

		public static void Chat(string body, string color = "FFFFFF")
		{
			HUDManager.Instance.AddChatMessage("<color=#" + color + ">[SM] " + body + "</color>", "");
			Debug(body);
			NeutralSound();
		}

		public static void ConfirmSound()
		{
			HUDManager.Instance.UIAudio.PlayOneShot(GameNetworkManager.Instance.buttonTuneSFX);
		}

		public static void ErrorSound()
		{
			HUDManager.Instance.UIAudio.PlayOneShot(GameNetworkManager.Instance.buttonSelectSFX);
		}

		public static void NeutralSound()
		{
			HUDManager.Instance.UIAudio.PlayOneShot(GameNetworkManager.Instance.buttonCancelSFX);
		}

		public static void PlaySound(int sound)
		{
			HUDManager instance = HUDManager.Instance;
			AudioSource uIAudio = instance.UIAudio;
			GameNetworkManager instance2 = GameNetworkManager.Instance;
			switch (sound)
			{
			case 0:
				uIAudio.PlayOneShot(instance2.buttonSelectSFX);
				break;
			case 1:
				uIAudio.PlayOneShot(instance2.buttonCancelSFX);
				break;
			case 2:
				uIAudio.PlayOneShot(instance2.buttonPressSFX);
				break;
			case 3:
				uIAudio.PlayOneShot(instance2.buttonTuneSFX);
				break;
			case 4:
				uIAudio.PlayOneShot(instance.addToScrapTotalSFX);
				break;
			case 5:
				uIAudio.PlayOneShot(instance.decreaseXPSFX);
				break;
			case 6:
				uIAudio.PlayOneShot(instance.displayCollectedScrapSFX);
				break;
			case 7:
				uIAudio.PlayOneShot(instance.displayCollectedScrapSFXSmall);
				break;
			case 8:
				uIAudio.PlayOneShot(instance.finishAddingToTotalSFX);
				break;
			case 9:
				uIAudio.PlayOneShot(instance.globalNotificationSFX);
				break;
			case 10:
				uIAudio.PlayOneShot(instance.increaseXPSFX);
				break;
			case 11:
				uIAudio.PlayOneShot(instance.levelDecreaseSFX);
				break;
			case 12:
				uIAudio.PlayOneShot(instance.levelIncreaseSFX);
				break;
			case 13:
				uIAudio.PlayOneShot(instance.newProfitQuotaSFX);
				break;
			case 14:
				uIAudio.PlayOneShot(instance.OneDayToMeetQuotaSFX);
				break;
			case 15:
				uIAudio.PlayOneShot(instance.profitQuotaDaysLeftCalmSFX);
				break;
			case 16:
				uIAudio.PlayOneShot(instance.reachedQuotaSFX);
				break;
			case 17:
				uIAudio.PlayOneShot(instance.scanSFX);
				break;
			}
		}

		public static void Exception(Exception e)
		{
			string message = e.Message;
			string stackTrace = e.StackTrace;
			_log.LogError((object)message);
			_log.LogError((object)stackTrace);
		}

		public static void Fatal(params object[] objects)
		{
			_log.LogFatal((object)JoinObjects(objects));
		}

		public static void Error(params object[] objects)
		{
			_log.LogError((object)JoinObjects(objects));
		}

		public static void Warning(params object[] objects)
		{
			_log.LogWarning((object)JoinObjects(objects));
		}

		public static void Message(params object[] objects)
		{
			_log.LogMessage((object)JoinObjects(objects));
		}

		public static void Info(params object[] objects)
		{
			_log.LogInfo((object)JoinObjects(objects));
		}

		public static void Debug(params object[] objects)
		{
			_log.LogDebug((object)JoinObjects(objects));
		}
	}
	public static class Patches
	{
		public static void Init()
		{
			Harmony.CreateAndPatchAll(typeof(Ship), (string)null);
			Harmony.CreateAndPatchAll(typeof(Chat), (string)null);
			Harmony.CreateAndPatchAll(typeof(Startup), (string)null);
		}
	}
	public static class Startup
	{
		[HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")]
		[HarmonyPostfix]
		private static void OnLocalPlayerCreated(PlayerControllerB __instance)
		{
			((Component)__instance).gameObject.AddComponent<Sorter>();
			((Component)__instance).gameObject.AddComponent<Seller>();
		}

		[HarmonyPatch(typeof(GrabbableObject), "OnHitGround")]
		[HarmonyPostfix]
		private static void OnItemHitGround(GrabbableObject __instance)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (Sorter.inProgress)
			{
				__instance.floorYRot = -1;
				((Component)__instance).transform.eulerAngles = Vector3.zero;
			}
		}
	}
	public static class Ship
	{
		public static Action OnShipOrbit;

		public static Action OnShipTouchdown;

		public static Action OnShipAscent;

		public static Action OnShipDescent;

		public static bool InOrbit { get; private set; }

		public static bool Stationary => InOrbit || StartOfRound.Instance.shipHasLanded;

		[HarmonyPatch(typeof(StartOfRound), "SwitchMapMonitorPurpose")]
		[HarmonyPostfix]
		[HarmonyWrapSafe]
		public static void OnShipStateChanged(bool displayInfo)
		{
			InOrbit = displayInfo;
			if (displayInfo)
			{
				OnShipOrbit?.Invoke();
			}
			else
			{
				OnShipDescent?.Invoke();
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "ShipLeave")]
		[HarmonyPostfix]
		[HarmonyWrapSafe]
		public static void OnShipLeave()
		{
			OnShipAscent?.Invoke();
		}

		[HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")]
		[HarmonyPostfix]
		[HarmonyWrapSafe]
		public static void OnShipLanded()
		{
			OnShipTouchdown?.Invoke();
		}
	}
	public static class Chat
	{
		[HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")]
		[HarmonyPrefix]
		[HarmonyWrapSafe]
		public static bool OnChatSubmit(HUDManager __instance, CallbackContext context)
		{
			if (!((CallbackContext)(ref context)).performed)
			{
				return true;
			}
			string text = __instance.chatTextField.text;
			if (text == "/help")
			{
				string text2 = "Commands: ";
				text2 += string.Join(", ", ChatCommand.Commands);
				Log.Chat(text2);
				CloseChat(__instance);
			}
			text = text.Substring(1).Trim();
			foreach (ChatCommand command in ChatCommand.Commands)
			{
				if (text.StartsWith(command.keyword))
				{
					CloseChat(__instance);
					try
					{
						command.action(command.GetArgs(text));
					}
					catch (Exception e)
					{
						Log.Exception(e);
					}
					return false;
				}
			}
			return true;
		}

		public static void CloseChat(HUDManager instance)
		{
			instance.localPlayer.isTypingChat = false;
			instance.chatTextField.text = "";
			EventSystem.current.SetSelectedGameObject((GameObject)null);
			((Behaviour)instance.typingIndicator).enabled = false;
		}

		public static void Reset()
		{
			ChatCommand.Commands.Clear();
		}

		[HarmonyPatch(typeof(HUDManager), "Start")]
		[HarmonyPostfix]
		public static void OnHUDStart(HUDManager __instance)
		{
			try
			{
				Log.Info("Hud started");
				((TMP_Text)__instance.chatText).fontSize = 11f;
			}
			catch (Exception e)
			{
				Log.Exception(e);
			}
		}
	}
	public static class Player
	{
		public static PlayerControllerB Local => StartOfRound.Instance?.localPlayerController;

		public static bool CanGrabObject(GrabbableObject item)
		{
			List<GrabbableObject> list = Seller.desk?.itemsOnCounter;
			if (!Object.op_Implicit((Object)(object)item) || !item.grabbable || item.deactivated || item.isHeld || item.isPocketed || (list != null && list.Contains(item)))
			{
				Log.Debug("  Can't grab object");
				return false;
			}
			if (Local.isPlayerDead || Local.isTypingChat || Local.inTerminalMenu || Local.throwingObject || Local.IsInspectingItem || Local.isGrabbingObjectAnimation || Object.op_Implicit((Object)(object)Local.inAnimationWithEnemy) || Local.inSpecialInteractAnimation || Local.jetpackControls || Local.disablingJetpackControls || Local.activatingItem || Local.waitingToDropItem || Local.FirstEmptyItemSlot() == -1)
			{
				Log.Debug("  Player is busy");
				return false;
			}
			return true;
		}

		public static IEnumerator DefuseEgg(StunGrenadeItem egg)
		{
			if (Object.op_Implicit((Object)(object)egg))
			{
				if (egg.explodeOnThrow)
				{
					Local.SwitchToItemSlot(Local.NextItemSlot(true), (GrabbableObject)null);
					yield return (object)new WaitForSeconds(0.5f);
					Local.SwitchToItemSlot(Local.NextItemSlot(false), (GrabbableObject)null);
				}
				int retry = 20;
				while (egg.explodeOnThrow && retry > 0)
				{
					Local.SwitchToItemSlot(Local.NextItemSlot(true), (GrabbableObject)null);
					yield return (object)new WaitForSeconds(0.5f);
					Local.SwitchToItemSlot(Local.NextItemSlot(false), (GrabbableObject)null);
					Log.Chat("Egg will pop if placed! Stand somewhere else!");
					retry--;
				}
				if (retry == 0)
				{
					Log.Chat("Skipping egg.");
				}
			}
		}

		public static IEnumerator StartGrabbingObject(GrabbableObject grabbableObject)
		{
			Log.Debug("Grabbing " + grabbableObject.itemProperties.itemName);
			if (CanGrabObject(grabbableObject))
			{
				Local.currentlyGrabbingObject = grabbableObject;
				Local.grabInvalidated = false;
				Local.currentlyGrabbingObject.InteractItem();
				Local.playerBodyAnimator.SetBool("GrabInvalidated", false);
				Local.playerBodyAnimator.SetBool("GrabValidated", false);
				Local.playerBodyAnimator.SetBool("cancelHolding", false);
				Local.playerBodyAnimator.ResetTrigger("Throw");
				Local.SetSpecialGrabAnimationBool(true, (GrabbableObject)null);
				Local.isGrabbingObjectAnimation = true;
				((Behaviour)Local.cursorIcon).enabled = false;
				((TMP_Text)Local.cursorTip).text = "";
				Local.twoHanded = Local.currentlyGrabbingObject.itemProperties.twoHanded;
				Local.carryWeight = Mathf.Clamp(Local.carryWeight + (Local.currentlyGrabbingObject.itemProperties.weight - 1f), 1f, 10f);
				if (Local.currentlyGrabbingObject.itemProperties.grabAnimationTime > 0f)
				{
					Local.grabObjectAnimationTime = Local.currentlyGrabbingObject.itemProperties.grabAnimationTime;
				}
				else
				{
					Local.grabObjectAnimationTime = 0.4f;
				}
				if (Local.grabObjectCoroutine != null)
				{
					((MonoBehaviour)Local).StopCoroutine(Local.grabObjectCoroutine);
				}
				Local.GrabObjectServerRpc(NetworkObjectReference.op_Implicit(((NetworkBehaviour)grabbableObject).NetworkObject));
				yield return Local.GrabObject();
			}
		}

		public static IEnumerator StartMovingObject(GrabbableObject item, Vector3 position, NetworkObject parent = null)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			yield return StartGrabbingObject(item);
			Log.Debug($"Moving {item.itemProperties.itemName} to {position}");
			yield return DefuseEgg(((Component)item).GetComponent<StunGrenadeItem>());
			try
			{
				Log.Debug("Placing " + item.itemProperties.itemName);
				item.floorYRot = -1;
				Local.DiscardHeldObject(true, parent, position, false);
			}
			catch (Exception e)
			{
				Log.Exception(e);
			}
		}
	}
	[BepInPlugin("kylethescientist.scrapmagic", "ScrapMagic", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public static ConfigFile config;

		private static Harmony harmony;

		public static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			Instance = this;
			config = new ConfigFile(Paths.ConfigPath + "\\kylethescientist.scrapmagic.cfg", true);
			Log.Init(((BaseUnityPlugin)this).Logger);
			Patches.Init();
			Log.Info("kylethescientist.scrapmagic v1.0.0 has loaded!");
		}
	}
	public class Seller : MonoBehaviour
	{
		public ConfigEntry<string> skippedItems;

		public static DepositItemsDesk desk;

		private InteractTrigger bell;

		private List<GrabbableObject> toSell;

		public static bool InProgress { get; private set; }

		private void FindDesk()
		{
			if (!Object.op_Implicit((Object)(object)desk))
			{
				desk = Object.FindObjectOfType<DepositItemsDesk>();
			}
		}

		private void Awake()
		{
			Log.Info("Initializing Seller");
			skippedItems = Plugin.config.Bind<string>("Seller", "skippedItems", "body, knife, shotgun, zed_dog", "Which items should be skipped when selling");
			Ship.OnShipDescent = (Action)Delegate.Combine(Ship.OnShipDescent, new Action(FindDesk));
			ChatCommand.New("sell", "Usage: /sell <$amount|all|quota|skip|unskip>", Sell);
			ChatCommand.New("ring", "Rings the company bell until the door opens", delegate
			{
				if (!InProgress)
				{
					((MonoBehaviour)this).StartCoroutine(OpenDoor());
				}
				else
				{
					Log.NotifyPlayer("Error", "Selling in progress. Press escape to cancel.");
				}
			});
			Log.Info("Seller initialized");
		}

		private void Update()
		{
			if (InProgress && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame)
			{
				InProgress = false;
				Log.ErrorSound();
			}
		}

		private void Configure(ChatArgs args)
		{
			if (args.Empty || args.Length == 1)
			{
				Log.Chat("", "FFFF00");
				Log.NeutralSound();
			}
			else if (args[0] == "skip")
			{
				Skip(args[1]);
			}
			else if (args[0] == "unskip")
			{
				Unskip(args[1]);
			}
		}

		private void Skip(string name)
		{
			if (skippedItems.Value.ContainsItem(name))
			{
				Log.Chat("Item " + name + " already skipped", "FFFF00");
				Log.NeutralSound();
			}
			else
			{
				skippedItems.Value = skippedItems.Value.AddToList(name);
				Log.Chat("Skipping " + name, "FFFF00");
				Log.ConfirmSound();
			}
		}

		private void Unskip(string name)
		{
			if (!skippedItems.Value.ContainsItem(name))
			{
				Log.Chat("Item " + name + " not skipped", "FFFF00");
				Log.NeutralSound();
			}
			else
			{
				skippedItems.Value = skippedItems.Value.RemoveFromList(name);
				Log.Chat("Unskipping " + name, "FFFF00");
				Log.ConfirmSound();
			}
		}

		private void Sell(ChatArgs args)
		{
			if (!((NetworkBehaviour)Player.Local).IsServer)
			{
				Log.Chat("This command is for hosts only", "FF0000");
				Log.ErrorSound();
				return;
			}
			if (args.Empty || args[0] == "help")
			{
				Log.Chat(args.help, "FFFF00");
				Log.NeutralSound();
				return;
			}
			if (args[0] == "skip" || args[0] == "unskip")
			{
				Configure(args);
				return;
			}
			if (args[0] == "quota")
			{
				Sell(-1);
				return;
			}
			if (args[0] == "all")
			{
				Sell(-2);
				return;
			}
			string text = args[0].ToLower();
			int num = 1;
			if (text.EndsWith('k'))
			{
				num = 1000;
				text = text.Substring(0, text.Length - 1);
			}
			if (!int.TryParse(text, out var result))
			{
				Log.NotifyPlayer("Error", "Amount must be a number");
				Log.ErrorSound();
			}
			else if (result <= 0)
			{
				Log.NotifyPlayer("Error", "Amount must be greater than 0");
				Log.ErrorSound();
			}
			else
			{
				Sell(result * num);
			}
		}

		private void Sell(int amount)
		{
			if (InProgress)
			{
				Log.NotifyPlayer("Seller Error", "Selling in progress. Press escape to cancel.", isWarning: true);
				return;
			}
			if (!Object.op_Implicit((Object)(object)bell))
			{
				GameObject obj = GameObject.Find("BellDinger/Trigger");
				bell = ((obj != null) ? obj.GetComponent<InteractTrigger>() : null);
			}
			if (!Object.op_Implicit((Object)(object)desk) || !Object.op_Implicit((Object)(object)bell))
			{
				Log.NotifyPlayer("Seller Error", "Not at the company", isWarning: true);
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(SellTo(amount));
			}
		}

		public IEnumerator SellTo(int total)
		{
			InProgress = true;
			if (!Object.op_Implicit((Object)(object)desk) || !Object.op_Implicit((Object)(object)bell))
			{
				InProgress = false;
				yield break;
			}
			yield return GetItemsToFillQuota(total);
			if (toSell.Count == 0)
			{
				InProgress = false;
				yield break;
			}
			int counter = 0;
			foreach (GrabbableObject item in toSell)
			{
				counter++;
				if (!InProgress)
				{
					Log.Chat("Selling cancelled", "FF0000");
					yield break;
				}
				if (item.isHeld)
				{
					continue;
				}
				yield return Player.StartGrabbingObject(item);
				while (true)
				{
					yield return PlaceItemOnCounter(item);
					if (!item.isHeld)
					{
						break;
					}
					yield return (object)new WaitForSeconds(0.15f);
					Log.Warning("--- Failed to place item. Counter full?");
				}
			}
			yield return OpenDoor();
			InProgress = false;
			Player.Local.carryWeight = 1f;
		}

		private IEnumerator PlaceItemOnCounter(GrabbableObject item)
		{
			if ((Object)(object)Player.Local.currentlyHeldObjectServer != (Object)(object)item)
			{
				Log.Warning("Item was not picked up");
				yield break;
			}
			yield return Player.DefuseEgg(((Component)item).GetComponent<StunGrenadeItem>());
			desk.AddObjectToDeskServerRpc(NetworkObjectReference.op_Implicit(((Component)item).gameObject.GetComponent<NetworkObject>()));
			Vector3 vector2 = RoundManager.RandomPointInBounds(((Collider)desk.triggerCollider).bounds);
			Bounds bounds = ((Collider)desk.triggerCollider).bounds;
			vector2.y = ((Bounds)(ref bounds)).min.y;
			RaycastHit raycastHit = default(RaycastHit);
			if (Physics.Raycast(new Ray(vector2 + Vector3.up * 3f, Vector3.down), ref raycastHit, 8f, 1048640, (QueryTriggerInteraction)2))
			{
				vector2 = ((RaycastHit)(ref raycastHit)).point;
			}
			yield return Player.DefuseEgg(((Component)item).GetComponent<StunGrenadeItem>());
			vector2.y += item.itemProperties.verticalOffset;
			vector2 = ((Component)desk.deskObjectsContainer).transform.InverseTransformPoint(vector2);
			Player.Local.DiscardHeldObject(true, desk.deskObjectsContainer, vector2, false);
		}

		private IEnumerator OpenDoor()
		{
			while (!desk.doorOpen)
			{
				bell.Interact(((Component)Player.Local).transform);
				yield return (object)new WaitForSeconds(0.5f);
			}
			while (desk.doorOpen)
			{
				yield return (object)new WaitForSeconds(1f);
			}
		}

		public IEnumerator GetItemsToFillQuota(float quota)
		{
			float buyRate = StartOfRound.Instance.companyBuyingRate;
			bool sellingToQuota = quota == -1f;
			bool sellingAll = quota == -2f;
			toSell = new List<GrabbableObject>();
			float alreadySold = 0f;
			if (sellingToQuota)
			{
				quota = TimeOfDay.Instance.profitQuota;
				alreadySold += (float)TimeOfDay.Instance.quotaFulfilled;
				alreadySold += (float)desk.itemsOnCounter.Sum((GrabbableObject i) => i.scrapValue) * buyRate;
				if (alreadySold >= quota)
				{
					Log.NotifyPlayer("Done", "Quota already fulfilled");
					Log.ConfirmSound();
					if (desk.itemsOnCounter.Count > 0)
					{
						yield return OpenDoor();
					}
					yield break;
				}
			}
			GiftBoxItem[] boxes = Object.FindObjectsOfType<GiftBoxItem>();
			GiftBoxItem[] array = boxes;
			foreach (GiftBoxItem box in array)
			{
				if (!box.hasUsedGift && Object.op_Implicit((Object)(object)((Component)box).GetComponent<Renderer>()))
				{
					yield return Player.StartGrabbingObject((GrabbableObject)(object)box);
					((GrabbableObject)box).ItemActivate(true, true);
					yield return (object)new WaitForSeconds(0.2f);
				}
			}
			List<GrabbableObject> sellableItems = new List<GrabbableObject>();
			GrabbableObject[] onCounter = desk.ItemsOnCounter();
			GrabbableObject[] array2 = onCounter;
			foreach (GrabbableObject item in array2)
			{
				Log.Chat($"On counter: {item.Name()} ${item.scrapValue}");
			}
			GrabbableObject[] array3 = Object.FindObjectsOfType<GrabbableObject>();
			foreach (GrabbableObject obj in array3)
			{
				if (obj.itemProperties.isScrap && !obj.isHeld && !obj.deactivated && !((IEnumerable<GrabbableObject>)(object)boxes).Contains(obj) && !onCounter.Contains(obj) && !IsSkipped(obj.Name()))
				{
					sellableItems.Add(obj);
				}
			}
			sellableItems.Sort((GrabbableObject a, GrabbableObject b) => b.scrapValue.CompareTo(a.scrapValue));
			string sellable = string.Join("\n\t", sellableItems.Select((GrabbableObject i) => i.Name() + $" ${i.scrapValue}"));
			Log.Debug("Sellable items:\n" + sellable);
			if (sellingToQuota)
			{
				Log.Debug($"Quota: {quota}");
				if (buyRate != 1f)
				{
					quota = Mathf.CeilToInt(quota / buyRate);
					Log.Debug($"Adjusted quota: {quota}");
				}
				if (alreadySold > 0f)
				{
					Log.Debug($"Already sold: {alreadySold}");
				}
			}
			int target = (int)(quota - alreadySold);
			if (!sellingAll)
			{
				int sum = 0;
				int[] scrapValues = sellableItems.Select((GrabbableObject i) => i.scrapValue).ToArray();
				for (int j = 0; j < scrapValues.Length; j++)
				{
					if (sum >= target - 200)
					{
						break;
					}
					sum += scrapValues[j];
					toSell.Add(sellableItems[j]);
				}
				foreach (GrabbableObject item2 in toSell)
				{
					sellableItems.Remove(item2);
				}
				Log.Debug($"Pre-knapsack sum: {sum}/{target}, remaining to fill: {target - sum}");
				scrapValues = sellableItems.Select((GrabbableObject i) => i.scrapValue).ToArray();
				Task task = Task.Run(delegate
				{
					List<int> list = Knapsack(scrapValues, target - sum);
					if (list.Count == 0)
					{
						Log.NotifyPlayer("Sell Error", $"Only ${scrapValues.Sum()} available to sell", isWarning: true);
					}
					list.ForEach(delegate(int i)
					{
						toSell.Add(sellableItems[i]);
					});
				});
				Log.Chat("Calculating items to sell...", "FFFF00");
				yield return (object)new WaitUntil((Func<bool>)(() => task.IsCompleted));
			}
			else
			{
				toSell.AddRange(sellableItems);
			}
			if (toSell.Count == 0)
			{
				Log.NotifyPlayer("Error", "No items to sell");
				Log.ErrorSound();
				yield break;
			}
			float total = toSell.Sum((GrabbableObject i) => i.scrapValue);
			Log.NotifyPlayer($"Selling ${total}.", $"{toSell.Count} items");
			string items = string.Join(", ", toSell.Select((GrabbableObject i) => i.Name() + $" ${i.scrapValue}"));
			Log.Debug("Items to sell: " + items);
			Log.ConfirmSound();
		}

		public static List<int> Knapsack(int[] nums, int target)
		{
			Log.Debug("Knapsack: " + string.Join(", ", nums));
			int num = nums.Length;
			int num2 = nums.Sum();
			int[,] array = new int[num + 1, num2 + 1];
			int i;
			int j;
			for (i = 0; i <= num; i++)
			{
				for (j = 0; j <= num2; j++)
				{
					array[i, j] = 1000000000;
				}
			}
			array[0, 0] = 0;
			for (i = 1; i <= num; i++)
			{
				if (!InProgress)
				{
					return new List<int>();
				}
				for (j = 0; j <= num2; j++)
				{
					array[i, j] = array[i - 1, j];
					if (j >= nums[i - 1])
					{
						array[i, j] = Mathf.Min(array[i, j], array[i - 1, j - nums[i - 1]] + nums[i - 1]);
					}
				}
			}
			int num3 = 1000000000;
			int num4 = -1;
			for (j = target; j <= num2; j++)
			{
				if (array[num, j] >= target && array[num, j] < num3)
				{
					num3 = array[num, j];
					num4 = j;
				}
			}
			List<int> list = new List<int>();
			i = num;
			j = num4;
			while (i > 0 && j > 0)
			{
				if (array[i, j] != array[i - 1, j])
				{
					list.Add(i - 1);
					j -= nums[i - 1];
				}
				i--;
			}
			return list;
		}

		public bool IsSkipped(string s)
		{
			return skippedItems.Value.ContainsItem(s);
		}

		private void OnDestroy()
		{
			Ship.OnShipDescent = (Action)Delegate.Remove(Ship.OnShipDescent, new Action(FindDesk));
		}
	}
	public class Sorter : MonoBehaviour
	{
		public ConfigEntry<string> doorframeItems;

		public ConfigEntry<string> summonCircleItems;

		public ConfigEntry<string> skippedItems;

		public ConfigEntry<string> cupboardTop;

		public ConfigEntry<string> cupboardShelfA;

		public ConfigEntry<string> cupboardShelfB;

		public ConfigEntry<string> cupboardShelfC;

		public ConfigEntry<string> cupboardShelfD;

		public ConfigEntry<bool> fixedLayout;

		private List<Item> allScrap;

		private List<GrabbableObject> scrap;

		private List<GrabbableObject> cupboardT;

		private List<GrabbableObject> cupboardA;

		private List<GrabbableObject> cupboardB;

		private List<GrabbableObject> cupboardC;

		private List<GrabbableObject> cupboardD;

		private List<GrabbableObject> summonCircle;

		private List<GrabbableObject> doorframe;

		public static bool inProgress;

		private bool force = false;

		private Vector3 originPosition = new Vector3(-5.8f, 0.5f, -5f);

		private Vector3 duckCenter = new Vector3(7f, 0.01f, -6.78f);

		private Vector3 shelfMinPosition = new Vector3(-6.78f, 4.23f, -8.62f);

		private Vector3 shelfMaxPosition = new Vector3(-6.82f, 4.354f, -4.97f);

		private bool CanSort => Ship.InOrbit || (Ship.Stationary && StartOfRound.Instance.currentLevel.PlanetName == "71 Gordion");

		private void Awake()
		{
			fixedLayout = Plugin.config.Bind<bool>("Sorter", "fixedLayout", false, "Whether each item will have a designated spot on the wall. This can reduce the amount of resorting that needs to be done upon obtaining a new item type");
			skippedItems = Plugin.config.Bind<string>("Sorter", "skippedItems", "body, clipboard, sticky_note, boombox", "Which items should be skipped when organizing");
			doorframeItems = Plugin.config.Bind<string>("Sorter", "doorframeItems", "apparatus, soccer_ball, whoopie_cushion", "Which items should be put on the doorframe");
			summonCircleItems = Plugin.config.Bind<string>("Sorter", "summonCircleItems", "", "Which items should be put in the summoning circle");
			cupboardTop = Plugin.config.Bind<string>("Sorter", "cupboardTop", "extension_ladder, radar_booster, jetpack", "Which items should be put on top of the cupboard");
			cupboardShelfA = Plugin.config.Bind<string>("Sorter", "cupboardShelfA", "kitchen_knife, shotgun, shovel", "Which items should be put on the cupboard's 1st shelf");
			cupboardShelfB = Plugin.config.Bind<string>("Sorter", "cupboardShelfB", "key, ammo, flashlight, pro_flashlight", "Which items should be put on the cupboard's 2st shelf");
			cupboardShelfC = Plugin.config.Bind<string>("Sorter", "cupboardShelfC", "belt_bag, stun_grenade, walkie_talkie, spray_paint", "Which items should be put on the cupboard's 3st shelf");
			cupboardShelfD = Plugin.config.Bind<string>("Sorter", "cupboardShelfD", "lockpicker, tzp_inhalant, weed_killer, zap_gun", "Which items should be put on the cupboard's 4st shelf");
			ChatCommand.New("sort", "Usage: /sort <scrap|cupboard|circle|doorframe|fixed|compact|skip|unskip> [-r|-redo] [help]", Organize);
			ChatCommand.New("pile", "Usage: /pile <item_name>", Pile);
		}

		private void Start()
		{
			GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
			foreach (GrabbableObject val in array)
			{
				val.isInShipRoom = true;
			}
		}

		private void Update()
		{
			if (inProgress && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame)
			{
				inProgress = false;
				Log.ErrorSound();
			}
		}

		private void Configure(ChatArgs args)
		{
			if (args.Empty || args.Length == 1)
			{
				Log.Chat("Usage: /sort <skip|unskip> <item_name>", "FFFF00");
				Log.NeutralSound();
			}
			else if (args[0] == "skip")
			{
				Skip(args[1]);
			}
			else if (args[0] == "unskip")
			{
				Unskip(args[1]);
			}
		}

		private void Skip(string name)
		{
			if (skippedItems.Value.ContainsItem(name))
			{
				Log.Chat("Item " + name + " already skipped", "FFFF00");
				Log.NeutralSound();
			}
			else
			{
				skippedItems.Value = skippedItems.Value.AddToList(name);
				Log.Chat("Skipping " + name, "FFFF00");
				Log.ConfirmSound();
			}
		}

		private void Unskip(string name)
		{
			if (!skippedItems.Value.ContainsItem(name))
			{
				Log.Chat("Item " + name + " not skipped", "FFFF00");
				Log.NeutralSound();
			}
			else
			{
				skippedItems.Value = skippedItems.Value.RemoveFromList(name);
				Log.Chat("Unskipping " + name, "FFFF00");
				Log.ConfirmSound();
			}
		}

		private void Organize(ChatArgs args)
		{
			if (!((NetworkBehaviour)Player.Local).IsServer)
			{
				Log.Chat("This command is for hosts only", "FF0000");
				Log.ErrorSound();
				return;
			}
			if (args[0] == "skip" || args[0] == "unskip")
			{
				Configure(args);
				return;
			}
			if (!CanSort)
			{
				Log.NotifyPlayer("Sorter Error", "Must be in orbit or stationary at company", isWarning: true);
				return;
			}
			if (inProgress)
			{
				Log.NotifyPlayer("Sorter Error", "Operation in progress", isWarning: true);
				return;
			}
			CategorizeItems();
			Log.ConfirmSound();
			if (args[0] == "help")
			{
				Log.Chat(args.help, "FFFF00");
				return;
			}
			if (args[0] == "fixed" || args[0] == "compact")
			{
				fixedLayout.Value = args[0] == "fixed";
				if (fixedLayout.Value)
				{
					Log.Chat("Fixed layout enabled", "FFFF00");
				}
				else
				{
					Log.Chat("Compact layout enabled", "FFFF00");
				}
				return;
			}
			force = args["r"] || args["redo"];
			bool flag = args[0] == null || args[0] == "scrap";
			bool flag2 = args[0] == null || args[0] == "cupboard";
			bool flag3 = args[0] == null || args[0] == "circle";
			bool flag4 = args[0] == null || args[0] == "doorframe";
			Log.Debug($"Organizing - Scrap: {flag}, Cupboard: {flag2}, Circle: {flag3}, Doorframe: {flag4}");
			((MonoBehaviour)this).StartCoroutine(Organize(flag, flag2, flag3, flag4));
		}

		private IEnumerator Organize(bool scrap, bool cupboard, bool circle, bool doorframe)
		{
			inProgress = true;
			Log.Chat("Press [Escape] to cancel sorting", "FFFF00");
			if (scrap && inProgress)
			{
				yield return Scrap();
			}
			if (doorframe && inProgress)
			{
				yield return Doorframe();
			}
			if (circle && inProgress)
			{
				yield return Circle();
			}
			if (cupboard && inProgress)
			{
				yield return Cupboard();
			}
			Log.NeutralSound();
			inProgress = false;
		}

		private IEnumerator Scrap()
		{
			inProgress = true;
			Transform parent = StartOfRound.Instance.elevatorTransform;
			Vector3 start = Vector3.zero;
			Vector3 shipOffset = parent.TransformPoint(originPosition) - ((Component)parent).transform.position;
			if (Player.Local.FirstEmptyItemSlot() < 0)
			{
				Log.NotifyPlayer("Sorter Error", "Inventory full", isWarning: true);
				inProgress = false;
				yield break;
			}
			new Vector2(0.8f, 0.8f);
			Dictionary<string, int> itemCounts = new Dictionary<string, int>();
			List<Item> propList = (fixedLayout.Value ? allScrap : scrap.Select((GrabbableObject i) => i.itemProperties).ToList());
			Dictionary<string, Vector2> layout = CreateScrapLayout(propList);
			foreach (GrabbableObject item in scrap)
			{
				itemCounts[item.Name()] = ((!itemCounts.ContainsKey(item.Name())) ? 1 : (itemCounts[item.Name()] + 1));
				if (ShouldBreak(item))
				{
					Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true);
					inProgress = false;
					yield break;
				}
				if (!layout.ContainsKey(item.Name()))
				{
					Log.Debug("No position for " + item.Name());
					continue;
				}
				Vector2 offset = layout[item.Name()];
				int gy = itemCounts[item.Name()] / 5;
				int gz = itemCounts[item.Name()] % 5;
				Vector3 position2 = start + new Vector3(offset.x, offset.y);
				position2 += new Vector3(0f, (float)gy * 0.05f, (float)gz * 0.05f);
				Vector3 worldPos = position2 + shipOffset + ((Component)parent).transform.position;
				if (!force && Vector3.Distance(worldPos, ((Component)item).transform.position) < 0.25f)
				{
					continue;
				}
				yield return GrabbableRetry(item);
				if (!ShouldSkip(item))
				{
					yield return Player.StartMovingObject(item, worldPos);
					int retry = 15;
					while (!Player.CanGrabObject(item) && retry > 0)
					{
						yield return (object)new WaitForEndOfFrame();
						retry--;
					}
				}
			}
		}

		private IEnumerator Circle()
		{
			foreach (GrabbableObject item in summonCircle)
			{
				if (ShouldBreak(item))
				{
					Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true);
					inProgress = false;
					yield break;
				}
				if (!Player.CanGrabObject(item) || !item.isInShipRoom)
				{
					continue;
				}
				float p = 0.5f;
				if (summonCircle.Count > 1)
				{
					p = (float)summonCircle.IndexOf(item) / (float)summonCircle.Count;
				}
				float diameter = 1f;
				float x = Mathf.Cos(p * MathF.PI * 2f) * diameter;
				float z = Mathf.Sin(p * MathF.PI * 2f) * diameter;
				Vector3 duckPos = duckCenter + new Vector3(x, 0f, z);
				Log.Debug($"Putting duck in a row. {duckPos}");
				Transform parent = StartOfRound.Instance.elevatorTransform;
				Vector3 worldPos = parent.TransformPoint(duckPos);
				if (force || !(Vector3.Distance(worldPos, ((Component)item).transform.position) < 0.05f))
				{
					yield return Player.StartMovingObject(item, worldPos);
					int retry = 15;
					while (!Player.CanGrabObject(item) && retry > 0)
					{
						yield return (object)new WaitForEndOfFrame();
						retry--;
					}
				}
			}
		}

		private IEnumerator Doorframe()
		{
			Dictionary<string, int> counts = CountGroups(doorframe);
			int seen = 0;
			string lastName = "";
			foreach (GrabbableObject item in doorframe)
			{
				if (ShouldBreak(item))
				{
					Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true);
					inProgress = false;
					yield break;
				}
				if (ShouldSkip(item))
				{
					continue;
				}
				if (item.Name() != lastName)
				{
					seen = 0;
					item.Name();
				}
				else
				{
					seen++;
				}
				lastName = item.Name();
				float p = 0.5f;
				int count = counts[item.Name()];
				if (count > 1)
				{
					p = (float)seen / (float)(count - 1);
				}
				Vector3 shelfPos = Vector3.Lerp(shelfMinPosition, shelfMaxPosition, p);
				Log.Debug($"Moving {((Object)item).name} to doorframe. {shelfPos}");
				Transform parent = StartOfRound.Instance.elevatorTransform;
				Vector3 worldPos = parent.TransformPoint(shelfPos);
				if (force || !(Vector3.Distance(worldPos, ((Component)item).transform.position) < 0.05f))
				{
					yield return Player.StartMovingObject(item, worldPos);
					int retry = 15;
					while (!Player.CanGrabObject(item) && retry > 0)
					{
						yield return (object)new WaitForEndOfFrame();
						retry--;
					}
				}
			}
		}

		public static Dictionary<string, int> CountGroups(List<GrabbableObject> items)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (GrabbableObject item in items)
			{
				if (dictionary.ContainsKey(item.Name()))
				{
					dictionary[item.Name()]++;
				}
				else
				{
					dictionary[item.Name()] = 1;
				}
			}
			return dictionary;
		}

		private IEnumerator Cupboard(bool standalone = false)
		{
			if (standalone)
			{
				inProgress = true;
				Log.Chat("Storing items in cupboard");
			}
			GameObject closet = GameObject.Find("Environment/HangarShip/StorageCloset");
			if (StartOfRound.Instance.unlockablesList.unlockables[7].inStorage)
			{
				if (standalone)
				{
					Log.Chat("No cupboard found. Return it from storage from the terminal.", "FFFF00");
				}
				yield break;
			}
			PlaceableObjectsSurface[] surfaces = closet.GetComponentsInChildren<PlaceableObjectsSurface>();
			Array.Sort(surfaces, (PlaceableObjectsSurface a, PlaceableObjectsSurface b) => ((Component)b).transform.position.y.CompareTo(((Component)a).transform.position.y));
			Log.Debug($"Found {surfaces.Length} shelves");
			yield return PlaceOnShelf(cupboardT, surfaces[0], Vector3.up * 0.6f);
			yield return PlaceOnShelf(cupboardA, surfaces[0]);
			yield return PlaceOnShelf(cupboardB, surfaces[1]);
			yield return PlaceOnShelf(cupboardC, surfaces[2]);
			yield return PlaceOnShelf(cupboardD, surfaces[3]);
			if (standalone)
			{
				inProgress = false;
			}
		}

		private IEnumerator PlaceOnShelf(List<GrabbableObject> items, PlaceableObjectsSurface shelf, Vector3 offset = default(Vector3))
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (items.Count == 0)
			{
				yield break;
			}
			Vector3 min = ((Component)shelf).transform.TransformPoint(Vector3.left * 0.6f) + offset;
			Vector3 max = ((Component)shelf).transform.TransformPoint(Vector3.right * 0.6f) + offset;
			int unique = items.Select((GrabbableObject i) => i.Name()).Distinct().Count();
			Vector3 margin = (max - min) / (float)(unique + 1);
			Vector3 forward = ((Component)shelf).transform.TransformDirection(Vector3.forward);
			string lastName = "";
			int x = 0;
			int y = 0;
			int z = 0;
			for (int j = 0; j < items.Count; j++)
			{
				GrabbableObject item = items[j];
				if (ShouldBreak(items[j]))
				{
					Log.NotifyPlayer("Sorter Stopping", "Operation cancelled or ship is in motion", isWarning: true);
					inProgress = false;
					break;
				}
				if (item.Name() != lastName)
				{
					x++;
					y = 0;
					z = 0;
				}
				else
				{
					y++;
					if (y > 5)
					{
						y = 0;
						z++;
					}
				}
				lastName = item.Name();
				Vector3 worldPos2 = min + margin * (float)x + Vector3.up * ((float)y * 0.05f) + forward * ((float)z * 0.05f);
				worldPos2 += ((Component)shelf).transform.TransformDirection(item.PlacementOffset());
				if (force || !(Vector3.Distance(worldPos2, ((Component)item).transform.position) < 0.05f))
				{
					Vector3 parentPos = ((Component)shelf.parentTo).transform.InverseTransformPoint(worldPos2);
					Vector3 rotation = new Vector3(0f, ((Component)shelf).transform.localEulerAngles.y - 45f, 0f);
					if (rotation.y < 0f)
					{
						rotation.y += 360f;
					}
					yield return Player.StartMovingObject(item, parentPos, shelf.parentTo);
					yield return GrabbableRetry(item);
				}
			}
		}

		private void Pile(ChatArgs args)
		{
			if (args[0] == "help")
			{
				Log.Chat(args.help, "FFFF00");
			}
			else if (!((NetworkBehaviour)Player.Local).IsServer)
			{
				Log.Chat("This command is for hosts only", "FF0000");
				Log.ErrorSound();
			}
			else if (!CanSort)
			{
				Log.NotifyPlayer("Sorter Error", "Must be in orbit or stationary at company", isWarning: true);
			}
			else if (inProgress)
			{
				Log.NotifyPlayer("Sorter Error", "Operation in progress. Press escape to cancel.", isWarning: true);
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(Pile(args[0]));
				Log.ConfirmSound();
			}
		}

		private IEnumerator Pile(string target)
		{
			inProgress = true;
			Log.Chat("Press Escape to cancel", "FFFF00");
			if (target == null)
			{
				GrabbableObject first = Player.Local.currentlyHeldObjectServer;
				target = first.itemProperties.itemName;
				Player.Local.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true);
			}
			IEnumerable<GrabbableObject> objs = from item in Object.FindObjectsOfType<GrabbableObject>()
				where item.Name() == target && item.isInShipRoom
				select item;
			Log.Chat($"{target} x{objs.Count()}", "00FFFF");
			foreach (GrabbableObject item2 in objs)
			{
				int retry = 10;
				while (!Player.CanGrabObject(item2) && retry > 0)
				{
					retry--;
					yield return (object)new WaitForSeconds(0.1f);
				}
				if (retry > 0)
				{
					yield return Player.StartGrabbingObject(item2);
					Player.Local.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true);
					if (!inProgress)
					{
						break;
					}
				}
			}
			inProgress = false;
		}

		private void CategorizeItems()
		{
			scrap = new List<GrabbableObject>();
			cupboardT = new List<GrabbableObject>();
			cupboardA = new List<GrabbableObject>();
			cupboardB = new List<GrabbableObject>();
			cupboardC = new List<GrabbableObject>();
			cupboardD = new List<GrabbableObject>();
			summonCircle = new List<GrabbableObject>();
			doorframe = new List<GrabbableObject>();
			allScrap = StartOfRound.Instance.allItemsList.itemsList.ToList();
			GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
			Array.Sort(array, (GrabbableObject a, GrabbableObject b) => a.scrapValue.CompareTo(b.scrapValue));
			Array.Sort(array, delegate(GrabbableObject a, GrabbableObject b)
			{
				if (a.itemProperties.twoHanded != b.itemProperties.twoHanded)
				{
					return -a.itemProperties.twoHanded.CompareTo(b.itemProperties.twoHanded);
				}
				if (a.itemProperties.isDefensiveWeapon != b.itemProperties.isDefensiveWeapon)
				{
					return -a.itemProperties.isDefensiveWeapon.CompareTo(b.itemProperties.isDefensiveWeapon);
				}
				return (a.AvgValue() != b.AvgValue()) ? a.AvgValue().CompareTo(b.AvgValue()) : a.Name().CompareTo(b.Name());
			});
			GrabbableObject[] array2 = array;
			foreach (GrabbableObject val in array2)
			{
				if (!ShouldSkip(val))
				{
					string t = val.Name();
					if (skippedItems.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
					}
					else if (summonCircleItems.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
						summonCircle.Add(val);
					}
					else if (cupboardTop.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
						cupboardT.Add(val);
					}
					else if (cupboardShelfA.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
						cupboardA.Add(val);
					}
					else if (cupboardShelfB.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
						cupboardB.Add(val);
					}
					else if (cupboardShelfC.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
						cupboardC.Add(val);
					}
					else if (cupboardShelfD.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
						cupboardD.Add(val);
					}
					else if (doorframeItems.Value.ContainsItem(t))
					{
						allScrap.Remove(val.itemProperties);
						doorframe.Add(val);
					}
					else
					{
						scrap.Add(val);
					}
				}
			}
		}

		private IEnumerator GrabbableRetry(GrabbableObject item)
		{
			int retry = 15;
			while (!Player.CanGrabObject(item) && retry > 0)
			{
				yield return (object)new WaitForEndOfFrame();
				retry--;
			}
		}

		private bool ShouldBreak(GrabbableObject item)
		{
			return !inProgress || !Ship.Stationary || Player.Local.beamOutParticle.isPlaying || Player.Local.beamUpParticle.isPlaying;
		}

		private bool ShouldSkip(GrabbableObject item)
		{
			if (!Player.CanGrabObject(item) || item.Name() == "body")
			{
				return true;
			}
			if (!item.isInShipRoom)
			{
				return true;
			}
			return false;
		}

		private Dictionary<string, Vector2> CreateScrapLayout(List<Item> items)
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: 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)
			Dictionary<string, Vector2> dictionary = new Dictionary<string, Vector2>();
			IEnumerable<Item> source = (from i in items
				where i.isScrap
				orderby i.Name()
				orderby (float)(i.minValue + i.maxValue) / 2f
				select i).Distinct();
			Item[] array = source.Where((Item i) => i.twoHanded).ToArray();
			Item[] array2 = source.Where((Item i) => !i.twoHanded).ToArray();
			for (int j = 0; j < array.Length; j++)
			{
				dictionary[array[j].Name()] = new Vector2((float)j * 0.7f, 3f);
			}
			Vector3 val = Vector2.op_Implicit(new Vector2(0.6f, 0.7f));
			for (int k = 0; k < array2.Length; k++)
			{
				int num = k / 4;
				int num2 = k % 4;
				float num3 = ((num % 2 == 0) ? 0f : 0.5f);
				dictionary[array2[k].Name()] = new Vector2((float)num, (float)num2 + num3) * Vector2.op_Implicit(val);
			}
			return dictionary;
		}

		private void OnDestroy()
		{
			ChatCommand.Commands.Clear();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "kylethescientist.scrapmagic";

		public const string PLUGIN_NAME = "ScrapMagic";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}